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 if (generator !== true) { 83 generator = toBuf(generator, genEncoding); 84 } else { 85 throw new ERR_INVALID_ARG_TYPE( 86 'generator', 87 ['number', 'string', 'ArrayBuffer', 'Buffer', 'TypedArray', 'DataView'], 88 generator 89 ); 90 } 91 92 93 this[kHandle] = new _DiffieHellman(sizeOrKey, generator); 94 ObjectDefineProperty(this, 'verifyError', { 95 enumerable: true, 96 value: this[kHandle].verifyError, 97 writable: false 98 }); 99} 100 101 102function DiffieHellmanGroup(name) { 103 if (!(this instanceof DiffieHellmanGroup)) 104 return new DiffieHellmanGroup(name); 105 this[kHandle] = new _DiffieHellmanGroup(name); 106 ObjectDefineProperty(this, 'verifyError', { 107 enumerable: true, 108 value: this[kHandle].verifyError, 109 writable: false 110 }); 111} 112 113 114DiffieHellmanGroup.prototype.generateKeys = 115 DiffieHellman.prototype.generateKeys = 116 dhGenerateKeys; 117 118function dhGenerateKeys(encoding) { 119 const keys = this[kHandle].generateKeys(); 120 encoding = encoding || getDefaultEncoding(); 121 return encode(keys, encoding); 122} 123 124 125DiffieHellmanGroup.prototype.computeSecret = 126 DiffieHellman.prototype.computeSecret = 127 dhComputeSecret; 128 129function dhComputeSecret(key, inEnc, outEnc) { 130 const encoding = getDefaultEncoding(); 131 inEnc = inEnc || encoding; 132 outEnc = outEnc || encoding; 133 const ret = this[kHandle].computeSecret(toBuf(key, inEnc)); 134 if (typeof ret === 'string') 135 throw new ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY(); 136 return encode(ret, outEnc); 137} 138 139 140DiffieHellmanGroup.prototype.getPrime = 141 DiffieHellman.prototype.getPrime = 142 dhGetPrime; 143 144function dhGetPrime(encoding) { 145 const prime = this[kHandle].getPrime(); 146 encoding = encoding || getDefaultEncoding(); 147 return encode(prime, encoding); 148} 149 150 151DiffieHellmanGroup.prototype.getGenerator = 152 DiffieHellman.prototype.getGenerator = 153 dhGetGenerator; 154 155function dhGetGenerator(encoding) { 156 const generator = this[kHandle].getGenerator(); 157 encoding = encoding || getDefaultEncoding(); 158 return encode(generator, encoding); 159} 160 161 162DiffieHellmanGroup.prototype.getPublicKey = 163 DiffieHellman.prototype.getPublicKey = 164 dhGetPublicKey; 165 166function dhGetPublicKey(encoding) { 167 const key = this[kHandle].getPublicKey(); 168 encoding = encoding || getDefaultEncoding(); 169 return encode(key, encoding); 170} 171 172 173DiffieHellmanGroup.prototype.getPrivateKey = 174 DiffieHellman.prototype.getPrivateKey = 175 dhGetPrivateKey; 176 177function dhGetPrivateKey(encoding) { 178 const key = this[kHandle].getPrivateKey(); 179 encoding = encoding || getDefaultEncoding(); 180 return encode(key, encoding); 181} 182 183 184DiffieHellman.prototype.setPublicKey = function setPublicKey(key, encoding) { 185 encoding = encoding || getDefaultEncoding(); 186 this[kHandle].setPublicKey(toBuf(key, encoding)); 187 return this; 188}; 189 190 191DiffieHellman.prototype.setPrivateKey = function setPrivateKey(key, encoding) { 192 encoding = encoding || getDefaultEncoding(); 193 this[kHandle].setPrivateKey(toBuf(key, encoding)); 194 return this; 195}; 196 197 198function ECDH(curve) { 199 if (!(this instanceof ECDH)) 200 return new ECDH(curve); 201 202 validateString(curve, 'curve'); 203 this[kHandle] = new _ECDH(curve); 204} 205 206ECDH.prototype.computeSecret = DiffieHellman.prototype.computeSecret; 207ECDH.prototype.setPrivateKey = DiffieHellman.prototype.setPrivateKey; 208ECDH.prototype.setPublicKey = DiffieHellman.prototype.setPublicKey; 209ECDH.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey; 210 211ECDH.prototype.generateKeys = function generateKeys(encoding, format) { 212 this[kHandle].generateKeys(); 213 214 return this.getPublicKey(encoding, format); 215}; 216 217ECDH.prototype.getPublicKey = function getPublicKey(encoding, format) { 218 const f = getFormat(format); 219 const key = this[kHandle].getPublicKey(f); 220 encoding = encoding || getDefaultEncoding(); 221 return encode(key, encoding); 222}; 223 224ECDH.convertKey = function convertKey(key, curve, inEnc, outEnc, format) { 225 if (typeof key !== 'string' && !isArrayBufferView(key)) { 226 throw new ERR_INVALID_ARG_TYPE( 227 'key', 228 ['string', 'Buffer', 'TypedArray', 'DataView'], 229 key 230 ); 231 } 232 233 validateString(curve, 'curve'); 234 235 const encoding = getDefaultEncoding(); 236 inEnc = inEnc || encoding; 237 outEnc = outEnc || encoding; 238 const f = getFormat(format); 239 const convertedKey = _ECDHConvertKey(toBuf(key, inEnc), curve, f); 240 return encode(convertedKey, outEnc); 241}; 242 243function encode(buffer, encoding) { 244 if (encoding && encoding !== 'buffer') 245 buffer = buffer.toString(encoding); 246 return buffer; 247} 248 249function getFormat(format) { 250 if (format) { 251 if (format === 'compressed') 252 return POINT_CONVERSION_COMPRESSED; 253 if (format === 'hybrid') 254 return POINT_CONVERSION_HYBRID; 255 if (format !== 'uncompressed') 256 throw new ERR_CRYPTO_ECDH_INVALID_FORMAT(format); 257 } 258 return POINT_CONVERSION_UNCOMPRESSED; 259} 260 261const dhEnabledKeyTypes = new Set(['dh', 'ec', 'x448', 'x25519']); 262 263function diffieHellman(options) { 264 if (typeof options !== 'object') 265 throw new ERR_INVALID_ARG_TYPE('options', 'object', options); 266 267 const { privateKey, publicKey } = options; 268 if (!(privateKey instanceof KeyObject)) 269 throw new ERR_INVALID_OPT_VALUE('privateKey', privateKey); 270 271 if (!(publicKey instanceof KeyObject)) 272 throw new ERR_INVALID_OPT_VALUE('publicKey', publicKey); 273 274 if (privateKey.type !== 'private') 275 throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(privateKey.type, 'private'); 276 277 if (publicKey.type !== 'public' && publicKey.type !== 'private') { 278 throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(publicKey.type, 279 'private or public'); 280 } 281 282 const privateType = privateKey.asymmetricKeyType; 283 const publicType = publicKey.asymmetricKeyType; 284 if (privateType !== publicType || !dhEnabledKeyTypes.has(privateType)) { 285 throw new ERR_CRYPTO_INCOMPATIBLE_KEY('key types for Diffie-Hellman', 286 `${privateType} and ${publicType}`); 287 } 288 289 return statelessDH(privateKey[kHandle], publicKey[kHandle]); 290} 291 292module.exports = { 293 DiffieHellman, 294 DiffieHellmanGroup, 295 ECDH, 296 diffieHellman 297}; 298