import 'dart:convert'; import 'dart:math'; import 'dart:typed_data'; import 'package:bcrypt/bcrypt.dart'; import 'package:crypto/crypto.dart'; import 'package:crypton/crypton.dart' as crypton; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart' as jwt; import '../../config/config.dart'; class KeyPair { final String publicKey; final String privateKey; KeyPair({required this.publicKey, required this.privateKey}); Map toJson() => { 'publicKey': publicKey, 'privateKey': privateKey, }; factory KeyPair.fromJson(Map json) { return KeyPair( publicKey: json['publicKey'] as String, privateKey: json['privateKey'] as String, ); } } class CryptoService { /// Generate a new Ed25519 key pair static KeyPair generateKeyPair() { final keyPair = crypton.ECKeypair.fromRandom(); return KeyPair( publicKey: keyPair.publicKey.toString(), privateKey: keyPair.privateKey.toString(), ); } /// Sign data with private key static String sign(String data, String privateKey) { final privKey = crypton.ECPrivateKey.fromString(privateKey); final message = Uint8List.fromList(utf8.encode(data)); final signature = privKey.createSHA256Signature(message); return base64Encode(signature); } /// Verify signature with public key static bool verify(String data, String signature, String publicKey) { try { final pubKey = crypton.ECPublicKey.fromString(publicKey); final message = Uint8List.fromList(utf8.encode(data)); final signatureBytes = Uint8List.fromList(base64Decode(signature)); return pubKey.verifySHA256Signature(message, signatureBytes); } catch (_) { return false; } } /// Encrypt private key with password (for storage/backup) static EncryptedKey encryptPrivateKey(String privateKey, String password) { final salt = BCrypt.gensalt(); final key = _deriveKey(password, salt); final encrypted = _xorEncrypt(privateKey, key); return EncryptedKey( encryptedData: encrypted, salt: salt, ); } /// Decrypt private key with password static String? decryptPrivateKey( String encryptedData, String salt, String password) { try { final key = _deriveKey(password, salt); return _xorDecrypt(encryptedData, key); } catch (_) { return null; } } static String _deriveKey(String password, String salt) { final bytes = utf8.encode(password + salt); final hash = sha256.convert(bytes); return hash.toString(); } static String _xorEncrypt(String data, String key) { final dataBytes = utf8.encode(data); final keyBytes = utf8.encode(key); final encrypted = []; for (var i = 0; i < dataBytes.length; i++) { encrypted.add(dataBytes[i] ^ keyBytes[i % keyBytes.length]); } return base64Encode(encrypted); } static String _xorDecrypt(String encrypted, String key) { final encryptedBytes = base64Decode(encrypted); final keyBytes = utf8.encode(key); final decrypted = []; for (var i = 0; i < encryptedBytes.length; i++) { decrypted.add(encryptedBytes[i] ^ keyBytes[i % keyBytes.length]); } return utf8.decode(decrypted); } } class EncryptedKey { final String encryptedData; final String salt; EncryptedKey({required this.encryptedData, required this.salt}); Map toJson() => { 'encryptedData': encryptedData, 'salt': salt, }; } class AuthService { static final _nonceRandom = Random.secure(); /// Generate JWT token for user static String generateToken(String userId, String username) { final payload = { 'sub': userId, 'username': username, }; final token = jwt.JWT(payload); return token.sign( jwt.SecretKey(Config.jwtSecret), expiresIn: Config.tokenExpiry, ); } /// Verify JWT token and return payload static Map? verifyToken(String token) { try { final verified = jwt.JWT.verify(token, jwt.SecretKey(Config.jwtSecret)); return Map.from(verified.payload as Map); } catch (_) { return null; } } /// Hash password with bcrypt static String hashPassword(String password) { return BCrypt.hashpw(password, BCrypt.gensalt()); } /// Verify password against hash static bool verifyPassword(String password, String hash) { return BCrypt.checkpw(password, hash); } /// Generate a nonce for signature verification static String generateNonce() { final bytes = List.generate(32, (_) => _nonceRandom.nextInt(256)); final hash = sha256.convert(bytes); return hash.toString().substring(0, 32); } }