tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

X509.sys.mjs (18327B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { DER } from "resource://gre/modules/psm/DER.sys.mjs";
      6 
      7 const ERROR_UNSUPPORTED_ASN1 = "unsupported asn.1";
      8 const ERROR_TIME_NOT_VALID = "Time not valid";
      9 const ERROR_LIBRARY_FAILURE = "library failure";
     10 
     11 const X509v3 = 2;
     12 
     13 /**
     14 * Helper function to read a NULL tag from the given DER.
     15 *
     16 * @param {DER} der a DER object to read a NULL from
     17 * @returns {null} an object representing an ASN.1 NULL
     18 */
     19 function readNULL(der) {
     20  return new NULL(der.readTagAndGetContents(DER.NULL));
     21 }
     22 
     23 /**
     24 * Class representing an ASN.1 NULL. When encoded as DER, the only valid value
     25 * is 05 00, and thus the contents should always be an empty array.
     26 */
     27 class NULL {
     28  /**
     29   * @param {number[]} bytes the contents of the NULL tag (should be empty)
     30   */
     31  constructor(bytes) {
     32    // Lint TODO: bytes should be an empty array
     33    this._contents = bytes;
     34  }
     35 }
     36 
     37 /**
     38 * Helper function to read an OBJECT IDENTIFIER from the given DER.
     39 *
     40 * @param {DER} der the DER to read an OBJECT IDENTIFIER from
     41 * @returns {OID} the value of the OBJECT IDENTIFIER
     42 */
     43 function readOID(der) {
     44  return new OID(der.readTagAndGetContents(DER.OBJECT_IDENTIFIER));
     45 }
     46 
     47 /** Class representing an ASN.1 OBJECT IDENTIFIER */
     48 class OID {
     49  /**
     50   * @param {number[]} bytes the encoded contents of the OBJECT IDENTIFIER
     51   *                   (not including the ASN.1 tag or length bytes)
     52   */
     53  constructor(bytes) {
     54    this._values = [];
     55    // First octet has value 40 * value1 + value2
     56    // Lint TODO: validate that value1 is one of {0, 1, 2}
     57    // Lint TODO: validate that value2 is in [0, 39] if value1 is 0 or 1
     58    let value1 = Math.floor(bytes[0] / 40);
     59    let value2 = bytes[0] - 40 * value1;
     60    this._values.push(value1);
     61    this._values.push(value2);
     62    bytes.shift();
     63    let accumulator = 0;
     64    // Lint TODO: prevent overflow here
     65    while (bytes.length) {
     66      let value = bytes.shift();
     67      accumulator *= 128;
     68      if (value > 128) {
     69        accumulator += value - 128;
     70      } else {
     71        accumulator += value;
     72        this._values.push(accumulator);
     73        accumulator = 0;
     74      }
     75    }
     76  }
     77 }
     78 
     79 /**
     80 * Class that serves as an abstract base class for more specific classes that
     81 * represent datatypes from RFC 5280 and others. Given an array of bytes
     82 * representing the DER encoding of such types, this framework simplifies the
     83 * process of making a new DER object, attempting to parse the given bytes, and
     84 * catching and stashing thrown exceptions. Subclasses are to implement
     85 * parseOverride, which should read from this._der to fill out the structure's
     86 * values.
     87 */
     88 class DecodedDER {
     89  constructor() {
     90    this._der = null;
     91    this._error = null;
     92  }
     93 
     94  /**
     95   * Returns the first exception encountered when decoding or null if none has
     96   * been encountered.
     97   *
     98   * @returns {Error} the first exception encountered when decoding or null
     99   */
    100  get error() {
    101    return this._error;
    102  }
    103 
    104  /**
    105   * Does the actual work of parsing the data. To be overridden by subclasses.
    106   * If an implementation of parseOverride throws an exception, parse will catch
    107   * that exception and stash it in the error property. This enables parent
    108   * levels in a nested decoding hierarchy to continue to decode as much as
    109   * possible.
    110   */
    111  parseOverride() {
    112    throw new Error(ERROR_LIBRARY_FAILURE);
    113  }
    114 
    115  /**
    116   * Public interface to be called to parse all data. Calls parseOverride inside
    117   * a try/catch block. If an exception is thrown, stashes the error, which can
    118   * be obtained via the error getter (above).
    119   *
    120   * @param {number[]} bytes encoded DER to be decoded
    121   */
    122  parse(bytes) {
    123    this._der = new DER.DERDecoder(bytes);
    124    try {
    125      this.parseOverride();
    126    } catch (e) {
    127      this._error = e;
    128    }
    129  }
    130 }
    131 
    132 /**
    133 * Helper function for reading the next SEQUENCE out of a DER and creating a new
    134 * DER out of the resulting bytes.
    135 *
    136 * @param {DER} der the underlying DER object
    137 * @returns {DER} the contents of the SEQUENCE
    138 */
    139 function readSEQUENCEAndMakeDER(der) {
    140  return new DER.DERDecoder(der.readTagAndGetContents(DER.SEQUENCE));
    141 }
    142 
    143 /**
    144 * Helper function for reading the next item identified by tag out of a DER and
    145 * creating a new DER out of the resulting bytes.
    146 *
    147 * @param {DER} der the underlying DER object
    148 * @param {number} tag the expected next tag in the DER
    149 * @returns {DER} the contents of the tag
    150 */
    151 function readTagAndMakeDER(der, tag) {
    152  return new DER.DERDecoder(der.readTagAndGetContents(tag));
    153 }
    154 
    155 // Certificate  ::=  SEQUENCE  {
    156 //      tbsCertificate       TBSCertificate,
    157 //      signatureAlgorithm   AlgorithmIdentifier,
    158 //      signatureValue       BIT STRING  }
    159 class Certificate extends DecodedDER {
    160  constructor() {
    161    super();
    162    this._tbsCertificate = new TBSCertificate();
    163    this._signatureAlgorithm = new AlgorithmIdentifier();
    164    this._signatureValue = [];
    165  }
    166 
    167  get tbsCertificate() {
    168    return this._tbsCertificate;
    169  }
    170 
    171  get signatureAlgorithm() {
    172    return this._signatureAlgorithm;
    173  }
    174 
    175  get signatureValue() {
    176    return this._signatureValue;
    177  }
    178 
    179  parseOverride() {
    180    let contents = readSEQUENCEAndMakeDER(this._der);
    181    this._tbsCertificate.parse(contents.readTLV());
    182    this._signatureAlgorithm.parse(contents.readTLV());
    183 
    184    let signatureValue = contents.readBIT_STRING();
    185    if (signatureValue.unusedBits != 0) {
    186      throw new Error(ERROR_UNSUPPORTED_ASN1);
    187    }
    188    this._signatureValue = signatureValue.contents;
    189    contents.assertAtEnd();
    190    this._der.assertAtEnd();
    191  }
    192 }
    193 
    194 // TBSCertificate  ::=  SEQUENCE  {
    195 //      version         [0]  EXPLICIT Version DEFAULT v1,
    196 //      serialNumber         CertificateSerialNumber,
    197 //      signature            AlgorithmIdentifier,
    198 //      issuer               Name,
    199 //      validity             Validity,
    200 //      subject              Name,
    201 //      subjectPublicKeyInfo SubjectPublicKeyInfo,
    202 //      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
    203 //                           -- If present, version MUST be v2 or v3
    204 //      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
    205 //                           -- If present, version MUST be v2 or v3
    206 //      extensions      [3]  EXPLICIT Extensions OPTIONAL
    207 //                           -- If present, version MUST be v3
    208 //      }
    209 class TBSCertificate extends DecodedDER {
    210  constructor() {
    211    super();
    212    this._version = null;
    213    this._serialNumber = [];
    214    this._signature = new AlgorithmIdentifier();
    215    this._issuer = new Name();
    216    this._validity = new Validity();
    217    this._subject = new Name();
    218    this._subjectPublicKeyInfo = new SubjectPublicKeyInfo();
    219    this._extensions = [];
    220  }
    221 
    222  get version() {
    223    return this._version;
    224  }
    225 
    226  get serialNumber() {
    227    return this._serialNumber;
    228  }
    229 
    230  get signature() {
    231    return this._signature;
    232  }
    233 
    234  get issuer() {
    235    return this._issuer;
    236  }
    237 
    238  get validity() {
    239    return this._validity;
    240  }
    241 
    242  get subject() {
    243    return this._subject;
    244  }
    245 
    246  get subjectPublicKeyInfo() {
    247    return this._subjectPublicKeyInfo;
    248  }
    249 
    250  get extensions() {
    251    return this._extensions;
    252  }
    253 
    254  parseOverride() {
    255    let contents = readSEQUENCEAndMakeDER(this._der);
    256 
    257    let versionTag = DER.CONTEXT_SPECIFIC | DER.CONSTRUCTED | 0;
    258    if (!contents.peekTag(versionTag)) {
    259      this._version = 1;
    260    } else {
    261      let versionContents = readTagAndMakeDER(contents, versionTag);
    262      let versionBytes = versionContents.readTagAndGetContents(DER.INTEGER);
    263      if (versionBytes.length == 1 && versionBytes[0] == X509v3) {
    264        this._version = 3;
    265      } else {
    266        // Lint TODO: warn about non-v3 certificates (this INTEGER could take up
    267        // multiple bytes, be negative, and so on).
    268        this._version = versionBytes;
    269      }
    270      versionContents.assertAtEnd();
    271    }
    272 
    273    let serialNumberBytes = contents.readTagAndGetContents(DER.INTEGER);
    274    this._serialNumber = serialNumberBytes;
    275    this._signature.parse(contents.readTLV());
    276    this._issuer.parse(contents.readTLV());
    277    this._validity.parse(contents.readTLV());
    278    this._subject.parse(contents.readTLV());
    279    this._subjectPublicKeyInfo.parse(contents.readTLV());
    280 
    281    // Lint TODO: warn about unsupported features
    282    let issuerUniqueIDTag = DER.CONTEXT_SPECIFIC | DER.CONSTRUCTED | 1;
    283    if (contents.peekTag(issuerUniqueIDTag)) {
    284      contents.readTagAndGetContents(issuerUniqueIDTag);
    285    }
    286    let subjectUniqueIDTag = DER.CONTEXT_SPECIFIC | DER.CONSTRUCTED | 2;
    287    if (contents.peekTag(subjectUniqueIDTag)) {
    288      contents.readTagAndGetContents(subjectUniqueIDTag);
    289    }
    290 
    291    let extensionsTag = DER.CONTEXT_SPECIFIC | DER.CONSTRUCTED | 3;
    292    if (contents.peekTag(extensionsTag)) {
    293      let extensionsSequence = readTagAndMakeDER(contents, extensionsTag);
    294      let extensionsContents = readSEQUENCEAndMakeDER(extensionsSequence);
    295      while (!extensionsContents.atEnd()) {
    296        // TODO: parse extensions
    297        this._extensions.push(extensionsContents.readTLV());
    298      }
    299      extensionsContents.assertAtEnd();
    300      extensionsSequence.assertAtEnd();
    301    }
    302    contents.assertAtEnd();
    303    this._der.assertAtEnd();
    304  }
    305 }
    306 
    307 // AlgorithmIdentifier  ::=  SEQUENCE  {
    308 //      algorithm               OBJECT IDENTIFIER,
    309 //      parameters              ANY DEFINED BY algorithm OPTIONAL  }
    310 class AlgorithmIdentifier extends DecodedDER {
    311  constructor() {
    312    super();
    313    this._algorithm = null;
    314    this._parameters = null;
    315  }
    316 
    317  get algorithm() {
    318    return this._algorithm;
    319  }
    320 
    321  get parameters() {
    322    return this._parameters;
    323  }
    324 
    325  parseOverride() {
    326    let contents = readSEQUENCEAndMakeDER(this._der);
    327    this._algorithm = readOID(contents);
    328    if (!contents.atEnd()) {
    329      if (contents.peekTag(DER.NULL)) {
    330        this._parameters = readNULL(contents);
    331      } else if (contents.peekTag(DER.OBJECT_IDENTIFIER)) {
    332        this._parameters = readOID(contents);
    333      }
    334    }
    335    contents.assertAtEnd();
    336    this._der.assertAtEnd();
    337  }
    338 }
    339 
    340 // Name ::= CHOICE { -- only one possibility for now --
    341 //   rdnSequence  RDNSequence }
    342 //
    343 // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
    344 class Name extends DecodedDER {
    345  constructor() {
    346    super();
    347    this._rdns = [];
    348  }
    349 
    350  get rdns() {
    351    return this._rdns;
    352  }
    353 
    354  parseOverride() {
    355    let contents = readSEQUENCEAndMakeDER(this._der);
    356    while (!contents.atEnd()) {
    357      let rdn = new RelativeDistinguishedName();
    358      rdn.parse(contents.readTLV());
    359      this._rdns.push(rdn);
    360    }
    361    contents.assertAtEnd();
    362    this._der.assertAtEnd();
    363  }
    364 }
    365 
    366 // RelativeDistinguishedName ::=
    367 //   SET SIZE (1..MAX) OF AttributeTypeAndValue
    368 class RelativeDistinguishedName extends DecodedDER {
    369  constructor() {
    370    super();
    371    this._avas = [];
    372  }
    373 
    374  get avas() {
    375    return this._avas;
    376  }
    377 
    378  parseOverride() {
    379    let contents = readTagAndMakeDER(this._der, DER.SET);
    380    // Lint TODO: enforce SET SIZE restrictions
    381    while (!contents.atEnd()) {
    382      let ava = new AttributeTypeAndValue();
    383      ava.parse(contents.readTLV());
    384      this._avas.push(ava);
    385    }
    386    contents.assertAtEnd();
    387    this._der.assertAtEnd();
    388  }
    389 }
    390 
    391 // AttributeTypeAndValue ::= SEQUENCE {
    392 //   type     AttributeType,
    393 //   value    AttributeValue }
    394 //
    395 // AttributeType ::= OBJECT IDENTIFIER
    396 //
    397 // AttributeValue ::= ANY -- DEFINED BY AttributeType
    398 class AttributeTypeAndValue extends DecodedDER {
    399  constructor() {
    400    super();
    401    this._type = null;
    402    this._value = new DirectoryString();
    403  }
    404 
    405  get type() {
    406    return this._type;
    407  }
    408 
    409  get value() {
    410    return this._value;
    411  }
    412 
    413  parseOverride() {
    414    let contents = readSEQUENCEAndMakeDER(this._der);
    415    this._type = readOID(contents);
    416    // We don't support universalString or bmpString.
    417    // IA5String is supported because it is valid if `type == id-emailaddress`.
    418    // Lint TODO: validate that the type of string is valid given `type`.
    419    this._value.parse(
    420      contents.readTLVChoice([
    421        DER.UTF8String,
    422        DER.PrintableString,
    423        DER.TeletexString,
    424        DER.IA5String,
    425      ])
    426    );
    427    contents.assertAtEnd();
    428    this._der.assertAtEnd();
    429  }
    430 }
    431 
    432 // DirectoryString ::= CHOICE {
    433 //       teletexString           TeletexString (SIZE (1..MAX)),
    434 //       printableString         PrintableString (SIZE (1..MAX)),
    435 //       universalString         UniversalString (SIZE (1..MAX)),
    436 //       utf8String              UTF8String (SIZE (1..MAX)),
    437 //       bmpString               BMPString (SIZE (1..MAX)) }
    438 class DirectoryString extends DecodedDER {
    439  constructor() {
    440    super();
    441    this._type = null;
    442    this._value = null;
    443  }
    444 
    445  get type() {
    446    return this._type;
    447  }
    448 
    449  get value() {
    450    return this._value;
    451  }
    452 
    453  parseOverride() {
    454    if (this._der.peekTag(DER.UTF8String)) {
    455      this._type = DER.UTF8String;
    456    } else if (this._der.peekTag(DER.PrintableString)) {
    457      this._type = DER.PrintableString;
    458    } else if (this._der.peekTag(DER.TeletexString)) {
    459      this._type = DER.TeletexString;
    460    } else if (this._der.peekTag(DER.IA5String)) {
    461      this._type = DER.IA5String;
    462    }
    463    // Lint TODO: validate that the contents are actually valid for the type
    464    this._value = this._der.readTagAndGetContents(this._type);
    465    this._der.assertAtEnd();
    466  }
    467 }
    468 
    469 // Time ::= CHOICE {
    470 //      utcTime        UTCTime,
    471 //      generalTime    GeneralizedTime }
    472 class Time extends DecodedDER {
    473  constructor() {
    474    super();
    475    this._type = null;
    476    this._time = null;
    477  }
    478 
    479  get time() {
    480    return this._time;
    481  }
    482 
    483  parseOverride() {
    484    if (this._der.peekTag(DER.UTCTime)) {
    485      this._type = DER.UTCTime;
    486    } else if (this._der.peekTag(DER.GeneralizedTime)) {
    487      this._type = DER.GeneralizedTime;
    488    }
    489    let contents = readTagAndMakeDER(this._der, this._type);
    490    let year;
    491    // Lint TODO: validate that the appropriate one of {UTCTime,GeneralizedTime}
    492    // is used according to RFC 5280 and what the value of the date is.
    493    // TODO TODO: explain this better (just quote the rfc).
    494    if (this._type == DER.UTCTime) {
    495      // UTCTime is YYMMDDHHMMSSZ in RFC 5280. If YY is greater than or equal
    496      // to 50, the year is 19YY. Otherwise, it is 20YY.
    497      let y1 = this._validateDigit(contents.readByte());
    498      let y2 = this._validateDigit(contents.readByte());
    499      let yy = y1 * 10 + y2;
    500      if (yy >= 50) {
    501        year = 1900 + yy;
    502      } else {
    503        year = 2000 + yy;
    504      }
    505    } else {
    506      // GeneralizedTime is YYYYMMDDHHMMSSZ in RFC 5280.
    507      year = 0;
    508      for (let i = 0; i < 4; i++) {
    509        let y = this._validateDigit(contents.readByte());
    510        year = year * 10 + y;
    511      }
    512    }
    513 
    514    let m1 = this._validateDigit(contents.readByte());
    515    let m2 = this._validateDigit(contents.readByte());
    516    let month = m1 * 10 + m2;
    517    if (month == 0 || month > 12) {
    518      throw new Error(ERROR_TIME_NOT_VALID);
    519    }
    520 
    521    let d1 = this._validateDigit(contents.readByte());
    522    let d2 = this._validateDigit(contents.readByte());
    523    let day = d1 * 10 + d2;
    524    if (day == 0 || day > 31) {
    525      throw new Error(ERROR_TIME_NOT_VALID);
    526    }
    527 
    528    let h1 = this._validateDigit(contents.readByte());
    529    let h2 = this._validateDigit(contents.readByte());
    530    let hour = h1 * 10 + h2;
    531    if (hour > 23) {
    532      throw new Error(ERROR_TIME_NOT_VALID);
    533    }
    534 
    535    let min1 = this._validateDigit(contents.readByte());
    536    let min2 = this._validateDigit(contents.readByte());
    537    let minute = min1 * 10 + min2;
    538    if (minute > 59) {
    539      throw new Error(ERROR_TIME_NOT_VALID);
    540    }
    541 
    542    let s1 = this._validateDigit(contents.readByte());
    543    let s2 = this._validateDigit(contents.readByte());
    544    let second = s1 * 10 + s2;
    545    if (second > 60) {
    546      // leap-seconds mean this can be as much as 60
    547      throw new Error(ERROR_TIME_NOT_VALID);
    548    }
    549 
    550    let z = contents.readByte();
    551    if (z != "Z".charCodeAt(0)) {
    552      throw new Error(ERROR_TIME_NOT_VALID);
    553    }
    554    // Lint TODO: verify that the Time doesn't specify a nonsensical
    555    // month/day/etc.
    556    // months are zero-indexed in JS
    557    this._time = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
    558 
    559    contents.assertAtEnd();
    560    this._der.assertAtEnd();
    561  }
    562 
    563  /**
    564   * Takes a byte that is supposed to be in the ASCII range for "0" to "9".
    565   * Validates the range and then converts it to the range 0 to 9.
    566   *
    567   * @param {number} d the digit in question (as ASCII in the range ["0", "9"])
    568   * @returns {number} the numerical value of the digit (in the range [0, 9])
    569   */
    570  _validateDigit(d) {
    571    if (d < "0".charCodeAt(0) || d > "9".charCodeAt(0)) {
    572      throw new Error(ERROR_TIME_NOT_VALID);
    573    }
    574    return d - "0".charCodeAt(0);
    575  }
    576 }
    577 
    578 // Validity ::= SEQUENCE {
    579 //      notBefore      Time,
    580 //      notAfter       Time }
    581 class Validity extends DecodedDER {
    582  constructor() {
    583    super();
    584    this._notBefore = new Time();
    585    this._notAfter = new Time();
    586  }
    587 
    588  get notBefore() {
    589    return this._notBefore;
    590  }
    591 
    592  get notAfter() {
    593    return this._notAfter;
    594  }
    595 
    596  parseOverride() {
    597    let contents = readSEQUENCEAndMakeDER(this._der);
    598    this._notBefore.parse(
    599      contents.readTLVChoice([DER.UTCTime, DER.GeneralizedTime])
    600    );
    601    this._notAfter.parse(
    602      contents.readTLVChoice([DER.UTCTime, DER.GeneralizedTime])
    603    );
    604    contents.assertAtEnd();
    605    this._der.assertAtEnd();
    606  }
    607 }
    608 
    609 // SubjectPublicKeyInfo  ::=  SEQUENCE  {
    610 //      algorithm            AlgorithmIdentifier,
    611 //      subjectPublicKey     BIT STRING  }
    612 class SubjectPublicKeyInfo extends DecodedDER {
    613  constructor() {
    614    super();
    615    this._algorithm = new AlgorithmIdentifier();
    616    this._subjectPublicKey = null;
    617  }
    618 
    619  get algorithm() {
    620    return this._algorithm;
    621  }
    622 
    623  get subjectPublicKey() {
    624    return this._subjectPublicKey;
    625  }
    626 
    627  parseOverride() {
    628    let contents = readSEQUENCEAndMakeDER(this._der);
    629    this._algorithm.parse(contents.readTLV());
    630    let subjectPublicKeyBitString = contents.readBIT_STRING();
    631    if (subjectPublicKeyBitString.unusedBits != 0) {
    632      throw new Error(ERROR_UNSUPPORTED_ASN1);
    633    }
    634    this._subjectPublicKey = subjectPublicKeyBitString.contents;
    635 
    636    contents.assertAtEnd();
    637    this._der.assertAtEnd();
    638  }
    639 }
    640 
    641 export var X509 = { Certificate };