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