tor-browser

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

PKCS8ShroudedKeyBag.ts (8262B)


      1 import * as asn1js from "asn1js";
      2 import * as pvutils from "pvutils";
      3 import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier";
      4 import { EncryptedData, EncryptedDataEncryptParams } from "./EncryptedData";
      5 import { EncryptedContentInfo } from "./EncryptedContentInfo";
      6 import { PrivateKeyInfo } from "./PrivateKeyInfo";
      7 import * as Schema from "./Schema";
      8 import { AsnError } from "./errors";
      9 import { PkiObject, PkiObjectParameters } from "./PkiObject";
     10 import { EMPTY_STRING } from "./constants";
     11 import * as common from "./common";
     12 
     13 const ENCRYPTION_ALGORITHM = "encryptionAlgorithm";
     14 const ENCRYPTED_DATA = "encryptedData";
     15 const PARSED_VALUE = "parsedValue";
     16 const CLEAR_PROPS = [
     17  ENCRYPTION_ALGORITHM,
     18  ENCRYPTED_DATA,
     19 ];
     20 
     21 export interface IPKCS8ShroudedKeyBag {
     22  encryptionAlgorithm: AlgorithmIdentifier;
     23  encryptedData: asn1js.OctetString;
     24  parsedValue?: PrivateKeyInfo;
     25 }
     26 
     27 export type PKCS8ShroudedKeyBagParameters = PkiObjectParameters & Partial<IPKCS8ShroudedKeyBag>;
     28 
     29 export interface PKCS8ShroudedKeyBagJson {
     30  encryptionAlgorithm: AlgorithmIdentifierJson;
     31  encryptedData: asn1js.OctetStringJson;
     32 }
     33 
     34 type PKCS8ShroudedKeyBagMakeInternalValuesParams = Omit<EncryptedDataEncryptParams, "contentToEncrypt">;
     35 
     36 /**
     37 * Represents the PKCS8ShroudedKeyBag structure described in [RFC7292](https://datatracker.ietf.org/doc/html/rfc7292)
     38 */
     39 export class PKCS8ShroudedKeyBag extends PkiObject implements IPKCS8ShroudedKeyBag {
     40 
     41  public static override CLASS_NAME = "PKCS8ShroudedKeyBag";
     42 
     43  public encryptionAlgorithm!: AlgorithmIdentifier;
     44  public encryptedData!: asn1js.OctetString;
     45  public parsedValue?: PrivateKeyInfo;
     46 
     47  /**
     48   * Initializes a new instance of the {@link PKCS8ShroudedKeyBag} class
     49   * @param parameters Initialization parameters
     50   */
     51  constructor(parameters: PKCS8ShroudedKeyBagParameters = {}) {
     52    super();
     53 
     54    this.encryptionAlgorithm = pvutils.getParametersValue(parameters, ENCRYPTION_ALGORITHM, PKCS8ShroudedKeyBag.defaultValues(ENCRYPTION_ALGORITHM));
     55    this.encryptedData = pvutils.getParametersValue(parameters, ENCRYPTED_DATA, PKCS8ShroudedKeyBag.defaultValues(ENCRYPTED_DATA));
     56    if (PARSED_VALUE in parameters) {
     57      this.parsedValue = pvutils.getParametersValue(parameters, PARSED_VALUE, PKCS8ShroudedKeyBag.defaultValues(PARSED_VALUE));
     58    }
     59 
     60    if (parameters.schema) {
     61      this.fromSchema(parameters.schema);
     62    }
     63  }
     64 
     65  /**
     66   * Returns default values for all class members
     67   * @param memberName String name for a class member
     68   * @returns Default value
     69   */
     70  public static override defaultValues(memberName: typeof ENCRYPTION_ALGORITHM): AlgorithmIdentifier;
     71  public static override defaultValues(memberName: typeof ENCRYPTED_DATA): asn1js.OctetString;
     72  public static override defaultValues(memberName: typeof PARSED_VALUE): PrivateKeyInfo;
     73  public static override defaultValues(memberName: string): any {
     74    switch (memberName) {
     75      case ENCRYPTION_ALGORITHM:
     76        return (new AlgorithmIdentifier());
     77      case ENCRYPTED_DATA:
     78        return (new asn1js.OctetString());
     79      case PARSED_VALUE:
     80        return {};
     81      default:
     82        return super.defaultValues(memberName);
     83    }
     84  }
     85 
     86  /**
     87   * Compare values with default values for all class members
     88   * @param memberName String name for a class member
     89   * @param memberValue Value to compare with default value
     90   */
     91  public static compareWithDefault(memberName: string, memberValue: any): boolean {
     92    switch (memberName) {
     93      case ENCRYPTION_ALGORITHM:
     94        return ((AlgorithmIdentifier.compareWithDefault("algorithmId", memberValue.algorithmId)) &&
     95          (("algorithmParams" in memberValue) === false));
     96      case ENCRYPTED_DATA:
     97        return (memberValue.isEqual(PKCS8ShroudedKeyBag.defaultValues(memberName)));
     98      case PARSED_VALUE:
     99        return ((memberValue instanceof Object) && (Object.keys(memberValue).length === 0));
    100      default:
    101        return super.defaultValues(memberName);
    102    }
    103  }
    104 
    105  /**
    106   * @inheritdoc
    107   * @asn ASN.1 schema
    108   * ```asn
    109   * PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo
    110   *
    111   * EncryptedPrivateKeyInfo ::= SEQUENCE {
    112   *    encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}},
    113   *    encryptedData EncryptedData
    114   * }
    115   *
    116   * EncryptedData ::= OCTET STRING
    117   *```
    118   */
    119  public static override schema(parameters: Schema.SchemaParameters<{
    120    encryptionAlgorithm?: AlgorithmIdentifierSchema;
    121    encryptedData?: string;
    122  }> = {}): Schema.SchemaType {
    123    /**
    124     * @type {Object}
    125     * @property {string} [blockName]
    126     * @property {string} [encryptionAlgorithm]
    127     * @property {string} [encryptedData]
    128     */
    129    const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
    130 
    131    return (new asn1js.Sequence({
    132      name: (names.blockName || EMPTY_STRING),
    133      value: [
    134        AlgorithmIdentifier.schema(names.encryptionAlgorithm || {
    135          names: {
    136            blockName: ENCRYPTION_ALGORITHM
    137          }
    138        }),
    139        new asn1js.Choice({
    140          value: [
    141            new asn1js.OctetString({ name: (names.encryptedData || ENCRYPTED_DATA) }),
    142            new asn1js.OctetString({
    143              idBlock: {
    144                isConstructed: true
    145              },
    146              name: (names.encryptedData || ENCRYPTED_DATA)
    147            })
    148          ]
    149        })
    150      ]
    151    }));
    152  }
    153 
    154  public fromSchema(schema: Schema.SchemaType): void {
    155    // Clear input data first
    156    pvutils.clearProps(schema, CLEAR_PROPS);
    157 
    158    // Check the schema is valid
    159    const asn1 = asn1js.compareSchema(schema,
    160      schema,
    161      PKCS8ShroudedKeyBag.schema({
    162        names: {
    163          encryptionAlgorithm: {
    164            names: {
    165              blockName: ENCRYPTION_ALGORITHM
    166            }
    167          },
    168          encryptedData: ENCRYPTED_DATA
    169        }
    170      })
    171    );
    172    AsnError.assertSchema(asn1, this.className);
    173 
    174    // Get internal properties from parsed schema
    175    this.encryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.encryptionAlgorithm });
    176    this.encryptedData = asn1.result.encryptedData;
    177  }
    178 
    179  public toSchema(): asn1js.Sequence {
    180    return (new asn1js.Sequence({
    181      value: [
    182        this.encryptionAlgorithm.toSchema(),
    183        this.encryptedData
    184      ]
    185    }));
    186  }
    187 
    188  public toJSON(): PKCS8ShroudedKeyBagJson {
    189    return {
    190      encryptionAlgorithm: this.encryptionAlgorithm.toJSON(),
    191      encryptedData: this.encryptedData.toJSON(),
    192    };
    193  }
    194 
    195  protected async parseInternalValues(parameters: {
    196    password: ArrayBuffer;
    197  }, crypto = common.getCrypto(true)) {
    198    //#region Initial variables
    199    const cmsEncrypted = new EncryptedData({
    200      encryptedContentInfo: new EncryptedContentInfo({
    201        contentEncryptionAlgorithm: this.encryptionAlgorithm,
    202        encryptedContent: this.encryptedData
    203      })
    204    });
    205    //#endregion
    206 
    207    //#region Decrypt internal data
    208    const decryptedData = await cmsEncrypted.decrypt(parameters, crypto);
    209 
    210    //#endregion
    211 
    212    //#region Initialize PARSED_VALUE with decrypted PKCS#8 private key
    213 
    214    this.parsedValue = PrivateKeyInfo.fromBER(decryptedData);
    215    //#endregion
    216  }
    217 
    218  public async makeInternalValues(parameters: PKCS8ShroudedKeyBagMakeInternalValuesParams, crypto = common.getCrypto(true)): Promise<void> {
    219    //#region Check that we do have PARSED_VALUE
    220    if (!this.parsedValue) {
    221      throw new Error("Please initialize \"parsedValue\" first");
    222    }
    223    //#endregion
    224 
    225    //#region Initial variables
    226    const cmsEncrypted = new EncryptedData();
    227    //#endregion
    228 
    229    //#region Encrypt internal data
    230    const encryptParams: EncryptedDataEncryptParams = {
    231      ...parameters,
    232      contentToEncrypt: this.parsedValue.toSchema().toBER(false),
    233    };
    234 
    235    await cmsEncrypted.encrypt(encryptParams, crypto);
    236    if (!cmsEncrypted.encryptedContentInfo.encryptedContent) {
    237      throw new Error("The filed `encryptedContent` in EncryptedContentInfo is empty");
    238    }
    239 
    240    //#endregion
    241 
    242    //#region Initialize internal values
    243    this.encryptionAlgorithm = cmsEncrypted.encryptedContentInfo.contentEncryptionAlgorithm;
    244    this.encryptedData = cmsEncrypted.encryptedContentInfo.encryptedContent;
    245    //#endregion
    246  }
    247 
    248 }