155 lines
4.2 KiB
Dart
155 lines
4.2 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:libac_dart/utils/Hashing.dart';
|
|
|
|
class XTEA {
|
|
static const int XTEA_DELTA = 0x9E3779B9; // (sqrt(5) - 1) * 2^31
|
|
static const int xteaNumRounds = 6;
|
|
List<int> _xteaKey = [0, 0, 0, 0];
|
|
|
|
/// Used as part of testsuite to compare provided keys.
|
|
///
|
|
/// This function will sequentially check each entry.
|
|
///
|
|
/// Returns: -1 on success, or which entry failed verification.
|
|
///
|
|
/// On failure it will print to the console what failed
|
|
int testKey(List<int> key) {
|
|
for (int i = 0; i < key.length; i++) {
|
|
int v0 = _xteaKey[i];
|
|
int v1 = key[i];
|
|
|
|
if (v0 != v1) {
|
|
print(
|
|
"FATAL: TestKey XTEA failed at position ${i}.\nExpected: ${v1}\nActual: ${v0}");
|
|
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
XTEA(String password) {
|
|
_xteaKey = _xteaKeyFromString(password);
|
|
}
|
|
|
|
List<int> _xteaKeyFromString(String str) {
|
|
// Convert string to MD5 and split into 4 integers
|
|
str = md5(str); // Assume md5 function exists
|
|
return [
|
|
int.parse(str.substring(0, 8), radix: 16),
|
|
int.parse(str.substring(8, 16), radix: 16),
|
|
int.parse(str.substring(16, 24), radix: 16),
|
|
int.parse(str.substring(24, 32), radix: 16)
|
|
];
|
|
}
|
|
|
|
List<int> encipher(List<int> data) {
|
|
int v0 = data[0];
|
|
int v1 = data[1];
|
|
int sum = 0;
|
|
|
|
for (int i = 0; i < xteaNumRounds; i++) {
|
|
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + _xteaKey[sum & 3]);
|
|
sum += XTEA_DELTA;
|
|
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + _xteaKey[(sum >> 11) & 3]);
|
|
}
|
|
|
|
return [v0 & 0xFFFFFFFF, v1 & 0xFFFFFFFF]; // Ensure 32-bit integers
|
|
}
|
|
|
|
List<int> decipher(List<int> data) {
|
|
int v0 = data[0];
|
|
int v1 = data[1];
|
|
int sum = XTEA_DELTA * xteaNumRounds;
|
|
|
|
for (int i = 0; i < xteaNumRounds; i++) {
|
|
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + _xteaKey[(sum >> 11) & 3]);
|
|
sum -= XTEA_DELTA;
|
|
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + _xteaKey[sum & 3]);
|
|
}
|
|
|
|
return [v0 & 0xFFFFFFFF, v1 & 0xFFFFFFFF]; // Ensure 32-bit integers
|
|
}
|
|
|
|
String encryptString(String plaintext) {
|
|
List<int> data = utf8.encode(plaintext).toList();
|
|
while (data.length % 8 != 0) {
|
|
data = [...data, 0]; // Zero padding
|
|
}
|
|
|
|
List<int> encrypted = [];
|
|
for (int i = 0; i < data.length; i += 8) {
|
|
List<int> block = encipher([
|
|
(data[i] << 24) |
|
|
(data[i + 1] << 16) |
|
|
(data[i + 2] << 8) |
|
|
data[i + 3],
|
|
(data[i + 4] << 24) |
|
|
(data[i + 5] << 16) |
|
|
(data[i + 6] << 8) |
|
|
data[i + 7]
|
|
]);
|
|
encrypted.addAll([
|
|
(block[0] >> 24) & 0xFF,
|
|
(block[0] >> 16) & 0xFF,
|
|
(block[0] >> 8) & 0xFF,
|
|
block[0] & 0xFF,
|
|
(block[1] >> 24) & 0xFF,
|
|
(block[1] >> 16) & 0xFF,
|
|
(block[1] >> 8) & 0xFF,
|
|
block[1] & 0xFF
|
|
]);
|
|
}
|
|
|
|
return base64.encode(encrypted);
|
|
}
|
|
|
|
String decryptString(String ciphertext) {
|
|
List<int> data = base64.decode(ciphertext).toList();
|
|
List<int> decrypted = [];
|
|
|
|
for (int i = 0; i < data.length; i += 8) {
|
|
List<int> block = decipher([
|
|
(data[i] << 24) |
|
|
(data[i + 1] << 16) |
|
|
(data[i + 2] << 8) |
|
|
data[i + 3],
|
|
(data[i + 4] << 24) |
|
|
(data[i + 5] << 16) |
|
|
(data[i + 6] << 8) |
|
|
data[i + 7]
|
|
]);
|
|
decrypted.addAll([
|
|
(block[0] >> 24) & 0xFF,
|
|
(block[0] >> 16) & 0xFF,
|
|
(block[0] >> 8) & 0xFF,
|
|
block[0] & 0xFF,
|
|
(block[1] >> 24) & 0xFF,
|
|
(block[1] >> 16) & 0xFF,
|
|
(block[1] >> 8) & 0xFF,
|
|
block[1] & 0xFF
|
|
]);
|
|
}
|
|
|
|
// Find the index of the last non-zero byte
|
|
int paddingStart = decrypted.lastIndexWhere((byte) => byte != 0);
|
|
|
|
// If padding was added, remove only trailing zero bytes
|
|
if (paddingStart != -1 && paddingStart + 1 < decrypted.length) {
|
|
decrypted = decrypted.sublist(0, paddingStart + 1);
|
|
}
|
|
|
|
// Attempt to decode as UTF-8
|
|
try {
|
|
return utf8.decode(decrypted);
|
|
} catch (e) {
|
|
throw FormatException('Decrypted data is not valid UTF-8: $e');
|
|
}
|
|
}
|
|
|
|
String md5(String input) {
|
|
return Hashing.llMD5String(input, 0);
|
|
}
|
|
}
|