summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt17
-rw-r--r--src/assert.c2
-rw-r--r--src/cbor.c25
-rw-r--r--src/cred.c12
-rw-r--r--src/credman.c8
-rw-r--r--src/dev.c207
-rwxr-xr-xsrc/diff_exports.sh29
-rw-r--r--src/err.c4
-rw-r--r--src/es256.c2
-rw-r--r--src/export.gnu9
-rw-r--r--src/export.llvm9
-rw-r--r--src/export.msvc9
-rw-r--r--src/extern.h20
-rw-r--r--src/fido.h23
-rw-r--r--src/fido/err.h2
-rw-r--r--src/fido/param.h12
-rw-r--r--src/fido/types.h20
-rw-r--r--src/hid_hidapi.c226
-rw-r--r--src/hid_linux.c308
-rw-r--r--src/hid_openbsd.c91
-rw-r--r--src/hid_osx.c323
-rw-r--r--src/hid_win.c398
-rw-r--r--src/info.c16
-rw-r--r--src/io.c94
-rw-r--r--src/iso7816.c6
-rw-r--r--src/pin.c10
-rw-r--r--src/u2f.c100
27 files changed, 1520 insertions, 462 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3cf62e8..ad02524 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -37,19 +37,24 @@ if(FUZZ)
37endif() 37endif()
38 38
39if(USE_HIDAPI) 39if(USE_HIDAPI)
40 list(APPEND COMPAT_SOURCES hid_hidapi.c) 40 list(APPEND FIDO_SOURCES hid_hidapi.c)
41elseif(WIN32) 41elseif(WIN32)
42 list(APPEND COMPAT_SOURCES hid_win.c) 42 list(APPEND FIDO_SOURCES hid_win.c)
43elseif(APPLE) 43elseif(APPLE)
44 list(APPEND COMPAT_SOURCES hid_osx.c) 44 list(APPEND FIDO_SOURCES hid_osx.c)
45elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") 45elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
46 list(APPEND COMPAT_SOURCES hid_linux.c) 46 list(APPEND FIDO_SOURCES hid_linux.c)
47elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") 47elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
48 list(APPEND COMPAT_SOURCES hid_openbsd.c) 48 list(APPEND FIDO_SOURCES hid_openbsd.c)
49else() 49else()
50 message(FATAL_ERROR "please define a hid backend for your platform") 50 message(FATAL_ERROR "please define a hid backend for your platform")
51endif() 51endif()
52 52
53if(NOT MSVC)
54 set_source_files_properties(${FIDO_SOURCES} PROPERTIES COMPILE_FLAGS
55 "-Wconversion -Wsign-conversion")
56endif()
57
53list(APPEND COMPAT_SOURCES 58list(APPEND COMPAT_SOURCES
54 ../openbsd-compat/bsd-getpagesize.c 59 ../openbsd-compat/bsd-getpagesize.c
55 ../openbsd-compat/explicit_bzero.c 60 ../openbsd-compat/explicit_bzero.c
@@ -94,7 +99,7 @@ elseif(APPLE)
94 "-framework IOKit") 99 "-framework IOKit")
95endif() 100endif()
96set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2 101set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2
97 VERSION ${LIB_VERSION} SOVERSION ${LIB_SOVERSION}) 102 VERSION ${FIDO_VERSION} SOVERSION ${FIDO_MAJOR})
98install(TARGETS fido2_shared 103install(TARGETS fido2_shared
99 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 104 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
100 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 105 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/src/assert.c b/src/assert.c
index b71d00e..1746387 100644
--- a/src/assert.c
+++ b/src/assert.c
@@ -313,7 +313,7 @@ fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
313 goto fail; 313 goto fail;
314 } 314 }
315 } 315 }
316 316
317 r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1); 317 r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
318 if (r == FIDO_OK && assert->ext & FIDO_EXT_HMAC_SECRET) 318 if (r == FIDO_OK && assert->ext & FIDO_EXT_HMAC_SECRET)
319 if (decrypt_hmac_secrets(assert, ecdh) < 0) { 319 if (decrypt_hmac_secrets(assert, ecdh) < 0) {
diff --git a/src/cbor.c b/src/cbor.c
index 3928325..b30da50 100644
--- a/src/cbor.c
+++ b/src/cbor.c
@@ -386,7 +386,7 @@ cbor_flatten_vector(cbor_item_t *argv[], size_t argc)
386 return (NULL); 386 return (NULL);
387 387
388 for (i = 0; i < argc; i++) 388 for (i = 0; i < argc; i++)
389 if (cbor_add_arg(map, i + 1, argv[i]) < 0) 389 if (cbor_add_arg(map, (uint8_t)(i + 1), argv[i]) < 0)
390 break; 390 break;
391 391
392 if (i != argc) { 392 if (i != argc) {
@@ -583,7 +583,9 @@ cbor_encode_extensions(const fido_cred_ext_t *ext)
583 } 583 }
584 } 584 }
585 if (ext->mask & FIDO_EXT_CRED_PROTECT) { 585 if (ext->mask & FIDO_EXT_CRED_PROTECT) {
586 if (cbor_add_uint8(item, "credProtect", ext->prot) < 0) { 586 if (ext->prot < 0 || ext->prot > UINT8_MAX ||
587 cbor_add_uint8(item, "credProtect",
588 (uint8_t)ext->prot) < 0) {
587 cbor_decref(&item); 589 cbor_decref(&item);
588 return (NULL); 590 return (NULL);
589 } 591 }
@@ -634,7 +636,7 @@ cbor_encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data)
634 unsigned int dgst_len; 636 unsigned int dgst_len;
635 637
636 if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr, 638 if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr,
637 (int)hmac_key->len, data->ptr, (int)data->len, dgst, 639 (int)hmac_key->len, data->ptr, data->len, dgst,
638 &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH) 640 &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
639 return (NULL); 641 return (NULL);
640 642
@@ -696,7 +698,6 @@ cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
696 fido_blob_t *npe = NULL; /* new pin, encrypted */ 698 fido_blob_t *npe = NULL; /* new pin, encrypted */
697 fido_blob_t *ph = NULL; /* pin hash */ 699 fido_blob_t *ph = NULL; /* pin hash */
698 fido_blob_t *phe = NULL; /* pin hash, encrypted */ 700 fido_blob_t *phe = NULL; /* pin hash, encrypted */
699 int ok = -1;
700 701
701 if ((npe = fido_blob_new()) == NULL || 702 if ((npe = fido_blob_new()) == NULL ||
702 (ph = fido_blob_new()) == NULL || 703 (ph = fido_blob_new()) == NULL ||
@@ -735,8 +736,8 @@ cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
735 if ((ctx = HMAC_CTX_new()) == NULL || 736 if ((ctx = HMAC_CTX_new()) == NULL ||
736 (md = EVP_sha256()) == NULL || 737 (md = EVP_sha256()) == NULL ||
737 HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 || 738 HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
738 HMAC_Update(ctx, npe->ptr, (int)npe->len) == 0 || 739 HMAC_Update(ctx, npe->ptr, npe->len) == 0 ||
739 HMAC_Update(ctx, phe->ptr, (int)phe->len) == 0 || 740 HMAC_Update(ctx, phe->ptr, phe->len) == 0 ||
740 HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) { 741 HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
741 fido_log_debug("%s: HMAC", __func__); 742 fido_log_debug("%s: HMAC", __func__);
742 goto fail; 743 goto fail;
@@ -748,7 +749,6 @@ cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
748 goto fail; 749 goto fail;
749 } 750 }
750 751
751 ok = 0;
752fail: 752fail:
753 fido_blob_free(&npe); 753 fido_blob_free(&npe);
754 fido_blob_free(&ph); 754 fido_blob_free(&ph);
@@ -759,13 +759,6 @@ fail:
759 HMAC_CTX_free(ctx); 759 HMAC_CTX_free(ctx);
760#endif 760#endif
761 761
762 if (ok < 0) {
763 if (item != NULL) {
764 cbor_decref(&item);
765 item = NULL;
766 }
767 }
768
769 return (item); 762 return (item);
770} 763}
771 764
@@ -787,7 +780,7 @@ cbor_encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin)
787 } 780 }
788 781
789 if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr, 782 if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr,
790 (int)key->len, pe->ptr, (int)pe->len, dgst, &dgst_len) == NULL || 783 (int)key->len, pe->ptr, pe->len, dgst, &dgst_len) == NULL ||
791 dgst_len != SHA256_DIGEST_LENGTH) { 784 dgst_len != SHA256_DIGEST_LENGTH) {
792 fido_log_debug("%s: HMAC", __func__); 785 fido_log_debug("%s: HMAC", __func__);
793 goto fail; 786 goto fail;
@@ -1292,7 +1285,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
1292 } 1285 }
1293 1286
1294 if (authdata_ext != NULL) { 1287 if (authdata_ext != NULL) {
1295 if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 && 1288 if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
1296 decode_extensions(&buf, &len, authdata_ext) < 0) 1289 decode_extensions(&buf, &len, authdata_ext) < 0)
1297 return (-1); 1290 return (-1);
1298 } 1291 }
diff --git a/src/cred.c b/src/cred.c
index 4ecbba8..9f902fa 100644
--- a/src/cred.c
+++ b/src/cred.c
@@ -967,6 +967,18 @@ fido_cred_id_len(const fido_cred_t *cred)
967 return (cred->attcred.id.len); 967 return (cred->attcred.id.len);
968} 968}
969 969
970const unsigned char *
971fido_cred_aaguid_ptr(const fido_cred_t *cred)
972{
973 return (cred->attcred.aaguid);
974}
975
976size_t
977fido_cred_aaguid_len(const fido_cred_t *cred)
978{
979 return (sizeof(cred->attcred.aaguid));
980}
981
970int 982int
971fido_cred_prot(const fido_cred_t *cred) 983fido_cred_prot(const fido_cred_t *cred)
972{ 984{
diff --git a/src/credman.c b/src/credman.c
index a382185..4219807 100644
--- a/src/credman.c
+++ b/src/credman.c
@@ -230,7 +230,8 @@ fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata
230static int 230static int
231credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg) 231credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
232{ 232{
233 fido_cred_t *cred = arg; 233 fido_cred_t *cred = arg;
234 uint64_t prot;
234 235
235 if (cbor_isa_uint(key) == false || 236 if (cbor_isa_uint(key) == false ||
236 cbor_int_get_width(key) != CBOR_INT_8) { 237 cbor_int_get_width(key) != CBOR_INT_8) {
@@ -249,6 +250,11 @@ credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
249 return (-1); 250 return (-1);
250 cred->type = cred->attcred.type; /* XXX */ 251 cred->type = cred->attcred.type; /* XXX */
251 return (0); 252 return (0);
253 case 10:
254 if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX ||
255 fido_cred_set_prot(cred, (int)prot) != FIDO_OK)
256 return (-1);
257 return (0);
252 default: 258 default:
253 fido_log_debug("%s: cbor type", __func__); 259 fido_log_debug("%s: cbor type", __func__);
254 return (0); /* ignore */ 260 return (0); /* ignore */
diff --git a/src/dev.c b/src/dev.c
index 51b9935..3463ae4 100644
--- a/src/dev.c
+++ b/src/dev.c
@@ -10,6 +10,8 @@
10#include <sys/random.h> 10#include <sys/random.h>
11#endif 11#endif
12 12
13#include <openssl/sha.h>
14
13#include <fcntl.h> 15#include <fcntl.h>
14#include <stdint.h> 16#include <stdint.h>
15#include <stdlib.h> 17#include <stdlib.h>
@@ -106,10 +108,49 @@ find_manifest_func_node(dev_manifest_func_t f, dev_manifest_func_node_t **curr,
106 } 108 }
107} 109}
108 110
111#ifdef FIDO_FUZZ
112static void
113set_random_report_len(fido_dev_t *dev)
114{
115 dev->rx_len = CTAP_MIN_REPORT_LEN +
116 uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1);
117 dev->tx_len = CTAP_MIN_REPORT_LEN +
118 uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1);
119}
120#endif
121
122static void
123fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
124{
125 char * const *ptr;
126 const bool *val;
127 size_t len;
128
129 ptr = fido_cbor_info_extensions_ptr(info);
130 len = fido_cbor_info_extensions_len(info);
131
132 for (size_t i = 0; i < len; i++)
133 if (strcmp(ptr[i], "credProtect") == 0)
134 dev->flags |= FIDO_DEV_CRED_PROT;
135
136 ptr = fido_cbor_info_options_name_ptr(info);
137 val = fido_cbor_info_options_value_ptr(info);
138 len = fido_cbor_info_options_len(info);
139
140 for (size_t i = 0; i < len; i++)
141 if (strcmp(ptr[i], "clientPin") == 0) {
142 if (val[i] == true)
143 dev->flags |= FIDO_DEV_PIN_SET;
144 else
145 dev->flags |= FIDO_DEV_PIN_UNSET;
146 }
147}
148
109static int 149static int
110fido_dev_open_tx(fido_dev_t *dev, const char *path) 150fido_dev_open_tx(fido_dev_t *dev, const char *path)
111{ 151{
112 const uint8_t cmd = CTAP_CMD_INIT; 152 const uint8_t cmd = CTAP_CMD_INIT;
153 int r;
113 154
114 if (dev->io_handle != NULL) { 155 if (dev->io_handle != NULL) {
115 fido_log_debug("%s: handle=%p", __func__, dev->io_handle); 156 fido_log_debug("%s: handle=%p", __func__, dev->io_handle);
@@ -131,14 +172,44 @@ fido_dev_open_tx(fido_dev_t *dev, const char *path)
131 return (FIDO_ERR_INTERNAL); 172 return (FIDO_ERR_INTERNAL);
132 } 173 }
133 174
175 if (dev->io_own) {
176 dev->rx_len = CTAP_MAX_REPORT_LEN;
177 dev->tx_len = CTAP_MAX_REPORT_LEN;
178 } else {
179 dev->rx_len = fido_hid_report_in_len(dev->io_handle);
180 dev->tx_len = fido_hid_report_out_len(dev->io_handle);
181 }
182
183#ifdef FIDO_FUZZ
184 set_random_report_len(dev);
185#endif
186
187 if (dev->rx_len < CTAP_MIN_REPORT_LEN ||
188 dev->rx_len > CTAP_MAX_REPORT_LEN) {
189 fido_log_debug("%s: invalid rx_len %zu", __func__, dev->rx_len);
190 r = FIDO_ERR_RX;
191 goto fail;
192 }
193
194 if (dev->tx_len < CTAP_MIN_REPORT_LEN ||
195 dev->tx_len > CTAP_MAX_REPORT_LEN) {
196 fido_log_debug("%s: invalid tx_len %zu", __func__, dev->tx_len);
197 r = FIDO_ERR_TX;
198 goto fail;
199 }
200
134 if (fido_tx(dev, cmd, &dev->nonce, sizeof(dev->nonce)) < 0) { 201 if (fido_tx(dev, cmd, &dev->nonce, sizeof(dev->nonce)) < 0) {
135 fido_log_debug("%s: fido_tx", __func__); 202 fido_log_debug("%s: fido_tx", __func__);
136 dev->io.close(dev->io_handle); 203 r = FIDO_ERR_TX;
137 dev->io_handle = NULL; 204 goto fail;
138 return (FIDO_ERR_TX);
139 } 205 }
140 206
141 return (FIDO_OK); 207 return (FIDO_OK);
208fail:
209 dev->io.close(dev->io_handle);
210 dev->io_handle = NULL;
211
212 return (r);
142} 213}
143 214
144static int 215static int
@@ -166,6 +237,7 @@ fido_dev_open_rx(fido_dev_t *dev, int ms)
166 goto fail; 237 goto fail;
167 } 238 }
168 239
240 dev->flags = 0;
169 dev->cid = dev->attr.cid; 241 dev->cid = dev->attr.cid;
170 242
171 if (fido_dev_is_fido2(dev)) { 243 if (fido_dev_is_fido2(dev)) {
@@ -177,6 +249,8 @@ fido_dev_open_rx(fido_dev_t *dev, int ms)
177 if (fido_dev_get_cbor_info_wait(dev, info, ms) != FIDO_OK) { 249 if (fido_dev_get_cbor_info_wait(dev, info, ms) != FIDO_OK) {
178 fido_log_debug("%s: falling back to u2f", __func__); 250 fido_log_debug("%s: falling back to u2f", __func__);
179 fido_dev_force_u2f(dev); 251 fido_dev_force_u2f(dev);
252 } else {
253 fido_dev_set_flags(dev, info);
180 } 254 }
181 } 255 }
182 256
@@ -303,6 +377,9 @@ fido_dev_close(fido_dev_t *dev)
303int 377int
304fido_dev_cancel(fido_dev_t *dev) 378fido_dev_cancel(fido_dev_t *dev)
305{ 379{
380 if (fido_dev_is_fido2(dev) == false)
381 return (FIDO_ERR_INVALID_ARGUMENT);
382
306 if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0) < 0) 383 if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0) < 0)
307 return (FIDO_ERR_TX); 384 return (FIDO_ERR_TX);
308 385
@@ -310,6 +387,105 @@ fido_dev_cancel(fido_dev_t *dev)
310} 387}
311 388
312int 389int
390fido_dev_get_touch_begin(fido_dev_t *dev)
391{
392 fido_blob_t f;
393 cbor_item_t *argv[9];
394 const char *clientdata = FIDO_DUMMY_CLIENTDATA;
395 const uint8_t user_id = FIDO_DUMMY_USER_ID;
396 unsigned char cdh[SHA256_DIGEST_LENGTH];
397 fido_rp_t rp;
398 fido_user_t user;
399 int r = FIDO_ERR_INTERNAL;
400
401 memset(&f, 0, sizeof(f));
402 memset(argv, 0, sizeof(argv));
403 memset(cdh, 0, sizeof(cdh));
404 memset(&rp, 0, sizeof(rp));
405 memset(&user, 0, sizeof(user));
406
407 if (fido_dev_is_fido2(dev) == false)
408 return (u2f_get_touch_begin(dev));
409
410 if (SHA256((const void *)clientdata, strlen(clientdata), cdh) != cdh) {
411 fido_log_debug("%s: sha256", __func__);
412 return (FIDO_ERR_INTERNAL);
413 }
414
415 if ((rp.id = strdup(FIDO_DUMMY_RP_ID)) == NULL ||
416 (user.name = strdup(FIDO_DUMMY_USER_NAME)) == NULL) {
417 fido_log_debug("%s: strdup", __func__);
418 goto fail;
419 }
420
421 if (fido_blob_set(&user.id, &user_id, sizeof(user_id)) < 0) {
422 fido_log_debug("%s: fido_blob_set", __func__);
423 goto fail;
424 }
425
426 if ((argv[0] = cbor_build_bytestring(cdh, sizeof(cdh))) == NULL ||
427 (argv[1] = cbor_encode_rp_entity(&rp)) == NULL ||
428 (argv[2] = cbor_encode_user_entity(&user)) == NULL ||
429 (argv[3] = cbor_encode_pubkey_param(COSE_ES256)) == NULL) {
430 fido_log_debug("%s: cbor encode", __func__);
431 goto fail;
432 }
433
434 if (fido_dev_supports_pin(dev)) {
435 if ((argv[7] = cbor_new_definite_bytestring()) == NULL ||
436 (argv[8] = cbor_encode_pin_opt()) == NULL) {
437 fido_log_debug("%s: cbor encode", __func__);
438 goto fail;
439 }
440 }
441
442 if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 ||
443 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
444 fido_log_debug("%s: fido_tx", __func__);
445 r = FIDO_ERR_TX;
446 goto fail;
447 }
448
449 r = FIDO_OK;
450fail:
451 cbor_vector_free(argv, nitems(argv));
452 free(f.ptr);
453 free(rp.id);
454 free(user.name);
455 free(user.id.ptr);
456
457 return (r);
458}
459
460int
461fido_dev_get_touch_status(fido_dev_t *dev, int *touched, int ms)
462{
463 int r;
464
465 *touched = 0;
466
467 if (fido_dev_is_fido2(dev) == false)
468 return (u2f_get_touch_status(dev, touched, ms));
469
470 switch ((r = fido_rx_cbor_status(dev, ms))) {
471 case FIDO_ERR_PIN_AUTH_INVALID:
472 case FIDO_ERR_PIN_INVALID:
473 case FIDO_ERR_PIN_NOT_SET:
474 case FIDO_ERR_SUCCESS:
475 *touched = 1;
476 break;
477 case FIDO_ERR_RX:
478 /* ignore */
479 break;
480 default:
481 fido_log_debug("%s: fido_rx_cbor_status", __func__);
482 return (r);
483 }
484
485 return (FIDO_OK);
486}
487
488int
313fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io) 489fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
314{ 490{
315 if (dev->io_handle != NULL) { 491 if (dev->io_handle != NULL) {
@@ -324,6 +500,7 @@ fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
324 } 500 }
325 501
326 dev->io = *io; 502 dev->io = *io;
503 dev->io_own = true;
327 504
328 return (FIDO_OK); 505 return (FIDO_OK);
329} 506}
@@ -337,6 +514,7 @@ fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t)
337 } 514 }
338 515
339 dev->transport = *t; 516 dev->transport = *t;
517 dev->io_own = true;
340 518
341 return (FIDO_OK); 519 return (FIDO_OK);
342} 520}
@@ -446,10 +624,29 @@ fido_dev_is_fido2(const fido_dev_t *dev)
446 return (dev->attr.flags & FIDO_CAP_CBOR); 624 return (dev->attr.flags & FIDO_CAP_CBOR);
447} 625}
448 626
627bool
628fido_dev_supports_pin(const fido_dev_t *dev)
629{
630 return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET));
631}
632
633bool
634fido_dev_has_pin(const fido_dev_t *dev)
635{
636 return (dev->flags & FIDO_DEV_PIN_SET);
637}
638
639bool
640fido_dev_supports_cred_prot(const fido_dev_t *dev)
641{
642 return (dev->flags & FIDO_DEV_CRED_PROT);
643}
644
449void 645void
450fido_dev_force_u2f(fido_dev_t *dev) 646fido_dev_force_u2f(fido_dev_t *dev)
451{ 647{
452 dev->attr.flags &= ~FIDO_CAP_CBOR; 648 dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR;
649 dev->flags = 0;
453} 650}
454 651
455void 652void
diff --git a/src/diff_exports.sh b/src/diff_exports.sh
index 7920f47..9cff009 100755
--- a/src/diff_exports.sh
+++ b/src/diff_exports.sh
@@ -1,23 +1,26 @@
1#!/bin/bash -u 1#!/bin/sh -u
2 2
3# Copyright (c) 2018 Yubico AB. All rights reserved. 3# Copyright (c) 2018 Yubico AB. All rights reserved.
4# Use of this source code is governed by a BSD-style 4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file. 5# license that can be found in the LICENSE file.
6 6
7[[ ! -f export.gnu || ! -f export.llvm || ! -f export.msvc ]] && exit 1 7for f in export.gnu export.llvm export.msvc; do
8 if [ ! -f "${f}" ]; then
9 exit 1
10 fi
11done
8 12
9TMPDIR=$(mktemp -d) 13TMPDIR="$(mktemp -d)"
10GNU=${TMPDIR}/gnu 14GNU="${TMPDIR}/gnu"
11LLVM=${TMPDIR}/llvm 15LLVM="${TMPDIR}/llvm"
12MSVC=${TMPDIR}/msvc 16MSVC="${TMPDIR}/msvc"
13 17
14egrep -o $'([^*{}\t]+);$' export.gnu | tr -d ';' | sort > ${GNU} 18awk '/^[^*{}]+;$/' export.gnu | tr -d '\t;' | sort > "${GNU}"
15sed 's/^_//g' export.llvm | sort > ${LLVM} 19sed 's/^_//' export.llvm | sort > "${LLVM}"
16egrep -v "^EXPORTS$" export.msvc | sort > ${MSVC} 20grep -v '^EXPORTS$' export.msvc | sort > "${MSVC}"
17diff -u ${GNU} ${LLVM} && diff -u ${MSVC} ${LLVM} 21diff -u "${GNU}" "${LLVM}" && diff -u "${MSVC}" "${LLVM}"
18ERROR=$? 22ERROR=$?
19 23rm "${GNU}" "${LLVM}" "${MSVC}"
20rm ${GNU} ${LLVM} ${MSVC} 24rmdir "${TMPDIR}"
21rmdir ${TMPDIR}
22 25
23exit ${ERROR} 26exit ${ERROR}
diff --git a/src/err.c b/src/err.c
index 6261bfc..19cda21 100644
--- a/src/err.c
+++ b/src/err.c
@@ -38,6 +38,8 @@ fido_strerr(int n)
38 return "FIDO_ERR_LIMIT_EXCEEDED"; 38 return "FIDO_ERR_LIMIT_EXCEEDED";
39 case FIDO_ERR_UNSUPPORTED_EXTENSION: 39 case FIDO_ERR_UNSUPPORTED_EXTENSION:
40 return "FIDO_ERR_UNSUPPORTED_EXTENSION"; 40 return "FIDO_ERR_UNSUPPORTED_EXTENSION";
41 case FIDO_ERR_FP_DATABASE_FULL:
42 return "FIDO_ERR_FP_DATABASE_FULL";
41 case FIDO_ERR_CREDENTIAL_EXCLUDED: 43 case FIDO_ERR_CREDENTIAL_EXCLUDED:
42 return "FIDO_ERR_CREDENTIAL_EXCLUDED"; 44 return "FIDO_ERR_CREDENTIAL_EXCLUDED";
43 case FIDO_ERR_PROCESSING: 45 case FIDO_ERR_PROCESSING:
@@ -94,6 +96,8 @@ fido_strerr(int n)
94 return "FIDO_ERR_ACTION_TIMEOUT"; 96 return "FIDO_ERR_ACTION_TIMEOUT";
95 case FIDO_ERR_UP_REQUIRED: 97 case FIDO_ERR_UP_REQUIRED:
96 return "FIDO_ERR_UP_REQUIRED"; 98 return "FIDO_ERR_UP_REQUIRED";
99 case FIDO_ERR_UV_BLOCKED:
100 return "FIDO_ERR_UV_BLOCKED";
97 case FIDO_ERR_ERR_OTHER: 101 case FIDO_ERR_ERR_OTHER:
98 return "FIDO_ERR_ERR_OTHER"; 102 return "FIDO_ERR_ERR_OTHER";
99 case FIDO_ERR_SPEC_LAST: 103 case FIDO_ERR_SPEC_LAST:
diff --git a/src/es256.c b/src/es256.c
index 020ecaa..5b4e6d6 100644
--- a/src/es256.c
+++ b/src/es256.c
@@ -92,7 +92,7 @@ es256_pk_encode(const es256_pk_t *pk, int ecdh)
92 92
93 /* alg */ 93 /* alg */
94 if ((argv[1].key = cbor_build_uint8(3)) == NULL || 94 if ((argv[1].key = cbor_build_uint8(3)) == NULL ||
95 (argv[1].value = cbor_build_negint8(-alg - 1)) == NULL || 95 (argv[1].value = cbor_build_negint8((uint8_t)(-alg - 1))) == NULL ||
96 !cbor_map_add(item, argv[1])) 96 !cbor_map_add(item, argv[1]))
97 goto fail; 97 goto fail;
98 98
diff --git a/src/export.gnu b/src/export.gnu
index cbfa69f..007b5b9 100644
--- a/src/export.gnu
+++ b/src/export.gnu
@@ -76,6 +76,8 @@
76 fido_cbor_info_extensions_ptr; 76 fido_cbor_info_extensions_ptr;
77 fido_cbor_info_free; 77 fido_cbor_info_free;
78 fido_cbor_info_maxmsgsiz; 78 fido_cbor_info_maxmsgsiz;
79 fido_cbor_info_maxcredcntlst;
80 fido_cbor_info_maxcredidlen;
79 fido_cbor_info_fwversion; 81 fido_cbor_info_fwversion;
80 fido_cbor_info_new; 82 fido_cbor_info_new;
81 fido_cbor_info_options_len; 83 fido_cbor_info_options_len;
@@ -96,6 +98,8 @@
96 fido_cred_free; 98 fido_cred_free;
97 fido_cred_id_len; 99 fido_cred_id_len;
98 fido_cred_id_ptr; 100 fido_cred_id_ptr;
101 fido_cred_aaguid_len;
102 fido_cred_aaguid_ptr;
99 fido_credman_del_dev_rk; 103 fido_credman_del_dev_rk;
100 fido_credman_get_dev_metadata; 104 fido_credman_get_dev_metadata;
101 fido_credman_get_dev_rk; 105 fido_credman_get_dev_rk;
@@ -155,6 +159,9 @@
155 fido_dev_get_assert; 159 fido_dev_get_assert;
156 fido_dev_get_cbor_info; 160 fido_dev_get_cbor_info;
157 fido_dev_get_retry_count; 161 fido_dev_get_retry_count;
162 fido_dev_get_touch_begin;
163 fido_dev_get_touch_status;
164 fido_dev_has_pin;
158 fido_dev_info_free; 165 fido_dev_info_free;
159 fido_dev_info_manifest; 166 fido_dev_info_manifest;
160 fido_dev_info_manufacturer_string; 167 fido_dev_info_manufacturer_string;
@@ -175,6 +182,8 @@
175 fido_dev_set_io_functions; 182 fido_dev_set_io_functions;
176 fido_dev_set_pin; 183 fido_dev_set_pin;
177 fido_dev_set_transport_functions; 184 fido_dev_set_transport_functions;
185 fido_dev_supports_cred_prot;
186 fido_dev_supports_pin;
178 fido_init; 187 fido_init;
179 fido_set_log_handler; 188 fido_set_log_handler;
180 fido_strerr; 189 fido_strerr;
diff --git a/src/export.llvm b/src/export.llvm
index abde2e9..ffbc157 100644
--- a/src/export.llvm
+++ b/src/export.llvm
@@ -74,6 +74,8 @@ _fido_cbor_info_extensions_len
74_fido_cbor_info_extensions_ptr 74_fido_cbor_info_extensions_ptr
75_fido_cbor_info_free 75_fido_cbor_info_free
76_fido_cbor_info_maxmsgsiz 76_fido_cbor_info_maxmsgsiz
77_fido_cbor_info_maxcredcntlst
78_fido_cbor_info_maxcredidlen
77_fido_cbor_info_fwversion 79_fido_cbor_info_fwversion
78_fido_cbor_info_new 80_fido_cbor_info_new
79_fido_cbor_info_options_len 81_fido_cbor_info_options_len
@@ -94,6 +96,8 @@ _fido_cred_fmt
94_fido_cred_free 96_fido_cred_free
95_fido_cred_id_len 97_fido_cred_id_len
96_fido_cred_id_ptr 98_fido_cred_id_ptr
99_fido_cred_aaguid_len
100_fido_cred_aaguid_ptr
97_fido_credman_del_dev_rk 101_fido_credman_del_dev_rk
98_fido_credman_get_dev_metadata 102_fido_credman_get_dev_metadata
99_fido_credman_get_dev_rk 103_fido_credman_get_dev_rk
@@ -153,6 +157,9 @@ _fido_dev_free
153_fido_dev_get_assert 157_fido_dev_get_assert
154_fido_dev_get_cbor_info 158_fido_dev_get_cbor_info
155_fido_dev_get_retry_count 159_fido_dev_get_retry_count
160_fido_dev_get_touch_begin
161_fido_dev_get_touch_status
162_fido_dev_has_pin
156_fido_dev_info_free 163_fido_dev_info_free
157_fido_dev_info_manifest 164_fido_dev_info_manifest
158_fido_dev_info_manufacturer_string 165_fido_dev_info_manufacturer_string
@@ -173,6 +180,8 @@ _fido_dev_reset
173_fido_dev_set_io_functions 180_fido_dev_set_io_functions
174_fido_dev_set_pin 181_fido_dev_set_pin
175_fido_dev_set_transport_functions 182_fido_dev_set_transport_functions
183_fido_dev_supports_cred_prot
184_fido_dev_supports_pin
176_fido_init 185_fido_init
177_fido_set_log_handler 186_fido_set_log_handler
178_fido_strerr 187_fido_strerr
diff --git a/src/export.msvc b/src/export.msvc
index 06ec69a..1a2a0b7 100644
--- a/src/export.msvc
+++ b/src/export.msvc
@@ -75,6 +75,8 @@ fido_cbor_info_extensions_len
75fido_cbor_info_extensions_ptr 75fido_cbor_info_extensions_ptr
76fido_cbor_info_free 76fido_cbor_info_free
77fido_cbor_info_maxmsgsiz 77fido_cbor_info_maxmsgsiz
78fido_cbor_info_maxcredcntlst
79fido_cbor_info_maxcredidlen
78fido_cbor_info_fwversion 80fido_cbor_info_fwversion
79fido_cbor_info_new 81fido_cbor_info_new
80fido_cbor_info_options_len 82fido_cbor_info_options_len
@@ -95,6 +97,8 @@ fido_cred_fmt
95fido_cred_free 97fido_cred_free
96fido_cred_id_len 98fido_cred_id_len
97fido_cred_id_ptr 99fido_cred_id_ptr
100fido_cred_aaguid_len
101fido_cred_aaguid_ptr
98fido_credman_del_dev_rk 102fido_credman_del_dev_rk
99fido_credman_get_dev_metadata 103fido_credman_get_dev_metadata
100fido_credman_get_dev_rk 104fido_credman_get_dev_rk
@@ -154,6 +158,9 @@ fido_dev_free
154fido_dev_get_assert 158fido_dev_get_assert
155fido_dev_get_cbor_info 159fido_dev_get_cbor_info
156fido_dev_get_retry_count 160fido_dev_get_retry_count
161fido_dev_get_touch_begin
162fido_dev_get_touch_status
163fido_dev_has_pin
157fido_dev_info_free 164fido_dev_info_free
158fido_dev_info_manifest 165fido_dev_info_manifest
159fido_dev_info_manufacturer_string 166fido_dev_info_manufacturer_string
@@ -174,6 +181,8 @@ fido_dev_reset
174fido_dev_set_io_functions 181fido_dev_set_io_functions
175fido_dev_set_pin 182fido_dev_set_pin
176fido_dev_set_transport_functions 183fido_dev_set_transport_functions
184fido_dev_supports_cred_prot
185fido_dev_supports_pin
177fido_init 186fido_init
178fido_set_log_handler 187fido_set_log_handler
179fido_strerr 188fido_strerr
diff --git a/src/extern.h b/src/extern.h
index fc0a49d..4c036cb 100644
--- a/src/extern.h
+++ b/src/extern.h
@@ -88,6 +88,8 @@ void *fido_hid_open(const char *);
88void fido_hid_close(void *); 88void fido_hid_close(void *);
89int fido_hid_read(void *, unsigned char *, size_t, int); 89int fido_hid_read(void *, unsigned char *, size_t, int);
90int fido_hid_write(void *, const unsigned char *, size_t); 90int fido_hid_write(void *, const unsigned char *, size_t);
91size_t fido_hid_report_in_len(void *);
92size_t fido_hid_report_out_len(void *);
91 93
92/* generic i/o */ 94/* generic i/o */
93int fido_rx_cbor_status(fido_dev_t *, int); 95int fido_rx_cbor_status(fido_dev_t *, int);
@@ -115,6 +117,8 @@ void fido_log_xxd(const void *, size_t);
115/* u2f */ 117/* u2f */
116int u2f_register(fido_dev_t *, fido_cred_t *, int); 118int u2f_register(fido_dev_t *, fido_cred_t *, int);
117int u2f_authenticate(fido_dev_t *, fido_assert_t *, int); 119int u2f_authenticate(fido_dev_t *, fido_assert_t *, int);
120int u2f_get_touch_begin(fido_dev_t *);
121int u2f_get_touch_status(fido_dev_t *, int *, int);
118 122
119/* unexposed fido ops */ 123/* unexposed fido ops */
120int fido_dev_authkey(fido_dev_t *, es256_pk_t *); 124int fido_dev_authkey(fido_dev_t *, es256_pk_t *);
@@ -149,6 +153,22 @@ typedef int (*dev_manifest_func_t)(fido_dev_info_t *, size_t, size_t *);
149int fido_dev_register_manifest_func(const dev_manifest_func_t); 153int fido_dev_register_manifest_func(const dev_manifest_func_t);
150void fido_dev_unregister_manifest_func(const dev_manifest_func_t); 154void fido_dev_unregister_manifest_func(const dev_manifest_func_t);
151 155
156/* fuzzing instrumentation */
157#ifdef FIDO_FUZZ
158uint32_t uniform_random(uint32_t);
159#endif
160
161/* internal device capability flags */
162#define FIDO_DEV_PIN_SET 0x01
163#define FIDO_DEV_PIN_UNSET 0x02
164#define FIDO_DEV_CRED_PROT 0x04
165
166/* miscellanea */
167#define FIDO_DUMMY_CLIENTDATA ""
168#define FIDO_DUMMY_RP_ID "localhost"
169#define FIDO_DUMMY_USER_NAME "dummy"
170#define FIDO_DUMMY_USER_ID 1
171
152#ifdef __cplusplus 172#ifdef __cplusplus
153} /* extern "C" */ 173} /* extern "C" */
154#endif /* __cplusplus */ 174#endif /* __cplusplus */
diff --git a/src/fido.h b/src/fido.h
index e41de89..baa2928 100644
--- a/src/fido.h
+++ b/src/fido.h
@@ -32,6 +32,12 @@
32extern "C" { 32extern "C" {
33#endif /* __cplusplus */ 33#endif /* __cplusplus */
34 34
35#ifdef _MSC_VER
36#define FIDO_DEPRECATED(reason) __declspec(deprecated(reason))
37#else
38#define FIDO_DEPRECATED(reason) __attribute__((__deprecated__(reason)))
39#endif
40
35fido_assert_t *fido_assert_new(void); 41fido_assert_t *fido_assert_new(void);
36fido_cred_t *fido_cred_new(void); 42fido_cred_t *fido_cred_new(void);
37fido_dev_t *fido_dev_new(void); 43fido_dev_t *fido_dev_new(void);
@@ -82,6 +88,7 @@ const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *);
82const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *); 88const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
83const unsigned char *fido_cred_clientdata_hash_ptr(const fido_cred_t *); 89const unsigned char *fido_cred_clientdata_hash_ptr(const fido_cred_t *);
84const unsigned char *fido_cred_id_ptr(const fido_cred_t *); 90const unsigned char *fido_cred_id_ptr(const fido_cred_t *);
91const unsigned char *fido_cred_aaguid_ptr(const fido_cred_t *);
85const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *); 92const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
86const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *); 93const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *);
87const unsigned char *fido_cred_sig_ptr(const fido_cred_t *); 94const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
@@ -97,8 +104,8 @@ int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *,
97int fido_assert_set_count(fido_assert_t *, size_t); 104int fido_assert_set_count(fido_assert_t *, size_t);
98int fido_assert_set_extensions(fido_assert_t *, int); 105int fido_assert_set_extensions(fido_assert_t *, int);
99int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t); 106int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t);
100int fido_assert_set_options(fido_assert_t *, bool, bool) 107FIDO_DEPRECATED("use fido_assert_set_up/fido_assert_set_uv")
101 __attribute__((__deprecated__("use fido_assert_set_up/fido_assert_set_uv"))); 108int fido_assert_set_options(fido_assert_t *, bool, bool);
102int fido_assert_set_rp(fido_assert_t *, const char *); 109int fido_assert_set_rp(fido_assert_t *, const char *);
103int fido_assert_set_up(fido_assert_t *, fido_opt_t); 110int fido_assert_set_up(fido_assert_t *, fido_opt_t);
104int fido_assert_set_uv(fido_assert_t *, fido_opt_t); 111int fido_assert_set_uv(fido_assert_t *, fido_opt_t);
@@ -111,8 +118,8 @@ int fido_cred_set_authdata_raw(fido_cred_t *, const unsigned char *, size_t);
111int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t); 118int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t);
112int fido_cred_set_extensions(fido_cred_t *, int); 119int fido_cred_set_extensions(fido_cred_t *, int);
113int fido_cred_set_fmt(fido_cred_t *, const char *); 120int fido_cred_set_fmt(fido_cred_t *, const char *);
114int fido_cred_set_options(fido_cred_t *, bool, bool) 121FIDO_DEPRECATED("use fido_cred_set_rk/fido_cred_set_uv")
115 __attribute__((__deprecated__("use fido_cred_set_rk/fido_cred_set_uv"))); 122int fido_cred_set_options(fido_cred_t *, bool, bool);
116int fido_cred_set_prot(fido_cred_t *, int); 123int fido_cred_set_prot(fido_cred_t *, int);
117int fido_cred_set_rk(fido_cred_t *, fido_opt_t); 124int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
118int fido_cred_set_rp(fido_cred_t *, const char *, const char *); 125int fido_cred_set_rp(fido_cred_t *, const char *, const char *);
@@ -130,6 +137,8 @@ int fido_dev_close(fido_dev_t *);
130int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *); 137int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *);
131int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *); 138int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
132int fido_dev_get_retry_count(fido_dev_t *, int *); 139int fido_dev_get_retry_count(fido_dev_t *, int *);
140int fido_dev_get_touch_begin(fido_dev_t *);
141int fido_dev_get_touch_status(fido_dev_t *, int *, int);
133int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *); 142int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
134int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *); 143int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *);
135int fido_dev_open_with_info(fido_dev_t *); 144int fido_dev_open_with_info(fido_dev_t *);
@@ -154,6 +163,7 @@ size_t fido_cbor_info_versions_len(const fido_cbor_info_t *);
154size_t fido_cred_authdata_len(const fido_cred_t *); 163size_t fido_cred_authdata_len(const fido_cred_t *);
155size_t fido_cred_clientdata_hash_len(const fido_cred_t *); 164size_t fido_cred_clientdata_hash_len(const fido_cred_t *);
156size_t fido_cred_id_len(const fido_cred_t *); 165size_t fido_cred_id_len(const fido_cred_t *);
166size_t fido_cred_aaguid_len(const fido_cred_t *);
157size_t fido_cred_user_id_len(const fido_cred_t *); 167size_t fido_cred_user_id_len(const fido_cred_t *);
158size_t fido_cred_pubkey_len(const fido_cred_t *); 168size_t fido_cred_pubkey_len(const fido_cred_t *);
159size_t fido_cred_sig_len(const fido_cred_t *); 169size_t fido_cred_sig_len(const fido_cred_t *);
@@ -170,9 +180,14 @@ uint8_t fido_dev_flags(const fido_dev_t *);
170int16_t fido_dev_info_vendor(const fido_dev_info_t *); 180int16_t fido_dev_info_vendor(const fido_dev_info_t *);
171int16_t fido_dev_info_product(const fido_dev_info_t *); 181int16_t fido_dev_info_product(const fido_dev_info_t *);
172uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *); 182uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *);
183uint64_t fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *);
184uint64_t fido_cbor_info_maxcredidlen(const fido_cbor_info_t *);
173uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *); 185uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *);
174 186
187bool fido_dev_has_pin(const fido_dev_t *);
175bool fido_dev_is_fido2(const fido_dev_t *); 188bool fido_dev_is_fido2(const fido_dev_t *);
189bool fido_dev_supports_pin(const fido_dev_t *);
190bool fido_dev_supports_cred_prot(const fido_dev_t *);
176 191
177#ifdef __cplusplus 192#ifdef __cplusplus
178} /* extern "C" */ 193} /* extern "C" */
diff --git a/src/fido/err.h b/src/fido/err.h
index d7453fc..253914f 100644
--- a/src/fido/err.h
+++ b/src/fido/err.h
@@ -21,6 +21,7 @@
21#define FIDO_ERR_MISSING_PARAMETER 0x14 21#define FIDO_ERR_MISSING_PARAMETER 0x14
22#define FIDO_ERR_LIMIT_EXCEEDED 0x15 22#define FIDO_ERR_LIMIT_EXCEEDED 0x15
23#define FIDO_ERR_UNSUPPORTED_EXTENSION 0x16 23#define FIDO_ERR_UNSUPPORTED_EXTENSION 0x16
24#define FIDO_ERR_FP_DATABASE_FULL 0x17
24#define FIDO_ERR_CREDENTIAL_EXCLUDED 0x19 25#define FIDO_ERR_CREDENTIAL_EXCLUDED 0x19
25#define FIDO_ERR_PROCESSING 0x21 26#define FIDO_ERR_PROCESSING 0x21
26#define FIDO_ERR_INVALID_CREDENTIAL 0x22 27#define FIDO_ERR_INVALID_CREDENTIAL 0x22
@@ -49,6 +50,7 @@
49#define FIDO_ERR_REQUEST_TOO_LARGE 0x39 50#define FIDO_ERR_REQUEST_TOO_LARGE 0x39
50#define FIDO_ERR_ACTION_TIMEOUT 0x3a 51#define FIDO_ERR_ACTION_TIMEOUT 0x3a
51#define FIDO_ERR_UP_REQUIRED 0x3b 52#define FIDO_ERR_UP_REQUIRED 0x3b
53#define FIDO_ERR_UV_BLOCKED 0x3c
52#define FIDO_ERR_ERR_OTHER 0x7f 54#define FIDO_ERR_ERR_OTHER 0x7f
53#define FIDO_ERR_SPEC_LAST 0xdf 55#define FIDO_ERR_SPEC_LAST 0xdf
54 56
diff --git a/src/fido/param.h b/src/fido/param.h
index 7d3c0cc..14ee74e 100644
--- a/src/fido/param.h
+++ b/src/fido/param.h
@@ -50,8 +50,14 @@
50/* HID Broadcast channel ID. */ 50/* HID Broadcast channel ID. */
51#define CTAP_CID_BROADCAST 0xffffffff 51#define CTAP_CID_BROADCAST 0xffffffff
52 52
53/* Expected size of a HID report in bytes. */ 53#define CTAP_INIT_HEADER_LEN 7
54#define CTAP_RPT_SIZE 64 54#define CTAP_CONT_HEADER_LEN 5
55
56/* Maximum length of a CTAP HID report in bytes. */
57#define CTAP_MAX_REPORT_LEN 64
58
59/* Minimum length of a CTAP HID report in bytes. */
60#define CTAP_MIN_REPORT_LEN (CTAP_INIT_HEADER_LEN + 1)
55 61
56/* Randomness device on UNIX-like platforms. */ 62/* Randomness device on UNIX-like platforms. */
57#ifndef FIDO_RANDOM_DEV 63#ifndef FIDO_RANDOM_DEV
@@ -60,7 +66,7 @@
60 66
61/* Maximum message size in bytes. */ 67/* Maximum message size in bytes. */
62#ifndef FIDO_MAXMSG 68#ifndef FIDO_MAXMSG
63#define FIDO_MAXMSG 1200 69#define FIDO_MAXMSG 2048
64#endif 70#endif
65 71
66/* CTAP capability bits. */ 72/* CTAP capability bits. */
diff --git a/src/fido/types.h b/src/fido/types.h
index 5df5e36..cce1a44 100644
--- a/src/fido/types.h
+++ b/src/fido/types.h
@@ -175,13 +175,15 @@ typedef struct fido_byte_array {
175} fido_byte_array_t; 175} fido_byte_array_t;
176 176
177typedef struct fido_cbor_info { 177typedef struct fido_cbor_info {
178 fido_str_array_t versions; /* supported versions: fido2|u2f */ 178 fido_str_array_t versions; /* supported versions: fido2|u2f */
179 fido_str_array_t extensions; /* list of supported extensions */ 179 fido_str_array_t extensions; /* list of supported extensions */
180 unsigned char aaguid[16]; /* aaguid */ 180 unsigned char aaguid[16]; /* aaguid */
181 fido_opt_array_t options; /* list of supported options */ 181 fido_opt_array_t options; /* list of supported options */
182 uint64_t maxmsgsiz; /* maximum message size */ 182 uint64_t maxmsgsiz; /* maximum message size */
183 fido_byte_array_t protocols; /* supported pin protocols */ 183 fido_byte_array_t protocols; /* supported pin protocols */
184 uint64_t fwversion; /* firmware version */ 184 uint64_t maxcredcntlst; /* max number of credentials in list */
185 uint64_t maxcredidlen; /* max credential ID length */
186 uint64_t fwversion; /* firmware version */
185} fido_cbor_info_t; 187} fido_cbor_info_t;
186 188
187typedef struct fido_dev_info { 189typedef struct fido_dev_info {
@@ -213,6 +215,10 @@ typedef struct fido_dev {
213 char *path; /* device path */ 215 char *path; /* device path */
214 void *io_handle; /* abstract i/o handle */ 216 void *io_handle; /* abstract i/o handle */
215 fido_dev_io_t io; /* i/o functions */ 217 fido_dev_io_t io; /* i/o functions */
218 bool io_own; /* device has own io/transport */
219 size_t rx_len; /* length of HID input reports */
220 size_t tx_len; /* length of HID output reports */
221 int flags; /* internal flags; see FIDO_DEV_* */
216 fido_dev_transport_t transport; /* transport functions */ 222 fido_dev_transport_t transport; /* transport functions */
217} fido_dev_t; 223} fido_dev_t;
218 224
diff --git a/src/hid_hidapi.c b/src/hid_hidapi.c
index 915621f..898fd9e 100644
--- a/src/hid_hidapi.c
+++ b/src/hid_hidapi.c
@@ -4,14 +4,26 @@
4 * license that can be found in the LICENSE file. 4 * license that can be found in the LICENSE file.
5 */ 5 */
6 6
7#include <hidapi/hidapi.h> 7#ifdef __linux__
8#include <sys/ioctl.h>
9#include <linux/hidraw.h>
10#include <linux/input.h>
11#include <fcntl.h>
12#endif
8 13
14#include <hidapi.h>
9#include <stdlib.h> 15#include <stdlib.h>
10#include <string.h> 16#include <string.h>
11#include <wchar.h> 17#include <wchar.h>
12 18
13#include "fido.h" 19#include "fido.h"
14 20
21struct hid_hidapi {
22 void *handle;
23 size_t report_in_len;
24 size_t report_out_len;
25};
26
15static size_t 27static size_t
16fido_wcslen(const wchar_t *wcs) 28fido_wcslen(const wchar_t *wcs)
17{ 29{
@@ -27,7 +39,7 @@ wcs_to_cs(const wchar_t *wcs)
27 char *cs; 39 char *cs;
28 size_t i; 40 size_t i;
29 41
30 if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL) 42 if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL)
31 return NULL; 43 return NULL;
32 44
33 for (i = 0; i < fido_wcslen(wcs); i++) { 45 for (i = 0; i < fido_wcslen(wcs); i++) {
@@ -68,11 +80,12 @@ copy_info(fido_dev_info_t *di, const struct hid_device_info *d)
68 free(di->path); 80 free(di->path);
69 free(di->manufacturer); 81 free(di->manufacturer);
70 free(di->product); 82 free(di->product);
83 explicit_bzero(di, sizeof(*di));
71 return -1; 84 return -1;
72 } 85 }
73 86
74 di->product_id = d->product_id; 87 di->product_id = (int16_t)d->product_id;
75 di->vendor_id = d->vendor_id; 88 di->vendor_id = (int16_t)d->vendor_id;
76 di->io = (fido_dev_io_t) { 89 di->io = (fido_dev_io_t) {
77 &fido_hid_open, 90 &fido_hid_open,
78 &fido_hid_close, 91 &fido_hid_close,
@@ -83,28 +96,199 @@ copy_info(fido_dev_info_t *di, const struct hid_device_info *d)
83 return 0; 96 return 0;
84} 97}
85 98
99#ifdef __linux__
100static int
101get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
102{
103 *key = tag & 0xfc;
104 if ((*key & 0xf0) == 0xf0) {
105 fido_log_debug("%s: *key=0x%02x", __func__, *key);
106 return -1;
107 }
108
109 *key_len = tag & 0x3;
110 if (*key_len == 3) {
111 *key_len = 4;
112 }
113
114 return 0;
115}
116
117static int
118get_key_val(const void *body, size_t key_len, uint32_t *val)
119{
120 const uint8_t *ptr = body;
121
122 switch (key_len) {
123 case 0:
124 *val = 0;
125 break;
126 case 1:
127 *val = ptr[0];
128 break;
129 case 2:
130 *val = (uint32_t)((ptr[1] << 8) | ptr[0]);
131 break;
132 default:
133 fido_log_debug("%s: key_len=%zu", __func__, key_len);
134 return -1;
135 }
136
137 return 0;
138}
139
140static int
141get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page,
142 uint32_t *usage)
143{
144 const uint8_t *ptr = hrd->value;
145 size_t len = hrd->size;
146
147 while (len > 0) {
148 const uint8_t tag = ptr[0];
149
150 ptr++;
151 len--;
152
153 uint8_t key;
154 size_t key_len;
155 uint32_t key_val;
156
157 if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
158 get_key_val(ptr, key_len, &key_val) < 0) {
159 return -1;
160 }
161
162 if (key == 0x4) {
163 *usage_page = key_val;
164 } else if (key == 0x8) {
165 *usage = key_val;
166 }
167
168 ptr += key_len;
169 len -= key_len;
170 }
171
172 return 0;
173}
174
175static int
176get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
177{
178 int fd;
179 int s = -1;
180 int ok = -1;
181
182 if ((fd = open(path, O_RDONLY)) < 0) {
183 fido_log_debug("%s: open", __func__);
184 return -1;
185 }
186
187 if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 ||
188 (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
189 fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__);
190 goto fail;
191 }
192
193 hrd->size = (unsigned)s;
194
195 if (ioctl(fd, HIDIOCGRDESC, hrd) < 0) {
196 fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__);
197 goto fail;
198 }
199
200 ok = 0;
201fail:
202 if (fd != -1)
203 close(fd);
204
205 return ok;
206}
207
208static bool
209is_fido(const struct hid_device_info *hdi)
210{
211 uint32_t usage = 0;
212 uint32_t usage_page = 0;
213 struct hidraw_report_descriptor hrd;
214
215 memset(&hrd, 0, sizeof(hrd));
216
217 if (get_report_descriptor(hdi->path, &hrd) < 0 ||
218 get_usage_info(&hrd, &usage_page, &usage) < 0) {
219 return false;
220 }
221
222 return usage_page == 0xf1d0;
223}
224#elif defined(_WIN32) || defined(__APPLE__)
225static bool
226is_fido(const struct hid_device_info *hdi)
227{
228 return hdi->usage_page == 0xf1d0;
229}
230#else
231static bool
232is_fido(const struct hid_device_info *hdi)
233{
234 (void)hdi;
235 fido_log_debug("%s: assuming FIDO HID", __func__);
236 return true;
237}
238#endif
239
86void * 240void *
87fido_hid_open(const char *path) 241fido_hid_open(const char *path)
88{ 242{
89 return hid_open_path(path); 243 struct hid_hidapi *ctx;
244
245 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
246 return (NULL);
247 }
248
249 if ((ctx->handle = hid_open_path(path)) == NULL) {
250 free(ctx);
251 return (NULL);
252 }
253
254 ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN;
255
256 return ctx;
90} 257}
91 258
92void 259void
93fido_hid_close(void *hid_dev_handle) 260fido_hid_close(void *handle)
94{ 261{
95 hid_close(hid_dev_handle); 262 struct hid_hidapi *ctx = handle;
263
264 hid_close(ctx->handle);
265 free(ctx);
96} 266}
97 267
98int 268int
99fido_hid_read(void *hid_dev_handle, unsigned char *buf, size_t len, int ms) 269fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
100{ 270{
101 return hid_read_timeout(hid_dev_handle, buf, len, ms); 271 struct hid_hidapi *ctx = handle;
272
273 if (len != ctx->report_in_len) {
274 fido_log_debug("%s: len %zu", __func__, len);
275 return -1;
276 }
277
278 return hid_read_timeout(ctx->handle, buf, len, ms);
102} 279}
103 280
104int 281int
105fido_hid_write(void *hid_dev_handle, const unsigned char *buf, size_t len) 282fido_hid_write(void *handle, const unsigned char *buf, size_t len)
106{ 283{
107 return hid_write(hid_dev_handle, buf, len); 284 struct hid_hidapi *ctx = handle;
285
286 if (len != ctx->report_out_len + 1) {
287 fido_log_debug("%s: len %zu", __func__, len);
288 return -1;
289 }
290
291 return hid_write(ctx->handle, buf, len);
108} 292}
109 293
110int 294int
@@ -122,10 +306,8 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
122 return FIDO_OK; /* nothing to do */ 306 return FIDO_OK; /* nothing to do */
123 307
124 for (struct hid_device_info *d = hdi; d != NULL; d = d->next) { 308 for (struct hid_device_info *d = hdi; d != NULL; d = d->next) {
125#if defined(_WIN32) || defined(__APPLE__) 309 if (is_fido(d) == false)
126 if (d->usage_page != 0xf1d0)
127 continue; 310 continue;
128#endif
129 if (copy_info(&devlist[*olen], d) == 0) { 311 if (copy_info(&devlist[*olen], d) == 0) {
130 if (++(*olen) == ilen) 312 if (++(*olen) == ilen)
131 break; 313 break;
@@ -136,3 +318,19 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
136 318
137 return FIDO_OK; 319 return FIDO_OK;
138} 320}
321
322size_t
323fido_hid_report_in_len(void *handle)
324{
325 struct hid_hidapi *ctx = handle;
326
327 return (ctx->report_in_len);
328}
329
330size_t
331fido_hid_report_out_len(void *handle)
332{
333 struct hid_hidapi *ctx = handle;
334
335 return (ctx->report_out_len);
336}
diff --git a/src/hid_linux.c b/src/hid_linux.c
index 99c5afb..9788012 100644
--- a/src/hid_linux.c
+++ b/src/hid_linux.c
@@ -8,16 +8,22 @@
8 8
9#include <sys/ioctl.h> 9#include <sys/ioctl.h>
10#include <linux/hidraw.h> 10#include <linux/hidraw.h>
11#include <linux/input.h>
11 12
13#include <errno.h>
12#include <fcntl.h> 14#include <fcntl.h>
13#include <libudev.h> 15#include <libudev.h>
16#include <poll.h>
14#include <string.h> 17#include <string.h>
15#include <unistd.h> 18#include <unistd.h>
16#include <errno.h>
17 19
18#include "fido.h" 20#include "fido.h"
19 21
20#define REPORT_LEN 65 22struct hid_linux {
23 int fd;
24 size_t report_in_len;
25 size_t report_out_len;
26};
21 27
22static int 28static int
23get_key_len(uint8_t tag, uint8_t *key, size_t *key_len) 29get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
@@ -63,11 +69,8 @@ static int
63get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page, 69get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page,
64 uint32_t *usage) 70 uint32_t *usage)
65{ 71{
66 const uint8_t *ptr; 72 const uint8_t *ptr = hrd->value;
67 size_t len; 73 size_t len = hrd->size;
68
69 ptr = hrd->value;
70 len = hrd->size;
71 74
72 while (len > 0) { 75 while (len > 0) {
73 const uint8_t tag = ptr[0]; 76 const uint8_t tag = ptr[0];
@@ -97,80 +100,113 @@ get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page,
97} 100}
98 101
99static int 102static int
100get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) 103get_report_sizes(const struct hidraw_report_descriptor *hrd,
104 size_t *report_in_len, size_t *report_out_len)
101{ 105{
102 int s = -1; 106 const uint8_t *ptr = hrd->value;
103 int fd; 107 size_t len = hrd->size;
104 int ok = -1; 108 uint32_t report_size = 0;
105 109
106 if ((fd = open(path, O_RDONLY)) < 0) { 110 while (len > 0) {
107 fido_log_debug("%s: open", __func__); 111 const uint8_t tag = ptr[0];
108 return (-1); 112 ptr++;
113 len--;
114
115 uint8_t key;
116 size_t key_len;
117 uint32_t key_val;
118
119 if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
120 get_key_val(ptr, key_len, &key_val) < 0) {
121 return (-1);
122 }
123
124 if (key == 0x94) {
125 report_size = key_val;
126 } else if (key == 0x80) {
127 *report_in_len = (size_t)report_size;
128 } else if (key == 0x90) {
129 *report_out_len = (size_t)report_size;
130 }
131
132 ptr += key_len;
133 len -= key_len;
109 } 134 }
110 135
136 return (0);
137}
138
139static int
140get_report_descriptor(int fd, struct hidraw_report_descriptor *hrd)
141{
142 int s = -1;
143
111 if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 || 144 if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 ||
112 (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { 145 (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
113 fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__); 146 fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__);
114 goto fail; 147 return (-1);
115 } 148 }
116 149
117 hrd->size = s; 150 hrd->size = (unsigned)s;
118 151
119 if (ioctl(fd, HIDIOCGRDESC, hrd) < 0) { 152 if (ioctl(fd, HIDIOCGRDESC, hrd) < 0) {
120 fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__); 153 fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__);
121 goto fail; 154 return (-1);
122 } 155 }
123 156
124 ok = 0; 157 return (0);
125fail:
126 if (fd != -1)
127 close(fd);
128
129 return (ok);
130} 158}
131 159
132static bool 160static bool
133is_fido(const char *path) 161is_fido(const char *path)
134{ 162{
163 int fd;
135 uint32_t usage = 0; 164 uint32_t usage = 0;
136 uint32_t usage_page = 0; 165 uint32_t usage_page = 0;
137 struct hidraw_report_descriptor hrd; 166 struct hidraw_report_descriptor hrd;
138 167
139 memset(&hrd, 0, sizeof(hrd)); 168 memset(&hrd, 0, sizeof(hrd));
140 169
141 if (get_report_descriptor(path, &hrd) < 0 || 170 if ((fd = open(path, O_RDONLY)) == -1) {
171 fido_log_debug("%s: open", __func__);
172 return (false);
173 }
174
175 if (get_report_descriptor(fd, &hrd) < 0 ||
142 get_usage_info(&hrd, &usage_page, &usage) < 0) { 176 get_usage_info(&hrd, &usage_page, &usage) < 0) {
177 close(fd);
143 return (false); 178 return (false);
144 } 179 }
145 180
181 close(fd);
182
146 return (usage_page == 0xf1d0); 183 return (usage_page == 0xf1d0);
147} 184}
148 185
149static int 186static int
150parse_uevent(struct udev_device *dev, int16_t *vendor_id, int16_t *product_id) 187parse_uevent(const char *uevent, int *bus, int16_t *vendor_id,
188 int16_t *product_id)
151{ 189{
152 const char *uevent;
153 char *cp; 190 char *cp;
154 char *p; 191 char *p;
155 char *s; 192 char *s;
156 int ok = -1; 193 int ok = -1;
157 short unsigned int x; 194 short unsigned int x;
158 short unsigned int y; 195 short unsigned int y;
159 196 short unsigned int z;
160 if ((uevent = udev_device_get_sysattr_value(dev, "uevent")) == NULL)
161 return (-1);
162 197
163 if ((s = cp = strdup(uevent)) == NULL) 198 if ((s = cp = strdup(uevent)) == NULL)
164 return (-1); 199 return (-1);
165 200
166 for ((p = strsep(&cp, "\n")); p && *p != '\0'; (p = strsep(&cp, "\n"))) { 201 while ((p = strsep(&cp, "\n")) != NULL && *p != '\0') {
167 if (strncmp(p, "HID_ID=", 7) == 0) { 202 if (strncmp(p, "HID_ID=", 7) == 0) {
168 if (sscanf(p + 7, "%*x:%hx:%hx", &x, &y) == 2) { 203 if (sscanf(p + 7, "%hx:%hx:%hx", &x, &y, &z) == 3) {
169 *vendor_id = (int16_t)x; 204 *bus = (int)x;
170 *product_id = (int16_t)y; 205 *vendor_id = (int16_t)y;
206 *product_id = (int16_t)z;
171 ok = 0; 207 ok = 0;
208 break;
172 } 209 }
173 break;
174 } 210 }
175 } 211 }
176 212
@@ -179,17 +215,36 @@ parse_uevent(struct udev_device *dev, int16_t *vendor_id, int16_t *product_id)
179 return (ok); 215 return (ok);
180} 216}
181 217
218static char *
219get_parent_attr(struct udev_device *dev, const char *subsystem,
220 const char *devtype, const char *attr)
221{
222 struct udev_device *parent;
223 const char *value;
224
225 if ((parent = udev_device_get_parent_with_subsystem_devtype(dev,
226 subsystem, devtype)) == NULL || (value =
227 udev_device_get_sysattr_value(parent, attr)) == NULL)
228 return (NULL);
229
230 return (strdup(value));
231}
232
233static char *
234get_usb_attr(struct udev_device *dev, const char *attr)
235{
236 return (get_parent_attr(dev, "usb", "usb_device", attr));
237}
238
182static int 239static int
183copy_info(fido_dev_info_t *di, struct udev *udev, 240copy_info(fido_dev_info_t *di, struct udev *udev,
184 struct udev_list_entry *udev_entry) 241 struct udev_list_entry *udev_entry)
185{ 242{
186 const char *name; 243 const char *name;
187 const char *path; 244 const char *path;
188 const char *manufacturer; 245 char *uevent = NULL;
189 const char *product;
190 struct udev_device *dev = NULL; 246 struct udev_device *dev = NULL;
191 struct udev_device *hid_parent; 247 int bus = 0;
192 struct udev_device *usb_parent;
193 int ok = -1; 248 int ok = -1;
194 249
195 memset(di, 0, sizeof(*di)); 250 memset(di, 0, sizeof(*di));
@@ -200,28 +255,24 @@ copy_info(fido_dev_info_t *di, struct udev *udev,
200 is_fido(path) == 0) 255 is_fido(path) == 0)
201 goto fail; 256 goto fail;
202 257
203 if ((hid_parent = udev_device_get_parent_with_subsystem_devtype(dev, 258 if ((uevent = get_parent_attr(dev, "hid", NULL, "uevent")) == NULL ||
204 "hid", NULL)) == NULL) 259 parse_uevent(uevent, &bus, &di->vendor_id, &di->product_id) < 0) {
205 goto fail; 260 fido_log_debug("%s: uevent", __func__);
206
207 if ((usb_parent = udev_device_get_parent_with_subsystem_devtype(dev,
208 "usb", "usb_device")) == NULL)
209 goto fail; 261 goto fail;
262 }
210 263
211 if (parse_uevent(hid_parent, &di->vendor_id, &di->product_id) < 0 || 264#ifndef FIDO_HID_ANY
212 (manufacturer = udev_device_get_sysattr_value(usb_parent, 265 if (bus != BUS_USB) {
213 "manufacturer")) == NULL || 266 fido_log_debug("%s: bus", __func__);
214 (product = udev_device_get_sysattr_value(usb_parent,
215 "product")) == NULL)
216 goto fail; 267 goto fail;
268 }
269#endif
217 270
218 di->path = strdup(path); 271 di->path = strdup(path);
219 di->manufacturer = strdup(manufacturer); 272 di->manufacturer = get_usb_attr(dev, "manufacturer");
220 di->product = strdup(product); 273 di->product = get_usb_attr(dev, "product");
221 274
222 if (di->path == NULL || 275 if (di->path == NULL || di->manufacturer == NULL || di->product == NULL)
223 di->manufacturer == NULL ||
224 di->product == NULL)
225 goto fail; 276 goto fail;
226 277
227 ok = 0; 278 ok = 0;
@@ -229,6 +280,8 @@ fail:
229 if (dev != NULL) 280 if (dev != NULL)
230 udev_device_unref(dev); 281 udev_device_unref(dev);
231 282
283 free(uevent);
284
232 if (ok < 0) { 285 if (ok < 0) {
233 free(di->path); 286 free(di->path);
234 free(di->manufacturer); 287 free(di->manufacturer);
@@ -261,9 +314,13 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
261 goto fail; 314 goto fail;
262 315
263 if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 || 316 if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 ||
264 udev_enumerate_scan_devices(udev_enum) < 0 || 317 udev_enumerate_scan_devices(udev_enum) < 0)
265 (udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) 318 goto fail;
319
320 if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) {
321 r = FIDO_OK; /* zero hidraw devices */
266 goto fail; 322 goto fail;
323 }
267 324
268 udev_list_entry_foreach(udev_entry, udev_list) { 325 udev_list_entry_foreach(udev_entry, udev_list) {
269 if (copy_info(&devlist[*olen], udev, udev_entry) == 0) { 326 if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
@@ -291,60 +348,157 @@ fail:
291void * 348void *
292fido_hid_open(const char *path) 349fido_hid_open(const char *path)
293{ 350{
294 int *fd; 351 struct hid_linux *ctx;
352 struct hidraw_report_descriptor hrd;
353
354 if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
355 return (NULL);
295 356
296 if ((fd = malloc(sizeof(*fd))) == NULL || 357 if ((ctx->fd = open(path, O_RDWR)) < 0) {
297 (*fd = open(path, O_RDWR)) < 0) { 358 free(ctx);
298 free(fd);
299 return (NULL); 359 return (NULL);
300 } 360 }
301 361
302 return (fd); 362 if (get_report_descriptor(ctx->fd, &hrd) < 0 || get_report_sizes(&hrd,
363 &ctx->report_in_len, &ctx->report_out_len) < 0 ||
364 ctx->report_in_len == 0 || ctx->report_out_len == 0) {
365 fido_log_debug("%s: using default report sizes", __func__);
366 ctx->report_in_len = CTAP_MAX_REPORT_LEN;
367 ctx->report_out_len = CTAP_MAX_REPORT_LEN;
368 }
369
370 return (ctx);
303} 371}
304 372
305void 373void
306fido_hid_close(void *handle) 374fido_hid_close(void *handle)
307{ 375{
308 int *fd = handle; 376 struct hid_linux *ctx = handle;
377
378 close(ctx->fd);
379 free(ctx);
380}
381
382static int
383timespec_to_ms(const struct timespec *ts, int upper_bound)
384{
385 int64_t x;
386 int64_t y;
387
388 if (ts->tv_sec < 0 || ts->tv_sec > INT64_MAX / 1000LL ||
389 ts->tv_nsec < 0 || ts->tv_nsec / 1000000LL > INT64_MAX)
390 return (upper_bound);
391
392 x = ts->tv_sec * 1000LL;
393 y = ts->tv_nsec / 1000000LL;
394
395 if (INT64_MAX - x < y || x + y > upper_bound)
396 return (upper_bound);
397
398 return (int)(x + y);
399}
400
401static int
402waitfd(int fd, int ms)
403{
404 struct timespec ts_start;
405 struct timespec ts_now;
406 struct timespec ts_delta;
407 struct pollfd pfd;
408 int ms_remain;
409 int r;
410
411 if (ms < 0)
412 return (0);
413
414 memset(&pfd, 0, sizeof(pfd));
415 pfd.events = POLLIN;
416 pfd.fd = fd;
417
418 if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
419 fido_log_debug("%s: clock_gettime: %s", __func__,
420 strerror(errno));
421 return (-1);
422 }
423
424 for (ms_remain = ms; ms_remain > 0;) {
425 if ((r = poll(&pfd, 1, ms_remain)) > 0)
426 return (0);
427 else if (r == 0)
428 break;
429 else if (errno != EINTR) {
430 fido_log_debug("%s: poll: %s", __func__,
431 strerror(errno));
432 return (-1);
433 }
434 /* poll interrupted - subtract time already waited */
435 if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
436 fido_log_debug("%s: clock_gettime: %s", __func__,
437 strerror(errno));
438 return (-1);
439 }
440 timespecsub(&ts_now, &ts_start, &ts_delta);
441 ms_remain = ms - timespec_to_ms(&ts_delta, ms);
442 }
309 443
310 close(*fd); 444 return (-1);
311 free(fd);
312} 445}
313 446
314int 447int
315fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 448fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
316{ 449{
317 int *fd = handle; 450 struct hid_linux *ctx = handle;
318 ssize_t r; 451 ssize_t r;
319 452
320 (void)ms; /* XXX */ 453 if (len != ctx->report_in_len) {
454 fido_log_debug("%s: len %zu", __func__, len);
455 return (-1);
456 }
321 457
322 if (len != REPORT_LEN - 1) { 458 if (waitfd(ctx->fd, ms) < 0) {
323 fido_log_debug("%s: invalid len", __func__); 459 fido_log_debug("%s: fd not ready", __func__);
324 return (-1); 460 return (-1);
325 } 461 }
326 462
327 if ((r = read(*fd, buf, len)) < 0 || r != REPORT_LEN - 1) 463 if ((r = read(ctx->fd, buf, len)) < 0 || (size_t)r != len) {
464 fido_log_debug("%s: read", __func__);
328 return (-1); 465 return (-1);
466 }
329 467
330 return (REPORT_LEN - 1); 468 return ((int)r);
331} 469}
332 470
333int 471int
334fido_hid_write(void *handle, const unsigned char *buf, size_t len) 472fido_hid_write(void *handle, const unsigned char *buf, size_t len)
335{ 473{
336 int *fd = handle; 474 struct hid_linux *ctx = handle;
337 ssize_t r; 475 ssize_t r;
338 476
339 if (len != REPORT_LEN) { 477 if (len != ctx->report_out_len + 1) {
340 fido_log_debug("%s: invalid len", __func__); 478 fido_log_debug("%s: len %zu", __func__, len);
341 return (-1); 479 return (-1);
342 } 480 }
343 481
344 if ((r = write(*fd, buf, len)) < 0 || r != REPORT_LEN) { 482 if ((r = write(ctx->fd, buf, len)) < 0 || (size_t)r != len) {
345 fido_log_debug("%s: write", __func__); 483 fido_log_debug("%s: write", __func__);
346 return (-1); 484 return (-1);
347 } 485 }
348 486
349 return (REPORT_LEN); 487 return ((int)r);
488}
489
490size_t
491fido_hid_report_in_len(void *handle)
492{
493 struct hid_linux *ctx = handle;
494
495 return (ctx->report_in_len);
496}
497
498size_t
499fido_hid_report_out_len(void *handle)
500{
501 struct hid_linux *ctx = handle;
502
503 return (ctx->report_out_len);
350} 504}
diff --git a/src/hid_openbsd.c b/src/hid_openbsd.c
index 2b31dba..319f7b8 100644
--- a/src/hid_openbsd.c
+++ b/src/hid_openbsd.c
@@ -8,19 +8,16 @@
8 8
9#include <sys/ioctl.h> 9#include <sys/ioctl.h>
10#include <dev/usb/usb.h> 10#include <dev/usb/usb.h>
11#include <dev/usb/usbhid.h>
12 11
13#include <errno.h> 12#include <errno.h>
14#include <fcntl.h> 13#include <fcntl.h>
15#include <string.h> 14#include <string.h>
16#include <unistd.h> 15#include <unistd.h>
17#include <usbhid.h>
18#include <poll.h> 16#include <poll.h>
19 17
20#include "fido.h" 18#include "fido.h"
21 19
22#define MAX_UHID 64 20#define MAX_UHID 64
23#define MAX_REPORT_LEN (sizeof(((struct usb_ctl_report *)(NULL))->ucr_data))
24 21
25struct hid_openbsd { 22struct hid_openbsd {
26 int fd; 23 int fd;
@@ -33,11 +30,8 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
33{ 30{
34 size_t i; 31 size_t i;
35 char path[64]; 32 char path[64];
36 int is_fido, fd; 33 int fd;
37 struct usb_device_info udi; 34 struct usb_device_info udi;
38 report_desc_t rdesc = NULL;
39 hid_data_t hdata = NULL;
40 hid_item_t hitem;
41 fido_dev_info_t *di; 35 fido_dev_info_t *di;
42 36
43 if (ilen == 0) 37 if (ilen == 0)
@@ -47,7 +41,7 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
47 return (FIDO_ERR_INVALID_ARGUMENT); 41 return (FIDO_ERR_INVALID_ARGUMENT);
48 42
49 for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) { 43 for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
50 snprintf(path, sizeof(path), "/dev/uhid%zu", i); 44 snprintf(path, sizeof(path), "/dev/fido/%zu", i);
51 if ((fd = open(path, O_RDWR)) == -1) { 45 if ((fd = open(path, O_RDWR)) == -1) {
52 if (errno != ENOENT && errno != ENXIO) { 46 if (errno != ENOENT && errno != ENXIO) {
53 fido_log_debug("%s: open %s: %s", __func__, 47 fido_log_debug("%s: open %s: %s", __func__,
@@ -55,6 +49,7 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
55 } 49 }
56 continue; 50 continue;
57 } 51 }
52
58 memset(&udi, 0, sizeof(udi)); 53 memset(&udi, 0, sizeof(udi));
59 if (ioctl(fd, USB_GET_DEVICEINFO, &udi) != 0) { 54 if (ioctl(fd, USB_GET_DEVICEINFO, &udi) != 0) {
60 fido_log_debug("%s: get device info %s: %s", __func__, 55 fido_log_debug("%s: get device info %s: %s", __func__,
@@ -62,35 +57,8 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
62 close(fd); 57 close(fd);
63 continue; 58 continue;
64 } 59 }
65 if ((rdesc = hid_get_report_desc(fd)) == NULL) {
66 fido_log_debug("%s: failed to get report descriptor: %s",
67 __func__, path);
68 close(fd);
69 continue;
70 }
71 if ((hdata = hid_start_parse(rdesc,
72 1<<hid_collection, -1)) == NULL) {
73 fido_log_debug("%s: failed to parse report descriptor: %s",
74 __func__, path);
75 hid_dispose_report_desc(rdesc);
76 close(fd);
77 continue;
78 }
79 is_fido = 0;
80 for (is_fido = 0; !is_fido;) {
81 memset(&hitem, 0, sizeof(hitem));
82 if (hid_get_item(hdata, &hitem) <= 0)
83 break;
84 if ((hitem._usage_page & 0xFFFF0000) == 0xf1d00000)
85 is_fido = 1;
86 }
87 hid_end_parse(hdata);
88 hid_dispose_report_desc(rdesc);
89 close(fd); 60 close(fd);
90 61
91 if (!is_fido)
92 continue;
93
94 fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x", 62 fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x",
95 __func__, path, udi.udi_bus, udi.udi_addr); 63 __func__, path, udi.udi_bus, udi.udi_addr);
96 fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"", 64 fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"",
@@ -116,8 +84,8 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
116 explicit_bzero(di, sizeof(*di)); 84 explicit_bzero(di, sizeof(*di));
117 return FIDO_ERR_INTERNAL; 85 return FIDO_ERR_INTERNAL;
118 } 86 }
119 di->vendor_id = udi.udi_vendorNo; 87 di->vendor_id = (int16_t)udi.udi_vendorNo;
120 di->product_id = udi.udi_productNo; 88 di->product_id = (int16_t)udi.udi_productNo;
121 (*olen)++; 89 (*olen)++;
122 } 90 }
123 91
@@ -184,42 +152,15 @@ void *
184fido_hid_open(const char *path) 152fido_hid_open(const char *path)
185{ 153{
186 struct hid_openbsd *ret = NULL; 154 struct hid_openbsd *ret = NULL;
187 report_desc_t rdesc = NULL;
188 int len, usb_report_id = 0;
189 155
190 if ((ret = calloc(1, sizeof(*ret))) == NULL || 156 if ((ret = calloc(1, sizeof(*ret))) == NULL ||
191 (ret->fd = open(path, O_RDWR)) < 0) { 157 (ret->fd = open(path, O_RDWR)) < 0) {
192 free(ret); 158 free(ret);
193 return (NULL); 159 return (NULL);
194 } 160 }
195 if (ioctl(ret->fd, USB_GET_REPORT_ID, &usb_report_id) != 0) { 161 ret->report_in_len = ret->report_out_len = CTAP_MAX_REPORT_LEN;
196 fido_log_debug("%s: failed to get report ID: %s", __func__, 162 fido_log_debug("%s: inlen = %zu outlen = %zu", __func__,
197 strerror(errno)); 163 ret->report_in_len, ret->report_out_len);
198 goto fail;
199 }
200 if ((rdesc = hid_get_report_desc(ret->fd)) == NULL) {
201 fido_log_debug("%s: failed to get report descriptor", __func__);
202 goto fail;
203 }
204 if ((len = hid_report_size(rdesc, hid_input, usb_report_id)) <= 0 ||
205 (size_t)len > MAX_REPORT_LEN) {
206 fido_log_debug("%s: bad input report size %d", __func__, len);
207 goto fail;
208 }
209 ret->report_in_len = (size_t)len;
210 if ((len = hid_report_size(rdesc, hid_output, usb_report_id)) <= 0 ||
211 (size_t)len > MAX_REPORT_LEN) {
212 fido_log_debug("%s: bad output report size %d", __func__, len);
213 fail:
214 hid_dispose_report_desc(rdesc);
215 close(ret->fd);
216 free(ret);
217 return NULL;
218 }
219 ret->report_out_len = (size_t)len;
220 hid_dispose_report_desc(rdesc);
221 fido_log_debug("%s: USB report ID %d, inlen = %zu outlen = %zu",
222 __func__, usb_report_id, ret->report_in_len, ret->report_out_len);
223 164
224 /* 165 /*
225 * OpenBSD (as of 201910) has a bug that causes it to lose 166 * OpenBSD (as of 201910) has a bug that causes it to lose
@@ -281,3 +222,19 @@ fido_hid_write(void *handle, const unsigned char *buf, size_t len)
281 } 222 }
282 return ((int)len); 223 return ((int)len);
283} 224}
225
226size_t
227fido_hid_report_in_len(void *handle)
228{
229 struct hid_openbsd *ctx = handle;
230
231 return (ctx->report_in_len);
232}
233
234size_t
235fido_hid_report_out_len(void *handle)
236{
237 struct hid_openbsd *ctx = handle;
238
239 return (ctx->report_out_len);
240}
diff --git a/src/hid_osx.c b/src/hid_osx.c
index 5c40747..6be5cd7 100644
--- a/src/hid_osx.c
+++ b/src/hid_osx.c
@@ -19,11 +19,13 @@
19 19
20#include "fido.h" 20#include "fido.h"
21 21
22#define REPORT_LEN 65 22struct hid_osx {
23
24struct dev {
25 IOHIDDeviceRef ref; 23 IOHIDDeviceRef ref;
26 CFStringRef loop_id; 24 CFStringRef loop_id;
25 int report_pipe[2];
26 size_t report_in_len;
27 size_t report_out_len;
28 unsigned char report[CTAP_MAX_REPORT_LEN];
27}; 29};
28 30
29static int 31static int
@@ -64,7 +66,8 @@ get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len)
64 return (-1); 66 return (-1);
65 } 67 }
66 68
67 if (CFStringGetCString(ref, buf, len, kCFStringEncodingUTF8) == false) { 69 if (CFStringGetCString(ref, buf, (long)len,
70 kCFStringEncodingUTF8) == false) {
68 fido_log_debug("%s: CFStringGetCString", __func__); 71 fido_log_debug("%s: CFStringGetCString", __func__);
69 return (-1); 72 return (-1);
70 } 73 }
@@ -72,30 +75,35 @@ get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len)
72 return (0); 75 return (0);
73} 76}
74 77
75static bool 78static int
76is_fido(IOHIDDeviceRef dev) 79get_report_len(IOHIDDeviceRef dev, int dir, size_t *report_len)
77{ 80{
78 uint32_t usage_page; 81 CFStringRef key;
79 int32_t report_len; 82 int32_t v;
80 83
81 if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey), 84 if (dir == 0)
82 (int32_t *)&usage_page) != 0 || usage_page != 0xf1d0) 85 key = CFSTR(kIOHIDMaxInputReportSizeKey);
83 return (false); 86 else
87 key = CFSTR(kIOHIDMaxOutputReportSizeKey);
84 88
85 if (get_int32(dev, CFSTR(kIOHIDMaxInputReportSizeKey), 89 if (get_int32(dev, key, &v) < 0) {
86 &report_len) < 0 || report_len != REPORT_LEN - 1) { 90 fido_log_debug("%s: get_int32/%d", __func__, dir);
87 fido_log_debug("%s: unsupported report len", __func__); 91 return (-1);
88 return (false);
89 } 92 }
90 93
91 return (true); 94 if ((*report_len = (size_t)v) > CTAP_MAX_REPORT_LEN) {
95 fido_log_debug("%s: report_len=%zu", __func__, *report_len);
96 return (-1);
97 }
98
99 return (0);
92} 100}
93 101
94static int 102static int
95get_id(IOHIDDeviceRef dev, int16_t *vendor_id, int16_t *product_id) 103get_id(IOHIDDeviceRef dev, int16_t *vendor_id, int16_t *product_id)
96{ 104{
97 int32_t vendor; 105 int32_t vendor;
98 int32_t product; 106 int32_t product;
99 107
100 if (get_int32(dev, CFSTR(kIOHIDVendorIDKey), &vendor) < 0 || 108 if (get_int32(dev, CFSTR(kIOHIDVendorIDKey), &vendor) < 0 ||
101 vendor > UINT16_MAX) { 109 vendor > UINT16_MAX) {
@@ -175,6 +183,31 @@ get_path(IOHIDDeviceRef dev)
175 return (strdup(path)); 183 return (strdup(path));
176} 184}
177 185
186static bool
187is_fido(IOHIDDeviceRef dev)
188{
189 char buf[32];
190 uint32_t usage_page;
191
192 if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey),
193 (int32_t *)&usage_page) < 0 || usage_page != 0xf1d0)
194 return (false);
195
196 if (get_utf8(dev, CFSTR(kIOHIDTransportKey), buf, sizeof(buf)) < 0) {
197 fido_log_debug("%s: get_utf8 transport", __func__);
198 return (false);
199 }
200
201#ifndef FIDO_HID_ANY
202 if (strcasecmp(buf, "usb") != 0) {
203 fido_log_debug("%s: transport", __func__);
204 return (false);
205 }
206#endif
207
208 return (true);
209}
210
178static int 211static int
179copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev) 212copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev)
180{ 213{
@@ -199,11 +232,12 @@ copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev)
199int 232int
200fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 233fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
201{ 234{
202 IOHIDManagerRef manager = NULL; 235 IOHIDManagerRef manager = NULL;
203 CFSetRef devset = NULL; 236 CFSetRef devset = NULL;
204 CFIndex devcnt; 237 size_t devcnt;
205 IOHIDDeviceRef *devs = NULL; 238 CFIndex n;
206 int r = FIDO_ERR_INTERNAL; 239 IOHIDDeviceRef *devs = NULL;
240 int r = FIDO_ERR_INTERNAL;
207 241
208 *olen = 0; 242 *olen = 0;
209 243
@@ -226,11 +260,13 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
226 goto fail; 260 goto fail;
227 } 261 }
228 262
229 if ((devcnt = CFSetGetCount(devset)) < 0) { 263 if ((n = CFSetGetCount(devset)) < 0) {
230 fido_log_debug("%s: CFSetGetCount", __func__); 264 fido_log_debug("%s: CFSetGetCount", __func__);
231 goto fail; 265 goto fail;
232 } 266 }
233 267
268 devcnt = (size_t)n;
269
234 if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) { 270 if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) {
235 fido_log_debug("%s: calloc", __func__); 271 fido_log_debug("%s: calloc", __func__);
236 goto fail; 272 goto fail;
@@ -238,7 +274,7 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
238 274
239 CFSetGetValues(devset, (void *)devs); 275 CFSetGetValues(devset, (void *)devs);
240 276
241 for (CFIndex i = 0; i < devcnt; i++) { 277 for (size_t i = 0; i < devcnt; i++) {
242 if (copy_info(&devlist[*olen], devs[i]) == 0) { 278 if (copy_info(&devlist[*olen], devs[i]) == 0) {
243 devlist[*olen].io = (fido_dev_io_t) { 279 devlist[*olen].io = (fido_dev_io_t) {
244 fido_hid_open, 280 fido_hid_open,
@@ -263,157 +299,258 @@ fail:
263 return (r); 299 return (r);
264} 300}
265 301
302static void
303report_callback(void *context, IOReturn result, void *dev, IOHIDReportType type,
304 uint32_t id, uint8_t *ptr, CFIndex len)
305{
306 struct hid_osx *ctx = context;
307 ssize_t r;
308
309 (void)dev;
310
311 if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput ||
312 id != 0 || len < 0 || (size_t)len != ctx->report_in_len) {
313 fido_log_debug("%s: io error", __func__);
314 return;
315 }
316
317 if ((r = write(ctx->report_pipe[1], ptr, (size_t)len)) < 0 ||
318 (size_t)r != (size_t)len) {
319 fido_log_debug("%s: write", __func__);
320 return;
321 }
322}
323
324static void
325removal_callback(void *context, IOReturn result, void *sender)
326{
327 (void)context;
328 (void)result;
329 (void)sender;
330
331 CFRunLoopStop(CFRunLoopGetMain());
332}
333
334static int
335set_nonblock(int fd)
336{
337 int flags;
338
339 if ((flags = fcntl(fd, F_GETFL)) == -1) {
340 fido_log_debug("%s: fcntl F_GETFL", __func__);
341 return (-1);
342 }
343
344 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
345 fido_log_debug("%s: fcntl, F_SETFL", __func__);
346 return (-1);
347 }
348
349 return (0);
350}
351
352static int
353disable_sigpipe(int fd)
354{
355 int disabled = 1;
356
357 if (fcntl(fd, F_SETNOSIGPIPE, &disabled) == -1) {
358 fido_log_debug("%s: fcntl F_SETNOSIGPIPE", __func__);
359 return (-1);
360 }
361
362 return (0);
363}
364
266void * 365void *
267fido_hid_open(const char *path) 366fido_hid_open(const char *path)
268{ 367{
368 struct hid_osx *ctx;
269 io_registry_entry_t entry = MACH_PORT_NULL; 369 io_registry_entry_t entry = MACH_PORT_NULL;
270 struct dev *dev = NULL; 370 char loop_id[32];
271 int ok = -1; 371 int ok = -1;
272 int r; 372 int r;
273 char loop_id[32];
274 373
275 if ((dev = calloc(1, sizeof(*dev))) == NULL) { 374 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
276 fido_log_debug("%s: calloc", __func__); 375 fido_log_debug("%s: calloc", __func__);
277 goto fail; 376 goto fail;
278 } 377 }
279 378
379 ctx->report_pipe[0] = -1;
380 ctx->report_pipe[1] = -1;
381
382 if (pipe(ctx->report_pipe) == -1) {
383 fido_log_debug("%s: pipe", __func__);
384 goto fail;
385 }
386
387 if (set_nonblock(ctx->report_pipe[0]) < 0 ||
388 set_nonblock(ctx->report_pipe[1]) < 0) {
389 fido_log_debug("%s: set_nonblock", __func__);
390 goto fail;
391 }
392
393 if (disable_sigpipe(ctx->report_pipe[1]) < 0) {
394 fido_log_debug("%s: disable_sigpipe", __func__);
395 goto fail;
396 }
397
280 if ((entry = IORegistryEntryFromPath(kIOMasterPortDefault, 398 if ((entry = IORegistryEntryFromPath(kIOMasterPortDefault,
281 path)) == MACH_PORT_NULL) { 399 path)) == MACH_PORT_NULL) {
282 fido_log_debug("%s: IORegistryEntryFromPath", __func__); 400 fido_log_debug("%s: IORegistryEntryFromPath", __func__);
283 goto fail; 401 goto fail;
284 } 402 }
285 403
286 if ((dev->ref = IOHIDDeviceCreate(kCFAllocatorDefault, 404 if ((ctx->ref = IOHIDDeviceCreate(kCFAllocatorDefault,
287 entry)) == NULL) { 405 entry)) == NULL) {
288 fido_log_debug("%s: IOHIDDeviceCreate", __func__); 406 fido_log_debug("%s: IOHIDDeviceCreate", __func__);
289 goto fail; 407 goto fail;
290 } 408 }
291 409
292 if (IOHIDDeviceOpen(dev->ref, 410 if (get_report_len(ctx->ref, 0, &ctx->report_in_len) < 0 ||
411 get_report_len(ctx->ref, 1, &ctx->report_out_len) < 0) {
412 fido_log_debug("%s: get_report_len", __func__);
413 goto fail;
414 }
415
416 if (ctx->report_in_len > sizeof(ctx->report)) {
417 fido_log_debug("%s: report_in_len=%zu", __func__,
418 ctx->report_in_len);
419 goto fail;
420 }
421
422 if (IOHIDDeviceOpen(ctx->ref,
293 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) { 423 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) {
294 fido_log_debug("%s: IOHIDDeviceOpen", __func__); 424 fido_log_debug("%s: IOHIDDeviceOpen", __func__);
295 goto fail; 425 goto fail;
296 } 426 }
297 427
298 if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p", 428 if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p",
299 (void *)dev->ref)) < 0 || (size_t)r >= sizeof(loop_id)) { 429 (void *)ctx->ref)) < 0 || (size_t)r >= sizeof(loop_id)) {
300 fido_log_debug("%s: snprintf", __func__); 430 fido_log_debug("%s: snprintf", __func__);
301 goto fail; 431 goto fail;
302 } 432 }
303 433
304 if ((dev->loop_id = CFStringCreateWithCString(NULL, loop_id, 434 if ((ctx->loop_id = CFStringCreateWithCString(NULL, loop_id,
305 kCFStringEncodingASCII)) == NULL) { 435 kCFStringEncodingASCII)) == NULL) {
306 fido_log_debug("%s: CFStringCreateWithCString", __func__); 436 fido_log_debug("%s: CFStringCreateWithCString", __func__);
307 goto fail; 437 goto fail;
308 } 438 }
309 439
440 IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report,
441 (long)ctx->report_in_len, &report_callback, ctx);
442 IOHIDDeviceRegisterRemovalCallback(ctx->ref, &removal_callback, ctx);
443 IOHIDDeviceScheduleWithRunLoop(ctx->ref, CFRunLoopGetMain(),
444 ctx->loop_id);
445
310 ok = 0; 446 ok = 0;
311fail: 447fail:
312 if (entry != MACH_PORT_NULL) 448 if (entry != MACH_PORT_NULL)
313 IOObjectRelease(entry); 449 IOObjectRelease(entry);
314 450
315 if (ok < 0 && dev != NULL) { 451 if (ok < 0 && ctx != NULL) {
316 if (dev->ref != NULL) 452 if (ctx->ref != NULL)
317 CFRelease(dev->ref); 453 CFRelease(ctx->ref);
318 if (dev->loop_id != NULL) 454 if (ctx->loop_id != NULL)
319 CFRelease(dev->loop_id); 455 CFRelease(ctx->loop_id);
320 free(dev); 456 if (ctx->report_pipe[0] != -1)
321 dev = NULL; 457 close(ctx->report_pipe[0]);
458 if (ctx->report_pipe[1] != -1)
459 close(ctx->report_pipe[1]);
460 free(ctx);
461 ctx = NULL;
322 } 462 }
323 463
324 return (dev); 464 return (ctx);
325} 465}
326 466
327void 467void
328fido_hid_close(void *handle) 468fido_hid_close(void *handle)
329{ 469{
330 struct dev *dev = handle; 470 struct hid_osx *ctx = handle;
471
472 IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report,
473 (long)ctx->report_in_len, NULL, ctx);
474 IOHIDDeviceRegisterRemovalCallback(ctx->ref, NULL, NULL);
475 IOHIDDeviceUnscheduleFromRunLoop(ctx->ref, CFRunLoopGetMain(),
476 ctx->loop_id);
331 477
332 if (IOHIDDeviceClose(dev->ref, 478 if (IOHIDDeviceClose(ctx->ref,
333 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) 479 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess)
334 fido_log_debug("%s: IOHIDDeviceClose", __func__); 480 fido_log_debug("%s: IOHIDDeviceClose", __func__);
335 481
336 CFRelease(dev->ref); 482 CFRelease(ctx->ref);
337 CFRelease(dev->loop_id); 483 CFRelease(ctx->loop_id);
338
339 free(dev);
340}
341 484
342static void 485 explicit_bzero(ctx->report, sizeof(ctx->report));
343read_callback(void *context, IOReturn result, void *dev, IOHIDReportType type, 486 close(ctx->report_pipe[0]);
344 uint32_t report_id, uint8_t *report, CFIndex report_len) 487 close(ctx->report_pipe[1]);
345{
346 (void)context;
347 (void)dev;
348 (void)report;
349 488
350 if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput || 489 free(ctx);
351 report_id != 0 || report_len != REPORT_LEN - 1) {
352 fido_log_debug("%s: io error", __func__);
353 }
354}
355
356static void
357removal_callback(void *context, IOReturn result, void *sender)
358{
359 (void)context;
360 (void)result;
361 (void)sender;
362
363 CFRunLoopStop(CFRunLoopGetCurrent());
364} 490}
365 491
366int 492int
367fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 493fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
368{ 494{
369 struct dev *dev = handle; 495 struct hid_osx *ctx = handle;
370 CFRunLoopRunResult r; 496 ssize_t r;
371 497
372 (void)ms; /* XXX */ 498 explicit_bzero(buf, len);
499 explicit_bzero(ctx->report, sizeof(ctx->report));
373 500
374 if (len != REPORT_LEN - 1) { 501 if (len != ctx->report_in_len || len > sizeof(ctx->report)) {
375 fido_log_debug("%s: invalid len", __func__); 502 fido_log_debug("%s: len %zu", __func__, len);
376 return (-1); 503 return (-1);
377 } 504 }
378 505
379 explicit_bzero(buf, len); 506 if (ms == -1)
380 507 ms = 5000; /* wait 5 seconds by default */
381 IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len,
382 &read_callback, NULL);
383 IOHIDDeviceRegisterRemovalCallback(dev->ref, &removal_callback, dev);
384 IOHIDDeviceScheduleWithRunLoop(dev->ref, CFRunLoopGetCurrent(),
385 dev->loop_id);
386 508
387 r = CFRunLoopRunInMode(dev->loop_id, 0.3, true); 509 if (CFRunLoopGetCurrent() != CFRunLoopGetMain())
510 fido_log_debug("%s: CFRunLoopGetCurrent != CFRunLoopGetMain",
511 __func__);
388 512
389 IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len, NULL, NULL); 513 CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0, true);
390 IOHIDDeviceRegisterRemovalCallback(dev->ref, NULL, NULL);
391 IOHIDDeviceUnscheduleFromRunLoop(dev->ref, CFRunLoopGetCurrent(),
392 dev->loop_id);
393 514
394 if (r != kCFRunLoopRunHandledSource) { 515 if ((r = read(ctx->report_pipe[0], buf, len)) < 0 || (size_t)r != len) {
395 fido_log_debug("%s: CFRunLoopRunInMode=%d", __func__, (int)r); 516 fido_log_debug("%s: read", __func__);
396 return (-1); 517 return (-1);
397 } 518 }
398 519
399 return (REPORT_LEN - 1); 520 return ((int)len);
400} 521}
401 522
402int 523int
403fido_hid_write(void *handle, const unsigned char *buf, size_t len) 524fido_hid_write(void *handle, const unsigned char *buf, size_t len)
404{ 525{
405 struct dev *dev = handle; 526 struct hid_osx *ctx = handle;
406 527
407 if (len != REPORT_LEN) { 528 if (len != ctx->report_out_len + 1 || len > LONG_MAX) {
408 fido_log_debug("%s: invalid len", __func__); 529 fido_log_debug("%s: len %zu", __func__, len);
409 return (-1); 530 return (-1);
410 } 531 }
411 532
412 if (IOHIDDeviceSetReport(dev->ref, kIOHIDReportTypeOutput, 0, buf + 1, 533 if (IOHIDDeviceSetReport(ctx->ref, kIOHIDReportTypeOutput, 0, buf + 1,
413 len - 1) != kIOReturnSuccess) { 534 (long)(len - 1)) != kIOReturnSuccess) {
414 fido_log_debug("%s: IOHIDDeviceSetReport", __func__); 535 fido_log_debug("%s: IOHIDDeviceSetReport", __func__);
415 return (-1); 536 return (-1);
416 } 537 }
417 538
418 return (REPORT_LEN); 539 return ((int)len);
540}
541
542size_t
543fido_hid_report_in_len(void *handle)
544{
545 struct hid_osx *ctx = handle;
546
547 return (ctx->report_in_len);
548}
549
550size_t
551fido_hid_report_out_len(void *handle)
552{
553 struct hid_osx *ctx = handle;
554
555 return (ctx->report_out_len);
419} 556}
diff --git a/src/hid_win.c b/src/hid_win.c
index f970589..018b4d9 100644
--- a/src/hid_win.c
+++ b/src/hid_win.c
@@ -14,19 +14,39 @@
14#include <windows.h> 14#include <windows.h>
15#include <setupapi.h> 15#include <setupapi.h>
16#include <initguid.h> 16#include <initguid.h>
17#include <devpkey.h>
18#include <devpropdef.h>
17#include <hidclass.h> 19#include <hidclass.h>
18#include <hidsdi.h> 20#include <hidsdi.h>
19 21
20#include "fido.h" 22#include "fido.h"
21 23
22#define REPORT_LEN 65 24#if defined(__MINGW32__) && __MINGW64_VERSION_MAJOR < 6
25WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO,
26 PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE,
27 DWORD, PDWORD, DWORD);
28#endif
29
30#if defined(__MINGW32__)
31DEFINE_DEVPROPKEY(DEVPKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97,
32 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8);
33#endif
34
35struct hid_win {
36 HANDLE dev;
37 OVERLAPPED overlap;
38 int report_pending;
39 size_t report_in_len;
40 size_t report_out_len;
41 unsigned char report[1 + CTAP_MAX_REPORT_LEN];
42};
23 43
24static bool 44static bool
25is_fido(HANDLE dev) 45is_fido(HANDLE dev)
26{ 46{
27 PHIDP_PREPARSED_DATA data = NULL; 47 PHIDP_PREPARSED_DATA data = NULL;
28 HIDP_CAPS caps; 48 HIDP_CAPS caps;
29 uint16_t usage_page = 0; 49 int fido = 0;
30 50
31 if (HidD_GetPreparsedData(dev, &data) == false) { 51 if (HidD_GetPreparsedData(dev, &data) == false) {
32 fido_log_debug("%s: HidD_GetPreparsedData", __func__); 52 fido_log_debug("%s: HidD_GetPreparsedData", __func__);
@@ -38,18 +58,48 @@ is_fido(HANDLE dev)
38 goto fail; 58 goto fail;
39 } 59 }
40 60
41 if (caps.OutputReportByteLength != REPORT_LEN || 61 fido = (uint16_t)caps.UsagePage == 0xf1d0;
42 caps.InputReportByteLength != REPORT_LEN) { 62fail:
43 fido_log_debug("%s: unsupported report len", __func__); 63 if (data != NULL)
64 HidD_FreePreparsedData(data);
65
66 return (fido);
67}
68
69static int
70get_report_len(HANDLE dev, int dir, size_t *report_len)
71{
72 PHIDP_PREPARSED_DATA data = NULL;
73 HIDP_CAPS caps;
74 USHORT v;
75 int ok = -1;
76
77 if (HidD_GetPreparsedData(dev, &data) == false) {
78 fido_log_debug("%s: HidD_GetPreparsedData/%d", __func__, dir);
44 goto fail; 79 goto fail;
45 } 80 }
46 81
47 usage_page = caps.UsagePage; 82 if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
83 fido_log_debug("%s: HidP_GetCaps/%d", __func__, dir);
84 goto fail;
85 }
86
87 if (dir == 0)
88 v = caps.InputReportByteLength;
89 else
90 v = caps.OutputReportByteLength;
91
92 if ((*report_len = (size_t)v) == 0) {
93 fido_log_debug("%s: report_len == 0", __func__);
94 goto fail;
95 }
96
97 ok = 0;
48fail: 98fail:
49 if (data != NULL) 99 if (data != NULL)
50 HidD_FreePreparsedData(data); 100 HidD_FreePreparsedData(data);
51 101
52 return (usage_page == 0xf1d0); 102 return (ok);
53} 103}
54 104
55static int 105static int
@@ -59,13 +109,14 @@ get_int(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
59 109
60 attr.Size = sizeof(attr); 110 attr.Size = sizeof(attr);
61 111
62 if (HidD_GetAttributes(dev, &attr) == false) { 112 if (HidD_GetAttributes(dev, &attr) == false ||
113 attr.VendorID > INT16_MAX || attr.ProductID > INT16_MAX) {
63 fido_log_debug("%s: HidD_GetAttributes", __func__); 114 fido_log_debug("%s: HidD_GetAttributes", __func__);
64 return (-1); 115 return (-1);
65 } 116 }
66 117
67 *vendor_id = attr.VendorID; 118 *vendor_id = (int16_t)attr.VendorID;
68 *product_id = attr.ProductID; 119 *product_id = (int16_t)attr.ProductID;
69 120
70 return (0); 121 return (0);
71} 122}
@@ -91,7 +142,7 @@ get_str(HANDLE dev, char **manufacturer, char **product)
91 goto fail; 142 goto fail;
92 } 143 }
93 144
94 if ((*manufacturer = malloc(utf8_len)) == NULL) { 145 if ((*manufacturer = malloc((size_t)utf8_len)) == NULL) {
95 fido_log_debug("%s: malloc", __func__); 146 fido_log_debug("%s: malloc", __func__);
96 goto fail; 147 goto fail;
97 } 148 }
@@ -113,7 +164,7 @@ get_str(HANDLE dev, char **manufacturer, char **product)
113 goto fail; 164 goto fail;
114 } 165 }
115 166
116 if ((*product = malloc(utf8_len)) == NULL) { 167 if ((*product = malloc((size_t)utf8_len)) == NULL) {
117 fido_log_debug("%s: malloc", __func__); 168 fido_log_debug("%s: malloc", __func__);
118 goto fail; 169 goto fail;
119 } 170 }
@@ -136,25 +187,138 @@ fail:
136 return (ok); 187 return (ok);
137} 188}
138 189
190static char *
191get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *ifdata)
192{
193 SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL;
194 char *path = NULL;
195 DWORD len = 0;
196
197 /*
198 * "Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail
199 * with a NULL DeviceInterfaceDetailData pointer, a
200 * DeviceInterfaceDetailDataSize of zero, and a valid RequiredSize
201 * variable. In response to such a call, this function returns the
202 * required buffer size at RequiredSize and fails with GetLastError
203 * returning ERROR_INSUFFICIENT_BUFFER."
204 */
205 if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, NULL, 0, &len,
206 NULL) != false || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
207 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
208 __func__);
209 goto fail;
210 }
211
212 if ((ifdetail = malloc(len)) == NULL) {
213 fido_log_debug("%s: malloc", __func__);
214 goto fail;
215 }
216
217 ifdetail->cbSize = sizeof(*ifdetail);
218
219 if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, ifdetail, len,
220 NULL, NULL) == false) {
221 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
222 __func__);
223 goto fail;
224 }
225
226 if ((path = strdup(ifdetail->DevicePath)) == NULL) {
227 fido_log_debug("%s: strdup", __func__);
228 goto fail;
229 }
230
231fail:
232 free(ifdetail);
233
234 return (path);
235}
236
237#ifndef FIDO_HID_ANY
238static bool
239hid_ok(HDEVINFO devinfo, DWORD idx)
240{
241 SP_DEVINFO_DATA devinfo_data;
242 wchar_t *parent = NULL;
243 DWORD parent_type = DEVPROP_TYPE_STRING;
244 DWORD len = 0;
245 bool ok = false;
246
247 memset(&devinfo_data, 0, sizeof(devinfo_data));
248 devinfo_data.cbSize = sizeof(devinfo_data);
249
250 if (SetupDiEnumDeviceInfo(devinfo, idx, &devinfo_data) == false) {
251 fido_log_debug("%s: SetupDiEnumDeviceInfo", __func__);
252 goto fail;
253 }
254
255 if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
256 &DEVPKEY_Device_Parent, &parent_type, NULL, 0, &len, 0) != false ||
257 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
258 fido_log_debug("%s: SetupDiGetDevicePropertyW 1", __func__);
259 goto fail;
260 }
261
262 if ((parent = malloc(len)) == NULL) {
263 fido_log_debug("%s: malloc", __func__);
264 goto fail;
265 }
266
267 if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
268 &DEVPKEY_Device_Parent, &parent_type, (PBYTE)parent, len, NULL,
269 0) == false) {
270 fido_log_debug("%s: SetupDiGetDevicePropertyW 2", __func__);
271 goto fail;
272 }
273
274 ok = wcsncmp(parent, L"USB\\", 4) == 0;
275fail:
276 free(parent);
277
278 return (ok);
279}
280#endif
281
139static int 282static int
140copy_info(fido_dev_info_t *di, const char *path) 283copy_info(fido_dev_info_t *di, HDEVINFO devinfo, DWORD idx,
284 SP_DEVICE_INTERFACE_DATA *ifdata)
141{ 285{
142 HANDLE dev = INVALID_HANDLE_VALUE; 286 HANDLE dev = INVALID_HANDLE_VALUE;
143 int ok = -1; 287 int ok = -1;
144 288
145 memset(di, 0, sizeof(*di)); 289 memset(di, 0, sizeof(*di));
146 290
147 dev = CreateFileA(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 291 if ((di->path = get_path(devinfo, ifdata)) == NULL) {
148 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 292 fido_log_debug("%s: get_path", __func__);
149 if (dev == INVALID_HANDLE_VALUE || is_fido(dev) == 0)
150 goto fail; 293 goto fail;
294 }
151 295
152 if (get_int(dev, &di->vendor_id, &di->product_id) < 0 || 296 fido_log_debug("%s: path=%s", __func__, di->path);
153 get_str(dev, &di->manufacturer, &di->product) < 0) 297
298#ifndef FIDO_HID_ANY
299 if (hid_ok(devinfo, idx) == false) {
300 fido_log_debug("%s: hid_ok", __func__);
154 goto fail; 301 goto fail;
302 }
303#endif
155 304
156 if ((di->path = strdup(path)) == NULL) 305 dev = CreateFileA(di->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
306 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
307 if (dev == INVALID_HANDLE_VALUE) {
308 fido_log_debug("%s: CreateFileA", __func__);
157 goto fail; 309 goto fail;
310 }
311
312 if (is_fido(dev) == false) {
313 fido_log_debug("%s: is_fido", __func__);
314 goto fail;
315 }
316
317 if (get_int(dev, &di->vendor_id, &di->product_id) < 0 ||
318 get_str(dev, &di->manufacturer, &di->product) < 0) {
319 fido_log_debug("%s: get_int/get_str", __func__);
320 goto fail;
321 }
158 322
159 ok = 0; 323 ok = 0;
160fail: 324fail:
@@ -174,66 +338,30 @@ fail:
174int 338int
175fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 339fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
176{ 340{
177 GUID hid_guid = GUID_DEVINTERFACE_HID; 341 GUID hid_guid = GUID_DEVINTERFACE_HID;
178 HDEVINFO devinfo = INVALID_HANDLE_VALUE; 342 HDEVINFO devinfo = INVALID_HANDLE_VALUE;
179 SP_DEVICE_INTERFACE_DATA ifdata; 343 SP_DEVICE_INTERFACE_DATA ifdata;
180 SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL; 344 DWORD idx;
181 DWORD len = 0; 345 int r = FIDO_ERR_INTERNAL;
182 DWORD idx = 0;
183 int r = FIDO_ERR_INTERNAL;
184 346
185 *olen = 0; 347 *olen = 0;
186 348
187 if (ilen == 0) 349 if (ilen == 0)
188 return (FIDO_OK); /* nothing to do */ 350 return (FIDO_OK); /* nothing to do */
189
190 if (devlist == NULL) 351 if (devlist == NULL)
191 return (FIDO_ERR_INVALID_ARGUMENT); 352 return (FIDO_ERR_INVALID_ARGUMENT);
192 353
193 devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL, 354 if ((devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
194 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); 355 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) {
195 if (devinfo == INVALID_HANDLE_VALUE) {
196 fido_log_debug("%s: SetupDiGetClassDevsA", __func__); 356 fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
197 goto fail; 357 goto fail;
198 } 358 }
199 359
200 ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 360 ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
201 361
202 while (SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, idx++, 362 for (idx = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid,
203 &ifdata) == true) { 363 idx, &ifdata) == true; idx++) {
204 /* 364 if (copy_info(&devlist[*olen], devinfo, idx, &ifdata) == 0) {
205 * "Get the required buffer size. Call
206 * SetupDiGetDeviceInterfaceDetail with a NULL
207 * DeviceInterfaceDetailData pointer, a
208 * DeviceInterfaceDetailDataSize of zero, and a valid
209 * RequiredSize variable. In response to such a call, this
210 * function returns the required buffer size at RequiredSize
211 * and fails with GetLastError returning
212 * ERROR_INSUFFICIENT_BUFFER."
213 */
214 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, NULL, 0,
215 &len, NULL) != false ||
216 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
217 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
218 __func__);
219 goto fail;
220 }
221
222 if ((ifdetail = malloc(len)) == NULL) {
223 fido_log_debug("%s: malloc", __func__);
224 goto fail;
225 }
226
227 ifdetail->cbSize = sizeof(*ifdetail);
228
229 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, ifdetail,
230 len, NULL, NULL) == false) {
231 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
232 __func__);
233 goto fail;
234 }
235
236 if (copy_info(&devlist[*olen], ifdetail->DevicePath) == 0) {
237 devlist[*olen].io = (fido_dev_io_t) { 365 devlist[*olen].io = (fido_dev_io_t) {
238 fido_hid_open, 366 fido_hid_open,
239 fido_hid_close, 367 fido_hid_close,
@@ -243,9 +371,6 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
243 if (++(*olen) == ilen) 371 if (++(*olen) == ilen)
244 break; 372 break;
245 } 373 }
246
247 free(ifdetail);
248 ifdetail = NULL;
249 } 374 }
250 375
251 r = FIDO_OK; 376 r = FIDO_OK;
@@ -253,78 +378,153 @@ fail:
253 if (devinfo != INVALID_HANDLE_VALUE) 378 if (devinfo != INVALID_HANDLE_VALUE)
254 SetupDiDestroyDeviceInfoList(devinfo); 379 SetupDiDestroyDeviceInfoList(devinfo);
255 380
256 free(ifdetail);
257
258 return (r); 381 return (r);
259} 382}
260 383
261void * 384void *
262fido_hid_open(const char *path) 385fido_hid_open(const char *path)
263{ 386{
264 HANDLE dev; 387 struct hid_win *ctx;
388
389 if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
390 return (NULL);
265 391
266 dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 392 ctx->dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
267 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 393 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
268 FILE_ATTRIBUTE_NORMAL, NULL); 394 FILE_FLAG_OVERLAPPED, NULL);
395
396 if (ctx->dev == INVALID_HANDLE_VALUE) {
397 free(ctx);
398 return (NULL);
399 }
269 400
270 if (dev == INVALID_HANDLE_VALUE) 401 if ((ctx->overlap.hEvent = CreateEventA(NULL, FALSE, FALSE,
402 NULL)) == NULL) {
403 fido_log_debug("%s: CreateEventA", __func__);
404 fido_hid_close(ctx);
271 return (NULL); 405 return (NULL);
406 }
272 407
273 return (dev); 408 if (get_report_len(ctx->dev, 0, &ctx->report_in_len) < 0 ||
409 get_report_len(ctx->dev, 1, &ctx->report_out_len) < 0) {
410 fido_log_debug("%s: get_report_len", __func__);
411 fido_hid_close(ctx);
412 return (NULL);
413 }
414
415 return (ctx);
274} 416}
275 417
276void 418void
277fido_hid_close(void *handle) 419fido_hid_close(void *handle)
278{ 420{
279 CloseHandle(handle); 421 struct hid_win *ctx = handle;
422
423 if (ctx->overlap.hEvent != NULL) {
424 if (ctx->report_pending) {
425 fido_log_debug("%s: report_pending", __func__);
426 CancelIo(ctx->dev);
427 }
428 CloseHandle(ctx->overlap.hEvent);
429 }
430
431 explicit_bzero(ctx->report, sizeof(ctx->report));
432 CloseHandle(ctx->dev);
433 free(ctx);
280} 434}
281 435
282int 436int
283fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) 437fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
284{ 438{
285 DWORD n; 439 struct hid_win *ctx = handle;
286 int r = -1; 440 DWORD n;
287 uint8_t report[REPORT_LEN];
288 441
289 (void)ms; /* XXX */ 442 if (len != ctx->report_in_len - 1 || len > sizeof(ctx->report) - 1) {
443 fido_log_debug("%s: len %zu", __func__, len);
444 return (-1);
445 }
446
447 if (ctx->report_pending == 0) {
448 memset(&ctx->report, 0, sizeof(ctx->report));
449 ResetEvent(ctx->overlap.hEvent);
450 if (ReadFile(ctx->dev, ctx->report, (DWORD)(len + 1), &n,
451 &ctx->overlap) == 0 && GetLastError() != ERROR_IO_PENDING) {
452 CancelIo(ctx->dev);
453 fido_log_debug("%s: ReadFile", __func__);
454 return (-1);
455 }
456 ctx->report_pending = 1;
457 }
290 458
291 memset(report, 0, sizeof(report)); 459 if (ms > -1 && WaitForSingleObject(ctx->overlap.hEvent,
460 (DWORD)ms) != WAIT_OBJECT_0)
461 return (0);
292 462
293 if (len != sizeof(report) - 1) { 463 ctx->report_pending = 0;
294 fido_log_debug("%s: invalid len", __func__); 464
465 if (GetOverlappedResult(ctx->dev, &ctx->overlap, &n, TRUE) == 0) {
466 fido_log_debug("%s: GetOverlappedResult", __func__);
295 return (-1); 467 return (-1);
296 } 468 }
297 469
298 if (ReadFile(handle, report, sizeof(report), &n, NULL) == false || 470 if (n != len + 1) {
299 n != sizeof(report)) { 471 fido_log_debug("%s: expected %zu, got %zu", __func__,
300 fido_log_debug("%s: ReadFile", __func__); 472 len + 1, (size_t)n);
301 goto fail; 473 return (-1);
302 } 474 }
303 475
304 r = sizeof(report) - 1; 476 memcpy(buf, ctx->report + 1, len);
305 memcpy(buf, report + 1, len); 477 explicit_bzero(ctx->report, sizeof(ctx->report));
306
307fail:
308 explicit_bzero(report, sizeof(report));
309 478
310 return (r); 479 return ((int)len);
311} 480}
312 481
313int 482int
314fido_hid_write(void *handle, const unsigned char *buf, size_t len) 483fido_hid_write(void *handle, const unsigned char *buf, size_t len)
315{ 484{
316 DWORD n; 485 struct hid_win *ctx = handle;
486 OVERLAPPED overlap;
487 DWORD n;
488
489 memset(&overlap, 0, sizeof(overlap));
317 490
318 if (len != REPORT_LEN) { 491 if (len != ctx->report_out_len) {
319 fido_log_debug("%s: invalid len", __func__); 492 fido_log_debug("%s: len %zu", __func__, len);
320 return (-1); 493 return (-1);
321 } 494 }
322 495
323 if (WriteFile(handle, buf, (DWORD)len, &n, NULL) == false || 496 if (WriteFile(ctx->dev, buf, (DWORD)len, NULL, &overlap) == 0 &&
324 n != REPORT_LEN) { 497 GetLastError() != ERROR_IO_PENDING) {
325 fido_log_debug("%s: WriteFile", __func__); 498 fido_log_debug("%s: WriteFile", __func__);
326 return (-1); 499 return (-1);
327 } 500 }
328 501
329 return (REPORT_LEN); 502 if (GetOverlappedResult(ctx->dev, &overlap, &n, TRUE) == 0) {
503 fido_log_debug("%s: GetOverlappedResult", __func__);
504 return (-1);
505 }
506
507 if (n != len) {
508 fido_log_debug("%s: expected %zu, got %zu", __func__, len,
509 (size_t)n);
510 return (-1);
511 }
512
513 return ((int)len);
514}
515
516size_t
517fido_hid_report_in_len(void *handle)
518{
519 struct hid_win *ctx = handle;
520
521 return (ctx->report_in_len - 1);
522}
523
524size_t
525fido_hid_report_out_len(void *handle)
526{
527 struct hid_win *ctx = handle;
528
529 return (ctx->report_out_len - 1);
330} 530}
diff --git a/src/info.c b/src/info.c
index 8e256fa..3199ac2 100644
--- a/src/info.c
+++ b/src/info.c
@@ -217,6 +217,10 @@ parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
217 return (cbor_decode_uint64(val, &ci->maxmsgsiz)); 217 return (cbor_decode_uint64(val, &ci->maxmsgsiz));
218 case 6: /* pinProtocols */ 218 case 6: /* pinProtocols */
219 return (decode_protocols(val, &ci->protocols)); 219 return (decode_protocols(val, &ci->protocols));
220 case 7: /* maxCredentialCountInList */
221 return (cbor_decode_uint64(val, &ci->maxcredcntlst));
222 case 8: /* maxCredentialIdLength */
223 return (cbor_decode_uint64(val, &ci->maxcredidlen));
220 case 14: /* fwVersion */ 224 case 14: /* fwVersion */
221 return (cbor_decode_uint64(val, &ci->fwversion)); 225 return (cbor_decode_uint64(val, &ci->fwversion));
222 default: /* ignore */ 226 default: /* ignore */
@@ -399,6 +403,18 @@ fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
399} 403}
400 404
401uint64_t 405uint64_t
406fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci)
407{
408 return (ci->maxcredcntlst);
409}
410
411uint64_t
412fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci)
413{
414 return (ci->maxcredidlen);
415}
416
417uint64_t
402fido_cbor_info_fwversion(const fido_cbor_info_t *ci) 418fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
403{ 419{
404 return (ci->fwversion); 420 return (ci->fwversion);
diff --git a/src/io.c b/src/io.c
index af2f49a..9d2de88 100644
--- a/src/io.c
+++ b/src/io.c
@@ -20,11 +20,11 @@ struct frame {
20 uint8_t cmd; 20 uint8_t cmd;
21 uint8_t bcnth; 21 uint8_t bcnth;
22 uint8_t bcntl; 22 uint8_t bcntl;
23 uint8_t data[CTAP_RPT_SIZE - 7]; 23 uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
24 } init; 24 } init;
25 struct { 25 struct {
26 uint8_t seq; 26 uint8_t seq;
27 uint8_t data[CTAP_RPT_SIZE - 5]; 27 uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
28 } cont; 28 } cont;
29 } body; 29 } body;
30}) 30})
@@ -38,6 +38,7 @@ tx_empty(fido_dev_t *d, uint8_t cmd)
38{ 38{
39 struct frame *fp; 39 struct frame *fp;
40 unsigned char pkt[sizeof(*fp) + 1]; 40 unsigned char pkt[sizeof(*fp) + 1];
41 const size_t len = d->tx_len + 1;
41 int n; 42 int n;
42 43
43 memset(&pkt, 0, sizeof(pkt)); 44 memset(&pkt, 0, sizeof(pkt));
@@ -45,8 +46,8 @@ tx_empty(fido_dev_t *d, uint8_t cmd)
45 fp->cid = d->cid; 46 fp->cid = d->cid;
46 fp->body.init.cmd = CTAP_FRAME_INIT | cmd; 47 fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
47 48
48 n = d->io.write(d->io_handle, pkt, sizeof(pkt)); 49 if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
49 if (n < 0 || (size_t)n != sizeof(pkt)) 50 len)) < 0 || (size_t)n != len)
50 return (-1); 51 return (-1);
51 52
52 return (0); 53 return (0);
@@ -57,19 +58,23 @@ tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
57{ 58{
58 struct frame *fp; 59 struct frame *fp;
59 unsigned char pkt[sizeof(*fp) + 1]; 60 unsigned char pkt[sizeof(*fp) + 1];
61 const size_t len = d->tx_len + 1;
60 int n; 62 int n;
61 63
64 if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
65 return (0);
66
62 memset(&pkt, 0, sizeof(pkt)); 67 memset(&pkt, 0, sizeof(pkt));
63 fp = (struct frame *)(pkt + 1); 68 fp = (struct frame *)(pkt + 1);
64 fp->cid = d->cid; 69 fp->cid = d->cid;
65 fp->body.init.cmd = CTAP_FRAME_INIT | cmd; 70 fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
66 fp->body.init.bcnth = (count >> 8) & 0xff; 71 fp->body.init.bcnth = (count >> 8) & 0xff;
67 fp->body.init.bcntl = count & 0xff; 72 fp->body.init.bcntl = count & 0xff;
68 count = MIN(count, sizeof(fp->body.init.data)); 73 count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
69 memcpy(&fp->body.init.data, buf, count); 74 memcpy(&fp->body.init.data, buf, count);
70 75
71 n = d->io.write(d->io_handle, pkt, sizeof(pkt)); 76 if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
72 if (n < 0 || (size_t)n != sizeof(pkt)) 77 len)) < 0 || (size_t)n != len)
73 return (0); 78 return (0);
74 79
75 return (count); 80 return (count);
@@ -80,17 +85,21 @@ tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count)
80{ 85{
81 struct frame *fp; 86 struct frame *fp;
82 unsigned char pkt[sizeof(*fp) + 1]; 87 unsigned char pkt[sizeof(*fp) + 1];
88 const size_t len = d->tx_len + 1;
83 int n; 89 int n;
84 90
91 if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
92 return (0);
93
85 memset(&pkt, 0, sizeof(pkt)); 94 memset(&pkt, 0, sizeof(pkt));
86 fp = (struct frame *)(pkt + 1); 95 fp = (struct frame *)(pkt + 1);
87 fp->cid = d->cid; 96 fp->cid = d->cid;
88 fp->body.cont.seq = seq; 97 fp->body.cont.seq = seq;
89 count = MIN(count, sizeof(fp->body.cont.data)); 98 count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
90 memcpy(&fp->body.cont.data, buf, count); 99 memcpy(&fp->body.cont.data, buf, count);
91 100
92 n = d->io.write(d->io_handle, pkt, sizeof(pkt)); 101 if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
93 if (n < 0 || (size_t)n != sizeof(pkt)) 102 len)) < 0 || (size_t)n != len)
94 return (0); 103 return (0);
95 104
96 return (count); 105 return (count);
@@ -129,16 +138,12 @@ fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
129 138
130 if (d->transport.tx != NULL) 139 if (d->transport.tx != NULL)
131 return (d->transport.tx(d, cmd, buf, count)); 140 return (d->transport.tx(d, cmd, buf, count));
132
133 if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) { 141 if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
134 fido_log_debug("%s: invalid argument", __func__); 142 fido_log_debug("%s: invalid argument", __func__);
135 return (-1); 143 return (-1);
136 } 144 }
137 145
138 if (count == 0) 146 return (count == 0 ? tx_empty(d, cmd) : tx(d, cmd, buf, count));
139 return (tx_empty(d, cmd));
140
141 return (tx(d, cmd, buf, count));
142} 147}
143 148
144static int 149static int
@@ -146,8 +151,10 @@ rx_frame(fido_dev_t *d, struct frame *fp, int ms)
146{ 151{
147 int n; 152 int n;
148 153
149 n = d->io.read(d->io_handle, (unsigned char *)fp, sizeof(*fp), ms); 154 memset(fp, 0, sizeof(*fp));
150 if (n < 0 || (size_t)n != sizeof(*fp)) 155
156 if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
157 (unsigned char *)fp, d->rx_len, ms)) < 0 || (size_t)n != d->rx_len)
151 return (-1); 158 return (-1);
152 159
153 return (0); 160 return (0);
@@ -165,8 +172,11 @@ rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms)
165 } while (fp->cid == d->cid && 172 } while (fp->cid == d->cid &&
166 fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)); 173 fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE));
167 174
175 if (d->rx_len > sizeof(*fp))
176 return (-1);
177
168 fido_log_debug("%s: initiation frame at %p", __func__, (void *)fp); 178 fido_log_debug("%s: initiation frame at %p", __func__, (void *)fp);
169 fido_log_xxd(fp, sizeof(*fp)); 179 fido_log_xxd(fp, d->rx_len);
170 180
171#ifdef FIDO_FUZZ 181#ifdef FIDO_FUZZ
172 fp->body.init.cmd = (CTAP_FRAME_INIT | cmd); 182 fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
@@ -185,30 +195,41 @@ static int
185rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms) 195rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
186{ 196{
187 struct frame f; 197 struct frame f;
188 uint16_t r, payload_len; 198 size_t r, payload_len, init_data_len, cont_data_len;
199
200 if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
201 d->rx_len <= CTAP_CONT_HEADER_LEN)
202 return (-1);
203
204 init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
205 cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
206
207 if (init_data_len > sizeof(f.body.init.data) ||
208 cont_data_len > sizeof(f.body.cont.data))
209 return (-1);
189 210
190 if (rx_preamble(d, cmd, &f, ms) < 0) { 211 if (rx_preamble(d, cmd, &f, ms) < 0) {
191 fido_log_debug("%s: rx_preamble", __func__); 212 fido_log_debug("%s: rx_preamble", __func__);
192 return (-1); 213 return (-1);
193 } 214 }
194 215
195 payload_len = (f.body.init.bcnth << 8) | f.body.init.bcntl; 216 payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
196 fido_log_debug("%s: payload_len=%zu", __func__, (size_t)payload_len); 217 fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
197 218
198 if (count < (size_t)payload_len) { 219 if (count < payload_len) {
199 fido_log_debug("%s: count < payload_len", __func__); 220 fido_log_debug("%s: count < payload_len", __func__);
200 return (-1); 221 return (-1);
201 } 222 }
202 223
203 if (payload_len < sizeof(f.body.init.data)) { 224 if (payload_len < init_data_len) {
204 memcpy(buf, f.body.init.data, payload_len); 225 memcpy(buf, f.body.init.data, payload_len);
205 return (payload_len); 226 return ((int)payload_len);
206 } 227 }
207 228
208 memcpy(buf, f.body.init.data, sizeof(f.body.init.data)); 229 memcpy(buf, f.body.init.data, init_data_len);
209 r = sizeof(f.body.init.data); 230 r = init_data_len;
210 231
211 for (int seq = 0; (size_t)r < payload_len; seq++) { 232 for (int seq = 0; r < payload_len; seq++) {
212 if (rx_frame(d, &f, ms) < 0) { 233 if (rx_frame(d, &f, ms) < 0) {
213 fido_log_debug("%s: rx_frame", __func__); 234 fido_log_debug("%s: rx_frame", __func__);
214 return (-1); 235 return (-1);
@@ -216,11 +237,11 @@ rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
216 237
217 fido_log_debug("%s: continuation frame at %p", __func__, 238 fido_log_debug("%s: continuation frame at %p", __func__,
218 (void *)&f); 239 (void *)&f);
219 fido_log_xxd(&f, sizeof(f)); 240 fido_log_xxd(&f, d->rx_len);
220 241
221#ifdef FIDO_FUZZ 242#ifdef FIDO_FUZZ
222 f.cid = d->cid; 243 f.cid = d->cid;
223 f.body.cont.seq = seq; 244 f.body.cont.seq = (uint8_t)seq;
224#endif 245#endif
225 246
226 if (f.cid != d->cid || f.body.cont.seq != seq) { 247 if (f.cid != d->cid || f.body.cont.seq != seq) {
@@ -229,17 +250,16 @@ rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
229 return (-1); 250 return (-1);
230 } 251 }
231 252
232 if ((size_t)(payload_len - r) > sizeof(f.body.cont.data)) { 253 if (payload_len - r > cont_data_len) {
233 memcpy(buf + r, f.body.cont.data, 254 memcpy(buf + r, f.body.cont.data, cont_data_len);
234 sizeof(f.body.cont.data)); 255 r += cont_data_len;
235 r += sizeof(f.body.cont.data);
236 } else { 256 } else {
237 memcpy(buf + r, f.body.cont.data, payload_len - r); 257 memcpy(buf + r, f.body.cont.data, payload_len - r);
238 r += (payload_len - r); /* break */ 258 r += payload_len - r; /* break */
239 } 259 }
240 } 260 }
241 261
242 return (r); 262 return ((int)r);
243} 263}
244 264
245int 265int
@@ -252,15 +272,13 @@ fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
252 272
253 if (d->transport.rx != NULL) 273 if (d->transport.rx != NULL)
254 return (d->transport.rx(d, cmd, buf, count, ms)); 274 return (d->transport.rx(d, cmd, buf, count, ms));
255
256 if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) { 275 if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
257 fido_log_debug("%s: invalid argument", __func__); 276 fido_log_debug("%s: invalid argument", __func__);
258 return (-1); 277 return (-1);
259 } 278 }
260
261 if ((n = rx(d, cmd, buf, count, ms)) >= 0) { 279 if ((n = rx(d, cmd, buf, count, ms)) >= 0) {
262 fido_log_debug("%s: buf=%p, len=%d", __func__, (void *)buf, n); 280 fido_log_debug("%s: buf=%p, len=%d", __func__, (void *)buf, n);
263 fido_log_xxd(buf, n); 281 fido_log_xxd(buf, (size_t)n);
264 } 282 }
265 283
266 return (n); 284 return (n);
diff --git a/src/iso7816.c b/src/iso7816.c
index a3fd280..4fe6329 100644
--- a/src/iso7816.c
+++ b/src/iso7816.c
@@ -23,8 +23,8 @@ iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len)
23 apdu->payload_ptr = apdu->payload; 23 apdu->payload_ptr = apdu->payload;
24 apdu->header.ins = ins; 24 apdu->header.ins = ins;
25 apdu->header.p1 = p1; 25 apdu->header.p1 = p1;
26 apdu->header.lc2 = (payload_len >> 8) & 0xff; 26 apdu->header.lc2 = (uint8_t)((payload_len >> 8) & 0xff);
27 apdu->header.lc3 = payload_len & 0xff; 27 apdu->header.lc3 = (uint8_t)(payload_len & 0xff);
28 28
29 return (apdu); 29 return (apdu);
30} 30}
@@ -51,7 +51,7 @@ iso7816_add(iso7816_apdu_t *apdu, const void *buf, size_t cnt)
51 51
52 memcpy(apdu->payload_ptr, buf, cnt); 52 memcpy(apdu->payload_ptr, buf, cnt);
53 apdu->payload_ptr += cnt; 53 apdu->payload_ptr += cnt;
54 apdu->payload_len -= (uint16_t)cnt; 54 apdu->payload_len = (uint16_t)(apdu->payload_len - cnt);
55 55
56 return (0); 56 return (0);
57} 57}
diff --git a/src/pin.c b/src/pin.c
index 36acbe4..8b23ae3 100644
--- a/src/pin.c
+++ b/src/pin.c
@@ -53,7 +53,7 @@ fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
53 53
54 if ((argv[0] = cbor_build_uint8(1)) == NULL || 54 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
55 (argv[1] = cbor_build_uint8(5)) == NULL || 55 (argv[1] = cbor_build_uint8(5)) == NULL ||
56 (argv[2] = es256_pk_encode(pk, 0)) == NULL || 56 (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
57 (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) { 57 (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) {
58 fido_log_debug("%s: cbor encode", __func__); 58 fido_log_debug("%s: cbor encode", __func__);
59 r = FIDO_ERR_INTERNAL; 59 r = FIDO_ERR_INTERNAL;
@@ -89,7 +89,7 @@ fido_dev_get_uv_token_tx(fido_dev_t *dev, const es256_pk_t *pk)
89 89
90 if ((argv[0] = cbor_build_uint8(1)) == NULL || 90 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
91 (argv[1] = cbor_build_uint8(6)) == NULL || 91 (argv[1] = cbor_build_uint8(6)) == NULL ||
92 (argv[2] = es256_pk_encode(pk, 0)) == NULL) { 92 (argv[2] = es256_pk_encode(pk, 1)) == NULL) {
93 fido_log_debug("%s: cbor encode", __func__); 93 fido_log_debug("%s: cbor encode", __func__);
94 r = FIDO_ERR_INTERNAL; 94 r = FIDO_ERR_INTERNAL;
95 goto fail; 95 goto fail;
@@ -240,7 +240,7 @@ pad64(const char *pin, fido_blob_t **ppin)
240 if ((*ppin = fido_blob_new()) == NULL) 240 if ((*ppin = fido_blob_new()) == NULL)
241 return (FIDO_ERR_INTERNAL); 241 return (FIDO_ERR_INTERNAL);
242 242
243 ppin_len = (pin_len + 63) & ~63; 243 ppin_len = (pin_len + 63U) & ~63U;
244 if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) { 244 if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
245 fido_blob_free(ppin); 245 fido_blob_free(ppin);
246 return (FIDO_ERR_INTERNAL); 246 return (FIDO_ERR_INTERNAL);
@@ -285,7 +285,7 @@ fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
285 285
286 if ((argv[0] = cbor_build_uint8(1)) == NULL || 286 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
287 (argv[1] = cbor_build_uint8(4)) == NULL || 287 (argv[1] = cbor_build_uint8(4)) == NULL ||
288 (argv[2] = es256_pk_encode(pk, 0)) == NULL || 288 (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
289 (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL || 289 (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL ||
290 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL || 290 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL ||
291 (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) { 291 (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) {
@@ -339,7 +339,7 @@ fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
339 339
340 if ((argv[0] = cbor_build_uint8(1)) == NULL || 340 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
341 (argv[1] = cbor_build_uint8(3)) == NULL || 341 (argv[1] = cbor_build_uint8(3)) == NULL ||
342 (argv[2] = es256_pk_encode(pk, 0)) == NULL || 342 (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
343 (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL || 343 (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL ||
344 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) { 344 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) {
345 fido_log_debug("%s: cbor encode", __func__); 345 fido_log_debug("%s: cbor encode", __func__);
diff --git a/src/u2f.c b/src/u2f.c
index 19a959d..3c6ea82 100644
--- a/src/u2f.c
+++ b/src/u2f.c
@@ -122,6 +122,7 @@ authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
122 return (0); 122 return (0);
123} 123}
124 124
125/* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */
125static int 126static int
126send_dummy_register(fido_dev_t *dev, int ms) 127send_dummy_register(fido_dev_t *dev, int ms)
127{ 128{
@@ -160,7 +161,7 @@ send_dummy_register(fido_dev_t *dev, int ms)
160 r = FIDO_ERR_RX; 161 r = FIDO_ERR_RX;
161 goto fail; 162 goto fail;
162 } 163 }
163 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) { 164 if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
164 fido_log_debug("%s: usleep", __func__); 165 fido_log_debug("%s: usleep", __func__);
165 r = FIDO_ERR_RX; 166 r = FIDO_ERR_RX;
166 goto fail; 167 goto fail;
@@ -204,8 +205,8 @@ key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
204 205
205 key_id_len = (uint8_t)key_id->len; 206 key_id_len = (uint8_t)key_id->len;
206 207
207 if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_CHECK, 2 * 208 if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
208 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len)) == NULL || 209 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
209 iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || 210 iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
210 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || 211 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
211 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 || 212 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
@@ -312,8 +313,8 @@ do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
312 313
313 key_id_len = (uint8_t)key_id->len; 314 key_id_len = (uint8_t)key_id->len;
314 315
315 if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_SIGN, 2 * 316 if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
316 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len)) == NULL || 317 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
317 iso7816_add(apdu, cdh->ptr, cdh->len) < 0 || 318 iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
318 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || 319 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
319 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 || 320 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
@@ -336,7 +337,7 @@ do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
336 r = FIDO_ERR_RX; 337 r = FIDO_ERR_RX;
337 goto fail; 338 goto fail;
338 } 339 }
339 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) { 340 if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
340 fido_log_debug("%s: usleep", __func__); 341 fido_log_debug("%s: usleep", __func__);
341 r = FIDO_ERR_RX; 342 r = FIDO_ERR_RX;
342 goto fail; 343 goto fail;
@@ -643,7 +644,7 @@ u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
643 r = FIDO_ERR_RX; 644 r = FIDO_ERR_RX;
644 goto fail; 645 goto fail;
645 } 646 }
646 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) { 647 if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
647 fido_log_debug("%s: usleep", __func__); 648 fido_log_debug("%s: usleep", __func__);
648 r = FIDO_ERR_RX; 649 r = FIDO_ERR_RX;
649 goto fail; 650 goto fail;
@@ -726,8 +727,8 @@ fail:
726int 727int
727u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms) 728u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms)
728{ 729{
729 int nfound = 0; 730 size_t nfound = 0;
730 int nauth_ok = 0; 731 size_t nauth_ok = 0;
731 int r; 732 int r;
732 733
733 if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) { 734 if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
@@ -769,3 +770,84 @@ u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms)
769 770
770 return (FIDO_OK); 771 return (FIDO_OK);
771} 772}
773
774int
775u2f_get_touch_begin(fido_dev_t *dev)
776{
777 iso7816_apdu_t *apdu = NULL;
778 const char *clientdata = FIDO_DUMMY_CLIENTDATA;
779 const char *rp_id = FIDO_DUMMY_RP_ID;
780 unsigned char clientdata_hash[SHA256_DIGEST_LENGTH];
781 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
782 unsigned char reply[FIDO_MAXMSG];
783 int r;
784
785 memset(&clientdata_hash, 0, sizeof(clientdata_hash));
786 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
787
788 if (SHA256((const void *)clientdata, strlen(clientdata),
789 clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id,
790 strlen(rp_id), rp_id_hash) != rp_id_hash) {
791 fido_log_debug("%s: sha256", __func__);
792 return (FIDO_ERR_INTERNAL);
793 }
794
795 if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
796 SHA256_DIGEST_LENGTH)) == NULL ||
797 iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 ||
798 iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
799 fido_log_debug("%s: iso7816", __func__);
800 r = FIDO_ERR_INTERNAL;
801 goto fail;
802 }
803
804 if (dev->attr.flags & FIDO_CAP_WINK) {
805 fido_tx(dev, CTAP_CMD_WINK, NULL, 0);
806 fido_rx(dev, CTAP_CMD_WINK, &reply, sizeof(reply), 200);
807 }
808
809 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
810 iso7816_len(apdu)) < 0) {
811 fido_log_debug("%s: fido_tx", __func__);
812 r = FIDO_ERR_TX;
813 goto fail;
814 }
815
816 r = FIDO_OK;
817fail:
818 iso7816_free(&apdu);
819
820 return (r);
821}
822
823int
824u2f_get_touch_status(fido_dev_t *dev, int *touched, int ms)
825{
826 unsigned char reply[FIDO_MAXMSG];
827 int reply_len;
828 int r;
829
830 if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply),
831 ms)) < 2) {
832 fido_log_debug("%s: fido_rx", __func__);
833 return (FIDO_OK); /* ignore */
834 }
835
836 switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) {
837 case SW_CONDITIONS_NOT_SATISFIED:
838 if ((r = u2f_get_touch_begin(dev)) != FIDO_OK) {
839 fido_log_debug("%s: u2f_get_touch_begin", __func__);
840 return (r);
841 }
842 *touched = 0;
843 break;
844 case SW_NO_ERROR:
845 *touched = 1;
846 break;
847 default:
848 fido_log_debug("%s: unexpected sw", __func__);
849 return (FIDO_ERR_RX);
850 }
851
852 return (FIDO_OK);
853}