source: trip-planner-front/node_modules/sshpk/lib/identity.js@ ceaed42

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

initial commit

  • Property mode set to 100644
File size: 9.8 KB
RevLine 
[6a3a178]1// Copyright 2017 Joyent, Inc.
2
3module.exports = Identity;
4
5var assert = require('assert-plus');
6var algs = require('./algs');
7var crypto = require('crypto');
8var Fingerprint = require('./fingerprint');
9var Signature = require('./signature');
10var errs = require('./errors');
11var util = require('util');
12var utils = require('./utils');
13var asn1 = require('asn1');
14var Buffer = require('safer-buffer').Buffer;
15
16/*JSSTYLED*/
17var DNS_NAME_RE = /^([*]|[a-z0-9][a-z0-9\-]{0,62})(?:\.([*]|[a-z0-9][a-z0-9\-]{0,62}))*$/i;
18
19var oids = {};
20oids.cn = '2.5.4.3';
21oids.o = '2.5.4.10';
22oids.ou = '2.5.4.11';
23oids.l = '2.5.4.7';
24oids.s = '2.5.4.8';
25oids.c = '2.5.4.6';
26oids.sn = '2.5.4.4';
27oids.postalCode = '2.5.4.17';
28oids.serialNumber = '2.5.4.5';
29oids.street = '2.5.4.9';
30oids.x500UniqueIdentifier = '2.5.4.45';
31oids.role = '2.5.4.72';
32oids.telephoneNumber = '2.5.4.20';
33oids.description = '2.5.4.13';
34oids.dc = '0.9.2342.19200300.100.1.25';
35oids.uid = '0.9.2342.19200300.100.1.1';
36oids.mail = '0.9.2342.19200300.100.1.3';
37oids.title = '2.5.4.12';
38oids.gn = '2.5.4.42';
39oids.initials = '2.5.4.43';
40oids.pseudonym = '2.5.4.65';
41oids.emailAddress = '1.2.840.113549.1.9.1';
42
43var unoids = {};
44Object.keys(oids).forEach(function (k) {
45 unoids[oids[k]] = k;
46});
47
48function Identity(opts) {
49 var self = this;
50 assert.object(opts, 'options');
51 assert.arrayOfObject(opts.components, 'options.components');
52 this.components = opts.components;
53 this.componentLookup = {};
54 this.components.forEach(function (c) {
55 if (c.name && !c.oid)
56 c.oid = oids[c.name];
57 if (c.oid && !c.name)
58 c.name = unoids[c.oid];
59 if (self.componentLookup[c.name] === undefined)
60 self.componentLookup[c.name] = [];
61 self.componentLookup[c.name].push(c);
62 });
63 if (this.componentLookup.cn && this.componentLookup.cn.length > 0) {
64 this.cn = this.componentLookup.cn[0].value;
65 }
66 assert.optionalString(opts.type, 'options.type');
67 if (opts.type === undefined) {
68 if (this.components.length === 1 &&
69 this.componentLookup.cn &&
70 this.componentLookup.cn.length === 1 &&
71 this.componentLookup.cn[0].value.match(DNS_NAME_RE)) {
72 this.type = 'host';
73 this.hostname = this.componentLookup.cn[0].value;
74
75 } else if (this.componentLookup.dc &&
76 this.components.length === this.componentLookup.dc.length) {
77 this.type = 'host';
78 this.hostname = this.componentLookup.dc.map(
79 function (c) {
80 return (c.value);
81 }).join('.');
82
83 } else if (this.componentLookup.uid &&
84 this.components.length ===
85 this.componentLookup.uid.length) {
86 this.type = 'user';
87 this.uid = this.componentLookup.uid[0].value;
88
89 } else if (this.componentLookup.cn &&
90 this.componentLookup.cn.length === 1 &&
91 this.componentLookup.cn[0].value.match(DNS_NAME_RE)) {
92 this.type = 'host';
93 this.hostname = this.componentLookup.cn[0].value;
94
95 } else if (this.componentLookup.uid &&
96 this.componentLookup.uid.length === 1) {
97 this.type = 'user';
98 this.uid = this.componentLookup.uid[0].value;
99
100 } else if (this.componentLookup.mail &&
101 this.componentLookup.mail.length === 1) {
102 this.type = 'email';
103 this.email = this.componentLookup.mail[0].value;
104
105 } else if (this.componentLookup.cn &&
106 this.componentLookup.cn.length === 1) {
107 this.type = 'user';
108 this.uid = this.componentLookup.cn[0].value;
109
110 } else {
111 this.type = 'unknown';
112 }
113 } else {
114 this.type = opts.type;
115 if (this.type === 'host')
116 this.hostname = opts.hostname;
117 else if (this.type === 'user')
118 this.uid = opts.uid;
119 else if (this.type === 'email')
120 this.email = opts.email;
121 else
122 throw (new Error('Unknown type ' + this.type));
123 }
124}
125
126Identity.prototype.toString = function () {
127 return (this.components.map(function (c) {
128 var n = c.name.toUpperCase();
129 /*JSSTYLED*/
130 n = n.replace(/=/g, '\\=');
131 var v = c.value;
132 /*JSSTYLED*/
133 v = v.replace(/,/g, '\\,');
134 return (n + '=' + v);
135 }).join(', '));
136};
137
138Identity.prototype.get = function (name, asArray) {
139 assert.string(name, 'name');
140 var arr = this.componentLookup[name];
141 if (arr === undefined || arr.length === 0)
142 return (undefined);
143 if (!asArray && arr.length > 1)
144 throw (new Error('Multiple values for attribute ' + name));
145 if (!asArray)
146 return (arr[0].value);
147 return (arr.map(function (c) {
148 return (c.value);
149 }));
150};
151
152Identity.prototype.toArray = function (idx) {
153 return (this.components.map(function (c) {
154 return ({
155 name: c.name,
156 value: c.value
157 });
158 }));
159};
160
161/*
162 * These are from X.680 -- PrintableString allowed chars are in section 37.4
163 * table 8. Spec for IA5Strings is "1,6 + SPACE + DEL" where 1 refers to
164 * ISO IR #001 (standard ASCII control characters) and 6 refers to ISO IR #006
165 * (the basic ASCII character set).
166 */
167/* JSSTYLED */
168var NOT_PRINTABLE = /[^a-zA-Z0-9 '(),+.\/:=?-]/;
169/* JSSTYLED */
170var NOT_IA5 = /[^\x00-\x7f]/;
171
172Identity.prototype.toAsn1 = function (der, tag) {
173 der.startSequence(tag);
174 this.components.forEach(function (c) {
175 der.startSequence(asn1.Ber.Constructor | asn1.Ber.Set);
176 der.startSequence();
177 der.writeOID(c.oid);
178 /*
179 * If we fit in a PrintableString, use that. Otherwise use an
180 * IA5String or UTF8String.
181 *
182 * If this identity was parsed from a DN, use the ASN.1 types
183 * from the original representation (otherwise this might not
184 * be a full match for the original in some validators).
185 */
186 if (c.asn1type === asn1.Ber.Utf8String ||
187 c.value.match(NOT_IA5)) {
188 var v = Buffer.from(c.value, 'utf8');
189 der.writeBuffer(v, asn1.Ber.Utf8String);
190
191 } else if (c.asn1type === asn1.Ber.IA5String ||
192 c.value.match(NOT_PRINTABLE)) {
193 der.writeString(c.value, asn1.Ber.IA5String);
194
195 } else {
196 var type = asn1.Ber.PrintableString;
197 if (c.asn1type !== undefined)
198 type = c.asn1type;
199 der.writeString(c.value, type);
200 }
201 der.endSequence();
202 der.endSequence();
203 });
204 der.endSequence();
205};
206
207function globMatch(a, b) {
208 if (a === '**' || b === '**')
209 return (true);
210 var aParts = a.split('.');
211 var bParts = b.split('.');
212 if (aParts.length !== bParts.length)
213 return (false);
214 for (var i = 0; i < aParts.length; ++i) {
215 if (aParts[i] === '*' || bParts[i] === '*')
216 continue;
217 if (aParts[i] !== bParts[i])
218 return (false);
219 }
220 return (true);
221}
222
223Identity.prototype.equals = function (other) {
224 if (!Identity.isIdentity(other, [1, 0]))
225 return (false);
226 if (other.components.length !== this.components.length)
227 return (false);
228 for (var i = 0; i < this.components.length; ++i) {
229 if (this.components[i].oid !== other.components[i].oid)
230 return (false);
231 if (!globMatch(this.components[i].value,
232 other.components[i].value)) {
233 return (false);
234 }
235 }
236 return (true);
237};
238
239Identity.forHost = function (hostname) {
240 assert.string(hostname, 'hostname');
241 return (new Identity({
242 type: 'host',
243 hostname: hostname,
244 components: [ { name: 'cn', value: hostname } ]
245 }));
246};
247
248Identity.forUser = function (uid) {
249 assert.string(uid, 'uid');
250 return (new Identity({
251 type: 'user',
252 uid: uid,
253 components: [ { name: 'uid', value: uid } ]
254 }));
255};
256
257Identity.forEmail = function (email) {
258 assert.string(email, 'email');
259 return (new Identity({
260 type: 'email',
261 email: email,
262 components: [ { name: 'mail', value: email } ]
263 }));
264};
265
266Identity.parseDN = function (dn) {
267 assert.string(dn, 'dn');
268 var parts = [''];
269 var idx = 0;
270 var rem = dn;
271 while (rem.length > 0) {
272 var m;
273 /*JSSTYLED*/
274 if ((m = /^,/.exec(rem)) !== null) {
275 parts[++idx] = '';
276 rem = rem.slice(m[0].length);
277 /*JSSTYLED*/
278 } else if ((m = /^\\,/.exec(rem)) !== null) {
279 parts[idx] += ',';
280 rem = rem.slice(m[0].length);
281 /*JSSTYLED*/
282 } else if ((m = /^\\./.exec(rem)) !== null) {
283 parts[idx] += m[0];
284 rem = rem.slice(m[0].length);
285 /*JSSTYLED*/
286 } else if ((m = /^[^\\,]+/.exec(rem)) !== null) {
287 parts[idx] += m[0];
288 rem = rem.slice(m[0].length);
289 } else {
290 throw (new Error('Failed to parse DN'));
291 }
292 }
293 var cmps = parts.map(function (c) {
294 c = c.trim();
295 var eqPos = c.indexOf('=');
296 while (eqPos > 0 && c.charAt(eqPos - 1) === '\\')
297 eqPos = c.indexOf('=', eqPos + 1);
298 if (eqPos === -1) {
299 throw (new Error('Failed to parse DN'));
300 }
301 /*JSSTYLED*/
302 var name = c.slice(0, eqPos).toLowerCase().replace(/\\=/g, '=');
303 var value = c.slice(eqPos + 1);
304 return ({ name: name, value: value });
305 });
306 return (new Identity({ components: cmps }));
307};
308
309Identity.fromArray = function (components) {
310 assert.arrayOfObject(components, 'components');
311 components.forEach(function (cmp) {
312 assert.object(cmp, 'component');
313 assert.string(cmp.name, 'component.name');
314 if (!Buffer.isBuffer(cmp.value) &&
315 !(typeof (cmp.value) === 'string')) {
316 throw (new Error('Invalid component value'));
317 }
318 });
319 return (new Identity({ components: components }));
320};
321
322Identity.parseAsn1 = function (der, top) {
323 var components = [];
324 der.readSequence(top);
325 var end = der.offset + der.length;
326 while (der.offset < end) {
327 der.readSequence(asn1.Ber.Constructor | asn1.Ber.Set);
328 var after = der.offset + der.length;
329 der.readSequence();
330 var oid = der.readOID();
331 var type = der.peek();
332 var value;
333 switch (type) {
334 case asn1.Ber.PrintableString:
335 case asn1.Ber.IA5String:
336 case asn1.Ber.OctetString:
337 case asn1.Ber.T61String:
338 value = der.readString(type);
339 break;
340 case asn1.Ber.Utf8String:
341 value = der.readString(type, true);
342 value = value.toString('utf8');
343 break;
344 case asn1.Ber.CharacterString:
345 case asn1.Ber.BMPString:
346 value = der.readString(type, true);
347 value = value.toString('utf16le');
348 break;
349 default:
350 throw (new Error('Unknown asn1 type ' + type));
351 }
352 components.push({ oid: oid, asn1type: type, value: value });
353 der._offset = after;
354 }
355 der._offset = end;
356 return (new Identity({
357 components: components
358 }));
359};
360
361Identity.isIdentity = function (obj, ver) {
362 return (utils.isCompatible(obj, Identity, ver));
363};
364
365/*
366 * API versions for Identity:
367 * [1,0] -- initial ver
368 */
369Identity.prototype._sshpkApiVersion = [1, 0];
370
371Identity._oldVersionDetect = function (obj) {
372 return ([1, 0]);
373};
Note: See TracBrowser for help on using the repository browser.