Repository: nodejs/node
Pull request: crypto: require HMAC key length with SHA-3 hashes in Web Cryptography

The interaction between HMAC when its hash is SHA-3 and its length is not defined during in generateKey() and deriveKey() is left undefined for now as per https://github.com/WICG/webcrypto-modern-algos/issues/23.

Changed files:
filename: lib/internal/crypto/util.js
patch: @@ -629,11 +629,13 @@ function getBlockSize(name) {
     case 'SHA-512':
       return 1024;
     case 'SHA3-256':
-      return 1088;
+      // Fall through
     case 'SHA3-384':
-      return 832;
+      // Fall through
     case 'SHA3-512':
-      return 576;
+      // This interaction is not defined for now.
+      // https://github.com/WICG/webcrypto-modern-algos/issues/23
+      throw lazyDOMException('Explicit algorithm length member is required', 'NotSupportedError');
   }
 }
 

filename: lib/internal/crypto/webcrypto.js
patch: @@ -9,6 +9,7 @@ const {
   ReflectConstruct,
   StringPrototypeRepeat,
   StringPrototypeSlice,
+  StringPrototypeStartsWith,
   SymbolToStringTag,
 } = primordials;
 
@@ -1235,7 +1236,6 @@ function check(op, alg, length) {
     case 'sign':
     case 'verify':
     case 'digest':
-    case 'generateKey':
     case 'importKey':
     case 'exportKey':
     case 'wrapKey':
@@ -1260,6 +1260,17 @@ function check(op, alg, length) {
 
       return true;
     }
+    case 'generateKey': {
+      if (
+        normalizedAlgorithm.name === 'HMAC' &&
+        normalizedAlgorithm.length === undefined &&
+        StringPrototypeStartsWith(normalizedAlgorithm.hash.name, 'SHA3-')
+      ) {
+        return false;
+      }
+
+      return true;
+    }
     default: {
       const assert = require('internal/assert');
       assert.fail('Unreachable code');

filename: test/fixtures/webcrypto/supports-sha3.mjs
patch: @@ -19,24 +19,26 @@ export const vectors = {
     [!boringSSL, 'SHA3-512'],
   ],
   'generateKey': [
-    [!boringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
     [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
     [false, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
     [!boringSSL, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
     [!boringSSL, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
     [!boringSSL, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
-    [!boringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
     [!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
     [false, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
     [false, { name: 'HMAC', hash: 'SHA3-256', length: 0 }],
+
+    // This interaction is not defined for now.
+    // https://github.com/WICG/webcrypto-modern-algos/issues/23
+    [false, { name: 'HMAC', hash: 'SHA3-256' }],
   ],
   'deriveKey': [
     [!boringSSL,
      { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
      { name: 'AES-CBC', length: 128 }],
     [!boringSSL,
      { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
-     { name: 'HMAC', hash: 'SHA3-256' }],
+     { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
     [false,
      { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
      'HKDF'],
@@ -45,14 +47,29 @@ export const vectors = {
      { name: 'AES-CBC', length: 128 }],
     [!boringSSL,
      { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
-     { name: 'HMAC', hash: 'SHA3-256' }],
+     { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
     [false,
      { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
      'HKDF'],
     [!boringSSL,
      { name: 'X25519', public: X25519.publicKey },
-     { name: 'HMAC', hash: 'SHA3-256' }],
+     { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
     [!boringSSL,
+     { name: 'ECDH', public: ECDH.publicKey },
+     { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
+
+    // This interaction is not defined for now.
+    // https://github.com/WICG/webcrypto-modern-algos/issues/23
+    [false,
+     { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
+     { name: 'HMAC', hash: 'SHA3-256' }],
+    [false,
+     { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
+     { name: 'HMAC', hash: 'SHA3-256' }],
+    [false,
+     { name: 'X25519', public: X25519.publicKey },
+     { name: 'HMAC', hash: 'SHA3-256' }],
+    [false,
      { name: 'ECDH', public: ECDH.publicKey },
      { name: 'HMAC', hash: 'SHA3-256' }],
   ],

filename: test/parallel/test-webcrypto-derivekey.js
patch: @@ -157,9 +157,14 @@ const { KeyObject } = require('crypto');
     // Not long enough secret generated by ECDH
     [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024],
     [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024],
-    [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 1088],
-    [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 832],
-    [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 576],
+    [{ name: 'HMAC', hash: 'SHA3-256', length: 256 }, 'sign', 256],
+    [{ name: 'HMAC', hash: 'SHA3-384', length: 384 }, 'sign', 384],
+    [{ name: 'HMAC', hash: 'SHA3-512', length: 512 }, 'sign', 512],
+    // This interaction is not defined for now.
+    // https://github.com/WICG/webcrypto-modern-algos/issues/23
+    // [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 256],
+    // [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 384],
+    // [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
   ];
 
   (async () => {
@@ -196,9 +201,14 @@ const { KeyObject } = require('crypto');
     [{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 512],
     [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024],
     [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024],
-    [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 1088],
-    [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 832],
-    [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 576],
+    [{ name: 'HMAC', hash: 'SHA3-256', length: 256 }, 'sign', 256],
+    [{ name: 'HMAC', hash: 'SHA3-384', length: 384 }, 'sign', 384],
+    [{ name: 'HMAC', hash: 'SHA3-512', length: 512 }, 'sign', 512],
+    // This interaction is not defined for now.
+    // https://github.com/WICG/webcrypto-modern-algos/issues/23
+    // [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 256],
+    // [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 384],
+    // [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
   ];
 
   (async () => {

filename: test/parallel/test-webcrypto-keygen.js
patch: @@ -614,9 +614,6 @@ if (hasOpenSSL(3, 5)) {
         case 'SHA-256': length = 512; break;
         case 'SHA-384': length = 1024; break;
         case 'SHA-512': length = 1024; break;
-        case 'SHA3-256': length = 1088; break;
-        case 'SHA3-384': length = 832; break;
-        case 'SHA3-512': length = 576; break;
       }
     }
 
@@ -642,20 +639,24 @@ if (hasOpenSSL(3, 5)) {
   }
 
   const kTests = [
-    [ undefined, 'SHA-1', ['sign', 'verify']],
-    [ undefined, 'SHA-256', ['sign', 'verify']],
-    [ undefined, 'SHA-384', ['sign', 'verify']],
-    [ undefined, 'SHA-512', ['sign', 'verify']],
-    [ 128, 'SHA-256', ['sign', 'verify']],
-    [ 1024, 'SHA-512', ['sign', 'verify']],
+    [undefined, 'SHA-1', ['sign', 'verify']],
+    [undefined, 'SHA-256', ['sign', 'verify']],
+    [undefined, 'SHA-384', ['sign', 'verify']],
+    [undefined, 'SHA-512', ['sign', 'verify']],
+    [128, 'SHA-256', ['sign', 'verify']],
+    [1024, 'SHA-512', ['sign', 'verify']],
   ];
 
   if (!process.features.openssl_is_boringssl) {
     kTests.push(
-
-      [ undefined, 'SHA3-256', ['sign', 'verify']],
-      [ undefined, 'SHA3-384', ['sign', 'verify']],
-      [ undefined, 'SHA3-512', ['sign', 'verify']],
+      [256, 'SHA3-256', ['sign', 'verify']],
+      [384, 'SHA3-384', ['sign', 'verify']],
+      [512, 'SHA3-512', ['sign', 'verify']],
+      // This interaction is not defined for now.
+      // https://github.com/WICG/webcrypto-modern-algos/issues/23
+      // [undefined, 'SHA3-256', ['sign', 'verify']],
+      // [undefined, 'SHA3-384', ['sign', 'verify']],
+      // [undefined, 'SHA3-512', ['sign', 'verify']],
     );
   } else {
     common.printSkipMessage('Skipping unsupported SHA-3 test cases');