1'use strict'; 2 3const { 4 ObjectDefineProperty, 5 Set 6} = primordials; 7 8const { Buffer } = require('buffer'); 9const { 10 ERR_CRYPTO_ECDH_INVALID_FORMAT, 11 ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY, 12 ERR_CRYPTO_INCOMPATIBLE_KEY, 13 ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE, 14 ERR_INVALID_ARG_TYPE, 15 ERR_INVALID_OPT_VALUE 16} = require('internal/errors').codes; 17const { 18 validateString, 19 validateInt32, 20} = require('internal/validators'); 21const { isArrayBufferView } = require('internal/util/types'); 22const { KeyObject } = require('internal/crypto/keys'); 23const { 24 getDefaultEncoding, 25 kHandle, 26 toBuf 27} = require('internal/crypto/util'); 28const { 29 DiffieHellman: _DiffieHellman, 30 DiffieHellmanGroup: _DiffieHellmanGroup, 31 ECDH: _ECDH, 32 ECDHConvertKey: _ECDHConvertKey, 33 statelessDH 34} = internalBinding('crypto'); 35const { 36 POINT_CONVERSION_COMPRESSED, 37 POINT_CONVERSION_HYBRID, 38 POINT_CONVERSION_UNCOMPRESSED 39} = internalBinding('constants').crypto; 40 41const DH_GENERATOR = 2; 42 43function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { 44 if (!(this instanceof DiffieHellman)) 45 return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding); 46 47 if (typeof sizeOrKey !== 'number' && 48 typeof sizeOrKey !== 'string' && 49 !isArrayBufferView(sizeOrKey)) { 50 throw new ERR_INVALID_ARG_TYPE( 51 'sizeOrKey', 52 ['number', 'string', 'Buffer', 'TypedArray', 'DataView'], 53 sizeOrKey 54 ); 55 } 56 57 // Sizes < 0 don't make sense but they _are_ accepted (and subsequently 58 // rejected with ERR_OSSL_BN_BITS_TOO_SMALL) by OpenSSL. The glue code 59 // in node_crypto.cc accepts values that are IsInt32() for that reason 60 // and that's why we do that here too. 61 if (typeof sizeOrKey === 'number') 62 validateInt32(sizeOrKey, 'sizeOrKey'); 63 64 if (keyEncoding && !Buffer.isEncoding(keyEncoding) && 65 keyEncoding !== 'buffer') { 66 genEncoding = generator; 67 generator = keyEncoding; 68 keyEncoding = false; 69 } 70 71 const encoding = getDefaultEncoding(); 72 keyEncoding = keyEncoding || encoding; 73 genEncoding = genEncoding || encoding; 74 75 if (typeof sizeOrKey !== 'number') 76 sizeOrKey = toBuf(sizeOrKey, keyEncoding); 77 78 if (!generator) 79 generator = DH_GENERATOR; 80 else if (typeof generator === 'number') 81 validateInt32(generator, 'generator'); 82 else 83 generator = toBuf(generator, genEncoding); 84 85 this[kHandle] = new _DiffieHellman(sizeOrKey, generator); 86 ObjectDefineProperty(this, 'verifyError', { 87 enumerable: true, 88 value: this[kHandle].verifyError, 89 writable: false 90 }); 91} 92 93 94function DiffieHellmanGroup(name) { 95 if (!(this instanceof DiffieHellmanGroup)) 96 return new DiffieHellmanGroup(name); 97 this[kHandle] = new _DiffieHellmanGroup(name); 98 ObjectDefineProperty(this, 'verifyError', { 99 enumerable: true, 100 value: this[kHandle].verifyError, 101 writable: false 102 }); 103} 104 105 106DiffieHellmanGroup.prototype.generateKeys = 107 DiffieHellman.prototype.generateKeys = 108 dhGenerateKeys; 109 110function dhGenerateKeys(encoding) { 111 const keys = this[kHandle].generateKeys(); 112 encoding = encoding || getDefaultEncoding(); 113 return encode(keys, encoding); 114} 115 116 117DiffieHellmanGroup.prototype.computeSecret = 118 DiffieHellman.prototype.computeSecret = 119 dhComputeSecret; 120 121function dhComputeSecret(key, inEnc, outEnc) { 122 const encoding = getDefaultEncoding(); 123 inEnc = inEnc || encoding; 124 outEnc = outEnc || encoding; 125 const ret = this[kHandle].computeSecret(toBuf(key, inEnc)); 126 if (typeof ret === 'string') 127 throw new ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY(); 128 return encode(ret, outEnc); 129} 130 131 132DiffieHellmanGroup.prototype.getPrime = 133 DiffieHellman.prototype.getPrime = 134 dhGetPrime; 135 136function dhGetPrime(encoding) { 137 const prime = this[kHandle].getPrime(); 138 encoding = encoding || getDefaultEncoding(); 139 return encode(prime, encoding); 140} 141 142 143DiffieHellmanGroup.prototype.getGenerator = 144 DiffieHellman.prototype.getGenerator = 145 dhGetGenerator; 146 147function dhGetGenerator(encoding) { 148 const generator = this[kHandle].getGenerator(); 149 encoding = encoding || getDefaultEncoding(); 150 return encode(generator, encoding); 151} 152 153 154DiffieHellmanGroup.prototype.getPublicKey = 155 DiffieHellman.prototype.getPublicKey = 156 dhGetPublicKey; 157 158function dhGetPublicKey(encoding) { 159 const key = this[kHandle].getPublicKey(); 160 encoding = encoding || getDefaultEncoding(); 161 return encode(key, encoding); 162} 163 164 165DiffieHellmanGroup.prototype.getPrivateKey = 166 DiffieHellman.prototype.getPrivateKey = 167 dhGetPrivateKey; 168 169function dhGetPrivateKey(encoding) { 170 const key = this[kHandle].getPrivateKey(); 171 encoding = encoding || getDefaultEncoding(); 172 return encode(key, encoding); 173} 174 175 176DiffieHellman.prototype.setPublicKey = function setPublicKey(key, encoding) { 177 encoding = encoding || getDefaultEncoding(); 178 this[kHandle].setPublicKey(toBuf(key, encoding)); 179 return this; 180}; 181 182 183DiffieHellman.prototype.setPrivateKey = function setPrivateKey(key, encoding) { 184 encoding = encoding || getDefaultEncoding(); 185 this[kHandle].setPrivateKey(toBuf(key, encoding)); 186 return this; 187}; 188 189 190function ECDH(curve) { 191 if (!(this instanceof ECDH)) 192 return new ECDH(curve); 193 194 validateString(curve, 'curve'); 195 this[kHandle] = new _ECDH(curve); 196} 197 198ECDH.prototype.computeSecret = DiffieHellman.prototype.computeSecret; 199ECDH.prototype.setPrivateKey = DiffieHellman.prototype.setPrivateKey; 200ECDH.prototype.setPublicKey = DiffieHellman.prototype.setPublicKey; 201ECDH.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey; 202 203ECDH.prototype.generateKeys = function generateKeys(encoding, format) { 204 this[kHandle].generateKeys(); 205 206 return this.getPublicKey(encoding, format); 207}; 208 209ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { 210 const f = getFormat(format); 211 const key = this[kHandle].getPublicKey(f); 212 encoding = encoding || getDefaultEncoding(); 213 return encode(key, encoding); 214}; 215 216ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { 217 if (typeof key !== 'string' && !isArrayBufferView(key)) { 218 throw new ERR_INVALID_ARG_TYPE( 219 'key', 220 ['string', 'Buffer', 'TypedArray', 'DataView'], 221 key 222 ); 223 } 224 225 validateString(curve, 'curve'); 226 227 const encoding = getDefaultEncoding(); 228 inEnc = inEnc || encoding; 229 outEnc = outEnc || encoding; 230 const f = getFormat(format); 231 const convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f); 232 return encode(convertedKey, outEnc); 233}; 234 235function encode(buffer, encoding) { 236 if (encoding && encoding !== 'buffer') 237 buffer = buffer.toString(encoding); 238 return buffer; 239} 240 241function getFormat(format) { 242 if (format) { 243 if (format === 'compressed') 244 return POINT_CONVERSION_COMPRESSED; 245 if (format === 'hybrid') 246 return POINT_CONVERSION_HYBRID; 247 if (format !== 'uncompressed') 248 throw new ERR_CRYPTO_ECDH_INVALID_FORMAT(format); 249 } 250 return POINT_CONVERSION_UNCOMPRESSED; 251} 252 253const dhEnabledKeyTypes = new Set(['dh', 'ec', 'x448', 'x25519']); 254 255function diffieHellman(options) { 256 if (typeof options !== 'object') 257 throw new ERR_INVALID_ARG_TYPE('options', 'object', options); 258 259 const { privateKey, publicKey } = options; 260 if (!(privateKey instanceof KeyObject)) 261 throw new ERR_INVALID_OPT_VALUE('privateKey', privateKey); 262 263 if (!(publicKey instanceof KeyObject)) 264 throw new ERR_INVALID_OPT_VALUE('publicKey', publicKey); 265 266 if (privateKey.type !== 'private') 267 throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(privateKey.type, 'private'); 268 269 if (publicKey.type !== 'public' && publicKey.type !== 'private') { 270 throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(publicKey.type, 271 'private or public'); 272 } 273 274 const privateType = privateKey.asymmetricKeyType; 275 const publicType = publicKey.asymmetricKeyType; 276 if (privateType !== publicType || !dhEnabledKeyTypes.has(privateType)) { 277 throw new ERR_CRYPTO_INCOMPATIBLE_KEY('key types for Diffie-Hellman', 278 `${privateType} and ${publicType}`); 279 } 280 281 return statelessDH(privateKey[kHandle], publicKey[kHandle]); 282} 283 284module.exports = { 285 DiffieHellman, 286 DiffieHellmanGroup, 287 ECDH, 288 diffieHellman 289}; 290