tor-browser

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

CertID.ts (10003B)


      1 import * as asn1js from "asn1js";
      2 import * as pvtsutils from "pvtsutils";
      3 import * as pvutils from "pvutils";
      4 import * as common from "./common";
      5 import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier";
      6 import { Certificate } from "./Certificate";
      7 import * as Schema from "./Schema";
      8 import { AsnError, ParameterError } from "./errors";
      9 import { PkiObject, PkiObjectParameters } from "./PkiObject";
     10 import { EMPTY_STRING } from "./constants";
     11 
     12 const HASH_ALGORITHM = "hashAlgorithm";
     13 const ISSUER_NAME_HASH = "issuerNameHash";
     14 const ISSUER_KEY_HASH = "issuerKeyHash";
     15 const SERIAL_NUMBER = "serialNumber";
     16 const CLEAR_PROPS = [
     17  HASH_ALGORITHM,
     18  ISSUER_NAME_HASH,
     19  ISSUER_KEY_HASH,
     20  SERIAL_NUMBER,
     21 ];
     22 
     23 export interface ICertID {
     24  /**
     25   * Hash algorithm used to generate the `issuerNameHash` and `issuerKeyHash` values
     26   */
     27  hashAlgorithm: AlgorithmIdentifier;
     28  /**
     29   * Hash of the issuer's distinguished name (DN). The hash shall be calculated over the DER encoding
     30   * of the issuer's name field in the certificate being checked.
     31   */
     32  issuerNameHash: asn1js.OctetString;
     33  /**
     34   * Hash of the issuer's public key. The hash shall be calculated over the value (excluding tag and length)
     35   * of the subject public key field in the issuer's certificate.
     36   */
     37  issuerKeyHash: asn1js.OctetString;
     38  /**
     39   * Serial number of the certificate for which status is being requested
     40   */
     41  serialNumber: asn1js.Integer;
     42 }
     43 
     44 export type CertIDParameters = PkiObjectParameters & Partial<ICertID>;
     45 
     46 export type CertIDSchema = Schema.SchemaParameters<{
     47  hashAlgorithm?: string;
     48  hashAlgorithmObject?: AlgorithmIdentifierSchema;
     49  issuerNameHash?: string;
     50  issuerKeyHash?: string;
     51  serialNumber?: string;
     52 }>;
     53 
     54 export interface CertIDJson {
     55  hashAlgorithm: AlgorithmIdentifierJson;
     56  issuerNameHash: asn1js.OctetStringJson;
     57  issuerKeyHash: asn1js.OctetStringJson;
     58  serialNumber: asn1js.IntegerJson;
     59 }
     60 
     61 export interface CertIDCreateParams {
     62  issuerCertificate: Certificate;
     63  hashAlgorithm: string;
     64 }
     65 
     66 /**
     67 * Represents an CertID described in [RFC6960](https://datatracker.ietf.org/doc/html/rfc6960)
     68 */
     69 export class CertID extends PkiObject implements ICertID {
     70 
     71  public static override CLASS_NAME = "CertID";
     72 
     73  /**
     74   * Making OCSP certificate identifier for specific certificate
     75   * @param certificate Certificate making OCSP Request for
     76   * @param parameters Additional parameters
     77   * @param crypto Crypto engine
     78   * @returns Returns created CertID object
     79   */
     80  public static async create(certificate: Certificate, parameters: CertIDCreateParams, crypto = common.getCrypto(true)): Promise<CertID> {
     81    const certID = new CertID();
     82    await certID.createForCertificate(certificate, parameters, crypto);
     83 
     84    return certID;
     85  }
     86 
     87  public hashAlgorithm!: AlgorithmIdentifier;
     88  public issuerNameHash!: asn1js.OctetString;
     89  public issuerKeyHash!: asn1js.OctetString;
     90  public serialNumber!: asn1js.Integer;
     91 
     92  /**
     93   * Initializes a new instance of the {@link CertID} class
     94   * @param parameters Initialization parameters
     95   */
     96  constructor(parameters: CertIDParameters = {}) {
     97    super();
     98 
     99    this.hashAlgorithm = pvutils.getParametersValue(parameters, HASH_ALGORITHM, CertID.defaultValues(HASH_ALGORITHM));
    100    this.issuerNameHash = pvutils.getParametersValue(parameters, ISSUER_NAME_HASH, CertID.defaultValues(ISSUER_NAME_HASH));
    101    this.issuerKeyHash = pvutils.getParametersValue(parameters, ISSUER_KEY_HASH, CertID.defaultValues(ISSUER_KEY_HASH));
    102    this.serialNumber = pvutils.getParametersValue(parameters, SERIAL_NUMBER, CertID.defaultValues(SERIAL_NUMBER));
    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 HASH_ALGORITHM): AlgorithmIdentifier;
    115  public static override defaultValues(memberName: typeof ISSUER_NAME_HASH): asn1js.OctetString;
    116  public static override defaultValues(memberName: typeof ISSUER_KEY_HASH): asn1js.OctetString;
    117  public static override defaultValues(memberName: typeof SERIAL_NUMBER): asn1js.Integer;
    118  public static override defaultValues(memberName: string): any {
    119    switch (memberName) {
    120      case HASH_ALGORITHM:
    121        return new AlgorithmIdentifier();
    122      case ISSUER_NAME_HASH:
    123      case ISSUER_KEY_HASH:
    124        return new asn1js.OctetString();
    125      case SERIAL_NUMBER:
    126        return new asn1js.Integer();
    127      default:
    128        return super.defaultValues(memberName);
    129    }
    130  }
    131 
    132  /**
    133   * Compare values with default values for all class members
    134   * @param memberName String name for a class member
    135   * @param memberValue Value to compare with default value
    136   */
    137  public static compareWithDefault(memberName: string, memberValue: any): boolean {
    138    switch (memberName) {
    139      case HASH_ALGORITHM:
    140        return ((memberValue.algorithmId === EMPTY_STRING) && (("algorithmParams" in memberValue) === false));
    141      case ISSUER_NAME_HASH:
    142      case ISSUER_KEY_HASH:
    143      case SERIAL_NUMBER:
    144        return (memberValue.isEqual(CertID.defaultValues(SERIAL_NUMBER)));
    145      default:
    146        return super.defaultValues(memberName);
    147    }
    148  }
    149 
    150  /**
    151   * @inheritdoc
    152   * @asn ASN.1 schema
    153   * ```asn
    154   * CertID ::= SEQUENCE {
    155   *    hashAlgorithm       AlgorithmIdentifier,
    156   *    issuerNameHash      OCTET STRING, -- Hash of issuer's DN
    157   *    issuerKeyHash       OCTET STRING, -- Hash of issuer's public key
    158   *    serialNumber        CertificateSerialNumber }
    159   *```
    160   */
    161  public static override schema(parameters: CertIDSchema = {}): Schema.SchemaType {
    162    const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
    163 
    164    return (new asn1js.Sequence({
    165      name: (names.blockName || EMPTY_STRING),
    166      value: [
    167        AlgorithmIdentifier.schema(names.hashAlgorithmObject || {
    168          names: {
    169            blockName: (names.hashAlgorithm || EMPTY_STRING)
    170          }
    171        }),
    172        new asn1js.OctetString({ name: (names.issuerNameHash || EMPTY_STRING) }),
    173        new asn1js.OctetString({ name: (names.issuerKeyHash || EMPTY_STRING) }),
    174        new asn1js.Integer({ name: (names.serialNumber || EMPTY_STRING) })
    175      ]
    176    }));
    177  }
    178 
    179  public fromSchema(schema: Schema.SchemaType): void {
    180    // Clear input data first
    181    pvutils.clearProps(schema, CLEAR_PROPS);
    182 
    183    // Check the schema is valid
    184    const asn1 = asn1js.compareSchema(schema,
    185      schema,
    186      CertID.schema({
    187        names: {
    188          hashAlgorithm: HASH_ALGORITHM,
    189          issuerNameHash: ISSUER_NAME_HASH,
    190          issuerKeyHash: ISSUER_KEY_HASH,
    191          serialNumber: SERIAL_NUMBER
    192        }
    193      })
    194    );
    195    AsnError.assertSchema(asn1, this.className);
    196 
    197    // Get internal properties from parsed schema
    198    this.hashAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.hashAlgorithm });
    199    this.issuerNameHash = asn1.result.issuerNameHash;
    200    this.issuerKeyHash = asn1.result.issuerKeyHash;
    201    this.serialNumber = asn1.result.serialNumber;
    202  }
    203 
    204  public toSchema(): asn1js.Sequence {
    205    return (new asn1js.Sequence({
    206      value: [
    207        this.hashAlgorithm.toSchema(),
    208        this.issuerNameHash,
    209        this.issuerKeyHash,
    210        this.serialNumber
    211      ]
    212    }));
    213  }
    214 
    215  public toJSON(): CertIDJson {
    216    return {
    217      hashAlgorithm: this.hashAlgorithm.toJSON(),
    218      issuerNameHash: this.issuerNameHash.toJSON(),
    219      issuerKeyHash: this.issuerKeyHash.toJSON(),
    220      serialNumber: this.serialNumber.toJSON(),
    221    };
    222  }
    223 
    224  /**
    225   * Checks that two "CertIDs" are equal
    226   * @param certificateID Identifier of the certificate to be checked
    227   */
    228  public isEqual(certificateID: CertID): boolean {
    229    // Check HASH_ALGORITHM
    230    if (this.hashAlgorithm.algorithmId !== certificateID.hashAlgorithm.algorithmId) {
    231      return false;
    232    }
    233 
    234    // Check ISSUER_NAME_HASH
    235    if (!pvtsutils.BufferSourceConverter.isEqual(this.issuerNameHash.valueBlock.valueHexView, certificateID.issuerNameHash.valueBlock.valueHexView)) {
    236      return false;
    237    }
    238 
    239    // Check ISSUER_KEY_HASH
    240    if (!pvtsutils.BufferSourceConverter.isEqual(this.issuerKeyHash.valueBlock.valueHexView, certificateID.issuerKeyHash.valueBlock.valueHexView)) {
    241      return false;
    242    }
    243 
    244    // Check SERIAL_NUMBER
    245    if (!this.serialNumber.isEqual(certificateID.serialNumber)) {
    246      return false;
    247    }
    248 
    249    return true;
    250  }
    251 
    252  /**
    253   * Making OCSP certificate identifier for specific certificate
    254   * @param certificate Certificate making OCSP Request for
    255   * @param parameters Additional parameters
    256   * @param crypto Crypto engine
    257   */
    258  public async createForCertificate(certificate: Certificate, parameters: CertIDCreateParams, crypto = common.getCrypto(true)): Promise<void> {
    259    //#region Check input parameters
    260    ParameterError.assert(parameters, HASH_ALGORITHM, "issuerCertificate");
    261 
    262    const hashOID = crypto.getOIDByAlgorithm({ name: parameters.hashAlgorithm }, true, "hashAlgorithm");
    263 
    264    this.hashAlgorithm = new AlgorithmIdentifier({
    265      algorithmId: hashOID,
    266      algorithmParams: new asn1js.Null()
    267    });
    268    const issuerCertificate = parameters.issuerCertificate;
    269    //#endregion
    270 
    271    // Initialize SERIAL_NUMBER field
    272    this.serialNumber = certificate.serialNumber;
    273 
    274    // Create ISSUER_NAME_HASH
    275    const hashIssuerName = await crypto.digest({ name: parameters.hashAlgorithm }, issuerCertificate.subject.toSchema().toBER(false));
    276    this.issuerNameHash = new asn1js.OctetString({ valueHex: hashIssuerName });
    277 
    278    // Create ISSUER_KEY_HASH
    279    const issuerKeyBuffer = issuerCertificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHexView;
    280    const hashIssuerKey = await crypto.digest({ name: parameters.hashAlgorithm }, issuerKeyBuffer as BufferSource);
    281    this.issuerKeyHash = new asn1js.OctetString({ valueHex: hashIssuerKey });
    282  }
    283 
    284 }