| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent fbec4eb commit 501f816
16 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,7 @@ const { | |||
| 22 | 22 | const { kMaxLength } = require('buffer'); | |
| 23 | 23 | ||
| 24 | 24 | const { | |
| 25 | + getDigestSizeInBytes, | ||
| 25 | 26 | jobPromise, | |
| 26 | 27 | normalizeHashName, | |
| 27 | 28 | toBuf, | |
@@ -141,19 +142,24 @@ function hkdfSync(hash, key, salt, info, length) { | |||
| 141 | 142 | return bits; | |
| 142 | 143 | } | |
| 143 | 144 | ||
| 144 | - function validateHkdfDeriveBitsLength(length) { | ||
| 145 | + function validateHkdfDeriveBitsLength(length, hash) { | ||
| 145 | 146 | if (length === null) | |
| 146 | 147 | throw lazyDOMException('length cannot be null', 'OperationError'); | |
| 147 | 148 | if (length % 8) { | |
| 148 | 149 | throw lazyDOMException( | |
| 149 | 150 | 'length must be a multiple of 8', | |
| 150 | 151 | 'OperationError'); | |
| 151 | 152 | } | |
| 153 | + if (length > 255 * getDigestSizeInBytes(hash.name) * 8) { | ||
| 154 | + throw lazyDOMException( | ||
| 155 | + 'length exceeds the maximum derived bit length', | ||
| 156 | + 'OperationError'); | ||
| 157 | + } | ||
| 152 | 158 | } | |
| 153 | 159 | ||
| 154 | 160 | function hkdfDeriveBits(algorithm, baseKey, length) { | |
| 155 | - validateHkdfDeriveBitsLength(length); | ||
| 156 | 161 | const { hash, salt, info } = algorithm; | |
| 162 | + validateHkdfDeriveBitsLength(length, hash); | ||
| 157 | 163 | ||
| 158 | 164 | if (length === 0) | |
| 159 | 165 | return PromiseResolve(new ArrayBuffer(0)); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -103,12 +103,6 @@ function rsaKeyGenerate( | |||
| 103 | 103 | extractable, | |
| 104 | 104 | usages, | |
| 105 | 105 | ) { | |
| 106 | - const publicExponentConverted = bigIntArrayToUnsignedInt(algorithm.publicExponent); | ||
| 107 | - if (publicExponentConverted === undefined) { | ||
| 108 | - throw lazyDOMException( | ||
| 109 | - 'The publicExponent must be equivalent to an unsigned 32-bit value', | ||
| 110 | - 'OperationError'); | ||
| 111 | - } | ||
| 112 | 106 | const { | |
| 113 | 107 | name, | |
| 114 | 108 | modulusLength, | |
@@ -118,6 +112,7 @@ function rsaKeyGenerate( | |||
| 118 | 112 | ||
| 119 | 113 | const allowedUsages = kUsages[name]; | |
| 120 | 114 | const usagesSet = validateKeyUsages(usages, allowedUsages.keygen, name); | |
| 115 | + const publicExponentConverted = bigIntArrayToUnsignedInt(publicExponent); | ||
| 121 | 116 | ||
| 122 | 117 | const keyAlgorithm = { | |
| 123 | 118 | name, | |
@@ -126,12 +121,6 @@ function rsaKeyGenerate( | |||
| 126 | 121 | hash, | |
| 127 | 122 | }; | |
| 128 | 123 | ||
| 129 | - if (publicExponentConverted < 3 || publicExponentConverted % 2 === 0) { | ||
| 130 | - throw lazyDOMException( | ||
| 131 | - 'The operation failed for an operation-specific reason', | ||
| 132 | - 'OperationError'); | ||
| 133 | - } | ||
| 134 | - | ||
| 135 | 124 | const keyUsages = getKeyPairUsages(usagesSet, allowedUsages); | |
| 136 | 125 | validateUsagesNotEmpty(keyUsages.private); | |
| 137 | 126 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -341,20 +341,23 @@ const kAlgorithmDefinitions = { | |||
| 341 | 341 | 'importKey': null, | |
| 342 | 342 | 'encapsulate': null, | |
| 343 | 343 | 'decapsulate': null, | |
| 344 | + 'get shared key length': null, | ||
| 344 | 345 | }, | |
| 345 | 346 | 'ML-KEM-768': { | |
| 346 | 347 | 'generateKey': null, | |
| 347 | 348 | 'exportKey': null, | |
| 348 | 349 | 'importKey': null, | |
| 349 | 350 | 'encapsulate': null, | |
| 350 | 351 | 'decapsulate': null, | |
| 352 | + 'get shared key length': null, | ||
| 351 | 353 | }, | |
| 352 | 354 | 'ML-KEM-1024': { | |
| 353 | 355 | 'generateKey': null, | |
| 354 | 356 | 'exportKey': null, | |
| 355 | 357 | 'importKey': null, | |
| 356 | 358 | 'encapsulate': null, | |
| 357 | 359 | 'decapsulate': null, | |
| 360 | + 'get shared key length': null, | ||
| 358 | 361 | }, | |
| 359 | 362 | 'PBKDF2': { | |
| 360 | 363 | 'importKey': null, | |
@@ -886,15 +889,18 @@ function jobPromiseThen(promise, onFulfilled, onRejected) { | |||
| 886 | 889 | // an unsigned int from a Buffer are not adequate. The implementation | |
| 887 | 890 | // here is adapted from the chromium implementation here: | |
| 888 | 891 | // https://github.com/chromium/chromium/blob/HEAD/third_party/blink/public/platform/web_crypto_algorithm_params.h, but ported to JavaScript | |
| 889 | - // Returns undefined if the conversion was unsuccessful. | ||
| 892 | + // Throws an OperationError if the value does not fit in an unsigned 32-bit integer. | ||
| 890 | 893 | function bigIntArrayToUnsignedInt(input) { | |
| 891 | 894 | let result = 0; | |
| 892 | 895 | const length = TypedArrayPrototypeGetLength(input); | |
| 893 | 896 | ||
| 894 | 897 | for (let n = 0; n < length; ++n) { | |
| 895 | 898 | const n_reversed = length - n - 1; | |
| 896 | - if (n_reversed >= 4 && input[n]) | ||
| 897 | - return; // Too large | ||
| 899 | + if (n_reversed >= 4 && input[n]) { | ||
| 900 | + throw lazyDOMException( | ||
| 901 | + 'algorithm.publicExponent must fit in an unsigned 32-bit integer', | ||
| 902 | + 'OperationError'); | ||
| 903 | + } | ||
| 898 | 904 | result |= input[n] << 8 * n_reversed; | |
| 899 | 905 | } | |
| 900 | 906 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -348,6 +348,54 @@ function getKeyLength({ name, length, hash }) { | |||
| 348 | 348 | } | |
| 349 | 349 | } | |
| 350 | 350 | ||
| 351 | + function getSharedKeyLength({ name }) { | ||
| 352 | + switch (name) { | ||
| 353 | + case 'ML-KEM-512': | ||
| 354 | + // Fall through | ||
| 355 | + case 'ML-KEM-768': | ||
| 356 | + // Fall through | ||
| 357 | + case 'ML-KEM-1024': | ||
| 358 | + return 256; | ||
| 359 | + /* c8 ignore start */ | ||
| 360 | + default: { | ||
| 361 | + const assert = require('internal/assert'); | ||
| 362 | + assert.fail('Unreachable code'); | ||
| 363 | + } | ||
| 364 | + /* c8 ignore stop */ | ||
| 365 | + } | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + function canImportRawSecret(algorithm, sharedKeyLength) { | ||
| 369 | + switch (algorithm.name) { | ||
| 370 | + case 'AES-OCB': | ||
| 371 | + case 'AES-KW': | ||
| 372 | + case 'AES-GCM': | ||
| 373 | + case 'AES-CTR': | ||
| 374 | + case 'AES-CBC': | ||
| 375 | + return sharedKeyLength === 128 || | ||
| 376 | + sharedKeyLength === 192 || | ||
| 377 | + sharedKeyLength === 256; | ||
| 378 | + case 'ChaCha20-Poly1305': | ||
| 379 | + return sharedKeyLength === 256; | ||
| 380 | + case 'HKDF': | ||
| 381 | + case 'PBKDF2': | ||
| 382 | + case 'Argon2i': | ||
| 383 | + case 'Argon2d': | ||
| 384 | + case 'Argon2id': | ||
| 385 | + return true; | ||
| 386 | + case 'HMAC': | ||
| 387 | + if (sharedKeyLength === 0) | ||
| 388 | + return false; | ||
| 389 | + // Fall through | ||
| 390 | + case 'KMAC128': | ||
| 391 | + case 'KMAC256': | ||
| 392 | + return algorithm.length === undefined || | ||
| 393 | + numBitsToBytes(algorithm.length) * 8 === sharedKeyLength; | ||
| 394 | + default: | ||
| 395 | + return false; | ||
| 396 | + } | ||
| 397 | + } | ||
| 398 | + | ||
| 351 | 399 | function deriveKey( | |
| 352 | 400 | algorithm, | |
| 353 | 401 | baseKey, | |
@@ -1741,37 +1789,19 @@ class SubtleCrypto { | |||
| 1741 | 1789 | }, | |
| 1742 | 1790 | ); | |
| 1743 | 1791 | ||
| 1792 | + let sharedKeyLength; | ||
| 1744 | 1793 | let normalizedAdditionalAlgorithm; | |
| 1745 | 1794 | try { | |
| 1795 | + const normalizedAlgorithm = | ||
| 1796 | + normalizeAlgorithm(algorithm, 'get shared key length'); | ||
| 1797 | + sharedKeyLength = getSharedKeyLength(normalizedAlgorithm); | ||
| 1746 | 1798 | normalizedAdditionalAlgorithm = normalizeAlgorithm(additionalAlgorithm, 'importKey'); | |
| 1747 | 1799 | } catch { | |
| 1748 | 1800 | return false; | |
| 1749 | 1801 | } | |
| 1750 | 1802 | ||
| 1751 | - switch (normalizedAdditionalAlgorithm.name) { | ||
| 1752 | - case 'AES-OCB': | ||
| 1753 | - case 'AES-KW': | ||
| 1754 | - case 'AES-GCM': | ||
| 1755 | - case 'AES-CTR': | ||
| 1756 | - case 'AES-CBC': | ||
| 1757 | - case 'ChaCha20-Poly1305': | ||
| 1758 | - case 'HKDF': | ||
| 1759 | - case 'PBKDF2': | ||
| 1760 | - case 'Argon2i': | ||
| 1761 | - case 'Argon2d': | ||
| 1762 | - case 'Argon2id': | ||
| 1763 | - break; | ||
| 1764 | - case 'HMAC': | ||
| 1765 | - case 'KMAC128': | ||
| 1766 | - case 'KMAC256': | ||
| 1767 | - if (normalizedAdditionalAlgorithm.length === undefined || | ||
| 1768 | - numBitsToBytes(normalizedAdditionalAlgorithm.length) === 32) { | ||
| 1769 | - break; | ||
| 1770 | - } | ||
| 1771 | - return false; | ||
| 1772 | - default: | ||
| 1773 | - return false; | ||
| 1774 | - } | ||
| 1803 | + if (!canImportRawSecret(normalizedAdditionalAlgorithm, sharedKeyLength)) | ||
| 1804 | + return false; | ||
| 1775 | 1805 | } | |
| 1776 | 1806 | ||
| 1777 | 1807 | try { | |
@@ -1807,8 +1837,6 @@ function check(op, alg, length) { | |||
| 1807 | 1837 | } | |
| 1808 | 1838 | ||
| 1809 | 1839 | switch (op) { | |
| 1810 | - case 'decapsulate': | ||
| 1811 | - case 'decrypt': | ||
| 1812 | 1840 | case 'digest': { | |
| 1813 | 1841 | if ((normalizedAlgorithm.name === 'cSHAKE128' || | |
| 1814 | 1842 | normalizedAlgorithm.name === 'cSHAKE256') && | |
@@ -1818,6 +1846,8 @@ function check(op, alg, length) { | |||
| 1818 | 1846 | } | |
| 1819 | 1847 | return true; | |
| 1820 | 1848 | } | |
| 1849 | + case 'decapsulate': | ||
| 1850 | + case 'decrypt': | ||
| 1821 | 1851 | case 'encapsulate': | |
| 1822 | 1852 | case 'encrypt': | |
| 1823 | 1853 | case 'exportKey': | |
@@ -1829,7 +1859,8 @@ function check(op, alg, length) { | |||
| 1829 | 1859 | return true; | |
| 1830 | 1860 | case 'deriveBits': { | |
| 1831 | 1861 | if (normalizedAlgorithm.name === 'HKDF') { | |
| 1832 | - require('internal/crypto/hkdf').validateHkdfDeriveBitsLength(length); | ||
| 1862 | + require('internal/crypto/hkdf') | ||
| 1863 | + .validateHkdfDeriveBitsLength(length, normalizedAlgorithm.hash); | ||
| 1833 | 1864 | } | |
| 1834 | 1865 | ||
| 1835 | 1866 | if (normalizedAlgorithm.name === 'PBKDF2') { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,6 +24,7 @@ const { | |||
| 24 | 24 | getCryptoKeyType, | |
| 25 | 25 | } = require('internal/crypto/keys'); | |
| 26 | 26 | const { | |
| 27 | + bigIntArrayToUnsignedInt, | ||
| 27 | 28 | validateMaxBufferLength, | |
| 28 | 29 | getBufferSourceByteLength, | |
| 29 | 30 | getBufferSourceBytes, | |
@@ -42,6 +43,8 @@ const { | |||
| 42 | 43 | type, | |
| 43 | 44 | } = require('internal/webidl'); | |
| 44 | 45 | ||
| 46 | + const kRsaKeyGenMinimumModulusLength = isFips ? 2048 : 512; | ||
| 47 | + | ||
| 45 | 48 | function validateByteLength(buf, name, target) { | |
| 46 | 49 | if (getBufferSourceByteLength(buf) !== target) { | |
| 47 | 50 | throw lazyDOMException( | |
@@ -176,11 +179,33 @@ const dictRsaKeyGenParams = [ | |||
| 176 | 179 | key: 'modulusLength', | |
| 177 | 180 | converter: (V, opts) => | |
| 178 | 181 | converters['unsigned long'](V, enforceRangeOptions(opts)), | |
| 182 | + validator: (modulusLength) => { | ||
| 183 | + if (modulusLength < kRsaKeyGenMinimumModulusLength) { | ||
| 184 | + throw lazyDOMException( | ||
| 185 | + `algorithm.modulusLength must be at least ${kRsaKeyGenMinimumModulusLength}`, | ||
| 186 | + 'OperationError'); | ||
| 187 | + } | ||
| 188 | + }, | ||
| 179 | 189 | required: true, | |
| 180 | 190 | }, | |
| 181 | 191 | { | |
| 182 | 192 | key: 'publicExponent', | |
| 183 | 193 | converter: converters.BigInteger, | |
| 194 | + validator: (publicExponent) => { | ||
| 195 | + const converted = bigIntArrayToUnsignedInt(publicExponent); | ||
| 196 | + | ||
| 197 | + if (converted < 3) { | ||
| 198 | + throw lazyDOMException( | ||
| 199 | + 'algorithm.publicExponent must be at least 3', | ||
| 200 | + 'OperationError'); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + if (converted % 2 === 0) { | ||
| 204 | + throw lazyDOMException( | ||
| 205 | + 'algorithm.publicExponent must be odd', | ||
| 206 | + 'OperationError'); | ||
| 207 | + } | ||
| 208 | + }, | ||
| 184 | 209 | required: true, | |
| 185 | 210 | }, | |
| 186 | 211 | ]; | |
@@ -649,20 +674,27 @@ converters.ContextParams = createDictionaryConverter( | |||
| 649 | 674 | key: 'context', | |
| 650 | 675 | converter: converters.BufferSource, | |
| 651 | 676 | validator(V, dict) { | |
| 677 | + const validateLength = (V) => | ||
| 678 | + validateMaxBufferLength(V, 'ContextParams.context', 255); | ||
| 679 | + | ||
| 652 | 680 | if (process.features.openssl_is_boringssl) { | |
| 653 | - this.validator = undefined; | ||
| 681 | + this.validator = validateLength; | ||
| 654 | 682 | } else { | |
| 655 | 683 | let { 0: major, 1: minor } = | |
| 656 | 684 | StringPrototypeSplit(process.versions.openssl, '.'); | |
| 657 | 685 | major = NumberParseInt(major, 10); | |
| 658 | 686 | minor = NumberParseInt(minor, 10); | |
| 659 | 687 | if (major > 3 || (major === 3 && minor >= 2)) { | |
| 660 | - this.validator = undefined; | ||
| 688 | + this.validator = validateLength; | ||
| 661 | 689 | } else { | |
| 662 | - this.validator = validateZeroLength('ContextParams.context'); | ||
| 663 | - this.validator(V, dict); | ||
| 690 | + const validateEmpty = validateZeroLength('ContextParams.context'); | ||
| 691 | + this.validator = (V, dict) => { | ||
| 692 | + validateLength(V); | ||
| 693 | + validateEmpty(V, dict); | ||
| 694 | + }; | ||
| 664 | 695 | } | |
| 665 | 696 | } | |
| 697 | + this.validator(V, dict); | ||
| 666 | 698 | }, | |
| 667 | 699 | }, | |
| 668 | 700 | ], | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,7 @@ | |||
| 1 | + import { getFips } from 'node:crypto'; | ||
| 2 | + | ||
| 1 | 3 | const { subtle } = globalThis.crypto; | |
| 4 | + const RSA_MINIMUM_MODULUS_LENGTH = getFips() === 1 ? 2048 : 512; | ||
| 2 | 5 | ||
| 3 | 6 | const RSA_KEY_GEN = { | |
| 4 | 7 | modulusLength: 2048, | |
@@ -66,6 +69,30 @@ export const vectors = { | |||
| 66 | 69 | [true, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256', ...RSA_KEY_GEN }], | |
| 67 | 70 | [true, { name: 'RSA-PSS', hash: 'SHA-256', ...RSA_KEY_GEN }], | |
| 68 | 71 | [true, { name: 'RSA-OAEP', hash: 'SHA-256', ...RSA_KEY_GEN }], | |
| 72 | + [true, { | ||
| 73 | + name: 'RSA-PSS', | ||
| 74 | + hash: 'SHA-256', | ||
| 75 | + modulusLength: RSA_MINIMUM_MODULUS_LENGTH, | ||
| 76 | + publicExponent: new Uint8Array([1, 0, 1]), | ||
| 77 | + }], | ||
| 78 | + [false, { | ||
| 79 | + name: 'RSASSA-PKCS1-v1_5', | ||
| 80 | + hash: 'SHA-256', | ||
| 81 | + modulusLength: RSA_MINIMUM_MODULUS_LENGTH - 1, | ||
| 82 | + publicExponent: new Uint8Array([1, 0, 1]), | ||
| 83 | + }], | ||
| 84 | + [false, { | ||
| 85 | + name: 'RSA-PSS', | ||
| 86 | + hash: 'SHA-256', | ||
| 87 | + ...RSA_KEY_GEN, | ||
| 88 | + publicExponent: new Uint8Array([2]), | ||
| 89 | + }], | ||
| 90 | + [false, { | ||
| 91 | + name: 'RSA-OAEP', | ||
| 92 | + hash: 'SHA-256', | ||
| 93 | + ...RSA_KEY_GEN, | ||
| 94 | + publicExponent: new Uint8Array([1, 0, 0, 0, 1]), | ||
| 95 | + }], | ||
| 69 | 96 | [true, { name: 'ECDSA', namedCurve: 'P-256' }], | |
| 70 | 97 | [false, { name: 'ECDSA', namedCurve: 'X25519' }], | |
| 71 | 98 | [true, { name: 'AES-CTR', length: 128 }], | |
@@ -146,6 +173,8 @@ export const vectors = { | |||
| 146 | 173 | 'deriveBits': [ | |
| 147 | 174 | [true, { name: 'HKDF', hash: 'SHA-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 8], | |
| 148 | 175 | [true, { name: 'HKDF', hash: 'SHA-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 0], | |
| 176 | + [true, { name: 'HKDF', hash: 'SHA-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 65280], | ||
| 177 | + [false, { name: 'HKDF', hash: 'SHA-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 65288], | ||
| 149 | 178 | [false, { name: 'HKDF', hash: 'SHA-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, null], | |
| 150 | 179 | [false, { name: 'HKDF', hash: 'SHA-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 7], | |
| 151 | 180 | [false, { name: 'HKDF', hash: 'Invalid', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 8], | |
@@ -234,4 +263,7 @@ export const vectors = { | |||
| 234 | 263 | 'get key length': [ | |
| 235 | 264 | [false, { name: 'HMAC', hash: 'SHA-256' }], | |
| 236 | 265 | ], | |
| 266 | + 'get shared key length': [ | ||
| 267 | + [false, 'ML-KEM-768'], | ||
| 268 | + ], | ||
| 237 | 269 | }; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments