/// /// Crypto Isolate Manager /// Manages worker pool for encryption/decryption operations /// import 'dart:async'; import 'package:worker_manager/worker_manager.dart'; import 'ccc_iso_operation.dart'; import 'ccc_iso_result.dart'; import 'ccc_iso_operation_id.dart'; import 'ccc_iso.dart'; import 'cipher_constants.dart'; /// Manages isolate workers for cryptographic operations /// /// CCC (Copius Cipher Chain) Architecture: /// - Single encryption system for ALL data (messages, attachments, thumbnails) /// - Uses channel UUID for key derivation scope /// - Same key encrypts message + its attachments (bundled together) /// - Used for both over-the-wire AND local storage encryption /// /// FUTURE WORK: Ratchet System /// - Per-message key derivation using Double Ratchet algorithm /// - Keys derived from: channelUuid + messageSequence + rootKey /// - Forward secrecy: compromise of one key doesn't reveal past messages /// - Break-in recovery: new keys derived after compromise /// /// Current State: Full CCC encryption with isolate workers /// Future: Ratchet key derivation integration class CryptoIsolateManager { // Performance tracking final CryptoMetrics _metrics = CryptoMetrics(); // Configuration static const Duration OPERATION_TIMEOUT = Duration(seconds: 30); static const int WORKER_POOL_SIZE = 4; // Fixed pool of 4 workers bool _initialized = false; bool _disposed = false; /// Initialize the crypto isolate manager Future initialize() async { if (_initialized) return; // Initialize operation ID generator CryptoOperationId.initialize(); // Configure worker manager with fixed pool size await workerManager.init(isolatesCount: WORKER_POOL_SIZE); _initialized = true; } /// Encrypt data with specified cipher sequence /// In passthrough mode, returns data unchanged (for development) /// /// [channelUuid] - Channel UUID for key derivation scope /// [messageSequence] - Message sequence number for per-message key derivation (future: ratchet) /// [isUserMessage] - Priority flag for worker queue /// /// FUTURE: When ratchet is implemented, key = derive(channelRootKey, messageSequence) /// Same key will encrypt: MsgData + all attachment files + all thumbnails for that message Future encrypt( List plaintext, { required String channelUuid, int? messageSequence, // Future: used for ratchet key derivation List? cipherSequence, Map? params, bool isUserMessage = false, }) async { _ensureInitialized(); final operation = CryptoOperation.encrypt( plaintext: plaintext, cipherSequence: cipherSequence ?? CipherConstants.PHASE1_SEQUENCE, params: params ?? CipherConstants.DEFAULT_CIPHER_PARAMS, channelUuid: channelUuid, messageSequence: messageSequence, isUserMessage: isUserMessage, ); return await _executeOperation(operation); } /// Decrypt data with specified cipher sequence /// In passthrough mode, returns data unchanged (for development) /// /// [channelUuid] - Channel UUID for key derivation scope /// [messageSequence] - Message sequence number for per-message key derivation (future: ratchet) /// /// FUTURE: When ratchet is implemented, key = derive(channelRootKey, messageSequence) Future decrypt( List ciphertext, { required String channelUuid, int? messageSequence, // Future: used for ratchet key derivation List? cipherSequence, Map? params, }) async { _ensureInitialized(); final operation = CryptoOperation.decrypt( ciphertext: ciphertext, cipherSequence: cipherSequence ?? CipherConstants.PHASE1_SEQUENCE, params: params ?? CipherConstants.DEFAULT_CIPHER_PARAMS, channelUuid: channelUuid, messageSequence: messageSequence, ); return await _executeOperation(operation); } /// Execute a crypto operation Future _executeOperation(CryptoOperation operation) async { _ensureInitialized(); if (_disposed) { throw StateError('CryptoIsolateManager has been disposed'); } try { // Execute using the global workerManager with correct API final resultMap = await workerManager.execute>( () => cryptoWorkerFunction(operation.toMap()), priority: operation.priority, ).timeout(OPERATION_TIMEOUT); // Deserialize result final result = CryptoResult.fromMap(resultMap); // Update metrics _updateMetrics(operation, result); return result; } catch (error, stackTrace) { // Create error result final result = CryptoResult.error( operationId: operation.id, error: error.toString(), stackTrace: stackTrace.toString(), processingTime: operation.age, workerId: 'manager_error', ); // Update metrics _metrics.recordError(); return result; } } /// Update performance metrics void _updateMetrics(CryptoOperation operation, CryptoResult result) { if (result.success) { if (operation.type == OperationType.encrypt) { _metrics.recordEncryption(result.processingTime, operation.isHighPriority); } else { _metrics.recordDecryption(result.processingTime); } } else { _metrics.recordError(); } } /// Get current performance metrics CryptoMetrics get metrics => _metrics; /// Get worker manager stats String get workerStats { return 'Crypto Manager Stats: ${_metrics.toString()}'; } /// Check if manager is ready bool get isReady => _initialized && !_disposed; /// Ensure manager is initialized void _ensureInitialized() { if (!_initialized) { throw StateError('CryptoIsolateManager must be initialized before use'); } if (_disposed) { throw StateError('CryptoIsolateManager has been disposed'); } } /// Dispose the isolate manager and clean up resources Future dispose() async { if (_disposed) return; _disposed = true; // Dispose global worker manager try { await workerManager.dispose(); } catch (e) { // Ignore disposal errors } // Clear metrics _metrics.reset(); } /// Create a test operation (for debugging) Future testOperation({ String testData = 'Hello, Crypto World!', String channelUuid = 'test-channel-uuid', }) async { final plaintext = testData.codeUnits; // Encrypt final encryptResult = await encrypt( plaintext, channelUuid: channelUuid, isUserMessage: true, ); if (!encryptResult.success) { return encryptResult; } // Decrypt final decryptResult = await decrypt( encryptResult.data, channelUuid: channelUuid, ); if (!decryptResult.success) { return decryptResult; } // Verify roundtrip final decryptedText = String.fromCharCodes(decryptResult.data); if (decryptedText == testData) { return CryptoResult.success( operationId: 'test_roundtrip', data: decryptResult.data, processingTime: encryptResult.processingTime + decryptResult.processingTime, workerId: 'test_manager', ); } else { return CryptoResult.error( operationId: 'test_roundtrip', error: 'Roundtrip failed: expected "$testData", got "$decryptedText"', processingTime: encryptResult.processingTime + decryptResult.processingTime, workerId: 'test_manager', ); } } }