x509.js (19556B)
1 // Copyright 2017 Joyent, Inc. 2 3 module.exports = { 4 read: read, 5 verify: verify, 6 sign: sign, 7 signAsync: signAsync, 8 write: write 9 }; 10 11 var assert = require('assert-plus'); 12 var asn1 = require('asn1'); 13 var Buffer = require('safer-buffer').Buffer; 14 var algs = require('../algs'); 15 var utils = require('../utils'); 16 var Key = require('../key'); 17 var PrivateKey = require('../private-key'); 18 var pem = require('./pem'); 19 var Identity = require('../identity'); 20 var Signature = require('../signature'); 21 var Certificate = require('../certificate'); 22 var pkcs8 = require('./pkcs8'); 23 24 /* 25 * This file is based on RFC5280 (X.509). 26 */ 27 28 /* Helper to read in a single mpint */ 29 function 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 35 function 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 55 function Local(i) { 56 return (asn1.Ber.Context | asn1.Ber.Constructor | i); 57 } 58 59 function Context(i) { 60 return (asn1.Ber.Context | i); 61 } 62 63 var 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 }; 77 Object.keys(SIGN_ALGS).forEach(function (k) { 78 SIGN_ALGS[SIGN_ALGS[k]] = k; 79 }); 80 SIGN_ALGS['1.3.14.3.2.3'] = 'rsa-md5'; 81 SIGN_ALGS['1.3.14.3.2.29'] = 'rsa-sha1'; 82 83 var 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 91 function 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 196 function 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 function 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) */ 215 var 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) */ 228 var 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 }; 237 var EXTPURPOSE_REV = {}; 238 Object.keys(EXTPURPOSE).forEach(function (k) { 239 EXTPURPOSE_REV[EXTPURPOSE[k]] = k; 240 }); 241 242 var KEYUSEBITS = [ 243 'signature', 'identity', 'keyEncryption', 244 'encryption', 'keyAgreement', 'ca', 'crl' 245 ]; 246 247 function 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 389 var UTCTIME_RE = 390 /^([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/; 391 function 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 411 var GTIME_RE = 412 /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/; 413 function 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 426 function 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 435 function 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 447 function 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 459 function 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 480 function 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 506 function 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 536 function 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 */ 711 function 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 */ 733 function 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 }