1'use strict'; 2const common = require('../common'); 3if (!common.hasCrypto) 4 common.skip('missing crypto'); 5 6const assert = require('assert'); 7const crypto = require('crypto'); 8 9// Test Diffie-Hellman with two parties sharing a secret, 10// using various encodings as we go along 11const dh1 = crypto.createDiffieHellman(common.hasFipsCrypto ? 1024 : 256); 12const p1 = dh1.getPrime('buffer'); 13const dh2 = crypto.createDiffieHellman(p1, 'buffer'); 14let key1 = dh1.generateKeys(); 15let key2 = dh2.generateKeys('hex'); 16let secret1 = dh1.computeSecret(key2, 'hex', 'base64'); 17let secret2 = dh2.computeSecret(key1, 'latin1', 'buffer'); 18 19assert.strictEqual(secret2.toString('base64'), secret1); 20assert.strictEqual(dh1.verifyError, 0); 21assert.strictEqual(dh2.verifyError, 0); 22 23// https://github.com/nodejs/node/issues/32738 24// XXX(bnoordhuis) validateInt32() throwing ERR_OUT_OF_RANGE and RangeError 25// instead of ERR_INVALID_ARG_TYPE and TypeError is questionable, IMO. 26assert.throws(() => crypto.createDiffieHellman(13.37), { 27 code: 'ERR_OUT_OF_RANGE', 28 name: 'RangeError', 29 message: 'The value of "sizeOrKey" is out of range. ' + 30 'It must be an integer. Received 13.37', 31}); 32 33assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), { 34 code: 'ERR_OUT_OF_RANGE', 35 name: 'RangeError', 36 message: 'The value of "generator" is out of range. ' + 37 'It must be an integer. Received 13.37', 38}); 39 40for (const bits of [-1, 0, 1]) { 41 assert.throws(() => crypto.createDiffieHellman(bits), { 42 code: 'ERR_OSSL_BN_BITS_TOO_SMALL', 43 name: 'Error', 44 message: /bits too small/, 45 }); 46} 47 48// Through a fluke of history, g=0 defaults to DH_GENERATOR (2). 49{ 50 const g = 0; 51 crypto.createDiffieHellman('abcdef', g); 52 crypto.createDiffieHellman('abcdef', 'hex', g); 53} 54 55for (const g of [-1, 1]) { 56 const ex = { 57 code: 'ERR_OSSL_DH_BAD_GENERATOR', 58 name: 'Error', 59 message: /bad generator/, 60 }; 61 assert.throws(() => crypto.createDiffieHellman('abcdef', g), ex); 62 assert.throws(() => crypto.createDiffieHellman('abcdef', 'hex', g), ex); 63} 64 65crypto.createDiffieHellman('abcdef', Buffer.from([2])); // OK 66 67for (const g of [Buffer.from([]), 68 Buffer.from([0]), 69 Buffer.from([1])]) { 70 const ex = { 71 code: 'ERR_OSSL_DH_BAD_GENERATOR', 72 name: 'Error', 73 message: /bad generator/, 74 }; 75 assert.throws(() => crypto.createDiffieHellman('abcdef', g), ex); 76 assert.throws(() => crypto.createDiffieHellman('abcdef', 'hex', g), ex); 77} 78 79{ 80 const DiffieHellman = crypto.DiffieHellman; 81 const dh = DiffieHellman(p1, 'buffer'); 82 assert(dh instanceof DiffieHellman, 'DiffieHellman is expected to return a ' + 83 'new instance when called without `new`'); 84} 85 86{ 87 const DiffieHellmanGroup = crypto.DiffieHellmanGroup; 88 const dhg = DiffieHellmanGroup('modp5'); 89 assert(dhg instanceof DiffieHellmanGroup, 'DiffieHellmanGroup is expected ' + 90 'to return a new instance when ' + 91 'called without `new`'); 92} 93 94{ 95 const ECDH = crypto.ECDH; 96 const ecdh = ECDH('prime256v1'); 97 assert(ecdh instanceof ECDH, 'ECDH is expected to return a new instance ' + 98 'when called without `new`'); 99} 100 101[ 102 [0x1, 0x2], 103 () => { }, 104 /abc/, 105 {}, 106].forEach((input) => { 107 assert.throws( 108 () => crypto.createDiffieHellman(input), 109 { 110 code: 'ERR_INVALID_ARG_TYPE', 111 name: 'TypeError', 112 message: 'The "sizeOrKey" argument must be one of type number or string' + 113 ' or an instance of Buffer, TypedArray, or DataView.' + 114 common.invalidArgTypeHelper(input) 115 } 116 ); 117}); 118 119// Create "another dh1" using generated keys from dh1, 120// and compute secret again 121const dh3 = crypto.createDiffieHellman(p1, 'buffer'); 122const privkey1 = dh1.getPrivateKey(); 123dh3.setPublicKey(key1); 124dh3.setPrivateKey(privkey1); 125 126assert.deepStrictEqual(dh1.getPrime(), dh3.getPrime()); 127assert.deepStrictEqual(dh1.getGenerator(), dh3.getGenerator()); 128assert.deepStrictEqual(dh1.getPublicKey(), dh3.getPublicKey()); 129assert.deepStrictEqual(dh1.getPrivateKey(), dh3.getPrivateKey()); 130assert.strictEqual(dh3.verifyError, 0); 131 132const secret3 = dh3.computeSecret(key2, 'hex', 'base64'); 133 134assert.strictEqual(secret1, secret3); 135 136// computeSecret works without a public key set at all. 137const dh4 = crypto.createDiffieHellman(p1, 'buffer'); 138dh4.setPrivateKey(privkey1); 139 140assert.deepStrictEqual(dh1.getPrime(), dh4.getPrime()); 141assert.deepStrictEqual(dh1.getGenerator(), dh4.getGenerator()); 142assert.deepStrictEqual(dh1.getPrivateKey(), dh4.getPrivateKey()); 143assert.strictEqual(dh4.verifyError, 0); 144 145const secret4 = dh4.computeSecret(key2, 'hex', 'base64'); 146 147assert.strictEqual(secret1, secret4); 148 149const wrongBlockLength = { 150 message: 'error:0606506D:digital envelope' + 151 ' routines:EVP_DecryptFinal_ex:wrong final block length', 152 code: 'ERR_OSSL_EVP_WRONG_FINAL_BLOCK_LENGTH', 153 library: 'digital envelope routines', 154 reason: 'wrong final block length' 155}; 156 157// Run this one twice to make sure that the dh3 clears its error properly 158{ 159 const c = crypto.createDecipheriv('aes-128-ecb', crypto.randomBytes(16), ''); 160 assert.throws(() => { 161 c.final('utf8'); 162 }, wrongBlockLength); 163} 164 165{ 166 const c = crypto.createDecipheriv('aes-128-ecb', crypto.randomBytes(16), ''); 167 assert.throws(() => { 168 c.final('utf8'); 169 }, wrongBlockLength); 170} 171 172assert.throws(() => { 173 dh3.computeSecret(''); 174}, { message: 'Supplied key is too small' }); 175 176// Create a shared using a DH group. 177const alice = crypto.createDiffieHellmanGroup('modp5'); 178const bob = crypto.createDiffieHellmanGroup('modp5'); 179alice.generateKeys(); 180bob.generateKeys(); 181const aSecret = alice.computeSecret(bob.getPublicKey()).toString('hex'); 182const bSecret = bob.computeSecret(alice.getPublicKey()).toString('hex'); 183assert.strictEqual(aSecret, bSecret); 184 185// Ensure specific generator (buffer) works as expected. 186// The values below (modp2/modp2buf) are for a 1024 bits long prime from 187// RFC 2412 E.2, see https://tools.ietf.org/html/rfc2412. */ 188const modp2 = crypto.createDiffieHellmanGroup('modp2'); 189const modp2buf = Buffer.from([ 190 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 191 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 192 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 193 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 194 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, 0xef, 0x95, 195 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 196 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 197 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 198 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 199 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, 0xee, 0x38, 0x6b, 0xfb, 200 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 201 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81, 202 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 203]); 204 205{ 206 const exmodp2 = crypto.createDiffieHellman(modp2buf, Buffer.from([2])); 207 modp2.generateKeys(); 208 exmodp2.generateKeys(); 209 const modp2Secret = modp2.computeSecret(exmodp2.getPublicKey()) 210 .toString('hex'); 211 const exmodp2Secret = exmodp2.computeSecret(modp2.getPublicKey()) 212 .toString('hex'); 213 assert.strictEqual(modp2Secret, exmodp2Secret); 214} 215 216for (const buf of [modp2buf, ...common.getArrayBufferViews(modp2buf)]) { 217 // Ensure specific generator (string with encoding) works as expected with 218 // any ArrayBufferViews as the first argument to createDiffieHellman(). 219 const exmodp2 = crypto.createDiffieHellman(buf, '02', 'hex'); 220 exmodp2.generateKeys(); 221 const modp2Secret = modp2.computeSecret(exmodp2.getPublicKey()) 222 .toString('hex'); 223 const exmodp2Secret = exmodp2.computeSecret(modp2.getPublicKey()) 224 .toString('hex'); 225 assert.strictEqual(modp2Secret, exmodp2Secret); 226} 227 228{ 229 // Ensure specific generator (string without encoding) works as expected. 230 const exmodp2 = crypto.createDiffieHellman(modp2buf, '\x02'); 231 exmodp2.generateKeys(); 232 const modp2Secret = modp2.computeSecret(exmodp2.getPublicKey()) 233 .toString('hex'); 234 const exmodp2Secret = exmodp2.computeSecret(modp2.getPublicKey()) 235 .toString('hex'); 236 assert.strictEqual(modp2Secret, exmodp2Secret); 237} 238 239{ 240 // Ensure specific generator (numeric) works as expected. 241 const exmodp2 = crypto.createDiffieHellman(modp2buf, 2); 242 exmodp2.generateKeys(); 243 const modp2Secret = modp2.computeSecret(exmodp2.getPublicKey()) 244 .toString('hex'); 245 const exmodp2Secret = exmodp2.computeSecret(modp2.getPublicKey()) 246 .toString('hex'); 247 assert.strictEqual(modp2Secret, exmodp2Secret); 248} 249 250// Second OAKLEY group, see 251// https://github.com/nodejs/node-v0.x-archive/issues/2338 and 252// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc2412.html#anchor49 253const p = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' + 254 '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' + 255 '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 256 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; 257crypto.createDiffieHellman(p, 'hex'); 258 259// Confirm DH_check() results are exposed for optional examination. 260const bad_dh = crypto.createDiffieHellman('02', 'hex'); 261assert.notStrictEqual(bad_dh.verifyError, 0); 262 263const availableCurves = new Set(crypto.getCurves()); 264const availableHashes = new Set(crypto.getHashes()); 265 266// Oakley curves do not clean up ERR stack, it was causing unexpected failure 267// when accessing other OpenSSL APIs afterwards. 268if (availableCurves.has('Oakley-EC2N-3')) { 269 crypto.createECDH('Oakley-EC2N-3'); 270 crypto.createHash('sha256'); 271} 272 273// Test ECDH 274if (availableCurves.has('prime256v1') && availableCurves.has('secp256k1')) { 275 const ecdh1 = crypto.createECDH('prime256v1'); 276 const ecdh2 = crypto.createECDH('prime256v1'); 277 key1 = ecdh1.generateKeys(); 278 key2 = ecdh2.generateKeys('hex'); 279 secret1 = ecdh1.computeSecret(key2, 'hex', 'base64'); 280 secret2 = ecdh2.computeSecret(key1, 'latin1', 'buffer'); 281 282 assert.strictEqual(secret1, secret2.toString('base64')); 283 284 // Point formats 285 assert.strictEqual(ecdh1.getPublicKey('buffer', 'uncompressed')[0], 4); 286 let firstByte = ecdh1.getPublicKey('buffer', 'compressed')[0]; 287 assert(firstByte === 2 || firstByte === 3); 288 firstByte = ecdh1.getPublicKey('buffer', 'hybrid')[0]; 289 assert(firstByte === 6 || firstByte === 7); 290 // Format value should be string 291 292 assert.throws( 293 () => ecdh1.getPublicKey('buffer', 10), 294 { 295 code: 'ERR_CRYPTO_ECDH_INVALID_FORMAT', 296 name: 'TypeError', 297 message: 'Invalid ECDH format: 10' 298 }); 299 300 // ECDH should check that point is on curve 301 const ecdh3 = crypto.createECDH('secp256k1'); 302 const key3 = ecdh3.generateKeys(); 303 304 assert.throws( 305 () => ecdh2.computeSecret(key3, 'latin1', 'buffer'), 306 { 307 code: 'ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY', 308 name: 'Error', 309 message: 'Public key is not valid for specified curve' 310 }); 311 312 // ECDH should allow .setPrivateKey()/.setPublicKey() 313 const ecdh4 = crypto.createECDH('prime256v1'); 314 315 ecdh4.setPrivateKey(ecdh1.getPrivateKey()); 316 ecdh4.setPublicKey(ecdh1.getPublicKey()); 317 318 assert.throws(() => { 319 ecdh4.setPublicKey(ecdh3.getPublicKey()); 320 }, { message: 'Failed to convert Buffer to EC_POINT' }); 321 322 // Verify that we can use ECDH without having to use newly generated keys. 323 const ecdh5 = crypto.createECDH('secp256k1'); 324 325 // Verify errors are thrown when retrieving keys from an uninitialized object. 326 assert.throws(() => { 327 ecdh5.getPublicKey(); 328 }, /^Error: Failed to get ECDH public key$/); 329 330 assert.throws(() => { 331 ecdh5.getPrivateKey(); 332 }, /^Error: Failed to get ECDH private key$/); 333 334 // A valid private key for the secp256k1 curve. 335 const cafebabeKey = 'cafebabe'.repeat(8); 336 // Associated compressed and uncompressed public keys (points). 337 const cafebabePubPtComp = 338 '03672a31bfc59d3f04548ec9b7daeeba2f61814e8ccc40448045007f5479f693a3'; 339 const cafebabePubPtUnComp = 340 '04672a31bfc59d3f04548ec9b7daeeba2f61814e8ccc40448045007f5479f693a3' + 341 '2e02c7f93d13dc2732b760ca377a5897b9dd41a1c1b29dc0442fdce6d0a04d1d'; 342 ecdh5.setPrivateKey(cafebabeKey, 'hex'); 343 assert.strictEqual(ecdh5.getPrivateKey('hex'), cafebabeKey); 344 // Show that the public point (key) is generated while setting the 345 // private key. 346 assert.strictEqual(ecdh5.getPublicKey('hex'), cafebabePubPtUnComp); 347 348 // Compressed and uncompressed public points/keys for other party's 349 // private key. 350 // 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF 351 const peerPubPtComp = 352 '02c6b754b20826eb925e052ee2c25285b162b51fdca732bcf67e39d647fb6830ae'; 353 const peerPubPtUnComp = 354 '04c6b754b20826eb925e052ee2c25285b162b51fdca732bcf67e39d647fb6830ae' + 355 'b651944a574a362082a77e3f2b5d9223eb54d7f2f76846522bf75f3bedb8178e'; 356 357 const sharedSecret = 358 '1da220b5329bbe8bfd19ceef5a5898593f411a6f12ea40f2a8eead9a5cf59970'; 359 360 assert.strictEqual(ecdh5.computeSecret(peerPubPtComp, 'hex', 'hex'), 361 sharedSecret); 362 assert.strictEqual(ecdh5.computeSecret(peerPubPtUnComp, 'hex', 'hex'), 363 sharedSecret); 364 365 // Verify that we still have the same key pair as before the computation. 366 assert.strictEqual(ecdh5.getPrivateKey('hex'), cafebabeKey); 367 assert.strictEqual(ecdh5.getPublicKey('hex'), cafebabePubPtUnComp); 368 369 // Verify setting and getting compressed and non-compressed serializations. 370 ecdh5.setPublicKey(cafebabePubPtComp, 'hex'); 371 assert.strictEqual(ecdh5.getPublicKey('hex'), cafebabePubPtUnComp); 372 assert.strictEqual( 373 ecdh5.getPublicKey('hex', 'compressed'), 374 cafebabePubPtComp 375 ); 376 ecdh5.setPublicKey(cafebabePubPtUnComp, 'hex'); 377 assert.strictEqual(ecdh5.getPublicKey('hex'), cafebabePubPtUnComp); 378 assert.strictEqual( 379 ecdh5.getPublicKey('hex', 'compressed'), 380 cafebabePubPtComp 381 ); 382 383 // Show why allowing the public key to be set on this type 384 // does not make sense. 385 ecdh5.setPublicKey(peerPubPtComp, 'hex'); 386 assert.strictEqual(ecdh5.getPublicKey('hex'), peerPubPtUnComp); 387 assert.throws(() => { 388 // Error because the public key does not match the private key anymore. 389 ecdh5.computeSecret(peerPubPtComp, 'hex', 'hex'); 390 }, /^Error: Invalid key pair$/); 391 392 // Set to a valid key to show that later attempts to set an invalid key are 393 // rejected. 394 ecdh5.setPrivateKey(cafebabeKey, 'hex'); 395 396 // Some invalid private keys for the secp256k1 curve. 397 const errMessage = /^Error: Private key is not valid for specified curve\.$/; 398 ['0000000000000000000000000000000000000000000000000000000000000000', 399 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 400 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 401 ].forEach((element) => { 402 assert.throws(() => { 403 ecdh5.setPrivateKey(element, 'hex'); 404 }, errMessage); 405 // Verify object state did not change. 406 assert.strictEqual(ecdh5.getPrivateKey('hex'), cafebabeKey); 407 }); 408} 409 410// Use of invalid keys was not cleaning up ERR stack, and was causing 411// unexpected failure in subsequent signing operations. 412if (availableCurves.has('prime256v1') && availableHashes.has('sha256')) { 413 const curve = crypto.createECDH('prime256v1'); 414 const invalidKey = Buffer.alloc(65); 415 invalidKey.fill('\0'); 416 curve.generateKeys(); 417 assert.throws( 418 () => curve.computeSecret(invalidKey), 419 { 420 code: 'ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY', 421 name: 'Error', 422 message: 'Public key is not valid for specified curve' 423 }); 424 // Check that signing operations are not impacted by the above error. 425 const ecPrivateKey = 426 '-----BEGIN EC PRIVATE KEY-----\n' + 427 'MHcCAQEEIF+jnWY1D5kbVYDNvxxo/Y+ku2uJPDwS0r/VuPZQrjjVoAoGCCqGSM49\n' + 428 'AwEHoUQDQgAEurOxfSxmqIRYzJVagdZfMMSjRNNhB8i3mXyIMq704m2m52FdfKZ2\n' + 429 'pQhByd5eyj3lgZ7m7jbchtdgyOF8Io/1ng==\n' + 430 '-----END EC PRIVATE KEY-----'; 431 crypto.createSign('SHA256').sign(ecPrivateKey); 432} 433 434// Invalid test: curve argument is undefined 435assert.throws( 436 () => crypto.createECDH(), 437 { 438 code: 'ERR_INVALID_ARG_TYPE', 439 name: 'TypeError', 440 message: 'The "curve" argument must be of type string. ' + 441 'Received undefined' 442 }); 443 444assert.throws( 445 function() { 446 crypto.getDiffieHellman('unknown-group'); 447 }, 448 { 449 name: 'Error', 450 code: 'ERR_CRYPTO_UNKNOWN_DH_GROUP', 451 message: 'Unknown DH group' 452 }, 453 'crypto.getDiffieHellman(\'unknown-group\') ' + 454 'failed to throw the expected error.' 455); 456assert.throws( 457 function() { 458 crypto.getDiffieHellman('modp1').setPrivateKey(''); 459 }, 460 new RegExp('^TypeError: crypto\\.getDiffieHellman\\(\\.\\.\\.\\)\\.' + 461 'setPrivateKey is not a function$'), 462 'crypto.getDiffieHellman(\'modp1\').setPrivateKey(\'\') ' + 463 'failed to throw the expected error.' 464); 465assert.throws( 466 function() { 467 crypto.getDiffieHellman('modp1').setPublicKey(''); 468 }, 469 new RegExp('^TypeError: crypto\\.getDiffieHellman\\(\\.\\.\\.\\)\\.' + 470 'setPublicKey is not a function$'), 471 'crypto.getDiffieHellman(\'modp1\').setPublicKey(\'\') ' + 472 'failed to throw the expected error.' 473); 474assert.throws( 475 () => crypto.createDiffieHellman('', true), 476 { 477 code: 'ERR_INVALID_ARG_TYPE' 478 } 479); 480