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 206/* RFC5280, section 4.2.1.6 (GeneralName type) */ 207var ALTNAME = { 208 OtherName: Local(0), 209 RFC822Name: Context(1), 210 DNSName: Context(2), 211 X400Address: Local(3), 212 DirectoryName: Local(4), 213 EDIPartyName: Local(5), 214 URI: Context(6), 215 IPAddress: Context(7), 216 OID: Context(8) 217}; 218 219/* RFC5280, section 4.2.1.12 (KeyPurposeId) */ 220var EXTPURPOSE = { 221 'serverAuth': '1.3.6.1.5.5.7.3.1', 222 'clientAuth': '1.3.6.1.5.5.7.3.2', 223 'codeSigning': '1.3.6.1.5.5.7.3.3', 224 225 /* See https://github.com/joyent/oid-docs/blob/master/root.md */ 226 'joyentDocker': '1.3.6.1.4.1.38678.1.4.1', 227 'joyentCmon': '1.3.6.1.4.1.38678.1.4.2' 228}; 229var EXTPURPOSE_REV = {}; 230Object.keys(EXTPURPOSE).forEach(function (k) { 231 EXTPURPOSE_REV[EXTPURPOSE[k]] = k; 232}); 233 234var KEYUSEBITS = [ 235 'signature', 'identity', 'keyEncryption', 236 'encryption', 'keyAgreement', 'ca', 'crl' 237]; 238 239function readExtension(cert, buf, der) { 240 der.readSequence(); 241 var after = der.offset + der.length; 242 var extId = der.readOID(); 243 var id; 244 var sig = cert.signatures.x509; 245 sig.extras.exts = []; 246 247 var critical; 248 if (der.peek() === asn1.Ber.Boolean) 249 critical = der.readBoolean(); 250 251 switch (extId) { 252 case (EXTS.basicConstraints): 253 der.readSequence(asn1.Ber.OctetString); 254 der.readSequence(); 255 var bcEnd = der.offset + der.length; 256 var ca = false; 257 if (der.peek() === asn1.Ber.Boolean) 258 ca = der.readBoolean(); 259 if (cert.purposes === undefined) 260 cert.purposes = []; 261 if (ca === true) 262 cert.purposes.push('ca'); 263 var bc = { oid: extId, critical: critical }; 264 if (der.offset < bcEnd && der.peek() === asn1.Ber.Integer) 265 bc.pathLen = der.readInt(); 266 sig.extras.exts.push(bc); 267 break; 268 case (EXTS.extKeyUsage): 269 der.readSequence(asn1.Ber.OctetString); 270 der.readSequence(); 271 if (cert.purposes === undefined) 272 cert.purposes = []; 273 var ekEnd = der.offset + der.length; 274 while (der.offset < ekEnd) { 275 var oid = der.readOID(); 276 cert.purposes.push(EXTPURPOSE_REV[oid] || oid); 277 } 278 /* 279 * This is a bit of a hack: in the case where we have a cert 280 * that's only allowed to do serverAuth or clientAuth (and not 281 * the other), we want to make sure all our Subjects are of 282 * the right type. But we already parsed our Subjects and 283 * decided if they were hosts or users earlier (since it appears 284 * first in the cert). 285 * 286 * So we go through and mutate them into the right kind here if 287 * it doesn't match. This might not be hugely beneficial, as it 288 * seems that single-purpose certs are not often seen in the 289 * wild. 290 */ 291 if (cert.purposes.indexOf('serverAuth') !== -1 && 292 cert.purposes.indexOf('clientAuth') === -1) { 293 cert.subjects.forEach(function (ide) { 294 if (ide.type !== 'host') { 295 ide.type = 'host'; 296 ide.hostname = ide.uid || 297 ide.email || 298 ide.components[0].value; 299 } 300 }); 301 } else if (cert.purposes.indexOf('clientAuth') !== -1 && 302 cert.purposes.indexOf('serverAuth') === -1) { 303 cert.subjects.forEach(function (ide) { 304 if (ide.type !== 'user') { 305 ide.type = 'user'; 306 ide.uid = ide.hostname || 307 ide.email || 308 ide.components[0].value; 309 } 310 }); 311 } 312 sig.extras.exts.push({ oid: extId, critical: critical }); 313 break; 314 case (EXTS.keyUsage): 315 der.readSequence(asn1.Ber.OctetString); 316 var bits = der.readString(asn1.Ber.BitString, true); 317 var setBits = readBitField(bits, KEYUSEBITS); 318 setBits.forEach(function (bit) { 319 if (cert.purposes === undefined) 320 cert.purposes = []; 321 if (cert.purposes.indexOf(bit) === -1) 322 cert.purposes.push(bit); 323 }); 324 sig.extras.exts.push({ oid: extId, critical: critical, 325 bits: bits }); 326 break; 327 case (EXTS.altName): 328 der.readSequence(asn1.Ber.OctetString); 329 der.readSequence(); 330 var aeEnd = der.offset + der.length; 331 while (der.offset < aeEnd) { 332 switch (der.peek()) { 333 case ALTNAME.OtherName: 334 case ALTNAME.EDIPartyName: 335 der.readSequence(); 336 der._offset += der.length; 337 break; 338 case ALTNAME.OID: 339 der.readOID(ALTNAME.OID); 340 break; 341 case ALTNAME.RFC822Name: 342 /* RFC822 specifies email addresses */ 343 var email = der.readString(ALTNAME.RFC822Name); 344 id = Identity.forEmail(email); 345 if (!cert.subjects[0].equals(id)) 346 cert.subjects.push(id); 347 break; 348 case ALTNAME.DirectoryName: 349 der.readSequence(ALTNAME.DirectoryName); 350 id = Identity.parseAsn1(der); 351 if (!cert.subjects[0].equals(id)) 352 cert.subjects.push(id); 353 break; 354 case ALTNAME.DNSName: 355 var host = der.readString( 356 ALTNAME.DNSName); 357 id = Identity.forHost(host); 358 if (!cert.subjects[0].equals(id)) 359 cert.subjects.push(id); 360 break; 361 default: 362 der.readString(der.peek()); 363 break; 364 } 365 } 366 sig.extras.exts.push({ oid: extId, critical: critical }); 367 break; 368 default: 369 sig.extras.exts.push({ 370 oid: extId, 371 critical: critical, 372 data: der.readString(asn1.Ber.OctetString, true) 373 }); 374 break; 375 } 376 377 der._offset = after; 378} 379 380var UTCTIME_RE = 381 /^([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/; 382function utcTimeToDate(t) { 383 var m = t.match(UTCTIME_RE); 384 assert.ok(m, 'timestamps must be in UTC'); 385 var d = new Date(); 386 387 var thisYear = d.getUTCFullYear(); 388 var century = Math.floor(thisYear / 100) * 100; 389 390 var year = parseInt(m[1], 10); 391 if (thisYear % 100 < 50 && year >= 60) 392 year += (century - 1); 393 else 394 year += century; 395 d.setUTCFullYear(year, parseInt(m[2], 10) - 1, parseInt(m[3], 10)); 396 d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10)); 397 if (m[6] && m[6].length > 0) 398 d.setUTCSeconds(parseInt(m[6], 10)); 399 return (d); 400} 401 402var GTIME_RE = 403 /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/; 404function gTimeToDate(t) { 405 var m = t.match(GTIME_RE); 406 assert.ok(m); 407 var d = new Date(); 408 409 d.setUTCFullYear(parseInt(m[1], 10), parseInt(m[2], 10) - 1, 410 parseInt(m[3], 10)); 411 d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10)); 412 if (m[6] && m[6].length > 0) 413 d.setUTCSeconds(parseInt(m[6], 10)); 414 return (d); 415} 416 417function zeroPad(n) { 418 var s = '' + n; 419 while (s.length < 2) 420 s = '0' + s; 421 return (s); 422} 423 424function dateToUTCTime(d) { 425 var s = ''; 426 s += zeroPad(d.getUTCFullYear() % 100); 427 s += zeroPad(d.getUTCMonth() + 1); 428 s += zeroPad(d.getUTCDate()); 429 s += zeroPad(d.getUTCHours()); 430 s += zeroPad(d.getUTCMinutes()); 431 s += zeroPad(d.getUTCSeconds()); 432 s += 'Z'; 433 return (s); 434} 435 436function sign(cert, key) { 437 if (cert.signatures.x509 === undefined) 438 cert.signatures.x509 = {}; 439 var sig = cert.signatures.x509; 440 441 sig.algo = key.type + '-' + key.defaultHashAlgorithm(); 442 if (SIGN_ALGS[sig.algo] === undefined) 443 return (false); 444 445 var der = new asn1.BerWriter(); 446 writeTBSCert(cert, der); 447 var blob = der.buffer; 448 sig.cache = blob; 449 450 var signer = key.createSign(); 451 signer.write(blob); 452 cert.signatures.x509.signature = signer.sign(); 453 454 return (true); 455} 456 457function signAsync(cert, signer, done) { 458 if (cert.signatures.x509 === undefined) 459 cert.signatures.x509 = {}; 460 var sig = cert.signatures.x509; 461 462 var der = new asn1.BerWriter(); 463 writeTBSCert(cert, der); 464 var blob = der.buffer; 465 sig.cache = blob; 466 467 signer(blob, function (err, signature) { 468 if (err) { 469 done(err); 470 return; 471 } 472 sig.algo = signature.type + '-' + signature.hashAlgorithm; 473 if (SIGN_ALGS[sig.algo] === undefined) { 474 done(new Error('Invalid signing algorithm "' + 475 sig.algo + '"')); 476 return; 477 } 478 sig.signature = signature; 479 done(); 480 }); 481} 482 483function write(cert, options) { 484 var sig = cert.signatures.x509; 485 assert.object(sig, 'x509 signature'); 486 487 var der = new asn1.BerWriter(); 488 der.startSequence(); 489 if (sig.cache) { 490 der._ensure(sig.cache.length); 491 sig.cache.copy(der._buf, der._offset); 492 der._offset += sig.cache.length; 493 } else { 494 writeTBSCert(cert, der); 495 } 496 497 der.startSequence(); 498 der.writeOID(SIGN_ALGS[sig.algo]); 499 if (sig.algo.match(/^rsa-/)) 500 der.writeNull(); 501 der.endSequence(); 502 503 var sigData = sig.signature.toBuffer('asn1'); 504 var data = Buffer.alloc(sigData.length + 1); 505 data[0] = 0; 506 sigData.copy(data, 1); 507 der.writeBuffer(data, asn1.Ber.BitString); 508 der.endSequence(); 509 510 return (der.buffer); 511} 512 513function writeTBSCert(cert, der) { 514 var sig = cert.signatures.x509; 515 assert.object(sig, 'x509 signature'); 516 517 der.startSequence(); 518 519 der.startSequence(Local(0)); 520 der.writeInt(2); 521 der.endSequence(); 522 523 der.writeBuffer(utils.mpNormalize(cert.serial), asn1.Ber.Integer); 524 525 der.startSequence(); 526 der.writeOID(SIGN_ALGS[sig.algo]); 527 if (sig.algo.match(/^rsa-/)) 528 der.writeNull(); 529 der.endSequence(); 530 531 cert.issuer.toAsn1(der); 532 533 der.startSequence(); 534 der.writeString(dateToUTCTime(cert.validFrom), asn1.Ber.UTCTime); 535 der.writeString(dateToUTCTime(cert.validUntil), asn1.Ber.UTCTime); 536 der.endSequence(); 537 538 var subject = cert.subjects[0]; 539 var altNames = cert.subjects.slice(1); 540 subject.toAsn1(der); 541 542 pkcs8.writePkcs8(der, cert.subjectKey); 543 544 if (sig.extras && sig.extras.issuerUniqueID) { 545 der.writeBuffer(sig.extras.issuerUniqueID, Local(1)); 546 } 547 548 if (sig.extras && sig.extras.subjectUniqueID) { 549 der.writeBuffer(sig.extras.subjectUniqueID, Local(2)); 550 } 551 552 if (altNames.length > 0 || subject.type === 'host' || 553 (cert.purposes !== undefined && cert.purposes.length > 0) || 554 (sig.extras && sig.extras.exts)) { 555 der.startSequence(Local(3)); 556 der.startSequence(); 557 558 var exts = []; 559 if (cert.purposes !== undefined && cert.purposes.length > 0) { 560 exts.push({ 561 oid: EXTS.basicConstraints, 562 critical: true 563 }); 564 exts.push({ 565 oid: EXTS.keyUsage, 566 critical: true 567 }); 568 exts.push({ 569 oid: EXTS.extKeyUsage, 570 critical: true 571 }); 572 } 573 exts.push({ oid: EXTS.altName }); 574 if (sig.extras && sig.extras.exts) 575 exts = sig.extras.exts; 576 577 for (var i = 0; i < exts.length; ++i) { 578 der.startSequence(); 579 der.writeOID(exts[i].oid); 580 581 if (exts[i].critical !== undefined) 582 der.writeBoolean(exts[i].critical); 583 584 if (exts[i].oid === EXTS.altName) { 585 der.startSequence(asn1.Ber.OctetString); 586 der.startSequence(); 587 if (subject.type === 'host') { 588 der.writeString(subject.hostname, 589 Context(2)); 590 } 591 for (var j = 0; j < altNames.length; ++j) { 592 if (altNames[j].type === 'host') { 593 der.writeString( 594 altNames[j].hostname, 595 ALTNAME.DNSName); 596 } else if (altNames[j].type === 597 'email') { 598 der.writeString( 599 altNames[j].email, 600 ALTNAME.RFC822Name); 601 } else { 602 /* 603 * Encode anything else as a 604 * DN style name for now. 605 */ 606 der.startSequence( 607 ALTNAME.DirectoryName); 608 altNames[j].toAsn1(der); 609 der.endSequence(); 610 } 611 } 612 der.endSequence(); 613 der.endSequence(); 614 } else if (exts[i].oid === EXTS.basicConstraints) { 615 der.startSequence(asn1.Ber.OctetString); 616 der.startSequence(); 617 var ca = (cert.purposes.indexOf('ca') !== -1); 618 var pathLen = exts[i].pathLen; 619 der.writeBoolean(ca); 620 if (pathLen !== undefined) 621 der.writeInt(pathLen); 622 der.endSequence(); 623 der.endSequence(); 624 } else if (exts[i].oid === EXTS.extKeyUsage) { 625 der.startSequence(asn1.Ber.OctetString); 626 der.startSequence(); 627 cert.purposes.forEach(function (purpose) { 628 if (purpose === 'ca') 629 return; 630 if (KEYUSEBITS.indexOf(purpose) !== -1) 631 return; 632 var oid = purpose; 633 if (EXTPURPOSE[purpose] !== undefined) 634 oid = EXTPURPOSE[purpose]; 635 der.writeOID(oid); 636 }); 637 der.endSequence(); 638 der.endSequence(); 639 } else if (exts[i].oid === EXTS.keyUsage) { 640 der.startSequence(asn1.Ber.OctetString); 641 /* 642 * If we parsed this certificate from a byte 643 * stream (i.e. we didn't generate it in sshpk) 644 * then we'll have a ".bits" property on the 645 * ext with the original raw byte contents. 646 * 647 * If we have this, use it here instead of 648 * regenerating it. This guarantees we output 649 * the same data we parsed, so signatures still 650 * validate. 651 */ 652 if (exts[i].bits !== undefined) { 653 der.writeBuffer(exts[i].bits, 654 asn1.Ber.BitString); 655 } else { 656 var bits = writeBitField(cert.purposes, 657 KEYUSEBITS); 658 der.writeBuffer(bits, 659 asn1.Ber.BitString); 660 } 661 der.endSequence(); 662 } else { 663 der.writeBuffer(exts[i].data, 664 asn1.Ber.OctetString); 665 } 666 667 der.endSequence(); 668 } 669 670 der.endSequence(); 671 der.endSequence(); 672 } 673 674 der.endSequence(); 675} 676 677/* 678 * Reads an ASN.1 BER bitfield out of the Buffer produced by doing 679 * `BerReader#readString(asn1.Ber.BitString)`. That function gives us the raw 680 * contents of the BitString tag, which is a count of unused bits followed by 681 * the bits as a right-padded byte string. 682 * 683 * `bits` is the Buffer, `bitIndex` should contain an array of string names 684 * for the bits in the string, ordered starting with bit #0 in the ASN.1 spec. 685 * 686 * Returns an array of Strings, the names of the bits that were set to 1. 687 */ 688function readBitField(bits, bitIndex) { 689 var bitLen = 8 * (bits.length - 1) - bits[0]; 690 var setBits = {}; 691 for (var i = 0; i < bitLen; ++i) { 692 var byteN = 1 + Math.floor(i / 8); 693 var bit = 7 - (i % 8); 694 var mask = 1 << bit; 695 var bitVal = ((bits[byteN] & mask) !== 0); 696 var name = bitIndex[i]; 697 if (bitVal && typeof (name) === 'string') { 698 setBits[name] = true; 699 } 700 } 701 return (Object.keys(setBits)); 702} 703 704/* 705 * `setBits` is an array of strings, containing the names for each bit that 706 * sould be set to 1. `bitIndex` is same as in `readBitField()`. 707 * 708 * Returns a Buffer, ready to be written out with `BerWriter#writeString()`. 709 */ 710function writeBitField(setBits, bitIndex) { 711 var bitLen = bitIndex.length; 712 var blen = Math.ceil(bitLen / 8); 713 var unused = blen * 8 - bitLen; 714 var bits = Buffer.alloc(1 + blen); // zero-filled 715 bits[0] = unused; 716 for (var i = 0; i < bitLen; ++i) { 717 var byteN = 1 + Math.floor(i / 8); 718 var bit = 7 - (i % 8); 719 var mask = 1 << bit; 720 var name = bitIndex[i]; 721 if (name === undefined) 722 continue; 723 var bitVal = (setBits.indexOf(name) !== -1); 724 if (bitVal) { 725 bits[byteN] |= mask; 726 } 727 } 728 return (bits); 729} 730