mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
crypto: add KMAC Web Cryptography algorithms
PR-URL: https://github.com/nodejs/node/pull/59647 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
a7fde8a86f
commit
14c68e3b53
90
deps/ncrypto/ncrypto.cc
vendored
90
deps/ncrypto/ncrypto.cc
vendored
|
|
@ -4413,6 +4413,96 @@ HMACCtxPointer HMACCtxPointer::New() {
|
||||||
return HMACCtxPointer(HMAC_CTX_new());
|
return HMACCtxPointer(HMAC_CTX_new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
EVPMacPointer::EVPMacPointer(EVP_MAC* mac) : mac_(mac) {}
|
||||||
|
|
||||||
|
EVPMacPointer::EVPMacPointer(EVPMacPointer&& other) noexcept
|
||||||
|
: mac_(std::move(other.mac_)) {}
|
||||||
|
|
||||||
|
EVPMacPointer& EVPMacPointer::operator=(EVPMacPointer&& other) noexcept {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
mac_ = std::move(other.mac_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPMacPointer::~EVPMacPointer() {
|
||||||
|
mac_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EVPMacPointer::reset(EVP_MAC* mac) {
|
||||||
|
mac_.reset(mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_MAC* EVPMacPointer::release() {
|
||||||
|
return mac_.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPMacPointer EVPMacPointer::Fetch(const char* algorithm) {
|
||||||
|
return EVPMacPointer(EVP_MAC_fetch(nullptr, algorithm, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPMacCtxPointer::EVPMacCtxPointer(EVP_MAC_CTX* ctx) : ctx_(ctx) {}
|
||||||
|
|
||||||
|
EVPMacCtxPointer::EVPMacCtxPointer(EVPMacCtxPointer&& other) noexcept
|
||||||
|
: ctx_(std::move(other.ctx_)) {}
|
||||||
|
|
||||||
|
EVPMacCtxPointer& EVPMacCtxPointer::operator=(
|
||||||
|
EVPMacCtxPointer&& other) noexcept {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
ctx_ = std::move(other.ctx_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPMacCtxPointer::~EVPMacCtxPointer() {
|
||||||
|
ctx_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EVPMacCtxPointer::reset(EVP_MAC_CTX* ctx) {
|
||||||
|
ctx_.reset(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_MAC_CTX* EVPMacCtxPointer::release() {
|
||||||
|
return ctx_.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EVPMacCtxPointer::init(const Buffer<const void>& key,
|
||||||
|
const OSSL_PARAM* params) {
|
||||||
|
if (!ctx_) return false;
|
||||||
|
return EVP_MAC_init(ctx_.get(),
|
||||||
|
static_cast<const unsigned char*>(key.data),
|
||||||
|
key.len,
|
||||||
|
params) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EVPMacCtxPointer::update(const Buffer<const void>& data) {
|
||||||
|
if (!ctx_) return false;
|
||||||
|
return EVP_MAC_update(ctx_.get(),
|
||||||
|
static_cast<const unsigned char*>(data.data),
|
||||||
|
data.len) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataPointer EVPMacCtxPointer::final(size_t length) {
|
||||||
|
if (!ctx_) return {};
|
||||||
|
auto buf = DataPointer::Alloc(length);
|
||||||
|
if (!buf) return {};
|
||||||
|
|
||||||
|
size_t result_len = length;
|
||||||
|
if (EVP_MAC_final(ctx_.get(),
|
||||||
|
static_cast<unsigned char*>(buf.get()),
|
||||||
|
&result_len,
|
||||||
|
length) != 1) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPMacCtxPointer EVPMacCtxPointer::New(EVP_MAC* mac) {
|
||||||
|
if (!mac) return EVPMacCtxPointer();
|
||||||
|
return EVPMacCtxPointer(EVP_MAC_CTX_new(mac));
|
||||||
|
}
|
||||||
|
#endif // OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
|
||||||
DataPointer hashDigest(const Buffer<const unsigned char>& buf,
|
DataPointer hashDigest(const Buffer<const unsigned char>& buf,
|
||||||
const EVP_MD* md) {
|
const EVP_MD* md) {
|
||||||
if (md == nullptr) return {};
|
if (md == nullptr) return {};
|
||||||
|
|
|
||||||
52
deps/ncrypto/ncrypto.h
vendored
52
deps/ncrypto/ncrypto.h
vendored
|
|
@ -229,6 +229,8 @@ class DataPointer;
|
||||||
class DHPointer;
|
class DHPointer;
|
||||||
class ECKeyPointer;
|
class ECKeyPointer;
|
||||||
class EVPKeyPointer;
|
class EVPKeyPointer;
|
||||||
|
class EVPMacCtxPointer;
|
||||||
|
class EVPMacPointer;
|
||||||
class EVPMDCtxPointer;
|
class EVPMDCtxPointer;
|
||||||
class SSLCtxPointer;
|
class SSLCtxPointer;
|
||||||
class SSLPointer;
|
class SSLPointer;
|
||||||
|
|
@ -1451,6 +1453,56 @@ class HMACCtxPointer final {
|
||||||
DeleteFnPtr<HMAC_CTX, HMAC_CTX_free> ctx_;
|
DeleteFnPtr<HMAC_CTX, HMAC_CTX_free> ctx_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
class EVPMacPointer final {
|
||||||
|
public:
|
||||||
|
EVPMacPointer() = default;
|
||||||
|
explicit EVPMacPointer(EVP_MAC* mac);
|
||||||
|
EVPMacPointer(EVPMacPointer&& other) noexcept;
|
||||||
|
EVPMacPointer& operator=(EVPMacPointer&& other) noexcept;
|
||||||
|
NCRYPTO_DISALLOW_COPY(EVPMacPointer)
|
||||||
|
~EVPMacPointer();
|
||||||
|
|
||||||
|
inline bool operator==(std::nullptr_t) noexcept { return mac_ == nullptr; }
|
||||||
|
inline operator bool() const { return mac_ != nullptr; }
|
||||||
|
inline EVP_MAC* get() const { return mac_.get(); }
|
||||||
|
inline operator EVP_MAC*() const { return mac_.get(); }
|
||||||
|
void reset(EVP_MAC* mac = nullptr);
|
||||||
|
EVP_MAC* release();
|
||||||
|
|
||||||
|
static EVPMacPointer Fetch(const char* algorithm);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeleteFnPtr<EVP_MAC, EVP_MAC_free> mac_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EVPMacCtxPointer final {
|
||||||
|
public:
|
||||||
|
EVPMacCtxPointer() = default;
|
||||||
|
explicit EVPMacCtxPointer(EVP_MAC_CTX* ctx);
|
||||||
|
EVPMacCtxPointer(EVPMacCtxPointer&& other) noexcept;
|
||||||
|
EVPMacCtxPointer& operator=(EVPMacCtxPointer&& other) noexcept;
|
||||||
|
NCRYPTO_DISALLOW_COPY(EVPMacCtxPointer)
|
||||||
|
~EVPMacCtxPointer();
|
||||||
|
|
||||||
|
inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; }
|
||||||
|
inline operator bool() const { return ctx_ != nullptr; }
|
||||||
|
inline EVP_MAC_CTX* get() const { return ctx_.get(); }
|
||||||
|
inline operator EVP_MAC_CTX*() const { return ctx_.get(); }
|
||||||
|
void reset(EVP_MAC_CTX* ctx = nullptr);
|
||||||
|
EVP_MAC_CTX* release();
|
||||||
|
|
||||||
|
bool init(const Buffer<const void>& key, const OSSL_PARAM* params = nullptr);
|
||||||
|
bool update(const Buffer<const void>& data);
|
||||||
|
DataPointer final(size_t length);
|
||||||
|
|
||||||
|
static EVPMacCtxPointer New(EVP_MAC* mac);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeleteFnPtr<EVP_MAC_CTX, EVP_MAC_CTX_free> ctx_;
|
||||||
|
};
|
||||||
|
#endif // OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
|
||||||
#ifndef OPENSSL_NO_ENGINE
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
class EnginePointer final {
|
class EnginePointer final {
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/59647
|
||||||
|
description: KMAC algorithms are now supported.
|
||||||
- version: REPLACEME
|
- version: REPLACEME
|
||||||
pr-url: https://github.com/nodejs/node/pull/59544
|
pr-url: https://github.com/nodejs/node/pull/59544
|
||||||
description: Argon2 algorithms are now supported.
|
description: Argon2 algorithms are now supported.
|
||||||
|
|
@ -117,6 +120,8 @@ Algorithms:
|
||||||
* `'ChaCha20-Poly1305'`
|
* `'ChaCha20-Poly1305'`
|
||||||
* `'cSHAKE128'`
|
* `'cSHAKE128'`
|
||||||
* `'cSHAKE256'`
|
* `'cSHAKE256'`
|
||||||
|
* `'KMAC128'`[^openssl30]
|
||||||
|
* `'KMAC256'`[^openssl30]
|
||||||
* `'ML-DSA-44'`[^openssl35]
|
* `'ML-DSA-44'`[^openssl35]
|
||||||
* `'ML-DSA-65'`[^openssl35]
|
* `'ML-DSA-65'`[^openssl35]
|
||||||
* `'ML-DSA-87'`[^openssl35]
|
* `'ML-DSA-87'`[^openssl35]
|
||||||
|
|
@ -522,6 +527,8 @@ implementation and the APIs supported for each:
|
||||||
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | ✔ |
|
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | ✔ |
|
||||||
| `'HKDF'` | | | ✔ | |
|
| `'HKDF'` | | | ✔ | |
|
||||||
| `'HMAC'` | ✔ | ✔ | ✔ | |
|
| `'HMAC'` | ✔ | ✔ | ✔ | |
|
||||||
|
| `'KMAC128'`[^modern-algos] | ✔ | ✔ | ✔ | |
|
||||||
|
| `'KMAC256'`[^modern-algos] | ✔ | ✔ | ✔ | |
|
||||||
| `'ML-DSA-44'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ |
|
| `'ML-DSA-44'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ |
|
||||||
| `'ML-DSA-65'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ |
|
| `'ML-DSA-65'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ |
|
||||||
| `'ML-DSA-87'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ |
|
| `'ML-DSA-87'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ |
|
||||||
|
|
@ -566,6 +573,8 @@ implementation and the APIs supported for each:
|
||||||
| `'Ed448'`[^secure-curves] | | ✔ | | | | |
|
| `'Ed448'`[^secure-curves] | | ✔ | | | | |
|
||||||
| `'HKDF'` | | | ✔ | | | |
|
| `'HKDF'` | | | ✔ | | | |
|
||||||
| `'HMAC'` | | ✔ | | | | |
|
| `'HMAC'` | | ✔ | | | | |
|
||||||
|
| `'KMAC128'`[^modern-algos] | | ✔ | | | | |
|
||||||
|
| `'KMAC256'`[^modern-algos] | | ✔ | | | | |
|
||||||
| `'ML-DSA-44'`[^modern-algos] | | ✔ | | | | |
|
| `'ML-DSA-44'`[^modern-algos] | | ✔ | | | | |
|
||||||
| `'ML-DSA-65'`[^modern-algos] | | ✔ | | | | |
|
| `'ML-DSA-65'`[^modern-algos] | | ✔ | | | | |
|
||||||
| `'ML-DSA-87'`[^modern-algos] | | ✔ | | | | |
|
| `'ML-DSA-87'`[^modern-algos] | | ✔ | | | | |
|
||||||
|
|
@ -648,7 +657,7 @@ added: v15.0.0
|
||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* Type: {KeyAlgorithm|RsaHashedKeyAlgorithm|EcKeyAlgorithm|AesKeyAlgorithm|HmacKeyAlgorithm}
|
* Type: {KeyAlgorithm|RsaHashedKeyAlgorithm|EcKeyAlgorithm|AesKeyAlgorithm|HmacKeyAlgorithm|KmacKeyAlgorithm}
|
||||||
|
|
||||||
<!--lint enable maximum-line-length remark-lint-->
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
|
|
@ -736,6 +745,8 @@ Valid key usages depend on the key algorithm (identified by
|
||||||
| `'Ed448'`[^secure-curves] | | ✔ | | | |
|
| `'Ed448'`[^secure-curves] | | ✔ | | | |
|
||||||
| `'HDKF'` | | | ✔ | | |
|
| `'HDKF'` | | | ✔ | | |
|
||||||
| `'HMAC'` | | ✔ | | | |
|
| `'HMAC'` | | ✔ | | | |
|
||||||
|
| `'KMAC128'`[^modern-algos] | | ✔ | | | |
|
||||||
|
| `'KMAC256'`[^modern-algos] | | ✔ | | | |
|
||||||
| `'ML-DSA-44'`[^modern-algos] | | ✔ | | | |
|
| `'ML-DSA-44'`[^modern-algos] | | ✔ | | | |
|
||||||
| `'ML-DSA-65'`[^modern-algos] | | ✔ | | | |
|
| `'ML-DSA-65'`[^modern-algos] | | ✔ | | | |
|
||||||
| `'ML-DSA-87'`[^modern-algos] | | ✔ | | | |
|
| `'ML-DSA-87'`[^modern-algos] | | ✔ | | | |
|
||||||
|
|
@ -831,7 +842,7 @@ added: v24.7.0
|
||||||
* `decapsulationAlgorithm` {string|Algorithm}
|
* `decapsulationAlgorithm` {string|Algorithm}
|
||||||
* `decapsulationKey` {CryptoKey}
|
* `decapsulationKey` {CryptoKey}
|
||||||
* `ciphertext` {ArrayBuffer|TypedArray|DataView|Buffer}
|
* `ciphertext` {ArrayBuffer|TypedArray|DataView|Buffer}
|
||||||
* `sharedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams}
|
* `sharedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams|KmacImportParams}
|
||||||
* `extractable` {boolean}
|
* `extractable` {boolean}
|
||||||
* `usages` {string\[]} See [Key usages][].
|
* `usages` {string\[]} See [Key usages][].
|
||||||
* Returns: {Promise} Fulfills with {CryptoKey} upon success.
|
* Returns: {Promise} Fulfills with {CryptoKey} upon success.
|
||||||
|
|
@ -946,7 +957,7 @@ changes:
|
||||||
|
|
||||||
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
|
* `algorithm` {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|Argon2Params}
|
||||||
* `baseKey` {CryptoKey}
|
* `baseKey` {CryptoKey}
|
||||||
* `derivedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams}
|
* `derivedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams|KmacImportParams}
|
||||||
* `extractable` {boolean}
|
* `extractable` {boolean}
|
||||||
* `keyUsages` {string\[]} See [Key usages][].
|
* `keyUsages` {string\[]} See [Key usages][].
|
||||||
* Returns: {Promise} Fulfills with a {CryptoKey} upon success.
|
* Returns: {Promise} Fulfills with a {CryptoKey} upon success.
|
||||||
|
|
@ -1037,7 +1048,7 @@ added: v24.7.0
|
||||||
|
|
||||||
* `encapsulationAlgorithm` {string|Algorithm}
|
* `encapsulationAlgorithm` {string|Algorithm}
|
||||||
* `encapsulationKey` {CryptoKey}
|
* `encapsulationKey` {CryptoKey}
|
||||||
* `sharedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams}
|
* `sharedKeyAlgorithm` {string|Algorithm|HmacImportParams|AesDerivedKeyParams|KmacImportParams}
|
||||||
* `extractable` {boolean}
|
* `extractable` {boolean}
|
||||||
* `usages` {string\[]} See [Key usages][].
|
* `usages` {string\[]} See [Key usages][].
|
||||||
* Returns: {Promise} Fulfills with {EncapsulatedKey} upon success.
|
* Returns: {Promise} Fulfills with {EncapsulatedKey} upon success.
|
||||||
|
|
@ -1085,6 +1096,9 @@ The algorithms currently supported include:
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/59647
|
||||||
|
description: KMAC algorithms are now supported.
|
||||||
- version: v24.7.0
|
- version: v24.7.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/59569
|
pr-url: https://github.com/nodejs/node/pull/59569
|
||||||
description: ML-KEM algorithms are now supported.
|
description: ML-KEM algorithms are now supported.
|
||||||
|
|
@ -1135,6 +1149,8 @@ specification.
|
||||||
| `'Ed25519'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
|
| `'Ed25519'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
|
||||||
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | ✔ | | ✔ | |
|
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | ✔ | | ✔ | |
|
||||||
| `'HMAC'` | | | ✔ | ✔ | ✔ | | |
|
| `'HMAC'` | | | ✔ | ✔ | ✔ | | |
|
||||||
|
| `'KMAC128'`[^modern-algos] | | | ✔ | | ✔ | | |
|
||||||
|
| `'KMAC256'`[^modern-algos] | | | ✔ | | ✔ | | |
|
||||||
| `'ML-DSA-44'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
| `'ML-DSA-44'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
||||||
| `'ML-DSA-65'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
| `'ML-DSA-65'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
||||||
| `'ML-DSA-87'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
| `'ML-DSA-87'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
||||||
|
|
@ -1164,6 +1180,9 @@ Derives the public key from a given private key.
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/59647
|
||||||
|
description: KMAC algorithms are now supported.
|
||||||
- version: v24.7.0
|
- version: v24.7.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/59569
|
pr-url: https://github.com/nodejs/node/pull/59569
|
||||||
description: ML-KEM algorithms are now supported.
|
description: ML-KEM algorithms are now supported.
|
||||||
|
|
@ -1177,7 +1196,7 @@ changes:
|
||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm` {string|Algorithm|RsaHashedKeyGenParams|EcKeyGenParams|HmacKeyGenParams|AesKeyGenParams}
|
* `algorithm` {string|Algorithm|RsaHashedKeyGenParams|EcKeyGenParams|HmacKeyGenParams|AesKeyGenParams|KmacKeyGenParams}
|
||||||
|
|
||||||
<!--lint enable maximum-line-length remark-lint-->
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
|
|
@ -1217,12 +1236,17 @@ The {CryptoKey} (secret key) generating algorithms supported include:
|
||||||
* `'AES-OCB'`[^modern-algos]
|
* `'AES-OCB'`[^modern-algos]
|
||||||
* `'ChaCha20-Poly1305'`[^modern-algos]
|
* `'ChaCha20-Poly1305'`[^modern-algos]
|
||||||
* `'HMAC'`
|
* `'HMAC'`
|
||||||
|
* `'KMAC128'`[^modern-algos]
|
||||||
|
* `'KMAC256'`[^modern-algos]
|
||||||
|
|
||||||
### `subtle.importKey(format, keyData, algorithm, extractable, keyUsages)`
|
### `subtle.importKey(format, keyData, algorithm, extractable, keyUsages)`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/59647
|
||||||
|
description: KMAC algorithms are now supported.
|
||||||
- version: v24.7.0
|
- version: v24.7.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/59569
|
pr-url: https://github.com/nodejs/node/pull/59569
|
||||||
description: ML-KEM algorithms are now supported.
|
description: ML-KEM algorithms are now supported.
|
||||||
|
|
@ -1249,7 +1273,7 @@ changes:
|
||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm` {string|Algorithm|RsaHashedImportParams|EcKeyImportParams|HmacImportParams}
|
* `algorithm` {string|Algorithm|RsaHashedImportParams|EcKeyImportParams|HmacImportParams|KmacImportParams}
|
||||||
|
|
||||||
<!--lint enable maximum-line-length remark-lint-->
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
|
|
@ -1283,6 +1307,8 @@ The algorithms currently supported include:
|
||||||
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | ✔ | | ✔ | |
|
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | ✔ | | ✔ | |
|
||||||
| `'HDKF'` | | | | ✔ | ✔ | | |
|
| `'HDKF'` | | | | ✔ | ✔ | | |
|
||||||
| `'HMAC'` | | | ✔ | ✔ | ✔ | | |
|
| `'HMAC'` | | | ✔ | ✔ | ✔ | | |
|
||||||
|
| `'KMAC128'`[^modern-algos] | | | ✔ | | ✔ | | |
|
||||||
|
| `'KMAC256'`[^modern-algos] | | | ✔ | | ✔ | | |
|
||||||
| `'ML-DSA-44'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
| `'ML-DSA-44'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
||||||
| `'ML-DSA-65'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
| `'ML-DSA-65'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
||||||
| `'ML-DSA-87'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
| `'ML-DSA-87'`[^modern-algos] | ✔ | ✔ | ✔ | | | ✔ | ✔ |
|
||||||
|
|
@ -1301,6 +1327,9 @@ The algorithms currently supported include:
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/59647
|
||||||
|
description: KMAC algorithms are now supported.
|
||||||
- version: v24.7.0
|
- version: v24.7.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/59365
|
pr-url: https://github.com/nodejs/node/pull/59365
|
||||||
description: ML-DSA algorithms are now supported.
|
description: ML-DSA algorithms are now supported.
|
||||||
|
|
@ -1313,7 +1342,7 @@ changes:
|
||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|Ed448Params|ContextParams}
|
* `algorithm` {string|Algorithm|RsaPssParams|EcdsaParams|Ed448Params|ContextParams|KmacParams}
|
||||||
* `key` {CryptoKey}
|
* `key` {CryptoKey}
|
||||||
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
|
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
|
||||||
* Returns: {Promise} Fulfills with an {ArrayBuffer} upon success.
|
* Returns: {Promise} Fulfills with an {ArrayBuffer} upon success.
|
||||||
|
|
@ -1331,6 +1360,8 @@ The algorithms currently supported include:
|
||||||
* `'Ed25519'`
|
* `'Ed25519'`
|
||||||
* `'Ed448'`[^secure-curves]
|
* `'Ed448'`[^secure-curves]
|
||||||
* `'HMAC'`
|
* `'HMAC'`
|
||||||
|
* `'KMAC128'`[^modern-algos]
|
||||||
|
* `'KMAC256'`[^modern-algos]
|
||||||
* `'ML-DSA-44'`[^modern-algos]
|
* `'ML-DSA-44'`[^modern-algos]
|
||||||
* `'ML-DSA-65'`[^modern-algos]
|
* `'ML-DSA-65'`[^modern-algos]
|
||||||
* `'ML-DSA-87'`[^modern-algos]
|
* `'ML-DSA-87'`[^modern-algos]
|
||||||
|
|
@ -1358,7 +1389,7 @@ changes:
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `unwrapAlgo` {string|Algorithm|RsaOaepParams|AesCtrParams|AesCbcParams|AeadParams}
|
* `unwrapAlgo` {string|Algorithm|RsaOaepParams|AesCtrParams|AesCbcParams|AeadParams}
|
||||||
* `unwrappedKeyAlgo` {string|Algorithm|RsaHashedImportParams|EcKeyImportParams|HmacImportParams}
|
* `unwrappedKeyAlgo` {string|Algorithm|RsaHashedImportParams|EcKeyImportParams|HmacImportParams|KmacImportParams}
|
||||||
|
|
||||||
<!--lint enable maximum-line-length remark-lint-->
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
|
|
@ -1398,6 +1429,8 @@ The unwrapped key algorithms supported include:
|
||||||
* `'Ed25519'`
|
* `'Ed25519'`
|
||||||
* `'Ed448'`[^secure-curves]
|
* `'Ed448'`[^secure-curves]
|
||||||
* `'HMAC'`
|
* `'HMAC'`
|
||||||
|
* `'KMAC128'`[^secure-curves]
|
||||||
|
* `'KMAC256'`[^secure-curves]
|
||||||
* `'ML-DSA-44'`[^modern-algos]
|
* `'ML-DSA-44'`[^modern-algos]
|
||||||
* `'ML-DSA-65'`[^modern-algos]
|
* `'ML-DSA-65'`[^modern-algos]
|
||||||
* `'ML-DSA-87'`[^modern-algos]
|
* `'ML-DSA-87'`[^modern-algos]
|
||||||
|
|
@ -1415,6 +1448,9 @@ The unwrapped key algorithms supported include:
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/59647
|
||||||
|
description: KMAC algorithms are now supported.
|
||||||
- version: v24.7.0
|
- version: v24.7.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/59365
|
pr-url: https://github.com/nodejs/node/pull/59365
|
||||||
description: ML-DSA algorithms are now supported.
|
description: ML-DSA algorithms are now supported.
|
||||||
|
|
@ -1446,6 +1482,8 @@ The algorithms currently supported include:
|
||||||
* `'Ed25519'`
|
* `'Ed25519'`
|
||||||
* `'Ed448'`[^secure-curves]
|
* `'Ed448'`[^secure-curves]
|
||||||
* `'HMAC'`
|
* `'HMAC'`
|
||||||
|
* `'KMAC128'`[^secure-curves]
|
||||||
|
* `'KMAC256'`[^secure-curves]
|
||||||
* `'ML-DSA-44'`[^modern-algos]
|
* `'ML-DSA-44'`[^modern-algos]
|
||||||
* `'ML-DSA-65'`[^modern-algos]
|
* `'ML-DSA-65'`[^modern-algos]
|
||||||
* `'ML-DSA-87'`[^modern-algos]
|
* `'ML-DSA-87'`[^modern-algos]
|
||||||
|
|
@ -2271,6 +2309,115 @@ added: v15.0.0
|
||||||
|
|
||||||
* Type: {string}
|
* Type: {string}
|
||||||
|
|
||||||
|
### Class: `KmacImportParams`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### `kmacImportParams.length`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {number}
|
||||||
|
|
||||||
|
The optional number of bits in the KMAC key. This is optional and should
|
||||||
|
be omitted for most cases.
|
||||||
|
|
||||||
|
#### `kmacImportParams.name`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {string} Must be `'KMAC128'` or `'KMAC256'`.
|
||||||
|
|
||||||
|
### Class: `KmacKeyAlgorithm`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### `kmacKeyAlgorithm.length`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {number}
|
||||||
|
|
||||||
|
The length of the KMAC key in bits.
|
||||||
|
|
||||||
|
#### `kmacKeyAlgorithm.name`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {string}
|
||||||
|
|
||||||
|
### Class: `KmacKeyGenParams`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### `kmacKeyGenParams.length`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {number}
|
||||||
|
|
||||||
|
The number of bits to generate for the KMAC key. If omitted,
|
||||||
|
the length will be determined by the KMAC algorithm used.
|
||||||
|
This is optional and should be omitted for most cases.
|
||||||
|
|
||||||
|
#### `kmacKeyGenParams.name`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {string} Must be `'KMAC128'` or `'KMAC256'`.
|
||||||
|
|
||||||
|
### Class: `KmacParams`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### `kmacParams.algorithm`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {string} Must be `'KMAC128'` or `'KMAC256'`.
|
||||||
|
|
||||||
|
#### `kmacParams.customization`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined}
|
||||||
|
|
||||||
|
The `customization` member represents the optional customization string.
|
||||||
|
|
||||||
|
#### `kmacParams.length`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {number}
|
||||||
|
|
||||||
|
The length of the output in bytes. This must be a positive integer.
|
||||||
|
|
||||||
### Class: `Pbkdf2Params`
|
### Class: `Pbkdf2Params`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
|
|
||||||
|
|
@ -189,8 +189,12 @@ const {
|
||||||
let result;
|
let result;
|
||||||
switch (algorithm.name) {
|
switch (algorithm.name) {
|
||||||
case 'HMAC':
|
case 'HMAC':
|
||||||
|
// Fall through
|
||||||
|
case 'KMAC128':
|
||||||
|
// Fall through
|
||||||
|
case 'KMAC256':
|
||||||
result = require('internal/crypto/mac')
|
result = require('internal/crypto/mac')
|
||||||
.hmacImportKey('KeyObject', this, algorithm, extractable, keyUsages);
|
.macImportKey('KeyObject', this, algorithm, extractable, keyUsages);
|
||||||
break;
|
break;
|
||||||
case 'AES-CTR':
|
case 'AES-CTR':
|
||||||
// Fall through
|
// Fall through
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@
|
||||||
const {
|
const {
|
||||||
ArrayFrom,
|
ArrayFrom,
|
||||||
SafeSet,
|
SafeSet,
|
||||||
|
StringPrototypeSubstring,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
HmacJob,
|
HmacJob,
|
||||||
KeyObjectHandle,
|
KeyObjectHandle,
|
||||||
|
KmacJob,
|
||||||
kCryptoJobAsync,
|
kCryptoJobAsync,
|
||||||
kSignJobModeSign,
|
kSignJobModeSign,
|
||||||
kSignJobModeVerify,
|
kSignJobModeVerify,
|
||||||
|
|
@ -32,6 +34,13 @@ const {
|
||||||
generateKey: _generateKey,
|
generateKey: _generateKey,
|
||||||
} = require('internal/crypto/keygen');
|
} = require('internal/crypto/keygen');
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
randomBytes: _randomBytes,
|
||||||
|
} = require('internal/crypto/random');
|
||||||
|
|
||||||
|
const randomBytes = promisify(_randomBytes);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
InternalCryptoKey,
|
InternalCryptoKey,
|
||||||
SecretKeyObject,
|
SecretKeyObject,
|
||||||
|
|
@ -42,11 +51,11 @@ const {
|
||||||
const generateKey = promisify(_generateKey);
|
const generateKey = promisify(_generateKey);
|
||||||
|
|
||||||
async function hmacGenerateKey(algorithm, extractable, keyUsages) {
|
async function hmacGenerateKey(algorithm, extractable, keyUsages) {
|
||||||
const { hash, name } = algorithm;
|
const {
|
||||||
let { length } = algorithm;
|
hash,
|
||||||
|
name,
|
||||||
if (length === undefined)
|
length = getBlockSize(hash.name),
|
||||||
length = getBlockSize(hash.name);
|
} = algorithm;
|
||||||
|
|
||||||
const usageSet = new SafeSet(keyUsages);
|
const usageSet = new SafeSet(keyUsages);
|
||||||
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
|
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
|
||||||
|
|
@ -68,17 +77,49 @@ async function hmacGenerateKey(algorithm, extractable, keyUsages) {
|
||||||
extractable);
|
extractable);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hmacImportKey(
|
async function kmacGenerateKey(algorithm, extractable, keyUsages) {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
length = {
|
||||||
|
__proto__: null,
|
||||||
|
KMAC128: 128,
|
||||||
|
KMAC256: 256,
|
||||||
|
}[name],
|
||||||
|
} = algorithm;
|
||||||
|
|
||||||
|
const usageSet = new SafeSet(keyUsages);
|
||||||
|
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
|
||||||
|
throw lazyDOMException(
|
||||||
|
`Unsupported key usage for ${name} key`,
|
||||||
|
'SyntaxError');
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyData = await randomBytes(length / 8).catch((err) => {
|
||||||
|
throw lazyDOMException(
|
||||||
|
'The operation failed for an operation-specific reason' +
|
||||||
|
`[${err.message}]`,
|
||||||
|
{ name: 'OperationError', cause: err });
|
||||||
|
});
|
||||||
|
|
||||||
|
return new InternalCryptoKey(
|
||||||
|
createSecretKey(keyData),
|
||||||
|
{ name, length },
|
||||||
|
ArrayFrom(usageSet),
|
||||||
|
extractable);
|
||||||
|
}
|
||||||
|
|
||||||
|
function macImportKey(
|
||||||
format,
|
format,
|
||||||
keyData,
|
keyData,
|
||||||
algorithm,
|
algorithm,
|
||||||
extractable,
|
extractable,
|
||||||
keyUsages,
|
keyUsages,
|
||||||
) {
|
) {
|
||||||
|
const isHmac = algorithm.name === 'HMAC';
|
||||||
const usagesSet = new SafeSet(keyUsages);
|
const usagesSet = new SafeSet(keyUsages);
|
||||||
if (hasAnyNotIn(usagesSet, ['sign', 'verify'])) {
|
if (hasAnyNotIn(usagesSet, ['sign', 'verify'])) {
|
||||||
throw lazyDOMException(
|
throw lazyDOMException(
|
||||||
'Unsupported key usage for an HMAC key',
|
`Unsupported key usage for ${algorithm.name} key`,
|
||||||
'SyntaxError');
|
'SyntaxError');
|
||||||
}
|
}
|
||||||
let keyObject;
|
let keyObject;
|
||||||
|
|
@ -87,7 +128,11 @@ function hmacImportKey(
|
||||||
keyObject = keyData;
|
keyObject = keyData;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'raw-secret':
|
||||||
case 'raw': {
|
case 'raw': {
|
||||||
|
if (format === 'raw' && !isHmac) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
keyObject = createSecretKey(keyData);
|
keyObject = createSecretKey(keyData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -115,8 +160,9 @@ function hmacImportKey(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyData.alg !== undefined) {
|
if (keyData.alg !== undefined) {
|
||||||
const expected =
|
const expected = isHmac ?
|
||||||
normalizeHashName(algorithm.hash.name, normalizeHashName.kContextJwkHmac);
|
normalizeHashName(algorithm.hash.name, normalizeHashName.kContextJwkHmac) :
|
||||||
|
`K${StringPrototypeSubstring(algorithm.name, 4)}`;
|
||||||
if (expected && keyData.alg !== expected)
|
if (expected && keyData.alg !== expected)
|
||||||
throw lazyDOMException(
|
throw lazyDOMException(
|
||||||
'JWK "alg" does not match the requested algorithm',
|
'JWK "alg" does not match the requested algorithm',
|
||||||
|
|
@ -147,12 +193,18 @@ function hmacImportKey(
|
||||||
throw lazyDOMException('Invalid key length', 'DataError');
|
throw lazyDOMException('Invalid key length', 'DataError');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const algorithmObject = {
|
||||||
|
name: algorithm.name,
|
||||||
|
length,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isHmac) {
|
||||||
|
algorithmObject.hash = algorithm.hash;
|
||||||
|
}
|
||||||
|
|
||||||
return new InternalCryptoKey(
|
return new InternalCryptoKey(
|
||||||
keyObject, {
|
keyObject,
|
||||||
name: 'HMAC',
|
algorithmObject,
|
||||||
hash: algorithm.hash,
|
|
||||||
length,
|
|
||||||
},
|
|
||||||
keyUsages,
|
keyUsages,
|
||||||
extractable);
|
extractable);
|
||||||
}
|
}
|
||||||
|
|
@ -168,8 +220,23 @@ function hmacSignVerify(key, data, algorithm, signature) {
|
||||||
signature));
|
signature));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function kmacSignVerify(key, data, algorithm, signature) {
|
||||||
|
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
|
||||||
|
return jobPromise(() => new KmacJob(
|
||||||
|
kCryptoJobAsync,
|
||||||
|
mode,
|
||||||
|
key[kKeyObject][kHandle],
|
||||||
|
algorithm.name,
|
||||||
|
algorithm.customization,
|
||||||
|
algorithm.length / 8,
|
||||||
|
data,
|
||||||
|
signature));
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
hmacImportKey,
|
macImportKey,
|
||||||
hmacGenerateKey,
|
hmacGenerateKey,
|
||||||
hmacSignVerify,
|
hmacSignVerify,
|
||||||
|
kmacGenerateKey,
|
||||||
|
kmacSignVerify,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ const {
|
||||||
EVP_PKEY_ML_KEM_1024,
|
EVP_PKEY_ML_KEM_1024,
|
||||||
kKeyVariantAES_OCB_128: hasAesOcbMode,
|
kKeyVariantAES_OCB_128: hasAesOcbMode,
|
||||||
Argon2Job,
|
Argon2Job,
|
||||||
|
KmacJob,
|
||||||
} = internalBinding('crypto');
|
} = internalBinding('crypto');
|
||||||
|
|
||||||
const { getOptionValue } = require('internal/options');
|
const { getOptionValue } = require('internal/options');
|
||||||
|
|
@ -283,6 +284,22 @@ const kAlgorithmDefinitions = {
|
||||||
'verify': null,
|
'verify': null,
|
||||||
'get key length': 'HmacImportParams',
|
'get key length': 'HmacImportParams',
|
||||||
},
|
},
|
||||||
|
'KMAC128': {
|
||||||
|
'generateKey': 'KmacKeyGenParams',
|
||||||
|
'exportKey': null,
|
||||||
|
'importKey': 'KmacImportParams',
|
||||||
|
'sign': 'KmacParams',
|
||||||
|
'verify': 'KmacParams',
|
||||||
|
'get key length': 'KmacImportParams',
|
||||||
|
},
|
||||||
|
'KMAC256': {
|
||||||
|
'generateKey': 'KmacKeyGenParams',
|
||||||
|
'exportKey': null,
|
||||||
|
'importKey': 'KmacImportParams',
|
||||||
|
'sign': 'KmacParams',
|
||||||
|
'verify': 'KmacParams',
|
||||||
|
'get key length': 'KmacImportParams',
|
||||||
|
},
|
||||||
'ML-DSA-44': {
|
'ML-DSA-44': {
|
||||||
'generateKey': null,
|
'generateKey': null,
|
||||||
'exportKey': null,
|
'exportKey': null,
|
||||||
|
|
@ -386,6 +403,8 @@ const conditionalAlgorithms = {
|
||||||
'cSHAKE256': !process.features.openssl_is_boringssl ||
|
'cSHAKE256': !process.features.openssl_is_boringssl ||
|
||||||
ArrayPrototypeIncludes(getHashes(), 'shake256'),
|
ArrayPrototypeIncludes(getHashes(), 'shake256'),
|
||||||
'Ed448': !process.features.openssl_is_boringssl,
|
'Ed448': !process.features.openssl_is_boringssl,
|
||||||
|
'KMAC128': !!KmacJob,
|
||||||
|
'KMAC256': !!KmacJob,
|
||||||
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
|
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
|
||||||
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
|
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
|
||||||
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
|
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
|
||||||
|
|
@ -411,6 +430,8 @@ const experimentalAlgorithms = [
|
||||||
'cSHAKE128',
|
'cSHAKE128',
|
||||||
'cSHAKE256',
|
'cSHAKE256',
|
||||||
'Ed448',
|
'Ed448',
|
||||||
|
'KMAC128',
|
||||||
|
'KMAC256',
|
||||||
'ML-DSA-44',
|
'ML-DSA-44',
|
||||||
'ML-DSA-65',
|
'ML-DSA-65',
|
||||||
'ML-DSA-87',
|
'ML-DSA-87',
|
||||||
|
|
@ -488,6 +509,9 @@ const simpleAlgorithmDictionaries = {
|
||||||
nonce: 'BufferSource',
|
nonce: 'BufferSource',
|
||||||
secretValue: 'BufferSource',
|
secretValue: 'BufferSource',
|
||||||
},
|
},
|
||||||
|
KmacParams: {
|
||||||
|
customization: 'BufferSource',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function validateMaxBufferLength(data, name) {
|
function validateMaxBufferLength(data, name) {
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,13 @@ async function generateKey(
|
||||||
result = await require('internal/crypto/ml_kem')
|
result = await require('internal/crypto/ml_kem')
|
||||||
.mlKemGenerateKey(algorithm, extractable, keyUsages);
|
.mlKemGenerateKey(algorithm, extractable, keyUsages);
|
||||||
break;
|
break;
|
||||||
|
case 'KMAC128':
|
||||||
|
// Fall through
|
||||||
|
case 'KMAC256':
|
||||||
|
resultType = 'CryptoKey';
|
||||||
|
result = await require('internal/crypto/mac')
|
||||||
|
.kmacGenerateKey(algorithm, extractable, keyUsages);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +285,13 @@ function getKeyLength({ name, length, hash }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
throw lazyDOMException('Invalid key length', 'OperationError');
|
throw lazyDOMException('Invalid key length', 'OperationError');
|
||||||
|
case 'KMAC128':
|
||||||
|
case 'KMAC256':
|
||||||
|
if (typeof length === 'number') {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name === 'KMAC128' ? 128 : 256;
|
||||||
case 'HKDF':
|
case 'HKDF':
|
||||||
case 'PBKDF2':
|
case 'PBKDF2':
|
||||||
case 'Argon2d':
|
case 'Argon2d':
|
||||||
|
|
@ -533,6 +547,10 @@ async function exportKeyRawSecret(key, format) {
|
||||||
return key[kKeyObject][kHandle].export().buffer;
|
return key[kKeyObject][kHandle].export().buffer;
|
||||||
case 'AES-OCB':
|
case 'AES-OCB':
|
||||||
// Fall through
|
// Fall through
|
||||||
|
case 'KMAC128':
|
||||||
|
// Fall through
|
||||||
|
case 'KMAC256':
|
||||||
|
// Fall through
|
||||||
case 'ChaCha20-Poly1305':
|
case 'ChaCha20-Poly1305':
|
||||||
if (format === 'raw-secret') {
|
if (format === 'raw-secret') {
|
||||||
return key[kKeyObject][kHandle].export().buffer;
|
return key[kKeyObject][kHandle].export().buffer;
|
||||||
|
|
@ -611,6 +629,13 @@ async function exportKeyJWK(key) {
|
||||||
if (alg) parameters.alg = alg;
|
if (alg) parameters.alg = alg;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'KMAC128':
|
||||||
|
parameters.alg = 'K128';
|
||||||
|
break;
|
||||||
|
case 'KMAC256': {
|
||||||
|
parameters.alg = 'K256';
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -772,9 +797,12 @@ async function importKey(
|
||||||
.cfrgImportKey(format, keyData, algorithm, extractable, keyUsages);
|
.cfrgImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||||
break;
|
break;
|
||||||
case 'HMAC':
|
case 'HMAC':
|
||||||
format = aliasKeyFormat(format);
|
// Fall through
|
||||||
|
case 'KMAC128':
|
||||||
|
// Fall through
|
||||||
|
case 'KMAC256':
|
||||||
result = require('internal/crypto/mac')
|
result = require('internal/crypto/mac')
|
||||||
.hmacImportKey(format, keyData, algorithm, extractable, keyUsages);
|
.macImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||||
break;
|
break;
|
||||||
case 'AES-CTR':
|
case 'AES-CTR':
|
||||||
// Fall through
|
// Fall through
|
||||||
|
|
@ -785,9 +813,6 @@ async function importKey(
|
||||||
case 'AES-KW':
|
case 'AES-KW':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'AES-OCB':
|
case 'AES-OCB':
|
||||||
if (algorithm.name !== 'AES-OCB') {
|
|
||||||
format = aliasKeyFormat(format);
|
|
||||||
}
|
|
||||||
result = require('internal/crypto/aes')
|
result = require('internal/crypto/aes')
|
||||||
.aesImportKey(algorithm, format, keyData, extractable, keyUsages);
|
.aesImportKey(algorithm, format, keyData, extractable, keyUsages);
|
||||||
break;
|
break;
|
||||||
|
|
@ -1024,6 +1049,11 @@ function signVerify(algorithm, key, data, signature) {
|
||||||
case 'ML-DSA-87':
|
case 'ML-DSA-87':
|
||||||
return require('internal/crypto/ml_dsa')
|
return require('internal/crypto/ml_dsa')
|
||||||
.mlDsaSignVerify(key, data, algorithm, signature);
|
.mlDsaSignVerify(key, data, algorithm, signature);
|
||||||
|
case 'KMAC128':
|
||||||
|
// Fall through
|
||||||
|
case 'KMAC256':
|
||||||
|
return require('internal/crypto/mac')
|
||||||
|
.kmacSignVerify(key, data, algorithm, signature);
|
||||||
}
|
}
|
||||||
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -461,32 +461,6 @@ converters.AesKeyGenParams = createDictionaryConverter(
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
converters.HmacKeyGenParams = createDictionaryConverter(
|
|
||||||
'HmacKeyGenParams', [
|
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
|
||||||
{
|
|
||||||
key: 'hash',
|
|
||||||
converter: converters.HashAlgorithmIdentifier,
|
|
||||||
validator: (V, dict) => ensureSHA(V, 'HmacKeyGenParams'),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'length',
|
|
||||||
converter: (V, opts) =>
|
|
||||||
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
|
||||||
validator: (V, dict) => validateHmacKeyAlgorithm(V),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
function validateHmacKeyAlgorithm(length) {
|
|
||||||
if (length === 0)
|
|
||||||
throw lazyDOMException('Zero-length key is not supported', 'DataError');
|
|
||||||
|
|
||||||
// The Web Crypto spec allows for key lengths that are not multiples of 8. We don't.
|
|
||||||
if (length % 8)
|
|
||||||
throw lazyDOMException('Unsupported algorithm.length', 'NotSupportedError');
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateZeroLength(parameterName) {
|
function validateZeroLength(parameterName) {
|
||||||
return (V, dict) => {
|
return (V, dict) => {
|
||||||
if (V.byteLength) {
|
if (V.byteLength) {
|
||||||
|
|
@ -527,22 +501,24 @@ converters.EcdsaParams = createDictionaryConverter(
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
converters.HmacImportParams = createDictionaryConverter(
|
for (const { 0: name, 1: zeroError } of [['HmacKeyGenParams', 'OperationError'], ['HmacImportParams', 'DataError']]) {
|
||||||
'HmacImportParams', [
|
converters[name] = createDictionaryConverter(
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
name, [
|
||||||
{
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
key: 'hash',
|
{
|
||||||
converter: converters.HashAlgorithmIdentifier,
|
key: 'hash',
|
||||||
validator: (V, dict) => ensureSHA(V, 'HmacImportParams'),
|
converter: converters.HashAlgorithmIdentifier,
|
||||||
required: true,
|
validator: (V, dict) => ensureSHA(V, name),
|
||||||
},
|
required: true,
|
||||||
{
|
},
|
||||||
key: 'length',
|
{
|
||||||
converter: (V, opts) =>
|
key: 'length',
|
||||||
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
converter: (V, opts) =>
|
||||||
validator: (V, dict) => validateHmacKeyAlgorithm(V),
|
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
||||||
},
|
validator: validateMacKeyLength(`${name}.length`, zeroError),
|
||||||
]);
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
const simpleDomStringKey = (key) => ({ key, converter: converters.DOMString });
|
const simpleDomStringKey = (key) => ({ key, converter: converters.DOMString });
|
||||||
|
|
||||||
|
|
@ -867,6 +843,50 @@ converters.Argon2Params = createDictionaryConverter(
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
function validateMacKeyLength(parameterName, zeroError) {
|
||||||
|
return (V) => {
|
||||||
|
if (V === 0)
|
||||||
|
throw lazyDOMException(`${parameterName} cannot be 0`, zeroError);
|
||||||
|
|
||||||
|
// The Web Crypto spec allows for key lengths that are not multiples of 8. We don't.
|
||||||
|
if (V % 8)
|
||||||
|
throw lazyDOMException(`Unsupported ${parameterName}`, 'NotSupportedError');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { 0: name, 1: zeroError } of [['KmacKeyGenParams', 'OperationError'], ['KmacImportParams', 'DataError']]) {
|
||||||
|
converters[name] = createDictionaryConverter(
|
||||||
|
name, [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: 'length',
|
||||||
|
converter: (V, opts) =>
|
||||||
|
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
||||||
|
validator: validateMacKeyLength(`${name}.length`, zeroError),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
converters.KmacParams = createDictionaryConverter(
|
||||||
|
'KmacParams', [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: 'length',
|
||||||
|
converter: (V, opts) =>
|
||||||
|
converters['unsigned long'](V, { ...opts, enforceRange: true }),
|
||||||
|
validator: (V, opts) => {
|
||||||
|
// The Web Crypto spec allows for KMAC output length that are not multiples of 8. We don't.
|
||||||
|
if (V % 8)
|
||||||
|
throw lazyDOMException('Unsupported KmacParams length', 'NotSupportedError');
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'customization',
|
||||||
|
converter: converters.BufferSource,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
converters,
|
converters,
|
||||||
requiredArguments,
|
requiredArguments,
|
||||||
|
|
|
||||||
2
node.gyp
2
node.gyp
|
|
@ -345,6 +345,7 @@
|
||||||
'src/crypto/crypto_ml_dsa.cc',
|
'src/crypto/crypto_ml_dsa.cc',
|
||||||
'src/crypto/crypto_kem.cc',
|
'src/crypto/crypto_kem.cc',
|
||||||
'src/crypto/crypto_hmac.cc',
|
'src/crypto/crypto_hmac.cc',
|
||||||
|
'src/crypto/crypto_kmac.cc',
|
||||||
'src/crypto/crypto_random.cc',
|
'src/crypto/crypto_random.cc',
|
||||||
'src/crypto/crypto_rsa.cc',
|
'src/crypto/crypto_rsa.cc',
|
||||||
'src/crypto/crypto_spkac.cc',
|
'src/crypto/crypto_spkac.cc',
|
||||||
|
|
@ -362,6 +363,7 @@
|
||||||
'src/crypto/crypto_clienthello-inl.h',
|
'src/crypto/crypto_clienthello-inl.h',
|
||||||
'src/crypto/crypto_dh.h',
|
'src/crypto/crypto_dh.h',
|
||||||
'src/crypto/crypto_hmac.h',
|
'src/crypto/crypto_hmac.h',
|
||||||
|
'src/crypto/crypto_kmac.h',
|
||||||
'src/crypto/crypto_rsa.h',
|
'src/crypto/crypto_rsa.h',
|
||||||
'src/crypto/crypto_spkac.h',
|
'src/crypto/crypto_spkac.h',
|
||||||
'src/crypto/crypto_util.h',
|
'src/crypto/crypto_util.h',
|
||||||
|
|
|
||||||
220
src/crypto/crypto_kmac.cc
Normal file
220
src/crypto/crypto_kmac.cc
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
#include "crypto/crypto_kmac.h"
|
||||||
|
#include "async_wrap-inl.h"
|
||||||
|
#include "node_internals.h"
|
||||||
|
#include "threadpoolwork-inl.h"
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
#include <openssl/core_names.h>
|
||||||
|
#include <openssl/params.h>
|
||||||
|
#include "crypto/crypto_keys.h"
|
||||||
|
#include "crypto/crypto_sig.h"
|
||||||
|
#include "ncrypto.h"
|
||||||
|
|
||||||
|
namespace node::crypto {
|
||||||
|
|
||||||
|
using ncrypto::EVPMacCtxPointer;
|
||||||
|
using ncrypto::EVPMacPointer;
|
||||||
|
using node::Utf8Value;
|
||||||
|
using v8::Boolean;
|
||||||
|
using v8::FunctionCallbackInfo;
|
||||||
|
using v8::JustVoid;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::Maybe;
|
||||||
|
using v8::MaybeLocal;
|
||||||
|
using v8::Nothing;
|
||||||
|
using v8::Object;
|
||||||
|
using v8::Uint32;
|
||||||
|
using v8::Value;
|
||||||
|
|
||||||
|
KmacConfig::KmacConfig(KmacConfig&& other) noexcept
|
||||||
|
: job_mode(other.job_mode),
|
||||||
|
mode(other.mode),
|
||||||
|
key(std::move(other.key)),
|
||||||
|
data(std::move(other.data)),
|
||||||
|
signature(std::move(other.signature)),
|
||||||
|
customization(std::move(other.customization)),
|
||||||
|
variant(other.variant),
|
||||||
|
length(other.length) {}
|
||||||
|
|
||||||
|
KmacConfig& KmacConfig::operator=(KmacConfig&& other) noexcept {
|
||||||
|
if (&other == this) return *this;
|
||||||
|
this->~KmacConfig();
|
||||||
|
return *new (this) KmacConfig(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KmacConfig::MemoryInfo(MemoryTracker* tracker) const {
|
||||||
|
tracker->TrackField("key", key);
|
||||||
|
// If the job is sync, then the KmacConfig does not own the data.
|
||||||
|
if (job_mode == kCryptoJobAsync) {
|
||||||
|
tracker->TrackFieldWithSize("data", data.size());
|
||||||
|
tracker->TrackFieldWithSize("signature", signature.size());
|
||||||
|
tracker->TrackFieldWithSize("customization", customization.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<void> KmacTraits::AdditionalConfig(
|
||||||
|
CryptoJobMode mode,
|
||||||
|
const FunctionCallbackInfo<Value>& args,
|
||||||
|
unsigned int offset,
|
||||||
|
KmacConfig* params) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
|
params->job_mode = mode;
|
||||||
|
|
||||||
|
CHECK(args[offset]->IsUint32()); // SignConfiguration::Mode
|
||||||
|
params->mode =
|
||||||
|
static_cast<SignConfiguration::Mode>(args[offset].As<Uint32>()->Value());
|
||||||
|
|
||||||
|
CHECK(args[offset + 1]->IsObject()); // Key
|
||||||
|
KeyObjectHandle* key;
|
||||||
|
ASSIGN_OR_RETURN_UNWRAP(&key, args[offset + 1], Nothing<void>());
|
||||||
|
params->key = key->Data().addRef();
|
||||||
|
|
||||||
|
CHECK(args[offset + 2]->IsString()); // Algorithm name
|
||||||
|
Utf8Value algorithm_name(env->isolate(), args[offset + 2]);
|
||||||
|
std::string_view algorithm_str = algorithm_name.ToStringView();
|
||||||
|
|
||||||
|
// Convert string to enum and validate
|
||||||
|
if (algorithm_str == OSSL_MAC_NAME_KMAC128) {
|
||||||
|
params->variant = KmacVariant::KMAC128;
|
||||||
|
} else if (algorithm_str == OSSL_MAC_NAME_KMAC256) {
|
||||||
|
params->variant = KmacVariant::KMAC256;
|
||||||
|
} else {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customization string (may be empty or undefined).
|
||||||
|
if (!args[offset + 3]->IsUndefined()) {
|
||||||
|
ArrayBufferOrViewContents<char> customization(args[offset + 3]);
|
||||||
|
if (!customization.CheckSizeInt32()) [[unlikely]] {
|
||||||
|
THROW_ERR_OUT_OF_RANGE(env, "customization is too big");
|
||||||
|
return Nothing<void>();
|
||||||
|
}
|
||||||
|
params->customization = mode == kCryptoJobAsync
|
||||||
|
? customization.ToCopy()
|
||||||
|
: customization.ToByteSource();
|
||||||
|
}
|
||||||
|
// If undefined, params->customization remains uninitialized (size 0).
|
||||||
|
|
||||||
|
CHECK(args[offset + 4]->IsUint32()); // Length
|
||||||
|
params->length = args[offset + 4].As<Uint32>()->Value();
|
||||||
|
|
||||||
|
ArrayBufferOrViewContents<char> data(args[offset + 5]);
|
||||||
|
if (!data.CheckSizeInt32()) [[unlikely]] {
|
||||||
|
THROW_ERR_OUT_OF_RANGE(env, "data is too big");
|
||||||
|
return Nothing<void>();
|
||||||
|
}
|
||||||
|
params->data = mode == kCryptoJobAsync ? data.ToCopy() : data.ToByteSource();
|
||||||
|
|
||||||
|
if (!args[offset + 6]->IsUndefined()) {
|
||||||
|
ArrayBufferOrViewContents<char> signature(args[offset + 6]);
|
||||||
|
if (!signature.CheckSizeInt32()) [[unlikely]] {
|
||||||
|
THROW_ERR_OUT_OF_RANGE(env, "signature is too big");
|
||||||
|
return Nothing<void>();
|
||||||
|
}
|
||||||
|
params->signature =
|
||||||
|
mode == kCryptoJobAsync ? signature.ToCopy() : signature.ToByteSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
return JustVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KmacTraits::DeriveBits(Environment* env,
|
||||||
|
const KmacConfig& params,
|
||||||
|
ByteSource* out,
|
||||||
|
CryptoJobMode mode) {
|
||||||
|
if (params.length == 0) {
|
||||||
|
*out = ByteSource();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the key data.
|
||||||
|
const void* key_data = params.key.GetSymmetricKey();
|
||||||
|
size_t key_size = params.key.GetSymmetricKeySize();
|
||||||
|
|
||||||
|
if (key_size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the KMAC algorithm
|
||||||
|
auto mac = EVPMacPointer::Fetch((params.variant == KmacVariant::KMAC128)
|
||||||
|
? OSSL_MAC_NAME_KMAC128
|
||||||
|
: OSSL_MAC_NAME_KMAC256);
|
||||||
|
if (!mac) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create MAC context
|
||||||
|
auto mac_ctx = EVPMacCtxPointer::New(mac.get());
|
||||||
|
if (!mac_ctx) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up parameters.
|
||||||
|
OSSL_PARAM params_array[3]; // Max 3: size + customization + end
|
||||||
|
size_t params_count = 0;
|
||||||
|
|
||||||
|
// Set output length (always required for KMAC).
|
||||||
|
size_t outlen = params.length;
|
||||||
|
params_array[params_count++] =
|
||||||
|
OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &outlen);
|
||||||
|
|
||||||
|
// Set customization if provided.
|
||||||
|
if (params.customization.size() > 0) {
|
||||||
|
params_array[params_count++] = OSSL_PARAM_construct_octet_string(
|
||||||
|
OSSL_MAC_PARAM_CUSTOM,
|
||||||
|
const_cast<void*>(params.customization.data()),
|
||||||
|
params.customization.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
params_array[params_count] = OSSL_PARAM_construct_end();
|
||||||
|
|
||||||
|
// Initialize the MAC context.
|
||||||
|
if (!mac_ctx.init(ncrypto::Buffer<const void>(key_data, key_size),
|
||||||
|
params_array)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update with data.
|
||||||
|
if (!mac_ctx.update(ncrypto::Buffer<const void>(params.data.data(),
|
||||||
|
params.data.size()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize and get the result.
|
||||||
|
auto result = mac_ctx.final(params.length);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buffer = result.release();
|
||||||
|
*out = ByteSource::Allocated(buffer.data, buffer.len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<Value> KmacTraits::EncodeOutput(Environment* env,
|
||||||
|
const KmacConfig& params,
|
||||||
|
ByteSource* out) {
|
||||||
|
switch (params.mode) {
|
||||||
|
case SignConfiguration::Mode::Sign:
|
||||||
|
return out->ToArrayBuffer(env);
|
||||||
|
case SignConfiguration::Mode::Verify:
|
||||||
|
return Boolean::New(
|
||||||
|
env->isolate(),
|
||||||
|
out->size() > 0 && out->size() == params.signature.size() &&
|
||||||
|
memcmp(out->data(), params.signature.data(), out->size()) == 0);
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kmac::Initialize(Environment* env, Local<Object> target) {
|
||||||
|
KmacJob::Initialize(env, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kmac::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||||
|
KmacJob::RegisterExternalReferences(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace node::crypto
|
||||||
|
|
||||||
|
#endif
|
||||||
79
src/crypto/crypto_kmac.h
Normal file
79
src/crypto/crypto_kmac.h
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#ifndef SRC_CRYPTO_CRYPTO_KMAC_H_
|
||||||
|
#define SRC_CRYPTO_CRYPTO_KMAC_H_
|
||||||
|
|
||||||
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "crypto/crypto_keys.h"
|
||||||
|
#include "crypto/crypto_sig.h"
|
||||||
|
#include "crypto/crypto_util.h"
|
||||||
|
|
||||||
|
namespace node::crypto {
|
||||||
|
|
||||||
|
// KMAC (Keccak Message Authentication Code) is available since OpenSSL 3.0.
|
||||||
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
|
||||||
|
enum class KmacVariant { KMAC128, KMAC256 };
|
||||||
|
|
||||||
|
struct KmacConfig final : public MemoryRetainer {
|
||||||
|
CryptoJobMode job_mode;
|
||||||
|
SignConfiguration::Mode mode;
|
||||||
|
KeyObjectData key;
|
||||||
|
ByteSource data;
|
||||||
|
ByteSource signature;
|
||||||
|
ByteSource customization;
|
||||||
|
KmacVariant variant;
|
||||||
|
uint32_t length; // Output length in bytes
|
||||||
|
|
||||||
|
KmacConfig() = default;
|
||||||
|
|
||||||
|
explicit KmacConfig(KmacConfig&& other) noexcept;
|
||||||
|
|
||||||
|
KmacConfig& operator=(KmacConfig&& other) noexcept;
|
||||||
|
|
||||||
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
||||||
|
SET_MEMORY_INFO_NAME(KmacConfig)
|
||||||
|
SET_SELF_SIZE(KmacConfig)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KmacTraits final {
|
||||||
|
using AdditionalParameters = KmacConfig;
|
||||||
|
static constexpr const char* JobName = "KmacJob";
|
||||||
|
static constexpr AsyncWrap::ProviderType Provider =
|
||||||
|
AsyncWrap::PROVIDER_SIGNREQUEST;
|
||||||
|
|
||||||
|
static v8::Maybe<void> AdditionalConfig(
|
||||||
|
CryptoJobMode mode,
|
||||||
|
const v8::FunctionCallbackInfo<v8::Value>& args,
|
||||||
|
unsigned int offset,
|
||||||
|
KmacConfig* params);
|
||||||
|
|
||||||
|
static bool DeriveBits(Environment* env,
|
||||||
|
const KmacConfig& params,
|
||||||
|
ByteSource* out,
|
||||||
|
CryptoJobMode mode);
|
||||||
|
|
||||||
|
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||||
|
const KmacConfig& params,
|
||||||
|
ByteSource* out);
|
||||||
|
};
|
||||||
|
|
||||||
|
using KmacJob = DeriveBitsJob<KmacTraits>;
|
||||||
|
|
||||||
|
namespace Kmac {
|
||||||
|
void Initialize(Environment* env, v8::Local<v8::Object> target);
|
||||||
|
void RegisterExternalReferences(ExternalReferenceRegistry* registry);
|
||||||
|
} // namespace Kmac
|
||||||
|
|
||||||
|
#else
|
||||||
|
// If there is no KMAC support, provide empty namespace functions.
|
||||||
|
namespace Kmac {
|
||||||
|
void Initialize(Environment* env, v8::Local<v8::Object> target) {}
|
||||||
|
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {}
|
||||||
|
} // namespace Kmac
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace node::crypto
|
||||||
|
|
||||||
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
#endif // SRC_CRYPTO_CRYPTO_KMAC_H_
|
||||||
|
|
@ -66,11 +66,13 @@ namespace crypto {
|
||||||
#define ARGON2_NAMESPACE_LIST(V)
|
#define ARGON2_NAMESPACE_LIST(V)
|
||||||
#endif // !OPENSSL_NO_ARGON2 && OpenSSL >= 3.2
|
#endif // !OPENSSL_NO_ARGON2 && OpenSSL >= 3.2
|
||||||
|
|
||||||
// KEM functionality requires OpenSSL 3.0.0 or later
|
// KEM and KMAC functionality requires OpenSSL 3.0.0 or later
|
||||||
#if OPENSSL_VERSION_MAJOR >= 3
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
#define KEM_NAMESPACE_LIST(V) V(KEM)
|
#define KEM_NAMESPACE_LIST(V) V(KEM)
|
||||||
|
#define KMAC_NAMESPACE_LIST(V) V(Kmac)
|
||||||
#else
|
#else
|
||||||
#define KEM_NAMESPACE_LIST(V)
|
#define KEM_NAMESPACE_LIST(V)
|
||||||
|
#define KMAC_NAMESPACE_LIST(V)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OPENSSL_NO_SCRYPT
|
#ifdef OPENSSL_NO_SCRYPT
|
||||||
|
|
@ -83,6 +85,7 @@ namespace crypto {
|
||||||
CRYPTO_NAMESPACE_LIST_BASE(V) \
|
CRYPTO_NAMESPACE_LIST_BASE(V) \
|
||||||
ARGON2_NAMESPACE_LIST(V) \
|
ARGON2_NAMESPACE_LIST(V) \
|
||||||
KEM_NAMESPACE_LIST(V) \
|
KEM_NAMESPACE_LIST(V) \
|
||||||
|
KMAC_NAMESPACE_LIST(V) \
|
||||||
SCRYPT_NAMESPACE_LIST(V)
|
SCRYPT_NAMESPACE_LIST(V)
|
||||||
|
|
||||||
void Initialize(Local<Object> target,
|
void Initialize(Local<Object> target,
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
#include "crypto/crypto_hmac.h"
|
#include "crypto/crypto_hmac.h"
|
||||||
#if OPENSSL_VERSION_MAJOR >= 3
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
#include "crypto/crypto_kem.h"
|
#include "crypto/crypto_kem.h"
|
||||||
|
#include "crypto/crypto_kmac.h"
|
||||||
#endif
|
#endif
|
||||||
#include "crypto/crypto_keygen.h"
|
#include "crypto/crypto_keygen.h"
|
||||||
#include "crypto/crypto_keys.h"
|
#include "crypto/crypto_keys.h"
|
||||||
|
|
|
||||||
120
test/fixtures/crypto/kmac.js
vendored
Normal file
120
test/fixtures/crypto/kmac.js
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
// KMAC test vectors from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf
|
||||||
|
const vectors = [
|
||||||
|
{
|
||||||
|
// Sample #1 - KMAC128, no customization
|
||||||
|
algorithm: 'KMAC128',
|
||||||
|
key: Buffer.from([
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||||
|
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
||||||
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
|
]),
|
||||||
|
data: Buffer.from([0x00, 0x01, 0x02, 0x03]),
|
||||||
|
customization: undefined,
|
||||||
|
length: 256,
|
||||||
|
expected: Buffer.from([
|
||||||
|
0xe5, 0x78, 0x0b, 0x0d, 0x3e, 0xa6, 0xf7, 0xd3, 0xa4, 0x29, 0xc5, 0x70,
|
||||||
|
0x6a, 0xa4, 0x3a, 0x00, 0xfa, 0xdb, 0xd7, 0xd4, 0x96, 0x28, 0x83, 0x9e,
|
||||||
|
0x31, 0x87, 0x24, 0x3f, 0x45, 0x6e, 0xe1, 0x4e,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Sample #2 - KMAC128, with customization
|
||||||
|
algorithm: 'KMAC128',
|
||||||
|
key: Buffer.from([
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||||
|
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
||||||
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
|
]),
|
||||||
|
data: Buffer.from([0x00, 0x01, 0x02, 0x03]),
|
||||||
|
customization: Buffer.from('My Tagged Application'),
|
||||||
|
length: 256,
|
||||||
|
expected: Buffer.from([
|
||||||
|
0x3b, 0x1f, 0xba, 0x96, 0x3c, 0xd8, 0xb0, 0xb5, 0x9e, 0x8c, 0x1a, 0x6d,
|
||||||
|
0x71, 0x88, 0x8b, 0x71, 0x43, 0x65, 0x1a, 0xf8, 0xba, 0x0a, 0x70, 0x70,
|
||||||
|
0xc0, 0x97, 0x9e, 0x28, 0x11, 0x32, 0x4a, 0xa5,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Sample #3 - KMAC128, large data, with customization
|
||||||
|
algorithm: 'KMAC128',
|
||||||
|
key: Buffer.from([
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||||
|
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
||||||
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
|
]),
|
||||||
|
data: Buffer.from(Array.from({ length: 200 }, (_, i) => i)), // 0x00-0xC7
|
||||||
|
customization: Buffer.from('My Tagged Application'),
|
||||||
|
length: 256,
|
||||||
|
expected: Buffer.from([
|
||||||
|
0x1f, 0x5b, 0x4e, 0x6c, 0xca, 0x02, 0x20, 0x9e, 0x0d, 0xcb, 0x5c, 0xa6,
|
||||||
|
0x35, 0xb8, 0x9a, 0x15, 0xe2, 0x71, 0xec, 0xc7, 0x60, 0x07, 0x1d, 0xfd,
|
||||||
|
0x80, 0x5f, 0xaa, 0x38, 0xf9, 0x72, 0x92, 0x30,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Sample #4 - KMAC256, with customization, 512-bit output
|
||||||
|
algorithm: 'KMAC256',
|
||||||
|
key: Buffer.from([
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||||
|
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
||||||
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
|
]),
|
||||||
|
data: Buffer.from([0x00, 0x01, 0x02, 0x03]),
|
||||||
|
customization: Buffer.from('My Tagged Application'),
|
||||||
|
length: 512,
|
||||||
|
expected: Buffer.from([
|
||||||
|
0x20, 0xc5, 0x70, 0xc3, 0x13, 0x46, 0xf7, 0x03, 0xc9, 0xac, 0x36, 0xc6,
|
||||||
|
0x1c, 0x03, 0xcb, 0x64, 0xc3, 0x97, 0x0d, 0x0c, 0xfc, 0x78, 0x7e, 0x9b,
|
||||||
|
0x79, 0x59, 0x9d, 0x27, 0x3a, 0x68, 0xd2, 0xf7, 0xf6, 0x9d, 0x4c, 0xc3,
|
||||||
|
0xde, 0x9d, 0x10, 0x4a, 0x35, 0x16, 0x89, 0xf2, 0x7c, 0xf6, 0xf5, 0x95,
|
||||||
|
0x1f, 0x01, 0x03, 0xf3, 0x3f, 0x4f, 0x24, 0x87, 0x10, 0x24, 0xd9, 0xc2,
|
||||||
|
0x77, 0x73, 0xa8, 0xdd,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Sample #5 - KMAC256, large data, no customization, 512-bit output
|
||||||
|
algorithm: 'KMAC256',
|
||||||
|
key: Buffer.from([
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||||
|
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
||||||
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
|
]),
|
||||||
|
data: Buffer.from(Array.from({ length: 200 }, (_, i) => i)), // 0x00-0xC7
|
||||||
|
customization: undefined,
|
||||||
|
length: 512,
|
||||||
|
expected: Buffer.from([
|
||||||
|
0x75, 0x35, 0x8c, 0xf3, 0x9e, 0x41, 0x49, 0x4e, 0x94, 0x97, 0x07, 0x92,
|
||||||
|
0x7c, 0xee, 0x0a, 0xf2, 0x0a, 0x3f, 0xf5, 0x53, 0x90, 0x4c, 0x86, 0xb0,
|
||||||
|
0x8f, 0x21, 0xcc, 0x41, 0x4b, 0xcf, 0xd6, 0x91, 0x58, 0x9d, 0x27, 0xcf,
|
||||||
|
0x5e, 0x15, 0x36, 0x9c, 0xbb, 0xff, 0x8b, 0x9a, 0x4c, 0x2e, 0xb1, 0x78,
|
||||||
|
0x00, 0x85, 0x5d, 0x02, 0x35, 0xff, 0x63, 0x5d, 0xa8, 0x25, 0x33, 0xec,
|
||||||
|
0x6b, 0x75, 0x9b, 0x69,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Sample #6 - KMAC256, large data, with customization, 512-bit output
|
||||||
|
algorithm: 'KMAC256',
|
||||||
|
key: Buffer.from([
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||||
|
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
|
||||||
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
|
]),
|
||||||
|
data: Buffer.from(Array.from({ length: 200 }, (_, i) => i)), // 0x00-0xC7
|
||||||
|
customization: Buffer.from('My Tagged Application'),
|
||||||
|
length: 512,
|
||||||
|
expected: Buffer.from([
|
||||||
|
0xb5, 0x86, 0x18, 0xf7, 0x1f, 0x92, 0xe1, 0xd5, 0x6c, 0x1b, 0x8c, 0x55,
|
||||||
|
0xdd, 0xd7, 0xcd, 0x18, 0x8b, 0x97, 0xb4, 0xca, 0x4d, 0x99, 0x83, 0x1e,
|
||||||
|
0xb2, 0x69, 0x9a, 0x83, 0x7d, 0xa2, 0xe4, 0xd9, 0x70, 0xfb, 0xac, 0xfd,
|
||||||
|
0xe5, 0x00, 0x33, 0xae, 0xa5, 0x85, 0xf1, 0xa2, 0x70, 0x85, 0x10, 0xc3,
|
||||||
|
0x2d, 0x07, 0x88, 0x08, 0x01, 0xbd, 0x18, 0x28, 0x98, 0xfe, 0x47, 0x68,
|
||||||
|
0x76, 0xfc, 0x89, 0x65,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return vectors;
|
||||||
|
};
|
||||||
|
|
@ -8,6 +8,7 @@ const shake128 = crypto.getHashes().includes('shake128');
|
||||||
const shake256 = crypto.getHashes().includes('shake256');
|
const shake256 = crypto.getHashes().includes('shake256');
|
||||||
const chacha = crypto.getCiphers().includes('chacha20-poly1305');
|
const chacha = crypto.getCiphers().includes('chacha20-poly1305');
|
||||||
const ocb = hasOpenSSL(3);
|
const ocb = hasOpenSSL(3);
|
||||||
|
const kmac = hasOpenSSL(3);
|
||||||
|
|
||||||
const { subtle } = globalThis.crypto;
|
const { subtle } = globalThis.crypto;
|
||||||
const X25519 = await subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']);
|
const X25519 = await subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']);
|
||||||
|
|
@ -34,6 +35,10 @@ export const vectors = {
|
||||||
[false, 'Argon2d'],
|
[false, 'Argon2d'],
|
||||||
[false, 'Argon2i'],
|
[false, 'Argon2i'],
|
||||||
[false, 'Argon2id'],
|
[false, 'Argon2id'],
|
||||||
|
[false, 'KMAC128'],
|
||||||
|
[false, 'KMAC256'],
|
||||||
|
[kmac, { name: 'KMAC128', length: 256 }],
|
||||||
|
[kmac, { name: 'KMAC256', length: 256 }],
|
||||||
],
|
],
|
||||||
'generateKey': [
|
'generateKey': [
|
||||||
[pqc, 'ML-DSA-44'],
|
[pqc, 'ML-DSA-44'],
|
||||||
|
|
@ -47,6 +52,14 @@ export const vectors = {
|
||||||
[false, 'Argon2d'],
|
[false, 'Argon2d'],
|
||||||
[false, 'Argon2i'],
|
[false, 'Argon2i'],
|
||||||
[false, 'Argon2id'],
|
[false, 'Argon2id'],
|
||||||
|
[kmac, 'KMAC128'],
|
||||||
|
[kmac, 'KMAC256'],
|
||||||
|
[kmac, { name: 'KMAC128', length: 256 }],
|
||||||
|
[kmac, { name: 'KMAC256', length: 128 }],
|
||||||
|
[false, { name: 'KMAC128', length: 0 }],
|
||||||
|
[false, { name: 'KMAC256', length: 0 }],
|
||||||
|
[false, { name: 'KMAC128', length: 1 }],
|
||||||
|
[false, { name: 'KMAC256', length: 1 }],
|
||||||
],
|
],
|
||||||
'importKey': [
|
'importKey': [
|
||||||
[pqc, 'ML-DSA-44'],
|
[pqc, 'ML-DSA-44'],
|
||||||
|
|
@ -60,6 +73,14 @@ export const vectors = {
|
||||||
[argon2, 'Argon2d'],
|
[argon2, 'Argon2d'],
|
||||||
[argon2, 'Argon2i'],
|
[argon2, 'Argon2i'],
|
||||||
[argon2, 'Argon2id'],
|
[argon2, 'Argon2id'],
|
||||||
|
[kmac, 'KMAC128'],
|
||||||
|
[kmac, 'KMAC256'],
|
||||||
|
[kmac, { name: 'KMAC128', length: 256 }],
|
||||||
|
[kmac, { name: 'KMAC256', length: 128 }],
|
||||||
|
[false, { name: 'KMAC128', length: 0 }],
|
||||||
|
[false, { name: 'KMAC256', length: 0 }],
|
||||||
|
[false, { name: 'KMAC128', length: 1 }],
|
||||||
|
[false, { name: 'KMAC256', length: 1 }],
|
||||||
],
|
],
|
||||||
'exportKey': [
|
'exportKey': [
|
||||||
[pqc, 'ML-DSA-44'],
|
[pqc, 'ML-DSA-44'],
|
||||||
|
|
@ -73,6 +94,8 @@ export const vectors = {
|
||||||
[false, 'Argon2d'],
|
[false, 'Argon2d'],
|
||||||
[false, 'Argon2i'],
|
[false, 'Argon2i'],
|
||||||
[false, 'Argon2id'],
|
[false, 'Argon2id'],
|
||||||
|
[kmac, 'KMAC128'],
|
||||||
|
[kmac, 'KMAC256'],
|
||||||
],
|
],
|
||||||
'getPublicKey': [
|
'getPublicKey': [
|
||||||
[true, 'RSA-OAEP'],
|
[true, 'RSA-OAEP'],
|
||||||
|
|
@ -99,6 +122,8 @@ export const vectors = {
|
||||||
[false, 'Argon2d'],
|
[false, 'Argon2d'],
|
||||||
[false, 'Argon2i'],
|
[false, 'Argon2i'],
|
||||||
[false, 'Argon2id'],
|
[false, 'Argon2id'],
|
||||||
|
[false, 'KMAC128'],
|
||||||
|
[false, 'KMAC256'],
|
||||||
],
|
],
|
||||||
'deriveKey': [
|
'deriveKey': [
|
||||||
[argon2,
|
[argon2,
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ function assertCryptoKey(cryptoKey, keyObject, algorithm, extractable, usages) {
|
||||||
hmac.toCryptoKey({ name: algorithm, hash: 'SHA-256', length: 0 }, true, usages);
|
hmac.toCryptoKey({ name: algorithm, hash: 'SHA-256', length: 0 }, true, usages);
|
||||||
}, {
|
}, {
|
||||||
name: 'DataError',
|
name: 'DataError',
|
||||||
message: 'Zero-length key is not supported',
|
message: 'HmacImportParams.length cannot be 0',
|
||||||
});
|
});
|
||||||
const cryptoKey = hmac.toCryptoKey({ name: algorithm, hash }, extractable, usages);
|
const cryptoKey = hmac.toCryptoKey({ name: algorithm, hash }, extractable, usages);
|
||||||
assertCryptoKey(cryptoKey, hmac, algorithm, extractable, usages);
|
assertCryptoKey(cryptoKey, hmac, algorithm, extractable, usages);
|
||||||
|
|
@ -205,3 +205,38 @@ if (hasOpenSSL(3, 5)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasOpenSSL(3)) {
|
||||||
|
for (const algorithm of ['KMAC128', 'KMAC256']) {
|
||||||
|
const hmac = createSecretKey(randomBytes(32));
|
||||||
|
const usages = ['sign', 'verify'];
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
createSecretKey(Buffer.alloc(0)).toCryptoKey({ name: algorithm }, true, usages);
|
||||||
|
}, {
|
||||||
|
name: 'DataError',
|
||||||
|
message: 'Zero-length key is not supported',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
hmac.toCryptoKey({
|
||||||
|
name: algorithm,
|
||||||
|
}, true, []);
|
||||||
|
}, {
|
||||||
|
name: 'SyntaxError',
|
||||||
|
message: 'Usages cannot be empty when importing a secret key.'
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const extractable of [true, false]) {
|
||||||
|
assert.throws(() => {
|
||||||
|
hmac.toCryptoKey({ name: algorithm, length: 0 }, true, usages);
|
||||||
|
}, {
|
||||||
|
name: 'DataError',
|
||||||
|
message: 'KmacImportParams.length cannot be 0',
|
||||||
|
});
|
||||||
|
const cryptoKey = hmac.toCryptoKey({ name: algorithm }, extractable, usages);
|
||||||
|
assertCryptoKey(cryptoKey, hmac, algorithm, extractable, usages);
|
||||||
|
assert.strictEqual(cryptoKey.algorithm.length, 256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
// Flags: --expose-internals --no-warnings
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
|
|
@ -6,6 +5,8 @@ const common = require('../common');
|
||||||
if (!common.hasCrypto)
|
if (!common.hasCrypto)
|
||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const { hasOpenSSL } = require('../common/crypto');
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const { subtle } = globalThis.crypto;
|
const { subtle } = globalThis.crypto;
|
||||||
const { KeyObject } = require('crypto');
|
const { KeyObject } = require('crypto');
|
||||||
|
|
@ -167,6 +168,15 @@ const { KeyObject } = require('crypto');
|
||||||
// [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
|
// [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (hasOpenSSL(3)) {
|
||||||
|
vectors.push(
|
||||||
|
['KMAC128', 'sign', 128],
|
||||||
|
[{ name: 'KMAC128', length: 384 }, 'sign', 384],
|
||||||
|
['KMAC256', 'sign', 256],
|
||||||
|
[{ name: 'KMAC256', length: 384 }, 'sign', 384],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const keyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, false, ['deriveKey']);
|
const keyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, false, ['deriveKey']);
|
||||||
for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
|
for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
|
||||||
|
|
@ -183,7 +193,7 @@ const { KeyObject } = require('crypto');
|
||||||
} else {
|
} else {
|
||||||
assert.strictEqual(result.status, 'fulfilled');
|
assert.strictEqual(result.status, 'fulfilled');
|
||||||
const derived = result.value;
|
const derived = result.value;
|
||||||
if (derived.algorithm.name === 'HMAC') {
|
if (derived.algorithm.name === 'HMAC' || derived.algorithm.name.startsWith('KMAC')) {
|
||||||
assert.strictEqual(derived.algorithm.length, expected);
|
assert.strictEqual(derived.algorithm.length, expected);
|
||||||
} else {
|
} else {
|
||||||
// KDFs cannot be exportable and do not indicate their length
|
// KDFs cannot be exportable and do not indicate their length
|
||||||
|
|
@ -211,6 +221,15 @@ const { KeyObject } = require('crypto');
|
||||||
// [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
|
// [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (hasOpenSSL(3)) {
|
||||||
|
vectors.push(
|
||||||
|
['KMAC128', 'sign', 128],
|
||||||
|
[{ name: 'KMAC128', length: 384 }, 'sign', 384],
|
||||||
|
['KMAC256', 'sign', 256],
|
||||||
|
[{ name: 'KMAC256', length: 384 }, 'sign', 384],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
|
for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
|
||||||
const derived = await subtle.deriveKey(
|
const derived = await subtle.deriveKey(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ const fixtures = require('../common/fixtures');
|
||||||
if (!common.hasCrypto)
|
if (!common.hasCrypto)
|
||||||
common.skip('missing crypto');
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const { hasOpenSSL } = require('../common/crypto');
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const { subtle } = globalThis.crypto;
|
const { subtle } = globalThis.crypto;
|
||||||
const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto');
|
const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto');
|
||||||
|
|
@ -53,7 +55,7 @@ const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto')
|
||||||
hash: 'SHA-256'
|
hash: 'SHA-256'
|
||||||
}, false, ['deriveBits']), {
|
}, false, ['deriveBits']), {
|
||||||
name: 'SyntaxError',
|
name: 'SyntaxError',
|
||||||
message: 'Unsupported key usage for an HMAC key'
|
message: 'Unsupported key usage for HMAC key'
|
||||||
});
|
});
|
||||||
await assert.rejects(
|
await assert.rejects(
|
||||||
subtle.importKey('raw', keyData, {
|
subtle.importKey('raw', keyData, {
|
||||||
|
|
@ -62,7 +64,7 @@ const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto')
|
||||||
length: 0
|
length: 0
|
||||||
}, false, ['sign', 'verify']), {
|
}, false, ['sign', 'verify']), {
|
||||||
name: 'DataError',
|
name: 'DataError',
|
||||||
message: 'Zero-length key is not supported'
|
message: 'HmacImportParams.length cannot be 0'
|
||||||
});
|
});
|
||||||
await assert.rejects(
|
await assert.rejects(
|
||||||
subtle.importKey('raw', keyData, {
|
subtle.importKey('raw', keyData, {
|
||||||
|
|
@ -71,7 +73,7 @@ const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto')
|
||||||
length: 1
|
length: 1
|
||||||
}, false, ['sign', 'verify']), {
|
}, false, ['sign', 'verify']), {
|
||||||
name: 'NotSupportedError',
|
name: 'NotSupportedError',
|
||||||
message: 'Unsupported algorithm.length'
|
message: 'Unsupported HmacImportParams.length'
|
||||||
});
|
});
|
||||||
await assert.rejects(
|
await assert.rejects(
|
||||||
subtle.importKey('jwk', null, {
|
subtle.importKey('jwk', null, {
|
||||||
|
|
@ -97,7 +99,6 @@ const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto')
|
||||||
hash: 'SHA-256'
|
hash: 'SHA-256'
|
||||||
}, true, ['sign', 'verify']);
|
}, true, ['sign', 'verify']);
|
||||||
|
|
||||||
|
|
||||||
assert.strictEqual(key.algorithm, key.algorithm);
|
assert.strictEqual(key.algorithm, key.algorithm);
|
||||||
assert.strictEqual(key.usages, key.usages);
|
assert.strictEqual(key.usages, key.usages);
|
||||||
|
|
||||||
|
|
@ -111,11 +112,44 @@ const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto')
|
||||||
assert.deepStrictEqual(jwk.key_ops, ['sign', 'verify']);
|
assert.deepStrictEqual(jwk.key_ops, ['sign', 'verify']);
|
||||||
assert(jwk.ext);
|
assert(jwk.ext);
|
||||||
assert.strictEqual(jwk.kty, 'oct');
|
assert.strictEqual(jwk.kty, 'oct');
|
||||||
|
assert.strictEqual(jwk.alg, 'HS256');
|
||||||
|
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
Buffer.from(jwk.k, 'base64').toString('hex'),
|
Buffer.from(jwk.k, 'base64').toString('hex'),
|
||||||
Buffer.from(raw).toString('hex'));
|
Buffer.from(raw).toString('hex'));
|
||||||
|
|
||||||
|
await subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
jwk,
|
||||||
|
{
|
||||||
|
name: 'HMAC',
|
||||||
|
hash: 'SHA-256'
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
['sign', 'verify']);
|
||||||
|
|
||||||
|
await subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
{ ...jwk, alg: undefined },
|
||||||
|
{
|
||||||
|
name: 'HMAC',
|
||||||
|
hash: 'SHA-256'
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
['sign', 'verify']);
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
{ ...jwk, alg: 'HS384' },
|
||||||
|
{
|
||||||
|
name: 'HMAC',
|
||||||
|
hash: 'SHA-256'
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
['sign', 'verify']),
|
||||||
|
{ name: 'DataError', message: 'JWK "alg" does not match the requested algorithm' });
|
||||||
|
|
||||||
await assert.rejects(
|
await assert.rejects(
|
||||||
subtle.importKey(
|
subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
|
|
@ -132,6 +166,76 @@ const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto')
|
||||||
test().then(common.mustCall());
|
test().then(common.mustCall());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Import/Export KMAC Secret Key
|
||||||
|
if (hasOpenSSL(3)) {
|
||||||
|
async function test(name) {
|
||||||
|
const keyData = globalThis.crypto.getRandomValues(new Uint8Array(32));
|
||||||
|
const key = await subtle.importKey(
|
||||||
|
'raw-secret',
|
||||||
|
keyData, name, true, ['sign', 'verify']);
|
||||||
|
|
||||||
|
assert.strictEqual(key.algorithm, key.algorithm);
|
||||||
|
assert.strictEqual(key.usages, key.usages);
|
||||||
|
|
||||||
|
const raw = await subtle.exportKey('raw-secret', key);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
Buffer.from(keyData).toString('hex'),
|
||||||
|
Buffer.from(raw).toString('hex'));
|
||||||
|
|
||||||
|
const jwk = await subtle.exportKey('jwk', key);
|
||||||
|
assert.deepStrictEqual(jwk.key_ops, ['sign', 'verify']);
|
||||||
|
assert(jwk.ext);
|
||||||
|
assert.strictEqual(jwk.kty, 'oct');
|
||||||
|
assert.strictEqual(jwk.alg, `K${name.substring(4)}`);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
Buffer.from(jwk.k, 'base64').toString('hex'),
|
||||||
|
Buffer.from(raw).toString('hex'));
|
||||||
|
|
||||||
|
await subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
jwk,
|
||||||
|
name,
|
||||||
|
true,
|
||||||
|
['sign', 'verify']);
|
||||||
|
|
||||||
|
await subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
{ ...jwk, alg: undefined },
|
||||||
|
name,
|
||||||
|
true,
|
||||||
|
['sign', 'verify']);
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
{ ...jwk, alg: name === 'KMAC128' ? 'K256' : 'K128' },
|
||||||
|
name,
|
||||||
|
true,
|
||||||
|
['sign', 'verify']),
|
||||||
|
{ name: 'DataError', message: 'JWK "alg" does not match the requested algorithm' });
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.importKey(
|
||||||
|
'raw',
|
||||||
|
keyData, name, true, ['sign', 'verify']),
|
||||||
|
{ name: 'NotSupportedError', message: `Unable to import ${name} using raw format` });
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.importKey(
|
||||||
|
'raw-secret',
|
||||||
|
keyData,
|
||||||
|
name,
|
||||||
|
true,
|
||||||
|
[/* empty usages */]),
|
||||||
|
{ name: 'SyntaxError', message: 'Usages cannot be empty when importing a secret key.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
test('KMAC128').then(common.mustCall());
|
||||||
|
test('KMAC256').then(common.mustCall());
|
||||||
|
}
|
||||||
|
|
||||||
// Import/Export AES Secret Key
|
// Import/Export AES Secret Key
|
||||||
{
|
{
|
||||||
async function test() {
|
async function test() {
|
||||||
|
|
|
||||||
50
test/parallel/test-webcrypto-keygen-kmac.js
Normal file
50
test/parallel/test-webcrypto-keygen-kmac.js
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const { hasOpenSSL } = require('../common/crypto');
|
||||||
|
|
||||||
|
if (!hasOpenSSL(3))
|
||||||
|
common.skip('requires OpenSSL >= 3');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { types: { isCryptoKey } } = require('util');
|
||||||
|
const { subtle } = globalThis.crypto;
|
||||||
|
|
||||||
|
const usages = ['sign', 'verify'];
|
||||||
|
|
||||||
|
async function test(name, length) {
|
||||||
|
length = length ?? name === 'KMAC128' ? 128 : 256;
|
||||||
|
const key = await subtle.generateKey({
|
||||||
|
name,
|
||||||
|
length,
|
||||||
|
}, true, usages);
|
||||||
|
|
||||||
|
assert(key);
|
||||||
|
assert(isCryptoKey(key));
|
||||||
|
|
||||||
|
assert.strictEqual(key.type, 'secret');
|
||||||
|
assert.strictEqual(key.toString(), '[object CryptoKey]');
|
||||||
|
assert.strictEqual(key.extractable, true);
|
||||||
|
assert.deepStrictEqual(key.usages, usages);
|
||||||
|
assert.strictEqual(key.algorithm.name, name);
|
||||||
|
assert.strictEqual(key.algorithm.length, length);
|
||||||
|
assert.strictEqual(key.algorithm, key.algorithm);
|
||||||
|
assert.strictEqual(key.usages, key.usages);
|
||||||
|
}
|
||||||
|
|
||||||
|
const kTests = [
|
||||||
|
['KMAC128', 128],
|
||||||
|
['KMAC128', 256],
|
||||||
|
['KMAC128'],
|
||||||
|
['KMAC256', 128],
|
||||||
|
['KMAC256', 256],
|
||||||
|
['KMAC256'],
|
||||||
|
];
|
||||||
|
|
||||||
|
const tests = Promise.all(kTests.map((args) => test(...args)));
|
||||||
|
|
||||||
|
tests.then(common.mustCall());
|
||||||
|
|
@ -184,6 +184,16 @@ if (hasOpenSSL(3)) {
|
||||||
'unwrapKey',
|
'unwrapKey',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (const name of ['KMAC128', 'KMAC256']) {
|
||||||
|
vectors[name] = {
|
||||||
|
result: 'CryptoKey',
|
||||||
|
usages: [
|
||||||
|
'sign',
|
||||||
|
'verify',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasOpenSSL(3, 5)) {
|
if (hasOpenSSL(3, 5)) {
|
||||||
|
|
|
||||||
193
test/parallel/test-webcrypto-sign-verify-kmac.js
Normal file
193
test/parallel/test-webcrypto-sign-verify-kmac.js
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const { hasOpenSSL } = require('../common/crypto');
|
||||||
|
|
||||||
|
if (!hasOpenSSL(3))
|
||||||
|
common.skip('requires OpenSSL >= 3');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { subtle } = globalThis.crypto;
|
||||||
|
|
||||||
|
const vectors = require('../fixtures/crypto/kmac')();
|
||||||
|
|
||||||
|
async function testVerify({ algorithm,
|
||||||
|
key,
|
||||||
|
data,
|
||||||
|
customization,
|
||||||
|
length,
|
||||||
|
expected }) {
|
||||||
|
const [
|
||||||
|
verifyKey,
|
||||||
|
noVerifyKey,
|
||||||
|
keyPair,
|
||||||
|
] = await Promise.all([
|
||||||
|
subtle.importKey(
|
||||||
|
'raw-secret',
|
||||||
|
key,
|
||||||
|
{ name: algorithm },
|
||||||
|
false,
|
||||||
|
['verify']),
|
||||||
|
subtle.importKey(
|
||||||
|
'raw-secret',
|
||||||
|
key,
|
||||||
|
{ name: algorithm },
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
'Ed25519',
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const signParams = {
|
||||||
|
name: algorithm,
|
||||||
|
length,
|
||||||
|
customization,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(await subtle.verify(signParams, verifyKey, expected, data));
|
||||||
|
|
||||||
|
// Test verification with altered buffers
|
||||||
|
const copy = Buffer.from(data);
|
||||||
|
const sigcopy = Buffer.from(expected);
|
||||||
|
const p = subtle.verify(signParams, verifyKey, sigcopy, copy);
|
||||||
|
copy[0] = 255 - copy[0];
|
||||||
|
sigcopy[0] = 255 - sigcopy[0];
|
||||||
|
assert(await p);
|
||||||
|
|
||||||
|
// Test failure when using wrong key
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.verify(signParams, noVerifyKey, expected, data), {
|
||||||
|
message: /Unable to use this key to verify/
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test failure when using the wrong algorithms
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.verify(signParams, keyPair.publicKey, expected, data), {
|
||||||
|
message: /Unable to use this key to verify/
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test failure when signature is altered
|
||||||
|
{
|
||||||
|
const copy = Buffer.from(expected);
|
||||||
|
copy[0] = 255 - copy[0];
|
||||||
|
assert(!(await subtle.verify(
|
||||||
|
signParams,
|
||||||
|
verifyKey,
|
||||||
|
copy,
|
||||||
|
data)));
|
||||||
|
assert(!(await subtle.verify(
|
||||||
|
signParams,
|
||||||
|
verifyKey,
|
||||||
|
copy.slice(1),
|
||||||
|
data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test failure when data is altered
|
||||||
|
{
|
||||||
|
const copy = Buffer.from(data);
|
||||||
|
copy[0] = 255 - copy[0];
|
||||||
|
assert(!(await subtle.verify(signParams, verifyKey, expected, copy)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test failure when wrong algorithm is used
|
||||||
|
{
|
||||||
|
const otherAlgorithm = algorithm === 'KMAC128' ? 'KMAC256' : 'KMAC128';
|
||||||
|
const keyWithOtherAlgorithm = await subtle.importKey(
|
||||||
|
'raw-secret',
|
||||||
|
key,
|
||||||
|
{ name: otherAlgorithm },
|
||||||
|
false,
|
||||||
|
['verify']);
|
||||||
|
const otherParams = { ...signParams, name: otherAlgorithm };
|
||||||
|
assert(!(await subtle.verify(otherParams, keyWithOtherAlgorithm, expected, data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test failure when output length is different
|
||||||
|
{
|
||||||
|
assert(!(await subtle.verify({
|
||||||
|
...signParams,
|
||||||
|
length: length === 256 ? 512 : 256,
|
||||||
|
}, verifyKey, expected, data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testSign({ algorithm,
|
||||||
|
key,
|
||||||
|
data,
|
||||||
|
customization,
|
||||||
|
length,
|
||||||
|
expected }) {
|
||||||
|
const [
|
||||||
|
signKey,
|
||||||
|
noSignKey,
|
||||||
|
keyPair,
|
||||||
|
] = await Promise.all([
|
||||||
|
subtle.importKey(
|
||||||
|
'raw-secret',
|
||||||
|
key,
|
||||||
|
{ name: algorithm },
|
||||||
|
false,
|
||||||
|
['verify', 'sign']),
|
||||||
|
subtle.importKey(
|
||||||
|
'raw-secret',
|
||||||
|
key,
|
||||||
|
{ name: algorithm },
|
||||||
|
false,
|
||||||
|
['verify']),
|
||||||
|
subtle.generateKey(
|
||||||
|
'Ed25519',
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const signParams = {
|
||||||
|
name: algorithm,
|
||||||
|
length,
|
||||||
|
customization,
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
const sig = await subtle.sign(signParams, signKey, data);
|
||||||
|
assert.strictEqual(
|
||||||
|
Buffer.from(sig).toString('hex'),
|
||||||
|
Buffer.from(expected).toString('hex'));
|
||||||
|
assert(await subtle.verify(signParams, signKey, sig, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const copy = Buffer.from(data);
|
||||||
|
const p = subtle.sign(signParams, signKey, copy);
|
||||||
|
copy[0] = 255 - copy[0];
|
||||||
|
const sig = await p;
|
||||||
|
assert(await subtle.verify(signParams, signKey, sig, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test failure when no sign usage
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.sign(signParams, noSignKey, data), {
|
||||||
|
message: /Unable to use this key to sign/
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test failure when using the wrong algorithms
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.sign(signParams, keyPair.privateKey, data), {
|
||||||
|
message: /Unable to use this key to sign/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
const variations = [];
|
||||||
|
|
||||||
|
for (const vector of vectors) {
|
||||||
|
variations.push(testVerify(vector));
|
||||||
|
variations.push(testSign(vector));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(variations);
|
||||||
|
})().then(common.mustCall());
|
||||||
|
|
@ -107,6 +107,30 @@ const { subtle } = globalThis.crypto;
|
||||||
test('hello world').then(common.mustCall());
|
test('hello world').then(common.mustCall());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test Sign/Verify KMAC
|
||||||
|
if (hasOpenSSL(3)) {
|
||||||
|
async function test(name, data) {
|
||||||
|
const ec = new TextEncoder();
|
||||||
|
|
||||||
|
const key = await subtle.generateKey({
|
||||||
|
name,
|
||||||
|
}, true, ['sign', 'verify']);
|
||||||
|
|
||||||
|
const signature = await subtle.sign({
|
||||||
|
name,
|
||||||
|
length: 256,
|
||||||
|
}, key, ec.encode(data));
|
||||||
|
|
||||||
|
assert(await subtle.verify({
|
||||||
|
name,
|
||||||
|
length: 256,
|
||||||
|
}, key, signature, ec.encode(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
test('KMAC128', 'hello world').then(common.mustCall());
|
||||||
|
test('KMAC256', 'hello world').then(common.mustCall());
|
||||||
|
}
|
||||||
|
|
||||||
// Test Sign/Verify Ed25519
|
// Test Sign/Verify Ed25519
|
||||||
{
|
{
|
||||||
async function test(data) {
|
async function test(data) {
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,10 @@ const customTypesMap = {
|
||||||
'Ed448Params': 'webcrypto.html#class-ed448params',
|
'Ed448Params': 'webcrypto.html#class-ed448params',
|
||||||
'ContextParams': 'webcrypto.html#class-contextparams',
|
'ContextParams': 'webcrypto.html#class-contextparams',
|
||||||
'CShakeParams': 'webcrypto.html#class-cshakeparams',
|
'CShakeParams': 'webcrypto.html#class-cshakeparams',
|
||||||
|
'KmacImportParams': 'webcrypto.html#class-kmacimportparams',
|
||||||
|
'KmacKeyAlgorithm': 'webcrypto.html#class-kmackeyalgorithm',
|
||||||
|
'KmacKeyGenParams': 'webcrypto.html#class-kmackeygenparams',
|
||||||
|
'KmacParams': 'webcrypto.html#class-kmacparams',
|
||||||
|
|
||||||
'dgram.Socket': 'dgram.html#class-dgramsocket',
|
'dgram.Socket': 'dgram.html#class-dgramsocket',
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user