tor-browser

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

OCSPRequest.ts (9152B)


      1 import * as asn1js from "asn1js";
      2 import * as pvutils from "pvutils";
      3 import * as common from "./common";
      4 import { TBSRequest, TBSRequestJson, TBSRequestSchema } from "./TBSRequest";
      5 import { Signature, SignatureJson, SignatureSchema } from "./Signature";
      6 import { Request } from "./Request";
      7 import { CertID, CertIDCreateParams } from "./CertID";
      8 import * as Schema from "./Schema";
      9 import { Certificate } from "./Certificate";
     10 import { AsnError, ParameterError } from "./errors";
     11 import { PkiObject, PkiObjectParameters } from "./PkiObject";
     12 
     13 const TBS_REQUEST = "tbsRequest";
     14 const OPTIONAL_SIGNATURE = "optionalSignature";
     15 const CLEAR_PROPS = [
     16  TBS_REQUEST,
     17  OPTIONAL_SIGNATURE
     18 ];
     19 
     20 export interface IOCSPRequest {
     21  tbsRequest: TBSRequest;
     22  optionalSignature?: Signature;
     23 }
     24 
     25 export interface OCSPRequestJson {
     26  tbsRequest: TBSRequestJson;
     27  optionalSignature?: SignatureJson;
     28 }
     29 
     30 export type OCSPRequestParameters = PkiObjectParameters & Partial<IOCSPRequest>;
     31 
     32 /**
     33 * Represents an OCSP request described in [RFC6960 Section 4.1](https://datatracker.ietf.org/doc/html/rfc6960#section-4.1)
     34 *
     35 * @example The following example demonstrates how to create OCSP request
     36 * ```js
     37 * // Create OCSP request
     38 * const ocspReq = new pkijs.OCSPRequest();
     39 *
     40 * ocspReq.tbsRequest.requestorName = new pkijs.GeneralName({
     41 *   type: 4,
     42 *   value: cert.subject,
     43 * });
     44 *
     45 * await ocspReq.createForCertificate(cert, {
     46 *   hashAlgorithm: "SHA-256",
     47 *   issuerCertificate: issuerCert,
     48 * });
     49 *
     50 * const nonce = pkijs.getRandomValues(new Uint8Array(10));
     51 * ocspReq.tbsRequest.requestExtensions = [
     52 *   new pkijs.Extension({
     53 *     extnID: "1.3.6.1.5.5.7.48.1.2", // nonce
     54 *     extnValue: new asn1js.OctetString({ valueHex: nonce.buffer }).toBER(),
     55 *   })
     56 * ];
     57 *
     58 * // Encode OCSP request
     59 * const ocspReqRaw = ocspReq.toSchema(true).toBER();
     60 * ```
     61 */
     62 export class OCSPRequest extends PkiObject implements IOCSPRequest {
     63 
     64  public static override CLASS_NAME = "OCSPRequest";
     65 
     66  public tbsRequest!: TBSRequest;
     67  public optionalSignature?: Signature;
     68 
     69  /**
     70   * Initializes a new instance of the {@link OCSPRequest} class
     71   * @param parameters Initialization parameters
     72   */
     73  constructor(parameters: OCSPRequestParameters = {}) {
     74    super();
     75 
     76    this.tbsRequest = pvutils.getParametersValue(parameters, TBS_REQUEST, OCSPRequest.defaultValues(TBS_REQUEST));
     77    if (OPTIONAL_SIGNATURE in parameters) {
     78      this.optionalSignature = pvutils.getParametersValue(parameters, OPTIONAL_SIGNATURE, OCSPRequest.defaultValues(OPTIONAL_SIGNATURE));
     79    }
     80 
     81    if (parameters.schema) {
     82      this.fromSchema(parameters.schema);
     83    }
     84  }
     85 
     86  /**
     87   * Returns default values for all class members
     88   * @param memberName String name for a class member
     89   * @returns Default value
     90   */
     91  public static override defaultValues(memberName: typeof TBS_REQUEST): TBSRequest;
     92  public static override defaultValues(memberName: typeof OPTIONAL_SIGNATURE): Signature;
     93  public static override defaultValues(memberName: string): any {
     94    switch (memberName) {
     95      case TBS_REQUEST:
     96        return new TBSRequest();
     97      case OPTIONAL_SIGNATURE:
     98        return new Signature();
     99      default:
    100        return super.defaultValues(memberName);
    101    }
    102  }
    103 
    104  /**
    105   * Compare values with default values for all class members
    106   * @param memberName String name for a class member
    107   * @param memberValue Value to compare with default value
    108   * @returns Returns `true` if `memberValue` is equal to default value for selected class member
    109   */
    110  public static compareWithDefault(memberName: string, memberValue: any): boolean {
    111    switch (memberName) {
    112      case TBS_REQUEST:
    113        return ((TBSRequest.compareWithDefault("tbs", memberValue.tbs)) &&
    114          (TBSRequest.compareWithDefault("version", memberValue.version)) &&
    115          (TBSRequest.compareWithDefault("requestorName", memberValue.requestorName)) &&
    116          (TBSRequest.compareWithDefault("requestList", memberValue.requestList)) &&
    117          (TBSRequest.compareWithDefault("requestExtensions", memberValue.requestExtensions)));
    118      case OPTIONAL_SIGNATURE:
    119        return ((Signature.compareWithDefault("signatureAlgorithm", memberValue.signatureAlgorithm)) &&
    120          (Signature.compareWithDefault("signature", memberValue.signature)) &&
    121          (Signature.compareWithDefault("certs", memberValue.certs)));
    122      default:
    123        return super.defaultValues(memberName);
    124    }
    125  }
    126 
    127  /**
    128   * @inheritdoc
    129   * @asn ASN.1 schema
    130   * ```asn
    131   * OCSPRequest ::= SEQUENCE {
    132   *    tbsRequest                  TBSRequest,
    133   *    optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
    134   *```
    135   */
    136  public static override schema(parameters: Schema.SchemaParameters<{
    137    tbsRequest?: TBSRequestSchema;
    138    optionalSignature?: SignatureSchema;
    139  }> = {}): Schema.SchemaType {
    140    const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
    141 
    142    return (new asn1js.Sequence({
    143      name: names.blockName || "OCSPRequest",
    144      value: [
    145        TBSRequest.schema(names.tbsRequest || {
    146          names: {
    147            blockName: TBS_REQUEST
    148          }
    149        }),
    150        new asn1js.Constructed({
    151          optional: true,
    152          idBlock: {
    153            tagClass: 3, // CONTEXT-SPECIFIC
    154            tagNumber: 0 // [0]
    155          },
    156          value: [
    157            Signature.schema(names.optionalSignature || {
    158              names: {
    159                blockName: OPTIONAL_SIGNATURE
    160              }
    161            })
    162          ]
    163        })
    164      ]
    165    }));
    166  }
    167 
    168  public fromSchema(schema: Schema.SchemaType): void {
    169    // Clear input data first
    170    pvutils.clearProps(schema, CLEAR_PROPS);
    171 
    172    // Check the schema is valid
    173    const asn1 = asn1js.compareSchema(schema,
    174      schema,
    175      OCSPRequest.schema()
    176    );
    177    AsnError.assertSchema(asn1, this.className);
    178 
    179    // Get internal properties from parsed schema
    180    this.tbsRequest = new TBSRequest({ schema: asn1.result.tbsRequest });
    181    if (OPTIONAL_SIGNATURE in asn1.result)
    182      this.optionalSignature = new Signature({ schema: asn1.result.optionalSignature });
    183  }
    184 
    185  public toSchema(encodeFlag = false) {
    186    //#region Create array for output sequence
    187    const outputArray = [];
    188 
    189    outputArray.push(this.tbsRequest.toSchema(encodeFlag));
    190    if (this.optionalSignature)
    191      outputArray.push(
    192        new asn1js.Constructed({
    193          optional: true,
    194          idBlock: {
    195            tagClass: 3, // CONTEXT-SPECIFIC
    196            tagNumber: 0 // [0]
    197          },
    198          value: [
    199            this.optionalSignature.toSchema()
    200          ]
    201        }));
    202    //#endregion
    203 
    204    //#region Construct and return new ASN.1 schema for this object
    205    return (new asn1js.Sequence({
    206      value: outputArray
    207    }));
    208    //#endregion
    209  }
    210 
    211  public toJSON(): OCSPRequestJson {
    212    const res: OCSPRequestJson = {
    213      tbsRequest: this.tbsRequest.toJSON()
    214    };
    215 
    216    if (this.optionalSignature) {
    217      res.optionalSignature = this.optionalSignature.toJSON();
    218    }
    219 
    220    return res;
    221  }
    222 
    223  /**
    224   * Making OCSP Request for specific certificate
    225   * @param certificate Certificate making OCSP Request for
    226   * @param parameters Additional parameters
    227   * @param crypto Crypto engine
    228   */
    229  public async createForCertificate(certificate: Certificate, parameters: CertIDCreateParams, crypto = common.getCrypto(true)): Promise<void> {
    230    //#region Initial variables
    231    const certID = new CertID();
    232    //#endregion
    233 
    234    //#region Create OCSP certificate identifier for the certificate
    235    await certID.createForCertificate(certificate, parameters, crypto);
    236    //#endregion
    237 
    238    //#region Make final request data
    239    this.tbsRequest.requestList.push(new Request({
    240      reqCert: certID,
    241    }));
    242    //#endregion
    243  }
    244 
    245  /**
    246   * Make signature for current OCSP Request
    247   * @param privateKey Private key for "subjectPublicKeyInfo" structure
    248   * @param hashAlgorithm Hashing algorithm. Default SHA-1
    249   * @param crypto Crypto engine
    250   */
    251  public async sign(privateKey: CryptoKey, hashAlgorithm = "SHA-1", crypto = common.getCrypto(true)) {
    252    // Initial checking
    253    ParameterError.assertEmpty(privateKey, "privateKey", "OCSPRequest.sign method");
    254 
    255    // Check that OPTIONAL_SIGNATURE exists in the current request
    256    if (!this.optionalSignature) {
    257      throw new Error("Need to create \"optionalSignature\" field before signing");
    258    }
    259 
    260    //#region Get a "default parameters" for current algorithm and set correct signature algorithm
    261    const signatureParams = await crypto.getSignatureParameters(privateKey, hashAlgorithm);
    262    const parameters = signatureParams.parameters;
    263    this.optionalSignature.signatureAlgorithm = signatureParams.signatureAlgorithm;
    264    //#endregion
    265 
    266    //#region Create TBS data for signing
    267    const tbs = this.tbsRequest.toSchema(true).toBER(false);
    268    //#endregion
    269 
    270    // Signing TBS data on provided private key
    271    const signature = await crypto.signWithPrivateKey(tbs, privateKey, parameters as any);
    272    this.optionalSignature.signature = new asn1js.BitString({ valueHex: signature });
    273  }
    274 
    275  verify() {
    276    // TODO: Create the function
    277  }
    278 
    279 }