GeezerVison is showing github.com — crypto: unify asymmetric key import through KeyObjectHandle::Init · nodejs/node@b46d52b
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/aes.js +8-23Lines changed: 8 additions & 23 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 9 | 9 | AESCipherJob, | 10 | 10 | KeyObjectHandle, | 11 | 11 | kCryptoJobAsync, | | 12 | + kKeyFormatJWK, | | 13 | + kKeyTypeSecret, | 12 | 14 | kKeyVariantAES_CTR_128, | 13 | 15 | kKeyVariantAES_CBC_128, | 14 | 16 | kKeyVariantAES_GCM_128, | @@ -29,7 +31,6 @@ const { | 29 | 31 | const { | 30 | 32 | hasAnyNotIn, | 31 | 33 | jobPromise, | 32 | | - validateKeyOps, | 33 | 34 | kHandle, | 34 | 35 | kKeyObject, | 35 | 36 | } = require('internal/crypto/util'); | @@ -46,6 +47,10 @@ const { | 46 | 47 | kAlgorithm, | 47 | 48 | } = require('internal/crypto/keys'); | 48 | 49 |
| | 50 | +const { | | 51 | + validateJwk, | | 52 | +} = require('internal/crypto/webcrypto_util'); | | 53 | + | 49 | 54 | const { | 50 | 55 | generateKey: _generateKey, | 51 | 56 | } = require('internal/crypto/keygen'); | @@ -244,31 +249,11 @@ function aesImportKey( | 244 | 249 | break; | 245 | 250 | } | 246 | 251 | case 'jwk': { | 247 | | - if (!keyData.kty) | 248 | | - throw lazyDOMException('Invalid keyData', 'DataError'); | 249 | | - | 250 | | - if (keyData.kty !== 'oct') | 251 | | - throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError'); | 252 | | - | 253 | | - if (usagesSet.size > 0 && | 254 | | - keyData.use !== undefined && | 255 | | - keyData.use !== 'enc') { | 256 | | - throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError'); | 257 | | - } | 258 | | - | 259 | | - validateKeyOps(keyData.key_ops, usagesSet); | 260 | | - | 261 | | - if (keyData.ext !== undefined && | 262 | | - keyData.ext === false && | 263 | | - extractable === true) { | 264 | | - throw lazyDOMException( | 265 | | - 'JWK "ext" Parameter and extractable mismatch', | 266 | | - 'DataError'); | 267 | | - } | | 252 | + validateJwk(keyData, 'oct', extractable, usagesSet, 'enc'); | 268 | 253 |
| 269 | 254 | const handle = new KeyObjectHandle(); | 270 | 255 | try { | 271 | | - handle.initJwk(keyData); | | 256 | + handle.init(kKeyTypeSecret, keyData, kKeyFormatJWK, null, null); | 272 | 257 | } catch (err) { | 273 | 258 | throw lazyDOMException( | 274 | 259 | 'Invalid keyData', { name: 'DataError', cause: err }); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/cfrg.js +54-140Lines changed: 54 additions & 140 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 3 | 3 | const { | 4 | 4 | SafeSet, | 5 | 5 | StringPrototypeToLowerCase, | | 6 | + TypedArrayPrototypeGetBuffer, | 6 | 7 | } = primordials; | 7 | 8 |
| 8 | 9 | const { Buffer } = require('buffer'); | 9 | 10 |
| 10 | 11 | const { | 11 | | - ECKeyExportJob, | 12 | | - KeyObjectHandle, | 13 | 12 | SignJob, | 14 | 13 | kCryptoJobAsync, | 15 | | - kKeyTypePrivate, | 16 | | - kKeyTypePublic, | | 14 | + kKeyFormatDER, | | 15 | + kKeyFormatRawPublic, | 17 | 16 | kSignJobModeSign, | 18 | 17 | kSignJobModeVerify, | | 18 | + kWebCryptoKeyFormatPKCS8, | | 19 | + kWebCryptoKeyFormatRaw, | | 20 | + kWebCryptoKeyFormatSPKI, | 19 | 21 | } = internalBinding('crypto'); | 20 | 22 |
| 21 | | -const { | 22 | | - codes: { | 23 | | - ERR_CRYPTO_INVALID_JWK, | 24 | | - }, | 25 | | -} = require('internal/errors'); | 26 | | - | 27 | 23 | const { | 28 | 24 | getUsagesUnion, | 29 | 25 | hasAnyNotIn, | 30 | 26 | jobPromise, | 31 | | - validateKeyOps, | 32 | 27 | kHandle, | 33 | 28 | kKeyObject, | 34 | 29 | } = require('internal/crypto/util'); | @@ -44,13 +39,16 @@ const { | 44 | 39 |
| 45 | 40 | const { | 46 | 41 | InternalCryptoKey, | 47 | | - PrivateKeyObject, | 48 | | - PublicKeyObject, | 49 | | - createPrivateKey, | 50 | | - createPublicKey, | 51 | 42 | kKeyType, | 52 | 43 | } = require('internal/crypto/keys'); | 53 | 44 |
| | 45 | +const { | | 46 | + importDerKey, | | 47 | + importJwkKey, | | 48 | + importRawKey, | | 49 | + validateJwk, | | 50 | +} = require('internal/crypto/webcrypto_util'); | | 51 | + | 54 | 52 | const generateKeyPair = promisify(_generateKeyPair); | 55 | 53 |
| 56 | 54 | function verifyAcceptableCfrgKeyUse(name, isPublic, usages) { | @@ -77,39 +75,6 @@ function verifyAcceptableCfrgKeyUse(name, isPublic, usages) { | 77 | 75 | } | 78 | 76 | } | 79 | 77 |
| 80 | | -function createCFRGRawKey(name, keyData, isPublic) { | 81 | | - const handle = new KeyObjectHandle(); | 82 | | - | 83 | | - switch (name) { | 84 | | - case 'Ed25519': | 85 | | - case 'X25519': | 86 | | - if (keyData.byteLength !== 32) { | 87 | | - throw lazyDOMException( | 88 | | - `${name} raw keys must be exactly 32-bytes`, 'DataError'); | 89 | | - } | 90 | | - break; | 91 | | - case 'Ed448': | 92 | | - if (keyData.byteLength !== 57) { | 93 | | - throw lazyDOMException( | 94 | | - `${name} raw keys must be exactly 57-bytes`, 'DataError'); | 95 | | - } | 96 | | - break; | 97 | | - case 'X448': | 98 | | - if (keyData.byteLength !== 56) { | 99 | | - throw lazyDOMException( | 100 | | - `${name} raw keys must be exactly 56-bytes`, 'DataError'); | 101 | | - } | 102 | | - break; | 103 | | - } | 104 | | - | 105 | | - const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate; | 106 | | - if (!handle.initEDRaw(name, keyData, keyType)) { | 107 | | - throw lazyDOMException('Invalid keyData', 'DataError'); | 108 | | - } | 109 | | - | 110 | | - return isPublic ? new PublicKeyObject(handle) : new PrivateKeyObject(handle); | 111 | | -} | 112 | | - | 113 | 78 | async function cfrgGenerateKey(algorithm, extractable, keyUsages) { | 114 | 79 | const { name } = algorithm; | 115 | 80 |
| @@ -171,7 +136,7 @@ async function cfrgGenerateKey(algorithm, extractable, keyUsages) { | 171 | 136 | case 'X25519': | 172 | 137 | // Fall through | 173 | 138 | case 'X448': | 174 | | - publicUsages = new SafeSet(); | | 139 | + publicUsages = []; | 175 | 140 | privateUsages = getUsagesUnion(usageSet, 'deriveKey', 'deriveBits'); | 176 | 141 | break; | 177 | 142 | } | @@ -196,10 +161,29 @@ async function cfrgGenerateKey(algorithm, extractable, keyUsages) { | 196 | 161 | } | 197 | 162 |
| 198 | 163 | function cfrgExportKey(key, format) { | 199 | | - return jobPromise(() => new ECKeyExportJob( | 200 | | - kCryptoJobAsync, | 201 | | - format, | 202 | | - key[kKeyObject][kHandle])); | | 164 | + try { | | 165 | + switch (format) { | | 166 | + case kWebCryptoKeyFormatRaw: { | | 167 | + const handle = key[kKeyObject][kHandle]; | | 168 | + return TypedArrayPrototypeGetBuffer( | | 169 | + key[kKeyType] === 'private' ? handle.rawPrivateKey() : handle.rawPublicKey()); | | 170 | + } | | 171 | + case kWebCryptoKeyFormatSPKI: { | | 172 | + return TypedArrayPrototypeGetBuffer( | | 173 | + key[kKeyObject][kHandle].export(kKeyFormatDER, kWebCryptoKeyFormatSPKI)); | | 174 | + } | | 175 | + case kWebCryptoKeyFormatPKCS8: { | | 176 | + return TypedArrayPrototypeGetBuffer( | | 177 | + key[kKeyObject][kHandle].export(kKeyFormatDER, kWebCryptoKeyFormatPKCS8, null, null)); | | 178 | + } | | 179 | + default: | | 180 | + return undefined; | | 181 | + } | | 182 | + } catch (err) { | | 183 | + throw lazyDOMException( | | 184 | + 'The operation failed for an operation-specific reason', | | 185 | + { name: 'OperationError', cause: err }); | | 186 | + } | 203 | 187 | } | 204 | 188 |
| 205 | 189 | function cfrgImportKey( | @@ -220,113 +204,42 @@ function cfrgImportKey( | 220 | 204 | } | 221 | 205 | case 'spki': { | 222 | 206 | verifyAcceptableCfrgKeyUse(name, true, usagesSet); | 223 | | - try { | 224 | | - keyObject = createPublicKey({ | 225 | | - key: keyData, | 226 | | - format: 'der', | 227 | | - type: 'spki', | 228 | | - }); | 229 | | - } catch (err) { | 230 | | - throw lazyDOMException( | 231 | | - 'Invalid keyData', { name: 'DataError', cause: err }); | 232 | | - } | | 207 | + keyObject = importDerKey(keyData, true); | 233 | 208 | break; | 234 | 209 | } | 235 | 210 | case 'pkcs8': { | 236 | 211 | verifyAcceptableCfrgKeyUse(name, false, usagesSet); | 237 | | - try { | 238 | | - keyObject = createPrivateKey({ | 239 | | - key: keyData, | 240 | | - format: 'der', | 241 | | - type: 'pkcs8', | 242 | | - }); | 243 | | - } catch (err) { | 244 | | - throw lazyDOMException( | 245 | | - 'Invalid keyData', { name: 'DataError', cause: err }); | 246 | | - } | | 212 | + keyObject = importDerKey(keyData, false); | 247 | 213 | break; | 248 | 214 | } | 249 | 215 | case 'jwk': { | 250 | | - if (!keyData.kty) | 251 | | - throw lazyDOMException('Invalid keyData', 'DataError'); | 252 | | - if (keyData.kty !== 'OKP') | 253 | | - throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError'); | | 216 | + const expectedUse = (name === 'X25519' || name === 'X448') ? 'enc' : 'sig'; | | 217 | + validateJwk(keyData, 'OKP', extractable, usagesSet, expectedUse); | | 218 | + | 254 | 219 | if (keyData.crv !== name) | 255 | 220 | throw lazyDOMException( | 256 | 221 | 'JWK "crv" Parameter and algorithm name mismatch', 'DataError'); | 257 | | - const isPublic = keyData.d === undefined; | 258 | | - | 259 | | - if (usagesSet.size > 0 && keyData.use !== undefined) { | 260 | | - let checkUse; | 261 | | - switch (name) { | 262 | | - case 'Ed25519': | 263 | | - // Fall through | 264 | | - case 'Ed448': | 265 | | - checkUse = 'sig'; | 266 | | - break; | 267 | | - case 'X25519': | 268 | | - // Fall through | 269 | | - case 'X448': | 270 | | - checkUse = 'enc'; | 271 | | - break; | 272 | | - } | 273 | | - if (keyData.use !== checkUse) | 274 | | - throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError'); | 275 | | - } | 276 | | - | 277 | | - validateKeyOps(keyData.key_ops, usagesSet); | 278 | | - | 279 | | - if (keyData.ext !== undefined && | 280 | | - keyData.ext === false && | 281 | | - extractable === true) { | 282 | | - throw lazyDOMException( | 283 | | - 'JWK "ext" Parameter and extractable mismatch', | 284 | | - 'DataError'); | 285 | | - } | 286 | 222 |
| 287 | 223 | if (keyData.alg !== undefined && (name === 'Ed25519' || name === 'Ed448')) { | 288 | | - if (keyData.alg !== name && keyData.alg !== 'EdDSA') { | | 224 | + if (keyData.alg !== name && keyData.alg !== 'EdDSA') | 289 | 225 | throw lazyDOMException( | 290 | | - 'JWK "alg" does not match the requested algorithm', | 291 | | - 'DataError'); | 292 | | - } | | 226 | + 'JWK "alg" does not match the requested algorithm', 'DataError'); | 293 | 227 | } | 294 | 228 |
| 295 | | - if (!isPublic && typeof keyData.x !== 'string') { | 296 | | - throw lazyDOMException('Invalid JWK', 'DataError'); | 297 | | - } | 298 | | - | 299 | | - verifyAcceptableCfrgKeyUse( | 300 | | - name, | 301 | | - isPublic, | 302 | | - usagesSet); | 303 | | - | 304 | | - try { | 305 | | - const publicKeyObject = createCFRGRawKey( | 306 | | - name, | 307 | | - Buffer.from(keyData.x, 'base64'), | 308 | | - true); | 309 | | - | 310 | | - if (isPublic) { | 311 | | - keyObject = publicKeyObject; | 312 | | - } else { | 313 | | - keyObject = createCFRGRawKey( | 314 | | - name, | 315 | | - Buffer.from(keyData.d, 'base64'), | 316 | | - false); | | 229 | + const isPublic = keyData.d === undefined; | | 230 | + verifyAcceptableCfrgKeyUse(name, isPublic, usagesSet); | | 231 | + keyObject = importJwkKey(isPublic, keyData); | 317 | 232 |
| 318 | | - if (!createPublicKey(keyObject).equals(publicKeyObject)) { | 319 | | - throw new ERR_CRYPTO_INVALID_JWK(); | 320 | | - } | 321 | | - } | 322 | | - } catch (err) { | 323 | | - throw lazyDOMException('Invalid keyData', { name: 'DataError', cause: err }); | | 233 | + if (!isPublic) { | | 234 | + const publicKey = Buffer.from(keyData.x, 'base64url'); | | 235 | + if (!Buffer.from(keyObject[kHandle].rawPublicKey()).equals(publicKey)) | | 236 | + throw lazyDOMException('Invalid keyData', 'DataError'); | 324 | 237 | } | 325 | 238 | break; | 326 | 239 | } | 327 | 240 | case 'raw': { | 328 | 241 | verifyAcceptableCfrgKeyUse(name, true, usagesSet); | 329 | | - keyObject = createCFRGRawKey(name, keyData, true); | | 242 | + keyObject = importRawKey(true, keyData, kKeyFormatRawPublic, name); | 330 | 243 | break; | 331 | 244 | } | 332 | 245 | default: | @@ -340,7 +253,7 @@ function cfrgImportKey( | 340 | 253 | return new InternalCryptoKey( | 341 | 254 | keyObject, | 342 | 255 | { name }, | 343 | | - usagesSet, | | 256 | + keyUsages, | 344 | 257 | extractable); | 345 | 258 | } | 346 | 259 |
| @@ -358,6 +271,7 @@ async function eddsaSignVerify(key, data, algorithm, signature) { | 358 | 271 | undefined, | 359 | 272 | undefined, | 360 | 273 | undefined, | | 274 | + undefined, | 361 | 275 | data, | 362 | 276 | undefined, | 363 | 277 | undefined, | |
|