Extension.ts (6125B)
1 import * as asn1js from "asn1js"; 2 import * as pvutils from "pvutils"; 3 import * as Schema from "./Schema"; 4 import { ExtensionParsedValue, ExtensionValueFactory } from "./ExtensionValueFactory"; 5 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 6 import { AsnError } from "./errors"; 7 import { EMPTY_STRING } from "./constants"; 8 9 const EXTN_ID = "extnID"; 10 const CRITICAL = "critical"; 11 const EXTN_VALUE = "extnValue"; 12 const PARSED_VALUE = "parsedValue"; 13 const CLEAR_PROPS = [ 14 EXTN_ID, 15 CRITICAL, 16 EXTN_VALUE 17 ]; 18 19 export interface IExtension { 20 extnID: string; 21 critical: boolean; 22 extnValue: asn1js.OctetString; 23 parsedValue?: ExtensionParsedValue; 24 } 25 26 export interface ExtensionConstructorParameters { 27 extnID?: string; 28 critical?: boolean; 29 extnValue?: ArrayBuffer; 30 parsedValue?: ExtensionParsedValue; 31 } 32 33 export type ExtensionParameters = PkiObjectParameters & ExtensionConstructorParameters; 34 35 export type ExtensionSchema = Schema.SchemaParameters<{ 36 extnID?: string; 37 critical?: string; 38 extnValue?: string; 39 }>; 40 41 export interface ExtensionJson { 42 extnID: string; 43 extnValue: asn1js.OctetStringJson; 44 critical?: boolean; 45 parsedValue?: any; 46 } 47 48 /** 49 * Represents the Extension structure described in [RFC5280](https://datatracker.ietf.org/doc/html/rfc5280) 50 */ 51 export class Extension extends PkiObject implements IExtension { 52 53 public static override CLASS_NAME = "Extension"; 54 55 public extnID!: string; 56 public critical!: boolean; 57 public extnValue!: asn1js.OctetString; 58 59 private _parsedValue?: ExtensionParsedValue | null; 60 public get parsedValue(): ExtensionParsedValue | undefined { 61 if (this._parsedValue === undefined) { 62 // Get PARSED_VALUE for well-known extensions 63 const parsedValue = ExtensionValueFactory.fromBER(this.extnID, this.extnValue.valueBlock.valueHexView as BufferSource); 64 this._parsedValue = parsedValue; 65 } 66 67 return this._parsedValue || undefined; 68 } 69 public set parsedValue(value: ExtensionParsedValue | undefined) { 70 this._parsedValue = value; 71 } 72 73 /** 74 * Initializes a new instance of the {@link Extension} class 75 * @param parameters Initialization parameters 76 */ 77 constructor(parameters: ExtensionParameters = {}) { 78 super(); 79 80 this.extnID = pvutils.getParametersValue(parameters, EXTN_ID, Extension.defaultValues(EXTN_ID)); 81 this.critical = pvutils.getParametersValue(parameters, CRITICAL, Extension.defaultValues(CRITICAL)); 82 if (EXTN_VALUE in parameters) { 83 this.extnValue = new asn1js.OctetString({ valueHex: parameters.extnValue }); 84 } else { 85 this.extnValue = Extension.defaultValues(EXTN_VALUE); 86 } 87 if (PARSED_VALUE in parameters) { 88 this.parsedValue = pvutils.getParametersValue(parameters, PARSED_VALUE, Extension.defaultValues(PARSED_VALUE)); 89 } 90 91 if (parameters.schema) { 92 this.fromSchema(parameters.schema); 93 } 94 } 95 96 /** 97 * Returns default values for all class members 98 * @param memberName String name for a class member 99 * @returns Default value 100 */ 101 public static override defaultValues(memberName: typeof EXTN_ID): string; 102 public static override defaultValues(memberName: typeof CRITICAL): boolean; 103 public static override defaultValues(memberName: typeof EXTN_VALUE): asn1js.OctetString; 104 public static override defaultValues(memberName: typeof PARSED_VALUE): ExtensionParsedValue; 105 public static override defaultValues(memberName: string): any { 106 switch (memberName) { 107 case EXTN_ID: 108 return EMPTY_STRING; 109 case CRITICAL: 110 return false; 111 case EXTN_VALUE: 112 return new asn1js.OctetString(); 113 case PARSED_VALUE: 114 return {}; 115 default: 116 return super.defaultValues(memberName); 117 } 118 } 119 120 /** 121 * @inheritdoc 122 * @asn ASN.1 schema 123 * ```asn 124 * Extension ::= SEQUENCE { 125 * extnID OBJECT IDENTIFIER, 126 * critical BOOLEAN DEFAULT FALSE, 127 * extnValue OCTET STRING 128 * } 129 *``` 130 */ 131 public static override schema(parameters: ExtensionSchema = {}): Schema.SchemaType { 132 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 133 134 return (new asn1js.Sequence({ 135 name: (names.blockName || EMPTY_STRING), 136 value: [ 137 new asn1js.ObjectIdentifier({ name: (names.extnID || EMPTY_STRING) }), 138 new asn1js.Boolean({ 139 name: (names.critical || EMPTY_STRING), 140 optional: true 141 }), 142 new asn1js.OctetString({ name: (names.extnValue || EMPTY_STRING) }) 143 ] 144 })); 145 } 146 147 public fromSchema(schema: Schema.SchemaType): void { 148 // Clear input data first 149 pvutils.clearProps(schema, CLEAR_PROPS); 150 151 // Check the schema is valid 152 const asn1 = asn1js.compareSchema(schema, 153 schema, 154 Extension.schema({ 155 names: { 156 extnID: EXTN_ID, 157 critical: CRITICAL, 158 extnValue: EXTN_VALUE 159 } 160 }) 161 ); 162 AsnError.assertSchema(asn1, this.className); 163 164 // Get internal properties from parsed schema 165 this.extnID = asn1.result.extnID.valueBlock.toString(); 166 if (CRITICAL in asn1.result) { 167 this.critical = asn1.result.critical.valueBlock.value; 168 } 169 this.extnValue = asn1.result.extnValue; 170 } 171 172 public toSchema(): asn1js.Sequence { 173 // Create array for output sequence 174 const outputArray = []; 175 176 outputArray.push(new asn1js.ObjectIdentifier({ value: this.extnID })); 177 178 if (this.critical !== Extension.defaultValues(CRITICAL)) { 179 outputArray.push(new asn1js.Boolean({ value: this.critical })); 180 } 181 182 outputArray.push(this.extnValue); 183 184 // Construct and return new ASN.1 schema for this object 185 return (new asn1js.Sequence({ 186 value: outputArray 187 })); 188 } 189 190 public toJSON(): ExtensionJson { 191 const object: ExtensionJson = { 192 extnID: this.extnID, 193 extnValue: this.extnValue.toJSON(), 194 }; 195 196 if (this.critical !== Extension.defaultValues(CRITICAL)) { 197 object.critical = this.critical; 198 } 199 if (this.parsedValue && this.parsedValue.toJSON) { 200 object.parsedValue = this.parsedValue.toJSON(); 201 } 202 203 return object; 204 } 205 206 }