mirror of
https://github.com/bitwarden/browser
synced 2026-02-08 20:50:28 +00:00
Add cose migration on userkey rotation
This commit is contained in:
@@ -5,14 +5,17 @@ import { Account } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
|
||||
import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { 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 { EncryptedString, EncString } 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";
|
||||
@@ -26,6 +29,7 @@ import {
|
||||
EmergencyAccessTrustComponent,
|
||||
KeyRotationTrustInfoComponent,
|
||||
} from "@bitwarden/key-management-ui";
|
||||
import { PureCrypto } 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";
|
||||
@@ -59,6 +63,7 @@ export class UserKeyRotationService {
|
||||
private toastService: ToastService,
|
||||
private i18nService: I18nService,
|
||||
private dialogService: DialogService,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -116,8 +121,19 @@ export class UserKeyRotationService {
|
||||
|
||||
const newMasterKey = await this.keyService.makeMasterKey(newMasterPassword, email, kdfConfig);
|
||||
|
||||
const [newUnencryptedUserKey, newMasterKeyEncryptedUserKey] =
|
||||
await this.keyService.makeUserKey(newMasterKey);
|
||||
let userKeyBytes = PureCrypto.generate_user_key_aes256_cbc_hmac();
|
||||
if (await this.configService.getFeatureFlag(FeatureFlag.EnrollAeadOnKeyRotation)) {
|
||||
userKeyBytes = PureCrypto.generate_user_key_xchacha20_poly1305();
|
||||
}
|
||||
const newMasterKeyEncryptedUserKey = new EncString(
|
||||
PureCrypto.encrypt_user_key_with_master_password(
|
||||
userKeyBytes,
|
||||
newMasterPassword,
|
||||
email,
|
||||
kdfConfig.toSdkConfig(),
|
||||
),
|
||||
);
|
||||
const newUnencryptedUserKey = new SymmetricCryptoKey(userKeyBytes) as UserKey;
|
||||
|
||||
if (!newUnencryptedUserKey || !newMasterKeyEncryptedUserKey) {
|
||||
this.logService.info("[Userkey rotation] User key could not be created. Aborting!");
|
||||
|
||||
@@ -45,6 +45,7 @@ export enum FeatureFlag {
|
||||
UserKeyRotationV2 = "userkey-rotation-v2",
|
||||
PM4154_BulkEncryptionService = "PM-4154-bulk-encryption-service",
|
||||
PM17987_BlockType0 = "pm-17987-block-type-0",
|
||||
EnrollAeadOnKeyRotation = "enroll-aead-on-key-rotation",
|
||||
|
||||
/* Tools */
|
||||
ItemShare = "item-share",
|
||||
@@ -126,9 +127,10 @@ export const DefaultFeatureFlagValue = {
|
||||
|
||||
/* Key Management */
|
||||
[FeatureFlag.PrivateKeyRegeneration]: FALSE,
|
||||
[FeatureFlag.UserKeyRotationV2]: FALSE,
|
||||
[FeatureFlag.UserKeyRotationV2]: true,
|
||||
[FeatureFlag.PM4154_BulkEncryptionService]: FALSE,
|
||||
[FeatureFlag.PM17987_BlockType0]: FALSE,
|
||||
[FeatureFlag.EnrollAeadOnKeyRotation]: true,
|
||||
|
||||
/* Platform */
|
||||
[FeatureFlag.IpcChannelFramework]: FALSE,
|
||||
|
||||
@@ -2,6 +2,9 @@ export enum EncryptionType {
|
||||
AesCbc256_B64 = 0,
|
||||
// Type 1 was the unused and removed AesCbc128_HmacSha256_B64
|
||||
AesCbc256_HmacSha256_B64 = 2,
|
||||
// Cose is the encoding for the key used, but contained can be:
|
||||
// - XChaCha20Poly1305
|
||||
CoseEncrypt0 = 7,
|
||||
Rsa2048_OaepSha256_B64 = 3,
|
||||
Rsa2048_OaepSha1_B64 = 4,
|
||||
Rsa2048_OaepSha256_HmacSha256_B64 = 5,
|
||||
@@ -36,4 +39,5 @@ export const EXPECTED_NUM_PARTS_BY_ENCRYPTION_TYPE = {
|
||||
[EncryptionType.Rsa2048_OaepSha1_B64]: 1,
|
||||
[EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64]: 2,
|
||||
[EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64]: 2,
|
||||
[EncryptionType.CoseEncrypt0]: 1,
|
||||
};
|
||||
|
||||
@@ -16,13 +16,19 @@ export type Aes256CbcKey = {
|
||||
encryptionKey: Uint8Array;
|
||||
};
|
||||
|
||||
export type CoseKey = {
|
||||
type: EncryptionType.CoseEncrypt0;
|
||||
// Encryption key here refers to the cose-encoded and padded key. This MAY later be refactored to contain the actual key bytes, as is the case in the SDK
|
||||
encryptionKey: Uint8Array;
|
||||
};
|
||||
|
||||
/**
|
||||
* A symmetric crypto key represents a symmetric key usable for symmetric encryption and decryption operations.
|
||||
* The specific algorithm used is private to the key, and should only be exposed to encrypt service implementations.
|
||||
* This can be done via `inner()`.
|
||||
*/
|
||||
export class SymmetricCryptoKey {
|
||||
private innerKey: Aes256CbcHmacKey | Aes256CbcKey;
|
||||
private innerKey: Aes256CbcHmacKey | Aes256CbcKey | CoseKey;
|
||||
|
||||
keyB64: string;
|
||||
|
||||
@@ -47,6 +53,12 @@ export class SymmetricCryptoKey {
|
||||
authenticationKey: key.slice(32),
|
||||
};
|
||||
this.keyB64 = this.toBase64();
|
||||
} else if (key.byteLength > 64) {
|
||||
this.innerKey = {
|
||||
type: EncryptionType.CoseEncrypt0,
|
||||
encryptionKey: key,
|
||||
};
|
||||
this.keyB64 = this.toBase64();
|
||||
} else {
|
||||
throw new Error(`Unsupported encType/key length ${key.byteLength}`);
|
||||
}
|
||||
@@ -63,7 +75,7 @@ export class SymmetricCryptoKey {
|
||||
*
|
||||
* @returns The inner key instance that can be directly used for encryption primitives
|
||||
*/
|
||||
inner(): Aes256CbcHmacKey | Aes256CbcKey {
|
||||
inner(): Aes256CbcHmacKey | Aes256CbcKey | CoseKey {
|
||||
return this.innerKey;
|
||||
}
|
||||
|
||||
@@ -90,6 +102,8 @@ export class SymmetricCryptoKey {
|
||||
encodedKey.set(this.innerKey.encryptionKey, 0);
|
||||
encodedKey.set(this.innerKey.authenticationKey, 32);
|
||||
return encodedKey;
|
||||
} else if (this.innerKey.type === EncryptionType.CoseEncrypt0) {
|
||||
return this.innerKey.encryptionKey;
|
||||
} else {
|
||||
throw new Error("Unsupported encryption type.");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { RangeWithDefault } from "@bitwarden/common/platform/misc/range-with-default";
|
||||
import { Kdf } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { KdfType } from "../enums/kdf-type.enum";
|
||||
|
||||
@@ -49,6 +50,14 @@ export class PBKDF2KdfConfig {
|
||||
static fromJSON(json: Jsonify<PBKDF2KdfConfig>): PBKDF2KdfConfig {
|
||||
return new PBKDF2KdfConfig(json.iterations);
|
||||
}
|
||||
|
||||
toSdkConfig(): Kdf {
|
||||
return {
|
||||
pBKDF2: {
|
||||
iterations: this.iterations,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +133,16 @@ export class Argon2KdfConfig {
|
||||
static fromJSON(json: Jsonify<Argon2KdfConfig>): Argon2KdfConfig {
|
||||
return new Argon2KdfConfig(json.iterations, json.memory, json.parallelism);
|
||||
}
|
||||
|
||||
toSdkConfig(): Kdf {
|
||||
return {
|
||||
argon2id: {
|
||||
iterations: this.iterations,
|
||||
memory: this.memory,
|
||||
parallelism: this.parallelism,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const DEFAULT_KDF_CONFIG = new PBKDF2KdfConfig(PBKDF2KdfConfig.ITERATIONS.defaultValue);
|
||||
|
||||
Reference in New Issue
Block a user