1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[PS-11868] Require key for enc string decryption (#10981)

* Specify enc string decryption key and service.

* Fix issue with identifying `this` type within extended classes

* Folder decryption example

* Test enc string changes

* Fix test name

* test decrypt with key
This commit is contained in:
Matt Gibson
2024-09-30 06:34:03 -07:00
committed by GitHub
parent cc9a72616a
commit a6b9088940
7 changed files with 358 additions and 4 deletions

View File

@@ -1,8 +1,18 @@
import { ConditionalExcept, ConditionalKeys, Constructor } from "type-fest";
import { View } from "../../../models/view/view";
import { EncryptService } from "../../abstractions/encrypt.service";
import { EncString } from "./enc-string";
import { SymmetricCryptoKey } from "./symmetric-crypto-key";
// eslint-disable-next-line @typescript-eslint/ban-types
type EncStringKeys<T> = ConditionalKeys<ConditionalExcept<T, Function>, EncString>;
export type DecryptedObject<
TEncryptedObject,
TDecryptedKeys extends EncStringKeys<TEncryptedObject>,
> = Record<TDecryptedKeys, string> & Omit<TEncryptedObject, TDecryptedKeys>;
// https://contributing.bitwarden.com/architecture/clients/data-model#domain
export default class Domain {
protected buildDomainModel<D extends Domain>(
@@ -80,4 +90,60 @@ export default class Domain {
await Promise.all(promises);
return viewModel;
}
/**
* Decrypts the requested properties of the domain object with the provided key and encrypt service.
*
* If a property is null, the result will be null.
* @see {@link EncString.decryptWithKey} for more details on decryption behavior.
*
* @param encryptedProperties The properties to decrypt. Type restricted to EncString properties of the domain object.
* @param key The key to use for decryption.
* @param encryptService The encryption service to use for decryption.
* @param _ The constructor of the domain object. Used for type inference if the domain object is not automatically inferred.
* @returns An object with the requested properties decrypted and the rest of the domain object untouched.
*/
protected async decryptObjWithKey<
TThis extends Domain,
const TEncryptedKeys extends EncStringKeys<TThis>,
>(
this: TThis,
encryptedProperties: TEncryptedKeys[],
key: SymmetricCryptoKey,
encryptService: EncryptService,
_: Constructor<TThis> = this.constructor as Constructor<TThis>,
): Promise<DecryptedObject<TThis, TEncryptedKeys>> {
const promises = [];
for (const prop of encryptedProperties) {
const value = (this as any)[prop] as EncString;
promises.push(this.decryptProperty(prop, value, key, encryptService));
}
const decryptedObjects = await Promise.all(promises);
const decryptedObject = decryptedObjects.reduce(
(acc, obj) => {
return { ...acc, ...obj };
},
{ ...this },
);
return decryptedObject as DecryptedObject<TThis, TEncryptedKeys>;
}
private async decryptProperty<const TEncryptedKeys extends EncStringKeys<this>>(
propertyKey: TEncryptedKeys,
value: EncString,
key: SymmetricCryptoKey,
encryptService: EncryptService,
) {
let decrypted: string = null;
if (value) {
decrypted = await value.decryptWithKey(key, encryptService);
} else {
decrypted = null;
}
return {
[propertyKey]: decrypted,
};
}
}