diff options
Diffstat (limited to 'third_party/js/PKI.js/src/PKCS8ShroudedKeyBag.ts')
-rw-r--r-- | third_party/js/PKI.js/src/PKCS8ShroudedKeyBag.ts | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/third_party/js/PKI.js/src/PKCS8ShroudedKeyBag.ts b/third_party/js/PKI.js/src/PKCS8ShroudedKeyBag.ts new file mode 100644 index 0000000000..34458958a9 --- /dev/null +++ b/third_party/js/PKI.js/src/PKCS8ShroudedKeyBag.ts @@ -0,0 +1,248 @@ +import * as asn1js from "asn1js"; +import * as pvutils from "pvutils"; +import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier"; +import { EncryptedData, EncryptedDataEncryptParams } from "./EncryptedData"; +import { EncryptedContentInfo } from "./EncryptedContentInfo"; +import { PrivateKeyInfo } from "./PrivateKeyInfo"; +import * as Schema from "./Schema"; +import { AsnError } from "./errors"; +import { PkiObject, PkiObjectParameters } from "./PkiObject"; +import { EMPTY_STRING } from "./constants"; +import * as common from "./common"; + +const ENCRYPTION_ALGORITHM = "encryptionAlgorithm"; +const ENCRYPTED_DATA = "encryptedData"; +const PARSED_VALUE = "parsedValue"; +const CLEAR_PROPS = [ + ENCRYPTION_ALGORITHM, + ENCRYPTED_DATA, +]; + +export interface IPKCS8ShroudedKeyBag { + encryptionAlgorithm: AlgorithmIdentifier; + encryptedData: asn1js.OctetString; + parsedValue?: PrivateKeyInfo; +} + +export type PKCS8ShroudedKeyBagParameters = PkiObjectParameters & Partial<IPKCS8ShroudedKeyBag>; + +export interface PKCS8ShroudedKeyBagJson { + encryptionAlgorithm: AlgorithmIdentifierJson; + encryptedData: asn1js.OctetStringJson; +} + +type PKCS8ShroudedKeyBagMakeInternalValuesParams = Omit<EncryptedDataEncryptParams, "contentToEncrypt">; + +/** + * Represents the PKCS8ShroudedKeyBag structure described in [RFC7292](https://datatracker.ietf.org/doc/html/rfc7292) + */ +export class PKCS8ShroudedKeyBag extends PkiObject implements IPKCS8ShroudedKeyBag { + + public static override CLASS_NAME = "PKCS8ShroudedKeyBag"; + + public encryptionAlgorithm!: AlgorithmIdentifier; + public encryptedData!: asn1js.OctetString; + public parsedValue?: PrivateKeyInfo; + + /** + * Initializes a new instance of the {@link PKCS8ShroudedKeyBag} class + * @param parameters Initialization parameters + */ + constructor(parameters: PKCS8ShroudedKeyBagParameters = {}) { + super(); + + this.encryptionAlgorithm = pvutils.getParametersValue(parameters, ENCRYPTION_ALGORITHM, PKCS8ShroudedKeyBag.defaultValues(ENCRYPTION_ALGORITHM)); + this.encryptedData = pvutils.getParametersValue(parameters, ENCRYPTED_DATA, PKCS8ShroudedKeyBag.defaultValues(ENCRYPTED_DATA)); + if (PARSED_VALUE in parameters) { + this.parsedValue = pvutils.getParametersValue(parameters, PARSED_VALUE, PKCS8ShroudedKeyBag.defaultValues(PARSED_VALUE)); + } + + if (parameters.schema) { + this.fromSchema(parameters.schema); + } + } + + /** + * Returns default values for all class members + * @param memberName String name for a class member + * @returns Default value + */ + public static override defaultValues(memberName: typeof ENCRYPTION_ALGORITHM): AlgorithmIdentifier; + public static override defaultValues(memberName: typeof ENCRYPTED_DATA): asn1js.OctetString; + public static override defaultValues(memberName: typeof PARSED_VALUE): PrivateKeyInfo; + public static override defaultValues(memberName: string): any { + switch (memberName) { + case ENCRYPTION_ALGORITHM: + return (new AlgorithmIdentifier()); + case ENCRYPTED_DATA: + return (new asn1js.OctetString()); + case PARSED_VALUE: + return {}; + default: + return super.defaultValues(memberName); + } + } + + /** + * Compare values with default values for all class members + * @param memberName String name for a class member + * @param memberValue Value to compare with default value + */ + public static compareWithDefault(memberName: string, memberValue: any): boolean { + switch (memberName) { + case ENCRYPTION_ALGORITHM: + return ((AlgorithmIdentifier.compareWithDefault("algorithmId", memberValue.algorithmId)) && + (("algorithmParams" in memberValue) === false)); + case ENCRYPTED_DATA: + return (memberValue.isEqual(PKCS8ShroudedKeyBag.defaultValues(memberName))); + case PARSED_VALUE: + return ((memberValue instanceof Object) && (Object.keys(memberValue).length === 0)); + default: + return super.defaultValues(memberName); + } + } + + /** + * @inheritdoc + * @asn ASN.1 schema + * ```asn + * PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo + * + * EncryptedPrivateKeyInfo ::= SEQUENCE { + * encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}}, + * encryptedData EncryptedData + * } + * + * EncryptedData ::= OCTET STRING + *``` + */ + public static override schema(parameters: Schema.SchemaParameters<{ + encryptionAlgorithm?: AlgorithmIdentifierSchema; + encryptedData?: string; + }> = {}): Schema.SchemaType { + /** + * @type {Object} + * @property {string} [blockName] + * @property {string} [encryptionAlgorithm] + * @property {string} [encryptedData] + */ + const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); + + return (new asn1js.Sequence({ + name: (names.blockName || EMPTY_STRING), + value: [ + AlgorithmIdentifier.schema(names.encryptionAlgorithm || { + names: { + blockName: ENCRYPTION_ALGORITHM + } + }), + new asn1js.Choice({ + value: [ + new asn1js.OctetString({ name: (names.encryptedData || ENCRYPTED_DATA) }), + new asn1js.OctetString({ + idBlock: { + isConstructed: true + }, + name: (names.encryptedData || ENCRYPTED_DATA) + }) + ] + }) + ] + })); + } + + public fromSchema(schema: Schema.SchemaType): void { + // Clear input data first + pvutils.clearProps(schema, CLEAR_PROPS); + + // Check the schema is valid + const asn1 = asn1js.compareSchema(schema, + schema, + PKCS8ShroudedKeyBag.schema({ + names: { + encryptionAlgorithm: { + names: { + blockName: ENCRYPTION_ALGORITHM + } + }, + encryptedData: ENCRYPTED_DATA + } + }) + ); + AsnError.assertSchema(asn1, this.className); + + // Get internal properties from parsed schema + this.encryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.encryptionAlgorithm }); + this.encryptedData = asn1.result.encryptedData; + } + + public toSchema(): asn1js.Sequence { + return (new asn1js.Sequence({ + value: [ + this.encryptionAlgorithm.toSchema(), + this.encryptedData + ] + })); + } + + public toJSON(): PKCS8ShroudedKeyBagJson { + return { + encryptionAlgorithm: this.encryptionAlgorithm.toJSON(), + encryptedData: this.encryptedData.toJSON(), + }; + } + + protected async parseInternalValues(parameters: { + password: ArrayBuffer; + }, crypto = common.getCrypto(true)) { + //#region Initial variables + const cmsEncrypted = new EncryptedData({ + encryptedContentInfo: new EncryptedContentInfo({ + contentEncryptionAlgorithm: this.encryptionAlgorithm, + encryptedContent: this.encryptedData + }) + }); + //#endregion + + //#region Decrypt internal data + const decryptedData = await cmsEncrypted.decrypt(parameters, crypto); + + //#endregion + + //#region Initialize PARSED_VALUE with decrypted PKCS#8 private key + + this.parsedValue = PrivateKeyInfo.fromBER(decryptedData); + //#endregion + } + + public async makeInternalValues(parameters: PKCS8ShroudedKeyBagMakeInternalValuesParams): Promise<void> { + //#region Check that we do have PARSED_VALUE + if (!this.parsedValue) { + throw new Error("Please initialize \"parsedValue\" first"); + } + //#endregion + + //#region Initial variables + const cmsEncrypted = new EncryptedData(); + //#endregion + + //#region Encrypt internal data + const encryptParams: EncryptedDataEncryptParams = { + ...parameters, + contentToEncrypt: this.parsedValue.toSchema().toBER(false), + }; + + await cmsEncrypted.encrypt(encryptParams); + if (!cmsEncrypted.encryptedContentInfo.encryptedContent) { + throw new Error("The filed `encryptedContent` in EncryptedContentInfo is empty"); + } + + //#endregion + + //#region Initialize internal values + this.encryptionAlgorithm = cmsEncrypted.encryptedContentInfo.contentEncryptionAlgorithm; + this.encryptedData = cmsEncrypted.encryptedContentInfo.encryptedContent; + //#endregion + } + +} |