import 'dart:convert'; import 'package:encrypt/encrypt.dart'; import 'package:on_field_work/helpers/services/app_logger.dart'; // <-- for logging // 🔑 CONSTANTS // Base64-encoded 32-byte key (256 bits for AES-256) const String _keyBase64 = "u4J7p9Qx2hF5vYtLz8Kq3mN1sG0bRwXyZcD6eH8jFQw="; // IV must be 16 bytes for AES-CBC mode const int _ivLength = 16; /// Decrypts a Base64-encoded string that contains the IV prepended to the ciphertext. /// Returns the decoded JSON object, the plain decrypted string, or null on failure. dynamic decryptResponse(String encryptedBase64Str) { try { // 1️⃣ Initialize Key final rawKeyBytes = base64.decode(_keyBase64); if (rawKeyBytes.length != 32) { logSafe("ERROR: Decoded key length is ${rawKeyBytes.length}. Expected 32 bytes for AES-256.", level: LogLevel.error); throw Exception("Invalid key length."); } final key = Key(rawKeyBytes); // 2️⃣ Decode incoming encrypted payload (IV + Ciphertext) final fullBytes = base64.decode(encryptedBase64Str); if (fullBytes.length < _ivLength + 16) { // Minimum length check (16 bytes IV + 1 block of ciphertext, which is 16 bytes) throw Exception("Encrypted string too short or corrupted."); } // 3️⃣ Extract IV & Ciphertext // Assumes the first 16 bytes are the IV final iv = IV(fullBytes.sublist(0, _ivLength)); final cipherTextBytes = fullBytes.sublist(_ivLength); // 4️⃣ Configure Encrypter with specific parameters // AES-256 with CBC mode and standard PKCS7 padding final encrypter = Encrypter( AES( key, mode: AESMode.cbc, padding: 'PKCS7' ) ); final encrypted = Encrypted(cipherTextBytes); // 5️⃣ Decrypt - This is where the "Invalid or corrupted pad block" error occurs final decryptedBytes = encrypter.decryptBytes(encrypted, iv: iv); final decryptedString = utf8.decode(decryptedBytes); if (decryptedString.isEmpty) { throw Exception("Decryption produced empty string (check if padding was correct)."); } // 🔹 Log decrypted snippet for verification final snippetLength = decryptedString.length > 50 ? 50 : decryptedString.length; logSafe( "Decryption successful. Snippet: ${decryptedString.substring(0, snippetLength)}...", level: LogLevel.info, ); // 6️⃣ Try parsing JSON try { return jsonDecode(decryptedString); } catch (_) { // return plain string if it's not JSON logSafe("Decrypted data is not JSON. Returning plain string.", level: LogLevel.warning); return decryptedString; } } catch (e, st) { // Catch the specific decryption error (e.g., 'Invalid or corrupted pad block') logSafe("FATAL Decryption failed: $e", level: LogLevel.error, stackTrace: st); return null; } }