source: trip-planner-front/node_modules/sshpk/lib/formats/x509.js@ 6fe77af

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

initial commit

  • Property mode set to 100644
File size: 19.1 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
11var assert = require('assert-plus');
12var asn1 = require('asn1');
13var Buffer = require('safer-buffer').Buffer;
14var algs = require('../algs');
15var utils = require('../utils');
16var Key = require('../key');
17var PrivateKey = require('../private-key');
18var pem = require('./pem');
19var Identity = require('../identity');
20var Signature = require('../signature');
21var Certificate = require('../certificate');
22var pkcs8 = require('./pkcs8');
23
24/*
25 * This file is based on RFC5280 (X.509).
26 */
27
28/* Helper to read in a single mpint */
29function readMPInt(der, nm) {
30 assert.strictEqual(der.peek(), asn1.Ber.Integer,
31 nm + ' is not an Integer');
32 return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true)));
33}
34
35function verify(cert, key) {
36 var sig = cert.signatures.x509;
37 assert.object(sig, 'x509 signature');
38
39 var algParts = sig.algo.split('-');
40 if (algParts[0] !== key.type)
41 return (false);
42
43 var blob = sig.cache;
44 if (blob === undefined) {
45 var der = new asn1.BerWriter();
46 writeTBSCert(cert, der);
47 blob = der.buffer;
48 }
49
50 var verifier = key.createVerify(algParts[1]);
51 verifier.write(blob);
52 return (verifier.verify(sig.signature));
53}
54
55function Local(i) {
56 return (asn1.Ber.Context | asn1.Ber.Constructor | i);
57}
58
59function Context(i) {
60 return (asn1.Ber.Context | i);
61}
62
63var SIGN_ALGS = {
64 'rsa-md5': '1.2.840.113549.1.1.4',
65 'rsa-sha1': '1.2.840.113549.1.1.5',
66 'rsa-sha256': '1.2.840.113549.1.1.11',
67 'rsa-sha384': '1.2.840.113549.1.1.12',
68 'rsa-sha512': '1.2.840.113549.1.1.13',
69 'dsa-sha1': '1.2.840.10040.4.3',
70 'dsa-sha256': '2.16.840.1.101.3.4.3.2',
71 'ecdsa-sha1': '1.2.840.10045.4.1',
72 'ecdsa-sha256': '1.2.840.10045.4.3.2',
73 'ecdsa-sha384': '1.2.840.10045.4.3.3',
74 'ecdsa-sha512': '1.2.840.10045.4.3.4',
75 'ed25519-sha512': '1.3.101.112'
76};
77Object.keys(SIGN_ALGS).forEach(function (k) {
78 SIGN_ALGS[SIGN_ALGS[k]] = k;
79});
80SIGN_ALGS['1.3.14.3.2.3'] = 'rsa-md5';
81SIGN_ALGS['1.3.14.3.2.29'] = 'rsa-sha1';
82
83var EXTS = {
84 'issuerKeyId': '2.5.29.35',
85 'altName': '2.5.29.17',
86 'basicConstraints': '2.5.29.19',
87 'keyUsage': '2.5.29.15',
88 'extKeyUsage': '2.5.29.37'
89};
90
91function read(buf, options) {
92 if (typeof (buf) === 'string') {
93 buf = Buffer.from(buf, 'binary');
94 }
95 assert.buffer(buf, 'buf');
96
97 var der = new asn1.BerReader(buf);
98
99 der.readSequence();
100 if (Math.abs(der.length - der.remain) > 1) {
101 throw (new Error('DER sequence does not contain whole byte ' +
102 'stream'));
103 }
104
105 var tbsStart = der.offset;
106 der.readSequence();
107 var sigOffset = der.offset + der.length;
108 var tbsEnd = sigOffset;
109
110 if (der.peek() === Local(0)) {
111 der.readSequence(Local(0));
112 var version = der.readInt();
113 assert.ok(version <= 3,
114 'only x.509 versions up to v3 supported');
115 }
116
117 var cert = {};
118 cert.signatures = {};
119 var sig = (cert.signatures.x509 = {});
120 sig.extras = {};
121
122 cert.serial = readMPInt(der, 'serial');
123
124 der.readSequence();
125 var after = der.offset + der.length;
126 var certAlgOid = der.readOID();
127 var certAlg = SIGN_ALGS[certAlgOid];
128 if (certAlg === undefined)
129 throw (new Error('unknown signature algorithm ' + certAlgOid));
130
131 der._offset = after;
132 cert.issuer = Identity.parseAsn1(der);
133
134 der.readSequence();
135 cert.validFrom = readDate(der);
136 cert.validUntil = readDate(der);
137
138 cert.subjects = [Identity.parseAsn1(der)];
139
140 der.readSequence();
141 after = der.offset + der.length;
142 cert.subjectKey = pkcs8.readPkcs8(undefined, 'public', der);
143 der._offset = after;
144
145 /* issuerUniqueID */
146 if (der.peek() === Local(1)) {
147 der.readSequence(Local(1));
148 sig.extras.issuerUniqueID =
149 buf.slice(der.offset, der.offset + der.length);
150 der._offset += der.length;
151 }
152
153 /* subjectUniqueID */
154 if (der.peek() === Local(2)) {
155 der.readSequence(Local(2));
156 sig.extras.subjectUniqueID =
157 buf.slice(der.offset, der.offset + der.length);
158 der._offset += der.length;
159 }
160
161 /* extensions */
162 if (der.peek() === Local(3)) {
163 der.readSequence(Local(3));
164 var extEnd = der.offset + der.length;
165 der.readSequence();
166
167 while (der.offset < extEnd)
168 readExtension(cert, buf, der);
169
170 assert.strictEqual(der.offset, extEnd);
171 }
172
173 assert.strictEqual(der.offset, sigOffset);
174
175 der.readSequence();
176 after = der.offset + der.length;
177 var sigAlgOid = der.readOID();
178 var sigAlg = SIGN_ALGS[sigAlgOid];
179 if (sigAlg === undefined)
180 throw (new Error('unknown signature algorithm ' + sigAlgOid));
181 der._offset = after;
182
183 var sigData = der.readString(asn1.Ber.BitString, true);
184 if (sigData[0] === 0)
185 sigData = sigData.slice(1);
186 var algParts = sigAlg.split('-');
187
188 sig.signature = Signature.parse(sigData, algParts[0], 'asn1');
189 sig.signature.hashAlgorithm = algParts[1];
190 sig.algo = sigAlg;
191 sig.cache = buf.slice(tbsStart, tbsEnd);
192
193 return (new Certificate(cert));
194}
195
196function readDate(der) {
197 if (der.peek() === asn1.Ber.UTCTime) {
198 return (utcTimeToDate(der.readString(asn1.Ber.UTCTime)));
199 } else if (der.peek() === asn1.Ber.GeneralizedTime) {
200 return (gTimeToDate(der.readString(asn1.Ber.GeneralizedTime)));
201 } else {
202 throw (new Error('Unsupported date format'));
203 }
204}
205
206function writeDate(der, date) {
207 if (date.getUTCFullYear() >= 2050 || date.getUTCFullYear() < 1950) {
208 der.writeString(dateToGTime(date), asn1.Ber.GeneralizedTime);
209 } else {
210 der.writeString(dateToUTCTime(date), asn1.Ber.UTCTime);
211 }
212}
213
214/* RFC5280, section 4.2.1.6 (GeneralName type) */
215var ALTNAME = {
216 OtherName: Local(0),
217 RFC822Name: Context(1),
218 DNSName: Context(2),
219 X400Address: Local(3),
220 DirectoryName: Local(4),
221 EDIPartyName: Local(5),
222 URI: Context(6),
223 IPAddress: Context(7),
224 OID: Context(8)
225};
226
227/* RFC5280, section 4.2.1.12 (KeyPurposeId) */
228var EXTPURPOSE = {
229 'serverAuth': '1.3.6.1.5.5.7.3.1',
230 'clientAuth': '1.3.6.1.5.5.7.3.2',
231 'codeSigning': '1.3.6.1.5.5.7.3.3',
232
233 /* See https://github.com/joyent/oid-docs/blob/master/root.md */
234 'joyentDocker': '1.3.6.1.4.1.38678.1.4.1',
235 'joyentCmon': '1.3.6.1.4.1.38678.1.4.2'
236};
237var EXTPURPOSE_REV = {};
238Object.keys(EXTPURPOSE).forEach(function (k) {
239 EXTPURPOSE_REV[EXTPURPOSE[k]] = k;
240});
241
242var KEYUSEBITS = [
243 'signature', 'identity', 'keyEncryption',
244 'encryption', 'keyAgreement', 'ca', 'crl'
245];
246
247function readExtension(cert, buf, der) {
248 der.readSequence();
249 var after = der.offset + der.length;
250 var extId = der.readOID();
251 var id;
252 var sig = cert.signatures.x509;
253 if (!sig.extras.exts)
254 sig.extras.exts = [];
255
256 var critical;
257 if (der.peek() === asn1.Ber.Boolean)
258 critical = der.readBoolean();
259
260 switch (extId) {
261 case (EXTS.basicConstraints):
262 der.readSequence(asn1.Ber.OctetString);
263 der.readSequence();
264 var bcEnd = der.offset + der.length;
265 var ca = false;
266 if (der.peek() === asn1.Ber.Boolean)
267 ca = der.readBoolean();
268 if (cert.purposes === undefined)
269 cert.purposes = [];
270 if (ca === true)
271 cert.purposes.push('ca');
272 var bc = { oid: extId, critical: critical };
273 if (der.offset < bcEnd && der.peek() === asn1.Ber.Integer)
274 bc.pathLen = der.readInt();
275 sig.extras.exts.push(bc);
276 break;
277 case (EXTS.extKeyUsage):
278 der.readSequence(asn1.Ber.OctetString);
279 der.readSequence();
280 if (cert.purposes === undefined)
281 cert.purposes = [];
282 var ekEnd = der.offset + der.length;
283 while (der.offset < ekEnd) {
284 var oid = der.readOID();
285 cert.purposes.push(EXTPURPOSE_REV[oid] || oid);
286 }
287 /*
288 * This is a bit of a hack: in the case where we have a cert
289 * that's only allowed to do serverAuth or clientAuth (and not
290 * the other), we want to make sure all our Subjects are of
291 * the right type. But we already parsed our Subjects and
292 * decided if they were hosts or users earlier (since it appears
293 * first in the cert).
294 *
295 * So we go through and mutate them into the right kind here if
296 * it doesn't match. This might not be hugely beneficial, as it
297 * seems that single-purpose certs are not often seen in the
298 * wild.
299 */
300 if (cert.purposes.indexOf('serverAuth') !== -1 &&
301 cert.purposes.indexOf('clientAuth') === -1) {
302 cert.subjects.forEach(function (ide) {
303 if (ide.type !== 'host') {
304 ide.type = 'host';
305 ide.hostname = ide.uid ||
306 ide.email ||
307 ide.components[0].value;
308 }
309 });
310 } else if (cert.purposes.indexOf('clientAuth') !== -1 &&
311 cert.purposes.indexOf('serverAuth') === -1) {
312 cert.subjects.forEach(function (ide) {
313 if (ide.type !== 'user') {
314 ide.type = 'user';
315 ide.uid = ide.hostname ||
316 ide.email ||
317 ide.components[0].value;
318 }
319 });
320 }
321 sig.extras.exts.push({ oid: extId, critical: critical });
322 break;
323 case (EXTS.keyUsage):
324 der.readSequence(asn1.Ber.OctetString);
325 var bits = der.readString(asn1.Ber.BitString, true);
326 var setBits = readBitField(bits, KEYUSEBITS);
327 setBits.forEach(function (bit) {
328 if (cert.purposes === undefined)
329 cert.purposes = [];
330 if (cert.purposes.indexOf(bit) === -1)
331 cert.purposes.push(bit);
332 });
333 sig.extras.exts.push({ oid: extId, critical: critical,
334 bits: bits });
335 break;
336 case (EXTS.altName):
337 der.readSequence(asn1.Ber.OctetString);
338 der.readSequence();
339 var aeEnd = der.offset + der.length;
340 while (der.offset < aeEnd) {
341 switch (der.peek()) {
342 case ALTNAME.OtherName:
343 case ALTNAME.EDIPartyName:
344 der.readSequence();
345 der._offset += der.length;
346 break;
347 case ALTNAME.OID:
348 der.readOID(ALTNAME.OID);
349 break;
350 case ALTNAME.RFC822Name:
351 /* RFC822 specifies email addresses */
352 var email = der.readString(ALTNAME.RFC822Name);
353 id = Identity.forEmail(email);
354 if (!cert.subjects[0].equals(id))
355 cert.subjects.push(id);
356 break;
357 case ALTNAME.DirectoryName:
358 der.readSequence(ALTNAME.DirectoryName);
359 id = Identity.parseAsn1(der);
360 if (!cert.subjects[0].equals(id))
361 cert.subjects.push(id);
362 break;
363 case ALTNAME.DNSName:
364 var host = der.readString(
365 ALTNAME.DNSName);
366 id = Identity.forHost(host);
367 if (!cert.subjects[0].equals(id))
368 cert.subjects.push(id);
369 break;
370 default:
371 der.readString(der.peek());
372 break;
373 }
374 }
375 sig.extras.exts.push({ oid: extId, critical: critical });
376 break;
377 default:
378 sig.extras.exts.push({
379 oid: extId,
380 critical: critical,
381 data: der.readString(asn1.Ber.OctetString, true)
382 });
383 break;
384 }
385
386 der._offset = after;
387}
388
389var UTCTIME_RE =
390 /^([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/;
391function utcTimeToDate(t) {
392 var m = t.match(UTCTIME_RE);
393 assert.ok(m, 'timestamps must be in UTC');
394 var d = new Date();
395
396 var thisYear = d.getUTCFullYear();
397 var century = Math.floor(thisYear / 100) * 100;
398
399 var year = parseInt(m[1], 10);
400 if (thisYear % 100 < 50 && year >= 60)
401 year += (century - 1);
402 else
403 year += century;
404 d.setUTCFullYear(year, parseInt(m[2], 10) - 1, parseInt(m[3], 10));
405 d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10));
406 if (m[6] && m[6].length > 0)
407 d.setUTCSeconds(parseInt(m[6], 10));
408 return (d);
409}
410
411var GTIME_RE =
412 /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/;
413function gTimeToDate(t) {
414 var m = t.match(GTIME_RE);
415 assert.ok(m);
416 var d = new Date();
417
418 d.setUTCFullYear(parseInt(m[1], 10), parseInt(m[2], 10) - 1,
419 parseInt(m[3], 10));
420 d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10));
421 if (m[6] && m[6].length > 0)
422 d.setUTCSeconds(parseInt(m[6], 10));
423 return (d);
424}
425
426function zeroPad(n, m) {
427 if (m === undefined)
428 m = 2;
429 var s = '' + n;
430 while (s.length < m)
431 s = '0' + s;
432 return (s);
433}
434
435function dateToUTCTime(d) {
436 var s = '';
437 s += zeroPad(d.getUTCFullYear() % 100);
438 s += zeroPad(d.getUTCMonth() + 1);
439 s += zeroPad(d.getUTCDate());
440 s += zeroPad(d.getUTCHours());
441 s += zeroPad(d.getUTCMinutes());
442 s += zeroPad(d.getUTCSeconds());
443 s += 'Z';
444 return (s);
445}
446
447function dateToGTime(d) {
448 var s = '';
449 s += zeroPad(d.getUTCFullYear(), 4);
450 s += zeroPad(d.getUTCMonth() + 1);
451 s += zeroPad(d.getUTCDate());
452 s += zeroPad(d.getUTCHours());
453 s += zeroPad(d.getUTCMinutes());
454 s += zeroPad(d.getUTCSeconds());
455 s += 'Z';
456 return (s);
457}
458
459function sign(cert, key) {
460 if (cert.signatures.x509 === undefined)
461 cert.signatures.x509 = {};
462 var sig = cert.signatures.x509;
463
464 sig.algo = key.type + '-' + key.defaultHashAlgorithm();
465 if (SIGN_ALGS[sig.algo] === undefined)
466 return (false);
467
468 var der = new asn1.BerWriter();
469 writeTBSCert(cert, der);
470 var blob = der.buffer;
471 sig.cache = blob;
472
473 var signer = key.createSign();
474 signer.write(blob);
475 cert.signatures.x509.signature = signer.sign();
476
477 return (true);
478}
479
480function signAsync(cert, signer, done) {
481 if (cert.signatures.x509 === undefined)
482 cert.signatures.x509 = {};
483 var sig = cert.signatures.x509;
484
485 var der = new asn1.BerWriter();
486 writeTBSCert(cert, der);
487 var blob = der.buffer;
488 sig.cache = blob;
489
490 signer(blob, function (err, signature) {
491 if (err) {
492 done(err);
493 return;
494 }
495 sig.algo = signature.type + '-' + signature.hashAlgorithm;
496 if (SIGN_ALGS[sig.algo] === undefined) {
497 done(new Error('Invalid signing algorithm "' +
498 sig.algo + '"'));
499 return;
500 }
501 sig.signature = signature;
502 done();
503 });
504}
505
506function write(cert, options) {
507 var sig = cert.signatures.x509;
508 assert.object(sig, 'x509 signature');
509
510 var der = new asn1.BerWriter();
511 der.startSequence();
512 if (sig.cache) {
513 der._ensure(sig.cache.length);
514 sig.cache.copy(der._buf, der._offset);
515 der._offset += sig.cache.length;
516 } else {
517 writeTBSCert(cert, der);
518 }
519
520 der.startSequence();
521 der.writeOID(SIGN_ALGS[sig.algo]);
522 if (sig.algo.match(/^rsa-/))
523 der.writeNull();
524 der.endSequence();
525
526 var sigData = sig.signature.toBuffer('asn1');
527 var data = Buffer.alloc(sigData.length + 1);
528 data[0] = 0;
529 sigData.copy(data, 1);
530 der.writeBuffer(data, asn1.Ber.BitString);
531 der.endSequence();
532
533 return (der.buffer);
534}
535
536function writeTBSCert(cert, der) {
537 var sig = cert.signatures.x509;
538 assert.object(sig, 'x509 signature');
539
540 der.startSequence();
541
542 der.startSequence(Local(0));
543 der.writeInt(2);
544 der.endSequence();
545
546 der.writeBuffer(utils.mpNormalize(cert.serial), asn1.Ber.Integer);
547
548 der.startSequence();
549 der.writeOID(SIGN_ALGS[sig.algo]);
550 if (sig.algo.match(/^rsa-/))
551 der.writeNull();
552 der.endSequence();
553
554 cert.issuer.toAsn1(der);
555
556 der.startSequence();
557 writeDate(der, cert.validFrom);
558 writeDate(der, cert.validUntil);
559 der.endSequence();
560
561 var subject = cert.subjects[0];
562 var altNames = cert.subjects.slice(1);
563 subject.toAsn1(der);
564
565 pkcs8.writePkcs8(der, cert.subjectKey);
566
567 if (sig.extras && sig.extras.issuerUniqueID) {
568 der.writeBuffer(sig.extras.issuerUniqueID, Local(1));
569 }
570
571 if (sig.extras && sig.extras.subjectUniqueID) {
572 der.writeBuffer(sig.extras.subjectUniqueID, Local(2));
573 }
574
575 if (altNames.length > 0 || subject.type === 'host' ||
576 (cert.purposes !== undefined && cert.purposes.length > 0) ||
577 (sig.extras && sig.extras.exts)) {
578 der.startSequence(Local(3));
579 der.startSequence();
580
581 var exts = [];
582 if (cert.purposes !== undefined && cert.purposes.length > 0) {
583 exts.push({
584 oid: EXTS.basicConstraints,
585 critical: true
586 });
587 exts.push({
588 oid: EXTS.keyUsage,
589 critical: true
590 });
591 exts.push({
592 oid: EXTS.extKeyUsage,
593 critical: true
594 });
595 }
596 exts.push({ oid: EXTS.altName });
597 if (sig.extras && sig.extras.exts)
598 exts = sig.extras.exts;
599
600 for (var i = 0; i < exts.length; ++i) {
601 der.startSequence();
602 der.writeOID(exts[i].oid);
603
604 if (exts[i].critical !== undefined)
605 der.writeBoolean(exts[i].critical);
606
607 if (exts[i].oid === EXTS.altName) {
608 der.startSequence(asn1.Ber.OctetString);
609 der.startSequence();
610 if (subject.type === 'host') {
611 der.writeString(subject.hostname,
612 Context(2));
613 }
614 for (var j = 0; j < altNames.length; ++j) {
615 if (altNames[j].type === 'host') {
616 der.writeString(
617 altNames[j].hostname,
618 ALTNAME.DNSName);
619 } else if (altNames[j].type ===
620 'email') {
621 der.writeString(
622 altNames[j].email,
623 ALTNAME.RFC822Name);
624 } else {
625 /*
626 * Encode anything else as a
627 * DN style name for now.
628 */
629 der.startSequence(
630 ALTNAME.DirectoryName);
631 altNames[j].toAsn1(der);
632 der.endSequence();
633 }
634 }
635 der.endSequence();
636 der.endSequence();
637 } else if (exts[i].oid === EXTS.basicConstraints) {
638 der.startSequence(asn1.Ber.OctetString);
639 der.startSequence();
640 var ca = (cert.purposes.indexOf('ca') !== -1);
641 var pathLen = exts[i].pathLen;
642 der.writeBoolean(ca);
643 if (pathLen !== undefined)
644 der.writeInt(pathLen);
645 der.endSequence();
646 der.endSequence();
647 } else if (exts[i].oid === EXTS.extKeyUsage) {
648 der.startSequence(asn1.Ber.OctetString);
649 der.startSequence();
650 cert.purposes.forEach(function (purpose) {
651 if (purpose === 'ca')
652 return;
653 if (KEYUSEBITS.indexOf(purpose) !== -1)
654 return;
655 var oid = purpose;
656 if (EXTPURPOSE[purpose] !== undefined)
657 oid = EXTPURPOSE[purpose];
658 der.writeOID(oid);
659 });
660 der.endSequence();
661 der.endSequence();
662 } else if (exts[i].oid === EXTS.keyUsage) {
663 der.startSequence(asn1.Ber.OctetString);
664 /*
665 * If we parsed this certificate from a byte
666 * stream (i.e. we didn't generate it in sshpk)
667 * then we'll have a ".bits" property on the
668 * ext with the original raw byte contents.
669 *
670 * If we have this, use it here instead of
671 * regenerating it. This guarantees we output
672 * the same data we parsed, so signatures still
673 * validate.
674 */
675 if (exts[i].bits !== undefined) {
676 der.writeBuffer(exts[i].bits,
677 asn1.Ber.BitString);
678 } else {
679 var bits = writeBitField(cert.purposes,
680 KEYUSEBITS);
681 der.writeBuffer(bits,
682 asn1.Ber.BitString);
683 }
684 der.endSequence();
685 } else {
686 der.writeBuffer(exts[i].data,
687 asn1.Ber.OctetString);
688 }
689
690 der.endSequence();
691 }
692
693 der.endSequence();
694 der.endSequence();
695 }
696
697 der.endSequence();
698}
699
700/*
701 * Reads an ASN.1 BER bitfield out of the Buffer produced by doing
702 * `BerReader#readString(asn1.Ber.BitString)`. That function gives us the raw
703 * contents of the BitString tag, which is a count of unused bits followed by
704 * the bits as a right-padded byte string.
705 *
706 * `bits` is the Buffer, `bitIndex` should contain an array of string names
707 * for the bits in the string, ordered starting with bit #0 in the ASN.1 spec.
708 *
709 * Returns an array of Strings, the names of the bits that were set to 1.
710 */
711function readBitField(bits, bitIndex) {
712 var bitLen = 8 * (bits.length - 1) - bits[0];
713 var setBits = {};
714 for (var i = 0; i < bitLen; ++i) {
715 var byteN = 1 + Math.floor(i / 8);
716 var bit = 7 - (i % 8);
717 var mask = 1 << bit;
718 var bitVal = ((bits[byteN] & mask) !== 0);
719 var name = bitIndex[i];
720 if (bitVal && typeof (name) === 'string') {
721 setBits[name] = true;
722 }
723 }
724 return (Object.keys(setBits));
725}
726
727/*
728 * `setBits` is an array of strings, containing the names for each bit that
729 * sould be set to 1. `bitIndex` is same as in `readBitField()`.
730 *
731 * Returns a Buffer, ready to be written out with `BerWriter#writeString()`.
732 */
733function writeBitField(setBits, bitIndex) {
734 var bitLen = bitIndex.length;
735 var blen = Math.ceil(bitLen / 8);
736 var unused = blen * 8 - bitLen;
737 var bits = Buffer.alloc(1 + blen); // zero-filled
738 bits[0] = unused;
739 for (var i = 0; i < bitLen; ++i) {
740 var byteN = 1 + Math.floor(i / 8);
741 var bit = 7 - (i % 8);
742 var mask = 1 << bit;
743 var name = bitIndex[i];
744 if (name === undefined)
745 continue;
746 var bitVal = (setBits.indexOf(name) !== -1);
747 if (bitVal) {
748 bits[byteN] |= mask;
749 }
750 }
751 return (bits);
752}
Note: See TracBrowser for help on using the repository browser.