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 ||
|
47
|
(decodedTag.tagStr + 'of') === tag || any;
|
48
|
};
|
49
|
|
50
|
DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) {
|
51
|
var decodedTag = derDecodeTag(buffer,
|
52
|
'Failed to decode tag of "' + tag + '"');
|
53
|
if (buffer.isError(decodedTag))
|
54
|
return decodedTag;
|
55
|
|
56
|
var len = derDecodeLen(buffer,
|
57
|
decodedTag.primitive,
|
58
|
'Failed to get length of "' + tag + '"');
|
59
|
|
60
|
// Failure
|
61
|
if (buffer.isError(len))
|
62
|
return len;
|
63
|
|
64
|
if (!any &&
|
65
|
decodedTag.tag !== tag &&
|
66
|
decodedTag.tagStr !== tag &&
|
67
|
decodedTag.tagStr + 'of' !== tag) {
|
68
|
return buffer.error('Failed to match tag: "' + tag + '"');
|
69
|
}
|
70
|
|
71
|
if (decodedTag.primitive || len !== null)
|
72
|
return buffer.skip(len, 'Failed to match body of: "' + tag + '"');
|
73
|
|
74
|
// Indefinite length... find END tag
|
75
|
var state = buffer.save();
|
76
|
var res = this._skipUntilEnd(
|
77
|
buffer,
|
78
|
'Failed to skip indefinite length body: "' + this.tag + '"');
|
79
|
if (buffer.isError(res))
|
80
|
return res;
|
81
|
|
82
|
len = buffer.offset - state.offset;
|
83
|
buffer.restore(state);
|
84
|
return buffer.skip(len, 'Failed to match body of: "' + tag + '"');
|
85
|
};
|
86
|
|
87
|
DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer, fail) {
|
88
|
while (true) {
|
89
|
var tag = derDecodeTag(buffer, fail);
|
90
|
if (buffer.isError(tag))
|
91
|
return tag;
|
92
|
var len = derDecodeLen(buffer, tag.primitive, fail);
|
93
|
if (buffer.isError(len))
|
94
|
return len;
|
95
|
|
96
|
var res;
|
97
|
if (tag.primitive || len !== null)
|
98
|
res = buffer.skip(len)
|
99
|
else
|
100
|
res = this._skipUntilEnd(buffer, fail);
|
101
|
|
102
|
// Failure
|
103
|
if (buffer.isError(res))
|
104
|
return res;
|
105
|
|
106
|
if (tag.tagStr === 'end')
|
107
|
break;
|
108
|
}
|
109
|
};
|
110
|
|
111
|
DERNode.prototype._decodeList = function decodeList(buffer, tag, decoder,
|
112
|
options) {
|
113
|
var result = [];
|
114
|
while (!buffer.isEmpty()) {
|
115
|
var possibleEnd = this._peekTag(buffer, 'end');
|
116
|
if (buffer.isError(possibleEnd))
|
117
|
return possibleEnd;
|
118
|
|
119
|
var res = decoder.decode(buffer, 'der', options);
|
120
|
if (buffer.isError(res) && possibleEnd)
|
121
|
break;
|
122
|
result.push(res);
|
123
|
}
|
124
|
return result;
|
125
|
};
|
126
|
|
127
|
DERNode.prototype._decodeStr = function decodeStr(buffer, tag) {
|
128
|
if (tag === 'bitstr') {
|
129
|
var unused = buffer.readUInt8();
|
130
|
if (buffer.isError(unused))
|
131
|
return unused;
|
132
|
return { unused: unused, data: buffer.raw() };
|
133
|
} else if (tag === 'bmpstr') {
|
134
|
var raw = buffer.raw();
|
135
|
if (raw.length % 2 === 1)
|
136
|
return buffer.error('Decoding of string type: bmpstr length mismatch');
|
137
|
|
138
|
var str = '';
|
139
|
for (var i = 0; i < raw.length / 2; i++) {
|
140
|
str += String.fromCharCode(raw.readUInt16BE(i * 2));
|
141
|
}
|
142
|
return str;
|
143
|
} else if (tag === 'numstr') {
|
144
|
var numstr = buffer.raw().toString('ascii');
|
145
|
if (!this._isNumstr(numstr)) {
|
146
|
return buffer.error('Decoding of string type: ' +
|
147
|
'numstr unsupported characters');
|
148
|
}
|
149
|
return numstr;
|
150
|
} else if (tag === 'octstr') {
|
151
|
return buffer.raw();
|
152
|
} else if (tag === 'objDesc') {
|
153
|
return buffer.raw();
|
154
|
} else if (tag === 'printstr') {
|
155
|
var printstr = buffer.raw().toString('ascii');
|
156
|
if (!this._isPrintstr(printstr)) {
|
157
|
return buffer.error('Decoding of string type: ' +
|
158
|
'printstr unsupported characters');
|
159
|
}
|
160
|
return printstr;
|
161
|
} else if (/str$/.test(tag)) {
|
162
|
return buffer.raw().toString();
|
163
|
} else {
|
164
|
return buffer.error('Decoding of string type: ' + tag + ' unsupported');
|
165
|
}
|
166
|
};
|
167
|
|
168
|
DERNode.prototype._decodeObjid = function decodeObjid(buffer, values, relative) {
|
169
|
var result;
|
170
|
var identifiers = [];
|
171
|
var ident = 0;
|
172
|
while (!buffer.isEmpty()) {
|
173
|
var subident = buffer.readUInt8();
|
174
|
ident <<= 7;
|
175
|
ident |= subident & 0x7f;
|
176
|
if ((subident & 0x80) === 0) {
|
177
|
identifiers.push(ident);
|
178
|
ident = 0;
|
179
|
}
|
180
|
}
|
181
|
if (subident & 0x80)
|
182
|
identifiers.push(ident);
|
183
|
|
184
|
var first = (identifiers[0] / 40) | 0;
|
185
|
var second = identifiers[0] % 40;
|
186
|
|
187
|
if (relative)
|
188
|
result = identifiers;
|
189
|
else
|
190
|
result = [first, second].concat(identifiers.slice(1));
|
191
|
|
192
|
if (values) {
|
193
|
var tmp = values[result.join(' ')];
|
194
|
if (tmp === undefined)
|
195
|
tmp = values[result.join('.')];
|
196
|
if (tmp !== undefined)
|
197
|
result = tmp;
|
198
|
}
|
199
|
|
200
|
return result;
|
201
|
};
|
202
|
|
203
|
DERNode.prototype._decodeTime = function decodeTime(buffer, tag) {
|
204
|
var str = buffer.raw().toString();
|
205
|
if (tag === 'gentime') {
|
206
|
var year = str.slice(0, 4) | 0;
|
207
|
var mon = str.slice(4, 6) | 0;
|
208
|
var day = str.slice(6, 8) | 0;
|
209
|
var hour = str.slice(8, 10) | 0;
|
210
|
var min = str.slice(10, 12) | 0;
|
211
|
var sec = str.slice(12, 14) | 0;
|
212
|
} else if (tag === 'utctime') {
|
213
|
var year = str.slice(0, 2) | 0;
|
214
|
var mon = str.slice(2, 4) | 0;
|
215
|
var day = str.slice(4, 6) | 0;
|
216
|
var hour = str.slice(6, 8) | 0;
|
217
|
var min = str.slice(8, 10) | 0;
|
218
|
var sec = str.slice(10, 12) | 0;
|
219
|
if (year < 70)
|
220
|
year = 2000 + year;
|
221
|
else
|
222
|
year = 1900 + year;
|
223
|
} else {
|
224
|
return buffer.error('Decoding ' + tag + ' time is not supported yet');
|
225
|
}
|
226
|
|
227
|
return Date.UTC(year, mon - 1, day, hour, min, sec, 0);
|
228
|
};
|
229
|
|
230
|
DERNode.prototype._decodeNull = function decodeNull(buffer) {
|
231
|
return null;
|
232
|
};
|
233
|
|
234
|
DERNode.prototype._decodeBool = function decodeBool(buffer) {
|
235
|
var res = buffer.readUInt8();
|
236
|
if (buffer.isError(res))
|
237
|
return res;
|
238
|
else
|
239
|
return res !== 0;
|
240
|
};
|
241
|
|
242
|
DERNode.prototype._decodeInt = function decodeInt(buffer, values) {
|
243
|
// Bigint, return as it is (assume big endian)
|
244
|
var raw = buffer.raw();
|
245
|
var res = new bignum(raw);
|
246
|
|
247
|
if (values)
|
248
|
res = values[res.toString(10)] || res;
|
249
|
|
250
|
return res;
|
251
|
};
|
252
|
|
253
|
DERNode.prototype._use = function use(entity, obj) {
|
254
|
if (typeof entity === 'function')
|
255
|
entity = entity(obj);
|
256
|
return entity._getDecoder('der').tree;
|
257
|
};
|
258
|
|
259
|
// Utility methods
|
260
|
|
261
|
function derDecodeTag(buf, fail) {
|
262
|
var tag = buf.readUInt8(fail);
|
263
|
if (buf.isError(tag))
|
264
|
return tag;
|
265
|
|
266
|
var cls = der.tagClass[tag >> 6];
|
267
|
var primitive = (tag & 0x20) === 0;
|
268
|
|
269
|
// Multi-octet tag - load
|
270
|
if ((tag & 0x1f) === 0x1f) {
|
271
|
var oct = tag;
|
272
|
tag = 0;
|
273
|
while ((oct & 0x80) === 0x80) {
|
274
|
oct = buf.readUInt8(fail);
|
275
|
if (buf.isError(oct))
|
276
|
return oct;
|
277
|
|
278
|
tag <<= 7;
|
279
|
tag |= oct & 0x7f;
|
280
|
}
|
281
|
} else {
|
282
|
tag &= 0x1f;
|
283
|
}
|
284
|
var tagStr = der.tag[tag];
|
285
|
|
286
|
return {
|
287
|
cls: cls,
|
288
|
primitive: primitive,
|
289
|
tag: tag,
|
290
|
tagStr: tagStr
|
291
|
};
|
292
|
}
|
293
|
|
294
|
function derDecodeLen(buf, primitive, fail) {
|
295
|
var len = buf.readUInt8(fail);
|
296
|
if (buf.isError(len))
|
297
|
return len;
|
298
|
|
299
|
// Indefinite form
|
300
|
if (!primitive && len === 0x80)
|
301
|
return null;
|
302
|
|
303
|
// Definite form
|
304
|
if ((len & 0x80) === 0) {
|
305
|
// Short form
|
306
|
return len;
|
307
|
}
|
308
|
|
309
|
// Long form
|
310
|
var num = len & 0x7f;
|
311
|
if (num > 4)
|
312
|
return buf.error('length octect is too long');
|
313
|
|
314
|
len = 0;
|
315
|
for (var i = 0; i < num; i++) {
|
316
|
len <<= 8;
|
317
|
var j = buf.readUInt8(fail);
|
318
|
if (buf.isError(j))
|
319
|
return j;
|
320
|
len |= j;
|
321
|
}
|
322
|
|
323
|
return len;
|
324
|
}
|