refactor: replace pointycastle with cryptography for AES-GCM

This commit is contained in:
Gringo
2026-03-01 09:02:50 +01:00
parent 34df71e518
commit 46280f603c
5 changed files with 50 additions and 41 deletions
+7
View File
@@ -0,0 +1,7 @@
- [ ] Translations
- [ ] Drop file
- [ ] Keep file name
- [ ] Use streams
- [ ] Better UI/UX
- [ ] Remove snackbars
- [x] Use https://pub.dev/packages/webcrypto or https://pub.dev/packages/cryptography for efficient web crypto operations
+27 -10
View File
@@ -1,10 +1,10 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:cryptography/cryptography.dart';
/// Decrypts data using AES-256-GCM
/// [encryptedBytes] - the encrypted data
/// [encryptedBytes] - the encrypted data (ciphertext + MAC)
/// [key] - base64 encoded encryption key
/// [nonce] - base64 encoded nonce (salt)
Future<Uint8List> decryptBlob({
@@ -12,20 +12,37 @@ Future<Uint8List> decryptBlob({
required String key,
required String nonce,
}) async {
final algorithm = AesGcm.with256bits();
// Decode base64 key and nonce
final keyBytes = base64Decode(key);
final nonceBytes = base64Decode(nonce);
// Set up AES-GCM parameters
final keyParam = KeyParameter(keyBytes);
final params = ParametersWithIV(keyParam, nonceBytes);
// Extract ciphertext and MAC (MAC is last 16 bytes)
final macLength = algorithm.macAlgorithm.macLength;
final ciphertext = encryptedBytes.sublist(
0,
encryptedBytes.length - macLength,
);
final macBytes = encryptedBytes.sublist(
encryptedBytes.length - macLength,
);
// Create and initialize the cipher for decryption (false = decrypt)
final aesGcm = GCMBlockCipher(AESEngine());
aesGcm.init(false, params);
// Create secret key from bytes
final secretKey = SecretKey(keyBytes);
// Create secret box with ciphertext, nonce, and MAC
final secretBox = SecretBox(
ciphertext,
nonce: nonceBytes,
mac: Mac(macBytes),
);
// Decrypt the data
final decryptedData = aesGcm.process(encryptedBytes);
final decryptedData = await algorithm.decrypt(
secretBox,
secretKey: secretKey,
);
return decryptedData;
return Uint8List.fromList(decryptedData);
}
+13 -28
View File
@@ -1,44 +1,29 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:cryptography/cryptography.dart';
import 'package:file_transfer/models/encrypted_blob.dart';
import 'package:pointycastle/export.dart';
/// Encrypts data using AES-256-GCM
/// Returns the encrypted blob with key, nonce, and auth tag
Future<EncryptedBlob> encryptBlob(Uint8List bytes) async {
// Generate random 256-bit key (32 bytes)
final key = _generateRandomBytes(32);
final algorithm = AesGcm.with256bits();
// Generate random 12-byte nonce (96 bits - recommended for GCM)
final nonce = _generateRandomBytes(12);
// Generate random secret key
final secretKey = await algorithm.newSecretKey();
// Set up AES-GCM parameters
final keyParam = KeyParameter(key);
final params = ParametersWithIV(keyParam, nonce);
// Encrypt with the key
final secretBox = await algorithm.encrypt(bytes, secretKey: secretKey);
// Create and initialize the cipher for encryption (true = encrypt)
final aesGcm = GCMBlockCipher(AESEngine());
aesGcm.init(true, params);
// Encrypt the data
final encryptedData = aesGcm.process(bytes);
// Concatenate ciphertext + MAC for storage
final macBytes = secretBox.mac.bytes;
final encryptedData = Uint8List.fromList(
[...secretBox.cipherText, ...macBytes],
);
return EncryptedBlob(
bytes: encryptedData,
key: base64Encode(key),
nonce: base64Encode(nonce),
key: base64Encode(await secretKey.extractBytes()),
nonce: base64Encode(secretBox.nonce),
);
}
Uint8List _generateRandomBytes(int length) {
final secureRandom = SecureRandom('Fortuna');
// Seed with platform entropy
final seed = Uint8List(32);
for (var i = 0; i < 32; i++) {
seed[i] = (DateTime.now().microsecondsSinceEpoch >> (i % 32)) & 0xFF;
}
secureRandom.seed(KeyParameter(seed));
return secureRandom.nextBytes(length);
}
+2 -2
View File
@@ -106,7 +106,7 @@ packages:
source: hosted
version: "3.0.7"
cryptography:
dependency: transitive
dependency: "direct main"
description:
name: cryptography
sha256: "3eda3029d34ec9095a27a198ac9785630fe525c0eb6a49f3d575272f8e792ef0"
@@ -558,7 +558,7 @@ packages:
source: hosted
version: "2.1.8"
pointycastle:
dependency: "direct main"
dependency: transitive
description:
name: pointycastle
sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5"
+1 -1
View File
@@ -8,6 +8,7 @@ environment:
dependencies:
crypto: ^3.0.7
cryptography: ^2.9.0
file_picker: ^10.3.10
file_saver: ^0.3.1
flutter:
@@ -17,7 +18,6 @@ dependencies:
ndk: ^0.7.1-dev.19
ndk_flutter: ^0.0.2-dev.13
nip49: ^1.1.2
pointycastle: ^4.0.0
dev_dependencies:
flutter_test: