Backed out changeset f53842753805 (bug 1804872) for causing reftest failures on 15535...
[gecko.git] / security / manager / ssl / X509.sys.mjs
blob0d85ca5730911ec407a03d30f75522e2801eeaf6
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/. */
5 import { DER } from "resource://gre/modules/psm/DER.sys.mjs";
7 const ERROR_UNSUPPORTED_ASN1 = "unsupported asn.1";
8 const ERROR_TIME_NOT_VALID = "Time not valid";
9 const ERROR_LIBRARY_FAILURE = "library failure";
11 const X509v3 = 2;
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));
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   }
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));
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   }
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   }
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   }
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   }
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   }
133  * Helper function for reading the next SEQUENCE out of a DER and creating a new
134  * DER out of the resulting bytes.
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));
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.
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));
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   }
167   get tbsCertificate() {
168     return this._tbsCertificate;
169   }
171   get signatureAlgorithm() {
172     return this._signatureAlgorithm;
173   }
175   get signatureValue() {
176     return this._signatureValue;
177   }
179   parseOverride() {
180     let contents = readSEQUENCEAndMakeDER(this._der);
181     this._tbsCertificate.parse(contents.readTLV());
182     this._signatureAlgorithm.parse(contents.readTLV());
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   }
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   }
222   get version() {
223     return this._version;
224   }
226   get serialNumber() {
227     return this._serialNumber;
228   }
230   get signature() {
231     return this._signature;
232   }
234   get issuer() {
235     return this._issuer;
236   }
238   get validity() {
239     return this._validity;
240   }
242   get subject() {
243     return this._subject;
244   }
246   get subjectPublicKeyInfo() {
247     return this._subjectPublicKeyInfo;
248   }
250   get extensions() {
251     return this._extensions;
252   }
254   parseOverride() {
255     let contents = readSEQUENCEAndMakeDER(this._der);
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     }
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());
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     }
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   }
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   }
317   get algorithm() {
318     return this._algorithm;
319   }
321   get parameters() {
322     return this._parameters;
323   }
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   }
340 // Name ::= CHOICE { -- only one possibility for now --
341 //   rdnSequence  RDNSequence }
343 // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
344 class Name extends DecodedDER {
345   constructor() {
346     super();
347     this._rdns = [];
348   }
350   get rdns() {
351     return this._rdns;
352   }
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   }
366 // RelativeDistinguishedName ::=
367 //   SET SIZE (1..MAX) OF AttributeTypeAndValue
368 class RelativeDistinguishedName extends DecodedDER {
369   constructor() {
370     super();
371     this._avas = [];
372   }
374   get avas() {
375     return this._avas;
376   }
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   }
391 // AttributeTypeAndValue ::= SEQUENCE {
392 //   type     AttributeType,
393 //   value    AttributeValue }
395 // AttributeType ::= OBJECT IDENTIFIER
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   }
405   get type() {
406     return this._type;
407   }
409   get value() {
410     return this._value;
411   }
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   }
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   }
445   get type() {
446     return this._type;
447   }
449   get value() {
450     return this._value;
451   }
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   }
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   }
479   get time() {
480     return this._time;
481   }
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     }
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     }
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     }
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     }
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     }
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     }
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));
559     contents.assertAtEnd();
560     this._der.assertAtEnd();
561   }
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   }
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   }
588   get notBefore() {
589     return this._notBefore;
590   }
592   get notAfter() {
593     return this._notAfter;
594   }
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   }
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   }
619   get algorithm() {
620     return this._algorithm;
621   }
623   get subjectPublicKey() {
624     return this._subjectPublicKey;
625   }
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;
636     contents.assertAtEnd();
637     this._der.assertAtEnd();
638   }
641 export var X509 = { Certificate };