106 lines
2.6 KiB
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++;
|
|
}
|
|
}
|