import * as asn1js from "asn1js"; import * as pvtsutils from "pvtsutils"; import * as pvutils from "pvutils"; import { GeneralName, GeneralNameJson, GeneralNameSchema } from "./GeneralName"; import { Request, RequestJson, RequestSchema } from "./Request"; import { Extension, ExtensionJson } from "./Extension"; import { Extensions, ExtensionsSchema } from "./Extensions"; import * as Schema from "./Schema"; import { AsnError } from "./errors"; import { PkiObject, PkiObjectParameters } from "./PkiObject"; import { EMPTY_BUFFER } from "./constants"; const TBS = "tbs"; const VERSION = "version"; const REQUESTOR_NAME = "requestorName"; const REQUEST_LIST = "requestList"; const REQUEST_EXTENSIONS = "requestExtensions"; const TBS_REQUEST = "TBSRequest"; const TBS_REQUEST_VERSION = `${TBS_REQUEST}.${VERSION}`; const TBS_REQUEST_REQUESTOR_NAME = `${TBS_REQUEST}.${REQUESTOR_NAME}`; const TBS_REQUEST_REQUESTS = `${TBS_REQUEST}.requests`; const TBS_REQUEST_REQUEST_EXTENSIONS = `${TBS_REQUEST}.${REQUEST_EXTENSIONS}`; const CLEAR_PROPS = [ TBS_REQUEST, TBS_REQUEST_VERSION, TBS_REQUEST_REQUESTOR_NAME, TBS_REQUEST_REQUESTS, TBS_REQUEST_REQUEST_EXTENSIONS ]; export interface ITBSRequest { tbs: ArrayBuffer; version?: number; requestorName?: GeneralName; requestList: Request[]; requestExtensions?: Extension[]; } export interface TBSRequestJson { tbs: string; version?: number; requestorName?: GeneralNameJson; requestList: RequestJson[]; requestExtensions?: ExtensionJson[]; } export type TBSRequestParameters = PkiObjectParameters & Partial; export type TBSRequestSchema = Schema.SchemaParameters<{ TBSRequestVersion?: string; requestorName?: GeneralNameSchema; requestList?: string; requests?: string; requestNames?: RequestSchema; extensions?: ExtensionsSchema; requestExtensions?: string; }>; /** * Represents the TBSRequest structure described in [RFC6960](https://datatracker.ietf.org/doc/html/rfc6960) */ export class TBSRequest extends PkiObject implements ITBSRequest { public static override CLASS_NAME = "TBSRequest"; public tbsView!: Uint8Array; /** * @deprecated Since version 3.0.0 */ public get tbs(): ArrayBuffer { return pvtsutils.BufferSourceConverter.toArrayBuffer(this.tbsView); } /** * @deprecated Since version 3.0.0 */ public set tbs(value: ArrayBuffer) { this.tbsView = new Uint8Array(value); } public version?: number; public requestorName?: GeneralName; public requestList!: Request[]; public requestExtensions?: Extension[]; /** * Initializes a new instance of the {@link TBSRequest} class * @param parameters Initialization parameters */ constructor(parameters: TBSRequestParameters = {}) { super(); this.tbsView = new Uint8Array(pvutils.getParametersValue(parameters, TBS, TBSRequest.defaultValues(TBS))); if (VERSION in parameters) { this.version = pvutils.getParametersValue(parameters, VERSION, TBSRequest.defaultValues(VERSION)); } if (REQUESTOR_NAME in parameters) { this.requestorName = pvutils.getParametersValue(parameters, REQUESTOR_NAME, TBSRequest.defaultValues(REQUESTOR_NAME)); } this.requestList = pvutils.getParametersValue(parameters, REQUEST_LIST, TBSRequest.defaultValues(REQUEST_LIST)); if (REQUEST_EXTENSIONS in parameters) { this.requestExtensions = pvutils.getParametersValue(parameters, REQUEST_EXTENSIONS, TBSRequest.defaultValues(REQUEST_EXTENSIONS)); } if (parameters.schema) { this.fromSchema(parameters.schema); } } /** * Returns default values for all class members * @param memberName String name for a class member * @returns Default value */ public static override defaultValues(memberName: typeof TBS): ArrayBuffer; public static override defaultValues(memberName: typeof VERSION): number; public static override defaultValues(memberName: typeof REQUESTOR_NAME): GeneralName; public static override defaultValues(memberName: typeof REQUEST_LIST): Request[]; public static override defaultValues(memberName: typeof REQUEST_EXTENSIONS): Extension[]; public static override defaultValues(memberName: string): any { switch (memberName) { case TBS: return EMPTY_BUFFER; case VERSION: return 0; case REQUESTOR_NAME: return new GeneralName(); case REQUEST_LIST: case REQUEST_EXTENSIONS: return []; default: return super.defaultValues(memberName); } } /** * Compare values with default values for all class members * @param memberName String name for a class member * @param memberValue Value to compare with default value */ public static compareWithDefault(memberName: string, memberValue: any): boolean { switch (memberName) { case TBS: return (memberValue.byteLength === 0); case VERSION: return (memberValue === TBSRequest.defaultValues(memberName)); case REQUESTOR_NAME: return ((memberValue.type === GeneralName.defaultValues("type")) && (Object.keys(memberValue.value).length === 0)); case REQUEST_LIST: case REQUEST_EXTENSIONS: return (memberValue.length === 0); default: return super.defaultValues(memberName); } } /** * @inheritdoc * @asn ASN.1 schema * ```asn * TBSRequest ::= SEQUENCE { * version [0] EXPLICIT Version DEFAULT v1, * requestorName [1] EXPLICIT GeneralName OPTIONAL, * requestList SEQUENCE OF Request, * requestExtensions [2] EXPLICIT Extensions OPTIONAL } *``` */ public static override schema(parameters: TBSRequestSchema = {}): Schema.SchemaType { const names = pvutils.getParametersValue>(parameters, "names", {}); return (new asn1js.Sequence({ name: (names.blockName || TBS_REQUEST), value: [ new asn1js.Constructed({ optional: true, idBlock: { tagClass: 3, // CONTEXT-SPECIFIC tagNumber: 0 // [0] }, value: [new asn1js.Integer({ name: (names.TBSRequestVersion || TBS_REQUEST_VERSION) })] }), new asn1js.Constructed({ optional: true, idBlock: { tagClass: 3, // CONTEXT-SPECIFIC tagNumber: 1 // [1] }, value: [GeneralName.schema(names.requestorName || { names: { blockName: TBS_REQUEST_REQUESTOR_NAME } })] }), new asn1js.Sequence({ name: (names.requestList || "TBSRequest.requestList"), value: [ new asn1js.Repeated({ name: (names.requests || TBS_REQUEST_REQUESTS), value: Request.schema(names.requestNames || {}) }) ] }), new asn1js.Constructed({ optional: true, idBlock: { tagClass: 3, // CONTEXT-SPECIFIC tagNumber: 2 // [2] }, value: [Extensions.schema(names.extensions || { names: { blockName: (names.requestExtensions || TBS_REQUEST_REQUEST_EXTENSIONS) } })] }) ] })); } public fromSchema(schema: Schema.SchemaType): void { // Clear input data first pvutils.clearProps(schema, CLEAR_PROPS); // Check the schema is valid const asn1 = asn1js.compareSchema(schema, schema, TBSRequest.schema() ); AsnError.assertSchema(asn1, this.className); // Get internal properties from parsed schema this.tbsView = asn1.result.TBSRequest.valueBeforeDecodeView; if (TBS_REQUEST_VERSION in asn1.result) this.version = asn1.result[TBS_REQUEST_VERSION].valueBlock.valueDec; if (TBS_REQUEST_REQUESTOR_NAME in asn1.result) this.requestorName = new GeneralName({ schema: asn1.result[TBS_REQUEST_REQUESTOR_NAME] }); this.requestList = Array.from(asn1.result[TBS_REQUEST_REQUESTS], element => new Request({ schema: element })); if (TBS_REQUEST_REQUEST_EXTENSIONS in asn1.result) this.requestExtensions = Array.from(asn1.result[TBS_REQUEST_REQUEST_EXTENSIONS].valueBlock.value, element => new Extension({ schema: element })); } /** * Convert current object to asn1js object and set correct values * @param encodeFlag If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts. * @returns asn1js object */ public toSchema(encodeFlag = false): asn1js.Sequence { //#region Decode stored TBS value let tbsSchema; if (encodeFlag === false) { if (this.tbsView.byteLength === 0) // No stored TBS part return TBSRequest.schema(); const asn1 = asn1js.fromBER(this.tbsView); AsnError.assert(asn1, "TBS Request"); if (!(asn1.result instanceof asn1js.Sequence)) { throw new Error("ASN.1 result should be SEQUENCE"); } tbsSchema = asn1.result; } //#endregion //#region Create TBS schema via assembling from TBS parts else { const outputArray = []; if (this.version !== undefined) { outputArray.push(new asn1js.Constructed({ idBlock: { tagClass: 3, // CONTEXT-SPECIFIC tagNumber: 0 // [0] }, value: [new asn1js.Integer({ value: this.version })] })); } if (this.requestorName) { outputArray.push(new asn1js.Constructed({ idBlock: { tagClass: 3, // CONTEXT-SPECIFIC tagNumber: 1 // [1] }, value: [this.requestorName.toSchema()] })); } outputArray.push(new asn1js.Sequence({ value: Array.from(this.requestList, o => o.toSchema()) })); if (this.requestExtensions) { outputArray.push(new asn1js.Constructed({ idBlock: { tagClass: 3, // CONTEXT-SPECIFIC tagNumber: 2 // [2] }, value: [ new asn1js.Sequence({ value: Array.from(this.requestExtensions, o => o.toSchema()) }) ] })); } tbsSchema = new asn1js.Sequence({ value: outputArray }); } //#endregion //#region Construct and return new ASN.1 schema for this object return tbsSchema; //#endregion } public toJSON(): TBSRequestJson { const res: any = {}; if (this.version != undefined) res.version = this.version; if (this.requestorName) { res.requestorName = this.requestorName.toJSON(); } res.requestList = Array.from(this.requestList, o => o.toJSON()); if (this.requestExtensions) { res.requestExtensions = Array.from(this.requestExtensions, o => o.toJSON()); } return res; } }