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.

634 lines
16 KiB

4 years ago
  1. var Reporter = require('../base').Reporter;
  2. var EncoderBuffer = require('../base').EncoderBuffer;
  3. var DecoderBuffer = require('../base').DecoderBuffer;
  4. var assert = require('minimalistic-assert');
  5. // Supported tags
  6. var tags = [
  7. 'seq', 'seqof', 'set', 'setof', 'objid', 'bool',
  8. 'gentime', 'utctime', 'null_', 'enum', 'int', 'objDesc',
  9. 'bitstr', 'bmpstr', 'charstr', 'genstr', 'graphstr', 'ia5str', 'iso646str',
  10. 'numstr', 'octstr', 'printstr', 't61str', 'unistr', 'utf8str', 'videostr'
  11. ];
  12. // Public methods list
  13. var methods = [
  14. 'key', 'obj', 'use', 'optional', 'explicit', 'implicit', 'def', 'choice',
  15. 'any', 'contains'
  16. ].concat(tags);
  17. // Overrided methods list
  18. var overrided = [
  19. '_peekTag', '_decodeTag', '_use',
  20. '_decodeStr', '_decodeObjid', '_decodeTime',
  21. '_decodeNull', '_decodeInt', '_decodeBool', '_decodeList',
  22. '_encodeComposite', '_encodeStr', '_encodeObjid', '_encodeTime',
  23. '_encodeNull', '_encodeInt', '_encodeBool'
  24. ];
  25. function Node(enc, parent) {
  26. var state = {};
  27. this._baseState = state;
  28. state.enc = enc;
  29. state.parent = parent || null;
  30. state.children = null;
  31. // State
  32. state.tag = null;
  33. state.args = null;
  34. state.reverseArgs = null;
  35. state.choice = null;
  36. state.optional = false;
  37. state.any = false;
  38. state.obj = false;
  39. state.use = null;
  40. state.useDecoder = null;
  41. state.key = null;
  42. state['default'] = null;
  43. state.explicit = null;
  44. state.implicit = null;
  45. state.contains = null;
  46. // Should create new instance on each method
  47. if (!state.parent) {
  48. state.children = [];
  49. this._wrap();
  50. }
  51. }
  52. module.exports = Node;
  53. var stateProps = [
  54. 'enc', 'parent', 'children', 'tag', 'args', 'reverseArgs', 'choice',
  55. 'optional', 'any', 'obj', 'use', 'alteredUse', 'key', 'default', 'explicit',
  56. 'implicit', 'contains'
  57. ];
  58. Node.prototype.clone = function clone() {
  59. var state = this._baseState;
  60. var cstate = {};
  61. stateProps.forEach(function(prop) {
  62. cstate[prop] = state[prop];
  63. });
  64. var res = new this.constructor(cstate.parent);
  65. res._baseState = cstate;
  66. return res;
  67. };
  68. Node.prototype._wrap = function wrap() {
  69. var state = this._baseState;
  70. methods.forEach(function(method) {
  71. this[method] = function _wrappedMethod() {
  72. var clone = new this.constructor(this);
  73. state.children.push(clone);
  74. return clone[method].apply(clone, arguments);
  75. };
  76. }, this);
  77. };
  78. Node.prototype._init = function init(body) {
  79. var state = this._baseState;
  80. assert(state.parent === null);
  81. body.call(this);
  82. // Filter children
  83. state.children = state.children.filter(function(child) {
  84. return child._baseState.parent === this;
  85. }, this);
  86. assert.equal(state.children.length, 1, 'Root node can have only one child');
  87. };
  88. Node.prototype._useArgs = function useArgs(args) {
  89. var state = this._baseState;
  90. // Filter children and args
  91. var children = args.filter(function(arg) {
  92. return arg instanceof this.constructor;
  93. }, this);
  94. args = args.filter(function(arg) {
  95. return !(arg instanceof this.constructor);
  96. }, this);
  97. if (children.length !== 0) {
  98. assert(state.children === null);
  99. state.children = children;
  100. // Replace parent to maintain backward link
  101. children.forEach(function(child) {
  102. child._baseState.parent = this;
  103. }, this);
  104. }
  105. if (args.length !== 0) {
  106. assert(state.args === null);
  107. state.args = args;
  108. state.reverseArgs = args.map(function(arg) {
  109. if (typeof arg !== 'object' || arg.constructor !== Object)
  110. return arg;
  111. var res = {};
  112. Object.keys(arg).forEach(function(key) {
  113. if (key == (key | 0))
  114. key |= 0;
  115. var value = arg[key];
  116. res[value] = key;
  117. });
  118. return res;
  119. });
  120. }
  121. };
  122. //
  123. // Overrided methods
  124. //
  125. overrided.forEach(function(method) {
  126. Node.prototype[method] = function _overrided() {
  127. var state = this._baseState;
  128. throw new Error(method + ' not implemented for encoding: ' + state.enc);
  129. };
  130. });
  131. //
  132. // Public methods
  133. //
  134. tags.forEach(function(tag) {
  135. Node.prototype[tag] = function _tagMethod() {
  136. var state = this._baseState;
  137. var args = Array.prototype.slice.call(arguments);
  138. assert(state.tag === null);
  139. state.tag = tag;
  140. this._useArgs(args);
  141. return this;
  142. };
  143. });
  144. Node.prototype.use = function use(item) {
  145. assert(item);
  146. var state = this._baseState;
  147. assert(state.use === null);
  148. state.use = item;
  149. return this;
  150. };
  151. Node.prototype.optional = function optional() {
  152. var state = this._baseState;
  153. state.optional = true;
  154. return this;
  155. };
  156. Node.prototype.def = function def(val) {
  157. var state = this._baseState;
  158. assert(state['default'] === null);
  159. state['default'] = val;
  160. state.optional = true;
  161. return this;
  162. };
  163. Node.prototype.explicit = function explicit(num) {
  164. var state = this._baseState;
  165. assert(state.explicit === null && state.implicit === null);
  166. state.explicit = num;
  167. return this;
  168. };
  169. Node.prototype.implicit = function implicit(num) {
  170. var state = this._baseState;
  171. assert(state.explicit === null && state.implicit === null);
  172. state.implicit = num;
  173. return this;
  174. };
  175. Node.prototype.obj = function obj() {
  176. var state = this._baseState;
  177. var args = Array.prototype.slice.call(arguments);
  178. state.obj = true;
  179. if (args.length !== 0)
  180. this._useArgs(args);
  181. return this;
  182. };
  183. Node.prototype.key = function key(newKey) {
  184. var state = this._baseState;
  185. assert(state.key === null);
  186. state.key = newKey;
  187. return this;
  188. };
  189. Node.prototype.any = function any() {
  190. var state = this._baseState;
  191. state.any = true;
  192. return this;
  193. };
  194. Node.prototype.choice = function choice(obj) {
  195. var state = this._baseState;
  196. assert(state.choice === null);
  197. state.choice = obj;
  198. this._useArgs(Object.keys(obj).map(function(key) {
  199. return obj[key];
  200. }));
  201. return this;
  202. };
  203. Node.prototype.contains = function contains(item) {
  204. var state = this._baseState;
  205. assert(state.use === null);
  206. state.contains = item;
  207. return this;
  208. };
  209. //
  210. // Decoding
  211. //
  212. Node.prototype._decode = function decode(input, options) {
  213. var state = this._baseState;
  214. // Decode root node
  215. if (state.parent === null)
  216. return input.wrapResult(state.children[0]._decode(input, options));
  217. var result = state['default'];
  218. var present = true;
  219. var prevKey = null;
  220. if (state.key !== null)
  221. prevKey = input.enterKey(state.key);
  222. // Check if tag is there
  223. if (state.optional) {
  224. var tag = null;
  225. if (state.explicit !== null)
  226. tag = state.explicit;
  227. else if (state.implicit !== null)
  228. tag = state.implicit;
  229. else if (state.tag !== null)
  230. tag = state.tag;
  231. if (tag === null && !state.any) {
  232. // Trial and Error
  233. var save = input.save();
  234. try {
  235. if (state.choice === null)
  236. this._decodeGeneric(state.tag, input, options);
  237. else
  238. this._decodeChoice(input, options);
  239. present = true;
  240. } catch (e) {
  241. present = false;
  242. }
  243. input.restore(save);
  244. } else {
  245. present = this._peekTag(input, tag, state.any);
  246. if (input.isError(present))
  247. return present;
  248. }
  249. }
  250. // Push object on stack
  251. var prevObj;
  252. if (state.obj && present)
  253. prevObj = input.enterObject();
  254. if (present) {
  255. // Unwrap explicit values
  256. if (state.explicit !== null) {
  257. var explicit = this._decodeTag(input, state.explicit);
  258. if (input.isError(explicit))
  259. return explicit;
  260. input = explicit;
  261. }
  262. var start = input.offset;
  263. // Unwrap implicit and normal values
  264. if (state.use === null && state.choice === null) {
  265. if (state.any)
  266. var save = input.save();
  267. var body = this._decodeTag(
  268. input,
  269. state.implicit !== null ? state.implicit : state.tag,
  270. state.any
  271. );
  272. if (input.isError(body))
  273. return body;
  274. if (state.any)
  275. result = input.raw(save);
  276. else
  277. input = body;
  278. }
  279. if (options && options.track && state.tag !== null)
  280. options.track(input.path(), start, input.length, 'tagged');
  281. if (options && options.track && state.tag !== null)
  282. options.track(input.path(), input.offset, input.length, 'content');
  283. // Select proper method for tag
  284. if (state.any)
  285. result = result;
  286. else if (state.choice === null)
  287. result = this._decodeGeneric(state.tag, input, options);
  288. else
  289. result = this._decodeChoice(input, options);
  290. if (input.isError(result))
  291. return result;
  292. // Decode children
  293. if (!state.any && state.choice === null && state.children !== null) {
  294. state.children.forEach(function decodeChildren(child) {
  295. // NOTE: We are ignoring errors here, to let parser continue with other
  296. // parts of encoded data
  297. child._decode(input, options);
  298. });
  299. }
  300. // Decode contained/encoded by schema, only in bit or octet strings
  301. if (state.contains && (state.tag === 'octstr' || state.tag === 'bitstr')) {
  302. var data = new DecoderBuffer(result);
  303. result = this._getUse(state.contains, input._reporterState.obj)
  304. ._decode(data, options);
  305. }
  306. }
  307. // Pop object
  308. if (state.obj && present)
  309. result = input.leaveObject(prevObj);
  310. // Set key
  311. if (state.key !== null && (result !== null || present === true))
  312. input.leaveKey(prevKey, state.key, result);
  313. else if (prevKey !== null)
  314. input.exitKey(prevKey);
  315. return result;
  316. };
  317. Node.prototype._decodeGeneric = function decodeGeneric(tag, input, options) {
  318. var state = this._baseState;
  319. if (tag === 'seq' || tag === 'set')
  320. return null;
  321. if (tag === 'seqof' || tag === 'setof')
  322. return this._decodeList(input, tag, state.args[0], options);
  323. else if (/str$/.test(tag))
  324. return this._decodeStr(input, tag, options);
  325. else if (tag === 'objid' && state.args)
  326. return this._decodeObjid(input, state.args[0], state.args[1], options);
  327. else if (tag === 'objid')
  328. return this._decodeObjid(input, null, null, options);
  329. else if (tag === 'gentime' || tag === 'utctime')
  330. return this._decodeTime(input, tag, options);
  331. else if (tag === 'null_')
  332. return this._decodeNull(input, options);
  333. else if (tag === 'bool')
  334. return this._decodeBool(input, options);
  335. else if (tag === 'objDesc')
  336. return this._decodeStr(input, tag, options);
  337. else if (tag === 'int' || tag === 'enum')
  338. return this._decodeInt(input, state.args && state.args[0], options);
  339. if (state.use !== null) {
  340. return this._getUse(state.use, input._reporterState.obj)
  341. ._decode(input, options);
  342. } else {
  343. return input.error('unknown tag: ' + tag);
  344. }
  345. };
  346. Node.prototype._getUse = function _getUse(entity, obj) {
  347. var state = this._baseState;
  348. // Create altered use decoder if implicit is set
  349. state.useDecoder = this._use(entity, obj);
  350. assert(state.useDecoder._baseState.parent === null);
  351. state.useDecoder = state.useDecoder._baseState.children[0];
  352. if (state.implicit !== state.useDecoder._baseState.implicit) {
  353. state.useDecoder = state.useDecoder.clone();
  354. state.useDecoder._baseState.implicit = state.implicit;
  355. }
  356. return state.useDecoder;
  357. };
  358. Node.prototype._decodeChoice = function decodeChoice(input, options) {
  359. var state = this._baseState;
  360. var result = null;
  361. var match = false;
  362. Object.keys(state.choice).some(function(key) {
  363. var save = input.save();
  364. var node = state.choice[key];
  365. try {
  366. var value = node._decode(input, options);
  367. if (input.isError(value))
  368. return false;
  369. result = { type: key, value: value };
  370. match = true;
  371. } catch (e) {
  372. input.restore(save);
  373. return false;
  374. }
  375. return true;
  376. }, this);
  377. if (!match)
  378. return input.error('Choice not matched');
  379. return result;
  380. };
  381. //
  382. // Encoding
  383. //
  384. Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) {
  385. return new EncoderBuffer(data, this.reporter);
  386. };
  387. Node.prototype._encode = function encode(data, reporter, parent) {
  388. var state = this._baseState;
  389. if (state['default'] !== null && state['default'] === data)
  390. return;
  391. var result = this._encodeValue(data, reporter, parent);
  392. if (result === undefined)
  393. return;
  394. if (this._skipDefault(result, reporter, parent))
  395. return;
  396. return result;
  397. };
  398. Node.prototype._encodeValue = function encode(data, reporter, parent) {
  399. var state = this._baseState;
  400. // Decode root node
  401. if (state.parent === null)
  402. return state.children[0]._encode(data, reporter || new Reporter());
  403. var result = null;
  404. // Set reporter to share it with a child class
  405. this.reporter = reporter;
  406. // Check if data is there
  407. if (state.optional && data === undefined) {
  408. if (state['default'] !== null)
  409. data = state['default']
  410. else
  411. return;
  412. }
  413. // Encode children first
  414. var content = null;
  415. var primitive = false;
  416. if (state.any) {
  417. // Anything that was given is translated to buffer
  418. result = this._createEncoderBuffer(data);
  419. } else if (state.choice) {
  420. result = this._encodeChoice(data, reporter);
  421. } else if (state.contains) {
  422. content = this._getUse(state.contains, parent)._encode(data, reporter);
  423. primitive = true;
  424. } else if (state.children) {
  425. content = state.children.map(function(child) {
  426. if (child._baseState.tag === 'null_')
  427. return child._encode(null, reporter, data);
  428. if (child._baseState.key === null)
  429. return reporter.error('Child should have a key');
  430. var prevKey = reporter.enterKey(child._baseState.key);
  431. if (typeof data !== 'object')
  432. return reporter.error('Child expected, but input is not object');
  433. var res = child._encode(data[child._baseState.key], reporter, data);
  434. reporter.leaveKey(prevKey);
  435. return res;
  436. }, this).filter(function(child) {
  437. return child;
  438. });
  439. content = this._createEncoderBuffer(content);
  440. } else {
  441. if (state.tag === 'seqof' || state.tag === 'setof') {
  442. // TODO(indutny): this should be thrown on DSL level
  443. if (!(state.args && state.args.length === 1))
  444. return reporter.error('Too many args for : ' + state.tag);
  445. if (!Array.isArray(data))
  446. return reporter.error('seqof/setof, but data is not Array');
  447. var child = this.clone();
  448. child._baseState.implicit = null;
  449. content = this._createEncoderBuffer(data.map(function(item) {
  450. var state = this._baseState;
  451. return this._getUse(state.args[0], data)._encode(item, reporter);
  452. }, child));
  453. } else if (state.use !== null) {
  454. result = this._getUse(state.use, parent)._encode(data, reporter);
  455. } else {
  456. content = this._encodePrimitive(state.tag, data);
  457. primitive = true;
  458. }
  459. }
  460. // Encode data itself
  461. var result;
  462. if (!state.any && state.choice === null) {
  463. var tag = state.implicit !== null ? state.implicit : state.tag;
  464. var cls = state.implicit === null ? 'universal' : 'context';
  465. if (tag === null) {
  466. if (state.use === null)
  467. reporter.error('Tag could be omitted only for .use()');
  468. } else {
  469. if (state.use === null)
  470. result = this._encodeComposite(tag, primitive, cls, content);
  471. }
  472. }
  473. // Wrap in explicit
  474. if (state.explicit !== null)
  475. result = this._encodeComposite(state.explicit, false, 'context', result);
  476. return result;
  477. };
  478. Node.prototype._encodeChoice = function encodeChoice(data, reporter) {
  479. var state = this._baseState;
  480. var node = state.choice[data.type];
  481. if (!node) {
  482. assert(
  483. false,
  484. data.type + ' not found in ' +
  485. JSON.stringify(Object.keys(state.choice)));
  486. }
  487. return node._encode(data.value, reporter);
  488. };
  489. Node.prototype._encodePrimitive = function encodePrimitive(tag, data) {
  490. var state = this._baseState;
  491. if (/str$/.test(tag))
  492. return this._encodeStr(data, tag);
  493. else if (tag === 'objid' && state.args)
  494. return this._encodeObjid(data, state.reverseArgs[0], state.args[1]);
  495. else if (tag === 'objid')
  496. return this._encodeObjid(data, null, null);
  497. else if (tag === 'gentime' || tag === 'utctime')
  498. return this._encodeTime(data, tag);
  499. else if (tag === 'null_')
  500. return this._encodeNull();
  501. else if (tag === 'int' || tag === 'enum')
  502. return this._encodeInt(data, state.args && state.reverseArgs[0]);
  503. else if (tag === 'bool')
  504. return this._encodeBool(data);
  505. else if (tag === 'objDesc')
  506. return this._encodeStr(data, tag);
  507. else
  508. throw new Error('Unsupported tag: ' + tag);
  509. };
  510. Node.prototype._isNumstr = function isNumstr(str) {
  511. return /^[0-9 ]*$/.test(str);
  512. };
  513. Node.prototype._isPrintstr = function isPrintstr(str) {
  514. return /^[A-Za-z0-9 '\(\)\+,\-\.\/:=\?]*$/.test(str);
  515. };