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>
This commit is contained in:
Filip Skokan authored and Node.js GitHub Bot committed 2026-10-24 22:04:14 +00:00
1 parent a2ce2c6def
commit 7e58295439
28 files changed
+616 -246

No files matched your search

+7
View File
@@ -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);
}
+11 -4
View File
@@ -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?)
+3 -1
View File
@@ -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 } =
+6
View File
@@ -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();
+6 -5
View File
@@ -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');
}
+20 -26
View File
@@ -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 = [];
+4 -3
View File
@@ -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,
+84 -90
View File
@@ -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;
+1 -2
View File
@@ -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 = {
+35 -33
View File
@@ -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': [
@@ -30,4 +30,7 @@ const hashes = getHashes();
'sha1',
'sha256',
'sha512-256',
'sha3-256',
'sha3-384',
'sha3-512',
].forEach((hash) => assert(hashes.includes(hash), hash));
@@ -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());
@@ -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' +
@@ -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 },
+117
View File
@@ -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);
}
}
@@ -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());
@@ -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)) {
+4 -4
View File
@@ -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)) {
+37 -37
View File
@@ -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) {
@@ -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());
@@ -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));
+20 -3
View File
@@ -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());
}
}
}
+2 -2
View File
@@ -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)));
@@ -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' });
@@ -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);
@@ -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));
}
@@ -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));
-1
View File
@@ -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',