xref: /freebsd/contrib/libfido2/fuzz/fuzz_largeblob.c (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1 /*
2  * Copyright (c) 2020 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 "large blob" operation. */
21 struct param {
22 	char pin[MAXSTR];
23 	int seed;
24 	struct blob key;
25 	struct blob get_wiredata;
26 	struct blob set_wiredata;
27 };
28 
29 /*
30  * Collection of HID reports from an authenticator issued with a FIDO2
31  * 'authenticatorLargeBlobs' 'get' command.
32  */
33 static const uint8_t dummy_get_wiredata[] = {
34 	WIREDATA_CTAP_INIT,
35 	WIREDATA_CTAP_CBOR_INFO,
36 	WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY
37 };
38 
39 /*
40  * Collection of HID reports from an authenticator issued with a FIDO2
41  * 'authenticatorLargeBlobs' 'set' command.
42  */
43 static const uint8_t dummy_set_wiredata[] = {
44 	WIREDATA_CTAP_INIT,
45 	WIREDATA_CTAP_CBOR_INFO,
46 	WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY,
47 	WIREDATA_CTAP_CBOR_AUTHKEY,
48 	WIREDATA_CTAP_CBOR_PINTOKEN,
49 	WIREDATA_CTAP_CBOR_STATUS
50 };
51 
52 /*
53  * XXX this needs to match the encrypted blob embedded in
54  * WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY.
55  */
56 static const uint8_t dummy_key[] = {
57 	0xa9, 0x1b, 0xc4, 0xdd, 0xfc, 0x9a, 0x93, 0x79,
58 	0x75, 0xba, 0xf7, 0x7f, 0x4d, 0x57, 0xfc, 0xa6,
59 	0xe1, 0xf8, 0x06, 0x43, 0x23, 0x99, 0x51, 0x32,
60 	0xce, 0x6e, 0x19, 0x84, 0x50, 0x13, 0x2d, 0x7b
61 };
62 
63 struct param *
64 unpack(const uint8_t *ptr, size_t len)
65 {
66 	cbor_item_t *item = NULL, **v;
67 	struct cbor_load_result cbor;
68 	struct param *p;
69 	int ok = -1;
70 
71 	if ((p = calloc(1, sizeof(*p))) == NULL ||
72 	    (item = cbor_load(ptr, len, &cbor)) == NULL ||
73 	    cbor.read != len ||
74 	    cbor_isa_array(item) == false ||
75 	    cbor_array_is_definite(item) == false ||
76 	    cbor_array_size(item) != 5 ||
77 	    (v = cbor_array_handle(item)) == NULL)
78 		goto fail;
79 
80 	if (unpack_int(v[0], &p->seed) < 0 ||
81 	    unpack_string(v[1], p->pin) < 0 ||
82 	    unpack_blob(v[2], &p->key) < 0 ||
83 	    unpack_blob(v[3], &p->get_wiredata) < 0 ||
84 	    unpack_blob(v[4], &p->set_wiredata) < 0)
85 		goto fail;
86 
87 	ok = 0;
88 fail:
89 	if (ok < 0) {
90 		free(p);
91 		p = NULL;
92 	}
93 
94 	if (item)
95 		cbor_decref(&item);
96 
97 	return p;
98 }
99 
100 size_t
101 pack(uint8_t *ptr, size_t len, const struct param *p)
102 {
103 	cbor_item_t *argv[5], *array = NULL;
104 	size_t cbor_alloc_len, cbor_len = 0;
105 	unsigned char *cbor = NULL;
106 
107 	memset(argv, 0, sizeof(argv));
108 
109 	if ((array = cbor_new_definite_array(5)) == NULL ||
110 	    (argv[0] = pack_int(p->seed)) == NULL ||
111 	    (argv[1] = pack_string(p->pin)) == NULL ||
112 	    (argv[2] = pack_blob(&p->key)) == NULL ||
113 	    (argv[3] = pack_blob(&p->get_wiredata)) == NULL ||
114 	    (argv[4] = pack_blob(&p->set_wiredata)) == NULL)
115 		goto fail;
116 
117 	for (size_t i = 0; i < 5; i++)
118 		if (cbor_array_push(array, argv[i]) == false)
119 			goto fail;
120 
121 	if ((cbor_len = cbor_serialize_alloc(array, &cbor,
122 	    &cbor_alloc_len)) == 0 || cbor_len > len) {
123 		cbor_len = 0;
124 		goto fail;
125 	}
126 
127 	memcpy(ptr, cbor, cbor_len);
128 fail:
129 	for (size_t i = 0; i < 5; i++)
130 		if (argv[i])
131 			cbor_decref(&argv[i]);
132 
133 	if (array)
134 		cbor_decref(&array);
135 
136 	free(cbor);
137 
138 	return cbor_len;
139 }
140 
141 size_t
142 pack_dummy(uint8_t *ptr, size_t len)
143 {
144 	struct param dummy;
145 	uint8_t blob[MAXCORPUS];
146 	size_t blob_len;
147 
148 	memset(&dummy, 0, sizeof(dummy));
149 
150 	strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
151 
152 	dummy.get_wiredata.len = sizeof(dummy_get_wiredata);
153 	dummy.set_wiredata.len = sizeof(dummy_set_wiredata);
154 	dummy.key.len = sizeof(dummy_key);
155 
156 	memcpy(&dummy.get_wiredata.body, &dummy_get_wiredata,
157 	    dummy.get_wiredata.len);
158 	memcpy(&dummy.set_wiredata.body, &dummy_set_wiredata,
159 	    dummy.set_wiredata.len);
160 	memcpy(&dummy.key.body, &dummy_key, dummy.key.len);
161 
162 	assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
163 
164 	if (blob_len > len) {
165 		memcpy(ptr, blob, len);
166 		return len;
167 	}
168 
169 	memcpy(ptr, blob, blob_len);
170 
171 	return blob_len;
172 }
173 
174 static fido_dev_t *
175 prepare_dev(void)
176 {
177 	fido_dev_t *dev;
178 
179 	if ((dev = open_dev(0)) == NULL)
180 		return NULL;
181 
182 	return dev;
183 }
184 
185 static void
186 get_blob(const struct param *p, int array)
187 {
188 	fido_dev_t *dev;
189 	u_char *ptr = NULL;
190 	size_t len = 0;
191 
192 	set_wire_data(p->get_wiredata.body, p->get_wiredata.len);
193 
194 	if ((dev = prepare_dev()) == NULL)
195 		return;
196 
197 	if (array)
198 		fido_dev_largeblob_get_array(dev, &ptr, &len);
199 	else
200 		fido_dev_largeblob_get(dev, p->key.body, p->key.len, &ptr, &len);
201 	consume(ptr, len);
202 	free(ptr);
203 
204 	fido_dev_close(dev);
205 	fido_dev_free(&dev);
206 }
207 
208 
209 static void
210 set_blob(const struct param *p, int op)
211 {
212 	fido_dev_t *dev;
213 	const char *pin;
214 
215 	set_wire_data(p->set_wiredata.body, p->set_wiredata.len);
216 
217 	if ((dev = prepare_dev()) == NULL)
218 		return;
219 	pin = p->pin;
220 	if (strlen(pin) == 0)
221 		pin = NULL;
222 
223 	switch (op) {
224 	case 0:
225 		fido_dev_largeblob_remove(dev, p->key.body, p->key.len, pin);
226 		break;
227 	case 1:
228 		/* XXX reuse p->get_wiredata as the blob to be set */
229 		fido_dev_largeblob_set(dev, p->key.body, p->key.len,
230 		    p->get_wiredata.body, p->get_wiredata.len, pin);
231 		break;
232 	case 2:
233 		/* XXX reuse p->get_wiredata as the body of the cbor array */
234 		fido_dev_largeblob_set_array(dev, p->get_wiredata.body,
235 		    p->get_wiredata.len, pin);
236 	}
237 
238 	fido_dev_close(dev);
239 	fido_dev_free(&dev);
240 }
241 
242 void
243 test(const struct param *p)
244 {
245 	prng_init((unsigned int)p->seed);
246 	fuzz_clock_reset();
247 	fido_init(FIDO_DEBUG);
248 	fido_set_log_handler(consume_str);
249 
250 	get_blob(p, 0);
251 	get_blob(p, 1);
252 	set_blob(p, 0);
253 	set_blob(p, 1);
254 	set_blob(p, 2);
255 }
256 
257 void
258 mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
259 {
260 	if (flags & MUTATE_SEED)
261 		p->seed = (int)seed;
262 
263 	if (flags & MUTATE_PARAM) {
264 		mutate_blob(&p->key);
265 		mutate_string(p->pin);
266 	}
267 
268 	if (flags & MUTATE_WIREDATA) {
269 		mutate_blob(&p->get_wiredata);
270 		mutate_blob(&p->set_wiredata);
271 	}
272 }
273