mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 13:23:34 +00:00
[PM-19603] Change asymmetric interface to only allow key encapsulation (#14046)
* Change asymmetric interface to only allow key encapsulation * Fix naming * Clean up naming * Update libs/common/src/key-management/crypto/abstractions/encrypt.service.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update libs/common/src/key-management/crypto/abstractions/encrypt.service.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Fix test --------- Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>
This commit is contained in:
@@ -71,7 +71,7 @@ export abstract class BaseBulkConfirmComponent implements OnInit {
|
||||
if (publicKey == null) {
|
||||
continue;
|
||||
}
|
||||
const encryptedKey = await this.encryptService.rsaEncrypt(key.key, publicKey);
|
||||
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(key, publicKey);
|
||||
userIdsWithKeys.push({
|
||||
id: user.id,
|
||||
key: encryptedKey.encryptedString,
|
||||
|
||||
@@ -322,7 +322,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
|
||||
async confirmUser(user: OrganizationUserView, publicKey: Uint8Array): Promise<void> {
|
||||
const orgKey = await this.keyService.getOrgKey(this.organization.id);
|
||||
const key = await this.encryptService.rsaEncrypt(orgKey.key, publicKey);
|
||||
const key = await this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey);
|
||||
const request = new OrganizationUserConfirmRequest();
|
||||
request.key = key.encryptedString;
|
||||
await this.organizationUserApiService.postOrganizationUserConfirm(
|
||||
|
||||
@@ -89,7 +89,7 @@ describe("OrganizationUserResetPasswordService", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
encryptService.rsaEncrypt.mockResolvedValue(
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(
|
||||
new EncString(EncryptionType.Rsa2048_OaepSha1_B64, "mockEncryptedUserKey"),
|
||||
);
|
||||
});
|
||||
@@ -111,7 +111,10 @@ describe("OrganizationUserResetPasswordService", () => {
|
||||
|
||||
it("should rsa encrypt the user key", async () => {
|
||||
await sut.buildRecoveryKey(mockOrgId, mockUserKey, mockPublicKeys);
|
||||
expect(encryptService.rsaEncrypt).toHaveBeenCalledWith(expect.anything(), expect.anything());
|
||||
expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw an error if the public key is not trusted", async () => {
|
||||
@@ -199,7 +202,7 @@ describe("OrganizationUserResetPasswordService", () => {
|
||||
publicKey: Utils.fromUtf8ToArray("test-public-key"),
|
||||
}),
|
||||
);
|
||||
encryptService.rsaEncrypt.mockResolvedValue(
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(
|
||||
new EncString(EncryptionType.Rsa2048_OaepSha1_B64, "mockEncryptedUserKey"),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -14,7 +14,6 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { UserKey } from "@bitwarden/common/types/key";
|
||||
import {
|
||||
@@ -59,6 +58,10 @@ export class OrganizationUserResetPasswordService
|
||||
userKey: UserKey,
|
||||
trustedPublicKeys: Uint8Array[],
|
||||
): Promise<EncryptedString> {
|
||||
if (userKey == null) {
|
||||
throw new Error("User key is required for recovery.");
|
||||
}
|
||||
|
||||
// Retrieve Public Key
|
||||
const orgKeys = await this.organizationApiService.getKeys(orgId);
|
||||
if (orgKeys == null) {
|
||||
@@ -76,7 +79,8 @@ export class OrganizationUserResetPasswordService
|
||||
}
|
||||
|
||||
// RSA Encrypt user key with organization's public key
|
||||
const encryptedKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey);
|
||||
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(userKey, publicKey);
|
||||
|
||||
return encryptedKey.encryptedString;
|
||||
}
|
||||
|
||||
@@ -114,11 +118,11 @@ export class OrganizationUserResetPasswordService
|
||||
);
|
||||
|
||||
// Decrypt User's Reset Password Key to get UserKey
|
||||
const decValue = await this.encryptService.rsaDecrypt(
|
||||
const userKey = await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(response.resetPasswordKey),
|
||||
decPrivateKey,
|
||||
);
|
||||
const existingUserKey = new SymmetricCryptoKey(decValue) as UserKey;
|
||||
const existingUserKey = userKey as UserKey;
|
||||
|
||||
// determine Kdf Algorithm
|
||||
const kdfConfig: KdfConfig =
|
||||
|
||||
@@ -35,7 +35,7 @@ describe("RotateableKeySetService", () => {
|
||||
const encryptedPrivateKey = Symbol();
|
||||
keyService.makeKeyPair.mockResolvedValue(["publicKey", encryptedPrivateKey as any]);
|
||||
keyService.getUserKey.mockResolvedValue({ key: userKey.key } as any);
|
||||
encryptService.rsaEncrypt.mockResolvedValue(encryptedUserKey as any);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedUserKey as any);
|
||||
encryptService.encrypt.mockResolvedValue(encryptedPublicKey as any);
|
||||
|
||||
const result = await service.createKeySet(externalKey as any);
|
||||
|
||||
@@ -25,7 +25,10 @@ export class RotateableKeySetService {
|
||||
|
||||
const userKey = await this.keyService.getUserKey();
|
||||
const rawPublicKey = Utils.fromB64ToArray(publicKey);
|
||||
const encryptedUserKey = await this.encryptService.rsaEncrypt(userKey.key, rawPublicKey);
|
||||
const encryptedUserKey = await this.encryptService.encapsulateKeyUnsigned(
|
||||
userKey,
|
||||
rawPublicKey,
|
||||
);
|
||||
const encryptedPublicKey = await this.encryptService.encrypt(rawPublicKey, userKey);
|
||||
return new RotateableKeySet(encryptedUserKey, encryptedPublicKey, encryptedPrivateKey);
|
||||
}
|
||||
@@ -60,7 +63,10 @@ export class RotateableKeySetService {
|
||||
throw new Error("failed to rotate key set: could not decrypt public key");
|
||||
}
|
||||
const newEncryptedPublicKey = await this.encryptService.encrypt(publicKey, newUserKey);
|
||||
const newEncryptedUserKey = await this.encryptService.rsaEncrypt(newUserKey.key, publicKey);
|
||||
const newEncryptedUserKey = await this.encryptService.encapsulateKeyUnsigned(
|
||||
newUserKey,
|
||||
publicKey,
|
||||
);
|
||||
|
||||
const newRotateableKeySet = new RotateableKeySet<ExternalKey>(
|
||||
newEncryptedUserKey,
|
||||
|
||||
@@ -130,7 +130,9 @@ describe("EmergencyAccessService", () => {
|
||||
|
||||
keyService.getUserKey.mockResolvedValueOnce(mockUserKey);
|
||||
|
||||
encryptService.rsaEncrypt.mockResolvedValueOnce(mockUserPublicKeyEncryptedUserKey);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValueOnce(
|
||||
mockUserPublicKeyEncryptedUserKey,
|
||||
);
|
||||
|
||||
emergencyAccessApiService.postEmergencyAccessConfirm.mockResolvedValueOnce();
|
||||
|
||||
@@ -160,7 +162,9 @@ describe("EmergencyAccessService", () => {
|
||||
|
||||
const mockDecryptedGrantorUserKey = new Uint8Array(64);
|
||||
keyService.getPrivateKey.mockResolvedValue(new Uint8Array(64));
|
||||
encryptService.rsaDecrypt.mockResolvedValueOnce(mockDecryptedGrantorUserKey);
|
||||
encryptService.decapsulateKeyUnsigned.mockResolvedValueOnce(
|
||||
new SymmetricCryptoKey(mockDecryptedGrantorUserKey),
|
||||
);
|
||||
|
||||
const mockMasterKey = new SymmetricCryptoKey(new Uint8Array(64) as CsprngArray) as MasterKey;
|
||||
|
||||
@@ -253,7 +257,7 @@ describe("EmergencyAccessService", () => {
|
||||
publicKey: Utils.fromUtf8ToB64("trustedPublicKey"),
|
||||
} as UserKeyResponse);
|
||||
|
||||
encryptService.rsaEncrypt.mockImplementation((plainValue, publicKey) => {
|
||||
encryptService.encapsulateKeyUnsigned.mockImplementation((plainValue, publicKey) => {
|
||||
return Promise.resolve(
|
||||
new EncString(EncryptionType.Rsa2048_OaepSha1_B64, "Encrypted: " + plainValue),
|
||||
);
|
||||
|
||||
@@ -12,7 +12,6 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { UserKey } from "@bitwarden/common/types/key";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@@ -234,11 +233,10 @@ export class EmergencyAccessService
|
||||
throw new Error("Active user does not have a private key, cannot get view only ciphers.");
|
||||
}
|
||||
|
||||
const grantorKeyBuffer = await this.encryptService.rsaDecrypt(
|
||||
const grantorUserKey = (await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(response.keyEncrypted),
|
||||
activeUserPrivateKey,
|
||||
);
|
||||
const grantorUserKey = new SymmetricCryptoKey(grantorKeyBuffer) as UserKey;
|
||||
)) as UserKey;
|
||||
|
||||
let ciphers: CipherView[] = [];
|
||||
if (await this.configService.getFeatureFlag(FeatureFlag.PM4154_BulkEncryptionService)) {
|
||||
@@ -271,15 +269,15 @@ export class EmergencyAccessService
|
||||
throw new Error("Active user does not have a private key, cannot complete a takeover.");
|
||||
}
|
||||
|
||||
const grantorKeyBuffer = await this.encryptService.rsaDecrypt(
|
||||
const grantorKey = await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(takeoverResponse.keyEncrypted),
|
||||
activeUserPrivateKey,
|
||||
);
|
||||
if (grantorKeyBuffer == null) {
|
||||
if (grantorKey == null) {
|
||||
throw new Error("Failed to decrypt grantor key");
|
||||
}
|
||||
|
||||
const grantorUserKey = new SymmetricCryptoKey(grantorKeyBuffer) as UserKey;
|
||||
const grantorUserKey = grantorKey as UserKey;
|
||||
|
||||
let config: KdfConfig;
|
||||
|
||||
@@ -407,6 +405,6 @@ export class EmergencyAccessService
|
||||
}
|
||||
|
||||
private async encryptKey(userKey: UserKey, publicKey: Uint8Array): Promise<EncryptedString> {
|
||||
return (await this.encryptService.rsaEncrypt(userKey.key, publicKey)).encryptedString;
|
||||
return (await this.encryptService.encapsulateKeyUnsigned(userKey, publicKey)).encryptedString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ describe("AcceptOrganizationInviteService", () => {
|
||||
);
|
||||
accountService.activeAccount$ = new BehaviorSubject({ id: "activeUserId" }) as any;
|
||||
keyService.userKey$.mockReturnValue(new BehaviorSubject({ key: "userKey" } as any));
|
||||
encryptService.rsaEncrypt.mockResolvedValue({
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue({
|
||||
encryptedString: "encryptedString",
|
||||
} as EncString);
|
||||
|
||||
@@ -218,8 +218,8 @@ describe("AcceptOrganizationInviteService", () => {
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(OrganizationTrustComponent.open).toHaveBeenCalled();
|
||||
expect(encryptService.rsaEncrypt).toHaveBeenCalledWith(
|
||||
"userKey",
|
||||
expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith(
|
||||
{ key: "userKey" },
|
||||
Utils.fromB64ToArray("publicKey"),
|
||||
);
|
||||
expect(organizationUserApiService.postOrganizationUserAccept).toHaveBeenCalled();
|
||||
|
||||
@@ -202,7 +202,7 @@ export class AcceptOrganizationInviteService {
|
||||
const activeUserId = (await firstValueFrom(this.accountService.activeAccount$)).id;
|
||||
const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId));
|
||||
// RSA Encrypt user's encKey.key with organization public key
|
||||
const encryptedKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey);
|
||||
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(userKey, publicKey);
|
||||
|
||||
// Add reset password key to accept request
|
||||
request.resetPasswordKey = encryptedKey.encryptedString;
|
||||
|
||||
Reference in New Issue
Block a user