mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
Update opaque login with password
This commit is contained in:
@@ -215,7 +215,6 @@ export class ChangePasswordComponent
|
||||
|
||||
try {
|
||||
if (this.rotateUserKey) {
|
||||
throw new Error("Userkey rotation not supported");
|
||||
this.formPromise = this.apiService.postPassword(request).then(async () => {
|
||||
// we need to save this for local masterkey verification during rotation
|
||||
await this.masterPasswordService.setMasterKeyHash(newLocalKeyHash, userId as UserId);
|
||||
@@ -223,18 +222,21 @@ export class ChangePasswordComponent
|
||||
return this.updateKey();
|
||||
});
|
||||
} else {
|
||||
const sessionId = await this.opaqueService.register(this.masterPassword, newUserKey[0], {
|
||||
memory: 256 * 1024,
|
||||
iterations: 3,
|
||||
parallelism: 4,
|
||||
});
|
||||
request.opaqueSessionId = sessionId;
|
||||
this.formPromise = this.apiService.postPassword(request);
|
||||
}
|
||||
|
||||
await this.opaqueService.register(this.masterPassword, newUserKey[0], {
|
||||
algorithm: "argon2id",
|
||||
parameters: { memory: 256 * 1024, iterations: 3, parallelism: 4 },
|
||||
});
|
||||
await this.opaqueService.login(this.email, this.masterPassword, {
|
||||
algorithm: "argon2id",
|
||||
parameters: { memory: 256 * 1024, iterations: 3, parallelism: 4 },
|
||||
});
|
||||
await this.formPromise;
|
||||
await this.opaqueService.login(this.email, this.masterPassword, {
|
||||
memory: 256 * 1024,
|
||||
iterations: 3,
|
||||
parallelism: 4,
|
||||
});
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
|
||||
@@ -6,4 +6,5 @@ export class PasswordRequest extends SecretVerificationRequest {
|
||||
newMasterPasswordHash: string;
|
||||
masterPasswordHint: string;
|
||||
key: string;
|
||||
opaqueSessionId: string;
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@ import { RotateableKeySet } from "@bitwarden/auth/common";
|
||||
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { OpaqueSessionId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { UserKey } from "../../types/key";
|
||||
|
||||
import { CipherConfiguration, KsfConfig } from "./models/cipher-configuration";
|
||||
import { Argon2IdParameters, 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";
|
||||
@@ -21,8 +22,12 @@ export class DefaultOpaqueService implements OpaqueService {
|
||||
private sdkService: SdkService,
|
||||
) {}
|
||||
|
||||
async register(masterPassword: string, userKey: UserKey, ksfConfig: KsfConfig): Promise<void> {
|
||||
const config = new CipherConfiguration(ksfConfig);
|
||||
async register(
|
||||
masterPassword: string,
|
||||
userKey: UserKey,
|
||||
ksfParameters: Argon2IdParameters,
|
||||
): Promise<OpaqueSessionId> {
|
||||
const config = new CipherConfiguration(ksfParameters);
|
||||
const cryptoClient = (await firstValueFrom(this.sdkService.client$)).crypto();
|
||||
|
||||
const registrationStart = cryptoClient.opaque_register_start(
|
||||
@@ -60,9 +65,15 @@ export class DefaultOpaqueService implements OpaqueService {
|
||||
keyset,
|
||||
),
|
||||
);
|
||||
|
||||
return registrationStartResponse.sessionId;
|
||||
}
|
||||
|
||||
async login(email: string, masterPassword: string, ksfConfig: KsfConfig): Promise<Uint8Array> {
|
||||
async login(
|
||||
email: string,
|
||||
masterPassword: string,
|
||||
ksfConfig: Argon2IdParameters,
|
||||
): Promise<Uint8Array> {
|
||||
const config = new CipherConfiguration(ksfConfig);
|
||||
const cryptoClient = (await firstValueFrom(this.sdkService.client$)).crypto();
|
||||
|
||||
|
||||
@@ -1,36 +1,34 @@
|
||||
import { CipherConfiguration as CipherConfigurationSdk } from "@bitwarden/sdk-internal";
|
||||
|
||||
export type OpaqueKeVersion = 3;
|
||||
export type CipherSuite = OPAQUEKE3_RISTRETTO255_3DH_ARGON2ID13_SUITE;
|
||||
export type OPAQUEKE3_RISTRETTO255_3DH_ARGON2ID13_SUITE =
|
||||
"OPAQUE_3_RISTRETTO255_OPRF_RISTRETTO255_KEGROUP_3DH_KEX_ARGON2ID13_KSF";
|
||||
|
||||
export class CipherConfiguration {
|
||||
opaqueVersion: OpaqueKeVersion;
|
||||
cipherSuite: CipherSuite;
|
||||
argon2Parameters: Argon2IdParameters;
|
||||
|
||||
oprfCs: OprfCs;
|
||||
keGroup: KeGroup;
|
||||
keyExchange: KeyExchange;
|
||||
ksf: KsfConfig;
|
||||
|
||||
constructor(ksf: KsfConfig) {
|
||||
this.opaqueVersion = 3;
|
||||
this.oprfCs = "ristretto255";
|
||||
this.keGroup = "ristretto255";
|
||||
this.keyExchange = "triple-dh";
|
||||
this.ksf = ksf;
|
||||
// only support one ciphersuite for now
|
||||
constructor(ksf: Argon2IdParameters) {
|
||||
this.cipherSuite = "OPAQUE_3_RISTRETTO255_OPRF_RISTRETTO255_KEGROUP_3DH_KEX_ARGON2ID13_KSF";
|
||||
this.argon2Parameters = ksf;
|
||||
}
|
||||
|
||||
toSdkConfig(): CipherConfigurationSdk {
|
||||
if (this.ksf.algorithm !== "argon2id") {
|
||||
throw new Error("Unsupported KSF algorithm");
|
||||
if (
|
||||
this.cipherSuite !== "OPAQUE_3_RISTRETTO255_OPRF_RISTRETTO255_KEGROUP_3DH_KEX_ARGON2ID13_KSF"
|
||||
) {
|
||||
throw new Error("Unsupported cipher suite");
|
||||
} else {
|
||||
return {
|
||||
oprf_cs: this.oprfCs,
|
||||
ke_group: this.keGroup,
|
||||
key_exchange: this.keyExchange,
|
||||
oprf_cs: "ristretto255",
|
||||
ke_group: "ristretto255",
|
||||
key_exchange: "triple-dh",
|
||||
ksf: {
|
||||
argon2id: [
|
||||
this.ksf.parameters.memory,
|
||||
this.ksf.parameters.iterations,
|
||||
this.ksf.parameters.parallelism,
|
||||
this.argon2Parameters.memory,
|
||||
this.argon2Parameters.iterations,
|
||||
this.argon2Parameters.parallelism,
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -38,22 +36,9 @@ export class CipherConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
export type OprfCs = "ristretto255";
|
||||
export type KeGroup = "ristretto255";
|
||||
export type KeyExchange = "triple-dh";
|
||||
|
||||
export type Argon2IdParameters = {
|
||||
// Memory in KiB
|
||||
memory: number;
|
||||
iterations: number;
|
||||
parallelism: number;
|
||||
};
|
||||
|
||||
export type KsfParameters = Argon2IdParameters;
|
||||
|
||||
type KsfAlgorithm = "argon2id";
|
||||
|
||||
export type KsfConfig = {
|
||||
algorithm: KsfAlgorithm;
|
||||
parameters: KsfParameters;
|
||||
};
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import { OpaqueSessionId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { UserKey } from "../../types/key";
|
||||
|
||||
import { KsfConfig } from "./models/cipher-configuration";
|
||||
import { Argon2IdParameters } from "./models/cipher-configuration";
|
||||
|
||||
export abstract class OpaqueService {
|
||||
/**
|
||||
* Register a user to use the Opaque login method.
|
||||
*/
|
||||
abstract register(masterPassword: string, userKey: UserKey, ksfConfig: KsfConfig): Promise<void>;
|
||||
abstract register(
|
||||
masterPassword: string,
|
||||
userKey: UserKey,
|
||||
ksfParameters: Argon2IdParameters,
|
||||
): Promise<OpaqueSessionId>;
|
||||
|
||||
/**
|
||||
* Authenticate using the Opaque login method. Returns the export key, which must be used
|
||||
* in combination with the rotateable keyset returned from the token endpoint.
|
||||
* @returns The ExportKey obtained during the Opaque login flow.
|
||||
*/
|
||||
abstract login(email: string, masterPassword: string, ksfConfig: KsfConfig): Promise<Uint8Array>;
|
||||
abstract login(
|
||||
email: string,
|
||||
masterPassword: string,
|
||||
ksfParameters: Argon2IdParameters,
|
||||
): Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user