import { DeviceIdentity, TokenData, EncryptedSessionKey } from '../types'; /** * Error class for keychain operations */ export declare class KeychainError extends Error { constructor(message: string); } /** * Manages device identity and OAuth tokens using native keychain */ export declare class KeychainManager { private static instance; private deviceIdentity; private sessionKeyCache; private isRegistered; private _serviceName; private constructor(); /** * Get the keychain service name (lazy-loaded from config) */ private get serviceName(); static getInstance(): KeychainManager; /** * Get the device identity from keychain. * * Returns null ONLY when the backend read succeeded and no record exists * (legitimate "not yet registered" state). Any other error — backend * unavailable, corrupted payload, parse failure — propagates to the * caller. Swallowing those errors here would let getOrCreateDeviceIdentity * silently mint a new device ID and fork identity state, re-creating the * exact bug the keychain-backend trust model is designed to prevent. */ getDeviceIdentity(): Promise; /** * Set the device identity in keychain */ setDeviceIdentity(identity: DeviceIdentity): Promise; /** * Get or create device identity */ getOrCreateDeviceIdentity(): Promise; /** * Get device ID (creates identity if needed) */ getDeviceId(): Promise; /** * Get device public key (creates identity if needed) */ getDevicePublicKey(): Promise; /** * Get device private key (creates identity if needed) */ getDevicePrivateKey(): Promise; /** * Check if device identity exists */ hasDeviceIdentity(): Promise; /** * Delete device identity (reset device) */ deleteDeviceIdentity(): Promise; /** * Get token account name for environment */ private getTokenAccount; /** * Get OAuth tokens for environment. * * Returns null ONLY when the backend read succeeded and no tokens exist * (legitimate "not yet logged in" state). Any other error — backend * unavailable, corrupted payload, parse failure — propagates to the * caller. Swallowing backend errors here would look like a silent logout * when it is actually a hard configuration failure the user needs to see * and fix. */ getTokens(environment?: string): Promise; /** * Save OAuth tokens for environment */ setTokens(tokens: TokenData, environment?: string): Promise; /** * Delete OAuth tokens for environment */ deleteTokens(environment?: string): Promise; /** * Check if token is expired */ isTokenExpired(tokens: TokenData): boolean; /** * Get session key for a session, decrypting from encryptedKeys if needed */ getSessionKey(sessionId: string, encryptedKeys?: EncryptedSessionKey[]): Promise; /** * Generate and encrypt a new session key for all devices. * * Per-device try/catch (task #331 Bug B): one bad publicKey (corrupted * base64, wrong curve, ECDH derive failure) does NOT abort the whole * call — that device is skipped and reported in `skippedDeviceIds`. The * caller decides what to do (telemetry, partial-success UX). Throw only * if NO device succeeded, preserving the existing "no encryption" * fallback at `prepareSessionEncryption`. * * Optional `onDeviceSkipped` callback fires per-skip for telemetry — * intentionally a callback rather than a direct beacon import so this * module stays free of analytics dependencies. */ createSessionKey(devicePublicKeys: Array<{ deviceId: string; publicKey: string; }>, options?: { onDeviceSkipped?: (totalSkippedSoFar: number) => void; }): { sessionKey: string; encryptedKeys: EncryptedSessionKey[]; skippedDeviceIds: string[]; }; /** * Cache a session key */ cacheSessionKey(sessionId: string, sessionKey: string): void; /** * Get a cached session key without touching the backend or attempting * to decrypt from encryptedKeys. Returns null if the session key is not * currently cached in memory. * * Used by the session re-key flow: when the plugin observes a new device * registration via the onDeviceKeyRegistered subscription, it needs the * plaintext session key of each active session to wrap it for the new * device. Those keys live in this cache for the lifetime of the session. */ getCachedSessionKey(sessionId: string): string | null; /** * List session IDs that currently have a cached session key in this * process's memory. Used by the re-key flow to iterate over all sessions * the plugin is actively managing. */ getCachedSessionIds(): string[]; /** * Clear cached session key */ clearSessionKey(sessionId: string): void; /** * Clear all cached session keys */ clearAllSessionKeys(): void; /** * Get registration status */ getIsRegistered(): boolean; /** * Set registration status */ setIsRegistered(registered: boolean): void; /** * Get device name for registration */ getDeviceName(): string; /** * Get platform for registration */ getDevicePlatform(): string; /** * Clear all data (device identity + all tokens) */ clearAllData(): Promise; } /** * Export singleton instance */ export declare const keychainManager: KeychainManager;