aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEd Maste <emaste@FreeBSD.org>2021-10-01 23:52:55 +0000
committerEd Maste <emaste@FreeBSD.org>2021-10-01 23:52:55 +0000
commita58dee945a5da64d0e97f35a508928e0d17c9cc7 (patch)
tree617b7d445f004bdadf8807ad752e5507661dfef9
downloadsrc-vendor/libfido2.tar.gz
src-vendor/libfido2.zip
Vendor import of libfido2 1.8.0vendor/libfido2
-rw-r--r--CMakeLists.txt418
-rw-r--r--LICENSE24
-rw-r--r--NEWS179
-rw-r--r--README.adoc93
-rw-r--r--examples/CMakeLists.txt69
-rw-r--r--examples/README.adoc98
-rw-r--r--examples/assert.c342
-rw-r--r--examples/cred.c346
-rw-r--r--examples/extern.h33
-rw-r--r--examples/info.c293
-rw-r--r--examples/manifest.c41
-rw-r--r--examples/reset.c55
-rw-r--r--examples/retries.c48
-rw-r--r--examples/select.c214
-rw-r--r--examples/setpin.c54
-rw-r--r--examples/util.c413
-rw-r--r--fuzz/CMakeLists.txt63
-rw-r--r--fuzz/Dockerfile12
-rw-r--r--fuzz/Makefile79
-rw-r--r--fuzz/README33
-rwxr-xr-xfuzz/build-coverage31
-rw-r--r--fuzz/dummy.h96
-rw-r--r--fuzz/export.gnu242
-rw-r--r--fuzz/functions.txt807
-rw-r--r--fuzz/fuzz_assert.c471
-rw-r--r--fuzz/fuzz_bio.c440
-rw-r--r--fuzz/fuzz_cred.c455
-rw-r--r--fuzz/fuzz_credman.c405
-rw-r--r--fuzz/fuzz_hid.c215
-rw-r--r--fuzz/fuzz_largeblob.c270
-rw-r--r--fuzz/fuzz_mgmt.c480
-rw-r--r--fuzz/fuzz_netlink.c249
-rw-r--r--fuzz/libfuzzer.c177
-rw-r--r--fuzz/mutator_aux.c326
-rw-r--r--fuzz/mutator_aux.h96
-rw-r--r--fuzz/preload-fuzz.c104
-rw-r--r--fuzz/preload-snoop.c217
-rw-r--r--fuzz/prng.c113
-rw-r--r--fuzz/report.tgzbin0 -> 303082 bytes
-rw-r--r--fuzz/summary.txt51
-rw-r--r--fuzz/udev.c269
-rw-r--r--fuzz/uniform_random.c57
-rw-r--r--fuzz/wiredata_fido2.h633
-rw-r--r--fuzz/wiredata_u2f.h152
-rw-r--r--fuzz/wrap.c582
-rw-r--r--fuzz/wrapped.sym83
-rw-r--r--man/CMakeLists.txt371
-rw-r--r--man/NOTES7
-rw-r--r--man/dyc.css14
-rw-r--r--man/eddsa_pk_new.3122
-rw-r--r--man/es256_pk_new.3126
-rw-r--r--man/fido2-assert.1256
-rw-r--r--man/fido2-cred.1267
-rw-r--r--man/fido2-token.1388
-rw-r--r--man/fido_assert_allow_cred.347
-rw-r--r--man/fido_assert_new.3243
-rw-r--r--man/fido_assert_set_authdata.3221
-rw-r--r--man/fido_assert_verify.379
-rw-r--r--man/fido_bio_dev_get_info.3122
-rw-r--r--man/fido_bio_enroll_new.395
-rw-r--r--man/fido_bio_info_new.381
-rw-r--r--man/fido_bio_template.3179
-rw-r--r--man/fido_cbor_info_new.3231
-rw-r--r--man/fido_cred_exclude.360
-rw-r--r--man/fido_cred_new.3257
-rw-r--r--man/fido_cred_set_authdata.3307
-rw-r--r--man/fido_cred_verify.369
-rw-r--r--man/fido_credman_metadata_new.3326
-rw-r--r--man/fido_dev_enable_entattest.398
-rw-r--r--man/fido_dev_get_assert.376
-rw-r--r--man/fido_dev_get_touch_begin.373
-rw-r--r--man/fido_dev_info_manifest.3143
-rw-r--r--man/fido_dev_largeblob_get.3194
-rw-r--r--man/fido_dev_make_cred.377
-rw-r--r--man/fido_dev_open.3250
-rw-r--r--man/fido_dev_set_io_functions.3134
-rw-r--r--man/fido_dev_set_pin.3103
-rw-r--r--man/fido_init.352
-rw-r--r--man/fido_strerr.327
-rw-r--r--man/rs256_pk_new.3122
-rw-r--r--man/style.css24
-rw-r--r--openbsd-compat/bsd-getline.c115
-rw-r--r--openbsd-compat/bsd-getpagesize.c27
-rw-r--r--openbsd-compat/clock_gettime.c32
-rw-r--r--openbsd-compat/endian_win32.c51
-rw-r--r--openbsd-compat/err.h85
-rw-r--r--openbsd-compat/explicit_bzero.c57
-rw-r--r--openbsd-compat/explicit_bzero_win32.c19
-rw-r--r--openbsd-compat/freezero.c30
-rw-r--r--openbsd-compat/getopt.h74
-rw-r--r--openbsd-compat/getopt_long.c523
-rw-r--r--openbsd-compat/hkdf.c124
-rw-r--r--openbsd-compat/hkdf.h65
-rw-r--r--openbsd-compat/openbsd-compat.h119
-rw-r--r--openbsd-compat/posix_ioctl_check.c7
-rw-r--r--openbsd-compat/posix_win.c61
-rw-r--r--openbsd-compat/posix_win.h47
-rw-r--r--openbsd-compat/readpassphrase.c214
-rw-r--r--openbsd-compat/readpassphrase.h44
-rw-r--r--openbsd-compat/readpassphrase_win32.c131
-rw-r--r--openbsd-compat/recallocarray.c91
-rw-r--r--openbsd-compat/strlcat.c63
-rw-r--r--openbsd-compat/strlcpy.c59
-rw-r--r--openbsd-compat/time.h61
-rw-r--r--openbsd-compat/timingsafe_bcmp.c35
-rw-r--r--openbsd-compat/types.h69
-rw-r--r--regress/CMakeLists.txt16
-rw-r--r--regress/assert.c553
-rw-r--r--regress/cred.c988
-rw-r--r--regress/dev.c266
-rw-r--r--src/CMakeLists.txt136
-rw-r--r--src/aes256.c215
-rw-r--r--src/assert.c1134
-rw-r--r--src/authkey.c97
-rw-r--r--src/bio.c841
-rw-r--r--src/blob.c133
-rw-r--r--src/blob.h41
-rw-r--r--src/buf.c33
-rw-r--r--src/cbor.c1635
-rw-r--r--src/compress.c49
-rw-r--r--src/config.c191
-rw-r--r--src/cred.c1086
-rw-r--r--src/credman.c767
-rw-r--r--src/dev.c732
-rwxr-xr-xsrc/diff_exports.sh26
-rw-r--r--src/ecdh.c207
-rw-r--r--src/eddsa.c172
-rw-r--r--src/err.c136
-rw-r--r--src/es256.c453
-rw-r--r--src/export.gnu234
-rw-r--r--src/export.llvm229
-rw-r--r--src/export.msvc230
-rw-r--r--src/extern.h240
-rw-r--r--src/fido.h228
-rw-r--r--src/fido/bio.h111
-rw-r--r--src/fido/config.h34
-rw-r--r--src/fido/credman.h91
-rw-r--r--src/fido/eddsa.h54
-rw-r--r--src/fido/err.h84
-rw-r--r--src/fido/es256.h48
-rw-r--r--src/fido/param.h117
-rw-r--r--src/fido/rs256.h36
-rw-r--r--src/fido/types.h281
-rw-r--r--src/hid.c179
-rw-r--r--src/hid_freebsd.c253
-rw-r--r--src/hid_hidapi.c268
-rw-r--r--src/hid_linux.c375
-rw-r--r--src/hid_netbsd.c338
-rw-r--r--src/hid_openbsd.c260
-rw-r--r--src/hid_osx.c571
-rw-r--r--src/hid_unix.c76
-rw-r--r--src/hid_win.c540
-rw-r--r--src/info.c553
-rw-r--r--src/io.c288
-rw-r--r--src/iso7816.c64
-rw-r--r--src/iso7816.h49
-rw-r--r--src/largeblob.c881
-rw-r--r--src/libfido2.pc.in12
-rw-r--r--src/log.c121
-rw-r--r--src/netlink.c782
-rw-r--r--src/netlink.h44
-rw-r--r--src/nfc_linux.c631
-rw-r--r--src/packed.h22
-rw-r--r--src/pin.c690
-rw-r--r--src/random.c82
-rw-r--r--src/reset.c43
-rw-r--r--src/rs256.c200
-rw-r--r--src/u2f.c820
-rw-r--r--src/winhello.c934
-rw-r--r--tools/CMakeLists.txt77
-rw-r--r--tools/assert_get.c316
-rw-r--r--tools/assert_verify.c192
-rw-r--r--tools/base64.c134
-rw-r--r--tools/bio.c277
-rw-r--r--tools/config.c149
-rw-r--r--tools/cred_make.c242
-rw-r--r--tools/cred_verify.c181
-rw-r--r--tools/credman.c329
-rw-r--r--tools/extern.h99
-rw-r--r--tools/fido2-assert.c54
-rwxr-xr-xtools/fido2-attach.sh14
-rw-r--r--tools/fido2-cred.c52
-rwxr-xr-xtools/fido2-detach.sh12
-rw-r--r--tools/fido2-token.c107
-rwxr-xr-xtools/fido2-unprot.sh75
-rwxr-xr-xtools/include_check.sh21
-rw-r--r--tools/largeblob.c593
-rw-r--r--tools/pin.c143
-rwxr-xr-xtools/test.sh296
-rw-r--r--tools/token.c576
-rw-r--r--tools/util.c591
-rw-r--r--udev/70-u2f.rules217
-rw-r--r--udev/CMakeLists.txt7
-rwxr-xr-xudev/check.sh31
-rw-r--r--udev/fidodevs126
-rwxr-xr-xudev/genrules.awk55
-rw-r--r--windows/build.ps1272
-rw-r--r--windows/libressl.gpgbin0 -> 16425 bytes
198 files changed, 43610 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 000000000000..101b7b33e2fc
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,418 @@
+# Copyright (c) 2018 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# detect AppleClang; needs to come before project()
+cmake_policy(SET CMP0025 NEW)
+
+project(libfido2 C)
+cmake_minimum_required(VERSION 3.0)
+# Set PIE flags for POSITION_INDEPENDENT_CODE targets, added in CMake 3.14.
+if(POLICY CMP0083)
+ cmake_policy(SET CMP0083 NEW)
+endif()
+
+include(CheckCCompilerFlag)
+include(CheckFunctionExists)
+include(CheckLibraryExists)
+include(CheckSymbolExists)
+include(CheckIncludeFiles)
+include(CheckTypeSize)
+include(GNUInstallDirs)
+include(CheckPIESupported OPTIONAL RESULT_VARIABLE CHECK_PIE_SUPPORTED)
+if(CHECK_PIE_SUPPORTED)
+ check_pie_supported(LANGUAGES C)
+endif()
+
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMAKE_COLOR_MAKEFILE OFF)
+set(CMAKE_VERBOSE_MAKEFILE ON)
+set(FIDO_MAJOR "1")
+set(FIDO_MINOR "8")
+set(FIDO_PATCH "0")
+set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH})
+
+option(BUILD_EXAMPLES "Build example programs" ON)
+option(BUILD_MANPAGES "Build man pages" ON)
+option(BUILD_SHARED_LIBS "Build the shared library" ON)
+option(BUILD_STATIC_LIBS "Build the static library" ON)
+option(BUILD_TOOLS "Build tool programs" ON)
+option(FUZZ "Enable fuzzing instrumentation" OFF)
+option(LIBFUZZER "Build libfuzzer harnesses" OFF)
+option(USE_HIDAPI "Use hidapi as the HID backend" OFF)
+option(USE_WINHELLO "Abstract Windows Hello as a FIDO device" OFF)
+option(NFC_LINUX "Experimental NFC support on Linux" OFF)
+
+add_definitions(-D_FIDO_MAJOR=${FIDO_MAJOR})
+add_definitions(-D_FIDO_MINOR=${FIDO_MINOR})
+add_definitions(-D_FIDO_PATCH=${FIDO_PATCH})
+
+if(CYGWIN OR MSYS)
+ set(WIN32 1)
+ add_definitions(-DWINVER=0x0a00)
+endif()
+
+if(WIN32)
+ add_definitions(-DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600)
+endif()
+
+if(APPLE)
+ set(CMAKE_INSTALL_NAME_DIR
+ "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+if(NOT MSVC)
+ set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_POSIX_C_SOURCE=200809L")
+ set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_BSD_SOURCE")
+ if(APPLE)
+ set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_DARWIN_C_SOURCE")
+ set(FIDO_CFLAGS "${FIDO_CFLAGS} -D__STDC_WANT_LIB_EXT1__=1")
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(NFC_LINUX OFF)
+ set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_GNU_SOURCE")
+ set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_DEFAULT_SOURCE")
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ set(FIDO_CFLAGS "${FIDO_CFLAGS} -D__BSD_VISIBLE=1")
+ endif()
+ set(FIDO_CFLAGS "${FIDO_CFLAGS} -std=c99")
+ set(CMAKE_C_FLAGS "${FIDO_CFLAGS} ${CMAKE_C_FLAGS}")
+endif()
+
+check_c_compiler_flag("-Wshorten-64-to-32" HAVE_SHORTEN_64_TO_32)
+check_c_compiler_flag("-fstack-protector-all" HAVE_STACK_PROTECTOR_ALL)
+
+check_include_files(cbor.h HAVE_CBOR_H)
+check_include_files(endian.h HAVE_ENDIAN_H)
+check_include_files(err.h HAVE_ERR_H)
+check_include_files(openssl/opensslv.h HAVE_OPENSSLV_H)
+check_include_files(signal.h HAVE_SIGNAL_H)
+check_include_files(sys/random.h HAVE_SYS_RANDOM_H)
+check_include_files(unistd.h HAVE_UNISTD_H)
+check_include_files("windows.h;webauthn.h" HAVE_WEBAUTHN_H)
+
+check_symbol_exists(arc4random_buf stdlib.h HAVE_ARC4RANDOM_BUF)
+check_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME)
+check_symbol_exists(explicit_bzero string.h HAVE_EXPLICIT_BZERO)
+check_symbol_exists(freezero stdlib.h HAVE_FREEZERO)
+check_symbol_exists(getline stdio.h HAVE_GETLINE)
+check_symbol_exists(getopt unistd.h HAVE_GETOPT)
+check_symbol_exists(getpagesize unistd.h HAVE_GETPAGESIZE)
+check_symbol_exists(getrandom sys/random.h HAVE_GETRANDOM)
+check_symbol_exists(memset_s string.h HAVE_MEMSET_S)
+check_symbol_exists(readpassphrase readpassphrase.h HAVE_READPASSPHRASE)
+check_symbol_exists(recallocarray stdlib.h HAVE_RECALLOCARRAY)
+check_symbol_exists(sigaction signal.h HAVE_SIGACTION)
+check_symbol_exists(strlcat string.h HAVE_STRLCAT)
+check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
+check_symbol_exists(sysconf unistd.h HAVE_SYSCONF)
+check_symbol_exists(timespecsub sys/time.h HAVE_TIMESPECSUB)
+check_symbol_exists(timingsafe_bcmp string.h HAVE_TIMINGSAFE_BCMP)
+
+set(CMAKE_EXTRA_INCLUDE_FILES signal.h)
+check_type_size("sig_atomic_t" HAVE_SIG_ATOMIC_T)
+set(CMAKE_EXTRA_INCLUDE_FILES)
+
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+try_compile(HAVE_POSIX_IOCTL
+ "${CMAKE_CURRENT_BINARY_DIR}/posix_ioctl_check.o"
+ "${CMAKE_CURRENT_SOURCE_DIR}/openbsd-compat/posix_ioctl_check.c"
+ COMPILE_DEFINITIONS "-Werror -Woverflow -Wsign-conversion")
+
+list(APPEND CHECK_VARIABLES
+ HAVE_ARC4RANDOM_BUF
+ HAVE_CBOR_H
+ HAVE_CLOCK_GETTIME
+ HAVE_ENDIAN_H
+ HAVE_ERR_H
+ HAVE_FREEZERO
+ HAVE_GETLINE
+ HAVE_GETOPT
+ HAVE_GETPAGESIZE
+ HAVE_GETRANDOM
+ HAVE_MEMSET_S
+ HAVE_OPENSSLV_H
+ HAVE_POSIX_IOCTL
+ HAVE_READPASSPHRASE
+ HAVE_RECALLOCARRAY
+ HAVE_SIGACTION
+ HAVE_SIGNAL_H
+ HAVE_STRLCAT
+ HAVE_STRLCPY
+ HAVE_SYSCONF
+ HAVE_SYS_RANDOM_H
+ HAVE_TIMESPECSUB
+ HAVE_TIMINGSAFE_BCMP
+ HAVE_UNISTD_H
+ HAVE_WEBAUTHN_H
+)
+
+foreach(v ${CHECK_VARIABLES})
+ if (${v})
+ add_definitions(-D${v})
+ endif()
+endforeach()
+
+if(HAVE_EXPLICIT_BZERO AND NOT LIBFUZZER)
+ add_definitions(-DHAVE_EXPLICIT_BZERO)
+endif()
+
+if(HAVE_SIGACTION AND (NOT HAVE_SIG_ATOMIC_T STREQUAL ""))
+ add_definitions(-DSIGNAL_EXAMPLE)
+endif()
+
+if(UNIX)
+ add_definitions(-DHAVE_DEV_URANDOM)
+endif()
+
+if(MSVC)
+ if((NOT CBOR_INCLUDE_DIRS) OR (NOT CBOR_LIBRARY_DIRS) OR
+ (NOT CRYPTO_INCLUDE_DIRS) OR (NOT CRYPTO_LIBRARY_DIRS) OR
+ (NOT ZLIB_INCLUDE_DIRS) OR (NOT ZLIB_LIBRARY_DIRS))
+ message(FATAL_ERROR "please provide definitions for "
+ "{CBOR,CRYPTO,ZLIB}_{INCLUDE,LIBRARY}_DIRS when building "
+ "under msvc")
+ endif()
+ set(CBOR_LIBRARIES cbor)
+ set(ZLIB_LIBRARIES zlib)
+ set(CRYPTO_LIBRARIES crypto-46)
+ set(MSVC_DISABLED_WARNINGS_LIST
+ "C4200" # nonstandard extension used: zero-sized array in
+ # struct/union;
+ "C4204" # nonstandard extension used: non-constant aggregate
+ # initializer;
+ "C4706" # assignment within conditional expression;
+ "C4996" # The POSIX name for this item is deprecated. Instead,
+ # use the ISO C and C++ conformant name;
+ "C6287" # redundant code: the left and right subexpressions are identical
+ )
+ # The construction in the following 3 lines was taken from LibreSSL's
+ # CMakeLists.txt.
+ string(REPLACE "C" " -wd" MSVC_DISABLED_WARNINGS_STR
+ ${MSVC_DISABLED_WARNINGS_LIST})
+ string(REGEX REPLACE "[/-]W[1234][ ]?" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MP -W4 -WX ${MSVC_DISABLED_WARNINGS_STR}")
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Z7 /guard:cf /sdl /RTCcsu")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi /guard:cf /sdl")
+ if (HAVE_WEBAUTHN_H)
+ add_definitions(-DUSE_WINHELLO)
+ set(USE_WINHELLO ON)
+ endif()
+else()
+ include(FindPkgConfig)
+ pkg_search_module(CBOR libcbor)
+ pkg_search_module(CRYPTO libcrypto)
+ pkg_search_module(ZLIB zlib)
+
+ if(NOT CBOR_FOUND AND NOT HAVE_CBOR_H)
+ message(FATAL_ERROR "could not find libcbor")
+ endif()
+ if(NOT CRYPTO_FOUND AND NOT HAVE_OPENSSLV_H)
+ message(FATAL_ERROR "could not find libcrypto")
+ endif()
+ if(NOT ZLIB_FOUND)
+ message(FATAL_ERROR "could not find zlib")
+ endif()
+
+ set(CBOR_LIBRARIES "cbor")
+ set(CRYPTO_LIBRARIES "crypto")
+
+ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ pkg_search_module(UDEV libudev REQUIRED)
+ set(UDEV_NAME "udev")
+ # If using hidapi, use hidapi-hidraw.
+ set(HIDAPI_SUFFIX -hidraw)
+ if(NOT HAVE_CLOCK_GETTIME)
+ # Look for clock_gettime in librt.
+ check_library_exists(rt clock_gettime "time.h"
+ HAVE_CLOCK_GETTIME)
+ if (HAVE_CLOCK_GETTIME)
+ add_definitions(-DHAVE_CLOCK_GETTIME)
+ set(BASE_LIBRARIES ${BASE_LIBRARIES} rt)
+ endif()
+ endif()
+ endif()
+
+ if(MINGW)
+ # MinGW is stuck with a flavour of C89.
+ add_definitions(-DFIDO_NO_DIAGNOSTIC)
+ add_definitions(-DWC_ERR_INVALID_CHARS=0x80)
+ add_compile_options(-Wno-unused-parameter)
+ endif()
+
+ if(USE_HIDAPI)
+ add_definitions(-DUSE_HIDAPI)
+ pkg_search_module(HIDAPI hidapi${HIDAPI_SUFFIX} REQUIRED)
+ set(HIDAPI_LIBRARIES hidapi${HIDAPI_SUFFIX})
+ endif()
+
+ if(FUZZ)
+ set(NFC_LINUX ON)
+ endif()
+
+ if(NFC_LINUX)
+ add_definitions(-DNFC_LINUX)
+ endif()
+
+ add_compile_options(-Wall)
+ add_compile_options(-Wextra)
+ add_compile_options(-Werror)
+ add_compile_options(-Wshadow)
+ add_compile_options(-Wcast-qual)
+ add_compile_options(-Wwrite-strings)
+ add_compile_options(-Wmissing-prototypes)
+ add_compile_options(-Wbad-function-cast)
+ add_compile_options(-pedantic)
+ add_compile_options(-pedantic-errors)
+
+ if(HAVE_SHORTEN_64_TO_32)
+ add_compile_options(-Wshorten-64-to-32)
+ endif()
+ if(HAVE_STACK_PROTECTOR_ALL)
+ add_compile_options(-fstack-protector-all)
+ endif()
+
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g2")
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2")
+
+ if(FUZZ)
+ add_definitions(-DFIDO_FUZZ)
+ endif()
+ if(LIBFUZZER)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=fuzzer-no-link")
+ endif()
+endif()
+
+# Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
+if(CMAKE_COMPILER_IS_GNUCC)
+ add_compile_options(-Wno-unused-result)
+endif()
+
+# Decide which keyword to use for thread-local storage.
+if(CMAKE_COMPILER_IS_GNUCC OR
+ CMAKE_C_COMPILER_ID STREQUAL "Clang" OR
+ CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+ set(TLS "__thread")
+elseif(WIN32)
+ set(TLS "__declspec(thread)")
+endif()
+add_definitions(-DTLS=${TLS})
+
+# export list
+if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR
+ CMAKE_C_COMPILER_ID STREQUAL "AppleClang"))
+ # clang + lld
+ string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}
+ " -exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/src/export.llvm")
+elseif(NOT MSVC)
+ # clang/gcc + gnu ld
+ if(FUZZ)
+ string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}
+ " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/export.gnu")
+ else()
+ string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}
+ " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/export.gnu")
+ endif()
+ if(NOT WIN32)
+ string(CONCAT CMAKE_SHARED_LINKER_FLAGS
+ ${CMAKE_SHARED_LINKER_FLAGS}
+ " -Wl,-z,noexecstack -Wl,-z,relro,-z,now")
+ string(CONCAT CMAKE_EXE_LINKER_FLAGS
+ ${CMAKE_EXE_LINKER_FLAGS}
+ " -Wl,-z,noexecstack -Wl,-z,relro,-z,now")
+ if(FUZZ)
+ file(STRINGS fuzz/wrapped.sym WRAPPED_SYMBOLS)
+ foreach(s ${WRAPPED_SYMBOLS})
+ string(CONCAT CMAKE_SHARED_LINKER_FLAGS
+ ${CMAKE_SHARED_LINKER_FLAGS}
+ " -Wl,--wrap=${s}")
+ endforeach()
+ endif()
+ endif()
+else()
+ string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}
+ " /def:\"${CMAKE_CURRENT_SOURCE_DIR}/src/export.msvc\"")
+endif()
+
+include_directories(${CMAKE_SOURCE_DIR}/src)
+include_directories(${CBOR_INCLUDE_DIRS})
+include_directories(${CRYPTO_INCLUDE_DIRS})
+include_directories(${HIDAPI_INCLUDE_DIRS})
+include_directories(${UDEV_INCLUDE_DIRS})
+include_directories(${ZLIB_INCLUDE_DIRS})
+
+link_directories(${CBOR_LIBRARY_DIRS})
+link_directories(${CRYPTO_LIBRARY_DIRS})
+link_directories(${HIDAPI_LIBRARY_DIRS})
+link_directories(${UDEV_LIBRARY_DIRS})
+link_directories(${ZLIB_LIBRARY_DIRS})
+
+message(STATUS "BASE_LIBRARIES: ${BASE_LIBRARIES}")
+message(STATUS "BUILD_EXAMPLES: ${BUILD_EXAMPLES}")
+message(STATUS "BUILD_MANPAGES: ${BUILD_MANPAGES}")
+message(STATUS "BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}")
+message(STATUS "BUILD_STATIC_LIBS: ${BUILD_STATIC_LIBS}")
+message(STATUS "BUILD_TOOLS: ${BUILD_TOOLS}")
+message(STATUS "CBOR_INCLUDE_DIRS: ${CBOR_INCLUDE_DIRS}")
+message(STATUS "CBOR_LIBRARIES: ${CBOR_LIBRARIES}")
+message(STATUS "CBOR_LIBRARY_DIRS: ${CBOR_LIBRARY_DIRS}")
+message(STATUS "CBOR_VERSION: ${CBOR_VERSION}")
+message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
+message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")
+message(STATUS "CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}")
+message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}")
+message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}")
+message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
+message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
+message(STATUS "CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}")
+message(STATUS "CRYPTO_INCLUDE_DIRS: ${CRYPTO_INCLUDE_DIRS}")
+message(STATUS "CRYPTO_LIBRARIES: ${CRYPTO_LIBRARIES}")
+message(STATUS "CRYPTO_LIBRARY_DIRS: ${CRYPTO_LIBRARY_DIRS}")
+message(STATUS "CRYPTO_VERSION: ${CRYPTO_VERSION}")
+message(STATUS "FIDO_VERSION: ${FIDO_VERSION}")
+message(STATUS "FUZZ: ${FUZZ}")
+message(STATUS "ZLIB_INCLUDE_DIRS: ${ZLIB_INCLUDE_DIRS}")
+message(STATUS "ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}")
+message(STATUS "ZLIB_LIBRARY_DIRS: ${ZLIB_LIBRARY_DIRS}")
+message(STATUS "ZLIB_VERSION: ${ZLIB_VERSION}")
+if(USE_HIDAPI)
+ message(STATUS "HIDAPI_INCLUDE_DIRS: ${HIDAPI_INCLUDE_DIRS}")
+ message(STATUS "HIDAPI_LIBRARIES: ${HIDAPI_LIBRARIES}")
+ message(STATUS "HIDAPI_LIBRARY_DIRS: ${HIDAPI_LIBRARY_DIRS}")
+ message(STATUS "HIDAPI_VERSION: ${HIDAPI_VERSION}")
+endif()
+message(STATUS "LIBFUZZER: ${LIBFUZZER}")
+message(STATUS "TLS: ${TLS}")
+message(STATUS "UDEV_INCLUDE_DIRS: ${UDEV_INCLUDE_DIRS}")
+message(STATUS "UDEV_LIBRARIES: ${UDEV_LIBRARIES}")
+message(STATUS "UDEV_LIBRARY_DIRS: ${UDEV_LIBRARY_DIRS}")
+message(STATUS "UDEV_RULES_DIR: ${UDEV_RULES_DIR}")
+message(STATUS "UDEV_VERSION: ${UDEV_VERSION}")
+message(STATUS "USE_HIDAPI: ${USE_HIDAPI}")
+message(STATUS "USE_WINHELLO: ${USE_WINHELLO}")
+message(STATUS "NFC_LINUX: ${NFC_LINUX}")
+
+subdirs(src)
+if(BUILD_EXAMPLES)
+ subdirs(examples)
+endif()
+if(BUILD_TOOLS)
+ subdirs(tools)
+endif()
+if(BUILD_MANPAGES)
+ subdirs(man)
+endif()
+
+if(NOT WIN32)
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ if(NOT LIBFUZZER AND NOT FUZZ)
+ subdirs(regress)
+ endif()
+ endif()
+ if(FUZZ)
+ subdirs(fuzz)
+ endif()
+ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ subdirs(udev)
+ endif()
+endif()
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000000..4224f20992c0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2018-2021 Yubico AB. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/NEWS b/NEWS
new file mode 100644
index 000000000000..a89766b72e89
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,179 @@
+* Version 1.8.0 (2021-07-22)
+ ** Dropped 'Requires.private' entry from pkg-config file.
+ ** Better support for FIDO 2.1 authenticators.
+ ** Support for Windows's native webauthn API.
+ ** Support for attestation format 'none'.
+ ** New API calls:
+ - fido_assert_set_clientdata;
+ - fido_cbor_info_algorithm_cose;
+ - fido_cbor_info_algorithm_count;
+ - fido_cbor_info_algorithm_type;
+ - fido_cbor_info_transports_len;
+ - fido_cbor_info_transports_ptr;
+ - fido_cred_set_clientdata;
+ - fido_cred_set_id;
+ - fido_credman_set_dev_rk;
+ - fido_dev_is_winhello.
+ ** fido2-token: new -Sc option to update a resident credential.
+ ** Documentation and reliability fixes.
+ ** HID access serialisation on Linux.
+
+* Version 1.7.0 (2021-03-29)
+ ** New dependency on zlib.
+ ** Fixed musl build; gh#259.
+ ** hid_win: detect devices with vendor or product IDs > 0x7fff; gh#264.
+ ** Support for FIDO 2.1 authenticator configuration.
+ ** Support for FIDO 2.1 UV token permissions.
+ ** Support for FIDO 2.1 "credBlobs" and "largeBlobs" extensions.
+ ** New API calls:
+ - fido_assert_blob_len;
+ - fido_assert_blob_ptr;
+ - fido_assert_largeblob_key_len;
+ - fido_assert_largeblob_key_ptr;
+ - fido_assert_set_hmac_secret;
+ - fido_cbor_info_maxcredbloblen;
+ - fido_cred_largeblob_key_len;
+ - fido_cred_largeblob_key_ptr;
+ - fido_cred_set_blob;
+ - fido_dev_enable_entattest;
+ - fido_dev_force_pin_change;
+ - fido_dev_has_uv;
+ - fido_dev_largeblob_get;
+ - fido_dev_largeblob_get_array;
+ - fido_dev_largeblob_remove;
+ - fido_dev_largeblob_set;
+ - fido_dev_largeblob_set_array;
+ - fido_dev_set_pin_minlen;
+ - fido_dev_set_sigmask;
+ - fido_dev_supports_credman;
+ - fido_dev_supports_permissions;
+ - fido_dev_supports_uv;
+ - fido_dev_toggle_always_uv.
+ ** New fido_init flag to disable fido_dev_open's U2F fallback; gh#282.
+ ** Experimental NFC support on Linux; enable with -DNFC_LINUX.
+
+* Version 1.6.0 (2020-12-22)
+ ** Fix OpenSSL 1.0 and Cygwin builds.
+ ** hid_linux: fix build on 32-bit systems.
+ ** hid_osx: allow reads from spawned threads.
+ ** Documentation and reliability fixes.
+ ** New API calls:
+ - fido_cred_authdata_raw_len;
+ - fido_cred_authdata_raw_ptr;
+ - fido_cred_sigcount;
+ - fido_dev_get_uv_retry_count;
+ - fido_dev_supports_credman.
+ ** Hardened Windows build.
+ ** Native FreeBSD and NetBSD support.
+ ** Use CTAP2 canonical CBOR when combining hmac-secret and credProtect.
+
+* Version 1.5.0 (2020-09-01)
+ ** hid_linux: return FIDO_OK if no devices are found.
+ ** hid_osx:
+ - repair communication with U2F tokens, gh#166;
+ - reliability fixes.
+ ** fido2-{assert,cred}: new options to explicitly toggle UP, UV.
+ ** Support for configurable report lengths.
+ ** New API calls:
+ - fido_cbor_info_maxcredcntlst;
+ - fido_cbor_info_maxcredidlen;
+ - fido_cred_aaguid_len;
+ - fido_cred_aaguid_ptr;
+ - fido_dev_get_touch_begin;
+ - fido_dev_get_touch_status.
+ ** Use COSE_ECDH_ES256 with CTAP_CBOR_CLIENT_PIN; gh#154.
+ ** Allow CTAP messages up to 2048 bytes; gh#171.
+ ** Ensure we only list USB devices by default.
+
+* Version 1.4.0 (2020-04-15)
+ ** hid_hidapi: hidapi backend; enable with -DUSE_HIDAPI=1.
+ ** Fall back to U2F if the key claims to, but does not support FIDO2.
+ ** FIDO2 credential protection (credprot) support.
+ ** New API calls:
+ - fido_cbor_info_fwversion;
+ - fido_cred_prot;
+ - fido_cred_set_prot;
+ - fido_dev_set_transport_functions;
+ - fido_set_log_handler.
+ ** Support for FreeBSD.
+ ** Support for C++.
+ ** Support for MSYS.
+ ** Fixed EdDSA and RSA self-attestation.
+
+* Version 1.3.1 (2020-02-19)
+ ** fix zero-ing of le1 and le2 when talking to a U2F device.
+ ** dropping sk-libfido2 middleware, please find it in the openssh tree.
+
+* Version 1.3.0 (2019-11-28)
+ ** assert/hmac: encode public key as per spec, gh#60.
+ ** fido2-cred: fix creation of resident keys.
+ ** fido2-{assert,cred}: support for hmac-secret extension.
+ ** hid_osx: detect device removal, gh#56.
+ ** hid_osx: fix device detection in MacOS Catalina.
+ ** New API calls:
+ - fido_assert_set_authdata_raw;
+ - fido_assert_sigcount;
+ - fido_cred_set_authdata_raw;
+ - fido_dev_cancel.
+ ** Middleware library for use by OpenSSH.
+ ** Support for biometric enrollment.
+ ** Support for OpenBSD.
+ ** Support for self-attestation.
+
+* Version 1.2.0 (released 2019-07-26)
+ ** Credential management support.
+ ** New API reflecting FIDO's 3-state booleans (true, false, absent):
+ - fido_assert_set_up;
+ - fido_assert_set_uv;
+ - fido_cred_set_rk;
+ - fido_cred_set_uv.
+ ** Command-line tools for Windows.
+ ** Documentation and reliability fixes.
+ ** fido_{assert,cred}_set_options() are now marked as deprecated.
+
+* Version 1.1.0 (released 2019-05-08)
+ ** MacOS: fix IOKit crash on HID read.
+ ** Windows: fix contents of release file.
+ ** EdDSA (Ed25519) support.
+ ** fido_dev_make_cred: fix order of CBOR map keys.
+ ** fido_dev_get_assert: plug memory leak when operating on U2F devices.
+
+* Version 1.0.0 (released 2019-03-21)
+ ** Native HID support on Linux, MacOS, and Windows.
+ ** fido2-{assert,cred}: new -u option to force U2F on dual authenticators.
+ ** fido2-assert: support for multiple resident keys with the same RP.
+ ** Strict checks for CTAP2 compliance on received CBOR payloads.
+ ** Better fuzzing harnesses.
+ ** Documentation and reliability fixes.
+
+* Version 0.4.0 (released 2019-01-07)
+ ** fido2-assert: print the user id for resident credentials.
+ ** Fix encoding of COSE algorithms when making a credential.
+ ** Rework purpose of fido_cred_set_type; no ABI change.
+ ** Minor documentation and code fixes.
+
+* Version 0.3.0 (released 2018-09-11)
+ ** Various reliability fixes.
+ ** Merged fuzzing instrumentation.
+ ** Added regress tests.
+ ** Added support for FIDO 2's hmac-secret extension.
+ ** New API calls:
+ - fido_assert_hmac_secret_len;
+ - fido_assert_hmac_secret_ptr;
+ - fido_assert_set_extensions;
+ - fido_assert_set_hmac_salt;
+ - fido_cred_set_extensions;
+ - fido_dev_force_fido2.
+ ** Support for native builds with Microsoft Visual Studio 17.
+
+* Version 0.2.0 (released 2018-06-20)
+ ** Added command-line tools.
+ ** Added a couple of missing get functions.
+
+* Version 0.1.1 (released 2018-06-05)
+ ** Added documentation.
+ ** Added OpenSSL 1.0 support.
+ ** Minor fixes.
+
+* Version 0.1.0 (released 2018-05-18)
+ ** First beta release.
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 000000000000..f5ffa7e4e602
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,93 @@
+== libfido2
+
+image:https://github.com/yubico/libfido2/workflows/linux/badge.svg["Linux Build Status (github actions)", link="https://github.com/Yubico/libfido2/actions"]
+image:https://github.com/yubico/libfido2/workflows/macos/badge.svg["macOS Build Status (github actions)", link="https://github.com/Yubico/libfido2/actions"]
+image:https://github.com/yubico/libfido2/workflows/windows/badge.svg["Windows Build Status (github actions)", link="https://github.com/Yubico/libfido2/actions"]
+image:https://github.com/yubico/libfido2/workflows/fuzzer/badge.svg["Fuzz Status (github actions)", link="https://github.com/Yubico/libfido2/actions"]
+image:https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfido2.svg["Fuzz Status (oss-fuzz)", link="https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:libfido2"]
+
+*libfido2* provides library functionality and command-line tools to
+communicate with a FIDO device over USB, and to verify attestation and
+assertion signatures.
+
+*libfido2* supports the FIDO U2F (CTAP 1) and FIDO 2.0 (CTAP 2) protocols.
+
+For usage, see the `examples/` directory.
+
+=== License
+
+*libfido2* is licensed under the BSD 2-clause license. See the LICENSE
+file for the full license text.
+
+=== Supported Platforms
+
+*libfido2* is known to work on Linux, macOS, Windows, OpenBSD, and FreeBSD.
+
+=== Documentation
+
+Documentation is available in troff and HTML formats. An
+https://developers.yubico.com/libfido2/Manuals/[online mirror of *libfido2*'s documentation]
+is also available.
+
+=== Bindings
+
+* .NET: https://github.com/borrrden/Fido2Net[Fido2Net]
+* Go: https://github.com/keys-pub/go-libfido2[go-libfido2]
+* Perl: https://github.com/jacquesg/p5-FIDO-Raw[p5-FIDO-Raw]
+* Rust: https://github.com/PvdBerg1998/libfido2[libfido2]
+
+=== Installation
+
+==== Releases
+
+The current release of *libfido2* is 1.8.0. Please consult Yubico's
+https://developers.yubico.com/libfido2/Releases[release page] for source
+and binary releases.
+
+==== Ubuntu 20.04 (Focal)
+
+ $ sudo apt install libfido2-1
+ $ sudo apt install libfido2-dev
+ $ sudo apt install libfido2-doc
+
+Alternatively, newer versions of *libfido2* are available in Yubico's PPA.
+Follow the instructions for Ubuntu 18.04 (Bionic) below.
+
+==== Ubuntu 18.04 (Bionic)
+
+ $ sudo apt install software-properties-common
+ $ sudo apt-add-repository ppa:yubico/stable
+ $ sudo apt update
+ $ sudo apt install libfido2-dev
+
+==== macOS
+
+ $ brew install libfido2
+
+Or from source, on UNIX-like systems:
+
+ $ (rm -rf build && mkdir build && cd build && cmake ..)
+ $ make -C build
+ $ sudo make -C build install
+
+Depending on the platform,
+https://www.freedesktop.org/wiki/Software/pkg-config/[pkg-config] may need to
+be installed, or the PKG_CONFIG_PATH environment variable set.
+
+*libfido2* depends on https://github.com/pjk/libcbor[libcbor],
+https://www.openssl.org[OpenSSL], and https://zlib.net[zlib]. On Linux, libudev
+(part of https://www.freedesktop.org/wiki/Software/systemd[systemd]) is also
+required.
+
+For complete, OS-specific installation instructions, please refer to the
+`.actions/` (Linux, macOS) and `windows/` directories.
+
+On Linux, you will need to add a udev rule to be able to access the FIDO
+device, or run as root. For example, the udev rule may contain the following:
+
+----
+#udev rule for allowing HID access to Yubico devices for FIDO support.
+
+KERNEL=="hidraw*", SUBSYSTEM=="hidraw", \
+ MODE="0664", GROUP="plugdev", ATTRS{idVendor}=="1050"
+----
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 000000000000..ad3d44faad6b
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,69 @@
+# Copyright (c) 2018 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+list(APPEND COMPAT_SOURCES
+ ../openbsd-compat/clock_gettime.c
+ ../openbsd-compat/getopt_long.c
+ ../openbsd-compat/strlcat.c
+ ../openbsd-compat/strlcpy.c
+)
+
+if(WIN32 AND BUILD_SHARED_LIBS AND NOT CYGWIN AND NOT MSYS)
+ list(APPEND COMPAT_SOURCES ../openbsd-compat/posix_win.c)
+endif()
+
+# set the library to link against
+if(BUILD_STATIC_LIBS)
+ # drop -rdynamic
+ set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+ set(_FIDO2_LIBRARY fido2)
+elseif(BUILD_SHARED_LIBS)
+ set(_FIDO2_LIBRARY fido2_shared)
+else()
+ set(_FIDO2_LIBRARY ${CRYPTO_LIBRARIES} fido2)
+endif()
+
+# enable -Wconversion -Wsign-conversion
+if(NOT MSVC)
+ set_source_files_properties(assert.c cred.c info.c manifest.c reset.c
+ retries.c setpin.c util.c PROPERTIES COMPILE_FLAGS
+ "-Wconversion -Wsign-conversion")
+endif()
+
+# manifest
+add_executable(manifest manifest.c ${COMPAT_SOURCES})
+target_link_libraries(manifest ${_FIDO2_LIBRARY})
+
+# info
+add_executable(info info.c ${COMPAT_SOURCES})
+target_link_libraries(info ${_FIDO2_LIBRARY})
+
+# reset
+add_executable(reset reset.c util.c ${COMPAT_SOURCES})
+target_link_libraries(reset ${_FIDO2_LIBRARY})
+
+# cred
+add_executable(cred cred.c util.c ${COMPAT_SOURCES})
+target_link_libraries(cred ${_FIDO2_LIBRARY})
+
+# assert
+add_executable(assert assert.c util.c ${COMPAT_SOURCES})
+target_link_libraries(assert ${_FIDO2_LIBRARY})
+
+# setpin
+add_executable(setpin setpin.c ${COMPAT_SOURCES})
+target_link_libraries(setpin ${_FIDO2_LIBRARY})
+
+# retries
+add_executable(retries retries.c ${COMPAT_SOURCES})
+target_link_libraries(retries ${_FIDO2_LIBRARY})
+
+# select
+add_executable(select select.c ${COMPAT_SOURCES})
+target_link_libraries(select ${_FIDO2_LIBRARY})
+
+if(MINGW)
+ # needed for nanosleep() in mingw
+ target_link_libraries(select winpthread)
+endif()
diff --git a/examples/README.adoc b/examples/README.adoc
new file mode 100644
index 000000000000..bcecb22f5258
--- /dev/null
+++ b/examples/README.adoc
@@ -0,0 +1,98 @@
+= Examples
+
+=== Definitions
+
+The following definitions are used in the description below:
+
+- <device>
+
+ The file system path or subsystem-specific identification string of a
+ FIDO device.
+
+- <pin>, [oldpin]
+
+ Strings passed directly in the executed command's argument vector.
+
+- <cred_id>
+
+ The file system path of a file containing a FIDO credential ID in
+ binary representation.
+
+- <pubkey>
+
+ The file system path of a file containing a NIST P-256 public key in
+ PEM format.
+
+- <blobkey>
+
+ A credential's associated FIDO 2.1 "largeBlob" symmetric key.
+
+=== Description
+
+The following examples are provided:
+
+- manifest
+
+ Prints a list of configured FIDO devices.
+
+- info <device>
+
+ Prints information about <device>.
+
+- reset <device>
+
+ Performs a factory reset on <device>.
+
+- setpin <pin> [oldpin] <device>
+
+ Configures <pin> as the new PIN of <device>. If [oldpin] is provided,
+ the device's PIN is changed from [oldpin] to <pin>.
+
+- cred [-t ecdsa|rsa|eddsa] [-k pubkey] [-ei cred_id] [-P pin] [-T seconds]
+ [-b blobkey] [-hruv] <device>
+
+ Creates a new credential on <device> and verify that the credential
+ was signed by the authenticator. The device's attestation certificate
+ is not verified. If option -k is specified, the credential's public
+ key is stored in <pubkey>. If option -i is specified, the credential
+ ID is stored in <cred_id>. The -e option may be used to add <cred_id>
+ to the list of excluded credentials. If option -h is specified,
+ the hmac-secret FIDO2 extension is enabled on the generated
+ credential. If option -r is specified, the generated credential
+ will involve a resident key. User verification may be requested
+ through the -v option. If option -u is specified, the credential
+ is generated using U2F (CTAP1) instead of FIDO2 (CTAP2) commands.
+ The -T option may be used to enforce a timeout of <seconds>. If the
+ option -b is specified, the credential's "largeBlob" key is stored in
+ <blobkey>.
+
+- assert [-t ecdsa|rsa|eddsa] [-a cred_id] [-h hmac_secret] [-s hmac_salt]
+ [-P pin] [-T seconds] [-b blobkey] [-puv] <pubkey> <device>
+
+ Asks <device> for a FIDO2 assertion corresponding to [cred_id],
+ which may be omitted for resident keys. The obtained assertion
+ is verified using <pubkey>. The -p option requests that the user
+ be present. User verification may be requested through the -v
+ option. If option -u is specified, the assertion is generated using
+ U2F (CTAP1) instead of FIDO2 (CTAP2) commands. If option -s is
+ specified, a FIDO2 hmac-secret is requested from the authenticator,
+ and the contents of <hmac_salt> are used as the salt. If option -h
+ is specified, the resulting hmac-secret is stored in <hmac_secret>.
+ The -T option may be used to enforce a timeout of <seconds>. If the
+ option -b specified, the credential's "largeBlob" key is stored in
+ <blobkey>.
+
+- retries <device>
+ Get the number of PIN attempts left on <device> before lockout.
+
+- select
+
+ Enumerates available FIDO devices and, if more than one is present,
+ simultaneously requests touch on all of them, printing information
+ about the device touched.
+
+Debugging is possible through the use of the FIDO_DEBUG environment variable.
+If set, libfido2 will produce a log of its transactions with the authenticator.
+
+Additionally, an example of a WebAuthn client using libfido2 is available at
+https://github.com/martelletto/fido2-webauthn-client.
diff --git a/examples/assert.c b/examples/assert.c
new file mode 100644
index 000000000000..dc3fda3ac447
--- /dev/null
+++ b/examples/assert.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <fido.h>
+#include <fido/es256.h>
+#include <fido/rs256.h>
+#include <fido/eddsa.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "../openbsd-compat/openbsd-compat.h"
+#include "extern.h"
+
+static const unsigned char cdh[32] = {
+ 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
+ 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
+ 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
+ 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
+};
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: assert [-t ecdsa|rsa|eddsa] [-a cred_id] "
+ "[-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] "
+ "[-b blobkey] [-puv] <pubkey> <device>\n");
+ exit(EXIT_FAILURE);
+}
+
+static void
+verify_assert(int type, const unsigned char *authdata_ptr, size_t authdata_len,
+ const unsigned char *sig_ptr, size_t sig_len, bool up, bool uv, int ext,
+ const char *key)
+{
+ fido_assert_t *assert = NULL;
+ EC_KEY *ec = NULL;
+ RSA *rsa = NULL;
+ EVP_PKEY *eddsa = NULL;
+ es256_pk_t *es256_pk = NULL;
+ rs256_pk_t *rs256_pk = NULL;
+ eddsa_pk_t *eddsa_pk = NULL;
+ void *pk;
+ int r;
+
+ /* credential pubkey */
+ switch (type) {
+ case COSE_ES256:
+ if ((ec = read_ec_pubkey(key)) == NULL)
+ errx(1, "read_ec_pubkey");
+
+ if ((es256_pk = es256_pk_new()) == NULL)
+ errx(1, "es256_pk_new");
+
+ if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
+ errx(1, "es256_pk_from_EC_KEY");
+
+ pk = es256_pk;
+ EC_KEY_free(ec);
+ ec = NULL;
+
+ break;
+ case COSE_RS256:
+ if ((rsa = read_rsa_pubkey(key)) == NULL)
+ errx(1, "read_rsa_pubkey");
+
+ if ((rs256_pk = rs256_pk_new()) == NULL)
+ errx(1, "rs256_pk_new");
+
+ if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
+ errx(1, "rs256_pk_from_RSA");
+
+ pk = rs256_pk;
+ RSA_free(rsa);
+ rsa = NULL;
+
+ break;
+ case COSE_EDDSA:
+ if ((eddsa = read_eddsa_pubkey(key)) == NULL)
+ errx(1, "read_eddsa_pubkey");
+
+ if ((eddsa_pk = eddsa_pk_new()) == NULL)
+ errx(1, "eddsa_pk_new");
+
+ if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
+ errx(1, "eddsa_pk_from_EVP_PKEY");
+
+ pk = eddsa_pk;
+ EVP_PKEY_free(eddsa);
+ eddsa = NULL;
+
+ break;
+ default:
+ errx(1, "unknown credential type %d", type);
+ }
+
+ if ((assert = fido_assert_new()) == NULL)
+ errx(1, "fido_assert_new");
+
+ /* client data hash */
+ r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)",
+ fido_strerr(r), r);
+
+ /* relying party */
+ r = fido_assert_set_rp(assert, "localhost");
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
+
+ /* authdata */
+ r = fido_assert_set_count(assert, 1);
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r);
+ r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len);
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r);
+
+ /* extension */
+ r = fido_assert_set_extensions(assert, ext);
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
+ r);
+
+ /* user presence */
+ if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
+ errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
+
+ /* user verification */
+ if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
+ errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
+
+ /* sig */
+ r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r);
+
+ r = fido_assert_verify(assert, 0, type, pk);
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r);
+
+ es256_pk_free(&es256_pk);
+ rs256_pk_free(&rs256_pk);
+ eddsa_pk_free(&eddsa_pk);
+
+ fido_assert_free(&assert);
+}
+
+int
+main(int argc, char **argv)
+{
+ bool up = false;
+ bool uv = false;
+ bool u2f = false;
+ fido_dev_t *dev = NULL;
+ fido_assert_t *assert = NULL;
+ const char *pin = NULL;
+ const char *blobkey_out = NULL;
+ const char *hmac_out = NULL;
+ unsigned char *body = NULL;
+ long long seconds = 0;
+ size_t len;
+ int type = COSE_ES256;
+ int ext = 0;
+ int ch;
+ int r;
+
+ if ((assert = fido_assert_new()) == NULL)
+ errx(1, "fido_assert_new");
+
+ while ((ch = getopt(argc, argv, "P:T:a:b:h:ps:t:uv")) != -1) {
+ switch (ch) {
+ case 'P':
+ pin = optarg;
+ break;
+ case 'T':
+#ifndef SIGNAL_EXAMPLE
+ (void)seconds;
+ errx(1, "-T not supported");
+#else
+ if (base10(optarg, &seconds) < 0)
+ errx(1, "base10: %s", optarg);
+ if (seconds <= 0 || seconds > 30)
+ errx(1, "-T: %s must be in (0,30]", optarg);
+ break;
+#endif
+ case 'a':
+ if (read_blob(optarg, &body, &len) < 0)
+ errx(1, "read_blob: %s", optarg);
+ if ((r = fido_assert_allow_cred(assert, body,
+ len)) != FIDO_OK)
+ errx(1, "fido_assert_allow_cred: %s (0x%x)",
+ fido_strerr(r), r);
+ free(body);
+ body = NULL;
+ break;
+ case 'b':
+ ext |= FIDO_EXT_LARGEBLOB_KEY;
+ blobkey_out = optarg;
+ break;
+ case 'h':
+ hmac_out = optarg;
+ break;
+ case 'p':
+ up = true;
+ break;
+ case 's':
+ ext |= FIDO_EXT_HMAC_SECRET;
+ if (read_blob(optarg, &body, &len) < 0)
+ errx(1, "read_blob: %s", optarg);
+ if ((r = fido_assert_set_hmac_salt(assert, body,
+ len)) != FIDO_OK)
+ errx(1, "fido_assert_set_hmac_salt: %s (0x%x)",
+ fido_strerr(r), r);
+ free(body);
+ body = NULL;
+ break;
+ case 't':
+ if (strcmp(optarg, "ecdsa") == 0)
+ type = COSE_ES256;
+ else if (strcmp(optarg, "rsa") == 0)
+ type = COSE_RS256;
+ else if (strcmp(optarg, "eddsa") == 0)
+ type = COSE_EDDSA;
+ else
+ errx(1, "unknown type %s", optarg);
+ break;
+ case 'u':
+ u2f = true;
+ break;
+ case 'v':
+ uv = true;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ fido_init(0);
+
+ if ((dev = fido_dev_new()) == NULL)
+ errx(1, "fido_dev_new");
+
+ r = fido_dev_open(dev, argv[1]);
+ if (r != FIDO_OK)
+ errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
+ if (u2f)
+ fido_dev_force_u2f(dev);
+
+ /* client data hash */
+ r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)",
+ fido_strerr(r), r);
+
+ /* relying party */
+ r = fido_assert_set_rp(assert, "localhost");
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
+
+ /* extensions */
+ r = fido_assert_set_extensions(assert, ext);
+ if (r != FIDO_OK)
+ errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
+ r);
+
+ /* user presence */
+ if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
+ errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
+
+ /* user verification */
+ if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
+ errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
+
+#ifdef SIGNAL_EXAMPLE
+ prepare_signal_handler(SIGINT);
+ if (seconds) {
+ prepare_signal_handler(SIGALRM);
+ alarm((unsigned)seconds);
+ }
+#endif
+
+ r = fido_dev_get_assert(dev, assert, pin);
+ if (r != FIDO_OK) {
+#ifdef SIGNAL_EXAMPLE
+ if (got_signal)
+ fido_dev_cancel(dev);
+#endif
+ errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r);
+ }
+
+ r = fido_dev_close(dev);
+ if (r != FIDO_OK)
+ errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
+
+ fido_dev_free(&dev);
+
+ if (fido_assert_count(assert) != 1)
+ errx(1, "fido_assert_count: %d signatures returned",
+ (int)fido_assert_count(assert));
+
+ /* when verifying, pin implies uv */
+ if (pin)
+ uv = true;
+
+ verify_assert(type, fido_assert_authdata_ptr(assert, 0),
+ fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0),
+ fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]);
+
+ if (hmac_out != NULL) {
+ /* extract the hmac secret */
+ if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0),
+ fido_assert_hmac_secret_len(assert, 0)) < 0)
+ errx(1, "write_blob");
+ }
+
+ if (blobkey_out != NULL) {
+ /* extract the hmac secret */
+ if (write_blob(blobkey_out,
+ fido_assert_largeblob_key_ptr(assert, 0),
+ fido_assert_largeblob_key_len(assert, 0)) < 0)
+ errx(1, "write_blob");
+ }
+
+ fido_assert_free(&assert);
+
+ exit(0);
+}
diff --git a/examples/cred.c b/examples/cred.c
new file mode 100644
index 000000000000..74145c761380
--- /dev/null
+++ b/examples/cred.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <errno.h>
+#include <fido.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "../openbsd-compat/openbsd-compat.h"
+#include "extern.h"
+
+static const unsigned char cdh[32] = {
+ 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
+ 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26,
+ 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31,
+ 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b,
+};
+
+static const unsigned char user_id[32] = {
+ 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63,
+ 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2,
+ 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5,
+ 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49,
+};
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: cred [-t ecdsa|rsa|eddsa] [-k pubkey] "
+ "[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-hruv] "
+ "<device>\n");
+ exit(EXIT_FAILURE);
+}
+
+static void
+verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr,
+ size_t authdata_len, const unsigned char *x509_ptr, size_t x509_len,
+ const unsigned char *sig_ptr, size_t sig_len, bool rk, bool uv, int ext,
+ const char *key_out, const char *id_out)
+{
+ fido_cred_t *cred;
+ int r;
+
+ if ((cred = fido_cred_new()) == NULL)
+ errx(1, "fido_cred_new");
+
+ /* type */
+ r = fido_cred_set_type(cred, type);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
+
+ /* client data hash */
+ r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh));
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_clientdata_hash: %s (0x%x)",
+ fido_strerr(r), r);
+
+ /* relying party */
+ r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
+
+ /* authdata */
+ r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r);
+
+ /* extensions */
+ r = fido_cred_set_extensions(cred, ext);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
+
+ /* resident key */
+ if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
+ errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
+
+ /* user verification */
+ if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
+ errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
+
+ /* fmt */
+ r = fido_cred_set_fmt(cred, fmt);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r);
+
+ if (!strcmp(fido_cred_fmt(cred), "none")) {
+ warnx("no attestation data, skipping credential verification");
+ goto out;
+ }
+
+ /* x509 */
+ r = fido_cred_set_x509(cred, x509_ptr, x509_len);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_x509: %s (0x%x)", fido_strerr(r), r);
+
+ /* sig */
+ r = fido_cred_set_sig(cred, sig_ptr, sig_len);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_sig: %s (0x%x)", fido_strerr(r), r);
+
+ r = fido_cred_verify(cred);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r);
+
+out:
+ if (key_out != NULL) {
+ /* extract the credential pubkey */
+ if (type == COSE_ES256) {
+ if (write_ec_pubkey(key_out, fido_cred_pubkey_ptr(cred),
+ fido_cred_pubkey_len(cred)) < 0)
+ errx(1, "write_ec_pubkey");
+ } else if (type == COSE_RS256) {
+ if (write_rsa_pubkey(key_out, fido_cred_pubkey_ptr(cred),
+ fido_cred_pubkey_len(cred)) < 0)
+ errx(1, "write_rsa_pubkey");
+ } else if (type == COSE_EDDSA) {
+ if (write_eddsa_pubkey(key_out, fido_cred_pubkey_ptr(cred),
+ fido_cred_pubkey_len(cred)) < 0)
+ errx(1, "write_eddsa_pubkey");
+ }
+ }
+
+ if (id_out != NULL) {
+ /* extract the credential id */
+ if (write_blob(id_out, fido_cred_id_ptr(cred),
+ fido_cred_id_len(cred)) < 0)
+ errx(1, "write_blob");
+ }
+
+ fido_cred_free(&cred);
+}
+
+static fido_dev_t *
+open_from_manifest(const fido_dev_info_t *dev_infos, size_t len,
+ const char *path)
+{
+ size_t i;
+ fido_dev_t *dev;
+
+ for (i = 0; i < len; i++) {
+ const fido_dev_info_t *curr = fido_dev_info_ptr(dev_infos, i);
+ if (path == NULL ||
+ strcmp(path, fido_dev_info_path(curr)) == 0) {
+ dev = fido_dev_new_with_info(curr);
+ if (fido_dev_open_with_info(dev) == FIDO_OK)
+ return (dev);
+ fido_dev_free(&dev);
+ }
+ }
+
+ return (NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+ bool rk = false;
+ bool uv = false;
+ bool u2f = false;
+ fido_dev_t *dev;
+ fido_cred_t *cred = NULL;
+ const char *pin = NULL;
+ const char *blobkey_out = NULL;
+ const char *key_out = NULL;
+ const char *id_out = NULL;
+ const char *path = NULL;
+ unsigned char *body = NULL;
+ long long seconds = 0;
+ size_t len;
+ int type = COSE_ES256;
+ int ext = 0;
+ int ch;
+ int r;
+ fido_dev_info_t *dev_infos = NULL;
+ size_t dev_infos_len = 0;
+
+ if ((cred = fido_cred_new()) == NULL)
+ errx(1, "fido_cred_new");
+
+ while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uv")) != -1) {
+ switch (ch) {
+ case 'P':
+ pin = optarg;
+ break;
+ case 'T':
+#ifndef SIGNAL_EXAMPLE
+ (void)seconds;
+ errx(1, "-T not supported");
+#else
+ if (base10(optarg, &seconds) < 0)
+ errx(1, "base10: %s", optarg);
+ if (seconds <= 0 || seconds > 30)
+ errx(1, "-T: %s must be in (0,30]", optarg);
+ break;
+#endif
+ case 'b':
+ ext |= FIDO_EXT_LARGEBLOB_KEY;
+ blobkey_out = optarg;
+ break;
+ case 'e':
+ if (read_blob(optarg, &body, &len) < 0)
+ errx(1, "read_blob: %s", optarg);
+ r = fido_cred_exclude(cred, body, len);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_exclude: %s (0x%x)",
+ fido_strerr(r), r);
+ free(body);
+ body = NULL;
+ break;
+ case 'h':
+ ext |= FIDO_EXT_HMAC_SECRET;
+ break;
+ case 'i':
+ id_out = optarg;
+ break;
+ case 'k':
+ key_out = optarg;
+ break;
+ case 'r':
+ rk = true;
+ break;
+ case 't':
+ if (strcmp(optarg, "ecdsa") == 0)
+ type = COSE_ES256;
+ else if (strcmp(optarg, "rsa") == 0)
+ type = COSE_RS256;
+ else if (strcmp(optarg, "eddsa") == 0)
+ type = COSE_EDDSA;
+ else
+ errx(1, "unknown type %s", optarg);
+ break;
+ case 'u':
+ u2f = true;
+ break;
+ case 'v':
+ uv = true;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ fido_init(0);
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ usage();
+ dev_infos = fido_dev_info_new(16);
+ fido_dev_info_manifest(dev_infos, 16, &dev_infos_len);
+ if (argc == 1)
+ path = argv[0];
+
+ if ((dev = open_from_manifest(dev_infos, dev_infos_len, path)) == NULL)
+ errx(1, "open_from_manifest");
+
+ if (u2f)
+ fido_dev_force_u2f(dev);
+
+ /* type */
+ r = fido_cred_set_type(cred, type);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
+
+ /* client data hash */
+ r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh));
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_clientdata_hash: %s (0x%x)",
+ fido_strerr(r), r);
+
+ /* relying party */
+ r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
+
+ /* user */
+ r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith",
+ "jsmith", NULL);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r);
+
+ /* extensions */
+ r = fido_cred_set_extensions(cred, ext);
+ if (r != FIDO_OK)
+ errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
+
+ /* resident key */
+ if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
+ errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
+
+ /* user verification */
+ if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
+ errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
+
+#ifdef SIGNAL_EXAMPLE
+ prepare_signal_handler(SIGINT);
+ if (seconds) {
+ prepare_signal_handler(SIGALRM);
+ alarm((unsigned)seconds);
+ }
+#endif
+
+ r = fido_dev_make_cred(dev, cred, pin);
+ if (r != FIDO_OK) {
+#ifdef SIGNAL_EXAMPLE
+ if (got_signal)
+ fido_dev_cancel(dev);
+#endif
+ errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r);
+ }
+
+ r = fido_dev_close(dev);
+ if (r != FIDO_OK)
+ errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
+
+ fido_dev_free(&dev);
+
+ /* when verifying, pin implies uv */
+ if (pin)
+ uv = true;
+
+ verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred),
+ fido_cred_authdata_len(cred), fido_cred_x5c_ptr(cred),
+ fido_cred_x5c_len(cred), fido_cred_sig_ptr(cred),
+ fido_cred_sig_len(cred), rk, uv, ext, key_out, id_out);
+
+ if (blobkey_out != NULL) {
+ /* extract the "largeBlob" key */
+ if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred),
+ fido_cred_largeblob_key_len(cred)) < 0)
+ errx(1, "write_blob");
+ }
+
+ fido_cred_free(&cred);
+
+ exit(0);
+}
diff --git a/examples/extern.h b/examples/extern.h
new file mode 100644
index 000000000000..0ea68c4fb585
--- /dev/null
+++ b/examples/extern.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef _EXTERN_H_
+#define _EXTERN_H_
+
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+/* util.c */
+EC_KEY *read_ec_pubkey(const char *);
+RSA *read_rsa_pubkey(const char *);
+EVP_PKEY *read_eddsa_pubkey(const char *);
+int base10(const char *, long long *);
+int read_blob(const char *, unsigned char **, size_t *);
+int write_blob(const char *, const unsigned char *, size_t);
+int write_ec_pubkey(const char *, const void *, size_t);
+int write_rsa_pubkey(const char *, const void *, size_t);
+int write_eddsa_pubkey(const char *, const void *, size_t);
+#ifdef SIGNAL_EXAMPLE
+void prepare_signal_handler(int);
+extern volatile sig_atomic_t got_signal;
+#endif
+
+#endif /* _EXTERN_H_ */
diff --git a/examples/info.c b/examples/info.c
new file mode 100644
index 000000000000..72b786a8bd83
--- /dev/null
+++ b/examples/info.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <fido.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+/*
+ * Pretty-print a device's capabilities flags and return the result.
+ */
+static void
+format_flags(char *ret, size_t retlen, uint8_t flags)
+{
+ memset(ret, 0, retlen);
+
+ if (flags & FIDO_CAP_WINK) {
+ if (strlcat(ret, "wink,", retlen) >= retlen)
+ goto toolong;
+ } else {
+ if (strlcat(ret, "nowink,", retlen) >= retlen)
+ goto toolong;
+ }
+
+ if (flags & FIDO_CAP_CBOR) {
+ if (strlcat(ret, " cbor,", retlen) >= retlen)
+ goto toolong;
+ } else {
+ if (strlcat(ret, " nocbor,", retlen) >= retlen)
+ goto toolong;
+ }
+
+ if (flags & FIDO_CAP_NMSG) {
+ if (strlcat(ret, " nomsg", retlen) >= retlen)
+ goto toolong;
+ } else {
+ if (strlcat(ret, " msg", retlen) >= retlen)
+ goto toolong;
+ }
+
+ return;
+toolong:
+ strlcpy(ret, "toolong", retlen);
+}
+
+/*
+ * Print a FIDO device's attributes on stdout.
+ */
+static void
+print_attr(const fido_dev_t *dev)
+{
+ char flags_txt[128];
+
+ printf("proto: 0x%02x\n", fido_dev_protocol(dev));
+ printf("major: 0x%02x\n", fido_dev_major(dev));
+ printf("minor: 0x%02x\n", fido_dev_minor(dev));
+ printf("build: 0x%02x\n", fido_dev_build(dev));
+
+ format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev));
+ printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt);
+}
+
+/*
+ * Auxiliary function to print an array of strings on stdout.
+ */
+static void
+print_str_array(const char *label, char * const *sa, size_t len)
+{
+ if (len == 0)
+ return;
+
+ printf("%s strings: ", label);
+
+ for (size_t i = 0; i < len; i++)
+ printf("%s%s", i > 0 ? ", " : "", sa[i]);
+
+ printf("\n");
+}
+
+/*
+ * Auxiliary function to print (char *, bool) pairs on stdout.
+ */
+static void
+print_opt_array(const char *label, char * const *name, const bool *value,
+ size_t len)
+{
+ if (len == 0)
+ return;
+
+ printf("%s: ", label);
+
+ for (size_t i = 0; i < len; i++)
+ printf("%s%s%s", i > 0 ? ", " : "",
+ value[i] ? "" : "no", name[i]);
+
+ printf("\n");
+}
+
+/*
+ * Auxiliary function to print a list of supported COSE algorithms on stdout.
+ */
+static void
+print_algorithms(const fido_cbor_info_t *ci)
+{
+ const char *cose, *type;
+ size_t len;
+
+ if ((len = fido_cbor_info_algorithm_count(ci)) == 0)
+ return;
+
+ printf("algorithms: ");
+
+ for (size_t i = 0; i < len; i++) {
+ cose = type = "unknown";
+ switch (fido_cbor_info_algorithm_cose(ci, i)) {
+ case COSE_EDDSA:
+ cose = "eddsa";
+ break;
+ case COSE_ES256:
+ cose = "es256";
+ break;
+ case COSE_RS256:
+ cose = "rs256";
+ break;
+ }
+ if (fido_cbor_info_algorithm_type(ci, i) != NULL)
+ type = fido_cbor_info_algorithm_type(ci, i);
+ printf("%s%s (%s)", i > 0 ? ", " : "", cose, type);
+ }
+
+ printf("\n");
+}
+
+/*
+ * Auxiliary function to print an authenticator's AAGUID on stdout.
+ */
+static void
+print_aaguid(const unsigned char *buf, size_t buflen)
+{
+ printf("aaguid: ");
+
+ while (buflen--)
+ printf("%02x", *buf++);
+
+ printf("\n");
+}
+
+/*
+ * Auxiliary function to print an authenticator's maximum message size on
+ * stdout.
+ */
+static void
+print_maxmsgsiz(uint64_t maxmsgsiz)
+{
+ printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
+}
+
+/*
+ * Auxiliary function to print an authenticator's maximum number of credentials
+ * in a credential list on stdout.
+ */
+static void
+print_maxcredcntlst(uint64_t maxcredcntlst)
+{
+ printf("maxcredcntlst: %d\n", (int)maxcredcntlst);
+}
+
+/*
+ * Auxiliary function to print an authenticator's maximum credential ID length
+ * on stdout.
+ */
+static void
+print_maxcredidlen(uint64_t maxcredidlen)
+{
+ printf("maxcredlen: %d\n", (int)maxcredidlen);
+}
+
+/*
+ * Auxiliary function to print an authenticator's firmware version on stdout.
+ */
+static void
+print_fwversion(uint64_t fwversion)
+{
+ printf("fwversion: 0x%x\n", (int)fwversion);
+}
+
+/*
+ * Auxiliary function to print an array of bytes on stdout.
+ */
+static void
+print_byte_array(const char *label, const uint8_t *ba, size_t len)
+{
+ if (len == 0)
+ return;
+
+ printf("%s: ", label);
+
+ for (size_t i = 0; i < len; i++)
+ printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]);
+
+ printf("\n");
+}
+
+static void
+getinfo(const char *path)
+{
+ fido_dev_t *dev;
+ fido_cbor_info_t *ci;
+ int r;
+
+ fido_init(0);
+
+ if ((dev = fido_dev_new()) == NULL)
+ errx(1, "fido_dev_new");
+ if ((r = fido_dev_open(dev, path)) != FIDO_OK)
+ errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
+
+ print_attr(dev);
+
+ if (fido_dev_is_fido2(dev) == false)
+ goto end;
+ if ((ci = fido_cbor_info_new()) == NULL)
+ errx(1, "fido_cbor_info_new");
+ if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK)
+ errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
+
+ /* print supported protocol versions */
+ print_str_array("version", fido_cbor_info_versions_ptr(ci),
+ fido_cbor_info_versions_len(ci));
+
+ /* print supported extensions */
+ print_str_array("extension", fido_cbor_info_extensions_ptr(ci),
+ fido_cbor_info_extensions_len(ci));
+
+ /* print supported transports */
+ print_str_array("transport", fido_cbor_info_transports_ptr(ci),
+ fido_cbor_info_transports_len(ci));
+
+ /* print supported algorithms */
+ print_algorithms(ci);
+
+ /* print aaguid */
+ print_aaguid(fido_cbor_info_aaguid_ptr(ci),
+ fido_cbor_info_aaguid_len(ci));
+
+ /* print supported options */
+ print_opt_array("options", fido_cbor_info_options_name_ptr(ci),
+ fido_cbor_info_options_value_ptr(ci),
+ fido_cbor_info_options_len(ci));
+
+ /* print maximum message size */
+ print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci));
+
+ /* print maximum number of credentials allowed in credential lists */
+ print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci));
+
+ /* print maximum length of a credential ID */
+ print_maxcredidlen(fido_cbor_info_maxcredidlen(ci));
+
+ /* print firmware version */
+ print_fwversion(fido_cbor_info_fwversion(ci));
+
+ /* print supported pin protocols */
+ print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci),
+ fido_cbor_info_protocols_len(ci));
+
+ fido_cbor_info_free(&ci);
+end:
+ if ((r = fido_dev_close(dev)) != FIDO_OK)
+ errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
+
+ fido_dev_free(&dev);
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: info <device>\n");
+ exit(EXIT_FAILURE);
+ }
+
+ getinfo(argv[1]);
+
+ exit(0);
+}
diff --git a/examples/manifest.c b/examples/manifest.c
new file mode 100644
index 000000000000..d38166a9fea9
--- /dev/null
+++ b/examples/manifest.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <fido.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+int
+main(void)
+{
+ fido_dev_info_t *devlist;
+ size_t ndevs;
+ int r;
+
+ fido_init(0);
+
+ if ((devlist = fido_dev_info_new(64)) == NULL)
+ errx(1, "fido_dev_info_new");
+
+ if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
+ errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
+
+ for (size_t i = 0; i < ndevs; i++) {
+ const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
+ printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
+ fido_dev_info_path(di),
+ (uint16_t)fido_dev_info_vendor(di),
+ (uint16_t)fido_dev_info_product(di),
+ fido_dev_info_manufacturer_string(di),
+ fido_dev_info_product_string(di));
+ }
+
+ fido_dev_info_free(&devlist, ndevs);
+
+ exit(0);
+}
diff --git a/examples/reset.c b/examples/reset.c
new file mode 100644
index 000000000000..eb341c26c0cd
--- /dev/null
+++ b/examples/reset.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+/*
+ * Perform a factory reset on a given authenticator.
+ */
+
+#include <fido.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../openbsd-compat/openbsd-compat.h"
+#include "extern.h"
+
+int
+main(int argc, char **argv)
+{
+ fido_dev_t *dev;
+ int r;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: reset <device>\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fido_init(0);
+
+ if ((dev = fido_dev_new()) == NULL)
+ errx(1, "fido_dev_new");
+
+ if ((r = fido_dev_open(dev, argv[1])) != FIDO_OK)
+ errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
+
+#ifdef SIGNAL_EXAMPLE
+ prepare_signal_handler(SIGINT);
+#endif
+
+ if ((r = fido_dev_reset(dev)) != FIDO_OK) {
+#ifdef SIGNAL_EXAMPLE
+ if (got_signal)
+ fido_dev_cancel(dev);
+#endif
+ errx(1, "fido_reset: %s (0x%x)", fido_strerr(r), r);
+ }
+
+ if ((r = fido_dev_close(dev)) != FIDO_OK)
+ errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
+
+ fido_dev_free(&dev);
+
+ exit(0);
+}
diff --git a/examples/retries.c b/examples/retries.c
new file mode 100644
index 000000000000..b96118b1e154
--- /dev/null
+++ b/examples/retries.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+/*
+ * Get an authenticator's number of PIN attempts left.
+ */
+
+#include <fido.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+int
+main(int argc, char **argv)
+{
+ fido_dev_t *dev;
+ int n;
+ int r;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: retries <device>\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fido_init(0);
+
+ if ((dev = fido_dev_new()) == NULL)
+ errx(1, "fido_dev_new");
+
+ if ((r = fido_dev_open(dev, argv[1])) != FIDO_OK)
+ errx(1, "fido_open: %s (0x%x)", fido_strerr(r), r);
+
+ if ((r = fido_dev_get_retry_count(dev, &n)) != FIDO_OK)
+ errx(1, "fido_get_retries: %s (0x%x)", fido_strerr(r), r);
+
+ if ((r = fido_dev_close(dev)) != FIDO_OK)
+ errx(1, "fido_close: %s (0x%x)", fido_strerr(r), r);
+
+ fido_dev_free(&dev);
+
+ printf("%d\n", n);
+
+ exit(0);
+}
diff --git a/examples/select.c b/examples/select.c
new file mode 100644
index 000000000000..6ede9b490a95
--- /dev/null
+++ b/examples/select.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <errno.h>
+#include <fido.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+#define FIDO_POLL_MS 50
+
+#if defined(_MSC_VER)
+static int
+nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
+{
+ if (rmtp != NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ Sleep(rqtp->tv_nsec / 1000000);
+
+ return (0);
+}
+#endif
+
+static fido_dev_t *
+open_dev(const fido_dev_info_t *di)
+{
+ fido_dev_t *dev;
+ int r;
+
+ if ((dev = fido_dev_new()) == NULL) {
+ warnx("%s: fido_dev_new", __func__);
+ return (NULL);
+ }
+
+ if ((r = fido_dev_open(dev, fido_dev_info_path(di))) != FIDO_OK) {
+ warnx("%s: fido_dev_open %s: %s", __func__,
+ fido_dev_info_path(di), fido_strerr(r));
+ fido_dev_free(&dev);
+ return (NULL);
+ }
+
+ printf("%s (0x%04x:0x%04x) is %s\n", fido_dev_info_path(di),
+ fido_dev_info_vendor(di), fido_dev_info_product(di),
+ fido_dev_is_fido2(dev) ? "fido2" : "u2f");
+
+ return (dev);
+}
+
+static int
+select_dev(const fido_dev_info_t *devlist, size_t ndevs, fido_dev_t **dev,
+ size_t *idx, int secs)
+{
+ const fido_dev_info_t *di;
+ fido_dev_t **devtab;
+ struct timespec ts_start;
+ struct timespec ts_now;
+ struct timespec ts_delta;
+ struct timespec ts_pause;
+ size_t nopen = 0;
+ int touched;
+ int r;
+ long ms_remain;
+
+ *dev = NULL;
+ *idx = 0;
+
+ printf("%u authenticator(s) detected\n", (unsigned)ndevs);
+
+ if (ndevs == 0)
+ return (0); /* nothing to do */
+
+ if ((devtab = calloc(ndevs, sizeof(*devtab))) == NULL) {
+ warn("%s: calloc", __func__);
+ return (-1);
+ }
+
+ for (size_t i = 0; i < ndevs; i++) {
+ di = fido_dev_info_ptr(devlist, i);
+ if ((devtab[i] = open_dev(di)) != NULL) {
+ *idx = i;
+ nopen++;
+ }
+ }
+
+ printf("%u authenticator(s) opened\n", (unsigned)nopen);
+
+ if (nopen < 2) {
+ if (nopen == 1)
+ *dev = devtab[*idx]; /* single candidate */
+ r = 0;
+ goto out;
+ }
+
+ for (size_t i = 0; i < ndevs; i++) {
+ di = fido_dev_info_ptr(devlist, i);
+ if (devtab[i] == NULL)
+ continue; /* failed to open */
+ if ((r = fido_dev_get_touch_begin(devtab[i])) != FIDO_OK) {
+ warnx("%s: fido_dev_get_touch_begin %s: %s", __func__,
+ fido_dev_info_path(di), fido_strerr(r));
+ r = -1;
+ goto out;
+ }
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
+ warn("%s: clock_gettime", __func__);
+ r = -1;
+ goto out;
+ }
+
+ ts_pause.tv_sec = 0;
+ ts_pause.tv_nsec = 200000000; /* 200ms */
+
+ do {
+ nanosleep(&ts_pause, NULL);
+
+ for (size_t i = 0; i < ndevs; i++) {
+ di = fido_dev_info_ptr(devlist, i);
+ if (devtab[i] == NULL) {
+ /* failed to open or discarded */
+ continue;
+ }
+ if ((r = fido_dev_get_touch_status(devtab[i], &touched,
+ FIDO_POLL_MS)) != FIDO_OK) {
+ warnx("%s: fido_dev_get_touch_status %s: %s",
+ __func__, fido_dev_info_path(di),
+ fido_strerr(r));
+ fido_dev_close(devtab[i]);
+ fido_dev_free(&devtab[i]);
+ continue; /* discard */
+ }
+ if (touched) {
+ *dev = devtab[i];
+ *idx = i;
+ r = 0;
+ goto out;
+ }
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
+ warn("%s: clock_gettime", __func__);
+ r = -1;
+ goto out;
+ }
+
+ timespecsub(&ts_now, &ts_start, &ts_delta);
+ ms_remain = (secs * 1000) - ((long)ts_delta.tv_sec * 1000) +
+ ((long)ts_delta.tv_nsec / 1000000);
+ } while (ms_remain > FIDO_POLL_MS);
+
+ printf("timeout after %d seconds\n", secs);
+ r = -1;
+out:
+ if (r != 0) {
+ *dev = NULL;
+ *idx = 0;
+ }
+
+ for (size_t i = 0; i < ndevs; i++) {
+ if (devtab[i] && devtab[i] != *dev) {
+ fido_dev_cancel(devtab[i]);
+ fido_dev_close(devtab[i]);
+ fido_dev_free(&devtab[i]);
+ }
+ }
+
+ free(devtab);
+
+ return (r);
+}
+
+int
+main(void)
+{
+ const fido_dev_info_t *di;
+ fido_dev_info_t *devlist;
+ fido_dev_t *dev;
+ size_t idx;
+ size_t ndevs;
+ int r;
+
+ fido_init(0);
+
+ if ((devlist = fido_dev_info_new(64)) == NULL)
+ errx(1, "fido_dev_info_new");
+
+ if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
+ errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
+ if (select_dev(devlist, ndevs, &dev, &idx, 15) != 0)
+ errx(1, "select_dev");
+ if (dev == NULL)
+ errx(1, "no authenticator found");
+
+ di = fido_dev_info_ptr(devlist, idx);
+ printf("%s: %s by %s (PIN %sset)\n", fido_dev_info_path(di),
+ fido_dev_info_product_string(di),
+ fido_dev_info_manufacturer_string(di),
+ fido_dev_has_pin(dev) ? "" : "un");
+
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+ fido_dev_info_free(&devlist, ndevs);
+
+ exit(0);
+}
diff --git a/examples/setpin.c b/examples/setpin.c
new file mode 100644
index 000000000000..4b9e792769d9
--- /dev/null
+++ b/examples/setpin.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+/*
+ * Configure a PIN on a given authenticator.
+ */
+
+#include <fido.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+static void
+setpin(const char *path, const char *pin, const char *oldpin)
+{
+ fido_dev_t *dev;
+ int r;
+
+ fido_init(0);
+
+ if ((dev = fido_dev_new()) == NULL)
+ errx(1, "fido_dev_new");
+
+ if ((r = fido_dev_open(dev, path)) != FIDO_OK)
+ errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
+
+ if ((r = fido_dev_set_pin(dev, pin, oldpin)) != FIDO_OK)
+ errx(1, "fido_setpin: %s (0x%x)", fido_strerr(r), r);
+
+ if ((r = fido_dev_close(dev)) != FIDO_OK)
+ errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
+
+ fido_dev_free(&dev);
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc < 3 || argc > 4) {
+ fprintf(stderr, "usage: setpin <pin> [oldpin] <device>\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc == 3)
+ setpin(argv[2], argv[1], NULL);
+ else
+ setpin(argv[3], argv[1], argv[2]);
+
+ exit(0);
+}
diff --git a/examples/util.c b/examples/util.c
new file mode 100644
index 000000000000..caa68aa880ee
--- /dev/null
+++ b/examples/util.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+
+#include <fido.h>
+#include <fido/es256.h>
+#include <fido/rs256.h>
+#include <fido/eddsa.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef _MSC_VER
+#include "../openbsd-compat/posix_win.h"
+#endif
+#include "../openbsd-compat/openbsd-compat.h"
+#include "extern.h"
+
+#ifdef SIGNAL_EXAMPLE
+volatile sig_atomic_t got_signal = 0;
+
+static void
+signal_handler(int signo)
+{
+ (void)signo;
+ got_signal = 1;
+}
+
+void
+prepare_signal_handler(int signo)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = signal_handler;
+
+ if (sigaction(signo, &sa, NULL) < 0)
+ err(1, "sigaction");
+}
+#endif
+
+int
+base10(const char *str, long long *ll)
+{
+ char *ep;
+
+ *ll = strtoll(str, &ep, 10);
+ if (str == ep || *ep != '\0')
+ return (-1);
+ else if (*ll == LLONG_MIN && errno == ERANGE)
+ return (-1);
+ else if (*ll == LLONG_MAX && errno == ERANGE)
+ return (-1);
+
+ return (0);
+}
+
+int
+write_blob(const char *path, const unsigned char *ptr, size_t len)
+{
+ int fd, ok = -1;
+ ssize_t n;
+
+ if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) {
+ warn("open %s", path);
+ goto fail;
+ }
+
+ if ((n = write(fd, ptr, len)) < 0) {
+ warn("write");
+ goto fail;
+ }
+ if ((size_t)n != len) {
+ warnx("write");
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return (ok);
+}
+
+int
+read_blob(const char *path, unsigned char **ptr, size_t *len)
+{
+ int fd, ok = -1;
+ struct stat st;
+ ssize_t n;
+
+ *ptr = NULL;
+ *len = 0;
+
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ warn("open %s", path);
+ goto fail;
+ }
+ if (fstat(fd, &st) < 0) {
+ warn("stat %s", path);
+ goto fail;
+ }
+ if (st.st_size < 0) {
+ warnx("stat %s: invalid size", path);
+ goto fail;
+ }
+ *len = (size_t)st.st_size;
+ if ((*ptr = malloc(*len)) == NULL) {
+ warn("malloc");
+ goto fail;
+ }
+ if ((n = read(fd, *ptr, *len)) < 0) {
+ warn("read");
+ goto fail;
+ }
+ if ((size_t)n != *len) {
+ warnx("read");
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (fd != -1) {
+ close(fd);
+ }
+ if (ok < 0) {
+ free(*ptr);
+ *ptr = NULL;
+ *len = 0;
+ }
+
+ return (ok);
+}
+
+EC_KEY *
+read_ec_pubkey(const char *path)
+{
+ FILE *fp = NULL;
+ EVP_PKEY *pkey = NULL;
+ EC_KEY *ec = NULL;
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ warn("fopen");
+ goto fail;
+ }
+
+ if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
+ warnx("PEM_read_PUBKEY");
+ goto fail;
+ }
+ if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
+ warnx("EVP_PKEY_get1_EC_KEY");
+ goto fail;
+ }
+
+fail:
+ if (fp != NULL) {
+ fclose(fp);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+
+ return (ec);
+}
+
+int
+write_ec_pubkey(const char *path, const void *ptr, size_t len)
+{
+ FILE *fp = NULL;
+ EVP_PKEY *pkey = NULL;
+ es256_pk_t *pk = NULL;
+ int fd = -1;
+ int ok = -1;
+
+ if ((pk = es256_pk_new()) == NULL) {
+ warnx("es256_pk_new");
+ goto fail;
+ }
+
+ if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
+ warnx("es256_pk_from_ptr");
+ goto fail;
+ }
+
+ if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
+ warn("open %s", path);
+ goto fail;
+ }
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ warn("fdopen");
+ goto fail;
+ }
+ fd = -1; /* owned by fp now */
+
+ if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
+ warnx("es256_pk_to_EVP_PKEY");
+ goto fail;
+ }
+
+ if (PEM_write_PUBKEY(fp, pkey) == 0) {
+ warnx("PEM_write_PUBKEY");
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ es256_pk_free(&pk);
+
+ if (fp != NULL) {
+ fclose(fp);
+ }
+ if (fd != -1) {
+ close(fd);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+
+ return (ok);
+}
+
+RSA *
+read_rsa_pubkey(const char *path)
+{
+ FILE *fp = NULL;
+ EVP_PKEY *pkey = NULL;
+ RSA *rsa = NULL;
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ warn("fopen");
+ goto fail;
+ }
+
+ if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
+ warnx("PEM_read_PUBKEY");
+ goto fail;
+ }
+ if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
+ warnx("EVP_PKEY_get1_RSA");
+ goto fail;
+ }
+
+fail:
+ if (fp != NULL) {
+ fclose(fp);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+
+ return (rsa);
+}
+
+int
+write_rsa_pubkey(const char *path, const void *ptr, size_t len)
+{
+ FILE *fp = NULL;
+ EVP_PKEY *pkey = NULL;
+ rs256_pk_t *pk = NULL;
+ int fd = -1;
+ int ok = -1;
+
+ if ((pk = rs256_pk_new()) == NULL) {
+ warnx("rs256_pk_new");
+ goto fail;
+ }
+
+ if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
+ warnx("rs256_pk_from_ptr");
+ goto fail;
+ }
+
+ if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
+ warn("open %s", path);
+ goto fail;
+ }
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ warn("fdopen");
+ goto fail;
+ }
+ fd = -1; /* owned by fp now */
+
+ if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
+ warnx("rs256_pk_to_EVP_PKEY");
+ goto fail;
+ }
+
+ if (PEM_write_PUBKEY(fp, pkey) == 0) {
+ warnx("PEM_write_PUBKEY");
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ rs256_pk_free(&pk);
+
+ if (fp != NULL) {
+ fclose(fp);
+ }
+ if (fd != -1) {
+ close(fd);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+
+ return (ok);
+}
+
+EVP_PKEY *
+read_eddsa_pubkey(const char *path)
+{
+ FILE *fp = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ warn("fopen");
+ goto fail;
+ }
+
+ if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
+ warnx("PEM_read_PUBKEY");
+ goto fail;
+ }
+
+fail:
+ if (fp) {
+ fclose(fp);
+ }
+
+ return (pkey);
+}
+
+int
+write_eddsa_pubkey(const char *path, const void *ptr, size_t len)
+{
+ FILE *fp = NULL;
+ EVP_PKEY *pkey = NULL;
+ eddsa_pk_t *pk = NULL;
+ int fd = -1;
+ int ok = -1;
+
+ if ((pk = eddsa_pk_new()) == NULL) {
+ warnx("eddsa_pk_new");
+ goto fail;
+ }
+
+ if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
+ warnx("eddsa_pk_from_ptr");
+ goto fail;
+ }
+
+ if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
+ warn("open %s", path);
+ goto fail;
+ }
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ warn("fdopen");
+ goto fail;
+ }
+ fd = -1; /* owned by fp now */
+
+ if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
+ warnx("eddsa_pk_to_EVP_PKEY");
+ goto fail;
+ }
+
+ if (PEM_write_PUBKEY(fp, pkey) == 0) {
+ warnx("PEM_write_PUBKEY");
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ eddsa_pk_free(&pk);
+
+ if (fp != NULL) {
+ fclose(fp);
+ }
+ if (fd != -1) {
+ close(fd);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+
+ return (ok);
+}
diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt
new file mode 100644
index 000000000000..b1eebd55481b
--- /dev/null
+++ b/fuzz/CMakeLists.txt
@@ -0,0 +1,63 @@
+# Copyright (c) 2019 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+list(APPEND COMPAT_SOURCES
+ ../openbsd-compat/strlcpy.c
+ ../openbsd-compat/strlcat.c
+)
+
+list(APPEND COMMON_SOURCES
+ libfuzzer.c
+ mutator_aux.c
+)
+
+set(FUZZ_LDFLAGS "-fsanitize=fuzzer")
+
+# fuzz_cred
+add_executable(fuzz_cred fuzz_cred.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
+target_compile_options(fuzz_cred PRIVATE ${FUZZ_LDFLAGS})
+set_target_properties(fuzz_cred PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
+target_link_libraries(fuzz_cred fido2_shared)
+
+# fuzz_assert
+add_executable(fuzz_assert fuzz_assert.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
+target_compile_options(fuzz_assert PRIVATE ${FUZZ_LDFLAGS})
+set_target_properties(fuzz_assert PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
+target_link_libraries(fuzz_assert fido2_shared)
+
+# fuzz_mgmt
+add_executable(fuzz_mgmt fuzz_mgmt.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
+target_compile_options(fuzz_mgmt PRIVATE ${FUZZ_LDFLAGS})
+set_target_properties(fuzz_mgmt PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
+target_link_libraries(fuzz_mgmt fido2_shared)
+
+# fuzz_credman
+add_executable(fuzz_credman fuzz_credman.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
+target_compile_options(fuzz_credman PRIVATE ${FUZZ_LDFLAGS})
+set_target_properties(fuzz_credman PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
+target_link_libraries(fuzz_credman fido2_shared)
+
+# fuzz_bio
+add_executable(fuzz_bio fuzz_bio.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
+target_compile_options(fuzz_bio PRIVATE ${FUZZ_LDFLAGS})
+set_target_properties(fuzz_bio PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
+target_link_libraries(fuzz_bio fido2_shared)
+
+# fuzz_hid
+add_executable(fuzz_hid fuzz_hid.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
+target_compile_options(fuzz_hid PRIVATE ${FUZZ_LDFLAGS})
+set_target_properties(fuzz_hid PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
+target_link_libraries(fuzz_hid fido2_shared)
+
+# fuzz_netlink
+add_executable(fuzz_netlink fuzz_netlink.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
+target_compile_options(fuzz_netlink PRIVATE ${FUZZ_LDFLAGS})
+set_target_properties(fuzz_netlink PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
+target_link_libraries(fuzz_netlink fido2_shared)
+
+# fuzz_largeblob
+add_executable(fuzz_largeblob fuzz_largeblob.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
+target_compile_options(fuzz_largeblob PRIVATE ${FUZZ_LDFLAGS})
+set_target_properties(fuzz_largeblob PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
+target_link_libraries(fuzz_largeblob fido2_shared)
diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile
new file mode 100644
index 000000000000..895da69e4c4c
--- /dev/null
+++ b/fuzz/Dockerfile
@@ -0,0 +1,12 @@
+# Copyright (c) 2019 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+FROM ubuntu:focal
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt-get update
+RUN apt-get install -y clang-11 cmake git libssl-dev libudev-dev make pkg-config
+RUN apt-get install -y zlib1g-dev
+RUN git clone --branch v0.8.0 https://github.com/PJK/libcbor
+RUN git clone https://github.com/yubico/libfido2
+RUN CC=clang-11 CXX=clang++-11 /libfido2/fuzz/build-coverage /libcbor /libfido2
diff --git a/fuzz/Makefile b/fuzz/Makefile
new file mode 100644
index 000000000000..4b067c23aac2
--- /dev/null
+++ b/fuzz/Makefile
@@ -0,0 +1,79 @@
+# Copyright (c) 2019 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+IMAGE := libfido2-coverage:1.8.0
+RUNNER := libfido2-runner
+PROFDATA := llvm-profdata-11
+COV := llvm-cov-11
+TARGETS := fuzz_assert fuzz_bio fuzz_cred fuzz_credman fuzz_hid \
+ fuzz_largeblob fuzz_netlink fuzz_mgmt
+CORPORA := $(foreach f,${TARGETS},${f}/corpus)
+MINIFY := $(foreach f,${TARGETS},/minify/${f}/corpus)
+REMOTE := gs://libfido2-corpus.clusterfuzz-external.appspot.com
+.DEFAULT_GOAL := all
+
+all: ${TARGETS}
+
+build:
+ docker build -t ${IMAGE} - < Dockerfile
+
+run: build
+ -docker run -it -d --name ${RUNNER} ${IMAGE}
+ docker start ${RUNNER}
+
+sync: run
+ tar Ccf .. - src fuzz | docker exec -i ${RUNNER} tar Cxf /libfido2 -
+ docker exec ${RUNNER} make -C libfido2/build
+
+corpus: sync
+ docker exec ${RUNNER} /bin/sh -c 'cd /libfido2/fuzz && rm -rf ${TARGETS}'
+ docker exec ${RUNNER} tar Czxf /libfido2/fuzz /libfido2/fuzz/corpus.tgz
+
+${TARGETS}: corpus sync
+ docker exec -e LLVM_PROFILE_FILE=/profraw/$@ ${RUNNER} \
+ /bin/sh -c 'rm -f /profraw/$@ && /libfido2/build/fuzz/$@ \
+ -runs=1 /libfido2/fuzz/$@'
+
+${MINIFY}: /minify/%/corpus: %
+ docker exec ${RUNNER} /bin/sh -c 'rm -rf $@ && mkdir -p $@ && \
+ /libfido2/build/fuzz/$< -use_value_profile=1 -merge=1 $@ \
+ /libfido2/fuzz/$</corpus'
+
+corpus.tgz-: ${MINIFY}
+ docker exec -i ${RUNNER} tar Czcf /minify - ${TARGETS} > $@
+
+profdata: run
+ docker exec ${RUNNER} /bin/sh -c 'rm -f /$@ && ${PROFDATA} \
+ merge -sparse profraw/* -o $@'
+
+report.tgz: profdata
+ docker exec ${RUNNER} /bin/sh -c 'rm -rf /report && mkdir /report && \
+ ${COV} show -format=html -tab-size=8 -instr-profile=/$< \
+ -output-dir=/report /libfido2/build/src/libfido2.so'
+ docker exec -i ${RUNNER} tar Czcf / - report > $@
+
+summary.txt: profdata
+ docker exec ${RUNNER} ${COV} report -use-color=false \
+ /libfido2/build/src/libfido2.so -instr-profile=/$< > $@
+
+functions.txt: profdata
+ docker exec ${RUNNER} /bin/sh -c '${COV} report -use-color=false \
+ -show-functions -instr-profile=/$< \
+ /libfido2/build/src/libfido2.so /libfido2/src/*.[ch]' > $@
+
+clean: run
+ docker exec ${RUNNER} /bin/sh -c 'rm -rf /profraw /profdata && \
+ make -C /libfido2/build clean'
+ -docker stop ${RUNNER}
+ rm -rf ${TARGETS}
+
+${CORPORA}:
+ -mkdir -p $@
+ gsutil -q -m rsync -d -r ${REMOTE}/libFuzzer/libfido2_$(@:/corpus=) $@
+
+corpus.tgz: ${CORPORA}
+ tar zcf $@ ${TARGETS}
+
+.PHONY: build run sync corpus ${TARGETS} ${CORPORA}
+.PHONY: report.tgz summary.txt functions.txt
diff --git a/fuzz/README b/fuzz/README
new file mode 100644
index 000000000000..28fc7f8f51b2
--- /dev/null
+++ b/fuzz/README
@@ -0,0 +1,33 @@
+libfido2 can be fuzzed using AFL or libFuzzer, with or without
+ASAN/MSAN/UBSAN.
+
+AFL is more convenient when fuzzing the path from the authenticator to
+libfido2 in an existing application. To do so, use preload-snoop.c with a real
+authenticator to obtain an initial corpus, rebuild libfido2 with -DFUZZ=ON, and
+use preload-fuzz.c to read device data from stdin.
+
+libFuzzer is better suited for bespoke fuzzers; see fuzz_cred.c, fuzz_credman.c,
+fuzz_assert.c, fuzz_hid.c, and fuzz_mgmt.c for examples. To build these
+harnesses, use -DFUZZ=ON -DLIBFUZZER=ON.
+
+To run under ASAN/MSAN/UBSAN, libfido2 needs to be linked against flavours of
+libcbor and OpenSSL built with the respective sanitiser. In order to keep
+memory utilisation at a manageable level, you can either enforce limits at
+the OS level (e.g. cgroups on Linux), or patch libcbor with the diff below.
+
+diff --git src/cbor/internal/memory_utils.c src/cbor/internal/memory_utils.c
+index aa049a2..e294b38 100644
+--- src/cbor/internal/memory_utils.c
++++ src/cbor/internal/memory_utils.c
+@@ -28,7 +28,10 @@ bool _cbor_safe_to_multiply(size_t a, size_t b) {
+
+ void* _cbor_alloc_multiple(size_t item_size, size_t item_count) {
+ if (_cbor_safe_to_multiply(item_size, item_count)) {
+- return _CBOR_MALLOC(item_size * item_count);
++ if (item_count > 1000) {
++ return NULL;
++ } else
++ return _CBOR_MALLOC(item_size * item_count);
+ } else {
+ return NULL;
+ }
diff --git a/fuzz/build-coverage b/fuzz/build-coverage
new file mode 100755
index 000000000000..e0e90da02b5d
--- /dev/null
+++ b/fuzz/build-coverage
@@ -0,0 +1,31 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2019 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+LIBCBOR="$1"
+LIBFIDO2="$2"
+
+CC="${CC:-clang}"
+CXX="${CXX:-clang++}"
+PKG_CONFIG_PATH="${PKG_CONFIG_PATH:-${LIBCBOR}/install/lib/pkgconfig}"
+export CC PKG_CONFIG_PATH
+
+# Clean up.
+rm -rf "${LIBCBOR}/build" "${LIBCBOR}/install" "${LIBFIDO2}/build"
+
+# Patch, build, and install libcbor.
+(cd "${LIBCBOR}" && patch -N -l -s -p0 < "${LIBFIDO2}/fuzz/README") || true
+mkdir "${LIBCBOR}/build" "${LIBCBOR}/install"
+(cd "${LIBCBOR}/build" && cmake -DBUILD_SHARED_LIBS=ON \
+ -DCMAKE_INSTALL_PREFIX="${LIBCBOR}/install" ..)
+make -C "${LIBCBOR}/build" VERBOSE=1 all install
+
+# Build libfido2.
+mkdir -p "${LIBFIDO2}/build"
+export CFLAGS="-fprofile-instr-generate -fcoverage-mapping"
+export LDFLAGS="${CFLAGS}"
+(cd "${LIBFIDO2}/build" && cmake -DFUZZ=ON -DLIBFUZZER=ON \
+ -DCMAKE_BUILD_TYPE=Debug ..)
+make -C "${LIBFIDO2}/build"
diff --git a/fuzz/dummy.h b/fuzz/dummy.h
new file mode 100644
index 000000000000..981cceec37b5
--- /dev/null
+++ b/fuzz/dummy.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef _DUMMY_H
+#define _DUMMY_H
+
+#include <stdint.h>
+
+const char dummy_name[] = "finger1";
+const char dummy_pin1[] = "skepp cg0u3;Y..";
+const char dummy_pin2[] = "bastilha 6rJrfQZI.";
+const char dummy_pin[] = "9}4gT:8d=A37Dh}U";
+const char dummy_rp_id[] = "localhost";
+const char dummy_rp_name[] = "sweet home localhost";
+const char dummy_user_icon[] = "an icon";
+const char dummy_user_name[] = "john smith";
+const char dummy_user_nick[] = "jsmith";
+const uint8_t dummy_id[] = { 0x5e, 0xd2 };
+
+const uint8_t dummy_user_id[] = {
+ 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63,
+ 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2,
+ 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5,
+ 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49,
+};
+
+const uint8_t dummy_cred_id[] = {
+ 0x4f, 0x72, 0x98, 0x42, 0x4a, 0xe1, 0x17, 0xa5,
+ 0x85, 0xa0, 0xef, 0x3b, 0x11, 0x24, 0x4a, 0x3d,
+};
+
+const uint8_t dummy_cdh[] = {
+ 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
+ 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
+ 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
+ 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
+};
+
+const uint8_t dummy_es256[] = {
+ 0xcc, 0x1b, 0x50, 0xac, 0xc4, 0x19, 0xf8, 0x3a,
+ 0xee, 0x0a, 0x77, 0xd6, 0xf3, 0x53, 0xdb, 0xef,
+ 0xf2, 0xb9, 0x5c, 0x2d, 0x8b, 0x1e, 0x52, 0x58,
+ 0x88, 0xf4, 0x0b, 0x85, 0x1f, 0x40, 0x6d, 0x18,
+ 0x15, 0xb3, 0xcc, 0x25, 0x7c, 0x38, 0x3d, 0xec,
+ 0xdf, 0xad, 0xbd, 0x46, 0x91, 0xc3, 0xac, 0x30,
+ 0x94, 0x2a, 0xf7, 0x78, 0x35, 0x70, 0x59, 0x6f,
+ 0x28, 0xcb, 0x8e, 0x07, 0x85, 0xb5, 0x91, 0x96,
+};
+
+const uint8_t dummy_rs256[] = {
+ 0xd2, 0xa8, 0xc0, 0x11, 0x82, 0x9e, 0x57, 0x2e,
+ 0x60, 0xae, 0x8c, 0xb0, 0x09, 0xe1, 0x58, 0x2b,
+ 0x99, 0xec, 0xc3, 0x11, 0x1b, 0xef, 0x81, 0x49,
+ 0x34, 0x53, 0x6a, 0x01, 0x65, 0x2c, 0x24, 0x09,
+ 0x30, 0x87, 0x98, 0x51, 0x6e, 0x30, 0x4f, 0x60,
+ 0xbd, 0x54, 0xd2, 0x54, 0xbd, 0x94, 0x42, 0xdd,
+ 0x63, 0xe5, 0x2c, 0xc6, 0x04, 0x32, 0xc0, 0x8f,
+ 0x72, 0xd5, 0xb4, 0xf0, 0x4f, 0x42, 0xe5, 0xb0,
+ 0xa2, 0x95, 0x11, 0xfe, 0xd8, 0xb0, 0x65, 0x34,
+ 0xff, 0xfb, 0x44, 0x97, 0x52, 0xfc, 0x67, 0x23,
+ 0x0b, 0xad, 0xf3, 0x3a, 0x82, 0xd4, 0x96, 0x10,
+ 0x87, 0x6b, 0xfa, 0xd6, 0x51, 0x60, 0x3e, 0x1c,
+ 0xae, 0x19, 0xb8, 0xce, 0x08, 0xae, 0x9a, 0xee,
+ 0x78, 0x16, 0x22, 0xcc, 0x92, 0xcb, 0xa8, 0x95,
+ 0x34, 0xe5, 0xb9, 0x42, 0x6a, 0xf0, 0x2e, 0x82,
+ 0x1f, 0x4c, 0x7d, 0x84, 0x94, 0x68, 0x7b, 0x97,
+ 0x2b, 0xf7, 0x7d, 0x67, 0x83, 0xbb, 0xc7, 0x8a,
+ 0x31, 0x5a, 0xf3, 0x2a, 0x95, 0xdf, 0x63, 0xe7,
+ 0x4e, 0xee, 0x26, 0xda, 0x87, 0x00, 0xe2, 0x23,
+ 0x4a, 0x33, 0x9a, 0xa0, 0x1b, 0xce, 0x60, 0x1f,
+ 0x98, 0xa1, 0xb0, 0xdb, 0xbf, 0x20, 0x59, 0x27,
+ 0xf2, 0x06, 0xd9, 0xbe, 0x37, 0xa4, 0x03, 0x6b,
+ 0x6a, 0x4e, 0xaf, 0x22, 0x68, 0xf3, 0xff, 0x28,
+ 0x59, 0x05, 0xc9, 0xf1, 0x28, 0xf4, 0xbb, 0x35,
+ 0xe0, 0xc2, 0x68, 0xc2, 0xaa, 0x54, 0xac, 0x8c,
+ 0xc1, 0x69, 0x9e, 0x4b, 0x32, 0xfc, 0x53, 0x58,
+ 0x85, 0x7d, 0x3f, 0x51, 0xd1, 0xc9, 0x03, 0x02,
+ 0x13, 0x61, 0x62, 0xda, 0xf8, 0xfe, 0x3e, 0xc8,
+ 0x95, 0x12, 0xfb, 0x0c, 0xdf, 0x06, 0x65, 0x6f,
+ 0x23, 0xc7, 0x83, 0x7c, 0x50, 0x2d, 0x27, 0x25,
+ 0x4d, 0xbf, 0x94, 0xf0, 0x89, 0x04, 0xb9, 0x2d,
+ 0xc4, 0xa5, 0x32, 0xa9, 0x25, 0x0a, 0x99, 0x59,
+ 0x01, 0x00, 0x01,
+};
+
+const uint8_t dummy_eddsa[] = {
+ 0xfe, 0x8b, 0x61, 0x50, 0x31, 0x7a, 0xe6, 0xdf,
+ 0xb1, 0x04, 0x9d, 0x4d, 0xb5, 0x7a, 0x5e, 0x96,
+ 0x4c, 0xb2, 0xf9, 0x5f, 0x72, 0x47, 0xb5, 0x18,
+ 0xe2, 0x39, 0xdf, 0x2f, 0x87, 0x19, 0xb3, 0x02,
+};
+
+#endif /* !_DUMMY_H */
diff --git a/fuzz/export.gnu b/fuzz/export.gnu
new file mode 100644
index 000000000000..bd70d1c7eaac
--- /dev/null
+++ b/fuzz/export.gnu
@@ -0,0 +1,242 @@
+{
+ global:
+ eddsa_pk_free;
+ eddsa_pk_from_EVP_PKEY;
+ eddsa_pk_from_ptr;
+ eddsa_pk_new;
+ eddsa_pk_to_EVP_PKEY;
+ es256_pk_free;
+ es256_pk_from_EC_KEY;
+ es256_pk_from_ptr;
+ es256_pk_new;
+ es256_pk_to_EVP_PKEY;
+ fido_assert_allow_cred;
+ fido_assert_authdata_len;
+ fido_assert_authdata_ptr;
+ fido_assert_blob_len;
+ fido_assert_blob_ptr;
+ fido_assert_clientdata_hash_len;
+ fido_assert_clientdata_hash_ptr;
+ fido_assert_count;
+ fido_assert_flags;
+ fido_assert_free;
+ fido_assert_hmac_secret_len;
+ fido_assert_hmac_secret_ptr;
+ fido_assert_id_len;
+ fido_assert_id_ptr;
+ fido_assert_largeblob_key_len;
+ fido_assert_largeblob_key_ptr;
+ fido_assert_new;
+ fido_assert_rp_id;
+ fido_assert_set_authdata;
+ fido_assert_set_authdata_raw;
+ fido_assert_set_clientdata_hash;
+ fido_assert_set_count;
+ fido_assert_set_extensions;
+ fido_assert_set_hmac_salt;
+ fido_assert_set_hmac_secret;
+ fido_assert_set_options;
+ fido_assert_set_rp;
+ fido_assert_set_sig;
+ fido_assert_set_up;
+ fido_assert_set_uv;
+ fido_assert_sigcount;
+ fido_assert_sig_len;
+ fido_assert_sig_ptr;
+ fido_assert_user_display_name;
+ fido_assert_user_icon;
+ fido_assert_user_id_len;
+ fido_assert_user_id_ptr;
+ fido_assert_user_name;
+ fido_assert_verify;
+ fido_bio_dev_enroll_begin;
+ fido_bio_dev_enroll_cancel;
+ fido_bio_dev_enroll_continue;
+ fido_bio_dev_enroll_remove;
+ fido_bio_dev_get_info;
+ fido_bio_dev_get_template_array;
+ fido_bio_dev_set_template_name;
+ fido_bio_enroll_free;
+ fido_bio_enroll_last_status;
+ fido_bio_enroll_new;
+ fido_bio_enroll_remaining_samples;
+ fido_bio_info_free;
+ fido_bio_info_max_samples;
+ fido_bio_info_new;
+ fido_bio_info_type;
+ fido_bio_template;
+ fido_bio_template_array_count;
+ fido_bio_template_array_free;
+ fido_bio_template_array_new;
+ fido_bio_template_free;
+ fido_bio_template_id_len;
+ fido_bio_template_id_ptr;
+ fido_bio_template_name;
+ fido_bio_template_new;
+ fido_bio_template_set_id;
+ fido_bio_template_set_name;
+ fido_cbor_info_aaguid_len;
+ fido_cbor_info_aaguid_ptr;
+ fido_cbor_info_algorithm_cose;
+ fido_cbor_info_algorithm_count;
+ fido_cbor_info_algorithm_type;
+ fido_cbor_info_extensions_len;
+ fido_cbor_info_extensions_ptr;
+ fido_cbor_info_free;
+ fido_cbor_info_maxmsgsiz;
+ fido_cbor_info_maxcredbloblen;
+ fido_cbor_info_maxcredcntlst;
+ fido_cbor_info_maxcredidlen;
+ fido_cbor_info_fwversion;
+ fido_cbor_info_new;
+ fido_cbor_info_options_len;
+ fido_cbor_info_options_name_ptr;
+ fido_cbor_info_options_value_ptr;
+ fido_cbor_info_protocols_len;
+ fido_cbor_info_protocols_ptr;
+ fido_cbor_info_transports_len;
+ fido_cbor_info_transports_ptr;
+ fido_cbor_info_versions_len;
+ fido_cbor_info_versions_ptr;
+ fido_cred_authdata_len;
+ fido_cred_authdata_ptr;
+ fido_cred_authdata_raw_len;
+ fido_cred_authdata_raw_ptr;
+ fido_cred_clientdata_hash_len;
+ fido_cred_clientdata_hash_ptr;
+ fido_cred_display_name;
+ fido_cred_exclude;
+ fido_cred_flags;
+ fido_cred_largeblob_key_len;
+ fido_cred_largeblob_key_ptr;
+ fido_cred_sigcount;
+ fido_cred_fmt;
+ fido_cred_free;
+ fido_cred_id_len;
+ fido_cred_id_ptr;
+ fido_cred_aaguid_len;
+ fido_cred_aaguid_ptr;
+ fido_credman_del_dev_rk;
+ fido_credman_get_dev_metadata;
+ fido_credman_get_dev_rk;
+ fido_credman_get_dev_rp;
+ fido_credman_metadata_free;
+ fido_credman_metadata_new;
+ fido_credman_rk;
+ fido_credman_rk_count;
+ fido_credman_rk_existing;
+ fido_credman_rk_free;
+ fido_credman_rk_new;
+ fido_credman_rk_remaining;
+ fido_credman_rp_count;
+ fido_credman_rp_free;
+ fido_credman_rp_id;
+ fido_credman_rp_id_hash_len;
+ fido_credman_rp_id_hash_ptr;
+ fido_credman_rp_name;
+ fido_credman_rp_new;
+ fido_credman_set_dev_rk;
+ fido_cred_new;
+ fido_cred_prot;
+ fido_cred_pubkey_len;
+ fido_cred_pubkey_ptr;
+ fido_cred_rp_id;
+ fido_cred_rp_name;
+ fido_cred_set_authdata;
+ fido_cred_set_authdata_raw;
+ fido_cred_set_blob;
+ fido_cred_set_clientdata_hash;
+ fido_cred_set_extensions;
+ fido_cred_set_fmt;
+ fido_cred_set_id;
+ fido_cred_set_options;
+ fido_cred_set_prot;
+ fido_cred_set_rk;
+ fido_cred_set_rp;
+ fido_cred_set_sig;
+ fido_cred_set_type;
+ fido_cred_set_user;
+ fido_cred_set_uv;
+ fido_cred_set_x509;
+ fido_cred_sig_len;
+ fido_cred_sig_ptr;
+ fido_cred_type;
+ fido_cred_user_id_len;
+ fido_cred_user_id_ptr;
+ fido_cred_user_name;
+ fido_cred_verify;
+ fido_cred_verify_self;
+ fido_cred_x5c_len;
+ fido_cred_x5c_ptr;
+ fido_dev_build;
+ fido_dev_cancel;
+ fido_dev_close;
+ fido_dev_enable_entattest;
+ fido_dev_flags;
+ fido_dev_force_fido2;
+ fido_dev_force_pin_change;
+ fido_dev_force_u2f;
+ fido_dev_free;
+ fido_dev_get_assert;
+ fido_dev_get_cbor_info;
+ fido_dev_get_retry_count;
+ fido_dev_get_uv_retry_count;
+ fido_dev_get_touch_begin;
+ fido_dev_get_touch_status;
+ fido_dev_has_pin;
+ fido_dev_has_uv;
+ fido_dev_info_free;
+ fido_dev_info_manifest;
+ fido_dev_info_manufacturer_string;
+ fido_dev_info_new;
+ fido_dev_info_path;
+ fido_dev_info_product;
+ fido_dev_info_product_string;
+ fido_dev_info_ptr;
+ fido_dev_info_vendor;
+ fido_dev_is_fido2;
+ fido_dev_major;
+ fido_dev_make_cred;
+ fido_dev_minor;
+ fido_dev_new;
+ fido_dev_open;
+ fido_dev_protocol;
+ fido_dev_reset;
+ fido_dev_set_io_functions;
+ fido_dev_set_pin;
+ fido_dev_set_pin_minlen;
+ fido_dev_set_transport_functions;
+ fido_dev_supports_cred_prot;
+ fido_dev_supports_credman;
+ fido_dev_supports_permissions;
+ fido_dev_supports_pin;
+ fido_dev_supports_uv;
+ fido_dev_toggle_always_uv;
+ fido_dev_largeblob_get;
+ fido_dev_largeblob_get_array;
+ fido_dev_largeblob_remove;
+ fido_dev_largeblob_set;
+ fido_dev_largeblob_set_array;
+ fido_hid_get_report_len;
+ fido_hid_get_usage;
+ fido_init;
+ fido_nfc_rx;
+ fido_nfc_tx;
+ fido_nl_free;
+ fido_nl_get_nfc_target;
+ fido_nl_new;
+ fido_nl_power_nfc;
+ fido_set_log_handler;
+ fido_strerr;
+ rs256_pk_free;
+ rs256_pk_from_ptr;
+ rs256_pk_from_RSA;
+ rs256_pk_new;
+ rs256_pk_to_EVP_PKEY;
+ prng_init;
+ set_netlink_io_functions;
+ set_udev_parameters;
+ uniform_random;
+ local:
+ *;
+};
diff --git a/fuzz/functions.txt b/fuzz/functions.txt
new file mode 100644
index 000000000000..28fe4f6af17b
--- /dev/null
+++ b/fuzz/functions.txt
@@ -0,0 +1,807 @@
+File '/libfido2/src/aes256.c':
+Name Regions Miss Cover Lines Miss Cover
+------------------------------------------------------------------------------
+aes256_cbc_enc 3 0 100.00% 4 0 100.00%
+aes256_cbc_dec 3 0 100.00% 4 0 100.00%
+aes256_gcm_enc 1 0 100.00% 3 0 100.00%
+aes256_gcm_dec 1 0 100.00% 3 0 100.00%
+aes256.c:aes256_cbc_fips 26 2 92.31% 45 7 84.44%
+aes256.c:aes256_cbc 29 1 96.55% 40 3 92.50%
+aes256.c:aes256_cbc_proto1 1 0 100.00% 7 0 100.00%
+aes256.c:aes256_gcm 51 1 98.04% 69 4 94.20%
+------------------------------------------------------------------------------
+TOTAL 115 4 96.52% 175 14 92.00%
+
+File '/libfido2/src/assert.c':
+Name Regions Miss Cover Lines Miss Cover
+---------------------------------------------------------------------------------------
+fido_dev_get_assert 40 0 100.00% 41 0 100.00%
+fido_check_flags 13 0 100.00% 18 0 100.00%
+fido_get_signed_hash 32 0 100.00% 46 0 100.00%
+fido_verify_sig_es256 17 2 88.24% 31 7 77.42%
+fido_verify_sig_rs256 17 2 88.24% 31 7 77.42%
+fido_verify_sig_eddsa 23 2 91.30% 43 7 83.72%
+fido_assert_verify 48 4 91.67% 79 5 93.67%
+fido_assert_set_clientdata 12 12 0.00% 12 12 0.00%
+fido_assert_set_clientdata_hash 8 0 100.00% 7 0 100.00%
+fido_assert_set_hmac_salt 10 0 100.00% 7 0 100.00%
+fido_assert_set_hmac_secret 12 12 0.00% 8 8 0.00%
+fido_assert_set_rp 12 0 100.00% 14 0 100.00%
+fido_assert_allow_cred 13 2 84.62% 29 3 89.66%
+fido_assert_set_extensions 14 0 100.00% 11 0 100.00%
+fido_assert_set_options 6 6 0.00% 6 6 0.00%
+fido_assert_set_up 2 0 100.00% 5 0 100.00%
+fido_assert_set_uv 2 0 100.00% 5 0 100.00%
+fido_assert_clientdata_hash_ptr 1 0 100.00% 3 0 100.00%
+fido_assert_clientdata_hash_len 1 0 100.00% 3 0 100.00%
+fido_assert_new 1 0 100.00% 3 0 100.00%
+fido_assert_reset_tx 1 0 100.00% 12 0 100.00%
+fido_assert_reset_rx 4 0 100.00% 19 0 100.00%
+fido_assert_free 6 0 100.00% 10 0 100.00%
+fido_assert_count 1 0 100.00% 3 0 100.00%
+fido_assert_rp_id 1 0 100.00% 3 0 100.00%
+fido_assert_flags 4 0 100.00% 6 0 100.00%
+fido_assert_sigcount 4 0 100.00% 6 0 100.00%
+fido_assert_authdata_ptr 4 0 100.00% 6 0 100.00%
+fido_assert_authdata_len 4 0 100.00% 6 0 100.00%
+fido_assert_sig_ptr 4 0 100.00% 6 0 100.00%
+fido_assert_sig_len 4 0 100.00% 6 0 100.00%
+fido_assert_id_ptr 4 0 100.00% 6 0 100.00%
+fido_assert_id_len 4 0 100.00% 6 0 100.00%
+fido_assert_user_id_ptr 4 0 100.00% 6 0 100.00%
+fido_assert_user_id_len 4 0 100.00% 6 0 100.00%
+fido_assert_user_icon 4 0 100.00% 6 0 100.00%
+fido_assert_user_name 4 0 100.00% 6 0 100.00%
+fido_assert_user_display_name 4 0 100.00% 6 0 100.00%
+fido_assert_hmac_secret_ptr 4 0 100.00% 6 0 100.00%
+fido_assert_hmac_secret_len 4 0 100.00% 6 0 100.00%
+fido_assert_largeblob_key_ptr 4 0 100.00% 6 0 100.00%
+fido_assert_largeblob_key_len 4 0 100.00% 6 0 100.00%
+fido_assert_blob_ptr 4 0 100.00% 6 0 100.00%
+fido_assert_blob_len 4 0 100.00% 6 0 100.00%
+fido_assert_set_authdata 24 0 100.00% 35 0 100.00%
+fido_assert_set_authdata_raw 24 0 100.00% 34 0 100.00%
+fido_assert_set_sig 14 0 100.00% 8 0 100.00%
+fido_assert_set_count 10 0 100.00% 21 0 100.00%
+assert.c:fido_dev_get_assert_wait 21 0 100.00% 16 0 100.00%
+assert.c:fido_dev_get_assert_tx 56 2 96.43% 77 5 93.51%
+assert.c:fido_dev_get_assert_rx 19 0 100.00% 38 0 100.00%
+assert.c:adjust_assert_count 24 0 100.00% 33 0 100.00%
+assert.c:parse_assert_reply 12 0 100.00% 26 0 100.00%
+assert.c:fido_get_next_assert_tx 8 0 100.00% 10 0 100.00%
+assert.c:fido_get_next_assert_rx 15 2 86.67% 26 4 84.62%
+assert.c:decrypt_hmac_secrets 9 0 100.00% 16 0 100.00%
+assert.c:check_extensions 5 0 100.00% 11 0 100.00%
+assert.c:fido_assert_reset_extattr 1 0 100.00% 5 0 100.00%
+assert.c:fido_assert_clean_authdata 1 0 100.00% 5 0 100.00%
+---------------------------------------------------------------------------------------
+TOTAL 616 46 92.53% 924 64 93.07%
+
+File '/libfido2/src/authkey.c':
+Name Regions Miss Cover Lines Miss Cover
+---------------------------------------------------------------------------------------
+fido_dev_authkey 1 0 100.00% 3 0 100.00%
+authkey.c:fido_dev_authkey_wait 10 0 100.00% 9 0 100.00%
+authkey.c:fido_dev_authkey_tx 19 0 100.00% 33 0 100.00%
+authkey.c:fido_dev_authkey_rx 6 0 100.00% 18 0 100.00%
+authkey.c:parse_authkey 8 0 100.00% 12 0 100.00%
+---------------------------------------------------------------------------------------
+TOTAL 44 0 100.00% 75 0 100.00%
+
+File '/libfido2/src/bio.c':
+Name Regions Miss Cover Lines Miss Cover
+---------------------------------------------------------------------------------------
+fido_bio_dev_get_template_array 5 2 60.00% 6 0 100.00%
+fido_bio_dev_set_template_name 7 0 100.00% 6 0 100.00%
+fido_bio_dev_enroll_begin 25 2 92.00% 37 0 100.00%
+fido_bio_dev_enroll_continue 5 2 60.00% 6 0 100.00%
+fido_bio_dev_enroll_cancel 1 1 0.00% 3 3 0.00%
+fido_bio_dev_enroll_remove 1 0 100.00% 3 0 100.00%
+fido_bio_dev_get_info 1 0 100.00% 3 0 100.00%
+fido_bio_template_name 1 0 100.00% 3 0 100.00%
+fido_bio_template_id_ptr 1 0 100.00% 3 0 100.00%
+fido_bio_template_id_len 1 0 100.00% 3 0 100.00%
+fido_bio_template_array_count 1 0 100.00% 3 0 100.00%
+fido_bio_template_array_new 1 0 100.00% 3 0 100.00%
+fido_bio_template_new 1 0 100.00% 3 0 100.00%
+fido_bio_template_array_free 6 0 100.00% 10 0 100.00%
+fido_bio_template_free 6 0 100.00% 10 0 100.00%
+fido_bio_template_set_name 8 0 100.00% 9 0 100.00%
+fido_bio_template_set_id 8 0 100.00% 8 0 100.00%
+fido_bio_template 4 0 100.00% 6 0 100.00%
+fido_bio_enroll_new 1 0 100.00% 3 0 100.00%
+fido_bio_info_new 1 0 100.00% 3 0 100.00%
+fido_bio_info_type 1 0 100.00% 3 0 100.00%
+fido_bio_info_max_samples 1 0 100.00% 3 0 100.00%
+fido_bio_enroll_free 6 0 100.00% 11 0 100.00%
+fido_bio_info_free 6 0 100.00% 9 0 100.00%
+fido_bio_enroll_remaining_samples 1 0 100.00% 3 0 100.00%
+fido_bio_enroll_last_status 1 0 100.00% 3 0 100.00%
+bio.c:bio_get_template_array_wait 11 0 100.00% 9 0 100.00%
+bio.c:bio_tx 43 0 100.00% 66 0 100.00%
+bio.c:bio_prepare_hmac 18 0 100.00% 36 0 100.00%
+bio.c:bio_rx_template_array 11 0 100.00% 21 0 100.00%
+bio.c:bio_parse_template_array 26 1 96.15% 34 4 88.24%
+bio.c:decode_template_array 12 1 91.67% 23 3 86.96%
+bio.c:decode_template 9 0 100.00% 18 0 100.00%
+bio.c:bio_set_template_name_wait 19 0 100.00% 24 0 100.00%
+bio.c:bio_enroll_begin_wait 17 0 100.00% 24 0 100.00%
+bio.c:bio_rx_enroll_begin 15 0 100.00% 29 0 100.00%
+bio.c:bio_parse_enroll_status 20 0 100.00% 31 0 100.00%
+bio.c:bio_parse_template_id 8 0 100.00% 12 0 100.00%
+bio.c:bio_enroll_continue_wait 19 0 100.00% 25 0 100.00%
+bio.c:bio_rx_enroll_continue 11 0 100.00% 22 0 100.00%
+bio.c:bio_enroll_cancel_wait 11 11 0.00% 12 12 0.00%
+bio.c:bio_enroll_remove_wait 17 0 100.00% 24 0 100.00%
+bio.c:bio_get_info_wait 11 0 100.00% 11 0 100.00%
+bio.c:bio_rx_info 11 0 100.00% 21 0 100.00%
+bio.c:bio_reset_info 1 0 100.00% 4 0 100.00%
+bio.c:bio_parse_info 20 0 100.00% 31 0 100.00%
+bio.c:bio_reset_template_array 4 0 100.00% 8 0 100.00%
+bio.c:bio_reset_template 1 0 100.00% 5 0 100.00%
+bio.c:bio_reset_enroll 3 0 100.00% 7 0 100.00%
+---------------------------------------------------------------------------------------
+TOTAL 419 20 95.23% 660 22 96.67%
+
+File '/libfido2/src/blob.c':
+Name Regions Miss Cover Lines Miss Cover
+---------------------------------------------------------------------------------------
+fido_blob_new 1 0 100.00% 3 0 100.00%
+fido_blob_reset 1 0 100.00% 4 0 100.00%
+fido_blob_set 9 0 100.00% 19 0 100.00%
+fido_blob_append 12 2 83.33% 22 6 72.73%
+fido_blob_free 6 0 100.00% 10 0 100.00%
+fido_free_blob_array 7 0 100.00% 14 0 100.00%
+fido_blob_encode 6 0 100.00% 6 0 100.00%
+fido_blob_decode 1 0 100.00% 3 0 100.00%
+fido_blob_is_empty 3 0 100.00% 3 0 100.00%
+fido_blob_serialise 7 1 85.71% 12 1 91.67%
+---------------------------------------------------------------------------------------
+TOTAL 53 3 94.34% 96 7 92.71%
+
+File '/libfido2/src/buf.c':
+Name Regions Miss Cover Lines Miss Cover
+---------------------------------------------------------------------------------------
+fido_buf_read 4 0 100.00% 10 0 100.00%
+fido_buf_write 4 1 75.00% 10 1 90.00%
+---------------------------------------------------------------------------------------
+TOTAL 8 1 87.50% 20 1 95.00%
+
+File '/libfido2/src/cbor.c':
+Name Regions Miss Cover Lines Miss Cover
+----------------------------------------------------------------------------------------
+cbor_map_iter 20 1 95.00% 30 4 86.67%
+cbor_array_iter 12 0 100.00% 20 0 100.00%
+cbor_parse_reply 27 0 100.00% 43 0 100.00%
+cbor_vector_free 6 0 100.00% 5 0 100.00%
+cbor_bytestring_copy 14 0 100.00% 22 0 100.00%
+cbor_string_copy 14 0 100.00% 23 0 100.00%
+cbor_add_bytestring 14 0 100.00% 26 0 100.00%
+cbor_add_string 14 0 100.00% 26 0 100.00%
+cbor_add_bool 14 0 100.00% 26 0 100.00%
+cbor_flatten_vector 14 1 92.86% 21 1 95.24%
+cbor_build_frame 15 0 100.00% 32 0 100.00%
+cbor_encode_rp_entity 13 0 100.00% 14 0 100.00%
+cbor_encode_user_entity 21 0 100.00% 18 0 100.00%
+cbor_encode_pubkey_param 36 0 100.00% 48 0 100.00%
+cbor_encode_pubkey 10 0 100.00% 13 0 100.00%
+cbor_encode_pubkey_list 18 0 100.00% 23 0 100.00%
+cbor_encode_cred_ext 46 0 100.00% 46 0 100.00%
+cbor_encode_cred_opt 13 0 100.00% 13 0 100.00%
+cbor_encode_assert_opt 13 0 100.00% 13 0 100.00%
+cbor_encode_pin_auth 20 1 95.00% 30 3 90.00%
+cbor_encode_pin_opt 4 0 100.00% 10 0 100.00%
+cbor_encode_change_pin_auth 33 1 96.97% 49 3 93.88%
+cbor_encode_assert_ext 33 0 100.00% 35 0 100.00%
+cbor_decode_fmt 11 0 100.00% 19 0 100.00%
+cbor_decode_pubkey 21 1 95.24% 32 2 93.75%
+cbor_decode_cred_authdata 31 1 96.77% 45 3 93.33%
+cbor_decode_assert_authdata 21 0 100.00% 42 0 100.00%
+cbor_decode_attstmt 8 0 100.00% 10 0 100.00%
+cbor_decode_uint64 4 0 100.00% 10 0 100.00%
+cbor_decode_cred_id 8 0 100.00% 10 0 100.00%
+cbor_decode_user 8 0 100.00% 10 0 100.00%
+cbor_decode_rp_entity 8 0 100.00% 10 0 100.00%
+cbor_build_uint 10 4 60.00% 10 5 50.00%
+cbor_array_append 17 0 100.00% 23 0 100.00%
+cbor_array_drop 18 2 88.89% 19 3 84.21%
+cbor.c:ctap_check_cbor 28 0 100.00% 32 0 100.00%
+cbor.c:check_key_type 8 0 100.00% 9 0 100.00%
+cbor.c:cbor_add_arg 13 0 100.00% 28 0 100.00%
+cbor.c:cbor_add_uint8 14 0 100.00% 26 0 100.00%
+cbor.c:cbor_encode_largeblob_key_ext 6 0 100.00% 7 0 100.00%
+cbor.c:cbor_encode_hmac_secret_param 53 2 96.23% 75 4 94.67%
+cbor.c:get_cose_alg 36 0 100.00% 48 0 100.00%
+cbor.c:find_cose_alg 35 0 100.00% 40 0 100.00%
+cbor.c:decode_attcred 25 0 100.00% 56 0 100.00%
+cbor.c:decode_cred_extensions 14 0 100.00% 31 0 100.00%
+cbor.c:decode_cred_extension 40 3 92.50% 45 9 80.00%
+cbor.c:decode_assert_extensions 14 0 100.00% 29 0 100.00%
+cbor.c:decode_assert_extension 19 0 100.00% 31 0 100.00%
+cbor.c:decode_attstmt_entry 38 0 100.00% 44 0 100.00%
+cbor.c:decode_x5c 4 0 100.00% 8 0 100.00%
+cbor.c:decode_cred_id_entry 10 0 100.00% 23 0 100.00%
+cbor.c:decode_user_entry 25 0 100.00% 39 0 100.00%
+cbor.c:decode_rp_entity_entry 15 0 100.00% 29 0 100.00%
+----------------------------------------------------------------------------------------
+TOTAL 986 17 98.28% 1426 37 97.41%
+
+File '/libfido2/src/compress.c':
+Name Regions Miss Cover Lines Miss Cover
+----------------------------------------------------------------------------------------
+fido_compress 1 0 100.00% 3 0 100.00%
+fido_uncompress 1 0 100.00% 3 0 100.00%
+compress.c:do_compress 32 4 87.50% 24 3 87.50%
+----------------------------------------------------------------------------------------
+TOTAL 34 4 88.24% 30 3 90.00%
+
+File '/libfido2/src/config.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_dev_enable_entattest 1 0 100.00% 3 0 100.00%
+fido_dev_toggle_always_uv 1 0 100.00% 3 0 100.00%
+fido_dev_set_pin_minlen 1 0 100.00% 3 0 100.00%
+fido_dev_force_pin_change 1 0 100.00% 3 0 100.00%
+config.c:config_enable_entattest_wait 6 0 100.00% 8 0 100.00%
+config.c:config_tx 37 0 100.00% 57 0 100.00%
+config.c:config_prepare_hmac 8 1 87.50% 22 3 86.36%
+config.c:config_toggle_always_uv_wait 6 0 100.00% 8 0 100.00%
+config.c:config_pin_minlen 5 0 100.00% 8 0 100.00%
+config.c:config_pin_minlen_tx 28 0 100.00% 31 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 94 1 98.94% 146 3 97.95%
+
+File '/libfido2/src/cred.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_dev_make_cred 12 0 100.00% 10 0 100.00%
+fido_check_rp_id 4 0 100.00% 14 0 100.00%
+fido_cred_verify 50 4 92.00% 75 8 89.33%
+fido_cred_verify_self 58 6 89.66% 94 10 89.36%
+fido_cred_new 1 0 100.00% 3 0 100.00%
+fido_cred_reset_tx 1 0 100.00% 22 0 100.00%
+fido_cred_reset_rx 1 0 100.00% 8 0 100.00%
+fido_cred_free 6 0 100.00% 10 0 100.00%
+fido_cred_set_authdata 23 0 100.00% 37 0 100.00%
+fido_cred_set_authdata_raw 25 0 100.00% 38 0 100.00%
+fido_cred_set_id 6 0 100.00% 6 0 100.00%
+fido_cred_set_x509 6 0 100.00% 6 0 100.00%
+fido_cred_set_sig 6 0 100.00% 6 0 100.00%
+fido_cred_exclude 14 2 85.71% 25 3 88.00%
+fido_cred_set_clientdata 12 12 0.00% 12 12 0.00%
+fido_cred_set_clientdata_hash 8 0 100.00% 7 0 100.00%
+fido_cred_set_rp 18 0 100.00% 26 0 100.00%
+fido_cred_set_user 32 0 100.00% 46 0 100.00%
+fido_cred_set_extensions 15 0 100.00% 11 0 100.00%
+fido_cred_set_options 6 6 0.00% 6 6 0.00%
+fido_cred_set_rk 2 0 100.00% 5 0 100.00%
+fido_cred_set_uv 2 0 100.00% 5 0 100.00%
+fido_cred_set_prot 21 0 100.00% 16 0 100.00%
+fido_cred_set_blob 13 2 84.62% 10 1 90.00%
+fido_cred_set_fmt 18 4 77.78% 16 1 93.75%
+fido_cred_set_type 17 0 100.00% 9 0 100.00%
+fido_cred_type 1 0 100.00% 3 0 100.00%
+fido_cred_flags 1 0 100.00% 3 0 100.00%
+fido_cred_sigcount 1 0 100.00% 3 0 100.00%
+fido_cred_clientdata_hash_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_clientdata_hash_len 1 0 100.00% 3 0 100.00%
+fido_cred_x5c_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_x5c_len 1 0 100.00% 3 0 100.00%
+fido_cred_sig_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_sig_len 1 0 100.00% 3 0 100.00%
+fido_cred_authdata_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_authdata_len 1 0 100.00% 3 0 100.00%
+fido_cred_authdata_raw_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_authdata_raw_len 1 0 100.00% 3 0 100.00%
+fido_cred_pubkey_ptr 9 0 100.00% 20 0 100.00%
+fido_cred_pubkey_len 9 0 100.00% 20 0 100.00%
+fido_cred_id_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_id_len 1 0 100.00% 3 0 100.00%
+fido_cred_aaguid_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_aaguid_len 1 0 100.00% 3 0 100.00%
+fido_cred_prot 1 0 100.00% 3 0 100.00%
+fido_cred_fmt 1 0 100.00% 3 0 100.00%
+fido_cred_rp_id 1 0 100.00% 3 0 100.00%
+fido_cred_rp_name 1 0 100.00% 3 0 100.00%
+fido_cred_user_name 1 0 100.00% 3 0 100.00%
+fido_cred_display_name 1 0 100.00% 3 0 100.00%
+fido_cred_user_id_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_user_id_len 1 0 100.00% 3 0 100.00%
+fido_cred_largeblob_key_ptr 1 0 100.00% 3 0 100.00%
+fido_cred_largeblob_key_len 1 0 100.00% 3 0 100.00%
+cred.c:fido_dev_make_cred_wait 10 0 100.00% 9 0 100.00%
+cred.c:fido_dev_make_cred_tx 64 0 100.00% 85 0 100.00%
+cred.c:fido_dev_make_cred_rx 19 0 100.00% 27 0 100.00%
+cred.c:parse_makecred_reply 14 0 100.00% 29 0 100.00%
+cred.c:check_extensions 2 0 100.00% 9 0 100.00%
+cred.c:get_signed_hash_u2f 22 0 100.00% 20 0 100.00%
+cred.c:verify_sig 27 2 92.59% 40 7 82.50%
+cred.c:fido_cred_clean_authdata 1 0 100.00% 9 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 581 38 93.46% 872 48 94.50%
+
+File '/libfido2/src/credman.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_credman_get_dev_metadata 1 0 100.00% 3 0 100.00%
+fido_credman_get_dev_rk 1 0 100.00% 3 0 100.00%
+fido_credman_del_dev_rk 1 0 100.00% 3 0 100.00%
+fido_credman_get_dev_rp 1 0 100.00% 3 0 100.00%
+fido_credman_set_dev_rk 1 0 100.00% 3 0 100.00%
+fido_credman_rk_new 1 0 100.00% 3 0 100.00%
+fido_credman_rk_free 6 1 83.33% 10 0 100.00%
+fido_credman_rk_count 1 0 100.00% 3 0 100.00%
+fido_credman_rk 4 0 100.00% 6 0 100.00%
+fido_credman_metadata_new 1 0 100.00% 3 0 100.00%
+fido_credman_metadata_free 6 1 83.33% 9 0 100.00%
+fido_credman_rk_existing 1 0 100.00% 3 0 100.00%
+fido_credman_rk_remaining 1 0 100.00% 3 0 100.00%
+fido_credman_rp_new 1 0 100.00% 3 0 100.00%
+fido_credman_rp_free 6 1 83.33% 10 0 100.00%
+fido_credman_rp_count 1 0 100.00% 3 0 100.00%
+fido_credman_rp_id 4 0 100.00% 6 0 100.00%
+fido_credman_rp_name 4 0 100.00% 6 0 100.00%
+fido_credman_rp_id_hash_len 4 0 100.00% 6 0 100.00%
+fido_credman_rp_id_hash_ptr 4 0 100.00% 6 0 100.00%
+credman.c:credman_get_metadata_wait 11 0 100.00% 10 0 100.00%
+credman.c:credman_tx 36 0 100.00% 60 0 100.00%
+credman.c:credman_prepare_hmac 31 1 96.77% 56 2 96.43%
+credman.c:credman_rx_metadata 11 0 100.00% 21 0 100.00%
+credman.c:credman_parse_metadata 9 0 100.00% 19 0 100.00%
+credman.c:credman_get_rk_wait 27 0 100.00% 28 0 100.00%
+credman.c:credman_rx_rk 19 0 100.00% 36 0 100.00%
+credman.c:credman_parse_rk_count 16 0 100.00% 25 0 100.00%
+credman.c:credman_grow_array 17 2 88.24% 28 5 82.14%
+credman.c:credman_parse_rk 23 0 100.00% 33 0 100.00%
+credman.c:credman_rx_next_rk 15 2 86.67% 26 4 84.62%
+credman.c:credman_del_rk_wait 16 0 100.00% 20 0 100.00%
+credman.c:credman_get_rp_wait 23 0 100.00% 18 0 100.00%
+credman.c:credman_rx_rp 19 0 100.00% 36 0 100.00%
+credman.c:credman_parse_rp_count 16 0 100.00% 25 0 100.00%
+credman.c:credman_parse_rp 9 0 100.00% 19 0 100.00%
+credman.c:credman_rx_next_rp 15 2 86.67% 26 4 84.62%
+credman.c:credman_set_dev_rk_wait 11 0 100.00% 10 0 100.00%
+credman.c:credman_reset_rk 4 0 100.00% 10 0 100.00%
+credman.c:credman_reset_rp 4 0 100.00% 13 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 382 10 97.38% 614 15 97.56%
+
+File '/libfido2/src/dev.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_dev_register_manifest_func 10 2 80.00% 18 3 83.33%
+fido_dev_unregister_manifest_func 7 7 0.00% 13 13 0.00%
+fido_dev_info_manifest 22 4 81.82% 28 0 100.00%
+fido_dev_open_with_info 5 5 0.00% 6 6 0.00%
+fido_dev_open 11 5 54.55% 26 12 53.85%
+fido_dev_close 9 2 77.78% 10 0 100.00%
+fido_dev_set_sigmask 12 12 0.00% 10 10 0.00%
+fido_dev_cancel 11 0 100.00% 8 0 100.00%
+fido_dev_get_touch_begin 50 0 100.00% 68 0 100.00%
+fido_dev_get_touch_status 17 0 100.00% 25 0 100.00%
+fido_dev_set_io_functions 18 4 77.78% 17 6 64.71%
+fido_dev_set_transport_functions 6 2 66.67% 11 3 72.73%
+fido_init 8 1 87.50% 6 0 100.00%
+fido_dev_new 5 0 100.00% 16 0 100.00%
+fido_dev_new_with_info 10 10 0.00% 20 20 0.00%
+fido_dev_free 6 0 100.00% 11 0 100.00%
+fido_dev_protocol 1 0 100.00% 3 0 100.00%
+fido_dev_major 1 0 100.00% 3 0 100.00%
+fido_dev_minor 1 0 100.00% 3 0 100.00%
+fido_dev_build 1 0 100.00% 3 0 100.00%
+fido_dev_flags 1 0 100.00% 3 0 100.00%
+fido_dev_is_fido2 2 0 100.00% 3 0 100.00%
+fido_dev_is_winhello 2 2 0.00% 3 3 0.00%
+fido_dev_supports_pin 3 0 100.00% 3 0 100.00%
+fido_dev_has_pin 2 0 100.00% 3 0 100.00%
+fido_dev_supports_cred_prot 2 0 100.00% 3 0 100.00%
+fido_dev_supports_credman 2 0 100.00% 3 0 100.00%
+fido_dev_supports_uv 3 0 100.00% 3 0 100.00%
+fido_dev_has_uv 2 0 100.00% 3 0 100.00%
+fido_dev_supports_permissions 2 0 100.00% 3 0 100.00%
+fido_dev_force_u2f 2 0 100.00% 4 0 100.00%
+fido_dev_force_fido2 2 2 0.00% 3 3 0.00%
+fido_dev_get_pin_protocol 11 0 100.00% 8 0 100.00%
+fido_dev_maxmsgsize 1 0 100.00% 3 0 100.00%
+dev.c:find_manifest_func_node 5 0 100.00% 9 0 100.00%
+dev.c:fido_dev_open_wait 10 0 100.00% 9 0 100.00%
+dev.c:fido_dev_open_tx 56 15 73.21% 67 26 61.19%
+dev.c:set_random_report_len 11 0 100.00% 6 0 100.00%
+dev.c:fido_dev_open_rx 36 1 97.22% 62 1 98.39%
+dev.c:fido_dev_set_flags 1 0 100.00% 5 0 100.00%
+dev.c:fido_dev_set_extension_flags 7 0 100.00% 8 0 100.00%
+dev.c:fido_dev_set_option_flags 29 0 100.00% 19 0 100.00%
+dev.c:fido_dev_set_protocol_flags 11 0 100.00% 18 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 414 74 82.13% 556 106 80.94%
+
+File '/libfido2/src/ecdh.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_do_ecdh 29 0 100.00% 40 0 100.00%
+ecdh.c:do_ecdh 37 0 100.00% 48 0 100.00%
+ecdh.c:kdf 19 1 94.74% 32 2 93.75%
+ecdh.c:hkdf_sha256 32 1 96.88% 41 3 92.68%
+-----------------------------------------------------------------------------------------
+TOTAL 117 2 98.29% 161 5 96.89%
+
+File '/libfido2/src/eddsa.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+eddsa_pk_decode 8 0 100.00% 10 0 100.00%
+eddsa_pk_new 1 0 100.00% 3 0 100.00%
+eddsa_pk_free 6 0 100.00% 9 0 100.00%
+eddsa_pk_from_ptr 6 0 100.00% 8 0 100.00%
+eddsa_pk_to_EVP_PKEY 3 0 100.00% 9 0 100.00%
+eddsa_pk_from_EVP_PKEY 14 0 100.00% 12 0 100.00%
+eddsa.c:decode_pubkey_point 8 0 100.00% 14 0 100.00%
+eddsa.c:decode_coord 8 0 100.00% 12 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 54 0 100.00% 77 0 100.00%
+
+File '/libfido2/src/err.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_strerr 122 10 91.80% 126 10 92.06%
+-----------------------------------------------------------------------------------------
+TOTAL 122 10 91.80% 126 10 92.06%
+
+File '/libfido2/src/es256.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+es256_pk_decode 8 0 100.00% 10 0 100.00%
+es256_pk_encode 56 0 100.00% 70 0 100.00%
+es256_sk_new 1 0 100.00% 3 0 100.00%
+es256_sk_free 6 0 100.00% 9 0 100.00%
+es256_pk_new 1 0 100.00% 3 0 100.00%
+es256_pk_free 6 0 100.00% 9 0 100.00%
+es256_pk_from_ptr 11 0 100.00% 13 0 100.00%
+es256_pk_set_x 1 0 100.00% 5 0 100.00%
+es256_pk_set_y 1 0 100.00% 5 0 100.00%
+es256_sk_create 39 0 100.00% 46 0 100.00%
+es256_pk_to_EVP_PKEY 42 0 100.00% 66 0 100.00%
+es256_pk_from_EC_KEY 38 0 100.00% 43 0 100.00%
+es256_sk_to_EVP_PKEY 28 0 100.00% 50 0 100.00%
+es256_derive_pk 25 0 100.00% 34 0 100.00%
+es256.c:decode_pubkey_point 9 0 100.00% 16 0 100.00%
+es256.c:decode_coord 8 0 100.00% 12 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 280 0 100.00% 394 0 100.00%
+
+File '/libfido2/src/extern.h':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+
+File '/libfido2/src/fido.h':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+
+File '/libfido2/src/hid.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_hid_get_usage 13 0 100.00% 28 0 100.00%
+fido_hid_get_report_len 19 0 100.00% 33 0 100.00%
+fido_dev_info_new 1 0 100.00% 3 0 100.00%
+fido_dev_info_free 9 0 100.00% 18 0 100.00%
+fido_dev_info_ptr 1 0 100.00% 3 0 100.00%
+fido_dev_info_path 1 0 100.00% 3 0 100.00%
+fido_dev_info_vendor 1 0 100.00% 3 0 100.00%
+fido_dev_info_product 1 0 100.00% 3 0 100.00%
+fido_dev_info_manufacturer_string 1 0 100.00% 3 0 100.00%
+fido_dev_info_product_string 1 0 100.00% 3 0 100.00%
+hid.c:get_key_len 6 0 100.00% 14 0 100.00%
+hid.c:get_key_val 6 0 100.00% 20 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 60 0 100.00% 134 0 100.00%
+
+File '/libfido2/src/hid_linux.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_hid_manifest 35 4 88.57% 50 1 98.00%
+fido_hid_open 27 27 0.00% 44 44 0.00%
+fido_hid_close 3 3 0.00% 8 8 0.00%
+fido_hid_set_sigmask 2 2 0.00% 8 8 0.00%
+fido_hid_read 15 15 0.00% 26 26 0.00%
+fido_hid_write 12 12 0.00% 21 21 0.00%
+fido_hid_report_in_len 1 1 0.00% 5 5 0.00%
+fido_hid_report_out_len 1 1 0.00% 5 5 0.00%
+hid_linux.c:copy_info 34 0 100.00% 53 0 100.00%
+hid_linux.c:is_fido 10 2 80.00% 19 2 89.47%
+hid_linux.c:get_parent_attr 6 0 100.00% 11 0 100.00%
+hid_linux.c:parse_uevent 12 0 100.00% 28 0 100.00%
+hid_linux.c:get_usb_attr 1 0 100.00% 3 0 100.00%
+hid_linux.c:get_report_descriptor 14 1 92.86% 22 3 86.36%
+-----------------------------------------------------------------------------------------
+TOTAL 173 68 60.69% 303 123 59.41%
+
+File '/libfido2/src/hid_unix.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_hid_unix_open 18 11 38.89% 26 14 46.15%
+fido_hid_unix_wait 12 9 25.00% 26 14 46.15%
+-----------------------------------------------------------------------------------------
+TOTAL 30 20 33.33% 52 28 46.15%
+
+File '/libfido2/src/info.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_dev_get_cbor_info_wait 10 0 100.00% 9 0 100.00%
+fido_dev_get_cbor_info 1 0 100.00% 3 0 100.00%
+fido_cbor_info_new 1 0 100.00% 3 0 100.00%
+fido_cbor_info_reset 1 0 100.00% 8 0 100.00%
+fido_cbor_info_free 6 0 100.00% 9 0 100.00%
+fido_cbor_info_versions_ptr 1 0 100.00% 3 0 100.00%
+fido_cbor_info_versions_len 1 0 100.00% 3 0 100.00%
+fido_cbor_info_extensions_ptr 1 0 100.00% 3 0 100.00%
+fido_cbor_info_extensions_len 1 0 100.00% 3 0 100.00%
+fido_cbor_info_transports_ptr 1 0 100.00% 3 0 100.00%
+fido_cbor_info_transports_len 1 0 100.00% 3 0 100.00%
+fido_cbor_info_aaguid_ptr 1 0 100.00% 3 0 100.00%
+fido_cbor_info_aaguid_len 1 0 100.00% 3 0 100.00%
+fido_cbor_info_options_name_ptr 1 0 100.00% 3 0 100.00%
+fido_cbor_info_options_value_ptr 1 0 100.00% 3 0 100.00%
+fido_cbor_info_options_len 1 0 100.00% 3 0 100.00%
+fido_cbor_info_maxcredbloblen 1 0 100.00% 3 0 100.00%
+fido_cbor_info_maxmsgsiz 1 0 100.00% 3 0 100.00%
+fido_cbor_info_maxcredcntlst 1 0 100.00% 3 0 100.00%
+fido_cbor_info_maxcredidlen 1 0 100.00% 3 0 100.00%
+fido_cbor_info_fwversion 1 0 100.00% 3 0 100.00%
+fido_cbor_info_protocols_ptr 1 0 100.00% 3 0 100.00%
+fido_cbor_info_protocols_len 1 0 100.00% 3 0 100.00%
+fido_cbor_info_algorithm_count 1 0 100.00% 3 0 100.00%
+fido_cbor_info_algorithm_type 4 0 100.00% 6 0 100.00%
+fido_cbor_info_algorithm_cose 4 0 100.00% 6 0 100.00%
+info.c:fido_dev_get_cbor_info_tx 8 0 100.00% 12 0 100.00%
+info.c:fido_dev_get_cbor_info_rx 6 0 100.00% 18 0 100.00%
+info.c:parse_reply_element 19 0 100.00% 39 0 100.00%
+info.c:decode_string_array 12 0 100.00% 21 0 100.00%
+info.c:decode_string 4 0 100.00% 14 0 100.00%
+info.c:decode_aaguid 8 0 100.00% 12 0 100.00%
+info.c:decode_options 11 0 100.00% 18 0 100.00%
+info.c:decode_option 11 0 100.00% 22 0 100.00%
+info.c:decode_protocols 12 0 100.00% 21 0 100.00%
+info.c:decode_protocol 6 0 100.00% 16 0 100.00%
+info.c:decode_algorithms 12 0 100.00% 21 0 100.00%
+info.c:decode_algorithm 9 0 100.00% 23 0 100.00%
+info.c:decode_algorithm_entry 20 0 100.00% 31 0 100.00%
+info.c:free_algo 1 0 100.00% 5 0 100.00%
+info.c:free_str_array 4 0 100.00% 8 0 100.00%
+info.c:free_opt_array 4 0 100.00% 9 0 100.00%
+info.c:free_byte_array 1 0 100.00% 6 0 100.00%
+info.c:free_algo_array 4 0 100.00% 8 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 198 0 100.00% 405 0 100.00%
+
+File '/libfido2/src/io.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_tx 13 0 100.00% 13 0 100.00%
+fido_rx 13 1 92.31% 17 3 82.35%
+fido_rx_cbor_status 8 0 100.00% 12 0 100.00%
+io.c:tx_empty 9 0 100.00% 17 0 100.00%
+io.c:tx 13 0 100.00% 21 0 100.00%
+io.c:tx_preamble 16 1 93.75% 24 1 95.83%
+io.c:tx_frame 15 1 93.33% 22 1 95.45%
+io.c:rx 40 2 95.00% 65 1 98.46%
+io.c:rx_preamble 23 2 91.30% 26 5 80.77%
+io.c:rx_frame 8 0 100.00% 11 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 158 7 95.57% 228 11 95.18%
+
+File '/libfido2/src/iso7816.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+iso7816_new 4 0 100.00% 18 0 100.00%
+iso7816_free 6 0 100.00% 8 0 100.00%
+iso7816_add 6 1 83.33% 9 0 100.00%
+iso7816_ptr 1 0 100.00% 3 0 100.00%
+iso7816_len 1 0 100.00% 4 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 18 1 94.44% 42 0 100.00%
+
+File '/libfido2/src/largeblob.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_dev_largeblob_get 26 2 92.31% 41 4 90.24%
+fido_dev_largeblob_set 27 0 100.00% 39 0 100.00%
+fido_dev_largeblob_remove 12 0 100.00% 21 0 100.00%
+fido_dev_largeblob_get_array 15 2 86.67% 30 4 86.67%
+fido_dev_largeblob_set_array 14 0 100.00% 21 0 100.00%
+largeblob.c:largeblob_get_array 32 0 100.00% 39 0 100.00%
+largeblob.c:get_chunklen 9 1 88.89% 11 0 100.00%
+largeblob.c:largeblob_get_tx 19 0 100.00% 28 0 100.00%
+largeblob.c:largeblob_get_rx 15 0 100.00% 23 0 100.00%
+largeblob.c:parse_largeblob_reply 8 0 100.00% 10 0 100.00%
+largeblob.c:largeblob_array_check 7 0 100.00% 18 0 100.00%
+largeblob.c:largeblob_array_digest 10 0 100.00% 11 0 100.00%
+largeblob.c:largeblob_array_load 14 2 85.71% 21 7 66.67%
+largeblob.c:largeblob_array_lookup 25 0 100.00% 36 0 100.00%
+largeblob.c:largeblob_decode 16 2 87.50% 17 6 64.71%
+largeblob.c:largeblob_do_decode 27 3 88.89% 32 5 84.38%
+largeblob.c:largeblob_decrypt 15 0 100.00% 28 0 100.00%
+largeblob.c:largeblob_aad 1 0 100.00% 12 0 100.00%
+largeblob.c:largeblob_reset 1 0 100.00% 5 0 100.00%
+largeblob.c:largeblob_encode 16 0 100.00% 23 0 100.00%
+largeblob.c:largeblob_new 1 0 100.00% 3 0 100.00%
+largeblob.c:largeblob_seal 20 0 100.00% 35 0 100.00%
+largeblob.c:largeblob_get_nonce 8 1 87.50% 19 3 84.21%
+largeblob.c:largeblob_free 6 0 100.00% 9 0 100.00%
+largeblob.c:largeblob_add 27 2 92.59% 40 3 92.50%
+largeblob.c:largeblob_drop 21 0 100.00% 30 0 100.00%
+largeblob.c:largeblob_set_array 54 2 96.30% 64 4 93.75%
+largeblob.c:largeblob_get_uv_token 19 0 100.00% 27 0 100.00%
+largeblob.c:largeblob_set_tx 35 0 100.00% 40 0 100.00%
+largeblob.c:prepare_hmac 13 2 84.62% 26 7 73.08%
+-----------------------------------------------------------------------------------------
+TOTAL 513 19 96.30% 759 43 94.33%
+
+File '/libfido2/src/log.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_log_init 1 0 100.00% 4 0 100.00%
+fido_log_debug 6 1 83.33% 10 0 100.00%
+fido_log_xxd 16 1 93.75% 27 0 100.00%
+fido_log_error 8 2 75.00% 13 1 92.31%
+fido_set_log_handler 3 0 100.00% 4 0 100.00%
+log.c:log_on_stderr 1 1 0.00% 3 3 0.00%
+log.c:do_log 4 0 100.00% 12 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 39 5 87.18% 73 4 94.52%
+
+File '/libfido2/src/netlink.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_nl_power_nfc 18 1 94.44% 26 3 88.46%
+fido_nl_get_nfc_target 16 1 93.75% 33 3 90.91%
+fido_nl_free 10 2 80.00% 11 1 90.91%
+fido_nl_new 16 2 87.50% 29 6 79.31%
+set_netlink_io_functions 1 0 100.00% 4 0 100.00%
+netlink.c:nlmsg_new 8 0 100.00% 18 0 100.00%
+netlink.c:nlmsg_set_genl 1 0 100.00% 9 0 100.00%
+netlink.c:nlmsg_write 6 1 83.33% 9 1 88.89%
+netlink.c:nlmsg_set_u32 1 0 100.00% 3 0 100.00%
+netlink.c:nlmsg_setattr 14 1 92.86% 21 0 100.00%
+netlink.c:nlmsg_tx 10 1 90.00% 15 3 80.00%
+netlink.c:nlmsg_ptr 1 0 100.00% 3 0 100.00%
+netlink.c:nlmsg_len 1 0 100.00% 3 0 100.00%
+netlink.c:nlmsg_rx 11 3 72.73% 19 9 52.63%
+netlink.c:nl_parse_reply 20 0 100.00% 30 0 100.00%
+netlink.c:nlmsg_from_buf 15 0 100.00% 22 0 100.00%
+netlink.c:nlmsg_type 1 0 100.00% 3 0 100.00%
+netlink.c:nlmsg_get_status 8 0 100.00% 10 0 100.00%
+netlink.c:nlmsg_read 6 0 100.00% 9 0 100.00%
+netlink.c:nlmsg_get_genl 6 0 100.00% 10 0 100.00%
+netlink.c:nlmsg_iter 6 0 100.00% 15 0 100.00%
+netlink.c:nlmsg_getattr 1 0 100.00% 3 0 100.00%
+netlink.c:nla_from_buf 17 0 100.00% 26 0 100.00%
+netlink.c:nl_nfc_poll 18 1 94.44% 27 3 88.89%
+netlink.c:parse_nfc_event 10 0 100.00% 19 0 100.00%
+netlink.c:nla_type 1 0 100.00% 3 0 100.00%
+netlink.c:nla_get_u32 1 0 100.00% 3 0 100.00%
+netlink.c:nla_read 6 0 100.00% 9 0 100.00%
+netlink.c:nl_dump_nfc_target 19 1 94.74% 33 3 90.91%
+netlink.c:parse_target 9 0 100.00% 15 0 100.00%
+netlink.c:nl_get_nfc_family 23 1 95.65% 35 3 91.43%
+netlink.c:nlmsg_set_u16 1 0 100.00% 3 0 100.00%
+netlink.c:nlmsg_set_str 1 0 100.00% 3 0 100.00%
+netlink.c:parse_family 10 0 100.00% 20 0 100.00%
+netlink.c:nla_get_u16 1 0 100.00% 3 0 100.00%
+netlink.c:nla_iter 6 0 100.00% 15 0 100.00%
+netlink.c:nla_getattr 1 0 100.00% 3 0 100.00%
+netlink.c:parse_mcastgrps 1 0 100.00% 3 0 100.00%
+netlink.c:parse_mcastgrp 15 0 100.00% 27 0 100.00%
+netlink.c:nla_get_str 10 0 100.00% 13 0 100.00%
+-----------------------------------------------------------------------------------------
+TOTAL 327 15 95.41% 565 35 93.81%
+
+File '/libfido2/src/nfc_linux.c':
+Name Regions Miss Cover Lines Miss Cover
+-----------------------------------------------------------------------------------------
+fido_nfc_tx 28 0 100.00% 48 0 100.00%
+fido_nfc_rx 8 1 87.50% 13 3 76.92%
+fido_nfc_manifest 35 2 94.29% 54 0 100.00%
+fido_nfc_open 14 14 0.00% 21 21 0.00%
+fido_nfc_close 1 1 0.00% 5 5 0.00%
+fido_nfc_set_sigmask 2 2 0.00% 8 8 0.00%
+fido_nfc_read 14 14 0.00% 34 34 0.00%
+fido_nfc_write 12 12 0.00% 21 21 0.00%
+nfc_linux.c:nfc_do_tx 20 2 90.00% 30 6 80.00%
+nfc_linux.c:tx_short_apdu 14 0 100.00% 37 0 100.00%
+nfc_linux.c:rx_init 25 6 76.00% 34 5 85.29%
+nfc_linux.c:rx_cbor 4 0 100.00% 8 0 100.00%
+nfc_linux.c:rx_msg 18 2 88.89% 28 6 78.57%
+nfc_linux.c:rx_apdu 8 1 87.50% 22 3 86.36%
+nfc_linux.c:tx_get_response 4 0 100.00% 14 0 100.00%
+nfc_linux.c:copy_info 30 6 80.00% 42 0 100.00%
+nfc_linux.c:get_usb_attr 1 0 100.00% 3 0 100.00%
+nfc_linux.c:get_parent_attr 6 0 100.00% 11 0 100.00%
+nfc_linux.c:to_int 21 21 0.00% 16 16 0.00%
+nfc_linux.c:sysnum_from_syspath 12 12 0.00% 20 20 0.00%
+nfc_linux.c:nfc_new 6 6 0.00% 14 14 0.00%
+nfc_linux.c:nfc_target_connect 9 9 0.00% 24 24 0.00%
+nfc_linux.c:nfc_free 12 12 0.00% 13 13 0.00%
+-----------------------------------------------------------------------------------------
+TOTAL 304 123 59.54% 520 199 61.73%
+
+File '/libfido2/src/pin.c':
+Name Regions Miss Cover Lines Miss Cover
+-------------------------------------------------------------------------------------------
+fido_sha256 7 0 100.00% 13 0 100.00%
+fido_dev_get_uv_token 1 0 100.00% 3 0 100.00%
+fido_dev_set_pin 1 0 100.00% 3 0 100.00%
+fido_dev_get_retry_count 1 0 100.00% 3 0 100.00%
+fido_dev_get_uv_retry_count 1 0 100.00% 3 0 100.00%
+cbor_add_uv_params 17 0 100.00% 28 0 100.00%
+pin.c:uv_token_wait 14 2 85.71% 14 0 100.00%
+pin.c:ctap21_uv_token_tx 49 0 100.00% 59 0 100.00%
+pin.c:pin_sha256_enc 19 0 100.00% 30 0 100.00%
+pin.c:encode_uv_permission 20 1 95.00% 19 3 84.21%
+pin.c:ctap20_uv_token_tx 37 0 100.00% 53 0 100.00%
+pin.c:uv_token_rx 20 0 100.00% 36 0 100.00%
+pin.c:parse_uv_token 8 0 100.00% 12 0 100.00%
+pin.c:fido_dev_set_pin_wait 21 0 100.00% 27 0 100.00%
+pin.c:fido_dev_change_pin_tx 45 0 100.00% 68 0 100.00%
+pin.c:pin_pad64_enc 15 0 100.00% 26 0 100.00%
+pin.c:pad64 18 0 100.00% 24 0 100.00%
+pin.c:fido_dev_set_pin_tx 33 0 100.00% 48 0 100.00%
+pin.c:fido_dev_get_pin_retry_count_wait 10 0 100.00% 9 0 100.00%
+pin.c:fido_dev_get_retry_count_tx 19 0 100.00% 28 0 100.00%
+pin.c:fido_dev_get_pin_retry_count_rx 11 0 100.00% 21 0 100.00%
+pin.c:parse_pin_retry_count 1 0 100.00% 3 0 100.00%
+pin.c:parse_retry_count 13 0 100.00% 20 0 100.00%
+pin.c:fido_dev_get_uv_retry_count_wait 10 0 100.00% 9 0 100.00%
+pin.c:fido_dev_get_uv_retry_count_rx 11 0 100.00% 21 0 100.00%
+pin.c:parse_uv_retry_count 1 0 100.00% 3 0 100.00%
+-------------------------------------------------------------------------------------------
+TOTAL 403 3 99.26% 583 3 99.49%
+
+File '/libfido2/src/random.c':
+Name Regions Miss Cover Lines Miss Cover
+-------------------------------------------------------------------------------------------
+fido_get_random 6 1 83.33% 8 1 87.50%
+-------------------------------------------------------------------------------------------
+TOTAL 6 1 83.33% 8 1 87.50%
+
+File '/libfido2/src/reset.c':
+Name Regions Miss Cover Lines Miss Cover
+-------------------------------------------------------------------------------------------
+fido_dev_reset 1 0 100.00% 3 0 100.00%
+reset.c:fido_dev_reset_wait 15 0 100.00% 14 0 100.00%
+reset.c:fido_dev_reset_tx 8 0 100.00% 10 0 100.00%
+-------------------------------------------------------------------------------------------
+TOTAL 24 0 100.00% 27 0 100.00%
+
+File '/libfido2/src/rs256.c':
+Name Regions Miss Cover Lines Miss Cover
+-------------------------------------------------------------------------------------------
+rs256_pk_decode 8 0 100.00% 10 0 100.00%
+rs256_pk_new 1 0 100.00% 3 0 100.00%
+rs256_pk_free 6 0 100.00% 9 0 100.00%
+rs256_pk_from_ptr 6 0 100.00% 8 0 100.00%
+rs256_pk_to_EVP_PKEY 32 0 100.00% 48 0 100.00%
+rs256_pk_from_RSA 32 4 87.50% 32 6 81.25%
+rs256.c:decode_rsa_pubkey 9 0 100.00% 16 0 100.00%
+rs256.c:decode_bignum 8 0 100.00% 12 0 100.00%
+-------------------------------------------------------------------------------------------
+TOTAL 102 4 96.08% 138 6 95.65%
+
+File '/libfido2/src/u2f.c':
+Name Regions Miss Cover Lines Miss Cover
+-------------------------------------------------------------------------------------------
+u2f_register 70 1 98.57% 88 0 100.00%
+u2f_authenticate 32 0 100.00% 44 0 100.00%
+u2f_get_touch_begin 30 0 100.00% 46 0 100.00%
+u2f_get_touch_status 18 0 100.00% 29 0 100.00%
+u2f.c:key_lookup 44 0 100.00% 69 0 100.00%
+u2f.c:send_dummy_register 31 1 96.77% 49 0 100.00%
+u2f.c:parse_register_reply 49 0 100.00% 71 0 100.00%
+u2f.c:x5c_get 21 1 95.24% 34 3 91.18%
+u2f.c:sig_get 6 0 100.00% 11 0 100.00%
+u2f.c:encode_cred_authdata 33 2 93.94% 76 6 92.11%
+u2f.c:cbor_blob_from_ec_point 22 0 100.00% 39 0 100.00%
+u2f.c:u2f_authenticate_single 32 0 100.00% 52 0 100.00%
+u2f.c:do_auth 50 1 98.00% 71 0 100.00%
+u2f.c:parse_auth_reply 23 0 100.00% 29 0 100.00%
+u2f.c:authdata_fake 12 0 100.00% 34 0 100.00%
+-------------------------------------------------------------------------------------------
+TOTAL 473 6 98.73% 742 9 98.79%
diff --git a/fuzz/fuzz_assert.c b/fuzz/fuzz_assert.c
new file mode 100644
index 000000000000..1ecbde38bd0a
--- /dev/null
+++ b/fuzz/fuzz_assert.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mutator_aux.h"
+#include "wiredata_fido2.h"
+#include "wiredata_u2f.h"
+#include "dummy.h"
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+/* Parameter set defining a FIDO2 get assertion operation. */
+struct param {
+ char pin[MAXSTR];
+ char rp_id[MAXSTR];
+ int ext;
+ int seed;
+ struct blob cdh;
+ struct blob cred;
+ struct blob es256;
+ struct blob rs256;
+ struct blob eddsa;
+ struct blob wire_data;
+ uint8_t cred_count;
+ uint8_t type;
+ uint8_t opt;
+ uint8_t up;
+ uint8_t uv;
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * get assertion using the example parameters above.
+ */
+static const uint8_t dummy_wire_data_fido[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_ASSERT,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a U2F
+ * authentication using the example parameters above.
+ */
+static const uint8_t dummy_wire_data_u2f[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_AUTH,
+};
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 15 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_byte(v[0], &p->uv) < 0 ||
+ unpack_byte(v[1], &p->up) < 0 ||
+ unpack_byte(v[2], &p->opt) < 0 ||
+ unpack_byte(v[3], &p->type) < 0 ||
+ unpack_byte(v[4], &p->cred_count) < 0 ||
+ unpack_int(v[5], &p->ext) < 0 ||
+ unpack_int(v[6], &p->seed) < 0 ||
+ unpack_string(v[7], p->rp_id) < 0 ||
+ unpack_string(v[8], p->pin) < 0 ||
+ unpack_blob(v[9], &p->wire_data) < 0 ||
+ unpack_blob(v[10], &p->rs256) < 0 ||
+ unpack_blob(v[11], &p->es256) < 0 ||
+ unpack_blob(v[12], &p->eddsa) < 0 ||
+ unpack_blob(v[13], &p->cred) < 0 ||
+ unpack_blob(v[14], &p->cdh) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[15], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(15)) == NULL ||
+ (argv[0] = pack_byte(p->uv)) == NULL ||
+ (argv[1] = pack_byte(p->up)) == NULL ||
+ (argv[2] = pack_byte(p->opt)) == NULL ||
+ (argv[3] = pack_byte(p->type)) == NULL ||
+ (argv[4] = pack_byte(p->cred_count)) == NULL ||
+ (argv[5] = pack_int(p->ext)) == NULL ||
+ (argv[6] = pack_int(p->seed)) == NULL ||
+ (argv[7] = pack_string(p->rp_id)) == NULL ||
+ (argv[8] = pack_string(p->pin)) == NULL ||
+ (argv[9] = pack_blob(&p->wire_data)) == NULL ||
+ (argv[10] = pack_blob(&p->rs256)) == NULL ||
+ (argv[11] = pack_blob(&p->es256)) == NULL ||
+ (argv[12] = pack_blob(&p->eddsa)) == NULL ||
+ (argv[13] = pack_blob(&p->cred)) == NULL ||
+ (argv[14] = pack_blob(&p->cdh)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 15; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 15; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ dummy.type = 1; /* rsa */
+ dummy.ext = FIDO_EXT_HMAC_SECRET;
+
+ strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
+ strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
+
+ dummy.cred.len = sizeof(dummy_cdh); /* XXX */
+ dummy.cdh.len = sizeof(dummy_cdh);
+ dummy.es256.len = sizeof(dummy_es256);
+ dummy.rs256.len = sizeof(dummy_rs256);
+ dummy.eddsa.len = sizeof(dummy_eddsa);
+ dummy.wire_data.len = sizeof(dummy_wire_data_fido);
+
+ memcpy(&dummy.cred.body, &dummy_cdh, dummy.cred.len); /* XXX */
+ memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len);
+ memcpy(&dummy.wire_data.body, &dummy_wire_data_fido,
+ dummy.wire_data.len);
+ memcpy(&dummy.es256.body, &dummy_es256, dummy.es256.len);
+ memcpy(&dummy.rs256.body, &dummy_rs256, dummy.rs256.len);
+ memcpy(&dummy.eddsa.body, &dummy_eddsa, dummy.eddsa.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+
+ if (blob_len > len) {
+ memcpy(ptr, blob, len);
+ return len;
+ }
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+static void
+get_assert(fido_assert_t *assert, uint8_t opt, const struct blob *cdh,
+ const char *rp_id, int ext, uint8_t up, uint8_t uv, const char *pin,
+ uint8_t cred_count, const struct blob *cred)
+{
+ fido_dev_t *dev;
+
+ if ((dev = open_dev(opt & 2)) == NULL)
+ return;
+ if (opt & 1)
+ fido_dev_force_u2f(dev);
+ if (ext & FIDO_EXT_HMAC_SECRET)
+ fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET);
+ if (ext & FIDO_EXT_CRED_BLOB)
+ fido_assert_set_extensions(assert, FIDO_EXT_CRED_BLOB);
+ if (ext & FIDO_EXT_LARGEBLOB_KEY)
+ fido_assert_set_extensions(assert, FIDO_EXT_LARGEBLOB_KEY);
+ if (up & 1)
+ fido_assert_set_up(assert, FIDO_OPT_TRUE);
+ else if (opt & 1)
+ fido_assert_set_up(assert, FIDO_OPT_FALSE);
+ if (uv & 1)
+ fido_assert_set_uv(assert, FIDO_OPT_TRUE);
+
+ for (uint8_t i = 0; i < cred_count; i++)
+ fido_assert_allow_cred(assert, cred->body, cred->len);
+
+ fido_assert_set_clientdata_hash(assert, cdh->body, cdh->len);
+ fido_assert_set_rp(assert, rp_id);
+ /* XXX reuse cred as hmac salt */
+ fido_assert_set_hmac_salt(assert, cred->body, cred->len);
+
+ /* repeat memory operations to trigger reallocation paths */
+ fido_assert_set_clientdata_hash(assert, cdh->body, cdh->len);
+ fido_assert_set_rp(assert, rp_id);
+ fido_assert_set_hmac_salt(assert, cred->body, cred->len);
+
+ if (strlen(pin) == 0)
+ pin = NULL;
+
+ fido_dev_get_assert(dev, assert, (opt & 1) ? NULL : pin);
+
+ fido_dev_cancel(dev);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+verify_assert(int type, const unsigned char *cdh_ptr, size_t cdh_len,
+ const char *rp_id, const unsigned char *authdata_ptr, size_t authdata_len,
+ const unsigned char *sig_ptr, size_t sig_len, uint8_t up, uint8_t uv,
+ int ext, void *pk)
+{
+ fido_assert_t *assert = NULL;
+
+ if ((assert = fido_assert_new()) == NULL)
+ return;
+
+ fido_assert_set_clientdata_hash(assert, cdh_ptr, cdh_len);
+ fido_assert_set_rp(assert, rp_id);
+ fido_assert_set_count(assert, 1);
+
+ if (fido_assert_set_authdata(assert, 0, authdata_ptr,
+ authdata_len) != FIDO_OK) {
+ fido_assert_set_authdata_raw(assert, 0, authdata_ptr,
+ authdata_len);
+ }
+
+ if (up & 1)
+ fido_assert_set_up(assert, FIDO_OPT_TRUE);
+ if (uv & 1)
+ fido_assert_set_uv(assert, FIDO_OPT_TRUE);
+
+ fido_assert_set_extensions(assert, ext);
+ fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
+
+ /* repeat memory operations to trigger reallocation paths */
+ if (fido_assert_set_authdata(assert, 0, authdata_ptr,
+ authdata_len) != FIDO_OK) {
+ fido_assert_set_authdata_raw(assert, 0, authdata_ptr,
+ authdata_len);
+ }
+ fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
+
+ assert(fido_assert_verify(assert, 0, type, pk) != FIDO_OK);
+
+ fido_assert_free(&assert);
+}
+
+/*
+ * Do a dummy conversion to exercise rs256_pk_from_RSA().
+ */
+static void
+rs256_convert(const rs256_pk_t *k)
+{
+ EVP_PKEY *pkey = NULL;
+ rs256_pk_t *pk = NULL;
+ RSA *rsa = NULL;
+ volatile int r;
+
+ if ((pkey = rs256_pk_to_EVP_PKEY(k)) == NULL ||
+ (pk = rs256_pk_new()) == NULL ||
+ (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL)
+ goto out;
+
+ r = rs256_pk_from_RSA(pk, rsa);
+out:
+ if (pk)
+ rs256_pk_free(&pk);
+ if (pkey)
+ EVP_PKEY_free(pkey);
+}
+
+/*
+ * Do a dummy conversion to exercise eddsa_pk_from_EVP_PKEY().
+ */
+static void
+eddsa_convert(const eddsa_pk_t *k)
+{
+ EVP_PKEY *pkey = NULL;
+ eddsa_pk_t *pk = NULL;
+ volatile int r;
+
+ if ((pkey = eddsa_pk_to_EVP_PKEY(k)) == NULL ||
+ (pk = eddsa_pk_new()) == NULL)
+ goto out;
+
+ r = eddsa_pk_from_EVP_PKEY(pk, pkey);
+out:
+ if (pk)
+ eddsa_pk_free(&pk);
+ if (pkey)
+ EVP_PKEY_free(pkey);
+}
+
+void
+test(const struct param *p)
+{
+ fido_assert_t *assert = NULL;
+ es256_pk_t *es256_pk = NULL;
+ rs256_pk_t *rs256_pk = NULL;
+ eddsa_pk_t *eddsa_pk = NULL;
+ uint8_t flags;
+ uint32_t sigcount;
+ int cose_alg = 0;
+ void *pk;
+
+ prng_init((unsigned int)p->seed);
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ switch (p->type & 3) {
+ case 0:
+ cose_alg = COSE_ES256;
+
+ if ((es256_pk = es256_pk_new()) == NULL)
+ return;
+
+ es256_pk_from_ptr(es256_pk, p->es256.body, p->es256.len);
+ pk = es256_pk;
+
+ break;
+ case 1:
+ cose_alg = COSE_RS256;
+
+ if ((rs256_pk = rs256_pk_new()) == NULL)
+ return;
+
+ rs256_pk_from_ptr(rs256_pk, p->rs256.body, p->rs256.len);
+ pk = rs256_pk;
+
+ rs256_convert(pk);
+
+ break;
+ default:
+ cose_alg = COSE_EDDSA;
+
+ if ((eddsa_pk = eddsa_pk_new()) == NULL)
+ return;
+
+ eddsa_pk_from_ptr(eddsa_pk, p->eddsa.body, p->eddsa.len);
+ pk = eddsa_pk;
+
+ eddsa_convert(pk);
+
+ break;
+ }
+
+ if ((assert = fido_assert_new()) == NULL)
+ goto out;
+
+ set_wire_data(p->wire_data.body, p->wire_data.len);
+
+ get_assert(assert, p->opt, &p->cdh, p->rp_id, p->ext, p->up, p->uv,
+ p->pin, p->cred_count, &p->cred);
+
+ /* XXX +1 on purpose */
+ for (size_t i = 0; i <= fido_assert_count(assert); i++) {
+ verify_assert(cose_alg,
+ fido_assert_clientdata_hash_ptr(assert),
+ fido_assert_clientdata_hash_len(assert),
+ fido_assert_rp_id(assert),
+ fido_assert_authdata_ptr(assert, i),
+ fido_assert_authdata_len(assert, i),
+ fido_assert_sig_ptr(assert, i),
+ fido_assert_sig_len(assert, i), p->up, p->uv, p->ext, pk);
+ consume(fido_assert_id_ptr(assert, i),
+ fido_assert_id_len(assert, i));
+ consume(fido_assert_user_id_ptr(assert, i),
+ fido_assert_user_id_len(assert, i));
+ consume(fido_assert_hmac_secret_ptr(assert, i),
+ fido_assert_hmac_secret_len(assert, i));
+ consume_str(fido_assert_user_icon(assert, i));
+ consume_str(fido_assert_user_name(assert, i));
+ consume_str(fido_assert_user_display_name(assert, i));
+ consume(fido_assert_blob_ptr(assert, i),
+ fido_assert_blob_len(assert, i));
+ consume(fido_assert_largeblob_key_ptr(assert, i),
+ fido_assert_largeblob_key_len(assert, i));
+ flags = fido_assert_flags(assert, i);
+ consume(&flags, sizeof(flags));
+ sigcount = fido_assert_sigcount(assert, i);
+ consume(&sigcount, sizeof(sigcount));
+ }
+
+out:
+ es256_pk_free(&es256_pk);
+ rs256_pk_free(&rs256_pk);
+ eddsa_pk_free(&eddsa_pk);
+
+ fido_assert_free(&assert);
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM) {
+ mutate_byte(&p->uv);
+ mutate_byte(&p->up);
+ mutate_byte(&p->opt);
+ mutate_byte(&p->type);
+ mutate_byte(&p->cred_count);
+ mutate_int(&p->ext);
+ mutate_blob(&p->rs256);
+ mutate_blob(&p->es256);
+ mutate_blob(&p->eddsa);
+ mutate_blob(&p->cred);
+ mutate_blob(&p->cdh);
+ mutate_string(p->rp_id);
+ mutate_string(p->pin);
+ }
+
+ if (flags & MUTATE_WIREDATA) {
+ if (p->opt & 1) {
+ p->wire_data.len = sizeof(dummy_wire_data_u2f);
+ memcpy(&p->wire_data.body, &dummy_wire_data_u2f,
+ p->wire_data.len);
+ } else {
+ p->wire_data.len = sizeof(dummy_wire_data_fido);
+ memcpy(&p->wire_data.body, &dummy_wire_data_fido,
+ p->wire_data.len);
+ }
+ mutate_blob(&p->wire_data);
+ }
+}
diff --git a/fuzz/fuzz_bio.c b/fuzz/fuzz_bio.c
new file mode 100644
index 000000000000..ed3deec93693
--- /dev/null
+++ b/fuzz/fuzz_bio.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mutator_aux.h"
+#include "wiredata_fido2.h"
+#include "dummy.h"
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+/* Parameter set defining a FIDO2 credential management operation. */
+struct param {
+ char pin[MAXSTR];
+ char name[MAXSTR];
+ int seed;
+ struct blob id;
+ struct blob info_wire_data;
+ struct blob enroll_wire_data;
+ struct blob list_wire_data;
+ struct blob set_name_wire_data;
+ struct blob remove_wire_data;
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'getFingerprintSensorInfo' bio enrollment command.
+ */
+static const uint8_t dummy_info_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_BIO_INFO,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with FIDO2
+ * 'enrollBegin' + 'enrollCaptureNextSample' bio enrollment commands.
+ */
+static const uint8_t dummy_enroll_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_BIO_ENROLL,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'enumerateEnrollments' bio enrollment command.
+ */
+static const uint8_t dummy_list_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_BIO_ENUM,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'setFriendlyName' bio enrollment command.
+ */
+static const uint8_t dummy_set_name_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_STATUS,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'removeEnrollment' bio enrollment command.
+ */
+static const uint8_t dummy_remove_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_STATUS,
+};
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 9 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_int(v[0], &p->seed) < 0 ||
+ unpack_string(v[1], p->pin) < 0 ||
+ unpack_string(v[2], p->name) < 0 ||
+ unpack_blob(v[3], &p->id) < 0 ||
+ unpack_blob(v[4], &p->info_wire_data) < 0 ||
+ unpack_blob(v[5], &p->enroll_wire_data) < 0 ||
+ unpack_blob(v[6], &p->list_wire_data) < 0 ||
+ unpack_blob(v[7], &p->set_name_wire_data) < 0 ||
+ unpack_blob(v[8], &p->remove_wire_data) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[9], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(9)) == NULL ||
+ (argv[0] = pack_int(p->seed)) == NULL ||
+ (argv[1] = pack_string(p->pin)) == NULL ||
+ (argv[2] = pack_string(p->name)) == NULL ||
+ (argv[3] = pack_blob(&p->id)) == NULL ||
+ (argv[4] = pack_blob(&p->info_wire_data)) == NULL ||
+ (argv[5] = pack_blob(&p->enroll_wire_data)) == NULL ||
+ (argv[6] = pack_blob(&p->list_wire_data)) == NULL ||
+ (argv[7] = pack_blob(&p->set_name_wire_data)) == NULL ||
+ (argv[8] = pack_blob(&p->remove_wire_data)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 9; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 9; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
+ strlcpy(dummy.name, dummy_name, sizeof(dummy.name));
+
+ dummy.info_wire_data.len = sizeof(dummy_info_wire_data);
+ dummy.enroll_wire_data.len = sizeof(dummy_enroll_wire_data);
+ dummy.list_wire_data.len = sizeof(dummy_list_wire_data);
+ dummy.set_name_wire_data.len = sizeof(dummy_set_name_wire_data);
+ dummy.remove_wire_data.len = sizeof(dummy_remove_wire_data);
+ dummy.id.len = sizeof(dummy_id);
+
+ memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data,
+ dummy.info_wire_data.len);
+ memcpy(&dummy.enroll_wire_data.body, &dummy_enroll_wire_data,
+ dummy.enroll_wire_data.len);
+ memcpy(&dummy.list_wire_data.body, &dummy_list_wire_data,
+ dummy.list_wire_data.len);
+ memcpy(&dummy.set_name_wire_data.body, &dummy_set_name_wire_data,
+ dummy.set_name_wire_data.len);
+ memcpy(&dummy.remove_wire_data.body, &dummy_remove_wire_data,
+ dummy.remove_wire_data.len);
+ memcpy(&dummy.id.body, &dummy_id, dummy.id.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+
+ if (blob_len > len) {
+ memcpy(ptr, blob, len);
+ return len;
+ }
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+static fido_dev_t *
+prepare_dev(void)
+{
+ fido_dev_t *dev;
+ bool x;
+
+ if ((dev = open_dev(0)) == NULL)
+ return NULL;
+
+ x = fido_dev_is_fido2(dev);
+ consume(&x, sizeof(x));
+ x = fido_dev_supports_pin(dev);
+ consume(&x, sizeof(x));
+ x = fido_dev_has_pin(dev);
+ consume(&x, sizeof(x));
+ x = fido_dev_supports_uv(dev);
+ consume(&x, sizeof(x));
+ x = fido_dev_has_uv(dev);
+ consume(&x, sizeof(x));
+
+ return dev;
+}
+
+static void
+get_info(const struct param *p)
+{
+ fido_dev_t *dev = NULL;
+ fido_bio_info_t *i = NULL;
+ uint8_t type;
+ uint8_t max_samples;
+ int r;
+
+ set_wire_data(p->info_wire_data.body, p->info_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL || (i = fido_bio_info_new()) == NULL)
+ goto done;
+
+ r = fido_bio_dev_get_info(dev, i);
+ consume_str(fido_strerr(r));
+
+ type = fido_bio_info_type(i);
+ max_samples = fido_bio_info_max_samples(i);
+ consume(&type, sizeof(type));
+ consume(&max_samples, sizeof(max_samples));
+
+done:
+ if (dev)
+ fido_dev_close(dev);
+
+ fido_dev_free(&dev);
+ fido_bio_info_free(&i);
+}
+
+static void
+consume_template(const fido_bio_template_t *t)
+{
+ consume_str(fido_bio_template_name(t));
+ consume(fido_bio_template_id_ptr(t), fido_bio_template_id_len(t));
+}
+
+static void
+consume_enroll(fido_bio_enroll_t *e)
+{
+ uint8_t last_status;
+ uint8_t remaining_samples;
+
+ last_status = fido_bio_enroll_last_status(e);
+ remaining_samples = fido_bio_enroll_remaining_samples(e);
+ consume(&last_status, sizeof(last_status));
+ consume(&remaining_samples, sizeof(remaining_samples));
+}
+
+static void
+enroll(const struct param *p)
+{
+ fido_dev_t *dev = NULL;
+ fido_bio_template_t *t = NULL;
+ fido_bio_enroll_t *e = NULL;
+ size_t cnt = 0;
+
+ set_wire_data(p->enroll_wire_data.body, p->enroll_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL ||
+ (t = fido_bio_template_new()) == NULL ||
+ (e = fido_bio_enroll_new()) == NULL)
+ goto done;
+
+ fido_bio_dev_enroll_begin(dev, t, e, (uint32_t)p->seed, p->pin);
+
+ consume_template(t);
+ consume_enroll(e);
+
+ while (fido_bio_enroll_remaining_samples(e) > 0 && cnt++ < 5) {
+ fido_bio_dev_enroll_continue(dev, t, e, p->seed);
+ consume_template(t);
+ consume_enroll(e);
+ }
+
+done:
+ if (dev)
+ fido_dev_close(dev);
+
+ fido_dev_free(&dev);
+ fido_bio_template_free(&t);
+ fido_bio_enroll_free(&e);
+}
+
+static void
+list(const struct param *p)
+{
+ fido_dev_t *dev = NULL;
+ fido_bio_template_array_t *ta = NULL;
+ const fido_bio_template_t *t = NULL;
+
+ set_wire_data(p->list_wire_data.body, p->list_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL ||
+ (ta = fido_bio_template_array_new()) == NULL)
+ goto done;
+
+ fido_bio_dev_get_template_array(dev, ta, p->pin);
+
+ /* +1 on purpose */
+ for (size_t i = 0; i < fido_bio_template_array_count(ta) + 1; i++)
+ if ((t = fido_bio_template(ta, i)) != NULL)
+ consume_template(t);
+
+done:
+ if (dev)
+ fido_dev_close(dev);
+
+ fido_dev_free(&dev);
+ fido_bio_template_array_free(&ta);
+}
+
+static void
+set_name(const struct param *p)
+{
+ fido_dev_t *dev = NULL;
+ fido_bio_template_t *t = NULL;
+
+ set_wire_data(p->set_name_wire_data.body, p->set_name_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL ||
+ (t = fido_bio_template_new()) == NULL)
+ goto done;
+
+ fido_bio_template_set_name(t, p->name);
+ fido_bio_template_set_id(t, p->id.body, p->id.len);
+ consume_template(t);
+
+ fido_bio_dev_set_template_name(dev, t, p->pin);
+
+done:
+ if (dev)
+ fido_dev_close(dev);
+
+ fido_dev_free(&dev);
+ fido_bio_template_free(&t);
+}
+
+static void
+del(const struct param *p)
+{
+ fido_dev_t *dev = NULL;
+ fido_bio_template_t *t = NULL;
+ int r;
+
+ set_wire_data(p->remove_wire_data.body, p->remove_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL ||
+ (t = fido_bio_template_new()) == NULL)
+ goto done;
+
+ r = fido_bio_template_set_id(t, p->id.body, p->id.len);
+ consume_template(t);
+ consume_str(fido_strerr(r));
+
+ fido_bio_dev_enroll_remove(dev, t, p->pin);
+
+done:
+ if (dev)
+ fido_dev_close(dev);
+
+ fido_dev_free(&dev);
+ fido_bio_template_free(&t);
+}
+
+void
+test(const struct param *p)
+{
+ prng_init((unsigned int)p->seed);
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ get_info(p);
+ enroll(p);
+ list(p);
+ set_name(p);
+ del(p);
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM) {
+ mutate_blob(&p->id);
+ mutate_string(p->pin);
+ mutate_string(p->name);
+ }
+
+ if (flags & MUTATE_WIREDATA) {
+ mutate_blob(&p->info_wire_data);
+ mutate_blob(&p->enroll_wire_data);
+ mutate_blob(&p->list_wire_data);
+ mutate_blob(&p->set_name_wire_data);
+ mutate_blob(&p->remove_wire_data);
+ }
+}
diff --git a/fuzz/fuzz_cred.c b/fuzz/fuzz_cred.c
new file mode 100644
index 000000000000..004852d3451a
--- /dev/null
+++ b/fuzz/fuzz_cred.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mutator_aux.h"
+#include "wiredata_fido2.h"
+#include "wiredata_u2f.h"
+#include "dummy.h"
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+/* Parameter set defining a FIDO2 make credential operation. */
+struct param {
+ char pin[MAXSTR];
+ char rp_id[MAXSTR];
+ char rp_name[MAXSTR];
+ char user_icon[MAXSTR];
+ char user_name[MAXSTR];
+ char user_nick[MAXSTR];
+ int ext;
+ int seed;
+ struct blob cdh;
+ struct blob excl_cred;
+ struct blob user_id;
+ struct blob wire_data;
+ uint8_t excl_count;
+ uint8_t rk;
+ uint8_t type;
+ uint8_t opt;
+ uint8_t uv;
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * make credential using the example parameters above.
+ */
+static const uint8_t dummy_wire_data_fido[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_KEEPALIVE,
+ WIREDATA_CTAP_KEEPALIVE,
+ WIREDATA_CTAP_KEEPALIVE,
+ WIREDATA_CTAP_CBOR_CRED,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a U2F
+ * registration using the example parameters above.
+ */
+static const uint8_t dummy_wire_data_u2f[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_6985,
+ WIREDATA_CTAP_U2F_REGISTER,
+};
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 17 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_byte(v[0], &p->rk) < 0 ||
+ unpack_byte(v[1], &p->type) < 0 ||
+ unpack_byte(v[2], &p->opt) < 0 ||
+ unpack_byte(v[3], &p->uv) < 0 ||
+ unpack_byte(v[4], &p->excl_count) < 0 ||
+ unpack_int(v[5], &p->ext) < 0 ||
+ unpack_int(v[6], &p->seed) < 0 ||
+ unpack_string(v[7], p->pin) < 0 ||
+ unpack_string(v[8], p->rp_id) < 0 ||
+ unpack_string(v[9], p->rp_name) < 0 ||
+ unpack_string(v[10], p->user_icon) < 0 ||
+ unpack_string(v[11], p->user_name) < 0 ||
+ unpack_string(v[12], p->user_nick) < 0 ||
+ unpack_blob(v[13], &p->cdh) < 0 ||
+ unpack_blob(v[14], &p->user_id) < 0 ||
+ unpack_blob(v[15], &p->wire_data) < 0 ||
+ unpack_blob(v[16], &p->excl_cred) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[17], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(17)) == NULL ||
+ (argv[0] = pack_byte(p->rk)) == NULL ||
+ (argv[1] = pack_byte(p->type)) == NULL ||
+ (argv[2] = pack_byte(p->opt)) == NULL ||
+ (argv[3] = pack_byte(p->uv)) == NULL ||
+ (argv[4] = pack_byte(p->excl_count)) == NULL ||
+ (argv[5] = pack_int(p->ext)) == NULL ||
+ (argv[6] = pack_int(p->seed)) == NULL ||
+ (argv[7] = pack_string(p->pin)) == NULL ||
+ (argv[8] = pack_string(p->rp_id)) == NULL ||
+ (argv[9] = pack_string(p->rp_name)) == NULL ||
+ (argv[10] = pack_string(p->user_icon)) == NULL ||
+ (argv[11] = pack_string(p->user_name)) == NULL ||
+ (argv[12] = pack_string(p->user_nick)) == NULL ||
+ (argv[13] = pack_blob(&p->cdh)) == NULL ||
+ (argv[14] = pack_blob(&p->user_id)) == NULL ||
+ (argv[15] = pack_blob(&p->wire_data)) == NULL ||
+ (argv[16] = pack_blob(&p->excl_cred)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 17; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 17; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ dummy.type = 1;
+ dummy.ext = FIDO_EXT_HMAC_SECRET;
+
+ strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
+ strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
+ strlcpy(dummy.rp_name, dummy_rp_name, sizeof(dummy.rp_name));
+ strlcpy(dummy.user_icon, dummy_user_icon, sizeof(dummy.user_icon));
+ strlcpy(dummy.user_name, dummy_user_name, sizeof(dummy.user_name));
+ strlcpy(dummy.user_nick, dummy_user_nick, sizeof(dummy.user_nick));
+
+ dummy.cdh.len = sizeof(dummy_cdh);
+ dummy.user_id.len = sizeof(dummy_user_id);
+ dummy.wire_data.len = sizeof(dummy_wire_data_fido);
+
+ memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len);
+ memcpy(&dummy.user_id.body, &dummy_user_id, dummy.user_id.len);
+ memcpy(&dummy.wire_data.body, &dummy_wire_data_fido,
+ dummy.wire_data.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+
+ if (blob_len > len) {
+ memcpy(ptr, blob, len);
+ return len;
+ }
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+static void
+make_cred(fido_cred_t *cred, uint8_t opt, int type, const struct blob *cdh,
+ const char *rp_id, const char *rp_name, const struct blob *user_id,
+ const char *user_name, const char *user_nick, const char *user_icon,
+ int ext, uint8_t rk, uint8_t uv, const char *pin, uint8_t excl_count,
+ const struct blob *excl_cred)
+{
+ fido_dev_t *dev;
+
+ if ((dev = open_dev(opt & 2)) == NULL)
+ return;
+ if (opt & 1)
+ fido_dev_force_u2f(dev);
+
+ for (uint8_t i = 0; i < excl_count; i++)
+ fido_cred_exclude(cred, excl_cred->body, excl_cred->len);
+
+ fido_cred_set_type(cred, type);
+ fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len);
+ fido_cred_set_rp(cred, rp_id, rp_name);
+ fido_cred_set_user(cred, user_id->body, user_id->len, user_name,
+ user_nick, user_icon);
+ if (ext & FIDO_EXT_HMAC_SECRET)
+ fido_cred_set_extensions(cred, FIDO_EXT_HMAC_SECRET);
+ if (ext & FIDO_EXT_CRED_BLOB)
+ fido_cred_set_blob(cred, user_id->body, user_id->len);
+ if (ext & FIDO_EXT_LARGEBLOB_KEY)
+ fido_cred_set_extensions(cred, FIDO_EXT_LARGEBLOB_KEY);
+
+ if (rk & 1)
+ fido_cred_set_rk(cred, FIDO_OPT_TRUE);
+ if (uv & 1)
+ fido_cred_set_uv(cred, FIDO_OPT_TRUE);
+ if (user_id->len)
+ fido_cred_set_prot(cred, user_id->body[0] & 0x03);
+
+ /* repeat memory operations to trigger reallocation paths */
+ fido_cred_set_type(cred, type);
+ fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len);
+ fido_cred_set_rp(cred, rp_id, rp_name);
+ fido_cred_set_user(cred, user_id->body, user_id->len, user_name,
+ user_nick, user_icon);
+
+ if (strlen(pin) == 0)
+ pin = NULL;
+
+ fido_dev_make_cred(dev, cred, (opt & 1) ? NULL : pin);
+
+ fido_dev_cancel(dev);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+verify_cred(int type, const unsigned char *cdh_ptr, size_t cdh_len,
+ const char *rp_id, const char *rp_name, const unsigned char *authdata_ptr,
+ size_t authdata_len, const unsigned char *authdata_raw_ptr,
+ size_t authdata_raw_len, int ext, uint8_t rk, uint8_t uv,
+ const unsigned char *x5c_ptr, size_t x5c_len, const unsigned char *sig_ptr,
+ size_t sig_len, const char *fmt, int prot)
+{
+ fido_cred_t *cred;
+ uint8_t flags;
+ uint32_t sigcount;
+
+ if ((cred = fido_cred_new()) == NULL)
+ return;
+
+ fido_cred_set_type(cred, type);
+ fido_cred_set_clientdata_hash(cred, cdh_ptr, cdh_len);
+ fido_cred_set_rp(cred, rp_id, rp_name);
+ consume(authdata_ptr, authdata_len);
+ consume(authdata_raw_ptr, authdata_raw_len);
+ if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK)
+ fido_cred_set_authdata_raw(cred, authdata_raw_ptr,
+ authdata_raw_len);
+ fido_cred_set_extensions(cred, ext);
+ fido_cred_set_x509(cred, x5c_ptr, x5c_len);
+ fido_cred_set_sig(cred, sig_ptr, sig_len);
+ fido_cred_set_prot(cred, prot);
+
+ if (rk & 1)
+ fido_cred_set_rk(cred, FIDO_OPT_TRUE);
+ if (uv & 1)
+ fido_cred_set_uv(cred, FIDO_OPT_TRUE);
+ if (fmt)
+ fido_cred_set_fmt(cred, fmt);
+
+ /* repeat memory operations to trigger reallocation paths */
+ if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK)
+ fido_cred_set_authdata_raw(cred, authdata_ptr, authdata_len);
+ fido_cred_set_x509(cred, x5c_ptr, x5c_len);
+ fido_cred_set_sig(cred, sig_ptr, sig_len);
+
+ assert(fido_cred_verify(cred) != FIDO_OK);
+ assert(fido_cred_verify_self(cred) != FIDO_OK);
+
+ consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred));
+ consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
+ consume(fido_cred_aaguid_ptr(cred), fido_cred_aaguid_len(cred));
+ consume(fido_cred_user_id_ptr(cred), fido_cred_user_id_len(cred));
+ consume_str(fido_cred_user_name(cred));
+ consume_str(fido_cred_display_name(cred));
+ consume(fido_cred_largeblob_key_ptr(cred),
+ fido_cred_largeblob_key_len(cred));
+
+ flags = fido_cred_flags(cred);
+ consume(&flags, sizeof(flags));
+ sigcount = fido_cred_sigcount(cred);
+ consume(&sigcount, sizeof(sigcount));
+ type = fido_cred_type(cred);
+ consume(&type, sizeof(type));
+
+ fido_cred_free(&cred);
+}
+
+static void
+test_cred(const struct param *p)
+{
+ fido_cred_t *cred = NULL;
+ int cose_alg = 0;
+
+ if ((cred = fido_cred_new()) == NULL)
+ return;
+
+ switch (p->type & 3) {
+ case 0:
+ cose_alg = COSE_ES256;
+ break;
+ case 1:
+ cose_alg = COSE_RS256;
+ break;
+ default:
+ cose_alg = COSE_EDDSA;
+ break;
+ }
+
+ set_wire_data(p->wire_data.body, p->wire_data.len);
+
+ make_cred(cred, p->opt, cose_alg, &p->cdh, p->rp_id, p->rp_name,
+ &p->user_id, p->user_name, p->user_nick, p->user_icon, p->ext,
+ p->rk, p->uv, p->pin, p->excl_count, &p->excl_cred);
+
+ verify_cred(cose_alg,
+ fido_cred_clientdata_hash_ptr(cred),
+ fido_cred_clientdata_hash_len(cred), fido_cred_rp_id(cred),
+ fido_cred_rp_name(cred), fido_cred_authdata_ptr(cred),
+ fido_cred_authdata_len(cred), fido_cred_authdata_raw_ptr(cred),
+ fido_cred_authdata_raw_len(cred), p->ext, p->rk, p->uv,
+ fido_cred_x5c_ptr(cred), fido_cred_x5c_len(cred),
+ fido_cred_sig_ptr(cred), fido_cred_sig_len(cred),
+ fido_cred_fmt(cred), fido_cred_prot(cred));
+
+ fido_cred_free(&cred);
+}
+
+static void
+test_touch(const struct param *p)
+{
+ fido_dev_t *dev;
+ int r;
+ int touched;
+
+ set_wire_data(p->wire_data.body, p->wire_data.len);
+
+ if ((dev = open_dev(p->opt & 2)) == NULL)
+ return;
+ if (p->opt & 1)
+ fido_dev_force_u2f(dev);
+
+ r = fido_dev_get_touch_begin(dev);
+ consume_str(fido_strerr(r));
+ r = fido_dev_get_touch_status(dev, &touched, -1);
+ consume_str(fido_strerr(r));
+ consume(&touched, sizeof(touched));
+
+ fido_dev_cancel(dev);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+test_misc(const struct param *p)
+{
+ fido_cred_t *cred = NULL;
+
+ if ((cred = fido_cred_new()) == NULL)
+ return;
+
+ /* reuse user id as credential id */
+ fido_cred_set_id(cred, p->user_id.body, p->user_id.len);
+ consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
+ fido_cred_free(&cred);
+}
+
+void
+test(const struct param *p)
+{
+ prng_init((unsigned int)p->seed);
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ test_cred(p);
+ test_touch(p);
+ test_misc(p);
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM) {
+ mutate_byte(&p->rk);
+ mutate_byte(&p->type);
+ mutate_byte(&p->opt);
+ mutate_byte(&p->uv);
+ mutate_byte(&p->excl_count);
+ mutate_int(&p->ext);
+ mutate_blob(&p->cdh);
+ mutate_blob(&p->user_id);
+ mutate_blob(&p->excl_cred);
+ mutate_string(p->pin);
+ mutate_string(p->user_icon);
+ mutate_string(p->user_name);
+ mutate_string(p->user_nick);
+ mutate_string(p->rp_id);
+ mutate_string(p->rp_name);
+ }
+
+ if (flags & MUTATE_WIREDATA) {
+ if (p->opt & 1) {
+ p->wire_data.len = sizeof(dummy_wire_data_u2f);
+ memcpy(&p->wire_data.body, &dummy_wire_data_u2f,
+ p->wire_data.len);
+ } else {
+ p->wire_data.len = sizeof(dummy_wire_data_fido);
+ memcpy(&p->wire_data.body, &dummy_wire_data_fido,
+ p->wire_data.len);
+ }
+ mutate_blob(&p->wire_data);
+ }
+}
diff --git a/fuzz/fuzz_credman.c b/fuzz/fuzz_credman.c
new file mode 100644
index 000000000000..89a37379d87f
--- /dev/null
+++ b/fuzz/fuzz_credman.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2019-2021 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mutator_aux.h"
+#include "wiredata_fido2.h"
+#include "dummy.h"
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+/* Parameter set defining a FIDO2 credential management operation. */
+struct param {
+ char pin[MAXSTR];
+ char rp_id[MAXSTR];
+ int seed;
+ struct blob cred_id;
+ struct blob del_wire_data;
+ struct blob meta_wire_data;
+ struct blob rk_wire_data;
+ struct blob rp_wire_data;
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'getCredsMetadata' credential management command.
+ */
+static const uint8_t dummy_meta_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_CREDMAN_META,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'enumerateRPsBegin' credential management command.
+ */
+static const uint8_t dummy_rp_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_CREDMAN_RPLIST,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'enumerateCredentialsBegin' credential management command.
+ */
+static const uint8_t dummy_rk_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_CREDMAN_RKLIST,
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'deleteCredential' credential management command.
+ */
+static const uint8_t dummy_del_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_STATUS,
+};
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 8 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_int(v[0], &p->seed) < 0 ||
+ unpack_string(v[1], p->pin) < 0 ||
+ unpack_string(v[2], p->rp_id) < 0 ||
+ unpack_blob(v[3], &p->cred_id) < 0 ||
+ unpack_blob(v[4], &p->meta_wire_data) < 0 ||
+ unpack_blob(v[5], &p->rp_wire_data) < 0 ||
+ unpack_blob(v[6], &p->rk_wire_data) < 0 ||
+ unpack_blob(v[7], &p->del_wire_data) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[8], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(8)) == NULL ||
+ (argv[0] = pack_int(p->seed)) == NULL ||
+ (argv[1] = pack_string(p->pin)) == NULL ||
+ (argv[2] = pack_string(p->rp_id)) == NULL ||
+ (argv[3] = pack_blob(&p->cred_id)) == NULL ||
+ (argv[4] = pack_blob(&p->meta_wire_data)) == NULL ||
+ (argv[5] = pack_blob(&p->rp_wire_data)) == NULL ||
+ (argv[6] = pack_blob(&p->rk_wire_data)) == NULL ||
+ (argv[7] = pack_blob(&p->del_wire_data)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 8; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 8; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
+ strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
+
+ dummy.meta_wire_data.len = sizeof(dummy_meta_wire_data);
+ dummy.rp_wire_data.len = sizeof(dummy_rp_wire_data);
+ dummy.rk_wire_data.len = sizeof(dummy_rk_wire_data);
+ dummy.del_wire_data.len = sizeof(dummy_del_wire_data);
+ dummy.cred_id.len = sizeof(dummy_cred_id);
+
+ memcpy(&dummy.meta_wire_data.body, &dummy_meta_wire_data,
+ dummy.meta_wire_data.len);
+ memcpy(&dummy.rp_wire_data.body, &dummy_rp_wire_data,
+ dummy.rp_wire_data.len);
+ memcpy(&dummy.rk_wire_data.body, &dummy_rk_wire_data,
+ dummy.rk_wire_data.len);
+ memcpy(&dummy.del_wire_data.body, &dummy_del_wire_data,
+ dummy.del_wire_data.len);
+ memcpy(&dummy.cred_id.body, &dummy_cred_id, dummy.cred_id.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+
+ if (blob_len > len) {
+ memcpy(ptr, blob, len);
+ return len;
+ }
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+static fido_dev_t *
+prepare_dev(void)
+{
+ fido_dev_t *dev;
+ bool x;
+
+ if ((dev = open_dev(0)) == NULL)
+ return NULL;
+
+ x = fido_dev_is_fido2(dev);
+ consume(&x, sizeof(x));
+ x = fido_dev_supports_cred_prot(dev);
+ consume(&x, sizeof(x));
+ x = fido_dev_supports_credman(dev);
+ consume(&x, sizeof(x));
+
+ return dev;
+}
+
+static void
+get_metadata(const struct param *p)
+{
+ fido_dev_t *dev;
+ fido_credman_metadata_t *metadata;
+ uint64_t existing;
+ uint64_t remaining;
+
+ set_wire_data(p->meta_wire_data.body, p->meta_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL)
+ return;
+
+ if ((metadata = fido_credman_metadata_new()) == NULL) {
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+ return;
+ }
+
+ fido_credman_get_dev_metadata(dev, metadata, p->pin);
+
+ existing = fido_credman_rk_existing(metadata);
+ remaining = fido_credman_rk_remaining(metadata);
+ consume(&existing, sizeof(existing));
+ consume(&remaining, sizeof(remaining));
+
+ fido_credman_metadata_free(&metadata);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+get_rp_list(const struct param *p)
+{
+ fido_dev_t *dev;
+ fido_credman_rp_t *rp;
+
+ set_wire_data(p->rp_wire_data.body, p->rp_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL)
+ return;
+
+ if ((rp = fido_credman_rp_new()) == NULL) {
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+ return;
+ }
+
+ fido_credman_get_dev_rp(dev, rp, p->pin);
+
+ /* +1 on purpose */
+ for (size_t i = 0; i < fido_credman_rp_count(rp) + 1; i++) {
+ consume(fido_credman_rp_id_hash_ptr(rp, i),
+ fido_credman_rp_id_hash_len(rp, i));
+ consume_str(fido_credman_rp_id(rp, i));
+ consume_str(fido_credman_rp_name(rp, i));
+ }
+
+ fido_credman_rp_free(&rp);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+get_rk_list(const struct param *p)
+{
+ fido_dev_t *dev;
+ fido_credman_rk_t *rk;
+ const fido_cred_t *cred;
+ int val;
+
+ set_wire_data(p->rk_wire_data.body, p->rk_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL)
+ return;
+
+ if ((rk = fido_credman_rk_new()) == NULL) {
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+ return;
+ }
+
+ fido_credman_get_dev_rk(dev, p->rp_id, rk, p->pin);
+
+ /* +1 on purpose */
+ for (size_t i = 0; i < fido_credman_rk_count(rk) + 1; i++) {
+ if ((cred = fido_credman_rk(rk, i)) == NULL) {
+ assert(i >= fido_credman_rk_count(rk));
+ continue;
+ }
+ val = fido_cred_type(cred);
+ consume(&val, sizeof(val));
+ consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
+ consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred));
+ consume(fido_cred_user_id_ptr(cred),
+ fido_cred_user_id_len(cred));
+ consume_str(fido_cred_user_name(cred));
+ consume_str(fido_cred_display_name(cred));
+ val = fido_cred_prot(cred);
+ consume(&val, sizeof(val));
+ }
+
+ fido_credman_rk_free(&rk);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+del_rk(const struct param *p)
+{
+ fido_dev_t *dev;
+
+ set_wire_data(p->del_wire_data.body, p->del_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL)
+ return;
+
+ fido_credman_del_dev_rk(dev, p->cred_id.body, p->cred_id.len, p->pin);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+set_rk(const struct param *p)
+{
+ fido_dev_t *dev = NULL;
+ fido_cred_t *cred = NULL;
+ const char *pin = p->pin;
+ int r0, r1, r2;
+
+ set_wire_data(p->del_wire_data.body, p->del_wire_data.len);
+
+ if ((dev = prepare_dev()) == NULL)
+ return;
+ if ((cred = fido_cred_new()) == NULL)
+ goto out;
+ r0 = fido_cred_set_id(cred, p->cred_id.body, p->cred_id.len);
+ r1 = fido_cred_set_user(cred, p->cred_id.body, p->cred_id.len, p->rp_id,
+ NULL, NULL);
+ if (strlen(pin) == 0)
+ pin = NULL;
+ r2 = fido_credman_set_dev_rk(dev, cred, pin);
+ consume(&r0, sizeof(r0));
+ consume(&r1, sizeof(r1));
+ consume(&r2, sizeof(r2));
+out:
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+ fido_cred_free(&cred);
+}
+
+void
+test(const struct param *p)
+{
+ prng_init((unsigned int)p->seed);
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ get_metadata(p);
+ get_rp_list(p);
+ get_rk_list(p);
+ del_rk(p);
+ set_rk(p);
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM) {
+ mutate_blob(&p->cred_id);
+ mutate_string(p->pin);
+ mutate_string(p->rp_id);
+ }
+
+ if (flags & MUTATE_WIREDATA) {
+ mutate_blob(&p->meta_wire_data);
+ mutate_blob(&p->rp_wire_data);
+ mutate_blob(&p->rk_wire_data);
+ mutate_blob(&p->del_wire_data);
+ }
+}
diff --git a/fuzz/fuzz_hid.c b/fuzz/fuzz_hid.c
new file mode 100644
index 000000000000..6aca7ef5da5b
--- /dev/null
+++ b/fuzz/fuzz_hid.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../openbsd-compat/openbsd-compat.h"
+#include "mutator_aux.h"
+
+extern int fido_hid_get_usage(const uint8_t *, size_t, uint32_t *);
+extern int fido_hid_get_report_len(const uint8_t *, size_t, size_t *, size_t *);
+extern void set_udev_parameters(const char *, const struct blob *);
+
+struct param {
+ int seed;
+ char uevent[MAXSTR];
+ struct blob report_descriptor;
+};
+
+/*
+ * Sample HID report descriptor from the FIDO HID interface of a YubiKey 5.
+ */
+static const uint8_t dummy_report_descriptor[] = {
+ 0x06, 0xd0, 0xf1, 0x09, 0x01, 0xa1, 0x01, 0x09,
+ 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08,
+ 0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00,
+ 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x40, 0x91,
+ 0x02, 0xc0
+};
+
+/*
+ * Sample uevent file from a Yubico Security Key.
+ */
+static const char dummy_uevent[] =
+ "DRIVER=hid-generic\n"
+ "HID_ID=0003:00001050:00000120\n"
+ "HID_NAME=Yubico Security Key by Yubico\n"
+ "HID_PHYS=usb-0000:00:14.0-3/input0\n"
+ "HID_UNIQ=\n"
+ "MODALIAS=hid:b0003g0001v00001050p00000120\n";
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 3 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_int(v[0], &p->seed) < 0 ||
+ unpack_string(v[1], p->uevent) < 0 ||
+ unpack_blob(v[2], &p->report_descriptor) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[3], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(3)) == NULL ||
+ (argv[0] = pack_int(p->seed)) == NULL ||
+ (argv[1] = pack_string(p->uevent)) == NULL ||
+ (argv[2] = pack_blob(&p->report_descriptor)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 3; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 3; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ dummy.report_descriptor.len = sizeof(dummy_report_descriptor);
+ strlcpy(dummy.uevent, dummy_uevent, sizeof(dummy.uevent));
+ memcpy(&dummy.report_descriptor.body, &dummy_report_descriptor,
+ dummy.report_descriptor.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+ if (blob_len > len)
+ blob_len = len;
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+static void
+get_usage(const struct param *p)
+{
+ uint32_t usage_page = 0;
+
+ fido_hid_get_usage(p->report_descriptor.body, p->report_descriptor.len,
+ &usage_page);
+ consume(&usage_page, sizeof(usage_page));
+}
+
+static void
+get_report_len(const struct param *p)
+{
+ size_t report_in_len = 0;
+ size_t report_out_len = 0;
+
+ fido_hid_get_report_len(p->report_descriptor.body,
+ p->report_descriptor.len, &report_in_len, &report_out_len);
+ consume(&report_in_len, sizeof(report_in_len));
+ consume(&report_out_len, sizeof(report_out_len));
+}
+
+static void
+manifest(const struct param *p)
+{
+ size_t ndevs, nfound;
+ fido_dev_info_t *devlist;
+ int16_t vendor_id, product_id;
+
+ set_udev_parameters(p->uevent, &p->report_descriptor);
+ ndevs = uniform_random(64);
+ if ((devlist = fido_dev_info_new(ndevs)) == NULL ||
+ fido_dev_info_manifest(devlist, ndevs, &nfound) != FIDO_OK)
+ goto out;
+ for (size_t i = 0; i < nfound; i++) {
+ const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
+ consume_str(fido_dev_info_path(di));
+ consume_str(fido_dev_info_manufacturer_string(di));
+ consume_str(fido_dev_info_product_string(di));
+ vendor_id = fido_dev_info_vendor(di);
+ product_id = fido_dev_info_product(di);
+ consume(&vendor_id, sizeof(vendor_id));
+ consume(&product_id, sizeof(product_id));
+ }
+out:
+ fido_dev_info_free(&devlist, ndevs);
+}
+
+void
+test(const struct param *p)
+{
+ prng_init((unsigned int)p->seed);
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ get_usage(p);
+ get_report_len(p);
+ manifest(p);
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM) {
+ mutate_blob(&p->report_descriptor);
+ mutate_string(p->uevent);
+ }
+}
diff --git a/fuzz/fuzz_largeblob.c b/fuzz/fuzz_largeblob.c
new file mode 100644
index 000000000000..6886261bf529
--- /dev/null
+++ b/fuzz/fuzz_largeblob.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mutator_aux.h"
+#include "wiredata_fido2.h"
+#include "dummy.h"
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+/* Parameter set defining a FIDO2 "large blob" operation. */
+struct param {
+ char pin[MAXSTR];
+ int seed;
+ struct blob key;
+ struct blob get_wiredata;
+ struct blob set_wiredata;
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'authenticatorLargeBlobs' 'get' command.
+ */
+static const uint8_t dummy_get_wiredata[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY
+};
+
+/*
+ * Collection of HID reports from an authenticator issued with a FIDO2
+ * 'authenticatorLargeBlobs' 'set' command.
+ */
+static const uint8_t dummy_set_wiredata[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_PINTOKEN,
+ WIREDATA_CTAP_CBOR_STATUS
+};
+
+/*
+ * XXX this needs to match the encrypted blob embedded in
+ * WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY.
+ */
+static const uint8_t dummy_key[] = {
+ 0xa9, 0x1b, 0xc4, 0xdd, 0xfc, 0x9a, 0x93, 0x79,
+ 0x75, 0xba, 0xf7, 0x7f, 0x4d, 0x57, 0xfc, 0xa6,
+ 0xe1, 0xf8, 0x06, 0x43, 0x23, 0x99, 0x51, 0x32,
+ 0xce, 0x6e, 0x19, 0x84, 0x50, 0x13, 0x2d, 0x7b
+};
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 5 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_int(v[0], &p->seed) < 0 ||
+ unpack_string(v[1], p->pin) < 0 ||
+ unpack_blob(v[2], &p->key) < 0 ||
+ unpack_blob(v[3], &p->get_wiredata) < 0 ||
+ unpack_blob(v[4], &p->set_wiredata) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[5], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(5)) == NULL ||
+ (argv[0] = pack_int(p->seed)) == NULL ||
+ (argv[1] = pack_string(p->pin)) == NULL ||
+ (argv[2] = pack_blob(&p->key)) == NULL ||
+ (argv[3] = pack_blob(&p->get_wiredata)) == NULL ||
+ (argv[4] = pack_blob(&p->set_wiredata)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 5; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 5; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
+
+ dummy.get_wiredata.len = sizeof(dummy_get_wiredata);
+ dummy.set_wiredata.len = sizeof(dummy_set_wiredata);
+ dummy.key.len = sizeof(dummy_key);
+
+ memcpy(&dummy.get_wiredata.body, &dummy_get_wiredata,
+ dummy.get_wiredata.len);
+ memcpy(&dummy.set_wiredata.body, &dummy_set_wiredata,
+ dummy.set_wiredata.len);
+ memcpy(&dummy.key.body, &dummy_key, dummy.key.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+
+ if (blob_len > len) {
+ memcpy(ptr, blob, len);
+ return len;
+ }
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+static fido_dev_t *
+prepare_dev(void)
+{
+ fido_dev_t *dev;
+
+ if ((dev = open_dev(0)) == NULL)
+ return NULL;
+
+ return dev;
+}
+
+static void
+get_blob(const struct param *p, int array)
+{
+ fido_dev_t *dev;
+ u_char *ptr = NULL;
+ size_t len = 0;
+
+ set_wire_data(p->get_wiredata.body, p->get_wiredata.len);
+
+ if ((dev = prepare_dev()) == NULL)
+ return;
+
+ if (array)
+ fido_dev_largeblob_get_array(dev, &ptr, &len);
+ else
+ fido_dev_largeblob_get(dev, p->key.body, p->key.len, &ptr, &len);
+ consume(ptr, len);
+ free(ptr);
+
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+
+static void
+set_blob(const struct param *p, int op)
+{
+ fido_dev_t *dev;
+ const char *pin;
+
+ set_wire_data(p->set_wiredata.body, p->set_wiredata.len);
+
+ if ((dev = prepare_dev()) == NULL)
+ return;
+ pin = p->pin;
+ if (strlen(pin) == 0)
+ pin = NULL;
+
+ switch (op) {
+ case 0:
+ fido_dev_largeblob_remove(dev, p->key.body, p->key.len, pin);
+ break;
+ case 1:
+ /* XXX reuse p->get_wiredata as the blob to be set */
+ fido_dev_largeblob_set(dev, p->key.body, p->key.len,
+ p->get_wiredata.body, p->get_wiredata.len, pin);
+ break;
+ case 2:
+ /* XXX reuse p->get_wiredata as the body of the cbor array */
+ fido_dev_largeblob_set_array(dev, p->get_wiredata.body,
+ p->get_wiredata.len, pin);
+ }
+
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+void
+test(const struct param *p)
+{
+ prng_init((unsigned int)p->seed);
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ get_blob(p, 0);
+ get_blob(p, 1);
+ set_blob(p, 0);
+ set_blob(p, 1);
+ set_blob(p, 2);
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM) {
+ mutate_blob(&p->key);
+ mutate_string(p->pin);
+ }
+
+ if (flags & MUTATE_WIREDATA) {
+ mutate_blob(&p->get_wiredata);
+ mutate_blob(&p->set_wiredata);
+ }
+}
diff --git a/fuzz/fuzz_mgmt.c b/fuzz/fuzz_mgmt.c
new file mode 100644
index 000000000000..28afbc6aae5f
--- /dev/null
+++ b/fuzz/fuzz_mgmt.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mutator_aux.h"
+#include "wiredata_fido2.h"
+#include "dummy.h"
+
+#include "../openbsd-compat/openbsd-compat.h"
+
+struct param {
+ char pin1[MAXSTR];
+ char pin2[MAXSTR];
+ struct blob reset_wire_data;
+ struct blob info_wire_data;
+ struct blob set_pin_wire_data;
+ struct blob change_pin_wire_data;
+ struct blob retry_wire_data;
+ struct blob config_wire_data;
+ int seed;
+};
+
+static const uint8_t dummy_reset_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_KEEPALIVE,
+ WIREDATA_CTAP_KEEPALIVE,
+ WIREDATA_CTAP_KEEPALIVE,
+ WIREDATA_CTAP_CBOR_STATUS,
+};
+
+static const uint8_t dummy_info_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_INFO,
+};
+
+static const uint8_t dummy_set_pin_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_STATUS,
+};
+
+static const uint8_t dummy_change_pin_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_AUTHKEY,
+ WIREDATA_CTAP_CBOR_STATUS,
+};
+
+static const uint8_t dummy_retry_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_RETRIES,
+};
+
+static const uint8_t dummy_config_wire_data[] = {
+ WIREDATA_CTAP_INIT,
+ WIREDATA_CTAP_CBOR_INFO,
+ WIREDATA_CTAP_CBOR_STATUS,
+};
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 9 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_int(v[0], &p->seed) < 0 ||
+ unpack_string(v[1], p->pin1) < 0 ||
+ unpack_string(v[2], p->pin2) < 0 ||
+ unpack_blob(v[3], &p->reset_wire_data) < 0 ||
+ unpack_blob(v[4], &p->info_wire_data) < 0 ||
+ unpack_blob(v[5], &p->set_pin_wire_data) < 0 ||
+ unpack_blob(v[6], &p->change_pin_wire_data) < 0 ||
+ unpack_blob(v[7], &p->retry_wire_data) < 0 ||
+ unpack_blob(v[8], &p->config_wire_data) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[9], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(9)) == NULL ||
+ (argv[0] = pack_int(p->seed)) == NULL ||
+ (argv[1] = pack_string(p->pin1)) == NULL ||
+ (argv[2] = pack_string(p->pin2)) == NULL ||
+ (argv[3] = pack_blob(&p->reset_wire_data)) == NULL ||
+ (argv[4] = pack_blob(&p->info_wire_data)) == NULL ||
+ (argv[5] = pack_blob(&p->set_pin_wire_data)) == NULL ||
+ (argv[6] = pack_blob(&p->change_pin_wire_data)) == NULL ||
+ (argv[7] = pack_blob(&p->retry_wire_data)) == NULL ||
+ (argv[8] = pack_blob(&p->config_wire_data)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 9; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 9; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ strlcpy(dummy.pin1, dummy_pin1, sizeof(dummy.pin1));
+ strlcpy(dummy.pin2, dummy_pin2, sizeof(dummy.pin2));
+
+ dummy.reset_wire_data.len = sizeof(dummy_reset_wire_data);
+ dummy.info_wire_data.len = sizeof(dummy_info_wire_data);
+ dummy.set_pin_wire_data.len = sizeof(dummy_set_pin_wire_data);
+ dummy.change_pin_wire_data.len = sizeof(dummy_change_pin_wire_data);
+ dummy.retry_wire_data.len = sizeof(dummy_retry_wire_data);
+ dummy.config_wire_data.len = sizeof(dummy_config_wire_data);
+
+ memcpy(&dummy.reset_wire_data.body, &dummy_reset_wire_data,
+ dummy.reset_wire_data.len);
+ memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data,
+ dummy.info_wire_data.len);
+ memcpy(&dummy.set_pin_wire_data.body, &dummy_set_pin_wire_data,
+ dummy.set_pin_wire_data.len);
+ memcpy(&dummy.change_pin_wire_data.body, &dummy_change_pin_wire_data,
+ dummy.change_pin_wire_data.len);
+ memcpy(&dummy.retry_wire_data.body, &dummy_retry_wire_data,
+ dummy.retry_wire_data.len);
+ memcpy(&dummy.config_wire_data.body, &dummy_config_wire_data,
+ dummy.config_wire_data.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+
+ if (blob_len > len) {
+ memcpy(ptr, blob, len);
+ return len;
+ }
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+static void
+dev_reset(const struct param *p)
+{
+ fido_dev_t *dev;
+
+ set_wire_data(p->reset_wire_data.body, p->reset_wire_data.len);
+
+ if ((dev = open_dev(0)) == NULL)
+ return;
+
+ fido_dev_reset(dev);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+dev_get_cbor_info(const struct param *p)
+{
+ fido_dev_t *dev;
+ fido_cbor_info_t *ci;
+ uint64_t n;
+ uint8_t proto, major, minor, build, flags;
+
+ set_wire_data(p->info_wire_data.body, p->info_wire_data.len);
+
+ if ((dev = open_dev(0)) == NULL)
+ return;
+
+ proto = fido_dev_protocol(dev);
+ major = fido_dev_major(dev);
+ minor = fido_dev_minor(dev);
+ build = fido_dev_build(dev);
+ flags = fido_dev_flags(dev);
+
+ consume(&proto, sizeof(proto));
+ consume(&major, sizeof(major));
+ consume(&minor, sizeof(minor));
+ consume(&build, sizeof(build));
+ consume(&flags, sizeof(flags));
+
+ if ((ci = fido_cbor_info_new()) == NULL)
+ goto out;
+
+ fido_dev_get_cbor_info(dev, ci);
+
+ for (size_t i = 0; i < fido_cbor_info_versions_len(ci); i++) {
+ char * const *sa = fido_cbor_info_versions_ptr(ci);
+ consume(sa[i], strlen(sa[i]));
+ }
+
+ for (size_t i = 0; i < fido_cbor_info_extensions_len(ci); i++) {
+ char * const *sa = fido_cbor_info_extensions_ptr(ci);
+ consume(sa[i], strlen(sa[i]));
+ }
+
+ for (size_t i = 0; i < fido_cbor_info_transports_len(ci); i++) {
+ char * const *sa = fido_cbor_info_transports_ptr(ci);
+ consume(sa[i], strlen(sa[i]));
+ }
+
+ for (size_t i = 0; i < fido_cbor_info_options_len(ci); i++) {
+ char * const *sa = fido_cbor_info_options_name_ptr(ci);
+ const bool *va = fido_cbor_info_options_value_ptr(ci);
+ consume(sa[i], strlen(sa[i]));
+ consume(&va[i], sizeof(va[i]));
+ }
+
+ /* +1 on purpose */
+ for (size_t i = 0; i <= fido_cbor_info_algorithm_count(ci); i++) {
+ const char *type = fido_cbor_info_algorithm_type(ci, i);
+ int cose = fido_cbor_info_algorithm_cose(ci, i);
+ consume_str(type);
+ consume(&cose, sizeof(cose));
+ }
+
+ n = fido_cbor_info_maxmsgsiz(ci);
+ consume(&n, sizeof(n));
+
+ n = fido_cbor_info_maxcredbloblen(ci);
+ consume(&n, sizeof(n));
+
+ n = fido_cbor_info_maxcredcntlst(ci);
+ consume(&n, sizeof(n));
+
+ n = fido_cbor_info_maxcredidlen(ci);
+ consume(&n, sizeof(n));
+
+ n = fido_cbor_info_fwversion(ci);
+ consume(&n, sizeof(n));
+
+ consume(fido_cbor_info_aaguid_ptr(ci), fido_cbor_info_aaguid_len(ci));
+ consume(fido_cbor_info_protocols_ptr(ci),
+ fido_cbor_info_protocols_len(ci));
+
+out:
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+
+ fido_cbor_info_free(&ci);
+}
+
+static void
+dev_set_pin(const struct param *p)
+{
+ fido_dev_t *dev;
+
+ set_wire_data(p->set_pin_wire_data.body, p->set_pin_wire_data.len);
+
+ if ((dev = open_dev(0)) == NULL)
+ return;
+
+ fido_dev_set_pin(dev, p->pin1, NULL);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+dev_change_pin(const struct param *p)
+{
+ fido_dev_t *dev;
+
+ set_wire_data(p->change_pin_wire_data.body, p->change_pin_wire_data.len);
+
+ if ((dev = open_dev(0)) == NULL)
+ return;
+
+ fido_dev_set_pin(dev, p->pin2, p->pin1);
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+dev_get_retry_count(const struct param *p)
+{
+ fido_dev_t *dev;
+ int n = 0;
+
+ set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len);
+
+ if ((dev = open_dev(0)) == NULL)
+ return;
+
+ fido_dev_get_retry_count(dev, &n);
+ consume(&n, sizeof(n));
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+dev_get_uv_retry_count(const struct param *p)
+{
+ fido_dev_t *dev;
+ int n = 0;
+
+ set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len);
+
+ if ((dev = open_dev(0)) == NULL)
+ return;
+
+ fido_dev_get_uv_retry_count(dev, &n);
+ consume(&n, sizeof(n));
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+dev_enable_entattest(const struct param *p)
+{
+ fido_dev_t *dev;
+ const char *pin;
+ int r;
+
+ set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
+ if ((dev = open_dev(0)) == NULL)
+ return;
+ pin = p->pin1;
+ if (strlen(pin) == 0)
+ pin = NULL;
+ r = fido_dev_enable_entattest(dev, pin);
+ consume_str(fido_strerr(r));
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+dev_toggle_always_uv(const struct param *p)
+{
+ fido_dev_t *dev;
+ const char *pin;
+ int r;
+
+ set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
+ if ((dev = open_dev(0)) == NULL)
+ return;
+ pin = p->pin1;
+ if (strlen(pin) == 0)
+ pin = NULL;
+ r = fido_dev_toggle_always_uv(dev, pin);
+ consume_str(fido_strerr(r));
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+dev_force_pin_change(const struct param *p)
+{
+ fido_dev_t *dev;
+ const char *pin;
+ int r;
+
+ set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
+ if ((dev = open_dev(0)) == NULL)
+ return;
+ pin = p->pin1;
+ if (strlen(pin) == 0)
+ pin = NULL;
+ r = fido_dev_force_pin_change(dev, pin);
+ consume_str(fido_strerr(r));
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+static void
+dev_set_pin_minlen(const struct param *p)
+{
+ fido_dev_t *dev;
+ const char *pin;
+ int r;
+
+ set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
+ if ((dev = open_dev(0)) == NULL)
+ return;
+ pin = p->pin1;
+ if (strlen(pin) == 0)
+ pin = NULL;
+ r = fido_dev_set_pin_minlen(dev, strlen(p->pin2), pin);
+ consume_str(fido_strerr(r));
+ fido_dev_close(dev);
+ fido_dev_free(&dev);
+}
+
+void
+test(const struct param *p)
+{
+ prng_init((unsigned int)p->seed);
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ dev_reset(p);
+ dev_get_cbor_info(p);
+ dev_set_pin(p);
+ dev_change_pin(p);
+ dev_get_retry_count(p);
+ dev_get_uv_retry_count(p);
+ dev_enable_entattest(p);
+ dev_toggle_always_uv(p);
+ dev_force_pin_change(p);
+ dev_set_pin_minlen(p);
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM) {
+ mutate_string(p->pin1);
+ mutate_string(p->pin2);
+ }
+
+ if (flags & MUTATE_WIREDATA) {
+ mutate_blob(&p->reset_wire_data);
+ mutate_blob(&p->info_wire_data);
+ mutate_blob(&p->set_pin_wire_data);
+ mutate_blob(&p->change_pin_wire_data);
+ mutate_blob(&p->retry_wire_data);
+ }
+}
diff --git a/fuzz/fuzz_netlink.c b/fuzz/fuzz_netlink.c
new file mode 100644
index 000000000000..9b7f930cde38
--- /dev/null
+++ b/fuzz/fuzz_netlink.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../openbsd-compat/openbsd-compat.h"
+#include "mutator_aux.h"
+
+struct param {
+ int seed;
+ int dev;
+ struct blob wiredata;
+};
+
+/*
+ * Sample netlink messages. These are unlikely to get the harness very far in
+ * terms of coverage, but serve to give libFuzzer a sense of the underlying
+ * structure.
+ */
+static const uint8_t sample_netlink_wiredata[] = {
+ 0xd8, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x9d, 0x2e, 0x00, 0x00,
+ 0x01, 0x02, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x6e, 0x66, 0x63, 0x00, 0x06, 0x00, 0x01, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00,
+ 0x1f, 0x00, 0x00, 0x00, 0x80, 0x01, 0x06, 0x00,
+ 0x14, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x05, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x06, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x07, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x08, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x09, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0a, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0c, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0e, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x10, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00,
+ 0x08, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x13, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x07, 0x00,
+ 0x18, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x01, 0x00,
+ 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x9d, 0x2e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x9d, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9d, 0x2e, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00,
+ 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x05, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x06, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x07, 0x00, 0x27, 0x00, 0x00, 0x00,
+ 0x93, 0xb9, 0x25, 0x00
+};
+
+struct param *
+unpack(const uint8_t *ptr, size_t len)
+{
+ cbor_item_t *item = NULL, **v;
+ struct cbor_load_result cbor;
+ struct param *p;
+ int ok = -1;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL ||
+ (item = cbor_load(ptr, len, &cbor)) == NULL ||
+ cbor.read != len ||
+ cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false ||
+ cbor_array_size(item) != 3 ||
+ (v = cbor_array_handle(item)) == NULL)
+ goto fail;
+
+ if (unpack_int(v[0], &p->seed) < 0 ||
+ unpack_int(v[1], &p->dev) < 0 ||
+ unpack_blob(v[2], &p->wiredata) < 0)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ free(p);
+ p = NULL;
+ }
+
+ if (item)
+ cbor_decref(&item);
+
+ return p;
+}
+
+size_t
+pack(uint8_t *ptr, size_t len, const struct param *p)
+{
+ cbor_item_t *argv[3], *array = NULL;
+ size_t cbor_alloc_len, cbor_len = 0;
+ unsigned char *cbor = NULL;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((array = cbor_new_definite_array(3)) == NULL ||
+ (argv[0] = pack_int(p->seed)) == NULL ||
+ (argv[1] = pack_int(p->dev)) == NULL ||
+ (argv[2] = pack_blob(&p->wiredata)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < 3; i++)
+ if (cbor_array_push(array, argv[i]) == false)
+ goto fail;
+
+ if ((cbor_len = cbor_serialize_alloc(array, &cbor,
+ &cbor_alloc_len)) > len) {
+ cbor_len = 0;
+ goto fail;
+ }
+
+ memcpy(ptr, cbor, cbor_len);
+fail:
+ for (size_t i = 0; i < 3; i++)
+ if (argv[i])
+ cbor_decref(&argv[i]);
+
+ if (array)
+ cbor_decref(&array);
+
+ free(cbor);
+
+ return cbor_len;
+}
+
+size_t
+pack_dummy(uint8_t *ptr, size_t len)
+{
+ struct param dummy;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&dummy, 0, sizeof(dummy));
+
+ dummy.wiredata.len = sizeof(sample_netlink_wiredata);
+ memcpy(&dummy.wiredata.body, &sample_netlink_wiredata,
+ dummy.wiredata.len);
+
+ assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
+
+ if (blob_len > len) {
+ memcpy(ptr, blob, len);
+ return len;
+ }
+
+ memcpy(ptr, blob, blob_len);
+
+ return blob_len;
+}
+
+void
+test(const struct param *p)
+{
+ fido_nl_t *nl;
+ uint32_t target;
+
+ prng_init((unsigned int)p->seed);
+ fido_init(FIDO_DEBUG);
+ fido_set_log_handler(consume_str);
+
+ set_netlink_io_functions(fd_read, fd_write);
+ set_wire_data(p->wiredata.body, p->wiredata.len);
+
+ if ((nl = fido_nl_new()) == NULL)
+ return;
+
+ consume(&nl->fd, sizeof(nl->fd));
+ consume(&nl->nfc_type, sizeof(nl->nfc_type));
+ consume(&nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp));
+ consume(&nl->saddr, sizeof(nl->saddr));
+
+ fido_nl_power_nfc(nl, (uint32_t)p->dev);
+
+ if (fido_nl_get_nfc_target(nl, (uint32_t)p->dev, &target) == 0)
+ consume(&target, sizeof(target));
+
+ fido_nl_free(&nl);
+}
+
+void
+mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
+{
+ if (flags & MUTATE_SEED)
+ p->seed = (int)seed;
+
+ if (flags & MUTATE_PARAM)
+ mutate_int(&p->dev);
+
+ if (flags & MUTATE_WIREDATA)
+ mutate_blob(&p->wiredata);
+}
diff --git a/fuzz/libfuzzer.c b/fuzz/libfuzzer.c
new file mode 100644
index 000000000000..09aec4ea2b68
--- /dev/null
+++ b/fuzz/libfuzzer.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mutator_aux.h"
+
+static bool debug;
+static unsigned int flags = MUTATE_ALL;
+static unsigned long long test_fail;
+static unsigned long long test_total;
+static unsigned long long mutate_fail;
+static unsigned long long mutate_total;
+
+int LLVMFuzzerInitialize(int *, char ***);
+int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
+size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int);
+
+static int
+save_seed(const char *opt)
+{
+ const char *path;
+ int fd = -1, status = 1;
+ void *buf = NULL;
+ const size_t buflen = 4096;
+ size_t n;
+ struct param *p = NULL;
+
+ if ((path = strchr(opt, '=')) == NULL || strlen(++path) == 0) {
+ warnx("usage: --fido-save-seed=<path>");
+ goto fail;
+ }
+
+ if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) {
+ warn("open %s", path);
+ goto fail;
+ }
+
+ if ((buf = malloc(buflen)) == NULL) {
+ warn("malloc");
+ goto fail;
+ }
+
+ n = pack_dummy(buf, buflen);
+
+ if ((p = unpack(buf, n)) == NULL) {
+ warnx("unpack");
+ goto fail;
+ }
+
+ if (write(fd, buf, n) != (ssize_t)n) {
+ warn("write %s", path);
+ goto fail;
+ }
+
+ status = 0;
+fail:
+ if (fd != -1)
+ close(fd);
+ free(buf);
+ free(p);
+
+ return status;
+}
+
+static void
+parse_mutate_flags(const char *opt, unsigned int *mutate_flags)
+{
+ const char *f;
+
+ if ((f = strchr(opt, '=')) == NULL || strlen(++f) == 0)
+ errx(1, "usage: --fido-mutate=<flag>");
+
+ if (strcmp(f, "seed") == 0)
+ *mutate_flags |= MUTATE_SEED;
+ else if (strcmp(f, "param") == 0)
+ *mutate_flags |= MUTATE_PARAM;
+ else if (strcmp(f, "wiredata") == 0)
+ *mutate_flags |= MUTATE_WIREDATA;
+ else
+ errx(1, "--fido-mutate: unknown flag '%s'", f);
+}
+
+int
+LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ unsigned int mutate_flags = 0;
+
+ for (int i = 0; i < *argc; i++)
+ if (strcmp((*argv)[i], "--fido-debug") == 0) {
+ debug = 1;
+ } else if (strncmp((*argv)[i], "--fido-save-seed=", 17) == 0) {
+ exit(save_seed((*argv)[i]));
+ } else if (strncmp((*argv)[i], "--fido-mutate=", 14) == 0) {
+ parse_mutate_flags((*argv)[i], &mutate_flags);
+ }
+
+ if (mutate_flags)
+ flags = mutate_flags;
+
+ return 0;
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct param *p;
+
+ if (size > 4096)
+ return 0;
+
+ if (++test_total % 100000 == 0 && debug) {
+ double r = (double)test_fail/(double)test_total * 100.0;
+ fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__,
+ test_fail, test_total, r);
+ }
+
+ if ((p = unpack(data, size)) == NULL)
+ test_fail++;
+ else {
+ test(p);
+ free(p);
+ }
+
+ return 0;
+}
+
+size_t
+LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize,
+ unsigned int seed) NO_MSAN
+{
+ struct param *p;
+ uint8_t blob[4096];
+ size_t blob_len;
+
+ memset(&p, 0, sizeof(p));
+
+#ifdef WITH_MSAN
+ __msan_unpoison(data, maxsize);
+#endif
+
+ if (++mutate_total % 100000 == 0 && debug) {
+ double r = (double)mutate_fail/(double)mutate_total * 100.0;
+ fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__,
+ mutate_fail, mutate_total, r);
+ }
+
+ if ((p = unpack(data, size)) == NULL) {
+ mutate_fail++;
+ return pack_dummy(data, maxsize);
+ }
+
+ mutate(p, seed, flags);
+
+ if ((blob_len = pack(blob, sizeof(blob), p)) == 0 ||
+ blob_len > sizeof(blob) || blob_len > maxsize) {
+ mutate_fail++;
+ free(p);
+ return 0;
+ }
+
+ free(p);
+
+ memcpy(data, blob, blob_len);
+
+ return blob_len;
+}
diff --git a/fuzz/mutator_aux.c b/fuzz/mutator_aux.c
new file mode 100644
index 000000000000..0dc3ae1bf054
--- /dev/null
+++ b/fuzz/mutator_aux.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <cbor.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mutator_aux.h"
+
+#define HID_DEV_HANDLE 0x68696421
+#define NFC_DEV_HANDLE 0x6e666321
+
+int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int);
+int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t);
+size_t LLVMFuzzerMutate(uint8_t *, size_t, size_t);
+
+static const uint8_t *wire_data_ptr = NULL;
+static size_t wire_data_len = 0;
+
+void
+consume(const void *body, size_t len)
+{
+ const volatile uint8_t *ptr = body;
+ volatile uint8_t x = 0;
+
+#ifdef WITH_MSAN
+ __msan_check_mem_is_initialized(body, len);
+#endif
+
+ while (len--)
+ x ^= *ptr++;
+}
+
+void
+consume_str(const char *str)
+{
+ if (str != NULL)
+ consume(str, strlen(str) + 1);
+}
+
+int
+unpack_int(cbor_item_t *item, int *v)
+{
+ if (cbor_is_int(item) == false ||
+ cbor_int_get_width(item) != CBOR_INT_64)
+ return -1;
+
+ if (cbor_isa_uint(item))
+ *v = (int)cbor_get_uint64(item);
+ else
+ *v = (int)(-cbor_get_uint64(item) - 1);
+
+ return 0;
+}
+
+int
+unpack_string(cbor_item_t *item, char *v)
+{
+ size_t len;
+
+ if (cbor_isa_bytestring(item) == false ||
+ (len = cbor_bytestring_length(item)) >= MAXSTR)
+ return -1;
+
+ memcpy(v, cbor_bytestring_handle(item), len);
+ v[len] = '\0';
+
+ return 0;
+}
+
+int
+unpack_byte(cbor_item_t *item, uint8_t *v)
+{
+ if (cbor_isa_uint(item) == false ||
+ cbor_int_get_width(item) != CBOR_INT_8)
+ return -1;
+
+ *v = cbor_get_uint8(item);
+
+ return 0;
+}
+
+int
+unpack_blob(cbor_item_t *item, struct blob *v)
+{
+ if (cbor_isa_bytestring(item) == false ||
+ (v->len = cbor_bytestring_length(item)) > sizeof(v->body))
+ return -1;
+
+ memcpy(v->body, cbor_bytestring_handle(item), v->len);
+
+ return 0;
+}
+
+cbor_item_t *
+pack_int(int v) NO_MSAN
+{
+ if (v < 0)
+ return cbor_build_negint64((uint64_t)(-(int64_t)v - 1));
+ else
+ return cbor_build_uint64((uint64_t)v);
+}
+
+cbor_item_t *
+pack_string(const char *v) NO_MSAN
+{
+ if (strlen(v) >= MAXSTR)
+ return NULL;
+
+ return cbor_build_bytestring((const unsigned char *)v, strlen(v));
+}
+
+cbor_item_t *
+pack_byte(uint8_t v) NO_MSAN
+{
+ return cbor_build_uint8(v);
+}
+
+cbor_item_t *
+pack_blob(const struct blob *v) NO_MSAN
+{
+ return cbor_build_bytestring(v->body, v->len);
+}
+
+void
+mutate_byte(uint8_t *b)
+{
+ LLVMFuzzerMutate(b, sizeof(*b), sizeof(*b));
+}
+
+void
+mutate_int(int *i)
+{
+ LLVMFuzzerMutate((uint8_t *)i, sizeof(*i), sizeof(*i));
+}
+
+void
+mutate_blob(struct blob *blob)
+{
+ blob->len = LLVMFuzzerMutate((uint8_t *)blob->body, blob->len,
+ sizeof(blob->body));
+}
+
+void
+mutate_string(char *s)
+{
+ size_t n;
+
+ n = LLVMFuzzerMutate((uint8_t *)s, strlen(s), MAXSTR - 1);
+ s[n] = '\0';
+}
+
+/* XXX should fail, but doesn't */
+static int
+buf_read(unsigned char *ptr, size_t len, int ms)
+{
+ size_t n;
+
+ (void)ms;
+
+ if (wire_data_len < len)
+ n = wire_data_len;
+ else
+ n = len;
+
+ memcpy(ptr, wire_data_ptr, n);
+
+ wire_data_ptr += n;
+ wire_data_len -= n;
+
+ return (int)n;
+}
+
+static int
+buf_write(const unsigned char *ptr, size_t len)
+{
+ consume(ptr, len);
+
+ if (uniform_random(400) < 1) {
+ errno = EIO;
+ return -1;
+ }
+
+ return (int)len;
+}
+
+static void *
+hid_open(const char *path)
+{
+ (void)path;
+
+ return (void *)HID_DEV_HANDLE;
+}
+
+static void
+hid_close(void *handle)
+{
+ assert(handle == (void *)HID_DEV_HANDLE);
+}
+
+static int
+hid_read(void *handle, unsigned char *ptr, size_t len, int ms)
+{
+ assert(handle == (void *)HID_DEV_HANDLE);
+ assert(len >= CTAP_MIN_REPORT_LEN && len <= CTAP_MAX_REPORT_LEN);
+
+ return buf_read(ptr, len, ms);
+}
+
+static int
+hid_write(void *handle, const unsigned char *ptr, size_t len)
+{
+ assert(handle == (void *)HID_DEV_HANDLE);
+ assert(len >= CTAP_MIN_REPORT_LEN + 1 &&
+ len <= CTAP_MAX_REPORT_LEN + 1);
+
+ return buf_write(ptr, len);
+}
+
+static void *
+nfc_open(const char *path)
+{
+ (void)path;
+
+ return (void *)NFC_DEV_HANDLE;
+}
+
+static void
+nfc_close(void *handle)
+{
+ assert(handle == (void *)NFC_DEV_HANDLE);
+}
+
+static int
+nfc_read(void *handle, unsigned char *ptr, size_t len, int ms)
+{
+ assert(handle == (void *)NFC_DEV_HANDLE);
+ assert(len > 0 && len <= 256 + 2);
+
+ return buf_read(ptr, len, ms);
+}
+
+static int
+nfc_write(void *handle, const unsigned char *ptr, size_t len)
+{
+ assert(handle == (void *)NFC_DEV_HANDLE);
+ assert(len > 0 && len <= 256 + 2);
+
+ return buf_write(ptr, len);
+}
+
+ssize_t
+fd_read(int fd, void *ptr, size_t len)
+{
+ assert(fd != -1);
+
+ return buf_read(ptr, len, -1);
+}
+
+ssize_t
+fd_write(int fd, const void *ptr, size_t len)
+{
+ assert(fd != -1);
+
+ return buf_write(ptr, len);
+}
+
+fido_dev_t *
+open_dev(int nfc)
+{
+ fido_dev_t *dev;
+ fido_dev_io_t io;
+ fido_dev_transport_t t;
+
+ memset(&io, 0, sizeof(io));
+ memset(&t, 0, sizeof(t));
+
+ if ((dev = fido_dev_new()) == NULL)
+ return NULL;
+
+ if (nfc) {
+ io.open = nfc_open;
+ io.close = nfc_close;
+ io.read = nfc_read;
+ io.write = nfc_write;
+ } else {
+ io.open = hid_open;
+ io.close = hid_close;
+ io.read = hid_read;
+ io.write = hid_write;
+ }
+
+ if (fido_dev_set_io_functions(dev, &io) != FIDO_OK)
+ goto fail;
+
+ if (nfc) {
+ t.rx = fido_nfc_rx;
+ t.tx = fido_nfc_tx;
+ if (fido_dev_set_transport_functions(dev, &t) != FIDO_OK)
+ goto fail;
+ }
+
+ if (fido_dev_open(dev, "nodev") != FIDO_OK)
+ goto fail;
+
+ return dev;
+fail:
+ fido_dev_free(&dev);
+
+ return NULL;
+}
+
+void
+set_wire_data(const uint8_t *ptr, size_t len)
+{
+ wire_data_ptr = ptr;
+ wire_data_len = len;
+}
diff --git a/fuzz/mutator_aux.h b/fuzz/mutator_aux.h
new file mode 100644
index 000000000000..6b1a98215b07
--- /dev/null
+++ b/fuzz/mutator_aux.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef _MUTATOR_AUX_H
+#define _MUTATOR_AUX_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <cbor.h>
+
+#include "../src/fido.h"
+#include "../src/fido/bio.h"
+#include "../src/fido/config.h"
+#include "../src/fido/credman.h"
+#include "../src/fido/eddsa.h"
+#include "../src/fido/es256.h"
+#include "../src/fido/es256.h"
+#include "../src/fido/rs256.h"
+#include "../src/netlink.h"
+
+/*
+ * As of LLVM 10.0.0, MSAN support in libFuzzer was still experimental.
+ * We therefore have to be careful when using our custom mutator, or
+ * MSAN will flag uninitialised reads on memory populated by libFuzzer.
+ * Since there is no way to suppress MSAN without regenerating object
+ * code (in which case you might as well rebuild libFuzzer with MSAN),
+ * we adjust our mutator to make it less accurate while allowing
+ * fuzzing to proceed.
+ */
+
+#if defined(__has_feature)
+# if __has_feature(memory_sanitizer)
+# include <sanitizer/msan_interface.h>
+# define NO_MSAN __attribute__((no_sanitize("memory")))
+# define WITH_MSAN 1
+# endif
+#endif
+
+#if !defined(WITH_MSAN)
+# define NO_MSAN
+#endif
+
+#define MUTATE_SEED 0x01
+#define MUTATE_PARAM 0x02
+#define MUTATE_WIREDATA 0x04
+#define MUTATE_ALL (MUTATE_SEED | MUTATE_PARAM | MUTATE_WIREDATA)
+
+#define MAXSTR 1024
+#define MAXBLOB 3072
+
+struct blob {
+ uint8_t body[MAXBLOB];
+ size_t len;
+};
+
+struct param;
+
+struct param *unpack(const uint8_t *, size_t);
+size_t pack(uint8_t *, size_t, const struct param *);
+size_t pack_dummy(uint8_t *, size_t);
+void mutate(struct param *, unsigned int, unsigned int);
+void test(const struct param *);
+
+void consume(const void *, size_t);
+void consume_str(const char *);
+
+int unpack_blob(cbor_item_t *, struct blob *);
+int unpack_byte(cbor_item_t *, uint8_t *);
+int unpack_int(cbor_item_t *, int *);
+int unpack_string(cbor_item_t *, char *);
+
+cbor_item_t *pack_blob(const struct blob *);
+cbor_item_t *pack_byte(uint8_t);
+cbor_item_t *pack_int(int);
+cbor_item_t *pack_string(const char *);
+
+void mutate_byte(uint8_t *);
+void mutate_int(int *);
+void mutate_blob(struct blob *);
+void mutate_string(char *);
+
+ssize_t fd_read(int, void *, size_t);
+ssize_t fd_write(int, const void *, size_t);
+
+fido_dev_t *open_dev(int);
+void set_wire_data(const uint8_t *, size_t);
+
+void prng_init(unsigned long);
+unsigned long prng_uint32(void);
+
+uint32_t uniform_random(uint32_t);
+
+#endif /* !_MUTATOR_AUX_H */
diff --git a/fuzz/preload-fuzz.c b/fuzz/preload-fuzz.c
new file mode 100644
index 000000000000..efcb8c632605
--- /dev/null
+++ b/fuzz/preload-fuzz.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+/*
+ * cc -fPIC -D_GNU_SOURCE -shared -o preload-fuzz.so preload-fuzz.c
+ * LD_PRELOAD=$(realpath preload-fuzz.so)
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define FUZZ_DEV_PREFIX "nodev"
+
+static int fd_fuzz = -1;
+static int (*open_f)(const char *, int, mode_t);
+static int (*close_f)(int);
+static ssize_t (*write_f)(int, const void *, size_t);
+
+int
+open(const char *path, int flags, ...)
+{
+ va_list ap;
+ mode_t mode;
+
+ va_start(ap, flags);
+ mode = va_arg(ap, mode_t);
+ va_end(ap);
+
+ if (open_f == NULL) {
+ open_f = dlsym(RTLD_NEXT, "open");
+ if (open_f == NULL) {
+ warnx("%s: dlsym", __func__);
+ errno = EACCES;
+ return (-1);
+ }
+ }
+
+ if (strncmp(path, FUZZ_DEV_PREFIX, strlen(FUZZ_DEV_PREFIX)) != 0)
+ return (open_f(path, flags, mode));
+
+ if (fd_fuzz != -1) {
+ warnx("%s: fd_fuzz != -1", __func__);
+ errno = EACCES;
+ return (-1);
+ }
+
+ if ((fd_fuzz = dup(STDIN_FILENO)) < 0) {
+ warn("%s: dup", __func__);
+ errno = EACCES;
+ return (-1);
+ }
+
+ return (fd_fuzz);
+}
+
+int
+close(int fd)
+{
+ if (close_f == NULL) {
+ close_f = dlsym(RTLD_NEXT, "close");
+ if (close_f == NULL) {
+ warnx("%s: dlsym", __func__);
+ errno = EACCES;
+ return (-1);
+ }
+ }
+
+ if (fd == fd_fuzz)
+ fd_fuzz = -1;
+
+ return (close_f(fd));
+}
+
+ssize_t
+write(int fd, const void *buf, size_t nbytes)
+{
+ if (write_f == NULL) {
+ write_f = dlsym(RTLD_NEXT, "write");
+ if (write_f == NULL) {
+ warnx("%s: dlsym", __func__);
+ errno = EBADF;
+ return (-1);
+ }
+ }
+
+ if (fd != fd_fuzz)
+ return (write_f(fd, buf, nbytes));
+
+ return (nbytes);
+}
diff --git a/fuzz/preload-snoop.c b/fuzz/preload-snoop.c
new file mode 100644
index 000000000000..373acc560a60
--- /dev/null
+++ b/fuzz/preload-snoop.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+/*
+ * cc -fPIC -D_GNU_SOURCE -shared -o preload-snoop.so preload-snoop.c
+ * LD_PRELOAD=$(realpath preload-snoop.so)
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define SNOOP_DEV_PREFIX "/dev/hidraw"
+
+struct fd_tuple {
+ int snoop_in;
+ int snoop_out;
+ int real_dev;
+};
+
+static struct fd_tuple *fd_tuple;
+static int (*open_f)(const char *, int, mode_t);
+static int (*close_f)(int);
+static ssize_t (*read_f)(int, void *, size_t);
+static ssize_t (*write_f)(int, const void *, size_t);
+
+static int
+get_fd(const char *hid_path, const char *suffix)
+{
+ char *s = NULL;
+ char path[PATH_MAX];
+ int fd;
+ int r;
+
+ if ((s = strdup(hid_path)) == NULL) {
+ warnx("%s: strdup", __func__);
+ return (-1);
+ }
+
+ for (size_t i = 0; i < strlen(s); i++)
+ if (s[i] == '/')
+ s[i] = '_';
+
+ if ((r = snprintf(path, sizeof(path), "%s-%s", s, suffix)) < 0 ||
+ (size_t)r >= sizeof(path)) {
+ warnx("%s: snprintf", __func__);
+ free(s);
+ return (-1);
+ }
+
+ free(s);
+ s = NULL;
+
+ if ((fd = open_f(path, O_CREAT | O_WRONLY, 0644)) < 0) {
+ warn("%s: open", __func__);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+int
+open(const char *path, int flags, ...)
+{
+ va_list ap;
+ mode_t mode;
+
+ va_start(ap, flags);
+ mode = va_arg(ap, mode_t);
+ va_end(ap);
+
+ if (open_f == NULL) {
+ open_f = dlsym(RTLD_NEXT, "open");
+ if (open_f == NULL) {
+ warnx("%s: dlsym", __func__);
+ errno = EACCES;
+ return (-1);
+ }
+ }
+
+ if (strncmp(path, SNOOP_DEV_PREFIX, strlen(SNOOP_DEV_PREFIX)) != 0)
+ return (open_f(path, flags, mode));
+
+ if (fd_tuple != NULL) {
+ warnx("%s: fd_tuple != NULL", __func__);
+ errno = EACCES;
+ return (-1);
+ }
+
+ if ((fd_tuple = calloc(1, sizeof(*fd_tuple))) == NULL) {
+ warn("%s: calloc", __func__);
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ fd_tuple->snoop_in = -1;
+ fd_tuple->snoop_out = -1;
+ fd_tuple->real_dev = -1;
+
+ if ((fd_tuple->snoop_in = get_fd(path, "in")) < 0 ||
+ (fd_tuple->snoop_out = get_fd(path, "out")) < 0 ||
+ (fd_tuple->real_dev = open_f(path, flags, mode)) < 0) {
+ warn("%s: get_fd/open", __func__);
+ goto fail;
+ }
+
+ return (fd_tuple->real_dev);
+fail:
+ if (fd_tuple->snoop_in != -1)
+ close(fd_tuple->snoop_in);
+ if (fd_tuple->snoop_out != -1)
+ close(fd_tuple->snoop_out);
+ if (fd_tuple->real_dev != -1)
+ close(fd_tuple->real_dev);
+
+ free(fd_tuple);
+ fd_tuple = NULL;
+
+ errno = EACCES;
+
+ return (-1);
+}
+
+int
+close(int fd)
+{
+ if (close_f == NULL) {
+ close_f = dlsym(RTLD_NEXT, "close");
+ if (close_f == NULL) {
+ warnx("%s: dlsym", __func__);
+ errno = EBADF;
+ return (-1);
+ }
+ }
+
+ if (fd_tuple == NULL || fd_tuple->real_dev != fd)
+ return (close_f(fd));
+
+ close_f(fd_tuple->snoop_in);
+ close_f(fd_tuple->snoop_out);
+ close_f(fd_tuple->real_dev);
+
+ free(fd_tuple);
+ fd_tuple = NULL;
+
+ return (0);
+}
+
+ssize_t
+read(int fd, void *buf, size_t nbytes)
+{
+ ssize_t n;
+
+ if (read_f == NULL) {
+ read_f = dlsym(RTLD_NEXT, "read");
+ if (read_f == NULL) {
+ warnx("%s: dlsym", __func__);
+ errno = EBADF;
+ return (-1);
+ }
+ }
+
+ if (write_f == NULL) {
+ write_f = dlsym(RTLD_NEXT, "write");
+ if (write_f == NULL) {
+ warnx("%s: dlsym", __func__);
+ errno = EBADF;
+ return (-1);
+ }
+ }
+
+ if (fd_tuple == NULL || fd_tuple->real_dev != fd)
+ return (read_f(fd, buf, nbytes));
+
+ if ((n = read_f(fd, buf, nbytes)) < 0 ||
+ write_f(fd_tuple->snoop_in, buf, n) != n)
+ return (-1);
+
+ return (n);
+}
+
+ssize_t
+write(int fd, const void *buf, size_t nbytes)
+{
+ ssize_t n;
+
+ if (write_f == NULL) {
+ write_f = dlsym(RTLD_NEXT, "write");
+ if (write_f == NULL) {
+ warnx("%s: dlsym", __func__);
+ errno = EBADF;
+ return (-1);
+ }
+ }
+
+ if (fd_tuple == NULL || fd_tuple->real_dev != fd)
+ return (write_f(fd, buf, nbytes));
+
+ if ((n = write_f(fd, buf, nbytes)) < 0 ||
+ write_f(fd_tuple->snoop_out, buf, n) != n)
+ return (-1);
+
+ return (n);
+}
diff --git a/fuzz/prng.c b/fuzz/prng.c
new file mode 100644
index 000000000000..61114ac94228
--- /dev/null
+++ b/fuzz/prng.c
@@ -0,0 +1,113 @@
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "mutator_aux.h"
+
+#define init_genrand prng_init
+#define genrand_int32 prng_uint32
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
+
+int prng_up = 0;
+static unsigned long mt[N]; /* the array for the state vector */
+static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+
+/* initializes mt[N] with a seed */
+void init_genrand(unsigned long s)
+{
+ mt[0]= s & 0xffffffffUL;
+ for (mti=1; mti<N; mti++) {
+ mt[mti] =
+ (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) +
+ (unsigned long)mti);
+ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+ /* In the previous versions, MSBs of the seed affect */
+ /* only MSBs of the array mt[]. */
+ /* 2002/01/09 modified by Makoto Matsumoto */
+ mt[mti] &= 0xffffffffUL;
+ /* for >32 bit machines */
+ }
+ prng_up = 1;
+}
+
+/* generates a random number on [0,0xffffffff]-interval */
+unsigned long genrand_int32(void)
+{
+ unsigned long y;
+ static unsigned long mag01[2]={0x0UL, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ int kk;
+
+ assert(mti != N+1);
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
diff --git a/fuzz/report.tgz b/fuzz/report.tgz
new file mode 100644
index 000000000000..c8d4d3f38028
--- /dev/null
+++ b/fuzz/report.tgz
Binary files differ
diff --git a/fuzz/summary.txt b/fuzz/summary.txt
new file mode 100644
index 000000000000..8516bf3723aa
--- /dev/null
+++ b/fuzz/summary.txt
@@ -0,0 +1,51 @@
+Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+fuzz/prng.c 31 0 100.00% 2 0 100.00% 49 0 100.00%
+fuzz/udev.c 103 5 95.15% 17 1 94.12% 141 7 95.04%
+fuzz/uniform_random.c 7 1 85.71% 1 0 100.00% 23 1 95.65%
+fuzz/wrap.c 6 0 100.00% 1 0 100.00% 7 0 100.00%
+openbsd-compat/explicit_bzero.c 4 0 100.00% 1 0 100.00% 13 0 100.00%
+openbsd-compat/freezero.c 4 0 100.00% 1 0 100.00% 6 0 100.00%
+openbsd-compat/recallocarray.c 41 7 82.93% 1 0 100.00% 49 7 85.71%
+openbsd-compat/strlcat.c 12 1 91.67% 1 0 100.00% 25 1 96.00%
+openbsd-compat/timingsafe_bcmp.c 4 0 100.00% 1 0 100.00% 8 0 100.00%
+src/aes256.c 115 4 96.52% 8 0 100.00% 175 14 92.00%
+src/assert.c 616 46 92.53% 59 3 94.92% 924 64 93.07%
+src/authkey.c 44 0 100.00% 5 0 100.00% 75 0 100.00%
+src/bio.c 419 20 95.23% 49 2 95.92% 660 22 96.67%
+src/blob.c 53 3 94.34% 10 0 100.00% 96 7 92.71%
+src/buf.c 8 1 87.50% 2 0 100.00% 20 1 95.00%
+src/cbor.c 986 17 98.28% 53 0 100.00% 1426 37 97.41%
+src/compress.c 34 4 88.24% 3 0 100.00% 30 3 90.00%
+src/config.c 94 1 98.94% 10 0 100.00% 146 3 97.95%
+src/cred.c 581 38 93.46% 63 2 96.83% 872 48 94.50%
+src/credman.c 382 10 97.38% 40 0 100.00% 614 15 97.56%
+src/dev.c 414 74 82.13% 43 6 86.05% 556 106 80.94%
+src/ecdh.c 117 2 98.29% 4 0 100.00% 161 5 96.89%
+src/eddsa.c 54 0 100.00% 8 0 100.00% 77 0 100.00%
+src/err.c 122 10 91.80% 1 0 100.00% 126 10 92.06%
+src/es256.c 280 0 100.00% 16 0 100.00% 394 0 100.00%
+src/hid.c 60 0 100.00% 12 0 100.00% 134 0 100.00%
+src/hid_linux.c 173 68 60.69% 14 7 50.00% 303 123 59.41%
+src/hid_unix.c 30 20 33.33% 2 0 100.00% 52 28 46.15%
+src/info.c 198 0 100.00% 44 0 100.00% 405 0 100.00%
+src/io.c 158 7 95.57% 10 0 100.00% 228 11 95.18%
+src/iso7816.c 18 1 94.44% 5 0 100.00% 42 0 100.00%
+src/largeblob.c 513 19 96.30% 30 0 100.00% 759 43 94.33%
+src/log.c 39 5 87.18% 7 1 85.71% 73 4 94.52%
+src/netlink.c 327 15 95.41% 40 0 100.00% 565 35 93.81%
+src/nfc_linux.c 304 123 59.54% 23 10 56.52% 520 199 61.73%
+src/pin.c 403 3 99.26% 26 0 100.00% 583 3 99.49%
+src/random.c 6 1 83.33% 1 0 100.00% 8 1 87.50%
+src/reset.c 24 0 100.00% 3 0 100.00% 27 0 100.00%
+src/rs256.c 102 4 96.08% 8 0 100.00% 138 6 95.65%
+src/u2f.c 473 6 98.73% 15 0 100.00% 742 9 98.79%
+
+Files which contain no functions:
+openbsd-compat/openbsd-compat.h 0 0 - 0 0 - 0 0 -
+src/extern.h 0 0 - 0 0 - 0 0 -
+src/fido.h 0 0 - 0 0 - 0 0 -
+src/fido/err.h 0 0 - 0 0 - 0 0 -
+src/fido/param.h 0 0 - 0 0 - 0 0 -
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+TOTAL 7359 516 92.99% 640 32 95.00% 11252 813 92.77%
diff --git a/fuzz/udev.c b/fuzz/udev.c
new file mode 100644
index 000000000000..3984d8f555ed
--- /dev/null
+++ b/fuzz/udev.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2021 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <sys/types.h>
+
+#include <linux/hidraw.h>
+#include <linux/input.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <libudev.h>
+#include <stdlib.h>
+
+#include "mutator_aux.h"
+
+struct udev {
+ int magic;
+};
+
+struct udev_enumerate {
+ int magic;
+ struct udev_list_entry *list_entry;
+};
+
+struct udev_list_entry {
+ int magic;
+};
+
+struct udev_device {
+ int magic;
+ struct udev_device *parent;
+};
+
+#define UDEV_MAGIC 0x584492cc
+#define UDEV_DEVICE_MAGIC 0x569180dd
+#define UDEV_LIST_ENTRY_MAGIC 0x497422ee
+#define UDEV_ENUM_MAGIC 0x583570ff
+
+#define ASSERT_TYPE(x, m) assert((x) != NULL && (x)->magic == (m))
+#define ASSERT_UDEV(x) ASSERT_TYPE((x), UDEV_MAGIC)
+#define ASSERT_UDEV_ENUM(x) ASSERT_TYPE((x), UDEV_ENUM_MAGIC)
+#define ASSERT_UDEV_LIST_ENTRY(x) ASSERT_TYPE((x), UDEV_LIST_ENTRY_MAGIC)
+#define ASSERT_UDEV_DEVICE(x) ASSERT_TYPE((x), UDEV_DEVICE_MAGIC)
+
+static const char *uevent;
+static const struct blob *report_descriptor;
+
+struct udev *__wrap_udev_new(void);
+struct udev_device *__wrap_udev_device_get_parent_with_subsystem_devtype(
+ struct udev_device *, const char *, const char *);
+struct udev_device *__wrap_udev_device_new_from_syspath(struct udev *,
+ const char *);
+struct udev_enumerate *__wrap_udev_enumerate_new(struct udev *);
+struct udev_list_entry *__wrap_udev_enumerate_get_list_entry(
+ struct udev_enumerate *);
+struct udev_list_entry *__wrap_udev_list_entry_get_next(
+ struct udev_list_entry *);
+const char *__wrap_udev_device_get_sysattr_value(struct udev_device *,
+ const char *);
+const char *__wrap_udev_list_entry_get_name(struct udev_list_entry *);
+const char *__wrap_udev_device_get_devnode(struct udev_device *);
+const char *__wrap_udev_device_get_sysnum(struct udev_device *);
+int __wrap_udev_enumerate_add_match_subsystem(struct udev_enumerate *,
+ const char *);
+int __wrap_udev_enumerate_scan_devices(struct udev_enumerate *);
+int __wrap_ioctl(int, unsigned long , ...);
+void __wrap_udev_device_unref(struct udev_device *);
+void __wrap_udev_enumerate_unref(struct udev_enumerate *);
+void __wrap_udev_unref(struct udev *);
+void set_udev_parameters(const char *, const struct blob *);
+
+struct udev_device *
+__wrap_udev_device_get_parent_with_subsystem_devtype(struct udev_device *child,
+ const char *subsystem, const char *devtype)
+{
+ ASSERT_UDEV_DEVICE(child);
+ fido_log_debug("%s", subsystem); /* XXX consume */
+ fido_log_debug("%s", devtype); /* XXX consume */
+ if (child->parent != NULL)
+ return child->parent;
+ if ((child->parent = calloc(1, sizeof(*child->parent))) == NULL)
+ return NULL;
+ child->parent->magic = UDEV_DEVICE_MAGIC;
+
+ return child->parent;
+}
+
+const char *
+__wrap_udev_device_get_sysattr_value(struct udev_device *udev_device,
+ const char *sysattr)
+{
+ ASSERT_UDEV_DEVICE(udev_device);
+ if (uniform_random(400) < 1)
+ return NULL;
+ if (!strcmp(sysattr, "manufacturer") || !strcmp(sysattr, "product"))
+ return "product info"; /* XXX randomise? */
+ else if (!strcmp(sysattr, "uevent"))
+ return uevent;
+
+ return NULL;
+}
+
+const char *
+__wrap_udev_list_entry_get_name(struct udev_list_entry *entry)
+{
+ ASSERT_UDEV_LIST_ENTRY(entry);
+ return uniform_random(400) < 1 ? NULL : "name"; /* XXX randomise? */
+}
+
+struct udev_device *
+__wrap_udev_device_new_from_syspath(struct udev *udev, const char *syspath)
+{
+ struct udev_device *udev_device;
+
+ ASSERT_UDEV(udev);
+ fido_log_debug("%s", syspath);
+ if ((udev_device = calloc(1, sizeof(*udev_device))) == NULL)
+ return NULL;
+ udev_device->magic = UDEV_DEVICE_MAGIC;
+
+ return udev_device;
+}
+
+const char *
+__wrap_udev_device_get_devnode(struct udev_device *udev_device)
+{
+ ASSERT_UDEV_DEVICE(udev_device);
+ return uniform_random(400) < 1 ? NULL : "/dev/zero";
+}
+
+const char *
+__wrap_udev_device_get_sysnum(struct udev_device *udev_device)
+{
+ ASSERT_UDEV_DEVICE(udev_device);
+ return uniform_random(400) < 1 ? NULL : "101010"; /* XXX randomise? */
+}
+
+void
+__wrap_udev_device_unref(struct udev_device *udev_device)
+{
+ ASSERT_UDEV_DEVICE(udev_device);
+ if (udev_device->parent) {
+ ASSERT_UDEV_DEVICE(udev_device->parent);
+ free(udev_device->parent);
+ }
+ free(udev_device);
+}
+
+struct udev *
+__wrap_udev_new(void)
+{
+ struct udev *udev;
+
+ if ((udev = calloc(1, sizeof(*udev))) == NULL)
+ return NULL;
+ udev->magic = UDEV_MAGIC;
+
+ return udev;
+}
+
+struct udev_enumerate *
+__wrap_udev_enumerate_new(struct udev *udev)
+{
+ struct udev_enumerate *udev_enum;
+
+ ASSERT_UDEV(udev);
+ if ((udev_enum = calloc(1, sizeof(*udev_enum))) == NULL)
+ return NULL;
+ udev_enum->magic = UDEV_ENUM_MAGIC;
+
+ return udev_enum;
+}
+
+int
+__wrap_udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum,
+ const char *subsystem)
+{
+ ASSERT_UDEV_ENUM(udev_enum);
+ fido_log_debug("%s:", subsystem);
+ return uniform_random(400) < 1 ? -EINVAL : 0;
+}
+
+int
+__wrap_udev_enumerate_scan_devices(struct udev_enumerate *udev_enum)
+{
+ ASSERT_UDEV_ENUM(udev_enum);
+ return uniform_random(400) < 1 ? -EINVAL : 0;
+}
+
+struct udev_list_entry *
+__wrap_udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum)
+{
+ ASSERT_UDEV_ENUM(udev_enum);
+ if ((udev_enum->list_entry = calloc(1,
+ sizeof(*udev_enum->list_entry))) == NULL)
+ return NULL;
+ udev_enum->list_entry->magic = UDEV_LIST_ENTRY_MAGIC;
+
+ return udev_enum->list_entry;
+}
+
+struct udev_list_entry *
+__wrap_udev_list_entry_get_next(struct udev_list_entry *udev_list_entry)
+{
+ ASSERT_UDEV_LIST_ENTRY(udev_list_entry);
+ return uniform_random(400) < 1 ? NULL : udev_list_entry;
+}
+
+void
+__wrap_udev_enumerate_unref(struct udev_enumerate *udev_enum)
+{
+ ASSERT_UDEV_ENUM(udev_enum);
+ if (udev_enum->list_entry)
+ ASSERT_UDEV_LIST_ENTRY(udev_enum->list_entry);
+ free(udev_enum->list_entry);
+ free(udev_enum);
+}
+
+void
+__wrap_udev_unref(struct udev *udev)
+{
+ ASSERT_UDEV(udev);
+ free(udev);
+}
+
+int
+__wrap_ioctl(int fd, unsigned long request, ...)
+{
+ va_list ap;
+ struct hidraw_report_descriptor *hrd;
+
+ (void)fd;
+
+ if (uniform_random(400) < 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ va_start(ap, request);
+
+ switch (request) {
+ case IOCTL_REQ(HIDIOCGRDESCSIZE):
+ *va_arg(ap, int *) = (int)report_descriptor->len;
+ break;
+ case IOCTL_REQ(HIDIOCGRDESC):
+ hrd = va_arg(ap, struct hidraw_report_descriptor *);
+ assert(hrd->size == report_descriptor->len);
+ memcpy(hrd->value, report_descriptor->body, hrd->size);
+ break;
+ default:
+ warnx("%s: unknown request 0x%lx", __func__, request);
+ abort();
+ }
+
+ va_end(ap);
+
+ return 0;
+}
+
+void
+set_udev_parameters(const char *uevent_ptr,
+ const struct blob *report_descriptor_ptr)
+{
+ uevent = uevent_ptr;
+ report_descriptor = report_descriptor_ptr;
+}
diff --git a/fuzz/uniform_random.c b/fuzz/uniform_random.c
new file mode 100644
index 000000000000..357091c3c2f1
--- /dev/null
+++ b/fuzz/uniform_random.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+uint32_t uniform_random(uint32_t);
+unsigned long prng_uint32(void);
+
+/*
+ * Calculate a uniformly distributed random number less than upper_bound
+ * avoiding "modulo bias".
+ *
+ * Uniformity is achieved by generating new random numbers until the one
+ * returned is outside the range [0, 2**32 % upper_bound). This
+ * guarantees the selected random number will be inside
+ * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
+ * after reduction modulo upper_bound.
+ */
+uint32_t
+uniform_random(uint32_t upper_bound)
+{
+ uint32_t r, min;
+
+ if (upper_bound < 2)
+ return 0;
+
+ /* 2**32 % x == (2**32 - x) % x */
+ min = -upper_bound % upper_bound;
+
+ /*
+ * This could theoretically loop forever but each retry has
+ * p > 0.5 (worst case, usually far better) of selecting a
+ * number inside the range we need, so it should rarely need
+ * to re-roll.
+ */
+ for (;;) {
+ r = (uint32_t)prng_uint32();
+ if (r >= min)
+ break;
+ }
+
+ return r % upper_bound;
+}
diff --git a/fuzz/wiredata_fido2.h b/fuzz/wiredata_fido2.h
new file mode 100644
index 000000000000..da905516f92a
--- /dev/null
+++ b/fuzz/wiredata_fido2.h
@@ -0,0 +1,633 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef _WIREDATA_FIDO2_H
+#define _WIREDATA_FIDO2_H
+
+#define WIREDATA_CTAP_INIT \
+ 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x80, \
+ 0x43, 0x56, 0x40, 0xb1, 0x4e, 0xd9, 0x2d, 0x00, \
+ 0x22, 0x00, 0x02, 0x02, 0x05, 0x02, 0x01, 0x05, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_KEEPALIVE \
+ 0x00, 0x22, 0x00, 0x02, 0xbb, 0x00, 0x01, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_INFO \
+ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0xb9, 0x00, \
+ 0xa9, 0x01, 0x83, 0x66, 0x55, 0x32, 0x46, 0x5f, \
+ 0x56, 0x32, 0x68, 0x46, 0x49, 0x44, 0x4f, 0x5f, \
+ 0x32, 0x5f, 0x30, 0x6c, 0x46, 0x49, 0x44, 0x4f, \
+ 0x5f, 0x32, 0x5f, 0x31, 0x5f, 0x50, 0x52, 0x45, \
+ 0x02, 0x82, 0x6b, 0x63, 0x72, 0x65, 0x64, 0x50, \
+ 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x6b, 0x68, \
+ 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x65, 0x63, 0x72, \
+ 0x00, 0x22, 0x00, 0x02, 0x00, 0x65, 0x74, 0x03, \
+ 0x50, 0x19, 0x56, 0xe5, 0xbd, 0xa3, 0x74, 0x45, \
+ 0xf1, 0xa8, 0x14, 0x35, 0x64, 0x03, 0xfd, 0xbc, \
+ 0x18, 0x04, 0xa5, 0x62, 0x72, 0x6b, 0xf5, 0x62, \
+ 0x75, 0x70, 0xf5, 0x64, 0x70, 0x6c, 0x61, 0x74, \
+ 0xf4, 0x69, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, \
+ 0x50, 0x69, 0x6e, 0xf4, 0x75, 0x63, 0x72, 0x65, \
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d, \
+ 0x00, 0x22, 0x00, 0x02, 0x01, 0x67, 0x6d, 0x74, \
+ 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0xf5, \
+ 0x05, 0x19, 0x04, 0xb0, 0x06, 0x81, 0x01, 0x07, \
+ 0x08, 0x08, 0x18, 0x80, 0x0a, 0x82, 0xa2, 0x63, \
+ 0x61, 0x6c, 0x67, 0x26, 0x64, 0x74, 0x79, 0x70, \
+ 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, \
+ 0x2d, 0x6b, 0x65, 0x79, 0xa2, 0x63, 0x61, 0x6c, \
+ 0x67, 0x27, 0x64, 0x74, 0x79, 0x70, 0x65, 0x6a, \
+ 0x00, 0x22, 0x00, 0x02, 0x02, 0x70, 0x75, 0x62, \
+ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_AUTHKEY \
+ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x51, 0x00, \
+ 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, \
+ 0x20, 0x01, 0x21, 0x58, 0x20, 0x2a, 0xb8, 0x2d, \
+ 0x36, 0x69, 0xab, 0x30, 0x9d, 0xe3, 0x5e, 0x9b, \
+ 0xfb, 0x94, 0xfc, 0x1d, 0x92, 0x95, 0xaf, 0x01, \
+ 0x47, 0xfe, 0x4b, 0x87, 0xe5, 0xcf, 0x3f, 0x05, \
+ 0x0b, 0x39, 0xda, 0x17, 0x49, 0x22, 0x58, 0x20, \
+ 0x15, 0x1b, 0xbe, 0x08, 0x78, 0x60, 0x4d, 0x3c, \
+ 0x00, 0x22, 0x00, 0x02, 0x00, 0x3f, 0xf1, 0x60, \
+ 0xa6, 0xd8, 0xf8, 0xed, 0xce, 0x4a, 0x30, 0x5d, \
+ 0x1a, 0xaf, 0x80, 0xc4, 0x0a, 0xd2, 0x6f, 0x77, \
+ 0x38, 0x12, 0x97, 0xaa, 0xbd, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_PINTOKEN \
+ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x14, 0x00, \
+ 0xa1, 0x02, 0x50, 0xee, 0x40, 0x4c, 0x85, 0xd7, \
+ 0xa1, 0x2f, 0x56, 0xc4, 0x4e, 0xc5, 0x93, 0x41, \
+ 0xd0, 0x3b, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_STATUS \
+ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x01, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_RETRIES \
+ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x04, 0x00, \
+ 0xa1, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_ASSERT \
+ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0xcb, 0x00, \
+ 0xa3, 0x01, 0xa2, 0x62, 0x69, 0x64, 0x58, 0x40, \
+ 0x4a, 0x4c, 0x9e, 0xcc, 0x81, 0x7d, 0x42, 0x03, \
+ 0x2b, 0x41, 0xd1, 0x38, 0xd3, 0x49, 0xb4, 0xfc, \
+ 0xfb, 0xe4, 0x4e, 0xe4, 0xff, 0x76, 0x34, 0x16, \
+ 0x68, 0x06, 0x9d, 0xa6, 0x01, 0x32, 0xb9, 0xff, \
+ 0xc2, 0x35, 0x0d, 0x89, 0x43, 0x66, 0x12, 0xf8, \
+ 0x8e, 0x5b, 0xde, 0xf4, 0xcc, 0xec, 0x9d, 0x03, \
+ 0x00, 0x92, 0x00, 0x0e, 0x00, 0x85, 0xc2, 0xf5, \
+ 0xe6, 0x8e, 0xeb, 0x3f, 0x3a, 0xec, 0xc3, 0x1d, \
+ 0x04, 0x6e, 0xf3, 0x5b, 0x88, 0x64, 0x74, 0x79, \
+ 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6c, 0x69, \
+ 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x02, 0x58, 0x25, \
+ 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e, 0x8c, 0x68, \
+ 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76, 0x60, 0x5b, \
+ 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86, 0x32, 0xc7, \
+ 0x00, 0x92, 0x00, 0x0e, 0x01, 0x99, 0x5c, 0xf3, \
+ 0xba, 0x83, 0x1d, 0x97, 0x63, 0x04, 0x00, 0x00, \
+ 0x00, 0x09, 0x03, 0x58, 0x47, 0x30, 0x45, 0x02, \
+ 0x21, 0x00, 0xcf, 0x3f, 0x36, 0x0e, 0x1f, 0x6f, \
+ 0xd6, 0xa0, 0x9d, 0x13, 0xcf, 0x55, 0xf7, 0x49, \
+ 0x8f, 0xc8, 0xc9, 0x03, 0x12, 0x76, 0x41, 0x75, \
+ 0x7b, 0xb5, 0x0a, 0x90, 0xa5, 0x82, 0x26, 0xf1, \
+ 0x6b, 0x80, 0x02, 0x20, 0x34, 0x9b, 0x7a, 0x82, \
+ 0x00, 0x92, 0x00, 0x0e, 0x02, 0xd3, 0xe1, 0x79, \
+ 0x49, 0x55, 0x41, 0x9f, 0xa4, 0x06, 0x06, 0xbd, \
+ 0xc8, 0xb9, 0x2b, 0x5f, 0xe1, 0xa7, 0x99, 0x1c, \
+ 0xa1, 0xfc, 0x7e, 0x3e, 0xd5, 0x85, 0x2e, 0x11, \
+ 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_CRED \
+ 0x00, 0x91, 0x00, 0x03, 0x90, 0x03, 0xe1, 0x00, \
+ 0xa3, 0x01, 0x66, 0x70, 0x61, 0x63, 0x6b, 0x65, \
+ 0x64, 0x02, 0x58, 0xc4, 0x49, 0x96, 0x0d, 0xe5, \
+ 0x88, 0x0e, 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f, \
+ 0x64, 0x76, 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9, \
+ 0xa2, 0x86, 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, \
+ 0x83, 0x1d, 0x97, 0x63, 0x45, 0x00, 0x00, 0x00, \
+ 0x00, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, \
+ 0x00, 0x91, 0x00, 0x03, 0x00, 0x15, 0x80, 0x06, \
+ 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x40, \
+ 0xed, 0x88, 0x48, 0xa1, 0xdb, 0x56, 0x4d, 0x0f, \
+ 0x0d, 0xc8, 0x8f, 0x0f, 0xe9, 0x16, 0xb1, 0x78, \
+ 0xa9, 0x40, 0x98, 0x71, 0xa0, 0xb3, 0xf2, 0xcf, \
+ 0x05, 0x73, 0x6c, 0x12, 0xbf, 0x00, 0x96, 0xf3, \
+ 0x7b, 0x93, 0xba, 0x49, 0xee, 0x23, 0xb4, 0x78, \
+ 0x2e, 0xfb, 0xce, 0x27, 0xa8, 0xc2, 0x26, 0x78, \
+ 0x00, 0x91, 0x00, 0x03, 0x01, 0xcc, 0x95, 0x2d, \
+ 0x40, 0xdb, 0xd1, 0x40, 0x3d, 0x2b, 0xa3, 0x31, \
+ 0xa0, 0x75, 0x82, 0x63, 0xf0, 0xa5, 0x01, 0x02, \
+ 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x9d, \
+ 0x95, 0xa1, 0xb5, 0xd6, 0x11, 0xbf, 0xe2, 0x28, \
+ 0xa0, 0x7f, 0xca, 0x1e, 0xd9, 0x09, 0x0f, 0x0d, \
+ 0xe7, 0x8e, 0x29, 0xe8, 0x2e, 0x11, 0xdb, 0x55, \
+ 0x62, 0x13, 0xd7, 0x26, 0xc2, 0x7e, 0x2b, 0x22, \
+ 0x00, 0x91, 0x00, 0x03, 0x02, 0x58, 0x20, 0xbe, \
+ 0x74, 0x2a, 0xac, 0xde, 0x11, 0x40, 0x76, 0x31, \
+ 0x0b, 0xed, 0x55, 0xde, 0xf3, 0x03, 0xe4, 0x1c, \
+ 0xac, 0x42, 0x63, 0x8f, 0xe8, 0x30, 0x63, 0xb7, \
+ 0x07, 0x4e, 0x5d, 0xfb, 0x17, 0x5e, 0x9b, 0x03, \
+ 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x26, 0x63, 0x73, \
+ 0x69, 0x67, 0x58, 0x48, 0x30, 0x46, 0x02, 0x21, \
+ 0x00, 0xfb, 0xd1, 0x26, 0x76, 0x34, 0x74, 0xac, \
+ 0x00, 0x91, 0x00, 0x03, 0x03, 0xf6, 0xd8, 0x5c, \
+ 0x5d, 0xbc, 0xda, 0xe0, 0x43, 0xe0, 0xa5, 0x42, \
+ 0x9f, 0xc7, 0xe2, 0x18, 0x3e, 0xe2, 0x2c, 0x94, \
+ 0x78, 0xbf, 0x9c, 0xeb, 0x3e, 0x9d, 0x02, 0x21, \
+ 0x00, 0xab, 0x21, 0x1b, 0xc4, 0x30, 0x69, 0xee, \
+ 0x7f, 0x09, 0xe6, 0x6b, 0x99, 0x98, 0x34, 0x07, \
+ 0x7b, 0x9a, 0x58, 0xb2, 0xe8, 0x77, 0xe0, 0xba, \
+ 0x7d, 0xab, 0x65, 0xf8, 0xba, 0x2a, 0xcb, 0x9a, \
+ 0x00, 0x91, 0x00, 0x03, 0x04, 0x41, 0x63, 0x78, \
+ 0x35, 0x63, 0x81, 0x59, 0x02, 0xb3, 0x30, 0x82, \
+ 0x02, 0xaf, 0x30, 0x82, 0x01, 0x97, 0xa0, 0x03, \
+ 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x5b, 0x3d, \
+ 0xb6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, \
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, \
+ 0x30, 0x21, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, \
+ 0x55, 0x04, 0x03, 0x0c, 0x16, 0x59, 0x75, 0x62, \
+ 0x00, 0x91, 0x00, 0x03, 0x05, 0x69, 0x63, 0x6f, \
+ 0x20, 0x46, 0x49, 0x44, 0x4f, 0x20, 0x50, 0x72, \
+ 0x65, 0x76, 0x69, 0x65, 0x77, 0x20, 0x43, 0x41, \
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x34, \
+ 0x31, 0x32, 0x31, 0x30, 0x35, 0x37, 0x31, 0x30, \
+ 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x32, 0x33, \
+ 0x31, 0x31, 0x30, 0x35, 0x37, 0x31, 0x30, 0x5a, \
+ 0x30, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \
+ 0x00, 0x91, 0x00, 0x03, 0x06, 0x55, 0x04, 0x06, \
+ 0x13, 0x02, 0x53, 0x45, 0x31, 0x12, 0x30, 0x10, \
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x59, \
+ 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x41, 0x42, \
+ 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, \
+ 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, \
+ 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, \
+ 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, \
+ 0x00, 0x91, 0x00, 0x03, 0x07, 0x74, 0x69, 0x6f, \
+ 0x6e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, \
+ 0x04, 0x03, 0x0c, 0x1f, 0x59, 0x75, 0x62, 0x69, \
+ 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, \
+ 0x45, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, \
+ 0x20, 0x31, 0x32, 0x31, 0x33, 0x39, 0x33, 0x39, \
+ 0x31, 0x32, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, \
+ 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, \
+ 0x00, 0x91, 0x00, 0x03, 0x08, 0x06, 0x08, 0x2a, \
+ 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, \
+ 0x42, 0x00, 0x04, 0xfb, 0x2c, 0xdd, 0x30, 0x43, \
+ 0x28, 0xc5, 0x72, 0x4a, 0x50, 0xcc, 0xe6, 0xf6, \
+ 0x0b, 0xad, 0x7d, 0x27, 0xa9, 0x1b, 0x59, 0xe1, \
+ 0xe6, 0x6f, 0x29, 0x7b, 0x89, 0xc9, 0xd4, 0x3d, \
+ 0xc2, 0xb2, 0xc7, 0x78, 0x89, 0xb4, 0xf0, 0xff, \
+ 0x9d, 0x02, 0x28, 0xcb, 0x94, 0x6d, 0xfc, 0xe0, \
+ 0x00, 0x91, 0x00, 0x03, 0x09, 0x1b, 0x19, 0x58, \
+ 0x9b, 0x67, 0x80, 0x4a, 0xac, 0x97, 0x7f, 0x28, \
+ 0x18, 0x9c, 0xcd, 0xb3, 0x25, 0x74, 0xca, 0x28, \
+ 0xa3, 0x6c, 0x30, 0x6a, 0x30, 0x22, 0x06, 0x09, \
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xc4, 0x0a, \
+ 0x02, 0x04, 0x15, 0x31, 0x2e, 0x33, 0x2e, 0x36, \
+ 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, \
+ 0x31, 0x34, 0x38, 0x32, 0x2e, 0x31, 0x2e, 0x36, \
+ 0x00, 0x91, 0x00, 0x03, 0x0a, 0x30, 0x13, 0x06, \
+ 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xe5, \
+ 0x1c, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, \
+ 0x04, 0x30, 0x30, 0x21, 0x06, 0x0b, 0x2b, 0x06, \
+ 0x01, 0x04, 0x01, 0x82, 0xe5, 0x1c, 0x01, 0x01, \
+ 0x04, 0x04, 0x12, 0x04, 0x10, 0xf8, 0xa0, 0x11, \
+ 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, \
+ 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x30, 0x0c, 0x06, \
+ 0x00, 0x91, 0x00, 0x03, 0x0b, 0x03, 0x55, 0x1d, \
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, \
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, \
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, \
+ 0x82, 0x01, 0x01, 0x00, 0x32, 0xf3, 0xe4, 0xbd, \
+ 0x58, 0xd7, 0x42, 0x2b, 0xaf, 0x49, 0x99, 0x86, \
+ 0x08, 0x1f, 0x0d, 0xa9, 0x3b, 0xc6, 0xaa, 0x1c, \
+ 0x72, 0x11, 0xf9, 0x28, 0x53, 0xeb, 0xf3, 0xeb, \
+ 0x00, 0x91, 0x00, 0x03, 0x0c, 0x73, 0xda, 0x69, \
+ 0x3b, 0x06, 0xde, 0x31, 0x33, 0x8e, 0x5d, 0x02, \
+ 0xec, 0xf6, 0x76, 0xe9, 0x5c, 0x42, 0xbe, 0xa5, \
+ 0x8f, 0x25, 0xd3, 0x37, 0x3f, 0x77, 0xbb, 0x2a, \
+ 0x9d, 0x7c, 0xb2, 0x3e, 0x11, 0x8c, 0x41, 0xd4, \
+ 0x9a, 0x4c, 0x9a, 0xd8, 0xf3, 0xe2, 0xa4, 0xec, \
+ 0x01, 0x77, 0x7a, 0x74, 0xa8, 0xc4, 0x12, 0x43, \
+ 0xc3, 0x1e, 0xce, 0x20, 0x8f, 0x2d, 0x0f, 0x6e, \
+ 0x00, 0x91, 0x00, 0x03, 0x0d, 0xbc, 0x61, 0x9b, \
+ 0xe1, 0x84, 0xa1, 0x72, 0xf6, 0xa9, 0xac, 0xcb, \
+ 0xf8, 0x73, 0x6d, 0x5b, 0xe2, 0x98, 0xb3, 0x6b, \
+ 0xec, 0xe7, 0x1e, 0x77, 0x8d, 0x0a, 0x69, 0xaa, \
+ 0xf9, 0x94, 0xb8, 0x63, 0x6d, 0xe8, 0xfa, 0xf6, \
+ 0x2f, 0xd3, 0xce, 0x7f, 0x04, 0x4c, 0x32, 0x2c, \
+ 0xf7, 0x26, 0x3e, 0x34, 0x99, 0xe6, 0xa5, 0xb2, \
+ 0xb0, 0x2a, 0xbb, 0xad, 0x5b, 0xd9, 0xec, 0xe5, \
+ 0x00, 0x91, 0x00, 0x03, 0x0e, 0xb0, 0x71, 0x4d, \
+ 0x73, 0xbb, 0x94, 0x61, 0x49, 0x9c, 0x94, 0x2a, \
+ 0x5f, 0x1d, 0xcc, 0xaf, 0x65, 0x03, 0x3b, 0x39, \
+ 0x39, 0xd4, 0x47, 0xd9, 0xfc, 0xc4, 0x7b, 0x0b, \
+ 0x16, 0xd8, 0xe9, 0x01, 0xfc, 0xec, 0x3f, 0x8c, \
+ 0x1b, 0xc0, 0xc6, 0xac, 0x0b, 0x5d, 0x74, 0xc7, \
+ 0xbb, 0x03, 0x05, 0x69, 0x17, 0xe9, 0x98, 0x1a, \
+ 0x19, 0xb9, 0x09, 0x5c, 0xa1, 0xf4, 0xab, 0x9f, \
+ 0x00, 0x91, 0x00, 0x03, 0x0f, 0x02, 0x7c, 0x28, \
+ 0x0f, 0x8a, 0xf9, 0xed, 0x1d, 0x29, 0x3c, 0xf6, \
+ 0xcc, 0x2f, 0x04, 0x6d, 0x9a, 0xd6, 0x62, 0xb4, \
+ 0xa9, 0x6e, 0xb1, 0xca, 0xca, 0xac, 0x5e, 0x05, \
+ 0x3e, 0x83, 0x91, 0x47, 0x7c, 0x1f, 0x8b, 0x60, \
+ 0x01, 0xde, 0x65, 0x3a, 0xbf, 0xf2, 0xaa, 0xbb, \
+ 0x55, 0x98, 0x86, 0x91, 0x7e, 0xad, 0x3b, 0x36, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_CREDMAN_META \
+ 0x00, 0x12, 0x00, 0x04, 0x90, 0x00, 0x07, 0x00, \
+ 0xa2, 0x01, 0x00, 0x02, 0x18, 0x19, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_CREDMAN_RPLIST \
+ 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x37, 0x00, \
+ 0xa3, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6a, 0x79, \
+ 0x75, 0x62, 0x69, 0x63, 0x6f, 0x2e, 0x63, 0x6f, \
+ 0x6d, 0x04, 0x58, 0x20, 0x37, 0x82, 0x09, 0xb7, \
+ 0x2d, 0xef, 0xcb, 0xa9, 0x1d, 0xcb, 0xf8, 0x54, \
+ 0xed, 0xb4, 0xda, 0xa6, 0x48, 0x82, 0x8a, 0x2c, \
+ 0xbd, 0x18, 0x0a, 0xfc, 0x77, 0xa7, 0x44, 0x34, \
+ 0x65, 0x5a, 0x1c, 0x7d, 0x05, 0x03, 0x00, 0x00, \
+ 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x36, 0x00, \
+ 0xa2, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6b, 0x79, \
+ 0x75, 0x62, 0x69, 0x6b, 0x65, 0x79, 0x2e, 0x6f, \
+ 0x72, 0x67, 0x04, 0x58, 0x20, 0x12, 0x6b, 0xba, \
+ 0x6a, 0x2d, 0x7a, 0x81, 0x84, 0x25, 0x7b, 0x74, \
+ 0xdd, 0x1d, 0xdd, 0x46, 0xb6, 0x2a, 0x8c, 0xa2, \
+ 0xa7, 0x83, 0xfe, 0xdb, 0x5b, 0x19, 0x48, 0x73, \
+ 0x55, 0xb7, 0xe3, 0x46, 0x09, 0x00, 0x00, 0x00, \
+ 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x37, 0x00, \
+ 0xa2, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6c, 0x77, \
+ 0x65, 0x62, 0x61, 0x75, 0x74, 0x68, 0x6e, 0x2e, \
+ 0x64, 0x65, 0x76, 0x04, 0x58, 0x20, 0xd6, 0x32, \
+ 0x7d, 0x8c, 0x6a, 0x5d, 0xe6, 0xae, 0x0e, 0x33, \
+ 0xd0, 0xa3, 0x31, 0xfb, 0x67, 0x77, 0xb9, 0x4e, \
+ 0xf4, 0x73, 0x19, 0xfe, 0x7e, 0xfd, 0xfa, 0x82, \
+ 0x70, 0x8e, 0x1f, 0xbb, 0xa2, 0x55, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_CREDMAN_RKLIST \
+ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc5, 0x00, \
+ 0xa5, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \
+ 0xe4, 0xe1, 0x06, 0x31, 0xde, 0x00, 0x0f, 0x4f, \
+ 0x12, 0x6e, 0xc9, 0x68, 0x2d, 0x43, 0x3f, 0xf1, \
+ 0x02, 0x2c, 0x6e, 0xe6, 0x96, 0x10, 0xbf, 0x73, \
+ 0x35, 0xc9, 0x20, 0x27, 0x06, 0xba, 0x39, 0x09, \
+ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \
+ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \
+ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \
+ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \
+ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \
+ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x19, \
+ 0xf7, 0x78, 0x0c, 0xa0, 0xbc, 0xb9, 0xa6, 0xd5, \
+ 0x1e, 0xd7, 0x87, 0xfb, 0x6c, 0x80, 0x03, 0x64, \
+ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \
+ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \
+ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02, \
+ 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x81, \
+ 0x6c, 0xdd, 0x8c, 0x8f, 0x8c, 0xc8, 0x43, 0xa7, \
+ 0xbb, 0x79, 0x51, 0x09, 0xb1, 0xdf, 0xbe, 0xc4, \
+ 0xa5, 0x54, 0x16, 0x9e, 0x58, 0x56, 0xb3, 0x0b, \
+ 0x34, 0x4f, 0xa5, 0x6c, 0x05, 0xa2, 0x21, 0x22, \
+ 0x58, 0x20, 0xcd, 0xc2, 0x0c, 0x99, 0x83, 0x5a, \
+ 0x61, 0x73, 0xd8, 0xe0, 0x74, 0x23, 0x46, 0x64, \
+ 0x00, 0x15, 0x00, 0x04, 0x02, 0x39, 0x4c, 0xb0, \
+ 0xf4, 0x6c, 0x0a, 0x37, 0x72, 0xaa, 0xa8, 0xea, \
+ 0x58, 0xd3, 0xd4, 0xe0, 0x51, 0xb2, 0x28, 0x09, \
+ 0x05, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xa0, 0x00, \
+ 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \
+ 0x56, 0xa1, 0x3c, 0x06, 0x2b, 0xad, 0xa2, 0x21, \
+ 0x7d, 0xcd, 0x91, 0x08, 0x47, 0xa8, 0x8a, 0x06, \
+ 0x06, 0xf6, 0x66, 0x91, 0xf6, 0xeb, 0x89, 0xe4, \
+ 0xdf, 0x26, 0xbc, 0x46, 0x59, 0xc3, 0x7d, 0xc0, \
+ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \
+ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \
+ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \
+ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \
+ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \
+ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0xd8, \
+ 0x27, 0x4b, 0x25, 0xed, 0x19, 0xef, 0x11, 0xaf, \
+ 0xa6, 0x89, 0x7b, 0x84, 0x50, 0xe7, 0x62, 0x64, \
+ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \
+ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \
+ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa4, 0x01, 0x01, \
+ 0x03, 0x27, 0x20, 0x06, 0x21, 0x58, 0x20, 0x8d, \
+ 0xfe, 0x45, 0xd5, 0x7d, 0xb6, 0x17, 0xab, 0x86, \
+ 0x2d, 0x32, 0xf6, 0x85, 0xf0, 0x92, 0x76, 0xb7, \
+ 0xce, 0x73, 0xca, 0x4e, 0x0e, 0xfd, 0xd5, 0xdb, \
+ 0x2a, 0x1d, 0x55, 0x90, 0x96, 0x52, 0xc2, 0x0a, \
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xa0, 0x00, \
+ 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \
+ 0x04, 0x0e, 0x0f, 0xa0, 0xcd, 0x60, 0x35, 0x9a, \
+ 0xba, 0x47, 0x0c, 0x10, 0xb6, 0x82, 0x6e, 0x2f, \
+ 0x66, 0xb9, 0xa7, 0xcf, 0xd8, 0x47, 0xb4, 0x3d, \
+ 0xfd, 0x77, 0x1a, 0x38, 0x22, 0xa1, 0xda, 0xa5, \
+ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \
+ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \
+ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \
+ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \
+ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \
+ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x00, \
+ 0x5d, 0xdf, 0xef, 0xe2, 0xf3, 0x06, 0xb2, 0xa5, \
+ 0x46, 0x4d, 0x98, 0xbc, 0x14, 0x65, 0xc1, 0x64, \
+ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \
+ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \
+ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa4, 0x01, 0x01, \
+ 0x03, 0x27, 0x20, 0x06, 0x21, 0x58, 0x20, 0x72, \
+ 0x79, 0x14, 0x69, 0xdf, 0xcb, 0x64, 0x75, 0xee, \
+ 0xd4, 0x45, 0x94, 0xbc, 0x48, 0x4d, 0x2a, 0x9f, \
+ 0xc9, 0xf4, 0xb5, 0x1b, 0x05, 0xa6, 0x5b, 0x54, \
+ 0x9a, 0xac, 0x6c, 0x2e, 0xc6, 0x90, 0x62, 0x0a, \
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc3, 0x00, \
+ 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \
+ 0xce, 0x32, 0xd8, 0x79, 0xdd, 0x86, 0xa2, 0x42, \
+ 0x7c, 0xc3, 0xe1, 0x95, 0x12, 0x93, 0x1a, 0x03, \
+ 0xe6, 0x70, 0xb8, 0xff, 0xcd, 0xa5, 0xdf, 0x15, \
+ 0xfc, 0x88, 0x2a, 0xf5, 0x44, 0xf1, 0x33, 0x9c, \
+ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \
+ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \
+ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \
+ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \
+ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \
+ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x0a, \
+ 0x26, 0x5b, 0x7e, 0x1a, 0x2a, 0xba, 0x70, 0x5f, \
+ 0x18, 0x26, 0x14, 0xb2, 0x71, 0xca, 0x98, 0x64, \
+ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \
+ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \
+ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02, \
+ 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x8b, \
+ 0x48, 0xf0, 0x69, 0xfb, 0x22, 0xfb, 0xf3, 0x86, \
+ 0x57, 0x7c, 0xdd, 0x82, 0x2c, 0x1c, 0x0c, 0xdc, \
+ 0x27, 0xe2, 0x6a, 0x4c, 0x1a, 0x10, 0x04, 0x27, \
+ 0x51, 0x3e, 0x2a, 0x9d, 0x3a, 0xb6, 0xb5, 0x22, \
+ 0x58, 0x20, 0x70, 0xfe, 0x91, 0x67, 0x64, 0x53, \
+ 0x63, 0x83, 0x72, 0x31, 0xe9, 0xe5, 0x20, 0xb7, \
+ 0x00, 0x15, 0x00, 0x04, 0x02, 0xee, 0xc9, 0xfb, \
+ 0x63, 0xd7, 0xe4, 0x76, 0x39, 0x80, 0x82, 0x74, \
+ 0xb8, 0xfa, 0x67, 0xf5, 0x1b, 0x8f, 0xe0, 0x0a, \
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc3, 0x00, \
+ 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \
+ 0xf9, 0xa3, 0x67, 0xbf, 0x5e, 0x80, 0x95, 0xdb, \
+ 0x4c, 0xc5, 0x8f, 0x65, 0x36, 0xc5, 0xaf, 0xdd, \
+ 0x90, 0x2e, 0x62, 0x68, 0x67, 0x9c, 0xa2, 0x26, \
+ 0x2f, 0x2a, 0xf9, 0x3a, 0xda, 0x15, 0xf2, 0x27, \
+ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \
+ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \
+ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \
+ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \
+ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \
+ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0xfb, \
+ 0xa6, 0xbe, 0xc1, 0x01, 0xf6, 0x7a, 0x81, 0xf9, \
+ 0xcd, 0x6d, 0x20, 0x41, 0x7a, 0x1c, 0x40, 0x64, \
+ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \
+ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \
+ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02, \
+ 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xda, \
+ 0x2b, 0x53, 0xc3, 0xbe, 0x48, 0xf8, 0xab, 0xbd, \
+ 0x06, 0x28, 0x46, 0xfa, 0x35, 0xab, 0xf9, 0xc5, \
+ 0x2e, 0xfd, 0x3c, 0x38, 0x88, 0xb3, 0xe1, 0xa7, \
+ 0xc5, 0xc6, 0xed, 0x72, 0x54, 0x37, 0x93, 0x22, \
+ 0x58, 0x20, 0x12, 0x82, 0x32, 0x2d, 0xab, 0xbc, \
+ 0x64, 0xb3, 0xed, 0xcc, 0xd5, 0x22, 0xec, 0x79, \
+ 0x00, 0x15, 0x00, 0x04, 0x02, 0x4b, 0xe2, 0x4d, \
+ 0x0c, 0x4b, 0x8d, 0x31, 0x4c, 0xb4, 0x0f, 0xd4, \
+ 0xa9, 0xbe, 0x0c, 0xab, 0x9e, 0x0a, 0xc9, 0x0a, \
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_BIO_INFO \
+ 0x00, 0x10, 0x00, 0x04, 0x90, 0x00, 0x06, 0x00, \
+ 0xa2, 0x02, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_BIO_ENROLL \
+ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x0a, 0x00, \
+ 0xa3, 0x04, 0x42, 0x68, 0x96, 0x05, 0x00, 0x06, \
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x06, 0x00, \
+ 0xa2, 0x05, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x06, 0x00, \
+ 0xa2, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_BIO_ENUM \
+ 0x00, 0x10, 0x00, 0x0f, 0x90, 0x00, 0x2e, 0x00, \
+ 0xa1, 0x07, 0x83, 0xa2, 0x01, 0x42, 0xce, 0xa3, \
+ 0x02, 0x67, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, \
+ 0x31, 0xa2, 0x01, 0x42, 0xbf, 0x5e, 0x02, 0x67, \
+ 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x32, 0xa2, \
+ 0x01, 0x42, 0x5e, 0xd2, 0x02, 0x67, 0x66, 0x69, \
+ 0x6e, 0x67, 0x65, 0x72, 0x33, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY \
+ 0x89, 0xc9, 0x8d, 0x28, 0x90, 0x01, 0xe6, 0x00, \
+ 0xa1, 0x01, 0x59, 0x01, 0xe0, 0x81, 0xa3, 0x01, \
+ 0x59, 0x01, 0xb8, 0xb3, 0x26, 0x24, 0x99, 0xde, \
+ 0x06, 0x3f, 0xca, 0xde, 0x98, 0x8d, 0x9d, 0xc5, \
+ 0x3f, 0x26, 0x6c, 0xc7, 0x40, 0x93, 0xc4, 0x88, \
+ 0x06, 0x51, 0x4f, 0xb9, 0x61, 0xf2, 0xc9, 0x8d, \
+ 0xbc, 0xce, 0x79, 0x08, 0xec, 0x90, 0xc5, 0x5b, \
+ 0xe5, 0x0a, 0x72, 0x08, 0x7b, 0xe1, 0xf9, 0x16, \
+ 0x89, 0xc9, 0x8d, 0x28, 0x00, 0x06, 0x8b, 0x76, \
+ 0x32, 0xa0, 0xae, 0x55, 0xb2, 0x39, 0x71, 0xce, \
+ 0x34, 0x4b, 0x6e, 0x6b, 0x89, 0xa6, 0x5e, 0x69, \
+ 0x07, 0xac, 0xf6, 0x01, 0x3c, 0xba, 0x45, 0x7a, \
+ 0x75, 0x25, 0x3a, 0xbd, 0x95, 0x22, 0x9d, 0xc3, \
+ 0xe4, 0x42, 0x31, 0x5c, 0xb5, 0xf4, 0x64, 0x6a, \
+ 0x56, 0x1d, 0xab, 0xc7, 0x6e, 0x96, 0x75, 0xe7, \
+ 0xb3, 0x22, 0x0b, 0x82, 0xac, 0x57, 0x78, 0xdf, \
+ 0x89, 0xc9, 0x8d, 0x28, 0x01, 0x57, 0x06, 0xc5, \
+ 0x4b, 0x61, 0x0b, 0x4d, 0xa1, 0x66, 0xa0, 0x89, \
+ 0xad, 0x19, 0x8f, 0xd8, 0x96, 0x55, 0x22, 0x5f, \
+ 0xca, 0x2e, 0xc1, 0xd7, 0xbd, 0xa1, 0x83, 0x66, \
+ 0x4d, 0x85, 0xcb, 0x01, 0x60, 0x3f, 0xf7, 0xf7, \
+ 0xa3, 0x7a, 0xfa, 0x99, 0xa0, 0x1e, 0x25, 0x90, \
+ 0xd0, 0xd0, 0x3b, 0x54, 0x90, 0x77, 0x94, 0xa6, \
+ 0x88, 0xea, 0xc3, 0x6b, 0xa0, 0x59, 0x5e, 0x69, \
+ 0x89, 0xc9, 0x8d, 0x28, 0x02, 0x78, 0x0b, 0x2b, \
+ 0xab, 0x5b, 0x04, 0x2f, 0x78, 0x15, 0x86, 0x2b, \
+ 0x0f, 0x63, 0xb2, 0xd7, 0xc9, 0xe9, 0xac, 0x0e, \
+ 0xbc, 0x17, 0xe4, 0x19, 0x88, 0xe0, 0xe6, 0x13, \
+ 0xf8, 0x15, 0x08, 0xa7, 0xe1, 0x6e, 0x71, 0x5c, \
+ 0xef, 0x3e, 0xc1, 0x0f, 0x74, 0xdb, 0xdc, 0x52, \
+ 0x9c, 0xfc, 0xe9, 0xa9, 0xf3, 0x0d, 0x52, 0xbc, \
+ 0x0c, 0xe8, 0xba, 0xd1, 0x76, 0x46, 0x87, 0xb5, \
+ 0x89, 0xc9, 0x8d, 0x28, 0x03, 0x30, 0xe6, 0x9d, \
+ 0xa1, 0x2b, 0xa5, 0x9e, 0x3b, 0x86, 0xb3, 0x5f, \
+ 0xe3, 0x81, 0xa6, 0x76, 0x32, 0x9d, 0xf9, 0xc5, \
+ 0x07, 0x93, 0xb3, 0xdf, 0x64, 0xe2, 0x78, 0x9c, \
+ 0x00, 0xc7, 0x86, 0x79, 0xd6, 0x67, 0xa2, 0xfb, \
+ 0xf2, 0x8d, 0xea, 0xe9, 0xc8, 0xfc, 0x43, 0xd2, \
+ 0x0f, 0x2f, 0x7d, 0x9d, 0xd3, 0x8f, 0x9c, 0xdd, \
+ 0xa2, 0x9f, 0x42, 0x76, 0x40, 0xcc, 0x4a, 0xd0, \
+ 0x89, 0xc9, 0x8d, 0x28, 0x04, 0xb4, 0x87, 0x18, \
+ 0x06, 0xc3, 0xc7, 0x89, 0x98, 0x72, 0xcc, 0x1a, \
+ 0xd1, 0xd8, 0x78, 0xb9, 0x75, 0x0b, 0x92, 0xe3, \
+ 0xcc, 0xed, 0x38, 0x39, 0x4b, 0xa9, 0xcf, 0x30, \
+ 0xd6, 0xb5, 0xa1, 0x3f, 0xfa, 0x4f, 0x29, 0x99, \
+ 0xa9, 0x03, 0x77, 0xf6, 0x53, 0xfa, 0xd8, 0x32, \
+ 0xce, 0xf4, 0xf6, 0x0a, 0x3c, 0xe8, 0x9c, 0x3d, \
+ 0xaa, 0xe0, 0x7b, 0x2c, 0xa5, 0x28, 0xe1, 0xdd, \
+ 0x89, 0xc9, 0x8d, 0x28, 0x05, 0x51, 0xbf, 0xe1, \
+ 0xd4, 0xf5, 0x5e, 0x38, 0x2c, 0xec, 0xab, 0xdd, \
+ 0xb8, 0x5c, 0x13, 0x43, 0x62, 0xc2, 0xb6, 0x02, \
+ 0x18, 0xce, 0x9a, 0x62, 0x67, 0x6a, 0xeb, 0x99, \
+ 0xf6, 0x2f, 0xf1, 0xf1, 0xec, 0x3e, 0x74, 0xfa, \
+ 0xf8, 0x16, 0x43, 0xea, 0x1e, 0xef, 0x5d, 0x37, \
+ 0x6c, 0x13, 0xf9, 0x7f, 0x65, 0x09, 0xab, 0x60, \
+ 0x38, 0xda, 0x0f, 0xe7, 0xfa, 0x9e, 0x17, 0x10, \
+ 0x89, 0xc9, 0x8d, 0x28, 0x06, 0xdc, 0x4c, 0x4d, \
+ 0xae, 0x5c, 0xb4, 0x0d, 0x6b, 0x05, 0x6d, 0x25, \
+ 0x3f, 0x78, 0x5d, 0xf3, 0x34, 0x33, 0xa4, 0x89, \
+ 0x34, 0x0e, 0x88, 0x66, 0x40, 0x57, 0x6b, 0x34, \
+ 0x83, 0xfd, 0x39, 0xe7, 0xfb, 0x84, 0x09, 0xb3, \
+ 0x16, 0x8f, 0x80, 0xdf, 0x1b, 0xe0, 0x02, 0x4c, \
+ 0xde, 0x31, 0x2a, 0x32, 0x58, 0x5b, 0xa3, 0x23, \
+ 0x8e, 0x2a, 0xa6, 0xaf, 0x03, 0x19, 0x02, 0x7a, \
+ 0x89, 0xc9, 0x8d, 0x28, 0x07, 0xf8, 0xbf, 0xa6, \
+ 0xad, 0xf9, 0xd1, 0xdc, 0xbd, 0x6e, 0xb3, 0xc1, \
+ 0xfb, 0x65, 0xd8, 0x5f, 0x2e, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#endif /* _WIREDATA_FIDO2_H */
diff --git a/fuzz/wiredata_u2f.h b/fuzz/wiredata_u2f.h
new file mode 100644
index 000000000000..afe418fe9d96
--- /dev/null
+++ b/fuzz/wiredata_u2f.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef _WIREDATA_U2F_H
+#define _WIREDATA_U2F_H
+
+#define WIREDATA_CTAP_U2F_6985 \
+ 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69, \
+ 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_U2F_AUTH \
+ 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x4e, 0x01, \
+ 0x00, 0x00, 0x00, 0x2c, 0x30, 0x45, 0x02, 0x20, \
+ 0x1c, 0xf5, 0x7c, 0xf6, 0xde, 0xbe, 0xe9, 0x86, \
+ 0xee, 0x97, 0xb7, 0x64, 0xa3, 0x4e, 0x7a, 0x70, \
+ 0x85, 0xd0, 0x66, 0xf9, 0xf0, 0xcd, 0x04, 0x5d, \
+ 0x97, 0xf2, 0x3c, 0x22, 0xe3, 0x0e, 0x61, 0xc8, \
+ 0x02, 0x21, 0x00, 0x97, 0xef, 0xae, 0x36, 0xe6, \
+ 0x17, 0x9f, 0x5e, 0x2d, 0xd7, 0x8c, 0x34, 0xa7, \
+ 0x00, 0x00, 0x99, 0x01, 0x00, 0xa1, 0xe9, 0xfb, \
+ 0x8f, 0x86, 0x8c, 0xe3, 0x1e, 0xde, 0x3f, 0x4e, \
+ 0x1b, 0xe1, 0x2f, 0x8f, 0x2f, 0xca, 0x42, 0x26, \
+ 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#define WIREDATA_CTAP_U2F_REGISTER \
+ 0x00, 0x00, 0x99, 0x01, 0x83, 0x03, 0x1e, 0x05, \
+ 0x04, 0x9f, 0xa0, 0xf9, 0x0d, 0x4c, 0xf4, 0xae, \
+ 0x96, 0x3c, 0xb7, 0x46, 0xb7, 0x5c, 0x9d, 0x8b, \
+ 0x48, 0x19, 0xdf, 0xc4, 0xad, 0xea, 0xb2, 0x70, \
+ 0x58, 0x72, 0xd9, 0xce, 0x75, 0xf5, 0xe6, 0x8e, \
+ 0x0f, 0x9c, 0x0e, 0x2e, 0x62, 0x3e, 0x91, 0xd3, \
+ 0x7b, 0x97, 0x46, 0x60, 0xb9, 0x57, 0x13, 0x97, \
+ 0x26, 0xae, 0x0f, 0xb3, 0x8f, 0x2e, 0x9b, 0x3f, \
+ 0x00, 0x00, 0x99, 0x01, 0x00, 0xa5, 0x55, 0xec, \
+ 0x8c, 0x25, 0x7c, 0x65, 0xb7, 0x09, 0x40, 0x48, \
+ 0xae, 0xa8, 0xcb, 0xa1, 0x91, 0xac, 0x40, 0x24, \
+ 0xf2, 0x34, 0x6e, 0x3a, 0x8f, 0xa5, 0xb7, 0x48, \
+ 0x54, 0x6e, 0xfb, 0xf4, 0x37, 0x88, 0x69, 0x79, \
+ 0x6f, 0x12, 0xc1, 0x32, 0xdf, 0x15, 0x5d, 0x6e, \
+ 0x82, 0x54, 0xc0, 0x6e, 0x56, 0x4f, 0x3a, 0x9c, \
+ 0xc3, 0x96, 0x7a, 0xde, 0xa5, 0xfe, 0xec, 0xd1, \
+ 0x00, 0x00, 0x99, 0x01, 0x01, 0x5a, 0x21, 0x85, \
+ 0x0e, 0x25, 0x7b, 0x8d, 0x6e, 0x1d, 0x32, 0x29, \
+ 0xdb, 0x21, 0xb0, 0xa3, 0x30, 0x82, 0x02, 0x4f, \
+ 0x30, 0x82, 0x01, 0x37, 0xa0, 0x03, 0x02, 0x01, \
+ 0x02, 0x02, 0x04, 0x2a, 0xd9, 0x6a, 0xf3, 0x30, \
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, \
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x2e, \
+ 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, \
+ 0x00, 0x00, 0x99, 0x01, 0x02, 0x03, 0x13, 0x23, \
+ 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, \
+ 0x32, 0x46, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, \
+ 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, \
+ 0x6c, 0x20, 0x34, 0x35, 0x37, 0x32, 0x30, 0x30, \
+ 0x36, 0x33, 0x31, 0x30, 0x20, 0x17, 0x0d, 0x31, \
+ 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, \
+ 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, \
+ 0x00, 0x00, 0x99, 0x01, 0x03, 0x35, 0x30, 0x30, \
+ 0x39, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, \
+ 0x30, 0x5a, 0x30, 0x31, 0x31, 0x2f, 0x30, 0x2d, \
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x26, 0x59, \
+ 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, \
+ 0x46, 0x20, 0x45, 0x45, 0x20, 0x53, 0x65, 0x72, \
+ 0x69, 0x61, 0x6c, 0x20, 0x32, 0x33, 0x39, 0x32, \
+ 0x35, 0x37, 0x33, 0x34, 0x35, 0x31, 0x36, 0x35, \
+ 0x00, 0x00, 0x99, 0x01, 0x04, 0x35, 0x30, 0x33, \
+ 0x38, 0x37, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, \
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, \
+ 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, \
+ 0x07, 0x03, 0x42, 0x00, 0x04, 0x2f, 0xe1, 0xa2, \
+ 0x3e, 0xbf, 0xa5, 0x5b, 0x3e, 0x46, 0x1d, 0x59, \
+ 0xa4, 0x35, 0x22, 0xd7, 0x97, 0x48, 0x98, 0x1c, \
+ 0xba, 0x6d, 0x28, 0x9a, 0x98, 0xf1, 0xbd, 0x7d, \
+ 0x00, 0x00, 0x99, 0x01, 0x05, 0xff, 0x65, 0x66, \
+ 0x80, 0xdb, 0xbb, 0xed, 0xbc, 0x2b, 0xae, 0x60, \
+ 0x7e, 0x6e, 0xf7, 0x72, 0xf5, 0x76, 0xb0, 0x4d, \
+ 0x54, 0xc4, 0xe5, 0xf3, 0x2f, 0x59, 0x6f, 0x26, \
+ 0xe6, 0x11, 0x15, 0xc7, 0x27, 0x2c, 0xf6, 0xca, \
+ 0x75, 0x94, 0xa3, 0x3b, 0x30, 0x39, 0x30, 0x22, \
+ 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, \
+ 0xc4, 0x0a, 0x02, 0x04, 0x15, 0x31, 0x2e, 0x33, \
+ 0x00, 0x00, 0x99, 0x01, 0x06, 0x2e, 0x36, 0x2e, \
+ 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x31, \
+ 0x34, 0x38, 0x32, 0x2e, 0x31, 0x2e, 0x32, 0x30, \
+ 0x13, 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, \
+ 0x82, 0xe5, 0x1c, 0x02, 0x01, 0x01, 0x04, 0x04, \
+ 0x03, 0x02, 0x04, 0x30, 0x30, 0x0d, 0x06, 0x09, \
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, \
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, \
+ 0x00, 0x00, 0x99, 0x01, 0x07, 0x85, 0x6a, 0xfa, \
+ 0x8b, 0xcf, 0x4f, 0x3f, 0x62, 0x5f, 0x29, 0x1b, \
+ 0xc1, 0x15, 0x8e, 0x3c, 0x7e, 0xbd, 0x25, 0x52, \
+ 0xbc, 0xf7, 0x57, 0x07, 0x53, 0xf5, 0x12, 0x1d, \
+ 0xa6, 0xa5, 0x4d, 0x24, 0xcc, 0xcf, 0xae, 0x27, \
+ 0xce, 0xd6, 0xab, 0x31, 0x12, 0x8c, 0x29, 0x7e, \
+ 0x5b, 0x5b, 0x89, 0x05, 0xdd, 0xa0, 0x20, 0x17, \
+ 0x93, 0x1f, 0x1f, 0x5f, 0x59, 0x25, 0x93, 0x59, \
+ 0x00, 0x00, 0x99, 0x01, 0x08, 0x51, 0xfc, 0x00, \
+ 0x4b, 0xcb, 0xe2, 0x0a, 0xdd, 0x7d, 0x8d, 0x05, \
+ 0x2f, 0x95, 0x43, 0xb3, 0x49, 0x6c, 0x15, 0xb8, \
+ 0x31, 0x0e, 0x10, 0xcb, 0xd9, 0xbb, 0x05, 0x38, \
+ 0x27, 0x4f, 0x58, 0x3e, 0xad, 0x1f, 0x45, 0x12, \
+ 0x88, 0xc3, 0xea, 0x76, 0xd0, 0x70, 0xad, 0x44, \
+ 0xe5, 0x3a, 0xfe, 0xa8, 0xf2, 0x2d, 0x1f, 0x73, \
+ 0x62, 0x5f, 0xf2, 0xd5, 0x89, 0xfe, 0x30, 0xdf, \
+ 0x00, 0x00, 0x99, 0x01, 0x09, 0x26, 0x62, 0xcb, \
+ 0x7c, 0xbb, 0x7c, 0x99, 0x61, 0x80, 0xad, 0xcf, \
+ 0xa9, 0x8a, 0x4d, 0x01, 0x2c, 0xf3, 0x13, 0x46, \
+ 0xcd, 0x11, 0x74, 0x6a, 0x58, 0x48, 0xe8, 0xbe, \
+ 0xed, 0xf3, 0xe3, 0x0c, 0xcb, 0xd9, 0xc1, 0xdd, \
+ 0x22, 0x16, 0x71, 0xb2, 0x83, 0x88, 0x61, 0xf6, \
+ 0x5a, 0x45, 0x36, 0x23, 0xb5, 0x18, 0xd5, 0x56, \
+ 0x7f, 0xa8, 0xf0, 0xa3, 0xce, 0x10, 0x5d, 0xf4, \
+ 0x00, 0x00, 0x99, 0x01, 0x0a, 0xf1, 0x39, 0x53, \
+ 0xe1, 0x14, 0xea, 0x59, 0xe0, 0xa7, 0xf2, 0xfe, \
+ 0x66, 0x88, 0x67, 0x43, 0x2e, 0x52, 0xfd, 0x6a, \
+ 0x2f, 0x64, 0xf7, 0x3c, 0x48, 0xcd, 0x9b, 0x38, \
+ 0xf2, 0xdf, 0xba, 0x2c, 0x7a, 0x4b, 0x3b, 0x11, \
+ 0x28, 0xdf, 0x26, 0xd6, 0x6a, 0x24, 0xf8, 0x95, \
+ 0xdd, 0xa0, 0xb6, 0x11, 0x80, 0xf4, 0x14, 0x4f, \
+ 0x6b, 0x70, 0x75, 0xc3, 0x18, 0xa4, 0x9a, 0xe0, \
+ 0x00, 0x00, 0x99, 0x01, 0x0b, 0x8b, 0x58, 0xd3, \
+ 0x6a, 0xdb, 0x1e, 0x30, 0x53, 0x67, 0x2b, 0x17, \
+ 0xc5, 0xa1, 0x9f, 0x7f, 0x0a, 0x22, 0xf1, 0x0e, \
+ 0x94, 0x30, 0x44, 0x02, 0x20, 0x07, 0x5c, 0x4f, \
+ 0xd2, 0x83, 0xb6, 0x9f, 0x0a, 0x4a, 0x4d, 0x4b, \
+ 0x08, 0x35, 0xeb, 0xc0, 0x7e, 0x4a, 0x14, 0x2e, \
+ 0xc7, 0x8c, 0xd6, 0x64, 0x2f, 0xd3, 0x1e, 0xcc, \
+ 0xb5, 0xe8, 0x42, 0xea, 0xf6, 0x02, 0x20, 0x6b, \
+ 0x00, 0x00, 0x99, 0x01, 0x0c, 0x5a, 0xba, 0x4a, \
+ 0xc8, 0xd7, 0x89, 0xcc, 0x77, 0xe6, 0xb9, 0xa3, \
+ 0x34, 0xea, 0x06, 0x85, 0x72, 0xc6, 0x28, 0xa8, \
+ 0x7a, 0xaa, 0x19, 0x88, 0x34, 0xbb, 0xdc, 0x64, \
+ 0x90, 0x0a, 0xdb, 0x39, 0x90, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+#endif /* !_WIREDATA_U2F_H */
diff --git a/fuzz/wrap.c b/fuzz/wrap.c
new file mode 100644
index 000000000000..5b91a64dbf4b
--- /dev/null
+++ b/fuzz/wrap.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 2019-2021 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <cbor.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mutator_aux.h"
+
+extern int prng_up;
+
+/*
+ * Build wrappers around functions of interest, and have them fail
+ * in a pseudo-random manner.
+ */
+
+#define WRAP(type, name, args, retval, param, prob) \
+extern type __wrap_##name args; \
+extern type __real_##name args; \
+type __wrap_##name args { \
+ if (prng_up && uniform_random(400) < (prob)) { \
+ return (retval); \
+ } \
+ \
+ return (__real_##name param); \
+}
+
+WRAP(void *,
+ malloc,
+ (size_t size),
+ NULL,
+ (size),
+ 1
+)
+
+WRAP(void *,
+ calloc,
+ (size_t nmemb, size_t size),
+ NULL,
+ (nmemb, size),
+ 1
+)
+
+WRAP(char *,
+ strdup,
+ (const char *s),
+ NULL,
+ (s),
+ 1
+)
+
+WRAP(int,
+ EVP_Cipher,
+ (EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in,
+ unsigned int inl),
+ -1,
+ (ctx, out, in, inl),
+ 1
+)
+
+WRAP(int,
+ EVP_CIPHER_CTX_ctrl,
+ (EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr),
+ 0,
+ (ctx, type, arg, ptr),
+ 1
+)
+
+WRAP(EVP_CIPHER_CTX *,
+ EVP_CIPHER_CTX_new,
+ (void),
+ NULL,
+ (),
+ 1
+)
+
+WRAP(int,
+ EVP_EncryptInit_ex,
+ (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl,
+ const unsigned char *key, const unsigned char *iv),
+ 0,
+ (ctx, type, impl, key, iv),
+ 1
+)
+
+WRAP(int,
+ EVP_CIPHER_CTX_set_padding,
+ (EVP_CIPHER_CTX *x, int padding),
+ 0,
+ (x, padding),
+ 1
+)
+
+WRAP(int,
+ EVP_EncryptUpdate,
+ (EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
+ const unsigned char *in, int inl),
+ 0,
+ (ctx, out, outl, in, inl),
+ 1
+)
+
+WRAP(int,
+ EVP_CipherInit,
+ (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+ const unsigned char *key, const unsigned char *iv, int enc),
+ 0,
+ (ctx, cipher, key, iv, enc),
+ 1
+)
+
+WRAP(int,
+ EVP_DecryptInit_ex,
+ (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl,
+ const unsigned char *key, const unsigned char *iv),
+ 0,
+ (ctx, type, impl, key, iv),
+ 1
+)
+
+WRAP(int,
+ EVP_DecryptUpdate,
+ (EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
+ const unsigned char *in, int inl),
+ 0,
+ (ctx, out, outl, in, inl),
+ 1
+)
+
+WRAP(int,
+ SHA256_Init,
+ (SHA256_CTX *c),
+ 0,
+ (c),
+ 1
+)
+
+WRAP(int,
+ SHA256_Update,
+ (SHA256_CTX *c, const void *data, size_t len),
+ 0,
+ (c, data, len),
+ 1
+)
+
+WRAP(int,
+ SHA256_Final,
+ (unsigned char *md, SHA256_CTX *c),
+ 0,
+ (md, c),
+ 1
+)
+
+WRAP(RSA *,
+ EVP_PKEY_get0_RSA,
+ (EVP_PKEY *pkey),
+ NULL,
+ (pkey),
+ 1
+)
+
+WRAP(EC_KEY *,
+ EVP_PKEY_get0_EC_KEY,
+ (EVP_PKEY *pkey),
+ NULL,
+ (pkey),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_get_raw_public_key,
+ (const EVP_PKEY *pkey, unsigned char *pub, size_t *len),
+ 0,
+ (pkey, pub, len),
+ 1
+)
+
+WRAP(EVP_MD_CTX *,
+ EVP_MD_CTX_new,
+ (void),
+ NULL,
+ (),
+ 1
+)
+
+WRAP(int,
+ EVP_DigestVerifyInit,
+ (EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e,
+ EVP_PKEY *pkey),
+ 0,
+ (ctx, pctx, type, e, pkey),
+ 1
+)
+
+WRAP(BIGNUM *,
+ BN_bin2bn,
+ (const unsigned char *s, int len, BIGNUM *ret),
+ NULL,
+ (s, len, ret),
+ 1
+)
+
+WRAP(int,
+ BN_bn2bin,
+ (const BIGNUM *a, unsigned char *to),
+ -1,
+ (a, to),
+ 1
+)
+
+WRAP(BIGNUM *,
+ BN_CTX_get,
+ (BN_CTX *ctx),
+ NULL,
+ (ctx),
+ 1
+)
+
+WRAP(BN_CTX *,
+ BN_CTX_new,
+ (void),
+ NULL,
+ (),
+ 1
+)
+
+WRAP(BIGNUM *,
+ BN_new,
+ (void),
+ NULL,
+ (),
+ 1
+)
+
+WRAP(int,
+ RSA_set0_key,
+ (RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d),
+ 0,
+ (r, n, e, d),
+ 1
+)
+
+WRAP(EC_KEY *,
+ EC_KEY_new_by_curve_name,
+ (int nid),
+ NULL,
+ (nid),
+ 1
+)
+
+WRAP(const EC_GROUP *,
+ EC_KEY_get0_group,
+ (const EC_KEY *key),
+ NULL,
+ (key),
+ 1
+)
+
+WRAP(const BIGNUM *,
+ EC_KEY_get0_private_key,
+ (const EC_KEY *key),
+ NULL,
+ (key),
+ 1
+)
+
+WRAP(EC_POINT *,
+ EC_POINT_new,
+ (const EC_GROUP *group),
+ NULL,
+ (group),
+ 1
+)
+
+WRAP(int,
+ EC_POINT_get_affine_coordinates_GFp,
+ (const EC_GROUP *group, const EC_POINT *p, BIGNUM *x, BIGNUM *y, BN_CTX *ctx),
+ 0,
+ (group, p, x, y, ctx),
+ 1
+)
+
+WRAP(EVP_PKEY *,
+ EVP_PKEY_new,
+ (void),
+ NULL,
+ (),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_assign,
+ (EVP_PKEY *pkey, int type, void *key),
+ 0,
+ (pkey, type, key),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_keygen_init,
+ (EVP_PKEY_CTX *ctx),
+ 0,
+ (ctx),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_keygen,
+ (EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey),
+ 0,
+ (ctx, ppkey),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_paramgen_init,
+ (EVP_PKEY_CTX *ctx),
+ 0,
+ (ctx),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_paramgen,
+ (EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey),
+ 0,
+ (ctx, ppkey),
+ 1
+)
+
+WRAP(EVP_PKEY *,
+ EVP_PKEY_new_raw_public_key,
+ (int type, ENGINE *e, const unsigned char *key, size_t keylen),
+ NULL,
+ (type, e, key, keylen),
+ 1
+)
+
+WRAP(EVP_PKEY_CTX *,
+ EVP_PKEY_CTX_new,
+ (EVP_PKEY *pkey, ENGINE *e),
+ NULL,
+ (pkey, e),
+ 1
+)
+
+WRAP(EVP_PKEY_CTX *,
+ EVP_PKEY_CTX_new_id,
+ (int id, ENGINE *e),
+ NULL,
+ (id, e),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_derive,
+ (EVP_PKEY_CTX *ctx, unsigned char *key, size_t *pkeylen),
+ 0,
+ (ctx, key, pkeylen),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_derive_init,
+ (EVP_PKEY_CTX *ctx),
+ 0,
+ (ctx),
+ 1
+)
+
+WRAP(int,
+ EVP_PKEY_derive_set_peer,
+ (EVP_PKEY_CTX *ctx, EVP_PKEY *peer),
+ 0,
+ (ctx, peer),
+ 1
+)
+
+WRAP(const EVP_MD *,
+ EVP_sha256,
+ (void),
+ NULL,
+ (),
+ 1
+)
+
+WRAP(unsigned char *,
+ HMAC,
+ (const EVP_MD *evp_md, const void *key, int key_len,
+ const unsigned char *d, int n, unsigned char *md,
+ unsigned int *md_len),
+ NULL,
+ (evp_md, key, key_len, d, n, md, md_len),
+ 1
+)
+
+WRAP(HMAC_CTX *,
+ HMAC_CTX_new,
+ (void),
+ NULL,
+ (),
+ 1
+)
+
+WRAP(int,
+ HMAC_Init_ex,
+ (HMAC_CTX *ctx, const void *key, int key_len, const EVP_MD *md,
+ ENGINE *impl),
+ 0,
+ (ctx, key, key_len, md, impl),
+ 1
+)
+
+WRAP(int,
+ HMAC_Update,
+ (HMAC_CTX *ctx, const unsigned char *data, int len),
+ 0,
+ (ctx, data, len),
+ 1
+)
+
+WRAP(int,
+ HMAC_Final,
+ (HMAC_CTX *ctx, unsigned char *md, unsigned int *len),
+ 0,
+ (ctx, md, len),
+ 1
+)
+
+WRAP(unsigned char *,
+ SHA256,
+ (const unsigned char *d, size_t n, unsigned char *md),
+ NULL,
+ (d, n, md),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_build_string,
+ (const char *val),
+ NULL,
+ (val),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_build_bytestring,
+ (cbor_data handle, size_t length),
+ NULL,
+ (handle, length),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_build_bool,
+ (bool value),
+ NULL,
+ (value),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_build_negint8,
+ (uint8_t value),
+ NULL,
+ (value),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_build_negint16,
+ (uint16_t value),
+ NULL,
+ (value),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_load,
+ (cbor_data source, size_t source_size, struct cbor_load_result *result),
+ NULL,
+ (source, source_size, result),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_build_uint8,
+ (uint8_t value),
+ NULL,
+ (value),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_build_uint32,
+ (uint32_t value),
+ NULL,
+ (value),
+ 1
+)
+
+WRAP(struct cbor_pair *,
+ cbor_map_handle,
+ (const cbor_item_t *item),
+ NULL,
+ (item),
+ 1
+)
+
+WRAP(cbor_item_t **,
+ cbor_array_handle,
+ (const cbor_item_t *item),
+ NULL,
+ (item),
+ 1
+)
+
+WRAP(bool,
+ cbor_array_push,
+ (cbor_item_t *array, cbor_item_t *pushee),
+ false,
+ (array, pushee),
+ 1
+)
+
+WRAP(bool,
+ cbor_map_add,
+ (cbor_item_t *item, struct cbor_pair pair),
+ false,
+ (item, pair),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_new_definite_map,
+ (size_t size),
+ NULL,
+ (size),
+ 1
+)
+
+WRAP(cbor_item_t *,
+ cbor_new_definite_array,
+ (size_t size),
+ NULL,
+ (size),
+ 1
+)
+
+WRAP(size_t,
+ cbor_serialize_alloc,
+ (const cbor_item_t *item, cbor_mutable_data *buffer,
+ size_t *buffer_size),
+ 0,
+ (item, buffer, buffer_size),
+ 1
+)
+
+WRAP(int,
+ fido_tx,
+ (fido_dev_t *d, uint8_t cmd, const void *buf, size_t count),
+ -1,
+ (d, cmd, buf, count),
+ 1
+)
+
+WRAP(int,
+ usleep,
+ (unsigned int usec),
+ -1,
+ (usec),
+ 1
+)
diff --git a/fuzz/wrapped.sym b/fuzz/wrapped.sym
new file mode 100644
index 000000000000..de4f24ae0355
--- /dev/null
+++ b/fuzz/wrapped.sym
@@ -0,0 +1,83 @@
+BN_bin2bn
+BN_bn2bin
+BN_CTX_get
+BN_CTX_new
+BN_new
+calloc
+cbor_array_handle
+cbor_array_push
+cbor_build_bool
+cbor_build_bytestring
+cbor_build_negint16
+cbor_build_negint8
+cbor_build_string
+cbor_build_uint32
+cbor_build_uint8
+cbor_load
+cbor_map_add
+cbor_map_handle
+cbor_new_definite_array
+cbor_new_definite_map
+cbor_serialize_alloc
+EC_KEY_get0_group
+EC_KEY_get0_private_key
+EC_KEY_new_by_curve_name
+EC_POINT_get_affine_coordinates_GFp
+EC_POINT_new
+EVP_Cipher
+EVP_CIPHER_CTX_ctrl
+EVP_CIPHER_CTX_new
+EVP_CIPHER_CTX_set_padding
+EVP_CipherInit
+EVP_DecryptInit_ex
+EVP_DecryptUpdate
+EVP_DigestVerifyInit
+EVP_EncryptInit_ex
+EVP_EncryptUpdate
+EVP_MD_CTX_new
+EVP_PKEY_assign
+EVP_PKEY_CTX_new
+EVP_PKEY_CTX_new_id
+EVP_PKEY_derive
+EVP_PKEY_derive_init
+EVP_PKEY_derive_set_peer
+EVP_PKEY_get0_EC_KEY
+EVP_PKEY_get0_RSA
+EVP_PKEY_get_raw_public_key
+EVP_PKEY_keygen
+EVP_PKEY_keygen_init
+EVP_PKEY_new
+EVP_PKEY_new_raw_public_key
+EVP_PKEY_paramgen
+EVP_PKEY_paramgen_init
+EVP_sha256
+fido_tx
+HMAC
+HMAC_CTX_new
+HMAC_Final
+HMAC_Init_ex
+HMAC_Update
+ioctl
+malloc
+RSA_set0_key
+SHA256
+SHA256_Final
+SHA256_Init
+SHA256_Update
+strdup
+udev_device_get_devnode
+udev_device_get_parent_with_subsystem_devtype
+udev_device_get_sysattr_value
+udev_device_get_sysnum
+udev_device_new_from_syspath
+udev_device_unref
+udev_enumerate_add_match_subsystem
+udev_enumerate_get_list_entry
+udev_enumerate_new
+udev_enumerate_scan_devices
+udev_enumerate_unref
+udev_list_entry_get_name
+udev_list_entry_get_next
+udev_new
+udev_unref
+usleep
diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt
new file mode 100644
index 000000000000..ad9f339e6f9b
--- /dev/null
+++ b/man/CMakeLists.txt
@@ -0,0 +1,371 @@
+# Copyright (c) 2018 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+find_program(MANDOC_PATH mandoc)
+find_program(GZIP_PATH gzip)
+
+message(STATUS "MANDOC_PATH: ${MANDOC_PATH}")
+message(STATUS "GZIP_PATH: ${GZIP_PATH}")
+
+list(APPEND MAN_SOURCES
+ eddsa_pk_new.3
+ es256_pk_new.3
+ fido2-assert.1
+ fido2-cred.1
+ fido2-token.1
+ fido_init.3
+ fido_assert_new.3
+ fido_assert_allow_cred.3
+ fido_assert_set_authdata.3
+ fido_assert_verify.3
+ fido_bio_dev_get_info.3
+ fido_bio_enroll_new.3
+ fido_bio_info_new.3
+ fido_bio_template.3
+ fido_cbor_info_new.3
+ fido_cred_new.3
+ fido_cred_exclude.3
+ fido_credman_metadata_new.3
+ fido_cred_set_authdata.3
+ fido_cred_verify.3
+ fido_dev_enable_entattest.3
+ fido_dev_get_assert.3
+ fido_dev_get_touch_begin.3
+ fido_dev_info_manifest.3
+ fido_dev_largeblob_get.3
+ fido_dev_make_cred.3
+ fido_dev_open.3
+ fido_dev_set_io_functions.3
+ fido_dev_set_pin.3
+ fido_strerr.3
+ rs256_pk_new.3
+)
+
+list(APPEND MAN_ALIAS
+ eddsa_pk_new eddsa_pk_free
+ eddsa_pk_new eddsa_pk_from_ptr
+ eddsa_pk_new eddsa_pk_to_EVP_PKEY
+ es256_pk_new es256_pk_free
+ es256_pk_new es256_pk_from_EC_KEY
+ es256_pk_new es256_pk_from_ptr
+ es256_pk_new es256_pk_to_EVP_PKEY
+ fido_assert_new fido_assert_authdata_len
+ fido_assert_new fido_assert_authdata_ptr
+ fido_assert_new fido_assert_blob_len
+ fido_assert_new fido_assert_blob_ptr
+ fido_assert_new fido_assert_clientdata_hash_len
+ fido_assert_new fido_assert_clientdata_hash_ptr
+ fido_assert_new fido_assert_count
+ fido_assert_new fido_assert_flags
+ fido_assert_new fido_assert_free
+ fido_assert_new fido_assert_hmac_secret_len
+ fido_assert_new fido_assert_hmac_secret_ptr
+ fido_assert_new fido_assert_id_len
+ fido_assert_new fido_assert_id_ptr
+ fido_assert_new fido_assert_largeblob_key_len
+ fido_assert_new fido_assert_largeblob_key_ptr
+ fido_assert_new fido_assert_rp_id
+ fido_assert_new fido_assert_sigcount
+ fido_assert_new fido_assert_sig_len
+ fido_assert_new fido_assert_sig_ptr
+ fido_assert_new fido_assert_user_display_name
+ fido_assert_new fido_assert_user_icon
+ fido_assert_new fido_assert_user_id_len
+ fido_assert_new fido_assert_user_id_ptr
+ fido_assert_new fido_assert_user_name
+ fido_assert_set_authdata fido_assert_set_clientdata
+ fido_assert_set_authdata fido_assert_set_clientdata_hash
+ fido_assert_set_authdata fido_assert_set_count
+ fido_assert_set_authdata fido_assert_set_extensions
+ fido_assert_set_authdata fido_assert_set_hmac_salt
+ fido_assert_set_authdata fido_assert_set_hmac_secret
+ fido_assert_set_authdata fido_assert_set_rp
+ fido_assert_set_authdata fido_assert_set_sig
+ fido_assert_set_authdata fido_assert_set_up
+ fido_assert_set_authdata fido_assert_set_uv
+ fido_bio_dev_get_info fido_bio_dev_enroll_begin
+ fido_bio_dev_get_info fido_bio_dev_enroll_cancel
+ fido_bio_dev_get_info fido_bio_dev_enroll_continue
+ fido_bio_dev_get_info fido_bio_dev_enroll_remove
+ fido_bio_dev_get_info fido_bio_dev_get_template_array
+ fido_bio_dev_get_info fido_bio_dev_set_template_name
+ fido_bio_enroll_new fido_bio_enroll_free
+ fido_bio_enroll_new fido_bio_enroll_last_status
+ fido_bio_enroll_new fido_bio_enroll_remaining_samples
+ fido_bio_info_new fido_bio_info_free
+ fido_bio_info_new fido_bio_info_max_samples
+ fido_bio_info_new fido_bio_info_type
+ fido_bio_template fido_bio_template_array_count
+ fido_bio_template fido_bio_template_array_free
+ fido_bio_template fido_bio_template_array_new
+ fido_bio_template fido_bio_template_free
+ fido_bio_template fido_bio_template_id_len
+ fido_bio_template fido_bio_template_id_ptr
+ fido_bio_template fido_bio_template_name
+ fido_bio_template fido_bio_template_new
+ fido_bio_template fido_bio_template_set_id
+ fido_bio_template fido_bio_template_set_name
+ fido_cbor_info_new fido_cbor_info_aaguid_len
+ fido_cbor_info_new fido_cbor_info_aaguid_ptr
+ fido_cbor_info_new fido_cbor_info_algorithm_cose
+ fido_cbor_info_new fido_cbor_info_algorithm_count
+ fido_cbor_info_new fido_cbor_info_algorithm_type
+ fido_cbor_info_new fido_cbor_info_extensions_len
+ fido_cbor_info_new fido_cbor_info_extensions_ptr
+ fido_cbor_info_new fido_cbor_info_free
+ fido_cbor_info_new fido_cbor_info_maxmsgsiz
+ fido_cbor_info_new fido_cbor_info_maxcredbloblen
+ fido_cbor_info_new fido_cbor_info_maxcredcntlst;
+ fido_cbor_info_new fido_cbor_info_maxcredidlen;
+ fido_cbor_info_new fido_cbor_info_fwversion
+ fido_cbor_info_new fido_cbor_info_options_len
+ fido_cbor_info_new fido_cbor_info_options_name_ptr
+ fido_cbor_info_new fido_cbor_info_options_value_ptr
+ fido_cbor_info_new fido_cbor_info_protocols_len
+ fido_cbor_info_new fido_cbor_info_protocols_ptr
+ fido_cbor_info_new fido_cbor_info_transports_len
+ fido_cbor_info_new fido_cbor_info_transports_ptr
+ fido_cbor_info_new fido_cbor_info_versions_len
+ fido_cbor_info_new fido_cbor_info_versions_ptr
+ fido_cbor_info_new fido_dev_get_cbor_info
+ fido_cred_new fido_cred_authdata_len
+ fido_cred_new fido_cred_authdata_ptr
+ fido_cred_new fido_cred_authdata_raw_len
+ fido_cred_new fido_cred_authdata_raw_ptr
+ fido_cred_new fido_cred_clientdata_hash_len
+ fido_cred_new fido_cred_clientdata_hash_ptr
+ fido_cred_new fido_cred_display_name
+ fido_cred_new fido_cred_flags
+ fido_cred_new fido_cred_sigcount
+ fido_cred_new fido_cred_fmt
+ fido_cred_new fido_cred_free
+ fido_cred_new fido_cred_id_len
+ fido_cred_new fido_cred_id_ptr
+ fido_cred_new fido_cred_aaguid_len
+ fido_cred_new fido_cred_aaguid_ptr
+ fido_cred_new fido_cred_largeblob_key_len
+ fido_cred_new fido_cred_largeblob_key_ptr
+ fido_cred_new fido_cred_prot
+ fido_cred_new fido_cred_pubkey_len
+ fido_cred_new fido_cred_pubkey_ptr
+ fido_cred_new fido_cred_rp_id
+ fido_cred_new fido_cred_rp_name
+ fido_cred_new fido_cred_sig_len
+ fido_cred_new fido_cred_sig_ptr
+ fido_cred_new fido_cred_type
+ fido_cred_new fido_cred_user_name
+ fido_cred_new fido_cred_user_id_len
+ fido_cred_new fido_cred_user_id_ptr
+ fido_cred_new fido_cred_x5c_len
+ fido_cred_new fido_cred_x5c_ptr
+ fido_credman_metadata_new fido_credman_del_dev_rk
+ fido_credman_metadata_new fido_credman_get_dev_metadata
+ fido_credman_metadata_new fido_credman_get_dev_rk
+ fido_credman_metadata_new fido_credman_get_dev_rp
+ fido_credman_metadata_new fido_credman_metadata_free
+ fido_credman_metadata_new fido_credman_rk
+ fido_credman_metadata_new fido_credman_rk_count
+ fido_credman_metadata_new fido_credman_rk_existing
+ fido_credman_metadata_new fido_credman_rk_free
+ fido_credman_metadata_new fido_credman_rk_new
+ fido_credman_metadata_new fido_credman_rk_remaining
+ fido_credman_metadata_new fido_credman_rp_count
+ fido_credman_metadata_new fido_credman_rp_free
+ fido_credman_metadata_new fido_credman_rp_id
+ fido_credman_metadata_new fido_credman_rp_id_hash_len
+ fido_credman_metadata_new fido_credman_rp_id_hash_ptr
+ fido_credman_metadata_new fido_credman_rp_name
+ fido_credman_metadata_new fido_credman_rp_new
+ fido_credman_metadata_new fido_credman_set_dev_rk
+ fido_cred_set_authdata fido_cred_set_authdata_raw
+ fido_cred_set_authdata fido_cred_set_blob
+ fido_cred_set_authdata fido_cred_set_clientdata
+ fido_cred_set_authdata fido_cred_set_clientdata_hash
+ fido_cred_set_authdata fido_cred_set_extensions
+ fido_cred_set_authdata fido_cred_set_fmt
+ fido_cred_set_authdata fido_cred_set_id
+ fido_cred_set_authdata fido_cred_set_prot
+ fido_cred_set_authdata fido_cred_set_rk
+ fido_cred_set_authdata fido_cred_set_rp
+ fido_cred_set_authdata fido_cred_set_sig
+ fido_cred_set_authdata fido_cred_set_type
+ fido_cred_set_authdata fido_cred_set_user
+ fido_cred_set_authdata fido_cred_set_uv
+ fido_cred_set_authdata fido_cred_set_x509
+ fido_dev_enable_entattest fido_dev_toggle_always_uv
+ fido_dev_enable_entattest fido_dev_force_pin_change
+ fido_dev_enable_entattest fido_dev_set_pin_minlen
+ fido_dev_get_touch_begin fido_dev_get_touch_status
+ fido_dev_info_manifest fido_dev_info_free
+ fido_dev_info_manifest fido_dev_info_manufacturer_string
+ fido_dev_info_manifest fido_dev_info_new
+ fido_dev_info_manifest fido_dev_info_path
+ fido_dev_info_manifest fido_dev_info_product
+ fido_dev_info_manifest fido_dev_info_product_string
+ fido_dev_info_manifest fido_dev_info_ptr
+ fido_dev_info_manifest fido_dev_info_vendor
+ fido_dev_open fido_dev_build
+ fido_dev_open fido_dev_cancel
+ fido_dev_open fido_dev_close
+ fido_dev_open fido_dev_flags
+ fido_dev_open fido_dev_force_fido2
+ fido_dev_open fido_dev_force_u2f
+ fido_dev_open fido_dev_free
+ fido_dev_open fido_dev_is_fido2
+ fido_dev_open fido_dev_is_winhello
+ fido_dev_open fido_dev_major
+ fido_dev_open fido_dev_minor
+ fido_dev_open fido_dev_new
+ fido_dev_open fido_dev_protocol
+ fido_dev_open fido_dev_supports_cred_prot
+ fido_dev_open fido_dev_supports_credman
+ fido_dev_open fido_dev_supports_pin
+ fido_dev_open fido_dev_supports_uv
+ fido_dev_open fido_dev_has_uv
+ fido_dev_set_pin fido_dev_get_retry_count
+ fido_dev_set_pin fido_dev_get_uv_retry_count
+ fido_dev_set_pin fido_dev_reset
+ fido_dev_set_io_functions fido_dev_set_sigmask
+ fido_dev_largeblob_get fido_dev_largeblob_set
+ fido_dev_largeblob_get fido_dev_largeblob_remove
+ fido_dev_largeblob_get fido_dev_largeblob_get_array
+ fido_dev_largeblob_get fido_dev_largeblob_set_array
+ rs256_pk_new rs256_pk_free
+ rs256_pk_new rs256_pk_from_ptr
+ rs256_pk_new rs256_pk_from_RSA
+ rs256_pk_new rs256_pk_to_EVP_PKEY
+)
+
+list(LENGTH MAN_ALIAS MAN_ALIAS_LEN)
+math(EXPR MAN_ALIAS_MAX "${MAN_ALIAS_LEN} - 2")
+
+# man_copy
+foreach(f ${MAN_SOURCES})
+ add_custom_command(OUTPUT ${f}
+ COMMAND cp -f ${CMAKE_SOURCE_DIR}/man/${f} .
+ DEPENDS ${f})
+ list(APPEND COPY_FILES ${f})
+endforeach()
+
+# man_lint
+foreach(f ${MAN_SOURCES})
+ add_custom_command(OUTPUT ${f}.lint
+ COMMAND mandoc -T lint -W warning ${f} > ${f}.lint
+ DEPENDS ${f})
+ list(APPEND LINT_FILES ${f}.lint)
+endforeach()
+
+# man_html
+foreach(f ${MAN_SOURCES})
+ string(REGEX REPLACE ".[13]" "" g ${f})
+ add_custom_command(OUTPUT ${g}.html
+ COMMAND mandoc -T html -O man="%N.html",style=style.css -I os="Yubico AB" ${f} > ${g}.html
+ DEPENDS ${f})
+ list(APPEND HTML_FILES ${g}.html)
+endforeach()
+
+# man_html_partial
+foreach(f ${MAN_SOURCES})
+ string(REGEX REPLACE ".[13]" "" g ${f})
+ add_custom_command(OUTPUT ${g}.partial
+ COMMAND cat ${CMAKE_SOURCE_DIR}/man/dyc.css > ${g}.partial
+ COMMAND mandoc -T html -O man="%N.html",fragment ${f} >> ${g}.partial
+ DEPENDS ${f})
+ list(APPEND HTML_PARTIAL_FILES ${g}.partial)
+endforeach()
+
+# man_gzip
+foreach(f ${MAN_SOURCES})
+ add_custom_command(OUTPUT ${f}.gz
+ COMMAND gzip -cn ${f} > ${f}.gz
+ DEPENDS ${f})
+ list(APPEND GZ_FILES ${f}.gz)
+endforeach()
+
+macro(define_symlink_target NAME EXT)
+ foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2)
+ math(EXPR j "${i} + 1")
+ list(GET MAN_ALIAS ${i} SRC)
+ list(GET MAN_ALIAS ${j} DST)
+ add_custom_command(OUTPUT ${DST}.${EXT}
+ COMMAND ln -sf ${SRC}.${EXT} ${DST}.${EXT})
+ list(APPEND ${NAME}_LINK_FILES ${DST}.${EXT})
+ endforeach()
+ add_custom_target(${NAME} DEPENDS ${${NAME}_LINK_FILES})
+endmacro()
+
+add_custom_target(man_copy DEPENDS ${COPY_FILES})
+add_custom_target(man_lint DEPENDS ${LINT_FILES})
+add_custom_target(man_html DEPENDS ${HTML_FILES})
+add_custom_target(man_html_partial DEPENDS ${HTML_PARTIAL_FILES})
+add_custom_target(man_gzip DEPENDS ${GZ_FILES})
+
+define_symlink_target(man_symlink 3)
+define_symlink_target(man_symlink_html html)
+define_symlink_target(man_symlink_html_partial partial)
+define_symlink_target(man_symlink_gzip 3.gz)
+
+add_dependencies(man_symlink man_copy)
+add_dependencies(man_lint man_symlink)
+add_dependencies(man_html man_lint)
+add_dependencies(man_symlink_html man_html)
+add_dependencies(man_html_partial man_lint)
+add_dependencies(man_symlink_html_partial man_html_partial)
+add_custom_target(man ALL)
+
+if(MANDOC_PATH)
+ add_dependencies(man man_symlink_html)
+ add_dependencies(man_gzip man_lint)
+ install(FILES ${CMAKE_SOURCE_DIR}/man/style.css
+ DESTINATION "${CMAKE_INSTALL_DOCDIR}/html")
+ foreach(f ${MAN_SOURCES})
+ string(REGEX REPLACE ".[13]" "" f ${f})
+ install(FILES ${CMAKE_BINARY_DIR}/man/${f}.html
+ DESTINATION "${CMAKE_INSTALL_DOCDIR}/html")
+ endforeach()
+ foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2)
+ math(EXPR j "${i} + 1")
+ list(GET MAN_ALIAS ${j} DST)
+ install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.html
+ DESTINATION "${CMAKE_INSTALL_DOCDIR}/html")
+ endforeach()
+endif()
+
+if(GZIP_PATH)
+ add_dependencies(man_gzip man_copy)
+ add_dependencies(man_symlink_gzip man_gzip)
+ add_dependencies(man man_symlink_gzip)
+ foreach(f ${MAN_SOURCES})
+ if (${f} MATCHES ".1$")
+ install(FILES ${CMAKE_BINARY_DIR}/man/${f}.gz
+ DESTINATION "${CMAKE_INSTALL_MANDIR}/man1")
+ elseif(${f} MATCHES ".3$")
+ install(FILES ${CMAKE_BINARY_DIR}/man/${f}.gz
+ DESTINATION "${CMAKE_INSTALL_MANDIR}/man3")
+ endif()
+ endforeach()
+ foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2)
+ math(EXPR j "${i} + 1")
+ list(GET MAN_ALIAS ${j} DST)
+ install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.3.gz
+ DESTINATION "${CMAKE_INSTALL_MANDIR}/man3")
+ endforeach()
+elseif(NOT MSVC)
+ add_dependencies(man man_symlink)
+ foreach(f ${MAN_SOURCES})
+ if (${f} MATCHES ".1$")
+ install(FILES ${CMAKE_BINARY_DIR}/man/${f}
+ DESTINATION "${CMAKE_INSTALL_MANDIR}/man1")
+ elseif(${f} MATCHES ".3$")
+ install(FILES ${CMAKE_BINARY_DIR}/man/${f}
+ DESTINATION "${CMAKE_INSTALL_MANDIR}/man3")
+ endif()
+ endforeach()
+ foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2)
+ math(EXPR j "${i} + 1")
+ list(GET MAN_ALIAS ${j} DST)
+ install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.3
+ DESTINATION "${CMAKE_INSTALL_MANDIR}/man3")
+ endforeach()
+endif()
diff --git a/man/NOTES b/man/NOTES
new file mode 100644
index 000000000000..5cba43663b7f
--- /dev/null
+++ b/man/NOTES
@@ -0,0 +1,7 @@
+To generate .partial files for https://developers.yubico.com/:
+
+$ make -C build man_symlink_html_partial
+$ (cd build/man && pax -p p -r -w *.partial /tmp/partial)
+
+Use mandoc 1.14.4. Otherwise, adjust dyc.css to mandoc's HTML
+output.
diff --git a/man/dyc.css b/man/dyc.css
new file mode 100644
index 000000000000..1ff5b593e8d2
--- /dev/null
+++ b/man/dyc.css
@@ -0,0 +1,14 @@
+<style>
+ table.head, table.foot { width: 100%; }
+ td.head-rtitle, td.foot-os { text-align: right; }
+ td.head-vol { text-align: center; }
+ div.Pp { margin: 1ex 0ex; }
+ div.Nd, div.Bf, div.Op { display: inline; }
+ span.Pa, span.Ad { font-style: italic; }
+ span.Ms { font-weight: bold; }
+ dl.Bl-diag > dt { font-weight: bold; }
+ code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
+ code.Cd { font-weight: bold; font-family: monospace; }
+ var { font-family: monospace; }
+ .Sh { font-size: 1.5em; padding-top: 1em; padding-bottom: 1em; }
+</style>
diff --git a/man/eddsa_pk_new.3 b/man/eddsa_pk_new.3
new file mode 100644
index 000000000000..65bf9a9f753d
--- /dev/null
+++ b/man/eddsa_pk_new.3
@@ -0,0 +1,122 @@
+.\" Copyright (c) 2019 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 15 2019 $
+.Dt EDDSA_PK_NEW 3
+.Os
+.Sh NAME
+.Nm eddsa_pk_new ,
+.Nm eddsa_pk_free ,
+.Nm eddsa_pk_from_EVP_PKEY ,
+.Nm eddsa_pk_from_ptr ,
+.Nm eddsa_pk_to_EVP_PKEY
+.Nd FIDO 2 COSE EDDSA API
+.Sh SYNOPSIS
+.In openssl/evp.h
+.In fido/eddsa.h
+.Ft eddsa_pk_t *
+.Fn eddsa_pk_new "void"
+.Ft void
+.Fn eddsa_pk_free "eddsa_pk_t **pkp"
+.Ft int
+.Fn eddsa_pk_from_EVP_PKEY "eddsa_pk_t *pk" "const EVP_PKEY *pkey"
+.Ft int
+.Fn eddsa_pk_from_ptr "eddsa_pk_t *pk" "const void *ptr" "size_t len"
+.Ft EVP_PKEY *
+.Fn eddsa_pk_to_EVP_PKEY "const eddsa_pk_t *pk"
+.Sh DESCRIPTION
+EDDSA is the name given in the CBOR Object Signing and Encryption
+(COSE) RFC to EDDSA over Curve25519 with SHA-512.
+The COSE EDDSA API of
+.Em libfido2
+is an auxiliary API with routines to convert between the different
+EDDSA public key types used in
+.Em libfido2
+and
+.Em OpenSSL .
+.Pp
+In
+.Em libfido2 ,
+EDDSA public keys are abstracted by the
+.Vt eddsa_pk_t
+type.
+.Pp
+The
+.Fn eddsa_pk_new
+function returns a pointer to a newly allocated, empty
+.Vt eddsa_pk_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn eddsa_pk_free
+function releases the memory backing
+.Fa *pkp ,
+where
+.Fa *pkp
+must have been previously allocated by
+.Fn eddsa_pk_new .
+On return,
+.Fa *pkp
+is set to NULL.
+Either
+.Fa pkp
+or
+.Fa *pkp
+may be NULL, in which case
+.Fn eddsa_pk_free
+is a NOP.
+.Pp
+The
+.Fn eddsa_pk_from_EVP_PKEY
+function fills
+.Fa pk
+with the contents of
+.Fa pkey .
+No references to
+.Fa pkey
+are kept.
+.Pp
+The
+.Fn eddsa_pk_from_ptr
+function fills
+.Fa pk
+with the contents of
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+No references to
+.Fa ptr
+are kept.
+.Pp
+The
+.Fn eddsa_pk_to_EVP_PKEY
+function converts
+.Fa pk
+to a newly allocated
+.Fa EVP_PKEY
+type with a reference count of 1.
+No internal references to the returned pointer are kept.
+If an error occurs,
+.Fn eddsa_pk_to_EVP_PKEY
+returns NULL.
+.Sh RETURN VALUES
+The
+.Fn eddsa_pk_from_EC_KEY
+and
+.Fn eddsa_pk_from_ptr
+functions return
+.Dv FIDO_OK
+on success.
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr es256_pk_new 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_cred_pubkey_ptr 3 ,
+.Xr rs256_pk_new 3
diff --git a/man/es256_pk_new.3 b/man/es256_pk_new.3
new file mode 100644
index 000000000000..54439cd300cf
--- /dev/null
+++ b/man/es256_pk_new.3
@@ -0,0 +1,126 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 24 2018 $
+.Dt ES256_PK_NEW 3
+.Os
+.Sh NAME
+.Nm es256_pk_new ,
+.Nm es256_pk_free ,
+.Nm es256_pk_from_EC_KEY ,
+.Nm es256_pk_from_ptr ,
+.Nm es256_pk_to_EVP_PKEY
+.Nd FIDO 2 COSE ES256 API
+.Sh SYNOPSIS
+.In openssl/ec.h
+.In fido/es256.h
+.Ft es256_pk_t *
+.Fn es256_pk_new "void"
+.Ft void
+.Fn es256_pk_free "es256_pk_t **pkp"
+.Ft int
+.Fn es256_pk_from_EC_KEY "es256_pk_t *pk" "const EC_KEY *ec"
+.Ft int
+.Fn es256_pk_from_ptr "es256_pk_t *pk" "const void *ptr" "size_t len"
+.Ft EVP_PKEY *
+.Fn es256_pk_to_EVP_PKEY "const es256_pk_t *pk"
+.Sh DESCRIPTION
+ES256 is the name given in the CBOR Object Signing and Encryption
+(COSE) RFC to ECDSA over P-256 with SHA-256.
+The COSE ES256 API of
+.Em libfido2
+is an auxiliary API with routines to convert between the different
+ECDSA public key types used in
+.Em libfido2
+and
+.Em OpenSSL .
+.Pp
+In
+.Em libfido2 ,
+ES256 public keys are abstracted by the
+.Vt es256_pk_t
+type.
+.Pp
+The
+.Fn es256_pk_new
+function returns a pointer to a newly allocated, empty
+.Vt es256_pk_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn es256_pk_free
+function releases the memory backing
+.Fa *pkp ,
+where
+.Fa *pkp
+must have been previously allocated by
+.Fn es256_pk_new .
+On return,
+.Fa *pkp
+is set to NULL.
+Either
+.Fa pkp
+or
+.Fa *pkp
+may be NULL, in which case
+.Fn es256_pk_free
+is a NOP.
+.Pp
+The
+.Fn es256_pk_from_EC_KEY
+function fills
+.Fa pk
+with the contents of
+.Fa ec .
+No references to
+.Fa ec
+are kept.
+.Pp
+The
+.Fn es256_pk_from_ptr
+function fills
+.Fa pk
+with the contents of
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+The
+.Fa ptr
+pointer may point to an uncompressed point, or to the
+concatenation of the x and y coordinates.
+No references to
+.Fa ptr
+are kept.
+.Pp
+The
+.Fn es256_pk_to_EVP_PKEY
+function converts
+.Fa pk
+to a newly allocated
+.Fa EVP_PKEY
+type with a reference count of 1.
+No internal references to the returned pointer are kept.
+If an error occurs,
+.Fn es256_pk_to_EVP_PKEY
+returns NULL.
+.Sh RETURN VALUES
+The
+.Fn es256_pk_from_EC_KEY
+and
+.Fn es256_pk_from_ptr
+functions return
+.Dv FIDO_OK
+on success.
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr eddsa_pk_new 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_cred_pubkey_ptr 3 ,
+.Xr rs256_pk_new 3
diff --git a/man/fido2-assert.1 b/man/fido2-assert.1
new file mode 100644
index 000000000000..da47d6f19dd3
--- /dev/null
+++ b/man/fido2-assert.1
@@ -0,0 +1,256 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: November 5 2019 $
+.Dt FIDO2-ASSERT 1
+.Os
+.Sh NAME
+.Nm fido2-assert
+.Nd get/verify a FIDO 2 assertion
+.Sh SYNOPSIS
+.Nm
+.Fl G
+.Op Fl bdhpruv
+.Op Fl t Ar option
+.Op Fl i Ar input_file
+.Op Fl o Ar output_file
+.Ar device
+.Nm
+.Fl V
+.Op Fl dhpv
+.Op Fl i Ar input_file
+.Ar key_file
+.Op Ar type
+.Sh DESCRIPTION
+.Nm
+gets or verifies a FIDO 2 assertion.
+.Pp
+The input of
+.Nm
+is defined by the parameters of the assertion to be obtained/verified.
+See the
+.Sx INPUT FORMAT
+section for details.
+.Pp
+The output of
+.Nm
+is defined by the result of the selected operation.
+See the
+.Sx OUTPUT FORMAT
+section for details.
+.Pp
+If an assertion is successfully obtained or verified,
+.Nm
+exits 0.
+Otherwise,
+.Nm
+exits 1.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl G
+Tells
+.Nm
+to obtain a new assertion from
+.Ar device .
+.It Fl V
+Tells
+.Nm
+to verify an assertion using the PEM-encoded public key in
+.Ar key_file
+of type
+.Ar type ,
+where
+.Ar type
+may be
+.Em es256
+(denoting ECDSA over NIST P-256 with SHA-256),
+.Em rs256
+(denoting 2048-bit RSA with PKCS#1.5 padding and SHA-256), or
+.Em eddsa
+(denoting EDDSA over Curve25519 with SHA-512).
+If
+.Ar type
+is not specified,
+.Em es256
+is assumed.
+.It Fl b
+Request the credential's
+.Dq largeBlobKey ,
+a 32-byte symmetric key associated with the asserted credential.
+.It Fl h
+If obtaining an assertion, enable the FIDO2 hmac-secret
+extension.
+If verifying an assertion, check whether the extension data bit was
+signed by the authenticator.
+.It Fl d
+Causes
+.Nm
+to emit debugging output on
+.Em stderr .
+.It Fl i Ar input_file
+Tells
+.Nm
+to read the parameters of the assertion from
+.Ar input_file
+instead of
+.Em stdin .
+.It Fl o Ar output_file
+Tells
+.Nm
+to write output on
+.Ar output_file
+instead of
+.Em stdout .
+.It Fl p
+If obtaining an assertion, request user presence.
+If verifying an assertion, check whether the user presence bit was
+signed by the authenticator.
+.It Fl r
+Obtain an assertion using a resident credential.
+If
+.Fl r
+is specified,
+.Nm
+will not expect a credential id in its input, and may output
+multiple assertions.
+Resident credentials are called
+.Dq discoverable credentials
+in FIDO 2.1.
+.It Fl t Ar option
+Toggles a key/value
+.Ar option ,
+where
+.Ar option
+is a string of the form
+.Dq key=value .
+The options supported at present are:
+.Bl -tag -width Ds
+.It Cm up Ns = Ns Ar true|false
+Asks the authenticator for user presence to be enabled or disabled.
+.It Cm uv Ns = Ns Ar true|false
+Asks the authenticator for user verification to be enabled or
+disabled.
+.It Cm pin Ns = Ns Ar true|false
+Tells
+.Nm
+whether to prompt for a PIN and request user verification.
+.El
+.Pp
+The
+.Fl t
+option may be specified multiple times.
+.It Fl u
+Obtain an assertion using U2F.
+By default,
+.Nm
+will use FIDO2 if supported by the authenticator, and fallback to
+U2F otherwise.
+.It Fl v
+If obtaining an assertion, prompt the user for a PIN and request
+user verification from the authenticator.
+If verifying an assertion, check whether the user verification bit
+was signed by the authenticator.
+.El
+.Pp
+If a
+.Em tty
+is available,
+.Nm
+will use it to obtain the PIN.
+Otherwise,
+.Em stdin
+is used.
+.Sh INPUT FORMAT
+The input of
+.Nm
+consists of base64 blobs and UTF-8 strings separated
+by newline characters ('\\n').
+.Pp
+When obtaining an assertion,
+.Nm
+expects its input to consist of:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+credential id, if credential not resident (base64 blob);
+.It
+hmac salt, if the FIDO2 hmac-secret extension is enabled
+(base64 blob);
+.El
+.Pp
+When verifying an assertion,
+.Nm
+expects its input to consist of:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+authenticator data (base64 blob);
+.It
+assertion signature (base64 blob);
+.El
+.Pp
+UTF-8 strings passed to
+.Nm
+must not contain embedded newline or NUL characters.
+.Sh OUTPUT FORMAT
+The output of
+.Nm
+consists of base64 blobs and UTF-8 strings separated
+by newline characters ('\\n').
+.Pp
+For each generated assertion,
+.Nm
+outputs:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+authenticator data (base64 blob);
+.It
+assertion signature (base64 blob);
+.It
+user id, if credential resident (base64 blob);
+.It
+hmac secret, if the FIDO2 hmac-secret extension is enabled
+(base64 blob);
+.It
+the credential's associated 32-byte symmetric key
+.Pq Dq largeBlobKey ,
+if requested (base64 blob).
+.El
+.Pp
+When verifying an assertion,
+.Nm
+produces no output.
+.Sh EXAMPLES
+Assuming
+.Pa cred
+contains a
+.Em es256
+credential created according to the steps outlined in
+.Xr fido2-cred 1 ,
+obtain an assertion from an authenticator at
+.Pa /dev/hidraw5
+and verify it:
+.Pp
+.Dl $ echo assertion challenge | openssl sha256 -binary | base64 > assert_param
+.Dl $ echo relying party >> assert_param
+.Dl $ head -1 cred >> assert_param
+.Dl $ tail -n +2 cred > pubkey
+.Dl $ fido2-assert -G -i assert_param /dev/hidraw5 | fido2-assert -V pubkey es256
+.Sh SEE ALSO
+.Xr fido2-cred 1 ,
+.Xr fido2-token 1
diff --git a/man/fido2-cred.1 b/man/fido2-cred.1
new file mode 100644
index 000000000000..301564d688e5
--- /dev/null
+++ b/man/fido2-cred.1
@@ -0,0 +1,267 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: November 5 2019 $
+.Dt FIDO2-CRED 1
+.Os
+.Sh NAME
+.Nm fido2-cred
+.Nd make/verify a FIDO 2 credential
+.Sh SYNOPSIS
+.Nm
+.Fl M
+.Op Fl bdhqruv
+.Op Fl c Ar cred_protect
+.Op Fl i Ar input_file
+.Op Fl o Ar output_file
+.Ar device
+.Op Ar type
+.Nm
+.Fl V
+.Op Fl dhv
+.Op Fl c Ar cred_protect
+.Op Fl i Ar input_file
+.Op Fl o Ar output_file
+.Op Ar type
+.Sh DESCRIPTION
+.Nm
+makes or verifies a FIDO 2 credential.
+.Pp
+A credential
+.Ar type
+may be
+.Em es256
+(denoting ECDSA over NIST P-256 with SHA-256),
+.Em rs256
+(denoting 2048-bit RSA with PKCS#1.5 padding and SHA-256), or
+.Em eddsa
+(denoting EDDSA over Curve25519 with SHA-512).
+If
+.Ar type
+is not specified,
+.Em es256
+is assumed.
+.Pp
+When making a credential, the authenticator may require the user
+to authenticate with a PIN.
+If the
+.Fl q
+option is not specified,
+.Nm
+will prompt the user for the PIN.
+If a
+.Em tty
+is available,
+.Nm
+will use it to obtain the PIN.
+Otherwise,
+.Em stdin
+is used.
+.Pp
+The input of
+.Nm
+is defined by the parameters of the credential to be made/verified.
+See the
+.Sx INPUT FORMAT
+section for details.
+.Pp
+The output of
+.Nm
+is defined by the result of the selected operation.
+See the
+.Sx OUTPUT FORMAT
+section for details.
+.Pp
+If a credential is successfully created or verified,
+.Nm
+exits 0.
+Otherwise,
+.Nm
+exits 1.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl M
+Tells
+.Nm
+to make a new credential on
+.Ar device .
+.It Fl V
+Tells
+.Nm
+to verify a credential.
+.It Fl b
+Request the credential's
+.Dq largeBlobKey ,
+a 32-byte symmetric key associated with the generated credential.
+.It Fl c Ar cred_protect
+If making a credential, set the credential's protection level to
+.Ar cred_protect ,
+where
+.Ar cred_protect
+is the credential's protection level in decimal notation.
+Please refer to
+.In fido/param.h
+for the set of possible values.
+If verifying a credential, check whether the credential's protection
+level was signed by the authenticator as
+.Ar cred_protect .
+.It Fl d
+Causes
+.Nm
+to emit debugging output on
+.Em stderr .
+.It Fl h
+If making a credential, enable the FIDO2 hmac-secret extension.
+If verifying a credential, check whether the extension data bit was
+signed by the authenticator.
+.It Fl i Ar input_file
+Tells
+.Nm
+to read the parameters of the credential from
+.Ar input_file
+instead of
+.Em stdin .
+.It Fl o Ar output_file
+Tells
+.Nm
+to write output on
+.Ar output_file
+instead of
+.Em stdout .
+.It Fl q
+Tells
+.Nm
+to be quiet.
+If a PIN is required and
+.Fl q
+is specified,
+.Nm
+will fail.
+.It Fl r
+Create a resident credential.
+Resident credentials are called
+.Dq discoverable credentials
+in FIDO 2.1.
+.It Fl u
+Create a U2F credential.
+By default,
+.Nm
+will use FIDO2 if supported by the authenticator, and fallback to
+U2F otherwise.
+.It Fl v
+If making a credential, request user verification.
+If verifying a credential, check whether the user verification bit
+was signed by the authenticator.
+.El
+.Sh INPUT FORMAT
+The input of
+.Nm
+consists of base64 blobs and UTF-8 strings separated
+by newline characters ('\\n').
+.Pp
+When making a credential,
+.Nm
+expects its input to consist of:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+user name (UTF-8 string);
+.It
+user id (base64 blob).
+.El
+.Pp
+When verifying a credential,
+.Nm
+expects its input to consist of:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+credential format (UTF-8 string);
+.It
+authenticator data (base64 blob);
+.It
+credential id (base64 blob);
+.It
+attestation signature (base64 blob);
+.It
+attestation certificate (optional, base64 blob).
+.El
+.Pp
+UTF-8 strings passed to
+.Nm
+must not contain embedded newline or NUL characters.
+.Sh OUTPUT FORMAT
+The output of
+.Nm
+consists of base64 blobs, UTF-8 strings, and PEM-encoded public
+keys separated by newline characters ('\\n').
+.Pp
+Upon the successful generation of a credential,
+.Nm
+outputs:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+credential format (UTF-8 string);
+.It
+authenticator data (base64 blob);
+.It
+credential id (base64 blob);
+.It
+attestation signature (base64 blob);
+.It
+attestation certificate, if present (base64 blob).
+.It
+the credential's associated 32-byte symmetric key
+.Pq Dq largeBlobKey ,
+if present (base64 blob).
+.El
+.Pp
+Upon the successful verification of a credential,
+.Nm
+outputs:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+credential id (base64 blob);
+.It
+PEM-encoded credential key.
+.El
+.Sh EXAMPLES
+Create a new
+.Em es256
+credential on
+.Pa /dev/hidraw5 ,
+verify it, and save the id and the public key of the credential in
+.Em cred :
+.Pp
+.Dl $ echo credential challenge | openssl sha256 -binary | base64 > cred_param
+.Dl $ echo relying party >> cred_param
+.Dl $ echo user name >> cred_param
+.Dl $ dd if=/dev/urandom bs=1 count=32 | base64 >> cred_param
+.Dl $ fido2-cred -M -i cred_param /dev/hidraw5 | fido2-cred -V -o cred
+.Sh SEE ALSO
+.Xr fido2-assert 1 ,
+.Xr fido2-token 1
+.Sh CAVEATS
+Please note that
+.Nm
+handles Basic Attestation and Self Attestation transparently.
+In the case of Basic Attestation, the validity of the authenticator's
+attestation certificate is
+.Em not
+verified.
diff --git a/man/fido2-token.1 b/man/fido2-token.1
new file mode 100644
index 000000000000..43f1c0ea48b7
--- /dev/null
+++ b/man/fido2-token.1
@@ -0,0 +1,388 @@
+.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: September 13 2019 $
+.Dt FIDO2-TOKEN 1
+.Os
+.Sh NAME
+.Nm fido2-token
+.Nd find and manage a FIDO 2 authenticator
+.Sh SYNOPSIS
+.Nm
+.Fl C
+.Op Fl d
+.Ar device
+.Nm
+.Fl D
+.Op Fl d
+.Fl i
+.Ar cred_id
+.Ar device
+.Nm
+.Fl D
+.Fl b
+.Op Fl d
+.Fl k Ar key_path
+.Ar device
+.Nm
+.Fl D
+.Fl b
+.Op Fl d
+.Fl n Ar rp_id
+.Op Fl i Ar cred_id
+.Ar device
+.Nm
+.Fl D
+.Fl e
+.Op Fl d
+.Fl i
+.Ar template_id
+.Ar device
+.Nm
+.Fl D
+.Fl u
+.Op Fl d
+.Ar device
+.Nm
+.Fl G
+.Fl b
+.Op Fl d
+.Fl k Ar key_path
+.Ar blob_path
+.Ar device
+.Nm
+.Fl G
+.Fl b
+.Op Fl d
+.Fl n Ar rp_id
+.Op Fl i Ar cred_id
+.Ar blob_path
+.Ar device
+.Nm
+.Fl I
+.Op Fl cd
+.Op Fl k Ar rp_id Fl i Ar cred_id
+.Ar device
+.Nm
+.Fl L
+.Op Fl bder
+.Op Fl k Ar rp_id
+.Op device
+.Nm
+.Fl R
+.Op Fl d
+.Ar device
+.Nm
+.Fl S
+.Op Fl adefu
+.Ar device
+.Nm
+.Fl S
+.Op Fl d
+.Fl i Ar template_id
+.Fl n Ar template_name
+.Nm
+.Fl S
+.Op Fl d
+.Fl l Ar pin_length
+.Ar device
+.Nm
+.Fl S
+.Fl b
+.Op Fl d
+.Fl k Ar key_path
+.Ar blob_path
+.Ar device
+.Nm
+.Fl S
+.Fl b
+.Op Fl d
+.Fl n Ar rp_id
+.Op Fl i Ar cred_id
+.Ar blob_path
+.Ar device
+.Nm
+.Fl S
+.Fl c
+.Op Fl d
+.Fl i Ar cred_id
+.Fl k Ar user_id
+.Fl n Ar name
+.Fl p Ar display_name
+.Ar device
+.Nm
+.Fl V
+.Sh DESCRIPTION
+.Nm
+manages a FIDO 2 authenticator.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl C Ar device
+Changes the PIN of
+.Ar device .
+The user will be prompted for the current and new PINs.
+.It Fl D Fl i Ar id Ar device
+Deletes the resident credential specified by
+.Ar id
+from
+.Ar device ,
+where
+.Ar id
+is the credential's base64-encoded id.
+The user will be prompted for the PIN.
+.It Fl D Fl b Fl k Ar key_path Ar device
+Deletes a
+.Dq largeBlob
+encrypted with
+.Ar key_path
+from
+.Ar device ,
+where
+.Ar key_path
+must hold the blob's base64-encoded encryption key.
+A PIN or equivalent user-verification gesture is required.
+.It Fl D Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar device
+Deletes a
+.Dq largeBlob
+corresponding to
+.Ar rp_id
+from
+.Ar device .
+If
+.Ar rp_id
+has multiple credentials enrolled on
+.Ar device ,
+the credential ID must be specified using
+.Fl i Ar cred_id ,
+where
+.Ar cred_id
+is a base64-encoded blob.
+A PIN or equivalent user-verification gesture is required.
+.It Fl D Fl e Fl i Ar id Ar device
+Deletes the biometric enrollment specified by
+.Ar id
+from
+.Ar device ,
+where
+.Ar id
+is the enrollment's template base64-encoded id.
+The user will be prompted for the PIN.
+.It Fl D Fl u Ar device
+Disables the FIDO 2.1
+.Dq user verification always
+feature on
+.Ar device .
+.It Fl G Fl b Fl k Ar key_path Ar blob_path Ar device
+Gets a FIDO 2.1
+.Dq largeBlob
+encrypted with
+.Ar key_path
+from
+.Ar device ,
+where
+.Ar key_path
+must hold the blob's base64-encoded encryption key.
+The blob is written to
+.Ar blob_path .
+A PIN or equivalent user-verification gesture is required.
+.It Fl G Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar blob_path Ar device
+Gets a FIDO 2.1
+.Dq largeBlob
+associated with
+.Ar rp_id
+from
+.Ar device .
+If
+.Ar rp_id
+has multiple credentials enrolled on
+.Ar device ,
+the credential ID must be specified using
+.Fl i Ar cred_id ,
+where
+.Ar cred_id
+is a base64-encoded blob.
+The blob is written to
+.Ar blob_path .
+A PIN or equivalent user-verification gesture is required.
+.It Fl I Ar device
+Retrieves information on
+.Ar device .
+.It Fl I Fl c Ar device
+Retrieves resident credential metadata from
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl I Fl k Ar rp_id Fl i Ar cred_id Ar device
+Prints the credential id (base64-encoded) and public key
+(PEM encoded) of the resident credential specified by
+.Ar rp_id
+and
+.Ar cred_id ,
+where
+.Ar rp_id
+is a UTF-8 relying party id, and
+.Ar cred_id
+is a base64-encoded credential id.
+The user will be prompted for the PIN.
+.It Fl L
+Produces a list of authenticators found by the operating system.
+.It Fl L Fl b Ar device
+Produces a list of FIDO 2.1
+.Dq largeBlobs
+on
+.Ar device .
+A PIN or equivalent user-verification gesture is required.
+.It Fl L Fl e Ar device
+Produces a list of biometric enrollments on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl L Fl r Ar device
+Produces a list of relying parties with resident credentials on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl L Fl k Ar rp_id Ar device
+Produces a list of resident credentials corresponding to
+relying party
+.Ar rp_id
+on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl R
+Performs a reset on
+.Ar device .
+.Nm
+will NOT prompt for confirmation.
+.It Fl S
+Sets the PIN of
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl S Fl a Ar device
+Enables FIDO 2.1 Enterprise Attestation on
+.Ar device .
+.It Fl S Fl b Fl k Ar key_path Ar blob_path Ar device
+Sets
+.Ar blob_path
+as a FIDO 2.1
+.Dq largeBlob
+encrypted with
+.Ar key_path
+on
+.Ar device ,
+where
+.Ar blob_path
+holds the blob's plaintext, and
+.Ar key_path
+the blob's base64-encoded encryption.
+A PIN or equivalent user-verification gesture is required.
+.It Fl S Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar blob_path Ar device
+Sets
+.Ar blob_path
+as a FIDO 2.1
+.Dq largeBlob
+associated with
+.Ar rp_id
+on
+.Ar device .
+If
+.Ar rp_id
+has multiple credentials enrolled on
+.Ar device ,
+the credential ID must be specified using
+.Fl i Ar cred_id ,
+where
+.Ar cred_id
+is a base64-encoded blob.
+A PIN or equivalent user-verification gesture is required.
+.It Fl S Fl c Fl i Ar cred_id Fl k Ar user_id Fl n Ar name Fl p Ar display_name Ar device
+Sets the
+.Ar name
+and
+.Ar display_name
+attributes of the resident credential identified by
+.Ar cred_id
+and
+.Ar user_id ,
+where
+.Ar name
+and
+.Ar display_name
+are UTF-8 strings and
+.Ar cred_id
+and
+.Ar user_id
+are base64-encoded blobs.
+A PIN or equivalent user-verification gesture is required.
+.It Fl S Fl e Ar device
+Performs a new biometric enrollment on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl S Fl e Fl i Ar template_id Fl n Ar template_name Ar device
+Sets the friendly name of the biometric enrollment specified by
+.Ar template_id
+to
+.Ar template_name
+on
+.Ar device ,
+where
+.Ar template_id
+is base64-encoded and
+.Ar template_name
+is a UTF-8 string.
+The user will be prompted for the PIN.
+.It Fl S Fl f Ar device
+Forces a PIN change on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl S Fl l Ar pin_length Ar device
+Sets the minimum PIN length of
+.Ar device
+to
+.Ar pin_length .
+The user will be prompted for the PIN.
+.It Fl S Fl u Ar device
+Enables the FIDO 2.1
+.Dq user verification always
+feature on
+.Ar device .
+.It Fl V
+Prints version information.
+.It Fl d
+Causes
+.Nm
+to emit debugging output on
+.Em stderr .
+.El
+.Pp
+If a
+.Em tty
+is available,
+.Nm
+will use it to prompt for PINs.
+Otherwise,
+.Em stdin
+is used.
+.Pp
+.Nm
+exits 0 on success and 1 on error.
+.Sh SEE ALSO
+.Xr fido2-assert 1 ,
+.Xr fido2-cred 1
+.Sh CAVEATS
+The actual user-flow to perform a reset is outside the scope of the
+FIDO2 specification, and may therefore vary depending on the
+authenticator.
+Yubico authenticators do not allow resets after 5 seconds from
+power-up, and expect a reset to be confirmed by the user through
+touch within 30 seconds.
+.Pp
+An authenticator's path may contain spaces.
+.Pp
+Resident credentials are called
+.Dq discoverable credentials
+in FIDO 2.1.
+.Pp
+Whether the FIDO 2.1
+.Dq user verification always
+feature is activated or deactivated after an authenticator reset
+is vendor-specific.
diff --git a/man/fido_assert_allow_cred.3 b/man/fido_assert_allow_cred.3
new file mode 100644
index 000000000000..bbe6e4d8929a
--- /dev/null
+++ b/man/fido_assert_allow_cred.3
@@ -0,0 +1,47 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 23 2018 $
+.Dt FIDO_ASSERT_ALLOW_CRED 3
+.Os
+.Sh NAME
+.Nm fido_assert_allow_cred
+.Nd appends a credential ID to the list of credentials allowed in an assertion
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_assert_allow_cred "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Sh DESCRIPTION
+The
+.Fn fido_assert_allow_cred
+function adds
+.Fa ptr
+to the list of credentials allowed in
+.Fa assert ,
+where
+.Fa ptr
+points to a credential ID of
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+If
+.Fn fido_assert_allow_cred
+fails, the existing list of allowed credentials is preserved.
+.Pp
+For the format of a FIDO 2 credential ID, please refer to the
+Web Authentication (webauthn) standard.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_assert_allow_cred
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_assert_new 3 ,
+.Xr fido_assert_set_authdata 3 ,
+.Xr fido_dev_get_assert 3
diff --git a/man/fido_assert_new.3 b/man/fido_assert_new.3
new file mode 100644
index 000000000000..16f4e3a6e46d
--- /dev/null
+++ b/man/fido_assert_new.3
@@ -0,0 +1,243 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: October 22 2019 $
+.Dt FIDO_ASSERT_NEW 3
+.Os
+.Sh NAME
+.Nm fido_assert_new ,
+.Nm fido_assert_free ,
+.Nm fido_assert_count ,
+.Nm fido_assert_rp_id ,
+.Nm fido_assert_user_display_name ,
+.Nm fido_assert_user_icon ,
+.Nm fido_assert_user_name ,
+.Nm fido_assert_authdata_ptr ,
+.Nm fido_assert_blob_ptr ,
+.Nm fido_assert_clientdata_hash_ptr ,
+.Nm fido_assert_hmac_secret_ptr ,
+.Nm fido_assert_largeblob_key_ptr ,
+.Nm fido_assert_user_id_ptr ,
+.Nm fido_assert_sig_ptr ,
+.Nm fido_assert_id_ptr ,
+.Nm fido_assert_authdata_len ,
+.Nm fido_assert_blob_len ,
+.Nm fido_assert_clientdata_hash_len ,
+.Nm fido_assert_hmac_secret_len ,
+.Nm fido_assert_largeblob_key_len ,
+.Nm fido_assert_user_id_len ,
+.Nm fido_assert_sig_len ,
+.Nm fido_assert_id_len ,
+.Nm fido_assert_sigcount ,
+.Nm fido_assert_flags
+.Nd FIDO 2 assertion API
+.Sh SYNOPSIS
+.In fido.h
+.Ft fido_assert_t *
+.Fn fido_assert_new "void"
+.Ft void
+.Fn fido_assert_free "fido_assert_t **assert_p"
+.Ft size_t
+.Fn fido_assert_count "const fido_assert_t *assert"
+.Ft const char *
+.Fn fido_assert_rp_id "const fido_assert_t *assert"
+.Ft const char *
+.Fn fido_assert_user_display_name "const fido_assert_t *assert" "size_t idx"
+.Ft const char *
+.Fn fido_assert_user_icon "const fido_assert_t *assert" "size_t idx"
+.Ft const char *
+.Fn fido_assert_user_name "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_authdata_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_clientdata_hash_ptr "const fido_assert_t *assert"
+.Ft const unsigned char *
+.Fn fido_assert_blob_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_hmac_secret_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_largeblob_key_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_user_id_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_sig_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_id_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_authdata_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_clientdata_hash_len "const fido_assert_t *assert"
+.Ft size_t
+.Fn fido_assert_blob_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_hmac_secret_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_largeblob_key_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_user_id_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_sig_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_id_len "const fido_assert_t *assert" "size_t idx"
+.Ft uint32_t
+.Fn fido_assert_sigcount "const fido_assert_t *assert" "size_t idx"
+.Ft uint8_t
+.Fn fido_assert_flags "const fido_assert_t *assert" "size_t idx"
+.Sh DESCRIPTION
+FIDO 2 assertions are abstracted in
+.Em libfido2
+by the
+.Vt fido_assert_t
+type.
+The functions described in this page allow a
+.Vt fido_assert_t
+type to be allocated, deallocated, and inspected.
+For other operations on
+.Vt fido_assert_t ,
+please refer to
+.Xr fido_assert_set_authdata 3 ,
+.Xr fido_assert_allow_cred 3 ,
+.Xr fido_assert_verify 3 ,
+and
+.Xr fido_dev_get_assert 3 .
+.Pp
+The
+.Fn fido_assert_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_assert_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_assert_free
+function releases the memory backing
+.Fa *assert_p ,
+where
+.Fa *assert_p
+must have been previously allocated by
+.Fn fido_assert_new .
+On return,
+.Fa *assert_p
+is set to NULL.
+Either
+.Fa assert_p
+or
+.Fa *assert_p
+may be NULL, in which case
+.Fn fido_assert_free
+is a NOP.
+.Pp
+The
+.Fn fido_assert_count
+function returns the number of statements in
+.Fa assert .
+.Pp
+The
+.Fn fido_assert_rp_id
+function returns a pointer to a NUL-terminated string holding the
+relying party ID of
+.Fa assert .
+.Pp
+The
+.Fn fido_assert_user_display_name ,
+.Fn fido_assert_user_icon ,
+and
+.Fn fido_assert_user_name ,
+functions return pointers to the user display name, icon, and
+name attributes of statement
+.Fa idx
+in
+.Fa assert .
+If not NULL, the values returned by these functions point to
+NUL-terminated UTF-8 strings.
+.Pp
+The
+.Fn fido_assert_user_id_ptr ,
+.Fn fido_assert_authdata_ptr ,
+.Fn fido_assert_blob_ptr ,
+.Fn fido_assert_hmac_secret_ptr ,
+.Fn fido_assert_largeblob_key_ptr ,
+.Fn fido_assert_sig_ptr ,
+and
+.Fn fido_assert_id_ptr
+functions return pointers to the user ID, CBOR-encoded
+authenticator data, cred blob, hmac-secret,
+.Dq largeBlobKey ,
+signature, and credential ID attributes of statement
+.Fa idx
+in
+.Fa assert .
+.Pp
+The
+.Fn fido_assert_user_id_len ,
+.Fn fido_assert_authdata_len ,
+.Fn fido_assert_blob_len ,
+.Fn fido_assert_hmac_secret_len ,
+.Fn fido_assert_largeblob_key_len ,
+.Fn fido_assert_sig_len ,
+and
+.Fn fido_assert_id_len
+functions can be used to retrieve the corresponding length of a
+specific attribute.
+.Pp
+The
+.Fn fido_assert_sigcount
+function can be used to obtain the signature counter of statement
+.Fa idx
+in
+.Fa assert .
+.Pp
+The
+.Fn fido_assert_flags
+function returns the authenticator data flags of statement
+.Fa idx
+in
+.Fa assert .
+.Pp
+Please note that the first statement in
+.Fa assert
+has an
+.Fa idx
+(index) value of 0.
+.Pp
+The authenticator data and signature parts of an assertion
+statement are typically passed to a FIDO 2 server for verification.
+.Pp
+The
+.Fn fido_assert_clientdata_hash_ptr
+function returns a pointer to the client data hash of
+.Fa assert .
+The corresponding length can be obtained by
+.Fn fido_assert_clientdata_hash_len .
+.Sh RETURN VALUES
+The authenticator data returned by
+.Fn fido_assert_authdata_ptr
+is a CBOR-encoded byte string, as obtained from the authenticator.
+.Pp
+The
+.Fn fido_assert_user_display_name ,
+.Fn fido_assert_user_icon ,
+.Fn fido_assert_user_name ,
+.Fn fido_assert_authdata_ptr ,
+.Fn fido_assert_clientdata_hash_ptr ,
+.Fn fido_assert_hmac_secret_ptr ,
+.Fn fido_assert_largeblob_key_ptr ,
+.Fn fido_assert_user_id_ptr ,
+and
+.Fn fido_assert_sig_ptr
+functions return NULL if the respective field in
+.Fa assert
+is not set.
+If not NULL, returned pointers are guaranteed to exist until any API
+function that takes
+.Fa assert
+without the
+.Em const
+qualifier is invoked.
+.Sh SEE ALSO
+.Xr fido_assert_allow_cred 3 ,
+.Xr fido_assert_set_authdata 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_dev_get_assert 3 ,
+.Xr fido_dev_largeblob_get 3
diff --git a/man/fido_assert_set_authdata.3 b/man/fido_assert_set_authdata.3
new file mode 100644
index 000000000000..2f2ca5b45d6e
--- /dev/null
+++ b/man/fido_assert_set_authdata.3
@@ -0,0 +1,221 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 23 2018 $
+.Dt FIDO_ASSERT_SET_AUTHDATA 3
+.Os
+.Sh NAME
+.Nm fido_assert_set_authdata ,
+.Nm fido_assert_set_authdata_raw ,
+.Nm fido_assert_set_clientdata ,
+.Nm fido_assert_set_clientdata_hash ,
+.Nm fido_assert_set_count ,
+.Nm fido_assert_set_extensions ,
+.Nm fido_assert_set_hmac_salt ,
+.Nm fido_assert_set_hmac_secret ,
+.Nm fido_assert_set_up ,
+.Nm fido_assert_set_uv ,
+.Nm fido_assert_set_rp ,
+.Nm fido_assert_set_sig
+.Nd set parameters of a FIDO 2 assertion
+.Sh SYNOPSIS
+.In fido.h
+.Bd -literal
+typedef enum {
+ FIDO_OPT_OMIT = 0, /* use authenticator's default */
+ FIDO_OPT_FALSE, /* explicitly set option to false */
+ FIDO_OPT_TRUE, /* explicitly set option to true */
+} fido_opt_t;
+.Ed
+.Ft int
+.Fn fido_assert_set_authdata "fido_assert_t *assert" " size_t idx" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_authdata_raw "fido_assert_t *assert" " size_t idx" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_clientdata "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_clientdata_hash "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_count "fido_assert_t *assert" "size_t n"
+.Ft int
+.Fn fido_assert_set_extensions "fido_assert_t *assert" "int flags"
+.Ft int
+.Fn fido_assert_set_hmac_salt "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_hmac_secret "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_up "fido_assert_t *assert" "fido_opt_t up"
+.Ft int
+.Fn fido_assert_set_uv "fido_assert_t *assert" "fido_opt_t uv"
+.Ft int
+.Fn fido_assert_set_rp "fido_assert_t *assert" "const char *id"
+.Ft int
+.Fn fido_assert_set_sig "fido_assert_t *assert" "size_t idx" "const unsigned char *ptr" "size_t len"
+.Sh DESCRIPTION
+The
+.Nm
+set of functions define the various parameters of a FIDO 2
+assertion, allowing a
+.Fa fido_assert_t
+type to be prepared for a subsequent call to
+.Xr fido_dev_get_assert 3
+or
+.Xr fido_assert_verify 3 .
+For the complete specification of a FIDO 2 assertion and the format
+of its constituent parts, please refer to the Web Authentication
+(webauthn) standard.
+.Pp
+The
+.Fn fido_assert_set_count
+function sets the number of assertion statements in
+.Fa assert
+to
+.Fa n .
+.Pp
+The
+.Fn fido_assert_set_authdata
+and
+.Fn fido_assert_set_sig
+functions set the authenticator data and signature parts of the
+statement with index
+.Fa idx
+of
+.Fa assert
+to
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+Please note that the first assertion statement of
+.Fa assert
+has an
+.Fa idx
+of
+.Em 0 .
+The authenticator data passed to
+.Fn fido_assert_set_authdata
+must be a CBOR-encoded byte string, as obtained from
+.Fn fido_assert_authdata_ptr .
+Alternatively, a raw binary blob may be passed to
+.Fn fido_assert_set_authdata_raw .
+.Pp
+The
+.Fn fido_assert_set_clientdata_hash ,
+.Fn fido_assert_set_hmac_salt ,
+and
+.Fn fido_assert_set_hmac_secret
+functions set the client data hash and hmac-salt parts of
+.Fa assert
+to
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+.Pp
+The
+.Fn fido_assert_set_clientdata
+function allows an application to set the client data hash of
+.Fa assert
+by specifying the assertion's unhashed client data.
+This is required by Windows Hello, which calculates the client data
+hash internally.
+For compatibility with Windows Hello, applications should use
+.Fn fido_assert_set_clientdata
+instead of
+.Fn fido_assert_set_clientdata_hash .
+.Pp
+The
+.Fn fido_assert_set_rp
+function sets the relying party
+.Fa id
+of
+.Fa assert ,
+where
+.Fa id
+is a NUL-terminated UTF-8 string.
+The content of
+.Fa id
+is copied, and no references to the passed pointer are kept.
+.Pp
+The
+.Fn fido_assert_set_extensions
+function sets the extensions of
+.Fa assert
+to the bitmask
+.Fa flags .
+At the moment, only the
+.Dv FIDO_EXT_CRED_BLOB ,
+.Dv FIDO_EXT_HMAC_SECRET ,
+and
+.Dv FIDO_EXT_LARGEBLOB_KEY
+extensions are supported.
+If
+.Fa flags
+is zero, the extensions of
+.Fa assert
+are cleared.
+.Pp
+The
+.Fn fido_assert_set_up
+and
+.Fn fido_assert_set_uv
+functions set the
+.Fa up
+(user presence) and
+.Fa uv
+(user verification)
+attributes of
+.Fa assert .
+Both are
+.Dv FIDO_OPT_OMIT
+by default, allowing the authenticator to use its default settings.
+.Pp
+Use of the
+.Nm
+set of functions may happen in two distinct situations:
+when asking a FIDO device to produce a series of assertion
+statements, prior to
+.Xr fido_dev_get_assert 3
+(i.e, in the context of a FIDO client), or when verifying assertion
+statements using
+.Xr fido_assert_verify 3
+(i.e, in the context of a FIDO server).
+.Pp
+For a complete description of the generation of a FIDO 2 assertion
+and its verification, please refer to the FIDO 2 specification.
+An example of how to use the
+.Nm
+set of functions can be found in the
+.Pa examples/assert.c
+file shipped with
+.Em libfido2 .
+.Pp
+.Fn fido_assert_set_hmac_secret
+is not normally useful in a FIDO client or server \(em it is provided
+to enable testing other functionality that relies on retrieving the
+HMAC secret from an assertion obtained from an authenticator.
+.Sh RETURN VALUES
+The
+.Nm
+functions return
+.Dv FIDO_OK
+on success.
+The error codes returned by the
+.Nm
+set of functions are defined in
+.In fido/err.h .
+.Sh SEE ALSO
+.Xr fido_assert_allow_cred 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_dev_get_assert 3
diff --git a/man/fido_assert_verify.3 b/man/fido_assert_verify.3
new file mode 100644
index 000000000000..82e64e12e27a
--- /dev/null
+++ b/man/fido_assert_verify.3
@@ -0,0 +1,79 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 24 2018 $
+.Dt FIDO_ASSERT_VERIFY 3
+.Os
+.Sh NAME
+.Nm fido_assert_verify
+.Nd verifies the signature of a FIDO 2 assertion statement
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_assert_verify "fido_assert_t *assert" "size_t idx" "int cose_alg" "const void *pk"
+.Sh DESCRIPTION
+The
+.Fn fido_assert_verify
+function verifies whether the signature contained in statement index
+.Fa idx
+of
+.Fa assert
+matches the parameters of the assertion.
+Before using
+.Fn fido_assert_verify
+in a sensitive context, the reader is strongly encouraged to make
+herself familiar with the FIDO 2 assertion statement process
+as defined in the Web Authentication (webauthn) standard.
+.Pp
+A brief description follows:
+.Pp
+The
+.Fn fido_assert_verify
+function verifies whether the client data hash, relying party ID,
+user presence and user verification attributes of
+.Fa assert
+have been attested by the holder of the private counterpart of
+the public key
+.Fa pk
+of COSE type
+.Fa cose_alg ,
+where
+.Fa cose_alg
+is
+.Dv COSE_ES256 ,
+.Dv COSE_RS256 ,
+or
+.Dv COSE_EDDSA ,
+and
+.Fa pk
+points to a
+.Vt es256_pk_t ,
+.Vt rs256_pk_t ,
+or
+.Vt eddsa_pk_t
+type accordingly.
+.Pp
+Please note that the first statement in
+.Fa assert
+has an
+.Fa idx
+of 0.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_assert_verify
+are defined in
+.In fido/err.h .
+If
+statement
+.Fa idx
+of
+.Fa assert
+passes verification with
+.Fa pk ,
+then
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_assert_new 3 ,
+.Xr fido_assert_set_authdata 3
diff --git a/man/fido_bio_dev_get_info.3 b/man/fido_bio_dev_get_info.3
new file mode 100644
index 000000000000..1fe3e8ebc18f
--- /dev/null
+++ b/man/fido_bio_dev_get_info.3
@@ -0,0 +1,122 @@
+.\" Copyright (c) 2019 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: September 13 2019 $
+.Dt FIDO_BIO_DEV_GET_INFO 3
+.Os
+.Sh NAME
+.Nm fido_bio_dev_get_info ,
+.Nm fido_bio_dev_enroll_begin ,
+.Nm fido_bio_dev_enroll_continue ,
+.Nm fido_bio_dev_enroll_cancel ,
+.Nm fido_bio_dev_enroll_remove ,
+.Nm fido_bio_dev_get_template_array ,
+.Nm fido_bio_dev_set_template_name
+.Nd FIDO 2 biometric authenticator API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/bio.h
+.Ft int
+.Fn fido_bio_dev_get_info "fido_dev_t *dev" "fido_bio_info_t *info"
+.Ft int
+.Fn fido_bio_dev_enroll_begin "fido_dev_t *dev" "fido_bio_template_t *template" "fido_bio_enroll_t *enroll" "uint32_t timeout_ms" "const char *pin"
+.Ft int
+.Fn fido_bio_dev_enroll_continue "fido_dev_t *dev" "const fido_bio_template_t *template" "fido_bio_enroll_t *enroll" "uint32_t timeout_ms"
+.Ft int
+.Fn fido_bio_dev_enroll_cancel "fido_dev_t *dev"
+.Ft int
+.Fn fido_bio_dev_enroll_remove "fido_dev_t *dev" "const fido_bio_template_t *template" "const char *pin"
+.Ft int
+.Fn fido_bio_dev_get_template_array "fido_dev_t *dev" "fido_bio_template_array_t *template_array" "const char *pin"
+.Ft int
+.Fn fido_bio_dev_set_template_name "fido_dev_t *dev" "const fido_bio_template_t *template" "const char *pin"
+.Sh DESCRIPTION
+The functions described in this page allow biometric
+templates on a FIDO2 authenticator to be listed, created,
+removed, and customised.
+Please note that not all FIDO2 authenticators support biometric
+enrollment.
+For a description of the types involved, please refer to
+.Xr fido_bio_info_new 3 ,
+.Xr fido_bio_enroll_new 3 ,
+and
+.Xr fido_bio_template 3 .
+.Pp
+The
+.Fn fido_bio_dev_get_info
+function populates
+.Fa info
+with sensor information from
+.Fa dev .
+.Pp
+The
+.Fn fido_bio_dev_enroll_begin
+function initiates a biometric enrollment on
+.Fa dev ,
+instructing the authenticator to wait
+.Fa timeout_ms
+milliseconds.
+On success,
+.Fa template
+and
+.Fa enroll
+will be populated with the newly created template's
+information and enrollment status, respectively.
+.Pp
+The
+.Fn fido_bio_dev_enroll_continue
+function continues an ongoing enrollment on
+.Fa dev ,
+instructing the authenticator to wait
+.Fa timeout_ms
+milliseconds.
+On success,
+.Fa enroll
+will be updated to reflect the status of the biometric
+enrollment.
+.Pp
+The
+.Fn fido_bio_dev_enroll_cancel
+function cancels an ongoing enrollment on
+.Fa dev .
+.Pp
+The
+.Fn fido_bio_dev_enroll_remove
+function removes
+.Fa template
+from
+.Fa dev .
+.Pp
+The
+.Fn fido_bio_dev_get_template_array
+function populates
+.Fa template_array
+with the templates currently enrolled on
+.Fa dev .
+.Pp
+The
+.Fn fido_bio_dev_set_template_name
+function sets the friendly name of
+.Fa template
+on
+.Fa dev .
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_bio_dev_get_info ,
+.Fn fido_bio_dev_enroll_begin ,
+.Fn fido_bio_dev_enroll_continue ,
+.Fn fido_bio_dev_enroll_cancel ,
+.Fn fido_bio_dev_enroll_remove ,
+.Fn fido_bio_dev_get_template_array ,
+and
+.Fn fido_bio_dev_set_template_name
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_bio_enroll_new 3 ,
+.Xr fido_bio_info_new 3 ,
+.Xr fido_bio_template 3
diff --git a/man/fido_bio_enroll_new.3 b/man/fido_bio_enroll_new.3
new file mode 100644
index 000000000000..3db3e7acd45d
--- /dev/null
+++ b/man/fido_bio_enroll_new.3
@@ -0,0 +1,95 @@
+.\" Copyright (c) 2019 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: September 13 2019 $
+.Dt FIDO_BIO_ENROLL_NEW 3
+.Os
+.Sh NAME
+.Nm fido_bio_enroll_new ,
+.Nm fido_bio_enroll_free ,
+.Nm fido_bio_enroll_last_status ,
+.Nm fido_bio_enroll_remaining_samples
+.Nd FIDO 2 biometric enrollment API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/bio.h
+.Bd -literal
+#define FIDO_BIO_ENROLL_FP_GOOD 0x00
+#define FIDO_BIO_ENROLL_FP_TOO_HIGH 0x01
+#define FIDO_BIO_ENROLL_FP_TOO_LOW 0x02
+#define FIDO_BIO_ENROLL_FP_TOO_LEFT 0x03
+#define FIDO_BIO_ENROLL_FP_TOO_RIGHT 0x04
+#define FIDO_BIO_ENROLL_FP_TOO_FAST 0x05
+#define FIDO_BIO_ENROLL_FP_TOO_SLOW 0x06
+#define FIDO_BIO_ENROLL_FP_POOR_QUALITY 0x07
+#define FIDO_BIO_ENROLL_FP_TOO_SKEWED 0x08
+#define FIDO_BIO_ENROLL_FP_TOO_SHORT 0x09
+#define FIDO_BIO_ENROLL_FP_MERGE_FAILURE 0x0a
+#define FIDO_BIO_ENROLL_FP_EXISTS 0x0b
+#define FIDO_BIO_ENROLL_FP_DATABASE_FULL 0x0c
+#define FIDO_BIO_ENROLL_NO_USER_ACTIVITY 0x0d
+#define FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION 0x0e
+.Ed
+.Ft fido_bio_enroll_t *
+.Fn fido_bio_enroll_new "void"
+.Ft void
+.Fn fido_bio_enroll_free "fido_bio_enroll_t **enroll_p"
+.Ft uint8_t
+.Fn fido_bio_enroll_last_status "const fido_bio_enroll_t *enroll"
+.Ft uint8_t
+.Fn fido_bio_enroll_remaining_samples "const fido_bio_enroll_t *enroll"
+.Sh DESCRIPTION
+Ongoing FIDO 2 biometric enrollments are abstracted in
+.Em libfido2
+by the
+.Vt fido_bio_enroll_t
+type.
+.Pp
+The functions described in this page allow a
+.Vt fido_bio_enroll_t
+type to be allocated, deallocated, and inspected.
+For device operations on
+.Vt fido_bio_enroll_t ,
+please refer to
+.Xr fido_bio_dev_get_info 3 .
+.Pp
+The
+.Fn fido_bio_enroll_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_bio_enroll_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_bio_enroll_free
+function releases the memory backing
+.Fa *enroll_p ,
+where
+.Fa *enroll_p
+must have been previously allocated by
+.Fn fido_bio_enroll_new .
+On return,
+.Fa *enroll_p
+is set to NULL.
+Either
+.Fa enroll_p
+or
+.Fa *enroll_p
+may be NULL, in which case
+.Fn fido_bio_enroll_free
+is a NOP.
+.Pp
+The
+.Fn fido_bio_enroll_last_status
+function returns the enrollment status of
+.Fa enroll .
+.Pp
+The
+.Fn fido_bio_enroll_remaining_samples
+function returns the number of samples left for
+.Fa enroll
+to complete.
+.Sh SEE ALSO
+.Xr fido_bio_dev_get_info 3 ,
+.Xr fido_bio_template 3
diff --git a/man/fido_bio_info_new.3 b/man/fido_bio_info_new.3
new file mode 100644
index 000000000000..c82733337b4e
--- /dev/null
+++ b/man/fido_bio_info_new.3
@@ -0,0 +1,81 @@
+.\" Copyright (c) 2019 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: September 13 2019 $
+.Dt FIDO_BIO_INFO_NEW 3
+.Os
+.Sh NAME
+.Nm fido_bio_info_new ,
+.Nm fido_bio_info_free ,
+.Nm fido_bio_info_type ,
+.Nm fido_bio_info_max_samples
+.Nd FIDO 2 biometric sensor information API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/bio.h
+.Ft fido_bio_info_t *
+.Fn fido_bio_info_new "void"
+.Ft void
+.Fn fido_bio_info_free "fido_bio_info_t **info_p"
+.Ft uint8_t
+.Fn fido_bio_info_type "const fido_bio_info_t *info"
+.Ft uint8_t
+.Fn fido_bio_info_max_samples "const fido_bio_info_t *info"
+.Sh DESCRIPTION
+Biometric sensor metadata is abstracted in
+.Em libfido2
+by the
+.Vt fido_bio_info_t
+type.
+.Pp
+The functions described in this page allow a
+.Vt fido_bio_info_t
+type to be allocated, deallocated, and inspected.
+For device operations on
+.Vt fido_bio_info_t ,
+please refer to
+.Xr fido_bio_dev_get_info 3 .
+.Pp
+The
+.Fn fido_bio_info_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_bio_info_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_bio_info_free
+function releases the memory backing
+.Fa *info_p ,
+where
+.Fa *info_p
+must have been previously allocated by
+.Fn fido_bio_info_new .
+On return,
+.Fa *info_p
+is set to NULL.
+Either
+.Fa info_p
+or
+.Fa *info_p
+may be NULL, in which case
+.Fn fido_bio_info_free
+is a NOP.
+.Pp
+The
+.Fn fido_bio_info_type
+function returns the fingerprint sensor type, which is
+.Dv 1
+for touch sensors, and
+.Dv 2
+for swipe sensors.
+.Pp
+The
+.Fn fido_bio_info_max_samples
+function returns the maximum number of successful samples
+required for enrollment.
+.Sh SEE ALSO
+.Xr fido_bio_dev_get_info 3 ,
+.Xr fido_bio_enroll_new 3 ,
+.Xr fido_bio_template 3
diff --git a/man/fido_bio_template.3 b/man/fido_bio_template.3
new file mode 100644
index 000000000000..12a379e9a46a
--- /dev/null
+++ b/man/fido_bio_template.3
@@ -0,0 +1,179 @@
+.\" Copyright (c) 2019 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: September 13 2019 $
+.Dt FIDO_BIO_TEMPLATE 3
+.Os
+.Sh NAME
+.Nm fido_bio_template ,
+.Nm fido_bio_template_array_count ,
+.Nm fido_bio_template_array_free ,
+.Nm fido_bio_template_array_new ,
+.Nm fido_bio_template_free ,
+.Nm fido_bio_template_id_len ,
+.Nm fido_bio_template_id_ptr ,
+.Nm fido_bio_template_name ,
+.Nm fido_bio_template_new ,
+.Nm fido_bio_template_set_id ,
+.Nm fido_bio_template_set_name
+.Nd FIDO 2 biometric template API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/bio.h
+.Ft fido_bio_template_t *
+.Fn fido_bio_template_new "void"
+.Ft void
+.Fn fido_bio_template_free "fido_bio_template_t **template_p"
+.Ft const char *
+.Fn fido_bio_template_name "const fido_bio_template_t *template"
+.Ft const unsigned char *
+.Fn fido_bio_template_id_ptr "const fido_bio_template_t *template"
+.Ft size_t
+.Fn fido_bio_template_id_len "const fido_bio_template_t *template"
+.Ft int
+.Fn fido_bio_template_set_id "fido_bio_template_t *template" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_bio_template_set_name "fido_bio_template_t *template" "const char *name"
+.Ft fido_bio_template_array_t *
+.Fn fido_bio_template_array_new "void"
+.Ft void
+.Fn fido_bio_template_array_free "fido_bio_template_array_t **array_p"
+.Ft size_t
+.Fn fido_bio_template_array_count "const fido_bio_template_array_t *array"
+.Ft const fido_bio_template_t *
+.Fn fido_bio_template "const fido_bio_template_array_t *array" "size_t idx"
+.Sh DESCRIPTION
+Existing FIDO 2 biometric enrollments are abstracted in
+.Em libfido2
+by the
+.Vt fido_bio_template_t
+and
+.Vt fido_bio_template_array_t
+types.
+.Pp
+The functions described in this page allow a
+.Vt fido_bio_template_t
+type to be allocated, deallocated, changed, and inspected,
+and a
+.Vt fido_bio_template_array_t
+type to be allocated, deallocated, and inspected.
+For device operations on
+.Vt fido_bio_template_t
+and
+.Vt fido_bio_template_array_t ,
+please refer to
+.Xr fido_bio_dev_get_info 3 .
+.Pp
+The
+.Fn fido_bio_template_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_bio_template_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_bio_template_free
+function releases the memory backing
+.Fa *template_p ,
+where
+.Fa *template_p
+must have been previously allocated by
+.Fn fido_bio_template_new .
+On return,
+.Fa *template_p
+is set to NULL.
+Either
+.Fa template_p
+or
+.Fa *template_p
+may be NULL, in which case
+.Fn fido_bio_template_free
+is a NOP.
+.Pp
+The
+.Fn fido_bio_template_name
+function returns a pointer to a NUL-terminated string containing
+the friendly name of
+.Fa template ,
+or NULL if
+.Fa template
+does not have a friendly name set.
+.Pp
+The
+.Fn fido_bio_template_id_ptr
+function returns a pointer to the template id of
+.Fa template ,
+or NULL if
+.Fa template
+does not have an id.
+The corresponding length can be obtained by
+.Fn fido_bio_template_id_len .
+.Pp
+The
+.Fn fido_bio_template_set_name
+function sets the friendly name of
+.Fa template
+to
+.Fa name .
+If
+.Fa name
+is NULL, the friendly name of
+.Fa template
+is unset.
+.Pp
+The
+.Fn fido_bio_template_array_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_bio_template_array_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_bio_template_array_free
+function releases the memory backing
+.Fa *array_p ,
+where
+.Fa *array_p
+must have been previously allocated by
+.Fn fido_bio_template_array_new .
+On return,
+.Fa *array_p
+is set to NULL.
+Either
+.Fa array_p
+or
+.Fa *array_p
+may be NULL, in which case
+.Fn fido_bio_template_array_free
+is a NOP.
+.Pp
+The
+.Fn fido_bio_template_array_count
+function returns the number of templates in
+.Fa array .
+.Pp
+The
+.Fn fido_bio_template
+function returns a pointer to the template at index
+.Fa idx
+in
+.Fa array .
+Please note that the first template in
+.Fa array
+has an
+.Fa idx
+(index) value of 0.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_bio_template_set_id
+and
+.Fn fido_bio_template_set_name
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_bio_dev_get_info 3 ,
+.Xr fido_bio_enroll_new 3
diff --git a/man/fido_cbor_info_new.3 b/man/fido_cbor_info_new.3
new file mode 100644
index 000000000000..ecba77291f53
--- /dev/null
+++ b/man/fido_cbor_info_new.3
@@ -0,0 +1,231 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 24 2018 $
+.Dt FIDO_CBOR_INFO_NEW 3
+.Os
+.Sh NAME
+.Nm fido_cbor_info_new ,
+.Nm fido_cbor_info_free ,
+.Nm fido_dev_get_cbor_info ,
+.Nm fido_cbor_info_aaguid_ptr ,
+.Nm fido_cbor_info_extensions_ptr ,
+.Nm fido_cbor_info_protocols_ptr ,
+.Nm fido_cbor_info_transports_ptr ,
+.Nm fido_cbor_info_versions_ptr ,
+.Nm fido_cbor_info_options_name_ptr ,
+.Nm fido_cbor_info_options_value_ptr ,
+.Nm fido_cbor_info_algorithm_type ,
+.Nm fido_cbor_info_algorithm_cose ,
+.Nm fido_cbor_info_algorithm_count ,
+.Nm fido_cbor_info_aaguid_len ,
+.Nm fido_cbor_info_extensions_len ,
+.Nm fido_cbor_info_protocols_len ,
+.Nm fido_cbor_info_transports_len ,
+.Nm fido_cbor_info_versions_len ,
+.Nm fido_cbor_info_options_len ,
+.Nm fido_cbor_info_maxmsgsiz ,
+.Nm fido_cbor_info_maxcredcntlst ,
+.Nm fido_cbor_info_maxcredidlen ,
+.Nm fido_cbor_info_fwversion
+.Nd FIDO 2 CBOR Info API
+.Sh SYNOPSIS
+.In fido.h
+.Ft fido_cbor_info_t *
+.Fn fido_cbor_info_new "void"
+.Ft void
+.Fn fido_cbor_info_free "fido_cbor_info_t **ci_p"
+.Ft int
+.Fn fido_dev_get_cbor_info "fido_dev_t *dev" "fido_cbor_info_t *ci"
+.Ft const unsigned char *
+.Fn fido_cbor_info_aaguid_ptr "const fido_cbor_info_t *ci"
+.Ft char **
+.Fn fido_cbor_info_extensions_ptr "const fido_cbor_info_t *ci"
+.Ft const uint8_t *
+.Fn fido_cbor_info_protocols_ptr "const fido_cbor_info_t *ci"
+.Ft char **
+.Fn fido_cbor_info_transports_ptr "const fido_cbor_info_t *ci"
+.Ft char **
+.Fn fido_cbor_info_versions_ptr "const fido_cbor_info_t *ci"
+.Ft char **
+.Fn fido_cbor_info_options_name_ptr "const fido_cbor_info_t *ci"
+.Ft const bool *
+.Fn fido_cbor_info_options_value_ptr "const fido_cbor_info_t *ci"
+.Ft const char *
+.Fn fido_cbor_info_algorithm_type "const fido_cbor_info_t *ci" "size_t idx"
+.Ft int
+.Fn fido_cbor_info_algorithm_cose "const fido_cbor_info_t *ci" "size_t idx"
+.Ft size_t
+.Fn fido_cbor_info_algorithm_count "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_aaguid_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_extensions_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_protocols_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_transports_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_versions_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_options_len "const fido_cbor_info_t *ci"
+.Ft uint64_t
+.Fn fido_cbor_info_maxmsgsiz "const fido_cbor_info_t *ci"
+.Ft uint64_t
+.Fn fido_cbor_info_maxcredbloblen "const fido_cbor_info_t *ci"
+.Ft uint64_t
+.Fn fido_cbor_info_maxcredcntlst "const fido_cbor_info_t *ci"
+.Ft uint64_t
+.Fn fido_cbor_info_maxcredidlen "const fido_cbor_info_t *ci"
+.Ft uint64_t
+.Fn fido_cbor_info_fwversion "const fido_cbor_info_t *ci"
+.Sh DESCRIPTION
+The
+.Fn fido_cbor_info_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_cbor_info_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_cbor_info_free
+function releases the memory backing
+.Fa *ci_p ,
+where
+.Fa *ci_p
+must have been previously allocated by
+.Fn fido_cbor_info_new .
+On return,
+.Fa *ci_p
+is set to NULL.
+Either
+.Fa ci_p
+or
+.Fa *ci_p
+may be NULL, in which case
+.Fn fido_cbor_info_free
+is a NOP.
+.Pp
+The
+.Fn fido_dev_get_cbor_info
+function transmits a
+.Dv CTAP_CBOR_GETINFO
+command to
+.Fa dev
+and fills
+.Fa ci
+with attributes retrieved from the command's response.
+The
+.Fn fido_dev_get_cbor_info
+function may block.
+.Pp
+The
+.Fn fido_cbor_info_aaguid_ptr ,
+.Fn fido_cbor_info_extensions_ptr ,
+.Fn fido_cbor_info_protocols_ptr ,
+.Fn fido_cbor_info_transports_ptr ,
+and
+.Fn fido_cbor_info_versions_ptr
+functions return pointers to the authenticator attestation GUID,
+supported extensions, PIN protocol, transports, and CTAP version
+strings of
+.Fa ci .
+The corresponding length of a given attribute can be
+obtained by
+.Fn fido_cbor_info_aaguid_len ,
+.Fn fido_cbor_info_extensions_len ,
+.Fn fido_cbor_info_protocols_len ,
+.Fn fido_cbor_info_transports_len ,
+or
+.Fn fido_cbor_info_versions_len .
+.Pp
+The
+.Fn fido_cbor_info_options_name_ptr
+and
+.Fn fido_cbor_info_options_value_ptr
+functions return pointers to the array of option names and their
+respective values
+in
+.Fa ci .
+The length of the options array is returned by
+.Fn fido_cbor_info_options_len .
+.Pp
+The
+.Fn fido_cbor_info_algorithm_count
+function returns the number of supported algorithms in
+.Fa ci .
+The
+.Fn fido_cbor_info_algorithm_cose
+function returns the COSE identifier of algorithm
+.Fa idx
+in
+.Fa ci ,
+or 0 if the COSE identifier is unknown or unset.
+The
+.Fn fido_cbor_info_algorithm_type
+function returns the type of algorithm
+.Fa idx
+in
+.Fa ci ,
+or NULL if the type is unset.
+Please note that the first algorithm in
+.Fa ci
+has an
+.Fa idx
+(index) value of 0.
+.Pp
+The
+.Fn fido_cbor_info_maxmsgsiz
+function returns the maximum message size attribute of
+.Fa ci .
+.Pp
+The
+.Fn fido_cbor_info_maxcredbloblen
+function returns the maximum
+.Dq credBlob
+length in bytes supported by the authenticator as reported in
+.Fa ci .
+.Pp
+The
+.Fn fido_cbor_info_maxcredcntlst
+function returns the maximum supported number of credentials in
+a single credential ID list as reported in
+.Fa ci .
+.Pp
+The
+.Fn fido_cbor_info_maxcredidlen
+function returns the maximum supported length of a credential ID
+as reported in
+.Fa ci .
+.Pp
+The
+.Fn fido_cbor_info_fwversion
+function returns the firmware version attribute of
+.Fa ci .
+.Pp
+A complete example of how to use these functions can be found in the
+.Pa example/info.c
+file shipped with
+.Em libfido2 .
+.Sh RETURN VALUES
+The
+.Fn fido_cbor_info_aaguid_ptr ,
+.Fn fido_cbor_info_extensions_ptr ,
+.Fn fido_cbor_info_protocols_ptr ,
+.Fn fido_cbor_info_transports_ptr ,
+.Fn fido_cbor_info_versions_ptr ,
+.Fn fido_cbor_info_options_name_ptr ,
+and
+.Fn fido_cbor_info_options_value_ptr
+functions return NULL if the respective field in
+.Fa ci
+is absent.
+If not NULL, returned pointers are guaranteed to exist until any
+API function that takes
+.Fa ci
+without the
+.Em const
+qualifier is invoked.
+.Sh SEE ALSO
+.Xr fido_dev_open 3
diff --git a/man/fido_cred_exclude.3 b/man/fido_cred_exclude.3
new file mode 100644
index 000000000000..700d6afd8746
--- /dev/null
+++ b/man/fido_cred_exclude.3
@@ -0,0 +1,60 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 23 2018 $
+.Dt FIDO_CRED_EXCLUDE 3
+.Os
+.Sh NAME
+.Nm fido_cred_exclude
+.Nd appends a credential ID to a credential's list of excluded credentials
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_cred_exclude "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Sh DESCRIPTION
+The
+.Fn fido_cred_exclude
+function adds
+.Fa ptr
+to the list of credentials excluded by
+.Fa cred ,
+where
+.Fa ptr
+points to a credential ID of
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+If
+.Fn fido_cred_exclude
+fails, the existing list of excluded credentials is preserved.
+.Pp
+If
+.Nm
+returns success and
+.Fa cred
+is later passed to
+.Xr fido_dev_make_cred 3
+on a device that contains the credential
+denoted by
+.Fa ptr ,
+then
+.Xr fido_dev_make_cred 3
+will fail.
+.Pp
+For the format of a FIDO 2 credential ID, please refer to the
+Web Authentication (webauthn) standard.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_cred_exclude
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_cred_new 3 ,
+.Xr fido_cred_set_authdata 3 ,
+.Xr fido_dev_make_cred 3
diff --git a/man/fido_cred_new.3 b/man/fido_cred_new.3
new file mode 100644
index 000000000000..8cecf5f29850
--- /dev/null
+++ b/man/fido_cred_new.3
@@ -0,0 +1,257 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 23 2018 $
+.Dt FIDO_CRED_NEW 3
+.Os
+.Sh NAME
+.Nm fido_cred_new ,
+.Nm fido_cred_free ,
+.Nm fido_cred_prot ,
+.Nm fido_cred_fmt ,
+.Nm fido_cred_rp_id ,
+.Nm fido_cred_rp_name ,
+.Nm fido_cred_user_name ,
+.Nm fido_cred_display_name ,
+.Nm fido_cred_authdata_ptr ,
+.Nm fido_cred_authdata_raw_ptr ,
+.Nm fido_cred_clientdata_hash_ptr ,
+.Nm fido_cred_id_ptr ,
+.Nm fido_cred_aaguid_ptr ,
+.Nm fido_cred_largeblob_key_ptr ,
+.Nm fido_cred_pubkey_ptr ,
+.Nm fido_cred_sig_ptr ,
+.Nm fido_cred_user_id_ptr ,
+.Nm fido_cred_x5c_ptr ,
+.Nm fido_cred_authdata_len ,
+.Nm fido_cred_authdata_raw_len ,
+.Nm fido_cred_clientdata_hash_len ,
+.Nm fido_cred_id_len ,
+.Nm fido_cred_aaguid_len ,
+.Nm fido_cred_largeblob_key_len ,
+.Nm fido_cred_pubkey_len ,
+.Nm fido_cred_sig_len ,
+.Nm fido_cred_user_id_len ,
+.Nm fido_cred_x5c_len ,
+.Nm fido_cred_type ,
+.Nm fido_cred_flags ,
+.Nm fido_cred_sigcount
+.Nd FIDO 2 credential API
+.Sh SYNOPSIS
+.In fido.h
+.Ft fido_cred_t *
+.Fn fido_cred_new "void"
+.Ft void
+.Fn fido_cred_free "fido_cred_t **cred_p"
+.Ft int
+.Fn fido_cred_prot "fido_cred_t *cred"
+.Ft const char *
+.Fn fido_cred_fmt "const fido_cred_t *cred"
+.Ft const char *
+.Fn fido_cred_rp_id "const fido_cred_t *cred"
+.Ft const char *
+.Fn fido_cred_rp_name "const fido_cred_t *cred"
+.Ft const char *
+.Fn fido_cred_user_name "const fido_cred_t *cred"
+.Ft const char *
+.Fn fido_cred_display_name "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_authdata_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_authdata_raw_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_clientdata_hash_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_id_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_aaguid_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_largeblob_key_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_pubkey_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_sig_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_user_id_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_x5c_ptr "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_authdata_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_authdata_raw_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_clientdata_hash_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_id_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_aaguid_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_largeblob_key_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_pubkey_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_sig_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_user_id_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_x5c_len "const fido_cred_t *cred"
+.Ft int
+.Fn fido_cred_type "const fido_cred_t *cred"
+.Ft uint8_t
+.Fn fido_cred_flags "const fido_cred_t *cred"
+.Ft uint32_t
+.Fn fido_cred_sigcount "const fido_cred_t *cred"
+.Sh DESCRIPTION
+FIDO 2 credentials are abstracted in
+.Em libfido2
+by the
+.Vt fido_cred_t
+type.
+The functions described in this page allow a
+.Vt fido_cred_t
+type to be allocated, deallocated, and inspected.
+For other operations on
+.Vt fido_cred_t ,
+please refer to
+.Xr fido_cred_set_authdata 3 ,
+.Xr fido_cred_exclude 3 ,
+.Xr fido_cred_verify 3 ,
+and
+.Xr fido_dev_make_cred 3 .
+.Pp
+The
+.Fn fido_cred_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_cred_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_cred_free
+function releases the memory backing
+.Fa *cred_p ,
+where
+.Fa *cred_p
+must have been previously allocated by
+.Fn fido_cred_new .
+On return,
+.Fa *cred_p
+is set to NULL.
+Either
+.Fa cred_p
+or
+.Fa *cred_p
+may be NULL, in which case
+.Fn fido_cred_free
+is a NOP.
+.Pp
+The
+.Fn fido_cred_prot
+function returns the protection of
+.Fa cred .
+See
+.Xr fido_cred_set_prot 3
+for the values understood by
+.Em libfido2 .
+.Pp
+The
+.Fn fido_cred_fmt
+function returns a pointer to a NUL-terminated string containing
+the format of
+.Fa cred ,
+or NULL if
+.Fa cred
+does not have a format set.
+.Pp
+The
+.Fn fido_cred_rp_id ,
+.Fn fido_cred_rp_name ,
+.Fn fido_cred_user_name ,
+and
+.Fn fido_cred_display_name
+functions return pointers to NUL-terminated strings holding the
+relying party ID, relying party name, user name, and user display
+name attributes of
+.Fa cred ,
+or NULL if the respective entry is not set.
+.Pp
+The
+.Fn fido_cred_authdata_ptr ,
+.Fn fido_cred_authdata_raw_ptr ,
+.Fn fido_cred_clientdata_hash_ptr ,
+.Fn fido_cred_id_ptr ,
+.Fn fido_cred_aaguid_ptr ,
+.Fn fido_cred_largeblob_key_ptr ,
+.Fn fido_cred_pubkey_ptr ,
+.Fn fido_cred_sig_ptr ,
+.Fn fido_cred_user_id_ptr ,
+and
+.Fn fido_cred_x5c_ptr
+functions return pointers to the CBOR-encoded and raw authenticator
+data, client data hash, ID, authenticator attestation GUID,
+.Dq largeBlobKey ,
+public key, signature, user ID, and x509 certificate parts of
+.Fa cred ,
+or NULL if the respective entry is not set.
+.Pp
+The corresponding length can be obtained by
+.Fn fido_cred_authdata_len ,
+.Fn fido_cred_authdata_raw_len ,
+.Fn fido_cred_clientdata_hash_len ,
+.Fn fido_cred_id_len ,
+.Fn fido_cred_aaguid_len ,
+.Fn fido_cred_largeblob_key_len ,
+.Fn fido_cred_pubkey_len ,
+.Fn fido_cred_sig_len ,
+.Fn fido_cred_user_id_len ,
+and
+.Fn fido_cred_x5c_len .
+.Pp
+The authenticator data, x509 certificate, and signature parts of a
+credential are typically passed to a FIDO 2 server for verification.
+.Pp
+The
+.Fn fido_cred_type
+function returns the COSE algorithm of
+.Fa cred .
+.Pp
+The
+.Fn fido_cred_flags
+function returns the authenticator data flags of
+.Fa cred .
+.Pp
+The
+.Fn fido_cred_sigcount
+function returns the authenticator data signature counter of
+.Fa cred .
+.Sh RETURN VALUES
+The authenticator data returned by
+.Fn fido_cred_authdata_ptr
+is a CBOR-encoded byte string, as obtained from the authenticator.
+To obtain the decoded byte string, use
+.Fn fido_cred_authdata_raw_ptr .
+.Pp
+If not NULL, pointers returned by
+.Fn fido_cred_fmt ,
+.Fn fido_cred_authdata_ptr ,
+.Fn fido_cred_clientdata_hash_ptr ,
+.Fn fido_cred_id_ptr ,
+.Fn fido_cred_aaguid_ptr ,
+.Fn fido_cred_largeblob_key_ptr ,
+.Fn fido_cred_pubkey_ptr ,
+.Fn fido_cred_sig_ptr ,
+and
+.Fn fido_cred_x5c_ptr
+are guaranteed to exist until any API function that takes
+.Fa cred
+without the
+.Em const
+qualifier is invoked.
+.Sh SEE ALSO
+.Xr fido_cred_exclude 3 ,
+.Xr fido_cred_set_authdata 3 ,
+.Xr fido_cred_verify 3 ,
+.Xr fido_credman_metadata_new 3 ,
+.Xr fido_dev_largeblob_get 3 ,
+.Xr fido_dev_make_cred 3
diff --git a/man/fido_cred_set_authdata.3 b/man/fido_cred_set_authdata.3
new file mode 100644
index 000000000000..91e1edbaf810
--- /dev/null
+++ b/man/fido_cred_set_authdata.3
@@ -0,0 +1,307 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 23 2018 $
+.Dt FIDO_CRED_SET_AUTHDATA 3
+.Os
+.Sh NAME
+.Nm fido_cred_set_authdata ,
+.Nm fido_cred_set_authdata_raw ,
+.Nm fido_cred_set_x509 ,
+.Nm fido_cred_set_sig ,
+.Nm fido_cred_set_id ,
+.Nm fido_cred_set_clientdata ,
+.Nm fido_cred_set_clientdata_hash ,
+.Nm fido_cred_set_rp ,
+.Nm fido_cred_set_user ,
+.Nm fido_cred_set_extensions ,
+.Nm fido_cred_set_blob ,
+.Nm fido_cred_set_prot ,
+.Nm fido_cred_set_rk ,
+.Nm fido_cred_set_uv ,
+.Nm fido_cred_set_fmt ,
+.Nm fido_cred_set_type
+.Nd set parameters of a FIDO 2 credential
+.Sh SYNOPSIS
+.In fido.h
+.Bd -literal
+typedef enum {
+ FIDO_OPT_OMIT = 0, /* use authenticator's default */
+ FIDO_OPT_FALSE, /* explicitly set option to false */
+ FIDO_OPT_TRUE, /* explicitly set option to true */
+} fido_opt_t;
+.Ed
+.Ft int
+.Fn fido_cred_set_authdata "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_authdata_raw "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_x509 "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_sig "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_id "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_clientdata "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_clientdata_hash "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_rp "fido_cred_t *cred" "const char *id" "const char *name"
+.Ft int
+.Fn fido_cred_set_user "fido_cred_t *cred" "const unsigned char *user_id" "size_t user_id_len" "const char *name" "const char *display_name" "const char *icon"
+.Ft int
+.Fn fido_cred_set_extensions "fido_cred_t *cred" "int flags"
+.Ft int
+.Fn fido_cred_set_blob "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_prot "fido_cred_t *cred" "int prot"
+.Ft int
+.Fn fido_cred_set_rk "fido_cred_t *cred" "fido_opt_t rk"
+.Ft int
+.Fn fido_cred_set_uv "fido_cred_t *cred" "fido_opt_t uv"
+.Ft int
+.Fn fido_cred_set_fmt "fido_cred_t *cred" "const char *ptr"
+.Ft int
+.Fn fido_cred_set_type "fido_cred_t *cred" "int cose_alg"
+.Sh DESCRIPTION
+The
+.Nm
+set of functions define the various parameters of a FIDO 2
+credential, allowing a
+.Fa fido_cred_t
+type to be prepared for a subsequent call to
+.Xr fido_dev_make_cred 3
+or
+.Xr fido_cred_verify 3 .
+For the complete specification of a FIDO 2 credential and the format
+of its constituent parts, please refer to the Web Authentication
+(webauthn) standard.
+.Pp
+The
+.Fn fido_cred_set_authdata ,
+.Fn fido_cred_set_x509 ,
+.Fn fido_cred_set_sig ,
+.Fn fido_cred_set_id ,
+and
+.Fn fido_cred_set_clientdata_hash
+functions set the authenticator data, attestation certificate,
+signature, id, and client data hash parts of
+.Fa cred
+to
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+The authenticator data passed to
+.Fn fido_cred_set_authdata
+must be a CBOR-encoded byte string, as obtained from
+.Fn fido_cred_authdata_ptr .
+Alternatively, a raw binary blob may be passed to
+.Fn fido_cred_set_authdata_raw .
+.Pp
+An application calling
+.Fn fido_cred_set_authdata
+does not need to call
+.Fn fido_cred_set_id .
+The latter is meant to be used in contexts where the
+credential's authenticator data is not available.
+.Pp
+The
+.Fn fido_cred_set_clientdata
+function allows an application to set the client data hash of
+.Fa cred
+by specifying the credential's unhashed client data.
+This is required by Windows Hello, which calculates the client data
+hash internally.
+For compatibility with Windows Hello, applications should use
+.Fn fido_cred_set_clientdata
+instead of
+.Fn fido_cred_set_clientdata_hash .
+.Pp
+The
+.Fn fido_cred_set_rp
+function sets the relying party
+.Fa id
+and
+.Fa name
+parameters of
+.Fa cred ,
+where
+.Fa id
+and
+.Fa name
+are NUL-terminated UTF-8 strings.
+The contents of
+.Fa id
+and
+.Fa name
+are copied, and no references to the passed pointers are kept.
+.Pp
+The
+.Fn fido_cred_set_user
+function sets the user attributes of
+.Fa cred ,
+where
+.Fa user_id
+points to
+.Fa user_id_len
+bytes and
+.Fa name ,
+.Fa display_name ,
+and
+.Fa icon
+are NUL-terminated UTF-8 strings.
+The contents of
+.Fa user_id ,
+.Fa name ,
+.Fa display_name ,
+and
+.Fa icon
+are copied, and no references to the passed pointers are kept.
+Previously set user attributes are flushed.
+The
+.Fa user_id ,
+.Fa name ,
+.Fa display_name ,
+and
+.Fa icon
+parameters may be NULL.
+.Pp
+The
+.Fn fido_cred_set_extensions
+function sets the extensions of
+.Fa cred
+to the bitmask
+.Fa flags .
+At the moment, only the
+.Dv FIDO_EXT_CRED_BLOB ,
+.Dv FIDO_EXT_CRED_PROTECT ,
+.Dv FIDO_EXT_HMAC_SECRET ,
+and
+.Dv FIDO_EXT_LARGEBLOB_KEY
+extensions are supported.
+If
+.Fa flags
+is zero, the extensions of
+.Fa cred
+are cleared.
+.Pp
+The
+.Fn fido_cred_set_blob
+function sets the
+.Dq credBlob
+to be stored with
+.Fa cred
+to the data pointed to by
+.Fa ptr ,
+which must be
+.Fa len
+bytes long.
+.Pp
+The
+.Fn fido_cred_set_prot
+function sets the protection of
+.Fa cred
+to the scalar
+.Fa prot .
+At the moment, only the
+.Dv FIDO_CRED_PROT_UV_OPTIONAL ,
+.Dv FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID ,
+and
+.Dv FIDO_CRED_PROT_UV_REQUIRED
+protections are supported.
+If
+.Fa prot
+is zero, the protection of
+.Fa cred
+is cleared.
+.Pp
+The
+.Fn fido_cred_set_rk
+and
+.Fn fido_cred_set_uv
+functions set the
+.Em rk
+.Pq resident/discoverable key
+and
+.Em uv
+.Pq user verification
+attributes of
+.Fa cred .
+Both are
+.Dv FIDO_OPT_OMIT
+by default, allowing the authenticator to use its default settings.
+.Pp
+The
+.Fn fido_cred_set_fmt
+function sets the attestation format of
+.Fa cred
+to
+.Fa fmt ,
+where
+.Fa fmt
+must be
+.Vt "packed"
+.Pq the format used in FIDO2 ,
+.Vt "fido-u2f"
+.Pq the format used by U2F ,
+or
+.Vt "none" .
+A copy of
+.Fa fmt
+is made, and no references to the passed pointer are kept.
+Note that not all authenticators support FIDO2 and therefore may not
+be able to generate
+.Vt "packed" .
+.Pp
+The
+.Fn fido_cred_set_type
+function sets the type of
+.Fa cred to
+.Fa cose_alg ,
+where
+.Fa cose_alg
+is
+.Dv COSE_ES256 ,
+.Dv COSE_RS256 ,
+or
+.Dv COSE_EDDSA .
+The type of a credential may only be set once.
+Note that not all authenticators support COSE_RS256 or COSE_EDDSA.
+.Pp
+Use of the
+.Nm
+set of functions may happen in two distinct situations:
+when generating a new credential on a FIDO device, prior to
+.Xr fido_dev_make_cred 3
+(i.e, in the context of a FIDO client), or when validating
+a generated credential using
+.Xr fido_cred_verify 3
+(i.e, in the context of a FIDO server).
+.Pp
+For a complete description of the generation of a FIDO 2 credential
+and its verification, please refer to the FIDO 2 specification.
+A concrete utilisation example of the
+.Nm
+set of functions can be found in the
+.Pa cred.c
+example shipped with
+.Em libfido2 .
+.Sh RETURN VALUES
+The error codes returned by the
+.Nm
+set of functions are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_cred_exclude 3 ,
+.Xr fido_cred_verify 3 ,
+.Xr fido_dev_make_cred 3
diff --git a/man/fido_cred_verify.3 b/man/fido_cred_verify.3
new file mode 100644
index 000000000000..6b720f2132ea
--- /dev/null
+++ b/man/fido_cred_verify.3
@@ -0,0 +1,69 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 23 2018 $
+.Dt FIDO_CRED_VERIFY 3
+.Os
+.Sh NAME
+.Nm fido_cred_verify
+.Nd verifies the attestation signature of a FIDO 2 credential
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_cred_verify "const fido_cred_t *cred"
+.Sh DESCRIPTION
+The
+.Fn fido_cred_verify
+function verifies whether the attestation signature contained in
+.Fa cred
+matches the attributes of the credential.
+Before using
+.Fn fido_cred_verify
+in a sensitive context, the reader is strongly encouraged to make
+herself familiar with the FIDO 2 credential attestation process
+as defined in the Web Authentication (webauthn) standard.
+.Pp
+A brief description follows:
+.Pp
+The
+.Fn fido_cred_verify
+function verifies whether the client data hash, relying party ID,
+credential ID, type, and resident/discoverable key and user verification
+attributes of
+.Fa cred
+have been attested by the holder of the private counterpart of
+the public key contained in the credential's x509 certificate.
+.Pp
+Please note that the x509 certificate itself is not verified.
+.Pp
+The attestation statement formats supported by
+.Fn fido_cred_verify
+are
+.Em packed
+and
+.Em fido-u2f .
+The attestation type implemented by
+.Fn fido_cred_verify
+is
+.Em Basic Attestation .
+The attestation key pair is assumed to be of the type ES256.
+Other attestation formats and types are not supported.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_cred_verify
+are defined in
+.In fido/err.h .
+If
+.Fa cred
+does not contain attestation data, then
+.Dv FIDO_ERR_INVALID_ARGUMENT
+is returned.
+If
+.Fa cred
+passes verification, then
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_cred_new 3 ,
+.Xr fido_cred_set_authdata 3
diff --git a/man/fido_credman_metadata_new.3 b/man/fido_credman_metadata_new.3
new file mode 100644
index 000000000000..31f240fbbe8c
--- /dev/null
+++ b/man/fido_credman_metadata_new.3
@@ -0,0 +1,326 @@
+.\" Copyright (c) 2019-2021 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: June 28 2019 $
+.Dt FIDO_CREDMAN_METADATA_NEW 3
+.Os
+.Sh NAME
+.Nm fido_credman_metadata_new ,
+.Nm fido_credman_rk_new ,
+.Nm fido_credman_rp_new ,
+.Nm fido_credman_metadata_free ,
+.Nm fido_credman_rk_free ,
+.Nm fido_credman_rp_free ,
+.Nm fido_credman_rk_existing ,
+.Nm fido_credman_rk_remaining ,
+.Nm fido_credman_rk ,
+.Nm fido_credman_rk_count ,
+.Nm fido_credman_rp_id ,
+.Nm fido_credman_rp_name ,
+.Nm fido_credman_rp_count ,
+.Nm fido_credman_rp_id_hash_ptr ,
+.Nm fido_credman_rp_id_hash_len ,
+.Nm fido_credman_get_dev_metadata ,
+.Nm fido_credman_get_dev_rk ,
+.Nm fido_credman_set_dev_rk ,
+.Nm fido_credman_del_dev_rk ,
+.Nm fido_credman_get_dev_rp
+.Nd FIDO 2 credential management API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/credman.h
+.Ft fido_credman_metadata_t *
+.Fn fido_credman_metadata_new "void"
+.Ft fido_credman_rk_t *
+.Fn fido_credman_rk_new "void"
+.Ft fido_credman_rp_t *
+.Fn fido_credman_rp_new "void"
+.Ft void
+.Fn fido_credman_metadata_free "fido_credman_metadata_t **metadata_p"
+.Ft void
+.Fn fido_credman_rk_free "fido_credman_rk_t **rk_p"
+.Ft void
+.Fn fido_credman_rp_free "fido_credman_rp_t **rp_p"
+.Ft uint64_t
+.Fn fido_credman_rk_existing "const fido_credman_metadata_t *metadata"
+.Ft uint64_t
+.Fn fido_credman_rk_remaining "const fido_credman_metadata_t *metadata"
+.Ft const fido_cred_t *
+.Fn fido_credman_rk "const fido_credman_rk_t *rk" "size_t idx"
+.Ft size_t
+.Fn fido_credman_rk_count "const fido_credman_rk_t *rk"
+.Ft const char *
+.Fn fido_credman_rp_id "const fido_credman_rp_t *rp" "size_t idx"
+.Ft const char *
+.Fn fido_credman_rp_name "const fido_credman_rp_t *rp" "size_t idx"
+.Ft size_t
+.Fn fido_credman_rp_count "const fido_credman_rp_t *rp"
+.Ft const unsigned char *
+.Fn fido_credman_rp_id_hash_ptr "const fido_credman_rp_t *rp" "size_t idx"
+.Ft size_t
+.Fn fido_credman_rp_id_hash_len "const fido_credman_rp_t *" "size_t idx"
+.Ft int
+.Fn fido_credman_get_dev_metadata "fido_dev_t *dev" "fido_credman_metadata_t *metadata" "const char *pin"
+.Ft int
+.Fn fido_credman_get_dev_rk "fido_dev_t *dev" "const char *rp_id" "fido_credman_rk_t *rk" "const char *pin"
+.Ft int
+.Fn fido_credman_set_dev_rk "fido_dev_t *dev" "fido_cred_t *cred" "const char *pin"
+.Ft int
+.Fn fido_credman_del_dev_rk "fido_dev_t *dev" "const unsigned char *cred_id" "size_t cred_id_len" "const char *pin"
+.Ft int
+.Fn fido_credman_get_dev_rp "fido_dev_t *dev" "fido_credman_rp_t *rp" "const char *pin"
+.Sh DESCRIPTION
+The credential management API of
+.Em libfido2
+allows resident credentials on a FIDO2 authenticator to be listed,
+inspected, modified, and removed.
+Please note that not all FIDO2 authenticators support credential
+management.
+To obtain information on what an authenticator supports, please
+refer to
+.Xr fido_cbor_info_new 3 .
+.Pp
+The
+.Vt fido_credman_metadata_t
+type abstracts credential management metadata.
+.Pp
+The
+.Fn fido_credman_metadata_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_credman_metadata_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_credman_metadata_free
+function releases the memory backing
+.Fa *metadata_p ,
+where
+.Fa *metadata_p
+must have been previously allocated by
+.Fn fido_credman_metadata_new .
+On return,
+.Fa *metadata_p
+is set to NULL.
+Either
+.Fa metadata_p
+or
+.Fa *metadata_p
+may be NULL, in which case
+.Fn fido_credman_metadata_free
+is a NOP.
+.Pp
+The
+.Fn fido_credman_get_dev_metadata
+function populates
+.Fa metadata
+with information retrieved from
+.Fa dev .
+A valid
+.Fa pin
+must be provided.
+.Pp
+The
+.Fn fido_credman_rk_existing
+function inspects
+.Fa metadata
+and returns the number of resident credentials on the
+authenticator.
+The
+.Fn fido_credman_rk_remaining
+function inspects
+.Fa metadata
+and returns the estimated number of resident credentials that can
+be created on the authenticator.
+.Pp
+The
+.Vt fido_credman_rk_t
+type abstracts the set of resident credentials belonging to a
+given relying party.
+.Pp
+The
+.Fn fido_credman_rk_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_credman_rk_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_credman_rk_free
+function releases the memory backing
+.Fa *rk_p ,
+where
+.Fa *rk_p
+must have been previously allocated by
+.Fn fido_credman_rk_new .
+On return,
+.Fa *rk_p
+is set to NULL.
+Either
+.Fa rk_p
+or
+.Fa *rk_p
+may be NULL, in which case
+.Fn fido_credman_rk_free
+is a NOP.
+.Pp
+The
+.Fn fido_credman_get_dev_rk
+function populates
+.Fa rk
+with the set of resident credentials belonging to
+.Fa rp_id
+in
+.Fa dev .
+A valid
+.Fa pin
+must be provided.
+.Pp
+The
+.Fn fido_credman_rk_count
+function returns the number of resident credentials in
+.Fa rk .
+The
+.Fn fido_credman_rk
+function returns a pointer to the credential at index
+.Fa idx
+in
+.Fa rk .
+Please note that the first credential in
+.Fa rk
+has an
+.Fa idx
+(index) value of 0.
+.Pp
+The
+.Fn fido_credman_set_dev_rk
+function updates the credential pointed to by
+.Fa cred
+in
+.Fa dev .
+The credential id and user id attributes of
+.Fa cred
+must be set.
+See
+.Xr fido_cred_set_id 3
+and
+.Xr fido_cred_set_user 3
+for details.
+Only a credential's user attributes (name, display name)
+may be updated at this time.
+.Pp
+The
+.Fn fido_credman_del_dev_rk
+function deletes the resident credential identified by
+.Fa cred_id
+from
+.Fa dev ,
+where
+.Fa cred_id
+points to
+.Fa cred_id_len
+bytes.
+A valid
+.Fa pin
+must be provided.
+.Pp
+The
+.Vt fido_credman_rp_t
+type abstracts information about a relying party.
+.Pp
+The
+.Fn fido_credman_rp_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_credman_rp_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_credman_rp_free
+function releases the memory backing
+.Fa *rp_p ,
+where
+.Fa *rp_p
+must have been previously allocated by
+.Fn fido_credman_rp_new .
+On return,
+.Fa *rp_p
+is set to NULL.
+Either
+.Fa rp_p
+or
+.Fa *rp_p
+may be NULL, in which case
+.Fn fido_credman_rp_free
+is a NOP.
+.Pp
+The
+.Fn fido_credman_get_dev_rp
+function populates
+.Fa rp
+with information about relying parties with resident credentials
+in
+.Fa dev .
+A valid
+.Fa pin
+must be provided.
+.Pp
+The
+.Fn fido_credman_rp_count
+function returns the number of relying parties in
+.Fa rp .
+.Pp
+The
+.Fn fido_credman_rp_id
+and
+.Fn fido_credman_rp_name
+functions return pointers to the id and name of relying party
+.Fa idx
+in
+.Fa rp .
+If not NULL, the values returned by these functions point to
+NUL-terminated UTF-8 strings.
+Please note that the first relying party in
+.Fa rp
+has an
+.Fa idx
+(index) value of 0.
+.Pp
+The
+.Fn fido_credman_rp_id_hash_ptr
+function returns a pointer to the hashed id of relying party
+.Fa idx
+in
+.Fa rp .
+The corresponding length can be obtained by
+.Fn fido_credman_rp_id_hash_len .
+Please note that the first relying party in
+.Fa rp
+has an
+.Fa idx
+(index) value of 0.
+.Sh RETURN VALUES
+The
+.Fn fido_credman_get_dev_metadata ,
+.Fn fido_credman_get_dev_rk ,
+.Fn fido_credman_set_dev_rk ,
+.Fn fido_credman_del_dev_rk ,
+and
+.Fn fido_credman_get_dev_rp
+functions return
+.Dv FIDO_OK
+on success.
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+Functions returning pointers are not guaranteed to succeed, and
+should have their return values checked for NULL.
+.Sh SEE ALSO
+.Xr fido_cbor_info_new 3 ,
+.Xr fido_cred_new 3 ,
+.Xr fido_dev_supports_credman 3
+.Sh CAVEATS
+Resident credentials are called
+.Dq discoverable credentials
+in FIDO 2.1.
diff --git a/man/fido_dev_enable_entattest.3 b/man/fido_dev_enable_entattest.3
new file mode 100644
index 000000000000..7cb766d41d0c
--- /dev/null
+++ b/man/fido_dev_enable_entattest.3
@@ -0,0 +1,98 @@
+.\" Copyright (c) 2020 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: September 22 2020 $
+.Dt FIDO_DEV_ENABLE_ENTATTEST 3
+.Os
+.Sh NAME
+.Nm fido_dev_enable_entattest ,
+.Nm fido_dev_toggle_always_uv ,
+.Nm fido_dev_force_pin_change ,
+.Nm fido_dev_set_pin_minlen
+.Nd FIDO 2.1 configuration authenticator API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/config.h
+.Ft int
+.Fn fido_dev_enable_entattest "fido_dev_t *dev" "const char *pin"
+.Ft int
+.Fn fido_dev_toggle_always_uv "fido_dev_t *dev" "const char *pin"
+.Ft int
+.Fn fido_dev_force_pin_change "fido_dev_t *dev" "const char *pin"
+.Ft int
+.Fn fido_dev_set_pin_minlen "fido_dev_t *dev" "size_t len" "const char *pin"
+.Sh DESCRIPTION
+The functions described in this page allow configuration of a
+FIDO 2.1 authenticator.
+.Pp
+The
+.Fn fido_dev_enable_entattest
+function enables the
+.Em Enterprise Attestation
+feature on
+.Fa dev .
+.Em Enterprise Attestation
+instructs the authenticator to include uniquely identifying
+information in subsequent attestation statements.
+The
+.Fa pin
+parameter may be NULL if
+.Fa dev
+does not have a PIN set.
+.Pp
+The
+.Fn fido_dev_toggle_always_uv
+function toggles the
+.Dq user verification always
+feature on
+.Fa dev .
+When set, this toggle enforces user verification at the
+authenticator level for all known credentials.
+If
+.Fa dev
+supports U2F (CTAP1) and the user verification methods supported by
+the authenticator do not allow protection of U2F credentials, the
+U2F subsystem will be disabled by the authenticator.
+The
+.Fa pin
+parameter may be NULL if
+.Fa dev
+does not have a PIN set.
+.Pp
+The
+.Fn fido_dev_force_pin_change
+instructs
+.Fa dev
+to require a PIN change.
+Subsequent PIN authentication attempts against
+.Fa dev
+will fail until its PIN is changed.
+.Pp
+The
+.Fn fido_dev_set_pin_minlen
+function sets the minimum PIN length of
+.Fa dev
+to
+.Fa len .
+Minimum PIN lengths may only be increased.
+.Pp
+Configuration settings are reflected in the payload returned by the
+authenticator in response to a
+.Xr fido_dev_get_cbor_info 3
+call.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_enable_entattest ,
+.Fn fido_dev_toggle_always_uv ,
+.Fn fido_dev_force_pin_change ,
+and
+.Fn fido_dev_set_pin_minlen
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_dev_get_cbor_info 3 ,
+.Xr fido_dev_reset 3
diff --git a/man/fido_dev_get_assert.3 b/man/fido_dev_get_assert.3
new file mode 100644
index 000000000000..2e33fc516e7d
--- /dev/null
+++ b/man/fido_dev_get_assert.3
@@ -0,0 +1,76 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 24 2018 $
+.Dt FIDO_DEV_GET_ASSERT 3
+.Os
+.Sh NAME
+.Nm fido_dev_get_assert
+.Nd obtains an assertion from a FIDO device
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_get_assert "fido_dev_t *dev" " fido_assert_t *assert" "const char *pin"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_get_assert
+function asks the FIDO device represented by
+.Fa dev
+for an assertion according to the following parameters defined in
+.Fa assert :
+.Pp
+.Bl -dash -compact
+.It
+.Nm relying party ID ;
+.It
+.Nm client data hash ;
+.It
+.Nm list of allowed credential IDs ;
+.It
+.Nm user presence and user verification attributes .
+.El
+.Pp
+See
+.Xr fido_assert_set_authdata 3
+for information on how these values are set.
+.Pp
+If a PIN is not needed to authenticate the request against
+.Fa dev ,
+then
+.Fa pin
+may be NULL.
+Otherwise
+.Fa pin
+must point to a NUL-terminated UTF-8 string.
+.Pp
+After a successful call to
+.Fn fido_dev_get_assert ,
+the
+.Xr fido_assert_count 3 ,
+.Xr fido_assert_user_display_name 3 ,
+.Xr fido_assert_user_icon 3 ,
+.Xr fido_assert_user_name 3 ,
+.Xr fido_assert_authdata_ptr 3 ,
+.Xr fido_assert_user_id_ptr 3 ,
+.Xr fido_assert_sig_ptr 3 ,
+and
+.Xr fido_assert_sigcount 3
+functions may be invoked on
+.Fa assert
+to retrieve the various attributes of the generated assertion.
+.Pp
+Please note that
+.Fn fido_dev_get_assert
+is synchronous and will block if necessary.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_get_assert
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_assert_new 3 ,
+.Xr fido_assert_set_authdata 3
diff --git a/man/fido_dev_get_touch_begin.3 b/man/fido_dev_get_touch_begin.3
new file mode 100644
index 000000000000..8372c6ff010b
--- /dev/null
+++ b/man/fido_dev_get_touch_begin.3
@@ -0,0 +1,73 @@
+.\" Copyright (c) 2020 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: August 5 2020 $
+.Dt FIDO_DEV_GET_TOUCH_BEGIN 3
+.Os
+.Sh NAME
+.Nm fido_dev_get_touch_begin ,
+.Nm fido_dev_get_touch_status
+.Nd asynchronously wait for touch on a FIDO 2 authenticator
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_get_touch_begin "fido_dev_t *dev"
+.Ft int
+.Fn fido_dev_get_touch_status "fido_dev_t *dev" "int *touched" "int ms"
+.Sh DESCRIPTION
+The functions described in this page allow an application to
+asynchronously wait for touch on a FIDO authenticator.
+This is useful when multiple authenticators are present and
+the application needs to know which one to use.
+.Pp
+The
+.Fn fido_dev_get_touch_begin
+function initiates a touch request on
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_get_touch_status
+function continues an ongoing touch request on
+.Fa dev ,
+blocking up to
+.Fa ms
+milliseconds.
+On success,
+.Fa touched
+will be updated to reflect the touch request status.
+If
+.Fa touched
+is 1, the device was touched, and the touch request is
+terminated.
+If
+.Fa touched
+is 0, the application may call
+.Fn fido_dev_get_touch_status
+to continue the touch request, or
+.Fn fido_dev_cancel
+to terminate it.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_get_touch_begin
+and
+.Fn fido_dev_get_touch_status
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh EXAMPLES
+Please refer to
+.Em examples/select.c
+in
+.Em libfido2's
+source tree.
+.Sh SEE ALSO
+.Xr fido_dev_cancel 3
+.Sh CAVEATS
+The
+.Fn fido_dev_get_touch_status
+function will cause a command to be transmitted to U2F
+authenticators.
+These transmissions should not exceed a frequency of 5Hz.
diff --git a/man/fido_dev_info_manifest.3 b/man/fido_dev_info_manifest.3
new file mode 100644
index 000000000000..22519e29b9fa
--- /dev/null
+++ b/man/fido_dev_info_manifest.3
@@ -0,0 +1,143 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 25 2018 $
+.Dt FIDO_DEV_INFO_MANIFEST 3
+.Os
+.Sh NAME
+.Nm fido_dev_info_manifest ,
+.Nm fido_dev_info_new ,
+.Nm fido_dev_info_free ,
+.Nm fido_dev_info_ptr ,
+.Nm fido_dev_info_path ,
+.Nm fido_dev_info_product ,
+.Nm fido_dev_info_vendor ,
+.Nm fido_dev_info_manufacturer_string ,
+.Nm fido_dev_info_product_string
+.Nd FIDO 2 device discovery functions
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_info_manifest "fido_dev_info_t *devlist" "size_t ilen" "size_t *olen"
+.Ft fido_dev_info_t *
+.Fn fido_dev_info_new "size_t n"
+.Ft void
+.Fn fido_dev_info_free "fido_dev_info_t **devlist_p" "size_t n"
+.Ft const fido_dev_info_t *
+.Fn fido_dev_info_ptr "const fido_dev_info_t *devlist" "size_t i"
+.Ft const char *
+.Fn fido_dev_info_path "const fido_dev_info_t *di"
+.Ft int16_t
+.Fn fido_dev_info_product "const fido_dev_info_t *di"
+.Ft int16_t
+.Fn fido_dev_info_vendor "const fido_dev_info_t *di"
+.Ft const char *
+.Fn fido_dev_info_manufacturer_string "const fido_dev_info_t *di"
+.Ft const char *
+.Fn fido_dev_info_product_string "const fido_dev_info_t *di"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_info_manifest
+function fills
+.Fa devlist
+with up to
+.Fa ilen
+FIDO devices found by the underlying operating system.
+Currently only USB HID devices are supported.
+The number of discovered devices is returned in
+.Fa olen ,
+where
+.Fa olen
+is an addressable pointer.
+.Pp
+The
+.Fn fido_dev_info_new
+function returns a pointer to a newly allocated, empty device list
+with
+.Fa n
+available slots.
+If memory is not available, NULL is returned.
+.Pp
+The
+.Fn fido_dev_info_free
+function releases the memory backing
+.Fa *devlist_p ,
+where
+.Fa *devlist_p
+must have been previously allocated by
+.Fn fido_dev_info_new .
+The number
+.Fa n
+of allocated slots must also be provided.
+On return,
+.Fa *devlist_p
+is set to NULL.
+Either
+.Fa devlist_p
+or
+.Fa *devlist_p
+may be NULL, in which case
+.Fn fido_dev_info_free
+is a NOP.
+.Pp
+The
+.Fn fido_dev_info_ptr
+function returns a pointer to slot number
+.Fa i
+of
+.Fa devlist .
+It is the caller's responsibility to ensure that
+.Fa i
+is bounded.
+Please note that the first slot has index 0.
+.Pp
+The
+.Fn fido_dev_info_path
+returns the filesystem path or subsystem-specific identification
+string of
+.Fa di .
+.Pp
+The
+.Fn fido_dev_info_product
+function returns the product ID of
+.Fa di .
+.Pp
+The
+.Fn fido_dev_info_vendor
+function returns the vendor ID of
+.Fa di .
+.Pp
+The
+.Fn fido_dev_info_manufacturer_string
+function returns the manufacturer string of
+.Fa di .
+.Pp
+The
+.Fn fido_dev_info_product_string
+function returns the product string of
+.Fa di .
+.Pp
+An example of how to use the functions described in this document
+can be found in the
+.Pa examples/manifest.c
+file shipped with
+.Em libfido2 .
+.Sh RETURN VALUES
+The
+.Fn fido_dev_info_manifest
+function always returns
+.Dv FIDO_OK .
+If a discovery error occurs, the
+.Fa olen
+pointer is set to 0.
+.Pp
+The pointers returned by
+.Fn fido_dev_info_ptr ,
+.Fn fido_dev_info_path ,
+.Fn fido_dev_info_manufacturer_string ,
+and
+.Fn fido_dev_info_product_string
+are guaranteed to exist until
+.Fn fido_dev_info_free
+is called on the corresponding device list.
diff --git a/man/fido_dev_largeblob_get.3 b/man/fido_dev_largeblob_get.3
new file mode 100644
index 000000000000..830534ed0e7b
--- /dev/null
+++ b/man/fido_dev_largeblob_get.3
@@ -0,0 +1,194 @@
+.\" Copyright (c) 2020 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: October 26 2020 $
+.Dt FIDO_LARGEBLOB_GET 3
+.Os
+.Sh NAME
+.Nm fido_dev_largeblob_get ,
+.Nm fido_dev_largeblob_set ,
+.Nm fido_dev_largeblob_remove ,
+.Nm fido_dev_largeblob_get_array ,
+.Nm fido_dev_largeblob_set_array
+.Nd FIDO 2 large blob API
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_largeblob_get "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "unsigned char **blob_ptr" "size_t *blob_len"
+.Ft int
+.Fn fido_dev_largeblob_set "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "const unsigned char *blob_ptr" "size_t blob_len" "const char *pin"
+.Ft int
+.Fn fido_dev_largeblob_remove "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "const char *pin"
+.Ft int
+.Fn fido_dev_largeblob_get_array "fido_dev_t *dev" "unsigned char **cbor_ptr" "size_t *cbor_len"
+.Ft int
+.Fn fido_dev_largeblob_set_array "fido_dev_t *dev" "const unsigned char *cbor_ptr" "size_t cbor_len" "const char *pin"
+.Sh DESCRIPTION
+The
+.Dq largeBlobs
+API of
+.Em libfido2
+allows binary blobs residing on a FIDO 2.1 authenticator to be
+read, written, and inspected.
+.Dq largeBlobs
+is a FIDO 2.1 extension.
+.Pp
+.Dq largeBlobs
+are stored as elements of a CBOR array.
+Confidentiality is ensured by encrypting each element with a
+distinct, credential-bound 256-bit AES-GCM key.
+The array is otherwise shared between different credentials and
+FIDO2 relying parties.
+.Pp
+Retrieval of a credential's encryption key is possible during
+enrollment with
+.Xr fido_cred_set_extensions 3
+and
+.Xr fido_cred_largeblob_key_ptr 3 ,
+during assertion with
+.Xr fido_assert_set_extensions 3
+and
+.Xr fido_assert_largeblob_key_ptr 3 ,
+or, in the case of a resident credential, via
+.Em libfido2's
+credential management API.
+.Pp
+The
+.Dq largeBlobs
+CBOR array is opaque to the authenticator.
+Management of the array is left at the discretion of FIDO2 clients.
+For further details on FIDO 2.1's
+.Dq largeBlobs
+extension, please refer to the FIDO 2.1 spec.
+.Pp
+The
+.Fn fido_dev_largeblob_get
+function retrieves the authenticator's
+.Dq largeBlobs
+CBOR array and, on success, returns the first blob
+.Pq iterating from array index zero
+that can be
+decrypted by
+.Fa key_ptr ,
+where
+.Fa key_ptr
+points to
+.Fa key_len
+bytes.
+On success,
+.Fn fido_dev_largeblob_get
+sets
+.Fa blob_ptr
+to the body of the decrypted blob, and
+.Fa blob_len
+to the length of the decrypted blob in bytes.
+It is the caller's responsibility to free
+.Fa blob_ptr .
+.Pp
+The
+.Fn fido_dev_largeblob_set
+function uses
+.Fa key_ptr
+to encrypt
+.Fa blob_ptr
+and inserts the result in the authenticator's
+.Dq largeBlobs
+CBOR array.
+Insertion happens at the end of the array if no existing element
+can be decrypted by
+.Fa key_ptr ,
+or at the position of the first element
+.Pq iterating from array index zero
+that can be decrypted by
+.Fa key_ptr .
+.Fa key_len
+holds the length of
+.Fa key_ptr
+in bytes, and
+.Fa blob_len
+the length of
+.Fa blob_ptr
+in bytes.
+A
+.Fa pin
+or equivalent user-verification gesture is required.
+.Pp
+The
+.Fn fido_dev_largeblob_remove
+function retrieves the authenticator's
+.Dq largeBlobs
+CBOR array and, on success, drops the first blob
+.Pq iterating from array index zero
+that can be decrypted by
+.Fa key_ptr ,
+where
+.Fa key_ptr
+points to
+.Fa key_len
+bytes.
+A
+.Fa pin
+or equivalent user-verification gesture is required.
+.Pp
+The
+.Fn fido_dev_largeblob_get_array
+function retrieves the authenticator's
+.Dq largeBlobs
+CBOR array and, on success,
+sets
+.Fa cbor_ptr
+to the body of the CBOR array, and
+.Fa cbor_len
+to its corresponding length in bytes.
+It is the caller's responsibility to free
+.Fa cbor_ptr .
+.Pp
+Finally, the
+.Fn fido_dev_largeblob_set_array
+function sets the authenticator's
+.Dq largeBlobs
+CBOR array to the data pointed to by
+.Fa cbor_ptr ,
+where
+.Fa cbor_ptr
+points to
+.Fa cbor_len
+bytes.
+A
+.Fa pin
+or equivalent user-verification gesture is required.
+.Sh RETURN VALUES
+The functions
+.Fn fido_dev_largeblob_set ,
+.Fn fido_dev_largeblob_get ,
+.Fn fido_dev_largeblob_remove ,
+.Fn fido_dev_largeblob_get_array ,
+and
+.Fn fido_dev_largeblob_set_array
+return
+.Dv FIDO_OK
+on success.
+On error, an error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr fido_assert_largeblob_key_len 3 ,
+.Xr fido_assert_largeblob_key_ptr 3 ,
+.Xr fido_assert_set_extensions 3 ,
+.Xr fido_cred_largeblob_key_len 3 ,
+.Xr fido_cred_largeblob_key_ptr 3 ,
+.Xr fido_cred_set_extensions 3 ,
+.Xr fido_credman_dev_get_rk 3 ,
+.Xr fido_credman_dev_get_rp 3 ,
+.Xr fido_dev_get_assert 3 ,
+.Xr fido_dev_make_cred 3
+.Sh CAVEATS
+The
+.Dq largeBlobs
+extension is not meant to be used to store sensitive data.
+When retrieved, a credential's
+.Dq largeBlobs
+encryption key is transmitted in the clear, and an authenticator's
+.Dq largeBlobs
+CBOR array can be read without user interaction or verification.
diff --git a/man/fido_dev_make_cred.3 b/man/fido_dev_make_cred.3
new file mode 100644
index 000000000000..cd156dc94f89
--- /dev/null
+++ b/man/fido_dev_make_cred.3
@@ -0,0 +1,77 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 23 2018 $
+.Dt FIDO_DEV_MAKE_CRED 3
+.Os
+.Sh NAME
+.Nm fido_dev_make_cred
+.Nd generates a new credential on a FIDO device
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_make_cred "fido_dev_t *dev" " fido_cred_t *cred" "const char *pin"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_make_cred
+function asks the FIDO device represented by
+.Fa dev
+to generate a new credential according to the following parameters
+defined in
+.Fa cred :
+.Pp
+.Bl -dash -compact
+.It
+.Nm type ;
+.It
+.Nm client data hash ;
+.It
+.Nm relying party ;
+.It
+.Nm user attributes ;
+.It
+.Nm list of excluded credential IDs ;
+.It
+.Nm resident/discoverable key and user verification attributes .
+.El
+.Pp
+See
+.Xr fido_cred_set_authdata 3
+for information on how these values are set.
+.Pp
+If a PIN is not needed to authenticate the request against
+.Fa dev ,
+then
+.Fa pin
+may be NULL.
+Otherwise
+.Fa pin
+must point to a NUL-terminated UTF-8 string.
+.Pp
+After a successful call to
+.Fn fido_dev_make_cred ,
+the
+.Xr fido_cred_authdata_ptr 3 ,
+.Xr fido_cred_pubkey_ptr 3 ,
+.Xr fido_cred_x5c_ptr 3 ,
+and
+.Xr fido_cred_sig_ptr 3
+functions may be invoked on
+.Fa cred
+to retrieve the various parts of the generated credential.
+.Pp
+Please note that
+.Fn fido_dev_make_cred
+is synchronous and will block if necessary.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_make_cred
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_cred_new 3 ,
+.Xr fido_cred_set_authdata 3
diff --git a/man/fido_dev_open.3 b/man/fido_dev_open.3
new file mode 100644
index 000000000000..f2af7817d801
--- /dev/null
+++ b/man/fido_dev_open.3
@@ -0,0 +1,250 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 25 2018 $
+.Dt FIDO_DEV_OPEN 3
+.Os
+.Sh NAME
+.Nm fido_dev_open ,
+.Nm fido_dev_close ,
+.Nm fido_dev_cancel ,
+.Nm fido_dev_new ,
+.Nm fido_dev_free ,
+.Nm fido_dev_force_fido2 ,
+.Nm fido_dev_force_u2f ,
+.Nm fido_dev_is_fido2 ,
+.Nm fido_dev_is_winhello ,
+.Nm fido_dev_supports_credman ,
+.Nm fido_dev_supports_cred_prot ,
+.Nm fido_dev_supports_pin ,
+.Nm fido_dev_has_pin ,
+.Nm fido_dev_supports_uv ,
+.Nm fido_dev_has_uv ,
+.Nm fido_dev_protocol ,
+.Nm fido_dev_build ,
+.Nm fido_dev_flags ,
+.Nm fido_dev_major ,
+.Nm fido_dev_minor
+.Nd FIDO 2 device open/close and related functions
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_open "fido_dev_t *dev" "const char *path"
+.Ft int
+.Fn fido_dev_close "fido_dev_t *dev"
+.Ft int
+.Fn fido_dev_cancel "fido_dev_t *dev"
+.Ft fido_dev_t *
+.Fn fido_dev_new "void"
+.Ft void
+.Fn fido_dev_free "fido_dev_t **dev_p"
+.Ft void
+.Fn fido_dev_force_fido2 "fido_dev_t *dev"
+.Ft void
+.Fn fido_dev_force_u2f "fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_is_fido2 "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_is_winhello "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_supports_credman "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_supports_cred_prot "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_supports_pin "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_has_pin "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_supports_uv "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_has_uv "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_protocol "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_build "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_flags "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_major "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_minor "const fido_dev_t *dev"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_open
+function opens the device pointed to by
+.Fa path ,
+where
+.Fa dev
+is a freshly allocated or otherwise closed
+.Vt fido_dev_t .
+If
+.Fa dev
+claims to be FIDO2,
+.Em libfido2
+will attempt to speak FIDO2 to
+.Fa dev .
+If that fails,
+.Em libfido2
+will fallback to U2F unless the
+.Dv FIDO_DISABLE_U2F_FALLBACK
+flag was set in
+.Xr fido_init 3 .
+.Pp
+The
+.Fn fido_dev_close
+function closes the device represented by
+.Fa dev .
+If
+.Fa dev
+is already closed,
+.Fn fido_dev_close
+is a NOP.
+.Pp
+The
+.Fn fido_dev_cancel
+function cancels any pending requests on
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_dev_t .
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_dev_free
+function releases the memory backing
+.Fa *dev_p ,
+where
+.Fa *dev_p
+must have been previously allocated by
+.Fn fido_dev_new .
+On return,
+.Fa *dev_p
+is set to NULL.
+Either
+.Fa dev_p
+or
+.Fa *dev_p
+may be NULL, in which case
+.Fn fido_dev_free
+is a NOP.
+.Pp
+The
+.Fn fido_dev_force_fido2
+function can be used to force CTAP2 communication with
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_force_u2f
+function can be used to force CTAP1 (U2F) communication with
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_is_fido2
+function returns
+.Dv true
+if
+.Fa dev
+is a FIDO 2 device.
+.Pp
+The
+.Fn fido_dev_is_winhello
+function returns
+.Dv true
+if
+.Fa dev
+is a Windows Hello device.
+.Pp
+The
+.Fn fido_dev_supports_credman
+function returns
+.Dv true
+if
+.Fa dev
+supports FIDO 2.1 Credential Management.
+.Pp
+The
+.Fn fido_dev_supports_cred_prot
+function returns
+.Dv true
+if
+.Fa dev
+supports FIDO 2.1 Credential Protection.
+.Pp
+The
+.Fn fido_dev_supports_pin
+function returns
+.Dv true
+if
+.Fa dev
+supports FIDO 2.0 Client PINs.
+.Pp
+The
+.Fn fido_dev_has_pin
+function returns
+.Dv true
+if
+.Fa dev
+has a FIDO 2.0 Client PIN set.
+.Pp
+The
+.Fn fido_dev_supports_uv
+function returns
+.Dv true
+if
+.Fa dev
+supports a built-in user verification method.
+.Pp
+The
+.Fn fido_dev_has_uv
+function returns
+.Dv true
+if
+.Fa dev
+supports built-in user verification and its user verification
+feature is configured.
+.Pp
+The
+.Fn fido_dev_protocol
+function returns the CTAPHID protocol version identifier of
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_build
+function returns the CTAPHID build version number of
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_flags
+function returns the CTAPHID capabilities flags of
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_major
+function returns the CTAPHID major version number of
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_minor
+function returns the CTAPHID minor version number of
+.Fa dev .
+.Pp
+For the format and meaning of the CTAPHID parameters returned by
+functions above, please refer to the FIDO Client to Authenticator
+Protocol (CTAP) specification.
+.Sh RETURN VALUES
+On success,
+.Fn fido_dev_open
+and
+.Fn fido_dev_close
+return
+.Dv FIDO_OK .
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr fido_dev_info_manifest 3 ,
+.Xr fido_dev_set_io_functions 3 ,
+.Xr fido_init 3
diff --git a/man/fido_dev_set_io_functions.3 b/man/fido_dev_set_io_functions.3
new file mode 100644
index 000000000000..231ae2411be8
--- /dev/null
+++ b/man/fido_dev_set_io_functions.3
@@ -0,0 +1,134 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 25 2018 $
+.Dt FIDO_DEV_SET_IO_FUNCTIONS 3
+.Os
+.Sh NAME
+.Nm fido_dev_set_io_functions ,
+.Nm fido_dev_set_sigmask
+.Nd FIDO 2 device I/O interface
+.Sh SYNOPSIS
+.In fido.h
+.Bd -literal
+typedef void *fido_dev_io_open_t(const char *);
+typedef void fido_dev_io_close_t(void *);
+typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int);
+typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t);
+
+typedef struct fido_dev_io {
+ fido_dev_io_open_t *open;
+ fido_dev_io_close_t *close;
+ fido_dev_io_read_t *read;
+ fido_dev_io_write_t *write;
+} fido_dev_io_t;
+
+#ifdef _WIN32
+typedef int fido_sigset_t;
+#else
+typedef sigset_t fido_sigset_t;
+#endif
+.Ed
+.Ft int
+.Fn fido_dev_set_io_functions "fido_dev_t *dev" "const fido_dev_io_t *io"
+.Ft int
+.Fn fido_dev_set_sigmask "fido_dev_t *dev" "const fido_sigset_t *sigmask"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_set_io_functions
+function sets the I/O handlers used by
+.Em libfido2
+to talk to
+.Fa dev .
+By default, these handlers are set to the operating system's native HID or NFC
+interfaces.
+They are defined as follows:
+.Bl -tag -width Ds
+.It Vt fido_dev_open_t
+Receives a
+.Vt const char *
+holding a path and opens the corresponding device, returning a
+non-NULL opaque pointer on success and NULL on error.
+.It Vt fido_dev_close_t
+Receives the opaque pointer returned by
+.Vt fido_dev_open_t
+and closes the device.
+.It Vt fido_dev_read_t
+Reads a single transmission unit (HID report, APDU) from a device.
+The first parameter is the opaque pointer returned by
+.Vt fido_dev_open_t .
+The second parameter is the read buffer, and the third parameter
+is the read buffer size.
+The fourth parameter is the number of milliseconds the caller is
+willing to sleep, should the call need to block.
+If this value holds -1,
+.Vt fido_dev_read_t
+may block indefinitely.
+On success, the number of bytes read is returned.
+On error, -1 is returned.
+.It Vt fido_dev_write_t
+Writes a single transmission unit (HID report, APDU) to
+.Fa dev .
+The first parameter is the opaque pointer returned by
+.Vt fido_dev_open_t .
+The second parameter is the write buffer, and the third parameter
+is the number of bytes to be written.
+A
+.Vt fido_dev_write_t
+may block.
+On success, the number of bytes written is returned.
+On error, -1 is returned.
+.El
+.Pp
+When calling
+.Fn fido_dev_set_io_functions ,
+the
+.Fa open ,
+.Fa close ,
+.Fa read ,
+and
+.Fa write
+fields of
+.Fa io
+may not be NULL.
+.Pp
+No references to
+.Fa io
+are held by
+.Fn fido_dev_set_io_functions .
+.Pp
+The
+.Fn fido_dev_set_sigmask
+function may be used to specify a non-NULL signal mask
+.Fa sigmask
+to be used while
+.Em libfido2's
+default I/O handlers wait on
+.Fa dev .
+On UNIX-like operating systems,
+.Vt fido_sigset_t
+is defined as
+.Vt sigset_t .
+On Windows,
+.Vt fido_sigset_t
+is defined as
+.Vt int
+and
+.Fn fido_dev_set_sigmask
+is a no-op.
+.Pp
+No references to
+.Fa sigmask
+are held by
+.Fn fido_dev_set_sigmask .
+.Sh RETURN VALUES
+On success,
+.Fn fido_dev_set_io_functions
+and
+.Fn fido_dev_set_sigmask
+return
+.Dv FIDO_OK .
+On error, a different error code defined in
+.In fido/err.h
+is returned.
diff --git a/man/fido_dev_set_pin.3 b/man/fido_dev_set_pin.3
new file mode 100644
index 000000000000..f5ef94ff6fb5
--- /dev/null
+++ b/man/fido_dev_set_pin.3
@@ -0,0 +1,103 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 25 2018 $
+.Dt FIDO_DEV_SET_PIN 3
+.Os
+.Sh NAME
+.Nm fido_dev_set_pin ,
+.Nm fido_dev_get_retry_count ,
+.Nm fido_dev_get_uv_retry_count ,
+.Nm fido_dev_reset
+.Nd FIDO 2 device management functions
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_set_pin "fido_dev_t *dev" "const char *pin" "const char *oldpin"
+.Ft int
+.Fn fido_dev_get_retry_count "fido_dev_t *dev" "int *retries"
+.Ft int
+.Fn fido_dev_get_uv_retry_count "fido_dev_t *dev" "int *retries"
+.Ft int
+.Fn fido_dev_reset "fido_dev_t *dev"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_set_pin
+function sets the PIN of device
+.Fa dev
+to
+.Fa pin ,
+where
+.Fa pin
+is a NUL-terminated UTF-8 string.
+If
+.Fa oldpin
+is not NULL, the device's PIN is changed from
+.Fa oldpin
+to
+.Fa pin ,
+where
+.Fa pin
+and
+.Fa oldpin
+are NUL-terminated UTF-8 strings.
+.Pp
+The
+.Fn fido_dev_get_retry_count
+function fills
+.Fa retries
+with the number of PIN retries left in
+.Fa dev
+before lock-out, where
+.Fa retries
+is an addressable pointer.
+.Pp
+The
+.Fn fido_dev_get_uv_retry_count
+function fills
+.Fa retries
+with the number of built-in UV retries left in
+.Fa dev
+before built-in UV is disabled, where
+.Fa retries
+is an addressable pointer.
+.Pp
+The
+.Fn fido_dev_reset
+function performs a reset on
+.Fa dev ,
+resetting the device's PIN and erasing credentials stored on the
+device.
+.Pp
+Please note that
+.Fn fido_dev_set_pin ,
+.Fn fido_dev_get_retry_count ,
+.Fn fido_dev_get_uv_retry_count ,
+and
+.Fn fido_dev_reset
+are synchronous and will block if necessary.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_set_pin ,
+.Fn fido_dev_get_retry_count ,
+.Fn fido_dev_get_uv_retry_count ,
+and
+.Fn fido_dev_reset
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh CAVEATS
+Regarding
+.Fn fido_dev_reset ,
+the actual user-flow to perform a reset is outside the scope of the
+FIDO2 specification, and may therefore vary depending on the
+authenticator.
+Yubico authenticators will return
+.Dv FIDO_ERR_NOT_ALLOWED
+if a reset is issued later than 5 seconds after power-up, and
+.Dv FIDO_ERR_ACTION_TIMEOUT
+if the user fails to confirm the reset by touching the key
+within 30 seconds.
diff --git a/man/fido_init.3 b/man/fido_init.3
new file mode 100644
index 000000000000..dcfc530c59ae
--- /dev/null
+++ b/man/fido_init.3
@@ -0,0 +1,52 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 25 2018 $
+.Dt FIDO_INIT 3
+.Os
+.Sh NAME
+.Nm fido_init
+.Nd initialise the FIDO 2 library
+.Sh SYNOPSIS
+.In fido.h
+.Ft void
+.Fn fido_init "int flags"
+.Sh DESCRIPTION
+The
+.Fn fido_init
+function initialises the
+.Em libfido2
+library.
+Its invocation must precede that of any other
+.Em libfido2
+function in the context of the executing thread.
+.Pp
+If
+.Dv FIDO_DEBUG
+is set in
+.Fa flags ,
+then
+debug output will be emitted by
+.Em libfido2
+on
+.Em stderr .
+Alternatively, the
+.Ev FIDO_DEBUG
+environment variable may be set.
+.Pp
+If
+.Dv FIDO_DISABLE_U2F_FALLBACK
+is set in
+.Fa flags ,
+then
+.Em libfido2
+will not fallback to U2F in
+.Xr fido_dev_open 3
+if a device claims to be FIDO2 but fails to respond to a
+FIDO2 command.
+.Sh SEE ALSO
+.Xr fido_assert_new 3 ,
+.Xr fido_cred_new 3 ,
+.Xr fido_dev_info_manifest 3 ,
+.Xr fido_dev_open 3
diff --git a/man/fido_strerr.3 b/man/fido_strerr.3
new file mode 100644
index 000000000000..05c86b92a158
--- /dev/null
+++ b/man/fido_strerr.3
@@ -0,0 +1,27 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 25 2018 $
+.Dt FIDO_STRERR 3
+.Os
+.Sh NAME
+.Nm fido_strerr
+.Nd FIDO 2 error codes
+.Sh SYNOPSIS
+.In fido.h
+.Ft const char *
+.Fn fido_strerr "int n"
+.Sh DESCRIPTION
+The
+.Fn fido_strerr
+function translates the error code
+.Fa n
+into a readable string,
+where
+.Fa n
+is an error code defined in
+.In fido/err.h .
+.Fn fido_strerr
+never returns NULL.
+Returned pointers point to static strings.
diff --git a/man/rs256_pk_new.3 b/man/rs256_pk_new.3
new file mode 100644
index 000000000000..4ad0ebe936f3
--- /dev/null
+++ b/man/rs256_pk_new.3
@@ -0,0 +1,122 @@
+.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: May 24 2018 $
+.Dt RS256_PK_NEW 3
+.Os
+.Sh NAME
+.Nm rs256_pk_new ,
+.Nm rs256_pk_free ,
+.Nm rs256_pk_from_RSA ,
+.Nm rs256_pk_from_ptr ,
+.Nm rs256_pk_to_EVP_PKEY
+.Nd FIDO 2 COSE RS256 API
+.Sh SYNOPSIS
+.In openssl/rsa.h
+.In fido/rs256.h
+.Ft rs256_pk_t *
+.Fn rs256_pk_new "void"
+.Ft void
+.Fn rs256_pk_free "rs256_pk_t **pkp"
+.Ft int
+.Fn rs256_pk_from_RSA "rs256_pk_t *pk" "const RSA *rsa"
+.Ft int
+.Fn rs256_pk_from_ptr "rs256_pk_t *pk" "const void *ptr" "size_t len"
+.Ft EVP_PKEY *
+.Fn rs256_pk_to_EVP_PKEY "const rs256_pk_t *pk"
+.Sh DESCRIPTION
+RS256 is the name given in the CBOR Object Signing and Encryption
+(COSE) RFC to PKCS#1.5 2048-bit RSA with SHA-256.
+The COSE RS256 API of
+.Em libfido2
+is an auxiliary API with routines to convert between the different
+RSA public key types used in
+.Em libfido2
+and
+.Em OpenSSL .
+.Pp
+In
+.Em libfido2 ,
+RS256 public keys are abstracted by the
+.Vt rs256_pk_t
+type.
+.Pp
+The
+.Fn rs256_pk_new
+function returns a pointer to a newly allocated, empty
+.Vt rs256_pk_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn rs256_pk_free
+function releases the memory backing
+.Fa *pkp ,
+where
+.Fa *pkp
+must have been previously allocated by
+.Fn rs256_pk_new .
+On return,
+.Fa *pkp
+is set to NULL.
+Either
+.Fa pkp
+or
+.Fa *pkp
+may be NULL, in which case
+.Fn rs256_pk_free
+is a NOP.
+.Pp
+The
+.Fn rs256_pk_from_RSA
+function fills
+.Fa pk
+with the contents of
+.Fa rsa .
+No references to
+.Fa rsa
+are kept.
+.Pp
+The
+.Fn rs256_pk_from_ptr
+function fills
+.Fa pk
+with the contents of
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+No references to
+.Fa ptr
+are kept.
+.Pp
+The
+.Fn rs256_pk_to_EVP_PKEY
+function converts
+.Fa pk
+to a newly allocated
+.Fa EVP_PKEY
+type with a reference count of 1.
+No internal references to the returned pointer are kept.
+If an error occurs,
+.Fn rs256_pk_to_EVP_PKEY
+returns NULL.
+.Sh RETURN VALUES
+The
+.Fn rs256_pk_from_RSA
+and
+.Fn rs256_pk_from_ptr
+functions return
+.Dv FIDO_OK
+on success.
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr eddsa_pk_new 3 ,
+.Xr es256_pk_new 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_cred_pubkey_ptr 3
diff --git a/man/style.css b/man/style.css
new file mode 100644
index 000000000000..8c223faa9a19
--- /dev/null
+++ b/man/style.css
@@ -0,0 +1,24 @@
+* { margin: 0; padding: 0; }
+
+body {
+ font-family: monospace;
+ font-size: 1em;
+ margin: 2% auto;
+ max-width: 54em;
+}
+
+ul { margin-left: 1em; }
+a { color: #009900; }
+.Sh { font-size: 1em; padding-top: 1em; padding-bottom: 1em; }
+.foot { padding-top: 1em; }
+
+table.head, table.foot { width: 100%; }
+td.head-rtitle, td.foot-os { text-align: right; }
+td.head-vol { text-align: center; }
+div.Pp { margin: 1ex 0ex; }
+div.Nd, div.Bf, div.Op { display: inline; }
+span.Pa, span.Ad { font-style: italic; }
+span.Ms { font-weight: bold; }
+dl.Bl-diag > dt { font-weight: bold; }
+code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
+code.Cd { font-weight: bold; font-family: inherit; }
diff --git a/openbsd-compat/bsd-getline.c b/openbsd-compat/bsd-getline.c
new file mode 100644
index 000000000000..52b44f70ba2f
--- /dev/null
+++ b/openbsd-compat/bsd-getline.c
@@ -0,0 +1,115 @@
+/* $NetBSD: getline.c,v 1.1.1.6 2015/01/02 20:34:27 christos Exp $ */
+
+/* NetBSD: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* NETBSD ORIGINAL: external/bsd/file/dist/src/getline.c */
+
+#include "openbsd-compat.h"
+
+#if 0
+#include "file.h"
+#endif
+
+#if !HAVE_GETLINE
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+
+static ssize_t
+getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
+{
+ char *ptr, *eptr;
+
+
+ if (*buf == NULL || *bufsiz == 0) {
+ if ((*buf = malloc(BUFSIZ)) == NULL)
+ return -1;
+ *bufsiz = BUFSIZ;
+ }
+
+ for (ptr = *buf, eptr = *buf + *bufsiz;;) {
+ int c = fgetc(fp);
+ if (c == -1) {
+ if (feof(fp)) {
+ ssize_t diff = (ssize_t)(ptr - *buf);
+ if (diff != 0) {
+ *ptr = '\0';
+ return diff;
+ }
+ }
+ return -1;
+ }
+ *ptr++ = (char)c;
+ if (c == delimiter) {
+ *ptr = '\0';
+ return ptr - *buf;
+ }
+ if (ptr + 2 >= eptr) {
+ char *nbuf;
+ size_t nbufsiz = *bufsiz * 2;
+ ssize_t d = ptr - *buf;
+ if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
+ return -1;
+ *buf = nbuf;
+ *bufsiz = nbufsiz;
+ eptr = nbuf + nbufsiz;
+ ptr = nbuf + d;
+ }
+ }
+}
+
+ssize_t
+getline(char **buf, size_t *bufsiz, FILE *fp)
+{
+ return getdelim(buf, bufsiz, '\n', fp);
+}
+
+#endif
+
+#ifdef TEST
+int
+main(int argc, char *argv[])
+{
+ char *p = NULL;
+ ssize_t len;
+ size_t n = 0;
+
+ while ((len = getline(&p, &n, stdin)) != -1)
+ (void)printf("%" SIZE_T_FORMAT "d %s", len, p);
+ free(p);
+ return 0;
+}
+#endif
diff --git a/openbsd-compat/bsd-getpagesize.c b/openbsd-compat/bsd-getpagesize.c
new file mode 100644
index 000000000000..903bfc310fb9
--- /dev/null
+++ b/openbsd-compat/bsd-getpagesize.c
@@ -0,0 +1,27 @@
+/* Placed in the public domain */
+
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_GETPAGESIZE)
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <limits.h>
+
+int
+getpagesize(void)
+{
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+ long r = sysconf(_SC_PAGESIZE);
+ if (r > 0 && r < INT_MAX)
+ return (int)r;
+#endif
+ /*
+ * This is at the lower end of common values and appropriate for
+ * our current use of getpagesize() in recallocarray().
+ */
+ return 4096;
+}
+
+#endif /* !defined(HAVE_GETPAGESIZE) */
diff --git a/openbsd-compat/clock_gettime.c b/openbsd-compat/clock_gettime.c
new file mode 100644
index 000000000000..ca261a65e7f1
--- /dev/null
+++ b/openbsd-compat/clock_gettime.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_CLOCK_GETTIME)
+
+#if _WIN32
+int
+clock_gettime(clockid_t clock_id, struct timespec *tp)
+{
+ ULONGLONG ms;
+
+ if (clock_id != CLOCK_MONOTONIC) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ ms = GetTickCount64();
+ tp->tv_sec = ms / 1000L;
+ tp->tv_nsec = (ms % 1000L) * 1000000L;
+
+ return (0);
+}
+#else
+#error "please provide an implementation of clock_gettime() for your platform"
+#endif /* _WIN32 */
+
+#endif /* !defined(HAVE_CLOCK_GETTIME) */
diff --git a/openbsd-compat/endian_win32.c b/openbsd-compat/endian_win32.c
new file mode 100644
index 000000000000..9981dfafbaeb
--- /dev/null
+++ b/openbsd-compat/endian_win32.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include "openbsd-compat.h"
+
+#if defined(_WIN32) && !defined(HAVE_ENDIAN_H)
+
+/*
+ * Hopefully, if the endianness differs from the end result, the compiler
+ * optimizes these functions with some type of bswap instruction. Or,
+ * otherwise, to just return the input value unmodified. GCC and clang
+ * both does these optimization at least. This should be preferred over
+ * relying on some BYTE_ORDER macro, which may or may not be defined.
+ */
+
+uint32_t
+htole32(uint32_t in)
+{
+ uint32_t out = 0;
+ uint8_t *b = (uint8_t *)&out;
+
+ b[0] = (uint8_t)((in >> 0) & 0xff);
+ b[1] = (uint8_t)((in >> 8) & 0xff);
+ b[2] = (uint8_t)((in >> 16) & 0xff);
+ b[3] = (uint8_t)((in >> 24) & 0xff);
+
+ return (out);
+}
+
+uint64_t
+htole64(uint64_t in)
+{
+ uint64_t out = 0;
+ uint8_t *b = (uint8_t *)&out;
+
+ b[0] = (uint8_t)((in >> 0) & 0xff);
+ b[1] = (uint8_t)((in >> 8) & 0xff);
+ b[2] = (uint8_t)((in >> 16) & 0xff);
+ b[3] = (uint8_t)((in >> 24) & 0xff);
+ b[4] = (uint8_t)((in >> 32) & 0xff);
+ b[5] = (uint8_t)((in >> 40) & 0xff);
+ b[6] = (uint8_t)((in >> 48) & 0xff);
+ b[7] = (uint8_t)((in >> 56) & 0xff);
+
+ return (out);
+}
+
+#endif /* WIN32 && !HAVE_ENDIAN_H */
diff --git a/openbsd-compat/err.h b/openbsd-compat/err.h
new file mode 100644
index 000000000000..394c7bb12f68
--- /dev/null
+++ b/openbsd-compat/err.h
@@ -0,0 +1,85 @@
+/*
+ * Public domain
+ * err.h compatibility shim
+ */
+
+#ifndef _COMPAT_ERR_H
+#define _COMPAT_ERR_H
+
+#if !defined(HAVE_ERR_H)
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(_MSC_VER)
+__declspec(noreturn)
+#else
+__attribute__((noreturn))
+#endif
+static inline void
+err(int eval, const char *fmt, ...)
+{
+ int sverrno = errno;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (fmt != NULL) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": ");
+ }
+ va_end(ap);
+ fprintf(stderr, "%s\n", strerror(sverrno));
+ exit(eval);
+}
+
+#if defined(_MSC_VER)
+__declspec(noreturn)
+#else
+__attribute__((noreturn))
+#endif
+static inline void
+errx(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (fmt != NULL)
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ exit(eval);
+}
+
+static inline void
+warn(const char *fmt, ...)
+{
+ int sverrno = errno;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (fmt != NULL) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": ");
+ }
+ va_end(ap);
+ fprintf(stderr, "%s\n", strerror(sverrno));
+}
+
+static inline void
+warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (fmt != NULL)
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+#endif /* !defined(HAVE_ERR_H) */
+
+#endif /* _COMPAT_ERR_H */
diff --git a/openbsd-compat/explicit_bzero.c b/openbsd-compat/explicit_bzero.c
new file mode 100644
index 000000000000..ac64e69b4d18
--- /dev/null
+++ b/openbsd-compat/explicit_bzero.c
@@ -0,0 +1,57 @@
+/* OPENBSD ORIGINAL: lib/libc/string/explicit_bzero.c */
+/* $OpenBSD: explicit_bzero.c,v 1.1 2014/01/22 21:06:45 tedu Exp $ */
+/*
+ * Public domain.
+ * Written by Ted Unangst
+ */
+
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_EXPLICIT_BZERO) && !defined(_WIN32)
+
+#include <string.h>
+
+/*
+ * explicit_bzero - don't let the compiler optimize away bzero
+ */
+
+#ifdef HAVE_MEMSET_S
+
+void
+explicit_bzero(void *p, size_t n)
+{
+ if (n == 0)
+ return;
+ (void)memset_s(p, n, 0, n);
+}
+
+#else /* HAVE_MEMSET_S */
+
+/*
+ * Indirect bzero through a volatile pointer to hopefully avoid
+ * dead-store optimisation eliminating the call.
+ */
+static void (* volatile ssh_bzero)(void *, size_t) = bzero;
+
+void
+explicit_bzero(void *p, size_t n)
+{
+ if (n == 0)
+ return;
+ /*
+ * clang -fsanitize=memory needs to intercept memset-like functions
+ * to correctly detect memory initialisation. Make sure one is called
+ * directly since our indirection trick above successfully confuses it.
+ */
+#if defined(__has_feature)
+# if __has_feature(memory_sanitizer)
+ memset(p, 0, n);
+# endif
+#endif
+
+ ssh_bzero(p, n);
+}
+
+#endif /* HAVE_MEMSET_S */
+
+#endif /* !defined(HAVE_EXPLICIT_BZERO) && !defined(_WIN32) */
diff --git a/openbsd-compat/explicit_bzero_win32.c b/openbsd-compat/explicit_bzero_win32.c
new file mode 100644
index 000000000000..8017aff99991
--- /dev/null
+++ b/openbsd-compat/explicit_bzero_win32.c
@@ -0,0 +1,19 @@
+/*
+ * Public domain.
+ * Win32 explicit_bzero compatibility shim.
+ */
+
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_EXPLICIT_BZERO) && defined(_WIN32)
+
+#include <windows.h>
+#include <string.h>
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ SecureZeroMemory(buf, len);
+}
+
+#endif /* !defined(HAVE_EXPLICIT_BZERO) && defined(_WIN32) */
diff --git a/openbsd-compat/freezero.c b/openbsd-compat/freezero.c
new file mode 100644
index 000000000000..d1e00661fd27
--- /dev/null
+++ b/openbsd-compat/freezero.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "openbsd-compat.h"
+
+#ifndef HAVE_FREEZERO
+
+void
+freezero(void *ptr, size_t sz)
+{
+ if (ptr == NULL)
+ return;
+ explicit_bzero(ptr, sz);
+ free(ptr);
+}
+
+#endif /* HAVE_FREEZERO */
diff --git a/openbsd-compat/getopt.h b/openbsd-compat/getopt.h
new file mode 100644
index 000000000000..8eb12447ed64
--- /dev/null
+++ b/openbsd-compat/getopt.h
@@ -0,0 +1,74 @@
+/* $OpenBSD: getopt.h,v 1.2 2008/06/26 05:42:04 ray Exp $ */
+/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _GETOPT_H_
+#define _GETOPT_H_
+
+/*
+ * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions
+ */
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+struct option {
+ /* name of long option */
+ const char *name;
+ /*
+ * one of no_argument, required_argument, and optional_argument:
+ * whether option takes an argument
+ */
+ int has_arg;
+ /* if not NULL, set *flag to val when option found */
+ int *flag;
+ /* if flag not NULL, value to set *flag to; else return value */
+ int val;
+};
+
+int getopt_long(int, char * const *, const char *,
+ const struct option *, int *);
+int getopt_long_only(int, char * const *, const char *,
+ const struct option *, int *);
+#ifndef _GETOPT_DEFINED_
+#define _GETOPT_DEFINED_
+int getopt(int, char * const *, const char *);
+int getsubopt(char **, char * const *, char **);
+
+extern char *optarg; /* getopt(3) external variables */
+extern int opterr;
+extern int optind;
+extern int optopt;
+extern int optreset;
+extern char *suboptarg; /* getsubopt(3) external variable */
+#endif
+
+#endif /* !_GETOPT_H_ */
diff --git a/openbsd-compat/getopt_long.c b/openbsd-compat/getopt_long.c
new file mode 100644
index 000000000000..dabbb461cbe5
--- /dev/null
+++ b/openbsd-compat/getopt_long.c
@@ -0,0 +1,523 @@
+/* $OpenBSD: getopt_long.c,v 1.25 2011/03/05 22:10:11 guenther Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_GETOPT)
+
+#if 0
+#include <err.h>
+#include <getopt.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define EMSG ""
+
+static int getopt_internal(int, char * const *, const char *,
+ const struct option *, int *, int);
+static int parse_long_options(char * const *, const char *,
+ const struct option *, int *, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char * const *nargv)
+{
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end+i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ ((char **) nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ ((char **)nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+ * parse_long_options --
+ * Parse long options in argc/argv argument vector.
+ * Returns -1 if short_too is set and the option does not match long_options.
+ */
+static int
+parse_long_options(char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int short_too)
+{
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, match;
+
+ current_argv = place;
+ match = -1;
+
+ optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name,
+ current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* partial match */
+ match = i;
+ else {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len,
+ current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument
+ && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return (BADARG);
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg ==
+ required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --optind;
+ return (-1);
+ }
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return (0);
+ } else
+ return (long_options[match].val);
+}
+
+/*
+ * getopt_internal --
+ * Parse argc/argv argument vector. Called by user level routines.
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int flags)
+{
+ char *oli; /* option letter list index */
+ int optchar, short_too;
+ static int posixly_correct = -1;
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * XXX Some GNU programs (like cvs) set optind to 0 instead of
+ * XXX using optreset. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = optreset = 1;
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ */
+ if (posixly_correct == -1 || optreset)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+ if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ else if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ if (*options == '+' || *options == '-')
+ options++;
+
+ optarg = NULL;
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[optind]) != '-' ||
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ nonopt_start = optind -
+ (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+ if (*place == '-')
+ place++; /* --foo long option */
+ else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, short_too);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ (optchar == (int)'-' && *place != '\0') ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[optind];
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, 0);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = place;
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else
+ optarg = nargv[optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the BSD getopt]
+ */
+int
+getopt(int nargc, char * const *nargv, const char *options)
+{
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+
+#if 0
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE));
+}
+
+/*
+ * getopt_long_only --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long_only(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE|FLAG_LONGONLY));
+}
+#endif
+
+#endif /* !defined(HAVE_GETOPT) */
diff --git a/openbsd-compat/hkdf.c b/openbsd-compat/hkdf.c
new file mode 100644
index 000000000000..745b420f3747
--- /dev/null
+++ b/openbsd-compat/hkdf.c
@@ -0,0 +1,124 @@
+/* $OpenBSD: hkdf.c,v 1.4 2019/11/21 20:02:20 tim Exp $ */
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "openbsd-compat.h"
+#include "fido.h"
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+
+#define CRYPTOerror(r) CRYPTOerr(ERR_LIB_CRYPTO, (r))
+
+/* https://tools.ietf.org/html/rfc5869#section-2 */
+int
+HKDF(uint8_t *out_key, size_t out_len, const EVP_MD *digest,
+ const uint8_t *secret, size_t secret_len, const uint8_t *salt,
+ size_t salt_len, const uint8_t *info, size_t info_len)
+{
+ uint8_t prk[EVP_MAX_MD_SIZE];
+ size_t prk_len;
+
+ if (!HKDF_extract(prk, &prk_len, digest, secret, secret_len, salt,
+ salt_len))
+ return 0;
+ if (!HKDF_expand(out_key, out_len, digest, prk, prk_len, info,
+ info_len))
+ return 0;
+
+ return 1;
+}
+
+/* https://tools.ietf.org/html/rfc5869#section-2.2 */
+int
+HKDF_extract(uint8_t *out_key, size_t *out_len,
+ const EVP_MD *digest, const uint8_t *secret, size_t secret_len,
+ const uint8_t *salt, size_t salt_len)
+{
+ unsigned int len;
+
+ /*
+ * If salt is not given, HashLength zeros are used. However, HMAC does
+ * that internally already so we can ignore it.
+ */
+ if (salt_len > INT_MAX || HMAC(digest, salt, (int)salt_len, secret,
+ secret_len, out_key, &len) == NULL) {
+ CRYPTOerror(ERR_R_CRYPTO_LIB);
+ return 0;
+ }
+ *out_len = len;
+ return 1;
+}
+
+/* https://tools.ietf.org/html/rfc5869#section-2.3 */
+int
+HKDF_expand(uint8_t *out_key, size_t out_len,
+ const EVP_MD *digest, const uint8_t *prk, size_t prk_len,
+ const uint8_t *info, size_t info_len)
+{
+ const size_t digest_len = EVP_MD_size(digest);
+ uint8_t previous[EVP_MAX_MD_SIZE];
+ size_t n, done = 0;
+ unsigned int i;
+ int ret = 0;
+ HMAC_CTX hmac;
+
+ /* Expand key material to desired length. */
+ n = (out_len + digest_len - 1) / digest_len;
+ if (out_len + digest_len < out_len || n > 255 || prk_len > INT_MAX) {
+ CRYPTOerror(EVP_R_TOO_LARGE);
+ return 0;
+ }
+
+ HMAC_CTX_init(&hmac);
+ if (!HMAC_Init_ex(&hmac, prk, (int)prk_len, digest, NULL))
+ goto out;
+
+ for (i = 0; i < n; i++) {
+ uint8_t ctr = i + 1;
+ size_t todo;
+
+ if (i != 0 && (!HMAC_Init_ex(&hmac, NULL, 0, NULL, NULL) ||
+ !HMAC_Update(&hmac, previous, digest_len)))
+ goto out;
+
+ if (!HMAC_Update(&hmac, info, info_len) ||
+ !HMAC_Update(&hmac, &ctr, 1) ||
+ !HMAC_Final(&hmac, previous, NULL))
+ goto out;
+
+ todo = digest_len;
+ if (done + todo > out_len)
+ todo = out_len - done;
+
+ memcpy(out_key + done, previous, todo);
+ done += todo;
+ }
+
+ ret = 1;
+
+ out:
+ HMAC_CTX_cleanup(&hmac);
+ explicit_bzero(previous, sizeof(previous));
+ if (ret != 1)
+ CRYPTOerror(ERR_R_CRYPTO_LIB);
+ return ret;
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
diff --git a/openbsd-compat/hkdf.h b/openbsd-compat/hkdf.h
new file mode 100644
index 000000000000..34450f9dd7f0
--- /dev/null
+++ b/openbsd-compat/hkdf.h
@@ -0,0 +1,65 @@
+/* $OpenBSD: hkdf.h,v 1.2 2018/04/03 13:33:53 tb Exp $ */
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_HKDF_H
+#define OPENSSL_HEADER_HKDF_H
+
+#include <openssl/evp.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * HKDF computes HKDF (as specified by RFC 5869) of initial keying
+ * material |secret| with |salt| and |info| using |digest|, and
+ * outputs |out_len| bytes to |out_key|. It returns one on success and
+ * zero on error.
+ *
+ * HKDF is an Extract-and-Expand algorithm. It does not do any key
+ * stretching, and as such, is not suited to be used alone to generate
+ * a key from a password.
+ */
+
+int HKDF(uint8_t *out_key, size_t out_len, const struct env_md_st *digest,
+ const uint8_t *secret, size_t secret_len, const uint8_t *salt,
+ size_t salt_len, const uint8_t *info, size_t info_len);
+
+/*
+ * HKDF_extract computes a HKDF PRK (as specified by RFC 5869) from
+ * initial keying material |secret| and salt |salt| using |digest|,
+ * and outputs |out_len| bytes to |out_key|. The maximum output size
+ * is |EVP_MAX_MD_SIZE|. It returns one on success and zero on error.
+ */
+int HKDF_extract(uint8_t *out_key, size_t *out_len,
+ const struct env_md_st *digest, const uint8_t *secret,
+ size_t secret_len, const uint8_t *salt, size_t salt_len);
+
+/*
+ * HKDF_expand computes a HKDF OKM (as specified by RFC 5869) of
+ * length |out_len| from the PRK |prk| and info |info| using |digest|,
+ * and outputs the result to |out_key|. It returns one on success and
+ * zero on error.
+ */
+int HKDF_expand(uint8_t *out_key, size_t out_len,
+ const EVP_MD *digest, const uint8_t *prk, size_t prk_len,
+ const uint8_t *info, size_t info_len);
+
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_HKDF_H */
diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h
new file mode 100644
index 000000000000..1be3aa295051
--- /dev/null
+++ b/openbsd-compat/openbsd-compat.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef _OPENBSD_COMPAT_H
+#define _OPENBSD_COMPAT_H
+
+#if defined(_MSC_VER)
+#include "types.h"
+#endif
+
+#if defined(HAVE_ENDIAN_H)
+#include <endian.h>
+#endif
+
+#if defined(__APPLE__) && !defined(HAVE_ENDIAN_H)
+#include <libkern/OSByteOrder.h>
+#define be16toh(x) OSSwapBigToHostInt16((x))
+#define htobe16(x) OSSwapHostToBigInt16((x))
+#define be32toh(x) OSSwapBigToHostInt32((x))
+#define htole32(x) OSSwapHostToLittleInt32((x))
+#define htole64(x) OSSwapHostToLittleInt64((x))
+#endif /* __APPLE__ && !HAVE_ENDIAN_H */
+
+#if defined(_WIN32) && !defined(HAVE_ENDIAN_H)
+#include <stdint.h>
+#include <winsock2.h>
+#if !defined(_MSC_VER)
+#include <sys/param.h>
+#endif
+#define be16toh(x) ntohs((x))
+#define htobe16(x) htons((x))
+#define be32toh(x) ntohl((x))
+uint32_t htole32(uint32_t);
+uint64_t htole64(uint64_t);
+#endif /* _WIN32 && !HAVE_ENDIAN_H */
+
+#if defined(__FreeBSD__) && !defined(HAVE_ENDIAN_H)
+#include <sys/endian.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(HAVE_STRLCAT)
+size_t strlcat(char *, const char *, size_t);
+#endif
+
+#if !defined(HAVE_STRLCPY)
+size_t strlcpy(char *, const char *, size_t);
+#endif
+
+#if !defined(HAVE_RECALLOCARRAY)
+void *recallocarray(void *, size_t, size_t, size_t);
+#endif
+
+#if !defined(HAVE_EXPLICIT_BZERO)
+void explicit_bzero(void *, size_t);
+#endif
+
+#if !defined(HAVE_FREEZERO)
+void freezero(void *, size_t);
+#endif
+
+#if !defined(HAVE_GETPAGESIZE)
+int getpagesize(void);
+#endif
+
+#if !defined(HAVE_TIMINGSAFE_BCMP)
+int timingsafe_bcmp(const void *, const void *, size_t);
+#endif
+
+#if !defined(HAVE_READPASSPHRASE)
+#include "readpassphrase.h"
+#else
+#include <readpassphrase.h>
+#endif
+
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#include <stdint.h>
+#include "hkdf.h"
+#define EVP_PKEY_get0_EC_KEY(x) ((x)->pkey.ec)
+#define EVP_PKEY_get0_RSA(x) ((x)->pkey.rsa)
+#endif
+
+#if !defined(HAVE_ERR_H)
+#include "err.h"
+#else
+#include <err.h>
+#endif
+
+#if !defined(HAVE_GETOPT)
+#include "getopt.h"
+#else
+#include <unistd.h>
+#endif
+
+#if !defined(HAVE_GETLINE)
+#include <stdio.h>
+ssize_t getline(char **, size_t *, FILE *);
+#endif
+
+#if defined(_MSC_VER)
+#define strerror_r(e, b, l) strerror_s((b), (l), (e))
+#endif
+
+#include "time.h"
+
+#if !defined(HAVE_POSIX_IOCTL)
+#define IOCTL_REQ(x) (x)
+#else
+#define IOCTL_REQ(x) ((int)(x))
+#endif
+
+#endif /* !_OPENBSD_COMPAT_H */
diff --git a/openbsd-compat/posix_ioctl_check.c b/openbsd-compat/posix_ioctl_check.c
new file mode 100644
index 000000000000..599a3bff3bc6
--- /dev/null
+++ b/openbsd-compat/posix_ioctl_check.c
@@ -0,0 +1,7 @@
+#include <sys/ioctl.h>
+
+int
+posix_ioctl_check(int fd)
+{
+ return ioctl(fd, -1, 0);
+}
diff --git a/openbsd-compat/posix_win.c b/openbsd-compat/posix_win.c
new file mode 100644
index 000000000000..eac67c2304f8
--- /dev/null
+++ b/openbsd-compat/posix_win.c
@@ -0,0 +1,61 @@
+/*
+ * Public domain
+ *
+ * File IO compatibility shims
+ * Brent Cook <bcook@openbsd.org>
+ */
+
+#define NO_REDEF_POSIX_FUNCTIONS
+
+#include <windows.h>
+
+#include <errno.h>
+#include <io.h>
+
+#include "posix_win.h"
+
+int
+posix_open(const char *path, ...)
+{
+ va_list ap;
+ int mode = 0;
+ int flags;
+
+ va_start(ap, path);
+ flags = va_arg(ap, int);
+ if (flags & O_CREAT)
+ mode = va_arg(ap, int);
+ va_end(ap);
+
+ flags |= O_BINARY | O_NOINHERIT;
+
+ return (open(path, flags, mode));
+}
+
+int
+posix_close(int fd)
+{
+ return (close(fd));
+}
+
+ssize_t
+posix_read(int fd, void *buf, size_t count)
+{
+ if (count > INT_MAX) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (read(fd, buf, (unsigned int)count));
+}
+
+ssize_t
+posix_write(int fd, const void *buf, size_t count)
+{
+ if (count > INT_MAX) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (write(fd, buf, (unsigned int)count));
+}
diff --git a/openbsd-compat/posix_win.h b/openbsd-compat/posix_win.h
new file mode 100644
index 000000000000..a1e0888cc7f5
--- /dev/null
+++ b/openbsd-compat/posix_win.h
@@ -0,0 +1,47 @@
+/*
+ * Public domain
+ *
+ * BSD socket emulation code for Winsock2
+ * Brent Cook <bcook@openbsd.org>
+ */
+
+#ifndef _COMPAT_POSIX_WIN_H
+#define _COMPAT_POSIX_WIN_H
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if _MSC_VER >= 1900
+#include <../ucrt/fcntl.h>
+#else
+#include <../include/fcntl.h>
+#endif
+
+#include "types.h"
+
+int posix_open(const char *path, ...);
+
+int posix_close(int fd);
+
+ssize_t posix_read(int fd, void *buf, size_t count);
+
+ssize_t posix_write(int fd, const void *buf, size_t count);
+
+#ifndef NO_REDEF_POSIX_FUNCTIONS
+#define open(path, ...) posix_open(path, __VA_ARGS__)
+#define close(fd) posix_close(fd)
+#define read(fd, buf, count) posix_read(fd, buf, count)
+#define write(fd, buf, count) posix_write(fd, buf, count)
+#endif
+
+#endif /* _WIN32 */
+
+#endif /* !_COMPAT_POSIX_WIN_H */
diff --git a/openbsd-compat/readpassphrase.c b/openbsd-compat/readpassphrase.c
new file mode 100644
index 000000000000..8b841906a735
--- /dev/null
+++ b/openbsd-compat/readpassphrase.c
@@ -0,0 +1,214 @@
+/* $OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $ */
+
+/*
+ * Copyright (c) 2000-2002, 2007, 2010
+ * Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */
+
+#include "openbsd-compat.h"
+
+#ifndef HAVE_READPASSPHRASE
+
+#include <termios.h>
+#include <signal.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <paths.h>
+
+#ifndef _PATH_TTY
+# define _PATH_TTY "/dev/tty"
+#endif
+
+#ifndef TCSASOFT
+/* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */
+# define TCSASOFT 0
+#endif
+
+/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */
+#if !defined(_POSIX_VDISABLE) && defined(VDISABLE)
+# define _POSIX_VDISABLE VDISABLE
+#endif
+
+static volatile sig_atomic_t signo[NSIG];
+
+static void handler(int);
+
+char *
+readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
+{
+ ssize_t nr;
+ int input, output, save_errno, i, need_restart;
+ char ch, *p, *end;
+ struct termios term, oterm;
+ struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
+ struct sigaction savetstp, savettin, savettou, savepipe;
+
+ /* I suppose we could alloc on demand in this case (XXX). */
+ if (bufsiz == 0) {
+ errno = EINVAL;
+ return(NULL);
+ }
+
+restart:
+ for (i = 0; i < NSIG; i++)
+ signo[i] = 0;
+ need_restart = 0;
+ /*
+ * Read and write to /dev/tty if available. If not, read from
+ * stdin and write to stderr unless a tty is required.
+ */
+ if ((flags & RPP_STDIN) ||
+ (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
+ if (flags & RPP_REQUIRE_TTY) {
+ errno = ENOTTY;
+ return(NULL);
+ }
+ input = STDIN_FILENO;
+ output = STDERR_FILENO;
+ }
+
+ /*
+ * Turn off echo if possible.
+ * If we are using a tty but are not the foreground pgrp this will
+ * generate SIGTTOU, so do it *before* installing the signal handlers.
+ */
+ if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
+ memcpy(&term, &oterm, sizeof(term));
+ if (!(flags & RPP_ECHO_ON))
+ term.c_lflag &= ~(ECHO | ECHONL);
+#ifdef VSTATUS
+ if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
+ term.c_cc[VSTATUS] = _POSIX_VDISABLE;
+#endif
+ (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
+ } else {
+ memset(&term, 0, sizeof(term));
+ term.c_lflag |= ECHO;
+ memset(&oterm, 0, sizeof(oterm));
+ oterm.c_lflag |= ECHO;
+ }
+
+ /*
+ * Catch signals that would otherwise cause the user to end
+ * up with echo turned off in the shell. Don't worry about
+ * things like SIGXCPU and SIGVTALRM for now.
+ */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0; /* don't restart system calls */
+ sa.sa_handler = handler;
+ (void)sigaction(SIGALRM, &sa, &savealrm);
+ (void)sigaction(SIGHUP, &sa, &savehup);
+ (void)sigaction(SIGINT, &sa, &saveint);
+ (void)sigaction(SIGPIPE, &sa, &savepipe);
+ (void)sigaction(SIGQUIT, &sa, &savequit);
+ (void)sigaction(SIGTERM, &sa, &saveterm);
+ (void)sigaction(SIGTSTP, &sa, &savetstp);
+ (void)sigaction(SIGTTIN, &sa, &savettin);
+ (void)sigaction(SIGTTOU, &sa, &savettou);
+
+ if (!(flags & RPP_STDIN))
+ (void)write(output, prompt, strlen(prompt));
+ end = buf + bufsiz - 1;
+ p = buf;
+ while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
+ if (p < end) {
+ if ((flags & RPP_SEVENBIT))
+ ch &= 0x7f;
+ if (isalpha((unsigned char)ch)) {
+ if ((flags & RPP_FORCELOWER))
+ ch = (char)tolower((unsigned char)ch);
+ if ((flags & RPP_FORCEUPPER))
+ ch = (char)toupper((unsigned char)ch);
+ }
+ *p++ = ch;
+ }
+ }
+ *p = '\0';
+ save_errno = errno;
+ if (!(term.c_lflag & ECHO))
+ (void)write(output, "\n", 1);
+
+ /* Restore old terminal settings and signals. */
+ if (memcmp(&term, &oterm, sizeof(term)) != 0) {
+ const int sigttou = signo[SIGTTOU];
+
+ /* Ignore SIGTTOU generated when we are not the fg pgrp. */
+ while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
+ errno == EINTR && !signo[SIGTTOU])
+ continue;
+ signo[SIGTTOU] = sigttou;
+ }
+ (void)sigaction(SIGALRM, &savealrm, NULL);
+ (void)sigaction(SIGHUP, &savehup, NULL);
+ (void)sigaction(SIGINT, &saveint, NULL);
+ (void)sigaction(SIGQUIT, &savequit, NULL);
+ (void)sigaction(SIGPIPE, &savepipe, NULL);
+ (void)sigaction(SIGTERM, &saveterm, NULL);
+ (void)sigaction(SIGTSTP, &savetstp, NULL);
+ (void)sigaction(SIGTTIN, &savettin, NULL);
+ (void)sigaction(SIGTTOU, &savettou, NULL);
+ if (input != STDIN_FILENO)
+ (void)close(input);
+
+ /*
+ * If we were interrupted by a signal, resend it to ourselves
+ * now that we have restored the signal handlers.
+ */
+ for (i = 0; i < NSIG; i++) {
+ if (signo[i]) {
+ kill(getpid(), i);
+ switch (i) {
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ need_restart = 1;
+ }
+ }
+ }
+ if (need_restart)
+ goto restart;
+
+ if (save_errno)
+ errno = save_errno;
+ return(nr == -1 ? NULL : buf);
+}
+
+#if 0
+char *
+getpass(const char *prompt)
+{
+ static char buf[_PASSWORD_LEN + 1];
+
+ return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
+}
+#endif
+
+static void handler(int s)
+{
+
+ signo[s] = 1;
+}
+#endif /* HAVE_READPASSPHRASE */
diff --git a/openbsd-compat/readpassphrase.h b/openbsd-compat/readpassphrase.h
new file mode 100644
index 000000000000..e4451f302ba5
--- /dev/null
+++ b/openbsd-compat/readpassphrase.h
@@ -0,0 +1,44 @@
+/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */
+
+/*
+ * Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/* OPENBSD ORIGINAL: include/readpassphrase.h */
+
+#ifndef _READPASSPHRASE_H_
+#define _READPASSPHRASE_H_
+
+#ifndef HAVE_READPASSPHRASE
+
+#include <stdlib.h>
+
+#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */
+#define RPP_ECHO_ON 0x01 /* Leave echo on. */
+#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */
+#define RPP_FORCELOWER 0x04 /* Force input to lower case. */
+#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */
+#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */
+#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */
+
+char * readpassphrase(const char *, char *, size_t, int);
+
+#endif /* HAVE_READPASSPHRASE */
+
+#endif /* !_READPASSPHRASE_H_ */
diff --git a/openbsd-compat/readpassphrase_win32.c b/openbsd-compat/readpassphrase_win32.c
new file mode 100644
index 000000000000..968987c563ab
--- /dev/null
+++ b/openbsd-compat/readpassphrase_win32.c
@@ -0,0 +1,131 @@
+/*
+* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
+*
+* Author: Bryan Berns <berns@uwalumni.com>
+* Modified group detection use s4u token information
+*
+* Copyright(c) 2016 Microsoft Corp.
+* All rights reserved
+*
+* Misc Unix POSIX routine implementations for Windows
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met :
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and / or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define UMDF_USING_NTSTATUS
+#define SECURITY_WIN32
+#include <windows.h>
+#include <stdio.h>
+#include <time.h>
+#include <shlwapi.h>
+#include <conio.h>
+#include <lm.h>
+#include <sddl.h>
+#include <aclapi.h>
+#include <ntsecapi.h>
+#include <security.h>
+#include <ntstatus.h>
+#include <wchar.h>
+
+#include "openbsd-compat.h"
+
+#ifndef HAVE_READPASSPHRASE
+
+/*on error returns NULL and sets errno*/
+static wchar_t *
+utf8_to_utf16(const char *utf8)
+{
+ int needed = 0;
+ wchar_t* utf16 = NULL;
+ if ((needed = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)) == 0 ||
+ (utf16 = malloc(needed * sizeof(wchar_t))) == NULL ||
+ MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, needed) == 0) {
+ /* debug3("failed to convert utf8 payload:%s error:%d", utf8, GetLastError()); */
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ return utf16;
+}
+
+char *
+readpassphrase(const char *prompt, char *outBuf, size_t outBufLen, int flags)
+{
+ size_t current_index = 0;
+ char ch;
+ wchar_t* wtmp = NULL;
+
+ if (outBufLen == 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ while (_kbhit()) (void)_getch();
+
+ wtmp = utf8_to_utf16(prompt);
+ if (wtmp == NULL)
+ errx(1, "unable to alloc memory");
+
+ _cputws(wtmp);
+ free(wtmp);
+
+ while (current_index < outBufLen - 1) {
+ ch = (char)_getch();
+
+ if (ch == '\r') {
+ if (_kbhit()) (void)_getch(); /* read linefeed if its there */
+ break;
+ } else if (ch == '\n') {
+ break;
+ } else if (ch == '\b') { /* backspace */
+ if (current_index > 0) {
+ if (flags & RPP_ECHO_ON)
+ printf_s("%c \b", ch);
+
+ current_index--; /* overwrite last character */
+ }
+ } else if (ch == '\003') { /* exit on Ctrl+C */
+ errx(1, "");
+ } else {
+ if (flags & RPP_SEVENBIT)
+ ch &= 0x7f;
+
+ if (isalpha((unsigned char)ch)) {
+ if(flags & RPP_FORCELOWER)
+ ch = (char)tolower((unsigned char)ch);
+ if(flags & RPP_FORCEUPPER)
+ ch = (char)toupper((unsigned char)ch);
+ }
+
+ outBuf[current_index++] = ch;
+ if(flags & RPP_ECHO_ON)
+ printf_s("%c", ch);
+ }
+ }
+
+ outBuf[current_index] = '\0';
+ _cputs("\n");
+
+ return outBuf;
+}
+
+#endif /* HAVE_READPASSPHRASE */
diff --git a/openbsd-compat/recallocarray.c b/openbsd-compat/recallocarray.c
new file mode 100644
index 000000000000..5d2f8d9885fd
--- /dev/null
+++ b/openbsd-compat/recallocarray.c
@@ -0,0 +1,91 @@
+/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */
+/*
+ * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/recallocarray.c */
+
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_RECALLOCARRAY)
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *
+recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
+{
+ size_t oldsize, newsize;
+ void *newptr;
+
+ if (ptr == NULL)
+ return calloc(newnmemb, size);
+
+ if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ newnmemb > 0 && SIZE_MAX / newnmemb < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ newsize = newnmemb * size;
+
+ if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
+ errno = EINVAL;
+ return NULL;
+ }
+ oldsize = oldnmemb * size;
+
+ /*
+ * Don't bother too much if we're shrinking just a bit,
+ * we do not shrink for series of small steps, oh well.
+ */
+ if (newsize <= oldsize) {
+ size_t d = oldsize - newsize;
+
+ if (d < oldsize / 2 && d < (size_t)getpagesize()) {
+ memset((char *)ptr + newsize, 0, d);
+ return ptr;
+ }
+ }
+
+ newptr = malloc(newsize);
+ if (newptr == NULL)
+ return NULL;
+
+ if (newsize > oldsize) {
+ memcpy(newptr, ptr, oldsize);
+ memset((char *)newptr + oldsize, 0, newsize - oldsize);
+ } else
+ memcpy(newptr, ptr, newsize);
+
+ explicit_bzero(ptr, oldsize);
+ free(ptr);
+
+ return newptr;
+}
+/* DEF_WEAK(recallocarray); */
+
+#endif /* !defined(HAVE_RECALLOCARRAY) */
diff --git a/openbsd-compat/strlcat.c b/openbsd-compat/strlcat.c
new file mode 100644
index 000000000000..44470debc972
--- /dev/null
+++ b/openbsd-compat/strlcat.c
@@ -0,0 +1,63 @@
+/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */
+
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_STRLCAT)
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
+
+#endif /* !defined(HAVE_STRLCAT) */
diff --git a/openbsd-compat/strlcpy.c b/openbsd-compat/strlcpy.c
new file mode 100644
index 000000000000..a8b18eaccf8f
--- /dev/null
+++ b/openbsd-compat/strlcpy.c
@@ -0,0 +1,59 @@
+/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */
+
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_STRLCPY)
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+
+#endif /* !defined(HAVE_STRLCPY) */
diff --git a/openbsd-compat/time.h b/openbsd-compat/time.h
new file mode 100644
index 000000000000..b125f73a7072
--- /dev/null
+++ b/openbsd-compat/time.h
@@ -0,0 +1,61 @@
+/*
+ * Public domain
+ * sys/time.h compatibility shim
+ */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+#include <../ucrt/time.h>
+#elif defined(_MSC_VER) && (_MSC_VER < 1900)
+#include <../include/time.h>
+#else
+#include <time.h>
+#endif
+
+#ifndef _COMPAT_TIME_H
+#define _COMPAT_TIME_H
+
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+typedef int clockid_t;
+int clock_gettime(clockid_t, struct timespec *);
+#endif
+
+#ifdef HAVE_TIMESPECSUB
+#include <sys/time.h>
+#endif
+
+#ifndef HAVE_TIMESPECSUB
+#define timespecadd(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec >= 1000000000L) { \
+ (vsp)->tv_sec++; \
+ (vsp)->tv_nsec -= 1000000000L; \
+ } \
+ } while (0)
+
+#define timespecsub(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec < 0) { \
+ (vsp)->tv_sec--; \
+ (vsp)->tv_nsec += 1000000000L; \
+ } \
+ } while (0)
+
+#define timespeccmp(tsp, usp, cmp) \
+ (((tsp)->tv_sec == (usp)->tv_sec) ? \
+ ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
+ ((tsp)->tv_sec cmp (usp)->tv_sec))
+#endif
+
+#endif /* _COMPAT_TIME_H */
diff --git a/openbsd-compat/timingsafe_bcmp.c b/openbsd-compat/timingsafe_bcmp.c
new file mode 100644
index 000000000000..3f7b9e541cec
--- /dev/null
+++ b/openbsd-compat/timingsafe_bcmp.c
@@ -0,0 +1,35 @@
+/* $OpenBSD: timingsafe_bcmp.c,v 1.1 2010/09/24 13:33:00 matthew Exp $ */
+/*
+ * Copyright (c) 2010 Damien Miller. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/string/timingsafe_bcmp.c */
+
+#include "openbsd-compat.h"
+
+#if !defined(HAVE_TIMINGSAFE_BCMP)
+
+int
+timingsafe_bcmp(const void *b1, const void *b2, size_t n)
+{
+ const unsigned char *p1 = b1, *p2 = b2;
+ int ret = 0;
+
+ for (; n > 0; n--)
+ ret |= *p1++ ^ *p2++;
+ return (ret != 0);
+}
+
+#endif /* !defined(HAVE_TIMINGSAFE_BCMP) */
diff --git a/openbsd-compat/types.h b/openbsd-compat/types.h
new file mode 100644
index 000000000000..617023078be3
--- /dev/null
+++ b/openbsd-compat/types.h
@@ -0,0 +1,69 @@
+/*
+ * Public domain
+ * sys/types.h compatibility shim
+ */
+
+#ifdef _MSC_VER
+#if _MSC_VER >= 1900
+#include <../ucrt/sys/types.h>
+#else
+#include <../include/sys/types.h>
+#endif
+#endif
+
+#ifndef _COMPAT_TYPES_H
+#define _COMPAT_TYPES_H
+
+#include <stdint.h>
+
+#ifdef __MINGW32__
+#include <_bsd_types.h>
+typedef uint32_t in_addr_t;
+typedef uint32_t uid_t;
+#endif
+
+#ifdef _MSC_VER
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+
+#include <basetsd.h>
+typedef SSIZE_T ssize_t;
+
+#ifndef SSIZE_MAX
+#ifdef _WIN64
+#define SSIZE_MAX _I64_MAX
+#else
+#define SSIZE_MAX INT_MAX
+#endif
+#endif
+
+#endif
+
+#if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__)
+# define __bounded__(x, y, z)
+#endif
+
+#ifdef _WIN32
+#define __warn_references(sym,msg)
+#else
+
+#ifndef __warn_references
+
+#ifndef __STRING
+#define __STRING(x) #x
+#endif
+
+#if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG)
+#define __warn_references(sym,msg) \
+ __asm__(".section .gnu.warning." __STRING(sym) \
+ "\n\t.ascii \"" msg "\"\n\t.text");
+#else
+#define __warn_references(sym,msg)
+#endif
+
+#endif /* __warn_references */
+#endif /* _WIN32 */
+
+#endif /* !_COMPAT_TYPES_H */
diff --git a/regress/CMakeLists.txt b/regress/CMakeLists.txt
new file mode 100644
index 000000000000..0314c38f7161
--- /dev/null
+++ b/regress/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright (c) 2018 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+add_custom_target(regress ALL)
+
+macro(add_regress_test NAME SOURCES)
+ add_executable(${NAME} ${SOURCES})
+ target_link_libraries(${NAME} fido2_shared)
+ add_custom_command(TARGET regress POST_BUILD COMMAND ${NAME}
+ DEPENDS ${NAME})
+endmacro()
+
+add_regress_test(regress_cred cred.c)
+add_regress_test(regress_assert assert.c)
+add_regress_test(regress_dev dev.c)
diff --git a/regress/assert.c b/regress/assert.c
new file mode 100644
index 000000000000..dfaf50662c76
--- /dev/null
+++ b/regress/assert.c
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <fido.h>
+#include <fido/es256.h>
+#include <fido/rs256.h>
+#include <fido/eddsa.h>
+#include <string.h>
+
+#define FAKE_DEV_HANDLE ((void *)0xdeadbeef)
+
+static const unsigned char es256_pk[64] = {
+ 0x34, 0xeb, 0x99, 0x77, 0x02, 0x9c, 0x36, 0x38,
+ 0xbb, 0xc2, 0xae, 0xa0, 0xa0, 0x18, 0xc6, 0x64,
+ 0xfc, 0xe8, 0x49, 0x92, 0xd7, 0x74, 0x9e, 0x0c,
+ 0x46, 0x8c, 0x9d, 0xa6, 0xdf, 0x46, 0xf7, 0x84,
+ 0x60, 0x1e, 0x0f, 0x8b, 0x23, 0x85, 0x4a, 0x9a,
+ 0xec, 0xc1, 0x08, 0x9f, 0x30, 0xd0, 0x0d, 0xd7,
+ 0x76, 0x7b, 0x55, 0x48, 0x91, 0x7c, 0x4f, 0x0f,
+ 0x64, 0x1a, 0x1d, 0xf8, 0xbe, 0x14, 0x90, 0x8a,
+};
+
+static const unsigned char cdh[32] = {
+ 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
+ 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
+ 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
+ 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
+};
+
+static const unsigned char authdata[39] = {
+ 0x58, 0x25, 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e,
+ 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76,
+ 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86,
+ 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d,
+ 0x97, 0x63, 0x00, 0x00, 0x00, 0x00, 0x03,
+};
+
+static const unsigned char sig[72] = {
+ 0x30, 0x46, 0x02, 0x21, 0x00, 0xf6, 0xd1, 0xa3,
+ 0xd5, 0x24, 0x2b, 0xde, 0xee, 0xa0, 0x90, 0x89,
+ 0xcd, 0xf8, 0x9e, 0xbd, 0x6b, 0x4d, 0x55, 0x79,
+ 0xe4, 0xc1, 0x42, 0x27, 0xb7, 0x9b, 0x9b, 0xa4,
+ 0x0a, 0xe2, 0x47, 0x64, 0x0e, 0x02, 0x21, 0x00,
+ 0xe5, 0xc9, 0xc2, 0x83, 0x47, 0x31, 0xc7, 0x26,
+ 0xe5, 0x25, 0xb2, 0xb4, 0x39, 0xa7, 0xfc, 0x3d,
+ 0x70, 0xbe, 0xe9, 0x81, 0x0d, 0x4a, 0x62, 0xa9,
+ 0xab, 0x4a, 0x91, 0xc0, 0x7d, 0x2d, 0x23, 0x1e,
+};
+
+static void *
+dummy_open(const char *path)
+{
+ (void)path;
+
+ return (FAKE_DEV_HANDLE);
+}
+
+static void
+dummy_close(void *handle)
+{
+ assert(handle == FAKE_DEV_HANDLE);
+}
+
+static int
+dummy_read(void *handle, unsigned char *buf, size_t len, int ms)
+{
+ (void)handle;
+ (void)buf;
+ (void)len;
+ (void)ms;
+
+ abort();
+ /* NOTREACHED */
+}
+
+static int
+dummy_write(void *handle, const unsigned char *buf, size_t len)
+{
+ (void)handle;
+ (void)buf;
+ (void)len;
+
+ abort();
+ /* NOTREACHED */
+}
+
+static fido_assert_t *
+alloc_assert(void)
+{
+ fido_assert_t *a;
+
+ a = fido_assert_new();
+ assert(a != NULL);
+
+ return (a);
+}
+
+static void
+free_assert(fido_assert_t *a)
+{
+ fido_assert_free(&a);
+ assert(a == NULL);
+}
+
+static fido_dev_t *
+alloc_dev(void)
+{
+ fido_dev_t *d;
+
+ d = fido_dev_new();
+ assert(d != NULL);
+
+ return (d);
+}
+
+static void
+free_dev(fido_dev_t *d)
+{
+ fido_dev_free(&d);
+ assert(d == NULL);
+}
+
+static es256_pk_t *
+alloc_es256_pk(void)
+{
+ es256_pk_t *pk;
+
+ pk = es256_pk_new();
+ assert(pk != NULL);
+
+ return (pk);
+}
+
+static void
+free_es256_pk(es256_pk_t *pk)
+{
+ es256_pk_free(&pk);
+ assert(pk == NULL);
+}
+
+static rs256_pk_t *
+alloc_rs256_pk(void)
+{
+ rs256_pk_t *pk;
+
+ pk = rs256_pk_new();
+ assert(pk != NULL);
+
+ return (pk);
+}
+
+static void
+free_rs256_pk(rs256_pk_t *pk)
+{
+ rs256_pk_free(&pk);
+ assert(pk == NULL);
+}
+
+static eddsa_pk_t *
+alloc_eddsa_pk(void)
+{
+ eddsa_pk_t *pk;
+
+ pk = eddsa_pk_new();
+ assert(pk != NULL);
+
+ return (pk);
+}
+
+static void
+free_eddsa_pk(eddsa_pk_t *pk)
+{
+ eddsa_pk_free(&pk);
+ assert(pk == NULL);
+}
+
+static void
+empty_assert(fido_dev_t *d, fido_assert_t *a, size_t idx)
+{
+ es256_pk_t *es256;
+ rs256_pk_t *rs256;
+ eddsa_pk_t *eddsa;
+
+ assert(fido_assert_flags(a, idx) == 0);
+ assert(fido_assert_authdata_len(a, idx) == 0);
+ assert(fido_assert_authdata_ptr(a, idx) == NULL);
+ assert(fido_assert_clientdata_hash_len(a) == 0);
+ assert(fido_assert_clientdata_hash_ptr(a) == NULL);
+ assert(fido_assert_id_len(a, idx) == 0);
+ assert(fido_assert_id_ptr(a, idx) == NULL);
+ assert(fido_assert_rp_id(a) == NULL);
+ assert(fido_assert_sig_len(a, idx) == 0);
+ assert(fido_assert_sig_ptr(a, idx) == NULL);
+ assert(fido_assert_user_display_name(a, idx) == NULL);
+ assert(fido_assert_user_icon(a, idx) == NULL);
+ assert(fido_assert_user_id_len(a, idx) == 0);
+ assert(fido_assert_user_id_ptr(a, idx) == NULL);
+ assert(fido_assert_user_name(a, idx) == NULL);
+
+ es256 = alloc_es256_pk();
+ rs256 = alloc_rs256_pk();
+ eddsa = alloc_eddsa_pk();
+
+ fido_dev_force_u2f(d);
+ assert(fido_dev_get_assert(d, a, NULL) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_dev_get_assert(d, a, "") == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, COSE_ES256,
+ NULL) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, COSE_ES256,
+ es256) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, -1,
+ es256) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, COSE_RS256,
+ rs256) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, COSE_EDDSA,
+ eddsa) == FIDO_ERR_INVALID_ARGUMENT);
+
+ fido_dev_force_fido2(d);
+ assert(fido_dev_get_assert(d, a, NULL) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_dev_get_assert(d, a, "") == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, COSE_ES256,
+ NULL) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, COSE_ES256,
+ es256) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, -1,
+ es256) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, COSE_RS256,
+ rs256) == FIDO_ERR_INVALID_ARGUMENT);
+ assert(fido_assert_verify(a, idx, COSE_EDDSA,
+ eddsa) == FIDO_ERR_INVALID_ARGUMENT);
+
+ free_es256_pk(es256);
+ free_rs256_pk(rs256);
+ free_eddsa_pk(eddsa);
+}
+
+static void
+empty_assert_tests(void)
+{
+ fido_assert_t *a;
+ fido_dev_t *d;
+ fido_dev_io_t io_f;
+ size_t i;
+
+ memset(&io_f, 0, sizeof(io_f));
+
+ a = alloc_assert();
+ d = alloc_dev();
+
+ io_f.open = dummy_open;
+ io_f.close = dummy_close;
+ io_f.read = dummy_read;
+ io_f.write = dummy_write;
+
+ assert(fido_dev_set_io_functions(d, &io_f) == FIDO_OK);
+
+ empty_assert(d, a, 0);
+ assert(fido_assert_count(a) == 0);
+ assert(fido_assert_set_count(a, 4) == FIDO_OK);
+ assert(fido_assert_count(a) == 4);
+ for (i = 0; i < 4; i++) {
+ empty_assert(d, a, i);
+ }
+ empty_a