ContentInfo.ts (5210B)
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 { id_ContentType_Data, id_ContentType_EncryptedData, id_ContentType_EnvelopedData, id_ContentType_SignedData } from "./ObjectIdentifiers"; 6 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 7 import * as Schema from "./Schema"; 8 9 const CONTENT_TYPE = "contentType"; 10 const CONTENT = "content"; 11 const CLEAR_PROPS = [CONTENT_TYPE, CONTENT]; 12 13 export interface IContentInfo { 14 contentType: string; 15 content: any; 16 } 17 18 export type ContentInfoParameters = PkiObjectParameters & Partial<IContentInfo>; 19 20 export type ContentInfoSchema = Schema.SchemaParameters<{ 21 contentType?: string; 22 content?: string; 23 }>; 24 25 export interface ContentInfoJson { 26 contentType: string; 27 content?: any; 28 } 29 30 /** 31 * Represents the ContentInfo structure described in [RFC5652](https://datatracker.ietf.org/doc/html/rfc5652) 32 */ 33 export class ContentInfo extends PkiObject implements IContentInfo { 34 35 public static override CLASS_NAME = "ContentInfo"; 36 public static readonly DATA = id_ContentType_Data; 37 public static readonly SIGNED_DATA = id_ContentType_SignedData; 38 public static readonly ENVELOPED_DATA = id_ContentType_EnvelopedData; 39 public static readonly ENCRYPTED_DATA = id_ContentType_EncryptedData; 40 41 public contentType!: string; 42 public content: any; 43 44 /** 45 * Initializes a new instance of the {@link ContentInfo} class 46 * @param parameters Initialization parameters 47 */ 48 constructor(parameters: ContentInfoParameters = {}) { 49 super(); 50 51 this.contentType = pvutils.getParametersValue(parameters, CONTENT_TYPE, ContentInfo.defaultValues(CONTENT_TYPE)); 52 this.content = pvutils.getParametersValue(parameters, CONTENT, ContentInfo.defaultValues(CONTENT)); 53 54 if (parameters.schema) { 55 this.fromSchema(parameters.schema); 56 } 57 } 58 59 /** 60 * Returns default values for all class members 61 * @param memberName String name for a class member 62 * @returns Default value 63 */ 64 public static override defaultValues(memberName: typeof CONTENT_TYPE): string; 65 public static override defaultValues(memberName: typeof CONTENT): any; 66 public static override defaultValues(memberName: string): any { 67 switch (memberName) { 68 case CONTENT_TYPE: 69 return EMPTY_STRING; 70 case CONTENT: 71 return new asn1js.Any(); 72 default: 73 return super.defaultValues(memberName); 74 } 75 } 76 77 /** 78 * Compare values with default values for all class members 79 * @param memberName String name for a class member 80 * @param memberValue Value to compare with default value 81 */ 82 static compareWithDefault<T>(memberName: string, memberValue: T): memberValue is T { 83 switch (memberName) { 84 case CONTENT_TYPE: 85 return (typeof memberValue === "string" && 86 memberValue === this.defaultValues(CONTENT_TYPE)); 87 case CONTENT: 88 return (memberValue instanceof asn1js.Any); 89 default: 90 return super.defaultValues(memberName); 91 } 92 } 93 94 /** 95 * @inheritdoc 96 * @asn ASN.1 schema 97 * ```asn 98 * ContentInfo ::= SEQUENCE { 99 * contentType ContentType, 100 * content [0] EXPLICIT ANY DEFINED BY contentType } 101 *``` 102 */ 103 public static override schema(parameters: ContentInfoSchema = {}): Schema.SchemaType { 104 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 105 106 if (("optional" in names) === false) { 107 names.optional = false; 108 } 109 110 return (new asn1js.Sequence({ 111 name: (names.blockName || "ContentInfo"), 112 optional: names.optional, 113 value: [ 114 new asn1js.ObjectIdentifier({ name: (names.contentType || CONTENT_TYPE) }), 115 new asn1js.Constructed({ 116 idBlock: { 117 tagClass: 3, // CONTEXT-SPECIFIC 118 tagNumber: 0 // [0] 119 }, 120 value: [new asn1js.Any({ name: (names.content || CONTENT) })] // EXPLICIT ANY value 121 }) 122 ] 123 })); 124 } 125 126 public fromSchema(schema: Schema.SchemaType): void { 127 // Clear input data first 128 pvutils.clearProps(schema, CLEAR_PROPS); 129 130 // Check the schema is valid 131 const asn1 = asn1js.compareSchema(schema, 132 schema, 133 ContentInfo.schema() 134 ); 135 AsnError.assertSchema(asn1, this.className); 136 137 // Get internal properties from parsed schema 138 this.contentType = asn1.result.contentType.valueBlock.toString(); 139 this.content = asn1.result.content; 140 } 141 142 public toSchema(): asn1js.Sequence { 143 //#region Construct and return new ASN.1 schema for this object 144 return (new asn1js.Sequence({ 145 value: [ 146 new asn1js.ObjectIdentifier({ value: this.contentType }), 147 new asn1js.Constructed({ 148 idBlock: { 149 tagClass: 3, // CONTEXT-SPECIFIC 150 tagNumber: 0 // [0] 151 }, 152 value: [this.content] // EXPLICIT ANY value 153 }) 154 ] 155 })); 156 //#endregion 157 } 158 159 public toJSON(): ContentInfoJson { 160 const object: ContentInfoJson = { 161 contentType: this.contentType 162 }; 163 164 if (!(this.content instanceof asn1js.Any)) { 165 object.content = this.content.toJSON(); 166 } 167 168 return object; 169 } 170 171 }