tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }