Signature.ts (6654B)
1 import * as asn1js from "asn1js"; 2 import * as pvutils from "pvutils"; 3 import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier"; 4 import { Certificate, CertificateJson } from "./Certificate"; 5 import { EMPTY_STRING } from "./constants"; 6 import { AsnError } from "./errors"; 7 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 8 import * as Schema from "./Schema"; 9 10 const SIGNATURE_ALGORITHM = "signatureAlgorithm"; 11 const SIGNATURE = "signature"; 12 const CERTS = "certs"; 13 14 export interface ISignature { 15 signatureAlgorithm: AlgorithmIdentifier; 16 signature: asn1js.BitString; 17 certs?: Certificate[]; 18 } 19 20 export interface SignatureJson { 21 signatureAlgorithm: AlgorithmIdentifierJson; 22 signature: asn1js.BitStringJson; 23 certs?: CertificateJson[]; 24 } 25 26 export type SignatureParameters = PkiObjectParameters & Partial<ISignature>; 27 28 export type SignatureSchema = Schema.SchemaParameters<{ 29 signatureAlgorithm?: AlgorithmIdentifierSchema; 30 signature?: string; 31 certs?: string; 32 }>; 33 34 /** 35 * Represents the Signature structure described in [RFC6960](https://datatracker.ietf.org/doc/html/rfc6960) 36 */ 37 export class Signature extends PkiObject implements ISignature { 38 39 public static override CLASS_NAME = "Signature"; 40 41 public signatureAlgorithm!: AlgorithmIdentifier; 42 public signature!: asn1js.BitString; 43 public certs?: Certificate[]; 44 45 /** 46 * Initializes a new instance of the {@link Signature} class 47 * @param parameters Initialization parameters 48 */ 49 constructor(parameters: SignatureParameters = {}) { 50 super(); 51 52 this.signatureAlgorithm = pvutils.getParametersValue(parameters, SIGNATURE_ALGORITHM, Signature.defaultValues(SIGNATURE_ALGORITHM)); 53 this.signature = pvutils.getParametersValue(parameters, SIGNATURE, Signature.defaultValues(SIGNATURE)); 54 if (CERTS in parameters) { 55 this.certs = pvutils.getParametersValue(parameters, CERTS, Signature.defaultValues(CERTS)); 56 } 57 if (parameters.schema) { 58 this.fromSchema(parameters.schema); 59 } 60 } 61 62 /** 63 * Returns default values for all class members 64 * @param memberName String name for a class member 65 * @returns Default value 66 */ 67 public static override defaultValues(memberName: typeof SIGNATURE_ALGORITHM): AlgorithmIdentifier; 68 public static override defaultValues(memberName: typeof SIGNATURE): asn1js.BitString; 69 public static override defaultValues(memberName: typeof CERTS): Certificate[]; 70 public static override defaultValues(memberName: string): any { 71 switch (memberName) { 72 case SIGNATURE_ALGORITHM: 73 return new AlgorithmIdentifier(); 74 case SIGNATURE: 75 return new asn1js.BitString(); 76 case CERTS: 77 return []; 78 default: 79 return super.defaultValues(memberName); 80 } 81 } 82 83 /** 84 * Compare values with default values for all class members 85 * @param memberName String name for a class member 86 * @param memberValue Value to compare with default value 87 */ 88 public static compareWithDefault(memberName: string, memberValue: any): boolean { 89 switch (memberName) { 90 case SIGNATURE_ALGORITHM: 91 return ((memberValue.algorithmId === EMPTY_STRING) && (("algorithmParams" in memberValue) === false)); 92 case SIGNATURE: 93 return (memberValue.isEqual(Signature.defaultValues(memberName))); 94 case CERTS: 95 return (memberValue.length === 0); 96 default: 97 return super.defaultValues(memberName); 98 } 99 } 100 101 /** 102 * @inheritdoc 103 * @asn ASN.1 schema 104 * ```asn 105 * Signature ::= SEQUENCE { 106 * signatureAlgorithm AlgorithmIdentifier, 107 * signature BIT STRING, 108 * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } 109 *``` 110 */ 111 public static override schema(parameters: SignatureSchema = {}): Schema.SchemaType { 112 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 113 114 return (new asn1js.Sequence({ 115 name: (names.blockName || EMPTY_STRING), 116 value: [ 117 AlgorithmIdentifier.schema(names.signatureAlgorithm || {}), 118 new asn1js.BitString({ name: (names.signature || EMPTY_STRING) }), 119 new asn1js.Constructed({ 120 optional: true, 121 idBlock: { 122 tagClass: 3, // CONTEXT-SPECIFIC 123 tagNumber: 0 // [0] 124 }, 125 value: [ 126 new asn1js.Sequence({ 127 value: [new asn1js.Repeated({ 128 name: (names.certs || EMPTY_STRING), 129 // TODO Double check 130 // value: Certificate.schema(names.certs || {}) 131 value: Certificate.schema({}) 132 })] 133 }) 134 ] 135 }) 136 ] 137 })); 138 } 139 140 public fromSchema(schema: Schema.SchemaType): void { 141 // Clear input data first 142 pvutils.clearProps(schema, [ 143 SIGNATURE_ALGORITHM, 144 SIGNATURE, 145 CERTS 146 ]); 147 148 // Check the schema is valid 149 const asn1 = asn1js.compareSchema(schema, 150 schema, 151 Signature.schema({ 152 names: { 153 signatureAlgorithm: { 154 names: { 155 blockName: SIGNATURE_ALGORITHM 156 } 157 }, 158 signature: SIGNATURE, 159 certs: CERTS 160 } 161 }) 162 ); 163 AsnError.assertSchema(asn1, this.className); 164 165 // Get internal properties from parsed schema 166 this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.signatureAlgorithm }); 167 this.signature = asn1.result.signature; 168 if (CERTS in asn1.result) 169 this.certs = Array.from(asn1.result.certs, element => new Certificate({ schema: element })); 170 } 171 172 public toSchema(): asn1js.Sequence { 173 //#region Create array of output sequence 174 const outputArray = []; 175 176 outputArray.push(this.signatureAlgorithm.toSchema()); 177 outputArray.push(this.signature); 178 179 if (this.certs) { 180 outputArray.push(new asn1js.Constructed({ 181 optional: true, 182 idBlock: { 183 tagClass: 3, // CONTEXT-SPECIFIC 184 tagNumber: 0 // [0] 185 }, 186 value: [ 187 new asn1js.Sequence({ 188 value: Array.from(this.certs, o => o.toSchema()) 189 }) 190 ] 191 })); 192 } 193 //#endregion 194 195 //#region Construct and return new ASN.1 schema for this object 196 return (new asn1js.Sequence({ 197 value: outputArray 198 })); 199 //#endregion 200 } 201 202 public toJSON(): SignatureJson { 203 const res: SignatureJson = { 204 signatureAlgorithm: this.signatureAlgorithm.toJSON(), 205 signature: this.signature.toJSON(), 206 }; 207 208 if (this.certs) { 209 res.certs = Array.from(this.certs, o => o.toJSON()); 210 } 211 212 return res; 213 } 214 215 }