Chepuhagram/lib/domain/services/crypto_service.dart

188 lines
5.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:typed_data';
import 'package:cryptography/cryptography.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'dart:convert';
class CryptoService {
final _storage = const FlutterSecureStorage();
final algorithm = X25519();
final aesGcm = AesGcm.with256bits();
Future<Map<String, String>> initAccountSecurity(String masterPassword) async {
// Генерируем пару X25519 ключей
final keyPair = await algorithm.newKeyPair();
final publicKey = await keyPair.extractPublicKey();
final privateKeyBytes = await keyPair.extractPrivateKeyBytes();
// Сохраняем приватный ключ в Secure Storage
await _storage.write(
key: 'private_key',
value: base64Encode(privateKeyBytes),
);
// Шифруем приватный ключ с мастер-паролем (AES-GCM)
final masterKey = await _deriveKeyFromPassword(masterPassword);
final nonce = aesGcm.newNonce();
final encrypted = await aesGcm.encrypt(
privateKeyBytes,
secretKey: masterKey,
nonce: nonce,
);
// Комбинируем nonce и зашифрованные данные
final encryptedData = nonce + encrypted.mac.bytes + encrypted.cipherText;
final encryptedPrivateKey = base64Encode(encryptedData);
final publicKeyBase64 = base64Encode(publicKey.bytes);
return {
'public_key': publicKeyBase64,
'encrypted_private_key': encryptedPrivateKey,
};
}
Future<String> decryptPrivateKey(
String encryptedPrivateKey,
String masterPassword,
) async {
try {
final encryptedData = base64Decode(encryptedPrivateKey);
// Разделяем nonce и зашифрованные данные
final nonce = encryptedData.sublist(0, 12); // GCM nonce = 12 bytes
final macBytes = encryptedData.sublist(12, 28);
final cipherText = encryptedData.sublist(28);
final masterKey = await _deriveKeyFromPassword(masterPassword);
final decrypted = await aesGcm.decrypt(
SecretBox(cipherText, nonce: nonce, mac: Mac(macBytes)),
secretKey: masterKey,
);
return base64Encode(decrypted);
} catch (e) {
throw Exception('Неверный мастер-пароль или поврежденные данные');
}
}
Future<String> encryptPrivateKeyWithPassword(
String privateKeyBase64,
String masterPassword,
) async {
final privateKeyBytes = base64Decode(privateKeyBase64);
final masterKey = await _deriveKeyFromPassword(masterPassword);
final nonce = aesGcm.newNonce();
final encrypted = await aesGcm.encrypt(
privateKeyBytes,
secretKey: masterKey,
nonce: nonce,
);
final encryptedData = nonce + encrypted.mac.bytes + encrypted.cipherText;
return base64Encode(encryptedData);
}
Future<SecretKey> _deriveKeyFromPassword(String password) async {
final pbkdf2 = Pbkdf2(
macAlgorithm: Hmac.sha256(),
iterations: 10000,
bits: 256,
);
final salt = utf8.encode('chepuhagram_salt');
return await pbkdf2.deriveKeyFromPassword(password: password, nonce: salt);
}
Future<SecretKey> deriveSharedSecret(
String myPrivateKeyBase64,
String theirPublicKeyBase64,
) async {
final myKeyPair = await algorithm.newKeyPairFromSeed(
base64Decode(myPrivateKeyBase64),
);
final theirPublicKey = SimplePublicKey(
base64Decode(theirPublicKeyBase64),
type: KeyPairType.x25519,
);
return await algorithm.sharedSecretKey(
keyPair: myKeyPair,
remotePublicKey: theirPublicKey,
);
}
Future<String> encryptMessage(String text, SecretKey sharedKey) async {
final nonce = aesGcm.newNonce();
final encrypted = await aesGcm.encrypt(
utf8.encode(text),
secretKey: sharedKey,
nonce: nonce,
);
// Сохраняем Nonce + MAC + CipherText для передачи
return base64Encode(nonce + encrypted.mac.bytes + encrypted.cipherText);
}
Future<(List<int>, String)?> encryptImage(
List<int> fileBytes,
SecretKey sharedKey,
) async {
try {
final SecretKey fileSecretKey = await aesGcm.newSecretKey();
final List<int> fileSecretKeyBytes = await fileSecretKey.extractBytes();
final SecretBox secretBox = await aesGcm.encrypt(
fileBytes,
secretKey: fileSecretKey,
);
final List<int> dataToUpload = secretBox.concatenation();
final encryptedKeyBox = await aesGcm.encrypt(
fileSecretKeyBytes,
secretKey: sharedKey,
);
final String encryptedKeyForServer = base64Encode(
encryptedKeyBox.concatenation(),
);
return (dataToUpload, encryptedKeyForServer);
} catch (e) {
print("Ошибка шифрования медиа: $e");
return null;
}
}
Future<String> decryptMessage(String base64Data, SecretKey sharedKey) async {
final data = base64Decode(base64Data);
final nonce = data.sublist(0, 12);
final mac = data.sublist(12, 28);
final cipherText = data.sublist(28);
final decrypted = await aesGcm.decrypt(
SecretBox(cipherText, nonce: nonce, mac: Mac(mac)),
secretKey: sharedKey,
);
return utf8.decode(decrypted);
}
Future<String?> getPrivateKey() async {
return await _storage.read(key: 'private_key');
}
Future<bool> hasPrivateKey() async {
final key = await _storage.read(key: 'private_key');
return key != null;
}
Future<void> savePrivateKey(String privateKey) async {
await _storage.write(key: 'private_key', value: privateKey);
}
Future<void> deletePrivateKey() async {
await _storage.delete(key: 'private_key');
}
}