schema.js 5.9 KB

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