• 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 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