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