195dbdf32Schristos /*
2*2d40c451Schristos * Copyright (c) 2020-2021 Yubico AB. All rights reserved.
395dbdf32Schristos * Use of this source code is governed by a BSD-style
495dbdf32Schristos * license that can be found in the LICENSE file.
5*2d40c451Schristos * SPDX-License-Identifier: BSD-2-Clause
695dbdf32Schristos */
795dbdf32Schristos
895dbdf32Schristos #include <assert.h>
995dbdf32Schristos #include <stdint.h>
1095dbdf32Schristos #include <stdlib.h>
1195dbdf32Schristos #include <string.h>
1295dbdf32Schristos #include <stdio.h>
1395dbdf32Schristos
1495dbdf32Schristos #include "../openbsd-compat/openbsd-compat.h"
1595dbdf32Schristos #include "mutator_aux.h"
16*2d40c451Schristos #include "dummy.h"
1795dbdf32Schristos
1895dbdf32Schristos extern int fido_hid_get_usage(const uint8_t *, size_t, uint32_t *);
1995dbdf32Schristos extern int fido_hid_get_report_len(const uint8_t *, size_t, size_t *, size_t *);
20ede6d7f8Schristos extern void set_udev_parameters(const char *, const struct blob *);
2195dbdf32Schristos
2295dbdf32Schristos struct param {
2395dbdf32Schristos int seed;
24ede6d7f8Schristos char uevent[MAXSTR];
2595dbdf32Schristos struct blob report_descriptor;
26*2d40c451Schristos struct blob netlink_wiredata;
2795dbdf32Schristos };
2895dbdf32Schristos
2995dbdf32Schristos /*
3095dbdf32Schristos * Sample HID report descriptor from the FIDO HID interface of a YubiKey 5.
3195dbdf32Schristos */
3295dbdf32Schristos static const uint8_t dummy_report_descriptor[] = {
3395dbdf32Schristos 0x06, 0xd0, 0xf1, 0x09, 0x01, 0xa1, 0x01, 0x09,
3495dbdf32Schristos 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08,
3595dbdf32Schristos 0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00,
3695dbdf32Schristos 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x40, 0x91,
3795dbdf32Schristos 0x02, 0xc0
3895dbdf32Schristos };
3995dbdf32Schristos
40ede6d7f8Schristos /*
41ede6d7f8Schristos * Sample uevent file from a Yubico Security Key.
42ede6d7f8Schristos */
43ede6d7f8Schristos static const char dummy_uevent[] =
44ede6d7f8Schristos "DRIVER=hid-generic\n"
45ede6d7f8Schristos "HID_ID=0003:00001050:00000120\n"
46ede6d7f8Schristos "HID_NAME=Yubico Security Key by Yubico\n"
47ede6d7f8Schristos "HID_PHYS=usb-0000:00:14.0-3/input0\n"
48ede6d7f8Schristos "HID_UNIQ=\n"
49ede6d7f8Schristos "MODALIAS=hid:b0003g0001v00001050p00000120\n";
50ede6d7f8Schristos
5195dbdf32Schristos struct param *
unpack(const uint8_t * ptr,size_t len)5295dbdf32Schristos unpack(const uint8_t *ptr, size_t len)
5395dbdf32Schristos {
5495dbdf32Schristos cbor_item_t *item = NULL, **v;
5595dbdf32Schristos struct cbor_load_result cbor;
5695dbdf32Schristos struct param *p;
5795dbdf32Schristos int ok = -1;
5895dbdf32Schristos
5995dbdf32Schristos if ((p = calloc(1, sizeof(*p))) == NULL ||
6095dbdf32Schristos (item = cbor_load(ptr, len, &cbor)) == NULL ||
6195dbdf32Schristos cbor.read != len ||
6295dbdf32Schristos cbor_isa_array(item) == false ||
6395dbdf32Schristos cbor_array_is_definite(item) == false ||
64*2d40c451Schristos cbor_array_size(item) != 4 ||
6595dbdf32Schristos (v = cbor_array_handle(item)) == NULL)
6695dbdf32Schristos goto fail;
6795dbdf32Schristos
6895dbdf32Schristos if (unpack_int(v[0], &p->seed) < 0 ||
69ede6d7f8Schristos unpack_string(v[1], p->uevent) < 0 ||
70*2d40c451Schristos unpack_blob(v[2], &p->report_descriptor) < 0 ||
71*2d40c451Schristos unpack_blob(v[3], &p->netlink_wiredata) < 0)
7295dbdf32Schristos goto fail;
7395dbdf32Schristos
7495dbdf32Schristos ok = 0;
7595dbdf32Schristos fail:
7695dbdf32Schristos if (ok < 0) {
7795dbdf32Schristos free(p);
7895dbdf32Schristos p = NULL;
7995dbdf32Schristos }
8095dbdf32Schristos
8195dbdf32Schristos if (item)
8295dbdf32Schristos cbor_decref(&item);
8395dbdf32Schristos
8495dbdf32Schristos return p;
8595dbdf32Schristos }
8695dbdf32Schristos
8795dbdf32Schristos size_t
pack(uint8_t * ptr,size_t len,const struct param * p)8895dbdf32Schristos pack(uint8_t *ptr, size_t len, const struct param *p)
8995dbdf32Schristos {
90*2d40c451Schristos cbor_item_t *argv[4], *array = NULL;
9195dbdf32Schristos size_t cbor_alloc_len, cbor_len = 0;
9295dbdf32Schristos unsigned char *cbor = NULL;
9395dbdf32Schristos
9495dbdf32Schristos memset(argv, 0, sizeof(argv));
9595dbdf32Schristos
96*2d40c451Schristos if ((array = cbor_new_definite_array(4)) == NULL ||
9795dbdf32Schristos (argv[0] = pack_int(p->seed)) == NULL ||
98ede6d7f8Schristos (argv[1] = pack_string(p->uevent)) == NULL ||
99*2d40c451Schristos (argv[2] = pack_blob(&p->report_descriptor)) == NULL ||
100*2d40c451Schristos (argv[3] = pack_blob(&p->netlink_wiredata)) == NULL)
10195dbdf32Schristos goto fail;
10295dbdf32Schristos
103*2d40c451Schristos for (size_t i = 0; i < 4; i++)
10495dbdf32Schristos if (cbor_array_push(array, argv[i]) == false)
10595dbdf32Schristos goto fail;
10695dbdf32Schristos
10795dbdf32Schristos if ((cbor_len = cbor_serialize_alloc(array, &cbor,
108*2d40c451Schristos &cbor_alloc_len)) == 0 || cbor_len > len) {
10995dbdf32Schristos cbor_len = 0;
11095dbdf32Schristos goto fail;
11195dbdf32Schristos }
11295dbdf32Schristos
11395dbdf32Schristos memcpy(ptr, cbor, cbor_len);
11495dbdf32Schristos fail:
115*2d40c451Schristos for (size_t i = 0; i < 4; i++)
11695dbdf32Schristos if (argv[i])
11795dbdf32Schristos cbor_decref(&argv[i]);
11895dbdf32Schristos
11995dbdf32Schristos if (array)
12095dbdf32Schristos cbor_decref(&array);
12195dbdf32Schristos
12295dbdf32Schristos free(cbor);
12395dbdf32Schristos
12495dbdf32Schristos return cbor_len;
12595dbdf32Schristos }
12695dbdf32Schristos
12795dbdf32Schristos size_t
pack_dummy(uint8_t * ptr,size_t len)12895dbdf32Schristos pack_dummy(uint8_t *ptr, size_t len)
12995dbdf32Schristos {
13095dbdf32Schristos struct param dummy;
131*2d40c451Schristos uint8_t blob[MAXCORPUS];
13295dbdf32Schristos size_t blob_len;
13395dbdf32Schristos
13495dbdf32Schristos memset(&dummy, 0, sizeof(dummy));
13595dbdf32Schristos
13695dbdf32Schristos dummy.report_descriptor.len = sizeof(dummy_report_descriptor);
137ede6d7f8Schristos strlcpy(dummy.uevent, dummy_uevent, sizeof(dummy.uevent));
13895dbdf32Schristos memcpy(&dummy.report_descriptor.body, &dummy_report_descriptor,
13995dbdf32Schristos dummy.report_descriptor.len);
140*2d40c451Schristos dummy.netlink_wiredata.len = sizeof(dummy_netlink_wiredata);
141*2d40c451Schristos memcpy(&dummy.netlink_wiredata.body, &dummy_netlink_wiredata,
142*2d40c451Schristos dummy.netlink_wiredata.len);
14395dbdf32Schristos
14495dbdf32Schristos assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
145ede6d7f8Schristos if (blob_len > len)
146ede6d7f8Schristos blob_len = len;
14795dbdf32Schristos
14895dbdf32Schristos memcpy(ptr, blob, blob_len);
14995dbdf32Schristos
15095dbdf32Schristos return blob_len;
15195dbdf32Schristos }
15295dbdf32Schristos
15395dbdf32Schristos static void
get_usage(const struct param * p)15495dbdf32Schristos get_usage(const struct param *p)
15595dbdf32Schristos {
15695dbdf32Schristos uint32_t usage_page = 0;
15795dbdf32Schristos
15895dbdf32Schristos fido_hid_get_usage(p->report_descriptor.body, p->report_descriptor.len,
15995dbdf32Schristos &usage_page);
16095dbdf32Schristos consume(&usage_page, sizeof(usage_page));
16195dbdf32Schristos }
16295dbdf32Schristos
16395dbdf32Schristos static void
get_report_len(const struct param * p)16495dbdf32Schristos get_report_len(const struct param *p)
16595dbdf32Schristos {
16695dbdf32Schristos size_t report_in_len = 0;
16795dbdf32Schristos size_t report_out_len = 0;
16895dbdf32Schristos
16995dbdf32Schristos fido_hid_get_report_len(p->report_descriptor.body,
17095dbdf32Schristos p->report_descriptor.len, &report_in_len, &report_out_len);
17195dbdf32Schristos consume(&report_in_len, sizeof(report_in_len));
17295dbdf32Schristos consume(&report_out_len, sizeof(report_out_len));
17395dbdf32Schristos }
17495dbdf32Schristos
175ede6d7f8Schristos static void
manifest(const struct param * p)176ede6d7f8Schristos manifest(const struct param *p)
177ede6d7f8Schristos {
178ede6d7f8Schristos size_t ndevs, nfound;
179*2d40c451Schristos fido_dev_info_t *devlist = NULL, *devlist_set = NULL;
180ede6d7f8Schristos int16_t vendor_id, product_id;
181*2d40c451Schristos fido_dev_io_t io;
182*2d40c451Schristos fido_dev_transport_t t;
183ede6d7f8Schristos
184*2d40c451Schristos memset(&io, 0, sizeof(io));
185*2d40c451Schristos memset(&t, 0, sizeof(t));
186*2d40c451Schristos set_netlink_io_functions(fd_read, fd_write);
187*2d40c451Schristos set_wire_data(p->netlink_wiredata.body, p->netlink_wiredata.len);
188ede6d7f8Schristos set_udev_parameters(p->uevent, &p->report_descriptor);
189*2d40c451Schristos
190ede6d7f8Schristos ndevs = uniform_random(64);
191ede6d7f8Schristos if ((devlist = fido_dev_info_new(ndevs)) == NULL ||
192*2d40c451Schristos (devlist_set = fido_dev_info_new(1)) == NULL ||
193ede6d7f8Schristos fido_dev_info_manifest(devlist, ndevs, &nfound) != FIDO_OK)
194ede6d7f8Schristos goto out;
195ede6d7f8Schristos for (size_t i = 0; i < nfound; i++) {
196ede6d7f8Schristos const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
197ede6d7f8Schristos consume_str(fido_dev_info_path(di));
198ede6d7f8Schristos consume_str(fido_dev_info_manufacturer_string(di));
199ede6d7f8Schristos consume_str(fido_dev_info_product_string(di));
200ede6d7f8Schristos vendor_id = fido_dev_info_vendor(di);
201ede6d7f8Schristos product_id = fido_dev_info_product(di);
202ede6d7f8Schristos consume(&vendor_id, sizeof(vendor_id));
203ede6d7f8Schristos consume(&product_id, sizeof(product_id));
204*2d40c451Schristos fido_dev_info_set(devlist_set, 0, fido_dev_info_path(di),
205*2d40c451Schristos fido_dev_info_manufacturer_string(di),
206*2d40c451Schristos fido_dev_info_product_string(di), &io, &t);
207ede6d7f8Schristos }
208ede6d7f8Schristos out:
209ede6d7f8Schristos fido_dev_info_free(&devlist, ndevs);
210*2d40c451Schristos fido_dev_info_free(&devlist_set, 1);
211ede6d7f8Schristos }
212ede6d7f8Schristos
21395dbdf32Schristos void
test(const struct param * p)21495dbdf32Schristos test(const struct param *p)
21595dbdf32Schristos {
21695dbdf32Schristos prng_init((unsigned int)p->seed);
217*2d40c451Schristos fuzz_clock_reset();
21895dbdf32Schristos fido_init(FIDO_DEBUG);
21995dbdf32Schristos fido_set_log_handler(consume_str);
22095dbdf32Schristos
22195dbdf32Schristos get_usage(p);
22295dbdf32Schristos get_report_len(p);
223ede6d7f8Schristos manifest(p);
22495dbdf32Schristos }
22595dbdf32Schristos
22695dbdf32Schristos void
mutate(struct param * p,unsigned int seed,unsigned int flags)22795dbdf32Schristos mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
22895dbdf32Schristos {
22995dbdf32Schristos if (flags & MUTATE_SEED)
23095dbdf32Schristos p->seed = (int)seed;
23195dbdf32Schristos
232ede6d7f8Schristos if (flags & MUTATE_PARAM) {
23395dbdf32Schristos mutate_blob(&p->report_descriptor);
234ede6d7f8Schristos mutate_string(p->uevent);
235ede6d7f8Schristos }
236*2d40c451Schristos
237*2d40c451Schristos if (flags & MUTATE_WIREDATA)
238*2d40c451Schristos mutate_blob(&p->netlink_wiredata);
23995dbdf32Schristos }
240