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// Second OAKLEY group, see 10// https://github.com/nodejs/node-v0.x-archive/issues/2338 and 11// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc2412.html#anchor49 12const p = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' + 13 '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' + 14 '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 15 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; 16crypto.createDiffieHellman(p, 'hex'); 17 18// Confirm DH_check() results are exposed for optional examination. 19const bad_dh = crypto.createDiffieHellman('02', 'hex'); 20assert.notStrictEqual(bad_dh.verifyError, 0); 21 22const availableCurves = new Set(crypto.getCurves()); 23const availableHashes = new Set(crypto.getHashes()); 24 25// Oakley curves do not clean up ERR stack, it was causing unexpected failure 26// when accessing other OpenSSL APIs afterwards. 27if (availableCurves.has('Oakley-EC2N-3')) { 28 crypto.createECDH('Oakley-EC2N-3'); 29 crypto.createHash('sha256'); 30} 31 32// Test ECDH 33if (availableCurves.has('prime256v1') && availableCurves.has('secp256k1')) { 34 const ecdh1 = crypto.createECDH('prime256v1'); 35 const ecdh2 = crypto.createECDH('prime256v1'); 36 const key1 = ecdh1.generateKeys(); 37 const key2 = ecdh2.generateKeys('hex'); 38 const secret1 = ecdh1.computeSecret(key2, 'hex', 'base64'); 39 const secret2 = ecdh2.computeSecret(key1, 'latin1', 'buffer'); 40 41 assert.strictEqual(secret1, secret2.toString('base64')); 42 43 // Point formats 44 assert.strictEqual(ecdh1.getPublicKey('buffer', 'uncompressed')[0], 4); 45 let firstByte = ecdh1.getPublicKey('buffer', 'compressed')[0]; 46 assert(firstByte === 2 || firstByte === 3); 47 firstByte = ecdh1.getPublicKey('buffer', 'hybrid')[0]; 48 assert(firstByte === 6 || firstByte === 7); 49 // Format value should be string 50 51 assert.throws( 52 () => ecdh1.getPublicKey('buffer', 10), 53 { 54 code: 'ERR_CRYPTO_ECDH_INVALID_FORMAT', 55 name: 'TypeError', 56 message: 'Invalid ECDH format: 10' 57 }); 58 59 // ECDH should check that point is on curve 60 const ecdh3 = crypto.createECDH('secp256k1'); 61 const key3 = ecdh3.generateKeys(); 62 63 assert.throws( 64 () => ecdh2.computeSecret(key3, 'latin1', 'buffer'), 65 { 66 code: 'ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY', 67 name: 'Error', 68 message: 'Public key is not valid for specified curve' 69 }); 70 71 // ECDH should allow .setPrivateKey()/.setPublicKey() 72 const ecdh4 = crypto.createECDH('prime256v1'); 73 74 ecdh4.setPrivateKey(ecdh1.getPrivateKey()); 75 ecdh4.setPublicKey(ecdh1.getPublicKey()); 76 77 assert.throws(() => { 78 ecdh4.setPublicKey(ecdh3.getPublicKey()); 79 }, { message: 'Failed to convert Buffer to EC_POINT' }); 80 81 // Verify that we can use ECDH without having to use newly generated keys. 82 const ecdh5 = crypto.createECDH('secp256k1'); 83 84 // Verify errors are thrown when retrieving keys from an uninitialized object. 85 assert.throws(() => { 86 ecdh5.getPublicKey(); 87 }, /^Error: Failed to get ECDH public key$/); 88 89 assert.throws(() => { 90 ecdh5.getPrivateKey(); 91 }, /^Error: Failed to get ECDH private key$/); 92 93 // A valid private key for the secp256k1 curve. 94 const cafebabeKey = 'cafebabe'.repeat(8); 95 // Associated compressed and uncompressed public keys (points). 96 const cafebabePubPtComp = 97 '03672a31bfc59d3f04548ec9b7daeeba2f61814e8ccc40448045007f5479f693a3'; 98 const cafebabePubPtUnComp = 99 '04672a31bfc59d3f04548ec9b7daeeba2f61814e8ccc40448045007f5479f693a3' + 100 '2e02c7f93d13dc2732b760ca377a5897b9dd41a1c1b29dc0442fdce6d0a04d1d'; 101 ecdh5.setPrivateKey(cafebabeKey, 'hex'); 102 assert.strictEqual(ecdh5.getPrivateKey('hex'), cafebabeKey); 103 // Show that the public point (key) is generated while setting the 104 // private key. 105 assert.strictEqual(ecdh5.getPublicKey('hex'), cafebabePubPtUnComp); 106 107 // Compressed and uncompressed public points/keys for other party's 108 // private key. 109 // 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF 110 const peerPubPtComp = 111 '02c6b754b20826eb925e052ee2c25285b162b51fdca732bcf67e39d647fb6830ae'; 112 const peerPubPtUnComp = 113 '04c6b754b20826eb925e052ee2c25285b162b51fdca732bcf67e39d647fb6830ae' + 114 'b651944a574a362082a77e3f2b5d9223eb54d7f2f76846522bf75f3bedb8178e'; 115 116 const sharedSecret = 117 '1da220b5329bbe8bfd19ceef5a5898593f411a6f12ea40f2a8eead9a5cf59970'; 118 119 assert.strictEqual(ecdh5.computeSecret(peerPubPtComp, 'hex', 'hex'), 120 sharedSecret); 121 assert.strictEqual(ecdh5.computeSecret(peerPubPtUnComp, 'hex', 'hex'), 122 sharedSecret); 123 124 // Verify that we still have the same key pair as before the computation. 125 assert.strictEqual(ecdh5.getPrivateKey('hex'), cafebabeKey); 126 assert.strictEqual(ecdh5.getPublicKey('hex'), cafebabePubPtUnComp); 127 128 // Verify setting and getting compressed and non-compressed serializations. 129 ecdh5.setPublicKey(cafebabePubPtComp, 'hex'); 130 assert.strictEqual(ecdh5.getPublicKey('hex'), cafebabePubPtUnComp); 131 assert.strictEqual( 132 ecdh5.getPublicKey('hex', 'compressed'), 133 cafebabePubPtComp 134 ); 135 ecdh5.setPublicKey(cafebabePubPtUnComp, 'hex'); 136 assert.strictEqual(ecdh5.getPublicKey('hex'), cafebabePubPtUnComp); 137 assert.strictEqual( 138 ecdh5.getPublicKey('hex', 'compressed'), 139 cafebabePubPtComp 140 ); 141 142 // Show why allowing the public key to be set on this type 143 // does not make sense. 144 ecdh5.setPublicKey(peerPubPtComp, 'hex'); 145 assert.strictEqual(ecdh5.getPublicKey('hex'), peerPubPtUnComp); 146 assert.throws(() => { 147 // Error because the public key does not match the private key anymore. 148 ecdh5.computeSecret(peerPubPtComp, 'hex', 'hex'); 149 }, /Invalid key pair/); 150 151 // Set to a valid key to show that later attempts to set an invalid key are 152 // rejected. 153 ecdh5.setPrivateKey(cafebabeKey, 'hex'); 154 155 // Some invalid private keys for the secp256k1 curve. 156 const errMessage = /Private key is not valid for specified curve/; 157 ['0000000000000000000000000000000000000000000000000000000000000000', 158 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 159 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 160 ].forEach((element) => { 161 assert.throws(() => { 162 ecdh5.setPrivateKey(element, 'hex'); 163 }, errMessage); 164 // Verify object state did not change. 165 assert.strictEqual(ecdh5.getPrivateKey('hex'), cafebabeKey); 166 }); 167} 168 169// Use of invalid keys was not cleaning up ERR stack, and was causing 170// unexpected failure in subsequent signing operations. 171if (availableCurves.has('prime256v1') && availableHashes.has('sha256')) { 172 const curve = crypto.createECDH('prime256v1'); 173 const invalidKey = Buffer.alloc(65); 174 invalidKey.fill('\0'); 175 curve.generateKeys(); 176 assert.throws( 177 () => curve.computeSecret(invalidKey), 178 { 179 code: 'ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY', 180 name: 'Error', 181 message: 'Public key is not valid for specified curve' 182 }); 183 // Check that signing operations are not impacted by the above error. 184 const ecPrivateKey = 185 '-----BEGIN EC PRIVATE KEY-----\n' + 186 'MHcCAQEEIF+jnWY1D5kbVYDNvxxo/Y+ku2uJPDwS0r/VuPZQrjjVoAoGCCqGSM49\n' + 187 'AwEHoUQDQgAEurOxfSxmqIRYzJVagdZfMMSjRNNhB8i3mXyIMq704m2m52FdfKZ2\n' + 188 'pQhByd5eyj3lgZ7m7jbchtdgyOF8Io/1ng==\n' + 189 '-----END EC PRIVATE KEY-----'; 190 crypto.createSign('SHA256').sign(ecPrivateKey); 191} 192