tor-browser

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

test_der.js (10240B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Tests DER.sys.mjs functionality.
      7 
      8 // Until DER.sys.mjs is actually used in production code, this is where we have to
      9 // import it from.
     10 var { DER } = ChromeUtils.importESModule(
     11  "resource://gre/modules/psm/DER.sys.mjs"
     12 );
     13 
     14 function run_simple_tests() {
     15  throws(
     16    () => new DER.DERDecoder("this is not an array"),
     17    /invalid input/,
     18    "should throw given non-array input"
     19  );
     20 
     21  let testReadByte = new DER.DERDecoder([0x0a, 0x0b]);
     22  equal(testReadByte.readByte(), 0x0a, "should read 0x0a");
     23  equal(testReadByte.readByte(), 0x0b, "should read 0x0b");
     24  throws(
     25    () => testReadByte.readByte(),
     26    /data truncated/,
     27    "reading more data than is available should fail"
     28  );
     29 
     30  let testReadBytes = new DER.DERDecoder([0x0c, 0x0d, 0x0e]);
     31  deepEqual(
     32    testReadBytes.readBytes(3),
     33    [0x0c, 0x0d, 0x0e],
     34    "should read correct sequence of bytes"
     35  );
     36 
     37  let testReadNegativeBytes = new DER.DERDecoder([0xff, 0xaf]);
     38  throws(
     39    () => testReadNegativeBytes.readBytes(-4),
     40    /invalid length/,
     41    "reading a negative number of bytes should fail"
     42  );
     43 
     44  let testReadZeroBytes = new DER.DERDecoder([]);
     45  equal(
     46    testReadZeroBytes.readBytes(0).length,
     47    0,
     48    "reading zero bytes should result in a zero-length array"
     49  );
     50 
     51  let testReadTooManyBytes = new DER.DERDecoder([0xab, 0xcd, 0xef]);
     52  throws(
     53    () => testReadTooManyBytes.readBytes(4),
     54    /data truncated/,
     55    "reading too many bytes should fail"
     56  );
     57 
     58  let testSEQUENCE = new DER.DERDecoder([0x30, 0x01, 0x01]);
     59  let content = testSEQUENCE.readTagAndGetContents(DER.SEQUENCE);
     60  equal(content.length, 1, "content should have length 1");
     61  equal(content[0], 1, "value of content should be [1]");
     62  ok(testSEQUENCE.atEnd(), "testSEQUENCE should be at the end of its input");
     63  testSEQUENCE.assertAtEnd();
     64 
     65  // The length purports to be 4 bytes, but there are only 2 available.
     66  let truncatedSEQUENCE = new DER.DERDecoder([0x30, 0x04, 0x00, 0x00]);
     67  throws(
     68    () => truncatedSEQUENCE.readTagAndGetContents(DER.SEQUENCE),
     69    /data truncated/,
     70    "should get 'data truncated' error"
     71  );
     72 
     73  // With 2 bytes of content, there is 1 remaining after reading the content.
     74  let extraDataSEQUENCE = new DER.DERDecoder([0x30, 0x02, 0xab, 0xcd, 0xef]);
     75  content = extraDataSEQUENCE.readTagAndGetContents(DER.SEQUENCE);
     76  equal(content.length, 2, "content should have length 2");
     77  deepEqual(content, [0xab, 0xcd], "value of content should be [0xab, 0xcd]");
     78  ok(
     79    !extraDataSEQUENCE.atEnd(),
     80    "extraDataSEQUENCE should not be at the end of its input"
     81  );
     82  throws(
     83    () => extraDataSEQUENCE.assertAtEnd(),
     84    /extra data/,
     85    "should get 'extra data' error"
     86  );
     87 
     88  // The length of 0x81 0x01 is invalid because it could be encoded as just
     89  // 0x01, which is shorter.
     90  let invalidLengthSEQUENCE1 = new DER.DERDecoder([0x30, 0x81, 0x01, 0x00]);
     91  throws(
     92    () => invalidLengthSEQUENCE1.readTagAndGetContents(DER.SEQUENCE),
     93    /invalid length/,
     94    "should get 'invalid length' error"
     95  );
     96 
     97  // Similarly, 0x82 0x00 0x01 could be encoded as just 0x01, which is shorter.
     98  let invalidLengthSEQUENCE2 = new DER.DERDecoder([
     99    0x30, 0x82, 0x00, 0x01, 0x00,
    100  ]);
    101  throws(
    102    () => invalidLengthSEQUENCE2.readTagAndGetContents(DER.SEQUENCE),
    103    /invalid length/,
    104    "should get 'invalid length' error"
    105  );
    106 
    107  // Lengths requiring 4 bytes to encode are not supported.
    108  let unsupportedLengthSEQUENCE = new DER.DERDecoder([
    109    0x30, 0x83, 0x01, 0x01, 0x01,
    110  ]);
    111  throws(
    112    () => unsupportedLengthSEQUENCE.readTagAndGetContents(DER.SEQUENCE),
    113    /unsupported length/,
    114    "should get 'unsupported length' error"
    115  );
    116 
    117  // Indefinite lengths are not supported (and aren't DER anyway).
    118  let unsupportedASN1SEQUENCE = new DER.DERDecoder([
    119    0x30, 0x80, 0x01, 0x00, 0x00,
    120  ]);
    121  throws(
    122    () => unsupportedASN1SEQUENCE.readTagAndGetContents(DER.SEQUENCE),
    123    /unsupported asn.1/,
    124    "should get 'unsupported asn.1' error"
    125  );
    126 
    127  let unexpectedTag = new DER.DERDecoder([0x31, 0x01, 0x00]);
    128  throws(
    129    () => unexpectedTag.readTagAndGetContents(DER.SEQUENCE),
    130    /unexpected tag/,
    131    "should get 'unexpected tag' error"
    132  );
    133 
    134  let readTLVTestcase = new DER.DERDecoder([0x02, 0x03, 0x45, 0x67, 0x89]);
    135  let bytes = readTLVTestcase.readTLV();
    136  deepEqual(
    137    bytes,
    138    [0x02, 0x03, 0x45, 0x67, 0x89],
    139    "bytes read with readTLV should be equal to expected value"
    140  );
    141 
    142  let peekTagTestcase = new DER.DERDecoder([0x30, 0x01, 0x00]);
    143  ok(
    144    peekTagTestcase.peekTag(DER.SEQUENCE),
    145    "peekTag should return true for peeking with a SEQUENCE at a SEQUENCE"
    146  );
    147  ok(
    148    !peekTagTestcase.peekTag(DER.SET),
    149    "peekTag should return false for peeking with a SET at a SEQUENCE"
    150  );
    151  peekTagTestcase.readTLV();
    152  ok(
    153    !peekTagTestcase.peekTag(DER.SEQUENCE),
    154    "peekTag should return false for peeking at a DER with no more data"
    155  );
    156 
    157  let tlvChoiceTestcase = new DER.DERDecoder([0x31, 0x02, 0xaa, 0xbb]);
    158  let tlvChoiceContents = tlvChoiceTestcase.readTLVChoice([DER.NULL, DER.SET]);
    159  deepEqual(
    160    tlvChoiceContents,
    161    [0x31, 0x02, 0xaa, 0xbb],
    162    "readTLVChoice should return expected bytes"
    163  );
    164 
    165  let tlvChoiceNoMatchTestcase = new DER.DERDecoder([0x30, 0x01, 0xff]);
    166  throws(
    167    () => tlvChoiceNoMatchTestcase.readTLVChoice([DER.NULL, DER.SET]),
    168    /unexpected tag/,
    169    "readTLVChoice should throw if no matching tag is found"
    170  );
    171 }
    172 
    173 function run_bit_string_tests() {
    174  let bitstringDER = new DER.DERDecoder([0x03, 0x04, 0x03, 0x01, 0x02, 0xf8]);
    175  let bitstring = bitstringDER.readBIT_STRING();
    176  equal(bitstring.unusedBits, 3, "BIT STRING should have 3 unused bits");
    177  deepEqual(
    178    bitstring.contents,
    179    [0x01, 0x02, 0xf8],
    180    "BIT STRING should have expected contents"
    181  );
    182 
    183  let bitstringTooManyUnusedBits = new DER.DERDecoder([0x03, 0x02, 0x08, 0x00]);
    184  throws(
    185    () => bitstringTooManyUnusedBits.readBIT_STRING(),
    186    /invalid BIT STRING encoding/,
    187    "BIT STRING with too many unused bits should throw"
    188  );
    189 
    190  // A BIT STRING must have the unused bits byte, and so its length must be at
    191  // least one.
    192  let bitstringMissingUnusedBits = new DER.DERDecoder([0x03, 0x00]);
    193  throws(
    194    () => bitstringMissingUnusedBits.readBIT_STRING(),
    195    /invalid BIT STRING encoding/,
    196    "BIT STRING with missing unused bits (and no contents) should throw"
    197  );
    198 
    199  // The minimal BIT STRING is 03 01 00 (zero bits of padding and zero bytes of
    200  // content).
    201  let minimalBitstringDER = new DER.DERDecoder([0x03, 0x01, 0x00]);
    202  let minimalBitstring = minimalBitstringDER.readBIT_STRING();
    203  equal(
    204    minimalBitstring.unusedBits,
    205    0,
    206    "minimal BIT STRING should have 0 unused bits"
    207  );
    208  equal(
    209    minimalBitstring.contents.length,
    210    0,
    211    "minimal BIT STRING should have empty contents"
    212  );
    213 
    214  // However, a BIT STRING with zero bytes of content can't have any padding,
    215  // because that makes no sense.
    216  let noContentsPaddedBitstringDER = new DER.DERDecoder([0x03, 0x01, 0x03]);
    217  throws(
    218    () => noContentsPaddedBitstringDER.readBIT_STRING(),
    219    /invalid BIT STRING encoding/,
    220    "BIT STRING with no contents with non-zero padding should throw"
    221  );
    222 }
    223 
    224 function run_compound_tests() {
    225  let derBytes = [
    226    0x30,
    227    0x1a, // SEQUENCE
    228    0x02,
    229    0x02,
    230    0x77,
    231    0xff, //   INTEGER
    232    0x06,
    233    0x03,
    234    0x2b,
    235    0x01,
    236    0x01, //   OBJECT IDENTIFIER
    237    0x30,
    238    0x07, //   SEQUENCE
    239    0x05,
    240    0x00, //     NULL
    241    0x02,
    242    0x03,
    243    0x45,
    244    0x46,
    245    0x47, //     INTEGER
    246    0x30,
    247    0x06, //   SEQUENCE
    248    0x02,
    249    0x02,
    250    0x00,
    251    0xff, //     INTEGER
    252    0x05,
    253    0x00,
    254  ]; //     NULL
    255  let der = new DER.DERDecoder(derBytes);
    256  let contents = new DER.DERDecoder(der.readTagAndGetContents(DER.SEQUENCE));
    257  let firstINTEGER = contents.readTagAndGetContents(DER.INTEGER);
    258  deepEqual(
    259    firstINTEGER,
    260    [0x77, 0xff],
    261    "first INTEGER should have expected value"
    262  );
    263  let oid = contents.readTagAndGetContents(DER.OBJECT_IDENTIFIER);
    264  deepEqual(
    265    oid,
    266    [0x2b, 0x01, 0x01],
    267    "OBJECT IDENTIFIER should have expected value"
    268  );
    269 
    270  let firstNested = new DER.DERDecoder(
    271    contents.readTagAndGetContents(DER.SEQUENCE)
    272  );
    273  let firstNestedNULL = firstNested.readTagAndGetContents(DER.NULL);
    274  equal(
    275    firstNestedNULL.length,
    276    0,
    277    "first nested NULL should have expected value (empty array)"
    278  );
    279  let firstNestedINTEGER = firstNested.readTagAndGetContents(DER.INTEGER);
    280  deepEqual(
    281    firstNestedINTEGER,
    282    [0x45, 0x46, 0x47],
    283    "first nested INTEGER should have expected value"
    284  );
    285  firstNested.assertAtEnd();
    286 
    287  let secondNested = new DER.DERDecoder(
    288    contents.readTagAndGetContents(DER.SEQUENCE)
    289  );
    290  let secondNestedINTEGER = secondNested.readTagAndGetContents(DER.INTEGER);
    291  deepEqual(
    292    secondNestedINTEGER,
    293    [0x00, 0xff],
    294    "second nested INTEGER should have expected value"
    295  );
    296  let secondNestedNULL = secondNested.readTagAndGetContents(DER.NULL);
    297  equal(
    298    secondNestedNULL.length,
    299    0,
    300    "second nested NULL should have expected value (empty array)"
    301  );
    302  secondNested.assertAtEnd();
    303 
    304  contents.assertAtEnd();
    305  der.assertAtEnd();
    306 
    307  let invalidDERBytes = [
    308    0x30,
    309    0x06, // SEQUENCE
    310    0x30,
    311    0x02, //   SEQUENCE
    312    0x02,
    313    0x01, //     INTEGER (missing data)
    314    0x05,
    315    0x00, //   NULL
    316    0x00,
    317    0x00,
    318  ]; // (extra data)
    319  let invalidDER = new DER.DERDecoder(invalidDERBytes);
    320  let invalidContents = new DER.DERDecoder(
    321    invalidDER.readTagAndGetContents(DER.SEQUENCE)
    322  );
    323  let invalidContentsContents = new DER.DERDecoder(
    324    invalidContents.readTagAndGetContents(DER.SEQUENCE)
    325  );
    326  throws(
    327    () => invalidContentsContents.readTagAndGetContents(DER.INTEGER),
    328    /data truncated/,
    329    "should throw due to missing data"
    330  );
    331  let nestedNULL = invalidContents.readTagAndGetContents(DER.NULL);
    332  equal(nestedNULL.length, 0, "nested NULL should have expected value");
    333  invalidContents.assertAtEnd();
    334  throws(
    335    () => invalidDER.assertAtEnd(),
    336    /extra data/,
    337    "should throw due to extra data"
    338  );
    339 }
    340 
    341 function run_test() {
    342  run_simple_tests();
    343  run_bit_string_tests();
    344  run_compound_tests();
    345 }