crypto: support SLH-DSA KeyObject, sign, and verify

PR-URL: https://github.com/nodejs/node/pull/59537
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
Filip Skokan 2025-08-26 17:41:25 +02:00 committed by GitHub
parent ab6991f66b
commit d00228f5ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 542 additions and 37 deletions

View File

@ -30,6 +30,18 @@ constexpr static PQCMapping pqc_mappings[] = {
{"ML-KEM-512", EVP_PKEY_ML_KEM_512},
{"ML-KEM-768", EVP_PKEY_ML_KEM_768},
{"ML-KEM-1024", EVP_PKEY_ML_KEM_1024},
{"SLH-DSA-SHA2-128f", EVP_PKEY_SLH_DSA_SHA2_128F},
{"SLH-DSA-SHA2-128s", EVP_PKEY_SLH_DSA_SHA2_128S},
{"SLH-DSA-SHA2-192f", EVP_PKEY_SLH_DSA_SHA2_192F},
{"SLH-DSA-SHA2-192s", EVP_PKEY_SLH_DSA_SHA2_192S},
{"SLH-DSA-SHA2-256f", EVP_PKEY_SLH_DSA_SHA2_256F},
{"SLH-DSA-SHA2-256s", EVP_PKEY_SLH_DSA_SHA2_256S},
{"SLH-DSA-SHAKE-128f", EVP_PKEY_SLH_DSA_SHAKE_128F},
{"SLH-DSA-SHAKE-128s", EVP_PKEY_SLH_DSA_SHAKE_128S},
{"SLH-DSA-SHAKE-192f", EVP_PKEY_SLH_DSA_SHAKE_192F},
{"SLH-DSA-SHAKE-192s", EVP_PKEY_SLH_DSA_SHAKE_192S},
{"SLH-DSA-SHAKE-256f", EVP_PKEY_SLH_DSA_SHAKE_256F},
{"SLH-DSA-SHAKE-256s", EVP_PKEY_SLH_DSA_SHAKE_256S},
};
#endif
@ -2659,6 +2671,18 @@ bool EVPKeyPointer::isOneShotVariant() const {
case EVP_PKEY_ML_DSA_44:
case EVP_PKEY_ML_DSA_65:
case EVP_PKEY_ML_DSA_87:
case EVP_PKEY_SLH_DSA_SHA2_128F:
case EVP_PKEY_SLH_DSA_SHA2_128S:
case EVP_PKEY_SLH_DSA_SHA2_192F:
case EVP_PKEY_SLH_DSA_SHA2_192S:
case EVP_PKEY_SLH_DSA_SHA2_256F:
case EVP_PKEY_SLH_DSA_SHA2_256S:
case EVP_PKEY_SLH_DSA_SHAKE_128F:
case EVP_PKEY_SLH_DSA_SHAKE_128S:
case EVP_PKEY_SLH_DSA_SHAKE_192F:
case EVP_PKEY_SLH_DSA_SHAKE_192S:
case EVP_PKEY_SLH_DSA_SHAKE_256F:
case EVP_PKEY_SLH_DSA_SHAKE_256S:
#endif
return true;
default:

View File

@ -78,7 +78,7 @@ try {
The following table lists the asymmetric key types recognized by the [`KeyObject`][] API:
| Key Type | Description | OID |
| --------------------------- | -------------- | ----------------------- |
| ---------------------------------- | ------------------ | ----------------------- |
| `'dh'` | Diffie-Hellman | 1.2.840.113549.1.3.1 |
| `'dsa'` | DSA | 1.2.840.10040.4.1 |
| `'ec'` | Elliptic curve | 1.2.840.10045.2.1 |
@ -92,6 +92,18 @@ The following table lists the asymmetric key types recognized by the [`KeyObject
| `'ml-kem-768'`[^openssl35] | ML-KEM-768 | 2.16.840.1.101.3.4.4.2 |
| `'rsa-pss'` | RSA PSS | 1.2.840.113549.1.1.10 |
| `'rsa'` | RSA | 1.2.840.113549.1.1.1 |
| `'slh-dsa-sha2-128f'`[^openssl35] | SLH-DSA-SHA2-128f | 2.16.840.1.101.3.4.3.21 |
| `'slh-dsa-sha2-128s'`[^openssl35] | SLH-DSA-SHA2-128s | 2.16.840.1.101.3.4.3.22 |
| `'slh-dsa-sha2-192f'`[^openssl35] | SLH-DSA-SHA2-192f | 2.16.840.1.101.3.4.3.23 |
| `'slh-dsa-sha2-192s'`[^openssl35] | SLH-DSA-SHA2-192s | 2.16.840.1.101.3.4.3.24 |
| `'slh-dsa-sha2-256f'`[^openssl35] | SLH-DSA-SHA2-256f | 2.16.840.1.101.3.4.3.25 |
| `'slh-dsa-sha2-256s'`[^openssl35] | SLH-DSA-SHA2-256s | 2.16.840.1.101.3.4.3.26 |
| `'slh-dsa-shake-128f'`[^openssl35] | SLH-DSA-SHAKE-128f | 2.16.840.1.101.3.4.3.27 |
| `'slh-dsa-shake-128s'`[^openssl35] | SLH-DSA-SHAKE-128s | 2.16.840.1.101.3.4.3.28 |
| `'slh-dsa-shake-192f'`[^openssl35] | SLH-DSA-SHAKE-192f | 2.16.840.1.101.3.4.3.29 |
| `'slh-dsa-shake-192s'`[^openssl35] | SLH-DSA-SHAKE-192s | 2.16.840.1.101.3.4.3.30 |
| `'slh-dsa-shake-256f'`[^openssl35] | SLH-DSA-SHAKE-256f | 2.16.840.1.101.3.4.3.31 |
| `'slh-dsa-shake-256s'`[^openssl35] | SLH-DSA-SHAKE-256s | 2.16.840.1.101.3.4.3.32 |
| `'x25519'` | X25519 | 1.3.101.110 |
| `'x448'` | X448 | 1.3.101.111 |
@ -2046,6 +2058,9 @@ Other key details might be exposed via this API using additional attributes.
<!-- YAML
added: v11.6.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59537
description: Add support for SLH-DSA keys.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59461
description: Add support for ML-KEM keys.
@ -3911,6 +3926,9 @@ underlying hash function. See [`crypto.createHmac()`][] for more information.
<!-- YAML
added: v10.12.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59537
description: Add support for SLH-DSA key pairs.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59461
description: Add support for ML-KEM key pairs.
@ -4036,6 +4054,9 @@ a `Promise` for an `Object` with `publicKey` and `privateKey` properties.
<!-- YAML
added: v10.12.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59537
description: Add support for SLH-DSA key pairs.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59461
description: Add support for ML-KEM key pairs.
@ -5691,6 +5712,9 @@ Throws an error if FIPS mode is not available.
<!-- YAML
added: v12.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59537
description: Add support for SLH-DSA signing.
- version: v24.6.0
pr-url: https://github.com/nodejs/node/pull/59259
description: Add support for ML-DSA signing.
@ -5807,6 +5831,9 @@ not introduce timing vulnerabilities.
<!-- YAML
added: v12.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59537
description: Add support for SLH-DSA signature verification.
- version: v24.6.0
pr-url: https://github.com/nodejs/node/pull/59259
description: Add support for ML-DSA signature verification.

View File

@ -25,6 +25,18 @@ const {
EVP_PKEY_ML_KEM_1024,
EVP_PKEY_ML_KEM_512,
EVP_PKEY_ML_KEM_768,
EVP_PKEY_SLH_DSA_SHA2_128F,
EVP_PKEY_SLH_DSA_SHA2_128S,
EVP_PKEY_SLH_DSA_SHA2_192F,
EVP_PKEY_SLH_DSA_SHA2_192S,
EVP_PKEY_SLH_DSA_SHA2_256F,
EVP_PKEY_SLH_DSA_SHA2_256S,
EVP_PKEY_SLH_DSA_SHAKE_128F,
EVP_PKEY_SLH_DSA_SHAKE_128S,
EVP_PKEY_SLH_DSA_SHAKE_192F,
EVP_PKEY_SLH_DSA_SHAKE_192S,
EVP_PKEY_SLH_DSA_SHAKE_256F,
EVP_PKEY_SLH_DSA_SHAKE_256S,
EVP_PKEY_X25519,
EVP_PKEY_X448,
OPENSSL_EC_NAMED_CURVE,
@ -168,7 +180,7 @@ function parseKeyEncoding(keyType, options = kEmptyObject) {
];
}
const ids = {
const nidOnlyKeyPairs = {
'ed25519': EVP_PKEY_ED25519,
'ed448': EVP_PKEY_ED448,
'x25519': EVP_PKEY_X25519,
@ -179,6 +191,18 @@ const ids = {
'ml-kem-512': EVP_PKEY_ML_KEM_512,
'ml-kem-768': EVP_PKEY_ML_KEM_768,
'ml-kem-1024': EVP_PKEY_ML_KEM_1024,
'slh-dsa-sha2-128f': EVP_PKEY_SLH_DSA_SHA2_128F,
'slh-dsa-sha2-128s': EVP_PKEY_SLH_DSA_SHA2_128S,
'slh-dsa-sha2-192f': EVP_PKEY_SLH_DSA_SHA2_192F,
'slh-dsa-sha2-192s': EVP_PKEY_SLH_DSA_SHA2_192S,
'slh-dsa-sha2-256f': EVP_PKEY_SLH_DSA_SHA2_256F,
'slh-dsa-sha2-256s': EVP_PKEY_SLH_DSA_SHA2_256S,
'slh-dsa-shake-128f': EVP_PKEY_SLH_DSA_SHAKE_128F,
'slh-dsa-shake-128s': EVP_PKEY_SLH_DSA_SHAKE_128S,
'slh-dsa-shake-192f': EVP_PKEY_SLH_DSA_SHAKE_192F,
'slh-dsa-shake-192s': EVP_PKEY_SLH_DSA_SHAKE_192S,
'slh-dsa-shake-256f': EVP_PKEY_SLH_DSA_SHAKE_256F,
'slh-dsa-shake-256s': EVP_PKEY_SLH_DSA_SHAKE_256S,
};
function createJob(mode, type, options) {
@ -293,22 +317,6 @@ function createJob(mode, type, options) {
paramEncoding,
...encoding);
}
case 'ed25519':
case 'ed448':
case 'x25519':
case 'x448':
case 'ml-dsa-44':
case 'ml-dsa-65':
case 'ml-dsa-87':
case 'ml-kem-512':
case 'ml-kem-768':
case 'ml-kem-1024':
{
if (ids[type] === undefined) {
throw new ERR_INVALID_ARG_VALUE('type', type, 'must be a supported key type');
}
return new NidKeyPairGenJob(mode, ids[type], ...encoding);
}
case 'dh':
{
validateObject(options, 'options');
@ -347,10 +355,13 @@ function createJob(mode, type, options) {
generator == null ? 2 : generator,
...encoding);
}
default:
// Fall through
}
default: {
if (nidOnlyKeyPairs[type] === undefined) {
throw new ERR_INVALID_ARG_VALUE('type', type, 'must be a supported key type');
}
return new NidKeyPairGenJob(mode, nidOnlyKeyPairs[type], ...encoding);
}
}
}
// Symmetric Key Generation

View File

@ -997,6 +997,30 @@ Local<Value> KeyObjectHandle::GetAsymmetricKeyType() const {
return env()->crypto_ml_kem_768_string();
case EVP_PKEY_ML_KEM_1024:
return env()->crypto_ml_kem_1024_string();
case EVP_PKEY_SLH_DSA_SHA2_128F:
return env()->crypto_slh_dsa_sha2_128f_string();
case EVP_PKEY_SLH_DSA_SHA2_128S:
return env()->crypto_slh_dsa_sha2_128s_string();
case EVP_PKEY_SLH_DSA_SHA2_192F:
return env()->crypto_slh_dsa_sha2_192f_string();
case EVP_PKEY_SLH_DSA_SHA2_192S:
return env()->crypto_slh_dsa_sha2_192s_string();
case EVP_PKEY_SLH_DSA_SHA2_256F:
return env()->crypto_slh_dsa_sha2_256f_string();
case EVP_PKEY_SLH_DSA_SHA2_256S:
return env()->crypto_slh_dsa_sha2_256s_string();
case EVP_PKEY_SLH_DSA_SHAKE_128F:
return env()->crypto_slh_dsa_shake_128f_string();
case EVP_PKEY_SLH_DSA_SHAKE_128S:
return env()->crypto_slh_dsa_shake_128s_string();
case EVP_PKEY_SLH_DSA_SHAKE_192F:
return env()->crypto_slh_dsa_shake_192f_string();
case EVP_PKEY_SLH_DSA_SHAKE_192S:
return env()->crypto_slh_dsa_shake_192s_string();
case EVP_PKEY_SLH_DSA_SHAKE_256F:
return env()->crypto_slh_dsa_shake_256f_string();
case EVP_PKEY_SLH_DSA_SHAKE_256S:
return env()->crypto_slh_dsa_shake_256s_string();
#endif
default:
return Undefined(env()->isolate());
@ -1324,6 +1348,18 @@ void Initialize(Environment* env, Local<Object> target) {
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_512);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_768);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_1024);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_128F);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_128S);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_192F);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_192S);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_256F);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_256S);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_128F);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_128S);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_192F);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_192S);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_256F);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_256S);
#endif
NODE_DEFINE_CONSTANT(target, EVP_PKEY_X25519);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_X448);

View File

@ -121,6 +121,18 @@
V(crypto_ml_kem_512_string, "ml-kem-512") \
V(crypto_ml_kem_768_string, "ml-kem-768") \
V(crypto_ml_kem_1024_string, "ml-kem-1024") \
V(crypto_slh_dsa_sha2_128f_string, "slh-dsa-sha2-128f") \
V(crypto_slh_dsa_sha2_128s_string, "slh-dsa-sha2-128s") \
V(crypto_slh_dsa_sha2_192f_string, "slh-dsa-sha2-192f") \
V(crypto_slh_dsa_sha2_192s_string, "slh-dsa-sha2-192s") \
V(crypto_slh_dsa_sha2_256f_string, "slh-dsa-sha2-256f") \
V(crypto_slh_dsa_sha2_256s_string, "slh-dsa-sha2-256s") \
V(crypto_slh_dsa_shake_128f_string, "slh-dsa-shake-128f") \
V(crypto_slh_dsa_shake_128s_string, "slh-dsa-shake-128s") \
V(crypto_slh_dsa_shake_192f_string, "slh-dsa-shake-192f") \
V(crypto_slh_dsa_shake_192s_string, "slh-dsa-shake-192s") \
V(crypto_slh_dsa_shake_256f_string, "slh-dsa-shake-256f") \
V(crypto_slh_dsa_shake_256s_string, "slh-dsa-shake-256s") \
V(crypto_x25519_string, "x25519") \
V(crypto_x448_string, "x448") \
V(crypto_rsa_string, "rsa") \

View File

@ -122,6 +122,30 @@ all: \
ml_kem_1024_private_seed_only.pem \
ml_kem_1024_private_priv_only.pem \
ml_kem_1024_public.pem \
slh_dsa_sha2_128s_private.pem \
slh_dsa_sha2_128s_public.pem \
slh_dsa_sha2_128f_private.pem \
slh_dsa_sha2_128f_public.pem \
slh_dsa_sha2_192s_private.pem \
slh_dsa_sha2_192s_public.pem \
slh_dsa_sha2_192f_private.pem \
slh_dsa_sha2_192f_public.pem \
slh_dsa_sha2_256s_private.pem \
slh_dsa_sha2_256s_public.pem \
slh_dsa_sha2_256f_private.pem \
slh_dsa_sha2_256f_public.pem \
slh_dsa_shake_128s_private.pem \
slh_dsa_shake_128s_public.pem \
slh_dsa_shake_128f_private.pem \
slh_dsa_shake_128f_public.pem \
slh_dsa_shake_192s_private.pem \
slh_dsa_shake_192s_public.pem \
slh_dsa_shake_192f_private.pem \
slh_dsa_shake_192f_public.pem \
slh_dsa_shake_256s_private.pem \
slh_dsa_shake_256s_public.pem \
slh_dsa_shake_256f_private.pem \
slh_dsa_shake_256f_public.pem \
#
# Create Certificate Authority: ca1
@ -879,6 +903,78 @@ ed448_private.pem:
ed448_public.pem: ed448_private.pem
openssl pkey -in ed448_private.pem -pubout -out ed448_public.pem
slh_dsa_sha2_128s_private.pem:
openssl genpkey -algorithm slh-dsa-sha2-128s -out slh_dsa_sha2_128s_private.pem
slh_dsa_sha2_128s_public.pem: slh_dsa_sha2_128s_private.pem
openssl pkey -in slh_dsa_sha2_128s_private.pem -pubout -out slh_dsa_sha2_128s_public.pem
slh_dsa_sha2_128f_private.pem:
openssl genpkey -algorithm slh-dsa-sha2-128f -out slh_dsa_sha2_128f_private.pem
slh_dsa_sha2_128f_public.pem: slh_dsa_sha2_128f_private.pem
openssl pkey -in slh_dsa_sha2_128f_private.pem -pubout -out slh_dsa_sha2_128f_public.pem
slh_dsa_sha2_192s_private.pem:
openssl genpkey -algorithm slh-dsa-sha2-192s -out slh_dsa_sha2_192s_private.pem
slh_dsa_sha2_192s_public.pem: slh_dsa_sha2_192s_private.pem
openssl pkey -in slh_dsa_sha2_192s_private.pem -pubout -out slh_dsa_sha2_192s_public.pem
slh_dsa_sha2_192f_private.pem:
openssl genpkey -algorithm slh-dsa-sha2-192f -out slh_dsa_sha2_192f_private.pem
slh_dsa_sha2_192f_public.pem: slh_dsa_sha2_192f_private.pem
openssl pkey -in slh_dsa_sha2_192f_private.pem -pubout -out slh_dsa_sha2_192f_public.pem
slh_dsa_sha2_256s_private.pem:
openssl genpkey -algorithm slh-dsa-sha2-256s -out slh_dsa_sha2_256s_private.pem
slh_dsa_sha2_256s_public.pem: slh_dsa_sha2_256s_private.pem
openssl pkey -in slh_dsa_sha2_256s_private.pem -pubout -out slh_dsa_sha2_256s_public.pem
slh_dsa_sha2_256f_private.pem:
openssl genpkey -algorithm slh-dsa-sha2-256f -out slh_dsa_sha2_256f_private.pem
slh_dsa_sha2_256f_public.pem: slh_dsa_sha2_256f_private.pem
openssl pkey -in slh_dsa_sha2_256f_private.pem -pubout -out slh_dsa_sha2_256f_public.pem
slh_dsa_shake_128s_private.pem:
openssl genpkey -algorithm slh-dsa-shake-128s -out slh_dsa_shake_128s_private.pem
slh_dsa_shake_128s_public.pem: slh_dsa_shake_128s_private.pem
openssl pkey -in slh_dsa_shake_128s_private.pem -pubout -out slh_dsa_shake_128s_public.pem
slh_dsa_shake_128f_private.pem:
openssl genpkey -algorithm slh-dsa-shake-128f -out slh_dsa_shake_128f_private.pem
slh_dsa_shake_128f_public.pem: slh_dsa_shake_128f_private.pem
openssl pkey -in slh_dsa_shake_128f_private.pem -pubout -out slh_dsa_shake_128f_public.pem
slh_dsa_shake_192s_private.pem:
openssl genpkey -algorithm slh-dsa-shake-192s -out slh_dsa_shake_192s_private.pem
slh_dsa_shake_192s_public.pem: slh_dsa_shake_192s_private.pem
openssl pkey -in slh_dsa_shake_192s_private.pem -pubout -out slh_dsa_shake_192s_public.pem
slh_dsa_shake_192f_private.pem:
openssl genpkey -algorithm slh-dsa-shake-192f -out slh_dsa_shake_192f_private.pem
slh_dsa_shake_192f_public.pem: slh_dsa_shake_192f_private.pem
openssl pkey -in slh_dsa_shake_192f_private.pem -pubout -out slh_dsa_shake_192f_public.pem
slh_dsa_shake_256s_private.pem:
openssl genpkey -algorithm slh-dsa-shake-256s -out slh_dsa_shake_256s_private.pem
slh_dsa_shake_256s_public.pem: slh_dsa_shake_256s_private.pem
openssl pkey -in slh_dsa_shake_256s_private.pem -pubout -out slh_dsa_shake_256s_public.pem
slh_dsa_shake_256f_private.pem:
openssl genpkey -algorithm slh-dsa-shake-256f -out slh_dsa_shake_256f_private.pem
slh_dsa_shake_256f_public.pem: slh_dsa_shake_256f_private.pem
openssl pkey -in slh_dsa_shake_256f_private.pem -pubout -out slh_dsa_shake_256f_public.pem
ml_dsa_44_private.pem:
openssl genpkey -algorithm ml-dsa-44 -out ml_dsa_44_private.pem

View File

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMVBECSzBw9GGOCapA9uSDmWwzK5By75k4dJZt9GEv7
aWL4AaTAj1duAUPpfzUlsf0d8m4lfHy9jWFFVRm/lzWoT3XK
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MDAwCwYJYIZIAWUDBAMVAyEApMCPV24BQ+l/NSWx/R3ybiV8fL2NYUVVGb+XNahP
dco=
-----END PUBLIC KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMUBEBW0ndyA81L0ztiRuOj7qOh5x71MsGmGPZuq1oP
LhR+9pUy5kK0mnqETDzlxw84Inja0BQ5haLCB5dVeguERf4t
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MDAwCwYJYIZIAWUDBAMUAyEAlTLmQrSaeoRMPOXHDzgieNrQFDmFosIHl1V6C4RF
/i0=
-----END PUBLIC KEY-----

View File

@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MHICAQAwCwYJYIZIAWUDBAMXBGCa6aZwUtiD3lIcwvnvIngLfDsybrGkVeTQF+Ta
fgeeJ6qppaQyXSJ2BE51rxAriw3IBZ06mrxSNEBPxCPyzUvmNOVTnNOqxTUwoYJD
xKpSz28YVVBlMB+VZiYuGwQEC+g=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MEAwCwYJYIZIAWUDBAMXAzEAyAWdOpq8UjRAT8Qj8s1L5jTlU5zTqsU1MKGCQ8Sq
Us9vGFVQZTAflWYmLhsEBAvo
-----END PUBLIC KEY-----

View File

@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MHICAQAwCwYJYIZIAWUDBAMWBGARGO8bI21ig1JwmC2xgePoYxMQUIBM3SJfTs9Z
ZJ41JNSF5ZsWM4OOLcrGKBx1QOefWfKo1OaGEqC9gqzRPIg+piH7fv9Ih/QgcBgi
V7bdV2bj3xngsvUBVpM8IA7tURw=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MEAwCwYJYIZIAWUDBAMWAzEAn1nyqNTmhhKgvYKs0TyIPqYh+37/SIf0IHAYIle2
3Vdm498Z4LL1AVaTPCAO7VEc
-----END PUBLIC KEY-----

View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMAsGCWCGSAFlAwQDGQSBgBCFdBpSeL3EYeX8p3F0GiXz1ZDSd+cEZT8u
XelMpqxBJvWcxXyvblXBQ1XJL1rLoZLc8xWxVf5sgGZ+FYoOlDjBRhE/W8QVDmJd
kFiNYIjFxTWwgA1gJtBRVPoHCnqJPctJTPhIwWO+TIw1sCsncLyKB3BUQpp12Kki
2UaE+TJ6
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFAwCwYJYIZIAWUDBAMZA0EAwUYRP1vEFQ5iXZBYjWCIxcU1sIANYCbQUVT6Bwp6
iT3LSUz4SMFjvkyMNbArJ3C8igdwVEKaddipItlGhPkyeg==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMAsGCWCGSAFlAwQDGASBgFScSqBk+4TiYRxgk7Giwd1+MHdDH611bGsX
SyPWfDJ2WbOg6DEy13bh7tJ7XlWmZlsYEclmv5i5bNwt09rX1iIavkJU5Hen/D+G
yfaVjFeIpFykZ4/mYoGHF+2LBnj6K6IiGYoJGpuDnnPL5DAXhlK2/7qtDGM4c7C0
9g86DDsD
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFAwCwYJYIZIAWUDBAMYA0EAGr5CVOR3p/w/hsn2lYxXiKRcpGeP5mKBhxftiwZ4
+iuiIhmKCRqbg55zy+QwF4ZStv+6rQxjOHOwtPYPOgw7Aw==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMbBEBnbtGLIm93IJMHZjnJRjTQw+3VjAGx6J9Z6GEW
Cr+ptNQLvzRUPRKuqzwuwFzDFTopvp8fQbQ/ofTE74ebQaNX
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MDAwCwYJYIZIAWUDBAMbAyEA1Au/NFQ9Eq6rPC7AXMMVOim+nx9BtD+h9MTvh5tB
o1c=
-----END PUBLIC KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMaBECDrmpiOwpBED6azWOQRSEObqOqK/JsImpBx7ja
xrpcca+mdL+BEs0AoaNnV84iHfNbjeO1ShFo6cyZhLkFAdtN
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MDAwCwYJYIZIAWUDBAMaAyEAr6Z0v4ESzQCho2dXziId81uN47VKEWjpzJmEuQUB
200=
-----END PUBLIC KEY-----

View File

@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MHICAQAwCwYJYIZIAWUDBAMdBGBux3qxhbGNmd4H+yHoNA9G0ehpfLhOT0Izyb+s
bX6P1IUQx+GLlHaYjUPSTlpk0ffnvIkK8T85hv9mARKU39jJuNhYXyHwhi/zpx8z
ANRRGsxQnn1Sli+Csh0JVSiJbSA=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MEAwCwYJYIZIAWUDBAMdAzEA57yJCvE/OYb/ZgESlN/YybjYWF8h8IYv86cfMwDU
URrMUJ59UpYvgrIdCVUoiW0g
-----END PUBLIC KEY-----

View File

@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MHICAQAwCwYJYIZIAWUDBAMcBGAve0BGI+QeA9aqzT7g12NSpuIiLIUxKx0h5YuY
D0maI297i0Fe/s7b+T10fLeXpBnzUJtcrtqg7nEbYTVCKas1rHSXA/AzWMwWLNpr
hg93oNYIiHM+imgaYGCdL9km5xE=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MEAwCwYJYIZIAWUDBAMcAzEA81CbXK7aoO5xG2E1QimrNax0lwPwM1jMFizaa4YP
d6DWCIhzPopoGmBgnS/ZJucR
-----END PUBLIC KEY-----

View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMAsGCWCGSAFlAwQDHwSBgKuw/5AtSV/FEsUD3l0uyJQ/xPKNPoUdMtsR
wDTGL2Il/8CqNQHVTaMOqgNJnx/gebXs7TgMEoeuMtzhHw+a/si8IGtFkLR6CFrQ
wWfp+TU5MWH+8tw+z3jLxfxY9GFgdJAgOvjoQoEfWKP+8iCvYP9Aor+6kWvYepfx
FGXL+vLc
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFAwCwYJYIZIAWUDBAMfA0EAvCBrRZC0egha0MFn6fk1OTFh/vLcPs94y8X8WPRh
YHSQIDr46EKBH1ij/vIgr2D/QKK/upFr2HqX8RRly/ry3A==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMAsGCWCGSAFlAwQDHgSBgFTySgpKJxiEngjbaXj/EzwYf8zCsk7BLG78
g7kKwGpnRQ8smQvXKUAJs4CWbBIY0qRLV3gfAn+pEsxpHsUFX60jgFQC1MwUUlKo
//QEjNMuQXILr7MIHTJ4UR1+lmoIpmmOc4OFK8P8APxP4tZwZ+DhEPHl94WjN/mX
kAHSru+f
-----END PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFAwCwYJYIZIAWUDBAMeA0EAI4BUAtTMFFJSqP/0BIzTLkFyC6+zCB0yeFEdfpZq
CKZpjnODhSvD/AD8T+LWcGfg4RDx5feFozf5l5AB0q7vnw==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,74 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const { hasOpenSSL } = require('../common/crypto');
const assert = require('assert');
const {
createPublicKey,
createPrivateKey,
} = require('crypto');
const fixtures = require('../common/fixtures');
function getKeyFileName(type, suffix) {
return `${type.replaceAll('-', '_')}_${suffix}.pem`;
}
for (const asymmetricKeyType of [
'slh-dsa-sha2-128f', 'slh-dsa-sha2-128s', 'slh-dsa-sha2-192f', 'slh-dsa-sha2-192s',
'slh-dsa-sha2-256f', 'slh-dsa-sha2-256s', 'slh-dsa-shake-128f', 'slh-dsa-shake-128s',
'slh-dsa-shake-192f', 'slh-dsa-shake-192s', 'slh-dsa-shake-256f', 'slh-dsa-shake-256s',
]) {
const keys = {
public: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'public'), 'ascii'),
private: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private'), 'ascii'),
};
function assertKey(key) {
assert.deepStrictEqual(key.asymmetricKeyDetails, {});
assert.strictEqual(key.asymmetricKeyType, asymmetricKeyType);
assert.strictEqual(key.equals(key), true);
assert.deepStrictEqual(key, key);
}
function assertPublicKey(key) {
assertKey(key);
assert.strictEqual(key.type, 'public');
assert.strictEqual(key.export({ format: 'pem', type: 'spki' }), keys.public);
key.export({ format: 'der', type: 'spki' });
assert.throws(() => key.export({ format: 'jwk' }),
{ code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE', message: 'Unsupported JWK Key Type.' });
}
function assertPrivateKey(key) {
assertKey(key);
assert.strictEqual(key.type, 'private');
assertPublicKey(createPublicKey(key));
key.export({ format: 'der', type: 'pkcs8' });
assert.strictEqual(key.export({ format: 'pem', type: 'pkcs8' }), keys.private);
assert.throws(() => key.export({ format: 'jwk' }),
{ code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE', message: 'Unsupported JWK Key Type.' });
}
if (!hasOpenSSL(3, 5)) {
assert.throws(() => createPublicKey(keys.public), {
code: hasOpenSSL(3) ? 'ERR_OSSL_EVP_DECODE_ERROR' : 'ERR_OSSL_EVP_UNSUPPORTED_ALGORITHM',
});
assert.throws(() => createPrivateKey(keys.private), {
code: hasOpenSSL(3) ? 'ERR_OSSL_UNSUPPORTED' : 'ERR_OSSL_EVP_UNSUPPORTED_ALGORITHM',
});
} else {
const publicKey = createPublicKey(keys.public);
assertPublicKey(publicKey);
const pubFromPriv = createPublicKey(keys.private);
assertPublicKey(pubFromPriv);
assertPrivateKey(createPrivateKey(keys.private));
assert.strictEqual(pubFromPriv.equals(publicKey), true);
}
}

View File

@ -0,0 +1,54 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const { hasOpenSSL } = require('../common/crypto');
const assert = require('assert');
const {
generateKeyPair,
} = require('crypto');
if (!hasOpenSSL(3, 5)) {
for (const asymmetricKeyType of [
'slh-dsa-sha2-128f', 'slh-dsa-sha2-128s', 'slh-dsa-sha2-192f', 'slh-dsa-sha2-192s',
'slh-dsa-sha2-256f', 'slh-dsa-sha2-256s', 'slh-dsa-shake-128f', 'slh-dsa-shake-128s',
'slh-dsa-shake-192f', 'slh-dsa-shake-192s', 'slh-dsa-shake-256f', 'slh-dsa-shake-256s',
]) {
assert.throws(() => generateKeyPair(asymmetricKeyType, common.mustNotCall()), {
code: 'ERR_INVALID_ARG_VALUE',
message: /The argument 'type' must be a supported key type/
});
}
} else {
for (const asymmetricKeyType of [
'slh-dsa-sha2-128f', 'slh-dsa-sha2-128s', 'slh-dsa-sha2-192f', 'slh-dsa-sha2-192s',
'slh-dsa-sha2-256f', 'slh-dsa-sha2-256s', 'slh-dsa-shake-128f', 'slh-dsa-shake-128s',
'slh-dsa-shake-192f', 'slh-dsa-shake-192s', 'slh-dsa-shake-256f', 'slh-dsa-shake-256s',
]) {
for (const [publicKeyEncoding, validate] of [
[undefined, (publicKey) => {
assert.strictEqual(publicKey.type, 'public');
assert.strictEqual(publicKey.asymmetricKeyType, asymmetricKeyType);
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {});
}],
[{ format: 'pem', type: 'spki' }, (publicKey) => assert.strictEqual(typeof publicKey, 'string')],
[{ format: 'der', type: 'spki' }, (publicKey) => assert.strictEqual(Buffer.isBuffer(publicKey), true)],
]) {
generateKeyPair(asymmetricKeyType, { publicKeyEncoding }, common.mustSucceed(validate));
}
for (const [privateKeyEncoding, validate] of [
[undefined, (_, privateKey) => {
assert.strictEqual(privateKey.type, 'private');
assert.strictEqual(privateKey.asymmetricKeyType, asymmetricKeyType);
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {});
}],
[{ format: 'pem', type: 'pkcs8' }, (_, privateKey) => assert.strictEqual(typeof privateKey, 'string')],
[{ format: 'der', type: 'pkcs8' }, (_, privateKey) => assert.strictEqual(Buffer.isBuffer(privateKey), true)],
]) {
generateKeyPair(asymmetricKeyType, { privateKeyEncoding }, common.mustSucceed(validate));
}
}
}

View File

@ -0,0 +1,63 @@
import * as common from '../common/index.mjs';
if (!common.hasCrypto)
common.skip('missing crypto');
import { hasOpenSSL } from '../common/crypto.js';
if (!hasOpenSSL(3, 5))
common.skip('requires OpenSSL >= 3.5');
import * as assert from 'node:assert';
import { promisify } from 'node:util';
import { randomBytes, sign, verify } from 'node:crypto';
import fixtures from '../common/fixtures.js';
function getKeyFileName(type, suffix) {
return `${type.replaceAll('-', '_')}_${suffix}.pem`;
}
for (const [asymmetricKeyType, sigLen] of [
['slh-dsa-sha2-128f', 17088],
['slh-dsa-sha2-128s', 7856],
['slh-dsa-sha2-192f', 35664],
['slh-dsa-sha2-192s', 16224],
['slh-dsa-sha2-256f', 49856],
['slh-dsa-sha2-256s', 29792],
['slh-dsa-shake-128f', 17088],
['slh-dsa-shake-128s', 7856],
['slh-dsa-shake-192f', 35664],
['slh-dsa-shake-192s', 16224],
['slh-dsa-shake-256f', 49856],
['slh-dsa-shake-256s', 29792],
]) {
const keys = {
public: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'public'), 'ascii'),
private: fixtures.readKey(getKeyFileName(asymmetricKeyType, 'private'), 'ascii'),
};
const data = randomBytes(32);
// sync
{
const signature = sign(undefined, data, keys.private);
assert.strictEqual(signature.byteLength, sigLen);
assert.strictEqual(verify(undefined, randomBytes(32), keys.public, signature), false);
assert.strictEqual(verify(undefined, data, keys.public, signature), true);
}
// async
{
const pSign = promisify(sign);
const pVerify = promisify(verify);
const signature = await pSign(undefined, data, keys.private);
assert.strictEqual(signature.byteLength, sigLen);
assert.strictEqual(await pVerify(undefined, randomBytes(32), keys.public, signature), false);
assert.strictEqual(await pVerify(undefined, data, keys.public, signature), true);
}
assert.throws(() => sign('sha256', data, keys.private), { code: 'ERR_OSSL_INVALID_DIGEST' });
assert.throws(
() => verify('sha256', data, keys.public, Buffer.alloc(sigLen)),
{ code: 'ERR_OSSL_INVALID_DIGEST' });
}