1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 13:40:06 +00:00
This commit is contained in:
Bernd Schoolmann
2025-02-24 19:59:22 +01:00
parent 754ebcaef9
commit 8ee5fb2cbc
34 changed files with 187 additions and 139 deletions

View File

@@ -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.rsaEncrypt(key.toEncoded(), publicKey);
userIdsWithKeys.push({
id: user.id,
key: encryptedKey.encryptedString,

View File

@@ -318,7 +318,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.rsaEncrypt(orgKey.toEncoded(), publicKey);
const request = new OrganizationUserConfirmRequest();
request.key = key.encryptedString;
await this.organizationUserApiService.postOrganizationUserConfirm(

View File

@@ -60,7 +60,7 @@ export class OrganizationUserResetPasswordService
if (userKey == null) {
throw new Error("No user key found");
}
const encryptedKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey);
const encryptedKey = await this.encryptService.rsaEncrypt(userKey.toEncoded(), publicKey);
return encryptedKey.encryptedString;
}

View File

@@ -25,7 +25,7 @@ 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.rsaEncrypt(userKey.toEncoded(), rawPublicKey);
const encryptedPublicKey = await this.encryptService.encrypt(rawPublicKey, userKey);
return new RotateableKeySet(encryptedUserKey, encryptedPublicKey, encryptedPrivateKey);
}
@@ -57,7 +57,7 @@ export class RotateableKeySetService {
oldUserKey,
);
const newEncryptedPublicKey = await this.encryptService.encrypt(publicKey, newUserKey);
const newEncryptedUserKey = await this.encryptService.rsaEncrypt(newUserKey.key, publicKey);
const newEncryptedUserKey = await this.encryptService.rsaEncrypt(newUserKey.toEncoded(), publicKey);
const newRotateableKeySet = new RotateableKeySet<ExternalKey>(
newEncryptedUserKey,

View File

@@ -356,6 +356,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.rsaEncrypt(userKey.toEncoded(), publicKey)).encryptedString;
}
}

View File

@@ -186,7 +186,7 @@ export class AcceptOrganizationInviteService {
// RSA Encrypt user's encKey.key with organization public key
const userKey = await this.keyService.getUserKey();
const encryptedKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey);
const encryptedKey = await this.encryptService.rsaEncrypt(userKey.toEncoded(), publicKey);
// Add reset password key to accept request
request.resetPasswordKey = encryptedKey.encryptedString;

View File

@@ -800,7 +800,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
);
const providerKey = await this.keyService.getProviderKey(this.providerId);
providerRequest.organizationCreateRequest.key = (
await this.encryptService.encrypt(orgKey.key, providerKey)
await this.encryptService.encrypt(orgKey.toEncoded(), providerKey)
).encryptedString;
const orgId = (
await this.apiService.postProviderCreateOrganization(this.providerId, providerRequest)

View File

@@ -11,10 +11,12 @@ import { VerificationType } from "@bitwarden/common/auth/enums/verification-type
import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { HashPurpose } from "@bitwarden/common/platform/enums";
import { EncryptionType, HashPurpose } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid";
import { UserKey } from "@bitwarden/common/types/key";
@@ -23,6 +25,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { ToastService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
import { CryptoClient } from "@bitwarden/sdk-internal";
import { OrganizationUserResetPasswordService } from "../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service";
import { WebauthnLoginAdminService } from "../../auth/core";
@@ -55,6 +58,7 @@ export class UserKeyRotationService {
private vaultTimeoutService: VaultTimeoutService,
private toastService: ToastService,
private i18nService: I18nService,
private keyGenerationService: KeyGenerationService,
) {}
/**
@@ -97,10 +101,21 @@ export class UserKeyRotationService {
);
const newMasterKey = await this.keyService.makeMasterKey(newMasterPassword, email, kdfConfig);
const [newUnencryptedUserKey, newMasterKeyEncryptedUserKey] =
let [newUnencryptedUserKey, newMasterKeyEncryptedUserKey] =
await this.keyService.makeUserKey(newMasterKey);
const userKey = CryptoClient.generate_user_key();
this.logService.info("[Userkey rotation] Encrypting user key in new format");
const userkeyEncodedBytes = Utils.fromB64ToArray(userKey);
const stretchedMasterKey = await this.keyGenerationService.stretchKey(newMasterKey);
const userkeyEncrypted = await this.encryptService.encrypt(
userkeyEncodedBytes,
stretchedMasterKey,
);
const userkeyBytes = Utils.fromB64ToArray(CryptoClient.decode_userkey(userKey).Aes256CbcHmac);
newUnencryptedUserKey = new SymmetricCryptoKey(userkeyBytes, EncryptionType.AesCbc256_HmacSha256_B64) as UserKey;
newMasterKeyEncryptedUserKey = userkeyEncrypted;
if (!newUnencryptedUserKey || !newMasterKeyEncryptedUserKey) {
this.logService.info("[Userkey rotation] User key could not be created. Aborting!");
throw new Error("User key could not be created");

View File

@@ -126,6 +126,6 @@ export class OrganizationAuthRequestService {
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.rsaEncrypt(userKey.toEncoded(), devicePubKey);
}
}

View File

@@ -188,7 +188,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.rsaEncrypt(providerKey.toEncoded(), publicKey);
const request = new ProviderUserConfirmRequest();
request.key = key.encryptedString;
await this.apiService.postProviderUserConfirm(this.providerId, user.id, request);

View File

@@ -36,7 +36,7 @@ export class WebProviderService {
const orgKey = await this.keyService.getOrgKey(organizationId);
const providerKey = await this.keyService.getProviderKey(providerId);
const encryptedOrgKey = await this.encryptService.encrypt(orgKey.key, providerKey);
const encryptedOrgKey = await this.encryptService.encrypt(orgKey.toEncoded(), providerKey);
const request = new ProviderAddOrganizationRequest();
request.organizationId = organizationId;
@@ -55,7 +55,7 @@ export class WebProviderService {
),
);
const providerKey = await this.keyService.getProviderKey(providerId);
const encryptedOrgKey = await this.encryptService.encrypt(orgKey.key, providerKey);
const encryptedOrgKey = await this.encryptService.encrypt(orgKey.toEncoded(), providerKey);
await this.providerApiService.addOrganizationToProvider(providerId, {
key: encryptedOrgKey.encryptedString,
organizationId,
@@ -82,7 +82,7 @@ export class WebProviderService {
const providerKey = await this.keyService.getProviderKey(providerId);
const encryptedProviderKey = await this.encryptService.encrypt(
organizationKey.key,
organizationKey.toEncoded(),
providerKey,
);

View File

@@ -104,10 +104,10 @@ export class AccessService {
const [name, encryptedPayload, key] = await Promise.all([
await this.encryptService.encrypt(accessTokenView.name, organizationKey),
await this.encryptService.encrypt(
JSON.stringify({ encryptionKey: organizationKey.keyB64 }),
JSON.stringify({ encryptionKey: organizationKey.toBase64() }),
encryptionKey,
),
await this.encryptService.encrypt(encryptionKey.keyB64, organizationKey),
await this.encryptService.encrypt(encryptionKey.toBase64(), organizationKey),
]);
accessTokenRequest.name = name;

View File

@@ -209,7 +209,7 @@ 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.rsaEncrypt(userKey.toEncoded(), publicKey);
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
resetRequest.masterPasswordHash = masterPasswordHash;

View File

@@ -825,9 +825,9 @@ export class AddEditComponent implements OnInit, OnDestroy {
private async generateSshKey(showNotification: boolean = true) {
await firstValueFrom(this.sdkService.client$);
const sshKey = generate_ssh_key("Ed25519");
this.cipher.sshKey.privateKey = sshKey.private_key;
this.cipher.sshKey.publicKey = sshKey.public_key;
this.cipher.sshKey.keyFingerprint = sshKey.key_fingerprint;
this.cipher.sshKey.privateKey = sshKey.privateKey;
this.cipher.sshKey.publicKey = sshKey.publicKey;
this.cipher.sshKey.keyFingerprint = sshKey.fingerprint;
if (showNotification) {
this.toastService.showToast({

View File

@@ -159,7 +159,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.rsaEncrypt(userKey.toEncoded(), publicKey);
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
resetRequest.masterPasswordHash = masterKeyHash;

View File

@@ -111,10 +111,10 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
Utils.fromUtf8ToArray(masterKeyHash),
pubKey,
);
keyToEncrypt = masterKey.encKey;
keyToEncrypt = masterKey.toEncoded();
} else {
const userKey = await this.keyService.getUserKey();
keyToEncrypt = userKey.key;
keyToEncrypt = userKey.toEncoded();
}
const encryptedKey = await this.encryptService.rsaEncrypt(keyToEncrypt, pubKey);

View File

@@ -193,7 +193,7 @@ export class PinService implements PinServiceAbstraction {
const pinKey = await this.makePinKey(pin, email, kdfConfig);
return await this.encryptService.encrypt(userKey.key, pinKey);
return await this.encryptService.encrypt(userKey.toEncoded(), pinKey);
}
async storePinKeyEncryptedUserKey(

View File

@@ -158,7 +158,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
deviceKeyEncryptedDevicePrivateKey,
] = await Promise.all([
// Encrypt user key with the DevicePublicKey
this.encryptService.rsaEncrypt(userKey.key, devicePublicKey),
this.encryptService.rsaEncrypt(userKey.toEncoded(), devicePublicKey),
// Encrypt devicePublicKey with user key
this.encryptService.encrypt(devicePublicKey, userKey),
@@ -226,7 +226,7 @@ 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,
newUserKey.toEncoded(),
decryptedDevicePublicKey,
);

View File

@@ -95,7 +95,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
userId ??= (await firstValueFrom(this.accountService.activeAccount$))?.id;
const organization = await this.getManagingOrganization(userId);
const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId));
const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64);
const keyConnectorRequest = new KeyConnectorUserKeyRequest(Utils.fromBufferToB64(masterKey.getInnerKey().encryptionKey));
try {
await this.apiService.postUserKeyToKeyConnector(
@@ -153,11 +153,11 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
: new Argon2KdfConfig(kdfIterations, kdfMemory, kdfParallelism);
const masterKey = await this.keyService.makeMasterKey(
password.keyB64,
password.toBase64(),
await this.tokenService.getEmail(),
kdfConfig,
);
const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64);
const keyConnectorRequest = new KeyConnectorUserKeyRequest(Utils.fromBufferToB64(masterKey.getInnerKey().encryptionKey));
await this.masterPasswordService.setMasterKey(masterKey, userId);
const userKey = await this.keyService.makeUserKey(masterKey);

View File

@@ -2,6 +2,9 @@
// @ts-strict-ignore
import { firstValueFrom, map, Observable } from "rxjs";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CryptoClient } from "@bitwarden/sdk-internal";
import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service";
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
import { LogService } from "../../../platform/abstractions/log.service";
@@ -191,6 +194,16 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr
newKey,
"Content: User Key; Encrypting Key: Stretched Master Key",
);
if (decUserKey.length === 32 || decUserKey.length === 64) {
return new SymmetricCryptoKey(decUserKey) as UserKey;
} else {
this.logService.info("[MasterPasswordService] Userkey in new format; using sdk");
const decodedKey = CryptoClient.decode_userkey(decUserKey);
if (decodedKey.Aes256CbcHmac != null) {
const key = Utils.fromB64ToArray(decodedKey.Aes256CbcHmac);
return new SymmetricCryptoKey(key) as UserKey;
}
}
} else {
throw new Error("Unsupported encryption type.");
}

View File

@@ -53,7 +53,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.rsaEncrypt(userKey.toEncoded(), orgPublicKey);
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
resetRequest.resetPasswordKey = encryptedKey.encryptedString;

View File

@@ -100,7 +100,7 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE,
[FeatureFlag.SSHKeyVaultItem]: FALSE,
[FeatureFlag.SSHAgent]: FALSE,
[FeatureFlag.UserKeyRotationV2]: FALSE,
[FeatureFlag.UserKeyRotationV2]: true,
[FeatureFlag.CipherKeyEncryption]: FALSE,
[FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader]: FALSE,
[FeatureFlag.TrialPaymentOptional]: FALSE,

View File

@@ -36,7 +36,6 @@ export abstract class EncryptService {
): Promise<Uint8Array>;
abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString>;
abstract rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array>;
abstract resolveLegacyKey(key: SymmetricCryptoKey, encThing: Encrypted): SymmetricCryptoKey;
/**
* @deprecated Replaced by BulkEncryptService, remove once the feature is tested and the featureflag PM-4154-multi-worker-encryption-service is removed
* @param items The items to decrypt

View File

@@ -44,7 +44,7 @@ export class EncryptServiceImplementation implements EncryptService {
const iv = Utils.fromBufferToB64(encObj.iv);
const data = Utils.fromBufferToB64(encObj.data);
const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : null;
return new EncString(encObj.key.encType, data, iv, mac);
return new EncString(encObj.key.encryptionType(), data, iv, mac);
}
async encryptToBytes(plainValue: Uint8Array, key: SymmetricCryptoKey): Promise<EncArrayBuffer> {
@@ -59,7 +59,7 @@ export class EncryptServiceImplementation implements EncryptService {
}
const encBytes = new Uint8Array(1 + encValue.iv.byteLength + macLen + encValue.data.byteLength);
encBytes.set([encValue.key.encType]);
encBytes.set([encValue.key.encryptionType()]);
encBytes.set(new Uint8Array(encValue.iv), 1);
if (encValue.mac != null) {
encBytes.set(new Uint8Array(encValue.mac), 1 + encValue.iv.byteLength);
@@ -78,13 +78,11 @@ export class EncryptServiceImplementation implements EncryptService {
throw new Error("No key provided for decryption.");
}
key = this.resolveLegacyKey(key, encString);
// DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality.
if (key.macKey != null && encString?.mac == null) {
this.logService.error(
"[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " +
encryptionTypeName(key.encType) +
encryptionTypeName(key.encryptionType()) +
"Payload type " +
encryptionTypeName(encString.encryptionType),
"Decrypt context: " + decryptContext,
@@ -92,10 +90,10 @@ export class EncryptServiceImplementation implements EncryptService {
return null;
}
if (key.encType !== encString.encryptionType) {
if (key.encryptionType() !== encString.encryptionType) {
this.logService.error(
"[Encrypt service] Key encryption type does not match payload encryption type. Key type " +
encryptionTypeName(key.encType) +
encryptionTypeName(key.encryptionType()) +
"Payload type " +
encryptionTypeName(encString.encryptionType),
"Decrypt context: " + decryptContext,
@@ -119,7 +117,7 @@ export class EncryptServiceImplementation implements EncryptService {
if (!macsEqual) {
this.logMacFailed(
"[Encrypt service] decryptToUtf8 MAC comparison failed. Key or payload has changed. Key type " +
encryptionTypeName(key.encType) +
encryptionTypeName(key.encryptionType()) +
"Payload type " +
encryptionTypeName(encString.encryptionType) +
" Decrypt context: " +
@@ -145,13 +143,11 @@ export class EncryptServiceImplementation implements EncryptService {
throw new Error("Nothing provided for decryption.");
}
key = this.resolveLegacyKey(key, encThing);
// DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality.
if (key.macKey != null && encThing.macBytes == null) {
this.logService.error(
"[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " +
encryptionTypeName(key.encType) +
encryptionTypeName(key.encryptionType()) +
" Payload type " +
encryptionTypeName(encThing.encryptionType) +
" Decrypt context: " +
@@ -160,10 +156,10 @@ export class EncryptServiceImplementation implements EncryptService {
return null;
}
if (key.encType !== encThing.encryptionType) {
if (key.encryptionType() !== encThing.encryptionType) {
this.logService.error(
"[Encrypt service] Key encryption type does not match payload encryption type. Key type " +
encryptionTypeName(key.encType) +
encryptionTypeName(key.encryptionType()) +
" Payload type " +
encryptionTypeName(encThing.encryptionType) +
" Decrypt context: " +
@@ -181,7 +177,7 @@ export class EncryptServiceImplementation implements EncryptService {
this.logMacFailed(
"[Encrypt service#decryptToBytes] Failed to compute MAC." +
" Key type " +
encryptionTypeName(key.encType) +
encryptionTypeName(key.encryptionType()) +
" Payload type " +
encryptionTypeName(encThing.encryptionType) +
" Decrypt context: " +
@@ -195,7 +191,7 @@ export class EncryptServiceImplementation implements EncryptService {
this.logMacFailed(
"[Encrypt service#decryptToBytes]: MAC comparison failed. Key or payload has changed." +
" Key type " +
encryptionTypeName(key.encType) +
encryptionTypeName(key.encryptionType()) +
" Payload type " +
encryptionTypeName(encThing.encryptionType) +
" Decrypt context: " +
@@ -208,7 +204,7 @@ export class EncryptServiceImplementation implements EncryptService {
const result = await this.cryptoFunctionService.aesDecrypt(
encThing.dataBytes,
encThing.ivBytes,
key.encKey,
key.encryptionType()
"cbc",
);
@@ -278,19 +274,23 @@ export class EncryptServiceImplementation implements EncryptService {
}
private async aesEncrypt(data: Uint8Array, key: SymmetricCryptoKey): Promise<EncryptedObject> {
const obj = new EncryptedObject();
obj.key = key;
obj.iv = await this.cryptoFunctionService.randomBytes(16);
obj.data = await this.cryptoFunctionService.aesEncrypt(data, obj.iv, obj.key.encKey);
const innerKey = key.getInnerKey();
if (innerKey.type === EncryptionType.AesCbc256_HmacSha256_B64) {
const obj = new EncryptedObject();
// AES-CBC Encrypt the data
obj.iv = await this.cryptoFunctionService.randomBytes(16);
obj.data = await this.cryptoFunctionService.aesEncrypt(data, obj.iv, innerKey.encryptionKey);
if (obj.key.macKey != null) {
// Calculate the HMAC
const macData = new Uint8Array(obj.iv.byteLength + obj.data.byteLength);
macData.set(new Uint8Array(obj.iv), 0);
macData.set(new Uint8Array(obj.data), obj.iv.byteLength);
obj.mac = await this.cryptoFunctionService.hmac(macData, obj.key.macKey, "sha256");
}
obj.mac = await this.cryptoFunctionService.hmac(macData, innerKey.authenticationKey, "sha256");
return obj;
return obj;
} else {
throw new Error("Unsupported encryption type.");
}
}
private logMacFailed(msg: string) {
@@ -298,19 +298,4 @@ export class EncryptServiceImplementation implements EncryptService {
this.logService.error(msg);
}
}
/**
* Transform into new key for the old encrypt-then-mac scheme if required, otherwise return the current key unchanged
* @param encThing The encrypted object (e.g. encString or encArrayBuffer) that you want to decrypt
*/
resolveLegacyKey(key: SymmetricCryptoKey, encThing: Encrypted): SymmetricCryptoKey {
if (
encThing.encryptionType === EncryptionType.AesCbc128_HmacSha256_B64 &&
key.encType === EncryptionType.AesCbc256_B64
) {
return new SymmetricCryptoKey(key.key, EncryptionType.AesCbc128_HmacSha256_B64);
}
return key;
}
}

View File

@@ -1,10 +1,5 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
export class EncryptedObject {
iv: Uint8Array;
data: Uint8Array;
mac: Uint8Array;
key: SymmetricCryptoKey;
}

View File

@@ -2,64 +2,61 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { CryptoClient, ExportedUserKey } from "@bitwarden/sdk-internal";
import { Utils } from "../../../platform/misc/utils";
import { EncryptionType } from "../../enums";
export class SymmetricCryptoKey {
key: Uint8Array;
encKey: Uint8Array;
macKey?: Uint8Array;
encType: EncryptionType;
type Aes256CbcHmacKey = {
type: EncryptionType.AesCbc256_HmacSha256_B64;
encryptionKey: Uint8Array;
authenticationKey: Uint8Array;
};
keyB64: string;
encKeyB64: string;
macKeyB64: string;
type Aes256CbcKey = {
type: EncryptionType.AesCbc256_B64;
encryptionKey: Uint8Array;
};
export class SymmetricCryptoKey {
private key: Aes256CbcKey | Aes256CbcHmacKey;
private newFormat = false;
meta: any;
constructor(key: Uint8Array, encType?: EncryptionType) {
constructor(key: Uint8Array, new_format = false) {
if (key == null) {
throw new Error("Must provide key");
}
if (encType == null) {
if (key.byteLength === 32) {
encType = EncryptionType.AesCbc256_B64;
} else if (key.byteLength === 64) {
encType = EncryptionType.AesCbc256_HmacSha256_B64;
} else {
throw new Error("Unable to determine encType.");
if (key.byteLength === 32) {
this.key = {
type: EncryptionType.AesCbc256_B64,
encryptionKey: key,
}
} else if (key.byteLength === 64) {
this.key = {
type: EncryptionType.AesCbc256_HmacSha256_B64,
encryptionKey: key.slice(0, 32),
authenticationKey: key.slice(32),
}
} else if (key.byteLength > 64) {
const decoded_key = CryptoClient.decode_userkey(key).Aes256CbcHmac;
this.key = {
type: EncryptionType.AesCbc256_HmacSha256_B64,
encryptionKey: decoded_key.encryption_key,
authenticationKey: decoded_key.authentication_key,
}
}
this.key = key;
this.encType = encType;
if (encType === EncryptionType.AesCbc256_B64 && key.byteLength === 32) {
this.encKey = key;
this.macKey = null;
} else if (encType === EncryptionType.AesCbc128_HmacSha256_B64 && key.byteLength === 32) {
this.encKey = key.slice(0, 16);
this.macKey = key.slice(16, 32);
} else if (encType === EncryptionType.AesCbc256_HmacSha256_B64 && key.byteLength === 64) {
this.encKey = key.slice(0, 32);
this.macKey = key.slice(32, 64);
} else {
throw new Error("Unsupported encType/key length.");
}
this.keyB64 = Utils.fromBufferToB64(this.key);
this.encKeyB64 = Utils.fromBufferToB64(this.encKey);
if (this.macKey != null) {
this.macKeyB64 = Utils.fromBufferToB64(this.macKey);
throw new Error("Unable to determine encType.");
}
}
toJSON() {
// The whole object is constructed from the initial key, so just store the B64 key
return { keyB64: this.keyB64 };
getInnerKey(): Aes256CbcKey | Aes256CbcHmacKey {
return this.key;
}
static fromString(s: string): SymmetricCryptoKey {
if (s == null) {
return null;
@@ -69,7 +66,51 @@ export class SymmetricCryptoKey {
return new SymmetricCryptoKey(arrayBuffer);
}
// For test only
toJSON() {
// The whole object is constructed from the initial key, so just store the B64 key
return { keyB64: Utils.fromBufferToB64(this) };
}
// For test only
static fromJSON(obj: Jsonify<SymmetricCryptoKey>): SymmetricCryptoKey {
return SymmetricCryptoKey.fromString(obj?.keyB64);
}
toSdkKey(): ExportedUserKey {
if (this.key.type === EncryptionType.AesCbc256_B64) {
throw new Error("Unsupported encryption type.");
} else if (this.key.type === EncryptionType.AesCbc256_HmacSha256_B64) {
return {
Aes256CbcHmac: {
encryption_key: this.key.encryptionKey,
authentication_key: this.key.authenticationKey,
},
};
} else {
throw new Error("Unsupported encryption type.");
}
}
toBase64(): string {
return Utils.fromBufferToB64(this.toEncoded());
}
toEncoded(): Uint8Array {
if (this.newFormat) {
return CryptoClient.encode_userkey(this.toSdkKey());
} else {
if (this.key.type === EncryptionType.AesCbc256_B64) {
return this.key.encryptionKey;
} else if (this.key.type === EncryptionType.AesCbc256_HmacSha256_B64) {
return new Uint8Array([...this.key.encryptionKey, ...this.key.authenticationKey]);
} else {
throw new Error("Unsupported encryption type.");
}
}
}
encryptionType(): EncryptionType {
return this.key.type;
}
}

View File

@@ -80,8 +80,8 @@ export class KeyGenerationService implements KeyGenerationServiceAbstraction {
async stretchKey(key: SymmetricCryptoKey): Promise<SymmetricCryptoKey> {
const newKey = new Uint8Array(64);
const encKey = await this.cryptoFunctionService.hkdfExpand(key.key, "enc", 32, "sha256");
const macKey = await this.cryptoFunctionService.hkdfExpand(key.key, "mac", 32, "sha256");
const encKey = await this.cryptoFunctionService.hkdfExpand(key.getInnerKey().encryptionKey, "enc", 32, "sha256");
const macKey = await this.cryptoFunctionService.hkdfExpand(key.getInnerKey().encryptionKey, "mac", 32, "sha256");
newKey.set(new Uint8Array(encKey));
newKey.set(new Uint8Array(macKey), 32);

View File

@@ -136,7 +136,7 @@ export class DefaultSdkService implements SdkService {
) {
await client.crypto().initialize_user_crypto({
email: account.email,
method: { decryptedKey: { decrypted_user_key: userKey.keyB64 } },
method: { decryptedKey: { decrypted_user_key: userKey.toBase64() } },
kdfParams:
kdfParams.kdfType === KdfType.PBKDF2_SHA256
? {

View File

@@ -251,7 +251,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
}
if (p.encKey == null) {
p.encKey = forge.util.decode64(key.encKeyB64);
p.encKey = forge.util.decode64(Utils.fromBufferToB64(key.getInnerKey().encryptionKey));
}
p.data = forge.util.decode64(data);
p.iv = forge.util.decode64(iv);

View File

@@ -76,7 +76,7 @@ export class SendService implements InternalSendServiceAbstraction {
model.key,
new PBKDF2KdfConfig(SEND_KDF_ITERATIONS),
);
send.password = passwordKey.keyB64;
send.password = passwordKey.toBase64();
}
if (key == null) {
key = await this.keyService.getUserKey();

View File

@@ -279,7 +279,7 @@ export class CipherService implements CipherServiceAbstraction {
key,
).then(async () => {
if (model.key != null) {
attachment.key = await this.encryptService.encrypt(model.key.key, key);
attachment.key = await this.encryptService.encrypt(model.key.toEncoded(), key);
}
encAttachments.push(attachment);
});
@@ -1770,7 +1770,7 @@ export class CipherService implements CipherServiceAbstraction {
// Then, we have to encrypt the cipher key with the proper key.
cipher.key = await this.encryptService.encrypt(
decryptedCipherKey.key,
decryptedCipherKey.toEncoded(),
keyForCipherKeyEncryption,
);

View File

@@ -57,7 +57,7 @@ import { PlatformUtilsService } from "../../common/src/platform/abstractions/pla
import { StateService } from "../../common/src/platform/abstractions/state.service";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { KeySuffixOptions, HashPurpose } from "../../common/src/platform/enums";
import { KeySuffixOptions, HashPurpose, EncryptionType } from "../../common/src/platform/enums";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { convertValues } from "../../common/src/platform/misc/convert-values";
@@ -274,7 +274,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
}
const newUserKey = await this.keyGenerationService.createKey(512);
return this.buildProtectedSymmetricKey(masterKey, newUserKey.key);
return this.buildProtectedSymmetricKey(masterKey, newUserKey.toEncoded());
}
/**
@@ -356,7 +356,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
userKey?: UserKey,
): Promise<[UserKey, EncString]> {
userKey ||= await this.getUserKey();
return await this.buildProtectedSymmetricKey(masterKey, userKey.key);
return await this.buildProtectedSymmetricKey(masterKey, userKey.toEncoded());
}
// TODO: move to MasterPasswordService
@@ -375,7 +375,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
}
const iterations = hashPurpose === HashPurpose.LocalAuthorization ? 2 : 1;
const hash = await this.cryptoFunctionService.pbkdf2(key.key, password, "sha256", iterations);
const hash = await this.cryptoFunctionService.pbkdf2(key.toEncoded(), password, "sha256", iterations);
return Utils.fromBufferToB64(hash);
}
@@ -462,7 +462,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
}
const newSymKey = await this.keyGenerationService.createKey(512);
return this.buildProtectedSymmetricKey(key, newSymKey.key);
return this.buildProtectedSymmetricKey(key, newSymKey.toEncoded());
}
private async clearOrgKeys(userId: UserId): Promise<void> {
@@ -510,7 +510,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
const shareKey = await this.keyGenerationService.createKey(512);
userId ??= await firstValueFrom(this.stateProvider.activeUserId$);
const publicKey = await firstValueFrom(this.userPublicKey$(userId));
const encShareKey = await this.encryptService.rsaEncrypt(shareKey.key, publicKey);
const encShareKey = await this.encryptService.rsaEncrypt(shareKey.toEncoded(), publicKey);
return [encShareKey, shareKey as T];
}
@@ -741,7 +741,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
protected async storeAdditionalKeys(key: UserKey, userId: UserId) {
const storeAuto = await this.shouldStoreKey(KeySuffixOptions.Auto, userId);
if (storeAuto) {
await this.stateService.setUserKeyAutoUnlock(key.keyB64, { userId: userId });
await this.stateService.setUserKeyAutoUnlock(key.toBase64(), { userId: userId });
} else {
await this.stateService.setUserKeyAutoUnlock(null, { userId: userId });
}
@@ -843,10 +843,10 @@ export class DefaultKeyService implements KeyServiceAbstraction {
newSymKey: Uint8Array,
): Promise<[T, EncString]> {
let protectedSymKey: EncString = null;
if (encryptionKey.key.byteLength === 32) {
if (encryptionKey.getInnerKey().type === EncryptionType.AesCbc256_B64) {
const stretchedEncryptionKey = await this.keyGenerationService.stretchKey(encryptionKey);
protectedSymKey = await this.encryptService.encrypt(newSymKey, stretchedEncryptionKey);
} else if (encryptionKey.key.byteLength === 64) {
} else if (encryptionKey.getInnerKey().type === EncryptionType.AesCbc256_HmacSha256_B64) {
protectedSymKey = await this.encryptService.encrypt(newSymKey, encryptionKey);
} else {
throw new Error("Invalid key size.");

View File

@@ -81,7 +81,7 @@ export class DefaultUserAsymmetricKeysRegenerationService
throw new Error("SDK is undefined");
}
return sdk.crypto().verify_asymmetric_keys({
userKey: userKey.keyB64,
userKey: userKey.toBase64(),
userPublicKey: publicKeyResponse.publicKey,
userKeyEncryptedPrivateKey: userKeyEncryptedPrivateKey,
});
@@ -125,7 +125,7 @@ export class DefaultUserAsymmetricKeysRegenerationService
if (sdk === undefined) {
throw new Error("SDK is undefined");
}
return sdk.crypto().make_key_pair(userKey.keyB64);
return sdk.crypto().make_key_pair(userKey.toBase64());
}),
),
);

View File

@@ -104,9 +104,9 @@ export class SshKeySectionComponent implements OnInit {
await firstValueFrom(this.sdkService.client$);
const sshKey = generate_ssh_key("Ed25519");
this.sshKeyForm.setValue({
privateKey: sshKey.private_key,
publicKey: sshKey.public_key,
keyFingerprint: sshKey.key_fingerprint,
privateKey: sshKey.privateKey,
publicKey: sshKey.publicKey,
keyFingerprint: sshKey.fingerprint,
});
}
}