serializer.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import * as asn1js from "asn1js";
  2. import { toArrayBuffer } from "@peculiar/utils/bytes";
  3. import * as converters from "./converters.js";
  4. import { AsnPropTypes, AsnTypeTypes } from "./enums.js";
  5. import { isConvertible, isArrayEqual } from "./helper.js";
  6. import { schemaStorage } from "./storage.js";
  7. export class AsnSerializer {
  8. static serialize(obj) {
  9. if (obj instanceof asn1js.BaseBlock) {
  10. return obj.toBER(false);
  11. }
  12. return this.toASN(obj).toBER(false);
  13. }
  14. static toASN(obj) {
  15. if (obj && typeof obj === "object" && isConvertible(obj)) {
  16. return obj.toASN();
  17. }
  18. if (!(obj && typeof obj === "object")) {
  19. throw new TypeError("Parameter 1 should be type of Object.");
  20. }
  21. const target = obj.constructor;
  22. const schema = schemaStorage.get(target);
  23. schemaStorage.cache(target);
  24. let asn1Value = [];
  25. if (schema.itemType) {
  26. if (!Array.isArray(obj)) {
  27. throw new TypeError("Parameter 1 should be type of Array.");
  28. }
  29. if (typeof schema.itemType === "number") {
  30. const converter = converters.defaultConverter(schema.itemType);
  31. if (!converter) {
  32. throw new Error(`Cannot get default converter for array item of ${target.name} ASN1 schema`);
  33. }
  34. asn1Value = obj.map((o) => converter.toASN(o));
  35. }
  36. else {
  37. asn1Value = obj.map((o) => this.toAsnItem({ type: schema.itemType }, "[]", target, o));
  38. }
  39. }
  40. else {
  41. for (const key in schema.items) {
  42. const schemaItem = schema.items[key];
  43. const objProp = obj[key];
  44. if (objProp === undefined
  45. || schemaItem.defaultValue === objProp
  46. || (typeof schemaItem.defaultValue === "object"
  47. && typeof objProp === "object"
  48. && isArrayEqual(this.serialize(schemaItem.defaultValue), this.serialize(objProp)))) {
  49. continue;
  50. }
  51. const asn1Item = AsnSerializer.toAsnItem(schemaItem, key, target, objProp);
  52. if (typeof schemaItem.context === "number") {
  53. if (schemaItem.implicit) {
  54. if (!schemaItem.repeated
  55. && (typeof schemaItem.type === "number" || isConvertible(schemaItem.type))) {
  56. const value = {};
  57. value.valueHex
  58. = asn1Item instanceof asn1js.Null
  59. ? toArrayBuffer(asn1Item.valueBeforeDecodeView)
  60. : asn1Item.valueBlock.toBER();
  61. asn1Value.push(new asn1js.Primitive({
  62. optional: schemaItem.optional,
  63. idBlock: {
  64. tagClass: 3,
  65. tagNumber: schemaItem.context,
  66. },
  67. ...value,
  68. }));
  69. }
  70. else {
  71. asn1Value.push(new asn1js.Constructed({
  72. optional: schemaItem.optional,
  73. idBlock: {
  74. tagClass: 3,
  75. tagNumber: schemaItem.context,
  76. },
  77. value: asn1Item.valueBlock.value,
  78. }));
  79. }
  80. }
  81. else {
  82. asn1Value.push(new asn1js.Constructed({
  83. optional: schemaItem.optional,
  84. idBlock: {
  85. tagClass: 3,
  86. tagNumber: schemaItem.context,
  87. },
  88. value: [asn1Item],
  89. }));
  90. }
  91. }
  92. else if (schemaItem.repeated) {
  93. asn1Value = asn1Value.concat(asn1Item);
  94. }
  95. else {
  96. asn1Value.push(asn1Item);
  97. }
  98. }
  99. }
  100. let asnSchema;
  101. switch (schema.type) {
  102. case AsnTypeTypes.Sequence:
  103. asnSchema = new asn1js.Sequence({ value: asn1Value });
  104. break;
  105. case AsnTypeTypes.Set:
  106. asnSchema = new asn1js.Set({ value: asn1Value });
  107. break;
  108. case AsnTypeTypes.Choice:
  109. if (!asn1Value[0]) {
  110. throw new Error(`Schema '${target.name}' has wrong data. Choice cannot be empty.`);
  111. }
  112. asnSchema = asn1Value[0];
  113. break;
  114. }
  115. return asnSchema;
  116. }
  117. static toAsnItem(schemaItem, key, target, objProp) {
  118. let asn1Item;
  119. if (typeof schemaItem.type === "number") {
  120. const converter = schemaItem.converter;
  121. if (!converter) {
  122. throw new Error(`Property '${key}' doesn't have converter for type ${AsnPropTypes[schemaItem.type]} in schema '${target.name}'`);
  123. }
  124. if (schemaItem.repeated) {
  125. if (!Array.isArray(objProp)) {
  126. throw new TypeError("Parameter 'objProp' should be type of Array.");
  127. }
  128. const items = Array.from(objProp, (element) => converter.toASN(element));
  129. const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
  130. asn1Item = new Container({ value: items });
  131. }
  132. else {
  133. asn1Item = converter.toASN(objProp);
  134. }
  135. }
  136. else {
  137. if (schemaItem.repeated) {
  138. if (!Array.isArray(objProp)) {
  139. throw new TypeError("Parameter 'objProp' should be type of Array.");
  140. }
  141. const items = Array.from(objProp, (element) => this.toASN(element));
  142. const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
  143. asn1Item = new Container({ value: items });
  144. }
  145. else {
  146. asn1Item = this.toASN(objProp);
  147. }
  148. }
  149. return asn1Item;
  150. }
  151. }