tor-browser

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

SingleResponse.ts (10068B)


      1 import * as asn1js from "asn1js";
      2 import * as pvutils from "pvutils";
      3 import { CertID, CertIDJson, CertIDSchema } from "./CertID";
      4 import { EMPTY_STRING } from "./constants";
      5 import { AsnError } from "./errors";
      6 import { Extension, ExtensionJson } from "./Extension";
      7 import { Extensions, ExtensionsSchema } from "./Extensions";
      8 import { PkiObject, PkiObjectParameters } from "./PkiObject";
      9 import * as Schema from "./Schema";
     10 
     11 const CERT_ID = "certID";
     12 const CERT_STATUS = "certStatus";
     13 const THIS_UPDATE = "thisUpdate";
     14 const NEXT_UPDATE = "nextUpdate";
     15 const SINGLE_EXTENSIONS = "singleExtensions";
     16 const CLEAR_PROPS = [
     17  CERT_ID,
     18  CERT_STATUS,
     19  THIS_UPDATE,
     20  NEXT_UPDATE,
     21  SINGLE_EXTENSIONS,
     22 ];
     23 
     24 export interface ISingleResponse {
     25  certID: CertID;
     26  certStatus: any;
     27  thisUpdate: Date;
     28  nextUpdate?: Date;
     29  singleExtensions?: Extension[];
     30 }
     31 
     32 export type SingleResponseParameters = PkiObjectParameters & Partial<ISingleResponse>;
     33 
     34 export type SingleResponseSchema = Schema.SchemaParameters<{
     35  certID?: CertIDSchema;
     36  certStatus?: string;
     37  thisUpdate?: string;
     38  nextUpdate?: string;
     39  singleExtensions?: ExtensionsSchema;
     40 }>;
     41 
     42 export interface SingleResponseJson {
     43  certID: CertIDJson;
     44  certStatus: any;
     45  thisUpdate: Date;
     46  nextUpdate?: Date;
     47  singleExtensions?: ExtensionJson[];
     48 }
     49 
     50 /**
     51 * Represents an SingleResponse described in [RFC6960](https://datatracker.ietf.org/doc/html/rfc6960)
     52 */
     53 export class SingleResponse extends PkiObject implements ISingleResponse {
     54 
     55  public static override CLASS_NAME = "SingleResponse";
     56 
     57  certID!: CertID;
     58  certStatus: any;
     59  thisUpdate!: Date;
     60  nextUpdate?: Date;
     61  singleExtensions?: Extension[];
     62 
     63  /**
     64   * Initializes a new instance of the {@link SingleResponse} class
     65   * @param parameters Initialization parameters
     66   */
     67  constructor(parameters: SingleResponseParameters = {}) {
     68    super();
     69 
     70    this.certID = pvutils.getParametersValue(parameters, CERT_ID, SingleResponse.defaultValues(CERT_ID));
     71    this.certStatus = pvutils.getParametersValue(parameters, CERT_STATUS, SingleResponse.defaultValues(CERT_STATUS));
     72    this.thisUpdate = pvutils.getParametersValue(parameters, THIS_UPDATE, SingleResponse.defaultValues(THIS_UPDATE));
     73    if (NEXT_UPDATE in parameters) {
     74      this.nextUpdate = pvutils.getParametersValue(parameters, NEXT_UPDATE, SingleResponse.defaultValues(NEXT_UPDATE));
     75    }
     76    if (SINGLE_EXTENSIONS in parameters) {
     77      this.singleExtensions = pvutils.getParametersValue(parameters, SINGLE_EXTENSIONS, SingleResponse.defaultValues(SINGLE_EXTENSIONS));
     78    }
     79 
     80    if (parameters.schema) {
     81      this.fromSchema(parameters.schema);
     82    }
     83  }
     84 
     85  /**
     86   * Returns default values for all class members
     87   * @param memberName String name for a class member
     88   * @returns Default value
     89   */
     90  public static override defaultValues(memberName: typeof CERT_ID): CertID;
     91  public static override defaultValues(memberName: typeof CERT_STATUS): any;
     92  public static override defaultValues(memberName: typeof THIS_UPDATE): Date;
     93  public static override defaultValues(memberName: typeof NEXT_UPDATE): Date;
     94  public static override defaultValues(memberName: typeof SINGLE_EXTENSIONS): Extension[];
     95  public static override defaultValues(memberName: string): any {
     96    switch (memberName) {
     97      case CERT_ID:
     98        return new CertID();
     99      case CERT_STATUS:
    100        return {};
    101      case THIS_UPDATE:
    102      case NEXT_UPDATE:
    103        return new Date(0, 0, 0);
    104      case SINGLE_EXTENSIONS:
    105        return [];
    106      default:
    107        return super.defaultValues(memberName);
    108    }
    109  }
    110 
    111  /**
    112   * Compare values with default values for all class members
    113   * @param memberName String name for a class member
    114   * @param memberValue Value to compare with default value
    115   */
    116  public static compareWithDefault(memberName: string, memberValue: any): boolean {
    117    switch (memberName) {
    118      case CERT_ID:
    119        return ((CertID.compareWithDefault("hashAlgorithm", memberValue.hashAlgorithm)) &&
    120          (CertID.compareWithDefault("issuerNameHash", memberValue.issuerNameHash)) &&
    121          (CertID.compareWithDefault("issuerKeyHash", memberValue.issuerKeyHash)) &&
    122          (CertID.compareWithDefault("serialNumber", memberValue.serialNumber)));
    123      case CERT_STATUS:
    124        return (Object.keys(memberValue).length === 0);
    125      case THIS_UPDATE:
    126      case NEXT_UPDATE:
    127        return (memberValue === SingleResponse.defaultValues(memberName as typeof NEXT_UPDATE));
    128      default:
    129        return super.defaultValues(memberName);
    130    }
    131  }
    132 
    133  /**
    134   * @inheritdoc
    135   * @asn ASN.1 schema
    136   * ```asn
    137   * SingleResponse ::= SEQUENCE {
    138   *    certID                       CertID,
    139   *    certStatus                   CertStatus,
    140   *    thisUpdate                   GeneralizedTime,
    141   *    nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
    142   *    singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
    143   *
    144   * CertStatus ::= CHOICE {
    145   *    good        [0]     IMPLICIT NULL,
    146   *    revoked     [1]     IMPLICIT RevokedInfo,
    147   *    unknown     [2]     IMPLICIT UnknownInfo }
    148   *
    149   * RevokedInfo ::= SEQUENCE {
    150   *    revocationTime              GeneralizedTime,
    151   *    revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
    152   *
    153   * UnknownInfo ::= NULL
    154   *```
    155   */
    156  public static override schema(parameters: SingleResponseSchema = {}): Schema.SchemaType {
    157    const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
    158 
    159    return (new asn1js.Sequence({
    160      name: (names.blockName || EMPTY_STRING),
    161      value: [
    162        CertID.schema(names.certID || {}),
    163        new asn1js.Choice({
    164          value: [
    165            new asn1js.Primitive({
    166              name: (names.certStatus || EMPTY_STRING),
    167              idBlock: {
    168                tagClass: 3, // CONTEXT-SPECIFIC
    169                tagNumber: 0 // [0]
    170              },
    171            }), // IMPLICIT NULL (no "valueBlock")
    172            new asn1js.Constructed({
    173              name: (names.certStatus || EMPTY_STRING),
    174              idBlock: {
    175                tagClass: 3, // CONTEXT-SPECIFIC
    176                tagNumber: 1 // [1]
    177              },
    178              value: [
    179                new asn1js.GeneralizedTime(),
    180                new asn1js.Constructed({
    181                  optional: true,
    182                  idBlock: {
    183                    tagClass: 3, // CONTEXT-SPECIFIC
    184                    tagNumber: 0 // [0]
    185                  },
    186                  value: [new asn1js.Enumerated()]
    187                })
    188              ]
    189            }),
    190            new asn1js.Primitive({
    191              name: (names.certStatus || EMPTY_STRING),
    192              idBlock: {
    193                tagClass: 3, // CONTEXT-SPECIFIC
    194                tagNumber: 2 // [2]
    195              },
    196              lenBlock: { length: 1 }
    197            }) // IMPLICIT NULL (no "valueBlock")
    198          ]
    199        }),
    200        new asn1js.GeneralizedTime({ name: (names.thisUpdate || EMPTY_STRING) }),
    201        new asn1js.Constructed({
    202          optional: true,
    203          idBlock: {
    204            tagClass: 3, // CONTEXT-SPECIFIC
    205            tagNumber: 0 // [0]
    206          },
    207          value: [new asn1js.GeneralizedTime({ name: (names.nextUpdate || EMPTY_STRING) })]
    208        }),
    209        new asn1js.Constructed({
    210          optional: true,
    211          idBlock: {
    212            tagClass: 3, // CONTEXT-SPECIFIC
    213            tagNumber: 1 // [1]
    214          },
    215          value: [Extensions.schema(names.singleExtensions || {})]
    216        }) // EXPLICIT SEQUENCE value
    217      ]
    218    }));
    219  }
    220 
    221  public fromSchema(schema: Schema.SchemaType): void {
    222    // Clear input data first
    223    pvutils.clearProps(schema, CLEAR_PROPS);
    224 
    225    // Check the schema is valid
    226    const asn1 = asn1js.compareSchema(schema,
    227      schema,
    228      SingleResponse.schema({
    229        names: {
    230          certID: {
    231            names: {
    232              blockName: CERT_ID
    233            }
    234          },
    235          certStatus: CERT_STATUS,
    236          thisUpdate: THIS_UPDATE,
    237          nextUpdate: NEXT_UPDATE,
    238          singleExtensions: {
    239            names: {
    240              blockName:
    241                SINGLE_EXTENSIONS
    242            }
    243          }
    244        }
    245      })
    246    );
    247    AsnError.assertSchema(asn1, this.className);
    248 
    249    // Get internal properties from parsed schema
    250    this.certID = new CertID({ schema: asn1.result.certID });
    251    this.certStatus = asn1.result.certStatus;
    252    this.thisUpdate = asn1.result.thisUpdate.toDate();
    253    if (NEXT_UPDATE in asn1.result)
    254      this.nextUpdate = asn1.result.nextUpdate.toDate();
    255    if (SINGLE_EXTENSIONS in asn1.result)
    256      this.singleExtensions = Array.from(asn1.result.singleExtensions.valueBlock.value, element => new Extension({ schema: element }));
    257  }
    258 
    259  public toSchema(): asn1js.Sequence {
    260    // Create value array for output sequence
    261    const outputArray = [];
    262 
    263    outputArray.push(this.certID.toSchema());
    264    outputArray.push(this.certStatus);
    265    outputArray.push(new asn1js.GeneralizedTime({ valueDate: this.thisUpdate }));
    266    if (this.nextUpdate) {
    267      outputArray.push(new asn1js.Constructed({
    268        idBlock: {
    269          tagClass: 3, // CONTEXT-SPECIFIC
    270          tagNumber: 0 // [0]
    271        },
    272        value: [new asn1js.GeneralizedTime({ valueDate: this.nextUpdate })]
    273      }));
    274    }
    275 
    276    if (this.singleExtensions) {
    277      outputArray.push(new asn1js.Constructed({
    278        idBlock: {
    279          tagClass: 3, // CONTEXT-SPECIFIC
    280          tagNumber: 1 // [1]
    281        },
    282        value: [new asn1js.Sequence({ value: Array.from(this.singleExtensions, o => o.toSchema()) })]
    283      }));
    284    }
    285 
    286    return (new asn1js.Sequence({
    287      value: outputArray
    288    }));
    289  }
    290 
    291  public toJSON(): SingleResponseJson {
    292    const res: SingleResponseJson = {
    293      certID: this.certID.toJSON(),
    294      certStatus: this.certStatus.toJSON(),
    295      thisUpdate: this.thisUpdate
    296    };
    297 
    298    if (this.nextUpdate) {
    299      res.nextUpdate = this.nextUpdate;
    300    }
    301 
    302    if (this.singleExtensions) {
    303      res.singleExtensions = Array.from(this.singleExtensions, o => o.toJSON());
    304    }
    305 
    306    return res;
    307  }
    308 
    309 }