mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 13:40:06 +00:00
Tmp
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.rsaEncrypt(key.toEncoded(), publicKey);
|
||||
userIdsWithKeys.push({
|
||||
id: user.id,
|
||||
key: encryptedKey.encryptedString,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
? {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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());
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user