DER.jsm 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. // A minimal ASN.1 DER decoder. Supports input lengths up to 65539 (one byte for
  6. // the outer tag, one byte for the 0x82 length-length indicator, two bytes
  7. // indicating a contents length of 65535, and then the 65535 bytes of contents).
  8. // Intended to be used like so:
  9. //
  10. // let bytes = <an array of bytes describing a SEQUENCE OF INTEGER>;
  11. // let der = new DER.DER(bytes);
  12. // let contents = new DER.DER(der.readTagAndGetContents(DER.SEQUENCE));
  13. // while (!contents.atEnd()) {
  14. // let integerBytes = contents.readTagAndGetContents(DER.INTEGER);
  15. // <... do something with integerBytes ...>
  16. // }
  17. // der.assertAtEnd();
  18. //
  19. // For CHOICE, use readTLVChoice and pass an array of acceptable tags.
  20. // The convenience function readBIT_STRING is provided to handle the unused bits
  21. // aspect of BIT STRING. It returns an object that has the properties contents
  22. // (an array of bytes consisting of the bytes making up the BIT STRING) and
  23. // unusedBits (indicating the number of unused bits at the end).
  24. // All other functions generally return an array of bytes or a single byte as
  25. // appropriate.
  26. // peekTag can be used to see if the next tag is an expected given tag.
  27. // readTLV reads and returns an entire (tag, length, value) tuple (again
  28. // returned as an array of bytes).
  29. const UNIVERSAL = 0 << 6;
  30. const CONSTRUCTED = 1 << 5;
  31. const CONTEXT_SPECIFIC = 2 << 6;
  32. const INTEGER = UNIVERSAL | 0x02; // 0x02
  33. const BIT_STRING = UNIVERSAL | 0x03; // 0x03
  34. const NULL = UNIVERSAL | 0x05; // 0x05
  35. const OBJECT_IDENTIFIER = UNIVERSAL | 0x06; // 0x06
  36. const PrintableString = UNIVERSAL | 0x13; // 0x13
  37. const TeletexString = UNIVERSAL | 0x14; // 0x14
  38. const IA5String = UNIVERSAL | 0x16; // 0x16
  39. const UTCTime = UNIVERSAL | 0x17; // 0x17
  40. const GeneralizedTime = UNIVERSAL | 0x18; // 0x18
  41. const UTF8String = UNIVERSAL | 0x0c; // 0x0c
  42. const SEQUENCE = UNIVERSAL | CONSTRUCTED | 0x10; // 0x30
  43. const SET = UNIVERSAL | CONSTRUCTED | 0x11; // 0x31
  44. const ERROR_INVALID_INPUT = "invalid input";
  45. const ERROR_DATA_TRUNCATED = "data truncated";
  46. const ERROR_EXTRA_DATA = "extra data";
  47. const ERROR_INVALID_LENGTH = "invalid length";
  48. const ERROR_UNSUPPORTED_ASN1 = "unsupported asn.1";
  49. const ERROR_UNSUPPORTED_LENGTH = "unsupported length";
  50. const ERROR_INVALID_BIT_STRING = "invalid BIT STRING encoding";
  51. /** Class representing a decoded BIT STRING. */
  52. class BitString {
  53. /**
  54. * @param {Number} unusedBits the number of unused bits
  55. * @param {Number[]} contents an array of bytes comprising the BIT STRING
  56. */
  57. constructor(unusedBits, contents) {
  58. this._unusedBits = unusedBits;
  59. this._contents = contents;
  60. }
  61. /**
  62. * Get the number of unused bits in the BIT STRING
  63. * @return {Number} the number of unused bits
  64. */
  65. get unusedBits() {
  66. return this._unusedBits;
  67. }
  68. /**
  69. * Get the contents of the BIT STRING
  70. * @return {Number[]} an array of bytes representing the contents
  71. */
  72. get contents() {
  73. return this._contents;
  74. }
  75. }
  76. /** Class representing DER-encoded data. Provides methods for decoding it. */
  77. class DER {
  78. /**
  79. * @param {Number[]} bytes an array of bytes representing the encoded data
  80. */
  81. constructor(bytes) {
  82. // Reject non-array inputs.
  83. if (!Array.isArray(bytes)) {
  84. throw new Error(ERROR_INVALID_INPUT);
  85. }
  86. if (bytes.length > 65539) {
  87. throw new Error(ERROR_UNSUPPORTED_LENGTH);
  88. }
  89. // Reject inputs containing non-integer values or values too small or large.
  90. if (bytes.some(b => !Number.isInteger(b) || b < 0 || b > 255)) {
  91. throw new Error(ERROR_INVALID_INPUT);
  92. }
  93. this._bytes = bytes;
  94. this._cursor = 0;
  95. }
  96. /**
  97. * Asserts that the decoder is at the end of the given data. Throws an error
  98. * if this is not the case.
  99. */
  100. assertAtEnd() {
  101. if (!this.atEnd()) {
  102. throw new Error(ERROR_EXTRA_DATA);
  103. }
  104. }
  105. /**
  106. * Determines whether or not the decoder is at the end of the given data.
  107. * @return {Boolean} true if the decoder is at the end and false otherwise
  108. */
  109. atEnd() {
  110. return this._cursor == this._bytes.length;
  111. }
  112. /**
  113. * Reads the next byte of data. Throws if no more data is available.
  114. * @return {Number} the next byte of data
  115. */
  116. readByte() {
  117. if (this._cursor >= this._bytes.length) {
  118. throw new Error(ERROR_DATA_TRUNCATED);
  119. }
  120. let val = this._bytes[this._cursor];
  121. this._cursor++;
  122. return val;
  123. }
  124. /**
  125. * Given the next expected tag, reads and asserts that the next tag is in fact
  126. * the given tag.
  127. * @param {Number} expectedTag the expected next tag
  128. */
  129. _readExpectedTag(expectedTag) {
  130. let tag = this.readByte();
  131. if (tag != expectedTag) {
  132. throw new Error(`unexpected tag: found ${tag} instead of ${expectedTag}`);
  133. }
  134. }
  135. /**
  136. * Decodes and returns the length portion of an ASN.1 TLV tuple. Throws if the
  137. * length is incorrectly encoded or if it describes a length greater than
  138. * 65535 bytes. Indefinite-length encoding is not supported.
  139. * @return {Number} the length of the value of the TLV tuple
  140. */
  141. _readLength() {
  142. let nextByte = this.readByte();
  143. if (nextByte < 0x80) {
  144. return nextByte;
  145. }
  146. if (nextByte == 0x80) {
  147. throw new Error(ERROR_UNSUPPORTED_ASN1);
  148. }
  149. if (nextByte == 0x81) {
  150. let length = this.readByte();
  151. if (length < 0x80) {
  152. throw new Error(ERROR_INVALID_LENGTH);
  153. }
  154. return length;
  155. }
  156. if (nextByte == 0x82) {
  157. let length1 = this.readByte();
  158. let length2 = this.readByte();
  159. let length = (length1 << 8) | length2;
  160. if (length < 256) {
  161. throw new Error(ERROR_INVALID_LENGTH);
  162. }
  163. return length;
  164. }
  165. throw new Error(ERROR_UNSUPPORTED_LENGTH);
  166. }
  167. /**
  168. * Reads <length> bytes of data if available. Throws if less than <length>
  169. * bytes are available.
  170. * @param {Number} length the number of bytes to read. Must be non-negative.
  171. * @return {Number[]} the next <length> bytes of data
  172. */
  173. readBytes(length) {
  174. if (length < 0) {
  175. throw new Error(ERROR_INVALID_LENGTH);
  176. }
  177. let bytes = [];
  178. for (let i = 0; i < length; i++) {
  179. bytes.push(this.readByte());
  180. }
  181. return bytes;
  182. }
  183. /**
  184. * Given an expected next ASN.1 tag, ensures that that tag is next and returns
  185. * the contents of that tag. Throws if a different tag is encountered or if
  186. * the data is otherwise incorrectly encoded.
  187. * @param {Number} tag the next expected ASN.1 tag
  188. * @return {Number[]} the contents of the tag
  189. */
  190. readTagAndGetContents(tag) {
  191. this._readExpectedTag(tag);
  192. let length = this._readLength();
  193. return this.readBytes(length);
  194. }
  195. /**
  196. * Returns the next byte without advancing the decoder. Throws if no more data
  197. * is available.
  198. * @return {Number} the next available byte
  199. */
  200. _peekByte() {
  201. if (this._cursor >= this._bytes.length) {
  202. throw new Error(ERROR_DATA_TRUNCATED);
  203. }
  204. return this._bytes[this._cursor];
  205. }
  206. /**
  207. * Given an expected tag, reads the next entire ASN.1 TLV tuple, asserting
  208. * that the tag matches.
  209. * @param {Number} tag the expected tag
  210. * @return {Number[]} an array of bytes representing the TLV tuple
  211. */
  212. _readExpectedTLV(tag) {
  213. let mark = this._cursor;
  214. this._readExpectedTag(tag);
  215. let length = this._readLength();
  216. // read the bytes so we know they're there (also to advance the cursor)
  217. this.readBytes(length);
  218. return this._bytes.slice(mark, this._cursor);
  219. }
  220. /**
  221. * Reads the next ASN.1 tag, length, and value and returns them as an array of
  222. * bytes.
  223. * @return {Number[]} an array of bytes representing the next ASN.1 TLV
  224. */
  225. readTLV() {
  226. let nextTag = this._peekByte();
  227. return this._readExpectedTLV(nextTag);
  228. }
  229. /**
  230. * Convenience function for decoding a BIT STRING. Reads and returns the
  231. * contents of the expected next BIT STRING. Throws if the next TLV isn't a
  232. * BIT STRING or if the BIT STRING is incorrectly encoded.
  233. * @return {BitString} the next BIT STRING
  234. */
  235. readBIT_STRING() {
  236. let contents = this.readTagAndGetContents(BIT_STRING);
  237. if (contents.length < 1) {
  238. throw new Error(ERROR_INVALID_BIT_STRING);
  239. }
  240. let unusedBits = contents[0];
  241. if (unusedBits > 7) {
  242. throw new Error(ERROR_INVALID_BIT_STRING);
  243. }
  244. // Zero bytes of content but some amount of padding is invalid.
  245. if (contents.length == 1 && unusedBits != 0) {
  246. throw new Error(ERROR_INVALID_BIT_STRING);
  247. }
  248. return new BitString(unusedBits, contents.slice(1, contents.length));
  249. }
  250. /**
  251. * Looks to see if the next ASN.1 tag is the expected given tag.
  252. * @param {Number} tag the expected next ASN.1 tag
  253. * @return {Boolean} true if the next tag is the given one and false otherwise
  254. */
  255. peekTag(tag) {
  256. if (this._cursor >= this._bytes.length) {
  257. return false;
  258. }
  259. return this._bytes[this._cursor] == tag;
  260. }
  261. /**
  262. * Given a list of possible next ASN.1 tags, returns the next TLV if the next
  263. * tag is in the list. Throws if the next tag is not in the list or if the
  264. * data is incorrectly encoded.
  265. * @param {Number[]} tagList the list of potential next tags
  266. * @return {Number[]} the contents of the next TLV if the next tag is in
  267. * <tagList>
  268. */
  269. readTLVChoice(tagList) {
  270. let tag = this._peekByte();
  271. if (!tagList.includes(tag)) {
  272. throw new Error(
  273. `unexpected tag: found ${tag} instead of one of ${tagList}`);
  274. }
  275. return this._readExpectedTLV(tag);
  276. }
  277. }
  278. this.DER = { UNIVERSAL, CONSTRUCTED, CONTEXT_SPECIFIC, INTEGER, BIT_STRING,
  279. NULL, OBJECT_IDENTIFIER, PrintableString, TeletexString, IA5String,
  280. UTCTime, GeneralizedTime, UTF8String, SEQUENCE, SET, DER };
  281. this.EXPORTED_SYMBOLS = ["DER"];