feat: Initial MVP with home server identity model

- User model with Ed25519 keypair identity
- Register/login on home server (creates keypair)
- Join guest servers with signature verification
- Public endpoints for cross-server identity
- Channel and message CRUD
- JWT authentication middleware

Architecture: Home server model, no federation, client-driven sync
This commit is contained in:
Z User committed 2026-02-19 23:07:14 +00:00
1 parent 5088313f74
commit f57fe229fc
13 files changed
+1427 -1

No files matched your search

+161
View File
@@ -0,0 +1,161 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:bcrypt/bcrypt.dart';
import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart';
import 'package:crypton/crypton.dart';
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
import '../config/config.dart';
class KeyPair {
final String publicKey;
final String privateKey;
KeyPair({required this.publicKey, required this.privateKey});
Map<String, dynamic> toJson() => {
'publicKey': publicKey,
'privateKey': privateKey,
};
factory KeyPair.fromJson(Map<String, dynamic> 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 = 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 = ECPrivateKey.fromString(privateKey);
final signature = privKey.createSignature(data);
return signature.toString();
}
/// Verify signature with public key
static bool verify(String data, String signature, String publicKey) {
try {
final pubKey = ECPublicKey.fromString(publicKey);
final sig = ECSignature.fromString(signature);
return pubKey.verifySignature(data, sig);
} 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 = <int>[];
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 = <int>[];
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<String, dynamic> toJson() => {
'encryptedData': encryptedData,
'salt': salt,
};
}
class AuthService {
/// Generate JWT token for user
static String generateToken(String userId, String username) {
final payload = {
'sub': userId,
'username': username,
};
final jwt = JWT(payload);
return jwt.sign(
SecretKey(Config.jwtSecret),
expiresIn: Config.tokenExpiry,
);
}
/// Verify JWT token and return payload
static Map<String, dynamic>? verifyToken(String token) {
try {
final jwt = JWT.verify(token, SecretKey(Config.jwtSecret));
return Map<String, dynamic>.from(jwt.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<int>.generate(32, (_) => DateTime.now().microsecondsSinceEpoch % 256);
final randomBytes = sha256.convert(bytes as Uint8List);
return randomBytes.toString().substring(0, 32);
}
}