CertID.ts (10003B)
1 import * as asn1js from "asn1js"; 2 import * as pvtsutils from "pvtsutils"; 3 import * as pvutils from "pvutils"; 4 import * as common from "./common"; 5 import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier"; 6 import { Certificate } from "./Certificate"; 7 import * as Schema from "./Schema"; 8 import { AsnError, ParameterError } from "./errors"; 9 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 10 import { EMPTY_STRING } from "./constants"; 11 12 const HASH_ALGORITHM = "hashAlgorithm"; 13 const ISSUER_NAME_HASH = "issuerNameHash"; 14 const ISSUER_KEY_HASH = "issuerKeyHash"; 15 const SERIAL_NUMBER = "serialNumber"; 16 const CLEAR_PROPS = [ 17 HASH_ALGORITHM, 18 ISSUER_NAME_HASH, 19 ISSUER_KEY_HASH, 20 SERIAL_NUMBER, 21 ]; 22 23 export interface ICertID { 24 /** 25 * Hash algorithm used to generate the `issuerNameHash` and `issuerKeyHash` values 26 */ 27 hashAlgorithm: AlgorithmIdentifier; 28 /** 29 * Hash of the issuer's distinguished name (DN). The hash shall be calculated over the DER encoding 30 * of the issuer's name field in the certificate being checked. 31 */ 32 issuerNameHash: asn1js.OctetString; 33 /** 34 * Hash of the issuer's public key. The hash shall be calculated over the value (excluding tag and length) 35 * of the subject public key field in the issuer's certificate. 36 */ 37 issuerKeyHash: asn1js.OctetString; 38 /** 39 * Serial number of the certificate for which status is being requested 40 */ 41 serialNumber: asn1js.Integer; 42 } 43 44 export type CertIDParameters = PkiObjectParameters & Partial<ICertID>; 45 46 export type CertIDSchema = Schema.SchemaParameters<{ 47 hashAlgorithm?: string; 48 hashAlgorithmObject?: AlgorithmIdentifierSchema; 49 issuerNameHash?: string; 50 issuerKeyHash?: string; 51 serialNumber?: string; 52 }>; 53 54 export interface CertIDJson { 55 hashAlgorithm: AlgorithmIdentifierJson; 56 issuerNameHash: asn1js.OctetStringJson; 57 issuerKeyHash: asn1js.OctetStringJson; 58 serialNumber: asn1js.IntegerJson; 59 } 60 61 export interface CertIDCreateParams { 62 issuerCertificate: Certificate; 63 hashAlgorithm: string; 64 } 65 66 /** 67 * Represents an CertID described in [RFC6960](https://datatracker.ietf.org/doc/html/rfc6960) 68 */ 69 export class CertID extends PkiObject implements ICertID { 70 71 public static override CLASS_NAME = "CertID"; 72 73 /** 74 * Making OCSP certificate identifier for specific certificate 75 * @param certificate Certificate making OCSP Request for 76 * @param parameters Additional parameters 77 * @param crypto Crypto engine 78 * @returns Returns created CertID object 79 */ 80 public static async create(certificate: Certificate, parameters: CertIDCreateParams, crypto = common.getCrypto(true)): Promise<CertID> { 81 const certID = new CertID(); 82 await certID.createForCertificate(certificate, parameters, crypto); 83 84 return certID; 85 } 86 87 public hashAlgorithm!: AlgorithmIdentifier; 88 public issuerNameHash!: asn1js.OctetString; 89 public issuerKeyHash!: asn1js.OctetString; 90 public serialNumber!: asn1js.Integer; 91 92 /** 93 * Initializes a new instance of the {@link CertID} class 94 * @param parameters Initialization parameters 95 */ 96 constructor(parameters: CertIDParameters = {}) { 97 super(); 98 99 this.hashAlgorithm = pvutils.getParametersValue(parameters, HASH_ALGORITHM, CertID.defaultValues(HASH_ALGORITHM)); 100 this.issuerNameHash = pvutils.getParametersValue(parameters, ISSUER_NAME_HASH, CertID.defaultValues(ISSUER_NAME_HASH)); 101 this.issuerKeyHash = pvutils.getParametersValue(parameters, ISSUER_KEY_HASH, CertID.defaultValues(ISSUER_KEY_HASH)); 102 this.serialNumber = pvutils.getParametersValue(parameters, SERIAL_NUMBER, CertID.defaultValues(SERIAL_NUMBER)); 103 104 if (parameters.schema) { 105 this.fromSchema(parameters.schema); 106 } 107 } 108 109 /** 110 * Returns default values for all class members 111 * @param memberName String name for a class member 112 * @returns Default value 113 */ 114 public static override defaultValues(memberName: typeof HASH_ALGORITHM): AlgorithmIdentifier; 115 public static override defaultValues(memberName: typeof ISSUER_NAME_HASH): asn1js.OctetString; 116 public static override defaultValues(memberName: typeof ISSUER_KEY_HASH): asn1js.OctetString; 117 public static override defaultValues(memberName: typeof SERIAL_NUMBER): asn1js.Integer; 118 public static override defaultValues(memberName: string): any { 119 switch (memberName) { 120 case HASH_ALGORITHM: 121 return new AlgorithmIdentifier(); 122 case ISSUER_NAME_HASH: 123 case ISSUER_KEY_HASH: 124 return new asn1js.OctetString(); 125 case SERIAL_NUMBER: 126 return new asn1js.Integer(); 127 default: 128 return super.defaultValues(memberName); 129 } 130 } 131 132 /** 133 * Compare values with default values for all class members 134 * @param memberName String name for a class member 135 * @param memberValue Value to compare with default value 136 */ 137 public static compareWithDefault(memberName: string, memberValue: any): boolean { 138 switch (memberName) { 139 case HASH_ALGORITHM: 140 return ((memberValue.algorithmId === EMPTY_STRING) && (("algorithmParams" in memberValue) === false)); 141 case ISSUER_NAME_HASH: 142 case ISSUER_KEY_HASH: 143 case SERIAL_NUMBER: 144 return (memberValue.isEqual(CertID.defaultValues(SERIAL_NUMBER))); 145 default: 146 return super.defaultValues(memberName); 147 } 148 } 149 150 /** 151 * @inheritdoc 152 * @asn ASN.1 schema 153 * ```asn 154 * CertID ::= SEQUENCE { 155 * hashAlgorithm AlgorithmIdentifier, 156 * issuerNameHash OCTET STRING, -- Hash of issuer's DN 157 * issuerKeyHash OCTET STRING, -- Hash of issuer's public key 158 * serialNumber CertificateSerialNumber } 159 *``` 160 */ 161 public static override schema(parameters: CertIDSchema = {}): Schema.SchemaType { 162 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 163 164 return (new asn1js.Sequence({ 165 name: (names.blockName || EMPTY_STRING), 166 value: [ 167 AlgorithmIdentifier.schema(names.hashAlgorithmObject || { 168 names: { 169 blockName: (names.hashAlgorithm || EMPTY_STRING) 170 } 171 }), 172 new asn1js.OctetString({ name: (names.issuerNameHash || EMPTY_STRING) }), 173 new asn1js.OctetString({ name: (names.issuerKeyHash || EMPTY_STRING) }), 174 new asn1js.Integer({ name: (names.serialNumber || EMPTY_STRING) }) 175 ] 176 })); 177 } 178 179 public fromSchema(schema: Schema.SchemaType): void { 180 // Clear input data first 181 pvutils.clearProps(schema, CLEAR_PROPS); 182 183 // Check the schema is valid 184 const asn1 = asn1js.compareSchema(schema, 185 schema, 186 CertID.schema({ 187 names: { 188 hashAlgorithm: HASH_ALGORITHM, 189 issuerNameHash: ISSUER_NAME_HASH, 190 issuerKeyHash: ISSUER_KEY_HASH, 191 serialNumber: SERIAL_NUMBER 192 } 193 }) 194 ); 195 AsnError.assertSchema(asn1, this.className); 196 197 // Get internal properties from parsed schema 198 this.hashAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.hashAlgorithm }); 199 this.issuerNameHash = asn1.result.issuerNameHash; 200 this.issuerKeyHash = asn1.result.issuerKeyHash; 201 this.serialNumber = asn1.result.serialNumber; 202 } 203 204 public toSchema(): asn1js.Sequence { 205 return (new asn1js.Sequence({ 206 value: [ 207 this.hashAlgorithm.toSchema(), 208 this.issuerNameHash, 209 this.issuerKeyHash, 210 this.serialNumber 211 ] 212 })); 213 } 214 215 public toJSON(): CertIDJson { 216 return { 217 hashAlgorithm: this.hashAlgorithm.toJSON(), 218 issuerNameHash: this.issuerNameHash.toJSON(), 219 issuerKeyHash: this.issuerKeyHash.toJSON(), 220 serialNumber: this.serialNumber.toJSON(), 221 }; 222 } 223 224 /** 225 * Checks that two "CertIDs" are equal 226 * @param certificateID Identifier of the certificate to be checked 227 */ 228 public isEqual(certificateID: CertID): boolean { 229 // Check HASH_ALGORITHM 230 if (this.hashAlgorithm.algorithmId !== certificateID.hashAlgorithm.algorithmId) { 231 return false; 232 } 233 234 // Check ISSUER_NAME_HASH 235 if (!pvtsutils.BufferSourceConverter.isEqual(this.issuerNameHash.valueBlock.valueHexView, certificateID.issuerNameHash.valueBlock.valueHexView)) { 236 return false; 237 } 238 239 // Check ISSUER_KEY_HASH 240 if (!pvtsutils.BufferSourceConverter.isEqual(this.issuerKeyHash.valueBlock.valueHexView, certificateID.issuerKeyHash.valueBlock.valueHexView)) { 241 return false; 242 } 243 244 // Check SERIAL_NUMBER 245 if (!this.serialNumber.isEqual(certificateID.serialNumber)) { 246 return false; 247 } 248 249 return true; 250 } 251 252 /** 253 * Making OCSP certificate identifier for specific certificate 254 * @param certificate Certificate making OCSP Request for 255 * @param parameters Additional parameters 256 * @param crypto Crypto engine 257 */ 258 public async createForCertificate(certificate: Certificate, parameters: CertIDCreateParams, crypto = common.getCrypto(true)): Promise<void> { 259 //#region Check input parameters 260 ParameterError.assert(parameters, HASH_ALGORITHM, "issuerCertificate"); 261 262 const hashOID = crypto.getOIDByAlgorithm({ name: parameters.hashAlgorithm }, true, "hashAlgorithm"); 263 264 this.hashAlgorithm = new AlgorithmIdentifier({ 265 algorithmId: hashOID, 266 algorithmParams: new asn1js.Null() 267 }); 268 const issuerCertificate = parameters.issuerCertificate; 269 //#endregion 270 271 // Initialize SERIAL_NUMBER field 272 this.serialNumber = certificate.serialNumber; 273 274 // Create ISSUER_NAME_HASH 275 const hashIssuerName = await crypto.digest({ name: parameters.hashAlgorithm }, issuerCertificate.subject.toSchema().toBER(false)); 276 this.issuerNameHash = new asn1js.OctetString({ valueHex: hashIssuerName }); 277 278 // Create ISSUER_KEY_HASH 279 const issuerKeyBuffer = issuerCertificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHexView; 280 const hashIssuerKey = await crypto.digest({ name: parameters.hashAlgorithm }, issuerKeyBuffer as BufferSource); 281 this.issuerKeyHash = new asn1js.OctetString({ valueHex: hashIssuerKey }); 282 } 283 284 }