1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 05:30:01 +00:00

Fix unit conversion bug (#13896)

* Fix unit conversion bug

* Fix formatting

* Fix build
This commit is contained in:
Bernd Schoolmann
2025-03-19 14:36:32 +01:00
committed by GitHub
parent 6a2794f3cf
commit 9a4449b538
9 changed files with 68 additions and 29 deletions

View File

@@ -36,6 +36,8 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { MasterPasswordApiService as MasterPasswordApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
import { DefaultOpaqueKeyExchangeService } from "@bitwarden/common/auth/opaque/default-opaque-key-exchange.service";
import { OpaqueKeyExchangeApiService } from "@bitwarden/common/auth/opaque/opaque-key-exchange-api.service";
import {
AccountServiceImplementation,
getUserId,
@@ -44,6 +46,7 @@ import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master-password/master-password-api.service.implementation";
import { PrePasswordLoginApiService } from "@bitwarden/common/auth/services/pre-password-login-api.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
@@ -640,6 +643,19 @@ export class ServiceContainer {
this.configService,
);
const opaqueKeyExchangeApiService = new OpaqueKeyExchangeApiService(
this.apiService,
this.environmentService,
);
const opaqueKeyExchangeService = new DefaultOpaqueKeyExchangeService(
opaqueKeyExchangeApiService,
this.sdkService,
);
const prePasswordLoginApiService = new PrePasswordLoginApiService(
this.apiService,
this.environmentService,
);
this.loginStrategyService = new LoginStrategyService(
this.accountService,
this.masterPasswordService,
@@ -666,6 +682,9 @@ export class ServiceContainer {
this.vaultTimeoutSettingsService,
this.kdfConfigService,
this.taskSchedulerService,
prePasswordLoginApiService,
this.configService,
opaqueKeyExchangeService,
);
// FIXME: CLI does not support autofill

View File

@@ -11,6 +11,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
import { CipherConfiguration } from "@bitwarden/common/auth/opaque/models/cipher-configuration";
import { OpaqueKeyExchangeService } from "@bitwarden/common/auth/opaque/opaque-key-exchange.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
@@ -24,7 +25,12 @@ import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService, ToastService } from "@bitwarden/components";
import { Argon2KdfConfig, KdfConfigService, KdfType, KeyService } from "@bitwarden/key-management";
import {
DEFAULT_OPAQUE_KDF_CONFIG,
KdfConfigService,
KdfType,
KeyService,
} from "@bitwarden/key-management";
import { UserKeyRotationService } from "../../key-management/key-rotation/user-key-rotation.service";
@@ -222,28 +228,22 @@ export class ChangePasswordComponent
return this.updateKey();
});
} else {
// PBKDF2 is not recommended for opaque, so force use of Argon2 with default params if the user is using PBKDF2.
const userConfiguredKdf = await this.kdfConfigService.getKdfConfig();
const opaqueKdf =
const cipherConfig = CipherConfiguration.fromKdfConfig(
userConfiguredKdf.kdfType === KdfType.Argon2id
? userConfiguredKdf
: new Argon2KdfConfig();
: DEFAULT_OPAQUE_KDF_CONFIG,
);
const sessionId = await this.opaqueKeyExchangeService.register(
this.masterPassword,
newUserKey[0],
opaqueKdf,
cipherConfig,
);
request.opaqueSessionId = sessionId;
this.formPromise = this.masterPasswordApiService.postPassword(request);
}
// TODO: remove this test code
const userConfiguredKdf = await this.kdfConfigService.getKdfConfig();
const opaqueKdf =
userConfiguredKdf.kdfType === KdfType.Argon2id ? userConfiguredKdf : new Argon2KdfConfig();
await this.opaqueKeyExchangeService.register(this.masterPassword, newUserKey[0], opaqueKdf);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("masterPasswordChanged"),

View File

@@ -13,6 +13,7 @@ import { IdentityCaptchaResponse } from "@bitwarden/common/auth/models/response/
import { IdentityDeviceVerificationResponse } from "@bitwarden/common/auth/models/response/identity-device-verification.response";
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response";
import { CipherConfiguration } from "@bitwarden/common/auth/opaque/models/cipher-configuration";
import { OpaqueKeyExchangeService } from "@bitwarden/common/auth/opaque/opaque-key-exchange.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -21,7 +22,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/sym
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey } from "@bitwarden/common/types/key";
import { Argon2KdfConfig, KdfType } from "@bitwarden/key-management";
import { DEFAULT_OPAQUE_KDF_CONFIG, KdfType } from "@bitwarden/key-management";
import { PasswordHashLoginCredentials } from "../models/domain/login-credentials";
import { CacheData } from "../services/login-strategies/login-strategy.state";
@@ -295,11 +296,14 @@ export class PasswordLoginStrategy extends BaseLoginStrategy {
return;
}
const opaqueKdf =
userConfiguredKdf.kdfType === KdfType.Argon2id ? userConfiguredKdf : new Argon2KdfConfig();
const cipherConfig = CipherConfiguration.fromKdfConfig(
userConfiguredKdf.kdfType === KdfType.Argon2id
? userConfiguredKdf
: DEFAULT_OPAQUE_KDF_CONFIG,
);
try {
await this.opaqueKeyExchangeService.register(masterPassword, userKey, opaqueKdf);
await this.opaqueKeyExchangeService.register(masterPassword, userKey, cipherConfig);
} catch (error) {
// If this process fails for any reason, we don't want to stop the login process
// so just log the error and continue.

View File

@@ -11,6 +11,7 @@ import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/ide
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response";
import { PrePasswordLoginResponse } from "@bitwarden/common/auth/models/response/pre-password-login.response";
import { OpaqueKeyExchangeService } from "@bitwarden/common/auth/opaque/opaque-key-exchange.service";
import { PrePasswordLoginApiService } from "@bitwarden/common/auth/services/pre-password-login-api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
@@ -22,6 +23,7 @@ import {
VaultTimeoutSettingsService,
} from "@bitwarden/common/key-management/vault-timeout";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -48,8 +50,6 @@ import { UserDecryptionOptionsService } from "../user-decryption-options/user-de
import { LoginStrategyService } from "./login-strategy.service";
import { CACHE_EXPIRATION_KEY } from "./login-strategy.state";
import { OpaqueKeyExchangeService } from "@bitwarden/common/auth/opaque/opaque-key-exchange.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
// TODO: update tests to pass
// TODO: test makePrePasswordLoginMasterKey

View File

@@ -8,7 +8,7 @@ import { OpaqueSessionId } from "@bitwarden/common/types/guid";
import { UserKey } from "../../types/key";
import { Argon2IdParameters, CipherConfiguration } from "./models/cipher-configuration";
import { CipherConfiguration } from "./models/cipher-configuration";
import { LoginFinishRequest } from "./models/login-finish.request";
import { LoginStartRequest } from "./models/login-start.request";
import { RegistrationFinishRequest } from "./models/registration-finish.request";
@@ -25,15 +25,14 @@ export class DefaultOpaqueKeyExchangeService implements OpaqueKeyExchangeService
async register(
masterPassword: string,
userKey: UserKey,
keyStretchingFuncArgon2Params: Argon2IdParameters, // TODO: eval if we can use KdfConfig existing type
cipherConfig: CipherConfiguration,
): Promise<OpaqueSessionId> {
if (!masterPassword || !userKey || !keyStretchingFuncArgon2Params) {
if (!masterPassword || !userKey || !cipherConfig) {
throw new Error(
`Unable to register user with missing parameters. masterPassword exists: ${!!masterPassword}, userKey exists: ${!!userKey}, keyStretchingFuncArgon2Params exists: ${!!keyStretchingFuncArgon2Params}`,
`Unable to register user with missing parameters. masterPassword exists: ${!!masterPassword}, userKey exists: ${!!userKey}, cipherConfig exists: ${!!cipherConfig}`,
);
}
const cipherConfig = new CipherConfiguration(keyStretchingFuncArgon2Params);
const cryptoClient = (await firstValueFrom(this.sdkService.client$)).crypto();
const registrationStart = cryptoClient.opaque_register_start(
@@ -81,15 +80,14 @@ export class DefaultOpaqueKeyExchangeService implements OpaqueKeyExchangeService
async login(
email: string,
masterPassword: string,
keyStretchingFuncArgon2Params: Argon2IdParameters,
cipherConfig: CipherConfiguration,
): Promise<Uint8Array> {
if (!email || !masterPassword || !keyStretchingFuncArgon2Params) {
if (!email || !masterPassword || !cipherConfig) {
throw new Error(
`Unable to log in user with missing parameters. email exists: ${!!email}; masterPassword exists: ${!!masterPassword}; keyStretchingFuncArgon2Params exists: ${!!keyStretchingFuncArgon2Params}`,
`Unable to log in user with missing parameters. email exists: ${!!email}; masterPassword exists: ${!!masterPassword}; cipherConfig exists: ${!!cipherConfig}`,
);
}
const cipherConfig = new CipherConfiguration(keyStretchingFuncArgon2Params);
const cryptoClient = (await firstValueFrom(this.sdkService.client$)).crypto();
const loginStart = cryptoClient.opaque_login_start(masterPassword, cipherConfig.toSdkConfig());

View File

@@ -1,3 +1,4 @@
import { Argon2KdfConfig } from "@bitwarden/key-management";
import { CipherConfiguration as CipherConfigurationSdk } from "@bitwarden/sdk-internal";
// TODO: add js docs to all types / classes here.
@@ -16,6 +17,21 @@ export class CipherConfiguration {
this.argon2Parameters = ksf;
}
/**
* Converts from Bitwarden KDF configs to OPAQUE KSF configs.
*
* @param kdfConfig - Bitwarden KDF config
* @returns OPAQUE KSF config
*/
static fromKdfConfig(kdfConfig: Argon2KdfConfig): CipherConfiguration {
return new CipherConfiguration({
// convert MiB to KiB
memory: kdfConfig.memory * 1024,
iterations: kdfConfig.iterations,
parallelism: kdfConfig.parallelism,
});
}
toSdkConfig(): CipherConfigurationSdk {
if (
this.cipherSuite !== "OPAQUE_3_RISTRETTO255_OPRF_RISTRETTO255_KEGROUP_3DH_KEX_ARGON2ID13_KSF"

View File

@@ -2,7 +2,7 @@ import { OpaqueSessionId } from "@bitwarden/common/types/guid";
import { UserKey } from "../../types/key";
import { Argon2IdParameters } from "./models/cipher-configuration";
import { CipherConfiguration } from "./models/cipher-configuration";
export abstract class OpaqueKeyExchangeService {
/**
@@ -11,7 +11,7 @@ export abstract class OpaqueKeyExchangeService {
abstract register(
masterPassword: string,
userKey: UserKey,
ksfParameters: Argon2IdParameters,
cipherConfiguration: CipherConfiguration,
): Promise<OpaqueSessionId>;
/**
@@ -22,6 +22,6 @@ export abstract class OpaqueKeyExchangeService {
abstract login(
email: string,
masterPassword: string,
ksfParameters: Argon2IdParameters,
cipherConfiguration: CipherConfiguration,
): Promise<Uint8Array>;
}

View File

@@ -16,6 +16,7 @@ export {
KdfConfig,
createKdfConfig,
DEFAULT_KDF_CONFIG,
DEFAULT_OPAQUE_KDF_CONFIG,
} from "./models/kdf-config";
export { KdfConfigService } from "./abstractions/kdf-config.service";
export { DefaultKdfConfigService } from "./kdf-config.service";

View File

@@ -141,3 +141,4 @@ export class Argon2KdfConfig {
}
export const DEFAULT_KDF_CONFIG = new PBKDF2KdfConfig(PBKDF2KdfConfig.ITERATIONS.defaultValue);
export const DEFAULT_OPAQUE_KDF_CONFIG = new Argon2KdfConfig(1, 256, 1);