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,
  },
);