RecipientInfo.ts (7402B)
1 import * as asn1js from "asn1js"; 2 import * as pvutils from "pvutils"; 3 import { KeyTransRecipientInfo, KeyTransRecipientInfoJson } from "./KeyTransRecipientInfo"; 4 import { KeyAgreeRecipientInfo, KeyAgreeRecipientInfoJson } from "./KeyAgreeRecipientInfo"; 5 import { KEKRecipientInfo, KEKRecipientInfoJson } from "./KEKRecipientInfo"; 6 import { PasswordRecipientinfo, PasswordRecipientInfoJson } from "./PasswordRecipientinfo"; 7 import { OtherRecipientInfo, OtherRecipientInfoJson } from "./OtherRecipientInfo"; 8 import * as Schema from "./Schema"; 9 import { AsnError, ParameterError } from "./errors"; 10 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 11 import { EMPTY_STRING } from "./constants"; 12 13 const VARIANT = "variant"; 14 const VALUE = "value"; 15 const CLEAR_PROPS = [ 16 "blockName" 17 ]; 18 19 export interface IRecipientInfo { 20 variant: number; 21 value?: RecipientInfoValue; 22 } 23 24 export interface RecipientInfoJson { 25 variant: number; 26 value?: RecipientInfoValueJson; 27 } 28 29 export type RecipientInfoValue = KeyTransRecipientInfo | KeyAgreeRecipientInfo | KEKRecipientInfo | PasswordRecipientinfo | OtherRecipientInfo; 30 export type RecipientInfoValueJson = KeyTransRecipientInfoJson | KeyAgreeRecipientInfoJson | KEKRecipientInfoJson | PasswordRecipientInfoJson | OtherRecipientInfoJson; 31 32 export type RecipientInfoParameters = PkiObjectParameters & Partial<IRecipientInfo>; 33 34 /** 35 * Represents the RecipientInfo structure described in [RFC5652](https://datatracker.ietf.org/doc/html/rfc5652) 36 */ 37 export class RecipientInfo extends PkiObject implements IRecipientInfo { 38 39 public static override CLASS_NAME = "RecipientInfo"; 40 41 public variant!: number; 42 public value?: RecipientInfoValue; 43 44 /** 45 * Initializes a new instance of the {@link RecipientInfo} class 46 * @param parameters Initialization parameters 47 */ 48 constructor(parameters: RecipientInfoParameters = {}) { 49 super(); 50 51 this.variant = pvutils.getParametersValue(parameters, VARIANT, RecipientInfo.defaultValues(VARIANT)); 52 if (VALUE in parameters) { 53 this.value = pvutils.getParametersValue(parameters, VALUE, RecipientInfo.defaultValues(VALUE)); 54 } 55 56 if (parameters.schema) { 57 this.fromSchema(parameters.schema); 58 } 59 } 60 61 /** 62 * Returns default values for all class members 63 * @param memberName String name for a class member 64 * @returns Default value 65 */ 66 public static override defaultValues(memberName: typeof VARIANT): number; 67 public static override defaultValues(memberName: typeof VALUE): RecipientInfoValue; 68 public static override defaultValues(memberName: string): any { 69 switch (memberName) { 70 case VARIANT: 71 return (-1); 72 case VALUE: 73 return {}; 74 default: 75 return super.defaultValues(memberName); 76 } 77 } 78 79 /** 80 * Compare values with default values for all class members 81 * @param memberName String name for a class member 82 * @param memberValue Value to compare with default value 83 */ 84 public static compareWithDefault(memberName: string, memberValue: any): boolean { 85 switch (memberName) { 86 case VARIANT: 87 return (memberValue === RecipientInfo.defaultValues(memberName)); 88 case VALUE: 89 return (Object.keys(memberValue).length === 0); 90 default: 91 return super.defaultValues(memberName); 92 } 93 } 94 95 /** 96 * @inheritdoc 97 * @asn ASN.1 schema 98 * ```asn 99 * RecipientInfo ::= CHOICE { 100 * ktri KeyTransRecipientInfo, 101 * kari [1] KeyAgreeRecipientInfo, 102 * kekri [2] KEKRecipientInfo, 103 * pwri [3] PasswordRecipientinfo, 104 * ori [4] OtherRecipientInfo } 105 *``` 106 */ 107 public static override schema(parameters: Schema.SchemaParameters = {}): Schema.SchemaType { 108 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 109 110 return (new asn1js.Choice({ 111 value: [ 112 KeyTransRecipientInfo.schema({ 113 names: { 114 blockName: (names.blockName || EMPTY_STRING) 115 } 116 }), 117 new asn1js.Constructed({ 118 name: (names.blockName || EMPTY_STRING), 119 idBlock: { 120 tagClass: 3, // CONTEXT-SPECIFIC 121 tagNumber: 1 // [1] 122 }, 123 value: KeyAgreeRecipientInfo.schema().valueBlock.value 124 }), 125 new asn1js.Constructed({ 126 name: (names.blockName || EMPTY_STRING), 127 idBlock: { 128 tagClass: 3, // CONTEXT-SPECIFIC 129 tagNumber: 2 // [2] 130 }, 131 value: KEKRecipientInfo.schema().valueBlock.value 132 }), 133 new asn1js.Constructed({ 134 name: (names.blockName || EMPTY_STRING), 135 idBlock: { 136 tagClass: 3, // CONTEXT-SPECIFIC 137 tagNumber: 3 // [3] 138 }, 139 value: PasswordRecipientinfo.schema().valueBlock.value 140 }), 141 new asn1js.Constructed({ 142 name: (names.blockName || EMPTY_STRING), 143 idBlock: { 144 tagClass: 3, // CONTEXT-SPECIFIC 145 tagNumber: 4 // [4] 146 }, 147 value: OtherRecipientInfo.schema().valueBlock.value 148 }) 149 ] 150 })); 151 } 152 153 public fromSchema(schema: Schema.SchemaType): void { 154 // Clear input data first 155 pvutils.clearProps(schema, CLEAR_PROPS); 156 157 // Check the schema is valid 158 const asn1 = asn1js.compareSchema(schema, 159 schema, 160 RecipientInfo.schema({ 161 names: { 162 blockName: "blockName" 163 } 164 }) 165 ); 166 AsnError.assertSchema(asn1, this.className); 167 168 // Get internal properties from parsed schema 169 if (asn1.result.blockName.idBlock.tagClass === 1) { 170 this.variant = 1; 171 this.value = new KeyTransRecipientInfo({ schema: asn1.result.blockName }); 172 } else { 173 // Create "SEQUENCE" from "ASN1_CONSTRUCTED" 174 const blockSequence = new asn1js.Sequence({ 175 value: asn1.result.blockName.valueBlock.value 176 }); 177 178 switch (asn1.result.blockName.idBlock.tagNumber) { 179 case 1: 180 this.variant = 2; 181 this.value = new KeyAgreeRecipientInfo({ schema: blockSequence }); 182 break; 183 case 2: 184 this.variant = 3; 185 this.value = new KEKRecipientInfo({ schema: blockSequence }); 186 break; 187 case 3: 188 this.variant = 4; 189 this.value = new PasswordRecipientinfo({ schema: blockSequence }); 190 break; 191 case 4: 192 this.variant = 5; 193 this.value = new OtherRecipientInfo({ schema: blockSequence }); 194 break; 195 default: 196 throw new Error("Incorrect structure of RecipientInfo block"); 197 } 198 } 199 } 200 201 public toSchema(): asn1js.BaseBlock<any> { 202 // Construct and return new ASN.1 schema for this object 203 ParameterError.assertEmpty(this.value, "value", "RecipientInfo"); 204 const _schema = this.value.toSchema(); 205 206 switch (this.variant) { 207 case 1: 208 return _schema; 209 case 2: 210 case 3: 211 case 4: 212 // Create "ASN1_CONSTRUCTED" from "SEQUENCE" 213 _schema.idBlock.tagClass = 3; // CONTEXT-SPECIFIC 214 _schema.idBlock.tagNumber = (this.variant - 1); 215 216 return _schema; 217 default: 218 return new asn1js.Any() as any; 219 } 220 } 221 222 public toJSON(): RecipientInfoJson { 223 const res: RecipientInfoJson = { 224 variant: this.variant 225 }; 226 227 if (this.value && (this.variant >= 1) && (this.variant <= 4)) { 228 res.value = this.value.toJSON(); 229 } 230 231 return res; 232 } 233 234 }