Initial commit
[yaffs-website] / node_modules / sshpk / lib / private-key.js
1 // Copyright 2015 Joyent, Inc.
2
3 module.exports = PrivateKey;
4
5 var assert = require('assert-plus');
6 var algs = require('./algs');
7 var crypto = require('crypto');
8 var Fingerprint = require('./fingerprint');
9 var Signature = require('./signature');
10 var errs = require('./errors');
11 var util = require('util');
12 var utils = require('./utils');
13 var edCompat;
14 var ed;
15
16 try {
17         edCompat = require('./ed-compat');
18 } catch (e) {
19         /* Just continue through, and bail out if we try to use it. */
20 }
21
22 var Key = require('./key');
23
24 var InvalidAlgorithmError = errs.InvalidAlgorithmError;
25 var KeyParseError = errs.KeyParseError;
26 var KeyEncryptedError = errs.KeyEncryptedError;
27
28 var formats = {};
29 formats['auto'] = require('./formats/auto');
30 formats['pem'] = require('./formats/pem');
31 formats['pkcs1'] = require('./formats/pkcs1');
32 formats['pkcs8'] = require('./formats/pkcs8');
33 formats['rfc4253'] = require('./formats/rfc4253');
34 formats['ssh-private'] = require('./formats/ssh-private');
35 formats['openssh'] = formats['ssh-private'];
36 formats['ssh'] = formats['ssh-private'];
37
38 function PrivateKey(opts) {
39         assert.object(opts, 'options');
40         Key.call(this, opts);
41
42         this._pubCache = undefined;
43 }
44 util.inherits(PrivateKey, Key);
45
46 PrivateKey.formats = formats;
47
48 PrivateKey.prototype.toBuffer = function (format, options) {
49         if (format === undefined)
50                 format = 'pkcs1';
51         assert.string(format, 'format');
52         assert.object(formats[format], 'formats[format]');
53         assert.optionalObject(options, 'options');
54
55         return (formats[format].write(this, options));
56 };
57
58 PrivateKey.prototype.hash = function (algo) {
59         return (this.toPublic().hash(algo));
60 };
61
62 PrivateKey.prototype.toPublic = function () {
63         if (this._pubCache)
64                 return (this._pubCache);
65
66         var algInfo = algs.info[this.type];
67         var pubParts = [];
68         for (var i = 0; i < algInfo.parts.length; ++i) {
69                 var p = algInfo.parts[i];
70                 pubParts.push(this.part[p]);
71         }
72
73         this._pubCache = new Key({
74                 type: this.type,
75                 source: this,
76                 parts: pubParts
77         });
78         if (this.comment)
79                 this._pubCache.comment = this.comment;
80         return (this._pubCache);
81 };
82
83 PrivateKey.prototype.derive = function (newType, newSize) {
84         assert.string(newType, 'type');
85         assert.optionalNumber(newSize, 'size');
86         var priv, pub;
87
88         if (this.type === 'ed25519' && newType === 'curve25519') {
89                 if (ed === undefined)
90                         ed = require('jodid25519');
91
92                 priv = this.part.r.data;
93                 if (priv[0] === 0x00)
94                         priv = priv.slice(1);
95                 priv = priv.slice(0, 32);
96
97                 pub = ed.dh.publicKey(priv);
98                 priv = utils.mpNormalize(Buffer.concat([priv, pub]));
99
100                 return (new PrivateKey({
101                         type: 'curve25519',
102                         parts: [
103                                 { name: 'R', data: utils.mpNormalize(pub) },
104                                 { name: 'r', data: priv }
105                         ]
106                 }));
107         } else if (this.type === 'curve25519' && newType === 'ed25519') {
108                 if (ed === undefined)
109                         ed = require('jodid25519');
110
111                 priv = this.part.r.data;
112                 if (priv[0] === 0x00)
113                         priv = priv.slice(1);
114                 priv = priv.slice(0, 32);
115
116                 pub = ed.eddsa.publicKey(priv.toString('binary'));
117                 pub = new Buffer(pub, 'binary');
118
119                 priv = utils.mpNormalize(Buffer.concat([priv, pub]));
120
121                 return (new PrivateKey({
122                         type: 'ed25519',
123                         parts: [
124                                 { name: 'R', data: utils.mpNormalize(pub) },
125                                 { name: 'r', data: priv }
126                         ]
127                 }));
128         }
129         throw (new Error('Key derivation not supported from ' + this.type +
130             ' to ' + newType));
131 };
132
133 PrivateKey.prototype.createVerify = function (hashAlgo) {
134         return (this.toPublic().createVerify(hashAlgo));
135 };
136
137 PrivateKey.prototype.createSign = function (hashAlgo) {
138         if (hashAlgo === undefined)
139                 hashAlgo = this.defaultHashAlgorithm();
140         assert.string(hashAlgo, 'hash algorithm');
141
142         /* ED25519 is not supported by OpenSSL, use a javascript impl. */
143         if (this.type === 'ed25519' && edCompat !== undefined)
144                 return (new edCompat.Signer(this, hashAlgo));
145         if (this.type === 'curve25519')
146                 throw (new Error('Curve25519 keys are not suitable for ' +
147                     'signing or verification'));
148
149         var v, nm, err;
150         try {
151                 nm = hashAlgo.toUpperCase();
152                 v = crypto.createSign(nm);
153         } catch (e) {
154                 err = e;
155         }
156         if (v === undefined || (err instanceof Error &&
157             err.message.match(/Unknown message digest/))) {
158                 nm = 'RSA-';
159                 nm += hashAlgo.toUpperCase();
160                 v = crypto.createSign(nm);
161         }
162         assert.ok(v, 'failed to create verifier');
163         var oldSign = v.sign.bind(v);
164         var key = this.toBuffer('pkcs1');
165         var type = this.type;
166         v.sign = function () {
167                 var sig = oldSign(key);
168                 if (typeof (sig) === 'string')
169                         sig = new Buffer(sig, 'binary');
170                 sig = Signature.parse(sig, type, 'asn1');
171                 sig.hashAlgorithm = hashAlgo;
172                 return (sig);
173         };
174         return (v);
175 };
176
177 PrivateKey.parse = function (data, format, options) {
178         if (typeof (data) !== 'string')
179                 assert.buffer(data, 'data');
180         if (format === undefined)
181                 format = 'auto';
182         assert.string(format, 'format');
183         if (typeof (options) === 'string')
184                 options = { filename: options };
185         assert.optionalObject(options, 'options');
186         if (options === undefined)
187                 options = {};
188         assert.optionalString(options.filename, 'options.filename');
189         if (options.filename === undefined)
190                 options.filename = '(unnamed)';
191
192         assert.object(formats[format], 'formats[format]');
193
194         try {
195                 var k = formats[format].read(data, options);
196                 assert.ok(k instanceof PrivateKey, 'key is not a private key');
197                 if (!k.comment)
198                         k.comment = options.filename;
199                 return (k);
200         } catch (e) {
201                 if (e.name === 'KeyEncryptedError')
202                         throw (e);
203                 throw (new KeyParseError(options.filename, format, e));
204         }
205 };
206
207 PrivateKey.isPrivateKey = function (obj, ver) {
208         return (utils.isCompatible(obj, PrivateKey, ver));
209 };
210
211 /*
212  * API versions for PrivateKey:
213  * [1,0] -- initial ver
214  * [1,1] -- added auto, pkcs[18], openssh/ssh-private formats
215  * [1,2] -- added defaultHashAlgorithm
216  * [1,3] -- added derive, ed, createDH
217  * [1,4] -- first tagged version
218  */
219 PrivateKey.prototype._sshpkApiVersion = [1, 4];
220
221 PrivateKey._oldVersionDetect = function (obj) {
222         assert.func(obj.toPublic);
223         assert.func(obj.createSign);
224         if (obj.derive)
225                 return ([1, 3]);
226         if (obj.defaultHashAlgorithm)
227                 return ([1, 2]);
228         if (obj.formats['auto'])
229                 return ([1, 1]);
230         return ([1, 0]);
231 };