You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

der.js 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. var inherits = require('inherits');
  2. var Buffer = require('buffer').Buffer;
  3. var asn1 = require('../../asn1');
  4. var base = asn1.base;
  5. // Import DER constants
  6. var der = asn1.constants.der;
  7. function DEREncoder(entity) {
  8. this.enc = 'der';
  9. this.name = entity.name;
  10. this.entity = entity;
  11. // Construct base tree
  12. this.tree = new DERNode();
  13. this.tree._init(entity.body);
  14. };
  15. module.exports = DEREncoder;
  16. DEREncoder.prototype.encode = function encode(data, reporter) {
  17. return this.tree._encode(data, reporter).join();
  18. };
  19. // Tree methods
  20. function DERNode(parent) {
  21. base.Node.call(this, 'der', parent);
  22. }
  23. inherits(DERNode, base.Node);
  24. DERNode.prototype._encodeComposite = function encodeComposite(tag,
  25. primitive,
  26. cls,
  27. content) {
  28. var encodedTag = encodeTag(tag, primitive, cls, this.reporter);
  29. // Short form
  30. if (content.length < 0x80) {
  31. var header = new Buffer(2);
  32. header[0] = encodedTag;
  33. header[1] = content.length;
  34. return this._createEncoderBuffer([ header, content ]);
  35. }
  36. // Long form
  37. // Count octets required to store length
  38. var lenOctets = 1;
  39. for (var i = content.length; i >= 0x100; i >>= 8)
  40. lenOctets++;
  41. var header = new Buffer(1 + 1 + lenOctets);
  42. header[0] = encodedTag;
  43. header[1] = 0x80 | lenOctets;
  44. for (var i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8)
  45. header[i] = j & 0xff;
  46. return this._createEncoderBuffer([ header, content ]);
  47. };
  48. DERNode.prototype._encodeStr = function encodeStr(str, tag) {
  49. if (tag === 'bitstr') {
  50. return this._createEncoderBuffer([ str.unused | 0, str.data ]);
  51. } else if (tag === 'bmpstr') {
  52. var buf = new Buffer(str.length * 2);
  53. for (var i = 0; i < str.length; i++) {
  54. buf.writeUInt16BE(str.charCodeAt(i), i * 2);
  55. }
  56. return this._createEncoderBuffer(buf);
  57. } else if (tag === 'numstr') {
  58. if (!this._isNumstr(str)) {
  59. return this.reporter.error('Encoding of string type: numstr supports ' +
  60. 'only digits and space');
  61. }
  62. return this._createEncoderBuffer(str);
  63. } else if (tag === 'printstr') {
  64. if (!this._isPrintstr(str)) {
  65. return this.reporter.error('Encoding of string type: printstr supports ' +
  66. 'only latin upper and lower case letters, ' +
  67. 'digits, space, apostrophe, left and rigth ' +
  68. 'parenthesis, plus sign, comma, hyphen, ' +
  69. 'dot, slash, colon, equal sign, ' +
  70. 'question mark');
  71. }
  72. return this._createEncoderBuffer(str);
  73. } else if (/str$/.test(tag)) {
  74. return this._createEncoderBuffer(str);
  75. } else if (tag === 'objDesc') {
  76. return this._createEncoderBuffer(str);
  77. } else {
  78. return this.reporter.error('Encoding of string type: ' + tag +
  79. ' unsupported');
  80. }
  81. };
  82. DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) {
  83. if (typeof id === 'string') {
  84. if (!values)
  85. return this.reporter.error('string objid given, but no values map found');
  86. if (!values.hasOwnProperty(id))
  87. return this.reporter.error('objid not found in values map');
  88. id = values[id].split(/[\s\.]+/g);
  89. for (var i = 0; i < id.length; i++)
  90. id[i] |= 0;
  91. } else if (Array.isArray(id)) {
  92. id = id.slice();
  93. for (var i = 0; i < id.length; i++)
  94. id[i] |= 0;
  95. }
  96. if (!Array.isArray(id)) {
  97. return this.reporter.error('objid() should be either array or string, ' +
  98. 'got: ' + JSON.stringify(id));
  99. }
  100. if (!relative) {
  101. if (id[1] >= 40)
  102. return this.reporter.error('Second objid identifier OOB');
  103. id.splice(0, 2, id[0] * 40 + id[1]);
  104. }
  105. // Count number of octets
  106. var size = 0;
  107. for (var i = 0; i < id.length; i++) {
  108. var ident = id[i];
  109. for (size++; ident >= 0x80; ident >>= 7)
  110. size++;
  111. }
  112. var objid = new Buffer(size);
  113. var offset = objid.length - 1;
  114. for (var i = id.length - 1; i >= 0; i--) {
  115. var ident = id[i];
  116. objid[offset--] = ident & 0x7f;
  117. while ((ident >>= 7) > 0)
  118. objid[offset--] = 0x80 | (ident & 0x7f);
  119. }
  120. return this._createEncoderBuffer(objid);
  121. };
  122. function two(num) {
  123. if (num < 10)
  124. return '0' + num;
  125. else
  126. return num;
  127. }
  128. DERNode.prototype._encodeTime = function encodeTime(time, tag) {
  129. var str;
  130. var date = new Date(time);
  131. if (tag === 'gentime') {
  132. str = [
  133. two(date.getFullYear()),
  134. two(date.getUTCMonth() + 1),
  135. two(date.getUTCDate()),
  136. two(date.getUTCHours()),
  137. two(date.getUTCMinutes()),
  138. two(date.getUTCSeconds()),
  139. 'Z'
  140. ].join('');
  141. } else if (tag === 'utctime') {
  142. str = [
  143. two(date.getFullYear() % 100),
  144. two(date.getUTCMonth() + 1),
  145. two(date.getUTCDate()),
  146. two(date.getUTCHours()),
  147. two(date.getUTCMinutes()),
  148. two(date.getUTCSeconds()),
  149. 'Z'
  150. ].join('');
  151. } else {
  152. this.reporter.error('Encoding ' + tag + ' time is not supported yet');
  153. }
  154. return this._encodeStr(str, 'octstr');
  155. };
  156. DERNode.prototype._encodeNull = function encodeNull() {
  157. return this._createEncoderBuffer('');
  158. };
  159. DERNode.prototype._encodeInt = function encodeInt(num, values) {
  160. if (typeof num === 'string') {
  161. if (!values)
  162. return this.reporter.error('String int or enum given, but no values map');
  163. if (!values.hasOwnProperty(num)) {
  164. return this.reporter.error('Values map doesn\'t contain: ' +
  165. JSON.stringify(num));
  166. }
  167. num = values[num];
  168. }
  169. // Bignum, assume big endian
  170. if (typeof num !== 'number' && !Buffer.isBuffer(num)) {
  171. var numArray = num.toArray();
  172. if (!num.sign && numArray[0] & 0x80) {
  173. numArray.unshift(0);
  174. }
  175. num = new Buffer(numArray);
  176. }
  177. if (Buffer.isBuffer(num)) {
  178. var size = num.length;
  179. if (num.length === 0)
  180. size++;
  181. var out = new Buffer(size);
  182. num.copy(out);
  183. if (num.length === 0)
  184. out[0] = 0
  185. return this._createEncoderBuffer(out);
  186. }
  187. if (num < 0x80)
  188. return this._createEncoderBuffer(num);
  189. if (num < 0x100)
  190. return this._createEncoderBuffer([0, num]);
  191. var size = 1;
  192. for (var i = num; i >= 0x100; i >>= 8)
  193. size++;
  194. var out = new Array(size);
  195. for (var i = out.length - 1; i >= 0; i--) {
  196. out[i] = num & 0xff;
  197. num >>= 8;
  198. }
  199. if(out[0] & 0x80) {
  200. out.unshift(0);
  201. }
  202. return this._createEncoderBuffer(new Buffer(out));
  203. };
  204. DERNode.prototype._encodeBool = function encodeBool(value) {
  205. return this._createEncoderBuffer(value ? 0xff : 0);
  206. };
  207. DERNode.prototype._use = function use(entity, obj) {
  208. if (typeof entity === 'function')
  209. entity = entity(obj);
  210. return entity._getEncoder('der').tree;
  211. };
  212. DERNode.prototype._skipDefault = function skipDefault(dataBuffer, reporter, parent) {
  213. var state = this._baseState;
  214. var i;
  215. if (state['default'] === null)
  216. return false;
  217. var data = dataBuffer.join();
  218. if (state.defaultBuffer === undefined)
  219. state.defaultBuffer = this._encodeValue(state['default'], reporter, parent).join();
  220. if (data.length !== state.defaultBuffer.length)
  221. return false;
  222. for (i=0; i < data.length; i++)
  223. if (data[i] !== state.defaultBuffer[i])
  224. return false;
  225. return true;
  226. };
  227. // Utility methods
  228. function encodeTag(tag, primitive, cls, reporter) {
  229. var res;
  230. if (tag === 'seqof')
  231. tag = 'seq';
  232. else if (tag === 'setof')
  233. tag = 'set';
  234. if (der.tagByName.hasOwnProperty(tag))
  235. res = der.tagByName[tag];
  236. else if (typeof tag === 'number' && (tag | 0) === tag)
  237. res = tag;
  238. else
  239. return reporter.error('Unknown tag: ' + tag);
  240. if (res >= 0x1f)
  241. return reporter.error('Multi-octet tag encoding unsupported');
  242. if (!primitive)
  243. res |= 0x20;
  244. res |= (der.tagClassByName[cls || 'universal'] << 6);
  245. return res;
  246. }