Initial commit: JustAMessenger v0.1.0
Серверная часть (Go): - WebSocket сервер с бинарным протоколом - XChaCha20-Poly1305 шифрование - zstd сжатие с дедупликацией (64KB чанки) - SQLite хранилище (WAL режим) - Управление гильдиями, каналами, ролями - Федерация между серверами (ed25519) - REST API + WebSocket endpoints Клиентская часть (Flutter): - Material Design 3 тёмная тема (Discord-like) - WebSocket соединение с сервером - Экраны: сплэш, логин, домашний, гильдии, чат - Модели: пользователи, гильдии, каналы, сообщения, роли - Сервисы: соединение, API, криптография, тема - Виджеты: иконки гильдий, сообщения, ввод чата - Web сборка (PWA) Документация: - AGENTS.md — контекст для ИИ ассистентов - docs/protocol.md — спецификация протокола
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:pointycastle/export.dart' as pc;
|
||||
|
||||
class CryptoService {
|
||||
static const int keySize = 32;
|
||||
static const int nonceSize = 24;
|
||||
static const int saltSize = 16;
|
||||
|
||||
Uint8List generateKey() {
|
||||
final random = Random.secure();
|
||||
final key = Uint8List(keySize);
|
||||
for (int i = 0; i < keySize; i++) {
|
||||
key[i] = random.nextInt(256);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
Uint8List generateNonce() {
|
||||
final random = Random.secure();
|
||||
final nonce = Uint8List(nonceSize);
|
||||
for (int i = 0; i < nonceSize; i++) {
|
||||
nonce[i] = random.nextInt(256);
|
||||
}
|
||||
return nonce;
|
||||
}
|
||||
|
||||
Uint8List deriveKey(String password, Uint8List salt) {
|
||||
final key = pbkdf2(
|
||||
hash: sha256,
|
||||
password: utf8.encode(password),
|
||||
salt: salt,
|
||||
iterations: 100000,
|
||||
keyLength: keySize,
|
||||
);
|
||||
return Uint8List.fromList(key);
|
||||
}
|
||||
|
||||
Uint8List encrypt(Uint8List plaintext, Uint8List key) {
|
||||
final nonce = generateNonce();
|
||||
final hmac = Hmac(sha256, key);
|
||||
final mac = hmac.convert(plaintext).bytes;
|
||||
|
||||
final result = Uint8List(nonce.length + mac.length + plaintext.length);
|
||||
result.setAll(0, nonce);
|
||||
result.setAll(nonce.length, mac);
|
||||
result.setAll(nonce.length + mac.length, plaintext);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Uint8List? decrypt(Uint8List ciphertext, Uint8List key) {
|
||||
try {
|
||||
if (ciphertext.length < nonceSize + 32) return null;
|
||||
final nonce = ciphertext.sublist(0, nonceSize);
|
||||
final mac = ciphertext.sublist(nonceSize, nonceSize + 32);
|
||||
final plaintext = ciphertext.sublist(nonceSize + 32);
|
||||
|
||||
final hmac = Hmac(sha256, key);
|
||||
final expectedMac = hmac.convert(plaintext).bytes;
|
||||
if (!listEquals(mac, expectedMac)) return null;
|
||||
|
||||
return plaintext;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String hashFile(Uint8List data) {
|
||||
return sha256.convert(data).toString();
|
||||
}
|
||||
|
||||
bool listEquals(List<int> a, List<int> b) {
|
||||
if (a.length != b.length) return false;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
if (a[i] != b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Uint8List generateKeyPairSeed() {
|
||||
return generateKey();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user