From 217af4277f62227bd79394eacbbb59af1c623eca Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 29 Apr 2025 15:47:03 +0200 Subject: [PATCH] Add cose migration on userkey rotation --- .../key-rotation/user-key-rotation.service.ts | 22 ++++++++++++++++--- libs/common/src/enums/feature-flag.enum.ts | 4 +++- .../platform/enums/encryption-type.enum.ts | 4 ++++ .../models/domain/symmetric-crypto-key.ts | 18 +++++++++++++-- libs/key-management/src/models/kdf-config.ts | 19 ++++++++++++++++ 5 files changed, 61 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts index ec38f49baeb..ea0b2bd8a32 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts @@ -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!"); diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 4eabb8ea114..94f04835ee4 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -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, diff --git a/libs/common/src/platform/enums/encryption-type.enum.ts b/libs/common/src/platform/enums/encryption-type.enum.ts index fd484dc2fdf..a7223330559 100644 --- a/libs/common/src/platform/enums/encryption-type.enum.ts +++ b/libs/common/src/platform/enums/encryption-type.enum.ts @@ -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, }; diff --git a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts index ad16ddd06f6..1fdca04aceb 100644 --- a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts +++ b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts @@ -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."); } diff --git a/libs/key-management/src/models/kdf-config.ts b/libs/key-management/src/models/kdf-config.ts index 689da77c163..a2ed8a22505 100644 --- a/libs/key-management/src/models/kdf-config.ts +++ b/libs/key-management/src/models/kdf-config.ts @@ -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 { return new PBKDF2KdfConfig(json.iterations); } + + toSdkConfig(): Kdf { + return { + pBKDF2: { + iterations: this.iterations, + }, + }; + } } /** @@ -124,6 +133,16 @@ export class Argon2KdfConfig { static fromJSON(json: Jsonify): 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);