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