mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 21:33:27 +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;
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
||||
@@ -124,8 +125,10 @@ describe("OrganizationAuthRequestService", () => {
|
||||
);
|
||||
|
||||
const encryptedUserKey = new EncString("encryptedUserKey");
|
||||
encryptService.rsaDecrypt.mockResolvedValue(new Uint8Array(32));
|
||||
encryptService.rsaEncrypt.mockResolvedValue(encryptedUserKey);
|
||||
encryptService.decapsulateKeyUnsigned.mockResolvedValue(
|
||||
new SymmetricCryptoKey(new Uint8Array(32)),
|
||||
);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedUserKey);
|
||||
|
||||
const mockPendingAuthRequest = new PendingAuthRequestView();
|
||||
mockPendingAuthRequest.id = "requestId1";
|
||||
@@ -166,8 +169,10 @@ describe("OrganizationAuthRequestService", () => {
|
||||
);
|
||||
|
||||
const encryptedUserKey = new EncString("encryptedUserKey");
|
||||
encryptService.rsaDecrypt.mockResolvedValue(new Uint8Array(32));
|
||||
encryptService.rsaEncrypt.mockResolvedValue(encryptedUserKey);
|
||||
encryptService.decapsulateKeyUnsigned.mockResolvedValue(
|
||||
new SymmetricCryptoKey(new Uint8Array(32)),
|
||||
);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedUserKey);
|
||||
|
||||
const mockPendingAuthRequest = new PendingAuthRequestView();
|
||||
mockPendingAuthRequest.id = "requestId1";
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.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 { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
||||
@@ -119,13 +118,12 @@ export class OrganizationAuthRequestService {
|
||||
);
|
||||
|
||||
// Decrypt user key with decrypted org private key
|
||||
const decValue = await this.encryptService.rsaDecrypt(
|
||||
const userKey = await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(encryptedUserKey),
|
||||
decOrgPrivateKey,
|
||||
);
|
||||
const userKey = new SymmetricCryptoKey(decValue);
|
||||
|
||||
// Re-encrypt user Key with the Device Public Key
|
||||
return await this.encryptService.rsaEncrypt(userKey.key, devicePubKey);
|
||||
return await this.encryptService.encapsulateKeyUnsigned(userKey, devicePubKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ export class MembersComponent extends BaseMembersComponent<ProviderUser> {
|
||||
|
||||
async confirmUser(user: ProviderUser, publicKey: Uint8Array): Promise<void> {
|
||||
const providerKey = await this.keyService.getProviderKey(this.providerId);
|
||||
const key = await this.encryptService.rsaEncrypt(providerKey.key, publicKey);
|
||||
const key = await this.encryptService.encapsulateKeyUnsigned(providerKey, publicKey);
|
||||
const request = new ProviderUserConfirmRequest();
|
||||
request.key = key.encryptedString;
|
||||
await this.apiService.postProviderUserConfirm(this.providerId, user.id, request);
|
||||
|
||||
@@ -211,7 +211,10 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements
|
||||
|
||||
// RSA Encrypt user key with organization public key
|
||||
const userKey = await this.keyService.getUserKey();
|
||||
const encryptedUserKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey);
|
||||
const encryptedUserKey = await this.encryptService.encapsulateKeyUnsigned(
|
||||
userKey,
|
||||
publicKey,
|
||||
);
|
||||
|
||||
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
|
||||
resetRequest.masterPasswordHash = masterPasswordHash;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -31,8 +31,10 @@ export class EncryptedOrganizationKey implements BaseEncryptedOrganizationKey {
|
||||
constructor(private key: string) {}
|
||||
|
||||
async decrypt(encryptService: EncryptService, privateKey: UserPrivateKey) {
|
||||
const decValue = await encryptService.rsaDecrypt(this.encryptedOrganizationKey, privateKey);
|
||||
return new SymmetricCryptoKey(decValue) as OrgKey;
|
||||
return (await encryptService.decapsulateKeyUnsigned(
|
||||
this.encryptedOrganizationKey,
|
||||
privateKey,
|
||||
)) as OrgKey;
|
||||
}
|
||||
|
||||
get encryptedOrganizationKey() {
|
||||
|
||||
@@ -100,7 +100,7 @@ describe("PasswordResetEnrollmentServiceImplementation", () => {
|
||||
activeAccountSubject.next(Object.assign(user1AccountInfo, { id: "userId" as UserId }));
|
||||
|
||||
keyService.getUserKey.mockResolvedValue({ key: "key" } as any);
|
||||
encryptService.rsaEncrypt.mockResolvedValue(encryptedKey as any);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedKey as any);
|
||||
|
||||
await service.enroll("orgId");
|
||||
|
||||
@@ -122,7 +122,7 @@ describe("PasswordResetEnrollmentServiceImplementation", () => {
|
||||
};
|
||||
const encryptedKey = { encryptedString: "encryptedString" };
|
||||
organizationApiService.getKeys.mockResolvedValue(orgKeyResponse as any);
|
||||
encryptService.rsaEncrypt.mockResolvedValue(encryptedKey as any);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedKey as any);
|
||||
|
||||
await service.enroll("orgId", "userId", { key: "key" } as any);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ export class PasswordResetEnrollmentServiceImplementation
|
||||
userId ?? (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))));
|
||||
userKey = userKey ?? (await this.keyService.getUserKey(userId));
|
||||
// RSA Encrypt user's userKey.key with organization public key
|
||||
const encryptedKey = await this.encryptService.rsaEncrypt(userKey.key, orgPublicKey);
|
||||
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(userKey, orgPublicKey);
|
||||
|
||||
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
|
||||
resetRequest.resetPasswordKey = encryptedKey.encryptedString;
|
||||
|
||||
@@ -35,7 +35,38 @@ export abstract class EncryptService {
|
||||
key: SymmetricCryptoKey,
|
||||
decryptTrace?: string,
|
||||
): Promise<Uint8Array | null>;
|
||||
|
||||
/**
|
||||
* Encapsulates a symmetric key with an asymmetric public key
|
||||
* Note: This does not establish sender authenticity
|
||||
* @param sharedKey - The symmetric key that is to be shared
|
||||
* @param encapsulationKey - The encapsulation key (public key) of the receiver that the key is shared with
|
||||
*/
|
||||
abstract encapsulateKeyUnsigned(
|
||||
sharedKey: SymmetricCryptoKey,
|
||||
encapsulationKey: Uint8Array,
|
||||
): Promise<EncString>;
|
||||
/**
|
||||
* Decapsulates a shared symmetric key with an asymmetric private key
|
||||
* Note: This does not establish sender authenticity
|
||||
* @param encryptedSharedKey - The encrypted shared symmetric key
|
||||
* @param decapsulationKey - The key to decapsulate with (private key)
|
||||
*/
|
||||
abstract decapsulateKeyUnsigned(
|
||||
encryptedSharedKey: EncString,
|
||||
decapsulationKey: Uint8Array,
|
||||
): Promise<SymmetricCryptoKey>;
|
||||
/**
|
||||
* @deprecated Use encapsulateKeyUnsigned instead
|
||||
* @param data - The data to encrypt
|
||||
* @param publicKey - The public key to encrypt with
|
||||
*/
|
||||
abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString>;
|
||||
/**
|
||||
* @deprecated Use decapsulateKeyUnsigned instead
|
||||
* @param data - The ciphertext to decrypt
|
||||
* @param privateKey - The privateKey to decrypt with
|
||||
*/
|
||||
abstract rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array>;
|
||||
/**
|
||||
* @deprecated Replaced by BulkEncryptService, remove once the feature is tested and the featureflag PM-4154-multi-worker-encryption-service is removed
|
||||
|
||||
@@ -235,42 +235,22 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
}
|
||||
}
|
||||
|
||||
async rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString> {
|
||||
if (data == null) {
|
||||
throw new Error("No data provided for encryption.");
|
||||
async encapsulateKeyUnsigned(
|
||||
sharedKey: SymmetricCryptoKey,
|
||||
encapsulationKey: Uint8Array,
|
||||
): Promise<EncString> {
|
||||
if (sharedKey == null) {
|
||||
throw new Error("No sharedKey provided for encapsulation");
|
||||
}
|
||||
return await this.rsaEncrypt(sharedKey.toEncoded(), encapsulationKey);
|
||||
}
|
||||
|
||||
if (publicKey == null) {
|
||||
throw new Error("No public key provided for encryption.");
|
||||
}
|
||||
const encrypted = await this.cryptoFunctionService.rsaEncrypt(data, publicKey, "sha1");
|
||||
return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(encrypted));
|
||||
}
|
||||
|
||||
async rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array> {
|
||||
if (data == null) {
|
||||
throw new Error("[Encrypt service] rsaDecrypt: No data provided for decryption.");
|
||||
}
|
||||
|
||||
let algorithm: "sha1" | "sha256";
|
||||
switch (data.encryptionType) {
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
|
||||
algorithm = "sha1";
|
||||
break;
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
|
||||
algorithm = "sha256";
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid encryption type.");
|
||||
}
|
||||
|
||||
if (privateKey == null) {
|
||||
throw new Error("[Encrypt service] rsaDecrypt: No private key provided for decryption.");
|
||||
}
|
||||
|
||||
return this.cryptoFunctionService.rsaDecrypt(data.dataBytes, privateKey, algorithm);
|
||||
async decapsulateKeyUnsigned(
|
||||
encryptedSharedKey: EncString,
|
||||
decapsulationKey: Uint8Array,
|
||||
): Promise<SymmetricCryptoKey> {
|
||||
const keyBytes = await this.rsaDecrypt(encryptedSharedKey, decapsulationKey);
|
||||
return new SymmetricCryptoKey(keyBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,4 +321,42 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
this.logDecryptError(msg, keyEncType, dataEncType, decryptContext);
|
||||
}
|
||||
}
|
||||
|
||||
async rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString> {
|
||||
if (data == null) {
|
||||
throw new Error("No data provided for encryption.");
|
||||
}
|
||||
|
||||
if (publicKey == null) {
|
||||
throw new Error("No public key provided for encryption.");
|
||||
}
|
||||
const encrypted = await this.cryptoFunctionService.rsaEncrypt(data, publicKey, "sha1");
|
||||
return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(encrypted));
|
||||
}
|
||||
|
||||
async rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array> {
|
||||
if (data == null) {
|
||||
throw new Error("[Encrypt service] rsaDecrypt: No data provided for decryption.");
|
||||
}
|
||||
|
||||
let algorithm: "sha1" | "sha256";
|
||||
switch (data.encryptionType) {
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
|
||||
algorithm = "sha1";
|
||||
break;
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
|
||||
algorithm = "sha256";
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid encryption type.");
|
||||
}
|
||||
|
||||
if (privateKey == null) {
|
||||
throw new Error("[Encrypt service] rsaDecrypt: No private key provided for decryption.");
|
||||
}
|
||||
|
||||
return this.cryptoFunctionService.rsaDecrypt(data.dataBytes, privateKey, algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,7 +412,8 @@ describe("EncryptService", () => {
|
||||
});
|
||||
|
||||
describe("rsa", () => {
|
||||
const data = makeStaticByteArray(10, 100);
|
||||
const data = makeStaticByteArray(64, 100);
|
||||
const testKey = new SymmetricCryptoKey(data);
|
||||
const encryptedData = makeStaticByteArray(10, 150);
|
||||
const publicKey = makeStaticByteArray(10, 200);
|
||||
const privateKey = makeStaticByteArray(10, 250);
|
||||
@@ -422,22 +423,26 @@ describe("EncryptService", () => {
|
||||
return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(data));
|
||||
}
|
||||
|
||||
describe("rsaEncrypt", () => {
|
||||
describe("encapsulateKeyUnsigned", () => {
|
||||
it("throws if no data is provided", () => {
|
||||
return expect(encryptService.rsaEncrypt(null, publicKey)).rejects.toThrow("No data");
|
||||
return expect(encryptService.encapsulateKeyUnsigned(null, publicKey)).rejects.toThrow(
|
||||
"No sharedKey provided for encapsulation",
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if no public key is provided", () => {
|
||||
return expect(encryptService.rsaEncrypt(data, null)).rejects.toThrow("No public key");
|
||||
return expect(encryptService.encapsulateKeyUnsigned(testKey, null)).rejects.toThrow(
|
||||
"No public key",
|
||||
);
|
||||
});
|
||||
|
||||
it("encrypts data with provided key", async () => {
|
||||
cryptoFunctionService.rsaEncrypt.mockResolvedValue(encryptedData);
|
||||
|
||||
const actual = await encryptService.rsaEncrypt(data, publicKey);
|
||||
const actual = await encryptService.encapsulateKeyUnsigned(testKey, publicKey);
|
||||
|
||||
expect(cryptoFunctionService.rsaEncrypt).toBeCalledWith(
|
||||
expect.toEqualBuffer(data),
|
||||
expect.toEqualBuffer(testKey.key),
|
||||
expect.toEqualBuffer(publicKey),
|
||||
"sha1",
|
||||
);
|
||||
@@ -447,13 +452,17 @@ describe("EncryptService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("rsaDecrypt", () => {
|
||||
describe("decapsulateKeyUnsigned", () => {
|
||||
it("throws if no data is provided", () => {
|
||||
return expect(encryptService.rsaDecrypt(null, privateKey)).rejects.toThrow("No data");
|
||||
return expect(encryptService.decapsulateKeyUnsigned(null, privateKey)).rejects.toThrow(
|
||||
"No data",
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if no private key is provided", () => {
|
||||
return expect(encryptService.rsaDecrypt(encString, null)).rejects.toThrow("No private key");
|
||||
return expect(encryptService.decapsulateKeyUnsigned(encString, null)).rejects.toThrow(
|
||||
"No private key",
|
||||
);
|
||||
});
|
||||
|
||||
it.each([EncryptionType.AesCbc256_B64, EncryptionType.AesCbc256_HmacSha256_B64])(
|
||||
@@ -461,16 +470,16 @@ describe("EncryptService", () => {
|
||||
async (encType) => {
|
||||
encString.encryptionType = encType;
|
||||
|
||||
await expect(encryptService.rsaDecrypt(encString, privateKey)).rejects.toThrow(
|
||||
"Invalid encryption type",
|
||||
);
|
||||
await expect(
|
||||
encryptService.decapsulateKeyUnsigned(encString, privateKey),
|
||||
).rejects.toThrow("Invalid encryption type");
|
||||
},
|
||||
);
|
||||
|
||||
it("decrypts data with provided key", async () => {
|
||||
cryptoFunctionService.rsaDecrypt.mockResolvedValue(data);
|
||||
|
||||
const actual = await encryptService.rsaDecrypt(makeEncString(data), privateKey);
|
||||
const actual = await encryptService.decapsulateKeyUnsigned(makeEncString(data), privateKey);
|
||||
|
||||
expect(cryptoFunctionService.rsaDecrypt).toBeCalledWith(
|
||||
expect.toEqualBuffer(data),
|
||||
@@ -478,7 +487,7 @@ describe("EncryptService", () => {
|
||||
"sha1",
|
||||
);
|
||||
|
||||
expect(actual).toEqualBuffer(data);
|
||||
expect(actual.key).toEqualBuffer(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -161,7 +161,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
|
||||
deviceKeyEncryptedDevicePrivateKey,
|
||||
] = await Promise.all([
|
||||
// Encrypt user key with the DevicePublicKey
|
||||
this.encryptService.rsaEncrypt(userKey.key, devicePublicKey),
|
||||
this.encryptService.encapsulateKeyUnsigned(userKey, devicePublicKey),
|
||||
|
||||
// Encrypt devicePublicKey with user key
|
||||
this.encryptService.encrypt(devicePublicKey, userKey),
|
||||
@@ -285,8 +285,8 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
|
||||
);
|
||||
|
||||
// Encrypt the brand new user key with the now-decrypted public key for the device
|
||||
const encryptedNewUserKey = await this.encryptService.rsaEncrypt(
|
||||
newUserKey.key,
|
||||
const encryptedNewUserKey = await this.encryptService.encapsulateKeyUnsigned(
|
||||
newUserKey,
|
||||
decryptedDevicePublicKey,
|
||||
);
|
||||
|
||||
@@ -401,12 +401,12 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
|
||||
);
|
||||
|
||||
// Attempt to decrypt encryptedUserDataKey with devicePrivateKey
|
||||
const userKey = await this.encryptService.rsaDecrypt(
|
||||
const userKey = await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(encryptedUserKey.encryptedString),
|
||||
devicePrivateKey,
|
||||
);
|
||||
|
||||
return new SymmetricCryptoKey(userKey) as UserKey;
|
||||
return userKey as UserKey;
|
||||
// FIXME: Remove when updating file. Eslint update
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
|
||||
@@ -416,7 +416,7 @@ describe("deviceTrustService", () => {
|
||||
.mockResolvedValue(mockUserKey);
|
||||
|
||||
cryptoSvcRsaEncryptSpy = jest
|
||||
.spyOn(encryptService, "rsaEncrypt")
|
||||
.spyOn(encryptService, "encapsulateKeyUnsigned")
|
||||
.mockResolvedValue(mockDevicePublicKeyEncryptedUserKey);
|
||||
|
||||
encryptServiceEncryptSpy = jest
|
||||
@@ -449,8 +449,8 @@ describe("deviceTrustService", () => {
|
||||
expect(cryptoSvcRsaEncryptSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
// RsaEncrypt must be called w/ a user key array buffer of 64 bytes
|
||||
const userKeyKey: Uint8Array = cryptoSvcRsaEncryptSpy.mock.calls[0][0];
|
||||
expect(userKeyKey.byteLength).toBe(64);
|
||||
const userKey = cryptoSvcRsaEncryptSpy.mock.calls[0][0];
|
||||
expect(userKey.key.byteLength).toBe(64);
|
||||
|
||||
expect(encryptServiceEncryptSpy).toHaveBeenCalledTimes(2);
|
||||
|
||||
@@ -610,7 +610,7 @@ describe("deviceTrustService", () => {
|
||||
mockUserId,
|
||||
mockEncryptedDevicePrivateKey,
|
||||
mockEncryptedUserKey,
|
||||
mockDeviceKey,
|
||||
null,
|
||||
);
|
||||
|
||||
expect(result).toBeNull();
|
||||
@@ -621,8 +621,8 @@ describe("deviceTrustService", () => {
|
||||
.spyOn(encryptService, "decryptToBytes")
|
||||
.mockResolvedValue(new Uint8Array(userKeyBytesLength));
|
||||
const rsaDecryptSpy = jest
|
||||
.spyOn(encryptService, "rsaDecrypt")
|
||||
.mockResolvedValue(new Uint8Array(userKeyBytesLength));
|
||||
.spyOn(encryptService, "decapsulateKeyUnsigned")
|
||||
.mockResolvedValue(new SymmetricCryptoKey(new Uint8Array(userKeyBytesLength)));
|
||||
|
||||
const result = await deviceTrustService.decryptUserKeyWithDeviceKey(
|
||||
mockUserId,
|
||||
@@ -863,9 +863,9 @@ describe("deviceTrustService", () => {
|
||||
});
|
||||
|
||||
// Mock the encryption of the new user key with the decrypted public key
|
||||
encryptService.rsaEncrypt.mockImplementationOnce((data, publicKey) => {
|
||||
expect(data.byteLength).toBe(64); // New key should also be 64 bytes
|
||||
expect(new Uint8Array(data)[0]).toBe(FakeNewUserKeyMarker); // New key should have the first byte be '1';
|
||||
encryptService.encapsulateKeyUnsigned.mockImplementationOnce((data, publicKey) => {
|
||||
expect(data.key.byteLength).toBe(64); // New key should also be 64 bytes
|
||||
expect(new Uint8Array(data.key)[0]).toBe(FakeNewUserKeyMarker); // New key should have the first byte be '1';
|
||||
|
||||
expect(new Uint8Array(publicKey)[0]).toBe(FakeDecryptedPublicKeyMarker);
|
||||
return Promise.resolve(new EncString("4.ZW5jcnlwdGVkdXNlcg=="));
|
||||
|
||||
@@ -557,8 +557,8 @@ describe("keyService", () => {
|
||||
return Promise.resolve(fakePrivateKeyDecryption(encryptedPrivateKey, userKey));
|
||||
});
|
||||
|
||||
encryptService.rsaDecrypt.mockImplementation((data, privateKey) => {
|
||||
return Promise.resolve(fakeOrgKeyDecryption(data, privateKey));
|
||||
encryptService.decapsulateKeyUnsigned.mockImplementation((data, privateKey) => {
|
||||
return Promise.resolve(new SymmetricCryptoKey(fakeOrgKeyDecryption(data, privateKey)));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -493,7 +493,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
throw new Error("No public key found.");
|
||||
}
|
||||
|
||||
const encShareKey = await this.encryptService.rsaEncrypt(shareKey.key, publicKey);
|
||||
const encShareKey = await this.encryptService.encapsulateKeyUnsigned(shareKey, publicKey);
|
||||
return [encShareKey, shareKey as T];
|
||||
}
|
||||
|
||||
@@ -968,11 +968,11 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
return this.stateProvider.getUser(userId, USER_ENCRYPTED_PROVIDER_KEYS).state$.pipe(
|
||||
// Convert each value in the record to it's own decryption observable
|
||||
convertValues(async (_, value) => {
|
||||
const decrypted = await this.encryptService.rsaDecrypt(
|
||||
const decapsulatedKey = await this.encryptService.decapsulateKeyUnsigned(
|
||||
new EncString(value),
|
||||
userPrivateKey,
|
||||
);
|
||||
return new SymmetricCryptoKey(decrypted) as ProviderKey;
|
||||
return decapsulatedKey as ProviderKey;
|
||||
}),
|
||||
// switchMap since there are no side effects
|
||||
switchMap((encryptedProviderKeys) => {
|
||||
|
||||
Reference in New Issue
Block a user