• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.ASN1Obj = void 0;
4/*
5Copyright 2023 The Sigstore Authors.
6
7Licensed under the Apache License, Version 2.0 (the "License");
8you may not use this file except in compliance with the License.
9You may obtain a copy of the License at
10
11    http://www.apache.org/licenses/LICENSE-2.0
12
13Unless required by applicable law or agreed to in writing, software
14distributed under the License is distributed on an "AS IS" BASIS,
15WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16See the License for the specific language governing permissions and
17limitations under the License.
18*/
19const stream_1 = require("../stream");
20const error_1 = require("./error");
21const length_1 = require("./length");
22const parse_1 = require("./parse");
23const tag_1 = require("./tag");
24class ASN1Obj {
25    constructor(tag, value, subs) {
26        this.tag = tag;
27        this.value = value;
28        this.subs = subs;
29    }
30    // Constructs an ASN.1 object from a Buffer of DER-encoded bytes.
31    static parseBuffer(buf) {
32        return parseStream(new stream_1.ByteStream(buf));
33    }
34    toDER() {
35        const valueStream = new stream_1.ByteStream();
36        if (this.subs.length > 0) {
37            for (const sub of this.subs) {
38                valueStream.appendView(sub.toDER());
39            }
40        }
41        else {
42            valueStream.appendView(this.value);
43        }
44        const value = valueStream.buffer;
45        // Concat tag/length/value
46        const obj = new stream_1.ByteStream();
47        obj.appendChar(this.tag.toDER());
48        obj.appendView((0, length_1.encodeLength)(value.length));
49        obj.appendView(value);
50        return obj.buffer;
51    }
52    /////////////////////////////////////////////////////////////////////////////
53    // Convenience methods for parsing ASN.1 primitives into JS types
54    // Returns the ASN.1 object's value as a boolean. Throws an error if the
55    // object is not a boolean.
56    toBoolean() {
57        if (!this.tag.isBoolean()) {
58            throw new error_1.ASN1TypeError('not a boolean');
59        }
60        return (0, parse_1.parseBoolean)(this.value);
61    }
62    // Returns the ASN.1 object's value as a BigInt. Throws an error if the
63    // object is not an integer.
64    toInteger() {
65        if (!this.tag.isInteger()) {
66            throw new error_1.ASN1TypeError('not an integer');
67        }
68        return (0, parse_1.parseInteger)(this.value);
69    }
70    // Returns the ASN.1 object's value as an OID string. Throws an error if the
71    // object is not an OID.
72    toOID() {
73        if (!this.tag.isOID()) {
74            throw new error_1.ASN1TypeError('not an OID');
75        }
76        return (0, parse_1.parseOID)(this.value);
77    }
78    // Returns the ASN.1 object's value as a Date. Throws an error if the object
79    // is not either a UTCTime or a GeneralizedTime.
80    toDate() {
81        switch (true) {
82            case this.tag.isUTCTime():
83                return (0, parse_1.parseTime)(this.value, true);
84            case this.tag.isGeneralizedTime():
85                return (0, parse_1.parseTime)(this.value, false);
86            default:
87                throw new error_1.ASN1TypeError('not a date');
88        }
89    }
90    // Returns the ASN.1 object's value as a number[] where each number is the
91    // value of a bit in the bit string. Throws an error if the object is not a
92    // bit string.
93    toBitString() {
94        if (!this.tag.isBitString()) {
95            throw new error_1.ASN1TypeError('not a bit string');
96        }
97        return (0, parse_1.parseBitString)(this.value);
98    }
99}
100exports.ASN1Obj = ASN1Obj;
101/////////////////////////////////////////////////////////////////////////////
102// Internal stream parsing functions
103function parseStream(stream) {
104    // Parse tag, length, and value from stream
105    const tag = new tag_1.ASN1Tag(stream.getUint8());
106    const len = (0, length_1.decodeLength)(stream);
107    const value = stream.slice(stream.position, len);
108    const start = stream.position;
109    let subs = [];
110    // If the object is constructed, parse its children. Sometimes, children
111    // are embedded in OCTESTRING objects, so we need to check those
112    // for children as well.
113    if (tag.constructed) {
114        subs = collectSubs(stream, len);
115    }
116    else if (tag.isOctetString()) {
117        // Attempt to parse children of OCTETSTRING objects. If anything fails,
118        // assume the object is not constructed and treat as primitive.
119        try {
120            subs = collectSubs(stream, len);
121        }
122        catch (e) {
123            // Fail silently and treat as primitive
124        }
125    }
126    // If there are no children, move stream cursor to the end of the object
127    if (subs.length === 0) {
128        stream.seek(start + len);
129    }
130    return new ASN1Obj(tag, value, subs);
131}
132function collectSubs(stream, len) {
133    // Calculate end of object content
134    const end = stream.position + len;
135    // Make sure there are enough bytes left in the stream. This should never
136    // happen, cause it'll get caught when the stream is sliced in parseStream.
137    // Leaving as an extra check just in case.
138    /* istanbul ignore if */
139    if (end > stream.length) {
140        throw new error_1.ASN1ParseError('invalid length');
141    }
142    // Parse all children
143    const subs = [];
144    while (stream.position < end) {
145        subs.push(parseStream(stream));
146    }
147    // When we're done parsing children, we should be at the end of the object
148    if (stream.position !== end) {
149        throw new error_1.ASN1ParseError('invalid length');
150    }
151    return subs;
152}
153