Files
moxxy/lib/shared/logging.dart

106 lines
2.6 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:cryptography/cryptography.dart';
import 'package:udp/udp.dart';
Future<List<int>> deriveKey(String key) async {
return (await Sha256().hash(utf8.encode(key))).bytes;
}
/// Encrypt a log entry with AES-256 in GCM mode. Takes an optional argument [nonce], which
/// SHOULD NEVER be used, except when testing the function.
/// This function is "validated" against
/// https://github.com/monal-im/Monal/blob/develop/UDPLogServer/server.py if
/// [key] is derived using [deriveKey].
Future<List<int>> encryptData(
List<int> data,
List<int> key, {
List<int>? nonce,
}) async {
final algorithm = AesGcm.with256bits();
final secretBox = await algorithm.encrypt(
data,
secretKey: SecretKey(key),
nonce: nonce ?? algorithm.newNonce(),
);
return [...secretBox.nonce, ...secretBox.mac.bytes, ...secretBox.cipherText];
}
/// Just a wrapper around encoder to compress the payload using GZip.
List<int> compressData(List<int> payload) {
return GZipEncoder().encode(payload)!;
}
/// Format a log message similarly as to how Monal does it.
List<int> logToPayload(
String line,
int timestamp,
String loglevel,
int counter, {
String? filename,
}) {
return utf8.encode(
jsonEncode(
<String, dynamic>{
'formattedMessage': line,
'timestamp': timestamp.toString(),
'level': loglevel,
'_counter': counter,
...filename != null
? <String, String>{'filename': filename}
: <String, String>{}
},
),
);
}
class UDPLogger {
UDPLogger()
: _counter = 0,
_canSend = false,
_enabled = true;
late UDP _sender;
late Endpoint _target;
late List<int> _derivedKey;
int _counter;
bool _canSend;
bool _enabled;
Future<void> init(String key, String ip, int port) async {
_sender = await UDP.bind(Endpoint.any());
_derivedKey = await deriveKey(key);
_target = Endpoint.unicast(InternetAddress(ip), port: Port(port));
_canSend = true;
_enabled = true;
}
void setEnabled(bool enabled) => _enabled = enabled;
bool isEnabled() => _enabled;
Future<void> sendLog(
String line,
int timestamp,
String loglevel, {
String? filename,
}) async {
if (!_canSend || !_enabled) return;
final rawPayload =
logToPayload(line, timestamp, loglevel, _counter, filename: filename);
final compressed = compressData(rawPayload);
final encrypted = await encryptData(compressed, _derivedKey);
try {
await _sender.send(encrypted, _target);
} catch (_) {
// Nothing
}
_counter++;
}
}