1'use strict'; 2 3const common = require('../common'); 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6 7const assert = require('assert'); 8const { 9 createCipheriv, 10 createDecipheriv, 11 createSign, 12 createVerify, 13 createSecretKey, 14 createPublicKey, 15 createPrivateKey, 16 KeyObject, 17 randomBytes, 18 publicDecrypt, 19 publicEncrypt, 20 privateDecrypt, 21 privateEncrypt, 22 getCurves, 23 generateKeySync, 24 generateKeyPairSync, 25} = require('crypto'); 26 27const fixtures = require('../common/fixtures'); 28 29const publicPem = fixtures.readKey('rsa_public.pem', 'ascii'); 30const privatePem = fixtures.readKey('rsa_private.pem', 'ascii'); 31 32const publicDsa = fixtures.readKey('dsa_public_1025.pem', 'ascii'); 33const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', 34 'ascii'); 35 36{ 37 // Attempting to create a key of a wrong type should throw 38 const TYPE = 'wrong_type'; 39 40 assert.throws(() => new KeyObject(TYPE), { 41 name: 'TypeError', 42 code: 'ERR_INVALID_ARG_VALUE', 43 message: `The argument 'type' is invalid. Received '${TYPE}'` 44 }); 45} 46 47{ 48 // Attempting to create a key with non-object handle should throw 49 assert.throws(() => new KeyObject('secret', ''), { 50 name: 'TypeError', 51 code: 'ERR_INVALID_ARG_TYPE', 52 message: 53 'The "handle" argument must be of type object. Received type ' + 54 "string ('')" 55 }); 56} 57 58{ 59 assert.throws(() => KeyObject.from('invalid_key'), { 60 name: 'TypeError', 61 code: 'ERR_INVALID_ARG_TYPE', 62 message: 63 'The "key" argument must be an instance of CryptoKey. Received type ' + 64 "string ('invalid_key')" 65 }); 66} 67 68{ 69 const keybuf = randomBytes(32); 70 const key = createSecretKey(keybuf); 71 assert.strictEqual(key.type, 'secret'); 72 assert.strictEqual(key.toString(), '[object KeyObject]'); 73 assert.strictEqual(key.symmetricKeySize, 32); 74 assert.strictEqual(key.asymmetricKeyType, undefined); 75 assert.strictEqual(key.asymmetricKeyDetails, undefined); 76 77 const exportedKey = key.export(); 78 assert(keybuf.equals(exportedKey)); 79 80 const plaintext = Buffer.from('Hello world', 'utf8'); 81 82 const cipher = createCipheriv('aes-256-ecb', key, null); 83 const ciphertext = Buffer.concat([ 84 cipher.update(plaintext), cipher.final(), 85 ]); 86 87 const decipher = createDecipheriv('aes-256-ecb', key, null); 88 const deciphered = Buffer.concat([ 89 decipher.update(ciphertext), decipher.final(), 90 ]); 91 92 assert(plaintext.equals(deciphered)); 93} 94 95{ 96 // Passing an existing public key object to createPublicKey should throw. 97 const publicKey = createPublicKey(publicPem); 98 assert.throws(() => createPublicKey(publicKey), { 99 name: 'TypeError', 100 code: 'ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE', 101 message: 'Invalid key object type public, expected private.' 102 }); 103 104 // Constructing a private key from a public key should be impossible, even 105 // if the public key was derived from a private key. 106 assert.throws(() => createPrivateKey(createPublicKey(privatePem)), { 107 name: 'TypeError', 108 code: 'ERR_INVALID_ARG_TYPE', 109 }); 110 111 // Similarly, passing an existing private key object to createPrivateKey 112 // should throw. 113 const privateKey = createPrivateKey(privatePem); 114 assert.throws(() => createPrivateKey(privateKey), { 115 name: 'TypeError', 116 code: 'ERR_INVALID_ARG_TYPE', 117 }); 118} 119 120{ 121 const jwk = { 122 e: 'AQAB', 123 n: 't9xYiIonscC3vz_A2ceR7KhZZlDu_5bye53nCVTcKnWd2seY6UAdKersX6njr83Dd5OVe' + 124 '1BW_wJvp5EjWTAGYbFswlNmeD44edEGM939B6Lq-_8iBkrTi8mGN4YCytivE24YI0D4XZ' + 125 'MPfkLSpab2y_Hy4DjQKBq1ThZ0UBnK-9IhX37Ju_ZoGYSlTIGIhzyaiYBh7wrZBoPczIE' + 126 'u6et_kN2VnnbRUtkYTF97ggcv5h-hDpUQjQW0ZgOMcTc8n-RkGpIt0_iM_bTjI3Tz_gsF' + 127 'di6hHcpZgbopPL630296iByyigQCPJVzdusFrQN5DeC-zT_nGypQkZanLb4ZspSx9Q', 128 d: 'ktnq2LvIMqBj4txP82IEOorIRQGVsw1khbm8A-cEpuEkgM71Yi_0WzupKktucUeevQ5i0' + 129 'Yh8w9e1SJiTLDRAlJz66kdky9uejiWWl6zR4dyNZVMFYRM43ijLC-P8rPne9Fz16IqHFW' + 130 '5VbJqA1xCBhKmuPMsD71RNxZ4Hrsa7Kt_xglQTYsLbdGIwDmcZihId9VGXRzvmCPsDRf2' + 131 'fCkAj7HDeRxpUdEiEDpajADc-PWikra3r3b40tVHKWm8wxJLivOIN7GiYXKQIW6RhZgH-' + 132 'Rk45JIRNKxNagxdeXUqqyhnwhbTo1Hite0iBDexN9tgoZk0XmdYWBn6ElXHRZ7VCDQ', 133 p: '8UovlB4nrBm7xH-u7XXBMbqxADQm5vaEZxw9eluc-tP7cIAI4sglMIvL_FMpbd2pEeP_B' + 134 'kR76NTDzzDuPAZvUGRavgEjy0O9j2NAs_WPK4tZF-vFdunhnSh4EHAF4Ij9kbsUi90NOp' + 135 'bGfVqPdOaHqzgHKoR23Cuusk9wFQ2XTV8', 136 q: 'wxHdEYT9xrpfrHPqSBQPpO0dWGKJEkrWOb-76rSfuL8wGR4OBNmQdhLuU9zTIh22pog-X' + 137 'PnLPAecC-4yu_wtJ2SPCKiKDbJBre0CKPyRfGqzvA3njXwMxXazU4kGs-2Fg-xu_iKbaI' + 138 'jxXrclBLhkxhBtySrwAFhxxOk6fFcPLSs', 139 dp: 'qS_Mdr5CMRGGMH0bKhPUWEtAixUGZhJaunX5wY71Xoc_Gh4cnO-b7BNJ_-5L8WZog0vr' + 140 '6PgiLhrqBaCYm2wjpyoG2o2wDHm-NAlzN_wp3G2EFhrSxdOux-S1c0kpRcyoiAO2n29rN' + 141 'Da-jOzwBBcU8ACEPdLOCQl0IEFFJO33tl8', 142 dq: 'WAziKpxLKL7LnL4dzDcx8JIPIuwnTxh0plCDdCffyLaT8WJ9lXbXHFTjOvt8WfPrlDP_' + 143 'Ylxmfkw5BbGZOP1VLGjZn2DkH9aMiwNmbDXFPdG0G3hzQovx_9fajiRV4DWghLHeT9wzJ' + 144 'fZabRRiI0VQR472300AVEeX4vgbrDBn600', 145 qi: 'k7czBCT9rHn_PNwCa17hlTy88C4vXkwbz83Oa-aX5L4e5gw5lhcR2ZuZHLb2r6oMt9rl' + 146 'D7EIDItSs-u21LOXWPTAlazdnpYUyw_CzogM_PN-qNwMRXn5uXFFhmlP2mVg2EdELTahX' + 147 'ch8kWqHaCSX53yvqCtRKu_j76V31TfQZGM', 148 kty: 'RSA', 149 }; 150 const publicJwk = { kty: jwk.kty, e: jwk.e, n: jwk.n }; 151 152 const publicKey = createPublicKey(publicPem); 153 assert.strictEqual(publicKey.type, 'public'); 154 assert.strictEqual(publicKey.toString(), '[object KeyObject]'); 155 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); 156 assert.strictEqual(publicKey.symmetricKeySize, undefined); 157 158 const privateKey = createPrivateKey(privatePem); 159 assert.strictEqual(privateKey.type, 'private'); 160 assert.strictEqual(privateKey.toString(), '[object KeyObject]'); 161 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); 162 assert.strictEqual(privateKey.symmetricKeySize, undefined); 163 164 // It should be possible to derive a public key from a private key. 165 const derivedPublicKey = createPublicKey(privateKey); 166 assert.strictEqual(derivedPublicKey.type, 'public'); 167 assert.strictEqual(derivedPublicKey.toString(), '[object KeyObject]'); 168 assert.strictEqual(derivedPublicKey.asymmetricKeyType, 'rsa'); 169 assert.strictEqual(derivedPublicKey.symmetricKeySize, undefined); 170 171 const publicKeyFromJwk = createPublicKey({ key: publicJwk, format: 'jwk' }); 172 assert.strictEqual(publicKey.type, 'public'); 173 assert.strictEqual(publicKey.toString(), '[object KeyObject]'); 174 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); 175 assert.strictEqual(publicKey.symmetricKeySize, undefined); 176 177 const privateKeyFromJwk = createPrivateKey({ key: jwk, format: 'jwk' }); 178 assert.strictEqual(privateKey.type, 'private'); 179 assert.strictEqual(privateKey.toString(), '[object KeyObject]'); 180 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); 181 assert.strictEqual(privateKey.symmetricKeySize, undefined); 182 183 // It should also be possible to import an encrypted private key as a public 184 // key. 185 const decryptedKey = createPublicKey({ 186 key: privateKey.export({ 187 type: 'pkcs8', 188 format: 'pem', 189 passphrase: '123', 190 cipher: 'aes-128-cbc' 191 }), 192 format: 'pem', 193 passphrase: '123' 194 }); 195 assert.strictEqual(decryptedKey.type, 'public'); 196 assert.strictEqual(decryptedKey.asymmetricKeyType, 'rsa'); 197 198 // Test exporting with an invalid options object, this should throw. 199 for (const opt of [undefined, null, 'foo', 0, NaN]) { 200 assert.throws(() => publicKey.export(opt), { 201 name: 'TypeError', 202 code: 'ERR_INVALID_ARG_TYPE', 203 message: /^The "options" argument must be of type object/ 204 }); 205 } 206 207 for (const keyObject of [publicKey, derivedPublicKey, publicKeyFromJwk]) { 208 assert.deepStrictEqual( 209 keyObject.export({ format: 'jwk' }), 210 { kty: 'RSA', n: jwk.n, e: jwk.e } 211 ); 212 } 213 214 for (const keyObject of [privateKey, privateKeyFromJwk]) { 215 assert.deepStrictEqual( 216 keyObject.export({ format: 'jwk' }), 217 jwk 218 ); 219 } 220 221 // Exporting the key using JWK should not work since this format does not 222 // support key encryption 223 assert.throws(() => { 224 privateKey.export({ format: 'jwk', passphrase: 'secret' }); 225 }, { 226 message: 'The selected key encoding jwk does not support encryption.', 227 code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' 228 }); 229 230 const publicDER = publicKey.export({ 231 format: 'der', 232 type: 'pkcs1' 233 }); 234 235 const privateDER = privateKey.export({ 236 format: 'der', 237 type: 'pkcs1' 238 }); 239 240 assert(Buffer.isBuffer(publicDER)); 241 assert(Buffer.isBuffer(privateDER)); 242 243 const plaintext = Buffer.from('Hello world', 'utf8'); 244 const testDecryption = (fn, ciphertexts, decryptionKeys) => { 245 for (const ciphertext of ciphertexts) { 246 for (const key of decryptionKeys) { 247 const deciphered = fn(key, ciphertext); 248 assert.deepStrictEqual(deciphered, plaintext); 249 } 250 } 251 }; 252 253 testDecryption(privateDecrypt, [ 254 // Encrypt using the public key. 255 publicEncrypt(publicKey, plaintext), 256 publicEncrypt({ key: publicKey }, plaintext), 257 publicEncrypt({ key: publicJwk, format: 'jwk' }, plaintext), 258 259 // Encrypt using the private key. 260 publicEncrypt(privateKey, plaintext), 261 publicEncrypt({ key: privateKey }, plaintext), 262 publicEncrypt({ key: jwk, format: 'jwk' }, plaintext), 263 264 // Encrypt using a public key derived from the private key. 265 publicEncrypt(derivedPublicKey, plaintext), 266 publicEncrypt({ key: derivedPublicKey }, plaintext), 267 268 // Test distinguishing PKCS#1 public and private keys based on the 269 // DER-encoded data only. 270 publicEncrypt({ format: 'der', type: 'pkcs1', key: publicDER }, plaintext), 271 publicEncrypt({ format: 'der', type: 'pkcs1', key: privateDER }, plaintext), 272 ], [ 273 privateKey, 274 { format: 'pem', key: privatePem }, 275 { format: 'der', type: 'pkcs1', key: privateDER }, 276 { key: jwk, format: 'jwk' }, 277 ]); 278 279 testDecryption(publicDecrypt, [ 280 privateEncrypt(privateKey, plaintext), 281 ], [ 282 // Decrypt using the public key. 283 publicKey, 284 { format: 'pem', key: publicPem }, 285 { format: 'der', type: 'pkcs1', key: publicDER }, 286 { key: publicJwk, format: 'jwk' }, 287 288 // Decrypt using the private key. 289 privateKey, 290 { format: 'pem', key: privatePem }, 291 { format: 'der', type: 'pkcs1', key: privateDER }, 292 { key: jwk, format: 'jwk' }, 293 ]); 294} 295 296{ 297 // This should not cause a crash: https://github.com/nodejs/node/issues/25247 298 assert.throws(() => { 299 createPrivateKey({ key: '' }); 300 }, common.hasOpenSSL3 ? { 301 message: 'error:1E08010C:DECODER routines::unsupported', 302 } : { 303 message: 'error:0909006C:PEM routines:get_name:no start line', 304 code: 'ERR_OSSL_PEM_NO_START_LINE', 305 reason: 'no start line', 306 library: 'PEM routines', 307 function: 'get_name', 308 }); 309 310 // This should not abort either: https://github.com/nodejs/node/issues/29904 311 assert.throws(() => { 312 createPrivateKey({ key: Buffer.alloc(0), format: 'der', type: 'spki' }); 313 }, { 314 code: 'ERR_INVALID_ARG_VALUE', 315 message: "The property 'options.type' is invalid. Received 'spki'" 316 }); 317 318 // Unlike SPKI, PKCS#1 is a valid encoding for private keys (and public keys), 319 // so it should be accepted by createPrivateKey, but OpenSSL won't parse it. 320 assert.throws(() => { 321 const key = createPublicKey(publicPem).export({ 322 format: 'der', 323 type: 'pkcs1' 324 }); 325 createPrivateKey({ key, format: 'der', type: 'pkcs1' }); 326 }, common.hasOpenSSL3 ? { 327 message: /error:1E08010C:DECODER routines::unsupported/, 328 library: 'DECODER routines' 329 } : { 330 message: /asn1 encoding/, 331 library: 'asn1 encoding routines' 332 }); 333} 334 335[ 336 { private: fixtures.readKey('ed25519_private.pem', 'ascii'), 337 public: fixtures.readKey('ed25519_public.pem', 'ascii'), 338 keyType: 'ed25519', 339 jwk: { 340 crv: 'Ed25519', 341 x: 'K1wIouqnuiA04b3WrMa-xKIKIpfHetNZRv3h9fBf768', 342 d: 'wVK6M3SMhQh3NK-7GRrSV-BVWQx1FO5pW8hhQeu_NdA', 343 kty: 'OKP' 344 } }, 345 { private: fixtures.readKey('ed448_private.pem', 'ascii'), 346 public: fixtures.readKey('ed448_public.pem', 'ascii'), 347 keyType: 'ed448', 348 jwk: { 349 crv: 'Ed448', 350 x: 'oX_ee5-jlcU53-BbGRsGIzly0V-SZtJ_oGXY0udf84q2hTW2RdstLktvwpkVJOoNb7o' + 351 'Dgc2V5ZUA', 352 d: '060Ke71sN0GpIc01nnGgMDkp0sFNQ09woVo4AM1ffax1-mjnakK0-p-S7-Xf859QewX' + 353 'jcR9mxppY', 354 kty: 'OKP' 355 } }, 356 { private: fixtures.readKey('x25519_private.pem', 'ascii'), 357 public: fixtures.readKey('x25519_public.pem', 'ascii'), 358 keyType: 'x25519', 359 jwk: { 360 crv: 'X25519', 361 x: 'aSb8Q-RndwfNnPeOYGYPDUN3uhAPnMLzXyfi-mqfhig', 362 d: 'mL_IWm55RrALUGRfJYzw40gEYWMvtRkesP9mj8o8Omc', 363 kty: 'OKP' 364 } }, 365 { private: fixtures.readKey('x448_private.pem', 'ascii'), 366 public: fixtures.readKey('x448_public.pem', 'ascii'), 367 keyType: 'x448', 368 jwk: { 369 crv: 'X448', 370 x: 'ioHSHVpTs6hMvghosEJDIR7ceFiE3-Xccxati64oOVJ7NWjfozE7ae31PXIUFq6cVYg' + 371 'vSKsDFPA', 372 d: 'tMNtrO_q8dlY6Y4NDeSTxNQ5CACkHiPvmukidPnNIuX_EkcryLEXt_7i6j6YZMKsrWy' + 373 'S0jlSYJk', 374 kty: 'OKP' 375 } }, 376].forEach((info) => { 377 const keyType = info.keyType; 378 379 { 380 const key = createPrivateKey(info.private); 381 assert.strictEqual(key.type, 'private'); 382 assert.strictEqual(key.asymmetricKeyType, keyType); 383 assert.strictEqual(key.symmetricKeySize, undefined); 384 assert.strictEqual( 385 key.export({ type: 'pkcs8', format: 'pem' }), info.private); 386 assert.deepStrictEqual( 387 key.export({ format: 'jwk' }), info.jwk); 388 } 389 390 { 391 const key = createPrivateKey({ key: info.jwk, format: 'jwk' }); 392 assert.strictEqual(key.type, 'private'); 393 assert.strictEqual(key.asymmetricKeyType, keyType); 394 assert.strictEqual(key.symmetricKeySize, undefined); 395 assert.strictEqual( 396 key.export({ type: 'pkcs8', format: 'pem' }), info.private); 397 assert.deepStrictEqual( 398 key.export({ format: 'jwk' }), info.jwk); 399 } 400 401 { 402 for (const input of [ 403 info.private, info.public, { key: info.jwk, format: 'jwk' }]) { 404 const key = createPublicKey(input); 405 assert.strictEqual(key.type, 'public'); 406 assert.strictEqual(key.asymmetricKeyType, keyType); 407 assert.strictEqual(key.symmetricKeySize, undefined); 408 assert.strictEqual( 409 key.export({ type: 'spki', format: 'pem' }), info.public); 410 const jwk = { ...info.jwk }; 411 delete jwk.d; 412 assert.deepStrictEqual( 413 key.export({ format: 'jwk' }), jwk); 414 } 415 } 416}); 417 418[ 419 { private: fixtures.readKey('ec_p256_private.pem', 'ascii'), 420 public: fixtures.readKey('ec_p256_public.pem', 'ascii'), 421 keyType: 'ec', 422 namedCurve: 'prime256v1', 423 jwk: { 424 crv: 'P-256', 425 d: 'DxBsPQPIgMuMyQbxzbb9toew6Ev6e9O6ZhpxLNgmAEo', 426 kty: 'EC', 427 x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs', 428 y: 'UbJuPy2Xi0lW7UYTBxPK3yGgDu9EAKYIecjkHX5s2lI' 429 } }, 430 { private: fixtures.readKey('ec_secp256k1_private.pem', 'ascii'), 431 public: fixtures.readKey('ec_secp256k1_public.pem', 'ascii'), 432 keyType: 'ec', 433 namedCurve: 'secp256k1', 434 jwk: { 435 crv: 'secp256k1', 436 d: 'c34ocwTwpFa9NZZh3l88qXyrkoYSxvC0FEsU5v1v4IM', 437 kty: 'EC', 438 x: 'cOzhFSpWxhalCbWNdP2H_yUkdC81C9T2deDpfxK7owA', 439 y: '-A3DAZTk9IPppN-f03JydgHaFvL1fAHaoXf4SX4NXyo' 440 } }, 441 { private: fixtures.readKey('ec_p384_private.pem', 'ascii'), 442 public: fixtures.readKey('ec_p384_public.pem', 'ascii'), 443 keyType: 'ec', 444 namedCurve: 'secp384r1', 445 jwk: { 446 crv: 'P-384', 447 d: 'dwfuHuAtTlMRn7ZBCBm_0grpc1D_4hPeNAgevgelljuC0--k_LDFosDgBlLLmZsi', 448 kty: 'EC', 449 x: 'hON3nzGJgv-08fdHpQxgRJFZzlK-GZDGa5f3KnvM31cvvjJmsj4UeOgIdy3rDAjV', 450 y: 'fidHhtecNCGCfLqmrLjDena1NSzWzWH1u_oUdMKGo5XSabxzD7-8JZxjpc8sR9cl' 451 } }, 452 { private: fixtures.readKey('ec_p521_private.pem', 'ascii'), 453 public: fixtures.readKey('ec_p521_public.pem', 'ascii'), 454 keyType: 'ec', 455 namedCurve: 'secp521r1', 456 jwk: { 457 crv: 'P-521', 458 d: 'ABIIbmn3Gm_Y11uIDkC3g2ijpRxIrJEBY4i_JJYo5OougzTl3BX2ifRluPJMaaHcNer' + 459 'bQH_WdVkLLX86ShlHrRyJ', 460 kty: 'EC', 461 x: 'AaLFgjwZtznM3N7qsfb86awVXe6c6djUYOob1FN-kllekv0KEXV0bwcDjPGQz5f6MxL' + 462 'CbhMeHRavUS6P10rsTtBn', 463 y: 'Ad3flexBeAfXceNzRBH128kFbOWD6W41NjwKRqqIF26vmgW_8COldGKZjFkOSEASxPB' + 464 'cvA2iFJRUyQ3whC00j0Np' 465 } }, 466].forEach((info) => { 467 const { keyType, namedCurve } = info; 468 469 { 470 const key = createPrivateKey(info.private); 471 assert.strictEqual(key.type, 'private'); 472 assert.strictEqual(key.asymmetricKeyType, keyType); 473 assert.deepStrictEqual(key.asymmetricKeyDetails, { namedCurve }); 474 assert.strictEqual(key.symmetricKeySize, undefined); 475 assert.strictEqual( 476 key.export({ type: 'pkcs8', format: 'pem' }), info.private); 477 assert.deepStrictEqual( 478 key.export({ format: 'jwk' }), info.jwk); 479 } 480 481 { 482 const key = createPrivateKey({ key: info.jwk, format: 'jwk' }); 483 assert.strictEqual(key.type, 'private'); 484 assert.strictEqual(key.asymmetricKeyType, keyType); 485 assert.deepStrictEqual(key.asymmetricKeyDetails, { namedCurve }); 486 assert.strictEqual(key.symmetricKeySize, undefined); 487 assert.strictEqual( 488 key.export({ type: 'pkcs8', format: 'pem' }), info.private); 489 assert.deepStrictEqual( 490 key.export({ format: 'jwk' }), info.jwk); 491 } 492 493 { 494 for (const input of [ 495 info.private, info.public, { key: info.jwk, format: 'jwk' }]) { 496 const key = createPublicKey(input); 497 assert.strictEqual(key.type, 'public'); 498 assert.strictEqual(key.asymmetricKeyType, keyType); 499 assert.deepStrictEqual(key.asymmetricKeyDetails, { namedCurve }); 500 assert.strictEqual(key.symmetricKeySize, undefined); 501 assert.strictEqual( 502 key.export({ type: 'spki', format: 'pem' }), info.public); 503 const jwk = { ...info.jwk }; 504 delete jwk.d; 505 assert.deepStrictEqual( 506 key.export({ format: 'jwk' }), jwk); 507 } 508 } 509}); 510 511{ 512 // Reading an encrypted key without a passphrase should fail. 513 assert.throws(() => createPrivateKey(privateDsa), common.hasOpenSSL3 ? { 514 name: 'Error', 515 message: 'error:07880109:common libcrypto routines::interrupted or ' + 516 'cancelled', 517 } : { 518 name: 'TypeError', 519 code: 'ERR_MISSING_PASSPHRASE', 520 message: 'Passphrase required for encrypted key' 521 }); 522 523 // Reading an encrypted key with a passphrase that exceeds OpenSSL's buffer 524 // size limit should fail with an appropriate error code. 525 assert.throws(() => createPrivateKey({ 526 key: privateDsa, 527 format: 'pem', 528 passphrase: Buffer.alloc(1025, 'a') 529 }), common.hasOpenSSL3 ? { name: 'Error' } : { 530 code: 'ERR_OSSL_PEM_BAD_PASSWORD_READ', 531 name: 'Error' 532 }); 533 534 // The buffer has a size of 1024 bytes, so this passphrase should be permitted 535 // (but will fail decryption). 536 assert.throws(() => createPrivateKey({ 537 key: privateDsa, 538 format: 'pem', 539 passphrase: Buffer.alloc(1024, 'a') 540 }), { 541 message: /bad decrypt/ 542 }); 543 544 const publicKey = createPublicKey(publicDsa); 545 assert.strictEqual(publicKey.type, 'public'); 546 assert.strictEqual(publicKey.asymmetricKeyType, 'dsa'); 547 assert.strictEqual(publicKey.symmetricKeySize, undefined); 548 assert.throws( 549 () => publicKey.export({ format: 'jwk' }), 550 { code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE' }); 551 552 const privateKey = createPrivateKey({ 553 key: privateDsa, 554 format: 'pem', 555 passphrase: 'secret' 556 }); 557 assert.strictEqual(privateKey.type, 'private'); 558 assert.strictEqual(privateKey.asymmetricKeyType, 'dsa'); 559 assert.strictEqual(privateKey.symmetricKeySize, undefined); 560 assert.throws( 561 () => privateKey.export({ format: 'jwk' }), 562 { code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE' }); 563} 564 565{ 566 // Test RSA-PSS. 567 { 568 // This key pair does not restrict the message digest algorithm or salt 569 // length. 570 const publicPem = fixtures.readKey('rsa_pss_public_2048.pem'); 571 const privatePem = fixtures.readKey('rsa_pss_private_2048.pem'); 572 573 const publicKey = createPublicKey(publicPem); 574 const privateKey = createPrivateKey(privatePem); 575 576 // Because no RSASSA-PSS-params appears in the PEM, no defaults should be 577 // added for the PSS parameters. This is different from an empty 578 // RSASSA-PSS-params sequence (see test below). 579 const expectedKeyDetails = { 580 modulusLength: 2048, 581 publicExponent: 65537n 582 }; 583 584 assert.strictEqual(publicKey.type, 'public'); 585 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss'); 586 assert.deepStrictEqual(publicKey.asymmetricKeyDetails, expectedKeyDetails); 587 588 assert.strictEqual(privateKey.type, 'private'); 589 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss'); 590 assert.deepStrictEqual(privateKey.asymmetricKeyDetails, expectedKeyDetails); 591 592 assert.throws( 593 () => publicKey.export({ format: 'jwk' }), 594 { code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE' }); 595 assert.throws( 596 () => privateKey.export({ format: 'jwk' }), 597 { code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE' }); 598 599 for (const key of [privatePem, privateKey]) { 600 // Any algorithm should work. 601 for (const algo of ['sha1', 'sha256']) { 602 // Any salt length should work. 603 for (const saltLength of [undefined, 8, 10, 12, 16, 18, 20]) { 604 const signature = createSign(algo) 605 .update('foo') 606 .sign({ key, saltLength }); 607 608 for (const pkey of [key, publicKey, publicPem]) { 609 const okay = createVerify(algo) 610 .update('foo') 611 .verify({ key: pkey, saltLength }, signature); 612 613 assert.ok(okay); 614 } 615 } 616 } 617 } 618 619 // Exporting the key using PKCS#1 should not work since this would discard 620 // any algorithm restrictions. 621 assert.throws(() => { 622 publicKey.export({ format: 'pem', type: 'pkcs1' }); 623 }, { 624 code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS' 625 }); 626 } 627 628 { 629 // This key pair enforces sha1 as the message digest and the MGF1 630 // message digest and a salt length of 20 bytes. 631 632 const publicPem = fixtures.readKey('rsa_pss_public_2048_sha1_sha1_20.pem'); 633 const privatePem = 634 fixtures.readKey('rsa_pss_private_2048_sha1_sha1_20.pem'); 635 636 const publicKey = createPublicKey(publicPem); 637 const privateKey = createPrivateKey(privatePem); 638 639 // Unlike the previous key pair, this key pair contains an RSASSA-PSS-params 640 // sequence. However, because all values in the RSASSA-PSS-params are set to 641 // their defaults (see RFC 3447), the ASN.1 structure contains an empty 642 // sequence. Node.js should add the default values to the key details. 643 const expectedKeyDetails = { 644 modulusLength: 2048, 645 publicExponent: 65537n, 646 hashAlgorithm: 'sha1', 647 mgf1HashAlgorithm: 'sha1', 648 saltLength: 20 649 }; 650 651 assert.strictEqual(publicKey.type, 'public'); 652 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss'); 653 assert.deepStrictEqual(publicKey.asymmetricKeyDetails, expectedKeyDetails); 654 655 assert.strictEqual(privateKey.type, 'private'); 656 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss'); 657 assert.deepStrictEqual(privateKey.asymmetricKeyDetails, expectedKeyDetails); 658 } 659 660 { 661 // This key pair enforces sha256 as the message digest and the MGF1 662 // message digest and a salt length of at least 16 bytes. 663 const publicPem = 664 fixtures.readKey('rsa_pss_public_2048_sha256_sha256_16.pem'); 665 const privatePem = 666 fixtures.readKey('rsa_pss_private_2048_sha256_sha256_16.pem'); 667 668 const publicKey = createPublicKey(publicPem); 669 const privateKey = createPrivateKey(privatePem); 670 671 assert.strictEqual(publicKey.type, 'public'); 672 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss'); 673 674 assert.strictEqual(privateKey.type, 'private'); 675 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss'); 676 677 for (const key of [privatePem, privateKey]) { 678 // Signing with anything other than sha256 should fail. 679 assert.throws(() => { 680 createSign('sha1').sign(key); 681 }, /digest not allowed/); 682 683 // Signing with salt lengths less than 16 bytes should fail. 684 for (const saltLength of [8, 10, 12]) { 685 assert.throws(() => { 686 createSign('sha1').sign({ key, saltLength }); 687 }, /pss saltlen too small/); 688 } 689 690 // Signing with sha256 and appropriate salt lengths should work. 691 for (const saltLength of [undefined, 16, 18, 20]) { 692 const signature = createSign('sha256') 693 .update('foo') 694 .sign({ key, saltLength }); 695 696 for (const pkey of [key, publicKey, publicPem]) { 697 const okay = createVerify('sha256') 698 .update('foo') 699 .verify({ key: pkey, saltLength }, signature); 700 701 assert.ok(okay); 702 } 703 } 704 } 705 } 706 707 { 708 // This key enforces sha512 as the message digest and sha256 as the MGF1 709 // message digest. 710 const publicPem = 711 fixtures.readKey('rsa_pss_public_2048_sha512_sha256_20.pem'); 712 const privatePem = 713 fixtures.readKey('rsa_pss_private_2048_sha512_sha256_20.pem'); 714 715 const publicKey = createPublicKey(publicPem); 716 const privateKey = createPrivateKey(privatePem); 717 718 const expectedKeyDetails = { 719 modulusLength: 2048, 720 publicExponent: 65537n, 721 hashAlgorithm: 'sha512', 722 mgf1HashAlgorithm: 'sha256', 723 saltLength: 20 724 }; 725 726 assert.strictEqual(publicKey.type, 'public'); 727 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss'); 728 assert.deepStrictEqual(publicKey.asymmetricKeyDetails, expectedKeyDetails); 729 730 assert.strictEqual(privateKey.type, 'private'); 731 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss'); 732 assert.deepStrictEqual(privateKey.asymmetricKeyDetails, expectedKeyDetails); 733 734 // Node.js usually uses the same hash function for the message and for MGF1. 735 // However, when a different MGF1 message digest algorithm has been 736 // specified as part of the key, it should automatically switch to that. 737 // This behavior is required by sections 3.1 and 3.3 of RFC4055. 738 for (const key of [privatePem, privateKey]) { 739 // sha256 matches the MGF1 hash function and should be used internally, 740 // but it should not be permitted as the main message digest algorithm. 741 for (const algo of ['sha1', 'sha256']) { 742 assert.throws(() => { 743 createSign(algo).sign(key); 744 }, /digest not allowed/); 745 } 746 747 // sha512 should produce a valid signature. 748 const signature = createSign('sha512') 749 .update('foo') 750 .sign(key); 751 752 for (const pkey of [key, publicKey, publicPem]) { 753 const okay = createVerify('sha512') 754 .update('foo') 755 .verify(pkey, signature); 756 757 assert.ok(okay); 758 } 759 } 760 } 761} 762 763{ 764 // Exporting an encrypted private key requires a cipher 765 const privateKey = createPrivateKey(privatePem); 766 assert.throws(() => { 767 privateKey.export({ 768 format: 'pem', type: 'pkcs8', passphrase: 'super-secret' 769 }); 770 }, { 771 name: 'TypeError', 772 code: 'ERR_INVALID_ARG_VALUE', 773 message: "The property 'options.cipher' is invalid. Received undefined" 774 }); 775} 776 777{ 778 // SecretKeyObject export buffer format (default) 779 const buffer = Buffer.from('Hello World'); 780 const keyObject = createSecretKey(buffer); 781 assert.deepStrictEqual(keyObject.export(), buffer); 782 assert.deepStrictEqual(keyObject.export({}), buffer); 783 assert.deepStrictEqual(keyObject.export({ format: 'buffer' }), buffer); 784 assert.deepStrictEqual(keyObject.export({ format: undefined }), buffer); 785} 786 787{ 788 // Exporting an "oct" JWK from a SecretKeyObject 789 const buffer = Buffer.from('Hello World'); 790 const keyObject = createSecretKey(buffer); 791 assert.deepStrictEqual( 792 keyObject.export({ format: 'jwk' }), 793 { kty: 'oct', k: 'SGVsbG8gV29ybGQ' } 794 ); 795} 796 797{ 798 // Exporting a JWK unsupported curve EC key 799 const supported = ['prime256v1', 'secp256k1', 'secp384r1', 'secp521r1']; 800 // Find an unsupported curve regardless of whether a FIPS compliant crypto 801 // provider is currently in use. 802 const namedCurve = getCurves().find((curve) => !supported.includes(curve)); 803 assert(namedCurve); 804 const keyPair = generateKeyPairSync('ec', { namedCurve }); 805 const { publicKey, privateKey } = keyPair; 806 assert.throws( 807 () => publicKey.export({ format: 'jwk' }), 808 { 809 code: 'ERR_CRYPTO_JWK_UNSUPPORTED_CURVE', 810 message: `Unsupported JWK EC curve: ${namedCurve}.` 811 }); 812 assert.throws( 813 () => privateKey.export({ format: 'jwk' }), 814 { 815 code: 'ERR_CRYPTO_JWK_UNSUPPORTED_CURVE', 816 message: `Unsupported JWK EC curve: ${namedCurve}.` 817 }); 818} 819 820{ 821 const first = Buffer.from('Hello'); 822 const second = Buffer.from('World'); 823 const keyObject = createSecretKey(first); 824 assert(createSecretKey(first).equals(createSecretKey(first))); 825 assert(!createSecretKey(first).equals(createSecretKey(second))); 826 827 assert.throws(() => keyObject.equals(0), { 828 name: 'TypeError', 829 code: 'ERR_INVALID_ARG_TYPE', 830 message: 'The "otherKeyObject" argument must be an instance of KeyObject. Received type number (0)' 831 }); 832 833 assert(keyObject.equals(keyObject)); 834 assert(!keyObject.equals(createPublicKey(publicPem))); 835 assert(!keyObject.equals(createPrivateKey(privatePem))); 836} 837 838{ 839 const first = generateKeyPairSync('ed25519'); 840 const second = generateKeyPairSync('ed25519'); 841 const secret = generateKeySync('aes', { length: 128 }); 842 843 assert(first.publicKey.equals(first.publicKey)); 844 assert(first.publicKey.equals(createPublicKey( 845 first.publicKey.export({ format: 'pem', type: 'spki' })))); 846 assert(!first.publicKey.equals(second.publicKey)); 847 assert(!first.publicKey.equals(second.privateKey)); 848 assert(!first.publicKey.equals(secret)); 849 850 assert(first.privateKey.equals(first.privateKey)); 851 assert(first.privateKey.equals(createPrivateKey( 852 first.privateKey.export({ format: 'pem', type: 'pkcs8' })))); 853 assert(!first.privateKey.equals(second.privateKey)); 854 assert(!first.privateKey.equals(second.publicKey)); 855 assert(!first.privateKey.equals(secret)); 856} 857 858{ 859 const first = generateKeyPairSync('ed25519'); 860 const second = generateKeyPairSync('ed448'); 861 862 assert(!first.publicKey.equals(second.publicKey)); 863 assert(!first.publicKey.equals(second.privateKey)); 864 assert(!first.privateKey.equals(second.privateKey)); 865 assert(!first.privateKey.equals(second.publicKey)); 866} 867 868{ 869 const first = createSecretKey(Buffer.alloc(0)); 870 const second = createSecretKey(new ArrayBuffer(0)); 871 const third = createSecretKey(Buffer.alloc(1)); 872 assert(first.equals(first)); 873 assert(first.equals(second)); 874 assert(!first.equals(third)); 875 assert(!third.equals(first)); 876} 877 878{ 879 // This should not cause a crash: https://github.com/nodejs/node/issues/44471 880 for (const key of ['', 'foo', null, undefined, true, Boolean]) { 881 assert.throws(() => { 882 createPublicKey({ key, format: 'jwk' }); 883 }, { code: 'ERR_INVALID_ARG_TYPE', message: /The "key\.key" property must be of type object/ }); 884 assert.throws(() => { 885 createPrivateKey({ key, format: 'jwk' }); 886 }, { code: 'ERR_INVALID_ARG_TYPE', message: /The "key\.key" property must be of type object/ }); 887 } 888} 889