Commit 7e582954396 for nodejs
commit 7e582954396511798bba959914ce3f64c046820a
Author: Filip Skokan <panva.ip@gmail.com>
Date: Thu Oct 22 11:15:44 2026 +0200
crypto: wire up SHA-3 with BoringSSL
Add SHA3-256, SHA3-384, and SHA3-512 to name lookup and getHashes().
BoringSSL exposes EVP_sha3_* descriptors but omits them from its
digest registry. Map the names directly to those descriptors so
node:crypto can use them too.
When using BoringSSL, limit WebCrypto SHA-3 support to digest
operations.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66922
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
index 81e6634a76e..025f47cd920 100644
--- a/deps/ncrypto/ncrypto.cc
+++ b/deps/ncrypto/ncrypto.cc
@@ -2804,6 +2804,13 @@ const EVP_MD* getDigestByName(const char* name) {
if (strcmp(name, "dss1") == 0 || strcmp(name, "DSS1") == 0) [[unlikely]] {
return EVP_sha1();
}
+#if NCRYPTO_USE_BORINGSSL
+ // BoringSSL exposes SHA-3 through EVP_MD descriptors without registering
+ // their names or assigning them NIDs.
+ if (CaseInsensitiveNameEqual()(name, "sha3-256")) return EVP_sha3_256();
+ if (CaseInsensitiveNameEqual()(name, "sha3-384")) return EVP_sha3_384();
+ if (CaseInsensitiveNameEqual()(name, "sha3-512")) return EVP_sha3_512();
+#endif
return EVP_get_digestbyname(name);
}
diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js
index 7602e375bc3..1a49c6f4083 100644
--- a/lib/internal/crypto/util.js
+++ b/lib/internal/crypto/util.js
@@ -53,6 +53,8 @@ const {
isKeyAlgorithmAvailable,
} = internalBinding('crypto');
+const { openssl_is_boringssl: isBoringSSL } = process.features;
+
let fips;
let fipsGeneration;
function isFips() {
@@ -533,7 +535,7 @@ function getConditionalAlgorithms() {
'Argon2d': !!Argon2Job && !fips,
'Argon2i': !!Argon2Job && !fips,
'Argon2id': !!Argon2Job && !fips,
- 'ChaCha20-Poly1305': process.features.openssl_is_boringssl ||
+ 'ChaCha20-Poly1305': isBoringSSL ||
has(ciphers, 'chacha20-poly1305'),
'cSHAKE128': has(hashes, 'shake128'),
'cSHAKE256': has(hashes, 'shake256'),
@@ -550,15 +552,15 @@ function getConditionalAlgorithms() {
'ML-KEM-768': mlKem768,
'ML-KEM-1024': mlKem1024,
'MLKEM768-P256': !fips && mlKem768 &&
- (!process.features.openssl_is_boringssl ||
+ (!isBoringSSL ||
(has(hashes, 'sha3-256') &&
has(hashes, 'shake256'))),
'MLKEM768-X25519': !fips && mlKem768 &&
- (!process.features.openssl_is_boringssl ||
+ (!isBoringSSL ||
(has(hashes, 'sha3-256') &&
has(hashes, 'shake256'))),
'MLKEM1024-P384': !fips && mlKem1024 &&
- (!process.features.openssl_is_boringssl ||
+ (!isBoringSSL ||
(has(hashes, 'sha3-256') &&
has(hashes, 'shake256'))),
'SHA-1': has(hashes, 'sha1'),
@@ -847,6 +849,11 @@ function normalizeAlgorithm(algorithm, op) {
throw lazyDOMException(
`Only SHA hashes are supported in ${desiredType}`, 'NotSupportedError');
}
+ if (isBoringSSL && StringPrototypeStartsWith(hash.name, 'SHA3-')) {
+ throw lazyDOMException(
+ 'SHA-3 is only supported for digest operations',
+ 'NotSupportedError');
+ }
normalizedAlgorithm[member] = hash;
} else if (idlType === 'AlgorithmIdentifier') {
// This extension point is not used by any supported algorithm (yet?)
diff --git a/lib/internal/crypto/webidl.js b/lib/internal/crypto/webidl.js
index 33705dbb9c3..63d086a7698 100644
--- a/lib/internal/crypto/webidl.js
+++ b/lib/internal/crypto/webidl.js
@@ -14,6 +14,8 @@ const {
TypedArrayPrototypeIncludes,
} = primordials;
+const { openssl_is_boringssl: isBoringSSL } = process.features;
+
const {
lazyDOMException,
} = require('internal/util');
@@ -670,7 +672,7 @@ converters.ContextParams = createAlgorithmDictionaryConverter(
const validateLength = (V) =>
validateMaxBufferLength(V, 'ContextParams.context', 255);
- if (process.features.openssl_is_boringssl) {
+ if (isBoringSSL) {
this.validator = validateLength;
} else {
let { 0: major, 1: minor } =
diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc
index c58c3ac6477..fabf6654430 100644
--- a/src/crypto/crypto_hash.cc
+++ b/src/crypto/crypto_hash.cc
@@ -255,6 +255,12 @@ const std::vector<std::string>& GetSupportedHashAlgorithms(Environment* env) {
EVP_MD_do_all_provided(nullptr, SaveSupportedProviderHashAlgorithms, env);
#else
EVP_MD_do_all_sorted(SaveSupportedHashAlgorithms, env);
+#endif
+#if NCRYPTO_USE_BORINGSSL
+ // SHA-3 descriptors are not included in BoringSSL's name registry.
+ for (const char* name : {"sha3-256", "sha3-384", "sha3-512"}) {
+ env->supported_hash_algorithms.emplace_back(name);
+ }
#endif
}
const uint64_t current_generation = ncrypto::getFipsStateGeneration();
diff --git a/test/fixtures/crypto/ecdsa.js b/test/fixtures/crypto/ecdsa.js
index 6da56a0601f..677e553d6e0 100644
--- a/test/fixtures/crypto/ecdsa.js
+++ b/test/fixtures/crypto/ecdsa.js
@@ -2,7 +2,8 @@
const { isBoringSSL } = require('../../common/crypto');
-module.exports = function() {
+module.exports = function({ includeUnsupported = false } = {}) {
+ const includeSha3 = !isBoringSSL || includeUnsupported;
const pkcs8 = {
'P-256': Buffer.from(
'308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b020101' +
@@ -74,7 +75,7 @@ module.exports = function() {
'b6a0a14d7e4bc6dd2eda82c9234f174b670b60c8f7d101f68fdf5889e02373b025' +
'dcbc4c82f2929b8e06c68535da98e38fe399c53a814b097935581ef21535eb',
'hex'),
- ...(!isBoringSSL ? {
+ ...(includeSha3 ? {
'SHA3-256': Buffer.from(
'f6a48eb5557f484ed0c3e4b5c78a3cf497cbd346db06a4165d429248aa2cc51a69' +
'747d09f57af145469a8b607a9b8b9709629d74e8f5ca337c6ddc581b6f6103',
@@ -106,7 +107,7 @@ module.exports = function() {
'72fbdb369fd34c1c54264d07f4facd69b02e4206f8a8bb259b882a305c56fde2d3' +
'5107e493c53cd6b4af0b31306f4d03fd43cfc762a1030e17a3d775453a1212b142' +
'9f7b3d93066a5f42a10b138cd177dc09616e827d598822d78d4627b754e6', 'hex'),
- ...(!isBoringSSL ? {
+ ...(includeSha3 ? {
'SHA3-256': Buffer.from(
'0b07c078be30fa5925a307d6fc559c5f398e63fb5d007d6b24a834847f2d3d18d5' +
'b5e840711c52a7bc6626c3ced93301e873c013a706f6b297c12cc6d47a71e0529e' +
@@ -146,7 +147,7 @@ module.exports = function() {
'01f0071e6a32867fa70f695cd39c4e87e142b9e4134d38740bd6fee354a575167e' +
'13524e94832637910fe11e53a85fb21b91adb81bb1779c4e2b8bc87c717dc35084',
'hex'),
- ...(!isBoringSSL ? {
+ ...(includeSha3 ? {
'SHA3-256': Buffer.from(
'00463679f47a4c705e03447360dcf34d1743e0d4b2591cc66832a6bc80d92e538c' +
'169a1fd330f98e7235ca7fec7e16ac44fb13095b8edf2c76b75c4845177d59e425' +
@@ -172,7 +173,7 @@ module.exports = function() {
const curves = ['P-256', 'P-384', 'P-521'];
const hashes = ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512'];
- if (!isBoringSSL) {
+ if (includeSha3) {
hashes.push('SHA3-256', 'SHA3-384', 'SHA3-512');
}
diff --git a/test/fixtures/crypto/hmac.js b/test/fixtures/crypto/hmac.js
index 55376a1299d..6505c6e2ae5 100644
--- a/test/fixtures/crypto/hmac.js
+++ b/test/fixtures/crypto/hmac.js
@@ -1,7 +1,5 @@
'use strict';
-const { isBoringSSL } = require('../../common/crypto');
-
module.exports = function () {
const plaintext = Buffer.from(
'5f4dba4f320c0ce876725afce5fbd25bf83e5a7125a08cafe73c3ebac421779df9d55d' +
@@ -24,18 +22,16 @@ module.exports = function () {
'5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' +
'0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9',
'hex'),
- ...(!isBoringSSL ? {
- 'SHA3-256': Buffer.from(
- 'e588ec0811463d767241df1074b47ae4071b51f2ce36537ba69ccdc3fdc2b7a8',
- 'hex'),
- 'SHA3-384': Buffer.from(
- '6b1da28eab1f582ad9718effe05e23d5fd2c9877a2d9443f90bec093bece2ea7' +
- 'd2354cd0bdc5e147d2e9009373494488', 'hex'),
- 'SHA3-512': Buffer.from(
- '5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' +
- '0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9',
- 'hex')
- } : {})
+ 'SHA3-256': Buffer.from(
+ 'e588ec0811463d767241df1074b47ae4071b51f2ce36537ba69ccdc3fdc2b7a8',
+ 'hex'),
+ 'SHA3-384': Buffer.from(
+ '6b1da28eab1f582ad9718effe05e23d5fd2c9877a2d9443f90bec093bece2ea7' +
+ 'd2354cd0bdc5e147d2e9009373494488', 'hex'),
+ 'SHA3-512': Buffer.from(
+ '5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' +
+ '0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9',
+ 'hex')
}
const signatures = {
@@ -50,18 +46,16 @@ module.exports = function () {
'61fb278c3ffb0cce2bf1cf723ddfd8ef1f931c0c618c25907324605939e3f9a2' +
'c6f4af690bda3407dc2f5770f6a0a44b954d64a332e3ee0821abf82b7f3e99c1',
'hex'),
- ...(!isBoringSSL ? {
- 'SHA3-256': Buffer.from(
- 'c1ac5e11fcd50c48bf567f6e296632f5801c4eb07a8a47579b41dee971a3099b',
- 'hex'),
- 'SHA3-384': Buffer.from(
- 'ac8c97f6dd8d9e16101063077c16b23fe291a5e6d149653e9ac7002365159317' +
- 'adcfad511996578b0053a5c14b75f16c', 'hex'),
- 'SHA3-512': Buffer.from(
- '2162c2a8907e6b2f68599a69e81a464d8f076b5eeb555d98b4d20330034df3c7' +
- 'cf35b1fa958a074ca12f0d242df39f0da3d4f1dbfb3629057798fe1f883974ee',
- 'hex')
- } : {})
+ 'SHA3-256': Buffer.from(
+ 'c1ac5e11fcd50c48bf567f6e296632f5801c4eb07a8a47579b41dee971a3099b',
+ 'hex'),
+ 'SHA3-384': Buffer.from(
+ 'ac8c97f6dd8d9e16101063077c16b23fe291a5e6d149653e9ac7002365159317' +
+ 'adcfad511996578b0053a5c14b75f16c', 'hex'),
+ 'SHA3-512': Buffer.from(
+ '2162c2a8907e6b2f68599a69e81a464d8f076b5eeb555d98b4d20330034df3c7' +
+ 'cf35b1fa958a074ca12f0d242df39f0da3d4f1dbfb3629057798fe1f883974ee',
+ 'hex')
}
const vectors = [];
diff --git a/test/fixtures/crypto/rsa_pkcs.js b/test/fixtures/crypto/rsa_pkcs.js
index 74a8c0512ef..598097362e7 100644
--- a/test/fixtures/crypto/rsa_pkcs.js
+++ b/test/fixtures/crypto/rsa_pkcs.js
@@ -2,7 +2,8 @@
const { isBoringSSL } = require('../../common/crypto');
-module.exports = function () {
+module.exports = function ({ includeUnsupported = false } = {}) {
+ const includeSha3 = !isBoringSSL || includeUnsupported;
const pkcs8 = Buffer.from(
'308204bf020100300d06092a864886f70d0101010500048204a9308204a50201000282' +
'010100d3576092e62957364544e7e4233b7bdb293db2085122c479328546f9f0f712f6' +
@@ -99,7 +100,7 @@ module.exports = function () {
'7a6335c70e193235dcda48add6858626bd96311e60f7e5ea4491b6c1e6248afe12b' +
'bbd54f8869b043a5b0444562813f0a98b300356f306e6b783a29f3bec97ca40ea20' +
'062cab8926ec5d96aa387cc84821a6d72b8ea126e7d', 'hex'),
- ...(!isBoringSSL ? {
+ ...(includeSha3 ? {
'sha3-256': Buffer.from(
'be1b476c1911a01d71710fd8a2f3158d6f7839e91443b01bed30dfdd04336d80c6b' +
'f692c06fad254877901c10a73853e8fb202a29cddefdf16c3adcda1fc123625897d' +
@@ -163,7 +164,7 @@ module.exports = function () {
plaintext,
signature: signatures['sha-512']
},
- ...(!isBoringSSL ? [
+ ...(includeSha3 ? [
{
publicKeyBuffer: spki,
privateKeyBuffer: pkcs8,
diff --git a/test/fixtures/crypto/rsa_pss.js b/test/fixtures/crypto/rsa_pss.js
index 4aeaf3330e7..101122b2ffe 100644
--- a/test/fixtures/crypto/rsa_pss.js
+++ b/test/fixtures/crypto/rsa_pss.js
@@ -1,7 +1,5 @@
'use strict';
-const { isBoringSSL } = require('../../common/crypto');
-
module.exports = function() {
const pkcs8 = Buffer.from(
'308204bf020100300d06092a864886f70d0101010500048204a9308204a5020100028' +
@@ -152,44 +150,42 @@ module.exports = function() {
'b68c04bfe452c3adc6c10066a915231b7b404727eb6201b4921eb96d9407de2b963' +
'3879ceb71d759d9828d7b4d062f6ef100757d8328187caf57dfb859d1555345207c' +
'1cce7905c3564c08fec78867a53d5a2cf84810e1ffa', 'hex'),
- ...(!isBoringSSL ? {
- 'sha3-512, no salt': Buffer.from(
- 'd2430dc87abeaa7d13f7cec8510f1a296e1c608f44b1696829c59a99e8eefe9b2ee' +
- '6ee8ad6fdc93c24fcba2f04d1da195924b6209717e1992c10ed9f4783478765fe34' +
- '3e761203bff9d326bb6dc2061b0a7554c8ce0814b29249136c20c8e30054df0c6bc' +
- '656509a82845149368896690e32ff5dd32ef01543686f01d6a69bb438b049e66a8b' +
- 'df485a13edcd7dc482da4cc57d0b740aca3e56f0da247794e600afd27b22b6da13b' +
- 'cc15dd2059b525f8cb6bcd07540aa843f0ae51d4b0eea27045485914b908bdd01d0' +
- 'a9d42379f9f7180f4ad162ff73df5fed0200eb02ad01473975d54a77c15a9c61a3c' +
- 'b5e27de5d1eecc363d45506f7123a5ddd115c5e4c9e', 'hex'),
- 'sha3-256, salted': Buffer.from(
- '59cb9cce6ae838eb20d38d6af4acb9b866b0753bb7df9e441037d788512c03279e8' +
- '3d9a9cf5c0921fe1c0b6e8e895a8c0ad24a18b123f809b34ef2a3a1f05974030320' +
- '435692ef5d378cef4368c3658c098a25371dfaf1c0b6910f653a4ec15f2c08956c1' +
- '405136c2aba7f25a808fa7dbf57a4cb2978bd91af710b27ee239d955c8cac7a76ae' +
- '9085cefeda2a585a99cc948f064b5da66a9c4aa4f3f767ac905a9f314b47038e05c' +
- '3608fbb7e67a278e4f009a62c3cd3fdf43692e759d9361be1217999a76a69d4d119' +
- 'f8791a90e207e46b3f6125721f56fd819292d06a3cdae2c62c9a1dc0d964a06036c' +
- '8c18661cc6c873532a3536ab51e1ce210926db299e2', 'hex'),
- 'sha3-384, salted': Buffer.from(
- '8d1f9297c8169f27f0c58827dba991d862de58c1155f612ad2995d2bf862d051c4a' +
- '91b48571849b0412384382e5b77990de6a3c84010046b35c4a504f175a3479483d9' +
- '5c58f86bb96d53a27e59d6f67fddaae295ce90610f5086acc711557c2c85aac32d3' +
- '24199cff2367ae44e1d91307a98c8cbfb085a8bce6b1c20714711bc15b0eddb7881' +
- '823227d4be477ffdad8093663a6a1fc62eb39c49c2c3a821c2b202cf7904b49ca92' +
- '3c83819602bb13931577354a80f99309030044935b1cd41f0513160e661db1959fb' +
- '1ec15f087f3d288e875d54cbf070ec860b0aeecc951ea65e97cd5460750d4b7de52' +
- '22cb9e7466b1f506ecf6a81fc399dfd8334160f9084', 'hex'),
- 'sha3-512, salted': Buffer.from(
- '7b6d7be418c5d37cc8070698b8b03d818ecd8b673d047d34921913f6d59c69cb496' +
- '172d6118207d9ff92b8e1246acf0e03a845d935a70f8a82c3d5d6db6a1a0e337269' +
- '4b904372413dcbaa7ac5486bc8ccaf70d7e9470be82b928a90017e272cf9761ed26' +
- 'c160fe874a2675a4fb2acad72c50fbfffdd70b5a6f2919553d7ea1829934670f8de' +
- 'f2a5c2816404b1aa153323c92c58400622f184b9b0463fa48d6b27091f68c287e3f' +
- '6d9ab9eb451711a5d984c547f3d56f14a686a89ddf36c47ce25092b8c6530904de9' +
- '5df7fc602fe9394315f1b1847aae304cb5ad71e2cb78acfbc997a87a9d62a6898bb' +
- '6d84a81bb89b50186265f4be171a93d837a4bf777c8', 'hex')
- } : {})
+ 'sha3-512, no salt': Buffer.from(
+ 'd2430dc87abeaa7d13f7cec8510f1a296e1c608f44b1696829c59a99e8eefe9b2ee' +
+ '6ee8ad6fdc93c24fcba2f04d1da195924b6209717e1992c10ed9f4783478765fe34' +
+ '3e761203bff9d326bb6dc2061b0a7554c8ce0814b29249136c20c8e30054df0c6bc' +
+ '656509a82845149368896690e32ff5dd32ef01543686f01d6a69bb438b049e66a8b' +
+ 'df485a13edcd7dc482da4cc57d0b740aca3e56f0da247794e600afd27b22b6da13b' +
+ 'cc15dd2059b525f8cb6bcd07540aa843f0ae51d4b0eea27045485914b908bdd01d0' +
+ 'a9d42379f9f7180f4ad162ff73df5fed0200eb02ad01473975d54a77c15a9c61a3c' +
+ 'b5e27de5d1eecc363d45506f7123a5ddd115c5e4c9e', 'hex'),
+ 'sha3-256, salted': Buffer.from(
+ '59cb9cce6ae838eb20d38d6af4acb9b866b0753bb7df9e441037d788512c03279e8' +
+ '3d9a9cf5c0921fe1c0b6e8e895a8c0ad24a18b123f809b34ef2a3a1f05974030320' +
+ '435692ef5d378cef4368c3658c098a25371dfaf1c0b6910f653a4ec15f2c08956c1' +
+ '405136c2aba7f25a808fa7dbf57a4cb2978bd91af710b27ee239d955c8cac7a76ae' +
+ '9085cefeda2a585a99cc948f064b5da66a9c4aa4f3f767ac905a9f314b47038e05c' +
+ '3608fbb7e67a278e4f009a62c3cd3fdf43692e759d9361be1217999a76a69d4d119' +
+ 'f8791a90e207e46b3f6125721f56fd819292d06a3cdae2c62c9a1dc0d964a06036c' +
+ '8c18661cc6c873532a3536ab51e1ce210926db299e2', 'hex'),
+ 'sha3-384, salted': Buffer.from(
+ '8d1f9297c8169f27f0c58827dba991d862de58c1155f612ad2995d2bf862d051c4a' +
+ '91b48571849b0412384382e5b77990de6a3c84010046b35c4a504f175a3479483d9' +
+ '5c58f86bb96d53a27e59d6f67fddaae295ce90610f5086acc711557c2c85aac32d3' +
+ '24199cff2367ae44e1d91307a98c8cbfb085a8bce6b1c20714711bc15b0eddb7881' +
+ '823227d4be477ffdad8093663a6a1fc62eb39c49c2c3a821c2b202cf7904b49ca92' +
+ '3c83819602bb13931577354a80f99309030044935b1cd41f0513160e661db1959fb' +
+ '1ec15f087f3d288e875d54cbf070ec860b0aeecc951ea65e97cd5460750d4b7de52' +
+ '22cb9e7466b1f506ecf6a81fc399dfd8334160f9084', 'hex'),
+ 'sha3-512, salted': Buffer.from(
+ '7b6d7be418c5d37cc8070698b8b03d818ecd8b673d047d34921913f6d59c69cb496' +
+ '172d6118207d9ff92b8e1246acf0e03a845d935a70f8a82c3d5d6db6a1a0e337269' +
+ '4b904372413dcbaa7ac5486bc8ccaf70d7e9470be82b928a90017e272cf9761ed26' +
+ 'c160fe874a2675a4fb2acad72c50fbfffdd70b5a6f2919553d7ea1829934670f8de' +
+ 'f2a5c2816404b1aa153323c92c58400622f184b9b0463fa48d6b27091f68c287e3f' +
+ '6d9ab9eb451711a5d984c547f3d56f14a686a89ddf36c47ce25092b8c6530904de9' +
+ '5df7fc602fe9394315f1b1847aae304cb5ad71e2cb78acfbc997a87a9d62a6898bb' +
+ '6d84a81bb89b50186265f4be171a93d837a4bf777c8', 'hex')
}
const vectors = [
@@ -257,56 +253,54 @@ module.exports = function() {
plaintext,
signature: signatures['sha-512, salted']
},
- ...(!isBoringSSL ? [
- {
- publicKeyBuffer: spki,
- privateKeyBuffer: pkcs8,
- algorithm: { name: 'RSA-PSS', saltLength: 0 },
- hash: 'SHA3-256',
- plaintext,
- signature: signatures['sha3-256, no salt']
- },
- {
- publicKeyBuffer: spki,
- privateKeyBuffer: pkcs8,
- algorithm: { name: 'RSA-PSS', saltLength: 0 },
- hash: 'SHA3-384',
- plaintext,
- signature: signatures['sha3-384, no salt']
- },
- {
- publicKeyBuffer: spki,
- privateKeyBuffer: pkcs8,
- algorithm: { name: 'RSA-PSS', saltLength: 0 },
- hash: 'SHA3-512',
- plaintext,
- signature: signatures['sha3-512, no salt']
- },
- {
- publicKeyBuffer: spki,
- privateKeyBuffer: pkcs8,
- algorithm: { name: 'RSA-PSS', saltLength: 32 },
- hash: 'SHA3-256',
- plaintext,
- signature: signatures['sha3-256, salted']
- },
- {
- publicKeyBuffer: spki,
- privateKeyBuffer: pkcs8,
- algorithm: { name: 'RSA-PSS', saltLength: 48 },
- hash: 'SHA3-384',
- plaintext,
- signature: signatures['sha3-384, salted']
- },
- {
- publicKeyBuffer: spki,
- privateKeyBuffer: pkcs8,
- algorithm: { name: 'RSA-PSS', saltLength: 64 },
- hash: 'SHA3-512',
- plaintext,
- signature: signatures['sha3-512, salted']
- }
- ] : []),
+ {
+ publicKeyBuffer: spki,
+ privateKeyBuffer: pkcs8,
+ algorithm: { name: 'RSA-PSS', saltLength: 0 },
+ hash: 'SHA3-256',
+ plaintext,
+ signature: signatures['sha3-256, no salt']
+ },
+ {
+ publicKeyBuffer: spki,
+ privateKeyBuffer: pkcs8,
+ algorithm: { name: 'RSA-PSS', saltLength: 0 },
+ hash: 'SHA3-384',
+ plaintext,
+ signature: signatures['sha3-384, no salt']
+ },
+ {
+ publicKeyBuffer: spki,
+ privateKeyBuffer: pkcs8,
+ algorithm: { name: 'RSA-PSS', saltLength: 0 },
+ hash: 'SHA3-512',
+ plaintext,
+ signature: signatures['sha3-512, no salt']
+ },
+ {
+ publicKeyBuffer: spki,
+ privateKeyBuffer: pkcs8,
+ algorithm: { name: 'RSA-PSS', saltLength: 32 },
+ hash: 'SHA3-256',
+ plaintext,
+ signature: signatures['sha3-256, salted']
+ },
+ {
+ publicKeyBuffer: spki,
+ privateKeyBuffer: pkcs8,
+ algorithm: { name: 'RSA-PSS', saltLength: 48 },
+ hash: 'SHA3-384',
+ plaintext,
+ signature: signatures['sha3-384, salted']
+ },
+ {
+ publicKeyBuffer: spki,
+ privateKeyBuffer: pkcs8,
+ algorithm: { name: 'RSA-PSS', saltLength: 64 },
+ hash: 'SHA3-512',
+ plaintext,
+ signature: signatures['sha3-512, salted']
+ }
];
return vectors;
diff --git a/test/fixtures/webcrypto/supports-modern-algorithms.mjs b/test/fixtures/webcrypto/supports-modern-algorithms.mjs
index 8d58397783e..250646125fa 100644
--- a/test/fixtures/webcrypto/supports-modern-algorithms.mjs
+++ b/test/fixtures/webcrypto/supports-modern-algorithms.mjs
@@ -13,10 +13,9 @@ const shake128 = crypto.getHashes().includes('shake128');
const shake256 = crypto.getHashes().includes('shake256');
const cshake128 = crypto.getHashes().includes('cshake128');
const cshake256 = crypto.getHashes().includes('cshake256');
-const sha3 = crypto.getHashes().includes('sha3-256');
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 hybridKems = !fips && pqc && (!boringSSL || shake256);
export const vectors = {
diff --git a/test/fixtures/webcrypto/supports-sha3.mjs b/test/fixtures/webcrypto/supports-sha3.mjs
index 699b72bb11b..a3221b1a2ec 100644
--- a/test/fixtures/webcrypto/supports-sha3.mjs
+++ b/test/fixtures/webcrypto/supports-sha3.mjs
@@ -1,28 +1,30 @@
-import { getHashes } from 'node:crypto';
+import { isBoringSSL } from '../../common/crypto.js';
import { ECDH, X25519 } from './supports-level-2.mjs';
-const hashes = getHashes();
-const hasSha3 = hashes.includes('sha3-256');
-
const RSA_KEY_GEN = {
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1])
};
export const vectors = {
+ 'sign': [
+ [!isBoringSSL, { name: 'ECDSA', hash: 'SHA3-256' }],
+ [!isBoringSSL, { name: 'ECDSA', hash: 'SHA3-384' }],
+ [!isBoringSSL, { name: 'ECDSA', hash: 'SHA3-512' }],
+ ],
'digest': [
- [hasSha3, 'SHA3-256'],
- [hashes.includes('sha3-384'), 'SHA3-384'],
- [hashes.includes('sha3-512'), 'SHA3-512'],
+ [true, 'SHA3-256'],
+ [true, 'SHA3-384'],
+ [true, 'SHA3-512'],
],
'generateKey': [
- [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 }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
+ [!isBoringSSL, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [!isBoringSSL, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [!isBoringSSL, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 0 }],
// This interaction is not defined for now.
@@ -30,28 +32,28 @@ export const vectors = {
[false, { name: 'HMAC', hash: 'SHA3-256' }],
],
'deriveKey': [
- [hasSha3,
+ [!isBoringSSL,
{ name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
{ name: 'AES-CBC', length: 128 }],
- [hasSha3,
+ [!isBoringSSL,
{ 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'],
- [hasSha3,
+ [!isBoringSSL,
{ name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
{ name: 'AES-CBC', length: 128 }],
- [hasSha3,
+ [!isBoringSSL,
{ 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'],
- [hasSha3 && X25519 !== undefined,
+ [!isBoringSSL && X25519 !== undefined,
{ name: 'X25519', public: X25519?.publicKey },
{ name: 'HMAC', hash: 'SHA3-256', length: 256 }],
- [hasSha3,
+ [!isBoringSSL,
{ name: 'ECDH', public: ECDH.publicKey },
{ name: 'HMAC', hash: 'SHA3-256', length: 256 }],
@@ -71,27 +73,27 @@ export const vectors = {
{ name: 'HMAC', hash: 'SHA3-256' }],
],
'deriveBits': [
- [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],
+ [!isBoringSSL, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 8],
+ [!isBoringSSL, { 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],
- [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],
+ [!isBoringSSL, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 8],
+ [!isBoringSSL, { 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': [
- [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 }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
+ [!isBoringSSL, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [!isBoringSSL, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [!isBoringSSL, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
+ [!isBoringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+ [!isBoringSSL, { 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-boringssl-evp-list.js b/test/parallel/test-crypto-boringssl-evp-list.js
index f2d8fd4796c..1589837eee8 100644
--- a/test/parallel/test-crypto-boringssl-evp-list.js
+++ b/test/parallel/test-crypto-boringssl-evp-list.js
@@ -30,4 +30,7 @@ const hashes = getHashes();
'sha1',
'sha256',
'sha512-256',
+ 'sha3-256',
+ 'sha3-384',
+ 'sha3-512',
].forEach((hash) => assert(hashes.includes(hash), hash));
diff --git a/test/parallel/test-crypto-boringssl-sha3-signatures.js b/test/parallel/test-crypto-boringssl-sha3-signatures.js
new file mode 100644
index 00000000000..f5549d73931
--- /dev/null
+++ b/test/parallel/test-crypto-boringssl-sha3-signatures.js
@@ -0,0 +1,104 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const { isBoringSSL } = require('../common/crypto');
+if (!isBoringSSL)
+ common.skip('BoringSSL-only test');
+
+const assert = require('assert');
+const {
+ constants,
+ createPrivateKey,
+ createPublicKey,
+ createSign,
+ createVerify,
+ sign,
+ subtle,
+ verify,
+} = require('crypto');
+const { promisify } = require('util');
+const signAsync = promisify(sign);
+const verifyAsync = promisify(verify);
+// BoringSSL exposes SHA-3 digests, but its PKCS1v1.5 DigestInfo table and ECDSA
+// digest restrictions still reject them. Use valid independent signatures so
+// these assertions fail when support lands and the fixture skips can be removed.
+const vectors = [
+ ...require('../fixtures/crypto/rsa_pkcs')({ includeUnsupported: true }),
+ ...require('../fixtures/crypto/ecdsa')({ includeUnsupported: true }),
+].filter(({ hash }) => hash.startsWith('SHA3-'));
+assert.strictEqual(vectors.length, 12);
+
+async function test(vector) {
+ const {
+ hash,
+ namedCurve,
+ plaintext,
+ privateKeyBuffer,
+ publicKeyBuffer,
+ signature,
+ } = vector;
+ const isECDSA = vector.name === 'ECDSA';
+ const name = isECDSA ? vector.name : vector.algorithm.name;
+ const context = `${name} ${namedCurve ?? ''} ${hash}`;
+ const error = {
+ code: isECDSA ? 'ERR_OSSL_EVP_INVALID_DIGEST_TYPE' :
+ 'ERR_OSSL_RSA_UNKNOWN_ALGORITHM_TYPE',
+ };
+ const privateKey = createPrivateKey({
+ key: privateKeyBuffer, format: 'der', type: 'pkcs8',
+ });
+ const publicKey = createPublicKey({
+ key: publicKeyBuffer, format: 'der', type: 'spki',
+ });
+ const options = isECDSA ? { dsaEncoding: 'ieee-p1363' } :
+ { padding: constants.RSA_PKCS1_PADDING };
+ const signingKey = { key: privateKey, ...options };
+ const verificationKey = { key: publicKey, ...options };
+
+ assert.throws(() => sign(hash, plaintext, signingKey), error, context);
+ await assert.rejects(signAsync(hash, plaintext, signingKey), error, context);
+ assert.throws(() => createSign(hash).update(plaintext).sign(signingKey),
+ error, context);
+
+ if (isECDSA) {
+ assert.throws(() => verify(hash, plaintext, verificationKey, signature),
+ error, context);
+ await assert.rejects(verifyAsync(hash, plaintext, verificationKey, signature),
+ error, context);
+ } else {
+ assert.strictEqual(verify(hash, plaintext, verificationKey, signature), false,
+ context);
+ assert.strictEqual(
+ await verifyAsync(hash, plaintext, verificationKey, signature), false,
+ context);
+ }
+ assert.strictEqual(createVerify(hash).update(plaintext)
+ .verify(verificationKey, signature), false, context);
+
+ const unsupportedHash = { name: 'NotSupportedError' };
+ if (!isECDSA) {
+ const algorithm = { name, hash };
+ await assert.rejects(subtle.importKey(
+ 'pkcs8', privateKeyBuffer, algorithm, false, ['sign']),
+ unsupportedHash, context);
+ await assert.rejects(subtle.importKey(
+ 'spki', publicKeyBuffer, algorithm, false, ['verify']),
+ unsupportedHash, context);
+ return;
+ }
+
+ const [webPrivateKey, webPublicKey] = await Promise.all([
+ subtle.importKey('pkcs8', privateKeyBuffer, { name, namedCurve }, false, ['sign']),
+ subtle.importKey('spki', publicKeyBuffer, { name, namedCurve }, false, ['verify']),
+ ]);
+ await assert.rejects(subtle.sign({ name, hash }, webPrivateKey, plaintext),
+ unsupportedHash, context);
+ await assert.rejects(subtle.verify({ name, hash }, webPublicKey, signature,
+ plaintext), unsupportedHash, context);
+}
+
+for (const vector of vectors)
+ test(vector).then(common.mustCall());
diff --git a/test/parallel/test-crypto-hash-stream-pipe.js b/test/parallel/test-crypto-hash-stream-pipe.js
index c6b9433456e..d94af277f63 100644
--- a/test/parallel/test-crypto-hash-stream-pipe.js
+++ b/test/parallel/test-crypto-hash-stream-pipe.js
@@ -22,7 +22,6 @@
'use strict';
const common = require('../common');
-const { isBoringSSL } = require('../common/crypto');
if (!common.hasCrypto)
common.skip('missing crypto');
@@ -32,14 +31,8 @@ const crypto = require('crypto');
const stream = require('stream');
const s = new stream.PassThrough();
-const h = isBoringSSL ?
- crypto.createHash('sha512') :
- crypto.createHash('sha3-512');
-const expect = isBoringSSL ?
- 'fba055c6fd0c5b6645407749ed7a8b41' +
- 'b8f629f2163c3ca3701d864adabda1f8' +
- '93c37bf82b22fdd151ba8e357f611da4' +
- '88a74b6a5525dd9b69554c6ce5138ad7' :
+const h = crypto.createHash('sha3-512');
+const expect =
'36a38a2a35e698974d4e5791a3f05b05' +
'198235381e864f91a0e8cd6a26b677ec' +
'dcde8e2b069bd7355fabd68abd6fc801' +
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 a5430df9e0f..88d90715041 100644
--- a/test/parallel/test-crypto-key-objects-to-crypto-key.js
+++ b/test/parallel/test-crypto-key-objects-to-crypto-key.js
@@ -12,13 +12,15 @@ const {
randomBytes,
generateKeyPairSync,
} = require('crypto');
-const { hasFIPS } = require('../common/crypto');
+const { hasFIPS, isBoringSSL } = require('../common/crypto');
const { kSupportedAlgorithms } = require('internal/crypto/util');
const rejectsXCurves = hasFIPS(3, 5);
const hashes = Object.keys(kSupportedAlgorithms.digest).filter((name) => {
return name.startsWith('SHA-') || name.startsWith('SHA3-');
});
+const unsupportedKeyHashes = isBoringSSL ? ['SHA3-256', 'SHA3-384', 'SHA3-512'] : [];
+const keyHashes = hashes.filter((hash) => !unsupportedKeyHashes.includes(hash));
const namedCurves = ['P-256', 'P-384', 'P-521'];
const signingUsages = {
@@ -105,6 +107,17 @@ function runVector(vector) {
assertVector(vector, false);
}
+function assertUnsupportedKeyHashes(keyObject, name, usages) {
+ for (const hash of unsupportedKeyHashes) {
+ for (const extractable of [true, false]) {
+ assert.throws(() => keyObject.toCryptoKey({ name, hash }, extractable, usages), {
+ name: 'NotSupportedError',
+ message: 'SHA-3 is only supported for digest operations',
+ });
+ }
+ }
+}
+
function symmetricVectors(name, usages) {
const vectors = [];
for (const length of [128, 192, 256]) {
@@ -181,7 +194,8 @@ function hmacVectors() {
const vectors = [];
for (const length of [128, 192, 256]) {
const key = secretKey(length);
- for (const hash of hashes) {
+ assertUnsupportedKeyHashes(key, 'HMAC', ['sign', 'verify']);
+ for (const hash of keyHashes) {
vectors.push(vector(
key,
{ name: 'HMAC', hash },
@@ -219,7 +233,8 @@ function rsaVectors(name, usagesByType) {
const keyPair = generateKeyPairSync('rsa', { modulusLength: 2048 });
const vectors = [];
for (const keyObject of [keyPair.publicKey, keyPair.privateKey]) {
- for (const hash of hashes) {
+ assertUnsupportedKeyHashes(keyObject, name, getUsages(keyObject, usagesByType));
+ for (const hash of keyHashes) {
vectors.push(vector(
keyObject,
{ name, hash },
diff --git a/test/parallel/test-crypto-sha3.js b/test/parallel/test-crypto-sha3.js
new file mode 100644
index 00000000000..6a7ed2e13b3
--- /dev/null
+++ b/test/parallel/test-crypto-sha3.js
@@ -0,0 +1,117 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const assert = require('assert');
+const {
+ constants,
+ createHash,
+ createHmac,
+ createPrivateKey,
+ createPublicKey,
+ createSign,
+ createVerify,
+ getHashes,
+ hash,
+ hkdf,
+ hkdfSync,
+ pbkdf2,
+ pbkdf2Sync,
+ sign,
+ verify,
+} = require('crypto');
+const rsaPss = require('../fixtures/crypto/rsa_pss');
+
+const vectors = [
+ {
+ name: 'sha3-256',
+ digest: '3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532',
+ hmac: '961fcf59ed455732e405e74f5dc78beb7aa41ad315af5e2b2a0dcf8cef9887e0',
+ hkdf: '38c2d1e7b3933f7df51e35f3eeaa0a0138b1b812e2bf57810ad4e2300e2c4a81',
+ pbkdf2: 'db7d83f5e7ebc6b0ea83e9b6c57a27e254e96cfef315278e9ed96b10483a7ad0',
+ },
+ {
+ name: 'sha3-384',
+ digest: 'ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b29' +
+ '8d88cea927ac7f539f1edf228376d25',
+ hmac: 'f1d3898bb4cd7b7904fe4e7cecb84d3225b3add0265842ec7386867d662ee3364' +
+ '8a90e98a5c673b2266eb1077fb91ed6',
+ hkdf: '3ca21ccc4e742ec6a365ee5857db1ab5a0a8843196467d7a0b26c724e542f6f5',
+ pbkdf2: '7f049e44bfddb1b591d1ae2beaa1f3be54d683063bd25759b412a86012186c16',
+ },
+ {
+ name: 'sha3-512',
+ digest: 'b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e1' +
+ '0e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0',
+ hmac: '5576c01b021fc928ae1bc83a6ddb8db5f9a201681e940ecf1f7d5c8a6ec80ac34' +
+ '614d4a68c9581957f69dc8a8c0fbf413984b0f938db3e8212c0744a1a9c5915',
+ hkdf: 'cd54f848a5f6ffb9e730d4bfbdff30d69a4b86316afdf5ba61b4fe95bd022037',
+ pbkdf2: '2b5150c50e0bf48dcb5b32205ad81e8c42d97f8f8dff57dcc365a38e857a05c7',
+ },
+];
+
+const hashes = getHashes();
+for (const vector of vectors) {
+ assert(hashes.includes(vector.name));
+ for (const name of [vector.name, vector.name.toUpperCase(),
+ vector.name.replace('sha', 'sHa')]) {
+ assert.strictEqual(hash(name, 'abc'), vector.digest);
+
+ const original = createHash(name).update('a');
+ const copy = original.copy();
+ assert.strictEqual(original.update('bc').digest('hex'), vector.digest);
+ assert.strictEqual(copy.update('b').copy().update('c').digest('hex'), vector.digest);
+
+ // Exceed every SHA-3 block size so HMAC also hashes the key.
+ assert.strictEqual(createHmac(name, Buffer.alloc(200, 0xaa))
+ .update('abc').digest('hex'), vector.hmac);
+
+ assert.strictEqual(Buffer.from(hkdfSync(name, 'passwordpassword', 'saltsaltsaltsalt', 'info', 32))
+ .toString('hex'), vector.hkdf);
+ hkdf(name, 'passwordpassword', 'saltsaltsaltsalt', 'info', 32, common.mustSucceed((result) => {
+ assert.strictEqual(Buffer.from(result).toString('hex'), vector.hkdf);
+ }));
+
+ assert.strictEqual(pbkdf2Sync('passwordpassword', 'saltsaltsaltsalt', 1000, 32, name)
+ .toString('hex'), vector.pbkdf2);
+ pbkdf2('passwordpassword', 'saltsaltsaltsalt', 1000, 32, name, common.mustSucceed((result) => {
+ assert.strictEqual(result.toString('hex'), vector.pbkdf2);
+ }));
+ }
+}
+
+// Reuse independent RSA-PSS signature vectors to check SHA-3 in both the
+// one-shot and streaming APIs, including zero-length and digest-length salts.
+for (const vector of rsaPss()) {
+ if (!vector.hash.startsWith('SHA3-')) continue;
+ const privateKey = createPrivateKey({
+ key: vector.privateKeyBuffer, format: 'der', type: 'pkcs8',
+ });
+ const publicKey = createPublicKey({
+ key: vector.publicKeyBuffer, format: 'der', type: 'spki',
+ });
+ const options = {
+ padding: constants.RSA_PKCS1_PSS_PADDING,
+ saltLength: vector.algorithm.saltLength,
+ };
+ const signingKey = { key: privateKey, ...options };
+ const verificationKey = { key: publicKey, ...options };
+
+ assert(verify(vector.hash, vector.plaintext, verificationKey,
+ vector.signature));
+ assert(createVerify(vector.hash).update(vector.plaintext)
+ .verify(verificationKey, vector.signature));
+
+ for (const signature of [
+ sign(vector.hash, vector.plaintext, signingKey),
+ createSign(vector.hash).update(vector.plaintext).sign(signingKey),
+ ]) {
+ assert(verify(vector.hash, vector.plaintext, verificationKey, signature));
+ assert(createVerify(vector.hash).update(vector.plaintext)
+ .verify(verificationKey, signature));
+ if (options.saltLength === 0)
+ assert.deepStrictEqual(signature, vector.signature);
+ }
+}
diff --git a/test/parallel/test-webcrypto-boringssl-sha3.js b/test/parallel/test-webcrypto-boringssl-sha3.js
new file mode 100644
index 00000000000..f4e2267fe0e
--- /dev/null
+++ b/test/parallel/test-webcrypto-boringssl-sha3.js
@@ -0,0 +1,80 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const { isBoringSSL } = require('../common/crypto');
+if (!isBoringSSL)
+ common.skip('BoringSSL-only test');
+
+const assert = require('assert');
+const { subtle } = require('crypto');
+const { SubtleCrypto } = globalThis;
+
+const unsupportedHash = {
+ name: 'NotSupportedError',
+ message: 'SHA-3 is only supported for digest operations',
+};
+
+async function test() {
+ const data = Buffer.from('abc');
+ const secret = Buffer.alloc(32);
+ const wrappingKey = await subtle.importKey(
+ 'raw', Buffer.alloc(16), 'AES-KW', false, ['wrapKey', 'unwrapKey']);
+ const keyToWrap = await subtle.importKey(
+ 'raw', secret, { name: 'HMAC', hash: 'SHA-256' }, true, ['sign']);
+ const wrapped = await subtle.wrapKey('raw', keyToWrap, wrappingKey, 'AES-KW');
+ const bases = await Promise.all(['HKDF', 'PBKDF2'].map((name) =>
+ subtle.importKey('raw', secret, name, false, ['deriveBits', 'deriveKey'])));
+
+ for (const [hash, length] of [
+ ['SHA3-256', 32],
+ ['SHA3-384', 48],
+ ['SHA3-512', 64],
+ ]) {
+ assert.strictEqual(SubtleCrypto.supports('digest', hash), true);
+ assert.strictEqual((await subtle.digest(hash, data)).byteLength, length);
+
+ for (const name of ['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP']) {
+ const algorithm = {
+ name, hash, modulusLength: 2048,
+ publicExponent: new Uint8Array([1, 0, 1]),
+ };
+ const usages = name === 'RSA-OAEP' ? ['encrypt', 'decrypt'] : ['sign', 'verify'];
+ assert.strictEqual(SubtleCrypto.supports('generateKey', algorithm), false);
+ assert.strictEqual(SubtleCrypto.supports('importKey', algorithm), false);
+ await assert.rejects(subtle.generateKey(algorithm, true, usages), unsupportedHash);
+ }
+
+ const hmac = { name: 'HMAC', hash, length: 256 };
+ assert.strictEqual(SubtleCrypto.supports('generateKey', hmac), false);
+ assert.strictEqual(SubtleCrypto.supports('importKey', hmac), false);
+ await assert.rejects(subtle.generateKey(hmac, true, ['sign']), unsupportedHash);
+ await assert.rejects(subtle.importKey('raw', secret, hmac, false, ['sign']),
+ unsupportedHash);
+ assert.strictEqual(SubtleCrypto.supports('unwrapKey', 'AES-KW', hmac), false);
+ await assert.rejects(subtle.unwrapKey(
+ 'raw', wrapped, wrappingKey, 'AES-KW', hmac, false, ['sign']), unsupportedHash);
+
+ for (const [index, algorithm] of [
+ { name: 'HKDF', hash, salt: secret, info: data },
+ { name: 'PBKDF2', hash, salt: secret, iterations: 1 },
+ ].entries()) {
+ const base = bases[index];
+ const derivedKey = { name: 'AES-GCM', length: 128 };
+ assert.strictEqual(SubtleCrypto.supports('deriveBits', algorithm, 128), false);
+ assert.strictEqual(SubtleCrypto.supports('deriveKey', algorithm, derivedKey), false);
+ await assert.rejects(subtle.deriveBits(algorithm, base, 128), unsupportedHash);
+ await assert.rejects(subtle.deriveKey(
+ algorithm, base, derivedKey, false, ['encrypt']), unsupportedHash);
+
+ const sha256 = { ...algorithm, hash: 'SHA-256' };
+ assert.strictEqual(SubtleCrypto.supports('deriveKey', sha256, hmac), false);
+ await assert.rejects(subtle.deriveKey(
+ sha256, base, hmac, false, ['sign']), unsupportedHash);
+ }
+ }
+}
+
+test().then(common.mustCall());
diff --git a/test/parallel/test-webcrypto-derivebits-hkdf.js b/test/parallel/test-webcrypto-derivebits-hkdf.js
index 4fb372fd3a8..4fbaa1d7c64 100644
--- a/test/parallel/test-webcrypto-derivebits-hkdf.js
+++ b/test/parallel/test-webcrypto-derivebits-hkdf.js
@@ -35,7 +35,7 @@ if (!isBoringSSL) {
['HMAC', 256, 'SHA3-512', 'sign', 'verify'],
);
} else {
- common.printSkipMessage('Skipping unsupported test cases');
+ common.printSkipMessage('SHA-3 is only supported for digest operations');
}
if (hasOpenSSL(3)) {
diff --git a/test/parallel/test-webcrypto-derivekey.js b/test/parallel/test-webcrypto-derivekey.js
index 318ca8d2500..a4acb112c6b 100644
--- a/test/parallel/test-webcrypto-derivekey.js
+++ b/test/parallel/test-webcrypto-derivekey.js
@@ -97,7 +97,7 @@ const fips4 = hasFIPS(4);
'ac5d90a6bc848961e78a491887539b29c532a9c0d0b39cec464df071a63e0061'],
);
} else {
- common.printSkipMessage('Skipping unsupported SHA-3 test cases');
+ common.printSkipMessage('SHA-3 is only supported for digest operations');
}
const tests = Promise.all(kTests.map((args) => test(...args)));
@@ -150,7 +150,7 @@ const fips4 = hasFIPS(4);
'03431052c37d626ae3fc1df582ff2a4d610642fc27e1b8130ca5980c0b0756ac']
);
} else {
- common.printSkipMessage('Skipping unsupported SHA-3 test cases');
+ common.printSkipMessage('SHA-3 is only supported for digest operations');
}
const promises = kTests.map((args) => test(...args));
@@ -188,7 +188,7 @@ const fips4 = hasFIPS(4);
// [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
);
} else {
- common.printSkipMessage('Skipping unsupported SHA-3 test cases');
+ common.printSkipMessage('SHA-3 is only supported for digest operations');
}
if (hasOpenSSL(3)) {
@@ -248,7 +248,7 @@ const fips4 = hasFIPS(4);
// [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
);
} else {
- common.printSkipMessage('Skipping unsupported SHA-3 test cases');
+ common.printSkipMessage('SHA-3 is only supported for digest operations');
}
if (hasOpenSSL(3)) {
diff --git a/test/parallel/test-webcrypto-digest.js b/test/parallel/test-webcrypto-digest.js
index 89bd86bd928..f9e31192602 100644
--- a/test/parallel/test-webcrypto-digest.js
+++ b/test/parallel/test-webcrypto-digest.js
@@ -16,15 +16,15 @@ const kTests = [
['SHA-256', ['sha256'], 256],
['SHA-384', ['sha384'], 384],
['SHA-512', ['sha512'], 512],
+ ['SHA3-256', ['sha3-256'], 256],
+ ['SHA3-384', ['sha3-384'], 384],
+ ['SHA3-512', ['sha3-512'], 512],
];
if (!isBoringSSL) {
kTests.push(
[{ name: 'cSHAKE128', outputLength: 256 }, ['shake128', { outputLength: 256 >> 3 }], 256],
[{ name: 'cSHAKE256', outputLength: 512 }, ['shake256', { outputLength: 512 >> 3 }], 512],
- ['SHA3-256', ['sha3-256'], 256],
- ['SHA3-384', ['sha3-384'], 384],
- ['SHA3-512', ['sha3-512'], 512],
);
} else {
common.printSkipMessage('Skipping unsupported test cases');
@@ -175,41 +175,41 @@ const kDigestedData = {
'd5290933492f9d17411926a613dd0611668c2ac999e8' +
'c011aabaa9004323425fbad75b0f58ee6e777a94'
},
- 'sha3-256': {
- empty: 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a',
- short: '3059af7aa33b517084e8ad7bbc4fb208a44c28ef32b4698d103dd540e4f91aa1',
- medium: '1fa7cd1da74cd8046417508c8314e74a9a4a9d38f9f18e6cb215b8c891a0a80e',
- long: 'b2cfc61e0386cdaef5e10a2be189891f5ef52a7624bfcd8edc893acc64fec600'
- },
- 'sha3-384': {
- empty: '0c63a75b845e4f7d01107d852e4c2485c51a50aaaa9' +
- '4fc61995e71bbee983a2ac3713831264adb47fb6bd1' +
- 'e058d5f004',
- short: '54b8f0e4cf4974de740098f66b3024479b01631315a' +
- '6773606c33eadc32556a6e778e08f0225ae79265aec' +
- '666cb2390b',
- medium: '437b7d8b68b250b5c1739ea4cc86db2033879dfb18' +
- 'de292c9c50d9c193a4c79a08a6cae3f4e483c2795e' +
- 'a5d1ef7e69d2',
- long: '3b39c4c97ad87613305d0ccc987181713e2d5e84b1f9' +
- '760011bcce0c297499005bdce8a3d2409b5ad0164f32' +
- 'bb8778d0'
- },
- 'sha3-512': {
- empty: 'a69f73cca23a9ac5c8b567dc185a756e97c982164fe' +
- '25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9' +
- '402c3ac558f500199d95b6d3e301758586281dcd26',
- short: '2dd2e07a62e6ad0498ba84f313c4d4024cb46001f78' +
- 'f75db336b0d4d8bd2a9ec152c4ad20878735d82ba08' +
- '72ecf59608ef3ced2b2a8669427e7da31e362333d8',
- medium: 'e640a21909536640369e9b0a48931c5cb2efcbc91f' +
- 'ecf247306bc96a0e4ca33307cb8e1b9af367946dd01' +
- 'c243f3907508d04f1692a3161df1f898de8ee25febe',
- long: 'bd262cecf565c338032de5ba0138f0aacfe7dde83d27' +
- '2d0d37d952829ed25de1a1342d98659ef7d2fa4aca7c' +
- 'e2b1aa0784d8fc1dcbf81bcec7a7431a3da36bf7'
- }
} : {}),
+ 'sha3-256': {
+ empty: 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a',
+ short: '3059af7aa33b517084e8ad7bbc4fb208a44c28ef32b4698d103dd540e4f91aa1',
+ medium: '1fa7cd1da74cd8046417508c8314e74a9a4a9d38f9f18e6cb215b8c891a0a80e',
+ long: 'b2cfc61e0386cdaef5e10a2be189891f5ef52a7624bfcd8edc893acc64fec600'
+ },
+ 'sha3-384': {
+ empty: '0c63a75b845e4f7d01107d852e4c2485c51a50aaaa9' +
+ '4fc61995e71bbee983a2ac3713831264adb47fb6bd1' +
+ 'e058d5f004',
+ short: '54b8f0e4cf4974de740098f66b3024479b01631315a' +
+ '6773606c33eadc32556a6e778e08f0225ae79265aec' +
+ '666cb2390b',
+ medium: '437b7d8b68b250b5c1739ea4cc86db2033879dfb18' +
+ 'de292c9c50d9c193a4c79a08a6cae3f4e483c2795e' +
+ 'a5d1ef7e69d2',
+ long: '3b39c4c97ad87613305d0ccc987181713e2d5e84b1f9' +
+ '760011bcce0c297499005bdce8a3d2409b5ad0164f32' +
+ 'bb8778d0'
+ },
+ 'sha3-512': {
+ empty: 'a69f73cca23a9ac5c8b567dc185a756e97c982164fe' +
+ '25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9' +
+ '402c3ac558f500199d95b6d3e301758586281dcd26',
+ short: '2dd2e07a62e6ad0498ba84f313c4d4024cb46001f78' +
+ 'f75db336b0d4d8bd2a9ec152c4ad20878735d82ba08' +
+ '72ecf59608ef3ced2b2a8669427e7da31e362333d8',
+ medium: 'e640a21909536640369e9b0a48931c5cb2efcbc91f' +
+ 'ecf247306bc96a0e4ca33307cb8e1b9af367946dd01' +
+ 'c243f3907508d04f1692a3161df1f898de8ee25febe',
+ long: 'bd262cecf565c338032de5ba0138f0aacfe7dde83d27' +
+ '2d0d37d952829ed25de1a1342d98659ef7d2fa4aca7c' +
+ 'e2b1aa0784d8fc1dcbf81bcec7a7431a3da36bf7'
+ }
};
async function testDigest(size, alg) {
diff --git a/test/parallel/test-webcrypto-encrypt-decrypt.js b/test/parallel/test-webcrypto-encrypt-decrypt.js
index 8c851682432..45d0aa64f72 100644
--- a/test/parallel/test-webcrypto-encrypt-decrypt.js
+++ b/test/parallel/test-webcrypto-encrypt-decrypt.js
@@ -48,18 +48,21 @@ async function testRSAOaep(publicKey, privateKey) {
await testRSAOaep(publicKey, privateKey);
+ // SHA-3 is only supported for digest operations in BoringSSL.
if (!isBoringSSL) {
// Import the same key material with SHA-3 to avoid another RSA keygen.
const [spki, pkcs8] = await Promise.all([
subtle.exportKey('spki', publicKey),
subtle.exportKey('pkcs8', privateKey),
]);
- const algorithm = { name: 'RSA-OAEP', hash: 'SHA3-384' };
- const [sha3PublicKey, sha3PrivateKey] = await Promise.all([
- subtle.importKey('spki', spki, algorithm, false, ['encrypt']),
- subtle.importKey('pkcs8', pkcs8, algorithm, false, ['decrypt']),
- ]);
- await testRSAOaep(sha3PublicKey, sha3PrivateKey);
+ for (const hash of ['SHA3-256', 'SHA3-384', 'SHA3-512']) {
+ const algorithm = { name: 'RSA-OAEP', hash };
+ const [sha3PublicKey, sha3PrivateKey] = await Promise.all([
+ subtle.importKey('spki', spki, algorithm, false, ['encrypt']),
+ subtle.importKey('pkcs8', pkcs8, algorithm, false, ['decrypt']),
+ ]);
+ await testRSAOaep(sha3PublicKey, sha3PrivateKey);
+ }
}
})().then(common.mustCall());
diff --git a/test/parallel/test-webcrypto-export-import-rsa.js b/test/parallel/test-webcrypto-export-import-rsa.js
index 385b8e0bc52..9f184084e7c 100644
--- a/test/parallel/test-webcrypto-export-import-rsa.js
+++ b/test/parallel/test-webcrypto-export-import-rsa.js
@@ -2,7 +2,6 @@
const common = require('../common');
-const { isBoringSSL } = require('../common/crypto');
const fixtures = require('../common/fixtures');
if (!common.hasCrypto)
@@ -10,6 +9,7 @@ if (!common.hasCrypto)
const assert = require('assert');
const crypto = require('crypto');
+const { isBoringSSL } = require('../common/crypto');
const { subtle } = globalThis.crypto;
const sizes = [1024, 2048, 4096];
@@ -19,18 +19,11 @@ const hashes = [
'SHA-256',
'SHA-384',
'SHA-512',
+ 'SHA3-256',
+ 'SHA3-384',
+ 'SHA3-512',
];
-if (!isBoringSSL) {
- hashes.push(
- 'SHA3-256',
- 'SHA3-384',
- 'SHA3-512',
- );
-} else {
- common.printSkipMessage('Skipping unsupported SHA-3 test cases');
-}
-
const keyData = {
1024: {
spki: Buffer.from(
@@ -643,6 +636,22 @@ const testVectors = [
hashes.forEach((hash) => {
[true, false].forEach((extractable) => {
testVectors.forEach((vector) => {
+ if (isBoringSSL && hash.startsWith('SHA3-')) {
+ const { spki, pkcs8, jwk } = keyData[size];
+ for (const [format, data, usages] of [
+ ['spki', spki, vector.publicUsages],
+ ['pkcs8', pkcs8, vector.privateUsages],
+ ['jwk', { kty: jwk.kty, n: jwk.n, e: jwk.e }, vector.publicUsages],
+ ['jwk', jwk, vector.privateUsages],
+ ]) {
+ variations.push(assert.rejects(subtle.importKey(
+ format, data, { name: vector.name, hash }, extractable, usages), {
+ name: 'NotSupportedError',
+ message: 'SHA-3 is only supported for digest operations',
+ }));
+ }
+ return;
+ }
variations.push(testImportSpki(vector, size, hash, extractable));
variations.push(testImportPkcs8(vector, size, hash, extractable));
variations.push(testImportJwk(vector, size, hash, extractable));
diff --git a/test/parallel/test-webcrypto-export-import.js b/test/parallel/test-webcrypto-export-import.js
index 63fdde05f1e..4578f7d587d 100644
--- a/test/parallel/test-webcrypto-export-import.js
+++ b/test/parallel/test-webcrypto-export-import.js
@@ -484,12 +484,19 @@ if (hasOpenSSL(3)) {
}
// SHA-3 hashes and JWK "alg"
-if (!isBoringSSL) {
+{
const rsa = fixtures.readKey('rsa_private_2048.pem');
const privateKey = createPrivateKey(rsa);
const publicKey = createPublicKey(privateKey);
async function test(keyObject, algorithm, usages) {
+ if (isBoringSSL) {
+ assert.throws(() => keyObject.toCryptoKey(algorithm, true, usages), {
+ name: 'NotSupportedError',
+ message: 'SHA-3 is only supported for digest operations',
+ });
+ return;
+ }
const key = keyObject.toCryptoKey(algorithm, true, usages);
const jwk = await subtle.exportKey('jwk', key);
assert.strictEqual(jwk.alg, undefined);
@@ -501,7 +508,8 @@ if (!isBoringSSL) {
test(privateKey, { name, hash }, [name === 'RSA-OAEP' ? 'unwrapKey' : 'sign']).then(common.mustCall());
}
- test(createSecretKey(Buffer.alloc(32)), { name: 'HMAC', hash }, ['sign']);
+ test(createSecretKey(Buffer.alloc(32)), { name: 'HMAC', hash }, ['sign'])
+ .then(common.mustCall());
}
{
@@ -517,7 +525,16 @@ if (!isBoringSSL) {
message: 'JWK "alg" does not match the requested algorithm',
}).then(common.mustCall());
- subtle.importKey('jwk', jwk, { name: 'HMAC', hash: 'SHA3-256' }, false, ['sign', 'verify']).then(common.mustCall());
+ const imported = subtle.importKey(
+ 'jwk', jwk, { name: 'HMAC', hash: 'SHA3-256' }, false, ['sign', 'verify']);
+ if (isBoringSSL) {
+ assert.rejects(imported, {
+ name: 'NotSupportedError',
+ message: 'SHA-3 is only supported for digest operations',
+ }).then(common.mustCall());
+ } else {
+ imported.then(common.mustCall());
+ }
}
}
diff --git a/test/parallel/test-webcrypto-keygen.js b/test/parallel/test-webcrypto-keygen.js
index b1fc8f41b2a..6618a43db74 100644
--- a/test/parallel/test-webcrypto-keygen.js
+++ b/test/parallel/test-webcrypto-keygen.js
@@ -527,7 +527,7 @@ if (hasOpenSSL(3, 5) || isBoringSSL) {
);
}
} else {
- common.printSkipMessage('Skipping unsupported SHA-3 test case');
+ common.printSkipMessage('SHA-3 is only supported for digest operations');
}
const tests = kTests.map((args) => test(...args));
@@ -733,7 +733,7 @@ if (hasOpenSSL(3, 5) || isBoringSSL) {
// [undefined, 'SHA3-512', ['sign', 'verify']],
);
} else {
- common.printSkipMessage('Skipping unsupported SHA-3 test cases');
+ common.printSkipMessage('SHA-3 is only supported for digest operations');
}
const tests = Promise.all(kTests.map((args) => test(...args)));
diff --git a/test/parallel/test-webcrypto-normalization-boundary.js b/test/parallel/test-webcrypto-normalization-boundary.js
index 20102c32e6a..8365a7d4059 100644
--- a/test/parallel/test-webcrypto-normalization-boundary.js
+++ b/test/parallel/test-webcrypto-normalization-boundary.js
@@ -39,7 +39,7 @@ const { subtle } = globalThis.crypto;
{ name: 'OperationError' });
await assert.rejects(subtle.generateKey({ name: 'HMAC', hash: 'SHA-256', length: 0 }, false, []),
{ name: 'OperationError' });
- if (SubtleCrypto.supports('digest', 'SHA3-256')) {
+ if (SubtleCrypto.supports('generateKey', { name: 'HMAC', hash: 'SHA3-256', length: 256 })) {
const hmac = { name: 'HMAC', hash: 'SHA3-256' };
await assert.rejects(subtle.generateKey(hmac, false, ['encrypt']), { name: 'SyntaxError' });
await assert.rejects(subtle.generateKey(hmac, false, []), { name: 'NotSupportedError' });
diff --git a/test/parallel/test-webcrypto-rsa-jwk-sha3-alg.js b/test/parallel/test-webcrypto-rsa-jwk-sha3-alg.js
index 88bc64194e6..55e45119103 100644
--- a/test/parallel/test-webcrypto-rsa-jwk-sha3-alg.js
+++ b/test/parallel/test-webcrypto-rsa-jwk-sha3-alg.js
@@ -4,13 +4,10 @@ const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
-const { isBoringSSL } = require('../common/crypto');
-if (isBoringSSL)
- common.skip('missing SHA-3');
-
const assert = require('assert');
const { createPrivateKey, createPublicKey } = require('crypto');
const fixtures = require('../common/fixtures');
+const { isBoringSSL } = require('../common/crypto');
const { subtle } = globalThis.crypto;
(async () => {
@@ -24,6 +21,14 @@ const { subtle } = globalThis.crypto;
const usages = name === 'RSA-OAEP' ? [jwk.d ? 'decrypt' : 'encrypt'] :
[jwk.d ? 'sign' : 'verify'];
const algorithm = { name, hash };
+ if (isBoringSSL) {
+ await assert.rejects(
+ subtle.importKey('jwk', jwk, algorithm, true, usages), {
+ name: 'NotSupportedError',
+ message: 'SHA-3 is only supported for digest operations',
+ });
+ continue;
+ }
// There is no JWK alg identifier for RSA with SHA-3. Omitting alg is
// valid, but an identifier for SHA-2 or an unknown identifier is not.
const key = await subtle.importKey('jwk', jwk, algorithm, true, usages);
diff --git a/test/parallel/test-webcrypto-sign-verify-hmac.js b/test/parallel/test-webcrypto-sign-verify-hmac.js
index bbe5dd49810..93d8a43c7ae 100644
--- a/test/parallel/test-webcrypto-sign-verify-hmac.js
+++ b/test/parallel/test-webcrypto-sign-verify-hmac.js
@@ -7,6 +7,7 @@ if (!common.hasCrypto)
const assert = require('assert');
const { getFips } = require('crypto');
+const { isBoringSSL } = require('../common/crypto');
const { subtle } = globalThis.crypto;
const vectors = require('../fixtures/crypto/hmac')();
@@ -174,6 +175,10 @@ async function testSign({ hash,
const variations = [];
for (const vector of vectors) {
+ // The shared fixtures also cover node:crypto; WebCrypto only supports
+ // SHA-3 for digest operations in BoringSSL.
+ if (isBoringSSL && vector.hash.startsWith('SHA3-'))
+ continue;
variations.push(testVerify(vector));
variations.push(testSign(vector));
}
diff --git a/test/parallel/test-webcrypto-sign-verify-rsa.js b/test/parallel/test-webcrypto-sign-verify-rsa.js
index 201d28fbf1f..8551ad3afa3 100644
--- a/test/parallel/test-webcrypto-sign-verify-rsa.js
+++ b/test/parallel/test-webcrypto-sign-verify-rsa.js
@@ -267,6 +267,7 @@ async function testSaltLengths(keyLength) {
['SHA-256', 32],
['SHA-384', 48],
['SHA-512', 64],
+ // SHA-3 is only supported for digest operations in BoringSSL.
...(!isBoringSSL ? [
['SHA3-256', 32],
['SHA3-384', 48],
@@ -283,12 +284,18 @@ async function testSaltLengths(keyLength) {
(async function() {
const variations = [];
+ // The shared fixtures also cover node:crypto; WebCrypto only supports
+ // SHA-3 for digest operations in BoringSSL.
rsa_pkcs().forEach((vector) => {
+ if (isBoringSSL && vector.hash.startsWith('SHA3-'))
+ return;
variations.push(testVerify(vector));
variations.push(rejectsSha1Signing && vector.hash === 'SHA-1' ?
testFipsSignRejected(vector) : testSign(vector));
});
rsa_pss().forEach((vector) => {
+ if (isBoringSSL && vector.hash.startsWith('SHA3-'))
+ return;
variations.push(testVerify(vector));
variations.push(rejectsSha1Signing && vector.hash === 'SHA-1' ?
testFipsSignRejected(vector) : testSign(vector));
diff --git a/test/wpt/status/WebCryptoAPI.cjs b/test/wpt/status/WebCryptoAPI.cjs
index a73db3793a9..4e8e4efd882 100644
--- a/test/wpt/status/WebCryptoAPI.cjs
+++ b/test/wpt/status/WebCryptoAPI.cjs
@@ -81,7 +81,6 @@ if (isBoringSSL) {
'secure-curves/derive_bits_keys/cfrg_curves_bits_curve448.https.any.js',
'secure-curves/derive_bits_keys/cfrg_curves_keys_curve448.https.any.js',
'modern-algos/digest/cshake.https.any.js',
- 'modern-algos/digest/sha3.https.any.js',
'secure-curves/generateKey/failures_Ed448.https.any.js',
'modern-algos/generateKey/failures_Hybrid-KEM.https.any.js',
'secure-curves/generateKey/failures_X448.https.any.js',