1 /*
2 * Testing and fuzzing harness for the A12 implementation
3 */
4 #include <arcan_shmif.h>
5 #include <arcan_shmif_server.h>
6 #include <arcan/a12.h>
7
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <fcntl.h>
11
12 #include <errno.h>
13 #include <unistd.h>
14 #include <poll.h>
15 #include <assert.h>
16
17 extern void arcan_random(uint8_t*, size_t);
18 #define clsrv_okstate() (a12_poll(cl) != -1 && a12_poll(srv) != -1)
19
20 static uint8_t clpriv[32];
21 static uint8_t srvpriv[32];
22
key_auth_cl(uint8_t pk[static32])23 static struct pk_response key_auth_cl(uint8_t pk[static 32])
24 {
25 /* don't really care for the time being, just return a key */
26 struct pk_response auth;
27 auth.authentic = true;
28 memcpy(auth.key, clpriv, 32);
29 return auth;
30 }
31
key_auth_srv(uint8_t pk[static32])32 static struct pk_response key_auth_srv(uint8_t pk[static 32])
33 {
34 struct pk_response auth;
35 auth.authentic = true;
36 memcpy(auth.key, srvpriv, 32);
37 return auth;
38 }
39
40 extern void arcan_random(uint8_t* buf, size_t buf_sz);
41
data_round_corrupt(struct a12_state * cl,struct a12_state * srv,bool cl_round)42 static void data_round_corrupt(
43 struct a12_state* cl, struct a12_state* srv, bool cl_round)
44 {
45 uint8_t* buf;
46 struct a12_state* src, (* dst);
47
48 if (cl_round){
49 src = cl;
50 dst = srv;
51 }
52 else {
53 src = srv;
54 dst = cl;
55 }
56
57 size_t out = a12_flush(src, &buf, 0);
58
59 if (out){
60 arcan_random(buf, 8);
61 a12_unpack(dst, buf, out, NULL, NULL);
62 }
63 }
64
data_round(struct a12_state * cl,struct a12_state * srv,bool cl_round)65 static size_t data_round(
66 struct a12_state* cl, struct a12_state* srv, bool cl_round)
67 {
68 uint8_t* buf;
69 struct a12_state* src, (* dst);
70 if (!clsrv_okstate())
71 return 0;
72
73 if (cl_round){
74 src = cl;
75 dst = srv;
76 }
77 else {
78 src = srv;
79 dst = cl;
80 }
81
82 size_t out = a12_flush(src, &buf, 0);
83
84 if (out)
85 a12_unpack(dst, buf, out, NULL, NULL);
86
87 return out;
88 }
89
90 #define FLUSH(X, Y) do {\
91 for(;data_round((X), (Y), false) || data_round((X), (Y), true);){}\
92 } while(0)
93
run_auth_test(struct a12_state * cl,struct a12_state * srv)94 static bool run_auth_test(struct a12_state* cl, struct a12_state* srv)
95 {
96 int s1, s2;
97 bool cl_round = true;
98
99 /* loop while there is still data to be pumped and authentication is still going */
100 do {
101 data_round(cl, srv, cl_round);
102 s1 = a12_poll(cl);
103 s2 = a12_poll(srv);
104 if (s1 == -1 || s2 == -1)
105 break;
106
107 cl_round = !cl_round;
108 } while (s1 > 0 || s2 > 0 || !a12_auth_state(cl) || !a12_auth_state(srv));
109
110 a12int_trace(A12_TRACE_SYSTEM, "auth-over:client=%d:server=%d", s1, s2);
111 return a12_auth_state(cl) && a12_auth_state(srv);
112 }
113
114 /* send a few thousand events back and forth */
event_test(struct a12_state * cl,struct a12_state * srv)115 static bool event_test(struct a12_state* cl, struct a12_state* srv)
116 {
117 size_t i = 0;
118 for (; i < 1000 && clsrv_okstate(); i++){
119 struct arcan_event ev = {
120 .category = EVENT_TARGET,
121 .tgt.kind = TARGET_COMMAND_RESET,
122 .tgt.ioevs[0].uiv = i
123 };
124
125 a12_channel_enqueue(cl, &ev);
126 a12_channel_enqueue(srv, &ev);
127
128 FLUSH(cl, srv);
129 }
130
131 return i == 1000;
132 }
133
134 struct video_tag {
135 shmif_pixel* buffer;
136 size_t buf_n_px;
137 size_t w;
138 bool match;
139 shmif_pixel* srv_buf;
140 };
141
video_signal_raw(size_t x1,size_t y1,size_t x2,size_t y2,void * tag)142 static void video_signal_raw(
143 size_t x1, size_t y1, size_t x2, size_t y2, void* tag)
144 {
145 struct video_tag* data = tag;
146 assert(data->srv_buf);
147 data->match = true;
148
149 for (size_t y = y1; y < y2; y++){
150 for (size_t x = x1; x < x2; x++){
151 size_t ofs = y * data->w + x;
152 assert(ofs < data->buf_n_px);
153 if (data->buffer[ofs] != data->srv_buf[ofs]){
154 data->match = false;
155 break;
156 }
157 }
158 }
159 }
160
video_signal_alloc(size_t w,size_t h,size_t * stride,int fl,void * tag)161 static shmif_pixel* video_signal_alloc(
162 size_t w, size_t h, size_t* stride, int fl, void* tag)
163 {
164 struct video_tag* data = tag;
165 if (data->srv_buf)
166 free(data->srv_buf);
167
168 assert(w == data->w);
169
170 *stride = sizeof(shmif_pixel) * w;
171 size_t buf_sz = *stride * h;
172 data->srv_buf = malloc(buf_sz);
173 return data->srv_buf;
174 }
175
video_test_raw(struct a12_state * cl,struct a12_state * srv)176 static bool video_test_raw(struct a12_state* cl, struct a12_state* srv)
177 {
178 /* deliberately !%2 */
179 size_t w = 513;
180 size_t h = 513;
181 size_t buf_sz = w * h * sizeof(shmif_pixel);
182 struct video_tag tag =
183 {
184 .buffer = malloc(buf_sz),
185 .buf_n_px = w * h,
186 .w = w,
187 .match = true
188 };
189
190 a12_set_destination_raw(srv, 0,
191 (struct a12_unpack_cfg){
192 .tag = &tag,
193 .signal_video = video_signal_raw,
194 .request_raw_buffer = video_signal_alloc,
195 }, sizeof(struct a12_unpack_cfg)
196 );
197
198 for (size_t i = 0; i < 10 && clsrv_okstate(); i++){
199 /* update buffer */
200 // arcan_random((uint8_t*)tag.buffer, buf_sz);
201 memset(tag.buffer, 0xff, buf_sz);
202
203 if (!tag.match)
204 break;
205
206 a12_channel_vframe(cl,
207 &(struct shmifsrv_vbuffer){
208 .buffer = tag.buffer,
209 .w = w,
210 .h = h,
211 .pitch = w,
212 .stride = w * sizeof(shmif_pixel),
213 },
214 (struct a12_vframe_opts){
215 });
216
217 FLUSH(cl, srv);
218 }
219
220 free(tag.buffer);
221 free(tag.srv_buf);
222 a12_set_destination_raw(srv, 0,
223 (struct a12_unpack_cfg){}, sizeof(struct a12_unpack_cfg));
224
225 return tag.match && clsrv_okstate();
226 }
227
228 struct audio_tag {
229 shmif_asample* buffer;
230 size_t buf_sz;
231 size_t n_ch;
232 size_t srate;
233 bool match;
234 };
235
audio_signal_raw(size_t bytes,void * tag)236 static void audio_signal_raw(size_t bytes, void* tag)
237 {
238 // struct audio_tag* buf = tag;
239
240 /* use first n- bytes from the buffer - check that they follow our 'signal' */
241 }
242
audio_buffer_alloc(size_t n_ch,size_t samplerate,size_t bytes,void * tag)243 static shmif_asample* audio_buffer_alloc(
244 size_t n_ch, size_t samplerate, size_t bytes, void* tag)
245 {
246 struct audio_tag* at = tag;
247 if (!at->buffer)
248 at->buffer = NULL;
249
250 at->n_ch = n_ch;
251 at->srate = samplerate;
252 at->buffer = malloc(bytes);
253
254 return at->buffer;
255 }
256
audio_test_raw(struct a12_state * cl,struct a12_state * srv)257 static bool audio_test_raw(struct a12_state* cl, struct a12_state* srv)
258 {
259 /* deliberately !%2 */
260 struct audio_tag tag =
261 {
262 .buffer = NULL,
263 .match = true
264 };
265
266 a12_set_destination_raw(srv, 0,
267 (struct a12_unpack_cfg){
268 .tag = &tag,
269 .signal_audio = audio_signal_raw,
270 .request_audio_buffer = audio_buffer_alloc,
271 }, sizeof(struct a12_unpack_cfg)
272 );
273
274 for (size_t i = 0; i < 10 &&
275 a12_poll(srv) != -1 && a12_poll(cl) != -1 && tag.match; i++){
276
277 /* send abuffer */
278 FLUSH(cl, srv);
279 }
280
281 free(tag.buffer);
282 a12_set_destination_raw(srv, 0,
283 (struct a12_unpack_cfg){}, sizeof(struct a12_unpack_cfg));
284
285 return tag.match && (a12_poll(cl) != -1 && a12_poll(srv) != -1);
286 }
287
288 struct test_pass {
289 bool (*pass)(struct a12_state*, struct a12_state*);
290 const char* name;
291 bool ignore;
292 };
293
294 /* normally possible with multiples, but not for this test */
295 struct blob_md {
296 int got_job;
297 };
298
bhandler(struct a12_state * S,struct a12_bhandler_meta md,void * tag)299 static struct a12_bhandler_res bhandler(
300 struct a12_state* S, struct a12_bhandler_meta md, void* tag)
301 {
302 struct blob_md* bmd = tag;
303
304 struct a12_bhandler_res res = {
305 .flag = A12_BHANDLER_DONTWANT,
306 .fd = -1
307 };
308
309 /* status update? */
310 if (bmd->got_job){
311 return res;
312 }
313
314 if (!md.known_size)
315 return res;
316
317 if (md.streaming)
318 return res;
319
320 a12int_trace(A12_TRACE_BTRANSFER, "new_transfer");
321 return res;
322 }
323
test_bxfer(struct a12_state * cl,struct a12_state * srv)324 static bool test_bxfer(struct a12_state* cl, struct a12_state* srv)
325 {
326 struct blob_md blob;
327
328 /* increment each time the test is run up to a cap */
329 static size_t base_sz = 1024;
330 if (base_sz < 10 * 1024)
331 base_sz *= 2;
332
333 FILE* fpek = fopen("bxfer.temp", "w+");
334 if (!fpek)
335 return false;
336
337 unlink("bxfer.temp");
338
339 char* buf = malloc(base_sz);
340 memset(buf, 'a', base_sz);
341 fwrite(buf, base_sz, 1, fpek);
342
343 int myfd = fileno(fpek);
344
345 a12_set_bhandler(srv, bhandler, &blob);
346
347 /* send same file twice, the second time we should be able to just reject */
348 for (size_t i = 0; i < 2 && a12_poll(cl) != -1 && a12_poll(srv) != -1; i++){
349 a12_enqueue_bstream(cl, myfd, A12_BTYPE_BLOB, false, base_sz);
350 FLUSH(cl, srv);
351 }
352
353 fclose(fpek);
354 a12_set_bhandler(srv, NULL, NULL);
355 return true;
356 }
357
buffer_sink(uint8_t * buf,size_t nb,void * tag)358 static bool buffer_sink(uint8_t* buf, size_t nb, void* tag)
359 {
360 struct a12_state* dst = tag;
361 a12_unpack(dst, buf, nb, NULL, NULL);
362 return a12_poll(dst) != -1;
363 }
364
main(int argc,char ** argv)365 int main(int argc, char** argv)
366 {
367 arcan_random(clpriv, 32);
368 arcan_random(srvpriv, 32);
369
370 struct a12_context_options cl_opts = {
371 .pk_lookup = key_auth_cl,
372 .disable_cipher = true,
373 .disable_ephemeral_k = false
374 };
375
376
377 struct a12_context_options srv_opts = cl_opts;
378 memcpy(cl_opts.priv_key, clpriv, 32);
379 srv_opts.pk_lookup = key_auth_srv;
380
381 /* parse arguments from cmdline, ... */
382 a12_set_trace_level(
383 A12_TRACE_CRYPTO |
384 A12_TRACE_SYSTEM |
385 A12_TRACE_VIDEO |
386 A12_TRACE_BTRANSFER |
387 A12_TRACE_AUDIO |
388 0,
389 stderr
390 );
391
392 /*
393 * sink- based data transfer (FOR ONE CONTEXT) is easier to debug
394 * as the packet that caused an issue will have its synthesis path
395 * fresh in the backtrace. NEVER do this in process on BOTH.
396 */
397 struct a12_state* srv = a12_server(&srv_opts);
398
399 /* cl_opts.sink = buffer_sink;
400 cl_opts.sink_tag = srv;
401 */
402 struct a12_state* cl = a12_client(&cl_opts);
403
404 /* authentication is always needed */
405 printf("authenticating: ");
406 if (!run_auth_test(cl, srv)){
407 printf(" fail\n");
408 return EXIT_FAILURE;
409 }
410 printf(" ok\n");
411
412 struct test_pass passes[] = {
413 {
414 .pass = event_test,
415 .name = "Event",
416 },
417 {
418 .pass = video_test_raw,
419 .name = "Video(Raw)",
420 },
421 {
422 .pass = audio_test_raw,
423 .name = "Audio(Raw)",
424 .ignore = true
425 },
426 {
427 .pass = test_bxfer,
428 .name = "Binary",
429 .ignore = true
430 }
431 /* checklist:
432 * - working audio
433 * - working bchunk
434 * - bchunk-handler stream-cancel (caching)
435 *
436 * - working 1-round x25519
437 * - working 2-round x25519
438 *
439 * - video-encode cancel-fallback
440 * - video-encode passthrough
441 *
442 * 9. working x264 passthrough and fallback
443 * 10. working subchannel alloc
444 * 11. working output segment
445 * 12. working rekey
446 */
447 };
448
449 size_t pc = 0;
450 for(;;){
451 for (size_t i = 0; i < sizeof(passes) / sizeof(passes[0]); i++){
452 struct test_pass* ct = &passes[i];
453 if (ct->ignore)
454 continue;
455 bool pass = ct->pass(cl, srv);
456 printf("[%zu] %s - %s\n", pc, ct->name, pass ? "ok" : "fail");
457 if (!pass)
458 return EXIT_FAILURE;
459 }
460 pc++;
461 }
462 return EXIT_SUCCESS;
463 }
464