| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0f54a87 commit b505cd5
29 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1924,26 +1924,39 @@ added: v24.15.0 | |||
| 1924 | 1924 | ||
| 1925 | 1925 | <!-- YAML | |
| 1926 | 1926 | added: v24.7.0 | |
| 1927 | + changes: | ||
| 1928 | + - version: REPLACEME | ||
| 1929 | + pr-url: https://github.com/nodejs/node/pull/63988 | ||
| 1930 | + description: Named cSHAKE variants are now accepted. | ||
| 1927 | 1931 | --> | |
| 1928 | 1932 | ||
| 1929 | 1933 | * Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined} | |
| 1930 | 1934 | ||
| 1931 | - The `functionName` member represents the function name, used by NIST to define | ||
| 1932 | - functions based on cSHAKE. | ||
| 1933 | - The Node.js Web Crypto API implementation only supports zero-length functionName | ||
| 1934 | - which is equivalent to not providing functionName at all. | ||
| 1935 | + The `functionName` member represents the NIST function-name byte string used to | ||
| 1936 | + domain-separate functions built on top of cSHAKE. Accepted values are: | ||
| 1937 | + | ||
| 1938 | + * empty or `undefined`, in which case cSHAKE is equivalent to plain SHAKE | ||
| 1939 | + * the ASCII byte sequence `'KMAC'` | ||
| 1940 | + * the ASCII byte sequence `'TupleHash'` | ||
| 1941 | + * the ASCII byte sequence `'ParallelHash'` | ||
| 1935 | 1942 | ||
| 1936 | 1943 | #### `cShakeParams.customization` | |
| 1937 | 1944 | ||
| 1938 | 1945 | <!-- YAML | |
| 1939 | 1946 | added: v24.7.0 | |
| 1947 | + changes: | ||
| 1948 | + - version: REPLACEME | ||
| 1949 | + pr-url: https://github.com/nodejs/node/pull/63988 | ||
| 1950 | + description: Non-empty customization is now supported. | ||
| 1940 | 1951 | --> | |
| 1941 | 1952 | ||
| 1942 | 1953 | * Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined} | |
| 1943 | 1954 | ||
| 1944 | - The `customization` member represents the customization string. | ||
| 1945 | - The Node.js Web Crypto API implementation only supports zero-length customization | ||
| 1946 | - which is equivalent to not providing customization at all. | ||
| 1955 | + The `customization` member represents the customization data. Accepted | ||
| 1956 | + values are: | ||
| 1957 | + | ||
| 1958 | + * empty or `undefined`, in which case cSHAKE is equivalent to plain SHAKE | ||
| 1959 | + * up to 512 bytes of arbitrary data | ||
| 1947 | 1960 | ||
| 1948 | 1961 | ### Class: `EcdhKeyDeriveParams` | |
| 1949 | 1962 | ||
@@ -2472,9 +2485,7 @@ added: v24.8.0 | |||
| 2472 | 2485 | added: v24.15.0 | |
| 2473 | 2486 | --> | |
| 2474 | 2487 | ||
| 2475 | - * Type: {number} | ||
| 2476 | - | ||
| 2477 | - The length of the output in bytes. This must be a positive integer. | ||
| 2488 | + * Type: {number} represents the requested output length in bits. | ||
| 2478 | 2489 | ||
| 2479 | 2490 | #### `kmacParams.customization` | |
| 2480 | 2491 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,11 +3,9 @@ | |||
| 3 | 3 | const { | |
| 4 | 4 | ArrayBufferPrototypeSlice, | |
| 5 | 5 | FunctionPrototypeCall, | |
| 6 | - MathCeil, | ||
| 7 | 6 | ObjectDefineProperty, | |
| 8 | 7 | SafeSet, | |
| 9 | 8 | TypedArrayPrototypeGetBuffer, | |
| 10 | - Uint8Array, | ||
| 11 | 9 | } = primordials; | |
| 12 | 10 | ||
| 13 | 11 | const { Buffer } = require('buffer'); | |
@@ -65,7 +63,9 @@ const { | |||
| 65 | 63 | getArrayBufferOrView, | |
| 66 | 64 | jobPromise, | |
| 67 | 65 | jobPromiseThen, | |
| 66 | + numBitsToBytes, | ||
| 68 | 67 | toBuf, | |
| 68 | + truncateToBitLength, | ||
| 69 | 69 | kHandle, | |
| 70 | 70 | } = require('internal/crypto/util'); | |
| 71 | 71 | ||
@@ -365,7 +365,6 @@ function diffieHellman(options, callback) { | |||
| 365 | 365 | job.run(); | |
| 366 | 366 | } | |
| 367 | 367 | ||
| 368 | - let masks; | ||
| 369 | 368 | // The ecdhDeriveBits function is part of the Web Crypto API and serves both | |
| 370 | 369 | // deriveKeys and deriveBits functions. | |
| 371 | 370 | function ecdhDeriveBits(algorithm, baseKey, length) { | |
@@ -409,27 +408,20 @@ function ecdhDeriveBits(algorithm, baseKey, length) { | |||
| 409 | 408 | return bits; | |
| 410 | 409 | ||
| 411 | 410 | return jobPromiseThen(bits, (bits) => { | |
| 412 | - // If the length is not a multiple of 8 the nearest ceiled | ||
| 413 | - // multiple of 8 is sliced. | ||
| 414 | - const sliceLength = MathCeil(length / 8); | ||
| 411 | + const sliceLength = numBitsToBytes(length); | ||
| 415 | 412 | ||
| 416 | 413 | const { byteLength } = bits; | |
| 417 | 414 | // If the length is larger than the derived secret, throw. | |
| 418 | 415 | if (byteLength < sliceLength) | |
| 419 | 416 | throw lazyDOMException('derived bit length is too small', 'OperationError'); | |
| 420 | 417 | ||
| 421 | - const slice = ArrayBufferPrototypeSlice(bits, 0, sliceLength); | ||
| 422 | - | ||
| 423 | - const mod = length % 8; | ||
| 424 | - if (mod === 0) | ||
| 425 | - return slice; | ||
| 426 | - | ||
| 427 | - // eslint-disable-next-line no-sparse-arrays | ||
| 428 | - masks ||= [, 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b11111100, 0b11111110]; | ||
| 418 | + if (length % 8 === 0) { | ||
| 419 | + if (byteLength === sliceLength) | ||
| 420 | + return bits; | ||
| 421 | + return ArrayBufferPrototypeSlice(bits, 0, sliceLength); | ||
| 422 | + } | ||
| 429 | 423 | ||
| 430 | - const masked = new Uint8Array(slice); | ||
| 431 | - masked[sliceLength - 1] = masked[sliceLength - 1] & masks[mod]; | ||
| 432 | - return TypedArrayPrototypeGetBuffer(masked); | ||
| 424 | + return TypedArrayPrototypeGetBuffer(truncateToBitLength(length, bits)); | ||
| 433 | 425 | }); | |
| 434 | 426 | } | |
| 435 | 427 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,9 +6,11 @@ const { | |||
| 6 | 6 | StringPrototypeReplace, | |
| 7 | 7 | StringPrototypeToLowerCase, | |
| 8 | 8 | Symbol, | |
| 9 | + TypedArrayPrototypeGetBuffer, | ||
| 9 | 10 | } = primordials; | |
| 10 | 11 | ||
| 11 | 12 | const { | |
| 13 | + CShakeJob, | ||
| 12 | 14 | Hash: _Hash, | |
| 13 | 15 | HashJob, | |
| 14 | 16 | Hmac: _Hmac, | |
@@ -21,7 +23,10 @@ const { | |||
| 21 | 23 | const { | |
| 22 | 24 | getStringOption, | |
| 23 | 25 | jobPromise, | |
| 26 | + jobPromiseThen, | ||
| 24 | 27 | normalizeHashName, | |
| 28 | + numBitsToBytes, | ||
| 29 | + truncateToBitLength, | ||
| 25 | 30 | validateMaxBufferLength, | |
| 26 | 31 | kHandle, | |
| 27 | 32 | getCachedHashId, | |
@@ -222,15 +227,41 @@ function asyncDigest(algorithm, data) { | |||
| 222 | 227 | case 'SHA3-384': | |
| 223 | 228 | // Fall through | |
| 224 | 229 | case 'SHA3-512': | |
| 225 | - // Fall through | ||
| 230 | + return jobPromise(() => new HashJob( | ||
| 231 | + kCryptoJobWebCrypto, | ||
| 232 | + normalizeHashName(algorithm.name), | ||
| 233 | + data)); | ||
| 226 | 234 | case 'cSHAKE128': | |
| 227 | 235 | // Fall through | |
| 228 | - case 'cSHAKE256': | ||
| 229 | - return jobPromise(() => new HashJob( | ||
| 236 | + case 'cSHAKE256': { | ||
| 237 | + const outputLength = algorithm.outputLength; | ||
| 238 | + if (algorithm.functionName?.byteLength || | ||
| 239 | + algorithm.customization?.byteLength) { | ||
| 240 | + if (CShakeJob === undefined) { | ||
| 241 | + throw lazyDOMException( | ||
| 242 | + 'Non-empty CShakeParams functionName or customization is not supported', | ||
| 243 | + 'NotSupportedError'); | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + return jobPromise(() => new CShakeJob( | ||
| 247 | + kCryptoJobWebCrypto, | ||
| 248 | + algorithm.name, | ||
| 249 | + data, | ||
| 250 | + algorithm.functionName, | ||
| 251 | + algorithm.customization, | ||
| 252 | + outputLength)); | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + const bits = jobPromise(() => new HashJob( | ||
| 230 | 256 | kCryptoJobWebCrypto, | |
| 231 | 257 | normalizeHashName(algorithm.name), | |
| 232 | 258 | data, | |
| 233 | - algorithm.outputLength)); | ||
| 259 | + numBitsToBytes(outputLength) * 8)); | ||
| 260 | + if (outputLength % 8 === 0) | ||
| 261 | + return bits; | ||
| 262 | + return jobPromiseThen(bits, (bits) => | ||
| 263 | + TypedArrayPrototypeGetBuffer(truncateToBitLength(outputLength, bits))); | ||
| 264 | + } | ||
| 234 | 265 | case 'TurboSHAKE128': | |
| 235 | 266 | // Fall through | |
| 236 | 267 | case 'TurboSHAKE256': | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,6 +18,8 @@ const { | |||
| 18 | 18 | getUsagesMask, | |
| 19 | 19 | jobPromise, | |
| 20 | 20 | normalizeHashName, | |
| 21 | + numBitsToBytes, | ||
| 22 | + truncateToBitLength, | ||
| 21 | 23 | } = require('internal/crypto/util'); | |
| 22 | 24 | ||
| 23 | 25 | const { | |
@@ -40,6 +42,27 @@ const { | |||
| 40 | 42 | ||
| 41 | 43 | const kUsages = ['sign', 'verify']; | |
| 42 | 44 | ||
| 45 | + function normalizeKeyLength(handle, algorithm) { | ||
| 46 | + let length = handle.getSymmetricKeySize() * 8; | ||
| 47 | + if (length === 0 && algorithm.name === 'HMAC') | ||
| 48 | + throw lazyDOMException('Zero-length key is not supported', 'DataError'); | ||
| 49 | + | ||
| 50 | + if (algorithm.length !== undefined) { | ||
| 51 | + const byteLength = numBitsToBytes(algorithm.length); | ||
| 52 | + if (byteLength !== handle.getSymmetricKeySize()) | ||
| 53 | + throw lazyDOMException('Invalid key length', 'DataError'); | ||
| 54 | + | ||
| 55 | + if (algorithm.length % 8 !== 0) { | ||
| 56 | + handle = importSecretKey( | ||
| 57 | + truncateToBitLength(algorithm.length, handle.export())); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + length = algorithm.length; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + return { handle, length }; | ||
| 64 | + } | ||
| 65 | + | ||
| 43 | 66 | function hmacGenerateKey(algorithm, extractable, usages) { | |
| 44 | 67 | const { | |
| 45 | 68 | hash, | |
@@ -93,7 +116,6 @@ function macImportKey( | |||
| 93 | 116 | let length; | |
| 94 | 117 | switch (format) { | |
| 95 | 118 | case 'KeyObjectHandle': { | |
| 96 | - length = keyData.getSymmetricKeySize() * 8; | ||
| 97 | 119 | handle = keyData; | |
| 98 | 120 | break; | |
| 99 | 121 | } | |
@@ -102,7 +124,6 @@ function macImportKey( | |||
| 102 | 124 | if (format === 'raw' && !isHmac) { | |
| 103 | 125 | return undefined; | |
| 104 | 126 | } | |
| 105 | - length = keyData.byteLength * 8; | ||
| 106 | 127 | handle = importSecretKey(keyData); | |
| 107 | 128 | break; | |
| 108 | 129 | } | |
@@ -120,20 +141,13 @@ function macImportKey( | |||
| 120 | 141 | } | |
| 121 | 142 | ||
| 122 | 143 | handle = importJwkSecretKey(keyData); | |
| 123 | - length = handle.getSymmetricKeySize() * 8; | ||
| 124 | 144 | break; | |
| 125 | 145 | } | |
| 126 | 146 | default: | |
| 127 | 147 | return undefined; | |
| 128 | 148 | } | |
| 129 | 149 | ||
| 130 | - if (length === 0) | ||
| 131 | - throw lazyDOMException('Zero-length key is not supported', 'DataError'); | ||
| 132 | - | ||
| 133 | - if (algorithm.length !== undefined && | ||
| 134 | - algorithm.length !== length) { | ||
| 135 | - throw lazyDOMException('Invalid key length', 'DataError'); | ||
| 136 | - } | ||
| 150 | + ({ handle, length } = normalizeKeyLength(handle, algorithm)); // eslint-disable-line prefer-const | ||
| 137 | 151 | ||
| 138 | 152 | const algorithmObject = { | |
| 139 | 153 | name: algorithm.name, | |
@@ -170,7 +184,8 @@ function kmacSignVerify(key, data, algorithm, signature) { | |||
| 170 | 184 | getCryptoKeyHandle(key), | |
| 171 | 185 | algorithm.name, | |
| 172 | 186 | algorithm.customization, | |
| 173 | - algorithm.outputLength / 8, | ||
| 187 | + getCryptoKeyAlgorithm(key).length, | ||
| 188 | + algorithm.outputLength, | ||
| 174 | 189 | data, | |
| 175 | 190 | signature)); | |
| 176 | 191 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,6 +9,7 @@ const { | |||
| 9 | 9 | DataViewPrototypeGetBuffer, | |
| 10 | 10 | DataViewPrototypeGetByteLength, | |
| 11 | 11 | DataViewPrototypeGetByteOffset, | |
| 12 | + MathFloor, | ||
| 12 | 13 | Number, | |
| 13 | 14 | ObjectDefineProperty, | |
| 14 | 15 | ObjectEntries, | |
@@ -549,6 +550,46 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) { | |||
| 549 | 550 | } | |
| 550 | 551 | } | |
| 551 | 552 | ||
| 553 | + /** | ||
| 554 | + * Converts a bit length to the number of bytes needed to contain it. | ||
| 555 | + * Non-byte lengths are rounded up to the next byte. | ||
| 556 | + * @param {number} length | ||
| 557 | + * @returns {number} | ||
| 558 | + */ | ||
| 559 | + function numBitsToBytes(length) { | ||
| 560 | + return MathFloor(length / 8) + MathFloor((7 + (length % 8)) / 8); | ||
| 561 | + } | ||
| 562 | + | ||
| 563 | + /** | ||
| 564 | + * Copies `bytes` up to the byte length needed for `length` bits, then clears | ||
| 565 | + * unused least-significant bits in the final byte. | ||
| 566 | + * @param {number} length | ||
| 567 | + * @param {ArrayBuffer|ArrayBufferView} bytes | ||
| 568 | + * @returns {Uint8Array} | ||
| 569 | + */ | ||
| 570 | + function truncateToBitLength(length, bytes) { | ||
| 571 | + const lengthBytes = numBitsToBytes(length); | ||
| 572 | + const isView = ArrayBufferIsView(bytes); | ||
| 573 | + const byteView = isView ? | ||
| 574 | + new Uint8Array( | ||
| 575 | + getDataViewOrTypedArrayBuffer(bytes), | ||
| 576 | + getDataViewOrTypedArrayByteOffset(bytes), | ||
| 577 | + getDataViewOrTypedArrayByteLength(bytes), | ||
| 578 | + ) : | ||
| 579 | + new Uint8Array(bytes, 0, ArrayBufferPrototypeGetByteLength(bytes)); | ||
| 580 | + const result = TypedArrayPrototypeSlice( | ||
| 581 | + byteView, | ||
| 582 | + 0, | ||
| 583 | + lengthBytes, | ||
| 584 | + ); | ||
| 585 | + | ||
| 586 | + const remainder = length % 8; | ||
| 587 | + if (remainder !== 0) | ||
| 588 | + result[lengthBytes - 1] &= (0xff << (8 - remainder)) & 0xff; | ||
| 589 | + | ||
| 590 | + return result; | ||
| 591 | + } | ||
| 592 | + | ||
| 552 | 593 | let webidl; | |
| 553 | 594 | ||
| 554 | 595 | // Keep this as a regular object. The WebIDL converters read and spread these | |
@@ -608,12 +649,19 @@ function normalizeAlgorithm(algorithm, op) { | |||
| 608 | 649 | // 3. | |
| 609 | 650 | if (idlType === 'BufferSource' && idlValue) { | |
| 610 | 651 | const isView = ArrayBufferIsView(idlValue); | |
| 611 | - normalizedAlgorithm[member] = TypedArrayPrototypeSlice( | ||
| 652 | + const idlValueBytes = isView ? | ||
| 612 | 653 | new Uint8Array( | |
| 613 | - isView ? getDataViewOrTypedArrayBuffer(idlValue) : idlValue, | ||
| 614 | - isView ? getDataViewOrTypedArrayByteOffset(idlValue) : 0, | ||
| 615 | - isView ? getDataViewOrTypedArrayByteLength(idlValue) : ArrayBufferPrototypeGetByteLength(idlValue), | ||
| 616 | - ), | ||
| 654 | + getDataViewOrTypedArrayBuffer(idlValue), | ||
| 655 | + getDataViewOrTypedArrayByteOffset(idlValue), | ||
| 656 | + getDataViewOrTypedArrayByteLength(idlValue), | ||
| 657 | + ) : | ||
| 658 | + new Uint8Array( | ||
| 659 | + idlValue, | ||
| 660 | + 0, | ||
| 661 | + ArrayBufferPrototypeGetByteLength(idlValue), | ||
| 662 | + ); | ||
| 663 | + normalizedAlgorithm[member] = TypedArrayPrototypeSlice( | ||
| 664 | + idlValueBytes, | ||
| 617 | 665 | ); | |
| 618 | 666 | } else if (idlType === 'HashAlgorithmIdentifier') { | |
| 619 | 667 | normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, 'digest'); | |
@@ -1003,6 +1051,8 @@ module.exports = { | |||
| 1003 | 1051 | cleanupWebCryptoResult, | |
| 1004 | 1052 | prepareWebCryptoResult, | |
| 1005 | 1053 | validateMaxBufferLength, | |
| 1054 | + numBitsToBytes, | ||
| 1055 | + truncateToBitLength, | ||
| 1006 | 1056 | bigIntArrayToUnsignedBigInt, | |
| 1007 | 1057 | bigIntArrayToUnsignedInt, | |
| 1008 | 1058 | getBlockSize, | |
| Back | FazBrowse Home | New Git URL |
0 commit comments