import { Observable } from "rxjs"; import { EncryptedOrganizationKeyData } from "@bitwarden/common/admin-console/models/data/encrypted-organization-key.data"; import { ProfileOrganizationResponse } from "../../../common/src/admin-console/models/response/profile-organization.response"; import { ProfileProviderOrganizationResponse } from "../../../common/src/admin-console/models/response/profile-provider-organization.response"; import { ProfileProviderResponse } from "../../../common/src/admin-console/models/response/profile-provider.response"; import { KdfConfig } from "../../../common/src/auth/models/domain/kdf-config"; import { KeySuffixOptions, HashPurpose } from "../../../common/src/platform/enums"; import { EncryptedString, EncString } from "../../../common/src/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "../../../common/src/platform/models/domain/symmetric-crypto-key"; import { OrganizationId, UserId } from "../../../common/src/types/guid"; import { UserKey, MasterKey, OrgKey, ProviderKey, CipherKey, UserPrivateKey, UserPublicKey, } from "../../../common/src/types/key"; 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; }; 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 when key 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?: string): 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) */ abstract refreshAdditionalKeys(): Promise; /** * Observable value that returns whether or not the currently active user has ever had auser key, * i.e. has ever been unlocked/decrypted. This is key for differentiating between TDE locked and standard locked states. */ abstract everHadUserKey$: 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; /** * Checks if the user is using an old encryption scheme that used the master key * for encryption of data instead of the user key. */ abstract isLegacyUser(masterKey?: MasterKey, userId?: string): Promise; /** * Use for encryption/decryption of data in order to support legacy * encryption models. It will return the user key if available, * if not it will return the master key. * * @deprecated Please provide the userId of the user you want the user key for. */ abstract getUserKeyWithLegacySupport(): Promise; /** * Use for encryption/decryption of data in order to support legacy * encryption models. It will return the user key if available, * if not it will return the master key. * @param userId The desired user */ abstract getUserKeyWithLegacySupport(userId: UserId): 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 */ abstract getUserKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string): Promise; /** * Determines whether the user key is available for the given user. * @param userId The desired user. If not provided, the active user will be used. If no active user exists, the method will return false. * @returns True if the user key is available */ abstract hasUserKey(userId?: UserId): Promise; /** * Determines whether the user key is available for the given user in memory. * @param userId The desired user. If not provided, the active user will be used. If no active user exists, the method will return false. * @returns True if the user key is available */ abstract hasUserKeyInMemory(userId?: string): Promise; /** * @param keySuffix The desired version of the user's key to check * @param userId The desired user * @returns True if the provided version of the user key is stored */ abstract hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise; /** * Generates a new user key * @param masterKey The user's master key * @returns A new user key and the master key protected version of it */ abstract makeUserKey(key: MasterKey): Promise<[UserKey, EncString]>; /** * Clears the user's stored version of the user key * @param keySuffix The desired version of the key to clear * @param userId The desired user */ abstract clearStoredUserKey(keySuffix: KeySuffixOptions, userId?: string): Promise; /** * Stores the master key encrypted user key * @param userKeyMasterKey The master key encrypted user key to set * @param userId The desired user */ abstract setMasterKeyEncryptedUserKey(UserKeyMasterKey: string, userId: string): Promise; /** * @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 * @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 existing (or provided) user key with the * provided master key * @param masterKey The user's master key * @param userKey The user key * @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. * @param password The user's master password * @param key The user's master key * @param hashPurpose The iterations to use for the hash * @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. * @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. * @returns True if the provided master password matches either the stored * key hash or the server key hash */ 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; /** * Retrieves a stream of the active users organization keys, * will NOT emit any value if there is no active user. * * @deprecated Use {@link orgKeys$} with a required {@link UserId} instead. */ abstract activeUserOrgKeys$: Observable>; /** * Returns the organization's symmetric key * @deprecated Use the observable userOrgKeys$ and `map` to the desired {@link OrgKey} instead * @param orgId The desired organization * @returns The organization's symmetric key */ abstract getOrgKey(orgId: string): Promise; /** * Uses the org key to derive a new symmetric key for encrypting data * @param orgKey The organization's symmetric key */ abstract makeDataEncKey( key: T, ): Promise<[SymmetricCryptoKey, EncString]>; /** * Stores the provider keys for a given user. * @param orgs 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(orgs: ProfileProviderResponse[], userId: UserId): Promise; /** * @param providerId The desired provider * @returns The provider's symmetric key */ abstract getProviderKey(providerId: string): Promise; /** * 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. * @returns The new encrypted org key and the decrypted key itself */ abstract makeOrgKey(): 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; /** * Returns the private key from memory. If not available, decrypts it * from storage and stores it in memory * @returns The user's private key * * @throws An error if there is no user currently active. * * @deprecated Use {@link userPrivateKey$} instead. */ abstract getPrivateKey(): 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. */ 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 with legacy support, * 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. */ abstract userPrivateKeyWithLegacySupport$(userId: UserId): Observable; /** * Generates a fingerprint phrase for the user based on their public key * @param fingerprintMaterial Fingerprint material * @param publicKey The user's public key * @returns The user's 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]>; /** * Clears the user's pin keys from storage * Note: This will remove the stored pin and as a result, * disable pin protection for the user * @param userId The desired user */ abstract clearPinKeys(userId?: string): Promise; /** * @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 */ abstract clearKeys(userId?: string): 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! * @returns The user's newly created public key, private key, and encrypted private key * * @throws An error if there is no user currently active. */ abstract initAccount(): Promise<{ userKey: UserKey; publicKey: string; privateKey: EncString; }>; /** * Previously, the master key was used for any additional key like the biometrics or pin key. * We have switched to using the user key for these purposes. This method is for clearing the state * of the older keys on logout or post migration. * @param keySuffix The desired type of key to clear * @param userId The desired user */ abstract clearDeprecatedKeys(keySuffix: KeySuffixOptions, userId?: string): Promise; /** * 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; /** * 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; }