import * as asn1js from "asn1js"; import * as converters from "./converters"; import { AsnPropTypes, AsnTypeTypes } from "./enums"; import { isConvertible, isArrayEqual } from "./helper"; import { schemaStorage } from "./storage"; export class AsnSerializer { static serialize(obj) { if (obj instanceof asn1js.BaseBlock) { return obj.toBER(false); } return this.toASN(obj).toBER(false); } static toASN(obj) { if (obj && typeof obj === "object" && isConvertible(obj)) { return obj.toASN(); } if (!(obj && typeof obj === "object")) { throw new TypeError("Parameter 1 should be type of Object."); } const target = obj.constructor; const schema = schemaStorage.get(target); schemaStorage.cache(target); let asn1Value = []; if (schema.itemType) { if (!Array.isArray(obj)) { throw new TypeError("Parameter 1 should be type of Array."); } if (typeof schema.itemType === "number") { const converter = converters.defaultConverter(schema.itemType); if (!converter) { throw new Error(`Cannot get default converter for array item of ${target.name} ASN1 schema`); } asn1Value = obj.map((o) => converter.toASN(o)); } else { asn1Value = obj.map((o) => this.toAsnItem({ type: schema.itemType }, "[]", target, o)); } } else { for (const key in schema.items) { const schemaItem = schema.items[key]; const objProp = obj[key]; if (objProp === undefined || schemaItem.defaultValue === objProp || (typeof schemaItem.defaultValue === "object" && typeof objProp === "object" && isArrayEqual(this.serialize(schemaItem.defaultValue), this.serialize(objProp)))) { continue; } const asn1Item = AsnSerializer.toAsnItem(schemaItem, key, target, objProp); if (typeof schemaItem.context === "number") { if (schemaItem.implicit) { if (!schemaItem.repeated && (typeof schemaItem.type === "number" || isConvertible(schemaItem.type))) { const value = {}; value.valueHex = asn1Item instanceof asn1js.Null ? asn1Item.valueBeforeDecodeView : asn1Item.valueBlock.toBER(); asn1Value.push(new asn1js.Primitive({ optional: schemaItem.optional, idBlock: { tagClass: 3, tagNumber: schemaItem.context, }, ...value, })); } else { asn1Value.push(new asn1js.Constructed({ optional: schemaItem.optional, idBlock: { tagClass: 3, tagNumber: schemaItem.context, }, value: asn1Item.valueBlock.value, })); } } else { asn1Value.push(new asn1js.Constructed({ optional: schemaItem.optional, idBlock: { tagClass: 3, tagNumber: schemaItem.context, }, value: [asn1Item], })); } } else if (schemaItem.repeated) { asn1Value = asn1Value.concat(asn1Item); } else { asn1Value.push(asn1Item); } } } let asnSchema; switch (schema.type) { case AsnTypeTypes.Sequence: asnSchema = new asn1js.Sequence({ value: asn1Value }); break; case AsnTypeTypes.Set: asnSchema = new asn1js.Set({ value: asn1Value }); break; case AsnTypeTypes.Choice: if (!asn1Value[0]) { throw new Error(`Schema '${target.name}' has wrong data. Choice cannot be empty.`); } asnSchema = asn1Value[0]; break; } return asnSchema; } static toAsnItem(schemaItem, key, target, objProp) { let asn1Item; if (typeof schemaItem.type === "number") { const converter = schemaItem.converter; if (!converter) { throw new Error(`Property '${key}' doesn't have converter for type ${AsnPropTypes[schemaItem.type]} in schema '${target.name}'`); } if (schemaItem.repeated) { if (!Array.isArray(objProp)) { throw new TypeError("Parameter 'objProp' should be type of Array."); } const items = Array.from(objProp, (element) => converter.toASN(element)); const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set; asn1Item = new Container({ value: items, }); } else { asn1Item = converter.toASN(objProp); } } else { if (schemaItem.repeated) { if (!Array.isArray(objProp)) { throw new TypeError("Parameter 'objProp' should be type of Array."); } const items = Array.from(objProp, (element) => this.toASN(element)); const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set; asn1Item = new Container({ value: items, }); } else { asn1Item = this.toASN(objProp); } } return asn1Item; } }