• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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