337 lines
11 KiB
TypeScript
337 lines
11 KiB
TypeScript
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<ITBSRequest>;
|
|
|
|
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<NonNullable<typeof parameters.names>>(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;
|
|
}
|
|
|
|
}
|