Finish implementing some encryption in the network protocols with AES
This commit is contained in:
parent
38eb7c6acd
commit
84cef345eb
10 changed files with 263 additions and 391 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -32,3 +32,6 @@ doc/api/
|
||||||
*.iml
|
*.iml
|
||||||
|
|
||||||
out
|
out
|
||||||
|
|
||||||
|
test/aesKey.bin
|
||||||
|
test/HelloWorld.*
|
|
@ -1,3 +1,3 @@
|
||||||
class Constants {
|
class Constants {
|
||||||
static const VERSION = "1.3.010525+0414";
|
static const VERSION = "1.3.010625+0228";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,80 +1,55 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
class AES {
|
import 'package:encrypt/encrypt.dart';
|
||||||
|
|
||||||
|
class AESData {
|
||||||
|
final List<int> iv;
|
||||||
|
final List<int> data;
|
||||||
|
|
||||||
|
AESData({required this.iv, required this.data});
|
||||||
|
}
|
||||||
|
|
||||||
|
class AESCipher {
|
||||||
final Uint8List _aesKey;
|
final Uint8List _aesKey;
|
||||||
|
|
||||||
AES._(this._aesKey);
|
AESCipher._(this._aesKey);
|
||||||
|
|
||||||
static Future<AES> generate({int aesKeySize = 256}) async {
|
static Future<AESCipher> generate({int aesKeySize = 256}) async {
|
||||||
final random = Random.secure();
|
return AESCipher._(Key.fromLength(aesKeySize ~/ 8).bytes);
|
||||||
|
|
||||||
// Generate AES Key
|
|
||||||
final aesKey = Uint8List(aesKeySize ~/ 8);
|
|
||||||
for (int i = 0; i < aesKey.length; i++) {
|
|
||||||
aesKey[i] = random.nextInt(256);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return AES._(aesKey);
|
static AESCipher useKey(Uint8List key) {
|
||||||
}
|
return AESCipher._(key);
|
||||||
|
|
||||||
static AES useKey(Uint8List key) {
|
|
||||||
AES aes = AES._(key);
|
|
||||||
|
|
||||||
return aes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List getKey() {
|
Uint8List getKey() {
|
||||||
return Uint8List.fromList(_aesKey);
|
return Uint8List.fromList(_aesKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List encrypt(Uint8List data) {
|
Future<AESData> encrypt(Uint8List data) async {
|
||||||
return _aesEncrypt(data, _aesKey);
|
final iv = IV.fromLength(16); // Generate a random 16-byte IV
|
||||||
|
final encryptedData = await _aesEncrypt(data, _aesKey, iv);
|
||||||
|
|
||||||
|
return AESData(iv: iv.bytes.toList(), data: encryptedData.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List decrypt(Uint8List encryptedData) {
|
Future<Uint8List> decrypt(AESData data) async {
|
||||||
return _aesDecrypt(encryptedData, _aesKey);
|
final iv = IV(Uint8List.fromList(data.iv));
|
||||||
|
return _aesDecrypt(Uint8List.fromList(data.data), _aesKey, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint8List _aesEncrypt(Uint8List data, Uint8List key) {
|
static Future<Uint8List> _aesEncrypt(
|
||||||
final blockSize = 16;
|
Uint8List data, Uint8List key, IV iv) async {
|
||||||
final paddedData = _pad(data, blockSize);
|
var aes = Encrypter(AES(Key(key), mode: AESMode.cbc));
|
||||||
final encrypted = Uint8List(paddedData.length);
|
|
||||||
|
|
||||||
for (int i = 0; i < paddedData.length; i += blockSize) {
|
final encrypted = await aes.encryptBytes(data.toList(), iv: iv);
|
||||||
for (int j = 0; j < blockSize; j++) {
|
return encrypted.bytes;
|
||||||
encrypted[i + j] = paddedData[i + j] ^ key[j % key.length];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return encrypted;
|
static Future<Uint8List> _aesDecrypt(
|
||||||
}
|
Uint8List data, Uint8List key, IV iv) async {
|
||||||
|
final aes = Encrypter(AES(Key(key), mode: AESMode.cbc));
|
||||||
static Uint8List _aesDecrypt(Uint8List data, Uint8List key) {
|
final decrypted = await aes.decryptBytes(Encrypted(data), iv: iv);
|
||||||
final blockSize = 16;
|
return Uint8List.fromList(decrypted);
|
||||||
final decrypted = Uint8List(data.length);
|
|
||||||
|
|
||||||
for (int i = 0; i < data.length; i += blockSize) {
|
|
||||||
for (int j = 0; j < blockSize; j++) {
|
|
||||||
decrypted[i + j] = data[i + j] ^ key[j % key.length];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _unpad(decrypted);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8List _pad(Uint8List data, int blockSize) {
|
|
||||||
final padLength = blockSize - (data.length % blockSize);
|
|
||||||
final paddedData = Uint8List(data.length + padLength);
|
|
||||||
paddedData.setAll(0, data);
|
|
||||||
for (int i = data.length; i < paddedData.length; i++) {
|
|
||||||
paddedData[i] = padLength;
|
|
||||||
}
|
|
||||||
return paddedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8List _unpad(Uint8List data) {
|
|
||||||
final padLength = data[data.length - 1];
|
|
||||||
return Uint8List.sublistView(data, 0, data.length - padLength);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
class RSA {
|
|
||||||
final RSAPublicKey publicKey;
|
|
||||||
final RSAPrivateKey privateKey;
|
|
||||||
|
|
||||||
RSA._(this.publicKey, this.privateKey);
|
|
||||||
|
|
||||||
static RSA generate({int keySize = 2048}) {
|
|
||||||
final random = Random.secure();
|
|
||||||
|
|
||||||
// Generate RSA Key Pair
|
|
||||||
final p = _generatePrime(keySize ~/ 2, random);
|
|
||||||
final q = _generatePrime(keySize ~/ 2, random);
|
|
||||||
final n = p * q;
|
|
||||||
final phi = (p - BigInt.one) * (q - BigInt.one);
|
|
||||||
final e = BigInt.from(65537); // Common public exponent
|
|
||||||
final d = _modInverse(e, phi);
|
|
||||||
|
|
||||||
final publicKey = RSAPublicKey(n, e);
|
|
||||||
final privateKey = RSAPrivateKey(n, d);
|
|
||||||
|
|
||||||
return RSA._(publicKey, privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
static RSA fromKeyPair(RSAPrivateKey priv, RSAPublicKey pub) {
|
|
||||||
RSA rsa = RSA._(pub, priv);
|
|
||||||
return rsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List encrypt(Uint8List data) {
|
|
||||||
return publicKey.encrypt(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List decrypt(Uint8List encryptedData) {
|
|
||||||
return privateKey.decrypt(encryptedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List sign(Uint8List data) {
|
|
||||||
return privateKey.sign(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool verify(Uint8List data, Uint8List signature) {
|
|
||||||
return publicKey.verify(data, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BigInt _generatePrime(int bitLength, Random random) {
|
|
||||||
while (true) {
|
|
||||||
final candidate = BigInt.from(random.nextInt(1 << (bitLength - 1)) | 1);
|
|
||||||
if (_isPrime(candidate)) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _isPrime(BigInt n) {
|
|
||||||
if (n < BigInt.two) return false;
|
|
||||||
for (BigInt i = BigInt.two; i * i <= n; i += BigInt.one) {
|
|
||||||
if (n % i == BigInt.zero) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BigInt _modInverse(BigInt a, BigInt m) {
|
|
||||||
BigInt m0 = m;
|
|
||||||
BigInt y = BigInt.zero, x = BigInt.one;
|
|
||||||
|
|
||||||
while (a > BigInt.one) {
|
|
||||||
BigInt q = a ~/ m;
|
|
||||||
BigInt t = m;
|
|
||||||
|
|
||||||
m = a % m;
|
|
||||||
a = t;
|
|
||||||
t = y;
|
|
||||||
|
|
||||||
y = x - q * y;
|
|
||||||
x = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x < BigInt.zero) {
|
|
||||||
x += m0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RSAPublicKey {
|
|
||||||
final BigInt modulus;
|
|
||||||
final BigInt exponent;
|
|
||||||
|
|
||||||
RSAPublicKey(this.modulus, this.exponent);
|
|
||||||
|
|
||||||
Uint8List encrypt(Uint8List data) {
|
|
||||||
final message = BigInt.parse(
|
|
||||||
data.toList().map((b) => b.toRadixString(16).padLeft(2, '0')).join(),
|
|
||||||
radix: 16);
|
|
||||||
final encrypted = message.modPow(exponent, modulus);
|
|
||||||
return Uint8List.fromList(encrypted
|
|
||||||
.toRadixString(16)
|
|
||||||
.padLeft((modulus.bitLength + 7) ~/ 8 * 2, '0')
|
|
||||||
.codeUnits);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool verify(Uint8List data, Uint8List signature) {
|
|
||||||
final signedBigInt = BigInt.parse(
|
|
||||||
signature
|
|
||||||
.toList()
|
|
||||||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
|
||||||
.join(),
|
|
||||||
radix: 16);
|
|
||||||
final decryptedSignature = signedBigInt.modPow(exponent, modulus);
|
|
||||||
final message = BigInt.parse(
|
|
||||||
data.toList().map((b) => b.toRadixString(16).padLeft(2, '0')).join(),
|
|
||||||
radix: 16);
|
|
||||||
return message == decryptedSignature;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RSAPrivateKey {
|
|
||||||
final BigInt modulus;
|
|
||||||
final BigInt exponent;
|
|
||||||
|
|
||||||
RSAPrivateKey(this.modulus, this.exponent);
|
|
||||||
|
|
||||||
Uint8List decrypt(Uint8List encryptedData) {
|
|
||||||
final encryptedBigInt = BigInt.parse(
|
|
||||||
encryptedData
|
|
||||||
.toList()
|
|
||||||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
|
||||||
.join(),
|
|
||||||
radix: 16);
|
|
||||||
final decrypted = encryptedBigInt.modPow(exponent, modulus);
|
|
||||||
return Uint8List.fromList(decrypted
|
|
||||||
.toRadixString(16)
|
|
||||||
.padLeft((modulus.bitLength + 7) ~/ 8 * 2, '0')
|
|
||||||
.codeUnits);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List sign(Uint8List data) {
|
|
||||||
final message = BigInt.parse(
|
|
||||||
data.toList().map((b) => b.toRadixString(16).padLeft(2, '0')).join(),
|
|
||||||
radix: 16);
|
|
||||||
final signed = message.modPow(exponent, modulus);
|
|
||||||
return Uint8List.fromList(signed
|
|
||||||
.toRadixString(16)
|
|
||||||
.padLeft((modulus.bitLength + 7) ~/ 8 * 2, '0')
|
|
||||||
.codeUnits);
|
|
||||||
}
|
|
||||||
}
|
|
155
lib/encryption/xtea.dart
Normal file
155
lib/encryption/xtea.dart
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,152 +0,0 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
class XXTEA {
|
|
||||||
final Uint8List _key;
|
|
||||||
|
|
||||||
XXTEA._(this._key);
|
|
||||||
|
|
||||||
static Future<XXTEA> fromPassword(String password,
|
|
||||||
{String salt = 'defaultSalt'}) async {
|
|
||||||
final keyBytes = Uint8List.fromList(utf8.encode(password + salt));
|
|
||||||
final key = _fixKeyLength(keyBytes);
|
|
||||||
return XXTEA._(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List encryptBytes(Uint8List data) {
|
|
||||||
final paddedData = _padData(data);
|
|
||||||
return _xxteaEncrypt(paddedData, _key);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List decryptBytes(Uint8List encryptedData) {
|
|
||||||
final decryptedData = _xxteaDecrypt(encryptedData, _key);
|
|
||||||
return _unpadData(decryptedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
String encryptString(String plaintext) {
|
|
||||||
final encryptedBytes =
|
|
||||||
encryptBytes(Uint8List.fromList(utf8.encode(plaintext)));
|
|
||||||
return base64.encode(encryptedBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
String decryptString(String encryptedText) {
|
|
||||||
final encryptedBytes = base64.decode(encryptedText);
|
|
||||||
final decryptedBytes = decryptBytes(encryptedBytes);
|
|
||||||
return utf8.decode(decryptedBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List signBytes(Uint8List data) {
|
|
||||||
final signature = _xxteaEncrypt(data, _key);
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool verifySignature(Uint8List data, Uint8List signature) {
|
|
||||||
final expectedSignature = signBytes(data);
|
|
||||||
return _constantTimeEquals(expectedSignature, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
String signString(String data) {
|
|
||||||
final signature = signBytes(Uint8List.fromList(utf8.encode(data)));
|
|
||||||
return base64.encode(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool verifyStringSignature(String data, String signature) {
|
|
||||||
final dataBytes = Uint8List.fromList(utf8.encode(data));
|
|
||||||
final signatureBytes = base64.decode(signature);
|
|
||||||
return verifySignature(dataBytes, signatureBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8List _fixKeyLength(Uint8List key) {
|
|
||||||
final fixedKey = Uint8List(16);
|
|
||||||
for (int i = 0; i < key.length && i < 16; i++) {
|
|
||||||
fixedKey[i] = key[i];
|
|
||||||
}
|
|
||||||
return fixedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8List _padData(Uint8List data) {
|
|
||||||
final padLength = 4 - (data.length % 4);
|
|
||||||
final paddedData = Uint8List(data.length + padLength);
|
|
||||||
paddedData.setAll(0, data);
|
|
||||||
return paddedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8List _unpadData(Uint8List data) {
|
|
||||||
int unpaddedLength = data.length;
|
|
||||||
while (unpaddedLength > 0 && data[unpaddedLength - 1] == 0) {
|
|
||||||
unpaddedLength--;
|
|
||||||
}
|
|
||||||
return Uint8List.sublistView(data, 0, unpaddedLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8List _xxteaEncrypt(Uint8List data, Uint8List key) {
|
|
||||||
final n = data.length ~/ 4;
|
|
||||||
final v = Uint32List.view(data.buffer);
|
|
||||||
final k = Uint32List.view(key.buffer);
|
|
||||||
int sum = 0;
|
|
||||||
const delta = 0x9E3779B9;
|
|
||||||
|
|
||||||
for (int i = 0; i < 6 + 52 ~/ n; i++) {
|
|
||||||
sum = (sum + delta) & 0xFFFFFFFF;
|
|
||||||
final e = (sum >> 2) & 3;
|
|
||||||
for (int p = 0; p < n - 1; p++) {
|
|
||||||
v[p] = (v[p] + _mx(sum, v, k, p, e, n)) & 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
v[n - 1] = (v[n - 1] + _mx(sum, v, k, n - 1, e, n)) & 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Uint8List.view(v.buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8List _xxteaDecrypt(Uint8List data, Uint8List key) {
|
|
||||||
final n = data.length ~/ 4;
|
|
||||||
final v = Uint32List.view(data.buffer);
|
|
||||||
final k = Uint32List.view(key.buffer);
|
|
||||||
const delta = 0x9E3779B9;
|
|
||||||
int sum = (6 + 52 ~/ n) * delta;
|
|
||||||
|
|
||||||
while (sum != 0) {
|
|
||||||
final e = (sum >> 2) & 3;
|
|
||||||
for (int p = n - 1; p > 0; p--) {
|
|
||||||
v[p] = (v[p] - _mx(sum, v, k, p, e, n)) & 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
v[0] = (v[0] - _mx(sum, v, k, 0, e, n)) & 0xFFFFFFFF;
|
|
||||||
sum = (sum - delta) & 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Uint8List.view(v.buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _mx(int sum, Uint32List v, Uint32List k, int p, int e, int n) {
|
|
||||||
return ((v[(p + 1) % n] ^ v[p]) + (k[p & 3 ^ e] ^ sum)) & 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _constantTimeEquals(Uint8List a, Uint8List b) {
|
|
||||||
if (a.length != b.length) return false;
|
|
||||||
int result = 0;
|
|
||||||
for (int i = 0; i < a.length; i++) {
|
|
||||||
result |= a[i] ^ b[i];
|
|
||||||
}
|
|
||||||
return result == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example usage:
|
|
||||||
//
|
|
||||||
// void main() async {
|
|
||||||
// final password = 'securePassword';
|
|
||||||
// final secure = await XXTEA.fromPassword(password);
|
|
||||||
//
|
|
||||||
// final plaintext = 'Hello, secure world!';
|
|
||||||
// final encrypted = secure.encryptString(plaintext);
|
|
||||||
// print('Encrypted: \$encrypted');
|
|
||||||
//
|
|
||||||
// final decrypted = secure.decryptString(encrypted);
|
|
||||||
// print('Decrypted: \$decrypted');
|
|
||||||
//
|
|
||||||
// final signature = secure.signString(plaintext);
|
|
||||||
// print('Signature: \$signature');
|
|
||||||
//
|
|
||||||
// final isValid = secure.verifyStringSignature(plaintext, signature);
|
|
||||||
// print('Signature valid: \$isValid');
|
|
||||||
// }
|
|
|
@ -4,8 +4,7 @@ import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:libac_dart/encryption/aes.dart';
|
import 'package:libac_dart/encryption/aes.dart';
|
||||||
import 'package:libac_dart/encryption/rsa.dart';
|
import 'package:libac_dart/encryption/xtea.dart';
|
||||||
import 'package:libac_dart/encryption/xxtea.dart';
|
|
||||||
|
|
||||||
import '../nbt/NbtIo.dart';
|
import '../nbt/NbtIo.dart';
|
||||||
import '../nbt/Stream.dart';
|
import '../nbt/Stream.dart';
|
||||||
|
@ -14,9 +13,8 @@ import '../nbt/impl/CompoundTag.dart';
|
||||||
import '../nbt/impl/StringTag.dart';
|
import '../nbt/impl/StringTag.dart';
|
||||||
|
|
||||||
enum EncryptionType {
|
enum EncryptionType {
|
||||||
RSA(value: 3),
|
|
||||||
AES(value: 2),
|
AES(value: 2),
|
||||||
XXTEA(value: 1),
|
XTEA(value: 1),
|
||||||
NONE(value: 0);
|
NONE(value: 0);
|
||||||
|
|
||||||
final int value;
|
final int value;
|
||||||
|
@ -26,16 +24,12 @@ enum EncryptionType {
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
return EncryptionType.XXTEA;
|
return EncryptionType.XTEA;
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
return EncryptionType.AES;
|
return EncryptionType.AES;
|
||||||
}
|
}
|
||||||
case 3:
|
|
||||||
{
|
|
||||||
return EncryptionType.RSA;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return EncryptionType.NONE;
|
return EncryptionType.NONE;
|
||||||
|
@ -49,8 +43,6 @@ class PacketServer {
|
||||||
static bool shouldRestart = true;
|
static bool shouldRestart = true;
|
||||||
static EncryptionType encryptionType = EncryptionType.NONE;
|
static EncryptionType encryptionType = EncryptionType.NONE;
|
||||||
static Uint8List AESKey = Uint8List(0);
|
static Uint8List AESKey = Uint8List(0);
|
||||||
static RSAPrivateKey rsaPrivateKey = RSAPrivateKey(BigInt.zero, BigInt.zero);
|
|
||||||
static RSAPublicKey rsaPublicKey = RSAPublicKey(BigInt.zero, BigInt.zero);
|
|
||||||
static String PSK = "";
|
static String PSK = "";
|
||||||
static String TEA_SALT = "Harbinger 01/05/2025 @ 03:59:17 AM";
|
static String TEA_SALT = "Harbinger 01/05/2025 @ 03:59:17 AM";
|
||||||
|
|
||||||
|
@ -115,16 +107,17 @@ class PacketServer {
|
||||||
|
|
||||||
List<int> remainingBytes = layer.readBytes(numBytes);
|
List<int> remainingBytes = layer.readBytes(numBytes);
|
||||||
if (ENCType == EncryptionType.AES) {
|
if (ENCType == EncryptionType.AES) {
|
||||||
AES aes = await AES.useKey(AESKey);
|
int ivLen = layer.readInt();
|
||||||
remainingBytes = aes.decrypt(Uint8List.fromList(remainingBytes));
|
List<int> ivBytes = layer.readBytes(ivLen);
|
||||||
} else if (ENCType == EncryptionType.RSA) {
|
AESData encData = AESData(iv: ivBytes, data: remainingBytes);
|
||||||
RSA rsa = await RSA.fromKeyPair(rsaPrivateKey, rsaPublicKey);
|
|
||||||
remainingBytes = rsa.decrypt(Uint8List.fromList(remainingBytes));
|
AESCipher aes = await AESCipher.useKey(AESKey);
|
||||||
} else if (ENCType == EncryptionType.XXTEA) {
|
remainingBytes = await aes.decrypt(encData);
|
||||||
XXTEA xtea = await XXTEA.fromPassword(PSK, salt: TEA_SALT);
|
} else if (ENCType == EncryptionType.XTEA) {
|
||||||
|
XTEA xtea = await XTEA(PSK);
|
||||||
|
|
||||||
remainingBytes =
|
remainingBytes =
|
||||||
xtea.decryptBytes(Uint8List.fromList(remainingBytes));
|
xtea.decipher(Uint8List.fromList(remainingBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
CompoundTag tag =
|
CompoundTag tag =
|
||||||
|
@ -153,14 +146,15 @@ class PacketServer {
|
||||||
|
|
||||||
// Encryption Subroutine
|
// Encryption Subroutine
|
||||||
if (ENCType == EncryptionType.AES) {
|
if (ENCType == EncryptionType.AES) {
|
||||||
AES aes = AES.useKey(AESKey);
|
AESCipher aes = AESCipher.useKey(AESKey);
|
||||||
nbtData = aes.encrypt(nbtData);
|
var encData = await aes.encrypt(nbtData);
|
||||||
} else if (ENCType == EncryptionType.RSA) {
|
nbtData = Uint8List.fromList(encData.data);
|
||||||
RSA rsa = RSA.fromKeyPair(rsaPrivateKey, rsaPublicKey);
|
|
||||||
nbtData = rsa.encrypt(nbtData);
|
layer.writeInt(encData.iv.length);
|
||||||
} else if (ENCType == EncryptionType.XXTEA) {
|
layer.writeBytes(encData.iv);
|
||||||
XXTEA tea = await XXTEA.fromPassword(PSK, salt: TEA_SALT);
|
} else if (ENCType == EncryptionType.XTEA) {
|
||||||
nbtData = tea.encryptBytes(nbtData);
|
XTEA tea = await XTEA(PSK);
|
||||||
|
nbtData = Uint8List.fromList(tea.encipher(nbtData));
|
||||||
}
|
}
|
||||||
|
|
||||||
layer.writeLong(nbtData.lengthInBytes);
|
layer.writeLong(nbtData.lengthInBytes);
|
||||||
|
|
|
@ -8,6 +8,10 @@ class Hashing {
|
||||||
return bytes2Hash(md5SumStr(input));
|
return bytes2Hash(md5SumStr(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String llMD5String(String src, int nonce) {
|
||||||
|
return md5Hash("${src}:${nonce}");
|
||||||
|
}
|
||||||
|
|
||||||
/// This will generate the Sha1 bytes and hash it using #bytes2Hash
|
/// This will generate the Sha1 bytes and hash it using #bytes2Hash
|
||||||
static String sha1Hash(String input) {
|
static String sha1Hash(String input) {
|
||||||
return bytes2Hash(sha1SumStr(input));
|
return bytes2Hash(sha1SumStr(input));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: libac_dart
|
name: libac_dart
|
||||||
description: "Aria's Creations code library"
|
description: "Aria's Creations code library"
|
||||||
version: 1.3.010525+0414
|
version: 1.3.010625+0228
|
||||||
homepage: "https://zontreck.com"
|
homepage: "https://zontreck.com"
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@ -10,6 +10,7 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
crypto: ^3.0.3
|
crypto: ^3.0.3
|
||||||
dio: ^5.5.0+1
|
dio: ^5.5.0+1
|
||||||
|
encrypt: ^5.0.3
|
||||||
# path: ^1.8.0
|
# path: ^1.8.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
43
test/encryption_test.dart
Normal file
43
test/encryption_test.dart
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:libac_dart/encryption/aes.dart';
|
||||||
|
import 'package:libac_dart/encryption/xtea.dart';
|
||||||
|
import 'package:libac_dart/utils/Hashing.dart';
|
||||||
|
import 'package:test/expect.dart';
|
||||||
|
import 'package:test/scaffolding.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test("Test XTEA Encryption", () async {
|
||||||
|
String knownEncryptedValue =
|
||||||
|
"MU1T+AuHyBmALhbMOgZJQa5A"; // "Hello World!" // Test Key
|
||||||
|
String keyHash = "131515d94e2574cd680ab1a41ecdc34c";
|
||||||
|
List<int> knownKey = [320148953, 1311077581, 1745531300, 516801356];
|
||||||
|
|
||||||
|
XTEA tea = await XTEA("Test Key");
|
||||||
|
expect(-1, tea.testKey(knownKey));
|
||||||
|
|
||||||
|
String newValue = tea.encryptString("Hello World!");
|
||||||
|
|
||||||
|
expect(Hashing.llMD5String("Test Key", 0), keyHash);
|
||||||
|
expect(newValue, knownEncryptedValue);
|
||||||
|
expect(tea.decryptString(newValue), "Hello World!");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Test AES Implementation", () async {
|
||||||
|
File key = File("test/aesKey.bin");
|
||||||
|
File enc = File("test/HelloWorld.enc");
|
||||||
|
File hw = File("test/HelloWorld.txt");
|
||||||
|
String helloWorld = "Hello World!";
|
||||||
|
|
||||||
|
hw.writeAsStringSync(helloWorld);
|
||||||
|
|
||||||
|
AESCipher aes = await AESCipher.generate();
|
||||||
|
AESData encryptedNew = await aes.encrypt(utf8.encode(helloWorld));
|
||||||
|
|
||||||
|
enc.writeAsBytes(encryptedNew.data);
|
||||||
|
key.writeAsBytes(aes.getKey());
|
||||||
|
|
||||||
|
expect(helloWorld, utf8.decode(await aes.decrypt(encryptedNew)));
|
||||||
|
});
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue