tor-browser

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

TBSRequest.ts (10885B)


      1 import * as asn1js from "asn1js";
      2 import * as pvtsutils from "pvtsutils";
      3 import * as pvutils from "pvutils";
      4 import { GeneralName, GeneralNameJson, GeneralNameSchema } from "./GeneralName";
      5 import { Request, RequestJson, RequestSchema } from "./Request";
      6 import { Extension, ExtensionJson } from "./Extension";
      7 import { Extensions, ExtensionsSchema } from "./Extensions";
      8 import * as Schema from "./Schema";
      9 import { AsnError } from "./errors";
     10 import { PkiObject, PkiObjectParameters } from "./PkiObject";
     11 import { EMPTY_BUFFER } from "./constants";
     12 
     13 const TBS = "tbs";
     14 const VERSION = "version";
     15 const REQUESTOR_NAME = "requestorName";
     16 const REQUEST_LIST = "requestList";
     17 const REQUEST_EXTENSIONS = "requestExtensions";
     18 const TBS_REQUEST = "TBSRequest";
     19 const TBS_REQUEST_VERSION = `${TBS_REQUEST}.${VERSION}`;
     20 const TBS_REQUEST_REQUESTOR_NAME = `${TBS_REQUEST}.${REQUESTOR_NAME}`;
     21 const TBS_REQUEST_REQUESTS = `${TBS_REQUEST}.requests`;
     22 const TBS_REQUEST_REQUEST_EXTENSIONS = `${TBS_REQUEST}.${REQUEST_EXTENSIONS}`;
     23 const CLEAR_PROPS = [
     24  TBS_REQUEST,
     25  TBS_REQUEST_VERSION,
     26  TBS_REQUEST_REQUESTOR_NAME,
     27  TBS_REQUEST_REQUESTS,
     28  TBS_REQUEST_REQUEST_EXTENSIONS
     29 ];
     30 
     31 export interface ITBSRequest {
     32  tbs: ArrayBuffer;
     33  version?: number;
     34  requestorName?: GeneralName;
     35  requestList: Request[];
     36  requestExtensions?: Extension[];
     37 }
     38 
     39 export interface TBSRequestJson {
     40  tbs: string;
     41  version?: number;
     42  requestorName?: GeneralNameJson;
     43  requestList: RequestJson[];
     44  requestExtensions?: ExtensionJson[];
     45 }
     46 
     47 export type TBSRequestParameters = PkiObjectParameters & Partial<ITBSRequest>;
     48 
     49 export type TBSRequestSchema = Schema.SchemaParameters<{
     50  TBSRequestVersion?: string;
     51  requestorName?: GeneralNameSchema;
     52  requestList?: string;
     53  requests?: string;
     54  requestNames?: RequestSchema;
     55  extensions?: ExtensionsSchema;
     56  requestExtensions?: string;
     57 }>;
     58 
     59 /**
     60 * Represents the TBSRequest structure described in [RFC6960](https://datatracker.ietf.org/doc/html/rfc6960)
     61 */
     62 export class TBSRequest extends PkiObject implements ITBSRequest {
     63 
     64  public static override CLASS_NAME = "TBSRequest";
     65 
     66  public tbsView!: Uint8Array;
     67  /**
     68   * @deprecated Since version 3.0.0
     69   */
     70  public get tbs(): ArrayBuffer {
     71    return pvtsutils.BufferSourceConverter.toArrayBuffer(this.tbsView);
     72  }
     73 
     74  /**
     75   * @deprecated Since version 3.0.0
     76   */
     77  public set tbs(value: ArrayBuffer) {
     78    this.tbsView = new Uint8Array(value);
     79  }
     80  public version?: number;
     81  public requestorName?: GeneralName;
     82  public requestList!: Request[];
     83  public requestExtensions?: Extension[];
     84 
     85  /**
     86   * Initializes a new instance of the {@link TBSRequest} class
     87   * @param parameters Initialization parameters
     88   */
     89  constructor(parameters: TBSRequestParameters = {}) {
     90    super();
     91 
     92    this.tbsView = new Uint8Array(pvutils.getParametersValue(parameters, TBS, TBSRequest.defaultValues(TBS)));
     93    if (VERSION in parameters) {
     94      this.version = pvutils.getParametersValue(parameters, VERSION, TBSRequest.defaultValues(VERSION));
     95    }
     96    if (REQUESTOR_NAME in parameters) {
     97      this.requestorName = pvutils.getParametersValue(parameters, REQUESTOR_NAME, TBSRequest.defaultValues(REQUESTOR_NAME));
     98    }
     99    this.requestList = pvutils.getParametersValue(parameters, REQUEST_LIST, TBSRequest.defaultValues(REQUEST_LIST));
    100    if (REQUEST_EXTENSIONS in parameters) {
    101      this.requestExtensions = pvutils.getParametersValue(parameters, REQUEST_EXTENSIONS, TBSRequest.defaultValues(REQUEST_EXTENSIONS));
    102    }
    103 
    104    if (parameters.schema) {
    105      this.fromSchema(parameters.schema);
    106    }
    107  }
    108 
    109  /**
    110   * Returns default values for all class members
    111   * @param memberName String name for a class member
    112   * @returns Default value
    113   */
    114  public static override defaultValues(memberName: typeof TBS): ArrayBuffer;
    115  public static override defaultValues(memberName: typeof VERSION): number;
    116  public static override defaultValues(memberName: typeof REQUESTOR_NAME): GeneralName;
    117  public static override defaultValues(memberName: typeof REQUEST_LIST): Request[];
    118  public static override defaultValues(memberName: typeof REQUEST_EXTENSIONS): Extension[];
    119  public static override defaultValues(memberName: string): any {
    120    switch (memberName) {
    121      case TBS:
    122        return EMPTY_BUFFER;
    123      case VERSION:
    124        return 0;
    125      case REQUESTOR_NAME:
    126        return new GeneralName();
    127      case REQUEST_LIST:
    128      case REQUEST_EXTENSIONS:
    129        return [];
    130      default:
    131        return super.defaultValues(memberName);
    132    }
    133  }
    134 
    135  /**
    136   * Compare values with default values for all class members
    137   * @param memberName String name for a class member
    138   * @param memberValue Value to compare with default value
    139   */
    140  public static compareWithDefault(memberName: string, memberValue: any): boolean {
    141    switch (memberName) {
    142      case TBS:
    143        return (memberValue.byteLength === 0);
    144      case VERSION:
    145        return (memberValue === TBSRequest.defaultValues(memberName));
    146      case REQUESTOR_NAME:
    147        return ((memberValue.type === GeneralName.defaultValues("type")) && (Object.keys(memberValue.value).length === 0));
    148      case REQUEST_LIST:
    149      case REQUEST_EXTENSIONS:
    150        return (memberValue.length === 0);
    151      default:
    152        return super.defaultValues(memberName);
    153    }
    154  }
    155 
    156  /**
    157   * @inheritdoc
    158   * @asn ASN.1 schema
    159   * ```asn
    160   * TBSRequest ::= SEQUENCE {
    161   *    version             [0]     EXPLICIT Version DEFAULT v1,
    162   *    requestorName       [1]     EXPLICIT GeneralName OPTIONAL,
    163   *    requestList                 SEQUENCE OF Request,
    164   *    requestExtensions   [2]     EXPLICIT Extensions OPTIONAL }
    165   *```
    166   */
    167  public static override schema(parameters: TBSRequestSchema = {}): Schema.SchemaType {
    168    const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
    169 
    170    return (new asn1js.Sequence({
    171      name: (names.blockName || TBS_REQUEST),
    172      value: [
    173        new asn1js.Constructed({
    174          optional: true,
    175          idBlock: {
    176            tagClass: 3, // CONTEXT-SPECIFIC
    177            tagNumber: 0 // [0]
    178          },
    179          value: [new asn1js.Integer({ name: (names.TBSRequestVersion || TBS_REQUEST_VERSION) })]
    180        }),
    181        new asn1js.Constructed({
    182          optional: true,
    183          idBlock: {
    184            tagClass: 3, // CONTEXT-SPECIFIC
    185            tagNumber: 1 // [1]
    186          },
    187          value: [GeneralName.schema(names.requestorName || {
    188            names: {
    189              blockName: TBS_REQUEST_REQUESTOR_NAME
    190            }
    191          })]
    192        }),
    193        new asn1js.Sequence({
    194          name: (names.requestList || "TBSRequest.requestList"),
    195          value: [
    196            new asn1js.Repeated({
    197              name: (names.requests || TBS_REQUEST_REQUESTS),
    198              value: Request.schema(names.requestNames || {})
    199            })
    200          ]
    201        }),
    202        new asn1js.Constructed({
    203          optional: true,
    204          idBlock: {
    205            tagClass: 3, // CONTEXT-SPECIFIC
    206            tagNumber: 2 // [2]
    207          },
    208          value: [Extensions.schema(names.extensions || {
    209            names: {
    210              blockName: (names.requestExtensions || TBS_REQUEST_REQUEST_EXTENSIONS)
    211            }
    212          })]
    213        })
    214      ]
    215    }));
    216  }
    217 
    218  public fromSchema(schema: Schema.SchemaType): void {
    219    // Clear input data first
    220    pvutils.clearProps(schema, CLEAR_PROPS);
    221 
    222    // Check the schema is valid
    223    const asn1 = asn1js.compareSchema(schema,
    224      schema,
    225      TBSRequest.schema()
    226    );
    227    AsnError.assertSchema(asn1, this.className);
    228 
    229    // Get internal properties from parsed schema
    230    this.tbsView = asn1.result.TBSRequest.valueBeforeDecodeView;
    231 
    232    if (TBS_REQUEST_VERSION in asn1.result)
    233      this.version = asn1.result[TBS_REQUEST_VERSION].valueBlock.valueDec;
    234    if (TBS_REQUEST_REQUESTOR_NAME in asn1.result)
    235      this.requestorName = new GeneralName({ schema: asn1.result[TBS_REQUEST_REQUESTOR_NAME] });
    236 
    237    this.requestList = Array.from(asn1.result[TBS_REQUEST_REQUESTS], element => new Request({ schema: element }));
    238 
    239    if (TBS_REQUEST_REQUEST_EXTENSIONS in asn1.result)
    240      this.requestExtensions = Array.from(asn1.result[TBS_REQUEST_REQUEST_EXTENSIONS].valueBlock.value, element => new Extension({ schema: element }));
    241  }
    242 
    243  /**
    244   * Convert current object to asn1js object and set correct values
    245   * @param encodeFlag If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts.
    246   * @returns asn1js object
    247   */
    248  public toSchema(encodeFlag = false): asn1js.Sequence {
    249    //#region Decode stored TBS value
    250    let tbsSchema;
    251 
    252    if (encodeFlag === false) {
    253      if (this.tbsView.byteLength === 0) // No stored TBS part
    254        return TBSRequest.schema();
    255 
    256      const asn1 = asn1js.fromBER(this.tbsView);
    257      AsnError.assert(asn1, "TBS Request");
    258      if (!(asn1.result instanceof asn1js.Sequence)) {
    259        throw new Error("ASN.1 result should be SEQUENCE");
    260      }
    261 
    262      tbsSchema = asn1.result;
    263    }
    264    //#endregion
    265    //#region Create TBS schema via assembling from TBS parts
    266    else {
    267      const outputArray = [];
    268 
    269      if (this.version !== undefined) {
    270        outputArray.push(new asn1js.Constructed({
    271          idBlock: {
    272            tagClass: 3, // CONTEXT-SPECIFIC
    273            tagNumber: 0 // [0]
    274          },
    275          value: [new asn1js.Integer({ value: this.version })]
    276        }));
    277      }
    278 
    279      if (this.requestorName) {
    280        outputArray.push(new asn1js.Constructed({
    281          idBlock: {
    282            tagClass: 3, // CONTEXT-SPECIFIC
    283            tagNumber: 1 // [1]
    284          },
    285          value: [this.requestorName.toSchema()]
    286        }));
    287      }
    288 
    289      outputArray.push(new asn1js.Sequence({
    290        value: Array.from(this.requestList, o => o.toSchema())
    291      }));
    292 
    293      if (this.requestExtensions) {
    294        outputArray.push(new asn1js.Constructed({
    295          idBlock: {
    296            tagClass: 3, // CONTEXT-SPECIFIC
    297            tagNumber: 2 // [2]
    298          },
    299          value: [
    300            new asn1js.Sequence({
    301              value: Array.from(this.requestExtensions, o => o.toSchema())
    302            })
    303          ]
    304        }));
    305      }
    306 
    307      tbsSchema = new asn1js.Sequence({
    308        value: outputArray
    309      });
    310    }
    311    //#endregion
    312 
    313    //#region Construct and return new ASN.1 schema for this object
    314    return tbsSchema;
    315    //#endregion
    316  }
    317 
    318  public toJSON(): TBSRequestJson {
    319    const res: any = {};
    320 
    321    if (this.version != undefined)
    322      res.version = this.version;
    323 
    324    if (this.requestorName) {
    325      res.requestorName = this.requestorName.toJSON();
    326    }
    327 
    328    res.requestList = Array.from(this.requestList, o => o.toJSON());
    329 
    330    if (this.requestExtensions) {
    331      res.requestExtensions = Array.from(this.requestExtensions, o => o.toJSON());
    332    }
    333 
    334    return res;
    335  }
    336 
    337 }