schema.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import * as asn1js from "asn1js";
  2. import { AsnPropTypes, AsnTypeTypes } from "./enums";
  3. import { isConvertible } from "./helper";
  4. export class AsnSchemaStorage {
  5. constructor() {
  6. this.items = new WeakMap();
  7. }
  8. has(target) {
  9. return this.items.has(target);
  10. }
  11. get(target, checkSchema = false) {
  12. const schema = this.items.get(target);
  13. if (!schema) {
  14. throw new Error(`Cannot get schema for '${target.prototype.constructor.name}' target`);
  15. }
  16. if (checkSchema && !schema.schema) {
  17. throw new Error(`Schema '${target.prototype.constructor.name}' doesn't contain ASN.1 schema. Call 'AsnSchemaStorage.cache'.`);
  18. }
  19. return schema;
  20. }
  21. cache(target) {
  22. const schema = this.get(target);
  23. if (!schema.schema) {
  24. schema.schema = this.create(target, true);
  25. }
  26. }
  27. createDefault(target) {
  28. const schema = { type: AsnTypeTypes.Sequence, items: {} };
  29. const parentSchema = this.findParentSchema(target);
  30. if (parentSchema) {
  31. Object.assign(schema, parentSchema);
  32. schema.items = Object.assign({}, schema.items, parentSchema.items);
  33. }
  34. return schema;
  35. }
  36. create(target, useNames) {
  37. const schema = this.items.get(target) || this.createDefault(target);
  38. const asn1Value = [];
  39. for (const key in schema.items) {
  40. const item = schema.items[key];
  41. const name = useNames ? key : "";
  42. let asn1Item;
  43. if (typeof item.type === "number") {
  44. const Asn1TypeName = AsnPropTypes[item.type];
  45. const Asn1Type = asn1js[Asn1TypeName];
  46. if (!Asn1Type) {
  47. throw new Error(`Cannot get ASN1 class by name '${Asn1TypeName}'`);
  48. }
  49. asn1Item = new Asn1Type({ name });
  50. }
  51. else if (isConvertible(item.type)) {
  52. const instance = new item.type();
  53. asn1Item = instance.toSchema(name);
  54. }
  55. else if (item.optional) {
  56. const itemSchema = this.get(item.type);
  57. if (itemSchema.type === AsnTypeTypes.Choice) {
  58. asn1Item = new asn1js.Any({ name });
  59. }
  60. else {
  61. asn1Item = this.create(item.type, false);
  62. asn1Item.name = name;
  63. }
  64. }
  65. else {
  66. asn1Item = new asn1js.Any({ name });
  67. }
  68. const optional = !!item.optional || item.defaultValue !== undefined;
  69. if (item.repeated) {
  70. asn1Item.name = "";
  71. const Container = item.repeated === "set" ? asn1js.Set : asn1js.Sequence;
  72. asn1Item = new Container({
  73. name: "",
  74. value: [new asn1js.Repeated({ name, value: asn1Item })],
  75. });
  76. }
  77. if (item.context !== null && item.context !== undefined) {
  78. if (item.implicit) {
  79. if (typeof item.type === "number" || isConvertible(item.type)) {
  80. const Container = item.repeated ? asn1js.Constructed : asn1js.Primitive;
  81. asn1Value.push(new Container({ name, optional, idBlock: { tagClass: 3, tagNumber: item.context } }));
  82. }
  83. else {
  84. this.cache(item.type);
  85. const isRepeated = !!item.repeated;
  86. let value = !isRepeated ? this.get(item.type, true).schema : asn1Item;
  87. value =
  88. "valueBlock" in value
  89. ? value.valueBlock.value
  90. :
  91. value.value;
  92. asn1Value.push(new asn1js.Constructed({
  93. name: !isRepeated ? name : "",
  94. optional,
  95. idBlock: { tagClass: 3, tagNumber: item.context },
  96. value: value,
  97. }));
  98. }
  99. }
  100. else {
  101. asn1Value.push(new asn1js.Constructed({
  102. optional,
  103. idBlock: { tagClass: 3, tagNumber: item.context },
  104. value: [asn1Item],
  105. }));
  106. }
  107. }
  108. else {
  109. asn1Item.optional = optional;
  110. asn1Value.push(asn1Item);
  111. }
  112. }
  113. switch (schema.type) {
  114. case AsnTypeTypes.Sequence:
  115. return new asn1js.Sequence({ value: asn1Value, name: "" });
  116. case AsnTypeTypes.Set:
  117. return new asn1js.Set({ value: asn1Value, name: "" });
  118. case AsnTypeTypes.Choice:
  119. return new asn1js.Choice({ value: asn1Value, name: "" });
  120. default:
  121. throw new Error(`Unsupported ASN1 type in use`);
  122. }
  123. }
  124. set(target, schema) {
  125. this.items.set(target, schema);
  126. return this;
  127. }
  128. findParentSchema(target) {
  129. const parent = Object.getPrototypeOf(target);
  130. if (parent) {
  131. const schema = this.items.get(parent);
  132. return schema || this.findParentSchema(parent);
  133. }
  134. return null;
  135. }
  136. }