tor-browser

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

TimeStampResp.ts (9076B)


      1 import * as asn1js from "asn1js";
      2 import * as pvutils from "pvutils";
      3 import { PKIStatusInfo, PKIStatusInfoJson, PKIStatusInfoSchema } from "./PKIStatusInfo";
      4 import { ContentInfo, ContentInfoJson, ContentInfoSchema } from "./ContentInfo";
      5 import { SignedData } from "./SignedData";
      6 import * as Schema from "./Schema";
      7 import { id_ContentType_SignedData } from "./ObjectIdentifiers";
      8 import { Certificate } from "./Certificate";
      9 import { PkiObject, PkiObjectParameters } from "./PkiObject";
     10 import { AsnError } from "./errors";
     11 import { EMPTY_BUFFER, EMPTY_STRING } from "./constants";
     12 import * as common from "./common";
     13 
     14 const STATUS = "status";
     15 const TIME_STAMP_TOKEN = "timeStampToken";
     16 const TIME_STAMP_RESP = "TimeStampResp";
     17 const TIME_STAMP_RESP_STATUS = `${TIME_STAMP_RESP}.${STATUS}`;
     18 const TIME_STAMP_RESP_TOKEN = `${TIME_STAMP_RESP}.${TIME_STAMP_TOKEN}`;
     19 const CLEAR_PROPS = [
     20  TIME_STAMP_RESP_STATUS,
     21  TIME_STAMP_RESP_TOKEN
     22 ];
     23 
     24 export interface ITimeStampResp {
     25  /**
     26   * Time-Stamp status
     27   */
     28  status: PKIStatusInfo;
     29  /**
     30   * Time-Stamp token
     31   */
     32  timeStampToken?: ContentInfo;
     33 }
     34 
     35 export interface TimeStampRespJson {
     36  status: PKIStatusInfoJson;
     37  timeStampToken?: ContentInfoJson;
     38 }
     39 
     40 export interface TimeStampRespVerifyParams {
     41  signer?: number;
     42  trustedCerts?: Certificate[];
     43  data?: ArrayBuffer;
     44 }
     45 
     46 export type TimeStampRespParameters = PkiObjectParameters & Partial<ITimeStampResp>;
     47 
     48 /**
     49 * Represents the TimeStampResp structure described in [RFC3161](https://www.ietf.org/rfc/rfc3161.txt)
     50 *
     51 * @example The following example demonstrates how to create and sign Time-Stamp Response
     52 * ```js
     53 * // Generate random serial number
     54 * const serialNumber = pkijs.getRandomValues(new Uint8Array(10)).buffer;
     55 *
     56 * // Create specific TST info structure to sign
     57 * const tstInfo = new pkijs.TSTInfo({
     58 *   version: 1,
     59 *   policy: tspReq.reqPolicy,
     60 *   messageImprint: tspReq.messageImprint,
     61 *   serialNumber: new asn1js.Integer({ valueHex: serialNumber }),
     62 *   genTime: new Date(),
     63 *   ordering: true,
     64 *   accuracy: new pkijs.Accuracy({
     65 *     seconds: 1,
     66 *     millis: 1,
     67 *     micros: 10
     68 *   }),
     69 *   nonce: tspReq.nonce,
     70 * });
     71 *
     72 * // Create and sign CMS Signed Data with TSTInfo
     73 * const cmsSigned = new pkijs.SignedData({
     74 *   version: 3,
     75 *   encapContentInfo: new pkijs.EncapsulatedContentInfo({
     76 *     eContentType: "1.2.840.113549.1.9.16.1.4", // "tSTInfo" content type
     77 *     eContent: new asn1js.OctetString({ valueHex: tstInfo.toSchema().toBER() }),
     78 *   }),
     79 *   signerInfos: [
     80 *     new pkijs.SignerInfo({
     81 *       version: 1,
     82 *       sid: new pkijs.IssuerAndSerialNumber({
     83 *         issuer: cert.issuer,
     84 *         serialNumber: cert.serialNumber
     85 *       })
     86 *     })
     87 *   ],
     88 *   certificates: [cert]
     89 * });
     90 *
     91 * await cmsSigned.sign(keys.privateKey, 0, "SHA-256");
     92 *
     93 * // Create CMS Content Info
     94 * const cmsContent = new pkijs.ContentInfo({
     95 *   contentType: pkijs.ContentInfo.SIGNED_DATA,
     96 *   content: cmsSigned.toSchema(true)
     97 * });
     98 *
     99 * // Finally create completed TSP response structure
    100 * const tspResp = new pkijs.TimeStampResp({
    101 *   status: new pkijs.PKIStatusInfo({ status: pkijs.PKIStatus.granted }),
    102 *   timeStampToken: new pkijs.ContentInfo({ schema: cmsContent.toSchema() })
    103 * });
    104 *
    105 * const tspRespRaw = tspResp.toSchema().toBER();
    106 * ```
    107 */
    108 export class TimeStampResp extends PkiObject implements ITimeStampResp {
    109 
    110  public static override CLASS_NAME = "TimeStampResp";
    111 
    112  public status!: PKIStatusInfo;
    113  public timeStampToken?: ContentInfo;
    114 
    115  /**
    116   * Initializes a new instance of the {@link TimeStampResp} class
    117   * @param parameters Initialization parameters
    118   */
    119  constructor(parameters: TimeStampRespParameters = {}) {
    120    super();
    121 
    122    this.status = pvutils.getParametersValue(parameters, STATUS, TimeStampResp.defaultValues(STATUS));
    123    if (TIME_STAMP_TOKEN in parameters) {
    124      this.timeStampToken = pvutils.getParametersValue(parameters, TIME_STAMP_TOKEN, TimeStampResp.defaultValues(TIME_STAMP_TOKEN));
    125    }
    126 
    127    if (parameters.schema) {
    128      this.fromSchema(parameters.schema);
    129    }
    130  }
    131 
    132  /**
    133   * Returns default values for all class members
    134   * @param memberName String name for a class member
    135   * @returns Default value
    136   */
    137  public static override defaultValues(memberName: typeof STATUS): PKIStatusInfo;
    138  public static override defaultValues(memberName: typeof TIME_STAMP_TOKEN): ContentInfo;
    139  public static override defaultValues(memberName: string): any {
    140    switch (memberName) {
    141      case STATUS:
    142        return new PKIStatusInfo();
    143      case TIME_STAMP_TOKEN:
    144        return new ContentInfo();
    145      default:
    146        return super.defaultValues(memberName);
    147    }
    148  }
    149 
    150  /**
    151   * Compare values with default values for all class members
    152   * @param memberName String name for a class member
    153   * @param memberValue Value to compare with default value
    154   */
    155  public static compareWithDefault(memberName: string, memberValue: any): boolean {
    156    switch (memberName) {
    157      case STATUS:
    158        return ((PKIStatusInfo.compareWithDefault(STATUS, memberValue.status)) &&
    159          (("statusStrings" in memberValue) === false) &&
    160          (("failInfo" in memberValue) === false));
    161      case TIME_STAMP_TOKEN:
    162        return ((memberValue.contentType === EMPTY_STRING) &&
    163          (memberValue.content instanceof asn1js.Any));
    164      default:
    165        return super.defaultValues(memberName);
    166    }
    167  }
    168 
    169  /**
    170   * @inheritdoc
    171   * @asn ASN.1 schema
    172   * ```asn
    173   * TimeStampResp ::= SEQUENCE  {
    174   *    status                  PKIStatusInfo,
    175   *    timeStampToken          TimeStampToken     OPTIONAL  }
    176   *```
    177   */
    178  public static override schema(parameters: Schema.SchemaParameters<{
    179    status?: PKIStatusInfoSchema,
    180    timeStampToken?: ContentInfoSchema,
    181  }> = {}): Schema.SchemaType {
    182    const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
    183 
    184    return (new asn1js.Sequence({
    185      name: (names.blockName || TIME_STAMP_RESP),
    186      value: [
    187        PKIStatusInfo.schema(names.status || {
    188          names: {
    189            blockName: TIME_STAMP_RESP_STATUS
    190          }
    191        }),
    192        ContentInfo.schema(names.timeStampToken || {
    193          names: {
    194            blockName: TIME_STAMP_RESP_TOKEN,
    195            optional: true
    196          }
    197        })
    198      ]
    199    }));
    200  }
    201 
    202  public fromSchema(schema: Schema.SchemaType): void {
    203    // Clear input data first
    204    pvutils.clearProps(schema, CLEAR_PROPS);
    205 
    206    // Check the schema is valid
    207    const asn1 = asn1js.compareSchema(schema,
    208      schema,
    209      TimeStampResp.schema()
    210    );
    211    AsnError.assertSchema(asn1, this.className);
    212 
    213    // Get internal properties from parsed schema
    214    this.status = new PKIStatusInfo({ schema: asn1.result[TIME_STAMP_RESP_STATUS] });
    215    if (TIME_STAMP_RESP_TOKEN in asn1.result)
    216      this.timeStampToken = new ContentInfo({ schema: asn1.result[TIME_STAMP_RESP_TOKEN] });
    217  }
    218 
    219  public toSchema(): asn1js.Sequence {
    220    //#region Create array for output sequence
    221    const outputArray = [];
    222 
    223    outputArray.push(this.status.toSchema());
    224    if (this.timeStampToken) {
    225      outputArray.push(this.timeStampToken.toSchema());
    226    }
    227    //#endregion
    228 
    229    //#region Construct and return new ASN.1 schema for this object
    230    return (new asn1js.Sequence({
    231      value: outputArray
    232    }));
    233    //#endregion
    234  }
    235 
    236  public toJSON(): TimeStampRespJson {
    237    const res: TimeStampRespJson = {
    238      status: this.status.toJSON()
    239    };
    240 
    241    if (this.timeStampToken) {
    242      res.timeStampToken = this.timeStampToken.toJSON();
    243    }
    244 
    245    return res;
    246  }
    247 
    248  /**
    249   * Sign current TSP Response
    250   * @param privateKey Private key for "subjectPublicKeyInfo" structure
    251   * @param hashAlgorithm Hashing algorithm. Default SHA-1
    252   * @param crypto Crypto engine
    253   */
    254  public async sign(privateKey: CryptoKey, hashAlgorithm?: string, crypto = common.getCrypto(true)) {
    255    this.assertContentType();
    256 
    257    // Sign internal signed data value
    258    const signed = new SignedData({ schema: this.timeStampToken.content });
    259 
    260    return signed.sign(privateKey, 0, hashAlgorithm, undefined, crypto);
    261  }
    262 
    263  /**
    264   * Verify current TSP Response
    265   * @param verificationParameters Input parameters for verification
    266   * @param crypto Crypto engine
    267   */
    268  public async verify(verificationParameters: TimeStampRespVerifyParams = { signer: 0, trustedCerts: [], data: EMPTY_BUFFER }, crypto = common.getCrypto(true)): Promise<boolean> {
    269    this.assertContentType();
    270 
    271    // Verify internal signed data value
    272    const signed = new SignedData({ schema: this.timeStampToken.content });
    273 
    274    return signed.verify(verificationParameters, crypto);
    275  }
    276 
    277  private assertContentType(): asserts this is { timeStampToken: ContentInfo; } {
    278    if (!this.timeStampToken) {
    279      throw new Error("timeStampToken is absent in TSP response");
    280    }
    281    if (this.timeStampToken.contentType !== id_ContentType_SignedData) { // Must be a CMS signed data
    282      throw new Error(`Wrong format of timeStampToken: ${this.timeStampToken.contentType}`);
    283    }
    284  }
    285 }