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