157 lines
5.8 KiB
JavaScript
157 lines
5.8 KiB
JavaScript
import * as asn1js from "asn1js";
|
|
import { AsnPropTypes, AsnTypeTypes } from "./enums";
|
|
import { isConvertible } from "./helper";
|
|
export class AsnSchemaStorage {
|
|
constructor() {
|
|
this.items = new WeakMap();
|
|
}
|
|
has(target) {
|
|
return this.items.has(target);
|
|
}
|
|
get(target, checkSchema = false) {
|
|
const schema = this.items.get(target);
|
|
if (!schema) {
|
|
throw new Error(`Cannot get schema for '${target.prototype.constructor.name}' target`);
|
|
}
|
|
if (checkSchema && !schema.schema) {
|
|
throw new Error(`Schema '${target.prototype.constructor.name}' doesn't contain ASN.1 schema. Call 'AsnSchemaStorage.cache'.`);
|
|
}
|
|
return schema;
|
|
}
|
|
cache(target) {
|
|
const schema = this.get(target);
|
|
if (!schema.schema) {
|
|
schema.schema = this.create(target, true);
|
|
}
|
|
}
|
|
createDefault(target) {
|
|
const schema = {
|
|
type: AsnTypeTypes.Sequence,
|
|
items: {},
|
|
};
|
|
const parentSchema = this.findParentSchema(target);
|
|
if (parentSchema) {
|
|
Object.assign(schema, parentSchema);
|
|
schema.items = Object.assign({}, schema.items, parentSchema.items);
|
|
}
|
|
return schema;
|
|
}
|
|
create(target, useNames) {
|
|
const schema = this.items.get(target) || this.createDefault(target);
|
|
const asn1Value = [];
|
|
for (const key in schema.items) {
|
|
const item = schema.items[key];
|
|
const name = useNames ? key : "";
|
|
let asn1Item;
|
|
if (typeof item.type === "number") {
|
|
const Asn1TypeName = AsnPropTypes[item.type];
|
|
const Asn1Type = asn1js[Asn1TypeName];
|
|
if (!Asn1Type) {
|
|
throw new Error(`Cannot get ASN1 class by name '${Asn1TypeName}'`);
|
|
}
|
|
asn1Item = new Asn1Type({ name });
|
|
}
|
|
else if (isConvertible(item.type)) {
|
|
const instance = new item.type();
|
|
asn1Item = instance.toSchema(name);
|
|
}
|
|
else if (item.optional) {
|
|
const itemSchema = this.get(item.type);
|
|
if (itemSchema.type === AsnTypeTypes.Choice) {
|
|
asn1Item = new asn1js.Any({ name });
|
|
}
|
|
else {
|
|
asn1Item = this.create(item.type, false);
|
|
asn1Item.name = name;
|
|
}
|
|
}
|
|
else {
|
|
asn1Item = new asn1js.Any({ name });
|
|
}
|
|
const optional = !!item.optional || item.defaultValue !== undefined;
|
|
if (item.repeated) {
|
|
asn1Item.name = "";
|
|
const Container = item.repeated === "set" ? asn1js.Set : asn1js.Sequence;
|
|
asn1Item = new Container({
|
|
name: "",
|
|
value: [
|
|
new asn1js.Repeated({
|
|
name,
|
|
value: asn1Item,
|
|
}),
|
|
],
|
|
});
|
|
}
|
|
if (item.context !== null && item.context !== undefined) {
|
|
if (item.implicit) {
|
|
if (typeof item.type === "number" || isConvertible(item.type)) {
|
|
const Container = item.repeated ? asn1js.Constructed : asn1js.Primitive;
|
|
asn1Value.push(new Container({
|
|
name,
|
|
optional,
|
|
idBlock: {
|
|
tagClass: 3,
|
|
tagNumber: item.context,
|
|
},
|
|
}));
|
|
}
|
|
else {
|
|
this.cache(item.type);
|
|
const isRepeated = !!item.repeated;
|
|
let value = !isRepeated ? this.get(item.type, true).schema : asn1Item;
|
|
value =
|
|
"valueBlock" in value
|
|
? value.valueBlock.value
|
|
: value.value;
|
|
asn1Value.push(new asn1js.Constructed({
|
|
name: !isRepeated ? name : "",
|
|
optional,
|
|
idBlock: {
|
|
tagClass: 3,
|
|
tagNumber: item.context,
|
|
},
|
|
value: value,
|
|
}));
|
|
}
|
|
}
|
|
else {
|
|
asn1Value.push(new asn1js.Constructed({
|
|
optional,
|
|
idBlock: {
|
|
tagClass: 3,
|
|
tagNumber: item.context,
|
|
},
|
|
value: [asn1Item],
|
|
}));
|
|
}
|
|
}
|
|
else {
|
|
asn1Item.optional = optional;
|
|
asn1Value.push(asn1Item);
|
|
}
|
|
}
|
|
switch (schema.type) {
|
|
case AsnTypeTypes.Sequence:
|
|
return new asn1js.Sequence({ value: asn1Value, name: "" });
|
|
case AsnTypeTypes.Set:
|
|
return new asn1js.Set({ value: asn1Value, name: "" });
|
|
case AsnTypeTypes.Choice:
|
|
return new asn1js.Choice({ value: asn1Value, name: "" });
|
|
default:
|
|
throw new Error(`Unsupported ASN1 type in use`);
|
|
}
|
|
}
|
|
set(target, schema) {
|
|
this.items.set(target, schema);
|
|
return this;
|
|
}
|
|
findParentSchema(target) {
|
|
const parent = Object.getPrototypeOf(target);
|
|
if (parent) {
|
|
const schema = this.items.get(parent);
|
|
return schema || this.findParentSchema(parent);
|
|
}
|
|
return null;
|
|
}
|
|
}
|