codekingpro/portable-devtools
114k
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 tag;
26 subs;
27 value;
28 constructor(tag, value, subs) {
29 this.tag = tag;
30 this.value = value;
31 this.subs = subs;
32 }
33 // Constructs an ASN.1 object from a Buffer of DER-encoded bytes.
34 static parseBuffer(buf) {
35 return parseStream(new stream_1.ByteStream(buf));
36 }
37 toDER() {
38 const valueStream = new stream_1.ByteStream();
39 if (this.subs.length > 0) {
40 for (const sub of this.subs) {
41 valueStream.appendView(sub.toDER());
42 }
43 }
44 else {
45 valueStream.appendView(this.value);
46 }
47 const value = valueStream.buffer;
48 // Concat tag/length/value
49 const obj = new stream_1.ByteStream();
50 obj.appendChar(this.tag.toDER());
51 obj.appendView((0, length_1.encodeLength)(value.length));
52 obj.appendView(value);
53 return obj.buffer;
54 }
55 /////////////////////////////////////////////////////////////////////////////
56 // Convenience methods for parsing ASN.1 primitives into JS types
57 // Returns the ASN.1 object's value as a boolean. Throws an error if the
58 // object is not a boolean.
59 toBoolean() {
60 if (!this.tag.isBoolean()) {
61 throw new error_1.ASN1TypeError('not a boolean');
62 }
63 return (0, parse_1.parseBoolean)(this.value);
64 }
65 // Returns the ASN.1 object's value as a BigInt. Throws an error if the
66 // object is not an integer.
67 toInteger() {
68 if (!this.tag.isInteger()) {
69 throw new error_1.ASN1TypeError('not an integer');
70 }
71 return (0, parse_1.parseInteger)(this.value);
72 }
73 // Returns the ASN.1 object's value as an OID string. Throws an error if the
74 // object is not an OID.
75 toOID() {
76 if (!this.tag.isOID()) {
77 throw new error_1.ASN1TypeError('not an OID');
78 }
79 return (0, parse_1.parseOID)(this.value);
80 }
81 // Returns the ASN.1 object's value as a Date. Throws an error if the object
82 // is not either a UTCTime or a GeneralizedTime.
83 toDate() {
84 switch (true) {
85 case this.tag.isUTCTime():
86 return (0, parse_1.parseTime)(this.value, true);
87 case this.tag.isGeneralizedTime():
88 return (0, parse_1.parseTime)(this.value, false);
89 default:
90 throw new error_1.ASN1TypeError('not a date');
91 }
92 }
93 // Returns the ASN.1 object's value as a number[] where each number is the
94 // value of a bit in the bit string. Throws an error if the object is not a
95 // bit string.
96 toBitString() {
97 if (!this.tag.isBitString()) {
98 throw new error_1.ASN1TypeError('not a bit string');
99 }
100 return (0, parse_1.parseBitString)(this.value);
101 }
102}
103exports.ASN1Obj = ASN1Obj;
104/////////////////////////////////////////////////////////////////////////////
105// Internal stream parsing functions
106function parseStream(stream) {
107 // Parse tag, length, and value from stream
108 const tag = new tag_1.ASN1Tag(stream.getUint8());
109 const len = (0, length_1.decodeLength)(stream);
110 const value = stream.slice(stream.position, len);
111 const start = stream.position;
112 let subs = [];
113 // If the object is constructed, parse its children. Sometimes, children
114 // are embedded in OCTESTRING objects, so we need to check those
115 // for children as well.
116 if (tag.constructed) {
117 subs = collectSubs(stream, len);
118 }
119 else if (tag.isOctetString()) {
120 // Attempt to parse children of OCTETSTRING objects. If anything fails,
121 // assume the object is not constructed and treat as primitive.
122 try {
123 subs = collectSubs(stream, len);
124 }
125 catch (e) {
126 // Fail silently and treat as primitive
127 }
128 }
129 // If there are no children, move stream cursor to the end of the object
130 if (subs.length === 0) {
131 stream.seek(start + len);
132 }
133 return new ASN1Obj(tag, value, subs);
134}
135function collectSubs(stream, len) {
136 // Calculate end of object content
137 const end = stream.position + len;
138 // Make sure there are enough bytes left in the stream. This should never
139 // happen, cause it'll get caught when the stream is sliced in parseStream.
140 // Leaving as an extra check just in case.
141 /* istanbul ignore if */
142 if (end > stream.length) {
143 throw new error_1.ASN1ParseError('invalid length');
144 }
145 // Parse all children
146 const subs = [];
147 while (stream.position < end) {
148 subs.push(parseStream(stream));
149 }
150 // When we're done parsing children, we should be at the end of the object
151 if (stream.position !== end) {
152 throw new error_1.ASN1ParseError('invalid length');
153 }
154 return subs;
155}
156 