parser.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. import * as asn1js from "asn1js";
  2. import { AsnPropTypes, AsnTypeTypes } from "./enums";
  3. import * as converters from "./converters";
  4. import { AsnSchemaValidationError } from "./errors";
  5. import { isConvertible, isTypeOfArray } from "./helper";
  6. import { schemaStorage } from "./storage";
  7. export class AsnParser {
  8. static parse(data, target) {
  9. const asn1Parsed = asn1js.fromBER(data);
  10. if (asn1Parsed.result.error) {
  11. throw new Error(asn1Parsed.result.error);
  12. }
  13. const res = this.fromASN(asn1Parsed.result, target);
  14. return res;
  15. }
  16. static fromASN(asn1Schema, target) {
  17. try {
  18. if (isConvertible(target)) {
  19. const value = new target();
  20. return value.fromASN(asn1Schema);
  21. }
  22. const schema = schemaStorage.get(target);
  23. schemaStorage.cache(target);
  24. let targetSchema = schema.schema;
  25. const choiceResult = this.handleChoiceTypes(asn1Schema, schema, target, targetSchema);
  26. if (choiceResult === null || choiceResult === void 0 ? void 0 : choiceResult.result) {
  27. return choiceResult.result;
  28. }
  29. if (choiceResult === null || choiceResult === void 0 ? void 0 : choiceResult.targetSchema) {
  30. targetSchema = choiceResult.targetSchema;
  31. }
  32. const sequenceResult = this.handleSequenceTypes(asn1Schema, schema, target, targetSchema);
  33. const res = new target();
  34. if (isTypeOfArray(target)) {
  35. return this.handleArrayTypes(asn1Schema, schema, target);
  36. }
  37. this.processSchemaItems(schema, sequenceResult, res);
  38. return res;
  39. }
  40. catch (error) {
  41. if (error instanceof AsnSchemaValidationError) {
  42. error.schemas.push(target.name);
  43. }
  44. throw error;
  45. }
  46. }
  47. static handleChoiceTypes(asn1Schema, schema, target, targetSchema) {
  48. if (asn1Schema.constructor === asn1js.Constructed &&
  49. schema.type === AsnTypeTypes.Choice &&
  50. asn1Schema.idBlock.tagClass === 3) {
  51. for (const key in schema.items) {
  52. const schemaItem = schema.items[key];
  53. if (schemaItem.context === asn1Schema.idBlock.tagNumber && schemaItem.implicit) {
  54. if (typeof schemaItem.type === "function" &&
  55. schemaStorage.has(schemaItem.type)) {
  56. const fieldSchema = schemaStorage.get(schemaItem.type);
  57. if (fieldSchema && fieldSchema.type === AsnTypeTypes.Sequence) {
  58. const newSeq = new asn1js.Sequence();
  59. if ("value" in asn1Schema.valueBlock &&
  60. Array.isArray(asn1Schema.valueBlock.value) &&
  61. "value" in newSeq.valueBlock) {
  62. newSeq.valueBlock.value = asn1Schema.valueBlock.value;
  63. const fieldValue = this.fromASN(newSeq, schemaItem.type);
  64. const res = new target();
  65. res[key] = fieldValue;
  66. return { result: res };
  67. }
  68. }
  69. }
  70. }
  71. }
  72. }
  73. else if (asn1Schema.constructor === asn1js.Constructed &&
  74. schema.type !== AsnTypeTypes.Choice) {
  75. const newTargetSchema = new asn1js.Constructed({
  76. idBlock: {
  77. tagClass: 3,
  78. tagNumber: asn1Schema.idBlock.tagNumber,
  79. },
  80. value: schema.schema.valueBlock.value,
  81. });
  82. for (const key in schema.items) {
  83. delete asn1Schema[key];
  84. }
  85. return { targetSchema: newTargetSchema };
  86. }
  87. return null;
  88. }
  89. static handleSequenceTypes(asn1Schema, schema, target, targetSchema) {
  90. if (schema.type === AsnTypeTypes.Sequence) {
  91. const asn1ComparedSchema = asn1js.compareSchema({}, asn1Schema, targetSchema);
  92. if (!asn1ComparedSchema.verified) {
  93. throw new AsnSchemaValidationError(`Data does not match to ${target.name} ASN1 schema.${asn1ComparedSchema.result.error ? ` ${asn1ComparedSchema.result.error}` : ""}`);
  94. }
  95. return asn1ComparedSchema;
  96. }
  97. else {
  98. const asn1ComparedSchema = asn1js.compareSchema({}, asn1Schema, targetSchema);
  99. if (!asn1ComparedSchema.verified) {
  100. throw new AsnSchemaValidationError(`Data does not match to ${target.name} ASN1 schema.${asn1ComparedSchema.result.error ? ` ${asn1ComparedSchema.result.error}` : ""}`);
  101. }
  102. return asn1ComparedSchema;
  103. }
  104. }
  105. static processRepeatedField(asn1Elements, asn1Index, schemaItem) {
  106. let elementsToProcess = asn1Elements.slice(asn1Index);
  107. if (elementsToProcess.length === 1 && elementsToProcess[0].constructor.name === "Sequence") {
  108. const seq = elementsToProcess[0];
  109. if (seq.valueBlock && seq.valueBlock.value && Array.isArray(seq.valueBlock.value)) {
  110. elementsToProcess = seq.valueBlock.value;
  111. }
  112. }
  113. if (typeof schemaItem.type === "number") {
  114. const converter = converters.defaultConverter(schemaItem.type);
  115. if (!converter)
  116. throw new Error(`No converter for ASN.1 type ${schemaItem.type}`);
  117. return elementsToProcess
  118. .filter((el) => el && el.valueBlock)
  119. .map((el) => {
  120. try {
  121. return converter.fromASN(el);
  122. }
  123. catch {
  124. return undefined;
  125. }
  126. })
  127. .filter((v) => v !== undefined);
  128. }
  129. else {
  130. return elementsToProcess
  131. .filter((el) => el && el.valueBlock)
  132. .map((el) => {
  133. try {
  134. return this.fromASN(el, schemaItem.type);
  135. }
  136. catch {
  137. return undefined;
  138. }
  139. })
  140. .filter((v) => v !== undefined);
  141. }
  142. }
  143. static processPrimitiveField(asn1Element, schemaItem) {
  144. const converter = converters.defaultConverter(schemaItem.type);
  145. if (!converter)
  146. throw new Error(`No converter for ASN.1 type ${schemaItem.type}`);
  147. return converter.fromASN(asn1Element);
  148. }
  149. static isOptionalChoiceField(schemaItem) {
  150. return (schemaItem.optional &&
  151. typeof schemaItem.type === "function" &&
  152. schemaStorage.has(schemaItem.type) &&
  153. schemaStorage.get(schemaItem.type).type === AsnTypeTypes.Choice);
  154. }
  155. static processOptionalChoiceField(asn1Element, schemaItem) {
  156. try {
  157. const value = this.fromASN(asn1Element, schemaItem.type);
  158. return { processed: true, value };
  159. }
  160. catch (err) {
  161. if (err instanceof AsnSchemaValidationError &&
  162. /Wrong values for Choice type/.test(err.message)) {
  163. return { processed: false };
  164. }
  165. throw err;
  166. }
  167. }
  168. static handleArrayTypes(asn1Schema, schema, target) {
  169. if (!("value" in asn1Schema.valueBlock && Array.isArray(asn1Schema.valueBlock.value))) {
  170. throw new Error(`Cannot get items from the ASN.1 parsed value. ASN.1 object is not constructed.`);
  171. }
  172. const itemType = schema.itemType;
  173. if (typeof itemType === "number") {
  174. const converter = converters.defaultConverter(itemType);
  175. if (!converter) {
  176. throw new Error(`Cannot get default converter for array item of ${target.name} ASN1 schema`);
  177. }
  178. return target.from(asn1Schema.valueBlock.value, (element) => converter.fromASN(element));
  179. }
  180. else {
  181. return target.from(asn1Schema.valueBlock.value, (element) => this.fromASN(element, itemType));
  182. }
  183. }
  184. static processSchemaItems(schema, asn1ComparedSchema, res) {
  185. for (const key in schema.items) {
  186. const asn1SchemaValue = asn1ComparedSchema.result[key];
  187. if (!asn1SchemaValue) {
  188. continue;
  189. }
  190. const schemaItem = schema.items[key];
  191. const schemaItemType = schemaItem.type;
  192. let parsedValue;
  193. if (typeof schemaItemType === "number" || isConvertible(schemaItemType)) {
  194. parsedValue = this.processPrimitiveSchemaItem(asn1SchemaValue, schemaItem, schemaItemType);
  195. }
  196. else {
  197. parsedValue = this.processComplexSchemaItem(asn1SchemaValue, schemaItem, schemaItemType);
  198. }
  199. if (parsedValue &&
  200. typeof parsedValue === "object" &&
  201. "value" in parsedValue &&
  202. "raw" in parsedValue) {
  203. res[key] = parsedValue.value;
  204. res[`${key}Raw`] = parsedValue.raw;
  205. }
  206. else {
  207. res[key] = parsedValue;
  208. }
  209. }
  210. }
  211. static processPrimitiveSchemaItem(asn1SchemaValue, schemaItem, schemaItemType) {
  212. var _a;
  213. const converter = (_a = schemaItem.converter) !== null && _a !== void 0 ? _a : (isConvertible(schemaItemType)
  214. ? new schemaItemType()
  215. : null);
  216. if (!converter) {
  217. throw new Error("Converter is empty");
  218. }
  219. if (schemaItem.repeated) {
  220. return this.processRepeatedPrimitiveItem(asn1SchemaValue, schemaItem, converter);
  221. }
  222. else {
  223. return this.processSinglePrimitiveItem(asn1SchemaValue, schemaItem, schemaItemType, converter);
  224. }
  225. }
  226. static processRepeatedPrimitiveItem(asn1SchemaValue, schemaItem, converter) {
  227. if (schemaItem.implicit) {
  228. const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
  229. const newItem = new Container();
  230. newItem.valueBlock = asn1SchemaValue.valueBlock;
  231. const newItemAsn = asn1js.fromBER(newItem.toBER(false));
  232. if (newItemAsn.offset === -1) {
  233. throw new Error(`Cannot parse the child item. ${newItemAsn.result.error}`);
  234. }
  235. if (!("value" in newItemAsn.result.valueBlock &&
  236. Array.isArray(newItemAsn.result.valueBlock.value))) {
  237. throw new Error("Cannot get items from the ASN.1 parsed value. ASN.1 object is not constructed.");
  238. }
  239. const value = newItemAsn.result.valueBlock.value;
  240. return Array.from(value, (element) => converter.fromASN(element));
  241. }
  242. else {
  243. return Array.from(asn1SchemaValue, (element) => converter.fromASN(element));
  244. }
  245. }
  246. static processSinglePrimitiveItem(asn1SchemaValue, schemaItem, schemaItemType, converter) {
  247. let value = asn1SchemaValue;
  248. if (schemaItem.implicit) {
  249. let newItem;
  250. if (isConvertible(schemaItemType)) {
  251. newItem = new schemaItemType().toSchema("");
  252. }
  253. else {
  254. const Asn1TypeName = AsnPropTypes[schemaItemType];
  255. const Asn1Type = asn1js[Asn1TypeName];
  256. if (!Asn1Type) {
  257. throw new Error(`Cannot get '${Asn1TypeName}' class from asn1js module`);
  258. }
  259. newItem = new Asn1Type();
  260. }
  261. newItem.valueBlock = value.valueBlock;
  262. value = asn1js.fromBER(newItem.toBER(false)).result;
  263. }
  264. return converter.fromASN(value);
  265. }
  266. static processComplexSchemaItem(asn1SchemaValue, schemaItem, schemaItemType) {
  267. if (schemaItem.repeated) {
  268. if (!Array.isArray(asn1SchemaValue)) {
  269. throw new Error("Cannot get list of items from the ASN.1 parsed value. ASN.1 value should be iterable.");
  270. }
  271. return Array.from(asn1SchemaValue, (element) => this.fromASN(element, schemaItemType));
  272. }
  273. else {
  274. const valueToProcess = this.handleImplicitTagging(asn1SchemaValue, schemaItem, schemaItemType);
  275. if (this.isOptionalChoiceField(schemaItem)) {
  276. try {
  277. return this.fromASN(valueToProcess, schemaItemType);
  278. }
  279. catch (err) {
  280. if (err instanceof AsnSchemaValidationError &&
  281. /Wrong values for Choice type/.test(err.message)) {
  282. return undefined;
  283. }
  284. throw err;
  285. }
  286. }
  287. else {
  288. const parsedValue = this.fromASN(valueToProcess, schemaItemType);
  289. if (schemaItem.raw) {
  290. return {
  291. value: parsedValue,
  292. raw: asn1SchemaValue.valueBeforeDecodeView,
  293. };
  294. }
  295. return parsedValue;
  296. }
  297. }
  298. }
  299. static handleImplicitTagging(asn1SchemaValue, schemaItem, schemaItemType) {
  300. if (schemaItem.implicit && typeof schemaItem.context === "number") {
  301. const schema = schemaStorage.get(schemaItemType);
  302. if (schema.type === AsnTypeTypes.Sequence) {
  303. const newSeq = new asn1js.Sequence();
  304. if ("value" in asn1SchemaValue.valueBlock &&
  305. Array.isArray(asn1SchemaValue.valueBlock.value) &&
  306. "value" in newSeq.valueBlock) {
  307. newSeq.valueBlock.value = asn1SchemaValue.valueBlock.value;
  308. return newSeq;
  309. }
  310. }
  311. else if (schema.type === AsnTypeTypes.Set) {
  312. const newSet = new asn1js.Set();
  313. if ("value" in asn1SchemaValue.valueBlock &&
  314. Array.isArray(asn1SchemaValue.valueBlock.value) &&
  315. "value" in newSet.valueBlock) {
  316. newSet.valueBlock.value = asn1SchemaValue.valueBlock.value;
  317. return newSet;
  318. }
  319. }
  320. }
  321. return asn1SchemaValue;
  322. }
  323. }