Skip to content

Commit 094137a

Browse files
committed
Redo accidental deletion of test file
1 parent b7b2231 commit 094137a

File tree

1 file changed

+286
-0
lines changed

1 file changed

+286
-0
lines changed

meesign_core/test/core_test.dart

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
@Timeout(Duration(seconds: 180))
2+
import 'dart:async';
3+
import 'dart:io' as io;
4+
import 'dart:io';
5+
import 'dart:math';
6+
7+
import 'package:collection/collection.dart';
8+
import 'package:meesign_core/meesign_core.dart';
9+
import 'package:test/test.dart';
10+
11+
import 'matcher.dart';
12+
13+
extension ListStream<T> on Stream<Iterable<T>> {
14+
Future<T> firstElement() async =>
15+
(await firstWhere((iter) => iter.isNotEmpty)).first;
16+
17+
Future<T> firstElementWhere(bool Function(T) test) =>
18+
map((iter) => iter.where(test)).firstElement();
19+
}
20+
21+
// TODO: use stream matchers?
22+
23+
Future<void> approveFirst(
24+
TaskRepository taskRepository,
25+
Device d, {
26+
bool agree = true,
27+
}) async {
28+
final t = await taskRepository.observeTasks(d.id).firstElement();
29+
await taskRepository.approveTask(d.id, t.id, agree: agree);
30+
}
31+
32+
Future<void> approveAllFirst(
33+
TaskRepository taskRepository,
34+
Iterable<Device> devices,
35+
) =>
36+
Future.wait(devices.map((d) => approveFirst(taskRepository, d)));
37+
38+
Future<void> verifyPdfSignature(String path) async {
39+
final res = await Process.run('pdfsig', ['-nocert', path]);
40+
expect(res.stdout, contains('Signature is Valid.'));
41+
}
42+
43+
const testFilePath = 'test/file.pdf';
44+
45+
final appDir = io.Directory('test/output');
46+
47+
void main() {
48+
late Database database;
49+
late KeyStore keyStore;
50+
late NetworkDispatcher dispatcher;
51+
late DeviceRepository deviceRepository;
52+
late GroupRepository groupRepository;
53+
late FileRepository fileRepository;
54+
late ChallengeRepository challengeRepository;
55+
late DecryptRepository decryptRepository;
56+
57+
List<int>? serverCerts;
58+
final String? serverCertsPath = io.Platform.environment['SERVER_CERTS'];
59+
final String? meesignServerDomain =
60+
io.Platform.environment['MEESIGN_SERVER_DOMAIN'];
61+
final String? meesignServerPort =
62+
io.Platform.environment['MEESIGN_SERVER_PORT'];
63+
64+
if (serverCertsPath != null) {
65+
serverCerts = io.File(serverCertsPath).readAsBytesSync();
66+
}
67+
68+
setUp(() {
69+
database = Database(appDir);
70+
keyStore = KeyStore(appDir);
71+
dispatcher = NetworkDispatcher(
72+
meesignServerDomain ?? "localhost",
73+
keyStore,
74+
serverCerts: serverCerts,
75+
allowBadCerts: serverCerts == null,
76+
// TODO is there a better way to set the default?
77+
port: int.tryParse(meesignServerPort ?? "") ?? 1337,
78+
);
79+
deviceRepository = DeviceRepository(
80+
dispatcher,
81+
keyStore,
82+
database.deviceDao,
83+
);
84+
final taskSource = TaskSource(dispatcher);
85+
final taskDao = database.taskDao;
86+
groupRepository = GroupRepository(
87+
dispatcher,
88+
keyStore,
89+
taskSource,
90+
taskDao,
91+
deviceRepository,
92+
);
93+
final fileStore = FileStore(appDir);
94+
fileRepository = FileRepository(
95+
dispatcher,
96+
keyStore,
97+
taskSource,
98+
taskDao,
99+
fileStore,
100+
);
101+
challengeRepository = ChallengeRepository(
102+
dispatcher,
103+
keyStore,
104+
taskSource,
105+
taskDao,
106+
);
107+
decryptRepository = DecryptRepository(
108+
dispatcher,
109+
keyStore,
110+
taskSource,
111+
taskDao,
112+
);
113+
});
114+
115+
Future<List<T>> testRepository<T>(
116+
TaskRepository<T> taskRepository,
117+
KeyType keyType,
118+
Protocol protocol, {
119+
int? n,
120+
List<int>? shares,
121+
required int t,
122+
required Future<void> Function(TaskRepository, Group) createTask,
123+
}) async {
124+
n ??= shares!.length;
125+
shares ??= List.filled(n, 1);
126+
127+
final ds = await Future.wait([
128+
for (var i = 0; i < n; ++i) deviceRepository.register('d$i'),
129+
]);
130+
131+
await Future.wait(ds.map((d) => groupRepository.subscribe(d.id)));
132+
final members = [for (final (i, d) in ds.indexed) Member(d, shares[i])];
133+
final sharesDesc = shares.any((value) => value > 1)
134+
? shares.join(' ')
135+
: shares.sum.toString();
136+
await groupRepository.group(
137+
'$t of $sharesDesc ${keyType.name} ${protocol.name}',
138+
members,
139+
t,
140+
protocol,
141+
keyType,
142+
);
143+
await approveAllFirst(groupRepository, ds);
144+
final gs = await Future.wait(
145+
ds.map((d) => groupRepository.observeGroups(d.id).firstElement()),
146+
);
147+
expect(gs.map((g) => g.id), allEqual);
148+
149+
await Future.wait(ds.map((d) => taskRepository.subscribe(d.id)));
150+
await createTask(taskRepository, gs.first);
151+
approveAllFirst(taskRepository, ds.take(t));
152+
return await Future.wait(
153+
ds.map((d) => taskRepository.observeResults(d.id).firstElement()),
154+
);
155+
}
156+
157+
Future<void> testSignPdf({int? n, List<int>? shares, required int t}) async {
158+
final files = await testRepository(
159+
fileRepository,
160+
KeyType.signPdf,
161+
Protocol.gg18,
162+
n: n,
163+
shares: shares,
164+
t: t,
165+
createTask: (_, Group g) async {
166+
final data = await io.File(testFilePath).readAsBytes();
167+
await fileRepository.sign('test.pdf', data, g.id);
168+
},
169+
);
170+
171+
for (var file in files) {
172+
await verifyPdfSignature(file.path);
173+
}
174+
}
175+
176+
Future<void> testSignChallenge(
177+
Protocol protocol, {
178+
int? n,
179+
List<int>? shares,
180+
required int t,
181+
}) async {
182+
final rng = Random();
183+
final message = List.generate(1024, (_) => rng.nextInt(256));
184+
185+
await testRepository(
186+
challengeRepository,
187+
KeyType.signChallenge,
188+
protocol,
189+
n: n,
190+
shares: shares,
191+
t: t,
192+
createTask: (_, Group g) async {
193+
await challengeRepository.sign('test challenge', message, g.id);
194+
},
195+
);
196+
197+
// TODO: verify signatures
198+
}
199+
200+
Future<void> testDecrypt({int? n, List<int>? shares, required int t}) async {
201+
final rng = Random();
202+
final message = List.generate(1024, (_) => rng.nextInt(256));
203+
204+
final decrypts = await testRepository(
205+
decryptRepository,
206+
KeyType.decrypt,
207+
Protocol.elgamal,
208+
n: n,
209+
shares: shares,
210+
t: t,
211+
createTask: (_, Group g) async {
212+
await decryptRepository.encrypt(
213+
'test secret',
214+
MimeType.octetStream,
215+
message,
216+
g.id,
217+
);
218+
},
219+
);
220+
final results = [for (var d in decrypts) d.data];
221+
222+
expect(results, allEqual);
223+
expect(results.first, equals(message));
224+
}
225+
226+
group('sign PDF', () {
227+
test('2-3', () => testSignPdf(n: 3, t: 2));
228+
test('3-3', () => testSignPdf(n: 3, t: 3));
229+
test('3-[1, 2, 3]', () => testSignPdf(shares: [1, 2, 3], t: 3));
230+
test('15-20', () => testSignPdf(n: 20, t: 15), tags: 'large');
231+
});
232+
233+
group('challenge', () {
234+
group('gg18', () {
235+
test('2-3', () => testSignChallenge(Protocol.gg18, n: 3, t: 2));
236+
test('3-3', () => testSignChallenge(Protocol.gg18, n: 3, t: 3));
237+
test(
238+
'3-[1, 2, 3]',
239+
() => testSignChallenge(Protocol.gg18, shares: [1, 2, 3], t: 3),
240+
);
241+
test(
242+
'15-20',
243+
() => testSignChallenge(Protocol.gg18, n: 20, t: 15),
244+
tags: 'large',
245+
);
246+
});
247+
group('frost', () {
248+
test('2-3', () => testSignChallenge(Protocol.frost, n: 3, t: 2));
249+
test('3-3', () => testSignChallenge(Protocol.frost, n: 3, t: 3));
250+
test(
251+
'3-[1, 2, 3]',
252+
() => testSignChallenge(Protocol.frost, shares: [1, 2, 3], t: 3),
253+
);
254+
test(
255+
'15-20',
256+
() => testSignChallenge(Protocol.frost, n: 20, t: 15),
257+
tags: 'large',
258+
);
259+
});
260+
group('musig2', () {
261+
test('2-2', () => testSignChallenge(Protocol.musig2, n: 2, t: 2));
262+
test('3-3', () => testSignChallenge(Protocol.musig2, n: 3, t: 3));
263+
test(
264+
'15-15',
265+
() => testSignChallenge(Protocol.musig2, n: 15, t: 15),
266+
tags: 'large',
267+
);
268+
});
269+
});
270+
271+
group('decrypt', () {
272+
test('2-3', () => testDecrypt(n: 3, t: 2));
273+
test('3-3', () => testDecrypt(n: 3, t: 3));
274+
test('3-[1, 2, 3]', () => testDecrypt(shares: [1, 2, 3], t: 3));
275+
test('15-20', () => testDecrypt(n: 20, t: 15), tags: 'large');
276+
});
277+
278+
tearDown(() async {
279+
try {
280+
// FIXME: not all db updates are written when the test finishes
281+
await Future.delayed(const Duration(milliseconds: 100));
282+
await database.close();
283+
appDir.deleteSync(recursive: true);
284+
} catch (_) {}
285+
});
286+
}

0 commit comments

Comments
 (0)