diff options
Diffstat (limited to 'ext/crypto/00_crypto.js')
-rw-r--r-- | ext/crypto/00_crypto.js | 446 |
1 files changed, 247 insertions, 199 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index c23fd5a36..311dcfbf1 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -142,6 +142,29 @@ }, }; + const aesJwkAlg = { + "AES-CTR": { + 128: "A128CTR", + 192: "A192CTR", + 256: "A256CTR", + }, + "AES-CBC": { + 128: "A128CBC", + 192: "A192CBC", + 256: "A256CBC", + }, + "AES-GCM": { + 128: "A128GCM", + 192: "A192GCM", + 256: "A256GCM", + }, + "AES-KW": { + 128: "A128KW", + 192: "A192KW", + 256: "A256KW", + }, + }; + // Decodes the unpadded base64 to the octet sequence containing key value `k` defined in RFC7518 Section 6.4 function decodeSymmetricKey(key) { // Decode from base64url without `=` padding. @@ -1342,44 +1365,13 @@ throw new DOMException("Invalid key usages", "SyntaxError"); } - // 2. - switch (format) { - case "raw": { - // 2. - if ( - !ArrayPrototypeIncludes([128, 192, 256], keyData.byteLength * 8) - ) { - throw new DOMException("Invalid key length", "Datarror"); - } - - break; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "raw", - data: keyData, - }); - - // 4-7. - const algorithm = { - name: "AES-CTR", - length: keyData.byteLength * 8, - }; - - const key = constructKey( - "secret", - false, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, + return importKeyAES( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, ); - - // 8. - return key; } case "AES-CBC": { // 1. @@ -1397,45 +1389,13 @@ ) { throw new DOMException("Invalid key usages", "SyntaxError"); } - - // 2. - switch (format) { - case "raw": { - // 2. - if ( - !ArrayPrototypeIncludes([128, 192, 256], keyData.byteLength * 8) - ) { - throw new DOMException("Invalid key length", "Datarror"); - } - - break; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "raw", - data: keyData, - }); - - // 4-7. - const algorithm = { - name: "AES-CBC", - length: keyData.byteLength * 8, - }; - - const key = constructKey( - "secret", - false, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, + return importKeyAES( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, ); - - // 8. - return key; } case "AES-GCM": { // 1. @@ -1454,44 +1414,13 @@ throw new DOMException("Invalid key usages", "SyntaxError"); } - // 2. - switch (format) { - case "raw": { - // 2. - if ( - !ArrayPrototypeIncludes([128, 192, 256], keyData.byteLength * 8) - ) { - throw new DOMException("Invalid key length", "Datarror"); - } - - break; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "raw", - data: keyData, - }); - - // 4-7. - const algorithm = { - name: "AES-GCM", - length: keyData.byteLength * 8, - }; - - const key = constructKey( - "secret", - false, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, + return importKeyAES( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, ); - - // 8. - return key; } case "AES-KW": { // 1. @@ -1508,44 +1437,13 @@ throw new DOMException("Invalid key usages", "SyntaxError"); } - // 2. - switch (format) { - case "raw": { - // 2. - if ( - !ArrayPrototypeIncludes([128, 192, 256], keyData.byteLength * 8) - ) { - throw new DOMException("Invalid key length", "Datarror"); - } - - break; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - - const handle = {}; - WeakMapPrototypeSet(KEY_STORE, handle, { - type: "raw", - data: keyData, - }); - - // 4-7. - const algorithm = { - name: "AES-KW", - length: keyData.byteLength * 8, - }; - - const key = constructKey( - "secret", - false, - usageIntersection(keyUsages, recognisedUsages), - algorithm, - handle, + return importKeyAES( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, ); - - // 8. - return key; } default: throw new DOMException("Not implemented", "NotSupportedError"); @@ -1786,57 +1684,11 @@ throw new DOMException("Not implemented", "NotSupportedError"); } } - case "AES-CTR": { - switch (format) { - // 2. - case "raw": { - // 1. - const data = innerKey.data; - // 2. - return data.buffer; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - } - case "AES-CBC": { - switch (format) { - // 2. - case "raw": { - // 1. - const data = innerKey.data; - // 2. - return data.buffer; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - } - case "AES-GCM": { - switch (format) { - // 2. - case "raw": { - // 1. - const data = innerKey.data; - // 2. - return data.buffer; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } - } + case "AES-CTR": + case "AES-CBC": + case "AES-GCM": case "AES-KW": { - switch (format) { - // 2. - case "raw": { - // 1. - const data = innerKey.data; - // 2. - return data.buffer; - } - default: - throw new DOMException("Not implemented", "NotSupportedError"); - } + return exportKeyAES(format, key, innerKey); } // TODO(@littledivy): ECDSA default: @@ -2591,6 +2443,202 @@ } } + function exportKeyAES( + format, + key, + innerKey, + ) { + switch (format) { + // 2. + case "raw": { + // 1. + const data = innerKey.data; + // 2. + return data.buffer; + } + case "jwk": { + // 1-3. + const jwk = { + kty: "oct", + k: unpaddedBase64(innerKey.data), + }; + + // 4. + const algorithm = key[_algorithm]; + switch (algorithm.length) { + case 128: + jwk.alg = aesJwkAlg[algorithm.name][128]; + break; + case 192: + jwk.alg = aesJwkAlg[algorithm.name][192]; + break; + case 256: + jwk.alg = aesJwkAlg[algorithm.name][256]; + break; + default: + throw new DOMException( + "Invalid key length", + "NotSupportedError", + ); + } + + // 5. + jwk.key_ops = key[_usages]; + // 6. + jwk.ext = key[_extractable]; + // 7. + return jwk; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + + function importKeyAES( + format, + normalizedAlgorithm, + keyData, + extractable, + keyUsages, + ) { + // 2. + let data = keyData; + switch (format) { + case "raw": { + // 2. + if ( + !ArrayPrototypeIncludes([128, 192, 256], keyData.byteLength * 8) + ) { + throw new DOMException("Invalid key length", "Datarror"); + } + + break; + } + case "jwk": { + // 1. + const jwk = keyData; + // 2. + if (jwk.kty !== "oct") { + throw new DOMException( + "`kty` member of JsonWebKey must be `oct`", + "DataError", + ); + } + + // Section 6.4.1 of RFC7518 + if (jwk.k === undefined) { + throw new DOMException( + "`k` member of JsonWebKey must be present", + "DataError", + ); + } + + // 4. + data = decodeSymmetricKey(jwk.k); + + // 5. + switch (data.byteLength * 8) { + case 128: + if ( + jwk.alg !== undefined && + jwk.alg !== aesJwkAlg[normalizedAlgorithm.name][128] + ) { + throw new DOMException("Invalid algorithm", "DataError"); + } + break; + case 192: + if ( + jwk.alg !== undefined && + jwk.alg !== aesJwkAlg[normalizedAlgorithm.name][192] + ) { + throw new DOMException("Invalid algorithm", "DataError"); + } + break; + case 256: + if ( + jwk.alg !== undefined && + jwk.alg !== aesJwkAlg[normalizedAlgorithm.name][256] + ) { + throw new DOMException("Invalid algorithm", "DataError"); + } + break; + default: + throw new DOMException( + "Invalid key length", + "DataError", + ); + } + + // 6. + if (keyUsages.length > 0 && jwk.use && jwk.use !== "enc") { + throw new DOMException("Invalid key usages", "DataError"); + } + + // 7. + // Section 4.3 of RFC7517 + if (jwk.key_ops) { + if ( + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined + ) { + throw new DOMException( + "`key_ops` member of JsonWebKey is invalid", + "DataError", + ); + } + + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "`key_ops` member of JsonWebKey is invalid", + "DataError", + ); + } + } + + // 8. + if (jwk.ext === false && extractable == true) { + throw new DOMException( + "`ext` member of JsonWebKey is invalid", + "DataError", + ); + } + + break; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, { + type: "raw", + data, + }); + + // 4-7. + const algorithm = { + name: normalizedAlgorithm.name, + length: data.byteLength * 8, + }; + + const key = constructKey( + "secret", + extractable, + usageIntersection(keyUsages, recognisedUsages), + algorithm, + handle, + ); + + // 8. + return key; + } async function generateKeyAES(normalizedAlgorithm, extractable, usages) { // 2. if (!ArrayPrototypeIncludes([128, 192, 256], normalizedAlgorithm.length)) { |