schema.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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, items: {},
  28. };
  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({
  75. name, value: asn1Item,
  76. })],
  77. });
  78. }
  79. if (item.context !== null && item.context !== undefined) {
  80. if (item.implicit) {
  81. if (typeof item.type === "number" || isConvertible(item.type)) {
  82. const Container = item.repeated ? asn1js.Constructed : asn1js.Primitive;
  83. asn1Value.push(new Container({
  84. name, optional, idBlock: {
  85. tagClass: 3, tagNumber: item.context,
  86. },
  87. }));
  88. }
  89. else {
  90. this.cache(item.type);
  91. const isRepeated = !!item.repeated;
  92. let value = !isRepeated ? this.get(item.type, true).schema : asn1Item;
  93. value
  94. = "valueBlock" in value
  95. ? value.valueBlock.value
  96. : value.value;
  97. asn1Value.push(new asn1js.Constructed({
  98. name: !isRepeated ? name : "",
  99. optional,
  100. idBlock: {
  101. tagClass: 3, tagNumber: item.context,
  102. },
  103. value: value,
  104. }));
  105. }
  106. }
  107. else {
  108. asn1Value.push(new asn1js.Constructed({
  109. optional,
  110. idBlock: {
  111. tagClass: 3, tagNumber: item.context,
  112. },
  113. value: [asn1Item],
  114. }));
  115. }
  116. }
  117. else {
  118. asn1Item.optional = optional;
  119. asn1Value.push(asn1Item);
  120. }
  121. }
  122. switch (schema.type) {
  123. case AsnTypeTypes.Sequence:
  124. return new asn1js.Sequence({
  125. value: asn1Value, name: "",
  126. });
  127. case AsnTypeTypes.Set:
  128. return new asn1js.Set({
  129. value: asn1Value, name: "",
  130. });
  131. case AsnTypeTypes.Choice:
  132. return new asn1js.Choice({
  133. value: asn1Value, name: "",
  134. });
  135. default:
  136. throw new Error("Unsupported ASN1 type in use");
  137. }
  138. }
  139. set(target, schema) {
  140. this.items.set(target, schema);
  141. return this;
  142. }
  143. findParentSchema(target) {
  144. const parent = Object.getPrototypeOf(target);
  145. if (parent) {
  146. const schema = this.items.get(parent);
  147. return schema || this.findParentSchema(parent);
  148. }
  149. return null;
  150. }
  151. }