import 'dart:convert'; import 'dart:math'; import 'package:cryptography/cryptography.dart'; /// Encriptación de contenido de notas usando AES-GCM /// Usa una clave derivada del master key del usuario class NoteEncryption { static final AesGcm _aes = AesGcm.with256bits(); static const int _passwordHashVersion = 1; static const int _kdfIterations = 100000; static final Pbkdf2 _kdf = Pbkdf2( macAlgorithm: Hmac.sha256(), iterations: _kdfIterations, bits: 256, ); /// Encripta el contenido de una nota usando el master key /// Retorna el contenido encriptado en formato JSON base64 static Future encryptNote( String plaintext, String masterKey, ) async { final List salt = _randomBytes(16); final SecretKey secretKey = await _kdf.deriveKey( secretKey: SecretKey(utf8.encode(masterKey)), nonce: salt, ); final List nonce = _randomBytes(12); final SecretBox box = await _aes.encrypt( utf8.encode(plaintext), secretKey: secretKey, nonce: nonce, ); return jsonEncode({ 'v': _passwordHashVersion, 'salt': base64Encode(salt), 'nonce': base64Encode(box.nonce), 'cipherText': base64Encode(box.cipherText), 'mac': base64Encode(box.mac.bytes), }); } /// Desencripta el contenido de una nota usando el master key static Future decryptNote( String encodedBox, String masterKey, ) async { try { final Map payload = jsonDecode(encodedBox) as Map; final List salt = base64Decode(payload['salt'] as String); final List nonce = base64Decode(payload['nonce'] as String); final List cipherText = base64Decode(payload['cipherText'] as String); final List macBytes = base64Decode(payload['mac'] as String); final SecretKey secretKey = await _kdf.deriveKey( secretKey: SecretKey(utf8.encode(masterKey)), nonce: salt, ); final SecretBox box = SecretBox( cipherText, nonce: nonce, mac: Mac(macBytes), ); final List clearText = await _aes.decrypt( box, secretKey: secretKey, ); return utf8.decode(clearText); } catch (e) { throw Exception('Failed to decrypt note: $e'); } } static List _randomBytes(int length) { final Random random = Random.secure(); return List.generate(length, (_) => random.nextInt(256)); } }