1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 00:03:56 +00:00

[PM-10607] Require userId for getKeyForCipherKeyDecryption (#10509)

* updated cipher service to stop using the deprecated getUserKeyWithLegacySupport and use the version that requires a user id

* Added account service mock

* fixed cipher test

* Fixed test

* removed async from encryptCipher

* updated encryptSharedCipher to pass userId to the encrypt function

* Pass userId to getUserKeyWithLegacySupport on encryptSharedCipher

* pass in userid when setting masterKeyEncryptedUserKey

* Added activer usedId to new web refresh function
This commit is contained in:
SmithThe4th
2024-08-20 12:00:48 -04:00
committed by GitHub
parent ed719f835a
commit dedd7f1b5c
67 changed files with 534 additions and 118 deletions

View File

@@ -165,6 +165,7 @@ export class CipherService implements CipherServiceAbstraction {
async encrypt(
model: CipherView,
userId: UserId,
keyForEncryption?: SymmetricCryptoKey,
keyForCipherKeyDecryption?: SymmetricCryptoKey,
originalCipher: Cipher = null,
@@ -174,7 +175,7 @@ export class CipherService implements CipherServiceAbstraction {
originalCipher = await this.get(model.id);
}
if (originalCipher != null) {
await this.updateModelfromExistingCipher(model, originalCipher);
await this.updateModelfromExistingCipher(model, originalCipher, userId);
}
this.adjustPasswordHistoryLength(model);
}
@@ -192,7 +193,7 @@ export class CipherService implements CipherServiceAbstraction {
if (await this.getCipherKeyEncryptionEnabled()) {
cipher.key = originalCipher?.key ?? null;
const userOrOrgKey = await this.getKeyForCipherKeyDecryption(cipher);
const userOrOrgKey = await this.getKeyForCipherKeyDecryption(cipher, userId);
// The keyForEncryption is only used for encrypting the cipher key, not the cipher itself, since cipher key encryption is enabled.
// If the caller has provided a key for cipher key encryption, use it. Otherwise, use the user or org key.
keyForEncryption ||= userOrOrgKey;
@@ -718,6 +719,7 @@ export class CipherService implements CipherServiceAbstraction {
cipher: CipherView,
organizationId: string,
collectionIds: string[],
userId: UserId,
): Promise<any> {
const attachmentPromises: Promise<any>[] = [];
if (cipher.attachments != null) {
@@ -733,7 +735,7 @@ export class CipherService implements CipherServiceAbstraction {
cipher.organizationId = organizationId;
cipher.collectionIds = collectionIds;
const encCipher = await this.encryptSharedCipher(cipher);
const encCipher = await this.encryptSharedCipher(cipher, userId);
const request = new CipherShareRequest(encCipher);
const response = await this.apiService.putShareCipher(cipher.id, request);
const data = new CipherData(response, collectionIds);
@@ -744,6 +746,7 @@ export class CipherService implements CipherServiceAbstraction {
ciphers: CipherView[],
organizationId: string,
collectionIds: string[],
userId: UserId,
): Promise<any> {
const promises: Promise<any>[] = [];
const encCiphers: Cipher[] = [];
@@ -751,7 +754,7 @@ export class CipherService implements CipherServiceAbstraction {
cipher.organizationId = organizationId;
cipher.collectionIds = collectionIds;
promises.push(
this.encryptSharedCipher(cipher).then((c) => {
this.encryptSharedCipher(cipher, userId).then((c) => {
encCiphers.push(c);
}),
);
@@ -770,7 +773,12 @@ export class CipherService implements CipherServiceAbstraction {
await this.upsert(encCiphers.map((c) => c.toCipherData()));
}
saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any, admin = false): Promise<Cipher> {
saveAttachmentWithServer(
cipher: Cipher,
unencryptedFile: any,
userId: UserId,
admin = false,
): Promise<Cipher> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsArrayBuffer(unencryptedFile);
@@ -780,6 +788,7 @@ export class CipherService implements CipherServiceAbstraction {
cipher,
unencryptedFile.name,
evt.target.result,
userId,
admin,
);
resolve(cData);
@@ -797,9 +806,10 @@ export class CipherService implements CipherServiceAbstraction {
cipher: Cipher,
filename: string,
data: Uint8Array,
userId: UserId,
admin = false,
): Promise<Cipher> {
const encKey = await this.getKeyForCipherKeyDecryption(cipher);
const encKey = await this.getKeyForCipherKeyDecryption(cipher, userId);
const cipherKeyEncryptionEnabled = await this.getCipherKeyEncryptionEnabled();
const cipherEncKey =
@@ -813,8 +823,8 @@ export class CipherService implements CipherServiceAbstraction {
//then we rollback to using the user key as the main key of encryption of the item
//in order to keep item and it's attachments with the same encryption level
if (cipher.key != null && !cipherKeyEncryptionEnabled) {
const model = await cipher.decrypt(await this.getKeyForCipherKeyDecryption(cipher));
cipher = await this.encrypt(model);
const model = await cipher.decrypt(await this.getKeyForCipherKeyDecryption(cipher, userId));
cipher = await this.encrypt(model, userId);
await this.updateWithServer(cipher);
}
@@ -1209,10 +1219,10 @@ export class CipherService implements CipherServiceAbstraction {
await this.restore(restores);
}
async getKeyForCipherKeyDecryption(cipher: Cipher): Promise<UserKey | OrgKey> {
async getKeyForCipherKeyDecryption(cipher: Cipher, userId: UserId): Promise<UserKey | OrgKey> {
return (
(await this.cryptoService.getOrgKey(cipher.organizationId)) ||
((await this.cryptoService.getUserKeyWithLegacySupport()) as UserKey)
((await this.cryptoService.getUserKeyWithLegacySupport(userId)) as UserKey)
);
}
@@ -1247,7 +1257,7 @@ export class CipherService implements CipherServiceAbstraction {
}
encryptedCiphers = await Promise.all(
userCiphers.map(async (cipher) => {
const encryptedCipher = await this.encrypt(cipher, newUserKey, originalUserKey);
const encryptedCipher = await this.encrypt(cipher, userId, newUserKey, originalUserKey);
return new CipherWithIdRequest(encryptedCipher);
}),
);
@@ -1259,17 +1269,18 @@ export class CipherService implements CipherServiceAbstraction {
// In the case of a cipher that is being shared with an organization, we want to decrypt the
// cipher key with the user's key and then re-encrypt it with the organization's key.
private async encryptSharedCipher(model: CipherView): Promise<Cipher> {
const keyForCipherKeyDecryption = await this.cryptoService.getUserKeyWithLegacySupport();
return await this.encrypt(model, null, keyForCipherKeyDecryption);
private async encryptSharedCipher(model: CipherView, userId: UserId): Promise<Cipher> {
const keyForCipherKeyDecryption = await this.cryptoService.getUserKeyWithLegacySupport(userId);
return await this.encrypt(model, userId, null, keyForCipherKeyDecryption);
}
private async updateModelfromExistingCipher(
model: CipherView,
originalCipher: Cipher,
userId: UserId,
): Promise<void> {
const existingCipher = await originalCipher.decrypt(
await this.getKeyForCipherKeyDecryption(originalCipher),
await this.getKeyForCipherKeyDecryption(originalCipher, userId),
);
model.passwordHistory = existingCipher.passwordHistory || [];
if (model.type === CipherType.Login && existingCipher.type === CipherType.Login) {