SignerInfo.ts (13366B)
1 import * as asn1js from "asn1js"; 2 import * as pvutils from "pvutils"; 3 import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier"; 4 import { SignedAndUnsignedAttributes, SignedAndUnsignedAttributesJson, SignedAndUnsignedAttributesSchema } from "./SignedAndUnsignedAttributes"; 5 import { IssuerAndSerialNumber, IssuerAndSerialNumberSchema } from "./IssuerAndSerialNumber"; 6 import * as Schema from "./Schema"; 7 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 8 import { AsnError } from "./errors"; 9 10 const VERSION = "version"; 11 const SID = "sid"; 12 const DIGEST_ALGORITHM = "digestAlgorithm"; 13 const SIGNED_ATTRS = "signedAttrs"; 14 const SIGNATURE_ALGORITHM = "signatureAlgorithm"; 15 const SIGNATURE = "signature"; 16 const UNSIGNED_ATTRS = "unsignedAttrs"; 17 const SIGNER_INFO = "SignerInfo"; 18 const SIGNER_INFO_VERSION = `${SIGNER_INFO}.${VERSION}`; 19 const SIGNER_INFO_SID = `${SIGNER_INFO}.${SID}`; 20 const SIGNER_INFO_DIGEST_ALGORITHM = `${SIGNER_INFO}.${DIGEST_ALGORITHM}`; 21 const SIGNER_INFO_SIGNED_ATTRS = `${SIGNER_INFO}.${SIGNED_ATTRS}`; 22 const SIGNER_INFO_SIGNATURE_ALGORITHM = `${SIGNER_INFO}.${SIGNATURE_ALGORITHM}`; 23 const SIGNER_INFO_SIGNATURE = `${SIGNER_INFO}.${SIGNATURE}`; 24 const SIGNER_INFO_UNSIGNED_ATTRS = `${SIGNER_INFO}.${UNSIGNED_ATTRS}`; 25 const CLEAR_PROPS = [ 26 SIGNER_INFO_VERSION, 27 SIGNER_INFO_SID, 28 SIGNER_INFO_DIGEST_ALGORITHM, 29 SIGNER_INFO_SIGNED_ATTRS, 30 SIGNER_INFO_SIGNATURE_ALGORITHM, 31 SIGNER_INFO_SIGNATURE, 32 SIGNER_INFO_UNSIGNED_ATTRS 33 ]; 34 35 export interface ISignerInfo { 36 version: number; 37 sid: Schema.SchemaType; 38 digestAlgorithm: AlgorithmIdentifier; 39 signedAttrs?: SignedAndUnsignedAttributes; 40 signatureAlgorithm: AlgorithmIdentifier; 41 signature: asn1js.OctetString; 42 unsignedAttrs?: SignedAndUnsignedAttributes; 43 } 44 45 export interface SignerInfoJson { 46 version: number; 47 sid?: Schema.SchemaType; 48 digestAlgorithm: AlgorithmIdentifierJson; 49 signedAttrs?: SignedAndUnsignedAttributesJson; 50 signatureAlgorithm: AlgorithmIdentifierJson; 51 signature: asn1js.OctetStringJson; 52 unsignedAttrs?: SignedAndUnsignedAttributesJson; 53 } 54 55 export type SignerInfoParameters = PkiObjectParameters & Partial<ISignerInfo>; 56 57 /** 58 * Represents the SignerInfo structure described in [RFC5652](https://datatracker.ietf.org/doc/html/rfc5652) 59 */ 60 export class SignerInfo extends PkiObject implements ISignerInfo { 61 62 public static override CLASS_NAME = "SignerInfo"; 63 64 public version!: number; 65 public sid: Schema.SchemaType; 66 public digestAlgorithm!: AlgorithmIdentifier; 67 public signedAttrs?: SignedAndUnsignedAttributes; 68 public signatureAlgorithm!: AlgorithmIdentifier; 69 public signature!: asn1js.OctetString; 70 public unsignedAttrs?: SignedAndUnsignedAttributes; 71 72 /** 73 * Initializes a new instance of the {@link SignerInfo} class 74 * @param parameters Initialization parameters 75 */ 76 constructor(parameters: SignerInfoParameters = {}) { 77 super(); 78 79 this.version = pvutils.getParametersValue(parameters, VERSION, SignerInfo.defaultValues(VERSION)); 80 this.sid = pvutils.getParametersValue(parameters, SID, SignerInfo.defaultValues(SID)); 81 this.digestAlgorithm = pvutils.getParametersValue(parameters, DIGEST_ALGORITHM, SignerInfo.defaultValues(DIGEST_ALGORITHM)); 82 if (SIGNED_ATTRS in parameters) { 83 this.signedAttrs = pvutils.getParametersValue(parameters, SIGNED_ATTRS, SignerInfo.defaultValues(SIGNED_ATTRS)); 84 } 85 this.signatureAlgorithm = pvutils.getParametersValue(parameters, SIGNATURE_ALGORITHM, SignerInfo.defaultValues(SIGNATURE_ALGORITHM)); 86 this.signature = pvutils.getParametersValue(parameters, SIGNATURE, SignerInfo.defaultValues(SIGNATURE)); 87 if (UNSIGNED_ATTRS in parameters) { 88 this.unsignedAttrs = pvutils.getParametersValue(parameters, UNSIGNED_ATTRS, SignerInfo.defaultValues(UNSIGNED_ATTRS)); 89 } 90 91 if (parameters.schema) { 92 this.fromSchema(parameters.schema); 93 } 94 } 95 96 /** 97 * Returns default values for all class members 98 * @param memberName String name for a class member 99 * @returns Default value 100 */ 101 public static override defaultValues(memberName: typeof VERSION): number; 102 public static override defaultValues(memberName: typeof SID): Schema.SchemaType; 103 public static override defaultValues(memberName: typeof DIGEST_ALGORITHM): AlgorithmIdentifier; 104 public static override defaultValues(memberName: typeof SIGNED_ATTRS): SignedAndUnsignedAttributes; 105 public static override defaultValues(memberName: typeof SIGNATURE_ALGORITHM): AlgorithmIdentifier; 106 public static override defaultValues(memberName: typeof SIGNATURE): asn1js.OctetString; 107 public static override defaultValues(memberName: typeof UNSIGNED_ATTRS): SignedAndUnsignedAttributes; 108 public static override defaultValues(memberName: string): any { 109 switch (memberName) { 110 case VERSION: 111 return 0; 112 case SID: 113 return new asn1js.Any(); 114 case DIGEST_ALGORITHM: 115 return new AlgorithmIdentifier(); 116 case SIGNED_ATTRS: 117 return new SignedAndUnsignedAttributes({ type: 0 }); 118 case SIGNATURE_ALGORITHM: 119 return new AlgorithmIdentifier(); 120 case SIGNATURE: 121 return new asn1js.OctetString(); 122 case UNSIGNED_ATTRS: 123 return new SignedAndUnsignedAttributes({ type: 1 }); 124 default: 125 return super.defaultValues(memberName); 126 } 127 } 128 129 /** 130 * Compare values with default values for all class members 131 * @param memberName String name for a class member 132 * @param memberValue Value to compare with default value 133 */ 134 public static compareWithDefault(memberName: string, memberValue: any): boolean { 135 switch (memberName) { 136 case VERSION: 137 return (SignerInfo.defaultValues(VERSION) === memberValue); 138 case SID: 139 return (memberValue instanceof asn1js.Any); 140 case DIGEST_ALGORITHM: 141 if ((memberValue instanceof AlgorithmIdentifier) === false) 142 return false; 143 144 return memberValue.isEqual(SignerInfo.defaultValues(DIGEST_ALGORITHM)); 145 case SIGNED_ATTRS: 146 return ((SignedAndUnsignedAttributes.compareWithDefault("type", memberValue.type)) 147 && (SignedAndUnsignedAttributes.compareWithDefault("attributes", memberValue.attributes)) 148 && (SignedAndUnsignedAttributes.compareWithDefault("encodedValue", memberValue.encodedValue))); 149 case SIGNATURE_ALGORITHM: 150 if ((memberValue instanceof AlgorithmIdentifier) === false) 151 return false; 152 153 return memberValue.isEqual(SignerInfo.defaultValues(SIGNATURE_ALGORITHM)); 154 case SIGNATURE: 155 case UNSIGNED_ATTRS: 156 return ((SignedAndUnsignedAttributes.compareWithDefault("type", memberValue.type)) 157 && (SignedAndUnsignedAttributes.compareWithDefault("attributes", memberValue.attributes)) 158 && (SignedAndUnsignedAttributes.compareWithDefault("encodedValue", memberValue.encodedValue))); 159 default: 160 return super.defaultValues(memberName); 161 } 162 } 163 164 /** 165 * @inheritdoc 166 * @asn ASN.1 schema 167 * ```asn 168 * SignerInfo ::= SEQUENCE { 169 * version CMSVersion, 170 * sid SignerIdentifier, 171 * digestAlgorithm DigestAlgorithmIdentifier, 172 * signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL, 173 * signatureAlgorithm SignatureAlgorithmIdentifier, 174 * signature SignatureValue, 175 * unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL } 176 * 177 * SignerIdentifier ::= CHOICE { 178 * issuerAndSerialNumber IssuerAndSerialNumber, 179 * subjectKeyIdentifier [0] SubjectKeyIdentifier } 180 * 181 * SubjectKeyIdentifier ::= OCTET STRING 182 *``` 183 */ 184 public static override schema(parameters: Schema.SchemaParameters<{ 185 version?: string; 186 sidSchema?: IssuerAndSerialNumberSchema; 187 sid?: string; 188 digestAlgorithm?: AlgorithmIdentifierSchema; 189 signedAttrs?: SignedAndUnsignedAttributesSchema; 190 signatureAlgorithm?: AlgorithmIdentifierSchema; 191 signature?: string; 192 unsignedAttrs?: SignedAndUnsignedAttributesSchema; 193 }> = {}): Schema.SchemaType { 194 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 195 196 return ( 197 new asn1js.Sequence({ 198 name: SIGNER_INFO, 199 value: [ 200 new asn1js.Integer({ name: (names.version || SIGNER_INFO_VERSION) }), 201 new asn1js.Choice({ 202 value: [ 203 IssuerAndSerialNumber.schema(names.sidSchema || { 204 names: { 205 blockName: SIGNER_INFO_SID 206 } 207 }), 208 new asn1js.Choice({ 209 value: [ 210 new asn1js.Constructed({ 211 optional: true, 212 name: (names.sid || SIGNER_INFO_SID), 213 idBlock: { 214 tagClass: 3, // CONTEXT-SPECIFIC 215 tagNumber: 0 // [0] 216 }, 217 value: [new asn1js.OctetString()] 218 }), 219 new asn1js.Primitive({ 220 optional: true, 221 name: (names.sid || SIGNER_INFO_SID), 222 idBlock: { 223 tagClass: 3, // CONTEXT-SPECIFIC 224 tagNumber: 0 // [0] 225 } 226 }), 227 ] 228 }), 229 ] 230 }), 231 AlgorithmIdentifier.schema(names.digestAlgorithm || { 232 names: { 233 blockName: SIGNER_INFO_DIGEST_ALGORITHM 234 } 235 }), 236 SignedAndUnsignedAttributes.schema(names.signedAttrs || { 237 names: { 238 blockName: SIGNER_INFO_SIGNED_ATTRS, 239 tagNumber: 0 240 } 241 }), 242 AlgorithmIdentifier.schema(names.signatureAlgorithm || { 243 names: { 244 blockName: SIGNER_INFO_SIGNATURE_ALGORITHM 245 } 246 }), 247 new asn1js.OctetString({ name: (names.signature || SIGNER_INFO_SIGNATURE) }), 248 SignedAndUnsignedAttributes.schema(names.unsignedAttrs || { 249 names: { 250 blockName: SIGNER_INFO_UNSIGNED_ATTRS, 251 tagNumber: 1 252 } 253 }) 254 ] 255 }) 256 ); 257 } 258 259 public fromSchema(schema: Schema.SchemaType): void { 260 // Clear input data first 261 pvutils.clearProps(schema, CLEAR_PROPS); 262 263 // Check the schema is valid 264 const asn1 = asn1js.compareSchema(schema, 265 schema, 266 SignerInfo.schema() 267 ); 268 AsnError.assertSchema(asn1, this.className); 269 270 // Get internal properties from parsed schema 271 this.version = asn1.result[SIGNER_INFO_VERSION].valueBlock.valueDec; 272 273 const currentSid = asn1.result[SIGNER_INFO_SID]; 274 if (currentSid.idBlock.tagClass === 1) 275 this.sid = new IssuerAndSerialNumber({ schema: currentSid }); 276 else 277 this.sid = currentSid; 278 279 this.digestAlgorithm = new AlgorithmIdentifier({ schema: asn1.result[SIGNER_INFO_DIGEST_ALGORITHM] }); 280 if (SIGNER_INFO_SIGNED_ATTRS in asn1.result) 281 this.signedAttrs = new SignedAndUnsignedAttributes({ type: 0, schema: asn1.result[SIGNER_INFO_SIGNED_ATTRS] }); 282 283 this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result[SIGNER_INFO_SIGNATURE_ALGORITHM] }); 284 this.signature = asn1.result[SIGNER_INFO_SIGNATURE]; 285 if (SIGNER_INFO_UNSIGNED_ATTRS in asn1.result) 286 this.unsignedAttrs = new SignedAndUnsignedAttributes({ type: 1, schema: asn1.result[SIGNER_INFO_UNSIGNED_ATTRS] }); 287 } 288 289 public toSchema(): asn1js.Sequence { 290 if (SignerInfo.compareWithDefault(SID, this.sid)) 291 throw new Error("Incorrectly initialized \"SignerInfo\" class"); 292 293 //#region Create array for output sequence 294 const outputArray = []; 295 296 outputArray.push(new asn1js.Integer({ value: this.version })); 297 298 if (this.sid instanceof IssuerAndSerialNumber) 299 outputArray.push(this.sid.toSchema()); 300 else 301 outputArray.push(this.sid); 302 303 outputArray.push(this.digestAlgorithm.toSchema()); 304 305 if (this.signedAttrs) { 306 if (SignerInfo.compareWithDefault(SIGNED_ATTRS, this.signedAttrs) === false) 307 outputArray.push(this.signedAttrs.toSchema()); 308 } 309 310 outputArray.push(this.signatureAlgorithm.toSchema()); 311 outputArray.push(this.signature); 312 313 if (this.unsignedAttrs) { 314 if (SignerInfo.compareWithDefault(UNSIGNED_ATTRS, this.unsignedAttrs) === false) 315 outputArray.push(this.unsignedAttrs.toSchema()); 316 } 317 //#endregion 318 319 //#region Construct and return new ASN.1 schema for this object 320 return (new asn1js.Sequence({ 321 value: outputArray 322 })); 323 //#endregion 324 } 325 326 public toJSON(): SignerInfoJson { 327 if (SignerInfo.compareWithDefault(SID, this.sid)) { 328 throw new Error("Incorrectly initialized \"SignerInfo\" class"); 329 } 330 331 const res: SignerInfoJson = { 332 version: this.version, 333 digestAlgorithm: this.digestAlgorithm.toJSON(), 334 signatureAlgorithm: this.signatureAlgorithm.toJSON(), 335 signature: this.signature.toJSON(), 336 }; 337 338 if (!(this.sid instanceof asn1js.Any)) 339 res.sid = this.sid.toJSON(); 340 341 if (this.signedAttrs && SignerInfo.compareWithDefault(SIGNED_ATTRS, this.signedAttrs) === false) { 342 res.signedAttrs = this.signedAttrs.toJSON(); 343 } 344 345 if (this.unsignedAttrs && SignerInfo.compareWithDefault(UNSIGNED_ATTRS, this.unsignedAttrs) === false) { 346 res.unsignedAttrs = this.unsignedAttrs.toJSON(); 347 } 348 349 return res; 350 } 351 352 }