Projekt

Obecné

Profil

Stáhnout (7.82 KB) Statistiky
| Větev: | Revize:
1
var inherits = require('inherits');
2
var Buffer = require('buffer').Buffer;
3

    
4
var asn1 = require('../../asn1');
5
var base = asn1.base;
6

    
7
// Import DER constants
8
var der = asn1.constants.der;
9

    
10
function DEREncoder(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 = DEREncoder;
20

    
21
DEREncoder.prototype.encode = function encode(data, reporter) {
22
  return this.tree._encode(data, reporter).join();
23
};
24

    
25
// Tree methods
26

    
27
function DERNode(parent) {
28
  base.Node.call(this, 'der', parent);
29
}
30
inherits(DERNode, base.Node);
31

    
32
DERNode.prototype._encodeComposite = function encodeComposite(tag,
33
                                                              primitive,
34
                                                              cls,
35
                                                              content) {
36
  var encodedTag = encodeTag(tag, primitive, cls, this.reporter);
37

    
38
  // Short form
39
  if (content.length < 0x80) {
40
    var header = new Buffer(2);
41
    header[0] = encodedTag;
42
    header[1] = content.length;
43
    return this._createEncoderBuffer([ header, content ]);
44
  }
45

    
46
  // Long form
47
  // Count octets required to store length
48
  var lenOctets = 1;
49
  for (var i = content.length; i >= 0x100; i >>= 8)
50
    lenOctets++;
51

    
52
  var header = new Buffer(1 + 1 + lenOctets);
53
  header[0] = encodedTag;
54
  header[1] = 0x80 | lenOctets;
55

    
56
  for (var i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8)
57
    header[i] = j & 0xff;
58

    
59
  return this._createEncoderBuffer([ header, content ]);
60
};
61

    
62
DERNode.prototype._encodeStr = function encodeStr(str, tag) {
63
  if (tag === 'bitstr') {
64
    return this._createEncoderBuffer([ str.unused | 0, str.data ]);
65
  } else if (tag === 'bmpstr') {
66
    var buf = new Buffer(str.length * 2);
67
    for (var i = 0; i < str.length; i++) {
68
      buf.writeUInt16BE(str.charCodeAt(i), i * 2);
69
    }
70
    return this._createEncoderBuffer(buf);
71
  } else if (tag === 'numstr') {
72
    if (!this._isNumstr(str)) {
73
      return this.reporter.error('Encoding of string type: numstr supports ' +
74
                                 'only digits and space');
75
    }
76
    return this._createEncoderBuffer(str);
77
  } else if (tag === 'printstr') {
78
    if (!this._isPrintstr(str)) {
79
      return this.reporter.error('Encoding of string type: printstr supports ' +
80
                                 'only latin upper and lower case letters, ' +
81
                                 'digits, space, apostrophe, left and rigth ' +
82
                                 'parenthesis, plus sign, comma, hyphen, ' +
83
                                 'dot, slash, colon, equal sign, ' +
84
                                 'question mark');
85
    }
86
    return this._createEncoderBuffer(str);
87
  } else if (/str$/.test(tag)) {
88
    return this._createEncoderBuffer(str);
89
  } else if (tag === 'objDesc') {
90
    return this._createEncoderBuffer(str);
91
  } else {
92
    return this.reporter.error('Encoding of string type: ' + tag +
93
                               ' unsupported');
94
  }
95
};
96

    
97
DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) {
98
  if (typeof id === 'string') {
99
    if (!values)
100
      return this.reporter.error('string objid given, but no values map found');
101
    if (!values.hasOwnProperty(id))
102
      return this.reporter.error('objid not found in values map');
103
    id = values[id].split(/[\s\.]+/g);
104
    for (var i = 0; i < id.length; i++)
105
      id[i] |= 0;
106
  } else if (Array.isArray(id)) {
107
    id = id.slice();
108
    for (var i = 0; i < id.length; i++)
109
      id[i] |= 0;
110
  }
111

    
112
  if (!Array.isArray(id)) {
113
    return this.reporter.error('objid() should be either array or string, ' +
114
                               'got: ' + JSON.stringify(id));
115
  }
116

    
117
  if (!relative) {
118
    if (id[1] >= 40)
119
      return this.reporter.error('Second objid identifier OOB');
120
    id.splice(0, 2, id[0] * 40 + id[1]);
121
  }
122

    
123
  // Count number of octets
124
  var size = 0;
125
  for (var i = 0; i < id.length; i++) {
126
    var ident = id[i];
127
    for (size++; ident >= 0x80; ident >>= 7)
128
      size++;
129
  }
130

    
131
  var objid = new Buffer(size);
132
  var offset = objid.length - 1;
133
  for (var i = id.length - 1; i >= 0; i--) {
134
    var ident = id[i];
135
    objid[offset--] = ident & 0x7f;
136
    while ((ident >>= 7) > 0)
137
      objid[offset--] = 0x80 | (ident & 0x7f);
138
  }
139

    
140
  return this._createEncoderBuffer(objid);
141
};
142

    
143
function two(num) {
144
  if (num < 10)
145
    return '0' + num;
146
  else
147
    return num;
148
}
149

    
150
DERNode.prototype._encodeTime = function encodeTime(time, tag) {
151
  var str;
152
  var date = new Date(time);
153

    
154
  if (tag === 'gentime') {
155
    str = [
156
      two(date.getFullYear()),
157
      two(date.getUTCMonth() + 1),
158
      two(date.getUTCDate()),
159
      two(date.getUTCHours()),
160
      two(date.getUTCMinutes()),
161
      two(date.getUTCSeconds()),
162
      'Z'
163
    ].join('');
164
  } else if (tag === 'utctime') {
165
    str = [
166
      two(date.getFullYear() % 100),
167
      two(date.getUTCMonth() + 1),
168
      two(date.getUTCDate()),
169
      two(date.getUTCHours()),
170
      two(date.getUTCMinutes()),
171
      two(date.getUTCSeconds()),
172
      'Z'
173
    ].join('');
174
  } else {
175
    this.reporter.error('Encoding ' + tag + ' time is not supported yet');
176
  }
177

    
178
  return this._encodeStr(str, 'octstr');
179
};
180

    
181
DERNode.prototype._encodeNull = function encodeNull() {
182
  return this._createEncoderBuffer('');
183
};
184

    
185
DERNode.prototype._encodeInt = function encodeInt(num, values) {
186
  if (typeof num === 'string') {
187
    if (!values)
188
      return this.reporter.error('String int or enum given, but no values map');
189
    if (!values.hasOwnProperty(num)) {
190
      return this.reporter.error('Values map doesn\'t contain: ' +
191
                                 JSON.stringify(num));
192
    }
193
    num = values[num];
194
  }
195

    
196
  // Bignum, assume big endian
197
  if (typeof num !== 'number' && !Buffer.isBuffer(num)) {
198
    var numArray = num.toArray();
199
    if (!num.sign && numArray[0] & 0x80) {
200
      numArray.unshift(0);
201
    }
202
    num = new Buffer(numArray);
203
  }
204

    
205
  if (Buffer.isBuffer(num)) {
206
    var size = num.length;
207
    if (num.length === 0)
208
      size++;
209

    
210
    var out = new Buffer(size);
211
    num.copy(out);
212
    if (num.length === 0)
213
      out[0] = 0
214
    return this._createEncoderBuffer(out);
215
  }
216

    
217
  if (num < 0x80)
218
    return this._createEncoderBuffer(num);
219

    
220
  if (num < 0x100)
221
    return this._createEncoderBuffer([0, num]);
222

    
223
  var size = 1;
224
  for (var i = num; i >= 0x100; i >>= 8)
225
    size++;
226

    
227
  var out = new Array(size);
228
  for (var i = out.length - 1; i >= 0; i--) {
229
    out[i] = num & 0xff;
230
    num >>= 8;
231
  }
232
  if(out[0] & 0x80) {
233
    out.unshift(0);
234
  }
235

    
236
  return this._createEncoderBuffer(new Buffer(out));
237
};
238

    
239
DERNode.prototype._encodeBool = function encodeBool(value) {
240
  return this._createEncoderBuffer(value ? 0xff : 0);
241
};
242

    
243
DERNode.prototype._use = function use(entity, obj) {
244
  if (typeof entity === 'function')
245
    entity = entity(obj);
246
  return entity._getEncoder('der').tree;
247
};
248

    
249
DERNode.prototype._skipDefault = function skipDefault(dataBuffer, reporter, parent) {
250
  var state = this._baseState;
251
  var i;
252
  if (state['default'] === null)
253
    return false;
254

    
255
  var data = dataBuffer.join();
256
  if (state.defaultBuffer === undefined)
257
    state.defaultBuffer = this._encodeValue(state['default'], reporter, parent).join();
258

    
259
  if (data.length !== state.defaultBuffer.length)
260
    return false;
261

    
262
  for (i=0; i < data.length; i++)
263
    if (data[i] !== state.defaultBuffer[i])
264
      return false;
265

    
266
  return true;
267
};
268

    
269
// Utility methods
270

    
271
function encodeTag(tag, primitive, cls, reporter) {
272
  var res;
273

    
274
  if (tag === 'seqof')
275
    tag = 'seq';
276
  else if (tag === 'setof')
277
    tag = 'set';
278

    
279
  if (der.tagByName.hasOwnProperty(tag))
280
    res = der.tagByName[tag];
281
  else if (typeof tag === 'number' && (tag | 0) === tag)
282
    res = tag;
283
  else
284
    return reporter.error('Unknown tag: ' + tag);
285

    
286
  if (res >= 0x1f)
287
    return reporter.error('Multi-octet tag encoding unsupported');
288

    
289
  if (!primitive)
290
    res |= 0x20;
291

    
292
  res |= (der.tagClassByName[cls || 'universal'] << 6);
293

    
294
  return res;
295
}
(1-1/3)