"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/sshpk/lib/formats/x509.js" (8 Mar 2017, 18326 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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 }