summaryrefslogtreecommitdiffstats
path: root/fuzz/fuzz_pcsc.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--fuzz/fuzz_pcsc.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/fuzz/fuzz_pcsc.c b/fuzz/fuzz_pcsc.c
new file mode 100644
index 0000000..f0f6cca
--- /dev/null
+++ b/fuzz/fuzz_pcsc.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2022 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#define _FIDO_INTERNAL
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <winscard.h>
+
+#include "mutator_aux.h"
+#include "wiredata_fido2.h"
+#include "dummy.h"
+
+#include "../src/extern.h"
+
+struct param {
+ int seed;
+ char path[MAXSTR];
+ struct blob pcsc_list;
+ struct blob tx_apdu;
+ struct blob wiredata_init;
+ struct blob wiredata_msg;
+};
+
+static const uint8_t dummy_tx_apdu[] = { WIREDATA_CTAP_EXTENDED_APDU };
+static const uint8_t dummy_wiredata_init[] = { WIREDATA_CTAP_NFC_INIT };
+static const uint8_t dummy_wiredata_msg[] = { WIREDATA_CTAP_NFC_MSG };
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 6 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_int(v[0], &p->seed) < 0 ||
+ unpack_string(v[1], p->path) < 0 ||
+ unpack_blob(v[2], &p->pcsc_list) < 0 ||
+ unpack_blob(v[3], &p->tx_apdu) < 0 ||
+ unpack_blob(v[4], &p->wiredata_init) < 0 ||
+ unpack_blob(v[5], &p->wiredata_msg) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[6], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(6)) == NULL ||
+ (argv[0] = pack_int(p->seed)) == NULL ||
+ (argv[1] = pack_string(p->path)) == NULL ||
+ (argv[2] = pack_blob(&p->pcsc_list)) == NULL ||
+ (argv[3] = pack_blob(&p->tx_apdu)) == NULL ||
+ (argv[4] = pack_blob(&p->wiredata_init)) == NULL ||
+ (argv[5] = pack_blob(&p->wiredata_msg)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 6; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 6; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[MAXCORPUS];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ strlcpy(dummy.path, dummy_pcsc_path, sizeof(dummy.path));
+
+ dummy.pcsc_list.len = sizeof(dummy_pcsc_list);
+ memcpy(&dummy.pcsc_list.body, &dummy_pcsc_list, dummy.pcsc_list.len);
+
+ dummy.tx_apdu.len = sizeof(dummy_tx_apdu);
+ memcpy(&dummy.tx_apdu.body, &dummy_tx_apdu, dummy.tx_apdu.len);
+
+ dummy.wiredata_init.len = sizeof(dummy_wiredata_init);
+ memcpy(&dummy.wiredata_init.body, &dummy_wiredata_init,
+ dummy.wiredata_init.len);
+
+ dummy.wiredata_msg.len = sizeof(dummy_wiredata_msg);
+ memcpy(&dummy.wiredata_msg.body, &dummy_wiredata_msg,
+ dummy.wiredata_msg.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+
+ if (blob_len > len) {
+ memcpy(ptr, blob, len);
+ return len;
+ }
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+static void
+test_manifest(void)
+{
+ size_t ndevs, nfound;
+ fido_dev_info_t *devlist = NULL;
+ int16_t vendor_id, product_id;
+ int r;
+
+ r = fido_pcsc_manifest(NULL, 0, &nfound);
+ assert(r == FIDO_OK && nfound == 0);
+ r = fido_pcsc_manifest(NULL, 1, &nfound);
+ assert(r == FIDO_ERR_INVALID_ARGUMENT);
+
+ ndevs = uniform_random(64);
+ if ((devlist = fido_dev_info_new(ndevs)) == NULL ||
+ fido_pcsc_manifest(devlist, ndevs, &nfound) != FIDO_OK)
+ goto out;
+
+ for (size_t i = 0; i < nfound; i++) {
+ const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
+ consume_str(fido_dev_info_path(di));
+ consume_str(fido_dev_info_manufacturer_string(di));
+ consume_str(fido_dev_info_product_string(di));
+ vendor_id = fido_dev_info_vendor(di);
+ product_id = fido_dev_info_product(di);
+ consume(&vendor_id, sizeof(vendor_id));
+ consume(&product_id, sizeof(product_id));
+ }
+
+out:
+ fido_dev_info_free(&devlist, ndevs);
+}
+
+static void
+test_tx(const char *path, const struct blob *apdu, uint8_t cmd, u_char *rx_buf,
+ size_t rx_len)
+{
+ fido_dev_t dev;
+ const u_char *tx_ptr = NULL;
+ size_t tx_len = 0;
+ int n;
+
+ memset(&dev, 0, sizeof(dev));
+
+ if (fido_dev_set_pcsc(&dev) < 0)
+ return;
+ if ((dev.io_handle = fido_pcsc_open(path)) == NULL)
+ return;
+
+ if (apdu) {
+ tx_ptr = apdu->body;
+ tx_len = apdu->len;
+ }
+
+ fido_pcsc_tx(&dev, cmd, tx_ptr, tx_len);
+
+ if ((n = fido_pcsc_rx(&dev, cmd, rx_buf, rx_len, -1)) >= 0)
+ consume(rx_buf, n);
+
+ fido_pcsc_close(dev.io_handle);
+}
+
+static void
+test_misc(void)
+{
+ assert(fido_pcsc_open(NULL) == NULL);
+ assert(fido_pcsc_write(NULL, NULL, INT_MAX + 1LL) == -1);
+}
+
+void
+test(const struct param *p)
+{
+ u_char buf[512];
+
+ prng_init((unsigned int)p->seed);
+ fuzz_clock_reset();
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ set_pcsc_parameters(&p->pcsc_list);
+ set_pcsc_io_functions(nfc_read, nfc_write, consume);
+
+ set_wire_data(p->wiredata_init.body, p->wiredata_init.len);
+ test_manifest();
+
+ test_misc();
+
+ set_wire_data(p->wiredata_init.body, p->wiredata_init.len);
+ test_tx(p->path, NULL, CTAP_CMD_INIT, buf, uniform_random(20));
+
+ set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len);
+ test_tx(p->path, &p->tx_apdu, CTAP_CMD_MSG, buf, sizeof(buf));
+
+ set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len);
+ test_tx(p->path, &p->tx_apdu, CTAP_CMD_CBOR, buf, sizeof(buf));
+
+ set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len);
+ test_tx(p->path, &p->tx_apdu, CTAP_CMD_LOCK, buf, sizeof(buf));
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM) {
+ mutate_string(p->path);
+ mutate_blob(&p->pcsc_list);
+ mutate_blob(&p->tx_apdu);
+ }
+
+ if (flags & MUTATE_WIREDATA) {
+ mutate_blob(&p->wiredata_init);
+ mutate_blob(&p->wiredata_msg);
+ }
+}