asn1.js 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. // **Github:** https://github.com/fidm/asn1
  4. //
  5. // **License:** MIT
  6. const util_1 = require("util");
  7. const common_1 = require("./common");
  8. /**
  9. * ASN.1 classes.
  10. */
  11. var Class;
  12. (function (Class) {
  13. Class[Class["UNIVERSAL"] = 0] = "UNIVERSAL";
  14. Class[Class["APPLICATION"] = 64] = "APPLICATION";
  15. Class[Class["CONTEXT_SPECIFIC"] = 128] = "CONTEXT_SPECIFIC";
  16. Class[Class["PRIVATE"] = 192] = "PRIVATE";
  17. })(Class = exports.Class || (exports.Class = {}));
  18. /**
  19. * ASN.1 types. Not all types are supported by this implementation.
  20. */
  21. var Tag;
  22. (function (Tag) {
  23. Tag[Tag["NONE"] = 0] = "NONE";
  24. Tag[Tag["BOOLEAN"] = 1] = "BOOLEAN";
  25. Tag[Tag["INTEGER"] = 2] = "INTEGER";
  26. Tag[Tag["BITSTRING"] = 3] = "BITSTRING";
  27. Tag[Tag["OCTETSTRING"] = 4] = "OCTETSTRING";
  28. Tag[Tag["NULL"] = 5] = "NULL";
  29. Tag[Tag["OID"] = 6] = "OID";
  30. // ODESC = 7,
  31. // EXTERNAL = 8,
  32. // REAL = 9,
  33. Tag[Tag["ENUMERATED"] = 10] = "ENUMERATED";
  34. // EMBEDDED = 11,
  35. Tag[Tag["UTF8"] = 12] = "UTF8";
  36. // ROID = 13,
  37. Tag[Tag["SEQUENCE"] = 16] = "SEQUENCE";
  38. Tag[Tag["SET"] = 17] = "SET";
  39. Tag[Tag["NUMERICSTRING"] = 18] = "NUMERICSTRING";
  40. Tag[Tag["PRINTABLESTRING"] = 19] = "PRINTABLESTRING";
  41. Tag[Tag["T61STRING"] = 20] = "T61STRING";
  42. Tag[Tag["IA5STRING"] = 22] = "IA5STRING";
  43. Tag[Tag["UTCTIME"] = 23] = "UTCTIME";
  44. Tag[Tag["GENERALIZEDTIME"] = 24] = "GENERALIZEDTIME";
  45. Tag[Tag["GENERALSTRING"] = 27] = "GENERALSTRING";
  46. })(Tag = exports.Tag || (exports.Tag = {}));
  47. /**
  48. * BitString is the structure to use when you want an ASN.1 BIT STRING type. A
  49. * bit string is padded up to the nearest byte in memory and the number of
  50. * valid bits is recorded. Padding bits will be zero.
  51. */
  52. class BitString {
  53. constructor(buf, bitLen) {
  54. this.buf = buf;
  55. this.bitLen = bitLen;
  56. }
  57. /**
  58. * Returns the value for the given bits offset.
  59. * @param i bits offet
  60. */
  61. at(i) {
  62. if (i < 0 || i >= this.bitLen || !Number.isInteger(i)) {
  63. return 0;
  64. }
  65. const x = Math.floor(i / 8);
  66. const y = 7 - i % 8;
  67. return (this.buf[x] >> y) & 1;
  68. }
  69. /**
  70. * Align buffer
  71. */
  72. rightAlign() {
  73. const shift = 8 - (this.bitLen % 8);
  74. if (shift === 8 || this.buf.length === 0) {
  75. return this.buf;
  76. }
  77. const buf = Buffer.alloc(this.buf.length);
  78. buf[0] = this.buf[0] >> shift;
  79. for (let i = 1; i < this.buf.length; i++) {
  80. buf[i] = this.buf[i - 1] << (8 - shift);
  81. buf[i] |= this.buf[i] >> shift;
  82. }
  83. return buf;
  84. }
  85. }
  86. exports.BitString = BitString;
  87. /**
  88. * Implements parsing of DER-encoded ASN.1 data structures,
  89. * as defined in ITU-T Rec X.690.
  90. *
  91. * See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,''
  92. * http://luca.ntop.org/Teaching/Appunti/asn1.html.
  93. *
  94. * ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc
  95. * are different encoding formats for those objects. Here, we'll be dealing
  96. * with DER, the Distinguished Encoding Rules. DER is used in X.509 because
  97. * it's fast to parse and, unlike BER, has a unique encoding for every object.
  98. * When calculating hashes over objects, it's important that the resulting
  99. * bytes be the same at both ends and DER removes this margin of error.
  100. * ASN.1 is very complex and this package doesn't attempt to implement
  101. * everything by any means.
  102. *
  103. * DER Encoding of ASN.1 Types:
  104. * https://msdn.microsoft.com/en-us/library/windows/desktop/bb540792(v=vs.85).aspx
  105. */
  106. class ASN1 {
  107. /**
  108. * Creates a Tag.BOOLEAN ASN.1 object.
  109. * @param val boolean value.
  110. */
  111. static Bool(val) {
  112. const asn1 = new ASN1(Class.UNIVERSAL, Tag.BOOLEAN, Buffer.from([val ? 0xff : 0x0]));
  113. asn1._value = val;
  114. return asn1;
  115. }
  116. /**
  117. * Parse a Tag.BOOLEAN value from ASN.1 object' value.
  118. * @param buf the buffer to parse.
  119. */
  120. static parseBool(buf) {
  121. if (!(buf instanceof Buffer) || buf.length !== 1) {
  122. throw new Error('ASN1 syntax error: invalid boolean');
  123. }
  124. switch (buf[0]) {
  125. case 0:
  126. return false;
  127. case 0xff:
  128. return true;
  129. default:
  130. throw new Error('ASN1 syntax error: invalid boolean');
  131. }
  132. }
  133. /**
  134. * Creates a Tag.INTEGER ASN.1 object.
  135. * @param val integer value or buffer.
  136. */
  137. static Integer(val) {
  138. if (val instanceof Buffer) {
  139. const asn = new ASN1(Class.UNIVERSAL, Tag.INTEGER, val);
  140. asn._value = val.toString('hex');
  141. return asn;
  142. }
  143. if (!Number.isSafeInteger(val)) {
  144. throw new Error('ASN1 syntax error: invalid integer');
  145. }
  146. let buf;
  147. if (val >= -0x80 && val < 0x80) {
  148. buf = Buffer.alloc(1);
  149. buf.writeInt8(val, 0);
  150. }
  151. else if (val >= -0x8000 && val < 0x8000) {
  152. buf = Buffer.alloc(2);
  153. buf.writeIntBE(val, 0, 2);
  154. }
  155. else if (val >= -0x800000 && val < 0x800000) {
  156. buf = Buffer.alloc(3);
  157. buf.writeIntBE(val, 0, 3);
  158. }
  159. else if (val >= -0x80000000 && val < 0x80000000) {
  160. buf = Buffer.alloc(4);
  161. buf.writeIntBE(val, 0, 4);
  162. }
  163. else if (val >= -0x8000000000 && val < 0x8000000000) {
  164. buf = Buffer.alloc(5);
  165. buf.writeIntBE(val, 0, 5);
  166. }
  167. else if (val >= -0x800000000000 && val < 0x800000000000) {
  168. buf = Buffer.alloc(6);
  169. buf.writeIntBE(val, 0, 6);
  170. }
  171. else {
  172. throw new Error('ASN1 syntax error: invalid Integer');
  173. }
  174. const asn1 = new ASN1(Class.UNIVERSAL, Tag.INTEGER, buf);
  175. asn1._value = val;
  176. return asn1;
  177. }
  178. /**
  179. * Parse a Tag.INTEGER value from ASN.1 object' value.
  180. * @param buf the buffer to parse.
  181. */
  182. static parseInteger(buf) {
  183. if (!(buf instanceof Buffer) || buf.length === 0) {
  184. throw new Error('ASN1 syntax error: invalid Integer');
  185. }
  186. // some INTEGER (BigInt) will be 16 bytes, 32 bytes or others.
  187. // CertificateSerialNumber ::= INTEGER (>= 16 bytes)
  188. if (buf.length > 6) {
  189. return buf.toString('hex');
  190. }
  191. return buf.readIntBE(0, buf.length);
  192. }
  193. /**
  194. * Parse a Tag.INTEGER value as a number from ASN.1 object' value.
  195. * @param buf the buffer to parse.
  196. */
  197. static parseIntegerNum(buf) {
  198. const value = ASN1.parseInteger(buf);
  199. if (typeof value !== 'number') {
  200. throw new Error('ASN1 syntax error: invalid Integer number');
  201. }
  202. return value;
  203. }
  204. /**
  205. * Parse a Tag.INTEGER value as a hex string(for BigInt) from ASN.1 object' value.
  206. * @param buf the buffer to parse.
  207. */
  208. static parseIntegerStr(buf) {
  209. const value = ASN1.parseInteger(buf);
  210. if (typeof value === 'number') {
  211. return value.toString(16);
  212. }
  213. return value;
  214. }
  215. /**
  216. * Creates a Tag.BITSTRING ASN.1 object.
  217. * @param val BitString object or buffer.
  218. */
  219. static BitString(val) {
  220. if (val instanceof Buffer) {
  221. val = new BitString(val, val.length * 8);
  222. }
  223. const paddingBits = val.buf.length * 8 - val.bitLen;
  224. const buf = Buffer.alloc(val.buf.length + 1);
  225. buf.writeInt8(paddingBits, 0);
  226. val.buf.copy(buf, 1);
  227. return new ASN1(Class.UNIVERSAL, Tag.BITSTRING, buf);
  228. }
  229. /**
  230. * Parse a Tag.BITSTRING value from ASN.1 object' value.
  231. * @param buf the buffer to parse.
  232. */
  233. static parseBitString(buf) {
  234. if (!(buf instanceof Buffer) || buf.length === 0) {
  235. throw new Error('ASN1 syntax error: invalid BitString');
  236. }
  237. const paddingBits = buf[0];
  238. if (paddingBits > 7 ||
  239. buf.length === 1 && paddingBits > 0 ||
  240. (buf[buf.length - 1] & ((1 << buf[0]) - 1)) !== 0) {
  241. throw new Error('ASN1 syntax error: invalid padding bits in BIT STRING');
  242. }
  243. return new BitString(buf.slice(1), (buf.length - 1) * 8 - paddingBits);
  244. }
  245. /**
  246. * Creates a Tag.NULL ASN.1 object.
  247. */
  248. static Null() {
  249. const asn1 = new ASN1(Class.UNIVERSAL, Tag.NULL, Buffer.alloc(0));
  250. asn1._value = null;
  251. return asn1;
  252. }
  253. /**
  254. * Parse a Tag.NULL value from ASN.1 object' value.
  255. * @param buf the buffer to parse.
  256. */
  257. static parseNull(buf) {
  258. if (!(buf instanceof Buffer) || buf.length !== 0) {
  259. throw new Error('ASN1 syntax error: invalid null');
  260. }
  261. return null;
  262. }
  263. /**
  264. * Creates an Tag.OID (dot-separated numeric string) ASN.1 object.
  265. * @param val dot-separated numeric string.
  266. */
  267. static OID(val) {
  268. const values = val.split('.');
  269. if (values.length === 0) {
  270. throw new Error('ASN1 syntax error: invalid Object Identifier');
  271. }
  272. const bytes = [];
  273. // first byte is 40 * value1 + value2
  274. bytes.push(40 * mustParseInt(values[0]) + mustParseInt(values[1]));
  275. // other bytes are each value in base 128 with 8th bit set except for
  276. // the last byte for each value
  277. const valueBytes = [];
  278. for (let i = 2; i < values.length; ++i) {
  279. let value = mustParseInt(values[i]);
  280. valueBytes.length = 0;
  281. valueBytes.push(value & 0x7f);
  282. while (value > 0x7f) {
  283. value = value >>> 7;
  284. valueBytes.unshift((value & 0x7f) | 0x80); // add value bytes in reverse for big endian
  285. }
  286. bytes.push(...valueBytes);
  287. }
  288. const asn1 = new ASN1(Class.UNIVERSAL, Tag.OID, Buffer.from(bytes));
  289. asn1._value = val;
  290. return asn1;
  291. }
  292. /**
  293. * Parse a Tag.OID value from ASN.1 object' value.
  294. * @param buf the buffer to parse.
  295. */
  296. static parseOID(buf) {
  297. if (!(buf instanceof Buffer) || buf.length === 0) {
  298. throw new Error('ASN1 syntax error: invalid OID');
  299. }
  300. // first byte is 40 * value1 + value2
  301. let oid = Math.floor(buf[0] / 40) + '.' + (buf[0] % 40);
  302. // other bytes are each value in base 128 with 8th bit set except for
  303. // the last byte for each value
  304. let high = 0;
  305. for (let i = 1; i < buf.length; i++) {
  306. // not the last byte for the value
  307. if (buf[i] >= 0x80) {
  308. high += buf[i] & 0x7F;
  309. high = high << 7;
  310. }
  311. else {
  312. oid += '.' + (high + buf[i]);
  313. high = 0;
  314. }
  315. }
  316. return oid;
  317. }
  318. /**
  319. * Creates an Tag.UTF8 ASN.1 object.
  320. * @param val utf8 string.
  321. */
  322. static UTF8(val) {
  323. const asn1 = new ASN1(Class.UNIVERSAL, Tag.UTF8, Buffer.from(val, 'utf8'));
  324. asn1._value = val;
  325. return asn1;
  326. }
  327. /**
  328. * Parse a Tag.UTF8 string from ASN.1 object' value.
  329. * @param buf the buffer to parse.
  330. */
  331. static parseUTF8(buf) {
  332. if (!(buf instanceof Buffer)) {
  333. throw new Error('parse ASN1 error: invalid Buffer');
  334. }
  335. return buf.toString('utf8');
  336. }
  337. /**
  338. * Creates an Tag.NUMERICSTRING ASN.1 object.
  339. * @param val numeric string.
  340. */
  341. static NumericString(val) {
  342. if (!isNumericString(val)) {
  343. throw new Error('ASN1 syntax error: invalid NumericString');
  344. }
  345. const asn1 = new ASN1(Class.UNIVERSAL, Tag.NUMERICSTRING, Buffer.from(val, 'utf8'));
  346. asn1._value = val;
  347. return asn1;
  348. }
  349. /**
  350. * Parse a Tag.UTF8 string from ASN.1 object' value.
  351. * @param buf the buffer to parse.
  352. */
  353. static parseNumericString(buf) {
  354. if (!(buf instanceof Buffer)) {
  355. throw new Error('parse ASN1 error: invalid Buffer');
  356. }
  357. const str = buf.toString('utf8');
  358. if (!isNumericString(str)) {
  359. throw new Error('ASN1 syntax error: invalid NumericString');
  360. }
  361. return str;
  362. }
  363. /**
  364. * Creates an Tag.NUMERICSTRING ASN.1 object.
  365. * @param val printable string.
  366. */
  367. static PrintableString(val) {
  368. // TODO, validate
  369. const asn1 = new ASN1(Class.UNIVERSAL, Tag.PRINTABLESTRING, Buffer.from(val, 'utf8'));
  370. asn1._value = val;
  371. return asn1;
  372. }
  373. /**
  374. * Parse a Tag.PRINTABLESTRING string from ASN.1 object' value.
  375. * @param buf the buffer to parse.
  376. */
  377. static parsePrintableString(buf) {
  378. if (!(buf instanceof Buffer)) {
  379. throw new Error('parse ASN1 error: invalid Buffer');
  380. }
  381. // TODO, validate
  382. return buf.toString('utf8');
  383. }
  384. /**
  385. * Creates an Tag.IA5STRING (ASCII string) ASN.1 object.
  386. * @param val ASCII string.
  387. */
  388. static IA5String(val) {
  389. if (!isIA5String(val)) {
  390. throw new Error('ASN1 syntax error: invalid IA5String');
  391. }
  392. const asn1 = new ASN1(Class.UNIVERSAL, Tag.IA5STRING, Buffer.from(val, 'utf8'));
  393. asn1._value = val;
  394. return asn1;
  395. }
  396. /**
  397. * Parse a Tag.IA5STRING string from ASN.1 object' value.
  398. * @param buf the buffer to parse.
  399. */
  400. static parseIA5String(buf) {
  401. if (!(buf instanceof Buffer)) {
  402. throw new Error('parse ASN1 error: invalid Buffer');
  403. }
  404. const str = buf.toString('utf8');
  405. if (!isIA5String(str)) {
  406. throw new Error('ASN1 syntax error: invalid IA5String');
  407. }
  408. return str;
  409. }
  410. /**
  411. * Creates an Tag.T61STRING (8-bit clean string) ASN.1 object.
  412. * @param val 8-bit clean string.
  413. */
  414. static T61String(val) {
  415. // TODO, validate
  416. const asn1 = new ASN1(Class.UNIVERSAL, Tag.T61STRING, Buffer.from(val, 'utf8'));
  417. asn1._value = val;
  418. return asn1;
  419. }
  420. /**
  421. * Parse a Tag.T61STRING string from ASN.1 object' value.
  422. * @param buf the buffer to parse.
  423. */
  424. static parseT61String(buf) {
  425. if (!(buf instanceof Buffer)) {
  426. throw new Error('parse ASN1 error: invalid Buffer');
  427. }
  428. // TODO, validate
  429. return buf.toString('utf8');
  430. }
  431. /**
  432. * Creates an Tag.GENERALSTRING (specified in ISO-2022/ECMA-35) ASN.1 object.
  433. * @param val general string.
  434. */
  435. static GeneralString(val) {
  436. // TODO, validate
  437. const asn1 = new ASN1(Class.UNIVERSAL, Tag.GENERALSTRING, Buffer.from(val, 'utf8'));
  438. asn1._value = val;
  439. return asn1;
  440. }
  441. /**
  442. * Parse a Tag.GENERALSTRING string from ASN.1 object' value.
  443. * @param buf the buffer to parse.
  444. */
  445. static parseGeneralString(buf) {
  446. if (!(buf instanceof Buffer)) {
  447. throw new Error('parse ASN1 error: invalid Buffer');
  448. }
  449. // TODO, validate
  450. return buf.toString('utf8');
  451. }
  452. /**
  453. * Creates an Tag.UTCTIME ASN.1 object.
  454. *
  455. * Note: GeneralizedTime has 4 digits for the year and is used for X.509.
  456. * dates past 2049. Converting to a GeneralizedTime hasn't been implemented yet.
  457. * @param date date value.
  458. */
  459. static UTCTime(date) {
  460. let rval = '';
  461. // create format YYMMDDhhmmssZ
  462. const format = [];
  463. format.push(('' + date.getUTCFullYear()).substr(2));
  464. format.push('' + (date.getUTCMonth() + 1));
  465. format.push('' + date.getUTCDate());
  466. format.push('' + date.getUTCHours());
  467. format.push('' + date.getUTCMinutes());
  468. format.push('' + date.getUTCSeconds());
  469. // ensure 2 digits are used for each format entry
  470. for (const s of format) {
  471. if (s.length < 2) {
  472. rval += '0';
  473. }
  474. rval += s;
  475. }
  476. rval += 'Z';
  477. const asn1 = new ASN1(Class.UNIVERSAL, Tag.UTCTIME, Buffer.from(rval, 'utf8'));
  478. asn1._value = date;
  479. return asn1;
  480. }
  481. /**
  482. * Parse a Tag.UTCTIME date from ASN.1 object' value.
  483. * @param buf the buffer to parse.
  484. */
  485. static parseUTCTime(buf) {
  486. if (!(buf instanceof Buffer) || buf.length === 0) {
  487. throw new Error('ASN1 syntax error: invalid UTC Time');
  488. }
  489. const utc = buf.toString('utf8');
  490. /* The following formats can be used:
  491. YYMMDDhhmmZ
  492. YYMMDDhhmm+hh'mm'
  493. YYMMDDhhmm-hh'mm'
  494. YYMMDDhhmmssZ
  495. YYMMDDhhmmss+hh'mm'
  496. YYMMDDhhmmss-hh'mm'
  497. Where:
  498. YY is the least significant two digits of the year
  499. MM is the month (01 to 12)
  500. DD is the day (01 to 31)
  501. hh is the hour (00 to 23)
  502. mm are the minutes (00 to 59)
  503. ss are the seconds (00 to 59)
  504. Z indicates that local time is GMT, + indicates that local time is
  505. later than GMT, and - indicates that local time is earlier than GMT
  506. hh' is the absolute value of the offset from GMT in hours
  507. mm' is the absolute value of the offset from GMT in minutes */
  508. const date = new Date();
  509. // if YY >= 50 use 19xx, if YY < 50 use 20xx
  510. let year = mustParseInt(utc.substr(0, 2));
  511. year = (year >= 50) ? 1900 + year : 2000 + year;
  512. const MM = mustParseInt(utc.substr(2, 2)) - 1; // use 0-11 for month
  513. const DD = mustParseInt(utc.substr(4, 2));
  514. const hh = mustParseInt(utc.substr(6, 2));
  515. const mm = mustParseInt(utc.substr(8, 2));
  516. let ss = 0;
  517. let end = 0;
  518. // get character after minutes
  519. let c = '';
  520. // not just YYMMDDhhmmZ
  521. if (utc.length > 11) {
  522. end = 10;
  523. // get character after minutes
  524. c = utc.charAt(end);
  525. // see if seconds are present
  526. if (c !== '+' && c !== '-') {
  527. // get seconds
  528. ss = mustParseInt(utc.substr(10, 2));
  529. end += 2;
  530. }
  531. }
  532. // update date
  533. date.setUTCFullYear(year, MM, DD);
  534. date.setUTCHours(hh, mm, ss, 0);
  535. if (end > 0) {
  536. // get +/- after end of time
  537. c = utc.charAt(end);
  538. if (c === '+' || c === '-') {
  539. // get hours+minutes offset
  540. const hhoffset = mustParseInt(utc.substr(end + 1, 2));
  541. const mmoffset = mustParseInt(utc.substr(end + 4, 2));
  542. // calculate offset in milliseconds
  543. let offset = hhoffset * 60 + mmoffset;
  544. offset *= 60000;
  545. // apply offset
  546. if (c === '+') {
  547. date.setTime(+date - offset);
  548. }
  549. else {
  550. date.setTime(+date + offset);
  551. }
  552. }
  553. }
  554. return date;
  555. }
  556. /**
  557. * Creates an Tag.GENERALIZEDTIME ASN.1 object.
  558. * @param date date value.
  559. */
  560. static GeneralizedTime(date) {
  561. let rval = '';
  562. // create format YYYYMMDDHHMMSSZ
  563. const format = [];
  564. format.push('' + date.getUTCFullYear());
  565. format.push('' + (date.getUTCMonth() + 1));
  566. format.push('' + date.getUTCDate());
  567. format.push('' + date.getUTCHours());
  568. format.push('' + date.getUTCMinutes());
  569. format.push('' + date.getUTCSeconds());
  570. // ensure 2 digits are used for each format entry
  571. for (const s of format) {
  572. if (s.length < 2) {
  573. rval += '0';
  574. }
  575. rval += s;
  576. }
  577. rval += 'Z';
  578. const asn1 = new ASN1(Class.UNIVERSAL, Tag.GENERALIZEDTIME, Buffer.from(rval, 'utf8'));
  579. asn1._value = date;
  580. return asn1;
  581. }
  582. /**
  583. * Parse a Tag.GENERALIZEDTIME date from ASN.1 object' value.
  584. * @param buf the buffer to parse.
  585. */
  586. static parseGeneralizedTime(buf) {
  587. if (!(buf instanceof Buffer) || buf.length === 0) {
  588. throw new Error('ASN1 syntax error: invalid Generalized Time');
  589. }
  590. const gentime = buf.toString('utf8');
  591. /* The following formats can be used:
  592. YYYYMMDDHHMMSS
  593. YYYYMMDDHHMMSS.fff
  594. YYYYMMDDHHMMSSZ
  595. YYYYMMDDHHMMSS.fffZ
  596. YYYYMMDDHHMMSS+hh'mm'
  597. YYYYMMDDHHMMSS.fff+hh'mm'
  598. YYYYMMDDHHMMSS-hh'mm'
  599. YYYYMMDDHHMMSS.fff-hh'mm'
  600. Where:
  601. YYYY is the year
  602. MM is the month (01 to 12)
  603. DD is the day (01 to 31)
  604. hh is the hour (00 to 23)
  605. mm are the minutes (00 to 59)
  606. ss are the seconds (00 to 59)
  607. .fff is the second fraction, accurate to three decimal places
  608. Z indicates that local time is GMT, + indicates that local time is
  609. later than GMT, and - indicates that local time is earlier than GMT
  610. hh' is the absolute value of the offset from GMT in hours
  611. mm' is the absolute value of the offset from GMT in minutes */
  612. const date = new Date();
  613. const YYYY = mustParseInt(gentime.substr(0, 4));
  614. const MM = mustParseInt(gentime.substr(4, 2)) - 1; // use 0-11 for month
  615. const DD = mustParseInt(gentime.substr(6, 2));
  616. const hh = mustParseInt(gentime.substr(8, 2));
  617. const mm = mustParseInt(gentime.substr(10, 2));
  618. const ss = mustParseInt(gentime.substr(12, 2));
  619. let fff = 0;
  620. let offset = 0;
  621. let isUTC = false;
  622. if (gentime.charAt(gentime.length - 1) === 'Z') {
  623. isUTC = true;
  624. }
  625. const end = gentime.length - 5;
  626. const c = gentime.charAt(end);
  627. if (c === '+' || c === '-') {
  628. // get hours+minutes offset
  629. const hhoffset = mustParseInt(gentime.substr(end + 1, 2));
  630. const mmoffset = mustParseInt(gentime.substr(end + 4, 2));
  631. // calculate offset in milliseconds
  632. offset = hhoffset * 60 + mmoffset;
  633. offset *= 60000;
  634. // apply offset
  635. if (c === '+') {
  636. offset *= -1;
  637. }
  638. isUTC = true;
  639. }
  640. // check for second fraction
  641. if (gentime.charAt(14) === '.') {
  642. fff = parseFloat(gentime.substr(14)) * 1000;
  643. }
  644. if (isUTC) {
  645. date.setUTCFullYear(YYYY, MM, DD);
  646. date.setUTCHours(hh, mm, ss, fff);
  647. // apply offset
  648. date.setTime(+date + offset);
  649. }
  650. else {
  651. date.setFullYear(YYYY, MM, DD);
  652. date.setHours(hh, mm, ss, fff);
  653. }
  654. return date;
  655. }
  656. /**
  657. * Parse a Tag.UTCTIME date of Tag.GENERALIZEDTIME date from ASN.1 object' value.
  658. * @param tag the type.
  659. * @param buf the buffer to parse.
  660. */
  661. static parseTime(tag, buf) {
  662. switch (tag) {
  663. case Tag.UTCTIME:
  664. return ASN1.parseUTCTime(buf);
  665. case Tag.GENERALIZEDTIME:
  666. return ASN1.parseGeneralizedTime(buf);
  667. default:
  668. throw new Error('Invalid ASN1 time tag');
  669. }
  670. }
  671. /**
  672. * Creates an Tag.SET ASN.1 object.
  673. * @param objs an array of ASN.1 objects.
  674. */
  675. static Set(objs) {
  676. const asn1 = new ASN1(Class.UNIVERSAL, Tag.SET, Buffer.concat(objs.map((obj) => obj.toDER())));
  677. asn1._value = objs;
  678. return asn1;
  679. }
  680. /**
  681. * Creates an Tag.SEQUENCE ASN.1 object.
  682. * @param objs an array of ASN.1 objects.
  683. */
  684. static Seq(objs) {
  685. const asn1 = new ASN1(Class.UNIVERSAL, Tag.SEQUENCE, Buffer.concat(objs.map((obj) => obj.toDER())));
  686. asn1._value = objs;
  687. return asn1;
  688. }
  689. /**
  690. * Creates an Class.CONTEXT_SPECIFIC ASN.1 object.
  691. *
  692. * Note: the tag means nothing with Class.CONTEXT_SPECIFIC
  693. * @param tag number.
  694. * @param objs an array of ASN.1 objects or a ASN.1 object.
  695. * @param isCompound when objs is a array, the isCompound will be set to true.
  696. */
  697. static Spec(tag, objs, isCompound = true) {
  698. const bytes = Array.isArray(objs) ? Buffer.concat(objs.map((obj) => obj.toDER())) : objs.toDER();
  699. if (Array.isArray(objs)) {
  700. isCompound = true;
  701. }
  702. const asn1 = new ASN1(Class.CONTEXT_SPECIFIC, tag, bytes, isCompound);
  703. asn1._value = objs;
  704. return asn1;
  705. }
  706. /**
  707. * Parse a ASN.1 object from a buffer in DER format.
  708. *
  709. * @param buf the buffer to parse.
  710. * @param deepParse deeply parse or not.
  711. */
  712. static fromDER(buf, deepParse = false) {
  713. return ASN1._fromDER(new common_1.BufferVisitor(buf), deepParse);
  714. }
  715. /**
  716. * Parse a ASN.1 object from a buffer in DER format with given class and tag.
  717. * If class or tag is not match, it will throw a error.
  718. *
  719. * @param tagClass expect class to parse.
  720. * @param tag expect type to parse.
  721. * @param buf the buffer to parse.
  722. */
  723. static parseDER(buf, tagClass, tag) {
  724. const obj = ASN1._fromDER(new common_1.BufferVisitor(buf), false);
  725. if (obj.class !== tagClass && obj.tag !== tag) {
  726. throw new Error(`invalid ASN.1 DER for class ${tagClass} and tag ${tag}`);
  727. }
  728. return obj;
  729. }
  730. /**
  731. * Parse a ASN.1 object from a buffer in DER format with given Template object.
  732. * If template is not match, it will throw a error.
  733. *
  734. * @param buf the buffer to parse.
  735. * @param tpl expect template to parse.
  736. *
  737. * @return a Captures object with captured ASN.1 objects
  738. */
  739. static parseDERWithTemplate(buf, tpl) {
  740. const obj = ASN1._fromDER(new common_1.BufferVisitor(buf), true);
  741. const captures = {};
  742. const err = obj.validate(tpl, captures);
  743. if (err != null) {
  744. err.data = obj;
  745. throw err;
  746. }
  747. return captures;
  748. }
  749. static _parseCompound(buf, deepParse) {
  750. const values = [];
  751. const len = buf.length;
  752. const bufv = new common_1.BufferVisitor(buf);
  753. let readByteLen = 0;
  754. while (readByteLen < len) {
  755. const start = bufv.end;
  756. values.push(ASN1._fromDER(bufv, deepParse));
  757. readByteLen += bufv.end - start;
  758. }
  759. return values;
  760. }
  761. // Internal function to parse an asn1 object from a byte buffer in DER format.
  762. static _fromDER(bufv, deepParse) {
  763. if (!(bufv.buf instanceof Buffer) || bufv.length === 0) {
  764. throw new Error('ASN1 syntax error: invalid Generalized Time');
  765. }
  766. bufv.mustWalk(1, 'Too few bytes to read ASN.1 tag.');
  767. const start = bufv.start;
  768. const b1 = bufv.buf[start];
  769. const tagClass = b1 & 0xc0;
  770. const tag = b1 & 0x1f;
  771. // value storage
  772. const valueLen = getValueLength(bufv);
  773. bufv.mustHas(valueLen);
  774. if (valueLen !== 0 && tag === Tag.NULL) {
  775. throw new Error('invalid value length or NULL tag.');
  776. }
  777. bufv.mustWalk(valueLen);
  778. const isCompound = ((b1 & 0x20) === 0x20);
  779. const asn1 = new ASN1(tagClass, tag, bufv.buf.slice(bufv.start, bufv.end), isCompound);
  780. if (isCompound && deepParse) {
  781. asn1._value = ASN1._parseCompound(asn1.bytes, deepParse);
  782. }
  783. asn1._der = bufv.buf.slice(start, bufv.end);
  784. return asn1;
  785. }
  786. constructor(tagClass, tag, data, isCompound = false) {
  787. this.class = tagClass;
  788. this.tag = tag;
  789. this.bytes = data;
  790. // CONTEXT_SPECIFIC, SEQUENCE, SET, others...
  791. this.isCompound = isCompound || tag === Tag.SEQUENCE || tag === Tag.SET;
  792. this._value = undefined;
  793. this._der = null;
  794. }
  795. /**
  796. * the well parsed value of this ASN.1 object.
  797. * It will be boolean, number, string, BitString, Date, array of ASN.1 objects and so on.
  798. */
  799. get value() {
  800. if (this._value === undefined) {
  801. this._value = this.valueOf();
  802. }
  803. return this._value;
  804. }
  805. /**
  806. * the DER format Buffer of this ASN.1 object.
  807. */
  808. get DER() {
  809. if (this._der == null) {
  810. this._der = this.toDER();
  811. }
  812. return this._der;
  813. }
  814. /**
  815. * Expecting it is compound ASN.1 object and returns an array of sub ASN.1 objects.
  816. * @param msg error message to throw when it is not compound ASN.1 object.
  817. */
  818. mustCompound(msg = 'asn1 object value is not compound') {
  819. if (!this.isCompound || !Array.isArray(this.value)) {
  820. const err = new Error(msg);
  821. err.data = this.toJSON();
  822. throw err;
  823. }
  824. return this.value;
  825. }
  826. /**
  827. * Returns true if two ASN.1 objects equally.
  828. * @param obj another ASN.1 object.
  829. */
  830. equals(obj) {
  831. if (!(obj instanceof ASN1)) {
  832. return false;
  833. }
  834. if (this.class !== obj.class || this.tag !== obj.tag || this.isCompound !== obj.isCompound) {
  835. return false;
  836. }
  837. if (!this.bytes.equals(obj.bytes)) {
  838. return false;
  839. }
  840. return true;
  841. }
  842. /**
  843. * Converts this ASN.1 object to a buffer of bytes in DER format.
  844. */
  845. toDER() {
  846. // build the first byte
  847. let b1 = this.class | this.tag;
  848. if (this.isCompound) {
  849. b1 |= 0x20;
  850. }
  851. const valueLenBytes = getValueLengthByte(this.bytes.length);
  852. const buf = Buffer.allocUnsafe(2 + valueLenBytes + this.bytes.length);
  853. buf.writeInt8(b1, 0);
  854. if (valueLenBytes === 0) {
  855. buf.writeUInt8(this.bytes.length, 1);
  856. this.bytes.copy(buf, 2);
  857. }
  858. else {
  859. buf.writeUInt8(valueLenBytes | 0x80, 1);
  860. buf.writeUIntBE(this.bytes.length, 2, valueLenBytes);
  861. this.bytes.copy(buf, 2 + valueLenBytes);
  862. }
  863. return buf;
  864. }
  865. /**
  866. * Parse the value of this ASN.1 object when it is Class.UNIVERSAL.
  867. * The value will be boolean, number, string, BitString, Date, array of ASN.1 objects and so on.
  868. */
  869. valueOf() {
  870. if (this.isCompound) {
  871. return ASN1._parseCompound(this.bytes, false);
  872. }
  873. if (this.class !== Class.UNIVERSAL) {
  874. return this.bytes;
  875. }
  876. switch (this.tag) {
  877. case Tag.BOOLEAN:
  878. return ASN1.parseBool(this.bytes);
  879. case Tag.INTEGER:
  880. return ASN1.parseInteger(this.bytes);
  881. case Tag.BITSTRING:
  882. return ASN1.parseBitString(this.bytes);
  883. case Tag.NULL:
  884. return ASN1.parseNull(this.bytes);
  885. case Tag.OID:
  886. return ASN1.parseOID(this.bytes);
  887. case Tag.UTF8:
  888. return ASN1.parseUTF8(this.bytes);
  889. case Tag.NUMERICSTRING:
  890. return ASN1.parseNumericString(this.bytes);
  891. case Tag.PRINTABLESTRING:
  892. return ASN1.parsePrintableString(this.bytes);
  893. case Tag.T61STRING:
  894. return ASN1.parseT61String(this.bytes);
  895. case Tag.IA5STRING:
  896. return ASN1.parseIA5String(this.bytes);
  897. case Tag.GENERALSTRING:
  898. return ASN1.parseGeneralString(this.bytes);
  899. case Tag.UTCTIME:
  900. return ASN1.parseUTCTime(this.bytes);
  901. case Tag.GENERALIZEDTIME:
  902. return ASN1.parseGeneralizedTime(this.bytes);
  903. default:
  904. return this.bytes;
  905. }
  906. }
  907. /**
  908. * Validates that the given ASN.1 object is at least a super set of the
  909. * given ASN.1 structure. Only tag classes and types are checked. An
  910. * optional map may also be provided to capture ASN.1 values while the
  911. * structure is checked.
  912. *
  913. * To capture an ASN.1 object, set an object in the validator's 'capture'
  914. * parameter to the key to use in the capture map.
  915. *
  916. * Objects in the validator may set a field 'optional' to true to indicate
  917. * that it isn't necessary to pass validation.
  918. *
  919. * @param tpl Template object to validate.
  920. * @param captures Captures object to capture ASN.1 object.
  921. */
  922. validate(tpl, captures = {}) {
  923. if (this.class !== tpl.class) {
  924. return new Error(`ASN.1 object validate failure for ${tpl.name} : error class ${Class[this.class]}`);
  925. }
  926. const tplTags = Array.isArray(tpl.tag) ? tpl.tag : [tpl.tag];
  927. if (!tplTags.includes(this.tag)) {
  928. return new Error(`ASN.1 object validate failure for ${tpl.name}: error tag ${Tag[this.tag]}`);
  929. }
  930. if (tpl.capture != null) {
  931. captures[tpl.capture] = this;
  932. }
  933. if (Array.isArray(tpl.value)) {
  934. const values = this.mustCompound(`${tpl.name} need compound ASN1 value`);
  935. for (let i = 0, j = 0; i < tpl.value.length; i++) {
  936. if (values[j] != null) {
  937. const err = values[j].validate(tpl.value[i], captures);
  938. if (err == null) {
  939. j++;
  940. }
  941. else if (tpl.value[i].optional !== true) {
  942. return err;
  943. }
  944. }
  945. else if (tpl.value[i].optional !== true) {
  946. return new Error(`ASN.1 object validate failure for ${tpl.value[i].name}: not exists`);
  947. }
  948. }
  949. }
  950. else if (tpl.value != null) {
  951. const buf = this.tag === Tag.BITSTRING ? this.bytes.slice(1) : this.bytes;
  952. return ASN1.fromDER(buf).validate(tpl.value, captures);
  953. }
  954. return null;
  955. }
  956. /**
  957. * Return a friendly JSON object for debuging.
  958. */
  959. toJSON() {
  960. let value = this.value;
  961. if (Array.isArray(value)) {
  962. value = value.map((val) => val.toJSON());
  963. }
  964. return {
  965. class: Class[this.class],
  966. tag: this.class === Class.UNIVERSAL ? Tag[this.tag] : this.tag,
  967. value,
  968. };
  969. }
  970. [util_1.inspect.custom](_depth, options) {
  971. if (options.depth <= 2) {
  972. options.depth = 10;
  973. }
  974. return `<${this.constructor.name} ${util_1.inspect(this.toJSON(), options)}>`;
  975. }
  976. }
  977. exports.ASN1 = ASN1;
  978. // Gets the length of a BER-encoded ASN.1 value.
  979. function getValueLength(bufv) {
  980. bufv.mustWalk(1, 'Too few bytes to read ASN.1 value length.');
  981. const byte = bufv.buf[bufv.start];
  982. // see if the length is "short form" or "long form" (bit 8 set)
  983. if ((byte & 0x80) === 0) {
  984. // if byte is 0, means asn1 object of indefinite length
  985. return byte;
  986. }
  987. const byteLen = byte & 0x7f;
  988. bufv.mustWalk(byteLen, 'Too few bytes to read ASN.1 value length.');
  989. return bufv.buf.readUIntBE(bufv.start, byteLen);
  990. }
  991. // Gets the length of a BER-encoded ASN.1 value length's bytes
  992. function getValueLengthByte(valueLen) {
  993. if (valueLen <= 127) {
  994. return 0;
  995. }
  996. else if (valueLen <= 0xff) {
  997. return 1;
  998. }
  999. else if (valueLen <= 0xffff) {
  1000. return 2;
  1001. }
  1002. else if (valueLen <= 0xffffff) {
  1003. return 3;
  1004. }
  1005. else if (valueLen <= 0xffffffff) {
  1006. return 4;
  1007. }
  1008. else if (valueLen <= 0xffffffffff) {
  1009. return 5;
  1010. }
  1011. else if (valueLen <= 0xffffffffffff) {
  1012. return 6;
  1013. }
  1014. else {
  1015. throw new Error('invalid value length');
  1016. }
  1017. }
  1018. function isNumericString(str) {
  1019. for (const s of str) {
  1020. const n = s.charCodeAt(0);
  1021. if (n !== 32 && (n < 48 || n > 57)) { // '0' to '9', and ' '
  1022. return false;
  1023. }
  1024. }
  1025. return true;
  1026. }
  1027. function isIA5String(str) {
  1028. for (const s of str) {
  1029. if (s.charCodeAt(0) >= 0x80) {
  1030. return false;
  1031. }
  1032. }
  1033. return true;
  1034. }
  1035. function mustParseInt(str, radix = 10) {
  1036. const val = parseInt(str, radix);
  1037. if (Number.isNaN(val)) {
  1038. throw new Error(`Invalid numeric string "${str}" in radix ${radix}.`);
  1039. }
  1040. return val;
  1041. }
  1042. //# sourceMappingURL=asn1.js.map