Add encryption routines to the server
This commit is contained in:
parent
d7cc626144
commit
38eb7c6acd
6 changed files with 472 additions and 13 deletions
80
lib/encryption/aes.dart
Normal file
80
lib/encryption/aes.dart
Normal 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
151
lib/encryption/rsa.dart
Normal 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
152
lib/encryption/xxtea.dart
Normal 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');
|
||||
// }
|
Loading…
Add table
Add a link
Reference in a new issue