Files
Client/test/push/push_decryptor_test.dart
T

93 lines
3.0 KiB
Dart

import 'dart:convert';
import 'dart:typed_data';
import 'package:crypton/crypton.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:marianum_mobile/push/push_decryptor.dart';
import 'package:pointycastle/export.dart' as pc;
/// Encrypts [plain] with [publicKey] using OAEP (SHA-1), mirroring Nextcloud's
/// default padding.
String _encryptOaep(RSAPublicKey publicKey, String plain) {
final cipher = pc.OAEPEncoding(pc.RSAEngine())
..init(
true,
pc.PublicKeyParameter<pc.RSAPublicKey>(publicKey.asPointyCastle),
);
final out = cipher.process(Uint8List.fromList(utf8.encode(plain)));
return base64.encode(out);
}
/// Signs the encrypted bytes with the server private key (SHA512withRSA).
String _sign(RSAPrivateKey serverPrivate, String subjectBase64) {
final signature = serverPrivate.createSHA512Signature(
Uint8List.fromList(base64.decode(subjectBase64)),
);
return base64.encode(signature);
}
void main() {
final device = RSAKeypair.fromRandom();
final server = RSAKeypair.fromRandom();
const subjectJson =
'{"app":"spreed","subject":"Max: Hallo","type":"chat","id":"abc123","nid":42}';
group('PushDecryptor', () {
test('decrypts an OAEP-encrypted subject', () {
final encrypted = _encryptOaep(device.publicKey, subjectJson);
final decryptor = PushDecryptor(
devicePrivateKey: device.privateKey,
serverPublicKey: server.publicKey,
);
expect(
decryptor.verify(encrypted, _sign(server.privateKey, encrypted)),
isTrue,
);
final subject = decryptor.decrypt(encrypted);
expect(subject, isNotNull);
expect(subject!.app, 'spreed');
expect(subject.isTalk, isTrue);
expect(subject.id, 'abc123');
expect(subject.nid, 42);
});
test('decrypts a PKCS1-encrypted subject (fallback)', () {
// crypton's encrypt uses PKCS#1 v1.5.
final encrypted = device.publicKey.encrypt(subjectJson);
final subject = PushDecryptor(
devicePrivateKey: device.privateKey,
).decrypt(encrypted);
expect(subject, isNotNull);
expect(subject!.subject, 'Max: Hallo');
});
test('rejects a signature made with the wrong key', () {
final encrypted = _encryptOaep(device.publicKey, subjectJson);
final wrong = RSAKeypair.fromRandom();
final decryptor = PushDecryptor(
devicePrivateKey: device.privateKey,
serverPublicKey: server.publicKey,
);
expect(
decryptor.verify(encrypted, _sign(wrong.privateKey, encrypted)),
isFalse,
);
});
test('parses delete-multiple subjects', () {
const deleteJson = '{"delete-multiple":true,"nids":[1,2,3]}';
final encrypted = _encryptOaep(device.publicKey, deleteJson);
final subject = PushDecryptor(
devicePrivateKey: device.privateKey,
).decrypt(encrypted);
expect(subject, isNotNull);
expect(subject!.deleteMultiple, isTrue);
expect(subject.isAnyDelete, isTrue);
expect(subject.nids, [1, 2, 3]);
});
});
}