diff --git a/libs/common/src/key-management/local-user-data-key-mapper.ts b/libs/common/src/key-management/local-user-data-key-mapper.ts new file mode 100644 index 00000000000..0ed037be44f --- /dev/null +++ b/libs/common/src/key-management/local-user-data-key-mapper.ts @@ -0,0 +1,24 @@ +import { SdkRecordMapper } from "@bitwarden/common/platform/services/sdk/client-managed-state"; +import { UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { LocalUserDataKeyState } from "@bitwarden/sdk-internal"; + +import { LOCAL_USER_DATA_KEY } from "../platform/services/key-state/local-user-data-key.state"; + +import { LocalUserDataKey } from "./types"; + +export class LocalUserDataKeyRecordMapper implements SdkRecordMapper< + LocalUserDataKey, + LocalUserDataKeyState +> { + userKeyDefinition(): UserKeyDefinition> { + return LOCAL_USER_DATA_KEY; + } + + toSdk(value: LocalUserDataKey): LocalUserDataKeyState { + return { wrapped_key: value } as LocalUserDataKeyState; + } + + fromSdk(value: LocalUserDataKeyState): LocalUserDataKey { + return value.wrapped_key as LocalUserDataKey; + } +} diff --git a/libs/common/src/key-management/types.ts b/libs/common/src/key-management/types.ts index 1c349e83a03..80ea53f1cdd 100644 --- a/libs/common/src/key-management/types.ts +++ b/libs/common/src/key-management/types.ts @@ -32,3 +32,8 @@ export type VerifyingKey = Opaque; * A signed security state, encoded in base64. */ export type SignedSecurityState = Opaque; + +/** + * A local user data key, encrypted with a symmetric key. + */ +export type LocalUserDataKey = Opaque; diff --git a/libs/common/src/platform/services/key-state/local-user-data-key.state.ts b/libs/common/src/platform/services/key-state/local-user-data-key.state.ts new file mode 100644 index 00000000000..6c57da80819 --- /dev/null +++ b/libs/common/src/platform/services/key-state/local-user-data-key.state.ts @@ -0,0 +1,11 @@ +import { LocalUserDataKey } from "../../../key-management/types"; +import { CRYPTO_DISK, UserKeyDefinition } from "../../state"; + +export const LOCAL_USER_DATA_KEY = UserKeyDefinition.record( + CRYPTO_DISK, + "localUserDataKey", + { + deserializer: (obj) => obj, + clearOn: ["logout"], + }, +); diff --git a/libs/common/src/platform/services/sdk/client-managed-state.ts b/libs/common/src/platform/services/sdk/client-managed-state.ts index 17bc77d90e8..f3067f019ad 100644 --- a/libs/common/src/platform/services/sdk/client-managed-state.ts +++ b/libs/common/src/platform/services/sdk/client-managed-state.ts @@ -2,12 +2,13 @@ import { firstValueFrom, map } from "rxjs"; import { UserId } from "@bitwarden/common/types/guid"; import { CipherRecordMapper } from "@bitwarden/common/vault/models/domain/cipher-sdk-mapper"; -import { StateClient, Repository } from "@bitwarden/sdk-internal"; +import { Repository, StateClient } from "@bitwarden/sdk-internal"; +import { LocalUserDataKeyRecordMapper } from "../../../key-management/local-user-data-key-mapper"; import { UserKeyRecordMapper } from "../../../key-management/user-key-mapper"; import { StateProvider, UserKeyDefinition } from "../../state"; -export async function initializeState( +export async function initializeClientManagedState( userId: UserId, stateClient: StateClient, stateProvider: StateProvider, @@ -16,6 +17,11 @@ export async function initializeState( cipher: new RepositoryRecord(userId, stateProvider, new CipherRecordMapper()), folder: null, user_key_state: new RepositoryRecord(userId, stateProvider, new UserKeyRecordMapper()), + local_user_data_key_state: new RepositoryRecord( + userId, + stateProvider, + new LocalUserDataKeyRecordMapper(), + ), }); } @@ -32,14 +38,6 @@ export class RepositoryRecord implements Repository, ) {} - private getUserState() { - return this.stateProvider.getUser(this.userId, this.mapper.userKeyDefinition()); - } - - private async getRecord(): Promise> { - return await firstValueFrom(this.getUserState().state$.pipe(map((state) => state ?? {}))); - } - async get(id: string): Promise { const record = await this.getRecord(); const element = record[id]; @@ -93,4 +91,12 @@ export class RepositoryRecord implements Repository { await this.getUserState().update(() => ({})); } + + private getUserState() { + return this.stateProvider.getUser(this.userId, this.mapper.userKeyDefinition()); + } + + private async getRecord(): Promise> { + return await firstValueFrom(this.getUserState().state$.pipe(map((state) => state ?? {}))); + } } diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index 156d7ac83b9..88f1198e074 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -52,7 +52,7 @@ import { compareValues } from "../../misc/compare-values"; import { Rc } from "../../misc/reference-counting/rc"; import { StateProvider } from "../../state"; -import { initializeState } from "./client-managed-state"; +import { initializeClientManagedState } from "./client-managed-state"; // A symbol that represents an overridden client that is explicitly set to undefined, // blocking the creation of an internal client for that user. @@ -252,8 +252,8 @@ export class DefaultSdkService implements SdkService { accountCryptographicState: WrappedAccountCryptographicState, orgKeys: Record, ) { - // Initialize the SDK managed database and the client managed repositories. - await initializeState(userId, client.platform().state(), this.stateProvider); + // Initialize the client managed repositories. + await initializeClientManagedState(userId, client.platform().state(), this.stateProvider); await this.loadFeatureFlags(client); if (await this.configService.getFeatureFlag(FeatureFlag.UnlockViaSDK)) { diff --git a/libs/common/src/platform/services/sdk/register-sdk.service.ts b/libs/common/src/platform/services/sdk/register-sdk.service.ts index 073c5c0560c..a109b9b0338 100644 --- a/libs/common/src/platform/services/sdk/register-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/register-sdk.service.ts @@ -29,7 +29,7 @@ import { toSdkDevice, UserNotLoggedInError } from "../../abstractions/sdk/sdk.se import { Rc } from "../../misc/reference-counting/rc"; import { StateProvider } from "../../state"; -import { initializeState } from "./client-managed-state"; +import { initializeClientManagedState } from "./client-managed-state"; // A symbol that represents an overridden client that is explicitly set to undefined, // blocking the creation of an internal client for that user. @@ -143,8 +143,12 @@ export class DefaultRegisterSdkService implements RegisterSdkService { settings, ); - // Initialize the SDK managed database and the client managed repositories. - await initializeState(userId, client.platform().state(), this.stateProvider); + // Initialize the client managed repositories. + await initializeClientManagedState( + userId, + client.platform().state(), + this.stateProvider, + ); await this.loadFeatureFlags(client); diff --git a/package-lock.json b/package-lock.json index 75c0e8df962..062c8c43256 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,8 @@ "@angular/platform-browser": "20.3.17", "@angular/platform-browser-dynamic": "20.3.17", "@angular/router": "20.3.17", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.579", - "@bitwarden/sdk-internal": "0.2.0-main.579", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.589", + "@bitwarden/sdk-internal": "0.2.0-main.589", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", @@ -4946,9 +4946,9 @@ "link": true }, "node_modules/@bitwarden/commercial-sdk-internal": { - "version": "0.2.0-main.579", - "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.579.tgz", - "integrity": "sha512-QZhDwEd/Y4fijqk2zpeH6z4ln8HE6XeKKqbN5asRtmlZFKcT9fT4xZy+u51QYlTZb8uqfVsQoVHiElL6JZYYng==", + "version": "0.2.0-main.589", + "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.589.tgz", + "integrity": "sha512-+4qZ0gj71jOFepD0hIwhcuM/Eegf+JP1IbQmYA5YIA1Q5TBpms6sULcQZRTkJEokH8PiGH5xxvUk4k/XQAL2tQ==", "license": "BITWARDEN SOFTWARE DEVELOPMENT KIT LICENSE AGREEMENT", "dependencies": { "type-fest": "^5.0.0" @@ -5054,9 +5054,9 @@ "link": true }, "node_modules/@bitwarden/sdk-internal": { - "version": "0.2.0-main.579", - "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.579.tgz", - "integrity": "sha512-bXRdhaBf/rtqdDFlhs7YQ0G8gN51ACUAPza/XpHx7cL+40CSr+hdzljnQPItaEtXdyNsAF71nMnLiTvGp2rVcg==", + "version": "0.2.0-main.589", + "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.589.tgz", + "integrity": "sha512-PDygPFmu+t/GHHLZjKjWOnpFjck4pYRZArB4ZBuyh7rwWK1tX5brBWtZu38+pJPVTiBHy1D1TUwIHbWfeiS0Uw==", "license": "GPL-3.0", "dependencies": { "type-fest": "^5.0.0" diff --git a/package.json b/package.json index 5d31a2d9c40..969f5db75b1 100644 --- a/package.json +++ b/package.json @@ -165,8 +165,8 @@ "@angular/platform-browser": "20.3.17", "@angular/platform-browser-dynamic": "20.3.17", "@angular/router": "20.3.17", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.579", - "@bitwarden/sdk-internal": "0.2.0-main.579", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.589", + "@bitwarden/sdk-internal": "0.2.0-main.589", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0",