Commit 18c2b337af7 for nodejs
commit 18c2b337af78c15112ffd522088904a38bef76f6
Author: Filip Skokan <panva.ip@gmail.com>
Date: Wed Sep 23 10:50:56 2026 +0200
crypto: use current FIPS state for availability
Refresh conditional algorithm registration on FIPS changes and use the
current state for parameter restrictions.
Use per-algorithm availability checks in normalization and native named
key generation. Remove getPqcKeyTypes().
Enable supports() tests under FIPS and cover state transitions in warmed
workers.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66160
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
index 4d415049b05..83b3eb9d674 100644
--- a/deps/ncrypto/ncrypto.cc
+++ b/deps/ncrypto/ncrypto.cc
@@ -3101,12 +3101,6 @@ const KeyAlgorithm* KeyAlgorithm::FromName(const char* name) {
return nullptr;
}
-void KeyAlgorithm::ForEachPqc(Callback callback) {
- for (const auto* algorithm : kKeyAlgorithms) {
- if (algorithm->isPqc() && algorithm->isAvailable()) callback(*algorithm);
- }
-}
-
bool KeyAlgorithm::isRsa() const {
return this == &RSA || this == &RSA_PSS;
}
diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h
index 13444e226b6..4649c514372 100644
--- a/deps/ncrypto/ncrypto.h
+++ b/deps/ncrypto/ncrypto.h
@@ -1093,8 +1093,6 @@ class KeyAlgorithm final {
// Look up a canonical name case-insensitively, including unavailable
// algorithms.
static const KeyAlgorithm* FromName(const char* name);
- using Callback = std::function<void(const KeyAlgorithm&)>;
- static void ForEachPqc(Callback callback);
const char* name() const { return name_; }
const char* keyTypeName() const {
diff --git a/lib/internal/crypto/keygen.js b/lib/internal/crypto/keygen.js
index 4ac2fdee2df..f49165074f7 100644
--- a/lib/internal/crypto/keygen.js
+++ b/lib/internal/crypto/keygen.js
@@ -4,7 +4,6 @@ const {
FunctionPrototypeCall,
ObjectDefineProperty,
SafeArrayIterator,
- StringPrototypeToLowerCase,
} = primordials;
const {
@@ -12,7 +11,6 @@ const {
DsaKeyPairGenJob,
EcKeyPairGenJob,
NamedKeyPairGenJob,
- getPqcKeyTypes,
RsaKeyPairGenJob,
SecretKeyGenJob,
kCryptoJobAsync,
@@ -160,17 +158,6 @@ function parseKeyEncoding(keyType, options = kEmptyObject) {
];
}
-const namedKeyPairs = {
- '__proto__': null,
- 'ed25519': 'Ed25519',
- 'ed448': 'Ed448',
- 'x25519': 'X25519',
- 'x448': 'X448',
-};
-for (const name of new SafeArrayIterator(getPqcKeyTypes())) {
- namedKeyPairs[StringPrototypeToLowerCase(name)] = name;
-}
-
function createJob(mode, type, options) {
validateString(type, 'type');
@@ -337,12 +324,8 @@ function createJob(mode, type, options) {
generator == null ? 2 : generator,
...encoding);
}
- default: {
- if (namedKeyPairs[type] === undefined) {
- throw new ERR_INVALID_ARG_VALUE('type', type, 'must be a supported key type');
- }
- return new NamedKeyPairGenJob(mode, namedKeyPairs[type], ...encoding);
- }
+ default:
+ return new NamedKeyPairGenJob(mode, type, ...encoding);
}
}
diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js
index 22b51f72074..8ac5046b740 100644
--- a/lib/internal/crypto/util.js
+++ b/lib/internal/crypto/util.js
@@ -47,10 +47,19 @@ const {
getFipsCrypto,
getFipsCryptoGeneration,
KmacJob,
- getPqcKeyTypes,
+ isKeyAlgorithmAvailable,
} = internalBinding('crypto');
-const isFips = getFipsCrypto() === 1;
+let fips;
+let fipsGeneration;
+function isFips() {
+ const generation = getFipsCryptoGeneration();
+ if (fipsGeneration !== generation) {
+ fips = getFipsCrypto() === 1;
+ fipsGeneration = generation;
+ }
+ return fips;
+}
const { getOptionValue } = require('internal/options');
@@ -119,6 +128,9 @@ let _hashCache;
let _macCache;
if (isBuildingSnapshot()) {
addSerializeCallback(() => {
+ fips = undefined;
+ fipsGeneration = undefined;
+ supportedAlgorithmsGeneration = undefined;
_hashCache = undefined;
_macCache = undefined;
});
@@ -495,53 +507,69 @@ const kAlgorithmDefinitions = {
},
};
-// Conditionally supported algorithms
-const pqcKeyTypes = getPqcKeyTypes();
-
-const conditionalAlgorithms = {
- 'AES-OCB': !!hasAesOcbMode,
- 'Argon2d': !!Argon2Job,
- 'Argon2i': !!Argon2Job,
- 'Argon2id': !!Argon2Job,
- 'ChaCha20-Poly1305': process.features.openssl_is_boringssl ||
- ArrayPrototypeIncludes(getCiphers(), 'chacha20-poly1305'),
- 'cSHAKE128': !process.features.openssl_is_boringssl ||
- ArrayPrototypeIncludes(getHashes(), 'shake128'),
- 'cSHAKE256': !process.features.openssl_is_boringssl ||
- ArrayPrototypeIncludes(getHashes(), 'shake256'),
- 'Ed448': !process.features.openssl_is_boringssl,
- 'KMAC128': !!KmacJob,
- 'KMAC256': !!KmacJob,
- 'KT128': !isFips,
- 'KT256': !isFips,
- 'ML-DSA-44': ArrayPrototypeIncludes(pqcKeyTypes, 'ML-DSA-44'),
- 'ML-DSA-65': ArrayPrototypeIncludes(pqcKeyTypes, 'ML-DSA-65'),
- 'ML-DSA-87': ArrayPrototypeIncludes(pqcKeyTypes, 'ML-DSA-87'),
- 'ML-KEM-512': ArrayPrototypeIncludes(pqcKeyTypes, 'ML-KEM-512'),
- 'ML-KEM-768': ArrayPrototypeIncludes(pqcKeyTypes, 'ML-KEM-768'),
- 'ML-KEM-1024': ArrayPrototypeIncludes(pqcKeyTypes, 'ML-KEM-1024'),
- 'MLKEM768-P256': !isFips && ArrayPrototypeIncludes(pqcKeyTypes, 'ML-KEM-768') &&
- (!process.features.openssl_is_boringssl ||
- (ArrayPrototypeIncludes(getHashes(), 'sha3-256') &&
- ArrayPrototypeIncludes(getHashes(), 'shake256'))),
- 'MLKEM768-X25519': !isFips && ArrayPrototypeIncludes(pqcKeyTypes, 'ML-KEM-768') &&
- (!process.features.openssl_is_boringssl ||
- (ArrayPrototypeIncludes(getHashes(), 'sha3-256') &&
- ArrayPrototypeIncludes(getHashes(), 'shake256'))),
- 'MLKEM1024-P384': !isFips && ArrayPrototypeIncludes(pqcKeyTypes, 'ML-KEM-1024') &&
- (!process.features.openssl_is_boringssl ||
- (ArrayPrototypeIncludes(getHashes(), 'sha3-256') &&
- ArrayPrototypeIncludes(getHashes(), 'shake256'))),
- 'SHA3-256': !process.features.openssl_is_boringssl ||
- ArrayPrototypeIncludes(getHashes(), 'sha3-256'),
- 'SHA3-384': !process.features.openssl_is_boringssl ||
- ArrayPrototypeIncludes(getHashes(), 'sha3-384'),
- 'SHA3-512': !process.features.openssl_is_boringssl ||
- ArrayPrototypeIncludes(getHashes(), 'sha3-512'),
- 'TurboSHAKE128': !isFips,
- 'TurboSHAKE256': !isFips,
- 'X448': !process.features.openssl_is_boringssl,
-};
+function has(algorithms, name) {
+ return ArrayPrototypeIncludes(algorithms, name);
+}
+
+// Re-evaluated when the active FIPS state changes.
+function getConditionalAlgorithms() {
+ const mlKem768 = isKeyAlgorithmAvailable('ML-KEM-768');
+ const mlKem1024 = isKeyAlgorithmAvailable('ML-KEM-1024');
+ const ciphers = getCiphers();
+ const hashes = getHashes();
+ const macs = getMacs();
+
+ const fips = isFips();
+
+ return {
+ 'AES-OCB': !!hasAesOcbMode &&
+ (has(ciphers, 'aes-128-ocb') ||
+ has(ciphers, 'aes-192-ocb') ||
+ has(ciphers, 'aes-256-ocb')),
+ 'Argon2d': !!Argon2Job && !fips,
+ 'Argon2i': !!Argon2Job && !fips,
+ 'Argon2id': !!Argon2Job && !fips,
+ 'ChaCha20-Poly1305': process.features.openssl_is_boringssl ||
+ has(ciphers, 'chacha20-poly1305'),
+ 'cSHAKE128': has(hashes, 'shake128'),
+ 'cSHAKE256': has(hashes, 'shake256'),
+ 'Ed25519': isKeyAlgorithmAvailable('Ed25519'),
+ 'Ed448': isKeyAlgorithmAvailable('Ed448'),
+ 'KMAC128': !!KmacJob && has(macs, 'kmac128'),
+ 'KMAC256': !!KmacJob && has(macs, 'kmac256'),
+ 'KT128': !fips,
+ 'KT256': !fips,
+ 'ML-DSA-44': isKeyAlgorithmAvailable('ML-DSA-44'),
+ 'ML-DSA-65': isKeyAlgorithmAvailable('ML-DSA-65'),
+ 'ML-DSA-87': isKeyAlgorithmAvailable('ML-DSA-87'),
+ 'ML-KEM-512': isKeyAlgorithmAvailable('ML-KEM-512'),
+ 'ML-KEM-768': mlKem768,
+ 'ML-KEM-1024': mlKem1024,
+ 'MLKEM768-P256': !fips && mlKem768 &&
+ (!process.features.openssl_is_boringssl ||
+ (has(hashes, 'sha3-256') &&
+ has(hashes, 'shake256'))),
+ 'MLKEM768-X25519': !fips && mlKem768 &&
+ (!process.features.openssl_is_boringssl ||
+ (has(hashes, 'sha3-256') &&
+ has(hashes, 'shake256'))),
+ 'MLKEM1024-P384': !fips && mlKem1024 &&
+ (!process.features.openssl_is_boringssl ||
+ (has(hashes, 'sha3-256') &&
+ has(hashes, 'shake256'))),
+ 'SHA-1': has(hashes, 'sha1'),
+ 'SHA-256': has(hashes, 'sha256'),
+ 'SHA-384': has(hashes, 'sha384'),
+ 'SHA-512': has(hashes, 'sha512'),
+ 'SHA3-256': has(hashes, 'sha3-256'),
+ 'SHA3-384': has(hashes, 'sha3-384'),
+ 'SHA3-512': has(hashes, 'sha3-512'),
+ 'TurboSHAKE128': !fips,
+ 'TurboSHAKE256': !fips,
+ 'X25519': isKeyAlgorithmAvailable('X25519'),
+ 'X448': isKeyAlgorithmAvailable('X448'),
+ };
+}
// Experimental algorithms
const experimentalAlgorithms = [
@@ -578,6 +606,7 @@ const experimentalAlgorithms = [
// Also builds a parallel Map<UPPERCASED_NAME, canonicalName> per operation
// for O(1) case-insensitive algorithm name lookup in normalizeAlgorithm.
function createSupportedAlgorithms(algorithmDefs) {
+ const conditionalAlgorithms = getConditionalAlgorithms();
// Detached below rather than declared `__proto__: null`: V8 puts that
// literal form in dictionary mode, slowing every registry lookup.
const result = {};
@@ -625,8 +654,16 @@ function createSupportedAlgorithms(algorithmDefs) {
return { algorithms: result, nameMap };
}
-const { algorithms: kSupportedAlgorithms, nameMap: kAlgorithmNameMap } =
- createSupportedAlgorithms(kAlgorithmDefinitions);
+let supportedAlgorithms;
+let supportedAlgorithmsGeneration;
+function getSupportedAlgorithms() {
+ const generation = getFipsCryptoGeneration();
+ if (supportedAlgorithmsGeneration !== generation) {
+ supportedAlgorithms = createSupportedAlgorithms(kAlgorithmDefinitions);
+ supportedAlgorithmsGeneration = generation;
+ }
+ return supportedAlgorithms;
+}
const simpleAlgorithmDictionaries = {
AesCbcParams: { iv: 'BufferSource' },
@@ -688,7 +725,7 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
}
function validateKmacKeyLength(length) {
- if ((length < 32 || length % 8) && isFips)
+ if ((length < 32 || length % 8) && isFips())
throw lazyDOMException('Invalid key length', 'NotSupportedError');
}
@@ -751,16 +788,16 @@ function normalizeAlgorithm(algorithm, op) {
webidl ??= require('internal/crypto/webidl');
- // 1.
- const registeredAlgorithms = kSupportedAlgorithms[op];
// 2. 3.
const initialAlg = webidl.converters.Algorithm(algorithm,
kNormalizeAlgorithmOpts);
+ const { algorithms, nameMap } = getSupportedAlgorithms();
+ const registeredAlgorithms = algorithms[op];
// 4.
let algName = initialAlg.name;
// 5. Case-insensitive lookup via pre-built Map (O(1) instead of O(n)).
- const canonicalName = kAlgorithmNameMap[op]?.get(
+ const canonicalName = nameMap[op]?.get(
StringPrototypeToUpperCase(algName));
if (canonicalName === undefined)
throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError');
@@ -1197,7 +1234,9 @@ module.exports = {
toBuf,
kNamedCurveAliases,
- kSupportedAlgorithms,
+ get kSupportedAlgorithms() {
+ return getSupportedAlgorithms().algorithms;
+ },
isFips,
normalizeAlgorithm,
normalizeHashName,
diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js
index e61ff3d47dd..caeca416836 100644
--- a/lib/internal/crypto/webcrypto.js
+++ b/lib/internal/crypto/webcrypto.js
@@ -1757,7 +1757,6 @@ class SubtleCrypto {
}
// Implements https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-supports
- // TODO(panva): Make supports() account for the active FIPS state.
static supports(operation, algorithm, lengthOrAdditionalAlgorithm = null) {
emitExperimentalWarning('The supports Web Crypto API method');
if (this !== SubtleCrypto) throw new ERR_INVALID_THIS('SubtleCrypto constructor');
diff --git a/lib/internal/crypto/webidl.js b/lib/internal/crypto/webidl.js
index 5ea062dcaf7..73b3af6a7f5 100644
--- a/lib/internal/crypto/webidl.js
+++ b/lib/internal/crypto/webidl.js
@@ -43,8 +43,6 @@ const {
type,
} = require('internal/webidl');
-const kRsaKeyGenMinimumModulusLength = isFips ? 2048 : 512;
-
function validateByteLength(buf, name, target) {
if (getBufferSourceByteLength(buf) !== target) {
throw lazyDOMException(
@@ -157,6 +155,7 @@ const dictRsaKeyGenParams = [
converter: (V, opts) =>
converters['unsigned long'](V, enforceRangeOptions(opts)),
validator: (modulusLength) => {
+ const kRsaKeyGenMinimumModulusLength = isFips() ? 2048 : 512;
if (modulusLength < kRsaKeyGenMinimumModulusLength) {
throw lazyDOMException(
`algorithm.modulusLength must be at least ${kRsaKeyGenMinimumModulusLength}`,
@@ -285,7 +284,7 @@ function validateCShakeFunctionName(V) {
const length = getBufferSourceByteLength(V);
if (length === 0) return;
- if (!isFips) {
+ if (!isFips()) {
const bytes = getBufferSourceBytes(V);
for (let i = 0; i < kCShakeFunctionNames.length; i++) {
const functionName = kCShakeFunctionNames[i];
@@ -305,7 +304,7 @@ function validateCShakeFunctionName(V) {
}
function validateCShakeCustomization(V) {
- if (isFips && getBufferSourceByteLength(V) !== 0)
+ if (isFips() && getBufferSourceByteLength(V) !== 0)
throw lazyDOMException(
'Unsupported CShakeParams customization',
'NotSupportedError');
@@ -782,7 +781,7 @@ converters.KmacParams = createDictionaryConverter(
converter: (V, opts) =>
converters['unsigned long'](V, enforceRangeOptions(opts)),
validator: (V) => {
- if ((V === 0 || V % 8) && isFips)
+ if ((V === 0 || V % 8) && isFips())
throw lazyDOMException(
'Invalid KmacParams outputLength',
'NotSupportedError');
diff --git a/node.gyp b/node.gyp
index 8692f42c897..7ad2d95ef9e 100644
--- a/node.gyp
+++ b/node.gyp
@@ -410,7 +410,6 @@
'src/crypto/crypto_context.cc',
'src/crypto/crypto_tls_certificates.cc',
'src/crypto/crypto_ec.cc',
- 'src/crypto/crypto_pqc.cc',
'src/crypto/crypto_kem.cc',
'src/crypto/crypto_hmac.cc',
'src/crypto/crypto_kmac.cc',
@@ -451,7 +450,6 @@
'src/crypto/crypto_context.h',
'src/crypto/crypto_tls_certificates.h',
'src/crypto/crypto_ec.h',
- 'src/crypto/crypto_pqc.h',
'src/crypto/crypto_hkdf.h',
'src/crypto/crypto_pbkdf2.h',
'src/crypto/crypto_sig.h',
diff --git a/src/crypto/README.md b/src/crypto/README.md
index de4883c424c..1655fc5ea0f 100644
--- a/src/crypto/README.md
+++ b/src/crypto/README.md
@@ -46,7 +46,6 @@ following table:
| `crypto_keys` | Utilities for using and generating secret, private, and public keys. |
| `crypto_mac` | Provider-generic MAC implementations. |
| `crypto_pbkdf2` | PBKDF2 key / bit generation implementation. |
-| `crypto_pqc` | Post-quantum algorithm enumeration. |
| `crypto_rsa` | RSA Key Generation functions. |
| `crypto_scrypt` | Scrypt key / bit generation implementation. |
| `crypto_sig` | General digital signature and verification utilities. |
@@ -205,10 +204,11 @@ Public input validation remains specific to each API: PQC JWK `alg` values use
exact canonical names such as `ML-DSA-44`, while raw imports require exact public
`asymmetricKeyType` values such as `ml-dsa-44`.
-The internal JavaScript binding exposes `getPqcKeyTypes()` for the available
-known PQC algorithm names in their canonical spelling. Named key generation
-passes algorithm names to `NamedKeyPairGenJob`, which resolves the name to a static
-`KeyAlgorithm` descriptor. Asymmetric key IDs are not exposed to JavaScript.
+The internal JavaScript binding exposes `isKeyAlgorithmAvailable()` to check
+whether a known key algorithm is available from the current backend. Named key
+generation passes algorithm names to `NamedKeyPairGenJob`, which resolves the
+name to a static `KeyAlgorithm` descriptor. Asymmetric key IDs are not exposed to
+JavaScript.
Real EC curve and ASN.1/OID NIDs still have their own uses. The EC generation
path keeps Ed/X algorithm descriptors separate from curve NIDs while preserving
diff --git a/src/crypto/crypto_keygen.cc b/src/crypto/crypto_keygen.cc
index 7e273eb57a5..1da910690b1 100644
--- a/src/crypto/crypto_keygen.cc
+++ b/src/crypto/crypto_keygen.cc
@@ -9,6 +9,7 @@
#include "v8.h"
#include <cmath>
+#include <string_view>
namespace node {
@@ -19,6 +20,7 @@ using v8::JustVoid;
using v8::Local;
using v8::Maybe;
using v8::MaybeLocal;
+using v8::Nothing;
using v8::Object;
using v8::Uint32;
using v8::Value;
@@ -40,8 +42,20 @@ Maybe<void> NamedKeyPairGenTraits::AdditionalConfig(
NamedKeyPairGenConfig* params) {
CHECK(args[*offset]->IsString());
Utf8Value name(args.GetIsolate(), args[*offset]);
- params->params.algorithm = ncrypto::KeyAlgorithm::FromName(*name);
- CHECK_NOT_NULL(params->params.algorithm);
+ const auto* algorithm = ncrypto::KeyAlgorithm::FromName(*name);
+ // Traditional key generation accepts lowercase key types; Web Crypto passes
+ // normalized algorithm names and checks availability during normalization.
+ if (algorithm == nullptr || (!algorithm->isOkp() && !algorithm->isPqc()) ||
+ (mode != kCryptoJobWebCrypto &&
+ (std::string_view(*name, name.length()) != algorithm->keyTypeName() ||
+ !algorithm->isAvailable()))) {
+ THROW_ERR_INVALID_ARG_VALUE(
+ Environment::GetCurrent(args),
+ "The argument 'type' must be a supported key type. Received '%s'",
+ *name);
+ return Nothing<void>();
+ }
+ params->params.algorithm = algorithm;
*offset += 1;
diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc
index 3b22175862a..f787c97eb7f 100644
--- a/src/crypto/crypto_keys.cc
+++ b/src/crypto/crypto_keys.cc
@@ -50,6 +50,13 @@ using v8::Value;
namespace crypto {
namespace {
+void IsKeyAlgorithmAvailable(const FunctionCallbackInfo<Value>& args) {
+ CHECK(args[0]->IsString());
+ const Utf8Value name(args.GetIsolate(), args[0]);
+ const auto* algorithm = KeyAlgorithm::FromName(*name);
+ args.GetReturnValue().Set(algorithm != nullptr && algorithm->isAvailable());
+}
+
Maybe<EVPKeyPointer::AsymmetricKeyEncodingConfig> GetKeyFormatAndTypeFromJs(
const FunctionCallbackInfo<Value>& args,
unsigned int* offset,
@@ -2188,7 +2195,8 @@ void Initialize(Environment* env, Local<Object> target) {
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatPKCS8);
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatSPKI);
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatJWK);
- SetMethod(context, target, "getPqcKeyTypes", GetPqcKeyTypes);
+ SetMethodNoSideEffect(
+ context, target, "isKeyAlgorithmAvailable", IsKeyAlgorithmAvailable);
NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);
NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS8);
NODE_DEFINE_CONSTANT(target, kKeyEncodingSPKI);
@@ -2209,7 +2217,7 @@ void Initialize(Environment* env, Local<Object> target) {
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
KeyObjectHandle::RegisterExternalReferences(registry);
- registry->Register(GetPqcKeyTypes);
+ registry->Register(IsKeyAlgorithmAvailable);
}
} // namespace Keys
diff --git a/src/crypto/crypto_pqc.cc b/src/crypto/crypto_pqc.cc
deleted file mode 100644
index 17d6855ada7..00000000000
--- a/src/crypto/crypto_pqc.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "crypto/crypto_pqc.h"
-#include "ncrypto.h"
-#include "util-inl.h"
-#include "v8.h"
-
-namespace node {
-
-using v8::Value;
-
-namespace crypto {
-
-void GetPqcKeyTypes(const v8::FunctionCallbackInfo<Value>& args) {
- v8::LocalVector<Value> names(args.GetIsolate());
- ncrypto::KeyAlgorithm::ForEachPqc(
- [&](const ncrypto::KeyAlgorithm& algorithm) {
- names.push_back(OneByteString(args.GetIsolate(), algorithm.name()));
- });
- args.GetReturnValue().Set(
- v8::Array::New(args.GetIsolate(), names.data(), names.size()));
-}
-} // namespace crypto
-} // namespace node
diff --git a/src/crypto/crypto_pqc.h b/src/crypto/crypto_pqc.h
deleted file mode 100644
index 23cdcdc0512..00000000000
--- a/src/crypto/crypto_pqc.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef SRC_CRYPTO_CRYPTO_PQC_H_
-#define SRC_CRYPTO_CRYPTO_PQC_H_
-
-#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
-
-#include "v8.h"
-
-namespace node {
-namespace crypto {
-void GetPqcKeyTypes(const v8::FunctionCallbackInfo<v8::Value>& args);
-} // namespace crypto
-} // namespace node
-
-#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
-#endif // SRC_CRYPTO_CRYPTO_PQC_H_
diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc
index dffa807d183..1e003e9e51b 100644
--- a/src/crypto/crypto_sig.cc
+++ b/src/crypto/crypto_sig.cc
@@ -3,7 +3,6 @@
#include "base_object-inl.h"
#include "crypto/crypto_ec.h"
#include "crypto/crypto_keys.h"
-#include "crypto/crypto_pqc.h"
#include "crypto/crypto_util.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
diff --git a/src/node_crypto.h b/src/node_crypto.h
index e15e5958808..92d35b844f9 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -51,7 +51,6 @@
#include "crypto/crypto_mac.h"
#include "crypto/crypto_pbkdf2.h"
#include "crypto/crypto_pkcs12.h"
-#include "crypto/crypto_pqc.h"
#include "crypto/crypto_random.h"
#include "crypto/crypto_rsa.h"
#include "crypto/crypto_scrypt.h"
diff --git a/test/fixtures/webcrypto/supports-level-2.mjs b/test/fixtures/webcrypto/supports-level-2.mjs
index b07ce9097bb..dacaa907002 100644
--- a/test/fixtures/webcrypto/supports-level-2.mjs
+++ b/test/fixtures/webcrypto/supports-level-2.mjs
@@ -1,4 +1,4 @@
-import { getFips } from 'node:crypto';
+import { generateKeyPairSync, getFips } from 'node:crypto';
const { subtle } = globalThis.crypto;
const RSA_MINIMUM_MODULUS_LENGTH = getFips() === 1 ? 2048 : 512;
@@ -8,10 +8,28 @@ const RSA_KEY_GEN = {
publicExponent: new Uint8Array([1, 0, 1])
};
-const [ECDH, X25519] = await Promise.all([
- subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits', 'deriveKey']),
- subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']),
-]);
+// Determine availability through traditional crypto, independently of supports().
+export function generateNamedKeyPair(name) {
+ let pair;
+ try {
+ pair = generateKeyPairSync(name.toLowerCase());
+ } catch (err) {
+ if (err.code !== 'ERR_INVALID_ARG_VALUE') throw err;
+ return;
+ }
+ const derive = name.startsWith('X');
+ return {
+ publicKey: pair.publicKey.toCryptoKey(name, true, derive ? [] : ['verify']),
+ privateKey: pair.privateKey.toCryptoKey(name, false, derive ? ['deriveBits', 'deriveKey'] : ['sign']),
+ };
+}
+
+export const ECDH = await subtle.generateKey(
+ { name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits', 'deriveKey']);
+export const X25519 = generateNamedKeyPair('X25519');
+export const Ed25519 = generateNamedKeyPair('Ed25519');
+const hasX25519 = X25519 !== undefined;
+const hasEd25519 = Ed25519 !== undefined;
export const vectors = {
'encrypt': [
@@ -32,7 +50,7 @@ export const vectors = {
[false, 'Invalid'],
[false, 'SHA-1'],
- [true, 'Ed25519'],
+ [hasEd25519, 'Ed25519'],
[true, 'RSASSA-PKCS1-v1_5'],
@@ -59,8 +77,8 @@ export const vectors = {
[false, 'Invalid'],
[false, 'HKDF'],
[false, 'PBKDF2'],
- [true, 'X25519'],
- [true, 'Ed25519'],
+ [hasX25519, 'X25519'],
+ [hasEd25519, 'Ed25519'],
[true, { name: 'HMAC', hash: 'SHA-256' }],
[true, { name: 'HMAC', hash: 'SHA-256', length: 256 }],
[true, { name: 'HMAC', hash: 'SHA-256', length: 25 }],
@@ -128,20 +146,20 @@ export const vectors = {
[false,
{ name: 'PBKDF2', hash: 'SHA-256', salt: Buffer.alloc(0), iterations: 1 },
'HKDF'],
- [true,
- { name: 'X25519', public: X25519.publicKey },
+ [hasX25519,
+ { name: 'X25519', public: X25519?.publicKey },
{ name: 'AES-CBC', length: 128 }],
[false,
- { name: 'X25519', public: X25519.publicKey },
+ { name: 'X25519', public: X25519?.publicKey },
{ name: 'HMAC', hash: 'SHA-256' }],
- [true,
- { name: 'X25519', public: X25519.publicKey },
+ [hasX25519,
+ { name: 'X25519', public: X25519?.publicKey },
{ name: 'HMAC', hash: 'SHA-256', length: 256 }],
[false,
- { name: 'X25519', public: X25519.publicKey },
+ { name: 'X25519', public: X25519?.publicKey },
{ name: 'HMAC', hash: 'SHA-256', length: 257 }],
- [true,
- { name: 'X25519', public: X25519.publicKey },
+ [hasX25519,
+ { name: 'X25519', public: X25519?.publicKey },
'HKDF'],
[true,
{ name: 'ECDH', public: ECDH.publicKey },
@@ -165,10 +183,10 @@ export const vectors = {
{ name: 'ECDH', public: ECDH.publicKey },
'HKDF'],
[false,
- { name: 'X25519', public: X25519.publicKey },
+ { name: 'X25519', public: X25519?.publicKey },
'SHA-256'],
[false,
- { name: 'X25519', public: X25519.publicKey },
+ { name: 'X25519', public: X25519?.publicKey },
'AES-CBC'],
],
'deriveBits': [
@@ -201,17 +219,17 @@ export const vectors = {
[false, { name: 'ECDH', public: ECDH.privateKey }],
[false, 'ECDH'],
- [true, { name: 'X25519', public: X25519.publicKey }],
- [true, { name: 'X25519', public: X25519.publicKey }, 256],
- [false, { name: 'X25519', public: X25519.publicKey }, 257],
- [false, { name: 'X25519', public: X25519.privateKey }],
+ [hasX25519, { name: 'X25519', public: X25519?.publicKey }],
+ [hasX25519, { name: 'X25519', public: X25519?.publicKey }, 256],
+ [false, { name: 'X25519', public: X25519?.publicKey }, 257],
+ [false, { name: 'X25519', public: X25519?.privateKey }],
[false, 'X25519'],
],
'importKey': [
[false, 'SHA-1'],
[false, 'Invalid'],
- [true, 'X25519'],
- [true, 'Ed25519'],
+ [hasX25519, 'X25519'],
+ [hasEd25519, 'Ed25519'],
[true, { name: 'HMAC', hash: 'SHA-256' }],
[true, { name: 'HMAC', hash: 'SHA-256', length: 256 }],
[true, { name: 'HMAC', hash: 'SHA-256', length: 25 }],
@@ -246,8 +264,8 @@ export const vectors = {
[true, 'AES-CBC'],
[true, 'AES-GCM'],
[true, 'AES-KW'],
- [true, 'Ed25519'],
- [true, 'X25519'],
+ [hasEd25519, 'Ed25519'],
+ [hasX25519, 'X25519'],
],
'wrapKey': [
[false, 'AES-KW'],
diff --git a/test/fixtures/webcrypto/supports-modern-algorithms.mjs b/test/fixtures/webcrypto/supports-modern-algorithms.mjs
index 67fad15f7f3..b01ebce3e5a 100644
--- a/test/fixtures/webcrypto/supports-modern-algorithms.mjs
+++ b/test/fixtures/webcrypto/supports-modern-algorithms.mjs
@@ -1,63 +1,65 @@
import * as crypto from 'node:crypto'
import { hasOpenSSL, isBoringSSL } from '../../common/crypto.js'
+import { X25519, Ed25519 } from './supports-level-2.mjs'
+import { X448, Ed448 } from './supports-secure-curves.mjs'
const boringSSL = isBoringSSL;
+const fips = crypto.getFips() === 1;
+const chacha = boringSSL || crypto.getCiphers().includes('chacha20-poly1305');
const pqc = hasOpenSSL(3, 5) || boringSSL;
-const argon2 = hasOpenSSL(3, 2);
+const argon2 = hasOpenSSL(3, 2) && !fips;
const shake128 = crypto.getHashes().includes('shake128');
const shake256 = crypto.getHashes().includes('shake256');
const sha3 = crypto.getHashes().includes('sha3-256');
-const ocb = hasOpenSSL(3);
-const kmac = hasOpenSSL(3);
-const hybridKems = pqc && (!boringSSL || (sha3 && shake256));
+const ocb = hasOpenSSL(3) && crypto.getCiphers().includes('aes-128-ocb');
+const kmac = hasOpenSSL(3) && crypto.getMacs().includes('kmac128');
+const hybridKems = !fips && pqc && (!boringSSL || (sha3 && shake256));
-const { subtle } = globalThis.crypto;
-const X25519 = await subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']);
export const vectors = {
'digest': [
[false, 'cSHAKE128'],
[shake128, { name: 'cSHAKE128', outputLength: 128 }],
[shake128, { name: 'cSHAKE128', outputLength: 128, functionName: Buffer.alloc(0), customization: Buffer.alloc(0) }],
- [shake128 && kmac, { name: 'cSHAKE128', outputLength: 128, functionName: Buffer.from('KMAC') }],
+ [shake128 && kmac && !fips, { name: 'cSHAKE128', outputLength: 128, functionName: Buffer.from('KMAC') }],
[false, { name: 'cSHAKE128', outputLength: 128, functionName: Buffer.from('SHAKE') }],
- [shake128 && kmac, { name: 'cSHAKE128', outputLength: 128, customization: Buffer.alloc(1) }],
+ [shake128 && kmac && !fips, { name: 'cSHAKE128', outputLength: 128, customization: Buffer.alloc(1) }],
[false, { name: 'cSHAKE128', outputLength: 128, customization: Buffer.alloc(513) }],
[shake128, { name: 'cSHAKE128', outputLength: 127 }],
[false, 'cSHAKE256'],
[shake256, { name: 'cSHAKE256', outputLength: 256 }],
[shake256, { name: 'cSHAKE256', outputLength: 256, functionName: Buffer.alloc(0), customization: Buffer.alloc(0) }],
- [shake256 && kmac, { name: 'cSHAKE256', outputLength: 256, functionName: Buffer.from('KMAC') }],
+ [shake256 && kmac && !fips, { name: 'cSHAKE256', outputLength: 256, functionName: Buffer.from('KMAC') }],
[false, { name: 'cSHAKE256', outputLength: 256, functionName: Buffer.from('SHAKE') }],
- [shake256 && kmac, { name: 'cSHAKE256', outputLength: 256, customization: Buffer.alloc(1) }],
+ [shake256 && kmac && !fips, { name: 'cSHAKE256', outputLength: 256, customization: Buffer.alloc(1) }],
[false, { name: 'cSHAKE256', outputLength: 256, customization: Buffer.alloc(513) }],
[shake256, { name: 'cSHAKE256', outputLength: 255 }],
[false, 'TurboSHAKE128'],
- [true, { name: 'TurboSHAKE128', outputLength: 128 }],
- [true, { name: 'TurboSHAKE128', outputLength: 128, domainSeparation: 0x07 }],
+ [!fips, { name: 'TurboSHAKE128', outputLength: 128 }],
+ [!fips, { name: 'TurboSHAKE128', outputLength: 128, domainSeparation: 0x07 }],
[false, { name: 'TurboSHAKE128', outputLength: 0 }],
[false, { name: 'TurboSHAKE128', outputLength: 127 }],
[false, { name: 'TurboSHAKE128', outputLength: 128, domainSeparation: 0x00 }],
[false, { name: 'TurboSHAKE128', outputLength: 128, domainSeparation: 0x80 }],
[false, 'TurboSHAKE256'],
- [true, { name: 'TurboSHAKE256', outputLength: 256 }],
- [true, { name: 'TurboSHAKE256', outputLength: 256, domainSeparation: 0x07 }],
+ [!fips, { name: 'TurboSHAKE256', outputLength: 256 }],
+ [!fips, { name: 'TurboSHAKE256', outputLength: 256, domainSeparation: 0x07 }],
[false, { name: 'TurboSHAKE256', outputLength: 0 }],
[false, { name: 'TurboSHAKE256', outputLength: 255 }],
[false, { name: 'TurboSHAKE256', outputLength: 256, domainSeparation: 0x00 }],
[false, { name: 'TurboSHAKE256', outputLength: 256, domainSeparation: 0x80 }],
[false, 'KT128'],
- [true, { name: 'KT128', outputLength: 128 }],
- [true, { name: 'KT128', outputLength: 128, customization: Buffer.alloc(0) }],
- [true, { name: 'KT128', outputLength: 128, customization: Buffer.alloc(512) }],
+ [!fips, { name: 'KT128', outputLength: 128 }],
+ [!fips, { name: 'KT128', outputLength: 128, customization: Buffer.alloc(0) }],
+ [!fips, { name: 'KT128', outputLength: 128, customization: Buffer.alloc(512) }],
[false, { name: 'KT128', outputLength: 128, customization: Buffer.alloc(513) }],
[false, { name: 'KT128', outputLength: 0 }],
[false, { name: 'KT128', outputLength: 127 }],
[false, 'KT256'],
- [true, { name: 'KT256', outputLength: 256 }],
- [true, { name: 'KT256', outputLength: 256, customization: Buffer.alloc(0) }],
- [true, { name: 'KT256', outputLength: 256, customization: Buffer.alloc(512) }],
+ [!fips, { name: 'KT256', outputLength: 256 }],
+ [!fips, { name: 'KT256', outputLength: 256, customization: Buffer.alloc(0) }],
+ [!fips, { name: 'KT256', outputLength: 256, customization: Buffer.alloc(512) }],
[false, { name: 'KT256', outputLength: 256, customization: Buffer.alloc(513) }],
[false, { name: 'KT256', outputLength: 0 }],
[false, { name: 'KT256', outputLength: 255 }],
@@ -84,9 +86,9 @@ export const vectors = {
[false, 'KMAC128'],
[false, 'KMAC256'],
[kmac, { name: 'KMAC128', outputLength: 256 }],
- [kmac, { name: 'KMAC128', outputLength: 255 }],
+ [kmac && !fips, { name: 'KMAC128', outputLength: 255 }],
[kmac, { name: 'KMAC256', outputLength: 256 }],
- [kmac, { name: 'KMAC256', outputLength: 255 }],
+ [kmac && !fips, { name: 'KMAC256', outputLength: 255 }],
],
'generateKey': [
[pqc, 'ML-DSA-44'],
@@ -95,7 +97,7 @@ export const vectors = {
[pqc && !boringSSL, 'ML-KEM-512'],
[pqc, 'ML-KEM-768'],
[pqc, 'ML-KEM-1024'],
- [true, 'ChaCha20-Poly1305'],
+ [chacha, 'ChaCha20-Poly1305'],
[hybridKems, 'MLKEM768-P256'],
[hybridKems, 'MLKEM768-X25519'],
[hybridKems, 'MLKEM1024-P384'],
@@ -107,10 +109,10 @@ export const vectors = {
[kmac, 'KMAC256'],
[kmac, { name: 'KMAC128', length: 256 }],
[kmac, { name: 'KMAC256', length: 128 }],
- [kmac, { name: 'KMAC128', length: 0 }],
- [kmac, { name: 'KMAC256', length: 0 }],
- [kmac, { name: 'KMAC128', length: 1 }],
- [kmac, { name: 'KMAC256', length: 1 }],
+ [kmac && !fips, { name: 'KMAC128', length: 0 }],
+ [kmac && !fips, { name: 'KMAC256', length: 0 }],
+ [kmac && !fips, { name: 'KMAC128', length: 1 }],
+ [kmac && !fips, { name: 'KMAC256', length: 1 }],
],
'importKey': [
[pqc, 'ML-DSA-44'],
@@ -119,7 +121,7 @@ export const vectors = {
[pqc && !boringSSL, 'ML-KEM-512'],
[pqc, 'ML-KEM-768'],
[pqc, 'ML-KEM-1024'],
- [true, 'ChaCha20-Poly1305'],
+ [chacha, 'ChaCha20-Poly1305'],
[hybridKems, 'MLKEM768-P256'],
[hybridKems, 'MLKEM768-X25519'],
[hybridKems, 'MLKEM1024-P384'],
@@ -131,10 +133,10 @@ export const vectors = {
[kmac, 'KMAC256'],
[kmac, { name: 'KMAC128', length: 256 }],
[kmac, { name: 'KMAC256', length: 128 }],
- [kmac, { name: 'KMAC128', length: 0 }],
- [kmac, { name: 'KMAC256', length: 0 }],
- [kmac, { name: 'KMAC128', length: 1 }],
- [kmac, { name: 'KMAC256', length: 1 }],
+ [kmac && !fips, { name: 'KMAC128', length: 0 }],
+ [kmac && !fips, { name: 'KMAC256', length: 0 }],
+ [kmac && !fips, { name: 'KMAC128', length: 1 }],
+ [kmac && !fips, { name: 'KMAC256', length: 1 }],
],
'exportKey': [
[pqc, 'ML-DSA-44'],
@@ -143,7 +145,7 @@ export const vectors = {
[pqc && !boringSSL, 'ML-KEM-512'],
[pqc, 'ML-KEM-768'],
[pqc, 'ML-KEM-1024'],
- [true, 'ChaCha20-Poly1305'],
+ [chacha, 'ChaCha20-Poly1305'],
[hybridKems, 'MLKEM768-P256'],
[hybridKems, 'MLKEM768-X25519'],
[hybridKems, 'MLKEM1024-P384'],
@@ -158,10 +160,10 @@ export const vectors = {
[true, 'RSA-OAEP'],
[true, 'RSA-PSS'],
[true, 'RSASSA-PKCS1-v1_5'],
- [true, 'X25519'],
- [!boringSSL, 'X448'],
- [true, 'Ed25519'],
- [!boringSSL, 'Ed448'],
+ [!!X25519, 'X25519'],
+ [!!X448, 'X448'],
+ [!!Ed25519, 'Ed25519'],
+ [!!Ed448, 'Ed448'],
[true, 'ECDH'],
[true, 'ECDSA'],
[pqc, 'ML-DSA-44'],
@@ -186,14 +188,14 @@ export const vectors = {
[false, 'KMAC256'],
],
'deriveKey': [
- [argon2,
- { name: 'X25519', public: X25519.publicKey },
+ [argon2 && !!X25519,
+ { name: 'X25519', public: X25519?.publicKey },
'Argon2d'],
- [argon2,
- { name: 'X25519', public: X25519.publicKey },
+ [argon2 && !!X25519,
+ { name: 'X25519', public: X25519?.publicKey },
'Argon2i'],
- [argon2,
- { name: 'X25519', public: X25519.publicKey },
+ [argon2 && !!X25519,
+ { name: 'X25519', public: X25519?.publicKey },
'Argon2id'],
],
'deriveBits': [
@@ -215,9 +217,9 @@ export const vectors = {
[false, { name: 'Argon2d', nonce: Buffer.alloc(8), parallelism: 16777215, memory: 8, passes: 1 }, 32],
],
'encrypt': [
- [true, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12) }],
+ [chacha, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12) }],
[false, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(16) }],
- [true, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12), tagLength: 128 }],
+ [chacha, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12), tagLength: 128 }],
[false, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12), tagLength: 64 }],
[false, 'ChaCha20-Poly1305'],
[ocb, { name: 'AES-OCB', iv: Buffer.alloc(15) }],
@@ -242,14 +244,14 @@ export const vectors = {
[pqc, 'ML-KEM-768', 'AES-GCM'],
[pqc, 'ML-KEM-768', 'AES-CTR'],
[pqc, 'ML-KEM-768', 'AES-CBC'],
- [pqc, 'ML-KEM-768', 'ChaCha20-Poly1305'],
+ [pqc && chacha, 'ML-KEM-768', 'ChaCha20-Poly1305'],
[pqc, 'ML-KEM-768', 'HKDF'],
[pqc, 'ML-KEM-768', 'PBKDF2'],
[pqc, 'ML-KEM-768', { name: 'HMAC', hash: 'SHA-256' }],
[pqc, 'ML-KEM-768', { name: 'HMAC', hash: 'SHA-256', length: 256 }],
[pqc, 'ML-KEM-768', { name: 'HMAC', hash: 'SHA-256', length: 255 }],
[pqc, 'ML-KEM-768', { name: 'HMAC', hash: 'SHA-256', length: 249 }],
- [pqc && kmac, 'ML-KEM-768', { name: 'KMAC128', length: 255 }],
+ [pqc && kmac && !fips, 'ML-KEM-768', { name: 'KMAC128', length: 255 }],
[hybridKems, 'MLKEM768-P256', 'HKDF'],
[hybridKems, 'MLKEM768-X25519', 'HKDF'],
[hybridKems, 'MLKEM1024-P384', 'HKDF'],
@@ -274,14 +276,14 @@ export const vectors = {
[pqc, 'ML-KEM-768', 'AES-GCM'],
[pqc, 'ML-KEM-768', 'AES-CTR'],
[pqc, 'ML-KEM-768', 'AES-CBC'],
- [pqc, 'ML-KEM-768', 'ChaCha20-Poly1305'],
+ [pqc && chacha, 'ML-KEM-768', 'ChaCha20-Poly1305'],
[pqc, 'ML-KEM-768', 'HKDF'],
[pqc, 'ML-KEM-768', 'PBKDF2'],
[pqc, 'ML-KEM-768', { name: 'HMAC', hash: 'SHA-256' }],
[pqc, 'ML-KEM-768', { name: 'HMAC', hash: 'SHA-256', length: 256 }],
[pqc, 'ML-KEM-768', { name: 'HMAC', hash: 'SHA-256', length: 255 }],
[pqc, 'ML-KEM-768', { name: 'HMAC', hash: 'SHA-256', length: 249 }],
- [pqc && kmac, 'ML-KEM-768', { name: 'KMAC128', length: 255 }],
+ [pqc && kmac && !fips, 'ML-KEM-768', { name: 'KMAC128', length: 255 }],
[hybridKems, 'MLKEM768-P256', 'HKDF'],
[hybridKems, 'MLKEM768-X25519', 'HKDF'],
[hybridKems, 'MLKEM1024-P384', 'HKDF'],
diff --git a/test/fixtures/webcrypto/supports-secure-curves.mjs b/test/fixtures/webcrypto/supports-secure-curves.mjs
index fa3f1d36521..52dd1c95995 100644
--- a/test/fixtures/webcrypto/supports-secure-curves.mjs
+++ b/test/fixtures/webcrypto/supports-secure-curves.mjs
@@ -1,62 +1,55 @@
-import { hasOpenSSL, isBoringSSL } from '../../common/crypto.js'
+import { hasOpenSSL } from '../../common/crypto.js';
+import { generateNamedKeyPair, X25519 } from './supports-level-2.mjs';
const supportsContext = hasOpenSSL(3, 2);
-
-const { subtle } = globalThis.crypto;
-
-const boringSSL = isBoringSSL;
-
-const X25519 = await subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']);
-let X448;
-let Ed448;
-if (!boringSSL) {
- X448 = await subtle.generateKey('X448', false, ['deriveBits', 'deriveKey'])
- Ed448 = await subtle.generateKey('Ed448', false, ['sign', 'verify'])
-}
+export const X448 = generateNamedKeyPair('X448');
+export const Ed448 = generateNamedKeyPair('Ed448');
+const hasX448 = X448 !== undefined;
+const hasEd448 = Ed448 !== undefined;
export const vectors = {
'sign': [
- [!boringSSL, 'Ed448'],
- [!boringSSL, { name: 'Ed448', context: Buffer.alloc(0) }],
- [!boringSSL && supportsContext, { name: 'Ed448', context: Buffer.alloc(32) }],
- [!boringSSL && supportsContext, { name: 'Ed448', context: Buffer.alloc(255) }],
+ [hasEd448, 'Ed448'],
+ [hasEd448, { name: 'Ed448', context: Buffer.alloc(0) }],
+ [hasEd448 && supportsContext, { name: 'Ed448', context: Buffer.alloc(32) }],
+ [hasEd448 && supportsContext, { name: 'Ed448', context: Buffer.alloc(255) }],
[false, { name: 'Ed448', context: Buffer.alloc(256) }],
],
'generateKey': [
- [!boringSSL, 'X448'],
- [!boringSSL, 'Ed448'],
+ [hasX448, 'X448'],
+ [hasEd448, 'Ed448'],
],
'deriveKey': [
- [!boringSSL,
+ [hasX448,
{ name: 'X448', public: X448?.publicKey },
{ name: 'AES-CBC', length: 128 }],
[false,
{ name: 'X448', public: X448?.publicKey },
{ name: 'HMAC', hash: 'SHA-256' }],
- [!boringSSL,
+ [hasX448,
{ name: 'X448', public: X448?.publicKey },
{ name: 'HMAC', hash: 'SHA-256', length: 448 }],
[false,
{ name: 'X448', public: X448?.publicKey },
{ name: 'HMAC', hash: 'SHA-256', length: 449 }],
- [!boringSSL,
+ [hasX448,
{ name: 'X448', public: X448?.publicKey },
'HKDF'],
],
'deriveBits': [
- [!boringSSL, { name: 'X448', public: X448?.publicKey }],
- [!boringSSL, { name: 'X448', public: X448?.publicKey }, 448],
+ [hasX448, { name: 'X448', public: X448?.publicKey }],
+ [hasX448, { name: 'X448', public: X448?.publicKey }, 448],
[false, { name: 'X448', public: X448?.publicKey }, 449],
- [false, { name: 'X448', public: X25519.publicKey }],
+ [false, { name: 'X448', public: X25519?.publicKey }],
[false, { name: 'X448', public: X448?.privateKey }],
[false, 'X448'],
],
'importKey': [
- [!boringSSL, 'X448'],
- [!boringSSL, 'Ed448'],
+ [hasX448, 'X448'],
+ [hasEd448, 'Ed448'],
],
'exportKey': [
- [!boringSSL, 'Ed448'],
- [!boringSSL, 'X448'],
+ [hasEd448, 'Ed448'],
+ [hasX448, 'X448'],
],
};
diff --git a/test/fixtures/webcrypto/supports-sha3.mjs b/test/fixtures/webcrypto/supports-sha3.mjs
index fe256dcfb16..699b72bb11b 100644
--- a/test/fixtures/webcrypto/supports-sha3.mjs
+++ b/test/fixtures/webcrypto/supports-sha3.mjs
@@ -1,33 +1,28 @@
-import { isBoringSSL } from '../../common/crypto.js'
+import { getHashes } from 'node:crypto';
+import { ECDH, X25519 } from './supports-level-2.mjs';
-const { subtle } = globalThis.crypto;
-
-const boringSSL = isBoringSSL;
+const hashes = getHashes();
+const hasSha3 = hashes.includes('sha3-256');
const RSA_KEY_GEN = {
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1])
};
-const [ECDH, X25519] = await Promise.all([
- subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits', 'deriveKey']),
- subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']),
-]);
-
export const vectors = {
'digest': [
- [!boringSSL, 'SHA3-256'],
- [!boringSSL, 'SHA3-384'],
- [!boringSSL, 'SHA3-512'],
+ [hasSha3, 'SHA3-256'],
+ [hashes.includes('sha3-384'), 'SHA3-384'],
+ [hashes.includes('sha3-512'), 'SHA3-512'],
],
'generateKey': [
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
- [!boringSSL, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
- [!boringSSL, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
- [!boringSSL, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
+ [hasSha3, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [hasSha3, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [hasSha3, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 0 }],
// This interaction is not defined for now.
@@ -35,28 +30,28 @@ export const vectors = {
[false, { name: 'HMAC', hash: 'SHA3-256' }],
],
'deriveKey': [
- [!boringSSL,
+ [hasSha3,
{ name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
{ name: 'AES-CBC', length: 128 }],
- [!boringSSL,
+ [hasSha3,
{ name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
{ name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[false,
{ name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
'HKDF'],
- [!boringSSL,
+ [hasSha3,
{ name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
{ name: 'AES-CBC', length: 128 }],
- [!boringSSL,
+ [hasSha3,
{ name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
{ name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[false,
{ name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
'HKDF'],
- [!boringSSL,
- { name: 'X25519', public: X25519.publicKey },
+ [hasSha3 && X25519 !== undefined,
+ { name: 'X25519', public: X25519?.publicKey },
{ name: 'HMAC', hash: 'SHA3-256', length: 256 }],
- [!boringSSL,
+ [hasSha3,
{ name: 'ECDH', public: ECDH.publicKey },
{ name: 'HMAC', hash: 'SHA3-256', length: 256 }],
@@ -69,34 +64,34 @@ export const vectors = {
{ name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
{ name: 'HMAC', hash: 'SHA3-256' }],
[false,
- { name: 'X25519', public: X25519.publicKey },
+ { name: 'X25519', public: X25519?.publicKey },
{ name: 'HMAC', hash: 'SHA3-256' }],
[false,
{ name: 'ECDH', public: ECDH.publicKey },
{ name: 'HMAC', hash: 'SHA3-256' }],
],
'deriveBits': [
- [!boringSSL, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 8],
- [!boringSSL, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 0],
+ [hasSha3, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 8],
+ [hasSha3, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 0],
[false, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, null],
[false, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 7],
- [!boringSSL, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 8],
- [!boringSSL, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 0],
+ [hasSha3, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 8],
+ [hasSha3, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 0],
[false, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 0 }, 8],
[false, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, null],
[false, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 7],
],
'importKey': [
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
- [!boringSSL, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
- [!boringSSL, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
- [!boringSSL, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
- [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256' }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
+ [hasSha3, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [hasSha3, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [hasSha3, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256' }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+ [hasSha3, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 0 }],
],
'get key length': [
diff --git a/test/parallel/test-crypto-key-objects-to-crypto-key.js b/test/parallel/test-crypto-key-objects-to-crypto-key.js
index f699ec5462c..ba41eb64eb2 100644
--- a/test/parallel/test-crypto-key-objects-to-crypto-key.js
+++ b/test/parallel/test-crypto-key-objects-to-crypto-key.js
@@ -257,7 +257,7 @@ function ecVectors(name, usagesByType) {
function cfrgVectors(name, usagesByType) {
if (rejectsXCurves && name.startsWith('X')) {
assert.throws(() => generateKeyPairSync(name.toLowerCase()), {
- code: 'ERR_OSSL_EVP_UNSUPPORTED',
+ code: 'ERR_INVALID_ARG_VALUE',
});
return [];
}
@@ -313,9 +313,12 @@ function invalidAsymmetricKeyType(name, invalidAlgorithm) {
const { publicKey } = generateKeyPairSync(name.toLowerCase());
assert.throws(() => {
publicKey.toCryptoKey(invalidAlgorithm, true, []);
- }, {
+ }, invalidAlgorithm in kSupportedAlgorithms.importKey ? {
name: 'DataError',
message: 'Invalid key type'
+ } : {
+ name: 'NotSupportedError',
+ message: 'Unrecognized algorithm name',
});
}
diff --git a/test/parallel/test-crypto-key-objects.js b/test/parallel/test-crypto-key-objects.js
index 6dfdada9430..97810af5d9c 100644
--- a/test/parallel/test-crypto-key-objects.js
+++ b/test/parallel/test-crypto-key-objects.js
@@ -1216,7 +1216,7 @@ if (!isBoringSSL) {
const first = generateKeyPairSync('ed25519');
if (rejectsXCurves) {
assert.throws(() => generateKeyPairSync('x25519'), {
- code: 'ERR_OSSL_EVP_UNSUPPORTED',
+ code: 'ERR_INVALID_ARG_VALUE',
});
} else {
const second = generateKeyPairSync('x25519');
diff --git a/test/parallel/test-crypto-key-store.js b/test/parallel/test-crypto-key-store.js
index 58a23192b28..d5636f350dc 100644
--- a/test/parallel/test-crypto-key-store.js
+++ b/test/parallel/test-crypto-key-store.js
@@ -86,7 +86,7 @@ const data = Buffer.from('hello store');
{
if (hasFIPS(3, 5)) {
assert.throws(() => generateKeyPairSync('x25519'), {
- code: 'ERR_OSSL_EVP_UNSUPPORTED',
+ code: 'ERR_INVALID_ARG_VALUE',
});
} else {
const alice = generateKeyPairSync('x25519');
diff --git a/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk.js b/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk.js
index 9985631b409..bd097f04eca 100644
--- a/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk.js
+++ b/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk.js
@@ -18,19 +18,21 @@ const rejectsXCurves = hasFIPS(3, 5);
common.printSkipMessage(`Skipping unsupported ${type} test case`);
continue;
}
- generateKeyPair(type, {
+ const options = {
publicKeyEncoding: {
format: 'jwk'
},
privateKeyEncoding: {
format: 'jwk'
}
- }, common.mustCall((err, publicKey, privateKey) => {
- if (rejectsXCurves && type.startsWith('x')) {
- assert.strictEqual(err?.code, 'ERR_OSSL_EVP_UNSUPPORTED');
- return;
- }
- assert.ifError(err);
+ };
+ if (rejectsXCurves && type.startsWith('x')) {
+ assert.throws(() => generateKeyPair(type, options, common.mustNotCall()), {
+ code: 'ERR_INVALID_ARG_VALUE',
+ });
+ continue;
+ }
+ generateKeyPair(type, options, common.mustSucceed((publicKey, privateKey) => {
assert.strictEqual(typeof publicKey, 'object');
assert.strictEqual(typeof privateKey, 'object');
assert.strictEqual(publicKey.x, privateKey.x);
diff --git a/test/parallel/test-crypto-keygen-eddsa.js b/test/parallel/test-crypto-keygen-eddsa.js
index 0f9f8421ce7..a9faf39d0ab 100644
--- a/test/parallel/test-crypto-keygen-eddsa.js
+++ b/test/parallel/test-crypto-keygen-eddsa.js
@@ -7,23 +7,40 @@ if (!common.hasCrypto)
const assert = require('assert');
const {
generateKeyPair,
+ generateKeyPairSync,
} = require('crypto');
const { hasFIPS, isBoringSSL } = require('../common/crypto');
const rejectsXCurves = hasFIPS(3, 5);
+// Named key generation accepts only supported, lowercase public key types.
+for (const type of [
+ 'toString', 'constructor', 'sm2', 'ml-kem-999',
+ 'Ed25519', 'X25519', 'ML-KEM-768', 'ML-DSA-44', 'SLH-DSA-SHA2-128f',
+]) {
+ const error = {
+ name: 'TypeError',
+ code: 'ERR_INVALID_ARG_VALUE',
+ message: `The argument 'type' must be a supported key type. Received '${type}'`,
+ };
+ assert.throws(() => generateKeyPairSync(type), error);
+ assert.throws(() => generateKeyPair(type, common.mustNotCall()), error);
+}
+
// Test EdDSA key generation.
{
for (const keyType of ['ed25519', 'ed448', 'x25519', 'x448']) {
- if (isBoringSSL && keyType.endsWith('448')) {
- common.printSkipMessage(`Skipping unsupported ${keyType} test case`);
+ if ((isBoringSSL && keyType.endsWith('448')) ||
+ (rejectsXCurves && keyType.startsWith('x'))) {
+ const error = {
+ name: 'TypeError',
+ code: 'ERR_INVALID_ARG_VALUE',
+ message: `The argument 'type' must be a supported key type. Received '${keyType}'`,
+ };
+ assert.throws(() => generateKeyPairSync(keyType), error);
+ assert.throws(() => generateKeyPair(keyType, common.mustNotCall()), error);
continue;
}
- generateKeyPair(keyType, common.mustCall((err, publicKey, privateKey) => {
- if (rejectsXCurves && keyType.startsWith('x')) {
- assert.strictEqual(err?.code, 'ERR_OSSL_EVP_UNSUPPORTED');
- return;
- }
- assert.ifError(err);
+ generateKeyPair(keyType, common.mustSucceed((publicKey, privateKey) => {
assert.strictEqual(publicKey.type, 'public');
assert.strictEqual(publicKey.asymmetricKeyType, keyType);
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {});
diff --git a/test/parallel/test-crypto-keygen-raw.js b/test/parallel/test-crypto-keygen-raw.js
index e748830757f..9c6b85a1fc2 100644
--- a/test/parallel/test-crypto-keygen-raw.js
+++ b/test/parallel/test-crypto-keygen-raw.js
@@ -17,18 +17,18 @@ const rejectsXCurves = hasFIPS(3, 5);
// Test generateKeyPairSync with raw encoding for EdDSA/ECDH key types.
{
- const types = ['ed25519', 'x25519'];
- if (!isBoringSSL) {
- types.push('ed448', 'x448');
- }
+ const types = ['ed25519', 'x25519', 'ed448', 'x448'];
for (const type of types) {
const options = {
publicKeyEncoding: { format: 'raw-public' },
privateKeyEncoding: { format: 'raw-private' },
};
- if (rejectsXCurves && type.startsWith('x')) {
+ if ((isBoringSSL && type.endsWith('448')) ||
+ (rejectsXCurves && type.startsWith('x'))) {
assert.throws(() => generateKeyPairSync(type, options), {
- code: 'ERR_OSSL_EVP_UNSUPPORTED',
+ name: 'TypeError',
+ code: 'ERR_INVALID_ARG_VALUE',
+ message: `The argument 'type' must be a supported key type. Received '${type}'`,
});
continue;
}
@@ -58,22 +58,23 @@ const rejectsXCurves = hasFIPS(3, 5);
// Test async generateKeyPair with raw encoding for EdDSA/ECDH key types.
{
- const types = ['ed25519', 'x25519'];
- if (!isBoringSSL) {
- types.push('ed448', 'x448');
- }
+ const types = ['ed25519', 'x25519', 'ed448', 'x448'];
for (const type of types) {
const options = {
publicKeyEncoding: { format: 'raw-public' },
privateKeyEncoding: { format: 'raw-private' },
};
+ if ((isBoringSSL && type.endsWith('448')) ||
+ (rejectsXCurves && type.startsWith('x'))) {
+ assert.throws(() => generateKeyPair(type, options, common.mustNotCall()), {
+ name: 'TypeError',
+ code: 'ERR_INVALID_ARG_VALUE',
+ message: `The argument 'type' must be a supported key type. Received '${type}'`,
+ });
+ continue;
+ }
generateKeyPair(type, options,
- common.mustCall((err, publicKey, privateKey) => {
- if (rejectsXCurves && type.startsWith('x')) {
- assert.strictEqual(err?.code, 'ERR_OSSL_EVP_UNSUPPORTED');
- return;
- }
- assert.ifError(err);
+ common.mustSucceed((publicKey, privateKey) => {
assert(Buffer.isBuffer(publicKey));
assert(Buffer.isBuffer(privateKey));
}));
diff --git a/test/parallel/test-crypto-provider-cache.js b/test/parallel/test-crypto-provider-cache.js
index 4767d47dc52..bbfee76b615 100644
--- a/test/parallel/test-crypto-provider-cache.js
+++ b/test/parallel/test-crypto-provider-cache.js
@@ -93,7 +93,26 @@ function checkMacAliases() {
assert.strictEqual(getAliasId(after, 'kmac-128'), id);
}
+function checkNamedKeygen() {
+ for (const name of ['ML-KEM-768', 'Ed25519', 'Ed448', 'X25519', 'X448']) {
+ const type = name.toLowerCase();
+ if (binding.isKeyAlgorithmAvailable(name)) {
+ const { publicKey, privateKey } = crypto.generateKeyPairSync(type);
+ assert.strictEqual(publicKey.asymmetricKeyType, type);
+ assert.strictEqual(privateKey.asymmetricKeyType, type);
+ } else {
+ const error = {
+ name: 'TypeError', code: 'ERR_INVALID_ARG_VALUE',
+ message: `The argument 'type' must be a supported key type. Received '${type}'`,
+ };
+ assert.throws(() => crypto.generateKeyPairSync(type), error);
+ assert.throws(() => crypto.generateKeyPair(type, common.mustNotCall()), error);
+ }
+ }
+}
+
function createFixtures(lists) {
+ checkNamedKeygen();
const fixtures = {};
if (lists.getCiphers.includes(cipherAlgorithm)) {
const info = crypto.getCipherInfo(cipherAlgorithm);
@@ -128,6 +147,7 @@ function createFixtures(lists) {
}
function checkEnabled(fixtures, available) {
+ checkNamedKeygen();
// Availability comes from a fresh environment. Exercise cached handles before
// refreshing the warmed JavaScript lists.
assert(!available.getCiphers.includes(cipherAlgorithm));
@@ -165,6 +185,7 @@ function checkEnabled(fixtures, available) {
}
function checkDisabled(fixtures) {
+ checkNamedKeygen();
if (fixtures.cipher !== undefined) {
assert(crypto.getCipherInfo(cipherAlgorithm));
const cipher = crypto.createCipheriv(cipherAlgorithm, cipherKey, iv);
diff --git a/test/parallel/test-webcrypto-aead-decrypt-detached-buffer.js b/test/parallel/test-webcrypto-aead-decrypt-detached-buffer.js
index 5960c46f8aa..53db81496b2 100644
--- a/test/parallel/test-webcrypto-aead-decrypt-detached-buffer.js
+++ b/test/parallel/test-webcrypto-aead-decrypt-detached-buffer.js
@@ -15,8 +15,13 @@ async function test(
keyLength,
ivLength,
format = 'raw',
- causeCode,
) {
+ if (fips3 && algorithmName === 'AES-OCB') {
+ await assert.rejects(
+ subtle.importKey(format, new Uint8Array(keyLength), algorithmName, false, ['decrypt']),
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' });
+ return;
+ }
const key = await subtle.importKey(
format,
new Uint8Array(keyLength),
@@ -28,13 +33,9 @@ async function test(
const data = new Uint8Array(32);
data.buffer.transfer();
- const expected = causeCode === undefined ?
- { name: 'OperationError' } :
- (err) => err.name === 'OperationError' &&
- err.cause?.code === causeCode;
await assert.rejects(
subtle.decrypt({ name: algorithmName, iv: new Uint8Array(ivLength) }, key, data),
- expected,
+ { name: 'OperationError' },
);
}
@@ -60,8 +61,7 @@ if (hasOpenSSL(3)) {
'AES-OCB',
32,
12,
- 'raw-secret',
- fips3 ? 'ERR_OSSL_EVP_UNSUPPORTED' : undefined));
+ 'raw-secret'));
}
Promise.all(tests).then(common.mustCall());
diff --git a/test/parallel/test-webcrypto-deduplicate-usages.js b/test/parallel/test-webcrypto-deduplicate-usages.js
index 1d7247644ab..4ef6cbd8d9b 100644
--- a/test/parallel/test-webcrypto-deduplicate-usages.js
+++ b/test/parallel/test-webcrypto-deduplicate-usages.js
@@ -71,7 +71,14 @@ function assertSameSet(actual, expected, msg) {
for (const { algorithm, usages, expected } of symmetric) {
tests.push((async () => {
- const key = await subtle.generateKey(algorithm, true, usages);
+ const generated = subtle.generateKey(algorithm, true, usages);
+ if (hasFIPS(3) && algorithm.name === 'AES-OCB') {
+ await assert.rejects(generated, {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ });
+ return;
+ }
+ const key = await generated;
assertSameSet(key.usages, expected,
`generateKey ${algorithm.name}`);
assert.strictEqual(key.usages.length, expected.length,
@@ -228,12 +235,19 @@ function assertSameSet(actual, expected, msg) {
// Argon2 only supports raw-secret import.
if (hasOpenSSL(3, 2)) {
tests.push((async () => {
- const key = await subtle.importKey(
+ const imported = subtle.importKey(
'raw-secret',
new Uint8Array(16),
'Argon2id',
false,
['deriveBits', 'deriveBits']);
+ if (hasFIPS(3)) {
+ await assert.rejects(imported, {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ });
+ return;
+ }
+ const key = await imported;
assertSameSet(key.usages, ['deriveBits'],
'importKey raw-secret Argon2id');
assert.strictEqual(key.usages.length, 1);
@@ -362,12 +376,19 @@ function assertSameSet(actual, expected, msg) {
// AES-OCB raw-secret import.
if (hasOpenSSL(3)) {
tests.push((async () => {
- const key = await subtle.importKey(
+ const imported = subtle.importKey(
'raw-secret',
new Uint8Array(16),
{ name: 'AES-OCB' },
true,
['decrypt', 'encrypt', 'decrypt', 'encrypt']);
+ if (hasFIPS(3)) {
+ await assert.rejects(imported, {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ });
+ return;
+ }
+ const key = await imported;
assertSameSet(key.usages, ['encrypt', 'decrypt']);
assert.strictEqual(key.usages.length, 2);
})());
@@ -465,7 +486,14 @@ function assertSameSet(actual, expected, msg) {
for (const { algorithm, usages, expected } of jwkVectors) {
tests.push((async () => {
- const key = await subtle.generateKey(algorithm, true, usages);
+ const generated = subtle.generateKey(algorithm, true, usages);
+ if (hasFIPS(3) && algorithm.name === 'AES-OCB') {
+ await assert.rejects(generated, {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ });
+ return;
+ }
+ const key = await generated;
const jwk = await subtle.exportKey('jwk', key);
assertSameSet(jwk.key_ops, expected,
`jwk key_ops for ${algorithm.name}`);
diff --git a/test/parallel/test-webcrypto-derivebits-cfrg.js b/test/parallel/test-webcrypto-derivebits-cfrg.js
index 134360c6275..faacfb6922a 100644
--- a/test/parallel/test-webcrypto-derivebits-cfrg.js
+++ b/test/parallel/test-webcrypto-derivebits-cfrg.js
@@ -45,10 +45,7 @@ async function prepareKeys() {
const keys = {};
await Promise.all(
kTests.map(async ({ name, size, pkcs8, spki, result }) => {
- const [
- privateKey,
- publicKey,
- ] = await Promise.all([
+ const imported = [
subtle.importKey(
'pkcs8',
Buffer.from(pkcs8, 'hex'),
@@ -61,7 +58,14 @@ async function prepareKeys() {
{ name },
true,
[]),
- ]);
+ ];
+ if (rejectsXCurves) {
+ await Promise.all(imported.map((promise) => assert.rejects(promise, {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ })));
+ return;
+ }
+ const [privateKey, publicKey] = await Promise.all(imported);
keys[name] = {
privateKey,
publicKey,
@@ -74,20 +78,12 @@ async function prepareKeys() {
(async function() {
const keys = await prepareKeys();
+ if (rejectsXCurves) return;
await Promise.all(
Object.keys(keys).map(async (name) => {
const { size, result, privateKey, publicKey } = keys[name];
- if (rejectsXCurves) {
- await assert.rejects(
- subtle.deriveBits({ name, public: publicKey }, privateKey, 8 * size),
- (err) => err.name === 'OperationError' &&
- err.cause?.code ===
- 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE');
- return;
- }
-
{
// Good parameters
const bits = await subtle.deriveBits({
diff --git a/test/parallel/test-webcrypto-derivebits-hkdf.js b/test/parallel/test-webcrypto-derivebits-hkdf.js
index fafd6a7afa5..4fb372fd3a8 100644
--- a/test/parallel/test-webcrypto-derivebits-hkdf.js
+++ b/test/parallel/test-webcrypto-derivebits-hkdf.js
@@ -6,7 +6,7 @@ if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
-const { hasOpenSSL, isBoringSSL } = require('../common/crypto');
+const { hasOpenSSL, hasFIPS, isBoringSSL } = require('../common/crypto');
const { subtle } = globalThis.crypto;
function getDeriveKeyInfo(name, length, hash, ...usages) {
@@ -606,6 +606,12 @@ async function testWrongKeyType(
kDerivedKeyTypes.forEach((keyType) => {
const keyArgs = getDeriveKeyInfo(...keyType);
+ if (hasFIPS() && keyType[0] === 'AES-OCB') {
+ variations.push(assert.rejects(testDeriveKey(...args, ...keyArgs), {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ }));
+ return;
+ }
variations.push(testDeriveKey(...args, ...keyArgs));
variations.push(testDeriveKeyBadHash(...args, ...keyArgs));
variations.push(testDeriveKeyBadUsage(
diff --git a/test/parallel/test-webcrypto-derivebits.js b/test/parallel/test-webcrypto-derivebits.js
index e7deee0eeae..99de1e6ba6d 100644
--- a/test/parallel/test-webcrypto-derivebits.js
+++ b/test/parallel/test-webcrypto-derivebits.js
@@ -168,8 +168,7 @@ const rejectsXCurves = hasFIPS(3, 5);
for (const name of ['X25519', 'X448']) {
assert.rejects(
test(name),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED')
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' })
.then(common.mustCall());
}
} else {
diff --git a/test/parallel/test-webcrypto-derivekey-cfrg.js b/test/parallel/test-webcrypto-derivekey-cfrg.js
index c9a9b60d091..130e2ad4cc8 100644
--- a/test/parallel/test-webcrypto-derivekey-cfrg.js
+++ b/test/parallel/test-webcrypto-derivekey-cfrg.js
@@ -44,10 +44,7 @@ async function prepareKeys() {
const keys = {};
await Promise.all(
kTests.map(async ({ name, size, pkcs8, spki, result }) => {
- const [
- privateKey,
- publicKey,
- ] = await Promise.all([
+ const imported = [
subtle.importKey(
'pkcs8',
Buffer.from(pkcs8, 'hex'),
@@ -60,7 +57,14 @@ async function prepareKeys() {
{ name },
true,
[]),
- ]);
+ ];
+ if (rejectsXCurves) {
+ await Promise.all(imported.map((promise) => assert.rejects(promise, {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ })));
+ return;
+ }
+ const [privateKey, publicKey] = await Promise.all(imported);
keys[name] = {
privateKey,
publicKey,
@@ -73,6 +77,7 @@ async function prepareKeys() {
(async function() {
const keys = await prepareKeys();
+ if (rejectsXCurves) return;
const otherArgs = [
{ name: 'HMAC', hash: 'SHA-256', length: 256 },
true,
@@ -82,15 +87,6 @@ async function prepareKeys() {
Object.keys(keys).map(async (name) => {
const { result, privateKey, publicKey } = keys[name];
- if (rejectsXCurves) {
- await assert.rejects(
- subtle.deriveKey({ name, public: publicKey }, privateKey, ...otherArgs),
- (err) => err.name === 'OperationError' &&
- err.cause?.code ===
- 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE');
- return;
- }
-
{
// Good parameters
const key = await subtle.deriveKey({
diff --git a/test/parallel/test-webcrypto-derivekey.js b/test/parallel/test-webcrypto-derivekey.js
index 516838aaf0b..e1b663fa30f 100644
--- a/test/parallel/test-webcrypto-derivekey.js
+++ b/test/parallel/test-webcrypto-derivekey.js
@@ -366,8 +366,7 @@ if (hasOpenSSL(3) && !hasFIPS()) {
for (const name of ['X25519', 'X448']) {
assert.rejects(
test(name),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED')
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' })
.then(common.mustCall());
}
} else {
diff --git a/test/parallel/test-webcrypto-encrypt-decrypt-aes.js b/test/parallel/test-webcrypto-encrypt-decrypt-aes.js
index 00c294839bf..bdf1a1d6666 100644
--- a/test/parallel/test-webcrypto-encrypt-decrypt-aes.js
+++ b/test/parallel/test-webcrypto-encrypt-decrypt-aes.js
@@ -241,8 +241,7 @@ if (hasOpenSSL(3)) {
if (getFips() === 1) {
await assert.rejects(
testEncrypt(passing[0]),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED');
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' });
return;
}
diff --git a/test/parallel/test-webcrypto-encrypt-decrypt.js b/test/parallel/test-webcrypto-encrypt-decrypt.js
index 9a10b214d7a..0f3341db64b 100644
--- a/test/parallel/test-webcrypto-encrypt-decrypt.js
+++ b/test/parallel/test-webcrypto-encrypt-decrypt.js
@@ -211,8 +211,7 @@ if (hasOpenSSL(3)) {
if (getFips() === 1) {
assert.rejects(
test(),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED')
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' })
.then(common.mustCall());
} else {
test().then(common.mustCall());
diff --git a/test/parallel/test-webcrypto-export-import-cfrg.js b/test/parallel/test-webcrypto-export-import-cfrg.js
index 1cc1cd478ac..4747a477804 100644
--- a/test/parallel/test-webcrypto-export-import-cfrg.js
+++ b/test/parallel/test-webcrypto-export-import-cfrg.js
@@ -413,18 +413,18 @@ async function testImportRaw({ name, publicUsages }) {
const tests = [];
for (const vector of testVectors) {
for (const extractable of [true, false]) {
- tests.push(testImportSpki(vector, extractable));
- tests.push(testImportPkcs8(vector, extractable));
- if (rejectsXCurves && vector.name.startsWith('X')) {
- tests.push(assert.rejects(
- testImportJwk(vector, extractable),
- { name: 'DataError' }));
- } else {
- tests.push(testImportJwk(vector, extractable));
+ for (const test of [testImportSpki, testImportPkcs8, testImportJwk]) {
+ const imported = test(vector, extractable);
+ tests.push(rejectsXCurves && vector.name.startsWith('X') ?
+ assert.rejects(imported, {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ }) : imported);
}
}
if (rejectsXCurves && vector.name.startsWith('X')) {
- tests.push(assert.rejects(testImportRaw(vector), { name: 'DataError' }));
+ tests.push(assert.rejects(testImportRaw(vector), {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ }));
} else {
tests.push(testImportRaw(vector));
}
@@ -453,7 +453,9 @@ async function testImportRaw({ name, publicUsages }) {
{ name },
true,
[invalidUsage]),
- { name: 'SyntaxError', message: /Unsupported key usage/ });
+ rejectsXCurves && isKeyAgreement ?
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' } :
+ { name: 'SyntaxError', message: /Unsupported key usage/ });
const validUsage = privateUsages[0];
await assert.rejects(
@@ -463,7 +465,9 @@ async function testImportRaw({ name, publicUsages }) {
{ name },
true,
[validUsage]),
- { name: 'DataError', message: 'Duplicate key operation' });
+ rejectsXCurves && isKeyAgreement ?
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' } :
+ { name: 'DataError', message: 'Duplicate key operation' });
}
})().then(common.mustCall());
@@ -477,15 +481,18 @@ async function testImportRaw({ name, publicUsages }) {
['Ed25519', ['verify'], ['sign']],
['X25519', [], ['deriveBits']],
]) {
+ const error = rejectsXCurves && name.startsWith('X') ?
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' } :
+ { message: /Invalid key type/ };
assert.rejects(subtle.importKey(
'spki',
rsaPublic.export({ format: 'der', type: 'spki' }),
{ name },
- true, publicUsages), { message: /Invalid key type/ }).then(common.mustCall());
+ true, publicUsages), error).then(common.mustCall());
assert.rejects(subtle.importKey(
'pkcs8',
rsaPrivate.export({ format: 'der', type: 'pkcs8' }),
{ name },
- true, privateUsages), { message: /Invalid key type/ }).then(common.mustCall());
+ true, privateUsages), error).then(common.mustCall());
}
}
diff --git a/test/parallel/test-webcrypto-keygen.js b/test/parallel/test-webcrypto-keygen.js
index e63ea8dfc7d..b1fc8f41b2a 100644
--- a/test/parallel/test-webcrypto-keygen.js
+++ b/test/parallel/test-webcrypto-keygen.js
@@ -251,18 +251,17 @@ if (hasOpenSSL(3, 5) || isBoringSSL) {
// Test bad usages
{
async function test(name) {
- if (fips3 && name === 'ChaCha20-Poly1305') {
+ if (fips3 && (name === 'ChaCha20-Poly1305' || name === 'AES-OCB')) {
await assert.rejects(
subtle.generateKey({ name }, true, []),
- { name: 'NotSupportedError' });
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' });
return;
}
if (fips35 && (name === 'X25519' || name === 'X448')) {
await assert.rejects(
subtle.generateKey({ name }, true, ['deriveBits']),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED');
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' });
return;
}
@@ -757,8 +756,7 @@ assert.throws(() => new CryptoKey(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' });
async function testFipsUnsupported(name) {
await assert.rejects(
subtle.generateKey({ name }, true, ['deriveKey', 'deriveBits']),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED');
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' });
}
async function test(
diff --git a/test/parallel/test-webcrypto-raw-format-aliases.js b/test/parallel/test-webcrypto-raw-format-aliases.js
index c9a0e8a4cf4..49a09713f49 100644
--- a/test/parallel/test-webcrypto-raw-format-aliases.js
+++ b/test/parallel/test-webcrypto-raw-format-aliases.js
@@ -72,8 +72,7 @@ const tests = [
if (rejectsXCurves) {
tests.push(assert.rejects(
subtle.generateKey('X25519', true, ['deriveBits']),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED'));
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' }));
} else {
tests.push(assertPublicKeyDoesNotAcceptRawSecret(
'X25519',
diff --git a/test/parallel/test-webcrypto-supports-fips.js b/test/parallel/test-webcrypto-supports-fips.js
new file mode 100644
index 00000000000..ae5d1d4b32d
--- /dev/null
+++ b/test/parallel/test-webcrypto-supports-fips.js
@@ -0,0 +1,135 @@
+// Flags: --expose-internals --no-warnings
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto) common.skip('missing crypto');
+const { hasOpenSSL, isBoringSSL } = require('../common/crypto');
+if (!hasOpenSSL(3) || isBoringSSL) common.skip('requires OpenSSL 3');
+
+const assert = require('node:assert');
+const crypto = require('node:crypto');
+const { once } = require('node:events');
+const { isMainThread, parentPort, Worker, workerData } = require('node:worker_threads');
+const { normalizeAlgorithm } = require('internal/crypto/util');
+const { subtle } = globalThis.crypto;
+
+if (!isMainThread && !workerData?.supportsFips)
+ common.skip('crypto.setFips() is not supported in workers');
+
+async function check() {
+ const fips = crypto.getFips() === 1;
+ const turbo = { name: 'TurboSHAKE128', outputLength: 128 };
+ if (fips) {
+ const error = { name: 'NotSupportedError', message: 'Unrecognized algorithm name' };
+ assert.throws(() => normalizeAlgorithm(turbo, 'digest'), error);
+ await assert.rejects(subtle.digest(turbo, new Uint8Array()), error);
+ } else {
+ assert.strictEqual(normalizeAlgorithm(turbo, 'digest').name, turbo.name);
+ assert.strictEqual((await subtle.digest(turbo, new Uint8Array())).byteLength, 16);
+ }
+ assert.strictEqual(SubtleCrypto.supports('digest', turbo), !fips);
+ for (const [algorithm, usages] of [
+ [{ name: 'AES-OCB', length: 128 }, ['encrypt']],
+ [{ name: 'X25519' }, ['deriveBits']],
+ ]) {
+ const supported = SubtleCrypto.supports('generateKey', algorithm);
+ const generated = subtle.generateKey(algorithm, true, usages);
+ if (supported) {
+ await generated;
+ } else {
+ await assert.rejects(generated, {
+ name: 'NotSupportedError', message: 'Unrecognized algorithm name',
+ });
+ }
+ }
+ for (const name of ['ECDH', 'ECDSA']) {
+ for (const namedCurve of ['P-256', 'P-384', 'P-521']) {
+ const algorithm = { name, namedCurve };
+ for (const operation of ['generateKey', 'importKey']) {
+ assert.strictEqual(SubtleCrypto.supports(operation, algorithm), true);
+ }
+ }
+ }
+ const rsa = {
+ name: 'RSA-PSS', modulusLength: 1024,
+ publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256',
+ };
+ assert.strictEqual(SubtleCrypto.supports('generateKey', rsa), !fips);
+ if (fips) {
+ assert.throws(() => normalizeAlgorithm(rsa, 'generateKey'), {
+ name: 'OperationError', message: 'algorithm.modulusLength must be at least 2048',
+ });
+ } else {
+ assert.strictEqual(normalizeAlgorithm(rsa, 'generateKey').modulusLength, 1024);
+ }
+ const hashes = crypto.getHashes();
+ const salt = new Uint8Array(16);
+ const hashError = { name: 'NotSupportedError', message: 'Unrecognized algorithm name' };
+ for (const [name, alias] of [
+ ['SHA-1', 'sha1'], ['SHA-256', 'sha256'], ['SHA-384', 'sha384'], ['SHA-512', 'sha512'],
+ ['SHA3-256', 'sha3-256'], ['SHA3-384', 'sha3-384'], ['SHA3-512', 'sha3-512'],
+ ]) {
+ const available = hashes.includes(alias);
+ for (const hash of [name, { name }]) {
+ const hmac = { name: 'HMAC', hash, length: 256 };
+ const hkdf = { name: 'HKDF', hash, salt, info: new Uint8Array() };
+ for (const [operations, algorithm] of [
+ [['digest'], hash],
+ [['generateKey', 'importKey'], { ...rsa, modulusLength: 2048, hash }],
+ [['generateKey', 'importKey'], hmac],
+ [['sign', 'verify'], { name: 'ECDSA', hash }],
+ [['deriveBits'], hkdf],
+ [['deriveBits'], { name: 'PBKDF2', hash, salt, iterations: 1 }],
+ ]) {
+ for (const operation of operations) {
+ const length = operation === 'deriveBits' ? 128 : undefined;
+ assert.strictEqual(SubtleCrypto.supports(operation, algorithm, length), available);
+ if (!available)
+ assert.throws(() => normalizeAlgorithm(algorithm, operation), hashError);
+ }
+ }
+ assert.strictEqual(SubtleCrypto.supports('deriveKey', hkdf, hmac), available);
+ if (!available)
+ assert.throws(() => normalizeAlgorithm(hmac, 'get key length'), hashError);
+ }
+ }
+}
+
+if (!isMainThread) {
+ parentPort.on('message', async () => {
+ await check();
+ parentPort.postMessage(crypto.getFips());
+ });
+ parentPort.postMessage('ready');
+} else {
+ (async () => {
+ const originalFips = crypto.getFips();
+ await check();
+ try {
+ crypto.setFips(0);
+ } catch (err) {
+ if (err.code !== 'ERR_CRYPTO_FIPS_FORCED') throw err;
+ common.printSkipMessage('FIPS mode cannot be disabled');
+ return;
+ }
+ let worker;
+ try {
+ // Start the worker before enabling FIPS: isolate initialization can need
+ // OpenSSL entropy, which is unavailable without an active FIPS provider.
+ worker = new Worker(__filename, { workerData: { supportsFips: true } });
+ worker.on('error', common.mustNotCall());
+ await once(worker, 'message');
+ for (const fips of [0, 1, 0]) {
+ crypto.setFips(fips);
+ await check();
+ const response = once(worker, 'message');
+ worker.postMessage('check');
+ const [actual] = await response;
+ assert.strictEqual(actual, fips);
+ }
+ } finally {
+ if (worker !== undefined) await worker.terminate();
+ crypto.setFips(originalFips);
+ }
+ })().then(common.mustCall());
+}
diff --git a/test/parallel/test-webcrypto-supports.mjs b/test/parallel/test-webcrypto-supports.mjs
index 15874102a22..7b98cf13c0d 100644
--- a/test/parallel/test-webcrypto-supports.mjs
+++ b/test/parallel/test-webcrypto-supports.mjs
@@ -4,10 +4,6 @@ if (!common.hasCrypto)
common.skip('missing crypto');
import * as assert from 'node:assert';
-import { hasFIPS } from '../common/crypto.js';
-
-if (hasFIPS(3))
- common.skip('SubtleCrypto.supports() does not reflect FIPS provider availability');
const { SubtleCrypto } = globalThis;
diff --git a/test/parallel/test-webcrypto-wrap-unwrap.js b/test/parallel/test-webcrypto-wrap-unwrap.js
index 60ceffd710c..b899e4f712d 100644
--- a/test/parallel/test-webcrypto-wrap-unwrap.js
+++ b/test/parallel/test-webcrypto-wrap-unwrap.js
@@ -390,13 +390,13 @@ function testWrapping(name, keys) {
for (const name of ['X25519', 'X448']) {
await assert.rejects(
subtle.generateKey({ name }, true, ['deriveBits']),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED');
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' });
}
}
- const wrappingKey = await subtle.generateKey(
- { name: 'AES-OCB', length: 128 }, true, ['wrapKey']);
+ await assert.rejects(
+ subtle.generateKey({ name: 'AES-OCB', length: 128 }, true, ['wrapKey']),
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' });
const key = await subtle.generateKey(
{ name: 'HMAC', hash: 'SHA-256', length: 256 },
true,
@@ -405,10 +405,9 @@ function testWrapping(name, keys) {
subtle.wrapKey(
'raw',
key,
- wrappingKey,
+ key,
{ name: 'AES-OCB', iv: new Uint8Array(15), tagLength: 128 }),
- (err) => err.name === 'OperationError' &&
- err.cause?.code === 'ERR_OSSL_EVP_UNSUPPORTED');
+ { name: 'NotSupportedError', message: 'Unrecognized algorithm name' });
}
await generateWrappingKeys();
diff --git a/test/wpt/status/WebCryptoAPI.cjs b/test/wpt/status/WebCryptoAPI.cjs
index eb85fa94e63..ff618016f3d 100644
--- a/test/wpt/status/WebCryptoAPI.cjs
+++ b/test/wpt/status/WebCryptoAPI.cjs
@@ -113,9 +113,13 @@ if (hasFIPS(3)) {
skip(
'encrypt_decrypt/aes_ocb.tentative.https.any.js',
'encrypt_decrypt/chacha20_poly1305.tentative.https.any.js',
+ 'generateKey/failures_AES-OCB.tentative.https.any.js',
'generateKey/failures_chacha20_poly1305.tentative.https.any.js',
+ 'generateKey/successes_AES-OCB.tentative.https.any.js',
'generateKey/successes_chacha20_poly1305.tentative.https.any.js',
+ 'import_export/AES-OCB_importKey.tentative.https.any.js',
'import_export/ChaCha20-Poly1305_importKey.tentative.https.any.js',
+ 'serialization/aes-ocb.tentative.https.any.js',
'serialization/chacha20-poly1305.tentative.https.any.js');
skipSubtests(
@@ -153,6 +157,8 @@ if (hasFIPS(3, 5)) {
'derive_bits_keys/cfrg_curves_keys_curve25519.https.any.js',
'derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.js',
'generateKey/failures_Hybrid-KEM.tentative.https.any.js',
+ 'generateKey/failures_X25519.https.any.js',
+ 'generateKey/failures_X448.tentative.https.any.js',
'generateKey/successes_Hybrid-KEM.tentative.https.any.js',
'generateKey/successes_X25519.https.any.js',
'generateKey/successes_X448.tentative.https.any.js',
diff --git a/typings/internalBinding/crypto.d.ts b/typings/internalBinding/crypto.d.ts
index b31bd413e0b..306e5738302 100644
--- a/typings/internalBinding/crypto.d.ts
+++ b/typings/internalBinding/crypto.d.ts
@@ -831,7 +831,6 @@ export interface CryptoBinding {
KEMEncapsulateJob?: InternalCryptoBinding.KEMEncapsulateJobConstructor;
KangarooTwelveJob: InternalCryptoBinding.KangarooTwelveJobConstructor;
KmacJob: InternalCryptoBinding.KmacJobConstructor;
- getPqcKeyTypes(): string[];
NamedKeyPairGenJob: InternalCryptoBinding.NamedKeyPairGenJobConstructor;
PBKDF2Job: InternalCryptoBinding.PBKDF2JobConstructor;
RandomBytesJob: InternalCryptoBinding.RandomBytesJobConstructor;
@@ -987,6 +986,7 @@ export interface CryptoBinding {
getHashes(): string[];
getMacs(): string[];
isCryptoKey(key: unknown): boolean;
+ isKeyAlgorithmAvailable(name: string): boolean;
isKeyObject(key: unknown): boolean;
isX509Certificate(value: unknown): boolean;
getKeyObjectSlots(key: object): InternalCryptoBinding.KeyObjectSlots;