diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/third_party/asn1js/src | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/third_party/asn1js/src')
68 files changed, 5363 insertions, 0 deletions
diff --git a/comm/third_party/asn1js/src/Any.ts b/comm/third_party/asn1js/src/Any.ts new file mode 100644 index 0000000000..36db46cc2e --- /dev/null +++ b/comm/third_party/asn1js/src/Any.ts @@ -0,0 +1,22 @@ +import { EMPTY_STRING } from "./internals/constants"; + +export interface IAny { + name: string; + optional: boolean; +} + +export type AnyParams = Partial<IAny>; + +export class Any implements IAny { + + public name: string; + public optional: boolean; + + constructor({ + name = EMPTY_STRING, optional = false, + }: AnyParams = {}) { + this.name = name; + this.optional = optional; + } + +} diff --git a/comm/third_party/asn1js/src/BaseBlock.ts b/comm/third_party/asn1js/src/BaseBlock.ts new file mode 100644 index 0000000000..8a4c58528c --- /dev/null +++ b/comm/third_party/asn1js/src/BaseBlock.ts @@ -0,0 +1,186 @@ +import * as pvtsutils from "pvtsutils"; +import * as pvutils from "pvutils"; +import { IBerConvertible } from "./types"; +import { LocalBaseBlockJson, LocalBaseBlockParams, LocalBaseBlock } from "./internals/LocalBaseBlock"; +import { LocalIdentificationBlock, LocalIdentificationBlockJson, LocalIdentificationBlockParams } from "./internals/LocalIdentificationBlock"; +import { LocalLengthBlock, LocalLengthBlockJson, LocalLengthBlockParams } from "./internals/LocalLengthBlock"; +import { ViewWriter } from "./ViewWriter"; +import { ValueBlock, ValueBlockJson } from "./ValueBlock"; +import { EMPTY_BUFFER, EMPTY_STRING } from "./internals/constants"; +import { typeStore } from "./TypeStore"; + +export interface IBaseBlock { + name: string; + optional: boolean; + primitiveSchema?: BaseBlock; +} + +export interface BaseBlockParams extends LocalBaseBlockParams, LocalIdentificationBlockParams, LocalLengthBlockParams, Partial<IBaseBlock> { } + +export interface ValueBlockConstructor<T extends ValueBlock = ValueBlock> { + new(...args: any[]): T; +} + +export interface BaseBlockJson<T extends LocalBaseBlockJson = LocalBaseBlockJson> extends LocalBaseBlockJson, Omit<IBaseBlock, "primitiveSchema"> { + idBlock: LocalIdentificationBlockJson; + lenBlock: LocalLengthBlockJson; + valueBlock: T; + primitiveSchema?: BaseBlockJson; +} + +export type StringEncoding = "ascii" | "hex"; + +export class BaseBlock<T extends ValueBlock = ValueBlock, J extends ValueBlockJson = ValueBlockJson> extends LocalBaseBlock implements IBaseBlock, IBerConvertible { + + static { + + } + + public static override NAME = "BaseBlock"; + + public idBlock: LocalIdentificationBlock; + public lenBlock: LocalLengthBlock; + public valueBlock: T; + public name: string; + public optional: boolean; + public primitiveSchema?: BaseBlock; + + constructor({ + name = EMPTY_STRING, + optional = false, + primitiveSchema, + ...parameters + }: BaseBlockParams = {}, valueBlockType?: ValueBlockConstructor<T>) { + super(parameters); + + this.name = name; + this.optional = optional; + if (primitiveSchema) { + this.primitiveSchema = primitiveSchema; + } + + this.idBlock = new LocalIdentificationBlock(parameters); + this.lenBlock = new LocalLengthBlock(parameters); + this.valueBlock = valueBlockType ? new valueBlockType(parameters) : new ValueBlock(parameters) as unknown as T; + } + + public fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + 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; + } + + public toBER(sizeOnly?: boolean, writer?: ViewWriter): ArrayBuffer { + 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; + } + + public override toJSON(): BaseBlockJson<J> { + const object: BaseBlockJson = { + ...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 as BaseBlockJson<J>; + } + public override toString(encoding: StringEncoding = "ascii"): string { + if (encoding === "ascii") { + return this.onAsciiEncoding(); + } + + return pvtsutils.Convert.ToHex(this.toBER()); + } + + protected onAsciiEncoding(): string { + return `${(this.constructor as typeof BaseBlock).NAME} : ${pvtsutils.Convert.ToHex(this.valueBlock.valueBeforeDecodeView)}`; + } + + /** + * Determines whether two object instances are equal + * @param other Object to compare with the current object + */ + public isEqual(other: unknown): other is this { + if (this === other) { + return true; + } + + // Check input type + if (!(other instanceof this.constructor)) { + return false; + } + + const thisRaw = this.toBER(); + const otherRaw = (other as BaseBlock).toBER(); + + return pvutils.isEqualBuffer(thisRaw, otherRaw); + } + +} + +/** + * Recursive function which checks and enables isIndefiniteForm flag for constructed blocks if any child has that flag enabled + * @param baseBlock Base ASN.1 block + * @returns Returns `true` if incoming block is `indefinite form` + */ +function prepareIndefiniteForm(baseBlock: BaseBlock): boolean { + if (baseBlock instanceof typeStore.Constructed) { + for (const value of baseBlock.valueBlock.value) { + if (prepareIndefiniteForm(value)) { + baseBlock.lenBlock.isIndefiniteForm = true; + } + } + } + + return !!baseBlock.lenBlock.isIndefiniteForm; +} diff --git a/comm/third_party/asn1js/src/BaseStringBlock.ts b/comm/third_party/asn1js/src/BaseStringBlock.ts new file mode 100644 index 0000000000..606b330e0c --- /dev/null +++ b/comm/third_party/asn1js/src/BaseStringBlock.ts @@ -0,0 +1,74 @@ +import { BaseBlock, BaseBlockParams } from "./BaseBlock"; +import { IStringConvertible } from "./types"; +import { EMPTY_STRING } from "./internals/constants"; +import { LocalStringValueBlock, LocalStringValueBlockJson, LocalStringValueBlockParams } from "./internals/LocalStringValueBlock"; + +export interface BaseStringBlockParams extends BaseBlockParams, LocalStringValueBlockParams { } +export type BaseStringBlockJson = LocalStringValueBlockJson; + +export abstract class BaseStringBlock<T extends LocalStringValueBlock = LocalStringValueBlock, J extends BaseStringBlockJson = BaseStringBlockJson> extends BaseBlock<T, J> implements IStringConvertible { + + public static override NAME = "BaseStringBlock"; + + /** + * String value + * @since 3.0.0 + */ + public getValue(): string { + return this.valueBlock.value; + } + /** + * String value + * @param value String value + * @since 3.0.0 + */ + public setValue(value: string): void { + this.valueBlock.value = value; + } + + constructor({ + value = EMPTY_STRING, + ...parameters + }: BaseStringBlockParams = {}, stringValueBlockType: new () => T) { + super(parameters, stringValueBlockType); + + if (value) { + this.fromString(value); + } + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + 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; + } + + /** + * Function converting ArrayBuffer into ASN.1 internal string + * @param inputBuffer ASN.1 BER encoded array + */ + public abstract fromBuffer(inputBuffer: ArrayBuffer | Uint8Array): void; + + public abstract fromString(inputString: string): void; + + protected override onAsciiEncoding(): string { + return `${(this.constructor as typeof BaseStringBlock).NAME} : '${this.valueBlock.value}'`; + } + +} diff --git a/comm/third_party/asn1js/src/BitString.ts b/comm/third_party/asn1js/src/BitString.ts new file mode 100644 index 0000000000..2c8ada83cc --- /dev/null +++ b/comm/third_party/asn1js/src/BitString.ts @@ -0,0 +1,64 @@ +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { Constructed } from "./Constructed"; +import { BIT_STRING_NAME } from "./internals/constants"; +import { LocalBitStringValueBlockParams, LocalBitStringValueBlock, LocalBitStringValueBlockJson } from "./internals/LocalBitStringValueBlock"; +import { typeStore } from "./TypeStore"; + +export interface BitStringParams extends BaseBlockParams, LocalBitStringValueBlockParams { } +export type BitStringJson = BaseBlockJson<LocalBitStringValueBlockJson>; + +export class BitString extends BaseBlock<LocalBitStringValueBlock, LocalBitStringValueBlockJson> { + + static { + typeStore.BitString = this; + } + + public static override NAME = BIT_STRING_NAME; + + constructor({ + idBlock = {}, + lenBlock = {}, + ...parameters + }: BitStringParams = {}) { + parameters.isConstructed ??= !!parameters.value?.length; + super({ + idBlock: { + isConstructed: parameters.isConstructed, + ...idBlock, + }, + lenBlock: { + ...lenBlock, + isIndefiniteForm: !!parameters.isIndefiniteForm, + }, + ...parameters, + }, LocalBitStringValueBlock); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 3; // BitString + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + this.valueBlock.isConstructed = this.idBlock.isConstructed; + this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm; + + return super.fromBER(inputBuffer, inputOffset, inputLength); + } + + protected override onAsciiEncoding(): string { + if (this.valueBlock.isConstructed || (this.valueBlock.value && this.valueBlock.value.length)) { + return Constructed.prototype.onAsciiEncoding.call(this); + } else { + // convert bytes to bits + const bits = []; + const valueHex = this.valueBlock.valueHexView; + for (const byte of valueHex) { + bits.push(byte.toString(2).padStart(8, "0")); + } + + const bitsStr = bits.join(""); + + return `${(this.constructor as typeof BitString).NAME} : ${bitsStr.substring(0, bitsStr.length - this.valueBlock.unusedBits)}`; + } + } + +} diff --git a/comm/third_party/asn1js/src/BmpString.ts b/comm/third_party/asn1js/src/BmpString.ts new file mode 100644 index 0000000000..42f23ea773 --- /dev/null +++ b/comm/third_party/asn1js/src/BmpString.ts @@ -0,0 +1,23 @@ +import { LocalBmpStringValueBlockParams, LocalBmpStringValueBlock, LocalBmpStringValueBlockJson } from "./internals/LocalBmpStringValueBlock"; +import { typeStore } from "./TypeStore"; + +export type BmpStringParams = LocalBmpStringValueBlockParams; +export type BmpStringJson = LocalBmpStringValueBlockJson; + +export class BmpString extends LocalBmpStringValueBlock { + + static { + typeStore.BmpString = this; + } + public static override NAME = "BMPString"; + + constructor({ + ...parameters + }: BmpStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 30; // BmpString + } + +} diff --git a/comm/third_party/asn1js/src/Boolean.ts b/comm/third_party/asn1js/src/Boolean.ts new file mode 100644 index 0000000000..3bcdb05ae2 --- /dev/null +++ b/comm/third_party/asn1js/src/Boolean.ts @@ -0,0 +1,43 @@ +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { LocalBooleanValueBlockParams, LocalBooleanValueBlock, LocalBooleanValueBlockJson } from "./internals/LocalBooleanValueBlock"; +import { typeStore } from "./TypeStore"; + +export interface BooleanParams extends BaseBlockParams, LocalBooleanValueBlockParams { } +export type BooleanJson = BaseBlockJson<LocalBooleanValueBlockJson>; + +export class Boolean extends BaseBlock<LocalBooleanValueBlock, LocalBooleanValueBlockJson> { + + static { + typeStore.Boolean = this; + } + + /** + * Gets value + * @since 3.0.0 + */ + public getValue(): boolean { + return this.valueBlock.value; + } + /** + * Sets value + * @param value Boolean value + * @since 3.0.0 + */ + public setValue(value: boolean): void { + this.valueBlock.value = value; + } + + public static override NAME = "BOOLEAN"; + + constructor(parameters: BooleanParams = {}) { + super(parameters, LocalBooleanValueBlock); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 1; // Boolean + } + + protected override onAsciiEncoding(): string { + return `${(this.constructor as typeof Boolean).NAME} : ${this.getValue}`; + } + +} diff --git a/comm/third_party/asn1js/src/CharacterString.ts b/comm/third_party/asn1js/src/CharacterString.ts new file mode 100644 index 0000000000..a15798cced --- /dev/null +++ b/comm/third_party/asn1js/src/CharacterString.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type CharacterStringParams = LocalSimpleStringBlockParams; +export type CharacterStringJson = LocalSimpleStringBlockJson; + +export class CharacterString extends LocalSimpleStringBlock { + + static { + typeStore.CharacterString = this; + } + + public static override NAME = "CharacterString"; + + constructor(parameters: CharacterStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 29; // CharacterString + } + +} diff --git a/comm/third_party/asn1js/src/Choice.ts b/comm/third_party/asn1js/src/Choice.ts new file mode 100644 index 0000000000..3246bfa3c3 --- /dev/null +++ b/comm/third_party/asn1js/src/Choice.ts @@ -0,0 +1,22 @@ +import { BaseBlock } from "./BaseBlock"; +import { IAny, Any } from "./Any"; + +export interface IChoice extends IAny { + value: BaseBlock[]; +} + +export type ChoiceParams = Partial<IChoice>; + +export class Choice extends Any implements IChoice { + public value: BaseBlock[]; + + constructor({ + value = [], + ...parameters + }: ChoiceParams = {}) { + super(parameters); + + this.value = value; + } + +} diff --git a/comm/third_party/asn1js/src/Constructed.ts b/comm/third_party/asn1js/src/Constructed.ts new file mode 100644 index 0000000000..8fe3bc502f --- /dev/null +++ b/comm/third_party/asn1js/src/Constructed.ts @@ -0,0 +1,61 @@ +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { LocalConstructedValueBlock, LocalConstructedValueBlockJson, LocalConstructedValueBlockParams } from "./internals/LocalConstructedValueBlock"; +import { typeStore } from "./TypeStore"; + +export interface ConstructedParams extends BaseBlockParams, LocalConstructedValueBlockParams { } +export type ConstructedJson = BaseBlockJson<LocalConstructedValueBlockJson>; + +export class Constructed extends BaseBlock<LocalConstructedValueBlock, LocalConstructedValueBlockJson> { + + static { + typeStore.Constructed = this; + } + + public static override NAME = "CONSTRUCTED"; + + constructor(parameters: ConstructedParams = {}) { + super(parameters, LocalConstructedValueBlock); + + this.idBlock.isConstructed = true; + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + 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; + } + + /** + * @internal + */ + public override onAsciiEncoding(): string { + 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 as typeof Constructed).NAME; + + return values.length + ? `${blockName} :\n${values.join("\n")}` // items + : `${blockName} :`; // empty + } + +} diff --git a/comm/third_party/asn1js/src/DATE.ts b/comm/third_party/asn1js/src/DATE.ts new file mode 100644 index 0000000000..3a6f73d144 --- /dev/null +++ b/comm/third_party/asn1js/src/DATE.ts @@ -0,0 +1,22 @@ +import { typeStore } from "./TypeStore"; +import { Utf8StringParams, Utf8String, Utf8StringJson } from "./Utf8String"; + +export type DATEParams = Utf8StringParams; +export type DATEJson = Utf8StringJson; + +export class DATE extends Utf8String { + + static { + typeStore.DATE = this; + } + + public static override NAME = "DATE"; + + constructor(parameters: DATEParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 31; // DATE + } + +} diff --git a/comm/third_party/asn1js/src/DateTime.ts b/comm/third_party/asn1js/src/DateTime.ts new file mode 100644 index 0000000000..a30f47dbe4 --- /dev/null +++ b/comm/third_party/asn1js/src/DateTime.ts @@ -0,0 +1,22 @@ +import { typeStore } from "./TypeStore"; +import { Utf8StringParams, Utf8String, Utf8StringJson } from "./Utf8String"; + +export type DateTimeParams = Utf8StringParams; +export type DateTimeJson = Utf8StringJson; + +export class DateTime extends Utf8String { + + static { + typeStore.DateTime = this; + } + + public static override NAME = "DateTime"; + + constructor(parameters: DateTimeParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 33; // DateTime + } + +} diff --git a/comm/third_party/asn1js/src/Duration.ts b/comm/third_party/asn1js/src/Duration.ts new file mode 100644 index 0000000000..618894e158 --- /dev/null +++ b/comm/third_party/asn1js/src/Duration.ts @@ -0,0 +1,21 @@ +import { typeStore } from "./TypeStore"; +import { Utf8StringParams, Utf8String, Utf8StringJson } from "./Utf8String"; + +export type DurationParams = Utf8StringParams; +export type DurationJson = Utf8StringJson; + +export class Duration extends Utf8String { + + static { + typeStore.Duration = this; + } + public static override NAME = "Duration"; + + constructor(parameters: DurationParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 34; // Duration + } + +} diff --git a/comm/third_party/asn1js/src/EndOfContent.ts b/comm/third_party/asn1js/src/EndOfContent.ts new file mode 100644 index 0000000000..b0e8ccf6c9 --- /dev/null +++ b/comm/third_party/asn1js/src/EndOfContent.ts @@ -0,0 +1,23 @@ +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { END_OF_CONTENT_NAME } from "./internals/constants"; +import { LocalEndOfContentValueBlock } from "./internals/LocalEndOfContentValueBlock"; +import { typeStore } from "./TypeStore"; + +export type EndOfContentParams = BaseBlockParams; +export type EndOfContentJson = BaseBlockJson; + +export class EndOfContent extends BaseBlock<LocalEndOfContentValueBlock> { + + static { + typeStore.EndOfContent = this; + } + public static override NAME = END_OF_CONTENT_NAME; + + constructor(parameters: EndOfContentParams = {}) { + super(parameters, LocalEndOfContentValueBlock); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 0; // EndOfContent + } + +} diff --git a/comm/third_party/asn1js/src/Enumerated.ts b/comm/third_party/asn1js/src/Enumerated.ts new file mode 100644 index 0000000000..53249a3cf9 --- /dev/null +++ b/comm/third_party/asn1js/src/Enumerated.ts @@ -0,0 +1,22 @@ +import { IntegerParams, Integer, IntegerJson } from "./Integer"; +import { typeStore } from "./TypeStore"; + +export type EnumeratedParams = IntegerParams; +export type EnumeratedJson = IntegerJson; + +export class Enumerated extends Integer { + + static { + typeStore.Enumerated = this; + } + + public static override NAME = "ENUMERATED"; + + constructor(parameters: EnumeratedParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 10; // Enumerated + } + +} diff --git a/comm/third_party/asn1js/src/GeneralString.ts b/comm/third_party/asn1js/src/GeneralString.ts new file mode 100644 index 0000000000..f0ce52a607 --- /dev/null +++ b/comm/third_party/asn1js/src/GeneralString.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type GeneralStringParams = LocalSimpleStringBlockParams; +export type GeneralStringJson = LocalSimpleStringBlockJson; + +export class GeneralString extends LocalSimpleStringBlock { + + static { + typeStore.GeneralString = this; + } + + public static override NAME = "GeneralString"; + + constructor(parameters: GeneralStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 27; // GeneralString + } + +} diff --git a/comm/third_party/asn1js/src/GeneralizedTime.ts b/comm/third_party/asn1js/src/GeneralizedTime.ts new file mode 100644 index 0000000000..7743ae1436 --- /dev/null +++ b/comm/third_party/asn1js/src/GeneralizedTime.ts @@ -0,0 +1,262 @@ +import * as pvutils from "pvutils"; +import { typeStore } from "./TypeStore"; +import { IUTCTime, UTCTimeParams, UTCTimeJson, UTCTime, DateStringEncoding } from "./UTCTime"; + +export interface IGeneralizedTime extends IUTCTime { + millisecond: number; +} + +export type GeneralizedTimeParams = UTCTimeParams; + +export interface GeneralizedTimeJson extends UTCTimeJson { + millisecond: number; +} + +export class GeneralizedTime extends UTCTime { + + static { + typeStore.GeneralizedTime = this; + } + + public static override NAME = "GeneralizedTime"; + + public millisecond: number; + + constructor(parameters: GeneralizedTimeParams = {}) { + super(parameters); + + this.millisecond ??= 0; + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 24; // GeneralizedTime + } + + public override fromDate(inputDate: Date): void { + super.fromDate(inputDate); + this.millisecond = inputDate.getUTCMilliseconds(); + } + + public override toDate(): Date { + return (new Date(Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second, this.millisecond))); + } + + public override fromString(inputString: string): void { + //#region Initial variables + let isUTC = false; + + let timeString = ""; + let dateTimeString = ""; + let fractionPart = 0; + + let parser; + + let hourDifference = 0; + let minuteDifference = 0; + //#endregion + + //#region Convert as UTC time + if (inputString[inputString.length - 1] === "Z") { + timeString = inputString.substring(0, inputString.length - 1); + + isUTC = true; + } + //#endregion + //#region Convert as local time + else { + const number = new Number(inputString[inputString.length - 1]); + + if (isNaN(number.valueOf())) + throw new Error("Wrong input string for conversion"); + + timeString = inputString; + } + //#endregion + + //#region Check that we do not have a "+" and "-" symbols inside UTC time + if (isUTC) { + if (timeString.indexOf("+") !== -1) + throw new Error("Wrong input string for conversion"); + + if (timeString.indexOf("-") !== -1) + throw new Error("Wrong input string for conversion"); + } + //#endregion + //#region Get "UTC time difference" in case of local time + else { + let multiplier = 1; + let differencePosition = timeString.indexOf("+"); + let differenceString = ""; + + if (differencePosition === -1) { + differencePosition = timeString.indexOf("-"); + multiplier = -1; + } + + if (differencePosition !== -1) { + differenceString = timeString.substring(differencePosition + 1); + timeString = timeString.substring(0, differencePosition); + + if ((differenceString.length !== 2) && (differenceString.length !== 4)) + throw new Error("Wrong input string for conversion"); + + let number = parseInt(differenceString.substring(0, 2), 10); + + if (isNaN(number.valueOf())) + throw new Error("Wrong input string for conversion"); + + hourDifference = multiplier * number; + + if (differenceString.length === 4) { + number = parseInt(differenceString.substring(2, 4), 10); + + if (isNaN(number.valueOf())) + throw new Error("Wrong input string for conversion"); + + minuteDifference = multiplier * number; + } + } + } + //#endregion + + //#region Get position of fraction point + let fractionPointPosition = timeString.indexOf("."); // Check for "full stop" symbol + if (fractionPointPosition === -1) + fractionPointPosition = timeString.indexOf(","); // Check for "comma" symbol + //#endregion + + //#region Get fraction part + if (fractionPointPosition !== -1) { + const fractionPartCheck = new Number(`0${timeString.substring(fractionPointPosition)}`); + + if (isNaN(fractionPartCheck.valueOf())) + throw new Error("Wrong input string for conversion"); + + fractionPart = fractionPartCheck.valueOf(); + + dateTimeString = timeString.substring(0, fractionPointPosition); + } + else + dateTimeString = timeString; + //#endregion + + //#region Parse internal date + switch (true) { + case (dateTimeString.length === 8): // "YYYYMMDD" + parser = /(\d{4})(\d{2})(\d{2})/ig; + if (fractionPointPosition !== -1) + throw new Error("Wrong input string for conversion"); // Here we should not have a "fraction point" + break; + case (dateTimeString.length === 10): // "YYYYMMDDHH" + parser = /(\d{4})(\d{2})(\d{2})(\d{2})/ig; + + if (fractionPointPosition !== -1) { + let fractionResult = 60 * fractionPart; + this.minute = Math.floor(fractionResult); + + fractionResult = 60 * (fractionResult - this.minute); + this.second = Math.floor(fractionResult); + + fractionResult = 1000 * (fractionResult - this.second); + this.millisecond = Math.floor(fractionResult); + } + break; + case (dateTimeString.length === 12): // "YYYYMMDDHHMM" + parser = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})/ig; + + if (fractionPointPosition !== -1) { + let fractionResult = 60 * fractionPart; + this.second = Math.floor(fractionResult); + + fractionResult = 1000 * (fractionResult - this.second); + this.millisecond = Math.floor(fractionResult); + } + break; + case (dateTimeString.length === 14): // "YYYYMMDDHHMMSS" + parser = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/ig; + + if (fractionPointPosition !== -1) { + const fractionResult = 1000 * fractionPart; + this.millisecond = Math.floor(fractionResult); + } + break; + default: + throw new Error("Wrong input string for conversion"); + } + //#endregion + + //#region Put parsed values at right places + const parserArray = parser.exec(dateTimeString); + if (parserArray === null) + throw new Error("Wrong input string for conversion"); + + for (let j = 1; j < parserArray.length; j++) { + switch (j) { + case 1: + this.year = parseInt(parserArray[j], 10); + break; + case 2: + this.month = parseInt(parserArray[j], 10); + break; + case 3: + this.day = parseInt(parserArray[j], 10); + break; + case 4: + this.hour = parseInt(parserArray[j], 10) + hourDifference; + break; + case 5: + this.minute = parseInt(parserArray[j], 10) + minuteDifference; + break; + case 6: + this.second = parseInt(parserArray[j], 10); + break; + default: + throw new Error("Wrong input string for conversion"); + } + } + //#endregion + + //#region Get final date + if (isUTC === false) { + const tempDate = new Date(this.year, this.month, this.day, this.hour, this.minute, this.second, this.millisecond); + + this.year = tempDate.getUTCFullYear(); + this.month = tempDate.getUTCMonth(); + this.day = tempDate.getUTCDay(); + this.hour = tempDate.getUTCHours(); + this.minute = tempDate.getUTCMinutes(); + this.second = tempDate.getUTCSeconds(); + this.millisecond = tempDate.getUTCMilliseconds(); + } + //#endregion + } + + public override toString(encoding: DateStringEncoding = "iso"): string { + if (encoding === "iso") { + const outputArray = []; + + outputArray.push(pvutils.padNumber(this.year, 4)); + outputArray.push(pvutils.padNumber(this.month, 2)); + outputArray.push(pvutils.padNumber(this.day, 2)); + outputArray.push(pvutils.padNumber(this.hour, 2)); + outputArray.push(pvutils.padNumber(this.minute, 2)); + outputArray.push(pvutils.padNumber(this.second, 2)); + if (this.millisecond !== 0) { + outputArray.push("."); + outputArray.push(pvutils.padNumber(this.millisecond, 3)); + } + outputArray.push("Z"); + + return outputArray.join(""); + } + + return super.toString(encoding); + } + + public override toJSON(): GeneralizedTimeJson { + return { + ...super.toJSON(), + millisecond: this.millisecond, + }; + } + +} diff --git a/comm/third_party/asn1js/src/GraphicString.ts b/comm/third_party/asn1js/src/GraphicString.ts new file mode 100644 index 0000000000..3d2ab07577 --- /dev/null +++ b/comm/third_party/asn1js/src/GraphicString.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type GraphicStringParams = LocalSimpleStringBlockParams; +export type GraphicStringJson = LocalSimpleStringBlockJson; + +export class GraphicString extends LocalSimpleStringBlock { + + static { + typeStore.GraphicString = this; + } + + public static override NAME = "GraphicString"; + + constructor(parameters: GraphicStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 25; // GraphicString + } + +} diff --git a/comm/third_party/asn1js/src/HexBlock.ts b/comm/third_party/asn1js/src/HexBlock.ts new file mode 100644 index 0000000000..cca34da5de --- /dev/null +++ b/comm/third_party/asn1js/src/HexBlock.ts @@ -0,0 +1,110 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import * as pvtsutils from "pvtsutils"; +import { IBerConvertible } from "./types"; +import { EMPTY_BUFFER, EMPTY_VIEW } from "./internals/constants"; +import { LocalBaseBlockConstructor } from "./internals/LocalBaseBlock"; +import { checkBufferParams } from "./internals/utils"; + +export interface IHexBlock { + isHexOnly: boolean; + valueHex: pvtsutils.BufferSource; +} + +export interface HexBlockJson extends Omit<IHexBlock, "valueHex"> { + valueHex: string; +} + +export type HexBlockParams = Partial<IHexBlock>; +/** + * Class used as a base block for all remaining ASN.1 classes + */ +export function HexBlock<T extends LocalBaseBlockConstructor>(BaseClass: T) { + return class Some extends BaseClass implements IHexBlock, IBerConvertible { + + public static override NAME = "hexBlock"; + + public isHexOnly: boolean; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + public get valueHex(): ArrayBuffer { + return this.valueHexView.slice().buffer; + } + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + public set valueHex(value: ArrayBuffer) { + this.valueHexView = new Uint8Array(value); + } + /** + * Binary data in Uint8Array representation + * + * @since 3.0.0 + */ + public valueHexView: Uint8Array; + + constructor(...args: any[]) { + super(...args); + + const params: HexBlockParams = args[0] || {}; + this.isHexOnly = params.isHexOnly ?? false; + this.valueHexView = params.valueHex ? pvtsutils.BufferSourceConverter.toUint8Array(params.valueHex) : EMPTY_VIEW; + } + + public fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + // Basic check for parameters + const view = inputBuffer instanceof ArrayBuffer ? new Uint8Array(inputBuffer) : inputBuffer; + if (!checkBufferParams(this, view, inputOffset, inputLength)) { + return -1; + } + + const endLength = inputOffset + inputLength; + + // Initial checks + this.valueHexView = view.subarray(inputOffset, endLength); + if (!this.valueHexView.length) { + this.warnings.push("Zero buffer length"); + + return inputOffset; + } + + this.blockLength = inputLength; + + return endLength; + } + + public toBER(sizeOnly = false): ArrayBuffer { + if (!this.isHexOnly) { + this.error = "Flag 'isHexOnly' is not set, abort"; + + return EMPTY_BUFFER; + } + + if (sizeOnly) { + return new ArrayBuffer(this.valueHexView.byteLength); + } + + // Don't copy data if View is not offset + return (this.valueHexView.byteLength === this.valueHexView.buffer.byteLength) + ? this.valueHexView.buffer + : this.valueHexView.slice().buffer; + } + + /** + * Returns a JSON representation of an object + * @returns JSON object + */ + public override toJSON() { + return { + ...super.toJSON(), + isHexOnly: this.isHexOnly, + valueHex: pvtsutils.Convert.ToHex(this.valueHexView), + }; + } + + }; +} diff --git a/comm/third_party/asn1js/src/IA5String.ts b/comm/third_party/asn1js/src/IA5String.ts new file mode 100644 index 0000000000..f0021cec51 --- /dev/null +++ b/comm/third_party/asn1js/src/IA5String.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type IA5StringParams = LocalSimpleStringBlockParams; +export type IA5StringJson = LocalSimpleStringBlockJson; + +export class IA5String extends LocalSimpleStringBlock { + + static { + typeStore.IA5String = this; + } + + public static override NAME = "IA5String"; + + constructor(parameters: IA5StringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 22; // IA5String + } + +} diff --git a/comm/third_party/asn1js/src/Integer.ts b/comm/third_party/asn1js/src/Integer.ts new file mode 100644 index 0000000000..fd3d1d9e70 --- /dev/null +++ b/comm/third_party/asn1js/src/Integer.ts @@ -0,0 +1,103 @@ +import * as pvtsutils from "pvtsutils"; +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { LocalIntegerValueBlockParams, LocalIntegerValueBlock, LocalIntegerValueBlockJson } from "./internals/LocalIntegerValueBlock"; +import { assertBigInt } from "./internals/utils"; +import { typeStore } from "./TypeStore"; +import { ViewWriter } from "./ViewWriter"; + +export interface IntegerParams extends BaseBlockParams, LocalIntegerValueBlockParams { } +export type IntegerJson = BaseBlockJson<LocalIntegerValueBlockJson>; + +export class Integer extends BaseBlock<LocalIntegerValueBlock, LocalIntegerValueBlockJson> { + + static { + typeStore.Integer = this; + } + + public static override NAME = "INTEGER"; + + constructor(parameters: IntegerParams = {}) { + super(parameters, LocalIntegerValueBlock); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 2; // Integer + } + + /** + * Converts Integer into BigInt + * @throws Throws Error if BigInt is not supported + * @since 3.0.0 + */ + public toBigInt(): bigint { + assertBigInt(); + + return BigInt(this.valueBlock.toString()); + } + + /** + * Creates Integer from BigInt value + * @param value BigInt value + * @returns ASN.1 Integer + * @throws Throws Error if BigInt is not supported + * @since 3.0.0 + */ + public static fromBigInt(value: number | string | bigint | boolean): Integer { + assertBigInt(); + + const bigIntValue = BigInt(value); + const writer = new ViewWriter(); + + const hex = bigIntValue.toString(16).replace(/^-/, ""); + const view = new Uint8Array(pvtsutils.Convert.FromHex(hex)); + + if (bigIntValue < 0) { + // a negative number + const first = new Uint8Array(view.length + (view[0] & 0x80 ? 1 : 0)); + first[0] |= 0x80; + + const firstInt = BigInt(`0x${pvtsutils.Convert.ToHex(first)}`); + const secondInt = firstInt + bigIntValue; + const second = pvtsutils.BufferSourceConverter.toUint8Array(pvtsutils.Convert.FromHex(secondInt.toString(16))); + second[0] |= 0x80; + + writer.write(second); + } else { + // a positive number + if (view[0] & 0x80) { + writer.write(new Uint8Array([0])); + } + writer.write(view); + } + + const res = new Integer({ + valueHex: writer.final(), + }); + + return res; + } + + public convertToDER(): Integer { + const integer = new Integer({ valueHex: this.valueBlock.valueHexView }); + + integer.valueBlock.toDER(); + + return integer; + } + + /** + * Convert current Integer value from DER to BER format + * @returns + */ + public convertFromDER(): Integer { + return new Integer({ + valueHex: this.valueBlock.valueHexView[0] === 0 + ? this.valueBlock.valueHexView.subarray(1) + : this.valueBlock.valueHexView, + }); + } + + protected override onAsciiEncoding(): string { + return `${(this.constructor as typeof Integer).NAME} : ${this.valueBlock.toString()}`; + } + +} diff --git a/comm/third_party/asn1js/src/Null.ts b/comm/third_party/asn1js/src/Null.ts new file mode 100644 index 0000000000..8c4c4c580d --- /dev/null +++ b/comm/third_party/asn1js/src/Null.ts @@ -0,0 +1,65 @@ +import { ViewWriter } from "./ViewWriter"; +import { ValueBlock, ValueBlockJson } from "./ValueBlock"; +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { typeStore } from "./TypeStore"; + +export type NullParams = BaseBlockParams; +export type NullJson = BaseBlockJson<ValueBlockJson>; + +export class Null extends BaseBlock<ValueBlock, ValueBlockJson> { + + static { + typeStore.Null = this; + } + + public static override NAME = "NULL"; + + constructor(parameters: NullParams = {}) { + super(parameters, ValueBlock); // We will not have a call to "Null value block" because of specified FROM_BER and TO_BER functions + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 5; // Null + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + 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); + } + + public override toBER(sizeOnly?: boolean, writer?: ViewWriter): ArrayBuffer { + 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; + } + + protected override onAsciiEncoding(): string { + return `${(this.constructor as typeof Null).NAME}`; + } + +} diff --git a/comm/third_party/asn1js/src/NumericString.ts b/comm/third_party/asn1js/src/NumericString.ts new file mode 100644 index 0000000000..a917fd1dc2 --- /dev/null +++ b/comm/third_party/asn1js/src/NumericString.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type NumericStringParams = LocalSimpleStringBlockParams; +export type NumericStringJson = LocalSimpleStringBlockJson; + +export class NumericString extends LocalSimpleStringBlock { + + static { + typeStore.NumericString = this; + } + + public static override NAME = "NumericString"; + + constructor(parameters: NumericStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 18; // NumericString + } + +} diff --git a/comm/third_party/asn1js/src/ObjectIdentifier.ts b/comm/third_party/asn1js/src/ObjectIdentifier.ts new file mode 100644 index 0000000000..7608a3cf7d --- /dev/null +++ b/comm/third_party/asn1js/src/ObjectIdentifier.ts @@ -0,0 +1,53 @@ +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { LocalObjectIdentifierValueBlockParams, LocalObjectIdentifierValueBlock, LocalObjectIdentifierValueBlockJson } from "./internals/LocalObjectIdentifierValueBlock"; +import { typeStore } from "./TypeStore"; + +export interface ObjectIdentifierParams extends BaseBlockParams, LocalObjectIdentifierValueBlockParams { } +export interface ObjectIdentifierJson extends BaseBlockJson<LocalObjectIdentifierValueBlockJson> { + value: string; +} + +export class ObjectIdentifier extends BaseBlock<LocalObjectIdentifierValueBlock, LocalObjectIdentifierValueBlockJson> { + + static { + typeStore.ObjectIdentifier = this; + } + + public static override NAME = "OBJECT IDENTIFIER"; + + /** + * Gets string representation of Object Identifier + * @since 3.0.0 + */ + public getValue(): string { + return this.valueBlock.toString(); + } + + /** + * Sets Object Identifier value from string + * @param value String value + * @since 3.0.0 + */ + public setValue(value: string): void { + this.valueBlock.fromString(value); + } + + constructor(parameters: ObjectIdentifierParams = {}) { + super(parameters, LocalObjectIdentifierValueBlock); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 6; // OBJECT IDENTIFIER + } + + protected override onAsciiEncoding(): string { + return `${(this.constructor as typeof ObjectIdentifier).NAME} : ${this.valueBlock.toString() || "empty"}`; + } + + public override toJSON(): ObjectIdentifierJson { + return { + ...super.toJSON(), + value: this.getValue(), + }; + } + +} diff --git a/comm/third_party/asn1js/src/OctetString.ts b/comm/third_party/asn1js/src/OctetString.ts new file mode 100644 index 0000000000..df57e9bc6e --- /dev/null +++ b/comm/third_party/asn1js/src/OctetString.ts @@ -0,0 +1,103 @@ +import * as pvtsutils from "pvtsutils"; +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { Constructed } from "./Constructed"; +import { LocalOctetStringValueBlockParams, LocalOctetStringValueBlock, LocalOctetStringValueBlockJson } from "./internals/LocalOctetStringValueBlock"; +import { OCTET_STRING_NAME } from "./internals/constants"; +import { localFromBER } from "./parser"; +import { typeStore } from "./TypeStore"; + +export interface OctetStringParams extends BaseBlockParams, LocalOctetStringValueBlockParams { } +export type OctetStringJson = BaseBlockJson<LocalOctetStringValueBlockJson>; + +export class OctetString extends BaseBlock<LocalOctetStringValueBlock, LocalOctetStringValueBlockJson> { + + static { + typeStore.OctetString = this; + } + + public static override NAME = OCTET_STRING_NAME; + + constructor({ + idBlock = {}, + lenBlock = {}, + ...parameters + }: OctetStringParams = {}) { + parameters.isConstructed ??= !!parameters.value?.length; + super({ + idBlock: { + isConstructed: parameters.isConstructed, + ...idBlock, + }, + lenBlock: { + ...lenBlock, + isIndefiniteForm: !!parameters.isIndefiniteForm, + }, + ...parameters, + }, LocalOctetStringValueBlock); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 4; // OctetString + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + this.valueBlock.isConstructed = this.idBlock.isConstructed; + this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm; + + // Ability to encode empty OCTET STRING + if (inputLength === 0) { + if (this.idBlock.error.length === 0) + this.blockLength += this.idBlock.blockLength; + + if (this.lenBlock.error.length === 0) + this.blockLength += this.lenBlock.blockLength; + + return inputOffset; + } + + if (!this.valueBlock.isConstructed) { + const view = inputBuffer instanceof ArrayBuffer ? new Uint8Array(inputBuffer) : inputBuffer; + const buf = view.subarray(inputOffset, inputOffset + inputLength); + try { + if (buf.byteLength) { + const asn = localFromBER(buf, 0, buf.byteLength); + if (asn.offset !== -1 && asn.offset === inputLength) { + this.valueBlock.value = [asn.result as OctetString]; + } + } + } catch (e) { + // nothing + } + } + + return super.fromBER(inputBuffer, inputOffset, inputLength); + } + + protected override onAsciiEncoding(): string { + if (this.valueBlock.isConstructed || (this.valueBlock.value && this.valueBlock.value.length)) { + return Constructed.prototype.onAsciiEncoding.call(this); + } + + return `${(this.constructor as typeof OctetString).NAME} : ${pvtsutils.Convert.ToHex(this.valueBlock.valueHexView)}`; + } + + /** + * Returns OctetString value. If OctetString is constructed, returns concatenated internal OctetString values + * @returns Array buffer + * @since 3.0.0 + */ + public getValue(): ArrayBuffer { + if (!this.idBlock.isConstructed) { + return this.valueBlock.valueHexView.slice().buffer; + } + + const array: ArrayBuffer[] = []; + for (const content of this.valueBlock.value) { + if (content instanceof OctetString) { + array.push(content.valueBlock.valueHexView); + } + } + + return pvtsutils.BufferSourceConverter.concat(array); + } + +} diff --git a/comm/third_party/asn1js/src/Primitive.ts b/comm/third_party/asn1js/src/Primitive.ts new file mode 100644 index 0000000000..49b25c343b --- /dev/null +++ b/comm/third_party/asn1js/src/Primitive.ts @@ -0,0 +1,22 @@ +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { LocalPrimitiveValueBlock, LocalPrimitiveValueBlockJson, LocalPrimitiveValueBlockParams } from "./internals/LocalPrimitiveValueBlock"; +import { typeStore } from "./TypeStore"; + +export interface PrimitiveParams extends BaseBlockParams, LocalPrimitiveValueBlockParams { } +export type PrimitiveJson = BaseBlockJson<LocalPrimitiveValueBlockJson>; + +export class Primitive extends BaseBlock<LocalPrimitiveValueBlock, LocalPrimitiveValueBlockJson> { + + static { + typeStore.Primitive = this; + } + + public static override NAME = "PRIMITIVE"; + + constructor(parameters: PrimitiveParams = {}) { + super(parameters, LocalPrimitiveValueBlock); + + this.idBlock.isConstructed = false; + } + +} diff --git a/comm/third_party/asn1js/src/PrintableString.ts b/comm/third_party/asn1js/src/PrintableString.ts new file mode 100644 index 0000000000..3eca544079 --- /dev/null +++ b/comm/third_party/asn1js/src/PrintableString.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type PrintableStringParams = LocalSimpleStringBlockParams; +export type PrintableStringJson = LocalSimpleStringBlockJson; + +export class PrintableString extends LocalSimpleStringBlock { + + static { + typeStore.PrintableString = this; + } + + public static override NAME = "PrintableString"; + + constructor(parameters: PrintableStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 19; // PrintableString + } + +} diff --git a/comm/third_party/asn1js/src/RawData.ts b/comm/third_party/asn1js/src/RawData.ts new file mode 100644 index 0000000000..86228739fc --- /dev/null +++ b/comm/third_party/asn1js/src/RawData.ts @@ -0,0 +1,51 @@ +import * as pvtsutils from "pvtsutils"; +import { IBerConvertible } from "./types"; +import { EMPTY_VIEW } from "./internals/constants"; + +export interface IRawData { + data: ArrayBuffer; +} + +export type RawDataParams = Partial<IRawData>; + +/** + * Special class providing ability to have "toBER/fromBER" for raw ArrayBuffer + */ +export class RawData implements IBerConvertible { + + + /** + * @deprecated Since v3.0.0 + */ + public get data(): ArrayBuffer { + return this.dataView.slice().buffer; + } + + /** + * @deprecated Since v3.0.0 + */ + public set data(value: ArrayBuffer) { + this.dataView = pvtsutils.BufferSourceConverter.toUint8Array(value); + } + + /** + * @since 3.0.0 + */ + public dataView: Uint8Array; + + constructor({ data = EMPTY_VIEW }: RawDataParams = {}) { + this.dataView = pvtsutils.BufferSourceConverter.toUint8Array(data); + } + + public fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + const endLength = inputOffset + inputLength; + this.dataView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer).subarray(inputOffset, endLength); + + return endLength; + } + + public toBER(sizeOnly?: boolean): ArrayBuffer { + return this.dataView.slice().buffer; + } + +} diff --git a/comm/third_party/asn1js/src/RelativeObjectIdentifier.ts b/comm/third_party/asn1js/src/RelativeObjectIdentifier.ts new file mode 100644 index 0000000000..997220eae4 --- /dev/null +++ b/comm/third_party/asn1js/src/RelativeObjectIdentifier.ts @@ -0,0 +1,54 @@ +import { BaseBlock, BaseBlockJson, BaseBlockParams } from "./BaseBlock"; +import { LocalRelativeObjectIdentifierValueBlockParams, LocalRelativeObjectIdentifierValueBlock, LocalRelativeObjectIdentifierValueBlockJson } from "./internals/LocalRelativeObjectIdentifierValueBlock"; +import { typeStore } from "./TypeStore"; + + +export interface RelativeObjectIdentifierParams extends BaseBlockParams, LocalRelativeObjectIdentifierValueBlockParams { } +export interface RelativeObjectIdentifierJson extends BaseBlockJson<LocalRelativeObjectIdentifierValueBlockJson> { + value: string; +} + +export class RelativeObjectIdentifier extends BaseBlock<LocalRelativeObjectIdentifierValueBlock, LocalRelativeObjectIdentifierValueBlockJson> { + + static { + typeStore.RelativeObjectIdentifier = this; + } + + /** + * Gets string representation of Relative Object Identifier + * @since 3.0.0 + */ + public getValue(): string { + return this.valueBlock.toString(); + } + + /** + * Sets Relative Object Identifier value from string + * @param value String value + * @since 3.0.0 + */ + public setValue(value: string): void { + this.valueBlock.fromString(value); + } + + public static override NAME = "RelativeObjectIdentifier"; + + constructor(parameters: RelativeObjectIdentifierParams = {}) { + super(parameters, LocalRelativeObjectIdentifierValueBlock); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 13; // RELATIVE OBJECT IDENTIFIER + } + + protected override onAsciiEncoding(): string { + return `${(this.constructor as typeof RelativeObjectIdentifier).NAME} : ${this.valueBlock.toString() || "empty"}`; + } + + public override toJSON(): RelativeObjectIdentifierJson { + return { + ...super.toJSON(), + value: this.getValue(), + }; + } + +} diff --git a/comm/third_party/asn1js/src/Repeated.ts b/comm/third_party/asn1js/src/Repeated.ts new file mode 100644 index 0000000000..38142fa533 --- /dev/null +++ b/comm/third_party/asn1js/src/Repeated.ts @@ -0,0 +1,26 @@ +import { IAny, Any } from "./Any"; + +export interface IRepeated extends IAny { + value: Any; + local: boolean; +} + +export type RepeatedParams = Partial<IRepeated>; + +export class Repeated extends Any { + + public value: Any; + public local: boolean; + + constructor({ + value = new Any(), + local = false, + ...parameters + }: RepeatedParams = {}) { + super(parameters); + + this.value = value; + this.local = local; // Could local or global array to store elements + } + +} diff --git a/comm/third_party/asn1js/src/Sequence.ts b/comm/third_party/asn1js/src/Sequence.ts new file mode 100644 index 0000000000..3303283bb2 --- /dev/null +++ b/comm/third_party/asn1js/src/Sequence.ts @@ -0,0 +1,22 @@ +import { ConstructedParams, Constructed, ConstructedJson } from "./Constructed"; +import { typeStore } from "./TypeStore"; + +export type SequenceParams = ConstructedParams; +export type SequenceJson = ConstructedJson; + +export class Sequence extends Constructed { + + static { + typeStore.Sequence = this; + } + + public static override NAME = "SEQUENCE"; + + constructor(parameters: SequenceParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 16; // Sequence + } + +} diff --git a/comm/third_party/asn1js/src/Set.ts b/comm/third_party/asn1js/src/Set.ts new file mode 100644 index 0000000000..1bbdec447c --- /dev/null +++ b/comm/third_party/asn1js/src/Set.ts @@ -0,0 +1,22 @@ +import { ConstructedParams, Constructed, ConstructedJson } from "./Constructed"; +import { typeStore } from "./TypeStore"; + +export type SetParams = ConstructedParams; +export type SetJson = ConstructedJson; + +export class Set extends Constructed { + + static { + typeStore.Set = this; + } + + public static override NAME = "SET"; + + constructor(parameters: SetParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 17; // Set + } + +} diff --git a/comm/third_party/asn1js/src/TIME.ts b/comm/third_party/asn1js/src/TIME.ts new file mode 100644 index 0000000000..97fee031b0 --- /dev/null +++ b/comm/third_party/asn1js/src/TIME.ts @@ -0,0 +1,22 @@ +import { typeStore } from "./TypeStore"; +import { Utf8StringParams, Utf8String, Utf8StringJson } from "./Utf8String"; + +export type TIMEParams = Utf8StringParams; +export type TIMEJson = Utf8StringJson; + +export class TIME extends Utf8String { + + static { + typeStore.TIME = this; + } + + public static override NAME = "TIME"; + + constructor(parameters: TIMEParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 14; // Time + } + +} diff --git a/comm/third_party/asn1js/src/TeletexString.ts b/comm/third_party/asn1js/src/TeletexString.ts new file mode 100644 index 0000000000..0f7e38d36c --- /dev/null +++ b/comm/third_party/asn1js/src/TeletexString.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type TeletexStringParams = LocalSimpleStringBlockParams; +export type TeletexStringJson = LocalSimpleStringBlockJson; + +export class TeletexString extends LocalSimpleStringBlock { + + static { + typeStore.TeletexString = this; + } + + public static override NAME = "TeletexString"; + + constructor(parameters: TeletexStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 20; // TeletexString + } + +} diff --git a/comm/third_party/asn1js/src/TimeOfDay.ts b/comm/third_party/asn1js/src/TimeOfDay.ts new file mode 100644 index 0000000000..78c8fac4ec --- /dev/null +++ b/comm/third_party/asn1js/src/TimeOfDay.ts @@ -0,0 +1,22 @@ +import { typeStore } from "./TypeStore"; +import { Utf8StringParams, Utf8String, Utf8StringJson } from "./Utf8String"; + +export type TimeOfDayParams = Utf8StringParams; +export type TimeOfDayJson = Utf8StringJson; + +export class TimeOfDay extends Utf8String { + + static { + typeStore.TimeOfDay = this; + } + + public static override NAME = "TimeOfDay"; + + constructor(parameters: TimeOfDayParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 32; // TimeOfDay + } + +} diff --git a/comm/third_party/asn1js/src/TypeStore.ts b/comm/third_party/asn1js/src/TypeStore.ts new file mode 100644 index 0000000000..17b86ae97d --- /dev/null +++ b/comm/third_party/asn1js/src/TypeStore.ts @@ -0,0 +1,71 @@ +import type { BaseBlock } from "./BaseBlock"; +import type { BitString } from "./BitString"; +import type { BmpString } from "./BmpString"; +import type { Boolean as AsnBoolean } from "./Boolean"; +import type { CharacterString } from "./CharacterString"; +import type { Constructed } from "./Constructed"; +import type { DATE } from "./DATE"; +import type { DateTime } from "./DateTime"; +import type { Duration } from "./Duration"; +import type { EndOfContent } from "./EndOfContent"; +import type { Enumerated } from "./Enumerated"; +import type { GeneralizedTime } from "./GeneralizedTime"; +import type { GeneralString } from "./GeneralString"; +import type { GraphicString } from "./GraphicString"; +import type { IA5String } from "./IA5String"; +import type { Integer } from "./Integer"; +import type { Null } from "./Null"; +import type { NumericString } from "./NumericString"; +import type { ObjectIdentifier } from "./ObjectIdentifier"; +import type { OctetString } from "./OctetString"; +import type { Primitive } from "./Primitive"; +import type { PrintableString } from "./PrintableString"; +import type { RelativeObjectIdentifier } from "./RelativeObjectIdentifier"; +import type { Sequence } from "./Sequence"; +import type { Set } from "./Set"; +import type { TeletexString } from "./TeletexString"; +import type { TIME } from "./TIME"; +import type { TimeOfDay } from "./TimeOfDay"; +import type { UniversalString } from "./UniversalString"; +import type { UTCTime } from "./UTCTime"; +import type { Utf8String } from "./Utf8String"; +import type { VideotexString } from "./VideotexString"; +import type { VisibleString } from "./VisibleString"; + +export type AsnType = BaseBlock | EndOfContent | AsnBoolean | Integer | BitString | OctetString | Null | ObjectIdentifier | Enumerated | Utf8String | RelativeObjectIdentifier | TIME | Sequence | Set | NumericString | PrintableString | TeletexString | VideotexString | IA5String | UTCTime | GeneralizedTime | GraphicString | VisibleString | GeneralString | UniversalString | CharacterString | BmpString | DATE | TimeOfDay | DateTime | Duration | Constructed | Primitive; + +export interface TypeStore { + BitString: typeof BitString; + BmpString: typeof BmpString; + Boolean: typeof AsnBoolean; + CharacterString: typeof CharacterString; + Constructed: typeof Constructed; + DATE: typeof DATE; + DateTime: typeof DateTime; + Duration: typeof Duration; + EndOfContent: typeof EndOfContent; + Enumerated: typeof Enumerated; + GeneralizedTime: typeof GeneralizedTime; + GeneralString: typeof GeneralString; + GraphicString: typeof GraphicString; + IA5String: typeof IA5String; + Integer: typeof Integer; + Null: typeof Null; + NumericString: typeof NumericString; + ObjectIdentifier: typeof ObjectIdentifier; + OctetString: typeof OctetString; + Primitive: typeof Primitive; + PrintableString: typeof PrintableString; + RelativeObjectIdentifier: typeof RelativeObjectIdentifier; + Sequence: typeof Sequence; + Set: typeof Set; + TeletexString: typeof TeletexString; + TIME: typeof TIME; + TimeOfDay: typeof TimeOfDay; + UniversalString: typeof UniversalString; + UTCTime: typeof UTCTime; + Utf8String: typeof Utf8String; + VideotexString: typeof VideotexString; + VisibleString: typeof VisibleString; +} +export const typeStore = {} as TypeStore; diff --git a/comm/third_party/asn1js/src/UTCTime.ts b/comm/third_party/asn1js/src/UTCTime.ts new file mode 100644 index 0000000000..a387556051 --- /dev/null +++ b/comm/third_party/asn1js/src/UTCTime.ts @@ -0,0 +1,172 @@ +import * as pvtsutils from "pvtsutils"; +import * as pvutils from "pvutils"; +import { BaseBlockJson, StringEncoding } from "./BaseBlock"; +import { LocalSimpleStringValueBlockJson } from "./internals/LocalSimpleStringValueBlock"; +import { IDateConvertible } from "./types"; +import { typeStore } from "./TypeStore"; +import { VisibleStringParams, VisibleString } from "./VisibleString"; + +export interface IUTCTime { + year: number; + month: number; + day: number; + hour: number; + minute: number; + second: number; +} + +export interface UTCTimeParams extends VisibleStringParams { + value?: string; + valueDate?: Date; +} +export interface UTCTimeJson extends BaseBlockJson<LocalSimpleStringValueBlockJson>, IUTCTime { } + +export type DateStringEncoding = StringEncoding | "iso"; + +export class UTCTime extends VisibleString implements IUTCTime, IDateConvertible { + + static { + typeStore.UTCTime = this; + } + + public static override NAME = "UTCTime"; + + public year: number; + public month: number; + public day: number; + public hour: number; + public minute: number; + public second: number; + + constructor({ + value, + valueDate, + ...parameters + }: UTCTimeParams = {}) { + super(parameters); + + this.year = 0; + this.month = 0; + this.day = 0; + this.hour = 0; + this.minute = 0; + this.second = 0; + + //#region Create UTCTime from ASN.1 UTC string value + if (value) { + this.fromString(value); + + this.valueBlock.valueHexView = new Uint8Array(value.length); + + for (let i = 0; i < value.length; i++) + this.valueBlock.valueHexView[i] = value.charCodeAt(i); + } + //#endregion + //#region Create GeneralizedTime from JavaScript Date type + if (valueDate) { + this.fromDate(valueDate); + this.valueBlock.valueHexView = new Uint8Array(this.toBuffer()); + } + //#endregion + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 23; // UTCTime + } + + public override fromBuffer(inputBuffer: ArrayBuffer | Uint8Array): void { + this.fromString(String.fromCharCode.apply(null, pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer) as unknown as number[])); + } + + /** + * Function converting ASN.1 internal string into ArrayBuffer + * @returns + */ + public toBuffer(): ArrayBuffer { + const str = this.toString(); // TODO use this.valueBlock.value and update toString + + const buffer = new ArrayBuffer(str.length); + const view = new Uint8Array(buffer); + + for (let i = 0; i < str.length; i++) + view[i] = str.charCodeAt(i); + + return buffer; + } + + /** + * Function converting "Date" object into ASN.1 internal string + * @param {!Date} inputDate JavaScript "Date" object + */ + public fromDate(inputDate: Date): void { + this.year = inputDate.getUTCFullYear(); + this.month = inputDate.getUTCMonth() + 1; + this.day = inputDate.getUTCDate(); + this.hour = inputDate.getUTCHours(); + this.minute = inputDate.getUTCMinutes(); + this.second = inputDate.getUTCSeconds(); + } + + public toDate(): Date { + return (new Date(Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second))); + } + + public override fromString(inputString: string): void { + //#region Parse input string + const parser = /(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z/ig; + const parserArray = parser.exec(inputString); + if (parserArray === null) { + this.error = "Wrong input string for conversion"; + + return; + } + //#endregion + //#region Store parsed values + const year = parseInt(parserArray[1], 10); + if (year >= 50) + this.year = 1900 + year; + + else + this.year = 2000 + year; + + this.month = parseInt(parserArray[2], 10); + this.day = parseInt(parserArray[3], 10); + this.hour = parseInt(parserArray[4], 10); + this.minute = parseInt(parserArray[5], 10); + this.second = parseInt(parserArray[6], 10); + //#endregion + } + + public override toString(encoding: DateStringEncoding = "iso"): string { + if (encoding === "iso") { + const outputArray = new Array(7); + + outputArray[0] = pvutils.padNumber(((this.year < 2000) ? (this.year - 1900) : (this.year - 2000)), 2); + outputArray[1] = pvutils.padNumber(this.month, 2); + outputArray[2] = pvutils.padNumber(this.day, 2); + outputArray[3] = pvutils.padNumber(this.hour, 2); + outputArray[4] = pvutils.padNumber(this.minute, 2); + outputArray[5] = pvutils.padNumber(this.second, 2); + outputArray[6] = "Z"; + + return outputArray.join(""); + } + + return super.toString(encoding); + } + + protected override onAsciiEncoding(): string { + return `${(this.constructor as typeof UTCTime).NAME} : ${this.toDate().toISOString()}`; + } + + public override toJSON(): UTCTimeJson { + return { + ...super.toJSON(), + year: this.year, + month: this.month, + day: this.day, + hour: this.hour, + minute: this.minute, + second: this.second, + }; + } + +} diff --git a/comm/third_party/asn1js/src/UniversalString.ts b/comm/third_party/asn1js/src/UniversalString.ts new file mode 100644 index 0000000000..fce5b1d0ac --- /dev/null +++ b/comm/third_party/asn1js/src/UniversalString.ts @@ -0,0 +1,24 @@ +import { LocalUniversalStringValueBlockParams, LocalUniversalStringValueBlock, LocalUniversalStringValueBlockJson } from "./internals/LocalUniversalStringValueBlockParams"; +import { typeStore } from "./TypeStore"; + +export type UniversalStringParams = LocalUniversalStringValueBlockParams; +export type UniversalStringJson = LocalUniversalStringValueBlockJson; + +export class UniversalString extends LocalUniversalStringValueBlock { + + static { + typeStore.UniversalString = this; + } + + public static override NAME = "UniversalString"; + + constructor({ + ...parameters + }: UniversalStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 28; // UniversalString + } + +} diff --git a/comm/third_party/asn1js/src/Utf8String.ts b/comm/third_party/asn1js/src/Utf8String.ts new file mode 100644 index 0000000000..2713cd1b95 --- /dev/null +++ b/comm/third_party/asn1js/src/Utf8String.ts @@ -0,0 +1,24 @@ +import { BaseBlockJson } from "./BaseBlock"; +import { BaseStringBlockParams } from "./BaseStringBlock"; +import { LocalUtf8StringValueBlockParams, LocalUtf8StringValueBlock, LocalUtf8StringValueBlockJson } from "./internals/LocalUtf8StringValueBlock"; +import { typeStore } from "./TypeStore"; + +export interface Utf8StringParams extends BaseStringBlockParams, LocalUtf8StringValueBlockParams { } +export type Utf8StringJson = BaseBlockJson<LocalUtf8StringValueBlockJson>; + +export class Utf8String extends LocalUtf8StringValueBlock { + + static { + typeStore.Utf8String = this; + } + + public static override NAME = "UTF8String"; + + constructor(parameters: Utf8StringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 12; // Utf8String + } + +} diff --git a/comm/third_party/asn1js/src/ValueBlock.ts b/comm/third_party/asn1js/src/ValueBlock.ts new file mode 100644 index 0000000000..9463214aef --- /dev/null +++ b/comm/third_party/asn1js/src/ValueBlock.ts @@ -0,0 +1,21 @@ +import { IBerConvertible } from "./types"; +import * as internals from "./internals/LocalBaseBlock"; +import { ViewWriter } from "./ViewWriter"; + +export type IValueBlock = internals.ILocalBaseBlock; +export type ValueBlockParams = internals.LocalBaseBlockParams; +export type ValueBlockJson = internals.LocalBaseBlockJson; + +export class ValueBlock extends internals.LocalBaseBlock implements IValueBlock, IBerConvertible { + + public static override NAME = "valueBlock"; + + public fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + throw TypeError("User need to make a specific function in a class which extends 'ValueBlock'"); + } + + public toBER(sizeOnly?: boolean, writer?: ViewWriter): ArrayBuffer { + throw TypeError("User need to make a specific function in a class which extends 'ValueBlock'"); + } + +} diff --git a/comm/third_party/asn1js/src/VideotexString.ts b/comm/third_party/asn1js/src/VideotexString.ts new file mode 100644 index 0000000000..d1867b602e --- /dev/null +++ b/comm/third_party/asn1js/src/VideotexString.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type VideotexStringParams = LocalSimpleStringBlockParams; +export type VideotexStringJson = LocalSimpleStringBlockJson; + +export class VideotexString extends LocalSimpleStringBlock { + + static { + typeStore.VideotexString = this; + } + + public static override NAME = "VideotexString"; + + constructor(parameters: VideotexStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 21; // VideotexString + } + +} diff --git a/comm/third_party/asn1js/src/ViewWriter.ts b/comm/third_party/asn1js/src/ViewWriter.ts new file mode 100644 index 0000000000..1df3a1170c --- /dev/null +++ b/comm/third_party/asn1js/src/ViewWriter.ts @@ -0,0 +1,22 @@ +import * as utils from "./internals/utils"; + +export class ViewWriter { + + public items: ArrayBuffer[] = []; + + /** + * Writes buffer + * @param buf + */ + public write(buf: ArrayBuffer): void { + this.items.push(buf); + } + + /** + * Concatenates all buffers + * @returns Concatenated buffer + */ + public final(): ArrayBuffer { + return utils.concat(this.items); + } +} diff --git a/comm/third_party/asn1js/src/VisibleString.ts b/comm/third_party/asn1js/src/VisibleString.ts new file mode 100644 index 0000000000..aead80a4cc --- /dev/null +++ b/comm/third_party/asn1js/src/VisibleString.ts @@ -0,0 +1,22 @@ +import { LocalSimpleStringBlockParams, LocalSimpleStringBlock, LocalSimpleStringBlockJson } from "./internals/LocalSimpleStringBlock"; +import { typeStore } from "./TypeStore"; + +export type VisibleStringParams = LocalSimpleStringBlockParams; +export type VisibleStringJson = LocalSimpleStringBlockJson; + +export class VisibleString extends LocalSimpleStringBlock { + + static { + typeStore.VisibleString = this; + } + + public static override NAME = "VisibleString"; + + constructor(parameters: VisibleStringParams = {}) { + super(parameters); + + this.idBlock.tagClass = 1; // UNIVERSAL + this.idBlock.tagNumber = 26; // VisibleString + } + +} diff --git a/comm/third_party/asn1js/src/index.ts b/comm/third_party/asn1js/src/index.ts new file mode 100644 index 0000000000..a4b15b34af --- /dev/null +++ b/comm/third_party/asn1js/src/index.ts @@ -0,0 +1,59 @@ +export * from "./types"; + +// basic +export * from "./ViewWriter"; +export * from "./HexBlock"; +export * from "./ValueBlock"; +export * from "./BaseBlock"; +export * from "./BaseStringBlock"; + +export * from "./Primitive"; +export * from "./Constructed"; +export * from "./EndOfContent"; + +// common +export * from "./Null"; +export * from "./Boolean"; +export * from "./OctetString"; +export * from "./BitString"; +export * from "./Integer"; +export * from "./Enumerated"; +export * from "./ObjectIdentifier"; +export * from "./RelativeObjectIdentifier"; +export * from "./Sequence"; +export * from "./Set"; + +// strings +export * from "./Utf8String"; +export * from "./BmpString"; +export * from "./UniversalString"; +export * from "./NumericString"; +export * from "./PrintableString"; +export * from "./TeletexString"; +export * from "./VideotexString"; +export * from "./IA5String"; +export * from "./GraphicString"; +export * from "./VisibleString"; +export * from "./GeneralString"; +export * from "./CharacterString"; + +// date and time +export * from "./UTCTime"; +export * from "./GeneralizedTime"; +export * from "./DATE"; +export * from "./TimeOfDay"; +export * from "./DateTime"; +export * from "./Duration"; +export * from "./TIME"; + +// schema types +export * from "./Any"; +export * from "./Choice"; +export * from "./Repeated"; + +// special +export * from "./RawData"; + +export { FromBerResult, fromBER } from "./parser"; +export * from "./schema"; +export { AsnType } from "./TypeStore";
\ No newline at end of file diff --git a/comm/third_party/asn1js/src/internals/LocalBaseBlock.ts b/comm/third_party/asn1js/src/internals/LocalBaseBlock.ts new file mode 100644 index 0000000000..c144c49d10 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalBaseBlock.ts @@ -0,0 +1,93 @@ +import * as pvtsutils from "pvtsutils"; +import { EMPTY_STRING, EMPTY_VIEW } from "./constants"; + +export interface ILocalBaseBlock { + blockLength: number; + error: string; + warnings: string[]; +} + +export interface LocalBaseBlockJson extends ILocalBaseBlock { + blockName: string; + valueBeforeDecode: string; +} + +export interface LocalBaseBlockParams extends Partial<ILocalBaseBlock> { + valueBeforeDecode?: pvtsutils.BufferSource; +} + +export interface LocalBaseBlockConstructor<T extends LocalBaseBlock = LocalBaseBlock> { + new(...args: any[]): T; + prototype: T; + NAME: string; + blockName(): string; +} + +/** + * Class used as a base block for all remaining ASN.1 classes + */ +export class LocalBaseBlock implements ILocalBaseBlock { + + /** + * Name of the block + */ + public static NAME = "baseBlock"; + /** + * Aux function, need to get a block name. Need to have it here for inheritance + * @returns Returns name of the block + */ + public static blockName(): string { + return this.NAME; + } + + public blockLength: number; + public error: string; + public warnings: string[]; + /** + * @deprecated since version 3.0.0 + */ + public get valueBeforeDecode(): ArrayBuffer { + return this.valueBeforeDecodeView.slice().buffer; + } + /** + * @deprecated since version 3.0.0 + */ + public set valueBeforeDecode(value: ArrayBuffer) { + this.valueBeforeDecodeView = new Uint8Array(value); + } + /** + * @since 3.0.0 + */ + public valueBeforeDecodeView: Uint8Array; + + /** + * Creates and initializes an object instance of that class + * @param param0 Initialization parameters + */ + constructor({ + blockLength = 0, + error = EMPTY_STRING, + warnings = [], + valueBeforeDecode = EMPTY_VIEW, + }: LocalBaseBlockParams = {}) { + this.blockLength = blockLength; + this.error = error; + this.warnings = warnings; + this.valueBeforeDecodeView = pvtsutils.BufferSourceConverter.toUint8Array(valueBeforeDecode); + } + + /** + * Returns a JSON representation of an object + * @returns JSON object + */ + public toJSON(): LocalBaseBlockJson { + return { + blockName: (this.constructor as typeof LocalBaseBlock).NAME, + blockLength: this.blockLength, + error: this.error, + warnings: this.warnings, + valueBeforeDecode: pvtsutils.Convert.ToHex(this.valueBeforeDecodeView), + }; + } + +} diff --git a/comm/third_party/asn1js/src/internals/LocalBitStringValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalBitStringValueBlock.ts new file mode 100644 index 0000000000..1720b55d82 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalBitStringValueBlock.ts @@ -0,0 +1,168 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import * as pvtsutils from "pvtsutils"; +import { ViewWriter } from "../ViewWriter"; +import { HexBlockJson, HexBlockParams, HexBlock } from "../HexBlock"; +import { BIT_STRING_NAME, EMPTY_BUFFER, END_OF_CONTENT_NAME } from "./constants"; +import { LocalConstructedValueBlockParams, LocalConstructedValueBlockJson, LocalConstructedValueBlock } from "./LocalConstructedValueBlock"; +import { localFromBER } from "../parser"; +import { checkBufferParams } from "./utils"; +import type { BitString } from "../BitString"; + +export interface ILocalBitStringValueBlock { + unusedBits: number; + isConstructed: boolean; +} + +export interface LocalBitStringValueBlockParams extends HexBlockParams, LocalConstructedValueBlockParams, Partial<ILocalBitStringValueBlock> { + value?: BitString[]; +} + +export interface LocalBitStringValueBlockJson extends HexBlockJson, LocalConstructedValueBlockJson, ILocalBitStringValueBlock { } + +export class LocalBitStringValueBlock extends HexBlock(LocalConstructedValueBlock) implements ILocalBitStringValueBlock { + + public static override NAME = "BitStringValueBlock"; + + public unusedBits: number; + public isConstructed: boolean; + + constructor({ + unusedBits = 0, + isConstructed = false, + ...parameters + }: LocalBitStringValueBlockParams = {}) { + super(parameters); + + this.unusedBits = unusedBits; + this.isConstructed = isConstructed; + this.blockLength = this.valueHexView.byteLength; + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + // Ability to decode zero-length BitString value + if (!inputLength) { + return inputOffset; + } + + let resultOffset = -1; + + // If the BIT STRING supposed to be a constructed value + if (this.isConstructed) { + resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength); + if (resultOffset === -1) + return resultOffset; + + for (const value of this.value) { + const currentBlockName = (value.constructor as typeof LocalBitStringValueBlock).NAME; + + if (currentBlockName === END_OF_CONTENT_NAME) { + if (this.isIndefiniteForm) + break; + else { + this.error = "EndOfContent is unexpected, BIT STRING may consists of BIT STRINGs only"; + + return -1; + } + } + + if (currentBlockName !== BIT_STRING_NAME) { + this.error = "BIT STRING may consists of BIT STRINGs only"; + + return -1; + } + + const valueBlock = value.valueBlock as unknown as LocalBitStringValueBlock; + if ((this.unusedBits > 0) && (valueBlock.unusedBits > 0)) { + this.error = "Using of \"unused bits\" inside constructive BIT STRING allowed for least one only"; + + return -1; + } + + this.unusedBits = valueBlock.unusedBits; + } + + return resultOffset; + } + + const inputView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + + //If the BitString supposed to be a primitive value + if (!checkBufferParams(this, inputView, inputOffset, inputLength)) { + return -1; + } + + const intBuffer = inputView.subarray(inputOffset, inputOffset + inputLength); + + this.unusedBits = intBuffer[0]; + + if (this.unusedBits > 7) { + this.error = "Unused bits for BitString must be in range 0-7"; + + return -1; + } + + if (!this.unusedBits) { + const buf = intBuffer.subarray(1); + try { + if (buf.byteLength) { + const asn = localFromBER(buf, 0, buf.byteLength); + if (asn.offset !== -1 && asn.offset === (inputLength - 1)) { + this.value = [asn.result as BitString]; + } + } + } catch (e) { + // nothing + } + } + + // Copy input buffer to internal buffer + this.valueHexView = intBuffer.subarray(1); + this.blockLength = intBuffer.length; + + return (inputOffset + inputLength); + } + + public override toBER(sizeOnly?: boolean, writer?: ViewWriter): ArrayBuffer { + if (this.isConstructed) { + return LocalConstructedValueBlock.prototype.toBER.call(this, sizeOnly, writer); + } + + if (sizeOnly) { + return new ArrayBuffer(this.valueHexView.byteLength + 1); + } + + if (!this.valueHexView.byteLength) { + return EMPTY_BUFFER; + } + + const retView = new Uint8Array(this.valueHexView.length + 1); + + retView[0] = this.unusedBits; + retView.set(this.valueHexView, 1); + + return retView.buffer; + } + + public override toJSON(): LocalBitStringValueBlockJson { + return { + ...super.toJSON(), + unusedBits: this.unusedBits, + isConstructed: this.isConstructed, + } as LocalBitStringValueBlockJson; + } +} + +export interface LocalBitStringValueBlock { + /** + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueBeforeDecode: ArrayBuffer; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueHex: ArrayBuffer; +} diff --git a/comm/third_party/asn1js/src/internals/LocalBmpStringValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalBmpStringValueBlock.ts new file mode 100644 index 0000000000..6245512755 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalBmpStringValueBlock.ts @@ -0,0 +1,21 @@ +import * as pvtsutils from "pvtsutils"; +import { LocalSimpleStringBlock, LocalSimpleStringBlockJson, LocalSimpleStringBlockParams } from "./LocalSimpleStringBlock"; + +export type LocalBmpStringValueBlockParams = LocalSimpleStringBlockParams; +export type LocalBmpStringValueBlockJson = LocalSimpleStringBlockJson; + +export class LocalBmpStringValueBlock extends LocalSimpleStringBlock { + + public static override NAME = "BmpStringValueBlock"; + + public override fromBuffer(inputBuffer: ArrayBuffer | Uint8Array): void { + this.valueBlock.value = pvtsutils.Convert.ToUtf16String(inputBuffer); + this.valueBlock.valueHexView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + } + + public override fromString(inputString: string): void { + this.valueBlock.value = inputString; + this.valueBlock.valueHexView = new Uint8Array(pvtsutils.Convert.FromUtf16String(inputString)); + } + +} diff --git a/comm/third_party/asn1js/src/internals/LocalBooleanValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalBooleanValueBlock.ts new file mode 100644 index 0000000000..a4cdb35dee --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalBooleanValueBlock.ts @@ -0,0 +1,96 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import * as pvtsutils from "pvtsutils"; +import * as pvutils from "pvutils"; +import { HexBlockJson, HexBlockParams, HexBlock } from "../HexBlock"; +import { ValueBlock, ValueBlockJson, ValueBlockParams } from "../ValueBlock"; +import { checkBufferParams } from "./utils"; + +export interface ILocalBooleanValueBlock { + value: boolean; +} + +export interface LocalBooleanValueBlockParams extends ValueBlockParams, HexBlockParams, Partial<ILocalBooleanValueBlock> { } + +export interface LocalBooleanValueBlockJson extends ValueBlockJson, HexBlockJson, ILocalBooleanValueBlock { } + +export class LocalBooleanValueBlock extends HexBlock(ValueBlock) implements ILocalBooleanValueBlock { + + public static override NAME = "BooleanValueBlock"; + + public get value(): boolean { + for (const octet of this.valueHexView) { + if (octet > 0) { + return true; + } + } + + return false; + } + + public set value(value: boolean) { + this.valueHexView[0] = value ? 0xFF : 0x00; + } + + constructor({ + value, + ...parameters + }: LocalBooleanValueBlockParams = {}) { + super(parameters); + + if (parameters.valueHex) { + this.valueHexView = pvtsutils.BufferSourceConverter.toUint8Array(parameters.valueHex); + } else { + this.valueHexView = new Uint8Array(1); + } + + if (value) { + this.value = value; + } + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + const inputView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + // Basic check for parameters + if (!checkBufferParams(this, inputView, inputOffset, inputLength)) { + return -1; + } + + // Getting Uint8Array + this.valueHexView = inputView.subarray(inputOffset, inputOffset + inputLength); + + if (inputLength > 1) + this.warnings.push("Boolean value encoded in more then 1 octet"); + + this.isHexOnly = true; + pvutils.utilDecodeTC.call(this); + this.blockLength = inputLength; + + return (inputOffset + inputLength); + } + + public override toBER(): ArrayBuffer { + return this.valueHexView.slice(); + } + + public override toJSON(): LocalBooleanValueBlockJson { + return { + ...super.toJSON(), + value: this.value, + }; + } +} + +export interface LocalBooleanValueBlock { + /** + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueBeforeDecode: ArrayBuffer; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueHex: ArrayBuffer; +} diff --git a/comm/third_party/asn1js/src/internals/LocalConstructedValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalConstructedValueBlock.ts new file mode 100644 index 0000000000..db3ba9f35e --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalConstructedValueBlock.ts @@ -0,0 +1,128 @@ +import * as pvtsutils from "pvtsutils"; +import { LocalBaseBlockJson } from "./LocalBaseBlock"; +import { EMPTY_BUFFER, END_OF_CONTENT_NAME } from "./constants"; +import type { BaseBlock } from "../BaseBlock"; +import { ValueBlock, ValueBlockParams } from "../ValueBlock"; +import { ViewWriter } from "../ViewWriter"; +import { localFromBER } from "../parser"; +import { checkBufferParams } from "./utils"; +import type { Any } from "../Any"; + +export type ConstructedItem = BaseBlock | Any; + +export interface ILocalConstructedValueBlock { + value: ConstructedItem[]; + isIndefiniteForm: boolean; +} + +export interface LocalConstructedValueBlockParams extends ValueBlockParams, Partial<ILocalConstructedValueBlock> { } + +export interface LocalConstructedValueBlockJson extends LocalBaseBlockJson, Omit<ILocalConstructedValueBlock, "value"> { + value: LocalBaseBlockJson[]; +} + +function checkLen(indefiniteLength: boolean, length: number): number { + if (indefiniteLength) { + return 1; + } + + return length; +} + +export class LocalConstructedValueBlock extends ValueBlock implements ILocalConstructedValueBlock { + + public static override NAME = "ConstructedValueBlock"; + + public value: BaseBlock[]; + public isIndefiniteForm: boolean; + + constructor({ + value = [], + isIndefiniteForm = false, + ...parameters + }: LocalConstructedValueBlockParams = {}) { + super(parameters); + + this.value = value as BaseBlock[]; // It's possible to set Any type for Schema declaration + this.isIndefiniteForm = isIndefiniteForm; + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + const view = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + + // Basic check for parameters + if (!checkBufferParams(this, view, inputOffset, inputLength)) { + return -1; + } + + this.valueBeforeDecodeView = view.subarray(inputOffset, inputOffset + inputLength); + + // Initial checks + 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 as typeof BaseBlock).NAME === END_OF_CONTENT_NAME) { + break; + } + } + + if (this.isIndefiniteForm) { + if ((this.value[this.value.length - 1].constructor as typeof BaseBlock).NAME === END_OF_CONTENT_NAME) { + this.value.pop(); + } else { + this.warnings.push("No EndOfContent block encoded"); + } + } + + return currentOffset; + } + + public override toBER(sizeOnly?: boolean, writer?: ViewWriter): ArrayBuffer { + 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; + } + + public override toJSON(): LocalConstructedValueBlockJson { + const object: LocalConstructedValueBlockJson = { + ...super.toJSON(), + isIndefiniteForm: this.isIndefiniteForm, + value: [], + }; + + for (const value of this.value) { + object.value.push(value.toJSON()); + } + + return object; + } +} diff --git a/comm/third_party/asn1js/src/internals/LocalEndOfContentValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalEndOfContentValueBlock.ts new file mode 100644 index 0000000000..4a1db86681 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalEndOfContentValueBlock.ts @@ -0,0 +1,16 @@ +import { ValueBlock } from "../ValueBlock"; +import { EMPTY_BUFFER } from "./constants"; + +export class LocalEndOfContentValueBlock extends ValueBlock { + + public static override = "EndOfContentValueBlock"; + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + // There is no "value block" for EndOfContent type and we need to return the same offset + return inputOffset; + } + + public override toBER(sizeOnly?: boolean): ArrayBuffer { + return EMPTY_BUFFER; + } +} diff --git a/comm/third_party/asn1js/src/internals/LocalIdentificationBlock.ts b/comm/third_party/asn1js/src/internals/LocalIdentificationBlock.ts new file mode 100644 index 0000000000..d97c874b7a --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalIdentificationBlock.ts @@ -0,0 +1,280 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import * as pvtsutils from "pvtsutils"; +import * as pvutils from "pvutils"; +import { HexBlockJson, HexBlockParams, HexBlock } from "../HexBlock"; +import { EMPTY_BUFFER, EMPTY_VIEW } from "./constants"; +import { LocalBaseBlock, LocalBaseBlockJson } from "./LocalBaseBlock"; +import { checkBufferParams } from "./utils"; + + +export interface ILocalIdentificationBlock { + tagClass: number; + tagNumber: number; + isConstructed: boolean; +} + +export interface LocalIdentificationBlockParams { + idBlock?: Partial<ILocalIdentificationBlock> & HexBlockParams; +} + +export interface LocalIdentificationBlockJson extends HexBlockJson, LocalBaseBlockJson, ILocalIdentificationBlock { } + + +export class LocalIdentificationBlock extends HexBlock(LocalBaseBlock) implements ILocalIdentificationBlock { + + public static override NAME = "identificationBlock"; + + public tagClass: number; + public tagNumber: number; + public isConstructed: boolean; + + constructor({ + idBlock = {}, + }: LocalIdentificationBlockParams = {}) { + super(); + + if (idBlock) { + //#region Properties from hexBlock class + this.isHexOnly = idBlock.isHexOnly ?? false; + this.valueHexView = idBlock.valueHex ? pvtsutils.BufferSourceConverter.toUint8Array(idBlock.valueHex) : EMPTY_VIEW; + //#endregion + this.tagClass = idBlock.tagClass ?? -1; + this.tagNumber = idBlock.tagNumber ?? -1; + this.isConstructed = idBlock.isConstructed ?? false; + } else { + this.tagClass = -1; + this.tagNumber = -1; + this.isConstructed = false; + } + } + + public override toBER(sizeOnly = false): ArrayBuffer { + let firstOctet = 0; + + switch (this.tagClass) { + case 1: + firstOctet |= 0x00; // UNIVERSAL + break; + case 2: + firstOctet |= 0x40; // APPLICATION + break; + case 3: + firstOctet |= 0x80; // CONTEXT-SPECIFIC + break; + case 4: + firstOctet |= 0xC0; // PRIVATE + break; + default: + this.error = "Unknown tag class"; + + return EMPTY_BUFFER; + } + + if (this.isConstructed) + firstOctet |= 0x20; + + if (this.tagNumber < 31 && !this.isHexOnly) { + const retView = new Uint8Array(1); + + if (!sizeOnly) { + let number = this.tagNumber; + number &= 0x1F; + firstOctet |= number; + + retView[0] = firstOctet; + } + + return retView.buffer; + } + + if (!this.isHexOnly) { + const encodedBuf = pvutils.utilToBase(this.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; + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + const inputView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + + // Basic check for parameters + if (!checkBufferParams(this, inputView, inputOffset, inputLength)) { + return -1; + } + + // Getting Uint8Array from ArrayBuffer + const intBuffer = inputView.subarray(inputOffset, inputOffset + inputLength); + + // Initial checks + if (intBuffer.length === 0) { + this.error = "Zero buffer length"; + + return -1; + } + + //#region Find tag class + const tagClassMask = intBuffer[0] & 0xC0; + + switch (tagClassMask) { + case 0x00: + this.tagClass = (1); // UNIVERSAL + break; + case 0x40: + this.tagClass = (2); // APPLICATION + break; + case 0x80: + this.tagClass = (3); // CONTEXT-SPECIFIC + break; + case 0xC0: + this.tagClass = (4); // PRIVATE + break; + default: + this.error = "Unknown tag class"; + + return -1; + } + //#endregion + // Find it's constructed or not + this.isConstructed = (intBuffer[0] & 0x20) === 0x20; + + // Find tag number + this.isHexOnly = false; + const tagNumberMask = intBuffer[0] & 0x1F; + + if (tagNumberMask !== 0x1F) { + // Simple case (tag number < 31) + this.tagNumber = (tagNumberMask); + this.blockLength = 1; + } else { + // Tag number bigger or equal to 31 + 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; + } + + // In case if tag number length is greater than 255 bytes (rare but possible case) + 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; // Write last byte to buffer + + + //#region Cut buffer + 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); + //#endregion + //#region Try to convert long tag number to short form + if (this.blockLength <= 9) + this.tagNumber = pvutils.utilFromBase(intTagNumberBuffer, 7); + else { + this.isHexOnly = true; + this.warnings.push("Tag too long, represented as hex-coded"); + } + //#endregion + } + //#endregion + //#endregion + //#region Check if constructed encoding was using for primitive type + if (((this.tagClass === 1)) && + (this.isConstructed)) { + switch (this.tagNumber) { + case 1: // Boolean + case 2: // REAL + case 5: // Null + case 6: // OBJECT IDENTIFIER + case 9: // REAL + case 13: // RELATIVE OBJECT IDENTIFIER + case 14: // Time + case 23: + case 24: + case 31: + case 32: + case 33: + case 34: + this.error = "Constructed encoding used for primitive type"; + + return -1; + default: + } + } + //#endregion + + return (inputOffset + this.blockLength); // Return current offset in input buffer + } + + public override toJSON(): LocalIdentificationBlockJson { + return { + ...super.toJSON(), + tagClass: this.tagClass, + tagNumber: this.tagNumber, + isConstructed: this.isConstructed, + }; + } +} + +export interface LocalIdentificationBlock { + /** + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueBeforeDecode: ArrayBuffer; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueHex: ArrayBuffer; +}
\ No newline at end of file diff --git a/comm/third_party/asn1js/src/internals/LocalIntegerValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalIntegerValueBlock.ts new file mode 100644 index 0000000000..3a7b299caf --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalIntegerValueBlock.ts @@ -0,0 +1,330 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import * as pvutils from "pvutils"; +import { HexBlockJson, HexBlockParams, HexBlock } from "../HexBlock"; +import { IDerConvertible } from "../types"; +import { ValueBlock, ValueBlockJson, ValueBlockParams } from "../ValueBlock"; +import { powers2, digitsString } from "./constants"; + +function viewAdd(first: Uint8Array, second: Uint8Array): Uint8Array { + //#region Initial variables + const c = new Uint8Array([0]); + + const firstView = new Uint8Array(first); + const secondView = new Uint8Array(second); + + let firstViewCopy = firstView.slice(0); + const firstViewCopyLength = firstViewCopy.length - 1; + const secondViewCopy = secondView.slice(0); + const secondViewCopyLength = secondViewCopy.length - 1; + + let value = 0; + + const max = (secondViewCopyLength < firstViewCopyLength) ? firstViewCopyLength : secondViewCopyLength; + + let counter = 0; + //#endregion + for (let i = max; i >= 0; i--, counter++) { + switch (true) { + case (counter < secondViewCopy.length): + value = firstViewCopy[firstViewCopyLength - counter] + secondViewCopy[secondViewCopyLength - counter] + c[0]; + break; + default: + value = firstViewCopy[firstViewCopyLength - counter] + c[0]; + } + + c[0] = value / 10; + + switch (true) { + case (counter >= firstViewCopy.length): + firstViewCopy = pvutils.utilConcatView(new Uint8Array([value % 10]), firstViewCopy); + break; + default: + firstViewCopy[firstViewCopyLength - counter] = value % 10; + } + } + + if (c[0] > 0) + firstViewCopy = pvutils.utilConcatView(c, firstViewCopy); + + return firstViewCopy; +} + +function power2(n: number): Uint8Array { + if (n >= powers2.length) { + for (let p = powers2.length; p <= n; p++) { + const c = new Uint8Array([0]); + let digits = (powers2[p - 1]).slice(0); + + for (let i = (digits.length - 1); i >= 0; i--) { + const newValue = new Uint8Array([(digits[i] << 1) + c[0]]); + c[0] = newValue[0] / 10; + digits[i] = newValue[0] % 10; + } + + if (c[0] > 0) + digits = pvutils.utilConcatView(c, digits); + + powers2.push(digits); + } + } + + return powers2[n]; +} + +function viewSub(first: Uint8Array, second: Uint8Array): Uint8Array { + //#region Initial variables + let b = 0; + + const firstView = new Uint8Array(first); + const secondView = new Uint8Array(second); + + const firstViewCopy = firstView.slice(0); + const firstViewCopyLength = firstViewCopy.length - 1; + const secondViewCopy = secondView.slice(0); + const secondViewCopyLength = secondViewCopy.length - 1; + + let value; + + let counter = 0; + //#endregion + for (let i = secondViewCopyLength; i >= 0; i--, counter++) { + value = firstViewCopy[firstViewCopyLength - counter] - secondViewCopy[secondViewCopyLength - counter] - b; + + switch (true) { + case (value < 0): + b = 1; + firstViewCopy[firstViewCopyLength - counter] = value + 10; + break; + default: + b = 0; + firstViewCopy[firstViewCopyLength - counter] = value; + } + } + + if (b > 0) { + for (let i = (firstViewCopyLength - secondViewCopyLength + 1); i >= 0; i--, counter++) { + value = firstViewCopy[firstViewCopyLength - counter] - b; + + if (value < 0) { + b = 1; + firstViewCopy[firstViewCopyLength - counter] = value + 10; + } + else { + b = 0; + firstViewCopy[firstViewCopyLength - counter] = value; + break; + } + } + } + + return firstViewCopy.slice(); +} + +export interface ILocalIntegerValueBlock { + value: number; +} + +export interface LocalIntegerValueBlockParams extends HexBlockParams, ValueBlockParams, Partial<ILocalIntegerValueBlock> { } + +export interface LocalIntegerValueBlockJson extends HexBlockJson, ValueBlockJson { + valueDec: number; +} + +export class LocalIntegerValueBlock extends HexBlock(ValueBlock) implements IDerConvertible { + protected setValueHex(): void { + if (this.valueHexView.length >= 4) { + this.warnings.push("Too big Integer for decoding, hex only"); + this.isHexOnly = true; + this._valueDec = 0; + } else { + this.isHexOnly = false; + + if (this.valueHexView.length > 0) { + this._valueDec = pvutils.utilDecodeTC.call(this); + } + } + } + + public static override NAME = "IntegerValueBlock"; + + static { + Object.defineProperty(this.prototype, "valueHex", { + set: function (this: LocalIntegerValueBlock, v: ArrayBuffer) { + this.valueHexView = new Uint8Array(v); + + this.setValueHex(); + }, + get: function (this: LocalIntegerValueBlock) { + return this.valueHexView.slice().buffer; + }, + }); + } + + private _valueDec = 0; + + constructor({ + value, + ...parameters + }: LocalIntegerValueBlockParams = {}) { + super(parameters); + + if (parameters.valueHex) { + this.setValueHex(); + } + + if (value !== undefined) { + this.valueDec = value; + } + } + + public set valueDec(v: number) { + this._valueDec = v; + + this.isHexOnly = false; + this.valueHexView = new Uint8Array(pvutils.utilEncodeTC(v)); + } + + public get valueDec(): number { + return this._valueDec; + } + + public fromDER(inputBuffer: ArrayBuffer, inputOffset: number, inputLength: number, expectedLength = 0): number { + const offset = this.fromBER(inputBuffer, inputOffset, inputLength); + if (offset === -1) + return offset; + + const view = this.valueHexView; + + if ((view[0] === 0x00) && ((view[1] & 0x80) !== 0)) { + this.valueHexView = view.subarray(1); + } + else { + if (expectedLength !== 0) { + if (view.length < expectedLength) { + if ((expectedLength - view.length) > 1) + expectedLength = view.length + 1; + + this.valueHexView = view.subarray(expectedLength - view.length); + } + } + } + + return offset; + } + + public toDER(sizeOnly = false): ArrayBuffer { + const view = this.valueHexView; + + switch (true) { + case ((view[0] & 0x80) !== 0): + { + const updatedView = new Uint8Array(this.valueHexView.length + 1); + + updatedView[0] = 0x00; + updatedView.set(view, 1); + + this.valueHexView = updatedView; + } + break; + case ((view[0] === 0x00) && ((view[1] & 0x80) === 0)): + { + this.valueHexView = this.valueHexView.subarray(1); + } + break; + default: + } + + return this.toBER(sizeOnly); + } + + public override fromBER(inputBuffer: ArrayBuffer, inputOffset: number, inputLength: number): number { + const resultOffset = super.fromBER(inputBuffer, inputOffset, inputLength); + if (resultOffset === -1) { + return resultOffset; + } + + this.setValueHex(); + + return resultOffset; + } + + public override toBER(sizeOnly?: boolean): ArrayBuffer { + return sizeOnly + ? new ArrayBuffer(this.valueHexView.length) + : this.valueHexView.slice().buffer; + } + + public override toJSON(): LocalIntegerValueBlockJson { + return { + ...super.toJSON(), + valueDec: this.valueDec, + }; + } + + public override toString(): string { + //#region Initial variables + const firstBit = (this.valueHexView.length * 8) - 1; + + let digits = new Uint8Array((this.valueHexView.length * 8) / 3); + let bitNumber = 0; + let currentByte; + + const asn1View = this.valueHexView; + + let result = ""; + + let flag = false; + //#endregion + //#region Calculate number + for (let byteNumber = (asn1View.byteLength - 1); byteNumber >= 0; byteNumber--) { + currentByte = asn1View[byteNumber]; + + for (let i = 0; i < 8; i++) { + if ((currentByte & 1) === 1) { + switch (bitNumber) { + case firstBit: + digits = viewSub(power2(bitNumber), digits); + result = "-"; + break; + default: + digits = viewAdd(digits, power2(bitNumber)); + } + } + + bitNumber++; + currentByte >>= 1; + } + } + //#endregion + //#region Print number + for (let i = 0; i < digits.length; i++) { + if (digits[i]) + flag = true; + + if (flag) + result += digitsString.charAt(digits[i]); + } + + if (flag === false) + result += digitsString.charAt(0); + //#endregion + + return result; + } + +} + +export interface LocalIntegerValueBlock { + /** + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueBeforeDecode: ArrayBuffer; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueHex: ArrayBuffer; +} diff --git a/comm/third_party/asn1js/src/internals/LocalLengthBlock.ts b/comm/third_party/asn1js/src/internals/LocalLengthBlock.ts new file mode 100644 index 0000000000..a4c7940b02 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalLengthBlock.ts @@ -0,0 +1,182 @@ +import * as pvtsutils from "pvtsutils"; +import * as pvutils from "pvutils"; +import { IBerConvertible } from "../types"; +import { EMPTY_BUFFER } from "./constants"; +import { LocalBaseBlock, LocalBaseBlockJson } from "./LocalBaseBlock"; +import { checkBufferParams } from "./utils"; + +export interface ILocalLengthBlock { + isIndefiniteForm: boolean; + longFormUsed: boolean; + length: number; +} + +export interface LocalLengthBlockParams { + lenBlock?: Partial<ILocalLengthBlock>; +} + +export interface LocalLengthBlockJson extends LocalBaseBlockJson, ILocalLengthBlock { + isIndefiniteForm: boolean; + longFormUsed: boolean; + length: number; +} + +export class LocalLengthBlock extends LocalBaseBlock implements ILocalLengthBlock, IBerConvertible { + + public static override NAME = "lengthBlock"; + + public isIndefiniteForm: boolean; + public longFormUsed: boolean; + public length: number; + + constructor({ + lenBlock = {}, + }: LocalLengthBlockParams = {}) { + super(); + + this.isIndefiniteForm = lenBlock.isIndefiniteForm ?? false; + this.longFormUsed = lenBlock.longFormUsed ?? false; + this.length = lenBlock.length ?? 0; + } + + + public fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + const view = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + // Basic check for parameters + if (!checkBufferParams(this, view, inputOffset, inputLength)) { + return -1; + } + //#region Getting Uint8Array from ArrayBuffer + const intBuffer = view.subarray(inputOffset, inputOffset + inputLength); + //#endregion + //#region Initial checks + 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; + } + //#endregion + //#region Check for length form type + this.isIndefiniteForm = intBuffer[0] === 0x80; + //#endregion + //#region Stop working in case of indefinite length form + if (this.isIndefiniteForm) { + this.blockLength = 1; + + return (inputOffset + this.blockLength); + } + //#endregion + //#region Check is long form of length encoding using + this.longFormUsed = !!(intBuffer[0] & 0x80); + //#endregion + //#region Stop working in case of short form of length value + if (this.longFormUsed === false) { + this.length = (intBuffer[0]); + this.blockLength = 1; + + return (inputOffset + this.blockLength); + } + //#endregion + //#region Calculate length value in case of long form + const count = intBuffer[0] & 0x7F; + + if (count > 8) // Too big length value + { + 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; + //#endregion + + return (inputOffset + this.blockLength); // Return current offset in input buffer + } + + public toBER(sizeOnly = false): ArrayBuffer { + //#region Initial variables + let retBuf: ArrayBuffer; + let retView: Uint8Array; + //#endregion + 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; + } + + public override toJSON(): LocalLengthBlockJson { + return { + ...super.toJSON(), + isIndefiniteForm: this.isIndefiniteForm, + longFormUsed: this.longFormUsed, + length: this.length, + }; + } +} diff --git a/comm/third_party/asn1js/src/internals/LocalObjectIdentifierValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalObjectIdentifierValueBlock.ts new file mode 100644 index 0000000000..a24b9e947a --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalObjectIdentifierValueBlock.ts @@ -0,0 +1,192 @@ +import { ValueBlock, ValueBlockJson, ValueBlockParams } from "../ValueBlock"; +import { EMPTY_BUFFER, EMPTY_STRING } from "./constants"; +import * as utils from "./utils"; +import { LocalSidValueBlockJson, LocalSidValueBlock } from "./LocalSidValueBlock"; +import { IStringConvertible } from "../types"; + + +export interface ILocalObjectIdentifierValueBlock { + value: string; +} + +export interface LocalObjectIdentifierValueBlockParams extends ValueBlockParams, Partial<ILocalObjectIdentifierValueBlock> { } + +export interface LocalObjectIdentifierValueBlockJson extends ValueBlockJson, ILocalObjectIdentifierValueBlock { + sidArray: LocalSidValueBlockJson[]; +} + +export class LocalObjectIdentifierValueBlock extends ValueBlock implements IStringConvertible { + + public static override NAME = "ObjectIdentifierValueBlock"; + + public value: LocalSidValueBlock[] = []; + + constructor({ + value = EMPTY_STRING, + ...parameters + }: LocalObjectIdentifierValueBlockParams = {}) { + super(parameters); + + if (value) { + this.fromString(value); + } + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + let resultOffset = inputOffset; + + while (inputLength > 0) { + const sidBlock = new LocalSidValueBlock(); + resultOffset = sidBlock.fromBER(inputBuffer, resultOffset, inputLength); + if (resultOffset === -1) { + this.blockLength = 0; + this.error = sidBlock.error; + + return resultOffset; + } + + if (this.value.length === 0) + sidBlock.isFirstSid = true; + + this.blockLength += sidBlock.blockLength; + inputLength -= sidBlock.blockLength; + + this.value.push(sidBlock); + } + + return resultOffset; + } + public override toBER(sizeOnly?: boolean): ArrayBuffer { + const retBuffers: ArrayBuffer[] = []; + + for (let i = 0; i < this.value.length; i++) { + const valueBuf = this.value[i].toBER(sizeOnly); + if (valueBuf.byteLength === 0) { + this.error = this.value[i].error; + + return EMPTY_BUFFER; + } + + retBuffers.push(valueBuf); + } + + return utils.concat(retBuffers); + } + + public fromString(string: string): void { + this.value = []; // Clear existing SID values + + let pos1 = 0; + let pos2 = 0; + + let sid = ""; + + let flag = false; + + // const sids = string.split("."); + // for (const sid of sids) { + + // } + do { + pos2 = string.indexOf(".", pos1); + if (pos2 === -1) + sid = string.substring(pos1); + + else + sid = string.substring(pos1, pos2); + + pos1 = pos2 + 1; + + if (flag) { + const sidBlock = this.value[0]; + + let plus = 0; + + switch (sidBlock.valueDec) { + case 0: + break; + case 1: + plus = 40; + break; + case 2: + plus = 80; + break; + default: + this.value = []; // clear SID array + + return; + } + + const parsedSID = parseInt(sid, 10); + if (isNaN(parsedSID)) + return; + + sidBlock.valueDec = parsedSID + plus; + + flag = false; + } else { + const sidBlock = new LocalSidValueBlock(); + if ((sid as any) > Number.MAX_SAFE_INTEGER) { // TODO remove as any + utils.assertBigInt(); + const sidValue = BigInt(sid); + sidBlock.valueBigInt = sidValue; + } else { + sidBlock.valueDec = parseInt(sid, 10); + if (isNaN(sidBlock.valueDec)) + return; + } + + if (!this.value.length) { + sidBlock.isFirstSid = true; + flag = true; + } + + this.value.push(sidBlock); + } + } while (pos2 !== -1); + } + + public override toString(): string { + let result = ""; + let isHexOnly = false; + + for (let i = 0; i < this.value.length; i++) { + isHexOnly = this.value[i].isHexOnly; + + let sidStr = this.value[i].toString(); + + if (i !== 0) + result = `${result}.`; + + if (isHexOnly) { + sidStr = `{${sidStr}}`; + + if (this.value[i].isFirstSid) + result = `2.{${sidStr} - 80}`; + + else + result += sidStr; + } + + else + result += sidStr; + } + + return result; + } + + public override toJSON(): LocalObjectIdentifierValueBlockJson { + const object: LocalObjectIdentifierValueBlockJson = { + ...super.toJSON(), + value: this.toString(), + sidArray: [], + }; + + for (let i = 0; i < this.value.length; i++) { + object.sidArray.push(this.value[i].toJSON()); + } + + return object; + } + +} diff --git a/comm/third_party/asn1js/src/internals/LocalOctetStringValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalOctetStringValueBlock.ts new file mode 100644 index 0000000000..fb2dfd562b --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalOctetStringValueBlock.ts @@ -0,0 +1,103 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ViewWriter } from "../ViewWriter"; +import { HexBlockJson, HexBlockParams, HexBlock } from "../HexBlock"; +import { END_OF_CONTENT_NAME, OCTET_STRING_NAME } from "./constants"; +import { LocalConstructedValueBlockParams, LocalConstructedValueBlockJson, LocalConstructedValueBlock } from "./LocalConstructedValueBlock"; +import type { OctetString } from "../OctetString"; + +export interface ILocalOctetStringValueBlock { + isConstructed: boolean; +} + +export interface LocalOctetStringValueBlockParams extends HexBlockParams, LocalConstructedValueBlockParams, Partial<ILocalOctetStringValueBlock> { + value?: OctetString[]; +} + +export interface LocalOctetStringValueBlockJson extends HexBlockJson, LocalConstructedValueBlockJson, ILocalOctetStringValueBlock { } + +export class LocalOctetStringValueBlock extends HexBlock(LocalConstructedValueBlock) { + + public static override NAME = "OctetStringValueBlock"; + + public isConstructed: boolean; + + constructor({ + isConstructed = false, + ...parameters + }: LocalOctetStringValueBlockParams = {}) { + super(parameters); + + this.isConstructed = isConstructed; + } + + public override fromBER(inputBuffer: ArrayBuffer, inputOffset: number, inputLength: number): number { + let resultOffset = 0; + + if (this.isConstructed) { + this.isHexOnly = false; + + resultOffset = LocalConstructedValueBlock.prototype.fromBER.call(this, inputBuffer, inputOffset, inputLength); + if (resultOffset === -1) + return resultOffset; + + for (let i = 0; i < this.value.length; i++) { + const currentBlockName = (this.value[i].constructor as typeof LocalOctetStringValueBlock).NAME; + + if (currentBlockName === END_OF_CONTENT_NAME) { + if (this.isIndefiniteForm) + break; + else { + this.error = "EndOfContent is unexpected, OCTET STRING may consists of OCTET STRINGs only"; + + return -1; + } + } + + if (currentBlockName !== OCTET_STRING_NAME) { + this.error = "OCTET STRING may consists of OCTET STRINGs only"; + + return -1; + } + } + } else { + this.isHexOnly = true; + + resultOffset = super.fromBER(inputBuffer, inputOffset, inputLength); + this.blockLength = inputLength; + } + + return resultOffset; + } + + public override toBER(sizeOnly?: boolean, writer?: ViewWriter): ArrayBuffer { + if (this.isConstructed) + return LocalConstructedValueBlock.prototype.toBER.call(this, sizeOnly, writer); + + return sizeOnly + ? new ArrayBuffer(this.valueHexView.byteLength) + : this.valueHexView.slice().buffer; + } + + public override toJSON(): LocalOctetStringValueBlockJson { + return { + ...super.toJSON(), + isConstructed: this.isConstructed, + } as LocalOctetStringValueBlockJson; + } + +} + +export interface LocalOctetStringValueBlock { + /** + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueBeforeDecode: ArrayBuffer; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueHex: ArrayBuffer; +} diff --git a/comm/third_party/asn1js/src/internals/LocalPrimitiveValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalPrimitiveValueBlock.ts new file mode 100644 index 0000000000..7fe2408b96 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalPrimitiveValueBlock.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { HexBlock, HexBlockJson, HexBlockParams } from "../HexBlock"; +import { ValueBlock, ValueBlockJson, ValueBlockParams } from "../ValueBlock"; + +export interface LocalPrimitiveValueBlockParams extends HexBlockParams, ValueBlockParams { } +export interface LocalPrimitiveValueBlockJson extends HexBlockJson, ValueBlockJson { } + +export class LocalPrimitiveValueBlock extends HexBlock(ValueBlock) { + + public static override NAME = "PrimitiveValueBlock"; + + constructor({ + isHexOnly = true, + ...parameters + }: LocalPrimitiveValueBlockParams = {}) { + super(parameters); + + this.isHexOnly = isHexOnly; + } + +} + +export interface LocalPrimitiveValueBlock { + /** + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueBeforeDecode: ArrayBuffer; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueHex: ArrayBuffer; +} diff --git a/comm/third_party/asn1js/src/internals/LocalRelativeObjectIdentifierValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalRelativeObjectIdentifierValueBlock.ts new file mode 100644 index 0000000000..13237f50ec --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalRelativeObjectIdentifierValueBlock.ts @@ -0,0 +1,140 @@ +import { ViewWriter } from "../ViewWriter"; +import { ValueBlock, ValueBlockJson, ValueBlockParams } from "../ValueBlock"; +import { EMPTY_BUFFER, EMPTY_STRING } from "./constants"; +import * as utils from "./utils"; +import { LocalRelativeSidValueBlockJson, LocalRelativeSidValueBlock } from "./LocalRelativeSidValueBlock"; +import { IStringConvertible } from "../types"; + +export interface ILocalRelativeObjectIdentifierValueBlock { + value: string; +} + +export interface LocalRelativeObjectIdentifierValueBlockParams extends ValueBlockParams, Partial<ILocalRelativeObjectIdentifierValueBlock> { } + +export interface LocalRelativeObjectIdentifierValueBlockJson extends ValueBlockJson, ILocalRelativeObjectIdentifierValueBlock { + sidArray: LocalRelativeSidValueBlockJson[]; +} + +export class LocalRelativeObjectIdentifierValueBlock extends ValueBlock implements IStringConvertible { + + public static override NAME = "RelativeObjectIdentifierValueBlock"; + + public value: LocalRelativeSidValueBlock[] = []; + + constructor({ + value = EMPTY_STRING, + ...parameters + }: LocalRelativeObjectIdentifierValueBlockParams = {}) { + super(parameters); + + if (value) { + this.fromString(value); + } + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + let resultOffset = inputOffset; + + while (inputLength > 0) { + const sidBlock = new LocalRelativeSidValueBlock(); + resultOffset = sidBlock.fromBER(inputBuffer, resultOffset, inputLength); + if (resultOffset === -1) { + this.blockLength = 0; + this.error = sidBlock.error; + + return resultOffset; + } + + this.blockLength += sidBlock.blockLength; + inputLength -= sidBlock.blockLength; + + this.value.push(sidBlock); + } + + return resultOffset; + } + + public override toBER(sizeOnly?: boolean, writer?: ViewWriter): ArrayBuffer { + const retBuffers: ArrayBuffer[] = []; + + for (let i = 0; i < this.value.length; i++) { + const valueBuf = this.value[i].toBER(sizeOnly); + if (valueBuf.byteLength === 0) { + this.error = this.value[i].error; + + return EMPTY_BUFFER; + } + + retBuffers.push(valueBuf); + } + + return utils.concat(retBuffers); + } + + public fromString(string: string): boolean { + this.value = []; // Clear existing SID values + + let pos1 = 0; + let pos2 = 0; + + let sid = ""; + + do { + pos2 = string.indexOf(".", pos1); + if (pos2 === -1) + sid = string.substring(pos1); + + else + sid = string.substring(pos1, pos2); + + pos1 = pos2 + 1; + + const sidBlock = new LocalRelativeSidValueBlock(); + sidBlock.valueDec = parseInt(sid, 10); + if (isNaN(sidBlock.valueDec)) + return true; + + this.value.push(sidBlock); + + } while (pos2 !== -1); + + return true; + } + + public override toString(): string { + let result = ""; + let isHexOnly = false; + + for (let i = 0; i < this.value.length; i++) { + isHexOnly = this.value[i].isHexOnly; + + let sidStr = this.value[i].toString(); + + if (i !== 0) + result = `${result}.`; + + if (isHexOnly) { + sidStr = `{${sidStr}}`; + result += sidStr; + } + else + result += sidStr; + } + + return result; + } + + public override toJSON(): LocalRelativeObjectIdentifierValueBlockJson { + const object: LocalRelativeObjectIdentifierValueBlockJson = { + ...super.toJSON(), + value: this.toString(), + sidArray: [], + }; + + for (let i = 0; i < this.value.length; i++) + object.sidArray.push(this.value[i].toJSON()); + + return object; + } + +} diff --git a/comm/third_party/asn1js/src/internals/LocalRelativeSidValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalRelativeSidValueBlock.ts new file mode 100644 index 0000000000..3a10c617aa --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalRelativeSidValueBlock.ts @@ -0,0 +1,140 @@ +import * as pvtsutils from "pvtsutils"; +import * as pvutils from "pvutils"; +import { HexBlockJson, HexBlockParams, HexBlock } from "../HexBlock"; +import { ValueBlockJson, ValueBlockParams } from "../ValueBlock"; +import { LocalBaseBlock } from "./LocalBaseBlock"; +import { EMPTY_BUFFER } from "./constants"; +import { checkBufferParams } from "./utils"; + +export interface ILocalRelativeSidValueBlock { + valueDec: number; +} + +export interface LocalRelativeSidValueBlockParams extends HexBlockParams, ValueBlockParams, Partial<ILocalRelativeSidValueBlock> { } + +export interface LocalRelativeSidValueBlockJson extends HexBlockJson, ValueBlockJson, ILocalRelativeSidValueBlock { } + +export class LocalRelativeSidValueBlock extends HexBlock(LocalBaseBlock) implements ILocalRelativeSidValueBlock { + + public static override NAME = "relativeSidBlock"; + + public valueDec: number; + + constructor({ + valueDec = 0, + ...parameters + }: LocalRelativeSidValueBlockParams = {}) { + super(parameters); + + this.valueDec = valueDec; + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + if (inputLength === 0) + return inputOffset; + + const inputView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + + // Basic check for parameters + if (!checkBufferParams(this, inputView, inputOffset, inputLength)) + return -1; + + const intBuffer = inputView.subarray(inputOffset, inputOffset + inputLength); + + this.valueHexView = new Uint8Array(inputLength); + + for (let i = 0; i < inputLength; i++) { + this.valueHexView[i] = intBuffer[i] & 0x7F; + + this.blockLength++; + + if ((intBuffer[i] & 0x80) === 0x00) + break; + } + + //#region Adjust size of valueHex buffer + const tempView = new Uint8Array(this.blockLength); + + for (let i = 0; i < this.blockLength; i++) + tempView[i] = this.valueHexView[i]; + + this.valueHexView = tempView; + //#endregion + if ((intBuffer[this.blockLength - 1] & 0x80) !== 0x00) { + this.error = "End of input reached before message was fully decoded"; + + return -1; + } + + if (this.valueHexView[0] === 0x00) + this.warnings.push("Needlessly long format of SID encoding"); + + if (this.blockLength <= 8) + this.valueDec = pvutils.utilFromBase(this.valueHexView, 7); + else { + this.isHexOnly = true; + this.warnings.push("Too big SID for decoding, hex only"); + } + + return (inputOffset + this.blockLength); + } + + public override toBER(sizeOnly?: boolean): ArrayBuffer { + if (this.isHexOnly) { + if (sizeOnly) + return (new ArrayBuffer(this.valueHexView.byteLength)); + + const curView = this.valueHexView; + + const retView = new Uint8Array(this.blockLength); + + for (let i = 0; i < (this.blockLength - 1); i++) + retView[i] = curView[i] | 0x80; + + retView[this.blockLength - 1] = curView[this.blockLength - 1]; + + return retView.buffer; + } + + const encodedBuf = pvutils.utilToBase(this.valueDec, 7); + if (encodedBuf.byteLength === 0) { + this.error = "Error during encoding SID value"; + + return EMPTY_BUFFER; + } + + const retView = new Uint8Array(encodedBuf.byteLength); + + if (!sizeOnly) { + const encodedView = new Uint8Array(encodedBuf); + const len = encodedBuf.byteLength - 1; + + for (let i = 0; i < len; i++) + retView[i] = encodedView[i] | 0x80; + + retView[len] = encodedView[len]; + } + + return retView.buffer; + } + + public override toString(): string { + let result = ""; + + if (this.isHexOnly) + result = pvtsutils.Convert.ToHex(this.valueHexView); + else { + result = this.valueDec.toString(); + } + + return result; + } + + public override toJSON(): LocalRelativeSidValueBlockJson { + return { + ...super.toJSON(), + valueDec: this.valueDec, + }; + } + +} diff --git a/comm/third_party/asn1js/src/internals/LocalSidValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalSidValueBlock.ts new file mode 100644 index 0000000000..a9a45ba359 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalSidValueBlock.ts @@ -0,0 +1,197 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import * as pvtsutils from "pvtsutils"; +import * as pvutils from "pvutils"; +import { HexBlockJson, HexBlockParams, HexBlock } from "../HexBlock"; +import { ValueBlock, ValueBlockJson, ValueBlockParams } from "../ValueBlock"; +import { EMPTY_BUFFER } from "./constants"; +import * as utils from "./utils"; + +export interface ILocalSidValueBlock { + valueDec: number; + isFirstSid: boolean; +} + +export interface LocalSidValueBlockParams extends HexBlockParams, ValueBlockParams, Partial<ILocalSidValueBlock> { } + +export interface LocalSidValueBlockJson extends HexBlockJson, ValueBlockJson, ILocalSidValueBlock { } + +export class LocalSidValueBlock extends HexBlock(ValueBlock) implements ILocalSidValueBlock { + + public static override NAME = "sidBlock"; + + public valueDec: number; + public isFirstSid: boolean; + + constructor({ + valueDec = -1, + isFirstSid = false, + ...parameters + }: LocalSidValueBlockParams = {}) { + super(parameters); + + this.valueDec = valueDec; + this.isFirstSid = isFirstSid; + } + + public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number { + if (!inputLength) { + return inputOffset; + } + const inputView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + + // Basic check for parameters + if (!utils.checkBufferParams(this, inputView, inputOffset, inputLength)) { + return -1; + } + + const intBuffer = inputView.subarray(inputOffset, inputOffset + inputLength); + + this.valueHexView = new Uint8Array(inputLength); + + for (let i = 0; i < inputLength; i++) { + this.valueHexView[i] = intBuffer[i] & 0x7F; + + this.blockLength++; + + if ((intBuffer[i] & 0x80) === 0x00) + break; + } + + //#region Adjust size of valueHex buffer + const tempView = new Uint8Array(this.blockLength); + + for (let i = 0; i < this.blockLength; i++) { + tempView[i] = this.valueHexView[i]; + } + + this.valueHexView = tempView; + //#endregion + + if ((intBuffer[this.blockLength - 1] & 0x80) !== 0x00) { + this.error = "End of input reached before message was fully decoded"; + + return -1; + } + + if (this.valueHexView[0] === 0x00) + this.warnings.push("Needlessly long format of SID encoding"); + + if (this.blockLength <= 8) + this.valueDec = pvutils.utilFromBase(this.valueHexView, 7); + else { + this.isHexOnly = true; + this.warnings.push("Too big SID for decoding, hex only"); + } + + return (inputOffset + this.blockLength); + } + + public set valueBigInt(value: bigint) { + + utils.assertBigInt(); + + let bits = BigInt(value).toString(2); + while (bits.length % 7) { + bits = "0" + bits; + } + const bytes = new Uint8Array(bits.length / 7); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = parseInt(bits.slice(i * 7, i * 7 + 7), 2) + (i + 1 < bytes.length ? 0x80 : 0); + } + this.fromBER(bytes.buffer, 0, bytes.length); + } + + public override toBER(sizeOnly?: boolean): ArrayBuffer { + if (this.isHexOnly) { + if (sizeOnly) + return (new ArrayBuffer(this.valueHexView.byteLength)); + + const curView = this.valueHexView; + const retView = new Uint8Array(this.blockLength); + + for (let i = 0; i < (this.blockLength - 1); i++) + retView[i] = curView[i] | 0x80; + + retView[this.blockLength - 1] = curView[this.blockLength - 1]; + + return retView.buffer; + } + + const encodedBuf = pvutils.utilToBase(this.valueDec, 7); + if (encodedBuf.byteLength === 0) { + this.error = "Error during encoding SID value"; + + return EMPTY_BUFFER; + } + + const retView = new Uint8Array(encodedBuf.byteLength); + + if (!sizeOnly) { + const encodedView = new Uint8Array(encodedBuf); + const len = encodedBuf.byteLength - 1; + + for (let i = 0; i < len; i++) + retView[i] = encodedView[i] | 0x80; + + retView[len] = encodedView[len]; + } + + return retView; + } + + public override toString(): string { + let result = ""; + + if (this.isHexOnly) + result = pvtsutils.Convert.ToHex(this.valueHexView); + else { + if (this.isFirstSid) { + let sidValue = this.valueDec; + + if (this.valueDec <= 39) + result = "0."; + else { + if (this.valueDec <= 79) { + result = "1."; + sidValue -= 40; + } + else { + result = "2."; + sidValue -= 80; + } + } + + result += sidValue.toString(); + } + + else + result = this.valueDec.toString(); + } + + return result; + } + + public override toJSON(): LocalSidValueBlockJson { + return { + ...super.toJSON(), + valueDec: this.valueDec, + isFirstSid: this.isFirstSid, + }; + } + +} + +export interface LocalSidValueBlock { + /** + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueBeforeDecode: ArrayBuffer; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueHex: ArrayBuffer; +} diff --git a/comm/third_party/asn1js/src/internals/LocalSimpleStringBlock.ts b/comm/third_party/asn1js/src/internals/LocalSimpleStringBlock.ts new file mode 100644 index 0000000000..a52c9e2898 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalSimpleStringBlock.ts @@ -0,0 +1,34 @@ +import * as pvtsutils from "pvtsutils"; +import { BaseBlockParams } from "../BaseBlock"; +import { BaseStringBlock } from "../BaseStringBlock"; +import { LocalSimpleStringValueBlock, LocalSimpleStringValueBlockJson, LocalSimpleStringValueBlockParams } from "./LocalSimpleStringValueBlock"; + +export interface LocalSimpleStringBlockParams extends BaseBlockParams, LocalSimpleStringValueBlockParams { } +export type LocalSimpleStringBlockJson = LocalSimpleStringValueBlockJson; + +export class LocalSimpleStringBlock extends BaseStringBlock<LocalSimpleStringValueBlock, LocalSimpleStringValueBlockJson> { + + public static override NAME = "SIMPLE STRING"; + + constructor({ + ...parameters + }: LocalSimpleStringBlockParams = {}) { + super(parameters, LocalSimpleStringValueBlock); + } + + public override fromBuffer(inputBuffer: ArrayBuffer | Uint8Array): void { + this.valueBlock.value = String.fromCharCode.apply(null, pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer) as unknown as number[]); + } + + public fromString(inputString: string): void { + const strLen = inputString.length; + + const view = this.valueBlock.valueHexView = new Uint8Array(strLen); + + for (let i = 0; i < strLen; i++) + view[i] = inputString.charCodeAt(i); + + this.valueBlock.value = inputString; + } + +} diff --git a/comm/third_party/asn1js/src/internals/LocalSimpleStringValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalSimpleStringValueBlock.ts new file mode 100644 index 0000000000..f2ee09a8a3 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalSimpleStringValueBlock.ts @@ -0,0 +1,11 @@ +import { ILocalStringValueBlock, LocalStringValueBlockParams, LocalStringValueBlockJson, LocalStringValueBlock } from "./LocalStringValueBlock"; + +export type ILocalSimpleStringValueBlock = ILocalStringValueBlock; +export type LocalSimpleStringValueBlockParams = LocalStringValueBlockParams; +export type LocalSimpleStringValueBlockJson = LocalStringValueBlockJson; + +export class LocalSimpleStringValueBlock extends LocalStringValueBlock { + + public static override NAME = "SimpleStringValueBlock"; + +} diff --git a/comm/third_party/asn1js/src/internals/LocalStringValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalStringValueBlock.ts new file mode 100644 index 0000000000..dfd0ee19e3 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalStringValueBlock.ts @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { HexBlockJson, HexBlockParams, HexBlock } from "../HexBlock"; +import { ValueBlock, ValueBlockJson, ValueBlockParams } from "../ValueBlock"; +import { EMPTY_STRING } from "./constants"; +import { LocalUtf8StringValueBlockParams, LocalUtf8StringValueBlockJson } from "./LocalUtf8StringValueBlock"; + +export interface ILocalStringValueBlock { + value: string; +} + +export interface LocalStringValueBlockParams extends Omit<HexBlockParams, "isHexOnly">, ValueBlockParams, Partial<ILocalStringValueBlock> { } + +export interface LocalStringValueBlockJson extends HexBlockJson, ValueBlockJson, ILocalStringValueBlock { } + +export abstract class LocalStringValueBlock extends HexBlock(ValueBlock) implements ILocalStringValueBlock { + + public static override NAME = "StringValueBlock"; + + public value: string; + + constructor({ + ...parameters + }: LocalUtf8StringValueBlockParams = {}) { + super(parameters); + + this.isHexOnly = true; + this.value = EMPTY_STRING; // String representation of decoded ArrayBuffer + } + + public override toJSON(): LocalUtf8StringValueBlockJson { + return { + ...super.toJSON(), + value: this.value, + }; + } + +} + +export interface LocalStringValueBlock { + /** + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueBeforeDecode: ArrayBuffer; + /** + * Binary data in ArrayBuffer representation + * + * @deprecated since version 3.0.0 + */ + // @ts-ignore + valueHex: ArrayBuffer; +} diff --git a/comm/third_party/asn1js/src/internals/LocalUniversalStringValueBlockParams.ts b/comm/third_party/asn1js/src/internals/LocalUniversalStringValueBlockParams.ts new file mode 100644 index 0000000000..6b3b09359a --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalUniversalStringValueBlockParams.ts @@ -0,0 +1,45 @@ +import * as pvutils from "pvutils"; +import { LocalSimpleStringBlock, LocalSimpleStringBlockJson, LocalSimpleStringBlockParams } from "./LocalSimpleStringBlock"; + +export type LocalUniversalStringValueBlockParams = LocalSimpleStringBlockParams; +export type LocalUniversalStringValueBlockJson = LocalSimpleStringBlockJson; + +export class LocalUniversalStringValueBlock extends LocalSimpleStringBlock { + + public static override NAME = "UniversalStringValueBlock"; + + public override fromBuffer(inputBuffer: ArrayBuffer | Uint8Array): void { + const copyBuffer = ArrayBuffer.isView(inputBuffer) ? inputBuffer.slice().buffer : inputBuffer.slice(0); + const valueView = new Uint8Array(copyBuffer); + + for (let i = 0; i < valueView.length; i += 4) { + valueView[i] = valueView[i + 3]; + valueView[i + 1] = valueView[i + 2]; + valueView[i + 2] = 0x00; + valueView[i + 3] = 0x00; + } + + this.valueBlock.value = String.fromCharCode.apply(null, new Uint32Array(copyBuffer) as unknown as number[]); + } + + public override fromString(inputString: string): void { + const strLength = inputString.length; + + const valueHexView = this.valueBlock.valueHexView = new Uint8Array(strLength * 4); + + for (let i = 0; i < strLength; i++) { + const codeBuf = pvutils.utilToBase(inputString.charCodeAt(i), 8); + const codeView = new Uint8Array(codeBuf); + if (codeView.length > 4) + continue; + + const dif = 4 - codeView.length; + + for (let j = (codeView.length - 1); j >= 0; j--) + valueHexView[i * 4 + j + dif] = codeView[j]; + } + + this.valueBlock.value = inputString; + } + +} diff --git a/comm/third_party/asn1js/src/internals/LocalUtf8StringValueBlock.ts b/comm/third_party/asn1js/src/internals/LocalUtf8StringValueBlock.ts new file mode 100644 index 0000000000..7d578d8950 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/LocalUtf8StringValueBlock.ts @@ -0,0 +1,27 @@ +import * as pvtsutils from "pvtsutils"; +import { LocalSimpleStringBlock, LocalSimpleStringBlockJson, LocalSimpleStringBlockParams } from "./LocalSimpleStringBlock"; + +export type LocalUtf8StringValueBlockParams = LocalSimpleStringBlockParams; +export type LocalUtf8StringValueBlockJson = LocalSimpleStringBlockJson; + +export class LocalUtf8StringValueBlock extends LocalSimpleStringBlock { + + public static override NAME = "Utf8StringValueBlock"; + + public override fromBuffer(inputBuffer: ArrayBuffer | Uint8Array): void { + this.valueBlock.valueHexView = pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer); + try { + this.valueBlock.value = pvtsutils.Convert.ToUtf8String(inputBuffer); + } + catch (ex) { + this.warnings.push(`Error during "decodeURIComponent": ${ex}, using raw string`); + this.valueBlock.value = pvtsutils.Convert.ToBinary(inputBuffer); + } + } + + public override fromString(inputString: string): void { + this.valueBlock.valueHexView = new Uint8Array(pvtsutils.Convert.FromUtf8String(inputString)); + this.valueBlock.value = inputString; + } + +} diff --git a/comm/third_party/asn1js/src/internals/constants.ts b/comm/third_party/asn1js/src/internals/constants.ts new file mode 100644 index 0000000000..beb0bd9eb8 --- /dev/null +++ b/comm/third_party/asn1js/src/internals/constants.ts @@ -0,0 +1,20 @@ +// Declaration of global variables + +export const powers2 = [new Uint8Array([1])]; +export const digitsString = "0123456789"; +export const NAME = "name"; +export const VALUE_HEX_VIEW = "valueHexView"; +export const IS_HEX_ONLY = "isHexOnly"; +export const ID_BLOCK = "idBlock"; +export const TAG_CLASS = "tagClass"; +export const TAG_NUMBER = "tagNumber"; +export const IS_CONSTRUCTED = "isConstructed"; +export const FROM_BER = "fromBER"; +export const TO_BER = "toBER"; +export const LOCAL = "local"; +export const EMPTY_STRING = ""; +export const EMPTY_BUFFER = new ArrayBuffer(0); +export const EMPTY_VIEW = new Uint8Array(0); +export const END_OF_CONTENT_NAME = "EndOfContent"; +export const OCTET_STRING_NAME = "OCTET STRING"; +export const BIT_STRING_NAME = "BIT STRING"; diff --git a/comm/third_party/asn1js/src/internals/utils.ts b/comm/third_party/asn1js/src/internals/utils.ts new file mode 100644 index 0000000000..84d1f3164f --- /dev/null +++ b/comm/third_party/asn1js/src/internals/utils.ts @@ -0,0 +1,81 @@ +// Utility functions + +import type { LocalBaseBlock } from "./LocalBaseBlock"; + +/** + * Throws an exception if BigInt is not supported + * @throws Throws Error if BigInt is not supported + */ +export function assertBigInt(): void { + if (typeof BigInt === "undefined") { + throw new Error("BigInt is not defined. Your environment doesn't implement BigInt."); + } +} + +/** + * Concatenates buffers from the list + * @param buffers List of buffers + * @returns Concatenated buffer + */ +export function concat(buffers: ArrayBuffer[]): ArrayBuffer { + let outputLength = 0; + let prevLength = 0; + + // Calculate output length + 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; +} + +/** + * Check input "Uint8Array" for common functions + * @param baseBlock + * @param inputBuffer + * @param inputOffset + * @param inputLength + * @returns + */ +export function checkBufferParams(baseBlock: LocalBaseBlock, inputBuffer: Uint8Array, inputOffset: number, inputLength: number): boolean { + 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; +}
\ No newline at end of file diff --git a/comm/third_party/asn1js/src/parser.ts b/comm/third_party/asn1js/src/parser.ts new file mode 100644 index 0000000000..51ab7dfcd8 --- /dev/null +++ b/comm/third_party/asn1js/src/parser.ts @@ -0,0 +1,301 @@ +import * as pvtsutils from "pvtsutils"; +import { ValueBlock } from "./ValueBlock"; +import { BaseBlock } from "./BaseBlock"; +import { LocalBaseBlock } from "./internals/LocalBaseBlock"; +import { AsnType, typeStore } from "./TypeStore"; +import { checkBufferParams } from "./internals/utils"; + +export interface FromBerResult { + offset: number; + result: AsnType; +} + +/** + * Local function changing a type for ASN.1 classes + * @param inputObject Incoming object + * @param newType Target type to convert + * @returns Converted object + */ +function localChangeType<T extends BaseBlock>(inputObject: BaseBlock, newType: new () => T): T { + 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; +} + +/** + * Internal library function for decoding ASN.1 BER + * @param inputBuffer ASN.1 BER encoded array + * @param inputOffset Offset in ASN.1 BER encoded array where decoding should be started + * @param inputLength Maximum length of array of bytes which can be using in this function + * @returns + */ +export function localFromBER(inputBuffer: Uint8Array, inputOffset = 0, inputLength = inputBuffer.length): FromBerResult { + const incomingOffset = inputOffset; // Need to store initial offset since "inputOffset" is changing in the function + + // Create a basic ASN.1 type since we need to return errors and warnings from the function + let returnObject = new BaseBlock({}, ValueBlock); + + // Basic check for parameters + const baseBlock = new LocalBaseBlock(); + if (!checkBufferParams(baseBlock, inputBuffer, inputOffset, inputLength)) { + returnObject.error = baseBlock.error; + + return { + offset: -1, + result: returnObject + }; + } + + // Getting Uint8Array subarray + const intBuffer = inputBuffer.subarray(inputOffset, inputOffset + inputLength); + + // Initial checks + if (!intBuffer.length) { + returnObject.error = "Zero buffer length"; + + return { + offset: -1, + result: returnObject + }; + } + + // Decode identification block of ASN.1 BER structure + // console.time("idBlock"); + 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 + }; + } + // console.timeEnd("idBlock"); + + inputOffset = resultOffset; + inputLength -= returnObject.idBlock.blockLength; + + // Decode length block of ASN.1 BER structure + // console.time("lengthBlock"); + 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 + }; + } + // console.timeEnd("lengthBlock"); + + inputOffset = resultOffset; + inputLength -= returnObject.lenBlock.blockLength; + + // Check for using indefinite length form in encoding for primitive types + if (!returnObject.idBlock.isConstructed && + returnObject.lenBlock.isIndefiniteForm) { + returnObject.error = "Indefinite length form used for primitive encoding form"; + + return { + offset: -1, + result: returnObject + }; + } + + // Switch ASN.1 block type + let newASN1Type: new () => AsnType = BaseBlock as any; + + switch (returnObject.idBlock.tagClass) { + // UNIVERSAL + case 1: + // Check for reserved tag numbers + 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 + }; + } + switch (returnObject.idBlock.tagNumber) { + case 0: // EndOfContent + // Check for EndOfContent type + if ((returnObject.idBlock.isConstructed) && + (returnObject.lenBlock.length > 0)) { + returnObject.error = "Type [UNIVERSAL 0] is reserved"; + + return { + offset: -1, + result: returnObject + }; + } + + newASN1Type = typeStore.EndOfContent; + + break; + case 1: // Boolean + newASN1Type = typeStore.Boolean; + break; + case 2: // Integer + newASN1Type = typeStore.Integer; + break; + case 3: // BitString + newASN1Type = typeStore.BitString; + break; + case 4: // OctetString + newASN1Type = typeStore.OctetString; + break; + case 5: // Null + newASN1Type = typeStore.Null; + break; + case 6: // ObjectIdentifier + newASN1Type = typeStore.ObjectIdentifier; + break; + case 10: // Enumerated + newASN1Type = typeStore.Enumerated; + break; + case 12: // Utf8String + newASN1Type = typeStore.Utf8String; + break; + case 13: // RelativeObjectIdentifier + newASN1Type = typeStore.RelativeObjectIdentifier; + break; + case 14: // TIME + newASN1Type = typeStore.TIME; + break; + case 15: + returnObject.error = "[UNIVERSAL 15] is reserved by ASN.1 standard"; + + return { + offset: -1, + result: returnObject + }; + case 16: // Sequence + newASN1Type = typeStore.Sequence; + break; + case 17: // Set + newASN1Type = typeStore.Set; + break; + case 18: // NumericString + newASN1Type = typeStore.NumericString; + break; + case 19: // PrintableString + newASN1Type = typeStore.PrintableString; + break; + case 20: // TeletexString + newASN1Type = typeStore.TeletexString; + break; + case 21: // VideotexString + newASN1Type = typeStore.VideotexString; + break; + case 22: // IA5String + newASN1Type = typeStore.IA5String; + break; + case 23: // UTCTime + newASN1Type = typeStore.UTCTime; + break; + case 24: // GeneralizedTime + newASN1Type = typeStore.GeneralizedTime; + break; + case 25: // GraphicString + newASN1Type = typeStore.GraphicString; + break; + case 26: // VisibleString + newASN1Type = typeStore.VisibleString; + break; + case 27: // GeneralString + newASN1Type = typeStore.GeneralString; + break; + case 28: // UniversalString + newASN1Type = typeStore.UniversalString; + break; + case 29: // CharacterString + newASN1Type = typeStore.CharacterString; + break; + case 30: // BmpString + newASN1Type = typeStore.BmpString; + break; + case 31: // DATE + newASN1Type = typeStore.DATE; + break; + case 32: // TimeOfDay + newASN1Type = typeStore.TimeOfDay; + break; + case 33: // DateTime + newASN1Type = typeStore.DateTime; + break; + case 34: // Duration + newASN1Type = typeStore.Duration; + break; + default: { + const newObject = returnObject.idBlock.isConstructed + ? new typeStore.Constructed() + : new typeStore.Primitive(); + + newObject.idBlock = returnObject.idBlock; + newObject.lenBlock = returnObject.lenBlock; + newObject.warnings = returnObject.warnings; + + returnObject = newObject; + } + } + break; + // All other tag classes + case 2: // APPLICATION + case 3: // CONTEXT-SPECIFIC + case 4: // PRIVATE + default: { + newASN1Type = returnObject.idBlock.isConstructed + ? typeStore.Constructed + : typeStore.Primitive; + } + } + + + // Change type and perform BER decoding + returnObject = localChangeType(returnObject, newASN1Type); + // console.time("valueBlock"); + resultOffset = returnObject.fromBER(inputBuffer, inputOffset, returnObject.lenBlock.isIndefiniteForm ? inputLength : returnObject.lenBlock.length); + + // Coping incoming buffer for entire ASN.1 block + returnObject.valueBeforeDecodeView = inputBuffer.subarray(incomingOffset, incomingOffset + returnObject.blockLength); + // console.timeEnd("valueBlock"); + + return { + offset: resultOffset, + result: returnObject + }; +} + +/** + * Major function for decoding ASN.1 BER array into internal library structures + * @param inputBuffer ASN.1 BER encoded array of bytes + */ +export function fromBER(inputBuffer: pvtsutils.BufferSource): FromBerResult { + 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); +} diff --git a/comm/third_party/asn1js/src/schema.ts b/comm/third_party/asn1js/src/schema.ts new file mode 100644 index 0000000000..4f07e92a13 --- /dev/null +++ b/comm/third_party/asn1js/src/schema.ts @@ -0,0 +1,475 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import * as pvtsutils from "pvtsutils"; +import { IS_CONSTRUCTED, EMPTY_STRING, NAME, ID_BLOCK, FROM_BER, TO_BER, TAG_CLASS, TAG_NUMBER, IS_HEX_ONLY, LOCAL, VALUE_HEX_VIEW } from "./internals/constants"; +import { Any } from "./Any"; +import { Choice } from "./Choice"; +import { Repeated } from "./Repeated"; +import { localFromBER } from "./parser"; +import { AsnType, typeStore } from "./TypeStore"; + +export type AsnSchemaType = AsnType | Any | Choice | Repeated; + +export interface CompareSchemaSuccess { + verified: true; + result: AsnType & { [key: string]: any; }; +} + +export interface CompareSchemaFail { + verified: false; + name?: string; + result: AsnType | { error: string; }; +} + +export type CompareSchemaResult = CompareSchemaSuccess | CompareSchemaFail; + +/** + * Compare of two ASN.1 object trees + * @param root Root of input ASN.1 object tree + * @param inputData Input ASN.1 object tree + * @param inputSchema Input ASN.1 schema to compare with + * @return Returns result of comparison + */ +export function compareSchema(root: AsnType, inputData: AsnType, inputSchema: AsnSchemaType): CompareSchemaResult { + //#region Special case for Choice schema element type + if (inputSchema instanceof Choice) { + const choiceResult = false; + + for (let j = 0; j < inputSchema.value.length; j++) { + const result = compareSchema(root, inputData, inputSchema.value[j]); + if (result.verified) { + return { + verified: true, + result: root + }; + } + } + + if (choiceResult === false) { + const _result: CompareSchemaResult = { + verified: false, + result: { + error: "Wrong values for Choice type" + }, + }; + + if (inputSchema.hasOwnProperty(NAME)) + _result.name = inputSchema.name; + + return _result; + } + } + //#endregion + //#region Special case for Any schema element type + if (inputSchema instanceof Any) { + //#region Add named component of ASN.1 schema + if (inputSchema.hasOwnProperty(NAME)) + (root as any)[inputSchema.name] = inputData; // TODO Such call may replace original field of the object (eg idBlock) + + + //#endregion + return { + verified: true, + result: root + }; + } + //#endregion + //#region Initial check + if ((root instanceof Object) === false) { + return { + verified: false, + result: { error: "Wrong root object" } + }; + } + + if ((inputData instanceof Object) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 data" } + }; + } + + if ((inputSchema instanceof Object) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + + if ((ID_BLOCK in inputSchema) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + //#endregion + //#region Comparing idBlock properties in ASN.1 data and ASN.1 schema + //#region Encode and decode ASN.1 schema idBlock + /// <remarks>This encoding/decoding is necessary because could be an errors in schema definition</remarks> + if ((FROM_BER in inputSchema.idBlock) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + + if ((TO_BER in inputSchema.idBlock) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + + const encodedId = inputSchema.idBlock.toBER(false); + if (encodedId.byteLength === 0) { + return { + verified: false, + result: { error: "Error encoding idBlock for ASN.1 schema" } + }; + } + + const decodedOffset = inputSchema.idBlock.fromBER(encodedId, 0, encodedId.byteLength); + if (decodedOffset === -1) { + return { + verified: false, + result: { error: "Error decoding idBlock for ASN.1 schema" } + }; + } + //#endregion + //#region tagClass + if (inputSchema.idBlock.hasOwnProperty(TAG_CLASS) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + + if (inputSchema.idBlock.tagClass !== inputData.idBlock.tagClass) { + return { + verified: false, + result: root + }; + } + //#endregion + //#region tagNumber + if (inputSchema.idBlock.hasOwnProperty(TAG_NUMBER) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + + if (inputSchema.idBlock.tagNumber !== inputData.idBlock.tagNumber) { + return { + verified: false, + result: root + }; + } + //#endregion + //#region isConstructed + if (inputSchema.idBlock.hasOwnProperty(IS_CONSTRUCTED) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + + if (inputSchema.idBlock.isConstructed !== inputData.idBlock.isConstructed) { + return { + verified: false, + result: root + }; + } + //#endregion + //#region isHexOnly + if (!(IS_HEX_ONLY in inputSchema.idBlock)) // Since 'isHexOnly' is an inherited property + { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + + if (inputSchema.idBlock.isHexOnly !== inputData.idBlock.isHexOnly) { + return { + verified: false, + result: root + }; + } + //#endregion + //#region valueHex + if (inputSchema.idBlock.isHexOnly) { + if ((VALUE_HEX_VIEW in inputSchema.idBlock) === false) // Since 'valueHex' is an inherited property + { + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + } + + const schemaView = inputSchema.idBlock.valueHexView; + const asn1View = inputData.idBlock.valueHexView; + + if (schemaView.length !== asn1View.length) { + return { + verified: false, + result: root + }; + } + + for (let i = 0; i < schemaView.length; i++) { + if (schemaView[i] !== asn1View[1]) { + return { + verified: false, + result: root + }; + } + } + } + //#endregion + //#endregion + //#region Add named component of ASN.1 schema + if (inputSchema.name) { + inputSchema.name = inputSchema.name.replace(/^\s+|\s+$/g, EMPTY_STRING); + if (inputSchema.name) + (root as any)[inputSchema.name] = inputData; // TODO check field existence. If exists throw an error + } + //#endregion + //#region Getting next ASN.1 block for comparison + if (inputSchema instanceof typeStore.Constructed) { + // TODO not clear how it works for OctetString and BitString + //* if (inputSchema.idBlock.isConstructed) { + let admission = 0; + let result: CompareSchemaResult = { + verified: false, + result: { + error: "Unknown error", + } + }; + + let maxLength = inputSchema.valueBlock.value.length; + + if (maxLength > 0) { + if (inputSchema.valueBlock.value[0] instanceof Repeated) { + // @ts-ignore + maxLength = inputData.valueBlock.value.length; // TODO debug it + } + } + + //#region Special case when constructive value has no elements + if (maxLength === 0) { + return { + verified: true, + result: root + }; + } + //#endregion + //#region Special case when "inputData" has no values and "inputSchema" has all optional values + // @ts-ignore + // TODO debug it + if ((inputData.valueBlock.value.length === 0) && + (inputSchema.valueBlock.value.length !== 0)) { + let _optional = true; + + for (let i = 0; i < inputSchema.valueBlock.value.length; i++) + _optional = _optional && (inputSchema.valueBlock.value[i].optional || false); + + if (_optional) { + return { + verified: true, + result: root + }; + } + + //#region Delete early added name of block + if (inputSchema.name) { + inputSchema.name = inputSchema.name.replace(/^\s+|\s+$/g, EMPTY_STRING); + if (inputSchema.name) + delete (root as any)[inputSchema.name]; + } + //#endregion + root.error = "Inconsistent object length"; + + return { + verified: false, + result: root + }; + } + //#endregion + for (let i = 0; i < maxLength; i++) { + //#region Special case when there is an OPTIONAL element of ASN.1 schema at the end + // @ts-ignore + if ((i - admission) >= inputData.valueBlock.value.length) { + if (inputSchema.valueBlock.value[i].optional === false) { + const _result: CompareSchemaResult = { + verified: false, + result: root + }; + + root.error = "Inconsistent length between ASN.1 data and schema"; + + //#region Delete early added name of block + if (inputSchema.name) { + inputSchema.name = inputSchema.name.replace(/^\s+|\s+$/g, EMPTY_STRING); + if (inputSchema.name) { + delete (root as any)[inputSchema.name]; + _result.name = inputSchema.name; + } + } + //#endregion + + return _result; + } + } + + //#endregion + else { + //#region Special case for Repeated type of ASN.1 schema element + if (inputSchema.valueBlock.value[0] instanceof Repeated) { + // @ts-ignore + result = compareSchema(root, inputData.valueBlock.value[i], inputSchema.valueBlock.value[0].value); + if (result.verified === false) { + if (inputSchema.valueBlock.value[0].optional) + admission++; + else { + //#region Delete early added name of block + if (inputSchema.name) { + inputSchema.name = inputSchema.name.replace(/^\s+|\s+$/g, EMPTY_STRING); + if (inputSchema.name) + delete (root as any)[inputSchema.name]; + } + //#endregion + + return result; + } + } + + if ((NAME in inputSchema.valueBlock.value[0]) && (inputSchema.valueBlock.value[0].name.length > 0)) { + let arrayRoot: Record<string, any> = {}; + + if ((LOCAL in inputSchema.valueBlock.value[0]) && (inputSchema.valueBlock.value[0].local)) + arrayRoot = inputData; + + else + arrayRoot = root; + + if (typeof arrayRoot[inputSchema.valueBlock.value[0].name] === "undefined") + arrayRoot[inputSchema.valueBlock.value[0].name] = []; + + // @ts-ignore + arrayRoot[inputSchema.valueBlock.value[0].name].push(inputData.valueBlock.value[i]); + } + } + + //#endregion + else { + // @ts-ignore + result = compareSchema(root, inputData.valueBlock.value[i - admission], inputSchema.valueBlock.value[i]); + if (result.verified === false) { + if (inputSchema.valueBlock.value[i].optional) + admission++; + else { + //#region Delete early added name of block + if (inputSchema.name) { + inputSchema.name = inputSchema.name.replace(/^\s+|\s+$/g, EMPTY_STRING); + if (inputSchema.name) + delete (root as any)[inputSchema.name]; + } + //#endregion + + return result; + } + } + } + } + } + + if (result.verified === false) // The situation may take place if last element is OPTIONAL and verification failed + { + const _result: CompareSchemaResult = { + verified: false, + result: root + }; + + //#region Delete early added name of block + if (inputSchema.name) { + inputSchema.name = inputSchema.name.replace(/^\s+|\s+$/g, EMPTY_STRING); + if (inputSchema.name) { + delete (root as any)[inputSchema.name]; + _result.name = inputSchema.name; + } + } + //#endregion + + return _result; + } + + return { + verified: true, + result: root + }; + } + //#endregion + //#region Ability to parse internal value for primitive-encoded value (value of OctetString, for example) + if (inputSchema.primitiveSchema && + (VALUE_HEX_VIEW in inputData.valueBlock)) { + //#region Decoding of raw ASN.1 data + const asn1 = localFromBER(inputData.valueBlock.valueHexView); + if (asn1.offset === -1) { + const _result: CompareSchemaResult = { + verified: false, + result: asn1.result + }; + + //#region Delete early added name of block + if (inputSchema.name) { + inputSchema.name = inputSchema.name.replace(/^\s+|\s+$/g, EMPTY_STRING); + if (inputSchema.name) { + delete (root as any)[inputSchema.name]; + _result.name = inputSchema.name; + } + } + //#endregion + + return _result; + } + //#endregion + + return compareSchema(root, asn1.result, inputSchema.primitiveSchema); + } + + return { + verified: true, + result: root + }; + //#endregion +} +/** + * ASN.1 schema verification for ArrayBuffer data + * @param inputBuffer Input BER-encoded ASN.1 data + * @param inputSchema Input ASN.1 schema to verify against to + * @return + */ + +export function verifySchema(inputBuffer: pvtsutils.BufferSource, inputSchema: AsnSchemaType): CompareSchemaResult { + //#region Initial check + if ((inputSchema instanceof Object) === false) { + return { + verified: false, + result: { error: "Wrong ASN.1 schema type" } + }; + } + //#endregion + //#region Decoding of raw ASN.1 data + const asn1 = localFromBER(pvtsutils.BufferSourceConverter.toUint8Array(inputBuffer)); + if (asn1.offset === -1) { + return { + verified: false, + result: asn1.result + }; + } + //#endregion + //#region Compare ASN.1 struct with input schema + + return compareSchema(asn1.result, asn1.result, inputSchema); + //#endregion +} diff --git a/comm/third_party/asn1js/src/types.ts b/comm/third_party/asn1js/src/types.ts new file mode 100644 index 0000000000..60b5ee414a --- /dev/null +++ b/comm/third_party/asn1js/src/types.ts @@ -0,0 +1,62 @@ + +export interface IBerConvertible { + + /** + * Base function for converting block from BER encoded array of bytes + * @param inputBuffer ASN.1 BER encoded array + * @param inputOffset Offset in ASN.1 BER encoded array where decoding should be started + * @param inputLength Maximum length of array of bytes which can be using in this function + * @returns Offset after least decoded byte + */ + fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number; + /** + * Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + * @param sizeOnly Flag that we need only a size of encoding, not a real array of bytes + * @returns ASN.1 BER encoded array + */ + toBER(sizeOnly?: boolean): ArrayBuffer; +} + +export interface IDerConvertible { + /** + * Base function for converting block from DER encoded array of bytes + * @param inputBuffer ASN.1 DER encoded array + * @param inputOffset Offset in ASN.1 DER encoded array where decoding should be started + * @param inputLength Maximum length of array of bytes which can be using in this function + * @param expectedLength Expected length of converted VALUE_HEX buffer + * @returns Offset after least decoded byte + */ + fromDER(inputBuffer: ArrayBuffer, inputOffset: number, inputLength: number, expectedLength?: number): number; + /** + * Encoding of current ASN.1 block into ASN.1 encoded array (DER rules) + * @param sizeOnly Flag that we need only a size of encoding, not a real array of bytes + * @returns ASN.1 DER encoded array + */ + toDER(sizeOnly?: boolean): ArrayBuffer; +} + +export interface IStringConvertible { + /** + * Returns a string representation of an object + * @returns String representation of the class object + */ + toString(): string; + /** + * Creates a class object from the string + * @param data Input string to convert from + */ + fromString(data: string): void; +} + +export interface IDateConvertible { + /** + * Converts a class object into the JavaScrip Date Object + * @returns Date object + */ + toDate(): Date; + /** + * Creates a class object from the JavaScript Date object + * @param date Date object + */ + fromDate(date: Date): void; +}
\ No newline at end of file |