source: trip-planner-front/node_modules/sshpk/lib/signature.js@ 188ee53

Last change on this file since 188ee53 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 7.8 KB
Line 
1// Copyright 2015 Joyent, Inc.
2
3module.exports = Signature;
4
5var assert = require('assert-plus');
6var Buffer = require('safer-buffer').Buffer;
7var algs = require('./algs');
8var crypto = require('crypto');
9var errs = require('./errors');
10var utils = require('./utils');
11var asn1 = require('asn1');
12var SSHBuffer = require('./ssh-buffer');
13
14var InvalidAlgorithmError = errs.InvalidAlgorithmError;
15var SignatureParseError = errs.SignatureParseError;
16
17function Signature(opts) {
18 assert.object(opts, 'options');
19 assert.arrayOfObject(opts.parts, 'options.parts');
20 assert.string(opts.type, 'options.type');
21
22 var partLookup = {};
23 for (var i = 0; i < opts.parts.length; ++i) {
24 var part = opts.parts[i];
25 partLookup[part.name] = part;
26 }
27
28 this.type = opts.type;
29 this.hashAlgorithm = opts.hashAlgo;
30 this.curve = opts.curve;
31 this.parts = opts.parts;
32 this.part = partLookup;
33}
34
35Signature.prototype.toBuffer = function (format) {
36 if (format === undefined)
37 format = 'asn1';
38 assert.string(format, 'format');
39
40 var buf;
41 var stype = 'ssh-' + this.type;
42
43 switch (this.type) {
44 case 'rsa':
45 switch (this.hashAlgorithm) {
46 case 'sha256':
47 stype = 'rsa-sha2-256';
48 break;
49 case 'sha512':
50 stype = 'rsa-sha2-512';
51 break;
52 case 'sha1':
53 case undefined:
54 break;
55 default:
56 throw (new Error('SSH signature ' +
57 'format does not support hash ' +
58 'algorithm ' + this.hashAlgorithm));
59 }
60 if (format === 'ssh') {
61 buf = new SSHBuffer({});
62 buf.writeString(stype);
63 buf.writePart(this.part.sig);
64 return (buf.toBuffer());
65 } else {
66 return (this.part.sig.data);
67 }
68 break;
69
70 case 'ed25519':
71 if (format === 'ssh') {
72 buf = new SSHBuffer({});
73 buf.writeString(stype);
74 buf.writePart(this.part.sig);
75 return (buf.toBuffer());
76 } else {
77 return (this.part.sig.data);
78 }
79 break;
80
81 case 'dsa':
82 case 'ecdsa':
83 var r, s;
84 if (format === 'asn1') {
85 var der = new asn1.BerWriter();
86 der.startSequence();
87 r = utils.mpNormalize(this.part.r.data);
88 s = utils.mpNormalize(this.part.s.data);
89 der.writeBuffer(r, asn1.Ber.Integer);
90 der.writeBuffer(s, asn1.Ber.Integer);
91 der.endSequence();
92 return (der.buffer);
93 } else if (format === 'ssh' && this.type === 'dsa') {
94 buf = new SSHBuffer({});
95 buf.writeString('ssh-dss');
96 r = this.part.r.data;
97 if (r.length > 20 && r[0] === 0x00)
98 r = r.slice(1);
99 s = this.part.s.data;
100 if (s.length > 20 && s[0] === 0x00)
101 s = s.slice(1);
102 if ((this.hashAlgorithm &&
103 this.hashAlgorithm !== 'sha1') ||
104 r.length + s.length !== 40) {
105 throw (new Error('OpenSSH only supports ' +
106 'DSA signatures with SHA1 hash'));
107 }
108 buf.writeBuffer(Buffer.concat([r, s]));
109 return (buf.toBuffer());
110 } else if (format === 'ssh' && this.type === 'ecdsa') {
111 var inner = new SSHBuffer({});
112 r = this.part.r.data;
113 inner.writeBuffer(r);
114 inner.writePart(this.part.s);
115
116 buf = new SSHBuffer({});
117 /* XXX: find a more proper way to do this? */
118 var curve;
119 if (r[0] === 0x00)
120 r = r.slice(1);
121 var sz = r.length * 8;
122 if (sz === 256)
123 curve = 'nistp256';
124 else if (sz === 384)
125 curve = 'nistp384';
126 else if (sz === 528)
127 curve = 'nistp521';
128 buf.writeString('ecdsa-sha2-' + curve);
129 buf.writeBuffer(inner.toBuffer());
130 return (buf.toBuffer());
131 }
132 throw (new Error('Invalid signature format'));
133 default:
134 throw (new Error('Invalid signature data'));
135 }
136};
137
138Signature.prototype.toString = function (format) {
139 assert.optionalString(format, 'format');
140 return (this.toBuffer(format).toString('base64'));
141};
142
143Signature.parse = function (data, type, format) {
144 if (typeof (data) === 'string')
145 data = Buffer.from(data, 'base64');
146 assert.buffer(data, 'data');
147 assert.string(format, 'format');
148 assert.string(type, 'type');
149
150 var opts = {};
151 opts.type = type.toLowerCase();
152 opts.parts = [];
153
154 try {
155 assert.ok(data.length > 0, 'signature must not be empty');
156 switch (opts.type) {
157 case 'rsa':
158 return (parseOneNum(data, type, format, opts));
159 case 'ed25519':
160 return (parseOneNum(data, type, format, opts));
161
162 case 'dsa':
163 case 'ecdsa':
164 if (format === 'asn1')
165 return (parseDSAasn1(data, type, format, opts));
166 else if (opts.type === 'dsa')
167 return (parseDSA(data, type, format, opts));
168 else
169 return (parseECDSA(data, type, format, opts));
170
171 default:
172 throw (new InvalidAlgorithmError(type));
173 }
174
175 } catch (e) {
176 if (e instanceof InvalidAlgorithmError)
177 throw (e);
178 throw (new SignatureParseError(type, format, e));
179 }
180};
181
182function parseOneNum(data, type, format, opts) {
183 if (format === 'ssh') {
184 try {
185 var buf = new SSHBuffer({buffer: data});
186 var head = buf.readString();
187 } catch (e) {
188 /* fall through */
189 }
190 if (buf !== undefined) {
191 var msg = 'SSH signature does not match expected ' +
192 'type (expected ' + type + ', got ' + head + ')';
193 switch (head) {
194 case 'ssh-rsa':
195 assert.strictEqual(type, 'rsa', msg);
196 opts.hashAlgo = 'sha1';
197 break;
198 case 'rsa-sha2-256':
199 assert.strictEqual(type, 'rsa', msg);
200 opts.hashAlgo = 'sha256';
201 break;
202 case 'rsa-sha2-512':
203 assert.strictEqual(type, 'rsa', msg);
204 opts.hashAlgo = 'sha512';
205 break;
206 case 'ssh-ed25519':
207 assert.strictEqual(type, 'ed25519', msg);
208 opts.hashAlgo = 'sha512';
209 break;
210 default:
211 throw (new Error('Unknown SSH signature ' +
212 'type: ' + head));
213 }
214 var sig = buf.readPart();
215 assert.ok(buf.atEnd(), 'extra trailing bytes');
216 sig.name = 'sig';
217 opts.parts.push(sig);
218 return (new Signature(opts));
219 }
220 }
221 opts.parts.push({name: 'sig', data: data});
222 return (new Signature(opts));
223}
224
225function parseDSAasn1(data, type, format, opts) {
226 var der = new asn1.BerReader(data);
227 der.readSequence();
228 var r = der.readString(asn1.Ber.Integer, true);
229 var s = der.readString(asn1.Ber.Integer, true);
230
231 opts.parts.push({name: 'r', data: utils.mpNormalize(r)});
232 opts.parts.push({name: 's', data: utils.mpNormalize(s)});
233
234 return (new Signature(opts));
235}
236
237function parseDSA(data, type, format, opts) {
238 if (data.length != 40) {
239 var buf = new SSHBuffer({buffer: data});
240 var d = buf.readBuffer();
241 if (d.toString('ascii') === 'ssh-dss')
242 d = buf.readBuffer();
243 assert.ok(buf.atEnd(), 'extra trailing bytes');
244 assert.strictEqual(d.length, 40, 'invalid inner length');
245 data = d;
246 }
247 opts.parts.push({name: 'r', data: data.slice(0, 20)});
248 opts.parts.push({name: 's', data: data.slice(20, 40)});
249 return (new Signature(opts));
250}
251
252function parseECDSA(data, type, format, opts) {
253 var buf = new SSHBuffer({buffer: data});
254
255 var r, s;
256 var inner = buf.readBuffer();
257 var stype = inner.toString('ascii');
258 if (stype.slice(0, 6) === 'ecdsa-') {
259 var parts = stype.split('-');
260 assert.strictEqual(parts[0], 'ecdsa');
261 assert.strictEqual(parts[1], 'sha2');
262 opts.curve = parts[2];
263 switch (opts.curve) {
264 case 'nistp256':
265 opts.hashAlgo = 'sha256';
266 break;
267 case 'nistp384':
268 opts.hashAlgo = 'sha384';
269 break;
270 case 'nistp521':
271 opts.hashAlgo = 'sha512';
272 break;
273 default:
274 throw (new Error('Unsupported ECDSA curve: ' +
275 opts.curve));
276 }
277 inner = buf.readBuffer();
278 assert.ok(buf.atEnd(), 'extra trailing bytes on outer');
279 buf = new SSHBuffer({buffer: inner});
280 r = buf.readPart();
281 } else {
282 r = {data: inner};
283 }
284
285 s = buf.readPart();
286 assert.ok(buf.atEnd(), 'extra trailing bytes');
287
288 r.name = 'r';
289 s.name = 's';
290
291 opts.parts.push(r);
292 opts.parts.push(s);
293 return (new Signature(opts));
294}
295
296Signature.isSignature = function (obj, ver) {
297 return (utils.isCompatible(obj, Signature, ver));
298};
299
300/*
301 * API versions for Signature:
302 * [1,0] -- initial ver
303 * [2,0] -- support for rsa in full ssh format, compat with sshpk-agent
304 * hashAlgorithm property
305 * [2,1] -- first tagged version
306 */
307Signature.prototype._sshpkApiVersion = [2, 1];
308
309Signature._oldVersionDetect = function (obj) {
310 assert.func(obj.toBuffer);
311 if (obj.hasOwnProperty('hashAlgorithm'))
312 return ([2, 0]);
313 return ([1, 0]);
314};
Note: See TracBrowser for help on using the repository browser.