75 lines
2.8 KiB
Dart
75 lines
2.8 KiB
Dart
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;
|
||
}
|
||
} |