/// import type { Key, KeyPair, KeyPairLocked, PublicKey, PrivateKey, RecoveryCode, Signature, PasswordHash, PasswordSalt, BufferLocked, RecoveryCodeLocked, PasswordOpsLimit, PasswordMemLimit } from './types.js'; import type { NodeId } from '../ids/types.js'; import type { PolykeyWorkerManager } from '../workers/types.js'; import type { FileSystem, ObjectEmpty } from '../types.js'; import Logger from '@matrixai/logger'; import { createDestroyStartStop } from '@matrixai/async-init'; import { Lock } from '@matrixai/async-locks'; interface KeyRing extends createDestroyStartStop.CreateDestroyStartStop { } declare class KeyRing { static createKeyRing({ keysPath, passwordOpsLimit, passwordMemLimit, strictMemoryLock, workerManager, fs, logger, ...startOptions }: { keysPath: string; password: string; passwordOpsLimit?: PasswordOpsLimit; passwordMemLimit?: PasswordMemLimit; strictMemoryLock?: boolean; workerManager?: PolykeyWorkerManager; fs?: FileSystem; logger?: Logger; fresh?: boolean; } & ({} | { recoveryCode: RecoveryCode; } | { privateKey: PrivateKey; } | { privateKeyPath: string; })): Promise; readonly keysPath: string; readonly publicKeyPath: string; readonly privateKeyPath: string; readonly dbKeyPath: string; readonly strictMemoryLock: boolean; protected logger: Logger; protected fs: FileSystem; protected workerManager?: PolykeyWorkerManager; protected _keyPair?: KeyPairLocked; protected _dbKey?: BufferLocked; protected passwordHash?: Readonly<{ hash: BufferLocked; salt: BufferLocked; }>; protected passwordOpsLimit?: PasswordOpsLimit; protected passwordMemLimit?: PasswordMemLimit; protected _recoveryCodeData?: RecoveryCodeLocked; protected rotateLock: Lock; constructor({ keysPath, workerManager, passwordOpsLimit, passwordMemLimit, strictMemoryLock, fs, logger, }: { keysPath: string; workerManager?: PolykeyWorkerManager; passwordOpsLimit?: PasswordOpsLimit; passwordMemLimit?: PasswordMemLimit; strictMemoryLock: boolean; fs: FileSystem; logger: Logger; }); setWorkerManager(workerManager: PolykeyWorkerManager): void; unsetWorkerManager(): void; start(options: { password: string; fresh?: boolean; } & (ObjectEmpty | { recoveryCode: RecoveryCode; } | { privateKey: PrivateKey; } | { privateKeyPath: string; })): Promise; stop(): Promise; destroy(): Promise; get keyPair(): KeyPairLocked; get dbKey(): BufferLocked; get recoveryCode(): RecoveryCode | undefined; getNodeId(): NodeId; /** * Warning: this is intended to be a slow operation to prevent brute force * attacks */ checkPassword(password: string): Promise; /** * Changes the root key pair password. * This will re-wrap the private key. * The password is the new password. * This does not require the old password because * if the `KeyRing` is ready, that means the agent is unlocked * at least from the perspective of the `KeyRing`. * If an external client intends to change the password, * they must be authenticated first. */ changePassword(password: string): Promise; /** * Rotates the key pair. * This generates a new recovery code and new key pair. * The DB key is not rotated, it is just re-encrypted with the new key pair. * The key pair is wrapped with the new password. */ rotateKeyPair(password: string, rotateHook?: (keyPairNew: KeyPair, keyPairOld: KeyPair, recoveryCodeNew: RecoveryCode, recoveryCodeOld?: RecoveryCode) => any): Promise; /** * Encrypt to a public key. * The `authenticated` option is used to determine whether to use * the static root key pair. By default it will use generate an ephemeral key pair. * Neither ensures forward secrecy. However ephemeral key pair provides one-way * forward secrecy. * If it is important that the receiver can authenticate the sender, consider doing * `sign-then-encrypt`, by adding a signature into the plain text being sent. */ encrypt(receiverPublicKey: PublicKey, plainText: Buffer, authenticated?: boolean): Buffer; /** * Decrypt data sent to this key pair * Note that this does not automatically authenticate the sender. */ decrypt(cipherText: Buffer): Buffer | undefined; sign(data: Buffer): Signature; verify(publicKey: PublicKey, data: Buffer, signature: Signature): boolean; /** * Sets up the root key pair. * If the root key pair already exists: * - If password is supplied, the key pair is decrypted with the password. * The key pair is returned without the recovery code. * - If password and recovery code is supplied, then the key pair will be * recovered. * The recovery code is used to derive a key pair that is checked against * the existing key pair. * If the key pairs match, then the derived key pair is encrypted with * the password. * The key pair is returned without the recovery code. * - Private key and private key path is ignored, and this is handled the * same as if only the password was supplied. * If the root key pair does not exist: * - If password is supplied, then recovery code and key pair is generated. * The key pair is encrypted with the password. * The key pair and recovery code will be returned. * - If password and recovery code is supplied, then it will be used for key pair generation. * The key pair is encrypted with the password. * The key pair and recovery code will be returned. * - If password and private key is supplied, then key pair will be derived from the private key. * The key pair is encrypted with the password. * The key pair is returned without the recovery code. */ protected setupKeyPair(options: { password: string; } | { password: string; recoveryCode: RecoveryCode; } | { password: string; privateKey: PrivateKey; } | { password: string; privateKeyPath: string; }): Promise<[KeyPair, RecoveryCode | undefined]>; /** * Only the private key is necessary. * We can derive the public key from the private key. */ protected existsKeyPair(): Promise; protected existsPublicKey(): Promise; protected existsPrivateKey(): Promise; /** * Reads the key pair from the filesystem. * This only needs to read the private key as the public key is derived. * The private key is expected to be stored in a flattened JWE format. */ protected readKeyPair(password: string): Promise; /** * Reads the public key from the filesystem. * The public key is expected to be stored in a flattened JWE format. */ protected readPublicKey(publicKeyPath?: string): Promise; /** * Reads the private key from the filesystem. * The private key is expected to be stored in a flattened JWE format. */ protected readPrivateKey(password: string, privateKeyPath?: string): Promise; /** * Writes the root key pair to the filesystem. * The public key will be stored in JWK format. * The private key will be stored in flattened JWE format. * This first writes the public key and private key to `.tmp` files. * Then proceeds to atomically rename the files together. * The files should be updated together to ensure consistency. */ protected writeKeyPair(keyPair: KeyPair, password: string): Promise; /** * Generates the root key pair. * If recovery code is passed in, it is used as a deterministic seed. */ protected generateKeyPair(recoveryCode?: RecoveryCode): Promise; protected recoverKeyPair(recoveryCode: RecoveryCode): Promise; /** * Setup the DB key. * This is the data encryption key for the rest of PK. * This is what makes PK a hybrid cryptosystem. */ protected setupDbKey(keyPair: KeyPair): Promise; /** * Checks the existence of the DB key path. * This checks if the file can be read and written. * If the file does not exist, this returns `false`. * If the file does exist but it cannot be read or written, then * this will throw `ErrorDBKeyRead`. */ protected existsDbKey(): Promise; /** * Reads the DB key from the filesystem. * The DB key is expected to be stored in flattened JWE format. * The DB key is expected to be encrypted with our ECIES. */ protected readDbKey(keyPair: KeyPair, dbKeyPath?: string): Promise; /** * Writes the DB key from the filesystem. * The DB key will be stored in flattened JWE format. * The DB key will be encrypted with our ECIES. */ protected writeDbKey(dbKey: Key, publicKey: PublicKey): Promise; /** * Generates the DB key. * This is 256 bit key. */ protected generateDbKey(): Key; /** * This sets up a password hash in-memory. * This is used to check if the password is correct. * The returned buffers are guaranteed to unpooled and memory-locked. * This means the underlying `ArrayBuffer` is safely transferrable. */ protected setupPasswordHash(password: string): Promise<[PasswordHash, PasswordSalt]>; } export default KeyRing;