Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MR414N-ID
GitHub Repository: MR414N-ID/botku2
Path: blob/master/node_modules/asn1/lib/ber/writer.js
1126 views
1
// Copyright 2011 Mark Cavage <[email protected]> All rights reserved.
2
3
var assert = require('assert');
4
var Buffer = require('safer-buffer').Buffer;
5
var ASN1 = require('./types');
6
var errors = require('./errors');
7
8
9
// --- Globals
10
11
var newInvalidAsn1Error = errors.newInvalidAsn1Error;
12
13
var DEFAULT_OPTS = {
14
size: 1024,
15
growthFactor: 8
16
};
17
18
19
// --- Helpers
20
21
function merge(from, to) {
22
assert.ok(from);
23
assert.equal(typeof (from), 'object');
24
assert.ok(to);
25
assert.equal(typeof (to), 'object');
26
27
var keys = Object.getOwnPropertyNames(from);
28
keys.forEach(function (key) {
29
if (to[key])
30
return;
31
32
var value = Object.getOwnPropertyDescriptor(from, key);
33
Object.defineProperty(to, key, value);
34
});
35
36
return to;
37
}
38
39
40
41
// --- API
42
43
function Writer(options) {
44
options = merge(DEFAULT_OPTS, options || {});
45
46
this._buf = Buffer.alloc(options.size || 1024);
47
this._size = this._buf.length;
48
this._offset = 0;
49
this._options = options;
50
51
// A list of offsets in the buffer where we need to insert
52
// sequence tag/len pairs.
53
this._seq = [];
54
}
55
56
Object.defineProperty(Writer.prototype, 'buffer', {
57
get: function () {
58
if (this._seq.length)
59
throw newInvalidAsn1Error(this._seq.length + ' unended sequence(s)');
60
61
return (this._buf.slice(0, this._offset));
62
}
63
});
64
65
Writer.prototype.writeByte = function (b) {
66
if (typeof (b) !== 'number')
67
throw new TypeError('argument must be a Number');
68
69
this._ensure(1);
70
this._buf[this._offset++] = b;
71
};
72
73
74
Writer.prototype.writeInt = function (i, tag) {
75
if (typeof (i) !== 'number')
76
throw new TypeError('argument must be a Number');
77
if (typeof (tag) !== 'number')
78
tag = ASN1.Integer;
79
80
var sz = 4;
81
82
while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000 >> 0)) &&
83
(sz > 1)) {
84
sz--;
85
i <<= 8;
86
}
87
88
if (sz > 4)
89
throw newInvalidAsn1Error('BER ints cannot be > 0xffffffff');
90
91
this._ensure(2 + sz);
92
this._buf[this._offset++] = tag;
93
this._buf[this._offset++] = sz;
94
95
while (sz-- > 0) {
96
this._buf[this._offset++] = ((i & 0xff000000) >>> 24);
97
i <<= 8;
98
}
99
100
};
101
102
103
Writer.prototype.writeNull = function () {
104
this.writeByte(ASN1.Null);
105
this.writeByte(0x00);
106
};
107
108
109
Writer.prototype.writeEnumeration = function (i, tag) {
110
if (typeof (i) !== 'number')
111
throw new TypeError('argument must be a Number');
112
if (typeof (tag) !== 'number')
113
tag = ASN1.Enumeration;
114
115
return this.writeInt(i, tag);
116
};
117
118
119
Writer.prototype.writeBoolean = function (b, tag) {
120
if (typeof (b) !== 'boolean')
121
throw new TypeError('argument must be a Boolean');
122
if (typeof (tag) !== 'number')
123
tag = ASN1.Boolean;
124
125
this._ensure(3);
126
this._buf[this._offset++] = tag;
127
this._buf[this._offset++] = 0x01;
128
this._buf[this._offset++] = b ? 0xff : 0x00;
129
};
130
131
132
Writer.prototype.writeString = function (s, tag) {
133
if (typeof (s) !== 'string')
134
throw new TypeError('argument must be a string (was: ' + typeof (s) + ')');
135
if (typeof (tag) !== 'number')
136
tag = ASN1.OctetString;
137
138
var len = Buffer.byteLength(s);
139
this.writeByte(tag);
140
this.writeLength(len);
141
if (len) {
142
this._ensure(len);
143
this._buf.write(s, this._offset);
144
this._offset += len;
145
}
146
};
147
148
149
Writer.prototype.writeBuffer = function (buf, tag) {
150
if (typeof (tag) !== 'number')
151
throw new TypeError('tag must be a number');
152
if (!Buffer.isBuffer(buf))
153
throw new TypeError('argument must be a buffer');
154
155
this.writeByte(tag);
156
this.writeLength(buf.length);
157
this._ensure(buf.length);
158
buf.copy(this._buf, this._offset, 0, buf.length);
159
this._offset += buf.length;
160
};
161
162
163
Writer.prototype.writeStringArray = function (strings) {
164
if ((!strings instanceof Array))
165
throw new TypeError('argument must be an Array[String]');
166
167
var self = this;
168
strings.forEach(function (s) {
169
self.writeString(s);
170
});
171
};
172
173
// This is really to solve DER cases, but whatever for now
174
Writer.prototype.writeOID = function (s, tag) {
175
if (typeof (s) !== 'string')
176
throw new TypeError('argument must be a string');
177
if (typeof (tag) !== 'number')
178
tag = ASN1.OID;
179
180
if (!/^([0-9]+\.){3,}[0-9]+$/.test(s))
181
throw new Error('argument is not a valid OID string');
182
183
function encodeOctet(bytes, octet) {
184
if (octet < 128) {
185
bytes.push(octet);
186
} else if (octet < 16384) {
187
bytes.push((octet >>> 7) | 0x80);
188
bytes.push(octet & 0x7F);
189
} else if (octet < 2097152) {
190
bytes.push((octet >>> 14) | 0x80);
191
bytes.push(((octet >>> 7) | 0x80) & 0xFF);
192
bytes.push(octet & 0x7F);
193
} else if (octet < 268435456) {
194
bytes.push((octet >>> 21) | 0x80);
195
bytes.push(((octet >>> 14) | 0x80) & 0xFF);
196
bytes.push(((octet >>> 7) | 0x80) & 0xFF);
197
bytes.push(octet & 0x7F);
198
} else {
199
bytes.push(((octet >>> 28) | 0x80) & 0xFF);
200
bytes.push(((octet >>> 21) | 0x80) & 0xFF);
201
bytes.push(((octet >>> 14) | 0x80) & 0xFF);
202
bytes.push(((octet >>> 7) | 0x80) & 0xFF);
203
bytes.push(octet & 0x7F);
204
}
205
}
206
207
var tmp = s.split('.');
208
var bytes = [];
209
bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10));
210
tmp.slice(2).forEach(function (b) {
211
encodeOctet(bytes, parseInt(b, 10));
212
});
213
214
var self = this;
215
this._ensure(2 + bytes.length);
216
this.writeByte(tag);
217
this.writeLength(bytes.length);
218
bytes.forEach(function (b) {
219
self.writeByte(b);
220
});
221
};
222
223
224
Writer.prototype.writeLength = function (len) {
225
if (typeof (len) !== 'number')
226
throw new TypeError('argument must be a Number');
227
228
this._ensure(4);
229
230
if (len <= 0x7f) {
231
this._buf[this._offset++] = len;
232
} else if (len <= 0xff) {
233
this._buf[this._offset++] = 0x81;
234
this._buf[this._offset++] = len;
235
} else if (len <= 0xffff) {
236
this._buf[this._offset++] = 0x82;
237
this._buf[this._offset++] = len >> 8;
238
this._buf[this._offset++] = len;
239
} else if (len <= 0xffffff) {
240
this._buf[this._offset++] = 0x83;
241
this._buf[this._offset++] = len >> 16;
242
this._buf[this._offset++] = len >> 8;
243
this._buf[this._offset++] = len;
244
} else {
245
throw newInvalidAsn1Error('Length too long (> 4 bytes)');
246
}
247
};
248
249
Writer.prototype.startSequence = function (tag) {
250
if (typeof (tag) !== 'number')
251
tag = ASN1.Sequence | ASN1.Constructor;
252
253
this.writeByte(tag);
254
this._seq.push(this._offset);
255
this._ensure(3);
256
this._offset += 3;
257
};
258
259
260
Writer.prototype.endSequence = function () {
261
var seq = this._seq.pop();
262
var start = seq + 3;
263
var len = this._offset - start;
264
265
if (len <= 0x7f) {
266
this._shift(start, len, -2);
267
this._buf[seq] = len;
268
} else if (len <= 0xff) {
269
this._shift(start, len, -1);
270
this._buf[seq] = 0x81;
271
this._buf[seq + 1] = len;
272
} else if (len <= 0xffff) {
273
this._buf[seq] = 0x82;
274
this._buf[seq + 1] = len >> 8;
275
this._buf[seq + 2] = len;
276
} else if (len <= 0xffffff) {
277
this._shift(start, len, 1);
278
this._buf[seq] = 0x83;
279
this._buf[seq + 1] = len >> 16;
280
this._buf[seq + 2] = len >> 8;
281
this._buf[seq + 3] = len;
282
} else {
283
throw newInvalidAsn1Error('Sequence too long');
284
}
285
};
286
287
288
Writer.prototype._shift = function (start, len, shift) {
289
assert.ok(start !== undefined);
290
assert.ok(len !== undefined);
291
assert.ok(shift);
292
293
this._buf.copy(this._buf, start + shift, start, start + len);
294
this._offset += shift;
295
};
296
297
Writer.prototype._ensure = function (len) {
298
assert.ok(len);
299
300
if (this._size - this._offset < len) {
301
var sz = this._size * this._options.growthFactor;
302
if (sz - this._offset < len)
303
sz += len;
304
305
var buf = Buffer.alloc(sz);
306
307
this._buf.copy(buf, 0, 0, this._offset);
308
this._buf = buf;
309
this._size = sz;
310
}
311
};
312
313
314
315
// --- Exported API
316
317
module.exports = Writer;
318
319