SingleResponse.ts (10068B)
1 import * as asn1js from "asn1js"; 2 import * as pvutils from "pvutils"; 3 import { CertID, CertIDJson, CertIDSchema } from "./CertID"; 4 import { EMPTY_STRING } from "./constants"; 5 import { AsnError } from "./errors"; 6 import { Extension, ExtensionJson } from "./Extension"; 7 import { Extensions, ExtensionsSchema } from "./Extensions"; 8 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 9 import * as Schema from "./Schema"; 10 11 const CERT_ID = "certID"; 12 const CERT_STATUS = "certStatus"; 13 const THIS_UPDATE = "thisUpdate"; 14 const NEXT_UPDATE = "nextUpdate"; 15 const SINGLE_EXTENSIONS = "singleExtensions"; 16 const CLEAR_PROPS = [ 17 CERT_ID, 18 CERT_STATUS, 19 THIS_UPDATE, 20 NEXT_UPDATE, 21 SINGLE_EXTENSIONS, 22 ]; 23 24 export interface ISingleResponse { 25 certID: CertID; 26 certStatus: any; 27 thisUpdate: Date; 28 nextUpdate?: Date; 29 singleExtensions?: Extension[]; 30 } 31 32 export type SingleResponseParameters = PkiObjectParameters & Partial<ISingleResponse>; 33 34 export type SingleResponseSchema = Schema.SchemaParameters<{ 35 certID?: CertIDSchema; 36 certStatus?: string; 37 thisUpdate?: string; 38 nextUpdate?: string; 39 singleExtensions?: ExtensionsSchema; 40 }>; 41 42 export interface SingleResponseJson { 43 certID: CertIDJson; 44 certStatus: any; 45 thisUpdate: Date; 46 nextUpdate?: Date; 47 singleExtensions?: ExtensionJson[]; 48 } 49 50 /** 51 * Represents an SingleResponse described in [RFC6960](https://datatracker.ietf.org/doc/html/rfc6960) 52 */ 53 export class SingleResponse extends PkiObject implements ISingleResponse { 54 55 public static override CLASS_NAME = "SingleResponse"; 56 57 certID!: CertID; 58 certStatus: any; 59 thisUpdate!: Date; 60 nextUpdate?: Date; 61 singleExtensions?: Extension[]; 62 63 /** 64 * Initializes a new instance of the {@link SingleResponse} class 65 * @param parameters Initialization parameters 66 */ 67 constructor(parameters: SingleResponseParameters = {}) { 68 super(); 69 70 this.certID = pvutils.getParametersValue(parameters, CERT_ID, SingleResponse.defaultValues(CERT_ID)); 71 this.certStatus = pvutils.getParametersValue(parameters, CERT_STATUS, SingleResponse.defaultValues(CERT_STATUS)); 72 this.thisUpdate = pvutils.getParametersValue(parameters, THIS_UPDATE, SingleResponse.defaultValues(THIS_UPDATE)); 73 if (NEXT_UPDATE in parameters) { 74 this.nextUpdate = pvutils.getParametersValue(parameters, NEXT_UPDATE, SingleResponse.defaultValues(NEXT_UPDATE)); 75 } 76 if (SINGLE_EXTENSIONS in parameters) { 77 this.singleExtensions = pvutils.getParametersValue(parameters, SINGLE_EXTENSIONS, SingleResponse.defaultValues(SINGLE_EXTENSIONS)); 78 } 79 80 if (parameters.schema) { 81 this.fromSchema(parameters.schema); 82 } 83 } 84 85 /** 86 * Returns default values for all class members 87 * @param memberName String name for a class member 88 * @returns Default value 89 */ 90 public static override defaultValues(memberName: typeof CERT_ID): CertID; 91 public static override defaultValues(memberName: typeof CERT_STATUS): any; 92 public static override defaultValues(memberName: typeof THIS_UPDATE): Date; 93 public static override defaultValues(memberName: typeof NEXT_UPDATE): Date; 94 public static override defaultValues(memberName: typeof SINGLE_EXTENSIONS): Extension[]; 95 public static override defaultValues(memberName: string): any { 96 switch (memberName) { 97 case CERT_ID: 98 return new CertID(); 99 case CERT_STATUS: 100 return {}; 101 case THIS_UPDATE: 102 case NEXT_UPDATE: 103 return new Date(0, 0, 0); 104 case SINGLE_EXTENSIONS: 105 return []; 106 default: 107 return super.defaultValues(memberName); 108 } 109 } 110 111 /** 112 * Compare values with default values for all class members 113 * @param memberName String name for a class member 114 * @param memberValue Value to compare with default value 115 */ 116 public static compareWithDefault(memberName: string, memberValue: any): boolean { 117 switch (memberName) { 118 case CERT_ID: 119 return ((CertID.compareWithDefault("hashAlgorithm", memberValue.hashAlgorithm)) && 120 (CertID.compareWithDefault("issuerNameHash", memberValue.issuerNameHash)) && 121 (CertID.compareWithDefault("issuerKeyHash", memberValue.issuerKeyHash)) && 122 (CertID.compareWithDefault("serialNumber", memberValue.serialNumber))); 123 case CERT_STATUS: 124 return (Object.keys(memberValue).length === 0); 125 case THIS_UPDATE: 126 case NEXT_UPDATE: 127 return (memberValue === SingleResponse.defaultValues(memberName as typeof NEXT_UPDATE)); 128 default: 129 return super.defaultValues(memberName); 130 } 131 } 132 133 /** 134 * @inheritdoc 135 * @asn ASN.1 schema 136 * ```asn 137 * SingleResponse ::= SEQUENCE { 138 * certID CertID, 139 * certStatus CertStatus, 140 * thisUpdate GeneralizedTime, 141 * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, 142 * singleExtensions [1] EXPLICIT Extensions OPTIONAL } 143 * 144 * CertStatus ::= CHOICE { 145 * good [0] IMPLICIT NULL, 146 * revoked [1] IMPLICIT RevokedInfo, 147 * unknown [2] IMPLICIT UnknownInfo } 148 * 149 * RevokedInfo ::= SEQUENCE { 150 * revocationTime GeneralizedTime, 151 * revocationReason [0] EXPLICIT CRLReason OPTIONAL } 152 * 153 * UnknownInfo ::= NULL 154 *``` 155 */ 156 public static override schema(parameters: SingleResponseSchema = {}): Schema.SchemaType { 157 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 158 159 return (new asn1js.Sequence({ 160 name: (names.blockName || EMPTY_STRING), 161 value: [ 162 CertID.schema(names.certID || {}), 163 new asn1js.Choice({ 164 value: [ 165 new asn1js.Primitive({ 166 name: (names.certStatus || EMPTY_STRING), 167 idBlock: { 168 tagClass: 3, // CONTEXT-SPECIFIC 169 tagNumber: 0 // [0] 170 }, 171 }), // IMPLICIT NULL (no "valueBlock") 172 new asn1js.Constructed({ 173 name: (names.certStatus || EMPTY_STRING), 174 idBlock: { 175 tagClass: 3, // CONTEXT-SPECIFIC 176 tagNumber: 1 // [1] 177 }, 178 value: [ 179 new asn1js.GeneralizedTime(), 180 new asn1js.Constructed({ 181 optional: true, 182 idBlock: { 183 tagClass: 3, // CONTEXT-SPECIFIC 184 tagNumber: 0 // [0] 185 }, 186 value: [new asn1js.Enumerated()] 187 }) 188 ] 189 }), 190 new asn1js.Primitive({ 191 name: (names.certStatus || EMPTY_STRING), 192 idBlock: { 193 tagClass: 3, // CONTEXT-SPECIFIC 194 tagNumber: 2 // [2] 195 }, 196 lenBlock: { length: 1 } 197 }) // IMPLICIT NULL (no "valueBlock") 198 ] 199 }), 200 new asn1js.GeneralizedTime({ name: (names.thisUpdate || EMPTY_STRING) }), 201 new asn1js.Constructed({ 202 optional: true, 203 idBlock: { 204 tagClass: 3, // CONTEXT-SPECIFIC 205 tagNumber: 0 // [0] 206 }, 207 value: [new asn1js.GeneralizedTime({ name: (names.nextUpdate || EMPTY_STRING) })] 208 }), 209 new asn1js.Constructed({ 210 optional: true, 211 idBlock: { 212 tagClass: 3, // CONTEXT-SPECIFIC 213 tagNumber: 1 // [1] 214 }, 215 value: [Extensions.schema(names.singleExtensions || {})] 216 }) // EXPLICIT SEQUENCE value 217 ] 218 })); 219 } 220 221 public fromSchema(schema: Schema.SchemaType): void { 222 // Clear input data first 223 pvutils.clearProps(schema, CLEAR_PROPS); 224 225 // Check the schema is valid 226 const asn1 = asn1js.compareSchema(schema, 227 schema, 228 SingleResponse.schema({ 229 names: { 230 certID: { 231 names: { 232 blockName: CERT_ID 233 } 234 }, 235 certStatus: CERT_STATUS, 236 thisUpdate: THIS_UPDATE, 237 nextUpdate: NEXT_UPDATE, 238 singleExtensions: { 239 names: { 240 blockName: 241 SINGLE_EXTENSIONS 242 } 243 } 244 } 245 }) 246 ); 247 AsnError.assertSchema(asn1, this.className); 248 249 // Get internal properties from parsed schema 250 this.certID = new CertID({ schema: asn1.result.certID }); 251 this.certStatus = asn1.result.certStatus; 252 this.thisUpdate = asn1.result.thisUpdate.toDate(); 253 if (NEXT_UPDATE in asn1.result) 254 this.nextUpdate = asn1.result.nextUpdate.toDate(); 255 if (SINGLE_EXTENSIONS in asn1.result) 256 this.singleExtensions = Array.from(asn1.result.singleExtensions.valueBlock.value, element => new Extension({ schema: element })); 257 } 258 259 public toSchema(): asn1js.Sequence { 260 // Create value array for output sequence 261 const outputArray = []; 262 263 outputArray.push(this.certID.toSchema()); 264 outputArray.push(this.certStatus); 265 outputArray.push(new asn1js.GeneralizedTime({ valueDate: this.thisUpdate })); 266 if (this.nextUpdate) { 267 outputArray.push(new asn1js.Constructed({ 268 idBlock: { 269 tagClass: 3, // CONTEXT-SPECIFIC 270 tagNumber: 0 // [0] 271 }, 272 value: [new asn1js.GeneralizedTime({ valueDate: this.nextUpdate })] 273 })); 274 } 275 276 if (this.singleExtensions) { 277 outputArray.push(new asn1js.Constructed({ 278 idBlock: { 279 tagClass: 3, // CONTEXT-SPECIFIC 280 tagNumber: 1 // [1] 281 }, 282 value: [new asn1js.Sequence({ value: Array.from(this.singleExtensions, o => o.toSchema()) })] 283 })); 284 } 285 286 return (new asn1js.Sequence({ 287 value: outputArray 288 })); 289 } 290 291 public toJSON(): SingleResponseJson { 292 const res: SingleResponseJson = { 293 certID: this.certID.toJSON(), 294 certStatus: this.certStatus.toJSON(), 295 thisUpdate: this.thisUpdate 296 }; 297 298 if (this.nextUpdate) { 299 res.nextUpdate = this.nextUpdate; 300 } 301 302 if (this.singleExtensions) { 303 res.singleExtensions = Array.from(this.singleExtensions, o => o.toJSON()); 304 } 305 306 return res; 307 } 308 309 }