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