UNPKG

asn1-ts

Version:

ASN.1 encoding and decoding, including BER, CER, and DER.

785 lines (784 loc) 26.9 kB
import { ASN1Element, BERElement, CERElement, DERElement, ASN1TagClass, ASN1Construction, ASN1UniversalType, ASN1ConstructionError, CharacterString, } from "./index.mjs"; export function hasTag(tagClass, tagNumber) { return function (index, elements) { const el = elements[index]; return ((el.tagClass === tagClass) && (el.tagNumber === tagNumber)); }; } export function hasAnyTag() { return true; } export function hasTagClass(tagClass) { return function (index, elements) { return (elements[index].tagClass === tagClass); }; } export function hasTagNumberIn(tagNumbers) { return function (index, elements) { return tagNumbers.some((tn) => tn === elements[index].tagNumber); }; } export function and(...fns) { return function (index, elements) { return fns.every((fn) => fn(index, elements)); }; } export function or(...fns) { return function (index, elements) { return fns.some((fn) => fn(index, elements)); }; } export function not(fn) { return function (index, elements) { return !fn(index, elements); }; } export function tagClassName(tagClass) { switch (tagClass) { case (ASN1TagClass.universal): return "UNIVERSAL"; case (ASN1TagClass.context): return "CONTEXT"; case (ASN1TagClass.application): return "APPLICATION"; case (ASN1TagClass.private): return "PRIVATE"; default: { throw new Error(`Unrecognized ASN.1 Tag Class ${tagClass}.`); } } } export function deepEq(value1, value2) { try { if (value1 === value2) { return true; } if (((typeof value1 === "bigint") || (typeof value1 === "number")) && ((typeof value2 === "bigint") || (typeof value2 === "number"))) { return (BigInt(value1) === BigInt(value2)); } return (JSON.stringify(value1) === JSON.stringify(value2)); } catch { return false; } } export function isDefault(defaultValue) { return function (actualValue) { return deepEq(defaultValue, actualValue); }; } export function present(value) { return (value !== undefined); } export function _encode_explicit(class_, tag, encoderGetter, outer) { return function (value, elGetter) { const ret = outer(value, outer); ret.sequence = [encoderGetter()(value, elGetter)]; ret.construction = ASN1Construction.constructed; if (class_) { ret.tagClass = class_; } if (typeof tag !== "undefined") { ret.tagNumber = tag; } return ret; }; } export function _decode_explicit(decoderGetter) { return (el) => decoderGetter()(el.inner); } export function _encode_implicit(class_, tag, encoderGetter, _) { return function (value, elGetter) { const ret = encoderGetter()(value, elGetter); if (class_) { ret.tagClass = class_; } if (typeof tag !== "undefined") { ret.tagNumber = tag; } return ret; }; } export function _decode_implicit(decoderGetter) { return (el) => decoderGetter()(el); } export function _tagClass(class_, encoderGetter) { const el = encoderGetter()(); el.tagClass = class_; return el; } export function _construction(con, encoderGetter) { const el = encoderGetter()(); el.construction = con; return el; } export function _tagNumber(tag, encoderGetter) { const el = encoderGetter()(); el.tagNumber = tag; return el; } export const BER = () => new BERElement(); export const CER = () => new CERElement(); export const DER = () => new DERElement(); export const _encodeBitString = (value, elGetter) => { const el = elGetter(value, elGetter); el.bitString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.bitString; return el; }; export const _decodeBitString = (el) => { return el.bitString; }; export const _encodeBoolean = (value, elGetter) => { const el = elGetter(value, elGetter); el.boolean = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.boolean; return el; }; export const _decodeBoolean = (el) => { return el.boolean; }; export const _encodeUnrestrictedCharacterString = (value, elGetter) => { const el = elGetter(value, elGetter); el.characterString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.characterString; return el; }; export const _decodeUnrestrictedCharacterString = (el) => { return el.characterString; }; export const _encodeDate = (value, elGetter) => { const el = elGetter(value, elGetter); el.date = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.date; return el; }; export const _decodeDate = (el) => { return el.date; }; export const _encodeDateTime = (value, elGetter) => { const el = elGetter(value, elGetter); el.dateTime = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.dateTime; return el; }; export const _decodeDateTime = (el) => { return el.dateTime; }; export const _encodeDuration = (value, elGetter) => { const el = elGetter(value, elGetter); el.duration = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.duration; return el; }; export const _decodeDuration = (el) => { return el.duration; }; export const _encodeEmbeddedPDV = (value, elGetter) => { const el = elGetter(value, elGetter); el.embeddedPDV = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.embeddedPDV; return el; }; export const _decodeEmbeddedPDV = (el) => { return el.embeddedPDV; }; export const _encodeEnumerated = (value, elGetter) => { const el = elGetter(value, elGetter); el.enumerated = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.enumerated; return el; }; export const _decodeEnumerated = (el) => { return el.enumerated; }; export const _encodeExternal = (value, elGetter) => { const el = elGetter(value, elGetter); el.external = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.external; return el; }; export const _decodeExternal = (el) => { return el.external; }; export const _encodeInstanceOf = (value, elGetter) => { const el = elGetter(value, elGetter); el.external = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.external; return el; }; export const _decodeInstanceOf = (el) => { return el.external; }; export const _encodeInteger = (value, elGetter) => { const el = elGetter(value, elGetter); el.integer = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.integer; return el; }; export const _decodeInteger = (el) => { return el.integer; }; export const _encodeIRI = (value, elGetter) => { const el = elGetter(value, elGetter); el.oidIRI = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.oidIRI; return el; }; export const _decodeIRI = (el) => { return el.oidIRI; }; export const _encodeNull = (value, elGetter) => { const el = elGetter(value, elGetter); el.value = new Uint8Array(0); el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.nill; return el; }; export const _decodeNull = () => { return null; }; export const _encodeObjectIdentifier = (value, elGetter) => { const el = elGetter(value, elGetter); el.objectIdentifier = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.objectIdentifier; return el; }; export const _decodeObjectIdentifier = (el) => { return el.objectIdentifier; }; export const _encodeOctetString = (value, elGetter) => { const el = elGetter(value, elGetter); el.octetString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.octetString; return el; }; export const _decodeOctetString = (el) => { return el.octetString; }; export const _encodeReal = (value, elGetter) => { const el = elGetter(value, elGetter); el.real = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.realNumber; return el; }; export const _decodeReal = (el) => { return el.real; }; export const _encodeRelativeIRI = (value, elGetter) => { const el = elGetter(value, elGetter); el.relativeOIDIRI = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.roidIRI; return el; }; export const _decodeRelativeIRI = (el) => { return el.relativeOIDIRI; }; export const _encodeRelativeOID = (value, elGetter) => { const el = elGetter(value, elGetter); el.relativeObjectIdentifier = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.relativeOID; return el; }; export const _decodeRelativeOID = (el) => { return el.relativeObjectIdentifier; }; export const _encodeSequence = (value, elGetter) => { const el = elGetter(value, elGetter); el.sequence = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.sequence; return el; }; export const _decodeSequence = (el) => { return el.sequence; }; export const _encodeSet = (value, elGetter) => { const el = elGetter(value, elGetter); el.set = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.set; return el; }; export const _decodeSet = (el) => { return el.set; }; export const _encodeTime = (value, elGetter) => { const el = elGetter(value, elGetter); el.time = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.time; return el; }; export const _decodeTime = (el) => { return el.time; }; export const _encodeTimeOfDay = (value, elGetter) => { const el = elGetter(value, elGetter); el.timeOfDay = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.timeOfDay; return el; }; export const _decodeTimeOfDay = (el) => { return el.timeOfDay; }; export const _encodeBMPString = (value, elGetter) => { const el = elGetter(value, elGetter); el.bmpString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.bmpString; return el; }; export const _decodeBMPString = (el) => { return el.bmpString; }; export const _encodeGeneralString = (value, elGetter) => { const el = elGetter(value, elGetter); el.generalString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.generalString; return el; }; export const _decodeGeneralString = (el) => { return el.generalString; }; export const _encodeGraphicString = (value, elGetter) => { const el = elGetter(value, elGetter); el.graphicString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.graphicString; return el; }; export const _decodeGraphicString = (el) => { return el.graphicString; }; export const _encodeIA5String = (value, elGetter) => { const el = elGetter(value, elGetter); el.ia5String = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.ia5String; return el; }; export const _decodeIA5String = (el) => { return el.ia5String; }; export const _encodeISO646String = (value, elGetter) => { const el = elGetter(value, elGetter); el.ia5String = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.ia5String; return el; }; export const _decodeISO646String = (el) => { return el.ia5String; }; export const _encodeNumericString = (value, elGetter) => { const el = elGetter(value, elGetter); el.numericString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.numericString; return el; }; export const _decodeNumericString = (el) => { return el.numericString; }; export const _encodePrintableString = (value, elGetter) => { const el = elGetter(value, elGetter); el.printableString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.printableString; return el; }; export const _decodePrintableString = (el) => { return el.printableString; }; export const _encodeTeletexString = (value, elGetter) => { const el = elGetter(value, elGetter); el.teletexString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.teletexString; return el; }; export const _decodeTeletexString = (el) => { return el.teletexString; }; export const _encodeT61String = (value, elGetter) => { const el = elGetter(value, elGetter); el.teletexString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.teletexString; return el; }; export const _decodeT61String = (el) => { return el.teletexString; }; export const _encodeUniversalString = (value, elGetter) => { const el = elGetter(value, elGetter); el.universalString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.universalString; return el; }; export const _decodeUniversalString = (el) => { return el.universalString; }; export const _encodeUTF8String = (value, elGetter) => { const el = elGetter(value, elGetter); el.utf8String = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.utf8String; return el; }; export const _decodeUTF8String = (el) => { return el.utf8String; }; export const _encodeVideotexString = (value, elGetter) => { const el = elGetter(value, elGetter); el.videotexString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.videotexString; return el; }; export const _decodeVideotexString = (el) => { return el.videotexString; }; export const _encodeVisibleString = (value, elGetter) => { const el = elGetter(value, elGetter); el.visibleString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.visibleString; return el; }; export const _decodeVisibleString = (el) => { return el.visibleString; }; export const _encodeGeneralizedTime = (value, elGetter) => { const el = elGetter(value, elGetter); el.generalizedTime = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.generalizedTime; return el; }; export const _decodeGeneralizedTime = (el) => { return el.generalizedTime; }; export const _encodeUTCTime = (value, elGetter) => { const el = elGetter(value, elGetter); el.utcTime = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.utcTime; return el; }; export const _decodeUTCTime = (el) => { return el.utcTime; }; export const _encodeObjectDescriptor = (value, elGetter) => { const el = elGetter(value, elGetter); el.objectDescriptor = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.objectDescriptor; return el; }; export const _decodeObjectDescriptor = (el) => { return el.objectDescriptor; }; export const _encodeAny = (value) => { return value; }; export const _decodeAny = (el) => { return el; }; export class ComponentSpec { constructor(name, optional, selector, groupIndex, versionNumber) { this.name = name; this.optional = optional; this.selector = selector; this.groupIndex = groupIndex; this.versionNumber = versionNumber; } } export function _parse_set(set, decodingCallbacks, rootComponentTypeList1, extensionAdditionsList, rootComponentTypeList2, unrecognizedExtensionHandler = () => { }, maximumElements) { const rootComponents = rootComponentTypeList1.concat(rootComponentTypeList2); const components = rootComponents.concat(extensionAdditionsList); const elements = set.set; { const calculatedMaximumElements = (maximumElements === undefined) ? (components.length + 100) : maximumElements; if (elements.length > calculatedMaximumElements) { throw new Error(`SET '${set.name}' contained ${elements.length} elements, which exceeds the ` + `limit of ${calculatedMaximumElements} permitted elements. This may be a result ` + "of a Denial-of-Service (DoS) attack."); } } { const encounteredTags = new Set([]); for (const e of elements) { const tag = `${e.tagClass} ${e.tagNumber}`; if (encounteredTags.has(tag)) { throw new Error(`Duplicate tag '${tagClassName(e.tagClass)} ${e.tagNumber}' in SET '${set.name}'.`); } encounteredTags.add(tag); } } const encounteredComponents = new Set([]); const encounteredExtensionGroups = new Set([]); for (let i = 0; i < elements.length; i++) { const e = elements[i]; const spec = components .find((cs) => cs.selector(i, elements)); if (!spec) { unrecognizedExtensionHandler(e); continue; } if (encounteredComponents.has(spec.name)) { throw new Error(`SET '${set.name}' contained more than one '${spec.name}' component.`); } encounteredComponents.add(spec.name); if (spec.groupIndex !== undefined) { encounteredExtensionGroups.add(spec.groupIndex); } e.name = spec.name; if (spec.name in decodingCallbacks) { decodingCallbacks[spec.name](e, spec.name); } else { unrecognizedExtensionHandler(e); } } const missingRequiredComponents = rootComponents .filter((c) => (!c.optional && !encounteredComponents.has(c.name))) .map((c) => c.name); for (const exg of encounteredExtensionGroups.values()) { for (const c of extensionAdditionsList) { if (!((c.groupIndex === exg) && !c.optional && !encounteredComponents.has(c.name))) { continue; } missingRequiredComponents.push(c.name); } } if (missingRequiredComponents.length > 0) { throw new Error(`SET '${set.name}' missing these required components: ${missingRequiredComponents.join(", ")}.`); } } function _parse_component_type_list(componentTypeList, decodingCallbacks, elements, isExtensionAdditionsList) { let e = 0; let s = 0; let currentGroup = undefined; while (s < componentTypeList.length) { const element = elements[e]; const spec = componentTypeList[s]; if (!element) { if (spec.optional) { s++; continue; } else if (isExtensionAdditionsList) { if ((spec.groupIndex !== undefined) && (spec.groupIndex <= (currentGroup || Infinity))) { throw new ASN1ConstructionError(`Missing required extension '${spec.name}' in SEQUENCE.`); } else { s++; continue; } } else { throw new ASN1ConstructionError(`Missing required component '${spec.name}' in SEQUENCE.`); } } if (spec.selector(e, elements)) { element.name = spec.name; if (spec.name in decodingCallbacks) { decodingCallbacks[spec.name](element, spec.name); } if (spec.groupIndex !== undefined) { currentGroup = spec.groupIndex; } e++; } else if (!spec.optional) { throw new Error(`Component '${spec.name}' missing from SEQUENCE.`); } s++; } return e; } export function _get_possible_initial_components(componentTypeList) { let i = 0; while (i < componentTypeList.length) { if (!(componentTypeList[i++].optional)) { break; } } return componentTypeList.slice(0, i); } function _parse_sequence_with_trailing_rctl(seq, decodingCallbacks, rootComponentTypeList1, extensionAdditionsList, rootComponentTypeList2, unrecognizedExtensionHandler = () => { }) { const elements = seq.sequence; const startOfExtensions = _parse_component_type_list(rootComponentTypeList1, decodingCallbacks, elements, false); const possibleInitialRCTL2Components = _get_possible_initial_components(rootComponentTypeList2); const rctl2EntirelyOptional = rootComponentTypeList2.every((ct) => ct.optional); const extensionsOnwards = elements.slice(startOfExtensions); let numberOfExtensionElements = extensionsOnwards .findIndex((_, i) => possibleInitialRCTL2Components .some((pirctl2c) => pirctl2c.selector(i, extensionsOnwards))); if (numberOfExtensionElements === -1) { if (!rctl2EntirelyOptional) { throw new Error(`Trailing root component type list for SEQUENCE '${seq.name}' not found.`); } numberOfExtensionElements = elements.length - startOfExtensions; } const startOfRCTL2 = (startOfExtensions + numberOfExtensionElements); const numberOfExtensionsRead = _parse_component_type_list(extensionAdditionsList, decodingCallbacks, elements.slice(startOfExtensions, startOfRCTL2), true); const endOfRecognizedExtensions = (startOfExtensions + numberOfExtensionsRead); for (let i = endOfRecognizedExtensions; i < startOfRCTL2; i++) { unrecognizedExtensionHandler(elements[i]); } const numberOfRCTL2ElementsRead = _parse_component_type_list(rootComponentTypeList2, decodingCallbacks, elements.slice(startOfRCTL2), false); if (startOfRCTL2 + numberOfRCTL2ElementsRead !== elements.length) { throw new Error(`SEQUENCE '${seq.name}' had excess elements at the end.`); } } function _parse_sequence_without_trailing_rctl(seq, decodingCallbacks, rootComponentTypeList1, extensionAdditionsList, unrecognizedExtensionHandler = () => { }) { const elements = seq.sequence; const startOfExtensions = _parse_component_type_list(rootComponentTypeList1, decodingCallbacks, elements, false); const numberOfExtensionsRead = _parse_component_type_list(extensionAdditionsList, decodingCallbacks, elements.slice(startOfExtensions), true); const endOfRecognizedExtensions = (startOfExtensions + numberOfExtensionsRead); for (let i = endOfRecognizedExtensions; i < elements.length; i++) { unrecognizedExtensionHandler(elements[i]); } } export function _parse_sequence(seq, decodingCallbacks, rootComponentTypeList1, extensionAdditionsList, rootComponentTypeList2, unrecognizedExtensionHandler = () => { }) { if (rootComponentTypeList2.length > 0) { _parse_sequence_with_trailing_rctl(seq, decodingCallbacks, rootComponentTypeList1, extensionAdditionsList, rootComponentTypeList2, unrecognizedExtensionHandler); } else { _parse_sequence_without_trailing_rctl(seq, decodingCallbacks, rootComponentTypeList1, extensionAdditionsList, unrecognizedExtensionHandler); } } export function _encode_choice(choices, elGetter) { return function (value) { if (value instanceof ASN1Element) { return value; } const key = Object.keys(value)[0]; if (!key) { throw new Error("Empty choice value object."); } const encoder = choices[key]; if (!encoder) { throw new Error(`Unrecognized alternative '${String(key)}'.`); } return encoder(value[key], elGetter); }; } export function _decode_inextensible_choice(choices) { const choiceMap = new Map(Object.entries(choices)); return function (el) { const result = choiceMap.get(`${tagClassName(el.tagClass)} ${el.tagNumber}`); if (!result) { const star = choiceMap.get("*"); if (star) { const ret = {}; ret[star[0]] = star[1](el); return ret; } else { throw new Error(`Unrecognized CHOICE tag '${tagClassName(el.tagClass)} ${el.tagNumber}'.`); } } const [name, decoder] = result; const ret = {}; ret[name] = decoder(el); return ret; }; } export function _decode_extensible_choice(choices) { const choiceMap = new Map(Object.entries(choices)); return function (el) { const result = choiceMap.get(`${tagClassName(el.tagClass)} ${el.tagNumber}`); if (!result) { return el; } const [name, decoder] = result; const ret = {}; ret[name] = decoder(el); return ret; }; } export function _decodeSequenceOf(decoderGetter) { const decoder = decoderGetter(); return function (el) { const seq = el.sequence; const result = new Array(seq.length); for (let i = 0; i < seq.length; i++) { result[i] = decoder(seq[i]); } return result; }; } export function _encodeSequenceOf(encoderGetter, outerElGetter) { const encoder = encoderGetter(); return function (value) { const el = outerElGetter(value, outerElGetter); const seq = new Array(value.length); for (let i = 0; i < value.length; i++) { seq[i] = encoder(value[i], outerElGetter); } el.sequence = seq; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.sequence; return el; }; } export function _decodeSetOf(decoderGetter) { const decoder = decoderGetter(); return function (el) { const setOf = el.setOf; const result = new Array(setOf.length); for (let i = 0; i < setOf.length; i++) { result[i] = decoder(setOf[i]); } return result; }; } export function _encodeSetOf(encoderGetter, outerElGetter) { const encoder = encoderGetter(); return function (value) { const el = outerElGetter(value, outerElGetter); const seq = new Array(value.length); for (let i = 0; i < value.length; i++) { seq[i] = encoder(value[i], outerElGetter); } el.setOf = seq; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.set; return el; }; } export const _encodeBigInt = (value, elGetter) => { const el = elGetter(value, elGetter); el.octetString = value; el.tagClass = ASN1TagClass.universal; el.tagNumber = ASN1UniversalType.integer; return el; }; export const _decodeBigInt = (el) => { return el.octetString; };