Flutter Client / Cryptography
Криптография во Flutter
Реализация сквозного шифрования, обмена ключами и оптимизация дешифрования. Файл: lib/domain/services/crypto_service.dart
Используемые криптоалгоритмы
Для выполнения криптографических вычислений используется библиотека cryptography на чистом Dart. Алгоритмы:
| Компонент | Алгоритм | Параметры / Описание |
|---|---|---|
| Обмен ключами (E2EE) | X25519 (Diffie-Hellman) | Вычисление 256-битного общего секрета на базе эллиптической кривой Curve25519. |
| Шифрование сообщений | AES-256-GCM | Использует случайный вектор инициализации (nonce) 12 байт, возвращает шифртекст + 16-байтную метку аутентификации (MAC). |
| Ключ мастер-пароля (KDF) | PBKDF2 (HMAC-SHA256) | 600 000 итераций, соль "chepuhagram_salt". Преобразует текстовый мастер-пароль в 256-битный ключ AES. |
| Локальное хранение | AES-GCM / Secure Storage | Приватный ключ X25519 в незашифрованном виде сохраняется в Android Keystore / iOS Keychain через FlutterSecureStorage. |
Код генерации аккаунта E2EE
dartinitAccountSecurity
Future<Map<String, String>> initAccountSecurity(String masterPassword) async {
// 1. Генерируем пару X25519 ключей
final keyPair = await algorithm.newKeyPair();
final publicKey = await keyPair.extractPublicKey();
final privateKeyBytes = await keyPair.extractPrivateKeyBytes();
// 2. Сохраняем приватный ключ локально в Secure Storage
await _storage.write(key: 'private_key', value: base64Encode(privateKeyBytes));
// 3. Выводим мастер-ключ из пароля через PBKDF2 (600 000 итераций)
final masterKey = await _deriveKeyFromPassword(masterPassword);
// 4. Шифруем приватный ключ мастер-ключом с помощью AES-GCM
final nonce = aesGcm.newNonce();
final encrypted = await aesGcm.encrypt(privateKeyBytes, secretKey: masterKey, nonce: nonce);
// 5. Собираем байты: nonce (12) + MAC (16) + cipher
final encryptedData = nonce + encrypted.mac.bytes + encrypted.cipherText;
return {
'public_key': base64Encode(publicKey.bytes),
'encrypted_private_key': base64Encode(encryptedData),
};
}
Оптимизация производительности: Isolates
Дешифрование большого объема сообщений в списке чатов при запуске приложения может приводить к лагам интерфейса (пропуску кадров), так как Dart работает в одном потоке.
Для решения этой проблемы метод bulkDecryptContacts() вынесен в фоновый Dart-поток (Isolate) с помощью функции compute(). Тяжелые математические вычисления по расчету общих секретов X25519 и расшифровке AES-GCM выполняются на фоне, не блокируя UI поток.
dartВызов фонового изолята
// Запуск дешифровки контактов в фоновом изоляте
final decryptedContacts = await compute(
CryptoService.bulkDecryptContacts,
{
'contacts': rawContacts,
'privKey': myPrivateKey,
'cache': _memoryKeysCache,
},
);