1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-02 18:17:46 +01:00
This commit is contained in:
Kyle Spearrin 2024-01-23 13:21:59 -05:00 committed by GitHub
parent 014281cb93
commit e359aef979
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 63 additions and 9 deletions

View File

@ -39,12 +39,19 @@ export class Client {
async openVault( async openVault(
username: string, username: string,
password: string, password: string,
fragmentId: string,
clientInfo: ClientInfo, clientInfo: ClientInfo,
ui: Ui, ui: Ui,
options: ParserOptions, options: ParserOptions,
): Promise<Account[]> { ): Promise<Account[]> {
const lowercaseUsername = username.toLowerCase(); const lowercaseUsername = username.toLowerCase();
const [session, rest] = await this.login(lowercaseUsername, password, clientInfo, ui); const [session, rest] = await this.login(
lowercaseUsername,
password,
fragmentId,
clientInfo,
ui,
);
try { try {
const blob = await this.downloadVault(session, rest); const blob = await this.downloadVault(session, rest);
const key = await this.cryptoUtils.deriveKey( const key = await this.cryptoUtils.deriveKey(
@ -111,6 +118,7 @@ export class Client {
private async login( private async login(
username: string, username: string,
password: string, password: string,
fragmentId: string,
clientInfo: ClientInfo, clientInfo: ClientInfo,
ui: Ui, ui: Ui,
): Promise<[Session, RestClient]> { ): Promise<[Session, RestClient]> {
@ -142,6 +150,7 @@ export class Client {
response = await this.performSingleLoginRequest( response = await this.performSingleLoginRequest(
username, username,
password, password,
fragmentId,
keyIterationCount, keyIterationCount,
new Map<string, any>(), new Map<string, any>(),
clientInfo, clientInfo,
@ -191,6 +200,7 @@ export class Client {
session = await this.loginWithOtp( session = await this.loginWithOtp(
username, username,
password, password,
fragmentId,
keyIterationCount, keyIterationCount,
optMethod, optMethod,
clientInfo, clientInfo,
@ -203,6 +213,7 @@ export class Client {
session = await this.loginWithOob( session = await this.loginWithOob(
username, username,
password, password,
fragmentId,
keyIterationCount, keyIterationCount,
this.getAllErrorAttributes(response), this.getAllErrorAttributes(response),
clientInfo, clientInfo,
@ -223,6 +234,7 @@ export class Client {
private async loginWithOtp( private async loginWithOtp(
username: string, username: string,
password: string, password: string,
fragmentId: string,
keyIterationCount: number, keyIterationCount: number,
method: OtpMethod, method: OtpMethod,
clientInfo: ClientInfo, clientInfo: ClientInfo,
@ -251,6 +263,7 @@ export class Client {
const response = await this.performSingleLoginRequest( const response = await this.performSingleLoginRequest(
username, username,
password, password,
fragmentId,
keyIterationCount, keyIterationCount,
new Map<string, string>([["otp", passcode.passcode]]), new Map<string, string>([["otp", passcode.passcode]]),
clientInfo, clientInfo,
@ -270,6 +283,7 @@ export class Client {
private async loginWithOob( private async loginWithOob(
username: string, username: string,
password: string, password: string,
fragmentId: string,
keyIterationCount: number, keyIterationCount: number,
parameters: Map<string, string>, parameters: Map<string, string>,
clientInfo: ClientInfo, clientInfo: ClientInfo,
@ -282,6 +296,7 @@ export class Client {
const response = await this.performSingleLoginRequest( const response = await this.performSingleLoginRequest(
username, username,
password, password,
fragmentId,
keyIterationCount, keyIterationCount,
extraParameters, extraParameters,
clientInfo, clientInfo,
@ -494,6 +509,7 @@ export class Client {
private async performSingleLoginRequest( private async performSingleLoginRequest(
username: string, username: string,
password: string, password: string,
fragmentId: string,
keyIterationCount: number, keyIterationCount: number,
extraParameters: Map<string, any>, extraParameters: Map<string, any>,
clientInfo: ClientInfo, clientInfo: ClientInfo,
@ -513,6 +529,10 @@ export class Client {
// TODO: Test against the real server if it's ok to send this every time! // TODO: Test against the real server if it's ok to send this every time!
["trustlabel", clientInfo.description], ["trustlabel", clientInfo.description],
]); ]);
if (fragmentId != null) {
parameters.set("alpfragmentid", fragmentId);
parameters.set("calculatedfragmentid", fragmentId);
}
for (const [key, value] of extraParameters) { for (const [key, value] of extraParameters) {
parameters.set(key, value); parameters.set(key, value);
} }

View File

@ -55,7 +55,7 @@ export class Parser {
// 3: url // 3: url
let url = Utils.fromBufferToUtf8( let url = Utils.fromBufferToUtf8(
Utils.fromHexToArray(Utils.fromBufferToUtf8(this.readItem(reader))), this.decodeHexLoose(Utils.fromBufferToUtf8(this.readItem(reader))),
); );
// Ignore "group" accounts. They have no credentials. // Ignore "group" accounts. They have no credentials.
@ -354,4 +354,9 @@ export class Parser {
private readPayload(reader: BinaryReader, size: number): Uint8Array { private readPayload(reader: BinaryReader, size: number): Uint8Array {
return reader.readBytes(size); return reader.readBytes(size);
} }
private decodeHexLoose(s: string): Uint8Array {
// This is a forgiving version that pads the input with a '0' when the length is odd
return Utils.fromHexToArray(s.length % 2 == 0 ? s : "0" + s);
}
} }

View File

@ -40,7 +40,14 @@ export class Vault {
ui: Ui, ui: Ui,
parserOptions: ParserOptions = ParserOptions.default, parserOptions: ParserOptions = ParserOptions.default,
): Promise<void> { ): Promise<void> {
this.accounts = await this.client.openVault(username, password, clientInfo, ui, parserOptions); this.accounts = await this.client.openVault(
username,
password,
null,
clientInfo,
ui,
parserOptions,
);
} }
async openFederated( async openFederated(
@ -53,13 +60,20 @@ export class Vault {
throw new Error("Federated user context is not set."); throw new Error("Federated user context is not set.");
} }
const k1 = await this.getK1(federatedUser); const k1 = await this.getK1(federatedUser);
const k2 = await this.getK2(federatedUser); const [k2, fragmentId] = await this.getK2FragmentId(federatedUser);
const hiddenPasswordArr = await this.cryptoFunctionService.hash( const hiddenPasswordArr = await this.cryptoFunctionService.hash(
this.cryptoUtils.ExclusiveOr(k1, k2), this.cryptoUtils.ExclusiveOr(k1, k2),
"sha256", "sha256",
); );
const hiddenPassword = Utils.fromBufferToB64(hiddenPasswordArr); const hiddenPassword = Utils.fromBufferToB64(hiddenPasswordArr);
await this.open(federatedUser.username, hiddenPassword, clientInfo, ui, parserOptions); this.accounts = await this.client.openVault(
federatedUser.username,
hiddenPassword,
fragmentId,
clientInfo,
ui,
parserOptions,
);
} }
async setUserTypeContext(username: string) { async setUserTypeContext(username: string) {
@ -80,6 +94,18 @@ export class Vault {
this.userType.pkceEnabled = json.PkceEnabled; this.userType.pkceEnabled = json.PkceEnabled;
this.userType.provider = json.Provider; this.userType.provider = json.Provider;
this.userType.type = json.type; this.userType.type = json.type;
if (this.userType.provider === IdpProvider.Azure) {
// Sometimes customers have malformed OIDC authority URLs. Try to fix them.
const appQueryIndex = this.userType.openIDConnectAuthority.indexOf("?app");
if (appQueryIndex > -1) {
this.userType.openIDConnectAuthority = this.userType.openIDConnectAuthority.substring(
0,
appQueryIndex,
);
}
}
return; return;
} }
throw new Error("Cannot determine LastPass user type."); throw new Error("Cannot determine LastPass user type.");
@ -194,7 +220,9 @@ export class Vault {
return null; return null;
} }
private async getK2(federatedUser: FederatedUserContext): Promise<Uint8Array> { private async getK2FragmentId(
federatedUser: FederatedUserContext,
): Promise<[Uint8Array, string]> {
if (this.userType == null) { if (this.userType == null) {
throw new Error("User type is not set."); throw new Error("User type is not set.");
} }
@ -212,10 +240,11 @@ export class Vault {
if (response.status === HttpStatusCode.Ok) { if (response.status === HttpStatusCode.Ok) {
const json = await response.json(); const json = await response.json();
const k2 = json?.k2 as string; const k2 = json?.k2 as string;
if (k2 != null) { const fragmentId = json?.fragment_id as string;
return Utils.fromB64ToArray(k2); if (k2 != null && fragmentId != null) {
return [Utils.fromB64ToArray(k2), fragmentId];
} }
} }
throw new Error("Cannot get k2."); throw new Error("Cannot get k2 and/or fragment ID.");
} }
} }