mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +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:
@@ -174,7 +174,7 @@ describe("DefaultSetPasswordJitService", () => {
|
||||
}
|
||||
|
||||
keyService.userKey$.mockReturnValue(of(userKey));
|
||||
encryptService.rsaEncrypt.mockResolvedValue(userKeyEncString);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(userKeyEncString);
|
||||
|
||||
organizationUserApiService.putOrganizationUserResetPasswordEnrollment.mockResolvedValue(
|
||||
undefined,
|
||||
@@ -216,7 +216,7 @@ describe("DefaultSetPasswordJitService", () => {
|
||||
// Assert
|
||||
expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest);
|
||||
expect(organizationApiService.getKeys).toHaveBeenCalledWith(orgId);
|
||||
expect(encryptService.rsaEncrypt).toHaveBeenCalledWith(userKey.key, orgPublicKey);
|
||||
expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith(userKey, orgPublicKey);
|
||||
expect(
|
||||
organizationUserApiService.putOrganizationUserResetPasswordEnrollment,
|
||||
).toHaveBeenCalled();
|
||||
|
||||
@@ -161,7 +161,7 @@ export class DefaultSetPasswordJitService implements SetPasswordJitService {
|
||||
throw new Error("userKey not found. Could not handle reset password auto enroll.");
|
||||
}
|
||||
|
||||
const encryptedUserKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey);
|
||||
const encryptedUserKey = await this.encryptService.encapsulateKeyUnsigned(userKey, publicKey);
|
||||
|
||||
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
|
||||
resetRequest.masterPasswordHash = masterKeyHash;
|
||||
|
||||
@@ -231,7 +231,9 @@ describe("WebAuthnLoginStrategy", () => {
|
||||
const mockUserKey = new SymmetricCryptoKey(mockUserKeyArray) as UserKey;
|
||||
|
||||
encryptService.decryptToBytes.mockResolvedValue(mockPrfPrivateKey);
|
||||
encryptService.rsaDecrypt.mockResolvedValue(mockUserKeyArray);
|
||||
encryptService.decapsulateKeyUnsigned.mockResolvedValue(
|
||||
new SymmetricCryptoKey(mockUserKeyArray),
|
||||
);
|
||||
|
||||
// Act
|
||||
await webAuthnLoginStrategy.logIn(webAuthnCredentials);
|
||||
@@ -249,8 +251,8 @@ describe("WebAuthnLoginStrategy", () => {
|
||||
idTokenResponse.userDecryptionOptions.webAuthnPrfOption.encryptedPrivateKey,
|
||||
webAuthnCredentials.prfKey,
|
||||
);
|
||||
expect(encryptService.rsaDecrypt).toHaveBeenCalledTimes(1);
|
||||
expect(encryptService.rsaDecrypt).toHaveBeenCalledWith(
|
||||
expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledTimes(1);
|
||||
expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith(
|
||||
idTokenResponse.userDecryptionOptions.webAuthnPrfOption.encryptedUserKey,
|
||||
mockPrfPrivateKey,
|
||||
);
|
||||
@@ -278,7 +280,7 @@ describe("WebAuthnLoginStrategy", () => {
|
||||
|
||||
// Assert
|
||||
expect(encryptService.decryptToBytes).not.toHaveBeenCalled();
|
||||
expect(encryptService.rsaDecrypt).not.toHaveBeenCalled();
|
||||
expect(encryptService.decapsulateKeyUnsigned).not.toHaveBeenCalled();
|
||||
expect(keyService.setUserKey).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -330,7 +332,7 @@ describe("WebAuthnLoginStrategy", () => {
|
||||
|
||||
apiService.postIdentityToken.mockResolvedValue(idTokenResponse);
|
||||
|
||||
encryptService.rsaDecrypt.mockResolvedValue(null);
|
||||
encryptService.decapsulateKeyUnsigned.mockResolvedValue(null);
|
||||
|
||||
// Act
|
||||
await webAuthnLoginStrategy.logIn(webAuthnCredentials);
|
||||
|
||||
@@ -7,7 +7,6 @@ import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { WebAuthnLoginTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/webauthn-login-token.request";
|
||||
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
|
||||
import { 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";
|
||||
|
||||
@@ -89,13 +88,13 @@ export class WebAuthnLoginStrategy extends LoginStrategy {
|
||||
);
|
||||
|
||||
// decrypt user key with private key
|
||||
const userKey = await this.encryptService.rsaDecrypt(
|
||||
const userKey = await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(webAuthnPrfOption.encryptedUserKey.encryptedString),
|
||||
privateKey,
|
||||
);
|
||||
|
||||
if (userKey) {
|
||||
await this.keyService.setUserKey(new SymmetricCryptoKey(userKey) as UserKey, userId);
|
||||
await this.keyService.setUserKey(userKey as UserKey, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,9 @@ describe("AuthRequestService", () => {
|
||||
encryptService.rsaEncrypt.mockResolvedValue({
|
||||
encryptedString: "ENCRYPTED_STRING",
|
||||
} as EncString);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue({
|
||||
encryptedString: "ENCRYPTED_STRING",
|
||||
} as EncString);
|
||||
appIdService.getAppId.mockResolvedValue("APP_ID");
|
||||
});
|
||||
it("should throw if auth request is missing id or key", async () => {
|
||||
@@ -111,7 +114,10 @@ describe("AuthRequestService", () => {
|
||||
new AuthRequestResponse({ id: "123", publicKey: "KEY" }),
|
||||
);
|
||||
|
||||
expect(encryptService.rsaEncrypt).toHaveBeenCalledWith(new Uint8Array(64), expect.anything());
|
||||
expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith(
|
||||
{ encKey: new Uint8Array(64) },
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it("should use the user key if the master key and hash do not exist", async () => {
|
||||
@@ -122,7 +128,10 @@ describe("AuthRequestService", () => {
|
||||
new AuthRequestResponse({ id: "123", publicKey: "KEY" }),
|
||||
);
|
||||
|
||||
expect(encryptService.rsaEncrypt).toHaveBeenCalledWith(new Uint8Array(64), expect.anything());
|
||||
expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith(
|
||||
{ key: new Uint8Array(64) },
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe("setUserKeyAfterDecryptingSharedUserKey", () => {
|
||||
@@ -214,7 +223,9 @@ describe("AuthRequestService", () => {
|
||||
const mockDecryptedUserKeyBytes = new Uint8Array(64);
|
||||
const mockDecryptedUserKey = new SymmetricCryptoKey(mockDecryptedUserKeyBytes) as UserKey;
|
||||
|
||||
encryptService.rsaDecrypt.mockResolvedValueOnce(mockDecryptedUserKeyBytes);
|
||||
encryptService.decapsulateKeyUnsigned.mockResolvedValueOnce(
|
||||
new SymmetricCryptoKey(mockDecryptedUserKeyBytes),
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await sut.decryptPubKeyEncryptedUserKey(
|
||||
@@ -223,7 +234,7 @@ describe("AuthRequestService", () => {
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(encryptService.rsaDecrypt).toBeCalledWith(
|
||||
expect(encryptService.decapsulateKeyUnsigned).toBeCalledWith(
|
||||
new EncString(mockPubKeyEncryptedUserKey),
|
||||
mockPrivateKey,
|
||||
);
|
||||
@@ -244,9 +255,10 @@ describe("AuthRequestService", () => {
|
||||
const mockDecryptedMasterKeyHashBytes = new Uint8Array(64);
|
||||
const mockDecryptedMasterKeyHash = Utils.fromBufferToUtf8(mockDecryptedMasterKeyHashBytes);
|
||||
|
||||
encryptService.rsaDecrypt
|
||||
.mockResolvedValueOnce(mockDecryptedMasterKeyBytes)
|
||||
.mockResolvedValueOnce(mockDecryptedMasterKeyHashBytes);
|
||||
encryptService.rsaDecrypt.mockResolvedValueOnce(mockDecryptedMasterKeyHashBytes);
|
||||
encryptService.decapsulateKeyUnsigned.mockResolvedValueOnce(
|
||||
new SymmetricCryptoKey(mockDecryptedMasterKeyBytes),
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await sut.decryptPubKeyEncryptedMasterKeyAndHash(
|
||||
@@ -256,13 +268,11 @@ describe("AuthRequestService", () => {
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(encryptService.rsaDecrypt).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith(
|
||||
new EncString(mockPubKeyEncryptedMasterKey),
|
||||
mockPrivateKey,
|
||||
);
|
||||
expect(encryptService.rsaDecrypt).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect(encryptService.rsaDecrypt).toHaveBeenCalledWith(
|
||||
new EncString(mockPubKeyEncryptedMasterKeyHash),
|
||||
mockPrivateKey,
|
||||
);
|
||||
|
||||
@@ -14,7 +14,6 @@ import { AuthRequestPushNotification } from "@bitwarden/common/models/response/n
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import {
|
||||
AUTH_REQUEST_DISK_LOCAL,
|
||||
StateProvider,
|
||||
@@ -116,13 +115,12 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
|
||||
Utils.fromUtf8ToArray(masterKeyHash),
|
||||
pubKey,
|
||||
);
|
||||
keyToEncrypt = masterKey.encKey;
|
||||
keyToEncrypt = masterKey;
|
||||
} else {
|
||||
const userKey = await this.keyService.getUserKey();
|
||||
keyToEncrypt = userKey.key;
|
||||
keyToEncrypt = await this.keyService.getUserKey();
|
||||
}
|
||||
|
||||
const encryptedKey = await this.encryptService.rsaEncrypt(keyToEncrypt, pubKey);
|
||||
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(keyToEncrypt, pubKey);
|
||||
|
||||
const response = new PasswordlessAuthRequest(
|
||||
encryptedKey.encryptedString,
|
||||
@@ -171,12 +169,10 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
|
||||
pubKeyEncryptedUserKey: string,
|
||||
privateKey: Uint8Array,
|
||||
): Promise<UserKey> {
|
||||
const decryptedUserKeyBytes = await this.encryptService.rsaDecrypt(
|
||||
return (await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(pubKeyEncryptedUserKey),
|
||||
privateKey,
|
||||
);
|
||||
|
||||
return new SymmetricCryptoKey(decryptedUserKeyBytes) as UserKey;
|
||||
)) as UserKey;
|
||||
}
|
||||
|
||||
async decryptPubKeyEncryptedMasterKeyAndHash(
|
||||
@@ -184,17 +180,15 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
|
||||
pubKeyEncryptedMasterKeyHash: string,
|
||||
privateKey: Uint8Array,
|
||||
): Promise<{ masterKey: MasterKey; masterKeyHash: string }> {
|
||||
const decryptedMasterKeyArrayBuffer = await this.encryptService.rsaDecrypt(
|
||||
const masterKey = (await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(pubKeyEncryptedMasterKey),
|
||||
privateKey,
|
||||
);
|
||||
)) as MasterKey;
|
||||
|
||||
const decryptedMasterKeyHashArrayBuffer = await this.encryptService.rsaDecrypt(
|
||||
new EncString(pubKeyEncryptedMasterKeyHash),
|
||||
privateKey,
|
||||
);
|
||||
|
||||
const masterKey = new SymmetricCryptoKey(decryptedMasterKeyArrayBuffer) as MasterKey;
|
||||
const masterKeyHash = Utils.fromBufferToUtf8(decryptedMasterKeyHashArrayBuffer);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user