UNPKG

@estos/asn1ts

Version:

ASN1.ts is a cross platform ASN1 library written in typescript. It supports encoding and (schema supported) decoding of ber encoded asn1 structures.

1,330 lines (1,308 loc) 147 kB
/*! * Copyright (c) 2014, GMO GlobalSign * Copyright (c) 2015-2022, Peculiar Ventures * Copyright (c) 2022, estos GmbH (extensions missing features see README.md - What has been added by estos) * All rights reserved. * * Author 2014-2019, Yury Strozhevsky * Author 2019-2022, Peculiar Ventures * Author from 2022, estos GmbH * * estos created a fork of the library, mainly written by Yury Strozhevsky and * Peculiar Ventures, in 2022 to add extensions, missing features. Details about those in the README.md * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ import * as pvtsutils from 'pvtsutils'; import * as pvutils from 'pvutils'; function assertBigInt() { if (typeof BigInt === "undefined") { throw new Error("BigInt is not defined. Your environment doesn't implement BigInt."); } } function concat(buffers) { let outputLength = 0; let prevLength = 0; for (let i = 0; i < buffers.length; i++) { const buffer = buffers[i]; outputLength += buffer.byteLength; } const retView = new Uint8Array(outputLength); for (let i = 0; i < buffers.length; i++) { const buffer = buffers[i]; retView.set(new Uint8Array(buffer), prevLength); prevLength += buffer.byteLength; } return retView.buffer; } function checkBufferParams(baseBlock, inputBuffer, inputOffset, inputLength) { if (!(inputBuffer instanceof Uint8Array)) { baseBlock.error = "Wrong parameter: inputBuffer must be 'Uint8Array'"; return false; } if (!inputBuffer.byteLength) { baseBlock.error = "Wrong parameter: inputBuffer has zero length"; return false; } if (inputOffset < 0) { baseBlock.error = "Wrong parameter: inputOffset less than zero"; return false; } if (inputLength < 0) { baseBlock.error = "Wrong parameter: inputLength less than zero"; return false; } if ((inputBuffer.byteLength - inputOffset - inputLength) < 0) { baseBlock.error = "End of input reached before message was fully decoded (inconsistent offset and length values)"; return false; } return true; } var ESpecialNumber; (function (ESpecialNumber) { ESpecialNumber[ESpecialNumber["nan"] = 1] = "nan"; ESpecialNumber[ESpecialNumber["infinite"] = 2] = "infinite"; })(ESpecialNumber || (ESpecialNumber = {})); function getNumberParts(x) { if (isNaN(x)) { return { mantissa: -6755399441055744, exponent: 972, sign: 1, special: ESpecialNumber.nan }; } const float = new Float64Array(1); const bytes = new Uint8Array(float.buffer); float[0] = x; const msb = bytes[7] >> 7; const sign = msb ? -1 : 1; if (!isFinite(x)) { return { mantissa: 4503599627370496, exponent: 972, sign, special: ESpecialNumber.infinite }; } let exponent = ((bytes[7] & 0x7f) << 4 | bytes[6] >> 4) - 0x3ff; bytes[7] = 0x3f; bytes[6] |= 0xf0; let mantissa = float[0]; while (mantissa >= 1) { mantissa /= 2; exponent++; } return { sign, exponent, mantissa, }; } function getSignedFromUnsigned(x) { return x << 24 >> 24; } class ViewWriter { constructor() { this.items = []; } write(buf) { this.items.push(buf); } final() { return concat(this.items); } } const powers2 = [new Uint8Array([1])]; const digitsString = "0123456789"; const VALUE_HEX_VIEW = "valueHexView"; const IS_HEX_ONLY = "isHexOnly"; const ID_BLOCK = "idBlock"; const TAG_CLASS = "tagClass"; const TAG_NUMBER = "tagNumber"; const IS_CONSTRUCTED = "isConstructed"; const FROM_BER = "fromBER"; const TO_BER = "toBER"; const EMPTY_STRING = ""; const EMPTY_BUFFER = new ArrayBuffer(0); const EMPTY_VIEW = new Uint8Array(0); const END_OF_CONTENT_NAME = "EndOfContent"; const OCTET_STRING_NAME = "OCTET STRING"; const BIT_STRING_NAME = "BIT STRING"; function HexBlock(BaseClass) { var _a; return _a = class Some extends BaseClass { get valueHex() { return this.valueHexView.slice().buffer; } set valueHex(value) { this.valueHexView = new Uint8Array(value); } constructor(...args) { var _b; super(...args); const params = args[0] || {}; this.isHexOnly = (_b = params.isHexOnly) !== null && _b !== void 0 ? _b : false; this.valueHexView = params.valueHex ? pvtsutils.BufferSourceConverter.toUint8Array(params.valueHex) : EMPTY_VIEW; } fromBER(inputBuffer, inputOffset, inputLength) { const view = inputBuffer instanceof ArrayBuffer ? new Uint8Array(inputBuffer) : inputBuffer; if (!checkBufferParams(this, view, inputOffset, inputLength)) { return -1; } const endLength = inputOffset + inputLength; this.valueHexView = view.subarray(inputOffset, endLength); if (!this.valueHexView.length) { this.warnings.push("Zero buffer length"); return inputOffset; } this.blockLength = inputLength; return endLength; } toBER(sizeOnly = false) { if (!this.isHexOnly) { this.error = "Flag 'isHexOnly' is not set, abort"; return EMPTY_BUFFER; } if (sizeOnly) { return new ArrayBuffer(this.valueHexView.byteLength); } return (this.valueHexView.byteLength === this.valueHexView.buffer.byteLength) ? this.valueHexView.buffer : this.valueHexView.slice().buffer; } toJSON() { return { ...super.toJSON(), isHexOnly: this.isHexOnly, valueHex: pvtsutils.Convert.ToHex(this.valueHexView), }; } }, _a.NAME = "hexBlock", _a; } class LocalBaseBlock { static blockName() { return this.NAME; } get valueBeforeDecode() { return this.valueBeforeDecodeView.slice().buffer; } set valueBeforeDecode(value) { this.valueBeforeDecodeView = new Uint8Array(value); } constructor({ blockLength = 0, error = EMPTY_STRING, warnings = [], valueBeforeDecode = EMPTY_VIEW, } = {}) { this.blockLength = blockLength; this.error = error; this.warnings = warnings; this.valueBeforeDecodeView = pvtsutils.BufferSourceConverter.toUint8Array(valueBeforeDecode); } toJSON() { return { blockName: this.constructor.NAME, blockLength: this.blockLength, error: this.error, warnings: this.warnings, valueBeforeDecode: pvtsutils.Convert.ToHex(this.valueBeforeDecodeView), }; } } LocalBaseBlock.NAME = "LocalBaseBlock"; LocalBaseBlock.defaultIDs = { tagClass: -1, tagNumber: -1 }; class ValueBlock extends LocalBaseBlock { fromBER(inputBuffer, inputOffset, inputLength) { throw TypeError("User need to make a specific function in a class which extends 'ValueBlock'"); } toBER(sizeOnly, writer) { throw TypeError("User need to make a specific function in a class which extends 'ValueBlock'"); } } ValueBlock.NAME = "valueBlock"; var ETagClass; (function (ETagClass) { ETagClass[ETagClass["UNKNOWN"] = -1] = "UNKNOWN"; ETagClass[ETagClass["UNIVERSAL"] = 1] = "UNIVERSAL"; ETagClass[ETagClass["APPLICATION"] = 2] = "APPLICATION"; ETagClass[ETagClass["CONTEXT_SPECIFIC"] = 3] = "CONTEXT_SPECIFIC"; ETagClass[ETagClass["PRIVATE"] = 4] = "PRIVATE"; })(ETagClass || (ETagClass = {})); var EUniversalTagNumber; (function (EUniversalTagNumber) { EUniversalTagNumber[EUniversalTagNumber["unknown"] = -1] = "unknown"; EUniversalTagNumber[EUniversalTagNumber["EndOfContent"] = 0] = "EndOfContent"; EUniversalTagNumber[EUniversalTagNumber["Boolean"] = 1] = "Boolean"; EUniversalTagNumber[EUniversalTagNumber["Integer"] = 2] = "Integer"; EUniversalTagNumber[EUniversalTagNumber["BitString"] = 3] = "BitString"; EUniversalTagNumber[EUniversalTagNumber["OctetString"] = 4] = "OctetString"; EUniversalTagNumber[EUniversalTagNumber["Null"] = 5] = "Null"; EUniversalTagNumber[EUniversalTagNumber["ObjectIdentifier"] = 6] = "ObjectIdentifier"; EUniversalTagNumber[EUniversalTagNumber["Real"] = 9] = "Real"; EUniversalTagNumber[EUniversalTagNumber["Enumerated"] = 10] = "Enumerated"; EUniversalTagNumber[EUniversalTagNumber["Utf8String"] = 12] = "Utf8String"; EUniversalTagNumber[EUniversalTagNumber["RelativeObjectIdentifier"] = 13] = "RelativeObjectIdentifier"; EUniversalTagNumber[EUniversalTagNumber["TIME"] = 14] = "TIME"; EUniversalTagNumber[EUniversalTagNumber["Sequence"] = 16] = "Sequence"; EUniversalTagNumber[EUniversalTagNumber["Set"] = 17] = "Set"; EUniversalTagNumber[EUniversalTagNumber["NumericString"] = 18] = "NumericString"; EUniversalTagNumber[EUniversalTagNumber["PrintableString"] = 19] = "PrintableString"; EUniversalTagNumber[EUniversalTagNumber["TeletexString"] = 20] = "TeletexString"; EUniversalTagNumber[EUniversalTagNumber["VideotexString"] = 21] = "VideotexString"; EUniversalTagNumber[EUniversalTagNumber["IA5String"] = 22] = "IA5String"; EUniversalTagNumber[EUniversalTagNumber["UTCTime"] = 23] = "UTCTime"; EUniversalTagNumber[EUniversalTagNumber["GeneralizedTime"] = 24] = "GeneralizedTime"; EUniversalTagNumber[EUniversalTagNumber["GraphicString"] = 25] = "GraphicString"; EUniversalTagNumber[EUniversalTagNumber["VisibleString"] = 26] = "VisibleString"; EUniversalTagNumber[EUniversalTagNumber["GeneralString"] = 27] = "GeneralString"; EUniversalTagNumber[EUniversalTagNumber["UniversalString"] = 28] = "UniversalString"; EUniversalTagNumber[EUniversalTagNumber["CharacterString"] = 29] = "CharacterString"; EUniversalTagNumber[EUniversalTagNumber["BmpString"] = 30] = "BmpString"; EUniversalTagNumber[EUniversalTagNumber["DATE"] = 31] = "DATE"; EUniversalTagNumber[EUniversalTagNumber["TimeOfDay"] = 32] = "TimeOfDay"; EUniversalTagNumber[EUniversalTagNumber["DateTime"] = 33] = "DateTime"; EUniversalTagNumber[EUniversalTagNumber["Duration"] = 34] = "Duration"; EUniversalTagNumber[EUniversalTagNumber["Extension"] = 35] = "Extension"; })(EUniversalTagNumber || (EUniversalTagNumber = {})); function getTagClassAsText(tagClass) { switch (tagClass) { case ETagClass.UNIVERSAL: return "UNIVERSAL"; case ETagClass.APPLICATION: return "APPLICATION"; case ETagClass.CONTEXT_SPECIFIC: return "CONTEXT_SPECIFIC"; case ETagClass.PRIVATE: return "PRIVATE"; default: return `TAGCLASS(${tagClass})`; } } function getTagNumberAsText(tagNumber) { switch (tagNumber) { case EUniversalTagNumber.EndOfContent: return "EndOfContent"; case EUniversalTagNumber.Boolean: return "Boolean"; case EUniversalTagNumber.Integer: return "Integer"; case EUniversalTagNumber.BitString: return "BitString"; case EUniversalTagNumber.OctetString: return "OctetString"; case EUniversalTagNumber.Null: return "Null"; case EUniversalTagNumber.ObjectIdentifier: return "ObjectIdentifier"; case EUniversalTagNumber.Real: return "Real"; case EUniversalTagNumber.Enumerated: return "Enumerated"; case EUniversalTagNumber.Utf8String: return "Utf8String"; case EUniversalTagNumber.RelativeObjectIdentifier: return "RelativeObjectIdentifier"; case EUniversalTagNumber.TIME: return "TIME"; case EUniversalTagNumber.Sequence: return "Sequence"; case EUniversalTagNumber.Set: return "Set"; case EUniversalTagNumber.NumericString: return "NumericString"; case EUniversalTagNumber.PrintableString: return "PrintableString"; case EUniversalTagNumber.TeletexString: return "TeletexString"; case EUniversalTagNumber.VideotexString: return "VideotexString"; case EUniversalTagNumber.IA5String: return "IA5String"; case EUniversalTagNumber.UTCTime: return "UTCTime"; case EUniversalTagNumber.GeneralizedTime: return "GeneralizedTime"; case EUniversalTagNumber.GraphicString: return "GraphicString"; case EUniversalTagNumber.VisibleString: return "VisibleString"; case EUniversalTagNumber.GeneralString: return "GeneralString"; case EUniversalTagNumber.UniversalString: return "UniversalString"; case EUniversalTagNumber.CharacterString: return "CharacterString"; case EUniversalTagNumber.BmpString: return "BmpString"; case EUniversalTagNumber.DATE: return "DATE"; case EUniversalTagNumber.TimeOfDay: return "TimeOfDay"; case EUniversalTagNumber.DateTime: return "DateTime"; case EUniversalTagNumber.Duration: return "Duration"; case EUniversalTagNumber.Extension: return "Extension"; default: return `TAGNUMBER(${tagNumber})`; } } const typeStore = {}; class LocalIdentificationBlock extends HexBlock(LocalBaseBlock) { constructor({ idBlock = {}, } = {}) { var _a, _b, _c, _d, _e; super(); if (idBlock) { this.isHexOnly = (_a = idBlock.isHexOnly) !== null && _a !== void 0 ? _a : false; this.valueHexView = idBlock.valueHex ? pvtsutils.BufferSourceConverter.toUint8Array(idBlock.valueHex) : EMPTY_VIEW; this.tagClass = (_b = idBlock.tagClass) !== null && _b !== void 0 ? _b : ETagClass.UNKNOWN; this.tagNumber = (_c = idBlock.tagNumber) !== null && _c !== void 0 ? _c : EUniversalTagNumber.unknown; this.isConstructed = (_d = idBlock.isConstructed) !== null && _d !== void 0 ? _d : false; this.optionalID = (_e = idBlock.optionalID) !== null && _e !== void 0 ? _e : -1; } else { this.tagClass = ETagClass.UNKNOWN; this.tagNumber = EUniversalTagNumber.unknown; this.isConstructed = false; this.optionalID = -1; } } getDebug(delimiter = "") { let result = ""; result += getTagClassAsText(this.tagClass); result += delimiter; if (this.tagClass === ETagClass.UNIVERSAL) result += getTagNumberAsText(this.tagNumber); else result += this.tagNumber; return result; } toBER(sizeOnly = false, ignoreOptionalID = false) { let firstOctet = 0; let tagClass = this.tagClass; let tagNumber = this.tagNumber; if (this.optionalID >= 0 && !ignoreOptionalID) { tagClass = ETagClass.CONTEXT_SPECIFIC; tagNumber = this.optionalID; } switch (tagClass) { case ETagClass.UNIVERSAL: firstOctet |= 0x00; break; case ETagClass.APPLICATION: firstOctet |= 0x40; break; case ETagClass.CONTEXT_SPECIFIC: firstOctet |= 0x80; break; case ETagClass.PRIVATE: firstOctet |= 0xC0; break; default: this.error = "Unknown tag class"; return EMPTY_BUFFER; } if (this.isConstructed) firstOctet |= 0x20; if (tagNumber < 31 && !this.isHexOnly) { const retView = new Uint8Array(1); if (!sizeOnly) { let number = tagNumber; number &= 0x1F; firstOctet |= number; retView[0] = firstOctet; } return retView.buffer; } if (!this.isHexOnly) { const encodedBuf = pvutils.utilToBase(tagNumber, 7); const encodedView = new Uint8Array(encodedBuf); const size = encodedBuf.byteLength; const retView = new Uint8Array(size + 1); retView[0] = (firstOctet | 0x1F); if (!sizeOnly) { for (let i = 0; i < (size - 1); i++) retView[i + 1] = encodedView[i] | 0x80; retView[size] = encodedView[size - 1]; } return retView.buffer; } const retView = new Uint8Array(this.valueHexView.byteLength + 1); retView[0] = (firstOctet | 0x1F); if (!sizeOnly) { const curView = this.valueHexView; for (let i = 0; i < (curView.length - 1); i++) retView[i + 1] = curView[i] | 0x80; retView[this.valueHexView.byteLength] = curView[curView.length - 1]; } return retView.buffer; } fromBER(inputBuffer, inputOffset, inputLength) { const inputView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); if (!checkBufferParams(this, inputView, inputOffset, inputLength)) { return -1; } const intBuffer = inputView.subarray(inputOffset, inputOffset + inputLength); if (intBuffer.length === 0) { this.error = "Zero buffer length"; return -1; } const tagClassMask = intBuffer[0] & 0xC0; switch (tagClassMask) { case 0x00: this.tagClass = ETagClass.UNIVERSAL; break; case 0x40: this.tagClass = ETagClass.APPLICATION; break; case 0x80: this.tagClass = ETagClass.CONTEXT_SPECIFIC; break; case 0xC0: this.tagClass = ETagClass.PRIVATE; break; default: this.error = "Unknown tag class"; return -1; } this.isConstructed = (intBuffer[0] & 0x20) === 0x20; this.isHexOnly = false; const tagNumberMask = intBuffer[0] & 0x1F; if (tagNumberMask !== 0x1F) { this.tagNumber = (tagNumberMask); this.blockLength = 1; } else { let count = 1; let intTagNumberBuffer = this.valueHexView = new Uint8Array(255); let tagNumberBufferMaxLength = 255; while (intBuffer[count] & 0x80) { intTagNumberBuffer[count - 1] = intBuffer[count] & 0x7F; count++; if (count >= intBuffer.length) { this.error = "End of input reached before message was fully decoded"; return -1; } if (count === tagNumberBufferMaxLength) { tagNumberBufferMaxLength += 255; const tempBufferView = new Uint8Array(tagNumberBufferMaxLength); for (let i = 0; i < intTagNumberBuffer.length; i++) tempBufferView[i] = intTagNumberBuffer[i]; intTagNumberBuffer = this.valueHexView = new Uint8Array(tagNumberBufferMaxLength); } } this.blockLength = (count + 1); intTagNumberBuffer[count - 1] = intBuffer[count] & 0x7F; const tempBufferView = new Uint8Array(count); for (let i = 0; i < count; i++) tempBufferView[i] = intTagNumberBuffer[i]; intTagNumberBuffer = this.valueHexView = new Uint8Array(count); intTagNumberBuffer.set(tempBufferView); if (this.blockLength <= 9) this.tagNumber = pvutils.utilFromBase(intTagNumberBuffer, 7); else { this.isHexOnly = true; this.warnings.push("Tag too long, represented as hex-coded"); } } if (((this.tagClass === 1)) && (this.isConstructed)) { switch (this.tagNumber) { case 1: case 2: case 5: case 6: case 9: case 13: case 14: case 23: case 24: case 31: case 32: case 33: case 34: this.error = "Constructed encoding used for primitive type"; return -1; } } return (inputOffset + this.blockLength); } toJSON() { return { ...super.toJSON(), tagClass: this.tagClass, tagNumber: this.tagNumber, isConstructed: this.isConstructed, optionalID: this.optionalID }; } isIdenticalType(other) { return this.tagClass === other.tagClass && this.tagNumber === other.tagNumber; } } LocalIdentificationBlock.NAME = "identificationBlock"; class LocalLengthBlock extends LocalBaseBlock { constructor({ lenBlock = {}, } = {}) { var _a, _b, _c; super(); this.isIndefiniteForm = (_a = lenBlock.isIndefiniteForm) !== null && _a !== void 0 ? _a : false; this.longFormUsed = (_b = lenBlock.longFormUsed) !== null && _b !== void 0 ? _b : false; this.length = (_c = lenBlock.length) !== null && _c !== void 0 ? _c : 0; } fromBER(inputBuffer, inputOffset, inputLength) { const view = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); if (!checkBufferParams(this, view, inputOffset, inputLength)) { return -1; } const intBuffer = view.subarray(inputOffset, inputOffset + inputLength); if (intBuffer.length === 0) { this.error = "Zero buffer length"; return -1; } if (intBuffer[0] === 0xFF) { this.error = "Length block 0xFF is reserved by standard"; return -1; } this.isIndefiniteForm = intBuffer[0] === 0x80; if (this.isIndefiniteForm) { this.blockLength = 1; return (inputOffset + this.blockLength); } this.longFormUsed = !!(intBuffer[0] & 0x80); if (this.longFormUsed === false) { this.length = (intBuffer[0]); this.blockLength = 1; return (inputOffset + this.blockLength); } const count = intBuffer[0] & 0x7F; if (count > 8) { this.error = "Too big integer"; return -1; } if ((count + 1) > intBuffer.length) { this.error = "End of input reached before message was fully decoded"; return -1; } const lenOffset = inputOffset + 1; const lengthBufferView = view.subarray(lenOffset, lenOffset + count); if (lengthBufferView[count - 1] === 0x00) this.warnings.push("Needlessly long encoded length"); this.length = pvutils.utilFromBase(lengthBufferView, 8); if (this.longFormUsed && (this.length <= 127)) this.warnings.push("Unnecessary usage of long length form"); this.blockLength = count + 1; return (inputOffset + this.blockLength); } toBER(sizeOnly = false) { let retBuf; let retView; if (this.length > 127) this.longFormUsed = true; if (this.isIndefiniteForm) { retBuf = new ArrayBuffer(1); if (sizeOnly === false) { retView = new Uint8Array(retBuf); retView[0] = 0x80; } return retBuf; } if (this.longFormUsed) { const encodedBuf = pvutils.utilToBase(this.length, 8); if (encodedBuf.byteLength > 127) { this.error = "Too big length"; return (EMPTY_BUFFER); } retBuf = new ArrayBuffer(encodedBuf.byteLength + 1); if (sizeOnly) return retBuf; const encodedView = new Uint8Array(encodedBuf); retView = new Uint8Array(retBuf); retView[0] = encodedBuf.byteLength | 0x80; for (let i = 0; i < encodedBuf.byteLength; i++) retView[i + 1] = encodedView[i]; return retBuf; } retBuf = new ArrayBuffer(1); if (sizeOnly === false) { retView = new Uint8Array(retBuf); retView[0] = this.length; } return retBuf; } toJSON() { return { ...super.toJSON(), isIndefiniteForm: this.isIndefiniteForm, longFormUsed: this.longFormUsed, length: this.length, }; } } LocalLengthBlock.NAME = "lengthBlock"; class BaseBlock extends LocalBaseBlock { constructor({ name = EMPTY_STRING, optional = false, primitiveSchema, ...parameters } = {}, valueBlockType) { var _a; super(parameters); this.name = name; this.optional = optional; if (((_a = parameters.idBlock) === null || _a === void 0 ? void 0 : _a.optionalID) !== undefined && parameters.idBlock.optionalID >= 0) this.optional = true; if (primitiveSchema) this.primitiveSchema = primitiveSchema; this.idBlock = new LocalIdentificationBlock(parameters); this.lenBlock = new LocalLengthBlock(parameters); this.valueBlock = valueBlockType ? new valueBlockType(parameters) : new ValueBlock(parameters); } fromBER(inputBuffer, inputOffset, inputLength) { const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, (this.lenBlock.isIndefiniteForm) ? inputLength : this.lenBlock.length); if (resultOffset === -1) { this.error = this.valueBlock.error; return resultOffset; } if (!this.idBlock.error.length) { this.blockLength += this.idBlock.blockLength; this.blockLength += this.lenBlock.blockLength; this.blockLength += this.valueBlock.blockLength; } return resultOffset; } toBER(sizeOnly, writer) { const _writer = writer || new ViewWriter(); if (!writer) { prepareIndefiniteForm(this); } const idBlockBuf = this.idBlock.toBER(sizeOnly); _writer.write(idBlockBuf); if (this.lenBlock.isIndefiniteForm) { _writer.write(new Uint8Array([0x80]).buffer); this.valueBlock.toBER(sizeOnly, _writer); _writer.write(new ArrayBuffer(2)); } else { const valueBlockBuf = this.valueBlock.toBER(sizeOnly); this.lenBlock.length = valueBlockBuf.byteLength; const lenBlockBuf = this.lenBlock.toBER(sizeOnly); _writer.write(lenBlockBuf); _writer.write(valueBlockBuf); } if (!writer) { return _writer.final(); } return EMPTY_BUFFER; } toJSON() { const object = { ...super.toJSON(), idBlock: this.idBlock.toJSON(), lenBlock: this.lenBlock.toJSON(), valueBlock: this.valueBlock.toJSON(), name: this.name, optional: this.optional, }; if (this.primitiveSchema) object.primitiveSchema = this.primitiveSchema.toJSON(); return object; } toString(encoding = "ascii") { if (encoding === "ascii") { return this.onAsciiEncoding(); } return pvtsutils.Convert.ToHex(this.toBER()); } onAsciiEncoding() { return `${this.constructor.NAME} : ${pvtsutils.Convert.ToHex(this.valueBlock.valueBeforeDecodeView)}`; } isEqual(other) { if (this === other) { return true; } if (!(other instanceof this.constructor)) { return false; } const thisRaw = this.toBER(); const otherRaw = other.toBER(); return pvutils.isEqualBuffer(thisRaw, otherRaw); } getUniversalTagNumber() { if (this.idBlock.tagClass === ETagClass.UNIVERSAL) return this.idBlock.tagNumber; return undefined; } static mergeIDBlock(params, baseIDs) { if (!params.idBlock) params.idBlock = {}; if (params.idBlock.tagClass === undefined) params.idBlock.tagClass = baseIDs.tagClass; if (params.idBlock.tagNumber === undefined) params.idBlock.tagNumber = baseIDs.tagNumber; } static matches(obj) { if (!obj) return false; const comp = obj; try { return comp.idBlock.tagClass === this.defaultIDs.tagClass && comp.idBlock.tagNumber === this.defaultIDs.tagNumber; } catch (error) { return false; } } } BaseBlock.NAME = "BaseBlock"; BaseBlock.defaultIDs = { tagClass: -1, tagNumber: -1 }; function prepareIndefiniteForm(baseBlock) { if (baseBlock instanceof typeStore.Constructed) { for (const value of baseBlock.valueBlock.value) { if (prepareIndefiniteForm(value)) { baseBlock.lenBlock.isIndefiniteForm = true; } } } return !!baseBlock.lenBlock.isIndefiniteForm; } class BaseStringBlock extends BaseBlock { getValue() { return this.valueBlock.value; } setValue(value) { this.valueBlock.value = value; } constructor({ value = EMPTY_STRING, ...parameters } = {}, stringValueBlockType) { super(parameters, stringValueBlockType); if (value) { this.fromString(value); } } fromBER(inputBuffer, inputOffset, inputLength) { const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, (this.lenBlock.isIndefiniteForm) ? inputLength : this.lenBlock.length); if (resultOffset === -1) { this.error = this.valueBlock.error; return resultOffset; } this.fromBuffer(this.valueBlock.valueHexView); if (!this.idBlock.error.length) this.blockLength += this.idBlock.blockLength; if (!this.lenBlock.error.length) this.blockLength += this.lenBlock.blockLength; if (!this.valueBlock.error.length) this.blockLength += this.valueBlock.blockLength; return resultOffset; } onAsciiEncoding() { return `${this.constructor.NAME} : '${this.valueBlock.value}'`; } } BaseStringBlock.NAME = "BaseStringBlock"; class LocalPrimitiveValueBlock extends HexBlock(ValueBlock) { constructor({ isHexOnly = true, ...parameters } = {}) { super(parameters); this.isHexOnly = isHexOnly; } } LocalPrimitiveValueBlock.NAME = "PrimitiveValueBlock"; var _a$z; class Primitive extends BaseBlock { constructor(parameters = {}) { super(parameters, LocalPrimitiveValueBlock); this.idBlock.isConstructed = false; } getValue() { return null; } setValue(value) { } } _a$z = Primitive; (() => { typeStore.Primitive = _a$z; })(); Primitive.NAME = "PRIMITIVE"; function localChangeType(inputObject, newType) { if (inputObject instanceof newType) { return inputObject; } const newObject = new newType(); newObject.idBlock = inputObject.idBlock; newObject.lenBlock = inputObject.lenBlock; newObject.warnings = inputObject.warnings; newObject.valueBeforeDecodeView = inputObject.valueBeforeDecodeView; return newObject; } function getTypeForIDBlock(idBlock) { if (idBlock.tagClass === 1) { switch (idBlock.tagNumber) { case EUniversalTagNumber.EndOfContent: return typeStore.EndOfContent; case EUniversalTagNumber.Boolean: return typeStore.Boolean; case EUniversalTagNumber.Integer: return typeStore.Integer; case EUniversalTagNumber.BitString: return typeStore.BitString; case EUniversalTagNumber.OctetString: return typeStore.OctetString; case EUniversalTagNumber.Null: return typeStore.Null; case EUniversalTagNumber.ObjectIdentifier: return typeStore.ObjectIdentifier; case EUniversalTagNumber.Real: return typeStore.Real; case EUniversalTagNumber.Enumerated: return typeStore.Enumerated; case EUniversalTagNumber.Utf8String: return typeStore.Utf8String; case EUniversalTagNumber.RelativeObjectIdentifier: return typeStore.RelativeObjectIdentifier; case EUniversalTagNumber.TIME: return typeStore.TIME; case EUniversalTagNumber.Sequence: return typeStore.Sequence; case EUniversalTagNumber.Set: return typeStore.Set; case EUniversalTagNumber.NumericString: return typeStore.NumericString; case EUniversalTagNumber.PrintableString: return typeStore.PrintableString; case EUniversalTagNumber.TeletexString: return typeStore.TeletexString; case EUniversalTagNumber.VideotexString: return typeStore.VideotexString; case EUniversalTagNumber.IA5String: return typeStore.IA5String; case EUniversalTagNumber.UTCTime: return typeStore.UTCTime; case EUniversalTagNumber.GeneralizedTime: return typeStore.GeneralizedTime; case EUniversalTagNumber.GraphicString: return typeStore.GraphicString; case EUniversalTagNumber.VisibleString: return typeStore.VisibleString; case EUniversalTagNumber.GeneralString: return typeStore.GeneralString; case EUniversalTagNumber.UniversalString: return typeStore.UniversalString; case EUniversalTagNumber.CharacterString: return typeStore.CharacterString; case EUniversalTagNumber.BmpString: return typeStore.BmpString; case EUniversalTagNumber.DATE: return typeStore.DATE; case EUniversalTagNumber.TimeOfDay: return typeStore.TimeOfDay; case EUniversalTagNumber.DateTime: return typeStore.DateTime; case EUniversalTagNumber.Duration: return typeStore.Duration; default: return undefined; } } else { return idBlock.isConstructed ? typeStore.Constructed : typeStore.Primitive; } } function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.length) { const incomingOffset = inputOffset; let returnObject = new BaseBlock({}, ValueBlock); const baseBlock = new LocalBaseBlock(); if (!checkBufferParams(baseBlock, inputBuffer, inputOffset, inputLength)) { returnObject.error = baseBlock.error; return { offset: -1, result: returnObject }; } const intBuffer = inputBuffer.subarray(inputOffset, inputOffset + inputLength); if (!intBuffer.length) { returnObject.error = "Zero buffer length"; return { offset: -1, result: returnObject }; } let resultOffset = returnObject.idBlock.fromBER(inputBuffer, inputOffset, inputLength); if (returnObject.idBlock.warnings.length) { returnObject.warnings.concat(returnObject.idBlock.warnings); } if (resultOffset === -1) { returnObject.error = returnObject.idBlock.error; return { offset: -1, result: returnObject }; } inputOffset = resultOffset; inputLength -= returnObject.idBlock.blockLength; resultOffset = returnObject.lenBlock.fromBER(inputBuffer, inputOffset, inputLength); if (returnObject.lenBlock.warnings.length) { returnObject.warnings.concat(returnObject.lenBlock.warnings); } if (resultOffset === -1) { returnObject.error = returnObject.lenBlock.error; return { offset: -1, result: returnObject }; } inputOffset = resultOffset; inputLength -= returnObject.lenBlock.blockLength; if (!returnObject.idBlock.isConstructed && returnObject.lenBlock.isIndefiniteForm) { returnObject.error = "Indefinite length form used for primitive encoding form"; return { offset: -1, result: returnObject }; } if (returnObject.idBlock.tagClass === 1) { if (returnObject.idBlock.tagNumber === 0 && returnObject.idBlock.isConstructed && returnObject.lenBlock.length > 0) { returnObject.error = "Type [UNIVERSAL 0] is reserved"; return { offset: -1, result: returnObject }; } else if (returnObject.idBlock.tagNumber >= 37 && returnObject.idBlock.isHexOnly === false) { returnObject.error = "UNIVERSAL 37 and upper tags are reserved by ASN.1 standard"; return { offset: -1, result: returnObject }; } } const newASN1Type = getTypeForIDBlock(returnObject.idBlock); if (newASN1Type === undefined) { returnObject.error = `Unable to create property for tagClass:${returnObject.idBlock.tagClass} tagNumber:${returnObject.idBlock.tagNumber}`; return { offset: -1, result: returnObject }; } if ((newASN1Type instanceof typeStore.Constructed || newASN1Type instanceof typeStore.Primitive) && returnObject.idBlock.tagClass === ETagClass.UNIVERSAL) { newASN1Type.idBlock = returnObject.idBlock; newASN1Type.lenBlock = returnObject.lenBlock; newASN1Type.warnings = returnObject.warnings; returnObject = newASN1Type; } returnObject = localChangeType(returnObject, newASN1Type); resultOffset = returnObject.fromBER(inputBuffer, inputOffset, returnObject.lenBlock.isIndefiniteForm ? inputLength : returnObject.lenBlock.length); returnObject.valueBeforeDecodeView = inputBuffer.subarray(incomingOffset, incomingOffset + returnObject.blockLength); return { offset: resultOffset, result: returnObject }; } function fromBER(inputBuffer) { if (!inputBuffer.byteLength) { const result = new BaseBlock({}, ValueBlock); result.error = "Input buffer has zero length"; return { offset: -1, result }; } return localFromBER(pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer).slice(), 0, inputBuffer.byteLength); } function checkLen(indefiniteLength, length) { if (indefiniteLength) { return 1; } return length; } class LocalConstructedValueBlock extends ValueBlock { constructor({ value = [], isIndefiniteForm = false, ...parameters } = {}) { super(parameters); this.value = value; this.isIndefiniteForm = isIndefiniteForm; } fromBER(inputBuffer, inputOffset, inputLength) { const view = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); if (!checkBufferParams(this, view, inputOffset, inputLength)) { return -1; } this.valueBeforeDecodeView = view.subarray(inputOffset, inputOffset + inputLength); if (this.valueBeforeDecodeView.length === 0) { this.warnings.push("Zero buffer length"); return inputOffset; } let currentOffset = inputOffset; while (checkLen(this.isIndefiniteForm, inputLength) > 0) { const returnObject = localFromBER(view, currentOffset, inputLength); if (returnObject.offset === -1) { this.error = returnObject.result.error; this.warnings.concat(returnObject.result.warnings); return -1; } currentOffset = returnObject.offset; this.blockLength += returnObject.result.blockLength; inputLength -= returnObject.result.blockLength; this.value.push(returnObject.result); if (this.isIndefiniteForm && returnObject.result.constructor.NAME === END_OF_CONTENT_NAME) { break; } } if (this.isIndefiniteForm) { if (this.value[this.value.length - 1].constructor.NAME === END_OF_CONTENT_NAME) { this.value.pop(); } else { this.warnings.push("No EndOfContent block encoded"); } } return currentOffset; } toBER(sizeOnly, writer) { const _writer = writer || new ViewWriter(); for (let i = 0; i < this.value.length; i++) { this.value[i].toBER(sizeOnly, _writer); } if (!writer) { return _writer.final(); } return EMPTY_BUFFER; } toJSON() { const object = { ...super.toJSON(), isIndefiniteForm: this.isIndefiniteForm, value: [], }; for (const value of this.value) { object.value.push(value.toJSON()); } return object; } } LocalConstructedValueBlock.NAME = "ConstructedValueBlock"; var _a$y; class Constructed extends BaseBlock { constructor(parameters = {}) { super(parameters, LocalConstructedValueBlock); this.idBlock.isConstructed = true; } fromBER(inputBuffer, inputOffset, inputLength) { this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm; const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, (this.lenBlock.isIndefiniteForm) ? inputLength : this.lenBlock.length); if (resultOffset === -1) { this.error = this.valueBlock.error; return resultOffset; } if (!this.idBlock.error.length) this.blockLength += this.idBlock.blockLength; if (!this.lenBlock.error.length) this.blockLength += this.lenBlock.blockLength; if (!this.valueBlock.error.length) this.blockLength += this.valueBlock.blockLength; return resultOffset; } onAsciiEncoding() { const values = []; for (const value of this.valueBlock.value) { values.push(value.toString("ascii").split("\n").map(o => ` ${o}`).join("\n")); } const blockName = this.idBlock.tagClass === 3 ? `[${this.idBlock.tagNumber}]` : this.constructor.NAME; return values.length ? `${blockName} :\n${values.join("\n")}` : `${blockName} :`; } getValueByName(name) { const fields = this.valueBlock.value; for (const value of fields) { if (value.name === name) return value; } return undefined; } getTypedValueByName(c, name) { const ids = c.defaultIDs; if (ids) { const fields = this.valueBlock.value; for (const value of fields) { if (value.name === name) { if (value.idBlock.isIdenticalType(ids)) return value; } } } return undefined; } getValue() { return this.valueBlock.value; } setValue(value) { this.valueBlock.value = value; } } _a$y = Constructed; (() => { typeStore.Constructed = _a$y; })(); Constructed.NAME = "CONSTRUCTED"; class LocalEndOfContentValueBlock extends ValueBlock { fromBER(inputBuffer, inputOffset, inputLength) { return inputOffset; } toBER(sizeOnly) { return EMPTY_BUFFER; } } LocalEndOfContentValueBlock.override = "EndOfContentValueBlock"; var _a$x; class EndOfContent extends BaseBlock { constructor(parameters = {}) { _a$x.mergeIDBlock(parameters, _a$x.defaultIDs); super(parameters, LocalEndOfContentValueBlock); } getValue() { return null; } setValue(value) { } static typeGuard(obj) { return this.matches(obj); } } _a$x = EndOfContent; (() => { typeStore.EndOfContent = _a$x; })(); EndOfContent.NAME = END_OF_CONTENT_NAME; EndOfContent.defaultIDs = { tagClass: ETagClass.UNIVERSAL, tagNumber: EUniversalTagNumber.EndOfContent }; var _a$w; class Null extends BaseBlock { constructor(parameters = {}) { _a$w.mergeIDBlock(parameters, _a$w.defaultIDs); super(parameters, ValueBlock); } getValue() { return null; } setValue(value) { } fromBER(inputBuffer, inputOffset, inputLength) { if (this.lenBlock.length > 0) this.warnings.push("Non-zero length of value block for Null type"); if (!this.idBlock.error.length) this.blockLength += this.idBlock.blockLength; if (!this.lenBlock.error.length) this.blockLength += this.lenBlock.blockLength; this.blockLength += inputLength; if ((inputOffset + inputLength) > inputBuffer.byteLength) { this.error = "End of input reached before message was fully decoded (inconsistent offset and length values)"; return -1; } return (inputOffset + inputLength); } toBER(sizeOnly, writer) { const retBuf = new ArrayBuffer(2); if (!sizeOnly) { const retView = new Uint8Array(retBuf); retView[0] = 0x05; retView[1] = 0x00; } if (writer) { writer.write(retBuf); } return retBuf; } onAsciiEncoding() { return `${this.constructor.NAME}`; } static typeGuard(obj) { return this.matches(obj); } } _a$w = Null; (() => { typeStore.Null = _a$w; })(); Null.NAME = "NULL"; Null.defaultIDs = { tagClass: ETagClass.UNIVERSAL, tagNumber: EUniversalTagNumber.Null }; var _a$v; class Extension extends BaseBlock { constructor() { super({ idBlock: _a$v.defaultIDs, optional: true }, ValueBlock); } getValue() { return null; } setValue(value) { } fromBER(inputBuffer, inputOffset, inputLength) { throw new Error("An extension attribute should never get decoded... It´s just for schema validation"); } toBER(sizeOnly, writer) { throw new Error("An extension attribute should never get encoded... It´s just for schema validation"); } onAsciiEncoding() { return `${this.constructor.NAME}`; } static typeGuard(obj) { return this.matches(obj); } } _a$v = Extension; (() => { typeStore.Extension = _a$v; })(); Extension.NAME = "NULL"; Extension.defaultIDs = { tagClass: ETagClass.UNIVERSAL, tagNumber: EUniversalTagNumber.Extension }; class LocalBooleanValueBlock extends HexBlock(ValueBlock) { get value() { for (const octet of this.valueHexView) { if (octet > 0) { return true; } } return false; } set value(value) { this.valueHexView[0] = value ? 0xFF : 0x00; } constructor({ value, ...parameters } = {}) { super(parameters); if (parameters.valueHex) { this.valueHexView = pvtsutils.BufferSourceConverter.toUint8Array(parameters.valueHex); } else { this.valueHexView = new Uint8Array(1); } if (value) { this.value = value; } } fromBER(inputBuffer, inputOffset, inputLength) { const inputView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); if (!checkBufferParams(this, inputView, inputOffset, inputLength)) { return -1; } this.valueHexView = inputView.subarray(inputOffset, inputOffset + inputLength); if (inputLength > 1) this.warnings.push("Boolean value encoded in more then 1 octet"); pvuti