1 /*
2  * Copyright (c) 2019-2021 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  */
6 
7 #include <assert.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include "mutator_aux.h"
14 #include "wiredata_fido2.h"
15 #include "dummy.h"
16 
17 #include "../openbsd-compat/openbsd-compat.h"
18 
19 /* Parameter set defining a FIDO2 credential management operation. */
20 struct param {
21 	char pin[MAXSTR];
22 	char rp_id[MAXSTR];
23 	int seed;
24 	struct blob cred_id;
25 	struct blob del_wire_data;
26 	struct blob meta_wire_data;
27 	struct blob rk_wire_data;
28 	struct blob rp_wire_data;
29 };
30 
31 /*
32  * Collection of HID reports from an authenticator issued with a FIDO2
33  * 'getCredsMetadata' credential management command.
34  */
35 static const uint8_t dummy_meta_wire_data[] = {
36 	WIREDATA_CTAP_INIT,
37 	WIREDATA_CTAP_CBOR_INFO,
38 	WIREDATA_CTAP_CBOR_AUTHKEY,
39 	WIREDATA_CTAP_CBOR_PINTOKEN,
40 	WIREDATA_CTAP_CBOR_CREDMAN_META,
41 };
42 
43 /*
44  * Collection of HID reports from an authenticator issued with a FIDO2
45  * 'enumerateRPsBegin' credential management command.
46  */
47 static const uint8_t dummy_rp_wire_data[] = {
48 	WIREDATA_CTAP_INIT,
49 	WIREDATA_CTAP_CBOR_INFO,
50 	WIREDATA_CTAP_CBOR_AUTHKEY,
51 	WIREDATA_CTAP_CBOR_PINTOKEN,
52 	WIREDATA_CTAP_CBOR_CREDMAN_RPLIST,
53 };
54 
55 /*
56  * Collection of HID reports from an authenticator issued with a FIDO2
57  * 'enumerateCredentialsBegin' credential management command.
58  */
59 static const uint8_t dummy_rk_wire_data[] = {
60 	WIREDATA_CTAP_INIT,
61 	WIREDATA_CTAP_CBOR_INFO,
62 	WIREDATA_CTAP_CBOR_AUTHKEY,
63 	WIREDATA_CTAP_CBOR_PINTOKEN,
64 	WIREDATA_CTAP_CBOR_CREDMAN_RKLIST,
65 };
66 
67 /*
68  * Collection of HID reports from an authenticator issued with a FIDO2
69  * 'deleteCredential' credential management command.
70  */
71 static const uint8_t dummy_del_wire_data[] = {
72 	WIREDATA_CTAP_INIT,
73 	WIREDATA_CTAP_CBOR_INFO,
74 	WIREDATA_CTAP_CBOR_AUTHKEY,
75 	WIREDATA_CTAP_CBOR_PINTOKEN,
76 	WIREDATA_CTAP_CBOR_STATUS,
77 };
78 
79 struct param *
80 unpack(const uint8_t *ptr, size_t len)
81 {
82 	cbor_item_t *item = NULL, **v;
83 	struct cbor_load_result cbor;
84 	struct param *p;
85 	int ok = -1;
86 
87 	if ((p = calloc(1, sizeof(*p))) == NULL ||
88 	    (item = cbor_load(ptr, len, &cbor)) == NULL ||
89 	    cbor.read != len ||
90 	    cbor_isa_array(item) == false ||
91 	    cbor_array_is_definite(item) == false ||
92 	    cbor_array_size(item) != 8 ||
93 	    (v = cbor_array_handle(item)) == NULL)
94 		goto fail;
95 
96 	if (unpack_int(v[0], &p->seed) < 0 ||
97 	    unpack_string(v[1], p->pin) < 0 ||
98 	    unpack_string(v[2], p->rp_id) < 0 ||
99 	    unpack_blob(v[3], &p->cred_id) < 0 ||
100 	    unpack_blob(v[4], &p->meta_wire_data) < 0 ||
101 	    unpack_blob(v[5], &p->rp_wire_data) < 0 ||
102 	    unpack_blob(v[6], &p->rk_wire_data) < 0 ||
103 	    unpack_blob(v[7], &p->del_wire_data) < 0)
104 		goto fail;
105 
106 	ok = 0;
107 fail:
108 	if (ok < 0) {
109 		free(p);
110 		p = NULL;
111 	}
112 
113 	if (item)
114 		cbor_decref(&item);
115 
116 	return p;
117 }
118 
119 size_t
120 pack(uint8_t *ptr, size_t len, const struct param *p)
121 {
122 	cbor_item_t *argv[8], *array = NULL;
123 	size_t cbor_alloc_len, cbor_len = 0;
124 	unsigned char *cbor = NULL;
125 
126 	memset(argv, 0, sizeof(argv));
127 
128 	if ((array = cbor_new_definite_array(8)) == NULL ||
129 	    (argv[0] = pack_int(p->seed)) == NULL ||
130 	    (argv[1] = pack_string(p->pin)) == NULL ||
131 	    (argv[2] = pack_string(p->rp_id)) == NULL ||
132 	    (argv[3] = pack_blob(&p->cred_id)) == NULL ||
133 	    (argv[4] = pack_blob(&p->meta_wire_data)) == NULL ||
134 	    (argv[5] = pack_blob(&p->rp_wire_data)) == NULL ||
135 	    (argv[6] = pack_blob(&p->rk_wire_data)) == NULL ||
136 	    (argv[7] = pack_blob(&p->del_wire_data)) == NULL)
137 		goto fail;
138 
139 	for (size_t i = 0; i < 8; i++)
140 		if (cbor_array_push(array, argv[i]) == false)
141 			goto fail;
142 
143 	if ((cbor_len = cbor_serialize_alloc(array, &cbor,
144 	    &cbor_alloc_len)) > len) {
145 		cbor_len = 0;
146 		goto fail;
147 	}
148 
149 	memcpy(ptr, cbor, cbor_len);
150 fail:
151 	for (size_t i = 0; i < 8; i++)
152 		if (argv[i])
153 			cbor_decref(&argv[i]);
154 
155 	if (array)
156 		cbor_decref(&array);
157 
158 	free(cbor);
159 
160 	return cbor_len;
161 }
162 
163 size_t
164 pack_dummy(uint8_t *ptr, size_t len)
165 {
166 	struct param dummy;
167 	uint8_t blob[4096];
168 	size_t blob_len;
169 
170 	memset(&dummy, 0, sizeof(dummy));
171 
172 	strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
173 	strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
174 
175 	dummy.meta_wire_data.len = sizeof(dummy_meta_wire_data);
176 	dummy.rp_wire_data.len = sizeof(dummy_rp_wire_data);
177 	dummy.rk_wire_data.len = sizeof(dummy_rk_wire_data);
178 	dummy.del_wire_data.len = sizeof(dummy_del_wire_data);
179 	dummy.cred_id.len = sizeof(dummy_cred_id);
180 
181 	memcpy(&dummy.meta_wire_data.body, &dummy_meta_wire_data,
182 	    dummy.meta_wire_data.len);
183 	memcpy(&dummy.rp_wire_data.body, &dummy_rp_wire_data,
184 	    dummy.rp_wire_data.len);
185 	memcpy(&dummy.rk_wire_data.body, &dummy_rk_wire_data,
186 	    dummy.rk_wire_data.len);
187 	memcpy(&dummy.del_wire_data.body, &dummy_del_wire_data,
188 	    dummy.del_wire_data.len);
189 	memcpy(&dummy.cred_id.body, &dummy_cred_id, dummy.cred_id.len);
190 
191 	assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
192 
193 	if (blob_len > len) {
194 		memcpy(ptr, blob, len);
195 		return len;
196 	}
197 
198 	memcpy(ptr, blob, blob_len);
199 
200 	return blob_len;
201 }
202 
203 static fido_dev_t *
204 prepare_dev(void)
205 {
206 	fido_dev_t *dev;
207 	bool x;
208 
209 	if ((dev = open_dev(0)) == NULL)
210 		return NULL;
211 
212 	x = fido_dev_is_fido2(dev);
213 	consume(&x, sizeof(x));
214 	x = fido_dev_supports_cred_prot(dev);
215 	consume(&x, sizeof(x));
216 	x = fido_dev_supports_credman(dev);
217 	consume(&x, sizeof(x));
218 
219 	return dev;
220 }
221 
222 static void
223 get_metadata(const struct param *p)
224 {
225 	fido_dev_t *dev;
226 	fido_credman_metadata_t *metadata;
227 	uint64_t existing;
228 	uint64_t remaining;
229 
230 	set_wire_data(p->meta_wire_data.body, p->meta_wire_data.len);
231 
232 	if ((dev = prepare_dev()) == NULL)
233 		return;
234 
235 	if ((metadata = fido_credman_metadata_new()) == NULL) {
236 		fido_dev_close(dev);
237 		fido_dev_free(&dev);
238 		return;
239 	}
240 
241 	fido_credman_get_dev_metadata(dev, metadata, p->pin);
242 
243 	existing = fido_credman_rk_existing(metadata);
244 	remaining = fido_credman_rk_remaining(metadata);
245 	consume(&existing, sizeof(existing));
246 	consume(&remaining, sizeof(remaining));
247 
248 	fido_credman_metadata_free(&metadata);
249 	fido_dev_close(dev);
250 	fido_dev_free(&dev);
251 }
252 
253 static void
254 get_rp_list(const struct param *p)
255 {
256 	fido_dev_t *dev;
257 	fido_credman_rp_t *rp;
258 
259 	set_wire_data(p->rp_wire_data.body, p->rp_wire_data.len);
260 
261 	if ((dev = prepare_dev()) == NULL)
262 		return;
263 
264 	if ((rp = fido_credman_rp_new()) == NULL) {
265 		fido_dev_close(dev);
266 		fido_dev_free(&dev);
267 		return;
268 	}
269 
270 	fido_credman_get_dev_rp(dev, rp, p->pin);
271 
272 	/* +1 on purpose */
273 	for (size_t i = 0; i < fido_credman_rp_count(rp) + 1; i++) {
274 		consume(fido_credman_rp_id_hash_ptr(rp, i),
275 		    fido_credman_rp_id_hash_len(rp, i));
276 		consume_str(fido_credman_rp_id(rp, i));
277 		consume_str(fido_credman_rp_name(rp, i));
278 	}
279 
280 	fido_credman_rp_free(&rp);
281 	fido_dev_close(dev);
282 	fido_dev_free(&dev);
283 }
284 
285 static void
286 get_rk_list(const struct param *p)
287 {
288 	fido_dev_t *dev;
289 	fido_credman_rk_t *rk;
290 	const fido_cred_t *cred;
291 	int val;
292 
293 	set_wire_data(p->rk_wire_data.body, p->rk_wire_data.len);
294 
295 	if ((dev = prepare_dev()) == NULL)
296 		return;
297 
298 	if ((rk = fido_credman_rk_new()) == NULL) {
299 		fido_dev_close(dev);
300 		fido_dev_free(&dev);
301 		return;
302 	}
303 
304 	fido_credman_get_dev_rk(dev, p->rp_id, rk, p->pin);
305 
306 	/* +1 on purpose */
307 	for (size_t i = 0; i < fido_credman_rk_count(rk) + 1; i++) {
308 		if ((cred = fido_credman_rk(rk, i)) == NULL) {
309 			assert(i >= fido_credman_rk_count(rk));
310 			continue;
311 		}
312 		val = fido_cred_type(cred);
313 		consume(&val, sizeof(val));
314 		consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
315 		consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred));
316 		consume(fido_cred_user_id_ptr(cred),
317 		    fido_cred_user_id_len(cred));
318 		consume_str(fido_cred_user_name(cred));
319 		consume_str(fido_cred_display_name(cred));
320 		val = fido_cred_prot(cred);
321 		consume(&val, sizeof(val));
322 	}
323 
324 	fido_credman_rk_free(&rk);
325 	fido_dev_close(dev);
326 	fido_dev_free(&dev);
327 }
328 
329 static void
330 del_rk(const struct param *p)
331 {
332 	fido_dev_t *dev;
333 
334 	set_wire_data(p->del_wire_data.body, p->del_wire_data.len);
335 
336 	if ((dev = prepare_dev()) == NULL)
337 		return;
338 
339 	fido_credman_del_dev_rk(dev, p->cred_id.body, p->cred_id.len, p->pin);
340 	fido_dev_close(dev);
341 	fido_dev_free(&dev);
342 }
343 
344 static void
345 set_rk(const struct param *p)
346 {
347 	fido_dev_t *dev = NULL;
348 	fido_cred_t *cred = NULL;
349 	const char *pin = p->pin;
350 	int r0, r1, r2;
351 
352 	set_wire_data(p->del_wire_data.body, p->del_wire_data.len);
353 
354 	if ((dev = prepare_dev()) == NULL)
355 		return;
356 	if ((cred = fido_cred_new()) == NULL)
357 		goto out;
358 	r0 = fido_cred_set_id(cred, p->cred_id.body, p->cred_id.len);
359 	r1 = fido_cred_set_user(cred, p->cred_id.body, p->cred_id.len, p->rp_id,
360 	    NULL, NULL);
361 	if (strlen(pin) == 0)
362 		pin = NULL;
363 	r2 = fido_credman_set_dev_rk(dev, cred, pin);
364 	consume(&r0, sizeof(r0));
365 	consume(&r1, sizeof(r1));
366 	consume(&r2, sizeof(r2));
367 out:
368 	fido_dev_close(dev);
369 	fido_dev_free(&dev);
370 	fido_cred_free(&cred);
371 }
372 
373 void
374 test(const struct param *p)
375 {
376 	prng_init((unsigned int)p->seed);
377 	fuzz_clock_reset();
378 	fido_init(FIDO_DEBUG);
379 	fido_set_log_handler(consume_str);
380 
381 	get_metadata(p);
382 	get_rp_list(p);
383 	get_rk_list(p);
384 	del_rk(p);
385 	set_rk(p);
386 }
387 
388 void
389 mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
390 {
391 	if (flags & MUTATE_SEED)
392 		p->seed = (int)seed;
393 
394 	if (flags & MUTATE_PARAM) {
395 		mutate_blob(&p->cred_id);
396 		mutate_string(p->pin);
397 		mutate_string(p->rp_id);
398 	}
399 
400 	if (flags & MUTATE_WIREDATA) {
401 		mutate_blob(&p->meta_wire_data);
402 		mutate_blob(&p->rp_wire_data);
403 		mutate_blob(&p->rk_wire_data);
404 		mutate_blob(&p->del_wire_data);
405 	}
406 }
407