summaryrefslogtreecommitdiffstats
path: root/third_party/js/PKI.js/src/TimeStampResp.ts
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/js/PKI.js/src/TimeStampResp.ts')
-rw-r--r--third_party/js/PKI.js/src/TimeStampResp.ts286
1 files changed, 286 insertions, 0 deletions
diff --git a/third_party/js/PKI.js/src/TimeStampResp.ts b/third_party/js/PKI.js/src/TimeStampResp.ts
new file mode 100644
index 0000000000..bb400f9f7a
--- /dev/null
+++ b/third_party/js/PKI.js/src/TimeStampResp.ts
@@ -0,0 +1,286 @@
+import * as asn1js from "asn1js";
+import * as pvutils from "pvutils";
+import { PKIStatusInfo, PKIStatusInfoJson, PKIStatusInfoSchema } from "./PKIStatusInfo";
+import { ContentInfo, ContentInfoJson, ContentInfoSchema } from "./ContentInfo";
+import { SignedData } from "./SignedData";
+import * as Schema from "./Schema";
+import { id_ContentType_SignedData } from "./ObjectIdentifiers";
+import { Certificate } from "./Certificate";
+import { PkiObject, PkiObjectParameters } from "./PkiObject";
+import { AsnError } from "./errors";
+import { EMPTY_BUFFER, EMPTY_STRING } from "./constants";
+import * as common from "./common";
+
+const STATUS = "status";
+const TIME_STAMP_TOKEN = "timeStampToken";
+const TIME_STAMP_RESP = "TimeStampResp";
+const TIME_STAMP_RESP_STATUS = `${TIME_STAMP_RESP}.${STATUS}`;
+const TIME_STAMP_RESP_TOKEN = `${TIME_STAMP_RESP}.${TIME_STAMP_TOKEN}`;
+const CLEAR_PROPS = [
+ TIME_STAMP_RESP_STATUS,
+ TIME_STAMP_RESP_TOKEN
+];
+
+export interface ITimeStampResp {
+ /**
+ * Time-Stamp status
+ */
+ status: PKIStatusInfo;
+ /**
+ * Time-Stamp token
+ */
+ timeStampToken?: ContentInfo;
+}
+
+export interface TimeStampRespJson {
+ status: PKIStatusInfoJson;
+ timeStampToken?: ContentInfoJson;
+}
+
+export interface TimeStampRespVerifyParams {
+ signer?: number;
+ trustedCerts?: Certificate[];
+ data?: ArrayBuffer;
+}
+
+export type TimeStampRespParameters = PkiObjectParameters & Partial<ITimeStampResp>;
+
+/**
+ * Represents the TimeStampResp structure described in [RFC3161](https://www.ietf.org/rfc/rfc3161.txt)
+ *
+ * @example The following example demonstrates how to create and sign Time-Stamp Response
+ * ```js
+ * // Generate random serial number
+ * const serialNumber = pkijs.getRandomValues(new Uint8Array(10)).buffer;
+ *
+ * // Create specific TST info structure to sign
+ * const tstInfo = new pkijs.TSTInfo({
+ * version: 1,
+ * policy: tspReq.reqPolicy,
+ * messageImprint: tspReq.messageImprint,
+ * serialNumber: new asn1js.Integer({ valueHex: serialNumber }),
+ * genTime: new Date(),
+ * ordering: true,
+ * accuracy: new pkijs.Accuracy({
+ * seconds: 1,
+ * millis: 1,
+ * micros: 10
+ * }),
+ * nonce: tspReq.nonce,
+ * });
+ *
+ * // Create and sign CMS Signed Data with TSTInfo
+ * const cmsSigned = new pkijs.SignedData({
+ * version: 3,
+ * encapContentInfo: new pkijs.EncapsulatedContentInfo({
+ * eContentType: "1.2.840.113549.1.9.16.1.4", // "tSTInfo" content type
+ * eContent: new asn1js.OctetString({ valueHex: tstInfo.toSchema().toBER() }),
+ * }),
+ * signerInfos: [
+ * new pkijs.SignerInfo({
+ * version: 1,
+ * sid: new pkijs.IssuerAndSerialNumber({
+ * issuer: cert.issuer,
+ * serialNumber: cert.serialNumber
+ * })
+ * })
+ * ],
+ * certificates: [cert]
+ * });
+ *
+ * await cmsSigned.sign(keys.privateKey, 0, "SHA-256");
+ *
+ * // Create CMS Content Info
+ * const cmsContent = new pkijs.ContentInfo({
+ * contentType: pkijs.ContentInfo.SIGNED_DATA,
+ * content: cmsSigned.toSchema(true)
+ * });
+ *
+ * // Finally create completed TSP response structure
+ * const tspResp = new pkijs.TimeStampResp({
+ * status: new pkijs.PKIStatusInfo({ status: pkijs.PKIStatus.granted }),
+ * timeStampToken: new pkijs.ContentInfo({ schema: cmsContent.toSchema() })
+ * });
+ *
+ * const tspRespRaw = tspResp.toSchema().toBER();
+ * ```
+ */
+export class TimeStampResp extends PkiObject implements ITimeStampResp {
+
+ public static override CLASS_NAME = "TimeStampResp";
+
+ public status!: PKIStatusInfo;
+ public timeStampToken?: ContentInfo;
+
+ /**
+ * Initializes a new instance of the {@link TimeStampResp} class
+ * @param parameters Initialization parameters
+ */
+ constructor(parameters: TimeStampRespParameters = {}) {
+ super();
+
+ this.status = pvutils.getParametersValue(parameters, STATUS, TimeStampResp.defaultValues(STATUS));
+ if (TIME_STAMP_TOKEN in parameters) {
+ this.timeStampToken = pvutils.getParametersValue(parameters, TIME_STAMP_TOKEN, TimeStampResp.defaultValues(TIME_STAMP_TOKEN));
+ }
+
+ 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 STATUS): PKIStatusInfo;
+ public static override defaultValues(memberName: typeof TIME_STAMP_TOKEN): ContentInfo;
+ public static override defaultValues(memberName: string): any {
+ switch (memberName) {
+ case STATUS:
+ return new PKIStatusInfo();
+ case TIME_STAMP_TOKEN:
+ return new ContentInfo();
+ 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 STATUS:
+ return ((PKIStatusInfo.compareWithDefault(STATUS, memberValue.status)) &&
+ (("statusStrings" in memberValue) === false) &&
+ (("failInfo" in memberValue) === false));
+ case TIME_STAMP_TOKEN:
+ return ((memberValue.contentType === EMPTY_STRING) &&
+ (memberValue.content instanceof asn1js.Any));
+ default:
+ return super.defaultValues(memberName);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ * @asn ASN.1 schema
+ * ```asn
+ * TimeStampResp ::= SEQUENCE {
+ * status PKIStatusInfo,
+ * timeStampToken TimeStampToken OPTIONAL }
+ *```
+ */
+ public static override schema(parameters: Schema.SchemaParameters<{
+ status?: PKIStatusInfoSchema,
+ timeStampToken?: ContentInfoSchema,
+ }> = {}): Schema.SchemaType {
+ const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
+
+ return (new asn1js.Sequence({
+ name: (names.blockName || TIME_STAMP_RESP),
+ value: [
+ PKIStatusInfo.schema(names.status || {
+ names: {
+ blockName: TIME_STAMP_RESP_STATUS
+ }
+ }),
+ ContentInfo.schema(names.timeStampToken || {
+ names: {
+ blockName: TIME_STAMP_RESP_TOKEN,
+ optional: true
+ }
+ })
+ ]
+ }));
+ }
+
+ 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,
+ TimeStampResp.schema()
+ );
+ AsnError.assertSchema(asn1, this.className);
+
+ // Get internal properties from parsed schema
+ this.status = new PKIStatusInfo({ schema: asn1.result[TIME_STAMP_RESP_STATUS] });
+ if (TIME_STAMP_RESP_TOKEN in asn1.result)
+ this.timeStampToken = new ContentInfo({ schema: asn1.result[TIME_STAMP_RESP_TOKEN] });
+ }
+
+ public toSchema(): asn1js.Sequence {
+ //#region Create array for output sequence
+ const outputArray = [];
+
+ outputArray.push(this.status.toSchema());
+ if (this.timeStampToken) {
+ outputArray.push(this.timeStampToken.toSchema());
+ }
+ //#endregion
+
+ //#region Construct and return new ASN.1 schema for this object
+ return (new asn1js.Sequence({
+ value: outputArray
+ }));
+ //#endregion
+ }
+
+ public toJSON(): TimeStampRespJson {
+ const res: TimeStampRespJson = {
+ status: this.status.toJSON()
+ };
+
+ if (this.timeStampToken) {
+ res.timeStampToken = this.timeStampToken.toJSON();
+ }
+
+ return res;
+ }
+
+ /**
+ * Sign current TSP Response
+ * @param privateKey Private key for "subjectPublicKeyInfo" structure
+ * @param hashAlgorithm Hashing algorithm. Default SHA-1
+ * @param crypto Crypto engine
+ */
+ public async sign(privateKey: CryptoKey, hashAlgorithm?: string, crypto = common.getCrypto(true)) {
+ this.assertContentType();
+
+ // Sign internal signed data value
+ const signed = new SignedData({ schema: this.timeStampToken.content });
+
+ return signed.sign(privateKey, 0, hashAlgorithm, undefined, crypto);
+ }
+
+ /**
+ * Verify current TSP Response
+ * @param verificationParameters Input parameters for verification
+ * @param crypto Crypto engine
+ */
+ public async verify(verificationParameters: TimeStampRespVerifyParams = { signer: 0, trustedCerts: [], data: EMPTY_BUFFER }, crypto = common.getCrypto(true)): Promise<boolean> {
+ this.assertContentType();
+
+ // Verify internal signed data value
+ const signed = new SignedData({ schema: this.timeStampToken.content });
+
+ return signed.verify(verificationParameters, crypto);
+ }
+
+ private assertContentType(): asserts this is { timeStampToken: ContentInfo; } {
+ if (!this.timeStampToken) {
+ throw new Error("timeStampToken is absent in TSP response");
+ }
+ if (this.timeStampToken.contentType !== id_ContentType_SignedData) { // Must be a CMS signed data
+ throw new Error(`Wrong format of timeStampToken: ${this.timeStampToken.contentType}`);
+ }
+ }
+}
+