xref: /freebsd/contrib/libfido2/fuzz/fuzz_pcsc.c (revision 61e21613)
1 /*
2  * Copyright (c) 2022 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 #define _FIDO_INTERNAL
9 
10 #include <assert.h>
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <winscard.h>
16 
17 #include "mutator_aux.h"
18 #include "wiredata_fido2.h"
19 #include "dummy.h"
20 
21 #include "../src/extern.h"
22 
23 struct param {
24 	int seed;
25 	char path[MAXSTR];
26 	struct blob pcsc_list;
27 	struct blob tx_apdu;
28 	struct blob wiredata_init;
29 	struct blob wiredata_msg;
30 };
31 
32 static const uint8_t dummy_tx_apdu[] = { WIREDATA_CTAP_EXTENDED_APDU };
33 static const uint8_t dummy_wiredata_init[] = { WIREDATA_CTAP_NFC_INIT };
34 static const uint8_t dummy_wiredata_msg[] = { WIREDATA_CTAP_NFC_MSG };
35 
36 struct param *
37 unpack(const uint8_t *ptr, size_t len)
38 {
39 	cbor_item_t *item = NULL, **v;
40 	struct cbor_load_result cbor;
41 	struct param *p;
42 	int ok = -1;
43 
44 	if ((p = calloc(1, sizeof(*p))) == NULL ||
45 	    (item = cbor_load(ptr, len, &cbor)) == NULL ||
46 	    cbor.read != len ||
47 	    cbor_isa_array(item) == false ||
48 	    cbor_array_is_definite(item) == false ||
49 	    cbor_array_size(item) != 6 ||
50 	    (v = cbor_array_handle(item)) == NULL)
51 		goto fail;
52 
53 	if (unpack_int(v[0], &p->seed) < 0 ||
54 	    unpack_string(v[1], p->path) < 0 ||
55 	    unpack_blob(v[2], &p->pcsc_list) < 0 ||
56 	    unpack_blob(v[3], &p->tx_apdu) < 0 ||
57 	    unpack_blob(v[4], &p->wiredata_init) < 0 ||
58 	    unpack_blob(v[5], &p->wiredata_msg) < 0)
59 		goto fail;
60 
61 	ok = 0;
62 fail:
63 	if (ok < 0) {
64 		free(p);
65 		p = NULL;
66 	}
67 
68 	if (item)
69 		cbor_decref(&item);
70 
71 	return p;
72 }
73 
74 size_t
75 pack(uint8_t *ptr, size_t len, const struct param *p)
76 {
77 	cbor_item_t *argv[6], *array = NULL;
78 	size_t cbor_alloc_len, cbor_len = 0;
79 	unsigned char *cbor = NULL;
80 
81 	memset(argv, 0, sizeof(argv));
82 
83 	if ((array = cbor_new_definite_array(6)) == NULL ||
84 	    (argv[0] = pack_int(p->seed)) == NULL ||
85 	    (argv[1] = pack_string(p->path)) == NULL ||
86 	    (argv[2] = pack_blob(&p->pcsc_list)) == NULL ||
87 	    (argv[3] = pack_blob(&p->tx_apdu)) == NULL ||
88 	    (argv[4] = pack_blob(&p->wiredata_init)) == NULL ||
89 	    (argv[5] = pack_blob(&p->wiredata_msg)) == NULL)
90 		goto fail;
91 
92 	for (size_t i = 0; i < 6; i++)
93 		if (cbor_array_push(array, argv[i]) == false)
94 			goto fail;
95 
96 	if ((cbor_len = cbor_serialize_alloc(array, &cbor,
97 	    &cbor_alloc_len)) == 0 || cbor_len > len) {
98 		cbor_len = 0;
99 		goto fail;
100 	}
101 
102 	memcpy(ptr, cbor, cbor_len);
103 fail:
104 	for (size_t i = 0; i < 6; i++)
105 		if (argv[i])
106 			cbor_decref(&argv[i]);
107 
108 	if (array)
109 		cbor_decref(&array);
110 
111 	free(cbor);
112 
113 	return cbor_len;
114 }
115 
116 size_t
117 pack_dummy(uint8_t *ptr, size_t len)
118 {
119 	struct param dummy;
120 	uint8_t	blob[MAXCORPUS];
121 	size_t blob_len;
122 
123 	memset(&dummy, 0, sizeof(dummy));
124 
125 	strlcpy(dummy.path, dummy_pcsc_path, sizeof(dummy.path));
126 
127 	dummy.pcsc_list.len = sizeof(dummy_pcsc_list);
128 	memcpy(&dummy.pcsc_list.body, &dummy_pcsc_list, dummy.pcsc_list.len);
129 
130 	dummy.tx_apdu.len = sizeof(dummy_tx_apdu);
131 	memcpy(&dummy.tx_apdu.body, &dummy_tx_apdu, dummy.tx_apdu.len);
132 
133 	dummy.wiredata_init.len = sizeof(dummy_wiredata_init);
134 	memcpy(&dummy.wiredata_init.body, &dummy_wiredata_init,
135 	    dummy.wiredata_init.len);
136 
137 	dummy.wiredata_msg.len = sizeof(dummy_wiredata_msg);
138 	memcpy(&dummy.wiredata_msg.body, &dummy_wiredata_msg,
139 	    dummy.wiredata_msg.len);
140 
141 	assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
142 
143 	if (blob_len > len) {
144 		memcpy(ptr, blob, len);
145 		return len;
146 	}
147 
148 	memcpy(ptr, blob, blob_len);
149 
150 	return blob_len;
151 }
152 
153 static void
154 test_manifest(void)
155 {
156 	size_t ndevs, nfound;
157 	fido_dev_info_t *devlist = NULL;
158 	int16_t vendor_id, product_id;
159 	int r;
160 
161 	r = fido_pcsc_manifest(NULL, 0, &nfound);
162 	assert(r == FIDO_OK && nfound == 0);
163 	r = fido_pcsc_manifest(NULL, 1, &nfound);
164 	assert(r == FIDO_ERR_INVALID_ARGUMENT);
165 
166 	ndevs = uniform_random(64);
167 	if ((devlist = fido_dev_info_new(ndevs)) == NULL ||
168 	    fido_pcsc_manifest(devlist, ndevs, &nfound) != FIDO_OK)
169 		goto out;
170 
171 	for (size_t i = 0; i < nfound; i++) {
172 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
173 		consume_str(fido_dev_info_path(di));
174 		consume_str(fido_dev_info_manufacturer_string(di));
175 		consume_str(fido_dev_info_product_string(di));
176 		vendor_id = fido_dev_info_vendor(di);
177 		product_id = fido_dev_info_product(di);
178 		consume(&vendor_id, sizeof(vendor_id));
179 		consume(&product_id, sizeof(product_id));
180 	}
181 
182 out:
183 	fido_dev_info_free(&devlist, ndevs);
184 }
185 
186 static void
187 test_tx(const char *path, const struct blob *apdu, uint8_t cmd, u_char *rx_buf,
188     size_t rx_len)
189 {
190 	fido_dev_t dev;
191 	const u_char *tx_ptr = NULL;
192 	size_t tx_len = 0;
193 	int n;
194 
195 	memset(&dev, 0, sizeof(dev));
196 
197 	if (fido_dev_set_pcsc(&dev) < 0)
198 		return;
199 	if ((dev.io_handle = fido_pcsc_open(path)) == NULL)
200 		return;
201 
202 	if (apdu) {
203 		tx_ptr = apdu->body;
204 		tx_len = apdu->len;
205 	}
206 
207 	fido_pcsc_tx(&dev, cmd, tx_ptr, tx_len);
208 
209 	if ((n = fido_pcsc_rx(&dev, cmd, rx_buf, rx_len, -1)) >= 0)
210 		consume(rx_buf, n);
211 
212 	fido_pcsc_close(dev.io_handle);
213 }
214 
215 static void
216 test_misc(void)
217 {
218 	assert(fido_pcsc_open(NULL) == NULL);
219 	assert(fido_pcsc_write(NULL, NULL, INT_MAX + 1LL) == -1);
220 }
221 
222 void
223 test(const struct param *p)
224 {
225 	u_char buf[512];
226 
227 	prng_init((unsigned int)p->seed);
228 	fuzz_clock_reset();
229 	fido_init(FIDO_DEBUG);
230 	fido_set_log_handler(consume_str);
231 
232 	set_pcsc_parameters(&p->pcsc_list);
233 	set_pcsc_io_functions(nfc_read, nfc_write, consume);
234 
235 	set_wire_data(p->wiredata_init.body, p->wiredata_init.len);
236 	test_manifest();
237 
238 	test_misc();
239 
240 	set_wire_data(p->wiredata_init.body, p->wiredata_init.len);
241 	test_tx(p->path, NULL, CTAP_CMD_INIT, buf, uniform_random(20));
242 
243 	set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len);
244 	test_tx(p->path, &p->tx_apdu, CTAP_CMD_MSG, buf, sizeof(buf));
245 
246 	set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len);
247 	test_tx(p->path, &p->tx_apdu, CTAP_CMD_CBOR, buf, sizeof(buf));
248 
249 	set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len);
250 	test_tx(p->path, &p->tx_apdu, CTAP_CMD_LOCK, buf, sizeof(buf));
251 }
252 
253 void
254 mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
255 {
256 	if (flags & MUTATE_SEED)
257 		p->seed = (int)seed;
258 
259 	if (flags & MUTATE_PARAM) {
260 		mutate_string(p->path);
261 		mutate_blob(&p->pcsc_list);
262 		mutate_blob(&p->tx_apdu);
263 	}
264 
265 	if (flags & MUTATE_WIREDATA) {
266 		mutate_blob(&p->wiredata_init);
267 		mutate_blob(&p->wiredata_msg);
268 	}
269 }
270