| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2134,6 +2134,24 @@ added: v11.6.0 | |||
| 2134 | 2134 | For secret keys, this property represents the size of the key in bytes. This | |
| 2135 | 2135 | property is `undefined` for asymmetric keys. | |
| 2136 | 2136 | ||
| 2137 | + ### `keyObject.toCryptoKey(algorithm, extractable, keyUsages)` | ||
| 2138 | + | ||
| 2139 | + <!-- YAML | ||
| 2140 | + added: REPLACEME | ||
| 2141 | + --> | ||
| 2142 | + | ||
| 2143 | + <!--lint disable maximum-line-length remark-lint--> | ||
| 2144 | + | ||
| 2145 | + * `algorithm`: {AlgorithmIdentifier|RsaHashedImportParams|EcKeyImportParams|HmacImportParams} | ||
| 2146 | + | ||
| 2147 | + <!--lint enable maximum-line-length remark-lint--> | ||
| 2148 | + | ||
| 2149 | + * `extractable`: {boolean} | ||
| 2150 | + * `keyUsages`: {string\[]} See [Key usages][]. | ||
| 2151 | + * Returns: {CryptoKey} | ||
| 2152 | + | ||
| 2153 | + Converts a `KeyObject` instance to a `CryptoKey`. | ||
| 2154 | + | ||
| 2137 | 2155 | ### `keyObject.type` | |
| 2138 | 2156 | ||
| 2139 | 2157 | <!-- YAML | |
@@ -6084,6 +6102,7 @@ See the [list of SSL OP Flags][] for details. | |||
| 6084 | 6102 | [FIPS provider from OpenSSL 3]: https://www.openssl.org/docs/man3.0/man7/crypto.html#FIPS-provider | |
| 6085 | 6103 | [HTML 5.2]: https://www.w3.org/TR/html52/changes.html#features-removed | |
| 6086 | 6104 | [JWK]: https://tools.ietf.org/html/rfc7517 | |
| 6105 | + [Key usages]: webcrypto.md#cryptokeyusages | ||
| 6087 | 6106 | [NIST SP 800-131A]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf | |
| 6088 | 6107 | [NIST SP 800-132]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf | |
| 6089 | 6108 | [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -245,7 +245,7 @@ async function aesGenerateKey(algorithm, extractable, keyUsages) { | |||
| 245 | 245 | extractable); | |
| 246 | 246 | } | |
| 247 | 247 | ||
| 248 | - async function aesImportKey( | ||
| 248 | + function aesImportKey( | ||
| 249 | 249 | algorithm, | |
| 250 | 250 | format, | |
| 251 | 251 | keyData, | |
@@ -266,6 +266,11 @@ async function aesImportKey( | |||
| 266 | 266 | let keyObject; | |
| 267 | 267 | let length; | |
| 268 | 268 | switch (format) { | |
| 269 | + case 'KeyObject': { | ||
| 270 | + validateKeyLength(keyData.symmetricKeySize * 8); | ||
| 271 | + keyObject = keyData; | ||
| 272 | + break; | ||
| 273 | + } | ||
| 269 | 274 | case 'raw': { | |
| 270 | 275 | validateKeyLength(keyData.byteLength * 8); | |
| 271 | 276 | keyObject = createSecretKey(keyData); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -197,7 +197,7 @@ function cfrgExportKey(key, format) { | |||
| 197 | 197 | key[kKeyObject][kHandle])); | |
| 198 | 198 | } | |
| 199 | 199 | ||
| 200 | - async function cfrgImportKey( | ||
| 200 | + function cfrgImportKey( | ||
| 201 | 201 | format, | |
| 202 | 202 | keyData, | |
| 203 | 203 | algorithm, | |
@@ -208,6 +208,11 @@ async function cfrgImportKey( | |||
| 208 | 208 | let keyObject; | |
| 209 | 209 | const usagesSet = new SafeSet(keyUsages); | |
| 210 | 210 | switch (format) { | |
| 211 | + case 'KeyObject': { | ||
| 212 | + verifyAcceptableCfrgKeyUse(name, keyData.type === 'public', usagesSet); | ||
| 213 | + keyObject = keyData; | ||
| 214 | + break; | ||
| 215 | + } | ||
| 211 | 216 | case 'spki': { | |
| 212 | 217 | verifyAcceptableCfrgKeyUse(name, true, usagesSet); | |
| 213 | 218 | try { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -149,7 +149,7 @@ function ecExportKey(key, format) { | |||
| 149 | 149 | key[kKeyObject][kHandle])); | |
| 150 | 150 | } | |
| 151 | 151 | ||
| 152 | - async function ecImportKey( | ||
| 152 | + function ecImportKey( | ||
| 153 | 153 | format, | |
| 154 | 154 | keyData, | |
| 155 | 155 | algorithm, | |
@@ -167,6 +167,11 @@ async function ecImportKey( | |||
| 167 | 167 | let keyObject; | |
| 168 | 168 | const usagesSet = new SafeSet(keyUsages); | |
| 169 | 169 | switch (format) { | |
| 170 | + case 'KeyObject': { | ||
| 171 | + verifyAcceptableEcKeyUse(name, keyData.type === 'public', usagesSet); | ||
| 172 | + keyObject = keyData; | ||
| 173 | + break; | ||
| 174 | + } | ||
| 170 | 175 | case 'spki': { | |
| 171 | 176 | verifyAcceptableEcKeyUse(name, true, usagesSet); | |
| 172 | 177 | try { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ const { | |||
| 6 | 6 | ObjectDefineProperties, | |
| 7 | 7 | ObjectDefineProperty, | |
| 8 | 8 | ObjectSetPrototypeOf, | |
| 9 | + SafeSet, | ||
| 9 | 10 | Symbol, | |
| 10 | 11 | SymbolToStringTag, | |
| 11 | 12 | Uint8Array, | |
@@ -49,6 +50,8 @@ const { | |||
| 49 | 50 | kKeyObject, | |
| 50 | 51 | getArrayBufferOrView, | |
| 51 | 52 | bigIntArrayToUnsignedBigInt, | |
| 53 | + normalizeAlgorithm, | ||
| 54 | + hasAnyNotIn, | ||
| 52 | 55 | } = require('internal/crypto/util'); | |
| 53 | 56 | ||
| 54 | 57 | const { | |
@@ -65,6 +68,7 @@ const { | |||
| 65 | 68 | const { | |
| 66 | 69 | customInspectSymbol: kInspect, | |
| 67 | 70 | kEnumerableProperty, | |
| 71 | + lazyDOMException, | ||
| 68 | 72 | } = require('internal/util'); | |
| 69 | 73 | ||
| 70 | 74 | const { inspect } = require('internal/util/inspect'); | |
@@ -148,6 +152,8 @@ const { | |||
| 148 | 152 | }, | |
| 149 | 153 | }); | |
| 150 | 154 | ||
| 155 | + let webidl; | ||
| 156 | + | ||
| 151 | 157 | class SecretKeyObject extends KeyObject { | |
| 152 | 158 | constructor(handle) { | |
| 153 | 159 | super('secret', handle); | |
@@ -168,6 +174,51 @@ const { | |||
| 168 | 174 | } | |
| 169 | 175 | return this[kHandle].export(); | |
| 170 | 176 | } | |
| 177 | + | ||
| 178 | + toCryptoKey(algorithm, extractable, keyUsages) { | ||
| 179 | + webidl ??= require('internal/crypto/webidl'); | ||
| 180 | + algorithm = normalizeAlgorithm(webidl.converters.AlgorithmIdentifier(algorithm), 'importKey'); | ||
| 181 | + extractable = webidl.converters.boolean(extractable); | ||
| 182 | + keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages); | ||
| 183 | + | ||
| 184 | + let result; | ||
| 185 | + switch (algorithm.name) { | ||
| 186 | + case 'HMAC': | ||
| 187 | + result = require('internal/crypto/mac') | ||
| 188 | + .hmacImportKey('KeyObject', this, algorithm, extractable, keyUsages); | ||
| 189 | + break; | ||
| 190 | + case 'AES-CTR': | ||
| 191 | + // Fall through | ||
| 192 | + case 'AES-CBC': | ||
| 193 | + // Fall through | ||
| 194 | + case 'AES-GCM': | ||
| 195 | + // Fall through | ||
| 196 | + case 'AES-KW': | ||
| 197 | + result = require('internal/crypto/aes') | ||
| 198 | + .aesImportKey(algorithm, 'KeyObject', this, extractable, keyUsages); | ||
| 199 | + break; | ||
| 200 | + case 'HKDF': | ||
| 201 | + // Fall through | ||
| 202 | + case 'PBKDF2': | ||
| 203 | + result = importGenericSecretKey( | ||
| 204 | + algorithm, | ||
| 205 | + 'KeyObject', | ||
| 206 | + this, | ||
| 207 | + extractable, | ||
| 208 | + keyUsages); | ||
| 209 | + break; | ||
| 210 | + default: | ||
| 211 | + throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError'); | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + if (result.usages.length === 0) { | ||
| 215 | + throw lazyDOMException( | ||
| 216 | + `Usages cannot be empty when importing a ${result.type} key.`, | ||
| 217 | + 'SyntaxError'); | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + return result; | ||
| 221 | + } | ||
| 171 | 222 | } | |
| 172 | 223 | ||
| 173 | 224 | const kAsymmetricKeyType = Symbol('kAsymmetricKeyType'); | |
@@ -209,6 +260,51 @@ const { | |||
| 209 | 260 | return {}; | |
| 210 | 261 | } | |
| 211 | 262 | } | |
| 263 | + | ||
| 264 | + toCryptoKey(algorithm, extractable, keyUsages) { | ||
| 265 | + webidl ??= require('internal/crypto/webidl'); | ||
| 266 | + algorithm = normalizeAlgorithm(webidl.converters.AlgorithmIdentifier(algorithm), 'importKey'); | ||
| 267 | + extractable = webidl.converters.boolean(extractable); | ||
| 268 | + keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages); | ||
| 269 | + | ||
| 270 | + let result; | ||
| 271 | + switch (algorithm.name) { | ||
| 272 | + case 'RSASSA-PKCS1-v1_5': | ||
| 273 | + // Fall through | ||
| 274 | + case 'RSA-PSS': | ||
| 275 | + // Fall through | ||
| 276 | + case 'RSA-OAEP': | ||
| 277 | + result = require('internal/crypto/rsa') | ||
| 278 | + .rsaImportKey('KeyObject', this, algorithm, extractable, keyUsages); | ||
| 279 | + break; | ||
| 280 | + case 'ECDSA': | ||
| 281 | + // Fall through | ||
| 282 | + case 'ECDH': | ||
| 283 | + result = require('internal/crypto/ec') | ||
| 284 | + .ecImportKey('KeyObject', this, algorithm, extractable, keyUsages); | ||
| 285 | + break; | ||
| 286 | + case 'Ed25519': | ||
| 287 | + // Fall through | ||
| 288 | + case 'Ed448': | ||
| 289 | + // Fall through | ||
| 290 | + case 'X25519': | ||
| 291 | + // Fall through | ||
| 292 | + case 'X448': | ||
| 293 | + result = require('internal/crypto/cfrg') | ||
| 294 | + .cfrgImportKey('KeyObject', this, algorithm, extractable, keyUsages); | ||
| 295 | + break; | ||
| 296 | + default: | ||
| 297 | + throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError'); | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + if (result.type === 'private' && result.usages.length === 0) { | ||
| 301 | + throw lazyDOMException( | ||
| 302 | + `Usages cannot be empty when importing a ${result.type} key.`, | ||
| 303 | + 'SyntaxError'); | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | + return result; | ||
| 307 | + } | ||
| 212 | 308 | } | |
| 213 | 309 | ||
| 214 | 310 | class PublicKeyObject extends AsymmetricKeyObject { | |
@@ -801,6 +897,68 @@ function isCryptoKey(obj) { | |||
| 801 | 897 | return obj != null && obj[kKeyObject] !== undefined; | |
| 802 | 898 | } | |
| 803 | 899 | ||
| 900 | + function importGenericSecretKey( | ||
| 901 | + { name, length }, | ||
| 902 | + format, | ||
| 903 | + keyData, | ||
| 904 | + extractable, | ||
| 905 | + keyUsages) { | ||
| 906 | + const usagesSet = new SafeSet(keyUsages); | ||
| 907 | + if (extractable) | ||
| 908 | + throw lazyDOMException(`${name} keys are not extractable`, 'SyntaxError'); | ||
| 909 | + | ||
| 910 | + if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) { | ||
| 911 | + throw lazyDOMException( | ||
| 912 | + `Unsupported key usage for a ${name} key`, | ||
| 913 | + 'SyntaxError'); | ||
| 914 | + } | ||
| 915 | + | ||
| 916 | + switch (format) { | ||
| 917 | + case 'KeyObject': { | ||
| 918 | + if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) { | ||
| 919 | + throw lazyDOMException( | ||
| 920 | + `Unsupported key usage for a ${name} key`, | ||
| 921 | + 'SyntaxError'); | ||
| 922 | + } | ||
| 923 | + | ||
| 924 | + const checkLength = keyData.symmetricKeySize * 8; | ||
| 925 | + | ||
| 926 | + // The Web Crypto spec allows for key lengths that are not multiples of | ||
| 927 | + // 8. We don't. Our check here is stricter than that defined by the spec | ||
| 928 | + // in that we require that algorithm.length match keyData.length * 8 if | ||
| 929 | + // algorithm.length is specified. | ||
| 930 | + if (length !== undefined && length !== checkLength) { | ||
| 931 | + throw lazyDOMException('Invalid key length', 'DataError'); | ||
| 932 | + } | ||
| 933 | + return new InternalCryptoKey(keyData, { name }, keyUsages, false); | ||
| 934 | + } | ||
| 935 | + case 'raw': { | ||
| 936 | + if (hasAnyNotIn(usagesSet, ['deriveKey', 'deriveBits'])) { | ||
| 937 | + throw lazyDOMException( | ||
| 938 | + `Unsupported key usage for a ${name} key`, | ||
| 939 | + 'SyntaxError'); | ||
| 940 | + } | ||
| 941 | + | ||
| 942 | + const checkLength = keyData.byteLength * 8; | ||
| 943 | + | ||
| 944 | + // The Web Crypto spec allows for key lengths that are not multiples of | ||
| 945 | + // 8. We don't. Our check here is stricter than that defined by the spec | ||
| 946 | + // in that we require that algorithm.length match keyData.length * 8 if | ||
| 947 | + // algorithm.length is specified. | ||
| 948 | + if (length !== undefined && length !== checkLength) { | ||
| 949 | + throw lazyDOMException('Invalid key length', 'DataError'); | ||
| 950 | + } | ||
| 951 | + | ||
| 952 | + const keyObject = createSecretKey(keyData); | ||
| 953 | + return new InternalCryptoKey(keyObject, { name }, keyUsages, false); | ||
| 954 | + } | ||
| 955 | + } | ||
| 956 | + | ||
| 957 | + throw lazyDOMException( | ||
| 958 | + `Unable to import ${name} key with format ${format}`, | ||
| 959 | + 'NotSupportedError'); | ||
| 960 | + } | ||
| 961 | + | ||
| 804 | 962 | module.exports = { | |
| 805 | 963 | // Public API. | |
| 806 | 964 | createSecretKey, | |
@@ -822,4 +980,5 @@ module.exports = { | |||
| 822 | 980 | PrivateKeyObject, | |
| 823 | 981 | isKeyObject, | |
| 824 | 982 | isCryptoKey, | |
| 983 | + importGenericSecretKey, | ||
| 825 | 984 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -82,7 +82,7 @@ function getAlgorithmName(hash) { | |||
| 82 | 82 | } | |
| 83 | 83 | } | |
| 84 | 84 | ||
| 85 | - async function hmacImportKey( | ||
| 85 | + function hmacImportKey( | ||
| 86 | 86 | format, | |
| 87 | 87 | keyData, | |
| 88 | 88 | algorithm, | |
@@ -96,6 +96,24 @@ async function hmacImportKey( | |||
| 96 | 96 | } | |
| 97 | 97 | let keyObject; | |
| 98 | 98 | switch (format) { | |
| 99 | + case 'KeyObject': { | ||
| 100 | + const checkLength = keyData.symmetricKeySize * 8; | ||
| 101 | + | ||
| 102 | + if (checkLength === 0 || algorithm.length === 0) | ||
| 103 | + throw lazyDOMException('Zero-length key is not supported', 'DataError'); | ||
| 104 | + | ||
| 105 | + // The Web Crypto spec allows for key lengths that are not multiples of | ||
| 106 | + // 8. We don't. Our check here is stricter than that defined by the spec | ||
| 107 | + // in that we require that algorithm.length match keyData.length * 8 if | ||
| 108 | + // algorithm.length is specified. | ||
| 109 | + if (algorithm.length !== undefined && | ||
| 110 | + algorithm.length !== checkLength) { | ||
| 111 | + throw lazyDOMException('Invalid key length', 'DataError'); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + keyObject = keyData; | ||
| 115 | + break; | ||
| 116 | + } | ||
| 99 | 117 | case 'raw': { | |
| 100 | 118 | const checkLength = keyData.byteLength * 8; | |
| 101 | 119 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -200,7 +200,7 @@ function rsaExportKey(key, format) { | |||
| 200 | 200 | kRsaVariants[key.algorithm.name])); | |
| 201 | 201 | } | |
| 202 | 202 | ||
| 203 | - async function rsaImportKey( | ||
| 203 | + function rsaImportKey( | ||
| 204 | 204 | format, | |
| 205 | 205 | keyData, | |
| 206 | 206 | algorithm, | |
@@ -209,6 +209,11 @@ async function rsaImportKey( | |||
| 209 | 209 | const usagesSet = new SafeSet(keyUsages); | |
| 210 | 210 | let keyObject; | |
| 211 | 211 | switch (format) { | |
| 212 | + case 'KeyObject': { | ||
| 213 | + verifyAcceptableRsaKeyUse(algorithm.name, keyData.type === 'public', usagesSet); | ||
| 214 | + keyObject = keyData; | ||
| 215 | + break; | ||
| 216 | + } | ||
| 212 | 217 | case 'spki': { | |
| 213 | 218 | verifyAcceptableRsaKeyUse(algorithm.name, true, usagesSet); | |
| 214 | 219 | try { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments