import { Observable } from "rxjs"; import { ProfileOrganizationResponse } from "@bitwarden/common/admin-console/models/response/profile-organization.response"; import { ProfileProviderOrganizationResponse } from "@bitwarden/common/admin-console/models/response/profile-provider-organization.response"; import { ProfileProviderResponse } from "@bitwarden/common/admin-console/models/response/profile-provider.response"; import { EncryptedString, EncString, } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { WrappedSigningKey } from "@bitwarden/common/key-management/types"; import { KeySuffixOptions, HashPurpose } from "@bitwarden/common/platform/enums"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid"; import { UserKey, MasterKey, OrgKey, ProviderKey, CipherKey, UserPrivateKey, UserPublicKey, } from "@bitwarden/common/types/key"; import { KdfConfig } from "../models/kdf-config"; export class UserPrivateKeyDecryptionFailedError extends Error { constructor() { super("Failed to decrypt the user's private key."); } } /** * An object containing all the users key needed to decrypt a users personal and organization vaults. */ export type CipherDecryptionKeys = { /** * A users {@link UserKey} that is useful for decrypted ciphers in the users personal vault. */ userKey: UserKey; /** * A users decrypted organization keys. */ orgKeys: Record | null; }; export abstract class KeyService { /** * Retrieves a stream of the given users {@see UserKey} values. Can emit null if the user does not have a user key, e.g. the user * is in a locked or logged out state. * @param userId The user id of the user to get the {@see UserKey} for. */ abstract userKey$(userId: UserId): Observable; /** * Returns the an observable key for the given user id. * * @note this observable represents only user keys stored in memory. A null value does not indicate that we cannot load a user key from storage. * @param userId The desired user */ abstract getInMemoryUserKeyFor$(userId: UserId): Observable; /** * Sets the provided user key and stores * any other necessary versions (such as auto, biometrics, * or pin) * * @throws Error when key or userId is null. Lock the account to clear a key. * @param key The user key to set * @param userId The desired user */ abstract setUserKey(key: UserKey, userId: UserId): Promise; /** * Sets the provided user keys and stores any other necessary versions * (such as auto, biometrics, or pin). * Also sets the user's encrypted private key in storage and * clears the decrypted private key from memory * Note: does not clear the private key if null is provided * * @throws Error when userKey, encPrivateKey or userId is null * @throws UserPrivateKeyDecryptionFailedError when the userKey cannot decrypt encPrivateKey * @param userKey The user key to set * @param encPrivateKey An encrypted private key * @param userId The desired user */ abstract setUserKeys(userKey: UserKey, encPrivateKey: string, userId: UserId): Promise; /** * Gets the user key from memory and sets it again, * kicking off a refresh of any additional keys * (such as auto, biometrics, or pin) * @param userId The target user to refresh keys for. * @throws Error when userId is null or undefined. * @throws When userKey doesn't exist in memory for the target user. */ abstract refreshAdditionalKeys(userId: UserId): Promise; /** * Observable value that returns whether or not the user has ever had a userKey, * i.e. has ever been unlocked/decrypted. This is key for differentiating between TDE locked and standard locked states. */ abstract everHadUserKey$(userId: UserId): Observable; /** * Retrieves the user key * @param userId The desired user * @returns The user key * * @deprecated Use {@link userKey$} with a required {@link UserId} instead. */ abstract getUserKey(userId?: string): Promise; /** * Retrieves the user key from storage * @param keySuffix The desired version of the user's key to retrieve * @param userId The desired user * @returns The user key * @throws Error when userId is null or undefined. */ abstract getUserKeyFromStorage( keySuffix: KeySuffixOptions, userId: string, ): Promise; /** * Determines whether the user key is available for the given user in memory. * @param userId The desired user. If null or undefined, will return false. * @returns True if the user key is available, returns false otherwise. */ abstract hasUserKey(userId: UserId): Promise; /** * Generates a new user key * @deprecated Interacting with the master key directly is prohibited. Use {@link makeUserKeyV1} 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 * Note: This will be replaced by a higher level function to initialize a whole users cryptographic state in the near future. * @returns A new user key */ abstract makeUserKeyV1(): Promise; /** * Clears the user's stored version of the user key * @param userId The desired user * @throws Error when userId is null or undefined. */ abstract clearStoredUserKey(userId: string): Promise; /** * Retrieves the user's master key if it is in state, or derives it from the provided password * @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 * @deprecated Interacting with the master key directly is prohibited. Use a high level function from MasterPasswordService instead. * @throws Error when userId is null/undefined. * @throws Error when email or Kdf configuration cannot be found for the user. * @returns The user's master key if it exists, or a newly derived master key. */ abstract getOrDeriveMasterKey(password: string, userId: UserId): 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 * @returns A master key derived from the provided password */ abstract makeMasterKey(password: string, email: string, kdfConfig: KdfConfig): Promise; /** * Encrypts the 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 * @throws Error when userKey or masterKey is null/undefined. * @returns The user key and the master key protected version of it */ abstract encryptUserKeyWithMasterKey( masterKey: MasterKey, userKey: UserKey, ): Promise<[UserKey, EncString]>; /** * 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. * @param password The user's master password * @param key The user's master key or active's user master key. * @param hashPurpose The iterations to use for the hash. Defaults to {@link HashPurpose.ServerAuthorization}. * @throws Error when password is null/undefined or key is null/undefined. * @returns The user's master password hash */ abstract hashMasterKey( password: string, key: MasterKey, hashPurpose?: HashPurpose, ): 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 masterKey The user's master key * @param userId The id of the user to do the operation for. * @throws Error when master key is null/undefined. * @returns True if the derived master password hash matches the stored * key hash, false otherwise. */ abstract compareKeyHash( masterPassword: string, masterKey: MasterKey, userId: UserId, ): Promise; /** * Stores the encrypted organization keys and clears any decrypted * organization keys currently in memory * @param orgs The organizations to set keys for * @param providerOrgs The provider organizations to set keys for * @param userId The user id of the user to set the org keys for */ abstract setOrgKeys( orgs: ProfileOrganizationResponse[], providerOrgs: ProfileProviderOrganizationResponse[], userId: UserId, ): Promise; /** * Returns the organization's symmetric key * @deprecated Use the observable userOrgKeys$ and `map` to the desired {@link OrgKey} instead * @throws Error when not active user * @param orgId The desired organization * @returns The organization's symmetric key */ abstract getOrgKey(orgId: string): Promise; /** * Makes a fresh attachment content encryption key and returns it along with a wrapped (encrypted) version of it. * @deprecated Do not use this for new code / new cryptographic designs. * @param key The organization's symmetric key or the user's user key to wrap the attachment key with * @returns The new attachment content encryption key and the wrapped version of it */ abstract makeDataEncKey( key: T, ): Promise<[SymmetricCryptoKey, EncString]>; /** * Stores the provider keys for a given user. * @param providers The provider orgs for which to save the keys from. * @param userId The user id of the user for which to store the keys for. */ abstract setProviderKeys(providers: ProfileProviderResponse[], userId: UserId): Promise; /** * Gets an observable of provider keys for the given user. * @param userId The user to get provider keys for. * @return An observable stream of the users providers keys if they are unlocked, or null if the user is not unlocked. * @throws If an invalid user id is passed in. */ abstract providerKeys$(userId: UserId): Observable | null>; /** * Creates a new organization key and encrypts it with the user's public key. * This method can also return Provider keys for creating new Provider users. * @param userId The user id of the target user's public key to use. * @throws Error when userId is null or undefined. * @throws Error when no public key is found for the target user. * @returns The new encrypted OrgKey | ProviderKey and the decrypted key itself */ abstract makeOrgKey(userId: UserId): Promise<[EncString, T]>; /** * Sets the user's encrypted private key in storage and * clears the decrypted private key from memory * Note: does not clear the private key if null is provided * @param encPrivateKey An encrypted private key */ abstract setPrivateKey(encPrivateKey: string, userId: UserId): Promise; /** * Sets the user's encrypted signing key in storage * In contrast to the private key, the decrypted signing key * is not stored in memory outside of the SDK. * @param encryptedSigningKey An encrypted signing key * @param userId The user id of the user to set the signing key for */ abstract setUserSigningKey(encryptedSigningKey: WrappedSigningKey, userId: UserId): Promise; /** * Gets an observable stream of the given users decrypted private key, will emit null if the user * doesn't have a UserKey to decrypt the encrypted private key or null if the user doesn't have an * encrypted private key at all. * * @param userId The user id of the user to get the data for. * @returns An observable stream of the decrypted private key or null. * @throws Error when decryption of the encrypted private key fails. */ abstract userPrivateKey$(userId: UserId): Observable; /** * Gets an observable stream of the given users encrypted private key, will emit null if the user * doesn't have an encrypted private key at all. * * @param userId The user id of the user to get the data for. * * @deprecated Temporary function to allow the SDK to be initialized after the login process, it * will be removed when auth has been migrated to the SDK. */ abstract userEncryptedPrivateKey$(userId: UserId): Observable; /** * Gets an observable stream of the given users decrypted private key and public key, guaranteed to be consistent. * Will emit null if the user doesn't have a userkey to decrypt the encrypted private key, or null if the user doesn't have a private key * at all. * * @param userId The user id of the user to get the data for. */ abstract userEncryptionKeyPair$( userId: UserId, ): Observable<{ privateKey: UserPrivateKey; publicKey: UserPublicKey } | null>; /** * Gets an observable stream of the given users decrypted private key and public key, guaranteed to be consistent. * Will emit null if the user doesn't have a userkey to decrypt the encrypted private key, or null if the user doesn't have a private key * at all. * * @param userId The user id of the user to get the data for. */ abstract userEncryptionKeyPair$( userId: UserId, ): Observable<{ privateKey: UserPrivateKey; publicKey: UserPublicKey } | null>; /** * Generates a fingerprint phrase for the public key provided. * * @throws Error when publicKey is null or undefined. * @param fingerprintMaterial Fingerprint material * @param publicKey The public key to generate the fingerprint phrase for. * @returns The fingerprint phrase */ abstract getFingerprint(fingerprintMaterial: string, publicKey: Uint8Array): Promise; /** * Generates a new keypair * @param key A key to encrypt the private key with. If not provided, * defaults to the user key * @returns A new keypair: [publicKey in Base64, encrypted privateKey] * @throws If the provided key is a null-ish value. */ abstract makeKeyPair(key: SymmetricCryptoKey): Promise<[string, EncString]>; /** * @param keyMaterial The key material to derive the send key from * @returns A new send key */ abstract makeSendKey(keyMaterial: Uint8Array): Promise; /** * Clears all of the user's keys from storage * @param userId The user's Id * @throws Error when provided userId is null or undefined */ abstract clearKeys(userId: UserId): Promise; abstract randomNumber(min: number, max: number): Promise; /** * Generates a new cipher key * @returns A new cipher key */ abstract makeCipherKey(): Promise; /** * Initialize all necessary crypto keys needed for a new account. * Warning! This completely replaces any existing keys! * @param userId The user id of the target user. * @returns The user's newly created public key, private key, and encrypted private key * @throws An error if the userId is null or undefined. * @throws An error if the user already has a user key. */ abstract initAccount(userId: UserId): Promise<{ userKey: UserKey; publicKey: string; privateKey: EncString; }>; /** * Retrieves all the keys needed for decrypting Ciphers * @param userId The user id of the keys to retrieve or null if the user is not Unlocked * @param legacySupport `true` if you need to support retrieving the legacy version of the users key, `false` if * you do not need legacy support. Use `true` by necessity only. Defaults to `false`. Legacy support is for users * that may not have updated to use the new {@link UserKey} yet. * * @throws If an invalid user id is passed in. */ abstract cipherDecryptionKeys$( userId: UserId, legacySupport?: boolean, ): Observable; /** * Gets an observable of org keys for the given user. * @param userId The user id of the user of which to get the keys for. * @return An observable stream of the users organization keys if they are unlocked, or null if the user is not unlocked. * The observable will stay alive through locks/unlocks. * * @throws If an invalid user id is passed in. */ abstract orgKeys$(userId: UserId): Observable | null>; /** * Gets an observable stream of the given users encrypted organisation keys. * * @param userId The user id of the user to get the data for. * * @deprecated Temporary function to allow the SDK to be initialized after the login process, it * will be removed when auth has been migrated to the SDK. */ abstract encryptedOrgKeys$(userId: UserId): Observable>; /** * Gets an observable stream of the users public key. If the user is does not have * a {@link UserKey} or {@link UserPrivateKey} that is decryptable, this will emit null. * * @param userId The user id of the user of which to get the public key for. * * @throws If an invalid user id is passed in. */ abstract userPublicKey$(userId: UserId): Observable; /** * Gets a users signing keys from local state. * The observable will emit null, exactly if the local state returns null. */ abstract userSigningKey$(userId: UserId): Observable; /** * Validates that a userkey is correct for a given user * @param key The key to validate * @param userId The user id for the key */ abstract validateUserKey(key: UserKey, userId: UserId): Promise; }