Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80647 views
1
var inherits = require('inherits');
2
3
var asn1 = require('../../asn1');
4
var base = asn1.base;
5
var bignum = asn1.bignum;
6
7
// Import DER constants
8
var der = asn1.constants.der;
9
10
function DERDecoder(entity) {
11
this.enc = 'der';
12
this.name = entity.name;
13
this.entity = entity;
14
15
// Construct base tree
16
this.tree = new DERNode();
17
this.tree._init(entity.body);
18
};
19
module.exports = DERDecoder;
20
21
DERDecoder.prototype.decode = function decode(data, options) {
22
if (!(data instanceof base.DecoderBuffer))
23
data = new base.DecoderBuffer(data, options);
24
25
return this.tree._decode(data, options);
26
};
27
28
// Tree methods
29
30
function DERNode(parent) {
31
base.Node.call(this, 'der', parent);
32
}
33
inherits(DERNode, base.Node);
34
35
DERNode.prototype._peekTag = function peekTag(buffer, tag, any) {
36
if (buffer.isEmpty())
37
return false;
38
39
var state = buffer.save();
40
var decodedTag = derDecodeTag(buffer, 'Failed to peek tag: "' + tag + '"');
41
if (buffer.isError(decodedTag))
42
return decodedTag;
43
44
buffer.restore(state);
45
46
return decodedTag.tag === tag || decodedTag.tagStr === tag || any;
47
};
48
49
DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) {
50
var decodedTag = derDecodeTag(buffer,
51
'Failed to decode tag of "' + tag + '"');
52
if (buffer.isError(decodedTag))
53
return decodedTag;
54
55
var len = derDecodeLen(buffer,
56
decodedTag.primitive,
57
'Failed to get length of "' + tag + '"');
58
59
// Failure
60
if (buffer.isError(len))
61
return len;
62
63
if (!any &&
64
decodedTag.tag !== tag &&
65
decodedTag.tagStr !== tag &&
66
decodedTag.tagStr + 'of' !== tag) {
67
return buffer.error('Failed to match tag: "' + tag + '"');
68
}
69
70
if (decodedTag.primitive || len !== null)
71
return buffer.skip(len, 'Failed to match body of: "' + tag + '"');
72
73
// Indefinite length... find END tag
74
var state = buffer.start();
75
var res = this._skipUntilEnd(
76
buffer,
77
'Failed to skip indefinite length body: "' + this.tag + '"');
78
if (buffer.isError(res))
79
return res;
80
81
return buffer.cut(state);
82
};
83
84
DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer, fail) {
85
while (true) {
86
var tag = derDecodeTag(buffer, fail);
87
if (buffer.isError(tag))
88
return tag;
89
var len = derDecodeLen(buffer, tag.primitive, fail);
90
if (buffer.isError(len))
91
return len;
92
93
var res;
94
if (tag.primitive || len !== null)
95
res = buffer.skip(len)
96
else
97
res = this._skipUntilEnd(buffer, fail);
98
99
// Failure
100
if (buffer.isError(res))
101
return res;
102
103
if (tag.tagStr === 'end')
104
break;
105
}
106
};
107
108
DERNode.prototype._decodeList = function decodeList(buffer, tag, decoder) {
109
var result = [];
110
while (!buffer.isEmpty()) {
111
var possibleEnd = this._peekTag(buffer, 'end');
112
if (buffer.isError(possibleEnd))
113
return possibleEnd;
114
115
var res = decoder.decode(buffer, 'der');
116
if (buffer.isError(res) && possibleEnd)
117
break;
118
result.push(res);
119
}
120
return result;
121
};
122
123
DERNode.prototype._decodeStr = function decodeStr(buffer, tag) {
124
if (tag === 'octstr') {
125
return buffer.raw();
126
} else if (tag === 'bitstr') {
127
var unused = buffer.readUInt8();
128
if (buffer.isError(unused))
129
return unused;
130
131
return { unused: unused, data: buffer.raw() };
132
} else if (tag === 'ia5str') {
133
return buffer.raw().toString();
134
} else {
135
return this.error('Decoding of string type: ' + tag + ' unsupported');
136
}
137
};
138
139
DERNode.prototype._decodeObjid = function decodeObjid(buffer, values, relative) {
140
var identifiers = [];
141
var ident = 0;
142
while (!buffer.isEmpty()) {
143
var subident = buffer.readUInt8();
144
ident <<= 7;
145
ident |= subident & 0x7f;
146
if ((subident & 0x80) === 0) {
147
identifiers.push(ident);
148
ident = 0;
149
}
150
}
151
if (subident & 0x80)
152
identifiers.push(ident);
153
154
var first = (identifiers[0] / 40) | 0;
155
var second = identifiers[0] % 40;
156
157
if (relative)
158
result = identifiers;
159
else
160
result = [first, second].concat(identifiers.slice(1));
161
162
if (values)
163
result = values[result.join(' ')];
164
165
return result;
166
};
167
168
DERNode.prototype._decodeTime = function decodeTime(buffer, tag) {
169
var str = buffer.raw().toString();
170
if (tag === 'gentime') {
171
var year = str.slice(0, 4) | 0;
172
var mon = str.slice(4, 6) | 0;
173
var day = str.slice(6, 8) | 0;
174
var hour = str.slice(8, 10) | 0;
175
var min = str.slice(10, 12) | 0;
176
var sec = str.slice(12, 14) | 0;
177
} else if (tag === 'utctime') {
178
var year = str.slice(0, 2) | 0;
179
var mon = str.slice(2, 4) | 0;
180
var day = str.slice(4, 6) | 0;
181
var hour = str.slice(6, 8) | 0;
182
var min = str.slice(8, 10) | 0;
183
var sec = str.slice(10, 12) | 0;
184
if (year < 70)
185
year = 2000 + year;
186
else
187
year = 1900 + year;
188
} else {
189
return this.error('Decoding ' + tag + ' time is not supported yet');
190
}
191
192
return Date.UTC(year, mon - 1, day, hour, min, sec, 0);
193
};
194
195
DERNode.prototype._decodeNull = function decodeNull(buffer) {
196
return null;
197
};
198
199
DERNode.prototype._decodeBool = function decodeBool(buffer) {
200
var res = buffer.readUInt8();
201
if (buffer.isError(res))
202
return res;
203
else
204
return res !== 0;
205
};
206
207
DERNode.prototype._decodeInt = function decodeInt(buffer, values) {
208
// Bigint, return as it is (assume big endian)
209
var raw = buffer.raw();
210
var res = new bignum(raw);
211
212
if (values)
213
res = values[res.toString(10)] || res;
214
215
return res;
216
};
217
218
DERNode.prototype._use = function use(entity, obj) {
219
if (typeof entity === 'function')
220
entity = entity(obj);
221
return entity._getDecoder('der').tree;
222
};
223
224
// Utility methods
225
226
function derDecodeTag(buf, fail) {
227
var tag = buf.readUInt8(fail);
228
if (buf.isError(tag))
229
return tag;
230
231
var cls = der.tagClass[tag >> 6];
232
var primitive = (tag & 0x20) === 0;
233
234
// Multi-octet tag - load
235
if ((tag & 0x1f) === 0x1f) {
236
var oct = tag;
237
tag = 0;
238
while ((oct & 0x80) === 0x80) {
239
oct = buf.readUInt8(fail);
240
if (buf.isError(oct))
241
return oct;
242
243
tag <<= 7;
244
tag |= oct & 0x7f;
245
}
246
} else {
247
tag &= 0x1f;
248
}
249
var tagStr = der.tag[tag];
250
251
return {
252
cls: cls,
253
primitive: primitive,
254
tag: tag,
255
tagStr: tagStr
256
};
257
}
258
259
function derDecodeLen(buf, primitive, fail) {
260
var len = buf.readUInt8(fail);
261
if (buf.isError(len))
262
return len;
263
264
// Indefinite form
265
if (!primitive && len === 0x80)
266
return null;
267
268
// Definite form
269
if ((len & 0x80) === 0) {
270
// Short form
271
return len;
272
}
273
274
// Long form
275
var num = len & 0x7f;
276
if (num >= 4)
277
return buf.error('length octect is too long');
278
279
len = 0;
280
for (var i = 0; i < num; i++) {
281
len <<= 8;
282
var j = buf.readUInt8(fail);
283
if (buf.isError(j))
284
return j;
285
len |= j;
286
}
287
288
return len;
289
}
290
291