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