@commit451/salamander
Version: 
Never be AFK
123 lines • 4.6 kB
JavaScript
import { CryptoService } from './crypto.js';
import { StorageService } from '../utils/storage.js';
export class KeyManagerService {
    static KEYS_STORAGE_KEY = 'salamander_runner_keys';
    static KEY_EXPIRY_DAYS = 30;
    /**
     * Initialize keys for a new runner
     */
    static async initializeRunnerKeys(runnerId) {
        const ecdhKeyPair = CryptoService.generateECDHKeyPair();
        const runnerKeys = {
            ecdhKeyPair,
            createdAt: Date.now()
        };
        await this.storeRunnerKeys(runnerId, runnerKeys);
        return ecdhKeyPair;
    }
    /**
     * Complete the key exchange with the Flutter app's public key
     */
    static async completeKeyExchange(runnerId, flutterPublicKey) {
        const runnerKeys = await this.getRunnerKeys(runnerId);
        if (!runnerKeys) {
            throw new Error(`No keys found for runner ${runnerId}`);
        }
        // Derive shared secret using our private key and Flutter's public key
        const sharedSecret = CryptoService.deriveSharedSecret(runnerKeys.ecdhKeyPair.privateKey, flutterPublicKey);
        // Update stored keys with shared secret
        runnerKeys.sharedSecret = sharedSecret;
        runnerKeys.keyHash = CryptoService.createKeyHash(sharedSecret);
        await this.storeRunnerKeys(runnerId, runnerKeys);
        return sharedSecret;
    }
    /**
     * Get the shared secret for encryption/decryption
     */
    static async getSharedSecret(runnerId) {
        const runnerKeys = await this.getRunnerKeys(runnerId);
        if (!runnerKeys?.sharedSecret) {
            return null;
        }
        // Check if keys are expired
        const daysSinceCreation = (Date.now() - runnerKeys.createdAt) / (1000 * 60 * 60 * 24);
        if (daysSinceCreation > this.KEY_EXPIRY_DAYS) {
            console.warn(`Keys for runner ${runnerId} have expired. Consider key rotation.`);
        }
        return runnerKeys.sharedSecret;
    }
    /**
     * Get the CLI's public key for a runner
     */
    static async getPublicKey(runnerId) {
        const runnerKeys = await this.getRunnerKeys(runnerId);
        return runnerKeys?.ecdhKeyPair.publicKey ?? null;
    }
    /**
     * Check if keys exist and are properly initialized for a runner
     */
    static async hasValidKeys(runnerId) {
        const runnerKeys = await this.getRunnerKeys(runnerId);
        return !!(runnerKeys?.ecdhKeyPair && runnerKeys?.sharedSecret);
    }
    /**
     * Rotate keys for a runner (generate new key pair)
     */
    static async rotateKeys(runnerId) {
        console.log(`Rotating keys for runner ${runnerId}`);
        return await this.initializeRunnerKeys(runnerId);
    }
    /**
     * Remove keys for a runner (when runner is deleted)
     */
    static async removeRunnerKeys(runnerId) {
        const allKeys = await this.getAllStoredKeys();
        delete allKeys[runnerId];
        await StorageService.set(this.KEYS_STORAGE_KEY, allKeys);
    }
    /**
     * Get all runners that have encryption keys
     */
    static async getEncryptedRunnerIds() {
        const allKeys = await this.getAllStoredKeys();
        return Object.keys(allKeys).filter(runnerId => {
            const keys = allKeys[runnerId];
            return keys?.sharedSecret;
        });
    }
    /**
     * Cleanup expired keys
     */
    static async cleanupExpiredKeys() {
        const allKeys = await this.getAllStoredKeys();
        const now = Date.now();
        let cleanedCount = 0;
        const updatedKeys = {};
        for (const [runnerId, keys] of Object.entries(allKeys)) {
            const daysSinceCreation = (now - keys.createdAt) / (1000 * 60 * 60 * 24);
            if (daysSinceCreation <= this.KEY_EXPIRY_DAYS) {
                updatedKeys[runnerId] = keys;
            }
            else {
                cleanedCount++;
                console.log(`Cleaned up expired keys for runner ${runnerId}`);
            }
        }
        await StorageService.set(this.KEYS_STORAGE_KEY, updatedKeys);
        return cleanedCount;
    }
    // Private helper methods
    static async getRunnerKeys(runnerId) {
        const allKeys = await this.getAllStoredKeys();
        return allKeys[runnerId] ?? null;
    }
    static async storeRunnerKeys(runnerId, keys) {
        const allKeys = await this.getAllStoredKeys();
        allKeys[runnerId] = keys;
        await StorageService.set(this.KEYS_STORAGE_KEY, allKeys);
    }
    static async getAllStoredKeys() {
        return (await StorageService.get(this.KEYS_STORAGE_KEY)) ?? {};
    }
}
//# sourceMappingURL=key-manager.js.map