// Helper untuk generate random key 32 digit static String _generateRandomKey() { final random = math.Random.secure(); final digits = List.generate(32, (_) => random.nextInt(10)); return digits.join(); } // Helper untuk minify JSON static String _minifyJson(Map json) { return jsonEncode(json); } // Helper untuk convert string key menjadi bytes static Uint8List _generateKeyBytes(String keyString, {required int length}) { // ignore: prefer_final_locals var bytes = utf8.encode(keyString); if (bytes.length < length) { final padded = Uint8List(length); for (var i = 0; i < bytes.length; i++) { padded[i] = bytes[i]; } for (var i = bytes.length; i < length; i++) { padded[i] = 0; } return padded; } else if (bytes.length > length) { return Uint8List.fromList(bytes.sublist(0, length)); } else { return Uint8List.fromList(bytes); } } // Helper untuk encrypt dengan AES256-CBC dan output HEX static String _aes256EncryptToHex( String plainText, String keyString, String ivString) { try { final keyBytes = _generateKeyBytes(keyString, length: 32); final ivBytes = _generateKeyBytes(ivString, length: 16); final key = encrypt.Key(keyBytes); final iv = encrypt.IV(ivBytes); final encrypter = encrypt.Encrypter( encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: 'PKCS7')); final encrypted = encrypter.encrypt(plainText, iv: iv); return _bytesToHex(encrypted.bytes); } catch (e) { debugPrint('Encryption to hex error: $e'); throw Exception('Encryption to hex failed: $e'); } } // Helper untuk encrypt dengan AES256-CBC dan output Base64 static String _aes256EncryptToBase64( String plainText, String keyString, String ivString) { try { final keyBytes = _generateKeyBytes(keyString, length: 32); final ivBytes = _generateKeyBytes(ivString, length: 16); final key = encrypt.Key(keyBytes); final iv = encrypt.IV(ivBytes); final encrypter = encrypt.Encrypter( encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: 'PKCS7')); final encrypted = encrypter.encrypt(plainText, iv: iv); return encrypted.base64; } catch (e) { debugPrint('Encryption to base64 error: $e'); throw Exception('Encryption to base64 failed: $e'); } } // Helper untuk decrypt dari Base64 - UPDATED VERSION static String _aes256DecryptFromBase64( String cipherTextBase64, String keyString, String ivString) { try { // Decode base64 final cipherBytes = base64.decode(cipherTextBase64); // Generate key dan iv final keyBytes = _generateKeyBytes(keyString, length: 32); final ivBytes = _generateKeyBytes(ivString, length: 16); final key = encrypt.Key(keyBytes); final iv = encrypt.IV(ivBytes); final encrypter = encrypt.Encrypter( encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: 'PKCS7')); final decrypted = encrypter.decryptBytes(encrypt.Encrypted(cipherBytes), iv: iv); // Backend mengembalikan hex string dalam bytes // Jadi kita perlu decode bytes ke string final resultString = utf8.decode(decrypted); // Hasilnya adalah hex string (32 karakter) return resultString; } catch (e) { debugPrint('_aes256DecryptFromBase64 error: $e'); throw Exception('Decryption from base64 failed: $e'); } } // Helper untuk decrypt dari Hex static String _aes256DecryptFromHex( String cipherTextHex, String keyString, String ivString) { try { final cipherBytes = _hexToBytes(cipherTextHex); final keyBytes = _generateKeyBytes(keyString, length: 32); final ivBytes = _generateKeyBytes(ivString, length: 16); final key = encrypt.Key(keyBytes); final iv = encrypt.IV(ivBytes); final encrypter = encrypt.Encrypter( encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: 'PKCS7')); final decrypted = encrypter.decryptBytes(encrypt.Encrypted(cipherBytes), iv: iv); return utf8.decode(decrypted); } catch (e) { debugPrint('Decryption from hex error: $e'); throw Exception('Decryption from hex failed: $e'); } } // Helper untuk convert bytes ke hex static String _bytesToHex(Uint8List bytes) { return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(); } // Helper untuk convert hex ke bytes static Uint8List _hexToBytes(String hexString) { final result = Uint8List(hexString.length ~/ 2); for (var i = 0; i < hexString.length; i += 2) { final byte = int.parse(hexString.substring(i, i + 2), radix: 16); result[i ~/ 2] = byte; } return result; } // Validasi hex string static bool isHexString(String str) { return RegExp(r'^[0-9a-fA-F]+$').hasMatch(str); } // ============ MAIN ENCRYPTION FLOW ============ /// Encrypt request body static Map encryptSecureRequest( Map requestBody) { try { debugPrint('=== ENCRYPTION START ==='); // 1. Generate random key 32 digit final randomKey = _generateRandomKey(); debugPrint('1. Random key (32 digit): $randomKey'); // 2. Generate unique identifier final uniqueIdentifier = DateTime.now().microsecondsSinceEpoch.toString(); // 3. Generate timestamp final timestamp = DateTime.now().toUtc().toIso8601String(); debugPrint('3. Timestamp: $timestamp'); // 4. Minify body final minifiedBody = _minifyJson(requestBody); debugPrint('4. Minified body length: ${minifiedBody.length}'); // 5. Encrypt body dengan random key (output HEX) final secureTextSaltEncryptor = dotenv.env['SECURE_TEXT_SALT_ENCRYPTOR']!; final cipherBodyHex = _aes256EncryptToHex( minifiedBody, randomKey, secureTextSaltEncryptor, ); debugPrint('5. Cipher body (HEX) length: ${cipherBodyHex.length}'); // 6. Encrypt random key (output Base64) // Format: RandomKey||UniqueIdentifier||Timestamp final plainKeyText = '$randomKey||$uniqueIdentifier||$timestamp'; debugPrint('6. Plain key text length: ${plainKeyText.length}'); final randKeyEncryptorKey = dotenv.env['RAND_KEY_ENCRYPTOR_KEY']!; final randKeyEncryptorSalt = dotenv.env['RAND_KEY_ENCRYPTOR_SALT']!; final cipherRandKeyBase64 = _aes256EncryptToBase64( plainKeyText, randKeyEncryptorKey, randKeyEncryptorSalt, ); debugPrint( '7. Cipher rand key (Base64) length: ${cipherRandKeyBase64.length}'); debugPrint('=== ENCRYPTION COMPLETE ==='); return { 'secureText': cipherBodyHex, 'encryptedKey': cipherRandKeyBase64, 'timestamp': timestamp, }; } catch (e) { debugPrint('Error in encryptSecureRequest: $e'); rethrow; } } /// Decrypt response - TRY APPROACH 1 static Map decryptSecureResponseApproach1( String secureResponseHex, String encryptedTokenBase64) { try { debugPrint('=== DECRYPTION APPROACH 1 ==='); // 1. Decrypt token final randKeyDecryptorKey = dotenv.env['RAND_KEY_DECRYPTOR_KEY']!; final randKeyDecryptorSalt = dotenv.env['RAND_KEY_DECRYPTOR_SALT']!; final decryptedTokenHex = _aes256DecryptFromBase64( encryptedTokenBase64, randKeyDecryptorKey, randKeyDecryptorSalt, ); debugPrint('1. Decrypted token (hex): $decryptedTokenHex'); debugPrint(' Length: ${decryptedTokenHex.length}'); // 2. Gunakan token hex langsung sebagai key string (bukan convert ke bytes) debugPrint('2. Using hex token directly as key string'); final secureTextSaltDecryptor = dotenv.env['SECURE_TEXT_SALT_DECRYPTOR']!; final decryptedResponse = _aes256DecryptFromHex( secureResponseHex, decryptedTokenHex, // Gunakan hex string langsung secureTextSaltDecryptor, ); debugPrint('3. Decrypted: $decryptedResponse'); return jsonDecode(decryptedResponse); } catch (e) { debugPrint('Approach 1 failed: $e'); rethrow; } }