TimeStampReq.ts (11565B)
1 import * as asn1js from "asn1js"; 2 import * as pvutils from "pvutils"; 3 import { MessageImprint, MessageImprintJson, MessageImprintSchema } from "./MessageImprint"; 4 import { Extension, ExtensionJson } from "./Extension"; 5 import * as Schema from "./Schema"; 6 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 7 import { AsnError } from "./errors"; 8 import { EMPTY_STRING } from "./constants"; 9 10 const VERSION = "version"; 11 const MESSAGE_IMPRINT = "messageImprint"; 12 const REQ_POLICY = "reqPolicy"; 13 const NONCE = "nonce"; 14 const CERT_REQ = "certReq"; 15 const EXTENSIONS = "extensions"; 16 const TIME_STAMP_REQ = "TimeStampReq"; 17 const TIME_STAMP_REQ_VERSION = `${TIME_STAMP_REQ}.${VERSION}`; 18 const TIME_STAMP_REQ_MESSAGE_IMPRINT = `${TIME_STAMP_REQ}.${MESSAGE_IMPRINT}`; 19 const TIME_STAMP_REQ_POLICY = `${TIME_STAMP_REQ}.${REQ_POLICY}`; 20 const TIME_STAMP_REQ_NONCE = `${TIME_STAMP_REQ}.${NONCE}`; 21 const TIME_STAMP_REQ_CERT_REQ = `${TIME_STAMP_REQ}.${CERT_REQ}`; 22 const TIME_STAMP_REQ_EXTENSIONS = `${TIME_STAMP_REQ}.${EXTENSIONS}`; 23 const CLEAR_PROPS = [ 24 TIME_STAMP_REQ_VERSION, 25 TIME_STAMP_REQ_MESSAGE_IMPRINT, 26 TIME_STAMP_REQ_POLICY, 27 TIME_STAMP_REQ_NONCE, 28 TIME_STAMP_REQ_CERT_REQ, 29 TIME_STAMP_REQ_EXTENSIONS, 30 ]; 31 32 export interface ITimeStampReq { 33 /** 34 * Version of the Time-Stamp request. Should be version 1. 35 */ 36 version: number; 37 /** 38 * Contains the hash of the datum to be time-stamped 39 */ 40 messageImprint: MessageImprint; 41 /** 42 * Indicates the TSA policy under which the TimeStampToken SHOULD be provided. 43 */ 44 reqPolicy?: string; 45 /** 46 * The nonce, if included, allows the client to verify the timeliness of 47 * the response when no local clock is available. The nonce is a large 48 * random number with a high probability that the client generates it 49 * only once. 50 */ 51 nonce?: asn1js.Integer; 52 /** 53 * If the certReq field is present and set to true, the TSA's public key 54 * certificate that is referenced by the ESSCertID identifier inside a 55 * SigningCertificate attribute in the response MUST be provided by the 56 * TSA in the certificates field from the SignedData structure in that 57 * response. That field may also contain other certificates. 58 * 59 * If the certReq field is missing or if the certReq field is present 60 * and set to false then the certificates field from the SignedData 61 * structure MUST not be present in the response. 62 */ 63 certReq?: boolean; 64 /** 65 * The extensions field is a generic way to add additional information 66 * to the request in the future. 67 */ 68 extensions?: Extension[]; 69 } 70 71 export interface TimeStampReqJson { 72 version: number; 73 messageImprint: MessageImprintJson; 74 reqPolicy?: string; 75 nonce?: asn1js.IntegerJson; 76 certReq?: boolean; 77 extensions?: ExtensionJson[]; 78 } 79 80 export type TimeStampReqParameters = PkiObjectParameters & Partial<ITimeStampReq>; 81 82 /** 83 * Represents the TimeStampReq structure described in [RFC3161](https://www.ietf.org/rfc/rfc3161.txt) 84 * 85 * @example The following example demonstrates how to create Time-Stamp Request 86 * ```js 87 * const nonce = pkijs.getRandomValues(new Uint8Array(10)).buffer; 88 * 89 * const tspReq = new pkijs.TimeStampReq({ 90 * version: 1, 91 * messageImprint: await pkijs.MessageImprint.create("SHA-256", message), 92 * reqPolicy: "1.2.3.4.5.6", 93 * certReq: true, 94 * nonce: new asn1js.Integer({ valueHex: nonce }), 95 * }); 96 * 97 * const tspReqRaw = tspReq.toSchema().toBER(); 98 * ``` 99 */ 100 export class TimeStampReq extends PkiObject implements ITimeStampReq { 101 102 public static override CLASS_NAME = "TimeStampReq"; 103 104 public version!: number; 105 public messageImprint!: MessageImprint; 106 public reqPolicy?: string; 107 public nonce?: asn1js.Integer; 108 public certReq?: boolean; 109 public extensions?: Extension[]; 110 111 /** 112 * Initializes a new instance of the {@link TimeStampReq} class 113 * @param parameters Initialization parameters 114 */ 115 constructor(parameters: TimeStampReqParameters = {}) { 116 super(); 117 118 this.version = pvutils.getParametersValue(parameters, VERSION, TimeStampReq.defaultValues(VERSION)); 119 this.messageImprint = pvutils.getParametersValue(parameters, MESSAGE_IMPRINT, TimeStampReq.defaultValues(MESSAGE_IMPRINT)); 120 if (REQ_POLICY in parameters) { 121 this.reqPolicy = pvutils.getParametersValue(parameters, REQ_POLICY, TimeStampReq.defaultValues(REQ_POLICY)); 122 } 123 if (NONCE in parameters) { 124 this.nonce = pvutils.getParametersValue(parameters, NONCE, TimeStampReq.defaultValues(NONCE)); 125 } 126 if (CERT_REQ in parameters) { 127 this.certReq = pvutils.getParametersValue(parameters, CERT_REQ, TimeStampReq.defaultValues(CERT_REQ)); 128 } 129 if (EXTENSIONS in parameters) { 130 this.extensions = pvutils.getParametersValue(parameters, EXTENSIONS, TimeStampReq.defaultValues(EXTENSIONS)); 131 } 132 133 if (parameters.schema) { 134 this.fromSchema(parameters.schema); 135 } 136 } 137 138 /** 139 * Returns default values for all class members 140 * @param memberName String name for a class member 141 * @returns Default value 142 */ 143 public static override defaultValues(memberName: typeof VERSION): number; 144 public static override defaultValues(memberName: typeof MESSAGE_IMPRINT): MessageImprint; 145 public static override defaultValues(memberName: typeof REQ_POLICY): string; 146 public static override defaultValues(memberName: typeof NONCE): asn1js.Integer; 147 public static override defaultValues(memberName: typeof CERT_REQ): boolean; 148 public static override defaultValues(memberName: typeof EXTENSIONS): Extension[]; 149 public static override defaultValues(memberName: string): any { 150 switch (memberName) { 151 case VERSION: 152 return 0; 153 case MESSAGE_IMPRINT: 154 return new MessageImprint(); 155 case REQ_POLICY: 156 return EMPTY_STRING; 157 case NONCE: 158 return new asn1js.Integer(); 159 case CERT_REQ: 160 return false; 161 case EXTENSIONS: 162 return []; 163 default: 164 return super.defaultValues(memberName); 165 } 166 } 167 168 /** 169 * Compare values with default values for all class members 170 * @param memberName String name for a class member 171 * @param memberValue Value to compare with default value 172 */ 173 public static compareWithDefault(memberName: string, memberValue: any): boolean { 174 switch (memberName) { 175 case VERSION: 176 case REQ_POLICY: 177 case CERT_REQ: 178 return (memberValue === TimeStampReq.defaultValues(memberName as typeof CERT_REQ)); 179 case MESSAGE_IMPRINT: 180 return ((MessageImprint.compareWithDefault("hashAlgorithm", memberValue.hashAlgorithm)) && 181 (MessageImprint.compareWithDefault("hashedMessage", memberValue.hashedMessage))); 182 case NONCE: 183 return (memberValue.isEqual(TimeStampReq.defaultValues(memberName))); 184 case EXTENSIONS: 185 return (memberValue.length === 0); 186 default: 187 return super.defaultValues(memberName); 188 } 189 } 190 191 /** 192 * @inheritdoc 193 * @asn ASN.1 schema 194 * ```asn 195 * TimeStampReq ::= SEQUENCE { 196 * version INTEGER { v1(1) }, 197 * messageImprint MessageImprint, 198 * reqPolicy TSAPolicyId OPTIONAL, 199 * nonce INTEGER OPTIONAL, 200 * certReq BOOLEAN DEFAULT FALSE, 201 * extensions [0] IMPLICIT Extensions OPTIONAL } 202 * 203 * TSAPolicyId ::= OBJECT IDENTIFIER 204 *``` 205 */ 206 public static override schema(parameters: Schema.SchemaParameters<{ 207 version?: string; 208 messageImprint?: MessageImprintSchema; 209 reqPolicy?: string; 210 nonce?: string; 211 certReq?: string; 212 extensions?: string; 213 }> = {}): Schema.SchemaType { 214 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 215 216 return (new asn1js.Sequence({ 217 name: (names.blockName || TIME_STAMP_REQ), 218 value: [ 219 new asn1js.Integer({ name: (names.version || TIME_STAMP_REQ_VERSION) }), 220 MessageImprint.schema(names.messageImprint || { 221 names: { 222 blockName: TIME_STAMP_REQ_MESSAGE_IMPRINT 223 } 224 }), 225 new asn1js.ObjectIdentifier({ 226 name: (names.reqPolicy || TIME_STAMP_REQ_POLICY), 227 optional: true 228 }), 229 new asn1js.Integer({ 230 name: (names.nonce || TIME_STAMP_REQ_NONCE), 231 optional: true 232 }), 233 new asn1js.Boolean({ 234 name: (names.certReq || TIME_STAMP_REQ_CERT_REQ), 235 optional: true 236 }), 237 new asn1js.Constructed({ 238 optional: true, 239 idBlock: { 240 tagClass: 3, // CONTEXT-SPECIFIC 241 tagNumber: 0 // [0] 242 }, 243 value: [new asn1js.Repeated({ 244 name: (names.extensions || TIME_STAMP_REQ_EXTENSIONS), 245 value: Extension.schema() 246 })] 247 }) // IMPLICIT SEQUENCE value 248 ] 249 })); 250 } 251 252 public fromSchema(schema: Schema.SchemaType): void { 253 // Clear input data first 254 pvutils.clearProps(schema, CLEAR_PROPS); 255 256 // Check the schema is valid 257 const asn1 = asn1js.compareSchema(schema, 258 schema, 259 TimeStampReq.schema() 260 ); 261 262 AsnError.assertSchema(asn1, this.className); 263 264 // Get internal properties from parsed schema 265 this.version = asn1.result[TIME_STAMP_REQ_VERSION].valueBlock.valueDec; 266 this.messageImprint = new MessageImprint({ schema: asn1.result[TIME_STAMP_REQ_MESSAGE_IMPRINT] }); 267 if (TIME_STAMP_REQ_POLICY in asn1.result) 268 this.reqPolicy = asn1.result[TIME_STAMP_REQ_POLICY].valueBlock.toString(); 269 if (TIME_STAMP_REQ_NONCE in asn1.result) 270 this.nonce = asn1.result[TIME_STAMP_REQ_NONCE]; 271 if (TIME_STAMP_REQ_CERT_REQ in asn1.result) 272 this.certReq = asn1.result[TIME_STAMP_REQ_CERT_REQ].valueBlock.value; 273 if (TIME_STAMP_REQ_EXTENSIONS in asn1.result) 274 this.extensions = Array.from(asn1.result[TIME_STAMP_REQ_EXTENSIONS], element => new Extension({ schema: element })); 275 } 276 277 public toSchema(): asn1js.Sequence { 278 //#region Create array for output sequence 279 const outputArray = []; 280 281 outputArray.push(new asn1js.Integer({ value: this.version })); 282 outputArray.push(this.messageImprint.toSchema()); 283 if (this.reqPolicy) 284 outputArray.push(new asn1js.ObjectIdentifier({ value: this.reqPolicy })); 285 if (this.nonce) 286 outputArray.push(this.nonce); 287 if ((CERT_REQ in this) && (TimeStampReq.compareWithDefault(CERT_REQ, this.certReq) === false)) 288 outputArray.push(new asn1js.Boolean({ value: this.certReq })); 289 290 //#region Create array of extensions 291 if (this.extensions) { 292 outputArray.push(new asn1js.Constructed({ 293 idBlock: { 294 tagClass: 3, // CONTEXT-SPECIFIC 295 tagNumber: 0 // [0] 296 }, 297 value: Array.from(this.extensions, o => o.toSchema()) 298 })); 299 } 300 //#endregion 301 //#endregion 302 303 //#region Construct and return new ASN.1 schema for this object 304 return (new asn1js.Sequence({ 305 value: outputArray 306 })); 307 //#endregion 308 } 309 310 public toJSON(): TimeStampReqJson { 311 const res: TimeStampReqJson = { 312 version: this.version, 313 messageImprint: this.messageImprint.toJSON() 314 }; 315 316 if (this.reqPolicy !== undefined) 317 res.reqPolicy = this.reqPolicy; 318 319 if (this.nonce !== undefined) 320 res.nonce = this.nonce.toJSON(); 321 322 if ((this.certReq !== undefined) && (TimeStampReq.compareWithDefault(CERT_REQ, this.certReq) === false)) 323 res.certReq = this.certReq; 324 325 if (this.extensions) { 326 res.extensions = Array.from(this.extensions, o => o.toJSON()); 327 } 328 329 return res; 330 } 331 332 }