人民医院前端

asn1.js 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. // ASN.1 JavaScript decoder
  2. // Copyright (c) 2008-2014 Lapo Luchini <lapo@lapo.it>
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. /*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */
  15. /*global oids */
  16. import { Int10 } from "./int10";
  17. var ellipsis = "\u2026";
  18. var reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/;
  19. var reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/;
  20. function stringCut(str, len) {
  21. if (str.length > len) {
  22. str = str.substring(0, len) + ellipsis;
  23. }
  24. return str;
  25. }
  26. var Stream = /** @class */ (function () {
  27. function Stream(enc, pos) {
  28. this.hexDigits = "0123456789ABCDEF";
  29. if (enc instanceof Stream) {
  30. this.enc = enc.enc;
  31. this.pos = enc.pos;
  32. }
  33. else {
  34. // enc should be an array or a binary string
  35. this.enc = enc;
  36. this.pos = pos;
  37. }
  38. }
  39. Stream.prototype.get = function (pos) {
  40. if (pos === undefined) {
  41. pos = this.pos++;
  42. }
  43. if (pos >= this.enc.length) {
  44. throw new Error("Requesting byte offset ".concat(pos, " on a stream of length ").concat(this.enc.length));
  45. }
  46. return ("string" === typeof this.enc) ? this.enc.charCodeAt(pos) : this.enc[pos];
  47. };
  48. Stream.prototype.hexByte = function (b) {
  49. return this.hexDigits.charAt((b >> 4) & 0xF) + this.hexDigits.charAt(b & 0xF);
  50. };
  51. Stream.prototype.hexDump = function (start, end, raw) {
  52. var s = "";
  53. for (var i = start; i < end; ++i) {
  54. s += this.hexByte(this.get(i));
  55. if (raw !== true) {
  56. switch (i & 0xF) {
  57. case 0x7:
  58. s += " ";
  59. break;
  60. case 0xF:
  61. s += "\n";
  62. break;
  63. default:
  64. s += " ";
  65. }
  66. }
  67. }
  68. return s;
  69. };
  70. Stream.prototype.isASCII = function (start, end) {
  71. for (var i = start; i < end; ++i) {
  72. var c = this.get(i);
  73. if (c < 32 || c > 176) {
  74. return false;
  75. }
  76. }
  77. return true;
  78. };
  79. Stream.prototype.parseStringISO = function (start, end) {
  80. var s = "";
  81. for (var i = start; i < end; ++i) {
  82. s += String.fromCharCode(this.get(i));
  83. }
  84. return s;
  85. };
  86. Stream.prototype.parseStringUTF = function (start, end) {
  87. var s = "";
  88. for (var i = start; i < end;) {
  89. var c = this.get(i++);
  90. if (c < 128) {
  91. s += String.fromCharCode(c);
  92. }
  93. else if ((c > 191) && (c < 224)) {
  94. s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F));
  95. }
  96. else {
  97. s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F));
  98. }
  99. }
  100. return s;
  101. };
  102. Stream.prototype.parseStringBMP = function (start, end) {
  103. var str = "";
  104. var hi;
  105. var lo;
  106. for (var i = start; i < end;) {
  107. hi = this.get(i++);
  108. lo = this.get(i++);
  109. str += String.fromCharCode((hi << 8) | lo);
  110. }
  111. return str;
  112. };
  113. Stream.prototype.parseTime = function (start, end, shortYear) {
  114. var s = this.parseStringISO(start, end);
  115. var m = (shortYear ? reTimeS : reTimeL).exec(s);
  116. if (!m) {
  117. return "Unrecognized time: " + s;
  118. }
  119. if (shortYear) {
  120. // to avoid querying the timer, use the fixed range [1970, 2069]
  121. // it will conform with ITU X.400 [-10, +40] sliding window until 2030
  122. m[1] = +m[1];
  123. m[1] += (+m[1] < 70) ? 2000 : 1900;
  124. }
  125. s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4];
  126. if (m[5]) {
  127. s += ":" + m[5];
  128. if (m[6]) {
  129. s += ":" + m[6];
  130. if (m[7]) {
  131. s += "." + m[7];
  132. }
  133. }
  134. }
  135. if (m[8]) {
  136. s += " UTC";
  137. if (m[8] != "Z") {
  138. s += m[8];
  139. if (m[9]) {
  140. s += ":" + m[9];
  141. }
  142. }
  143. }
  144. return s;
  145. };
  146. Stream.prototype.parseInteger = function (start, end) {
  147. var v = this.get(start);
  148. var neg = (v > 127);
  149. var pad = neg ? 255 : 0;
  150. var len;
  151. var s = "";
  152. // skip unuseful bits (not allowed in DER)
  153. while (v == pad && ++start < end) {
  154. v = this.get(start);
  155. }
  156. len = end - start;
  157. if (len === 0) {
  158. return neg ? -1 : 0;
  159. }
  160. // show bit length of huge integers
  161. if (len > 4) {
  162. s = v;
  163. len <<= 3;
  164. while (((+s ^ pad) & 0x80) == 0) {
  165. s = +s << 1;
  166. --len;
  167. }
  168. s = "(" + len + " bit)\n";
  169. }
  170. // decode the integer
  171. if (neg) {
  172. v = v - 256;
  173. }
  174. var n = new Int10(v);
  175. for (var i = start + 1; i < end; ++i) {
  176. n.mulAdd(256, this.get(i));
  177. }
  178. return s + n.toString();
  179. };
  180. Stream.prototype.parseBitString = function (start, end, maxLength) {
  181. var unusedBit = this.get(start);
  182. var lenBit = ((end - start - 1) << 3) - unusedBit;
  183. var intro = "(" + lenBit + " bit)\n";
  184. var s = "";
  185. for (var i = start + 1; i < end; ++i) {
  186. var b = this.get(i);
  187. var skip = (i == end - 1) ? unusedBit : 0;
  188. for (var j = 7; j >= skip; --j) {
  189. s += (b >> j) & 1 ? "1" : "0";
  190. }
  191. if (s.length > maxLength) {
  192. return intro + stringCut(s, maxLength);
  193. }
  194. }
  195. return intro + s;
  196. };
  197. Stream.prototype.parseOctetString = function (start, end, maxLength) {
  198. if (this.isASCII(start, end)) {
  199. return stringCut(this.parseStringISO(start, end), maxLength);
  200. }
  201. var len = end - start;
  202. var s = "(" + len + " byte)\n";
  203. maxLength /= 2; // we work in bytes
  204. if (len > maxLength) {
  205. end = start + maxLength;
  206. }
  207. for (var i = start; i < end; ++i) {
  208. s += this.hexByte(this.get(i));
  209. }
  210. if (len > maxLength) {
  211. s += ellipsis;
  212. }
  213. return s;
  214. };
  215. Stream.prototype.parseOID = function (start, end, maxLength) {
  216. var s = "";
  217. var n = new Int10();
  218. var bits = 0;
  219. for (var i = start; i < end; ++i) {
  220. var v = this.get(i);
  221. n.mulAdd(128, v & 0x7F);
  222. bits += 7;
  223. if (!(v & 0x80)) { // finished
  224. if (s === "") {
  225. n = n.simplify();
  226. if (n instanceof Int10) {
  227. n.sub(80);
  228. s = "2." + n.toString();
  229. }
  230. else {
  231. var m = n < 80 ? n < 40 ? 0 : 1 : 2;
  232. s = m + "." + (n - m * 40);
  233. }
  234. }
  235. else {
  236. s += "." + n.toString();
  237. }
  238. if (s.length > maxLength) {
  239. return stringCut(s, maxLength);
  240. }
  241. n = new Int10();
  242. bits = 0;
  243. }
  244. }
  245. if (bits > 0) {
  246. s += ".incomplete";
  247. }
  248. return s;
  249. };
  250. return Stream;
  251. }());
  252. export { Stream };
  253. var ASN1 = /** @class */ (function () {
  254. function ASN1(stream, header, length, tag, sub) {
  255. if (!(tag instanceof ASN1Tag)) {
  256. throw new Error("Invalid tag value.");
  257. }
  258. this.stream = stream;
  259. this.header = header;
  260. this.length = length;
  261. this.tag = tag;
  262. this.sub = sub;
  263. }
  264. ASN1.prototype.typeName = function () {
  265. switch (this.tag.tagClass) {
  266. case 0: // universal
  267. switch (this.tag.tagNumber) {
  268. case 0x00:
  269. return "EOC";
  270. case 0x01:
  271. return "BOOLEAN";
  272. case 0x02:
  273. return "INTEGER";
  274. case 0x03:
  275. return "BIT_STRING";
  276. case 0x04:
  277. return "OCTET_STRING";
  278. case 0x05:
  279. return "NULL";
  280. case 0x06:
  281. return "OBJECT_IDENTIFIER";
  282. case 0x07:
  283. return "ObjectDescriptor";
  284. case 0x08:
  285. return "EXTERNAL";
  286. case 0x09:
  287. return "REAL";
  288. case 0x0A:
  289. return "ENUMERATED";
  290. case 0x0B:
  291. return "EMBEDDED_PDV";
  292. case 0x0C:
  293. return "UTF8String";
  294. case 0x10:
  295. return "SEQUENCE";
  296. case 0x11:
  297. return "SET";
  298. case 0x12:
  299. return "NumericString";
  300. case 0x13:
  301. return "PrintableString"; // ASCII subset
  302. case 0x14:
  303. return "TeletexString"; // aka T61String
  304. case 0x15:
  305. return "VideotexString";
  306. case 0x16:
  307. return "IA5String"; // ASCII
  308. case 0x17:
  309. return "UTCTime";
  310. case 0x18:
  311. return "GeneralizedTime";
  312. case 0x19:
  313. return "GraphicString";
  314. case 0x1A:
  315. return "VisibleString"; // ASCII subset
  316. case 0x1B:
  317. return "GeneralString";
  318. case 0x1C:
  319. return "UniversalString";
  320. case 0x1E:
  321. return "BMPString";
  322. }
  323. return "Universal_" + this.tag.tagNumber.toString();
  324. case 1:
  325. return "Application_" + this.tag.tagNumber.toString();
  326. case 2:
  327. return "[" + this.tag.tagNumber.toString() + "]"; // Context
  328. case 3:
  329. return "Private_" + this.tag.tagNumber.toString();
  330. }
  331. };
  332. ASN1.prototype.content = function (maxLength) {
  333. if (this.tag === undefined) {
  334. return null;
  335. }
  336. if (maxLength === undefined) {
  337. maxLength = Infinity;
  338. }
  339. var content = this.posContent();
  340. var len = Math.abs(this.length);
  341. if (!this.tag.isUniversal()) {
  342. if (this.sub !== null) {
  343. return "(" + this.sub.length + " elem)";
  344. }
  345. return this.stream.parseOctetString(content, content + len, maxLength);
  346. }
  347. switch (this.tag.tagNumber) {
  348. case 0x01: // BOOLEAN
  349. return (this.stream.get(content) === 0) ? "false" : "true";
  350. case 0x02: // INTEGER
  351. return this.stream.parseInteger(content, content + len);
  352. case 0x03: // BIT_STRING
  353. return this.sub ? "(" + this.sub.length + " elem)" :
  354. this.stream.parseBitString(content, content + len, maxLength);
  355. case 0x04: // OCTET_STRING
  356. return this.sub ? "(" + this.sub.length + " elem)" :
  357. this.stream.parseOctetString(content, content + len, maxLength);
  358. // case 0x05: // NULL
  359. case 0x06: // OBJECT_IDENTIFIER
  360. return this.stream.parseOID(content, content + len, maxLength);
  361. // case 0x07: // ObjectDescriptor
  362. // case 0x08: // EXTERNAL
  363. // case 0x09: // REAL
  364. // case 0x0A: // ENUMERATED
  365. // case 0x0B: // EMBEDDED_PDV
  366. case 0x10: // SEQUENCE
  367. case 0x11: // SET
  368. if (this.sub !== null) {
  369. return "(" + this.sub.length + " elem)";
  370. }
  371. else {
  372. return "(no elem)";
  373. }
  374. case 0x0C: // UTF8String
  375. return stringCut(this.stream.parseStringUTF(content, content + len), maxLength);
  376. case 0x12: // NumericString
  377. case 0x13: // PrintableString
  378. case 0x14: // TeletexString
  379. case 0x15: // VideotexString
  380. case 0x16: // IA5String
  381. // case 0x19: // GraphicString
  382. case 0x1A: // VisibleString
  383. // case 0x1B: // GeneralString
  384. // case 0x1C: // UniversalString
  385. return stringCut(this.stream.parseStringISO(content, content + len), maxLength);
  386. case 0x1E: // BMPString
  387. return stringCut(this.stream.parseStringBMP(content, content + len), maxLength);
  388. case 0x17: // UTCTime
  389. case 0x18: // GeneralizedTime
  390. return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17));
  391. }
  392. return null;
  393. };
  394. ASN1.prototype.toString = function () {
  395. return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.sub === null) ? "null" : this.sub.length) + "]";
  396. };
  397. ASN1.prototype.toPrettyString = function (indent) {
  398. if (indent === undefined) {
  399. indent = "";
  400. }
  401. var s = indent + this.typeName() + " @" + this.stream.pos;
  402. if (this.length >= 0) {
  403. s += "+";
  404. }
  405. s += this.length;
  406. if (this.tag.tagConstructed) {
  407. s += " (constructed)";
  408. }
  409. else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) {
  410. s += " (encapsulates)";
  411. }
  412. s += "\n";
  413. if (this.sub !== null) {
  414. indent += " ";
  415. for (var i = 0, max = this.sub.length; i < max; ++i) {
  416. s += this.sub[i].toPrettyString(indent);
  417. }
  418. }
  419. return s;
  420. };
  421. ASN1.prototype.posStart = function () {
  422. return this.stream.pos;
  423. };
  424. ASN1.prototype.posContent = function () {
  425. return this.stream.pos + this.header;
  426. };
  427. ASN1.prototype.posEnd = function () {
  428. return this.stream.pos + this.header + Math.abs(this.length);
  429. };
  430. ASN1.prototype.toHexString = function () {
  431. return this.stream.hexDump(this.posStart(), this.posEnd(), true);
  432. };
  433. ASN1.decodeLength = function (stream) {
  434. var buf = stream.get();
  435. var len = buf & 0x7F;
  436. if (len == buf) {
  437. return len;
  438. }
  439. // no reason to use Int10, as it would be a huge buffer anyways
  440. if (len > 6) {
  441. throw new Error("Length over 48 bits not supported at position " + (stream.pos - 1));
  442. }
  443. if (len === 0) {
  444. return null;
  445. } // undefined
  446. buf = 0;
  447. for (var i = 0; i < len; ++i) {
  448. buf = (buf * 256) + stream.get();
  449. }
  450. return buf;
  451. };
  452. /**
  453. * Retrieve the hexadecimal value (as a string) of the current ASN.1 element
  454. * @returns {string}
  455. * @public
  456. */
  457. ASN1.prototype.getHexStringValue = function () {
  458. var hexString = this.toHexString();
  459. var offset = this.header * 2;
  460. var length = this.length * 2;
  461. return hexString.substr(offset, length);
  462. };
  463. ASN1.decode = function (str) {
  464. var stream;
  465. if (!(str instanceof Stream)) {
  466. stream = new Stream(str, 0);
  467. }
  468. else {
  469. stream = str;
  470. }
  471. var streamStart = new Stream(stream);
  472. var tag = new ASN1Tag(stream);
  473. var len = ASN1.decodeLength(stream);
  474. var start = stream.pos;
  475. var header = start - streamStart.pos;
  476. var sub = null;
  477. var getSub = function () {
  478. var ret = [];
  479. if (len !== null) {
  480. // definite length
  481. var end = start + len;
  482. while (stream.pos < end) {
  483. ret[ret.length] = ASN1.decode(stream);
  484. }
  485. if (stream.pos != end) {
  486. throw new Error("Content size is not correct for container starting at offset " + start);
  487. }
  488. }
  489. else {
  490. // undefined length
  491. try {
  492. for (;;) {
  493. var s = ASN1.decode(stream);
  494. if (s.tag.isEOC()) {
  495. break;
  496. }
  497. ret[ret.length] = s;
  498. }
  499. len = start - stream.pos; // undefined lengths are represented as negative values
  500. }
  501. catch (e) {
  502. throw new Error("Exception while decoding undefined length content: " + e);
  503. }
  504. }
  505. return ret;
  506. };
  507. if (tag.tagConstructed) {
  508. // must have valid content
  509. sub = getSub();
  510. }
  511. else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) {
  512. // sometimes BitString and OctetString are used to encapsulate ASN.1
  513. try {
  514. if (tag.tagNumber == 0x03) {
  515. if (stream.get() != 0) {
  516. throw new Error("BIT STRINGs with unused bits cannot encapsulate.");
  517. }
  518. }
  519. sub = getSub();
  520. for (var i = 0; i < sub.length; ++i) {
  521. if (sub[i].tag.isEOC()) {
  522. throw new Error("EOC is not supposed to be actual content.");
  523. }
  524. }
  525. }
  526. catch (e) {
  527. // but silently ignore when they don't
  528. sub = null;
  529. }
  530. }
  531. if (sub === null) {
  532. if (len === null) {
  533. throw new Error("We can't skip over an invalid tag with undefined length at offset " + start);
  534. }
  535. stream.pos = start + Math.abs(len);
  536. }
  537. return new ASN1(streamStart, header, len, tag, sub);
  538. };
  539. return ASN1;
  540. }());
  541. export { ASN1 };
  542. var ASN1Tag = /** @class */ (function () {
  543. function ASN1Tag(stream) {
  544. var buf = stream.get();
  545. this.tagClass = buf >> 6;
  546. this.tagConstructed = ((buf & 0x20) !== 0);
  547. this.tagNumber = buf & 0x1F;
  548. if (this.tagNumber == 0x1F) { // long tag
  549. var n = new Int10();
  550. do {
  551. buf = stream.get();
  552. n.mulAdd(128, buf & 0x7F);
  553. } while (buf & 0x80);
  554. this.tagNumber = n.simplify();
  555. }
  556. }
  557. ASN1Tag.prototype.isUniversal = function () {
  558. return this.tagClass === 0x00;
  559. };
  560. ASN1Tag.prototype.isEOC = function () {
  561. return this.tagClass === 0x00 && this.tagNumber === 0x00;
  562. };
  563. return ASN1Tag;
  564. }());
  565. export { ASN1Tag };