1 // Copyright 2015 Joyent, Inc.
4 bufferSplit: bufferSplit,
5 addRSAMissing: addRSAMissing,
6 calculateDSAPublic: calculateDSAPublic,
7 mpNormalize: mpNormalize,
8 ecNormalize: ecNormalize,
9 countZeros: countZeros,
10 assertCompatible: assertCompatible,
11 isCompatible: isCompatible,
12 opensslKeyDeriv: opensslKeyDeriv
15 var assert = require('assert-plus');
16 var PrivateKey = require('./private-key');
17 var crypto = require('crypto');
19 var MAX_CLASS_DEPTH = 3;
21 function isCompatible(obj, klass, needVer) {
22 if (obj === null || typeof (obj) !== 'object')
24 if (needVer === undefined)
25 needVer = klass.prototype._sshpkApiVersion;
26 if (obj instanceof klass &&
27 klass.prototype._sshpkApiVersion[0] == needVer[0])
29 var proto = Object.getPrototypeOf(obj);
31 while (proto.constructor.name !== klass.name) {
32 proto = Object.getPrototypeOf(proto);
33 if (!proto || ++depth > MAX_CLASS_DEPTH)
36 if (proto.constructor.name !== klass.name)
38 var ver = proto._sshpkApiVersion;
39 if (ver === undefined)
40 ver = klass._oldVersionDetect(obj);
41 if (ver[0] != needVer[0] || ver[1] < needVer[1])
46 function assertCompatible(obj, klass, needVer, name) {
47 if (name === undefined)
49 assert.ok(obj, name + ' must not be null');
50 assert.object(obj, name + ' must be an object');
51 if (needVer === undefined)
52 needVer = klass.prototype._sshpkApiVersion;
53 if (obj instanceof klass &&
54 klass.prototype._sshpkApiVersion[0] == needVer[0])
56 var proto = Object.getPrototypeOf(obj);
58 while (proto.constructor.name !== klass.name) {
59 proto = Object.getPrototypeOf(proto);
60 assert.ok(proto && ++depth <= MAX_CLASS_DEPTH,
61 name + ' must be a ' + klass.name + ' instance');
63 assert.strictEqual(proto.constructor.name, klass.name,
64 name + ' must be a ' + klass.name + ' instance');
65 var ver = proto._sshpkApiVersion;
66 if (ver === undefined)
67 ver = klass._oldVersionDetect(obj);
68 assert.ok(ver[0] == needVer[0] && ver[1] >= needVer[1],
69 name + ' must be compatible with ' + klass.name + ' klass ' +
70 'version ' + needVer[0] + '.' + needVer[1]);
74 'des-ede3-cbc': { key: 7, iv: 8 },
75 'aes-128-cbc': { key: 16, iv: 16 }
77 var PKCS5_SALT_LEN = 8;
79 function opensslKeyDeriv(cipher, salt, passphrase, count) {
80 assert.buffer(salt, 'salt');
81 assert.buffer(passphrase, 'passphrase');
82 assert.number(count, 'iteration count');
84 var clen = CIPHER_LEN[cipher];
85 assert.object(clen, 'supported cipher');
87 salt = salt.slice(0, PKCS5_SALT_LEN);
90 var material = new Buffer(0);
91 while (material.length < clen.key + clen.iv) {
95 bufs.push(passphrase);
97 D = Buffer.concat(bufs);
98 for (var j = 0; j < count; ++j)
99 D = crypto.createHash('md5').update(D).digest();
100 material = Buffer.concat([material, D]);
105 key: material.slice(0, clen.key),
106 iv: material.slice(clen.key, clen.key + clen.iv)
110 /* Count leading zero bits on a buffer */
111 function countZeros(buf) {
113 while (o < buf.length) {
114 var mask = (1 << obit);
115 if ((buf[o] & mask) === mask)
123 return (o*8 + (8 - obit) - 1);
126 function bufferSplit(buf, chr) {
133 for (var i = 0; i < buf.length; ++i) {
134 if (buf[i] === chr.charCodeAt(matches))
136 else if (buf[i] === chr.charCodeAt(0))
141 if (matches >= chr.length) {
143 parts.push(buf.slice(lastPart, newPart - matches));
148 if (lastPart <= buf.length)
149 parts.push(buf.slice(lastPart, buf.length));
154 function ecNormalize(buf, addZero) {
156 if (buf[0] === 0x00 && buf[1] === 0x04) {
159 return (buf.slice(1));
160 } else if (buf[0] === 0x04) {
164 while (buf[0] === 0x00)
166 if (buf[0] === 0x02 || buf[0] === 0x03)
167 throw (new Error('Compressed elliptic curve points ' +
168 'are not supported'));
170 throw (new Error('Not a valid elliptic curve point'));
174 var b = new Buffer(buf.length + 1);
180 function mpNormalize(buf) {
182 while (buf.length > 1 && buf[0] === 0x00 && (buf[1] & 0x80) === 0x00)
184 if ((buf[0] & 0x80) === 0x80) {
185 var b = new Buffer(buf.length + 1);
193 function bigintToMpBuf(bigint) {
194 var buf = new Buffer(bigint.toByteArray());
195 buf = mpNormalize(buf);
199 function calculateDSAPublic(g, p, x) {
204 var bigInt = require('jsbn').BigInteger;
206 throw (new Error('To load a PKCS#8 format DSA private key, ' +
207 'the node jsbn library is required.'));
212 var y = g.modPow(x, p);
213 var ybuf = bigintToMpBuf(y);
217 function addRSAMissing(key) {
219 assertCompatible(key, PrivateKey, [1, 1]);
221 var bigInt = require('jsbn').BigInteger;
223 throw (new Error('To write a PEM private key from ' +
224 'this source, the node jsbn lib is required.'));
227 var d = new bigInt(key.part.d.data);
230 if (!key.part.dmodp) {
231 var p = new bigInt(key.part.p.data);
232 var dmodp = d.mod(p.subtract(1));
234 buf = bigintToMpBuf(dmodp);
235 key.part.dmodp = {name: 'dmodp', data: buf};
236 key.parts.push(key.part.dmodp);
238 if (!key.part.dmodq) {
239 var q = new bigInt(key.part.q.data);
240 var dmodq = d.mod(q.subtract(1));
242 buf = bigintToMpBuf(dmodq);
243 key.part.dmodq = {name: 'dmodq', data: buf};
244 key.parts.push(key.part.dmodq);