diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index f1de0934922..f24b6446e48 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -669,6 +669,7 @@ export default class MainBackground { this.encryptService, this.logService, this.cryptoFunctionService, + this.accountService, ); this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider); diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index f076afbb515..f54fd122cb2 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -439,6 +439,7 @@ export class ServiceContainer { this.encryptService, this.logService, this.cryptoFunctionService, + this.accountService, ); this.pinService = new PinService( diff --git a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts index 2738a54b841..6e889c5191c 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts @@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { of } from "rxjs"; import * as rxjs from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; // eslint-disable-next-line no-restricted-imports @@ -32,6 +33,7 @@ describe("MasterPasswordService", () => { let encryptService: MockProxy; let logService: MockProxy; let cryptoFunctionService: MockProxy; + let accountService: MockProxy; const userId = "user-id" as UserId; const mockUserState = { @@ -54,6 +56,7 @@ describe("MasterPasswordService", () => { encryptService = mock(); logService = mock(); cryptoFunctionService = mock(); + accountService = mock(); stateProvider.getUser.mockReturnValue(mockUserState as any); @@ -66,6 +69,7 @@ describe("MasterPasswordService", () => { encryptService, logService, cryptoFunctionService, + accountService, ); encryptService.unwrapSymmetricKey.mockResolvedValue(makeSymmetricCryptoKey(64, 1)); diff --git a/libs/common/src/key-management/master-password/services/master-password.service.ts b/libs/common/src/key-management/master-password/services/master-password.service.ts index 6f2d8689221..6d74a084593 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.ts @@ -2,6 +2,8 @@ // @ts-strict-ignore import { firstValueFrom, map, Observable } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { assertNonNullish } from "@bitwarden/common/auth/utils"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; // eslint-disable-next-line no-restricted-imports @@ -74,8 +76,17 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr private encryptService: EncryptService, private logService: LogService, private cryptoFunctionService: CryptoFunctionService, + private accountService: AccountService, ) {} + saltForUser$(userId: UserId): Observable { + assertNonNullish(userId, "userId"); + return this.accountService.accounts$.pipe( + map((accounts) => accounts[userId].email), + map((email) => this.emailToSalt(email)), + ); + } + masterKey$(userId: UserId): Observable { if (userId == null) { throw new Error("User ID is required."); @@ -227,15 +238,12 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr kdf: KdfConfig, salt: MasterPasswordSalt, ): Promise { - if (password == null) { - throw new Error("Password is required."); - } - if (kdf == null) { - throw new Error("KDF configuration is required."); - } - if (salt == null) { - throw new Error("Salt is required."); - } + assertNonNullish(password, "password"); + assertNonNullish(kdf, "kdf"); + assertNonNullish(salt, "salt"); + + // We don't trust callers to use masterpasswordsalt correctly. They may type assert incorrectly. + salt = salt.toLowerCase().trim() as MasterPasswordSalt; const SERVER_AUTHENTICATION_HASH_ITERATIONS = 1; @@ -267,18 +275,13 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr salt: MasterPasswordSalt, userKey: UserKey, ): Promise { - if (password == null) { - throw new Error("Password is required."); - } - if (kdf == null) { - throw new Error("KDF configuration is required."); - } - if (salt == null) { - throw new Error("Salt is required."); - } - if (userKey == null) { - throw new Error("User key is required."); - } + assertNonNullish(password, "password"); + assertNonNullish(kdf, "kdf"); + assertNonNullish(salt, "salt"); + assertNonNullish(userKey, "userKey"); + + // We don't trust callers to use masterpasswordsalt correctly. They may type assert incorrectly. + salt = salt.toLowerCase().trim() as MasterPasswordSalt; await SdkLoadService.Ready; const masterKeyWrappedUserKey = new EncString( @@ -300,12 +303,8 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr password: string, masterPasswordUnlockData: MasterPasswordUnlockData, ): Promise { - if (password == null) { - throw new Error("Password is required."); - } - if (masterPasswordUnlockData == null) { - throw new Error("Master password unlock data is required."); - } + assertNonNullish(password, "password"); + assertNonNullish(masterPasswordUnlockData, "masterPasswordUnlockData"); await SdkLoadService.Ready; const userKey = new SymmetricCryptoKey( diff --git a/libs/common/src/types/key.ts b/libs/common/src/types/key.ts index c9fd6975960..8984452e701 100644 --- a/libs/common/src/types/key.ts +++ b/libs/common/src/types/key.ts @@ -6,6 +6,7 @@ import { SymmetricCryptoKey } from "../platform/models/domain/symmetric-crypto-k export type DeviceKey = Opaque; export type PrfKey = Opaque; export type UserKey = Opaque; +/** @deprecated Interacting with the master key directly is prohibited. Use a high level function from MasterPasswordService instead. */ export type MasterKey = Opaque; export type PinKey = Opaque; export type OrgKey = Opaque; diff --git a/libs/key-management/src/abstractions/key.service.ts b/libs/key-management/src/abstractions/key.service.ts index c843d8dc872..d9669f07c44 100644 --- a/libs/key-management/src/abstractions/key.service.ts +++ b/libs/key-management/src/abstractions/key.service.ts @@ -150,11 +150,17 @@ export abstract class KeyService { /** * Generates a new user key + * @deprecated Interacting with the master key directly is prohibited. Use a high level function from MasterPasswordService instead. * @throws Error when master key is null and there is no active user * @param masterKey The user's master key. When null, grabs master key from active user. * @returns A new user key and the master key protected version of it */ abstract makeUserKey(masterKey: MasterKey | null): Promise<[UserKey, EncString]>; + /** + * Generates a new user key for a V1 user + * @returns A new user key + */ + abstract makeUserKeyV1(): Promise; /** * Clears the user's stored version of the user key * @param keySuffix The desired version of the key to clear @@ -164,12 +170,14 @@ export abstract class KeyService { abstract clearStoredUserKey(keySuffix: KeySuffixOptions, userId: string): Promise; /** * @throws Error when userId is null and no active user + * @deprecated Interacting with the master key directly is prohibited. Use a high level function from MasterPasswordService instead. * @param password The user's master password that will be used to derive a master key if one isn't found * @param userId The desired user */ abstract getOrDeriveMasterKey(password: string, userId?: string): Promise; /** * Generates a master key from the provided password + * @deprecated Interacting with the master key directly is prohibited. * @param password The user's master password * @param email The user's email * @param KdfConfig The user's key derivation function configuration @@ -179,6 +187,7 @@ export abstract class KeyService { /** * Encrypts the existing (or provided) user key with the * provided master key + * @deprecated Interacting with the master key directly is prohibited. Use a high level function from MasterPasswordService instead. * @param masterKey The user's master key * @param userKey The user key * @returns The user key and the master key protected version of it @@ -191,6 +200,7 @@ export abstract class KeyService { * Creates a master password hash from the user's master password. Can * be used for local authentication or for server authentication depending * on the hashPurpose provided. + * @deprecated Interacting with the master key directly is prohibited. Use a high level function from MasterPasswordService instead. * @throws Error when password is null or key is null and no active user or active user have no master key * @param password The user's master password * @param key The user's master key or active's user master key. @@ -204,6 +214,7 @@ export abstract class KeyService { ): Promise; /** * Compares the provided master password to the stored password hash. + * @deprecated Interacting with the master key directly is prohibited. Use a high level function from MasterPasswordService instead. * @param masterPassword The user's master password * @param key The user's master key * @param userId The id of the user to do the operation for. diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index eed5a529204..14ef9b16c91 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -215,9 +215,6 @@ export class DefaultKeyService implements KeyServiceAbstraction { return (await firstValueFrom(this.stateProvider.getUserState$(USER_KEY, userId))) != null; } - /** - * @deprecated Please use `makeMasterPasswordUnlockData` in {@link MasterPasswordService} instead. - */ async makeUserKey(masterKey: MasterKey | null): Promise<[UserKey, EncString]> { if (masterKey == null) { const userId = await firstValueFrom(this.stateProvider.activeUserId$); @@ -235,6 +232,11 @@ export class DefaultKeyService implements KeyServiceAbstraction { return this.buildProtectedSymmetricKey(masterKey, newUserKey); } + async makeUserKeyV1(): Promise { + const newUserKey = await this.keyGenerationService.createKey(512); + return newUserKey as UserKey; + } + /** * Clears the user key. Clears all stored versions of the user keys as well, such as the biometrics key * @param userId The desired user