source: trip-planner-front/node_modules/sshpk/lib/formats/openssh-cert.js@ 6a80231

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

initial commit

  • Property mode set to 100644
File size: 8.5 KB
Line 
1// Copyright 2017 Joyent, Inc.
2
3module.exports = {
4 read: read,
5 verify: verify,
6 sign: sign,
7 signAsync: signAsync,
8 write: write,
9
10 /* Internal private API */
11 fromBuffer: fromBuffer,
12 toBuffer: toBuffer
13};
14
15var assert = require('assert-plus');
16var SSHBuffer = require('../ssh-buffer');
17var crypto = require('crypto');
18var Buffer = require('safer-buffer').Buffer;
19var algs = require('../algs');
20var Key = require('../key');
21var PrivateKey = require('../private-key');
22var Identity = require('../identity');
23var rfc4253 = require('./rfc4253');
24var Signature = require('../signature');
25var utils = require('../utils');
26var Certificate = require('../certificate');
27
28function verify(cert, key) {
29 /*
30 * We always give an issuerKey, so if our verify() is being called then
31 * there was no signature. Return false.
32 */
33 return (false);
34}
35
36var TYPES = {
37 'user': 1,
38 'host': 2
39};
40Object.keys(TYPES).forEach(function (k) { TYPES[TYPES[k]] = k; });
41
42var ECDSA_ALGO = /^ecdsa-sha2-([^@-]+)-cert-v01@openssh.com$/;
43
44function read(buf, options) {
45 if (Buffer.isBuffer(buf))
46 buf = buf.toString('ascii');
47 var parts = buf.trim().split(/[ \t\n]+/g);
48 if (parts.length < 2 || parts.length > 3)
49 throw (new Error('Not a valid SSH certificate line'));
50
51 var algo = parts[0];
52 var data = parts[1];
53
54 data = Buffer.from(data, 'base64');
55 return (fromBuffer(data, algo));
56}
57
58function fromBuffer(data, algo, partial) {
59 var sshbuf = new SSHBuffer({ buffer: data });
60 var innerAlgo = sshbuf.readString();
61 if (algo !== undefined && innerAlgo !== algo)
62 throw (new Error('SSH certificate algorithm mismatch'));
63 if (algo === undefined)
64 algo = innerAlgo;
65
66 var cert = {};
67 cert.signatures = {};
68 cert.signatures.openssh = {};
69
70 cert.signatures.openssh.nonce = sshbuf.readBuffer();
71
72 var key = {};
73 var parts = (key.parts = []);
74 key.type = getAlg(algo);
75
76 var partCount = algs.info[key.type].parts.length;
77 while (parts.length < partCount)
78 parts.push(sshbuf.readPart());
79 assert.ok(parts.length >= 1, 'key must have at least one part');
80
81 var algInfo = algs.info[key.type];
82 if (key.type === 'ecdsa') {
83 var res = ECDSA_ALGO.exec(algo);
84 assert.ok(res !== null);
85 assert.strictEqual(res[1], parts[0].data.toString());
86 }
87
88 for (var i = 0; i < algInfo.parts.length; ++i) {
89 parts[i].name = algInfo.parts[i];
90 if (parts[i].name !== 'curve' &&
91 algInfo.normalize !== false) {
92 var p = parts[i];
93 p.data = utils.mpNormalize(p.data);
94 }
95 }
96
97 cert.subjectKey = new Key(key);
98
99 cert.serial = sshbuf.readInt64();
100
101 var type = TYPES[sshbuf.readInt()];
102 assert.string(type, 'valid cert type');
103
104 cert.signatures.openssh.keyId = sshbuf.readString();
105
106 var principals = [];
107 var pbuf = sshbuf.readBuffer();
108 var psshbuf = new SSHBuffer({ buffer: pbuf });
109 while (!psshbuf.atEnd())
110 principals.push(psshbuf.readString());
111 if (principals.length === 0)
112 principals = ['*'];
113
114 cert.subjects = principals.map(function (pr) {
115 if (type === 'user')
116 return (Identity.forUser(pr));
117 else if (type === 'host')
118 return (Identity.forHost(pr));
119 throw (new Error('Unknown identity type ' + type));
120 });
121
122 cert.validFrom = int64ToDate(sshbuf.readInt64());
123 cert.validUntil = int64ToDate(sshbuf.readInt64());
124
125 var exts = [];
126 var extbuf = new SSHBuffer({ buffer: sshbuf.readBuffer() });
127 var ext;
128 while (!extbuf.atEnd()) {
129 ext = { critical: true };
130 ext.name = extbuf.readString();
131 ext.data = extbuf.readBuffer();
132 exts.push(ext);
133 }
134 extbuf = new SSHBuffer({ buffer: sshbuf.readBuffer() });
135 while (!extbuf.atEnd()) {
136 ext = { critical: false };
137 ext.name = extbuf.readString();
138 ext.data = extbuf.readBuffer();
139 exts.push(ext);
140 }
141 cert.signatures.openssh.exts = exts;
142
143 /* reserved */
144 sshbuf.readBuffer();
145
146 var signingKeyBuf = sshbuf.readBuffer();
147 cert.issuerKey = rfc4253.read(signingKeyBuf);
148
149 /*
150 * OpenSSH certs don't give the identity of the issuer, just their
151 * public key. So, we use an Identity that matches anything. The
152 * isSignedBy() function will later tell you if the key matches.
153 */
154 cert.issuer = Identity.forHost('**');
155
156 var sigBuf = sshbuf.readBuffer();
157 cert.signatures.openssh.signature =
158 Signature.parse(sigBuf, cert.issuerKey.type, 'ssh');
159
160 if (partial !== undefined) {
161 partial.remainder = sshbuf.remainder();
162 partial.consumed = sshbuf._offset;
163 }
164
165 return (new Certificate(cert));
166}
167
168function int64ToDate(buf) {
169 var i = buf.readUInt32BE(0) * 4294967296;
170 i += buf.readUInt32BE(4);
171 var d = new Date();
172 d.setTime(i * 1000);
173 d.sourceInt64 = buf;
174 return (d);
175}
176
177function dateToInt64(date) {
178 if (date.sourceInt64 !== undefined)
179 return (date.sourceInt64);
180 var i = Math.round(date.getTime() / 1000);
181 var upper = Math.floor(i / 4294967296);
182 var lower = Math.floor(i % 4294967296);
183 var buf = Buffer.alloc(8);
184 buf.writeUInt32BE(upper, 0);
185 buf.writeUInt32BE(lower, 4);
186 return (buf);
187}
188
189function sign(cert, key) {
190 if (cert.signatures.openssh === undefined)
191 cert.signatures.openssh = {};
192 try {
193 var blob = toBuffer(cert, true);
194 } catch (e) {
195 delete (cert.signatures.openssh);
196 return (false);
197 }
198 var sig = cert.signatures.openssh;
199 var hashAlgo = undefined;
200 if (key.type === 'rsa' || key.type === 'dsa')
201 hashAlgo = 'sha1';
202 var signer = key.createSign(hashAlgo);
203 signer.write(blob);
204 sig.signature = signer.sign();
205 return (true);
206}
207
208function signAsync(cert, signer, done) {
209 if (cert.signatures.openssh === undefined)
210 cert.signatures.openssh = {};
211 try {
212 var blob = toBuffer(cert, true);
213 } catch (e) {
214 delete (cert.signatures.openssh);
215 done(e);
216 return;
217 }
218 var sig = cert.signatures.openssh;
219
220 signer(blob, function (err, signature) {
221 if (err) {
222 done(err);
223 return;
224 }
225 try {
226 /*
227 * This will throw if the signature isn't of a
228 * type/algo that can be used for SSH.
229 */
230 signature.toBuffer('ssh');
231 } catch (e) {
232 done(e);
233 return;
234 }
235 sig.signature = signature;
236 done();
237 });
238}
239
240function write(cert, options) {
241 if (options === undefined)
242 options = {};
243
244 var blob = toBuffer(cert);
245 var out = getCertType(cert.subjectKey) + ' ' + blob.toString('base64');
246 if (options.comment)
247 out = out + ' ' + options.comment;
248 return (out);
249}
250
251
252function toBuffer(cert, noSig) {
253 assert.object(cert.signatures.openssh, 'signature for openssh format');
254 var sig = cert.signatures.openssh;
255
256 if (sig.nonce === undefined)
257 sig.nonce = crypto.randomBytes(16);
258 var buf = new SSHBuffer({});
259 buf.writeString(getCertType(cert.subjectKey));
260 buf.writeBuffer(sig.nonce);
261
262 var key = cert.subjectKey;
263 var algInfo = algs.info[key.type];
264 algInfo.parts.forEach(function (part) {
265 buf.writePart(key.part[part]);
266 });
267
268 buf.writeInt64(cert.serial);
269
270 var type = cert.subjects[0].type;
271 assert.notStrictEqual(type, 'unknown');
272 cert.subjects.forEach(function (id) {
273 assert.strictEqual(id.type, type);
274 });
275 type = TYPES[type];
276 buf.writeInt(type);
277
278 if (sig.keyId === undefined) {
279 sig.keyId = cert.subjects[0].type + '_' +
280 (cert.subjects[0].uid || cert.subjects[0].hostname);
281 }
282 buf.writeString(sig.keyId);
283
284 var sub = new SSHBuffer({});
285 cert.subjects.forEach(function (id) {
286 if (type === TYPES.host)
287 sub.writeString(id.hostname);
288 else if (type === TYPES.user)
289 sub.writeString(id.uid);
290 });
291 buf.writeBuffer(sub.toBuffer());
292
293 buf.writeInt64(dateToInt64(cert.validFrom));
294 buf.writeInt64(dateToInt64(cert.validUntil));
295
296 var exts = sig.exts;
297 if (exts === undefined)
298 exts = [];
299
300 var extbuf = new SSHBuffer({});
301 exts.forEach(function (ext) {
302 if (ext.critical !== true)
303 return;
304 extbuf.writeString(ext.name);
305 extbuf.writeBuffer(ext.data);
306 });
307 buf.writeBuffer(extbuf.toBuffer());
308
309 extbuf = new SSHBuffer({});
310 exts.forEach(function (ext) {
311 if (ext.critical === true)
312 return;
313 extbuf.writeString(ext.name);
314 extbuf.writeBuffer(ext.data);
315 });
316 buf.writeBuffer(extbuf.toBuffer());
317
318 /* reserved */
319 buf.writeBuffer(Buffer.alloc(0));
320
321 sub = rfc4253.write(cert.issuerKey);
322 buf.writeBuffer(sub);
323
324 if (!noSig)
325 buf.writeBuffer(sig.signature.toBuffer('ssh'));
326
327 return (buf.toBuffer());
328}
329
330function getAlg(certType) {
331 if (certType === 'ssh-rsa-cert-v01@openssh.com')
332 return ('rsa');
333 if (certType === 'ssh-dss-cert-v01@openssh.com')
334 return ('dsa');
335 if (certType.match(ECDSA_ALGO))
336 return ('ecdsa');
337 if (certType === 'ssh-ed25519-cert-v01@openssh.com')
338 return ('ed25519');
339 throw (new Error('Unsupported cert type ' + certType));
340}
341
342function getCertType(key) {
343 if (key.type === 'rsa')
344 return ('ssh-rsa-cert-v01@openssh.com');
345 if (key.type === 'dsa')
346 return ('ssh-dss-cert-v01@openssh.com');
347 if (key.type === 'ecdsa')
348 return ('ecdsa-sha2-' + key.curve + '-cert-v01@openssh.com');
349 if (key.type === 'ed25519')
350 return ('ssh-ed25519-cert-v01@openssh.com');
351 throw (new Error('Unsupported key type ' + key.type));
352}
Note: See TracBrowser for help on using the repository browser.