Add encryption routines to the server

This commit is contained in:
zontreck 2025-01-05 04:15:33 -07:00
parent d7cc626144
commit 38eb7c6acd
6 changed files with 472 additions and 13 deletions

80
lib/encryption/aes.dart Normal file
View file

@ -0,0 +1,80 @@
import 'dart:typed_data';
import 'dart:math';
class AES {
final Uint8List _aesKey;
AES._(this._aesKey);
static Future<AES> generate({int aesKeySize = 256}) async {
final random = Random.secure();
// 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 AES useKey(Uint8List key) {
AES aes = AES._(key);
return aes;
}
Uint8List getKey() {
return Uint8List.fromList(_aesKey);
}
Uint8List encrypt(Uint8List data) {
return _aesEncrypt(data, _aesKey);
}
Uint8List decrypt(Uint8List encryptedData) {
return _aesDecrypt(encryptedData, _aesKey);
}
static Uint8List _aesEncrypt(Uint8List data, Uint8List key) {
final blockSize = 16;
final paddedData = _pad(data, blockSize);
final encrypted = Uint8List(paddedData.length);
for (int i = 0; i < paddedData.length; i += blockSize) {
for (int j = 0; j < blockSize; j++) {
encrypted[i + j] = paddedData[i + j] ^ key[j % key.length];
}
}
return encrypted;
}
static Uint8List _aesDecrypt(Uint8List data, Uint8List key) {
final blockSize = 16;
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);
}
}

151
lib/encryption/rsa.dart Normal file
View file

@ -0,0 +1,151 @@
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);
}
}

152
lib/encryption/xxtea.dart Normal file
View file

@ -0,0 +1,152 @@
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');
// }