• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.x509SCTExtension = exports.x509SubjectKeyIDExtension = exports.x509AuthorityKeyIDExtension = exports.x509SubjectAlternativeNameExtension = exports.x509KeyUsageExtension = exports.x509BasicConstraintsExtension = exports.x509Extension = void 0;
4const stream_1 = require("../util/stream");
5const sct_1 = require("./sct");
6// https://www.rfc-editor.org/rfc/rfc5280#section-4.1
7class x509Extension {
8    constructor(asn1) {
9        this.root = asn1;
10    }
11    get oid() {
12        return this.root.subs[0].toOID();
13    }
14    get critical() {
15        // The critical field is optional and will be the second element of the
16        // extension sequence if present. Default to false if not present.
17        return this.root.subs.length === 3 ? this.root.subs[1].toBoolean() : false;
18    }
19    get value() {
20        return this.extnValueObj.value;
21    }
22    get valueObj() {
23        return this.extnValueObj;
24    }
25    get extnValueObj() {
26        // The extnValue field will be the last element of the extension sequence
27        return this.root.subs[this.root.subs.length - 1];
28    }
29}
30exports.x509Extension = x509Extension;
31// https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.9
32class x509BasicConstraintsExtension extends x509Extension {
33    get isCA() {
34        return this.sequence.subs[0].toBoolean();
35    }
36    get pathLenConstraint() {
37        return this.sequence.subs.length > 1
38            ? this.sequence.subs[1].toInteger()
39            : undefined;
40    }
41    // The extnValue field contains a single sequence wrapping the isCA and
42    // pathLenConstraint.
43    get sequence() {
44        return this.extnValueObj.subs[0];
45    }
46}
47exports.x509BasicConstraintsExtension = x509BasicConstraintsExtension;
48// https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.3
49class x509KeyUsageExtension extends x509Extension {
50    get digitalSignature() {
51        return this.bitString[0] === 1;
52    }
53    get keyCertSign() {
54        return this.bitString[5] === 1;
55    }
56    get crlSign() {
57        return this.bitString[6] === 1;
58    }
59    // The extnValue field contains a single bit string which is a bit mask
60    // indicating which key usages are enabled.
61    get bitString() {
62        return this.extnValueObj.subs[0].toBitString();
63    }
64}
65exports.x509KeyUsageExtension = x509KeyUsageExtension;
66// https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.6
67class x509SubjectAlternativeNameExtension extends x509Extension {
68    get rfc822Name() {
69        return this.findGeneralName(0x01)?.value.toString('ascii');
70    }
71    get uri() {
72        return this.findGeneralName(0x06)?.value.toString('ascii');
73    }
74    // Retrieve the value of an otherName with the given OID.
75    otherName(oid) {
76        const otherName = this.findGeneralName(0x00);
77        if (otherName === undefined) {
78            return undefined;
79        }
80        // The otherName is a sequence containing an OID and a value.
81        // Need to check that the OID matches the one we're looking for.
82        const otherNameOID = otherName.subs[0].toOID();
83        if (otherNameOID !== oid) {
84            return undefined;
85        }
86        // The otherNameValue is a sequence containing the actual value.
87        const otherNameValue = otherName.subs[1];
88        return otherNameValue.subs[0].value.toString('ascii');
89    }
90    findGeneralName(tag) {
91        return this.generalNames.find((gn) => gn.tag.isContextSpecific(tag));
92    }
93    // The extnValue field contains a sequence of GeneralNames.
94    get generalNames() {
95        return this.extnValueObj.subs[0].subs;
96    }
97}
98exports.x509SubjectAlternativeNameExtension = x509SubjectAlternativeNameExtension;
99// https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.1
100class x509AuthorityKeyIDExtension extends x509Extension {
101    get keyIdentifier() {
102        return this.findSequenceMember(0x00)?.value;
103    }
104    findSequenceMember(tag) {
105        return this.sequence.subs.find((el) => el.tag.isContextSpecific(tag));
106    }
107    // The extnValue field contains a single sequence wrapping the keyIdentifier
108    get sequence() {
109        return this.extnValueObj.subs[0];
110    }
111}
112exports.x509AuthorityKeyIDExtension = x509AuthorityKeyIDExtension;
113// https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.2
114class x509SubjectKeyIDExtension extends x509Extension {
115    get keyIdentifier() {
116        return this.extnValueObj.subs[0].value;
117    }
118}
119exports.x509SubjectKeyIDExtension = x509SubjectKeyIDExtension;
120// https://www.rfc-editor.org/rfc/rfc6962#section-3.3
121class x509SCTExtension extends x509Extension {
122    constructor(asn1) {
123        super(asn1);
124    }
125    get signedCertificateTimestamps() {
126        const buf = this.extnValueObj.subs[0].value;
127        const stream = new stream_1.ByteStream(buf);
128        // The overall list length is encoded in the first two bytes -- note this
129        // is the length of the list in bytes, NOT the number of SCTs in the list
130        const end = stream.getUint16() + 2;
131        const sctList = [];
132        while (stream.position < end) {
133            // Read the length of the next SCT
134            const sctLength = stream.getUint16();
135            // Slice out the bytes for the next SCT and parse it
136            const sct = stream.getBlock(sctLength);
137            sctList.push(sct_1.SignedCertificateTimestamp.parse(sct));
138        }
139        if (stream.position !== end) {
140            throw new Error('SCT list length does not match actual length');
141        }
142        return sctList;
143    }
144}
145exports.x509SCTExtension = x509SCTExtension;
146