tor-browser

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

TSTInfo.ts (17238B)


      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 { MessageImprint, HASHED_MESSAGE, HASH_ALGORITHM, MessageImprintSchema, MessageImprintJson } from "./MessageImprint";
      6 import { Accuracy, AccuracyJson, AccuracySchema, MICROS, MILLIS, SECONDS } from "./Accuracy";
      7 import { GeneralName, GeneralNameJson, GeneralNameSchema, TYPE, VALUE } from "./GeneralName";
      8 import { Extension, ExtensionJson, ExtensionSchema } from "./Extension";
      9 import * as Schema from "./Schema";
     10 import { PkiObject, PkiObjectParameters } from "./PkiObject";
     11 import { AsnError } from "./errors";
     12 import { EMPTY_STRING } from "./constants";
     13 
     14 const VERSION = "version";
     15 const POLICY = "policy";
     16 const MESSAGE_IMPRINT = "messageImprint";
     17 const SERIAL_NUMBER = "serialNumber";
     18 const GEN_TIME = "genTime";
     19 const ORDERING = "ordering";
     20 const NONCE = "nonce";
     21 const ACCURACY = "accuracy";
     22 const TSA = "tsa";
     23 const EXTENSIONS = "extensions";
     24 const TST_INFO = "TSTInfo";
     25 const TST_INFO_VERSION = `${TST_INFO}.${VERSION}`;
     26 const TST_INFO_POLICY = `${TST_INFO}.${POLICY}`;
     27 const TST_INFO_MESSAGE_IMPRINT = `${TST_INFO}.${MESSAGE_IMPRINT}`;
     28 const TST_INFO_SERIAL_NUMBER = `${TST_INFO}.${SERIAL_NUMBER}`;
     29 const TST_INFO_GEN_TIME = `${TST_INFO}.${GEN_TIME}`;
     30 const TST_INFO_ACCURACY = `${TST_INFO}.${ACCURACY}`;
     31 const TST_INFO_ORDERING = `${TST_INFO}.${ORDERING}`;
     32 const TST_INFO_NONCE = `${TST_INFO}.${NONCE}`;
     33 const TST_INFO_TSA = `${TST_INFO}.${TSA}`;
     34 const TST_INFO_EXTENSIONS = `${TST_INFO}.${EXTENSIONS}`;
     35 const CLEAR_PROPS = [
     36  TST_INFO_VERSION,
     37  TST_INFO_POLICY,
     38  TST_INFO_MESSAGE_IMPRINT,
     39  TST_INFO_SERIAL_NUMBER,
     40  TST_INFO_GEN_TIME,
     41  TST_INFO_ACCURACY,
     42  TST_INFO_ORDERING,
     43  TST_INFO_NONCE,
     44  TST_INFO_TSA,
     45  TST_INFO_EXTENSIONS
     46 ];
     47 
     48 export interface ITSTInfo {
     49  /**
     50   * Version of the time-stamp token.
     51   *
     52   * Conforming time-stamping servers MUST be able to provide version 1 time-stamp tokens.
     53   */
     54  version: number;
     55  /**
     56   * TSA's policy under which the response was produced.
     57   *
     58   * If a similar field was present in the TimeStampReq, then it MUST have the same value,
     59   * otherwise an error (unacceptedPolicy) MUST be returned
     60   */
     61  policy: string;
     62  /**
     63   * The messageImprint MUST have the same value as the similar field in
     64   * TimeStampReq, provided that the size of the hash value matches the
     65   * expected size of the hash algorithm identified in hashAlgorithm.
     66   */
     67  messageImprint: MessageImprint;
     68  /**
     69   * Integer assigned by the TSA to each TimeStampToken.
     70   *
     71   * It MUST be unique for each TimeStampToken issued by a given TSA.
     72   */
     73  serialNumber: asn1js.Integer;
     74  /**
     75   * Time at which the time-stamp token has been created by the TSA
     76   */
     77  genTime: Date;
     78  /**
     79   * Represents the time deviation around the UTC time contained in GeneralizedTime
     80   */
     81  accuracy?: Accuracy;
     82  /**
     83   * If the ordering field is missing, or if the ordering field is present
     84   * and set to false, then the genTime field only indicates the time at
     85   * which the time-stamp token has been created by the TSA.In such a
     86   * case, the ordering of time-stamp tokens issued by the same TSA or
     87   * different TSAs is only possible when the difference between the
     88   * genTime of the first time-stamp token and the genTime of the second
     89   * time-stamp token is greater than the sum of the accuracies of the
     90   * genTime for each time-stamp token.
     91   *
     92   * If the ordering field is present and set to true, every time-stamp
     93   * token from the same TSA can always be ordered based on the genTime
     94   * field, regardless of the genTime accuracy.
     95   */
     96  ordering?: boolean;
     97  /**
     98   * Field MUST be present if it was present in the TimeStampReq.
     99   * In such a case it MUST equal the value provided in the TimeStampReq structure.
    100   */
    101  nonce?: asn1js.Integer;
    102  /**
    103   * `tsa` field is to give a hint in identifying the name of the TSA.
    104   * If present, it MUST correspond to one of the subject names included
    105   * in the certificate that is to be used to verify the token.
    106   */
    107  tsa?: GeneralName;
    108  /**
    109   * Additional information in the future.  Extensions is defined in [RFC2459](https://datatracker.ietf.org/doc/html/rfc2459)
    110   */
    111  extensions?: Extension[];
    112 }
    113 
    114 export interface TSTInfoJson {
    115  version: number;
    116  policy: string;
    117  messageImprint: MessageImprintJson;
    118  serialNumber: asn1js.IntegerJson;
    119  genTime: Date;
    120  accuracy?: AccuracyJson;
    121  ordering?: boolean;
    122  nonce?: asn1js.IntegerJson;
    123  tsa?: GeneralNameJson;
    124  extensions?: ExtensionJson[];
    125 }
    126 
    127 export type TSTInfoParameters = PkiObjectParameters & Partial<ITSTInfo>;
    128 
    129 export interface TSTInfoVerifyParams {
    130  data: ArrayBuffer;
    131  notBefore?: Date;
    132  notAfter?: Date;
    133 }
    134 
    135 /**
    136 * Represents the TSTInfo structure described in [RFC3161](https://www.ietf.org/rfc/rfc3161.txt)
    137 */
    138 export class TSTInfo extends PkiObject implements ITSTInfo {
    139 
    140  public static override CLASS_NAME = "TSTInfo";
    141 
    142  public version!: number;
    143  public policy!: string;
    144  public messageImprint!: MessageImprint;
    145  public serialNumber!: asn1js.Integer;
    146  public genTime!: Date;
    147  public accuracy?: Accuracy;
    148  public ordering?: boolean;
    149  public nonce?: asn1js.Integer;
    150  public tsa?: GeneralName;
    151  public extensions?: Extension[];
    152 
    153  /**
    154   * Initializes a new instance of the {@link TSTInfo} class
    155   * @param parameters Initialization parameters
    156   */
    157  constructor(parameters: TSTInfoParameters = {}) {
    158    super();
    159 
    160    this.version = pvutils.getParametersValue(parameters, VERSION, TSTInfo.defaultValues(VERSION));
    161    this.policy = pvutils.getParametersValue(parameters, POLICY, TSTInfo.defaultValues(POLICY));
    162    this.messageImprint = pvutils.getParametersValue(parameters, MESSAGE_IMPRINT, TSTInfo.defaultValues(MESSAGE_IMPRINT));
    163    this.serialNumber = pvutils.getParametersValue(parameters, SERIAL_NUMBER, TSTInfo.defaultValues(SERIAL_NUMBER));
    164    this.genTime = pvutils.getParametersValue(parameters, GEN_TIME, TSTInfo.defaultValues(GEN_TIME));
    165 
    166    if (ACCURACY in parameters) {
    167      this.accuracy = pvutils.getParametersValue(parameters, ACCURACY, TSTInfo.defaultValues(ACCURACY));
    168    }
    169 
    170    if (ORDERING in parameters) {
    171      this.ordering = pvutils.getParametersValue(parameters, ORDERING, TSTInfo.defaultValues(ORDERING));
    172    }
    173 
    174    if (NONCE in parameters) {
    175      this.nonce = pvutils.getParametersValue(parameters, NONCE, TSTInfo.defaultValues(NONCE));
    176    }
    177 
    178    if (TSA in parameters) {
    179      this.tsa = pvutils.getParametersValue(parameters, TSA, TSTInfo.defaultValues(TSA));
    180    }
    181 
    182    if (EXTENSIONS in parameters) {
    183      this.extensions = pvutils.getParametersValue(parameters, EXTENSIONS, TSTInfo.defaultValues(EXTENSIONS));
    184    }
    185 
    186    if (parameters.schema) {
    187      this.fromSchema(parameters.schema);
    188    }
    189  }
    190 
    191  /**
    192   * Returns default values for all class members
    193   * @param memberName String name for a class member
    194   * @returns Default value
    195   */
    196  public static override defaultValues(memberName: typeof VERSION): number;
    197  public static override defaultValues(memberName: typeof POLICY): string;
    198  public static override defaultValues(memberName: typeof MESSAGE_IMPRINT): MessageImprint;
    199  public static override defaultValues(memberName: typeof SERIAL_NUMBER): asn1js.Integer;
    200  public static override defaultValues(memberName: typeof GEN_TIME): Date;
    201  public static override defaultValues(memberName: typeof ACCURACY): Accuracy;
    202  public static override defaultValues(memberName: typeof ORDERING): boolean;
    203  public static override defaultValues(memberName: typeof NONCE): asn1js.Integer;
    204  public static override defaultValues(memberName: typeof TSA): GeneralName;
    205  public static override defaultValues(memberName: typeof EXTENSIONS): Extension[];
    206  public static override defaultValues(memberName: string): any {
    207    switch (memberName) {
    208      case VERSION:
    209        return 0;
    210      case POLICY:
    211        return EMPTY_STRING;
    212      case MESSAGE_IMPRINT:
    213        return new MessageImprint();
    214      case SERIAL_NUMBER:
    215        return new asn1js.Integer();
    216      case GEN_TIME:
    217        return new Date(0, 0, 0);
    218      case ACCURACY:
    219        return new Accuracy();
    220      case ORDERING:
    221        return false;
    222      case NONCE:
    223        return new asn1js.Integer();
    224      case TSA:
    225        return new GeneralName();
    226      case EXTENSIONS:
    227        return [];
    228      default:
    229        return super.defaultValues(memberName);
    230    }
    231  }
    232 
    233  /**
    234   * Compare values with default values for all class members
    235   * @param memberName String name for a class member
    236   * @param memberValue Value to compare with default value
    237   */
    238  public static compareWithDefault(memberName: string, memberValue: any): boolean {
    239    switch (memberName) {
    240      case VERSION:
    241      case POLICY:
    242      case GEN_TIME:
    243      case ORDERING:
    244        return (memberValue === TSTInfo.defaultValues(ORDERING));
    245      case MESSAGE_IMPRINT:
    246        return ((MessageImprint.compareWithDefault(HASH_ALGORITHM, memberValue.hashAlgorithm)) &&
    247          (MessageImprint.compareWithDefault(HASHED_MESSAGE, memberValue.hashedMessage)));
    248      case SERIAL_NUMBER:
    249      case NONCE:
    250        return (memberValue.isEqual(TSTInfo.defaultValues(NONCE)));
    251      case ACCURACY:
    252        return ((Accuracy.compareWithDefault(SECONDS, memberValue.seconds)) &&
    253          (Accuracy.compareWithDefault(MILLIS, memberValue.millis)) &&
    254          (Accuracy.compareWithDefault(MICROS, memberValue.micros)));
    255      case TSA:
    256        return ((GeneralName.compareWithDefault(TYPE, memberValue.type)) &&
    257          (GeneralName.compareWithDefault(VALUE, memberValue.value)));
    258      case EXTENSIONS:
    259        return (memberValue.length === 0);
    260      default:
    261        return super.defaultValues(memberName);
    262    }
    263  }
    264 
    265  /**
    266   * @inheritdoc
    267   * @asn ASN.1 schema
    268   * ```asn
    269   * TSTInfo ::= SEQUENCE  {
    270   *   version                      INTEGER  { v1(1) },
    271   *   policy                       TSAPolicyId,
    272   *   messageImprint               MessageImprint,
    273   *   serialNumber                 INTEGER,
    274   *   genTime                      GeneralizedTime,
    275   *   accuracy                     Accuracy                 OPTIONAL,
    276   *   ordering                     BOOLEAN             DEFAULT FALSE,
    277   *   nonce                        INTEGER                  OPTIONAL,
    278   *   tsa                          [0] GeneralName          OPTIONAL,
    279   *   extensions                   [1] IMPLICIT Extensions  OPTIONAL  }
    280   *```
    281   */
    282  public static override schema(parameters: Schema.SchemaParameters<{
    283    version?: string;
    284    policy?: string;
    285    messageImprint?: MessageImprintSchema;
    286    serialNumber?: string;
    287    genTime?: string;
    288    accuracy?: AccuracySchema;
    289    ordering?: string;
    290    nonce?: string;
    291    tsa?: GeneralNameSchema;
    292    extensions?: string;
    293    extension?: ExtensionSchema;
    294  }> = {}): Schema.SchemaType {
    295    const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
    296 
    297    return (new asn1js.Sequence({
    298      name: (names.blockName || TST_INFO),
    299      value: [
    300        new asn1js.Integer({ name: (names.version || TST_INFO_VERSION) }),
    301        new asn1js.ObjectIdentifier({ name: (names.policy || TST_INFO_POLICY) }),
    302        MessageImprint.schema(names.messageImprint || {
    303          names: {
    304            blockName: TST_INFO_MESSAGE_IMPRINT
    305          }
    306        }),
    307        new asn1js.Integer({ name: (names.serialNumber || TST_INFO_SERIAL_NUMBER) }),
    308        new asn1js.GeneralizedTime({ name: (names.genTime || TST_INFO_GEN_TIME) }),
    309        Accuracy.schema(names.accuracy || {
    310          names: {
    311            blockName: TST_INFO_ACCURACY
    312          }
    313        }),
    314        new asn1js.Boolean({
    315          name: (names.ordering || TST_INFO_ORDERING),
    316          optional: true
    317        }),
    318        new asn1js.Integer({
    319          name: (names.nonce || TST_INFO_NONCE),
    320          optional: true
    321        }),
    322        new asn1js.Constructed({
    323          optional: true,
    324          idBlock: {
    325            tagClass: 3, // CONTEXT-SPECIFIC
    326            tagNumber: 0 // [0]
    327          },
    328          value: [GeneralName.schema(names.tsa || {
    329            names: {
    330              blockName: TST_INFO_TSA
    331            }
    332          })]
    333        }),
    334        new asn1js.Constructed({
    335          optional: true,
    336          idBlock: {
    337            tagClass: 3, // CONTEXT-SPECIFIC
    338            tagNumber: 1 // [1]
    339          },
    340          value: [
    341            new asn1js.Repeated({
    342              name: (names.extensions || TST_INFO_EXTENSIONS),
    343              value: Extension.schema(names.extension || {})
    344            })
    345          ]
    346        }) // IMPLICIT Extensions
    347      ]
    348    }));
    349  }
    350 
    351  public fromSchema(schema: Schema.SchemaType): void {
    352    // Clear input data first
    353    pvutils.clearProps(schema, CLEAR_PROPS);
    354 
    355    // Check the schema is valid
    356    const asn1 = asn1js.compareSchema(schema,
    357      schema,
    358      TSTInfo.schema()
    359    );
    360    AsnError.assertSchema(asn1, this.className);
    361 
    362    // Get internal properties from parsed schema
    363    this.version = asn1.result[TST_INFO_VERSION].valueBlock.valueDec;
    364    this.policy = asn1.result[TST_INFO_POLICY].valueBlock.toString();
    365    this.messageImprint = new MessageImprint({ schema: asn1.result[TST_INFO_MESSAGE_IMPRINT] });
    366    this.serialNumber = asn1.result[TST_INFO_SERIAL_NUMBER];
    367    this.genTime = asn1.result[TST_INFO_GEN_TIME].toDate();
    368    if (TST_INFO_ACCURACY in asn1.result)
    369      this.accuracy = new Accuracy({ schema: asn1.result[TST_INFO_ACCURACY] });
    370    if (TST_INFO_ORDERING in asn1.result)
    371      this.ordering = asn1.result[TST_INFO_ORDERING].valueBlock.value;
    372    if (TST_INFO_NONCE in asn1.result)
    373      this.nonce = asn1.result[TST_INFO_NONCE];
    374    if (TST_INFO_TSA in asn1.result)
    375      this.tsa = new GeneralName({ schema: asn1.result[TST_INFO_TSA] });
    376    if (TST_INFO_EXTENSIONS in asn1.result)
    377      this.extensions = Array.from(asn1.result[TST_INFO_EXTENSIONS], element => new Extension({ schema: element }));
    378  }
    379 
    380  public toSchema(): asn1js.Sequence {
    381    //#region Create array for output sequence
    382    const outputArray = [];
    383 
    384    outputArray.push(new asn1js.Integer({ value: this.version }));
    385    outputArray.push(new asn1js.ObjectIdentifier({ value: this.policy }));
    386    outputArray.push(this.messageImprint.toSchema());
    387    outputArray.push(this.serialNumber);
    388    outputArray.push(new asn1js.GeneralizedTime({ valueDate: this.genTime }));
    389    if (this.accuracy)
    390      outputArray.push(this.accuracy.toSchema());
    391    if (this.ordering !== undefined)
    392      outputArray.push(new asn1js.Boolean({ value: this.ordering }));
    393    if (this.nonce)
    394      outputArray.push(this.nonce);
    395    if (this.tsa) {
    396      outputArray.push(new asn1js.Constructed({
    397        optional: true,
    398        idBlock: {
    399          tagClass: 3, // CONTEXT-SPECIFIC
    400          tagNumber: 0 // [0]
    401        },
    402        value: [this.tsa.toSchema()]
    403      }));
    404    }
    405 
    406    //#region Create array of extensions
    407    if (this.extensions) {
    408      outputArray.push(new asn1js.Constructed({
    409        optional: true,
    410        idBlock: {
    411          tagClass: 3, // CONTEXT-SPECIFIC
    412          tagNumber: 1 // [1]
    413        },
    414        value: Array.from(this.extensions, o => o.toSchema())
    415      }));
    416    }
    417    //#endregion
    418    //#endregion
    419 
    420    //#region Construct and return new ASN.1 schema for this object
    421    return (new asn1js.Sequence({
    422      value: outputArray
    423    }));
    424    //#endregion
    425  }
    426 
    427  public toJSON(): TSTInfoJson {
    428    const res: TSTInfoJson = {
    429      version: this.version,
    430      policy: this.policy,
    431      messageImprint: this.messageImprint.toJSON(),
    432      serialNumber: this.serialNumber.toJSON(),
    433      genTime: this.genTime
    434    };
    435 
    436    if (this.accuracy)
    437      res.accuracy = this.accuracy.toJSON();
    438 
    439    if (this.ordering !== undefined)
    440      res.ordering = this.ordering;
    441 
    442    if (this.nonce)
    443      res.nonce = this.nonce.toJSON();
    444 
    445    if (this.tsa)
    446      res.tsa = this.tsa.toJSON();
    447 
    448    if (this.extensions)
    449      res.extensions = Array.from(this.extensions, o => o.toJSON());
    450 
    451    return res;
    452  }
    453 
    454  /**
    455   * Verify current TST Info value
    456   * @param params Input parameters
    457   * @param crypto Crypto engine
    458   */
    459  public async verify(params: TSTInfoVerifyParams, crypto = common.getCrypto(true)): Promise<boolean> {
    460 
    461    //#region Get initial parameters
    462    if (!params.data) {
    463      throw new Error("\"data\" is a mandatory attribute for TST_INFO verification");
    464    }
    465    const data = params.data;
    466    //#endregion
    467 
    468    //#region Check date
    469    if (params.notBefore) {
    470      if (this.genTime < params.notBefore)
    471        throw new Error("Generation time for TSTInfo object is less than notBefore value");
    472    }
    473 
    474    if (params.notAfter) {
    475      if (this.genTime > params.notAfter)
    476        throw new Error("Generation time for TSTInfo object is more than notAfter value");
    477    }
    478    //#endregion
    479 
    480    // Find hashing algorithm
    481    const shaAlgorithm = crypto.getAlgorithmByOID(this.messageImprint.hashAlgorithm.algorithmId, true, "MessageImprint.hashAlgorithm");
    482 
    483    // Calculate message digest for input "data" buffer
    484    const hash = await crypto.digest(shaAlgorithm.name, new Uint8Array(data));
    485    return pvtsutils.BufferSourceConverter.isEqual(hash, this.messageImprint.hashedMessage.valueBlock.valueHexView);
    486  }
    487 
    488 }