CertificateSet.ts (7484B)
1 import * as asn1js from "asn1js"; 2 import * as pvutils from "pvutils"; 3 import { Certificate, CertificateJson } from "./Certificate"; 4 import { AttributeCertificateV1, AttributeCertificateV1Json } from "./AttributeCertificateV1"; 5 import { AttributeCertificateV2, AttributeCertificateV2Json } from "./AttributeCertificateV2"; 6 import { OtherCertificateFormat, OtherCertificateFormatJson } from "./OtherCertificateFormat"; 7 import * as Schema from "./Schema"; 8 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 9 import { AsnError } from "./errors"; 10 import { EMPTY_STRING } from "./constants"; 11 12 const CERTIFICATES = "certificates"; 13 const CLEAR_PROPS = [ 14 CERTIFICATES, 15 ]; 16 17 export interface ICertificateSet { 18 certificates: CertificateSetItem[]; 19 } 20 21 export interface CertificateSetJson { 22 certificates: CertificateSetItemJson[]; 23 } 24 25 export type CertificateSetItemJson = CertificateJson | AttributeCertificateV1Json | AttributeCertificateV2Json | OtherCertificateFormatJson; 26 27 export type CertificateSetItem = Certificate | AttributeCertificateV1 | AttributeCertificateV2 | OtherCertificateFormat; 28 29 export type CertificateSetParameters = PkiObjectParameters & Partial<ICertificateSet>; 30 31 /** 32 * Represents the CertificateSet structure described in [RFC5652](https://datatracker.ietf.org/doc/html/rfc5652) 33 */ 34 export class CertificateSet extends PkiObject implements ICertificateSet { 35 36 public static override CLASS_NAME = "CertificateSet"; 37 38 public certificates!: CertificateSetItem[]; 39 40 /** 41 * Initializes a new instance of the {@link CertificateSet} class 42 * @param parameters Initialization parameters 43 */ 44 constructor(parameters: CertificateSetParameters = {}) { 45 super(); 46 47 this.certificates = pvutils.getParametersValue(parameters, CERTIFICATES, CertificateSet.defaultValues(CERTIFICATES)); 48 49 if (parameters.schema) { 50 this.fromSchema(parameters.schema); 51 } 52 } 53 54 /** 55 * Returns default values for all class members 56 * @param memberName String name for a class member 57 * @returns Default value 58 */ 59 public static override defaultValues(memberName: typeof CERTIFICATES): CertificateSetItem[]; 60 public static override defaultValues(memberName: string): any { 61 switch (memberName) { 62 case CERTIFICATES: 63 return []; 64 default: 65 return super.defaultValues(memberName); 66 } 67 } 68 69 /** 70 * @inheritdoc 71 * @asn ASN.1 schema 72 * ```asn 73 * CertificateSet ::= SET OF CertificateChoices 74 * 75 * CertificateChoices ::= CHOICE { 76 * certificate Certificate, 77 * extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete 78 * v1AttrCert [1] IMPLICIT AttributeCertificateV1, -- Obsolete 79 * v2AttrCert [2] IMPLICIT AttributeCertificateV2, 80 * other [3] IMPLICIT OtherCertificateFormat } 81 *``` 82 */ 83 public static override schema(parameters: Schema.SchemaParameters<{ 84 certificates?: string; 85 }> = {}): Schema.SchemaType { 86 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 87 88 return ( 89 new asn1js.Set({ 90 name: (names.blockName || EMPTY_STRING), 91 value: [ 92 new asn1js.Repeated({ 93 name: (names.certificates || CERTIFICATES), 94 value: new asn1js.Choice({ 95 value: [ 96 Certificate.schema(), 97 new asn1js.Constructed({ 98 idBlock: { 99 tagClass: 3, // CONTEXT-SPECIFIC 100 tagNumber: 0 // [0] 101 }, 102 value: [ 103 new asn1js.Any() 104 ] 105 }), // JUST A STUB 106 new asn1js.Constructed({ 107 idBlock: { 108 tagClass: 3, // CONTEXT-SPECIFIC 109 tagNumber: 1 // [1] 110 }, 111 value: [ 112 new asn1js.Sequence 113 ] 114 }), 115 new asn1js.Constructed({ 116 idBlock: { 117 tagClass: 3, // CONTEXT-SPECIFIC 118 tagNumber: 2 // [2] 119 }, 120 value: AttributeCertificateV2.schema().valueBlock.value 121 }), 122 new asn1js.Constructed({ 123 idBlock: { 124 tagClass: 3, // CONTEXT-SPECIFIC 125 tagNumber: 3 // [3] 126 }, 127 value: OtherCertificateFormat.schema().valueBlock.value 128 }) 129 ] 130 }) 131 }) 132 ] 133 }) 134 ); 135 } 136 137 public fromSchema(schema: Schema.SchemaType): void { 138 // Clear input data first 139 pvutils.clearProps(schema, CLEAR_PROPS); 140 141 // Check the schema is valid 142 const asn1 = asn1js.compareSchema(schema, 143 schema, 144 CertificateSet.schema() 145 ); 146 AsnError.assertSchema(asn1, this.className); 147 148 //#region Get internal properties from parsed schema 149 this.certificates = Array.from(asn1.result.certificates || [], (element: any) => { 150 const initialTagNumber = element.idBlock.tagNumber; 151 152 if (element.idBlock.tagClass === 1) 153 return new Certificate({ schema: element }); 154 155 //#region Making "Sequence" from "Constructed" value 156 const elementSequence = new asn1js.Sequence({ 157 value: element.valueBlock.value 158 }); 159 //#endregion 160 161 switch (initialTagNumber) { 162 case 1: 163 // WARN: It's possible that CMS contains AttributeCertificateV2 instead of AttributeCertificateV1 164 // Check the certificate version 165 if ((elementSequence.valueBlock.value[0] as any).valueBlock.value[0].valueBlock.valueDec === 1) { 166 return new AttributeCertificateV2({ schema: elementSequence }); 167 } else { 168 return new AttributeCertificateV1({ schema: elementSequence }); 169 } 170 case 2: 171 return new AttributeCertificateV2({ schema: elementSequence }); 172 case 3: 173 return new OtherCertificateFormat({ schema: elementSequence }); 174 case 0: 175 default: 176 } 177 178 return element; 179 }); 180 //#endregion 181 } 182 183 public toSchema(): asn1js.Set { 184 // Construct and return new ASN.1 schema for this object 185 return (new asn1js.Set({ 186 value: Array.from(this.certificates, element => { 187 switch (true) { 188 case (element instanceof Certificate): 189 return element.toSchema(); 190 case (element instanceof AttributeCertificateV1): 191 return new asn1js.Constructed({ 192 idBlock: { 193 tagClass: 3, 194 tagNumber: 1 // [1] 195 }, 196 value: element.toSchema().valueBlock.value 197 }); 198 case (element instanceof AttributeCertificateV2): 199 return new asn1js.Constructed({ 200 idBlock: { 201 tagClass: 3, 202 tagNumber: 2 // [2] 203 }, 204 value: element.toSchema().valueBlock.value 205 }); 206 case (element instanceof OtherCertificateFormat): 207 return new asn1js.Constructed({ 208 idBlock: { 209 tagClass: 3, 210 tagNumber: 3 // [3] 211 }, 212 value: element.toSchema().valueBlock.value 213 }); 214 default: 215 } 216 217 return (element as any).toSchema(); 218 }) 219 })); 220 } 221 222 public toJSON(): CertificateSetJson { 223 return { 224 certificates: Array.from(this.certificates, o => o.toJSON()) 225 }; 226 } 227 228 }