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 }