Initial commit
[yaffs-website] / node_modules / sshpk / lib / dhe.js
1 // Copyright 2015 Joyent, Inc.
2
3 module.exports = DiffieHellman;
4
5 var assert = require('assert-plus');
6 var crypto = require('crypto');
7 var algs = require('./algs');
8 var utils = require('./utils');
9 var ed;
10
11 var Key = require('./key');
12 var PrivateKey = require('./private-key');
13
14 var CRYPTO_HAVE_ECDH = (crypto.createECDH !== undefined);
15
16 var ecdh, ec, jsbn;
17
18 function DiffieHellman(key) {
19         utils.assertCompatible(key, Key, [1, 4], 'key');
20         this._isPriv = PrivateKey.isPrivateKey(key, [1, 3]);
21         this._algo = key.type;
22         this._curve = key.curve;
23         this._key = key;
24         if (key.type === 'dsa') {
25                 if (!CRYPTO_HAVE_ECDH) {
26                         throw (new Error('Due to bugs in the node 0.10 ' +
27                             'crypto API, node 0.12.x or later is required ' +
28                             'to use DH'));
29                 }
30                 this._dh = crypto.createDiffieHellman(
31                     key.part.p.data, undefined,
32                     key.part.g.data, undefined);
33                 this._p = key.part.p;
34                 this._g = key.part.g;
35                 if (this._isPriv)
36                         this._dh.setPrivateKey(key.part.x.data);
37                 this._dh.setPublicKey(key.part.y.data);
38
39         } else if (key.type === 'ecdsa') {
40                 if (!CRYPTO_HAVE_ECDH) {
41                         if (ecdh === undefined)
42                                 ecdh = require('ecc-jsbn');
43                         if (ec === undefined)
44                                 ec = require('ecc-jsbn/lib/ec');
45                         if (jsbn === undefined)
46                                 jsbn = require('jsbn').BigInteger;
47
48                         this._ecParams = new X9ECParameters(this._curve);
49
50                         if (this._isPriv) {
51                                 this._priv = new ECPrivate(
52                                     this._ecParams, key.part.d.data);
53                         }
54                         return;
55                 }
56
57                 var curve = {
58                         'nistp256': 'prime256v1',
59                         'nistp384': 'secp384r1',
60                         'nistp521': 'secp521r1'
61                 }[key.curve];
62                 this._dh = crypto.createECDH(curve);
63                 if (typeof (this._dh) !== 'object' ||
64                     typeof (this._dh.setPrivateKey) !== 'function') {
65                         CRYPTO_HAVE_ECDH = false;
66                         DiffieHellman.call(this, key);
67                         return;
68                 }
69                 if (this._isPriv)
70                         this._dh.setPrivateKey(key.part.d.data);
71                 this._dh.setPublicKey(key.part.Q.data);
72
73         } else if (key.type === 'curve25519') {
74                 if (ed === undefined)
75                         ed = require('jodid25519');
76
77                 if (this._isPriv) {
78                         this._priv = key.part.r.data;
79                         if (this._priv[0] === 0x00)
80                                 this._priv = this._priv.slice(1);
81                         this._priv = this._priv.slice(0, 32);
82                 }
83
84         } else {
85                 throw (new Error('DH not supported for ' + key.type + ' keys'));
86         }
87 }
88
89 DiffieHellman.prototype.getPublicKey = function () {
90         if (this._isPriv)
91                 return (this._key.toPublic());
92         return (this._key);
93 };
94
95 DiffieHellman.prototype.getPrivateKey = function () {
96         if (this._isPriv)
97                 return (this._key);
98         else
99                 return (undefined);
100 };
101 DiffieHellman.prototype.getKey = DiffieHellman.prototype.getPrivateKey;
102
103 DiffieHellman.prototype._keyCheck = function (pk, isPub) {
104         assert.object(pk, 'key');
105         if (!isPub)
106                 utils.assertCompatible(pk, PrivateKey, [1, 3], 'key');
107         utils.assertCompatible(pk, Key, [1, 4], 'key');
108
109         if (pk.type !== this._algo) {
110                 throw (new Error('A ' + pk.type + ' key cannot be used in ' +
111                     this._algo + ' Diffie-Hellman'));
112         }
113
114         if (pk.curve !== this._curve) {
115                 throw (new Error('A key from the ' + pk.curve + ' curve ' +
116                     'cannot be used with a ' + this._curve +
117                     ' Diffie-Hellman'));
118         }
119
120         if (pk.type === 'dsa') {
121                 assert.deepEqual(pk.part.p, this._p,
122                     'DSA key prime does not match');
123                 assert.deepEqual(pk.part.g, this._g,
124                     'DSA key generator does not match');
125         }
126 };
127
128 DiffieHellman.prototype.setKey = function (pk) {
129         this._keyCheck(pk);
130
131         if (pk.type === 'dsa') {
132                 this._dh.setPrivateKey(pk.part.x.data);
133                 this._dh.setPublicKey(pk.part.y.data);
134
135         } else if (pk.type === 'ecdsa') {
136                 if (CRYPTO_HAVE_ECDH) {
137                         this._dh.setPrivateKey(pk.part.d.data);
138                         this._dh.setPublicKey(pk.part.Q.data);
139                 } else {
140                         this._priv = new ECPrivate(
141                             this._ecParams, pk.part.d.data);
142                 }
143
144         } else if (pk.type === 'curve25519') {
145                 this._priv = pk.part.r.data;
146                 if (this._priv[0] === 0x00)
147                         this._priv = this._priv.slice(1);
148                 this._priv = this._priv.slice(0, 32);
149         }
150         this._key = pk;
151         this._isPriv = true;
152 };
153 DiffieHellman.prototype.setPrivateKey = DiffieHellman.prototype.setKey;
154
155 DiffieHellman.prototype.computeSecret = function (otherpk) {
156         this._keyCheck(otherpk, true);
157         if (!this._isPriv)
158                 throw (new Error('DH exchange has not been initialized with ' +
159                     'a private key yet'));
160
161         var pub;
162         if (this._algo === 'dsa') {
163                 return (this._dh.computeSecret(
164                     otherpk.part.y.data));
165
166         } else if (this._algo === 'ecdsa') {
167                 if (CRYPTO_HAVE_ECDH) {
168                         return (this._dh.computeSecret(
169                             otherpk.part.Q.data));
170                 } else {
171                         pub = new ECPublic(
172                             this._ecParams, otherpk.part.Q.data);
173                         return (this._priv.deriveSharedSecret(pub));
174                 }
175
176         } else if (this._algo === 'curve25519') {
177                 pub = otherpk.part.R.data;
178                 if (pub[0] === 0x00)
179                         pub = pub.slice(1);
180
181                 var secret = ed.dh.computeKey(
182                     this._priv.toString('binary'),
183                     pub.toString('binary'));
184
185                 return (new Buffer(secret, 'binary'));
186         }
187
188         throw (new Error('Invalid algorithm: ' + this._algo));
189 };
190
191 DiffieHellman.prototype.generateKey = function () {
192         var parts = [];
193         var priv, pub;
194         if (this._algo === 'dsa') {
195                 this._dh.generateKeys();
196
197                 parts.push({name: 'p', data: this._p.data});
198                 parts.push({name: 'q', data: this._key.part.q.data});
199                 parts.push({name: 'g', data: this._g.data});
200                 parts.push({name: 'y', data: this._dh.getPublicKey()});
201                 parts.push({name: 'x', data: this._dh.getPrivateKey()});
202                 this._key = new PrivateKey({
203                         type: 'dsa',
204                         parts: parts
205                 });
206                 this._isPriv = true;
207                 return (this._key);
208
209         } else if (this._algo === 'ecdsa') {
210                 if (CRYPTO_HAVE_ECDH) {
211                         this._dh.generateKeys();
212
213                         parts.push({name: 'curve',
214                             data: new Buffer(this._curve)});
215                         parts.push({name: 'Q', data: this._dh.getPublicKey()});
216                         parts.push({name: 'd', data: this._dh.getPrivateKey()});
217                         this._key = new PrivateKey({
218                                 type: 'ecdsa',
219                                 curve: this._curve,
220                                 parts: parts
221                         });
222                         this._isPriv = true;
223                         return (this._key);
224
225                 } else {
226                         var n = this._ecParams.getN();
227                         var r = new jsbn(crypto.randomBytes(n.bitLength()));
228                         var n1 = n.subtract(jsbn.ONE);
229                         priv = r.mod(n1).add(jsbn.ONE);
230                         pub = this._ecParams.getG().multiply(priv);
231
232                         priv = new Buffer(priv.toByteArray());
233                         pub = new Buffer(this._ecParams.getCurve().
234                             encodePointHex(pub), 'hex');
235
236                         this._priv = new ECPrivate(this._ecParams, priv);
237
238                         parts.push({name: 'curve',
239                             data: new Buffer(this._curve)});
240                         parts.push({name: 'Q', data: pub});
241                         parts.push({name: 'd', data: priv});
242
243                         this._key = new PrivateKey({
244                                 type: 'ecdsa',
245                                 curve: this._curve,
246                                 parts: parts
247                         });
248                         this._isPriv = true;
249                         return (this._key);
250                 }
251
252         } else if (this._algo === 'curve25519') {
253                 priv = ed.dh.generateKey();
254                 pub = ed.dh.publicKey(priv);
255                 this._priv = priv = new Buffer(priv, 'binary');
256                 pub = new Buffer(pub, 'binary');
257
258                 parts.push({name: 'R', data: pub});
259                 parts.push({name: 'r', data: Buffer.concat([priv, pub])});
260                 this._key = new PrivateKey({
261                         type: 'curve25519',
262                         parts: parts
263                 });
264                 this._isPriv = true;
265                 return (this._key);
266         }
267
268         throw (new Error('Invalid algorithm: ' + this._algo));
269 };
270 DiffieHellman.prototype.generateKeys = DiffieHellman.prototype.generateKey;
271
272 /* These are helpers for using ecc-jsbn (for node 0.10 compatibility). */
273
274 function X9ECParameters(name) {
275         var params = algs.curves[name];
276         assert.object(params);
277
278         var p = new jsbn(params.p);
279         var a = new jsbn(params.a);
280         var b = new jsbn(params.b);
281         var n = new jsbn(params.n);
282         var h = jsbn.ONE;
283         var curve = new ec.ECCurveFp(p, a, b);
284         var G = curve.decodePointHex(params.G.toString('hex'));
285
286         this.curve = curve;
287         this.g = G;
288         this.n = n;
289         this.h = h;
290 }
291 X9ECParameters.prototype.getCurve = function () { return (this.curve); };
292 X9ECParameters.prototype.getG = function () { return (this.g); };
293 X9ECParameters.prototype.getN = function () { return (this.n); };
294 X9ECParameters.prototype.getH = function () { return (this.h); };
295
296 function ECPublic(params, buffer) {
297         this._params = params;
298         if (buffer[0] === 0x00)
299                 buffer = buffer.slice(1);
300         this._pub = params.getCurve().decodePointHex(buffer.toString('hex'));
301 }
302
303 function ECPrivate(params, buffer) {
304         this._params = params;
305         this._priv = new jsbn(utils.mpNormalize(buffer));
306 }
307 ECPrivate.prototype.deriveSharedSecret = function (pubKey) {
308         assert.ok(pubKey instanceof ECPublic);
309         var S = pubKey._pub.multiply(this._priv);
310         return (new Buffer(S.getX().toBigInteger().toByteArray()));
311 };