From c79050aa44b8836d836c5dd22a383a073c28b74b Mon Sep 17 00:00:00 2001 From: nicoo Date: Wed, 12 Feb 2020 13:42:22 +0100 Subject: Import upstream release 1.3.0 Closes: #951184 --- CMakeLists.txt | 383 +++++++++ LICENSE | 24 + NEWS | 73 ++ README.adoc | 88 ++ docker/bionic/Dockerfile | 14 + examples/CMakeLists.txt | 44 + examples/README.adoc | 81 ++ examples/assert.c | 329 +++++++ examples/cred.c | 303 +++++++ examples/extern.h | 32 + examples/info.c | 216 +++++ examples/manifest.c | 45 + examples/reset.c | 64 ++ examples/retries.c | 52 ++ examples/setpin.c | 59 ++ examples/util.c | 415 +++++++++ fuzz/CMakeLists.txt | 44 + fuzz/README | 157 ++++ fuzz/corpus.tgz | Bin 0 -> 1131788 bytes fuzz/functions.txt | 564 ++++++++++++ fuzz/fuzz_assert.c | 664 ++++++++++++++ fuzz/fuzz_bio.c | 755 ++++++++++++++++ fuzz/fuzz_cred.c | 925 ++++++++++++++++++++ fuzz/fuzz_credman.c | 667 +++++++++++++++ fuzz/fuzz_mgmt.c | 529 ++++++++++++ fuzz/harnesses/assert | 32 + fuzz/harnesses/assert-rsa-h-p | 33 + fuzz/harnesses/assert-u2f | 32 + fuzz/harnesses/cred | 31 + fuzz/harnesses/cred-rsa-h-p | 32 + fuzz/harnesses/cred-u2f | 31 + fuzz/harnesses/cred-u2f-exclude | 33 + fuzz/harnesses/fido2-assert-G | 31 + fuzz/harnesses/fido2-assert-V | 32 + fuzz/harnesses/fido2-cred-M | 31 + fuzz/harnesses/fido2-cred-V | 31 + fuzz/harnesses/fuzz_assert | 29 + fuzz/harnesses/fuzz_bio | 29 + fuzz/harnesses/fuzz_cred | 28 + fuzz/harnesses/fuzz_credman | 28 + fuzz/harnesses/fuzz_mgmt | 29 + fuzz/mutator_aux.c | 314 +++++++ fuzz/mutator_aux.h | 65 ++ fuzz/preload-fuzz.c | 104 +++ fuzz/preload-snoop.c | 217 +++++ fuzz/report | 80 ++ fuzz/summary.txt | 39 + fuzz/uniform_random.c | 56 ++ fuzz/wrap.c | 419 +++++++++ fuzz/wrapped.sym | 47 + man/CMakeLists.txt | 314 +++++++ man/NOTES | 4 + man/dyc.css | 14 + man/eddsa_pk_new.3 | 122 +++ man/es256_pk_new.3 | 122 +++ man/fido2-assert.1 | 220 +++++ man/fido2-cred.1 | 238 ++++++ man/fido2-token.1 | 158 ++++ man/fido_assert_allow_cred.3 | 47 + man/fido_assert_new.3 | 190 +++++ man/fido_assert_set_authdata.3 | 194 +++++ man/fido_assert_verify.3 | 79 ++ man/fido_bio_dev_get_info.3 | 120 +++ man/fido_bio_enroll_new.3 | 95 +++ man/fido_bio_info_new.3 | 81 ++ man/fido_bio_template.3 | 169 ++++ man/fido_cbor_info_new.3 | 153 ++++ man/fido_cred_exclude.3 | 60 ++ man/fido_cred_new.3 | 157 ++++ man/fido_cred_set_authdata.3 | 240 ++++++ man/fido_cred_verify.3 | 64 ++ man/fido_credman_metadata_new.3 | 299 +++++++ man/fido_dev_get_assert.3 | 76 ++ man/fido_dev_info_manifest.3 | 143 ++++ man/fido_dev_make_cred.3 | 77 ++ man/fido_dev_open.3 | 159 ++++ man/fido_dev_set_io_functions.3 | 95 +++ man/fido_dev_set_pin.3 | 88 ++ man/fido_init.3 | 40 + man/fido_strerr.3 | 27 + man/rs256_pk_new.3 | 122 +++ man/style.css | 24 + openbsd-compat/bsd-getline.c | 115 +++ openbsd-compat/bsd-getpagesize.c | 27 + openbsd-compat/diff.sh | 24 + openbsd-compat/err.h | 85 ++ openbsd-compat/explicit_bzero.c | 57 ++ openbsd-compat/explicit_bzero_win32.c | 19 + openbsd-compat/getopt.h | 74 ++ openbsd-compat/getopt_long.c | 523 ++++++++++++ openbsd-compat/openbsd-compat.h | 87 ++ openbsd-compat/posix_win.c | 61 ++ openbsd-compat/posix_win.h | 47 + openbsd-compat/readpassphrase.c | 214 +++++ openbsd-compat/readpassphrase.h | 42 + openbsd-compat/readpassphrase_win32.c | 131 +++ openbsd-compat/recallocarray.c | 91 ++ openbsd-compat/strlcat.c | 63 ++ openbsd-compat/strlcpy.c | 59 ++ openbsd-compat/timingsafe_bcmp.c | 35 + openbsd-compat/types.h | 71 ++ regress/CMakeLists.txt | 18 + regress/assert.c | 511 +++++++++++ regress/cred.c | 816 ++++++++++++++++++ regress/dev.c | 77 ++ src/CMakeLists.txt | 104 +++ src/aes256.c | 98 +++ src/assert.c | 1090 +++++++++++++++++++++++ src/authkey.c | 98 +++ src/bio.c | 844 ++++++++++++++++++ src/blob.c | 102 +++ src/blob.h | 28 + src/buf.c | 34 + src/cbor.c | 1520 +++++++++++++++++++++++++++++++++ src/cred.c | 1034 ++++++++++++++++++++++ src/credman.c | 736 ++++++++++++++++ src/dev.c | 284 ++++++ src/diff_exports.sh | 23 + src/ecdh.c | 121 +++ src/eddsa.c | 169 ++++ src/err.c | 122 +++ src/es256.c | 434 ++++++++++ src/export.gnu | 183 ++++ src/export.llvm | 178 ++++ src/export.msvc | 179 ++++ src/extern.h | 132 +++ src/fido.h | 194 +++++ src/fido/bio.h | 95 +++ src/fido/credman.h | 74 ++ src/fido/eddsa.h | 40 + src/fido/err.h | 69 ++ src/fido/es256.h | 34 + src/fido/param.h | 84 ++ src/fido/rs256.h | 22 + src/hid.c | 70 ++ src/hid_linux.c | 344 ++++++++ src/hid_openbsd.c | 277 ++++++ src/hid_osx.c | 410 +++++++++ src/hid_win.c | 324 +++++++ src/info.c | 410 +++++++++ src/io.c | 256 ++++++ src/iso7816.c | 70 ++ src/iso7816.h | 38 + src/libfido2.pc.in | 12 + src/log.c | 63 ++ src/packed.h | 22 + src/pin.c | 428 ++++++++++ src/reset.c | 40 + src/rs256.c | 204 +++++ src/types.h | 171 ++++ src/u2f.c | 758 ++++++++++++++++ tools/CMakeLists.txt | 65 ++ tools/assert_get.c | 233 +++++ tools/assert_verify.c | 201 +++++ tools/base64.c | 135 +++ tools/bio.c | 270 ++++++ tools/cred_make.c | 221 +++++ tools/cred_verify.c | 177 ++++ tools/credman.c | 237 +++++ tools/extern.h | 64 ++ tools/fido2-assert.c | 54 ++ tools/fido2-cred.c | 52 ++ tools/fido2-token.c | 95 +++ tools/pin.c | 146 ++++ tools/sk-libfido2.c | 784 +++++++++++++++++ tools/test.sh | 96 +++ tools/token.c | 364 ++++++++ tools/util.c | 364 ++++++++ udev/70-u2f.rules | 72 ++ udev/CMakeLists.txt | 7 + windows/build.ps1 | 204 +++++ windows/libressl.gpg | Bin 0 -> 16425 bytes 172 files changed, 31461 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 NEWS create mode 100644 README.adoc create mode 100644 docker/bionic/Dockerfile create mode 100644 examples/CMakeLists.txt create mode 100644 examples/README.adoc create mode 100644 examples/assert.c create mode 100644 examples/cred.c create mode 100644 examples/extern.h create mode 100644 examples/info.c create mode 100644 examples/manifest.c create mode 100644 examples/reset.c create mode 100644 examples/retries.c create mode 100644 examples/setpin.c create mode 100644 examples/util.c create mode 100644 fuzz/CMakeLists.txt create mode 100644 fuzz/README create mode 100644 fuzz/corpus.tgz create mode 100644 fuzz/functions.txt create mode 100644 fuzz/fuzz_assert.c create mode 100644 fuzz/fuzz_bio.c create mode 100644 fuzz/fuzz_cred.c create mode 100644 fuzz/fuzz_credman.c create mode 100644 fuzz/fuzz_mgmt.c create mode 100755 fuzz/harnesses/assert create mode 100755 fuzz/harnesses/assert-rsa-h-p create mode 100755 fuzz/harnesses/assert-u2f create mode 100755 fuzz/harnesses/cred create mode 100755 fuzz/harnesses/cred-rsa-h-p create mode 100755 fuzz/harnesses/cred-u2f create mode 100755 fuzz/harnesses/cred-u2f-exclude create mode 100755 fuzz/harnesses/fido2-assert-G create mode 100755 fuzz/harnesses/fido2-assert-V create mode 100755 fuzz/harnesses/fido2-cred-M create mode 100755 fuzz/harnesses/fido2-cred-V create mode 100755 fuzz/harnesses/fuzz_assert create mode 100755 fuzz/harnesses/fuzz_bio create mode 100755 fuzz/harnesses/fuzz_cred create mode 100755 fuzz/harnesses/fuzz_credman create mode 100755 fuzz/harnesses/fuzz_mgmt create mode 100644 fuzz/mutator_aux.c create mode 100644 fuzz/mutator_aux.h create mode 100644 fuzz/preload-fuzz.c create mode 100644 fuzz/preload-snoop.c create mode 100755 fuzz/report create mode 100644 fuzz/summary.txt create mode 100644 fuzz/uniform_random.c create mode 100644 fuzz/wrap.c create mode 100644 fuzz/wrapped.sym create mode 100644 man/CMakeLists.txt create mode 100644 man/NOTES create mode 100644 man/dyc.css create mode 100644 man/eddsa_pk_new.3 create mode 100644 man/es256_pk_new.3 create mode 100644 man/fido2-assert.1 create mode 100644 man/fido2-cred.1 create mode 100644 man/fido2-token.1 create mode 100644 man/fido_assert_allow_cred.3 create mode 100644 man/fido_assert_new.3 create mode 100644 man/fido_assert_set_authdata.3 create mode 100644 man/fido_assert_verify.3 create mode 100644 man/fido_bio_dev_get_info.3 create mode 100644 man/fido_bio_enroll_new.3 create mode 100644 man/fido_bio_info_new.3 create mode 100644 man/fido_bio_template.3 create mode 100644 man/fido_cbor_info_new.3 create mode 100644 man/fido_cred_exclude.3 create mode 100644 man/fido_cred_new.3 create mode 100644 man/fido_cred_set_authdata.3 create mode 100644 man/fido_cred_verify.3 create mode 100644 man/fido_credman_metadata_new.3 create mode 100644 man/fido_dev_get_assert.3 create mode 100644 man/fido_dev_info_manifest.3 create mode 100644 man/fido_dev_make_cred.3 create mode 100644 man/fido_dev_open.3 create mode 100644 man/fido_dev_set_io_functions.3 create mode 100644 man/fido_dev_set_pin.3 create mode 100644 man/fido_init.3 create mode 100644 man/fido_strerr.3 create mode 100644 man/rs256_pk_new.3 create mode 100644 man/style.css create mode 100644 openbsd-compat/bsd-getline.c create mode 100644 openbsd-compat/bsd-getpagesize.c create mode 100755 openbsd-compat/diff.sh create mode 100644 openbsd-compat/err.h create mode 100644 openbsd-compat/explicit_bzero.c create mode 100644 openbsd-compat/explicit_bzero_win32.c create mode 100644 openbsd-compat/getopt.h create mode 100644 openbsd-compat/getopt_long.c create mode 100644 openbsd-compat/openbsd-compat.h create mode 100644 openbsd-compat/posix_win.c create mode 100644 openbsd-compat/posix_win.h create mode 100644 openbsd-compat/readpassphrase.c create mode 100644 openbsd-compat/readpassphrase.h create mode 100644 openbsd-compat/readpassphrase_win32.c create mode 100644 openbsd-compat/recallocarray.c create mode 100644 openbsd-compat/strlcat.c create mode 100644 openbsd-compat/strlcpy.c create mode 100644 openbsd-compat/timingsafe_bcmp.c create mode 100644 openbsd-compat/types.h create mode 100644 regress/CMakeLists.txt create mode 100644 regress/assert.c create mode 100644 regress/cred.c create mode 100644 regress/dev.c create mode 100644 src/CMakeLists.txt create mode 100644 src/aes256.c create mode 100644 src/assert.c create mode 100644 src/authkey.c create mode 100644 src/bio.c create mode 100644 src/blob.c create mode 100644 src/blob.h create mode 100644 src/buf.c create mode 100644 src/cbor.c create mode 100644 src/cred.c create mode 100644 src/credman.c create mode 100644 src/dev.c create mode 100755 src/diff_exports.sh create mode 100644 src/ecdh.c create mode 100644 src/eddsa.c create mode 100644 src/err.c create mode 100644 src/es256.c create mode 100644 src/export.gnu create mode 100644 src/export.llvm create mode 100644 src/export.msvc create mode 100644 src/extern.h create mode 100644 src/fido.h create mode 100644 src/fido/bio.h create mode 100644 src/fido/credman.h create mode 100644 src/fido/eddsa.h create mode 100644 src/fido/err.h create mode 100644 src/fido/es256.h create mode 100644 src/fido/param.h create mode 100644 src/fido/rs256.h create mode 100644 src/hid.c create mode 100644 src/hid_linux.c create mode 100644 src/hid_openbsd.c create mode 100644 src/hid_osx.c create mode 100644 src/hid_win.c create mode 100644 src/info.c create mode 100644 src/io.c create mode 100644 src/iso7816.c create mode 100644 src/iso7816.h create mode 100644 src/libfido2.pc.in create mode 100644 src/log.c create mode 100644 src/packed.h create mode 100644 src/pin.c create mode 100644 src/reset.c create mode 100644 src/rs256.c create mode 100644 src/types.h create mode 100644 src/u2f.c create mode 100644 tools/CMakeLists.txt create mode 100644 tools/assert_get.c create mode 100644 tools/assert_verify.c create mode 100644 tools/base64.c create mode 100644 tools/bio.c create mode 100644 tools/cred_make.c create mode 100644 tools/cred_verify.c create mode 100644 tools/credman.c create mode 100644 tools/extern.h create mode 100644 tools/fido2-assert.c create mode 100644 tools/fido2-cred.c create mode 100644 tools/fido2-token.c create mode 100644 tools/pin.c create mode 100644 tools/sk-libfido2.c create mode 100755 tools/test.sh create mode 100644 tools/token.c create mode 100644 tools/util.c create mode 100644 udev/70-u2f.rules create mode 100644 udev/CMakeLists.txt create mode 100644 windows/build.ps1 create mode 100644 windows/libressl.gpg diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c7c5991 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,383 @@ +# 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) + +include(CheckCCompilerFlag) +include(CheckFunctionExists) +include(CheckIncludeFiles) +include(CheckTypeSize) +include(GNUInstallDirs) + +set(CMAKE_COLOR_MAKEFILE off) +set(CMAKE_VERBOSE_MAKEFILE on) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +set(FIDO_MAJOR "1") +set(FIDO_MINOR "3") +set(FIDO_PATCH "0") +set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH}) + +add_definitions(-D_FIDO_MAJOR=${FIDO_MAJOR}) +add_definitions(-D_FIDO_MINOR=${FIDO_MINOR}) +add_definitions(-D_FIDO_PATCH=${FIDO_PATCH}) + +if(WIN32) + add_definitions(-DWIN32_LEAN_AND_MEAN) +endif() + +if(APPLE) + set(CMAKE_INSTALL_NAME_DIR + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") +endif() + +# /dev/urandom +if(UNIX) + add_definitions(-DHAS_DEV_URANDOM) +endif() + +# Observe OpenBSD's library versioning scheme. +if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + set(LIB_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}) + set(LIB_SOVERSION ${LIB_VERSION}) +else() + set(LIB_VERSION ${FIDO_VERSION}) + set(LIB_SOVERSION ${FIDO_MAJOR}) +endif() + +if(MSVC) + if((NOT CBOR_INCLUDE_DIRS) OR (NOT CBOR_LIBRARY_DIRS) OR + (NOT CRYPTO_INCLUDE_DIRS) OR (NOT CRYPTO_LIBRARY_DIRS)) + message(FATAL_ERROR "please provide definitions for " + "{CBOR,CRYPTO}_{INCLUDE,LIBRARY}_DIRS when building " + "under msvc") + endif() + set(CBOR_LIBRARIES cbor) + set(CRYPTO_LIBRARIES crypto-45) + 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 + ) + # 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 ${MSVC_DISABLED_WARNINGS_STR}") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Z7") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi") +else() + include(FindPkgConfig) + pkg_search_module(CBOR libcbor) + pkg_search_module(CRYPTO libcrypto REQUIRED) + + # XXX workaround libcbor's missing .pc file + if(NOT CBOR_FOUND) + check_include_files(cbor.h HAVE_CBOR_H) + if(NOT HAVE_CBOR_H) + message(FATAL_ERROR "could not find cbor header files") + endif() + set(CBOR_LIBRARIES "cbor") + endif() + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + pkg_search_module(UDEV libudev REQUIRED) + set(UDEV_NAME "udev") + # Define be32toh(). + add_definitions(-D_GNU_SOURCE) + elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + set(BASE_LIBRARIES usbhid) + endif() + + if(MINGW) + # MinGW is stuck with a flavour of C89. + add_definitions(-DFIDO_NO_DIAGNOSTIC) + add_definitions(-DWC_ERR_INVALID_CHARS=0x80) + endif() + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wwrite-strings") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-prototypes") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wbad-function-cast") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-all") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") + + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g2") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer") + + if(FUZZ) + if(LIBFUZZER) + set(FUZZ_LDFLAGS "-fsanitize=fuzzer") + endif() + add_definitions(-DFIDO_FUZZ) + endif() + + if(ASAN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,leak") + endif() + + if(MSAN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-track-origins") + endif() + + if(UBSAN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-trap=undefined") + endif() + + if(COVERAGE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-instr-generate -fcoverage-mapping") + endif() +endif() + +# Use -Wshorten-64-to-32 if available. +check_c_compiler_flag("-Wshorten-64-to-32" HAVE_SHORTEN_64_TO_32) +if(HAVE_SHORTEN_64_TO_32) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshorten-64-to-32") +endif() + +# Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 +if(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -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}) + +# endian.h +check_include_files(endian.h HAVE_ENDIAN_H) +if(HAVE_ENDIAN_H) + add_definitions(-DHAVE_ENDIAN_H) +endif() + +# err.h +check_include_files(err.h HAVE_ERR_H) +if(HAVE_ERR_H) + add_definitions(-DHAVE_ERR_H) +endif() + +# unistd.h +check_include_files(unistd.h HAVE_UNISTD_H) +if(HAVE_UNISTD_H) + add_definitions(-DHAVE_UNISTD_H) +endif() + +# signal.h +check_include_files(signal.h HAVE_SIGNAL_H) +if(HAVE_SIGNAL_H) + add_definitions(-DHAVE_SIGNAL_H) +endif() + +# strlcpy +check_function_exists(strlcpy HAVE_STRLCPY) +if(HAVE_STRLCPY) + add_definitions(-DHAVE_STRLCPY) +endif() + +# strlcat +check_function_exists(strlcpy HAVE_STRLCAT) +if(HAVE_STRLCAT) + add_definitions(-DHAVE_STRLCAT) +endif() + +# recallocarray +check_function_exists(recallocarray HAVE_RECALLOCARRAY) +if(HAVE_RECALLOCARRAY) + add_definitions(-DHAVE_RECALLOCARRAY) +endif() + +# XXX getpagesize is incorrectly detected when cross-compiling +# with mingw on Linux. Avoid. +if(NOT WIN32) + check_function_exists(getpagesize HAVE_GETPAGESIZE) +endif() +if(HAVE_GETPAGESIZE) + add_definitions(-DHAVE_GETPAGESIZE) +endif() + +# sysconf +check_function_exists(sysconf HAVE_SYSCONF) +if(HAVE_SYSCONF) + add_definitions(-DHAVE_SYSCONF) +endif() + +# memset_s +if(APPLE) + add_definitions(-D__STDC_WANT_LIB_EXT1__=1) +endif() +check_function_exists(memset_s HAVE_MEMSET_S) +if(HAVE_MEMSET_S) + add_definitions(-DHAVE_MEMSET_S) +endif() + +# explicit_bzero +if(NOT LIBFUZZER) + check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) + if(HAVE_EXPLICIT_BZERO) + add_definitions(-DHAVE_EXPLICIT_BZERO) + endif() +endif() + +# timingsafe_bcmp +check_function_exists(timingsafe_bcmp HAVE_TIMINGSAFE_BCMP) +if(HAVE_TIMINGSAFE_BCMP) + add_definitions(-DHAVE_TIMINGSAFE_BCMP) +endif() + +# readpassphrase +check_function_exists(readpassphrase HAVE_READPASSPHRASE) +if(HAVE_READPASSPHRASE) + add_definitions(-DHAVE_READPASSPHRASE) +endif() + +# getline +check_function_exists(getline HAVE_GETLINE) +if(HAVE_GETLINE) + add_definitions(-DHAVE_GETLINE) +endif() + +# getopt +check_function_exists(getopt HAVE_GETOPT) +if(HAVE_GETOPT) + add_definitions(-DHAVE_GETOPT) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual") +else() + if(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-discarded-qualifiers") + endif() + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types-discards-qualifiers") + endif() +endif() + +# usable sigaction +set(CMAKE_EXTRA_INCLUDE_FILES signal.h) +check_function_exists(sigaction HAVE_SIGACTION) +check_type_size("sig_atomic_t" HAVE_SIG_ATOMIC_T) +if(HAVE_SIGACTION AND (NOT HAVE_SIG_ATOMIC_T STREQUAL "")) + add_definitions(-DSIGNAL_EXAMPLE) +endif() +set(CMAKE_EXTRA_INCLUDE_FILES) + +# arc4random_buf +check_function_exists(arc4random_buf HAVE_ARC4RANDOM_BUF) +if(HAVE_ARC4RANDOM_BUF) + add_definitions(-DHAVE_ARC4RANDOM_BUF) +endif() + +# getentropy +check_function_exists(getentropy HAVE_GETENTROPY) +if(HAVE_GETENTROPY) + add_definitions(-DHAVE_GETENTROPY) +endif() + +# export list +if(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 + string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} + " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/export.gnu") + 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}) + +link_directories(${CBOR_LIBRARY_DIRS}) +link_directories(${CRYPTO_LIBRARY_DIRS}) + +message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}") +message(STATUS "CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}") +message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}") +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") +message(STATUS "CBOR_INCLUDE_DIRS: ${CBOR_INCLUDE_DIRS}") +message(STATUS "CBOR_LIBRARY_DIRS: ${CBOR_LIBRARY_DIRS}") +message(STATUS "CBOR_LIBRARIES: ${CBOR_LIBRARIES}") +message(STATUS "CRYPTO_INCLUDE_DIRS: ${CRYPTO_INCLUDE_DIRS}") +message(STATUS "CRYPTO_LIBRARY_DIRS: ${CRYPTO_LIBRARY_DIRS}") +message(STATUS "CRYPTO_LIBRARIES: ${CRYPTO_LIBRARIES}") +message(STATUS "BASE_LIBRARIES: ${BASE_LIBRARIES}") +message(STATUS "VERSION: ${FIDO_VERSION}") +message(STATUS "LIB_VERSION: ${LIB_VERSION}") +message(STATUS "LIB_SOVERSION: ${LIB_SOVERSION}") +message(STATUS "FUZZ: ${FUZZ}") +message(STATUS "AFL: ${AFL}") +message(STATUS "LIBFUZZER: ${LIBFUZZER}") +message(STATUS "ASAN: ${ASAN}") +message(STATUS "MSAN: ${MSAN}") +message(STATUS "COVERAGE: ${COVERAGE}") +message(STATUS "TLS: ${TLS}") + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(STATUS "UDEV_INCLUDE_DIRS: ${UDEV_INCLUDE_DIRS}") + message(STATUS "UDEV_LIBRARY_DIRS: ${UDEV_LIBRARY_DIRS}") + message(STATUS "UDEV_LIBRARIES: ${UDEV_LIBRARIES}") + message(STATUS "UDEV_RULES_DIR: ${UDEV_RULES_DIR}") +endif() + +subdirs(src) +subdirs(examples) +subdirs(tools) +subdirs(man) + +if(NOT WIN32) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(NOT MSAN AND NOT LIBFUZZER) + 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 0000000..340cc35 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2018 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 0000000..1b78c7c --- /dev/null +++ b/NEWS @@ -0,0 +1,73 @@ +* 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 0000000..8693417 --- /dev/null +++ b/README.adoc @@ -0,0 +1,88 @@ +== libfido2 + +image:https://api.travis-ci.org/Yubico/libfido2.svg?branch=master["Build Status (Travis)", link="https://travis-ci.org/Yubico/libfido2"] +image:https://github.com/yubico/libfido2/workflows/windows/badge.svg["windows build status (github actions)", link="https://github.com/Yubico/libfido2/actions"] +image:https://img.shields.io/badge/license-BSD-blue.svg["License", link="https://raw.githubusercontent.com/Yubico/libfido2/master/LICENSE"] + +*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, and OpenBSD. + +=== 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. + +=== Installation + +==== Releases + +The current release of *libfido2* is 1.3.0. Please consult Yubico's +https://developers.yubico.com/libfido2/Releases[release page] for source +and binary releases. + +==== Ubuntu + + $ sudo apt-add-repository ppa:yubico/stable + $ sudo apt update + $ sudo apt install libfido2-dev + +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, the PKG_CONFIG_PATH environment variable may need to +be set. + +*libfido2* depends on https://github.com/pjk/libcbor[libcbor] and +https://github.com/libressl-portable/portable[LibreSSL] (alternatively, +https://www.openssl.org[OpenSSL] may be used). 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 +`.travis/` (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" +---- + +On Windows 1903 and newer versions, access to FIDO devices has been restricted +to applications using the operating system's native API. Use of *libfido2* +is still possible in privileged applications. + +=== OpenSSH Integration + +*libfido2* includes middleware allowing https://www.openssh.com[OpenSSH] to +talk to U2F/FIDO2 devices. Note that server support is required for +authentication. In a nutshell: + +==== Key Generation + + $ ssh-keygen -t [ecdsa-sk|ed25519-sk] -w /path/to/libsk-libfido2.so + +==== Authentication + + $ ssh-agent -P /path/to/libsk-libfido2.so + $ ssh-add -S /path/to/libsk-libfido2.so diff --git a/docker/bionic/Dockerfile b/docker/bionic/Dockerfile new file mode 100644 index 0000000..9225ed8 --- /dev/null +++ b/docker/bionic/Dockerfile @@ -0,0 +1,14 @@ +# unlock-yk +# docker run --rm --volume=/home/pedro/projects/libfido2:/workdir \ +# --volume=$(gpgconf --list-dirs socketdir):/root/.gnupg \ +# --volume=$(gpgconf --list-dirs homedir)/pubring.kbx:/root/.gnupg/pubring.kbx \ +# -it libfido2-staging --install-deps --ppa martelletto/ppa \ +# --key pedro@yubico.com +FROM ubuntu:bionic +ENV DEBIAN_FRONTEND noninteractive +RUN apt-get -qq update && apt-get -qq upgrade +RUN apt-get install -qq packaging-dev debian-keyring devscripts equivs gnupg python sudo +ADD https://raw.githubusercontent.com/dainnilsson/scripts/master/make-ppa /make-ppa +RUN chmod +x /make-ppa +WORKDIR /workdir +ENTRYPOINT ["/make-ppa"] diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..957311e --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,44 @@ +# 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/getopt_long.c + ../openbsd-compat/strlcat.c + ../openbsd-compat/strlcpy.c +) + +if(WIN32) + list(APPEND COMPAT_SOURCES ../openbsd-compat/posix_win.c) +endif() + +# drop -rdynamic +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + +# manifest +add_executable(manifest manifest.c ${COMPAT_SOURCES}) +target_link_libraries(manifest fido2) + +# info +add_executable(info info.c ${COMPAT_SOURCES}) +target_link_libraries(info fido2) + +# reset +add_executable(reset reset.c util.c ${COMPAT_SOURCES}) +target_link_libraries(reset fido2) + +# cred +add_executable(cred cred.c util.c ${COMPAT_SOURCES}) +target_link_libraries(cred fido2) + +# assert +add_executable(assert assert.c util.c ${COMPAT_SOURCES}) +target_link_libraries(assert fido2) + +# setpin +add_executable(setpin setpin.c ${COMPAT_SOURCES}) +target_link_libraries(setpin fido2) + +# retries +add_executable(retries retries.c ${COMPAT_SOURCES}) +target_link_libraries(retries fido2) diff --git a/examples/README.adoc b/examples/README.adoc new file mode 100644 index 0000000..091c6bc --- /dev/null +++ b/examples/README.adoc @@ -0,0 +1,81 @@ += Examples + +=== Definitions + +The following definitions are used in the description below: + +- + + The file system path or subsystem-specific identification string of a + FIDO device. + +- , [oldpin] + + Strings passed directly in the executed command's argument vector. + +- + + The file system path of a file containing a FIDO credential ID in + binary representation. + +- + + The file system path of a file containing a NIST P-256 public key in + PEM format. + +=== Description + +The following examples are provided: + +- manifest + + Prints a list of configured FIDO devices. + +- info + + Prints information about . + +- reset + + Performs a factory reset on . + +- setpin [oldpin] + + Configures as the new PIN of . If [oldpin] is provided, + the device's PIN is changed from [oldpin] to . + +- cred [-t ecdsa|rsa|eddsa] [-k pubkey] [-ei cred_id] [-P pin] [-T seconds] + [-hruv] + + Creates a new credential on 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 . If option -i is specified, the credential + ID is stored in . The -e option may be used to add + 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 . + +- assert [-t ecdsa|rsa|eddsa] [-a cred_id] [-h hmac_secret] [-s hmac_salt] + [-P pin] [-T seconds] [-puv] + + Asks for a FIDO2 assertion corresponding to [cred_id], + which may be omitted for resident keys. The obtained assertion + is verified using . 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 are used as the salt. If option -h + is specified, the resulting hmac-secret is stored in . + The -T option may be used to enforce a timeout of . + +- retries + Get the number of PIN attempts left on before lockout. + +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. diff --git a/examples/assert.c b/examples/assert.c new file mode 100644 index 0000000..a421a51 --- /dev/null +++ b/examples/assert.c @@ -0,0 +1,329 @@ +/* + * 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 + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" + +#include "fido.h" +#include "fido/es256.h" +#include "fido/rs256.h" +#include "fido/eddsa.h" +#include "extern.h" + +#ifdef SIGNAL_EXAMPLE +extern volatile sig_atomic_t got_signal; +#endif + +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] [-puv] " + " \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 *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:h:ps:t:uv")) != -1) { + switch (ch) { + case 'P': + pin = optarg; + break; + case 'T': +#ifndef SIGNAL_EXAMPLE + errx(1, "-T not supported"); +#endif + 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; + 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 '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)); + + 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"); + } + + fido_assert_free(&assert); + + exit(0); +} diff --git a/examples/cred.c b/examples/cred.c new file mode 100644 index 0000000..e471f7e --- /dev/null +++ b/examples/cred.c @@ -0,0 +1,303 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" + +#include "fido.h" +#include "extern.h" + +#ifdef SIGNAL_EXAMPLE +extern volatile sig_atomic_t got_signal; +#endif + +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] [-hruv] \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); + + /* 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); + + /* 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); + + r = fido_cred_verify(cred); + if (r != FIDO_OK) + errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r); + + 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); +} + +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 *key_out = NULL; + const char *id_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 ((cred = fido_cred_new()) == NULL) + errx(1, "fido_cred_new"); + + while ((ch = getopt(argc, argv, "P:T:e:hi:k:rt:uv")) != -1) { + switch (ch) { + case 'P': + pin = optarg; + break; + case 'T': +#ifndef SIGNAL_EXAMPLE + errx(1, "-T not supported"); +#endif + 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; + 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(); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + fido_init(0); + + if ((dev = fido_dev_new()) == NULL) + errx(1, "fido_dev_new"); + + if ((r = fido_dev_open(dev, argv[0])) != FIDO_OK) + errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); + 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); + + 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); + + fido_cred_free(&cred); + + exit(0); +} diff --git a/examples/extern.h b/examples/extern.h new file mode 100644 index 0000000..578b8c4 --- /dev/null +++ b/examples/extern.h @@ -0,0 +1,32 @@ +/* + * 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 +#include +#include + +#ifdef HAVE_SIGNAL_H +#include +#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); +#endif + +#endif /* _EXTERN_H_ */ diff --git a/examples/info.c b/examples/info.c new file mode 100644 index 0000000..e79729c --- /dev/null +++ b/examples/info.c @@ -0,0 +1,216 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" + +#include "fido.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 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 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 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 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 \n"); + exit(EXIT_FAILURE); + } + + getinfo(argv[1]); + + exit(0); +} diff --git a/examples/manifest.c b/examples/manifest.c new file mode 100644 index 0000000..895447a --- /dev/null +++ b/examples/manifest.c @@ -0,0 +1,45 @@ +/* + * 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 + +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" + +#include "fido.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 0000000..36a7de2 --- /dev/null +++ b/examples/reset.c @@ -0,0 +1,64 @@ +/* + * 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 + +#include +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" + +#include "fido.h" +#include "extern.h" + +#ifdef SIGNAL_EXAMPLE +extern volatile sig_atomic_t got_signal; +#endif + +int +main(int argc, char **argv) +{ + fido_dev_t *dev; + int r; + + if (argc != 2) { + fprintf(stderr, "usage: reset \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 0000000..3ed7558 --- /dev/null +++ b/examples/retries.c @@ -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. + */ + +/* + * Get an authenticator's number of PIN attempts left. + */ + +#include + +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" + +#include "fido.h" + +int +main(int argc, char **argv) +{ + fido_dev_t *dev; + int n; + int r; + + if (argc != 2) { + fprintf(stderr, "usage: retries \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/setpin.c b/examples/setpin.c new file mode 100644 index 0000000..75d3d4a --- /dev/null +++ b/examples/setpin.c @@ -0,0 +1,59 @@ +/* + * 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 + +#include +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" + +#include "fido.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 [oldpin] \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 0000000..2f6a845 --- /dev/null +++ b/examples/util.c @@ -0,0 +1,415 @@ +/* + * 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 +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _MSC_VER +#include "../openbsd-compat/posix_win.h" +#endif + +#include "../openbsd-compat/openbsd-compat.h" + +#include "fido.h" +#include "fido/es256.h" +#include "fido/rs256.h" +#include "fido/eddsa.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, 0644)) < 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 0000000..ad30aa3 --- /dev/null +++ b/fuzz/CMakeLists.txt @@ -0,0 +1,44 @@ +# 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 + mutator_aux.c + uniform_random.c +) + + +# 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) diff --git a/fuzz/README b/fuzz/README new file mode 100644 index 0000000..ecb02bb --- /dev/null +++ b/fuzz/README @@ -0,0 +1,157 @@ +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=1 +-DAFL=1, and use preload-fuzz.c to read device data from stdin. Examples of +this approach can be found in the harnesses under fuzz/harnesses/ that fuzz +the standalone examples and tools bundled with libfido2. + +libFuzzer is better suited for bespoke fuzzers; see fuzz_cred.c, fuzz_credman.c, +fuzz_assert.c, and fuzz_mgmt.c for examples. To build these harnesses, +use -DFUZZ=1 -DLIBFUZZER=1. + +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, alternatively, patch libcbor with +the diff at the bottom of this file. + +1. Using ASAN + UBSAN + +- Make sure you have libcbor built with -fsanitize=address; +- Make sure you have OpenSSL built with -fsanitize=address; +- Rebuild libfido2 with -DASAN=1 -DUBSAN=1. + +1.1 Decide where your workspace will live + +$ export FAKEROOT=/home/pedro/fakeroot +$ mkdir -p ${FAKEROOT}/src + +1.2 Building libcbor with ASAN + +$ git clone https://github.com/pjk/libcbor ${FAKEROOT}/src/libcbor +$ cd ${FAKEROOT}/src/libcbor + +Assuming libfido2 is under ${FAKEROOT}/src/libfido2: + +$ patch -p0 < ${FAKEROOT}/src/libfido2/fuzz/README +$ mkdir build +$ cd build +$ cmake -DCMAKE_C_FLAGS_DEBUG="-g2 -fno-omit-frame-pointer" \ + -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_INSTALL_PREFIX=${FAKEROOT} -DSANITIZE=ON \ + -DCMAKE_INSTALL_LIBDIR=lib .. +$ make +$ make install + +1.3 Building OpenSSL with ASAN + +$ git clone https://github.com/openssl/openssl ${FAKEROOT}/src/openssl +$ cd ${FAKEROOT}/src/openssl +$ ./Configure linux-x86_64-clang enable-asan --prefix=${FAKEROOT} \ + --openssldir=${FAKEROOT}/openssl +$ make clean +$ make +$ make install_sw + +1.4 Building libfido2 with libFuzzer and ASAN + UBSAN + +$ cd ${FAKEROOT}/src/libfido2 +$ mkdir build +$ cd build +$ cmake -DFUZZ=1 -DLIBFUZZER=1 -DASAN=1 -DUBSAN=1 -DCMAKE_C_COMPILER=clang \ + -DCRYPTO_INCLUDE_DIRS=${FAKEROOT}/include \ + -DCRYPTO_LIBRARY_DIRS=${FAKEROOT}/lib \ + -DCBOR_INCLUDE_DIRS=${FAKEROOT}/include \ + -DCBOR_LIBRARY_DIRS=${FAKEROOT}/lib \ + -DCMAKE_BUILD_TYPE=Debug .. +$ make + +2. Using MSAN + UBSAN + +- Make sure you have libcbor built with -fsanitize=memory; +- Make sure you have OpenSSL built with -fsanitize=memory; +- Rebuild libfido2 with -DMSAN=1 -DUBSAN=1. + +2.1 Decide where your workspace will live + +$ export FAKEROOT=/home/pedro/fakeroot +$ mkdir -p ${FAKEROOT}/src + +2.2 Building libcbor with MSAN + +$ git clone https://github.com/pjk/libcbor ${FAKEROOT}/src/libcbor +$ cd ${FAKEROOT}/src/libcbor + +Assuming libfido2 is under ${FAKEROOT}/src/libfido2: + +$ patch -p0 < ${FAKEROOT}/src/libfido2/fuzz/README +$ mkdir build +$ cd build +$ cmake -DCMAKE_C_FLAGS_DEBUG="-fsanitize=memory,undefined -g2 -fno-omit-frame-pointer" \ + -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_INSTALL_PREFIX=${FAKEROOT} -DSANITIZE=OFF \ + -DCMAKE_INSTALL_LIBDIR=lib .. +$ make +$ make install + +2.2 Building OpenSSL with MSAN + +$ mkdir -p ${FAKEROOT}/src +$ git clone https://github.com/openssl/openssl ${FAKEROOT}/src/openssl +$ cd ${FAKEROOT}/src/openssl +$ ./Configure linux-x86_64-clang enable-msan --prefix=${FAKEROOT} \ + --openssldir=${FAKEROOT}/openssl +$ make clean +$ make +$ make install_sw + +2.3 Building libfido2 with libFuzzer and MSAN + UBSAN + +$ cd ${FAKEROOT}/src/libfido2 +$ mkdir build +$ cd build +$ cmake -DFUZZ=1 -DLIBFUZZER=1 -DMSAN=1 -DUBSAN=1 -DCMAKE_C_COMPILER=clang \ + -DCRYPTO_INCLUDE_DIRS=${FAKEROOT}/include \ + -DCRYPTO_LIBRARY_DIRS=${FAKEROOT}/lib \ + -DCBOR_INCLUDE_DIRS=${FAKEROOT}/include \ + -DCBOR_LIBRARY_DIRS=${FAKEROOT}/lib \ + -DCMAKE_BUILD_TYPE=Debug .. +$ make + +3. Running the libFuzzer harnesses + +When running under ASAN, you may want to set ASAN_OPTIONS to +'allocator_may_return_null=1:detect_stack_use_after_return=1'. + +The recommended way to run the harnesses is: + +$ fuzz_{assert,cred,credman,mgmt} -use_value_profile=1 -reload=30 \ + -print_pcs=1 -print_funcs=30 -timeout=10 -max_len=17408 CORPUS_DIR + +You may want to use -jobs or -workers depending on the number of logical +cores available for fuzzing. + +4. Auxiliary scripts + +A set of harnesses and auxiliary scripts can be found under harnesses/. To +compile coverage reports, adjust the harnesses to your setup and run 'report'. + +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/corpus.tgz b/fuzz/corpus.tgz new file mode 100644 index 0000000..9da3099 Binary files /dev/null and b/fuzz/corpus.tgz differ diff --git a/fuzz/functions.txt b/fuzz/functions.txt new file mode 100644 index 0000000..cd652f2 --- /dev/null +++ b/fuzz/functions.txt @@ -0,0 +1,564 @@ +File '/home/pedro/projects/libfido2/src/aes256.c': +Name Regions Miss Cover Lines Miss Cover +----------------------------------------------------------------------------- +aes256_cbc_enc 28 0 100.00% 41 0 100.00% +aes256_cbc_dec 28 0 100.00% 41 0 100.00% +----------------------------------------------------------------------------- +TOTAL 56 0 100.00% 82 0 100.00% + +File '/home/pedro/projects/libfido2/src/assert.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_dev_get_assert 35 3 91.43% 38 4 89.47% +fido_check_flags 13 0 100.00% 18 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 4 94.94% +fido_assert_set_clientdata_hash 6 0 100.00% 6 0 100.00% +fido_assert_set_hmac_salt 10 0 100.00% 7 0 100.00% +fido_assert_set_rp 12 1 91.67% 14 3 78.57% +fido_assert_allow_cred 13 2 84.62% 29 3 89.66% +fido_assert_set_extensions 9 0 100.00% 8 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% 15 0 100.00% +fido_assert_reset_rx 6 0 100.00% 24 0 100.00% +fido_assert_free 6 0 100.00% 13 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_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% 17 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 58 4 93.10% 84 11 86.90% +assert.c:fido_dev_get_assert_rx 20 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 11 0 100.00% 25 0 100.00% +assert.c:fido_get_next_assert_tx 9 0 100.00% 11 0 100.00% +assert.c:fido_get_next_assert_rx 16 2 87.50% 26 4 84.62% +assert.c:decrypt_hmac_secrets 9 1 88.89% 15 4 73.33% +assert.c:check_extensions 4 0 100.00% 9 0 100.00% +assert.c:get_signed_hash 32 0 100.00% 46 0 100.00% +assert.c:fido_assert_clean_authdata 1 0 100.00% 9 0 100.00% +assert.c:fido_assert_clean_sig 1 0 100.00% 5 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 569 29 94.90% 901 60 93.34% + +File '/home/pedro/projects/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 7 0 100.00% 18 0 100.00% +authkey.c:parse_authkey 8 0 100.00% 12 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 45 0 100.00% 75 0 100.00% + +File '/home/pedro/projects/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 24 2 91.67% 36 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% 10 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% 65 0 100.00% +bio.c:bio_prepare_hmac 18 0 100.00% 36 0 100.00% +bio.c:bio_rx_template_array 12 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 1 94.12% 24 3 87.50% +bio.c:bio_rx_enroll_begin 16 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 12 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 12 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% 6 0 100.00% +bio.c:bio_reset_enroll 3 0 100.00% 7 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 422 21 95.02% 661 25 96.22% + +File '/home/pedro/projects/libfido2/src/blob.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_blob_new 1 0 100.00% 3 0 100.00% +fido_blob_set 11 1 90.91% 25 4 84.00% +fido_blob_free 8 0 100.00% 16 0 100.00% +fido_free_blob_array 9 0 100.00% 17 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% +--------------------------------------------------------------------------------------- +TOTAL 39 1 97.44% 73 4 94.52% + +File '/home/pedro/projects/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 '/home/pedro/projects/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 2 88.89% 23 0 100.00% +cbor_encode_extensions 13 1 92.31% 16 0 100.00% +cbor_encode_options 13 0 100.00% 14 0 100.00% +cbor_encode_assert_options 13 0 100.00% 14 0 100.00% +cbor_encode_pin_auth 8 0 100.00% 12 0 100.00% +cbor_encode_pin_opt 1 0 100.00% 3 0 100.00% +cbor_encode_pin_enc 4 0 100.00% 12 0 100.00% +cbor_encode_change_pin_auth 44 1 97.73% 69 3 95.65% +cbor_encode_set_pin_auth 17 0 100.00% 28 0 100.00% +cbor_encode_pin_hash_enc 15 0 100.00% 27 0 100.00% +cbor_encode_hmac_secret_param 41 1 97.56% 66 4 93.94% +cbor_decode_fmt 9 0 100.00% 18 0 100.00% +cbor_decode_pubkey 21 1 95.24% 32 2 93.75% +cbor_decode_cred_authdata 31 0 100.00% 46 0 100.00% +cbor_decode_assert_authdata 23 0 100.00% 44 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.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:sha256 7 0 100.00% 15 0 100.00% +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% 58 0 100.00% +cbor.c:decode_extensions 16 4 75.00% 34 6 82.35% +cbor.c:decode_extension 19 19 0.00% 27 27 0.00% +cbor.c:decode_hmac_secret 16 0 100.00% 32 0 100.00% +cbor.c:decode_hmac_secret_aux 7 0 100.00% 17 0 100.00% +cbor.c:decode_attstmt_entry 29 0 100.00% 39 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 844 31 96.33% 1319 47 96.44% + +File '/home/pedro/projects/libfido2/src/cred.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_dev_make_cred 12 0 100.00% 9 0 100.00% +fido_check_rp_id 4 0 100.00% 14 0 100.00% +fido_cred_verify 45 0 100.00% 71 0 100.00% +fido_cred_verify_self 54 10 81.48% 90 14 84.44% +fido_cred_new 1 0 100.00% 3 0 100.00% +fido_cred_reset_tx 1 0 100.00% 20 0 100.00% +fido_cred_reset_rx 1 0 100.00% 8 0 100.00% +fido_cred_free 6 1 83.33% 13 0 100.00% +fido_cred_set_authdata 22 0 100.00% 36 0 100.00% +fido_cred_set_authdata_raw 22 2 90.91% 35 4 88.57% +fido_cred_set_x509 12 0 100.00% 16 0 100.00% +fido_cred_set_sig 12 0 100.00% 16 0 100.00% +fido_cred_exclude 14 2 85.71% 25 3 88.00% +fido_cred_set_clientdata_hash 6 0 100.00% 6 0 100.00% +fido_cred_set_rp 18 2 88.89% 26 6 76.92% +fido_cred_set_user 33 4 87.88% 50 13 74.00% +fido_cred_set_extensions 9 0 100.00% 8 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_fmt 16 4 75.00% 15 1 93.33% +fido_cred_set_type 17 2 88.24% 9 1 88.89% +fido_cred_type 1 0 100.00% 3 0 100.00% +fido_cred_flags 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_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_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% +cred.c:fido_dev_make_cred_wait 10 0 100.00% 9 0 100.00% +cred.c:fido_dev_make_cred_tx 59 0 100.00% 81 0 100.00% +cred.c:fido_dev_make_cred_rx 22 0 100.00% 28 0 100.00% +cred.c:parse_makecred_reply 10 0 100.00% 23 0 100.00% +cred.c:check_extensions 4 0 100.00% 9 0 100.00% +cred.c:get_signed_hash_packed 23 1 95.65% 38 3 92.11% +cred.c:get_signed_hash_u2f 22 0 100.00% 20 0 100.00% +cred.c:verify_sig 27 1 96.30% 40 4 90.00% +cred.c:fido_cred_clean_authdata 1 0 100.00% 9 0 100.00% +cred.c:fido_cred_clean_x509 1 0 100.00% 5 0 100.00% +cred.c:fido_cred_clean_sig 1 0 100.00% 5 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 532 35 93.42% 850 55 93.53% + +File '/home/pedro/projects/libfido2/src/credman.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_credman_get_dev_metadata 9 2 77.78% 8 0 100.00% +fido_credman_get_dev_rk 9 2 77.78% 8 0 100.00% +fido_credman_del_dev_rk 9 2 77.78% 8 0 100.00% +fido_credman_get_dev_rp 9 2 77.78% 8 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% 9 0 100.00% +credman.c:credman_tx 30 0 100.00% 53 0 100.00% +credman.c:credman_prepare_hmac 21 1 95.24% 43 2 95.35% +credman.c:credman_rx_metadata 12 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% 26 0 100.00% +credman.c:credman_rx_rk 20 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 13 0 100.00% 25 0 100.00% +credman.c:credman_rx_next_rk 16 2 87.50% 26 4 84.62% +credman.c:credman_del_rk_wait 16 0 100.00% 19 0 100.00% +credman.c:credman_get_rp_wait 23 0 100.00% 16 0 100.00% +credman.c:credman_rx_rp 20 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 16 2 87.50% 26 4 84.62% +credman.c:credman_reset_rk 4 0 100.00% 10 0 100.00% +credman.c:credman_reset_rp 4 0 100.00% 15 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 381 18 95.28% 589 15 97.45% + +File '/home/pedro/projects/libfido2/src/dev.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_dev_open 1 0 100.00% 3 0 100.00% +fido_dev_close 8 2 75.00% 9 0 100.00% +fido_dev_cancel 8 2 75.00% 6 3 50.00% +fido_dev_set_io_functions 18 4 77.78% 19 6 68.42% +fido_init 7 1 85.71% 4 0 100.00% +fido_dev_new 9 1 88.89% 22 4 81.82% +fido_dev_free 6 0 100.00% 10 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_force_u2f 2 0 100.00% 3 0 100.00% +fido_dev_force_fido2 2 2 0.00% 3 3 0.00% +dev.c:fido_dev_open_wait 10 0 100.00% 9 0 100.00% +dev.c:fido_dev_open_tx 26 8 69.23% 32 12 62.50% +dev.c:obtain_nonce 13 2 84.62% 18 2 88.89% +dev.c:fido_dev_open_rx 14 0 100.00% 27 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 131 22 83.21% 183 30 83.61% + +File '/home/pedro/projects/libfido2/src/ecdh.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_do_ecdh 29 0 100.00% 44 0 100.00% +ecdh.c:do_ecdh 39 0 100.00% 60 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 68 0 100.00% 104 0 100.00% + +File '/home/pedro/projects/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% 11 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 4 71.43% 12 2 83.33% +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 4 92.59% 79 2 97.47% + +File '/home/pedro/projects/libfido2/src/err.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_strerr 108 108 0.00% 112 112 0.00% +--------------------------------------------------------------------------------------- +TOTAL 108 108 0.00% 112 112 0.00% + +File '/home/pedro/projects/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% 11 0 100.00% +es256_pk_new 1 0 100.00% 3 0 100.00% +es256_pk_free 6 0 100.00% 11 0 100.00% +es256_pk_from_ptr 6 0 100.00% 8 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 2 94.87% 46 6 86.96% +es256_pk_to_EVP_PKEY 41 0 100.00% 58 0 100.00% +es256_pk_from_EC_KEY 38 2 94.74% 39 7 82.05% +es256_sk_to_EVP_PKEY 27 0 100.00% 41 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 273 4 98.53% 372 13 96.51% + +File '/home/pedro/projects/libfido2/src/extern.h': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- + +File '/home/pedro/projects/libfido2/src/fido.h': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- + +File '/home/pedro/projects/libfido2/src/hid.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_dev_info_new 1 1 0.00% 3 3 0.00% +fido_dev_info_free 9 9 0.00% 17 17 0.00% +fido_dev_info_ptr 1 1 0.00% 3 3 0.00% +fido_dev_info_path 1 1 0.00% 3 3 0.00% +fido_dev_info_vendor 1 1 0.00% 3 3 0.00% +fido_dev_info_product 1 1 0.00% 3 3 0.00% +fido_dev_info_manufacturer_string 1 1 0.00% 3 3 0.00% +fido_dev_info_product_string 1 1 0.00% 3 3 0.00% +--------------------------------------------------------------------------------------- +TOTAL 16 16 0.00% 38 38 0.00% + +File '/home/pedro/projects/libfido2/src/hid_linux.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_dev_info_manifest 33 33 0.00% 40 40 0.00% +fido_hid_open 6 6 0.00% 11 11 0.00% +fido_hid_close 1 1 0.00% 6 6 0.00% +fido_hid_read 12 12 0.00% 16 16 0.00% +fido_hid_write 12 12 0.00% 16 16 0.00% +hid_linux.c:copy_info 35 35 0.00% 56 56 0.00% +hid_linux.c:is_fido 6 6 0.00% 14 14 0.00% +hid_linux.c:get_report_descriptor 17 17 0.00% 31 31 0.00% +hid_linux.c:get_usage_info 16 16 0.00% 33 33 0.00% +hid_linux.c:get_key_len 6 6 0.00% 14 14 0.00% +hid_linux.c:get_key_val 6 6 0.00% 20 20 0.00% +hid_linux.c:parse_uevent 16 16 0.00% 30 30 0.00% +--------------------------------------------------------------------------------------- +TOTAL 166 166 0.00% 287 287 0.00% + +File '/home/pedro/projects/libfido2/src/info.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +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_free 6 1 83.33% 14 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_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_maxmsgsiz 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% +info.c:fido_dev_get_cbor_info_wait 10 0 100.00% 9 0 100.00% +info.c:fido_dev_get_cbor_info_tx 9 0 100.00% 13 0 100.00% +info.c:fido_dev_get_cbor_info_rx 7 0 100.00% 18 0 100.00% +info.c:parse_reply_element 13 0 100.00% 27 0 100.00% +info.c:decode_versions 12 0 100.00% 21 0 100.00% +info.c:decode_version 4 0 100.00% 14 0 100.00% +info.c:decode_extensions 12 0 100.00% 21 0 100.00% +info.c:decode_extension 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: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% +--------------------------------------------------------------------------------------- +TOTAL 148 1 99.32% 305 0 100.00% + +File '/home/pedro/projects/libfido2/src/io.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_tx 18 0 100.00% 35 0 100.00% +fido_rx 34 3 91.18% 84 12 85.71% +fido_rx_cbor_status 9 0 100.00% 13 0 100.00% +io.c:tx_preamble 16 1 93.75% 24 1 95.83% +io.c:tx_frame 16 1 93.75% 21 0 100.00% +io.c:rx_preamble 11 0 100.00% 12 0 100.00% +io.c:rx_frame 9 1 88.89% 12 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 113 6 94.69% 201 13 93.53% + +File '/home/pedro/projects/libfido2/src/iso7816.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +iso7816_new 4 0 100.00% 19 0 100.00% +iso7816_free 6 0 100.00% 11 0 100.00% +iso7816_add 6 1 83.33% 10 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% 47 0 100.00% + +File '/home/pedro/projects/libfido2/src/log.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_log_init 1 1 0.00% 3 3 0.00% +fido_log_xxd 11 8 27.27% 18 12 33.33% +fido_log_debug 4 1 75.00% 13 8 38.46% +--------------------------------------------------------------------------------------- +TOTAL 16 10 37.50% 34 23 32.35% + +File '/home/pedro/projects/libfido2/src/pin.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +fido_dev_get_pin_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% +cbor_add_pin_params 17 0 100.00% 27 0 100.00% +pin.c:fido_dev_get_pin_token_wait 10 0 100.00% 9 0 100.00% +pin.c:fido_dev_get_pin_token_tx 29 0 100.00% 40 0 100.00% +pin.c:fido_dev_get_pin_token_rx 21 0 100.00% 36 0 100.00% +pin.c:parse_pintoken 8 0 100.00% 12 0 100.00% +pin.c:fido_dev_set_pin_wait 16 0 100.00% 22 0 100.00% +pin.c:fido_dev_change_pin_tx 41 0 100.00% 59 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_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_retry_count_rx 12 0 100.00% 21 0 100.00% +pin.c:parse_retry_count 13 0 100.00% 20 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 250 0 100.00% 364 0 100.00% + +File '/home/pedro/projects/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 10 0 100.00% 9 0 100.00% +reset.c:fido_dev_reset_tx 9 0 100.00% 11 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 20 0 100.00% 23 0 100.00% + +File '/home/pedro/projects/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% 11 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 6 81.25% 32 9 71.88% +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 6 94.12% 140 9 93.57% + +File '/home/pedro/projects/libfido2/src/u2f.c': +Name Regions Miss Cover Lines Miss Cover +--------------------------------------------------------------------------------------- +u2f_register 70 1 98.57% 89 0 100.00% +u2f_authenticate 27 0 100.00% 33 0 100.00% +u2f.c:key_lookup 44 0 100.00% 69 0 100.00% +u2f.c:send_dummy_register 31 1 96.77% 50 0 100.00% +u2f.c:parse_register_reply 57 0 100.00% 83 0 100.00% +u2f.c:x5c_get 21 1 95.24% 37 3 91.89% +u2f.c:sig_get 8 1 87.50% 16 6 62.50% +u2f.c:encode_cred_authdata 37 2 94.59% 82 6 92.68% +u2f.c:cbor_blob_from_ec_point 22 0 100.00% 39 0 100.00% +u2f.c:u2f_authenticate_single 34 2 94.12% 53 4 92.45% +u2f.c:do_auth 50 1 98.00% 72 0 100.00% +u2f.c:parse_auth_reply 23 2 91.30% 29 3 89.66% +u2f.c:authdata_fake 12 0 100.00% 34 0 100.00% +--------------------------------------------------------------------------------------- +TOTAL 436 11 97.48% 686 22 96.79% diff --git a/fuzz/fuzz_assert.c b/fuzz/fuzz_assert.c new file mode 100644 index 0000000..0395345 --- /dev/null +++ b/fuzz/fuzz_assert.c @@ -0,0 +1,664 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "mutator_aux.h" +#include "fido.h" +#include "fido/es256.h" +#include "fido/rs256.h" +#include "fido/eddsa.h" + +#include "../openbsd-compat/openbsd-compat.h" + +#define TAG_U2F 0x01 +#define TAG_TYPE 0x02 +#define TAG_CDH 0x03 +#define TAG_RP_ID 0x04 +#define TAG_EXT 0x05 +#define TAG_SEED 0x06 +#define TAG_UP 0x07 +#define TAG_UV 0x08 +#define TAG_WIRE_DATA 0x09 +#define TAG_CRED_COUNT 0x0a +#define TAG_CRED 0x0b +#define TAG_ES256 0x0c +#define TAG_RS256 0x0d +#define TAG_PIN 0x0e +#define TAG_EDDSA 0x0f + +/* 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 u2f; + uint8_t up; + uint8_t uv; +}; + +/* Example parameters. */ +static const char dummy_rp_id[] = "localhost"; +static const char dummy_pin[] = "9}4gT:8d=A37Dh}U"; + +static 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, +}; + +static 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, +}; + +static 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, +}; + +static 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, +}; + +/* + * 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[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xf7, + 0x6f, 0xda, 0x52, 0xfd, 0xcb, 0xb6, 0x24, 0x00, + 0x92, 0x00, 0x0e, 0x02, 0x05, 0x00, 0x02, 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, + 0x00, 0x92, 0x00, 0x0e, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0xe9, 0x1d, 0x9b, + 0xac, 0x14, 0x25, 0x5f, 0xda, 0x1e, 0x11, 0xdb, + 0xae, 0xc2, 0x90, 0x22, 0xca, 0x32, 0xec, 0x32, + 0xe6, 0x05, 0x15, 0x44, 0xe5, 0xe8, 0xbc, 0x4f, + 0x0a, 0xb6, 0x1a, 0xeb, 0x11, 0x22, 0x58, 0x20, + 0xcc, 0x72, 0xf0, 0x22, 0xe8, 0x28, 0x82, 0xc5, + 0x00, 0x92, 0x00, 0x0e, 0x00, 0xa6, 0x65, 0x6e, + 0xff, 0x1e, 0xe3, 0x7f, 0x27, 0x44, 0x2d, 0xfb, + 0x8d, 0x41, 0xfa, 0x85, 0x0e, 0xcb, 0xda, 0x95, + 0x64, 0x64, 0x9b, 0x1f, 0x34, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0e, 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, + 0x00, 0x92, 0x00, 0x0e, 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 +}; + +/* + * 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[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x0f, + 0x26, 0x9c, 0xd3, 0x87, 0x0d, 0x7b, 0xf6, 0x00, + 0x00, 0x99, 0x01, 0x02, 0x01, 0x01, 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, 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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 +}; + +int LLVMFuzzerTestOneInput(const uint8_t *, size_t); +size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); + +static int +unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN +{ + uint8_t **pp = (void *)&ptr; + + if (unpack_byte(TAG_UV, pp, &len, &p->uv) < 0 || + unpack_byte(TAG_UP, pp, &len, &p->up) < 0 || + unpack_byte(TAG_U2F, pp, &len, &p->u2f) < 0 || + unpack_byte(TAG_TYPE, pp, &len, &p->type) < 0 || + unpack_byte(TAG_CRED_COUNT, pp, &len, &p->cred_count) < 0 || + unpack_int(TAG_EXT, pp, &len, &p->ext) < 0 || + unpack_int(TAG_SEED, pp, &len, &p->seed) < 0 || + unpack_string(TAG_RP_ID, pp, &len, p->rp_id) < 0 || + unpack_string(TAG_PIN, pp, &len, p->pin) < 0 || + unpack_blob(TAG_WIRE_DATA, pp, &len, &p->wire_data) < 0 || + unpack_blob(TAG_RS256, pp, &len, &p->rs256) < 0 || + unpack_blob(TAG_ES256, pp, &len, &p->es256) < 0 || + unpack_blob(TAG_EDDSA, pp, &len, &p->eddsa) < 0 || + unpack_blob(TAG_CRED, pp, &len, &p->cred) < 0 || + unpack_blob(TAG_CDH, pp, &len, &p->cdh) < 0) + return (-1); + + return (0); +} + +static size_t +pack(uint8_t *ptr, size_t len, const struct param *p) +{ + const size_t max = len; + + if (pack_byte(TAG_UV, &ptr, &len, p->uv) < 0 || + pack_byte(TAG_UP, &ptr, &len, p->up) < 0 || + pack_byte(TAG_U2F, &ptr, &len, p->u2f) < 0 || + pack_byte(TAG_TYPE, &ptr, &len, p->type) < 0 || + pack_byte(TAG_CRED_COUNT, &ptr, &len, p->cred_count) < 0 || + pack_int(TAG_EXT, &ptr, &len, p->ext) < 0 || + pack_int(TAG_SEED, &ptr, &len, p->seed) < 0 || + pack_string(TAG_RP_ID, &ptr, &len, p->rp_id) < 0 || + pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 || + pack_blob(TAG_WIRE_DATA, &ptr, &len, &p->wire_data) < 0 || + pack_blob(TAG_RS256, &ptr, &len, &p->rs256) < 0 || + pack_blob(TAG_ES256, &ptr, &len, &p->es256) < 0 || + pack_blob(TAG_EDDSA, &ptr, &len, &p->eddsa) < 0 || + pack_blob(TAG_CRED, &ptr, &len, &p->cred) < 0 || + pack_blob(TAG_CDH, &ptr, &len, &p->cdh) < 0) + return (0); + + return (max - len); +} + +static void +get_assert(fido_assert_t *assert, uint8_t u2f, const struct blob *cdh, + const char *rp_id, int ext, uint8_t up, uint8_t uv, const char *pin, + uint8_t cred_count, struct blob *cred) +{ + fido_dev_t *dev; + fido_dev_io_t io; + + io.open = dev_open; + io.close = dev_close; + io.read = dev_read; + io.write = dev_write; + + if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev, + &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) { + fido_dev_free(&dev); + return; + } + + if (u2f & 1) + fido_dev_force_u2f(dev); + + 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); + if (ext & 1) + fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET); + if (up & 1) + fido_assert_set_up(assert, FIDO_OPT_TRUE); + if (uv & 1) + fido_assert_set_uv(assert, FIDO_OPT_TRUE); + /* XXX reuse cred as hmac salt to keep struct param small */ + fido_assert_set_hmac_salt(assert, cred->body, cred->len); + + fido_dev_get_assert(dev, assert, u2f & 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); + } + fido_assert_set_extensions(assert, ext); + 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_sig(assert, 0, sig_ptr, sig_len); + fido_assert_verify(assert, 0, type, pk); + + 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); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + 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; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (0); + + srandom((unsigned int)p.seed); + + fido_init(0); + + switch (p.type & 3) { + case 0: + cose_alg = COSE_ES256; + + if ((es256_pk = es256_pk_new()) == NULL) + return (0); + + 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 (0); + + 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 (0); + + 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.u2f, &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(fido_assert_user_icon(assert, i), + xstrlen(fido_assert_user_icon(assert, i))); + consume(fido_assert_user_name(assert, i), + xstrlen(fido_assert_user_name(assert, i))); + consume(fido_assert_user_display_name(assert, i), + xstrlen(fido_assert_user_display_name(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); + + return (0); +} + +static size_t +pack_dummy(uint8_t *ptr, size_t len) +{ + struct param dummy; + uint8_t blob[16384]; + 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)); + + 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.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); + + blob_len = pack(blob, sizeof(blob), &dummy); + assert(blob_len != 0); + + if (blob_len > len) { + memcpy(ptr, blob, len); + return (len); + } + + memcpy(ptr, blob, blob_len); + + return (blob_len); +} + +size_t +LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, + unsigned int seed) NO_MSAN +{ + struct param p; + uint8_t blob[16384]; + size_t blob_len; + + (void)seed; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (pack_dummy(data, maxsize)); + + mutate_byte(&p.uv); + mutate_byte(&p.up); + mutate_byte(&p.u2f); + mutate_byte(&p.type); + mutate_byte(&p.cred_count); + + mutate_int(&p.ext); + p.seed = (int)seed; + + if (p.u2f & 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); + 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); + + blob_len = pack(blob, sizeof(blob), &p); + + if (blob_len == 0 || blob_len > maxsize) + return (0); + + memcpy(data, blob, blob_len); + + return (blob_len); +} diff --git a/fuzz/fuzz_bio.c b/fuzz/fuzz_bio.c new file mode 100644 index 0000000..f1596a7 --- /dev/null +++ b/fuzz/fuzz_bio.c @@ -0,0 +1,755 @@ +/* + * 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 +#include +#include +#include +#include + +#include "mutator_aux.h" +#include "fido.h" +#include "fido/bio.h" + +#include "../openbsd-compat/openbsd-compat.h" + +#define TAG_PIN 0x01 +#define TAG_NAME 0x02 +#define TAG_SEED 0x03 +#define TAG_ID 0x04 +#define TAG_INFO_WIRE_DATA 0x05 +#define TAG_ENROLL_WIRE_DATA 0x06 +#define TAG_LIST_WIRE_DATA 0x07 +#define TAG_SET_NAME_WIRE_DATA 0x08 +#define TAG_REMOVE_WIRE_DATA 0x09 + +/* 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; +}; + +/* Example parameters. */ +static const uint8_t dummy_id[] = { 0x5e, 0xd2, }; +static const char dummy_pin[] = "3Q;I){TAx"; +static const char dummy_name[] = "finger1"; + +/* + * Collection of HID reports from an authenticator issued with a FIDO2 + * 'getFingerprintSensorInfo' bio enrollment command. + */ +static const uint8_t dummy_info_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xf0, + 0x08, 0xc1, 0x8f, 0x76, 0x4b, 0x8f, 0xa9, 0x00, + 0x10, 0x00, 0x04, 0x02, 0x00, 0x04, 0x06, 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, + 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, +}; + +/* + * Collection of HID reports from an authenticator issued with FIDO2 + * 'enrollBegin' + 'enrollCaptureNextSample' bio enrollment commands. + */ +static const uint8_t dummy_enroll_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x06, + 0xb4, 0xba, 0x2e, 0xb3, 0x88, 0x24, 0x38, 0x00, + 0x0a, 0x00, 0x05, 0x02, 0x00, 0x04, 0x06, 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, + 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0xc9, 0x12, 0x01, + 0xab, 0x88, 0xd7, 0x0a, 0x24, 0xdd, 0xdc, 0xde, + 0x16, 0x27, 0x50, 0x77, 0x37, 0x06, 0xd3, 0x48, + 0xe6, 0xf9, 0xdb, 0xaa, 0x10, 0x83, 0x81, 0xac, + 0x13, 0x3c, 0xf9, 0x77, 0x2d, 0x22, 0x58, 0x20, + 0xda, 0x20, 0x71, 0x03, 0x01, 0x40, 0xac, 0xd0, + 0x00, 0x0a, 0x00, 0x05, 0x00, 0xb8, 0xdf, 0x2a, + 0x95, 0xd3, 0x88, 0x1c, 0x06, 0x34, 0x30, 0xf1, + 0xf3, 0xcd, 0x27, 0x40, 0x90, 0x5c, 0xc6, 0x74, + 0x66, 0xff, 0x10, 0xde, 0xb6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0x18, 0x81, 0xff, 0xf2, 0xf5, + 0xde, 0x74, 0x43, 0xd5, 0xe0, 0x77, 0x37, 0x6b, + 0x6c, 0x18, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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, 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, 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, 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, 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, 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, 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, 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, +}; + +/* + * Collection of HID reports from an authenticator issued with a FIDO2 + * 'enumerateEnrollments' bio enrollment command. + */ +static const uint8_t dummy_list_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xae, + 0x21, 0x88, 0x51, 0x09, 0x6f, 0xd7, 0xbb, 0x00, + 0x10, 0x00, 0x0f, 0x02, 0x00, 0x04, 0x06, 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, + 0x00, 0x10, 0x00, 0x0f, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x5a, 0x70, 0x63, + 0x11, 0x5b, 0xa6, 0xe1, 0x8e, 0x4a, 0xb0, 0x75, + 0xe7, 0xfd, 0x39, 0x26, 0x29, 0xed, 0x69, 0xb0, + 0xc1, 0x1f, 0xa5, 0x7d, 0xcb, 0x64, 0x1e, 0x7c, + 0x9f, 0x60, 0x5e, 0xb2, 0xf8, 0x22, 0x58, 0x20, + 0xec, 0xe9, 0x1b, 0x11, 0xac, 0x2a, 0x0d, 0xd5, + 0x00, 0x10, 0x00, 0x0f, 0x00, 0x3b, 0x9f, 0xba, + 0x0f, 0x25, 0xd5, 0x24, 0x33, 0x4c, 0x5d, 0x0f, + 0x63, 0xbf, 0xf1, 0xf3, 0x64, 0x55, 0x78, 0x1a, + 0x59, 0x6e, 0x65, 0x59, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x0f, 0x90, 0x00, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0xb9, 0x31, 0x34, 0xe2, 0x71, + 0x6a, 0x8e, 0xa3, 0x60, 0xec, 0x5e, 0xd2, 0x13, + 0x2e, 0x19, 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, 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 +}; + +/* + * Collection of HID reports from an authenticator issued with a FIDO2 + * 'setFriendlyName' bio enrollment command. + */ +static const uint8_t dummy_set_name_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xac, + 0x48, 0xfd, 0xbd, 0xdd, 0x36, 0x24, 0x4d, 0x00, + 0x10, 0x00, 0x10, 0x02, 0x00, 0x04, 0x06, 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, + 0x00, 0x10, 0x00, 0x10, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x5a, 0x70, 0x63, + 0x11, 0x5b, 0xa6, 0xe1, 0x8e, 0x4a, 0xb0, 0x75, + 0xe7, 0xfd, 0x39, 0x26, 0x29, 0xed, 0x69, 0xb0, + 0xc1, 0x1f, 0xa5, 0x7d, 0xcb, 0x64, 0x1e, 0x7c, + 0x9f, 0x60, 0x5e, 0xb2, 0xf8, 0x22, 0x58, 0x20, + 0xec, 0xe9, 0x1b, 0x11, 0xac, 0x2a, 0x0d, 0xd5, + 0x00, 0x10, 0x00, 0x10, 0x00, 0x3b, 0x9f, 0xba, + 0x0f, 0x25, 0xd5, 0x24, 0x33, 0x4c, 0x5d, 0x0f, + 0x63, 0xbf, 0xf1, 0xf3, 0x64, 0x55, 0x78, 0x1a, + 0x59, 0x6e, 0x65, 0x59, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x10, 0x90, 0x00, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0x40, 0x95, 0xf3, 0xcb, 0xae, + 0xf2, 0x8d, 0xd9, 0xe0, 0xe0, 0x8a, 0xbd, 0xc3, + 0x03, 0x58, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x10, 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, +}; + +/* + * Collection of HID reports from an authenticator issued with a FIDO2 + * 'removeEnrollment' bio enrollment command. + */ +static const uint8_t dummy_remove_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x4b, + 0x24, 0xde, 0xd9, 0x06, 0x57, 0x1a, 0xbd, 0x00, + 0x10, 0x00, 0x15, 0x02, 0x00, 0x04, 0x06, 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, + 0x00, 0x10, 0x00, 0x15, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x5a, 0x70, 0x63, + 0x11, 0x5b, 0xa6, 0xe1, 0x8e, 0x4a, 0xb0, 0x75, + 0xe7, 0xfd, 0x39, 0x26, 0x29, 0xed, 0x69, 0xb0, + 0xc1, 0x1f, 0xa5, 0x7d, 0xcb, 0x64, 0x1e, 0x7c, + 0x9f, 0x60, 0x5e, 0xb2, 0xf8, 0x22, 0x58, 0x20, + 0xec, 0xe9, 0x1b, 0x11, 0xac, 0x2a, 0x0d, 0xd5, + 0x00, 0x10, 0x00, 0x15, 0x00, 0x3b, 0x9f, 0xba, + 0x0f, 0x25, 0xd5, 0x24, 0x33, 0x4c, 0x5d, 0x0f, + 0x63, 0xbf, 0xf1, 0xf3, 0x64, 0x55, 0x78, 0x1a, + 0x59, 0x6e, 0x65, 0x59, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x15, 0x90, 0x00, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0xb0, 0xd0, 0x71, 0x2f, 0xa7, + 0x8b, 0x89, 0xbd, 0xca, 0xa4, 0x1e, 0x6c, 0x43, + 0xa1, 0x71, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x15, 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, +}; + +int LLVMFuzzerTestOneInput(const uint8_t *, size_t); +size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); + +static int +unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN +{ + uint8_t **pp = (void *)&ptr; + + if (unpack_string(TAG_PIN, pp, &len, p->pin) < 0 || + unpack_string(TAG_NAME, pp, &len, p->name) < 0 || + unpack_int(TAG_SEED, pp, &len, &p->seed) < 0 || + unpack_blob(TAG_ID, pp, &len, &p->id) < 0 || + unpack_blob(TAG_INFO_WIRE_DATA, pp, &len, &p->info_wire_data) < 0 || + unpack_blob(TAG_ENROLL_WIRE_DATA, pp, &len, &p->enroll_wire_data) < 0 || + unpack_blob(TAG_LIST_WIRE_DATA, pp, &len, &p->list_wire_data) < 0 || + unpack_blob(TAG_SET_NAME_WIRE_DATA, pp, &len, &p->set_name_wire_data) < 0 || + unpack_blob(TAG_REMOVE_WIRE_DATA, pp, &len, &p->remove_wire_data) < 0) + return (-1); + + return (0); +} + +static size_t +pack(uint8_t *ptr, size_t len, const struct param *p) +{ + const size_t max = len; + + if (pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 || + pack_string(TAG_NAME, &ptr, &len, p->name) < 0 || + pack_int(TAG_SEED, &ptr, &len, p->seed) < 0 || + pack_blob(TAG_ID, &ptr, &len, &p->id) < 0 || + pack_blob(TAG_INFO_WIRE_DATA, &ptr, &len, &p->info_wire_data) < 0 || + pack_blob(TAG_ENROLL_WIRE_DATA, &ptr, &len, &p->enroll_wire_data) < 0 || + pack_blob(TAG_LIST_WIRE_DATA, &ptr, &len, &p->list_wire_data) < 0 || + pack_blob(TAG_SET_NAME_WIRE_DATA, &ptr, &len, &p->set_name_wire_data) < 0 || + pack_blob(TAG_REMOVE_WIRE_DATA, &ptr, &len, &p->remove_wire_data) < 0) + return (0); + + return (max - len); +} + +static fido_dev_t * +prepare_dev() +{ + fido_dev_t *dev; + fido_dev_io_t io; + + io.open = dev_open; + io.close = dev_close; + io.read = dev_read; + io.write = dev_write; + + if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev, + &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) { + fido_dev_free(&dev); + return (NULL); + } + + return (dev); +} + +static void +get_info(struct param *p) +{ + fido_dev_t *dev = NULL; + fido_bio_info_t *i = NULL; + uint8_t type; + uint8_t max_samples; + + 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; + + fido_bio_dev_get_info(dev, i); + + 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(fido_bio_template_name(t), xstrlen(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(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, 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(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(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(struct param *p) +{ + fido_dev_t *dev = NULL; + fido_bio_template_t *t = NULL; + + 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; + + fido_bio_template_set_id(t, p->id.body, p->id.len); + consume_template(t); + + 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); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct param p; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (0); + + srandom((unsigned int)p.seed); + + fido_init(0); + + get_info(&p); + enroll(&p); + list(&p); + set_name(&p); + del(&p); + + return (0); +} + +static size_t +pack_dummy(uint8_t *ptr, size_t len) +{ + struct param dummy; + uint8_t blob[32768]; + 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); + + blob_len = pack(blob, sizeof(blob), &dummy); + assert(blob_len != 0); + + if (blob_len > len) { + memcpy(ptr, blob, len); + return (len); + } + + memcpy(ptr, blob, blob_len); + + return (blob_len); +} + +size_t +LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, + unsigned int seed) NO_MSAN +{ + struct param p; + uint8_t blob[16384]; + size_t blob_len; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (pack_dummy(data, maxsize)); + + p.seed = (int)seed; + + mutate_blob(&p.id); + 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); + + mutate_string(p.pin); + mutate_string(p.name); + + blob_len = pack(blob, sizeof(blob), &p); + + if (blob_len == 0 || blob_len > maxsize) + return (0); + + memcpy(data, blob, blob_len); + + return (blob_len); +} diff --git a/fuzz/fuzz_cred.c b/fuzz/fuzz_cred.c new file mode 100644 index 0000000..7bd1d3c --- /dev/null +++ b/fuzz/fuzz_cred.c @@ -0,0 +1,925 @@ +/* + * 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 +#include +#include +#include +#include + +#include "mutator_aux.h" +#include "fido.h" + +#include "../openbsd-compat/openbsd-compat.h" + +#define TAG_U2F 0x01 +#define TAG_TYPE 0x02 +#define TAG_CDH 0x03 +#define TAG_RP_ID 0x04 +#define TAG_RP_NAME 0x05 +#define TAG_USER_ID 0x06 +#define TAG_USER_NAME 0x07 +#define TAG_USER_NICK 0x08 +#define TAG_USER_ICON 0x09 +#define TAG_EXT 0x0a +#define TAG_SEED 0x0b +#define TAG_RK 0x0c +#define TAG_UV 0x0d +#define TAG_PIN 0x0e +#define TAG_WIRE_DATA 0x0f +#define TAG_EXCL_COUNT 0x10 +#define TAG_EXCL_CRED 0x11 + +/* 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 u2f; + uint8_t uv; +}; + +/* Example parameters. */ +static const char dummy_rp_id[] = "localhost"; +static const char dummy_rp_name[] = "sweet home localhost"; +static const char dummy_pin[] = "9}4gT:8d=A37Dh}U"; +static const char dummy_user_icon[] = "an icon"; +static const char dummy_user_name[] = "john smith"; +static const char dummy_user_nick[] = "jsmith"; + +static const uint8_t dummy_cdh[] = { + 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 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, +}; + +/* + * 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[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xb0, + 0x84, 0xeb, 0xec, 0x4d, 0x97, 0x72, 0x09, 0x00, + 0x91, 0x00, 0x03, 0x02, 0x05, 0x00, 0x02, 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, + 0x00, 0x91, 0x00, 0x03, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x69, 0xf2, 0x7d, + 0x37, 0x57, 0xda, 0x11, 0xba, 0x42, 0xde, 0x79, + 0xe4, 0xab, 0x8d, 0x73, 0x63, 0xee, 0x66, 0x9e, + 0x8a, 0x70, 0xa9, 0xb5, 0xf6, 0x38, 0x4f, 0x5b, + 0xdf, 0xe1, 0xa0, 0xa4, 0xff, 0x22, 0x58, 0x20, + 0x8a, 0xcb, 0x23, 0x2e, 0x93, 0xdb, 0xe0, 0xa4, + 0x00, 0x91, 0x00, 0x03, 0x00, 0xbb, 0xb5, 0x60, + 0x19, 0x18, 0x8b, 0x4d, 0xb8, 0x88, 0x6e, 0x13, + 0x75, 0xac, 0x00, 0x19, 0x27, 0x80, 0xcc, 0x63, + 0xc4, 0xbf, 0xfe, 0x4b, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x03, 0x90, 0x00, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0x10, 0x89, 0x77, 0x43, 0x3a, + 0x58, 0xa2, 0xc9, 0x98, 0x18, 0x1a, 0xb1, 0xcc, + 0x09, 0x6b, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 0x91, 0x00, 0x03, 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, 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, +}; + +/* + * 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[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x8e, + 0x80, 0xd0, 0xe2, 0x3b, 0x24, 0x93, 0xea, 0x00, + 0x00, 0x99, 0x01, 0x02, 0x01, 0x01, 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, 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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 +}; + +int LLVMFuzzerTestOneInput(const uint8_t *, size_t); +size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); + +static int +unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN +{ + uint8_t **pp = (void *)&ptr; + + if (unpack_byte(TAG_RK, pp, &len, &p->rk) < 0 || + unpack_byte(TAG_TYPE, pp, &len, &p->type) < 0 || + unpack_byte(TAG_U2F, pp, &len, &p->u2f) < 0 || + unpack_byte(TAG_UV, pp, &len, &p->uv) < 0 || + unpack_byte(TAG_EXCL_COUNT, pp, &len, &p->excl_count) < 0 || + unpack_string(TAG_PIN, pp, &len, p->pin) < 0 || + unpack_string(TAG_RP_ID, pp, &len, p->rp_id) < 0 || + unpack_string(TAG_RP_NAME, pp, &len, p->rp_name) < 0 || + unpack_string(TAG_USER_ICON, pp, &len, p->user_icon) < 0 || + unpack_string(TAG_USER_NAME, pp, &len, p->user_name) < 0 || + unpack_string(TAG_USER_NICK, pp, &len, p->user_nick) < 0 || + unpack_int(TAG_EXT, pp, &len, &p->ext) < 0 || + unpack_int(TAG_SEED, pp, &len, &p->seed) < 0 || + unpack_blob(TAG_CDH, pp, &len, &p->cdh) < 0 || + unpack_blob(TAG_USER_ID, pp, &len, &p->user_id) < 0 || + unpack_blob(TAG_WIRE_DATA, pp, &len, &p->wire_data) < 0 || + unpack_blob(TAG_EXCL_CRED, pp, &len, &p->excl_cred) < 0) + return (-1); + + return (0); +} + +static size_t +pack(uint8_t *ptr, size_t len, const struct param *p) +{ + const size_t max = len; + + if (pack_byte(TAG_RK, &ptr, &len, p->rk) < 0 || + pack_byte(TAG_TYPE, &ptr, &len, p->type) < 0 || + pack_byte(TAG_U2F, &ptr, &len, p->u2f) < 0 || + pack_byte(TAG_UV, &ptr, &len, p->uv) < 0 || + pack_byte(TAG_EXCL_COUNT, &ptr, &len, p->excl_count) < 0 || + pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 || + pack_string(TAG_RP_ID, &ptr, &len, p->rp_id) < 0 || + pack_string(TAG_RP_NAME, &ptr, &len, p->rp_name) < 0 || + pack_string(TAG_USER_ICON, &ptr, &len, p->user_icon) < 0 || + pack_string(TAG_USER_NAME, &ptr, &len, p->user_name) < 0 || + pack_string(TAG_USER_NICK, &ptr, &len, p->user_nick) < 0 || + pack_int(TAG_EXT, &ptr, &len, p->ext) < 0 || + pack_int(TAG_SEED, &ptr, &len, p->seed) < 0 || + pack_blob(TAG_CDH, &ptr, &len, &p->cdh) < 0 || + pack_blob(TAG_USER_ID, &ptr, &len, &p->user_id) < 0 || + pack_blob(TAG_WIRE_DATA, &ptr, &len, &p->wire_data) < 0 || + pack_blob(TAG_EXCL_CRED, &ptr, &len, &p->excl_cred) < 0) + return (0); + + return (max - len); +} + +static void +make_cred(fido_cred_t *cred, uint8_t u2f, int type, const struct blob *cdh, + const char *rp_id, const char *rp_name, 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, + struct blob *excl_cred) +{ + fido_dev_t *dev; + fido_dev_io_t io; + + io.open = dev_open; + io.close = dev_close; + io.read = dev_read; + io.write = dev_write; + + if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev, + &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) { + fido_dev_free(&dev); + return; + } + + if (u2f & 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); + fido_cred_set_extensions(cred, ext); + if (rk & 1) + fido_cred_set_rk(cred, FIDO_OPT_TRUE); + if (uv & 1) + fido_cred_set_uv(cred, FIDO_OPT_TRUE); + + fido_dev_make_cred(dev, cred, u2f & 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, 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) +{ + fido_cred_t *cred; + uint8_t flags; + + if ((cred = fido_cred_new()) == NULL) { + warnx("%s: fido_cred_new", __func__); + 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); + 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_extensions(cred, ext); + fido_cred_set_x509(cred, x5c_ptr, x5c_len); + fido_cred_set_sig(cred, sig_ptr, sig_len); + + 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); + + fido_cred_verify(cred); + fido_cred_verify_self(cred); + + 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_user_id_ptr(cred), fido_cred_user_id_len(cred)); + consume(fido_cred_user_name(cred), xstrlen(fido_cred_user_name(cred))); + consume(fido_cred_display_name(cred), + xstrlen(fido_cred_display_name(cred))); + + flags = fido_cred_flags(cred); + consume(&flags, sizeof(flags)); + type = fido_cred_type(cred); + consume(&type, sizeof(type)); + + fido_cred_free(&cred); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct param p; + fido_cred_t *cred = NULL; + int cose_alg = 0; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (0); + + srandom((unsigned int)p.seed); + + fido_init(0); + + if ((cred = fido_cred_new()) == NULL) + return (0); + + set_wire_data(p.wire_data.body, p.wire_data.len); + + switch (p.type & 3) { + case 0: + cose_alg = COSE_ES256; + break; + case 1: + cose_alg = COSE_RS256; + break; + default: + cose_alg = COSE_EDDSA; + break; + } + + make_cred(cred, p.u2f, 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), 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_free(&cred); + + return (0); +} + +static size_t +pack_dummy(uint8_t *ptr, size_t len) +{ + struct param dummy; + uint8_t blob[16384]; + 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); + + blob_len = pack(blob, sizeof(blob), &dummy); + assert(blob_len != 0); + + if (blob_len > len) { + memcpy(ptr, blob, len); + return (len); + } + + memcpy(ptr, blob, blob_len); + + return (blob_len); +} + +size_t +LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, + unsigned int seed) NO_MSAN +{ + struct param p; + uint8_t blob[16384]; + size_t blob_len; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (pack_dummy(data, maxsize)); + + mutate_byte(&p.rk); + mutate_byte(&p.type); + mutate_byte(&p.u2f); + mutate_byte(&p.uv); + mutate_byte(&p.excl_count); + + mutate_int(&p.ext); + p.seed = (int)seed; + + mutate_blob(&p.cdh); + mutate_blob(&p.user_id); + + if (p.u2f & 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); + 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); + + blob_len = pack(blob, sizeof(blob), &p); + + if (blob_len == 0 || blob_len > maxsize) + return (0); + + memcpy(data, blob, blob_len); + + return (blob_len); +} diff --git a/fuzz/fuzz_credman.c b/fuzz/fuzz_credman.c new file mode 100644 index 0000000..4359938 --- /dev/null +++ b/fuzz/fuzz_credman.c @@ -0,0 +1,667 @@ +/* + * 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 +#include +#include +#include +#include + +#include "mutator_aux.h" +#include "fido.h" +#include "fido/credman.h" + +#include "../openbsd-compat/openbsd-compat.h" + +#define TAG_META_WIRE_DATA 0x01 +#define TAG_RP_WIRE_DATA 0x02 +#define TAG_RK_WIRE_DATA 0x03 +#define TAG_DEL_WIRE_DATA 0x04 +#define TAG_CRED_ID 0x05 +#define TAG_PIN 0x06 +#define TAG_RP_ID 0x07 +#define TAG_SEED 0x08 + +/* 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; +}; + +/* Example parameters. */ +static const uint8_t dummy_cred_id[] = { + 0x4f, 0x72, 0x98, 0x42, 0x4a, 0xe1, 0x17, 0xa5, + 0x85, 0xa0, 0xef, 0x3b, 0x11, 0x24, 0x4a, 0x3d, +}; +static const char dummy_pin[] = "[n#899:~m"; +static const char dummy_rp_id[] = "yubico.com"; + +/* + * Collection of HID reports from an authenticator issued with a FIDO2 + * 'getCredsMetadata' credential management command. + */ +static const uint8_t dummy_meta_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xc5, + 0xb7, 0x89, 0xba, 0x8d, 0x5f, 0x94, 0x1b, 0x00, + 0x12, 0x00, 0x04, 0x02, 0x00, 0x04, 0x05, 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, + 0x00, 0x12, 0x00, 0x04, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x93, 0xc5, 0x64, + 0x71, 0xe9, 0xd1, 0xb8, 0xed, 0xf6, 0xd5, 0xf3, + 0xa7, 0xd5, 0x96, 0x70, 0xbb, 0xd5, 0x20, 0xa1, + 0xa3, 0xd3, 0x93, 0x4c, 0x5c, 0x20, 0x5c, 0x22, + 0xeb, 0xb0, 0x6a, 0x27, 0x59, 0x22, 0x58, 0x20, + 0x63, 0x02, 0x33, 0xa8, 0xed, 0x3c, 0xbc, 0xe9, + 0x00, 0x12, 0x00, 0x04, 0x00, 0xda, 0x44, 0xf5, + 0xed, 0xda, 0xe6, 0xa4, 0xad, 0x3f, 0x9e, 0xf8, + 0x50, 0x8d, 0x01, 0x47, 0x6c, 0x4e, 0x72, 0xa4, + 0x04, 0x13, 0xa8, 0x65, 0x97, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x04, 0x90, 0x00, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0x6f, 0x11, 0x96, 0x21, 0x92, + 0x52, 0xf1, 0x6b, 0xd4, 0x2c, 0xe3, 0xf8, 0xc9, + 0x8c, 0x47, 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, 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, +}; + +/* + * Collection of HID reports from an authenticator issued with a FIDO2 + * 'enumerateRPsBegin' credential management command. + */ +static const uint8_t dummy_rp_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x87, + 0xbf, 0xc6, 0x7f, 0x36, 0xf5, 0xe2, 0x49, 0x00, + 0x15, 0x00, 0x02, 0x02, 0x00, 0x04, 0x05, 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, + 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x12, 0xc1, 0x81, + 0x6b, 0x92, 0x6a, 0x56, 0x05, 0xfe, 0xdb, 0xab, + 0x90, 0x2f, 0x57, 0x0b, 0x3d, 0x85, 0x3e, 0x3f, + 0xbc, 0xe5, 0xd3, 0xb6, 0x86, 0xdf, 0x10, 0x43, + 0xc2, 0xaf, 0x87, 0x34, 0x0e, 0x22, 0x58, 0x20, + 0xd3, 0x0f, 0x7e, 0x5d, 0x10, 0x33, 0x57, 0x24, + 0x00, 0x15, 0x00, 0x02, 0x00, 0x6e, 0x90, 0x58, + 0x61, 0x2a, 0xd2, 0xc2, 0x1e, 0x08, 0xea, 0x91, + 0xcb, 0x44, 0x66, 0x73, 0x29, 0x92, 0x29, 0x59, + 0x91, 0xa3, 0x4d, 0x2c, 0xbb, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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, 0x02, 0x90, 0x00, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0x6d, 0x95, 0x0e, 0x73, 0x78, + 0x46, 0x13, 0x2e, 0x07, 0xbf, 0xeb, 0x61, 0x31, + 0x37, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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, 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, +}; + +/* + * Collection of HID reports from an authenticator issued with a FIDO2 + * 'enumerateCredentialsBegin' credential management command. + */ +static const uint8_t dummy_rk_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x35, + 0x3b, 0x34, 0xb9, 0xcb, 0xeb, 0x40, 0x55, 0x00, + 0x15, 0x00, 0x04, 0x02, 0x00, 0x04, 0x05, 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, + 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x12, 0xc1, 0x81, + 0x6b, 0x92, 0x6a, 0x56, 0x05, 0xfe, 0xdb, 0xab, + 0x90, 0x2f, 0x57, 0x0b, 0x3d, 0x85, 0x3e, 0x3f, + 0xbc, 0xe5, 0xd3, 0xb6, 0x86, 0xdf, 0x10, 0x43, + 0xc2, 0xaf, 0x87, 0x34, 0x0e, 0x22, 0x58, 0x20, + 0xd3, 0x0f, 0x7e, 0x5d, 0x10, 0x33, 0x57, 0x24, + 0x00, 0x15, 0x00, 0x04, 0x00, 0x6e, 0x90, 0x58, + 0x61, 0x2a, 0xd2, 0xc2, 0x1e, 0x08, 0xea, 0x91, + 0xcb, 0x44, 0x66, 0x73, 0x29, 0x92, 0x29, 0x59, + 0x91, 0xa3, 0x4d, 0x2c, 0xbb, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0x1b, 0xf0, 0x01, 0x0d, 0x32, + 0xee, 0x28, 0xa4, 0x5a, 0x7f, 0x56, 0x5b, 0x28, + 0xfd, 0x1f, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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, 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, +}; + +/* + * Collection of HID reports from an authenticator issued with a FIDO2 + * 'deleteCredential' credential management command. + */ +static const uint8_t dummy_del_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x8b, + 0xe1, 0xf0, 0x3a, 0x18, 0xa5, 0xda, 0x59, 0x00, + 0x15, 0x00, 0x05, 0x02, 0x00, 0x04, 0x05, 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, + 0x00, 0x15, 0x00, 0x05, 0x90, 0x00, 0x51, 0x00, + 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x12, 0xc1, 0x81, + 0x6b, 0x92, 0x6a, 0x56, 0x05, 0xfe, 0xdb, 0xab, + 0x90, 0x2f, 0x57, 0x0b, 0x3d, 0x85, 0x3e, 0x3f, + 0xbc, 0xe5, 0xd3, 0xb6, 0x86, 0xdf, 0x10, 0x43, + 0xc2, 0xaf, 0x87, 0x34, 0x0e, 0x22, 0x58, 0x20, + 0xd3, 0x0f, 0x7e, 0x5d, 0x10, 0x33, 0x57, 0x24, + 0x00, 0x15, 0x00, 0x05, 0x00, 0x6e, 0x90, 0x58, + 0x61, 0x2a, 0xd2, 0xc2, 0x1e, 0x08, 0xea, 0x91, + 0xcb, 0x44, 0x66, 0x73, 0x29, 0x92, 0x29, 0x59, + 0x91, 0xa3, 0x4d, 0x2c, 0xbb, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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, 0x05, 0x90, 0x00, 0x14, 0x00, + 0xa1, 0x02, 0x50, 0x33, 0xf1, 0x3b, 0xde, 0x1e, + 0xa5, 0xd1, 0xbf, 0xf6, 0x5d, 0x63, 0xb6, 0xfc, + 0xd2, 0x24, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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, 0x05, 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, +}; + +int LLVMFuzzerTestOneInput(const uint8_t *, size_t); +size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); + +static int +unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN +{ + uint8_t **pp = (void *)&ptr; + + if (unpack_string(TAG_PIN, pp, &len, p->pin) < 0 || + unpack_string(TAG_RP_ID, pp, &len, p->rp_id) < 0 || + unpack_blob(TAG_CRED_ID, pp, &len, &p->cred_id) < 0 || + unpack_blob(TAG_META_WIRE_DATA, pp, &len, &p->meta_wire_data) < 0 || + unpack_blob(TAG_RP_WIRE_DATA, pp, &len, &p->rp_wire_data) < 0 || + unpack_blob(TAG_RK_WIRE_DATA, pp, &len, &p->rk_wire_data) < 0 || + unpack_blob(TAG_DEL_WIRE_DATA, pp, &len, &p->del_wire_data) < 0 || + unpack_int(TAG_SEED, pp, &len, &p->seed) < 0) + return (-1); + + return (0); +} + +static size_t +pack(uint8_t *ptr, size_t len, const struct param *p) +{ + const size_t max = len; + + if (pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 || + pack_string(TAG_RP_ID, &ptr, &len, p->rp_id) < 0 || + pack_blob(TAG_CRED_ID, &ptr, &len, &p->cred_id) < 0 || + pack_blob(TAG_META_WIRE_DATA, &ptr, &len, &p->meta_wire_data) < 0 || + pack_blob(TAG_RP_WIRE_DATA, &ptr, &len, &p->rp_wire_data) < 0 || + pack_blob(TAG_RK_WIRE_DATA, &ptr, &len, &p->rk_wire_data) < 0 || + pack_blob(TAG_DEL_WIRE_DATA, &ptr, &len, &p->del_wire_data) < 0 || + pack_int(TAG_SEED, &ptr, &len, p->seed) < 0) + return (0); + + return (max - len); +} + +static fido_dev_t * +prepare_dev() +{ + fido_dev_t *dev; + fido_dev_io_t io; + + io.open = dev_open; + io.close = dev_close; + io.read = dev_read; + io.write = dev_write; + + if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev, + &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) { + fido_dev_free(&dev); + return (NULL); + } + + return (dev); +} + +static void +get_metadata(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(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(fido_credman_rp_id(rp, i), + xstrlen(fido_credman_rp_id(rp, i))); + consume(fido_credman_rp_name(rp, i), + xstrlen(fido_credman_rp_name(rp, i))); + } + + fido_credman_rp_free(&rp); + fido_dev_close(dev); + fido_dev_free(&dev); +} + +static void +get_rk_list(struct param *p) +{ + fido_dev_t *dev; + fido_credman_rk_t *rk; + const fido_cred_t *cred; + int type; + + 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; + } + type = fido_cred_type(cred); + consume(&type, sizeof(type)); + 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(fido_cred_user_name(cred), + xstrlen(fido_cred_user_name(cred))); + consume(fido_cred_display_name(cred), + xstrlen(fido_cred_display_name(cred))); + } + + fido_credman_rk_free(&rk); + fido_dev_close(dev); + fido_dev_free(&dev); +} + +static void +del_rk(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); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct param p; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (0); + + srandom((unsigned int)p.seed); + + fido_init(0); + + get_metadata(&p); + get_rp_list(&p); + get_rk_list(&p); + del_rk(&p); + + return (0); +} + +static size_t +pack_dummy(uint8_t *ptr, size_t len) +{ + struct param dummy; + uint8_t blob[32768]; + 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); + + blob_len = pack(blob, sizeof(blob), &dummy); + assert(blob_len != 0); + + if (blob_len > len) { + memcpy(ptr, blob, len); + return (len); + } + + memcpy(ptr, blob, blob_len); + + return (blob_len); +} + +size_t +LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, + unsigned int seed) NO_MSAN +{ + struct param p; + uint8_t blob[16384]; + size_t blob_len; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (pack_dummy(data, maxsize)); + + p.seed = (int)seed; + + mutate_blob(&p.cred_id); + mutate_blob(&p.meta_wire_data); + mutate_blob(&p.rp_wire_data); + mutate_blob(&p.rk_wire_data); + mutate_blob(&p.del_wire_data); + + mutate_string(p.pin); + mutate_string(p.rp_id); + + blob_len = pack(blob, sizeof(blob), &p); + + if (blob_len == 0 || blob_len > maxsize) + return (0); + + memcpy(data, blob, blob_len); + + return (blob_len); +} diff --git a/fuzz/fuzz_mgmt.c b/fuzz/fuzz_mgmt.c new file mode 100644 index 0000000..741b375 --- /dev/null +++ b/fuzz/fuzz_mgmt.c @@ -0,0 +1,529 @@ +/* + * 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 +#include +#include +#include +#include + +#include "mutator_aux.h" +#include "fido.h" + +#include "../openbsd-compat/openbsd-compat.h" + +#define TAG_PIN1 0x01 +#define TAG_PIN2 0x02 +#define TAG_RESET_WIRE_DATA 0x03 +#define TAG_INFO_WIRE_DATA 0x04 +#define TAG_SET_PIN_WIRE_DATA 0x05 +#define TAG_CHANGE_PIN_WIRE_DATA 0x06 +#define TAG_RETRY_WIRE_DATA 0x07 +#define TAG_SEED 0x08 + +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; + int seed; +}; + +/* Example parameters. */ +static const char dummy_pin1[] = "skepp cg0u3;Y.."; +static const char dummy_pin2[] = "bastilha 6rJrfQZI."; + +static const uint8_t dummy_reset_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x91, + 0xef, 0xbe, 0x74, 0x39, 0x1a, 0x1c, 0x4a, 0x00, + 0x22, 0x00, 0x01, 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, + 0x00, 0x22, 0x00, 0x01, 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, 0x22, 0x00, 0x01, 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, 0x22, 0x00, 0x01, 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, 0x22, 0x00, 0x01, 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, 0x22, 0x00, 0x01, 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, 0x22, 0x00, 0x01, 0xbb, 0x00, 0x01, 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, 0x22, 0x00, 0x01, 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 +}; + +static const uint8_t dummy_info_wire_data[] = { + 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, + 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 +}; + +static const uint8_t dummy_set_pin_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x59, + 0x50, 0x8c, 0x27, 0x14, 0x83, 0x43, 0xd5, 0x00, + 0x22, 0x00, 0x03, 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, + 0x00, 0x22, 0x00, 0x03, 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, 0x03, 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, + 0x00, 0x22, 0x00, 0x03, 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 +}; + +static const uint8_t dummy_change_pin_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x48, + 0xfd, 0xf9, 0xde, 0x28, 0x21, 0x99, 0xd5, 0x00, + 0x22, 0x00, 0x04, 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, + 0x00, 0x22, 0x00, 0x04, 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, 0x04, 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, + 0x00, 0x22, 0x00, 0x04, 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 +}; + +static const uint8_t dummy_retry_wire_data[] = { + 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x7f, + 0xaa, 0x73, 0x3e, 0x95, 0x98, 0xa8, 0x60, 0x00, + 0x22, 0x00, 0x05, 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, + 0x00, 0x22, 0x00, 0x05, 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 +}; + +int LLVMFuzzerTestOneInput(const uint8_t *, size_t); +size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); + +static int +unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN +{ + uint8_t **pp = (void *)&ptr; + + if (unpack_string(TAG_PIN1, pp, &len, p->pin1) < 0 || + unpack_string(TAG_PIN2, pp, &len, p->pin2) < 0 || + unpack_blob(TAG_RESET_WIRE_DATA, pp, &len, &p->reset_wire_data) < 0 || + unpack_blob(TAG_INFO_WIRE_DATA, pp, &len, &p->info_wire_data) < 0 || + unpack_blob(TAG_SET_PIN_WIRE_DATA, pp, &len, &p->set_pin_wire_data) < 0 || + unpack_blob(TAG_CHANGE_PIN_WIRE_DATA, pp, &len, &p->change_pin_wire_data) < 0 || + unpack_blob(TAG_RETRY_WIRE_DATA, pp, &len, &p->retry_wire_data) < 0 || + unpack_int(TAG_SEED, pp, &len, &p->seed) < 0) + return (-1); + + return (0); +} + +static size_t +pack(uint8_t *ptr, size_t len, const struct param *p) +{ + const size_t max = len; + + if (pack_string(TAG_PIN1, &ptr, &len, p->pin1) < 0 || + pack_string(TAG_PIN2, &ptr, &len, p->pin2) < 0 || + pack_blob(TAG_RESET_WIRE_DATA, &ptr, &len, &p->reset_wire_data) < 0 || + pack_blob(TAG_INFO_WIRE_DATA, &ptr, &len, &p->info_wire_data) < 0 || + pack_blob(TAG_SET_PIN_WIRE_DATA, &ptr, &len, &p->set_pin_wire_data) < 0 || + pack_blob(TAG_CHANGE_PIN_WIRE_DATA, &ptr, &len, &p->change_pin_wire_data) < 0 || + pack_blob(TAG_RETRY_WIRE_DATA, &ptr, &len, &p->retry_wire_data) < 0 || + pack_int(TAG_SEED, &ptr, &len, p->seed) < 0) + return (0); + + return (max - len); +} + +static fido_dev_t * +prepare_dev() +{ + fido_dev_t *dev; + fido_dev_io_t io; + + io.open = dev_open; + io.close = dev_close; + io.read = dev_read; + io.write = dev_write; + + if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev, + &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) { + fido_dev_free(&dev); + return (NULL); + } + + return (dev); +} + +static void +dev_reset(struct param *p) +{ + fido_dev_t *dev; + + set_wire_data(p->reset_wire_data.body, p->reset_wire_data.len); + + if ((dev = prepare_dev()) == NULL) { + return; + } + + fido_dev_reset(dev); + fido_dev_close(dev); + fido_dev_free(&dev); +} + +static void +dev_get_cbor_info(struct param *p) +{ + fido_dev_t *dev; + fido_cbor_info_t *ci; + uint64_t n; + uint8_t proto; + uint8_t major; + uint8_t minor; + uint8_t build; + uint8_t flags; + + set_wire_data(p->info_wire_data.body, p->info_wire_data.len); + + if ((dev = prepare_dev()) == 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) { + fido_dev_close(dev); + fido_dev_free(&dev); + return; + } + + fido_dev_get_cbor_info(dev, ci); + fido_dev_close(dev); + fido_dev_free(&dev); + + 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_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])); + } + + n = fido_cbor_info_maxmsgsiz(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)); + + fido_cbor_info_free(&ci); +} + +static void +dev_set_pin(struct param *p) +{ + fido_dev_t *dev; + + set_wire_data(p->set_pin_wire_data.body, p->set_pin_wire_data.len); + + if ((dev = prepare_dev()) == NULL) { + return; + } + + fido_dev_set_pin(dev, p->pin1, NULL); + fido_dev_close(dev); + fido_dev_free(&dev); +} + +static void +dev_change_pin(struct param *p) +{ + fido_dev_t *dev; + + set_wire_data(p->change_pin_wire_data.body, p->change_pin_wire_data.len); + + if ((dev = prepare_dev()) == 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(struct param *p) +{ + fido_dev_t *dev; + int n; + + set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len); + + if ((dev = prepare_dev()) == NULL) { + return; + } + + fido_dev_get_retry_count(dev, &n); + consume(&n, sizeof(n)); + fido_dev_close(dev); + fido_dev_free(&dev); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct param p; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (0); + + srandom((unsigned int)p.seed); + + fido_init(0); + + dev_reset(&p); + dev_get_cbor_info(&p); + dev_set_pin(&p); + dev_change_pin(&p); + dev_get_retry_count(&p); + + return (0); +} + +static size_t +pack_dummy(uint8_t *ptr, size_t len) +{ + struct param dummy; + uint8_t blob[16384]; + 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); + + 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); + + blob_len = pack(blob, sizeof(blob), &dummy); + assert(blob_len != 0); + + if (blob_len > len) { + memcpy(ptr, blob, len); + return (len); + } + + memcpy(ptr, blob, blob_len); + + return (blob_len); +} + +size_t +LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, + unsigned int seed) +{ + struct param p; + uint8_t blob[16384]; + size_t blob_len; + + memset(&p, 0, sizeof(p)); + + if (unpack(data, size, &p) < 0) + return (pack_dummy(data, maxsize)); + + p.seed = (int)seed; + + mutate_string(p.pin1); + mutate_string(p.pin2); + + 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); + + blob_len = pack(blob, sizeof(blob), &p); + + if (blob_len == 0 || blob_len > maxsize) + return (0); + + memcpy(data, blob, blob_len); + + return (blob_len); +} diff --git a/fuzz/harnesses/assert b/fuzz/harnesses/assert new file mode 100755 index 0000000..55cd889 --- /dev/null +++ b/fuzz/harnesses/assert @@ -0,0 +1,32 @@ +#!/bin/bash -u +# 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. + +T=assert + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../pubkey" | \ + tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/assert \ + ${T}/pubkey nodev 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/assert-rsa-h-p b/fuzz/harnesses/assert-rsa-h-p new file mode 100755 index 0000000..8eb9ea6 --- /dev/null +++ b/fuzz/harnesses/assert-rsa-h-p @@ -0,0 +1,33 @@ +#!/bin/bash -u +# 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. + +T=assert-rsa-h-p + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../pubkey ../hmac-salt" | \ + tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/assert \ + -t rsa -h ${T}/hmac-out -s ${T}/hmac-salt \ + -p ${T}/pubkey nodev 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/assert-u2f b/fuzz/harnesses/assert-u2f new file mode 100755 index 0000000..257d1d6 --- /dev/null +++ b/fuzz/harnesses/assert-u2f @@ -0,0 +1,32 @@ +#!/bin/bash -u +# 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. + +T=assert-u2f + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../cred_id ../pubkey" | \ + tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/assert -up \ + -a ${T}/cred_id ${T}/pubkey nodev 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/cred b/fuzz/harnesses/cred new file mode 100755 index 0000000..71ee845 --- /dev/null +++ b/fuzz/harnesses/cred @@ -0,0 +1,31 @@ +#!/bin/bash -u +# 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. + +T=cred + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/cred \ + -k ${T}/pubkey -i ${T}/cred_id nodev 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/cred-rsa-h-p b/fuzz/harnesses/cred-rsa-h-p new file mode 100755 index 0000000..bb14c23 --- /dev/null +++ b/fuzz/harnesses/cred-rsa-h-p @@ -0,0 +1,32 @@ +#!/bin/bash -u +# 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. + +T=cred-rsa-h-p + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/cred \ + -t rsa -r -k ${T}/pubkey -i ${T}/cred_id -h nodev \ + 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/cred-u2f b/fuzz/harnesses/cred-u2f new file mode 100755 index 0000000..3af4393 --- /dev/null +++ b/fuzz/harnesses/cred-u2f @@ -0,0 +1,31 @@ +#!/bin/bash -u +# 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. + +T=cred-u2f + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/cred \ + -k ${T}/pubkey -i ${T}/cred_id -u nodev 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/cred-u2f-exclude b/fuzz/harnesses/cred-u2f-exclude new file mode 100755 index 0000000..3777134 --- /dev/null +++ b/fuzz/harnesses/cred-u2f-exclude @@ -0,0 +1,33 @@ +#!/bin/bash -u +# 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. + +T=cred-u2f-exclude + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../excl_id" | \ + tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/cred \ + -k ${T}/pubkey -i ${T}/cred_id -e ${T}/excl_id \ + -u nodev 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fido2-assert-G b/fuzz/harnesses/fido2-assert-G new file mode 100755 index 0000000..6671449 --- /dev/null +++ b/fuzz/harnesses/fido2-assert-G @@ -0,0 +1,31 @@ +#!/bin/bash -u +# 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. + +T=fido2-assert-G + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/tools/fido2-assert \ + -G -i - nodev 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fido2-assert-V b/fuzz/harnesses/fido2-assert-V new file mode 100755 index 0000000..898cb0f --- /dev/null +++ b/fuzz/harnesses/fido2-assert-V @@ -0,0 +1,32 @@ +#!/bin/bash -u +# 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. + +T=fido2-assert-V + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../pubkey" | \ + tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/tools/fido2-assert -V \ + pubkey es256 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fido2-cred-M b/fuzz/harnesses/fido2-cred-M new file mode 100755 index 0000000..f82fbf7 --- /dev/null +++ b/fuzz/harnesses/fido2-cred-M @@ -0,0 +1,31 @@ +#!/bin/bash -u +# 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. + +T=fido2-cred-M + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/tools/fido2-cred -M \ + -q -i - nodev 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fido2-cred-V b/fuzz/harnesses/fido2-cred-V new file mode 100755 index 0000000..13a648f --- /dev/null +++ b/fuzz/harnesses/fido2-cred-V @@ -0,0 +1,31 @@ +#!/bin/bash -u +# 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. + +T=fido2-cred-V + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf- +} + +run() { + find ${T}/queue -type f | while read f; do + cat "${f}" | LD_PRELOAD=${PRELOAD} build/tools/fido2-cred -V \ + -o cred 2>/dev/null 1>&2 + done +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fuzz_assert b/fuzz/harnesses/fuzz_assert new file mode 100755 index 0000000..6a0baa9 --- /dev/null +++ b/fuzz/harnesses/fuzz_assert @@ -0,0 +1,29 @@ +#!/bin/bash -u +# 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. + +T=fuzz_assert + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf- +} + +run() { + build/fuzz/fuzz_assert -max_len=17408 -runs=1 ${T}/corpus \ + 2>/dev/null 1>&2 +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fuzz_bio b/fuzz/harnesses/fuzz_bio new file mode 100755 index 0000000..f4bab19 --- /dev/null +++ b/fuzz/harnesses/fuzz_bio @@ -0,0 +1,29 @@ +#!/bin/bash -u +# 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. + +T=fuzz_bio + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf- +} + +run() { + build/fuzz/fuzz_bio -max_len=17408 -runs=1 ${T}/corpus \ + 2>/dev/null 1>&2 +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fuzz_cred b/fuzz/harnesses/fuzz_cred new file mode 100755 index 0000000..8dfb168 --- /dev/null +++ b/fuzz/harnesses/fuzz_cred @@ -0,0 +1,28 @@ +#!/bin/bash -u +# 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. + +T=fuzz_cred + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf- +} + +run() { + build/fuzz/fuzz_cred -max_len=17408 -runs=1 ${T}/corpus 2>/dev/null 1>&2 +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fuzz_credman b/fuzz/harnesses/fuzz_credman new file mode 100755 index 0000000..7721a58 --- /dev/null +++ b/fuzz/harnesses/fuzz_credman @@ -0,0 +1,28 @@ +#!/bin/bash -u +# 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. + +T=fuzz_credman + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf- +} + +run() { + build/fuzz/fuzz_credman -max_len=17408 -runs=1 ${T}/corpus 2>/dev/null 1>&2 +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/harnesses/fuzz_mgmt b/fuzz/harnesses/fuzz_mgmt new file mode 100755 index 0000000..9224eea --- /dev/null +++ b/fuzz/harnesses/fuzz_mgmt @@ -0,0 +1,29 @@ +#!/bin/bash -u +# 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. + +T=fuzz_mgmt + +sync() { + mkdir ${T} + ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf- +} + +run() { + build/fuzz/fuzz_mgmt -max_len=17408 -runs=1 ${T}/corpus \ + 2>/dev/null 1>&2 +} + +case "$1" in +sync) + sync + ;; +run) + run + exit 0 + ;; +*) + echo unknown command "$1" + exit 1 +esac diff --git a/fuzz/mutator_aux.c b/fuzz/mutator_aux.c new file mode 100644 index 0000000..24aa716 --- /dev/null +++ b/fuzz/mutator_aux.c @@ -0,0 +1,314 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "mutator_aux.h" + +size_t LLVMFuzzerMutate(uint8_t *, size_t, size_t); + +static uint8_t *wire_data_ptr = NULL; +static size_t wire_data_len = 0; + +size_t +xstrlen(const char *s) +{ + if (s == NULL) + return (0); + + return (strlen(s)); +} + +void +consume(const void *body, size_t len) +{ + const volatile uint8_t *ptr = body; + volatile uint8_t x = 0; + + while (len--) + x ^= *ptr++; +} + +int +unpack_int(uint8_t t, uint8_t **ptr, size_t *len, int *v) NO_MSAN +{ + size_t l; + + if (*len < sizeof(t) || **ptr != t) + return (-1); + + *ptr += sizeof(t); + *len -= sizeof(t); + + if (*len < sizeof(l)) + return (-1); + + memcpy(&l, *ptr, sizeof(l)); + *ptr += sizeof(l); + *len -= sizeof(l); + + if (l != sizeof(*v) || *len < l) + return (-1); + + memcpy(v, *ptr, sizeof(*v)); + *ptr += sizeof(*v); + *len -= sizeof(*v); + + return (0); +} + +int +unpack_string(uint8_t t, uint8_t **ptr, size_t *len, char *v) NO_MSAN +{ + size_t l; + + if (*len < sizeof(t) || **ptr != t) + return (-1); + + *ptr += sizeof(t); + *len -= sizeof(t); + + if (*len < sizeof(l)) + return (-1); + + memcpy(&l, *ptr, sizeof(l)); + *ptr += sizeof(l); + *len -= sizeof(l); + + if (*len < l || l >= MAXSTR) + return (-1); + + memcpy(v, *ptr, l); + v[l] = '\0'; + + *ptr += l; + *len -= l; + + return (0); +} + +int +unpack_byte(uint8_t t, uint8_t **ptr, size_t *len, uint8_t *v) NO_MSAN +{ + size_t l; + + if (*len < sizeof(t) || **ptr != t) + return (-1); + + *ptr += sizeof(t); + *len -= sizeof(t); + + if (*len < sizeof(l)) + return (-1); + + memcpy(&l, *ptr, sizeof(l)); + *ptr += sizeof(l); + *len -= sizeof(l); + + if (l != sizeof(*v) || *len < l) + return (-1); + + memcpy(v, *ptr, sizeof(*v)); + *ptr += sizeof(*v); + *len -= sizeof(*v); + + return (0); +} + +int +unpack_blob(uint8_t t, uint8_t **ptr, size_t *len, struct blob *v) NO_MSAN +{ + size_t l; + + v->len = 0; + + if (*len < sizeof(t) || **ptr != t) + return (-1); + + *ptr += sizeof(t); + *len -= sizeof(t); + + if (*len < sizeof(l)) + return (-1); + + memcpy(&l, *ptr, sizeof(l)); + *ptr += sizeof(l); + *len -= sizeof(l); + + if (*len < l || l > sizeof(v->body)) + return (-1); + + memcpy(v->body, *ptr, l); + *ptr += l; + *len -= l; + + v->len = l; + + return (0); +} + +int +pack_int(uint8_t t, uint8_t **ptr, size_t *len, int v) NO_MSAN +{ + const size_t l = sizeof(v); + + if (*len < sizeof(t) + sizeof(l) + l) + return (-1); + + (*ptr)[0] = t; + memcpy(&(*ptr)[sizeof(t)], &l, sizeof(l)); + memcpy(&(*ptr)[sizeof(t) + sizeof(l)], &v, l); + + *ptr += sizeof(t) + sizeof(l) + l; + *len -= sizeof(t) + sizeof(l) + l; + + return (0); +} + +int +pack_string(uint8_t t, uint8_t **ptr, size_t *len, const char *v) NO_MSAN +{ + const size_t l = strlen(v); + + if (*len < sizeof(t) + sizeof(l) + l) + return (-1); + + (*ptr)[0] = t; + memcpy(&(*ptr)[sizeof(t)], &l, sizeof(l)); + memcpy(&(*ptr)[sizeof(t) + sizeof(l)], v, l); + + *ptr += sizeof(t) + sizeof(l) + l; + *len -= sizeof(t) + sizeof(l) + l; + + return (0); +} + +int +pack_byte(uint8_t t, uint8_t **ptr, size_t *len, uint8_t v) NO_MSAN +{ + const size_t l = sizeof(v); + + if (*len < sizeof(t) + sizeof(l) + l) + return (-1); + + (*ptr)[0] = t; + memcpy(&(*ptr)[sizeof(t)], &l, sizeof(l)); + memcpy(&(*ptr)[sizeof(t) + sizeof(l)], &v, l); + + *ptr += sizeof(t) + sizeof(l) + l; + *len -= sizeof(t) + sizeof(l) + l; + + return (0); +} + +int +pack_blob(uint8_t t, uint8_t **ptr, size_t *len, const struct blob *v) NO_MSAN +{ + const size_t l = v->len; + + if (*len < sizeof(t) + sizeof(l) + l) + return (-1); + + (*ptr)[0] = t; + memcpy(&(*ptr)[sizeof(t)], &l, sizeof(l)); + memcpy(&(*ptr)[sizeof(t) + sizeof(l)], v->body, l); + + *ptr += sizeof(t) + sizeof(l) + l; + *len -= sizeof(t) + sizeof(l) + l; + + return (0); +} + +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'; +} + +void * +dev_open(const char *path) +{ + (void)path; + + return ((void *)0xdeadbeef); +} + +void +dev_close(void *handle) +{ + assert(handle == (void *)0xdeadbeef); +} + +int +dev_read(void *handle, unsigned char *ptr, size_t len, int ms) +{ + size_t n; + + (void)ms; + + assert(handle == (void *)0xdeadbeef); + assert(len == 64); + + 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); +} + +int +dev_write(void *handle, const unsigned char *ptr, size_t len) +{ + assert(handle == (void *)0xdeadbeef); + assert(len == 64 + 1); + + consume(ptr, len); + + if (uniform_random(400) < 1) + return (-1); + + return ((int)len); +} + +void +set_wire_data(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 0000000..687f130 --- /dev/null +++ b/fuzz/mutator_aux.h @@ -0,0 +1,65 @@ +/* + * 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 + +/* + * As of LLVM 7.0.1, 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) +# define NO_MSAN __attribute__((no_sanitize("memory"))) +# define WITH_MSAN 1 +# endif +#endif + +#if !defined(WITH_MSAN) +# define NO_MSAN +#endif + +#define MAXSTR 1024 +#define MAXBLOB 3072 + +struct blob { + uint8_t body[MAXBLOB]; + size_t len; +}; + +size_t xstrlen(const char *); +void consume(const void *, size_t); + +int unpack_blob(uint8_t, uint8_t **, size_t *, struct blob *); +int unpack_byte(uint8_t, uint8_t **, size_t *, uint8_t *); +int unpack_int(uint8_t, uint8_t **, size_t *, int *); +int unpack_string(uint8_t, uint8_t **, size_t *, char *); + +int pack_blob(uint8_t, uint8_t **, size_t *, const struct blob *); +int pack_byte(uint8_t, uint8_t **, size_t *, uint8_t); +int pack_int(uint8_t, uint8_t **, size_t *, int); +int pack_string(uint8_t, uint8_t **, size_t *, const char *); + +void mutate_byte(uint8_t *); +void mutate_int(int *); +void mutate_blob(struct blob *); +void mutate_string(char *); + +void * dev_open(const char *); +void dev_close(void *); +void set_wire_data(uint8_t *, size_t); +int dev_read(void *, unsigned char *, size_t, int); +int dev_write(void *, const unsigned char *, size_t); + +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 0000000..efcb8c6 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..373acc5 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/report b/fuzz/report new file mode 100755 index 0000000..bebb0ca --- /dev/null +++ b/fuzz/report @@ -0,0 +1,80 @@ +#!/bin/bash -e +# 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. +# +# XXX This should really be a Makefile. + +T="" +#T+=" harnesses/assert" +#T+=" harnesses/assert-rsa-h-p" +#T+=" harnesses/assert-u2f" +#T+=" harnesses/cred" +#T+=" harnesses/cred-rsa-h-p" +#T+=" harnesses/cred-u2f" +#T+=" harnesses/cred-u2f-exclude" +#T+=" harnesses/fido2-assert-G" +#T+=" harnesses/fido2-assert-V" +#T+=" harnesses/fido2-cred-M" +#T+=" harnesses/fido2-cred-V" +T+=" harnesses/fuzz_assert" +T+=" harnesses/fuzz_bio" +T+=" harnesses/fuzz_cred" +T+=" harnesses/fuzz_credman" +T+=" harnesses/fuzz_mgmt" + +clean() { + echo cleaning + rm -rf obj + mkdir obj +} + +build() { + echo building + mkdir obj/build + (cd obj/build && cmake -DFUZZ=1 -DLIBFUZZER=1 -DCMAKE_C_COMPILER=clang \ + -DCOVERAGE=1 -DCMAKE_BUILD_TYPE=Debug ../../..) 2>/dev/null 1>&2 + make -C obj/build 2>/dev/null 1>&2 + cc -fPIC -D_GNU_SOURCE -shared -o obj/preload-fuzz.so preload-fuzz.c +} + +sync() { + if [ -n "${REMOTE}" ]; then + for t in ${T}; do + echo syncing ${t} + (cd obj && REMOTE="${REMOTE}" ../${t} sync) + done + else + tar -C obj -zxf corpus.tgz + fi +} + +run() { + export LLVM_PROFILE_FILE="profraw/%h-%p.profraw" + export PRELOAD=$(realpath obj/preload-fuzz.so) + + for t in ${T}; do + echo running ${t} + (cd obj && ../${t} run) + done +} + +merge() { + echo merging + (cd obj && \ + llvm-profdata merge -sparse profraw/*.profraw \ + -o libfido2.profdata && + llvm-cov show -format=html -tab-size=8 build/src/libfido2.so \ + -instr-profile=libfido2.profdata > report.html && + llvm-cov report -use-color=false build/src/libfido2.so \ + -instr-profile=libfido2.profdata > summary.txt && + llvm-cov report -use-color=false -show-functions \ + -instr-profile=libfido2.profdata build/src/libfido2.so \ + ../../src/*.[ch] > functions.txt) +} + +clean +build +sync +run +merge diff --git a/fuzz/summary.txt b/fuzz/summary.txt new file mode 100644 index 0000000..e494865 --- /dev/null +++ b/fuzz/summary.txt @@ -0,0 +1,39 @@ +Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +fuzz/uniform_random.c 7 1 85.71% 1 0 100.00% 23 1 95.65% +fuzz/wrap.c 4 0 100.00% 1 0 100.00% 7 0 100.00% +openbsd-compat/explicit_bzero.c 4 0 100.00% 1 0 100.00% 12 0 100.00% +openbsd-compat/recallocarray.c 41 7 82.93% 1 0 100.00% 49 7 85.71% +openbsd-compat/timingsafe_bcmp.c 4 0 100.00% 1 0 100.00% 8 0 100.00% +src/aes256.c 56 0 100.00% 2 0 100.00% 82 0 100.00% +src/assert.c 569 29 94.90% 53 1 98.11% 901 60 93.34% +src/authkey.c 45 0 100.00% 5 0 100.00% 75 0 100.00% +src/bio.c 422 21 95.02% 49 2 95.92% 661 25 96.22% +src/blob.c 39 1 97.44% 7 0 100.00% 73 4 94.52% +src/buf.c 8 1 87.50% 2 0 100.00% 20 1 95.00% +src/cbor.c 844 31 96.33% 51 1 98.04% 1319 47 96.44% +src/cred.c 532 35 93.42% 54 1 98.15% 850 55 93.53% +src/credman.c 381 18 95.28% 38 0 100.00% 589 15 97.45% +src/dev.c 131 22 83.21% 19 1 94.74% 183 30 83.61% +src/ecdh.c 68 0 100.00% 2 0 100.00% 104 0 100.00% +src/eddsa.c 54 4 92.59% 8 0 100.00% 79 2 97.47% +src/err.c 108 108 0.00% 1 1 0.00% 112 112 0.00% +src/es256.c 273 4 98.53% 16 0 100.00% 372 13 96.51% +src/hid.c 16 16 0.00% 8 8 0.00% 38 38 0.00% +src/hid_linux.c 166 166 0.00% 12 12 0.00% 287 287 0.00% +src/info.c 148 1 99.32% 31 0 100.00% 305 0 100.00% +src/io.c 113 6 94.69% 7 0 100.00% 201 13 93.53% +src/iso7816.c 18 1 94.44% 5 0 100.00% 47 0 100.00% +src/log.c 16 10 37.50% 3 1 66.67% 34 23 32.35% +src/pin.c 250 0 100.00% 16 0 100.00% 364 0 100.00% +src/reset.c 20 0 100.00% 3 0 100.00% 23 0 100.00% +src/rs256.c 102 6 94.12% 8 0 100.00% 140 9 93.57% +src/u2f.c 436 11 97.48% 13 0 100.00% 686 22 96.79% + +Files which contain no functions: +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 4875 499 89.76% 418 28 93.30% 7644 764 90.01% diff --git a/fuzz/uniform_random.c b/fuzz/uniform_random.c new file mode 100644 index 0000000..7711d69 --- /dev/null +++ b/fuzz/uniform_random.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2008, Damien Miller + * + * 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 +#include + +uint32_t uniform_random(uint32_t); + +/* + * 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)random(); + if (r >= min) + break; + } + + return r % upper_bound; +} diff --git a/fuzz/wrap.c b/fuzz/wrap.c new file mode 100644 index 0000000..8ff7ee7 --- /dev/null +++ b/fuzz/wrap.c @@ -0,0 +1,419 @@ +/* + * 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 +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "mutator_aux.h" + +/* + * 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 (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(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_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(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(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(EC_POINT *, + EC_POINT_new, + (const EC_GROUP *group), + NULL, + (group), + 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(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(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_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(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_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(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 0000000..3679f91 --- /dev/null +++ b/fuzz/wrapped.sym @@ -0,0 +1,47 @@ +BN_bin2bn +BN_CTX_get +BN_CTX_new +BN_new +calloc +cbor_array_handle +cbor_build_bytestring +cbor_build_string +cbor_build_uint8 +cbor_load +cbor_map_add +cbor_map_handle +cbor_new_definite_map +cbor_serialize_alloc +EC_KEY_get0_group +EC_KEY_new_by_curve_name +EC_POINT_new +EVP_CIPHER_CTX_new +EVP_CIPHER_CTX_set_padding +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_derive_init +EVP_PKEY_derive_set_peer +EVP_PKEY_get0_RSA +EVP_PKEY_new +EVP_PKEY_new_raw_public_key +EVP_sha256 +fido_tx +HMAC +HMAC_CTX_new +HMAC_Final +HMAC_Init_ex +HMAC_Update +malloc +RSA_set0_key +SHA256 +SHA256_Final +SHA256_Init +SHA256_Update +strdup +usleep diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt new file mode 100644 index 0000000..c903ab8 --- /dev/null +++ b/man/CMakeLists.txt @@ -0,0 +1,314 @@ +# 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) +message(STATUS "MANDOC_PATH: ${MANDOC_PATH}") + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_program(GZIP_PATH gzip) + message(STATUS "GZIP_PATH: ${GZIP_PATH}") +endif() + +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_get_assert.3 + fido_dev_info_manifest.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_clientdata_hash_len + fido_assert_new fido_assert_clientdata_hash_ptr + fido_assert_new fido_assert_count + 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_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_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_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_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_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_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_clientdata_hash_len + fido_cred_new fido_cred_clientdata_hash_ptr + 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_pubkey_len + fido_cred_new fido_cred_pubkey_ptr + fido_cred_new fido_cred_sig_len + fido_cred_new fido_cred_sig_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_cred_set_authdata fido_cred_set_authdata_raw + 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_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_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_major + fido_dev_open fido_dev_minor + fido_dev_open fido_dev_new + fido_dev_open fido_dev_protocol + fido_dev_set_pin fido_dev_get_retry_count + fido_dev_set_pin fido_dev_reset + 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 -c ${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_PREFIX}/share/doc/libfido2) + foreach(f ${MAN_SOURCES}) + string(REGEX REPLACE ".[13]" "" f ${f}) + install(FILES ${CMAKE_BINARY_DIR}/man/${f}.html + DESTINATION ${CMAKE_INSTALL_PREFIX}/share/doc/libfido2) + 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_PREFIX}/share/doc/libfido2) + 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_PREFIX}/share/man/man1") + elseif(${f} MATCHES ".3$") + install(FILES ${CMAKE_BINARY_DIR}/man/${f}.gz + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/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_PREFIX}/share/man/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_PREFIX}/man/man1") + elseif(${f} MATCHES ".3$") + install(FILES ${CMAKE_BINARY_DIR}/man/${f} + DESTINATION "${CMAKE_INSTALL_PREFIX}/man/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_PREFIX}/man/man3) + endforeach() +endif() diff --git a/man/NOTES b/man/NOTES new file mode 100644 index 0000000..4a461ff --- /dev/null +++ b/man/NOTES @@ -0,0 +1,4 @@ +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) diff --git a/man/dyc.css b/man/dyc.css new file mode 100644 index 0000000..1ff5b59 --- /dev/null +++ b/man/dyc.css @@ -0,0 +1,14 @@ + diff --git a/man/eddsa_pk_new.3 b/man/eddsa_pk_new.3 new file mode 100644 index 0000000..65bf9a9 --- /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 0000000..48eda0b --- /dev/null +++ b/man/es256_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 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. +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 0000000..67883e2 --- /dev/null +++ b/man/fido2-assert.1 @@ -0,0 +1,220 @@ +.\" 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 dhpruv +.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 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. +.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 a +.Em tty +is available, +.Nm +will use it to obtain the PIN. +Otherwise, +.Em stdin +is used. +If verifying an assertion, 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 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); +.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 0000000..d9bf7d2 --- /dev/null +++ b/man/fido2-cred.1 @@ -0,0 +1,238 @@ +.\" 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 dhqruv +.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 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 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. +.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). +.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. diff --git a/man/fido2-token.1 b/man/fido2-token.1 new file mode 100644 index 0000000..d5a5734 --- /dev/null +++ b/man/fido2-token.1 @@ -0,0 +1,158 @@ +.\" 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: September 13 2019 $ +.Dt FIDO2-TOKEN 1 +.Os +.Sh NAME +.Nm fido2-token +.Nd find and manage a FIDO 2 authenticator +.Sh SYNOPSIS +.Nm +.Op Fl CR +.Op Fl d +.Ar device +.Nm +.Fl D +.Op Fl de +.Fl i +.Ar id +.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 der +.Op Fl k Ar rp_id +.Op device +.Nm +.Fl S +.Op Fl de +.Op Fl i Ar template_id Fl n Ar template_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 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 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 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 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 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. diff --git a/man/fido_assert_allow_cred.3 b/man/fido_assert_allow_cred.3 new file mode 100644 index 0000000..bbe6e4d --- /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 0000000..0c2f92f --- /dev/null +++ b/man/fido_assert_new.3 @@ -0,0 +1,190 @@ +.\" 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_user_display_name , +.Nm fido_assert_user_icon , +.Nm fido_assert_user_name , +.Nm fido_assert_authdata_ptr , +.Nm fido_assert_clientdata_hash_ptr , +.Nm fido_assert_hmac_secret_ptr , +.Nm fido_assert_user_id_ptr , +.Nm fido_assert_sig_ptr , +.Nm fido_assert_authdata_len , +.Nm fido_assert_clientdata_hash_len , +.Nm fido_assert_hmac_secret_len , +.Nm fido_assert_user_id_len , +.Nm fido_assert_sig_len , +.Nm fido_assert_sigcount +.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_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_hmac_secret_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 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_hmac_secret_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 uint32_t +.Fn fido_assert_sigcount "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_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_hmac_secret_ptr , +and +.Fn fido_assert_sig_ptr +functions return pointers to the user ID, authenticator data, +hmac-secret, and signature attributes of statement +.Fa idx +in +.Fa assert . +The +.Fn fido_assert_user_id_len , +.Fn fido_assert_authdata_len , +.Fn fido_assert_hmac_secret_len , +and +.Fn fido_assert_sig_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 +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 +.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_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 diff --git a/man/fido_assert_set_authdata.3 b/man/fido_assert_set_authdata.3 new file mode 100644 index 0000000..29a86c1 --- /dev/null +++ b/man/fido_assert_set_authdata.3 @@ -0,0 +1,194 @@ +.\" 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_hash , +.Nm fido_assert_set_count , +.Nm fido_assert_set_extensions , +.Nm fido_assert_set_hmac_salt , +.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_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_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 +and +.Fn fido_assert_set_hmac_salt +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_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_HMAC_SECRET +extension is 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 . +.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 0000000..82e64e1 --- /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 0000000..bde1396 --- /dev/null +++ b/man/fido_bio_dev_get_info.3 @@ -0,0 +1,120 @@ +.\" 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. +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 . +.Pp +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 0000000..3db3e7a --- /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 0000000..c827333 --- /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 0000000..6140926 --- /dev/null +++ b/man/fido_bio_template.3 @@ -0,0 +1,169 @@ +.\" 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 **template_array_p" +.Ft size_t +.Fn fido_bio_template_array_count "const fido_bio_template_array_t *template_array" +.Ft const fido_bio_template_t * +.Fn fido_bio_template "const fido_bio_template_array_t *template_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 *template_array_p , +where +.Fa *template_array_p +must have been previously allocated by +.Fn fido_bio_template_array_new . +On return, +.Fa *template_array_p +is set to NULL. +Either +.Fa template_array_p +or +.Fa *template_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 template_array . +.Pp +The +.Fn fido_bio_template +function returns a pointer to the template at index +.Fa idx +in +.Fa template_array . +Please note that the first template in +.Fa template_array +has an +.Fa idx +(index) value of 0. +.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 0000000..d1e4c41 --- /dev/null +++ b/man/fido_cbor_info_new.3 @@ -0,0 +1,153 @@ +.\" 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_versions_ptr , +.Nm fido_cbor_info_options_name_ptr , +.Nm fido_cbor_info_options_value_ptr , +.Nm fido_cbor_info_aaguid_len , +.Nm fido_cbor_info_extensions_len , +.Nm fido_cbor_info_protocols_len , +.Nm fido_cbor_info_versions_len , +.Nm fido_cbor_info_options_len , +.Nm fido_cbor_info_maxmsgsiz +.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_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 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_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" +.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 , +and +.Fn fido_cbor_info_versions_ptr +functions return pointers to the AAGUID, supported extensions, +PIN protocol 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 , +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_maxmsgsiz +function returns the maximum message size 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_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 0000000..700d6af --- /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 0000000..47eeba8 --- /dev/null +++ b/man/fido_cred_new.3 @@ -0,0 +1,157 @@ +.\" 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_fmt , +.Nm fido_cred_authdata_ptr , +.Nm fido_cred_clientdata_hash_ptr , +.Nm fido_cred_id_ptr , +.Nm fido_cred_pubkey_ptr , +.Nm fido_cred_sig_ptr , +.Nm fido_cred_x5c_ptr , +.Nm fido_cred_authdata_len , +.Nm fido_cred_clientdata_hash_len , +.Nm fido_cred_id_len , +.Nm fido_cred_pubkey_len , +.Nm fido_cred_sig_len , +.Nm fido_cred_x5c_len +.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 const char * +.Fn fido_cred_fmt "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_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_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_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_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_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_x5c_len "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_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_authdata_ptr , +.Fn fido_cred_clientdata_hash_ptr , +.Fn fido_cred_id_ptr , +.Fn fido_cred_pubkey_ptr , +.Fn fido_cred_sig_ptr , +and +.Fn fido_cred_x5c_ptr +functions return pointers to the authenticator data, client data +hash, ID, public key, signature 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_clientdata_hash_len , +.Fn fido_cred_id_len , +.Fn fido_cred_pubkey_len , +and +.Fn fido_cred_sig_len . +.Pp +The authenticator data, x509 certificate, and signature parts of a +credential are typically passed to a FIDO 2 server for verification. +.Sh RETURN VALUES +The authenticator data returned by +.Fn fido_cred_authdata_ptr +is a CBOR-encoded byte string, as obtained from the authenticator. +.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_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_make_cred 3 diff --git a/man/fido_cred_set_authdata.3 b/man/fido_cred_set_authdata.3 new file mode 100644 index 0000000..8b087fa --- /dev/null +++ b/man/fido_cred_set_authdata.3 @@ -0,0 +1,240 @@ +.\" 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_clientdata_hash , +.Nm fido_cred_set_rp , +.Nm fido_cred_set_user , +.Nm fido_cred_set_extensions , +.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_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_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 , +and +.Fn fido_cred_set_clientdata_hash +functions set the authenticator data, attestation certificate, +signature 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 +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_HMAC_SECRET +extension is supported. +If +.Fa flags +is zero, the extensions of +.Fa cred +are cleared. +.Pp +The +.Fn fido_cred_set_rk +and +.Fn fido_cred_set_uv +functions set the +.Em rk +(resident key) +and +.Em uv +(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 format of +.Fa cred +to +.Fa fmt , +where +.Fa fmt +must be either +.Vt "packed" +(the format used in FIDO 2) +or +.Vt "fido-u2f" +(the format used by U2F). +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 0000000..c75b9a1 --- /dev/null +++ b/man/fido_cred_verify.3 @@ -0,0 +1,64 @@ +.\" 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 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 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 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 +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 0000000..16f0192 --- /dev/null +++ b/man/fido_credman_metadata_new.3 @@ -0,0 +1,299 @@ +.\" 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: 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_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_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, and removed. +Please note that not all 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_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_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 diff --git a/man/fido_dev_get_assert.3 b/man/fido_dev_get_assert.3 new file mode 100644 index 0000000..2e33fc5 --- /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_info_manifest.3 b/man/fido_dev_info_manifest.3 new file mode 100644 index 0000000..22519e2 --- /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_make_cred.3 b/man/fido_dev_make_cred.3 new file mode 100644 index 0000000..ccaf788 --- /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 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 0000000..53e3a12 --- /dev/null +++ b/man/fido_dev_open.3 @@ -0,0 +1,159 @@ +.\" 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_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 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 . +.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_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 diff --git a/man/fido_dev_set_io_functions.3 b/man/fido_dev_set_io_functions.3 new file mode 100644 index 0000000..adc4a9e --- /dev/null +++ b/man/fido_dev_set_io_functions.3 @@ -0,0 +1,95 @@ +.\" 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 +.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; +.Ed +.Ft int +.Fn fido_dev_set_io_functions "fido_dev_t *dev" "const fido_dev_io_t *io" +.Sh DESCRIPTION +The +.Nm +interface defines the I/O handlers used to talk to +.Fa dev . +Its usage is optional. +By default, +.Em libfido2 +will use the operating system's native HID interface to talk to +a FIDO device. +.Pp +A +.Vt fido_dev_io_open_t +function is expected to return a non-NULL opaque pointer on success, +and NULL on error. +The returned opaque pointer is never dereferenced by +.Em libfido2 . +.Pp +A +.Vt fido_dev_io_close_t +function receives the opaque handle obtained from +.Vt fido_dev_io_open_t . +It is not expected to be idempotent. +.Pp +A +.Vt fido_dev_io_read_t +function reads from +.Fa dev . +The first parameter taken is the opaque handle obtained from +.Vt fido_dev_io_open_t . +The read buffer is pointed to by the second parameter, and the +third parameter holds its size. +Finally, the last argument passed to +.Vt fido_dev_io_read_t +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_io_read_t +may block indefinitely. +The number of bytes read is returned. +On error, -1 is returned. +.Pp +Conversely, a +.Vt fido_dev_io_write_t +function writes to +.Fa dev . +The first parameter taken is the opaque handle returned by +.Vt fido_dev_io_open_t . +The write buffer is pointed to by the second parameter, and the +third parameter holds its size. +A +.Vt fido_dev_io_write_t +function may block. +The number of bytes written is returned. +On error, -1 is returned. +.Pp +No references to +.Fa io +are held by +.Fn fido_dev_set_io_functions . +.Sh RETURN VALUES +On success, +.Fn fido_dev_set_io_functions +returns +.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 0000000..94f841b --- /dev/null +++ b/man/fido_dev_set_pin.3 @@ -0,0 +1,88 @@ +.\" 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_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_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_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 , +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 , +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 0000000..7f38948 --- /dev/null +++ b/man/fido_init.3 @@ -0,0 +1,40 @@ +.\" 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. +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. +.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 0000000..05c86b9 --- /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 0000000..4ad0ebe --- /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 0000000..8c223fa --- /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 0000000..52b44f7 --- /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 +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include + +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 0000000..903bfc3 --- /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 +#endif +#include + +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/diff.sh b/openbsd-compat/diff.sh new file mode 100755 index 0000000..f21e7d8 --- /dev/null +++ b/openbsd-compat/diff.sh @@ -0,0 +1,24 @@ +#!/bin/bash -u + +# 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. + +OPENSSH=$(realpath ../../openssh) +LIBRESSL=$(realpath ../../libressl-2.8.3) +[[ ! -d "${OPENSSH}" || ! -d "${LIBRESSL}" ]] && exit 1 + +diff -pu bsd-getpagesize.c ${OPENSSH}/openbsd-compat/bsd-getpagesize.c +diff -pu err.h ${LIBRESSL}/include/compat/err.h +diff -pu explicit_bzero.c ${OPENSSH}/openbsd-compat/explicit_bzero.c +diff -pu explicit_bzero_win32.c ${LIBRESSL}/crypto/compat/explicit_bzero_win.c +diff -pu getopt.h ${OPENSSH}/openbsd-compat/getopt.h +diff -pu getopt_long.c ${OPENSSH}/openbsd-compat/getopt_long.c +diff -pu posix_win.c ${LIBRESSL}/crypto/compat/posix_win.c +diff -pu readpassphrase.c ${OPENSSH}/openbsd-compat/readpassphrase.c +diff -pu readpassphrase.h ${OPENSSH}/openbsd-compat/readpassphrase.h +diff -pu recallocarray.c ${OPENSSH}/openbsd-compat/recallocarray.c +diff -pu strlcat.c ${OPENSSH}/openbsd-compat/strlcat.c +diff -pu strlcpy.c ${OPENSSH}/openbsd-compat/strlcpy.c +diff -pu timingsafe_bcmp.c ${OPENSSH}/openbsd-compat/timingsafe_bcmp.c +diff -pu types.h ${LIBRESSL}/include/compat/sys/types.h diff --git a/openbsd-compat/err.h b/openbsd-compat/err.h new file mode 100644 index 0000000..394c7bb --- /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 +#include +#include +#include +#include + +#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 0000000..ac64e69 --- /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 + +/* + * 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 0000000..8017aff --- /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 +#include + +void +explicit_bzero(void *buf, size_t len) +{ + SecureZeroMemory(buf, len); +} + +#endif /* !defined(HAVE_EXPLICIT_BZERO) && defined(_WIN32) */ diff --git a/openbsd-compat/getopt.h b/openbsd-compat/getopt.h new file mode 100644 index 0000000..8eb1244 --- /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 0000000..dabbb46 --- /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 + * + * 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 +#include +#endif +#include +#include +#include +#include + +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/openbsd-compat.h b/openbsd-compat/openbsd-compat.h new file mode 100644 index 0000000..d1d8652 --- /dev/null +++ b/openbsd-compat/openbsd-compat.h @@ -0,0 +1,87 @@ +/* + * 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 +#endif + +#if defined(__APPLE__) && !defined(HAVE_ENDIAN_H) +#include +#define be16toh(x) OSSwapBigToHostInt16((x)) +#define be32toh(x) OSSwapBigToHostInt32((x)) +#endif /* __APPLE__ && !HAVE_ENDIAN_H */ + +#if defined(_WIN32) && !defined(HAVE_ENDIAN_H) +#include +#if !defined(_MSC_VER) +#include +#endif +#define be16toh(x) ntohs((x)) +#define be32toh(x) ntohl((x)) +#endif /* _WIN32 && !HAVE_ENDIAN_H */ + +#include + +#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_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 +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#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 +#endif + +#if !defined(HAVE_GETOPT) +#include "getopt.h" +#else +#include +#endif + +#if !defined(HAVE_GETLINE) +#include +ssize_t getline(char **, size_t *, FILE *); +#endif + +#endif /* !_OPENBSD_COMPAT_H */ diff --git a/openbsd-compat/posix_win.c b/openbsd-compat/posix_win.c new file mode 100644 index 0000000..eac67c2 --- /dev/null +++ b/openbsd-compat/posix_win.c @@ -0,0 +1,61 @@ +/* + * Public domain + * + * File IO compatibility shims + * Brent Cook + */ + +#define NO_REDEF_POSIX_FUNCTIONS + +#include + +#include +#include + +#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 0000000..a1e0888 --- /dev/null +++ b/openbsd-compat/posix_win.h @@ -0,0 +1,47 @@ +/* + * Public domain + * + * BSD socket emulation code for Winsock2 + * Brent Cook + */ + +#ifndef _COMPAT_POSIX_WIN_H +#define _COMPAT_POSIX_WIN_H + +#ifdef _WIN32 + +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..dfb3065 --- /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 + * + * 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 +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#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 0000000..0c4a59e --- /dev/null +++ b/openbsd-compat/readpassphrase.h @@ -0,0 +1,42 @@ +/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ + +/* + * Copyright (c) 2000, 2002 Todd C. Miller + * + * 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 + +#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 0000000..f3079e4 --- /dev/null +++ b/openbsd-compat/readpassphrase_win32.c @@ -0,0 +1,131 @@ +/* +* Author: Manoj Ampalam +* +* Author: Bryan Berns +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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()) _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()) _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 0000000..5d2f8d9 --- /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 + * + * 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 +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#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 0000000..44470de --- /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 + * + * 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 +#include + +/* + * 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 0000000..a8b18ea --- /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 + * + * 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 +#include + +/* + * 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/timingsafe_bcmp.c b/openbsd-compat/timingsafe_bcmp.c new file mode 100644 index 0000000..3f7b9e5 --- /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 0000000..cc1da66 --- /dev/null +++ b/openbsd-compat/types.h @@ -0,0 +1,71 @@ +/* + * 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 + +#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 uint32_t in_addr_t; +typedef uint32_t mode_t; +typedef uint32_t uid_t; + +#include +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 0000000..b8fea64 --- /dev/null +++ b/regress/CMakeLists.txt @@ -0,0 +1,18 @@ +# 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. + +# cred +add_executable(regress_cred cred.c) +target_link_libraries(regress_cred fido2_shared) +add_custom_command(TARGET regress_cred POST_BUILD COMMAND regress_cred) + +# assert +add_executable(regress_assert assert.c) +target_link_libraries(regress_assert fido2_shared) +add_custom_command(TARGET regress_assert POST_BUILD COMMAND regress_assert) + +# dev +add_executable(regress_dev dev.c) +target_link_libraries(regress_dev fido2_shared) +add_custom_command(TARGET regress_dev POST_BUILD COMMAND regress_dev) diff --git a/regress/assert.c b/regress/assert.c new file mode 100644 index 0000000..ebf0652 --- /dev/null +++ b/regress/assert.c @@ -0,0 +1,511 @@ +/* + * 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 +#include +#include +#include +#include + +#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 void +empty_assert(fido_dev_t *d, fido_assert_t *a, int idx) +{ + es256_pk_t *es256; + rs256_pk_t *rs256; + + 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(); + + 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, COSE_ES256, idx, + NULL) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_assert_verify(a, COSE_ES256, idx, + es256) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_assert_verify(a, COSE_RS256, idx, + rs256) == 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, COSE_ES256, idx, + NULL) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_assert_verify(a, COSE_ES256, idx, + es256) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_assert_verify(a, COSE_RS256, idx, + rs256) == FIDO_ERR_INVALID_ARGUMENT); + + free_es256_pk(es256); + free_rs256_pk(rs256); +} + +static void +empty_assert_tests(void) +{ + fido_assert_t *a; + fido_dev_t *d; + fido_dev_io_t io_f; + int i; + + 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_assert(d, a, 10); + free_assert(a); + free_dev(d); +} + +static void +valid_assert(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_assert_set_rp(a, "localhost") == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_OK); + free_assert(a); + free_es256_pk(pk); +} + +static void +no_cdh(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_rp(a, "localhost") == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, + pk) == FIDO_ERR_INVALID_ARGUMENT); + free_assert(a); + free_es256_pk(pk); +} + +static void +no_rp(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, + pk) == FIDO_ERR_INVALID_ARGUMENT); + free_assert(a); + free_es256_pk(pk); +} + +static void +no_authdata(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_assert_set_rp(a, "localhost") == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, + pk) == FIDO_ERR_INVALID_ARGUMENT); + free_assert(a); + free_es256_pk(pk); +} + +static void +no_sig(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_assert_set_rp(a, "localhost") == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, + pk) == FIDO_ERR_INVALID_ARGUMENT); + free_assert(a); + free_es256_pk(pk); +} + +static void +junk_cdh(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + unsigned char *junk; + + junk = malloc(sizeof(cdh)); + assert(junk != NULL); + memcpy(junk, cdh, sizeof(cdh)); + junk[0] = ~junk[0]; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_clientdata_hash(a, junk, sizeof(cdh)) == FIDO_OK); + assert(fido_assert_set_rp(a, "localhost") == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_ERR_INVALID_SIG); + free_assert(a); + free_es256_pk(pk); + free(junk); +} + +static void +junk_rp(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_assert_set_rp(a, "potato") == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, + pk) == FIDO_ERR_INVALID_PARAM); + free_assert(a); + free_es256_pk(pk); +} + +static void +junk_authdata(void) +{ + fido_assert_t *a; + unsigned char *junk; + + junk = malloc(sizeof(authdata)); + assert(junk != NULL); + memcpy(junk, authdata, sizeof(authdata)); + junk[0] = ~junk[0]; + + a = alloc_assert(); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, junk, + sizeof(authdata)) == FIDO_ERR_INVALID_ARGUMENT); + free_assert(a); + free(junk); +} + +static void +junk_sig(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + unsigned char *junk; + + junk = malloc(sizeof(sig)); + assert(junk != NULL); + memcpy(junk, sig, sizeof(sig)); + junk[0] = ~junk[0]; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_assert_set_rp(a, "localhost") == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_sig(a, 0, junk, sizeof(sig)) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_ERR_INVALID_SIG); + free_assert(a); + free_es256_pk(pk); + free(junk); +} + +static void +wrong_options(void) +{ + fido_assert_t *a; + es256_pk_t *pk; + + a = alloc_assert(); + pk = alloc_es256_pk(); + assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK); + assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_assert_set_rp(a, "localhost") == FIDO_OK); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_set_up(a, FIDO_OPT_TRUE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, + pk) == FIDO_ERR_INVALID_PARAM); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_TRUE) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, + pk) == FIDO_ERR_INVALID_PARAM); + assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_OK); + free_assert(a); + free_es256_pk(pk); +} + +/* cbor_serialize_alloc misuse */ +static void +bad_cbor_serialize(void) +{ + fido_assert_t *a; + + a = alloc_assert(); + assert(fido_assert_set_count(a, 1) == FIDO_OK); + assert(fido_assert_set_authdata(a, 0, authdata, + sizeof(authdata)) == FIDO_OK); + assert(fido_assert_authdata_len(a, 0) == sizeof(authdata)); + free_assert(a); +} + +int +main(void) +{ + fido_init(0); + + empty_assert_tests(); + valid_assert(); + no_cdh(); + no_rp(); + no_authdata(); + no_sig(); + junk_cdh(); + junk_rp(); + junk_authdata(); + junk_sig(); + wrong_options(); + bad_cbor_serialize(); + + exit(0); +} diff --git a/regress/cred.c b/regress/cred.c new file mode 100644 index 0000000..4998649 --- /dev/null +++ b/regress/cred.c @@ -0,0 +1,816 @@ +/* + * 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 +#include +#include + +#define FAKE_DEV_HANDLE ((void *)0xdeadbeef) + +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 authdata[198] = { + 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, 0x41, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, + 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, + 0x40, 0x53, 0xfb, 0xdf, 0xaa, 0xce, 0x63, 0xde, + 0xc5, 0xfe, 0x47, 0xe6, 0x52, 0xeb, 0xf3, 0x5d, + 0x53, 0xa8, 0xbf, 0x9d, 0xd6, 0x09, 0x6b, 0x5e, + 0x7f, 0xe0, 0x0d, 0x51, 0x30, 0x85, 0x6a, 0xda, + 0x68, 0x70, 0x85, 0xb0, 0xdb, 0x08, 0x0b, 0x83, + 0x2c, 0xef, 0x44, 0xe2, 0x36, 0x88, 0xee, 0x76, + 0x90, 0x6e, 0x7b, 0x50, 0x3e, 0x9a, 0xa0, 0xd6, + 0x3c, 0x34, 0xe3, 0x83, 0xe7, 0xd1, 0xbd, 0x9f, + 0x25, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, + 0x21, 0x58, 0x20, 0x17, 0x5b, 0x27, 0xa6, 0x56, + 0xb2, 0x26, 0x0c, 0x26, 0x0c, 0x55, 0x42, 0x78, + 0x17, 0x5d, 0x4c, 0xf8, 0xa2, 0xfd, 0x1b, 0xb9, + 0x54, 0xdf, 0xd5, 0xeb, 0xbf, 0x22, 0x64, 0xf5, + 0x21, 0x9a, 0xc6, 0x22, 0x58, 0x20, 0x87, 0x5f, + 0x90, 0xe6, 0xfd, 0x71, 0x27, 0x9f, 0xeb, 0xe3, + 0x03, 0x44, 0xbc, 0x8d, 0x49, 0xc6, 0x1c, 0x31, + 0x3b, 0x72, 0xae, 0xd4, 0x53, 0xb1, 0xfe, 0x5d, + 0xe1, 0x30, 0xfc, 0x2b, 0x1e, 0xd2, +}; + +static const unsigned char authdata_dupkeys[200] = { + 0x58, 0xc6, 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, 0x41, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, + 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, + 0x40, 0x53, 0xfb, 0xdf, 0xaa, 0xce, 0x63, 0xde, + 0xc5, 0xfe, 0x47, 0xe6, 0x52, 0xeb, 0xf3, 0x5d, + 0x53, 0xa8, 0xbf, 0x9d, 0xd6, 0x09, 0x6b, 0x5e, + 0x7f, 0xe0, 0x0d, 0x51, 0x30, 0x85, 0x6a, 0xda, + 0x68, 0x70, 0x85, 0xb0, 0xdb, 0x08, 0x0b, 0x83, + 0x2c, 0xef, 0x44, 0xe2, 0x36, 0x88, 0xee, 0x76, + 0x90, 0x6e, 0x7b, 0x50, 0x3e, 0x9a, 0xa0, 0xd6, + 0x3c, 0x34, 0xe3, 0x83, 0xe7, 0xd1, 0xbd, 0x9f, + 0x25, 0xa6, 0x01, 0x02, 0x01, 0x02, 0x03, 0x26, + 0x20, 0x01, 0x21, 0x58, 0x20, 0x17, 0x5b, 0x27, + 0xa6, 0x56, 0xb2, 0x26, 0x0c, 0x26, 0x0c, 0x55, + 0x42, 0x78, 0x17, 0x5d, 0x4c, 0xf8, 0xa2, 0xfd, + 0x1b, 0xb9, 0x54, 0xdf, 0xd5, 0xeb, 0xbf, 0x22, + 0x64, 0xf5, 0x21, 0x9a, 0xc6, 0x22, 0x58, 0x20, + 0x87, 0x5f, 0x90, 0xe6, 0xfd, 0x71, 0x27, 0x9f, + 0xeb, 0xe3, 0x03, 0x44, 0xbc, 0x8d, 0x49, 0xc6, + 0x1c, 0x31, 0x3b, 0x72, 0xae, 0xd4, 0x53, 0xb1, + 0xfe, 0x5d, 0xe1, 0x30, 0xfc, 0x2b, 0x1e, 0xd2, +}; + +static const unsigned char authdata_unsorted_keys[198] = { + 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, 0x41, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, + 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, + 0x40, 0x53, 0xfb, 0xdf, 0xaa, 0xce, 0x63, 0xde, + 0xc5, 0xfe, 0x47, 0xe6, 0x52, 0xeb, 0xf3, 0x5d, + 0x53, 0xa8, 0xbf, 0x9d, 0xd6, 0x09, 0x6b, 0x5e, + 0x7f, 0xe0, 0x0d, 0x51, 0x30, 0x85, 0x6a, 0xda, + 0x68, 0x70, 0x85, 0xb0, 0xdb, 0x08, 0x0b, 0x83, + 0x2c, 0xef, 0x44, 0xe2, 0x36, 0x88, 0xee, 0x76, + 0x90, 0x6e, 0x7b, 0x50, 0x3e, 0x9a, 0xa0, 0xd6, + 0x3c, 0x34, 0xe3, 0x83, 0xe7, 0xd1, 0xbd, 0x9f, + 0x25, 0xa5, 0x03, 0x26, 0x01, 0x02, 0x20, 0x01, + 0x21, 0x58, 0x20, 0x17, 0x5b, 0x27, 0xa6, 0x56, + 0xb2, 0x26, 0x0c, 0x26, 0x0c, 0x55, 0x42, 0x78, + 0x17, 0x5d, 0x4c, 0xf8, 0xa2, 0xfd, 0x1b, 0xb9, + 0x54, 0xdf, 0xd5, 0xeb, 0xbf, 0x22, 0x64, 0xf5, + 0x21, 0x9a, 0xc6, 0x22, 0x58, 0x20, 0x87, 0x5f, + 0x90, 0xe6, 0xfd, 0x71, 0x27, 0x9f, 0xeb, 0xe3, + 0x03, 0x44, 0xbc, 0x8d, 0x49, 0xc6, 0x1c, 0x31, + 0x3b, 0x72, 0xae, 0xd4, 0x53, 0xb1, 0xfe, 0x5d, + 0xe1, 0x30, 0xfc, 0x2b, 0x1e, 0xd2, +}; + +static const unsigned char x509[742] = { + 0x30, 0x82, 0x02, 0xe2, 0x30, 0x81, 0xcb, 0x02, + 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x59, 0x75, + 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, + 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x35, + 0x31, 0x35, 0x31, 0x32, 0x35, 0x38, 0x35, 0x34, + 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x36, 0x31, + 0x34, 0x31, 0x32, 0x35, 0x38, 0x35, 0x34, 0x5a, + 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x12, 0x59, 0x75, 0x62, + 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20, + 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x45, 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, 0xdb, 0x0a, 0xdb, 0xf5, 0x21, 0xc7, + 0x5c, 0xce, 0x63, 0xdc, 0xa6, 0xe1, 0xe8, 0x25, + 0x06, 0x0d, 0x94, 0xe6, 0x27, 0x54, 0x19, 0x4f, + 0x9d, 0x24, 0xaf, 0x26, 0x1a, 0xbe, 0xad, 0x99, + 0x44, 0x1f, 0x95, 0xa3, 0x71, 0x91, 0x0a, 0x3a, + 0x20, 0xe7, 0x3e, 0x91, 0x5e, 0x13, 0xe8, 0xbe, + 0x38, 0x05, 0x7a, 0xd5, 0x7a, 0xa3, 0x7e, 0x76, + 0x90, 0x8f, 0xaf, 0xe2, 0x8a, 0x94, 0xb6, 0x30, + 0xeb, 0x9d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x95, 0x40, + 0x6b, 0x50, 0x61, 0x7d, 0xad, 0x84, 0xa3, 0xb4, + 0xeb, 0x88, 0x0f, 0xe3, 0x30, 0x0f, 0x2d, 0xa2, + 0x0a, 0x00, 0xd9, 0x25, 0x04, 0xee, 0x72, 0xfa, + 0x67, 0xdf, 0x58, 0x51, 0x0f, 0x0b, 0x47, 0x02, + 0x9c, 0x3e, 0x41, 0x29, 0x4a, 0x93, 0xac, 0x29, + 0x85, 0x89, 0x2d, 0xa4, 0x7a, 0x81, 0x32, 0x28, + 0x57, 0x71, 0x01, 0xef, 0xa8, 0x42, 0x88, 0x16, + 0x96, 0x37, 0x91, 0xd5, 0xdf, 0xe0, 0x8f, 0xc9, + 0x3c, 0x8d, 0xb0, 0xcd, 0x89, 0x70, 0x82, 0xec, + 0x79, 0xd3, 0xc6, 0x78, 0x73, 0x29, 0x32, 0xe5, + 0xab, 0x6c, 0xbd, 0x56, 0x9f, 0xd5, 0x45, 0x91, + 0xce, 0xc1, 0xdd, 0x8d, 0x64, 0xdc, 0xe9, 0x9c, + 0x1f, 0x5e, 0x3c, 0xd2, 0xaf, 0x51, 0xa5, 0x82, + 0x18, 0xaf, 0xe0, 0x37, 0xe7, 0x32, 0x9e, 0x76, + 0x05, 0x77, 0x02, 0x7b, 0xe6, 0x24, 0xa0, 0x31, + 0x56, 0x1b, 0xfd, 0x19, 0xc5, 0x71, 0xd3, 0xf0, + 0x9e, 0xc0, 0x73, 0x05, 0x4e, 0xbc, 0x85, 0xb8, + 0x53, 0x9e, 0xef, 0xc5, 0xbc, 0x9c, 0x56, 0xa3, + 0xba, 0xd9, 0x27, 0x6a, 0xbb, 0xa9, 0x7a, 0x40, + 0xd7, 0x47, 0x8b, 0x55, 0x72, 0x6b, 0xe3, 0xfe, + 0x28, 0x49, 0x71, 0x24, 0xf4, 0x8f, 0xf4, 0x20, + 0x81, 0xea, 0x38, 0xff, 0x7c, 0x0a, 0x4f, 0xdf, + 0x02, 0x82, 0x39, 0x81, 0x82, 0x3b, 0xca, 0x09, + 0xdd, 0xca, 0xaa, 0x0f, 0x27, 0xf5, 0xa4, 0x83, + 0x55, 0x6c, 0x9a, 0x39, 0x9b, 0x15, 0x3a, 0x16, + 0x63, 0xdc, 0x5b, 0xf9, 0xac, 0x5b, 0xbc, 0xf7, + 0x9f, 0xbe, 0x0f, 0x8a, 0xa2, 0x3c, 0x31, 0x13, + 0xa3, 0x32, 0x48, 0xca, 0x58, 0x87, 0xf8, 0x7b, + 0xa0, 0xa1, 0x0a, 0x6a, 0x60, 0x96, 0x93, 0x5f, + 0x5d, 0x26, 0x9e, 0x63, 0x1d, 0x09, 0xae, 0x9a, + 0x41, 0xe5, 0xbd, 0x08, 0x47, 0xfe, 0xe5, 0x09, + 0x9b, 0x20, 0xfd, 0x12, 0xe2, 0xe6, 0x40, 0x7f, + 0xba, 0x4a, 0x61, 0x33, 0x66, 0x0d, 0x0e, 0x73, + 0xdb, 0xb0, 0xd5, 0xa2, 0x9a, 0x9a, 0x17, 0x0d, + 0x34, 0x30, 0x85, 0x6a, 0x42, 0x46, 0x9e, 0xff, + 0x34, 0x8f, 0x5f, 0x87, 0x6c, 0x35, 0xe7, 0xa8, + 0x4d, 0x35, 0xeb, 0xc1, 0x41, 0xaa, 0x8a, 0xd2, + 0xda, 0x19, 0xaa, 0x79, 0xa2, 0x5f, 0x35, 0x2c, + 0xa0, 0xfd, 0x25, 0xd3, 0xf7, 0x9d, 0x25, 0x18, + 0x2d, 0xfa, 0xb4, 0xbc, 0xbb, 0x07, 0x34, 0x3c, + 0x8d, 0x81, 0xbd, 0xf4, 0xe9, 0x37, 0xdb, 0x39, + 0xe9, 0xd1, 0x45, 0x5b, 0x20, 0x41, 0x2f, 0x2d, + 0x27, 0x22, 0xdc, 0x92, 0x74, 0x8a, 0x92, 0xd5, + 0x83, 0xfd, 0x09, 0xfb, 0x13, 0x9b, 0xe3, 0x39, + 0x7a, 0x6b, 0x5c, 0xfa, 0xe6, 0x76, 0x9e, 0xe0, + 0xe4, 0xe3, 0xef, 0xad, 0xbc, 0xfd, 0x42, 0x45, + 0x9a, 0xd4, 0x94, 0xd1, 0x7e, 0x8d, 0xa7, 0xd8, + 0x05, 0xd5, 0xd3, 0x62, 0xcf, 0x15, 0xcf, 0x94, + 0x7d, 0x1f, 0x5b, 0x58, 0x20, 0x44, 0x20, 0x90, + 0x71, 0xbe, 0x66, 0xe9, 0x9a, 0xab, 0x74, 0x32, + 0x70, 0x53, 0x1d, 0x69, 0xed, 0x87, 0x66, 0xf4, + 0x09, 0x4f, 0xca, 0x25, 0x30, 0xc2, 0x63, 0x79, + 0x00, 0x3c, 0xb1, 0x9b, 0x39, 0x3f, 0x00, 0xe0, + 0xa8, 0x88, 0xef, 0x7a, 0x51, 0x5b, 0xe7, 0xbd, + 0x49, 0x64, 0xda, 0x41, 0x7b, 0x24, 0xc3, 0x71, + 0x22, 0xfd, 0xd1, 0xd1, 0x20, 0xb3, 0x3f, 0x97, + 0xd3, 0x97, 0xb2, 0xaa, 0x18, 0x1c, 0x9e, 0x03, + 0x77, 0x7b, 0x5b, 0x7e, 0xf9, 0xa3, 0xa0, 0xd6, + 0x20, 0x81, 0x2c, 0x38, 0x8f, 0x9d, 0x25, 0xde, + 0xe9, 0xc8, 0xf5, 0xdd, 0x6a, 0x47, 0x9c, 0x65, + 0x04, 0x5a, 0x56, 0xe6, 0xc2, 0xeb, 0xf2, 0x02, + 0x97, 0xe1, 0xb9, 0xd8, 0xe1, 0x24, 0x76, 0x9f, + 0x23, 0x62, 0x39, 0x03, 0x4b, 0xc8, 0xf7, 0x34, + 0x07, 0x49, 0xd6, 0xe7, 0x4d, 0x9a, +}; + +const unsigned char sig[70] = { + 0x30, 0x44, 0x02, 0x20, 0x54, 0x92, 0x28, 0x3b, + 0x83, 0x33, 0x47, 0x56, 0x68, 0x79, 0xb2, 0x0c, + 0x84, 0x80, 0xcc, 0x67, 0x27, 0x8b, 0xfa, 0x48, + 0x43, 0x0d, 0x3c, 0xb4, 0x02, 0x36, 0x87, 0x97, + 0x3e, 0xdf, 0x2f, 0x65, 0x02, 0x20, 0x1b, 0x56, + 0x17, 0x06, 0xe2, 0x26, 0x0f, 0x6a, 0xe9, 0xa9, + 0x70, 0x99, 0x62, 0xeb, 0x3a, 0x04, 0x1a, 0xc4, + 0xa7, 0x03, 0x28, 0x56, 0x7c, 0xed, 0x47, 0x08, + 0x68, 0x73, 0x6a, 0xb6, 0x89, 0x0d, +}; + +const unsigned char pubkey[64] = { + 0x17, 0x5b, 0x27, 0xa6, 0x56, 0xb2, 0x26, 0x0c, + 0x26, 0x0c, 0x55, 0x42, 0x78, 0x17, 0x5d, 0x4c, + 0xf8, 0xa2, 0xfd, 0x1b, 0xb9, 0x54, 0xdf, 0xd5, + 0xeb, 0xbf, 0x22, 0x64, 0xf5, 0x21, 0x9a, 0xc6, + 0x87, 0x5f, 0x90, 0xe6, 0xfd, 0x71, 0x27, 0x9f, + 0xeb, 0xe3, 0x03, 0x44, 0xbc, 0x8d, 0x49, 0xc6, + 0x1c, 0x31, 0x3b, 0x72, 0xae, 0xd4, 0x53, 0xb1, + 0xfe, 0x5d, 0xe1, 0x30, 0xfc, 0x2b, 0x1e, 0xd2, +}; + +const unsigned char id[64] = { + 0x53, 0xfb, 0xdf, 0xaa, 0xce, 0x63, 0xde, 0xc5, + 0xfe, 0x47, 0xe6, 0x52, 0xeb, 0xf3, 0x5d, 0x53, + 0xa8, 0xbf, 0x9d, 0xd6, 0x09, 0x6b, 0x5e, 0x7f, + 0xe0, 0x0d, 0x51, 0x30, 0x85, 0x6a, 0xda, 0x68, + 0x70, 0x85, 0xb0, 0xdb, 0x08, 0x0b, 0x83, 0x2c, + 0xef, 0x44, 0xe2, 0x36, 0x88, 0xee, 0x76, 0x90, + 0x6e, 0x7b, 0x50, 0x3e, 0x9a, 0xa0, 0xd6, 0x3c, + 0x34, 0xe3, 0x83, 0xe7, 0xd1, 0xbd, 0x9f, 0x25, +}; + +const char rp_id[] = "localhost"; +const char rp_name[] = "sweet home localhost"; + +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_cred_t * +alloc_cred(void) +{ + fido_cred_t *c; + + c = fido_cred_new(); + assert(c != NULL); + + return (c); +} + +static void +free_cred(fido_cred_t *c) +{ + fido_cred_free(&c); + assert(c == 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 void +empty_cred(void) +{ + fido_cred_t *c; + fido_dev_t *d; + fido_dev_io_t io_f; + + c = alloc_cred(); + assert(fido_cred_authdata_len(c) == 0); + assert(fido_cred_authdata_ptr(c) == NULL); + assert(fido_cred_clientdata_hash_len(c) == 0); + assert(fido_cred_clientdata_hash_ptr(c) == NULL); + assert(fido_cred_flags(c) == 0); + assert(fido_cred_fmt(c) == NULL); + assert(fido_cred_id_len(c) == 0); + assert(fido_cred_id_ptr(c) == NULL); + assert(fido_cred_pubkey_len(c) == 0); + assert(fido_cred_pubkey_ptr(c) == NULL); + assert(fido_cred_rp_id(c) == NULL); + assert(fido_cred_rp_name(c) == NULL); + assert(fido_cred_sig_len(c) == 0); + assert(fido_cred_sig_ptr(c) == NULL); + assert(fido_cred_x5c_len(c) == 0); + assert(fido_cred_x5c_ptr(c) == NULL); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + + io_f.open = dummy_open; + io_f.close = dummy_close; + io_f.read = dummy_read; + io_f.write = dummy_write; + d = alloc_dev(); + + fido_dev_force_u2f(d); + assert(fido_dev_set_io_functions(d, &io_f) == FIDO_OK); + assert(fido_dev_make_cred(d, c, NULL) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_dev_make_cred(d, c, "") == FIDO_ERR_UNSUPPORTED_OPTION); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + + fido_dev_force_fido2(d); + assert(fido_dev_set_io_functions(d, &io_f) == FIDO_OK); + assert(fido_dev_make_cred(d, c, NULL) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_dev_make_cred(d, c, "") == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + + free_cred(c); + free_dev(d); +} + +static void +valid_cred(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_OK); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +no_cdh(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +no_rp_id(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +no_rp_name(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, NULL) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_OK); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +no_authdata(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_pubkey_len(c) == 0); + assert(fido_cred_pubkey_ptr(c) == NULL); + assert(fido_cred_id_len(c) == 0); + assert(fido_cred_id_ptr(c) == NULL); + free_cred(c); +} + +static void +no_x509(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +no_sig(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +no_fmt(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +wrong_options(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_TRUE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_PARAM); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +junk_cdh(void) +{ + fido_cred_t *c; + unsigned char *junk; + + junk = malloc(sizeof(cdh)); + assert(junk != NULL); + memcpy(junk, cdh, sizeof(cdh)); + junk[0] = ~junk[0]; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, junk, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_SIG); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); + free(junk); +} + +static void +junk_rp_id(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, "potato", rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_PARAM); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +junk_rp_name(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, "potato") == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_OK); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); +} + +static void +junk_authdata(void) +{ + fido_cred_t *c; + unsigned char *junk; + + junk = malloc(sizeof(authdata)); + assert(junk != NULL); + memcpy(junk, authdata, sizeof(authdata)); + junk[0] = ~junk[0]; + + c = alloc_cred(); + assert(fido_cred_set_authdata(c, junk, + sizeof(authdata)) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_authdata_len(c) == 0); + assert(fido_cred_authdata_ptr(c) == NULL); + assert(fido_cred_flags(c) == 0); + assert(fido_cred_fmt(c) == NULL); + assert(fido_cred_id_len(c) == 0); + assert(fido_cred_id_ptr(c) == NULL); + assert(fido_cred_pubkey_len(c) == 0); + assert(fido_cred_pubkey_ptr(c) == NULL); + assert(fido_cred_rp_id(c) == NULL); + assert(fido_cred_rp_name(c) == NULL); + assert(fido_cred_sig_len(c) == 0); + assert(fido_cred_sig_ptr(c) == NULL); + assert(fido_cred_x5c_len(c) == 0); + assert(fido_cred_x5c_ptr(c) == NULL); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + free_cred(c); + free(junk); +} + +static void +junk_sig(void) +{ + fido_cred_t *c; + unsigned char *junk; + + junk = malloc(sizeof(sig)); + assert(junk != NULL); + memcpy(junk, sig, sizeof(sig)); + junk[0] = ~junk[0]; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, junk, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_SIG); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); + free(junk); +} + +static void +junk_x509(void) +{ + fido_cred_t *c; + unsigned char *junk; + + junk = malloc(sizeof(x509)); + assert(junk != NULL); + memcpy(junk, x509, sizeof(x509)); + junk[0] = ~junk[0]; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, junk, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_SIG); + assert(fido_cred_pubkey_len(c) == sizeof(pubkey)); + assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0); + assert(fido_cred_id_len(c) == sizeof(id)); + assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0); + free_cred(c); + free(junk); +} + +/* github issue #6 */ +static void +invalid_type(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_RS256) == FIDO_OK); + assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK); + assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK); + assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK); + assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK); + assert(fido_cred_set_fmt(c, "packed") == FIDO_OK); + assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); + assert(fido_cred_pubkey_len(c) == 0); + assert(fido_cred_pubkey_ptr(c) == NULL); + assert(fido_cred_id_len(c) == 0); + assert(fido_cred_id_ptr(c) == NULL); + free_cred(c); +} + +/* cbor_serialize_alloc misuse */ +static void +bad_cbor_serialize(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); + assert(fido_cred_authdata_len(c) == sizeof(authdata)); + free_cred(c); +} + +static void +duplicate_keys(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata_dupkeys, + sizeof(authdata_dupkeys)) == FIDO_ERR_INVALID_ARGUMENT); + free_cred(c); +} + +static void +unsorted_keys(void) +{ + fido_cred_t *c; + + c = alloc_cred(); + assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); + assert(fido_cred_set_authdata(c, authdata_unsorted_keys, + sizeof(authdata_unsorted_keys)) == FIDO_ERR_INVALID_ARGUMENT); + free_cred(c); +} + +int +main(void) +{ + fido_init(0); + + empty_cred(); + valid_cred(); + no_cdh(); + no_rp_id(); + no_rp_name(); + no_authdata(); + no_x509(); + no_sig(); + no_fmt(); + junk_cdh(); + junk_rp_id(); + junk_rp_name(); + junk_authdata(); + junk_x509(); + junk_sig(); + wrong_options(); + invalid_type(); + bad_cbor_serialize(); + duplicate_keys(); + unsorted_keys(); + + exit(0); +} diff --git a/regress/dev.c b/regress/dev.c new file mode 100644 index 0000000..39b3584 --- /dev/null +++ b/regress/dev.c @@ -0,0 +1,77 @@ +/* + * 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 +#include + +#define FAKE_DEV_HANDLE ((void *)0xdeadbeef) +#define REPORT_LEN (64 + 1) + +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 *ptr, size_t len, int ms) +{ + (void)ptr; + (void)len; + (void)ms; + + assert(handle == FAKE_DEV_HANDLE); + + return (-1); +} + +static int +dummy_write(void *handle, const unsigned char *ptr, size_t len) +{ + assert(handle == FAKE_DEV_HANDLE); + assert(ptr != NULL); + assert(len == REPORT_LEN); + + return ((int)len); +} + +/* gh#56 */ +static void +open_iff_ok(void) +{ + fido_dev_t *dev = NULL; + fido_dev_io_t io; + + io.open = dummy_open; + io.close = dummy_close; + io.read = dummy_read; + io.write = dummy_write; + + assert((dev = fido_dev_new()) != NULL); + assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK); + assert(fido_dev_open(dev, "dummy") == FIDO_ERR_RX); + assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT); + + fido_dev_free(&dev); +} + +int +main(void) +{ + fido_init(0); + + open_iff_ok(); + + exit(0); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..926e7f2 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,104 @@ +# 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_definitions(-D_FIDO_INTERNAL) + +list(APPEND FIDO_SOURCES + aes256.c + assert.c + authkey.c + bio.c + blob.c + buf.c + cbor.c + cred.c + credman.c + dev.c + ecdh.c + eddsa.c + err.c + es256.c + hid.c + info.c + io.c + iso7816.c + log.c + pin.c + reset.c + rs256.c + u2f.c +) + +if(FUZZ) + list(APPEND FIDO_SOURCES ../fuzz/uniform_random.c) + list(APPEND FIDO_SOURCES ../fuzz/wrap.c) +endif() + +if(WIN32) + list(APPEND COMPAT_SOURCES hid_win.c) +elseif(APPLE) + list(APPEND COMPAT_SOURCES hid_osx.c) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND COMPAT_SOURCES hid_linux.c) +elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + list(APPEND COMPAT_SOURCES hid_openbsd.c) +endif() + +list(APPEND COMPAT_SOURCES + ../openbsd-compat/bsd-getpagesize.c + ../openbsd-compat/explicit_bzero.c + ../openbsd-compat/explicit_bzero_win32.c + ../openbsd-compat/recallocarray.c + ../openbsd-compat/timingsafe_bcmp.c +) + +# static library +add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES}) +target_link_libraries(fido2 ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES} + ${UDEV_LIBRARIES} ${BASE_LIBRARIES}) +if(WIN32) + if (MINGW) + target_link_libraries(fido2 wsock32 ws2_32 bcrypt setupapi hid) + else() + target_link_libraries(fido2 wsock32 ws2_32 bcrypt SetupAPI hid) + set_target_properties(fido2 PROPERTIES OUTPUT_NAME fido2_static) + endif() +elseif(APPLE) + target_link_libraries(fido2 "-framework CoreFoundation" + "-framework IOKit") +endif() +install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +# dynamic library +add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES}) +target_link_libraries(fido2_shared ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES} + ${UDEV_LIBRARIES} ${BASE_LIBRARIES}) +if(WIN32) + if (MINGW) + target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt + setupapi hid) + else() + target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt + SetupAPI hid) + endif() +elseif(APPLE) + target_link_libraries(fido2_shared "-framework CoreFoundation" + "-framework IOKit") +endif() +set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2 + VERSION ${LIB_VERSION} SOVERSION ${LIB_SOVERSION}) +install(TARGETS fido2_shared + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +install(FILES fido.h DESTINATION include) +install(DIRECTORY fido DESTINATION include) + +if(NOT WIN32) + configure_file(libfido2.pc.in libfido2.pc @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libfido2.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif() diff --git a/src/aes256.c b/src/aes256.c new file mode 100644 index 0000000..767cdb2 --- /dev/null +++ b/src/aes256.c @@ -0,0 +1,98 @@ +/* + * 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 +#include + +#include "fido.h" + +int +aes256_cbc_enc(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out) +{ + EVP_CIPHER_CTX *ctx = NULL; + unsigned char iv[32]; + int len; + int ok = -1; + + memset(iv, 0, sizeof(iv)); + out->ptr = NULL; + out->len = 0; + + /* sanity check */ + if (in->len > INT_MAX || (in->len % 16) != 0 || + (out->ptr = calloc(1, in->len)) == NULL) { + fido_log_debug("%s: in->len=%zu", __func__, in->len); + goto fail; + } + + if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 || + !EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) || + !EVP_CIPHER_CTX_set_padding(ctx, 0) || + !EVP_EncryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) || + len < 0 || (size_t)len != in->len) { + fido_log_debug("%s: EVP_Encrypt", __func__); + goto fail; + } + + out->len = (size_t)len; + + ok = 0; +fail: + if (ctx != NULL) + EVP_CIPHER_CTX_free(ctx); + + if (ok < 0) { + free(out->ptr); + out->ptr = NULL; + out->len = 0; + } + + return (ok); +} + +int +aes256_cbc_dec(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out) +{ + EVP_CIPHER_CTX *ctx = NULL; + unsigned char iv[32]; + int len; + int ok = -1; + + memset(iv, 0, sizeof(iv)); + out->ptr = NULL; + out->len = 0; + + /* sanity check */ + if (in->len > INT_MAX || (in->len % 16) != 0 || + (out->ptr = calloc(1, in->len)) == NULL) { + fido_log_debug("%s: in->len=%zu", __func__, in->len); + goto fail; + } + + if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 || + !EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) || + !EVP_CIPHER_CTX_set_padding(ctx, 0) || + !EVP_DecryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) || + len < 0 || (size_t)len > in->len + 32) { + fido_log_debug("%s: EVP_Decrypt", __func__); + goto fail; + } + + out->len = (size_t)len; + + ok = 0; +fail: + if (ctx != NULL) + EVP_CIPHER_CTX_free(ctx); + + if (ok < 0) { + free(out->ptr); + out->ptr = NULL; + out->len = 0; + } + + return (ok); +} diff --git a/src/assert.c b/src/assert.c new file mode 100644 index 0000000..a21b308 --- /dev/null +++ b/src/assert.c @@ -0,0 +1,1090 @@ +/* + * 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 +#include +#include +#include + +#include +#include "fido.h" +#include "fido/es256.h" +#include "fido/rs256.h" +#include "fido/eddsa.h" + +static int +adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_assert_t *assert = arg; + uint64_t n; + + /* numberOfCredentials; see section 6.2 */ + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8 || + cbor_get_uint8(key) != 5) { + fido_log_debug("%s: cbor_type", __func__); + return (0); /* ignore */ + } + + if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { + fido_log_debug("%s: cbor_decode_uint64", __func__); + return (-1); + } + + if (assert->stmt_len != 0 || assert->stmt_cnt != 1 || + (size_t)n < assert->stmt_cnt) { + fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu", + __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n); + return (-1); + } + + if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) { + fido_log_debug("%s: fido_assert_set_count", __func__); + return (-1); + } + + assert->stmt_len = 0; /* XXX */ + + return (0); +} + +static int +parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_assert_stmt *stmt = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 1: /* credential id */ + return (cbor_decode_cred_id(val, &stmt->id)); + case 2: /* authdata */ + return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor, + &stmt->authdata, &stmt->authdata_ext, + &stmt->hmac_secret_enc)); + case 3: /* signature */ + return (fido_blob_decode(val, &stmt->sig)); + case 4: /* user attributes */ + return (cbor_decode_user(val, &stmt->user)); + default: /* ignore */ + fido_log_debug("%s: cbor type", __func__); + return (0); + } +} + +static int +fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert, + const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin) +{ + fido_blob_t f; + cbor_item_t *argv[7]; + int r; + + memset(argv, 0, sizeof(argv)); + memset(&f, 0, sizeof(f)); + + /* do we have everything we need? */ + if (assert->rp_id == NULL || assert->cdh.ptr == NULL) { + fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__, + (void *)assert->rp_id, (void *)assert->cdh.ptr); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL || + (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + /* allowed credentials */ + if (assert->allow_list.len) { + const fido_blob_array_t *cl = &assert->allow_list; + if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) { + fido_log_debug("%s: cbor_encode_pubkey_list", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + } + + /* hmac-secret extension */ + if (assert->ext & FIDO_EXT_HMAC_SECRET) + if ((argv[3] = cbor_encode_hmac_secret_param(ecdh, pk, + &assert->hmac_salt)) == NULL) { + fido_log_debug("%s: cbor_encode_hmac_secret_param", + __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + /* options */ + if (assert->up != FIDO_OPT_OMIT || assert->uv != FIDO_OPT_OMIT) + if ((argv[4] = cbor_encode_assert_options(assert->up, + assert->uv)) == NULL) { + fido_log_debug("%s: cbor_encode_assert_options", + __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + /* pin authentication */ + if (pin) { + if (pk == NULL || ecdh == NULL) { + fido_log_debug("%s: pin=%p, pk=%p, ecdh=%p", __func__, + (const void *)pin, (const void *)pk, + (const void *)ecdh); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + if ((r = cbor_add_pin_params(dev, &assert->cdh, pk, ecdh, pin, + &argv[5], &argv[6])) != FIDO_OK) { + fido_log_debug("%s: cbor_add_pin_params", __func__); + goto fail; + } + } + + /* frame and transmit */ + if (cbor_build_frame(CTAP_CBOR_ASSERT, argv, 7, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + free(f.ptr); + + return (r); +} + +static int +fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + fido_assert_reset_rx(assert); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + /* start with room for a single assertion */ + if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL) + return (FIDO_ERR_INTERNAL); + + assert->stmt_len = 0; + assert->stmt_cnt = 1; + + /* adjust as needed */ + if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert, + adjust_assert_count)) != FIDO_OK) { + fido_log_debug("%s: adjust_assert_count", __func__); + return (r); + } + + /* parse the first assertion */ + if ((r = cbor_parse_reply(reply, (size_t)reply_len, + &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) { + fido_log_debug("%s: parse_assert_reply", __func__); + return (r); + } + + assert->stmt_len++; + + return (FIDO_OK); +} + +static int +fido_get_next_assert_tx(fido_dev_t *dev) +{ + const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT }; + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + + if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + return (FIDO_ERR_TX); + } + + return (FIDO_OK); +} + +static int +fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + /* sanity check */ + if (assert->stmt_len >= assert->stmt_cnt) { + fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__, + assert->stmt_len, assert->stmt_cnt); + return (FIDO_ERR_INTERNAL); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, + &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) { + fido_log_debug("%s: parse_assert_reply", __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert, + const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int ms) +{ + int r; + + if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin)) != FIDO_OK || + (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK) + return (r); + + while (assert->stmt_len < assert->stmt_cnt) { + if ((r = fido_get_next_assert_tx(dev)) != FIDO_OK || + (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK) + return (r); + assert->stmt_len++; + } + + return (FIDO_OK); +} + +static int +decrypt_hmac_secrets(fido_assert_t *assert, const fido_blob_t *key) +{ + for (size_t i = 0; i < assert->stmt_cnt; i++) { + fido_assert_stmt *stmt = &assert->stmt[i]; + if (stmt->hmac_secret_enc.ptr != NULL) { + if (aes256_cbc_dec(key, &stmt->hmac_secret_enc, + &stmt->hmac_secret) < 0) { + fido_log_debug("%s: aes256_cbc_dec %zu", + __func__, i); + return (-1); + } + } + } + + return (0); +} + +int +fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin) +{ + fido_blob_t *ecdh = NULL; + es256_pk_t *pk = NULL; + int r; + + if (assert->rp_id == NULL || assert->cdh.ptr == NULL) { + fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__, + (void *)assert->rp_id, (void *)assert->cdh.ptr); + return (FIDO_ERR_INVALID_ARGUMENT); + } + + if (fido_dev_is_fido2(dev) == false) { + if (pin != NULL || assert->ext != 0) + return (FIDO_ERR_UNSUPPORTED_OPTION); + return (u2f_authenticate(dev, assert, -1)); + } + + if (pin != NULL || assert->ext != 0) { + if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { + fido_log_debug("%s: fido_do_ecdh", __func__); + goto fail; + } + } + + r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1); + if (r == FIDO_OK && assert->ext & FIDO_EXT_HMAC_SECRET) + if (decrypt_hmac_secrets(assert, ecdh) < 0) { + fido_log_debug("%s: decrypt_hmac_secrets", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + +fail: + es256_pk_free(&pk); + fido_blob_free(&ecdh); + + return (r); +} + +int +fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv) +{ + fido_log_debug("%s: flags=%02x", __func__, flags); + fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv); + + if (up == FIDO_OPT_TRUE && + (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) { + fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__); + return (-1); /* user not present */ + } + + if (uv == FIDO_OPT_TRUE && + (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) { + fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__); + return (-1); /* user not verified */ + } + + return (0); +} + +static int +check_extensions(int authdata_ext, int ext) +{ + if (authdata_ext != ext) { + fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__, + authdata_ext, ext); + return (-1); + } + + return (0); +} + +static int +get_signed_hash(int cose_alg, fido_blob_t *dgst, const fido_blob_t *clientdata, + const fido_blob_t *authdata_cbor) +{ + cbor_item_t *item = NULL; + unsigned char *authdata_ptr = NULL; + size_t authdata_len; + struct cbor_load_result cbor; + SHA256_CTX ctx; + int ok = -1; + + if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len, + &cbor)) == NULL || cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false) { + fido_log_debug("%s: authdata", __func__); + goto fail; + } + + authdata_ptr = cbor_bytestring_handle(item); + authdata_len = cbor_bytestring_length(item); + + if (cose_alg != COSE_EDDSA) { + if (dgst->len < SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 || + SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 || + SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 || + SHA256_Final(dgst->ptr, &ctx) == 0) { + fido_log_debug("%s: sha256", __func__); + goto fail; + } + dgst->len = SHA256_DIGEST_LENGTH; + } else { + if (SIZE_MAX - authdata_len < clientdata->len || + dgst->len < authdata_len + clientdata->len) { + fido_log_debug("%s: memcpy", __func__); + goto fail; + } + memcpy(dgst->ptr, authdata_ptr, authdata_len); + memcpy(dgst->ptr + authdata_len, clientdata->ptr, + clientdata->len); + dgst->len = authdata_len + clientdata->len; + } + + ok = 0; +fail: + if (item != NULL) + cbor_decref(&item); + + return (ok); +} + +int +fido_verify_sig_es256(const fido_blob_t *dgst, const es256_pk_t *pk, + const fido_blob_t *sig) +{ + EVP_PKEY *pkey = NULL; + EC_KEY *ec = NULL; + int ok = -1; + + /* ECDSA_verify needs ints */ + if (dgst->len > INT_MAX || sig->len > INT_MAX) { + fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__, + dgst->len, sig->len); + return (-1); + } + + if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL || + (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) { + fido_log_debug("%s: pk -> ec", __func__); + goto fail; + } + + if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr, + (int)sig->len, ec) != 1) { + fido_log_debug("%s: ECDSA_verify", __func__); + goto fail; + } + + ok = 0; +fail: + if (pkey != NULL) + EVP_PKEY_free(pkey); + + return (ok); +} + +int +fido_verify_sig_rs256(const fido_blob_t *dgst, const rs256_pk_t *pk, + const fido_blob_t *sig) +{ + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + int ok = -1; + + /* RSA_verify needs unsigned ints */ + if (dgst->len > UINT_MAX || sig->len > UINT_MAX) { + fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__, + dgst->len, sig->len); + return (-1); + } + + if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL || + (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) { + fido_log_debug("%s: pk -> ec", __func__); + goto fail; + } + + if (RSA_verify(NID_sha256, dgst->ptr, (unsigned int)dgst->len, sig->ptr, + (unsigned int)sig->len, rsa) != 1) { + fido_log_debug("%s: RSA_verify", __func__); + goto fail; + } + + ok = 0; +fail: + if (pkey != NULL) + EVP_PKEY_free(pkey); + + return (ok); +} + +int +fido_verify_sig_eddsa(const fido_blob_t *dgst, const eddsa_pk_t *pk, + const fido_blob_t *sig) +{ + EVP_PKEY *pkey = NULL; + EVP_MD_CTX *mdctx = NULL; + int ok = -1; + + /* EVP_DigestVerify needs ints */ + if (dgst->len > INT_MAX || sig->len > INT_MAX) { + fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__, + dgst->len, sig->len); + return (-1); + } + + if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) { + fido_log_debug("%s: pk -> pkey", __func__); + goto fail; + } + + if ((mdctx = EVP_MD_CTX_new()) == NULL) { + fido_log_debug("%s: EVP_MD_CTX_new", __func__); + goto fail; + } + + if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) { + fido_log_debug("%s: EVP_DigestVerifyInit", __func__); + goto fail; + } + + if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr, + dgst->len) != 1) { + fido_log_debug("%s: EVP_DigestVerify", __func__); + goto fail; + } + + ok = 0; +fail: + if (mdctx != NULL) + EVP_MD_CTX_free(mdctx); + + if (pkey != NULL) + EVP_PKEY_free(pkey); + + return (ok); +} + +int +fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg, + const void *pk) +{ + unsigned char buf[1024]; + fido_blob_t dgst; + const fido_assert_stmt *stmt = NULL; + int ok = -1; + int r; + + dgst.ptr = buf; + dgst.len = sizeof(buf); + + if (idx >= assert->stmt_len || pk == NULL) { + r = FIDO_ERR_INVALID_ARGUMENT; + goto out; + } + + stmt = &assert->stmt[idx]; + + /* do we have everything we need? */ + if (assert->cdh.ptr == NULL || assert->rp_id == NULL || + stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) { + fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p", + __func__, (void *)assert->cdh.ptr, assert->rp_id, + (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr); + r = FIDO_ERR_INVALID_ARGUMENT; + goto out; + } + + if (fido_check_flags(stmt->authdata.flags, assert->up, + assert->uv) < 0) { + fido_log_debug("%s: fido_check_flags", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (check_extensions(stmt->authdata_ext, assert->ext) < 0) { + fido_log_debug("%s: check_extensions", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) { + fido_log_debug("%s: fido_check_rp_id", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (get_signed_hash(cose_alg, &dgst, &assert->cdh, + &stmt->authdata_cbor) < 0) { + fido_log_debug("%s: get_signed_hash", __func__); + r = FIDO_ERR_INTERNAL; + goto out; + } + + switch (cose_alg) { + case COSE_ES256: + ok = fido_verify_sig_es256(&dgst, pk, &stmt->sig); + break; + case COSE_RS256: + ok = fido_verify_sig_rs256(&dgst, pk, &stmt->sig); + break; + case COSE_EDDSA: + ok = fido_verify_sig_eddsa(&dgst, pk, &stmt->sig); + break; + default: + fido_log_debug("%s: unsupported cose_alg %d", __func__, + cose_alg); + r = FIDO_ERR_UNSUPPORTED_OPTION; + goto out; + } + + if (ok < 0) + r = FIDO_ERR_INVALID_SIG; + else + r = FIDO_OK; +out: + explicit_bzero(buf, sizeof(buf)); + + return (r); +} + +int +fido_assert_set_clientdata_hash(fido_assert_t *assert, + const unsigned char *hash, size_t hash_len) +{ + if (fido_blob_set(&assert->cdh, hash, hash_len) < 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (FIDO_OK); +} + +int +fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt, + size_t salt_len) +{ + if ((salt_len != 32 && salt_len != 64) || + fido_blob_set(&assert->hmac_salt, salt, salt_len) < 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (FIDO_OK); +} + +int +fido_assert_set_rp(fido_assert_t *assert, const char *id) +{ + if (assert->rp_id != NULL) { + free(assert->rp_id); + assert->rp_id = NULL; + } + + if (id == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + if ((assert->rp_id = strdup(id)) == NULL) + return (FIDO_ERR_INTERNAL); + + return (FIDO_OK); +} + +int +fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr, + size_t len) +{ + fido_blob_t id; + fido_blob_t *list_ptr; + int r; + + memset(&id, 0, sizeof(id)); + + if (assert->allow_list.len == SIZE_MAX) { + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr = + recallocarray(assert->allow_list.ptr, assert->allow_list.len, + assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) { + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + list_ptr[assert->allow_list.len++] = id; + assert->allow_list.ptr = list_ptr; + + return (FIDO_OK); +fail: + free(id.ptr); + + return (r); + +} + +int +fido_assert_set_extensions(fido_assert_t *assert, int ext) +{ + if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET) + return (FIDO_ERR_INVALID_ARGUMENT); + + assert->ext = ext; + + return (FIDO_OK); +} + +int +fido_assert_set_options(fido_assert_t *assert, bool up, bool uv) +{ + assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; + assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; + + return (FIDO_OK); +} + +int +fido_assert_set_up(fido_assert_t *assert, fido_opt_t up) +{ + assert->up = up; + + return (FIDO_OK); +} + +int +fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv) +{ + assert->uv = uv; + + return (FIDO_OK); +} + +const unsigned char * +fido_assert_clientdata_hash_ptr(const fido_assert_t *assert) +{ + return (assert->cdh.ptr); +} + +size_t +fido_assert_clientdata_hash_len(const fido_assert_t *assert) +{ + return (assert->cdh.len); +} + +fido_assert_t * +fido_assert_new(void) +{ + return (calloc(1, sizeof(fido_assert_t))); +} + +void +fido_assert_reset_tx(fido_assert_t *assert) +{ + free(assert->rp_id); + free(assert->cdh.ptr); + free(assert->hmac_salt.ptr); + fido_free_blob_array(&assert->allow_list); + + memset(&assert->cdh, 0, sizeof(assert->cdh)); + memset(&assert->hmac_salt, 0, sizeof(assert->hmac_salt)); + memset(&assert->allow_list, 0, sizeof(assert->allow_list)); + + assert->rp_id = NULL; + assert->up = FIDO_OPT_OMIT; + assert->uv = FIDO_OPT_OMIT; + assert->ext = 0; +} + +void +fido_assert_reset_rx(fido_assert_t *assert) +{ + for (size_t i = 0; i < assert->stmt_cnt; i++) { + free(assert->stmt[i].user.id.ptr); + free(assert->stmt[i].user.icon); + free(assert->stmt[i].user.name); + free(assert->stmt[i].user.display_name); + free(assert->stmt[i].id.ptr); + if (assert->stmt[i].hmac_secret.ptr != NULL) { + explicit_bzero(assert->stmt[i].hmac_secret.ptr, + assert->stmt[i].hmac_secret.len); + } + free(assert->stmt[i].hmac_secret.ptr); + free(assert->stmt[i].hmac_secret_enc.ptr); + free(assert->stmt[i].authdata_cbor.ptr); + free(assert->stmt[i].sig.ptr); + memset(&assert->stmt[i], 0, sizeof(assert->stmt[i])); + } + + free(assert->stmt); + + assert->stmt = NULL; + assert->stmt_len = 0; + assert->stmt_cnt = 0; +} + +void +fido_assert_free(fido_assert_t **assert_p) +{ + fido_assert_t *assert; + + if (assert_p == NULL || (assert = *assert_p) == NULL) + return; + + fido_assert_reset_tx(assert); + fido_assert_reset_rx(assert); + + free(assert); + + *assert_p = NULL; +} + +size_t +fido_assert_count(const fido_assert_t *assert) +{ + return (assert->stmt_len); +} + +const char * +fido_assert_rp_id(const fido_assert_t *assert) +{ + return (assert->rp_id); +} + +uint8_t +fido_assert_flags(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (0); + + return (assert->stmt[idx].authdata.flags); +} + +uint32_t +fido_assert_sigcount(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (0); + + return (assert->stmt[idx].authdata.sigcount); +} + +const unsigned char * +fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (NULL); + + return (assert->stmt[idx].authdata_cbor.ptr); +} + +size_t +fido_assert_authdata_len(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (0); + + return (assert->stmt[idx].authdata_cbor.len); +} + +const unsigned char * +fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (NULL); + + return (assert->stmt[idx].sig.ptr); +} + +size_t +fido_assert_sig_len(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (0); + + return (assert->stmt[idx].sig.len); +} + +const unsigned char * +fido_assert_id_ptr(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (NULL); + + return (assert->stmt[idx].id.ptr); +} + +size_t +fido_assert_id_len(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (0); + + return (assert->stmt[idx].id.len); +} + +const unsigned char * +fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (NULL); + + return (assert->stmt[idx].user.id.ptr); +} + +size_t +fido_assert_user_id_len(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (0); + + return (assert->stmt[idx].user.id.len); +} + +const char * +fido_assert_user_icon(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (NULL); + + return (assert->stmt[idx].user.icon); +} + +const char * +fido_assert_user_name(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (NULL); + + return (assert->stmt[idx].user.name); +} + +const char * +fido_assert_user_display_name(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (NULL); + + return (assert->stmt[idx].user.display_name); +} + +const unsigned char * +fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (NULL); + + return (assert->stmt[idx].hmac_secret.ptr); +} + +size_t +fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx) +{ + if (idx >= assert->stmt_len) + return (0); + + return (assert->stmt[idx].hmac_secret.len); +} + +static void +fido_assert_clean_authdata(fido_assert_stmt *as) +{ + free(as->authdata_cbor.ptr); + free(as->hmac_secret_enc.ptr); + + memset(&as->authdata_ext, 0, sizeof(as->authdata_ext)); + memset(&as->authdata_cbor, 0, sizeof(as->authdata_cbor)); + memset(&as->authdata, 0, sizeof(as->authdata)); + memset(&as->hmac_secret_enc, 0, sizeof(as->hmac_secret_enc)); +} + +int +fido_assert_set_authdata(fido_assert_t *assert, size_t idx, + const unsigned char *ptr, size_t len) +{ + cbor_item_t *item = NULL; + fido_assert_stmt *stmt = NULL; + struct cbor_load_result cbor; + int r; + + if (idx >= assert->stmt_len || ptr == NULL || len == 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + stmt = &assert->stmt[idx]; + fido_assert_clean_authdata(stmt); + + if ((item = cbor_load(ptr, len, &cbor)) == NULL) { + fido_log_debug("%s: cbor_load", __func__); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor, + &stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) { + fido_log_debug("%s: cbor_decode_assert_authdata", __func__); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + r = FIDO_OK; +fail: + if (item != NULL) + cbor_decref(&item); + + if (r != FIDO_OK) + fido_assert_clean_authdata(stmt); + + return (r); +} + +int +fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx, + const unsigned char *ptr, size_t len) +{ + cbor_item_t *item = NULL; + fido_assert_stmt *stmt = NULL; + int r; + + if (idx >= assert->stmt_len || ptr == NULL || len == 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + stmt = &assert->stmt[idx]; + fido_assert_clean_authdata(stmt); + + if ((item = cbor_build_bytestring(ptr, len)) == NULL) { + fido_log_debug("%s: cbor_build_bytestring", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor, + &stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) { + fido_log_debug("%s: cbor_decode_assert_authdata", __func__); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + r = FIDO_OK; +fail: + if (item != NULL) + cbor_decref(&item); + + if (r != FIDO_OK) + fido_assert_clean_authdata(stmt); + + return (r); +} + +static void +fido_assert_clean_sig(fido_assert_stmt *as) +{ + free(as->sig.ptr); + as->sig.ptr = NULL; + as->sig.len = 0; +} + +int +fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr, + size_t len) +{ + unsigned char *sig; + + if (idx >= a->stmt_len || ptr == NULL || len == 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + fido_assert_clean_sig(&a->stmt[idx]); + + if ((sig = malloc(len)) == NULL) + return (FIDO_ERR_INTERNAL); + + memcpy(sig, ptr, len); + a->stmt[idx].sig.ptr = sig; + a->stmt[idx].sig.len = len; + + return (FIDO_OK); +} + +/* XXX shrinking leaks memory; fortunately that shouldn't happen */ +int +fido_assert_set_count(fido_assert_t *assert, size_t n) +{ + void *new_stmt; + +#ifdef FIDO_FUZZ + if (n > UINT8_MAX) { + fido_log_debug("%s: n > UINT8_MAX", __func__); + return (FIDO_ERR_INTERNAL); + } +#endif + + new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n, + sizeof(fido_assert_stmt)); + if (new_stmt == NULL) + return (FIDO_ERR_INTERNAL); + + assert->stmt = new_stmt; + assert->stmt_cnt = n; + assert->stmt_len = n; + + return (FIDO_OK); +} diff --git a/src/authkey.c b/src/authkey.c new file mode 100644 index 0000000..9de37f1 --- /dev/null +++ b/src/authkey.c @@ -0,0 +1,98 @@ +/* + * 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 +#include "fido.h" + +static int +parse_authkey(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + es256_pk_t *authkey = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8 || + cbor_get_uint8(key) != 1) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + return (es256_pk_decode(val, authkey)); +} + +static int +fido_dev_authkey_tx(fido_dev_t *dev) +{ + fido_blob_t f; + cbor_item_t *argv[2]; + int r; + + fido_log_debug("%s: dev=%p", __func__, (void *)dev); + + memset(&f, 0, sizeof(f)); + memset(argv, 0, sizeof(argv)); + + /* add command parameters */ + if ((argv[0] = cbor_build_uint8(1)) == NULL || + (argv[1] = cbor_build_uint8(2)) == NULL) { + fido_log_debug("%s: cbor_build", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + /* frame and transmit */ + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + free(f.ptr); + + return (r); +} + +static int +fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + + fido_log_debug("%s: dev=%p, authkey=%p, ms=%d", __func__, (void *)dev, + (void *)authkey, ms); + + memset(authkey, 0, sizeof(*authkey)); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + return (cbor_parse_reply(reply, (size_t)reply_len, authkey, + parse_authkey)); +} + +static int +fido_dev_authkey_wait(fido_dev_t *dev, es256_pk_t *authkey, int ms) +{ + int r; + + if ((r = fido_dev_authkey_tx(dev)) != FIDO_OK || + (r = fido_dev_authkey_rx(dev, authkey, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_dev_authkey(fido_dev_t *dev, es256_pk_t *authkey) +{ + return (fido_dev_authkey_wait(dev, authkey, -1)); +} diff --git a/src/bio.c b/src/bio.c new file mode 100644 index 0000000..74814b9 --- /dev/null +++ b/src/bio.c @@ -0,0 +1,844 @@ +/* + * 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 + +#include "fido.h" +#include "fido/bio.h" +#include "fido/es256.h" + +#define CMD_ENROLL_BEGIN 0x01 +#define CMD_ENROLL_NEXT 0x02 +#define CMD_ENROLL_CANCEL 0x03 +#define CMD_ENUM 0x04 +#define CMD_SET_NAME 0x05 +#define CMD_ENROLL_REMOVE 0x06 +#define CMD_GET_INFO 0x07 + +static int +bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc, + cbor_item_t **param, fido_blob_t *hmac_data) +{ + const uint8_t prefix[2] = { 0x01 /* modality */, cmd }; + int ok = -1; + size_t cbor_alloc_len; + size_t cbor_len; + unsigned char *cbor = NULL; + + if (argv == NULL || param == NULL) + return (fido_blob_set(hmac_data, prefix, sizeof(prefix))); + + if ((*param = cbor_flatten_vector(argv, argc)) == NULL) { + fido_log_debug("%s: cbor_flatten_vector", __func__); + goto fail; + } + + if ((cbor_len = cbor_serialize_alloc(*param, &cbor, + &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) { + fido_log_debug("%s: cbor_serialize_alloc", __func__); + goto fail; + } + + if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) { + fido_log_debug("%s: malloc", __func__); + goto fail; + } + + memcpy(hmac_data->ptr, prefix, sizeof(prefix)); + memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len); + hmac_data->len = cbor_len + sizeof(prefix); + + ok = 0; +fail: + free(cbor); + + return (ok); +} + +static int +bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc, + const char *pin, const fido_blob_t *token) +{ + cbor_item_t *argv[5]; + es256_pk_t *pk = NULL; + fido_blob_t *ecdh = NULL; + fido_blob_t f; + fido_blob_t hmac; + int r = FIDO_ERR_INTERNAL; + + memset(&f, 0, sizeof(f)); + memset(&hmac, 0, sizeof(hmac)); + memset(&argv, 0, sizeof(argv)); + + /* modality, subCommand */ + if ((argv[0] = cbor_build_uint8(1)) == NULL || + (argv[1] = cbor_build_uint8(cmd)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + goto fail; + } + + /* subParams */ + if (pin || token) { + if (bio_prepare_hmac(cmd, sub_argv, sub_argc, &argv[2], + &hmac) < 0) { + fido_log_debug("%s: bio_prepare_hmac", __func__); + goto fail; + } + } + + /* pinProtocol, pinAuth */ + if (pin) { + if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { + fido_log_debug("%s: fido_do_ecdh", __func__); + goto fail; + } + if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin, + &argv[4], &argv[3])) != FIDO_OK) { + fido_log_debug("%s: cbor_add_pin_params", __func__); + goto fail; + } + } else if (token) { + if ((argv[3] = cbor_encode_pin_opt()) == NULL || + (argv[4] = cbor_encode_pin_auth(token, &hmac)) == NULL) { + fido_log_debug("%s: encode pin", __func__); + goto fail; + } + } + + /* framing and transmission */ + if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, 5, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + es256_pk_free(&pk); + fido_blob_free(&ecdh); + free(f.ptr); + free(hmac.ptr); + + return (r); +} + +static void +bio_reset_template(fido_bio_template_t *t) +{ + free(t->name); + free(t->id.ptr); + t->name = NULL; + memset(&t->id, 0, sizeof(t->id)); +} + +static void +bio_reset_template_array(fido_bio_template_array_t *ta) +{ + for (size_t i = 0; i < ta->n_alloc; i++) + bio_reset_template(&ta->ptr[i]); + + free(ta->ptr); + ta->ptr = NULL; + memset(ta, 0, sizeof(*ta)); +} + +static int +decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_bio_template_t *t = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 1: /* id */ + return (fido_blob_decode(val, &t->id)); + case 2: /* name */ + return (cbor_string_copy(val, &t->name)); + } + + return (0); /* ignore */ +} + +static int +decode_template_array(const cbor_item_t *item, void *arg) +{ + fido_bio_template_array_t *ta = arg; + + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + if (ta->n_rx >= ta->n_alloc) { + fido_log_debug("%s: n_rx >= n_alloc", __func__); + return (-1); + } + + if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) { + fido_log_debug("%s: decode_template", __func__); + return (-1); + } + + ta->n_rx++; + + return (0); +} + +static int +bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val, + void *arg) +{ + fido_bio_template_array_t *ta = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8 || + cbor_get_uint8(key) != 7) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + if (cbor_isa_array(val) == false || + cbor_array_is_definite(val) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) { + fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0", + __func__); + return (-1); + } + + if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL) + return (-1); + + ta->n_alloc = cbor_array_size(val); + + if (cbor_array_iter(val, ta, decode_template_array) < 0) { + fido_log_debug("%s: decode_template_array", __func__); + return (-1); + } + + return (0); +} + +static int +bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + bio_reset_template_array(ta); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, ta, + bio_parse_template_array)) != FIDO_OK) { + fido_log_debug("%s: bio_parse_template_array" , __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta, + const char *pin, int ms) +{ + int r; + + if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL)) != FIDO_OK || + (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, + const char *pin) +{ + if (pin == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (bio_get_template_array_wait(dev, ta, pin, -1)); +} + +static int +bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t, + const char *pin, int ms) +{ + cbor_item_t *argv[2]; + int r = FIDO_ERR_INTERNAL; + + memset(&argv, 0, sizeof(argv)); + + if ((argv[0] = fido_blob_encode(&t->id)) == NULL || + (argv[1] = cbor_build_string(t->name)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + goto fail; + } + + if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL)) != FIDO_OK || + (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { + fido_log_debug("%s: tx/rx", __func__); + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + + return (r); +} + +int +fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t, + const char *pin) +{ + if (pin == NULL || t->name == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (bio_set_template_name_wait(dev, t, pin, -1)); +} + +static void +bio_reset_enroll(fido_bio_enroll_t *e) +{ + e->remaining_samples = 0; + e->last_status = 0; + + if (e->token) + fido_blob_free(&e->token); +} + +static int +bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val, + void *arg) +{ + fido_bio_enroll_t *e = arg; + uint64_t x; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 5: + if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { + fido_log_debug("%s: cbor_decode_uint64", __func__); + return (-1); + } + e->last_status = (uint8_t)x; + break; + case 6: + if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { + fido_log_debug("%s: cbor_decode_uint64", __func__); + return (-1); + } + e->remaining_samples = (uint8_t)x; + break; + default: + return (0); /* ignore */ + } + + return (0); +} + +static int +bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val, + void *arg) +{ + fido_blob_t *id = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8 || + cbor_get_uint8(key) != 4) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + return (fido_blob_decode(val, id)); +} + +static int +bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, + fido_bio_enroll_t *e, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + bio_reset_template(t); + + e->remaining_samples = 0; + e->last_status = 0; + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, e, + bio_parse_enroll_status)) != FIDO_OK) { + fido_log_debug("%s: bio_parse_enroll_status", __func__); + return (r); + } + if ((r = cbor_parse_reply(reply, (size_t)reply_len, &t->id, + bio_parse_template_id)) != FIDO_OK) { + fido_log_debug("%s: bio_parse_template_id", __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t, + fido_bio_enroll_t *e, uint32_t timo_ms, int ms) +{ + cbor_item_t *argv[3]; + const uint8_t cmd = CMD_ENROLL_BEGIN; + int r = FIDO_ERR_INTERNAL; + + memset(&argv, 0, sizeof(argv)); + + if ((argv[2] = cbor_build_uint32(timo_ms)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + goto fail; + } + + if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK || + (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) { + fido_log_debug("%s: tx/rx", __func__); + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + + return (r); +} + +int +fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, + fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin) +{ + es256_pk_t *pk = NULL; + fido_blob_t *ecdh = NULL; + fido_blob_t *token = NULL; + int r; + + if (pin == NULL || e->token != NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + if ((token = fido_blob_new()) == NULL) { + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { + fido_log_debug("%s: fido_do_ecdh", __func__); + goto fail; + } + + if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) { + fido_log_debug("%s: fido_dev_get_pin_token", __func__); + goto fail; + } + + e->token = token; + token = NULL; +fail: + es256_pk_free(&pk); + fido_blob_free(&ecdh); + fido_blob_free(&token); + + if (r != FIDO_OK) + return (r); + + return (bio_enroll_begin_wait(dev, t, e, timo_ms, -1)); +} + +static int +bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + e->remaining_samples = 0; + e->last_status = 0; + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, e, + bio_parse_enroll_status)) != FIDO_OK) { + fido_log_debug("%s: bio_parse_enroll_status", __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t, + fido_bio_enroll_t *e, uint32_t timo_ms, int ms) +{ + cbor_item_t *argv[3]; + const uint8_t cmd = CMD_ENROLL_NEXT; + int r = FIDO_ERR_INTERNAL; + + memset(&argv, 0, sizeof(argv)); + + if ((argv[0] = fido_blob_encode(&t->id)) == NULL || + (argv[2] = cbor_build_uint32(timo_ms)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + goto fail; + } + + if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK || + (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) { + fido_log_debug("%s: tx/rx", __func__); + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + + return (r); +} + +int +fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t, + fido_bio_enroll_t *e, uint32_t timo_ms) +{ + if (e->token == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (bio_enroll_continue_wait(dev, t, e, timo_ms, -1)); +} + +static int +bio_enroll_cancel_wait(fido_dev_t *dev, int ms) +{ + const uint8_t cmd = CMD_ENROLL_CANCEL; + int r; + + if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL)) != FIDO_OK || + (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { + fido_log_debug("%s: tx/rx", __func__); + return (r); + } + + return (FIDO_OK); +} + +int +fido_bio_dev_enroll_cancel(fido_dev_t *dev) +{ + return (bio_enroll_cancel_wait(dev, -1)); +} + +static int +bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t, + const char *pin, int ms) +{ + cbor_item_t *argv[1]; + const uint8_t cmd = CMD_ENROLL_REMOVE; + int r = FIDO_ERR_INTERNAL; + + memset(&argv, 0, sizeof(argv)); + + if ((argv[0] = fido_blob_encode(&t->id)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + goto fail; + } + + if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL)) != FIDO_OK || + (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { + fido_log_debug("%s: tx/rx", __func__); + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + + return (r); +} + +int +fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t, + const char *pin) +{ + return (bio_enroll_remove_wait(dev, t, pin, -1)); +} + +static void +bio_reset_info(fido_bio_info_t *i) +{ + i->type = 0; + i->max_samples = 0; +} + +static int +bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_bio_info_t *i = arg; + uint64_t x; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 2: + if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { + fido_log_debug("%s: cbor_decode_uint64", __func__); + return (-1); + } + i->type = (uint8_t)x; + break; + case 3: + if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { + fido_log_debug("%s: cbor_decode_uint64", __func__); + return (-1); + } + i->max_samples = (uint8_t)x; + break; + default: + return (0); /* ignore */ + } + + return (0); +} + +static int +bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + bio_reset_info(i); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, i, + bio_parse_info)) != FIDO_OK) { + fido_log_debug("%s: bio_parse_info" , __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms) +{ + int r; + + if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL)) != FIDO_OK || + (r = bio_rx_info(dev, i, ms)) != FIDO_OK) { + fido_log_debug("%s: tx/rx", __func__); + return (r); + } + + return (FIDO_OK); +} + +int +fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i) +{ + return (bio_get_info_wait(dev, i, -1)); +} + +const char * +fido_bio_template_name(const fido_bio_template_t *t) +{ + return (t->name); +} + +const unsigned char * +fido_bio_template_id_ptr(const fido_bio_template_t *t) +{ + return (t->id.ptr); +} + +size_t +fido_bio_template_id_len(const fido_bio_template_t *t) +{ + return (t->id.len); +} + +size_t +fido_bio_template_array_count(const fido_bio_template_array_t *ta) +{ + return (ta->n_rx); +} + +fido_bio_template_array_t * +fido_bio_template_array_new(void) +{ + return (calloc(1, sizeof(fido_bio_template_array_t))); +} + +fido_bio_template_t * +fido_bio_template_new(void) +{ + return (calloc(1, sizeof(fido_bio_template_t))); +} + +void +fido_bio_template_array_free(fido_bio_template_array_t **tap) +{ + fido_bio_template_array_t *ta; + + if (tap == NULL || (ta = *tap) == NULL) + return; + + bio_reset_template_array(ta); + free(ta); + *tap = NULL; +} + +void +fido_bio_template_free(fido_bio_template_t **tp) +{ + fido_bio_template_t *t; + + if (tp == NULL || (t = *tp) == NULL) + return; + + bio_reset_template(t); + free(t); + *tp = NULL; +} + +int +fido_bio_template_set_name(fido_bio_template_t *t, const char *name) +{ + free(t->name); + t->name = NULL; + + if (name && (t->name = strdup(name)) == NULL) + return (FIDO_ERR_INTERNAL); + + return (FIDO_OK); +} + +int +fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr, + size_t len) +{ + free(t->id.ptr); + t->id.ptr = NULL; + t->id.len = 0; + + if (ptr && fido_blob_set(&t->id, ptr, len) < 0) + return (FIDO_ERR_INTERNAL); + + return (FIDO_OK); +} + +const fido_bio_template_t * +fido_bio_template(const fido_bio_template_array_t *ta, size_t idx) +{ + if (idx >= ta->n_alloc) + return (NULL); + + return (&ta->ptr[idx]); +} + +fido_bio_enroll_t * +fido_bio_enroll_new(void) +{ + return (calloc(1, sizeof(fido_bio_enroll_t))); +} + +fido_bio_info_t * +fido_bio_info_new(void) +{ + return (calloc(1, sizeof(fido_bio_info_t))); +} + +uint8_t +fido_bio_info_type(const fido_bio_info_t *i) +{ + return (i->type); +} + +uint8_t +fido_bio_info_max_samples(const fido_bio_info_t *i) +{ + return (i->max_samples); +} + +void +fido_bio_enroll_free(fido_bio_enroll_t **ep) +{ + fido_bio_enroll_t *e; + + if (ep == NULL || (e = *ep) == NULL) + return; + + bio_reset_enroll(e); + + free(e); + *ep = NULL; +} + +void +fido_bio_info_free(fido_bio_info_t **ip) +{ + fido_bio_info_t *i; + + if (ip == NULL || (i = *ip) == NULL) + return; + + free(i); + *ip = NULL; +} + +uint8_t +fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e) +{ + return (e->remaining_samples); +} + +uint8_t +fido_bio_enroll_last_status(const fido_bio_enroll_t *e) +{ + return (e->last_status); +} diff --git a/src/blob.c b/src/blob.c new file mode 100644 index 0000000..d4eae70 --- /dev/null +++ b/src/blob.c @@ -0,0 +1,102 @@ +/* + * 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 +#include "fido.h" + +fido_blob_t * +fido_blob_new(void) +{ + return (calloc(1, sizeof(fido_blob_t))); +} + +int +fido_blob_set(fido_blob_t *b, const unsigned char *ptr, size_t len) +{ + if (b->ptr != NULL) { + explicit_bzero(b->ptr, b->len); + free(b->ptr); + b->ptr = NULL; + } + + b->len = 0; + + if (ptr == NULL || len == 0) { + fido_log_debug("%s: ptr=%p, len=%zu", __func__, + (const void *)ptr, len); + return (-1); + } + + if ((b->ptr = malloc(len)) == NULL) { + fido_log_debug("%s: malloc", __func__); + return (-1); + } + + memcpy(b->ptr, ptr, len); + b->len = len; + + return (0); +} + +void +fido_blob_free(fido_blob_t **bp) +{ + fido_blob_t *b; + + if (bp == NULL || (b = *bp) == NULL) + return; + + if (b->ptr) { + explicit_bzero(b->ptr, b->len); + free(b->ptr); + } + + explicit_bzero(b, sizeof(*b)); + free(b); + + *bp = NULL; +} + +void +fido_free_blob_array(fido_blob_array_t *array) +{ + if (array->ptr == NULL) + return; + + for (size_t i = 0; i < array->len; i++) { + fido_blob_t *b = &array->ptr[i]; + if (b->ptr != NULL) { + explicit_bzero(b->ptr, b->len); + free(b->ptr); + b->ptr = NULL; + } + } + + free(array->ptr); + array->ptr = NULL; + array->len = 0; +} + +cbor_item_t * +fido_blob_encode(const fido_blob_t *b) +{ + if (b == NULL || b->ptr == NULL) + return (NULL); + + return (cbor_build_bytestring(b->ptr, b->len)); +} + +int +fido_blob_decode(const cbor_item_t *item, fido_blob_t *b) +{ + return (cbor_bytestring_copy(item, &b->ptr, &b->len)); +} + +int +fido_blob_is_empty(const fido_blob_t *b) +{ + return (b->ptr == NULL || b->len == 0); +} diff --git a/src/blob.h b/src/blob.h new file mode 100644 index 0000000..24fdc23 --- /dev/null +++ b/src/blob.h @@ -0,0 +1,28 @@ +/* + * 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 _BLOB_H +#define _BLOB_H + +typedef struct fido_blob { + unsigned char *ptr; + size_t len; +} fido_blob_t; + +typedef struct fido_blob_array { + fido_blob_t *ptr; + size_t len; +} fido_blob_array_t; + +cbor_item_t *fido_blob_encode(const fido_blob_t *); +fido_blob_t *fido_blob_new(void); +int fido_blob_decode(const cbor_item_t *, fido_blob_t *); +int fido_blob_is_empty(const fido_blob_t *); +int fido_blob_set(fido_blob_t *, const unsigned char *, size_t); +void fido_blob_free(fido_blob_t **); +void fido_free_blob_array(fido_blob_array_t *); + +#endif /* !_BLOB_H */ diff --git a/src/buf.c b/src/buf.c new file mode 100644 index 0000000..4646476 --- /dev/null +++ b/src/buf.c @@ -0,0 +1,34 @@ +/* + * 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 +#include "fido.h" + +int +fido_buf_read(const unsigned char **buf, size_t *len, void *dst, size_t count) +{ + if (count > *len) + return (-1); + + memcpy(dst, *buf, count); + *buf += count; + *len -= count; + + return (0); +} + +int +fido_buf_write(unsigned char **buf, size_t *len, const void *src, size_t count) +{ + if (count > *len) + return (-1); + + memcpy(*buf, src, count); + *buf += count; + *len -= count; + + return (0); +} diff --git a/src/cbor.c b/src/cbor.c new file mode 100644 index 0000000..3e03592 --- /dev/null +++ b/src/cbor.c @@ -0,0 +1,1520 @@ +/* + * 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 +#include +#include + +#include +#include "fido.h" + +static int +check_key_type(cbor_item_t *item) +{ + if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT || + item->type == CBOR_TYPE_STRING) + return (0); + + fido_log_debug("%s: invalid type: %d", __func__, item->type); + + return (-1); +} + +/* + * Validate CTAP2 canonical CBOR encoding rules for maps. + */ +static int +ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr) +{ + size_t curr_len; + size_t prev_len; + + if (check_key_type(prev) < 0 || check_key_type(curr) < 0) + return (-1); + + if (prev->type != curr->type) { + if (prev->type < curr->type) + return (0); + fido_log_debug("%s: unsorted types", __func__); + return (-1); + } + + if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) { + if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) && + cbor_get_int(curr) > cbor_get_int(prev)) + return (0); + } else { + curr_len = cbor_string_length(curr); + prev_len = cbor_string_length(prev); + + if (curr_len > prev_len || (curr_len == prev_len && + memcmp(cbor_string_handle(prev), cbor_string_handle(curr), + curr_len) < 0)) + return (0); + } + + fido_log_debug("%s: invalid cbor", __func__); + + return (-1); +} + +int +cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *, + const cbor_item_t *, void *)) +{ + struct cbor_pair *v; + size_t n; + + if ((v = cbor_map_handle(item)) == NULL) { + fido_log_debug("%s: cbor_map_handle", __func__); + return (-1); + } + + n = cbor_map_size(item); + + for (size_t i = 0; i < n; i++) { + if (v[i].key == NULL || v[i].value == NULL) { + fido_log_debug("%s: key=%p, value=%p for i=%zu", + __func__, (void *)v[i].key, (void *)v[i].value, i); + return (-1); + } + if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) { + fido_log_debug("%s: ctap_check_cbor", __func__); + return (-1); + } + if (f(v[i].key, v[i].value, arg) < 0) { + fido_log_debug("%s: iterator < 0 on i=%zu", __func__, + i); + return (-1); + } + } + + return (0); +} + +int +cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *, + void *)) +{ + cbor_item_t **v; + size_t n; + + if ((v = cbor_array_handle(item)) == NULL) { + fido_log_debug("%s: cbor_array_handle", __func__); + return (-1); + } + + n = cbor_array_size(item); + + for (size_t i = 0; i < n; i++) + if (v[i] == NULL || f(v[i], arg) < 0) { + fido_log_debug("%s: iterator < 0 on i=%zu,%p", + __func__, i, (void *)v[i]); + return (-1); + } + + return (0); +} + +int +cbor_parse_reply(const unsigned char *blob, size_t blob_len, void *arg, + int(*parser)(const cbor_item_t *, const cbor_item_t *, void *)) +{ + cbor_item_t *item = NULL; + struct cbor_load_result cbor; + int r; + + if (blob_len < 1) { + fido_log_debug("%s: blob_len=%zu", __func__, blob_len); + r = FIDO_ERR_RX; + goto fail; + } + + if (blob[0] != FIDO_OK) { + fido_log_debug("%s: blob[0]=0x%02x", __func__, blob[0]); + r = blob[0]; + goto fail; + } + + if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) { + fido_log_debug("%s: cbor_load", __func__); + r = FIDO_ERR_RX_NOT_CBOR; + goto fail; + } + + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + r = FIDO_ERR_RX_INVALID_CBOR; + goto fail; + } + + if (cbor_map_iter(item, arg, parser) < 0) { + fido_log_debug("%s: cbor_map_iter", __func__); + r = FIDO_ERR_RX_INVALID_CBOR; + goto fail; + } + + r = FIDO_OK; +fail: + if (item != NULL) + cbor_decref(&item); + + return (r); +} + +void +cbor_vector_free(cbor_item_t **item, size_t len) +{ + for (size_t i = 0; i < len; i++) + if (item[i] != NULL) + cbor_decref(&item[i]); +} + +int +cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len) +{ + if (*buf != NULL || *len != 0) { + fido_log_debug("%s: dup", __func__); + return (-1); + } + + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + *len = cbor_bytestring_length(item); + if ((*buf = malloc(*len)) == NULL) { + *len = 0; + return (-1); + } + + memcpy(*buf, cbor_bytestring_handle(item), *len); + + return (0); +} + +int +cbor_string_copy(const cbor_item_t *item, char **str) +{ + size_t len; + + if (*str != NULL) { + fido_log_debug("%s: dup", __func__); + return (-1); + } + + if (cbor_isa_string(item) == false || + cbor_string_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + if ((len = cbor_string_length(item)) == SIZE_MAX || + (*str = malloc(len + 1)) == NULL) + return (-1); + + memcpy(*str, cbor_string_handle(item), len); + (*str)[len] = '\0'; + + return (0); +} + +int +cbor_add_bytestring(cbor_item_t *item, const char *key, + const unsigned char *value, size_t value_len) +{ + struct cbor_pair pair; + int ok = -1; + + memset(&pair, 0, sizeof(pair)); + + if ((pair.key = cbor_build_string(key)) == NULL || + (pair.value = cbor_build_bytestring(value, value_len)) == NULL) { + fido_log_debug("%s: cbor_build", __func__); + goto fail; + } + + if (!cbor_map_add(item, pair)) { + fido_log_debug("%s: cbor_map_add", __func__); + goto fail; + } + + ok = 0; +fail: + if (pair.key) + cbor_decref(&pair.key); + if (pair.value) + cbor_decref(&pair.value); + + return (ok); +} + +int +cbor_add_string(cbor_item_t *item, const char *key, const char *value) +{ + struct cbor_pair pair; + int ok = -1; + + memset(&pair, 0, sizeof(pair)); + + if ((pair.key = cbor_build_string(key)) == NULL || + (pair.value = cbor_build_string(value)) == NULL) { + fido_log_debug("%s: cbor_build", __func__); + goto fail; + } + + if (!cbor_map_add(item, pair)) { + fido_log_debug("%s: cbor_map_add", __func__); + goto fail; + } + + ok = 0; +fail: + if (pair.key) + cbor_decref(&pair.key); + if (pair.value) + cbor_decref(&pair.value); + + return (ok); +} + +int +cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value) +{ + struct cbor_pair pair; + int ok = -1; + + memset(&pair, 0, sizeof(pair)); + + if ((pair.key = cbor_build_string(key)) == NULL || + (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) { + fido_log_debug("%s: cbor_build", __func__); + goto fail; + } + + if (!cbor_map_add(item, pair)) { + fido_log_debug("%s: cbor_map_add", __func__); + goto fail; + } + + ok = 0; +fail: + if (pair.key) + cbor_decref(&pair.key); + if (pair.value) + cbor_decref(&pair.value); + + return (ok); +} + +static int +cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg) +{ + struct cbor_pair pair; + int ok = -1; + + memset(&pair, 0, sizeof(pair)); + + if (arg == NULL) + return (0); /* empty argument */ + + if ((pair.key = cbor_build_uint8(n)) == NULL) { + fido_log_debug("%s: cbor_build", __func__); + goto fail; + } + + pair.value = arg; + + if (!cbor_map_add(item, pair)) { + fido_log_debug("%s: cbor_map_add", __func__); + goto fail; + } + + ok = 0; +fail: + if (pair.key) + cbor_decref(&pair.key); + + return (ok); +} + +cbor_item_t * +cbor_flatten_vector(cbor_item_t *argv[], size_t argc) +{ + cbor_item_t *map; + uint8_t i; + + if (argc > UINT8_MAX - 1) + return (NULL); + + if ((map = cbor_new_definite_map(argc)) == NULL) + return (NULL); + + for (i = 0; i < argc; i++) + if (cbor_add_arg(map, i + 1, argv[i]) < 0) + break; + + if (i != argc) { + cbor_decref(&map); + map = NULL; + } + + return (map); +} + +int +cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f) +{ + cbor_item_t *flat = NULL; + unsigned char *cbor = NULL; + size_t cbor_len; + size_t cbor_alloc_len; + int ok = -1; + + if ((flat = cbor_flatten_vector(argv, argc)) == NULL) + goto fail; + + cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len); + if (cbor_len == 0 || cbor_len == SIZE_MAX) { + fido_log_debug("%s: cbor_len=%zu", __func__, cbor_len); + goto fail; + } + + if ((f->ptr = malloc(cbor_len + 1)) == NULL) + goto fail; + + f->len = cbor_len + 1; + f->ptr[0] = cmd; + memcpy(f->ptr + 1, cbor, f->len - 1); + + ok = 0; +fail: + if (flat != NULL) + cbor_decref(&flat); + + free(cbor); + + return (ok); +} + +cbor_item_t * +cbor_encode_rp_entity(const fido_rp_t *rp) +{ + cbor_item_t *item = NULL; + + if ((item = cbor_new_definite_map(2)) == NULL) + return (NULL); + + if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) || + (rp->name && cbor_add_string(item, "name", rp->name) < 0)) { + cbor_decref(&item); + return (NULL); + } + + return (item); +} + +cbor_item_t * +cbor_encode_user_entity(const fido_user_t *user) +{ + cbor_item_t *item = NULL; + const fido_blob_t *id = &user->id; + const char *display = user->display_name; + + if ((item = cbor_new_definite_map(4)) == NULL) + return (NULL); + + if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) || + (user->icon && cbor_add_string(item, "icon", user->icon) < 0) || + (user->name && cbor_add_string(item, "name", user->name) < 0) || + (display && cbor_add_string(item, "displayName", display) < 0)) { + cbor_decref(&item); + return (NULL); + } + + return (item); +} + +cbor_item_t * +cbor_encode_pubkey_param(int cose_alg) +{ + cbor_item_t *item = NULL; + cbor_item_t *body = NULL; + struct cbor_pair alg; + int ok = -1; + + memset(&alg, 0, sizeof(alg)); + + if ((item = cbor_new_definite_array(1)) == NULL || + (body = cbor_new_definite_map(2)) == NULL || + cose_alg > -1 || cose_alg < INT16_MIN) + goto fail; + + alg.key = cbor_build_string("alg"); + + if (-cose_alg - 1 > UINT8_MAX) + alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1)); + else + alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1)); + + if (alg.key == NULL || alg.value == NULL) { + fido_log_debug("%s: cbor_build", __func__); + goto fail; + } + + if (cbor_map_add(body, alg) == false || + cbor_add_string(body, "type", "public-key") < 0 || + cbor_array_push(item, body) == false) + goto fail; + + ok = 0; +fail: + if (ok < 0) { + if (item != NULL) { + cbor_decref(&item); + item = NULL; + } + } + + if (body != NULL) + cbor_decref(&body); + if (alg.key != NULL) + cbor_decref(&alg.key); + if (alg.value != NULL) + cbor_decref(&alg.value); + + return (item); +} + +cbor_item_t * +cbor_encode_pubkey(const fido_blob_t *pubkey) +{ + cbor_item_t *cbor_key = NULL; + + if ((cbor_key = cbor_new_definite_map(2)) == NULL || + cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 || + cbor_add_string(cbor_key, "type", "public-key") < 0) { + if (cbor_key) + cbor_decref(&cbor_key); + return (NULL); + } + + return (cbor_key); +} + +cbor_item_t * +cbor_encode_pubkey_list(const fido_blob_array_t *list) +{ + cbor_item_t *array = NULL; + cbor_item_t *key = NULL; + + if ((array = cbor_new_definite_array(list->len)) == NULL) + goto fail; + + for (size_t i = 0; i < list->len; i++) { + if ((key = cbor_encode_pubkey(&list->ptr[i])) == NULL || + cbor_array_push(array, key) == false) + goto fail; + cbor_decref(&key); + } + + return (array); +fail: + if (key != NULL) + cbor_decref(&key); + if (array != NULL) + cbor_decref(&array); + + return (NULL); +} + +cbor_item_t * +cbor_encode_extensions(int ext) +{ + cbor_item_t *item = NULL; + + if (ext == 0 || ext != FIDO_EXT_HMAC_SECRET) + return (NULL); + + if ((item = cbor_new_definite_map(1)) == NULL) + return (NULL); + + if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) { + cbor_decref(&item); + return (NULL); + } + + return (item); +} + +cbor_item_t * +cbor_encode_options(fido_opt_t rk, fido_opt_t uv) +{ + cbor_item_t *item = NULL; + + if ((item = cbor_new_definite_map(2)) == NULL) + return (NULL); + + if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) || + (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) { + cbor_decref(&item); + return (NULL); + } + + return (item); +} + +cbor_item_t * +cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv) +{ + cbor_item_t *item = NULL; + + if ((item = cbor_new_definite_map(2)) == NULL) + return (NULL); + + if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) || + (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) { + cbor_decref(&item); + return (NULL); + } + + return (item); +} + +cbor_item_t * +cbor_encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data) +{ + const EVP_MD *md = NULL; + unsigned char dgst[SHA256_DIGEST_LENGTH]; + unsigned int dgst_len; + + if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr, + (int)hmac_key->len, data->ptr, (int)data->len, dgst, + &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH) + return (NULL); + + return (cbor_build_bytestring(dgst, 16)); +} + +cbor_item_t * +cbor_encode_pin_opt(void) +{ + return (cbor_build_uint8(1)); +} + +cbor_item_t * +cbor_encode_pin_enc(const fido_blob_t *key, const fido_blob_t *pin) +{ + fido_blob_t pe; + cbor_item_t *item = NULL; + + if (aes256_cbc_enc(key, pin, &pe) < 0) + return (NULL); + + item = cbor_build_bytestring(pe.ptr, pe.len); + free(pe.ptr); + + return (item); +} + +static int +sha256(const unsigned char *data, size_t data_len, fido_blob_t *digest) +{ + if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL) + return (-1); + + digest->len = SHA256_DIGEST_LENGTH; + + if (SHA256(data, data_len, digest->ptr) != digest->ptr) { + free(digest->ptr); + digest->ptr = NULL; + digest->len = 0; + return (-1); + } + + return (0); +} + +cbor_item_t * +cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin, + const fido_blob_t *pin) +{ + unsigned char dgst[SHA256_DIGEST_LENGTH]; + unsigned int dgst_len; + cbor_item_t *item = NULL; + const EVP_MD *md = NULL; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX ctx; +#else + HMAC_CTX *ctx = NULL; +#endif + fido_blob_t *npe = NULL; /* new pin, encrypted */ + fido_blob_t *ph = NULL; /* pin hash */ + fido_blob_t *phe = NULL; /* pin hash, encrypted */ + int ok = -1; + + if ((npe = fido_blob_new()) == NULL || + (ph = fido_blob_new()) == NULL || + (phe = fido_blob_new()) == NULL) + goto fail; + + if (aes256_cbc_enc(key, new_pin, npe) < 0) { + fido_log_debug("%s: aes256_cbc_enc 1", __func__); + goto fail; + } + + if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) { + fido_log_debug("%s: sha256", __func__); + goto fail; + } + + ph->len = 16; /* first 16 bytes */ + + if (aes256_cbc_enc(key, ph, phe) < 0) { + fido_log_debug("%s: aes256_cbc_enc 2", __func__); + goto fail; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX_init(&ctx); + + if ((md = EVP_sha256()) == NULL || + HMAC_Init_ex(&ctx, key->ptr, (int)key->len, md, NULL) == 0 || + HMAC_Update(&ctx, npe->ptr, (int)npe->len) == 0 || + HMAC_Update(&ctx, phe->ptr, (int)phe->len) == 0 || + HMAC_Final(&ctx, dgst, &dgst_len) == 0 || dgst_len != 32) { + fido_log_debug("%s: HMAC", __func__); + goto fail; + } +#else + if ((ctx = HMAC_CTX_new()) == NULL || + (md = EVP_sha256()) == NULL || + HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 || + HMAC_Update(ctx, npe->ptr, (int)npe->len) == 0 || + HMAC_Update(ctx, phe->ptr, (int)phe->len) == 0 || + HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) { + fido_log_debug("%s: HMAC", __func__); + goto fail; + } +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + + if ((item = cbor_build_bytestring(dgst, 16)) == NULL) { + fido_log_debug("%s: cbor_build_bytestring", __func__); + goto fail; + } + + ok = 0; +fail: + fido_blob_free(&npe); + fido_blob_free(&ph); + fido_blob_free(&phe); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (ctx != NULL) + HMAC_CTX_free(ctx); +#endif + + if (ok < 0) { + if (item != NULL) { + cbor_decref(&item); + item = NULL; + } + } + + return (item); +} + +cbor_item_t * +cbor_encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin) +{ + const EVP_MD *md = NULL; + unsigned char dgst[SHA256_DIGEST_LENGTH]; + unsigned int dgst_len; + cbor_item_t *item = NULL; + fido_blob_t *pe = NULL; + + if ((pe = fido_blob_new()) == NULL) + goto fail; + + if (aes256_cbc_enc(key, pin, pe) < 0) { + fido_log_debug("%s: aes256_cbc_enc", __func__); + goto fail; + } + + if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr, + (int)key->len, pe->ptr, (int)pe->len, dgst, &dgst_len) == NULL || + dgst_len != SHA256_DIGEST_LENGTH) { + fido_log_debug("%s: HMAC", __func__); + goto fail; + } + + item = cbor_build_bytestring(dgst, 16); +fail: + fido_blob_free(&pe); + + return (item); +} + +cbor_item_t * +cbor_encode_pin_hash_enc(const fido_blob_t *shared, const fido_blob_t *pin) +{ + cbor_item_t *item = NULL; + fido_blob_t *ph = NULL; + fido_blob_t *phe = NULL; + + if ((ph = fido_blob_new()) == NULL || (phe = fido_blob_new()) == NULL) + goto fail; + + if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) { + fido_log_debug("%s: SHA256", __func__); + goto fail; + } + + ph->len = 16; /* first 16 bytes */ + + if (aes256_cbc_enc(shared, ph, phe) < 0) { + fido_log_debug("%s: aes256_cbc_enc", __func__); + goto fail; + } + + item = cbor_build_bytestring(phe->ptr, phe->len); +fail: + fido_blob_free(&ph); + fido_blob_free(&phe); + + return (item); +} + +cbor_item_t * +cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk, + const fido_blob_t *hmac_salt) +{ + cbor_item_t *item = NULL; + cbor_item_t *param = NULL; + cbor_item_t *argv[3]; + struct cbor_pair pair; + + memset(argv, 0, sizeof(argv)); + memset(&pair, 0, sizeof(pair)); + + if (ecdh == NULL || pk == NULL || hmac_salt->ptr == NULL) { + fido_log_debug("%s: ecdh=%p, pk=%p, hmac_salt->ptr=%p", + __func__, (const void *)ecdh, (const void *)pk, + (const void *)hmac_salt->ptr); + goto fail; + } + + if (hmac_salt->len != 32 && hmac_salt->len != 64) { + fido_log_debug("%s: hmac_salt->len=%zu", __func__, + hmac_salt->len); + goto fail; + } + + /* XXX not pin, but salt */ + if ((argv[0] = es256_pk_encode(pk, 1)) == NULL || + (argv[1] = cbor_encode_pin_enc(ecdh, hmac_salt)) == NULL || + (argv[2] = cbor_encode_set_pin_auth(ecdh, hmac_salt)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + goto fail; + } + + if ((param = cbor_flatten_vector(argv, 3)) == NULL) { + fido_log_debug("%s: cbor_flatten_vector", __func__); + goto fail; + } + + if ((item = cbor_new_definite_map(1)) == NULL) { + fido_log_debug("%s: cbor_new_definite_map", __func__); + goto fail; + } + + if ((pair.key = cbor_build_string("hmac-secret")) == NULL) { + fido_log_debug("%s: cbor_build", __func__); + goto fail; + } + + pair.value = param; + + if (!cbor_map_add(item, pair)) { + fido_log_debug("%s: cbor_map_add", __func__); + cbor_decref(&item); + item = NULL; + goto fail; + } + +fail: + for (size_t i = 0; i < 3; i++) + if (argv[i] != NULL) + cbor_decref(&argv[i]); + + if (param != NULL) + cbor_decref(¶m); + if (pair.key != NULL) + cbor_decref(&pair.key); + + return (item); +} + +int +cbor_decode_fmt(const cbor_item_t *item, char **fmt) +{ + char *type = NULL; + + if (cbor_string_copy(item, &type) < 0) { + fido_log_debug("%s: cbor_string_copy", __func__); + return (-1); + } + + if (strcmp(type, "packed") && strcmp(type, "fido-u2f")) { + fido_log_debug("%s: type=%s", __func__, type); + free(type); + return (-1); + } + + *fmt = type; + + return (0); +} + +struct cose_key { + int kty; + int alg; + int crv; +}; + +static int +find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + struct cose_key *cose_key = arg; + + if (cbor_isa_uint(key) == true && + cbor_int_get_width(key) == CBOR_INT_8) { + switch (cbor_get_uint8(key)) { + case 1: + if (cbor_isa_uint(val) == false || + cbor_get_int(val) > INT_MAX || cose_key->kty != 0) { + fido_log_debug("%s: kty", __func__); + return (-1); + } + + cose_key->kty = (int)cbor_get_int(val); + + break; + case 3: + if (cbor_isa_negint(val) == false || + cbor_get_int(val) > INT_MAX || cose_key->alg != 0) { + fido_log_debug("%s: alg", __func__); + return (-1); + } + + cose_key->alg = -(int)cbor_get_int(val) - 1; + + break; + } + } else if (cbor_isa_negint(key) == true && + cbor_int_get_width(key) == CBOR_INT_8) { + if (cbor_get_uint8(key) == 0) { + /* get crv if not rsa, otherwise ignore */ + if (cbor_isa_uint(val) == true && + cbor_get_int(val) <= INT_MAX && + cose_key->crv == 0) + cose_key->crv = (int)cbor_get_int(val); + } + } + + return (0); +} + +static int +get_cose_alg(const cbor_item_t *item, int *cose_alg) +{ + struct cose_key cose_key; + + memset(&cose_key, 0, sizeof(cose_key)); + + *cose_alg = 0; + + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, &cose_key, find_cose_alg) < 0) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + switch (cose_key.alg) { + case COSE_ES256: + if (cose_key.kty != COSE_KTY_EC2 || + cose_key.crv != COSE_P256) { + fido_log_debug("%s: invalid kty/crv", __func__); + return (-1); + } + + break; + case COSE_EDDSA: + if (cose_key.kty != COSE_KTY_OKP || + cose_key.crv != COSE_ED25519) { + fido_log_debug("%s: invalid kty/crv", __func__); + return (-1); + } + + break; + case COSE_RS256: + if (cose_key.kty != COSE_KTY_RSA) { + fido_log_debug("%s: invalid kty/crv", __func__); + return (-1); + } + + break; + default: + fido_log_debug("%s: unknown alg %d", __func__, cose_key.alg); + + return (-1); + } + + *cose_alg = cose_key.alg; + + return (0); +} + +int +cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key) +{ + if (get_cose_alg(item, type) < 0) { + fido_log_debug("%s: get_cose_alg", __func__); + return (-1); + } + + switch (*type) { + case COSE_ES256: + if (es256_pk_decode(item, key) < 0) { + fido_log_debug("%s: es256_pk_decode", __func__); + return (-1); + } + break; + case COSE_RS256: + if (rs256_pk_decode(item, key) < 0) { + fido_log_debug("%s: rs256_pk_decode", __func__); + return (-1); + } + break; + case COSE_EDDSA: + if (eddsa_pk_decode(item, key) < 0) { + fido_log_debug("%s: eddsa_pk_decode", __func__); + return (-1); + } + break; + default: + fido_log_debug("%s: invalid cose_alg %d", __func__, *type); + return (-1); + } + + return (0); +} + +static int +decode_attcred(const unsigned char **buf, size_t *len, int cose_alg, + fido_attcred_t *attcred) +{ + cbor_item_t *item = NULL; + struct cbor_load_result cbor; + uint16_t id_len; + int ok = -1; + + fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, + *len); + + if (fido_buf_read(buf, len, &attcred->aaguid, + sizeof(attcred->aaguid)) < 0) { + fido_log_debug("%s: fido_buf_read aaguid", __func__); + return (-1); + } + + if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) { + fido_log_debug("%s: fido_buf_read id_len", __func__); + return (-1); + } + + attcred->id.len = (size_t)be16toh(id_len); + if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL) + return (-1); + + fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len); + + if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) { + fido_log_debug("%s: fido_buf_read id", __func__); + return (-1); + } + + if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { + fido_log_debug("%s: cbor_load", __func__); + fido_log_xxd(*buf, *len); + goto fail; + } + + if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) { + fido_log_debug("%s: cbor_decode_pubkey", __func__); + goto fail; + } + + if (attcred->type != cose_alg) { + fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__, + attcred->type, cose_alg); + goto fail; + } + + *buf += cbor.read; + *len -= cbor.read; + + ok = 0; +fail: + if (item != NULL) + cbor_decref(&item); + + return (ok); +} + +static int +decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + int *authdata_ext = arg; + char *type = NULL; + int ok = -1; + + if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) { + fido_log_debug("%s: cbor type", __func__); + ok = 0; /* ignore */ + goto out; + } + + if (cbor_isa_float_ctrl(val) == false || + cbor_float_get_width(val) != CBOR_FLOAT_0 || + cbor_is_bool(val) == false || *authdata_ext != 0) { + fido_log_debug("%s: cbor type", __func__); + goto out; + } + + if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE) + *authdata_ext |= FIDO_EXT_HMAC_SECRET; + + ok = 0; +out: + free(type); + + return (ok); +} + +static int +decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext) +{ + cbor_item_t *item = NULL; + struct cbor_load_result cbor; + int ok = -1; + + fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, + *len); + + *authdata_ext = 0; + + if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { + fido_log_debug("%s: cbor_load", __func__); + fido_log_xxd(*buf, *len); + goto fail; + } + + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_size(item) != 1 || + cbor_map_iter(item, authdata_ext, decode_extension) < 0) { + fido_log_debug("%s: cbor type", __func__); + goto fail; + } + + *buf += cbor.read; + *len -= cbor.read; + + ok = 0; +fail: + if (item != NULL) + cbor_decref(&item); + + return (ok); +} + +static int +decode_hmac_secret_aux(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_blob_t *out = arg; + char *type = NULL; + int ok = -1; + + if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) { + fido_log_debug("%s: cbor type", __func__); + ok = 0; /* ignore */ + goto out; + } + + ok = cbor_bytestring_copy(val, &out->ptr, &out->len); +out: + free(type); + + return (ok); +} + +static int +decode_hmac_secret(const unsigned char **buf, size_t *len, fido_blob_t *out) +{ + cbor_item_t *item = NULL; + struct cbor_load_result cbor; + int ok = -1; + + fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, + *len); + + if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { + fido_log_debug("%s: cbor_load", __func__); + fido_log_xxd(*buf, *len); + goto fail; + } + + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_size(item) != 1 || + cbor_map_iter(item, out, decode_hmac_secret_aux) < 0) { + fido_log_debug("%s: cbor type", __func__); + goto fail; + } + + *buf += cbor.read; + *len -= cbor.read; + + ok = 0; +fail: + if (item != NULL) + cbor_decref(&item); + + return (ok); +} + +int +cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg, + fido_blob_t *authdata_cbor, fido_authdata_t *authdata, + fido_attcred_t *attcred, int *authdata_ext) +{ + const unsigned char *buf = NULL; + size_t len; + size_t alloc_len; + + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + if (authdata_cbor->ptr != NULL || + (authdata_cbor->len = cbor_serialize_alloc(item, + &authdata_cbor->ptr, &alloc_len)) == 0) { + fido_log_debug("%s: cbor_serialize_alloc", __func__); + return (-1); + } + + buf = cbor_bytestring_handle(item); + len = cbor_bytestring_length(item); + + fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len); + + if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) { + fido_log_debug("%s: fido_buf_read", __func__); + return (-1); + } + + authdata->sigcount = be32toh(authdata->sigcount); + + if (attcred != NULL) { + if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 || + decode_attcred(&buf, &len, cose_alg, attcred) < 0) + return (-1); + } + + if (authdata_ext != NULL) { + if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 && + decode_extensions(&buf, &len, authdata_ext) < 0) + return (-1); + } + + /* XXX we should probably ensure that len == 0 at this point */ + + return (FIDO_OK); +} + +int +cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor, + fido_authdata_t *authdata, int *authdata_ext, fido_blob_t *hmac_secret_enc) +{ + const unsigned char *buf = NULL; + size_t len; + size_t alloc_len; + + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + if (authdata_cbor->ptr != NULL || + (authdata_cbor->len = cbor_serialize_alloc(item, + &authdata_cbor->ptr, &alloc_len)) == 0) { + fido_log_debug("%s: cbor_serialize_alloc", __func__); + return (-1); + } + + buf = cbor_bytestring_handle(item); + len = cbor_bytestring_length(item); + + fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len); + + if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) { + fido_log_debug("%s: fido_buf_read", __func__); + return (-1); + } + + authdata->sigcount = be32toh(authdata->sigcount); + + *authdata_ext = 0; + if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) { + /* XXX semantic leap: extensions -> hmac_secret */ + if (decode_hmac_secret(&buf, &len, hmac_secret_enc) < 0) { + fido_log_debug("%s: decode_hmac_secret", __func__); + return (-1); + } + *authdata_ext = FIDO_EXT_HMAC_SECRET; + } + + /* XXX we should probably ensure that len == 0 at this point */ + + return (FIDO_OK); +} + +static int +decode_x5c(const cbor_item_t *item, void *arg) +{ + fido_blob_t *x5c = arg; + + if (x5c->len) + return (0); /* ignore */ + + return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len)); +} + +static int +decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_attstmt_t *attstmt = arg; + char *name = NULL; + int ok = -1; + + if (cbor_string_copy(key, &name) < 0) { + fido_log_debug("%s: cbor type", __func__); + ok = 0; /* ignore */ + goto out; + } + + if (!strcmp(name, "alg")) { + if (cbor_isa_negint(val) == false || + cbor_int_get_width(val) != CBOR_INT_8 || + cbor_get_uint8(val) != -COSE_ES256 - 1) { + fido_log_debug("%s: alg", __func__); + goto out; + } + } else if (!strcmp(name, "sig")) { + if (cbor_bytestring_copy(val, &attstmt->sig.ptr, + &attstmt->sig.len) < 0) { + fido_log_debug("%s: sig", __func__); + goto out; + } + } else if (!strcmp(name, "x5c")) { + if (cbor_isa_array(val) == false || + cbor_array_is_definite(val) == false || + cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) { + fido_log_debug("%s: x5c", __func__); + goto out; + } + } + + ok = 0; +out: + free(name); + + return (ok); +} + +int +cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt) +{ + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + return (0); +} + +int +cbor_decode_uint64(const cbor_item_t *item, uint64_t *n) +{ + if (cbor_isa_uint(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + *n = cbor_get_int(item); + + return (0); +} + +static int +decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_blob_t *id = arg; + char *name = NULL; + int ok = -1; + + if (cbor_string_copy(key, &name) < 0) { + fido_log_debug("%s: cbor type", __func__); + ok = 0; /* ignore */ + goto out; + } + + if (!strcmp(name, "id")) + if (cbor_bytestring_copy(val, &id->ptr, &id->len) < 0) { + fido_log_debug("%s: cbor_bytestring_copy", __func__); + goto out; + } + + ok = 0; +out: + free(name); + + return (ok); +} + +int +cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id) +{ + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, id, decode_cred_id_entry) < 0) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + return (0); +} + +static int +decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_user_t *user = arg; + char *name = NULL; + int ok = -1; + + if (cbor_string_copy(key, &name) < 0) { + fido_log_debug("%s: cbor type", __func__); + ok = 0; /* ignore */ + goto out; + } + + if (!strcmp(name, "icon")) { + if (cbor_string_copy(val, &user->icon) < 0) { + fido_log_debug("%s: icon", __func__); + goto out; + } + } else if (!strcmp(name, "name")) { + if (cbor_string_copy(val, &user->name) < 0) { + fido_log_debug("%s: name", __func__); + goto out; + } + } else if (!strcmp(name, "displayName")) { + if (cbor_string_copy(val, &user->display_name) < 0) { + fido_log_debug("%s: display_name", __func__); + goto out; + } + } else if (!strcmp(name, "id")) { + if (cbor_bytestring_copy(val, &user->id.ptr, &user->id.len) < 0) { + fido_log_debug("%s: id", __func__); + goto out; + } + } + + ok = 0; +out: + free(name); + + return (ok); +} + +int +cbor_decode_user(const cbor_item_t *item, fido_user_t *user) +{ + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, user, decode_user_entry) < 0) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + return (0); +} + +static int +decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val, + void *arg) +{ + fido_rp_t *rp = arg; + char *name = NULL; + int ok = -1; + + if (cbor_string_copy(key, &name) < 0) { + fido_log_debug("%s: cbor type", __func__); + ok = 0; /* ignore */ + goto out; + } + + if (!strcmp(name, "id")) { + if (cbor_string_copy(val, &rp->id) < 0) { + fido_log_debug("%s: id", __func__); + goto out; + } + } else if (!strcmp(name, "name")) { + if (cbor_string_copy(val, &rp->name) < 0) { + fido_log_debug("%s: name", __func__); + goto out; + } + } + + ok = 0; +out: + free(name); + + return (ok); +} + +int +cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp) +{ + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + return (0); +} diff --git a/src/cred.c b/src/cred.c new file mode 100644 index 0000000..c4e1edb --- /dev/null +++ b/src/cred.c @@ -0,0 +1,1034 @@ +/* + * 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 +#include +#include +#include + +#include +#include "fido.h" +#include "fido/es256.h" + +static int +parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_cred_t *cred = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 1: /* fmt */ + return (cbor_decode_fmt(val, &cred->fmt)); + case 2: /* authdata */ + return (cbor_decode_cred_authdata(val, cred->type, + &cred->authdata_cbor, &cred->authdata, &cred->attcred, + &cred->authdata_ext)); + case 3: /* attestation statement */ + return (cbor_decode_attstmt(val, &cred->attstmt)); + default: /* ignore */ + fido_log_debug("%s: cbor type", __func__); + return (0); + } +} + +static int +fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin) +{ + fido_blob_t f; + fido_blob_t *ecdh = NULL; + es256_pk_t *pk = NULL; + cbor_item_t *argv[9]; + int r; + + memset(&f, 0, sizeof(f)); + memset(argv, 0, sizeof(argv)); + + if (cred->cdh.ptr == NULL || cred->type == 0) { + fido_log_debug("%s: cdh=%p, type=%d", __func__, + (void *)cred->cdh.ptr, cred->type); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL || + (argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL || + (argv[2] = cbor_encode_user_entity(&cred->user)) == NULL || + (argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + /* excluded credentials */ + if (cred->excl.len) + if ((argv[4] = cbor_encode_pubkey_list(&cred->excl)) == NULL) { + fido_log_debug("%s: cbor_encode_pubkey_list", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + /* extensions */ + if (cred->ext) + if ((argv[5] = cbor_encode_extensions(cred->ext)) == NULL) { + fido_log_debug("%s: cbor_encode_extensions", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + /* options */ + if (cred->rk != FIDO_OPT_OMIT || cred->uv != FIDO_OPT_OMIT) + if ((argv[6] = cbor_encode_options(cred->rk, + cred->uv)) == NULL) { + fido_log_debug("%s: cbor_encode_options", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + /* pin authentication */ + if (pin) { + if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { + fido_log_debug("%s: fido_do_ecdh", __func__); + goto fail; + } + if ((r = cbor_add_pin_params(dev, &cred->cdh, pk, ecdh, pin, + &argv[7], &argv[8])) != FIDO_OK) { + fido_log_debug("%s: cbor_add_pin_params", __func__); + goto fail; + } + } + + /* framing and transmission */ + if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, 9, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + es256_pk_free(&pk); + fido_blob_free(&ecdh); + cbor_vector_free(argv, nitems(argv)); + free(f.ptr); + + return (r); +} + +static int +fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + fido_cred_reset_rx(cred); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred, + parse_makecred_reply)) != FIDO_OK) { + fido_log_debug("%s: parse_makecred_reply", __func__); + return (r); + } + + if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) || + fido_blob_is_empty(&cred->attcred.id) || + fido_blob_is_empty(&cred->attstmt.sig)) { + fido_cred_reset_rx(cred); + return (FIDO_ERR_INVALID_CBOR); + } + + return (FIDO_OK); +} + +static int +fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int ms) +{ + int r; + + if ((r = fido_dev_make_cred_tx(dev, cred, pin)) != FIDO_OK || + (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) +{ + if (fido_dev_is_fido2(dev) == false) { + if (pin != NULL || cred->rk == FIDO_OPT_TRUE || cred->ext != 0) + return (FIDO_ERR_UNSUPPORTED_OPTION); + return (u2f_register(dev, cred, -1)); + } + + return (fido_dev_make_cred_wait(dev, cred, pin, -1)); +} + +static int +check_extensions(int authdata_ext, int ext) +{ + if (authdata_ext != ext) { + fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__, + authdata_ext, ext); + return (-1); + } + + return (0); +} + +int +fido_check_rp_id(const char *id, const unsigned char *obtained_hash) +{ + unsigned char expected_hash[SHA256_DIGEST_LENGTH]; + + explicit_bzero(expected_hash, sizeof(expected_hash)); + + if (SHA256((const unsigned char *)id, strlen(id), + expected_hash) != expected_hash) { + fido_log_debug("%s: sha256", __func__); + return (-1); + } + + return (timingsafe_bcmp(expected_hash, obtained_hash, + SHA256_DIGEST_LENGTH)); +} + +static int +get_signed_hash_packed(fido_blob_t *dgst, const fido_blob_t *clientdata, + const fido_blob_t *authdata_cbor) +{ + cbor_item_t *item = NULL; + unsigned char *authdata_ptr = NULL; + size_t authdata_len; + struct cbor_load_result cbor; + SHA256_CTX ctx; + int ok = -1; + + if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len, + &cbor)) == NULL) { + fido_log_debug("%s: cbor_load", __func__); + goto fail; + } + + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + goto fail; + } + + authdata_ptr = cbor_bytestring_handle(item); + authdata_len = cbor_bytestring_length(item); + + if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 || + SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 || + SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 || + SHA256_Final(dgst->ptr, &ctx) == 0) { + fido_log_debug("%s: sha256", __func__); + goto fail; + } + + ok = 0; +fail: + if (item != NULL) + cbor_decref(&item); + + return (ok); +} + +static int +get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id, + size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id, + const es256_pk_t *pk) +{ + const uint8_t zero = 0; + const uint8_t four = 4; /* uncompressed point */ + SHA256_CTX ctx; + + if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 || + SHA256_Update(&ctx, &zero, sizeof(zero)) == 0 || + SHA256_Update(&ctx, rp_id, rp_id_len) == 0 || + SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 || + SHA256_Update(&ctx, id->ptr, id->len) == 0 || + SHA256_Update(&ctx, &four, sizeof(four)) == 0 || + SHA256_Update(&ctx, pk->x, sizeof(pk->x)) == 0 || + SHA256_Update(&ctx, pk->y, sizeof(pk->y)) == 0 || + SHA256_Final(dgst->ptr, &ctx) == 0) { + fido_log_debug("%s: sha256", __func__); + return (-1); + } + + return (0); +} + +static int +verify_sig(const fido_blob_t *dgst, const fido_blob_t *x5c, + const fido_blob_t *sig) +{ + BIO *rawcert = NULL; + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; + EC_KEY *ec; + int ok = -1; + + /* openssl needs ints */ + if (dgst->len > INT_MAX || x5c->len > INT_MAX || sig->len > INT_MAX) { + fido_log_debug("%s: dgst->len=%zu, x5c->len=%zu, sig->len=%zu", + __func__, dgst->len, x5c->len, sig->len); + return (-1); + } + + /* fetch key from x509 */ + if ((rawcert = BIO_new_mem_buf(x5c->ptr, (int)x5c->len)) == NULL || + (cert = d2i_X509_bio(rawcert, NULL)) == NULL || + (pkey = X509_get_pubkey(cert)) == NULL || + (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) { + fido_log_debug("%s: x509 key", __func__); + goto fail; + } + + if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr, + (int)sig->len, ec) != 1) { + fido_log_debug("%s: ECDSA_verify", __func__); + goto fail; + } + + ok = 0; +fail: + if (rawcert != NULL) + BIO_free(rawcert); + if (cert != NULL) + X509_free(cert); + if (pkey != NULL) + EVP_PKEY_free(pkey); + + return (ok); +} + +int +fido_cred_verify(const fido_cred_t *cred) +{ + unsigned char buf[SHA256_DIGEST_LENGTH]; + fido_blob_t dgst; + int r; + + dgst.ptr = buf; + dgst.len = sizeof(buf); + + /* do we have everything we need? */ + if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL || + cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL || + cred->fmt == NULL || cred->attcred.id.ptr == NULL || + cred->rp.id == NULL) { + fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, " + "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr, + (void *)cred->authdata_cbor.ptr, + (void *)cred->attstmt.x5c.ptr, + (void *)cred->attstmt.sig.ptr, (void *)cred->fmt, + (void *)cred->attcred.id.ptr, cred->rp.id); + r = FIDO_ERR_INVALID_ARGUMENT; + goto out; + } + + if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) { + fido_log_debug("%s: fido_check_rp_id", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE, + cred->uv) < 0) { + fido_log_debug("%s: fido_check_flags", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (check_extensions(cred->authdata_ext, cred->ext) < 0) { + fido_log_debug("%s: check_extensions", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (!strcmp(cred->fmt, "packed")) { + if (get_signed_hash_packed(&dgst, &cred->cdh, + &cred->authdata_cbor) < 0) { + fido_log_debug("%s: get_signed_hash_packed", __func__); + r = FIDO_ERR_INTERNAL; + goto out; + } + } else { + if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash, + sizeof(cred->authdata.rp_id_hash), &cred->cdh, + &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) { + fido_log_debug("%s: get_signed_hash_u2f", __func__); + r = FIDO_ERR_INTERNAL; + goto out; + } + } + + if (verify_sig(&dgst, &cred->attstmt.x5c, &cred->attstmt.sig) < 0) { + fido_log_debug("%s: verify_sig", __func__); + r = FIDO_ERR_INVALID_SIG; + goto out; + } + + r = FIDO_OK; +out: + explicit_bzero(buf, sizeof(buf)); + + return (r); +} + +int +fido_cred_verify_self(const fido_cred_t *cred) +{ + unsigned char buf[SHA256_DIGEST_LENGTH]; + fido_blob_t dgst; + int ok = -1; + int r; + + dgst.ptr = buf; + dgst.len = sizeof(buf); + + /* do we have everything we need? */ + if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL || + cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL || + cred->fmt == NULL || cred->attcred.id.ptr == NULL || + cred->rp.id == NULL) { + fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, " + "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr, + (void *)cred->authdata_cbor.ptr, + (void *)cred->attstmt.x5c.ptr, + (void *)cred->attstmt.sig.ptr, (void *)cred->fmt, + (void *)cred->attcred.id.ptr, cred->rp.id); + r = FIDO_ERR_INVALID_ARGUMENT; + goto out; + } + + if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) { + fido_log_debug("%s: fido_check_rp_id", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE, + cred->uv) < 0) { + fido_log_debug("%s: fido_check_flags", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (check_extensions(cred->authdata_ext, cred->ext) < 0) { + fido_log_debug("%s: check_extensions", __func__); + r = FIDO_ERR_INVALID_PARAM; + goto out; + } + + if (!strcmp(cred->fmt, "packed")) { + if (get_signed_hash_packed(&dgst, &cred->cdh, + &cred->authdata_cbor) < 0) { + fido_log_debug("%s: get_signed_hash_packed", __func__); + r = FIDO_ERR_INTERNAL; + goto out; + } + } else { + if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash, + sizeof(cred->authdata.rp_id_hash), &cred->cdh, + &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) { + fido_log_debug("%s: get_signed_hash_u2f", __func__); + r = FIDO_ERR_INTERNAL; + goto out; + } + } + + switch (cred->attcred.type) { + case COSE_ES256: + ok = fido_verify_sig_es256(&dgst, &cred->attcred.pubkey.es256, + &cred->attstmt.sig); + break; + case COSE_RS256: + ok = fido_verify_sig_rs256(&dgst, &cred->attcred.pubkey.rs256, + &cred->attstmt.sig); + break; + case COSE_EDDSA: + ok = fido_verify_sig_eddsa(&dgst, &cred->attcred.pubkey.eddsa, + &cred->attstmt.sig); + break; + default: + fido_log_debug("%s: unsupported cose_alg %d", __func__, + cred->attcred.type); + r = FIDO_ERR_UNSUPPORTED_OPTION; + goto out; + } + + if (ok < 0) + r = FIDO_ERR_INVALID_SIG; + else + r = FIDO_OK; + +out: + explicit_bzero(buf, sizeof(buf)); + + return (r); +} + +fido_cred_t * +fido_cred_new(void) +{ + return (calloc(1, sizeof(fido_cred_t))); +} + +static void +fido_cred_clean_authdata(fido_cred_t *cred) +{ + free(cred->authdata_cbor.ptr); + free(cred->attcred.id.ptr); + + memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext)); + memset(&cred->authdata_cbor, 0, sizeof(cred->authdata_cbor)); + memset(&cred->authdata, 0, sizeof(cred->authdata)); + memset(&cred->attcred, 0, sizeof(cred->attcred)); +} + +void +fido_cred_reset_tx(fido_cred_t *cred) +{ + free(cred->cdh.ptr); + free(cred->rp.id); + free(cred->rp.name); + free(cred->user.id.ptr); + free(cred->user.icon); + free(cred->user.name); + free(cred->user.display_name); + fido_free_blob_array(&cred->excl); + + memset(&cred->cdh, 0, sizeof(cred->cdh)); + memset(&cred->rp, 0, sizeof(cred->rp)); + memset(&cred->user, 0, sizeof(cred->user)); + memset(&cred->excl, 0, sizeof(cred->excl)); + + cred->type = 0; + cred->ext = 0; + cred->rk = FIDO_OPT_OMIT; + cred->uv = FIDO_OPT_OMIT; +} + +static void +fido_cred_clean_x509(fido_cred_t *cred) +{ + free(cred->attstmt.x5c.ptr); + cred->attstmt.x5c.ptr = NULL; + cred->attstmt.x5c.len = 0; +} + +static void +fido_cred_clean_sig(fido_cred_t *cred) +{ + free(cred->attstmt.sig.ptr); + cred->attstmt.sig.ptr = NULL; + cred->attstmt.sig.len = 0; +} + +void +fido_cred_reset_rx(fido_cred_t *cred) +{ + free(cred->fmt); + cred->fmt = NULL; + + fido_cred_clean_authdata(cred); + fido_cred_clean_x509(cred); + fido_cred_clean_sig(cred); +} + +void +fido_cred_free(fido_cred_t **cred_p) +{ + fido_cred_t *cred; + + if (cred_p == NULL || (cred = *cred_p) == NULL) + return; + + fido_cred_reset_tx(cred); + fido_cred_reset_rx(cred); + + free(cred); + + *cred_p = NULL; +} + +int +fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len) +{ + cbor_item_t *item = NULL; + struct cbor_load_result cbor; + int r; + + fido_cred_clean_authdata(cred); + + if (ptr == NULL || len == 0) { + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if ((item = cbor_load(ptr, len, &cbor)) == NULL) { + fido_log_debug("%s: cbor_load", __func__); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor, + &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) { + fido_log_debug("%s: cbor_decode_cred_authdata", __func__); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + r = FIDO_OK; +fail: + if (item != NULL) + cbor_decref(&item); + + if (r != FIDO_OK) + fido_cred_clean_authdata(cred); + + return (r); + +} + +int +fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr, + size_t len) +{ + cbor_item_t *item = NULL; + int r; + + fido_cred_clean_authdata(cred); + + if (ptr == NULL || len == 0) { + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if ((item = cbor_build_bytestring(ptr, len)) == NULL) { + fido_log_debug("%s: cbor_build_bytestring", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor, + &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) { + fido_log_debug("%s: cbor_decode_cred_authdata", __func__); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + r = FIDO_OK; +fail: + if (item != NULL) + cbor_decref(&item); + + if (r != FIDO_OK) + fido_cred_clean_authdata(cred); + + return (r); + +} + +int +fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len) +{ + unsigned char *x509; + + fido_cred_clean_x509(cred); + + if (ptr == NULL || len == 0) + return (FIDO_ERR_INVALID_ARGUMENT); + if ((x509 = malloc(len)) == NULL) + return (FIDO_ERR_INTERNAL); + + memcpy(x509, ptr, len); + cred->attstmt.x5c.ptr = x509; + cred->attstmt.x5c.len = len; + + return (FIDO_OK); +} + +int +fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len) +{ + unsigned char *sig; + + fido_cred_clean_sig(cred); + + if (ptr == NULL || len == 0) + return (FIDO_ERR_INVALID_ARGUMENT); + if ((sig = malloc(len)) == NULL) + return (FIDO_ERR_INTERNAL); + + memcpy(sig, ptr, len); + cred->attstmt.sig.ptr = sig; + cred->attstmt.sig.len = len; + + return (FIDO_OK); +} + +int +fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len) +{ + fido_blob_t id_blob; + fido_blob_t *list_ptr; + + memset(&id_blob, 0, sizeof(id_blob)); + + if (fido_blob_set(&id_blob, id_ptr, id_len) < 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + if (cred->excl.len == SIZE_MAX) { + free(id_blob.ptr); + return (FIDO_ERR_INVALID_ARGUMENT); + } + + if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len, + cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) { + free(id_blob.ptr); + return (FIDO_ERR_INTERNAL); + } + + list_ptr[cred->excl.len++] = id_blob; + cred->excl.ptr = list_ptr; + + return (FIDO_OK); +} + +int +fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash, + size_t hash_len) +{ + if (fido_blob_set(&cred->cdh, hash, hash_len) < 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (FIDO_OK); +} + +int +fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name) +{ + fido_rp_t *rp = &cred->rp; + + if (rp->id != NULL) { + free(rp->id); + rp->id = NULL; + } + if (rp->name != NULL) { + free(rp->name); + rp->name = NULL; + } + + if (id != NULL && (rp->id = strdup(id)) == NULL) + goto fail; + if (name != NULL && (rp->name = strdup(name)) == NULL) + goto fail; + + return (FIDO_OK); +fail: + free(rp->id); + free(rp->name); + rp->id = NULL; + rp->name = NULL; + + return (FIDO_ERR_INTERNAL); +} + +int +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) +{ + fido_user_t *up = &cred->user; + + if (up->id.ptr != NULL) { + free(up->id.ptr); + up->id.ptr = NULL; + up->id.len = 0; + } + if (up->name != NULL) { + free(up->name); + up->name = NULL; + } + if (up->display_name != NULL) { + free(up->display_name); + up->display_name = NULL; + } + if (up->icon != NULL) { + free(up->icon); + up->icon = NULL; + } + + if (user_id != NULL) { + if ((up->id.ptr = malloc(user_id_len)) == NULL) + goto fail; + memcpy(up->id.ptr, user_id, user_id_len); + up->id.len = user_id_len; + } + if (name != NULL && (up->name = strdup(name)) == NULL) + goto fail; + if (display_name != NULL && + (up->display_name = strdup(display_name)) == NULL) + goto fail; + if (icon != NULL && (up->icon = strdup(icon)) == NULL) + goto fail; + + return (FIDO_OK); +fail: + free(up->id.ptr); + free(up->name); + free(up->display_name); + free(up->icon); + + up->id.ptr = NULL; + up->id.len = 0; + up->name = NULL; + up->display_name = NULL; + up->icon = NULL; + + return (FIDO_ERR_INTERNAL); +} + +int +fido_cred_set_extensions(fido_cred_t *cred, int ext) +{ + if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET) + return (FIDO_ERR_INVALID_ARGUMENT); + + cred->ext = ext; + + return (FIDO_OK); +} + +int +fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv) +{ + cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; + cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; + + return (FIDO_OK); +} + +int +fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk) +{ + cred->rk = rk; + + return (FIDO_OK); +} + +int +fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv) +{ + cred->uv = uv; + + return (FIDO_OK); +} + +int +fido_cred_set_fmt(fido_cred_t *cred, const char *fmt) +{ + free(cred->fmt); + cred->fmt = NULL; + + if (fmt == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f")) + return (FIDO_ERR_INVALID_ARGUMENT); + + if ((cred->fmt = strdup(fmt)) == NULL) + return (FIDO_ERR_INTERNAL); + + return (FIDO_OK); +} + +int +fido_cred_set_type(fido_cred_t *cred, int cose_alg) +{ + if ((cose_alg != COSE_ES256 && cose_alg != COSE_RS256 && + cose_alg != COSE_EDDSA) || cred->type != 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + cred->type = cose_alg; + + return (FIDO_OK); +} + +int +fido_cred_type(const fido_cred_t *cred) +{ + return (cred->type); +} + +uint8_t +fido_cred_flags(const fido_cred_t *cred) +{ + return (cred->authdata.flags); +} + +const unsigned char * +fido_cred_clientdata_hash_ptr(const fido_cred_t *cred) +{ + return (cred->cdh.ptr); +} + +size_t +fido_cred_clientdata_hash_len(const fido_cred_t *cred) +{ + return (cred->cdh.len); +} + +const unsigned char * +fido_cred_x5c_ptr(const fido_cred_t *cred) +{ + return (cred->attstmt.x5c.ptr); +} + +size_t +fido_cred_x5c_len(const fido_cred_t *cred) +{ + return (cred->attstmt.x5c.len); +} + +const unsigned char * +fido_cred_sig_ptr(const fido_cred_t *cred) +{ + return (cred->attstmt.sig.ptr); +} + +size_t +fido_cred_sig_len(const fido_cred_t *cred) +{ + return (cred->attstmt.sig.len); +} + +const unsigned char * +fido_cred_authdata_ptr(const fido_cred_t *cred) +{ + return (cred->authdata_cbor.ptr); +} + +size_t +fido_cred_authdata_len(const fido_cred_t *cred) +{ + return (cred->authdata_cbor.len); +} + +const unsigned char * +fido_cred_pubkey_ptr(const fido_cred_t *cred) +{ + const void *ptr; + + switch (cred->attcred.type) { + case COSE_ES256: + ptr = &cred->attcred.pubkey.es256; + break; + case COSE_RS256: + ptr = &cred->attcred.pubkey.rs256; + break; + case COSE_EDDSA: + ptr = &cred->attcred.pubkey.eddsa; + break; + default: + ptr = NULL; + break; + } + + return (ptr); +} + +size_t +fido_cred_pubkey_len(const fido_cred_t *cred) +{ + size_t len; + + switch (cred->attcred.type) { + case COSE_ES256: + len = sizeof(cred->attcred.pubkey.es256); + break; + case COSE_RS256: + len = sizeof(cred->attcred.pubkey.rs256); + break; + case COSE_EDDSA: + len = sizeof(cred->attcred.pubkey.eddsa); + break; + default: + len = 0; + break; + } + + return (len); +} + +const unsigned char * +fido_cred_id_ptr(const fido_cred_t *cred) +{ + return (cred->attcred.id.ptr); +} + +size_t +fido_cred_id_len(const fido_cred_t *cred) +{ + return (cred->attcred.id.len); +} + +const char * +fido_cred_fmt(const fido_cred_t *cred) +{ + return (cred->fmt); +} + +const char * +fido_cred_rp_id(const fido_cred_t *cred) +{ + return (cred->rp.id); +} + +const char * +fido_cred_rp_name(const fido_cred_t *cred) +{ + return (cred->rp.name); +} + +const char * +fido_cred_user_name(const fido_cred_t *cred) +{ + return (cred->user.name); +} + +const char * +fido_cred_display_name(const fido_cred_t *cred) +{ + return (cred->user.display_name); +} + +const unsigned char * +fido_cred_user_id_ptr(const fido_cred_t *cred) +{ + return (cred->user.id.ptr); +} + +size_t +fido_cred_user_id_len(const fido_cred_t *cred) +{ + return (cred->user.id.len); +} diff --git a/src/credman.c b/src/credman.c new file mode 100644 index 0000000..76327e5 --- /dev/null +++ b/src/credman.c @@ -0,0 +1,736 @@ +/* + * 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 + +#include + +#include "fido.h" +#include "fido/credman.h" +#include "fido/es256.h" + +#define CMD_CRED_METADATA 0x01 +#define CMD_RP_BEGIN 0x02 +#define CMD_RP_NEXT 0x03 +#define CMD_RK_BEGIN 0x04 +#define CMD_RK_NEXT 0x05 +#define CMD_DELETE_CRED 0x06 + +static int +credman_grow_array(void **ptr, size_t *n_alloc, size_t *n_rx, size_t n, + size_t size) +{ + void *new_ptr; + +#ifdef FIDO_FUZZ + if (n > UINT8_MAX) { + fido_log_debug("%s: n > UINT8_MAX", __func__); + return (-1); + } +#endif + + if (n < *n_alloc) + return (0); + + /* sanity check */ + if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) { + fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n, + *n_rx, *n_alloc); + return (-1); + } + + if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL) + return (-1); + + *ptr = new_ptr; + *n_alloc = n; + + return (0); +} + +static int +credman_prepare_hmac(uint8_t cmd, const fido_blob_t *body, cbor_item_t **param, + fido_blob_t *hmac_data) +{ + cbor_item_t *param_cbor[2]; + size_t n; + int ok = -1; + + memset(¶m_cbor, 0, sizeof(param_cbor)); + + if (body == NULL) + return (fido_blob_set(hmac_data, &cmd, sizeof(cmd))); + + switch (cmd) { + case CMD_RK_BEGIN: + n = 1; + param_cbor[n - 1] = fido_blob_encode(body); + break; + case CMD_DELETE_CRED: + n = 2; + param_cbor[n - 1] = cbor_encode_pubkey(body); + break; + default: + fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd); + return (-1); + } + + if (param_cbor[n - 1] == NULL) { + fido_log_debug("%s: cbor encode", __func__); + return (-1); + } + if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) { + fido_log_debug("%s: cbor_flatten_vector", __func__); + goto fail; + } + if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) { + fido_log_debug("%s: cbor_build_frame", __func__); + goto fail; + } + + ok = 0; +fail: + cbor_vector_free(param_cbor, nitems(param_cbor)); + + return (ok); +} + +static int +credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param, + const char *pin) +{ + fido_blob_t f; + fido_blob_t *ecdh = NULL; + fido_blob_t hmac; + es256_pk_t *pk = NULL; + cbor_item_t *argv[4]; + int r = FIDO_ERR_INTERNAL; + + memset(&f, 0, sizeof(f)); + memset(&hmac, 0, sizeof(hmac)); + memset(&argv, 0, sizeof(argv)); + + /* subCommand */ + if ((argv[0] = cbor_build_uint8(cmd)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + goto fail; + } + + /* pinProtocol, pinAuth */ + if (pin != NULL) { + if (credman_prepare_hmac(cmd, param, &argv[1], &hmac) < 0) { + fido_log_debug("%s: credman_prepare_hmac", __func__); + goto fail; + } + if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { + fido_log_debug("%s: fido_do_ecdh", __func__); + goto fail; + } + if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin, + &argv[3], &argv[2])) != FIDO_OK) { + fido_log_debug("%s: cbor_add_pin_params", __func__); + goto fail; + } + } + + /* framing and transmission */ + if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, 4, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + es256_pk_free(&pk); + fido_blob_free(&ecdh); + cbor_vector_free(argv, nitems(argv)); + free(f.ptr); + free(hmac.ptr); + + return (r); +} + +static int +credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val, + void *arg) +{ + fido_credman_metadata_t *metadata = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 1: + return (cbor_decode_uint64(val, &metadata->rk_existing)); + case 2: + return (cbor_decode_uint64(val, &metadata->rk_remaining)); + default: + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } +} + +static int +credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[512]; + int reply_len; + int r; + + memset(metadata, 0, sizeof(*metadata)); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, metadata, + credman_parse_metadata)) != FIDO_OK) { + fido_log_debug("%s: credman_parse_metadata", __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata, + const char *pin, int ms) +{ + int r; + + if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin)) != FIDO_OK || + (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, + const char *pin) +{ + if (fido_dev_is_fido2(dev) == false) + return (FIDO_ERR_INVALID_COMMAND); + if (pin == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (credman_get_metadata_wait(dev, metadata, pin, -1)); +} + +static int +credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_cred_t *cred = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 6: /* user entity */ + return (cbor_decode_user(val, &cred->user)); + case 7: + return (cbor_decode_cred_id(val, &cred->attcred.id)); + case 8: + if (cbor_decode_pubkey(val, &cred->attcred.type, + &cred->attcred.pubkey) < 0) + return (-1); + cred->type = cred->attcred.type; /* XXX */ + return (0); + default: + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } +} + +static void +credman_reset_rk(fido_credman_rk_t *rk) +{ + for (size_t i = 0; i < rk->n_alloc; i++) { + fido_cred_reset_tx(&rk->ptr[i]); + fido_cred_reset_rx(&rk->ptr[i]); + } + + free(rk->ptr); + rk->ptr = NULL; + memset(rk, 0, sizeof(*rk)); +} + +static int +credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val, + void *arg) +{ + fido_credman_rk_t *rk = arg; + uint64_t n; + + /* totalCredentials */ + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8 || + cbor_get_uint8(key) != 9) { + fido_log_debug("%s: cbor_type", __func__); + return (0); /* ignore */ + } + + if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { + fido_log_debug("%s: cbor_decode_uint64", __func__); + return (-1); + } + + if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx, + (size_t)n, sizeof(*rk->ptr)) < 0) { + fido_log_debug("%s: credman_grow_array", __func__); + return (-1); + } + + return (0); +} + +static int +credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + credman_reset_rk(rk); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + /* adjust as needed */ + if ((r = cbor_parse_reply(reply, (size_t)reply_len, rk, + credman_parse_rk_count)) != FIDO_OK) { + fido_log_debug("%s: credman_parse_rk_count", __func__); + return (r); + } + + if (rk->n_alloc == 0) { + fido_log_debug("%s: n_alloc=0", __func__); + return (FIDO_OK); + } + + /* parse the first rk */ + if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[0], + credman_parse_rk)) != FIDO_OK) { + fido_log_debug("%s: credman_parse_rk", __func__); + return (r); + } + + rk->n_rx++; + + return (FIDO_OK); +} + +static int +credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + /* sanity check */ + if (rk->n_rx >= rk->n_alloc) { + fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx, + rk->n_alloc); + return (FIDO_ERR_INTERNAL); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[rk->n_rx], + credman_parse_rk)) != FIDO_OK) { + fido_log_debug("%s: credman_parse_rk", __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk, + const char *pin, int ms) +{ + fido_blob_t rp_dgst; + uint8_t dgst[SHA256_DIGEST_LENGTH]; + int r; + + if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) { + fido_log_debug("%s: sha256", __func__); + return (FIDO_ERR_INTERNAL); + } + + rp_dgst.ptr = dgst; + rp_dgst.len = sizeof(dgst); + + if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin)) != FIDO_OK || + (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK) + return (r); + + while (rk->n_rx < rk->n_alloc) { + if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL)) != FIDO_OK || + (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK) + return (r); + rk->n_rx++; + } + + return (FIDO_OK); +} + +int +fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id, + fido_credman_rk_t *rk, const char *pin) +{ + if (fido_dev_is_fido2(dev) == false) + return (FIDO_ERR_INVALID_COMMAND); + if (pin == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (credman_get_rk_wait(dev, rp_id, rk, pin, -1)); +} + +static int +credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id, + size_t cred_id_len, const char *pin, int ms) +{ + fido_blob_t cred; + int r; + + memset(&cred, 0, sizeof(cred)); + + if (fido_blob_set(&cred, cred_id, cred_id_len) < 0) + return (FIDO_ERR_INVALID_ARGUMENT); + + if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin)) != FIDO_OK || + (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) + goto fail; + + r = FIDO_OK; +fail: + free(cred.ptr); + + return (r); +} + +int +fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id, + size_t cred_id_len, const char *pin) +{ + if (fido_dev_is_fido2(dev) == false) + return (FIDO_ERR_INVALID_COMMAND); + if (pin == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1)); +} + +static int +credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + struct fido_credman_single_rp *rp = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 3: + return (cbor_decode_rp_entity(val, &rp->rp_entity)); + case 4: + return (fido_blob_decode(val, &rp->rp_id_hash)); + default: + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } +} + +static void +credman_reset_rp(fido_credman_rp_t *rp) +{ + for (size_t i = 0; i < rp->n_alloc; i++) { + free(rp->ptr[i].rp_entity.id); + free(rp->ptr[i].rp_entity.name); + rp->ptr[i].rp_entity.id = NULL; + rp->ptr[i].rp_entity.name = NULL; + free(rp->ptr[i].rp_id_hash.ptr); + memset(&rp->ptr[i].rp_id_hash, 0, + sizeof(rp->ptr[i].rp_id_hash)); + } + + free(rp->ptr); + rp->ptr = NULL; + memset(rp, 0, sizeof(*rp)); +} + +static int +credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val, + void *arg) +{ + fido_credman_rp_t *rp = arg; + uint64_t n; + + /* totalRPs */ + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8 || + cbor_get_uint8(key) != 5) { + fido_log_debug("%s: cbor_type", __func__); + return (0); /* ignore */ + } + + if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { + fido_log_debug("%s: cbor_decode_uint64", __func__); + return (-1); + } + + if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx, + (size_t)n, sizeof(*rp->ptr)) < 0) { + fido_log_debug("%s: credman_grow_array", __func__); + return (-1); + } + + return (0); +} + +static int +credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + credman_reset_rp(rp); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + /* adjust as needed */ + if ((r = cbor_parse_reply(reply, (size_t)reply_len, rp, + credman_parse_rp_count)) != FIDO_OK) { + fido_log_debug("%s: credman_parse_rp_count", __func__); + return (r); + } + + if (rp->n_alloc == 0) { + fido_log_debug("%s: n_alloc=0", __func__); + return (FIDO_OK); + } + + /* parse the first rp */ + if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[0], + credman_parse_rp)) != FIDO_OK) { + fido_log_debug("%s: credman_parse_rp", __func__); + return (r); + } + + rp->n_rx++; + + return (FIDO_OK); +} + +static int +credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + int r; + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + /* sanity check */ + if (rp->n_rx >= rp->n_alloc) { + fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx, + rp->n_alloc); + return (FIDO_ERR_INTERNAL); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[rp->n_rx], + credman_parse_rp)) != FIDO_OK) { + fido_log_debug("%s: credman_parse_rp", __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin, + int ms) +{ + int r; + + if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin)) != FIDO_OK || + (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK) + return (r); + + while (rp->n_rx < rp->n_alloc) { + if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL)) != FIDO_OK || + (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK) + return (r); + rp->n_rx++; + } + + return (FIDO_OK); +} + +int +fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin) +{ + if (fido_dev_is_fido2(dev) == false) + return (FIDO_ERR_INVALID_COMMAND); + if (pin == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (credman_get_rp_wait(dev, rp, pin, -1)); +} + +fido_credman_rk_t * +fido_credman_rk_new(void) +{ + return (calloc(1, sizeof(fido_credman_rk_t))); +} + +void +fido_credman_rk_free(fido_credman_rk_t **rk_p) +{ + fido_credman_rk_t *rk; + + if (rk_p == NULL || (rk = *rk_p) == NULL) + return; + + credman_reset_rk(rk); + free(rk); + *rk_p = NULL; +} + +size_t +fido_credman_rk_count(const fido_credman_rk_t *rk) +{ + return (rk->n_rx); +} + +const fido_cred_t * +fido_credman_rk(const fido_credman_rk_t *rk, size_t idx) +{ + if (idx >= rk->n_alloc) + return (NULL); + + return (&rk->ptr[idx]); +} + +fido_credman_metadata_t * +fido_credman_metadata_new(void) +{ + return (calloc(1, sizeof(fido_credman_metadata_t))); +} + +void +fido_credman_metadata_free(fido_credman_metadata_t **metadata_p) +{ + fido_credman_metadata_t *metadata; + + if (metadata_p == NULL || (metadata = *metadata_p) == NULL) + return; + + free(metadata); + *metadata_p = NULL; +} + +uint64_t +fido_credman_rk_existing(const fido_credman_metadata_t *metadata) +{ + return (metadata->rk_existing); +} + +uint64_t +fido_credman_rk_remaining(const fido_credman_metadata_t *metadata) +{ + return (metadata->rk_remaining); +} + +fido_credman_rp_t * +fido_credman_rp_new(void) +{ + return (calloc(1, sizeof(fido_credman_rp_t))); +} + +void +fido_credman_rp_free(fido_credman_rp_t **rp_p) +{ + fido_credman_rp_t *rp; + + if (rp_p == NULL || (rp = *rp_p) == NULL) + return; + + credman_reset_rp(rp); + free(rp); + *rp_p = NULL; +} + +size_t +fido_credman_rp_count(const fido_credman_rp_t *rp) +{ + return (rp->n_rx); +} + +const char * +fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx) +{ + if (idx >= rp->n_alloc) + return (NULL); + + return (rp->ptr[idx].rp_entity.id); +} + +const char * +fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx) +{ + if (idx >= rp->n_alloc) + return (NULL); + + return (rp->ptr[idx].rp_entity.name); +} + +size_t +fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx) +{ + if (idx >= rp->n_alloc) + return (0); + + return (rp->ptr[idx].rp_id_hash.len); +} + +const unsigned char * +fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx) +{ + if (idx >= rp->n_alloc) + return (NULL); + + return (rp->ptr[idx].rp_id_hash.ptr); +} diff --git a/src/dev.c b/src/dev.c new file mode 100644 index 0000000..d0efac7 --- /dev/null +++ b/src/dev.c @@ -0,0 +1,284 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "fido.h" + +#if defined(_WIN32) +#include + +#include +#include +#include +#include +#include + +static int +obtain_nonce(uint64_t *nonce) +{ + NTSTATUS status; + + status = BCryptGenRandom(NULL, (unsigned char *)nonce, sizeof(*nonce), + BCRYPT_USE_SYSTEM_PREFERRED_RNG); + + if (!NT_SUCCESS(status)) + return (-1); + + return (0); +} +#elif defined(HAS_DEV_URANDOM) +static int +obtain_nonce(uint64_t *nonce) +{ + int fd = -1; + int ok = -1; + ssize_t r; + + if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0) + goto fail; + if ((r = read(fd, nonce, sizeof(*nonce))) < 0 || + (size_t)r != sizeof(*nonce)) + goto fail; + + ok = 0; +fail: + if (fd != -1) + close(fd); + + return (ok); +} +#else +#error "please provide an implementation of obtain_nonce() for your platform" +#endif /* _WIN32 */ + +static int +fido_dev_open_tx(fido_dev_t *dev, const char *path) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT; + + if (dev->io_handle != NULL) { + fido_log_debug("%s: handle=%p", __func__, dev->io_handle); + return (FIDO_ERR_INVALID_ARGUMENT); + } + + if (dev->io.open == NULL || dev->io.close == NULL) { + fido_log_debug("%s: NULL open/close", __func__); + return (FIDO_ERR_INVALID_ARGUMENT); + } + + if (obtain_nonce(&dev->nonce) < 0) { + fido_log_debug("%s: obtain_nonce", __func__); + return (FIDO_ERR_INTERNAL); + } + + if ((dev->io_handle = dev->io.open(path)) == NULL) { + fido_log_debug("%s: dev->io.open", __func__); + return (FIDO_ERR_INTERNAL); + } + + if (fido_tx(dev, cmd, &dev->nonce, sizeof(dev->nonce)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + dev->io.close(dev->io_handle); + dev->io_handle = NULL; + return (FIDO_ERR_TX); + } + + return (FIDO_OK); +} + +static int +fido_dev_open_rx(fido_dev_t *dev, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT; + int n; + + if ((n = fido_rx(dev, cmd, &dev->attr, sizeof(dev->attr), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + goto fail; + } + +#ifdef FIDO_FUZZ + dev->attr.nonce = dev->nonce; +#endif + + if ((size_t)n != sizeof(dev->attr) || dev->attr.nonce != dev->nonce) { + fido_log_debug("%s: invalid nonce", __func__); + goto fail; + } + + dev->cid = dev->attr.cid; + + return (FIDO_OK); +fail: + dev->io.close(dev->io_handle); + dev->io_handle = NULL; + + return (FIDO_ERR_RX); +} + +static int +fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms) +{ + int r; + + if ((r = fido_dev_open_tx(dev, path)) != FIDO_OK || + (r = fido_dev_open_rx(dev, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_dev_open(fido_dev_t *dev, const char *path) +{ + return (fido_dev_open_wait(dev, path, -1)); +} + +int +fido_dev_close(fido_dev_t *dev) +{ + if (dev->io_handle == NULL || dev->io.close == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + dev->io.close(dev->io_handle); + dev->io_handle = NULL; + + return (FIDO_OK); +} + +int +fido_dev_cancel(fido_dev_t *dev) +{ + if (fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CANCEL, NULL, 0) < 0) + return (FIDO_ERR_TX); + + return (FIDO_OK); +} + +int +fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io) +{ + if (dev->io_handle != NULL) { + fido_log_debug("%s: NULL handle", __func__); + return (FIDO_ERR_INVALID_ARGUMENT); + } + + if (io == NULL || io->open == NULL || io->close == NULL || + io->read == NULL || io->write == NULL) { + fido_log_debug("%s: NULL function", __func__); + return (FIDO_ERR_INVALID_ARGUMENT); + } + + dev->io.open = io->open; + dev->io.close = io->close; + dev->io.read = io->read; + dev->io.write = io->write; + + return (FIDO_OK); +} + +void +fido_init(int flags) +{ + if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL) + fido_log_init(); +} + +fido_dev_t * +fido_dev_new(void) +{ + fido_dev_t *dev; + fido_dev_io_t io; + + if ((dev = calloc(1, sizeof(*dev))) == NULL) + return (NULL); + + dev->cid = CTAP_CID_BROADCAST; + + io.open = fido_hid_open; + io.close = fido_hid_close; + io.read = fido_hid_read; + io.write = fido_hid_write; + + if (fido_dev_set_io_functions(dev, &io) != FIDO_OK) { + fido_log_debug("%s: fido_dev_set_io_functions", __func__); + fido_dev_free(&dev); + return (NULL); + } + + return (dev); +} + +void +fido_dev_free(fido_dev_t **dev_p) +{ + fido_dev_t *dev; + + if (dev_p == NULL || (dev = *dev_p) == NULL) + return; + + free(dev); + + *dev_p = NULL; +} + +uint8_t +fido_dev_protocol(const fido_dev_t *dev) +{ + return (dev->attr.protocol); +} + +uint8_t +fido_dev_major(const fido_dev_t *dev) +{ + return (dev->attr.major); +} + +uint8_t +fido_dev_minor(const fido_dev_t *dev) +{ + return (dev->attr.minor); +} + +uint8_t +fido_dev_build(const fido_dev_t *dev) +{ + return (dev->attr.build); +} + +uint8_t +fido_dev_flags(const fido_dev_t *dev) +{ + return (dev->attr.flags); +} + +bool +fido_dev_is_fido2(const fido_dev_t *dev) +{ + return (dev->attr.flags & FIDO_CAP_CBOR); +} + +void +fido_dev_force_u2f(fido_dev_t *dev) +{ + dev->attr.flags &= ~FIDO_CAP_CBOR; +} + +void +fido_dev_force_fido2(fido_dev_t *dev) +{ + dev->attr.flags |= FIDO_CAP_CBOR; +} diff --git a/src/diff_exports.sh b/src/diff_exports.sh new file mode 100755 index 0000000..7920f47 --- /dev/null +++ b/src/diff_exports.sh @@ -0,0 +1,23 @@ +#!/bin/bash -u + +# 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. + +[[ ! -f export.gnu || ! -f export.llvm || ! -f export.msvc ]] && exit 1 + +TMPDIR=$(mktemp -d) +GNU=${TMPDIR}/gnu +LLVM=${TMPDIR}/llvm +MSVC=${TMPDIR}/msvc + +egrep -o $'([^*{}\t]+);$' export.gnu | tr -d ';' | sort > ${GNU} +sed 's/^_//g' export.llvm | sort > ${LLVM} +egrep -v "^EXPORTS$" export.msvc | sort > ${MSVC} +diff -u ${GNU} ${LLVM} && diff -u ${MSVC} ${LLVM} +ERROR=$? + +rm ${GNU} ${LLVM} ${MSVC} +rmdir ${TMPDIR} + +exit ${ERROR} diff --git a/src/ecdh.c b/src/ecdh.c new file mode 100644 index 0000000..7f25c7b --- /dev/null +++ b/src/ecdh.c @@ -0,0 +1,121 @@ +/* + * 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 +#include + +#include "fido.h" +#include "fido/es256.h" + +static int +do_ecdh(const es256_sk_t *sk, const es256_pk_t *pk, fido_blob_t **ecdh) +{ + EVP_PKEY *pk_evp = NULL; + EVP_PKEY *sk_evp = NULL; + EVP_PKEY_CTX *ctx = NULL; + fido_blob_t *secret = NULL; + int ok = -1; + + *ecdh = NULL; + + /* allocate blobs for secret & ecdh */ + if ((secret = fido_blob_new()) == NULL || + (*ecdh = fido_blob_new()) == NULL) + goto fail; + + /* wrap the keys as openssl objects */ + if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL || + (sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) { + fido_log_debug("%s: es256_to_EVP_PKEY", __func__); + goto fail; + } + + /* set ecdh parameters */ + if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL || + EVP_PKEY_derive_init(ctx) <= 0 || + EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) { + fido_log_debug("%s: EVP_PKEY_derive_init", __func__); + goto fail; + } + + /* perform ecdh */ + if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 || + (secret->ptr = calloc(1, secret->len)) == NULL || + EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) { + fido_log_debug("%s: EVP_PKEY_derive", __func__); + goto fail; + } + + /* use sha256 as a kdf on the resulting secret */ + (*ecdh)->len = SHA256_DIGEST_LENGTH; + if (((*ecdh)->ptr = calloc(1, (*ecdh)->len)) == NULL || + SHA256(secret->ptr, secret->len, (*ecdh)->ptr) != (*ecdh)->ptr) { + fido_log_debug("%s: sha256", __func__); + goto fail; + } + + ok = 0; +fail: + if (pk_evp != NULL) + EVP_PKEY_free(pk_evp); + if (sk_evp != NULL) + EVP_PKEY_free(sk_evp); + if (ctx != NULL) + EVP_PKEY_CTX_free(ctx); + if (ok < 0) + fido_blob_free(ecdh); + + fido_blob_free(&secret); + + return (ok); +} + +int +fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh) +{ + es256_sk_t *sk = NULL; /* our private key */ + es256_pk_t *ak = NULL; /* authenticator's public key */ + int r; + + *pk = NULL; /* our public key; returned */ + *ecdh = NULL; /* shared ecdh secret; returned */ + + if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) { + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) { + fido_log_debug("%s: es256_derive_pk", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if ((ak = es256_pk_new()) == NULL || + fido_dev_authkey(dev, ak) != FIDO_OK) { + fido_log_debug("%s: fido_dev_authkey", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (do_ecdh(sk, ak, ecdh) < 0) { + fido_log_debug("%s: do_ecdh", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + r = FIDO_OK; +fail: + es256_sk_free(&sk); + es256_pk_free(&ak); + + if (r != FIDO_OK) { + es256_pk_free(pk); + fido_blob_free(ecdh); + } + + return (r); +} diff --git a/src/eddsa.c b/src/eddsa.c new file mode 100644 index 0000000..92a0222 --- /dev/null +++ b/src/eddsa.c @@ -0,0 +1,169 @@ +/* + * 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 +#include +#include +#include + +#include +#include "fido.h" +#include "fido/eddsa.h" + +#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L +EVP_PKEY * +EVP_PKEY_new_raw_public_key(int type, ENGINE *e, const unsigned char *key, + size_t keylen) +{ + (void)type; + (void)e; + (void)key; + (void)keylen; + + return (NULL); +} + +int +EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub, + size_t *len) +{ + (void)pkey; + (void)pub; + (void)len; + + return (0); +} + +int +EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + (void)ctx; + (void)sigret; + (void)siglen; + (void)tbs; + (void)tbslen; + + return (0); +} +#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +EVP_MD_CTX * +EVP_MD_CTX_new(void) +{ + return (NULL); +} + +void +EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + (void)ctx; +} +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + +static int +decode_coord(const cbor_item_t *item, void *xy, size_t xy_len) +{ + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false || + cbor_bytestring_length(item) != xy_len) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + memcpy(xy, cbor_bytestring_handle(item), xy_len); + + return (0); +} + +static int +decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + eddsa_pk_t *k = arg; + + if (cbor_isa_negint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) + return (0); /* ignore */ + + switch (cbor_get_uint8(key)) { + case 1: /* x coordinate */ + return (decode_coord(val, &k->x, sizeof(k->x))); + } + + return (0); /* ignore */ +} + +int +eddsa_pk_decode(const cbor_item_t *item, eddsa_pk_t *k) +{ + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, k, decode_pubkey_point) < 0) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + return (0); +} + +eddsa_pk_t * +eddsa_pk_new(void) +{ + return (calloc(1, sizeof(eddsa_pk_t))); +} + +void +eddsa_pk_free(eddsa_pk_t **pkp) +{ + eddsa_pk_t *pk; + + if (pkp == NULL || (pk = *pkp) == NULL) + return; + + explicit_bzero(pk, sizeof(*pk)); + free(pk); + + *pkp = NULL; +} + +int +eddsa_pk_from_ptr(eddsa_pk_t *pk, const void *ptr, size_t len) +{ + if (len < sizeof(*pk)) + return (FIDO_ERR_INVALID_ARGUMENT); + + memcpy(pk, ptr, sizeof(*pk)); + + return (FIDO_OK); +} + +EVP_PKEY * +eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *k) +{ + EVP_PKEY *pkey = NULL; + + if ((pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, k->x, + sizeof(k->x))) == NULL) + fido_log_debug("%s: EVP_PKEY_new_raw_public_key", __func__); + + return (pkey); +} + +int +eddsa_pk_from_EVP_PKEY(eddsa_pk_t *pk, const EVP_PKEY *pkey) +{ + size_t len = 0; + + if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 || + len != sizeof(pk->x)) + return (FIDO_ERR_INTERNAL); + if (EVP_PKEY_get_raw_public_key(pkey, pk->x, &len) != 1 || + len != sizeof(pk->x)) + return (FIDO_ERR_INTERNAL); + + return (FIDO_OK); +} diff --git a/src/err.c b/src/err.c new file mode 100644 index 0000000..5d3efd4 --- /dev/null +++ b/src/err.c @@ -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. + */ + +#include "fido/err.h" + +const char * +fido_strerr(int n) +{ + switch (n) { + case FIDO_ERR_SUCCESS: + return "FIDO_ERR_SUCCESS"; + case FIDO_ERR_INVALID_COMMAND: + return "FIDO_ERR_INVALID_COMMAND"; + case FIDO_ERR_INVALID_PARAMETER: + return "FIDO_ERR_INVALID_PARAMETER"; + case FIDO_ERR_INVALID_LENGTH: + return "FIDO_ERR_INVALID_LENGTH"; + case FIDO_ERR_INVALID_SEQ: + return "FIDO_ERR_INVALID_SEQ"; + case FIDO_ERR_TIMEOUT: + return "FIDO_ERR_TIMEOUT"; + case FIDO_ERR_CHANNEL_BUSY: + return "FIDO_ERR_CHANNEL_BUSY"; + case FIDO_ERR_LOCK_REQUIRED: + return "FIDO_ERR_LOCK_REQUIRED"; + case FIDO_ERR_INVALID_CHANNEL: + return "FIDO_ERR_INVALID_CHANNEL"; + case FIDO_ERR_CBOR_UNEXPECTED_TYPE: + return "FIDO_ERR_UNEXPECTED_TYPE"; + case FIDO_ERR_INVALID_CBOR: + return "FIDO_ERR_INVALID_CBOR"; + case FIDO_ERR_MISSING_PARAMETER: + return "FIDO_ERR_MISSING_PARAMETER"; + case FIDO_ERR_LIMIT_EXCEEDED: + return "FIDO_ERR_LIMIT_EXCEEDED"; + case FIDO_ERR_UNSUPPORTED_EXTENSION: + return "FIDO_ERR_UNSUPPORTED_EXTENSION"; + case FIDO_ERR_CREDENTIAL_EXCLUDED: + return "FIDO_ERR_CREDENTIAL_EXCLUDED"; + case FIDO_ERR_PROCESSING: + return "FIDO_ERR_PROCESSING"; + case FIDO_ERR_INVALID_CREDENTIAL: + return "FIDO_ERR_INVALID_CREDENTIAL"; + case FIDO_ERR_USER_ACTION_PENDING: + return "FIDO_ERR_ACTION_PENDING"; + case FIDO_ERR_OPERATION_PENDING: + return "FIDO_ERR_OPERATION_PENDING"; + case FIDO_ERR_NO_OPERATIONS: + return "FIDO_ERR_NO_OPERATIONS"; + case FIDO_ERR_UNSUPPORTED_ALGORITHM: + return "FIDO_ERR_UNSUPPORTED_ALGORITHM"; + case FIDO_ERR_OPERATION_DENIED: + return "FIDO_ERR_OPERATION_DENIED"; + case FIDO_ERR_KEY_STORE_FULL: + return "FIDO_ERR_STORE_FULL"; + case FIDO_ERR_NOT_BUSY: + return "FIDO_ERR_NOT_BUSY"; + case FIDO_ERR_NO_OPERATION_PENDING: + return "FIDO_ERR_OPERATION_PENDING"; + case FIDO_ERR_UNSUPPORTED_OPTION: + return "FIDO_ERR_UNSUPPORTED_OPTION"; + case FIDO_ERR_INVALID_OPTION: + return "FIDO_ERR_INVALID_OPTION"; + case FIDO_ERR_KEEPALIVE_CANCEL: + return "FIDO_ERR_KEEPALIVE_CANCEL"; + case FIDO_ERR_NO_CREDENTIALS: + return "FIDO_ERR_NO_CREDENTIALS"; + case FIDO_ERR_USER_ACTION_TIMEOUT: + return "FIDO_ERR_ACTION_TIMEOUT"; + case FIDO_ERR_NOT_ALLOWED: + return "FIDO_ERR_NOT_ALLOWED"; + case FIDO_ERR_PIN_INVALID: + return "FIDO_ERR_PIN_INVALID"; + case FIDO_ERR_PIN_BLOCKED: + return "FIDO_ERR_PIN_BLOCKED"; + case FIDO_ERR_PIN_AUTH_INVALID: + return "FIDO_ERR_AUTH_INVALID"; + case FIDO_ERR_PIN_AUTH_BLOCKED: + return "FIDO_ERR_AUTH_BLOCKED"; + case FIDO_ERR_PIN_NOT_SET: + return "FIDO_ERR_NOT_SET"; + case FIDO_ERR_PIN_REQUIRED: + return "FIDO_ERR_PIN_REQUIRED"; + case FIDO_ERR_PIN_POLICY_VIOLATION: + return "FIDO_ERR_POLICY_VIOLATION"; + case FIDO_ERR_PIN_TOKEN_EXPIRED: + return "FIDO_ERR_TOKEN_EXPIRED"; + case FIDO_ERR_REQUEST_TOO_LARGE: + return "FIDO_ERR_TOO_LARGE"; + case FIDO_ERR_ACTION_TIMEOUT: + return "FIDO_ERR_ACTION_TIMEOUT"; + case FIDO_ERR_UP_REQUIRED: + return "FIDO_ERR_UP_REQUIRED"; + case FIDO_ERR_ERR_OTHER: + return "FIDO_ERR_OTHER"; + case FIDO_ERR_SPEC_LAST: + return "FIDO_ERR_SPEC_LAST"; + case FIDO_ERR_TX: + return "FIDO_ERR_TX"; + case FIDO_ERR_RX: + return "FIDO_ERR_RX"; + case FIDO_ERR_RX_NOT_CBOR: + return "FIDO_ERR_RX_NOT_CBOR"; + case FIDO_ERR_RX_INVALID_CBOR: + return "FIDO_ERR_RX_INVALID_CBOR"; + case FIDO_ERR_INVALID_PARAM: + return "FIDO_ERR_INVALID_PARAM"; + case FIDO_ERR_INVALID_SIG: + return "FIDO_ERR_INVALID_SIG"; + case FIDO_ERR_INVALID_ARGUMENT: + return "FIDO_ERR_INVALID_ARGUMENT"; + case FIDO_ERR_USER_PRESENCE_REQUIRED: + return "FIDO_ERR_USER_PRESENCE_REQUIRED"; + case FIDO_ERR_INTERNAL: + return "FIDO_ERR_INTERNAL"; + default: + return "FIDO_ERR_UNKNOWN"; + } +} diff --git a/src/es256.c b/src/es256.c new file mode 100644 index 0000000..c8fd9f4 --- /dev/null +++ b/src/es256.c @@ -0,0 +1,434 @@ +/* + * 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 +#include +#include +#include + +#include +#include "fido.h" +#include "fido/es256.h" + +static int +decode_coord(const cbor_item_t *item, void *xy, size_t xy_len) +{ + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false || + cbor_bytestring_length(item) != xy_len) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + memcpy(xy, cbor_bytestring_handle(item), xy_len); + + return (0); +} + +static int +decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + es256_pk_t *k = arg; + + if (cbor_isa_negint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) + return (0); /* ignore */ + + switch (cbor_get_uint8(key)) { + case 1: /* x coordinate */ + return (decode_coord(val, &k->x, sizeof(k->x))); + case 2: /* y coordinate */ + return (decode_coord(val, &k->y, sizeof(k->y))); + } + + return (0); /* ignore */ +} + +int +es256_pk_decode(const cbor_item_t *item, es256_pk_t *k) +{ + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, k, decode_pubkey_point) < 0) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + return (0); +} + +cbor_item_t * +es256_pk_encode(const es256_pk_t *pk, int ecdh) +{ + cbor_item_t *item = NULL; + struct cbor_pair argv[5]; + int alg; + int ok = -1; + + memset(argv, 0, sizeof(argv)); + + if ((item = cbor_new_definite_map(5)) == NULL) + goto fail; + + /* kty */ + if ((argv[0].key = cbor_build_uint8(1)) == NULL || + (argv[0].value = cbor_build_uint8(2)) == NULL || + !cbor_map_add(item, argv[0])) + goto fail; + + /* + * "The COSEAlgorithmIdentifier used is -25 (ECDH-ES + + * HKDF-256) although this is NOT the algorithm actually + * used. Setting this to a different value may result in + * compatibility issues." + */ + if (ecdh) + alg = COSE_ECDH_ES256; + else + alg = COSE_ES256; + + /* alg */ + if ((argv[1].key = cbor_build_uint8(3)) == NULL || + (argv[1].value = cbor_build_negint8(-alg - 1)) == NULL || + !cbor_map_add(item, argv[1])) + goto fail; + + /* crv */ + if ((argv[2].key = cbor_build_negint8(0)) == NULL || + (argv[2].value = cbor_build_uint8(1)) == NULL || + !cbor_map_add(item, argv[2])) + goto fail; + + /* x */ + if ((argv[3].key = cbor_build_negint8(1)) == NULL || + (argv[3].value = cbor_build_bytestring(pk->x, + sizeof(pk->x))) == NULL || !cbor_map_add(item, argv[3])) + goto fail; + + /* y */ + if ((argv[4].key = cbor_build_negint8(2)) == NULL || + (argv[4].value = cbor_build_bytestring(pk->y, + sizeof(pk->y))) == NULL || !cbor_map_add(item, argv[4])) + goto fail; + + ok = 0; +fail: + if (ok < 0) { + if (item != NULL) { + cbor_decref(&item); + item = NULL; + } + } + + for (size_t i = 0; i < 5; i++) { + if (argv[i].key) + cbor_decref(&argv[i].key); + if (argv[i].value) + cbor_decref(&argv[i].value); + } + + return (item); +} + +es256_sk_t * +es256_sk_new(void) +{ + return (calloc(1, sizeof(es256_sk_t))); +} + +void +es256_sk_free(es256_sk_t **skp) +{ + es256_sk_t *sk; + + if (skp == NULL || (sk = *skp) == NULL) + return; + + explicit_bzero(sk, sizeof(*sk)); + free(sk); + + *skp = NULL; +} + +es256_pk_t * +es256_pk_new(void) +{ + return (calloc(1, sizeof(es256_pk_t))); +} + +void +es256_pk_free(es256_pk_t **pkp) +{ + es256_pk_t *pk; + + if (pkp == NULL || (pk = *pkp) == NULL) + return; + + explicit_bzero(pk, sizeof(*pk)); + free(pk); + + *pkp = NULL; +} + +int +es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len) +{ + if (len < sizeof(*pk)) + return (FIDO_ERR_INVALID_ARGUMENT); + + memcpy(pk, ptr, sizeof(*pk)); + + return (FIDO_OK); +} + +int +es256_pk_set_x(es256_pk_t *pk, const unsigned char *x) +{ + memcpy(pk->x, x, sizeof(pk->x)); + + return (0); +} + +int +es256_pk_set_y(es256_pk_t *pk, const unsigned char *y) +{ + memcpy(pk->y, y, sizeof(pk->y)); + + return (0); +} + +int +es256_sk_create(es256_sk_t *key) +{ + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY_CTX *kctx = NULL; + EVP_PKEY *p = NULL; + EVP_PKEY *k = NULL; + const EC_KEY *ec; + const BIGNUM *d; + const int nid = NID_X9_62_prime256v1; + int n; + int ok = -1; + + if ((pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL || + EVP_PKEY_paramgen_init(pctx) <= 0 || + EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 || + EVP_PKEY_paramgen(pctx, &p) <= 0) { + fido_log_debug("%s: EVP_PKEY_paramgen", __func__); + goto fail; + } + + if ((kctx = EVP_PKEY_CTX_new(p, NULL)) == NULL || + EVP_PKEY_keygen_init(kctx) <= 0 || EVP_PKEY_keygen(kctx, &k) <= 0) { + fido_log_debug("%s: EVP_PKEY_keygen", __func__); + goto fail; + } + + if ((ec = EVP_PKEY_get0_EC_KEY(k)) == NULL || + (d = EC_KEY_get0_private_key(ec)) == NULL || + (n = BN_num_bytes(d)) < 0 || (size_t)n > sizeof(key->d) || + (n = BN_bn2bin(d, key->d)) < 0 || (size_t)n > sizeof(key->d)) { + fido_log_debug("%s: EC_KEY_get0_private_key", __func__); + goto fail; + } + + ok = 0; +fail: + if (p != NULL) + EVP_PKEY_free(p); + if (k != NULL) + EVP_PKEY_free(k); + if (pctx != NULL) + EVP_PKEY_CTX_free(pctx); + if (kctx != NULL) + EVP_PKEY_CTX_free(kctx); + + return (ok); +} + +EVP_PKEY * +es256_pk_to_EVP_PKEY(const es256_pk_t *k) +{ + BN_CTX *bnctx = NULL; + EC_KEY *ec = NULL; + EC_POINT *q = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *x = NULL; + BIGNUM *y = NULL; + const EC_GROUP *g = NULL; + const int nid = NID_X9_62_prime256v1; + int ok = -1; + + if ((bnctx = BN_CTX_new()) == NULL || + (x = BN_CTX_get(bnctx)) == NULL || + (y = BN_CTX_get(bnctx)) == NULL) + goto fail; + + if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL || + BN_bin2bn(k->y, sizeof(k->y), y) == NULL) { + fido_log_debug("%s: BN_bin2bn", __func__); + goto fail; + } + + if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL || + (g = EC_KEY_get0_group(ec)) == NULL) { + fido_log_debug("%s: EC_KEY init", __func__); + goto fail; + } + + if ((q = EC_POINT_new(g)) == NULL || + EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 || + EC_KEY_set_public_key(ec, q) == 0) { + fido_log_debug("%s: EC_KEY_set_public_key", __func__); + goto fail; + } + + if ((pkey = EVP_PKEY_new()) == NULL || + EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) { + fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__); + goto fail; + } + + ec = NULL; /* at this point, ec belongs to evp */ + + ok = 0; +fail: + if (bnctx != NULL) + BN_CTX_free(bnctx); + if (ec != NULL) + EC_KEY_free(ec); + if (q != NULL) + EC_POINT_free(q); + if (ok < 0 && pkey != NULL) { + EVP_PKEY_free(pkey); + pkey = NULL; + } + + return (pkey); +} + +int +es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec) +{ + BN_CTX *ctx = NULL; + BIGNUM *x = NULL; + BIGNUM *y = NULL; + const EC_POINT *q = NULL; + const EC_GROUP *g = NULL; + int ok = FIDO_ERR_INTERNAL; + int n; + + if ((q = EC_KEY_get0_public_key(ec)) == NULL || + (g = EC_KEY_get0_group(ec)) == NULL) + goto fail; + + if ((ctx = BN_CTX_new()) == NULL || + (x = BN_CTX_get(ctx)) == NULL || + (y = BN_CTX_get(ctx)) == NULL) + goto fail; + + if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, ctx) == 0 || + (n = BN_num_bytes(x)) < 0 || (size_t)n > sizeof(pk->x) || + (n = BN_num_bytes(y)) < 0 || (size_t)n > sizeof(pk->y)) { + fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp", + __func__); + goto fail; + } + + if ((n = BN_bn2bin(x, pk->x)) < 0 || (size_t)n > sizeof(pk->x) || + (n = BN_bn2bin(y, pk->y)) < 0 || (size_t)n > sizeof(pk->y)) { + fido_log_debug("%s: BN_bn2bin", __func__); + goto fail; + } + + ok = FIDO_OK; +fail: + if (ctx != NULL) + BN_CTX_free(ctx); + + return (ok); +} + +EVP_PKEY * +es256_sk_to_EVP_PKEY(const es256_sk_t *k) +{ + BN_CTX *bnctx = NULL; + EC_KEY *ec = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *d = NULL; + const int nid = NID_X9_62_prime256v1; + int ok = -1; + + if ((bnctx = BN_CTX_new()) == NULL || (d = BN_CTX_get(bnctx)) == NULL || + BN_bin2bn(k->d, sizeof(k->d), d) == NULL) { + fido_log_debug("%s: BN_bin2bn", __func__); + goto fail; + } + + if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL || + EC_KEY_set_private_key(ec, d) == 0) { + fido_log_debug("%s: EC_KEY_set_private_key", __func__); + goto fail; + } + + if ((pkey = EVP_PKEY_new()) == NULL || + EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) { + fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__); + goto fail; + } + + ec = NULL; /* at this point, ec belongs to evp */ + + ok = 0; +fail: + if (bnctx != NULL) + BN_CTX_free(bnctx); + if (ec != NULL) + EC_KEY_free(ec); + if (ok < 0 && pkey != NULL) { + EVP_PKEY_free(pkey); + pkey = NULL; + } + + return (pkey); +} + +int +es256_derive_pk(const es256_sk_t *sk, es256_pk_t *pk) +{ + BIGNUM *d = NULL; + EC_KEY *ec = NULL; + EC_POINT *q = NULL; + const EC_GROUP *g = NULL; + const int nid = NID_X9_62_prime256v1; + int ok = -1; + + if ((d = BN_bin2bn(sk->d, (int)sizeof(sk->d), NULL)) == NULL || + (ec = EC_KEY_new_by_curve_name(nid)) == NULL || + (g = EC_KEY_get0_group(ec)) == NULL || + (q = EC_POINT_new(g)) == NULL) { + fido_log_debug("%s: get", __func__); + goto fail; + } + + if (EC_POINT_mul(g, q, d, NULL, NULL, NULL) == 0 || + EC_KEY_set_public_key(ec, q) == 0 || + es256_pk_from_EC_KEY(pk, ec) != FIDO_OK) { + fido_log_debug("%s: set", __func__); + goto fail; + } + + ok = 0; +fail: + if (d != NULL) + BN_clear_free(d); + if (q != NULL) + EC_POINT_free(q); + if (ec != NULL) + EC_KEY_free(ec); + + return (ok); +} diff --git a/src/export.gnu b/src/export.gnu new file mode 100644 index 0000000..f111e33 --- /dev/null +++ b/src/export.gnu @@ -0,0 +1,183 @@ +{ + 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_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_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_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_extensions_len; + fido_cbor_info_extensions_ptr; + fido_cbor_info_free; + fido_cbor_info_maxmsgsiz; + 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_versions_len; + fido_cbor_info_versions_ptr; + fido_cred_authdata_len; + fido_cred_authdata_ptr; + fido_cred_clientdata_hash_len; + fido_cred_clientdata_hash_ptr; + fido_cred_display_name; + fido_cred_exclude; + fido_cred_flags; + fido_cred_fmt; + fido_cred_free; + fido_cred_id_len; + fido_cred_id_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_cred_new; + 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_clientdata_hash; + fido_cred_set_extensions; + fido_cred_set_fmt; + fido_cred_set_options; + 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_flags; + fido_dev_force_fido2; + fido_dev_force_u2f; + fido_dev_free; + fido_dev_get_assert; + fido_dev_get_cbor_info; + fido_dev_get_retry_count; + 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_init; + fido_strerr; + rs256_pk_free; + rs256_pk_from_ptr; + rs256_pk_from_RSA; + rs256_pk_new; + rs256_pk_to_EVP_PKEY; + local: + *; +}; diff --git a/src/export.llvm b/src/export.llvm new file mode 100644 index 0000000..ef99a26 --- /dev/null +++ b/src/export.llvm @@ -0,0 +1,178 @@ +_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_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_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_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_extensions_len +_fido_cbor_info_extensions_ptr +_fido_cbor_info_free +_fido_cbor_info_maxmsgsiz +_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_versions_len +_fido_cbor_info_versions_ptr +_fido_cred_authdata_len +_fido_cred_authdata_ptr +_fido_cred_clientdata_hash_len +_fido_cred_clientdata_hash_ptr +_fido_cred_display_name +_fido_cred_exclude +_fido_cred_flags +_fido_cred_fmt +_fido_cred_free +_fido_cred_id_len +_fido_cred_id_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_cred_new +_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_clientdata_hash +_fido_cred_set_extensions +_fido_cred_set_fmt +_fido_cred_set_options +_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_flags +_fido_dev_force_fido2 +_fido_dev_force_u2f +_fido_dev_free +_fido_dev_get_assert +_fido_dev_get_cbor_info +_fido_dev_get_retry_count +_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_init +_fido_strerr +_rs256_pk_free +_rs256_pk_from_ptr +_rs256_pk_from_RSA +_rs256_pk_new +_rs256_pk_to_EVP_PKEY diff --git a/src/export.msvc b/src/export.msvc new file mode 100644 index 0000000..ff5425a --- /dev/null +++ b/src/export.msvc @@ -0,0 +1,179 @@ +EXPORTS +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_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_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_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_extensions_len +fido_cbor_info_extensions_ptr +fido_cbor_info_free +fido_cbor_info_maxmsgsiz +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_versions_len +fido_cbor_info_versions_ptr +fido_cred_authdata_len +fido_cred_authdata_ptr +fido_cred_clientdata_hash_len +fido_cred_clientdata_hash_ptr +fido_cred_display_name +fido_cred_exclude +fido_cred_flags +fido_cred_fmt +fido_cred_free +fido_cred_id_len +fido_cred_id_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_cred_new +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_clientdata_hash +fido_cred_set_extensions +fido_cred_set_fmt +fido_cred_set_options +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_flags +fido_dev_force_fido2 +fido_dev_force_u2f +fido_dev_free +fido_dev_get_assert +fido_dev_get_cbor_info +fido_dev_get_retry_count +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_init +fido_strerr +rs256_pk_free +rs256_pk_from_ptr +rs256_pk_from_RSA +rs256_pk_new +rs256_pk_to_EVP_PKEY diff --git a/src/extern.h b/src/extern.h new file mode 100644 index 0000000..c35af58 --- /dev/null +++ b/src/extern.h @@ -0,0 +1,132 @@ +/* + * 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 + +/* aes256 */ +int aes256_cbc_dec(const fido_blob_t *, const fido_blob_t *, fido_blob_t *); +int aes256_cbc_enc(const fido_blob_t *, const fido_blob_t *, fido_blob_t *); + +/* cbor encoding functions */ +cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t); +cbor_item_t *cbor_encode_assert_options(fido_opt_t, fido_opt_t); +cbor_item_t *cbor_encode_change_pin_auth(const fido_blob_t *, + const fido_blob_t *, const fido_blob_t *); +cbor_item_t *cbor_encode_extensions(int); +cbor_item_t *cbor_encode_hmac_secret_param(const fido_blob_t *, + const es256_pk_t *, const fido_blob_t *); +cbor_item_t *cbor_encode_options(fido_opt_t, fido_opt_t); +cbor_item_t *cbor_encode_pin_auth(const fido_blob_t *, const fido_blob_t *); +cbor_item_t *cbor_encode_pin_enc(const fido_blob_t *, const fido_blob_t *); +cbor_item_t *cbor_encode_pin_hash_enc(const fido_blob_t *, const fido_blob_t *); +cbor_item_t *cbor_encode_pin_opt(void); +cbor_item_t *cbor_encode_pubkey(const fido_blob_t *); +cbor_item_t *cbor_encode_pubkey_list(const fido_blob_array_t *); +cbor_item_t *cbor_encode_pubkey_param(int); +cbor_item_t *cbor_encode_rp_entity(const fido_rp_t *); +cbor_item_t *cbor_encode_set_pin_auth(const fido_blob_t *, const fido_blob_t *); +cbor_item_t *cbor_encode_user_entity(const fido_user_t *); +cbor_item_t *es256_pk_encode(const es256_pk_t *, int); + +/* cbor decoding functions */ +int cbor_decode_attstmt(const cbor_item_t *, fido_attstmt_t *); +int cbor_decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *, + fido_authdata_t *, fido_attcred_t *, int *); +int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *, + fido_authdata_t *, int *, fido_blob_t *); +int cbor_decode_cred_id(const cbor_item_t *, fido_blob_t *); +int cbor_decode_fmt(const cbor_item_t *, char **); +int cbor_decode_pubkey(const cbor_item_t *, int *, void *); +int cbor_decode_rp_entity(const cbor_item_t *, fido_rp_t *); +int cbor_decode_uint64(const cbor_item_t *, uint64_t *); +int cbor_decode_user(const cbor_item_t *, fido_user_t *); +int es256_pk_decode(const cbor_item_t *, es256_pk_t *); +int rs256_pk_decode(const cbor_item_t *, rs256_pk_t *); +int eddsa_pk_decode(const cbor_item_t *, eddsa_pk_t *); + +/* auxiliary cbor routines */ +int cbor_add_bool(cbor_item_t *, const char *, fido_opt_t); +int cbor_add_bytestring(cbor_item_t *, const char *, const unsigned char *, + size_t); +int cbor_add_string(cbor_item_t *, const char *, const char *); +int cbor_array_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *, + void *)); +int cbor_build_frame(uint8_t, cbor_item_t *[], size_t, fido_blob_t *); +int cbor_bytestring_copy(const cbor_item_t *, unsigned char **, size_t *); +int cbor_map_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *, + const cbor_item_t *, void *)); +int cbor_string_copy(const cbor_item_t *, char **); +int cbor_parse_reply(const unsigned char *, size_t, void *, + int(*)(const cbor_item_t *, const cbor_item_t *, void *)); +int cbor_add_pin_params(fido_dev_t *, const fido_blob_t *, const es256_pk_t *, + const fido_blob_t *,const char *, cbor_item_t **, cbor_item_t **); +void cbor_vector_free(cbor_item_t **, size_t); + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +/* buf */ +int fido_buf_read(const unsigned char **, size_t *, void *, size_t); +int fido_buf_write(unsigned char **, size_t *, const void *, size_t); + +/* hid i/o */ +void *fido_hid_open(const char *); +void fido_hid_close(void *); +int fido_hid_read(void *, unsigned char *, size_t, int); +int fido_hid_write(void *, const unsigned char *, size_t); + +/* generic i/o */ +int fido_rx_cbor_status(fido_dev_t *, int); +int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int); +int fido_tx(fido_dev_t *, uint8_t, const void *, size_t); + +/* log */ +#ifdef FIDO_NO_DIAGNOSTIC +#define fido_log_init(...) do { /* nothing */ } while (0) +#define fido_log_debug(...) do { /* nothing */ } while (0) +#define fido_log_xxd(...) do { /* nothing */ } while (0) +#else +#ifdef __GNUC__ +void fido_log_init(void); +void fido_log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void fido_log_xxd(const void *, size_t); +#else +void fido_log_init(void); +void fido_log_debug(const char *, ...); +void fido_log_xxd(const void *, size_t); +#endif /* __GNUC__ */ +#endif /* FIDO_NO_DIAGNOSTIC */ + +/* u2f */ +int u2f_register(fido_dev_t *, fido_cred_t *, int); +int u2f_authenticate(fido_dev_t *, fido_assert_t *, int); + +/* unexposed fido ops */ +int fido_dev_authkey(fido_dev_t *, es256_pk_t *); +int fido_dev_get_pin_token(fido_dev_t *, const char *, const fido_blob_t *, + const es256_pk_t *, fido_blob_t *); +int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **); + +/* misc */ +void fido_assert_reset_rx(fido_assert_t *); +void fido_assert_reset_tx(fido_assert_t *); +void fido_cred_reset_rx(fido_cred_t *); +void fido_cred_reset_tx(fido_cred_t *); +int fido_check_rp_id(const char *, const unsigned char *); +int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t); + +/* crypto */ +int fido_verify_sig_es256(const fido_blob_t *, const es256_pk_t *, + const fido_blob_t *); +int fido_verify_sig_rs256(const fido_blob_t *, const rs256_pk_t *, + const fido_blob_t *); +int fido_verify_sig_eddsa(const fido_blob_t *, const eddsa_pk_t *, + const fido_blob_t *); + +#endif /* !_EXTERN_H */ diff --git a/src/fido.h b/src/fido.h new file mode 100644 index 0000000..f85a41a --- /dev/null +++ b/src/fido.h @@ -0,0 +1,194 @@ +/* + * 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 _FIDO_H +#define _FIDO_H + +#include +#include + +#include +#include +#include + +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; + +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; + +#ifdef _FIDO_INTERNAL +#include +#include + +#include "blob.h" +#include "../openbsd-compat/openbsd-compat.h" +#include "iso7816.h" +#include "types.h" +#include "extern.h" +#endif + +#include "fido/err.h" +#include "fido/param.h" + +#ifndef _FIDO_INTERNAL +typedef struct fido_assert fido_assert_t; +typedef struct fido_cbor_info fido_cbor_info_t; +typedef struct fido_cred fido_cred_t; +typedef struct fido_dev fido_dev_t; +typedef struct fido_dev_info fido_dev_info_t; +typedef struct es256_pk es256_pk_t; +typedef struct es256_sk es256_sk_t; +typedef struct rs256_pk rs256_pk_t; +typedef struct eddsa_pk eddsa_pk_t; +#endif + +fido_assert_t *fido_assert_new(void); +fido_cred_t *fido_cred_new(void); +fido_dev_t *fido_dev_new(void); +fido_dev_info_t *fido_dev_info_new(size_t); +fido_cbor_info_t *fido_cbor_info_new(void); + +void fido_assert_free(fido_assert_t **); +void fido_cbor_info_free(fido_cbor_info_t **); +void fido_cred_free(fido_cred_t **); +void fido_dev_force_fido2(fido_dev_t *); +void fido_dev_force_u2f(fido_dev_t *); +void fido_dev_free(fido_dev_t **); +void fido_dev_info_free(fido_dev_info_t **, size_t); + +/* fido_init() flags. */ +#define FIDO_DEBUG 0x01 + +void fido_init(int); + +const unsigned char *fido_assert_authdata_ptr(const fido_assert_t *, size_t); +const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *); +const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t); +const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t); +const unsigned char *fido_assert_sig_ptr(const fido_assert_t *, size_t); +const unsigned char *fido_assert_user_id_ptr(const fido_assert_t *, size_t); + +char **fido_cbor_info_extensions_ptr(const fido_cbor_info_t *); +char **fido_cbor_info_options_name_ptr(const fido_cbor_info_t *); +char **fido_cbor_info_versions_ptr(const fido_cbor_info_t *); +const bool *fido_cbor_info_options_value_ptr(const fido_cbor_info_t *); +const char *fido_assert_rp_id(const fido_assert_t *); +const char *fido_assert_user_display_name(const fido_assert_t *, size_t); +const char *fido_assert_user_icon(const fido_assert_t *, size_t); +const char *fido_assert_user_name(const fido_assert_t *, size_t); +const char *fido_cred_display_name(const fido_cred_t *); +const char *fido_cred_fmt(const fido_cred_t *); +const char *fido_cred_rp_id(const fido_cred_t *); +const char *fido_cred_rp_name(const fido_cred_t *); +const char *fido_cred_user_name(const fido_cred_t *); +const char *fido_dev_info_manufacturer_string(const fido_dev_info_t *); +const char *fido_dev_info_path(const fido_dev_info_t *); +const char *fido_dev_info_product_string(const fido_dev_info_t *); +const fido_dev_info_t *fido_dev_info_ptr(const fido_dev_info_t *, size_t); +const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *); +const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *); +const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *); +const unsigned char *fido_cred_clientdata_hash_ptr(const fido_cred_t *); +const unsigned char *fido_cred_id_ptr(const fido_cred_t *); +const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *); +const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *); +const unsigned char *fido_cred_sig_ptr(const fido_cred_t *); +const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *); + +int fido_assert_allow_cred(fido_assert_t *, const unsigned char *, size_t); +int fido_assert_set_authdata(fido_assert_t *, size_t, const unsigned char *, + size_t); +int fido_assert_set_authdata_raw(fido_assert_t *, size_t, const unsigned char *, + size_t); +int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *, + size_t); +int fido_assert_set_count(fido_assert_t *, size_t); +int fido_assert_set_extensions(fido_assert_t *, int); +int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t); +int fido_assert_set_options(fido_assert_t *, bool, bool) __attribute__((__deprecated__)); +int fido_assert_set_rp(fido_assert_t *, const char *); +int fido_assert_set_up(fido_assert_t *, fido_opt_t); +int fido_assert_set_uv(fido_assert_t *, fido_opt_t); +int fido_assert_set_sig(fido_assert_t *, size_t, const unsigned char *, size_t); +int fido_assert_verify(const fido_assert_t *, size_t, int, const void *); +int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t); +int fido_cred_set_authdata(fido_cred_t *, const unsigned char *, size_t); +int fido_cred_set_authdata_raw(fido_cred_t *, const unsigned char *, size_t); +int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t); +int fido_cred_set_extensions(fido_cred_t *, int); +int fido_cred_set_fmt(fido_cred_t *, const char *); +int fido_cred_set_options(fido_cred_t *, bool, bool) __attribute__((__deprecated__)); +int fido_cred_set_rk(fido_cred_t *, fido_opt_t); +int fido_cred_set_rp(fido_cred_t *, const char *, const char *); +int fido_cred_set_sig(fido_cred_t *, const unsigned char *, size_t); +int fido_cred_set_type(fido_cred_t *, int); +int fido_cred_set_uv(fido_cred_t *, fido_opt_t); +int fido_cred_type(const fido_cred_t *); +int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t, + const char *, const char *, const char *); +int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t); +int fido_cred_verify(const fido_cred_t *); +int fido_cred_verify_self(const fido_cred_t *); +int fido_dev_cancel(fido_dev_t *); +int fido_dev_close(fido_dev_t *); +int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *); +int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *); +int fido_dev_get_retry_count(fido_dev_t *, int *); +int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *); +int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *); +int fido_dev_open(fido_dev_t *, const char *); +int fido_dev_reset(fido_dev_t *); +int fido_dev_set_io_functions(fido_dev_t *, const fido_dev_io_t *); +int fido_dev_set_pin(fido_dev_t *, const char *, const char *); + +size_t fido_assert_authdata_len(const fido_assert_t *, size_t); +size_t fido_assert_clientdata_hash_len(const fido_assert_t *); +size_t fido_assert_count(const fido_assert_t *); +size_t fido_assert_hmac_secret_len(const fido_assert_t *, size_t); +size_t fido_assert_id_len(const fido_assert_t *, size_t); +size_t fido_assert_sig_len(const fido_assert_t *, size_t); +size_t fido_assert_user_id_len(const fido_assert_t *, size_t); +size_t fido_cbor_info_aaguid_len(const fido_cbor_info_t *); +size_t fido_cbor_info_extensions_len(const fido_cbor_info_t *); +size_t fido_cbor_info_options_len(const fido_cbor_info_t *); +size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *); +size_t fido_cbor_info_versions_len(const fido_cbor_info_t *); +size_t fido_cred_authdata_len(const fido_cred_t *); +size_t fido_cred_clientdata_hash_len(const fido_cred_t *); +size_t fido_cred_id_len(const fido_cred_t *); +size_t fido_cred_user_id_len(const fido_cred_t *); +size_t fido_cred_pubkey_len(const fido_cred_t *); +size_t fido_cred_sig_len(const fido_cred_t *); +size_t fido_cred_x5c_len(const fido_cred_t *); + +uint8_t fido_assert_flags(const fido_assert_t *, size_t); +uint32_t fido_assert_sigcount(const fido_assert_t *, size_t); +uint8_t fido_cred_flags(const fido_cred_t *); +uint8_t fido_dev_protocol(const fido_dev_t *); +uint8_t fido_dev_major(const fido_dev_t *); +uint8_t fido_dev_minor(const fido_dev_t *); +uint8_t fido_dev_build(const fido_dev_t *); +uint8_t fido_dev_flags(const fido_dev_t *); +int16_t fido_dev_info_vendor(const fido_dev_info_t *); +int16_t fido_dev_info_product(const fido_dev_info_t *); +uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *); + +bool fido_dev_is_fido2(const fido_dev_t *); + +#endif /* !_FIDO_H */ diff --git a/src/fido/bio.h b/src/fido/bio.h new file mode 100644 index 0000000..31dffe4 --- /dev/null +++ b/src/fido/bio.h @@ -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. + */ + +#ifndef _FIDO_BIO_H +#define _FIDO_BIO_H + +#include +#include + +#include "fido/err.h" +#include "fido/param.h" + +#ifdef _FIDO_INTERNAL +struct fido_bio_template { + fido_blob_t id; + char *name; +}; + +struct fido_bio_template_array { + struct fido_bio_template *ptr; + size_t n_alloc; /* number of allocated entries */ + size_t n_rx; /* number of populated entries */ +}; + +struct fido_bio_enroll { + uint8_t remaining_samples; + uint8_t last_status; + fido_blob_t *token; +}; + +struct fido_bio_info { + uint8_t type; + uint8_t max_samples; +}; +#endif + +typedef struct fido_bio_template fido_bio_template_t; +typedef struct fido_bio_template_array fido_bio_template_array_t; +typedef struct fido_bio_enroll fido_bio_enroll_t; +typedef struct fido_bio_info fido_bio_info_t; + +#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 + +const char *fido_bio_template_name(const fido_bio_template_t *); +const fido_bio_template_t *fido_bio_template(const fido_bio_template_array_t *, + size_t); +const unsigned char *fido_bio_template_id_ptr(const fido_bio_template_t *); +fido_bio_enroll_t *fido_bio_enroll_new(void); +fido_bio_info_t *fido_bio_info_new(void); +fido_bio_template_array_t *fido_bio_template_array_new(void); +fido_bio_template_t *fido_bio_template_new(void); +int fido_bio_dev_enroll_begin(fido_dev_t *, fido_bio_template_t *, + fido_bio_enroll_t *, uint32_t, const char *); +int fido_bio_dev_enroll_cancel(fido_dev_t *); +int fido_bio_dev_enroll_continue(fido_dev_t *, const fido_bio_template_t *, + fido_bio_enroll_t *, uint32_t); +int fido_bio_dev_enroll_remove(fido_dev_t *, const fido_bio_template_t *, + const char *); +int fido_bio_dev_get_info(fido_dev_t *, fido_bio_info_t *); +int fido_bio_dev_get_template_array(fido_dev_t *, fido_bio_template_array_t *, + const char *); +int fido_bio_dev_set_template_name(fido_dev_t *, const fido_bio_template_t *, + const char *); +int fido_bio_template_set_id(fido_bio_template_t *, const unsigned char *, + size_t); +int fido_bio_template_set_name(fido_bio_template_t *, const char *); +size_t fido_bio_template_array_count(const fido_bio_template_array_t *); +size_t fido_bio_template_id_len(const fido_bio_template_t *); +uint8_t fido_bio_enroll_last_status(const fido_bio_enroll_t *); +uint8_t fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *); +uint8_t fido_bio_info_max_samples(const fido_bio_info_t *); +uint8_t fido_bio_info_type(const fido_bio_info_t *); +void fido_bio_enroll_free(fido_bio_enroll_t **); +void fido_bio_info_free(fido_bio_info_t **); +void fido_bio_template_array_free(fido_bio_template_array_t **); +void fido_bio_template_free(fido_bio_template_t **); + +#endif /* !_FIDO_BIO_H */ diff --git a/src/fido/credman.h b/src/fido/credman.h new file mode 100644 index 0000000..1c7cafe --- /dev/null +++ b/src/fido/credman.h @@ -0,0 +1,74 @@ +/* + * 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 _FIDO_CREDMAN_H +#define _FIDO_CREDMAN_H + +#include +#include + +#include "fido/err.h" +#include "fido/param.h" + +#ifdef _FIDO_INTERNAL +struct fido_credman_metadata { + uint64_t rk_existing; + uint64_t rk_remaining; +}; + +struct fido_credman_single_rp { + fido_rp_t rp_entity; + fido_blob_t rp_id_hash; +}; + +struct fido_credman_rp { + struct fido_credman_single_rp *ptr; + size_t n_alloc; /* number of allocated entries */ + size_t n_rx; /* number of populated entries */ +}; + +struct fido_credman_rk { + fido_cred_t *ptr; + size_t n_alloc; /* number of allocated entries */ + size_t n_rx; /* number of populated entries */ +}; +#endif + +typedef struct fido_credman_metadata fido_credman_metadata_t; +typedef struct fido_credman_rk fido_credman_rk_t; +typedef struct fido_credman_rp fido_credman_rp_t; + +const char *fido_credman_rp_id(const fido_credman_rp_t *, size_t); +const char *fido_credman_rp_name(const fido_credman_rp_t *, size_t); + +const fido_cred_t *fido_credman_rk(const fido_credman_rk_t *, size_t); +const unsigned char *fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *, + size_t); + +fido_credman_metadata_t *fido_credman_metadata_new(void); +fido_credman_rk_t *fido_credman_rk_new(void); +fido_credman_rp_t *fido_credman_rp_new(void); + +int fido_credman_del_dev_rk(fido_dev_t *, const unsigned char *, size_t, + const char *); +int fido_credman_get_dev_metadata(fido_dev_t *, fido_credman_metadata_t *, + const char *); +int fido_credman_get_dev_rk(fido_dev_t *, const char *, fido_credman_rk_t *, + const char *); +int fido_credman_get_dev_rp(fido_dev_t *, fido_credman_rp_t *, const char *); + +size_t fido_credman_rk_count(const fido_credman_rk_t *); +size_t fido_credman_rp_count(const fido_credman_rp_t *); +size_t fido_credman_rp_id_hash_len(const fido_credman_rp_t *, size_t); + +uint64_t fido_credman_rk_existing(const fido_credman_metadata_t *); +uint64_t fido_credman_rk_remaining(const fido_credman_metadata_t *); + +void fido_credman_metadata_free(fido_credman_metadata_t **); +void fido_credman_rk_free(fido_credman_rk_t **); +void fido_credman_rp_free(fido_credman_rp_t **); + +#endif /* !_FIDO_CREDMAN_H */ diff --git a/src/fido/eddsa.h b/src/fido/eddsa.h new file mode 100644 index 0000000..9de272d --- /dev/null +++ b/src/fido/eddsa.h @@ -0,0 +1,40 @@ +/* + * 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 _FIDO_EDDSA_H +#define _FIDO_EDDSA_H + +#include + +#include +#include + +eddsa_pk_t *eddsa_pk_new(void); +void eddsa_pk_free(eddsa_pk_t **); +EVP_PKEY *eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *); + +int eddsa_pk_from_EVP_PKEY(eddsa_pk_t *, const EVP_PKEY *); +int eddsa_pk_from_ptr(eddsa_pk_t *, const void *, size_t); + +#ifdef _FIDO_INTERNAL + +#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L +#define EVP_PKEY_ED25519 EVP_PKEY_NONE +int EVP_PKEY_get_raw_public_key(const EVP_PKEY *, unsigned char *, size_t *); +EVP_PKEY *EVP_PKEY_new_raw_public_key(int, ENGINE *, const unsigned char *, + size_t); +int EVP_DigestVerify(EVP_MD_CTX *, const unsigned char *, size_t, + const unsigned char *, size_t); +#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +EVP_MD_CTX *EVP_MD_CTX_new(void); +void EVP_MD_CTX_free(EVP_MD_CTX *); +#endif + +#endif /* _FIDO_INTERNAL */ + +#endif /* !_FIDO_EDDSA_H */ diff --git a/src/fido/err.h b/src/fido/err.h new file mode 100644 index 0000000..11f52bc --- /dev/null +++ b/src/fido/err.h @@ -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. + */ + +#ifndef _FIDO_ERR_H +#define _FIDO_ERR_H + +#define FIDO_ERR_SUCCESS 0x00 +#define FIDO_ERR_INVALID_COMMAND 0x01 +#define FIDO_ERR_INVALID_PARAMETER 0x02 +#define FIDO_ERR_INVALID_LENGTH 0x03 +#define FIDO_ERR_INVALID_SEQ 0x04 +#define FIDO_ERR_TIMEOUT 0x05 +#define FIDO_ERR_CHANNEL_BUSY 0x06 +#define FIDO_ERR_LOCK_REQUIRED 0x0a +#define FIDO_ERR_INVALID_CHANNEL 0x0b +#define FIDO_ERR_CBOR_UNEXPECTED_TYPE 0x11 +#define FIDO_ERR_INVALID_CBOR 0x12 +#define FIDO_ERR_MISSING_PARAMETER 0x14 +#define FIDO_ERR_LIMIT_EXCEEDED 0x15 +#define FIDO_ERR_UNSUPPORTED_EXTENSION 0x16 +#define FIDO_ERR_CREDENTIAL_EXCLUDED 0x19 +#define FIDO_ERR_PROCESSING 0x21 +#define FIDO_ERR_INVALID_CREDENTIAL 0x22 +#define FIDO_ERR_USER_ACTION_PENDING 0x23 +#define FIDO_ERR_OPERATION_PENDING 0x24 +#define FIDO_ERR_NO_OPERATIONS 0x25 +#define FIDO_ERR_UNSUPPORTED_ALGORITHM 0x26 +#define FIDO_ERR_OPERATION_DENIED 0x27 +#define FIDO_ERR_KEY_STORE_FULL 0x28 +#define FIDO_ERR_NOT_BUSY 0x29 +#define FIDO_ERR_NO_OPERATION_PENDING 0x2a +#define FIDO_ERR_UNSUPPORTED_OPTION 0x2b +#define FIDO_ERR_INVALID_OPTION 0x2c +#define FIDO_ERR_KEEPALIVE_CANCEL 0x2d +#define FIDO_ERR_NO_CREDENTIALS 0x2e +#define FIDO_ERR_USER_ACTION_TIMEOUT 0x2f +#define FIDO_ERR_NOT_ALLOWED 0x30 +#define FIDO_ERR_PIN_INVALID 0x31 +#define FIDO_ERR_PIN_BLOCKED 0x32 +#define FIDO_ERR_PIN_AUTH_INVALID 0x33 +#define FIDO_ERR_PIN_AUTH_BLOCKED 0x34 +#define FIDO_ERR_PIN_NOT_SET 0x35 +#define FIDO_ERR_PIN_REQUIRED 0x36 +#define FIDO_ERR_PIN_POLICY_VIOLATION 0x37 +#define FIDO_ERR_PIN_TOKEN_EXPIRED 0x38 +#define FIDO_ERR_REQUEST_TOO_LARGE 0x39 +#define FIDO_ERR_ACTION_TIMEOUT 0x3a +#define FIDO_ERR_UP_REQUIRED 0x3b +#define FIDO_ERR_ERR_OTHER 0x7f +#define FIDO_ERR_SPEC_LAST 0xdf + +/* defined internally */ +#define FIDO_OK FIDO_ERR_SUCCESS +#define FIDO_ERR_TX -1 +#define FIDO_ERR_RX -2 +#define FIDO_ERR_RX_NOT_CBOR -3 +#define FIDO_ERR_RX_INVALID_CBOR -4 +#define FIDO_ERR_INVALID_PARAM -5 +#define FIDO_ERR_INVALID_SIG -6 +#define FIDO_ERR_INVALID_ARGUMENT -7 +#define FIDO_ERR_USER_PRESENCE_REQUIRED -8 +#define FIDO_ERR_INTERNAL -9 + +const char *fido_strerr(int); + +#endif /* _FIDO_ERR_H */ diff --git a/src/fido/es256.h b/src/fido/es256.h new file mode 100644 index 0000000..d3d13dd --- /dev/null +++ b/src/fido/es256.h @@ -0,0 +1,34 @@ +/* + * 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 _FIDO_ES256_H +#define _FIDO_ES256_H + +#include + +#include +#include + +es256_pk_t *es256_pk_new(void); +void es256_pk_free(es256_pk_t **); +EVP_PKEY *es256_pk_to_EVP_PKEY(const es256_pk_t *); + +int es256_pk_from_EC_KEY(es256_pk_t *, const EC_KEY *); +int es256_pk_from_ptr(es256_pk_t *, const void *, size_t); + +#ifdef _FIDO_INTERNAL +es256_sk_t *es256_sk_new(void); +void es256_sk_free(es256_sk_t **); +EVP_PKEY *es256_sk_to_EVP_PKEY(const es256_sk_t *); + +int es256_derive_pk(const es256_sk_t *, es256_pk_t *); +int es256_sk_create(es256_sk_t *); + +int es256_pk_set_x(es256_pk_t *, const unsigned char *); +int es256_pk_set_y(es256_pk_t *, const unsigned char *); +#endif + +#endif /* !_FIDO_ES256_H */ diff --git a/src/fido/param.h b/src/fido/param.h new file mode 100644 index 0000000..9e12ac6 --- /dev/null +++ b/src/fido/param.h @@ -0,0 +1,84 @@ +/* + * 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 _FIDO_PARAM_H +#define _FIDO_PARAM_H + +/* Authentication data flags. */ +#define CTAP_AUTHDATA_USER_PRESENT 0x01 +#define CTAP_AUTHDATA_USER_VERIFIED 0x04 +#define CTAP_AUTHDATA_ATT_CRED 0x40 +#define CTAP_AUTHDATA_EXT_DATA 0x80 + +/* CTAPHID command opcodes. */ +#define CTAP_CMD_PING 0x01 +#define CTAP_CMD_MSG 0x03 +#define CTAP_CMD_LOCK 0x04 +#define CTAP_CMD_INIT 0x06 +#define CTAP_CMD_WINK 0x08 +#define CTAP_CMD_CBOR 0x10 +#define CTAP_CMD_CANCEL 0x11 +#define CTAP_KEEPALIVE 0x3b +#define CTAP_FRAME_INIT 0x80 + +/* CTAPHID CBOR command opcodes. */ +#define CTAP_CBOR_MAKECRED 0x01 +#define CTAP_CBOR_ASSERT 0x02 +#define CTAP_CBOR_GETINFO 0x04 +#define CTAP_CBOR_CLIENT_PIN 0x06 +#define CTAP_CBOR_RESET 0x07 +#define CTAP_CBOR_NEXT_ASSERT 0x08 +#define CTAP_CBOR_BIO_ENROLL_PRE 0x40 +#define CTAP_CBOR_CRED_MGMT_PRE 0x41 + +/* U2F command opcodes. */ +#define U2F_CMD_REGISTER 0x01 +#define U2F_CMD_AUTH 0x02 + +/* U2F command flags. */ +#define U2F_AUTH_SIGN 0x03 +#define U2F_AUTH_CHECK 0x07 + +/* ISO7816-4 status words. */ +#define SW_CONDITIONS_NOT_SATISFIED 0x6985 +#define SW_WRONG_DATA 0x6a80 +#define SW_NO_ERROR 0x9000 + +/* HID Broadcast channel ID. */ +#define CTAP_CID_BROADCAST 0xffffffff + +/* Expected size of a HID report in bytes. */ +#define CTAP_RPT_SIZE 64 + +/* Randomness device on UNIX-like platforms. */ +#ifndef FIDO_RANDOM_DEV +#define FIDO_RANDOM_DEV "/dev/urandom" +#endif + +/* CTAP capability bits. */ +#define FIDO_CAP_WINK 0x01 /* if set, device supports CTAP_CMD_WINK */ +#define FIDO_CAP_CBOR 0x04 /* if set, device supports CTAP_CMD_CBOR */ +#define FIDO_CAP_NMSG 0x08 /* if set, device doesn't support CTAP_CMD_MSG */ + +/* Supported COSE algorithms. */ +#define COSE_ES256 -7 +#define COSE_EDDSA -8 +#define COSE_ECDH_ES256 -25 +#define COSE_RS256 -257 + +/* Supported COSE types. */ +#define COSE_KTY_OKP 1 +#define COSE_KTY_EC2 2 +#define COSE_KTY_RSA 3 + +/* Supported curves. */ +#define COSE_P256 1 +#define COSE_ED25519 6 + +/* Supported extensions. */ +#define FIDO_EXT_HMAC_SECRET 0x01 + +#endif /* !_FIDO_PARAM_H */ diff --git a/src/fido/rs256.h b/src/fido/rs256.h new file mode 100644 index 0000000..d2fa162 --- /dev/null +++ b/src/fido/rs256.h @@ -0,0 +1,22 @@ +/* + * 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 _FIDO_RS256_H +#define _FIDO_RS256_H + +#include + +#include +#include + +rs256_pk_t *rs256_pk_new(void); +void rs256_pk_free(rs256_pk_t **); +EVP_PKEY *rs256_pk_to_EVP_PKEY(const rs256_pk_t *); + +int rs256_pk_from_RSA(rs256_pk_t *, const RSA *); +int rs256_pk_from_ptr(rs256_pk_t *, const void *, size_t); + +#endif /* !_FIDO_RS256_H */ diff --git a/src/hid.c b/src/hid.c new file mode 100644 index 0000000..783d22c --- /dev/null +++ b/src/hid.c @@ -0,0 +1,70 @@ +/* + * 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 +#include "fido.h" + +fido_dev_info_t * +fido_dev_info_new(size_t n) +{ + return (calloc(n, sizeof(fido_dev_info_t))); +} + +void +fido_dev_info_free(fido_dev_info_t **devlist_p, size_t n) +{ + fido_dev_info_t *devlist; + + if (devlist_p == NULL || (devlist = *devlist_p) == NULL) + return; + + for (size_t i = 0; i < n; i++) { + const fido_dev_info_t *di = &devlist[i]; + free(di->path); + free(di->manufacturer); + free(di->product); + } + + free(devlist); + + *devlist_p = NULL; +} + +const fido_dev_info_t * +fido_dev_info_ptr(const fido_dev_info_t *devlist, size_t i) +{ + return (&devlist[i]); +} + +const char * +fido_dev_info_path(const fido_dev_info_t *di) +{ + return (di->path); +} + +int16_t +fido_dev_info_vendor(const fido_dev_info_t *di) +{ + return (di->vendor_id); +} + +int16_t +fido_dev_info_product(const fido_dev_info_t *di) +{ + return (di->product_id); +} + +const char * +fido_dev_info_manufacturer_string(const fido_dev_info_t *di) +{ + return (di->manufacturer); +} + +const char * +fido_dev_info_product_string(const fido_dev_info_t *di) +{ + return (di->product); +} diff --git a/src/hid_linux.c b/src/hid_linux.c new file mode 100644 index 0000000..c7cabc9 --- /dev/null +++ b/src/hid_linux.c @@ -0,0 +1,344 @@ +/* + * 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 + +#include +#include + +#include +#include +#include +#include + +#include "fido.h" + +#define REPORT_LEN 65 + +static int +get_key_len(uint8_t tag, uint8_t *key, size_t *key_len) +{ + *key = tag & 0xfc; + if ((*key & 0xf0) == 0xf0) { + fido_log_debug("%s: *key=0x%02x", __func__, *key); + return (-1); + } + + *key_len = tag & 0x3; + if (*key_len == 3) { + *key_len = 4; + } + + return (0); +} + +static int +get_key_val(const void *body, size_t key_len, uint32_t *val) +{ + const uint8_t *ptr = body; + + switch (key_len) { + case 0: + *val = 0; + break; + case 1: + *val = ptr[0]; + break; + case 2: + *val = (uint32_t)((ptr[1] << 8) | ptr[0]); + break; + default: + fido_log_debug("%s: key_len=%zu", __func__, key_len); + return (-1); + } + + return (0); +} + +static int +get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page, + uint32_t *usage) +{ + const uint8_t *ptr; + size_t len; + + ptr = hrd->value; + len = hrd->size; + + while (len > 0) { + const uint8_t tag = ptr[0]; + ptr++; + len--; + + uint8_t key; + size_t key_len; + uint32_t key_val; + + if (get_key_len(tag, &key, &key_len) < 0 || key_len > len || + get_key_val(ptr, key_len, &key_val) < 0) { + return (-1); + } + + if (key == 0x4) { + *usage_page = key_val; + } else if (key == 0x8) { + *usage = key_val; + } + + ptr += key_len; + len -= key_len; + } + + return (0); +} + +static int +get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) +{ + int r; + int s = -1; + int fd; + int ok = -1; + + if ((fd = open(path, O_RDONLY)) < 0) { + fido_log_debug("%s: open", __func__); + return (-1); + } + + if ((r = ioctl(fd, HIDIOCGRDESCSIZE, &s)) < 0 || s < 0 || + (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { + fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__); + goto fail; + } + + hrd->size = s; + + if ((r = ioctl(fd, HIDIOCGRDESC, hrd)) < 0) { + fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__); + goto fail; + } + + ok = 0; +fail: + if (fd != -1) + close(fd); + + return (ok); +} + +static bool +is_fido(const char *path) +{ + uint32_t usage = 0; + uint32_t usage_page = 0; + struct hidraw_report_descriptor hrd; + + memset(&hrd, 0, sizeof(hrd)); + + if (get_report_descriptor(path, &hrd) < 0 || + get_usage_info(&hrd, &usage_page, &usage) < 0) { + return (false); + } + + return (usage_page == 0xf1d0); +} + +static int +parse_uevent(struct udev_device *dev, int16_t *vendor_id, int16_t *product_id) +{ + const char *uevent; + char *cp; + char *p; + char *s; + int ok = -1; + short unsigned int x; + short unsigned int y; + + if ((uevent = udev_device_get_sysattr_value(dev, "uevent")) == NULL) + return (-1); + + if ((s = cp = strdup(uevent)) == NULL) + return (-1); + + for ((p = strsep(&cp, "\n")); p && *p != '\0'; (p = strsep(&cp, "\n"))) { + if (strncmp(p, "HID_ID=", 7) == 0) { + if (sscanf(p + 7, "%*x:%hx:%hx", &x, &y) == 2) { + *vendor_id = (int16_t)x; + *product_id = (int16_t)y; + ok = 0; + } + break; + } + } + + free(s); + + return (ok); +} + +static int +copy_info(fido_dev_info_t *di, struct udev *udev, + struct udev_list_entry *udev_entry) +{ + const char *name; + const char *path; + const char *manufacturer; + const char *product; + struct udev_device *dev = NULL; + struct udev_device *hid_parent; + struct udev_device *usb_parent; + int ok = -1; + + memset(di, 0, sizeof(*di)); + + if ((name = udev_list_entry_get_name(udev_entry)) == NULL || + (dev = udev_device_new_from_syspath(udev, name)) == NULL || + (path = udev_device_get_devnode(dev)) == NULL || + is_fido(path) == 0) + goto fail; + + if ((hid_parent = udev_device_get_parent_with_subsystem_devtype(dev, + "hid", NULL)) == NULL) + goto fail; + + if ((usb_parent = udev_device_get_parent_with_subsystem_devtype(dev, + "usb", "usb_device")) == NULL) + goto fail; + + if (parse_uevent(hid_parent, &di->vendor_id, &di->product_id) < 0 || + (manufacturer = udev_device_get_sysattr_value(usb_parent, + "manufacturer")) == NULL || + (product = udev_device_get_sysattr_value(usb_parent, + "product")) == NULL) + goto fail; + + di->path = strdup(path); + di->manufacturer = strdup(manufacturer); + di->product = strdup(product); + + if (di->path == NULL || + di->manufacturer == NULL || + di->product == NULL) + goto fail; + + ok = 0; +fail: + if (dev != NULL) + udev_device_unref(dev); + + if (ok < 0) { + free(di->path); + free(di->manufacturer); + free(di->product); + explicit_bzero(di, sizeof(*di)); + } + + return (ok); +} + +int +fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +{ + struct udev *udev = NULL; + struct udev_enumerate *udev_enum = NULL; + struct udev_list_entry *udev_list; + struct udev_list_entry *udev_entry; + int r = FIDO_ERR_INTERNAL; + + *olen = 0; + + if (ilen == 0) + return (FIDO_OK); /* nothing to do */ + + if (devlist == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + if ((udev = udev_new()) == NULL || + (udev_enum = udev_enumerate_new(udev)) == NULL) + goto fail; + + if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 || + udev_enumerate_scan_devices(udev_enum) < 0 || + (udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) + goto fail; + + udev_list_entry_foreach(udev_entry, udev_list) { + if (copy_info(&devlist[*olen], udev, udev_entry) == 0) { + if (++(*olen) == ilen) + break; + } + } + + r = FIDO_OK; +fail: + if (udev_enum != NULL) + udev_enumerate_unref(udev_enum); + if (udev != NULL) + udev_unref(udev); + + return (r); +} + +void * +fido_hid_open(const char *path) +{ + int *fd; + + if ((fd = malloc(sizeof(*fd))) == NULL || + (*fd = open(path, O_RDWR)) < 0) { + free(fd); + return (NULL); + } + + return (fd); +} + +void +fido_hid_close(void *handle) +{ + int *fd = handle; + + close(*fd); + free(fd); +} + +int +fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) +{ + int *fd = handle; + ssize_t r; + + (void)ms; /* XXX */ + + if (len != REPORT_LEN - 1) { + fido_log_debug("%s: invalid len", __func__); + return (-1); + } + + if ((r = read(*fd, buf, len)) < 0 || r != REPORT_LEN - 1) + return (-1); + + return (REPORT_LEN - 1); +} + +int +fido_hid_write(void *handle, const unsigned char *buf, size_t len) +{ + int *fd = handle; + ssize_t r; + + if (len != REPORT_LEN) { + fido_log_debug("%s: invalid len", __func__); + return (-1); + } + + if ((r = write(*fd, buf, len)) < 0 || r != REPORT_LEN) { + fido_log_debug("%s: write", __func__); + return (-1); + } + + return (REPORT_LEN); +} diff --git a/src/hid_openbsd.c b/src/hid_openbsd.c new file mode 100644 index 0000000..8b92bd6 --- /dev/null +++ b/src/hid_openbsd.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2019 Google LLC. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "fido.h" + +#define MAX_UHID 64 +#define MAX_REPORT_LEN (sizeof(((struct usb_ctl_report *)(NULL))->ucr_data)) + +struct hid_openbsd { + int fd; + size_t report_in_len; + size_t report_out_len; +}; + +int +fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +{ + size_t i; + char path[64]; + int is_fido, fd; + struct usb_device_info udi; + report_desc_t rdesc = NULL; + hid_data_t hdata = NULL; + hid_item_t hitem; + fido_dev_info_t *di; + + if (ilen == 0) + return (FIDO_OK); /* nothing to do */ + + if (devlist == NULL || olen == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) { + snprintf(path, sizeof(path), "/dev/uhid%zu", i); + if ((fd = open(path, O_RDWR)) == -1) { + if (errno != ENOENT && errno != ENXIO) { + fido_log_debug("%s: open %s: %s", __func__, + path, strerror(errno)); + } + continue; + } + memset(&udi, 0, sizeof(udi)); + if (ioctl(fd, USB_GET_DEVICEINFO, &udi) != 0) { + fido_log_debug("%s: get device info %s: %s", __func__, + path, strerror(errno)); + close(fd); + continue; + } + if ((rdesc = hid_get_report_desc(fd)) == NULL) { + fido_log_debug("%s: failed to get report descriptor: %s", + __func__, path); + close(fd); + continue; + } + if ((hdata = hid_start_parse(rdesc, + 1<path = strdup(path)) == NULL || + (di->manufacturer = strdup(udi.udi_vendor)) == NULL || + (di->product = strdup(udi.udi_product)) == NULL) { + free(di->path); + free(di->manufacturer); + free(di->product); + explicit_bzero(di, sizeof(*di)); + return FIDO_ERR_INTERNAL; + } + di->vendor_id = udi.udi_vendorNo; + di->product_id = udi.udi_productNo; + (*olen)++; + } + + return FIDO_OK; +} + +/* + * Workaround for OpenBSD <=6.6-current (as of 201910) bug that loses + * sync of DATA0/DATA1 sequence bit across uhid open/close. + * Send pings until we get a response - early pings with incorrect + * sequence bits will be ignored as duplicate packets by the device. + */ +static int +terrible_ping_kludge(struct hid_openbsd *ctx) +{ + u_char data[256]; + int i, n; + struct pollfd pfd; + + if (sizeof(data) < ctx->report_out_len + 1) + return -1; + for (i = 0; i < 4; i++) { + memset(data, 0, sizeof(data)); + /* broadcast channel ID */ + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + data[4] = 0xff; + /* Ping command */ + data[5] = 0x81; + /* One byte ping only, Vasili */ + data[6] = 0; + data[7] = 1; + fido_log_debug("%s: send ping %d", __func__, i); + if (fido_hid_write(ctx, data, ctx->report_out_len + 1) == -1) + return -1; + fido_log_debug("%s: wait reply", __func__); + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = ctx->fd; + pfd.events = POLLIN; + if ((n = poll(&pfd, 1, 100)) == -1) { + fido_log_debug("%s: poll: %s", __func__, strerror(errno)); + return -1; + } else if (n == 0) { + fido_log_debug("%s: timed out", __func__); + continue; + } + if (fido_hid_read(ctx, data, ctx->report_out_len, 250) == -1) + return -1; + /* + * Ping isn't always supported on the broadcast channel, + * so we might get an error, but we don't care - we're + * synched now. + */ + fido_log_debug("%s: got reply", __func__); + fido_log_xxd(data, ctx->report_out_len); + return 0; + } + fido_log_debug("%s: no response", __func__); + return -1; +} + +void * +fido_hid_open(const char *path) +{ + struct hid_openbsd *ret = NULL; + report_desc_t rdesc = NULL; + int len, usb_report_id = 0; + + if ((ret = calloc(1, sizeof(*ret))) == NULL || + (ret->fd = open(path, O_RDWR)) < 0) { + free(ret); + return (NULL); + } + if (ioctl(ret->fd, USB_GET_REPORT_ID, &usb_report_id) != 0) { + fido_log_debug("%s: failed to get report ID: %s", __func__, + strerror(errno)); + goto fail; + } + if ((rdesc = hid_get_report_desc(ret->fd)) == NULL) { + fido_log_debug("%s: failed to get report descriptor", __func__); + goto fail; + } + if ((len = hid_report_size(rdesc, hid_input, usb_report_id)) <= 0 || + (size_t)len > MAX_REPORT_LEN) { + fido_log_debug("%s: bad input report size %d", __func__, len); + goto fail; + } + ret->report_in_len = (size_t)len; + if ((len = hid_report_size(rdesc, hid_output, usb_report_id)) <= 0 || + (size_t)len > MAX_REPORT_LEN) { + fido_log_debug("%s: bad output report size %d", __func__, len); + fail: + hid_dispose_report_desc(rdesc); + close(ret->fd); + free(ret); + return NULL; + } + ret->report_out_len = (size_t)len; + hid_dispose_report_desc(rdesc); + fido_log_debug("%s: USB report ID %d, inlen = %zu outlen = %zu", + __func__, usb_report_id, ret->report_in_len, ret->report_out_len); + + /* + * OpenBSD (as of 201910) has a bug that causes it to lose + * track of the DATA0/DATA1 sequence toggle across uhid device + * open and close. This is a terrible hack to work around it. + */ + if (terrible_ping_kludge(ret) != 0) { + fido_hid_close(ret); + return NULL; + } + + return (ret); +} + +void +fido_hid_close(void *handle) +{ + struct hid_openbsd *ctx = (struct hid_openbsd *)handle; + + close(ctx->fd); + free(ctx); +} + +int +fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) +{ + struct hid_openbsd *ctx = (struct hid_openbsd *)handle; + ssize_t r; + + (void)ms; /* XXX */ + + if (len != ctx->report_in_len) { + fido_log_debug("%s: invalid len: got %zu, want %zu", __func__, + len, ctx->report_in_len); + return (-1); + } + if ((r = read(ctx->fd, buf, len)) == -1 || (size_t)r != len) { + fido_log_debug("%s: read: %s", __func__, strerror(errno)); + return (-1); + } + return ((int)len); +} + +int +fido_hid_write(void *handle, const unsigned char *buf, size_t len) +{ + struct hid_openbsd *ctx = (struct hid_openbsd *)handle; + ssize_t r; + + if (len != ctx->report_out_len + 1) { + fido_log_debug("%s: invalid len: got %zu, want %zu", __func__, + len, ctx->report_out_len); + return (-1); + } + if ((r = write(ctx->fd, buf + 1, len - 1)) == -1 || + (size_t)r != len - 1) { + fido_log_debug("%s: write: %s", __func__, strerror(errno)); + return (-1); + } + return ((int)len); +} diff --git a/src/hid_osx.c b/src/hid_osx.c new file mode 100644 index 0000000..b705b43 --- /dev/null +++ b/src/hid_osx.c @@ -0,0 +1,410 @@ +/* + * 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 + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include + +#include "fido.h" + +#define REPORT_LEN 65 + +struct dev { + IOHIDDeviceRef ref; + CFStringRef loop_id; +}; + +static int +get_int32(IOHIDDeviceRef dev, CFStringRef key, int32_t *v) +{ + CFTypeRef ref; + + if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL || + CFGetTypeID(ref) != CFNumberGetTypeID()) { + fido_log_debug("%s: IOHIDDeviceGetProperty", __func__); + return (-1); + } + + if (CFNumberGetType(ref) != kCFNumberSInt32Type && + CFNumberGetType(ref) != kCFNumberSInt64Type) { + fido_log_debug("%s: CFNumberGetType", __func__); + return (-1); + } + + if (CFNumberGetValue(ref, kCFNumberSInt32Type, v) == false) { + fido_log_debug("%s: CFNumberGetValue", __func__); + return (-1); + } + + return (0); +} + +static int +get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len) +{ + CFTypeRef ref; + + memset(buf, 0, len); + + if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL || + CFGetTypeID(ref) != CFStringGetTypeID()) { + fido_log_debug("%s: IOHIDDeviceGetProperty", __func__); + return (-1); + } + + if (CFStringGetCString(ref, buf, len, kCFStringEncodingUTF8) == false) { + fido_log_debug("%s: CFStringGetCString", __func__); + return (-1); + } + + return (0); +} + +static bool +is_fido(IOHIDDeviceRef dev) +{ + uint32_t usage_page; + int32_t report_len; + + if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey), + (int32_t *)&usage_page) != 0 || usage_page != 0xf1d0) + return (false); + + if (get_int32(dev, CFSTR(kIOHIDMaxInputReportSizeKey), + &report_len) < 0 || report_len != REPORT_LEN - 1) { + fido_log_debug("%s: unsupported report len", __func__); + return (false); + } + + return (true); +} + +static int +get_id(IOHIDDeviceRef dev, int16_t *vendor_id, int16_t *product_id) +{ + int32_t vendor; + int32_t product; + + if (get_int32(dev, CFSTR(kIOHIDVendorIDKey), &vendor) < 0 || + vendor > UINT16_MAX) { + fido_log_debug("%s: get_int32 vendor", __func__); + return (-1); + } + + if (get_int32(dev, CFSTR(kIOHIDProductIDKey), &product) < 0 || + product > UINT16_MAX) { + fido_log_debug("%s: get_int32 product", __func__); + return (-1); + } + + *vendor_id = (int16_t)vendor; + *product_id = (int16_t)product; + + return (0); +} + +static int +get_str(IOHIDDeviceRef dev, char **manufacturer, char **product) +{ + char buf[512]; + int ok = -1; + + *manufacturer = NULL; + *product = NULL; + + if (get_utf8(dev, CFSTR(kIOHIDManufacturerKey), buf, sizeof(buf)) < 0) { + fido_log_debug("%s: get_utf8 manufacturer", __func__); + goto fail; + } + + if ((*manufacturer = strdup(buf)) == NULL) { + fido_log_debug("%s: strdup manufacturer", __func__); + goto fail; + } + + if (get_utf8(dev, CFSTR(kIOHIDProductKey), buf, sizeof(buf)) < 0) { + fido_log_debug("%s: get_utf8 product", __func__); + goto fail; + } + + if ((*product = strdup(buf)) == NULL) { + fido_log_debug("%s: strdup product", __func__); + goto fail; + } + + ok = 0; +fail: + if (ok < 0) { + free(*manufacturer); + free(*product); + *manufacturer = NULL; + *product = NULL; + } + + return (ok); +} + +static char * +get_path(IOHIDDeviceRef dev) +{ + io_service_t s; + io_string_t path; + + if ((s = IOHIDDeviceGetService(dev)) == MACH_PORT_NULL) { + fido_log_debug("%s: IOHIDDeviceGetService", __func__); + return (NULL); + } + + if (IORegistryEntryGetPath(s, kIOServicePlane, path) != KERN_SUCCESS) { + fido_log_debug("%s: IORegistryEntryGetPath", __func__); + return (NULL); + } + + return (strdup(path)); +} + +static int +copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev) +{ + memset(di, 0, sizeof(*di)); + + if (is_fido(dev) == false) + return (-1); + + if (get_id(dev, &di->vendor_id, &di->product_id) < 0 || + get_str(dev, &di->manufacturer, &di->product) < 0 || + (di->path = get_path(dev)) == NULL) { + free(di->path); + free(di->manufacturer); + free(di->product); + explicit_bzero(di, sizeof(*di)); + return (-1); + } + + return (0); +} + +int +fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +{ + IOHIDManagerRef manager = NULL; + CFSetRef devset = NULL; + CFIndex devcnt; + IOHIDDeviceRef *devs = NULL; + int r = FIDO_ERR_INTERNAL; + + *olen = 0; + + if (ilen == 0) + return (FIDO_OK); /* nothing to do */ + + if (devlist == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + if ((manager = IOHIDManagerCreate(kCFAllocatorDefault, + kIOHIDManagerOptionNone)) == NULL) { + fido_log_debug("%s: IOHIDManagerCreate", __func__); + goto fail; + } + + IOHIDManagerSetDeviceMatching(manager, NULL); + + if ((devset = IOHIDManagerCopyDevices(manager)) == NULL) { + fido_log_debug("%s: IOHIDManagerCopyDevices", __func__); + goto fail; + } + + if ((devcnt = CFSetGetCount(devset)) < 0) { + fido_log_debug("%s: CFSetGetCount", __func__); + goto fail; + } + + if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) { + fido_log_debug("%s: calloc", __func__); + goto fail; + } + + CFSetGetValues(devset, (void *)devs); + + for (CFIndex i = 0; i < devcnt; i++) { + if (copy_info(&devlist[*olen], devs[i]) == 0) { + if (++(*olen) == ilen) + break; + } + } + + r = FIDO_OK; +fail: + if (manager != NULL) + CFRelease(manager); + if (devset != NULL) + CFRelease(devset); + + free(devs); + + return (r); +} + +void * +fido_hid_open(const char *path) +{ + io_registry_entry_t entry = MACH_PORT_NULL; + struct dev *dev = NULL; + int ok = -1; + int r; + char loop_id[32]; + + if ((dev = calloc(1, sizeof(*dev))) == NULL) { + fido_log_debug("%s: calloc", __func__); + goto fail; + } + + if ((entry = IORegistryEntryFromPath(kIOMasterPortDefault, + path)) == MACH_PORT_NULL) { + fido_log_debug("%s: IORegistryEntryFromPath", __func__); + goto fail; + } + + if ((dev->ref = IOHIDDeviceCreate(kCFAllocatorDefault, + entry)) == NULL) { + fido_log_debug("%s: IOHIDDeviceCreate", __func__); + goto fail; + } + + if (IOHIDDeviceOpen(dev->ref, + kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) { + fido_log_debug("%s: IOHIDDeviceOpen", __func__); + goto fail; + } + + if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p", + (void *)dev->ref)) < 0 || (size_t)r >= sizeof(loop_id)) { + fido_log_debug("%s: snprintf", __func__); + goto fail; + } + + if ((dev->loop_id = CFStringCreateWithCString(NULL, loop_id, + kCFStringEncodingASCII)) == NULL) { + fido_log_debug("%s: CFStringCreateWithCString", __func__); + goto fail; + } + + ok = 0; +fail: + if (entry != MACH_PORT_NULL) + IOObjectRelease(entry); + + if (ok < 0 && dev != NULL) { + if (dev->ref != NULL) + CFRelease(dev->ref); + if (dev->loop_id != NULL) + CFRelease(dev->loop_id); + free(dev); + dev = NULL; + } + + return (dev); +} + +void +fido_hid_close(void *handle) +{ + struct dev *dev = handle; + + if (IOHIDDeviceClose(dev->ref, + kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) + fido_log_debug("%s: IOHIDDeviceClose", __func__); + + CFRelease(dev->ref); + CFRelease(dev->loop_id); + + free(dev); +} + +static void +read_callback(void *context, IOReturn result, void *dev, IOHIDReportType type, + uint32_t report_id, uint8_t *report, CFIndex report_len) +{ + (void)context; + (void)dev; + (void)report; + + if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput || + report_id != 0 || report_len != REPORT_LEN - 1) { + fido_log_debug("%s: io error", __func__); + } +} + +static void +removal_callback(void *context, IOReturn result, void *sender) +{ + (void)context; + (void)result; + (void)sender; + + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +int +fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) +{ + struct dev *dev = handle; + CFRunLoopRunResult r; + + (void)ms; /* XXX */ + + if (len != REPORT_LEN - 1) { + fido_log_debug("%s: invalid len", __func__); + return (-1); + } + + explicit_bzero(buf, len); + + IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len, + &read_callback, NULL); + IOHIDDeviceRegisterRemovalCallback(dev->ref, &removal_callback, dev); + IOHIDDeviceScheduleWithRunLoop(dev->ref, CFRunLoopGetCurrent(), + dev->loop_id); + + do + r = CFRunLoopRunInMode(dev->loop_id, 0.003, true); + while (r != kCFRunLoopRunHandledSource); + + IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len, NULL, NULL); + IOHIDDeviceRegisterRemovalCallback(dev->ref, NULL, NULL); + IOHIDDeviceUnscheduleFromRunLoop(dev->ref, CFRunLoopGetCurrent(), + dev->loop_id); + + return (REPORT_LEN - 1); +} + +int +fido_hid_write(void *handle, const unsigned char *buf, size_t len) +{ + struct dev *dev = handle; + + if (len != REPORT_LEN) { + fido_log_debug("%s: invalid len", __func__); + return (-1); + } + + if (IOHIDDeviceSetReport(dev->ref, kIOHIDReportTypeOutput, 0, buf + 1, + len - 1) != kIOReturnSuccess) { + fido_log_debug("%s: IOHIDDeviceSetReport", __func__); + return (-1); + } + + return (REPORT_LEN); +} diff --git a/src/hid_win.c b/src/hid_win.c new file mode 100644 index 0000000..6d93778 --- /dev/null +++ b/src/hid_win.c @@ -0,0 +1,324 @@ +/* + * 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 + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +#include "fido.h" + +#define REPORT_LEN 65 + +static bool +is_fido(HANDLE dev) +{ + PHIDP_PREPARSED_DATA data = NULL; + HIDP_CAPS caps; + uint16_t usage_page = 0; + + if (HidD_GetPreparsedData(dev, &data) == false) { + fido_log_debug("%s: HidD_GetPreparsedData", __func__); + goto fail; + } + + if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) { + fido_log_debug("%s: HidP_GetCaps", __func__); + goto fail; + } + + if (caps.OutputReportByteLength != REPORT_LEN || + caps.InputReportByteLength != REPORT_LEN) { + fido_log_debug("%s: unsupported report len", __func__); + goto fail; + } + + usage_page = caps.UsagePage; +fail: + if (data != NULL) + HidD_FreePreparsedData(data); + + return (usage_page == 0xf1d0); +} + +static int +get_int(HANDLE dev, int16_t *vendor_id, int16_t *product_id) +{ + HIDD_ATTRIBUTES attr; + + attr.Size = sizeof(attr); + + if (HidD_GetAttributes(dev, &attr) == false) { + fido_log_debug("%s: HidD_GetAttributes", __func__); + return (-1); + } + + *vendor_id = attr.VendorID; + *product_id = attr.ProductID; + + return (0); +} + +static int +get_str(HANDLE dev, char **manufacturer, char **product) +{ + wchar_t buf[512]; + int utf8_len; + int ok = -1; + + *manufacturer = NULL; + *product = NULL; + + if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) { + fido_log_debug("%s: HidD_GetManufacturerString", __func__); + goto fail; + } + + if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, + -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) { + fido_log_debug("%s: WideCharToMultiByte", __func__); + goto fail; + } + + if ((*manufacturer = malloc(utf8_len)) == NULL) { + fido_log_debug("%s: malloc", __func__); + goto fail; + } + + if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1, + *manufacturer, utf8_len, NULL, NULL) != utf8_len) { + fido_log_debug("%s: WideCharToMultiByte", __func__); + goto fail; + } + + if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) { + fido_log_debug("%s: HidD_GetProductString", __func__); + goto fail; + } + + if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, + -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) { + fido_log_debug("%s: WideCharToMultiByte", __func__); + goto fail; + } + + if ((*product = malloc(utf8_len)) == NULL) { + fido_log_debug("%s: malloc", __func__); + goto fail; + } + + if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1, + *product, utf8_len, NULL, NULL) != utf8_len) { + fido_log_debug("%s: WideCharToMultiByte", __func__); + goto fail; + } + + ok = 0; +fail: + if (ok < 0) { + free(*manufacturer); + free(*product); + *manufacturer = NULL; + *product = NULL; + } + + return (ok); +} + +static int +copy_info(fido_dev_info_t *di, const char *path) +{ + HANDLE dev = INVALID_HANDLE_VALUE; + int ok = -1; + + memset(di, 0, sizeof(*di)); + + dev = CreateFileA(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (dev == INVALID_HANDLE_VALUE || is_fido(dev) == 0) + goto fail; + + if (get_int(dev, &di->vendor_id, &di->product_id) < 0 || + get_str(dev, &di->manufacturer, &di->product) < 0) + goto fail; + + if ((di->path = strdup(path)) == NULL) + goto fail; + + ok = 0; +fail: + if (dev != INVALID_HANDLE_VALUE) + CloseHandle(dev); + + if (ok < 0) { + free(di->path); + free(di->manufacturer); + free(di->product); + explicit_bzero(di, sizeof(*di)); + } + + return (ok); +} + +int +fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +{ + GUID hid_guid = GUID_DEVINTERFACE_HID; + HDEVINFO devinfo = INVALID_HANDLE_VALUE; + SP_DEVICE_INTERFACE_DATA ifdata; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL; + DWORD len = 0; + DWORD idx = 0; + int r = FIDO_ERR_INTERNAL; + + *olen = 0; + + if (ilen == 0) + return (FIDO_OK); /* nothing to do */ + + if (devlist == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL, + DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + if (devinfo == INVALID_HANDLE_VALUE) { + fido_log_debug("%s: SetupDiGetClassDevsA", __func__); + goto fail; + } + + ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + while (SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, idx++, + &ifdata) == true) { + /* + * "Get the required buffer size. Call + * SetupDiGetDeviceInterfaceDetail with a NULL + * DeviceInterfaceDetailData pointer, a + * DeviceInterfaceDetailDataSize of zero, and a valid + * RequiredSize variable. In response to such a call, this + * function returns the required buffer size at RequiredSize + * and fails with GetLastError returning + * ERROR_INSUFFICIENT_BUFFER." + */ + if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, NULL, 0, + &len, NULL) != false || + GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1", + __func__); + goto fail; + } + + if ((ifdetail = malloc(len)) == NULL) { + fido_log_debug("%s: malloc", __func__); + goto fail; + } + + ifdetail->cbSize = sizeof(*ifdetail); + + if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, ifdetail, + len, NULL, NULL) == false) { + fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2", + __func__); + goto fail; + } + + if (copy_info(&devlist[*olen], ifdetail->DevicePath) == 0) { + if (++(*olen) == ilen) + break; + } + + free(ifdetail); + ifdetail = NULL; + } + + r = FIDO_OK; +fail: + if (devinfo != INVALID_HANDLE_VALUE) + SetupDiDestroyDeviceInfoList(devinfo); + + free(ifdetail); + + return (r); +} + +void * +fido_hid_open(const char *path) +{ + HANDLE dev; + + dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (dev == INVALID_HANDLE_VALUE) + return (NULL); + + return (dev); +} + +void +fido_hid_close(void *handle) +{ + CloseHandle(handle); +} + +int +fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) +{ + DWORD n; + int r = -1; + uint8_t report[REPORT_LEN]; + + (void)ms; /* XXX */ + + memset(report, 0, sizeof(report)); + + if (len != sizeof(report) - 1) { + fido_log_debug("%s: invalid len", __func__); + return (-1); + } + + if (ReadFile(handle, report, sizeof(report), &n, NULL) == false || + n != sizeof(report)) { + fido_log_debug("%s: ReadFile", __func__); + goto fail; + } + + r = sizeof(report) - 1; + memcpy(buf, report + 1, len); + +fail: + explicit_bzero(report, sizeof(report)); + + return (r); +} + +int +fido_hid_write(void *handle, const unsigned char *buf, size_t len) +{ + DWORD n; + + if (len != REPORT_LEN) { + fido_log_debug("%s: invalid len", __func__); + return (-1); + } + + if (WriteFile(handle, buf, (DWORD)len, &n, NULL) == false || + n != REPORT_LEN) { + fido_log_debug("%s: WriteFile", __func__); + return (-1); + } + + return (REPORT_LEN); +} diff --git a/src/info.c b/src/info.c new file mode 100644 index 0000000..e896503 --- /dev/null +++ b/src/info.c @@ -0,0 +1,410 @@ +/* + * 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 +#include "fido.h" + +static int +decode_version(const cbor_item_t *item, void *arg) +{ + fido_str_array_t *v = arg; + const size_t i = v->len; + + /* keep ptr[x] and len consistent */ + if (cbor_string_copy(item, &v->ptr[i]) < 0) { + fido_log_debug("%s: cbor_string_copy", __func__); + return (-1); + } + + v->len++; + + return (0); +} + +static int +decode_versions(const cbor_item_t *item, fido_str_array_t *v) +{ + v->ptr = NULL; + v->len = 0; + + if (cbor_isa_array(item) == false || + cbor_array_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + v->ptr = calloc(cbor_array_size(item), sizeof(char *)); + if (v->ptr == NULL) + return (-1); + + if (cbor_array_iter(item, v, decode_version) < 0) { + fido_log_debug("%s: decode_version", __func__); + return (-1); + } + + return (0); +} + +static int +decode_extension(const cbor_item_t *item, void *arg) +{ + fido_str_array_t *e = arg; + const size_t i = e->len; + + /* keep ptr[x] and len consistent */ + if (cbor_string_copy(item, &e->ptr[i]) < 0) { + fido_log_debug("%s: cbor_string_copy", __func__); + return (-1); + } + + e->len++; + + return (0); +} + +static int +decode_extensions(const cbor_item_t *item, fido_str_array_t *e) +{ + e->ptr = NULL; + e->len = 0; + + if (cbor_isa_array(item) == false || + cbor_array_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + e->ptr = calloc(cbor_array_size(item), sizeof(char *)); + if (e->ptr == NULL) + return (-1); + + if (cbor_array_iter(item, e, decode_extension) < 0) { + fido_log_debug("%s: decode_extension", __func__); + return (-1); + } + + return (0); +} + +static int +decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len) +{ + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false || + cbor_bytestring_length(item) != aaguid_len) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len); + + return (0); +} + +static int +decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_opt_array_t *o = arg; + const size_t i = o->len; + + if (cbor_isa_float_ctrl(val) == false || + cbor_float_get_width(val) != CBOR_FLOAT_0 || + cbor_is_bool(val) == false) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + if (cbor_string_copy(key, &o->name[i]) < 0) { + fido_log_debug("%s: cbor_string_copy", __func__); + return (0); /* ignore */ + } + + /* keep name/value and len consistent */ + o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE; + o->len++; + + return (0); +} + +static int +decode_options(const cbor_item_t *item, fido_opt_array_t *o) +{ + o->name = NULL; + o->value = NULL; + o->len = 0; + + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + o->name = calloc(cbor_map_size(item), sizeof(char *)); + o->value = calloc(cbor_map_size(item), sizeof(bool)); + if (o->name == NULL || o->value == NULL) + return (-1); + + return (cbor_map_iter(item, o, decode_option)); +} + +static int +decode_protocol(const cbor_item_t *item, void *arg) +{ + fido_byte_array_t *p = arg; + const size_t i = p->len; + + if (cbor_isa_uint(item) == false || + cbor_int_get_width(item) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + /* keep ptr[x] and len consistent */ + p->ptr[i] = cbor_get_uint8(item); + p->len++; + + return (0); +} + +static int +decode_protocols(const cbor_item_t *item, fido_byte_array_t *p) +{ + p->ptr = NULL; + p->len = 0; + + if (cbor_isa_array(item) == false || + cbor_array_is_definite(item) == false) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t)); + if (p->ptr == NULL) + return (-1); + + if (cbor_array_iter(item, p, decode_protocol) < 0) { + fido_log_debug("%s: decode_protocol", __func__); + return (-1); + } + + return (0); +} + +static int +parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_cbor_info_t *ci = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + switch (cbor_get_uint8(key)) { + case 1: /* versions */ + return (decode_versions(val, &ci->versions)); + case 2: /* extensions */ + return (decode_extensions(val, &ci->extensions)); + case 3: /* aaguid */ + return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid))); + case 4: /* options */ + return (decode_options(val, &ci->options)); + case 5: /* maxMsgSize */ + return (cbor_decode_uint64(val, &ci->maxmsgsiz)); + case 6: /* pinProtocols */ + return (decode_protocols(val, &ci->protocols)); + default: /* ignore */ + fido_log_debug("%s: cbor type", __func__); + return (0); + } +} + +static int +fido_dev_get_cbor_info_tx(fido_dev_t *dev) +{ + const unsigned char cbor[] = { CTAP_CBOR_GETINFO }; + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + + fido_log_debug("%s: dev=%p", __func__, (void *)dev); + + if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + return (FIDO_ERR_TX); + } + + return (FIDO_OK); +} + +static int +fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[512]; + int reply_len; + + fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev, + (void *)ci, ms); + + memset(ci, 0, sizeof(*ci)); + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + return (cbor_parse_reply(reply, (size_t)reply_len, ci, + parse_reply_element)); +} + +static int +fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) +{ + int r; + + if ((r = fido_dev_get_cbor_info_tx(dev)) != FIDO_OK || + (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) +{ + return (fido_dev_get_cbor_info_wait(dev, ci, -1)); +} + +/* + * get/set functions for fido_cbor_info_t; always at the end of the file + */ + +fido_cbor_info_t * +fido_cbor_info_new(void) +{ + return (calloc(1, sizeof(fido_cbor_info_t))); +} + +static void +free_str_array(fido_str_array_t *sa) +{ + for (size_t i = 0; i < sa->len; i++) + free(sa->ptr[i]); + + free(sa->ptr); + sa->ptr = NULL; + sa->len = 0; +} + +static void +free_opt_array(fido_opt_array_t *oa) +{ + for (size_t i = 0; i < oa->len; i++) + free(oa->name[i]); + + free(oa->name); + free(oa->value); + oa->name = NULL; + oa->value = NULL; +} + +static void +free_byte_array(fido_byte_array_t *ba) +{ + free(ba->ptr); + + ba->ptr = NULL; + ba->len = 0; +} + +void +fido_cbor_info_free(fido_cbor_info_t **ci_p) +{ + fido_cbor_info_t *ci; + + if (ci_p == NULL || (ci = *ci_p) == NULL) + return; + + free_str_array(&ci->versions); + free_str_array(&ci->extensions); + free_opt_array(&ci->options); + free_byte_array(&ci->protocols); + free(ci); + + *ci_p = NULL; +} + +char ** +fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci) +{ + return (ci->versions.ptr); +} + +size_t +fido_cbor_info_versions_len(const fido_cbor_info_t *ci) +{ + return (ci->versions.len); +} + +char ** +fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci) +{ + return (ci->extensions.ptr); +} + +size_t +fido_cbor_info_extensions_len(const fido_cbor_info_t *ci) +{ + return (ci->extensions.len); +} + +const unsigned char * +fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci) +{ + return (ci->aaguid); +} + +size_t +fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci) +{ + return (sizeof(ci->aaguid)); +} + +char ** +fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci) +{ + return (ci->options.name); +} + +const bool * +fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci) +{ + return (ci->options.value); +} + +size_t +fido_cbor_info_options_len(const fido_cbor_info_t *ci) +{ + return (ci->options.len); +} + +uint64_t +fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) +{ + return (ci->maxmsgsiz); +} + +const uint8_t * +fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci) +{ + return (ci->protocols.ptr); +} + +size_t +fido_cbor_info_protocols_len(const fido_cbor_info_t *ci) +{ + return (ci->protocols.len); +} diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..aa88720 --- /dev/null +++ b/src/io.c @@ -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. + */ + +#include +#include +#include + +#include "fido.h" +#include "packed.h" + +PACKED_TYPE(frame_t, +struct frame { + uint32_t cid; /* channel id */ + union { + uint8_t type; + struct { + uint8_t cmd; + uint8_t bcnth; + uint8_t bcntl; + uint8_t data[CTAP_RPT_SIZE - 7]; + } init; + struct { + uint8_t seq; + uint8_t data[CTAP_RPT_SIZE - 5]; + } cont; + } body; +}) + +#ifndef MIN +#define MIN(x, y) ((x) > (y) ? (y) : (x)) +#endif + +static size_t +tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count) +{ + struct frame *fp; + unsigned char pkt[sizeof(*fp) + 1]; + int n; + + if (d->io.write == NULL || (cmd & 0x80) == 0) + return (0); + + memset(&pkt, 0, sizeof(pkt)); + fp = (struct frame *)(pkt + 1); + fp->cid = d->cid; + fp->body.init.cmd = 0x80 | cmd; + fp->body.init.bcnth = (count >> 8) & 0xff; + fp->body.init.bcntl = count & 0xff; + count = MIN(count, sizeof(fp->body.init.data)); + if (count) + memcpy(&fp->body.init.data, buf, count); + + n = d->io.write(d->io_handle, pkt, sizeof(pkt)); + if (n < 0 || (size_t)n != sizeof(pkt)) + return (0); + + return (count); +} + +static size_t +tx_frame(fido_dev_t *d, int seq, const void *buf, size_t count) +{ + struct frame *fp; + unsigned char pkt[sizeof(*fp) + 1]; + int n; + + if (d->io.write == NULL || seq < 0 || seq > UINT8_MAX) + return (0); + + memset(&pkt, 0, sizeof(pkt)); + fp = (struct frame *)(pkt + 1); + fp->cid = d->cid; + fp->body.cont.seq = (uint8_t)seq; + count = MIN(count, sizeof(fp->body.cont.data)); + memcpy(&fp->body.cont.data, buf, count); + + n = d->io.write(d->io_handle, pkt, sizeof(pkt)); + if (n < 0 || (size_t)n != sizeof(pkt)) + return (0); + + return (count); +} + +int +fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count) +{ + int seq = 0; + size_t sent; + + fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__, + (void *)d, cmd, buf, count); + fido_log_xxd(buf, count); + + if (d->io_handle == NULL || count > UINT16_MAX) { + fido_log_debug("%s: invalid argument (%p, %zu)", __func__, + d->io_handle, count); + return (-1); + } + + if ((sent = tx_preamble(d, cmd, buf, count)) == 0) { + fido_log_debug("%s: tx_preamble", __func__); + return (-1); + } + + while (sent < count) { + if (seq & 0x80) { + fido_log_debug("%s: seq & 0x80", __func__); + return (-1); + } + const uint8_t *p = (const uint8_t *)buf + sent; + size_t n = tx_frame(d, seq++, p, count - sent); + if (n == 0) { + fido_log_debug("%s: tx_frame", __func__); + return (-1); + } + sent += n; + } + + return (0); +} + +static int +rx_frame(fido_dev_t *d, struct frame *fp, int ms) +{ + int n; + + if (d->io.read == NULL) + return (-1); + + n = d->io.read(d->io_handle, (unsigned char *)fp, sizeof(*fp), ms); + if (n < 0 || (size_t)n != sizeof(*fp)) + return (-1); + + return (0); +} + +static int +rx_preamble(fido_dev_t *d, struct frame *fp, int ms) +{ + do { + if (rx_frame(d, fp, ms) < 0) + return (-1); +#ifdef FIDO_FUZZ + fp->cid = d->cid; +#endif + } while (fp->cid == d->cid && + fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)); + + return (0); +} + +int +fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms) +{ + struct frame f; + uint16_t r; + uint16_t flen; + int seq; + + if (d->io_handle == NULL || (cmd & 0x80) == 0) { + fido_log_debug("%s: invalid argument (%p, 0x%02x)", __func__, + d->io_handle, cmd); + return (-1); + } + + if (rx_preamble(d, &f, ms) < 0) { + fido_log_debug("%s: rx_preamble", __func__); + return (-1); + } + + fido_log_debug("%s: initiation frame at %p, len %zu", __func__, + (void *)&f, sizeof(f)); + fido_log_xxd(&f, sizeof(f)); + +#ifdef FIDO_FUZZ + f.cid = d->cid; + f.body.init.cmd = cmd; +#endif + + if (f.cid != d->cid || f.body.init.cmd != cmd) { + fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)", + __func__, f.cid, d->cid, f.body.init.cmd, cmd); + return (-1); + } + + flen = (f.body.init.bcnth << 8) | f.body.init.bcntl; + if (count < (size_t)flen) { + fido_log_debug("%s: count < flen (%zu, %zu)", __func__, count, + (size_t)flen); + return (-1); + } + if (flen < sizeof(f.body.init.data)) { + memcpy(buf, f.body.init.data, flen); + return (flen); + } + + memcpy(buf, f.body.init.data, sizeof(f.body.init.data)); + r = sizeof(f.body.init.data); + seq = 0; + + while ((size_t)r < flen) { + if (rx_frame(d, &f, ms) < 0) { + fido_log_debug("%s: rx_frame", __func__); + return (-1); + } + + fido_log_debug("%s: continuation frame at %p, len %zu", + __func__, (void *)&f, sizeof(f)); + fido_log_xxd(&f, sizeof(f)); + +#ifdef FIDO_FUZZ + f.cid = d->cid; + f.body.cont.seq = seq; +#endif + + if (f.cid != d->cid || f.body.cont.seq != seq++) { + fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)", + __func__, f.cid, d->cid, f.body.cont.seq, seq); + return (-1); + } + + uint8_t *p = (uint8_t *)buf + r; + + if ((size_t)(flen - r) > sizeof(f.body.cont.data)) { + memcpy(p, f.body.cont.data, sizeof(f.body.cont.data)); + r += sizeof(f.body.cont.data); + } else { + memcpy(p, f.body.cont.data, flen - r); + r += (flen - r); /* break */ + } + } + + fido_log_debug("%s: payload at %p, len %zu", __func__, buf, (size_t)r); + fido_log_xxd(buf, r); + + return (r); +} + +int +fido_rx_cbor_status(fido_dev_t *d, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[2048]; + int reply_len; + + if ((reply_len = fido_rx(d, cmd, &reply, sizeof(reply), ms)) < 0 || + (size_t)reply_len < 1) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + return (reply[0]); +} diff --git a/src/iso7816.c b/src/iso7816.c new file mode 100644 index 0000000..e2ea281 --- /dev/null +++ b/src/iso7816.c @@ -0,0 +1,70 @@ +/* + * 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 +#include "fido.h" + +iso7816_apdu_t * +iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len) +{ + iso7816_apdu_t *apdu; + size_t alloc_len; + + alloc_len = sizeof(iso7816_apdu_t) + payload_len; + + if ((apdu = calloc(1, alloc_len)) == NULL) + return (NULL); + + apdu->alloc_len = alloc_len; + apdu->payload_len = payload_len; + apdu->payload_ptr = apdu->payload; + apdu->header.ins = ins; + apdu->header.p1 = p1; + apdu->header.lc2 = (payload_len >> 8) & 0xff; + apdu->header.lc3 = payload_len & 0xff; + + return (apdu); +} + +void +iso7816_free(iso7816_apdu_t **apdu_p) +{ + iso7816_apdu_t *apdu; + + if (apdu_p == NULL || (apdu = *apdu_p) == NULL) + return; + + explicit_bzero(apdu, apdu->alloc_len); + free(apdu); + + *apdu_p = NULL; +} + +int +iso7816_add(iso7816_apdu_t *apdu, const void *buf, size_t cnt) +{ + if (cnt > apdu->payload_len || cnt > UINT16_MAX) + return (-1); + + memcpy(apdu->payload_ptr, buf, cnt); + apdu->payload_ptr += cnt; + apdu->payload_len -= (uint16_t)cnt; + + return (0); +} + +const unsigned char * +iso7816_ptr(const iso7816_apdu_t *apdu) +{ + return ((const unsigned char *)&apdu->header); +} + +size_t +iso7816_len(const iso7816_apdu_t *apdu) +{ + return (apdu->alloc_len - sizeof(apdu->alloc_len) - + sizeof(apdu->payload_len) - sizeof(apdu->payload_ptr)); +} diff --git a/src/iso7816.h b/src/iso7816.h new file mode 100644 index 0000000..426cd97 --- /dev/null +++ b/src/iso7816.h @@ -0,0 +1,38 @@ +/* + * 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 _ISO7816_H +#define _ISO7816_H + +#include "packed.h" + +PACKED_TYPE(iso7816_header_t, +struct iso7816_header { + uint8_t cla; + uint8_t ins; + uint8_t p1; + uint8_t p2; + uint8_t lc1; + uint8_t lc2; + uint8_t lc3; +}) + +PACKED_TYPE(iso7816_apdu_t, +struct iso7816_apdu { + size_t alloc_len; + uint16_t payload_len; + uint8_t *payload_ptr; + iso7816_header_t header; + uint8_t payload[]; +}) + +const unsigned char *iso7816_ptr(const iso7816_apdu_t *); +int iso7816_add(iso7816_apdu_t *, const void *, size_t); +iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint16_t); +size_t iso7816_len(const iso7816_apdu_t *); +void iso7816_free(iso7816_apdu_t **); + +#endif /* !_ISO7816_H */ diff --git a/src/libfido2.pc.in b/src/libfido2.pc.in new file mode 100644 index 0000000..03d0606 --- /dev/null +++ b/src/libfido2.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/include + +Name: @PROJECT_NAME@ +Description: A FIDO2 library +URL: https://github.com/yubico/libfido2 +Version: @FIDO_VERSION@ +Requires: libcrypto +Libs: -L${libdir} -lfido2 +Cflags: -I${includedir} diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..982bdb7 --- /dev/null +++ b/src/log.c @@ -0,0 +1,63 @@ +/* + * 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 +#include +#include +#include "fido.h" + +#ifndef FIDO_NO_DIAGNOSTIC + +#ifndef TLS +#define TLS +#endif + +static TLS int logging; + +void +fido_log_init(void) +{ + logging = 1; +} + +void +fido_log_xxd(const void *buf, size_t count) +{ + const uint8_t *ptr = buf; + size_t i; + + if (!logging) + return; + + fprintf(stderr, " "); + + for (i = 0; i < count; i++) { + fprintf(stderr, "%02x ", *ptr++); + if ((i + 1) % 16 == 0 && i + 1 < count) + fprintf(stderr, "\n "); + } + + fprintf(stderr, "\n"); + fflush(stderr); +} + +void +fido_log_debug(const char *fmt, ...) +{ + va_list ap; + + if (!logging) + return; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + fflush(stderr); +} + +#endif /* !FIDO_NO_DIAGNOSTIC */ diff --git a/src/packed.h b/src/packed.h new file mode 100644 index 0000000..3857c22 --- /dev/null +++ b/src/packed.h @@ -0,0 +1,22 @@ +/* + * 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 _PACKED_H +#define _PACKED_H + +#if defined(__GNUC__) +#define PACKED_TYPE(type, def) \ + typedef def __attribute__ ((__packed__)) type; +#elif defined(_MSC_VER) +#define PACKED_TYPE(type, def) \ + __pragma(pack(push, 1)) \ + typedef def type; \ + __pragma(pack(pop)) +#else +#error "please provide a way to define packed types on your platform" +#endif + +#endif /* !_PACKED_H */ diff --git a/src/pin.c b/src/pin.c new file mode 100644 index 0000000..1ed555c --- /dev/null +++ b/src/pin.c @@ -0,0 +1,428 @@ +/* + * 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 +#include "fido.h" +#include "fido/es256.h" + +static int +parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + fido_blob_t *token = arg; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8 || + cbor_get_uint8(key) != 2) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + return (fido_blob_decode(val, token)); +} + +static int +fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin, + const fido_blob_t *ecdh, const es256_pk_t *pk) +{ + fido_blob_t f; + fido_blob_t *p = NULL; + cbor_item_t *argv[6]; + int r; + + memset(&f, 0, sizeof(f)); + memset(argv, 0, sizeof(argv)); + + if ((p = fido_blob_new()) == NULL || fido_blob_set(p, + (const unsigned char *)pin, strlen(pin)) < 0) { + fido_log_debug("%s: fido_blob_set", __func__); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if ((argv[0] = cbor_build_uint8(1)) == NULL || + (argv[1] = cbor_build_uint8(5)) == NULL || + (argv[2] = es256_pk_encode(pk, 0)) == NULL || + (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + fido_blob_free(&p); + free(f.ptr); + + return (r); +} + +static int +fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, + fido_blob_t *token, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + fido_blob_t *aes_token = NULL; + unsigned char reply[2048]; + int reply_len; + int r; + + if ((aes_token = fido_blob_new()) == NULL) { + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + r = FIDO_ERR_RX; + goto fail; + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token, + parse_pintoken)) != FIDO_OK) { + fido_log_debug("%s: parse_pintoken", __func__); + goto fail; + } + + if (aes256_cbc_dec(ecdh, aes_token, token) < 0) { + fido_log_debug("%s: aes256_cbc_dec", __func__); + r = FIDO_ERR_RX; + goto fail; + } + + r = FIDO_OK; +fail: + fido_blob_free(&aes_token); + + return (r); +} + +static int +fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin, + const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms) +{ + int r; + + if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK || + (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_dev_get_pin_token(fido_dev_t *dev, const char *pin, + const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token) +{ + return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1)); +} + +static int +pad64(const char *pin, fido_blob_t **ppin) +{ + size_t pin_len; + size_t ppin_len; + + pin_len = strlen(pin); + if (pin_len < 4 || pin_len > 255) { + fido_log_debug("%s: invalid pin length", __func__); + return (FIDO_ERR_PIN_POLICY_VIOLATION); + } + + if ((*ppin = fido_blob_new()) == NULL) + return (FIDO_ERR_INTERNAL); + + ppin_len = (pin_len + 63) & ~63; + if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) { + fido_blob_free(ppin); + return (FIDO_ERR_INTERNAL); + } + + memcpy((*ppin)->ptr, pin, pin_len); + (*ppin)->len = ppin_len; + + return (FIDO_OK); +} + +static int +fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin) +{ + fido_blob_t f; + fido_blob_t *ppin = NULL; + fido_blob_t *ecdh = NULL; + fido_blob_t *opin = NULL; + cbor_item_t *argv[6]; + es256_pk_t *pk = NULL; + int r; + + memset(&f, 0, sizeof(f)); + memset(argv, 0, sizeof(argv)); + + if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin, + (const unsigned char *)oldpin, strlen(oldpin)) < 0) { + fido_log_debug("%s: fido_blob_set", __func__); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + if ((r = pad64(pin, &ppin)) != FIDO_OK) { + fido_log_debug("%s: pad64", __func__); + goto fail; + } + + if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { + fido_log_debug("%s: fido_do_ecdh", __func__); + goto fail; + } + + if ((argv[0] = cbor_build_uint8(1)) == NULL || + (argv[1] = cbor_build_uint8(4)) == NULL || + (argv[2] = es256_pk_encode(pk, 0)) == NULL || + (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL || + (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL || + (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + es256_pk_free(&pk); + fido_blob_free(&ppin); + fido_blob_free(&ecdh); + fido_blob_free(&opin); + free(f.ptr); + + return (r); + +} + +static int +fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin) +{ + fido_blob_t f; + fido_blob_t *ppin = NULL; + fido_blob_t *ecdh = NULL; + cbor_item_t *argv[5]; + es256_pk_t *pk = NULL; + int r; + + memset(&f, 0, sizeof(f)); + memset(argv, 0, sizeof(argv)); + + if ((r = pad64(pin, &ppin)) != FIDO_OK) { + fido_log_debug("%s: pad64", __func__); + goto fail; + } + + if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { + fido_log_debug("%s: fido_do_ecdh", __func__); + goto fail; + } + + if ((argv[0] = cbor_build_uint8(1)) == NULL || + (argv[1] = cbor_build_uint8(3)) == NULL || + (argv[2] = es256_pk_encode(pk, 0)) == NULL || + (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL || + (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 5, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + es256_pk_free(&pk); + fido_blob_free(&ppin); + fido_blob_free(&ecdh); + free(f.ptr); + + return (r); +} + +static int +fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin, + int ms) +{ + int r; + + if (oldpin != NULL) { + if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) { + fido_log_debug("%s: fido_dev_change_pin_tx", __func__); + return (r); + } + } else { + if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) { + fido_log_debug("%s: fido_dev_set_pin_tx", __func__); + return (r); + } + } + + if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { + fido_log_debug("%s: fido_rx_cbor_status", __func__); + return (r); + } + + return (FIDO_OK); +} + +int +fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin) +{ + return (fido_dev_set_pin_wait(dev, pin, oldpin, -1)); +} + +static int +parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + int *retries = arg; + uint64_t n; + + if (cbor_isa_uint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8 || + cbor_get_uint8(key) != 3) { + fido_log_debug("%s: cbor type", __func__); + return (0); /* ignore */ + } + + if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) { + fido_log_debug("%s: cbor_decode_uint64", __func__); + return (-1); + } + + *retries = (int)n; + + return (0); +} + +static int +fido_dev_get_retry_count_tx(fido_dev_t *dev) +{ + fido_blob_t f; + cbor_item_t *argv[2]; + int r; + + memset(&f, 0, sizeof(f)); + memset(argv, 0, sizeof(argv)); + + if ((argv[0] = cbor_build_uint8(1)) == NULL || + (argv[1] = cbor_build_uint8(1)) == NULL) { + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 || + fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + + r = FIDO_OK; +fail: + cbor_vector_free(argv, nitems(argv)); + free(f.ptr); + + return (r); +} + +static int +fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + unsigned char reply[512]; + int reply_len; + int r; + + *retries = 0; + + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + return (FIDO_ERR_RX); + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries, + parse_retry_count)) != FIDO_OK) { + fido_log_debug("%s: parse_retry_count", __func__); + return (r); + } + + return (FIDO_OK); +} + +static int +fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms) +{ + int r; + + if ((r = fido_dev_get_retry_count_tx(dev)) != FIDO_OK || + (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_dev_get_retry_count(fido_dev_t *dev, int *retries) +{ + return (fido_dev_get_retry_count_wait(dev, retries, -1)); +} + +int +cbor_add_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data, + const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, + cbor_item_t **auth, cbor_item_t **opt) +{ + fido_blob_t *token = NULL; + int r; + + if ((token = fido_blob_new()) == NULL) { + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) { + fido_log_debug("%s: fido_dev_get_pin_token", __func__); + goto fail; + } + + if ((*auth = cbor_encode_pin_auth(token, hmac_data)) == NULL || + (*opt = cbor_encode_pin_opt()) == NULL) { + fido_log_debug("%s: cbor encode", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + r = FIDO_OK; +fail: + fido_blob_free(&token); + + return (r); +} diff --git a/src/reset.c b/src/reset.c new file mode 100644 index 0000000..4b2c88a --- /dev/null +++ b/src/reset.c @@ -0,0 +1,40 @@ +/* + * 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 +#include "fido.h" + +static int +fido_dev_reset_tx(fido_dev_t *dev) +{ + const unsigned char cbor[] = { CTAP_CBOR_RESET }; + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + + if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + return (FIDO_ERR_TX); + } + + return (FIDO_OK); +} + +static int +fido_dev_reset_wait(fido_dev_t *dev, int ms) +{ + int r; + + if ((r = fido_dev_reset_tx(dev)) != FIDO_OK || + (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) + return (r); + + return (FIDO_OK); +} + +int +fido_dev_reset(fido_dev_t *dev) +{ + return (fido_dev_reset_wait(dev, -1)); +} diff --git a/src/rs256.c b/src/rs256.c new file mode 100644 index 0000000..9f30163 --- /dev/null +++ b/src/rs256.c @@ -0,0 +1,204 @@ +/* + * 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 +#include +#include +#include + +#include +#include "fido.h" +#include "fido/rs256.h" + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static int +RSA_bits(const RSA *r) +{ + return (BN_num_bits(r->n)); +} + +static int +RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + r->n = n; + r->e = e; + r->d = d; + + return (1); +} + +static void +RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + *n = r->n; + *e = r->e; + *d = r->d; +} +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + +static int +decode_bignum(const cbor_item_t *item, void *ptr, size_t len) +{ + if (cbor_isa_bytestring(item) == false || + cbor_bytestring_is_definite(item) == false || + cbor_bytestring_length(item) != len) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + memcpy(ptr, cbor_bytestring_handle(item), len); + + return (0); +} + +static int +decode_rsa_pubkey(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + rs256_pk_t *k = arg; + + if (cbor_isa_negint(key) == false || + cbor_int_get_width(key) != CBOR_INT_8) + return (0); /* ignore */ + + switch (cbor_get_uint8(key)) { + case 0: /* modulus */ + return (decode_bignum(val, &k->n, sizeof(k->n))); + case 1: /* public exponent */ + return (decode_bignum(val, &k->e, sizeof(k->e))); + } + + return (0); /* ignore */ +} + +int +rs256_pk_decode(const cbor_item_t *item, rs256_pk_t *k) +{ + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, k, decode_rsa_pubkey) < 0) { + fido_log_debug("%s: cbor type", __func__); + return (-1); + } + + return (0); +} + +rs256_pk_t * +rs256_pk_new(void) +{ + return (calloc(1, sizeof(rs256_pk_t))); +} + +void +rs256_pk_free(rs256_pk_t **pkp) +{ + rs256_pk_t *pk; + + if (pkp == NULL || (pk = *pkp) == NULL) + return; + + explicit_bzero(pk, sizeof(*pk)); + free(pk); + + *pkp = NULL; +} + +int +rs256_pk_from_ptr(rs256_pk_t *pk, const void *ptr, size_t len) +{ + if (len < sizeof(*pk)) + return (FIDO_ERR_INVALID_ARGUMENT); + + memcpy(pk, ptr, sizeof(*pk)); + + return (FIDO_OK); +} + +EVP_PKEY * +rs256_pk_to_EVP_PKEY(const rs256_pk_t *k) +{ + RSA *rsa = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *n = NULL; + BIGNUM *e = NULL; + int ok = -1; + + if ((n = BN_new()) == NULL || (e = BN_new()) == NULL) + goto fail; + + if (BN_bin2bn(k->n, sizeof(k->n), n) == NULL || + BN_bin2bn(k->e, sizeof(k->e), e) == NULL) { + fido_log_debug("%s: BN_bin2bn", __func__); + goto fail; + } + + if ((rsa = RSA_new()) == NULL || RSA_set0_key(rsa, n, e, NULL) == 0) { + fido_log_debug("%s: RSA_set0_key", __func__); + goto fail; + } + + /* at this point, n and e belong to rsa */ + n = NULL; + e = NULL; + + if ((pkey = EVP_PKEY_new()) == NULL || + EVP_PKEY_assign_RSA(pkey, rsa) == 0) { + fido_log_debug("%s: EVP_PKEY_assign_RSA", __func__); + goto fail; + } + + rsa = NULL; /* at this point, rsa belongs to evp */ + + ok = 0; +fail: + if (n != NULL) + BN_free(n); + if (e != NULL) + BN_free(e); + if (rsa != NULL) + RSA_free(rsa); + if (ok < 0 && pkey != NULL) { + EVP_PKEY_free(pkey); + pkey = NULL; + } + + return (pkey); +} + +int +rs256_pk_from_RSA(rs256_pk_t *pk, const RSA *rsa) +{ + const BIGNUM *n = NULL; + const BIGNUM *e = NULL; + const BIGNUM *d = NULL; + int k; + + if (RSA_bits(rsa) != 2048) { + fido_log_debug("%s: invalid key length", __func__); + return (FIDO_ERR_INVALID_ARGUMENT); + } + + RSA_get0_key(rsa, &n, &e, &d); + + if (n == NULL || e == NULL) { + fido_log_debug("%s: RSA_get0_key", __func__); + return (FIDO_ERR_INTERNAL); + } + + if ((k = BN_num_bytes(n)) < 0 || (size_t)k > sizeof(pk->n) || + (k = BN_num_bytes(e)) < 0 || (size_t)k > sizeof(pk->e)) { + fido_log_debug("%s: invalid key", __func__); + return (FIDO_ERR_INTERNAL); + } + + if ((k = BN_bn2bin(n, pk->n)) < 0 || (size_t)k > sizeof(pk->n) || + (k = BN_bn2bin(e, pk->e)) < 0 || (size_t)k > sizeof(pk->e)) { + fido_log_debug("%s: BN_bn2bin", __func__); + return (FIDO_ERR_INTERNAL); + } + + return (FIDO_OK); +} diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..42ed1b7 --- /dev/null +++ b/src/types.h @@ -0,0 +1,171 @@ +/* + * 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 _TYPES_H +#define _TYPES_H + +#include "packed.h" + +/* COSE ES256 (ECDSA over P-256 with SHA-256) public key */ +typedef struct es256_pk { + unsigned char x[32]; + unsigned char y[32]; +} es256_pk_t; + +/* COSE ES256 (ECDSA over P-256 with SHA-256) (secret) key */ +typedef struct es256_sk { + unsigned char d[32]; +} es256_sk_t; + +/* COSE RS256 (2048-bit RSA with PKCS1 padding and SHA-256) public key */ +typedef struct rs256_pk { + unsigned char n[256]; + unsigned char e[3]; +} rs256_pk_t; + +/* COSE EDDSA (ED25519) */ +typedef struct eddsa_pk { + unsigned char x[32]; +} eddsa_pk_t; + +PACKED_TYPE(fido_authdata_t, +struct fido_authdata { + unsigned char rp_id_hash[32]; /* sha256 of fido_rp.id */ + uint8_t flags; /* user present/verified */ + uint32_t sigcount; /* signature counter */ + /* actually longer */ +}) + +PACKED_TYPE(fido_attcred_raw_t, +struct fido_attcred_raw { + unsigned char aaguid[16]; /* credential's aaguid */ + uint16_t id_len; /* credential id length */ + uint8_t body[]; /* credential id + pubkey */ +}) + +typedef struct fido_attcred { + unsigned char aaguid[16]; /* credential's aaguid */ + fido_blob_t id; /* credential id */ + int type; /* credential's cose algorithm */ + union { /* credential's public key */ + es256_pk_t es256; + rs256_pk_t rs256; + eddsa_pk_t eddsa; + } pubkey; +} fido_attcred_t; + +typedef struct fido_attstmt { + fido_blob_t x5c; /* attestation certificate */ + fido_blob_t sig; /* attestation signature */ +} fido_attstmt_t; + +typedef struct fido_rp { + char *id; /* relying party id */ + char *name; /* relying party name */ +} fido_rp_t; + +typedef struct fido_user { + fido_blob_t id; /* required */ + char *icon; /* optional */ + char *name; /* optional */ + char *display_name; /* required */ +} fido_user_t; + +typedef struct fido_cred { + fido_blob_t cdh; /* client data hash */ + fido_rp_t rp; /* relying party */ + fido_user_t user; /* user entity */ + fido_blob_array_t excl; /* list of credential ids to exclude */ + fido_opt_t rk; /* resident key */ + fido_opt_t uv; /* user verification */ + int ext; /* enabled extensions */ + int type; /* cose algorithm */ + char *fmt; /* credential format */ + int authdata_ext; /* decoded extensions */ + fido_blob_t authdata_cbor; /* raw cbor payload */ + fido_authdata_t authdata; /* decoded authdata payload */ + fido_attcred_t attcred; /* returned credential (key + id) */ + fido_attstmt_t attstmt; /* attestation statement (x509 + sig) */ +} fido_cred_t; + +typedef struct _fido_assert_stmt { + fido_blob_t id; /* credential id */ + fido_user_t user; /* user attributes */ + fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */ + fido_blob_t hmac_secret; /* hmac secret */ + int authdata_ext; /* decoded extensions */ + fido_blob_t authdata_cbor; /* raw cbor payload */ + fido_authdata_t authdata; /* decoded authdata payload */ + fido_blob_t sig; /* signature of cdh + authdata */ +} fido_assert_stmt; + +typedef struct fido_assert { + char *rp_id; /* relying party id */ + fido_blob_t cdh; /* client data hash */ + fido_blob_t hmac_salt; /* optional hmac-secret salt */ + fido_blob_array_t allow_list; /* list of allowed credentials */ + fido_opt_t up; /* user presence */ + fido_opt_t uv; /* user verification */ + int ext; /* enabled extensions */ + fido_assert_stmt *stmt; /* array of expected assertions */ + size_t stmt_cnt; /* number of allocated assertions */ + size_t stmt_len; /* number of received assertions */ +} fido_assert_t; + +typedef struct fido_opt_array { + char **name; + bool *value; + size_t len; +} fido_opt_array_t; + +typedef struct fido_str_array { + char **ptr; + size_t len; +} fido_str_array_t; + +typedef struct fido_byte_array { + uint8_t *ptr; + size_t len; +} fido_byte_array_t; + +typedef struct fido_cbor_info { + fido_str_array_t versions; /* supported versions: fido2|u2f */ + fido_str_array_t extensions; /* list of supported extensions */ + unsigned char aaguid[16]; /* aaguid */ + fido_opt_array_t options; /* list of supported options */ + uint64_t maxmsgsiz; /* maximum message size */ + fido_byte_array_t protocols; /* supported pin protocols */ +} fido_cbor_info_t; + +typedef struct fido_dev_info { + char *path; /* device path */ + int16_t vendor_id; /* 2-byte vendor id */ + int16_t product_id; /* 2-byte product id */ + char *manufacturer; /* manufacturer string */ + char *product; /* product string */ +} fido_dev_info_t; + +PACKED_TYPE(fido_ctap_info_t, +/* defined in section 8.1.9.1.3 (CTAPHID_INIT) of the fido2 ctap spec */ +struct fido_ctap_info { + uint64_t nonce; /* echoed nonce */ + uint32_t cid; /* channel id */ + uint8_t protocol; /* ctaphid protocol id */ + uint8_t major; /* major version number */ + uint8_t minor; /* minor version number */ + uint8_t build; /* build version number */ + uint8_t flags; /* capabilities flags; see FIDO_CAP_* */ +}) + +typedef struct fido_dev { + uint64_t nonce; /* issued nonce */ + fido_ctap_info_t attr; /* device attributes */ + uint32_t cid; /* assigned channel id */ + void *io_handle; /* abstract i/o handle */ + fido_dev_io_t io; /* i/o functions & data */ +} fido_dev_t; + +#endif /* !_TYPES_H */ diff --git a/src/u2f.c b/src/u2f.c new file mode 100644 index 0000000..3f2d9aa --- /dev/null +++ b/src/u2f.c @@ -0,0 +1,758 @@ +/* + * 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 +#include + +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "fido.h" +#include "fido/es256.h" + +#if defined(_MSC_VER) +static int +usleep(unsigned int usec) +{ + Sleep(usec / 1000); + + return (0); +} +#endif + +static int +sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len) +{ + sig->len = *len; /* consume the whole buffer */ + if ((sig->ptr = calloc(1, sig->len)) == NULL || + fido_buf_read(buf, len, sig->ptr, sig->len) < 0) { + fido_log_debug("%s: fido_buf_read", __func__); + if (sig->ptr != NULL) { + explicit_bzero(sig->ptr, sig->len); + free(sig->ptr); + sig->ptr = NULL; + sig->len = 0; + return (-1); + } + } + + return (0); +} + +static int +x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len) +{ + X509 *cert = NULL; + int ok = -1; + + if (*len > LONG_MAX) { + fido_log_debug("%s: invalid len %zu", __func__, *len); + goto fail; + } + + /* find out the certificate's length */ + const unsigned char *end = *buf; + if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf || + (x5c->len = (size_t)(end - *buf)) >= *len) { + fido_log_debug("%s: d2i_X509", __func__); + goto fail; + } + + /* read accordingly */ + if ((x5c->ptr = calloc(1, x5c->len)) == NULL || + fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) { + fido_log_debug("%s: fido_buf_read", __func__); + goto fail; + } + + ok = 0; +fail: + if (cert != NULL) + X509_free(cert); + + if (ok < 0) { + free(x5c->ptr); + x5c->ptr = NULL; + x5c->len = 0; + } + + return (ok); +} + +static int +authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount, + fido_blob_t *fake_cbor_ad) +{ + fido_authdata_t ad; + cbor_item_t *item = NULL; + size_t alloc_len; + + memset(&ad, 0, sizeof(ad)); + + if (SHA256((const void *)rp_id, strlen(rp_id), + ad.rp_id_hash) != ad.rp_id_hash) { + fido_log_debug("%s: sha256", __func__); + return (-1); + } + + ad.flags = flags; /* XXX translate? */ + ad.sigcount = sigcount; + + if ((item = cbor_build_bytestring((const unsigned char *)&ad, + sizeof(ad))) == NULL) { + fido_log_debug("%s: cbor_build_bytestring", __func__); + return (-1); + } + + if (fake_cbor_ad->ptr != NULL || + (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr, + &alloc_len)) == 0) { + fido_log_debug("%s: cbor_serialize_alloc", __func__); + cbor_decref(&item); + return (-1); + } + + cbor_decref(&item); + + return (0); +} + +static int +send_dummy_register(fido_dev_t *dev, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG; + iso7816_apdu_t *apdu = NULL; + unsigned char challenge[SHA256_DIGEST_LENGTH]; + unsigned char application[SHA256_DIGEST_LENGTH]; + unsigned char reply[2048]; + int r; + +#ifdef FIDO_FUZZ + ms = 0; /* XXX */ +#endif + + /* dummy challenge & application */ + memset(&challenge, 0xff, sizeof(challenge)); + memset(&application, 0xff, sizeof(application)); + + if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 * + SHA256_DIGEST_LENGTH)) == NULL || + iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || + iso7816_add(apdu, &application, sizeof(application)) < 0) { + fido_log_debug("%s: iso7816", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + do { + if (fido_tx(dev, cmd, iso7816_ptr(apdu), + iso7816_len(apdu)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + if (fido_rx(dev, cmd, &reply, sizeof(reply), ms) < 2) { + fido_log_debug("%s: fido_rx", __func__); + r = FIDO_ERR_RX; + goto fail; + } + if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) { + fido_log_debug("%s: usleep", __func__); + r = FIDO_ERR_RX; + goto fail; + } + } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); + + r = FIDO_OK; +fail: + iso7816_free(&apdu); + + return (r); +} + +static int +key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id, + int *found, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG; + iso7816_apdu_t *apdu = NULL; + unsigned char challenge[SHA256_DIGEST_LENGTH]; + unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; + unsigned char reply[8]; + uint8_t key_id_len; + int r; + + if (key_id->len > UINT8_MAX || rp_id == NULL) { + fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__, + key_id->len, (const void *)rp_id); + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + memset(&challenge, 0xff, sizeof(challenge)); + memset(&rp_id_hash, 0, sizeof(rp_id_hash)); + + if (SHA256((const void *)rp_id, strlen(rp_id), + rp_id_hash) != rp_id_hash) { + fido_log_debug("%s: sha256", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + key_id_len = (uint8_t)key_id->len; + + if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_CHECK, 2 * + SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len)) == NULL || + iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || + iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || + iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 || + iso7816_add(apdu, key_id->ptr, key_id_len) < 0) { + fido_log_debug("%s: iso7816", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if (fido_tx(dev, cmd, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + if (fido_rx(dev, cmd, &reply, sizeof(reply), ms) != 2) { + fido_log_debug("%s: fido_rx", __func__); + r = FIDO_ERR_RX; + goto fail; + } + + switch ((reply[0] << 8) | reply[1]) { + case SW_CONDITIONS_NOT_SATISFIED: + *found = 1; /* key exists */ + break; + case SW_WRONG_DATA: + *found = 0; /* key does not exist */ + break; + default: + /* unexpected sw */ + r = FIDO_ERR_INTERNAL; + goto fail; + } + + r = FIDO_OK; +fail: + iso7816_free(&apdu); + + return (r); +} + +static int +parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id, + const unsigned char *reply, size_t len) +{ + uint8_t flags; + uint32_t sigcount; + + if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) { + fido_log_debug("%s: unexpected sw", __func__); + return (FIDO_ERR_RX); + } + + len -= 2; + + if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 || + fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) { + fido_log_debug("%s: fido_buf_read", __func__); + return (FIDO_ERR_RX); + } + + if (sig_get(sig, &reply, &len) < 0) { + fido_log_debug("%s: sig_get", __func__); + return (FIDO_ERR_RX); + } + + if (authdata_fake(rp_id, flags, sigcount, ad) < 0) { + fido_log_debug("%s; authdata_fake", __func__); + return (FIDO_ERR_RX); + } + + return (FIDO_OK); +} + +static int +do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id, + const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG; + iso7816_apdu_t *apdu = NULL; + unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; + unsigned char reply[128]; + int reply_len; + uint8_t key_id_len; + int r; + +#ifdef FIDO_FUZZ + ms = 0; /* XXX */ +#endif + + if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX || + rp_id == NULL) { + r = FIDO_ERR_INVALID_ARGUMENT; + goto fail; + } + + memset(&rp_id_hash, 0, sizeof(rp_id_hash)); + + if (SHA256((const void *)rp_id, strlen(rp_id), + rp_id_hash) != rp_id_hash) { + fido_log_debug("%s: sha256", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + key_id_len = (uint8_t)key_id->len; + + if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_SIGN, 2 * + SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len)) == NULL || + iso7816_add(apdu, cdh->ptr, cdh->len) < 0 || + iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || + iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 || + iso7816_add(apdu, key_id->ptr, key_id_len) < 0) { + fido_log_debug("%s: iso7816", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + do { + if (fido_tx(dev, cmd, iso7816_ptr(apdu), + iso7816_len(apdu)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), + ms)) < 2) { + fido_log_debug("%s: fido_rx", __func__); + r = FIDO_ERR_RX; + goto fail; + } + if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) { + fido_log_debug("%s: usleep", __func__); + r = FIDO_ERR_RX; + goto fail; + } + } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); + + if ((r = parse_auth_reply(sig, ad, rp_id, reply, + (size_t)reply_len)) != FIDO_OK) { + fido_log_debug("%s: parse_auth_reply", __func__); + goto fail; + } + +fail: + iso7816_free(&apdu); + + return (r); +} + +static int +cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len, + fido_blob_t *cbor_blob) +{ + es256_pk_t *pk = NULL; + cbor_item_t *pk_cbor = NULL; + size_t alloc_len; + int ok = -1; + + /* only handle uncompressed points */ + if (ec_point_len != 65 || ec_point[0] != 0x04) { + fido_log_debug("%s: unexpected format", __func__); + goto fail; + } + + if ((pk = es256_pk_new()) == NULL || + es256_pk_set_x(pk, &ec_point[1]) < 0 || + es256_pk_set_y(pk, &ec_point[33]) < 0) { + fido_log_debug("%s: es256_pk_set", __func__); + goto fail; + } + + if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) { + fido_log_debug("%s: es256_pk_encode", __func__); + goto fail; + } + + if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr, + &alloc_len)) != 77) { + fido_log_debug("%s: cbor_serialize_alloc", __func__); + goto fail; + } + + ok = 0; +fail: + es256_pk_free(&pk); + + if (pk_cbor) + cbor_decref(&pk_cbor); + + return (ok); +} + +static int +encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len, + const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out) +{ + fido_authdata_t authdata; + fido_attcred_raw_t attcred_raw; + fido_blob_t pk_blob; + fido_blob_t authdata_blob; + cbor_item_t *authdata_cbor = NULL; + unsigned char *ptr; + size_t len; + size_t alloc_len; + int ok = -1; + + memset(&pk_blob, 0, sizeof(pk_blob)); + memset(&authdata, 0, sizeof(authdata)); + memset(&authdata_blob, 0, sizeof(authdata_blob)); + memset(out, 0, sizeof(*out)); + + if (rp_id == NULL) { + fido_log_debug("%s: NULL rp_id", __func__); + goto fail; + } + + if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) { + fido_log_debug("%s: cbor_blob_from_ec_point", __func__); + goto fail; + } + + if (SHA256((const void *)rp_id, strlen(rp_id), + authdata.rp_id_hash) != authdata.rp_id_hash) { + fido_log_debug("%s: sha256", __func__); + goto fail; + } + + authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT); + authdata.sigcount = 0; + + memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid)); + attcred_raw.id_len = (uint16_t)(kh_len << 8); /* XXX */ + + len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) + + kh_len + pk_blob.len; + ptr = authdata_blob.ptr = calloc(1, authdata_blob.len); + + fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len); + + if (authdata_blob.ptr == NULL) + goto fail; + + if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 || + fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 || + fido_buf_write(&ptr, &len, kh, kh_len) < 0 || + fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) { + fido_log_debug("%s: fido_buf_write", __func__); + goto fail; + } + + if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) { + fido_log_debug("%s: fido_blob_encode", __func__); + goto fail; + } + + if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr, + &alloc_len)) == 0) { + fido_log_debug("%s: cbor_serialize_alloc", __func__); + goto fail; + } + + ok = 0; +fail: + if (authdata_cbor) + cbor_decref(&authdata_cbor); + + if (pk_blob.ptr) { + explicit_bzero(pk_blob.ptr, pk_blob.len); + free(pk_blob.ptr); + } + if (authdata_blob.ptr) { + explicit_bzero(authdata_blob.ptr, authdata_blob.len); + free(authdata_blob.ptr); + } + + return (ok); +} + +static int +parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len) +{ + fido_blob_t x5c; + fido_blob_t sig; + fido_blob_t ad; + uint8_t dummy; + uint8_t pubkey[65]; + uint8_t kh_len = 0; + uint8_t *kh = NULL; + int r; + + memset(&x5c, 0, sizeof(x5c)); + memset(&sig, 0, sizeof(sig)); + memset(&ad, 0, sizeof(ad)); + r = FIDO_ERR_RX; + + /* status word */ + if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) { + fido_log_debug("%s: unexpected sw", __func__); + goto fail; + } + + len -= 2; + + /* reserved byte */ + if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 || + dummy != 0x05) { + fido_log_debug("%s: reserved byte", __func__); + goto fail; + } + + /* pubkey + key handle */ + if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 || + fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 || + (kh = calloc(1, kh_len)) == NULL || + fido_buf_read(&reply, &len, kh, kh_len) < 0) { + fido_log_debug("%s: fido_buf_read", __func__); + goto fail; + } + + /* x5c + sig */ + if (x5c_get(&x5c, &reply, &len) < 0 || + sig_get(&sig, &reply, &len) < 0) { + fido_log_debug("%s: x5c || sig", __func__); + goto fail; + } + + /* authdata */ + if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey, + sizeof(pubkey), &ad) < 0) { + fido_log_debug("%s: encode_cred_authdata", __func__); + goto fail; + } + + if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK || + fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK || + fido_cred_set_x509(cred, x5c.ptr, x5c.len) != FIDO_OK || + fido_cred_set_sig(cred, sig.ptr, sig.len) != FIDO_OK) { + fido_log_debug("%s: fido_cred_set", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + r = FIDO_OK; +fail: + if (kh) { + explicit_bzero(kh, kh_len); + free(kh); + } + if (x5c.ptr) { + explicit_bzero(x5c.ptr, x5c.len); + free(x5c.ptr); + } + if (sig.ptr) { + explicit_bzero(sig.ptr, sig.len); + free(sig.ptr); + } + if (ad.ptr) { + explicit_bzero(ad.ptr, ad.len); + free(ad.ptr); + } + + return (r); +} + +int +u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms) +{ + const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG; + iso7816_apdu_t *apdu = NULL; + unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; + unsigned char reply[2048]; + int reply_len; + int found; + int r; + +#ifdef FIDO_FUZZ + ms = 0; /* XXX */ +#endif + + if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) { + fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk, + cred->uv); + return (FIDO_ERR_UNSUPPORTED_OPTION); + } + + if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL || + cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) { + fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__, + cred->type, (void *)cred->cdh.ptr, cred->cdh.len); + return (FIDO_ERR_INVALID_ARGUMENT); + } + + for (size_t i = 0; i < cred->excl.len; i++) { + if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i], + &found, ms)) != FIDO_OK) { + fido_log_debug("%s: key_lookup", __func__); + return (r); + } + if (found) { + if ((r = send_dummy_register(dev, ms)) != FIDO_OK) { + fido_log_debug("%s: send_dummy_register", + __func__); + return (r); + } + return (FIDO_ERR_CREDENTIAL_EXCLUDED); + } + } + + memset(&rp_id_hash, 0, sizeof(rp_id_hash)); + + if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id), + rp_id_hash) != rp_id_hash) { + fido_log_debug("%s: sha256", __func__); + return (FIDO_ERR_INTERNAL); + } + + if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 * + SHA256_DIGEST_LENGTH)) == NULL || + iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 || + iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) { + fido_log_debug("%s: iso7816", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + do { + if (fido_tx(dev, cmd, iso7816_ptr(apdu), + iso7816_len(apdu)) < 0) { + fido_log_debug("%s: fido_tx", __func__); + r = FIDO_ERR_TX; + goto fail; + } + if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), + ms)) < 2) { + fido_log_debug("%s: fido_rx", __func__); + r = FIDO_ERR_RX; + goto fail; + } + if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) { + fido_log_debug("%s: usleep", __func__); + r = FIDO_ERR_RX; + goto fail; + } + } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); + + if ((r = parse_register_reply(cred, reply, + (size_t)reply_len)) != FIDO_OK) { + fido_log_debug("%s: parse_register_reply", __func__); + goto fail; + } +fail: + iso7816_free(&apdu); + + return (r); +} + +static int +u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id, + fido_assert_t *fa, size_t idx, int ms) +{ + fido_blob_t sig; + fido_blob_t ad; + int found; + int r; + + memset(&sig, 0, sizeof(sig)); + memset(&ad, 0, sizeof(ad)); + + if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) { + fido_log_debug("%s: key_lookup", __func__); + goto fail; + } + + if (!found) { + fido_log_debug("%s: not found", __func__); + r = FIDO_ERR_CREDENTIAL_EXCLUDED; + goto fail; + } + + if (fa->up == FIDO_OPT_FALSE) { + fido_log_debug("%s: checking for key existence only", __func__); + r = FIDO_ERR_USER_PRESENCE_REQUIRED; + goto fail; + } + + if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad, + ms)) != FIDO_OK) { + fido_log_debug("%s: do_auth", __func__); + goto fail; + } + + if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0 || + fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK || + fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) { + fido_log_debug("%s: fido_assert_set", __func__); + r = FIDO_ERR_INTERNAL; + goto fail; + } + + r = FIDO_OK; +fail: + if (sig.ptr) { + explicit_bzero(sig.ptr, sig.len); + free(sig.ptr); + } + if (ad.ptr) { + explicit_bzero(ad.ptr, ad.len); + free(ad.ptr); + } + + return (r); +} + +int +u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms) +{ + int nauth_ok = 0; + int r; + + if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) { + fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv, + (void *)fa->allow_list.ptr); + return (FIDO_ERR_UNSUPPORTED_OPTION); + } + + if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) { + fido_log_debug("%s: fido_assert_set_count", __func__); + return (r); + } + + for (size_t i = 0; i < fa->allow_list.len; i++) { + if ((r = u2f_authenticate_single(dev, &fa->allow_list.ptr[i], + fa, nauth_ok, ms)) == FIDO_OK) { + nauth_ok++; + } else if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) { + fido_log_debug("%s: u2f_authenticate_single", __func__); + return (r); + } + /* ignore credentials that don't exist */ + } + + fa->stmt_len = nauth_ok; + + if (nauth_ok == 0) + return (FIDO_ERR_NO_CREDENTIALS); + + return (FIDO_OK); +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..5f27e88 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,65 @@ +# 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/explicit_bzero.c + ../openbsd-compat/strlcpy.c + ../openbsd-compat/strlcat.c +) + +if(WIN32) + list(APPEND COMPAT_SOURCES + ../openbsd-compat/bsd-getline.c + ../openbsd-compat/explicit_bzero_win32.c + ../openbsd-compat/getopt_long.c + ../openbsd-compat/posix_win.c + ../openbsd-compat/readpassphrase_win32.c + ) +else() + list(APPEND COMPAT_SOURCES ../openbsd-compat/readpassphrase.c) +endif() + +add_executable(fido2-cred + fido2-cred.c + cred_make.c + cred_verify.c + base64.c + util.c + ${COMPAT_SOURCES} +) + +add_executable(fido2-assert + fido2-assert.c + assert_get.c + assert_verify.c + base64.c + util.c + ${COMPAT_SOURCES} +) + +add_executable(fido2-token + fido2-token.c + base64.c + bio.c + credman.c + pin.c + token.c + util.c + ${COMPAT_SOURCES} +) + +add_library(sk-libfido2 MODULE sk-libfido2.c) +set_target_properties(sk-libfido2 PROPERTIES + COMPILE_FLAGS "-DSK_STANDALONE -DWITH_OPENSSL" + OUTPUT_NAME sk-libfido2 +) + +target_link_libraries(fido2-cred ${CRYPTO_LIBRARIES} fido2_shared) +target_link_libraries(fido2-assert ${CRYPTO_LIBRARIES} fido2_shared) +target_link_libraries(fido2-token ${CRYPTO_LIBRARIES} fido2_shared) +target_link_libraries(sk-libfido2 ${CRYPTO_LIBRARIES} fido2_shared) + +install(TARGETS fido2-cred fido2-assert fido2-token + DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS sk-libfido2 DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/tools/assert_get.c b/tools/assert_get.c new file mode 100644 index 0000000..5e209cd --- /dev/null +++ b/tools/assert_get.c @@ -0,0 +1,233 @@ +/* + * 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 +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +static fido_assert_t * +prepare_assert(FILE *in_f, int flags) +{ + fido_assert_t *assert = NULL; + struct blob cdh; + struct blob id; + struct blob hmac_salt; + char *rpid = NULL; + int r; + + memset(&cdh, 0, sizeof(cdh)); + memset(&id, 0, sizeof(id)); + memset(&hmac_salt, 0, sizeof(hmac_salt)); + + r = base64_read(in_f, &cdh); + r |= string_read(in_f, &rpid); + if ((flags & FLAG_RK) == 0) + r |= base64_read(in_f, &id); + if (flags & FLAG_HMAC) + r |= base64_read(in_f, &hmac_salt); + if (r < 0) + errx(1, "input error"); + + if (flags & FLAG_DEBUG) { + fprintf(stderr, "client data hash:\n"); + xxd(cdh.ptr, cdh.len); + fprintf(stderr, "relying party id: %s\n", rpid); + if ((flags & FLAG_RK) == 0) { + fprintf(stderr, "credential id:\n"); + xxd(id.ptr, id.len); + } + } + + if ((assert = fido_assert_new()) == NULL) + errx(1, "fido_assert_new"); + + if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr, + cdh.len)) != FIDO_OK || + (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK) + errx(1, "fido_assert_set: %s", fido_strerr(r)); + + if (flags & FLAG_UP) { + if ((r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK) + errx(1, "fido_assert_set_up: %s", fido_strerr(r)); + } + if (flags & FLAG_UV) { + if ((r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) + errx(1, "fido_assert_set_uv: %s", fido_strerr(r)); + } + if (flags & FLAG_HMAC) { + if ((r = fido_assert_set_extensions(assert, + FIDO_EXT_HMAC_SECRET)) != FIDO_OK) + errx(1, "fido_assert_set_extensions: %s", + fido_strerr(r)); + if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr, + hmac_salt.len)) != FIDO_OK) + errx(1, "fido_assert_set_hmac_salt: %s", + fido_strerr(r)); + } + if ((flags & FLAG_RK) == 0) { + if ((r = fido_assert_allow_cred(assert, id.ptr, + id.len)) != FIDO_OK) + errx(1, "fido_assert_allow_cred: %s", fido_strerr(r)); + } + + free(hmac_salt.ptr); + free(cdh.ptr); + free(id.ptr); + free(rpid); + + return (assert); +} + +static void +print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags) +{ + char *cdh = NULL; + char *authdata = NULL; + char *sig = NULL; + char *user_id = NULL; + char *hmac_secret = NULL; + int r; + + r = base64_encode(fido_assert_clientdata_hash_ptr(assert), + fido_assert_clientdata_hash_len(assert), &cdh); + r |= base64_encode(fido_assert_authdata_ptr(assert, idx), + fido_assert_authdata_len(assert, 0), &authdata); + r |= base64_encode(fido_assert_sig_ptr(assert, idx), + fido_assert_sig_len(assert, idx), &sig); + if (flags & FLAG_RK) + r |= base64_encode(fido_assert_user_id_ptr(assert, idx), + fido_assert_user_id_len(assert, idx), &user_id); + if (flags & FLAG_HMAC) + r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx), + fido_assert_hmac_secret_len(assert, idx), &hmac_secret); + if (r < 0) + errx(1, "output error"); + + fprintf(out_f, "%s\n", cdh); + fprintf(out_f, "%s\n", fido_assert_rp_id(assert)); + fprintf(out_f, "%s\n", authdata); + fprintf(out_f, "%s\n", sig); + if (flags & FLAG_RK) + fprintf(out_f, "%s\n", user_id); + if (hmac_secret) { + fprintf(out_f, "%s\n", hmac_secret); + explicit_bzero(hmac_secret, strlen(hmac_secret)); + } + + free(hmac_secret); + free(cdh); + free(authdata); + free(sig); + free(user_id); +} + +int +assert_get(int argc, char **argv) +{ + fido_dev_t *dev = NULL; + fido_assert_t *assert = NULL; + char pin[1024]; + char prompt[1024]; + char *in_path = NULL; + char *out_path = NULL; + FILE *in_f = NULL; + FILE *out_f = NULL; + int flags = 0; + int ch; + int r; + + while ((ch = getopt(argc, argv, "dhi:o:pruv")) != -1) { + switch (ch) { + case 'd': + flags |= FLAG_DEBUG; + break; + case 'h': + flags |= FLAG_HMAC; + break; + case 'i': + in_path = optarg; + break; + case 'o': + out_path = optarg; + break; + case 'p': + flags |= FLAG_UP; + break; + case 'r': + flags |= FLAG_RK; + break; + case 'u': + flags |= FLAG_U2F; + break; + case 'v': + flags |= FLAG_UV; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + in_f = open_read(in_path); + out_f = open_write(out_path); + + fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); + + assert = prepare_assert(in_f, flags); + + dev = open_dev(argv[0]); + if (flags & FLAG_U2F) + fido_dev_force_u2f(dev); + + if (flags & FLAG_UV) { + r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", + argv[0]); + if (r < 0 || (size_t)r >= sizeof(prompt)) + errx(1, "snprintf"); + if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) + errx(1, "readpassphrase"); + r = fido_dev_get_assert(dev, assert, pin); + } else + r = fido_dev_get_assert(dev, assert, NULL); + + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_dev_get_assert: %s", fido_strerr(r)); + + if (flags & FLAG_RK) { + for (size_t idx = 0; idx < fido_assert_count(assert); idx++) + print_assert(out_f, assert, idx, flags); + } else { + if (fido_assert_count(assert) != 1) + errx(1, "fido_assert_count: %zu", + fido_assert_count(assert)); + print_assert(out_f, assert, 0, flags); + } + + fido_dev_close(dev); + fido_dev_free(&dev); + fido_assert_free(&assert); + + fclose(in_f); + fclose(out_f); + in_f = NULL; + out_f = NULL; + + exit(0); +} diff --git a/tools/assert_verify.c b/tools/assert_verify.c new file mode 100644 index 0000000..ccff57a --- /dev/null +++ b/tools/assert_verify.c @@ -0,0 +1,201 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +static fido_assert_t * +prepare_assert(FILE *in_f, int flags) +{ + fido_assert_t *assert = NULL; + struct blob cdh; + struct blob authdata; + struct blob sig; + char *rpid = NULL; + int r; + + memset(&cdh, 0, sizeof(cdh)); + memset(&authdata, 0, sizeof(authdata)); + memset(&sig, 0, sizeof(sig)); + + r = base64_read(in_f, &cdh); + r |= string_read(in_f, &rpid); + r |= base64_read(in_f, &authdata); + r |= base64_read(in_f, &sig); + if (r < 0) + errx(1, "input error"); + + if (flags & FLAG_DEBUG) { + fprintf(stderr, "client data hash:\n"); + xxd(cdh.ptr, cdh.len); + fprintf(stderr, "relying party id: %s\n", rpid); + fprintf(stderr, "authenticator data:\n"); + xxd(authdata.ptr, authdata.len); + fprintf(stderr, "signature:\n"); + xxd(sig.ptr, sig.len); + } + + if ((assert = fido_assert_new()) == NULL) + errx(1, "fido_assert_new"); + if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK) + errx(1, "fido_assert_count: %s", fido_strerr(r)); + + if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr, + cdh.len)) != FIDO_OK || + (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK || + (r = fido_assert_set_authdata(assert, 0, authdata.ptr, + authdata.len)) != FIDO_OK || + (r = fido_assert_set_sig(assert, 0, sig.ptr, sig.len)) != FIDO_OK) + errx(1, "fido_assert_set: %s", fido_strerr(r)); + + if (flags & FLAG_UP) { + if ((r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK) + errx(1, "fido_assert_set_up: %s", fido_strerr(r)); + } + if (flags & FLAG_UV) { + if ((r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) + errx(1, "fido_assert_set_uv: %s", fido_strerr(r)); + } + if (flags & FLAG_HMAC) { + if ((r = fido_assert_set_extensions(assert, + FIDO_EXT_HMAC_SECRET)) != FIDO_OK) + errx(1, "fido_assert_set_extensions: %s", + fido_strerr(r)); + } + + free(cdh.ptr); + free(authdata.ptr); + free(sig.ptr); + free(rpid); + + return (assert); +} + +static void * +load_pubkey(int type, const char *file) +{ + 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 = NULL; + + if (type == COSE_ES256) { + if ((ec = read_ec_pubkey(file)) == 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); + } else if (type == COSE_RS256) { + if ((rsa = read_rsa_pubkey(file)) == 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); + } else if (type == COSE_EDDSA) { + if ((eddsa = read_eddsa_pubkey(file)) == 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); + } + + return (pk); +} + +int +assert_verify(int argc, char **argv) +{ + fido_assert_t *assert = NULL; + void *pk = NULL; + char *in_path = NULL; + FILE *in_f = NULL; + int type = COSE_ES256; + int flags = 0; + int ch; + int r; + + while ((ch = getopt(argc, argv, "dhi:pv")) != -1) { + switch (ch) { + case 'd': + flags |= FLAG_DEBUG; + break; + case 'h': + flags |= FLAG_HMAC; + break; + case 'i': + in_path = optarg; + break; + case 'p': + flags |= FLAG_UP; + break; + case 'v': + flags |= FLAG_UV; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1 || argc > 2) + usage(); + + in_f = open_read(in_path); + + if (argc > 1) { + if (strcmp(argv[1], "es256") == 0) + type = COSE_ES256; + else if (strcmp(argv[1], "rs256") == 0) + type = COSE_RS256; + else if (strcmp(argv[1], "eddsa") == 0) + type = COSE_EDDSA; + else + errx(1, "unknown type %s", argv[1]); + } + + fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); + + pk = load_pubkey(type, argv[0]); + assert = prepare_assert(in_f, flags); + if ((r = fido_assert_verify(assert, 0, type, pk)) != FIDO_OK) + errx(1, "fido_assert_verify: %s", fido_strerr(r)); + fido_assert_free(&assert); + + fclose(in_f); + in_f = NULL; + + exit(0); +} diff --git a/tools/base64.c b/tools/base64.c new file mode 100644 index 0000000..9f31def --- /dev/null +++ b/tools/base64.c @@ -0,0 +1,135 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +int +base64_encode(const void *ptr, size_t len, char **out) +{ + BIO *bio_b64 = NULL; + BIO *bio_mem = NULL; + char *b64_ptr = NULL; + long b64_len; + int n; + int ok = -1; + + if (ptr == NULL || out == NULL || len > INT_MAX) + return (-1); + + *out = NULL; + + if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL) + goto fail; + if ((bio_mem = BIO_new(BIO_s_mem())) == NULL) + goto fail; + + BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL); + BIO_push(bio_b64, bio_mem); + + n = BIO_write(bio_b64, ptr, (int)len); + if (n < 0 || (size_t)n != len) + goto fail; + + if (BIO_flush(bio_b64) < 0) + goto fail; + + b64_len = BIO_get_mem_data(bio_b64, &b64_ptr); + if (b64_len < 0 || (size_t)b64_len == SIZE_MAX || b64_ptr == NULL) + goto fail; + if ((*out = calloc(1, (size_t)b64_len + 1)) == NULL) + goto fail; + + memcpy(*out, b64_ptr, (size_t)b64_len); + ok = 0; + +fail: + BIO_free(bio_b64); + BIO_free(bio_mem); + + return (ok); +} + +int +base64_decode(char *in, void **ptr, size_t *len) +{ + BIO *bio_mem = NULL; + BIO *bio_b64 = NULL; + size_t alloc_len; + int n; + int ok = -1; + + if (in == NULL || ptr == NULL || len == NULL || strlen(in) > INT_MAX) + return (-1); + + *ptr = NULL; + *len = 0; + + if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL) + goto fail; + if ((bio_mem = BIO_new_mem_buf((void *)in, -1)) == NULL) + goto fail; + + BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL); + BIO_push(bio_b64, bio_mem); + + alloc_len = strlen(in); + if ((*ptr = calloc(1, alloc_len)) == NULL) + goto fail; + + n = BIO_read(bio_b64, *ptr, (int)alloc_len); + if (n <= 0 || BIO_eof(bio_b64) == 0) + goto fail; + + *len = (size_t)n; + ok = 0; + +fail: + BIO_free(bio_b64); + BIO_free(bio_mem); + + if (ok < 0) { + free(*ptr); + *ptr = NULL; + *len = 0; + } + + return (ok); +} + +int +base64_read(FILE *f, struct blob *out) +{ + char *line = NULL; + size_t linesize = 0; + ssize_t n; + + out->ptr = NULL; + out->len = 0; + + if ((n = getline(&line, &linesize, f)) <= 0 || + (size_t)n != strlen(line)) { + free(line); /* XXX should be free'd _even_ if getline() fails */ + return (-1); + } + + if (base64_decode(line, (void **)&out->ptr, &out->len) < 0) { + free(line); + return (-1); + } + + free(line); + + return (0); +} diff --git a/tools/bio.c b/tools/bio.c new file mode 100644 index 0000000..b8f9b38 --- /dev/null +++ b/tools/bio.c @@ -0,0 +1,270 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +static void +print_template(const fido_bio_template_array_t *ta, size_t idx) +{ + char *id = NULL; + const fido_bio_template_t *t = NULL; + + if ((t = fido_bio_template(ta, idx)) == NULL) + errx(1, "fido_bio_template"); + + if (base64_encode(fido_bio_template_id_ptr(t), + fido_bio_template_id_len(t), &id) < 0) + errx(1, "output error"); + + printf("%02u: %s %s\n", (unsigned)idx, id, fido_bio_template_name(t)); + + free(id); +} + +int +bio_list(char *path) +{ + char pin[1024]; + fido_bio_template_array_t *ta = NULL; + fido_dev_t *dev = NULL; + int r; + + if (path == NULL) + usage(); + if ((ta = fido_bio_template_array_new()) == NULL) + errx(1, "fido_bio_template_array_new"); + + dev = open_dev(path); + read_pin(path, pin, sizeof(pin)); + r = fido_bio_dev_get_template_array(dev, ta, pin); + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_bio_dev_get_template_array: %s", fido_strerr(r)); + for (size_t i = 0; i < fido_bio_template_array_count(ta); i++) + print_template(ta, i); + + fido_bio_template_array_free(&ta); + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +int +bio_set_name(char *path, char *id, char *name) +{ + char pin[1024]; + fido_bio_template_t *t = NULL; + fido_dev_t *dev = NULL; + int r; + size_t id_blob_len = 0; + void *id_blob_ptr = NULL; + + if (path == NULL) + usage(); + if ((t = fido_bio_template_new()) == NULL) + errx(1, "fido_bio_template_new"); + + if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0) + errx(1, "base64_decode"); + + if ((r = fido_bio_template_set_name(t, name)) != FIDO_OK) + errx(1, "fido_bio_template_set_name: %s", fido_strerr(r)); + if ((r = fido_bio_template_set_id(t, id_blob_ptr, + id_blob_len)) != FIDO_OK) + errx(1, "fido_bio_template_set_id: %s", fido_strerr(r)); + + dev = open_dev(path); + read_pin(path, pin, sizeof(pin)); + r = fido_bio_dev_set_template_name(dev, t, pin); + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_bio_dev_set_template_name: %s", fido_strerr(r)); + + free(id_blob_ptr); + fido_bio_template_free(&t); + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +static const char * +plural(uint8_t n) +{ + if (n == 1) + return ""; + return "s"; +} + +static const char * +enroll_strerr(uint8_t n) +{ + switch (n) { + case FIDO_BIO_ENROLL_FP_GOOD: + return "Sample ok"; + case FIDO_BIO_ENROLL_FP_TOO_HIGH: + return "Sample too high"; + case FIDO_BIO_ENROLL_FP_TOO_LOW: + return "Sample too low"; + case FIDO_BIO_ENROLL_FP_TOO_LEFT: + return "Sample too left"; + case FIDO_BIO_ENROLL_FP_TOO_RIGHT: + return "Sample too right"; + case FIDO_BIO_ENROLL_FP_TOO_FAST: + return "Sample too fast"; + case FIDO_BIO_ENROLL_FP_TOO_SLOW: + return "Sample too slow"; + case FIDO_BIO_ENROLL_FP_POOR_QUALITY: + return "Poor quality sample"; + case FIDO_BIO_ENROLL_FP_TOO_SKEWED: + return "Sample too skewed"; + case FIDO_BIO_ENROLL_FP_TOO_SHORT: + return "Sample too short"; + case FIDO_BIO_ENROLL_FP_MERGE_FAILURE: + return "Sample merge failure"; + case FIDO_BIO_ENROLL_FP_EXISTS: + return "Sample exists"; + case FIDO_BIO_ENROLL_FP_DATABASE_FULL: + return "Fingerprint database full"; + case FIDO_BIO_ENROLL_NO_USER_ACTIVITY: + return "No user activity"; + case FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION: + return "No user presence transition"; + default: + return "Unknown error"; + } +} + +int +bio_enroll(char *path) +{ + char pin[1024]; + fido_bio_enroll_t *e = NULL; + fido_bio_template_t *t = NULL; + fido_dev_t *dev = NULL; + int r; + + if (path == NULL) + usage(); + if ((t = fido_bio_template_new()) == NULL) + errx(1, "fido_bio_template_new"); + if ((e = fido_bio_enroll_new()) == NULL) + errx(1, "fido_bio_enroll_new"); + + dev = open_dev(path); + read_pin(path, pin, sizeof(pin)); + + printf("Touch your security key.\n"); + + r = fido_bio_dev_enroll_begin(dev, t, e, 10000, pin); + explicit_bzero(pin, sizeof(pin)); + if (r != FIDO_OK) + errx(1, "fido_bio_dev_enroll_begin: %s", fido_strerr(r)); + + printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e))); + + while (fido_bio_enroll_remaining_samples(e) > 0) { + printf("Touch your security key (%u sample%s left).\n", + (unsigned)fido_bio_enroll_remaining_samples(e), + plural(fido_bio_enroll_remaining_samples(e))); + if ((r = fido_bio_dev_enroll_continue(dev, t, e, + 10000)) != FIDO_OK) { + errx(1, "fido_bio_dev_enroll_continue: %s", + fido_strerr(r)); + } + printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e))); + } + + fido_bio_template_free(&t); + fido_bio_enroll_free(&e); + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +int +bio_delete(fido_dev_t *dev, char *path, char *id) +{ + char pin[1024]; + fido_bio_template_t *t = NULL; + int r; + size_t id_blob_len = 0; + void *id_blob_ptr = NULL; + + if (path == NULL) + usage(); + if ((t = fido_bio_template_new()) == NULL) + errx(1, "fido_bio_template_new"); + + if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0) + errx(1, "base64_decode"); + if ((r = fido_bio_template_set_id(t, id_blob_ptr, + id_blob_len)) != FIDO_OK) + errx(1, "fido_bio_template_set_id: %s", fido_strerr(r)); + + read_pin(path, pin, sizeof(pin)); + r = fido_bio_dev_enroll_remove(dev, t, pin); + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_bio_dev_enroll_remove: %s", fido_strerr(r)); + + free(id_blob_ptr); + fido_bio_template_free(&t); + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +static const char * +type_str(uint8_t t) +{ + switch (t) { + case 1: + return "touch"; + case 2: + return "swipe"; + default: + return "unknown"; + } +} + +void +bio_info(fido_dev_t *dev) +{ + fido_bio_info_t *i = NULL; + int r; + + if ((i = fido_bio_info_new()) == NULL) + errx(1, "fido_bio_info_new"); + if ((r = fido_bio_dev_get_info(dev, i)) != FIDO_OK) { + fido_bio_info_free(&i); + return; + } + + printf("sensor type: %u (%s)\n", (unsigned)fido_bio_info_type(i), + type_str(fido_bio_info_type(i))); + printf("max samples: %u\n", (unsigned)fido_bio_info_max_samples(i)); + + fido_bio_info_free(&i); +} diff --git a/tools/cred_make.c b/tools/cred_make.c new file mode 100644 index 0000000..380c67a --- /dev/null +++ b/tools/cred_make.c @@ -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. + */ + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +static fido_cred_t * +prepare_cred(FILE *in_f, int type, int flags) +{ + fido_cred_t *cred = NULL; + struct blob cdh; + struct blob uid; + char *rpid = NULL; + char *uname = NULL; + int r; + + memset(&cdh, 0, sizeof(cdh)); + memset(&uid, 0, sizeof(uid)); + + r = base64_read(in_f, &cdh); + r |= string_read(in_f, &rpid); + r |= string_read(in_f, &uname); + r |= base64_read(in_f, &uid); + if (r < 0) + errx(1, "input error"); + + if (flags & FLAG_DEBUG) { + fprintf(stderr, "client data hash:\n"); + xxd(cdh.ptr, cdh.len); + fprintf(stderr, "relying party id: %s\n", rpid); + fprintf(stderr, "user name: %s\n", uname); + fprintf(stderr, "user id:\n"); + xxd(uid.ptr, uid.len); + } + + if ((cred = fido_cred_new()) == NULL) + errx(1, "fido_cred_new"); + + if ((r = fido_cred_set_type(cred, type)) != FIDO_OK || + (r = fido_cred_set_clientdata_hash(cred, cdh.ptr, + cdh.len)) != FIDO_OK || + (r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK || + (r = fido_cred_set_user(cred, uid.ptr, uid.len, uname, NULL, + NULL)) != FIDO_OK) + errx(1, "fido_cred_set: %s", fido_strerr(r)); + + if (flags & FLAG_RK) { + if ((r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) + errx(1, "fido_cred_set_rk: %s", fido_strerr(r)); + } + if (flags & FLAG_UV) { + if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) + errx(1, "fido_cred_set_uv: %s", fido_strerr(r)); + } + if (flags & FLAG_HMAC) { + if ((r = fido_cred_set_extensions(cred, + FIDO_EXT_HMAC_SECRET)) != FIDO_OK) + errx(1, "fido_cred_set_extensions: %s", fido_strerr(r)); + } + + free(cdh.ptr); + free(uid.ptr); + free(rpid); + free(uname); + + return (cred); +} + +static void +print_attcred(FILE *out_f, const fido_cred_t *cred) +{ + char *cdh = NULL; + char *authdata = NULL; + char *id = NULL; + char *sig = NULL; + char *x5c = NULL; + int r; + + r = base64_encode(fido_cred_clientdata_hash_ptr(cred), + fido_cred_clientdata_hash_len(cred), &cdh); + r |= base64_encode(fido_cred_authdata_ptr(cred), + fido_cred_authdata_len(cred), &authdata); + r |= base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), + &id); + r |= base64_encode(fido_cred_sig_ptr(cred), fido_cred_sig_len(cred), + &sig); + if (fido_cred_x5c_ptr(cred) != NULL) + r |= base64_encode(fido_cred_x5c_ptr(cred), + fido_cred_x5c_len(cred), &x5c); + if (r < 0) + errx(1, "output error"); + + fprintf(out_f, "%s\n", cdh); + fprintf(out_f, "%s\n", fido_cred_rp_id(cred)); + fprintf(out_f, "%s\n", fido_cred_fmt(cred)); + fprintf(out_f, "%s\n", authdata); + fprintf(out_f, "%s\n", id); + fprintf(out_f, "%s\n", sig); + if (x5c != NULL) + fprintf(out_f, "%s\n", x5c); + + free(cdh); + free(authdata); + free(id); + free(sig); + free(x5c); +} + +int +cred_make(int argc, char **argv) +{ + fido_dev_t *dev = NULL; + fido_cred_t *cred = NULL; + char prompt[1024]; + char pin[1024]; + char *in_path = NULL; + char *out_path = NULL; + FILE *in_f = NULL; + FILE *out_f = NULL; + int type = COSE_ES256; + int flags = 0; + int ch; + int r; + + while ((ch = getopt(argc, argv, "dhi:o:qruv")) != -1) { + switch (ch) { + case 'd': + flags |= FLAG_DEBUG; + break; + case 'h': + flags |= FLAG_HMAC; + break; + case 'i': + in_path = optarg; + break; + case 'o': + out_path = optarg; + break; + case 'q': + flags |= FLAG_QUIET; + break; + case 'r': + flags |= FLAG_RK; + break; + case 'u': + flags |= FLAG_U2F; + break; + case 'v': + flags |= FLAG_UV; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1 || argc > 2) + usage(); + + in_f = open_read(in_path); + out_f = open_write(out_path); + + if (argc > 1) { + if (strcmp(argv[1], "es256") == 0) + type = COSE_ES256; + else if (strcmp(argv[1], "rs256") == 0) + type = COSE_RS256; + else if (strcmp(argv[1], "eddsa") == 0) + type = COSE_EDDSA; + else + errx(1, "unknown type %s", argv[1]); + } + + fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); + + cred = prepare_cred(in_f, type, flags); + + dev = open_dev(argv[0]); + if (flags & FLAG_U2F) + fido_dev_force_u2f(dev); + + r = fido_dev_make_cred(dev, cred, NULL); + if (r == FIDO_ERR_PIN_REQUIRED && !(flags & FLAG_QUIET)) { + r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", + argv[0]); + if (r < 0 || (size_t)r >= sizeof(prompt)) + errx(1, "snprintf"); + if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) + errx(1, "readpassphrase"); + r = fido_dev_make_cred(dev, cred, pin); + } + + explicit_bzero(pin, sizeof(pin)); + if (r != FIDO_OK) + errx(1, "fido_dev_make_cred: %s", fido_strerr(r)); + print_attcred(out_f, cred); + + fido_dev_close(dev); + fido_dev_free(&dev); + fido_cred_free(&cred); + + fclose(in_f); + fclose(out_f); + in_f = NULL; + out_f = NULL; + + exit(0); +} diff --git a/tools/cred_verify.c b/tools/cred_verify.c new file mode 100644 index 0000000..3f7a400 --- /dev/null +++ b/tools/cred_verify.c @@ -0,0 +1,177 @@ +/* + * 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 +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +static fido_cred_t * +prepare_cred(FILE *in_f, int type, int flags) +{ + fido_cred_t *cred = NULL; + struct blob cdh; + struct blob authdata; + struct blob id; + struct blob sig; + struct blob x5c; + char *rpid = NULL; + char *fmt = NULL; + int r; + + memset(&cdh, 0, sizeof(cdh)); + memset(&authdata, 0, sizeof(authdata)); + memset(&id, 0, sizeof(id)); + memset(&sig, 0, sizeof(sig)); + memset(&x5c, 0, sizeof(x5c)); + + r = base64_read(in_f, &cdh); + r |= string_read(in_f, &rpid); + r |= string_read(in_f, &fmt); + r |= base64_read(in_f, &authdata); + r |= base64_read(in_f, &id); + r |= base64_read(in_f, &sig); + if (r < 0) + errx(1, "input error"); + + (void)base64_read(in_f, &x5c); + + if (flags & FLAG_DEBUG) { + fprintf(stderr, "client data hash:\n"); + xxd(cdh.ptr, cdh.len); + fprintf(stderr, "relying party id: %s\n", rpid); + fprintf(stderr, "format: %s\n", fmt); + fprintf(stderr, "authenticator data:\n"); + xxd(authdata.ptr, authdata.len); + fprintf(stderr, "credential id:\n"); + xxd(id.ptr, id.len); + fprintf(stderr, "signature:\n"); + xxd(sig.ptr, sig.len); + fprintf(stderr, "x509:\n"); + xxd(x5c.ptr, x5c.len); + } + + if ((cred = fido_cred_new()) == NULL) + errx(1, "fido_cred_new"); + + if ((r = fido_cred_set_type(cred, type)) != FIDO_OK || + (r = fido_cred_set_clientdata_hash(cred, cdh.ptr, + cdh.len)) != FIDO_OK || + (r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK || + (r = fido_cred_set_authdata(cred, authdata.ptr, + authdata.len)) != FIDO_OK || + (r = fido_cred_set_sig(cred, sig.ptr, sig.len)) != FIDO_OK || + (r = fido_cred_set_fmt(cred, fmt)) != FIDO_OK) + errx(1, "fido_cred_set: %s", fido_strerr(r)); + + if (x5c.ptr != NULL) { + if ((r = fido_cred_set_x509(cred, x5c.ptr, x5c.len)) != FIDO_OK) + errx(1, "fido_cred_set_x509: %s", fido_strerr(r)); + } + + if (flags & FLAG_UV) { + if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) + errx(1, "fido_cred_set_uv: %s", fido_strerr(r)); + } + if (flags & FLAG_HMAC) { + if ((r = fido_cred_set_extensions(cred, + FIDO_EXT_HMAC_SECRET)) != FIDO_OK) + errx(1, "fido_cred_set_extensions: %s", fido_strerr(r)); + } + + free(cdh.ptr); + free(authdata.ptr); + free(id.ptr); + free(sig.ptr); + free(x5c.ptr); + free(rpid); + free(fmt); + + return (cred); +} + +int +cred_verify(int argc, char **argv) +{ + fido_cred_t *cred = NULL; + char *in_path = NULL; + char *out_path = NULL; + FILE *in_f = NULL; + FILE *out_f = NULL; + int type = COSE_ES256; + int flags = 0; + int ch; + int r; + + while ((ch = getopt(argc, argv, "dhi:o:v")) != -1) { + switch (ch) { + case 'd': + flags |= FLAG_DEBUG; + break; + case 'h': + flags |= FLAG_HMAC; + break; + case 'i': + in_path = optarg; + break; + case 'o': + out_path = optarg; + break; + case 'v': + flags |= FLAG_UV; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + + in_f = open_read(in_path); + out_f = open_write(out_path); + + if (argc > 0) { + if (strcmp(argv[0], "es256") == 0) + type = COSE_ES256; + else if (strcmp(argv[0], "rs256") == 0) + type = COSE_RS256; + else if (strcmp(argv[0], "eddsa") == 0) + type = COSE_EDDSA; + else + errx(1, "unknown type %s", argv[0]); + } + + fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); + cred = prepare_cred(in_f, type, flags); + + if (fido_cred_x5c_ptr(cred) == NULL) { + if ((r = fido_cred_verify_self(cred)) != FIDO_OK) + errx(1, "fido_cred_verify_self: %s", fido_strerr(r)); + } else { + if ((r = fido_cred_verify(cred)) != FIDO_OK) + errx(1, "fido_cred_verify: %s", fido_strerr(r)); + } + + print_cred(out_f, type, cred); + fido_cred_free(&cred); + + fclose(in_f); + fclose(out_f); + in_f = NULL; + out_f = NULL; + + exit(0); +} diff --git a/tools/credman.c b/tools/credman.c new file mode 100644 index 0000000..08c9eb8 --- /dev/null +++ b/tools/credman.c @@ -0,0 +1,237 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +int +credman_get_metadata(fido_dev_t *dev, const char *path) +{ + fido_credman_metadata_t *metadata = NULL; + char pin[1024]; + int r; + + if ((metadata = fido_credman_metadata_new()) == NULL) + errx(1, "fido_credman_metadata_new"); + + read_pin(path, pin, sizeof(pin)); + r = fido_credman_get_dev_metadata(dev, metadata, pin); + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_credman_get_dev_metadata: %s", fido_strerr(r)); + + printf("existing rk(s): %u\n", + (unsigned)fido_credman_rk_existing(metadata)); + printf("possible rk(s): %u\n", + (unsigned)fido_credman_rk_remaining(metadata)); + + fido_credman_metadata_free(&metadata); + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +static void +print_rp(fido_credman_rp_t *rp, size_t idx) +{ + char *rp_id_hash = NULL; + + if (base64_encode(fido_credman_rp_id_hash_ptr(rp, idx), + fido_credman_rp_id_hash_len(rp, idx), &rp_id_hash) < 0) + errx(1, "output error"); + + printf("%02u: %s %s\n", (unsigned)idx, rp_id_hash, + fido_credman_rp_id(rp, idx)); + + free(rp_id_hash); + rp_id_hash = NULL; +} + +int +credman_list_rp(char *path) +{ + fido_dev_t *dev = NULL; + fido_credman_rp_t *rp = NULL; + char pin[1024]; + int r; + + if (path == NULL) + usage(); + if ((rp = fido_credman_rp_new()) == NULL) + errx(1, "fido_credman_rp_new"); + + dev = open_dev(path); + read_pin(path, pin, sizeof(pin)); + r = fido_credman_get_dev_rp(dev, rp, pin); + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_credman_get_dev_rp: %s", fido_strerr(r)); + + for (size_t i = 0; i < fido_credman_rp_count(rp); i++) + print_rp(rp, i); + + fido_credman_rp_free(&rp); + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +static void +print_rk(const fido_credman_rk_t *rk, size_t idx) +{ + const fido_cred_t *cred; + char *id = NULL; + char *user_id = NULL; + const char *type; + + if ((cred = fido_credman_rk(rk, idx)) == NULL) + errx(1, "fido_credman_rk"); + if (base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), + &id) < 0 || base64_encode(fido_cred_user_id_ptr(cred), + fido_cred_user_id_len(cred), &user_id) < 0) + errx(1, "output error"); + + switch (fido_cred_type(cred)) { + case COSE_EDDSA: + type = "eddsa"; + break; + case COSE_ES256: + type = "es256"; + break; + case COSE_RS256: + type = "rs256"; + break; + default: + type = "unknown"; + break; + } + + printf("%02u: %s %s (%s) %s\n", (unsigned)idx, id, + fido_cred_display_name(cred), user_id, type); + + free(user_id); + free(id); + user_id = NULL; + id = NULL; +} + +int +credman_list_rk(char *path, const char *rp_id) +{ + fido_dev_t *dev = NULL; + fido_credman_rk_t *rk = NULL; + char pin[1024]; + int r; + + if (path == NULL) + usage(); + if ((rk = fido_credman_rk_new()) == NULL) + errx(1, "fido_credman_rk_new"); + + dev = open_dev(path); + read_pin(path, pin, sizeof(pin)); + r = fido_credman_get_dev_rk(dev, rp_id, rk, pin); + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_credman_get_dev_rk: %s", fido_strerr(r)); + for (size_t i = 0; i < fido_credman_rk_count(rk); i++) + print_rk(rk, i); + + fido_credman_rk_free(&rk); + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +int +credman_print_rk(fido_dev_t *dev, const char *path, char *rp_id, char *cred_id) +{ + const fido_cred_t *cred = NULL; + fido_credman_rk_t *rk = NULL; + char pin[1024]; + void *cred_id_ptr = NULL; + size_t cred_id_len = 0; + int r; + + if ((rk = fido_credman_rk_new()) == NULL) + errx(1, "fido_credman_rk_new"); + if (base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0) + errx(1, "base64_decode"); + + read_pin(path, pin, sizeof(pin)); + r = fido_credman_get_dev_rk(dev, rp_id, rk, pin); + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_credman_get_dev_rk: %s", fido_strerr(r)); + + for (size_t i = 0; i < fido_credman_rk_count(rk); i++) { + if ((cred = fido_credman_rk(rk, i)) == NULL || + fido_cred_id_ptr(cred) == NULL) + errx(1, "output error"); + if (cred_id_len != fido_cred_id_len(cred) || + memcmp(cred_id_ptr, fido_cred_id_ptr(cred), cred_id_len)) + continue; + print_cred(stdout, fido_cred_type(cred), cred); + goto out; + } + + errx(1, "credential not found"); + +out: + free(cred_id_ptr); + cred_id_ptr = NULL; + + fido_credman_rk_free(&rk); + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +int +credman_delete_rk(fido_dev_t *dev, const char *path, char *id) +{ + char pin[1024]; + void *id_ptr = NULL; + size_t id_len = 0; + int r; + + if (base64_decode(id, &id_ptr, &id_len) < 0) + errx(1, "base64_decode"); + + read_pin(path, pin, sizeof(pin)); + r = fido_credman_del_dev_rk(dev, id_ptr, id_len, pin); + explicit_bzero(pin, sizeof(pin)); + + if (r != FIDO_OK) + errx(1, "fido_credman_del_dev_rk: %s", fido_strerr(r)); + + free(id_ptr); + id_ptr = NULL; + + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} diff --git a/tools/extern.h b/tools/extern.h new file mode 100644 index 0000000..e79e6f0 --- /dev/null +++ b/tools/extern.h @@ -0,0 +1,64 @@ +/* + * 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_ + +struct blob { + unsigned char *ptr; + size_t len; +}; + +#define TOKEN_OPT "CDILPRSVbcdei:k:n:r" + +#define FLAG_DEBUG 0x01 +#define FLAG_QUIET 0x02 +#define FLAG_RK 0x04 +#define FLAG_UV 0x08 +#define FLAG_U2F 0x10 +#define FLAG_HMAC 0x20 +#define FLAG_UP 0x40 + +EC_KEY *read_ec_pubkey(const char *); +fido_dev_t *open_dev(const char *); +FILE *open_read(const char *); +FILE *open_write(const char *); +int assert_get(int, char **); +int assert_verify(int, char **); +int base64_decode(char *, void **, size_t *); +int base64_encode(const void *, size_t, char **); +int base64_read(FILE *, struct blob *); +int bio_delete(fido_dev_t *, char *, char *); +int bio_enroll(char *); +void bio_info(fido_dev_t *); +int bio_list(char *); +int bio_set_name(char *, char *, char *); +int cred_make(int, char **); +int cred_verify(int, char **); +int credman_delete_rk(fido_dev_t *, const char *, char *); +int credman_get_metadata(fido_dev_t *, const char *); +int credman_list_rk(char *, const char *); +int credman_list_rp(char *); +int credman_print_rk(fido_dev_t *, const char *, char *, char *); +int pin_change(char *); +int pin_set(char *); +int string_read(FILE *, char **); +int token_delete(int, char **, char *); +int token_info(int, char **, char *); +int token_list(int, char **, char *); +int token_reset(char *); +int token_set(int, char **, char *); +int write_ec_pubkey(FILE *, const void *, size_t); +int write_rsa_pubkey(FILE *, const void *, size_t); +RSA *read_rsa_pubkey(const char *); +EVP_PKEY *read_eddsa_pubkey(const char *); +int write_eddsa_pubkey(FILE *, const void *, size_t); +void print_cred(FILE *, int, const fido_cred_t *); +void read_pin(const char *, char *, size_t); +void usage(void); +void xxd(const void *, size_t); + +#endif /* _EXTERN_H_ */ diff --git a/tools/fido2-assert.c b/tools/fido2-assert.c new file mode 100644 index 0000000..9ce537a --- /dev/null +++ b/tools/fido2-assert.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. + */ + +/* + * Example usage: + * + * $ echo assertion challenge | openssl sha256 -binary | base64 > assert_param + * $ echo relying party >> assert_param + * $ head -1 cred >> assert_param # credential id + * $ tail -n +2 cred > pubkey # credential pubkey + * $ fido2-assert -G -i assert_param /dev/hidraw5 | fido2-assert -V pubkey rs256 + * + * See blurb in fido2-cred.c on how to obtain cred. + */ + +#include +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +void +usage(void) +{ + fprintf(stderr, +"usage: fido2-assert -G [-dhpruv] [-i input_file] [-o output_file] device\n" +" fido2-assert -V [-dhpv] [-i input_file] key_file [type]\n" + ); + + exit(1); +} + +int +main(int argc, char **argv) +{ + if (argc < 2 || strlen(argv[1]) != 2 || argv[1][0] != '-') + usage(); + + switch (argv[1][1]) { + case 'G': + return (assert_get(--argc, ++argv)); + case 'V': + return (assert_verify(--argc, ++argv)); + } + + usage(); + + /* NOTREACHED */ +} diff --git a/tools/fido2-cred.c b/tools/fido2-cred.c new file mode 100644 index 0000000..45efca0 --- /dev/null +++ b/tools/fido2-cred.c @@ -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. + */ + +/* + * Example usage: + * + * $ echo credential challenge | openssl sha256 -binary | base64 > cred_param + * $ echo relying party >> cred_param + * $ echo user name >> cred_param + * $ dd if=/dev/urandom bs=1 count=32 | base64 >> cred_param + * $ fido2-cred -M -i cred_param /dev/hidraw5 | fido2-cred -V -o cred + */ + +#include +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +void +usage(void) +{ + fprintf(stderr, +"usage: fido2-cred -M [-dhqruv] [-i input_file] [-o output_file] device [type]\n" +" fido2-cred -V [-dhv] [-i input_file] [-o output_file] [type]\n" + ); + + exit(1); +} + +int +main(int argc, char **argv) +{ + if (argc < 2 || strlen(argv[1]) != 2 || argv[1][0] != '-') + usage(); + + switch (argv[1][1]) { + case 'M': + return (cred_make(--argc, ++argv)); + case 'V': + return (cred_verify(--argc, ++argv)); + } + + usage(); + + /* NOTREACHED */ +} diff --git a/tools/fido2-token.c b/tools/fido2-token.c new file mode 100644 index 0000000..0b02fea --- /dev/null +++ b/tools/fido2-token.c @@ -0,0 +1,95 @@ +/* + * 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 +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +static int action; + +void +usage(void) +{ + fprintf(stderr, +"usage: fido2-token [-CR] [-d] device\n" +" fido2-token -D [-de] -i id device\n" +" fido2-token -I [-cd] [-k rp_id -i cred_id] device\n" +" fido2-token -L [-der] [-k rp_id] [device]\n" +" fido2-token -S [-de] [-i template_id -n template_name] device\n" +" fido2-token -V\n" + ); + + exit(1); +} + +static void +setaction(int ch) +{ + if (action) + usage(); + action = ch; +} + +int +main(int argc, char **argv) +{ + int ch; + int flags = 0; + char *device; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'b': + case 'c': + case 'e': + case 'i': + case 'k': + case 'n': + case 'r': + break; /* ignore */ + case 'd': + flags = FIDO_DEBUG; + break; + default: + setaction(ch); + break; + } + } + + if (argc - optind == 1) + device = argv[optind]; + else + device = NULL; + + fido_init(flags); + + switch (action) { + case 'C': + return (pin_change(device)); + case 'D': + return (token_delete(argc, argv, device)); + case 'I': + return (token_info(argc, argv, device)); + case 'L': + return (token_list(argc, argv, device)); + case 'R': + return (token_reset(device)); + case 'S': + return (token_set(argc, argv, device)); + case 'V': + fprintf(stderr, "%d.%d.%d\n", _FIDO_MAJOR, _FIDO_MINOR, + _FIDO_PATCH); + exit(0); + } + + usage(); + + /* NOTREACHED */ +} diff --git a/tools/pin.c b/tools/pin.c new file mode 100644 index 0000000..eab178a --- /dev/null +++ b/tools/pin.c @@ -0,0 +1,146 @@ +/* + * 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 +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +int +pin_set(char *path) +{ + fido_dev_t *dev = NULL; + char prompt[1024]; + char pin1[1024]; + char pin2[1024]; + int r; + int status = 1; + + if (path == NULL) + usage(); + + dev = open_dev(path); + + r = snprintf(prompt, sizeof(prompt), "Enter new PIN for %s: ", path); + if (r < 0 || (size_t)r >= sizeof(prompt)) { + warnx("snprintf"); + goto out; + } + + if (!readpassphrase(prompt, pin1, sizeof(pin1), RPP_ECHO_OFF)) { + warnx("readpassphrase"); + goto out; + } + + r = snprintf(prompt, sizeof(prompt), "Enter the same PIN again: "); + if (r < 0 || (size_t)r >= sizeof(prompt)) { + warnx("snprintf"); + goto out; + } + + if (!readpassphrase(prompt, pin2, sizeof(pin2), RPP_ECHO_OFF)) { + warnx("readpassphrase"); + goto out; + } + + if (strcmp(pin1, pin2) != 0) { + fprintf(stderr, "PINs do not match. Try again.\n"); + goto out; + } + + if ((r = fido_dev_set_pin(dev, pin1, NULL)) != FIDO_OK) { + warnx("fido_dev_set_pin: %s", fido_strerr(r)); + goto out; + } + + fido_dev_close(dev); + fido_dev_free(&dev); + + status = 0; +out: + explicit_bzero(pin1, sizeof(pin1)); + explicit_bzero(pin2, sizeof(pin2)); + + exit(status); +} + +int +pin_change(char *path) +{ + fido_dev_t *dev = NULL; + char prompt[1024]; + char pin0[1024]; + char pin1[1024]; + char pin2[1024]; + int r; + int status = 1; + + if (path == NULL) + usage(); + + dev = open_dev(path); + + r = snprintf(prompt, sizeof(prompt), "Enter current PIN for %s: ", path); + if (r < 0 || (size_t)r >= sizeof(prompt)) { + warnx("snprintf"); + goto out; + } + + if (!readpassphrase(prompt, pin0, sizeof(pin0), RPP_ECHO_OFF)) { + warnx("readpassphrase"); + goto out; + } + + r = snprintf(prompt, sizeof(prompt), "Enter new PIN for %s: ", path); + if (r < 0 || (size_t)r >= sizeof(prompt)) { + warnx("snprintf"); + goto out; + } + + if (!readpassphrase(prompt, pin1, sizeof(pin1), RPP_ECHO_OFF)) { + warnx("readpassphrase"); + goto out; + } + + r = snprintf(prompt, sizeof(prompt), "Enter the same PIN again: "); + if (r < 0 || (size_t)r >= sizeof(prompt)) { + warnx("snprintf"); + goto out; + } + + if (!readpassphrase(prompt, pin2, sizeof(pin2), RPP_ECHO_OFF)) { + warnx("readpassphrase"); + goto out; + } + + if (strcmp(pin1, pin2) != 0) { + fprintf(stderr, "PINs do not match. Try again.\n"); + goto out; + } + + if ((r = fido_dev_set_pin(dev, pin1, pin0)) != FIDO_OK) { + warnx("fido_dev_set_pin: %s", fido_strerr(r)); + goto out; + } + + fido_dev_close(dev); + fido_dev_free(&dev); + + status = 0; +out: + explicit_bzero(pin0, sizeof(pin0)); + explicit_bzero(pin1, sizeof(pin1)); + explicit_bzero(pin2, sizeof(pin2)); + + exit(status); +} diff --git a/tools/sk-libfido2.c b/tools/sk-libfido2.c new file mode 100644 index 0000000..15aa813 --- /dev/null +++ b/tools/sk-libfido2.c @@ -0,0 +1,784 @@ +/* + * Copyright (c) 2019 Markus Friedl + * + * 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 +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#include +#endif /* WITH_OPENSSL */ + +#include + +#ifndef SK_STANDALONE +#include "log.h" +#include "xmalloc.h" +#endif + +/* #define SK_DEBUG 1 */ + +#if defined(_WIN32) +#include +#include +#include +#include +#include +#endif + +#define MAX_FIDO_DEVICES 256 + +/* Compatibility with OpenSSL 1.0.x */ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) +#define ECDSA_SIG_get0(sig, pr, ps) \ + do { \ + (*pr) = sig->r; \ + (*ps) = sig->s; \ + } while (0) +#endif + +#define SK_VERSION_MAJOR 0x00020000 /* current API version */ + +/* Flags */ +#define SK_USER_PRESENCE_REQD 0x01 + +/* Algs */ +#define SK_ECDSA 0x00 +#define SK_ED25519 0x01 + +struct sk_enroll_response { + uint8_t *public_key; + size_t public_key_len; + uint8_t *key_handle; + size_t key_handle_len; + uint8_t *signature; + size_t signature_len; + uint8_t *attestation_cert; + size_t attestation_cert_len; +}; + +struct sk_sign_response { + uint8_t flags; + uint32_t counter; + uint8_t *sig_r; + size_t sig_r_len; + uint8_t *sig_s; + size_t sig_s_len; +}; + +/* If building as part of OpenSSH, then rename exported functions */ +#if !defined(SK_STANDALONE) +#define sk_api_version ssh_sk_api_version +#define sk_enroll ssh_sk_enroll +#define sk_sign ssh_sk_sign +#endif + +/* Return the version of the middleware API */ +uint32_t sk_api_version(void); + +/* Enroll a U2F key (private key generation) */ +int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, + const char *application, uint8_t flags, + struct sk_enroll_response **enroll_response); + +/* Sign a challenge */ +int sk_sign(int alg, const uint8_t *message, size_t message_len, + const char *application, const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, struct sk_sign_response **sign_response); + +#ifdef SK_DEBUG +static void skdebug(const char *func, const char *fmt, ...) + __attribute__((__format__ (printf, 2, 3))); + +static void +skdebug(const char *func, const char *fmt, ...) +{ +#if !defined(SK_STANDALONE) + char *msg; + va_list ap; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + debug("%s: %s", func, msg); + free(msg); +#else + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "%s: ", func); + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + va_end(ap); +#endif /* !SK_STANDALONE */ +} +#else +#define skdebug(...) do { /* nothing */ } while (0) +#endif /* SK_DEBUG */ + +uint32_t +sk_api_version(void) +{ + return SK_VERSION_MAJOR; +} + +/* Select the first identified FIDO device attached to the system */ +static char * +pick_first_device(void) +{ + char *ret = NULL; + fido_dev_info_t *devlist = NULL; + size_t olen = 0; + int r; + const fido_dev_info_t *di; + + if ((devlist = fido_dev_info_new(1)) == NULL) { + skdebug(__func__, "fido_dev_info_new failed"); + goto out; + } + if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) { + skdebug(__func__, "fido_dev_info_manifest failed: %s", + fido_strerr(r)); + goto out; + } + if (olen != 1) { + skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen); + goto out; + } + di = fido_dev_info_ptr(devlist, 0); + if ((ret = strdup(fido_dev_info_path(di))) == NULL) { + skdebug(__func__, "fido_dev_info_path failed"); + goto out; + } + out: + fido_dev_info_free(&devlist, 1); + return ret; +} + +#if defined(HAVE_ARC4RANDOM_BUF) +static int +get_random_challenge(uint8_t *ptr, size_t len) +{ + arc4random_buf(ptr, len); + + return 0; +} +#elif defined(HAVE_GETENTROPY) +static int +get_random_challenge(uint8_t *ptr, size_t len) +{ + if (getentropy(ptr, len) == -1) { + skdebug(__func__, "getentropy failed"); + return -1; + } + + return 0; +} +#elif defined(HAS_DEV_URANDOM) +static int +get_random_challenge(uint8_t *ptr, size_t len) +{ + int fd; + ssize_t n; + + if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0) { + skdebug(__func__, "open %s failed", FIDO_RANDOM_DEV); + return -1; + } + + n = read(fd, ptr, len); + close(fd); + + if (n < 0 || (size_t)n != len) { + skdebug(__func__, "read from %s failed", FIDO_RANDOM_DEV); + return -1; + } + + return 0; +} +#elif defined(_WIN32) +static int +get_random_challenge(uint8_t *ptr, size_t len) +{ + NTSTATUS status; + + status = BCryptGenRandom(NULL, ptr, len, + BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (!NT_SUCCESS(status)) + return -1; + + return 0; +} +#else +#error "please provide an implementation of get_random_challenge() for your platform" +#endif + +/* Check if the specified key handle exists on a given device. */ +static int +try_device(fido_dev_t *dev, const char *application, + const uint8_t *key_handle, size_t key_handle_len) +{ + fido_assert_t *assert = NULL; + uint8_t challenge[32]; + int r = FIDO_ERR_INTERNAL; + + if (get_random_challenge(challenge, sizeof(challenge)) == -1) { + skdebug(__func__, "get_random_challenge failed"); + goto out; + } + + if ((assert = fido_assert_new()) == NULL) { + skdebug(__func__, "fido_assert_new failed"); + goto out; + } + if ((r = fido_assert_set_clientdata_hash(assert, challenge, + sizeof(challenge))) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_clientdata_hash: %s", + fido_strerr(r)); + goto out; + } + if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_assert_allow_cred(assert, key_handle, + key_handle_len)) != FIDO_OK) { + skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { + skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); + goto out; + } + r = fido_dev_get_assert(dev, assert, NULL); + skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); + if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { + /* U2F tokens may return this */ + r = FIDO_OK; + } + out: + fido_assert_free(&assert); + + return r != FIDO_OK ? -1 : 0; +} + +/* Iterate over configured devices looking for a specific key handle */ +static fido_dev_t * +find_device(const char *application, const uint8_t *key_handle, + size_t key_handle_len) +{ + fido_dev_info_t *devlist = NULL; + fido_dev_t *dev = NULL; + size_t devlist_len = 0, i; + const char *path; + int r; + + if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { + skdebug(__func__, "fido_dev_info_new failed"); + goto out; + } + if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, + &devlist_len)) != FIDO_OK) { + skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r)); + goto out; + } + + skdebug(__func__, "found %zu device(s)", devlist_len); + + for (i = 0; i < devlist_len; i++) { + const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); + + if (di == NULL) { + skdebug(__func__, "fido_dev_info_ptr %zu failed", i); + continue; + } + if ((path = fido_dev_info_path(di)) == NULL) { + skdebug(__func__, "fido_dev_info_path %zu failed", i); + continue; + } + skdebug(__func__, "trying device %zu: %s", i, path); + if ((dev = fido_dev_new()) == NULL) { + skdebug(__func__, "fido_dev_new failed"); + continue; + } + if ((r = fido_dev_open(dev, path)) != FIDO_OK) { + skdebug(__func__, "fido_dev_open failed"); + fido_dev_free(&dev); + continue; + } + if (try_device(dev, application, key_handle, + key_handle_len) == 0) { + skdebug(__func__, "found key"); + break; + } + fido_dev_close(dev); + fido_dev_free(&dev); + } + + out: + if (devlist != NULL) + fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); + + return dev; +} + +#ifdef WITH_OPENSSL +/* + * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, + * but the API expects a SEC1 octet string. + */ +static int +pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response) +{ + const uint8_t *ptr; + BIGNUM *x = NULL, *y = NULL; + EC_POINT *q = NULL; + EC_GROUP *g = NULL; + int ret = -1; + + response->public_key = NULL; + response->public_key_len = 0; + + if ((x = BN_new()) == NULL || + (y = BN_new()) == NULL || + (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || + (q = EC_POINT_new(g)) == NULL) { + skdebug(__func__, "libcrypto setup failed"); + goto out; + } + if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { + skdebug(__func__, "fido_cred_pubkey_ptr failed"); + goto out; + } + if (fido_cred_pubkey_len(cred) != 64) { + skdebug(__func__, "bad fido_cred_pubkey_len %zu", + fido_cred_pubkey_len(cred)); + goto out; + } + + if (BN_bin2bn(ptr, 32, x) == NULL || + BN_bin2bn(ptr + 32, 32, y) == NULL) { + skdebug(__func__, "BN_bin2bn failed"); + goto out; + } + if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { + skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); + goto out; + } + response->public_key_len = EC_POINT_point2oct(g, q, + POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + if (response->public_key_len == 0 || response->public_key_len > 2048) { + skdebug(__func__, "bad pubkey length %zu", + response->public_key_len); + goto out; + } + if ((response->public_key = malloc(response->public_key_len)) == NULL) { + skdebug(__func__, "malloc pubkey failed"); + goto out; + } + if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, + response->public_key, response->public_key_len, NULL) == 0) { + skdebug(__func__, "EC_POINT_point2oct failed"); + goto out; + } + /* success */ + ret = 0; + out: + if (ret != 0 && response->public_key != NULL) { + memset(response->public_key, 0, response->public_key_len); + free(response->public_key); + response->public_key = NULL; + } + EC_POINT_free(q); + EC_GROUP_free(g); + BN_clear_free(x); + BN_clear_free(y); + return ret; +} +#endif /* WITH_OPENSSL */ + +static int +pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response) +{ + const uint8_t *ptr; + size_t len; + int ret = -1; + + response->public_key = NULL; + response->public_key_len = 0; + + if ((len = fido_cred_pubkey_len(cred)) != 32) { + skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); + goto out; + } + if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { + skdebug(__func__, "fido_cred_pubkey_ptr failed"); + goto out; + } + response->public_key_len = len; + if ((response->public_key = malloc(response->public_key_len)) == NULL) { + skdebug(__func__, "malloc pubkey failed"); + goto out; + } + memcpy(response->public_key, ptr, len); + ret = 0; + out: + if (ret != 0) + free(response->public_key); + return ret; +} + +static int +pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response) +{ + switch(alg) { +#ifdef WITH_OPENSSL + case SK_ECDSA: + return pack_public_key_ecdsa(cred, response); +#endif /* WITH_OPENSSL */ + case SK_ED25519: + return pack_public_key_ed25519(cred, response); + default: + return -1; + } +} + +int +sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, + const char *application, uint8_t flags, + struct sk_enroll_response **enroll_response) +{ + fido_cred_t *cred = NULL; + fido_dev_t *dev = NULL; + const uint8_t *ptr; + uint8_t user_id[32]; + struct sk_enroll_response *response = NULL; + size_t len; + int cose_alg; + int ret = -1; + int r; + char *device = NULL; + + (void)flags; /* XXX; unused */ +#ifdef SK_DEBUG + fido_init(FIDO_DEBUG); +#endif + if (enroll_response == NULL) { + skdebug(__func__, "enroll_response == NULL"); + goto out; + } + *enroll_response = NULL; + switch(alg) { +#ifdef WITH_OPENSSL + case SK_ECDSA: + cose_alg = COSE_ES256; + break; +#endif /* WITH_OPENSSL */ + case SK_ED25519: + cose_alg = COSE_EDDSA; + break; + default: + skdebug(__func__, "unsupported key type %d", alg); + goto out; + } + if ((device = pick_first_device()) == NULL) { + skdebug(__func__, "pick_first_device failed"); + goto out; + } + skdebug(__func__, "using device %s", device); + if ((cred = fido_cred_new()) == NULL) { + skdebug(__func__, "fido_cred_new failed"); + goto out; + } + memset(user_id, 0, sizeof(user_id)); + if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_cred_set_clientdata_hash(cred, challenge, + challenge_len)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_clientdata_hash: %s", + fido_strerr(r)); + goto out; + } + if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), + "openssh", "openssh", NULL)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); + goto out; + } + if ((dev = fido_dev_new()) == NULL) { + skdebug(__func__, "fido_dev_new failed"); + goto out; + } + if ((r = fido_dev_open(dev, device)) != FIDO_OK) { + skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) { + skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); + goto out; + } + if (fido_cred_x5c_ptr(cred) != NULL) { + if ((r = fido_cred_verify(cred)) != FIDO_OK) { + skdebug(__func__, "fido_cred_verify: %s", + fido_strerr(r)); + goto out; + } + } else { + skdebug(__func__, "self-attested credential"); + if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { + skdebug(__func__, "fido_cred_verify_self: %s", + fido_strerr(r)); + goto out; + } + } + if ((response = calloc(1, sizeof(*response))) == NULL) { + skdebug(__func__, "calloc response failed"); + goto out; + } + if (pack_public_key(alg, cred, response) != 0) { + skdebug(__func__, "pack_public_key failed"); + goto out; + } + if ((ptr = fido_cred_id_ptr(cred)) != NULL) { + len = fido_cred_id_len(cred); + if ((response->key_handle = calloc(1, len)) == NULL) { + skdebug(__func__, "calloc key handle failed"); + goto out; + } + memcpy(response->key_handle, ptr, len); + response->key_handle_len = len; + } + if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { + len = fido_cred_sig_len(cred); + if ((response->signature = calloc(1, len)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + memcpy(response->signature, ptr, len); + response->signature_len = len; + } + if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { + len = fido_cred_x5c_len(cred); + if ((response->attestation_cert = calloc(1, len)) == NULL) { + skdebug(__func__, "calloc attestation cert failed"); + goto out; + } + memcpy(response->attestation_cert, ptr, len); + response->attestation_cert_len = len; + } + *enroll_response = response; + response = NULL; + ret = 0; + out: + free(device); + if (response != NULL) { + free(response->public_key); + free(response->key_handle); + free(response->signature); + free(response->attestation_cert); + free(response); + } + if (dev != NULL) { + fido_dev_close(dev); + fido_dev_free(&dev); + } + if (cred != NULL) { + fido_cred_free(&cred); + } + return ret; +} + +#ifdef WITH_OPENSSL +static int +pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) +{ + ECDSA_SIG *sig = NULL; + const BIGNUM *sig_r, *sig_s; + const unsigned char *cp; + size_t sig_len; + int ret = -1; + + cp = fido_assert_sig_ptr(assert, 0); + sig_len = fido_assert_sig_len(assert, 0); + if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { + skdebug(__func__, "d2i_ECDSA_SIG failed"); + goto out; + } + ECDSA_SIG_get0(sig, &sig_r, &sig_s); + response->sig_r_len = BN_num_bytes(sig_r); + response->sig_s_len = BN_num_bytes(sig_s); + if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || + (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + BN_bn2bin(sig_r, response->sig_r); + BN_bn2bin(sig_s, response->sig_s); + ret = 0; + out: + ECDSA_SIG_free(sig); + if (ret != 0) { + free(response->sig_r); + free(response->sig_s); + response->sig_r = NULL; + response->sig_s = NULL; + } + return ret; +} +#endif /* WITH_OPENSSL */ + +static int +pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) +{ + const unsigned char *ptr; + size_t len; + int ret = -1; + + ptr = fido_assert_sig_ptr(assert, 0); + len = fido_assert_sig_len(assert, 0); + if (len != 64) { + skdebug(__func__, "bad length %zu", len); + goto out; + } + response->sig_r_len = len; + if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + memcpy(response->sig_r, ptr, len); + ret = 0; + out: + if (ret != 0) { + free(response->sig_r); + response->sig_r = NULL; + } + return ret; +} + +static int +pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) +{ + switch(alg) { +#ifdef WITH_OPENSSL + case SK_ECDSA: + return pack_sig_ecdsa(assert, response); +#endif /* WITH_OPENSSL */ + case SK_ED25519: + return pack_sig_ed25519(assert, response); + default: + return -1; + } +} + +int +sk_sign(int alg, const uint8_t *message, size_t message_len, + const char *application, + const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, struct sk_sign_response **sign_response) +{ + fido_assert_t *assert = NULL; + fido_dev_t *dev = NULL; + struct sk_sign_response *response = NULL; + int ret = -1; + int r; + +#ifdef SK_DEBUG + fido_init(FIDO_DEBUG); +#endif + + if (sign_response == NULL) { + skdebug(__func__, "sign_response == NULL"); + goto out; + } + *sign_response = NULL; + if ((dev = find_device(application, key_handle, + key_handle_len)) == NULL) { + skdebug(__func__, "couldn't find device for key handle"); + goto out; + } + if ((assert = fido_assert_new()) == NULL) { + skdebug(__func__, "fido_assert_new failed"); + goto out; + } + if ((r = fido_assert_set_clientdata_hash(assert, message, + message_len)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_clientdata_hash: %s", + fido_strerr(r)); + goto out; + } + if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_assert_allow_cred(assert, key_handle, + key_handle_len)) != FIDO_OK) { + skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_assert_set_up(assert, + (flags & SK_USER_PRESENCE_REQD) ? + FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) { + skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); + goto out; + } + if ((response = calloc(1, sizeof(*response))) == NULL) { + skdebug(__func__, "calloc response failed"); + goto out; + } + response->flags = fido_assert_flags(assert, 0); + response->counter = fido_assert_sigcount(assert, 0); + if (pack_sig(alg, assert, response) != 0) { + skdebug(__func__, "pack_sig failed"); + goto out; + } + *sign_response = response; + response = NULL; + ret = 0; + out: + if (response != NULL) { + free(response->sig_r); + free(response->sig_s); + free(response); + } + if (dev != NULL) { + fido_dev_close(dev); + fido_dev_free(&dev); + } + if (assert != NULL) { + fido_assert_free(&assert); + } + return ret; +} diff --git a/tools/test.sh b/tools/test.sh new file mode 100755 index 0000000..8159a44 --- /dev/null +++ b/tools/test.sh @@ -0,0 +1,96 @@ +#!/bin/bash -e +# +# 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. + +if [[ "$#" -ne 1 ]]; then + echo "usage: test.sh device" 1>&2 + exit 1 +fi + +read -p "This script will reset the authenticator at $1, permanently erasing "\ +"its credentials. Are you *SURE* you want to proceed (yes/no)? " +if [[ "${REPLY}" != "yes" ]]; then + exit 1 +fi + +echo "Resetting authenticator... (tap to continue!)" +fido2-token -R $1 + +CRED_PARAM="$(mktemp /tmp/cred_param.XXXXXXXX)" +ASSERT_PARAM="$(mktemp /tmp/assert_param.XXXXXXXX)" +ASSERT_PUBKEY="$(mktemp /tmp/assert_pubkey.XXXXXXXX)" +ES256_CRED="$(mktemp /tmp/es256_cred.XXXXXXX)" +ES256_CRED_R="$(mktemp /tmp/es256_cred_r.XXXXXXXX)" + +cleanup() { + echo "Cleaning up..." + [[ "${CRED_PARAM}" != "" ]] && rm "${CRED_PARAM}" + [[ "${ASSERT_PARAM}" != "" ]] && rm "${ASSERT_PARAM}" + [[ "${ASSERT_PUBKEY}" != "" ]] && rm "${ASSERT_PUBKEY}" + [[ "${ES256_CRED}" != "" ]] && rm "${ES256_CRED}" + [[ "${ES256_CRED_R}" != "" ]] && rm "${ES256_CRED_R}" +} + +trap cleanup EXIT + +dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${CRED_PARAM}" +echo "Boring Relying Party" >> "${CRED_PARAM}" +echo "Boring User Name" >> "${CRED_PARAM}" +dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 >> "${CRED_PARAM}" +echo "Credential parameters:" +cat "${CRED_PARAM}" + +echo "Generating non-resident ES256 credential... (tap to continue!)" +fido2-cred -M -i "${CRED_PARAM}" $1 | fido2-cred -V | tee "${ES256_CRED}" +echo "Generating resident ES256 credential... (tap to continue!)" +fido2-cred -M -r -i "${CRED_PARAM}" $1 | fido2-cred -V | tee "${ES256_CRED_R}" + +PIN1="$(dd if=/dev/urandom | tr -cd '[:print:]' | fold -w50 | head -1)" +PIN2="$(dd if=/dev/urandom | tr -cd '[:print:]' | fold -w50 | head -1)" + +echo "Setting ${PIN1} as the PIN..." +echo -e "${PIN1}\n${PIN1}" | setsid -w fido2-token -S $1 +echo "Changing PIN from ${PIN1} to ${PIN2}..." +echo -e "${PIN1}\n${PIN2}\n${PIN2}" | setsid -w fido2-token -C $1 +echo "" + +echo "Testing non-resident ES256 credential..." +echo "Getting assertion without user presence verification..." +dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${ASSERT_PARAM}" +echo "Boring Relying Party" >> "${ASSERT_PARAM}" +head -1 "${ES256_CRED}" >> "${ASSERT_PARAM}" +tail -n +2 "${ES256_CRED}" > "${ASSERT_PUBKEY}" +echo "Assertion parameters:" +cat "${ASSERT_PARAM}" +fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V "${ASSERT_PUBKEY}" +echo "Checking that the user presence bit is observed..." +! fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}" +echo "Checking that the user verification bit is observed..." +! fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V -v "${ASSERT_PUBKEY}" +echo "Getting assertion _with_ user presence verification... (tap to continue!)" +fido2-assert -G -p -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}" +echo "Getting assertion _with_ user verification..." +echo -e "${PIN2}\n" | setsid -w fido2-assert -G -v -i "${ASSERT_PARAM}" $1 | \ + fido2-assert -V -v "${ASSERT_PUBKEY}" +echo "" + +echo "Testing resident ES256 credential..." +echo "Getting assertion without user presence verification..." +dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${ASSERT_PARAM}" +echo "Boring Relying Party" >> "${ASSERT_PARAM}" +tail -n +2 "${ES256_CRED_R}" > "${ASSERT_PUBKEY}" +echo "Assertion parameters:" +cat "${ASSERT_PARAM}" +fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V "${ASSERT_PUBKEY}" +echo "Checking that the user presence bit is observed..." +! fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}" +echo "Checking that the user verification bit is observed..." +! fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V -v "${ASSERT_PUBKEY}" +echo "Getting assertion _with_ user presence verification... (tap to continue!)" +fido2-assert -G -r -p -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}" +echo "Getting assertion _with_ user verification..." +echo -e "${PIN2}\n" | setsid -w fido2-assert -G -v -r -i "${ASSERT_PARAM}" $1 | \ + fido2-assert -V -v "${ASSERT_PUBKEY}" +echo "" diff --git a/tools/token.c b/tools/token.c new file mode 100644 index 0000000..b149208 --- /dev/null +++ b/tools/token.c @@ -0,0 +1,364 @@ +/* + * 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 +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +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); +} + +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); +} + +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"); +} + +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"); +} + +static void +print_aaguid(const unsigned char *buf, size_t buflen) +{ + printf("aaguid: "); + + while (buflen--) + printf("%02x", *buf++); + + printf("\n"); +} + +static void +print_maxmsgsiz(uint64_t maxmsgsiz) +{ + printf("maxmsgsiz: %d\n", (int)maxmsgsiz); +} + +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"); +} + +int +token_info(int argc, char **argv, char *path) +{ + char *cred_id = NULL; + char *rp_id = NULL; + fido_cbor_info_t *ci = NULL; + fido_dev_t *dev = NULL; + int ch; + int credman = 0; + int r; + int retrycnt; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'c': + credman = 1; + break; + case 'i': + cred_id = optarg; + break; + case 'k': + rp_id = optarg; + break; + default: + break; /* ignore */ + } + } + + if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL))) + usage(); + + dev = open_dev(path); + + if (credman) + return (credman_get_metadata(dev, path)); + if (cred_id && rp_id) + return (credman_print_rk(dev, path, rp_id, cred_id)); + if (cred_id || rp_id) + usage(); + + 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 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 supported pin protocols */ + print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci), + fido_cbor_info_protocols_len(ci)); + + if ((r = fido_dev_get_retry_count(dev, &retrycnt)) != FIDO_OK) + printf("pin retries: undefined\n"); + else + printf("pin retries: %d\n", retrycnt); + + bio_info(dev); + + fido_cbor_info_free(&ci); +end: + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +int +token_reset(char *path) +{ + fido_dev_t *dev = NULL; + int r; + + if (path == NULL) + usage(); + + dev = open_dev(path); + if ((r = fido_dev_reset(dev)) != FIDO_OK) + errx(1, "fido_dev_reset: %s", fido_strerr(r)); + + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +int +token_set(int argc, char **argv, char *path) +{ + char *id = NULL; + char *name = NULL; + int ch; + int enroll = 0; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'e': + enroll = 1; + break; + case 'i': + id = optarg; + break; + case 'n': + name = optarg; + break; + default: + break; /* ignore */ + } + } + + if (enroll) { + if (id && name) + return (bio_set_name(path, id, name)); + if (!id && !name) + return (bio_enroll(path)); + usage(); + } + + return (pin_set(path)); +} + +int +token_list(int argc, char **argv, char *path) +{ + fido_dev_info_t *devlist; + size_t ndevs; + const char *rp_id = NULL; + int enrolls = 0; + int keys = 0; + int rplist = 0; + int ch; + int r; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'e': + enrolls = 1; + break; + case 'k': + keys = 1; + rp_id = optarg; + break; + case 'r': + rplist = 1; + break; + default: + break; /* ignore */ + } + } + + if (enrolls) + return (bio_list(path)); + if (keys) + return (credman_list_rk(path, rp_id)); + if (rplist) + return (credman_list_rp(path)); + + 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); +} + +int +token_delete(int argc, char **argv, char *path) +{ + char *id = NULL; + fido_dev_t *dev = NULL; + int ch; + int enroll = 0; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'e': + enroll = 1; + break; + case 'i': + id = optarg; + break; + default: + break; /* ignore */ + } + } + + if (path == NULL || id == NULL) + usage(); + + dev = open_dev(path); + + if (id && !enroll) + return (credman_delete_rk(dev, path, id)); + + return (bio_delete(dev, path, id)); +} diff --git a/tools/util.c b/tools/util.c new file mode 100644 index 0000000..de70388 --- /dev/null +++ b/tools/util.c @@ -0,0 +1,364 @@ +/* + * 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 +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../openbsd-compat/openbsd-compat.h" +#ifdef _MSC_VER +#include "../openbsd-compat/posix_win.h" +#endif + +#include "extern.h" + +void +read_pin(const char *path, char *buf, size_t len) +{ + char prompt[1024]; + int r; + + r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path); + if (r < 0 || (size_t)r >= sizeof(prompt)) + errx(1, "snprintf"); + if (!readpassphrase(prompt, buf, len, RPP_ECHO_OFF)) + errx(1, "readpassphrase"); +} + +FILE * +open_write(const char *file) +{ + int fd; + FILE *f; + + if (file == NULL || strcmp(file, "-") == 0) + return (stdout); + if ((fd = open(file, O_WRONLY | O_CREAT, 0600)) < 0) + err(1, "open %s", file); + if ((f = fdopen(fd, "w")) == NULL) + err(1, "fdopen %s", file); + + return (f); +} + +FILE * +open_read(const char *file) +{ + int fd; + FILE *f; + + if (file == NULL || strcmp(file, "-") == 0) { +#ifdef FIDO_FUZZ + setvbuf(stdin, NULL, _IONBF, 0); +#endif + return (stdin); + } + if ((fd = open(file, O_RDONLY)) < 0) + err(1, "open %s", file); + if ((f = fdopen(fd, "r")) == NULL) + err(1, "fdopen %s", file); + + return (f); +} + +void +xxd(const void *buf, size_t count) +{ + const uint8_t *ptr = buf; + size_t i; + + fprintf(stderr, " "); + + for (i = 0; i < count; i++) { + fprintf(stderr, "%02x ", *ptr++); + if ((i + 1) % 16 == 0 && i + 1 < count) + fprintf(stderr, "\n "); + } + + fprintf(stderr, "\n"); + fflush(stderr); +} + +int +string_read(FILE *f, char **out) +{ + char *line = NULL; + size_t linesize = 0; + ssize_t n; + + *out = NULL; + + if ((n = getline(&line, &linesize, f)) <= 0 || + (size_t)n != strlen(line)) { + free(line); + return (-1); + } + + line[n - 1] = '\0'; /* trim \n */ + *out = line; + + return (0); +} + +fido_dev_t * +open_dev(const char *path) +{ + fido_dev_t *dev; + int r; + + if ((dev = fido_dev_new()) == NULL) + errx(1, "fido_dev_new"); + + r = fido_dev_open(dev, path); + if (r != FIDO_OK) + errx(1, "fido_dev_open %s: %s", path, fido_strerr(r)); + + return (dev); +} + +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) { + fclose(fp); + } + if (pkey) { + EVP_PKEY_free(pkey); + } + + return (ec); +} + +int +write_ec_pubkey(FILE *f, const void *ptr, size_t len) +{ + EVP_PKEY *pkey = NULL; + es256_pk_t *pk = NULL; + 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 ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) { + warnx("es256_pk_to_EVP_PKEY"); + goto fail; + } + + if (PEM_write_PUBKEY(f, pkey) == 0) { + warnx("PEM_write_PUBKEY"); + goto fail; + } + + ok = 0; +fail: + es256_pk_free(&pk); + + 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) { + fclose(fp); + } + if (pkey) { + EVP_PKEY_free(pkey); + } + + return (rsa); +} + +int +write_rsa_pubkey(FILE *f, const void *ptr, size_t len) +{ + EVP_PKEY *pkey = NULL; + rs256_pk_t *pk = NULL; + 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 ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) { + warnx("rs256_pk_to_EVP_PKEY"); + goto fail; + } + + if (PEM_write_PUBKEY(f, pkey) == 0) { + warnx("PEM_write_PUBKEY"); + goto fail; + } + + ok = 0; +fail: + rs256_pk_free(&pk); + + 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(FILE *f, const void *ptr, size_t len) +{ + EVP_PKEY *pkey = NULL; + eddsa_pk_t *pk = NULL; + 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 ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) { + warnx("eddsa_pk_to_EVP_PKEY"); + goto fail; + } + + if (PEM_write_PUBKEY(f, pkey) == 0) { + warnx("PEM_write_PUBKEY"); + goto fail; + } + + ok = 0; +fail: + eddsa_pk_free(&pk); + + if (pkey != NULL) { + EVP_PKEY_free(pkey); + } + + return (ok); +} + +void +print_cred(FILE *out_f, int type, const fido_cred_t *cred) +{ + char *id; + int r; + + r = base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &id); + if (r < 0) + errx(1, "output error"); + + fprintf(out_f, "%s\n", id); + + if (type == COSE_ES256) { + write_ec_pubkey(out_f, fido_cred_pubkey_ptr(cred), + fido_cred_pubkey_len(cred)); + } else if (type == COSE_RS256) { + write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred), + fido_cred_pubkey_len(cred)); + } else if (type == COSE_EDDSA) { + write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred), + fido_cred_pubkey_len(cred)); + } else { + errx(1, "print_cred: unknown type"); + } + + free(id); +} diff --git a/udev/70-u2f.rules b/udev/70-u2f.rules new file mode 100644 index 0000000..8dc20a1 --- /dev/null +++ b/udev/70-u2f.rules @@ -0,0 +1,72 @@ +# 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. + +# this udev file should be used with udev 188 and newer +ACTION!="add|change", GOTO="u2f_end" + +# Yubico YubiKey +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0113|0114|0115|0116|0120|0121|0200|0402|0403|0406|0407|0410", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Happlink (formerly Plug-Up) Security KEY +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Neowave Keydo and Keydo AES +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1e0d", ATTRS{idProduct}=="f1d0|f1ae", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# HyperSecu HyperFIDO +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e|2ccf", ATTRS{idProduct}=="0880", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Feitian ePass FIDO, BioPass FIDO2 +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0850|0852|0853|0854|0856|0858|085a|085b|085d", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# JaCarta U2F +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="24dc", ATTRS{idProduct}=="0101|0501", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# U2F Zero +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# VASCO SecureClick +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1a44", ATTRS{idProduct}=="00bb", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Bluink Key +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2abe", ATTRS{idProduct}=="1002", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Thetis Key +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1ea8", ATTRS{idProduct}=="f025", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Nitrokey FIDO U2F +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Google Titan U2F +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="5026", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Tomu board + chopstx U2F + SoloKeys +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="cdab|a2ca", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# SoloKeys +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="5070|50b0", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Trezor +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", TAG+="uaccess", GROUP="plugdev", MODE="0660" +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Infineon FIDO +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="058b", ATTRS{idProduct}=="022d", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Ledger Nano S and Nano X +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001|0004", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Kensington VeriMark +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="06cb", ATTRS{idProduct}=="0088", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# Longmai mFIDO +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="4c4d", ATTRS{idProduct}=="f703", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# eWBM FIDO2 - Goldengate 310, 320, 500, 450 +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="4a1a|4c2a|5c2f|f47c", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +# OnlyKey (FIDO2 / U2F) +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", TAG+="uaccess", GROUP="plugdev", MODE="0660" + +LABEL="u2f_end" diff --git a/udev/CMakeLists.txt b/udev/CMakeLists.txt new file mode 100644 index 0000000..29a9d41 --- /dev/null +++ b/udev/CMakeLists.txt @@ -0,0 +1,7 @@ +# 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. + +if(UDEV_RULES_DIR) + install(FILES 70-u2f.rules DESTINATION ${UDEV_RULES_DIR}) +endif() diff --git a/windows/build.ps1 b/windows/build.ps1 new file mode 100644 index 0000000..aaa848d --- /dev/null +++ b/windows/build.ps1 @@ -0,0 +1,204 @@ +param( + [string]$CMakePath = "C:\Program Files\CMake\bin\cmake.exe", + [string]$GitPath = "C:\Program Files\Git\bin\git.exe", + [string]$SevenZPath = "C:\Program Files\7-Zip\7z.exe", + [string]$GPGPath = "C:\Program Files (x86)\GnuPG\bin\gpg.exe" +) + +$ErrorActionPreference = "Continue" + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +# LibreSSL coordinates. +New-Variable -Name 'LIBRESSL_URL' ` + -Value 'https://ftp.openbsd.org/pub/OpenBSD/LibreSSL' -Option Constant +New-Variable -Name 'LIBRESSL' -Value 'libressl-3.0.2' -Option Constant + +# libcbor coordinates. +New-Variable -Name 'LIBCBOR' -Value 'libcbor-0.5.0' -Option Constant +New-Variable -Name 'LIBCBOR_BRANCH' -Value 'v0.5.0' -Option Constant +New-Variable -Name 'LIBCBOR_GIT' -Value 'https://github.com/pjk/libcbor' ` + -Option Constant + +# Work directories. +New-Variable -Name 'BUILD' -Value "$PSScriptRoot\..\build" -Option Constant +New-Variable -Name 'OUTPUT' -Value "$PSScriptRoot\..\output" -Option Constant + +# Find CMake. +$CMake = $(Get-Command cmake -ErrorAction Ignore | Select-Object -ExpandProperty Source) +if([string]::IsNullOrEmpty($CMake)) { + $CMake = $CMakePath +} + +# Find Git. +$Git = $(Get-Command git -ErrorAction Ignore | Select-Object -ExpandProperty Source) +if([string]::IsNullOrEmpty($Git)) { + $Git = $GitPath +} + +# Find 7z. +$SevenZ = $(Get-Command 7z -ErrorAction Ignore | Select-Object -ExpandProperty Source) +if([string]::IsNullOrEmpty($SevenZ)) { + $SevenZ = $SevenZPath +} + +# Find GPG. +$GPG = $(Get-Command gpg -ErrorAction Ignore | Select-Object -ExpandProperty Source) +if([string]::IsNullOrEmpty($GPG)) { + $GPG = $GPGPath +} + +if(-Not (Test-Path $CMake)) { + throw "Unable to find CMake at $CMake" +} + +if(-Not (Test-Path $Git)) { + throw "Unable to find Git at $Git" +} + +if(-Not (Test-Path $SevenZ)) { + throw "Unable to find 7z at $SevenZ" +} + +if(-Not (Test-Path $GPG)) { + throw "Unable to find GPG at $GPG" +} + +Write-Host "Git: $Git" +Write-Host "CMake: $CMake" +Write-Host "7z: $SevenZ" +Write-Host "GPG: $GPG" + +New-Item -Type Directory ${BUILD} +New-Item -Type Directory ${BUILD}\32 +New-Item -Type Directory ${BUILD}\64 +New-Item -Type Directory ${OUTPUT} +New-Item -Type Directory ${OUTPUT}\pkg\Win64\Release\v142\dynamic +New-Item -Type Directory ${OUTPUT}\pkg\Win32\Release\v142\dynamic + +Push-Location ${BUILD} + +try { + if (Test-Path .\${LIBRESSL}) { + Remove-Item .\${LIBRESSL} -Recurse -ErrorAction Stop + } + + if(-Not (Test-Path .\${LIBRESSL}.tar.gz -PathType leaf)) { + Invoke-WebRequest ${LIBRESSL_URL}/${LIBRESSL}.tar.gz ` + -OutFile .\${LIBRESSL}.tar.gz + } + if(-Not (Test-Path .\${LIBRESSL}.tar.gz.asc -PathType leaf)) { + Invoke-WebRequest ${LIBRESSL_URL}/${LIBRESSL}.tar.gz.asc ` + -OutFile .\${LIBRESSL}.tar.gz.asc + } + + Copy-Item "$PSScriptRoot\libressl.gpg" -Destination "${BUILD}" + & $GPG --list-keys + & $GPG -v --no-default-keyring --keyring ./libressl.gpg ` + --verify .\${LIBRESSL}.tar.gz.asc .\${LIBRESSL}.tar.gz + if ($LastExitCode -ne 0) { + throw "GPG signature verification failed" + } + + & $SevenZ e .\${LIBRESSL}.tar.gz + & $SevenZ x .\${LIBRESSL}.tar + Remove-Item -Force .\${LIBRESSL}.tar + + if(-Not (Test-Path .\${LIBCBOR})) { + Write-Host "Cloning ${LIBCBOR}..." + & $Git clone --branch ${LIBCBOR_BRANCH} ${LIBCBOR_GIT} ` + .\${LIBCBOR} + } +} catch { + throw "Failed to fetch and verify dependencies" +} finally { + Pop-Location +} + +Function Build(${OUTPUT}, ${GENERATOR}, ${ARCH}) { + if(-Not (Test-Path .\${LIBRESSL})) { + New-Item -Type Directory .\${LIBRESSL} -ErrorAction Stop + } + + Push-Location .\${LIBRESSL} + & $CMake ..\..\${LIBRESSL} -G "${GENERATOR}" -A "${ARCH}" ` + -DCMAKE_C_FLAGS_RELEASE="/Zi" ` + -DCMAKE_INSTALL_PREFIX="${OUTPUT}" -DBUILD_SHARED_LIBS=ON ` + -DLIBRESSL_TESTS=OFF + & $CMake --build . --config Release + & $CMake --build . --config Release --target install + Pop-Location + + if(-Not (Test-Path .\${LIBCBOR})) { + New-Item -Type Directory .\${LIBCBOR} -ErrorAction Stop + } + + Push-Location .\${LIBCBOR} + & $CMake ..\..\${LIBCBOR} -G "${GENERATOR}" -A "${ARCH}" ` + -DCMAKE_C_FLAGS_RELEASE="/Zi" ` + -DCMAKE_INSTALL_PREFIX="${OUTPUT}" + & $CMake --build . --config Release + & $CMake --build . --config Release --target install + Pop-Location + + & $CMake ..\.. -G "${GENERATOR}" -A "${ARCH}" ` + -DCBOR_INCLUDE_DIRS="${OUTPUT}\include" ` + -DCBOR_LIBRARY_DIRS="${OUTPUT}\lib" ` + -DCRYPTO_INCLUDE_DIRS="${OUTPUT}\include" ` + -DCRYPTO_LIBRARY_DIRS="${OUTPUT}\lib" ` + -DCMAKE_INSTALL_PREFIX="${OUTPUT}" + & $CMake --build . --config Release + & $CMake --build . --config Release --target install + "cbor.dll", "crypto-45.dll" | %{ Copy-Item "${OUTPUT}\bin\$_" ` + -Destination "examples\Release" } +} + +Function Package-Headers() { + Copy-Item "${OUTPUT}\64\include" -Destination "${OUTPUT}\pkg" ` + -Recurse -ErrorAction Stop +} + +Function Package-Libraries(${SRC}, ${DEST}) { + Copy-Item "${SRC}\bin\cbor.dll" "${DEST}" -ErrorAction Stop + Copy-Item "${SRC}\lib\cbor.lib" "${DEST}" -ErrorAction Stop + Copy-Item "${SRC}\bin\crypto-45.dll" "${DEST}" -ErrorAction Stop + Copy-Item "${SRC}\lib\crypto-45.lib" "${DEST}" -ErrorAction Stop + Copy-Item "${SRC}\lib\fido2.dll" "${DEST}" -ErrorAction Stop + Copy-Item "${SRC}\lib\fido2.lib" "${DEST}" -ErrorAction Stop +} + +Function Package-PDBs(${SRC}, ${DEST}) { + Copy-Item "${SRC}\${LIBRESSL}\crypto\crypto.dir\Release\vc142.pdb" ` + "${DEST}\crypto-45.pdb" -ErrorAction Stop + Copy-Item "${SRC}\${LIBCBOR}\src\cbor_shared.dir\Release\vc142.pdb" ` + "${DEST}\cbor.pdb" -ErrorAction Stop + Copy-Item "${SRC}\src\fido2_shared.dir\Release\vc142.pdb" ` + "${DEST}\fido2.pdb" -ErrorAction Stop +} + +Function Package-Tools(${SRC}, ${DEST}) { + Copy-Item "${SRC}\tools\Release\fido2-assert.exe" ` + "${DEST}\fido2-assert.exe" -ErrorAction stop + Copy-Item "${SRC}\tools\Release\fido2-cred.exe" ` + "${DEST}\fido2-cred.exe" -ErrorAction stop + Copy-Item "${SRC}\tools\Release\fido2-token.exe" ` + "${DEST}\fido2-token.exe" -ErrorAction stop +} + +Push-Location ${BUILD}\64 +Build ${OUTPUT}\64 "Visual Studio 16 2019" "x64" +Pop-Location + +Push-Location ${BUILD}\32 +Build ${OUTPUT}\32 "Visual Studio 16 2019" "Win32" +Pop-Location + +Package-Headers + +Package-Libraries ${OUTPUT}\64 ${OUTPUT}\pkg\Win64\Release\v142\dynamic +Package-PDBs ${BUILD}\64 ${OUTPUT}\pkg\Win64\Release\v142\dynamic +Package-Tools ${BUILD}\64 ${OUTPUT}\pkg\Win64\Release\v142\dynamic + +Package-Libraries ${OUTPUT}\32 ${OUTPUT}\pkg\Win32\Release\v142\dynamic +Package-PDBs ${BUILD}\32 ${OUTPUT}\pkg\Win32\Release\v142\dynamic +Package-Tools ${BUILD}\32 ${OUTPUT}\pkg\Win32\Release\v142\dynamic diff --git a/windows/libressl.gpg b/windows/libressl.gpg new file mode 100644 index 0000000..87d5dad Binary files /dev/null and b/windows/libressl.gpg differ -- cgit v1.2.3