pax_global_header00006660000000000000000000000064141712620330014511gustar00rootroot0000000000000052 comment=7c83da50b2462f96891cc04b10ce0d49176f3870 libfido2-1.10.0/000077500000000000000000000000001417126203300132625ustar00rootroot00000000000000libfido2-1.10.0/CMakeLists.txt000066400000000000000000000336161417126203300160330ustar00rootroot00000000000000# Copyright (c) 2018-2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # detect AppleClang; needs to come before project() cmake_policy(SET CMP0025 NEW) project(libfido2 C) cmake_minimum_required(VERSION 3.0) # Set PIE flags for POSITION_INDEPENDENT_CODE targets, added in CMake 3.14. if(POLICY CMP0083) cmake_policy(SET CMP0083 NEW) endif() include(CheckCCompilerFlag) include(CheckFunctionExists) include(CheckLibraryExists) include(CheckSymbolExists) include(CheckIncludeFiles) include(CheckTypeSize) include(GNUInstallDirs) include(CheckPIESupported OPTIONAL RESULT_VARIABLE CHECK_PIE_SUPPORTED) if(CHECK_PIE_SUPPORTED) check_pie_supported(LANGUAGES C) endif() set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_COLOR_MAKEFILE OFF) set(CMAKE_VERBOSE_MAKEFILE ON) set(FIDO_MAJOR "1") set(FIDO_MINOR "10") set(FIDO_PATCH "0") set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH}) option(BUILD_EXAMPLES "Build example programs" ON) option(BUILD_MANPAGES "Build man pages" ON) option(BUILD_SHARED_LIBS "Build the shared library" ON) option(BUILD_STATIC_LIBS "Build the static library" ON) option(BUILD_TOOLS "Build tool programs" ON) option(FUZZ "Enable fuzzing instrumentation" OFF) option(LIBFUZZER "Build libfuzzer harnesses" OFF) option(USE_HIDAPI "Use hidapi as the HID backend" OFF) option(USE_WINHELLO "Abstract Windows Hello as a FIDO device" ON) option(NFC_LINUX "Enable NFC support on Linux" ON) add_definitions(-D_FIDO_MAJOR=${FIDO_MAJOR}) add_definitions(-D_FIDO_MINOR=${FIDO_MINOR}) add_definitions(-D_FIDO_PATCH=${FIDO_PATCH}) if(CYGWIN OR MSYS OR MINGW) set(WIN32 1) endif() if(WIN32) add_definitions(-DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600) endif() if(APPLE) set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") endif() if(NOT MSVC) set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_POSIX_C_SOURCE=200809L") set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_BSD_SOURCE") if(APPLE) set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_DARWIN_C_SOURCE") set(FIDO_CFLAGS "${FIDO_CFLAGS} -D__STDC_WANT_LIB_EXT1__=1") elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_GNU_SOURCE") set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_DEFAULT_SOURCE") elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") set(FIDO_CFLAGS "${FIDO_CFLAGS} -D__BSD_VISIBLE=1") elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_NETBSD_SOURCE") endif() set(FIDO_CFLAGS "${FIDO_CFLAGS} -std=c99") set(CMAKE_C_FLAGS "${FIDO_CFLAGS} ${CMAKE_C_FLAGS}") endif() check_c_compiler_flag("-Wshorten-64-to-32" HAVE_SHORTEN_64_TO_32) check_c_compiler_flag("-Werror -fstack-protector-all" HAVE_STACK_PROTECTOR_ALL) check_include_files(cbor.h HAVE_CBOR_H) check_include_files(endian.h HAVE_ENDIAN_H) check_include_files(err.h HAVE_ERR_H) check_include_files(openssl/opensslv.h HAVE_OPENSSLV_H) check_include_files(signal.h HAVE_SIGNAL_H) check_include_files(sys/random.h HAVE_SYS_RANDOM_H) check_include_files(unistd.h HAVE_UNISTD_H) check_symbol_exists(arc4random_buf stdlib.h HAVE_ARC4RANDOM_BUF) check_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME) check_symbol_exists(explicit_bzero string.h HAVE_EXPLICIT_BZERO) check_symbol_exists(freezero stdlib.h HAVE_FREEZERO) check_symbol_exists(getline stdio.h HAVE_GETLINE) check_symbol_exists(getopt unistd.h HAVE_GETOPT) check_symbol_exists(getpagesize unistd.h HAVE_GETPAGESIZE) check_symbol_exists(getrandom sys/random.h HAVE_GETRANDOM) check_symbol_exists(memset_s string.h HAVE_MEMSET_S) check_symbol_exists(readpassphrase readpassphrase.h HAVE_READPASSPHRASE) check_symbol_exists(recallocarray stdlib.h HAVE_RECALLOCARRAY) check_symbol_exists(strlcat string.h HAVE_STRLCAT) check_symbol_exists(strlcpy string.h HAVE_STRLCPY) check_symbol_exists(strsep string.h HAVE_STRSEP) check_symbol_exists(sysconf unistd.h HAVE_SYSCONF) check_symbol_exists(timespecsub sys/time.h HAVE_TIMESPECSUB) check_symbol_exists(timingsafe_bcmp string.h HAVE_TIMINGSAFE_BCMP) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) try_compile(HAVE_POSIX_IOCTL "${CMAKE_CURRENT_BINARY_DIR}/posix_ioctl_check.o" "${CMAKE_CURRENT_SOURCE_DIR}/openbsd-compat/posix_ioctl_check.c" COMPILE_DEFINITIONS "-Werror -Woverflow -Wsign-conversion") list(APPEND CHECK_VARIABLES HAVE_ARC4RANDOM_BUF HAVE_CBOR_H HAVE_CLOCK_GETTIME HAVE_ENDIAN_H HAVE_ERR_H HAVE_FREEZERO HAVE_GETLINE HAVE_GETOPT HAVE_GETPAGESIZE HAVE_GETRANDOM HAVE_MEMSET_S HAVE_OPENSSLV_H HAVE_POSIX_IOCTL HAVE_READPASSPHRASE HAVE_RECALLOCARRAY HAVE_SIGNAL_H HAVE_STRLCAT HAVE_STRLCPY HAVE_STRSEP HAVE_SYSCONF HAVE_SYS_RANDOM_H HAVE_TIMESPECSUB HAVE_TIMINGSAFE_BCMP HAVE_UNISTD_H ) foreach(v ${CHECK_VARIABLES}) if (${v}) add_definitions(-D${v}) endif() endforeach() if(HAVE_EXPLICIT_BZERO AND NOT LIBFUZZER) add_definitions(-DHAVE_EXPLICIT_BZERO) endif() if(UNIX) add_definitions(-DHAVE_DEV_URANDOM) endif() if(MSVC) if((NOT CBOR_INCLUDE_DIRS) OR (NOT CBOR_LIBRARY_DIRS) OR (NOT CBOR_BIN_DIRS) OR (NOT CRYPTO_INCLUDE_DIRS) OR (NOT CRYPTO_LIBRARY_DIRS) OR (NOT CRYPTO_BIN_DIRS) OR (NOT ZLIB_INCLUDE_DIRS) OR (NOT ZLIB_LIBRARY_DIRS) OR (NOT ZLIB_BIN_DIRS)) message(FATAL_ERROR "please define " "{CBOR,CRYPTO,ZLIB}_{INCLUDE,LIBRARY,BIN}_DIRS when " "building under msvc") endif() set(CBOR_LIBRARIES cbor) set(ZLIB_LIBRARIES zlib) set(CRYPTO_LIBRARIES crypto-47) set(MSVC_DISABLED_WARNINGS_LIST "C4152" # nonstandard extension used: function/data pointer # conversion in expression; "C4200" # nonstandard extension used: zero-sized array in # struct/union; "C4201" # nonstandard extension used: nameless struct/union; "C4204" # nonstandard extension used: non-constant aggregate # initializer; "C4706" # assignment within conditional expression; "C4996" # The POSIX name for this item is deprecated. Instead, # use the ISO C and C++ conformant name; "C6287" # redundant code: the left and right subexpressions are identical ) # The construction in the following 3 lines was taken from LibreSSL's # CMakeLists.txt. string(REPLACE "C" " -wd" MSVC_DISABLED_WARNINGS_STR ${MSVC_DISABLED_WARNINGS_LIST}) string(REGEX REPLACE "[/-]W[1234][ ]?" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MP -W4 -WX ${MSVC_DISABLED_WARNINGS_STR}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Z7 /guard:cf /sdl /RTCcsu") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi /guard:cf /sdl") if(USE_WINHELLO) add_definitions(-DUSE_WINHELLO) endif() set(NFC_LINUX OFF) else() include(FindPkgConfig) pkg_search_module(CBOR libcbor) pkg_search_module(CRYPTO libcrypto) pkg_search_module(ZLIB zlib) if(NOT CBOR_FOUND AND NOT HAVE_CBOR_H) message(FATAL_ERROR "could not find libcbor") endif() if(NOT CRYPTO_FOUND AND NOT HAVE_OPENSSLV_H) message(FATAL_ERROR "could not find libcrypto") endif() if(NOT ZLIB_FOUND) message(FATAL_ERROR "could not find zlib") endif() set(CBOR_LIBRARIES "cbor") set(CRYPTO_LIBRARIES "crypto") if(CMAKE_SYSTEM_NAME STREQUAL "Linux") pkg_search_module(UDEV libudev REQUIRED) set(UDEV_NAME "udev") # If using hidapi, use hidapi-hidraw. set(HIDAPI_SUFFIX -hidraw) if(NOT HAVE_CLOCK_GETTIME) # Look for clock_gettime in librt. check_library_exists(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME) if (HAVE_CLOCK_GETTIME) add_definitions(-DHAVE_CLOCK_GETTIME) set(BASE_LIBRARIES ${BASE_LIBRARIES} rt) endif() endif() else() set(NFC_LINUX OFF) endif() if(MINGW) # MinGW is stuck with a flavour of C89. add_definitions(-DFIDO_NO_DIAGNOSTIC) add_definitions(-DWC_ERR_INVALID_CHARS=0x80) add_compile_options(-Wno-unused-parameter) endif() if(USE_HIDAPI) add_definitions(-DUSE_HIDAPI) pkg_search_module(HIDAPI hidapi${HIDAPI_SUFFIX} REQUIRED) set(HIDAPI_LIBRARIES hidapi${HIDAPI_SUFFIX}) endif() if(NFC_LINUX) add_definitions(-DNFC_LINUX) endif() if(WIN32) if(USE_WINHELLO) add_definitions(-DUSE_WINHELLO) endif() else() set(USE_WINHELLO OFF) endif() add_compile_options(-Wall) add_compile_options(-Wextra) add_compile_options(-Werror) add_compile_options(-Wshadow) add_compile_options(-Wcast-qual) add_compile_options(-Wwrite-strings) add_compile_options(-Wmissing-prototypes) add_compile_options(-Wbad-function-cast) add_compile_options(-pedantic) add_compile_options(-pedantic-errors) if(WIN32) add_compile_options(-Wno-type-limits) add_compile_options(-Wno-cast-function-type) endif() if(HAVE_SHORTEN_64_TO_32) add_compile_options(-Wshorten-64-to-32) endif() if(HAVE_STACK_PROTECTOR_ALL) add_compile_options(-fstack-protector-all) endif() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g2") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2") if(CRYPTO_VERSION VERSION_GREATER_EQUAL 3.0) add_definitions(-DOPENSSL_API_COMPAT=0x10100000L) endif() if(FUZZ) add_definitions(-DFIDO_FUZZ) endif() if(LIBFUZZER) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=fuzzer-no-link") endif() endif() # Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 if(CMAKE_COMPILER_IS_GNUCC) add_compile_options(-Wno-unused-result) endif() # Decide which keyword to use for thread-local storage. if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang") set(TLS "__thread") elseif(WIN32) set(TLS "__declspec(thread)") endif() add_definitions(-DTLS=${TLS}) # export list if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang")) # clang + lld string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} " -exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/src/export.llvm") elseif(NOT MSVC) # clang/gcc + gnu ld if(FUZZ) string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/export.gnu") else() string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/export.gnu") endif() if(NOT WIN32) string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} " -Wl,-z,noexecstack -Wl,-z,relro,-z,now") string(CONCAT CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " -Wl,-z,noexecstack -Wl,-z,relro,-z,now") if(FUZZ) file(STRINGS fuzz/wrapped.sym WRAPPED_SYMBOLS) foreach(s ${WRAPPED_SYMBOLS}) string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} " -Wl,--wrap=${s}") endforeach() endif() endif() else() string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} " /def:\"${CMAKE_CURRENT_SOURCE_DIR}/src/export.msvc\"") endif() include_directories(${CMAKE_SOURCE_DIR}/src) include_directories(${CBOR_INCLUDE_DIRS}) include_directories(${CRYPTO_INCLUDE_DIRS}) include_directories(${HIDAPI_INCLUDE_DIRS}) include_directories(${UDEV_INCLUDE_DIRS}) include_directories(${ZLIB_INCLUDE_DIRS}) link_directories(${CBOR_LIBRARY_DIRS}) link_directories(${CRYPTO_LIBRARY_DIRS}) link_directories(${HIDAPI_LIBRARY_DIRS}) link_directories(${UDEV_LIBRARY_DIRS}) link_directories(${ZLIB_LIBRARY_DIRS}) message(STATUS "BASE_LIBRARIES: ${BASE_LIBRARIES}") message(STATUS "BUILD_EXAMPLES: ${BUILD_EXAMPLES}") message(STATUS "BUILD_MANPAGES: ${BUILD_MANPAGES}") message(STATUS "BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}") message(STATUS "BUILD_STATIC_LIBS: ${BUILD_STATIC_LIBS}") message(STATUS "BUILD_TOOLS: ${BUILD_TOOLS}") message(STATUS "CBOR_INCLUDE_DIRS: ${CBOR_INCLUDE_DIRS}") message(STATUS "CBOR_LIBRARIES: ${CBOR_LIBRARIES}") message(STATUS "CBOR_LIBRARY_DIRS: ${CBOR_LIBRARY_DIRS}") message(STATUS "CBOR_VERSION: ${CBOR_VERSION}") message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}") message(STATUS "CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}") message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}") message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") message(STATUS "CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}") message(STATUS "CRYPTO_INCLUDE_DIRS: ${CRYPTO_INCLUDE_DIRS}") message(STATUS "CRYPTO_LIBRARIES: ${CRYPTO_LIBRARIES}") message(STATUS "CRYPTO_LIBRARY_DIRS: ${CRYPTO_LIBRARY_DIRS}") message(STATUS "CRYPTO_VERSION: ${CRYPTO_VERSION}") message(STATUS "FIDO_VERSION: ${FIDO_VERSION}") message(STATUS "FUZZ: ${FUZZ}") message(STATUS "ZLIB_INCLUDE_DIRS: ${ZLIB_INCLUDE_DIRS}") message(STATUS "ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}") message(STATUS "ZLIB_LIBRARY_DIRS: ${ZLIB_LIBRARY_DIRS}") message(STATUS "ZLIB_VERSION: ${ZLIB_VERSION}") if(USE_HIDAPI) message(STATUS "HIDAPI_INCLUDE_DIRS: ${HIDAPI_INCLUDE_DIRS}") message(STATUS "HIDAPI_LIBRARIES: ${HIDAPI_LIBRARIES}") message(STATUS "HIDAPI_LIBRARY_DIRS: ${HIDAPI_LIBRARY_DIRS}") message(STATUS "HIDAPI_VERSION: ${HIDAPI_VERSION}") endif() message(STATUS "LIBFUZZER: ${LIBFUZZER}") message(STATUS "TLS: ${TLS}") message(STATUS "UDEV_INCLUDE_DIRS: ${UDEV_INCLUDE_DIRS}") message(STATUS "UDEV_LIBRARIES: ${UDEV_LIBRARIES}") message(STATUS "UDEV_LIBRARY_DIRS: ${UDEV_LIBRARY_DIRS}") message(STATUS "UDEV_RULES_DIR: ${UDEV_RULES_DIR}") message(STATUS "UDEV_VERSION: ${UDEV_VERSION}") message(STATUS "USE_HIDAPI: ${USE_HIDAPI}") message(STATUS "USE_WINHELLO: ${USE_WINHELLO}") message(STATUS "NFC_LINUX: ${NFC_LINUX}") subdirs(src) if(BUILD_EXAMPLES) subdirs(examples) endif() if(BUILD_TOOLS) subdirs(tools) endif() if(BUILD_MANPAGES) subdirs(man) endif() if(NOT WIN32) if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT FUZZ) enable_testing() subdirs(regress) endif() if(FUZZ) subdirs(fuzz) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") subdirs(udev) endif() endif() libfido2-1.10.0/LICENSE000066400000000000000000000024521417126203300142720ustar00rootroot00000000000000Copyright (c) 2018-2022 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. libfido2-1.10.0/NEWS000066400000000000000000000167411417126203300137720ustar00rootroot00000000000000* Version 1.10.0 (2022-01-17) ** hid_osx: handle devices with paths > 511 bytes; gh#462. ** bio: fix CTAP2 canonical CBOR encoding in fido_bio_dev_enroll_*(); gh#480. ** winhello: fallback to GetTopWindow() if GetForegroundWindow() fails. ** winhello: fallback to hid_win.c if webauthn.dll isn't available. ** New API calls: - fido_dev_info_set; - fido_dev_io_handle; - fido_dev_new_with_info; - fido_dev_open_with_info. ** Cygwin and NetBSD build fixes. ** Documentation and reliability fixes. ** Support for TPM 2.0 attestation of COSE_ES256 credentials. * Version 1.9.0 (2021-10-27) ** Enabled NFC support on Linux. ** Added OpenSSL 3.0 compatibility. ** Removed OpenSSL 1.0 compatibility. ** Support for FIDO 2.1 "minPinLength" extension. ** Support for COSE_EDDSA, COSE_ES256, and COSE_RS1 attestation. ** Support for TPM 2.0 attestation. ** Support for device timeouts; see fido_dev_set_timeout(). ** New API calls: - es256_pk_from_EVP_PKEY; - fido_cred_attstmt_len; - fido_cred_attstmt_ptr; - fido_cred_pin_minlen; - fido_cred_set_attstmt; - fido_cred_set_pin_minlen; - fido_dev_set_pin_minlen_rpid; - fido_dev_set_timeout; - rs256_pk_from_EVP_PKEY. ** Reliability and portability fixes. ** Better handling of HID devices without identification strings; gh#381. ** Fixed detection of Windows's native webauthn API; gh#382. * Version 1.8.0 (2021-07-22) ** Dropped 'Requires.private' entry from pkg-config file. ** Better support for FIDO 2.1 authenticators. ** Support for Windows's native webauthn API. ** Support for attestation format 'none'. ** New API calls: - fido_assert_set_clientdata; - fido_cbor_info_algorithm_cose; - fido_cbor_info_algorithm_count; - fido_cbor_info_algorithm_type; - fido_cbor_info_transports_len; - fido_cbor_info_transports_ptr; - fido_cred_set_clientdata; - fido_cred_set_id; - fido_credman_set_dev_rk; - fido_dev_is_winhello. ** fido2-token: new -Sc option to update a resident credential. ** Documentation and reliability fixes. ** HID access serialisation on Linux. * Version 1.7.0 (2021-03-29) ** New dependency on zlib. ** Fixed musl build; gh#259. ** hid_win: detect devices with vendor or product IDs > 0x7fff; gh#264. ** Support for FIDO 2.1 authenticator configuration. ** Support for FIDO 2.1 UV token permissions. ** Support for FIDO 2.1 "credBlobs" and "largeBlobs" extensions. ** New API calls: - fido_assert_blob_len; - fido_assert_blob_ptr; - fido_assert_largeblob_key_len; - fido_assert_largeblob_key_ptr; - fido_assert_set_hmac_secret; - fido_cbor_info_maxcredbloblen; - fido_cred_largeblob_key_len; - fido_cred_largeblob_key_ptr; - fido_cred_set_blob; - fido_dev_enable_entattest; - fido_dev_force_pin_change; - fido_dev_has_uv; - fido_dev_largeblob_get; - fido_dev_largeblob_get_array; - fido_dev_largeblob_remove; - fido_dev_largeblob_set; - fido_dev_largeblob_set_array; - fido_dev_set_pin_minlen; - fido_dev_set_sigmask; - fido_dev_supports_credman; - fido_dev_supports_permissions; - fido_dev_supports_uv; - fido_dev_toggle_always_uv. ** New fido_init flag to disable fido_dev_open's U2F fallback; gh#282. ** Experimental NFC support on Linux; enable with -DNFC_LINUX. * Version 1.6.0 (2020-12-22) ** Fix OpenSSL 1.0 and Cygwin builds. ** hid_linux: fix build on 32-bit systems. ** hid_osx: allow reads from spawned threads. ** Documentation and reliability fixes. ** New API calls: - fido_cred_authdata_raw_len; - fido_cred_authdata_raw_ptr; - fido_cred_sigcount; - fido_dev_get_uv_retry_count; - fido_dev_supports_credman. ** Hardened Windows build. ** Native FreeBSD and NetBSD support. ** Use CTAP2 canonical CBOR when combining hmac-secret and credProtect. * Version 1.5.0 (2020-09-01) ** hid_linux: return FIDO_OK if no devices are found. ** hid_osx: - repair communication with U2F tokens, gh#166; - reliability fixes. ** fido2-{assert,cred}: new options to explicitly toggle UP, UV. ** Support for configurable report lengths. ** New API calls: - fido_cbor_info_maxcredcntlst; - fido_cbor_info_maxcredidlen; - fido_cred_aaguid_len; - fido_cred_aaguid_ptr; - fido_dev_get_touch_begin; - fido_dev_get_touch_status. ** Use COSE_ECDH_ES256 with CTAP_CBOR_CLIENT_PIN; gh#154. ** Allow CTAP messages up to 2048 bytes; gh#171. ** Ensure we only list USB devices by default. * Version 1.4.0 (2020-04-15) ** hid_hidapi: hidapi backend; enable with -DUSE_HIDAPI=1. ** Fall back to U2F if the key claims to, but does not support FIDO2. ** FIDO2 credential protection (credprot) support. ** New API calls: - fido_cbor_info_fwversion; - fido_cred_prot; - fido_cred_set_prot; - fido_dev_set_transport_functions; - fido_set_log_handler. ** Support for FreeBSD. ** Support for C++. ** Support for MSYS. ** Fixed EdDSA and RSA self-attestation. * Version 1.3.1 (2020-02-19) ** fix zero-ing of le1 and le2 when talking to a U2F device. ** dropping sk-libfido2 middleware, please find it in the openssh tree. * Version 1.3.0 (2019-11-28) ** assert/hmac: encode public key as per spec, gh#60. ** fido2-cred: fix creation of resident keys. ** fido2-{assert,cred}: support for hmac-secret extension. ** hid_osx: detect device removal, gh#56. ** hid_osx: fix device detection in MacOS Catalina. ** New API calls: - fido_assert_set_authdata_raw; - fido_assert_sigcount; - fido_cred_set_authdata_raw; - fido_dev_cancel. ** Middleware library for use by OpenSSH. ** Support for biometric enrollment. ** Support for OpenBSD. ** Support for self-attestation. * Version 1.2.0 (released 2019-07-26) ** Credential management support. ** New API reflecting FIDO's 3-state booleans (true, false, absent): - fido_assert_set_up; - fido_assert_set_uv; - fido_cred_set_rk; - fido_cred_set_uv. ** Command-line tools for Windows. ** Documentation and reliability fixes. ** fido_{assert,cred}_set_options() are now marked as deprecated. * Version 1.1.0 (released 2019-05-08) ** MacOS: fix IOKit crash on HID read. ** Windows: fix contents of release file. ** EdDSA (Ed25519) support. ** fido_dev_make_cred: fix order of CBOR map keys. ** fido_dev_get_assert: plug memory leak when operating on U2F devices. * Version 1.0.0 (released 2019-03-21) ** Native HID support on Linux, MacOS, and Windows. ** fido2-{assert,cred}: new -u option to force U2F on dual authenticators. ** fido2-assert: support for multiple resident keys with the same RP. ** Strict checks for CTAP2 compliance on received CBOR payloads. ** Better fuzzing harnesses. ** Documentation and reliability fixes. * Version 0.4.0 (released 2019-01-07) ** fido2-assert: print the user id for resident credentials. ** Fix encoding of COSE algorithms when making a credential. ** Rework purpose of fido_cred_set_type; no ABI change. ** Minor documentation and code fixes. * Version 0.3.0 (released 2018-09-11) ** Various reliability fixes. ** Merged fuzzing instrumentation. ** Added regress tests. ** Added support for FIDO 2's hmac-secret extension. ** New API calls: - fido_assert_hmac_secret_len; - fido_assert_hmac_secret_ptr; - fido_assert_set_extensions; - fido_assert_set_hmac_salt; - fido_cred_set_extensions; - fido_dev_force_fido2. ** Support for native builds with Microsoft Visual Studio 17. * Version 0.2.0 (released 2018-06-20) ** Added command-line tools. ** Added a couple of missing get functions. * Version 0.1.1 (released 2018-06-05) ** Added documentation. ** Added OpenSSL 1.0 support. ** Minor fixes. * Version 0.1.0 (released 2018-05-18) ** First beta release. libfido2-1.10.0/README.adoc000066400000000000000000000066211417126203300150540ustar00rootroot00000000000000== libfido2 image:https://github.com/yubico/libfido2/workflows/linux/badge.svg["Linux Build Status (github actions)", link="https://github.com/Yubico/libfido2/actions"] image:https://github.com/yubico/libfido2/workflows/macos/badge.svg["macOS Build Status (github actions)", link="https://github.com/Yubico/libfido2/actions"] image:https://github.com/yubico/libfido2/workflows/windows/badge.svg["Windows Build Status (github actions)", link="https://github.com/Yubico/libfido2/actions"] image:https://github.com/yubico/libfido2/workflows/fuzzer/badge.svg["Fuzz Status (github actions)", link="https://github.com/Yubico/libfido2/actions"] image:https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfido2.svg["Fuzz Status (oss-fuzz)", link="https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:libfido2"] *libfido2* provides library functionality and command-line tools to communicate with a FIDO device over USB, and to verify attestation and assertion signatures. *libfido2* supports the FIDO U2F (CTAP 1) and FIDO2 (CTAP 2) protocols. For usage, see the `examples/` directory. === License *libfido2* is licensed under the BSD 2-clause license. See the LICENSE file for the full license text. === Supported Platforms *libfido2* is known to work on Linux, macOS, Windows, OpenBSD, and FreeBSD. NFC support is available on Linux and Windows. === Documentation Documentation is available in troff and HTML formats. An https://developers.yubico.com/libfido2/Manuals/[online mirror of *libfido2*'s documentation] is also available. === Bindings * .NET: https://github.com/borrrden/Fido2Net[Fido2Net] * Go: https://github.com/keys-pub/go-libfido2[go-libfido2] * Perl: https://github.com/jacquesg/p5-FIDO-Raw[p5-FIDO-Raw] * Rust: https://github.com/PvdBerg1998/libfido2[libfido2] === Installation ==== Releases The current release of *libfido2* is 1.10.0. Please consult Yubico's https://developers.yubico.com/libfido2/Releases[release page] for source and binary releases. ==== Ubuntu 20.04 (Focal) $ sudo apt install libfido2-1 $ sudo apt install libfido2-dev $ sudo apt install libfido2-doc Alternatively, newer versions of *libfido2* are available in Yubico's PPA. Follow the instructions for Ubuntu 18.04 (Bionic) below. ==== Ubuntu 18.04 (Bionic) $ sudo apt install software-properties-common $ sudo apt-add-repository ppa:yubico/stable $ sudo apt update $ sudo apt install libfido2-dev ==== macOS $ brew install libfido2 Or from source, on UNIX-like systems: $ cmake -B build $ make -C build $ sudo make -C build install Depending on the platform, https://www.freedesktop.org/wiki/Software/pkg-config/[pkg-config] may need to be installed, or the PKG_CONFIG_PATH environment variable set. *libfido2* depends on https://github.com/pjk/libcbor[libcbor], https://www.openssl.org[OpenSSL] 1.1 or newer, and https://zlib.net[zlib]. On Linux, libudev (part of https://www.freedesktop.org/wiki/Software/systemd[systemd]) is also required. For complete, OS-specific installation instructions, please refer to the `.actions/` (Linux, macOS) and `windows/` directories. On Linux, you will need to add a udev rule to be able to access the FIDO device, or run as root. For example, the udev rule may contain the following: ---- #udev rule for allowing HID access to Yubico devices for FIDO support. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", \ MODE="0664", GROUP="plugdev", ATTRS{idVendor}=="1050" ---- libfido2-1.10.0/SECURITY.md000066400000000000000000000003331417126203300150520ustar00rootroot00000000000000# Reporting libfido2 Security Issues To report security issues in libfido2, please contact security@yubico.com. A PGP public key can be found at https://www.yubico.com/support/security-advisories/issue-rating-system/. libfido2-1.10.0/examples/000077500000000000000000000000001417126203300151005ustar00rootroot00000000000000libfido2-1.10.0/examples/CMakeLists.txt000066400000000000000000000035631417126203300176470ustar00rootroot00000000000000# Copyright (c) 2018 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. list(APPEND COMPAT_SOURCES ../openbsd-compat/clock_gettime.c ../openbsd-compat/getopt_long.c ../openbsd-compat/strlcat.c ../openbsd-compat/strlcpy.c ) if(WIN32 AND BUILD_SHARED_LIBS AND NOT CYGWIN AND NOT MSYS) list(APPEND COMPAT_SOURCES ../openbsd-compat/posix_win.c) endif() # set the library to link against if(BUILD_STATIC_LIBS) # drop -rdynamic set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set(_FIDO2_LIBRARY fido2) elseif(BUILD_SHARED_LIBS) set(_FIDO2_LIBRARY fido2_shared) else() set(_FIDO2_LIBRARY ${CRYPTO_LIBRARIES} fido2) endif() # enable -Wconversion -Wsign-conversion if(NOT MSVC) set_source_files_properties(assert.c cred.c info.c manifest.c reset.c retries.c setpin.c util.c PROPERTIES COMPILE_FLAGS "-Wconversion -Wsign-conversion") endif() # manifest add_executable(manifest manifest.c ${COMPAT_SOURCES}) target_link_libraries(manifest ${_FIDO2_LIBRARY}) # info add_executable(info info.c ${COMPAT_SOURCES}) target_link_libraries(info ${_FIDO2_LIBRARY}) # reset add_executable(reset reset.c util.c ${COMPAT_SOURCES}) target_link_libraries(reset ${_FIDO2_LIBRARY}) # cred add_executable(cred cred.c util.c ${COMPAT_SOURCES}) target_link_libraries(cred ${_FIDO2_LIBRARY}) # assert add_executable(assert assert.c util.c ${COMPAT_SOURCES}) target_link_libraries(assert ${_FIDO2_LIBRARY}) # setpin add_executable(setpin setpin.c ${COMPAT_SOURCES}) target_link_libraries(setpin ${_FIDO2_LIBRARY}) # retries add_executable(retries retries.c ${COMPAT_SOURCES}) target_link_libraries(retries ${_FIDO2_LIBRARY}) # select add_executable(select select.c ${COMPAT_SOURCES}) target_link_libraries(select ${_FIDO2_LIBRARY}) if(MINGW) # needed for nanosleep() in mingw target_link_libraries(select winpthread) endif() libfido2-1.10.0/examples/README.adoc000066400000000000000000000064631417126203300166760ustar00rootroot00000000000000= 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. - A credential's associated CTAP 2.1 "largeBlob" symmetric key. === 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] [-b blobkey] [-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 . If the option -b is specified, the credential's "largeBlob" key is stored in . - assert [-t ecdsa|rsa|eddsa] [-a cred_id] [-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] [-b blobkey] [-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 . If the option -b specified, the credential's "largeBlob" key is stored in . - retries Get the number of PIN attempts left on before lockout. - select Enumerates available FIDO devices and, if more than one is present, simultaneously requests touch on all of them, printing information about the device touched. Debugging is possible through the use of the FIDO_DEBUG environment variable. If set, libfido2 will produce a log of its transactions with the authenticator. Additionally, an example of a WebAuthn client using libfido2 is available at https://github.com/martelletto/fido2-webauthn-client. libfido2-1.10.0/examples/assert.c000066400000000000000000000200451417126203300165460ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "../openbsd-compat/openbsd-compat.h" #include "extern.h" static const unsigned char cd[32] = { 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7, 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56, 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52, 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76, }; static void usage(void) { fprintf(stderr, "usage: assert [-t ecdsa|rsa|eddsa] [-a cred_id] " "[-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] " "[-b blobkey] [-puv] \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(assert, cd, sizeof(cd)); if (r != FIDO_OK) errx(1, "fido_assert_set_clientdata: %s (0x%x)", fido_strerr(r), r); /* relying party */ r = fido_assert_set_rp(assert, "localhost"); if (r != FIDO_OK) errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r); /* authdata */ r = fido_assert_set_count(assert, 1); if (r != FIDO_OK) errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r); r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len); if (r != FIDO_OK) errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r); /* extension */ r = fido_assert_set_extensions(assert, ext); if (r != FIDO_OK) errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r), r); /* user presence */ if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK) errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r); /* user verification */ if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r); /* sig */ r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len); if (r != FIDO_OK) errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r); r = fido_assert_verify(assert, 0, type, pk); if (r != FIDO_OK) errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r); es256_pk_free(&es256_pk); rs256_pk_free(&rs256_pk); eddsa_pk_free(&eddsa_pk); fido_assert_free(&assert); } int main(int argc, char **argv) { bool up = false; bool uv = false; bool u2f = false; fido_dev_t *dev = NULL; fido_assert_t *assert = NULL; const char *pin = NULL; const char *blobkey_out = NULL; const char *hmac_out = NULL; unsigned char *body = NULL; long long ms = 0; size_t len; int type = COSE_ES256; int ext = 0; int ch; int r; if ((assert = fido_assert_new()) == NULL) errx(1, "fido_assert_new"); while ((ch = getopt(argc, argv, "P:T:a:b:h:ps:t:uv")) != -1) { switch (ch) { case 'P': pin = optarg; break; case 'T': if (base10(optarg, &ms) < 0) errx(1, "base10: %s", optarg); if (ms <= 0 || ms > 30) errx(1, "-T: %s must be in (0,30]", optarg); ms *= 1000; /* seconds to milliseconds */ 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 'b': ext |= FIDO_EXT_LARGEBLOB_KEY; blobkey_out = optarg; break; case 'h': hmac_out = optarg; break; case 'p': up = true; break; case 's': ext |= FIDO_EXT_HMAC_SECRET; if (read_blob(optarg, &body, &len) < 0) errx(1, "read_blob: %s", optarg); if ((r = fido_assert_set_hmac_salt(assert, body, len)) != FIDO_OK) errx(1, "fido_assert_set_hmac_salt: %s (0x%x)", fido_strerr(r), r); free(body); body = NULL; break; case 't': if (strcmp(optarg, "ecdsa") == 0) type = COSE_ES256; else if (strcmp(optarg, "rsa") == 0) type = COSE_RS256; else if (strcmp(optarg, "eddsa") == 0) type = COSE_EDDSA; else errx(1, "unknown type %s", optarg); break; case 'u': u2f = true; break; case 'v': uv = true; break; default: usage(); } } argc -= optind; argv += optind; if (argc != 2) usage(); fido_init(0); if ((dev = fido_dev_new()) == NULL) errx(1, "fido_dev_new"); r = fido_dev_open(dev, argv[1]); if (r != FIDO_OK) errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); if (u2f) fido_dev_force_u2f(dev); /* client data hash */ r = fido_assert_set_clientdata(assert, cd, sizeof(cd)); if (r != FIDO_OK) errx(1, "fido_assert_set_clientdata: %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); /* timeout */ if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK) errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r); if ((r = fido_dev_get_assert(dev, assert, pin)) != FIDO_OK) { fido_dev_cancel(dev); errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r); } r = fido_dev_close(dev); if (r != FIDO_OK) errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); fido_dev_free(&dev); if (fido_assert_count(assert) != 1) errx(1, "fido_assert_count: %d signatures returned", (int)fido_assert_count(assert)); /* when verifying, pin implies uv */ if (pin) uv = true; verify_assert(type, fido_assert_authdata_ptr(assert, 0), fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0), fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]); if (hmac_out != NULL) { /* extract the hmac secret */ if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0), fido_assert_hmac_secret_len(assert, 0)) < 0) errx(1, "write_blob"); } if (blobkey_out != NULL) { /* extract the hmac secret */ if (write_blob(blobkey_out, fido_assert_largeblob_key_ptr(assert, 0), fido_assert_largeblob_key_len(assert, 0)) < 0) errx(1, "write_blob"); } fido_assert_free(&assert); exit(0); } libfido2-1.10.0/examples/cred.c000066400000000000000000000171771417126203300161760ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "../openbsd-compat/openbsd-compat.h" #include "extern.h" static const unsigned char cd[32] = { 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb, 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26, 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31, 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b, }; static const unsigned char user_id[32] = { 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63, 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2, 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5, 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49, }; static void usage(void) { fprintf(stderr, "usage: cred [-t ecdsa|rsa|eddsa] [-k pubkey] " "[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-hruv] " "\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 *attstmt_ptr, size_t attstmt_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 */ r = fido_cred_set_clientdata(cred, cd, sizeof(cd)); if (r != FIDO_OK) errx(1, "fido_cred_set_clientdata: %s (0x%x)", fido_strerr(r), r); /* relying party */ r = fido_cred_set_rp(cred, "localhost", "sweet home localhost"); if (r != FIDO_OK) errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r); /* authdata */ r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len); if (r != FIDO_OK) errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r); /* extensions */ r = fido_cred_set_extensions(cred, ext); if (r != FIDO_OK) errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r); /* resident key */ if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK) errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r); /* user verification */ if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r); /* fmt */ r = fido_cred_set_fmt(cred, fmt); if (r != FIDO_OK) errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r); if (!strcmp(fido_cred_fmt(cred), "none")) { warnx("no attestation data, skipping credential verification"); goto out; } /* attestation statement */ r = fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len); if (r != FIDO_OK) errx(1, "fido_cred_set_attstmt: %s (0x%x)", fido_strerr(r), r); r = fido_cred_verify(cred); if (r != FIDO_OK) errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r); out: if (key_out != NULL) { /* extract the credential pubkey */ if (type == COSE_ES256) { if (write_ec_pubkey(key_out, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)) < 0) errx(1, "write_ec_pubkey"); } else if (type == COSE_RS256) { if (write_rsa_pubkey(key_out, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)) < 0) errx(1, "write_rsa_pubkey"); } else if (type == COSE_EDDSA) { if (write_eddsa_pubkey(key_out, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)) < 0) errx(1, "write_eddsa_pubkey"); } } if (id_out != NULL) { /* extract the credential id */ if (write_blob(id_out, fido_cred_id_ptr(cred), fido_cred_id_len(cred)) < 0) errx(1, "write_blob"); } fido_cred_free(&cred); } int main(int argc, char **argv) { bool rk = false; bool uv = false; bool u2f = false; fido_dev_t *dev; fido_cred_t *cred = NULL; const char *pin = NULL; const char *blobkey_out = NULL; const char *key_out = NULL; const char *id_out = NULL; unsigned char *body = NULL; long long ms = 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:b:e:hi:k:rt:uv")) != -1) { switch (ch) { case 'P': pin = optarg; break; case 'T': if (base10(optarg, &ms) < 0) errx(1, "base10: %s", optarg); if (ms <= 0 || ms > 30) errx(1, "-T: %s must be in (0,30]", optarg); ms *= 1000; /* seconds to milliseconds */ break; case 'b': ext |= FIDO_EXT_LARGEBLOB_KEY; blobkey_out = optarg; break; case 'e': if (read_blob(optarg, &body, &len) < 0) errx(1, "read_blob: %s", optarg); r = fido_cred_exclude(cred, body, len); if (r != FIDO_OK) errx(1, "fido_cred_exclude: %s (0x%x)", fido_strerr(r), r); free(body); body = NULL; break; case 'h': ext |= FIDO_EXT_HMAC_SECRET; break; case 'i': id_out = optarg; break; case 'k': key_out = optarg; break; case 'r': rk = true; break; case 't': if (strcmp(optarg, "ecdsa") == 0) type = COSE_ES256; else if (strcmp(optarg, "rsa") == 0) type = COSE_RS256; else if (strcmp(optarg, "eddsa") == 0) type = COSE_EDDSA; else errx(1, "unknown type %s", optarg); break; case 'u': u2f = true; break; case 'v': uv = true; break; default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); fido_init(0); if ((dev = fido_dev_new()) == NULL) errx(1, "fido_dev_new"); r = fido_dev_open(dev, argv[0]); if (r != 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 */ r = fido_cred_set_clientdata(cred, cd, sizeof(cd)); if (r != FIDO_OK) errx(1, "fido_cred_set_clientdata: %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); /* timeout */ if (ms != 0 && (r = fido_dev_set_timeout(dev, (int)ms)) != FIDO_OK) errx(1, "fido_dev_set_timeout: %s (0x%x)", fido_strerr(r), r); if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) { fido_dev_cancel(dev); errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r); } r = fido_dev_close(dev); if (r != FIDO_OK) errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); fido_dev_free(&dev); /* when verifying, pin implies uv */ if (pin) uv = true; verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred), fido_cred_authdata_len(cred), fido_cred_attstmt_ptr(cred), fido_cred_attstmt_len(cred), rk, uv, ext, key_out, id_out); if (blobkey_out != NULL) { /* extract the "largeBlob" key */ if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred), fido_cred_largeblob_key_len(cred)) < 0) errx(1, "write_blob"); } fido_cred_free(&cred); exit(0); } libfido2-1.10.0/examples/extern.h000066400000000000000000000014001417126203300165510ustar00rootroot00000000000000/* * 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 /* 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); #endif /* _EXTERN_H_ */ libfido2-1.10.0/examples/info.c000066400000000000000000000144251417126203300162050ustar00rootroot00000000000000/* * 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" /* * Pretty-print a device's capabilities flags and return the result. */ static void format_flags(char *ret, size_t retlen, uint8_t flags) { memset(ret, 0, retlen); if (flags & FIDO_CAP_WINK) { if (strlcat(ret, "wink,", retlen) >= retlen) goto toolong; } else { if (strlcat(ret, "nowink,", retlen) >= retlen) goto toolong; } if (flags & FIDO_CAP_CBOR) { if (strlcat(ret, " cbor,", retlen) >= retlen) goto toolong; } else { if (strlcat(ret, " nocbor,", retlen) >= retlen) goto toolong; } if (flags & FIDO_CAP_NMSG) { if (strlcat(ret, " nomsg", retlen) >= retlen) goto toolong; } else { if (strlcat(ret, " msg", retlen) >= retlen) goto toolong; } return; toolong: strlcpy(ret, "toolong", retlen); } /* * Print a FIDO device's attributes on stdout. */ static void print_attr(const fido_dev_t *dev) { char flags_txt[128]; printf("proto: 0x%02x\n", fido_dev_protocol(dev)); printf("major: 0x%02x\n", fido_dev_major(dev)); printf("minor: 0x%02x\n", fido_dev_minor(dev)); printf("build: 0x%02x\n", fido_dev_build(dev)); format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev)); printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt); } /* * Auxiliary function to print an array of strings on stdout. */ static void print_str_array(const char *label, char * const *sa, size_t len) { if (len == 0) return; printf("%s strings: ", label); for (size_t i = 0; i < len; i++) printf("%s%s", i > 0 ? ", " : "", sa[i]); printf("\n"); } /* * Auxiliary function to print (char *, bool) pairs on stdout. */ static void print_opt_array(const char *label, char * const *name, const bool *value, size_t len) { if (len == 0) return; printf("%s: ", label); for (size_t i = 0; i < len; i++) printf("%s%s%s", i > 0 ? ", " : "", value[i] ? "" : "no", name[i]); printf("\n"); } /* * Auxiliary function to print a list of supported COSE algorithms on stdout. */ static void print_algorithms(const fido_cbor_info_t *ci) { const char *cose, *type; size_t len; if ((len = fido_cbor_info_algorithm_count(ci)) == 0) return; printf("algorithms: "); for (size_t i = 0; i < len; i++) { cose = type = "unknown"; switch (fido_cbor_info_algorithm_cose(ci, i)) { case COSE_EDDSA: cose = "eddsa"; break; case COSE_ES256: cose = "es256"; break; case COSE_RS256: cose = "rs256"; break; } if (fido_cbor_info_algorithm_type(ci, i) != NULL) type = fido_cbor_info_algorithm_type(ci, i); printf("%s%s (%s)", i > 0 ? ", " : "", cose, type); } printf("\n"); } /* * Auxiliary function to print an authenticator's AAGUID on stdout. */ static void print_aaguid(const unsigned char *buf, size_t buflen) { printf("aaguid: "); while (buflen--) printf("%02x", *buf++); printf("\n"); } /* * Auxiliary function to print an authenticator's maximum message size on * stdout. */ static void print_maxmsgsiz(uint64_t maxmsgsiz) { printf("maxmsgsiz: %d\n", (int)maxmsgsiz); } /* * Auxiliary function to print an authenticator's maximum number of credentials * in a credential list on stdout. */ static void print_maxcredcntlst(uint64_t maxcredcntlst) { printf("maxcredcntlst: %d\n", (int)maxcredcntlst); } /* * Auxiliary function to print an authenticator's maximum credential ID length * on stdout. */ static void print_maxcredidlen(uint64_t maxcredidlen) { printf("maxcredlen: %d\n", (int)maxcredidlen); } /* * Auxiliary function to print an authenticator's firmware version on stdout. */ static void print_fwversion(uint64_t fwversion) { printf("fwversion: 0x%x\n", (int)fwversion); } /* * Auxiliary function to print an array of bytes on stdout. */ static void print_byte_array(const char *label, const uint8_t *ba, size_t len) { if (len == 0) return; printf("%s: ", label); for (size_t i = 0; i < len; i++) printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]); printf("\n"); } static void getinfo(const char *path) { fido_dev_t *dev; fido_cbor_info_t *ci; int r; fido_init(0); if ((dev = fido_dev_new()) == NULL) errx(1, "fido_dev_new"); if ((r = fido_dev_open(dev, path)) != FIDO_OK) errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); print_attr(dev); if (fido_dev_is_fido2(dev) == false) goto end; if ((ci = fido_cbor_info_new()) == NULL) errx(1, "fido_cbor_info_new"); if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK) errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r); /* print supported protocol versions */ print_str_array("version", fido_cbor_info_versions_ptr(ci), fido_cbor_info_versions_len(ci)); /* print supported extensions */ print_str_array("extension", fido_cbor_info_extensions_ptr(ci), fido_cbor_info_extensions_len(ci)); /* print supported transports */ print_str_array("transport", fido_cbor_info_transports_ptr(ci), fido_cbor_info_transports_len(ci)); /* print supported algorithms */ print_algorithms(ci); /* print aaguid */ print_aaguid(fido_cbor_info_aaguid_ptr(ci), fido_cbor_info_aaguid_len(ci)); /* print supported options */ print_opt_array("options", fido_cbor_info_options_name_ptr(ci), fido_cbor_info_options_value_ptr(ci), fido_cbor_info_options_len(ci)); /* print maximum message size */ print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci)); /* print maximum number of credentials allowed in credential lists */ print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci)); /* print maximum length of a credential ID */ print_maxcredidlen(fido_cbor_info_maxcredidlen(ci)); /* print firmware version */ print_fwversion(fido_cbor_info_fwversion(ci)); /* print supported pin protocols */ print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci), fido_cbor_info_protocols_len(ci)); fido_cbor_info_free(&ci); end: if ((r = fido_dev_close(dev)) != FIDO_OK) errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); fido_dev_free(&dev); } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: info \n"); exit(EXIT_FAILURE); } getinfo(argv[1]); exit(0); } libfido2-1.10.0/examples/manifest.c000066400000000000000000000017321417126203300170550ustar00rootroot00000000000000/* * 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 "../openbsd-compat/openbsd-compat.h" int main(void) { fido_dev_info_t *devlist; size_t ndevs; int r; fido_init(0); if ((devlist = fido_dev_info_new(64)) == NULL) errx(1, "fido_dev_info_new"); if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK) errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r); for (size_t i = 0; i < ndevs; i++) { const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n", fido_dev_info_path(di), (uint16_t)fido_dev_info_vendor(di), (uint16_t)fido_dev_info_product(di), fido_dev_info_manufacturer_string(di), fido_dev_info_product_string(di)); } fido_dev_info_free(&devlist, ndevs); exit(0); } libfido2-1.10.0/examples/reset.c000066400000000000000000000017131417126203300163700ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ /* * Perform a factory reset on a given authenticator. */ #include #include #include #include "../openbsd-compat/openbsd-compat.h" #include "extern.h" int main(int argc, char **argv) { fido_dev_t *dev; int r; if (argc != 2) { fprintf(stderr, "usage: reset \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); if ((r = fido_dev_reset(dev)) != FIDO_OK) { fido_dev_cancel(dev); errx(1, "fido_dev_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); } libfido2-1.10.0/examples/retries.c000066400000000000000000000016761417126203300167330ustar00rootroot00000000000000/* * 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 "../openbsd-compat/openbsd-compat.h" int main(int argc, char **argv) { fido_dev_t *dev; int n; int r; if (argc != 2) { fprintf(stderr, "usage: retries \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); } libfido2-1.10.0/examples/select.c000066400000000000000000000110131417126203300165170ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "../openbsd-compat/openbsd-compat.h" #define FIDO_POLL_MS 50 #if defined(_MSC_VER) static int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { if (rmtp != NULL) { errno = EINVAL; return (-1); } Sleep(rqtp->tv_nsec / 1000000); return (0); } #endif static fido_dev_t * open_dev(const fido_dev_info_t *di) { fido_dev_t *dev; int r; if ((dev = fido_dev_new()) == NULL) { warnx("%s: fido_dev_new", __func__); return (NULL); } if ((r = fido_dev_open(dev, fido_dev_info_path(di))) != FIDO_OK) { warnx("%s: fido_dev_open %s: %s", __func__, fido_dev_info_path(di), fido_strerr(r)); fido_dev_free(&dev); return (NULL); } printf("%s (0x%04x:0x%04x) is %s\n", fido_dev_info_path(di), fido_dev_info_vendor(di), fido_dev_info_product(di), fido_dev_is_fido2(dev) ? "fido2" : "u2f"); return (dev); } static int select_dev(const fido_dev_info_t *devlist, size_t ndevs, fido_dev_t **dev, size_t *idx, int secs) { const fido_dev_info_t *di; fido_dev_t **devtab; struct timespec ts_start; struct timespec ts_now; struct timespec ts_delta; struct timespec ts_pause; size_t nopen = 0; int touched; int r; long ms_remain; *dev = NULL; *idx = 0; printf("%u authenticator(s) detected\n", (unsigned)ndevs); if (ndevs == 0) return (0); /* nothing to do */ if ((devtab = calloc(ndevs, sizeof(*devtab))) == NULL) { warn("%s: calloc", __func__); return (-1); } for (size_t i = 0; i < ndevs; i++) { di = fido_dev_info_ptr(devlist, i); if ((devtab[i] = open_dev(di)) != NULL) { *idx = i; nopen++; } } printf("%u authenticator(s) opened\n", (unsigned)nopen); if (nopen < 2) { if (nopen == 1) *dev = devtab[*idx]; /* single candidate */ r = 0; goto out; } for (size_t i = 0; i < ndevs; i++) { di = fido_dev_info_ptr(devlist, i); if (devtab[i] == NULL) continue; /* failed to open */ if ((r = fido_dev_get_touch_begin(devtab[i])) != FIDO_OK) { warnx("%s: fido_dev_get_touch_begin %s: %s", __func__, fido_dev_info_path(di), fido_strerr(r)); r = -1; goto out; } } if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) { warn("%s: clock_gettime", __func__); r = -1; goto out; } ts_pause.tv_sec = 0; ts_pause.tv_nsec = 200000000; /* 200ms */ do { nanosleep(&ts_pause, NULL); for (size_t i = 0; i < ndevs; i++) { di = fido_dev_info_ptr(devlist, i); if (devtab[i] == NULL) { /* failed to open or discarded */ continue; } if ((r = fido_dev_get_touch_status(devtab[i], &touched, FIDO_POLL_MS)) != FIDO_OK) { warnx("%s: fido_dev_get_touch_status %s: %s", __func__, fido_dev_info_path(di), fido_strerr(r)); fido_dev_close(devtab[i]); fido_dev_free(&devtab[i]); continue; /* discard */ } if (touched) { *dev = devtab[i]; *idx = i; r = 0; goto out; } } if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) { warn("%s: clock_gettime", __func__); r = -1; goto out; } timespecsub(&ts_now, &ts_start, &ts_delta); ms_remain = (secs * 1000) - ((long)ts_delta.tv_sec * 1000) + ((long)ts_delta.tv_nsec / 1000000); } while (ms_remain > FIDO_POLL_MS); printf("timeout after %d seconds\n", secs); r = -1; out: if (r != 0) { *dev = NULL; *idx = 0; } for (size_t i = 0; i < ndevs; i++) { if (devtab[i] && devtab[i] != *dev) { fido_dev_cancel(devtab[i]); fido_dev_close(devtab[i]); fido_dev_free(&devtab[i]); } } free(devtab); return (r); } int main(void) { const fido_dev_info_t *di; fido_dev_info_t *devlist; fido_dev_t *dev; size_t idx; size_t ndevs; int r; fido_init(0); if ((devlist = fido_dev_info_new(64)) == NULL) errx(1, "fido_dev_info_new"); if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK) errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r); if (select_dev(devlist, ndevs, &dev, &idx, 15) != 0) errx(1, "select_dev"); if (dev == NULL) errx(1, "no authenticator found"); di = fido_dev_info_ptr(devlist, idx); printf("%s: %s by %s (PIN %sset)\n", fido_dev_info_path(di), fido_dev_info_product_string(di), fido_dev_info_manufacturer_string(di), fido_dev_has_pin(dev) ? "" : "un"); fido_dev_close(dev); fido_dev_free(&dev); fido_dev_info_free(&devlist, ndevs); exit(0); } libfido2-1.10.0/examples/setpin.c000066400000000000000000000021331417126203300165450ustar00rootroot00000000000000/* * 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 "../openbsd-compat/openbsd-compat.h" static void setpin(const char *path, const char *pin, const char *oldpin) { fido_dev_t *dev; int r; fido_init(0); if ((dev = fido_dev_new()) == NULL) errx(1, "fido_dev_new"); if ((r = fido_dev_open(dev, path)) != FIDO_OK) errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r); if ((r = fido_dev_set_pin(dev, pin, oldpin)) != FIDO_OK) errx(1, "fido_setpin: %s (0x%x)", fido_strerr(r), r); if ((r = fido_dev_close(dev)) != FIDO_OK) errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r); fido_dev_free(&dev); } int main(int argc, char **argv) { if (argc < 3 || argc > 4) { fprintf(stderr, "usage: setpin [oldpin] \n"); exit(EXIT_FAILURE); } if (argc == 3) setpin(argv[2], argv[1], NULL); else setpin(argv[3], argv[1], argv[2]); exit(0); } libfido2-1.10.0/examples/util.c000066400000000000000000000136741417126203300162340ustar00rootroot00000000000000/* * 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 #ifdef HAVE_UNISTD_H #include #endif #ifdef _MSC_VER #include "../openbsd-compat/posix_win.h" #endif #include "../openbsd-compat/openbsd-compat.h" #include "extern.h" int base10(const char *str, long long *ll) { char *ep; *ll = strtoll(str, &ep, 10); if (str == ep || *ep != '\0') return (-1); else if (*ll == LLONG_MIN && errno == ERANGE) return (-1); else if (*ll == LLONG_MAX && errno == ERANGE) return (-1); return (0); } int write_blob(const char *path, const unsigned char *ptr, size_t len) { int fd, ok = -1; ssize_t n; if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) { warn("open %s", path); goto fail; } if ((n = write(fd, ptr, len)) < 0) { warn("write"); goto fail; } if ((size_t)n != len) { warnx("write"); goto fail; } ok = 0; fail: if (fd != -1) { close(fd); } return (ok); } int read_blob(const char *path, unsigned char **ptr, size_t *len) { int fd, ok = -1; struct stat st; ssize_t n; *ptr = NULL; *len = 0; if ((fd = open(path, O_RDONLY)) < 0) { warn("open %s", path); goto fail; } if (fstat(fd, &st) < 0) { warn("stat %s", path); goto fail; } if (st.st_size < 0) { warnx("stat %s: invalid size", path); goto fail; } *len = (size_t)st.st_size; if ((*ptr = malloc(*len)) == NULL) { warn("malloc"); goto fail; } if ((n = read(fd, *ptr, *len)) < 0) { warn("read"); goto fail; } if ((size_t)n != *len) { warnx("read"); goto fail; } ok = 0; fail: if (fd != -1) { close(fd); } if (ok < 0) { free(*ptr); *ptr = NULL; *len = 0; } return (ok); } EC_KEY * read_ec_pubkey(const char *path) { FILE *fp = NULL; EVP_PKEY *pkey = NULL; EC_KEY *ec = NULL; if ((fp = fopen(path, "r")) == NULL) { warn("fopen"); goto fail; } if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { warnx("PEM_read_PUBKEY"); goto fail; } if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) { warnx("EVP_PKEY_get1_EC_KEY"); goto fail; } fail: if (fp != NULL) { fclose(fp); } if (pkey != NULL) { EVP_PKEY_free(pkey); } return (ec); } int write_ec_pubkey(const char *path, const void *ptr, size_t len) { FILE *fp = NULL; EVP_PKEY *pkey = NULL; es256_pk_t *pk = NULL; int fd = -1; int ok = -1; if ((pk = es256_pk_new()) == NULL) { warnx("es256_pk_new"); goto fail; } if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) { warnx("es256_pk_from_ptr"); goto fail; } if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) { warn("open %s", path); goto fail; } if ((fp = fdopen(fd, "w")) == NULL) { warn("fdopen"); goto fail; } fd = -1; /* owned by fp now */ if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) { warnx("es256_pk_to_EVP_PKEY"); goto fail; } if (PEM_write_PUBKEY(fp, pkey) == 0) { warnx("PEM_write_PUBKEY"); goto fail; } ok = 0; fail: es256_pk_free(&pk); if (fp != NULL) { fclose(fp); } if (fd != -1) { close(fd); } if (pkey != NULL) { EVP_PKEY_free(pkey); } return (ok); } RSA * read_rsa_pubkey(const char *path) { FILE *fp = NULL; EVP_PKEY *pkey = NULL; RSA *rsa = NULL; if ((fp = fopen(path, "r")) == NULL) { warn("fopen"); goto fail; } if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { warnx("PEM_read_PUBKEY"); goto fail; } if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) { warnx("EVP_PKEY_get1_RSA"); goto fail; } fail: if (fp != NULL) { fclose(fp); } if (pkey != NULL) { EVP_PKEY_free(pkey); } return (rsa); } int write_rsa_pubkey(const char *path, const void *ptr, size_t len) { FILE *fp = NULL; EVP_PKEY *pkey = NULL; rs256_pk_t *pk = NULL; int fd = -1; int ok = -1; if ((pk = rs256_pk_new()) == NULL) { warnx("rs256_pk_new"); goto fail; } if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) { warnx("rs256_pk_from_ptr"); goto fail; } if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) { warn("open %s", path); goto fail; } if ((fp = fdopen(fd, "w")) == NULL) { warn("fdopen"); goto fail; } fd = -1; /* owned by fp now */ if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) { warnx("rs256_pk_to_EVP_PKEY"); goto fail; } if (PEM_write_PUBKEY(fp, pkey) == 0) { warnx("PEM_write_PUBKEY"); goto fail; } ok = 0; fail: rs256_pk_free(&pk); if (fp != NULL) { fclose(fp); } if (fd != -1) { close(fd); } if (pkey != NULL) { EVP_PKEY_free(pkey); } return (ok); } EVP_PKEY * read_eddsa_pubkey(const char *path) { FILE *fp = NULL; EVP_PKEY *pkey = NULL; if ((fp = fopen(path, "r")) == NULL) { warn("fopen"); goto fail; } if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { warnx("PEM_read_PUBKEY"); goto fail; } fail: if (fp) { fclose(fp); } return (pkey); } int write_eddsa_pubkey(const char *path, const void *ptr, size_t len) { FILE *fp = NULL; EVP_PKEY *pkey = NULL; eddsa_pk_t *pk = NULL; int fd = -1; int ok = -1; if ((pk = eddsa_pk_new()) == NULL) { warnx("eddsa_pk_new"); goto fail; } if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) { warnx("eddsa_pk_from_ptr"); goto fail; } if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) { warn("open %s", path); goto fail; } if ((fp = fdopen(fd, "w")) == NULL) { warn("fdopen"); goto fail; } fd = -1; /* owned by fp now */ if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) { warnx("eddsa_pk_to_EVP_PKEY"); goto fail; } if (PEM_write_PUBKEY(fp, pkey) == 0) { warnx("PEM_write_PUBKEY"); goto fail; } ok = 0; fail: eddsa_pk_free(&pk); if (fp != NULL) { fclose(fp); } if (fd != -1) { close(fd); } if (pkey != NULL) { EVP_PKEY_free(pkey); } return (ok); } libfido2-1.10.0/fuzz/000077500000000000000000000000001417126203300142605ustar00rootroot00000000000000libfido2-1.10.0/fuzz/CMakeLists.txt000066400000000000000000000047061417126203300170270ustar00rootroot00000000000000# Copyright (c) 2019 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. list(APPEND COMPAT_SOURCES ../openbsd-compat/strlcpy.c ../openbsd-compat/strlcat.c ) list(APPEND COMMON_SOURCES libfuzzer.c mutator_aux.c ) set(FUZZ_LDFLAGS "-fsanitize=fuzzer") # fuzz_cred add_executable(fuzz_cred fuzz_cred.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) target_compile_options(fuzz_cred PRIVATE ${FUZZ_LDFLAGS}) set_target_properties(fuzz_cred PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS}) target_link_libraries(fuzz_cred fido2_shared) # fuzz_assert add_executable(fuzz_assert fuzz_assert.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) target_compile_options(fuzz_assert PRIVATE ${FUZZ_LDFLAGS}) set_target_properties(fuzz_assert PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS}) target_link_libraries(fuzz_assert fido2_shared) # fuzz_mgmt add_executable(fuzz_mgmt fuzz_mgmt.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) target_compile_options(fuzz_mgmt PRIVATE ${FUZZ_LDFLAGS}) set_target_properties(fuzz_mgmt PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS}) target_link_libraries(fuzz_mgmt fido2_shared) # fuzz_credman add_executable(fuzz_credman fuzz_credman.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) target_compile_options(fuzz_credman PRIVATE ${FUZZ_LDFLAGS}) set_target_properties(fuzz_credman PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS}) target_link_libraries(fuzz_credman fido2_shared) # fuzz_bio add_executable(fuzz_bio fuzz_bio.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) target_compile_options(fuzz_bio PRIVATE ${FUZZ_LDFLAGS}) set_target_properties(fuzz_bio PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS}) target_link_libraries(fuzz_bio fido2_shared) # fuzz_hid add_executable(fuzz_hid fuzz_hid.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) target_compile_options(fuzz_hid PRIVATE ${FUZZ_LDFLAGS}) set_target_properties(fuzz_hid PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS}) target_link_libraries(fuzz_hid fido2_shared) # fuzz_netlink add_executable(fuzz_netlink fuzz_netlink.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) target_compile_options(fuzz_netlink PRIVATE ${FUZZ_LDFLAGS}) set_target_properties(fuzz_netlink PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS}) target_link_libraries(fuzz_netlink fido2_shared) # fuzz_largeblob add_executable(fuzz_largeblob fuzz_largeblob.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) target_compile_options(fuzz_largeblob PRIVATE ${FUZZ_LDFLAGS}) set_target_properties(fuzz_largeblob PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS}) target_link_libraries(fuzz_largeblob fido2_shared) libfido2-1.10.0/fuzz/Dockerfile000066400000000000000000000010321417126203300162460ustar00rootroot00000000000000# Copyright (c) 2019-2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. FROM ubuntu:focal ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update RUN apt-get install -y clang-12 cmake git libssl-dev libudev-dev make pkg-config RUN apt-get install -y zlib1g-dev RUN git clone --branch v0.9.0 https://github.com/PJK/libcbor RUN git clone https://github.com/yubico/libfido2 RUN CC=clang-12 CXX=clang++-12 /libfido2/fuzz/build-coverage /libcbor /libfido2 libfido2-1.10.0/fuzz/Makefile000066400000000000000000000051221417126203300157200ustar00rootroot00000000000000# Copyright (c) 2019-2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. IMAGE := libfido2-coverage:1.10.0 RUNNER := libfido2-runner PROFDATA := llvm-profdata-12 COV := llvm-cov-12 TARGETS := fuzz_assert fuzz_bio fuzz_cred fuzz_credman fuzz_hid \ fuzz_largeblob fuzz_netlink fuzz_mgmt CORPORA := $(foreach f,${TARGETS},${f}/corpus) MINIFY := $(foreach f,${TARGETS},/minify/${f}/corpus) REMOTE := gs://libfido2-corpus.clusterfuzz-external.appspot.com .DEFAULT_GOAL := all all: ${TARGETS} build: docker build -t ${IMAGE} - < Dockerfile run: build -docker run -it -d --name ${RUNNER} ${IMAGE} docker start ${RUNNER} sync: run tar Ccf .. - src fuzz | docker exec -i ${RUNNER} tar Cxf /libfido2 - docker exec ${RUNNER} make -C libfido2/build corpus: sync docker exec ${RUNNER} /bin/sh -c 'cd /libfido2/fuzz && rm -rf ${TARGETS}' docker exec ${RUNNER} tar Czxf /libfido2/fuzz /libfido2/fuzz/corpus.tgz ${TARGETS}: corpus sync docker exec -e LLVM_PROFILE_FILE=/profraw/$@ ${RUNNER} \ /bin/sh -c 'rm -f /profraw/$@ && /libfido2/build/fuzz/$@ \ -runs=1 /libfido2/fuzz/$@' ${MINIFY}: /minify/%/corpus: % docker exec ${RUNNER} /bin/sh -c 'rm -rf $@ && mkdir -p $@ && \ /libfido2/build/fuzz/$< -use_value_profile=1 -merge=1 $@ \ /libfido2/fuzz/$ $@ profdata: run docker exec ${RUNNER} /bin/sh -c 'rm -f /$@ && ${PROFDATA} \ merge -sparse profraw/* -o $@' report.tgz: profdata docker exec ${RUNNER} /bin/sh -c 'rm -rf /report && mkdir /report && \ ${COV} show -format=html -tab-size=8 -instr-profile=/$< \ --show-branch-summary=false -output-dir=/report \ /libfido2/build/src/libfido2.so' docker exec -i ${RUNNER} tar Czcf / - report > $@ summary.txt: profdata docker exec ${RUNNER} ${COV} report -use-color=false \ --show-branch-summary=false /libfido2/build/src/libfido2.so \ -instr-profile=/$< > $@ functions.txt: profdata docker exec ${RUNNER} /bin/sh -c '${COV} report -use-color=false \ -show-functions --show-branch-summary=false -instr-profile=/$< \ /libfido2/build/src/libfido2.so /libfido2/src/*.[ch]' > $@ clean: run docker exec ${RUNNER} /bin/sh -c 'rm -rf /profraw /profdata && \ make -C /libfido2/build clean' -docker stop ${RUNNER} rm -rf ${TARGETS} ${CORPORA}: -mkdir -p $@ gsutil -q -m rsync -d -r ${REMOTE}/libFuzzer/libfido2_$(@:/corpus=) $@ corpus.tgz: ${CORPORA} tar zcf $@ ${TARGETS} .PHONY: build run sync corpus ${TARGETS} ${CORPORA} .PHONY: report.tgz summary.txt functions.txt libfido2-1.10.0/fuzz/README000066400000000000000000000026341417126203300151450ustar00rootroot00000000000000libfido2 can be fuzzed using AFL or libFuzzer, with or without ASAN/MSAN/UBSAN. AFL is more convenient when fuzzing the path from the authenticator to libfido2 in an existing application. To do so, use preload-snoop.c with a real authenticator to obtain an initial corpus, rebuild libfido2 with -DFUZZ=ON, and use preload-fuzz.c to read device data from stdin. libFuzzer is better suited for bespoke fuzzers; see fuzz_cred.c, fuzz_credman.c, fuzz_assert.c, fuzz_hid.c, and fuzz_mgmt.c for examples. To build these harnesses, use -DFUZZ=ON -DLIBFUZZER=ON. To run under ASAN/MSAN/UBSAN, libfido2 needs to be linked against flavours of libcbor and OpenSSL built with the respective sanitiser. In order to keep memory utilisation at a manageable level, you can either enforce limits at the OS level (e.g. cgroups on Linux), or patch libcbor with the diff below. diff --git src/cbor/internal/memory_utils.c src/cbor/internal/memory_utils.c index aa049a2..e294b38 100644 --- src/cbor/internal/memory_utils.c +++ src/cbor/internal/memory_utils.c @@ -28,7 +28,10 @@ bool _cbor_safe_to_multiply(size_t a, size_t b) { void* _cbor_alloc_multiple(size_t item_size, size_t item_count) { if (_cbor_safe_to_multiply(item_size, item_count)) { - return _CBOR_MALLOC(item_size * item_count); + if (item_count > 1000) { + return NULL; + } else + return _CBOR_MALLOC(item_size * item_count); } else { return NULL; } libfido2-1.10.0/fuzz/build-coverage000077500000000000000000000017341417126203300171030ustar00rootroot00000000000000#!/bin/sh -eux # Copyright (c) 2019 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. LIBCBOR="$1" LIBFIDO2="$2" CC="${CC:-clang}" CXX="${CXX:-clang++}" PKG_CONFIG_PATH="${PKG_CONFIG_PATH:-${LIBCBOR}/install/lib/pkgconfig}" export CC PKG_CONFIG_PATH # Clean up. rm -rf "${LIBCBOR}/build" "${LIBCBOR}/install" "${LIBFIDO2}/build" # Patch, build, and install libcbor. (cd "${LIBCBOR}" && patch -N -l -s -p0 < "${LIBFIDO2}/fuzz/README") || true mkdir "${LIBCBOR}/build" "${LIBCBOR}/install" (cd "${LIBCBOR}/build" && cmake -DBUILD_SHARED_LIBS=ON \ -DCMAKE_INSTALL_PREFIX="${LIBCBOR}/install" ..) make -C "${LIBCBOR}/build" VERBOSE=1 all install # Build libfido2. mkdir -p "${LIBFIDO2}/build" export CFLAGS="-fprofile-instr-generate -fcoverage-mapping" export LDFLAGS="${CFLAGS}" (cd "${LIBFIDO2}/build" && cmake -DFUZZ=ON -DLIBFUZZER=ON \ -DCMAKE_BUILD_TYPE=Debug ..) make -C "${LIBFIDO2}/build" libfido2-1.10.0/fuzz/clock.c000066400000000000000000000033321417126203300155200ustar00rootroot00000000000000/* * Copyright (c) 2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include "mutator_aux.h" /* * A pseudo-random monotonic clock with a probabilistic discontinuity to * the end of time (as measured by struct timespec). */ extern int prng_up; extern int __wrap_clock_gettime(clockid_t, struct timespec *); extern int __real_clock_gettime(clockid_t, struct timespec *); extern int __wrap_usleep(unsigned int); static TLS struct timespec fuzz_clock; static void tick(unsigned int usec) { long long drift; /* * Simulate a jump to the end of time with 0.125% probability. * This condition should be gracefully handled by callers of * clock_gettime(). */ if (uniform_random(800) < 1) { fuzz_clock.tv_sec = LLONG_MAX; fuzz_clock.tv_nsec = LONG_MAX; return; } drift = usec * 1000LL + (long long)uniform_random(10000000); /* 10ms */ if (LLONG_MAX - drift < (long long)fuzz_clock.tv_nsec) { fuzz_clock_reset(); /* Not much we can do here. */ } else if (drift + (long long)fuzz_clock.tv_nsec < 1000000000) { fuzz_clock.tv_nsec += (long)(drift); } else { fuzz_clock.tv_sec += (long)(drift / 1000000000); fuzz_clock.tv_nsec += (long)(drift % 1000000000); } } int __wrap_clock_gettime(clockid_t clk_id, struct timespec *tp) { if (!prng_up || clk_id != CLOCK_MONOTONIC) return __real_clock_gettime(clk_id, tp); if (uniform_random(400) < 1) return -1; tick(0); *tp = fuzz_clock; return 0; } int __wrap_usleep(unsigned int usec) { if (uniform_random(400) < 1) return -1; tick(usec); return 0; } void fuzz_clock_reset(void) { memset(&fuzz_clock, 0, sizeof(fuzz_clock)); } libfido2-1.10.0/fuzz/dummy.h000066400000000000000000000165261417126203300155760ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _DUMMY_H #define _DUMMY_H #include const char dummy_name[] = "finger1"; const char dummy_pin1[] = "skepp cg0u3;Y.."; const char dummy_pin2[] = "bastilha 6rJrfQZI."; const char dummy_pin[] = "9}4gT:8d=A37Dh}U"; const char dummy_rp_id[] = "localhost"; const char dummy_rp_name[] = "sweet home localhost"; const char dummy_user_icon[] = "an icon"; const char dummy_user_name[] = "john smith"; const char dummy_user_nick[] = "jsmith"; const uint8_t dummy_id[] = { 0x5e, 0xd2 }; const uint8_t dummy_user_id[] = { 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63, 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2, 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5, 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49, }; const uint8_t dummy_cred_id[] = { 0x4f, 0x72, 0x98, 0x42, 0x4a, 0xe1, 0x17, 0xa5, 0x85, 0xa0, 0xef, 0x3b, 0x11, 0x24, 0x4a, 0x3d, }; const uint8_t dummy_cdh[] = { 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7, 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56, 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52, 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76, }; const uint8_t dummy_es256[] = { 0xcc, 0x1b, 0x50, 0xac, 0xc4, 0x19, 0xf8, 0x3a, 0xee, 0x0a, 0x77, 0xd6, 0xf3, 0x53, 0xdb, 0xef, 0xf2, 0xb9, 0x5c, 0x2d, 0x8b, 0x1e, 0x52, 0x58, 0x88, 0xf4, 0x0b, 0x85, 0x1f, 0x40, 0x6d, 0x18, 0x15, 0xb3, 0xcc, 0x25, 0x7c, 0x38, 0x3d, 0xec, 0xdf, 0xad, 0xbd, 0x46, 0x91, 0xc3, 0xac, 0x30, 0x94, 0x2a, 0xf7, 0x78, 0x35, 0x70, 0x59, 0x6f, 0x28, 0xcb, 0x8e, 0x07, 0x85, 0xb5, 0x91, 0x96, }; const uint8_t dummy_rs256[] = { 0xd2, 0xa8, 0xc0, 0x11, 0x82, 0x9e, 0x57, 0x2e, 0x60, 0xae, 0x8c, 0xb0, 0x09, 0xe1, 0x58, 0x2b, 0x99, 0xec, 0xc3, 0x11, 0x1b, 0xef, 0x81, 0x49, 0x34, 0x53, 0x6a, 0x01, 0x65, 0x2c, 0x24, 0x09, 0x30, 0x87, 0x98, 0x51, 0x6e, 0x30, 0x4f, 0x60, 0xbd, 0x54, 0xd2, 0x54, 0xbd, 0x94, 0x42, 0xdd, 0x63, 0xe5, 0x2c, 0xc6, 0x04, 0x32, 0xc0, 0x8f, 0x72, 0xd5, 0xb4, 0xf0, 0x4f, 0x42, 0xe5, 0xb0, 0xa2, 0x95, 0x11, 0xfe, 0xd8, 0xb0, 0x65, 0x34, 0xff, 0xfb, 0x44, 0x97, 0x52, 0xfc, 0x67, 0x23, 0x0b, 0xad, 0xf3, 0x3a, 0x82, 0xd4, 0x96, 0x10, 0x87, 0x6b, 0xfa, 0xd6, 0x51, 0x60, 0x3e, 0x1c, 0xae, 0x19, 0xb8, 0xce, 0x08, 0xae, 0x9a, 0xee, 0x78, 0x16, 0x22, 0xcc, 0x92, 0xcb, 0xa8, 0x95, 0x34, 0xe5, 0xb9, 0x42, 0x6a, 0xf0, 0x2e, 0x82, 0x1f, 0x4c, 0x7d, 0x84, 0x94, 0x68, 0x7b, 0x97, 0x2b, 0xf7, 0x7d, 0x67, 0x83, 0xbb, 0xc7, 0x8a, 0x31, 0x5a, 0xf3, 0x2a, 0x95, 0xdf, 0x63, 0xe7, 0x4e, 0xee, 0x26, 0xda, 0x87, 0x00, 0xe2, 0x23, 0x4a, 0x33, 0x9a, 0xa0, 0x1b, 0xce, 0x60, 0x1f, 0x98, 0xa1, 0xb0, 0xdb, 0xbf, 0x20, 0x59, 0x27, 0xf2, 0x06, 0xd9, 0xbe, 0x37, 0xa4, 0x03, 0x6b, 0x6a, 0x4e, 0xaf, 0x22, 0x68, 0xf3, 0xff, 0x28, 0x59, 0x05, 0xc9, 0xf1, 0x28, 0xf4, 0xbb, 0x35, 0xe0, 0xc2, 0x68, 0xc2, 0xaa, 0x54, 0xac, 0x8c, 0xc1, 0x69, 0x9e, 0x4b, 0x32, 0xfc, 0x53, 0x58, 0x85, 0x7d, 0x3f, 0x51, 0xd1, 0xc9, 0x03, 0x02, 0x13, 0x61, 0x62, 0xda, 0xf8, 0xfe, 0x3e, 0xc8, 0x95, 0x12, 0xfb, 0x0c, 0xdf, 0x06, 0x65, 0x6f, 0x23, 0xc7, 0x83, 0x7c, 0x50, 0x2d, 0x27, 0x25, 0x4d, 0xbf, 0x94, 0xf0, 0x89, 0x04, 0xb9, 0x2d, 0xc4, 0xa5, 0x32, 0xa9, 0x25, 0x0a, 0x99, 0x59, 0x01, 0x00, 0x01, }; const uint8_t dummy_eddsa[] = { 0xfe, 0x8b, 0x61, 0x50, 0x31, 0x7a, 0xe6, 0xdf, 0xb1, 0x04, 0x9d, 0x4d, 0xb5, 0x7a, 0x5e, 0x96, 0x4c, 0xb2, 0xf9, 0x5f, 0x72, 0x47, 0xb5, 0x18, 0xe2, 0x39, 0xdf, 0x2f, 0x87, 0x19, 0xb3, 0x02, }; const uint8_t dummy_netlink_wiredata[] = { 0xd8, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x2e, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x6e, 0x66, 0x63, 0x00, 0x06, 0x00, 0x01, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x80, 0x01, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x00, 0x08, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x05, 0x00, 0x08, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x06, 0x00, 0x08, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00, 0x08, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x09, 0x00, 0x08, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x01, 0x00, 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x01, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x01, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x10, 0x00, 0x08, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x08, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x13, 0x00, 0x08, 0x00, 0x01, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x07, 0x00, 0x18, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x2e, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x07, 0x00, 0x27, 0x00, 0x00, 0x00, 0x93, 0xb9, 0x25, 0x00 }; #endif /* !_DUMMY_H */ libfido2-1.10.0/fuzz/export.gnu000066400000000000000000000142771417126203300163270ustar00rootroot00000000000000{ 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_EVP_PKEY; es256_pk_from_ptr; es256_pk_new; es256_pk_to_EVP_PKEY; fido_assert_allow_cred; fido_assert_authdata_len; fido_assert_authdata_ptr; fido_assert_blob_len; fido_assert_blob_ptr; fido_assert_clientdata_hash_len; fido_assert_clientdata_hash_ptr; fido_assert_count; fido_assert_flags; fido_assert_free; fido_assert_hmac_secret_len; fido_assert_hmac_secret_ptr; fido_assert_id_len; fido_assert_id_ptr; fido_assert_largeblob_key_len; fido_assert_largeblob_key_ptr; fido_assert_new; fido_assert_rp_id; fido_assert_set_authdata; fido_assert_set_authdata_raw; fido_assert_set_clientdata; fido_assert_set_clientdata_hash; fido_assert_set_count; fido_assert_set_extensions; fido_assert_set_hmac_salt; fido_assert_set_hmac_secret; fido_assert_set_options; fido_assert_set_rp; fido_assert_set_sig; fido_assert_set_up; fido_assert_set_uv; fido_assert_sigcount; fido_assert_sig_len; fido_assert_sig_ptr; fido_assert_user_display_name; fido_assert_user_icon; fido_assert_user_id_len; fido_assert_user_id_ptr; fido_assert_user_name; fido_assert_verify; fido_bio_dev_enroll_begin; fido_bio_dev_enroll_cancel; fido_bio_dev_enroll_continue; fido_bio_dev_enroll_remove; fido_bio_dev_get_info; fido_bio_dev_get_template_array; fido_bio_dev_set_template_name; fido_bio_enroll_free; fido_bio_enroll_last_status; fido_bio_enroll_new; fido_bio_enroll_remaining_samples; fido_bio_info_free; fido_bio_info_max_samples; fido_bio_info_new; fido_bio_info_type; fido_bio_template; fido_bio_template_array_count; fido_bio_template_array_free; fido_bio_template_array_new; fido_bio_template_free; fido_bio_template_id_len; fido_bio_template_id_ptr; fido_bio_template_name; fido_bio_template_new; fido_bio_template_set_id; fido_bio_template_set_name; fido_cbor_info_aaguid_len; fido_cbor_info_aaguid_ptr; fido_cbor_info_algorithm_cose; fido_cbor_info_algorithm_count; fido_cbor_info_algorithm_type; fido_cbor_info_extensions_len; fido_cbor_info_extensions_ptr; fido_cbor_info_free; fido_cbor_info_maxmsgsiz; fido_cbor_info_maxcredbloblen; fido_cbor_info_maxcredcntlst; fido_cbor_info_maxcredidlen; fido_cbor_info_fwversion; fido_cbor_info_new; fido_cbor_info_options_len; fido_cbor_info_options_name_ptr; fido_cbor_info_options_value_ptr; fido_cbor_info_protocols_len; fido_cbor_info_protocols_ptr; fido_cbor_info_transports_len; fido_cbor_info_transports_ptr; fido_cbor_info_versions_len; fido_cbor_info_versions_ptr; fido_cred_attstmt_len; fido_cred_attstmt_ptr; fido_cred_authdata_len; fido_cred_authdata_ptr; fido_cred_authdata_raw_len; fido_cred_authdata_raw_ptr; fido_cred_clientdata_hash_len; fido_cred_clientdata_hash_ptr; fido_cred_display_name; fido_cred_exclude; fido_cred_flags; fido_cred_largeblob_key_len; fido_cred_largeblob_key_ptr; fido_cred_sigcount; fido_cred_fmt; fido_cred_free; fido_cred_id_len; fido_cred_id_ptr; fido_cred_aaguid_len; fido_cred_aaguid_ptr; fido_credman_del_dev_rk; fido_credman_get_dev_metadata; fido_credman_get_dev_rk; fido_credman_get_dev_rp; fido_credman_metadata_free; fido_credman_metadata_new; fido_credman_rk; fido_credman_rk_count; fido_credman_rk_existing; fido_credman_rk_free; fido_credman_rk_new; fido_credman_rk_remaining; fido_credman_rp_count; fido_credman_rp_free; fido_credman_rp_id; fido_credman_rp_id_hash_len; fido_credman_rp_id_hash_ptr; fido_credman_rp_name; fido_credman_rp_new; fido_credman_set_dev_rk; fido_cred_new; fido_cred_pin_minlen; fido_cred_prot; fido_cred_pubkey_len; fido_cred_pubkey_ptr; fido_cred_rp_id; fido_cred_rp_name; fido_cred_set_attstmt; fido_cred_set_authdata; fido_cred_set_authdata_raw; fido_cred_set_blob; fido_cred_set_clientdata; fido_cred_set_clientdata_hash; fido_cred_set_extensions; fido_cred_set_fmt; fido_cred_set_id; fido_cred_set_options; fido_cred_set_pin_minlen; fido_cred_set_prot; fido_cred_set_rk; fido_cred_set_rp; fido_cred_set_sig; fido_cred_set_type; fido_cred_set_user; fido_cred_set_uv; fido_cred_set_x509; fido_cred_sig_len; fido_cred_sig_ptr; fido_cred_type; fido_cred_user_id_len; fido_cred_user_id_ptr; fido_cred_user_name; fido_cred_verify; fido_cred_verify_self; fido_cred_x5c_len; fido_cred_x5c_ptr; fido_dev_build; fido_dev_cancel; fido_dev_close; fido_dev_enable_entattest; fido_dev_flags; fido_dev_force_fido2; fido_dev_force_pin_change; fido_dev_force_u2f; fido_dev_free; fido_dev_get_assert; fido_dev_get_cbor_info; fido_dev_get_retry_count; fido_dev_get_uv_retry_count; fido_dev_get_touch_begin; fido_dev_get_touch_status; fido_dev_has_pin; fido_dev_has_uv; fido_dev_info_free; fido_dev_info_manifest; fido_dev_info_manufacturer_string; fido_dev_info_new; fido_dev_info_path; fido_dev_info_product; fido_dev_info_product_string; fido_dev_info_ptr; fido_dev_info_set; fido_dev_info_vendor; fido_dev_is_fido2; fido_dev_major; fido_dev_make_cred; fido_dev_minor; fido_dev_new; fido_dev_open; fido_dev_protocol; fido_dev_reset; fido_dev_set_io_functions; fido_dev_set_pin; fido_dev_set_pin_minlen; fido_dev_set_pin_minlen_rpid; fido_dev_set_timeout; fido_dev_set_transport_functions; fido_dev_supports_cred_prot; fido_dev_supports_credman; fido_dev_supports_permissions; fido_dev_supports_pin; fido_dev_supports_uv; fido_dev_toggle_always_uv; fido_dev_largeblob_get; fido_dev_largeblob_get_array; fido_dev_largeblob_remove; fido_dev_largeblob_set; fido_dev_largeblob_set_array; fido_hid_get_report_len; fido_hid_get_usage; fido_init; fido_nfc_rx; fido_nfc_tx; fido_nl_free; fido_nl_get_nfc_target; fido_nl_new; fido_nl_power_nfc; fido_set_log_handler; fido_strerr; rs256_pk_free; rs256_pk_from_ptr; rs256_pk_from_EVP_PKEY; rs256_pk_from_RSA; rs256_pk_new; rs256_pk_to_EVP_PKEY; prng_init; fuzz_clock_reset; set_netlink_io_functions; set_udev_parameters; uniform_random; local: *; }; libfido2-1.10.0/fuzz/functions.txt000066400000000000000000002215401417126203300170350ustar00rootroot00000000000000File '/libfido2/src/aes256.c': Name Regions Miss Cover Lines Miss Cover -------------------------------------------------------------------------------------------------------- aes256_cbc_enc 3 0 100.00% 4 0 100.00% aes256_cbc_dec 3 0 100.00% 4 0 100.00% aes256_gcm_enc 1 0 100.00% 3 0 100.00% aes256_gcm_dec 1 0 100.00% 3 0 100.00% aes256.c:aes256_cbc_fips 26 2 92.31% 42 7 83.33% aes256.c:aes256_cbc 29 1 96.55% 36 3 91.67% aes256.c:aes256_cbc_proto1 1 0 100.00% 5 0 100.00% aes256.c:aes256_gcm 51 1 98.04% 60 4 93.33% -------------------------------------------------------------------------------------------------------- TOTAL 115 4 96.52% 157 14 91.08% File '/libfido2/src/assert.c': Name Regions Miss Cover Lines Miss Cover ----------------------------------------------------------------------------------------------------------------- fido_dev_get_assert 40 0 100.00% 35 0 100.00% fido_check_flags 13 0 100.00% 15 0 100.00% fido_get_signed_hash 36 0 100.00% 46 0 100.00% fido_assert_verify 48 4 91.67% 67 5 92.54% fido_assert_set_clientdata 12 12 0.00% 11 11 0.00% fido_assert_set_clientdata_hash 8 0 100.00% 6 0 100.00% fido_assert_set_hmac_salt 10 0 100.00% 6 0 100.00% fido_assert_set_hmac_secret 12 12 0.00% 7 7 0.00% fido_assert_set_rp 12 0 100.00% 11 0 100.00% fido_assert_allow_cred 13 2 84.62% 22 3 86.36% fido_assert_set_extensions 14 0 100.00% 10 0 100.00% fido_assert_set_options 6 6 0.00% 5 5 0.00% fido_assert_set_up 2 0 100.00% 4 0 100.00% fido_assert_set_uv 2 0 100.00% 4 0 100.00% fido_assert_clientdata_hash_ptr 1 0 100.00% 3 0 100.00% fido_assert_clientdata_hash_len 1 0 100.00% 3 0 100.00% fido_assert_new 1 0 100.00% 3 0 100.00% fido_assert_reset_tx 1 0 100.00% 12 0 100.00% fido_assert_reset_rx 4 0 100.00% 19 0 100.00% fido_assert_free 6 0 100.00% 9 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% 5 0 100.00% fido_assert_sigcount 4 0 100.00% 5 0 100.00% fido_assert_authdata_ptr 4 0 100.00% 5 0 100.00% fido_assert_authdata_len 4 0 100.00% 5 0 100.00% fido_assert_sig_ptr 4 0 100.00% 5 0 100.00% fido_assert_sig_len 4 0 100.00% 5 0 100.00% fido_assert_id_ptr 4 0 100.00% 5 0 100.00% fido_assert_id_len 4 0 100.00% 5 0 100.00% fido_assert_user_id_ptr 4 0 100.00% 5 0 100.00% fido_assert_user_id_len 4 0 100.00% 5 0 100.00% fido_assert_user_icon 4 0 100.00% 5 0 100.00% fido_assert_user_name 4 0 100.00% 5 0 100.00% fido_assert_user_display_name 4 0 100.00% 5 0 100.00% fido_assert_hmac_secret_ptr 4 0 100.00% 5 0 100.00% fido_assert_hmac_secret_len 4 0 100.00% 5 0 100.00% fido_assert_largeblob_key_ptr 4 0 100.00% 5 0 100.00% fido_assert_largeblob_key_len 4 0 100.00% 5 0 100.00% fido_assert_blob_ptr 4 0 100.00% 5 0 100.00% fido_assert_blob_len 4 0 100.00% 5 0 100.00% fido_assert_set_authdata 24 0 100.00% 28 0 100.00% fido_assert_set_authdata_raw 24 0 100.00% 27 0 100.00% fido_assert_set_sig 14 0 100.00% 7 0 100.00% fido_assert_set_count 10 0 100.00% 17 0 100.00% assert.c:fido_dev_get_assert_wait 21 0 100.00% 14 0 100.00% assert.c:fido_dev_get_assert_tx 56 2 96.43% 62 5 91.94% assert.c:fido_dev_get_assert_rx 19 0 100.00% 27 0 100.00% assert.c:adjust_assert_count 24 0 100.00% 26 0 100.00% assert.c:parse_assert_reply 12 0 100.00% 24 0 100.00% assert.c:fido_get_next_assert_tx 8 0 100.00% 8 0 100.00% assert.c:fido_get_next_assert_rx 15 2 86.67% 21 4 80.95% assert.c:decrypt_hmac_secrets 9 0 100.00% 15 0 100.00% assert.c:check_extensions 5 0 100.00% 9 0 100.00% assert.c:fido_assert_reset_extattr 1 0 100.00% 5 0 100.00% assert.c:fido_assert_clean_authdata 1 0 100.00% 5 0 100.00% ----------------------------------------------------------------------------------------------------------------- TOTAL 563 40 92.90% 694 40 94.24% File '/libfido2/src/authkey.c': Name Regions Miss Cover Lines Miss Cover ----------------------------------------------------------------------------------------------------------------- fido_dev_authkey 1 0 100.00% 3 0 100.00% authkey.c:fido_dev_authkey_wait 10 0 100.00% 7 0 100.00% authkey.c:fido_dev_authkey_tx 19 0 100.00% 25 0 100.00% authkey.c:fido_dev_authkey_rx 6 0 100.00% 14 0 100.00% authkey.c:parse_authkey 8 0 100.00% 10 0 100.00% ----------------------------------------------------------------------------------------------------------------- TOTAL 44 0 100.00% 59 0 100.00% File '/libfido2/src/bio.c': Name Regions Miss Cover Lines Miss Cover ----------------------------------------------------------------------------------------------------------------- fido_bio_dev_get_template_array 5 2 60.00% 6 0 100.00% fido_bio_dev_set_template_name 7 0 100.00% 6 0 100.00% fido_bio_dev_enroll_begin 25 2 92.00% 31 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% 4 4 0.00% fido_bio_dev_enroll_remove 1 0 100.00% 4 0 100.00% fido_bio_dev_get_info 1 0 100.00% 4 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% 8 0 100.00% fido_bio_template_free 6 0 100.00% 8 0 100.00% fido_bio_template_set_name 8 0 100.00% 7 0 100.00% fido_bio_template_set_id 8 0 100.00% 6 0 100.00% fido_bio_template 4 0 100.00% 5 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% 8 0 100.00% fido_bio_info_free 6 0 100.00% 7 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% 7 0 100.00% bio.c:bio_tx 43 0 100.00% 55 0 100.00% bio.c:bio_prepare_hmac 18 0 100.00% 29 0 100.00% bio.c:bio_rx_template_array 11 0 100.00% 17 0 100.00% bio.c:bio_parse_template_array 26 1 96.15% 27 4 85.19% bio.c:decode_template_array 12 1 91.67% 18 3 83.33% bio.c:decode_template 9 0 100.00% 15 0 100.00% bio.c:bio_set_template_name_wait 19 0 100.00% 20 0 100.00% bio.c:bio_enroll_begin_wait 17 0 100.00% 19 0 100.00% bio.c:bio_rx_enroll_begin 15 0 100.00% 24 0 100.00% bio.c:bio_parse_enroll_status 20 0 100.00% 28 0 100.00% bio.c:bio_parse_template_id 8 0 100.00% 10 0 100.00% bio.c:bio_enroll_continue_wait 19 0 100.00% 20 0 100.00% bio.c:bio_rx_enroll_continue 11 0 100.00% 18 0 100.00% bio.c:bio_enroll_cancel_wait 11 11 0.00% 10 10 0.00% bio.c:bio_enroll_remove_wait 17 0 100.00% 19 0 100.00% bio.c:bio_get_info_wait 11 0 100.00% 10 0 100.00% bio.c:bio_rx_info 11 0 100.00% 17 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% 28 0 100.00% bio.c:bio_reset_template_array 4 0 100.00% 7 0 100.00% bio.c:bio_reset_template 1 0 100.00% 5 0 100.00% bio.c:bio_reset_enroll 3 0 100.00% 6 0 100.00% ----------------------------------------------------------------------------------------------------------------- TOTAL 419 20 95.23% 559 21 96.24% File '/libfido2/src/blob.c': Name Regions Miss Cover Lines Miss Cover ----------------------------------------------------------------------------------------------------------------- fido_blob_new 1 0 100.00% 3 0 100.00% fido_blob_reset 1 0 100.00% 4 0 100.00% fido_blob_set 9 0 100.00% 15 0 100.00% fido_blob_append 12 1 91.67% 20 3 85.00% fido_blob_free 6 0 100.00% 8 0 100.00% fido_free_blob_array 7 0 100.00% 12 0 100.00% fido_blob_encode 6 0 100.00% 5 0 100.00% fido_blob_decode 1 0 100.00% 3 0 100.00% fido_blob_is_empty 3 0 100.00% 3 0 100.00% fido_blob_serialise 7 1 85.71% 10 1 90.00% ----------------------------------------------------------------------------------------------------------------- TOTAL 53 2 96.23% 83 4 95.18% File '/libfido2/src/buf.c': Name Regions Miss Cover Lines Miss Cover ----------------------------------------------------------------------------------------------------------------- fido_buf_read 4 0 100.00% 8 0 100.00% fido_buf_write 4 1 75.00% 8 1 87.50% ----------------------------------------------------------------------------------------------------------------- TOTAL 8 1 87.50% 16 1 93.75% File '/libfido2/src/cbor.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------ cbor_map_iter 20 1 95.00% 26 4 84.62% cbor_array_iter 12 0 100.00% 16 0 100.00% cbor_parse_reply 27 0 100.00% 36 0 100.00% cbor_vector_free 6 0 100.00% 5 0 100.00% cbor_bytestring_copy 14 0 100.00% 18 0 100.00% cbor_string_copy 14 0 100.00% 18 0 100.00% cbor_add_bytestring 14 0 100.00% 21 0 100.00% cbor_add_string 14 0 100.00% 21 0 100.00% cbor_add_bool 14 0 100.00% 21 0 100.00% cbor_flatten_vector 14 1 92.86% 16 1 93.75% cbor_build_frame 15 0 100.00% 25 0 100.00% cbor_encode_rp_entity 13 0 100.00% 11 0 100.00% cbor_encode_user_entity 21 0 100.00% 15 0 100.00% cbor_encode_pubkey_param 36 0 100.00% 39 0 100.00% cbor_encode_pubkey 10 0 100.00% 11 0 100.00% cbor_encode_pubkey_list 18 0 100.00% 19 0 100.00% cbor_encode_str_array 18 0 100.00% 19 0 100.00% cbor_encode_cred_ext 55 0 100.00% 50 0 100.00% cbor_encode_cred_opt 13 0 100.00% 11 0 100.00% cbor_encode_assert_opt 13 0 100.00% 11 0 100.00% cbor_encode_pin_auth 20 1 95.00% 22 3 86.36% cbor_encode_pin_opt 4 0 100.00% 8 0 100.00% cbor_encode_change_pin_auth 31 1 96.77% 36 3 91.67% cbor_encode_assert_ext 33 0 100.00% 32 0 100.00% cbor_decode_fmt 13 0 100.00% 15 0 100.00% cbor_decode_pubkey 21 1 95.24% 30 2 93.33% cbor_decode_cred_authdata 31 1 96.77% 35 3 91.43% cbor_decode_assert_authdata 21 0 100.00% 32 0 100.00% cbor_decode_attstmt 13 0 100.00% 16 0 100.00% cbor_decode_uint64 4 0 100.00% 8 0 100.00% cbor_decode_cred_id 8 0 100.00% 9 0 100.00% cbor_decode_user 8 0 100.00% 9 0 100.00% cbor_decode_rp_entity 8 0 100.00% 9 0 100.00% cbor_build_uint 10 1 90.00% 9 2 77.78% cbor_array_append 17 0 100.00% 21 0 100.00% cbor_array_drop 18 2 88.89% 17 3 82.35% cbor.c:ctap_check_cbor 28 0 100.00% 26 0 100.00% cbor.c:check_key_type 8 0 100.00% 7 0 100.00% cbor.c:cbor_add_arg 13 0 100.00% 21 0 100.00% cbor.c:cbor_add_uint8 14 0 100.00% 21 0 100.00% cbor.c:cbor_encode_largeblob_key_ext 6 0 100.00% 6 0 100.00% cbor.c:cbor_encode_hmac_secret_param 59 4 93.22% 66 8 87.88% cbor.c:get_cose_alg 36 0 100.00% 38 0 100.00% cbor.c:find_cose_alg 35 0 100.00% 33 0 100.00% cbor.c:decode_attcred 25 0 100.00% 44 0 100.00% cbor.c:decode_cred_extensions 14 0 100.00% 24 0 100.00% cbor.c:decode_cred_extension 49 10 79.59% 49 17 65.31% cbor.c:decode_assert_extensions 14 0 100.00% 23 0 100.00% cbor.c:decode_assert_extension 19 0 100.00% 27 0 100.00% cbor.c:decode_attstmt_entry 52 0 100.00% 50 0 100.00% cbor.c:decode_x5c 4 0 100.00% 6 0 100.00% cbor.c:decode_cred_id_entry 10 0 100.00% 19 0 100.00% cbor.c:decode_user_entry 25 0 100.00% 35 0 100.00% cbor.c:decode_rp_entity_entry 15 0 100.00% 25 0 100.00% ------------------------------------------------------------------------------------------------------------------ TOTAL 1047 23 97.80% 1237 46 96.28% File '/libfido2/src/compress.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------ fido_compress 1 0 100.00% 3 0 100.00% fido_uncompress 1 0 100.00% 3 0 100.00% compress.c:do_compress 32 4 87.50% 22 3 86.36% ------------------------------------------------------------------------------------------------------------------ TOTAL 34 4 88.24% 28 3 89.29% File '/libfido2/src/config.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_dev_enable_entattest 1 0 100.00% 4 0 100.00% fido_dev_toggle_always_uv 1 0 100.00% 4 0 100.00% fido_dev_set_pin_minlen 1 0 100.00% 4 0 100.00% fido_dev_force_pin_change 1 0 100.00% 4 0 100.00% fido_dev_set_pin_minlen_rpid 6 0 100.00% 15 0 100.00% config.c:config_enable_entattest_wait 6 0 100.00% 7 0 100.00% config.c:config_tx 37 0 100.00% 48 0 100.00% config.c:config_prepare_hmac 8 0 100.00% 19 0 100.00% config.c:config_toggle_always_uv_wait 6 0 100.00% 7 0 100.00% config.c:config_pin_minlen 5 0 100.00% 7 0 100.00% config.c:config_pin_minlen_tx 36 0 100.00% 32 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 108 0 100.00% 151 0 100.00% File '/libfido2/src/cred.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_dev_make_cred 12 0 100.00% 10 0 100.00% fido_check_rp_id 4 0 100.00% 11 0 100.00% fido_cred_verify 56 2 96.43% 72 5 93.06% fido_cred_verify_self 58 4 93.10% 83 5 93.98% fido_cred_new 1 0 100.00% 3 0 100.00% fido_cred_reset_tx 1 0 100.00% 19 0 100.00% fido_cred_reset_rx 1 0 100.00% 7 0 100.00% fido_cred_free 6 0 100.00% 9 0 100.00% fido_cred_set_authdata 23 0 100.00% 28 0 100.00% fido_cred_set_authdata_raw 25 0 100.00% 29 0 100.00% fido_cred_set_id 6 0 100.00% 5 0 100.00% fido_cred_set_x509 6 0 100.00% 5 0 100.00% fido_cred_set_sig 6 0 100.00% 5 0 100.00% fido_cred_set_attstmt 20 0 100.00% 23 0 100.00% fido_cred_exclude 14 2 85.71% 19 3 84.21% fido_cred_set_clientdata 12 12 0.00% 11 11 0.00% fido_cred_set_clientdata_hash 8 0 100.00% 6 0 100.00% fido_cred_set_rp 18 0 100.00% 22 0 100.00% fido_cred_set_user 32 0 100.00% 41 0 100.00% fido_cred_set_extensions 16 0 100.00% 10 0 100.00% fido_cred_set_options 6 6 0.00% 5 5 0.00% fido_cred_set_rk 2 0 100.00% 4 0 100.00% fido_cred_set_uv 2 0 100.00% 4 0 100.00% fido_cred_set_prot 21 0 100.00% 14 0 100.00% fido_cred_set_pin_minlen 7 0 100.00% 8 0 100.00% fido_cred_set_blob 13 2 84.62% 8 1 87.50% fido_cred_set_fmt 20 4 80.00% 12 1 91.67% fido_cred_set_type 17 0 100.00% 7 0 100.00% fido_cred_type 1 0 100.00% 3 0 100.00% fido_cred_flags 1 0 100.00% 3 0 100.00% fido_cred_sigcount 1 0 100.00% 3 0 100.00% fido_cred_clientdata_hash_ptr 1 0 100.00% 3 0 100.00% fido_cred_clientdata_hash_len 1 0 100.00% 3 0 100.00% fido_cred_x5c_ptr 1 0 100.00% 3 0 100.00% fido_cred_x5c_len 1 0 100.00% 3 0 100.00% fido_cred_sig_ptr 1 0 100.00% 3 0 100.00% fido_cred_sig_len 1 0 100.00% 3 0 100.00% fido_cred_authdata_ptr 1 0 100.00% 3 0 100.00% fido_cred_authdata_len 1 0 100.00% 3 0 100.00% fido_cred_authdata_raw_ptr 1 0 100.00% 3 0 100.00% fido_cred_authdata_raw_len 1 0 100.00% 3 0 100.00% fido_cred_attstmt_ptr 1 0 100.00% 3 0 100.00% fido_cred_attstmt_len 1 0 100.00% 3 0 100.00% fido_cred_pubkey_ptr 9 0 100.00% 18 0 100.00% fido_cred_pubkey_len 9 0 100.00% 18 0 100.00% fido_cred_id_ptr 1 0 100.00% 3 0 100.00% fido_cred_id_len 1 0 100.00% 3 0 100.00% fido_cred_aaguid_ptr 1 0 100.00% 3 0 100.00% fido_cred_aaguid_len 1 0 100.00% 3 0 100.00% fido_cred_prot 1 0 100.00% 3 0 100.00% fido_cred_pin_minlen 1 0 100.00% 3 0 100.00% fido_cred_fmt 1 0 100.00% 3 0 100.00% fido_cred_rp_id 1 0 100.00% 3 0 100.00% fido_cred_rp_name 1 0 100.00% 3 0 100.00% fido_cred_user_name 1 0 100.00% 3 0 100.00% fido_cred_display_name 1 0 100.00% 3 0 100.00% fido_cred_user_id_ptr 1 0 100.00% 3 0 100.00% fido_cred_user_id_len 1 0 100.00% 3 0 100.00% fido_cred_largeblob_key_ptr 1 0 100.00% 3 0 100.00% fido_cred_largeblob_key_len 1 0 100.00% 3 0 100.00% cred.c:fido_dev_make_cred_wait 10 0 100.00% 7 0 100.00% cred.c:fido_dev_make_cred_tx 64 0 100.00% 70 0 100.00% cred.c:fido_dev_make_cred_rx 29 0 100.00% 32 0 100.00% cred.c:parse_makecred_reply 14 0 100.00% 27 0 100.00% cred.c:check_extensions 2 0 100.00% 6 0 100.00% cred.c:get_signed_hash_u2f 27 0 100.00% 26 0 100.00% cred.c:verify_attstmt 23 2 91.30% 40 5 87.50% cred.c:fido_cred_clean_authdata 1 0 100.00% 8 0 100.00% cred.c:fido_cred_clean_attstmt 1 0 100.00% 8 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 632 34 94.62% 830 36 95.66% File '/libfido2/src/credman.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_credman_get_dev_metadata 1 0 100.00% 4 0 100.00% fido_credman_get_dev_rk 1 0 100.00% 4 0 100.00% fido_credman_del_dev_rk 1 0 100.00% 4 0 100.00% fido_credman_get_dev_rp 1 0 100.00% 4 0 100.00% fido_credman_set_dev_rk 1 0 100.00% 4 0 100.00% fido_credman_rk_new 1 0 100.00% 3 0 100.00% fido_credman_rk_free 6 1 83.33% 8 0 100.00% fido_credman_rk_count 1 0 100.00% 3 0 100.00% fido_credman_rk 4 0 100.00% 5 0 100.00% fido_credman_metadata_new 1 0 100.00% 3 0 100.00% fido_credman_metadata_free 6 1 83.33% 7 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% 8 0 100.00% fido_credman_rp_count 1 0 100.00% 3 0 100.00% fido_credman_rp_id 4 0 100.00% 5 0 100.00% fido_credman_rp_name 4 0 100.00% 5 0 100.00% fido_credman_rp_id_hash_len 4 0 100.00% 5 0 100.00% fido_credman_rp_id_hash_ptr 4 0 100.00% 5 0 100.00% credman.c:credman_get_metadata_wait 11 0 100.00% 8 0 100.00% credman.c:credman_tx 36 0 100.00% 50 0 100.00% credman.c:credman_prepare_hmac 31 1 96.77% 50 2 96.00% credman.c:credman_rx_metadata 11 0 100.00% 17 0 100.00% credman.c:credman_parse_metadata 9 0 100.00% 17 0 100.00% credman.c:credman_get_rk_wait 27 0 100.00% 23 0 100.00% credman.c:credman_rx_rk 19 0 100.00% 27 0 100.00% credman.c:credman_parse_rk_count 16 0 100.00% 20 0 100.00% credman.c:credman_grow_array 17 2 88.24% 21 5 76.19% credman.c:credman_parse_rk 23 0 100.00% 31 0 100.00% credman.c:credman_rx_next_rk 15 2 86.67% 21 4 80.95% credman.c:credman_del_rk_wait 16 0 100.00% 15 0 100.00% credman.c:credman_get_rp_wait 23 0 100.00% 15 0 100.00% credman.c:credman_rx_rp 19 0 100.00% 27 0 100.00% credman.c:credman_parse_rp_count 16 0 100.00% 20 0 100.00% credman.c:credman_parse_rp 9 0 100.00% 17 0 100.00% credman.c:credman_rx_next_rp 15 2 86.67% 21 4 80.95% credman.c:credman_set_dev_rk_wait 11 0 100.00% 8 0 100.00% credman.c:credman_reset_rk 4 0 100.00% 9 0 100.00% credman.c:credman_reset_rp 4 0 100.00% 12 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 382 10 97.38% 518 15 97.10% File '/libfido2/src/dev.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_dev_register_manifest_func 10 2 80.00% 14 3 78.57% fido_dev_unregister_manifest_func 7 7 0.00% 11 11 0.00% fido_dev_info_manifest 22 4 81.82% 24 0 100.00% fido_dev_open_with_info 5 5 0.00% 6 6 0.00% fido_dev_open 5 1 80.00% 19 12 36.84% fido_dev_close 9 2 77.78% 8 0 100.00% fido_dev_set_sigmask 18 18 0.00% 11 11 0.00% fido_dev_cancel 11 0 100.00% 8 0 100.00% fido_dev_get_touch_begin 50 0 100.00% 59 0 100.00% fido_dev_get_touch_status 17 0 100.00% 20 0 100.00% fido_dev_set_io_functions 18 4 77.78% 14 6 57.14% fido_dev_set_transport_functions 6 2 66.67% 9 3 66.67% fido_dev_io_handle 1 1 0.00% 3 3 0.00% fido_init 8 1 87.50% 5 0 100.00% fido_dev_new 5 0 100.00% 14 0 100.00% fido_dev_new_with_info 10 10 0.00% 16 16 0.00% fido_dev_free 6 0 100.00% 8 0 100.00% fido_dev_protocol 1 0 100.00% 3 0 100.00% fido_dev_major 1 0 100.00% 3 0 100.00% fido_dev_minor 1 0 100.00% 3 0 100.00% fido_dev_build 1 0 100.00% 3 0 100.00% fido_dev_flags 1 0 100.00% 3 0 100.00% fido_dev_is_fido2 2 0 100.00% 3 0 100.00% fido_dev_is_winhello 2 2 0.00% 3 3 0.00% fido_dev_supports_pin 3 0 100.00% 3 0 100.00% fido_dev_has_pin 2 0 100.00% 3 0 100.00% fido_dev_supports_cred_prot 2 0 100.00% 3 0 100.00% fido_dev_supports_credman 2 0 100.00% 3 0 100.00% fido_dev_supports_uv 3 0 100.00% 3 0 100.00% fido_dev_has_uv 2 0 100.00% 3 0 100.00% fido_dev_supports_permissions 2 0 100.00% 3 0 100.00% fido_dev_force_u2f 2 0 100.00% 4 0 100.00% fido_dev_force_fido2 2 2 0.00% 3 3 0.00% fido_dev_get_pin_protocol 11 0 100.00% 7 0 100.00% fido_dev_maxmsgsize 1 0 100.00% 3 0 100.00% fido_dev_set_timeout 6 2 66.67% 6 1 83.33% dev.c:find_manifest_func_node 5 0 100.00% 8 0 100.00% dev.c:fido_dev_open_wait 10 0 100.00% 7 0 100.00% dev.c:fido_dev_open_tx 56 15 73.21% 56 26 53.57% dev.c:set_random_report_len 11 0 100.00% 6 0 100.00% dev.c:fido_dev_open_rx 36 1 97.22% 53 1 98.11% dev.c:fido_dev_set_flags 1 0 100.00% 5 0 100.00% dev.c:fido_dev_set_extension_flags 7 0 100.00% 7 0 100.00% dev.c:fido_dev_set_option_flags 29 0 100.00% 18 0 100.00% dev.c:fido_dev_set_protocol_flags 11 0 100.00% 17 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 421 79 81.24% 491 105 78.62% File '/libfido2/src/ecdh.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_do_ecdh 29 0 100.00% 36 0 100.00% ecdh.c:do_ecdh 37 0 100.00% 44 0 100.00% ecdh.c:kdf 19 1 94.74% 28 2 92.86% ecdh.c:hkdf_sha256 32 1 96.88% 38 3 92.11% ------------------------------------------------------------------------------------------------------------------- TOTAL 117 2 98.29% 146 5 96.58% File '/libfido2/src/eddsa.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- eddsa_pk_decode 8 0 100.00% 9 0 100.00% eddsa_pk_new 1 0 100.00% 3 0 100.00% eddsa_pk_free 6 0 100.00% 7 0 100.00% eddsa_pk_from_ptr 6 0 100.00% 6 0 100.00% eddsa_pk_to_EVP_PKEY 3 0 100.00% 7 0 100.00% eddsa_pk_from_EVP_PKEY 14 0 100.00% 10 0 100.00% eddsa_verify_sig 19 2 89.47% 30 6 80.00% eddsa_pk_verify_sig 7 1 85.71% 13 2 84.62% eddsa.c:decode_pubkey_point 8 0 100.00% 11 0 100.00% eddsa.c:decode_coord 8 0 100.00% 10 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 80 3 96.25% 106 8 92.45% File '/libfido2/src/err.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_strerr 122 10 91.80% 126 10 92.06% ------------------------------------------------------------------------------------------------------------------- TOTAL 122 10 91.80% 126 10 92.06% File '/libfido2/src/es256.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- es256_pk_decode 8 0 100.00% 9 0 100.00% es256_pk_encode 56 0 100.00% 48 0 100.00% es256_sk_new 1 0 100.00% 3 0 100.00% es256_sk_free 6 0 100.00% 7 0 100.00% es256_pk_new 1 0 100.00% 3 0 100.00% es256_pk_free 6 0 100.00% 7 0 100.00% es256_pk_from_ptr 11 0 100.00% 10 0 100.00% es256_pk_set_x 1 0 100.00% 4 0 100.00% es256_pk_set_y 1 0 100.00% 4 0 100.00% es256_sk_create 39 0 100.00% 41 0 100.00% es256_pk_to_EVP_PKEY 42 0 100.00% 54 0 100.00% es256_pk_from_EC_KEY 38 0 100.00% 36 0 100.00% es256_pk_from_EVP_PKEY 7 2 71.43% 7 0 100.00% es256_sk_to_EVP_PKEY 28 0 100.00% 40 0 100.00% es256_derive_pk 25 0 100.00% 30 0 100.00% es256_verify_sig 12 2 83.33% 19 5 73.68% es256_pk_verify_sig 7 1 85.71% 13 2 84.62% es256.c:decode_pubkey_point 9 0 100.00% 13 0 100.00% es256.c:decode_coord 8 0 100.00% 10 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 306 5 98.37% 358 7 98.04% File '/libfido2/src/extern.h': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- File '/libfido2/src/fido.h': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- File '/libfido2/src/hid.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_hid_get_usage 13 0 100.00% 22 0 100.00% fido_hid_get_report_len 19 0 100.00% 27 0 100.00% fido_dev_info_new 1 0 100.00% 3 0 100.00% fido_dev_info_free 9 0 100.00% 9 0 100.00% fido_dev_info_ptr 1 0 100.00% 3 0 100.00% fido_dev_info_set 26 2 92.31% 30 3 90.00% fido_dev_info_path 1 0 100.00% 3 0 100.00% fido_dev_info_vendor 1 0 100.00% 3 0 100.00% fido_dev_info_product 1 0 100.00% 3 0 100.00% fido_dev_info_manufacturer_string 1 0 100.00% 3 0 100.00% fido_dev_info_product_string 1 0 100.00% 3 0 100.00% hid.c:get_key_len 6 0 100.00% 12 0 100.00% hid.c:get_key_val 6 0 100.00% 18 0 100.00% hid.c:fido_dev_info_reset 1 0 100.00% 6 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 87 2 97.70% 145 3 97.93% File '/libfido2/src/hid_linux.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_hid_manifest 35 4 88.57% 41 1 97.56% fido_hid_open 27 27 0.00% 40 40 0.00% fido_hid_close 3 3 0.00% 6 6 0.00% fido_hid_set_sigmask 2 2 0.00% 6 6 0.00% fido_hid_read 15 15 0.00% 21 21 0.00% fido_hid_write 12 12 0.00% 17 17 0.00% fido_hid_report_in_len 1 1 0.00% 4 4 0.00% fido_hid_report_out_len 1 1 0.00% 4 4 0.00% hid_linux.c:copy_info 34 0 100.00% 44 0 100.00% hid_linux.c:is_fido 10 2 80.00% 14 2 85.71% hid_linux.c:get_parent_attr 6 0 100.00% 9 0 100.00% hid_linux.c:parse_uevent 12 0 100.00% 24 0 100.00% hid_linux.c:get_usb_attr 1 0 100.00% 3 0 100.00% hid_linux.c:get_report_descriptor 14 1 92.86% 17 3 82.35% ------------------------------------------------------------------------------------------------------------------- TOTAL 173 68 60.69% 250 104 58.40% File '/libfido2/src/hid_unix.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_hid_unix_open 18 11 38.89% 22 14 36.36% fido_hid_unix_wait 10 9 10.00% 21 10 52.38% ------------------------------------------------------------------------------------------------------------------- TOTAL 28 20 28.57% 43 24 44.19% File '/libfido2/src/info.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_dev_get_cbor_info_wait 10 0 100.00% 7 0 100.00% fido_dev_get_cbor_info 1 0 100.00% 4 0 100.00% fido_cbor_info_new 1 0 100.00% 3 0 100.00% fido_cbor_info_reset 1 0 100.00% 8 0 100.00% fido_cbor_info_free 6 0 100.00% 8 0 100.00% fido_cbor_info_versions_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_versions_len 1 0 100.00% 3 0 100.00% fido_cbor_info_extensions_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_extensions_len 1 0 100.00% 3 0 100.00% fido_cbor_info_transports_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_transports_len 1 0 100.00% 3 0 100.00% fido_cbor_info_aaguid_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_aaguid_len 1 0 100.00% 3 0 100.00% fido_cbor_info_options_name_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_options_value_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_options_len 1 0 100.00% 3 0 100.00% fido_cbor_info_maxcredbloblen 1 0 100.00% 3 0 100.00% fido_cbor_info_maxmsgsiz 1 0 100.00% 3 0 100.00% fido_cbor_info_maxcredcntlst 1 0 100.00% 3 0 100.00% fido_cbor_info_maxcredidlen 1 0 100.00% 3 0 100.00% fido_cbor_info_fwversion 1 0 100.00% 3 0 100.00% fido_cbor_info_protocols_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_protocols_len 1 0 100.00% 3 0 100.00% fido_cbor_info_algorithm_count 1 0 100.00% 3 0 100.00% fido_cbor_info_algorithm_type 4 0 100.00% 5 0 100.00% fido_cbor_info_algorithm_cose 4 0 100.00% 5 0 100.00% info.c:fido_dev_get_cbor_info_tx 8 0 100.00% 9 0 100.00% info.c:fido_dev_get_cbor_info_rx 6 0 100.00% 14 0 100.00% info.c:parse_reply_element 19 0 100.00% 37 0 100.00% info.c:decode_string_array 12 0 100.00% 17 0 100.00% info.c:decode_string 4 0 100.00% 10 0 100.00% info.c:decode_aaguid 8 0 100.00% 10 0 100.00% info.c:decode_options 11 0 100.00% 15 0 100.00% info.c:decode_option 11 0 100.00% 17 0 100.00% info.c:decode_protocols 12 0 100.00% 17 0 100.00% info.c:decode_protocol 6 0 100.00% 12 0 100.00% info.c:decode_algorithms 12 0 100.00% 17 0 100.00% info.c:decode_algorithm 9 0 100.00% 17 0 100.00% info.c:decode_algorithm_entry 20 0 100.00% 27 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 184 0 100.00% 316 0 100.00% File '/libfido2/src/io.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_tx 13 0 100.00% 11 0 100.00% fido_rx 13 1 92.31% 14 3 78.57% fido_rx_cbor_status 8 0 100.00% 10 0 100.00% io.c:transport_tx 7 0 100.00% 10 0 100.00% io.c:tx_empty 9 0 100.00% 14 0 100.00% io.c:tx_pkt 7 0 100.00% 10 0 100.00% io.c:tx 13 0 100.00% 19 0 100.00% io.c:tx_preamble 16 1 93.75% 20 1 95.00% io.c:tx_frame 15 1 93.33% 18 1 94.44% io.c:transport_rx 7 0 100.00% 10 0 100.00% io.c:rx 40 2 95.00% 52 1 98.08% io.c:rx_preamble 23 2 91.30% 22 5 77.27% io.c:rx_frame 11 0 100.00% 11 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 182 7 96.15% 221 11 95.02% File '/libfido2/src/iso7816.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- iso7816_new 4 0 100.00% 16 0 100.00% iso7816_free 6 0 100.00% 7 0 100.00% iso7816_add 6 1 83.33% 8 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% 38 0 100.00% File '/libfido2/src/largeblob.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_dev_largeblob_get 26 2 92.31% 38 4 89.47% fido_dev_largeblob_set 27 0 100.00% 36 0 100.00% fido_dev_largeblob_remove 12 0 100.00% 18 0 100.00% fido_dev_largeblob_get_array 15 2 86.67% 27 4 85.19% fido_dev_largeblob_set_array 14 0 100.00% 19 0 100.00% largeblob.c:largeblob_get_array 32 0 100.00% 36 0 100.00% largeblob.c:get_chunklen 9 1 88.89% 9 0 100.00% largeblob.c:largeblob_get_tx 19 0 100.00% 24 0 100.00% largeblob.c:largeblob_get_rx 15 0 100.00% 21 0 100.00% largeblob.c:parse_largeblob_reply 8 0 100.00% 9 0 100.00% largeblob.c:largeblob_array_check 7 0 100.00% 16 0 100.00% largeblob.c:largeblob_array_digest 10 0 100.00% 9 0 100.00% largeblob.c:largeblob_array_load 14 2 85.71% 19 7 63.16% largeblob.c:largeblob_array_lookup 25 0 100.00% 33 0 100.00% largeblob.c:largeblob_decode 16 2 87.50% 16 6 62.50% largeblob.c:largeblob_do_decode 27 3 88.89% 30 5 83.33% largeblob.c:largeblob_decrypt 15 0 100.00% 24 0 100.00% largeblob.c:largeblob_aad 1 0 100.00% 10 0 100.00% largeblob.c:largeblob_reset 1 0 100.00% 5 0 100.00% largeblob.c:largeblob_encode 16 0 100.00% 21 0 100.00% largeblob.c:largeblob_new 1 0 100.00% 3 0 100.00% largeblob.c:largeblob_seal 20 0 100.00% 32 0 100.00% largeblob.c:largeblob_get_nonce 8 1 87.50% 16 3 81.25% largeblob.c:largeblob_free 6 0 100.00% 8 0 100.00% largeblob.c:largeblob_add 27 2 92.59% 35 3 91.43% largeblob.c:largeblob_drop 21 0 100.00% 27 0 100.00% largeblob.c:largeblob_set_array 54 2 96.30% 61 4 93.44% largeblob.c:largeblob_get_uv_token 19 0 100.00% 23 0 100.00% largeblob.c:largeblob_set_tx 35 0 100.00% 36 0 100.00% largeblob.c:prepare_hmac 13 2 84.62% 23 7 69.57% ------------------------------------------------------------------------------------------------------------------- TOTAL 513 19 96.30% 684 43 93.71% File '/libfido2/src/log.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_log_init 1 0 100.00% 4 0 100.00% fido_log_debug 6 1 83.33% 8 0 100.00% fido_log_xxd 16 1 93.75% 24 0 100.00% fido_log_error 8 2 75.00% 11 1 90.91% fido_set_log_handler 3 0 100.00% 4 0 100.00% log.c:log_on_stderr 1 1 0.00% 3 3 0.00% log.c:do_log 4 0 100.00% 9 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 39 5 87.18% 63 4 93.65% File '/libfido2/src/netlink.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_nl_power_nfc 18 1 94.44% 24 3 87.50% fido_nl_get_nfc_target 17 1 94.12% 31 3 90.32% fido_nl_free 10 2 80.00% 9 1 88.89% fido_nl_new 16 1 93.75% 26 3 88.46% set_netlink_io_functions 1 0 100.00% 4 0 100.00% netlink.c:nlmsg_new 8 0 100.00% 15 0 100.00% netlink.c:nlmsg_set_genl 1 0 100.00% 7 0 100.00% netlink.c:nlmsg_write 6 1 83.33% 7 1 85.71% netlink.c:nlmsg_set_u32 1 0 100.00% 3 0 100.00% netlink.c:nlmsg_setattr 14 1 92.86% 17 0 100.00% netlink.c:nlmsg_tx 10 1 90.00% 13 3 76.92% netlink.c:nlmsg_ptr 1 0 100.00% 3 0 100.00% netlink.c:nlmsg_len 1 0 100.00% 3 0 100.00% netlink.c:nlmsg_rx 11 3 72.73% 17 9 47.06% netlink.c:nl_parse_reply 20 0 100.00% 28 0 100.00% netlink.c:nlmsg_from_buf 15 0 100.00% 17 0 100.00% netlink.c:nlmsg_type 1 0 100.00% 3 0 100.00% netlink.c:nlmsg_get_status 8 0 100.00% 8 0 100.00% netlink.c:nlmsg_read 6 0 100.00% 7 0 100.00% netlink.c:nlmsg_get_genl 6 0 100.00% 7 0 100.00% netlink.c:nlmsg_iter 6 0 100.00% 13 0 100.00% netlink.c:nlmsg_getattr 1 0 100.00% 3 0 100.00% netlink.c:nla_from_buf 17 0 100.00% 21 0 100.00% netlink.c:nl_nfc_poll 18 1 94.44% 25 3 88.00% netlink.c:parse_nfc_event 10 0 100.00% 17 0 100.00% netlink.c:nla_type 1 0 100.00% 3 0 100.00% netlink.c:nla_get_u32 1 0 100.00% 3 0 100.00% netlink.c:nla_read 6 0 100.00% 7 0 100.00% netlink.c:nl_dump_nfc_target 19 1 94.74% 31 3 90.32% netlink.c:parse_target 9 0 100.00% 13 0 100.00% netlink.c:nl_get_nfc_family 23 1 95.65% 33 3 90.91% netlink.c:nlmsg_set_u16 1 0 100.00% 3 0 100.00% netlink.c:nlmsg_set_str 1 0 100.00% 3 0 100.00% netlink.c:parse_family 10 0 100.00% 17 0 100.00% netlink.c:nla_get_u16 1 0 100.00% 3 0 100.00% netlink.c:nla_iter 6 0 100.00% 13 0 100.00% netlink.c:nla_getattr 1 0 100.00% 3 0 100.00% netlink.c:parse_mcastgrps 1 0 100.00% 3 0 100.00% netlink.c:parse_mcastgrp 15 0 100.00% 24 0 100.00% netlink.c:nla_get_str 10 0 100.00% 11 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 328 14 95.73% 498 32 93.57% File '/libfido2/src/nfc_linux.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_nfc_tx 28 0 100.00% 43 0 100.00% fido_nfc_rx 8 1 87.50% 13 3 76.92% fido_nfc_manifest 35 5 85.71% 45 13 71.11% fido_nfc_open 20 3 85.00% 23 5 78.26% fido_nfc_close 1 1 0.00% 4 4 0.00% fido_nfc_set_sigmask 2 2 0.00% 6 6 0.00% fido_nfc_read 14 14 0.00% 30 30 0.00% fido_nfc_write 12 12 0.00% 18 18 0.00% nfc_linux.c:nfc_do_tx 20 2 90.00% 25 6 76.00% nfc_linux.c:tx_short_apdu 14 0 100.00% 32 0 100.00% nfc_linux.c:rx_init 25 6 76.00% 27 5 81.48% nfc_linux.c:rx_cbor 4 0 100.00% 6 0 100.00% nfc_linux.c:rx_msg 18 2 88.89% 23 6 73.91% nfc_linux.c:rx_apdu 14 1 92.86% 22 3 86.36% nfc_linux.c:tx_get_response 4 0 100.00% 11 0 100.00% nfc_linux.c:copy_info 41 9 78.05% 44 3 93.18% nfc_linux.c:get_usb_attr 1 0 100.00% 3 0 100.00% nfc_linux.c:get_parent_attr 6 0 100.00% 9 0 100.00% nfc_linux.c:to_int 21 6 71.43% 14 1 92.86% nfc_linux.c:sysnum_from_syspath 12 0 100.00% 17 0 100.00% nfc_linux.c:nfc_new 6 0 100.00% 11 0 100.00% nfc_linux.c:nfc_target_connect 9 9 0.00% 21 21 0.00% nfc_linux.c:nfc_free 12 0 100.00% 11 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 327 73 77.68% 458 124 72.93% File '/libfido2/src/pin.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- fido_sha256 7 0 100.00% 10 0 100.00% fido_dev_get_uv_token 1 0 100.00% 3 0 100.00% fido_dev_set_pin 1 0 100.00% 4 0 100.00% fido_dev_get_retry_count 1 0 100.00% 4 0 100.00% fido_dev_get_uv_retry_count 1 0 100.00% 4 0 100.00% cbor_add_uv_params 17 0 100.00% 23 0 100.00% pin.c:uv_token_wait 14 2 85.71% 12 0 100.00% pin.c:ctap21_uv_token_tx 49 0 100.00% 53 0 100.00% pin.c:pin_sha256_enc 19 0 100.00% 24 0 100.00% pin.c:encode_uv_permission 20 1 95.00% 19 3 84.21% pin.c:ctap20_uv_token_tx 37 0 100.00% 45 0 100.00% pin.c:uv_token_rx 20 0 100.00% 30 0 100.00% pin.c:parse_uv_token 8 0 100.00% 10 0 100.00% pin.c:fido_dev_set_pin_wait 21 0 100.00% 24 0 100.00% pin.c:fido_dev_change_pin_tx 45 0 100.00% 56 0 100.00% pin.c:pin_pad64_enc 15 0 100.00% 21 0 100.00% pin.c:pad64 18 0 100.00% 19 0 100.00% pin.c:fido_dev_set_pin_tx 33 0 100.00% 41 0 100.00% pin.c:fido_dev_get_pin_retry_count_wait 10 0 100.00% 7 0 100.00% pin.c:fido_dev_get_retry_count_tx 19 0 100.00% 23 0 100.00% pin.c:fido_dev_get_pin_retry_count_rx 11 0 100.00% 17 0 100.00% pin.c:parse_pin_retry_count 1 0 100.00% 3 0 100.00% pin.c:parse_retry_count 13 0 100.00% 16 0 100.00% pin.c:fido_dev_get_uv_retry_count_wait 10 0 100.00% 7 0 100.00% pin.c:fido_dev_get_uv_retry_count_rx 11 0 100.00% 17 0 100.00% pin.c:parse_uv_retry_count 1 0 100.00% 3 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 403 3 99.26% 495 3 99.39% File '/libfido2/src/random.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- fido_get_random 6 1 83.33% 6 1 83.33% --------------------------------------------------------------------------------------------------------------------- TOTAL 6 1 83.33% 6 1 83.33% File '/libfido2/src/reset.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- fido_dev_reset 1 0 100.00% 4 0 100.00% reset.c:fido_dev_reset_wait 15 0 100.00% 11 0 100.00% reset.c:fido_dev_reset_tx 8 0 100.00% 8 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 24 0 100.00% 23 0 100.00% File '/libfido2/src/rs1.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- rs1_verify_sig 20 0 100.00% 30 0 100.00% rs1.c:rs1_get_EVP_MD 4 0 100.00% 6 0 100.00% rs1.c:rs1_free_EVP_MD 1 0 100.00% 3 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 25 0 100.00% 39 0 100.00% File '/libfido2/src/rs256.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- rs256_pk_decode 8 0 100.00% 9 0 100.00% rs256_pk_new 1 0 100.00% 3 0 100.00% rs256_pk_free 6 0 100.00% 7 0 100.00% rs256_pk_from_ptr 6 0 100.00% 6 0 100.00% rs256_pk_to_EVP_PKEY 32 0 100.00% 39 0 100.00% rs256_pk_from_RSA 32 4 87.50% 26 6 76.92% rs256_pk_from_EVP_PKEY 7 2 71.43% 7 0 100.00% rs256_verify_sig 20 1 95.00% 30 2 93.33% rs256_pk_verify_sig 7 1 85.71% 13 2 84.62% rs256.c:decode_rsa_pubkey 9 0 100.00% 13 0 100.00% rs256.c:decode_bignum 8 0 100.00% 10 0 100.00% rs256.c:rs256_get_EVP_MD 4 0 100.00% 6 0 100.00% rs256.c:rs256_free_EVP_MD 1 0 100.00% 3 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 141 8 94.33% 172 10 94.19% File '/libfido2/src/time.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- fido_time_now 4 0 100.00% 7 0 100.00% fido_time_delta 23 1 95.65% 23 0 100.00% time.c:timespec_to_ms 16 2 87.50% 13 1 92.31% --------------------------------------------------------------------------------------------------------------------- TOTAL 43 3 93.02% 43 1 97.67% File '/libfido2/src/tpm.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- fido_get_signed_hash_tpm 25 0 100.00% 39 0 100.00% tpm.c:check_es256_pubarea 18 0 100.00% 30 0 100.00% tpm.c:bswap_es256_pubarea 1 0 100.00% 12 0 100.00% tpm.c:check_rs256_pubarea 16 0 100.00% 28 0 100.00% tpm.c:bswap_rs256_pubarea 1 0 100.00% 10 0 100.00% tpm.c:check_sha1_certinfo 14 0 100.00% 38 0 100.00% tpm.c:get_signed_sha1 17 0 100.00% 19 0 100.00% tpm.c:get_signed_name 7 0 100.00% 10 0 100.00% tpm.c:bswap_sha1_certinfo 1 0 100.00% 8 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 100 0 100.00% 194 0 100.00% File '/libfido2/src/types.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- fido_str_array_free 4 0 100.00% 7 0 100.00% fido_opt_array_free 4 0 100.00% 8 0 100.00% fido_byte_array_free 1 0 100.00% 5 0 100.00% fido_algo_free 1 0 100.00% 5 0 100.00% fido_algo_array_free 4 0 100.00% 7 0 100.00% fido_str_array_pack 11 0 100.00% 14 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 25 0 100.00% 46 0 100.00% File '/libfido2/src/u2f.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- u2f_register 69 0 100.00% 75 0 100.00% u2f_authenticate 32 0 100.00% 36 0 100.00% u2f_get_touch_begin 30 0 100.00% 39 0 100.00% u2f_get_touch_status 18 0 100.00% 26 0 100.00% u2f.c:key_lookup 44 0 100.00% 59 0 100.00% u2f.c:send_dummy_register 30 0 100.00% 39 0 100.00% u2f.c:delay_ms 13 1 92.31% 15 3 80.00% u2f.c:parse_register_reply 49 0 100.00% 62 0 100.00% u2f.c:x5c_get 21 1 95.24% 26 3 88.46% u2f.c:sig_get 6 0 100.00% 10 0 100.00% u2f.c:encode_cred_attstmt 45 0 100.00% 52 0 100.00% u2f.c:encode_cred_authdata 33 2 93.94% 61 6 90.16% u2f.c:cbor_blob_from_ec_point 22 0 100.00% 31 0 100.00% u2f.c:u2f_authenticate_single 32 0 100.00% 43 0 100.00% u2f.c:do_auth 49 0 100.00% 61 0 100.00% u2f.c:parse_auth_reply 23 0 100.00% 23 0 100.00% u2f.c:authdata_fake 12 0 100.00% 27 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 528 4 99.24% 685 12 98.25% libfido2-1.10.0/fuzz/fuzz_assert.c000066400000000000000000000300671417126203300170110ustar00rootroot00000000000000/* * 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 "wiredata_fido2.h" #include "wiredata_u2f.h" #include "dummy.h" #include "../openbsd-compat/openbsd-compat.h" /* Parameter set defining a FIDO2 get assertion operation. */ struct param { char pin[MAXSTR]; char rp_id[MAXSTR]; int ext; int seed; struct blob cdh; struct blob cred; struct blob es256; struct blob rs256; struct blob eddsa; struct blob wire_data; uint8_t cred_count; uint8_t type; uint8_t opt; uint8_t up; uint8_t uv; }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * get assertion using the example parameters above. */ static const uint8_t dummy_wire_data_fido[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_ASSERT, }; /* * Collection of HID reports from an authenticator issued with a U2F * authentication using the example parameters above. */ static const uint8_t dummy_wire_data_u2f[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_AUTH, }; struct param * unpack(const uint8_t *ptr, size_t len) { cbor_item_t *item = NULL, **v; struct cbor_load_result cbor; struct param *p; int ok = -1; if ((p = calloc(1, sizeof(*p))) == NULL || (item = cbor_load(ptr, len, &cbor)) == NULL || cbor.read != len || cbor_isa_array(item) == false || cbor_array_is_definite(item) == false || cbor_array_size(item) != 15 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_byte(v[0], &p->uv) < 0 || unpack_byte(v[1], &p->up) < 0 || unpack_byte(v[2], &p->opt) < 0 || unpack_byte(v[3], &p->type) < 0 || unpack_byte(v[4], &p->cred_count) < 0 || unpack_int(v[5], &p->ext) < 0 || unpack_int(v[6], &p->seed) < 0 || unpack_string(v[7], p->rp_id) < 0 || unpack_string(v[8], p->pin) < 0 || unpack_blob(v[9], &p->wire_data) < 0 || unpack_blob(v[10], &p->rs256) < 0 || unpack_blob(v[11], &p->es256) < 0 || unpack_blob(v[12], &p->eddsa) < 0 || unpack_blob(v[13], &p->cred) < 0 || unpack_blob(v[14], &p->cdh) < 0) goto fail; ok = 0; fail: if (ok < 0) { free(p); p = NULL; } if (item) cbor_decref(&item); return p; } size_t pack(uint8_t *ptr, size_t len, const struct param *p) { cbor_item_t *argv[15], *array = NULL; size_t cbor_alloc_len, cbor_len = 0; unsigned char *cbor = NULL; memset(argv, 0, sizeof(argv)); if ((array = cbor_new_definite_array(15)) == NULL || (argv[0] = pack_byte(p->uv)) == NULL || (argv[1] = pack_byte(p->up)) == NULL || (argv[2] = pack_byte(p->opt)) == NULL || (argv[3] = pack_byte(p->type)) == NULL || (argv[4] = pack_byte(p->cred_count)) == NULL || (argv[5] = pack_int(p->ext)) == NULL || (argv[6] = pack_int(p->seed)) == NULL || (argv[7] = pack_string(p->rp_id)) == NULL || (argv[8] = pack_string(p->pin)) == NULL || (argv[9] = pack_blob(&p->wire_data)) == NULL || (argv[10] = pack_blob(&p->rs256)) == NULL || (argv[11] = pack_blob(&p->es256)) == NULL || (argv[12] = pack_blob(&p->eddsa)) == NULL || (argv[13] = pack_blob(&p->cred)) == NULL || (argv[14] = pack_blob(&p->cdh)) == NULL) goto fail; for (size_t i = 0; i < 15; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 15; i++) if (argv[i]) cbor_decref(&argv[i]); if (array) cbor_decref(&array); free(cbor); return cbor_len; } size_t pack_dummy(uint8_t *ptr, size_t len) { struct param dummy; uint8_t blob[4096]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); dummy.type = 1; /* rsa */ dummy.ext = FIDO_EXT_HMAC_SECRET; strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin)); strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id)); dummy.cred.len = sizeof(dummy_cdh); /* XXX */ dummy.cdh.len = sizeof(dummy_cdh); dummy.es256.len = sizeof(dummy_es256); dummy.rs256.len = sizeof(dummy_rs256); dummy.eddsa.len = sizeof(dummy_eddsa); dummy.wire_data.len = sizeof(dummy_wire_data_fido); memcpy(&dummy.cred.body, &dummy_cdh, dummy.cred.len); /* XXX */ memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len); memcpy(&dummy.wire_data.body, &dummy_wire_data_fido, dummy.wire_data.len); memcpy(&dummy.es256.body, &dummy_es256, dummy.es256.len); memcpy(&dummy.rs256.body, &dummy_rs256, dummy.rs256.len); memcpy(&dummy.eddsa.body, &dummy_eddsa, dummy.eddsa.len); assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); if (blob_len > len) { memcpy(ptr, blob, len); return len; } memcpy(ptr, blob, blob_len); return blob_len; } static void get_assert(fido_assert_t *assert, uint8_t opt, const struct blob *cdh, const char *rp_id, int ext, uint8_t up, uint8_t uv, const char *pin, uint8_t cred_count, const struct blob *cred) { fido_dev_t *dev; if ((dev = open_dev(opt & 2)) == NULL) return; if (opt & 1) fido_dev_force_u2f(dev); if (ext & FIDO_EXT_HMAC_SECRET) fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET); if (ext & FIDO_EXT_CRED_BLOB) fido_assert_set_extensions(assert, FIDO_EXT_CRED_BLOB); if (ext & FIDO_EXT_LARGEBLOB_KEY) fido_assert_set_extensions(assert, FIDO_EXT_LARGEBLOB_KEY); if (up & 1) fido_assert_set_up(assert, FIDO_OPT_TRUE); else if (opt & 1) fido_assert_set_up(assert, FIDO_OPT_FALSE); if (uv & 1) fido_assert_set_uv(assert, FIDO_OPT_TRUE); for (uint8_t i = 0; i < cred_count; i++) fido_assert_allow_cred(assert, cred->body, cred->len); fido_assert_set_clientdata_hash(assert, cdh->body, cdh->len); fido_assert_set_rp(assert, rp_id); /* XXX reuse cred as hmac salt */ fido_assert_set_hmac_salt(assert, cred->body, cred->len); /* repeat memory operations to trigger reallocation paths */ fido_assert_set_clientdata_hash(assert, cdh->body, cdh->len); fido_assert_set_rp(assert, rp_id); fido_assert_set_hmac_salt(assert, cred->body, cred->len); if (strlen(pin) == 0) pin = NULL; fido_dev_get_assert(dev, assert, (opt & 1) ? NULL : pin); fido_dev_cancel(dev); fido_dev_close(dev); fido_dev_free(&dev); } static void verify_assert(int type, const unsigned char *cdh_ptr, size_t cdh_len, const char *rp_id, const unsigned char *authdata_ptr, size_t authdata_len, const unsigned char *sig_ptr, size_t sig_len, uint8_t up, uint8_t uv, int ext, void *pk) { fido_assert_t *assert = NULL; int r; if ((assert = fido_assert_new()) == NULL) return; fido_assert_set_clientdata_hash(assert, cdh_ptr, cdh_len); fido_assert_set_rp(assert, rp_id); fido_assert_set_count(assert, 1); if (fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len) != FIDO_OK) { fido_assert_set_authdata_raw(assert, 0, authdata_ptr, authdata_len); } if (up & 1) fido_assert_set_up(assert, FIDO_OPT_TRUE); if (uv & 1) fido_assert_set_uv(assert, FIDO_OPT_TRUE); fido_assert_set_extensions(assert, ext); fido_assert_set_sig(assert, 0, sig_ptr, sig_len); /* repeat memory operations to trigger reallocation paths */ if (fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len) != FIDO_OK) { fido_assert_set_authdata_raw(assert, 0, authdata_ptr, authdata_len); } fido_assert_set_sig(assert, 0, sig_ptr, sig_len); r = fido_assert_verify(assert, 0, type, pk); consume(&r, sizeof(r)); fido_assert_free(&assert); } /* * Do a dummy conversion to exercise es256_pk_from_EVP_PKEY(). */ static void es256_convert(const es256_pk_t *k) { EVP_PKEY *pkey = NULL; es256_pk_t *pk = NULL; int r; if ((pkey = es256_pk_to_EVP_PKEY(k)) == NULL || (pk = es256_pk_new()) == NULL) goto out; r = es256_pk_from_EVP_PKEY(pk, pkey); consume(&r, sizeof(r)); out: es256_pk_free(&pk); EVP_PKEY_free(pkey); } /* * Do a dummy conversion to exercise rs256_pk_from_EVP_PKEY(). */ static void rs256_convert(const rs256_pk_t *k) { EVP_PKEY *pkey = NULL; rs256_pk_t *pk = NULL; int r; if ((pkey = rs256_pk_to_EVP_PKEY(k)) == NULL || (pk = rs256_pk_new()) == NULL) goto out; r = rs256_pk_from_EVP_PKEY(pk, pkey); consume(&r, sizeof(r)); out: rs256_pk_free(&pk); 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; 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); consume(&r, sizeof(r)); out: if (pk) eddsa_pk_free(&pk); if (pkey) EVP_PKEY_free(pkey); } void test(const struct param *p) { fido_assert_t *assert = NULL; es256_pk_t *es256_pk = NULL; rs256_pk_t *rs256_pk = NULL; eddsa_pk_t *eddsa_pk = NULL; uint8_t flags; uint32_t sigcount; int cose_alg = 0; void *pk; prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); switch (p->type & 3) { case 0: cose_alg = COSE_ES256; if ((es256_pk = es256_pk_new()) == NULL) return; es256_pk_from_ptr(es256_pk, p->es256.body, p->es256.len); pk = es256_pk; es256_convert(pk); break; case 1: cose_alg = COSE_RS256; if ((rs256_pk = rs256_pk_new()) == NULL) return; rs256_pk_from_ptr(rs256_pk, p->rs256.body, p->rs256.len); pk = rs256_pk; rs256_convert(pk); break; default: cose_alg = COSE_EDDSA; if ((eddsa_pk = eddsa_pk_new()) == NULL) return; eddsa_pk_from_ptr(eddsa_pk, p->eddsa.body, p->eddsa.len); pk = eddsa_pk; eddsa_convert(pk); break; } if ((assert = fido_assert_new()) == NULL) goto out; set_wire_data(p->wire_data.body, p->wire_data.len); get_assert(assert, p->opt, &p->cdh, p->rp_id, p->ext, p->up, p->uv, p->pin, p->cred_count, &p->cred); /* XXX +1 on purpose */ for (size_t i = 0; i <= fido_assert_count(assert); i++) { verify_assert(cose_alg, fido_assert_clientdata_hash_ptr(assert), fido_assert_clientdata_hash_len(assert), fido_assert_rp_id(assert), fido_assert_authdata_ptr(assert, i), fido_assert_authdata_len(assert, i), fido_assert_sig_ptr(assert, i), fido_assert_sig_len(assert, i), p->up, p->uv, p->ext, pk); consume(fido_assert_id_ptr(assert, i), fido_assert_id_len(assert, i)); consume(fido_assert_user_id_ptr(assert, i), fido_assert_user_id_len(assert, i)); consume(fido_assert_hmac_secret_ptr(assert, i), fido_assert_hmac_secret_len(assert, i)); consume_str(fido_assert_user_icon(assert, i)); consume_str(fido_assert_user_name(assert, i)); consume_str(fido_assert_user_display_name(assert, i)); consume(fido_assert_blob_ptr(assert, i), fido_assert_blob_len(assert, i)); consume(fido_assert_largeblob_key_ptr(assert, i), fido_assert_largeblob_key_len(assert, i)); flags = fido_assert_flags(assert, i); consume(&flags, sizeof(flags)); sigcount = fido_assert_sigcount(assert, i); consume(&sigcount, sizeof(sigcount)); } out: es256_pk_free(&es256_pk); rs256_pk_free(&rs256_pk); eddsa_pk_free(&eddsa_pk); fido_assert_free(&assert); } void mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN { if (flags & MUTATE_SEED) p->seed = (int)seed; if (flags & MUTATE_PARAM) { mutate_byte(&p->uv); mutate_byte(&p->up); mutate_byte(&p->opt); mutate_byte(&p->type); mutate_byte(&p->cred_count); mutate_int(&p->ext); mutate_blob(&p->rs256); mutate_blob(&p->es256); mutate_blob(&p->eddsa); mutate_blob(&p->cred); mutate_blob(&p->cdh); mutate_string(p->rp_id); mutate_string(p->pin); } if (flags & MUTATE_WIREDATA) { if (p->opt & 1) { p->wire_data.len = sizeof(dummy_wire_data_u2f); memcpy(&p->wire_data.body, &dummy_wire_data_u2f, p->wire_data.len); } else { p->wire_data.len = sizeof(dummy_wire_data_fido); memcpy(&p->wire_data.body, &dummy_wire_data_fido, p->wire_data.len); } mutate_blob(&p->wire_data); } } libfido2-1.10.0/fuzz/fuzz_bio.c000066400000000000000000000240341417126203300162560ustar00rootroot00000000000000/* * 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 "wiredata_fido2.h" #include "dummy.h" #include "../openbsd-compat/openbsd-compat.h" /* Parameter set defining a FIDO2 credential management operation. */ struct param { char pin[MAXSTR]; char name[MAXSTR]; int seed; struct blob id; struct blob info_wire_data; struct blob enroll_wire_data; struct blob list_wire_data; struct blob set_name_wire_data; struct blob remove_wire_data; }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'getFingerprintSensorInfo' bio enrollment command. */ static const uint8_t dummy_info_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_BIO_INFO, }; /* * Collection of HID reports from an authenticator issued with FIDO2 * 'enrollBegin' + 'enrollCaptureNextSample' bio enrollment commands. */ static const uint8_t dummy_enroll_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_BIO_ENROLL, }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'enumerateEnrollments' bio enrollment command. */ static const uint8_t dummy_list_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_BIO_ENUM, }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'setFriendlyName' bio enrollment command. */ static const uint8_t dummy_set_name_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_STATUS, }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'removeEnrollment' bio enrollment command. */ static const uint8_t dummy_remove_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_STATUS, }; struct param * unpack(const uint8_t *ptr, size_t len) { cbor_item_t *item = NULL, **v; struct cbor_load_result cbor; struct param *p; int ok = -1; if ((p = calloc(1, sizeof(*p))) == NULL || (item = cbor_load(ptr, len, &cbor)) == NULL || cbor.read != len || cbor_isa_array(item) == false || cbor_array_is_definite(item) == false || cbor_array_size(item) != 9 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_int(v[0], &p->seed) < 0 || unpack_string(v[1], p->pin) < 0 || unpack_string(v[2], p->name) < 0 || unpack_blob(v[3], &p->id) < 0 || unpack_blob(v[4], &p->info_wire_data) < 0 || unpack_blob(v[5], &p->enroll_wire_data) < 0 || unpack_blob(v[6], &p->list_wire_data) < 0 || unpack_blob(v[7], &p->set_name_wire_data) < 0 || unpack_blob(v[8], &p->remove_wire_data) < 0) goto fail; ok = 0; fail: if (ok < 0) { free(p); p = NULL; } if (item) cbor_decref(&item); return p; } size_t pack(uint8_t *ptr, size_t len, const struct param *p) { cbor_item_t *argv[9], *array = NULL; size_t cbor_alloc_len, cbor_len = 0; unsigned char *cbor = NULL; memset(argv, 0, sizeof(argv)); if ((array = cbor_new_definite_array(9)) == NULL || (argv[0] = pack_int(p->seed)) == NULL || (argv[1] = pack_string(p->pin)) == NULL || (argv[2] = pack_string(p->name)) == NULL || (argv[3] = pack_blob(&p->id)) == NULL || (argv[4] = pack_blob(&p->info_wire_data)) == NULL || (argv[5] = pack_blob(&p->enroll_wire_data)) == NULL || (argv[6] = pack_blob(&p->list_wire_data)) == NULL || (argv[7] = pack_blob(&p->set_name_wire_data)) == NULL || (argv[8] = pack_blob(&p->remove_wire_data)) == NULL) goto fail; for (size_t i = 0; i < 9; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 9; i++) if (argv[i]) cbor_decref(&argv[i]); if (array) cbor_decref(&array); free(cbor); return cbor_len; } size_t pack_dummy(uint8_t *ptr, size_t len) { struct param dummy; uint8_t blob[4096]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin)); strlcpy(dummy.name, dummy_name, sizeof(dummy.name)); dummy.info_wire_data.len = sizeof(dummy_info_wire_data); dummy.enroll_wire_data.len = sizeof(dummy_enroll_wire_data); dummy.list_wire_data.len = sizeof(dummy_list_wire_data); dummy.set_name_wire_data.len = sizeof(dummy_set_name_wire_data); dummy.remove_wire_data.len = sizeof(dummy_remove_wire_data); dummy.id.len = sizeof(dummy_id); memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data, dummy.info_wire_data.len); memcpy(&dummy.enroll_wire_data.body, &dummy_enroll_wire_data, dummy.enroll_wire_data.len); memcpy(&dummy.list_wire_data.body, &dummy_list_wire_data, dummy.list_wire_data.len); memcpy(&dummy.set_name_wire_data.body, &dummy_set_name_wire_data, dummy.set_name_wire_data.len); memcpy(&dummy.remove_wire_data.body, &dummy_remove_wire_data, dummy.remove_wire_data.len); memcpy(&dummy.id.body, &dummy_id, dummy.id.len); assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); if (blob_len > len) { memcpy(ptr, blob, len); return len; } memcpy(ptr, blob, blob_len); return blob_len; } static fido_dev_t * prepare_dev(void) { fido_dev_t *dev; bool x; if ((dev = open_dev(0)) == NULL) return NULL; x = fido_dev_is_fido2(dev); consume(&x, sizeof(x)); x = fido_dev_supports_pin(dev); consume(&x, sizeof(x)); x = fido_dev_has_pin(dev); consume(&x, sizeof(x)); x = fido_dev_supports_uv(dev); consume(&x, sizeof(x)); x = fido_dev_has_uv(dev); consume(&x, sizeof(x)); return dev; } static void get_info(const struct param *p) { fido_dev_t *dev = NULL; fido_bio_info_t *i = NULL; uint8_t type; uint8_t max_samples; int r; set_wire_data(p->info_wire_data.body, p->info_wire_data.len); if ((dev = prepare_dev()) == NULL || (i = fido_bio_info_new()) == NULL) goto done; r = fido_bio_dev_get_info(dev, i); consume_str(fido_strerr(r)); type = fido_bio_info_type(i); max_samples = fido_bio_info_max_samples(i); consume(&type, sizeof(type)); consume(&max_samples, sizeof(max_samples)); done: if (dev) fido_dev_close(dev); fido_dev_free(&dev); fido_bio_info_free(&i); } static void consume_template(const fido_bio_template_t *t) { consume_str(fido_bio_template_name(t)); consume(fido_bio_template_id_ptr(t), fido_bio_template_id_len(t)); } static void consume_enroll(fido_bio_enroll_t *e) { uint8_t last_status; uint8_t remaining_samples; last_status = fido_bio_enroll_last_status(e); remaining_samples = fido_bio_enroll_remaining_samples(e); consume(&last_status, sizeof(last_status)); consume(&remaining_samples, sizeof(remaining_samples)); } static void enroll(const struct param *p) { fido_dev_t *dev = NULL; fido_bio_template_t *t = NULL; fido_bio_enroll_t *e = NULL; size_t cnt = 0; set_wire_data(p->enroll_wire_data.body, p->enroll_wire_data.len); if ((dev = prepare_dev()) == NULL || (t = fido_bio_template_new()) == NULL || (e = fido_bio_enroll_new()) == NULL) goto done; fido_bio_dev_enroll_begin(dev, t, e, (uint32_t)p->seed, p->pin); consume_template(t); consume_enroll(e); while (fido_bio_enroll_remaining_samples(e) > 0 && cnt++ < 5) { fido_bio_dev_enroll_continue(dev, t, e, p->seed); consume_template(t); consume_enroll(e); } done: if (dev) fido_dev_close(dev); fido_dev_free(&dev); fido_bio_template_free(&t); fido_bio_enroll_free(&e); } static void list(const struct param *p) { fido_dev_t *dev = NULL; fido_bio_template_array_t *ta = NULL; const fido_bio_template_t *t = NULL; set_wire_data(p->list_wire_data.body, p->list_wire_data.len); if ((dev = prepare_dev()) == NULL || (ta = fido_bio_template_array_new()) == NULL) goto done; fido_bio_dev_get_template_array(dev, ta, p->pin); /* +1 on purpose */ for (size_t i = 0; i < fido_bio_template_array_count(ta) + 1; i++) if ((t = fido_bio_template(ta, i)) != NULL) consume_template(t); done: if (dev) fido_dev_close(dev); fido_dev_free(&dev); fido_bio_template_array_free(&ta); } static void set_name(const struct param *p) { fido_dev_t *dev = NULL; fido_bio_template_t *t = NULL; set_wire_data(p->set_name_wire_data.body, p->set_name_wire_data.len); if ((dev = prepare_dev()) == NULL || (t = fido_bio_template_new()) == NULL) goto done; fido_bio_template_set_name(t, p->name); fido_bio_template_set_id(t, p->id.body, p->id.len); consume_template(t); fido_bio_dev_set_template_name(dev, t, p->pin); done: if (dev) fido_dev_close(dev); fido_dev_free(&dev); fido_bio_template_free(&t); } static void del(const struct param *p) { fido_dev_t *dev = NULL; fido_bio_template_t *t = NULL; int r; set_wire_data(p->remove_wire_data.body, p->remove_wire_data.len); if ((dev = prepare_dev()) == NULL || (t = fido_bio_template_new()) == NULL) goto done; r = fido_bio_template_set_id(t, p->id.body, p->id.len); consume_template(t); consume_str(fido_strerr(r)); fido_bio_dev_enroll_remove(dev, t, p->pin); done: if (dev) fido_dev_close(dev); fido_dev_free(&dev); fido_bio_template_free(&t); } void test(const struct param *p) { prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); get_info(p); enroll(p); list(p); set_name(p); del(p); } void mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN { if (flags & MUTATE_SEED) p->seed = (int)seed; if (flags & MUTATE_PARAM) { mutate_blob(&p->id); mutate_string(p->pin); mutate_string(p->name); } if (flags & MUTATE_WIREDATA) { mutate_blob(&p->info_wire_data); mutate_blob(&p->enroll_wire_data); mutate_blob(&p->list_wire_data); mutate_blob(&p->set_name_wire_data); mutate_blob(&p->remove_wire_data); } } libfido2-1.10.0/fuzz/fuzz_cred.c000066400000000000000000000315261417126203300164260ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "mutator_aux.h" #include "wiredata_fido2.h" #include "wiredata_u2f.h" #include "dummy.h" #include "../openbsd-compat/openbsd-compat.h" /* Parameter set defining a FIDO2 make credential operation. */ struct param { char pin[MAXSTR]; char rp_id[MAXSTR]; char rp_name[MAXSTR]; char user_icon[MAXSTR]; char user_name[MAXSTR]; char user_nick[MAXSTR]; int ext; int seed; struct blob cdh; struct blob excl_cred; struct blob user_id; struct blob wire_data; uint8_t excl_count; uint8_t rk; uint8_t type; uint8_t opt; uint8_t uv; }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * make credential using the example parameters above. */ static const uint8_t dummy_wire_data_fido[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_CBOR_CRED, }; /* * Collection of HID reports from an authenticator issued with a U2F * registration using the example parameters above. */ static const uint8_t dummy_wire_data_u2f[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_6985, WIREDATA_CTAP_U2F_REGISTER, }; struct param * unpack(const uint8_t *ptr, size_t len) { cbor_item_t *item = NULL, **v; struct cbor_load_result cbor; struct param *p; int ok = -1; if ((p = calloc(1, sizeof(*p))) == NULL || (item = cbor_load(ptr, len, &cbor)) == NULL || cbor.read != len || cbor_isa_array(item) == false || cbor_array_is_definite(item) == false || cbor_array_size(item) != 17 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_byte(v[0], &p->rk) < 0 || unpack_byte(v[1], &p->type) < 0 || unpack_byte(v[2], &p->opt) < 0 || unpack_byte(v[3], &p->uv) < 0 || unpack_byte(v[4], &p->excl_count) < 0 || unpack_int(v[5], &p->ext) < 0 || unpack_int(v[6], &p->seed) < 0 || unpack_string(v[7], p->pin) < 0 || unpack_string(v[8], p->rp_id) < 0 || unpack_string(v[9], p->rp_name) < 0 || unpack_string(v[10], p->user_icon) < 0 || unpack_string(v[11], p->user_name) < 0 || unpack_string(v[12], p->user_nick) < 0 || unpack_blob(v[13], &p->cdh) < 0 || unpack_blob(v[14], &p->user_id) < 0 || unpack_blob(v[15], &p->wire_data) < 0 || unpack_blob(v[16], &p->excl_cred) < 0) goto fail; ok = 0; fail: if (ok < 0) { free(p); p = NULL; } if (item) cbor_decref(&item); return p; } size_t pack(uint8_t *ptr, size_t len, const struct param *p) { cbor_item_t *argv[17], *array = NULL; size_t cbor_alloc_len, cbor_len = 0; unsigned char *cbor = NULL; memset(argv, 0, sizeof(argv)); if ((array = cbor_new_definite_array(17)) == NULL || (argv[0] = pack_byte(p->rk)) == NULL || (argv[1] = pack_byte(p->type)) == NULL || (argv[2] = pack_byte(p->opt)) == NULL || (argv[3] = pack_byte(p->uv)) == NULL || (argv[4] = pack_byte(p->excl_count)) == NULL || (argv[5] = pack_int(p->ext)) == NULL || (argv[6] = pack_int(p->seed)) == NULL || (argv[7] = pack_string(p->pin)) == NULL || (argv[8] = pack_string(p->rp_id)) == NULL || (argv[9] = pack_string(p->rp_name)) == NULL || (argv[10] = pack_string(p->user_icon)) == NULL || (argv[11] = pack_string(p->user_name)) == NULL || (argv[12] = pack_string(p->user_nick)) == NULL || (argv[13] = pack_blob(&p->cdh)) == NULL || (argv[14] = pack_blob(&p->user_id)) == NULL || (argv[15] = pack_blob(&p->wire_data)) == NULL || (argv[16] = pack_blob(&p->excl_cred)) == NULL) goto fail; for (size_t i = 0; i < 17; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 17; i++) if (argv[i]) cbor_decref(&argv[i]); if (array) cbor_decref(&array); free(cbor); return cbor_len; } size_t pack_dummy(uint8_t *ptr, size_t len) { struct param dummy; uint8_t blob[4096]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); dummy.type = 1; dummy.ext = FIDO_EXT_HMAC_SECRET; strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin)); strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id)); strlcpy(dummy.rp_name, dummy_rp_name, sizeof(dummy.rp_name)); strlcpy(dummy.user_icon, dummy_user_icon, sizeof(dummy.user_icon)); strlcpy(dummy.user_name, dummy_user_name, sizeof(dummy.user_name)); strlcpy(dummy.user_nick, dummy_user_nick, sizeof(dummy.user_nick)); dummy.cdh.len = sizeof(dummy_cdh); dummy.user_id.len = sizeof(dummy_user_id); dummy.wire_data.len = sizeof(dummy_wire_data_fido); memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len); memcpy(&dummy.user_id.body, &dummy_user_id, dummy.user_id.len); memcpy(&dummy.wire_data.body, &dummy_wire_data_fido, dummy.wire_data.len); assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); if (blob_len > len) { memcpy(ptr, blob, len); return len; } memcpy(ptr, blob, blob_len); return blob_len; } static void make_cred(fido_cred_t *cred, uint8_t opt, int type, const struct blob *cdh, const char *rp_id, const char *rp_name, const struct blob *user_id, const char *user_name, const char *user_nick, const char *user_icon, int ext, uint8_t rk, uint8_t uv, const char *pin, uint8_t excl_count, const struct blob *excl_cred) { fido_dev_t *dev; if ((dev = open_dev(opt & 2)) == NULL) return; if (opt & 1) fido_dev_force_u2f(dev); for (uint8_t i = 0; i < excl_count; i++) fido_cred_exclude(cred, excl_cred->body, excl_cred->len); fido_cred_set_type(cred, type); fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len); fido_cred_set_rp(cred, rp_id, rp_name); fido_cred_set_user(cred, user_id->body, user_id->len, user_name, user_nick, user_icon); if (ext & FIDO_EXT_HMAC_SECRET) fido_cred_set_extensions(cred, FIDO_EXT_HMAC_SECRET); if (ext & FIDO_EXT_CRED_BLOB) fido_cred_set_blob(cred, user_id->body, user_id->len); if (ext & FIDO_EXT_LARGEBLOB_KEY) fido_cred_set_extensions(cred, FIDO_EXT_LARGEBLOB_KEY); if (ext & FIDO_EXT_MINPINLEN) fido_cred_set_pin_minlen(cred, strlen(pin)); if (rk & 1) fido_cred_set_rk(cred, FIDO_OPT_TRUE); if (uv & 1) fido_cred_set_uv(cred, FIDO_OPT_TRUE); if (user_id->len) fido_cred_set_prot(cred, user_id->body[0] & 0x03); /* repeat memory operations to trigger reallocation paths */ fido_cred_set_type(cred, type); fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len); fido_cred_set_rp(cred, rp_id, rp_name); fido_cred_set_user(cred, user_id->body, user_id->len, user_name, user_nick, user_icon); if (strlen(pin) == 0) pin = NULL; fido_dev_make_cred(dev, cred, (opt & 1) ? NULL : pin); fido_dev_cancel(dev); fido_dev_close(dev); fido_dev_free(&dev); } static void verify_cred(int type, const unsigned char *cdh_ptr, size_t cdh_len, const char *rp_id, const char *rp_name, const unsigned char *authdata_ptr, size_t authdata_len, const unsigned char *authdata_raw_ptr, size_t authdata_raw_len, int ext, uint8_t rk, uint8_t uv, const unsigned char *x5c_ptr, size_t x5c_len, const unsigned char *sig_ptr, size_t sig_len, const unsigned char *attstmt_ptr, size_t attstmt_len, const char *fmt, int prot, size_t minpinlen) { fido_cred_t *cred; uint8_t flags; uint32_t sigcount; int r; if ((cred = fido_cred_new()) == NULL) return; fido_cred_set_type(cred, type); fido_cred_set_clientdata_hash(cred, cdh_ptr, cdh_len); fido_cred_set_rp(cred, rp_id, rp_name); consume(authdata_ptr, authdata_len); consume(authdata_raw_ptr, authdata_raw_len); consume(x5c_ptr, x5c_len); consume(sig_ptr, sig_len); consume(attstmt_ptr, attstmt_len); if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK) fido_cred_set_authdata_raw(cred, authdata_raw_ptr, authdata_raw_len); fido_cred_set_extensions(cred, ext); if (fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len) != FIDO_OK) { fido_cred_set_x509(cred, x5c_ptr, x5c_len); fido_cred_set_sig(cred, sig_ptr, sig_len); } fido_cred_set_prot(cred, prot); fido_cred_set_pin_minlen(cred, minpinlen); if (rk & 1) fido_cred_set_rk(cred, FIDO_OPT_TRUE); if (uv & 1) fido_cred_set_uv(cred, FIDO_OPT_TRUE); if (fmt) fido_cred_set_fmt(cred, fmt); /* repeat memory operations to trigger reallocation paths */ if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK) fido_cred_set_authdata_raw(cred, authdata_raw_ptr, authdata_raw_len); if (fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len) != FIDO_OK) { fido_cred_set_x509(cred, x5c_ptr, x5c_len); fido_cred_set_sig(cred, sig_ptr, sig_len); } fido_cred_set_x509(cred, x5c_ptr, x5c_len); fido_cred_set_sig(cred, sig_ptr, sig_len); r = fido_cred_verify(cred); consume(&r, sizeof(r)); r = fido_cred_verify_self(cred); consume(&r, sizeof(r)); consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)); consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred)); consume(fido_cred_aaguid_ptr(cred), fido_cred_aaguid_len(cred)); consume(fido_cred_user_id_ptr(cred), fido_cred_user_id_len(cred)); consume_str(fido_cred_user_name(cred)); consume_str(fido_cred_display_name(cred)); consume(fido_cred_largeblob_key_ptr(cred), fido_cred_largeblob_key_len(cred)); flags = fido_cred_flags(cred); consume(&flags, sizeof(flags)); sigcount = fido_cred_sigcount(cred); consume(&sigcount, sizeof(sigcount)); type = fido_cred_type(cred); consume(&type, sizeof(type)); minpinlen = fido_cred_pin_minlen(cred); consume(&minpinlen, sizeof(minpinlen)); fido_cred_free(&cred); } static void test_cred(const struct param *p) { fido_cred_t *cred = NULL; int cose_alg = 0; if ((cred = fido_cred_new()) == NULL) return; switch (p->type & 3) { case 0: cose_alg = COSE_ES256; break; case 1: cose_alg = COSE_RS256; break; default: cose_alg = COSE_EDDSA; break; } set_wire_data(p->wire_data.body, p->wire_data.len); make_cred(cred, p->opt, cose_alg, &p->cdh, p->rp_id, p->rp_name, &p->user_id, p->user_name, p->user_nick, p->user_icon, p->ext, p->rk, p->uv, p->pin, p->excl_count, &p->excl_cred); verify_cred(cose_alg, fido_cred_clientdata_hash_ptr(cred), fido_cred_clientdata_hash_len(cred), fido_cred_rp_id(cred), fido_cred_rp_name(cred), fido_cred_authdata_ptr(cred), fido_cred_authdata_len(cred), fido_cred_authdata_raw_ptr(cred), fido_cred_authdata_raw_len(cred), p->ext, p->rk, p->uv, fido_cred_x5c_ptr(cred), fido_cred_x5c_len(cred), fido_cred_sig_ptr(cred), fido_cred_sig_len(cred), fido_cred_attstmt_ptr(cred), fido_cred_attstmt_len(cred), fido_cred_fmt(cred), fido_cred_prot(cred), fido_cred_pin_minlen(cred)); fido_cred_free(&cred); } static void test_touch(const struct param *p) { fido_dev_t *dev; int r; int touched; set_wire_data(p->wire_data.body, p->wire_data.len); if ((dev = open_dev(p->opt & 2)) == NULL) return; if (p->opt & 1) fido_dev_force_u2f(dev); r = fido_dev_get_touch_begin(dev); consume_str(fido_strerr(r)); r = fido_dev_get_touch_status(dev, &touched, -1); consume_str(fido_strerr(r)); consume(&touched, sizeof(touched)); fido_dev_cancel(dev); fido_dev_close(dev); fido_dev_free(&dev); } static void test_misc(const struct param *p) { fido_cred_t *cred = NULL; if ((cred = fido_cred_new()) == NULL) return; /* reuse user id as credential id */ fido_cred_set_id(cred, p->user_id.body, p->user_id.len); consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred)); fido_cred_free(&cred); } void test(const struct param *p) { prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); test_cred(p); test_touch(p); test_misc(p); } void mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN { if (flags & MUTATE_SEED) p->seed = (int)seed; if (flags & MUTATE_PARAM) { mutate_byte(&p->rk); mutate_byte(&p->type); mutate_byte(&p->opt); mutate_byte(&p->uv); mutate_byte(&p->excl_count); mutate_int(&p->ext); mutate_blob(&p->cdh); mutate_blob(&p->user_id); mutate_blob(&p->excl_cred); mutate_string(p->pin); mutate_string(p->user_icon); mutate_string(p->user_name); mutate_string(p->user_nick); mutate_string(p->rp_id); mutate_string(p->rp_name); } if (flags & MUTATE_WIREDATA) { if (p->opt & 1) { p->wire_data.len = sizeof(dummy_wire_data_u2f); memcpy(&p->wire_data.body, &dummy_wire_data_u2f, p->wire_data.len); } else { p->wire_data.len = sizeof(dummy_wire_data_fido); memcpy(&p->wire_data.body, &dummy_wire_data_fido, p->wire_data.len); } mutate_blob(&p->wire_data); } } libfido2-1.10.0/fuzz/fuzz_credman.c000066400000000000000000000224121417126203300171140ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "mutator_aux.h" #include "wiredata_fido2.h" #include "dummy.h" #include "../openbsd-compat/openbsd-compat.h" /* Parameter set defining a FIDO2 credential management operation. */ struct param { char pin[MAXSTR]; char rp_id[MAXSTR]; int seed; struct blob cred_id; struct blob del_wire_data; struct blob meta_wire_data; struct blob rk_wire_data; struct blob rp_wire_data; }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'getCredsMetadata' credential management command. */ static const uint8_t dummy_meta_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_CREDMAN_META, }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'enumerateRPsBegin' credential management command. */ static const uint8_t dummy_rp_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_CREDMAN_RPLIST, }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'enumerateCredentialsBegin' credential management command. */ static const uint8_t dummy_rk_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_CREDMAN_RKLIST, }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'deleteCredential' credential management command. */ static const uint8_t dummy_del_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_STATUS, }; struct param * unpack(const uint8_t *ptr, size_t len) { cbor_item_t *item = NULL, **v; struct cbor_load_result cbor; struct param *p; int ok = -1; if ((p = calloc(1, sizeof(*p))) == NULL || (item = cbor_load(ptr, len, &cbor)) == NULL || cbor.read != len || cbor_isa_array(item) == false || cbor_array_is_definite(item) == false || cbor_array_size(item) != 8 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_int(v[0], &p->seed) < 0 || unpack_string(v[1], p->pin) < 0 || unpack_string(v[2], p->rp_id) < 0 || unpack_blob(v[3], &p->cred_id) < 0 || unpack_blob(v[4], &p->meta_wire_data) < 0 || unpack_blob(v[5], &p->rp_wire_data) < 0 || unpack_blob(v[6], &p->rk_wire_data) < 0 || unpack_blob(v[7], &p->del_wire_data) < 0) goto fail; ok = 0; fail: if (ok < 0) { free(p); p = NULL; } if (item) cbor_decref(&item); return p; } size_t pack(uint8_t *ptr, size_t len, const struct param *p) { cbor_item_t *argv[8], *array = NULL; size_t cbor_alloc_len, cbor_len = 0; unsigned char *cbor = NULL; memset(argv, 0, sizeof(argv)); if ((array = cbor_new_definite_array(8)) == NULL || (argv[0] = pack_int(p->seed)) == NULL || (argv[1] = pack_string(p->pin)) == NULL || (argv[2] = pack_string(p->rp_id)) == NULL || (argv[3] = pack_blob(&p->cred_id)) == NULL || (argv[4] = pack_blob(&p->meta_wire_data)) == NULL || (argv[5] = pack_blob(&p->rp_wire_data)) == NULL || (argv[6] = pack_blob(&p->rk_wire_data)) == NULL || (argv[7] = pack_blob(&p->del_wire_data)) == NULL) goto fail; for (size_t i = 0; i < 8; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 8; i++) if (argv[i]) cbor_decref(&argv[i]); if (array) cbor_decref(&array); free(cbor); return cbor_len; } size_t pack_dummy(uint8_t *ptr, size_t len) { struct param dummy; uint8_t blob[4096]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin)); strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id)); dummy.meta_wire_data.len = sizeof(dummy_meta_wire_data); dummy.rp_wire_data.len = sizeof(dummy_rp_wire_data); dummy.rk_wire_data.len = sizeof(dummy_rk_wire_data); dummy.del_wire_data.len = sizeof(dummy_del_wire_data); dummy.cred_id.len = sizeof(dummy_cred_id); memcpy(&dummy.meta_wire_data.body, &dummy_meta_wire_data, dummy.meta_wire_data.len); memcpy(&dummy.rp_wire_data.body, &dummy_rp_wire_data, dummy.rp_wire_data.len); memcpy(&dummy.rk_wire_data.body, &dummy_rk_wire_data, dummy.rk_wire_data.len); memcpy(&dummy.del_wire_data.body, &dummy_del_wire_data, dummy.del_wire_data.len); memcpy(&dummy.cred_id.body, &dummy_cred_id, dummy.cred_id.len); assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); if (blob_len > len) { memcpy(ptr, blob, len); return len; } memcpy(ptr, blob, blob_len); return blob_len; } static fido_dev_t * prepare_dev(void) { fido_dev_t *dev; bool x; if ((dev = open_dev(0)) == NULL) return NULL; x = fido_dev_is_fido2(dev); consume(&x, sizeof(x)); x = fido_dev_supports_cred_prot(dev); consume(&x, sizeof(x)); x = fido_dev_supports_credman(dev); consume(&x, sizeof(x)); return dev; } static void get_metadata(const struct param *p) { fido_dev_t *dev; fido_credman_metadata_t *metadata; uint64_t existing; uint64_t remaining; set_wire_data(p->meta_wire_data.body, p->meta_wire_data.len); if ((dev = prepare_dev()) == NULL) return; if ((metadata = fido_credman_metadata_new()) == NULL) { fido_dev_close(dev); fido_dev_free(&dev); return; } fido_credman_get_dev_metadata(dev, metadata, p->pin); existing = fido_credman_rk_existing(metadata); remaining = fido_credman_rk_remaining(metadata); consume(&existing, sizeof(existing)); consume(&remaining, sizeof(remaining)); fido_credman_metadata_free(&metadata); fido_dev_close(dev); fido_dev_free(&dev); } static void get_rp_list(const struct param *p) { fido_dev_t *dev; fido_credman_rp_t *rp; set_wire_data(p->rp_wire_data.body, p->rp_wire_data.len); if ((dev = prepare_dev()) == NULL) return; if ((rp = fido_credman_rp_new()) == NULL) { fido_dev_close(dev); fido_dev_free(&dev); return; } fido_credman_get_dev_rp(dev, rp, p->pin); /* +1 on purpose */ for (size_t i = 0; i < fido_credman_rp_count(rp) + 1; i++) { consume(fido_credman_rp_id_hash_ptr(rp, i), fido_credman_rp_id_hash_len(rp, i)); consume_str(fido_credman_rp_id(rp, i)); consume_str(fido_credman_rp_name(rp, i)); } fido_credman_rp_free(&rp); fido_dev_close(dev); fido_dev_free(&dev); } static void get_rk_list(const struct param *p) { fido_dev_t *dev; fido_credman_rk_t *rk; const fido_cred_t *cred; int val; set_wire_data(p->rk_wire_data.body, p->rk_wire_data.len); if ((dev = prepare_dev()) == NULL) return; if ((rk = fido_credman_rk_new()) == NULL) { fido_dev_close(dev); fido_dev_free(&dev); return; } fido_credman_get_dev_rk(dev, p->rp_id, rk, p->pin); /* +1 on purpose */ for (size_t i = 0; i < fido_credman_rk_count(rk) + 1; i++) { if ((cred = fido_credman_rk(rk, i)) == NULL) { assert(i >= fido_credman_rk_count(rk)); continue; } val = fido_cred_type(cred); consume(&val, sizeof(val)); consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred)); consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)); consume(fido_cred_user_id_ptr(cred), fido_cred_user_id_len(cred)); consume_str(fido_cred_user_name(cred)); consume_str(fido_cred_display_name(cred)); val = fido_cred_prot(cred); consume(&val, sizeof(val)); } fido_credman_rk_free(&rk); fido_dev_close(dev); fido_dev_free(&dev); } static void del_rk(const struct param *p) { fido_dev_t *dev; set_wire_data(p->del_wire_data.body, p->del_wire_data.len); if ((dev = prepare_dev()) == NULL) return; fido_credman_del_dev_rk(dev, p->cred_id.body, p->cred_id.len, p->pin); fido_dev_close(dev); fido_dev_free(&dev); } static void set_rk(const struct param *p) { fido_dev_t *dev = NULL; fido_cred_t *cred = NULL; const char *pin = p->pin; int r0, r1, r2; set_wire_data(p->del_wire_data.body, p->del_wire_data.len); if ((dev = prepare_dev()) == NULL) return; if ((cred = fido_cred_new()) == NULL) goto out; r0 = fido_cred_set_id(cred, p->cred_id.body, p->cred_id.len); r1 = fido_cred_set_user(cred, p->cred_id.body, p->cred_id.len, p->rp_id, NULL, NULL); if (strlen(pin) == 0) pin = NULL; r2 = fido_credman_set_dev_rk(dev, cred, pin); consume(&r0, sizeof(r0)); consume(&r1, sizeof(r1)); consume(&r2, sizeof(r2)); out: fido_dev_close(dev); fido_dev_free(&dev); fido_cred_free(&cred); } void test(const struct param *p) { prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); get_metadata(p); get_rp_list(p); get_rk_list(p); del_rk(p); set_rk(p); } void mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN { if (flags & MUTATE_SEED) p->seed = (int)seed; if (flags & MUTATE_PARAM) { mutate_blob(&p->cred_id); mutate_string(p->pin); mutate_string(p->rp_id); } if (flags & MUTATE_WIREDATA) { mutate_blob(&p->meta_wire_data); mutate_blob(&p->rp_wire_data); mutate_blob(&p->rk_wire_data); mutate_blob(&p->del_wire_data); } } libfido2-1.10.0/fuzz/fuzz_hid.c000066400000000000000000000135371417126203300162570ustar00rootroot00000000000000/* * Copyright (c) 2020-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "../openbsd-compat/openbsd-compat.h" #include "mutator_aux.h" #include "dummy.h" extern int fido_hid_get_usage(const uint8_t *, size_t, uint32_t *); extern int fido_hid_get_report_len(const uint8_t *, size_t, size_t *, size_t *); extern void set_udev_parameters(const char *, const struct blob *); struct param { int seed; char uevent[MAXSTR]; struct blob report_descriptor; struct blob netlink_wiredata; }; /* * Sample HID report descriptor from the FIDO HID interface of a YubiKey 5. */ static const uint8_t dummy_report_descriptor[] = { 0x06, 0xd0, 0xf1, 0x09, 0x01, 0xa1, 0x01, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x40, 0x91, 0x02, 0xc0 }; /* * Sample uevent file from a Yubico Security Key. */ static const char dummy_uevent[] = "DRIVER=hid-generic\n" "HID_ID=0003:00001050:00000120\n" "HID_NAME=Yubico Security Key by Yubico\n" "HID_PHYS=usb-0000:00:14.0-3/input0\n" "HID_UNIQ=\n" "MODALIAS=hid:b0003g0001v00001050p00000120\n"; struct param * unpack(const uint8_t *ptr, size_t len) { cbor_item_t *item = NULL, **v; struct cbor_load_result cbor; struct param *p; int ok = -1; if ((p = calloc(1, sizeof(*p))) == NULL || (item = cbor_load(ptr, len, &cbor)) == NULL || cbor.read != len || cbor_isa_array(item) == false || cbor_array_is_definite(item) == false || cbor_array_size(item) != 4 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_int(v[0], &p->seed) < 0 || unpack_string(v[1], p->uevent) < 0 || unpack_blob(v[2], &p->report_descriptor) < 0 || unpack_blob(v[3], &p->netlink_wiredata) < 0) goto fail; ok = 0; fail: if (ok < 0) { free(p); p = NULL; } if (item) cbor_decref(&item); return p; } size_t pack(uint8_t *ptr, size_t len, const struct param *p) { cbor_item_t *argv[4], *array = NULL; size_t cbor_alloc_len, cbor_len = 0; unsigned char *cbor = NULL; memset(argv, 0, sizeof(argv)); if ((array = cbor_new_definite_array(4)) == NULL || (argv[0] = pack_int(p->seed)) == NULL || (argv[1] = pack_string(p->uevent)) == NULL || (argv[2] = pack_blob(&p->report_descriptor)) == NULL || (argv[3] = pack_blob(&p->netlink_wiredata)) == NULL) goto fail; for (size_t i = 0; i < 4; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 4; i++) if (argv[i]) cbor_decref(&argv[i]); if (array) cbor_decref(&array); free(cbor); return cbor_len; } size_t pack_dummy(uint8_t *ptr, size_t len) { struct param dummy; uint8_t blob[4096]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); dummy.report_descriptor.len = sizeof(dummy_report_descriptor); strlcpy(dummy.uevent, dummy_uevent, sizeof(dummy.uevent)); memcpy(&dummy.report_descriptor.body, &dummy_report_descriptor, dummy.report_descriptor.len); dummy.netlink_wiredata.len = sizeof(dummy_netlink_wiredata); memcpy(&dummy.netlink_wiredata.body, &dummy_netlink_wiredata, dummy.netlink_wiredata.len); assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); if (blob_len > len) blob_len = len; memcpy(ptr, blob, blob_len); return blob_len; } static void get_usage(const struct param *p) { uint32_t usage_page = 0; fido_hid_get_usage(p->report_descriptor.body, p->report_descriptor.len, &usage_page); consume(&usage_page, sizeof(usage_page)); } static void get_report_len(const struct param *p) { size_t report_in_len = 0; size_t report_out_len = 0; fido_hid_get_report_len(p->report_descriptor.body, p->report_descriptor.len, &report_in_len, &report_out_len); consume(&report_in_len, sizeof(report_in_len)); consume(&report_out_len, sizeof(report_out_len)); } static void manifest(const struct param *p) { size_t ndevs, nfound; fido_dev_info_t *devlist = NULL, *devlist_set = NULL; int16_t vendor_id, product_id; fido_dev_io_t io; fido_dev_transport_t t; memset(&io, 0, sizeof(io)); memset(&t, 0, sizeof(t)); set_netlink_io_functions(fd_read, fd_write); set_wire_data(p->netlink_wiredata.body, p->netlink_wiredata.len); set_udev_parameters(p->uevent, &p->report_descriptor); ndevs = uniform_random(64); if ((devlist = fido_dev_info_new(ndevs)) == NULL || (devlist_set = fido_dev_info_new(1)) == NULL || fido_dev_info_manifest(devlist, ndevs, &nfound) != FIDO_OK) goto out; for (size_t i = 0; i < nfound; i++) { const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); consume_str(fido_dev_info_path(di)); consume_str(fido_dev_info_manufacturer_string(di)); consume_str(fido_dev_info_product_string(di)); vendor_id = fido_dev_info_vendor(di); product_id = fido_dev_info_product(di); consume(&vendor_id, sizeof(vendor_id)); consume(&product_id, sizeof(product_id)); fido_dev_info_set(devlist_set, 0, fido_dev_info_path(di), fido_dev_info_manufacturer_string(di), fido_dev_info_product_string(di), &io, &t); } out: fido_dev_info_free(&devlist, ndevs); fido_dev_info_free(&devlist_set, 1); } void test(const struct param *p) { prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); get_usage(p); get_report_len(p); manifest(p); } void mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN { if (flags & MUTATE_SEED) p->seed = (int)seed; if (flags & MUTATE_PARAM) { mutate_blob(&p->report_descriptor); mutate_string(p->uevent); } if (flags & MUTATE_WIREDATA) mutate_blob(&p->netlink_wiredata); } libfido2-1.10.0/fuzz/fuzz_largeblob.c000066400000000000000000000132711417126203300174370ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "mutator_aux.h" #include "wiredata_fido2.h" #include "dummy.h" #include "../openbsd-compat/openbsd-compat.h" /* Parameter set defining a FIDO2 "large blob" operation. */ struct param { char pin[MAXSTR]; int seed; struct blob key; struct blob get_wiredata; struct blob set_wiredata; }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'authenticatorLargeBlobs' 'get' command. */ static const uint8_t dummy_get_wiredata[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY }; /* * Collection of HID reports from an authenticator issued with a FIDO2 * 'authenticatorLargeBlobs' 'set' command. */ static const uint8_t dummy_set_wiredata[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_PINTOKEN, WIREDATA_CTAP_CBOR_STATUS }; /* * XXX this needs to match the encrypted blob embedded in * WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY. */ static const uint8_t dummy_key[] = { 0xa9, 0x1b, 0xc4, 0xdd, 0xfc, 0x9a, 0x93, 0x79, 0x75, 0xba, 0xf7, 0x7f, 0x4d, 0x57, 0xfc, 0xa6, 0xe1, 0xf8, 0x06, 0x43, 0x23, 0x99, 0x51, 0x32, 0xce, 0x6e, 0x19, 0x84, 0x50, 0x13, 0x2d, 0x7b }; struct param * unpack(const uint8_t *ptr, size_t len) { cbor_item_t *item = NULL, **v; struct cbor_load_result cbor; struct param *p; int ok = -1; if ((p = calloc(1, sizeof(*p))) == NULL || (item = cbor_load(ptr, len, &cbor)) == NULL || cbor.read != len || cbor_isa_array(item) == false || cbor_array_is_definite(item) == false || cbor_array_size(item) != 5 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_int(v[0], &p->seed) < 0 || unpack_string(v[1], p->pin) < 0 || unpack_blob(v[2], &p->key) < 0 || unpack_blob(v[3], &p->get_wiredata) < 0 || unpack_blob(v[4], &p->set_wiredata) < 0) goto fail; ok = 0; fail: if (ok < 0) { free(p); p = NULL; } if (item) cbor_decref(&item); return p; } size_t pack(uint8_t *ptr, size_t len, const struct param *p) { cbor_item_t *argv[5], *array = NULL; size_t cbor_alloc_len, cbor_len = 0; unsigned char *cbor = NULL; memset(argv, 0, sizeof(argv)); if ((array = cbor_new_definite_array(5)) == NULL || (argv[0] = pack_int(p->seed)) == NULL || (argv[1] = pack_string(p->pin)) == NULL || (argv[2] = pack_blob(&p->key)) == NULL || (argv[3] = pack_blob(&p->get_wiredata)) == NULL || (argv[4] = pack_blob(&p->set_wiredata)) == NULL) goto fail; for (size_t i = 0; i < 5; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 5; i++) if (argv[i]) cbor_decref(&argv[i]); if (array) cbor_decref(&array); free(cbor); return cbor_len; } size_t pack_dummy(uint8_t *ptr, size_t len) { struct param dummy; uint8_t blob[4096]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin)); dummy.get_wiredata.len = sizeof(dummy_get_wiredata); dummy.set_wiredata.len = sizeof(dummy_set_wiredata); dummy.key.len = sizeof(dummy_key); memcpy(&dummy.get_wiredata.body, &dummy_get_wiredata, dummy.get_wiredata.len); memcpy(&dummy.set_wiredata.body, &dummy_set_wiredata, dummy.set_wiredata.len); memcpy(&dummy.key.body, &dummy_key, dummy.key.len); assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); if (blob_len > len) { memcpy(ptr, blob, len); return len; } memcpy(ptr, blob, blob_len); return blob_len; } static fido_dev_t * prepare_dev(void) { fido_dev_t *dev; if ((dev = open_dev(0)) == NULL) return NULL; return dev; } static void get_blob(const struct param *p, int array) { fido_dev_t *dev; u_char *ptr = NULL; size_t len = 0; set_wire_data(p->get_wiredata.body, p->get_wiredata.len); if ((dev = prepare_dev()) == NULL) return; if (array) fido_dev_largeblob_get_array(dev, &ptr, &len); else fido_dev_largeblob_get(dev, p->key.body, p->key.len, &ptr, &len); consume(ptr, len); free(ptr); fido_dev_close(dev); fido_dev_free(&dev); } static void set_blob(const struct param *p, int op) { fido_dev_t *dev; const char *pin; set_wire_data(p->set_wiredata.body, p->set_wiredata.len); if ((dev = prepare_dev()) == NULL) return; pin = p->pin; if (strlen(pin) == 0) pin = NULL; switch (op) { case 0: fido_dev_largeblob_remove(dev, p->key.body, p->key.len, pin); break; case 1: /* XXX reuse p->get_wiredata as the blob to be set */ fido_dev_largeblob_set(dev, p->key.body, p->key.len, p->get_wiredata.body, p->get_wiredata.len, pin); break; case 2: /* XXX reuse p->get_wiredata as the body of the cbor array */ fido_dev_largeblob_set_array(dev, p->get_wiredata.body, p->get_wiredata.len, pin); } fido_dev_close(dev); fido_dev_free(&dev); } void test(const struct param *p) { prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); get_blob(p, 0); get_blob(p, 1); set_blob(p, 0); set_blob(p, 1); set_blob(p, 2); } void mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN { if (flags & MUTATE_SEED) p->seed = (int)seed; if (flags & MUTATE_PARAM) { mutate_blob(&p->key); mutate_string(p->pin); } if (flags & MUTATE_WIREDATA) { mutate_blob(&p->get_wiredata); mutate_blob(&p->set_wiredata); } } libfido2-1.10.0/fuzz/fuzz_mgmt.c000066400000000000000000000266471417126203300164650ustar00rootroot00000000000000/* * 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 "wiredata_fido2.h" #include "dummy.h" #include "../openbsd-compat/openbsd-compat.h" #define MAXRPID 64 struct param { char pin1[MAXSTR]; char pin2[MAXSTR]; struct blob reset_wire_data; struct blob info_wire_data; struct blob set_pin_wire_data; struct blob change_pin_wire_data; struct blob retry_wire_data; struct blob config_wire_data; int seed; }; static const uint8_t dummy_reset_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_CBOR_STATUS, }; static const uint8_t dummy_info_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_INFO, }; static const uint8_t dummy_set_pin_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_STATUS, }; static const uint8_t dummy_change_pin_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_STATUS, }; static const uint8_t dummy_retry_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_RETRIES, }; static const uint8_t dummy_config_wire_data[] = { WIREDATA_CTAP_INIT, WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_STATUS, }; struct param * unpack(const uint8_t *ptr, size_t len) { cbor_item_t *item = NULL, **v; struct cbor_load_result cbor; struct param *p; int ok = -1; if ((p = calloc(1, sizeof(*p))) == NULL || (item = cbor_load(ptr, len, &cbor)) == NULL || cbor.read != len || cbor_isa_array(item) == false || cbor_array_is_definite(item) == false || cbor_array_size(item) != 9 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_int(v[0], &p->seed) < 0 || unpack_string(v[1], p->pin1) < 0 || unpack_string(v[2], p->pin2) < 0 || unpack_blob(v[3], &p->reset_wire_data) < 0 || unpack_blob(v[4], &p->info_wire_data) < 0 || unpack_blob(v[5], &p->set_pin_wire_data) < 0 || unpack_blob(v[6], &p->change_pin_wire_data) < 0 || unpack_blob(v[7], &p->retry_wire_data) < 0 || unpack_blob(v[8], &p->config_wire_data) < 0) goto fail; ok = 0; fail: if (ok < 0) { free(p); p = NULL; } if (item) cbor_decref(&item); return p; } size_t pack(uint8_t *ptr, size_t len, const struct param *p) { cbor_item_t *argv[9], *array = NULL; size_t cbor_alloc_len, cbor_len = 0; unsigned char *cbor = NULL; memset(argv, 0, sizeof(argv)); if ((array = cbor_new_definite_array(9)) == NULL || (argv[0] = pack_int(p->seed)) == NULL || (argv[1] = pack_string(p->pin1)) == NULL || (argv[2] = pack_string(p->pin2)) == NULL || (argv[3] = pack_blob(&p->reset_wire_data)) == NULL || (argv[4] = pack_blob(&p->info_wire_data)) == NULL || (argv[5] = pack_blob(&p->set_pin_wire_data)) == NULL || (argv[6] = pack_blob(&p->change_pin_wire_data)) == NULL || (argv[7] = pack_blob(&p->retry_wire_data)) == NULL || (argv[8] = pack_blob(&p->config_wire_data)) == NULL) goto fail; for (size_t i = 0; i < 9; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 9; i++) if (argv[i]) cbor_decref(&argv[i]); if (array) cbor_decref(&array); free(cbor); return cbor_len; } size_t pack_dummy(uint8_t *ptr, size_t len) { struct param dummy; uint8_t blob[4096]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); strlcpy(dummy.pin1, dummy_pin1, sizeof(dummy.pin1)); strlcpy(dummy.pin2, dummy_pin2, sizeof(dummy.pin2)); dummy.reset_wire_data.len = sizeof(dummy_reset_wire_data); dummy.info_wire_data.len = sizeof(dummy_info_wire_data); dummy.set_pin_wire_data.len = sizeof(dummy_set_pin_wire_data); dummy.change_pin_wire_data.len = sizeof(dummy_change_pin_wire_data); dummy.retry_wire_data.len = sizeof(dummy_retry_wire_data); dummy.config_wire_data.len = sizeof(dummy_config_wire_data); memcpy(&dummy.reset_wire_data.body, &dummy_reset_wire_data, dummy.reset_wire_data.len); memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data, dummy.info_wire_data.len); memcpy(&dummy.set_pin_wire_data.body, &dummy_set_pin_wire_data, dummy.set_pin_wire_data.len); memcpy(&dummy.change_pin_wire_data.body, &dummy_change_pin_wire_data, dummy.change_pin_wire_data.len); memcpy(&dummy.retry_wire_data.body, &dummy_retry_wire_data, dummy.retry_wire_data.len); memcpy(&dummy.config_wire_data.body, &dummy_config_wire_data, dummy.config_wire_data.len); assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); if (blob_len > len) { memcpy(ptr, blob, len); return len; } memcpy(ptr, blob, blob_len); return blob_len; } static void dev_reset(const struct param *p) { fido_dev_t *dev; set_wire_data(p->reset_wire_data.body, p->reset_wire_data.len); if ((dev = open_dev(0)) == NULL) return; fido_dev_reset(dev); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_get_cbor_info(const struct param *p) { fido_dev_t *dev; fido_cbor_info_t *ci; uint64_t n; uint8_t proto, major, minor, build, flags; set_wire_data(p->info_wire_data.body, p->info_wire_data.len); if ((dev = open_dev(0)) == NULL) return; proto = fido_dev_protocol(dev); major = fido_dev_major(dev); minor = fido_dev_minor(dev); build = fido_dev_build(dev); flags = fido_dev_flags(dev); consume(&proto, sizeof(proto)); consume(&major, sizeof(major)); consume(&minor, sizeof(minor)); consume(&build, sizeof(build)); consume(&flags, sizeof(flags)); if ((ci = fido_cbor_info_new()) == NULL) goto out; fido_dev_get_cbor_info(dev, ci); for (size_t i = 0; i < fido_cbor_info_versions_len(ci); i++) { char * const *sa = fido_cbor_info_versions_ptr(ci); consume(sa[i], strlen(sa[i])); } for (size_t i = 0; i < fido_cbor_info_extensions_len(ci); i++) { char * const *sa = fido_cbor_info_extensions_ptr(ci); consume(sa[i], strlen(sa[i])); } for (size_t i = 0; i < fido_cbor_info_transports_len(ci); i++) { char * const *sa = fido_cbor_info_transports_ptr(ci); consume(sa[i], strlen(sa[i])); } for (size_t i = 0; i < fido_cbor_info_options_len(ci); i++) { char * const *sa = fido_cbor_info_options_name_ptr(ci); const bool *va = fido_cbor_info_options_value_ptr(ci); consume(sa[i], strlen(sa[i])); consume(&va[i], sizeof(va[i])); } /* +1 on purpose */ for (size_t i = 0; i <= fido_cbor_info_algorithm_count(ci); i++) { const char *type = fido_cbor_info_algorithm_type(ci, i); int cose = fido_cbor_info_algorithm_cose(ci, i); consume_str(type); consume(&cose, sizeof(cose)); } n = fido_cbor_info_maxmsgsiz(ci); consume(&n, sizeof(n)); n = fido_cbor_info_maxcredbloblen(ci); consume(&n, sizeof(n)); n = fido_cbor_info_maxcredcntlst(ci); consume(&n, sizeof(n)); n = fido_cbor_info_maxcredidlen(ci); consume(&n, sizeof(n)); n = fido_cbor_info_fwversion(ci); consume(&n, sizeof(n)); consume(fido_cbor_info_aaguid_ptr(ci), fido_cbor_info_aaguid_len(ci)); consume(fido_cbor_info_protocols_ptr(ci), fido_cbor_info_protocols_len(ci)); out: fido_dev_close(dev); fido_dev_free(&dev); fido_cbor_info_free(&ci); } static void dev_set_pin(const struct param *p) { fido_dev_t *dev; set_wire_data(p->set_pin_wire_data.body, p->set_pin_wire_data.len); if ((dev = open_dev(0)) == NULL) return; fido_dev_set_pin(dev, p->pin1, NULL); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_change_pin(const struct param *p) { fido_dev_t *dev; set_wire_data(p->change_pin_wire_data.body, p->change_pin_wire_data.len); if ((dev = open_dev(0)) == NULL) return; fido_dev_set_pin(dev, p->pin2, p->pin1); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_get_retry_count(const struct param *p) { fido_dev_t *dev; int n = 0; set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len); if ((dev = open_dev(0)) == NULL) return; fido_dev_get_retry_count(dev, &n); consume(&n, sizeof(n)); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_get_uv_retry_count(const struct param *p) { fido_dev_t *dev; int n = 0; set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len); if ((dev = open_dev(0)) == NULL) return; fido_dev_get_uv_retry_count(dev, &n); consume(&n, sizeof(n)); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_enable_entattest(const struct param *p) { fido_dev_t *dev; const char *pin; int r; set_wire_data(p->config_wire_data.body, p->config_wire_data.len); if ((dev = open_dev(0)) == NULL) return; pin = p->pin1; if (strlen(pin) == 0) pin = NULL; r = fido_dev_enable_entattest(dev, pin); consume_str(fido_strerr(r)); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_toggle_always_uv(const struct param *p) { fido_dev_t *dev; const char *pin; int r; set_wire_data(p->config_wire_data.body, p->config_wire_data.len); if ((dev = open_dev(0)) == NULL) return; pin = p->pin1; if (strlen(pin) == 0) pin = NULL; r = fido_dev_toggle_always_uv(dev, pin); consume_str(fido_strerr(r)); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_force_pin_change(const struct param *p) { fido_dev_t *dev; const char *pin; int r; set_wire_data(p->config_wire_data.body, p->config_wire_data.len); if ((dev = open_dev(0)) == NULL) return; pin = p->pin1; if (strlen(pin) == 0) pin = NULL; r = fido_dev_force_pin_change(dev, pin); consume_str(fido_strerr(r)); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_set_pin_minlen(const struct param *p) { fido_dev_t *dev; const char *pin; int r; set_wire_data(p->config_wire_data.body, p->config_wire_data.len); if ((dev = open_dev(0)) == NULL) return; pin = p->pin1; if (strlen(pin) == 0) pin = NULL; r = fido_dev_set_pin_minlen(dev, strlen(p->pin2), pin); consume_str(fido_strerr(r)); fido_dev_close(dev); fido_dev_free(&dev); } static void dev_set_pin_minlen_rpid(const struct param *p) { fido_dev_t *dev; const char *rpid[MAXRPID]; const char *pin; size_t n; int r; set_wire_data(p->config_wire_data.body, p->config_wire_data.len); if ((dev = open_dev(0)) == NULL) return; n = uniform_random(MAXRPID); for (size_t i = 0; i < n; i++) rpid[i] = dummy_rp_id; pin = p->pin1; if (strlen(pin) == 0) pin = NULL; r = fido_dev_set_pin_minlen_rpid(dev, rpid, n, pin); consume_str(fido_strerr(r)); fido_dev_close(dev); fido_dev_free(&dev); } void test(const struct param *p) { prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); dev_reset(p); dev_get_cbor_info(p); dev_set_pin(p); dev_change_pin(p); dev_get_retry_count(p); dev_get_uv_retry_count(p); dev_enable_entattest(p); dev_toggle_always_uv(p); dev_force_pin_change(p); dev_set_pin_minlen(p); dev_set_pin_minlen_rpid(p); } void mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN { if (flags & MUTATE_SEED) p->seed = (int)seed; if (flags & MUTATE_PARAM) { mutate_string(p->pin1); mutate_string(p->pin2); } if (flags & MUTATE_WIREDATA) { mutate_blob(&p->reset_wire_data); mutate_blob(&p->info_wire_data); mutate_blob(&p->set_pin_wire_data); mutate_blob(&p->change_pin_wire_data); mutate_blob(&p->retry_wire_data); } } libfido2-1.10.0/fuzz/fuzz_netlink.c000066400000000000000000000063311417126203300171510ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "../openbsd-compat/openbsd-compat.h" #include "mutator_aux.h" #include "dummy.h" struct param { int seed; int dev; struct blob wiredata; }; struct param * unpack(const uint8_t *ptr, size_t len) { cbor_item_t *item = NULL, **v; struct cbor_load_result cbor; struct param *p; int ok = -1; if ((p = calloc(1, sizeof(*p))) == NULL || (item = cbor_load(ptr, len, &cbor)) == NULL || cbor.read != len || cbor_isa_array(item) == false || cbor_array_is_definite(item) == false || cbor_array_size(item) != 3 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_int(v[0], &p->seed) < 0 || unpack_int(v[1], &p->dev) < 0 || unpack_blob(v[2], &p->wiredata) < 0) goto fail; ok = 0; fail: if (ok < 0) { free(p); p = NULL; } if (item) cbor_decref(&item); return p; } size_t pack(uint8_t *ptr, size_t len, const struct param *p) { cbor_item_t *argv[3], *array = NULL; size_t cbor_alloc_len, cbor_len = 0; unsigned char *cbor = NULL; memset(argv, 0, sizeof(argv)); if ((array = cbor_new_definite_array(3)) == NULL || (argv[0] = pack_int(p->seed)) == NULL || (argv[1] = pack_int(p->dev)) == NULL || (argv[2] = pack_blob(&p->wiredata)) == NULL) goto fail; for (size_t i = 0; i < 3; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 3; i++) if (argv[i]) cbor_decref(&argv[i]); if (array) cbor_decref(&array); free(cbor); return cbor_len; } size_t pack_dummy(uint8_t *ptr, size_t len) { struct param dummy; uint8_t blob[4096]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); dummy.wiredata.len = sizeof(dummy_netlink_wiredata); memcpy(&dummy.wiredata.body, &dummy_netlink_wiredata, dummy.wiredata.len); assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0); if (blob_len > len) { memcpy(ptr, blob, len); return len; } memcpy(ptr, blob, blob_len); return blob_len; } void test(const struct param *p) { fido_nl_t *nl; uint32_t target; prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); set_netlink_io_functions(fd_read, fd_write); set_wire_data(p->wiredata.body, p->wiredata.len); if ((nl = fido_nl_new()) == NULL) return; consume(&nl->fd, sizeof(nl->fd)); consume(&nl->nfc_type, sizeof(nl->nfc_type)); consume(&nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)); consume(&nl->saddr, sizeof(nl->saddr)); fido_nl_power_nfc(nl, (uint32_t)p->dev); if (fido_nl_get_nfc_target(nl, (uint32_t)p->dev, &target) == 0) consume(&target, sizeof(target)); fido_nl_free(&nl); } void mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN { if (flags & MUTATE_SEED) p->seed = (int)seed; if (flags & MUTATE_PARAM) mutate_int(&p->dev); if (flags & MUTATE_WIREDATA) mutate_blob(&p->wiredata); } libfido2-1.10.0/fuzz/libfuzzer.c000066400000000000000000000070241417126203300164430ustar00rootroot00000000000000/* * 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 "mutator_aux.h" static bool debug; static unsigned int flags = MUTATE_ALL; static unsigned long long test_fail; static unsigned long long test_total; static unsigned long long mutate_fail; static unsigned long long mutate_total; int LLVMFuzzerInitialize(int *, char ***); int LLVMFuzzerTestOneInput(const uint8_t *, size_t); size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int); static int save_seed(const char *opt) { const char *path; int fd = -1, status = 1; void *buf = NULL; const size_t buflen = 4096; size_t n; struct param *p = NULL; if ((path = strchr(opt, '=')) == NULL || strlen(++path) == 0) { warnx("usage: --fido-save-seed="); goto fail; } if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) { warn("open %s", path); goto fail; } if ((buf = malloc(buflen)) == NULL) { warn("malloc"); goto fail; } n = pack_dummy(buf, buflen); if ((p = unpack(buf, n)) == NULL) { warnx("unpack"); goto fail; } if (write(fd, buf, n) != (ssize_t)n) { warn("write %s", path); goto fail; } status = 0; fail: if (fd != -1) close(fd); free(buf); free(p); return status; } static void parse_mutate_flags(const char *opt, unsigned int *mutate_flags) { const char *f; if ((f = strchr(opt, '=')) == NULL || strlen(++f) == 0) errx(1, "usage: --fido-mutate="); if (strcmp(f, "seed") == 0) *mutate_flags |= MUTATE_SEED; else if (strcmp(f, "param") == 0) *mutate_flags |= MUTATE_PARAM; else if (strcmp(f, "wiredata") == 0) *mutate_flags |= MUTATE_WIREDATA; else errx(1, "--fido-mutate: unknown flag '%s'", f); } int LLVMFuzzerInitialize(int *argc, char ***argv) { unsigned int mutate_flags = 0; for (int i = 0; i < *argc; i++) if (strcmp((*argv)[i], "--fido-debug") == 0) { debug = 1; } else if (strncmp((*argv)[i], "--fido-save-seed=", 17) == 0) { exit(save_seed((*argv)[i])); } else if (strncmp((*argv)[i], "--fido-mutate=", 14) == 0) { parse_mutate_flags((*argv)[i], &mutate_flags); } if (mutate_flags) flags = mutate_flags; return 0; } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { struct param *p; if (size > 4096) return 0; if (++test_total % 100000 == 0 && debug) { double r = (double)test_fail/(double)test_total * 100.0; fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__, test_fail, test_total, r); } if ((p = unpack(data, size)) == NULL) test_fail++; else { test(p); free(p); } return 0; } size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize, unsigned int seed) NO_MSAN { struct param *p; uint8_t blob[4096]; size_t blob_len; memset(&p, 0, sizeof(p)); #ifdef WITH_MSAN __msan_unpoison(data, maxsize); #endif if (++mutate_total % 100000 == 0 && debug) { double r = (double)mutate_fail/(double)mutate_total * 100.0; fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__, mutate_fail, mutate_total, r); } if ((p = unpack(data, size)) == NULL) { mutate_fail++; return pack_dummy(data, maxsize); } mutate(p, seed, flags); if ((blob_len = pack(blob, sizeof(blob), p)) == 0 || blob_len > sizeof(blob) || blob_len > maxsize) { mutate_fail++; free(p); return 0; } free(p); memcpy(data, blob, blob_len); return blob_len; } libfido2-1.10.0/fuzz/mutator_aux.c000066400000000000000000000127401417126203300170000ustar00rootroot00000000000000/* * 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 "mutator_aux.h" #define HID_DEV_HANDLE 0x68696421 #define NFC_DEV_HANDLE 0x6e666321 int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int); int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t); size_t LLVMFuzzerMutate(uint8_t *, size_t, size_t); static const uint8_t *wire_data_ptr = NULL; static size_t wire_data_len = 0; void consume(const void *body, size_t len) { const volatile uint8_t *ptr = body; volatile uint8_t x = 0; #ifdef WITH_MSAN __msan_check_mem_is_initialized(body, len); #endif while (len--) x ^= *ptr++; (void)x; } void consume_str(const char *str) { if (str != NULL) consume(str, strlen(str) + 1); } int unpack_int(cbor_item_t *item, int *v) { if (cbor_is_int(item) == false || cbor_int_get_width(item) != CBOR_INT_64) return -1; if (cbor_isa_uint(item)) *v = (int)cbor_get_uint64(item); else *v = (int)(-cbor_get_uint64(item) - 1); return 0; } int unpack_string(cbor_item_t *item, char *v) { size_t len; if (cbor_isa_bytestring(item) == false || (len = cbor_bytestring_length(item)) >= MAXSTR) return -1; memcpy(v, cbor_bytestring_handle(item), len); v[len] = '\0'; return 0; } int unpack_byte(cbor_item_t *item, uint8_t *v) { if (cbor_isa_uint(item) == false || cbor_int_get_width(item) != CBOR_INT_8) return -1; *v = cbor_get_uint8(item); return 0; } int unpack_blob(cbor_item_t *item, struct blob *v) { if (cbor_isa_bytestring(item) == false || (v->len = cbor_bytestring_length(item)) > sizeof(v->body)) return -1; memcpy(v->body, cbor_bytestring_handle(item), v->len); return 0; } cbor_item_t * pack_int(int v) NO_MSAN { if (v < 0) return cbor_build_negint64((uint64_t)(-(int64_t)v - 1)); else return cbor_build_uint64((uint64_t)v); } cbor_item_t * pack_string(const char *v) NO_MSAN { if (strlen(v) >= MAXSTR) return NULL; return cbor_build_bytestring((const unsigned char *)v, strlen(v)); } cbor_item_t * pack_byte(uint8_t v) NO_MSAN { return cbor_build_uint8(v); } cbor_item_t * pack_blob(const struct blob *v) NO_MSAN { return cbor_build_bytestring(v->body, v->len); } void mutate_byte(uint8_t *b) { LLVMFuzzerMutate(b, sizeof(*b), sizeof(*b)); } void mutate_int(int *i) { LLVMFuzzerMutate((uint8_t *)i, sizeof(*i), sizeof(*i)); } void mutate_blob(struct blob *blob) { blob->len = LLVMFuzzerMutate((uint8_t *)blob->body, blob->len, sizeof(blob->body)); } void mutate_string(char *s) { size_t n; n = LLVMFuzzerMutate((uint8_t *)s, strlen(s), MAXSTR - 1); s[n] = '\0'; } /* XXX should fail, but doesn't */ static int buf_read(unsigned char *ptr, size_t len, int ms) { size_t n; (void)ms; if (wire_data_len < len) n = wire_data_len; else n = len; memcpy(ptr, wire_data_ptr, n); wire_data_ptr += n; wire_data_len -= n; return (int)n; } static int buf_write(const unsigned char *ptr, size_t len) { consume(ptr, len); if (uniform_random(400) < 1) { errno = EIO; return -1; } return (int)len; } static void * hid_open(const char *path) { (void)path; return (void *)HID_DEV_HANDLE; } static void hid_close(void *handle) { assert(handle == (void *)HID_DEV_HANDLE); } static int hid_read(void *handle, unsigned char *ptr, size_t len, int ms) { assert(handle == (void *)HID_DEV_HANDLE); assert(len >= CTAP_MIN_REPORT_LEN && len <= CTAP_MAX_REPORT_LEN); return buf_read(ptr, len, ms); } static int hid_write(void *handle, const unsigned char *ptr, size_t len) { assert(handle == (void *)HID_DEV_HANDLE); assert(len >= CTAP_MIN_REPORT_LEN + 1 && len <= CTAP_MAX_REPORT_LEN + 1); return buf_write(ptr, len); } static void * nfc_open(const char *path) { (void)path; return (void *)NFC_DEV_HANDLE; } static void nfc_close(void *handle) { assert(handle == (void *)NFC_DEV_HANDLE); } static int nfc_read(void *handle, unsigned char *ptr, size_t len, int ms) { assert(handle == (void *)NFC_DEV_HANDLE); assert(len > 0 && len <= 256 + 2); return buf_read(ptr, len, ms); } static int nfc_write(void *handle, const unsigned char *ptr, size_t len) { assert(handle == (void *)NFC_DEV_HANDLE); assert(len > 0 && len <= 256 + 2); return buf_write(ptr, len); } ssize_t fd_read(int fd, void *ptr, size_t len) { assert(fd != -1); return buf_read(ptr, len, -1); } ssize_t fd_write(int fd, const void *ptr, size_t len) { assert(fd != -1); return buf_write(ptr, len); } fido_dev_t * open_dev(int nfc) { fido_dev_t *dev; fido_dev_io_t io; fido_dev_transport_t t; memset(&io, 0, sizeof(io)); memset(&t, 0, sizeof(t)); if ((dev = fido_dev_new()) == NULL) return NULL; if (nfc) { io.open = nfc_open; io.close = nfc_close; io.read = nfc_read; io.write = nfc_write; } else { io.open = hid_open; io.close = hid_close; io.read = hid_read; io.write = hid_write; } if (fido_dev_set_io_functions(dev, &io) != FIDO_OK) goto fail; if (nfc) { t.rx = fido_nfc_rx; t.tx = fido_nfc_tx; if (fido_dev_set_transport_functions(dev, &t) != FIDO_OK) goto fail; } if (fido_dev_set_timeout(dev, 300) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) goto fail; return dev; fail: fido_dev_free(&dev); return NULL; } void set_wire_data(const uint8_t *ptr, size_t len) { wire_data_ptr = ptr; wire_data_len = len; } libfido2-1.10.0/fuzz/mutator_aux.h000066400000000000000000000047731417126203300170140ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _MUTATOR_AUX_H #define _MUTATOR_AUX_H #include #include #include #include "../src/fido.h" #include "../src/fido/bio.h" #include "../src/fido/config.h" #include "../src/fido/credman.h" #include "../src/fido/eddsa.h" #include "../src/fido/es256.h" #include "../src/fido/es256.h" #include "../src/fido/rs256.h" #include "../src/netlink.h" /* * As of LLVM 10.0.0, MSAN support in libFuzzer was still experimental. * We therefore have to be careful when using our custom mutator, or * MSAN will flag uninitialised reads on memory populated by libFuzzer. * Since there is no way to suppress MSAN without regenerating object * code (in which case you might as well rebuild libFuzzer with MSAN), * we adjust our mutator to make it less accurate while allowing * fuzzing to proceed. */ #if defined(__has_feature) # if __has_feature(memory_sanitizer) # include # define NO_MSAN __attribute__((no_sanitize("memory"))) # define WITH_MSAN 1 # endif #endif #if !defined(WITH_MSAN) # define NO_MSAN #endif #define MUTATE_SEED 0x01 #define MUTATE_PARAM 0x02 #define MUTATE_WIREDATA 0x04 #define MUTATE_ALL (MUTATE_SEED | MUTATE_PARAM | MUTATE_WIREDATA) #define MAXSTR 1024 #define MAXBLOB 3600 struct blob { uint8_t body[MAXBLOB]; size_t len; }; struct param; struct param *unpack(const uint8_t *, size_t); size_t pack(uint8_t *, size_t, const struct param *); size_t pack_dummy(uint8_t *, size_t); void mutate(struct param *, unsigned int, unsigned int); void test(const struct param *); void consume(const void *, size_t); void consume_str(const char *); int unpack_blob(cbor_item_t *, struct blob *); int unpack_byte(cbor_item_t *, uint8_t *); int unpack_int(cbor_item_t *, int *); int unpack_string(cbor_item_t *, char *); cbor_item_t *pack_blob(const struct blob *); cbor_item_t *pack_byte(uint8_t); cbor_item_t *pack_int(int); cbor_item_t *pack_string(const char *); void mutate_byte(uint8_t *); void mutate_int(int *); void mutate_blob(struct blob *); void mutate_string(char *); ssize_t fd_read(int, void *, size_t); ssize_t fd_write(int, const void *, size_t); fido_dev_t *open_dev(int); void set_wire_data(const uint8_t *, size_t); void fuzz_clock_reset(void); void prng_init(unsigned long); unsigned long prng_uint32(void); uint32_t uniform_random(uint32_t); #endif /* !_MUTATOR_AUX_H */ libfido2-1.10.0/fuzz/preload-fuzz.c000066400000000000000000000036231417126203300170520ustar00rootroot00000000000000/* * 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); } libfido2-1.10.0/fuzz/preload-snoop.c000066400000000000000000000101041417126203300172020ustar00rootroot00000000000000/* * 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); } libfido2-1.10.0/fuzz/prng.c000066400000000000000000000076161417126203300154040ustar00rootroot00000000000000/* A C-program for MT19937, with initialization improved 2002/1/26. Coded by Takuji Nishimura and Makoto Matsumoto. Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Any feedback is very welcome. http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) */ #include #include #include #include "mutator_aux.h" #define init_genrand prng_init #define genrand_int32 prng_uint32 /* Period parameters */ #define N 624 #define M 397 #define MATRIX_A 0x9908b0dfUL /* constant vector a */ #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ int prng_up = 0; static unsigned long mt[N]; /* the array for the state vector */ static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ /* initializes mt[N] with a seed */ void init_genrand(unsigned long s) { mt[0]= s & 0xffffffffUL; for (mti=1; mti> 30)) + (unsigned long)mti); /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ /* only MSBs of the array mt[]. */ /* 2002/01/09 modified by Makoto Matsumoto */ mt[mti] &= 0xffffffffUL; /* for >32 bit machines */ } prng_up = 1; } /* generates a random number on [0,0xffffffff]-interval */ unsigned long genrand_int32(void) { unsigned long y; static unsigned long mag01[2]={0x0UL, MATRIX_A}; /* mag01[x] = x * MATRIX_A for x=0,1 */ if (mti >= N) { /* generate N words at one time */ int kk; assert(mti != N+1); for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; } for (;kk> 1) ^ mag01[y & 0x1UL]; } y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mti = 0; } y = mt[mti++]; /* Tempering */ y ^= (y >> 11); y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 15) & 0xefc60000UL; y ^= (y >> 18); return y; } libfido2-1.10.0/fuzz/report.tgz000066400000000000000000011701721417126203300163320ustar00rootroot00000000000000][s6 R;EʷZG3:;ky6'.J,P$/8H Ŗ NHݍF@Jq6/ .ǶofӿuKwtò,NuѿCV*"˽8{+e!VY>-k[cΌ bf㯤G~~M"[8hkv742Ki5G(m̝ڮh*NX#y x)f[>\o\yzyҿ,Ɖ7eNKVt:6(ghȃV onA̬l*oe"{ V.$k x=s0 ,o AJ|OBoc%s6+*]eO-f[gSsW9x?$?9*b?9Cĩ>!%QʜPt\oa?L`:c}lb8 C/Z. J xƝchPqC xT>A"S2H4'Dѽ[}ʭ{98 -z` (99 Wt}<9PaEJM8UA0ڧ;/iG:U~n дL2Gw~] ~N(PP>2j*GjrrwȲ*Ʃ RgɝAɀԣSP*VsN5·=V{M 9fXT`0 PG@TtvN""w#i1򨯷:#]K+jګTgvUul5aF{ JRP膜XFb2TĴl)`ĚRb%IIwTGi>(nEZ((]3T]3K+ IgЏ'3rU]{]i;6T_IOg껶P:%wzIS^9+նD S'[7qR{⩿$gG$Gc29ͪҟGz)_| /Me"y)v`sG5E05 )_gi8eron؊a j!wJ0&7d d${ , ]ˍ/HFi [Bb[HOeK+p7U(Yr-Mmskiڶ-Y)^mL0 $tt|?4%Vm`AdwT4y({"!%YPlCJ\!)ګizPt@;Eƃhf6]PSX,l]eOA1j|~YMűmUfJp  B4b!>)Z2m6,gKfNT)p#q;!i֙j683bE*ڈmЀrI)(:pzMͺv],piw\m *LhN^lUM-%nkGBοlg1r-Ţ6UPk*;. mw&&2TːSdzSFIhCGHV^R+/z q\-6tHFd+x?Ncx޻QNd[r4,Se o L^n2^;ԬqRQIn| Gu|z#5b[ cdϲxq6O>a4!ܡVM޳' PPHe~kFs9z=9M>˴_{kSۦ:z|vbئ6sۚzs\_Eeo GC>eXl\n_[X=rJ3fqnShn pn UL.=@ ՇψϿhعCvH\ߩ{KN%o.oM`UV cJQ6eH!Eq@B=GI#9CoehL6?cŜbDX#ȚdMYd N'AFF"`>}4`a49f!e(RLO1)[vI-H8>$N#0@؞}]ô^N&W{W-a=8NI2V`o0$5)b,9SE;%-reS/?\ڽꡳ>g{OWaOԽxO~tyޟOϻg{~U3SRבgןUc )_ޟ]|l} .?V&ϽO@bott>]_}B5"qϺ{ o<$ט,oK{wv;ˋ~_?x~~$CuEY]]~^/ad@#_ί}/DЗ~^w ;0aPPCxEF{!z,G  UEi0sI9 {u'Rr->8A]pI|]Ԥ9EǩwsribaH\tYN*Hzkk]`Dð1u\:z(%"HXYȂ1F8-``Mㆄ|ļ[ J`Ҩ`ҐE c4owq Ṡul+)P˨PːZ?6F649azzF\\#2޾5 #{ B C;B;CZy[xtg x8?0D,ǁY)-ɗñW&FM$62X 2šY)-dߌO(qt Z%RRSV$)o+N+wI2T TM9į`` ̯V:0<[@ GQ@T^t}4 "э ? ?M9)H{f{#$TvhkρYȑ#Ev#1I@rV"( (mƆJql GQoޑox?-=m59Hٮ}}Mmb c}n/U'_Κ oJl/}0 tˬi*wxK߷ Ŷ"2r^DvFtq*h0|~vڻc\pqkYbl-[rBt3ĩZjYb^Tn,ȽEb9pϘM> lX^3W$I$n)@׆.Wmkg$/Xu-=ZB#Qvɂm3x4&O7f]CjNN=At5hs 2u`;C<*H$5#A0wW45VFαn , ی~L;X;myf"/*kH:J íkv~~F/7ctrOxd$Ϭ]7OySn M jS 1X 0$8P~;=)@5U]G$AW.YKpeq ˜fMn># +  "DV6x΃ytv>vFvF!*hVRKVyDUQZ11[T[$sɞEHJ"[8w_p9PJSba!='DDWUPTPDS`j!1*1s#+i~Kwb6aT(T(ϫ1ҙҙ3&tN\NL[11ҠҠXF!+e!)F"W"uPP 2vN %Ac[1RR)e`5kӨѽ|h}L{w$d{|FsC݊ P_7;GhT//|7,j.O&{]r^vۏn9g96yenSTG.̳xʹ[y}?lvse,5jm,Oyy'Eؿ՘E 6>sTkW7oTf0Ϝ*3ytD n̥Q[o9&\W |jWY􊸷jE%аo%㏆wǙDFk1y#[qb𫃕=+qσPw](J-*1j2Ay* l<[y ;ܹM;Nܼ5il^v; UʣJ %МStʹ|}wsn=k<4bPH?l8i0giQYEcYx+B +ʖOgy6$c峋I9KG;H!V9Uo*wU?9fuRjGf45׹9(s-J[l'gߋz}߼?y?y(YT,<ٛfFKBywTc5.~y{rtZԾ]tQt^9cda({yȔg/:Z2[AoOΎ{yx}wg:gU?EJ^_uYщvZTY.[cU5̟}٤_xxwKٽC wg_&QWggΎ߼yQӣ~w0>#6o>wE`8~}vtr(+>?yQD7n7'gWm8܌"-^T=G3coR2k6A}M=uwBL,UAtuQԛkNg"ME|t_sYM-ʲɷO~ _|_ztlNOn<}izLgLEϝI^axw;Mk4t{ӺۙouM%JjtDs%ygN6XJxů6YiQO/)n%t.ӑ^);L{,UU;7SЙ&7XiQO{o`?42z{,/Q*/Wbk/XS~t7c-K_:^SJ[w/do>z樄@V5ͽop/(_{ô⟫w_ȊWg,&0XH+PA?^Z𐊣[3b~7&N]2m F>>9[y ]&W7`tޠFZsNџHά&f-U)]zl_y354+JM^gn> y?oaș=H5&bm浧ס@`x~*%֣G7L.zry;S~ƟM<<ϟ@b\ d8H|dbȥrhp7efޫge1ԲE>rf9ϥhyrꖏ_MvsRRO[2VK]3 $g&,n*e!9ݩwYb%lr<߀O[2ˎtg*i^!?Z듁I5ii; L*Lh{v,^R^B_|?s+M"YK*YK66P^?>lDN~Qa'/D`kоOZ=2vIv}^I%%DiG +3g|Q>{׏phQhOy'ٰ)t{f>bƑkɮeܻM>r\ҵaA>#h>.Q*tT5r9z w;RkM[].5 W5V'*\Z&6\]u>>Ͻ坧|>Ml<2湌T*skqqڝt(X HE-al/\Y\u 39}esft]%)׹4"m ku.R8_jtGYowɌOqy\AdiEOdڎS-$G;.,v>t7Z`EEC%\~)*UO城򿰶7]b_ŇInGT,,^ܺ~纛tv-|* >~׻~fvW=j<{p/cLpoZ׌Q&AXێt'2ŗ[8 {Aad޺*̵*qϴk%X(ZZj035IƂ7˻Fsט;d<'IW+"lhz Ǘy?+/XqV~#=*/EqR PdE}FY)Ztкkձ6FL:;Q洅UC]Gj,n?g^;{{*T㋽ǫ?#s lIή<3c:9 jծ=n*ErYx8S\A*V* Ǵʛuy}.dHfK0dgG')>+ .NYpc/hy%_0сqO~!AA[>1|WENwywOO~?}vXupo|q,n:qdk\[KunOOB(ڕtIΟuLO[l6us8I1^Cd[IAgkٸn.m}ϸY#<9̱v.e΍ ztD%2$D"W6.͏qCh&ਹZ-D6$*|a,.PFB,#QX."ȹ}qҠiW`]`l\#gt9\0 I IXH.H;AR·G ÔД H/!L?bADA#M MJ5 k0p =c!s爘ӤԤHM\VI VΘۤܤM8>s}r 'ec< <) <)k⺅X(\(eCzB^9?}|tZms]j )e#Nq]A | ޫaK ALRRB,)z@ *$*eCQQsv h*EeS1ET"yBe!Y)Y)"Ϫܨ1U 8Uj>*bbN,aXII~ aᔸli|vtJkV1%)/ɵia) IhH\BRBRB0$QXdHFw 3bmBBՄReEi0rVI h9wB BFb۸f | XB(D\tGH@B ,v[cp $lj w_1aƐȐQ]a͇=?Z a"q² e92R1`HaHCl۹s%RRB!QKe4u4e. S2ec8ϋC0(H)HPA0&`*,sas}xB5bG=7+xQtC\lgO_|h[lװnG?j((ye#lD{01G2G 1sEl:TzQX|z661GG0aC|>;ʫ+ b:<"yjрDmՂl/Ry%Gcqu~up Ǯ q2LhG쎄Mֹ~VXs꣉TKz?qm:Fh@h"ʧ"9<8{i_,hh"e4Wb+Tԝ#Vhh'G7 >5%p|{ <)t`44&yj٤;?^toY6̷Sр6tӓ>"qȻ?9*ӹ tpN-@6BPc-=ޣC^ 6n4:8! 8!m 5?, , MdW'騟)? 0|Sb!}r&hL@DO=}4}ﳁ>ZpȊ>>Zf ;,puYu3ɦ̴I?H?ZSP {{|kh<`ԏԏ&~LU掋1GGQ?lbU7ԢV-hK5 $'H@fy?iV@z}}INxvppn?ԜL;A1-oſS@k8y-1/-7yǙ-}q/Wge*Mlx,w٬ ?O=010 W/;)s}4fi[O~ROTַfC8()-ֆf;iI6ȆUZ;Z`V2ؖ?2˱|fH](,2syARIT|<<\^e^QUIԕAo~^#釴sMd;w,^v5yexטK:矽'Ũ\f^0̃(#;O(3^??z}z] Aͳͫм[͵}޼@jR-7W^uSgWfg*BWhL{V43ճT%27Zo<ɇl|z˥+sedi釉Ka+{eTkK*{RT٧HFjZw>:5R͂PMYCY|<:ٺplQU[#xT+}AlԱK&(K'Szf:TF^d]սa楔M E2rCq=i2]vn/Th {t]$D sәם+sH ƣYB= Ϳu`dw x[v|]o2Ko'm;Sq)/ dJ\ʄ{̓›/#&x;9|g։ſw\&>t^q'1T-P-|nR q)[ەi6 R5Ȳ"&Y5x8钛cx#viyX 5-s{=n^1ApE Zuyʭ2Rܒ'[ۦO5\{P4ɵ>XqF9%e E[[,R_P}zܒ#eUǵ#Qe9uf!혟!zk¼X\R*zk¼M\SdwJݬ7Uϱ-+ŭm,ZܔcS$ǶL9 k2|fnZ%nXe˖ITIhrSI8eUϹ$Zo>ۭ?wR9VJ$6)Wn1,c[T{xJe v/.rG:x֬;CCXϱ-0'AIhrS,(msXU~ZVJUϱ-V E"xMJE땛rL@hmRs)UÃ"&NU=*CYvkG)^)DUǶL9}2uU ͛ bblb.ŢM9&XǶN8Pkj79Vb ^mX^ ͱ ,tX, Rϱ-֬[_bnl fac[Yu{Krc[,WN.^m\Qz+c['Wu{xreM|8V:v c[,\j'YIhrS 8eUϹ'\W b wR^mZzI {jwHZ;Xe V ^m`z,c&X{xO;kojzUnX}N(H9ebUϹ*V;vK0}>:SuJĺ#^\jjf73۽g><ډT8T80^䚇33'D0D0 @4 XPX`Q1i&! * ,|CTXdcaBPB # U깄DXD(ant^{A/DVʪC vvځZ݌H;J;BY`ƾFXF("+eiPF c_""ъ)!+eiنl*"$'Q%'@[r1BU3EH:J:"`ƽDTD$L8D8D2PĽUwʎ{҉҉HvE1˦ @$Q%l G+rHBJB"Y qvH?J?"a7HV*Q.F2W23HJbM`Z " FkHJbمo;t5D\D,TXy_ǽӾw3 .Gi7پWÕ F\ߐĕIJc^`oW(FWJ5L\L,2{E(zBНN\N,+:ܰ''QfXO$$2Jô MR M"#4a=AT[-N/"J*JdEh}|7\(?N/=Xsu**U66I6ڔlբE]MRM"6 I*Id"$$2´HRH"#E\~FҮĥ-۵/GGHwڕe7Nm#iWӖU~MܰGRz0#^Gx0#^C҇>0-BЮd-# 6v%mb< EѮT-ߵ~>XvٸP-+҆3]+>StF*R9*wZ<ߔ+ܴSp `^OU+V`}V(sCWbl_ nhG\Y67&`1Mexg(nck`9W qR҇.( @bԽ+C~/iNgKkGK #0 r%q x,< 53&y F `9 -g+HW D@,klCTzJ(tNNWkԕ0.z(!Q`t=}ipZ>C7K](ql}-@pĉP"DpYPVHpĩRpj?78T~ Z(@FuP~~?0{ Z8'kHB|"^LoكkBy'kHBIЬ͈rp>7&L} Z(SqD{-h5 kzN!^ C04DE:x 4θאq21eI ŧ=5k4U7NאЯ꾗ҢX _ 's,אׯAgkB\ARJ[!_ ɲkHB0nHD} Z(Q_b5dk}hEv } yZ8opt}-ύ~8_C JP޽'kH \r!kȿB0fWKNJye7 'kHBD!m_ C ZSkb~-Xb q_ %s$אrFa=|-$qF|-A23W8K_Cҿ)ݗ8_CJw8_C*J_ld VH©X5$k~v,)Z(ek BY9>9+m*0Vxk}ĥꉏ}]3}2q`2`8qP?3>8߇~_(kTqfl~}l~8{8[q9Pl>BA|B ٢Y>|a7b_8߇_(k2]!! bi_(#EL  QC#B"fy>|!^7B}QXL J9~qp?>~L;p)(P  fQO[[8Z?#~@_(Ppct_.\umUP bX1 @/5`):Qn} c{| anMBSn쁍>|!rD0FlPĺ(_%2B#$V BQXL A#$F3 _4̍$H HGL* QMw 'O pC#Ј>0|!F("&@<.ǀ kiYzJe" 5jB ,odܞX9 #|` Bln@k`Bȉq>|a\Jt:BX. ### B-)>P|a7bd_,:Bp²_ 1&L/ X4Ltʶ |!|m:$sK|!Ҁs|s`y֎Xg? Q |!7Jb _5 $BQ Cӌ 70cX9"b@_ 2cx0 1$/`B \l@޿/ qֿYp9C=c|BT̼O?/ 24VTUr]ǀ`Nd̂U| A 3C|Bnl8Gk!B%«n  s.`c~/1M/Dlt9hZZ߂X^. | B nôh-k `BI>|aR<0bۉ3@(a%/DXE' _a3 A "0_@!((QUiOG'`@4@`,N;c.0P*@ 7 ۗ}%#1 @ 5@AakXb qd!@ (#@B@@ah7bF@@X< ?J5d"@v9@ ӫl @ vy!0 ]Kgfm+ bx6M|~B4u`@@4= F.0i@۫$w{}vƲ4@&5L$Y!V(,kp 01 b@ D `@@# 30A%Fbj@Ԁ@oA2!07a@@5 BahuOlK b](Iq:g0 q<1p=c aF(CqΩe*S f p> `ܨ!@!(,EQ B0nfTs \Auuﲨ0{:1D @ (Cg٠.0a7Zb@@ 8 p܀@-1@ @ X? F]nvAQaj4±Pk f %' d2\ujJ<1` !7Xb@d@,5 !)! @hU;鐛2`@ā@8w׉ڜP=1 A #pzżx0o`:$v@6Xe2QFa@@2 11y @ D@f`@@?ј a@KD0=fb4Ah@MJ pc&@#h\ r K T0f0wft0䚋 a`A('`[uz<b*ATPJ4 C BBP9!p B!NAQqOjB*U "簈a! BaXKXĨP01,bZAPV U bp"f(\ BE)@-Kaq8rslB,Y # l|3-v/;lzNuf?#{cAԃPzC1 A(1؃ǭڸXW c BBXnd(Pk! BdȊ!0B! B`B58f$ B@&f"Yܐ)!PB! 7$b@P<5 B!܀\Hx_c"ADPH ǨP@$(b\A(+p X[T AB` BnPHRk B !PBajJP5\50KAAB BxY!B!րPPȁ0rg  "BQ" BE  VbZP9(b@P" 1R @( ,Y & p" xܠ! B!$(@EPGdb@$PPzݝtWb0@`P x0|dlL $$0аne0@1< x@(GB(5 K B)'؈sCs [iktĺP\t P 12 d@( , p# 03C2Fa!P(&!!?G1qr3bRCEDız9>R3B`BLʍpL ڞXq cSBBTn!$B\@(+qp?}}HB(>P˗ {dcR@P )17 n@( XL;6& vĊPXZ ?gCHNippa(Qn lA(6`g  !$٘ ,5& fp"`ܠCHFa\P(_0(bEtP8vP,Z<#L Q$b"!4@X0' N@$  fD̰a"@DH N ?Ja"0ꁥD8?H8vXtE pv)a4@hH 1' N@$ Vd D܀a"!7 #QX; ?/ovO29p gtkǶk GBy뛬{>L7#@DB3޹9f Dz:F DQ$" DpnT1 @$Dr#Jd ?Š?dBN ?JA\XO?hj{Nڛo`p7yeɮgю ? Qp"an)QvuiUM 0C1$ H@$ 5b(@PH Pu{2a@@H `޼c~0|P0”(e=pb@tH.Wzټ΍0c71S @$7Kc!|ʮ5M'ix*ppnf_xg'=u7Z+[\*V:bB)'mY֝zqf/}K_ui2Yt|;0îapw<狜WZz>Ɵϓ+<^Y? ҏL't=A?zIž9StOf0}ffů0fiWޝLޛgefcų?e駼;f<-+~|R_ϻwRt:?iniSo >!s}2ʠo')dlXITRIY!\eX%׺oݫ̼cۣߟޜx^6}zO[m=~sM)?wx7̝[yTޛT𴥞jim+6xG&b;@f]1 \e󏽷7yzuw0:`ަAwiYh:t;읥}ݨ;e̙2'Wd!;] ܫy}^BCzowa|g34Z0>At? .w&O?u~yY::yvv/|BLJ;%kw8xsw|B\u'p.uxe=ɥnx)IyU84So<׃^cW SHk,^(6@\!raTDž Dbo={浸톄NɅɫ4n#r8)>(/n^׳4^9ZF#o3MBBLvYQ,>^VV 8:fq Cm3mDjÙw<i/+uo:އ?o2G/{Wf8uz\O'l'crSr8:ey{M{]c6*gSj{Vv(fp.tδu u;%?p-to\AZ>ib>@m }bN dU^)(w__>Z@}!>c\\̺h|<~Fiڟ-N2F}]9^FS3ȧ 43sع2xWأ mN!C/7ӌܵ` noF6{7zlVNz,y9u,pU,ia3s՝u.nRn ER e%#ʡЩk5RS( T= e1 x;)%* Kw<ų^urteі jVYDyˍb3J,5`7sSiJ}>rE2S ] }RHHYGZ\f{b~SA-;׍ǓUOOUyo|jN}qf8Mc8x3^fb=>U7|ǹh~7Woˮ3o6>vw1?]L'Y}dW^n8ɼ<gr̓FߛL-/hlʡ1gy.OL&Y}k2Ifos?ۦ!}Ӽ~:NpÓ#{{G/WPO7O/?|wӛǯN×/>;>b^j~}|wrI܇υϯ|ǯirkԫ?&~Aw3'zpy/yw};|w×y?;9z~T}ӣ]_3ދW?GuR?ɟI#O߽<3=7oNMgޝNw?<yhώ߼6*,BS*I>^b2_/>ۺ c4y]b89:2PNûp˸+ՆAu$-?Wey« i./@| $o[sJ Kl̍du2~_Me'n2"fLS s ެM]^9$lUgu&PlOT$7?CYgC+4w㽛:91O3?`!.ͯ}zߟxR wLO,-GzݑwnnF}s`K^??z}z] 1΃[;61ҐҐ͏Ggo =iK\iK,-eM~e)nDWB&FWr|n79;~u:?]HNJNb91T]κiw/lLrHJ_$HqJq\$HSJSV9/,$7I%75.4'LYvC>ATHk3f6S''6o%{?A*T*ܿ %HJTPRP͆O?8=^MI$$MjNNGm$0J`(Nu+iK-g]K[peIIғ+Ai>YEҋvSdj[kIHbkĵ;җv/m1F Ү-u! `F1N_R"M>+sb[jjǯ I-Ȋm5q:N-)ۂTٖy1?8Ԝ#j$ֶj4Ѷjd̶F.Sh[CےF=˦lZ7; )zj޶j"QJۂ\ֽP Va0vQ̆>/J%4 g%oߜ9~ˍTKԆmXˠ5[*~ˠ5Z3~ʠ5AMD|)?>|mX;7 k7m4QINhs2-jH2gOEM;W.ƍ24J4e.6 F4Lz^EoZǘv5dZ Di *lb=!Uٞɂ:ho3]g^= 5s/r?gWޫw/;t ^[5d{L$od{J\,{{Կ1]5?!95,)v5\|:>7F7.N\U3Y\F76p`NW 6Q!(St5{Wpf6dZDpk^? hMϬRhT\T٤;~^'0w⯛ iox L5?b{sUlxϼo^+w.WPcKάRWf?i:2Gpz9LUD2-NE|5ݾO6mǯ>tׇੰXב9_= So* gYt2vqYdt'wYNl77FKof>ӵȵsۋ˫t:HgDܒedwT.uj%f3$k>So"i,Nv6HeZD T5lHDBB~ @&J,Qh/X7eZDΧ.ɖA2W; h}z+r*0_A_7FW/`g`hz.O̺V|OrQ®]dȇ`2]}0!l6ڄswA sfΥD?}8ÿ-z{˧I1ӢF1À±$.77%ʗ W?MO ͐ڻ@*?!ԄO[IJm&׃#i=$mܳ"Rv❍}j0¼uP~N X4?c=#js8=#jTfQ=H,瞈DR{H,^Dr{H,~D{H iôAϗD]X_ 5iE iQd,ӳȚd0-im Ǚ އiQO3a̛M{fS|^ͅof4/Jr)>_3w^]ܘ܋ܙj/ՇE3z<+OTettA.?:O˝~yfnyٍutVP> Y~sm =ὢG G5̣c>m 8"0rQ|۩)۫<ۃ$`?.B{kdd3y8&s qbѹmL4Ek[sYfl=n,/ņٸn' B߰dv&!8LdܕR~Ma Ӄ+Fワъ[+l5c4D?RU{qOJ0o·^> F8 %\(zW`8>{5sӥ欩 挤 st^>|tA;?Uw\9y\MǿX! ͡KXmd&$ҹL|pdӢs :2:߼z{x9;~u-^d@UJ5xz_yΫ7ߜy}ihGb듣×3nK]6Zw'Vvz-M^w{e+0љCk1FYqyweX[\ ey{6I{c.֬ʽ.{͔&KfԘw3 m.DžJ 7xYiQ{lV|5{"_f}a6ytfʴzNȺ`ŨzbF3g[x{7o}c>89Z:«>n9"nUwhD|]?5_@ڻ>Yz~k|z`f~pHQwu){@1h55;a09dA" Nfי~;$40ӊ' w1̉hkm/(|xd(yL|ScJ̍5AUpKX B3rG󵧩93|򯚝H-?o2GҸw[z[y:+~Z_`VtՔ5XEy| k*oE.g݋sR{ #thBT*zjG TiTo[-ﺛeWGTUf4\N4{=HGjo?2ˇ,~`sLa&q%4 r%4, rO%4 3~FeB~FEA~FA~FA~FA~FA~F%A3bZ`OEm0j0 : 1-j2)VWy@==Wo4LbZ`b`7[J"=>W<|vĩCkiATL"9gm`uzVh6̇=Q3ϴ6M%9ciڼElAXfwިSraԄlnzf:7xte6܏Kg0mn 9Um9dӢ&3@Nhk[_wΦB9s_X|tB~B?>n:aD:_+(XyM"}ֿ}T_"۴Zy9M^x7+*Ob~^_cV矽'ypgyG,-Ϙ{ݑ)?x95g/>=;{>U.&Sv8qkbvmcg=dilԹN7d4s>w2sx)w~r0f[9tԛ~d\q=Kɇlvq/=3^riSkg^p$wb0J\G=~7ŵyAέx^뛽"TʣŞ MUoٽs^x{Gy^e~rm~P"˴Oo.ʉgCw8{ykx%ϛ^s{LsiCqunw0ttLաO=_{ Ϋ_Cxu-`S/hr=)BQƲ[2nĕ@H1 `1&PK4 *H$^ḷw1oaO-ƹEHwÜ}z,pȖs@0{-p q\ elg~[\%+sS puW|~ vO;f6wSG x4Xx?Va1*EQZ8Z:{7({' F*Su$E[HE2TUC@͌ MROQak8;\%cK /~4J2 1s'@L04Bbs1MS; @H suaZ-@[kť8L47ICvS٠ͳN0=~lC2U"μT94\sLJ}ẃ&qHr2CErc|("q~^T5Kp} ;Rte fFRURѶe9)[T)[tiҳҳHF\6BU(ڋdNigi/ﳲbዸpWTb!-*-hZ/o06BUYu̻ʼr2fW/olnBU4L!FWߧHJbb2#+s%#+iۉF5.ew@^t/t/+k@ǹ$6$6HT3wS#+)R+|[xvqg@ %o!;19^Qh6sR{f#+׆ol`4?4?!(;Gl41HABQ%iRiB[7[̑ARbv02:?3xp[9jސǼ዁a(pi4sHCr_3AMм! }6AӅ.$Eљ/gfq- g);@R 7Wn.HTȷ+o ;Ay{ v%m=";W'YB%i;̹L ռM5 eާ- smJĻD$J[9%:)*-?[:qiFݮMJH 6v%ZbFܮĹM琞VR܈˻Q*s5Aq_ug^ /B` ZMzE ";y -@thJ5uѢi;īw"ǵc0ϣ@M9N˹$&-|hA+D7 ,H8 `x@0<EZĩr 5CZiY7F0G徭#-Zbi-`Z6p._4 Z sj a ՅEtt[[-VBe+5hK"*ìЬ glVO/#{UH̡RRD-aĔƔ5 !D^ugs=Lh_aqV;^]}Cn**eX5zȿo8^lPzzlث#TuvH\}rΠ0JJVhUJz(q QXqkX XB?O׻  yW@ 5qC0IY YJWe^)7-S/]LEA|ez~:Av[LRRDVV?[>' ߘh]VKZ7mTƥǥ<.Af,Qa "b6OlR4ƙM  Zg7*RRD`'Zo4õе "7Л7*RRTp}TcQj~c"RmaдS)"!$#0 0u$0Q` X`K";a"FZPC 02L3L٠ak׏.) Ŕ(hѝv??d+ɸ8ZlubE~Kt*<\;(::E14|L)B0;4;e٭۵B<S>STAj.?hVƕ= %'QY7oEZWOaޙ6$w.SU7cXZ"=Qnp0444eá51LLf FGl<2^p率 UVlܱLe n0CPCVS4P4B[5{5ƒii"ewccFk}@^Tg3404 V/vkc `}i֘m䯍=6/i {  `0VLVL۰bk~y0YLYLbL 9a?À1 1m1fy?y>?\eÿd<͞nw/ӧl{ڽɮ w`fA1| |S@k8y-A?|̲~73}[^yzEϴ4҇At?`ٟ>ճ|=:O/A6zaR+L}Ywg?<1|pCٟfa:J7+ܢ|ڛͪ{p3 Au|};_On~ROTַ&. c9)-(\f;iI6ȆըcTe*eTCI\i3/Ew!"O31 U(tpyy{GVAo~^ݙgQ$啐ˬsRo|eWW8yi~({矽'hľz|0܏쪛y;Ow&MqקG*6i>Ip_ܠJg<,9bۗ-Ut97oZ3&xKY+/h/;xu{nmo ɧ{P^1Kʌp{DžmQuʊi ydn]13=>J"@M{|T 'LZ. <|;i$F8Iuyˇ_J8Iә5i:6mn^;p锼$NM@om-0WS Z"yUvC1ۗ8ֆm/!54Lܦ!J՜5̪TTul$@%BeϜJ@WN؝ߧ)vuP|ݛ¤Y9G &Z, LȒW'` H/KR\`/0]ʖhY AWCdPĕy3=83nA 0Z"y-OU I0j|z3{4FmGc,dm, 10x6\ˊcwmd͌c7}'d%KDqa,@dm -ژ2g uJoяa}g=kC\Y[5q$w|tJȧlz1VU˳g>KaB0UٔI HZ"̩DƐmY#a U62}X>`T,bmXs HkŲZoe?f4s?Oa 1L4-U?y{y$}s=ŷty ްݿpQm`ԓԨ(_QVsԨax?{pe \2 ~#(qV$5>-֑&SQ&OLB\[ߟuM~J&lT|Q)n&R:j&@wg-C1 N4/Lnt#W"ئ T)iT^ז|јwgy4hPDM$pN4O̫Y2%m2^ȾFFu zgֽ$Q'QyTfJٝIK*^W\gV ``Q C.@jDU"V ߪqnBkrF Yvs-z>*UݹhO|al߳?f߫+*ޜ%T<ȥZYoi=b0ֽ^hVj#R~ xweV^[l*ɬ._1ud\fW,EEmݜ)2k_~"oXa>^T {JjBAmI<ԟ2EoM<)u4G㐀G3piPM =.t9m"år(Z6uAF9{b^;d $(Z@GSEwP̲rs)MQVkGlL8Y}dyY>cQaAR}km=[+ ƣΠlkA}# P##2Cn?*vәO*={)eZ͝^2~iIѢWE4n|Gfh9-p委{8D.k(NP<|y,o_rt9OylzO\ J$VA t>^WVsMҁu?ZKG'g\o$aoJ_s4/Ǧl&dqT2 <_UCzWsv5]FiOz sWΠ b /͢ g$&OΙFf~Eo]{yZOQ}`j95`X#%X"3oJ\JHk Wr?<~9<ݫgw fk\!4hWS6eJп@ r2Zo,(զ.kβi.ELjaXG h-ھ8".T/erF *WF|ѢwgKgU CAZ@i<Zժ9"-h;t'\2Ңm7ZZH>/kҒHܱMմP- iEx'MW7`AZi-vۻlyTYUl:Rt!`_{-S.bZ·o1\y-h^6jZl/Yoeq `2pq pazx*Z~-_6WJZ/Jhb);sYjK B QgUa̿\ `A!„6{ H1L1eY[KMpΑ[u/i{%)SS6ґH !S"S63O 1%aF cf,6-x()ݭ0CLDL)b|ͱ,]R,O,Ra0).r'771E/Qً`Bw1tLuLc(}}Yi())"*F)`)"b;wd d^)d cpȔ D E4":81~LLd~'EVoX}Lld2Gk껙Gc}vE,؋g hF@I]3vU,b\^"κ^L;S;S6ޙܸd d$l0ǫk7߰>V]y)KMK٘\mσ9 Kh\L˙Hc444 #bWWچQJ#w; ֘oo|+œ1h DxU q?,/ )?L _e1JJW{6*hFit(~ۢUkx$r WcJJQmnB]@-؇P44dTc˘[[*fM4NiNi"wXRƒNJ x(mCmraP`Pr8mr( (mG1,W0LJLJaRp:RRȕ"}NcHH&B\PPƆj>ann&rHy74&;i ;i"ىyI餉H'` `#4 %/QQFr;Ô( (mDm a׿A9ش}1IIMhS+vٺy1IIM/R̳?f߫Z|%[Ecwn>4F:i@:iҩ.]Ky=Οtn<LaX%QQƍj^0JJAPP 4 y= 4𩴍OVnqq+η4FWi@Wi"`:wVqSpSچj>va&61IIOu,>pLv@v68VP7i!|29i9i!ӺdAlapAO@Ozڪ44`ypLLgVDfob֒֒6:?IIjveV ?e%jſ31}I}IK+hU +4DRfNNFuj~dc&B Waz~9X] ꤁꤉T' lbx @6a`͙}#q:041uW(`&2( f챯k 60UUiUi!Z(6Fø1JJ TM^* *m#Pmc6 ǘ( (mD맽Iֹ:4jc qҀB)3,ߧRRHZܱ_Vcpp)p}6,7(f?m* *MPqO xLY{.2`_) )M$N14bc{Y&w)wB`U.x* x*MS7#͹y/or:Gn$<* <*MQ]27zl/tJU^"/a̕̕a֍?' ެEY,uZoqC0VBKZ8l^p'$-wwO5,+=fbi`biK$Dœ ei".kmXMuum+ބxud 4 3J۰WxXSm*Q1MiMio|vʙ.wɉ SSH-U'&Ii Ii"IKs޼t\C.LigtGc h60yO,"$=\㛬s=UUr}{#4 I|DǴ( (mE࢟^xN:;~˗ovb9ГyfK\g'b/~/qC4@ pcuTDɏU:-Us* B?kOEi.jc &@ ΖӁBTE˕s1WJWJR,gӳ5I>vTV5|0!sv&L?1,Xoe@J, yv3٬3I׃l0͊}Ǩ-P[>E{tj]FC)󓣳G|H.\>3wfZ OU>?D1Ns||6ڔVZ [Nhɼ9r;}!b1f̘oÌ U#aX!b&00R;RX@K'vSDh5j?]}^cޘ1s;ίuc~2/ߩSg5`3H1{mF- 0|y9@2d H^ -N(m25 (_Lto>},-2 2D! ̷[9=(/P^ Ŝqj@IT-e 8Y'}}Ǩ,PY E>Up;řMniɼͩ\,;ŀ,Y>_^rgd'XygoqYV>XD7g]*pM#Ю|j|ʱ*fF6a<,+浇wg?8<;;=:=9:5a(Ff,_~ݾ-G1 op82 fN8d˷1\΍0/Dh\ƵHl s|nDŕ:磓^8+̮O Wr +<|K;U>&@0O܀_>|^>@|"+r'#|@tDDWُ({q˷61Hoi+W,78LFN+/9':c|PH+V iū2/| go0+gi/s~ǀ+W>pU+r3C˗'?}հ'R(07``O0o_*޳֧?~X|.tYǀ-[>pۼcy|i[>ж|"m;a.8\f;wf\>|!6(,@mDukd@p[Lُ-|`k+&@|Tk#7˷8>쪘Ʀrֿq9p>_m+w\o8HGсdv 61,s@+?i4 |WDpU 8Vdug9y-ǀ*U>PEf+xȤt p|"՜g:˲:u'RHdӛ^VL0+" ))o;^,kú )HRzy~r{>:T>AEq*z b{T Nt|Q6k:[tio˭o'(WYRL; R8g,C`tY"畟]X>P|"hO/ܫ,N˷|﯈Pa6l..4wm.'ùR\X[h[Ѯޫ?[v .Hb9U֗_u*2rt 8|Z>|"QFūQŋC~pd-Z.1̚-jQ&'thn߿8g?E#|@t6DwW*<}vͽ[p]j 4/Ft\\,QQ/mx/AC/y:Nqt߻NpSao{5ܻI5 ߰/Eqgo1̷vWX謼'9Ŗbɜ=a0C-^g D, jsmPFZF$H2G]f 1̵BQb"JLkzȳSS) N'"f?9$ Id~z_GWxOvf DR[fBh] Ub >Θ}^&bvf "QcbLlpmzъ-p,3[ic m `OkL71,Y@1450,Y@D1XB|1 DAndldM]b"@uWcOK7"-ْ#aF=,N5z\˱1,gv ((1. l 6, 80͈>cz6WM1\`"Ҥ1`VHB;jFc lh V`cӖ&ʅX"AcŭL  Xl]J~n3 0+(V`b9ZkiH+ hEEh( p}1[E q l0| 0+zW D] ܴfL$b7'^@tDDrWÈb/w=^ ᵁ[Ekf gVl3 +Ig+q֞tV/"N?a-r @.k@ lt)_@ +}9IA2D_]"`j eWj|{S!oήK(MɟcV8âڀ+VPV5XYE K"O 1Zg@ ,_$4γIugEDXYbWįHn1 oOZj) 4{ҿ&bA  Ś R9s,1d\q RK, rɚF,6,n+sbb]QY ,8ГL 7"p@6d 3`6f+ 3`D&pnhh |„gp֪jG ))uk0`*y>s bY賀> '랬XňipWhh"s B#ΎXs+,8gs_>8zW J ?`nYܲ- kl;KDSF$ ! C1dWq JwvMHCf /C؈dp"4g[5Xc5bѳNzV^`D5h² +խoaY@$EU`] ,@"`rgݍ,,Ԁ2 l(3W:f: l[ }훓w׶6ZcmS-c[- " s6.Z4, YPe3awf)"b6ҷ6Od_<#X@DQ<1`(!@ 41Nߎ'_o[B" EYd2BHȜ?.],$h+pdӱIljA rc!Bx bLX 5prϾUbWįH3XbTWІÎQ1%SBqbVbfҬ`ݑs_.gZ?.Aqlc&SsLbLVІE dX%0;^O{kD &j ljdtʚl s9׾>rnX_bk~y=qwù-C " D@doG$dM&ia4'tF&Y-՚ݴ,4]~-D(H4+n! v߱OPpO1-[H"kWVFn B ʅ6XrT+W.q6ЩPrJ4hrM5dyqB x.Йqvkޜe!1s!`BfnݾRt6o 1b.\HEղ- !҅ \:c3{ zYIAu!B"uꟕuΕ{o3B`օ6f]QBυT\tТog2 e;wcBȅ 1F.\hmD1$.H\h9Huיuic\P!v."r!`B"V.8h5(.$وE~j\]񇇿:Qq!B"*ۗuGàJ(>f̅ 9M8l^1.]x<BхDRRs!B}i9W1.](ur /8pp pŝ\r-$[@ B 6C9/8pl Ƕb+hi>ڔ[up@x!sq*b]ݍDvh|ƴhu!VG =7a|]І9~mvB߅6: MӅD>]ԄQVlڿ#Xt!EGs҃잃qt!BQs!B"j.obmL >sl۹at!Bn `B6n#~ ow8G:6!a5[hN1-[HĸE~3Y>&@l Ķz6~(ώYXҀ8n?fY'\'0-[HќUo=< 6[!cBDIk؋7$xy-Q6мXlQJzegu^h͎fr m,{FwIo, ;up 20!.B\h#m j`[HeƚRdМqUZ=c:o!BmZkLHu;jʝcD\Іn1Shm1. \h6*Xx.o &<$lh:w#B@6ZB6aUtǝco~ͻ9Al!B"՝6umm!pBm] 9 B"B&E@dD:Q6&, @jFmőR5\jxg0 -@Zd963,i Q!E;:7Ja@Z^iEHa@Z!@*xٓaZ8ȆC[w~n0 -Zd#mKY҇ a ZȆA[rI`Hf>jqc+ƕE+llJd+3"`E6&_.?9u^(0l1+WdczsOqn%\Jo0+Wd}88*,֊l`-goSŊXS J٠T_jiTШ"m;c8Up,*"aT$H:O2|<z7\9$HR$e~pMEH)k4fO?<:/ߞuNݝmӻQ_X@"K?&B"oh-,+A}gE+HVX] "y̭[sv+atά|Zw3=pEኈ.YsW+q6-y?vE슄]\p+ѺTj!I\"m;w!!\@"k P"uk3"1%~P (ŠYX@GTLlVls?F`El[E@t+RJT̽{ٸWl۹c"`EB,QX 1p-zhHm \+=Yuak0"jNs0ì"YE6՘mt-Ü"9E6 nxTJ$0(QDmT`5 "!- Qs"E6.E lؠ⁹ώX!BX( FaPN(cO0$(HPd1m>8"@ETTP}% !@l\e>J*GAS*#y@ @?͏L }X >ENi ;`EODeuRء+z"mc D9`DXJ8vDl@Y: (P"*?{eZ{t%oXLQA$0iaaaw9٘CMyx^w8(_3l8#gWc&1G|$Bq l޼:Fs(aD = )"9scR,Bj~΍ G"ሲuA3So47,.u9å<yQGaK /E6l`#HhD,N:<#EGp$9|o< )ʄ43"`*ETR-?s" E$xD AȮEH!ʮTxWC"E6:F)S("2mpv$VlwƎ1* Tl@I{PTVb Qn.0'"B.k11XR1b!c&ƤHQܘ)ʦ|WMy1D*TLH5y[$Q18^z0Q͏(SQPV]p; \7ȺЎb̂SYP$Wy])&rͷ|~`SL$<1u "ПbIn(ݏiQ1b* 3bD 1 *@T,rɳ1#*FT#7e dĘ*6}>O $v~͊0O)Rl)mj}D1b)2(W Iba8PL<6 X AN13(fPlc5bt>ƀC 0sb6nPQb6P(K3 a6M˙-W9O`B1b"@` @A1D eO+bQh3է[Czk46кѢ\}c,!nl ADFŀ m;& @BF?|L0C\JI ܡC1;Ԭ`P ܡzyE1bif0F\(rАj#b*QlDM|0`DH cBQ XPn<}&+n(6+W(nWԸ`Q `6rӕܔ0(QlCm `QlcmFZ;.Hb !6b!( =z۠G:¬XGu%_`Ih ;FÎb6 I IhIJ1b!(nGxSb*TRBI1pb7i}Џb(k(q^X`Q 83jTR0(,QL= ID1@t)ېBG S!Ar_E  Fsb6z8BE Ā?ⱁ&@fb DLi?t>|P=1#.۸>W a~Fa9O,\`O 0a ɠEfu{bDx쉁=x< |T:1#(%tb DNJ181`pbg0kbfSk 2 3|db@DLҁ p010#@/ rOKC[$6hK"t, "+Z$TXK`K8Ćc@GQ!%#f+sT$6 SF p3ːW$6ʦ$8,򀵤i34aeH- [(q- Z*ENN$OH. \e㚂. ]e `XeCksV$6Ί4 I0t%JB4*"P%U`JHY $YIlc$VIl`(˪Y$60Z $BQXրؘ+Y$p%Wt$7i+ Ҩ`J^y*- 0[a-E0%K"t (- "-$. uIP.f$lIl̖$iI -2- [!`-1l29/ p^e2- [F-$@yI{LvI.Xؒ؀-c2%i•nIݒ-jIՒZds[$Bܖ[2A5-'%Q_6.'%iҸ`K|G9 / 5-ݲ9(`L [V%\ݒ%bHcK@D肵(;$Il $@sI4.%!b_Q]0%KbCl `|Kb÷4.h;cL W[0%KbclG`xK"oq:..11<$Il ƹ$sI4.&%!r_^$6Kq[$6~(3``$0`Ja. \e=[!z*#]@$BH,K%7N 0qiX.I#Xƥ_$D=S0%Kbc4K0%Kb4.Kii0 `Fh0\}/ >E.i. p]!݂ $Il\ F$zIA4.( &!2ak!0 @`ȁ. ]ejZ1I#`KЗ}@D"Z/ G#7H1 yMؗK㚂0 a") &1a!%q^6D N?ef0;-T01&bL1+-&Q`6/%¾8-T0%K"%X`fL̘ƌٸ`LLZ1 sO6&ĴӶbm ~imlZH(miBB)$miml_بJϺ٠}\Bl_iwLpy?y1mŴm5S5'SBKgc"V.t|l?ד"Hb4?6$دI#ïy)<3a;<̿wl49x^V9_#6LFa >Li>cxL1m<``\*10 0f)оv)fB3n+p?ncMp4mfƒVs2m˴tz6VΈOgyc3N쐆5H4m"ϭMO?XkF&hh̲M/+pfc;54mّNQJ܇@mm` tovb!>P~\۱^Ml^U8LPۆ ê 6CS/Ï5m#uPP @'6Ckj;?mmp3j 1FaYO[?Ĵ6z6Zeg%Ӷ}%&|sׅbOFq>h~ia?4<Č60~Dc?Lj}6ԶQܛ0R H )~'PjKaPBm"@(ԙ&ƒ4b6pjcP8Bm"GкNo~sβ`t '}&PH>[uYl&jD"1>d30Ȩ d$2kԶq e1 R| T6PD*badQEm"yAFmԶrmNjSIt9ø6T\h!Jm(m%76B`%QXԶ!֟ aVRXIm"+\ÒKjaI #&W/=LHj!}6&$&Bd?jGN"ÒKj+,aIm%m$Vv?Ԥ6P6jO?!Hm m H:a䶶h/_@yZj%U %ފOsW-FVr=QK::j.x:.ض. ZmגDD`JRYއ{pNϫ[w {so&jIjBj%զMiM^R^P^"rKDZ-Df?~~LKL- MXM |;O7XKҎ(d{zD8KkC/m% [Wh(rIBzߞ}=ͺCN!|ʮ.d<͞-ؽLFٴ|0>V?Qw*PAhwB;%u3˺St ӬGQdN t=A?zO? zh '|V>3TqO̬ 1|Yyή4ƊgOywͪ0pp3 y|};_Ont{ɗο|CP}21$9Ls'MS<Ro*ᣌyϾcr<*n6}G<:B@̣\Or?^w䝧E>`z/>=;{>y5o>A+jzhZ-AeN>07y,M<*?Y Khd->;2Ytٻ \`QU".{[w7h?Ջ7/_v?ui}Z=DX"Kj/g@3=IXLRToG/y6ayZSny۽RefHگڻW;q# ۹ܠFbъ! -\99z磅 {Tof)S>[ضM[e 29ž(waaVv::Iwv̤ώKǏpO$<kۋx]^LNQ^,j7eaK![*/v{Oʿy1bJ=}] /fM͜=}[ص%)"ڌ=Q\ç80W1P`s:)*m,p)*|xyx|K*˞' oψ(¿9ǞyRc _( u/9`=pm{&Ʒ_:"Vx<7elv,u>l<+'Bf$@|Y te'-,;,u}O$3\u`=Pf3oaL=Pm.U A%Nj[;K|Ι^"4e Hg^`bVQ;{z_GdU i:m`u>KCVT|SyYab>QV|-h)'35q7ŊW T05su`R>muXq|-;r`pۧ;}Z#V[jVD4}"L`b4&(^9|$ǫmd{؞x}*os؇sa +^roTy7g9 Fi57%!s#Oo#xO3 guPS\h?FwvIؙNO޻a0>pX| ..%.#y=dfsG {*6WoNkϼgo;ϿsMu/@I~ݺ/!Yk| >Ok|쵋}-L=ow [ b@ |W5$aHc-oCgnuwg`}Ãtf п2v06潘6C|*&^1yJ~L>௳XȍI3| 6 Ϳ6EaA­minx}b4p|qx]k_,O{s\}S;bl٘)(uWQ}~y߻zT|0f&ILxARyYF8^ٿڸlEQ6O7a(szCk<+T1D]fl1]*S9K\#35/ǓW`XW^oB1_ x^@六Z׮qC-@t]`S!`1T@o/"||r=6PaO)䱭3d& lw* \#Ѽn]WcP\`AR&[_ e }Kjx-{[B-/HKbt؁o )SQGt&Kx]Y:Ө%g%ij,_*_ SFehv=c9ث(Ӥg5˓#TWLMґ4+Mdk"hB>P^xܱ]C#u+U-! -Dk ObDZ,-KWX~aۏ|"; ?k"sEb d@Y |6^]-t,b5z 2REb)3n;-.X- -`^-=XQ.M([]`N`=]8 ⴈNӋl,DFlu# |Ɯ++E$]њӹl+p+E[QTctvWx**eWsLUH}IJlT*bޒag\&nT&iNåcДҔ֎&Uc))EOճq)Uq/aefQl_0'J(JIQĆYCiCѠ Xɀzp/G) G{EG)̎RR6zf/,?))vʊp~0[N Nu k\FbbRbRDSԮx1z{AA2?ssrĨMo 1IIQaMVZe?;);{;)wRxRDS^Т\(@5,_Q(9&1)@1)*`< ˰QW&8#Tc(*W,\&`&e#3m0\I]IJiq/뜐)(a~13)R҂J JVj7'阑l$kVURUR6RÓГDhY8̺E^޷;/]s}\ K KX"x|vT,3?~}IR E E;M-LY uR@uRTj;sUbp9OS zR@zRTS}? |R@|R|Rlԧ5Uzw5Eff+C)C)j~u󐩾-/?]a蓲Q^ r2WLlU^<]vd9bV͇F7p9@) @)!(xRD->fyRTS CP ˴GIʆOf#)#)xtҝlpR(%ec)sڝw0HI Hɗ.R.RDxQ &4i$Ҁ$6$QU1dHdH!Cn꺋! !M<1Z]|O>DS=O?1{u1uHuHC!d-ICCb({1=H=HA?-!A A @8MQ ETy{SxƮ\S*.>Yr0eHeH(Cn<0iDhDH#?1^H^HB_}mfh`h"ǯELLr4bZz{xODc006fZ3 -CjEKXHc>>&;ppmv,1HH`GGL6@6D18-Q7&<;vDSx&#M `𑶁6ЋHAF>Tg޺nbhUҀU64F%i/inj4D|Z˚Q.,_ϸ=JҀJDTR5aNNqxVf4\G"6LQ\__1 Еj-•0=%m/ {Ж|À% %M,o; WY|m8 ,V;`!i ɱeUUv<5|#)K(KHY"k^E)E ,i,i"eIwV1ŤŤ,&.pO~5XLbjh44&2i 2iiج>\VyșA*-aaܫtl =wc4`4^'<8PPHbL֘m<(7ӹ 340||¸' 'M=}Ia~XY餩L/~X6Ȥm@ ~Ó߽:z}vx4aʒQ6ٰDQIۈJϬz23YzqefܖDŽ& &M$4q;Sh% h%MB+ ::$Q9XXI"~ig)E}=TÌ4̨L:@:DQ|4w΂DD$ca0 ?4f i` i"k5ǯώN^r i 6D imȱ5 +M/J9URRm#4 Aڋ͎`1ZV7h }:2?R]ۀhh@#6`:? s6<.Badc&ҏS}{r8n`Dgc; Z %m,9Νbڒڒ-q" $m$> ٸѾWʂƇ\aI K :`% %M/Q/_g^qGz>2dm@ V>=cp'w8S}LsiNәQnj&MxT1qO$. x0O<&`oVFlxU!e&n)Ns=s[CɿWJ>0|"Cg>%K򉰤 |ȷИUȷ6q8c ""~F> |!QDf}5kFA;I$fm!~?1&LOɇ,Nmז]>-Ux`cpqtgU>}|"ۧ &@cF𡟱(7>v`w|"vUXH 혦ԋ~n>D`D݉||m8 D,bm65]=9x:>Hؑ( ;}'w< a^;>~; ~L`#E>|۸;.cHItcl u|DN ǩ:> u|"R)6c0t|D!*IcH߆qt Tj<> y|"<|9< ey'Ry~pU]Ƿu6p.'rv ]ͣr h:lFG͐{ĊA;>v|"hb-!qK-S2[ec]OtxBX;I}1).Xp<>;ǰ`;>g1bĎOD젚,-^WN=$H<#4guX=>ՃXBr '"z(wH?ndאg mL!Y%B2=xȷd|`d,@m$`}>1mڐO qr A'"PFݛaƍBȷA ^BKU .\E>;`1to5VŠ @A>Di>Z >m GhAGH@ qT `0!_&dƔY m\rY@mT K3:y:`V j>"`<x icG&$ȷA]1f˸2d'BXNݮŘ9s'2Tyd3g?9ȷQcڐ!H8fad ߆ j>bJ F rHB]rː]|` 6v t!_.5 *@|!(<| 6zyz9F$%a`  "Nq%1%'RPs|6*5V{Ǹ pA>DE)A>P|%HXƹZ}nyCAeK_Ul%ys  Rozs4 6(F">3}z` %;"?y-;9cGb>x\6K`Gˣ.=eQr= s= EzH!OSQ(C}rɧݣQT>7ԙ|>M|fwG;ufkޤAoDprY' &Ns,,8iA'' & N}{,Mh6Q cLP&MD̦arIkxuߤL &jJAEt6YZ! &:J!$AX44K(,Q@ָo-9v*i"pK֒kIZZ`-阵iF{> ROXl 6 7Ig")%Fu% Jldu]\\#i+% ؒ&bKBmp_RDKK v7Jc\IcRb,i+)& &RLI!"0L I&0i0i`4a0i0i`A&& &2LvHIcIä %^0i0i`4a2ib f 0L01"00` a"0`A&& !2LShT V (L0a2a20"$3Nl0l00 `bD  1 b'%ƒK<8dbLҐXB2 !L~Bg'-ӾXN2 't#wü٦ﶵcQ@L IVtx6L@ڊ'?y?k9I$$^}+b%(mԍ8K.I&'g>Qf{vl8ޟy[_\M8`Lq~5^jaO뱥fcsi/jb+)Δ9S\Y~Rr!U"ʒ}&LLԿn}Vc.`L꿓M*&T k^R:tlOݿ{J]rWsW+*TkmT*>LILG`hRt86ebX2 N8z/dq/Oʼn0+ì>SV(+oޱTe@21jȁ-LT L lp?ƫ U&Wg<>*V!ZU99,QLLbn\ 2V"[E΍ /oOt !rVݺ+ĕ9(qe0qe2Tغ%+IV\`&21*)m]/ (}yz2j%岘2e"ս[f@L bʀ~e brke2D#tpH^͏-)Z{˨]͏E."!\'pp @L9(e0e2Dr(}1?V Yfu,@ ,a?-ư,`X6aZ[Y,\;W+sXHYL^Y lFk\( 3#M78 2Nh1-e1Z*m9DJ՛v8[AVpramh1 eD@jWdoh1 eD@K mZOb6el糏0l( J%*Q]np}}>٘4ŔfZR ռ~_0[>[zғۘaʂ e&8ޚ3:Y@,uV`1HUP]gnۀ& xMܤS?]ze1ivg9,["]R7ͬYO͖9{bɂdc&Sr00\VQztG4g_OKŪU'qgt=\KN$'K8Aoa{}}x{ZGf& ^%zM@ /Yl ^A¤RI%[t؞5InQ]u{K%5ESp2Nɜcb@1Y"D)lp:w?5mQ3=>GTaq5F?UMjΓJC8d`48Zrdkf1 eD*%궹}(A<yP`78eXtv1 {>,,RH1xsKU) R{g֍6N8XC7Ƙ@Ye'~!b[Yl̷tW+K41p[bL^Y ,tlt(iz+ WyIkqu,OfW-E4,K԰(^G3bʂ[ecnվ@zk-,XV6fYr) %S>8Dec T6J-Nk`QLUt V,(SL1=GSY OYl x.O'T{;EbdcSpS-,XP6fA[t /Y, ^aȂYdcfQdNoY:zhA! $8|bn7dcގ}-O &)ČO[ YplJۡؐlf†)!J.CD "k_wg0`'O82LX)٘R)7,`٘YXvn{! ڐ%iC#YplKxGRϜ` ٘5+8vD ڐͤ q s"@A6Wͪ[-#Nq1dqq9ܞK KX,a8ij\i9.e]X))@))bJI-fI `I "KB3Q)ܗ~0r~[)Nz1d)0RR$-)n Ot)9O" pqr,a!]l9סa !)}.{qݬHD|CnƴHHAEG<*z:cgg :#Z_JWjn# I"ݓr{->F`TTO/kS%E -aM !)!)bwg5G:,5Y HH\qV5$!)~3"e 23,FJFL)2F$ 9wWFH FHI4B{Us=CJCʃ!%CJCʘe<\V7qQXyx2Ƌ|KL@1bdg*PEJ*21wqG66CJ0CʘLH LHcBh-W"%"eI,y⎂c"%`"e a۽|H |HCx6q/y!tQQƴ+Vعjߪ/]+̀1dVAJPAʘ koꎳ!e&:[(}@({? aj:VBJB^<+, AJ, D6QlKL@DVԮf?J`?1X V@JP@ʘ2e l%eHnYgۆ^y) lh{cPbݣݣ2^|׃71(H (HCAرp샔|npy5 ((cGBPvڽAG G?-B_b$ ʸA@DGIt?Eq)z;{t_9]|S˻ϿY/Wugo_/os*ެOviO.1>VsWSТRk-Ƣc8OU|G߭WmO_NܪQPiYϷm=߻]n=:XxP%b|9-\J r3Ou^񾪪Gl?Uy|^ˮ~ͦu_7}ՀMux'd^֏s}Y}]z>O0c)CQZ}OUZX*mfutv]u_mip]a& l?qƫ::r-:mWU:YLwhY?3S M}<=%QOGE.?omGG'C_YF9=^.Gw7#7װ0?=ŀzm#Oo|:m-6U|ͧlͫo֕i<#U ˪ؾmG'ћ謪EgO>zث+?ꅴ}5M87/˄UEa?^Vv YFq2*Nvv 圙Vs2Kߖu)[f+2aAfl=&H4R:rg}3Ux&Ԥ.Rpo3X7fPŖHײڌIuq`q&qm0DŽXNOo5|_nG_^ӣ/y|h:=nt$JNXI<fw\ont@S̺OO^,7[ra!(q(썗L'1K aqali͍xΛM`܆/uc.34]omU%ZXmJ?? JڔؔVKbe4g/բŢrW+,$0ץڎªآ0J<e/!ߺݞ_r+:JRB4@r%.\>ޡBPXJ\*/x))$-$egVc3+Saa*9B9%Vv?W?eMxט6WoU}d%b5`F#~nf;?w\m6%raklkoʔp̻ԀV~|kڋbt?!0>8/v_bwCH5Q /CJ?8/v_P7A)CJ>8/vD_IP>8x$B&( >9|]mPބz>jNڋ7@8O!QeƓa&Ohɨc\f&yx tg`{>@O89@,8 Zh~߄4Gcpp8şc,a;>{ȇ"8--(q\`SpƋY;)w꘲ Ԣ οK.Ȣ Xf wGNc[Pb_t؜gj=]l)Ӄ~zrpne 8O<ǶHp.##ho2u>7Oy"vn^r4a?(=GQzkg 8;OΫxsXN|7mm=G zݢZ=G艃'zc/<牬p:G"HtRHxxOoqʸazt\p=C"p^a|Ab﩮]S=͂Hf>7SFQ2,bъ?ua,FavN0#@1f/A<eԵ@T'5]93:p?=.vîIp-ms ~;vC80pg>\Mz,޺Wr~`纜nx^09΋V@<0T_ґ!:f8B6ZpPoػ>x߿iM=l*ROH쯋-uc&hvKM={716マѩȿg3#.̯v~twt~>Lc$DvNs$0&$ ]&m|EFs%hȮs"&unrA G&RgܐO_s|;79·Vߦ/DNVӰ$N4Zr,moo|mwP6 $6 $o}M.g^s/`7:z\v8a?Ng}ߎXꟾjyXO#n'#,0u"Pմ;]VWv4FMWU>~E6rmϹ,yIsg3Q@ruvǬW|;Ǥ9X5yPҺ՟#=gŅ?ōG8wv9j{5?ݤMRu]+/Ul4'D? 4!6I|:]wz:c!N{ˍٷ/O_>{?vw? h Zj&Ls/1ʹ/?,1ij}34-FB'Ϙ&=QI}?|h |Uv=]UUˋ$#w9_n#C9R)%7dۑcZI֣/k⯮k|5Ku?P<n)qྏG?|խ?xEs͟ٹy u(TۡP!2B]v!!Roھod/ʽ"Oաe evfvuž epfpu,e\f\q' u{9ʝ͝-T=?ݦ?M|Pk5_Qq?nMۤ`VzOaT4J6M6:OWzototiѺ:78@IIIIӒ)۔i/$ "V0nrr*;]-& B걸 m߮vXԖqȵ9Zﷵ|AZ%7zy<+|#M+o DrrkخC|÷ӧ׏_~{  fEwU,9pCK/F7Fxzvx*wum<1Z3GL#/K.}0& ʰͰGRMRM@]_|vq^^cXg?}!|sXN@f2(6(0}{tx4p5AY״YG!]9qXri!w/6ӠjjC-ʡ͡6k'jV->Y-?<ھC)Ӷ)ӒS&qҩ*ڏZե[ļ^fQ"ok %T&TKBk{7L>ԡc7eRfRKfYl9nj}Ԏ!4EYڶY^|%}nS|aQm)`~ȩg&mcԾڴoX;8h;ϣ~ ф9wrms%tM]n^l lCĵY 1V'?Q9g֧u%+QO($ͬtgtzܻAwq_0:TG-SRm xx\0 _\թ@}p+QmNs"V)(@AH:NӺciUШaMrS2`Qh{}1vI[`1sqVgk8PߣhrrEۏ(FF em(!r : ea(I(mF/]GgUZ\zv uGqJ65L+v͏o]Sz־İ%5S„sI|ӘFg\rD{Z2~fˌs%-3 n_-5}x15_B%ٲM!'(N<:!QfD'(N,;snTPmIۢMPꝴw6XTG`IR'}.K|ݫ#;1'mƜ9E^248iׅe]=(N89#&mnl{v3hPw-uI*<,ƁጃcLgyYL&1減"_8e7鯳Jc 3ƴ-caӲXoMceAcd_(ƘaBGy/12 eop11& !1&2`dz(dCyx.];u{8mWMnJzNW8,E| SQ^tj$P8y *qD;y);Lq<T>{(>Cv8 CP=2I$xBF/k7q]_ͳ4;p IOav4;֑W~yI%zuw@QDFqGJu:W&/ngF$F"IHݧE,' N.s-$Ld1frH#J;G#ÖHP5Nz f8D Gٟ,xX>A1 $TtSW=aH$P\íΏGd~'1#@؟} c H$bg=+ ? cG#nπ3@;3;(pkKulyV,Y0>dZL9=Y#cG#bO<{=HebG#O`#zDSxYe:5˛s(exQ)7Ls{`,!Od^u<<6€DreQG݋r3oDX,1#1Ƈ&vlH0}d&Ӈ[( %Py^m GN[\wsGjc/?"#T{u,a-ĬGXX U@pe-MDm~Kc 1tNp 'PC.3H Ž ;]m?4CopGfy\%{$=2& `Ș˼1q/ H{$9ˇĦGRMb) z<##ATp )ě貙@%6z$=jP kIH>0@bG#cϞ ܜaܹ֐HI`d mpBt!ӅD Fb;H$v-&$BJIKƾY$x؍@! Cv-aEH%"v_CUa_k1k ! $BLv?A fYկY~~y=_w}kvr5k7/z|rƸX_OV#'(ZK(ߍƹ}6E|j]7|T7|Vmf}Xu]v/~tQ',.lps2[iOz}U#|ry7W+ţoqi[㯛?zګMN|a$R>P}OUmfk`|@onVW*Hw)neۺ-m[7M:h~OVW\WutT%6Yrzf;:Z`4# /Y6C?z^}ܫQ>1՛jrTȦzR71/о+dZxf>ZQ?uE`4yúb@/kDU ˪ؾmG'ћ謪EgO>zW~о!Y>o'w}Mm=,X$ߖZtu&cqa\֯g}yz}A8N%m]l_A6CS|mZv#'zv~œ60gmo2϶w޷VٲY\3:]no] +<^]`~W򤡓r+]w2SV?VU/\ܵIxF&d\X^`_v)_۴5.;w`=)Fy q{Ïmcb6^Iq]v{ɸ_2%zG'"2,epţ]mz)|, A<Bc՛Ax3(;ᔈ!""HYBpH +2nWz;o^6%h 0ӛ%|9;q"-| B fPs'bN쫜:Ve^G=vs)bRmWyݑNηznNTۻ̠ {a͜]PNs/b^$ݛZ n[me ڷ15pAg3xWt6˅>Ϋ-ʣg,3! 8i,l3,."n'Cs!e_>Ns @Te-`6_8xL1|Սw 4i迁Cy _ @5}itMͮ_/DL?5ie0su1 pJF|2@~!/=WRG%*GIN*2!4|@A:"WhJ~8"vfEfjлzzGGbxj$c0b`nj- w6I׮@n)>*Nʈ=*>N YgeQԓ24qO[Ύ|mzA8R#vƞٝǍͶU߭\] ݧ?fsvH*žZڰBZ34CrlipG,f[N 9bs&OـC6glޭ'KpF vweQ8#4 god:z=̀c3bfP's9 8v#vq:'miĎ7d;t} |AOCy"vF5 'm&%ﴍq8CĎ):tgFَmGu8C`Ě+x!s&u8CpP QU^ØiOp1!bo]PO9 %RZxһa< &1tnoy%IN1){pW TZdZ`Ey,Ef@{S{Xi׳MT@ldSlb}qSLqv륥LRs(xK".QGV.C<]/v%W[61YadJ? PDf"ԣ@u|CA},2 H|HAl< @nel254nÇ}(@ A/ ^fU\-M@p2J⎧!aG'xR"(-NI$~ilz΁) Rr~n}h;k ehJ%Dsp˟!`JP%D/cV0U"T%۳~J[ž&[ʮ# O DV)E憧 cqS}`Jn%+h.>NY *h*~zS5U W nupVJĄZ6DOJc"~CnF_ %܏uJ*YvsW tV"FXe ZGh3ؑD WH[&,X Ѱ.n!. E ы%~%omfnX3kM) G "E }M윹ׂJhJq^J}){i-kJqoJ)Nכggߦ58ك4%bThS,R ѤۜbMJ'%bT)FO>U-qH+5՗`{:v@P(A2gu{Q(Axz-IopJJ *Z-IRRH)A4䘴 ac\S(RJuH81]`[J.%bT9k:8C%bUrك.7'tJd«))/ob LN 0^pZN `D̝vsL%܂gRlUs ѹ8IB,Ha"Z8ht>[.W'Gu3$ P-zEc8c$bQghPw$<d+G#soPl$61ho ,'C5z%i$II ?@DLIJ[J{$>1]jv¹#8y$b?cF0#Ì?~v#>2y$v$F2m~ʑHNEl`Vy # FW'! ؐ$bC+YU' pkC!IԆ2fX( ɘP%ĎH;SE"yPHbHU$TFXޡ1" \$rEp'F$$@EMtg8,&K$%DdFC!I(6CX ɘ.3 |A{-[όgt%[!D4%s~5hηIOD'WEY"yPHbH\$crQ%mGqgd1ֻ# GQGU_'.W?Wv8q$3GBd >$Dk< %1X$,DSD "38;'$c PX LBPĞ)}CPT=N~ƂB2 XjwBnӔdJb]H.$:0} HR5Nelۮ'6c`ID ^UN{(y  So0#1'ZV]|,uѸ#{ N9A\bD+w-8wӄWi;δdgnp85#-Y;VծmzE "$@~ n&l#AX I2uĎGHs` ɘ-K $ @ ^u7w;DHycc "@0k*6$@j?m <($1$D%uFNbG#O%astGzvָoX㑠HCύr i4Ixw0rx$`<M8; d<(#1#Dv{9@Iy:t% I:oÑ1gX,HndL v| ֎<08ޤ=nޗɑHӭm3u$9IshdĀ@G,Hq$Qơ]F2#AǑq$q$8סF21#ёDD:G1s ]bG#cϾ )D]bG#cO D0#1'"f}$>2 4L $:>KHP|$QQiKH`~$a\S1#ḑE <##A1' ]bG#c)CHCoS.#DÇrCH"ü I`Hѓ> ]bG#cpOVz$(=2$_ CHӭt] .<oԑ1PgKH@pd a].rۮ6bȑH"#eh6Glv6 %vt$8:0n ^y97XHzC[b G#c'/0n$Ѹ!>C[bG#c.Nrps Lޞ)?=1 $;; ;YbL<Gyu3@3*<2&diznGܞ}=@R1(KTaGO3Gޫ²GdՔ' `*(`ְ$ `*,(~Q7 = E{]0>QDχyR~?*&|0Ɓ@*aG"`*L( |jsx<*& pqYGdB6vq2bc*(tT&N12LTQ(c'w13{ : PEDumr`*(@tѡ?}Ra;Gj(l(sΡ\F/s9*&Q1&QhRaEG%4uis-TQ("YoWQד%!l(0}TI*8OƏdp '`=*쭷ݵ~)zx=|?( I\aGO{}>꠴´Gi;~F)1ޣQ1HĚG5jo,u3= Edz섞ObG$cUG%iA$vH;*& D:FI(PwQNst`:*1}Q1fqI(mT̷K{Sc6_Qਘ1-qtnw8 XEdqmKb%GJ- Q3@38(ЮeCXQ ⨘%؍FZuƪf,QWصQڨk\띆??_n?U]57$AQ1gGA0oѼ1cǯ!R)GŤ}~zGLQ&aGΣb:Obѹl(vT  :cL"o=Ζl6G؜ Ӹ9v`;cGãb O8[?(zayGb=rU˙ˡVbF}MV:n@7 ݐ{cؽQި{|QFڣaS6 (ElF5 Ck8GP25'87VbZ gv;~lW OT|aFb4]/n 6 El1hZw}W}HvCSH6(g.=Np ר\3@4.nd?n8e}Ñ>l(hɢq?8&w]?Wksvç>3Q٨g3X"b5gj:Ƌp{-_6n7*f$vT p s#`8*)$LghQT+Á'7h4TvVzv^;,~ׄvL3n4 xEiĤc5e42t+cXJ [3Mfhq|WqR8 qIqkA(8)!r:I EP4B\~C - >i&5o47:4l4h6/=56].x02oD߆_Iט@q33@0жkjꖥ~72}[p;8 M4phKx=_x쿞aj[F#G{?/yG1p'OVy4<:򤕜ϣљ|np^Gyz=:{覔.3z4TPhrr,GYZdj1ggg\@wt E ar~LPv= 4=hpbqarKU *<ɥ?\~BMyfG->:cGȣ R'zрh"ӭ7Qc(GP~"-`+GV _pzMGTXѠhCgz/%΋@3ĉMnH2}΍FSN.# ƍ>qq1f6TSm%_Lחﶋ$_/Ve__r~hn4zL m46t+c8Q 6.@DvaݿN%cF^cxMr`E*: ЍA7?.h"dS]U~#[6`1ڎ,m=6 !5S L19O+G+~5p_D.G\}W!J,/aGߣc~O0рh"C(=W b=4d8£ OUG=8aãc;s/<: pq>]GgurG`jGc=j3_y\watG8.M`EGc&'Ǜx4<n^iMcz#IMx햕;u\.gNv_cwo-1TXt~/ÓX~6{4=:f a<{jAF8}Eo>mїCnI"4H@(ѮH[ݪ!\l{I Ǖ=4Jܷq%p!MW\ǟKRi@4-Rtl.4F:& #KH#B鹯X< xüHyV`e<H}ngqܕHT-X-ҠZ44E:1.޼^ֳ#]dqKnHܢCZH(nŒH#vӐF4G:ϛxpBH#Z<7XSv8<-'L%itJ%)$/0&gds 'it NcIc R9׳OG"i[H,$s]yrpQIE%zhmz|ͽm>{ZϞs;XT *iD}wL`MI)i)iД4US:zn=~ Eż^Ix.6Σ!i"DخE}H\U_PtLEJ?@{z@PEl44pK-cAl/7sfq1XDbVN7 V&f[bPe@%vuz2TOFZS2@H!,%n3Vcf3e48*e7};(pR&Iuk*N@PAÇ( *Pe$M&C&ƘL07Ntn4hv E&1G6]e㟹 E=+1pL-b0hNsaaȀtdQR:9 =21h',L :JFfd@M215)1lﮘS2)LP8dPDŽ<^2\aZdS jDdfd@]2Du*47 >h &!LT|`2D "NJưC%C1de21ighLWڛd˗g L&C$&w;e SJ(%C:MS8d$R!JI>`d,d&L@bE%cod@21)fH$.m57a}db78LL("jm'dDD"U^i`?nѶ|M!ҐIC{L4} C!Cdg꒦Qa[Ȁ-d=-_.vz}S=Emש?ٷ//~|=}򧧰l/| "3ȉm v !ΐ9C2` )g1BT!C]Mu2@!4Ʃ|}X뱠ؘvUIr~j #_MŜ8}J?;ZlX},R ;cbc%а%(`ؘ{acADՇr51J}sbOӅx铗O~oؕ,D|#b?.w弜?xdiuG^oF*!(ڛ܌ .M^/~|'컁M &@l6٠w/{Z_};p2QED?(=)Ϸ` Y)t[7A6ߩ$I V(~ȪSA 2=39-F,B6 `%A-cbaȂ0dcо-IPa^x^;^F6E6e} [Y#fE>YP# %Gh4]XE"YD9Xh~dA?1ho}]RDDRR{\ %ZHvBfq|փF3"# ޑ%zGHrɦ>Ft~ݥn%F|ҳ Ǝ,`Gїe0sd9DM,# ؑ%bG m7&,G6Fm{Ǚ,v,8H QJJaAHD!IE[a?m|ƞ96dcgG`ϯM' N#cdp.[7Y,o6ͮŘs9Y"~ݺT,HO6&=/S@DY"ŏ&K;( %P ص'y0l|WMJ.)NAY"E)ii~ ƹ(KDdA_c/.~\\|?x}S' kP4(KԠ>N,O?' %Ou0dyDib˳K35 Oȓȓ'q “.fmTAw1i'M6&7 LP&CRVӮ.GZ7U>p'1dhDIux>K%% mei4 &e50M&sک“Ƅŧ8Zw{fg%}^l,Q6G%>GW_}hïZ6|[,SOew8q*zy* N@RY"Iս^6,Ss)eecH$eTbs6,xQ^^/Ƽ~`?٘Wߛ{WzXv ;YO^:֙,L3u)a_tL3YlftȒ%"K:H/{L<&Ir&iGWn%tS/31eDgKǴZi)$H^:f,0SL񩇤 m) %R aIʂ$ecT9PX(cVk81eD8Kw'k@l nT .Bc$e3!QAj/{Ifw4,S60U'E>mŚM5)y\wŞO=ne[_q÷O_70-eDZfb^Ȼ}fWX;wO_~wvX8 e3E3$XR6fI%NguM gK,Q"އfwm`)0TPhy?.?λTك"U#U*C/ju /몹lriya XX1*K Ue ccU`UE (OUOU}*څ5=x|w|[wW'|fi|o]?}a+2H~%7~帾2z{ [|צ٘NϪRM*jh|54\ kq} ).2eSj\J ,pYYEL"w9?َ~j].F|zUiGRPJ6(Mo?N|qɋ%lSPWYI ^qpk 0*M**bV5YYbԽVw!3r*yC "% Ēey0rc?5߫VAu}M [E7ױ͜[UU-cz["38p[As[p[۪Um{Fs$C! 5UǙ"g%_Fx!ԩnvw(pu9Q`PPZ?ؼ**bհ d0!fLW@WTJw9|9/P`Tض x3aJUJUSU8HUA*ـ*,<ք.ƹhTUUA$hp% ,@T@TA<IF)/Du 4Txp)Pw0TT@'w/)0TT!n5}oH{fwEl8`8DI=Mg*g*>SB76 0 ĉ)ӪNNv"D)ɰSSAt(A^T` BMj??~L'] "D!ґLLeT w 44DP+ L7@71 "DW`@@ Jr.fRfR3,TT $U `GG9JVSVSAr]UbN@=1s[٧ا"> Z S[AW`66QD-X*@*2RB >TAyh pu 7Á*!ٷN'=lJ`J1S*=?̷Gy0??Wc˪˪ ZV(hPܟBD"&Z d FUA4(l]=촓 Cش**qb"//Ta׿x/\*k1UU0<Ϯa8ݿvïU=]n`8UA,[m'JkkD\[i؜UO_>휔**cV(RG&Klp`pDPrEլԬfN(%ƫJ^XnU K"K[[Vw|%}ϫ%u%ƲJJ뒸hn+R+R,/uE2-1UUȮlhA@2rߐbhhA[V5~"Ӳ ]%]%Q"vfPɛ7|T7}OU8I>8W 8WIĹ8ȇQi.1UU.a9{X*A*=N)JʘTpnc\%`\e )i2hxcVE>W >WݢSbիի^wE!V!V ϭ+ D+2Q-N 1y ϣլ7WۺքKD_Z3b#7/r1UVUVƨܧ L"s=qhg%Q;c%i^?6 G=+5̺?ţccMp}.v`ʅu_u|gÅ:js#l`Dd#1,-KGT+T+ZsߏVVƴ;J=vټVWr/'zw{٭ HPbqqk CamYleg,7_+_+ZJ̰1mRpJh>~?vv۔jV#?lV#7_*ass$kXa$ln0VVQ4FXb+Ab+[FJ@c-,hWn>̖W;I'0jA3j%0j%Q#\O׸QiKw:`f5'ƙ4nw!kq*rk_ÍHZ Zspc$ hL@1l_CC/>/ʾ8YV,+YVYVͲkv++uUl83f~ p<(CVb$2dĭ w,nqs$cȩ{_ۼo֢z~~=}o 4VI!嫯NeT-+㭨9WO;}3pRG9ZJme?~SW/}wW WI**cDWѴB< KNnǍ?a WC2ݞ++3`B WӼ^Sڦn= Hxɒ*0;=Yf/|Su٧f0/z`%`%o x )F,1 V Va0~4y? ћbfÇ_v )Bٹ)S0&5&؜95!SvLGgvPrv~r/`Qo ͟^Aī>{"X]̻]9dp8]75qS{.w~l\_&{{1"5DjC2Vg|&1+*O35!Qie5Gq s2P e^ss=W-ħIL|J \ӄ5Qd[y2KԥJs&hI4 tIL7"HH0;bh|$1:w' PEU,l]Hb)drp!O؅ܢI-"_b$ _14h7Xt4N\~fl?)ciӄ8cKTIn4MehTw P &D~CzL 2ǚM Gvg#9\I&WQraӫ֛كjnuз}T&1jo6}ec8m5Vc:?>nsru5QW{٭7(jBqγ?К5!Z;X͚5!Y]Vܗ(krP(k @Y"EJ_- XXխۍ6W{5)ܪI̭r&SMb<xh;& RMA*y- Vƶ__`Yj$&K_ɰ5/j/|36{ۋ`4~Pekl} S85 "]jh &&@>MbW,&Dv50G5PB|& 8M-xr,6` 'H`&D)%|7N&X3Q`&D&J2nCFM(NfW5 jB(v14iol14iBdm")XuUeWQ|h&qM)'/^=>}%ciXӄ5ъd֜ߧOH~v` vEUߜ6*7̖S*|9x &D)! XvԎm'F$QMė2("z>I򭊨+_v$3DUH^'}#h,?DUze|XD ֭i1ag6hY}B,؊ MK AgeFà%zF|1 2-1A>* Ҳ28U%犗 /U,-|&VR0rWU$p8⯡MA>ut4IA>dҪdA>TA>+)YbU*t~6Zn-iWϫTe~­mկn?j%-}掹S<2e**E#9ZBfS3ѕ%a` JrKU -J2V\OCϋ%|O~DuvybU ّHOu+8ZPTxPNtbc#Q&w ;ӗWo}DzlXДOh1jZUiĥ|iUYVdKHd7[ ڧ-;nH$Lf j~ 22fk%ݼ }*>3I[rQHEHiZ~m IgqoTZO11+Y}Q{zvMi%,%Av>;S&ǯŝMA>sSmΏ kى# v ̭}&r[X1\q0bS1ܢ=܂*j1]+ yf0i  talu7оo@DA@AW67`&H'{ŚM[Fcm|ΎQY&NfSeDn|"'[l2AGǜ2A.GnL0@E}@! YhQJ~~f}Jom|!Y7} :v}L웸ߞ5vE]A..st0(7v@ؠ`}/!t 0f=|3m [މV{1\kXsl  TSjL"p75nOMo]]lo/oΚnk9hfv; Ztc.:Wo3s6ȭvCcwmOxD[1 OO=EvPlU YV-p-WzhMX >9*Z,ڽq鴪hYIv(Ѹ&hgީ\\*uS Scɪh(Q]fxzd&|E JPU >S*}6:/~uͫVgvzT_;`jvjqE b)º}m0Uɺ:\{Ϸ#&Ԍ5(HO1@+K9F >|ҳ_h >2HťOD5+sSt0{jA.} [Y6tMjxtJ ]s21+$'5D RuSuJ8?/NG͐7sawm?A.}&_Z|ul\\^r=jfeKǕ.ٺ:} PA&AjL+K0co?^ݜo$L|/ w qkl=ٚhrnMwrI'>oǐ< :TR&A/I8͵Nf jJ :HƉ}cTXT?񩞈~ѣ|ڮFgŒ[vH_K_(}u/;*D| | 2E{6!'fݍ/7)=  T(Lfd"ń֛Ɨ {4DKtzv` *}@l2vabs>7}yލDv ͆n& *bқDHPxvaa" :_ǂp{}ռ\f3$Xx E+}A~]C<A6H!]׵^t=m*[j%W.keD@v Ov "EHx%%zWh©`?6%<% rDvۡ+d;oq ե`'5D`p op5jZZ"{?nbTJf˖Uis]uٻ mX s /s ա{X{sWwJ%Af􂗈 ^i ѧ.Z◈_iqX̛W44DdF/{ E@f^\.?խOfD s> &Ȼ^DÝzxyU K{K/zj˿A _U(^D ~ /~ e&j}N/v5u|w>;yqoC Z {L=0ɓȳjl00A= Z]n59/qB&&bXJju۳YI&&bRXo{Ӓiڅz1k 2UZtAd"ݿR z)D,}u괪Wsu璘_8f;f옱|l~ߏFyӟ_OO>y5f£fiV[zj;? 3 3A4̸ݴ=6[ ̈́'4c>MP==-{ɕAǻh"+fMxfMĘ\@lCl"}2ׄ^cTֻH>Oi$քZe=EyrnY7OΎQ`1ƻ7՛L)2ڄktlssDύ$ʞ_MxMĔ!nn [9D yM@ tUNxUNPU倞Dzw|o}@^1.狋/n߅n : t#۷:]u>Arw\;on@ARH-(<-(bOjzp^$1~'S g^)1捺#[3C=[4DDC{{"n^⢑N*Et=(bhbS((k%JJD+!q@ ~[V(("ez>?jxf> E71 R ($\fPxfP*E rr$ A A3SWo쨂L@9m-'<'䟹J`?a?A:mE O/Ҹ@@A s1p&=HDˏp/FcVÆOxO''砋D "GiKD "ӗyDO` o 7||4h>i>At9`첮YVz |I:*t~Tw='7|.w݃2PWdoJ_v7A2} @@IDwe`Ioɘ+ROzROI==wed I/IG)|.Hžž<'aOzaO=EM'='^wH$wwD h;i;Iez|"8w;w2 PNzNL+aE$w v X;Y;c톸A\%ϻ1qf3_o6; yNƈ;L2$NzNu_NzN ;Y1̶u=풑Of'=f'c]ZX$yf`dII"Gxҳx2 Ъ9xxNIAzDg 0>1>DS'='s8=S''_2dL $Q?ީ S''c^LU՛!Gq} ~?_dhOzO>z)~a'='?6-07$裇nOzOA>A'='l!!1o_|<P:=J":} J"/x1xFP[ǀD"cc8;2&EǿAK}$ƞƞ{i]iѓѓDGøEnx҃xQFw@^1n4HDp5s:$sLAV@$qoyIIS`IoIFu{ 558}n̊d#[Mz[MmZ~44I:m#MzMA4B<&=&!ަLzLƜ! -zLHk<8c;c&&cBX=aҳaȆ}gˀD>kLzLQB$^= z9K}m֬=(2 'dJ B}AG%=G%cUΠ؃r"Øv`TIoTɘQwjz3**3jjQGdro {>!`TtS*T bLrM|ȉp洫9kP 0vï>NKyNK9-ּ;v.Ig/ඔTi\ʣ\ru/=;A\TRR1|+noOVWz3Wrzw I2I^r]*weiaw#0ߛV#]*t%ǀc '8}ǯ͗kW"_ݧ]ŽF;xDL1Ε55Ly5L0q,`ʣ`* x_ɗ1r\z `ʋ`(`nM}ءߛahBG-b}z81 11EtLQ֦,@xGL2) S1;l,H S}}?~6VWzffiT=WhnmFR\~}،.WnM [S77b=” #=mAW@R^RD[ٕ.Tiף[*n M򸖊ZUmfS"qI[*flք[fxZZk L,M,E4m{\JyJ+ZD} S|<|UP׆RR1kTVʃVZ.tkT_)_~V] kmW`h)oh)w@ޭ*pwݢG1ด . .uXK"\Ʃ[*๔碷K |/}/9䊭@ S^ S1-l_Mb+ǔTLq9@ƔGT K+>E@1A1hDLyDLj?[""-Wl򚘊ibC<9A.2`֖z}yY/,{?7FѠ搪{"d~/@6} TK/R@'S^'SD=-5 22ED><>Xҳv`)o56@%`1a1Vr3lJJ)eC\ #{pL1/nHiR$2EdV󚆄Ѓ92Ei0쐃&>1`ʔg)#Q򂙊 fy`g@7S^7SDݬjhLyL<rL@)OLTY{&ϔTL<ݻfўlhLyLnW zLd!~S29eE@,S^,S1~^GsrcU))"`V1[#bLyL3f!_. igz;S;S3v3/{h;Ov532h^U)L\A"bda96^-S-SDK@HdKd*& 4򬘊bf S S0Em=w,oןOF{^GvLyvL1u:.ى;`ĔgV uwh;F``*f%XwNxs=AlFa iC^@21Q7եK{KQIA{ui:tfz8buuwiwi"ʼnpGQ~hūӧlq9|y"xKm/΅wB|.HhJL{LSq㵝ҫj^їi+"t@iO57pXEOV=VX?Xd[d:f剁4L{L2ya}h:7 }@/^/Dl8/?F#gK՚* eڻe͛[D9T=TP?V"0ʴ74(n$L{Lǜ*^hN@:^:Dm x׳OޣL{L3NXSUO^Y&f9so٫'v| i/il&Ju{={˞=Ӟ=DlRv;D=Y\YXf[fhQq00DÌ].::D茾<044ҲP5Q5MDպ 44>,M{MiI=]b3gȜu)6׃d: qσ0SÆr00sCWU9}ġ3= =2&`݊u]ls@ ^ 1%lg"cڣc: 3GP1Q1CҊnhL{hLǠ@^DAݺdӇti0|u3twL{wL1vٵ*Ŵt YbC6:´t&* M8=ayL{yL1 Hd7dL{dLn ;}n1`,9>dڛdhBɭ@"^"1l2Ȍ1|,001lq擥2> 1hΙΙ&:g8(&g³knncQ@iOi"U&'~^Ky؛e:f&<8K9 3{Ll rW4UEcZiߋg*CY&͌LL72\f\frJ[lx3v/a P3Q3CFHdKM@O]6jr_>q3,Q6,f<,fXo/frAJ_sgd6H2D hzc|&"DfܣI&&oT6->%%Ct/HdZZ!jM=P^h$xQ&`gL}D(E(C2[#q\.gi+ QwXMb4Wt,Beb 0 iƶ#`xxϫ0X3X&`VV[ am_ɯ?e LzڻEvXb!O݊{ L(M(C4hO7FxFqf N '+ORRKw++#cf Z/Zhս|VƳV&Z xuF3v$, ))C߃])sAdH/ 4)5)Ӥ7ח.k,xk8+S&2jY2ޜ2DsJ12ޗ2D_K!,]=:Zą?eSjeje#̜KJxE+Fɳ$ce;W‷DJU:}Y ++㮘f޾+vT+I :2BԓU6FVQ jcK ujB5 [K`>%L pddc6@MCM65 pzazzzfP>]./ٯod=dcT^^^M֣M661 dl`d=dD%qll1if!HP^Dxr1 %%K(ϝ`*(Ό^G%Kf150xX-Y-Y2ĞuMx=%%KԖ"lo<,l d{3wq}6=w f;;q&4l 6{{ oi}OI%{Ʀt0UwBBHTBF\A>Wc~^?}q:qyFCg}>߶b{P~db.N?,N?O0c)CQnJ i^w蛛պ ]v}mij[U8z~U_U[۠ur]kSMe}C5szi_wH.?omGG'C'D~fq=xx?|@ЫI^gHOhu6ھ[lFS:r?03Ճh6շċs/yV1,䢊cnT7Ut8}篞VO|ʏ^*7ԏAvۓ©=_ۓw؆Cp5_GQ҄=hݳcp }yRUssUvYf/6,x=쟏GKœ++vƿ5('IJ9Kt=5Y4ûa}l0`+9?5}Q c[' omM{+mijT(ףbÅF6NsKࢾɸ<@ @2?PO4%@ YYY]ܯob^ջ*vb-dD|L}Qy(oSFh3u'{P'"Q@Sr / |+V ( |-}b0"E@{{e0/TPnQ- f>2wg!6O([]Oꉈݰ#j#~ypsP$q/fd)76Sqo#x>ܧT Myw|5[a>u~RONߥ7Ö~~z-{Uok{OW{Iԫ,H_O(>.lBw/PZhlB89wO^?qZfO݊nD(@H8Vy,:V8 bv@~O?<} JP1r$ћgŒ[j(}Y`h2bxn KR=}e~QU%͠t`SD'@nq(/`N C]_lb2@HH6XwrDpѪ^^y?4gGaND sbN>˛sc@"Z }ܗs *@$!N &@&OA80bbq\}?7UלΖF XwW(r~.Q"/D^yijӢoiZsfhK6^얋+(mn/i=!iDɻlwIK %B-4Q\9<o]y&4_lRl٦ؒ:-Jvr6aM$s=9ԳUX72Ö9~6A{Ҧ -}sbt0Ξ!MPF}B|5'tRvkb6A}f %s6AYzfIr ̓63Oz_Lm'jS%Il'y-JmnhTYp'm.Pgqcml'r]|?=E(32oAʼӳ K6WNL/ŦU߸߀&ƁbB'޲u;8]ϭ9׈77F{~5!bL91o4++ 1CoBC182qdL!p1G o֊^bV |z: our~z}|]\}7d씌*![7:B<Nj6%g{1@(c7E߲rg{rת`ke ʘF.!V,c0Y~ #v1 +c6 @zi(t^A\b`Xb ܫZa ;19LX{ɪ6iKJgbNX}кQe.yb#cpa\fUw_UD`y, xdut橳u5CI"@ҼcҲISj1K-K4b4Zich44xiu..ގfzv \ v@h"&훟V@{sXN]H5Y Kޮe׌6%ßyxBn?67p<5+y҂*o] V{!l+l6AtIʚ 2k' , V# 0&@Wk(v|+T j֪Grb?M&sz[}ݬa"M&Hwӭ X4A-1z$=1 /Ngܢ)\4E#bFlf~/k:_͖9{FbHu$֑8p`7?߸ Ft#IԍxA Յuۘ`H[$cn;HӁ# q 33E2& 8$.6$A1mPwSϛ5t]{߃5,|^[bFH#$cyK_)#@ IDoSe $>]J6mׯhˆu%f}$>2 "$vdlvrOftںl}7g.قG|~~r[.V=#\(p|j~ H5 J,Uqxͣta~=~o}wv@83#cO;=Itz(W5m7'GAj,^cs-=t ۃ0G`$ܑ1sg1s ĎD4i?/#<po՛Ofw^ fz$0=n<LI4ym:c|X HC^.N׫C|.cG#cdgGpw$ݡ\r7I$EsL*IPdLU))$SҮh;b$1$UDTCGN`'ɘ4y$AJ I@$B[}ζ|{gOl-w^I$ItVa6I$clm#ͳAWz|MҒiIƤ8~6IG pI< \OA\J :0d\J "(<6$L2f2߄cI$cHf]wmK0IpdaJ|FGg0'ݑ{?m.gR~ZO5h'nwzmwї`碹>ZEY (3z(ڹpBIЧQ+-,?IA'' O̳ۓw#֫VTiaI$;=3ަ雫mㆆ8pQ2E>GU-0) ԔQSݨSb޿ jp*Ӫ2+aetWog8MbJS%LCψdЪB6>+ ʕ$*WLJ1l%1*]Id&W(e+VF޺u^f˷ʩ0jR1jl3=y#l~|90YR1jOU+i, [AG[]YcH,5#f.fUETR\)"sňz>{.6Pj[x^&=TBL{ faT ]B{ϔ`?Q\ H"'wJQ+zIOU*Vq2߷߾z}0bR1*_#5?=̆ypKX7RReTCbr1,T"QY{xbTG_Ƈp]>mSaKťbWoפ~HwᰟU K. UaKѥjt)lt)0T]%B [ -E$hsڠ݈`K-nYSRqrGK<M]g\T꿞aK$ႦRRdⶨRfL`P82iYX cW +îȊ,N uwsK!,yl'mh@R1ъ]ƶrAգ:t),b)QĢ{9cg20T̼J.KX*fcaJk튺'MzWξ8`nңvQ;%cJQbFxO>[q:}}xִOjY*,Tm89}<{Sxq2J+ZϺwCǻ,LD%,"JX Qooxq)PRD.+!eKBQ-Z{0so//jYz_gqn8ۥblWݧ)|Vu@}чUu߻ޖ1))PTLxnp`0z,lq> `* .syF-(XhQxpf4K%SDVϞϾ>~O?<}SL/zYꊝC!SDVvc*1" bN}P77 c~ c 138wu~9r-UX2S )duoC㗏`; )`%½,#>QdO8ۂ% 'B'l?v0'SDN@LWrǔFa 0Edy֦b/L^=M*`#ImQ1RD{ *]*&vߠbaK–:|7JoᓉS{|Rn)*q8[C?x\q 8\<\iإ@R1 \ \.EtĘF/ٮ)#(nrv~{!oRD/8CoڱG~֮mzm)x0`y"v[NbEL"d*ŝ2":deO_>{6e*fe &5`LobY Nw5/7;[wWc^ɳg}PQ=33\NAMzl,ۺcDM#^-gdZS)"ƌZ&)iHzrz<My6CgKusS)u\q7nyIMvS'nN8–n{y0~S1-Wpۋ̴ꨏ)TLsf}Y~5 )lS1m_WpӐ-`6Nbl\г)T̛MjzN9FVߺZֿzep;Cz2I yS13Pu*?ڏ:RIu5֞cv44wHٌg WwIc!OB#н}D@$Bh0I!ili04#3Ӏz{siDӮ0GDcN\cr8uDcNy'l91dL}oƢQOE|eq@44H{,eDcOͧc4_hi4Yه1dP4:%;U@ *Τr  i'Ӏi"5k?;  ?MfDÏJj> h&}s'i`t}N;}5 OM4.O9K01vli43݇r7.'FG Li 4޼̪+ܽX iGJo7|o;o `li04nײf40wѣy޳ 5(u8_a^&UqD5fPcI5fP4QcK=TDNZ :MdPWux4Ѣ#;27˸ˮ3Ks3ؓi'G'`7Ncn\bvSA:ͮΟH@44r:B1\]^[zOg˷/o,}x)ph:MwɽinjND놴O?ߺՄ^lJnV_5Y1X3{m5Xz3 7efy31s p& X\( s~2eg3DC#o7sP`fbKڎ3KA7n^w2{ n`7<qefy31]5d\N4oA1g31"c΀gb\5o7ClgU(np7V?YgvQ.`̀fǺ$` iV`|{~z5QY 7܆k=6 phe  yx8of@|31-J#MmֵYcr097kB89g\8?1K(qjinQrD؅3™ 7@9 73àfb6[JhwE\=łN 񸿎 xhu+c4u`..Vv[h,4C2GuyfAT !BhoUJcg3C2GќYT3CT2`̀jfbY se2ChWy׷"}o"d fx,LL8A  23J`1̀fbbRc  g -ne6:p^/ļރyǏ'@ fNVYo13o1f3Tv50QqrP#80C"h Pa&FC}YeN4- 3D2rs^>]NppO|p ]c˽üK X`heՋѹo$ć1b&f{ ?>ځ#3D,K|f0Jf%31lr  ]rnh̽3,=*q-/!_0>d p wd ].2`t5Ƶ Z&k%L3La'ˀeN>.PNe}O\ F UTJ^V.>{M*7Etn$ؚ2`M5nN|F^~d8/Be =[GӖyOpnqq%& U&FT( e@21AjD5(iPDY7A'1e2Djr07q֩1֓!ZO^983dXn%vd@`21FK%Z\l%X}!Woߟ`tlRţw~}*խ~n9`@AK2D-`dR2 zϫˋ? #L GJg]_lYc&Pv 8Kdb6Rr Uu]uZzq & GDeo/1Qd(21(3A!!B//Α@* D{wE/UUn  A&$tl cNƙf%`!x0p7ݠnhq>Sxۋ7ŚMR5!rYwmĹf!Y*B4x} ٘'TpĐbƈŠPA`\ݏ|b6dA1mh_jk"[Hi9izE.p (8-+w/8"% bfdcފ]z]jߦ }ߦFn_aI$#v6߲v a{%lM,XNh91bNn0dlDPrn#& n=ddm1i_e'0r[1`N69ߙݣɴ%-F,M6w/f-Ɯ,`N9~7 I\@ qloa|dXbW6,K6f,e{4zh44#(J(.s2jdDDKBaYl3fg%-6,F6aV Of&>xX. ٘\|cنdA1)K9O~D@"Y"D"c'ɂdNRsz0d@1KF$#K4m4Xjd" \qEVkcI"Kԋ`uwќ03Y"fTKŌH2:t{" r%EO bWd+DKɹ Dl"8;dc SC! L@/-Z4x~KuO2HA1DŨ8/=٘{s" x%Et_!+`ȂVdZQs ֆ,hCڐڐmƴA ,A6tm>)y%" E" Y`,lp6! C%fVdA+D3 1PPA%p>DDE r+\(sKf=& "Q I*CCEP EtwFaHPPAts6im6 0A'1%@iPrGed"& _uSSBu¼M{ɶ]M]Z` ?{խ [e(P(PAD&Wf1%(ʌ0AEL2< 2PAEɫ\ 0A \ L@@V**bX*@*brP@@E$PU g˻4 $4#@A 7*0TT fe,  $!npF L@ _WC)կ/!T!T 80}=PPA41h| qL@D:_fv8 TT*TT U |K l` l`D+voY׼mwm_ښ3B0B2~7}.^o+6  Յ  D]U@Yvx7C={?m.g?Q]r_clj*BE q(,wW=Q)aQQBV6zuH<8كUTĬ_!tjQSo:0QTQTĈ,MQy=r:׳O:hgF죂hѢGn4_oP^CL @ TH(B]` *kaOס.FU7"QQhpE,af8F_+GGQ>tYH}ŔQQ^/T/T}!ʹNPNPAtH`@t@)2?N 1d_lIGL74 *bNOr鱄X 1((۫C /v po}]r<{wPPAr5alIU-݈ѷ5l&`&13221sSSArRCD[mͤ7[ϩRE o||_ӓ\!p ("FQ}E2_;;|`$UdⲸ8 BSuv{^ w*bV+VV0UUXAmUmUdn{kmu~ut?}uLVLVcx)**&Vql;~lb`b1]FN^ 'RPVokz5**bH׾Ɂ3 ss1kN ĥ kkd\Kٹ˜**Je**bT CX@XE b'9]ٕ۞a@@ZgLii1O+9Է`}nrڎ%ii "-Nj gd9%Y_Wsq ;k(0UUa-y5c[Ex@fD2IVK"d%̀**nwH1UU AWPɯFⳏS)\E:ܳS*P]E&[(*BӤpvz<ӫ'z?n8Uy.$WWAcC8i"XI껻Muc~p> r]XsݨK\%H\A%K\%H\%Q⪮(c;k;}2fmIJlo`oD{C!v}Kmm s87(m'8RROowOj'7qf{vjJ%[,i%:/~ J<ӖaPNe8$].%+OץVK+yz5.i\]/GVKBBuP3.Y]2L#kYq"%Sv< 꺰VJ,`V&D{&e]y&eZ]n]14uy]t@K\k03e^~]!{7<>f.kwu30A_]8 m.\] 3 ޽YyK}jdP<vua^jf?ޯPe_cھMkM~uI/X(dzu]w:7L qR.;Y۷jzW7dtu]LWT7yuWQx&u*W^lFa0.FajSVԭ.S*(cYluI2Fz lu]k ~spur.Yٽmur-YɃL\ %h낶&j\]Ҹ.v.1[]. ..[]/0u!]AJ` .9^] uy]al ^],+|#lh%=ҳ=T]r,*G KTA_(-$D,fC.h ?8G #ZtQ/^uEX0'xgyı`B%Bc&!uAHBa%ҋPdu 42A.W 4@-tLu]Vz|u qo:Mhq/7i.A]dqVs.C]0\~A۵*q/Eet z_!vQEꒊuH]O/?b$n(Nq=K]",ɷ'%O"%0(DuI꺄d{Ԟ=uiOܰ}#P|gQчeP ͽtT.ڬoi/.O]3S(.r]P&D;ufePnuM[阘Hb$4h+uVl%NO4K]b.vimL6u60uIa2x_\c"[법Laf"34x4XC .@ڬ†QNRG=z.h i1F=c$nHgi~߻F3a+CGQ%F=zLh2K#{]ozH41I#-<)_=+ޜ??6k=ďzQKRR(BZRqUyV!#(DŽxE*UڙoN]PRꑤcJJ/cEG"S%2m)w7yYN=4;{C¡NS%;䇂5k_M;;w|v[8%c PX#z$A\:nz.ɻhXCGS>mRvMLSE:hsM["#pѤ=ɺJޓCwGR.nR[ڬ-wעXcK@`lۗ'O K=җzL}w_hJZ\Ll,uGRz.i 7=ҕzL]wy4Mg{+) V`/.H=zLwqCuKꑗczI]!BGR!mc"$ި⍶!BG`Q;D٘N$Dz\њ W IH(H<1ţMJr?#E=z.#={V?7OE1Y$=vk?XC GRI![O{"E\F=zL(=|gs?z*c&˨粌p1EQcEWPġSڤ>B=bz;ez Xɐ sA>ڥ x~B+ҼP\2"x A=I $l}Wأ#?8eb=zD\tP-@=Rz.Ӝ#9ǔ8oFG6QemaN$vdmϯgG7OlG$U)苻,/1"nd:6z{G;yG=wPMJJ=4zd*QݻnWJ2"=$zD,Ē$a##SS(bZwM"#š%&Qas)L[Ή]إ&UsJduݷ|¼IR*mvt4R(RW]\@:I+s7 iȘ X1%1w#~#2Ҧymz+>0ʻ(-HZ깤O$zD*\ڥvJ(ϨN/,fNz.i %J=&Ļ"H.\%q٥z$0 LBa$~_wFQꑣc9JtFʎrNwD 0sAJk׾6X(퀠=#{粇e7ZS#OKG }c>yvzdX)n_##LөrGtGNυDfCGOxs?ɯ|pPy S,q yKB x^ ^Ƕ7鹘4|z3CH1v[vzD\N@u' "==Bzz.g =={zi0cs;B]+-)-Bxz.G*G$ղ5X\g~cZ=!l!b=W˿[?Ru-WKuB=K3lP:nDp3XmBQ(,إf(n҈#mF1Ӻ1,F:uL6e6 ۭ z&KrBo(hA<&nT#:1a:?(Oߡ(v#:1 HN;ek|#:䚭uT#:qFA5rsӛiUh0U05nuPܧTjnYOYlUh2U@MЮnd4]g@8Mc*n0]0cFB7rsS HwïO׹ٴHFut5[h$t#:Йut#:qFA7rsAaaL\&`uЍܧBf*čYlU,sU!ndhl1^,K~K7Y\'2NBKQ݊ܯ7R`\@&Gv[ȅq ]&6ڊF bfAč׉ͥ5mt#F>|\mL#:idBSgB&r`99pxKo$5S'5߲i6S6V[LFN3uNs.;h/99ͥ^ï/a젹hdESgE梑L\I#%uvs.C֒:I#%uZc/!DȇI&LCȄI wjFZKYv՝H-Q5iVzI#%uFs(;h)-3(ioV"i¤NLޤ"mĴΉ.d -EHpi#~=: NDHfi̘(IMCHfi\2IMW'"md΂.dMC#u hi\hɮ:i#u^s%bvs1J"m䲴eL]e:1eF4 K^_*tSg ":uc""<ϯͯrТЍשsYM^p ,2~;u"d#WdWdd"8yo$N,$y6(/y#Ov`H: - ۭ3,^Q^3F l[*JH:]Z5RaNp^#\W|%ژ5ZNkLy^#<4W#h:OW篝B,zunrY,Dzury-ȔDzuzc-Q[[ͧwo NJs`{ud.۟5bNLeZyQ.%}:5SŨQ.,P GFՎr92Ąj1VcS7kԄG2Lo fT (60Ź fT(7lhQkiS(hԨQ.fg!ҨZQ.>j4&hMLP mFڌbj3NL6jmFVQ mFڌ ̈́\? X;Ŵg҃tPհgTm(=Y1o7w:EKՠgTM(&=Y8&jzF햞Q zF Dx]5jkFf6yKՠfTM(&5rРfTM(&5M64UK3)ʹ&4UK3jҌjH3fT i&hϨQ򦥮CÞQ=L<~QFŶgU3!D#b3- E_kzF햞Q zF Du֌ޚY mtҌŖfZmҌŖf$}ӭA>4Us3ʹ>42`ͨr3ͨQ)zE fTpkf6>J(e#qƌb3AQEØQ1fż}m7 ZFմb2"ni[ZF5hU2*-QhX2dTpKf6G6: EFՊb*2Ɂix4TU+2lVLgP܈Yy/glcUȊɇ_-Ѭh\yrMhtU Y0ƪXaFWtu>ʋ$*||Q+.lx X*",^Dh-+jd!*}_<)"Y 0^afE2)ȯyt>DgYtQ<h4)E^>?>y\ Z@+lcD0TPhJG~49U_N/OW+[Wj/`|T ¼絛L?lS\߅|P03k KYѯЍA}VR8,2NNo4ML0eavQ/_;~}|zh5^>r fËǿ|[hܯǓ`<7_ɾ00H`,"s±= X_5[~8C#E zUc}ˏD4@D׈bV\ߏ.57_>ۿ K 9+oŹ磋ho!gi7(ZgԬ`Aу>;?n.#?,~nG~_ߗBD,(%.9dB2\bgɅב"(_eų(dұ׻L?U߾]ԡ:!墵roW?o#{+/v//R\Wk~Vc# 1^.ؚ֫] u"|C/CZ',,&,e׌00zVԌ/3d9OQlybL>d.5bdD+ dzr*X'~x*eE9@'0[*25b0|ȕą0ϋX0˜>&C#債!=R8^hj*`{7EQ<:GILtٵ~\¹lˆ QO\]Ԇ0FNK [6Rg FۤU;ը eEo=݂lEHc~c`WoF*0]m%-3$7BbA>#2y_z{2~b,%1r\XR!al0U]QM{0Tc aKֹVJQYT_p=|$3Ҕr؆b.]lڏd+&W'R1\غ7o5'Ѻ茑248>@]iBAt1&8&)s9#ݦAi6۲y iB42E?;mzf.,hS[f0KKI3z< h䟹ON.ȝvƤ8e4 W$@cg:| 6Zzqm`Jeݸ*۸ekӟݜ}̾k^!_=7;i_!œ2†FKa S|a ykLmM'ǧE~5.oĨ5ָ I5v(m@%TL4 f T rT\1F7r\[PPK.v'H!Ǖڄ.s]^t2x&{ȑc*rRl3#4G7#@G S|aGP:I9n5٣NG6K,Jbp3Z0t.no&'Z?/w)GfH|bﶱvo0w$1;uěK$}S;R\+˗Uwb\ISvO{k`zy.9o]vLB#Y#aSk}wjz5EƜ݌Ehd{ۼc_+ϋ:DoC{g@F"dqH!s>~ $X%?5TTqfPHO=q2}*P|8$iⰍݶ^ '!rIrA yXҢ}Ȳf.`[X/8$gpkUCa&}\n8L5Nmא)^jK5җY5`ZVrJ傧a.,؏e͋;+#<l6J5>ílƂ /;GY^DxSDkY>^ Hy$j.jI3pJ_*G۠|{%/ \_F1_~W֒!o`ɌayT*7o\۷OicqdmR.;2t߽<9=~wr$oljyR.Np'ԝx_ @'ErOA)b4aQEɣFc6aRʥImc&$J1(fr՝Ѣ_{`shA) W:0Fźi;gD@J ([iG^? q<ɉRLkJiЋk9*t q*E:rT[П1 rUֻ}UxBJl?H6TRy\ HR,.*x+ʓ"IZhv^x~qt_QNe9oooGWD|K߼=퟾p,oePwR;)lk|cQvi(wWuiP<,ve "-ȖRL\#l)EƔr!Sa"7Ø̉RLJDͫ ٩R!zQ^~W50x0``ك^pv R.*prY,!H,Y)f`|9k|$rI[>d(Rdp),bYrJ#`"Kxufϴ` a0Gߐ400b) v)H Sl&L1aea%?CL\lPg׉&;zX/Eb_{KǪ}F<(P)a0v8R3YZ{oG%{aA/s1EИrIcXg~gۏ8E[.SLLaocb'L0l)2c\$.S$)]~H'ra5c%L1-^,F{L>pAT>{5 o0CtMZ@LK0`Eۍǃ24Ș7Pv $IbbbѾ_1=%\Xm7a0Dxڎq)1 lfGQSă)8>7 {7].傻$`Y>0V}Ot.ENrA]̑rD Rdq)&e9C@Է[omPF'6UZ\pi32"Kl4[n1.2Mr^Ջ"(q)bDB]"e?2=/ON],].T4s ʿ­BKΥ< F8I.'y&DzK#iW8S>T~kRd]+rl?ʟWWk^m>bN5xRLj~K?̖^qSg\˙*$c?2Zɥ|X"-K1,⛃P,Rvws`4;ɾR.j;͗-r R\*ޠI,E&rXab R$i)&H.Ԍ"K1= *. iRjDTiQi2&Jd*4K=qG_xpdKhd4TReaL#& LLjVmdQ[p E6m6-^US[.IL]e(q&=L#;.v*p⒧XJy^&~J3)\m[z/5)⮠FJR]Jq գB1*[.Mj|W^2݈G lp&J3*FeN=Wi4UkW뗯[^FJ|]UN9z<dif@`P=I.jݳ* y[59{˓PQsQn ,4AwaVk4Y)fKׇJ]|n6fF➴{bJCI]\Λٞ6ahAit jM_OȢɇ.j]<4QPIAqx׋"u\9M1rXH04Oe?DɤdLseiҽñ[]߷\ʴBI¤ &JS*K%RP?0]6g =҄iz$ޖ2ݐC#r 9L$3#Mf*F1MnoOؖoHi46ⅱ\ٯ;\&IK*j@RH)襁mEI L)LkuF=Hzbnǡx+c5C!$A] Q#ҤiFCD&H"ŒJfC͟6h4Bi )z`Ie MNf:AuVE6 Q 4YAiQ2ts4@J/en4\wkyn`4AʼnLRa07+A.[Ȼw:S39+-׽i[:g`&H3"Y>e̴Q&nR4S5Sj%x0yQQtWlZSo&^IDLI eGIM҆ #MґfJGⷛ;&HrdH4AI %IzdQDmXT~W\G˰_"T.R)`;$4LK0im 1i&)-pר2iRKe fMº.x0)n{=zZo`oFI]gvfЁ@i(3DEiҢ4WbMqHGi.QYe|M)҄Ai1g_Iˇ'"$;3ۉk?J;/H9i4rR &DH1i4bR㈛]mFOey̏`<33iҙ4SgRj_@*Y>&I8" 'PiB"(?iK~j=GIfmU _|QkҤ5i+{]hyVH oLW՞n"ao["4d( si&ba%7Iܤm.pBI]fo(i PڲPD(in i#D%q%*Fy)&y)vKa+)#(֛'Ɏʧ`_e|4/S12I11I1I(&$&L1lL#J),flH=3F)&)L]ҍCNhh3d3.)Xa4cbb E͍cc]S9)-u毃|:׫/ؾ\?(.$.ŭKq =(3$3ŭLn؅5oo]~f-Lo6ˇڰ5~׽[3rט1F)&)fL&8cCsg^SLS$-StaB')fM`]88.ɯ mle;x!s&J41bN1aN sY|1ISs[c caLTe)4_)F)&)fjMӚ3MJX=1RN1QNr%#3'EFSoPeIe*zY 3#-\RLm(.GɷhhD'^ yǨ4Ť4L)&# cTbRb KJTLJTH QP8 d?OG,h&Y6,z/sbb&َEW\¡b&ʼnnsD~Ł̧2%=n!TʸP }ՠz}*C>qTA;H Fb(ic"!ʸD-<)*Bh , _ )CaR/ޭm8.Fѥ(v( d{2L/V߼zE_?IZކT (CVqYQޡr<]g=$6 2S)d4nr}yr[ a)CR0sg RQ(㢢2@&ƛWj4n˻nE⢌M6mN;/?qb&0*I뗣W6_ 2LXJFZ%äOДaBS(gG ?0(T.=j!B0 )NC4 RfACʐ!eI6_h"ߠ$eH2LIjo8&eN%)!I3 )C DH5f4_,4LGŜgMY|ijC]ʐ.eT(CjaQ9녝>-]gӛd(GKj@ʐ e\&LumˮrVrƠD}9GȔa"SE)M#HIN))!Jʸ(-6(?K~fzQ&O*BRKb&ՙtr.}~/l~2D?&;|xPibAd&%>|'l/AÔHԓqQO%A(C @I 'd8puٯ I'CqN[e2LUI/X[׺jգ K1?d '|3ףW/_ɩ&aJ%ɸl-$L0ôNm9;qp3K!0e'Nֻ-D !O:rujGd\ӺY͇UuĔKēqO*`Rb&0M'NE-ȵ m&C6qL[X2Lcs1dJ2L+'K:!ɸ<-h\D@|#CqGs;=;B!!6vPR2$)TI$gU2*JQ%CqJW9O.5#PS4B i+(-▌[B-4G’q KۜHG4d\hts JD%zaS@#dN2.8IXªY~y1\-Ž'7.B ]j1Dɐd2*uhA)ɐdR'lrPđ H#)WB3mO2'&$ 2!q%CqJH(.L qIZ(L*m~(?Z5H=Y̭&XH6oEK32gY;w8z>g|rD?Q72nn|lyo`~&vȸ!޾7 Z{[!`qq_0yMd\6Ѻ80܌sLnaE 1#Qd(2LHdČ$$LGO Ө^.+}ESM//(II\Ϻ5p0 DɋIР0#JJJ\tкْ0s0-Qݚ2l_w+Y(J\DgS3Y>X1A(!(qAEU-XSpMsRj$6Dۅ 1.m3J3Jv%%%\ψ̘hI'J:ej̧d%;o&J&J&JDn=tjWI%J%J\.ѺhvPY̡͡imRo utqb }yLQBLQdBɢȢddQdQBdQ$8RL)J)Jv%%%LUEwy6|bQBbQ8OMF0O9J9JNOO0J0JQ'$%%.h Kb+${6"heâ"9GN(!N(qqBk/@+S.D S&ći@" E E S.Rmzx*` +*H )H SAD $(!%$!%. ɳ&yx#ߤ0#x7~~I IHZr2Ӻ a1K \ bS*U~#RBRHY S,IKOzWYQE2~J J TRo{n/?RBRⲗ<] B)!)aMM55%5%LIZA)!)q1N^%7IvJNBa>%)qyNk]|WLSBLSbڿ(3%$3%Lu-J`J`J\҇=<1mc#Y)qJ|FRBFR2d/)n"H H E{! zG yG ;ڤUKEK'%'%L?+q1ё̤=.Ĺ;}8RB8Rđ8OWa$%$%L')߅0$A!_0$F~G GK>B{ĥ(Z|F)(I̕0*79<<~\o)k`%)aH~%vG<lui?j*҉dJX$SRcj'tJtJXSN:=~wr,؅ +*qYQ/y] ¤&~ӏ TB Tdm:4&yvĢ¢&ʼn~ZgG#*!#*aQXiv%utC1."򔸐-4UIsJ\Ӻsz8E)%)eO\xSRR OmJ mJ]hSRR$.ls&=÷h0@C.^)N)NvZ77/utԥ; KH+SR@bP],T ś}f]Cי>ZhSRRĉ~Z:6E)%)eROhwkUa5 smTJT"bI@T c85wVRtRrRUӪJļKtTʤ$q6Z_^ta&I*eJRҰn7aբxR&%%2P{]RR" RJJJ]gBRR7~@*%*Pj"+=mPr ǤLSp1ޔқ388.IzJ(O|X#STRRR ťĥ).qa\NQRJIRJ]++r]V[RRj\G铏OJOTa"'eR=Z ,j윔=,F]Vڌb>wwL-j`:$'ݩruE,8#<Z^ftRrR *st*PJ*PUZ JJJC@Bi0 2n^~hdLGtU{-b<)a<)٬q/.kE~|lƁȝKcʡaCl'%l'ua;7.XgmІ-y4QwP q0~gҺKЗ }]!R_PUS,TJ f4S# !֥u=%}-r݌պ'nU⽊kA٥CK)pځj4R]rJIC֥CKɺ,!Yo^ x6 t`fa]F- tHa06S:t~JvnaGhJ0`#O0`"LL>MACTIExs\I+Y %K:Ld&(m%OMv6la&%m6l)lAؤCI'l"l!٤ӆlRԼ5{g3Ddݒ%o-[!Ӣ[V?H,T,X!ӢX<Yw`1%-M5j!դSդITK5ʷVΊ:#Nt=ߖ\Qb|t\g/RQM:DtIdPi&m &@z2&bL:.ģszU.' L{DtT:0Vu @a$NqHS٬5i"j"D:t\{=c! Z2#bF:.fdA9q#b7P!J}͡:#G:.d f2"G:Lr$_rsDx"I>ʿkH #M(tH"$k!C:.;d)!@tȝyv@wCHl9i8!qGI$S"Y@CHIDžlLFH MlY4K#r@C4I'M"-2bI:XF:čt\܈5X\Sht(06"$BH:.tH-0q(nP'Nq$[h,0/q"%{P0Sa2%H=UƼHnI|p6><\㟾?l6O=̞Gg6'觋a+aI),>?MwT21FΡ|YEOϏN,*WkڐO쳭([)=&?ճγ'( OqfʿY?pۉ-E7~":WYVͶϾϳ/Eu\,_=?GYecrzѻ:?O φ?DP'DREDmXSVq`ӛyOQ>Z5OKݲu^!m6? }vj7׳(=y ]5Ѹ? ;zaO]c?V0+tu>ʣGEPo7gitAt4GO]5 d1@_yPaEӋ(-e/DŗC '#U2йT:[0R͆y]?nl^wYIa^͞kw1fa&(Y;LD=m}6.B[=rtmdvm[ܞ%ӿLF"_Obp.woMMsyZnMڬګ)(?KKه6r_/.,dhDeh+gs[v=n|wzN@C0g8v׿x|68?ikgnMMoD]i3О"TԽumžZ?>υPhDB.~pCiwks b}`ڣq'dnܬU+/g鍶2w2 mknQf,->$<ݠ2~hoy]Z?V?yTi\t]R#J[D;DՁ>RU2.a Ե4\WoG7RR^ …_2wnH iv:^X"Hyӵ7%bGǿ|r ӻ#[@ih v`2^/(etp(meri96~2}nV@XQ_u.ZGo_}$$2"!ʋ~nD<_*20'료t&ͤ̽q ѶS׮ R'eumeG*L3RiOkKj)7@{'L[J[K];K+=mjfh:_y:~l:&SC0Sx<{봖Uq*mBeAݬU[\*#[n֢\[aC;Sic*s_*/:LChq^!B;؊Zi[kW뺁Wz0ߜΣjvqTa߱۫_(|q,mu]7pB>ve"_ʵ_KΧyv/Q[!~Xvǘzler_߾{s*}2p tMn {1K6ɺȶBB}zp,um|] Ka`[*FXN-J;ki_k[etT تkDU!ܪK;u]u?,梣:``p.m΍o1G$;4zCҗLu*";Fo_F?4 g>ƙw;e?eEᄒntvSf*Lޘ\&| Ӣ jc?u njr8yӛ:M>|:ɯN0%y]%LCުm;M雿{^gMQT7Iji4viWJZFBNmIG Y>Ӆ<ޭx7z l>ͧpU1{ lU9_0I-p{ [./߶1QG;х$ۭlwKƺ5U}/{Ur:y2*oF.fKkd^{aէ|Ero9fq2#]ut!TRcPxP8$R㰍hk5' "t6 8Vqu!qu!CuW}rl_jjaoHO5䲞M5Z|js̪@ T JZ(̃52EWG`2gվGk?1bbQ)*exwXsyavgݹV6y]̬5VҪB]"?(x߽<ѫ/G~4]{X,gk+%-frWۚBHvxXZ!$Kj*HOq-}Rs)?ALX؎ݐ.kra%mxGC۽)_s4)Wv"xR0E~Y#@(QT,HzC#-{& *&( ]Y᱓AbzK7![-T.fAEԠbZ^iYyE!"HTLFpo+P)UkLLb~pǎ*~mYČIb| [=syuhP^QoԂ^q$| m>E8 }ΧS}r|;O])۬ZJh)BS%;S*{=4xj3fvF 9k1qS.m=EbzKkqRV?7\zֽBO§ 5 aCxo{|)}p)RؿQSD)' {`"O1A>^᥽KzʅĢSL3Ow6ix- B,O\fE96d-/`L) Y"ԝ"N-T_̛D)aǫ eaI ?; {6_uSL=Uw꺼S>" OuunȿKP-# 1ШiRK hnFO˧],_|;=Mf{ >Mv | =MfzE#]Ɩg>'Txv:Ϛg-q$)FR$̷eXΧI./x,bp671/5~p?:]w~.|S|߰kAv~[|8\F&FEM ֽ@Oۧ]n_;ۖb:?/D֞&kO`!K{=2a{F}i' LqBoO]ޞwB\V9Y"^a5:t:rZV/_k(rRVLiRRW1׃/׋K;GwM?:gQ#{LN9k>_aQ0uFNr]]܏n&$F ir˱S!. ,\.xC\&.N A2o" \Pn9K5o7BgjJpib&>M ALPN$i~h4kidFMæ/i~A[M]ZO(h`4ͅDsaHi"д@gEHBf eo7;4 P{#4b`00k(_~*FJL%[Ĥ0Mvn9K# .&l ""t lXhi2q;g *eC\X!|ir4rT-M֖vY[aP[²u^֤[/E+F5 Z-0k"~+ )9Zhs|wHhi"N -&BK3 -LL,MJf*YѵZik*I,UB;(rˡ ~Wy1cH] Unz9_{^DXiai%{_#p .sKVjzD&"K{!o&KW#@$si3 ƾ`0Q# .,| M(iK Za0S+L .+l+/?}i%6:-淗l9q^|c4&=Hv!LK v ߅uuLk2>8Z}/> cTbRb-8$!i {'cpުTH($. ;{#]#!!ihI.qpzxf1*`1)`N1++nWg.*`aTXLTX̤x`~~HXLXD60'M{ q,vcgy(omcbb1R`1Q`kWLW첼4Abk6;K6"\xgxWLW4d 9抙6,ji݌ƭٛlisB*߲A]M E\eyIz,oqB+&+fj^` 0Y3.Y ӆxRWLW̥b,l "_1!_ OaiW(؄L=صl >o&u"b&Żc1]1]jVLV$x÷@KctbrbY٥7}|x>V>VL>V*F+&+vXrb_ns҆UTإb}#cdbbb%-:%I]/d|tMo wVLVv F?+&?+vY]W 4.Fu+&u+v[Zٜ_^|?z'mQI]WY)(Eymv3{֨F${5@/,&/,vyaѰа؅m&a?8& uaKڏAN,&N,vqb۶bbV|:(00!-<\s ]LzኈXLXBĂK|>euP޾;oQq|xFYLFY2B,~&Lń,,||Elq_!s.f17 g6{Ƈ雌em/ D."m{γ7>"y>ʽo /RYZ˷Nl`kb6Ɠ ^ m o#ŷ~bطž]q1q1ˏV>4b2b9*s1)s1Sp@E͵?B*.&*.vQqcfnBns1,47bb:)D%me 8VXZ"p1!p1HiNW!'QEk-&k-fYk͜Thd,mgjqp-cN>ϓNZ}H]N<+ ;bbb~|=bb'oՋՋ]^ g3.Hoc_!ϸ < <zf3D&ut78^ \g3Ln2҈5ob!T3(KIH!BֽfjAkg\]AΐXg\bݖ^O4 qi1[ tFw]^=/2iAOΐ'g/Lp!808}Jl勡/ϏN/GQa:*q'8C *pws_"L "NZ(L%ۧ+ :o7re2hL MZ(̞@珐!͸ 7IҾʚޜ_ϲѝ]V &lMmH [_P?=O*[z0Ahfgyg׫?/?q O3l?CyxM D+LŇׯeyqtz$jMû)ڝ&, B&JLd1dCuëN?:>ڈ)5ӪVYgcv%w05A 8DV;g Af\D[Ï"8"~'6Cpam|]/|]#) nƅær~fv H0K00g\~\z5a(G$ 3.L.h$61`,kh0U/gX\F0pMgȍ3.7.F[͊;#rwAgX\5R]%Fgى[im?GEzo{8n7eWČSsvs/q3jP\Qy X_\ɐi3Ĵ$0T mƥyE"bDfAnBa%͸f=EwfwoZuFyh7ä8Oy8eX]9qrУ¤N0apI&7\Y8w9ͧEd48rm^pq9(9ov3춮9mSGn Vjա[U6 ׏蝩1F͐f\ۺ*cܸf Ao iF͐f\WO3Bl 6Â?͘IO3.=mӼycX/ʗ?GBg75ϊ5\.'bp&@5Cq9j2TEfv޾dM1!f\Zc\N7ijƥ2c?{߼?/!rՌU[ xl2`i}5Cqja;ԌKS+4fL3.3ă>!͸|uOq›wlvE|$NU3&ލpJ;.ME(0hyX3d&nwZѤ?嫛fh3.-Ƞv7/v~3.-hm03.$.Hs:q ĩzL!&b⤅”K&qp60D(yro_muૉjB!gȐ3.C3ljM/ޏ.fQ ō/*s9R Y]`-.bu:up!oc:uN +ŤLhqq[hy1fқR}-lK֛Zoba)Qq3"Lhbe{wCf#22! 7u߸.߲ ɒ 8B^ 98|4(ȓ3<9i0g\>tݠgH3.NOiA5Cajo5f4L L 2C`aezw"7آ]lR53jh⿌ I@S-Ca[+t 9[ly_ŪCtDXaaU/&#˂2"eH2.E̫l﨩^Ռ,!ʰXu O>yj^|Wv d_ /;]m\WlTBlTbŞY=I+BC8xMJJ\Ȕ⛻Q1Mn'oNO@L5#xť7XzCgHW ąU)<|:RTjyFXa&+,x Vq4LY],p 8Y:>z< G S S Qᩄ੄ ONp]歹vOIJJ\QX \P]Pı±%,RVBRV⒲ڿY%Y%.͊Q*-E@%R/gv'ؒPZ}2P2՝zU =;=g߾"U !U FgMPJHJ\;oE%.,I tĥ;q;kёv 4΁i8RL QL bڬ8p^s&~s>]YM>//~TWfa %)qQOhw~P P jA)!)O:-go7\ bҽ|KW~>z.Vp"vhJhJ\F?$5ZI YIJp}޳yQ܄ab$")aIfA)!)qH26z"}PJPJ\RG G 8]+.uz-ǁITFQxaWL{%.h 3D >/OEXbX/_~ PbO+Bz"\QB\Q.lk,oD(!(aGalWa.|ۗF̣i\' QP".Gɖ0!\W(3H G_YX"%%.h 7"F7hyx$oQ~M._ÙRBR/ I I L1WDKJ\^_#ߎA]Fe8(AM j>{˷^c&*qP'@y(-c$`u(V'v (=Q'v IGH>%D>%ȧ|,8(l&-rNJ J]8GC(A$A. JPd !E)%)u1O2 GҢXk.SJ.Srٟrݘ/h6d6.)xβ{{.iAJ5`p_{S)]m,i}q!KfPY|Mq {ŋҥͶ(@ghddd.#k.U}4d{IQJIJ]w-Ws"+u]톾!vHJYWowŽ>a koU qD.RN3*b))b)K4zuGCXǤo-zDYJDY( 䋥._l+kSDRBR*&y<^9ؙL'E&,%&,u1ak*gW$-LJJ]wBElJ J]h\p.vrfU= nnLvV!%HJ]j &.BRrڢlp#HE*%*uUJ//46bR  (+xTj"EIgJ J](8&-@hwBb'E)%)uyO^l>ͧq)O)O)|>j[B)%)uqOW (D$D.!JEVTȊ NvړSJSR$Oߧ=jM)iMKkcRJR겔xk/~Q)>)Sꃴw.G\Ƈ{e?y'o 0 qh1Ai &HZ(U.h;J@)I@KGl<֯A@(%(eq@6E4(%4(uA;dE˓?z-I=E(%(uD .RҝC)C)CR39︙ٝl4 af$E(e*BAb.RR&Y7i`!yo|8)HWAH(%H(eBBE+ DRBRFHA >)>KY[UlVMڇ̓)@(%(e*@&n 0TJQJIJ)@>M2΋]2)p9[t~Rr~R#.#UxE7vC ,'xNJNʲuZiIIQ7ۅA8)8~銐ԅlUx}t"o0 ȽI7Ba.#& ۄZ4h:,mͧAa4LCy4OСZBBy7wD7 ]!âki!R,XJ =~m@ߣCG{ 谸m&4B;:,Bɑ"9:Hzt~^),EjGǥvtk q2VM}KE{ƪ)iSGYt(.:$}tv*}tPqIiqYvl4PKVG갿^=:$zt\Gk$4xt %_VH FxuP ٩A1CbH%lkIQ  "ix..hǑ!5RCTW9hq!""".cո)7cR%"H#=t,f4$BI:LD'x-$t /-t\|Ldt\ɺk|`/%NN:.D\jѓ'@艴PC3<f~O8#e!ʤLdL̗ť=L*ljGĤCIn1&c".}s?t>q(@P6lF_Z.LC$t\Iѷ_p{Ra?*J]/O?$L:Ld3P#h!ФM=HU.OqWՓ'z8vG~a=q]_^|?z'2R'm^ H LT pB|]1]q+[Q0Ye~qF^CJū]想pt*3Ke謜~?·W9,>1ŧRNQM:E>M?7|0i~,٠nY^UO쳭(Gh#&?ճgO/GQ>,Mp_q~5g8Oi?~g,+f[gٗ:.#vpt؟^pYOW7zWV<HjP=QH`O, z䊘2zͼ^'(W级z^˧sٞ,U7Oo=jQ¿\Ϣ|Z ŰZh\T=0͡7P|tyG{珊ʡOݜΧx(j"ʆ@Q¼-ȢE_ѲFKLq5D?|d1@@>1dRđ_ TeEP L,z xUoDudwtP ]8M7E'{b1~}\.0ήFak Հ:Q{0(~m߿/ߜO>4Z-_}^\c\KKKi}Y$,0ZHh{!UzY z(?]Y xK\J [[WH,njB)lԳȐhkAҡֽؒCeΪ+<ϗzWZ:PE.*b5z1-ߖ!ix4V)g3|kK]ik+#.9.`嫜과}"z|=>ZZ8{!-*%ZhmDhUkEŻήYg~twӋkvR\JkZ?p궎چ<R-jei=Ο◽ {磢/=~u|aq/vkyz]>o|=&e ֨n&VhG&{ϋxݳHNׯ@WYUZ/G|IZ-j2e UR5vE2:cJiUis|FV*B'1\zPrPupF4\I+<'mV]򫾸Ai-(s)(uP\JAA%V\_Z֊z`{ Yqi-.s)./q^_gUx3 C kNY}{=; _W(q--u]'7)V4|] ( zn{"?6lDņn垏ySz2*Yd1z|)bG˅JC/2±sn7u3C ?:o2.ۍ]F6zޑBotf`tǛ?j#<3p藡Y뗁7a3WPIy04̅$Tk9I.;"75:c(-o'=u6?tMZ>)Ls ,' gN\e)U.?L泥q.fUlfWcbKA^U)wqeN3Z?^r:Sұqe0|5Y!; ~ 2Q `sb=4:=|<ˊX[`DSϋ@rSv'p{nҪDmӯUL?J!n=DI,+pC h \=}] <tQ~ Edoc%1 IH:?ҲZ Rvߋ\i"x%@:rf\(GQI(rE༜j?>rŃIKD2|L񘒐ʗU\|^u>{j Oؤ6kJ{h<\rykaӁo75;o$O:u~g(}Ytԧ0~P:uggl Q≟t'ONP-̡x(: ?柧Ũ>Mγ@k'-eV<e Գ¼!s+"J 63+ DP7W@ J^0aC ^"pٮW@T<5V{&(<2Uёyd|74TGqKUR})N'l4R?~zWx S`֮~n`'IhV5mӓ`US* 蹭Z5hڶrV2o6&ڳ ZI iƬiVoTe@U[%uSQwgwgU[X0,36Z)0j˥`}t[{*Vb\,qZ bA?4۳DVmd1JZ(-<9VB ,Elbˋaͻz(cM>r_s}RnRk)}-۷` %K.:`z}S!ХHR.;&٫n}!I+].rd?kZ\0.ҸonLP4HU|L&r\Um n n1["Kuæ^Kh!`ȃ)  l"7L1O]:ȍfs6sk ݬznoQ7Ȓ)r[^||^Tf3SoC{gY6_nD[G}8nAtri47r؍ *M14Fmv HFSLx`8|Nlsڱn/Wˀ"AM1 5^˾Oc[c1­ 4()VGv^纱6M6E(bltrJn(7<@XwshLL0{\"HuSL֍J6v+ _hү^ړ/'Pnxn/Ar n㥐BgS)(<;D)YtソBהK]1INSL:kOBM*f3ffT3dӯ1 2EnbeyIWAqulJbRf۰s;<96,0̚.R:D)Z*YIr5˅#$"KqQ *t%.EbZ\j $vfO<{;].Ի$a-Ԡ&&|O2MZD3L̑}) >s񩥘 R.٫.Ejb]X9Rt)_PRDy)Ŭꯏt¯hoyO1J8 GM_?OGw4wC?owyuEec٠h9j>϶|o5<Ͼ<Wφ٧yb OqʿžiȊɇ_.uѠ},qʲox}})bQOWm.+Uޕչ.o~z^=P=QH`?h ,bJ&US ?\LoER-U[ܰWE3l>i1~F{ECHzAh#ʶѸ?I4IO S&$*])P8z>}.hQQ;TIQET4: :W柲,V0NȇEfj4_^ڿfRT '#$U5XW0.Z̯yt>DgYtQ<h$_|~|E +?X^u_ &vةҪׁA||STI;;=wz1~z69kee#^7huzOQ`FDlHUcfz唽M{aa:jU0iMaWiܯ *N6DaG(,ucGvU0y&ek&I?|/{~>__GH,F6ᗇȕG ckb g.Fhut9&&eLHa2S5 i:>@~_ہ )-nLDY %,*6^IJwX<$bQLD(4mկG׳qvmPK )%ԅqΞ w{qi|f;p&da > N":]Bc[e?p{~O~꜄slNY籼׃&.P^R$eLq*5*dLy!oΤeDBХ˹Qޡ@J\z$a.kP (IlC"'Dzt(ŗ r."r90GάK6.{\3['^-Q^/o/B";E'n.".{1_kI~A3U|QU- Ra.Kٸ ?[PwH$#!DԐLCi(D,P/$e˸bBN#0\x^!bd(CBZFjB2z\JHu֓/JۿÐHt["G Ng7gٴ(NjN|? Uu0VQ#iӀ)~LoUXxZ_V2)~.į 2q}Ls-=IviwQ# q"qB ?M)‡=ڞ :yd:푶Z4LyϣjUm=)*L'az1u=H|P<[Dhb}kqJdn7ʔYXe93[}葴ׇzL\OoHG,Kk!_p.\F*wLn o7vdڹHo7tDҹDm,@E9!(u)-Ѝ'1rLEn(Lfhǔ8z z=EI 9#u@ژnF :&A''{G~fP a"VGV#K`i;]=&u]]oqc)Eu:D6)VjK­])/'h)2T S hrA 9TAg]U9Ebr}>{uՐ!n>]ԇF{C}Hwjz4PZzIZQ apT.'Nggg!xԄX5vZ<ۮazԨKzG]6\${H Y=\\>kUU]:K&pA5`Zu@Pj|Ll|g\Y{:86p A<.שzh(BAK[+@D  B5(ڢ 3W)?E $@`zݟfG,WC? B $@Quop π4"Dpq1u9)m4Lk(>[8\zѻ_?>>97((GN"}DuzU 7!VH+QaQd(6we5_WEr$K0z$@H$/ctm"=Q:NUQW5CT(RI%YLENrA%2V|I쁣J%Q;uI$dIjyꂺN $_^ZI$ 9'xl?w=N HGQ.%H WBU+jŠBbE\Ȋݨ$a_M̬d(".Kbq tE $gP4QD@Isfz# Ta$EDxU=AE\"q)2|HF  "EHi%.;sie. rsEJ~9*TbUL:S|'QD&?RW]Y|?ŝcEZrq-[x3ZAaV%E o5O^ʇ /1ʅĬ%^Q$( #.oT"+FbB#F)1-Z/^}O%_.WslѵEvxo h0m \8NlP%mi~Q(Ro? GE㢘•Y~_/3([naDai|v %qM}N+ńW8WugE S5s{vSB!բjQ.e]kapt?$`0#B(R^y*/"E\].tǧۖr4ɲ¶ vU"Eu朎mvl'UF?#%>.,:k(q1Lzn-$=|LҌbQ3n7nb=߸~{^LD(q߈ݶ^b7nZ w+ƹyyQgc +GQ(r5{Uȗ۞_h"5G1ٜQ(.^9t .핡Q.WENbB9wW)졽#\XKy<ĘQLO'C|{1c.eL(Gqm}N3/:LDu<5ǸV/_=S CEGϣ\@_;ؾj>8|`5H#4!Aʥ ջ5BH ,y E>rA7Gh(~KwwUNzQ(&?H z$L;v_H`B%G,-\|ݺWa48yΫ`|&G|FG]Ϻ&jT}4>lP@LZ1Mޏfz?~hﳅ6 'G<ϫ {]Aƅ$&HH *e\j[_Q#( .P?, v̡"Mf:DP}u"M:fD 1"M)F#҄iFM"5D "46Hܐ&nH0}gFHJ]*/&TR4Sb\6Ghij4wO?ϦïW8y6eqf9t1?/e?tԘsϴcwʨw:CꆟE>Gw4wC?ow鹵g6(*uWOQVp1&3m OIž5G`=G/'*#;~":WYVͶϾϳ/Eu\,g_=?,_韞ou}y6ȳ>ɡz::%-~cShO.WOp1AK$y]TjX.ۗ*fMsΣ?-Q|ӿ\"7͋a;54Ѹ?IÔ_5$<$0s*uCuݜΧx?]ut?eY :M}Xd"ʯFhY?# Qk&E8 ߿xR>;w\==.lqW<:/RY]0MofѫϏOxeǓW@K⹯k; ļ_||STh6eyZVZ~Tz7 a.t7V |wCn^x'U&Ɔ^03}ہ|C/x$ ɫ7w~xXBn&4+m7 M7WyT f67N{{Eoo=Q. ͶlMX~i~yvS%7wxtp ~ș~˓_GHT΄Cݘ;1aC^~y? q7sΙyn@# "͙|8y7l|oNIs͙y۳6.n9.<]mG|x%rN98unwo/@:@lG~=՛O߼;fzἛ^\w!p+j OCHVߍAvվz}Ttx73w7~@߀xw =d y!tЀ')ݍAPx /Co; qc@;zuwwpQc;-;'o`+6}'݅{}gTwnG~}_Kt g ڸwlE})LUOTKJö vOֽxo*;hoOKo4/ KG8'oN}M#O>>5BgJgsg |]^~Oʺg!f߼=OMO>ocjz>LT=׽A^t}&Mh?a.?-qsw-.M߽>L#3OA'$`v3w_?@oi.oE'-ͥX݉~>.=?`fG^W`|]p~ DwG t7&nyyRLAf}&}yeSGuG'Q KvAWX}h{"]Ow}3~Y @')Ks>@'wJC7g>~rs*;ٞxn@I UB=yWG0(Cޛa#$}雿{@ jww@g cM"7%һ:.yZ@~g'Ȅ 3@ h 89c ijeo}!Dώ6 Pxlc ؂寞c'(:@N8h5@FGPѿƒ\!{WB <.Av߼{o/oIט)48=X1֯ >z'ꈊxD7 wvaʥ%m/}w1M$cFr$ajר\^7_|8i`HH2!\A+J`'H"*koJ FHT.rUAJU*yv?=~w"BJŕ+7 mӴ/Ǎ))xxUbp3;_T*R()/'ooBRQH%P٬ )JEbbnC-?.t-U3 } = 'EQ{ (|՜R?q鬸Ӌӳտ\~s@-kv>\ |!k;=ܧm)} `HQ>4PղWdq9]|t^d\X6y 3E|:VMQ]s?4 _Gf|31wT`6#MҸYE3,b?g|>\(z,.?) ( ?.- X+jUOD8vKS&\-?,~(-oGEvx ,%.xcfl=8sxL|uϦO`2ZY_).uX/b7+]e*B~wuvs1ڟMB?܏|`\ufKa5ank nT (GϢn fc00.m%/jtrYPw2\Rߕ>NT;d:ӳۯSɁ.>Uݷu妨4ݲR!Kz1s_։/Q92 ryq/51GT\^쭾~H q_r!^8^}uzܫ'E=ZoXCǻtaSU6+skscHGk7oۣCq8Iv*o'I",;NɅ }8] 51sA1=͊,Z#%jE&@e@aoiV"?DJq q4[|nEBYyU-SvWgɇWvx/E>9_ugќ/?=4ǯ|g0hqZ % {Zvx:^d!rٯ3\c_33f-{lE&gQ9͢0 8Um-x[_Cǔqv>OGGe;A1h<\cRu5$8Pdl]l:Dl!;& YUM wS=t}{ l՞ӫ,zr:W/$]ytȆvoD{_ ɂ_^R7}Tr_'oNq*hp ˆ"7 zhr xptqQeE|t^ڭ7 #&|z=䣳xTdbq-|[cqW|x=ixf>œYlkԷM}$KDӼx=/ۅC++qtهlɆIhe[;tCůٚrjwo ?{boݓ97I[x^("Z"/gip5?O')WEeiu4?#z p/Ҏ4wo{Mӯ &GrU *YE:u;Z0Og3xՍOjٻ4 gWYիV4MS !& TIn'1SE澣,q4M!^:p^4 Ҧ)̩TEx~QiidMSv0PV-+́SFo^qE3怩->i)ȁQE\v(m6?k(n0E,u1h~CQ}o+wR,/(+.8'QQ%̙DZ0LyFe)(Ĥ?Qަ Z{ m[Ʋ4*ഠu!t8Ѓ #%nZ'ro~YF&?cy0oJbOV;z.al!fpgʰ)->7QIp}޳U s6Lp (Z "HR.FH\O`<)|QW 2!R)&CYDCʅ mClrABIZ0@(#y/YǙGG룘O>^G3nzmtJEJb2=iGE-bY]mV Enb=IB\A0v*($}>ʅ݃gRh3OZAi"H UkEb?[+@G\OE~r>@>@Gǣ\4u:tZDU85>69ϸQ7 (r("cS,GQ./g;4q8ʥHw ,[3`ENrA9fM >9sel*FQTi0+Rr``Vf(uaEf0"FG-TƉVVĐi00c0""G1M^gރo8%-Q.J"F"n!sȹQ.F)^g|6֖?3<CFrMqC4ti4)ӤojJёz1Rˡ L}D(]n 8C=. k\L׃\5Ery8n VBG\>_Yʫ[2Y5ZG_ϯ.9*#-YBoK{蛿[Mm6ʅ .caQi0^\zVH(2j YrOf]Nj0&nlpc拸3iqE:b4N;[TTo((jNX"FK+k5lPdi{"F-hB$(Er/fxq?Z00`۞" ȆQ.&d{u?z=ўb"FF->Mf2*q| GPd&~ DR쵐>A| A(ZHF˞*ٹHhW4^痿|x](.=&GU4SVNe|Ou6]gEf$=EITLQ֝cfK-4*)TR&}{bMhPѤh"s_p8\녝f4WzaO<dE^ .4f &i^4{tqvme!Z.a40Exe?{v| dT1431 53 B|/$Q FrIdTo7EWY1}i4SG|6gp?D.Ahn˻YۺCrj:4t읋hYti.BjG]NmMf:݃{Mvf9=[~&?G a3aGcG]NIu&nАhvL'(@LGPc&$GԞ*,;̌dh#xB̕h6| a;e~42?g~׷Z 5hY0ٰ&i7Mv)>od{4=:C>FGࣃ>~UԽkr\sQXI.g~9A->hͤ}6+cՒe?x+YVi^Q`~#G3vo߼<9(7li ĝE^˳dHv9B.Z\`j$sH̡A D4!Dj{mĥokI)LHD0DiaVeS(iKMylr?):NCIy (2Bk[Zl<:_-?-[b& J3-6p.vȖL[JwË&HJ )ϢK[^ԥ4RڥKɏL?܉{$Zќ 7GMi˛l@_]N6z6I(ViKZ7IHk$!. +L-f4Sb\ a&K-4H{ .j ,;ȣjf$ntETTLLj4yXa~cbbxŻ^+c$b"b%~"=F+&+vXA^4.3s!F<+&<+vY pd۰10iyQ.i"(0HjDj.Rk \ աBt>E[#3Q,ՠsyٹ0R){#31,_W?IHEb8"hh3A+~(p8E9Ζ\Ť\L6(-\L\.*L Jcİb°b&(4?"ff0-<ɢUYL$KLYjыc#$*v!QqĜbœb 7F)&)vIMcVst!EG f9bvHAiB)&)fN9.[Tbdbbb6 $981yᛉZ Z/zdW;n|H.D.LrID1jL1iLKcb8~ɑ<$̥d9l+$)Ħ{h-d-lkuIaE-kc4b2bĉ~6X flb&$+1JH1IH1SBGc?obbb/G=Z{EcpWbbbk m qiWbĒb’b6 lDZپ# %n8AZbb~G)&)fH1̤i-aP Pـ'iM~\=ƬKRVy_d6SL6S̴}J2t d]0oα@S̤%NK#pjN1iNKsZ2j2ErϸF`TL`TyW{K 3JT1ITKZ?05F*&*fzU2TbRbb(-C*&*v)U[h0:3ѩ⮬,g[E|133d|W##3)e(H$HLAJ6h )Hy`}%R1R1E!mЅɅ].TMBO1AO z~})(IAmB76b8$_=vSLvS첛G*#AǨ9Ť9.iM nf1bbAǾ^#}nn]vSO L1 L1S`b\rZ;z6Xdβڮ )"`&Saye@)&)fNpl0~}?>?/zON_sÖV"ff31#^@ҰhuhdLHĚ1*G1)G1S9nb^%(vG~%BB]#Ld.h+;L]; dC2| 0yaU$ rI$hC5M%CaK}&!r0%FɅMAVd\RMA%ɐdJaiӲ4H)RlZ, bL0&Ę.nղ4h82 pbft!03 QME5 d^2.{i8`KAd\.8OB a]p/ d\2Lri(y%CaJy0k- K%B8O&CHa"M^A4(6 _lj߲4(7N&r!ɸ eiv2d;l=eit2d:鴅i'D5=N ds2LIZ jN4'Ԝ168B qb]D#dj2.i $8 ^=!8E!^0y:;!DQ2(B QƂx!ɸ$a6}k-xi#CaGSҬ!Ȱ5MH=K FT#R dA2l\EĐ aHƅ!yZ!4'“ O3!Bɰ %NXt I&JC2!aajȂֈjȢ^)RpAjd'^u Jm+k`4' Ob:T>Fɐd:J.\ܲ`PB2$!$ 2H$d8Eʐ=2{T9#"oKgN#z}3^quezu$CVa[IV\ɻ!a'ɰ$nHzv2s%țXt 9G"`đ!0#IxM,G#48E6BN#ȑ!#\y#CaFt|^#od72lވ;Cldذwl?| 7Cq!Ȱ]#= ed22lHrYg82DqFɐdxÍ&!ɸL "H$B/eI2$$N^I$L sGmujG#Ҏدӊ_]|/Fiel<=/І|.%$ BH $BК+I$4{QG#6( j6xLd 1G1V%"C٩DdP"2$DĹ^Rbb#ȸ;wӖw~[Y7u0dXR$^1a^&Kɰ,%ߘ3L-@ޒP(b Qb{dI|&K%5752޲S`'0 &h@]pYq6ُl'~$ 4& ^S)w L31,,ocwD !N8 "|GɐdNQ2bL0&S dc2.Iw7+\ONFɐdg>%CޒqyKw>f2D3~Fpd\EYٳ#dj2.)XسW$!ɸH-Tk(Y@ GeIM&q= Ұ0d\0NG2#n8V!+p̩0_$h!%d!%\ iL>J>J\QmZ8QB8Qĉ8W z1(?A(!(aE e-jx tt#1E&'%'%\>iJMNRBNRuZWj|R"ĊŠ&VA5E&%%.(LC{QBQگ%%.8L0J0J\Q#ȧll3}*y6~-ʯ?}9:%7OjLgi9NQU'Ipau}7Ns=7_gYTWEy4>ۊx$0z6>γ'h0~8gU+|8[\eY7R<>/*Ţzz.Ȕ\>?W+ss]|| lC~rNX<<1e.7"H{)|\=럖/ecWElM>iNT?x_wY2m̲s"ي`prR䣧} U(=Og_˫<;T}RTlt>~>EQYS6<< "KpYQ# Qk&E8 ߿xR>D9R}E2)ȯyt^dӳ,(a4̢W/?.j|{z/aU{ʊ#j<f4d]DNߝ,+ UE/ Wo`TvG4: w/O~X2bʡ&~49 Z|]<]!,-#bZM a H5 ^ YXuG=++A.U\FثA tenA GŨ_ M"Q75&MA$NETQCuqWNq5wEBAپh,,hhHKSN]ܩp>, لS_Y"PW7+.TJ )uFBp9#4I$uCH$dG__/H* RUgavkT٧rJiݚI&uѤS_OKã0֝&[v:mNU. 5~Vy( >䱥P*٣Ųyޟy,'@4!SFs~5\fhWkUȍ6h@ck)E *L~1R{S}/| 0L~葲1y ^ |]K %UdaR5vWIjjͶO 6TEm7֨wA~_m93 IcafF]eh߉e0zE:iQgnCuK 8ig~>+-半U|&rR_e8^bZM&Mܘ>BJ2Q!b!p;YalVUV]*sά eyv/몵oTUn>5UW]G@ev솃|>;Yeubiޒ2;8%$Vj-S[yVpY^iV[ѭkp!6.#5 z,҂0v vi:zDbos?uo%c61ۈf9>jf#JIV9noMZ"i6.YII7jɪuQ@2z2m]- w%@ kbm߼隠@N]?te4 _&/WDغ[I33gxv^V2HFZPi0_lϋ;kډ]w o m1/&YBdY솗~˾J_"eu@W=wٵ+{z{YtӒMii'^|eu鲒\Nq1/{ֵo%4KЬ˙ES|:}MA*&[EJbv@R'v֥Ί⭍F Af'@Jǃs,Y.JV̪y6WA j0fW}վai'l 9ޢ&$[`둭%օ֊Ru=adhunZwrJZ%6s[>ei+RhqlMwZxpYa YY<ܝr}8fvm&@L@$WWŪmoe&*2p)8 \~UFu؉0r;&)T v({z9,^hٻ>سy=˿㑔KBW^Ud* m 5g7FWOȧU.V^e,3Sx\r)&Rv{SJ|R쫢68!tHR+% Xa,lڐRdb@(qRg@zV#mڐRdl@V|W;7 3+\*Ո,7nY½T ݫZY`*&-TT ѫ^G"cz"K\,hz0]d @ FAF RA9Ėm"KD%_0Eo5z8IELγqcN&K乚eA1V[֬~IgHv)2T k}.L߭,Mv wk>. 6#oS,0i0_Ez&|9@CK^T7_5,> 5/E yA?L zQ8u9X/ 1\*U<4Go'o/^zb$=K⳪%t(=k`Tb8U'ʲ F}hPP,El f iy+NH'IR;!}2RHe RԬ;!et]”L k>/i)yY*E'A KiXeZl6C[ĈΕ"J]0^Ңc$Jrշx|tvg^1_~?ڛ͋~#H)ApR巭U[8>(b-#sK0mV[ls{9H+ȴ6L`@ WHޕ ^nѲRY@fG R*y(3-U*YhW`%JeK>tEJC9TuI^=ʌ)Hq^}^\X\5(E Aϳv%MOR-(,iu>ȳixƃY@(PطVܣII]d9P* ՌyiH0IPBa2$IRl2ɾ̦lL)tM*T[o+gi>=Q0n\!H|R'ܷŴU LT@qOwbޖzG(PjDwS2nEoԯ:soo M75&I£?~+l7yd^̲sy=it Ya6D9sDD@DP ePN8'M“(<-EoorFI9O ;u(pyyyb|]/\/at=.\ڇB\~@U* |FJMTs-Iҁ37S: ?Em9G[)H ~ @Ҕ-M].bӿ,֜PHNi"t r*=TNdJwzXoҦt mꁷˉ4QiN=*&J٧yUBF5 U*Hjpۀ=4U*j5ۉS4V?{q{uS2afڦo"Qz$NrS(X8`GN._`9DrQ(=;kl% ],t @UD/`C;=:ZQ@ *[J`/9rp(A)u:H5;QJfQY˚䪂˸NqN BAVVBL8*WF&׿%f i깜L L]*$WM@)Bd3s6* S)S(S:eʧMF&D P:*[=Mtd~C;e^OE}Z~jo''tz6xyjHګ ]=T4zh]4qz<~qdӋG, U x U_:FBDi"Fc>珟?Ul.SEiEi(F}Ԓ+CzhQ]z{/hXt.(Tݼjdzzz?&vqjbF(?d3ųФp"Niq~⯧g:yi|N/kȫ3UU:J*lL@eh2 P!!7J7JQ5"nug<~zzrɣWJAL(bF9a\? NPd2;N.gO/t.pT^mεzѳV^!pJpJSOBt*4,G & H]O^ גÇ9&dv!jIlӟ^zpBY@(+P莉Mv~=YKDJ)9|$_ SS:` 9*4¢%D P:'?:yq6S>Lo&]c2|Wa>o{.Z-;|Սk~UZ}bS)泶vJIfQ|6K]׷f1ʗ?afT{;Qi2M|?E A͡ż,Om| [H /<H?NedJqeEi[Om, եrF/\g='4}Ef?63wd5o~3%P~{FGnތgѣ[ѣ$ dԒm"ޏ^/hvETL~ ʾ}kcc4Ad:@$\GL3=aћ$f(O7㓳'8[2!D.@HVi` 1p4DeI>|'tnXt)S>{ڎ7b{&30؎Rar޺[޿ 7PnVw^V:SFU)Q *)"&^WiQ*RuW*(VmBb뫮֧~~UZtRdd>{XMU7·W"@%u"২>;PyP"LQ- s\eTHr@}xb5]j+\ Oꅻo{n9X.MҁVpoEDсZ"+0@StN /uoMsϽS jŒT-ƃߩMgsjӲ9 %j[zd 8i7}zMo. ͮs ЌJEP@zUJ^iA@~W( N9pft#e^λwMC 50t&1Tl L]5MKƋcnq(ZTm!ݽoQnjj>qzy5-Zo .ojg)n[W kl%L5* D 08Pz6̵ P{@݅oU!DzjA\DV?!ԅxC :No. /mV($AUmǦ2˜ZAQ4) ̪@VԪU+(w^**`h߇N~Ÿ ²cؒO6W@:m׷-bUAg Ԩ=1}E تU]3_ѷv KKz+Gw JGKur0>  h |h E rŤ .䩔CΒޢml>b@1=VU̞M!lLmLUYӎ FD'Sd5U3kTNڭRkT ٦cGaA5z¤q0! csɒD" pTr}#gP7ET7붩ms77E7xYVgMߕrHSSr\tLו}$WwN(bK#ѩ@8M翬K`:E;`Wĵl2bNm*EƭS(x+.D)b@PMQ%$ U 8V%[ٿ|}~eeza@hBM5B0 E7tZ-Yݭ4\ul dȵ_%TDTЃRХ3P.C*3R=j::aktZ{jb;;T_@gNA5XB|nTT5jGMjp*oFCOA7Xܠ: ;qk)o07zx @Yנ>ܫa>YS*LJ9@*i}{#R  @zQȃBР"Ҡ ש6FDTЃ VͨQU aJ]nF'L u"r3a@BRXhr{{VDӁ~:\ kRkO+1)o*f<U tH0xS s@U2c3mN$TFv&ve٩ 2}TOʄx3v5px94T>MT>P:?m"= |NOD4r4qt JG<(tDӁt~n@!tӁx~RСO] ^yu]@ox>Mx>Ϸ)ͻң/'J?^ uPObm~o&ww::Z]0H;MH;iW} ?z fVn;ȭ :M:\W}B* ;yHÉ[qꉟ4th܆ ǵр@&&&ͤ۰52ѐVVM4tӁsRmGNa|1J.zr(q:%ȖΧf~(/at ,h|a)t T(oӁm|? tfbe>f_.ӫwzymp2~_.7٫i_*V=8泶궻골uH3}6RouOeˮ >nvI_{^OC*zr}3f*IKnK24=x^g$6$ׯ~x` ~fEq,.$V/_?<_,J'j=nZkXK ^3:ޏ{#磊&~wOX uPKl( -B~xvq>~{F nތgѣ[ѣ$,b.d:Wޏ^/hvETL~ ʾ}kݎ _>y/ (w_l2=aN7ItQ4fo&'g/Oɞ(|v>bEW(w a fqxŋdơ64de@/wИWZ8ܑ['xK=%|˗ګaUԥju/><~٣'RiT pk5zţg'N^ZGBõz<=9՟pn4sý}/OzT@7FWN~U;B=*@Rs 3_GWGPax/N'O*z zTbz8|ou|.[h^}?<~udo?z(Ԟnz|ad U:Jɪӳ7jI)ϡKW{WVYVVzɣWdOWϣ't%ыOW_ cL ?:{| Hxf>!!%>tj>ƂcDžP1̼ӧ8`7DjX`3YnB%"uDt5D <$A"?t? >^CA"5BtA Gdz !z!ʏZEbyjmb 7!Z : )dl XEHM]=G%SD F'&1y'gZ=L6"RWDWS0N2]YFC f 1،JJ=H]X?խsp b =Rѭ-D!R7DW3P':֡?qloןz; RDW:sYt"Z{ =N]}+?RAWڜ19^' 1#t#wdz ri~pgM GEa&v4yq&½)%uDb9_jE½)"NFuyVWHm RAW:p$U?7uzͬ^OiRBWQXm^?;9+}RsBWoºDO/N^=>Ym(ؒ:=:yqiT}=N/!%tu%DD2gOP;R#BVBAAJgvb_$vq=>Rs@ 禮 8`@jS`-rϥ6`k?P"ԛ/Pk>DE:Onvj'Qϼ@-B-dGmu aw.PQߺ@m[sحR`_w?CwGpr6/Df<dO]w]zO%o;|ti:>mZu1W=~觼^2ɾgx"9yqCyƃޡZq{4{UG^uꫬ<$Cz#.'?ʯ.}.\G;:}8WW,>]۟.wCT[Ym`6fe˳uu 'a 륗bBp6b0rzvӉ|P4AH|iu-EJ@V_z5 "=X BhuN8/Ut2P7zBꞋ9yhF%[GS< ѐb T!f.wZxԳG>yVbfKYieC `34Vhˏ'N~xb~VOO ftb'3پ8pי϶<\3X0)BC:EH"Eht[mvܔg?nl2]( ^JON^<VH1!pV[f29xWE%8'68,Z&|P<|<4# B赸 篞?~TU[qK"[a4>d !A"O9(?߹8;> R5.6E>[6ŢYQg1]Dv菗?)$qlˊ4y4|FN4>|>}WqH"z?<&uFcR< LLDlo_nw\nK8~C"PLx~ 9ڎfڜֵV Ѽxfφaqe:M&zx>|'kH˗P>i3?8 RxV*$ӷ-kNgn٣mӮώ+p .9?]̏N<~kA*R–l>C Z͛F~v5M^ͦӟK]Ol~1g؄Aex^tٓON" Pԏ IwUf<4;BI BW{_e[0M*K+^(C۝&@|Qv,4HZ@ b5HBXYEd~ތSuÆ$Ԟ(Fj+PeYjOOEH.CZAXO.h./IU[wh۶zYablLYqx=[|*W'=<8K<3Өod2mN)2L33y~IXO^NP>~JL_ϩ%2/E(e2ZN~5?-Xr}& _8v+ .#0GएJ'}ļs}TH{)=*QP"pG< ܴaI1J ''GJx-9 +JKXrtGLb.5+6 .hGGJ'wpoVҧ/pOk Gy4GL/騣@B&jW( +>KkTA m"%U׬&EMuqm!;=ֵ-=z^EBMk}|޲n2{;%onc:n?oFEvl8x0]Iz3FXv.F[_6J -.XӽflsA X[R82b~ԑ$! /\tlwlnޜ-ml[jbC Y\rxlHIPvɿN6fQt&(_ڟ"7چ|1! X.bit3Sopq#KC@Ycx?N/r)uA-ă"_ KQJfiۖ Xjob&CIgG4Oّܵ)Ti^Sˏŝ!Z:Cli_i&Ab;8E3$?ߋA8}Z C4Ҏ$!6P[uk&nL\"HVL]d\zupFS7s;/&PE>e.xH3$'dA&r<e"mObA)A|5ȒKgU\g!8ť5z+(ެL_ZCYNիȹ&T.y2\t$myv(Nt{湡 WR `RglNtg8v>b OVzm*B<YH\s!Xע yZ %AGs4/<ˣr:#qRq ]A|;P*M"eY\y٣ų͒uAJ6a ~N͑pFhO3aooC0wn8-͏x(jg̦O7rw~fCUm9&vaE&+Æ=ךSR~a;o%`iJO>4i*?ӄR +גwߐאyN%C`VGp(xM}OM&Onnl$CeJYAG?@ItPhِ;2-ܾvh 눠봴|@JH ; ARԭvJsz :7~!ڴ[ژQ2Y$G5AԺ̯UM[EqY{VG>ϊOӥsQY*[c;&!T{4Je̞Hl\r_H N`kc uY,B?N&!`mܻ'„[Ax.l;66Ŷk^GXƉT>tLĮQ5 hb07ŹKmЋ '(F,ci.Ir0?az)_LjnxW<3o\9"(& gT,?oo -Ureh|ɝؕ*#1sRpEt4ALh"qk77|=mx7"\Sݑ8D"bo y77INFds@H`#bbmxkӨ4m2ӧt|7:V^ F/H(dTnP3!7(7ʅi~ FF116qb\e~:ueٗpvS776mbקF!?}^p$$E(b`T+7{;'APDŀG$1k^5 55ɭ}ڹI(B(&٤ "l1l^!6 )606*Ǧ( dE" !)-/, mm nS&`66IN$l(E$l8 FF6NĪD?%_EԈ A}u/)D(b(y0b^t&0+`VeM:;"(Ff<;\A:NyJsFl_F!7Ml("(f&|vq3l-;-qQlPGhE{a 88i#FF7Q\\ "|i-b\`oEsegp]\綝h{⧇.:٘R;U}#7R-_'G/~|խϑtZ F,EW%\*Q.L :u(N' <@H磛{xxõv:UΞC膎P=膑KGҫSq7"{1{BX ɋG 9>@>E YG1? G>P((6SGqIJVG j%b€jT|X RLR{i?xF&fpda:|3?F/~Ǿ x|" &'7r臋Cۍ%Z7Z-;vq5{Uj;UO8m;ϢvPMoy,Gg~}}hv~N|3fv*IKnK{"Mǣ80Ax:NɃp[ HIO^uٜ%/IL7[)Lt\,J[j=,ebR.`a.w7zO:{?<(QA[=PHw^~:NI^y8VRZܖֿO3C]5g7R^ oDY_F9D@ʵs XJ(p "Pt?z<8L{ QGnތgѣ[ѣ$l]2jtT Z~zD(/bFQ[kٜx1F߿| _A2`U¤3&fz,N7ItQ4fo&'g/Oɞ*wYa2owsG=(^&xhui#fi 5pbˊ#Ն̉O|DW}'yXYqG}{!?KøK -wUq4Lk*7KIøg&;/Y+JY8\VԫLNjt%juX.QJ,ny.=2A8)DE: h(-E4]\i&q!Kb(I1#)RLF*7-R͎[ekxDGIv9$EDϾ.~J'Kt1!$R܉¸_2Q4!F(.4. !".@4C6\>Ju:Xz1:Vn hl~.9 vH&`4Q[0) iv%\T&t P"pLv": v>=_<9yO-`.+*r +ͤJV-DNfo0[p~:ʇ!ZWa4\dO0"PLRLofȨ&DP-ՄvQ_%2̈́Lw8 ^@oރiMѽEџ7g~ W_ kjT(!=Gɛ%أ/F=s?&2 ۩b |TWOl#! @{`#(L6Ndqh^дl fB$M8ҰM,l& m{ @n<(De) V-}(*Tm"otMn[vaxnss %I|m&^v'qDf¶XIn#nv l2f6ú*, t?m1`,L!S.SDH&P"EzO>MdlWLsM׶>vjnL,6KҥY$Tلvѳ7rH&5se;j^ϥkt&3H ƈ;RAz g831<nMs~B3`g"KK."AR= }2gB?3ρTTpDvRL^dGy_/_*?˯_8uZxueB(/fiC{NvtOM@l&;^*& Ml&(;byl,M"M`.vcj'C,o}fޮ V,f%ˌq+&[Ų"瀠n3BA7`Y; s[s[핹\mq,7 _cq+[,x ހhEFᵕ {YAt"trW6l=`YudmϾF_ܼY|\ɕ05*n'oǮ)7B:Xm*gafM\ߘ gqx2f,|I[Գ"0(i\U!C.PGZ.x_!B!HH1٨ JQy|+nWWx+4^w(RD(RE!"_D)b@0 # NO\|z}sH$qC/C!.FlB}HFb&Q^1G 9G@G!)D)b@#t8o.rPjBFFE7bl?RZ=))6}P`WXn-nI EgI|$$^끎(HAq[[QBU o|#Ba1}%x \zL\LT+FTRRRR.`~r\#]^wuvdžH(R(R,FQEJ! y\2] ԏ ޒjPD"Ҿ #i~p1]Wމ>rA(Z \QJM54vhGʅ? ]ȯqRЁ,I1iIqLۙq4O 5ѐ 󅎗pE+?u@#p9&Νs)U ؄=RLo r}O淯w~ םzKH%EL%*ՕIҭC|h.&E&4tkRlRLbOxi MMj&&E&8 0'8ߤbW4Y=lM}~}= l=)&ۊ>n5,r :vE)&0JUQɈ /* Oo! HuRuR{:);)";)&i+t)s 7B/H\';ykKUӛyz3ON.!tŏG S}Ai|K53Ѥ '.ci3]~"DN)"M~+>JU7nUAW1 CXV =TX!U(UʅځBO)OՏ(#uJ!JJT[LD"smS-2dH)H)E*DӾ0A{3dS♂Sj~!JJKJaRo/-!DwJ)WƔBȔ"ʔraB~{eA= Sq <ՄyuA7Hd*DS?"L*E*XH>\Ot_|L8F"bXI'RDR.lU&1T8T b<9:ԩRFHT)JqG$Nr8 ~RRO|"Y5;[qXvO)}s}' ܞ4j.pPP N `Y0b[$)Ӥ'W)tuR\X)(^*ekސRɐ MXČR\hK[k(mr8??'{~4(EYttЩIMG$&$"my0h3i3i&'+_=u0/~|Q:@Hs H(; i @FX&XvLcQnMJZ:]kұ/<)M)bO`P v,;=#Pr<_7HD.Ÿ.+=;iaiai N9~Jy y!Bc'f…}ftӪJ6J4B4A4eسǺ0`1Zqī x,RK}:Ѱ%0 ],]Lcl /3]7Ѭ4f%סRlAY(xMG?F&frxF믪ҬWDM+^ׯ|щmF*qhDfiBfi&2+By{/M/}4CT&TfXXTtD{%vi$vi"vi{ÇHD.8?Qެեե] )UүY#KK3]DZt^ms]'4%{&ޏ='A48VA>, ]@ B4A4J\ !UKUKZ;x#)V#%K%K(Y@t^΋ܥ2#(fANoOy+ L, c~͇yjy^`YjP}-Mjq>_y&WKD.RVSZdqGfn0Yڅ d6}kdp8>k>NJ̢~q]-_SZ,nӰA+ɰ s@d[eB?O/x!rP~}6g$Gx3(azn ?0&!v@o[ٙȑ祉]G-{Br O= c9.jvaS0G\;hFD."n^@GᓚlL: %ÛI_v _]ƆsTxOg~8W'g~s ]]-Oc0J%vq7?yx:3pNMNur`OMO"xSw`H`OWF&uR0n!M, Ep:$Ғ^ujujS)E&vanp{UN4,S-W:Ӎ55{ ʯAʯ!ʯ+ nD! xF}+ kv m^aD^BW2W4ս^M7"EE4@ nD!a!m]v " #BvcD2tWCW֫E!iầk]m5m5,lm[5[5;ŭMdל#XXkѷ԰9Gb[!s@PC@PnoΗ) QPf'OO{OCOSg{{{=o%qy% {V0wFwٟofٗjgWC6|,jT"Σl>wo?ӏIo&l_%iߍv|i'd~a\d0On)iK)$mrI682pϨ! ,(Ö^OӉ qCIg?A9EQR,X#/Q!A&x ^~jl As Qr gI PDp)tu!b&`ڮ(t! ]t;t4n.臞tx˝..䈴]qU:w+Rjč l&Q/wfG(h?pR,N]ӭqץq_i)hfH.+NҍUEtpp1|qu=xeu뽚&1r_ ɹ6M+r`ٓQ1baZ_4{GMu ַ~JT,ITaҔ59-ƌsu>Ze"1F 0hyNMw\ _.NHT~뻨k/8XE5^{- /*r!zqH[tp}( /ӝ^i'?є+t}?!Wv+tuוs!^L{kR]QgP!^߬ ulͺ!@N".eFz9Ul]71NM]k}rOW:9҅pkrqHr{Fy1ŧxn2.2 n\*,V\Q>^®¾w\e Ϙ][s.c{R^Z]2MqOOϤ7î!DϏ]f=/u]$ox](.82ˠ-]`xO n أ:ӕ`׍`aeC-W /ӝ^9._颕,p8 j~VpLY׍*7-;"W^t?)HS/)ee2~Ǧ;ɬ+ɾnBZ^aaJs?(]BOp-[ϨL!t u ;Ԫdo|Ӆo}P@NQ쮒xߜxK=t[~[fNC]2IA$o"d%p[oy93ϻq7Vߪu]~\R.f~Ӎ/)~ pw:x|Oś=J7E:Oߖq\o0*pi[O^Ks[sp%pƩ(3H|>!sGHꊸ"Ťi3,I2rD ^"/G$?>3f6b#=^ب]νM41,b;(J-ftϲHk;[VYu5ɽFjO$86r&o %^_zuKY]NV"KocyK[EXy{D~'e`pP B0]d=a}@X8jKkG zNjr%e_NI u^%Y] K1^ rjmgWIoI u0 ~ C-~pPs>LfhmvXT^!m*hYxVa¾NĊYCvm\i)'|SzwU`lr}A͝ީ^(žEHˁ{Sr PWm;٪m0¾F+YU+s.>8~,nU^oɇIB^q胿!b K+[aצHW/^x}"]k|D?R>~W<F;[ tcaGKgQ̎>#pQG: zYdwTcL!:OvTz#n\+#plGc;Zj6ޯ QЎG| ':Wq-U:*>x[15Ʊ^:>'vT:#E&kiD'wT XxB<&@d.T mBTm 'fMF`mb)] Umb c`P=TDfAm#fh>"FUy2~FAxm^ƖXHㄸ6 6h|tuW~zU?b; >&@竷L*'G*T߳٬z*Dž說tv>4 IBo0=JRJȳ"-nr.SMk}=G<6HA9TRRBRпTHBc}2R.vP@@f ??*hm3vfepZ1C"b"*Sȯv2n8SB E DId;(TQDQ. :)(&7}}mc^)D)b)&$h[ RD RLL@-D)b)&$!4HA" +H!HH1@V_]c@@@Iii-u +x_x9 \~ \L}tQ.OJBl!HRRLvP?ZS1C"b"X| F\4"?٥ EE*H*zU)v 8ǍNCFGH 2ҩ4Yu JVl>N/4ײ]j.\ ff\=>IѝVs I=P=NƊ;Ni9K@d ) )Uh($&_]X963Z垇AJF1Xt\>Tgnv~h4|ACfD')b')&-//.xQ DD $ &+i;'vaU= =A T-L÷76?+tb."xb0!fҰ8 HeIaB,̈́fuVOM<FLG &q!XKXK3ZfGf~"-M-h/_;<@|&|fdj1 KK3XXX NVȼļ.U+ҁV҄.Cw8daDR=JsWžJz4+M+%_m%#ytydTw?W~G?:=yԚ!!K!K,֐;7q^s/A&v1v0]-ѷ.ȌLnX)I>ɿ:dɾwefǻ^\\( e9 -{i{i+e烓/~k^ h!g ]444ؕiFYK(]b(A ڵn[e-M-h ;E~&~f򳶛R,M,dB5Vzxg/NDiBiJ[;I.Vˑ[[ƅjܐee/{dmNjq ;rr&}[#d x*(Lb}e}e+TXX>]ԡK:zA!a2/۵ɋoa>j'> 33˸Y9p#8to=ZZՒ)|!|llg[N ~~˸['kk.]˹qT9;KtP!!˸\ WHA!qJ7^y@?9ȕ2ĕ2LTsiA`!`aq7*a.e.e\p)^RO)C)bM5oee\(Pv9IC_E<(Aɗ~QrS'C'dԟ+mcmchm$HOV O\#%&JK좴11Zb.^K]:kUPILn!݆#&%f"YCl P#p9)1qRb'A$H("7 EbI]^{4%fBS$s] (11Qb&71Ob.z+"ā'^arNbā8'(1Qbe׫Mb&D[JObLIV'1OOb<O1BNbā 'k|Yy&ׄ@'Ex؅ص@'E04r .1Ѩ@FEh R#&FBj 3;ЗScDhĄЈ] ! XCՈ3ͦ W#vq5B 1xE@GL,y119@LP:HDFLTEa  \#&F̄k4l o^#v5>DrĄ]H[ds9B"Fl ]#&FkxT!fD11a6b.fcuz f#&Fl%"aRz8"s.2P)@GG|8C*:,l.{u) 116bc#AH?^'{P >}dn܈ h6 q#&F"n%,m~fCYp[2H9uJ i1<@4P3:!ߑm+͜ʕ2zC"xL I9bs.0z6"qāH~~18bqā``rѸ@H(؅ح Cxi<:0Q>-5iwtq;dvtq1;wvG9FbR('tᛉ}g4~f6}L&}kX\dS)DZ6U~è~,lk}6Roٹ|}3fS*In(si2M|?ǣ$nIO^ur%[Γ/IL7[)Lt\,ʵj=,ebF.`N.w7zO:{?<H~VT/RmmoF3./"p1gJڡxI7WKu\O3 Q5 m֪/ft(\]G,J94diI #85(xk#xvq>~{f^65T?͛,z}+z4DO0Z2XtxHETL~ ʾ}k%ɼ _>y/a AjB0,4#pIl=4o˓mf 9wl_}/̞&Y2Ia'X$y/YaoWYS+WWO ^@B)  <[#TDr8d9F2{8}=o4D 8U.LDx6,xQ8Tqm响6TD Zw9o ΆPUHU뒮H:bbuoΧTnPĬr!= ފHV.U@Gtǯ4xx5xzr=wON_ t ?*" W.ƕFv,| $`+YH[: ;?? 2-6$(!\,F6I$'\(, 35"^WH"U Qr=;= W.UMJ_m{?|8>ދ>Ou)ퟁ ^Kd:_¦.e-]K\*Fr#6BQQ~\G;{V=:cl:ή'ݿZH$vt.3u`;B\x [ɷRdqGB$_nNr;ĝ+]5KaĥzWXw^].EGmq$Pc텥FgQm֕G:ĩ/%4:Fyxz?Ar8M(>5 q>B*`qW+oLa+W[0[ݕvZH)^U.]q}ʒ|yU.`o%BV֗zI(L.7TK'ewru~\!l#U.|U]bX0+bYPVbT/{ݿVa5#32i,٫ W c!^U)@]늋biSl2;KʂaeTcKE4, ]pjDr*Z$ioHBd3=VܻYDrAܿgus[LbkZȗxDKԖNKӒo-{ʜkH~ËGNSGЈ" EѴ0- 6Vl)lXѿـ/Ӎ66p݋8E;PUńrQUyݛ w]a&v"%K%Vj8YEUvJd\r1,E0@,.k@$q m%y8T]%ҹdsm%_M%BDvDorO.v1],eR"_.—:v#X P"D@iRXfieW VB׍]-ǒb $a.D3lopvq=˯Bo~'U(5> Fh0&5JS ?a.zd)8F1n_I dE$ $}9 %)/s-Dsk[(_DQŅT;4c"d-V6͘l3fd>+;-|>@ @d4->XXZ~{\uVxns05bPj!bFl5&Z}"vkLjŌZB1\ƧΪ6ڼm'o~5%Tm nr d Uym AYs\ĜPlPeȯ*OgiGotZ3VA<.(OPx""*О'G*Dž R "xkk_rPBY\:mwcUa @(@RE?.OP;E gqA~Ē Q = =*G*P@DMJWt+&]tmaj!|bJ!GG=;.QSQQ.wZ1ę[$bB"  ~; qu`rOUv۩ 薱űT,Iw#ra|+z2{77o Qh0` ;RR.:P0yIBz"|XK'T @T(tR.:PjGd+Mn$ Ao|uM1C8C ?( U,K{X#Sî7]wbq\pӄh#El#ń t'4RL|udܵG_`wR=ѷ IpH ғ$Ii'ƒbB|t ` \@JHҘ[%bYeI%IY<W@+S NRHNRNR.v.PIRIR.P' 5N`|TCQ YK`KE[ v(/)/)I,ci!/-R!IIL 0)0)&9/BQˤW!"1 ´Q\j]¼VI0d[!̬ʇ#1^!K )K0KY u(L%E%-e1_$ƒrA!NRDNRLtR6CDLd$Eh$b#JRKR.Z7:3)h Y׸FFE6[iӵ.j2)2)WuZIKV' ^Lebk|z{`-'ӳWk['~yIO '\%4@>"@R#JJqP. \ 12A仾+a"–)[xVi%Dlx&batfRLީ4)4)!kKޛd4)4)&I ֘"II1L[Xp}/)0)&%WfJJEWj II1HA\^v+lu^ ) )i#ΉE/MfLԫ&ҝ]Ɣ6R7R.Qs1D8D "bύb5/nMݷP}]<6ڋI5FgH"4$a8=<ޔÏJя T|z-F IIPIJB_J$$E!I䃌nIʅN# IIJIb)IB'K%体A"r!x;mPw6MRMRLpR[$l4‘44fF&fXb\0 v;=l¦p҆==LQm1m}HH@(V۸?/lWN<1)l%hDiBiH>5b4a c=F/Ǔ$:1=dy%Cq5(IVO>6;>9ӿfJJPj<шC҄CLVx W IIH26JJ.PRFF6[w" #M#u  $co&PcI=q444w55.+Yi{^9ci^e c&i0I.6Exo+ﰣuŇy/?i뽥 ä]þV%}itDS|FaO`O{ 5ͧVH-JhQZDҁRR0N f"xF*&*fRZ){@i@i@ijb&M&72qӸ5&M&E650F,&,b8;J^^J#IIMނsc~]?NfQ|`aŷ]KߊCG'=5oKҤ Ҥ&$渾CNڤ ڤ&^u^@gV2̤d&VM'֤ ֤] dL oeՉ%9:i0i&]ňޢ:kNMEhBL(%D)MmuRUzֺ-446L)Uw$&M$&"1Rll{e6id6ib6i6@5@ۢ{A|@\T'MT'ͤ: nl54 LHD.Ҽ"4!t T(L/ WOQzPmEf\|ڠ['fBlM5F&v|.T^;plTTɧb3Z%9YiYi&Jsǁ,+M,+bYy/u҄..z^"TinjN ]<]+yaL1V^2t+vjv ʠ&v]$bi"bi+M+^{Wtb $K$K Yzͳ"Xxtҁ`ZRMIKHZuc{7~b|S ZZMpY2m-M-ͤn"tg@:̹LؖBQjijiV+b.[EjX!AKAK ZKIop144 ,j+M+\tX\6'ozzԫO4B4A^WWW ̆RU[$6O>[nˣ \i\i&jUn.جs|U_V2-:KYa/fnm2WsܙC'&v1j Bp&pv ѥ ѥ]MB4A4%6i{`~o9>DviBvi&Z*}eK׋ :ᾴ \׹F9a8a ikL|a@t0YNKk~ҭ-;Xc6vk }='_aD@ 2t.5T2MT25  0^JLK__ Nt3GLt/WF&vѽvyWa]`]NC-44 UgZVӑ]/UFՈӄ.,>Z/M/~5n% " ! F__oǺo aP(CB1ĭqSהn^Y<1jHdubu&7Mv322LNWdes622.V 1d8ceeYY eeıA!q^^&J-jQAΕ!Εar1Dk^e^e\,LD2.R 2Y{O7z5a& }k'^C^!^aO"k2.hJi7mgdd\̧J&p+'C'?CM[ 3H2D2,`my z{Wg[[c__*Ă;8O.=È22.|Vmf] "qI Z V3" !LH-YYja#b^Έ22.<83b}CF!atlqYEFX!Xaª\U]דkͳ)Ȩ2Ĩ2.F6vTL'L+CL+bZF"a#k ,(],@TϞ NUQwOG8!8q[íF!D\\. .X>`1 t%Rcr{vr=3ʸVzHCUUҪ Ҫ Ѫ VY]:-ZgTIF'0wFwٟv4r^M{eC=sokPx1Uu޲ՍkZ{LŪt|V,jMfg,u}F_~>(.LtJrI'D2POǣ8 ɃC{<0·M]dam7㓳'dϖL~ٖ0M-7ܢܞIa<=dS؞Xcٺ,.r!,ald0،Тczj]&aZJ8MPdOoUHP1Ux[x0WKTݷY19.}Oeup2{hj\%Kw=f+ f %at\S^UW/M_};Qԉ9m*z}{ʒ԰i Knl&;㾅}y @]i__K ը&Tܗ^ZS7 }VMsw'VI'Cq6Ѐvl'A#HDJRZšpqd3x?9Ȇ:2sEr{1Ypq!g"T x)k*VtׯT,kW =J7i)~͟2fkKRUՔR* ZF$9X[|+Jt~܅jiP)  l+5C}.Wbgm+(rz*@TۍԪZ!*jcYd>f'>2 jrzU>J4_,СWyN&Km҆@%XSC%5*j!F޷XF"?:M q: M藱jn%7 似TWj.: OIߘx [;%>P~}%uc6㩴եʛ0ޓƹzySb ǩQ9f9O{O<)SRq'Mߨ盫/p[81m'0PNog >ڰ1$*<~~Q5W5A٩BF!^s^tXب v-q/Oei|dcvdN59F]\M՚753\2A \WJcvJktc3yly&Z5"I7 4܋7G\Ѽ[Mz.I1r;Q5f5m1a_ӽٻ˥#_-h=mGw?dD~ lكL.g/~Ė"ړ|6u=I" o[v;! Yu<{ѣ~͎}^bh~ŔZ=<9}N e^h~w,8 ]矟<=9"2roMd|Qt_($z@ȧg'v$u+{Z}P83 ǽkΧU0As{'O*{9{,۲O_&( sه9X9-el$ [o?(azKrS=<3 ؄gN ї̼"*.MFGڊx*= qpVc pG@ŭgM놨NLl?a?#{A@,@?ol-K#lG{Q>ɼ %ClK Ճ]-zIbM=-o/hڋ7-]rV X mcu!de??Wv;[by5`+ɥ?lnN!7YjO [ V5x)HVBɩ85!d.O&w%!Ձ[/AZ`P"nN^b 8jIRB']V{vO. 8ܒ{a;KWMז,D,ܿ힪%Uv~%@={B<Ϛyd7]nһu;*'сXGVB .nLuuKf [zn͔x+@0땮m7JF@J563{ +X)`'|E?lN쒞LJc:="x^zܝJc/06iJc5.F=B}zx^{k)eUC{JgvhL 4Ho핾m40>8~DM8ѵ:JAu\lt&]ln_~n~6Y$lk_҃;>~+Ls~H 6/"յQ}\jt}^մ_ =rhW'_7_G_5uQQ6 o4&R Kg/E2o* >*=ha-|5QXG?bvc`'Tn1 xt\>Tz#3].}bGn!#;ȴ&< :8*&Q{lJM8ߣ;u|zxq~m4Sڕ{㫋pg~ݷ[vy2tѼכmjަmnxX6jUmަm&]Mj&٪xML'x8=[/6^o=61}EzEU>,2-cƫmk'Ej}ַy] lV~ "JL7;㬪dڥAG *w+@_Vԗb_*ܗ%E eEy yE˿a/]awW0̊ l+H . 2fŌa_u~Uʬ2̮+("ͨ@P貈0B!fr} # LLfo(NoGuV֪~;wW\P`;-`wXzO~)ŏ1˅V*)HG_.yQ, j0\&\E,*5] BKA\M{7l|}J*p:y"6"u5IұlF,A&m~$x%4ya3kPWt@B & +y@@_ .le3MR5*({sY6aT*rĵ~56PX,/:ck{\1nX!œ",ktmnff8W׳71X58Ώ*7Øׅö]р=eύ`}>H F9j9A ^-pt9pg>A­A[ X.cqU)G/<6,5vL]-{#`HҟOsAK/rgrt қP3'%!9Mn3\QO1K2c?}+I9f d`mP;{Z>7QB;J')Z/ǟN(%T,DGunU}~I>Jcmī=f7.wok{*d _2H{v4=JEEtpa5K{ixȾ|.bDR82?x ~=" ޯ'{%o/08akݕi"\;WP e[-e˂XZ,`x,y J s+V!FPp2Ga"CЗM@/X}{H@K[3q̦}[eBA`[v~-1IYsn炔!̍a6IB(sq䪛 ++q!C@3b]dV X咂\ `_QmpfWgd`c)U<7Ez߹6>uO1Éܕz\ӯ*"&n_bwV\9 =|5}S=豗R0n=|^)@mG'gcד8W2m);Dˇ {r?E<|p ΍2B;K¢"N67ubO)<І0m\Ǝ$DbOJWi+^bgqg <;׹)((6wTB"bm K-wo!oyW{,E0x .ˣo%Dr1rjeH09]e/0!5cߟc&rujӁHs@EDD^ QDQLTKwwUKz[]h4$gM{||'h,T!GG1=|5wb(Kp@MBH>6A lVFp?%:"12|nBBLcUO';WїTI)R\NCImNNx"7SD=`FF4hm |=g""mzrB)&(l/i G񐣫'rэU!HHFwP#E#" P>H>R>R.Qp`$E$d Q\Ҏ᎔w9CRCRL 6vB'yy\Уi))&؈iٗ<EE 0beN/ Eʅ/j~!HHhE#LE3"2R3RL6FBj[``\PF̑&̑fb.C/:y!=@@LCaȥq q]f ,GGL>ҧ,TTL_5Y]} 4˜4 TPxyYT$g ZR1 v)"Q$J agеDϋ~,EzC0i&)֍SSL6&`oΗv3oH 44Ѣ2W]-ґ4ё4LdͪƓO^Z@#/I/IxIyK>JJPjLeL̦zi^JEW:h/i/i&x{p|ۚ5t\LׂXK\ns;|B 9/8aL`L c|WϤϤ|S\X҄XLRyϩ~,5s/EP<^dSblˢ4|ȤȤ]D;_gzI(Mi?QrScIXNOO}b $>i">444İ9yC&fbZbЋIO?k7i7i*7R 3I3Үw7i3i3.H[%m f3\4[B.zhԵ0Mnkk\&hlff4椙IjHjIRs]ȝU kYk~3C3By? ̸eB"5}33,XgaE 3'A! Dۄm~>f7)Gn033.q!b a +5 733.PŠ+'wR1C1By ^דaRB! ɌOfo})3)3LNGX\y,3.dY9d8d!e~1Lљ8REИ!ИaƸC~"p:P@I1If{" !Ќ fˢ>|y{q;Uˋgzf&P ";UL&D*,za&4#,>viiLk>dž\3C\3qfZG\'i2\B#h*&TMy ƁW4hR1&RtQ. żM:at/Nu:?VaE`P1 w-e[ee^H3IPL_fdiʟ{DJ-qh#Q*&T$J5~#*&:T̤Cq5۠zyPXO4b)XE%\+||N,~vASLylgxv1Hoί:ֵ0#-*&ZTEyYF!x'TKv##U*&T*% 2Abjy$BD]D(FvWٗõK^j^߹n,*f¢T+nm P %+ɓ~jZQ1Sf)*fx2\K *vv`е)撜X#)&.S2񞾽eK2#X\Ɛj 3AM]&'©b:t@ھ蔙 ਊq? Dl&:?W'/ "bB2L$Ss4oZ)f8n:UY_7b"(LD|e+WʬJ?Z7_G QuK1b&|I}<_zS>p 3NJp2kɟ-p\UX2Dwt'\w6 BL b)P1Qb&JtЧO T2ju2]e[k&V)շAȔ)3Ro,UDT1HӔ"BzULE ȄPW uFW ~Ug'-_'ItPYvW!w1ȷo[IBN 3n՝BEq k$^D+ŸZ#^3ۄRe}ۻ:G\ܣ:F6VLlƂɝ*oH3 Xaן&n讁odf̊,nROtbg.~荸f$nD܊->K0!b&RkŗAcjńՊXm܆7FVL84R#Y1嫃_* )Y1Qb%V1b&ڊ37CcUm,4PGTL|ŗFFE>Ҝb9.Cy2ظ"Iko~ yO1b&9Pn|N1Ȝ}[~s"eу^V1Bb@LSKItY: O;,3NLB7Im *v!ѷp2Ev\ίw{:_K.Tcca_UfV61bK.ԧަ6FTLEƈ A3TLKSXE<3܄8US?;SJY{ b4BLŴ ]On bBxX1b&Ϙè"hqbW1ab&!}oNq_=i/t[V]ZEC[z?F4VLhlhI~&xA/OܬK(i\=[lrSVUߐ+vỂTnuj +v> RLP^ %4`^ ռwFWL,S̫̝6uwlss!7DH^2S _1b] ص,4bbr.,Z٢(D޼SɊIgXSR5R%х+vd\is_~$9y /dϭ#+&,Wr %t0gx;`duia2=1F 00fХAfu\ 3OIkOp_|u/雜.}5@Ft:.VY"fu,ӈxv!~VIKj"XC`9~Jlv_XouqjwBi ϫC<%;:LvGti!:L0a׫ep2k<+ҵnQ:LG_*J62`1]tu.xפ3 ܫÄ{I6 ЫzqD qѼ!JWI銷2 ?̮1:Lf6!O~\ruT.n4G(,:.ʉFNlԐ!zWEj~!CۺQuFaҨ8KhM1wZ y4/چ,Ab#qSMu)eswBӏn"uCԩ:%QA:LȔDޞ0S&f#xc@ 4UwaM"dH0ZvVPU;GU@V&ڃo ®:`WA^by .]H"cծwQ]SA_~y^{+!ADVY"~Qs98:Za3Qj[[`AH Ƞ "|u\O PAW0_&k| s0OE;_A :x`ua႒l.źrb{CX<"KK,%%0"^Kx./g4#_U", Qݼizd fH7?ͦ>]du1e28Zkv=& Mz6Od~5^,Ƴby.ƺD2Ic2r&P~TW;LK@]$u,e$ZW.ú2aPRb8G9Cs* ̚7 Rc.AȺL58%XIc]Lͣj+Cy tWWtx-7!Đ?^-6;~P*x&&U "u2Aj:ҵ|.źLU~rߔVKTQ '2eR$C;$9Ew7ļ."ź2bRWp-ɻY3X.ƺLҘVYB7`Ⱥ#2yd>zzH($ITk$uT+H,)2bh;m`{I@c]&h;mJ#OK<.'TdGYc]buN;du Heyb$uPu$5tD-뺨e5vJ%*YI%SۏM.2ʺ(2ebz"}K.>&-=_ݪpEYd]&LlEZ j]&Du `guerxHTQ"Q@"lK.:;6KX`q%ÛI*CHTQ" `Gu e/`l2m(ۺoEx[m]sn7|` u ua k[,E|"Ẅ뺐p^,˄qDhDu u!_|dm PӀl.ݺL6Iiĵu eڸs]u ucb;Fl6m#aI_czk1yM 0?-oDž71ķ'(WlԳU2IW crJ="RzDe깨L{$,c8c}yjgËxJ=&OI]COI;**crX-ۜ,ݞ- +GTWHo͌jXۂd|&%Sc@nMCXS`M=.tz\\qYˈ|/RzDu깨NoS.r>ɾXcQ=#*ԘAz7ˆƣ}G=QFszTOQW=^\ԫOIPX=b\\,Oveۼ6oGx z 뱁aw/g|sD*igz\ꏜ($ c'#lV~pkqֳ)!z.=qz\.c\}js#Uɹ"(YiĮU?gQGsvRUܩqz._UbWU5BSyVjQZSF>b"XߥS=BOQF)S=LdD ˀ?#Tş!h!zĪ1YU<(BUHT=&ؓ#KG,%ռDJT(Q=&%7M!GG#TAP`7$Tb.p%AȞ{dOm/~ņ(U=BW*M!ŪGb孓tpzLU:u [d_}c](YTRяUjܾn+RHrQ)ۀaz.>RDz."W]")!ЫG@яDU"8Y15,H$2na=0AQq@C1b=ˆ1ޥPy .gcdu]ʁe^gLTJKC1nQ*Pl#]Gt.&5GXHc=&iLGXc=&z#t#1Gİ6]UqMQ,_jk'^X 𠷩1bDq>[dqc>ćqd [Ktz.:X9K}cҾ8ܯ>qL yB[B.}wg8YGW\}&k# Hi"mAwP z \>q._AʥZj)H}Ϥom'W\jÐ'V=|B ]}"t.b߁>.@W\}r]T.Ql0>`^Rɫ"yO`g}u w3ԫτz}Rg}Ā gbvrfGW_}&c=,$>>2 saW0>.e|>Lg#O>?&n #Ot^d}Ng8<>1.F}4L} g¶Xj '0XkA#O> DR !Z}hY-VRlȋ{"Y[ IL>īxUuF|w)4EUpT}&j KC2QT}RX*³ڈ%GEd>Lő 3S\%cn0R}Fy'L>1L&g 1{g0rč껸Q;`w3NIZL[:Pz昢};wѝ-(jPNphQ}F]ب2E) {ߟ)GUPT} SȪ>!.d'#O>B1oW+xC"UT}&/S>ALןGZUhU}&핽{4G#OЫ>z%Q7 >LGri*W}_+=qTh>΢Ep^} (>.Vw:kT0 V/dl)=W/c[}o>"s\R% +!8'lW߅9BA'W?K*Yx]/?N>`]RЯi"moȂĭ>L8m]N5L7r39Y׫"O>}Ŷ %\dZ'8V :24{HY$kIljA!KO,>H0 $V:j{1-GL0@bqTs- AU1.V±3Xg6Ь>A.h71LgP}qH$Um#]|L;OKȾ\W>k\a&'78ꇋifOy> q^կuLŪt|V|yfQ|6K]׷f$ܷ?a:{;Qy4_,>'oq:N,·I̝*3Y/IL7[)Lt\,J{j=,ebC.`a*.w7zO:{?<H~VT/RmWo3C*)k]#bv3ϔC ҖݲZJ ۳ʪ,<AfK,o<ϓK[M;aD{rg|}@)w!܏Ϯ?o/ o7od?]D]2jtGta~zD(/bFQ[k ț0{rG#pIlA4{3>>9{yMl䇍Fs(N`݀Ea0On)i _ui`B2PA_Fa.T.͇ox" BbtL¸:/νED %H:~|>yxb=q \d38`?a6/PXp9qy®,vb4bEa((?߅;x9 m{:>™7 lf)M0"8. 4YޚfvS#NW}P(:ٿ^.'@7\Ojcppԇ0!7 ^ ױkH"rr INWACjA;\̎;z؟C==פDua1 D JNzp1O>8u8 99Hڧ[SG,Lʫ"};ʊ}m}ꁋ B6Bl4_M}^gt (80оF;c!Oc#ɻdO{S+pn&ETO' bO^&7S4t/e d,^5qn(_mof7$ƌ 骕mR(ަp6bp-mzt8G&O ~8O^]bK(. G+8B?tQuH.T(Y썐ݡޡ\099T :T i ͤ{?\ E` "k|v5Ȅr& ^ Η*ʅV S)W"ZKE" ` P+*k%`.++*sn&q~Pv( \m+TNQNQ.xX^^,CN}4Xuyb_Q{(į(⯨@ ++*xJq`+E[V**T˸ v9VWQDWQ.XG" DX .)*>%`lLEPRP ((F Wu4+nVnd>8Z d((UE>#̊ Y ^x**PeiSi\E b](dS4 ((aEP cEdE(+b,a=Z:|@>"@rZ/˚bRm!:ifWq&3{Q.KAo(@P= \i WqDmENWZ̭z5Ex}9DoJ"Lj!HQQ1RGFÄE,aM=(ee/SW܊ͅ%QDQ.X ,KQDQL O.E "rhv`&UXF121 ^گH]He^5r("ը@PQħQ.@M͘ƭ#FpŠQĭQLp [Arr\t|DQLd ob,S1hS[\oUhPA(tt DB0ՓQQ.OrέZ#/Db"db2{*HQn|di-ˬ @)sb+i=my$9~DH"uHʗ>=糿(SJܻ 3ĺS@.WD(鳆miP YZ8}~2:7sM>'4? 6'G3 Dh&Z#AM1>kx91=::!hMIu)L&G3jnFqGje^:H=$0H;uMf;3+L|Y)\&G+wZOKid}4>4BأILG18Z,6=h{t{x xzJѣLgRK;%hhvYƄI^wJP$h|^ o&PG3Ag5:Due\7qcgE[mCQAGͣ}6Opx Y[WMf ?=&7a{sm=r?~oGh{.꺠I>h ,9?߃ B@ ̈́V+Ώ&Go7= I>Ňuӎ;XnU][Is={67 &G@iꎧ'*h&GО5T'`h&ú\'5J:$͔tx{0v挖kנdh#+|L/_v~:8zb(hrOYcPDmXxohy=_3/~ht# Jڅ@F{}M5kم@FA}d`S]g43:\&UF3U&WF\5T(L`h|9E3ə>gf S12.ۥik6J-t~z H >f:uN!\ ёi0R}HͲmt;^>>T&F3J- U>f S2ʬs&QFGeWIA#8 >pfɳx$ 7>nFXBiR]cq ǼFތy3f43 ̰.T[][ʷ՗ I[|xtysx$}ѢdhEÉ9bEFR}HͲnڇ!e>fYa|G'yVѸdHƍPɱLdžDԏ񬒊&Fm4Y4TSip1C#14i44:HCB(8:kk&F8t[܂\&F3ʈ@F]v ^nǦh&S+x}n.tf493̰@&Ȍ&dFoшhBf4aV7uFؿ3i_ڕ* !13Z>.βY-Kfv[ϒ mmhhk4ӮGM!6WTk45ڧ_S2yW>dЍ&F8; kI M8QCGbU dh mL[缻bc0q]]ٗ!aB'bG3|DגElmoM?o Gohy4ݐq)]^1eɣ}&&&bvO ofR"ţLgRKfhF,G#a93hbsԽU#3@GGGN5wkm46۬TĻqՅ/%E~*7TnV PP`B#F54Hё4g Wh( AcȠ1LF6ߠ7cț1Lof2n0(iOG(l+ B5jo+ :4shV$h 4&A3o+&+T!Ee7[6MEnxJ4U;1/e;9Qm.ƷKq}%!p i 4Ӭ@H$c| i R< m1$4Z͵ re ӕYI, rcύ#m1#vS)1ƧĬE@P;P\o׉vsA2,}(jDi22@P(cjHe 24 c1>Tni 1cbXrM$yMC&,6`I>-ZV)ICqlEƊ+%Y-1,ǰcz"e0," ԩrKuԴ;EPmhת-<6ZOk }& ˎ`^2H+4/_9/^/6raυS6A c, Ҏ0pa|}]dƤJL(c1&ùBb&%0MU,~Z1gb|n5L[L$%u%CI.-WFjk1ĵ6P:r-ZB'tAŐb"i.Ba" Y<?><=ro4߸'WnNzv3%!by1LE 1bV.;x<o߷|+/N碸ߝ8 c1L,Fiwb!1ĘK"B2 ÄdҌ1bUoUѓ{7[2KVWijɛ1,ol`g[y{Fm';ʷs;Q7G4v=k^HTH"mm <~4;C@9BK3r9ˑ S6!9&3{uELM&8saw }}p}=!c1L>'D-ǐc|Z1|BƐvc B.&{{rrnrIp a8Ƈᬿ?Y\e:(nL$7cH1,]gFx Yx_1sc|kBb   KtniDƧŨ҆i^&2L|=|`MQ%+O,G#㳏~$C@I_Z=D*dT. U:DATd|R} I&^A%CaJ+Z:! (5l֤ZylT D&JT mb_V&[OoI>~$'! FeT.2d]J`q᭴M@Ydd!ADd|Q-A C$xEKJe{=ij( Wj0TWT5&ngˆSZ@, 㳀EQʋ DEǩG&AG-T\TOW>*zD<""y"CB!0J-ݘ>(2>h !B EI!CD $8_=m"C6D(2P$.cDyYˆ,#<=ƢLdI&>"&d LL(ͤ1]zcQ$Y<D.[EUd}TQ(b,2F#c=[EȒ[dnRb,G#B" hY2,8!*Z$,GGWiYQB$!HPJ?>(8X,G VjIGkݘ />ho1"Kr$ jSa}ӳ茖/:Vb-Ɔ$ @r9"d O>V!$KAHK/"툼 ^%/ee߱©k#;]]S!Gx1md}ҒxՍ_ʫ&KleM!Fxc@ɒd$i e'KN;)cS8֧mPt$Rvp|8`&ܧ%QIN 냿k2,ӌ  7AP AݑEo'K̓e2O'KO;-_(fQ}>Y$ }ڽW8^~v,BQ(˄gv?ݹ[7a%SLцj(PY,S xoғ-TD*d15dTYQ|~O֮UR/rf{6u>Xg`k R+vamZd-˔6ˢe޲L{k ,[-듷B!XO ,]|1ye}V[bYB,mO e²L ɭ4,K Xk٘ɩ,d,QF=\j;^o@) ,PC-y(N9 ,O?Vƺoz೻՟=~t,N:Qq5Rc",N ;.cz~UQst;>Yb} ҋ#O^쟞w޼?9tsh700e}PT}*(|_yDwIp)å"U>hMvrOGg7clhNY2,ӜR[-{rRY&Jw%ϟ&k9zυAZeI>*RP>#VH+$潅=BHVY"T d,SN-}4 +iAgʒ3e}TpF]y2J˖ћMY7 8?PI>=jٜ| *si~T\wbٽ ]Eʒe}:Բlr<6\%.aFD.e\?i&Dd} į&KVYMv5~ȎEɒdYRWR,J)*q.߃Hr8(i}ĊD,ٍK%KĒeKa%a2U,q1 X,K%K%K,- O #d Pl@iwc;9,J (Rƻ՝6wqYE[ɒdٶ*AI,qJvENd}:,HG#ۃu3H"58G%M!~d ?>h  39El4,EkpN/3P %CȒZdj+~Oڎ gd3>hYO?!|d >l%4Be#dQI$Y+?=,T,)K֧,E1LL&3 0"d i>iR+ &KeLj22fyq,NJe*M0|iMSSJSĜ,NXRtRrҍ:N):N)9N)q] bH=D=>i 7b,y7"LmJ mJhj׻ ˘QPBPm*TЁmlg#hץ^ףt^<-kKS"l>E;)%;)eIifdRJdR#E[jGRJRTXҶ{?ąHRĕ}>TA\xd.>u)'422>)̮uIGRdRbHLPWJ}RU\Qǭrᫎ{kNZIf#MQXJIXJ󭥊k{˷o^u~:8F̏mlm3s#Iul b%)esNPe԰κZ[-bP)aPSudh`|TJ|TvRA)Q)Q)[=|/E *% *ebPK_sJsJS2v> oJxu m}$]jt1]c픒2m'V@t&Na_=vgl6wmTJT#m}"u"/ gH}aGlHHDHLB_%G:j%[F"R&9Ŋ}#~tUna*Q԰}r)"T)!T)R;J?ܘ&{GqpSIbwBm*%m*ekSKNJJLV*t9v=sfO;jZR4 G+u2qJXk RRR&O% 4FM9S)әb^\QG@HTD+i"R]^eRR&*߭ 0xG>QJIJʆO"2U,j>>RRRIRRR^=/i)O)O|Z_}|ע;TJT%yî.w9;E*%*C SJSd8;BՋ:6|Y ͟tp9p+>{ L)L)dZu[uQ]JI]Jg#JOTRRR)vH#D#LS॒H!D!L S䰕H!D!> vkq.Aeq+ޫGt~k5$$> )0LjULy7QλWաr%q\kV9LQu.Txq UJ}Rg3AĘR˜R&Ƥ C \"SJ"S$XSJXSÚ /틣e|ђ>tJ}S`ﯲ*vBB>b;RR8XlTJTvRR>*F\m4Fo|g]LΖh(u`RR@t6+l "@z+%z+[K{kkRRRw׹IHIjRfF0+%0+YjRRU,\\>`H/U)U)Zwci ᩔ੔ ORºGEl*%l*ebSbEx*%x*eS772)NFzJ J}T mTFTĠXq#:R9Rϑ Z#'ǧ߾1!GG19*yl͏fOeOe>}>~{'/;'ot5BcC4}?#WWӼR|0l 3C+#+9Xq! @222lwׄ>{X=*0`K| ̇w}^^O†322ŭ˸#Fq]2-eq͎cghedeLLpRcQck ٰذdž5ƍWFW@G3d2b2&ӵJ'p5|G322^ߔCٳldk.ۮ'a m9qpkP1ȝ1-V;LRc"*Wa62!\\રgReRe>*ATTj>u1(5\yXջ7?G5*#5*cQ0\:,6SW{oʘ885ʣ1(Qїȗʘ3l$Gm-ED)B5SFSƴګ }$01(FycEit2r2#glO >4VVƦѳȳʘ^{3dvZ} !@|+#|+c[6" Z^Y^oWFW擼.ܡ1u/z/5 _OwJE^PHʘbMz+CU˓hŲ2$2"2&%5B5@+#+c]u2*[)[S[O1ݬK,͘C |8n392I22HUՁp=C>ofHceDceL |`(ae$aeL k4SY!㬩Ԙ"9ZBa"D+!Zv"Ѫ8^.﨡1U-F쩭VPHʘZ$g8\SRO I.-,g2)=V{ķ2]jWFWƥ6(WF(WDV)5lJ WSbebe,BʈXdW^tA4 [[تzTTS661)UQ_I[\lL|$GeL9jCSFSƤ=d=e=e>i /f-2{@5tQ^ʈʘj>Ch ڈTFT$aFxqD\*#\*RvPH"S_ {E᫷HK, md}@B}6Vmo|^xuFߪMU[Cq6Vmnnbe6rVm>jdUȪZ_fh#L&탩֔Nɝj3)NfpҸ$mS}>$md]}՗|UZ$mDڄP}U`5 %ORTT੶ (N9kT6skSFlMTMqbtzKIǷ^VޝKP j3*&6U6SDsU6^QCMUgP-aVh^OBjvȫj3*5^Qn7M|IX&FMU)^IP;jaX$d}BV8sDMJm'=^'kj|esmqSʝȣ#&nƨ/Ij3%.Fs6[mrm6Zm}Zm$DjVch jo32Ymbڑ,IZ0vDe}T`VzpEVի6WmzfGAiBMzUWTąU/Vm4dR}&¥L\u1~ mER>0!bLvP?V`$FUQ nl _*uyl8Зj/RE~Z6de%.5f\>?JZji}6Pm)yOm$[_Z>̬:}|CuTi2Sd6SfJvs=+JEC%MJR$] O\6Hm|]G MRi!.*`thFx&< "&(̈́X1(/}PP?j~fGn)K_$n9P>q˿v=WǗzR:ˈNR6IbN~:8:8>|9_} Jm,_i7cC { bNmœ>̩+p9nu>K[D"TDO ye;n`{|.ڙy#n-3@TGIh-.ݚLǷӲ&(OpeiĞqTmG\\ZFm6TmMv ;? {mn 6Wm&q% ;Y BU)\ /}FתMUZ-)w6vWwWGi<<|61Zm.ـJdyūZد `Dц,?fY+HjD_Xj~fW@MdUnsd?ւp5ڙ>w%&Kɒ*_1iNM!OIje+cwt$Q}nώ{nMH\ԦڤMڔSSˏ,6ӢZҤT̩Ϝ |=z]M8{׽̟gOtq7o? oOfmon,JYVZW-i5>iw$_Go׽ѹ1JʊE wn>A˖|8}E/?ϟ_li;x69.eVOޝuW\ߢL?U+ŋ?MO8 _q?qBY߿=q8?/T3{$3NT.wECCSS^N||7k]gtP7d֖m+[ɭqծrtK?8)V8 &qCqw7{a:u "7LIx|/ݞG;`?OjlG:G7&&y2HWIR=58tN^=+k,ԽH-*_;Mλ,xJyA \Gb6>z`᫻{^Ƈtcoϓ箙\y YQ~U-mRPxg+h"UW*b?O>'.{]?T$L#|vSA >B~ϊ?~*+dR|Z/APQ!EJZQ 򒺃 ه -_\נB!V*4$y@O<]r߽8 a}~wK ^(l /' @|tDP; C&9/99$Im 鉤B 3SDŽVsM~H^$'i}궶m JKC"Fs/f1[f4]ٗz,~j@$!p-◯|:VBpH6DIe2XӐt$B 0 <.Io1TPe\%}<"B 0Db*mEI  (poLDz * 0 AtVi!C$jfk6@dKLLKI"ha xz;bdF٦:xn+h-]Ջfo57ݏ9t?Fݞp]Tf}9+egs1^Nә(xX~ČVzɻãΛ_?+!u$31wz&4x@t=rC#I ). @Qh>@#!$_E#Z #@UR6-"{U0+~3׼ٔ,& CeDIQ]~##E7Pt--:$Pb|A[WtHKRh]Jiho>ϛخƈMT |`AJ\ܑx-ױ`\F$[ ||A 23_$[}UwIR >n0.v!J25?Ĩ 25V/LCL3LU:R_Rg'p2JG2w9'vqۆ;ԇ||R_HQQ6L?-¥yrf:޾;K$Dg2'"d qW-hv뤵IRg2nvP|n/Jc>\=/I+JbWO4v.:rrFOӮ.n8EWV{̨))└SZRp8jם;/߼~' ?G2K%僖7YX7lR߱u8 _x1⤘$ޅ͖q-2֓"IeuY^АRH)"7u (EH)Qq"vqR8)䴬~9]TR$*)Xo{)&EaLˆa]T1N3(2_Ҧnp,,  D;)4d>r̭hz-̂<̂{< ~Ut(EXiQڴ?L)RipAtJ:|T)ES*N_y)ޜT-tB@~UgW- H> oȷR>*>`* мR^)z'cK˽Po fxgp"J5"_v\гRZFEzݦۜzrV`+ŖTV>;Wn%񓌹+Ʈ$THJU`(oR>+8JAsfT2ɒd1*^t`V%K1Q,IѫǦ|q3,AX)aq /mQR^){|덞"J5*Z{YJniL GqRLq% 5]8Vg~s|&BJx⪊$+QJeRljmTd*ťV رИRL)2%ZMp1n@qޔ"pJĩXRL_ *Eb+TMBdJ2|7f&Zu-6?-Φ;­GVimClBIS{i Y'EbNp3v>wbT՚/I[$ M'EbNe[ϬV3)g=[1)☔cjCtI/'s$eT"I&Z.Ƌ[аͽ u(E0V!HR>"+R|RL Sx0XL,CoR])wl| k 3)TʇR^rF- 5QO*ZgۋY.)lI_wI`C*ER揇v^J?=1NtUw]xnݰ;"J̫//kOZ|-b<;*X,shv~88Lʤ?3;h!D)Yd #C_$igA,Xnѽ:PՊPq7;)l)HpCZ2쎐"Y tRj)ź^BZ4UF)QRD[)mx'T#D Lshx$~sOݯ*^QҤWi^P \֬8$l$4TT*TV)j4qSMឧX e&kT4)SڧĻq> 'HFi&e_%+k4yO=P/,`h=izj:u>)b1O@ir4ӁbD1?UןN;'?=:9Y4PiCq#)Oc YpыEzQ(M^yQasc>*MTlxQhOi4Ӟ*DRi&Izѥ}=44SgNb$JJ3))lit49RHPdF1J7*Fi4Q'FS.&J\(ђ}3?w{+F>5OŋF5Q(UΓ&IsJxwfɟ-IpLSt|{>M}1?Oq1ɤ&@OĤIb>i f5Җ4W[\)(tptR.ߑSj4K/ -g%MڒiK?!i4CjAVz.ٞr6+ bI$ĒB{i4I &I eI@LStig#MґIGkǘ,I3LH{/Lu _.> k1r;1 C[6E mR)iˊ H$$85Lir4=Z1KHi4:ns|<·&HԣXԌ4iFq.m6il$eҷT4)Gڧ^c""Hu#3>f7~=\3 i&@K:X&yH!a `aA`"H3m"NclfG ]ـ\&H[ 4Diq{ggOXiV?AE i‹/Z6 ?ݗfO4I'- #[sMew1j~5J%$jz[Zd1iŤ8!1e&M2fLqFhti4i&$A"Ԥ j4BM&̈́8֒a8c9iҜ9M>GIߤ~FIߤC~FIߤ77i4M7iٛ:<:ur5`'IlDo G@ R(MTQQ_Zc&J844Nk81w$rN8'VCiDI78id41N8sh&Mj6ouͣI}\4&&MN9MR5JP%CY(iO< nßgSN=<.sAl>(i4S uG#>h O -"4  p;.Ɖ/&&34SZt&GG|F쏎,>tI);hAz  MPfBAjǶ?BzDi$.c$L>Wɖ;S:n2&#ı|O=<:<2(O/Ƴ35lamP2$ $,9J^Zve7Tv4,KnHnX RP 뒘7ĝ,-zEOy]Fї܏ۑaLWY0d72o3,;9 إ+^xU _ Jt%ӕAtdjeFbqO?1Hp$đ{ԛAȐpdje7%OpI`Ď aGCwp i$QɠdH@2LwEˡAH^сx PҙU-CE9ɐdr/ԲP̢w_'WU>U iXG[2>oiyX޷"F׈vhfw;7 W"do2>)ʭdY~iGd"OBa%09&WEnT&C\arME "de2>i )yK-!YD\u;9ctrC:2 dj֒!k0%^[7w:ʋ_ M7onRs JM&㓚qK M75߄dW2>_iU Hx$8NSmk-κ\RdHF2L)v3H""D^ ߈8f; Lby0^! H`$パP1zd|QEI/RI$äXW߉;E߸vxֿuoFoVoi!4}I?oA#CFsދG?u}x<-(~x觃Y⛉#3B^ļL9H_z71dҗw73@K1&mu9ba4)drӗw'AO$s29ĺ q'C!NҢKt2D:-Gd|d:k25H^sx1/t_k/8[< @d"O٠?vt*ugi!e6!01H0!n0d؈̘x2LUƃjK;by èKO?1(CabP~/|ē! LL&4Xsv(3##7Gɐd|6,>ba ~P#~aiq-H8/$N:t yas1] t,h?O'taO( ov2d;픶5ANnEyQ/~yΆ1'8'Sݏ~|_gݶS8kOzjlb3NT'T7skvۋy暻 S0SV}' eȂ2L yYlH,JmE7ʐe|nTz3\7;:h7DR&Ir3!G!fq:_w7{2ϔus!Q(Ò/ɯ?'ɻ8{6)CRQZrf ډgO|zC/bȦ2L[{d袜V܈kq6PnETen pFQ+CV䒭kUkS=j1"~/! H/yn8)PeH2>j-U;9=Z]Eʐge|!W0]uubWV [Iob0.Q e2>+J R ג1ɇ[&b!/CA^P:X Aːef~r b]:ٌ!l2h~2N й\!2}&@h\I/C^1ݨao~r 4h(G!f3L} \ m 3> ,-_k)F܅ 1CԘDI ɑ1sb<-ExA;̐fvi);?33L[,8bčY&7~?W*y PFnw,[d,cɎ"!-*c1So"-*a0TXNƼz0Krad0"f , ,BPQQ׳Z,A\qyyR7J2;JK%ߣ70,X4,[il^-[gDpY1xk+-].cBW\rzyYīF-Ke[U-Kܖq[_]D%2.-q0%e6T6c&&nHwY Dɯ%D[:L%o7\\.sqHE7˒e}n䐒OKV:?ȇQBne}RUP7w2E}˒e}Kݣ ? e>+f (S>Nsڹ|{&=,to78L$}Y1h0of/ *wZwNfQ"FI0$%2I0ޕ-QYDe}W3Z,4eeLˬ\=Ӳwؖ%l5< ʲL*4zV`H^Y",<)ճ~__EA,KxYkx0Mekd(+KWQ-O.5n2#e вL@KVBW7[$,ZGh5#e Ͳ>4k'oG/s8c+^xhIM,\a8z%6IxՐ.F{~w5/Kė_{Hu̝?yO%/('l}$bP1C;t,^t:m ;1dŐ,li;$Yl82bp3hѐBĎf֧?Ώ4b(4ѣrfI9 (gv:'7tͥhzC,h_$,hG?ip؉" ,/wf;>\?yZnJёgNi'~0" vW>_+Q 5Kܚqk򇞚%O<ߔ=Y&ziKBerdM(11KebbLV,b)f] |ΗVg0Kvea{|;0eW툰.Kf]ͷrYB $\"@˒ejj4!e߲>~k IY'es+3bX0,ð+ߡc7t>i}(I?T!n0鑏eY>{%E6h=3݋}Wd eIղ>US XZLskY6)9&]f bt7CDMje]t9fkwih,E/%/ex.Bp"v)M,60=j]İݽܽ5>z$)~JZ jz)izOB98yi$1o6z><9^sgD wn):y)9y)S;ʾyA.NOKKje[3%$'٭Ba#.eu:;s<=ǁ9SRR&f k!}JKK}|R/c;Qv]>/"/$bR.)Y1RRRJ+ZRRҍy)y)yi$9oْS(yzi$Oϕ'éѝ-"m`KKz.̵ c^n m|oK K}\`TMՇN&_:vCȝK}\%;tݫ,YRΥΥ>u.B0x!>K}\Bu!B, `/T @m_O ;6!E-%-lݤHD>R-4ǛyA;-%;-i_qQɳȳt-Y;d)d)&ަ221 `K#bV5|t|}!6HXJX3ǚA V7%IXY63>E,%,doQRM4HD>45&RRR7&i' yݝ_hjM_>Ki)K )KYHݶ#xxFϢj?KYYǖfm|1V`%-] @pn€ZHQSKISKY:$R"Rz_V;_Bofqҵk4R2R ZJ Zњ耠ff4: ;=aѸ lc_iy\O}DRBҵ f}RRLEA2K2Kj?BtNYVMYʐˈʈMfm84(勄O32B_5P4C+#+kEo#>tκƪ>#:AiC )ƘR\bT wLƕa\ŵ΁w?CjcXm |PH֪Qyɶ{unOx=پ}g1 l VypKymVFVƢb]hYWFWրŵv4C+#+k#aҠ[(#aGa=wpiQ@;,#;,cawI#,c1e_`k22nŶ5#- hm U߀?7Bw+v2~Ht[Ƣ۾]72ee>n~W.7}jm\F\_;EBB@Fr.#r.s**2F\\F\"Bۙg ~Ƞ|]džZOz[V5JdcgFW/#W/zLSee>0o rOS22Y0̇w Lf ◑◱_IF(0#(0AbT3R3"y/Ff0#f0c1uYU 'Vys'e!f!f> qcT^۱Dbs Kd1f,ع"1cckQi//M4 ؗ#2cɔg~7U\?vtWլV"$v@Ӏ>fF>f13Ml~D ]̌\̌bWq,|fcϧݳN٨zW8˛ѽ̟g'7dAV)z忋meLVZJ[_%H1z?iw$_Go׽M5?nQi]?Au}qޝoYK=SDq-1ELyqfp2AKlڟhImѩZw_bV}vqI-]j[ęgq9?kGA~/LY8YC,Y8s#y5MΟMv{?%?$ARtR<|!bXg8cMdtLz@ Ik礛pYYyd1@=g}Aàh;Eӫ49/2Y\7˃a/꾬v_6y\_RW`^8#^ݱ䪻sd7=4Ό¼ۣk֋r^W8Ӓ>':ʋ5hF>ǽ·b+|I׳_Zu9}׃o:ozU4 rmXHi:X@BM = w7:o~zsZb@Vy\|dW7ڀ,~xj H ǭ[h=@ ΀($MZu(\jO+Y YTt4XIgљz|2PjS;'EG W"{!f'k<$\N+ܙgVq+ب|9y3/yFu^tpry}p_wi-;@u^GEe,K+Y c М֙3=/fk K)S"3iF<؜y9JY]ҭ۽dgPHK˙G^:eX73E ٔV3KYK } .<.!䴀ܷ~|iL'8%ո֖3P_һ#44iw3'ʸpmm?\pU-{o}TA£ۑoEۋy: s3?/vX7_k~_? ;dӒltEǥҴRzǝ"fZ[#P0n1.Wʾ˂1Hʇ;GGҺˆi0uAb_Z357-NPFOAyfdCchIM?-RYryOkBNd'~-^Y׵r%C|-_>gokwYp"+6?̷|1h.VGK҂( !R }˥b [l˛%b*Hk^g jV]&D΋TT) ws%mʲ]u7\6Q%͎+ZO םS$*K FNǹɕ3x6ISm_bt6hJWW#7ѩ|nu>xb6;/;,jO_ m:Buv}Y]х6_$T:w&3#i-,d0::a֡bx$5H:3.Vrllkej:uL=2'_$PӾ:ʇI9ҾcFK"W/\cUka|~AT:XXC+:Z:ZX(ܟO@+1Cg[ "srUѤ/ ~VtqClA3>+:Y}Px@  OhVtBb{:_YWg=}:Y`Fqc{|G "*.7sGlb @-r8sgs6Lfqkw`L,T ܻa7|7PP-h8slvZpYo }pe6 6[u_1蛹}od+B6$ŤlD/Ȱb0`BtQ\?z[pj/T(hU1iÄ:"FmTȌ"eFEbffgq?ͯݠ~QN.hM)~d nnQT+FJF%~vщQ(B >؟_%[(bbTsNywRq|?=9Ҭz">F; oAv QLE.Kᣘ <`bBG|OX[Eqo@ !urè@l փMσE=i}cKX뾇ȥPRD @]J2,Sv/J:&"ZH1mUJ*.!vRuBd!:QY"H|xn}>L?m= k-Ymk2@ :_WY7PEO' ?Y:Ώ"GqopeL.M/? M9$Aqi も2߳cX8d~r bT()"j>y"HQLwIkGuI:Gctu!AG(Pc@0|PO ?+^U<| A 4G,{+"hkqR )($8n'8̵):$x=bC!ㆂ .}CHA|Zcx!Y&[7~f~E"SgȪNv yDHy|Ѳ)܃Mbn&?I8qYW޽98:F"QI1I%A4-bK%%6,)XjE?I| )ǧʱtG̈́,"I1a&%5{x;B&T1N8I" ˘lR\Qtqe^8s~JF2MRL5ˬPN;q<5{L]pbBkI|RpteϩNQt8 &EfbM Fӫ[FU0;⤘4 :2% uR>iIhF(L(aQEIŒ(EPbJQ0"rO'8EfEtN';jL4)dvZ7n!Y\l2ׇŻ%N㡛Tҡ݅cts?'PМ~zUo^o';tS{= q@OJ(T;b;۳Y*8D[R҄PiB|Q)MfR Z”&aJ3)AQj4TzFJ@~8ѡPiCKV[=&J3!+Xru{+MڕiW˞CV.i4AZ i:>֖&kK3BG5b\0.øD @Kԥ}PײA0t/Mܗq_KUvt0MRfJaW)b$ qŽbki^>Uj2;:uZ&eO~&n<}. 1McP+\\Di&fƵv4x՝tn?K ][$oi&$?A&N4T4bD,i.46 1MfbM4a0$,F-ti}@c'2L30F;YX0c@Kख़wtc- 5^p/Ž2ަ)rnD }f74Id'Ncv#>Lu0@K}x7$ Q G?{`;05uH~i"4;{1$Z# Lk2O ť.M%*i@K}W5.M|]n9ĿU< o)_/dxPsO;U/r贳AB4Baꐴ|/Mf"^xo "LKd}i=)'8I> ,0i0MDakhZ1쥙2.y[U(E/Mf^$}P$si&SKLuvodai(?sF%/P>Y,#g" Lv#>K\eoy'%,MtY`w-79I=,M@YpottPfK4 BZz4QVGY5vЧSiOż:5meD ^ LJrd([iOEDW+%^ixJt+֭Zm.M^u/_VPNL˯k)п_iʼn}Y4WI_I6wO-@K2$ąX& e2>#+RmAIːeԠeH2>= e2>.k(Xq)ȭYZ 5 umAe|Wp87gAe|`ײ`ZGմ ]wE4ejcTt/iKK%A~eN2%m19?;wCi~C;G.C@a]*UdT )^xI"GBːe$.Cza]K[" Y]fVAːeVN8$|Ae8jWG [-ӷ"ϗ,R.o0e|d^̣q&Ż Zp-ĵVs e,C2Xn UWqԠCeȡ2>J2S۟ޟ+}Puj4 XRK%.')0_e||3%3ԁ3*Ca"Tf 6Fr ӍZbS\VԜA&e|L9LkDA&żɖzI޼8a$(= SzERt㟾Swݏ ҄/~"}P2$K,ſՔ?&77tLBa&W0])ξ |X鱴Cgh{& JE"Õ㯠v!CАBC+Pa!CȐ"CezS2|A^d|Ҳt&&ا AM5E c Ld&Ô$/Dd`d Lf,Ad,]<"R3:ե#H?TdAl2d6٤tiv5M8B q^@jaM2fs^ZC]K1J4y4,&E m(!VX%7>]ȅp2{G?$7BDKTaRMA8 p@ !TK AO&$-fXL$ ڟj*^` Mg7-vu3~gG&Q!e2LTޚ5HI a'60٨ .n0ke|!̰;&dWzk;0Ϡd~2>),4gD)(ģDQ5zRA>e|(0sIj뺷UnS Nu#^T R"[~ݛ& ae2>j0Z^AG4 Xib )t 9W\  re2\jK6HUjkcOY&?eV?i'Y,IU)U1t g^Yz;Y,Z-9 "eŲLk ,KeXKZ,VXmcT(+_p"Re >J"3VYR,Sbnk,<<%2y*J78Jr LtJf(_1`%w2ݩ+<̑OY?{y`*냩 A~Ede+-2V+cOhVY2,׬b\u*K:ݨNeQSYNBODCDe}UW )K$OhPY2l$jrշvmVYrϭZVۏϠKߴQYD%, ]ʢ5eɚ>k*rclOYҧ,W #|EʒOe>#tR`%oб[Y,׷b,%Ds&*R)U:(KreQ(ֱBYROZ'˵8Un3K-P (5 k]K-zQ(qo#e >,*7$, R'H.c$2([ZxmQHY"l7ɍSYgpMF]e}tUXɥ)*KDQI I)ce٣El6e-{4QœK+X hֲdkY%ȘK-Y,ղjYzu-KZ'{d,Yf1/DU˒e#JB@D,!\pEp}.K>\aͮ|! \X.e6bN'r5\,['n5ࢫeղ>WkYS0InYb,Za6d! ZX-eh -D]˒e}V( qY.%-KЖB[1-DFe+hHbY"ZCÍY`+˄8W2|VD+VH6%nYYZZ/ԧ,S֧OZ~_sEuʒ:e}Z!o%[2mv0W?4,RgJ KH֬Emʒ6e#iSBa"H2!)5Hf~ wEʒe:g4F+, 0?\+@ e0$B0(KLe2Qzou/"wo`,SOE*R v;[Y.nոOhDUYU|)KҔJSuOrѽKN(Z3ؔ%lrG1Gk}DLY.1%E ntАdHY!D PD(P1d+/*~{1^N ⪾:ywT]L9Y&"#?^ {L)4XY!a%PMLDD>'"2YknRיUxSRRx7` o RRRSԐRҐR0 /o((>@i)jJ)iJ)SSo_Ow|t;\O.KAlr3EM)%M)ejJ+['WS$R"R&J҆ԇ(5dT5Ί($>QxwGus>Nfӿ?O 8R$R"R$:~eF^HsG)G)>QQJQS%y.WZܜa.$)eH8C< HJ7 $$$L `kn1lh47]7u"W"(PF e8\c7ER?<:ّ(G1 Tmܫ~MpSJpSꃛbk7GH9D9LIdݹl=֝ӿƹlŧħ)>qv!wJwJ}S-+jM)iM)Sk͚8̵Uvq!n&.qӛ_4fdR&x5W12'Au~eLOSefRҍN)N)N)woZV?w;tJtJ}.'&IRv9!7J7Pdq,HRSp1 u $@޻?roؤ RR}#'D 29UJx=?ev]uqvRJvRʴVJdzzԣ RRh&4cT=6b2U$qpR[4^ْpIJQSb`#Q(Bkm10 X r[^OLnί{lyPJPʴԎ}XKOJOʤ~V+D )Y>))fKI I/P0"2 uY{):B)9B)DKJJ}P7JҦԇ 5"00-^E R$R"RA${)ZD)YD".ތ==`0-E(%(Dkz !JZoM"J"J}̈&J}4n7)R8gRR$q3/u.GgYǜJJP$ 2UJXIrqe|{'-?)?)rw䶌SC4GPDBSq@jC'%'e= kZKRbn#I'eJ:GU*5'V# /(yxpu @'%'& hTkffkl3}BvRw&Ab^%'=Aq V}KÍXa>''e?_QIII& 6=i|NO&NOFOƴ|B!ړړО5n2T8i2t%C'#'dxZk1G[222Ǟ>ޛf  RBj>i>OYCBRkx^||=Sn(C'#'c?8~KMXM1nQ:I@@F] ]\}]22ޮ??Y$GZ(~d>g_OFO|"(w5m&D"}2&dឌDa2~2~2|B'#'c=+{{_C! ! F'#'gϐɈɘ'^1Ϟ!q&sY<{POFPOz"q=C'#'>QH;ddi0[ʓ1QqŋM:3t}2r}2 }*q3$}2"}2&NˢT!H ##K($2"HPʘF(#(c@+\|1s5nLd`$~2"~Hďoe ee> hؠi RBꭄL̟kR>Q>a]Õ''ّɈɘ'NF|>Pptʈ| G22 @@Zo=<vc#'Y>kcG'#')gyLrِYYIny)zP&Zi8Iɘr/N)C?'nmɘ.㴚w1,0"@!@2!1NѥMZ?Y?F i{V%_+oWӬV XW%A@-o~p7C2(#2(cAP??Sm9]f.qOFO~b$'cjOFOuxVo}Pl\jz=?|h<}^ގe|?sUM?ϟz|]LU|2k;6&mg_)J[kiJZ|s;-h_Go׽M?nQi]?A-?}W/^u<V~;_^KWI^Oޝ̵E_4;L?U+ŋ?MO8-_q?q,`Uկ/gor\>u}qޝoYK=SDq+D~W41g p2AKlڟhym9_mbV}]lM-aEu04 ʿpBu3:OeeݱӭL&/G7˫iux)$"UXYWH#DXE?.(R"VA U>JD*H%娼{Q(njgaa! $H" [|< \( %yM{QPI!㡤%=; uDO_nؓ '|EBtS$Ir&|rwP|+0gԙ&|(97n\@&\F>G'N|+K>s񺁘YJ>JIpKH~xq\*I\n{'9$$}~xyO;N!Ӄ7~<8>>x9.">JII?oD$"BN;'^<89٪'Ҧ 2yI>.I3%㐕>GqH.EW_7DܐObRT!R|kPbo`K;ݏºyB8!qA!|'BO8!&|!"Au]mu5;u*@n }!"B1 E2$|2PXiHgc28YF~6lIZ*M<(fG5~jE[閛܏<>'ޯⱝ=u뾝M9 6NsE}=g"GGXk6>&rF(; 5>I\K08z/n~d_|Ф!'^|&-HȐ dXr+foyw'7ч~/Oä]|::~4tXz71UX܈z9RboM]o?54=KvTaN#tny꾗j3(ٻTry\Or;-LZ{]#ąyMLm6VX"NeKY~}Š{9:ãN$w.{2Œ^$nPN@Hzx݌oF[a{g*j<{{{NZa-+߯>t"wz썴qOwmr[D=޹YsCOpeo;LTX'kg?g֤EXj6GW]Wx]dɠh_>^vtiy kZ#ԋ_p|prۣѻ7?KT5g6G{ϝ7aos/XN&uelsjmg ׵FPغQR*-oAxwq䪫tr>*uw0oW wgE(ۉHZswtpbjm5gr E6.c\4^dޤ.]P ^Sw G[׽ykW\Dqmα6ू|ptsI듙Viu̺;M:M҅Bk <\]E>=q՛_zqM/hpރvQ'&HUu+;ZݥI M&-x7bCqwY閏]@]X".vޝAMl=,R·A]X"bYNg:^uz7[1"L6ou߲}MoP'61溬twzeưW(Fsv4t|K6Duڿܙ/.f6l0:+w3\NeMK޼? lRcwJ_%NZRސyyS8l = @H{Xk(>b~D\=z^T>NNڶrd_Gs7X4MmRK36㛭F\|˨\~#A^X"B6nVVYG;aLp]U ۲ /ގ2زp{6'M/ːSyիãB Dvw{0{U\✱ yNȻ]"Xc2nwybh؅d[']^2_#9 xwJv!։vhwY N:E]-xEy~ ^݋7tzCvGj8w뜾Y;/V ~N{Ϗ2ك\W=^.ot݃TWMwItN{$|قN>ۃdW'ýA۫Z)O,ꄸVlͽ:omr $:EK|dni=ͼx+~t5|JU|wgYc)kKƷI\?.ɠK7O6 _ Aw8["GAM!GMB5HwUv+o/fjC35( a,A\H35h9p5Bp& aDpTTLkO#SV/JDͯCK2AݭWao0kxJ*}rQ9@Ĺu}jmZҊU AR6i -vR3;MR<'/Jw@pe &Z.teQJ72[DB"rLU#/x{9<:J+a gq,(ގG\!BC|ꇰ I S*!DU|(R>T$tU,1Y+DB)!DŽ,=o5W@D |(ڊKZa%"g,[ ږ/m[fW޽98:7fATMsH(DY>DT(-&DΈ"hDEFDFԈY#aWv}WF"K0"|Q`D0Q!1QQuGI3 E#Kt E$d!q=O}oI$ǓA"ł$hyEhb5h(j48h%Qg34!7țQ>pfYeNCKi['.nEr5k?IQ>z&C-qI.b4 Fi)1F wrxj//7?`6c?UYŀBFg|MK_rLt$(uK"F1!%wMrEGFpč䢻Aٺc$F׽hx_ف:tEJb29yԃ-oE~O0>?(pi~!R7QFvQLS5Na±s[rnncb$F$63Sgֲ!EQ2%nTcc-eTb11Ęvc($b1ʇKXΗASF*LP#H(e 2h f6jY5*V#-&4j EuIٝw`ݲ~ʙV o;ͬPXcps- Mf7J#j >ԦQ$hDWvraC^;;[|ċFxF<}.3sNf2\NR|8MwXoğ\}ONfA4_U|𖵨AOh'FЛ.@@|zp| ,x6<:D,O(hޚl-d>%rQv@zyMV@I>',ZmzW7iRO Z# m,D "3'Hi2X4YGz֑FHu}Q6líSLzZǐ}RvtNA3[\FIĤS^q8i㵿jőu>)(>RO'oPkҤ5iWuX&I&Yŷ0M7vc^%I(u̝iԖ4iKڧ-X>6']i@6 g>ih&MRIMԤc"IG2BI¤ w΃͜u'y߫"^M&͔8Qݾj0Bwtk4AN 9rHW)_g?V-44Pz&FJ &wb 6&Jl(a0 BQ(탢 `%JtX~d?HSܿܙ/gQzx6Kr2ݾ[ٖF( r5kIE\)M)R&R&)J3UJX]5_~zs>BF&ILMśW:pFLJ&'FHJ$}T 3Ph(ͤXu~|(MZfjQN;gGCUJ*}THCvj4S)O^piw )MRZԡ4PڧC-խ:5pتGK\aQ$Ki,ލ2ZEeJ2+,R)_&J5TXL;id#)K*.Dh44PgBWw1ˠfW2JfjRp=Ny*a/˰k+/OTW~ s0AR IFċvw{P{Ul6QGK)wtԔ&jJs)~)MSa%&4yT:G%-fcҥ4SR˷VZr ௾ nяb>'J3U*NC%w'{6ETiROjmQu#5G J3+I}r4ӹ] a&J54HoI>eJZ|ψ Y&tJ3ѩUFT&qJĩ3u u*M:fTWzWzZ *M(@Pi&@ź'chEĴI&K<,s>ޕ&J3+I wjpO:tʟ{4N_c7z?gnovxix'Ey^_;'Mǹ.o9λӼM[Z?kg7naMmDS^6=p2AKlڟdYmۑը_bT}vqtI_MrnqDE<&&~/E)AdC \x1gtirtyܿ&[OáݞG;`?${;PwtMdtQP=58tN^=++,i8E1,^uy䢨?,'_{Qe凾EݳW|@b/؛{8ΨmHA|<vܬ[Xp xݍbh9Gr.:Oy"1Pމx\>nz2WLǷӤYt:&LDpfuݳG&$- hg3M>m'axp{]M.V g%bn(e]ŋdk:)W7M?t&#:x.@QxuhL K^Η`IHg"'˽BjfKĔ ߺOn-yAuNh9<~6CL|haN8߻"o|_;o=}{tr;]pD)"|YVhUm'4 ZIF>(R/_{c-m!IAQF>him4e2bJF+WxQ2"5~Q%"g# #3Dʐe^>v.rIg_wCa~Fv!:{\~F"?R~;ʤ\i!8Ċ=ii!/So xׯkWYXxVe㿣-9B?HĄ!˃˪v"ӎ?\g7`':;Hvڑ"A[إCTL!)|݅HOeT2:Rm# y@LdܮG}IbB@*$^R~ªL)=>;L|UUvr{`5}KdzH(փVQ=L*n]Oܜ iy0dEģ cn{z&~ C[6Bz%w4_a!ubL#+A ó:Mμw|N˗!WävX K<{!AcTHj͉4.AQ@#[o9п:??[&Sw}߬GjKR4ϸkj7ӯxNE/jσb4Nތz=M촒tFdr{Njt~{]۲XDUI.41.R;fj1򤈶 ֍/e\ BqkrcDҝM O!m  {Aۀ[7gL4QۉƵO^u~@p-{?PO/g*Oƶk~Gǟd;z_9|%=y㓞x]YoZJXK1V._u͊~&,z]{)w^*Z¢s-[.˗h#,9wK:`nǓj+yy`o ?nVw-o ."sϺ 'Ӄ_/O)` 4@`&7 Kn=x vgaaAl/v/Lk{F>tmr};ѹ<qxd3* ;I.x;>,3?n/_ &OD[~[cS--s;GcmONO~Ũ1^wMFe؝czoarS+g?ݠDYNR˴:蜈bעV热)L/ɴ3zkJh6RURYgZԦߺ0rSOwۢEWԿf:FFYK#-w'CnI kTǥ`y݃(n4pFI<&I%m &6aO&6h|,MtY*utmbKqN|!|3".휻Usܭ krݲ@ܦ:pI"6Orr oA#]{k% vs٭RI5*Mn[:P.aDޜҞ{?n KC:u=NР ]'v܄y" sӝ^%emh#Czg,Va+8 َ?jJcΎoe>?0renWw㎀ޯ _?+7k9;;S' nw㎐ޏv*eU2m w4]Hջux.:%OU;z~x}NcvkRnw[j\D;jfw4}d *BܭUFRwq mlEI3h.:Gn r2^6iZt6YP7OC}g4PyYGqODt}cJ)<u4Rx=l:~}3&{ꜵ'g(O{u~ۤ?igN;{q6JϦL&|k78hyt3*LqZ c\{*K+mT hy+Bӟ|mvGoE2V,hGoцV%mT 7hWzkֶpyltJٻމ{s7'-܈ޢ8ihMwܽhpzd{o{ei NoVC+P& 2r׬-ڢފ?Xp~碅`єXj矫m\eew i+-˩hEg[y.?%N{T9eg|el_YWK$~VɰU,9IcyUD9d * X\kĵ%,-s%Y*vK$^yߛ/1%Y@Z/I{{hwEt*8lmRV(b[m H)q: ØIlQNWZa-*r-*8 HrQ({ |E BȼBE";6@}Qԗ{-X:v."FEap6f4.)5qK~QŨHǶc8 #?AIL$ŨHTb? i0Igssy΍ xQ> ȵ(Zl 7 (XOb z; H(2UUi.;N`$6EE:}EN4͑RQdHڳ9*+YpE֊,)v5$.6&NbWT$wEm=ǭ7:t";EESJdܝ.-@bTT$G2vHw9npE;-o -y;窺sl]̝1Kuf].||")3ӿ$4T̕(Pg:٠EU X8 b~BO QUy qtLb0 Nŷ\hGOĄ<_^ӻ"!UHuW7 apɦQ̃;ýc<<^aw@BOp"G%E<5V(tӉȝ;ʧ\xBcG");Ba'JG1_<{΢b"PG1_q7B.4? q^fS49jg+Dp)8yL=Ύ_H̆(,i{"FmԼQ(RoT$&x EDw)KGfSJ&pQ8y\ O"G1ϧg$*.;e爤8#p"E8I.Ab#GT f#%E,i/8?GzlS5W8F܏wĵ01:, -8>%"tGԝ(Su:\@,G"q9Ba6'FeY|f["GYŷ48*#-&]nT$f֧0|IۜM>voW/[<$;"zHQ>fS쵻p a'8^MGgʶ{$"F؛aRpR"*G(c:ׁ5<j$(#jάKxcg0/|Nڌv?ş,JTY=+7=[Cy`}IQ>'^`MD6<4$(#˫Yy@R>H+T"+Hm R)T$/(+*"HEr\~+iD4!Aڇ-y*??[ؓirюF,H}XP(4B !3b܍8&tHСq4܋ӨiRO-z:qa#MFkQ?Ҥi~-Z x v1].}⡮iBFG^-G8P18.4J*E&qCy\ mI%Q\NF?I#IBa'=IGғ;!%M)Ury>?FKI}Ҳǧp}n4I'' gߕWL*i#wͽ}SfRҕOWHu~& )K'.-^D L&b;[JyLZz'0},$i]'M:I i&m ]h4H@SwάUygj/뽗q8iQm<&Ix&Y桺f+]Qh$4iÑ>|a%ImIJH7it$IZ(̐3ix/u吳xFIؤ}`S+ % zP<(󠾈(MfQXQҤDi6[U8Kn!y~Xn~F;J#QBa&!J{_Oֿuo3vڮ[{Ou31QQGETygqɾ8LDi)_] (MbQD5ROɌ>3*f@ey߭wN9x򫗯a&UJThn(~<cꋓ_f_t$s6QTGQE-Kxg4LWi_5Bw=H#>+Vl榑(ǃ,sSFG  ei5*Bw8 *v8r{+fdni% VBm܏Zd41\p:7;voWzxc.5Vx&`.QKޥI\k[uQ'݋sv~}s]D|!BKܥp1(9sGݍ/^\A K 5J,V[~cQ8ndG V? ig;?iqF{*$i%e3;t6y5MfjǽA3š&aM_vQKӤi\&Jj$5͔X :2QR$i i<ʙ&L3#U*>l s2qcǍs"1MؘU[qcMs(5e2.ssxfg3Mg;ҠhfH43Lь}J!"h֠|fH>3LK#Hk̎ mq01LcD?d1Mq0Lc'A>8svA}f|Y)ؠXfH,3Lquq uo;2Cdaea, }; ] l4!r01ɴwOLh,v$6~[ d 1ϖ{=C1 &[}uiypʝ;4q+3C̙1gQ =k fB3> -wP$!Hee2 2( SH5CakvY3LdqfP3j-2rӌMk!4'5Jˠ}f>3g4SSknfOeZ3C֙o a NN;/O8 L$)9\\S}O &kԌRCVGDJcM )laLj k n{*N`ll6õ1FL-q$Ie3\MɎ-Pf`3\MP_3鏆w-%6CIl"r%׏O m b}BGٞ6-Ӳ0H"ٌdkܚ!n05 X!pk7Bb&,!1( Wr ka7nP7EݢDW Ɯ@@͐f|[mm6Vq am5uD kƇc$t4F+ Jg3*pa*MFu7[ k`zi+ g3\ӝN'jIJGq\rI>3>LWQ7+p͒A̐qfƙy|G:|!0 X +ze=)@u#qI 34¢MWLjf43̚oQ3$8ƸnBg\oO:'J>!pfņf4.z{P1gcf;Ts]`D !ec& *d2S6AM dKژajcGcz~U69k0?aK`&<0;ɫDt2CbeA~66Y=OYz* QhG-{Dm7=,(QfHJ3L)Mtp#!J0)5~4CVaZiOx =4Cazh#);i?3~Vm٘ז#e X3L`M[-7Ӗa$6Ӹ-jqTMܖaO,5õ] e5CajR`*mO3U|]-d{_Ef,?V?`Ib3LmG l 1u k53֚cE(fPs[ҍɳ?Dsg4NL&Hm Zh,4ô 6Kna.ri4E SCc\sf93>E2C"adjuõEn/W?v^t=T d1"rzEѬw` 7K ic!0]?JEk03D&a~dx2ɚߟ!N091`B ~_pL6s+Q_G}]s+^ !0 |y}> wz?>_W_8wo ߽̟g5'ts.\U|2k;62_)J[kV_%hQz>iw$_Go׽y9R֎f̻EUΧum?Onmʴx{E/?ϟ_li;x69oZW$/'N|ZӢz/?M*ϋٕşqLcgy\W79.e7{$zډj}{?h9)brMOdt;.tٴ? R7FeSo.Ŷb'V[^ϟ ݑUwO4:Rk*Y|I+yoG ݝ $OF?I.q_^fI7ճBb~~ˏ|aP"UwwY\x/o×G'EϲCOGZ*E^w7zu?^>-a~5v5oŷgD1;{GDGgt?5H䃀o!·?p1W<\Syg;,yzm(2(0Պ-mC!7CPoC!\K҆"8BH$ndh&7]{*mP&!ɒ4MFo6bFbCVT&*4gА"[6\23Fo*!=ޮoW'D ލ.#LrDTwFa2# )Bir!AևXeY>C{~’skD/~TW”N1LcAaFT&ʱJzKm-Οw֐Wci?hu5pO#Dn0ōU"|#RmSպ/kY W֖lxo@'~/8~ __ z3/XUn;=<\uw\[Vƶ .-짴Rv w/rywtxra)wv؟L{!S|_ (x<]Mf +R,2w_w<޽=,;c7'ɖjQ[TׯU\_7vDtSMu閥 7tG^x9,N x@7=9D$3HoތШYY2H /:H}q߾Ӿtz8𵷠x8!=At^}6rΗ{̷ChUك,Wg=x=)rN{L_._Ϩ.a!yt)3~낐X{UꬺW=H{u2c&CcPTYvr^,`-C~zk^|h ԷhS}y iAz>e ҷF5oFVDY[{{;uE}њ;{mQ4t/'՗F_ON6ږ;[%pa΅9?;ݞt+v h#~CU ݷh}Vw+?~I퉛/ܡߢ--/4TrOݟ6noq^Wb6<#bN!37d"!2B-1s2:v=L)l]!,=HP `yu]ߓ4ۚwL,L *z"M2 \3aSs՝\=qxw#mԚ9[=>@[X#XjK7/T \iHZ`pYC9sB"ٗ.זtNiē ED9q(0Ӓ"9B-(lsF[aS<zaEr/ugIaFCw" A&F"Gf3-qQ>KI\)HBH "@Ba6%HTUuu5J8A3_+tAB' >VҔߙ~w^oo-"E`E"D^5n"Hh5w`-$EbjHhˠzy#)bApL"I1G#QHq9RLH1f"?1R,(E\LC5xۙBAH!|F"$HmT R)r bh}֑9yr/܆tt{~9/GRr?q|_EăAs2E?^w{}3.tq)N^M"KQ:g,9Fx_OڑCG|J1wUnQ}͊[;˵S+zF@#ž ?֧ <|B-$EiH;s'2I@lIER>i a!eh71uZ1/߼txrzp\RЉL6"Iq=$Hز,agBH/Mz-WB_g "M 2HQ"]hyrH<$"uήDMyI & }wh_ v"Sj gc&0Jq(ƾBb&>J(bߡR$J))gҼD0f;"cb&aJ5^bqK&ch0)BOaZ#ԗK*>\tFoo/5Z!TZAI|sf\0u1ug)QNi9G/9)rZw3AS:)SvDSh>)BO}|'v0]|TXhP*RLW Oծ8K) m b-(PiOjQ$Li0źV 0:y<_jtU:&Jte[W?v*hԯ4Wڧ_E z^%M@#>6+J\wPUgwoNLJ0k_KDfi&ze,MpYkhmwPT(f6ۭgŪF#KF4yI'yqu4Y,t8{FFK}V`Xe:E@4[-ͤktք:i44\ip㋹QILyDpFRK&`4YgLXaYKۘg4+~QKʖ)[_b4YYieq. )ߝ//]ݎ◧O,4Zl-ue\ɟ{R| CۥL&vah4] wE {KbZ'K3HΟ%u`1陵Wx3r.i4xz]51M bQEqL8?(bx1?L]# > ^^p/Ľ<h4A^ yq"Ö5&K3= )E#LKa(90Mq`q~ L3Qrqip0 *Ɠ_l;ѨiR4SְD#R )LLb 3Mf(iδO: ,#\ .ӑ2i0!W}\˷cz~] ` 0\loλܷF_h[tI:\IC*5:?'ӉX(i4W<s6λ fk43cla?~{SK^fzewI2M()\Knfe"$4eG-+Z}._toS飃&LGOO74Bx a0LŒsvBP,$iX& #C &L@e4RI)>b|18/|FGL#*^i44`iIv>zi&K3).U Q$oiJ%MHli"ZíƬDin(kiOb;Aukaۤ[=N*bvǖcEHnUZ~jli؃H PҤfiJ fv=i19׳Oz,M 1XͷxH[i4\V7T_)'w)s}eݨץqT=IL%z /"FGK, κ 03x#iI+MVk]I\x/-ߘO>j S(1SL1WVۋWmTfr[u ˯4Y~aS) VwpeZRIҍ+UyDKhYitU&J3}f&&JoԤhRi24Ӥ2Wj׊N*, p&J3᪈xs+D6\u1|}f.fZCLڕfjW, nǝ5Iݏ:!w{j0`ҲOjrkB1[-쐌X҄tiҵ}mit$nKZ(Lji&%9t63&cLnL5RZ(-p1둂} :P$Yid(^Ѷv!noawqQ+MVye<`„JΧ]i&w\i!P+a _3`R SD. e2L+,XAmeiǿ`;!0+  UWq3,{FeXAWe|\/T?x Kf2" _U Ve2Ljk{ a1ў2dOS)CaSlF"|2_Jc%1c %0a*N!&0 bT0*ĨT&(lAz=e e)CޔazS _ R|)`z$L0eWowᨵ=*CyTkؘwmYp^dWVR]zVaJ3YH%Xe$SJ<<$<3urNuuկL< T0TƋ)qũ)`,0e@G!͒ 3P 2P3,8N&%8:\2 \=<}-}%_ɤ|5b.Kb/xu^аd,$ >45pM&5xQfs6,fO3l?OFh?GU0eA!U(ؖUA\e\T~-(0ªaQ@2BQꞻKRFKC)lD0Q&D7bv2Bg~\ #N"Nrn az2BI!$aʀ2)Hf SSԔRSVu)]uۛ0*eJ!* QnQEymg5CQ'Wj@SFMi⬏Eqa2RJrVN4RTFR#-Av *#uqdg7 *DMo,VUF(V8({߻X{U6ѫa2R? أޕweUnMƓϽ`X&a5 gYFg[fef}Gռ_iW=Ky{ Lj-Y-VˤXwj3'_zLs\FHsIsAq~BuIa2R7^eRWP0aDIay-צ_ 0S 60/b3#3EØebJGѰe`}kϥ ^(6 .2F3exfZˀ2)Zk X"IXy90w.N)NlN}pk62N`L.o_9 U^Ѱeei6]'c9@2B9kNsVXϣ4]~y)s"SZIQZy!4w٨yCBYn\Ԡo%c\Ύq*8 ,-#!,,烈p=eR"VՆ*ʤ*f{wqr N;֫ *#ԫ$E~~^5i=vR~{jo]hW&]-+֝/$l~| ZZP_h20Zڎ.SWԕIQWkH=\[aW]%Xk(3Ḳe2)*/ΰ?~"|hFjsgY2ed!%>r X>X'WrŒ+X<X09M,M''e`b{5 X&XN'u[Q~:˴,2Hɬq[ِLJ<\סsu?-l0]ӥm[[&el-QH Esx6g]y>r<_?hJ z-A1T0 A,\:\2Ӳ+X*=YV,/+U֞,]F]m'eSVɲeiYVliܞ:4tSDeSDVaӫODͿ2j̲ N{e)BʲB)KX룩'{z7iY`Z^1-˘e~A?[aY`XVanE+ ʦ5\i.RplʭZt8NeS8yn0t{pyQ:g\sʮeB&=-wi.sWܕrWOj,l Rp$gY,[eBRD4}_eRJ|:'|| e!aY%r8\Ɨw6@aeBRJ7)2|e_Y)|%eaVțaY²lJj'{eSUe ?}Qˇ, )J,yUY2 5,{Be!BȲR!K#:ܤƋEՆ)ŲeaaYUx8f3Y²TZx&WWgMeaSٔMˆ!eSwҪOGn9Uwm/Xv,)+uq$/|utc]-#kPRVJI cGUNˏ;M؏=) O =bGbDUS-Wz T6Pe5\hpå)D?eS%yv<;<+g@9M[4;̽",pXTVhQ c%nW,)+ W܂+ T T^YPj?8e!Nٔ8E<P;s}Wz W.0Ќ9M7zs}cH\AEY!% GeaGY% l\<ӽ|ܙF(#*.ГREPsJ";SΔ:Sre֒Q$27eMY!7%.$eGYzõ%y( ʦxK"OOQ8ztیO~e)BB)Jf},L}Y,' ϜeQBRQj( 1JŨ[PMYP#V,&+Tf2dA5Y!մg4Y8M64|{\29Y)$hXYNf=YpOV= ^XrOXO6e=u6\DADǵP+&`lxpyXϣ~ QlMQF{z;k]7=SMSK-(Eww?T,MTůI3*\pOٔ>_S*ׅ+4*+D$EkdB)?e@Xؔ6ecSKͥMQRk\*CB^Oƕ Ap%eT9=2e!EYU"u1lGYQVhG`#sY RW\2.eKY).U1J&?ү,aTBTJ4rDӟeE|Q!-\_dDžĔS.gbŸOY,)+pFr7[W_ F3uP\K/pxʀŦ߬~snW5gbٗT0KTb☶}=76;V)Rv} XǞg儞Ֆ|ԓsQjcˁrBKqNNe"Ur,b9XN(b >uwH˪o> GzOMYfYON쓦#\r0\jwNW]by9. 9ςslr9\Nhr> 1@s{\4K\˞אp8UfgwG=uՙŻýۃýG-/ ;ƷL]~4߱ v9%j4XTpl=^ǃ/˥5<\qǕwoHy4xyÿo@w6&/*Nօr ˥5\°\mɭ5ڱ`wݕr\ ZM8\i+ _g)>n1}ZcˁrBK8R*ϪNarB|DI'1?F\ [KӎLcFKr̓9d.œ+2̥䲿+2n(s \(k*2-@9!-&7'-S" 92 9mq<0P1fKfE":2z怞9!zzõCŬ9e.0379!7 ~^ rp9dN ⭏c|sB|X,s˜P,OXL9f.E',VT3R֐ sB,1 rp,9bN(cAsBL8h.kǵXsPfXX;vas)G/ /:As 5(CԱpUr,~9_N*~s7|| D[r\ZÕB˥5v e9@Y.e w #l8:>oGX+=mXu\1rRֲ>dվ n9J d17wM p9\N p崾qԭZIaNԍImS?3:[[>7v;)3Ont\?m44$hZ_ףߞB5$\}1uhN,Vr@풇]r Z*Ur,p9\.%px$cyFefˁrBfkk['IIZkX 2@q9!% yh'p44asH4fpLnar\Ze\\}/ 9`N7wʕMs)o1w3yus<]@]˦gr˃-*qXz<$./eo64ۗ򀷼DWE S'QȖֶ|kj?zPNӞ=. =.E94pZ.䂖hF4>'_y6<-2tI7՟`) -CK -ds{YCxpw$R, 5ߛsZZ_rB%!ָ<4./ոֿ߽g)+$fSfWIe-Y˧d-e̗B\'ha:fLmpfScUxͳry%Rn~fj0H\voy!UY.rAS>Nzv<0rگ+lty]^ht}OgXBXk{=X4X)~}cRc.Ь|J*Vs!pFSU!ԓ!p>ST!Ti4ž){Pשcʧ5$nz MYVq( qD3@fC|'iS蓠]Ջ%M_DO_?w,rm~*H{&3ooGXС|JZAkWO쇪P_3%-<)/!]Eu)v )EӾ=F߽>|훃>(|ʟ Jgä)j )0)/D[)Υ~)`6)f.cO"LWyU>EWe96)U}%Ō.OXK"62:?kd*g?\=׏ƿPVЖSVx.tlzlny[^hn)]Hs݃^e@Mqyx\>qș`2;Agcǝ5Pf fُ?s+\ +\ ^Sj6t}ьݜ? ]=W3-bKs}44/;|gCRɗYfW{fe|YEn~6Hޤb0: 0a\InІc^h>;cΘ:crLyPc^HeNvG+慮>_, gWu~ۻ{/W{ |+bpKS,H!H [aVZa&>?6 S6N6__^~m?6c7H&QѠz_<呶2hgMW?jKLyPc^JYea2̧`"-OsG5Oif2b桘b5ʘ*cOW_3޶q{r+Ұe"dcG|dz5ay5&3NNxSђ!ly2 yX֑y3̧xv2LyPi>E2%Ұ!y&"{! ggWF<2گ,yc^(9瀓K}"28 0Ic7dm<`hmS_|Z&_, 0ü Dȥ~SQX'nzSLh:$ B $+\=8 ,aVYk<ee,HeA(9k92Hm#HV.w-Vm-pJ?InG{gYHygen;"hRѦ76 4hc3Qz;w?7ql6#g#Zv'pG-4}|4 ad?(i! @sR^_ie$w#zF:7Gݣ_Sl[HQl Ӷ5S~n%<^՗d@-P R@MؗK}.siK}-H}в W7; ଅtme|-_ B|m L`;-N B;Mmn2U3)R\~g,0 BLrV< VI'(p )HLШzʳaV{5}ؿZóEzc<;wA߬'矝h; Ld!E y঱/QzYɷ\9$0G!Gݽsm),)UZ816bDڿMX! PBJ!}n[E7m qoea`,9 )lٷF75.: i!2}!-H4Aӵo,Hi!%b ,m!%M,Mc]\Ɲr$WeHgA(Cda:PςP=[V_,w Lf^iYmٶlU Iw^?_4s%6 `gAKSs'Xb,8 R,/UeΏ gA??1d)d~~ ,ܫc1 p̂1Cdt}5+ۡ^V lXeAhr[idžw?6^;6Y`Y|.;;=;{|{KdA)bGLcAH ZMH a! @B [wK;q=:p492YC$(6LX5Ԙ/8cr5%Y` '$Â\ )k +, opos_Iϣ-X PPĊGb,-)^`2, B2sP,k&) `X `06WW#,yH^!%yxY PPl ^`}+@ R}k9s 0M([`+@ ){讐 pB<* `Z%Qh+{ZVHyZk\`c VUHV*oĬ77-1s*"VUV~0VUHVNLEոaеmb*+U{)\[!o%it2zVUHVg3Ʀ ĦD>d,UV2mkl8MY/MxSAMoQ;@07 #_cP8TAP)$K/*UJ*" ,VUA(V ˘ ½Uɪ*H*' ͑6ŻPfث dJy ɱ``7[*2t]E!tLNUSSNUudu*BBu*6[7Z4m6@;lt|:Ia-_Y" ;.I:9_Ф 8L07OˈrH3T!UETh :! 2ookϛ_v׃7^Yȭh vR[nv8D%֊p_5Ob*B*'?'uáByyOz#ìT+ӣEmu`p*Bp)"L, dmH٩pЩ*w}侏Df"(e :UNS:UX,LXEV1EXg/.3::'wu9e}ǀ)* )+MY}1胫BJbmKb jU B* )'y՝n@9BdZ*)Zj -$(d8jJTl.bʏ*:ޅ79T"(V_\|i̹\OE?US74 *ޫZYPbJ2NO&>pV1Yڭ-xRETKmRTR8lio0YR/uN}ի}~U_{*5#;x.lWEUQhWɂV/5f͖j,<:B.0дʌp_\)誅3#_:&"($Mbjk2UL22vfƯ[d*BJckXG&"ȫxUd*RJ޺5n&v>V>,%ugХgb\0eEUdO.YG[;fE-hM{xG"|(;2ApvY+X2kYZVjY3eyy-xGF"(DdGK;yG"(,fYޑj+ 1wd+)ktrUޑ+ /E%iW"\ 2vE!%hV!qŔ~dd+يBdDNV+O_3qG罏|m_k!"\&WL\'Faz6yʦy8L{E^QJ{c[wmd]u?(Z#uzWL]kxĹ܊BpK/ -`66. }@Slno+e)hoNb3` X2`zRUb+BBKtm"en+ۊ)nD|V|۫'|J)2gE!fϊb )cdj+ڊ)jk -HPZQHik t"h(V{Yk쬘ڿaEpXC"VS"zUIT_ΜR\V\jϕC`VS`Vs^Di,mEH[1%mW[MwVe,WL\P7 ֐݊BtK”Xivo̵RWJ]r޼nqꊀz#~[[.^WL^ł\NkϘ`햱zŔ~bs+܊Bskkg.3Σ)dSg}7r|WqƸ"0(ĸV_o^دɭ7Gݣ_h\W\@˄a6)1vlwE]1ew-!ޙ^.?s6X`ߏZR`ֺ"(պ{];v`9];vR|W^iu3@vw#$sVRtm7h{wF[4ZX$τack֎4ruv?UkB8)ǥtaV(GpTYwځZua:kt֎ZZbĬuw/ϣޟONξT=p<p<p<p<p<p< 04libfido2-1.10.0/fuzz/summary.txt000066400000000000000000000206461417126203300165260ustar00rootroot00000000000000Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ fuzz/clock.c 24 1 95.83% 4 0 100.00% 35 0 100.00% fuzz/prng.c 31 0 100.00% 2 0 100.00% 35 0 100.00% fuzz/udev.c 103 1 99.03% 17 0 100.00% 126 3 97.62% fuzz/uniform_random.c 7 1 85.71% 1 0 100.00% 12 1 91.67% fuzz/wrap.c 6 0 100.00% 1 0 100.00% 7 0 100.00% openbsd-compat/explicit_bzero.c 4 0 100.00% 1 0 100.00% 7 0 100.00% openbsd-compat/freezero.c 4 0 100.00% 1 0 100.00% 6 0 100.00% openbsd-compat/recallocarray.c 41 7 82.93% 1 0 100.00% 36 7 80.56% openbsd-compat/strlcat.c 12 1 91.67% 1 0 100.00% 21 1 95.24% openbsd-compat/timingsafe_bcmp.c 4 0 100.00% 1 0 100.00% 7 0 100.00% src/aes256.c 115 4 96.52% 8 0 100.00% 157 14 91.08% src/assert.c 563 40 92.90% 56 3 94.64% 694 40 94.24% src/authkey.c 44 0 100.00% 5 0 100.00% 59 0 100.00% src/bio.c 419 20 95.23% 49 2 95.92% 559 21 96.24% src/blob.c 53 2 96.23% 10 0 100.00% 83 4 95.18% src/buf.c 8 1 87.50% 2 0 100.00% 16 1 93.75% src/cbor.c 1047 23 97.80% 54 0 100.00% 1237 46 96.28% src/compress.c 34 4 88.24% 3 0 100.00% 28 3 89.29% src/config.c 108 0 100.00% 11 0 100.00% 151 0 100.00% src/cred.c 632 34 94.62% 69 2 97.10% 830 36 95.66% src/credman.c 382 10 97.38% 40 0 100.00% 518 15 97.10% src/dev.c 421 79 81.24% 45 7 84.44% 491 105 78.62% src/ecdh.c 117 2 98.29% 4 0 100.00% 146 5 96.58% src/eddsa.c 80 3 96.25% 10 0 100.00% 106 8 92.45% src/err.c 122 10 91.80% 1 0 100.00% 126 10 92.06% src/es256.c 306 5 98.37% 19 0 100.00% 358 7 98.04% src/hid.c 87 2 97.70% 14 0 100.00% 145 3 97.93% src/hid_linux.c 173 68 60.69% 14 7 50.00% 250 104 58.40% src/hid_unix.c 28 20 28.57% 2 0 100.00% 43 24 44.19% src/info.c 184 0 100.00% 39 0 100.00% 316 0 100.00% src/io.c 182 7 96.15% 13 0 100.00% 221 11 95.02% src/iso7816.c 18 1 94.44% 5 0 100.00% 38 0 100.00% src/largeblob.c 513 19 96.30% 30 0 100.00% 684 43 93.71% src/log.c 39 5 87.18% 7 1 85.71% 63 4 93.65% src/netlink.c 328 14 95.73% 40 0 100.00% 498 32 93.57% src/nfc_linux.c 327 73 77.68% 23 5 78.26% 458 124 72.93% src/pin.c 403 3 99.26% 26 0 100.00% 495 3 99.39% src/random.c 6 1 83.33% 1 0 100.00% 6 1 83.33% src/reset.c 24 0 100.00% 3 0 100.00% 23 0 100.00% src/rs1.c 25 0 100.00% 3 0 100.00% 39 0 100.00% src/rs256.c 141 8 94.33% 13 0 100.00% 172 10 94.19% src/time.c 43 3 93.02% 3 0 100.00% 43 1 97.67% src/tpm.c 100 0 100.00% 9 0 100.00% 194 0 100.00% src/types.c 25 0 100.00% 6 0 100.00% 46 0 100.00% src/u2f.c 528 4 99.24% 17 0 100.00% 685 12 98.25% Files which contain no functions: openbsd-compat/openbsd-compat.h 0 0 - 0 0 - 0 0 - openbsd-compat/time.h 0 0 - 0 0 - 0 0 - src/extern.h 0 0 - 0 0 - 0 0 - src/fido.h 0 0 - 0 0 - 0 0 - src/fido/err.h 0 0 - 0 0 - 0 0 - src/fido/param.h 0 0 - 0 0 - 0 0 - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ TOTAL 7861 476 93.94% 684 27 96.05% 10270 699 93.19% libfido2-1.10.0/fuzz/udev.c000066400000000000000000000150641417126203300153750ustar00rootroot00000000000000/* * Copyright (c) 2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #include #include "mutator_aux.h" struct udev { int magic; }; struct udev_enumerate { int magic; struct udev_list_entry *list_entry; }; struct udev_list_entry { int magic; }; struct udev_device { int magic; struct udev_device *parent; }; #define UDEV_MAGIC 0x584492cc #define UDEV_DEVICE_MAGIC 0x569180dd #define UDEV_LIST_ENTRY_MAGIC 0x497422ee #define UDEV_ENUM_MAGIC 0x583570ff #define ASSERT_TYPE(x, m) assert((x) != NULL && (x)->magic == (m)) #define ASSERT_UDEV(x) ASSERT_TYPE((x), UDEV_MAGIC) #define ASSERT_UDEV_ENUM(x) ASSERT_TYPE((x), UDEV_ENUM_MAGIC) #define ASSERT_UDEV_LIST_ENTRY(x) ASSERT_TYPE((x), UDEV_LIST_ENTRY_MAGIC) #define ASSERT_UDEV_DEVICE(x) ASSERT_TYPE((x), UDEV_DEVICE_MAGIC) static const char *uevent; static const struct blob *report_descriptor; struct udev *__wrap_udev_new(void); struct udev_device *__wrap_udev_device_get_parent_with_subsystem_devtype( struct udev_device *, const char *, const char *); struct udev_device *__wrap_udev_device_new_from_syspath(struct udev *, const char *); struct udev_enumerate *__wrap_udev_enumerate_new(struct udev *); struct udev_list_entry *__wrap_udev_enumerate_get_list_entry( struct udev_enumerate *); struct udev_list_entry *__wrap_udev_list_entry_get_next( struct udev_list_entry *); const char *__wrap_udev_device_get_sysattr_value(struct udev_device *, const char *); const char *__wrap_udev_list_entry_get_name(struct udev_list_entry *); const char *__wrap_udev_device_get_devnode(struct udev_device *); const char *__wrap_udev_device_get_sysnum(struct udev_device *); int __wrap_udev_enumerate_add_match_subsystem(struct udev_enumerate *, const char *); int __wrap_udev_enumerate_scan_devices(struct udev_enumerate *); int __wrap_ioctl(int, unsigned long , ...); void __wrap_udev_device_unref(struct udev_device *); void __wrap_udev_enumerate_unref(struct udev_enumerate *); void __wrap_udev_unref(struct udev *); void set_udev_parameters(const char *, const struct blob *); struct udev_device * __wrap_udev_device_get_parent_with_subsystem_devtype(struct udev_device *child, const char *subsystem, const char *devtype) { ASSERT_UDEV_DEVICE(child); fido_log_debug("%s", subsystem); /* XXX consume */ fido_log_debug("%s", devtype); /* XXX consume */ if (child->parent != NULL) return child->parent; if ((child->parent = calloc(1, sizeof(*child->parent))) == NULL) return NULL; child->parent->magic = UDEV_DEVICE_MAGIC; return child->parent; } const char * __wrap_udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) { ASSERT_UDEV_DEVICE(udev_device); if (uniform_random(400) < 1) return NULL; if (!strcmp(sysattr, "manufacturer") || !strcmp(sysattr, "product")) return "product info"; /* XXX randomise? */ else if (!strcmp(sysattr, "uevent")) return uevent; return NULL; } const char * __wrap_udev_list_entry_get_name(struct udev_list_entry *entry) { ASSERT_UDEV_LIST_ENTRY(entry); return uniform_random(400) < 1 ? NULL : "name"; /* XXX randomise? */ } struct udev_device * __wrap_udev_device_new_from_syspath(struct udev *udev, const char *syspath) { struct udev_device *udev_device; ASSERT_UDEV(udev); fido_log_debug("%s", syspath); if ((udev_device = calloc(1, sizeof(*udev_device))) == NULL) return NULL; udev_device->magic = UDEV_DEVICE_MAGIC; return udev_device; } const char * __wrap_udev_device_get_devnode(struct udev_device *udev_device) { ASSERT_UDEV_DEVICE(udev_device); return uniform_random(400) < 1 ? NULL : "/dev/zero"; } const char * __wrap_udev_device_get_sysnum(struct udev_device *udev_device) { ASSERT_UDEV_DEVICE(udev_device); return uniform_random(400) < 1 ? NULL : "101010"; /* XXX randomise? */ } void __wrap_udev_device_unref(struct udev_device *udev_device) { ASSERT_UDEV_DEVICE(udev_device); if (udev_device->parent) { ASSERT_UDEV_DEVICE(udev_device->parent); free(udev_device->parent); } free(udev_device); } struct udev * __wrap_udev_new(void) { struct udev *udev; if ((udev = calloc(1, sizeof(*udev))) == NULL) return NULL; udev->magic = UDEV_MAGIC; return udev; } struct udev_enumerate * __wrap_udev_enumerate_new(struct udev *udev) { struct udev_enumerate *udev_enum; ASSERT_UDEV(udev); if ((udev_enum = calloc(1, sizeof(*udev_enum))) == NULL) return NULL; udev_enum->magic = UDEV_ENUM_MAGIC; return udev_enum; } int __wrap_udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum, const char *subsystem) { ASSERT_UDEV_ENUM(udev_enum); fido_log_debug("%s:", subsystem); return uniform_random(400) < 1 ? -EINVAL : 0; } int __wrap_udev_enumerate_scan_devices(struct udev_enumerate *udev_enum) { ASSERT_UDEV_ENUM(udev_enum); return uniform_random(400) < 1 ? -EINVAL : 0; } struct udev_list_entry * __wrap_udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum) { ASSERT_UDEV_ENUM(udev_enum); if ((udev_enum->list_entry = calloc(1, sizeof(*udev_enum->list_entry))) == NULL) return NULL; udev_enum->list_entry->magic = UDEV_LIST_ENTRY_MAGIC; return udev_enum->list_entry; } struct udev_list_entry * __wrap_udev_list_entry_get_next(struct udev_list_entry *udev_list_entry) { ASSERT_UDEV_LIST_ENTRY(udev_list_entry); return uniform_random(400) < 1 ? NULL : udev_list_entry; } void __wrap_udev_enumerate_unref(struct udev_enumerate *udev_enum) { ASSERT_UDEV_ENUM(udev_enum); if (udev_enum->list_entry) ASSERT_UDEV_LIST_ENTRY(udev_enum->list_entry); free(udev_enum->list_entry); free(udev_enum); } void __wrap_udev_unref(struct udev *udev) { ASSERT_UDEV(udev); free(udev); } int __wrap_ioctl(int fd, unsigned long request, ...) { va_list ap; struct hidraw_report_descriptor *hrd; (void)fd; if (uniform_random(400) < 1) { errno = EINVAL; return -1; } va_start(ap, request); switch (request) { case IOCTL_REQ(HIDIOCGRDESCSIZE): *va_arg(ap, int *) = (int)report_descriptor->len; break; case IOCTL_REQ(HIDIOCGRDESC): hrd = va_arg(ap, struct hidraw_report_descriptor *); assert(hrd->size == report_descriptor->len); memcpy(hrd->value, report_descriptor->body, hrd->size); break; default: warnx("%s: unknown request 0x%lx", __func__, request); abort(); } va_end(ap); return 0; } void set_udev_parameters(const char *uevent_ptr, const struct blob *report_descriptor_ptr) { uevent = uevent_ptr; report_descriptor = report_descriptor_ptr; } libfido2-1.10.0/fuzz/uniform_random.c000066400000000000000000000034031417126203300174430ustar00rootroot00000000000000/* * 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); unsigned long prng_uint32(void); /* * Calculate a uniformly distributed random number less than upper_bound * avoiding "modulo bias". * * Uniformity is achieved by generating new random numbers until the one * returned is outside the range [0, 2**32 % upper_bound). This * guarantees the selected random number will be inside * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) * after reduction modulo upper_bound. */ uint32_t uniform_random(uint32_t upper_bound) { uint32_t r, min; if (upper_bound < 2) return 0; /* 2**32 % x == (2**32 - x) % x */ min = -upper_bound % upper_bound; /* * This could theoretically loop forever but each retry has * p > 0.5 (worst case, usually far better) of selecting a * number inside the range we need, so it should rarely need * to re-roll. */ for (;;) { r = (uint32_t)prng_uint32(); if (r >= min) break; } return r % upper_bound; } libfido2-1.10.0/fuzz/wiredata_fido2.h000066400000000000000000000744321417126203300173260ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _WIREDATA_FIDO2_H #define _WIREDATA_FIDO2_H #define WIREDATA_CTAP_INIT \ 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x80, \ 0x43, 0x56, 0x40, 0xb1, 0x4e, 0xd9, 0x2d, 0x00, \ 0x22, 0x00, 0x02, 0x02, 0x05, 0x02, 0x01, 0x05, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_KEEPALIVE \ 0x00, 0x22, 0x00, 0x02, 0xbb, 0x00, 0x01, 0x02, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_INFO \ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0xb9, 0x00, \ 0xa9, 0x01, 0x83, 0x66, 0x55, 0x32, 0x46, 0x5f, \ 0x56, 0x32, 0x68, 0x46, 0x49, 0x44, 0x4f, 0x5f, \ 0x32, 0x5f, 0x30, 0x6c, 0x46, 0x49, 0x44, 0x4f, \ 0x5f, 0x32, 0x5f, 0x31, 0x5f, 0x50, 0x52, 0x45, \ 0x02, 0x82, 0x6b, 0x63, 0x72, 0x65, 0x64, 0x50, \ 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x6b, 0x68, \ 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x65, 0x63, 0x72, \ 0x00, 0x22, 0x00, 0x02, 0x00, 0x65, 0x74, 0x03, \ 0x50, 0x19, 0x56, 0xe5, 0xbd, 0xa3, 0x74, 0x45, \ 0xf1, 0xa8, 0x14, 0x35, 0x64, 0x03, 0xfd, 0xbc, \ 0x18, 0x04, 0xa5, 0x62, 0x72, 0x6b, 0xf5, 0x62, \ 0x75, 0x70, 0xf5, 0x64, 0x70, 0x6c, 0x61, 0x74, \ 0xf4, 0x69, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, \ 0x50, 0x69, 0x6e, 0xf4, 0x75, 0x63, 0x72, 0x65, \ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d, \ 0x00, 0x22, 0x00, 0x02, 0x01, 0x67, 0x6d, 0x74, \ 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0xf5, \ 0x05, 0x19, 0x04, 0xb0, 0x06, 0x81, 0x01, 0x07, \ 0x08, 0x08, 0x18, 0x80, 0x0a, 0x82, 0xa2, 0x63, \ 0x61, 0x6c, 0x67, 0x26, 0x64, 0x74, 0x79, 0x70, \ 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, \ 0x2d, 0x6b, 0x65, 0x79, 0xa2, 0x63, 0x61, 0x6c, \ 0x67, 0x27, 0x64, 0x74, 0x79, 0x70, 0x65, 0x6a, \ 0x00, 0x22, 0x00, 0x02, 0x02, 0x70, 0x75, 0x62, \ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_AUTHKEY \ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x51, 0x00, \ 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, \ 0x20, 0x01, 0x21, 0x58, 0x20, 0x2a, 0xb8, 0x2d, \ 0x36, 0x69, 0xab, 0x30, 0x9d, 0xe3, 0x5e, 0x9b, \ 0xfb, 0x94, 0xfc, 0x1d, 0x92, 0x95, 0xaf, 0x01, \ 0x47, 0xfe, 0x4b, 0x87, 0xe5, 0xcf, 0x3f, 0x05, \ 0x0b, 0x39, 0xda, 0x17, 0x49, 0x22, 0x58, 0x20, \ 0x15, 0x1b, 0xbe, 0x08, 0x78, 0x60, 0x4d, 0x3c, \ 0x00, 0x22, 0x00, 0x02, 0x00, 0x3f, 0xf1, 0x60, \ 0xa6, 0xd8, 0xf8, 0xed, 0xce, 0x4a, 0x30, 0x5d, \ 0x1a, 0xaf, 0x80, 0xc4, 0x0a, 0xd2, 0x6f, 0x77, \ 0x38, 0x12, 0x97, 0xaa, 0xbd, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_PINTOKEN \ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x14, 0x00, \ 0xa1, 0x02, 0x50, 0xee, 0x40, 0x4c, 0x85, 0xd7, \ 0xa1, 0x2f, 0x56, 0xc4, 0x4e, 0xc5, 0x93, 0x41, \ 0xd0, 0x3b, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_STATUS \ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x01, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_RETRIES \ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x04, 0x00, \ 0xa1, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_ASSERT \ 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0xcb, 0x00, \ 0xa3, 0x01, 0xa2, 0x62, 0x69, 0x64, 0x58, 0x40, \ 0x4a, 0x4c, 0x9e, 0xcc, 0x81, 0x7d, 0x42, 0x03, \ 0x2b, 0x41, 0xd1, 0x38, 0xd3, 0x49, 0xb4, 0xfc, \ 0xfb, 0xe4, 0x4e, 0xe4, 0xff, 0x76, 0x34, 0x16, \ 0x68, 0x06, 0x9d, 0xa6, 0x01, 0x32, 0xb9, 0xff, \ 0xc2, 0x35, 0x0d, 0x89, 0x43, 0x66, 0x12, 0xf8, \ 0x8e, 0x5b, 0xde, 0xf4, 0xcc, 0xec, 0x9d, 0x03, \ 0x00, 0x92, 0x00, 0x0e, 0x00, 0x85, 0xc2, 0xf5, \ 0xe6, 0x8e, 0xeb, 0x3f, 0x3a, 0xec, 0xc3, 0x1d, \ 0x04, 0x6e, 0xf3, 0x5b, 0x88, 0x64, 0x74, 0x79, \ 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6c, 0x69, \ 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x02, 0x58, 0x25, \ 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e, 0x8c, 0x68, \ 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76, 0x60, 0x5b, \ 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86, 0x32, 0xc7, \ 0x00, 0x92, 0x00, 0x0e, 0x01, 0x99, 0x5c, 0xf3, \ 0xba, 0x83, 0x1d, 0x97, 0x63, 0x04, 0x00, 0x00, \ 0x00, 0x09, 0x03, 0x58, 0x47, 0x30, 0x45, 0x02, \ 0x21, 0x00, 0xcf, 0x3f, 0x36, 0x0e, 0x1f, 0x6f, \ 0xd6, 0xa0, 0x9d, 0x13, 0xcf, 0x55, 0xf7, 0x49, \ 0x8f, 0xc8, 0xc9, 0x03, 0x12, 0x76, 0x41, 0x75, \ 0x7b, 0xb5, 0x0a, 0x90, 0xa5, 0x82, 0x26, 0xf1, \ 0x6b, 0x80, 0x02, 0x20, 0x34, 0x9b, 0x7a, 0x82, \ 0x00, 0x92, 0x00, 0x0e, 0x02, 0xd3, 0xe1, 0x79, \ 0x49, 0x55, 0x41, 0x9f, 0xa4, 0x06, 0x06, 0xbd, \ 0xc8, 0xb9, 0x2b, 0x5f, 0xe1, 0xa7, 0x99, 0x1c, \ 0xa1, 0xfc, 0x7e, 0x3e, 0xd5, 0x85, 0x2e, 0x11, \ 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_CRED \ 0x00, 0x91, 0x00, 0x03, 0x90, 0x03, 0xe1, 0x00, \ 0xa3, 0x01, 0x66, 0x70, 0x61, 0x63, 0x6b, 0x65, \ 0x64, 0x02, 0x58, 0xc4, 0x49, 0x96, 0x0d, 0xe5, \ 0x88, 0x0e, 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f, \ 0x64, 0x76, 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9, \ 0xa2, 0x86, 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, \ 0x83, 0x1d, 0x97, 0x63, 0x45, 0x00, 0x00, 0x00, \ 0x00, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, \ 0x00, 0x91, 0x00, 0x03, 0x00, 0x15, 0x80, 0x06, \ 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x40, \ 0xed, 0x88, 0x48, 0xa1, 0xdb, 0x56, 0x4d, 0x0f, \ 0x0d, 0xc8, 0x8f, 0x0f, 0xe9, 0x16, 0xb1, 0x78, \ 0xa9, 0x40, 0x98, 0x71, 0xa0, 0xb3, 0xf2, 0xcf, \ 0x05, 0x73, 0x6c, 0x12, 0xbf, 0x00, 0x96, 0xf3, \ 0x7b, 0x93, 0xba, 0x49, 0xee, 0x23, 0xb4, 0x78, \ 0x2e, 0xfb, 0xce, 0x27, 0xa8, 0xc2, 0x26, 0x78, \ 0x00, 0x91, 0x00, 0x03, 0x01, 0xcc, 0x95, 0x2d, \ 0x40, 0xdb, 0xd1, 0x40, 0x3d, 0x2b, 0xa3, 0x31, \ 0xa0, 0x75, 0x82, 0x63, 0xf0, 0xa5, 0x01, 0x02, \ 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x9d, \ 0x95, 0xa1, 0xb5, 0xd6, 0x11, 0xbf, 0xe2, 0x28, \ 0xa0, 0x7f, 0xca, 0x1e, 0xd9, 0x09, 0x0f, 0x0d, \ 0xe7, 0x8e, 0x29, 0xe8, 0x2e, 0x11, 0xdb, 0x55, \ 0x62, 0x13, 0xd7, 0x26, 0xc2, 0x7e, 0x2b, 0x22, \ 0x00, 0x91, 0x00, 0x03, 0x02, 0x58, 0x20, 0xbe, \ 0x74, 0x2a, 0xac, 0xde, 0x11, 0x40, 0x76, 0x31, \ 0x0b, 0xed, 0x55, 0xde, 0xf3, 0x03, 0xe4, 0x1c, \ 0xac, 0x42, 0x63, 0x8f, 0xe8, 0x30, 0x63, 0xb7, \ 0x07, 0x4e, 0x5d, 0xfb, 0x17, 0x5e, 0x9b, 0x03, \ 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x26, 0x63, 0x73, \ 0x69, 0x67, 0x58, 0x48, 0x30, 0x46, 0x02, 0x21, \ 0x00, 0xfb, 0xd1, 0x26, 0x76, 0x34, 0x74, 0xac, \ 0x00, 0x91, 0x00, 0x03, 0x03, 0xf6, 0xd8, 0x5c, \ 0x5d, 0xbc, 0xda, 0xe0, 0x43, 0xe0, 0xa5, 0x42, \ 0x9f, 0xc7, 0xe2, 0x18, 0x3e, 0xe2, 0x2c, 0x94, \ 0x78, 0xbf, 0x9c, 0xeb, 0x3e, 0x9d, 0x02, 0x21, \ 0x00, 0xab, 0x21, 0x1b, 0xc4, 0x30, 0x69, 0xee, \ 0x7f, 0x09, 0xe6, 0x6b, 0x99, 0x98, 0x34, 0x07, \ 0x7b, 0x9a, 0x58, 0xb2, 0xe8, 0x77, 0xe0, 0xba, \ 0x7d, 0xab, 0x65, 0xf8, 0xba, 0x2a, 0xcb, 0x9a, \ 0x00, 0x91, 0x00, 0x03, 0x04, 0x41, 0x63, 0x78, \ 0x35, 0x63, 0x81, 0x59, 0x02, 0xb3, 0x30, 0x82, \ 0x02, 0xaf, 0x30, 0x82, 0x01, 0x97, 0xa0, 0x03, \ 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x5b, 0x3d, \ 0xb6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, \ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, \ 0x30, 0x21, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, \ 0x55, 0x04, 0x03, 0x0c, 0x16, 0x59, 0x75, 0x62, \ 0x00, 0x91, 0x00, 0x03, 0x05, 0x69, 0x63, 0x6f, \ 0x20, 0x46, 0x49, 0x44, 0x4f, 0x20, 0x50, 0x72, \ 0x65, 0x76, 0x69, 0x65, 0x77, 0x20, 0x43, 0x41, \ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x34, \ 0x31, 0x32, 0x31, 0x30, 0x35, 0x37, 0x31, 0x30, \ 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x32, 0x33, \ 0x31, 0x31, 0x30, 0x35, 0x37, 0x31, 0x30, 0x5a, \ 0x30, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \ 0x00, 0x91, 0x00, 0x03, 0x06, 0x55, 0x04, 0x06, \ 0x13, 0x02, 0x53, 0x45, 0x31, 0x12, 0x30, 0x10, \ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x59, \ 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x41, 0x42, \ 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, \ 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, \ 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, \ 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, \ 0x00, 0x91, 0x00, 0x03, 0x07, 0x74, 0x69, 0x6f, \ 0x6e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, \ 0x04, 0x03, 0x0c, 0x1f, 0x59, 0x75, 0x62, 0x69, \ 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, \ 0x45, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, \ 0x20, 0x31, 0x32, 0x31, 0x33, 0x39, 0x33, 0x39, \ 0x31, 0x32, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, \ 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, \ 0x00, 0x91, 0x00, 0x03, 0x08, 0x06, 0x08, 0x2a, \ 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, \ 0x42, 0x00, 0x04, 0xfb, 0x2c, 0xdd, 0x30, 0x43, \ 0x28, 0xc5, 0x72, 0x4a, 0x50, 0xcc, 0xe6, 0xf6, \ 0x0b, 0xad, 0x7d, 0x27, 0xa9, 0x1b, 0x59, 0xe1, \ 0xe6, 0x6f, 0x29, 0x7b, 0x89, 0xc9, 0xd4, 0x3d, \ 0xc2, 0xb2, 0xc7, 0x78, 0x89, 0xb4, 0xf0, 0xff, \ 0x9d, 0x02, 0x28, 0xcb, 0x94, 0x6d, 0xfc, 0xe0, \ 0x00, 0x91, 0x00, 0x03, 0x09, 0x1b, 0x19, 0x58, \ 0x9b, 0x67, 0x80, 0x4a, 0xac, 0x97, 0x7f, 0x28, \ 0x18, 0x9c, 0xcd, 0xb3, 0x25, 0x74, 0xca, 0x28, \ 0xa3, 0x6c, 0x30, 0x6a, 0x30, 0x22, 0x06, 0x09, \ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xc4, 0x0a, \ 0x02, 0x04, 0x15, 0x31, 0x2e, 0x33, 0x2e, 0x36, \ 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, \ 0x31, 0x34, 0x38, 0x32, 0x2e, 0x31, 0x2e, 0x36, \ 0x00, 0x91, 0x00, 0x03, 0x0a, 0x30, 0x13, 0x06, \ 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xe5, \ 0x1c, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, \ 0x04, 0x30, 0x30, 0x21, 0x06, 0x0b, 0x2b, 0x06, \ 0x01, 0x04, 0x01, 0x82, 0xe5, 0x1c, 0x01, 0x01, \ 0x04, 0x04, 0x12, 0x04, 0x10, 0xf8, 0xa0, 0x11, \ 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, \ 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x30, 0x0c, 0x06, \ 0x00, 0x91, 0x00, 0x03, 0x0b, 0x03, 0x55, 0x1d, \ 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, \ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, \ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, \ 0x82, 0x01, 0x01, 0x00, 0x32, 0xf3, 0xe4, 0xbd, \ 0x58, 0xd7, 0x42, 0x2b, 0xaf, 0x49, 0x99, 0x86, \ 0x08, 0x1f, 0x0d, 0xa9, 0x3b, 0xc6, 0xaa, 0x1c, \ 0x72, 0x11, 0xf9, 0x28, 0x53, 0xeb, 0xf3, 0xeb, \ 0x00, 0x91, 0x00, 0x03, 0x0c, 0x73, 0xda, 0x69, \ 0x3b, 0x06, 0xde, 0x31, 0x33, 0x8e, 0x5d, 0x02, \ 0xec, 0xf6, 0x76, 0xe9, 0x5c, 0x42, 0xbe, 0xa5, \ 0x8f, 0x25, 0xd3, 0x37, 0x3f, 0x77, 0xbb, 0x2a, \ 0x9d, 0x7c, 0xb2, 0x3e, 0x11, 0x8c, 0x41, 0xd4, \ 0x9a, 0x4c, 0x9a, 0xd8, 0xf3, 0xe2, 0xa4, 0xec, \ 0x01, 0x77, 0x7a, 0x74, 0xa8, 0xc4, 0x12, 0x43, \ 0xc3, 0x1e, 0xce, 0x20, 0x8f, 0x2d, 0x0f, 0x6e, \ 0x00, 0x91, 0x00, 0x03, 0x0d, 0xbc, 0x61, 0x9b, \ 0xe1, 0x84, 0xa1, 0x72, 0xf6, 0xa9, 0xac, 0xcb, \ 0xf8, 0x73, 0x6d, 0x5b, 0xe2, 0x98, 0xb3, 0x6b, \ 0xec, 0xe7, 0x1e, 0x77, 0x8d, 0x0a, 0x69, 0xaa, \ 0xf9, 0x94, 0xb8, 0x63, 0x6d, 0xe8, 0xfa, 0xf6, \ 0x2f, 0xd3, 0xce, 0x7f, 0x04, 0x4c, 0x32, 0x2c, \ 0xf7, 0x26, 0x3e, 0x34, 0x99, 0xe6, 0xa5, 0xb2, \ 0xb0, 0x2a, 0xbb, 0xad, 0x5b, 0xd9, 0xec, 0xe5, \ 0x00, 0x91, 0x00, 0x03, 0x0e, 0xb0, 0x71, 0x4d, \ 0x73, 0xbb, 0x94, 0x61, 0x49, 0x9c, 0x94, 0x2a, \ 0x5f, 0x1d, 0xcc, 0xaf, 0x65, 0x03, 0x3b, 0x39, \ 0x39, 0xd4, 0x47, 0xd9, 0xfc, 0xc4, 0x7b, 0x0b, \ 0x16, 0xd8, 0xe9, 0x01, 0xfc, 0xec, 0x3f, 0x8c, \ 0x1b, 0xc0, 0xc6, 0xac, 0x0b, 0x5d, 0x74, 0xc7, \ 0xbb, 0x03, 0x05, 0x69, 0x17, 0xe9, 0x98, 0x1a, \ 0x19, 0xb9, 0x09, 0x5c, 0xa1, 0xf4, 0xab, 0x9f, \ 0x00, 0x91, 0x00, 0x03, 0x0f, 0x02, 0x7c, 0x28, \ 0x0f, 0x8a, 0xf9, 0xed, 0x1d, 0x29, 0x3c, 0xf6, \ 0xcc, 0x2f, 0x04, 0x6d, 0x9a, 0xd6, 0x62, 0xb4, \ 0xa9, 0x6e, 0xb1, 0xca, 0xca, 0xac, 0x5e, 0x05, \ 0x3e, 0x83, 0x91, 0x47, 0x7c, 0x1f, 0x8b, 0x60, \ 0x01, 0xde, 0x65, 0x3a, 0xbf, 0xf2, 0xaa, 0xbb, \ 0x55, 0x98, 0x86, 0x91, 0x7e, 0xad, 0x3b, 0x36, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_CREDMAN_META \ 0x00, 0x12, 0x00, 0x04, 0x90, 0x00, 0x07, 0x00, \ 0xa2, 0x01, 0x00, 0x02, 0x18, 0x19, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_CREDMAN_RPLIST \ 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x37, 0x00, \ 0xa3, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6a, 0x79, \ 0x75, 0x62, 0x69, 0x63, 0x6f, 0x2e, 0x63, 0x6f, \ 0x6d, 0x04, 0x58, 0x20, 0x37, 0x82, 0x09, 0xb7, \ 0x2d, 0xef, 0xcb, 0xa9, 0x1d, 0xcb, 0xf8, 0x54, \ 0xed, 0xb4, 0xda, 0xa6, 0x48, 0x82, 0x8a, 0x2c, \ 0xbd, 0x18, 0x0a, 0xfc, 0x77, 0xa7, 0x44, 0x34, \ 0x65, 0x5a, 0x1c, 0x7d, 0x05, 0x03, 0x00, 0x00, \ 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x36, 0x00, \ 0xa2, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6b, 0x79, \ 0x75, 0x62, 0x69, 0x6b, 0x65, 0x79, 0x2e, 0x6f, \ 0x72, 0x67, 0x04, 0x58, 0x20, 0x12, 0x6b, 0xba, \ 0x6a, 0x2d, 0x7a, 0x81, 0x84, 0x25, 0x7b, 0x74, \ 0xdd, 0x1d, 0xdd, 0x46, 0xb6, 0x2a, 0x8c, 0xa2, \ 0xa7, 0x83, 0xfe, 0xdb, 0x5b, 0x19, 0x48, 0x73, \ 0x55, 0xb7, 0xe3, 0x46, 0x09, 0x00, 0x00, 0x00, \ 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x37, 0x00, \ 0xa2, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6c, 0x77, \ 0x65, 0x62, 0x61, 0x75, 0x74, 0x68, 0x6e, 0x2e, \ 0x64, 0x65, 0x76, 0x04, 0x58, 0x20, 0xd6, 0x32, \ 0x7d, 0x8c, 0x6a, 0x5d, 0xe6, 0xae, 0x0e, 0x33, \ 0xd0, 0xa3, 0x31, 0xfb, 0x67, 0x77, 0xb9, 0x4e, \ 0xf4, 0x73, 0x19, 0xfe, 0x7e, 0xfd, 0xfa, 0x82, \ 0x70, 0x8e, 0x1f, 0xbb, 0xa2, 0x55, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_CREDMAN_RKLIST \ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc5, 0x00, \ 0xa5, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \ 0xe4, 0xe1, 0x06, 0x31, 0xde, 0x00, 0x0f, 0x4f, \ 0x12, 0x6e, 0xc9, 0x68, 0x2d, 0x43, 0x3f, 0xf1, \ 0x02, 0x2c, 0x6e, 0xe6, 0x96, 0x10, 0xbf, 0x73, \ 0x35, 0xc9, 0x20, 0x27, 0x06, 0xba, 0x39, 0x09, \ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x19, \ 0xf7, 0x78, 0x0c, 0xa0, 0xbc, 0xb9, 0xa6, 0xd5, \ 0x1e, 0xd7, 0x87, 0xfb, 0x6c, 0x80, 0x03, 0x64, \ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02, \ 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x81, \ 0x6c, 0xdd, 0x8c, 0x8f, 0x8c, 0xc8, 0x43, 0xa7, \ 0xbb, 0x79, 0x51, 0x09, 0xb1, 0xdf, 0xbe, 0xc4, \ 0xa5, 0x54, 0x16, 0x9e, 0x58, 0x56, 0xb3, 0x0b, \ 0x34, 0x4f, 0xa5, 0x6c, 0x05, 0xa2, 0x21, 0x22, \ 0x58, 0x20, 0xcd, 0xc2, 0x0c, 0x99, 0x83, 0x5a, \ 0x61, 0x73, 0xd8, 0xe0, 0x74, 0x23, 0x46, 0x64, \ 0x00, 0x15, 0x00, 0x04, 0x02, 0x39, 0x4c, 0xb0, \ 0xf4, 0x6c, 0x0a, 0x37, 0x72, 0xaa, 0xa8, 0xea, \ 0x58, 0xd3, 0xd4, 0xe0, 0x51, 0xb2, 0x28, 0x09, \ 0x05, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xa0, 0x00, \ 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \ 0x56, 0xa1, 0x3c, 0x06, 0x2b, 0xad, 0xa2, 0x21, \ 0x7d, 0xcd, 0x91, 0x08, 0x47, 0xa8, 0x8a, 0x06, \ 0x06, 0xf6, 0x66, 0x91, 0xf6, 0xeb, 0x89, 0xe4, \ 0xdf, 0x26, 0xbc, 0x46, 0x59, 0xc3, 0x7d, 0xc0, \ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0xd8, \ 0x27, 0x4b, 0x25, 0xed, 0x19, 0xef, 0x11, 0xaf, \ 0xa6, 0x89, 0x7b, 0x84, 0x50, 0xe7, 0x62, 0x64, \ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa4, 0x01, 0x01, \ 0x03, 0x27, 0x20, 0x06, 0x21, 0x58, 0x20, 0x8d, \ 0xfe, 0x45, 0xd5, 0x7d, 0xb6, 0x17, 0xab, 0x86, \ 0x2d, 0x32, 0xf6, 0x85, 0xf0, 0x92, 0x76, 0xb7, \ 0xce, 0x73, 0xca, 0x4e, 0x0e, 0xfd, 0xd5, 0xdb, \ 0x2a, 0x1d, 0x55, 0x90, 0x96, 0x52, 0xc2, 0x0a, \ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xa0, 0x00, \ 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \ 0x04, 0x0e, 0x0f, 0xa0, 0xcd, 0x60, 0x35, 0x9a, \ 0xba, 0x47, 0x0c, 0x10, 0xb6, 0x82, 0x6e, 0x2f, \ 0x66, 0xb9, 0xa7, 0xcf, 0xd8, 0x47, 0xb4, 0x3d, \ 0xfd, 0x77, 0x1a, 0x38, 0x22, 0xa1, 0xda, 0xa5, \ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x00, \ 0x5d, 0xdf, 0xef, 0xe2, 0xf3, 0x06, 0xb2, 0xa5, \ 0x46, 0x4d, 0x98, 0xbc, 0x14, 0x65, 0xc1, 0x64, \ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa4, 0x01, 0x01, \ 0x03, 0x27, 0x20, 0x06, 0x21, 0x58, 0x20, 0x72, \ 0x79, 0x14, 0x69, 0xdf, 0xcb, 0x64, 0x75, 0xee, \ 0xd4, 0x45, 0x94, 0xbc, 0x48, 0x4d, 0x2a, 0x9f, \ 0xc9, 0xf4, 0xb5, 0x1b, 0x05, 0xa6, 0x5b, 0x54, \ 0x9a, 0xac, 0x6c, 0x2e, 0xc6, 0x90, 0x62, 0x0a, \ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc3, 0x00, \ 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \ 0xce, 0x32, 0xd8, 0x79, 0xdd, 0x86, 0xa2, 0x42, \ 0x7c, 0xc3, 0xe1, 0x95, 0x12, 0x93, 0x1a, 0x03, \ 0xe6, 0x70, 0xb8, 0xff, 0xcd, 0xa5, 0xdf, 0x15, \ 0xfc, 0x88, 0x2a, 0xf5, 0x44, 0xf1, 0x33, 0x9c, \ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x0a, \ 0x26, 0x5b, 0x7e, 0x1a, 0x2a, 0xba, 0x70, 0x5f, \ 0x18, 0x26, 0x14, 0xb2, 0x71, 0xca, 0x98, 0x64, \ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02, \ 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x8b, \ 0x48, 0xf0, 0x69, 0xfb, 0x22, 0xfb, 0xf3, 0x86, \ 0x57, 0x7c, 0xdd, 0x82, 0x2c, 0x1c, 0x0c, 0xdc, \ 0x27, 0xe2, 0x6a, 0x4c, 0x1a, 0x10, 0x04, 0x27, \ 0x51, 0x3e, 0x2a, 0x9d, 0x3a, 0xb6, 0xb5, 0x22, \ 0x58, 0x20, 0x70, 0xfe, 0x91, 0x67, 0x64, 0x53, \ 0x63, 0x83, 0x72, 0x31, 0xe9, 0xe5, 0x20, 0xb7, \ 0x00, 0x15, 0x00, 0x04, 0x02, 0xee, 0xc9, 0xfb, \ 0x63, 0xd7, 0xe4, 0x76, 0x39, 0x80, 0x82, 0x74, \ 0xb8, 0xfa, 0x67, 0xf5, 0x1b, 0x8f, 0xe0, 0x0a, \ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc3, 0x00, \ 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20, \ 0xf9, 0xa3, 0x67, 0xbf, 0x5e, 0x80, 0x95, 0xdb, \ 0x4c, 0xc5, 0x8f, 0x65, 0x36, 0xc5, 0xaf, 0xdd, \ 0x90, 0x2e, 0x62, 0x68, 0x67, 0x9c, 0xa2, 0x26, \ 0x2f, 0x2a, 0xf9, 0x3a, 0xda, 0x15, 0xf2, 0x27, \ 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f, \ 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61, \ 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69, \ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, \ 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e, \ 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0xfb, \ 0xa6, 0xbe, 0xc1, 0x01, 0xf6, 0x7a, 0x81, 0xf9, \ 0xcd, 0x6d, 0x20, 0x41, 0x7a, 0x1c, 0x40, 0x64, \ 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, \ 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08, \ 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02, \ 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xda, \ 0x2b, 0x53, 0xc3, 0xbe, 0x48, 0xf8, 0xab, 0xbd, \ 0x06, 0x28, 0x46, 0xfa, 0x35, 0xab, 0xf9, 0xc5, \ 0x2e, 0xfd, 0x3c, 0x38, 0x88, 0xb3, 0xe1, 0xa7, \ 0xc5, 0xc6, 0xed, 0x72, 0x54, 0x37, 0x93, 0x22, \ 0x58, 0x20, 0x12, 0x82, 0x32, 0x2d, 0xab, 0xbc, \ 0x64, 0xb3, 0xed, 0xcc, 0xd5, 0x22, 0xec, 0x79, \ 0x00, 0x15, 0x00, 0x04, 0x02, 0x4b, 0xe2, 0x4d, \ 0x0c, 0x4b, 0x8d, 0x31, 0x4c, 0xb4, 0x0f, 0xd4, \ 0xa9, 0xbe, 0x0c, 0xab, 0x9e, 0x0a, 0xc9, 0x0a, \ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_BIO_INFO \ 0x00, 0x10, 0x00, 0x04, 0x90, 0x00, 0x06, 0x00, \ 0xa2, 0x02, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_BIO_ENROLL \ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x0a, 0x00, \ 0xa3, 0x04, 0x42, 0x68, 0x96, 0x05, 0x00, 0x06, \ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x06, 0x00, \ 0xa2, 0x05, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x06, 0x00, \ 0xa2, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_BIO_ENUM \ 0x00, 0x10, 0x00, 0x0f, 0x90, 0x00, 0x2e, 0x00, \ 0xa1, 0x07, 0x83, 0xa2, 0x01, 0x42, 0xce, 0xa3, \ 0x02, 0x67, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, \ 0x31, 0xa2, 0x01, 0x42, 0xbf, 0x5e, 0x02, 0x67, \ 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x32, 0xa2, \ 0x01, 0x42, 0x5e, 0xd2, 0x02, 0x67, 0x66, 0x69, \ 0x6e, 0x67, 0x65, 0x72, 0x33, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY \ 0x89, 0xc9, 0x8d, 0x28, 0x90, 0x01, 0xe6, 0x00, \ 0xa1, 0x01, 0x59, 0x01, 0xe0, 0x81, 0xa3, 0x01, \ 0x59, 0x01, 0xb8, 0xb3, 0x26, 0x24, 0x99, 0xde, \ 0x06, 0x3f, 0xca, 0xde, 0x98, 0x8d, 0x9d, 0xc5, \ 0x3f, 0x26, 0x6c, 0xc7, 0x40, 0x93, 0xc4, 0x88, \ 0x06, 0x51, 0x4f, 0xb9, 0x61, 0xf2, 0xc9, 0x8d, \ 0xbc, 0xce, 0x79, 0x08, 0xec, 0x90, 0xc5, 0x5b, \ 0xe5, 0x0a, 0x72, 0x08, 0x7b, 0xe1, 0xf9, 0x16, \ 0x89, 0xc9, 0x8d, 0x28, 0x00, 0x06, 0x8b, 0x76, \ 0x32, 0xa0, 0xae, 0x55, 0xb2, 0x39, 0x71, 0xce, \ 0x34, 0x4b, 0x6e, 0x6b, 0x89, 0xa6, 0x5e, 0x69, \ 0x07, 0xac, 0xf6, 0x01, 0x3c, 0xba, 0x45, 0x7a, \ 0x75, 0x25, 0x3a, 0xbd, 0x95, 0x22, 0x9d, 0xc3, \ 0xe4, 0x42, 0x31, 0x5c, 0xb5, 0xf4, 0x64, 0x6a, \ 0x56, 0x1d, 0xab, 0xc7, 0x6e, 0x96, 0x75, 0xe7, \ 0xb3, 0x22, 0x0b, 0x82, 0xac, 0x57, 0x78, 0xdf, \ 0x89, 0xc9, 0x8d, 0x28, 0x01, 0x57, 0x06, 0xc5, \ 0x4b, 0x61, 0x0b, 0x4d, 0xa1, 0x66, 0xa0, 0x89, \ 0xad, 0x19, 0x8f, 0xd8, 0x96, 0x55, 0x22, 0x5f, \ 0xca, 0x2e, 0xc1, 0xd7, 0xbd, 0xa1, 0x83, 0x66, \ 0x4d, 0x85, 0xcb, 0x01, 0x60, 0x3f, 0xf7, 0xf7, \ 0xa3, 0x7a, 0xfa, 0x99, 0xa0, 0x1e, 0x25, 0x90, \ 0xd0, 0xd0, 0x3b, 0x54, 0x90, 0x77, 0x94, 0xa6, \ 0x88, 0xea, 0xc3, 0x6b, 0xa0, 0x59, 0x5e, 0x69, \ 0x89, 0xc9, 0x8d, 0x28, 0x02, 0x78, 0x0b, 0x2b, \ 0xab, 0x5b, 0x04, 0x2f, 0x78, 0x15, 0x86, 0x2b, \ 0x0f, 0x63, 0xb2, 0xd7, 0xc9, 0xe9, 0xac, 0x0e, \ 0xbc, 0x17, 0xe4, 0x19, 0x88, 0xe0, 0xe6, 0x13, \ 0xf8, 0x15, 0x08, 0xa7, 0xe1, 0x6e, 0x71, 0x5c, \ 0xef, 0x3e, 0xc1, 0x0f, 0x74, 0xdb, 0xdc, 0x52, \ 0x9c, 0xfc, 0xe9, 0xa9, 0xf3, 0x0d, 0x52, 0xbc, \ 0x0c, 0xe8, 0xba, 0xd1, 0x76, 0x46, 0x87, 0xb5, \ 0x89, 0xc9, 0x8d, 0x28, 0x03, 0x30, 0xe6, 0x9d, \ 0xa1, 0x2b, 0xa5, 0x9e, 0x3b, 0x86, 0xb3, 0x5f, \ 0xe3, 0x81, 0xa6, 0x76, 0x32, 0x9d, 0xf9, 0xc5, \ 0x07, 0x93, 0xb3, 0xdf, 0x64, 0xe2, 0x78, 0x9c, \ 0x00, 0xc7, 0x86, 0x79, 0xd6, 0x67, 0xa2, 0xfb, \ 0xf2, 0x8d, 0xea, 0xe9, 0xc8, 0xfc, 0x43, 0xd2, \ 0x0f, 0x2f, 0x7d, 0x9d, 0xd3, 0x8f, 0x9c, 0xdd, \ 0xa2, 0x9f, 0x42, 0x76, 0x40, 0xcc, 0x4a, 0xd0, \ 0x89, 0xc9, 0x8d, 0x28, 0x04, 0xb4, 0x87, 0x18, \ 0x06, 0xc3, 0xc7, 0x89, 0x98, 0x72, 0xcc, 0x1a, \ 0xd1, 0xd8, 0x78, 0xb9, 0x75, 0x0b, 0x92, 0xe3, \ 0xcc, 0xed, 0x38, 0x39, 0x4b, 0xa9, 0xcf, 0x30, \ 0xd6, 0xb5, 0xa1, 0x3f, 0xfa, 0x4f, 0x29, 0x99, \ 0xa9, 0x03, 0x77, 0xf6, 0x53, 0xfa, 0xd8, 0x32, \ 0xce, 0xf4, 0xf6, 0x0a, 0x3c, 0xe8, 0x9c, 0x3d, \ 0xaa, 0xe0, 0x7b, 0x2c, 0xa5, 0x28, 0xe1, 0xdd, \ 0x89, 0xc9, 0x8d, 0x28, 0x05, 0x51, 0xbf, 0xe1, \ 0xd4, 0xf5, 0x5e, 0x38, 0x2c, 0xec, 0xab, 0xdd, \ 0xb8, 0x5c, 0x13, 0x43, 0x62, 0xc2, 0xb6, 0x02, \ 0x18, 0xce, 0x9a, 0x62, 0x67, 0x6a, 0xeb, 0x99, \ 0xf6, 0x2f, 0xf1, 0xf1, 0xec, 0x3e, 0x74, 0xfa, \ 0xf8, 0x16, 0x43, 0xea, 0x1e, 0xef, 0x5d, 0x37, \ 0x6c, 0x13, 0xf9, 0x7f, 0x65, 0x09, 0xab, 0x60, \ 0x38, 0xda, 0x0f, 0xe7, 0xfa, 0x9e, 0x17, 0x10, \ 0x89, 0xc9, 0x8d, 0x28, 0x06, 0xdc, 0x4c, 0x4d, \ 0xae, 0x5c, 0xb4, 0x0d, 0x6b, 0x05, 0x6d, 0x25, \ 0x3f, 0x78, 0x5d, 0xf3, 0x34, 0x33, 0xa4, 0x89, \ 0x34, 0x0e, 0x88, 0x66, 0x40, 0x57, 0x6b, 0x34, \ 0x83, 0xfd, 0x39, 0xe7, 0xfb, 0x84, 0x09, 0xb3, \ 0x16, 0x8f, 0x80, 0xdf, 0x1b, 0xe0, 0x02, 0x4c, \ 0xde, 0x31, 0x2a, 0x32, 0x58, 0x5b, 0xa3, 0x23, \ 0x8e, 0x2a, 0xa6, 0xaf, 0x03, 0x19, 0x02, 0x7a, \ 0x89, 0xc9, 0x8d, 0x28, 0x07, 0xf8, 0xbf, 0xa6, \ 0xad, 0xf9, 0xd1, 0xdc, 0xbd, 0x6e, 0xb3, 0xc1, \ 0xfb, 0x65, 0xd8, 0x5f, 0x2e, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #endif /* _WIREDATA_FIDO2_H */ libfido2-1.10.0/fuzz/wiredata_u2f.h000066400000000000000000000161661417126203300170170ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _WIREDATA_U2F_H #define _WIREDATA_U2F_H #define WIREDATA_CTAP_U2F_6985 \ 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69, \ 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_U2F_AUTH \ 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x4e, 0x01, \ 0x00, 0x00, 0x00, 0x2c, 0x30, 0x45, 0x02, 0x20, \ 0x1c, 0xf5, 0x7c, 0xf6, 0xde, 0xbe, 0xe9, 0x86, \ 0xee, 0x97, 0xb7, 0x64, 0xa3, 0x4e, 0x7a, 0x70, \ 0x85, 0xd0, 0x66, 0xf9, 0xf0, 0xcd, 0x04, 0x5d, \ 0x97, 0xf2, 0x3c, 0x22, 0xe3, 0x0e, 0x61, 0xc8, \ 0x02, 0x21, 0x00, 0x97, 0xef, 0xae, 0x36, 0xe6, \ 0x17, 0x9f, 0x5e, 0x2d, 0xd7, 0x8c, 0x34, 0xa7, \ 0x00, 0x00, 0x99, 0x01, 0x00, 0xa1, 0xe9, 0xfb, \ 0x8f, 0x86, 0x8c, 0xe3, 0x1e, 0xde, 0x3f, 0x4e, \ 0x1b, 0xe1, 0x2f, 0x8f, 0x2f, 0xca, 0x42, 0x26, \ 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #define WIREDATA_CTAP_U2F_REGISTER \ 0x00, 0x00, 0x99, 0x01, 0x83, 0x03, 0x1e, 0x05, \ 0x04, 0x9f, 0xa0, 0xf9, 0x0d, 0x4c, 0xf4, 0xae, \ 0x96, 0x3c, 0xb7, 0x46, 0xb7, 0x5c, 0x9d, 0x8b, \ 0x48, 0x19, 0xdf, 0xc4, 0xad, 0xea, 0xb2, 0x70, \ 0x58, 0x72, 0xd9, 0xce, 0x75, 0xf5, 0xe6, 0x8e, \ 0x0f, 0x9c, 0x0e, 0x2e, 0x62, 0x3e, 0x91, 0xd3, \ 0x7b, 0x97, 0x46, 0x60, 0xb9, 0x57, 0x13, 0x97, \ 0x26, 0xae, 0x0f, 0xb3, 0x8f, 0x2e, 0x9b, 0x3f, \ 0x00, 0x00, 0x99, 0x01, 0x00, 0xa5, 0x55, 0xec, \ 0x8c, 0x25, 0x7c, 0x65, 0xb7, 0x09, 0x40, 0x48, \ 0xae, 0xa8, 0xcb, 0xa1, 0x91, 0xac, 0x40, 0x24, \ 0xf2, 0x34, 0x6e, 0x3a, 0x8f, 0xa5, 0xb7, 0x48, \ 0x54, 0x6e, 0xfb, 0xf4, 0x37, 0x88, 0x69, 0x79, \ 0x6f, 0x12, 0xc1, 0x32, 0xdf, 0x15, 0x5d, 0x6e, \ 0x82, 0x54, 0xc0, 0x6e, 0x56, 0x4f, 0x3a, 0x9c, \ 0xc3, 0x96, 0x7a, 0xde, 0xa5, 0xfe, 0xec, 0xd1, \ 0x00, 0x00, 0x99, 0x01, 0x01, 0x5a, 0x21, 0x85, \ 0x0e, 0x25, 0x7b, 0x8d, 0x6e, 0x1d, 0x32, 0x29, \ 0xdb, 0x21, 0xb0, 0xa3, 0x30, 0x82, 0x02, 0x4f, \ 0x30, 0x82, 0x01, 0x37, 0xa0, 0x03, 0x02, 0x01, \ 0x02, 0x02, 0x04, 0x2a, 0xd9, 0x6a, 0xf3, 0x30, \ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, \ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x2e, \ 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, \ 0x00, 0x00, 0x99, 0x01, 0x02, 0x03, 0x13, 0x23, \ 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, \ 0x32, 0x46, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, \ 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, \ 0x6c, 0x20, 0x34, 0x35, 0x37, 0x32, 0x30, 0x30, \ 0x36, 0x33, 0x31, 0x30, 0x20, 0x17, 0x0d, 0x31, \ 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, \ 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, \ 0x00, 0x00, 0x99, 0x01, 0x03, 0x35, 0x30, 0x30, \ 0x39, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, \ 0x30, 0x5a, 0x30, 0x31, 0x31, 0x2f, 0x30, 0x2d, \ 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x26, 0x59, \ 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, \ 0x46, 0x20, 0x45, 0x45, 0x20, 0x53, 0x65, 0x72, \ 0x69, 0x61, 0x6c, 0x20, 0x32, 0x33, 0x39, 0x32, \ 0x35, 0x37, 0x33, 0x34, 0x35, 0x31, 0x36, 0x35, \ 0x00, 0x00, 0x99, 0x01, 0x04, 0x35, 0x30, 0x33, \ 0x38, 0x37, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, \ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, \ 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, \ 0x07, 0x03, 0x42, 0x00, 0x04, 0x2f, 0xe1, 0xa2, \ 0x3e, 0xbf, 0xa5, 0x5b, 0x3e, 0x46, 0x1d, 0x59, \ 0xa4, 0x35, 0x22, 0xd7, 0x97, 0x48, 0x98, 0x1c, \ 0xba, 0x6d, 0x28, 0x9a, 0x98, 0xf1, 0xbd, 0x7d, \ 0x00, 0x00, 0x99, 0x01, 0x05, 0xff, 0x65, 0x66, \ 0x80, 0xdb, 0xbb, 0xed, 0xbc, 0x2b, 0xae, 0x60, \ 0x7e, 0x6e, 0xf7, 0x72, 0xf5, 0x76, 0xb0, 0x4d, \ 0x54, 0xc4, 0xe5, 0xf3, 0x2f, 0x59, 0x6f, 0x26, \ 0xe6, 0x11, 0x15, 0xc7, 0x27, 0x2c, 0xf6, 0xca, \ 0x75, 0x94, 0xa3, 0x3b, 0x30, 0x39, 0x30, 0x22, \ 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, \ 0xc4, 0x0a, 0x02, 0x04, 0x15, 0x31, 0x2e, 0x33, \ 0x00, 0x00, 0x99, 0x01, 0x06, 0x2e, 0x36, 0x2e, \ 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x31, \ 0x34, 0x38, 0x32, 0x2e, 0x31, 0x2e, 0x32, 0x30, \ 0x13, 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, \ 0x82, 0xe5, 0x1c, 0x02, 0x01, 0x01, 0x04, 0x04, \ 0x03, 0x02, 0x04, 0x30, 0x30, 0x0d, 0x06, 0x09, \ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, \ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, \ 0x00, 0x00, 0x99, 0x01, 0x07, 0x85, 0x6a, 0xfa, \ 0x8b, 0xcf, 0x4f, 0x3f, 0x62, 0x5f, 0x29, 0x1b, \ 0xc1, 0x15, 0x8e, 0x3c, 0x7e, 0xbd, 0x25, 0x52, \ 0xbc, 0xf7, 0x57, 0x07, 0x53, 0xf5, 0x12, 0x1d, \ 0xa6, 0xa5, 0x4d, 0x24, 0xcc, 0xcf, 0xae, 0x27, \ 0xce, 0xd6, 0xab, 0x31, 0x12, 0x8c, 0x29, 0x7e, \ 0x5b, 0x5b, 0x89, 0x05, 0xdd, 0xa0, 0x20, 0x17, \ 0x93, 0x1f, 0x1f, 0x5f, 0x59, 0x25, 0x93, 0x59, \ 0x00, 0x00, 0x99, 0x01, 0x08, 0x51, 0xfc, 0x00, \ 0x4b, 0xcb, 0xe2, 0x0a, 0xdd, 0x7d, 0x8d, 0x05, \ 0x2f, 0x95, 0x43, 0xb3, 0x49, 0x6c, 0x15, 0xb8, \ 0x31, 0x0e, 0x10, 0xcb, 0xd9, 0xbb, 0x05, 0x38, \ 0x27, 0x4f, 0x58, 0x3e, 0xad, 0x1f, 0x45, 0x12, \ 0x88, 0xc3, 0xea, 0x76, 0xd0, 0x70, 0xad, 0x44, \ 0xe5, 0x3a, 0xfe, 0xa8, 0xf2, 0x2d, 0x1f, 0x73, \ 0x62, 0x5f, 0xf2, 0xd5, 0x89, 0xfe, 0x30, 0xdf, \ 0x00, 0x00, 0x99, 0x01, 0x09, 0x26, 0x62, 0xcb, \ 0x7c, 0xbb, 0x7c, 0x99, 0x61, 0x80, 0xad, 0xcf, \ 0xa9, 0x8a, 0x4d, 0x01, 0x2c, 0xf3, 0x13, 0x46, \ 0xcd, 0x11, 0x74, 0x6a, 0x58, 0x48, 0xe8, 0xbe, \ 0xed, 0xf3, 0xe3, 0x0c, 0xcb, 0xd9, 0xc1, 0xdd, \ 0x22, 0x16, 0x71, 0xb2, 0x83, 0x88, 0x61, 0xf6, \ 0x5a, 0x45, 0x36, 0x23, 0xb5, 0x18, 0xd5, 0x56, \ 0x7f, 0xa8, 0xf0, 0xa3, 0xce, 0x10, 0x5d, 0xf4, \ 0x00, 0x00, 0x99, 0x01, 0x0a, 0xf1, 0x39, 0x53, \ 0xe1, 0x14, 0xea, 0x59, 0xe0, 0xa7, 0xf2, 0xfe, \ 0x66, 0x88, 0x67, 0x43, 0x2e, 0x52, 0xfd, 0x6a, \ 0x2f, 0x64, 0xf7, 0x3c, 0x48, 0xcd, 0x9b, 0x38, \ 0xf2, 0xdf, 0xba, 0x2c, 0x7a, 0x4b, 0x3b, 0x11, \ 0x28, 0xdf, 0x26, 0xd6, 0x6a, 0x24, 0xf8, 0x95, \ 0xdd, 0xa0, 0xb6, 0x11, 0x80, 0xf4, 0x14, 0x4f, \ 0x6b, 0x70, 0x75, 0xc3, 0x18, 0xa4, 0x9a, 0xe0, \ 0x00, 0x00, 0x99, 0x01, 0x0b, 0x8b, 0x58, 0xd3, \ 0x6a, 0xdb, 0x1e, 0x30, 0x53, 0x67, 0x2b, 0x17, \ 0xc5, 0xa1, 0x9f, 0x7f, 0x0a, 0x22, 0xf1, 0x0e, \ 0x94, 0x30, 0x44, 0x02, 0x20, 0x07, 0x5c, 0x4f, \ 0xd2, 0x83, 0xb6, 0x9f, 0x0a, 0x4a, 0x4d, 0x4b, \ 0x08, 0x35, 0xeb, 0xc0, 0x7e, 0x4a, 0x14, 0x2e, \ 0xc7, 0x8c, 0xd6, 0x64, 0x2f, 0xd3, 0x1e, 0xcc, \ 0xb5, 0xe8, 0x42, 0xea, 0xf6, 0x02, 0x20, 0x6b, \ 0x00, 0x00, 0x99, 0x01, 0x0c, 0x5a, 0xba, 0x4a, \ 0xc8, 0xd7, 0x89, 0xcc, 0x77, 0xe6, 0xb9, 0xa3, \ 0x34, 0xea, 0x06, 0x85, 0x72, 0xc6, 0x28, 0xa8, \ 0x7a, 0xaa, 0x19, 0x88, 0x34, 0xbb, 0xdc, 0x64, \ 0x90, 0x0a, 0xdb, 0x39, 0x90, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 #endif /* !_WIREDATA_U2F_H */ libfido2-1.10.0/fuzz/wrap.c000066400000000000000000000175371417126203300154120ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #include #include #include #include #include "mutator_aux.h" extern int prng_up; /* * Build wrappers around functions of interest, and have them fail * in a pseudo-random manner. */ #define WRAP(type, name, args, retval, param, prob) \ extern type __wrap_##name args; \ extern type __real_##name args; \ type __wrap_##name args { \ if (prng_up && uniform_random(400) < (prob)) { \ return (retval); \ } \ \ return (__real_##name param); \ } WRAP(void *, malloc, (size_t size), NULL, (size), 1 ) WRAP(void *, calloc, (size_t nmemb, size_t size), NULL, (nmemb, size), 1 ) WRAP(void *, realloc, (void *ptr, size_t size), NULL, (ptr, size), 1 ) WRAP(char *, strdup, (const char *s), NULL, (s), 1 ) WRAP(int, EVP_Cipher, (EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, unsigned int inl), -1, (ctx, out, in, inl), 1 ) WRAP(int, EVP_CIPHER_CTX_ctrl, (EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr), 0, (ctx, type, arg, ptr), 1 ) WRAP(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, (void), NULL, (), 1 ) WRAP(int, EVP_CipherInit, (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, const unsigned char *key, const unsigned char *iv, int enc), 0, (ctx, cipher, key, iv, enc), 1 ) WRAP(RSA *, EVP_PKEY_get0_RSA, (EVP_PKEY *pkey), NULL, (pkey), 1 ) WRAP(EC_KEY *, EVP_PKEY_get0_EC_KEY, (EVP_PKEY *pkey), NULL, (pkey), 1 ) WRAP(int, EVP_PKEY_get_raw_public_key, (const EVP_PKEY *pkey, unsigned char *pub, size_t *len), 0, (pkey, pub, len), 1 ) WRAP(EVP_MD_CTX *, EVP_MD_CTX_new, (void), NULL, (), 1 ) WRAP(int, EVP_DigestVerifyInit, (EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey), 0, (ctx, pctx, type, e, pkey), 1 ) WRAP(int, EVP_DigestInit_ex, (EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl), 0, (ctx, type, impl), 1 ) WRAP(int, EVP_DigestUpdate, (EVP_MD_CTX *ctx, const void *data, size_t count), 0, (ctx, data, count), 1 ) WRAP(int, EVP_DigestFinal_ex, (EVP_MD_CTX *ctx, unsigned char *md, unsigned int *isize), 0, (ctx, md, isize), 1 ) WRAP(BIGNUM *, BN_bin2bn, (const unsigned char *s, int len, BIGNUM *ret), NULL, (s, len, ret), 1 ) WRAP(int, BN_bn2bin, (const BIGNUM *a, unsigned char *to), -1, (a, to), 1 ) WRAP(BIGNUM *, BN_CTX_get, (BN_CTX *ctx), NULL, (ctx), 1 ) WRAP(BN_CTX *, BN_CTX_new, (void), NULL, (), 1 ) WRAP(BIGNUM *, BN_new, (void), NULL, (), 1 ) WRAP(RSA *, RSA_new, (void), NULL, (), 1 ) WRAP(int, RSA_set0_key, (RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d), 0, (r, n, e, d), 1 ) WRAP(int, RSA_pkey_ctx_ctrl, (EVP_PKEY_CTX *ctx, int optype, int cmd, int p1, void *p2), -1, (ctx, optype, cmd, p1, p2), 1 ) WRAP(EC_KEY *, EC_KEY_new_by_curve_name, (int nid), NULL, (nid), 1 ) WRAP(const EC_GROUP *, EC_KEY_get0_group, (const EC_KEY *key), NULL, (key), 1 ) WRAP(const BIGNUM *, EC_KEY_get0_private_key, (const EC_KEY *key), NULL, (key), 1 ) WRAP(EC_POINT *, EC_POINT_new, (const EC_GROUP *group), NULL, (group), 1 ) WRAP(int, EC_POINT_get_affine_coordinates_GFp, (const EC_GROUP *group, const EC_POINT *p, BIGNUM *x, BIGNUM *y, BN_CTX *ctx), 0, (group, p, x, y, ctx), 1 ) WRAP(EVP_PKEY *, EVP_PKEY_new, (void), NULL, (), 1 ) WRAP(int, EVP_PKEY_assign, (EVP_PKEY *pkey, int type, void *key), 0, (pkey, type, key), 1 ) WRAP(int, EVP_PKEY_keygen_init, (EVP_PKEY_CTX *ctx), 0, (ctx), 1 ) WRAP(int, EVP_PKEY_keygen, (EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey), 0, (ctx, ppkey), 1 ) WRAP(int, EVP_PKEY_paramgen_init, (EVP_PKEY_CTX *ctx), 0, (ctx), 1 ) WRAP(int, EVP_PKEY_paramgen, (EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey), 0, (ctx, ppkey), 1 ) WRAP(EVP_PKEY *, EVP_PKEY_new_raw_public_key, (int type, ENGINE *e, const unsigned char *key, size_t keylen), NULL, (type, e, key, keylen), 1 ) WRAP(EVP_PKEY_CTX *, EVP_PKEY_CTX_new, (EVP_PKEY *pkey, ENGINE *e), NULL, (pkey, e), 1 ) WRAP(EVP_PKEY_CTX *, EVP_PKEY_CTX_new_id, (int id, ENGINE *e), NULL, (id, e), 1 ) WRAP(int, EVP_PKEY_derive, (EVP_PKEY_CTX *ctx, unsigned char *key, size_t *pkeylen), 0, (ctx, key, pkeylen), 1 ) WRAP(int, EVP_PKEY_derive_init, (EVP_PKEY_CTX *ctx), 0, (ctx), 1 ) WRAP(int, EVP_PKEY_derive_set_peer, (EVP_PKEY_CTX *ctx, EVP_PKEY *peer), 0, (ctx, peer), 1 ) WRAP(int, EVP_PKEY_verify_init, (EVP_PKEY_CTX *ctx), 0, (ctx), 1 ) WRAP(int, EVP_PKEY_CTX_ctrl, (EVP_PKEY_CTX *ctx, int keytype, int optype, int cmd, int p1, void *p2), -1, (ctx, keytype, optype, cmd, p1, p2), 1 ) WRAP(const EVP_MD *, EVP_sha1, (void), NULL, (), 1 ) WRAP(const EVP_MD *, EVP_sha256, (void), NULL, (), 1 ) WRAP(const EVP_CIPHER *, EVP_aes_256_cbc, (void), NULL, (), 1 ) WRAP(const EVP_CIPHER *, EVP_aes_256_gcm, (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 *, SHA1, (const unsigned char *d, size_t n, unsigned char *md), NULL, (d, n, md), 1 ) WRAP(unsigned char *, SHA256, (const unsigned char *d, size_t n, unsigned char *md), NULL, (d, n, md), 1 ) WRAP(cbor_item_t *, cbor_build_string, (const char *val), NULL, (val), 1 ) WRAP(cbor_item_t *, cbor_build_bytestring, (cbor_data handle, size_t length), NULL, (handle, length), 1 ) WRAP(cbor_item_t *, cbor_build_bool, (bool value), NULL, (value), 1 ) WRAP(cbor_item_t *, cbor_build_negint8, (uint8_t value), NULL, (value), 1 ) WRAP(cbor_item_t *, cbor_build_negint16, (uint16_t value), NULL, (value), 1 ) WRAP(cbor_item_t *, cbor_load, (cbor_data source, size_t source_size, struct cbor_load_result *result), NULL, (source, source_size, result), 1 ) WRAP(cbor_item_t *, cbor_build_uint8, (uint8_t value), NULL, (value), 1 ) WRAP(cbor_item_t *, cbor_build_uint16, (uint16_t value), NULL, (value), 1 ) WRAP(cbor_item_t *, cbor_build_uint32, (uint32_t value), NULL, (value), 1 ) WRAP(cbor_item_t *, cbor_build_uint64, (uint64_t value), NULL, (value), 1 ) WRAP(struct cbor_pair *, cbor_map_handle, (const cbor_item_t *item), NULL, (item), 1 ) WRAP(cbor_item_t **, cbor_array_handle, (const cbor_item_t *item), NULL, (item), 1 ) WRAP(bool, cbor_array_push, (cbor_item_t *array, cbor_item_t *pushee), false, (array, pushee), 1 ) WRAP(bool, cbor_map_add, (cbor_item_t *item, struct cbor_pair pair), false, (item, pair), 1 ) WRAP(cbor_item_t *, cbor_new_definite_map, (size_t size), NULL, (size), 1 ) WRAP(cbor_item_t *, cbor_new_definite_array, (size_t size), NULL, (size), 1 ) WRAP(cbor_item_t *, cbor_new_definite_bytestring, (void), NULL, (), 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, int *ms), -1, (d, cmd, buf, count, ms), 1 ) WRAP(int, bind, (int sockfd, const struct sockaddr *addr, socklen_t addrlen), -1, (sockfd, addr, addrlen), 1 ) libfido2-1.10.0/fuzz/wrapped.sym000066400000000000000000000030721417126203300164560ustar00rootroot00000000000000bind BN_bin2bn BN_bn2bin BN_CTX_get BN_CTX_new BN_new calloc cbor_array_handle cbor_array_push cbor_build_bool cbor_build_bytestring cbor_build_negint16 cbor_build_negint8 cbor_build_string cbor_build_uint16 cbor_build_uint32 cbor_build_uint64 cbor_build_uint8 cbor_load cbor_map_add cbor_map_handle cbor_new_definite_array cbor_new_definite_bytestring cbor_new_definite_map cbor_serialize_alloc clock_gettime EC_KEY_get0_group EC_KEY_get0_private_key EC_KEY_new_by_curve_name EC_POINT_get_affine_coordinates_GFp EC_POINT_new EVP_aes_256_cbc EVP_aes_256_gcm EVP_Cipher EVP_CIPHER_CTX_ctrl EVP_CIPHER_CTX_new EVP_CipherInit EVP_DigestFinal_ex EVP_DigestInit_ex EVP_DigestUpdate EVP_DigestVerifyInit EVP_MD_CTX_new EVP_PKEY_assign EVP_PKEY_CTX_ctrl EVP_PKEY_CTX_new EVP_PKEY_CTX_new_id EVP_PKEY_derive EVP_PKEY_derive_init EVP_PKEY_derive_set_peer EVP_PKEY_get0_EC_KEY EVP_PKEY_get0_RSA EVP_PKEY_get_raw_public_key EVP_PKEY_keygen EVP_PKEY_keygen_init EVP_PKEY_new EVP_PKEY_new_raw_public_key EVP_PKEY_paramgen EVP_PKEY_paramgen_init EVP_PKEY_verify_init EVP_sha1 EVP_sha256 fido_tx HMAC HMAC_CTX_new HMAC_Final HMAC_Init_ex HMAC_Update ioctl malloc realloc RSA_new RSA_pkey_ctx_ctrl RSA_set0_key SHA1 SHA256 strdup udev_device_get_devnode udev_device_get_parent_with_subsystem_devtype udev_device_get_sysattr_value udev_device_get_sysnum udev_device_new_from_syspath udev_device_unref udev_enumerate_add_match_subsystem udev_enumerate_get_list_entry udev_enumerate_new udev_enumerate_scan_devices udev_enumerate_unref udev_list_entry_get_name udev_list_entry_get_next udev_new udev_unref usleep libfido2-1.10.0/man/000077500000000000000000000000001417126203300140355ustar00rootroot00000000000000libfido2-1.10.0/man/CMakeLists.txt000066400000000000000000000336071417126203300166060ustar00rootroot00000000000000# Copyright (c) 2018 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. find_program(MANDOC_PATH mandoc) find_program(GZIP_PATH gzip) message(STATUS "MANDOC_PATH: ${MANDOC_PATH}") message(STATUS "GZIP_PATH: ${GZIP_PATH}") list(APPEND MAN_SOURCES eddsa_pk_new.3 es256_pk_new.3 fido2-assert.1 fido2-cred.1 fido2-token.1 fido_init.3 fido_assert_new.3 fido_assert_allow_cred.3 fido_assert_set_authdata.3 fido_assert_verify.3 fido_bio_dev_get_info.3 fido_bio_enroll_new.3 fido_bio_info_new.3 fido_bio_template.3 fido_cbor_info_new.3 fido_cred_new.3 fido_cred_exclude.3 fido_credman_metadata_new.3 fido_cred_set_authdata.3 fido_cred_verify.3 fido_dev_enable_entattest.3 fido_dev_get_assert.3 fido_dev_get_touch_begin.3 fido_dev_info_manifest.3 fido_dev_largeblob_get.3 fido_dev_make_cred.3 fido_dev_open.3 fido_dev_set_io_functions.3 fido_dev_set_pin.3 fido_strerr.3 rs256_pk_new.3 ) list(APPEND MAN_ALIAS eddsa_pk_new eddsa_pk_free eddsa_pk_new eddsa_pk_from_EVP_PKEY 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_EVP_PKEY es256_pk_new es256_pk_from_ptr es256_pk_new es256_pk_to_EVP_PKEY fido_assert_new fido_assert_authdata_len fido_assert_new fido_assert_authdata_ptr fido_assert_new fido_assert_blob_len fido_assert_new fido_assert_blob_ptr fido_assert_new fido_assert_clientdata_hash_len fido_assert_new fido_assert_clientdata_hash_ptr fido_assert_new fido_assert_count fido_assert_new fido_assert_flags fido_assert_new fido_assert_free fido_assert_new fido_assert_hmac_secret_len fido_assert_new fido_assert_hmac_secret_ptr fido_assert_new fido_assert_id_len fido_assert_new fido_assert_id_ptr fido_assert_new fido_assert_largeblob_key_len fido_assert_new fido_assert_largeblob_key_ptr fido_assert_new fido_assert_rp_id fido_assert_new fido_assert_sigcount fido_assert_new fido_assert_sig_len fido_assert_new fido_assert_sig_ptr fido_assert_new fido_assert_user_display_name fido_assert_new fido_assert_user_icon fido_assert_new fido_assert_user_id_len fido_assert_new fido_assert_user_id_ptr fido_assert_new fido_assert_user_name fido_assert_set_authdata fido_assert_set_authdata_raw fido_assert_set_authdata fido_assert_set_clientdata fido_assert_set_authdata fido_assert_set_clientdata_hash fido_assert_set_authdata fido_assert_set_count fido_assert_set_authdata fido_assert_set_extensions fido_assert_set_authdata fido_assert_set_hmac_salt fido_assert_set_authdata fido_assert_set_hmac_secret fido_assert_set_authdata fido_assert_set_rp fido_assert_set_authdata fido_assert_set_sig fido_assert_set_authdata fido_assert_set_up fido_assert_set_authdata fido_assert_set_uv fido_bio_dev_get_info fido_bio_dev_enroll_begin fido_bio_dev_get_info fido_bio_dev_enroll_cancel fido_bio_dev_get_info fido_bio_dev_enroll_continue fido_bio_dev_get_info fido_bio_dev_enroll_remove fido_bio_dev_get_info fido_bio_dev_get_template_array fido_bio_dev_get_info fido_bio_dev_set_template_name fido_bio_enroll_new fido_bio_enroll_free fido_bio_enroll_new fido_bio_enroll_last_status fido_bio_enroll_new fido_bio_enroll_remaining_samples fido_bio_info_new fido_bio_info_free fido_bio_info_new fido_bio_info_max_samples fido_bio_info_new fido_bio_info_type fido_bio_template fido_bio_template_array_count fido_bio_template fido_bio_template_array_free fido_bio_template fido_bio_template_array_new fido_bio_template fido_bio_template_free fido_bio_template fido_bio_template_id_len fido_bio_template fido_bio_template_id_ptr fido_bio_template fido_bio_template_name fido_bio_template fido_bio_template_new fido_bio_template fido_bio_template_set_id fido_bio_template fido_bio_template_set_name fido_cbor_info_new fido_cbor_info_aaguid_len fido_cbor_info_new fido_cbor_info_aaguid_ptr fido_cbor_info_new fido_cbor_info_algorithm_cose fido_cbor_info_new fido_cbor_info_algorithm_count fido_cbor_info_new fido_cbor_info_algorithm_type fido_cbor_info_new fido_cbor_info_extensions_len fido_cbor_info_new fido_cbor_info_extensions_ptr fido_cbor_info_new fido_cbor_info_free fido_cbor_info_new fido_cbor_info_maxmsgsiz fido_cbor_info_new fido_cbor_info_maxcredbloblen fido_cbor_info_new fido_cbor_info_maxcredcntlst fido_cbor_info_new fido_cbor_info_maxcredidlen fido_cbor_info_new fido_cbor_info_fwversion fido_cbor_info_new fido_cbor_info_options_len fido_cbor_info_new fido_cbor_info_options_name_ptr fido_cbor_info_new fido_cbor_info_options_value_ptr fido_cbor_info_new fido_cbor_info_protocols_len fido_cbor_info_new fido_cbor_info_protocols_ptr fido_cbor_info_new fido_cbor_info_transports_len fido_cbor_info_new fido_cbor_info_transports_ptr fido_cbor_info_new fido_cbor_info_versions_len fido_cbor_info_new fido_cbor_info_versions_ptr fido_cbor_info_new fido_dev_get_cbor_info fido_cred_new fido_cred_aaguid_len fido_cred_new fido_cred_aaguid_ptr fido_cred_new fido_cred_attstmt_len fido_cred_new fido_cred_attstmt_ptr fido_cred_new fido_cred_authdata_len fido_cred_new fido_cred_authdata_ptr fido_cred_new fido_cred_authdata_raw_len fido_cred_new fido_cred_authdata_raw_ptr fido_cred_new fido_cred_clientdata_hash_len fido_cred_new fido_cred_clientdata_hash_ptr fido_cred_new fido_cred_display_name fido_cred_new fido_cred_flags fido_cred_new fido_cred_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_largeblob_key_len fido_cred_new fido_cred_largeblob_key_ptr fido_cred_new fido_cred_pin_minlen fido_cred_new fido_cred_prot fido_cred_new fido_cred_pubkey_len fido_cred_new fido_cred_pubkey_ptr fido_cred_new fido_cred_rp_id fido_cred_new fido_cred_rp_name fido_cred_new fido_cred_sigcount fido_cred_new fido_cred_sig_len fido_cred_new fido_cred_sig_ptr fido_cred_new fido_cred_type fido_cred_new fido_cred_user_id_len fido_cred_new fido_cred_user_id_ptr fido_cred_new fido_cred_user_name fido_cred_new fido_cred_x5c_len fido_cred_new fido_cred_x5c_ptr fido_cred_verify fido_cred_verify_self fido_credman_metadata_new fido_credman_del_dev_rk fido_credman_metadata_new fido_credman_get_dev_metadata fido_credman_metadata_new fido_credman_get_dev_rk fido_credman_metadata_new fido_credman_get_dev_rp fido_credman_metadata_new fido_credman_metadata_free fido_credman_metadata_new fido_credman_rk fido_credman_metadata_new fido_credman_rk_count fido_credman_metadata_new fido_credman_rk_existing fido_credman_metadata_new fido_credman_rk_free fido_credman_metadata_new fido_credman_rk_new fido_credman_metadata_new fido_credman_rk_remaining fido_credman_metadata_new fido_credman_rp_count fido_credman_metadata_new fido_credman_rp_free fido_credman_metadata_new fido_credman_rp_id fido_credman_metadata_new fido_credman_rp_id_hash_len fido_credman_metadata_new fido_credman_rp_id_hash_ptr fido_credman_metadata_new fido_credman_rp_name fido_credman_metadata_new fido_credman_rp_new fido_credman_metadata_new fido_credman_set_dev_rk fido_cred_set_authdata fido_cred_set_attstmt fido_cred_set_authdata fido_cred_set_authdata_raw fido_cred_set_authdata fido_cred_set_blob fido_cred_set_authdata fido_cred_set_clientdata fido_cred_set_authdata fido_cred_set_clientdata_hash fido_cred_set_authdata fido_cred_set_extensions fido_cred_set_authdata fido_cred_set_fmt fido_cred_set_authdata fido_cred_set_id fido_cred_set_authdata fido_cred_set_pin_minlen fido_cred_set_authdata fido_cred_set_prot fido_cred_set_authdata fido_cred_set_rk fido_cred_set_authdata fido_cred_set_rp fido_cred_set_authdata fido_cred_set_sig fido_cred_set_authdata fido_cred_set_type fido_cred_set_authdata fido_cred_set_user fido_cred_set_authdata fido_cred_set_uv fido_cred_set_authdata fido_cred_set_x509 fido_dev_enable_entattest fido_dev_toggle_always_uv fido_dev_enable_entattest fido_dev_force_pin_change fido_dev_enable_entattest fido_dev_set_pin_minlen fido_dev_enable_entattest fido_dev_set_pin_minlen_rpid fido_dev_get_touch_begin fido_dev_get_touch_status fido_dev_info_manifest fido_dev_info_free fido_dev_info_manifest fido_dev_info_manufacturer_string fido_dev_info_manifest fido_dev_info_new fido_dev_info_manifest fido_dev_info_path fido_dev_info_manifest fido_dev_info_product fido_dev_info_manifest fido_dev_info_product_string fido_dev_info_manifest fido_dev_info_ptr fido_dev_info_manifest fido_dev_info_set 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_has_pin fido_dev_open fido_dev_has_uv fido_dev_open fido_dev_is_fido2 fido_dev_open fido_dev_is_winhello fido_dev_open fido_dev_major fido_dev_open fido_dev_minor fido_dev_open fido_dev_new fido_dev_open fido_dev_new_with_info fido_dev_open fido_dev_open_with_info fido_dev_open fido_dev_protocol fido_dev_open fido_dev_supports_cred_prot fido_dev_open fido_dev_supports_credman fido_dev_open fido_dev_supports_permissions fido_dev_open fido_dev_supports_pin fido_dev_open fido_dev_supports_uv fido_dev_set_pin fido_dev_get_retry_count fido_dev_set_pin fido_dev_get_uv_retry_count fido_dev_set_pin fido_dev_reset fido_dev_set_io_functions fido_dev_io_handle fido_dev_set_io_functions fido_dev_set_sigmask fido_dev_set_io_functions fido_dev_set_timeout fido_dev_set_io_functions fido_dev_set_transport_functions fido_dev_largeblob_get fido_dev_largeblob_set fido_dev_largeblob_get fido_dev_largeblob_remove fido_dev_largeblob_get fido_dev_largeblob_get_array fido_dev_largeblob_get fido_dev_largeblob_set_array fido_init fido_set_log_handler rs256_pk_new rs256_pk_free rs256_pk_new rs256_pk_from_ptr rs256_pk_new rs256_pk_from_EVP_PKEY rs256_pk_new rs256_pk_from_RSA rs256_pk_new rs256_pk_to_EVP_PKEY ) list(LENGTH MAN_ALIAS MAN_ALIAS_LEN) math(EXPR MAN_ALIAS_MAX "${MAN_ALIAS_LEN} - 2") # man_copy foreach(f ${MAN_SOURCES}) add_custom_command(OUTPUT ${f} COMMAND cp -f ${CMAKE_SOURCE_DIR}/man/${f} . DEPENDS ${f}) list(APPEND COPY_FILES ${f}) endforeach() # man_lint foreach(f ${MAN_SOURCES}) add_custom_command(OUTPUT ${f}.lint COMMAND mandoc -T lint -W warning ${f} > ${f}.lint DEPENDS ${f}) list(APPEND LINT_FILES ${f}.lint) endforeach() # man_html foreach(f ${MAN_SOURCES}) string(REGEX REPLACE ".[13]" "" g ${f}) add_custom_command(OUTPUT ${g}.html COMMAND mandoc -T html -O man="%N.html",style=style.css -I os="Yubico AB" ${f} > ${g}.html DEPENDS ${f}) list(APPEND HTML_FILES ${g}.html) endforeach() # man_html_partial foreach(f ${MAN_SOURCES}) string(REGEX REPLACE ".[13]" "" g ${f}) add_custom_command(OUTPUT ${g}.partial COMMAND cat ${CMAKE_SOURCE_DIR}/man/dyc.css > ${g}.partial COMMAND mandoc -T html -O man="%N.html",fragment ${f} >> ${g}.partial DEPENDS ${f}) list(APPEND HTML_PARTIAL_FILES ${g}.partial) endforeach() # man_gzip foreach(f ${MAN_SOURCES}) add_custom_command(OUTPUT ${f}.gz COMMAND gzip -cn ${f} > ${f}.gz DEPENDS ${f}) list(APPEND GZ_FILES ${f}.gz) endforeach() macro(define_symlink_target NAME EXT) foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2) math(EXPR j "${i} + 1") list(GET MAN_ALIAS ${i} SRC) list(GET MAN_ALIAS ${j} DST) add_custom_command(OUTPUT ${DST}.${EXT} COMMAND ln -sf ${SRC}.${EXT} ${DST}.${EXT}) list(APPEND ${NAME}_LINK_FILES ${DST}.${EXT}) endforeach() add_custom_target(${NAME} DEPENDS ${${NAME}_LINK_FILES}) endmacro() add_custom_target(man_copy DEPENDS ${COPY_FILES}) add_custom_target(man_lint DEPENDS ${LINT_FILES}) add_custom_target(man_html DEPENDS ${HTML_FILES}) add_custom_target(man_html_partial DEPENDS ${HTML_PARTIAL_FILES}) add_custom_target(man_gzip DEPENDS ${GZ_FILES}) define_symlink_target(man_symlink 3) define_symlink_target(man_symlink_html html) define_symlink_target(man_symlink_html_partial partial) define_symlink_target(man_symlink_gzip 3.gz) add_dependencies(man_symlink man_copy) add_dependencies(man_lint man_symlink) add_dependencies(man_html man_lint) add_dependencies(man_symlink_html man_html) add_dependencies(man_html_partial man_lint) add_dependencies(man_symlink_html_partial man_html_partial) add_custom_target(man ALL) if(MANDOC_PATH) add_dependencies(man man_symlink_html) add_dependencies(man_gzip man_lint) install(FILES ${CMAKE_SOURCE_DIR}/man/style.css DESTINATION "${CMAKE_INSTALL_DOCDIR}/html") foreach(f ${MAN_SOURCES}) string(REGEX REPLACE ".[13]" "" f ${f}) install(FILES ${CMAKE_BINARY_DIR}/man/${f}.html DESTINATION "${CMAKE_INSTALL_DOCDIR}/html") endforeach() foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2) math(EXPR j "${i} + 1") list(GET MAN_ALIAS ${j} DST) install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.html DESTINATION "${CMAKE_INSTALL_DOCDIR}/html") endforeach() endif() if(GZIP_PATH) add_dependencies(man_gzip man_copy) add_dependencies(man_symlink_gzip man_gzip) add_dependencies(man man_symlink_gzip) foreach(f ${MAN_SOURCES}) if (${f} MATCHES ".1$") install(FILES ${CMAKE_BINARY_DIR}/man/${f}.gz DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") elseif(${f} MATCHES ".3$") install(FILES ${CMAKE_BINARY_DIR}/man/${f}.gz DESTINATION "${CMAKE_INSTALL_MANDIR}/man3") endif() endforeach() foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2) math(EXPR j "${i} + 1") list(GET MAN_ALIAS ${j} DST) install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.3.gz DESTINATION "${CMAKE_INSTALL_MANDIR}/man3") endforeach() elseif(NOT MSVC) add_dependencies(man man_symlink) foreach(f ${MAN_SOURCES}) if (${f} MATCHES ".1$") install(FILES ${CMAKE_BINARY_DIR}/man/${f} DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") elseif(${f} MATCHES ".3$") install(FILES ${CMAKE_BINARY_DIR}/man/${f} DESTINATION "${CMAKE_INSTALL_MANDIR}/man3") endif() endforeach() foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2) math(EXPR j "${i} + 1") list(GET MAN_ALIAS ${j} DST) install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.3 DESTINATION "${CMAKE_INSTALL_MANDIR}/man3") endforeach() endif() libfido2-1.10.0/man/NOTES000066400000000000000000000003521417126203300146500ustar00rootroot00000000000000To generate .partial files for https://developers.yubico.com/: $ make -C build man_symlink_html_partial $ (cd build/man && pax -p p -r -w *.partial /tmp/partial) Use mandoc 1.14.4. Otherwise, adjust dyc.css to mandoc's HTML output. libfido2-1.10.0/man/check.sh000077500000000000000000000033451417126203300154560ustar00rootroot00000000000000#!/bin/sh -u # Copyright (c) 2022 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. T=$(mktemp -d) || exit 1 find . -maxdepth 1 -type f -name '*.3' -print0 > "$T/files" xargs -0 awk '/^.Sh NAME/,/^.Nd/' < "$T/files" | \ awk '/^.Nm/ { print $2 }' | sort -u > "$T/Nm" xargs -0 awk '/^.Fn/ { print $2 }' < "$T/files" | sort -u > "$T/Fn" (cd "$T" && diff -u Nm Fn) cut -c2- ../src/export.llvm | sort > "$T/exports" (cd "$T" && diff -u Nm exports) awk '/^list\(APPEND MAN_SOURCES/,/^\)/' CMakeLists.txt | \ awk '/.3$/ { print $1 }' | sort > "$T/listed_sources" xargs -0 -n1 basename < "$T/files" | sort > "$T/actual_sources" (cd "$T" && diff -u listed_sources actual_sources) awk '/^list\(APPEND MAN_ALIAS/,/^\)/' CMakeLists.txt | \ sed '1d;$d' | awk '{ print $1, $2 }' | sort > "$T/listed_aliases" xargs -0 grep -o "^.Fn [A-Za-z0-9_]* \"" < "$T/files" | \ cut -c3- | sed 's/\.3:\.Fn//;s/ "//' | awk '$1 != $2' | \ sort > "$T/actual_aliases" (cd "$T" && diff -u listed_aliases actual_aliases) xargs -0 grep -hB1 "^.Fn [A-Za-z0-9_]* \"" < "$T/files" | \ sed -E 's/^.F[tn] //;s/\*[^"\*]+"/\*"/g;s/ [^" \*]+"/"/g;/^--$/d' | \ paste -d " " - - | sed 's/\* /\*/' | sort > "$T/documented_prototypes" while read -r f; do awk "/\/\*/ { next } /$f\(/,/;/" ../src/fido.h ../src/fido/*.h | \ sed -E 's/^[ ]+//;s/[ ]+/ /' | tr '\n' ' ' | \ sed 's/(/ "/;s/, /" "/g;s/);/"/;s/ $/\n/' done < "$T/exports" | sort > "$T/actual_prototypes" (cd "$T" && diff -u documented_prototypes actual_prototypes) (cd "$T" && rm files Nm Fn exports listed_sources actual_sources \ listed_aliases actual_aliases documented_prototypes actual_prototypes) rmdir -- "$T" libfido2-1.10.0/man/dyc.css000066400000000000000000000011151417126203300153240ustar00rootroot00000000000000 libfido2-1.10.0/man/eddsa_pk_new.3000066400000000000000000000044361417126203300165530ustar00rootroot00000000000000.\" 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 FIDO2 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_EVP_PKEY 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 libfido2-1.10.0/man/es256_pk_new.3000066400000000000000000000051751417126203300163400ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: 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_EVP_PKEY , .Nm es256_pk_from_ptr , .Nm es256_pk_to_EVP_PKEY .Nd FIDO2 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_EVP_PKEY "es256_pk_t *pk" "const EVP_PKEY *pkey" .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_EVP_PKEY function fills .Fa pk with the contents of .Fa pkey . No references to .Fa pkey are kept. .Pp The .Fn es256_pk_from_ptr function fills .Fa pk with the contents of .Fa ptr , where .Fa ptr points to .Fa len bytes. The .Fa ptr pointer may point to an uncompressed point, or to the concatenation of the x and y coordinates. No references to .Fa ptr are kept. .Pp The .Fn es256_pk_to_EVP_PKEY function converts .Fa pk to a newly allocated .Fa EVP_PKEY type with a reference count of 1. No internal references to the returned pointer are kept. If an error occurs, .Fn es256_pk_to_EVP_PKEY returns NULL. .Sh RETURN VALUES The .Fn es256_pk_from_EC_KEY , .Fn es256_pk_from_EVP_PKEY , 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 libfido2-1.10.0/man/fido2-assert.1000066400000000000000000000122141417126203300164210ustar00rootroot00000000000000.\" 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 FIDO2 assertion .Sh SYNOPSIS .Nm .Fl G .Op Fl bdhpruv .Op Fl t Ar option .Op Fl i Ar input_file .Op Fl o Ar output_file .Ar device .Nm .Fl V .Op Fl dhpv .Op Fl i Ar input_file .Ar key_file .Op Ar type .Sh DESCRIPTION .Nm gets or verifies a FIDO2 assertion. .Pp The input of .Nm is defined by the parameters of the assertion to be obtained/verified. See the .Sx INPUT FORMAT section for details. .Pp The output of .Nm is defined by the result of the selected operation. See the .Sx OUTPUT FORMAT section for details. .Pp If an assertion is successfully obtained or verified, .Nm exits 0. Otherwise, .Nm exits 1. .Pp The options are as follows: .Bl -tag -width Ds .It Fl G Tells .Nm to obtain a new assertion from .Ar device . .It Fl V Tells .Nm to verify an assertion using the PEM-encoded public key in .Ar key_file of type .Ar type , where .Ar type may be .Em es256 (denoting ECDSA over NIST P-256 with SHA-256), .Em rs256 (denoting 2048-bit RSA with PKCS#1.5 padding and SHA-256), or .Em eddsa (denoting EDDSA over Curve25519 with SHA-512). If .Ar type is not specified, .Em es256 is assumed. .It Fl b Request the credential's .Dq largeBlobKey , a 32-byte symmetric key associated with the asserted credential. .It Fl h If obtaining an assertion, enable the FIDO2 hmac-secret extension. If verifying an assertion, check whether the extension data bit was signed by the authenticator. .It Fl d Causes .Nm to emit debugging output on .Em stderr . .It Fl i Ar input_file Tells .Nm to read the parameters of the assertion from .Ar input_file instead of .Em stdin . .It Fl o Ar output_file Tells .Nm to write output on .Ar output_file instead of .Em stdout . .It Fl p If obtaining an assertion, request user presence. If verifying an assertion, check whether the user presence bit was signed by the authenticator. .It Fl r Obtain an assertion using a resident credential. If .Fl r is specified, .Nm will not expect a credential id in its input, and may output multiple assertions. Resident credentials are called .Dq discoverable credentials in CTAP 2.1. .It Fl t Ar option Toggles a key/value .Ar option , where .Ar option is a string of the form .Dq key=value . The options supported at present are: .Bl -tag -width Ds .It Cm up Ns = Ns Ar true|false Asks the authenticator for user presence to be enabled or disabled. .It Cm uv Ns = Ns Ar true|false Asks the authenticator for user verification to be enabled or disabled. .It Cm pin Ns = Ns Ar true|false Tells .Nm whether to prompt for a PIN and request user verification. .El .Pp The .Fl t option may be specified multiple times. .It Fl u Obtain an assertion using U2F. By default, .Nm will use FIDO2 if supported by the authenticator, and fallback to U2F otherwise. .It Fl v If obtaining an assertion, prompt the user for a PIN and request user verification from the authenticator. If verifying an assertion, check whether the user verification bit was signed by the authenticator. .El .Pp If a .Em tty is available, .Nm will use it to obtain the PIN. Otherwise, .Em stdin is used. .Sh INPUT FORMAT The input of .Nm consists of base64 blobs and UTF-8 strings separated by newline characters ('\\n'). .Pp When obtaining an assertion, .Nm expects its input to consist of: .Pp .Bl -enum -offset indent -compact .It client data hash (base64 blob); .It relying party id (UTF-8 string); .It credential id, if credential not resident (base64 blob); .It hmac salt, if the FIDO2 hmac-secret extension is enabled (base64 blob); .El .Pp When verifying an assertion, .Nm expects its input to consist of: .Pp .Bl -enum -offset indent -compact .It client data hash (base64 blob); .It relying party id (UTF-8 string); .It authenticator data (base64 blob); .It assertion signature (base64 blob); .El .Pp UTF-8 strings passed to .Nm must not contain embedded newline or NUL characters. .Sh OUTPUT FORMAT The output of .Nm consists of base64 blobs and UTF-8 strings separated by newline characters ('\\n'). .Pp For each generated assertion, .Nm outputs: .Pp .Bl -enum -offset indent -compact .It client data hash (base64 blob); .It relying party id (UTF-8 string); .It authenticator data (base64 blob); .It assertion signature (base64 blob); .It user id, if credential resident (base64 blob); .It hmac secret, if the FIDO2 hmac-secret extension is enabled (base64 blob); .It the credential's associated 32-byte symmetric key .Pq Dq largeBlobKey , if requested (base64 blob). .El .Pp When verifying an assertion, .Nm produces no output. .Sh EXAMPLES Assuming .Pa cred contains a .Em es256 credential created according to the steps outlined in .Xr fido2-cred 1 , obtain an assertion from an authenticator at .Pa /dev/hidraw5 and verify it: .Pp .Dl $ echo assertion challenge | openssl sha256 -binary | base64 > assert_param .Dl $ echo relying party >> assert_param .Dl $ head -1 cred >> assert_param .Dl $ tail -n +2 cred > pubkey .Dl $ fido2-assert -G -i assert_param /dev/hidraw5 | fido2-assert -V pubkey es256 .Sh SEE ALSO .Xr fido2-cred 1 , .Xr fido2-token 1 libfido2-1.10.0/man/fido2-cred.1000066400000000000000000000124221417126203300160360ustar00rootroot00000000000000.\" 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 FIDO2 credential .Sh SYNOPSIS .Nm .Fl M .Op Fl bdhqruv .Op Fl c Ar cred_protect .Op Fl i Ar input_file .Op Fl o Ar output_file .Ar device .Op Ar type .Nm .Fl V .Op Fl dhv .Op Fl c Ar cred_protect .Op Fl i Ar input_file .Op Fl o Ar output_file .Op Ar type .Sh DESCRIPTION .Nm makes or verifies a FIDO2 credential. .Pp A credential .Ar type may be .Em es256 (denoting ECDSA over NIST P-256 with SHA-256), .Em rs256 (denoting 2048-bit RSA with PKCS#1.5 padding and SHA-256), or .Em eddsa (denoting EDDSA over Curve25519 with SHA-512). If .Ar type is not specified, .Em es256 is assumed. .Pp When making a credential, the authenticator may require the user to authenticate with a PIN. If the .Fl q option is not specified, .Nm will prompt the user for the PIN. If a .Em tty is available, .Nm will use it to obtain the PIN. Otherwise, .Em stdin is used. .Pp The input of .Nm is defined by the parameters of the credential to be made/verified. See the .Sx INPUT FORMAT section for details. .Pp The output of .Nm is defined by the result of the selected operation. See the .Sx OUTPUT FORMAT section for details. .Pp If a credential is successfully created or verified, .Nm exits 0. Otherwise, .Nm exits 1. .Pp The options are as follows: .Bl -tag -width Ds .It Fl M Tells .Nm to make a new credential on .Ar device . .It Fl V Tells .Nm to verify a credential. .It Fl b Request the credential's .Dq largeBlobKey , a 32-byte symmetric key associated with the generated credential. .It Fl c Ar cred_protect If making a credential, set the credential's protection level to .Ar cred_protect , where .Ar cred_protect is the credential's protection level in decimal notation. Please refer to .In fido/param.h for the set of possible values. If verifying a credential, check whether the credential's protection level was signed by the authenticator as .Ar cred_protect . .It Fl d Causes .Nm to emit debugging output on .Em stderr . .It Fl h If making a credential, enable the FIDO2 hmac-secret extension. If verifying a credential, check whether the extension data bit was signed by the authenticator. .It Fl i Ar input_file Tells .Nm to read the parameters of the credential from .Ar input_file instead of .Em stdin . .It Fl o Ar output_file Tells .Nm to write output on .Ar output_file instead of .Em stdout . .It Fl q Tells .Nm to be quiet. If a PIN is required and .Fl q is specified, .Nm will fail. .It Fl r Create a resident credential. Resident credentials are called .Dq discoverable credentials in CTAP 2.1. .It Fl u Create a U2F credential. By default, .Nm will use FIDO2 if supported by the authenticator, and fallback to U2F otherwise. .It Fl v If making a credential, request user verification. If verifying a credential, check whether the user verification bit was signed by the authenticator. .El .Sh INPUT FORMAT The input of .Nm consists of base64 blobs and UTF-8 strings separated by newline characters ('\\n'). .Pp When making a credential, .Nm expects its input to consist of: .Pp .Bl -enum -offset indent -compact .It client data hash (base64 blob); .It relying party id (UTF-8 string); .It user name (UTF-8 string); .It user id (base64 blob). .El .Pp When verifying a credential, .Nm expects its input to consist of: .Pp .Bl -enum -offset indent -compact .It client data hash (base64 blob); .It relying party id (UTF-8 string); .It credential format (UTF-8 string); .It authenticator data (base64 blob); .It credential id (base64 blob); .It attestation signature (base64 blob); .It attestation certificate (optional, base64 blob). .El .Pp UTF-8 strings passed to .Nm must not contain embedded newline or NUL characters. .Sh OUTPUT FORMAT The output of .Nm consists of base64 blobs, UTF-8 strings, and PEM-encoded public keys separated by newline characters ('\\n'). .Pp Upon the successful generation of a credential, .Nm outputs: .Pp .Bl -enum -offset indent -compact .It client data hash (base64 blob); .It relying party id (UTF-8 string); .It credential format (UTF-8 string); .It authenticator data (base64 blob); .It credential id (base64 blob); .It attestation signature (base64 blob); .It attestation certificate, if present (base64 blob). .It the credential's associated 32-byte symmetric key .Pq Dq largeBlobKey , if present (base64 blob). .El .Pp Upon the successful verification of a credential, .Nm outputs: .Pp .Bl -enum -offset indent -compact .It credential id (base64 blob); .It PEM-encoded credential key. .El .Sh EXAMPLES Create a new .Em es256 credential on .Pa /dev/hidraw5 , verify it, and save the id and the public key of the credential in .Em cred : .Pp .Dl $ echo credential challenge | openssl sha256 -binary | base64 > cred_param .Dl $ echo relying party >> cred_param .Dl $ echo user name >> cred_param .Dl $ dd if=/dev/urandom bs=1 count=32 | base64 >> cred_param .Dl $ fido2-cred -M -i cred_param /dev/hidraw5 | fido2-cred -V -o cred .Sh SEE ALSO .Xr fido2-assert 1 , .Xr fido2-token 1 .Sh CAVEATS Please note that .Nm handles Basic Attestation and Self Attestation transparently. In the case of Basic Attestation, the validity of the authenticator's attestation certificate is .Em not verified. libfido2-1.10.0/man/fido2-token.1000066400000000000000000000170111417126203300162400ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: September 13 2019 $ .Dt FIDO2-TOKEN 1 .Os .Sh NAME .Nm fido2-token .Nd find and manage a FIDO2 authenticator .Sh SYNOPSIS .Nm .Fl C .Op Fl d .Ar device .Nm .Fl D .Op Fl d .Fl i .Ar cred_id .Ar device .Nm .Fl D .Fl b .Op Fl d .Fl k Ar key_path .Ar device .Nm .Fl D .Fl b .Op Fl d .Fl n Ar rp_id .Op Fl i Ar cred_id .Ar device .Nm .Fl D .Fl e .Op Fl d .Fl i .Ar template_id .Ar device .Nm .Fl D .Fl u .Op Fl d .Ar device .Nm .Fl G .Fl b .Op Fl d .Fl k Ar key_path .Ar blob_path .Ar device .Nm .Fl G .Fl b .Op Fl d .Fl n Ar rp_id .Op Fl i Ar cred_id .Ar blob_path .Ar device .Nm .Fl I .Op Fl cd .Op Fl k Ar rp_id Fl i Ar cred_id .Ar device .Nm .Fl L .Op Fl bder .Op Fl k Ar rp_id .Op device .Nm .Fl R .Op Fl d .Ar device .Nm .Fl S .Op Fl adefu .Ar device .Nm .Fl S .Op Fl d .Fl i Ar template_id .Fl n Ar template_name .Ar device .Nm .Fl S .Op Fl d .Fl l Ar pin_length .Ar device .Nm .Fl S .Fl b .Op Fl d .Fl k Ar key_path .Ar blob_path .Ar device .Nm .Fl S .Fl b .Op Fl d .Fl n Ar rp_id .Op Fl i Ar cred_id .Ar blob_path .Ar device .Nm .Fl S .Fl c .Op Fl d .Fl i Ar cred_id .Fl k Ar user_id .Fl n Ar name .Fl p Ar display_name .Ar device .Nm .Fl S .Fl m .Ar rp_id .Ar device .Nm .Fl V .Sh DESCRIPTION .Nm manages a FIDO2 authenticator. .Pp The options are as follows: .Bl -tag -width Ds .It Fl C Ar device Changes the PIN of .Ar device . The user will be prompted for the current and new PINs. .It Fl D Fl i Ar id Ar device Deletes the resident credential specified by .Ar id from .Ar device , where .Ar id is the credential's base64-encoded id. The user will be prompted for the PIN. .It Fl D Fl b Fl k Ar key_path Ar device Deletes a .Dq largeBlob encrypted with .Ar key_path from .Ar device , where .Ar key_path must hold the blob's base64-encoded encryption key. A PIN or equivalent user-verification gesture is required. .It Fl D Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar device Deletes a .Dq largeBlob corresponding to .Ar rp_id from .Ar device . If .Ar rp_id has multiple credentials enrolled on .Ar device , the credential ID must be specified using .Fl i Ar cred_id , where .Ar cred_id is a base64-encoded blob. A PIN or equivalent user-verification gesture is required. .It Fl D Fl e Fl i Ar id Ar device Deletes the biometric enrollment specified by .Ar id from .Ar device , where .Ar id is the enrollment's template base64-encoded id. The user will be prompted for the PIN. .It Fl D Fl u Ar device Disables the CTAP 2.1 .Dq user verification always feature on .Ar device . .It Fl G Fl b Fl k Ar key_path Ar blob_path Ar device Gets a CTAP 2.1 .Dq largeBlob encrypted with .Ar key_path from .Ar device , where .Ar key_path must hold the blob's base64-encoded encryption key. The blob is written to .Ar blob_path . A PIN or equivalent user-verification gesture is required. .It Fl G Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar blob_path Ar device Gets a CTAP 2.1 .Dq largeBlob associated with .Ar rp_id from .Ar device . If .Ar rp_id has multiple credentials enrolled on .Ar device , the credential ID must be specified using .Fl i Ar cred_id , where .Ar cred_id is a base64-encoded blob. The blob is written to .Ar blob_path . A PIN or equivalent user-verification gesture is required. .It Fl I Ar device Retrieves information on .Ar device . .It Fl I Fl c Ar device Retrieves resident credential metadata from .Ar device . The user will be prompted for the PIN. .It Fl I Fl k Ar rp_id Fl i Ar cred_id Ar device Prints the credential id (base64-encoded) and public key (PEM encoded) of the resident credential specified by .Ar rp_id and .Ar cred_id , where .Ar rp_id is a UTF-8 relying party id, and .Ar cred_id is a base64-encoded credential id. The user will be prompted for the PIN. .It Fl L Produces a list of authenticators found by the operating system. .It Fl L Fl b Ar device Produces a list of CTAP 2.1 .Dq largeBlobs on .Ar device . A PIN or equivalent user-verification gesture is required. .It Fl L Fl e Ar device Produces a list of biometric enrollments on .Ar device . The user will be prompted for the PIN. .It Fl L Fl r Ar device Produces a list of relying parties with resident credentials on .Ar device . The user will be prompted for the PIN. .It Fl L Fl k Ar rp_id Ar device Produces a list of resident credentials corresponding to relying party .Ar rp_id on .Ar device . The user will be prompted for the PIN. .It Fl R Performs a reset on .Ar device . .Nm will NOT prompt for confirmation. .It Fl S Sets the PIN of .Ar device . The user will be prompted for the PIN. .It Fl S Fl a Ar device Enables CTAP 2.1 Enterprise Attestation on .Ar device . .It Fl S Fl b Fl k Ar key_path Ar blob_path Ar device Sets .Ar blob_path as a CTAP 2.1 .Dq largeBlob encrypted with .Ar key_path on .Ar device , where .Ar blob_path holds the blob's plaintext, and .Ar key_path the blob's base64-encoded encryption. A PIN or equivalent user-verification gesture is required. .It Fl S Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar blob_path Ar device Sets .Ar blob_path as a CTAP 2.1 .Dq largeBlob associated with .Ar rp_id on .Ar device . If .Ar rp_id has multiple credentials enrolled on .Ar device , the credential ID must be specified using .Fl i Ar cred_id , where .Ar cred_id is a base64-encoded blob. A PIN or equivalent user-verification gesture is required. .It Fl S Fl c Fl i Ar cred_id Fl k Ar user_id Fl n Ar name Fl p Ar display_name Ar device Sets the .Ar name and .Ar display_name attributes of the resident credential identified by .Ar cred_id and .Ar user_id , where .Ar name and .Ar display_name are UTF-8 strings and .Ar cred_id and .Ar user_id are base64-encoded blobs. A PIN or equivalent user-verification gesture is required. .It Fl S Fl e Ar device Performs a new biometric enrollment on .Ar device . The user will be prompted for the PIN. .It Fl S Fl e Fl i Ar template_id Fl n Ar template_name Ar device Sets the friendly name of the biometric enrollment specified by .Ar template_id to .Ar template_name on .Ar device , where .Ar template_id is base64-encoded and .Ar template_name is a UTF-8 string. The user will be prompted for the PIN. .It Fl S Fl f Ar device Forces a PIN change on .Ar device . The user will be prompted for the PIN. .It Fl S Fl l Ar pin_length Ar device Sets the minimum PIN length of .Ar device to .Ar pin_length . The user will be prompted for the PIN. .It Fl S Fl m Ar rp_id Ar device Sets the list of relying party IDs that are allowed to retrieve the minimum PIN length of .Ar device . Multiple IDs may be specified, separated by commas. The user will be prompted for the PIN. .It Fl S Fl u Ar device Enables the CTAP 2.1 .Dq user verification always feature on .Ar device . .It Fl V Prints version information. .It Fl d Causes .Nm to emit debugging output on .Em stderr . .El .Pp If a .Em tty is available, .Nm will use it to prompt for PINs. Otherwise, .Em stdin is used. .Pp .Nm exits 0 on success and 1 on error. .Sh SEE ALSO .Xr fido2-assert 1 , .Xr fido2-cred 1 .Sh CAVEATS The actual user-flow to perform a reset is outside the scope of the FIDO2 specification, and may therefore vary depending on the authenticator. Yubico authenticators do not allow resets after 5 seconds from power-up, and expect a reset to be confirmed by the user through touch within 30 seconds. .Pp An authenticator's path may contain spaces. .Pp Resident credentials are called .Dq discoverable credentials in CTAP 2.1. .Pp Whether the CTAP 2.1 .Dq user verification always feature is activated or deactivated after an authenticator reset is vendor-specific. libfido2-1.10.0/man/fido_assert_allow_cred.3000066400000000000000000000021401417126203300206130ustar00rootroot00000000000000.\" 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 allow a credential in a FIDO2 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 FIDO2 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 libfido2-1.10.0/man/fido_assert_new.3000066400000000000000000000154171417126203300173040ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: October 22 2019 $ .Dt FIDO_ASSERT_NEW 3 .Os .Sh NAME .Nm fido_assert_new , .Nm fido_assert_free , .Nm fido_assert_count , .Nm fido_assert_rp_id , .Nm fido_assert_user_display_name , .Nm fido_assert_user_icon , .Nm fido_assert_user_name , .Nm fido_assert_authdata_ptr , .Nm fido_assert_blob_ptr , .Nm fido_assert_clientdata_hash_ptr , .Nm fido_assert_hmac_secret_ptr , .Nm fido_assert_largeblob_key_ptr , .Nm fido_assert_user_id_ptr , .Nm fido_assert_sig_ptr , .Nm fido_assert_id_ptr , .Nm fido_assert_authdata_len , .Nm fido_assert_blob_len , .Nm fido_assert_clientdata_hash_len , .Nm fido_assert_hmac_secret_len , .Nm fido_assert_largeblob_key_len , .Nm fido_assert_user_id_len , .Nm fido_assert_sig_len , .Nm fido_assert_id_len , .Nm fido_assert_sigcount , .Nm fido_assert_flags .Nd FIDO2 assertion API .Sh SYNOPSIS .In fido.h .Ft fido_assert_t * .Fn fido_assert_new "void" .Ft void .Fn fido_assert_free "fido_assert_t **assert_p" .Ft size_t .Fn fido_assert_count "const fido_assert_t *assert" .Ft const char * .Fn fido_assert_rp_id "const fido_assert_t *assert" .Ft const char * .Fn fido_assert_user_display_name "const fido_assert_t *assert" "size_t idx" .Ft const char * .Fn fido_assert_user_icon "const fido_assert_t *assert" "size_t idx" .Ft const char * .Fn fido_assert_user_name "const fido_assert_t *assert" "size_t idx" .Ft const unsigned char * .Fn fido_assert_authdata_ptr "const fido_assert_t *assert" "size_t idx" .Ft const unsigned char * .Fn fido_assert_clientdata_hash_ptr "const fido_assert_t *assert" .Ft const unsigned char * .Fn fido_assert_blob_ptr "const fido_assert_t *assert" "size_t idx" .Ft const unsigned char * .Fn fido_assert_hmac_secret_ptr "const fido_assert_t *assert" "size_t idx" .Ft const unsigned char * .Fn fido_assert_largeblob_key_ptr "const fido_assert_t *assert" "size_t idx" .Ft const unsigned char * .Fn fido_assert_user_id_ptr "const fido_assert_t *assert" "size_t idx" .Ft const unsigned char * .Fn fido_assert_sig_ptr "const fido_assert_t *assert" "size_t idx" .Ft const unsigned char * .Fn fido_assert_id_ptr "const fido_assert_t *assert" "size_t idx" .Ft size_t .Fn fido_assert_authdata_len "const fido_assert_t *assert" "size_t idx" .Ft size_t .Fn fido_assert_clientdata_hash_len "const fido_assert_t *assert" .Ft size_t .Fn fido_assert_blob_len "const fido_assert_t *assert" "size_t idx" .Ft size_t .Fn fido_assert_hmac_secret_len "const fido_assert_t *assert" "size_t idx" .Ft size_t .Fn fido_assert_largeblob_key_len "const fido_assert_t *assert" "size_t idx" .Ft size_t .Fn fido_assert_user_id_len "const fido_assert_t *assert" "size_t idx" .Ft size_t .Fn fido_assert_sig_len "const fido_assert_t *assert" "size_t idx" .Ft size_t .Fn fido_assert_id_len "const fido_assert_t *assert" "size_t idx" .Ft uint32_t .Fn fido_assert_sigcount "const fido_assert_t *assert" "size_t idx" .Ft uint8_t .Fn fido_assert_flags "const fido_assert_t *assert" "size_t idx" .Sh DESCRIPTION A FIDO2 assertion is a collection of statements, each statement a map between a challenge, a credential, a signature, and ancillary attributes. In .Em libfido2 , a FIDO2 assertion is abstracted by the .Vt fido_assert_t type. The functions described in this page allow a .Vt fido_assert_t type to be allocated, deallocated, and inspected. For other operations on .Vt fido_assert_t , please refer to .Xr fido_assert_set_authdata 3 , .Xr fido_assert_allow_cred 3 , .Xr fido_assert_verify 3 , and .Xr fido_dev_get_assert 3 . .Pp The .Fn fido_assert_new function returns a pointer to a newly allocated, empty .Vt fido_assert_t type. If memory cannot be allocated, NULL is returned. .Pp The .Fn fido_assert_free function releases the memory backing .Fa *assert_p , where .Fa *assert_p must have been previously allocated by .Fn fido_assert_new . On return, .Fa *assert_p is set to NULL. Either .Fa assert_p or .Fa *assert_p may be NULL, in which case .Fn fido_assert_free is a NOP. .Pp The .Fn fido_assert_count function returns the number of statements in .Fa assert . .Pp The .Fn fido_assert_rp_id function returns a pointer to a NUL-terminated string holding the relying party ID of .Fa assert . .Pp The .Fn fido_assert_user_display_name , .Fn fido_assert_user_icon , and .Fn fido_assert_user_name , functions return pointers to the user display name, icon, and name attributes of statement .Fa idx in .Fa assert . If not NULL, the values returned by these functions point to NUL-terminated UTF-8 strings. .Pp The .Fn fido_assert_authdata_ptr , .Fn fido_assert_clientdata_hash_ptr , .Fn fido_assert_id_ptr , .Fn fido_assert_user_id_ptr , .Fn fido_assert_sig_ptr , .Fn fido_assert_sigcount , and .Fn fido_assert_flags functions return pointers to the CBOR-encoded authenticator data, client data hash, credential ID, user ID, signature, signature count, and authenticator data flags of statement .Fa idx in .Fa assert . .Pp The .Fn fido_assert_hmac_secret_ptr function returns a pointer to the hmac-secret attribute of statement .Fa idx in .Fa assert . The HMAC Secret Extension .Pq hmac-secret is a CTAP 2.0 extension. .Pp The .Fn fido_assert_blob_ptr and .Fn fido_assert_largeblob_key_ptr functions return pointers to the .Dq credBlob and .Dq largeBlobKey attributes of statement .Fa idx in .Fa assert . Credential Blob .Pq credBlob and Large Blob Key .Pq largeBlobKey are CTAP 2.1 extensions. .Pp The .Fn fido_assert_authdata_len , .Fn fido_assert_clientdata_hash_len , .Fn fido_assert_id_len , .Fn fido_assert_user_id_len , .Fn fido_assert_sig_len , .Fn fido_assert_hmac_secret_len , .Fn fido_assert_blob_len , and .Fn fido_assert_largeblob_key_len functions return the length of a given attribute. .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 FIDO2 server for verification. .Sh RETURN VALUES The authenticator data returned by .Fn fido_assert_authdata_ptr is a CBOR-encoded byte string, as obtained from the authenticator. .Pp The .Fn fido_assert_rp_id , .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_id_ptr , .Fn fido_assert_user_id_ptr , .Fn fido_assert_sig_ptr , .Fn fido_assert_hmac_secret_ptr , .Fn fido_assert_blob_ptr , and .Fn fido_assert_largeblob_key_ptr functions may return NULL if the respective field in .Fa assert is not set. If not NULL, returned pointers are guaranteed to exist until any API function that takes .Fa assert without the .Em const qualifier is invoked. .Sh SEE ALSO .Xr fido_assert_allow_cred 3 , .Xr fido_assert_set_authdata 3 , .Xr fido_assert_verify 3 , .Xr fido_dev_get_assert 3 , .Xr fido_dev_largeblob_get 3 libfido2-1.10.0/man/fido_assert_set_authdata.3000066400000000000000000000132101417126203300211460ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: May 23 2018 $ .Dt FIDO_ASSERT_SET_AUTHDATA 3 .Os .Sh NAME .Nm fido_assert_set_authdata , .Nm fido_assert_set_authdata_raw , .Nm fido_assert_set_clientdata , .Nm fido_assert_set_clientdata_hash , .Nm fido_assert_set_count , .Nm fido_assert_set_extensions , .Nm fido_assert_set_hmac_salt , .Nm fido_assert_set_hmac_secret , .Nm fido_assert_set_up , .Nm fido_assert_set_uv , .Nm fido_assert_set_rp , .Nm fido_assert_set_sig .Nd set parameters of a FIDO2 assertion .Sh SYNOPSIS .In fido.h .Bd -literal typedef enum { FIDO_OPT_OMIT = 0, /* use authenticator's default */ FIDO_OPT_FALSE, /* explicitly set option to false */ FIDO_OPT_TRUE, /* explicitly set option to true */ } fido_opt_t; .Ed .Ft int .Fn fido_assert_set_authdata "fido_assert_t *assert" "size_t idx" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_assert_set_authdata_raw "fido_assert_t *assert" "size_t idx" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_assert_set_clientdata "fido_assert_t *assert" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_assert_set_clientdata_hash "fido_assert_t *assert" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_assert_set_count "fido_assert_t *assert" "size_t n" .Ft int .Fn fido_assert_set_extensions "fido_assert_t *assert" "int flags" .Ft int .Fn fido_assert_set_hmac_salt "fido_assert_t *assert" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_assert_set_hmac_secret "fido_assert_t *assert" "size_t idx" "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 FIDO2 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 FIDO2 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 function sets the client data hash of .Fa assert to .Fa ptr , where .Fa ptr points to .Fa len bytes. A copy of .Fa ptr is made, and no references to the passed pointer are kept. .Pp The .Fn fido_assert_set_clientdata function allows an application to set the client data hash of .Fa assert by specifying the assertion's unhashed client data. This is required by Windows Hello, which calculates the client data hash internally. For compatibility with Windows Hello, applications should use .Fn fido_assert_set_clientdata instead of .Fn fido_assert_set_clientdata_hash . .Pp The .Fn fido_assert_set_rp function sets the relying party .Fa id of .Fa assert , where .Fa id is a NUL-terminated UTF-8 string. The content of .Fa id is copied, and no references to the passed pointer are kept. .Pp The .Fn fido_assert_set_extensions function sets the extensions of .Fa assert to the bitmask .Fa flags . At the moment, only the .Dv FIDO_EXT_CRED_BLOB , .Dv FIDO_EXT_HMAC_SECRET , and .Dv FIDO_EXT_LARGEBLOB_KEY extensions are supported. If .Fa flags is zero, the extensions of .Fa assert are cleared. .Pp The .Fn fido_assert_set_hmac_salt and .Fn fido_assert_set_hmac_secret functions set the hmac-salt and hmac-secret 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. The HMAC Secret .Pq hmac-secret Extension is a CTAP 2.0 extension. The .Fn fido_assert_set_hmac_secret function is normally only useful when writing tests. .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 FIDO2 device to produce a series of assertion statements, prior to .Xr fido_dev_get_assert 3 (i.e, in the context of a FIDO2 client), or when verifying assertion statements using .Xr fido_assert_verify 3 (i.e, in the context of a FIDO2 server). .Pp For a complete description of the generation of a FIDO2 assertion and its verification, please refer to the FIDO2 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 libfido2-1.10.0/man/fido_assert_verify.3000066400000000000000000000031671417126203300200160ustar00rootroot00000000000000.\" 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 FIDO2 assertion statement .Sh SYNOPSIS .In fido.h .Ft int .Fn fido_assert_verify "const 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 FIDO2 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 libfido2-1.10.0/man/fido_bio_dev_get_info.3000066400000000000000000000062251417126203300204100ustar00rootroot00000000000000.\" 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 FIDO2 biometric authenticator API .Sh SYNOPSIS .In fido.h .In fido/bio.h .Ft int .Fn fido_bio_dev_get_info "fido_dev_t *dev" "fido_bio_info_t *info" .Ft int .Fn fido_bio_dev_enroll_begin "fido_dev_t *dev" "fido_bio_template_t *template" "fido_bio_enroll_t *enroll" "uint32_t timeout_ms" "const char *pin" .Ft int .Fn fido_bio_dev_enroll_continue "fido_dev_t *dev" "const fido_bio_template_t *template" "fido_bio_enroll_t *enroll" "uint32_t timeout_ms" .Ft int .Fn fido_bio_dev_enroll_cancel "fido_dev_t *dev" .Ft int .Fn fido_bio_dev_enroll_remove "fido_dev_t *dev" "const fido_bio_template_t *template" "const char *pin" .Ft int .Fn fido_bio_dev_get_template_array "fido_dev_t *dev" "fido_bio_template_array_t *template_array" "const char *pin" .Ft int .Fn fido_bio_dev_set_template_name "fido_dev_t *dev" "const fido_bio_template_t *template" "const char *pin" .Sh DESCRIPTION The functions described in this page allow biometric templates on a FIDO2 authenticator to be listed, created, removed, and customised. Please note that not all FIDO2 authenticators support biometric enrollment. For a description of the types involved, please refer to .Xr fido_bio_info_new 3 , .Xr fido_bio_enroll_new 3 , and .Xr fido_bio_template 3 . .Pp The .Fn fido_bio_dev_get_info function populates .Fa info with sensor information from .Fa dev . .Pp The .Fn fido_bio_dev_enroll_begin function initiates a biometric enrollment on .Fa dev , instructing the authenticator to wait .Fa timeout_ms milliseconds. On success, .Fa template and .Fa enroll will be populated with the newly created template's information and enrollment status, respectively. .Pp The .Fn fido_bio_dev_enroll_continue function continues an ongoing enrollment on .Fa dev , instructing the authenticator to wait .Fa timeout_ms milliseconds. On success, .Fa enroll will be updated to reflect the status of the biometric enrollment. .Pp The .Fn fido_bio_dev_enroll_cancel function cancels an ongoing enrollment on .Fa dev . .Pp The .Fn fido_bio_dev_enroll_remove function removes .Fa template from .Fa dev . .Pp The .Fn fido_bio_dev_get_template_array function populates .Fa template_array with the templates currently enrolled on .Fa dev . .Pp The .Fn fido_bio_dev_set_template_name function sets the friendly name of .Fa template on .Fa dev . .Sh RETURN VALUES The error codes returned by .Fn fido_bio_dev_get_info , .Fn fido_bio_dev_enroll_begin , .Fn fido_bio_dev_enroll_continue , .Fn fido_bio_dev_enroll_cancel , .Fn fido_bio_dev_enroll_remove , .Fn fido_bio_dev_get_template_array , and .Fn fido_bio_dev_set_template_name are defined in .In fido/err.h . On success, .Dv FIDO_OK is returned. .Sh SEE ALSO .Xr fido_bio_enroll_new 3 , .Xr fido_bio_info_new 3 , .Xr fido_bio_template 3 libfido2-1.10.0/man/fido_bio_enroll_new.3000066400000000000000000000047031417126203300201230ustar00rootroot00000000000000.\" 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 FIDO2 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 FIDO2 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 libfido2-1.10.0/man/fido_bio_info_new.3000066400000000000000000000034061417126203300175620ustar00rootroot00000000000000.\" 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 FIDO2 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 libfido2-1.10.0/man/fido_bio_template.3000066400000000000000000000101521417126203300175650ustar00rootroot00000000000000.\" 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 FIDO2 biometric template API .Sh SYNOPSIS .In fido.h .In fido/bio.h .Ft fido_bio_template_t * .Fn fido_bio_template_new "void" .Ft void .Fn fido_bio_template_free "fido_bio_template_t **template_p" .Ft const char * .Fn fido_bio_template_name "const fido_bio_template_t *template" .Ft const unsigned char * .Fn fido_bio_template_id_ptr "const fido_bio_template_t *template" .Ft size_t .Fn fido_bio_template_id_len "const fido_bio_template_t *template" .Ft int .Fn fido_bio_template_set_id "fido_bio_template_t *template" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_bio_template_set_name "fido_bio_template_t *template" "const char *name" .Ft fido_bio_template_array_t * .Fn fido_bio_template_array_new "void" .Ft void .Fn fido_bio_template_array_free "fido_bio_template_array_t **array_p" .Ft size_t .Fn fido_bio_template_array_count "const fido_bio_template_array_t *array" .Ft const fido_bio_template_t * .Fn fido_bio_template "const fido_bio_template_array_t *array" "size_t idx" .Sh DESCRIPTION Existing FIDO2 biometric enrollments are abstracted in .Em libfido2 by the .Vt fido_bio_template_t and .Vt fido_bio_template_array_t types. .Pp The functions described in this page allow a .Vt fido_bio_template_t type to be allocated, deallocated, changed, and inspected, and a .Vt fido_bio_template_array_t type to be allocated, deallocated, and inspected. For device operations on .Vt fido_bio_template_t and .Vt fido_bio_template_array_t , please refer to .Xr fido_bio_dev_get_info 3 . .Pp The .Fn fido_bio_template_new function returns a pointer to a newly allocated, empty .Vt fido_bio_template_t type. If memory cannot be allocated, NULL is returned. .Pp The .Fn fido_bio_template_free function releases the memory backing .Fa *template_p , where .Fa *template_p must have been previously allocated by .Fn fido_bio_template_new . On return, .Fa *template_p is set to NULL. Either .Fa template_p or .Fa *template_p may be NULL, in which case .Fn fido_bio_template_free is a NOP. .Pp The .Fn fido_bio_template_name function returns a pointer to a NUL-terminated string containing the friendly name of .Fa template , or NULL if .Fa template does not have a friendly name set. .Pp The .Fn fido_bio_template_id_ptr function returns a pointer to the template id of .Fa template , or NULL if .Fa template does not have an id. The corresponding length can be obtained by .Fn fido_bio_template_id_len . .Pp The .Fn fido_bio_template_set_name function sets the friendly name of .Fa template to .Fa name . If .Fa name is NULL, the friendly name of .Fa template is unset. .Pp The .Fn fido_bio_template_array_new function returns a pointer to a newly allocated, empty .Vt fido_bio_template_array_t type. If memory cannot be allocated, NULL is returned. .Pp The .Fn fido_bio_template_array_free function releases the memory backing .Fa *array_p , where .Fa *array_p must have been previously allocated by .Fn fido_bio_template_array_new . On return, .Fa *array_p is set to NULL. Either .Fa array_p or .Fa *array_p may be NULL, in which case .Fn fido_bio_template_array_free is a NOP. .Pp The .Fn fido_bio_template_array_count function returns the number of templates in .Fa array . .Pp The .Fn fido_bio_template function returns a pointer to the template at index .Fa idx in .Fa array . Please note that the first template in .Fa array has an .Fa idx (index) value of 0. .Sh RETURN VALUES The error codes returned by .Fn fido_bio_template_set_id and .Fn fido_bio_template_set_name are defined in .In fido/err.h . On success, .Dv FIDO_OK is returned. .Sh SEE ALSO .Xr fido_bio_dev_get_info 3 , .Xr fido_bio_enroll_new 3 libfido2-1.10.0/man/fido_cbor_info_new.3000066400000000000000000000140041417126203300177320ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: May 24 2018 $ .Dt FIDO_CBOR_INFO_NEW 3 .Os .Sh NAME .Nm fido_cbor_info_new , .Nm fido_cbor_info_free , .Nm fido_dev_get_cbor_info , .Nm fido_cbor_info_aaguid_ptr , .Nm fido_cbor_info_extensions_ptr , .Nm fido_cbor_info_protocols_ptr , .Nm fido_cbor_info_transports_ptr , .Nm fido_cbor_info_versions_ptr , .Nm fido_cbor_info_options_name_ptr , .Nm fido_cbor_info_options_value_ptr , .Nm fido_cbor_info_algorithm_type , .Nm fido_cbor_info_algorithm_cose , .Nm fido_cbor_info_algorithm_count , .Nm fido_cbor_info_aaguid_len , .Nm fido_cbor_info_extensions_len , .Nm fido_cbor_info_protocols_len , .Nm fido_cbor_info_transports_len , .Nm fido_cbor_info_versions_len , .Nm fido_cbor_info_options_len , .Nm fido_cbor_info_maxmsgsiz , .Nm fido_cbor_info_maxcredbloblen , .Nm fido_cbor_info_maxcredcntlst , .Nm fido_cbor_info_maxcredidlen , .Nm fido_cbor_info_fwversion .Nd FIDO2 CBOR Info API .Sh SYNOPSIS .In fido.h .Ft fido_cbor_info_t * .Fn fido_cbor_info_new "void" .Ft void .Fn fido_cbor_info_free "fido_cbor_info_t **ci_p" .Ft int .Fn fido_dev_get_cbor_info "fido_dev_t *dev" "fido_cbor_info_t *ci" .Ft const unsigned char * .Fn fido_cbor_info_aaguid_ptr "const fido_cbor_info_t *ci" .Ft char ** .Fn fido_cbor_info_extensions_ptr "const fido_cbor_info_t *ci" .Ft const uint8_t * .Fn fido_cbor_info_protocols_ptr "const fido_cbor_info_t *ci" .Ft char ** .Fn fido_cbor_info_transports_ptr "const fido_cbor_info_t *ci" .Ft char ** .Fn fido_cbor_info_versions_ptr "const fido_cbor_info_t *ci" .Ft char ** .Fn fido_cbor_info_options_name_ptr "const fido_cbor_info_t *ci" .Ft const bool * .Fn fido_cbor_info_options_value_ptr "const fido_cbor_info_t *ci" .Ft const char * .Fn fido_cbor_info_algorithm_type "const fido_cbor_info_t *ci" "size_t idx" .Ft int .Fn fido_cbor_info_algorithm_cose "const fido_cbor_info_t *ci" "size_t idx" .Ft size_t .Fn fido_cbor_info_algorithm_count "const fido_cbor_info_t *ci" .Ft size_t .Fn fido_cbor_info_aaguid_len "const fido_cbor_info_t *ci" .Ft size_t .Fn fido_cbor_info_extensions_len "const fido_cbor_info_t *ci" .Ft size_t .Fn fido_cbor_info_protocols_len "const fido_cbor_info_t *ci" .Ft size_t .Fn fido_cbor_info_transports_len "const fido_cbor_info_t *ci" .Ft size_t .Fn fido_cbor_info_versions_len "const fido_cbor_info_t *ci" .Ft size_t .Fn fido_cbor_info_options_len "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_maxmsgsiz "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_maxcredbloblen "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_maxcredcntlst "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_maxcredidlen "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_fwversion "const fido_cbor_info_t *ci" .Sh DESCRIPTION The .Fn fido_cbor_info_new function returns a pointer to a newly allocated, empty .Vt fido_cbor_info_t type. If memory cannot be allocated, NULL is returned. .Pp The .Fn fido_cbor_info_free function releases the memory backing .Fa *ci_p , where .Fa *ci_p must have been previously allocated by .Fn fido_cbor_info_new . On return, .Fa *ci_p is set to NULL. Either .Fa ci_p or .Fa *ci_p may be NULL, in which case .Fn fido_cbor_info_free is a NOP. .Pp The .Fn fido_dev_get_cbor_info function transmits a .Dv CTAP_CBOR_GETINFO command to .Fa dev and fills .Fa ci with attributes retrieved from the command's response. The .Fn fido_dev_get_cbor_info function may block. .Pp The .Fn fido_cbor_info_aaguid_ptr , .Fn fido_cbor_info_extensions_ptr , .Fn fido_cbor_info_protocols_ptr , .Fn fido_cbor_info_transports_ptr , and .Fn fido_cbor_info_versions_ptr functions return pointers to the authenticator attestation GUID, supported extensions, PIN protocol, transports, and CTAP version strings of .Fa ci . The corresponding length of a given attribute can be obtained by .Fn fido_cbor_info_aaguid_len , .Fn fido_cbor_info_extensions_len , .Fn fido_cbor_info_protocols_len , .Fn fido_cbor_info_transports_len , or .Fn fido_cbor_info_versions_len . .Pp The .Fn fido_cbor_info_options_name_ptr and .Fn fido_cbor_info_options_value_ptr functions return pointers to the array of option names and their respective values in .Fa ci . The length of the options array is returned by .Fn fido_cbor_info_options_len . .Pp The .Fn fido_cbor_info_algorithm_count function returns the number of supported algorithms in .Fa ci . The .Fn fido_cbor_info_algorithm_cose function returns the COSE identifier of algorithm .Fa idx in .Fa ci , or 0 if the COSE identifier is unknown or unset. The .Fn fido_cbor_info_algorithm_type function returns the type of algorithm .Fa idx in .Fa ci , or NULL if the type is unset. Please note that the first algorithm in .Fa ci has an .Fa idx (index) value of 0. .Pp The .Fn fido_cbor_info_maxmsgsiz function returns the maximum message size attribute of .Fa ci . .Pp The .Fn fido_cbor_info_maxcredbloblen function returns the maximum .Dq credBlob length in bytes supported by the authenticator as reported in .Fa ci . .Pp The .Fn fido_cbor_info_maxcredcntlst function returns the maximum supported number of credentials in a single credential ID list as reported in .Fa ci . .Pp The .Fn fido_cbor_info_maxcredidlen function returns the maximum supported length of a credential ID as reported in .Fa ci . .Pp The .Fn fido_cbor_info_fwversion function returns the firmware version attribute of .Fa ci . .Pp A complete example of how to use these functions can be found in the .Pa example/info.c file shipped with .Em libfido2 . .Sh RETURN VALUES The .Fn fido_cbor_info_aaguid_ptr , .Fn fido_cbor_info_extensions_ptr , .Fn fido_cbor_info_protocols_ptr , .Fn fido_cbor_info_transports_ptr , .Fn fido_cbor_info_versions_ptr , .Fn fido_cbor_info_options_name_ptr , and .Fn fido_cbor_info_options_value_ptr functions return NULL if the respective field in .Fa ci is absent. If not NULL, returned pointers are guaranteed to exist until any API function that takes .Fa ci without the .Em const qualifier is invoked. .Sh SEE ALSO .Xr fido_dev_open 3 libfido2-1.10.0/man/fido_cred_exclude.3000066400000000000000000000024231417126203300175510ustar00rootroot00000000000000.\" 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 FIDO2 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 libfido2-1.10.0/man/fido_cred_new.3000066400000000000000000000166001417126203300167130ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: May 23 2018 $ .Dt FIDO_CRED_NEW 3 .Os .Sh NAME .Nm fido_cred_new , .Nm fido_cred_free , .Nm fido_cred_pin_minlen , .Nm fido_cred_prot , .Nm fido_cred_fmt , .Nm fido_cred_rp_id , .Nm fido_cred_rp_name , .Nm fido_cred_user_name , .Nm fido_cred_display_name , .Nm fido_cred_authdata_ptr , .Nm fido_cred_authdata_raw_ptr , .Nm fido_cred_clientdata_hash_ptr , .Nm fido_cred_id_ptr , .Nm fido_cred_aaguid_ptr , .Nm fido_cred_largeblob_key_ptr , .Nm fido_cred_pubkey_ptr , .Nm fido_cred_sig_ptr , .Nm fido_cred_user_id_ptr , .Nm fido_cred_x5c_ptr , .Nm fido_cred_attstmt_ptr , .Nm fido_cred_authdata_len , .Nm fido_cred_authdata_raw_len , .Nm fido_cred_clientdata_hash_len , .Nm fido_cred_id_len , .Nm fido_cred_aaguid_len , .Nm fido_cred_largeblob_key_len , .Nm fido_cred_pubkey_len , .Nm fido_cred_sig_len , .Nm fido_cred_user_id_len , .Nm fido_cred_x5c_len , .Nm fido_cred_attstmt_len , .Nm fido_cred_type , .Nm fido_cred_flags , .Nm fido_cred_sigcount .Nd FIDO2 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 size_t .Fn fido_cred_pin_minlen "const fido_cred_t *cred" .Ft int .Fn fido_cred_prot "const fido_cred_t *cred" .Ft const char * .Fn fido_cred_fmt "const fido_cred_t *cred" .Ft const char * .Fn fido_cred_rp_id "const fido_cred_t *cred" .Ft const char * .Fn fido_cred_rp_name "const fido_cred_t *cred" .Ft const char * .Fn fido_cred_user_name "const fido_cred_t *cred" .Ft const char * .Fn fido_cred_display_name "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_authdata_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_authdata_raw_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_clientdata_hash_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_id_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_aaguid_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_largeblob_key_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_pubkey_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_sig_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_user_id_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_x5c_ptr "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_attstmt_ptr "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_authdata_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_authdata_raw_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_clientdata_hash_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_id_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_aaguid_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_largeblob_key_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_pubkey_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_sig_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_user_id_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_x5c_len "const fido_cred_t *cred" .Ft size_t .Fn fido_cred_attstmt_len "const fido_cred_t *cred" .Ft int .Fn fido_cred_type "const fido_cred_t *cred" .Ft uint8_t .Fn fido_cred_flags "const fido_cred_t *cred" .Ft uint32_t .Fn fido_cred_sigcount "const fido_cred_t *cred" .Sh DESCRIPTION FIDO2 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 If the CTAP 2.1 .Dv FIDO_EXT_MINPINLEN extension is enabled on .Fa cred , then the .Fn fido_cred_pin_minlen function returns the minimum PIN length of .Fa cred . Otherwise, .Fn fido_cred_pin_minlen returns zero. See .Xr fido_cred_set_pin_minlen 3 on how to enable this extension. .Pp If the CTAP 2.1 .Dv FIDO_EXT_CRED_PROTECT extension is enabled on .Fa cred , then the .Fn fido_cred_prot function returns the protection of .Fa cred . Otherwise, .Fn fido_cred_prot returns zero. See .Xr fido_cred_set_prot 3 for the protection policies understood by .Em libfido2 . .Pp The .Fn fido_cred_fmt function returns a pointer to a NUL-terminated string containing the format of .Fa cred , or NULL if .Fa cred does not have a format set. .Pp The .Fn fido_cred_rp_id , .Fn fido_cred_rp_name , .Fn fido_cred_user_name , and .Fn fido_cred_display_name functions return pointers to NUL-terminated strings holding the relying party ID, relying party name, user name, and user display name attributes of .Fa cred , or NULL if the respective entry is not set. .Pp The .Fn fido_cred_authdata_ptr , .Fn fido_cred_authdata_raw_ptr , .Fn fido_cred_clientdata_hash_ptr , .Fn fido_cred_id_ptr , .Fn fido_cred_aaguid_ptr , .Fn fido_cred_largeblob_key_ptr , .Fn fido_cred_pubkey_ptr , .Fn fido_cred_sig_ptr , .Fn fido_cred_user_id_ptr , .Fn fido_cred_x5c_ptr , and .Fn fido_cred_attstmt_ptr functions return pointers to the CBOR-encoded and raw authenticator data, client data hash, ID, authenticator attestation GUID, .Dq largeBlobKey , public key, signature, user ID, x509 certificate, and attestation statement parts of .Fa cred , or NULL if the respective entry is not set. .Pp The corresponding length can be obtained by .Fn fido_cred_authdata_len , .Fn fido_cred_authdata_raw_len , .Fn fido_cred_clientdata_hash_len , .Fn fido_cred_id_len , .Fn fido_cred_aaguid_len , .Fn fido_cred_largeblob_key_len , .Fn fido_cred_pubkey_len , .Fn fido_cred_sig_len , .Fn fido_cred_user_id_len , .Fn fido_cred_x5c_len , and .Fn fido_cred_attstmt_len . .Pp The authenticator data, x509 certificate, and signature parts of a credential are typically passed to a FIDO2 server for verification. .Pp The .Fn fido_cred_type function returns the COSE algorithm of .Fa cred . .Pp The .Fn fido_cred_flags function returns the authenticator data flags of .Fa cred . .Pp The .Fn fido_cred_sigcount function returns the authenticator data signature counter of .Fa cred . .Sh RETURN VALUES The authenticator data returned by .Fn fido_cred_authdata_ptr is a CBOR-encoded byte string, as obtained from the authenticator. To obtain the decoded byte string, use .Fn fido_cred_authdata_raw_ptr . .Pp If not NULL, pointers returned by .Fn fido_cred_fmt , .Fn fido_cred_authdata_ptr , .Fn fido_cred_clientdata_hash_ptr , .Fn fido_cred_id_ptr , .Fn fido_cred_aaguid_ptr , .Fn fido_cred_largeblob_key_ptr , .Fn fido_cred_pubkey_ptr , .Fn fido_cred_sig_ptr , and .Fn fido_cred_x5c_ptr are guaranteed to exist until any API function that takes .Fa cred without the .Em const qualifier is invoked. .Sh SEE ALSO .Xr fido_cred_exclude 3 , .Xr fido_cred_set_authdata 3 , .Xr fido_cred_set_pin_minlen 3 , .Xr fido_cred_set_prot 3 , .Xr fido_cred_verify 3 , .Xr fido_credman_metadata_new 3 , .Xr fido_dev_largeblob_get 3 , .Xr fido_dev_make_cred 3 libfido2-1.10.0/man/fido_cred_set_authdata.3000066400000000000000000000201331417126203300205640ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: 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_attstmt , .Nm fido_cred_set_x509 , .Nm fido_cred_set_sig , .Nm fido_cred_set_id , .Nm fido_cred_set_clientdata , .Nm fido_cred_set_clientdata_hash , .Nm fido_cred_set_rp , .Nm fido_cred_set_user , .Nm fido_cred_set_extensions , .Nm fido_cred_set_blob , .Nm fido_cred_set_pin_minlen , .Nm fido_cred_set_prot , .Nm fido_cred_set_rk , .Nm fido_cred_set_uv , .Nm fido_cred_set_fmt , .Nm fido_cred_set_type .Nd set parameters of a FIDO2 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_attstmt "fido_cred_t *cred" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_cred_set_x509 "fido_cred_t *cred" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_cred_set_sig "fido_cred_t *cred" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_cred_set_id "fido_cred_t *cred" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_cred_set_clientdata "fido_cred_t *cred" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_cred_set_clientdata_hash "fido_cred_t *cred" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_cred_set_rp "fido_cred_t *cred" "const char *id" "const char *name" .Ft int .Fn fido_cred_set_user "fido_cred_t *cred" "const unsigned char *user_id" "size_t user_id_len" "const char *name" "const char *display_name" "const char *icon" .Ft int .Fn fido_cred_set_extensions "fido_cred_t *cred" "int flags" .Ft int .Fn fido_cred_set_blob "fido_cred_t *cred" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_cred_set_pin_minlen "fido_cred_t *cred" "size_t len" .Ft int .Fn fido_cred_set_prot "fido_cred_t *cred" "int prot" .Ft int .Fn fido_cred_set_rk "fido_cred_t *cred" "fido_opt_t rk" .Ft int .Fn fido_cred_set_uv "fido_cred_t *cred" "fido_opt_t uv" .Ft int .Fn fido_cred_set_fmt "fido_cred_t *cred" "const char *ptr" .Ft int .Fn fido_cred_set_type "fido_cred_t *cred" "int cose_alg" .Sh DESCRIPTION The .Nm set of functions define the various parameters of a FIDO2 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 FIDO2 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_attstmt , .Fn fido_cred_set_x509 , .Fn fido_cred_set_sig , .Fn fido_cred_set_id , and .Fn fido_cred_set_clientdata_hash functions set the authenticator data, attestation statement, attestation certificate, attestation signature, id, and client data hash parts of .Fa cred to .Fa ptr , where .Fa ptr points to .Fa len bytes. A copy of .Fa ptr is made, and no references to the passed pointer are kept. .Pp 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 . An application calling .Fn fido_cred_set_authdata does not need to call .Fn fido_cred_set_id . The latter is meant to be used in contexts where the credential's authenticator data is not available. .Pp The attestation statement passed to .Fn fido_cred_set_attstmt must be a CBOR-encoded map, as obtained from .Fn fido_cred_attstmt_ptr . An application calling .Fn fido_cred_set_attstmt does not need to call .Fn fido_cred_set_x509 or .Fn fido_cred_set_sig . The latter two are meant to be used in contexts where the credential's complete attestation statement is not available or required. .Pp The .Fn fido_cred_set_clientdata function allows an application to set the client data hash of .Fa cred by specifying the credential's unhashed client data. This is required by Windows Hello, which calculates the client data hash internally. For compatibility with Windows Hello, applications should use .Fn fido_cred_set_clientdata instead of .Fn fido_cred_set_clientdata_hash . .Pp The .Fn fido_cred_set_rp function sets the relying party .Fa id and .Fa name parameters of .Fa cred , where .Fa id and .Fa name are NUL-terminated UTF-8 strings. The contents of .Fa id and .Fa name are copied, and no references to the passed pointers are kept. .Pp The .Fn fido_cred_set_user function sets the user attributes of .Fa cred , where .Fa user_id points to .Fa user_id_len bytes and .Fa name , .Fa display_name , and .Fa icon are NUL-terminated UTF-8 strings. The contents of .Fa user_id , .Fa name , .Fa display_name , and .Fa icon are copied, and no references to the passed pointers are kept. Previously set user attributes are flushed. The .Fa user_id , .Fa name , .Fa display_name , and .Fa icon parameters may be NULL. .Pp The .Fn fido_cred_set_extensions function sets the extensions of .Fa cred to the bitmask .Fa flags . At the moment, only the .Dv FIDO_EXT_CRED_BLOB , .Dv FIDO_EXT_CRED_PROTECT , .Dv FIDO_EXT_HMAC_SECRET , .Dv FIDO_EXT_MINPINLEN , and .Dv FIDO_EXT_LARGEBLOB_KEY extensions are supported. If .Fa flags is zero, the extensions of .Fa cred are cleared. .Pp The .Fn fido_cred_set_blob function sets the .Dq credBlob to be stored with .Fa cred to the data pointed to by .Fa ptr , which must be .Fa len bytes long. .Pp The .Fn fido_cred_set_pin_minlen function enables the CTAP 2.1 .Dv FIDO_EXT_MINPINLEN extension on .Fa cred and sets the expected minimum PIN length of .Fa cred to .Fa len , where .Fa len is greater than zero. If .Fa len is zero, the .Dv FIDO_EXT_MINPINLEN extension is disabled on .Fa cred . .Pp The .Fn fido_cred_set_prot function enables the CTAP 2.1 .Dv FIDO_EXT_CRED_PROTECT extension on .Fa cred and sets the protection of .Fa cred to the scalar .Fa prot . At the moment, only the .Dv FIDO_CRED_PROT_UV_OPTIONAL , .Dv FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID , and .Dv FIDO_CRED_PROT_UV_REQUIRED protections are supported. If .Fa prot is zero, the protection of .Fa cred is cleared. .Pp The .Fn fido_cred_set_rk and .Fn fido_cred_set_uv functions set the .Em rk .Pq resident/discoverable key and .Em uv .Pq user verification attributes of .Fa cred . Both are .Dv FIDO_OPT_OMIT by default, allowing the authenticator to use its default settings. .Pp The .Fn fido_cred_set_fmt function sets the attestation format of .Fa cred to .Fa fmt , where .Fa fmt must be .Vt "packed" .Pq the format used in FIDO2 , .Vt "fido-u2f" .Pq the format used by U2F , or .Vt "none" . A copy of .Fa fmt is made, and no references to the passed pointer are kept. Note that not all authenticators support FIDO2 and therefore may not be able to generate .Vt "packed" . .Pp The .Fn fido_cred_set_type function sets the type of .Fa cred to .Fa cose_alg , where .Fa cose_alg is .Dv COSE_ES256 , .Dv COSE_RS256 , or .Dv COSE_EDDSA . The type of a credential may only be set once. Note that not all authenticators support COSE_RS256 or COSE_EDDSA. .Pp Use of the .Nm set of functions may happen in two distinct situations: when generating a new credential on a FIDO2 device, prior to .Xr fido_dev_make_cred 3 (i.e, in the context of a FIDO2 client), or when validating a generated credential using .Xr fido_cred_verify 3 (i.e, in the context of a FIDO2 server). .Pp For a complete description of the generation of a FIDO2 credential and its verification, please refer to the FIDO2 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 libfido2-1.10.0/man/fido_cred_verify.3000066400000000000000000000044651417126203300174340ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: May 23 2018 $ .Dt FIDO_CRED_VERIFY 3 .Os .Sh NAME .Nm fido_cred_verify , .Nm fido_cred_verify_self .Nd verify the attestation signature of a FIDO2 credential .Sh SYNOPSIS .In fido.h .Ft int .Fn fido_cred_verify "const fido_cred_t *cred" .Ft int .Fn fido_cred_verify_self "const fido_cred_t *cred" .Sh DESCRIPTION The .Fn fido_cred_verify and .Fn fido_cred_verify_self functions verify whether the attestation signature contained in .Fa cred matches the attributes of the credential. Before using .Fn fido_cred_verify or .Fn fido_cred_verify_self in a sensitive context, the reader is strongly encouraged to make herself familiar with the FIDO2 credential attestation process as defined in the Web Authentication (webauthn) standard. .Pp The .Fn fido_cred_verify function verifies whether the client data hash, relying party ID, credential ID, type, protection policy, minimum PIN length, and resident/discoverable key and user verification attributes of .Fa cred have been attested by the holder of the private counterpart of the public key contained in the credential's x509 certificate. .Pp Please note that the x509 certificate itself is not verified. .Pp The attestation statement formats supported by .Fn fido_cred_verify are .Em packed , .Em fido-u2f , and .Em tpm . The attestation type implemented by .Fn fido_cred_verify is .Em Basic Attestation . .Pp The .Fn fido_cred_verify_self function verifies whether the client data hash, relying party ID, credential ID, type, protection policy, minimum PIN length, and resident/discoverable key and user verification attributes of .Fa cred have been attested by the holder of the credential's private key. .Pp The attestation statement formats supported by .Fn fido_cred_verify_self are .Em packed and .Em fido-u2f . The attestation type implemented by .Fn fido_cred_verify_self is .Em Self Attestation . .Pp Other attestation formats and types are not supported. .Sh RETURN VALUES The error codes returned by .Fn fido_cred_verify and .Fn fido_cred_verify_self 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 libfido2-1.10.0/man/fido_credman_metadata_new.3000066400000000000000000000165131417126203300212520ustar00rootroot00000000000000.\" Copyright (c) 2019-2021 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: June 28 2019 $ .Dt FIDO_CREDMAN_METADATA_NEW 3 .Os .Sh NAME .Nm fido_credman_metadata_new , .Nm fido_credman_rk_new , .Nm fido_credman_rp_new , .Nm fido_credman_metadata_free , .Nm fido_credman_rk_free , .Nm fido_credman_rp_free , .Nm fido_credman_rk_existing , .Nm fido_credman_rk_remaining , .Nm fido_credman_rk , .Nm fido_credman_rk_count , .Nm fido_credman_rp_id , .Nm fido_credman_rp_name , .Nm fido_credman_rp_count , .Nm fido_credman_rp_id_hash_ptr , .Nm fido_credman_rp_id_hash_len , .Nm fido_credman_get_dev_metadata , .Nm fido_credman_get_dev_rk , .Nm fido_credman_set_dev_rk , .Nm fido_credman_del_dev_rk , .Nm fido_credman_get_dev_rp .Nd FIDO2 credential management API .Sh SYNOPSIS .In fido.h .In fido/credman.h .Ft fido_credman_metadata_t * .Fn fido_credman_metadata_new "void" .Ft fido_credman_rk_t * .Fn fido_credman_rk_new "void" .Ft fido_credman_rp_t * .Fn fido_credman_rp_new "void" .Ft void .Fn fido_credman_metadata_free "fido_credman_metadata_t **metadata_p" .Ft void .Fn fido_credman_rk_free "fido_credman_rk_t **rk_p" .Ft void .Fn fido_credman_rp_free "fido_credman_rp_t **rp_p" .Ft uint64_t .Fn fido_credman_rk_existing "const fido_credman_metadata_t *metadata" .Ft uint64_t .Fn fido_credman_rk_remaining "const fido_credman_metadata_t *metadata" .Ft const fido_cred_t * .Fn fido_credman_rk "const fido_credman_rk_t *rk" "size_t idx" .Ft size_t .Fn fido_credman_rk_count "const fido_credman_rk_t *rk" .Ft const char * .Fn fido_credman_rp_id "const fido_credman_rp_t *rp" "size_t idx" .Ft const char * .Fn fido_credman_rp_name "const fido_credman_rp_t *rp" "size_t idx" .Ft size_t .Fn fido_credman_rp_count "const fido_credman_rp_t *rp" .Ft const unsigned char * .Fn fido_credman_rp_id_hash_ptr "const fido_credman_rp_t *rp" "size_t idx" .Ft size_t .Fn fido_credman_rp_id_hash_len "const fido_credman_rp_t *" "size_t idx" .Ft int .Fn fido_credman_get_dev_metadata "fido_dev_t *dev" "fido_credman_metadata_t *metadata" "const char *pin" .Ft int .Fn fido_credman_get_dev_rk "fido_dev_t *dev" "const char *rp_id" "fido_credman_rk_t *rk" "const char *pin" .Ft int .Fn fido_credman_set_dev_rk "fido_dev_t *dev" "fido_cred_t *cred" "const char *pin" .Ft int .Fn fido_credman_del_dev_rk "fido_dev_t *dev" "const unsigned char *cred_id" "size_t cred_id_len" "const char *pin" .Ft int .Fn fido_credman_get_dev_rp "fido_dev_t *dev" "fido_credman_rp_t *rp" "const char *pin" .Sh DESCRIPTION The credential management API of .Em libfido2 allows resident credentials on a FIDO2 authenticator to be listed, inspected, modified, and removed. Please note that not all FIDO2 authenticators support credential management. To obtain information on what an authenticator supports, please refer to .Xr fido_cbor_info_new 3 . .Pp The .Vt fido_credman_metadata_t type abstracts credential management metadata. .Pp The .Fn fido_credman_metadata_new function returns a pointer to a newly allocated, empty .Vt fido_credman_metadata_t type. If memory cannot be allocated, NULL is returned. .Pp The .Fn fido_credman_metadata_free function releases the memory backing .Fa *metadata_p , where .Fa *metadata_p must have been previously allocated by .Fn fido_credman_metadata_new . On return, .Fa *metadata_p is set to NULL. Either .Fa metadata_p or .Fa *metadata_p may be NULL, in which case .Fn fido_credman_metadata_free is a NOP. .Pp The .Fn fido_credman_get_dev_metadata function populates .Fa metadata with information retrieved from .Fa dev . A valid .Fa pin must be provided. .Pp The .Fn fido_credman_rk_existing function inspects .Fa metadata and returns the number of resident credentials on the authenticator. The .Fn fido_credman_rk_remaining function inspects .Fa metadata and returns the estimated number of resident credentials that can be created on the authenticator. .Pp The .Vt fido_credman_rk_t type abstracts the set of resident credentials belonging to a given relying party. .Pp The .Fn fido_credman_rk_new function returns a pointer to a newly allocated, empty .Vt fido_credman_rk_t type. If memory cannot be allocated, NULL is returned. .Pp The .Fn fido_credman_rk_free function releases the memory backing .Fa *rk_p , where .Fa *rk_p must have been previously allocated by .Fn fido_credman_rk_new . On return, .Fa *rk_p is set to NULL. Either .Fa rk_p or .Fa *rk_p may be NULL, in which case .Fn fido_credman_rk_free is a NOP. .Pp The .Fn fido_credman_get_dev_rk function populates .Fa rk with the set of resident credentials belonging to .Fa rp_id in .Fa dev . A valid .Fa pin must be provided. .Pp The .Fn fido_credman_rk_count function returns the number of resident credentials in .Fa rk . The .Fn fido_credman_rk function returns a pointer to the credential at index .Fa idx in .Fa rk . Please note that the first credential in .Fa rk has an .Fa idx (index) value of 0. .Pp The .Fn fido_credman_set_dev_rk function updates the credential pointed to by .Fa cred in .Fa dev . The credential id and user id attributes of .Fa cred must be set. See .Xr fido_cred_set_id 3 and .Xr fido_cred_set_user 3 for details. Only a credential's user attributes (name, display name) may be updated at this time. .Pp The .Fn fido_credman_del_dev_rk function deletes the resident credential identified by .Fa cred_id from .Fa dev , where .Fa cred_id points to .Fa cred_id_len bytes. A valid .Fa pin must be provided. .Pp The .Vt fido_credman_rp_t type abstracts information about a relying party. .Pp The .Fn fido_credman_rp_new function returns a pointer to a newly allocated, empty .Vt fido_credman_rp_t type. If memory cannot be allocated, NULL is returned. .Pp The .Fn fido_credman_rp_free function releases the memory backing .Fa *rp_p , where .Fa *rp_p must have been previously allocated by .Fn fido_credman_rp_new . On return, .Fa *rp_p is set to NULL. Either .Fa rp_p or .Fa *rp_p may be NULL, in which case .Fn fido_credman_rp_free is a NOP. .Pp The .Fn fido_credman_get_dev_rp function populates .Fa rp with information about relying parties with resident credentials in .Fa dev . A valid .Fa pin must be provided. .Pp The .Fn fido_credman_rp_count function returns the number of relying parties in .Fa rp . .Pp The .Fn fido_credman_rp_id and .Fn fido_credman_rp_name functions return pointers to the id and name of relying party .Fa idx in .Fa rp . If not NULL, the values returned by these functions point to NUL-terminated UTF-8 strings. Please note that the first relying party in .Fa rp has an .Fa idx (index) value of 0. .Pp The .Fn fido_credman_rp_id_hash_ptr function returns a pointer to the hashed id of relying party .Fa idx in .Fa rp . The corresponding length can be obtained by .Fn fido_credman_rp_id_hash_len . Please note that the first relying party in .Fa rp has an .Fa idx (index) value of 0. .Sh RETURN VALUES The .Fn fido_credman_get_dev_metadata , .Fn fido_credman_get_dev_rk , .Fn fido_credman_set_dev_rk , .Fn fido_credman_del_dev_rk , and .Fn fido_credman_get_dev_rp functions return .Dv FIDO_OK on success. On error, a different error code defined in .In fido/err.h is returned. Functions returning pointers are not guaranteed to succeed, and should have their return values checked for NULL. .Sh SEE ALSO .Xr fido_cbor_info_new 3 , .Xr fido_cred_new 3 , .Xr fido_dev_supports_credman 3 .Sh CAVEATS Resident credentials are called .Dq discoverable credentials in CTAP 2.1. libfido2-1.10.0/man/fido_dev_enable_entattest.3000066400000000000000000000057231417126203300213100ustar00rootroot00000000000000.\" Copyright (c) 2020 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: September 22 2020 $ .Dt FIDO_DEV_ENABLE_ENTATTEST 3 .Os .Sh NAME .Nm fido_dev_enable_entattest , .Nm fido_dev_toggle_always_uv , .Nm fido_dev_force_pin_change , .Nm fido_dev_set_pin_minlen , .Nm fido_dev_set_pin_minlen_rpid .Nd CTAP 2.1 configuration authenticator API .Sh SYNOPSIS .In fido.h .In fido/config.h .Ft int .Fn fido_dev_enable_entattest "fido_dev_t *dev" "const char *pin" .Ft int .Fn fido_dev_toggle_always_uv "fido_dev_t *dev" "const char *pin" .Ft int .Fn fido_dev_force_pin_change "fido_dev_t *dev" "const char *pin" .Ft int .Fn fido_dev_set_pin_minlen "fido_dev_t *dev" "size_t len" "const char *pin" .Ft int .Fn fido_dev_set_pin_minlen_rpid "fido_dev_t *dev" "const char * const *rpid" "size_t n" "const char *pin" .Sh DESCRIPTION The functions described in this page allow configuration of a CTAP 2.1 authenticator. .Pp The .Fn fido_dev_enable_entattest function enables the .Em Enterprise Attestation feature on .Fa dev . .Em Enterprise Attestation instructs the authenticator to include uniquely identifying information in subsequent attestation statements. The .Fa pin parameter may be NULL if .Fa dev does not have a PIN set. .Pp The .Fn fido_dev_toggle_always_uv function toggles the .Dq user verification always feature on .Fa dev . When set, this toggle enforces user verification at the authenticator level for all known credentials. If .Fa dev supports U2F (CTAP1) and the user verification methods supported by the authenticator do not allow protection of U2F credentials, the U2F subsystem will be disabled by the authenticator. The .Fa pin parameter may be NULL if .Fa dev does not have a PIN set. .Pp The .Fn fido_dev_force_pin_change instructs .Fa dev to require a PIN change. Subsequent PIN authentication attempts against .Fa dev will fail until its PIN is changed. .Pp The .Fn fido_dev_set_pin_minlen function sets the minimum PIN length of .Fa dev to .Fa len . Minimum PIN lengths may only be increased. .Pp The .Fn fido_dev_set_pin_minlen_rpid function sets the list of relying party identifiers .Pq RP IDs that are allowed to obtain the minimum PIN length of .Fa dev through the CTAP 2.1 .Dv FIDO_EXT_MINPINLEN extension. The list of RP identifiers is denoted by .Fa rpid , a vector of .Fa n NUL-terminated UTF-8 strings. A copy of .Fa rpid is made, and no reference to it or its contents is kept. .Pp Configuration settings are reflected in the payload returned by the authenticator in response to a .Xr fido_dev_get_cbor_info 3 call. .Sh RETURN VALUES The error codes returned by .Fn fido_dev_enable_entattest , .Fn fido_dev_toggle_always_uv , .Fn fido_dev_force_pin_change , .Fn fido_dev_set_pin_minlen , and .Fn fido_dev_set_pin_minlen_rpid are defined in .In fido/err.h . On success, .Dv FIDO_OK is returned. .Sh SEE ALSO .Xr fido_cred_pin_minlen 3 , .Xr fido_dev_get_cbor_info 3 , .Xr fido_dev_reset 3 libfido2-1.10.0/man/fido_dev_get_assert.3000066400000000000000000000032771417126203300201310ustar00rootroot00000000000000.\" 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 FIDO2 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 FIDO2 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 libfido2-1.10.0/man/fido_dev_get_touch_begin.3000066400000000000000000000033301417126203300211040ustar00rootroot00000000000000.\" Copyright (c) 2020 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: August 5 2020 $ .Dt FIDO_DEV_GET_TOUCH_BEGIN 3 .Os .Sh NAME .Nm fido_dev_get_touch_begin , .Nm fido_dev_get_touch_status .Nd asynchronously wait for touch on a FIDO2 authenticator .Sh SYNOPSIS .In fido.h .Ft int .Fn fido_dev_get_touch_begin "fido_dev_t *dev" .Ft int .Fn fido_dev_get_touch_status "fido_dev_t *dev" "int *touched" "int ms" .Sh DESCRIPTION The functions described in this page allow an application to asynchronously wait for touch on a FIDO2 authenticator. This is useful when multiple authenticators are present and the application needs to know which one to use. .Pp The .Fn fido_dev_get_touch_begin function initiates a touch request on .Fa dev . .Pp The .Fn fido_dev_get_touch_status function continues an ongoing touch request on .Fa dev , blocking up to .Fa ms milliseconds. On success, .Fa touched will be updated to reflect the touch request status. If .Fa touched is 1, the device was touched, and the touch request is terminated. If .Fa touched is 0, the application may call .Fn fido_dev_get_touch_status to continue the touch request, or .Fn fido_dev_cancel to terminate it. .Sh RETURN VALUES The error codes returned by .Fn fido_dev_get_touch_begin and .Fn fido_dev_get_touch_status are defined in .In fido/err.h . On success, .Dv FIDO_OK is returned. .Sh EXAMPLES Please refer to .Em examples/select.c in .Em libfido2's source tree. .Sh SEE ALSO .Xr fido_dev_cancel 3 .Sh CAVEATS The .Fn fido_dev_get_touch_status function will cause a command to be transmitted to U2F authenticators. These transmissions should not exceed a frequency of 5Hz. libfido2-1.10.0/man/fido_dev_info_manifest.3000066400000000000000000000107131417126203300206030ustar00rootroot00000000000000.\" 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 , .Nm fido_dev_info_set .Nd FIDO2 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" .Ft int .Fn fido_dev_info_set "fido_dev_info_t *devlist" "size_t i" "const char *path" "const char *manufacturer" "const char *product" "const fido_dev_io_t *io" "const fido_dev_transport_t *transport" .Sh DESCRIPTION The .Fn fido_dev_info_manifest function fills .Fa devlist with up to .Fa ilen FIDO2 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 . If .Fa di does not have an associated manufacturer string, .Fn fido_dev_info_manufacturer_string returns an empty string. .Pp The .Fn fido_dev_info_product_string function returns the product string of .Fa di . If .Fa di does not have an associated product string, .Fn fido_dev_info_product_string returns an empty string. .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 . .Pp The .Fn fido_dev_info_set function initializes an entry in a device list allocated by .Fn fido_dev_info_new with the specified path, manufacturer, and product strings, and with the specified I/O handlers and, optionally, transport functions, as described in .Xr fido_dev_set_io_functions 3 . The .Fa io argument must be specified; the .Fa transport argument may be .Dv NULL . The path, I/O handlers, and transport functions will be used automatically by .Xr fido_dev_new_with_info 3 and .Xr fido_dev_open_with_info 3 . An application can use this, for example, to substitute mock FIDO2 devices in testing for the real ones that .Fn fido_dev_info_manifest would discover. .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 On success, the .Fn fido_dev_info_set function returns .Dv FIDO_OK . On error, a different error code defined in .In fido/err.h is returned. .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. libfido2-1.10.0/man/fido_dev_largeblob_get.3000066400000000000000000000115031417126203300205500ustar00rootroot00000000000000.\" Copyright (c) 2020 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: October 26 2020 $ .Dt FIDO_LARGEBLOB_GET 3 .Os .Sh NAME .Nm fido_dev_largeblob_get , .Nm fido_dev_largeblob_set , .Nm fido_dev_largeblob_remove , .Nm fido_dev_largeblob_get_array , .Nm fido_dev_largeblob_set_array .Nd FIDO2 large blob API .Sh SYNOPSIS .In fido.h .Ft int .Fn fido_dev_largeblob_get "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "unsigned char **blob_ptr" "size_t *blob_len" .Ft int .Fn fido_dev_largeblob_set "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "const unsigned char *blob_ptr" "size_t blob_len" "const char *pin" .Ft int .Fn fido_dev_largeblob_remove "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "const char *pin" .Ft int .Fn fido_dev_largeblob_get_array "fido_dev_t *dev" "unsigned char **cbor_ptr" "size_t *cbor_len" .Ft int .Fn fido_dev_largeblob_set_array "fido_dev_t *dev" "const unsigned char *cbor_ptr" "size_t cbor_len" "const char *pin" .Sh DESCRIPTION The .Dq largeBlobs API of .Em libfido2 allows binary blobs residing on a CTAP 2.1 authenticator to be read, written, and inspected. .Dq largeBlobs is a CTAP 2.1 extension. .Pp .Dq largeBlobs are stored as elements of a CBOR array. Confidentiality is ensured by encrypting each element with a distinct, credential-bound 256-bit AES-GCM key. The array is otherwise shared between different credentials and FIDO2 relying parties. .Pp Retrieval of a credential's encryption key is possible during enrollment with .Xr fido_cred_set_extensions 3 and .Xr fido_cred_largeblob_key_ptr 3 , during assertion with .Xr fido_assert_set_extensions 3 and .Xr fido_assert_largeblob_key_ptr 3 , or, in the case of a resident credential, via .Em libfido2's credential management API. .Pp The .Dq largeBlobs CBOR array is opaque to the authenticator. Management of the array is left at the discretion of FIDO2 clients. For further details on CTAP 2.1's .Dq largeBlobs extension, please refer to the CTAP 2.1 spec. .Pp The .Fn fido_dev_largeblob_get function retrieves the authenticator's .Dq largeBlobs CBOR array and, on success, returns the first blob .Pq iterating from array index zero that can be decrypted by .Fa key_ptr , where .Fa key_ptr points to .Fa key_len bytes. On success, .Fn fido_dev_largeblob_get sets .Fa blob_ptr to the body of the decrypted blob, and .Fa blob_len to the length of the decrypted blob in bytes. It is the caller's responsibility to free .Fa blob_ptr . .Pp The .Fn fido_dev_largeblob_set function uses .Fa key_ptr to encrypt .Fa blob_ptr and inserts the result in the authenticator's .Dq largeBlobs CBOR array. Insertion happens at the end of the array if no existing element can be decrypted by .Fa key_ptr , or at the position of the first element .Pq iterating from array index zero that can be decrypted by .Fa key_ptr . .Fa key_len holds the length of .Fa key_ptr in bytes, and .Fa blob_len the length of .Fa blob_ptr in bytes. A .Fa pin or equivalent user-verification gesture is required. .Pp The .Fn fido_dev_largeblob_remove function retrieves the authenticator's .Dq largeBlobs CBOR array and, on success, drops the first blob .Pq iterating from array index zero that can be decrypted by .Fa key_ptr , where .Fa key_ptr points to .Fa key_len bytes. A .Fa pin or equivalent user-verification gesture is required. .Pp The .Fn fido_dev_largeblob_get_array function retrieves the authenticator's .Dq largeBlobs CBOR array and, on success, sets .Fa cbor_ptr to the body of the CBOR array, and .Fa cbor_len to its corresponding length in bytes. It is the caller's responsibility to free .Fa cbor_ptr . .Pp Finally, the .Fn fido_dev_largeblob_set_array function sets the authenticator's .Dq largeBlobs CBOR array to the data pointed to by .Fa cbor_ptr , where .Fa cbor_ptr points to .Fa cbor_len bytes. A .Fa pin or equivalent user-verification gesture is required. .Sh RETURN VALUES The functions .Fn fido_dev_largeblob_set , .Fn fido_dev_largeblob_get , .Fn fido_dev_largeblob_remove , .Fn fido_dev_largeblob_get_array , and .Fn fido_dev_largeblob_set_array return .Dv FIDO_OK on success. On error, an error code defined in .In fido/err.h is returned. .Sh SEE ALSO .Xr fido_assert_largeblob_key_len 3 , .Xr fido_assert_largeblob_key_ptr 3 , .Xr fido_assert_set_extensions 3 , .Xr fido_cred_largeblob_key_len 3 , .Xr fido_cred_largeblob_key_ptr 3 , .Xr fido_cred_set_extensions 3 , .Xr fido_credman_dev_get_rk 3 , .Xr fido_credman_dev_get_rp 3 , .Xr fido_dev_get_assert 3 , .Xr fido_dev_make_cred 3 .Sh CAVEATS The .Dq largeBlobs extension is not meant to be used to store sensitive data. When retrieved, a credential's .Dq largeBlobs encryption key is transmitted in the clear, and an authenticator's .Dq largeBlobs CBOR array can be read without user interaction or verification. libfido2-1.10.0/man/fido_dev_make_cred.3000066400000000000000000000031431417126203300176730ustar00rootroot00000000000000.\" 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 FIDO2 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 FIDO2 device represented by .Fa dev to generate a new credential according to the following parameters defined in .Fa cred : .Pp .Bl -dash -compact .It .Nm type ; .It .Nm client data hash ; .It .Nm relying party ; .It .Nm user attributes ; .It .Nm list of excluded credential IDs ; .It .Nm resident/discoverable key and user verification attributes . .El .Pp See .Xr fido_cred_set_authdata 3 for information on how these values are set. .Pp If a PIN is not needed to authenticate the request against .Fa dev , then .Fa pin may be NULL. Otherwise .Fa pin must point to a NUL-terminated UTF-8 string. .Pp After a successful call to .Fn fido_dev_make_cred , the .Xr fido_cred_authdata_ptr 3 , .Xr fido_cred_pubkey_ptr 3 , .Xr fido_cred_x5c_ptr 3 , and .Xr fido_cred_sig_ptr 3 functions may be invoked on .Fa cred to retrieve the various parts of the generated credential. .Pp Please note that .Fn fido_dev_make_cred is synchronous and will block if necessary. .Sh RETURN VALUES The error codes returned by .Fn fido_dev_make_cred are defined in .In fido/err.h . On success, .Dv FIDO_OK is returned. .Sh SEE ALSO .Xr fido_cred_new 3 , .Xr fido_cred_set_authdata 3 libfido2-1.10.0/man/fido_dev_open.3000066400000000000000000000131311417126203300167200ustar00rootroot00000000000000.\" 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_open_with_info , .Nm fido_dev_close , .Nm fido_dev_cancel , .Nm fido_dev_new , .Nm fido_dev_new_with_info , .Nm fido_dev_free , .Nm fido_dev_force_fido2 , .Nm fido_dev_force_u2f , .Nm fido_dev_is_fido2 , .Nm fido_dev_is_winhello , .Nm fido_dev_supports_credman , .Nm fido_dev_supports_cred_prot , .Nm fido_dev_supports_permissions , .Nm fido_dev_supports_pin , .Nm fido_dev_supports_uv , .Nm fido_dev_has_pin , .Nm fido_dev_has_uv , .Nm fido_dev_protocol , .Nm fido_dev_build , .Nm fido_dev_flags , .Nm fido_dev_major , .Nm fido_dev_minor .Nd FIDO2 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_open_with_info "fido_dev_t *dev" .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 fido_dev_t * .Fn fido_dev_new_with_info "const fido_dev_info_t *" .Ft void .Fn fido_dev_free "fido_dev_t **dev_p" .Ft void .Fn fido_dev_force_fido2 "fido_dev_t *dev" .Ft void .Fn fido_dev_force_u2f "fido_dev_t *dev" .Ft bool .Fn fido_dev_is_fido2 "const fido_dev_t *dev" .Ft bool .Fn fido_dev_is_winhello "const fido_dev_t *dev" .Ft bool .Fn fido_dev_supports_credman "const fido_dev_t *dev" .Ft bool .Fn fido_dev_supports_cred_prot "const fido_dev_t *dev" .Ft bool .Fn fido_dev_supports_permissions "const fido_dev_t *dev" .Ft bool .Fn fido_dev_supports_pin "const fido_dev_t *dev" .Ft bool .Fn fido_dev_supports_uv "const fido_dev_t *dev" .Ft bool .Fn fido_dev_has_pin "const fido_dev_t *dev" .Ft bool .Fn fido_dev_has_uv "const fido_dev_t *dev" .Ft uint8_t .Fn fido_dev_protocol "const fido_dev_t *dev" .Ft uint8_t .Fn fido_dev_build "const fido_dev_t *dev" .Ft uint8_t .Fn fido_dev_flags "const fido_dev_t *dev" .Ft uint8_t .Fn fido_dev_major "const fido_dev_t *dev" .Ft uint8_t .Fn fido_dev_minor "const fido_dev_t *dev" .Sh DESCRIPTION The .Fn fido_dev_open function opens the device pointed to by .Fa path , where .Fa dev is a freshly allocated or otherwise closed .Vt fido_dev_t . If .Fa dev claims to be FIDO2, .Em libfido2 will attempt to speak FIDO2 to .Fa dev . If that fails, .Em libfido2 will fallback to U2F unless the .Dv FIDO_DISABLE_U2F_FALLBACK flag was set in .Xr fido_init 3 . .Pp The .Fn fido_dev_open_with_info function opens .Fa dev as previously allocated using .Fn fido_dev_new_with_info . .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_new_with_info function returns a pointer to a newly allocated .Vt fido_dev_t with .Vt fido_dev_info_t parameters, for use with .Xr fido_dev_info_manifest 3 and .Fn fido_dev_open_with_info . 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 , where .Fa dev is an open device. .Pp The .Fn fido_dev_force_u2f function can be used to force CTAP1 (U2F) communication with .Fa dev , where .Fa dev is an open device. .Pp The .Fn fido_dev_is_fido2 function returns .Dv true if .Fa dev is a FIDO2 device. .Pp The .Fn fido_dev_is_winhello function returns .Dv true if .Fa dev is a Windows Hello device. .Pp The .Fn fido_dev_supports_credman function returns .Dv true if .Fa dev supports CTAP 2.1 Credential Management. .Pp The .Fn fido_dev_supports_cred_prot function returns .Dv true if .Fa dev supports CTAP 2.1 Credential Protection. .Pp The .Fn fido_dev_supports_permissions function returns .Dv true if .Fa dev supports CTAP 2.1 UV token permissions. .Pp The .Fn fido_dev_supports_pin function returns .Dv true if .Fa dev supports CTAP 2.0 Client PINs. .Pp The .Fn fido_dev_supports_uv function returns .Dv true if .Fa dev supports a built-in user verification method. .Pp The .Fn fido_dev_has_pin function returns .Dv true if .Fa dev has a CTAP 2.0 Client PIN set. .Pp The .Fn fido_dev_has_uv function returns .Dv true if .Fa dev supports built-in user verification and its user verification feature is configured. .Pp The .Fn fido_dev_protocol function returns the CTAPHID protocol version identifier of .Fa dev . .Pp The .Fn fido_dev_build function returns the CTAPHID build version number of .Fa dev . .Pp The .Fn fido_dev_flags function returns the CTAPHID capabilities flags of .Fa dev . .Pp The .Fn fido_dev_major function returns the CTAPHID major version number of .Fa dev . .Pp The .Fn fido_dev_minor function returns the CTAPHID minor version number of .Fa dev . .Pp For the format and meaning of the CTAPHID parameters returned by functions above, please refer to the FIDO Client to Authenticator Protocol (CTAP) specification. .Sh RETURN VALUES On success, .Fn fido_dev_open , .Fn fido_dev_open_with_info , and .Fn fido_dev_close return .Dv FIDO_OK . On error, a different error code defined in .In fido/err.h is returned. .Sh SEE ALSO .Xr fido_dev_info_manifest 3 , .Xr fido_dev_set_io_functions 3 , .Xr fido_init 3 libfido2-1.10.0/man/fido_dev_set_io_functions.3000066400000000000000000000141641417126203300213400ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: May 25 2018 $ .Dt FIDO_DEV_SET_IO_FUNCTIONS 3 .Os .Sh NAME .Nm fido_dev_set_io_functions , .Nm fido_dev_set_sigmask , .Nm fido_dev_set_timeout , .Nm fido_dev_set_transport_functions , .Nm fido_dev_io_handle .Nd FIDO2 device I/O interface .Sh SYNOPSIS .In fido.h .Bd -literal typedef void *fido_dev_io_open_t(const char *); typedef void fido_dev_io_close_t(void *); typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int); typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t); typedef struct fido_dev_io { fido_dev_io_open_t *open; fido_dev_io_close_t *close; fido_dev_io_read_t *read; fido_dev_io_write_t *write; } fido_dev_io_t; #ifdef _WIN32 typedef int fido_sigset_t; #else typedef sigset_t fido_sigset_t; #endif typedef int fido_dev_rx_t(struct fido_dev *, uint8_t, unsigned char *, size_t, int); typedef int fido_dev_tx_t(struct fido_dev *, uint8_t, const unsigned char *, size_t); typedef struct fido_dev_transport { fido_dev_rx_t *rx; fido_dev_tx_t *tx; } fido_dev_transport_t; .Ed .Pp .Ft int .Fn fido_dev_set_io_functions "fido_dev_t *dev" "const fido_dev_io_t *io" .Ft int .Fn fido_dev_set_sigmask "fido_dev_t *dev" "const fido_sigset_t *sigmask" .Ft int .Fn fido_dev_set_timeout "fido_dev_t *dev" "int ms" .Ft int .Fn fido_dev_set_transport_functions "fido_dev_t *dev" "const fido_dev_transport_t *t" .Ft void * .Fn fido_dev_io_handle "const fido_dev_t *dev" .Sh DESCRIPTION The .Fn fido_dev_set_io_functions function sets the I/O handlers used by .Em libfido2 to talk to .Fa dev . By default, these handlers are set to the operating system's native HID or NFC interfaces. They are defined as follows: .Bl -tag -width Ds .It Vt fido_dev_open_t Receives a .Vt const char * holding a path and opens the corresponding device, returning a non-NULL opaque pointer on success and NULL on error. .It Vt fido_dev_close_t Receives the opaque pointer returned by .Vt fido_dev_open_t and closes the device. .It Vt fido_dev_read_t Reads a single transmission unit (HID report, APDU) from a device. The first parameter is the opaque pointer returned by .Vt fido_dev_open_t . The second parameter is the read buffer, and the third parameter is the read buffer size. The fourth parameter is the number of milliseconds the caller is willing to sleep, should the call need to block. If this value holds -1, .Vt fido_dev_read_t may block indefinitely. On success, the number of bytes read is returned. On error, -1 is returned. .It Vt fido_dev_write_t Writes a single transmission unit (HID report, APDU) to .Fa dev . The first parameter is the opaque pointer returned by .Vt fido_dev_open_t . The second parameter is the write buffer, and the third parameter is the number of bytes to be written. A .Vt fido_dev_write_t may block. On success, the number of bytes written is returned. On error, -1 is returned. .El .Pp When calling .Fn fido_dev_set_io_functions , the .Fa open , .Fa close , .Fa read , and .Fa write fields of .Fa io may not be NULL. .Pp No references to .Fa io are held by .Fn fido_dev_set_io_functions . .Pp The .Fn fido_dev_set_sigmask function may be used to specify a non-NULL signal mask .Fa sigmask to be used while .Em libfido2's default I/O handlers wait on .Fa dev . On UNIX-like operating systems, .Vt fido_sigset_t is defined as .Vt sigset_t . On Windows, .Vt fido_sigset_t is defined as .Vt int and .Fn fido_dev_set_sigmask is a no-op. .Pp No references to .Fa sigmask are held by .Fn fido_dev_set_sigmask . .Pp The .Fn fido_dev_set_timeout function informs .Em libfido2 not to block for more than .Fa ms milliseconds while communicating with .Fa dev . If a timeout occurs, the corresponding .Em fido_dev_* function will fail with .Dv FIDO_ERR_RX . If .Fa ms is -1, then .Em libfido2 may block indefinitely. This is the default behaviour. When using the Windows Hello backend, .Fa ms is used as a guidance and may be overwritten by the platform. .Pp The .Fn fido_dev_set_transport_functions function sets the transport functions used by .Em libfido2 to talk to .Fa dev . While the I/O handlers are responsible for sending and receiving transmission units of initialization and continuation packets already formatted by .Em libfido2 , the transport handlers are responsible for sending and receiving the CTAPHID commands and data directly, as defined in the FIDO Client to Authenticator Protocol (CTAP) standard. They are defined as follows: .Bl -tag -width Ds .It Vt fido_dev_tx_t Receives a device, a CTAPHID command to transmit, a data buffer to transmit, and the length of the data buffer. On success, 0 is returned. On error, -1 is returned. .It Vt fido_dev_rx_t Receives a device, a CTAPHID command whose response the caller expects to receive, a data buffer to receive into, the size of the data buffer determining the maximum length of a response, and the maximum number of milliseconds to wait for a response. On success, the number of bytes read into the data buffer is returned. On error, -1 is returned. .El .Pp When transport functions are specified, .Em libfido2 will use them instead of the .Dv read and .Dv write functions of the I/O handlers. However, the I/O handlers must still be specified to open and close the device. .Pp The .Fn fido_dev_io_handle function returns the opaque pointer returned by the .Dv open function of the I/O handlers. This is useful mainly for the transport functions, which unlike the I/O handlers are passed the .Vt fido_dev_t pointer instead of the opaque I/O handle. .Sh RETURN VALUES On success, .Fn fido_dev_set_io_functions , .Fn fido_dev_set_transport_functions , .Fn fido_dev_set_sigmask , and .Fn fido_dev_set_timeout 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_open 3 .Rs .%D 2021-06-15 .%O Proposed Standard, Version 2.1 .%Q FIDO Alliance .%R Client to Authenticator Protocol (CTAP) .%U https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html .Re libfido2-1.10.0/man/fido_dev_set_pin.3000066400000000000000000000043621417126203300174260ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: May 25 2018 $ .Dt FIDO_DEV_SET_PIN 3 .Os .Sh NAME .Nm fido_dev_set_pin , .Nm fido_dev_get_retry_count , .Nm fido_dev_get_uv_retry_count , .Nm fido_dev_reset .Nd FIDO2 device management functions .Sh SYNOPSIS .In fido.h .Ft int .Fn fido_dev_set_pin "fido_dev_t *dev" "const char *pin" "const char *oldpin" .Ft int .Fn fido_dev_get_retry_count "fido_dev_t *dev" "int *retries" .Ft int .Fn fido_dev_get_uv_retry_count "fido_dev_t *dev" "int *retries" .Ft int .Fn fido_dev_reset "fido_dev_t *dev" .Sh DESCRIPTION The .Fn fido_dev_set_pin function sets the PIN of device .Fa dev to .Fa pin , where .Fa pin is a NUL-terminated UTF-8 string. If .Fa oldpin is not NULL, the device's PIN is changed from .Fa oldpin to .Fa pin , where .Fa pin and .Fa oldpin are NUL-terminated UTF-8 strings. .Pp The .Fn fido_dev_get_retry_count function fills .Fa retries with the number of PIN retries left in .Fa dev before lock-out, where .Fa retries is an addressable pointer. .Pp The .Fn fido_dev_get_uv_retry_count function fills .Fa retries with the number of built-in UV retries left in .Fa dev before built-in UV is disabled, where .Fa retries is an addressable pointer. .Pp The .Fn fido_dev_reset function performs a reset on .Fa dev , resetting the device's PIN and erasing credentials stored on the device. .Pp Please note that .Fn fido_dev_set_pin , .Fn fido_dev_get_retry_count , .Fn fido_dev_get_uv_retry_count , and .Fn fido_dev_reset are synchronous and will block if necessary. .Sh RETURN VALUES The error codes returned by .Fn fido_dev_set_pin , .Fn fido_dev_get_retry_count , .Fn fido_dev_get_uv_retry_count , and .Fn fido_dev_reset are defined in .In fido/err.h . On success, .Dv FIDO_OK is returned. .Sh CAVEATS Regarding .Fn fido_dev_reset , the actual user-flow to perform a reset is outside the scope of the FIDO2 specification, and may therefore vary depending on the authenticator. Yubico authenticators will return .Dv FIDO_ERR_NOT_ALLOWED if a reset is issued later than 5 seconds after power-up, and .Dv FIDO_ERR_ACTION_TIMEOUT if the user fails to confirm the reset by touching the key within 30 seconds. libfido2-1.10.0/man/fido_init.3000066400000000000000000000026251417126203300160720ustar00rootroot00000000000000.\" 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 , .Nm fido_set_log_handler .Nd initialise the FIDO2 library .Sh SYNOPSIS .In fido.h .Bd -literal typedef void fido_log_handler_t(const char *); .Ed .Pp .Ft void .Fn fido_init "int flags" .Ft void .Fn fido_set_log_handler "fido_log_handler_t *handler" .Sh DESCRIPTION The .Fn fido_init function initialises the .Em libfido2 library. Its invocation must precede that of any other .Em libfido2 function in the context of the executing thread. .Pp If .Dv FIDO_DEBUG is set in .Fa flags , then debug output will be emitted by .Em libfido2 on .Em stderr . Alternatively, the .Ev FIDO_DEBUG environment variable may be set. .Pp If .Dv FIDO_DISABLE_U2F_FALLBACK is set in .Fa flags , then .Em libfido2 will not fallback to U2F in .Xr fido_dev_open 3 if a device claims to support FIDO2 but fails to respond to a CTAP 2.0 greeting. .Pp The .Fn fido_set_log_handler function causes .Fa handler to be called for each log line generated in the context of the executing thread. Lines passed to .Fa handler include a trailing newline character and are not printed by .Em libfido2 on .Em stderr . .Sh SEE ALSO .Xr fido_assert_new 3 , .Xr fido_cred_new 3 , .Xr fido_dev_info_manifest 3 , .Xr fido_dev_open 3 libfido2-1.10.0/man/fido_strerr.3000066400000000000000000000010651417126203300164450ustar00rootroot00000000000000.\" 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 FIDO2 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. libfido2-1.10.0/man/rs256_pk_new.3000066400000000000000000000050111417126203300163420ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" .Dd $Mdocdate: 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_EVP_PKEY , .Nm rs256_pk_from_ptr , .Nm rs256_pk_to_EVP_PKEY .Nd FIDO2 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_EVP_PKEY "rs256_pk_t *pk" "const EVP_PKEY *pkey" .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_EVP_PKEY function fills .Fa pk with the contents of .Fa pkey . No references to .Fa pkey are kept. .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_EVP_PKEY , .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 libfido2-1.10.0/man/style.css000066400000000000000000000012521417126203300157070ustar00rootroot00000000000000* { 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; } libfido2-1.10.0/openbsd-compat/000077500000000000000000000000001417126203300161755ustar00rootroot00000000000000libfido2-1.10.0/openbsd-compat/bsd-getline.c000066400000000000000000000057011417126203300205410ustar00rootroot00000000000000/* $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 libfido2-1.10.0/openbsd-compat/bsd-getpagesize.c000066400000000000000000000010011417126203300214060ustar00rootroot00000000000000/* 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) */ libfido2-1.10.0/openbsd-compat/clock_gettime.c000066400000000000000000000012041417126203300211470ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "openbsd-compat.h" #if !defined(HAVE_CLOCK_GETTIME) #if _WIN32 int clock_gettime(clockid_t clock_id, struct timespec *tp) { ULONGLONG ms; if (clock_id != CLOCK_MONOTONIC) { errno = EINVAL; return (-1); } ms = GetTickCount64(); tp->tv_sec = ms / 1000L; tp->tv_nsec = (ms % 1000L) * 1000000L; return (0); } #else #error "please provide an implementation of clock_gettime() for your platform" #endif /* _WIN32 */ #endif /* !defined(HAVE_CLOCK_GETTIME) */ libfido2-1.10.0/openbsd-compat/endian_win32.c000066400000000000000000000024351417126203300206250ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "openbsd-compat.h" #if defined(_WIN32) && !defined(HAVE_ENDIAN_H) /* * Hopefully, if the endianness differs from the end result, the compiler * optimizes these functions with some type of bswap instruction. Or, * otherwise, to just return the input value unmodified. GCC and clang * both does these optimization at least. This should be preferred over * relying on some BYTE_ORDER macro, which may or may not be defined. */ uint32_t htole32(uint32_t in) { uint32_t out = 0; uint8_t *b = (uint8_t *)&out; b[0] = (uint8_t)((in >> 0) & 0xff); b[1] = (uint8_t)((in >> 8) & 0xff); b[2] = (uint8_t)((in >> 16) & 0xff); b[3] = (uint8_t)((in >> 24) & 0xff); return (out); } uint64_t htole64(uint64_t in) { uint64_t out = 0; uint8_t *b = (uint8_t *)&out; b[0] = (uint8_t)((in >> 0) & 0xff); b[1] = (uint8_t)((in >> 8) & 0xff); b[2] = (uint8_t)((in >> 16) & 0xff); b[3] = (uint8_t)((in >> 24) & 0xff); b[4] = (uint8_t)((in >> 32) & 0xff); b[5] = (uint8_t)((in >> 40) & 0xff); b[6] = (uint8_t)((in >> 48) & 0xff); b[7] = (uint8_t)((in >> 56) & 0xff); return (out); } #endif /* WIN32 && !HAVE_ENDIAN_H */ libfido2-1.10.0/openbsd-compat/err.h000066400000000000000000000024451417126203300171430ustar00rootroot00000000000000/* * 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 */ libfido2-1.10.0/openbsd-compat/explicit_bzero.c000066400000000000000000000022311417126203300213610ustar00rootroot00000000000000/* 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) */ libfido2-1.10.0/openbsd-compat/explicit_bzero_win32.c000066400000000000000000000005161417126203300224070ustar00rootroot00000000000000/* * 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) */ libfido2-1.10.0/openbsd-compat/freezero.c000066400000000000000000000017421417126203300201660ustar00rootroot00000000000000/* * Copyright (c) 2008, 2010, 2011, 2016 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. */ #include "openbsd-compat.h" #ifndef HAVE_FREEZERO void freezero(void *ptr, size_t sz) { if (ptr == NULL) return; explicit_bzero(ptr, sz); free(ptr); } #endif /* HAVE_FREEZERO */ libfido2-1.10.0/openbsd-compat/getopt.h000066400000000000000000000053221417126203300176520ustar00rootroot00000000000000/* $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_ */ libfido2-1.10.0/openbsd-compat/getopt_long.c000066400000000000000000000343021417126203300206640ustar00rootroot00000000000000/* $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) */ libfido2-1.10.0/openbsd-compat/openbsd-compat.h000066400000000000000000000047031417126203300212650ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #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 htobe16(x) OSSwapHostToBigInt16((x)) #define be32toh(x) OSSwapBigToHostInt32((x)) #define htobe32(x) OSSwapHostToBigInt32((x)) #define htole32(x) OSSwapHostToLittleInt32((x)) #define htole64(x) OSSwapHostToLittleInt64((x)) #endif /* __APPLE__ && !HAVE_ENDIAN_H */ #if defined(_WIN32) && !defined(HAVE_ENDIAN_H) #include #include #if !defined(_MSC_VER) #include #endif #define be16toh(x) ntohs((x)) #define htobe16(x) htons((x)) #define be32toh(x) ntohl((x)) #define htobe32(x) htonl((x)) uint32_t htole32(uint32_t); uint64_t htole64(uint64_t); #endif /* _WIN32 && !HAVE_ENDIAN_H */ #if (defined(__FreeBSD__) || defined(__MidnightBSD__)) && !defined(HAVE_ENDIAN_H) #include #endif #include #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_STRSEP) char *strsep(char **, const char *); #endif #if !defined(HAVE_RECALLOCARRAY) void *recallocarray(void *, size_t, size_t, size_t); #endif #if !defined(HAVE_EXPLICIT_BZERO) void explicit_bzero(void *, size_t); #endif #if !defined(HAVE_FREEZERO) void freezero(void *, size_t); #endif #if !defined(HAVE_GETPAGESIZE) int getpagesize(void); #endif #if !defined(HAVE_TIMINGSAFE_BCMP) int timingsafe_bcmp(const void *, const void *, size_t); #endif #if !defined(HAVE_READPASSPHRASE) #include "readpassphrase.h" #else #include #endif #include #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 #if defined(_MSC_VER) #define strerror_r(e, b, l) strerror_s((b), (l), (e)) #endif #include "time.h" #if !defined(HAVE_POSIX_IOCTL) #define IOCTL_REQ(x) (x) #else #define IOCTL_REQ(x) ((int)(x)) #endif #endif /* !_OPENBSD_COMPAT_H */ libfido2-1.10.0/openbsd-compat/posix_ioctl_check.c000066400000000000000000000001241417126203300220270ustar00rootroot00000000000000#include int posix_ioctl_check(int fd) { return ioctl(fd, -1, 0); } libfido2-1.10.0/openbsd-compat/posix_win.c000066400000000000000000000015311417126203300203600ustar00rootroot00000000000000/* * 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)); } libfido2-1.10.0/openbsd-compat/posix_win.h000066400000000000000000000016141417126203300203670ustar00rootroot00000000000000/* * 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 */ libfido2-1.10.0/openbsd-compat/readpassphrase.c000066400000000000000000000135071417126203300213540ustar00rootroot00000000000000/* $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 */ libfido2-1.10.0/openbsd-compat/readpassphrase.h000066400000000000000000000034011417126203300213510ustar00rootroot00000000000000/* $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 #include #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_ */ libfido2-1.10.0/openbsd-compat/readpassphrase_win32.c000066400000000000000000000067751417126203300224070ustar00rootroot00000000000000/* * 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()) (void)_getch(); wtmp = utf8_to_utf16(prompt); if (wtmp == NULL) errx(1, "unable to alloc memory"); _cputws(wtmp); free(wtmp); while (current_index < outBufLen - 1) { ch = (char)_getch(); if (ch == '\r') { if (_kbhit()) (void)_getch(); /* read linefeed if its there */ break; } else if (ch == '\n') { break; } else if (ch == '\b') { /* backspace */ if (current_index > 0) { if (flags & RPP_ECHO_ON) printf_s("%c \b", ch); current_index--; /* overwrite last character */ } } else if (ch == '\003') { /* exit on Ctrl+C */ errx(1, ""); } else { if (flags & RPP_SEVENBIT) ch &= 0x7f; if (isalpha((unsigned char)ch)) { if(flags & RPP_FORCELOWER) ch = (char)tolower((unsigned char)ch); if(flags & RPP_FORCEUPPER) ch = (char)toupper((unsigned char)ch); } outBuf[current_index++] = ch; if(flags & RPP_ECHO_ON) printf_s("%c", ch); } } outBuf[current_index] = '\0'; _cputs("\n"); return outBuf; } #endif /* HAVE_READPASSPHRASE */ libfido2-1.10.0/openbsd-compat/recallocarray.c000066400000000000000000000046631417126203300211750ustar00rootroot00000000000000/* $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) */ libfido2-1.10.0/openbsd-compat/strlcat.c000066400000000000000000000034441417126203300200220ustar00rootroot00000000000000/* $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) */ libfido2-1.10.0/openbsd-compat/strlcpy.c000066400000000000000000000032521417126203300200430ustar00rootroot00000000000000/* $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) */ libfido2-1.10.0/openbsd-compat/strsep.c000066400000000000000000000051431417126203300176640ustar00rootroot00000000000000/* $OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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/string/strsep.c */ #include "openbsd-compat.h" #if !defined(HAVE_STRSEP) #include #include /* * Get next token from string *stringp, where tokens are possibly-empty * strings separated by characters from delim. * * Writes NULs into the string at *stringp to end tokens. * delim need not remain constant from call to call. * On return, *stringp points past the last NUL written (if there might * be further tokens), or is NULL (if there are definitely no more tokens). * * If *stringp is NULL, strsep returns NULL. */ char * strsep(char **stringp, const char *delim) { char *s; const char *spanp; int c, sc; char *tok; if ((s = *stringp) == NULL) return (NULL); for (tok = s;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; *stringp = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ } #endif /* !defined(HAVE_STRSEP) */ libfido2-1.10.0/openbsd-compat/time.h000066400000000000000000000025421417126203300173070ustar00rootroot00000000000000/* * Public domain * sys/time.h compatibility shim */ #if defined(_MSC_VER) && (_MSC_VER >= 1900) #include <../ucrt/time.h> #elif defined(_MSC_VER) && (_MSC_VER < 1900) #include <../include/time.h> #else #include #endif #ifndef _COMPAT_TIME_H #define _COMPAT_TIME_H #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC CLOCK_REALTIME #endif #ifndef CLOCK_REALTIME #define CLOCK_REALTIME 0 #endif #ifndef HAVE_CLOCK_GETTIME typedef int clockid_t; int clock_gettime(clockid_t, struct timespec *); #endif #ifdef HAVE_TIMESPECSUB #include #endif #ifndef HAVE_TIMESPECSUB #define timespecadd(tsp, usp, vsp) \ do { \ (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ if ((vsp)->tv_nsec >= 1000000000L) { \ (vsp)->tv_sec++; \ (vsp)->tv_nsec -= 1000000000L; \ } \ } while (0) #define timespecsub(tsp, usp, vsp) \ do { \ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ if ((vsp)->tv_nsec < 0) { \ (vsp)->tv_sec--; \ (vsp)->tv_nsec += 1000000000L; \ } \ } while (0) #define timespeccmp(tsp, usp, cmp) \ (((tsp)->tv_sec == (usp)->tv_sec) ? \ ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ ((tsp)->tv_sec cmp (usp)->tv_sec)) #endif #endif /* _COMPAT_TIME_H */ libfido2-1.10.0/openbsd-compat/timingsafe_bcmp.c000066400000000000000000000023111417126203300214650ustar00rootroot00000000000000/* $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) */ libfido2-1.10.0/openbsd-compat/types.h000066400000000000000000000023301417126203300175100ustar00rootroot00000000000000/* * 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 unsigned long u_long; #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 */ libfido2-1.10.0/regress/000077500000000000000000000000001417126203300147345ustar00rootroot00000000000000libfido2-1.10.0/regress/CMakeLists.txt000066400000000000000000000012051417126203300174720ustar00rootroot00000000000000# Copyright (c) 2018-2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. add_custom_target(regress ALL) macro(add_regress_test NAME SOURCES) add_executable(${NAME} ${SOURCES}) target_link_libraries(${NAME} fido2_shared) add_test(${NAME} ${NAME}) add_dependencies(regress ${NAME}) endmacro() add_custom_command(TARGET regress POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) add_regress_test(regress_cred cred.c) add_regress_test(regress_assert assert.c) add_regress_test(regress_dev dev.c) libfido2-1.10.0/regress/assert.c000066400000000000000000000414261417126203300164100ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #define _FIDO_INTERNAL #include #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 rs256_pk[259] = { 0x9e, 0x54, 0x78, 0xb2, 0x51, 0xbe, 0x19, 0x7c, 0xcb, 0x1a, 0x9a, 0xc3, 0x49, 0x2a, 0x2f, 0xfd, 0x99, 0x64, 0x76, 0xc6, 0xdb, 0xca, 0x38, 0x3f, 0xb0, 0x6a, 0xc9, 0xc0, 0x07, 0x9f, 0x5c, 0x4d, 0xfc, 0xd1, 0x01, 0x7f, 0x69, 0x65, 0xab, 0x9c, 0x2a, 0xc2, 0x95, 0xd9, 0x44, 0xf3, 0xea, 0x94, 0x6b, 0x25, 0x66, 0x54, 0x81, 0xee, 0x24, 0x1d, 0xe1, 0x7d, 0x7f, 0xbe, 0xea, 0x76, 0x90, 0x5c, 0xbf, 0x59, 0x22, 0xd3, 0xa0, 0x68, 0x1a, 0x65, 0x8b, 0x2f, 0xb6, 0xa8, 0x30, 0x2d, 0x26, 0x81, 0xfa, 0x9e, 0x59, 0xec, 0x2f, 0xee, 0x59, 0x39, 0xe2, 0x79, 0x19, 0x54, 0x54, 0xdf, 0x24, 0x83, 0xee, 0x61, 0x5a, 0x66, 0x24, 0x2b, 0x7b, 0xfb, 0x82, 0x66, 0xe4, 0x85, 0x18, 0x20, 0x76, 0xe5, 0x4a, 0xb6, 0xcb, 0xec, 0x43, 0xbe, 0xfd, 0xb0, 0x8f, 0xfd, 0x2f, 0x69, 0xda, 0x06, 0x9c, 0x09, 0x68, 0x7a, 0x94, 0x6c, 0xb7, 0x51, 0x6d, 0x4c, 0xf7, 0x13, 0xe8, 0xd5, 0x22, 0x6b, 0x1e, 0xba, 0xb9, 0x85, 0xe8, 0x5f, 0xa1, 0x66, 0xe3, 0x20, 0x75, 0x30, 0x11, 0xb5, 0xa3, 0xc3, 0xb0, 0x72, 0x08, 0xff, 0xa3, 0xbb, 0xf1, 0x32, 0x0b, 0x06, 0xc4, 0x12, 0xa3, 0x49, 0x30, 0x19, 0xb9, 0xfe, 0x69, 0x0c, 0xd6, 0xe1, 0x58, 0x36, 0xe6, 0x41, 0x22, 0x41, 0xbf, 0x96, 0x50, 0x35, 0x56, 0x0d, 0x92, 0x8c, 0x34, 0xea, 0x28, 0x91, 0x88, 0x9e, 0x8a, 0xaa, 0x36, 0xd0, 0x0f, 0xbe, 0x16, 0xde, 0x9d, 0x5f, 0x7b, 0xda, 0x52, 0xf7, 0xf1, 0xb6, 0x28, 0x10, 0x05, 0x8f, 0xb9, 0x19, 0x7a, 0xcf, 0x18, 0x9b, 0x40, 0xcd, 0xff, 0x78, 0xea, 0x61, 0x24, 0x3b, 0x80, 0x68, 0x04, 0x9b, 0x40, 0x07, 0x98, 0xd4, 0x94, 0xd1, 0x18, 0x44, 0xa5, 0xed, 0xee, 0x18, 0xc2, 0x25, 0x52, 0x66, 0x42, 0xdf, 0x01, 0x00, 0x01, }; static const unsigned char cdh[32] = { 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7, 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56, 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52, 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76, }; static const unsigned char authdata[39] = { 0x58, 0x25, 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e, 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76, 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86, 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d, 0x97, 0x63, 0x00, 0x00, 0x00, 0x00, 0x03, }; static const unsigned char sig[72] = { 0x30, 0x46, 0x02, 0x21, 0x00, 0xf6, 0xd1, 0xa3, 0xd5, 0x24, 0x2b, 0xde, 0xee, 0xa0, 0x90, 0x89, 0xcd, 0xf8, 0x9e, 0xbd, 0x6b, 0x4d, 0x55, 0x79, 0xe4, 0xc1, 0x42, 0x27, 0xb7, 0x9b, 0x9b, 0xa4, 0x0a, 0xe2, 0x47, 0x64, 0x0e, 0x02, 0x21, 0x00, 0xe5, 0xc9, 0xc2, 0x83, 0x47, 0x31, 0xc7, 0x26, 0xe5, 0x25, 0xb2, 0xb4, 0x39, 0xa7, 0xfc, 0x3d, 0x70, 0xbe, 0xe9, 0x81, 0x0d, 0x4a, 0x62, 0xa9, 0xab, 0x4a, 0x91, 0xc0, 0x7d, 0x2d, 0x23, 0x1e, }; static void * dummy_open(const char *path) { (void)path; return (FAKE_DEV_HANDLE); } static void dummy_close(void *handle) { assert(handle == FAKE_DEV_HANDLE); } static int dummy_read(void *handle, unsigned char *buf, size_t len, int ms) { (void)handle; (void)buf; (void)len; (void)ms; abort(); /* NOTREACHED */ } static int dummy_write(void *handle, const unsigned char *buf, size_t len) { (void)handle; (void)buf; (void)len; abort(); /* NOTREACHED */ } static fido_assert_t * alloc_assert(void) { fido_assert_t *a; a = fido_assert_new(); assert(a != NULL); return (a); } static void free_assert(fido_assert_t *a) { fido_assert_free(&a); assert(a == NULL); } static fido_dev_t * alloc_dev(void) { fido_dev_t *d; d = fido_dev_new(); assert(d != NULL); return (d); } static void free_dev(fido_dev_t *d) { fido_dev_free(&d); assert(d == NULL); } static es256_pk_t * alloc_es256_pk(void) { es256_pk_t *pk; pk = es256_pk_new(); assert(pk != NULL); return (pk); } static void free_es256_pk(es256_pk_t *pk) { es256_pk_free(&pk); assert(pk == NULL); } static rs256_pk_t * alloc_rs256_pk(void) { rs256_pk_t *pk; pk = rs256_pk_new(); assert(pk != NULL); return (pk); } static void free_rs256_pk(rs256_pk_t *pk) { rs256_pk_free(&pk); assert(pk == NULL); } static eddsa_pk_t * alloc_eddsa_pk(void) { eddsa_pk_t *pk; pk = eddsa_pk_new(); assert(pk != NULL); return (pk); } static void free_eddsa_pk(eddsa_pk_t *pk) { eddsa_pk_free(&pk); assert(pk == NULL); } static void empty_assert(fido_dev_t *d, fido_assert_t *a, size_t idx) { es256_pk_t *es256; rs256_pk_t *rs256; eddsa_pk_t *eddsa; assert(fido_assert_flags(a, idx) == 0); assert(fido_assert_authdata_len(a, idx) == 0); assert(fido_assert_authdata_ptr(a, idx) == NULL); assert(fido_assert_clientdata_hash_len(a) == 0); assert(fido_assert_clientdata_hash_ptr(a) == NULL); assert(fido_assert_id_len(a, idx) == 0); assert(fido_assert_id_ptr(a, idx) == NULL); assert(fido_assert_rp_id(a) == NULL); assert(fido_assert_sig_len(a, idx) == 0); assert(fido_assert_sig_ptr(a, idx) == NULL); assert(fido_assert_user_display_name(a, idx) == NULL); assert(fido_assert_user_icon(a, idx) == NULL); assert(fido_assert_user_id_len(a, idx) == 0); assert(fido_assert_user_id_ptr(a, idx) == NULL); assert(fido_assert_user_name(a, idx) == NULL); es256 = alloc_es256_pk(); rs256 = alloc_rs256_pk(); eddsa = alloc_eddsa_pk(); fido_dev_force_u2f(d); assert(fido_dev_get_assert(d, a, NULL) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_dev_get_assert(d, a, "") == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, COSE_ES256, NULL) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, COSE_ES256, es256) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, -1, es256) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, COSE_RS256, rs256) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, COSE_EDDSA, eddsa) == FIDO_ERR_INVALID_ARGUMENT); fido_dev_force_fido2(d); assert(fido_dev_get_assert(d, a, NULL) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_dev_get_assert(d, a, "") == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, COSE_ES256, NULL) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, COSE_ES256, es256) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, -1, es256) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, COSE_RS256, rs256) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_assert_verify(a, idx, COSE_EDDSA, eddsa) == FIDO_ERR_INVALID_ARGUMENT); free_es256_pk(es256); free_rs256_pk(rs256); free_eddsa_pk(eddsa); } static void empty_assert_tests(void) { fido_assert_t *a; fido_dev_t *d; fido_dev_io_t io_f; size_t i; memset(&io_f, 0, sizeof(io_f)); a = alloc_assert(); d = alloc_dev(); io_f.open = dummy_open; io_f.close = dummy_close; io_f.read = dummy_read; io_f.write = dummy_write; assert(fido_dev_set_io_functions(d, &io_f) == FIDO_OK); empty_assert(d, a, 0); assert(fido_assert_count(a) == 0); assert(fido_assert_set_count(a, 4) == FIDO_OK); assert(fido_assert_count(a) == 4); for (i = 0; i < 4; i++) { empty_assert(d, a, i); } empty_assert(d, a, 10); free_assert(a); free_dev(d); } static void valid_assert(void) { fido_assert_t *a; es256_pk_t *es256; rs256_pk_t *rs256; eddsa_pk_t *eddsa; a = alloc_assert(); es256 = alloc_es256_pk(); rs256 = alloc_rs256_pk(); eddsa = alloc_eddsa_pk(); assert(es256_pk_from_ptr(es256, 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, es256) == FIDO_OK); assert(fido_assert_verify(a, 0, COSE_RS256, rs256) == FIDO_ERR_INVALID_SIG); assert(fido_assert_verify(a, 0, COSE_EDDSA, eddsa) == FIDO_ERR_INVALID_SIG); free_assert(a); free_es256_pk(es256); free_rs256_pk(rs256); free_eddsa_pk(eddsa); } 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] = (unsigned char)~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] = (unsigned char)~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] = (unsigned char)~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); } /* rs256 <-> EVP_PKEY transformations */ static void rs256_PKEY(void) { rs256_pk_t *pk1, *pk2; EVP_PKEY *pkey; pk1 = alloc_rs256_pk(); pk2 = alloc_rs256_pk(); assert(rs256_pk_from_ptr(pk1, rs256_pk, sizeof(rs256_pk)) == FIDO_OK); assert((pkey = rs256_pk_to_EVP_PKEY(pk1)) != NULL); assert(rs256_pk_from_EVP_PKEY(pk2, pkey) == FIDO_OK); assert(memcmp(pk1, pk2, sizeof(*pk1)) == 0); free_rs256_pk(pk1); free_rs256_pk(pk2); EVP_PKEY_free(pkey); } /* es256 <-> EVP_PKEY transformations */ static void es256_PKEY(void) { es256_pk_t *pk1, *pk2; EVP_PKEY *pkey; pk1 = alloc_es256_pk(); pk2 = alloc_es256_pk(); assert(es256_pk_from_ptr(pk1, es256_pk, sizeof(es256_pk)) == FIDO_OK); assert((pkey = es256_pk_to_EVP_PKEY(pk1)) != NULL); assert(es256_pk_from_EVP_PKEY(pk2, pkey) == FIDO_OK); assert(memcmp(pk1, pk2, sizeof(*pk1)) == 0); free_es256_pk(pk1); free_es256_pk(pk2); EVP_PKEY_free(pkey); } 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(); rs256_PKEY(); es256_PKEY(); exit(0); } libfido2-1.10.0/regress/cred.c000066400000000000000000002632441417126203300160300ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #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, }; const unsigned char authdata_tpm_rs256[362] = { 0x59, 0x01, 0x67, 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, 0x08, 0x98, 0x70, 0x58, 0xca, 0xdc, 0x4b, 0x81, 0xb6, 0xe1, 0x30, 0xde, 0x50, 0xdc, 0xbe, 0x96, 0x00, 0x20, 0x89, 0x99, 0x6d, 0x5a, 0x00, 0x29, 0xe5, 0x3e, 0x6a, 0x1c, 0x72, 0x6d, 0x71, 0x4a, 0x4f, 0x03, 0x9b, 0x68, 0x17, 0xdb, 0x29, 0x1a, 0x6b, 0x02, 0x6c, 0x26, 0xf9, 0xbd, 0xc3, 0x0e, 0x38, 0x1a, 0xa4, 0x01, 0x03, 0x03, 0x39, 0x01, 0x00, 0x20, 0x59, 0x01, 0x00, 0xc5, 0xb6, 0x9c, 0x06, 0x1d, 0xcf, 0xb9, 0xf2, 0x5e, 0x99, 0x7d, 0x6d, 0x73, 0xd8, 0x36, 0xc1, 0x4a, 0x90, 0x05, 0x4d, 0x82, 0x57, 0xc1, 0xb6, 0x6a, 0xd1, 0x43, 0x03, 0x85, 0xf8, 0x52, 0x4f, 0xd2, 0x27, 0x91, 0x0b, 0xb5, 0x93, 0xa0, 0x68, 0xf8, 0x80, 0x1b, 0xaa, 0x65, 0x97, 0x45, 0x11, 0x86, 0x34, 0xd6, 0x67, 0xf8, 0xd5, 0x12, 0x79, 0x84, 0xee, 0x70, 0x99, 0x00, 0x63, 0xa8, 0xb4, 0x43, 0x0b, 0x4c, 0x57, 0x4a, 0xd6, 0x9b, 0x75, 0x63, 0x8a, 0x46, 0x57, 0xdb, 0x14, 0xc8, 0x71, 0xd1, 0xb3, 0x07, 0x68, 0x58, 0xbc, 0x55, 0x84, 0x80, 0x2a, 0xd2, 0x36, 0x9f, 0xc1, 0x64, 0xa0, 0x11, 0x4b, 0xc9, 0x32, 0x31, 0x3a, 0xd6, 0x87, 0x26, 0x1a, 0x3a, 0x78, 0x3d, 0x89, 0xdb, 0x00, 0x28, 0x3b, 0xae, 0x2b, 0x1b, 0x56, 0xe2, 0x8c, 0x4c, 0x63, 0xac, 0x6e, 0x6c, 0xf7, 0xb5, 0x7d, 0x4d, 0x0b, 0x9f, 0x06, 0xa0, 0x10, 0x35, 0x38, 0x20, 0x4d, 0xcc, 0x07, 0xd7, 0x00, 0x4e, 0x86, 0xba, 0xfe, 0x8b, 0xe4, 0x3f, 0x4a, 0xd6, 0xca, 0xbf, 0x67, 0x40, 0x1a, 0xa4, 0xda, 0x82, 0x52, 0x15, 0xb8, 0x14, 0x3a, 0x7c, 0xa9, 0x02, 0xc1, 0x01, 0x69, 0xc6, 0x51, 0xd4, 0xbc, 0x1f, 0x95, 0xb2, 0xee, 0x1f, 0xdd, 0xb5, 0x73, 0x16, 0x5e, 0x29, 0x3f, 0x47, 0xac, 0x65, 0xfb, 0x63, 0x5c, 0xb9, 0xc8, 0x13, 0x2d, 0xec, 0x85, 0xde, 0x71, 0x0d, 0x84, 0x93, 0x74, 0x76, 0x91, 0xdd, 0x1d, 0x6d, 0x3d, 0xc7, 0x36, 0x19, 0x19, 0x86, 0xde, 0x7c, 0xca, 0xd6, 0xc6, 0x65, 0x7e, 0x4b, 0x24, 0x9c, 0xce, 0x92, 0x6b, 0x1c, 0xe0, 0xa0, 0xa9, 0x6c, 0xc3, 0xed, 0x4f, 0x2a, 0x54, 0x07, 0x00, 0x32, 0x5e, 0x1b, 0x94, 0x37, 0xcd, 0xe2, 0x32, 0xa8, 0xd5, 0x2c, 0xfb, 0x03, 0x9d, 0x79, 0xdf, 0x21, 0x43, 0x01, 0x00, 0x01 }; static const unsigned char authdata_tpm_es256[166] = { 0x58, 0xa4, 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, 0x08, 0x98, 0x70, 0x58, 0xca, 0xdc, 0x4b, 0x81, 0xb6, 0xe1, 0x30, 0xde, 0x50, 0xdc, 0xbe, 0x96, 0x00, 0x20, 0xa8, 0xdf, 0x03, 0xf7, 0xbf, 0x39, 0x51, 0x94, 0x95, 0x8f, 0xa4, 0x84, 0x97, 0x30, 0xbc, 0x3c, 0x7e, 0x1c, 0x99, 0x91, 0x4d, 0xae, 0x6d, 0xfb, 0xdf, 0x53, 0xb5, 0xb6, 0x1f, 0x3a, 0x4e, 0x6a, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xfb, 0xd6, 0xba, 0x74, 0xe6, 0x6e, 0x5c, 0x87, 0xef, 0x89, 0xa2, 0xe8, 0x3d, 0x0b, 0xe9, 0x69, 0x2c, 0x07, 0x07, 0x7a, 0x8a, 0x1e, 0xce, 0x12, 0xea, 0x3b, 0xb3, 0xf1, 0xf3, 0xd9, 0xc3, 0xe6, 0x22, 0x58, 0x20, 0x3c, 0x68, 0x51, 0x94, 0x54, 0x8d, 0xeb, 0x9f, 0xb2, 0x2c, 0x66, 0x75, 0xb6, 0xb7, 0x55, 0x22, 0x0d, 0x87, 0x59, 0xc4, 0x39, 0x91, 0x62, 0x17, 0xc2, 0xc3, 0x53, 0xa5, 0x26, 0x97, 0x4f, 0x2d }; 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 pubkey_tpm_rs256[259] = { 0xc5, 0xb6, 0x9c, 0x06, 0x1d, 0xcf, 0xb9, 0xf2, 0x5e, 0x99, 0x7d, 0x6d, 0x73, 0xd8, 0x36, 0xc1, 0x4a, 0x90, 0x05, 0x4d, 0x82, 0x57, 0xc1, 0xb6, 0x6a, 0xd1, 0x43, 0x03, 0x85, 0xf8, 0x52, 0x4f, 0xd2, 0x27, 0x91, 0x0b, 0xb5, 0x93, 0xa0, 0x68, 0xf8, 0x80, 0x1b, 0xaa, 0x65, 0x97, 0x45, 0x11, 0x86, 0x34, 0xd6, 0x67, 0xf8, 0xd5, 0x12, 0x79, 0x84, 0xee, 0x70, 0x99, 0x00, 0x63, 0xa8, 0xb4, 0x43, 0x0b, 0x4c, 0x57, 0x4a, 0xd6, 0x9b, 0x75, 0x63, 0x8a, 0x46, 0x57, 0xdb, 0x14, 0xc8, 0x71, 0xd1, 0xb3, 0x07, 0x68, 0x58, 0xbc, 0x55, 0x84, 0x80, 0x2a, 0xd2, 0x36, 0x9f, 0xc1, 0x64, 0xa0, 0x11, 0x4b, 0xc9, 0x32, 0x31, 0x3a, 0xd6, 0x87, 0x26, 0x1a, 0x3a, 0x78, 0x3d, 0x89, 0xdb, 0x00, 0x28, 0x3b, 0xae, 0x2b, 0x1b, 0x56, 0xe2, 0x8c, 0x4c, 0x63, 0xac, 0x6e, 0x6c, 0xf7, 0xb5, 0x7d, 0x4d, 0x0b, 0x9f, 0x06, 0xa0, 0x10, 0x35, 0x38, 0x20, 0x4d, 0xcc, 0x07, 0xd7, 0x00, 0x4e, 0x86, 0xba, 0xfe, 0x8b, 0xe4, 0x3f, 0x4a, 0xd6, 0xca, 0xbf, 0x67, 0x40, 0x1a, 0xa4, 0xda, 0x82, 0x52, 0x15, 0xb8, 0x14, 0x3a, 0x7c, 0xa9, 0x02, 0xc1, 0x01, 0x69, 0xc6, 0x51, 0xd4, 0xbc, 0x1f, 0x95, 0xb2, 0xee, 0x1f, 0xdd, 0xb5, 0x73, 0x16, 0x5e, 0x29, 0x3f, 0x47, 0xac, 0x65, 0xfb, 0x63, 0x5c, 0xb9, 0xc8, 0x13, 0x2d, 0xec, 0x85, 0xde, 0x71, 0x0d, 0x84, 0x93, 0x74, 0x76, 0x91, 0xdd, 0x1d, 0x6d, 0x3d, 0xc7, 0x36, 0x19, 0x19, 0x86, 0xde, 0x7c, 0xca, 0xd6, 0xc6, 0x65, 0x7e, 0x4b, 0x24, 0x9c, 0xce, 0x92, 0x6b, 0x1c, 0xe0, 0xa0, 0xa9, 0x6c, 0xc3, 0xed, 0x4f, 0x2a, 0x54, 0x07, 0x00, 0x32, 0x5e, 0x1b, 0x94, 0x37, 0xcd, 0xe2, 0x32, 0xa8, 0xd5, 0x2c, 0xfb, 0x03, 0x9d, 0x79, 0xdf, 0x01, 0x00, 0x01, }; const unsigned char pubkey_tpm_es256[64] = { 0xfb, 0xd6, 0xba, 0x74, 0xe6, 0x6e, 0x5c, 0x87, 0xef, 0x89, 0xa2, 0xe8, 0x3d, 0x0b, 0xe9, 0x69, 0x2c, 0x07, 0x07, 0x7a, 0x8a, 0x1e, 0xce, 0x12, 0xea, 0x3b, 0xb3, 0xf1, 0xf3, 0xd9, 0xc3, 0xe6, 0x3c, 0x68, 0x51, 0x94, 0x54, 0x8d, 0xeb, 0x9f, 0xb2, 0x2c, 0x66, 0x75, 0xb6, 0xb7, 0x55, 0x22, 0x0d, 0x87, 0x59, 0xc4, 0x39, 0x91, 0x62, 0x17, 0xc2, 0xc3, 0x53, 0xa5, 0x26, 0x97, 0x4f, 0x2d }; 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 unsigned char id_tpm_rs256[32] = { 0x89, 0x99, 0x6d, 0x5a, 0x00, 0x29, 0xe5, 0x3e, 0x6a, 0x1c, 0x72, 0x6d, 0x71, 0x4a, 0x4f, 0x03, 0x9b, 0x68, 0x17, 0xdb, 0x29, 0x1a, 0x6b, 0x02, 0x6c, 0x26, 0xf9, 0xbd, 0xc3, 0x0e, 0x38, 0x1a }; const unsigned char id_tpm_es256[32] = { 0xa8, 0xdf, 0x03, 0xf7, 0xbf, 0x39, 0x51, 0x94, 0x95, 0x8f, 0xa4, 0x84, 0x97, 0x30, 0xbc, 0x3c, 0x7e, 0x1c, 0x99, 0x91, 0x4d, 0xae, 0x6d, 0xfb, 0xdf, 0x53, 0xb5, 0xb6, 0x1f, 0x3a, 0x4e, 0x6a }; const unsigned char attstmt_tpm_rs256[4034] = { 0xa6, 0x63, 0x61, 0x6c, 0x67, 0x39, 0xff, 0xfe, 0x63, 0x73, 0x69, 0x67, 0x59, 0x01, 0x00, 0x1c, 0x09, 0x0d, 0x35, 0x97, 0x22, 0xfc, 0xfe, 0xc0, 0x58, 0x49, 0x9e, 0xd4, 0x7e, 0x6a, 0x7d, 0xdb, 0x6d, 0x20, 0x95, 0x5c, 0x0b, 0xd0, 0xd5, 0x72, 0x4f, 0x15, 0x22, 0x38, 0x97, 0xb2, 0x4b, 0xd0, 0xef, 0x31, 0x7c, 0xf2, 0x42, 0x19, 0x41, 0xa1, 0xe2, 0xc5, 0xca, 0xc6, 0x74, 0x95, 0xcf, 0xf9, 0x41, 0x75, 0x0b, 0x56, 0x39, 0x82, 0x78, 0xf6, 0x59, 0xf1, 0x09, 0x96, 0x9e, 0x38, 0x7f, 0x14, 0x9b, 0xf5, 0x36, 0xbb, 0x92, 0x32, 0xc4, 0x64, 0xe8, 0xff, 0xb4, 0xc7, 0xcf, 0xcd, 0x17, 0x48, 0x0f, 0x83, 0xd9, 0x44, 0x03, 0x35, 0x26, 0xad, 0x01, 0xb7, 0x57, 0x06, 0xb3, 0x9c, 0xa0, 0x6e, 0x2f, 0x58, 0xcb, 0x5c, 0xaa, 0x7c, 0xea, 0x7e, 0x3f, 0xbc, 0x76, 0xc9, 0x0e, 0x52, 0x39, 0x81, 0xa9, 0x9e, 0x37, 0x14, 0x1f, 0x50, 0x6a, 0x4f, 0xd7, 0xfc, 0xd4, 0xfa, 0xf2, 0x18, 0x60, 0xd5, 0xc3, 0x57, 0x7d, 0x6d, 0x05, 0x28, 0x25, 0xc3, 0xde, 0x86, 0x85, 0x06, 0x71, 0xfb, 0x84, 0xa2, 0x07, 0xb6, 0x77, 0xc9, 0x68, 0x41, 0x53, 0x32, 0x4c, 0xa8, 0x4b, 0xf7, 0x08, 0x84, 0x62, 0x6c, 0x8a, 0xb6, 0xcf, 0xc1, 0xde, 0x6b, 0x61, 0xc8, 0xdd, 0xc0, 0x13, 0x70, 0x22, 0x28, 0xe1, 0x0f, 0x46, 0x02, 0xc6, 0xb1, 0xfa, 0x30, 0xcb, 0xec, 0xd1, 0x82, 0xfa, 0x51, 0xcb, 0x71, 0x5e, 0x1f, 0x1b, 0x5f, 0xe0, 0xb0, 0x02, 0x8a, 0x7c, 0x78, 0xd1, 0xb7, 0x4d, 0x56, 0xb0, 0x92, 0x3e, 0xda, 0xc7, 0xb1, 0x74, 0xcf, 0x6a, 0x40, 0xeb, 0x98, 0x1c, 0x2e, 0xf2, 0x86, 0x76, 0xf8, 0x2e, 0x6a, 0x9f, 0x77, 0x51, 0x64, 0xce, 0xdc, 0x12, 0x85, 0x84, 0x6b, 0x01, 0xc8, 0xeb, 0xbc, 0x57, 0x6c, 0x32, 0x26, 0xcb, 0xb2, 0x84, 0x02, 0x2a, 0x33, 0x15, 0xd9, 0xe3, 0x15, 0xfc, 0x3a, 0x24, 0x63, 0x76, 0x65, 0x72, 0x63, 0x32, 0x2e, 0x30, 0x63, 0x78, 0x35, 0x63, 0x82, 0x59, 0x05, 0xc4, 0x30, 0x82, 0x05, 0xc0, 0x30, 0x82, 0x03, 0xa8, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x78, 0xd9, 0xa8, 0xb2, 0x64, 0xf9, 0x4d, 0x28, 0x82, 0xc0, 0xd3, 0x1b, 0x40, 0x3c, 0xc8, 0xd9, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x41, 0x31, 0x3f, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x36, 0x45, 0x55, 0x53, 0x2d, 0x53, 0x54, 0x4d, 0x2d, 0x4b, 0x45, 0x59, 0x49, 0x44, 0x2d, 0x31, 0x41, 0x44, 0x42, 0x39, 0x39, 0x34, 0x41, 0x42, 0x35, 0x38, 0x42, 0x45, 0x35, 0x37, 0x41, 0x30, 0x43, 0x43, 0x39, 0x42, 0x39, 0x30, 0x30, 0x45, 0x37, 0x38, 0x35, 0x31, 0x45, 0x31, 0x41, 0x34, 0x33, 0x43, 0x30, 0x38, 0x36, 0x36, 0x30, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x37, 0x31, 0x35, 0x31, 0x31, 0x31, 0x32, 0x31, 0x33, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x33, 0x32, 0x31, 0x32, 0x30, 0x32, 0x39, 0x31, 0x35, 0x5a, 0x30, 0x00, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0xbe, 0x77, 0x9f, 0x45, 0x97, 0x17, 0x8d, 0x01, 0xe1, 0x18, 0xcc, 0xf0, 0xb5, 0xed, 0x9a, 0xb7, 0x36, 0xac, 0x05, 0x26, 0xbe, 0x35, 0xd9, 0x5c, 0x00, 0x5c, 0x5d, 0x8b, 0x6f, 0x2a, 0xb8, 0xf6, 0x02, 0x4f, 0x33, 0xfe, 0x84, 0x45, 0x4c, 0x4f, 0x7a, 0xdb, 0xa9, 0x6a, 0x62, 0x0f, 0x19, 0x35, 0x5d, 0xd2, 0x34, 0x1a, 0x9d, 0x73, 0x55, 0xe5, 0x3e, 0x04, 0xa2, 0xd6, 0xbe, 0xe7, 0x5a, 0xb9, 0x16, 0x6c, 0x55, 0x18, 0xa8, 0x4b, 0xb2, 0x37, 0xb9, 0xa3, 0x87, 0xfc, 0x76, 0xa8, 0x55, 0xc9, 0xe7, 0x30, 0xe5, 0x0e, 0x3c, 0x7b, 0x74, 0xd2, 0x1e, 0xa8, 0x05, 0xd5, 0xe2, 0xe3, 0xcb, 0xaf, 0x63, 0x33, 0x12, 0xaa, 0xfd, 0x31, 0x32, 0x71, 0x4f, 0x41, 0x96, 0x05, 0xb5, 0x69, 0x73, 0x45, 0xbe, 0x6f, 0x90, 0xd9, 0x10, 0x36, 0xaf, 0x7a, 0x1c, 0xf1, 0x6d, 0x14, 0xb0, 0x1e, 0xbb, 0xae, 0x1c, 0x35, 0xec, 0x1c, 0xb5, 0x0e, 0xf6, 0x33, 0x98, 0x13, 0x4e, 0x44, 0x7b, 0x5c, 0x97, 0x47, 0xed, 0x4f, 0xfe, 0xbd, 0x08, 0xd2, 0xa9, 0xc6, 0xbe, 0x8c, 0x04, 0x9e, 0xdc, 0x3d, 0xbe, 0x98, 0xe9, 0x2a, 0xb1, 0xf4, 0xfa, 0x45, 0xf9, 0xc8, 0x9a, 0x55, 0x85, 0x26, 0xfc, 0x5f, 0xad, 0x00, 0x8b, 0xc8, 0x41, 0xf2, 0x86, 0x4e, 0xba, 0x55, 0x1c, 0xb2, 0x89, 0xe8, 0x85, 0x6e, 0x1e, 0x02, 0x9f, 0x55, 0x70, 0xbe, 0xfd, 0xe7, 0x9f, 0xba, 0x59, 0xa0, 0x2e, 0x9a, 0x74, 0x11, 0xe7, 0xad, 0xa9, 0xc7, 0x7b, 0x58, 0xc4, 0x16, 0xd3, 0x35, 0xcb, 0x61, 0x00, 0xec, 0x36, 0x4a, 0xa3, 0x51, 0xa3, 0xdd, 0x61, 0xb6, 0xd6, 0x29, 0xcb, 0x76, 0xe1, 0xab, 0x51, 0x3a, 0xe8, 0xbf, 0xdb, 0x09, 0x4a, 0x39, 0x96, 0xd9, 0xac, 0x8f, 0x6c, 0x62, 0xe0, 0x03, 0x23, 0x24, 0xbe, 0xd4, 0x83, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xf3, 0x30, 0x82, 0x01, 0xef, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x6d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x01, 0x01, 0xff, 0x04, 0x63, 0x30, 0x61, 0x30, 0x5f, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x1f, 0x30, 0x52, 0x30, 0x50, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x44, 0x1e, 0x42, 0x00, 0x54, 0x00, 0x43, 0x00, 0x50, 0x00, 0x41, 0x00, 0x20, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x75, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x20, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x74, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x20, 0x00, 0x49, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x30, 0x10, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x09, 0x30, 0x07, 0x06, 0x05, 0x67, 0x81, 0x05, 0x08, 0x03, 0x30, 0x59, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04, 0x4f, 0x30, 0x4d, 0xa4, 0x4b, 0x30, 0x49, 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x01, 0x0c, 0x0b, 0x69, 0x64, 0x3a, 0x35, 0x33, 0x35, 0x34, 0x34, 0x44, 0x32, 0x30, 0x31, 0x17, 0x30, 0x15, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x02, 0x0c, 0x0c, 0x53, 0x54, 0x33, 0x33, 0x48, 0x54, 0x50, 0x48, 0x41, 0x48, 0x42, 0x34, 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x03, 0x0c, 0x0b, 0x69, 0x64, 0x3a, 0x30, 0x30, 0x34, 0x39, 0x30, 0x30, 0x30, 0x34, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb8, 0x5f, 0xd5, 0x67, 0xca, 0x92, 0xc4, 0x0e, 0xcf, 0x0c, 0xd8, 0x1f, 0x6d, 0x3f, 0x03, 0x55, 0x6f, 0x38, 0xa6, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xd4, 0x04, 0x64, 0xfc, 0x6e, 0x50, 0x0a, 0x56, 0x48, 0x0f, 0x05, 0xa9, 0x00, 0xb7, 0x1d, 0x5e, 0x57, 0x08, 0xd5, 0xdc, 0x30, 0x81, 0xb2, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa5, 0x30, 0x81, 0xa2, 0x30, 0x81, 0x9f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x81, 0x92, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x7a, 0x63, 0x73, 0x70, 0x72, 0x6f, 0x64, 0x65, 0x75, 0x73, 0x61, 0x69, 0x6b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x2e, 0x62, 0x6c, 0x6f, 0x62, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x65, 0x75, 0x73, 0x2d, 0x73, 0x74, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x69, 0x64, 0x2d, 0x31, 0x61, 0x64, 0x62, 0x39, 0x39, 0x34, 0x61, 0x62, 0x35, 0x38, 0x62, 0x65, 0x35, 0x37, 0x61, 0x30, 0x63, 0x63, 0x39, 0x62, 0x39, 0x30, 0x30, 0x65, 0x37, 0x38, 0x35, 0x31, 0x65, 0x31, 0x61, 0x34, 0x33, 0x63, 0x30, 0x38, 0x36, 0x36, 0x30, 0x2f, 0x61, 0x62, 0x64, 0x36, 0x31, 0x35, 0x66, 0x32, 0x2d, 0x31, 0x35, 0x38, 0x61, 0x2d, 0x34, 0x35, 0x38, 0x65, 0x2d, 0x61, 0x31, 0x35, 0x35, 0x2d, 0x37, 0x63, 0x34, 0x63, 0x38, 0x63, 0x62, 0x31, 0x33, 0x63, 0x36, 0x35, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0xa2, 0x10, 0xc5, 0xbf, 0x41, 0xa6, 0xba, 0x8c, 0x72, 0xca, 0x0f, 0x3e, 0x5e, 0x7f, 0xe2, 0xcb, 0x60, 0xb8, 0x3f, 0xfb, 0xde, 0x03, 0xe2, 0xfe, 0x20, 0x29, 0xdf, 0x11, 0xf5, 0xb0, 0x50, 0x6d, 0x32, 0xe8, 0x1b, 0x05, 0xad, 0x6b, 0x60, 0xb5, 0xed, 0xf3, 0xa4, 0x4a, 0xea, 0x09, 0xe5, 0x65, 0x7e, 0xe0, 0xd5, 0x3a, 0x6a, 0xdb, 0x64, 0xb7, 0x07, 0x8f, 0xa1, 0x63, 0xb3, 0x89, 0x8a, 0xac, 0x49, 0x97, 0xa0, 0x9a, 0xa3, 0xd3, 0x3a, 0xc2, 0x13, 0xb2, 0xbb, 0xab, 0x0d, 0xf2, 0x35, 0xc5, 0x03, 0xde, 0x1c, 0xad, 0x6a, 0x03, 0x0a, 0x4c, 0xe1, 0x37, 0x8f, 0xbc, 0x13, 0xc0, 0x9a, 0x17, 0xd4, 0x2e, 0x36, 0x17, 0x51, 0x12, 0xb0, 0x79, 0xbf, 0x9b, 0xb3, 0xb0, 0x74, 0x25, 0x81, 0x7e, 0x21, 0x31, 0xb7, 0xc2, 0x5e, 0xfb, 0x36, 0xab, 0xf3, 0x7a, 0x5f, 0xa4, 0x5e, 0x8f, 0x0c, 0xbd, 0xcf, 0xf5, 0x50, 0xe7, 0x0c, 0x51, 0x55, 0x48, 0xe6, 0x15, 0xb6, 0xd4, 0xaf, 0x95, 0x72, 0x56, 0x94, 0xf7, 0x0e, 0xd6, 0x90, 0xe3, 0xd3, 0x5d, 0xbd, 0x93, 0xa1, 0xbd, 0x6c, 0xe4, 0xf2, 0x39, 0x4d, 0x54, 0x74, 0xcf, 0xf5, 0xeb, 0x70, 0xdb, 0x4f, 0x52, 0xcd, 0x39, 0x8f, 0x11, 0x54, 0x28, 0x06, 0x29, 0x8f, 0x23, 0xde, 0x9e, 0x2f, 0x7b, 0xb6, 0x5f, 0xa3, 0x89, 0x04, 0x99, 0x0a, 0xf1, 0x2d, 0xf9, 0x66, 0xd3, 0x13, 0x45, 0xbd, 0x6c, 0x22, 0x57, 0xf5, 0xb1, 0xb9, 0xdf, 0x5b, 0x7b, 0x1a, 0x3a, 0xdd, 0x6b, 0xc7, 0x35, 0x88, 0xed, 0xc4, 0x09, 0x70, 0x4e, 0x5f, 0xb5, 0x3e, 0xd1, 0x0b, 0xd0, 0xca, 0xef, 0x0b, 0xe9, 0x8b, 0x6f, 0xc3, 0x16, 0xc3, 0x3d, 0x79, 0x06, 0xef, 0x81, 0xf0, 0x60, 0x0b, 0x32, 0xe3, 0x86, 0x6b, 0x92, 0x38, 0x90, 0x62, 0xed, 0x84, 0x3a, 0xb7, 0x45, 0x43, 0x2e, 0xd0, 0x3a, 0x71, 0x9e, 0x80, 0xcc, 0x9c, 0xac, 0x27, 0x10, 0x91, 0xb7, 0xb2, 0xbd, 0x41, 0x40, 0xa7, 0xb7, 0xcf, 0xe7, 0x38, 0xca, 0x68, 0xdd, 0x62, 0x09, 0xff, 0x68, 0xce, 0xba, 0xe2, 0x07, 0x49, 0x09, 0xe7, 0x1f, 0xdf, 0xe6, 0x26, 0xe5, 0x0f, 0xa9, 0xbf, 0x2a, 0x5b, 0x67, 0x92, 0xa1, 0x10, 0x53, 0xb2, 0x7a, 0x07, 0x29, 0x9d, 0xfd, 0x6d, 0xb6, 0x3b, 0x45, 0xc1, 0x94, 0xcb, 0x1c, 0xc3, 0xce, 0xf6, 0x8a, 0x1a, 0x81, 0x66, 0xb0, 0xa5, 0x14, 0xc7, 0x9e, 0x1f, 0x6e, 0xb6, 0xff, 0x8b, 0x90, 0x87, 0x3a, 0x3f, 0xa8, 0xc2, 0x2d, 0x8f, 0x6f, 0xdb, 0xb4, 0xc4, 0x14, 0x3c, 0x1d, 0x12, 0x1d, 0x6d, 0xcf, 0xa6, 0x04, 0x6a, 0xa8, 0x13, 0x5e, 0xf2, 0x5e, 0x77, 0x80, 0x6b, 0x85, 0x83, 0xfe, 0xbb, 0xeb, 0x70, 0xcb, 0x5f, 0xe4, 0x95, 0xaa, 0x0f, 0x61, 0x36, 0x7c, 0xbb, 0x22, 0x1e, 0xba, 0x98, 0x43, 0x52, 0x33, 0xae, 0xed, 0x5d, 0x10, 0x2c, 0xb3, 0xa9, 0x31, 0x8e, 0x60, 0x54, 0xaf, 0x40, 0x6d, 0x2e, 0x18, 0xc2, 0x6a, 0xf4, 0x7b, 0x9a, 0x73, 0x0f, 0x58, 0x69, 0x23, 0xbb, 0xc4, 0x84, 0x53, 0x30, 0xe2, 0xd6, 0x1e, 0x10, 0xc1, 0xec, 0x82, 0x13, 0xab, 0x53, 0x86, 0xa2, 0xb9, 0xda, 0xbb, 0x3a, 0xa2, 0xbe, 0xb0, 0x10, 0x99, 0x0e, 0xe5, 0x9c, 0xc9, 0xf1, 0xce, 0x76, 0x46, 0xea, 0x86, 0xaa, 0x36, 0x83, 0x99, 0x09, 0x9b, 0x30, 0xd3, 0x26, 0xc7, 0xdf, 0x66, 0xc7, 0xf0, 0xdd, 0x08, 0x09, 0x15, 0x15, 0x21, 0x49, 0x46, 0xd8, 0x8a, 0x66, 0xca, 0x62, 0x9c, 0x79, 0x1d, 0x81, 0xea, 0x5d, 0x82, 0xb0, 0xa6, 0x6b, 0x5c, 0xf5, 0xb8, 0x8c, 0xf6, 0x16, 0x01, 0x2c, 0xf8, 0x27, 0xf8, 0xcf, 0x88, 0xfe, 0xf3, 0xa4, 0xfc, 0x17, 0x97, 0xe7, 0x07, 0x59, 0x06, 0xef, 0x30, 0x82, 0x06, 0xeb, 0x30, 0x82, 0x04, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x33, 0x00, 0x00, 0x02, 0x39, 0xf9, 0xbb, 0x6a, 0x1d, 0x49, 0x64, 0x47, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x32, 0x30, 0x31, 0x34, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x30, 0x33, 0x32, 0x31, 0x32, 0x30, 0x32, 0x39, 0x31, 0x35, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x33, 0x32, 0x31, 0x32, 0x30, 0x32, 0x39, 0x31, 0x35, 0x5a, 0x30, 0x41, 0x31, 0x3f, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x36, 0x45, 0x55, 0x53, 0x2d, 0x53, 0x54, 0x4d, 0x2d, 0x4b, 0x45, 0x59, 0x49, 0x44, 0x2d, 0x31, 0x41, 0x44, 0x42, 0x39, 0x39, 0x34, 0x41, 0x42, 0x35, 0x38, 0x42, 0x45, 0x35, 0x37, 0x41, 0x30, 0x43, 0x43, 0x39, 0x42, 0x39, 0x30, 0x30, 0x45, 0x37, 0x38, 0x35, 0x31, 0x45, 0x31, 0x41, 0x34, 0x33, 0x43, 0x30, 0x38, 0x36, 0x36, 0x30, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xdb, 0xe2, 0x23, 0xf9, 0x86, 0x8f, 0xa9, 0x71, 0x9f, 0x8b, 0xf9, 0x7c, 0xe9, 0x45, 0x2d, 0x59, 0x56, 0x5e, 0x96, 0xf4, 0xdd, 0x9a, 0x12, 0xcd, 0x90, 0x1a, 0x0c, 0xb5, 0x03, 0xbf, 0x09, 0xbe, 0xbf, 0xf7, 0x55, 0x52, 0xe8, 0x39, 0x4c, 0xbe, 0x2a, 0x28, 0x88, 0x78, 0x39, 0xa7, 0xcb, 0xf9, 0x4c, 0x55, 0xd2, 0x31, 0x96, 0x3b, 0x48, 0xa2, 0xf3, 0xf6, 0xd3, 0x1a, 0x81, 0x7f, 0x90, 0x62, 0xab, 0xec, 0x5a, 0xc7, 0xa0, 0x7f, 0x81, 0x32, 0x27, 0x9b, 0x29, 0x75, 0x7d, 0x1e, 0x96, 0xc5, 0xfa, 0x0e, 0x7c, 0xe0, 0x60, 0x96, 0x7a, 0xca, 0x94, 0xba, 0xe6, 0xb2, 0x69, 0xdd, 0xc4, 0x7d, 0xbb, 0xd3, 0xc4, 0xb4, 0x6e, 0x00, 0x86, 0x1f, 0x9d, 0x25, 0xe8, 0xae, 0xc7, 0x10, 0x84, 0xdc, 0xc0, 0x34, 0x24, 0x6e, 0xf7, 0xfc, 0xdd, 0x3d, 0x32, 0x7a, 0x43, 0x96, 0xd6, 0xc8, 0x7b, 0xf4, 0x9b, 0x3d, 0xa7, 0x1e, 0xba, 0x4d, 0xd0, 0x3b, 0x3d, 0x84, 0x9a, 0xd1, 0x25, 0x22, 0x5d, 0x00, 0x44, 0xb0, 0x59, 0xb7, 0x40, 0xc5, 0xa3, 0x53, 0x53, 0xaf, 0x8f, 0x9e, 0xfd, 0x8f, 0x1e, 0x02, 0xd3, 0x4f, 0xf7, 0x09, 0xce, 0xc5, 0xc6, 0x71, 0x5c, 0xe9, 0xe8, 0x7a, 0xb5, 0x6b, 0xa4, 0xbf, 0x0b, 0xd9, 0xb6, 0xfa, 0x24, 0xb0, 0xcd, 0x52, 0x22, 0x1d, 0x7e, 0xe8, 0x15, 0x2f, 0x1e, 0x5e, 0xa2, 0xec, 0xd3, 0xa8, 0x02, 0x77, 0xb9, 0x55, 0x9a, 0xcf, 0xcc, 0xd7, 0x08, 0x20, 0xa5, 0xda, 0x39, 0x9a, 0x30, 0x76, 0x90, 0x37, 0xa7, 0x60, 0xdf, 0x18, 0x12, 0x65, 0x17, 0xaa, 0xdd, 0x48, 0xd5, 0x12, 0x1d, 0x4c, 0x83, 0x5d, 0x81, 0x07, 0x1d, 0x18, 0x81, 0x40, 0x55, 0x60, 0x8f, 0xa3, 0x6b, 0x34, 0x1e, 0xd5, 0xe6, 0xcf, 0x52, 0x73, 0x77, 0x4a, 0x50, 0x4f, 0x1b, 0x0f, 0x39, 0xc3, 0x0d, 0x16, 0xf9, 0xbb, 0x4c, 0x77, 0xf6, 0x4e, 0xac, 0x9c, 0xfe, 0xe8, 0xbb, 0x52, 0xa5, 0x0a, 0x0e, 0x9b, 0xf0, 0x0d, 0xef, 0xfb, 0x6f, 0x89, 0x34, 0x7d, 0x47, 0xec, 0x14, 0x6a, 0xf4, 0x0a, 0xe1, 0x60, 0x44, 0x73, 0x7b, 0xa0, 0xab, 0x5b, 0x8c, 0x43, 0xa6, 0x05, 0x42, 0x61, 0x46, 0xaa, 0x1c, 0xf5, 0xec, 0x2c, 0x86, 0x85, 0x21, 0x99, 0xdf, 0x45, 0x8e, 0xf4, 0xd1, 0x1e, 0xfb, 0xcd, 0x9b, 0x94, 0x32, 0xe0, 0xa0, 0xcc, 0x4f, 0xad, 0xae, 0x44, 0x8b, 0x86, 0x27, 0x91, 0xfe, 0x60, 0x9f, 0xf2, 0x63, 0x30, 0x6c, 0x5d, 0x8d, 0xbc, 0xab, 0xd4, 0xf5, 0xa2, 0xb2, 0x74, 0xe8, 0xd4, 0x95, 0xf2, 0xd6, 0x03, 0x8b, 0xc9, 0xa3, 0x52, 0xe7, 0x63, 0x05, 0x64, 0x50, 0xe5, 0x0a, 0x6a, 0xa0, 0x6c, 0x50, 0xcd, 0x37, 0x98, 0xa8, 0x87, 0x02, 0x38, 0x5b, 0x6c, 0x02, 0x69, 0x3d, 0x1f, 0x95, 0x74, 0x4d, 0x46, 0x76, 0x2a, 0x9d, 0x62, 0xd4, 0xc7, 0x1b, 0xf9, 0x31, 0xa6, 0x51, 0xee, 0x7b, 0xc8, 0xe4, 0x6e, 0x3a, 0xcf, 0x4f, 0x4f, 0x49, 0x8a, 0xf5, 0x4f, 0x25, 0x93, 0x23, 0x02, 0xef, 0x79, 0xa6, 0x27, 0xbe, 0x5a, 0xe7, 0x74, 0xb7, 0xd7, 0xa8, 0xc1, 0xae, 0x55, 0x88, 0xa4, 0xc7, 0x4d, 0xb7, 0x62, 0xf0, 0xf9, 0x5b, 0xbf, 0x47, 0x5b, 0xfe, 0xcc, 0x0b, 0x89, 0x19, 0x65, 0x4b, 0x6f, 0xdf, 0x4f, 0x7d, 0x4d, 0x96, 0x42, 0x0d, 0x2a, 0xa1, 0xbd, 0x3e, 0x70, 0x92, 0xba, 0xc8, 0x59, 0xd5, 0x1d, 0x3a, 0x98, 0x53, 0x75, 0xa6, 0x32, 0xc8, 0x72, 0x03, 0x46, 0x5f, 0x5c, 0x13, 0xa4, 0xdb, 0xc7, 0x55, 0x35, 0x22, 0x0d, 0xc6, 0x17, 0x85, 0xbd, 0x46, 0x4b, 0xfa, 0x1e, 0x49, 0xc2, 0xfe, 0x1e, 0xf9, 0x62, 0x89, 0x56, 0x84, 0xdf, 0xa0, 0xfb, 0xfd, 0x93, 0xa4, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x8e, 0x30, 0x82, 0x01, 0x8a, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x84, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x14, 0x30, 0x12, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x24, 0x06, 0x05, 0x67, 0x81, 0x05, 0x08, 0x03, 0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0f, 0x30, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x1f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb8, 0x5f, 0xd5, 0x67, 0xca, 0x92, 0xc4, 0x0e, 0xcf, 0x0c, 0xd8, 0x1f, 0x6d, 0x3f, 0x03, 0x55, 0x6f, 0x38, 0xa6, 0x51, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7a, 0x8c, 0x0a, 0xce, 0x2f, 0x48, 0x62, 0x17, 0xe2, 0x94, 0xd1, 0xae, 0x55, 0xc1, 0x52, 0xec, 0x71, 0x74, 0xa4, 0x56, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x69, 0x30, 0x67, 0x30, 0x65, 0xa0, 0x63, 0xa0, 0x61, 0x86, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x25, 0x32, 0x30, 0x54, 0x50, 0x4d, 0x25, 0x32, 0x30, 0x52, 0x6f, 0x6f, 0x74, 0x25, 0x32, 0x30, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x25, 0x32, 0x30, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x25, 0x32, 0x30, 0x32, 0x30, 0x31, 0x34, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x7d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x71, 0x30, 0x6f, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x61, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x25, 0x32, 0x30, 0x54, 0x50, 0x4d, 0x25, 0x32, 0x30, 0x52, 0x6f, 0x6f, 0x74, 0x25, 0x32, 0x30, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x25, 0x32, 0x30, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x25, 0x32, 0x30, 0x32, 0x30, 0x31, 0x34, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x41, 0xaa, 0xfe, 0x28, 0x6c, 0xf7, 0x6b, 0x53, 0xde, 0x77, 0xc0, 0x80, 0x50, 0x94, 0xd9, 0xdb, 0x46, 0x8e, 0x6a, 0x93, 0xa9, 0x10, 0x37, 0x27, 0x1f, 0xf5, 0x70, 0xf1, 0xa8, 0xcf, 0xa1, 0x45, 0x86, 0x2a, 0xdd, 0x8f, 0xb8, 0xb5, 0xc1, 0xe6, 0xcf, 0x8a, 0xfa, 0x32, 0xa1, 0x4b, 0xb7, 0xa4, 0xbf, 0x0a, 0x48, 0xcb, 0x42, 0x63, 0x71, 0xc1, 0x96, 0xb9, 0x3a, 0x37, 0x84, 0x0e, 0x24, 0x39, 0xeb, 0x58, 0xce, 0x3d, 0xb7, 0xa9, 0x44, 0x92, 0x59, 0xb9, 0xff, 0xdb, 0x18, 0xbe, 0x6a, 0x5e, 0xe7, 0xce, 0xef, 0xb8, 0x40, 0x53, 0xaf, 0xc1, 0x9b, 0xfb, 0x42, 0x99, 0x7e, 0x9d, 0x05, 0x2b, 0x71, 0x0a, 0x7a, 0x7a, 0x44, 0xd1, 0x31, 0xca, 0xf0, 0x5f, 0x74, 0x85, 0xa9, 0xe2, 0xbc, 0xc8, 0x0c, 0xad, 0x57, 0xd1, 0xe9, 0x48, 0x90, 0x88, 0x57, 0x86, 0xd7, 0xc5, 0xc9, 0xe6, 0xb2, 0x5e, 0x5f, 0x13, 0xdc, 0x10, 0x7f, 0xdf, 0x63, 0x8a, 0xd5, 0x9e, 0x90, 0xc2, 0x75, 0x53, 0x1e, 0x68, 0x17, 0x2b, 0x03, 0x29, 0x15, 0x03, 0xc5, 0x8c, 0x66, 0x3e, 0xae, 0xbd, 0x4a, 0x32, 0x7e, 0x59, 0x89, 0x0b, 0x84, 0xc2, 0xd9, 0x90, 0xfa, 0x02, 0x22, 0x90, 0x8d, 0x9c, 0xb6, 0x0c, 0x4d, 0xe1, 0x28, 0x76, 0xd7, 0x82, 0xc3, 0x36, 0xc2, 0xa3, 0x2a, 0x52, 0xe5, 0xfe, 0x3c, 0x8f, 0xe3, 0x4b, 0xda, 0x6a, 0xdb, 0xc0, 0x7a, 0x3c, 0x57, 0xfa, 0x85, 0x8f, 0xfb, 0x62, 0xc3, 0xa1, 0x38, 0xce, 0x84, 0xf2, 0xba, 0x12, 0xf4, 0x30, 0x2a, 0x4a, 0x94, 0xa9, 0x35, 0x2c, 0x7d, 0x11, 0xc7, 0x68, 0x1f, 0x47, 0xaa, 0x57, 0x43, 0x06, 0x70, 0x79, 0x8c, 0xb6, 0x3b, 0x5d, 0x57, 0xf3, 0xf3, 0xc0, 0x2c, 0xc5, 0xde, 0x41, 0x99, 0xf6, 0xdd, 0x55, 0x8a, 0xe4, 0x13, 0xca, 0xc9, 0xec, 0x69, 0x93, 0x13, 0x48, 0xf0, 0x5f, 0xda, 0x2e, 0xfd, 0xfb, 0xa9, 0x1b, 0x92, 0xde, 0x49, 0x71, 0x37, 0x8c, 0x3f, 0xc2, 0x08, 0x0a, 0x83, 0x25, 0xf1, 0x6e, 0x0a, 0xe3, 0x55, 0x85, 0x96, 0x9a, 0x2d, 0xa2, 0xc0, 0xa1, 0xee, 0xfe, 0x23, 0x3b, 0x69, 0x22, 0x03, 0xfd, 0xcc, 0x8a, 0xdd, 0xb4, 0x53, 0x8d, 0x84, 0xa6, 0xac, 0xe0, 0x1e, 0x07, 0xe5, 0xd7, 0xf9, 0xcb, 0xb9, 0xe3, 0x9a, 0xb7, 0x84, 0x70, 0xa1, 0x93, 0xd6, 0x02, 0x1e, 0xfe, 0xdb, 0x28, 0x7c, 0xf7, 0xd4, 0x62, 0x6f, 0x80, 0x75, 0xc8, 0xd8, 0x35, 0x26, 0x0c, 0xcb, 0x84, 0xed, 0xbb, 0x95, 0xdf, 0x7f, 0xd5, 0xbb, 0x00, 0x96, 0x97, 0x32, 0xe7, 0xba, 0xe8, 0x29, 0xb5, 0x1a, 0x51, 0x81, 0xbb, 0x04, 0xd1, 0x21, 0x76, 0x34, 0x6d, 0x1e, 0x93, 0x96, 0x1f, 0x96, 0x53, 0x5f, 0x5c, 0x9e, 0xf3, 0x9d, 0x82, 0x1c, 0x39, 0x36, 0x59, 0xae, 0xc9, 0x3c, 0x53, 0x4a, 0x67, 0x65, 0x6e, 0xbf, 0xa6, 0xac, 0x3e, 0xda, 0xb2, 0xa7, 0x63, 0x07, 0x17, 0xe1, 0x5b, 0xda, 0x6a, 0x31, 0x9f, 0xfb, 0xb4, 0xea, 0xa1, 0x97, 0x08, 0x6e, 0xb2, 0x68, 0xf3, 0x72, 0x76, 0x99, 0xe8, 0x00, 0x46, 0x88, 0x26, 0xe1, 0x3c, 0x07, 0x2b, 0x78, 0x49, 0xda, 0x79, 0x3a, 0xbd, 0x6f, 0xca, 0x5c, 0xa0, 0xa8, 0xed, 0x34, 0xcc, 0xdb, 0x13, 0xe2, 0x51, 0x9b, 0x3d, 0x03, 0xac, 0xc7, 0xf6, 0x32, 0xe1, 0x11, 0x5d, 0xe1, 0xc5, 0xfd, 0x9e, 0x7a, 0xcd, 0x06, 0xb9, 0xe6, 0xfc, 0xe0, 0x03, 0x31, 0xf4, 0x4a, 0xa9, 0x3b, 0x79, 0x01, 0xb0, 0x64, 0x68, 0x9f, 0x6e, 0x76, 0xa1, 0xcc, 0xec, 0x17, 0x41, 0x9d, 0xd4, 0x5b, 0x4e, 0x9d, 0xe5, 0x46, 0xd4, 0x6b, 0x60, 0x2a, 0x23, 0xb5, 0x7a, 0x89, 0x7c, 0x27, 0x96, 0x65, 0x97, 0x56, 0xec, 0x98, 0xe3, 0x67, 0x70, 0x75, 0x62, 0x41, 0x72, 0x65, 0x61, 0x59, 0x01, 0x36, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x06, 0x04, 0x72, 0x00, 0x20, 0x9d, 0xff, 0xcb, 0xf3, 0x6c, 0x38, 0x3a, 0xe6, 0x99, 0xfb, 0x98, 0x68, 0xdc, 0x6d, 0xcb, 0x89, 0xd7, 0x15, 0x38, 0x84, 0xbe, 0x28, 0x03, 0x92, 0x2c, 0x12, 0x41, 0x58, 0xbf, 0xad, 0x22, 0xae, 0x00, 0x10, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc5, 0xb6, 0x9c, 0x06, 0x1d, 0xcf, 0xb9, 0xf2, 0x5e, 0x99, 0x7d, 0x6d, 0x73, 0xd8, 0x36, 0xc1, 0x4a, 0x90, 0x05, 0x4d, 0x82, 0x57, 0xc1, 0xb6, 0x6a, 0xd1, 0x43, 0x03, 0x85, 0xf8, 0x52, 0x4f, 0xd2, 0x27, 0x91, 0x0b, 0xb5, 0x93, 0xa0, 0x68, 0xf8, 0x80, 0x1b, 0xaa, 0x65, 0x97, 0x45, 0x11, 0x86, 0x34, 0xd6, 0x67, 0xf8, 0xd5, 0x12, 0x79, 0x84, 0xee, 0x70, 0x99, 0x00, 0x63, 0xa8, 0xb4, 0x43, 0x0b, 0x4c, 0x57, 0x4a, 0xd6, 0x9b, 0x75, 0x63, 0x8a, 0x46, 0x57, 0xdb, 0x14, 0xc8, 0x71, 0xd1, 0xb3, 0x07, 0x68, 0x58, 0xbc, 0x55, 0x84, 0x80, 0x2a, 0xd2, 0x36, 0x9f, 0xc1, 0x64, 0xa0, 0x11, 0x4b, 0xc9, 0x32, 0x31, 0x3a, 0xd6, 0x87, 0x26, 0x1a, 0x3a, 0x78, 0x3d, 0x89, 0xdb, 0x00, 0x28, 0x3b, 0xae, 0x2b, 0x1b, 0x56, 0xe2, 0x8c, 0x4c, 0x63, 0xac, 0x6e, 0x6c, 0xf7, 0xb5, 0x7d, 0x4d, 0x0b, 0x9f, 0x06, 0xa0, 0x10, 0x35, 0x38, 0x20, 0x4d, 0xcc, 0x07, 0xd7, 0x00, 0x4e, 0x86, 0xba, 0xfe, 0x8b, 0xe4, 0x3f, 0x4a, 0xd6, 0xca, 0xbf, 0x67, 0x40, 0x1a, 0xa4, 0xda, 0x82, 0x52, 0x15, 0xb8, 0x14, 0x3a, 0x7c, 0xa9, 0x02, 0xc1, 0x01, 0x69, 0xc6, 0x51, 0xd4, 0xbc, 0x1f, 0x95, 0xb2, 0xee, 0x1f, 0xdd, 0xb5, 0x73, 0x16, 0x5e, 0x29, 0x3f, 0x47, 0xac, 0x65, 0xfb, 0x63, 0x5c, 0xb9, 0xc8, 0x13, 0x2d, 0xec, 0x85, 0xde, 0x71, 0x0d, 0x84, 0x93, 0x74, 0x76, 0x91, 0xdd, 0x1d, 0x6d, 0x3d, 0xc7, 0x36, 0x19, 0x19, 0x86, 0xde, 0x7c, 0xca, 0xd6, 0xc6, 0x65, 0x7e, 0x4b, 0x24, 0x9c, 0xce, 0x92, 0x6b, 0x1c, 0xe0, 0xa0, 0xa9, 0x6c, 0xc3, 0xed, 0x4f, 0x2a, 0x54, 0x07, 0x00, 0x32, 0x5e, 0x1b, 0x94, 0x37, 0xcd, 0xe2, 0x32, 0xa8, 0xd5, 0x2c, 0xfb, 0x03, 0x9d, 0x79, 0xdf, 0x68, 0x63, 0x65, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x58, 0xa1, 0xff, 0x54, 0x43, 0x47, 0x80, 0x17, 0x00, 0x22, 0x00, 0x0b, 0xdb, 0x1f, 0x74, 0x21, 0x4f, 0xa9, 0x0d, 0x90, 0x64, 0xa2, 0x33, 0xbe, 0x3f, 0xf1, 0x95, 0xb0, 0x4e, 0x3f, 0x02, 0xdc, 0xad, 0xb0, 0x05, 0x13, 0xe6, 0x32, 0x5f, 0xed, 0x90, 0x2c, 0xad, 0xc0, 0x00, 0x14, 0x58, 0x52, 0x07, 0x5d, 0x64, 0x6c, 0x1f, 0xd1, 0x13, 0x7f, 0xc3, 0x74, 0xf6, 0x4b, 0xe3, 0xa0, 0x2e, 0xb7, 0x71, 0xda, 0x00, 0x00, 0x00, 0x00, 0x29, 0x3c, 0x64, 0xdf, 0x95, 0x38, 0xba, 0x73, 0xe3, 0x57, 0x61, 0xa0, 0x01, 0x24, 0x01, 0x08, 0xc9, 0xd6, 0xea, 0x60, 0xe4, 0x00, 0x22, 0x00, 0x0b, 0xe1, 0x86, 0xbb, 0x79, 0x27, 0xe5, 0x01, 0x19, 0x90, 0xb3, 0xe9, 0x08, 0xb0, 0xee, 0xfa, 0x3a, 0x67, 0xa9, 0xf3, 0xc8, 0x9e, 0x03, 0x41, 0x07, 0x75, 0x60, 0xbc, 0x94, 0x0c, 0x2a, 0xb7, 0xad, 0x00, 0x22, 0x00, 0x0b, 0x35, 0xb1, 0x72, 0xd6, 0x3c, 0xe9, 0x85, 0xe8, 0x66, 0xed, 0x10, 0x7a, 0x5c, 0xa3, 0xe6, 0xd9, 0x4d, 0xf0, 0x52, 0x69, 0x26, 0x14, 0xb4, 0x36, 0x7e, 0xad, 0x76, 0x9e, 0x58, 0x68, 0x3e, 0x91 }; const unsigned char attstmt_tpm_es256[3841] = { 0xa6, 0x63, 0x61, 0x6c, 0x67, 0x39, 0xff, 0xfe, 0x63, 0x73, 0x69, 0x67, 0x59, 0x01, 0x00, 0x6d, 0x11, 0x61, 0x1f, 0x45, 0xb9, 0x7f, 0x65, 0x6f, 0x97, 0x46, 0xfe, 0xbb, 0x8a, 0x98, 0x07, 0xa3, 0xbc, 0x67, 0x5c, 0xd7, 0x65, 0xa4, 0xf4, 0x6c, 0x5b, 0x37, 0x75, 0xa4, 0x7f, 0x08, 0x52, 0xeb, 0x1e, 0x12, 0xe2, 0x78, 0x8c, 0x7d, 0x94, 0xab, 0x7b, 0xed, 0x05, 0x17, 0x67, 0x7e, 0xaa, 0x02, 0x89, 0x6d, 0xe8, 0x6d, 0x43, 0x30, 0x99, 0xc6, 0xf9, 0x59, 0xe5, 0x82, 0x3c, 0x56, 0x4e, 0x77, 0x11, 0x25, 0xe4, 0x43, 0x6a, 0xae, 0x92, 0x4f, 0x60, 0x92, 0x50, 0xf9, 0x65, 0x0e, 0x44, 0x38, 0x3d, 0xf7, 0xaf, 0x66, 0x89, 0xc7, 0xe6, 0xe6, 0x01, 0x07, 0x9e, 0x90, 0xfd, 0x6d, 0xaa, 0x35, 0x51, 0x51, 0xbf, 0x54, 0x13, 0x95, 0xc2, 0x17, 0xfa, 0x32, 0x0f, 0xa7, 0x82, 0x17, 0x58, 0x6c, 0x3d, 0xea, 0x88, 0xd8, 0x64, 0xc7, 0xf8, 0xc2, 0xd6, 0x1c, 0xbb, 0xea, 0x1e, 0xb3, 0xd9, 0x4c, 0xa7, 0xce, 0x18, 0x1e, 0xcb, 0x42, 0x5f, 0xbf, 0x44, 0xe7, 0xf1, 0x22, 0xe0, 0x5b, 0xeb, 0xff, 0xb6, 0x1e, 0x6f, 0x60, 0x12, 0x16, 0x63, 0xfe, 0xab, 0x5e, 0x31, 0x13, 0xdb, 0x72, 0xc6, 0x9a, 0xf8, 0x8f, 0x19, 0x6b, 0x2e, 0xaf, 0x7d, 0xca, 0x9f, 0xbc, 0x6b, 0x1a, 0x8b, 0x5e, 0xe3, 0x9e, 0xaa, 0x8c, 0x79, 0x9c, 0x4e, 0xed, 0xe4, 0xff, 0x3d, 0x12, 0x79, 0x90, 0x09, 0x61, 0x97, 0x67, 0xbf, 0x04, 0xac, 0x37, 0xea, 0xa9, 0x1f, 0x9f, 0x52, 0x64, 0x0b, 0xeb, 0xc3, 0x61, 0xd4, 0x13, 0xb0, 0x84, 0xf1, 0x3c, 0x74, 0x83, 0xcc, 0xa8, 0x1c, 0x14, 0xe6, 0x9d, 0xfe, 0xec, 0xee, 0xa1, 0xd2, 0xc2, 0x0a, 0xa6, 0x36, 0x08, 0xbb, 0x17, 0xa5, 0x7b, 0x53, 0x34, 0x0e, 0xc9, 0x09, 0xe5, 0x10, 0xa6, 0x85, 0x01, 0x71, 0x66, 0xff, 0xd0, 0x6d, 0x4b, 0x93, 0xdb, 0x81, 0x25, 0x01, 0x63, 0x76, 0x65, 0x72, 0x63, 0x32, 0x2e, 0x30, 0x63, 0x78, 0x35, 0x63, 0x82, 0x59, 0x05, 0xc4, 0x30, 0x82, 0x05, 0xc0, 0x30, 0x82, 0x03, 0xa8, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x30, 0xcd, 0xf2, 0x7e, 0x81, 0xc0, 0x43, 0x85, 0xa2, 0xd7, 0x29, 0xef, 0xf7, 0x9f, 0xa5, 0x2b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x41, 0x31, 0x3f, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x36, 0x45, 0x55, 0x53, 0x2d, 0x53, 0x54, 0x4d, 0x2d, 0x4b, 0x45, 0x59, 0x49, 0x44, 0x2d, 0x31, 0x41, 0x44, 0x42, 0x39, 0x39, 0x34, 0x41, 0x42, 0x35, 0x38, 0x42, 0x45, 0x35, 0x37, 0x41, 0x30, 0x43, 0x43, 0x39, 0x42, 0x39, 0x30, 0x30, 0x45, 0x37, 0x38, 0x35, 0x31, 0x45, 0x31, 0x41, 0x34, 0x33, 0x43, 0x30, 0x38, 0x36, 0x36, 0x30, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x32, 0x31, 0x35, 0x30, 0x36, 0x35, 0x33, 0x5a, 0x17, 0x0d, 0x32, 0x37, 0x30, 0x36, 0x30, 0x33, 0x31, 0x39, 0x34, 0x30, 0x31, 0x36, 0x5a, 0x30, 0x00, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdb, 0xd5, 0x9a, 0xfc, 0x09, 0xa7, 0xc4, 0xa5, 0x5f, 0xbe, 0x5f, 0xa2, 0xeb, 0xd6, 0x8e, 0xed, 0xc5, 0x67, 0xa6, 0xa7, 0xd9, 0xb2, 0x46, 0xc6, 0xe0, 0xae, 0x0c, 0x02, 0x25, 0x0a, 0xf2, 0xc5, 0x96, 0xdc, 0xb7, 0x0e, 0xb9, 0x86, 0xd3, 0x51, 0xbb, 0x63, 0xf0, 0x4f, 0x8a, 0x5e, 0xd7, 0xf7, 0xff, 0xbb, 0x29, 0xbd, 0x58, 0xcf, 0x75, 0x02, 0x39, 0xcb, 0x80, 0xf1, 0xd4, 0xb6, 0x75, 0x67, 0x2f, 0x27, 0x4d, 0x0c, 0xcc, 0x18, 0x59, 0x87, 0xfa, 0x51, 0xd1, 0x80, 0xb5, 0x1a, 0xac, 0xac, 0x29, 0x51, 0xcf, 0x27, 0xaa, 0x74, 0xac, 0x3e, 0x59, 0x56, 0x67, 0xe4, 0x42, 0xe8, 0x30, 0x35, 0xb2, 0xf6, 0x27, 0x91, 0x62, 0x60, 0x42, 0x42, 0x12, 0xde, 0xfe, 0xdd, 0xee, 0xe8, 0xa8, 0x82, 0xf9, 0xb1, 0x08, 0xd5, 0x8d, 0x57, 0x9a, 0x29, 0xb9, 0xb4, 0xe9, 0x19, 0x1e, 0x33, 0x7d, 0x37, 0xa0, 0xce, 0x2e, 0x53, 0x13, 0x39, 0xb6, 0x12, 0x61, 0x63, 0xbf, 0xd3, 0x42, 0xeb, 0x6f, 0xed, 0xc1, 0x8e, 0x26, 0xba, 0x7d, 0x8b, 0x37, 0x7c, 0xbb, 0x42, 0x1e, 0x56, 0x76, 0xda, 0xdb, 0x35, 0x6b, 0x80, 0xe1, 0x8e, 0x00, 0xac, 0xd2, 0xfc, 0x22, 0x96, 0x14, 0x0c, 0xf4, 0xe4, 0xc5, 0xad, 0x14, 0xb7, 0x4d, 0x46, 0x63, 0x30, 0x79, 0x3a, 0x7c, 0x33, 0xb5, 0xe5, 0x2e, 0xbb, 0x5f, 0xca, 0xf2, 0x75, 0xe3, 0x4e, 0x99, 0x64, 0x1b, 0x26, 0x99, 0x60, 0x1a, 0x79, 0xcc, 0x30, 0x2c, 0xb3, 0x4c, 0x59, 0xf7, 0x77, 0x59, 0xd5, 0x90, 0x70, 0x21, 0x79, 0x8c, 0x1f, 0x79, 0x0a, 0x12, 0x8b, 0x3b, 0x37, 0x2d, 0x97, 0x39, 0x89, 0x92, 0x0c, 0x44, 0x7c, 0xe9, 0x9f, 0xce, 0x6d, 0xad, 0xc5, 0xae, 0xea, 0x8e, 0x50, 0x22, 0x37, 0xe0, 0xd1, 0x9e, 0xd6, 0xe6, 0xa8, 0xcc, 0x21, 0xfb, 0xff, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xf3, 0x30, 0x82, 0x01, 0xef, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x6d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x01, 0x01, 0xff, 0x04, 0x63, 0x30, 0x61, 0x30, 0x5f, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x1f, 0x30, 0x52, 0x30, 0x50, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x44, 0x1e, 0x42, 0x00, 0x54, 0x00, 0x43, 0x00, 0x50, 0x00, 0x41, 0x00, 0x20, 0x00, 0x20, 0x00, 0x54, 0x00, 0x72, 0x00, 0x75, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x20, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x74, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x20, 0x00, 0x49, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x30, 0x10, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x09, 0x30, 0x07, 0x06, 0x05, 0x67, 0x81, 0x05, 0x08, 0x03, 0x30, 0x59, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04, 0x4f, 0x30, 0x4d, 0xa4, 0x4b, 0x30, 0x49, 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x01, 0x0c, 0x0b, 0x69, 0x64, 0x3a, 0x35, 0x33, 0x35, 0x34, 0x34, 0x44, 0x32, 0x30, 0x31, 0x17, 0x30, 0x15, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x02, 0x0c, 0x0c, 0x53, 0x54, 0x33, 0x33, 0x48, 0x54, 0x50, 0x48, 0x41, 0x48, 0x42, 0x34, 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x03, 0x0c, 0x0b, 0x69, 0x64, 0x3a, 0x30, 0x30, 0x34, 0x39, 0x30, 0x30, 0x30, 0x34, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x45, 0x1a, 0xec, 0xfc, 0x91, 0x70, 0xf8, 0x83, 0x8b, 0x9c, 0x47, 0x2f, 0x0b, 0x9f, 0x07, 0xf3, 0x2f, 0x7c, 0xa2, 0x8a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x55, 0xa6, 0xee, 0xe3, 0x28, 0xdd, 0x40, 0x7f, 0x21, 0xd2, 0x7b, 0x8c, 0x69, 0x2f, 0x8c, 0x08, 0x29, 0xbc, 0x95, 0xb8, 0x30, 0x81, 0xb2, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa5, 0x30, 0x81, 0xa2, 0x30, 0x81, 0x9f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x81, 0x92, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x7a, 0x63, 0x73, 0x70, 0x72, 0x6f, 0x64, 0x65, 0x75, 0x73, 0x61, 0x69, 0x6b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x2e, 0x62, 0x6c, 0x6f, 0x62, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x65, 0x75, 0x73, 0x2d, 0x73, 0x74, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x69, 0x64, 0x2d, 0x31, 0x61, 0x64, 0x62, 0x39, 0x39, 0x34, 0x61, 0x62, 0x35, 0x38, 0x62, 0x65, 0x35, 0x37, 0x61, 0x30, 0x63, 0x63, 0x39, 0x62, 0x39, 0x30, 0x30, 0x65, 0x37, 0x38, 0x35, 0x31, 0x65, 0x31, 0x61, 0x34, 0x33, 0x63, 0x30, 0x38, 0x36, 0x36, 0x30, 0x2f, 0x62, 0x36, 0x63, 0x30, 0x64, 0x39, 0x38, 0x64, 0x2d, 0x35, 0x37, 0x38, 0x61, 0x2d, 0x34, 0x62, 0x66, 0x62, 0x2d, 0x61, 0x32, 0x64, 0x33, 0x2d, 0x65, 0x64, 0x66, 0x65, 0x35, 0x66, 0x38, 0x32, 0x30, 0x36, 0x30, 0x31, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x2a, 0x08, 0x30, 0x1f, 0xfd, 0x8f, 0x80, 0x9b, 0x4b, 0x37, 0x82, 0x61, 0x86, 0x36, 0x57, 0x90, 0xb5, 0x1d, 0x1f, 0xa3, 0xae, 0x68, 0xac, 0xa7, 0x96, 0x6a, 0x25, 0x5e, 0xc5, 0x82, 0x7c, 0x36, 0x64, 0x58, 0x11, 0xcb, 0xa5, 0xee, 0xbf, 0xc4, 0xdb, 0xa0, 0xc7, 0x82, 0x3b, 0xa3, 0x85, 0x9b, 0xc4, 0xee, 0x07, 0x36, 0xd7, 0xc7, 0xb6, 0x23, 0xed, 0xc2, 0x73, 0xab, 0xbe, 0xbe, 0xee, 0x63, 0x17, 0xf9, 0xd7, 0x7a, 0x23, 0x7b, 0xf8, 0x09, 0x7a, 0xaa, 0x7f, 0x67, 0xc3, 0x04, 0x84, 0x71, 0x9b, 0x06, 0x9c, 0x07, 0x42, 0x4b, 0x65, 0x41, 0x56, 0x58, 0x14, 0x92, 0xb0, 0xb9, 0xaf, 0xa1, 0x39, 0xd4, 0x08, 0x2d, 0x71, 0xd5, 0x6c, 0x56, 0xb9, 0x2b, 0x1e, 0xf3, 0x93, 0xa5, 0xe9, 0xb2, 0x9b, 0x4d, 0x05, 0x2b, 0xbc, 0xd2, 0x20, 0x57, 0x3b, 0xa4, 0x01, 0x68, 0x8c, 0x23, 0x20, 0x7d, 0xbb, 0x71, 0xe4, 0x2a, 0x24, 0xba, 0x75, 0x0c, 0x89, 0x54, 0x22, 0xeb, 0x0e, 0xb2, 0xf4, 0xc2, 0x1f, 0x02, 0xb7, 0xe3, 0x06, 0x41, 0x15, 0x6b, 0xf3, 0xc8, 0x2d, 0x5b, 0xc2, 0x21, 0x82, 0x3e, 0xe8, 0x95, 0x40, 0x39, 0x9e, 0x91, 0x68, 0x33, 0x0c, 0x3d, 0x45, 0xef, 0x99, 0x79, 0xe6, 0x32, 0xc9, 0x00, 0x84, 0x36, 0xfb, 0x0a, 0x8d, 0x41, 0x1c, 0x32, 0x64, 0x06, 0x9e, 0x0f, 0xb5, 0x04, 0xcc, 0x08, 0xb1, 0xb6, 0x2b, 0xcf, 0x36, 0x0f, 0x73, 0x14, 0x8e, 0x25, 0x44, 0xb3, 0x0c, 0x34, 0x14, 0x96, 0x0c, 0x8a, 0x65, 0xa1, 0xde, 0x8e, 0xc8, 0x9d, 0xbe, 0x66, 0xdf, 0x06, 0x91, 0xca, 0x15, 0x0f, 0x92, 0xd5, 0x2a, 0x0b, 0xdc, 0x4c, 0x6a, 0xf3, 0x16, 0x4a, 0x3e, 0xb9, 0x76, 0xbc, 0xfe, 0x62, 0xd4, 0xa8, 0xcd, 0x94, 0x78, 0x0d, 0xdd, 0x94, 0xfd, 0x5e, 0x63, 0x57, 0x27, 0x05, 0x9c, 0xd0, 0x80, 0x91, 0x91, 0x79, 0xe8, 0x5e, 0x18, 0x64, 0x22, 0xe4, 0x2c, 0x13, 0x65, 0xa4, 0x51, 0x5a, 0x1e, 0x3b, 0x71, 0x2e, 0x70, 0x9f, 0xc4, 0xa5, 0x20, 0xcd, 0xef, 0xd8, 0x3f, 0xa4, 0xf5, 0x89, 0x8a, 0xa5, 0x4f, 0x76, 0x2d, 0x49, 0x56, 0x00, 0x8d, 0xde, 0x40, 0xba, 0x24, 0x46, 0x51, 0x38, 0xad, 0xdb, 0xc4, 0x04, 0xf4, 0x6e, 0xc0, 0x29, 0x48, 0x07, 0x6a, 0x1b, 0x26, 0x32, 0x0a, 0xfb, 0xea, 0x71, 0x2a, 0x11, 0xfc, 0x98, 0x7c, 0x44, 0x87, 0xbc, 0x06, 0x3a, 0x4d, 0xbd, 0x91, 0x63, 0x4f, 0x26, 0x48, 0x54, 0x47, 0x1b, 0xbd, 0xf0, 0xf1, 0x56, 0x05, 0xc5, 0x0f, 0x8f, 0x20, 0xa5, 0xcc, 0xfb, 0x76, 0xb0, 0xbd, 0x83, 0xde, 0x7f, 0x39, 0x4f, 0xcf, 0x61, 0x74, 0x52, 0xa7, 0x1d, 0xf6, 0xb5, 0x5e, 0x4a, 0x82, 0x20, 0xc1, 0x94, 0xaa, 0x2c, 0x33, 0xd6, 0x0a, 0xf9, 0x8f, 0x92, 0xc6, 0x29, 0x80, 0xf5, 0xa2, 0xb1, 0xff, 0xb6, 0x2b, 0xaa, 0x04, 0x00, 0x72, 0xb4, 0x12, 0xbb, 0xb1, 0xf1, 0x3c, 0x88, 0xa3, 0xab, 0x49, 0x17, 0x90, 0x80, 0x59, 0xa2, 0x96, 0x41, 0x69, 0x74, 0x33, 0x8a, 0x28, 0x33, 0x7e, 0xb3, 0x19, 0x92, 0x28, 0xc1, 0xf0, 0xd1, 0x82, 0xd5, 0x42, 0xff, 0xe7, 0xa5, 0x3f, 0x1e, 0xb6, 0x4a, 0x23, 0xcc, 0x6a, 0x7f, 0x15, 0x15, 0x52, 0x25, 0xb1, 0xca, 0x21, 0x95, 0x11, 0x53, 0x3e, 0x1f, 0x50, 0x33, 0x12, 0x7a, 0x62, 0xce, 0xcc, 0x71, 0xc2, 0x5f, 0x34, 0x47, 0xc6, 0x7c, 0x71, 0xfa, 0xa0, 0x54, 0x00, 0xb2, 0xdf, 0xc5, 0x54, 0xac, 0x6c, 0x53, 0xef, 0x64, 0x6b, 0x08, 0x82, 0xd8, 0x16, 0x1e, 0xca, 0x40, 0xf3, 0x1f, 0xdf, 0x56, 0x63, 0x10, 0xbc, 0xd7, 0xa0, 0xeb, 0xee, 0xd1, 0x95, 0xe5, 0xef, 0xf1, 0x6a, 0x83, 0x2d, 0x5a, 0x59, 0x06, 0xef, 0x30, 0x82, 0x06, 0xeb, 0x30, 0x82, 0x04, 0xd3, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x33, 0x00, 0x00, 0x05, 0x23, 0xbf, 0xe8, 0xa1, 0x1a, 0x2a, 0x68, 0xbd, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, 0x23, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x32, 0x30, 0x31, 0x34, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x36, 0x30, 0x33, 0x31, 0x39, 0x34, 0x30, 0x31, 0x36, 0x5a, 0x17, 0x0d, 0x32, 0x37, 0x30, 0x36, 0x30, 0x33, 0x31, 0x39, 0x34, 0x30, 0x31, 0x36, 0x5a, 0x30, 0x41, 0x31, 0x3f, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x36, 0x45, 0x55, 0x53, 0x2d, 0x53, 0x54, 0x4d, 0x2d, 0x4b, 0x45, 0x59, 0x49, 0x44, 0x2d, 0x31, 0x41, 0x44, 0x42, 0x39, 0x39, 0x34, 0x41, 0x42, 0x35, 0x38, 0x42, 0x45, 0x35, 0x37, 0x41, 0x30, 0x43, 0x43, 0x39, 0x42, 0x39, 0x30, 0x30, 0x45, 0x37, 0x38, 0x35, 0x31, 0x45, 0x31, 0x41, 0x34, 0x33, 0x43, 0x30, 0x38, 0x36, 0x36, 0x30, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xdb, 0x03, 0x34, 0x82, 0xfa, 0x81, 0x1c, 0x84, 0x0b, 0xa0, 0x0e, 0x60, 0xd8, 0x9d, 0x84, 0xf4, 0x81, 0xc4, 0xe9, 0xff, 0xcf, 0xe9, 0xa3, 0x57, 0x53, 0x60, 0xa8, 0x19, 0xce, 0xbe, 0xe1, 0x97, 0xee, 0x5d, 0x8c, 0x9f, 0xe4, 0xbd, 0xef, 0xbd, 0x94, 0x14, 0xe4, 0x74, 0x41, 0x02, 0xe9, 0x03, 0x19, 0x9f, 0xdd, 0x48, 0x2d, 0xbd, 0xca, 0x26, 0x47, 0x2c, 0x01, 0x31, 0x5f, 0x34, 0xef, 0x59, 0x35, 0x48, 0x36, 0x3d, 0x1e, 0xdf, 0xd8, 0x13, 0xf0, 0xd0, 0x67, 0xc1, 0xb0, 0x47, 0x67, 0xa2, 0xd6, 0x62, 0xc8, 0xe1, 0x00, 0x36, 0x8b, 0x45, 0xf6, 0x3b, 0x96, 0x60, 0xa0, 0x45, 0x26, 0xcb, 0xc7, 0x0b, 0x5b, 0x97, 0xd1, 0xaf, 0x54, 0x25, 0x7a, 0x67, 0xe4, 0x2a, 0xd8, 0x9d, 0x53, 0x05, 0xbd, 0x12, 0xac, 0xa2, 0x8e, 0x95, 0xb4, 0x2a, 0xca, 0x89, 0x93, 0x64, 0x97, 0x25, 0xdc, 0x1f, 0xa9, 0xe0, 0x55, 0x07, 0x38, 0x1d, 0xee, 0x02, 0x90, 0x22, 0xf5, 0xad, 0x4e, 0x5c, 0xf8, 0xc5, 0x1f, 0x9e, 0x84, 0x7e, 0x13, 0x47, 0x52, 0xa2, 0x36, 0xf9, 0xf6, 0xbf, 0x76, 0x9e, 0x0f, 0xdd, 0x14, 0x99, 0xb9, 0xd8, 0x5a, 0x42, 0x3d, 0xd8, 0xbf, 0xdd, 0xb4, 0x9b, 0xbf, 0x6a, 0x9f, 0x89, 0x13, 0x75, 0xaf, 0x96, 0xd2, 0x72, 0xdf, 0xb3, 0x80, 0x6f, 0x84, 0x1a, 0x9d, 0x06, 0x55, 0x09, 0x29, 0xea, 0xa7, 0x05, 0x31, 0xec, 0x47, 0x3a, 0xcf, 0x3f, 0x9c, 0x2c, 0xbd, 0xd0, 0x7d, 0xe4, 0x75, 0x5b, 0x33, 0xbe, 0x12, 0x86, 0x09, 0xcf, 0x66, 0x9a, 0xeb, 0xf8, 0xf8, 0x72, 0x91, 0x88, 0x4a, 0x5e, 0x89, 0x62, 0x6a, 0x94, 0xdc, 0x48, 0x37, 0x13, 0xd8, 0x91, 0x02, 0xe3, 0x42, 0x41, 0x7c, 0x2f, 0xe3, 0xb6, 0x0f, 0xb4, 0x96, 0x06, 0x80, 0xca, 0x28, 0x01, 0x6f, 0x4b, 0xcd, 0x28, 0xd4, 0x2c, 0x94, 0x7e, 0x40, 0x7e, 0xdf, 0x01, 0xe5, 0xf2, 0x33, 0xd4, 0xda, 0xf4, 0x1a, 0x17, 0xf7, 0x5d, 0xcb, 0x66, 0x2c, 0x2a, 0xeb, 0xe1, 0xb1, 0x4a, 0xc3, 0x85, 0x63, 0xb2, 0xac, 0xd0, 0x3f, 0x1a, 0x8d, 0xa5, 0x0c, 0xee, 0x4f, 0xde, 0x74, 0x9c, 0xe0, 0x5a, 0x10, 0xc7, 0xb8, 0xe4, 0xec, 0xe7, 0x73, 0xa6, 0x41, 0x42, 0x37, 0xe1, 0xdf, 0xb9, 0xc7, 0xb5, 0x14, 0xa8, 0x80, 0x95, 0xa0, 0x12, 0x67, 0x99, 0xf5, 0xba, 0x25, 0x0a, 0x74, 0x86, 0x71, 0x9c, 0x7f, 0x59, 0x97, 0xd2, 0x3f, 0x10, 0xfe, 0x6a, 0xb9, 0xe4, 0x47, 0x36, 0xfb, 0x0f, 0x50, 0xee, 0xfc, 0x87, 0x99, 0x7e, 0x36, 0x64, 0x1b, 0xc7, 0x13, 0xb3, 0x33, 0x18, 0x71, 0xa4, 0xc3, 0xb0, 0xfc, 0x45, 0x37, 0x11, 0x40, 0xb3, 0xde, 0x2c, 0x9f, 0x0a, 0xcd, 0xaf, 0x5e, 0xfb, 0xd5, 0x9c, 0xea, 0xd7, 0x24, 0x19, 0x3a, 0x92, 0x80, 0xa5, 0x63, 0xc5, 0x3e, 0xdd, 0x51, 0xd0, 0x9f, 0xb8, 0x5e, 0xd5, 0xf1, 0xfe, 0xa5, 0x93, 0xfb, 0x7f, 0xd9, 0xb8, 0xb7, 0x0e, 0x0d, 0x12, 0x71, 0xf0, 0x52, 0x9d, 0xe9, 0xd0, 0xd2, 0x8b, 0x38, 0x8b, 0x85, 0x83, 0x98, 0x24, 0x88, 0xe8, 0x42, 0x30, 0x83, 0x12, 0xef, 0x09, 0x96, 0x2f, 0x21, 0x81, 0x05, 0x30, 0x0c, 0xbb, 0xba, 0x21, 0x39, 0x16, 0x12, 0xe8, 0x4b, 0x7b, 0x7a, 0x66, 0xb8, 0x22, 0x2c, 0x71, 0xaf, 0x59, 0xa1, 0xfc, 0x61, 0xf1, 0xb4, 0x5e, 0xfc, 0x43, 0x19, 0x45, 0x6e, 0xa3, 0x45, 0xe4, 0xcb, 0x66, 0x5f, 0xe0, 0x57, 0xf6, 0x0a, 0x30, 0xa3, 0xd6, 0x51, 0x24, 0xc9, 0x07, 0x55, 0x82, 0x4a, 0x66, 0x0e, 0x9d, 0xb2, 0x2f, 0x84, 0x56, 0x6c, 0x3e, 0x71, 0xef, 0x9b, 0x35, 0x4d, 0x72, 0xdc, 0x46, 0x2a, 0xe3, 0x7b, 0x13, 0x20, 0xbf, 0xab, 0x77, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x8e, 0x30, 0x82, 0x01, 0x8a, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x84, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x14, 0x30, 0x12, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x24, 0x06, 0x05, 0x67, 0x81, 0x05, 0x08, 0x03, 0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0f, 0x30, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x1f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0x1a, 0xec, 0xfc, 0x91, 0x70, 0xf8, 0x83, 0x8b, 0x9c, 0x47, 0x2f, 0x0b, 0x9f, 0x07, 0xf3, 0x2f, 0x7c, 0xa2, 0x8a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7a, 0x8c, 0x0a, 0xce, 0x2f, 0x48, 0x62, 0x17, 0xe2, 0x94, 0xd1, 0xae, 0x55, 0xc1, 0x52, 0xec, 0x71, 0x74, 0xa4, 0x56, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x69, 0x30, 0x67, 0x30, 0x65, 0xa0, 0x63, 0xa0, 0x61, 0x86, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x25, 0x32, 0x30, 0x54, 0x50, 0x4d, 0x25, 0x32, 0x30, 0x52, 0x6f, 0x6f, 0x74, 0x25, 0x32, 0x30, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x25, 0x32, 0x30, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x25, 0x32, 0x30, 0x32, 0x30, 0x31, 0x34, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x7d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x71, 0x30, 0x6f, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x61, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x25, 0x32, 0x30, 0x54, 0x50, 0x4d, 0x25, 0x32, 0x30, 0x52, 0x6f, 0x6f, 0x74, 0x25, 0x32, 0x30, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x25, 0x32, 0x30, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x25, 0x32, 0x30, 0x32, 0x30, 0x31, 0x34, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x48, 0x24, 0x32, 0xe8, 0xd6, 0x38, 0xda, 0x65, 0xec, 0x1b, 0x18, 0x8e, 0x37, 0x07, 0xd5, 0x18, 0x5a, 0xc8, 0xb9, 0xbb, 0x24, 0x8a, 0x4d, 0xa1, 0x3c, 0x9e, 0x46, 0x76, 0xcf, 0xa5, 0xdf, 0xd7, 0x61, 0xba, 0x05, 0x89, 0x3c, 0x13, 0xc2, 0x1f, 0x71, 0xe3, 0xec, 0x5d, 0x54, 0x9e, 0xd9, 0x01, 0x5a, 0x10, 0x3b, 0x17, 0x75, 0xde, 0xa1, 0x45, 0xbf, 0x1d, 0x1b, 0x41, 0x21, 0x42, 0x68, 0x22, 0x6b, 0xbb, 0xcb, 0x11, 0x04, 0xd2, 0xae, 0x86, 0xcf, 0x73, 0x5a, 0xf2, 0x80, 0x18, 0x00, 0xf0, 0xd6, 0x6c, 0x5a, 0x1e, 0xb3, 0x4d, 0x30, 0x02, 0x4a, 0x6a, 0x03, 0x36, 0x42, 0xde, 0xb2, 0x52, 0x55, 0xff, 0x71, 0xeb, 0x7b, 0x8b, 0x55, 0x6c, 0xdf, 0x05, 0x35, 0x47, 0x70, 0x53, 0xfb, 0x6c, 0xba, 0x06, 0xb2, 0x61, 0x86, 0xdc, 0x2a, 0x64, 0x81, 0x24, 0x79, 0x46, 0x73, 0x04, 0x55, 0x59, 0xed, 0xd6, 0x06, 0x61, 0x15, 0xf9, 0x8d, 0x78, 0x39, 0x7b, 0x84, 0x7a, 0x40, 0x45, 0x13, 0x1a, 0x91, 0x71, 0x8f, 0xd1, 0x4f, 0x78, 0x10, 0x68, 0x9b, 0x15, 0x79, 0x3f, 0x79, 0x2d, 0x9b, 0xc7, 0x5d, 0xa3, 0xcf, 0xa9, 0x14, 0xb0, 0xc4, 0xdb, 0xa9, 0x45, 0x6a, 0x6e, 0x60, 0x45, 0x0b, 0x14, 0x25, 0xc7, 0x74, 0xd0, 0x36, 0xaf, 0xc5, 0xbd, 0x4f, 0x7b, 0xc0, 0x04, 0x43, 0x85, 0xbb, 0x06, 0x36, 0x77, 0x26, 0x02, 0x23, 0x0b, 0xf8, 0x57, 0x8f, 0x1f, 0x27, 0x30, 0x95, 0xff, 0x83, 0x23, 0x2b, 0x49, 0x33, 0x43, 0x62, 0x87, 0x5d, 0x27, 0x12, 0x1a, 0x68, 0x7b, 0xba, 0x2d, 0xf6, 0xed, 0x2c, 0x26, 0xb5, 0xbb, 0xe2, 0x6f, 0xc2, 0x61, 0x17, 0xfc, 0x72, 0x14, 0x57, 0x2c, 0x2c, 0x5a, 0x92, 0x13, 0x41, 0xc4, 0x7e, 0xb5, 0x64, 0x5b, 0x86, 0x57, 0x13, 0x14, 0xff, 0xf5, 0x04, 0xb9, 0x3d, 0x2d, 0xc3, 0xe9, 0x75, 0x1f, 0x68, 0x0b, 0xb5, 0x76, 0xe1, 0x7d, 0xe3, 0xb0, 0x14, 0xa8, 0x45, 0x05, 0x98, 0x81, 0x32, 0xc1, 0xf5, 0x49, 0x4d, 0x58, 0xa4, 0xee, 0xd8, 0x84, 0xba, 0x65, 0x07, 0x8d, 0xf7, 0x9a, 0xff, 0x7d, 0xa5, 0xbc, 0x9a, 0xed, 0x4a, 0x5d, 0xa4, 0x97, 0x4b, 0x4d, 0x31, 0x90, 0xb5, 0x7d, 0x28, 0x77, 0x25, 0x88, 0x1c, 0xbf, 0x78, 0x22, 0xb2, 0xb5, 0x5c, 0x9a, 0xc9, 0x63, 0x17, 0x96, 0xe9, 0xc2, 0x52, 0x30, 0xb8, 0x9b, 0x37, 0x69, 0x1a, 0x6a, 0x66, 0x76, 0x18, 0xac, 0xc0, 0x48, 0xee, 0x46, 0x5b, 0xbe, 0x6a, 0xd5, 0x72, 0x07, 0xdc, 0x7d, 0x05, 0xbe, 0x76, 0x7d, 0xa5, 0x5e, 0x53, 0xb5, 0x47, 0x80, 0x58, 0xf0, 0xaf, 0x6f, 0x4e, 0xc0, 0xf1, 0x1e, 0x37, 0x64, 0x15, 0x42, 0x96, 0x18, 0x3a, 0x89, 0xc8, 0x14, 0x48, 0x89, 0x5c, 0x12, 0x88, 0x98, 0x0b, 0x7b, 0x4e, 0xce, 0x1c, 0xda, 0xd5, 0xa4, 0xd3, 0x32, 0x32, 0x74, 0x5b, 0xcc, 0xfd, 0x2b, 0x02, 0xfb, 0xae, 0xd0, 0x5a, 0x4c, 0xc9, 0xc1, 0x35, 0x19, 0x90, 0x5f, 0xca, 0x14, 0xeb, 0x4c, 0x17, 0xd7, 0xe3, 0xe2, 0x5d, 0xb4, 0x49, 0xaa, 0xf0, 0x50, 0x87, 0xc3, 0x20, 0x00, 0xda, 0xe9, 0x04, 0x80, 0x64, 0xac, 0x9f, 0xcd, 0x26, 0x41, 0x48, 0xe8, 0x4c, 0x46, 0xcc, 0x5b, 0xd7, 0xca, 0x4c, 0x1b, 0x43, 0x43, 0x1e, 0xbd, 0x94, 0xe7, 0xa7, 0xa6, 0x86, 0xe5, 0xd1, 0x78, 0x29, 0xa2, 0x40, 0xc5, 0xc5, 0x47, 0xb6, 0x6d, 0x53, 0xde, 0xac, 0x97, 0x74, 0x24, 0x57, 0xcc, 0x05, 0x93, 0xfd, 0x52, 0x35, 0x29, 0xd5, 0xe0, 0xfa, 0x23, 0x0d, 0xd7, 0xaa, 0x8b, 0x07, 0x4b, 0xf6, 0x64, 0xc7, 0xad, 0x3c, 0xa1, 0xb5, 0xc5, 0x70, 0xaf, 0x46, 0xfe, 0x9a, 0x82, 0x4d, 0x75, 0xb8, 0x6d, 0x67, 0x70, 0x75, 0x62, 0x41, 0x72, 0x65, 0x61, 0x58, 0x76, 0x00, 0x23, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x72, 0x00, 0x20, 0x9d, 0xff, 0xcb, 0xf3, 0x6c, 0x38, 0x3a, 0xe6, 0x99, 0xfb, 0x98, 0x68, 0xdc, 0x6d, 0xcb, 0x89, 0xd7, 0x15, 0x38, 0x84, 0xbe, 0x28, 0x03, 0x92, 0x2c, 0x12, 0x41, 0x58, 0xbf, 0xad, 0x22, 0xae, 0x00, 0x10, 0x00, 0x10, 0x00, 0x03, 0x00, 0x10, 0x00, 0x20, 0xfb, 0xd6, 0xba, 0x74, 0xe6, 0x6e, 0x5c, 0x87, 0xef, 0x89, 0xa2, 0xe8, 0x3d, 0x0b, 0xe9, 0x69, 0x2c, 0x07, 0x07, 0x7a, 0x8a, 0x1e, 0xce, 0x12, 0xea, 0x3b, 0xb3, 0xf1, 0xf3, 0xd9, 0xc3, 0xe6, 0x00, 0x20, 0x3c, 0x68, 0x51, 0x94, 0x54, 0x8d, 0xeb, 0x9f, 0xb2, 0x2c, 0x66, 0x75, 0xb6, 0xb7, 0x55, 0x22, 0x0d, 0x87, 0x59, 0xc4, 0x39, 0x91, 0x62, 0x17, 0xc2, 0xc3, 0x53, 0xa5, 0x26, 0x97, 0x4f, 0x2d, 0x68, 0x63, 0x65, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x58, 0xa1, 0xff, 0x54, 0x43, 0x47, 0x80, 0x17, 0x00, 0x22, 0x00, 0x0b, 0x73, 0xbe, 0xb7, 0x40, 0x82, 0xc0, 0x49, 0x9a, 0xf7, 0xf2, 0xd0, 0x79, 0x6c, 0x88, 0xf3, 0x56, 0x7b, 0x7a, 0x7d, 0xcd, 0x70, 0xd1, 0xbc, 0x41, 0x88, 0x48, 0x51, 0x03, 0xf3, 0x58, 0x3e, 0xb8, 0x00, 0x14, 0x9f, 0x57, 0x39, 0x67, 0xa8, 0x7b, 0xd8, 0xf6, 0x9e, 0x75, 0xc9, 0x85, 0xab, 0xe3, 0x55, 0xc7, 0x9c, 0xf6, 0xd8, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x1c, 0x12, 0xfd, 0xc6, 0x05, 0xc6, 0x2b, 0xf5, 0xe9, 0x88, 0x01, 0x1f, 0x70, 0x8d, 0x98, 0x2a, 0x04, 0x21, 0x30, 0x00, 0x22, 0x00, 0x0b, 0xf4, 0xfd, 0x9a, 0x33, 0x55, 0x21, 0x08, 0x27, 0x48, 0x55, 0x01, 0x56, 0xf9, 0x0b, 0x4e, 0x47, 0x55, 0x08, 0x2e, 0x3c, 0x91, 0x3d, 0x6e, 0x53, 0xcf, 0x08, 0xe9, 0x0a, 0x4b, 0xc9, 0x7e, 0x99, 0x00, 0x22, 0x00, 0x0b, 0x51, 0xd3, 0x38, 0xfe, 0xaa, 0xda, 0xc6, 0x68, 0x84, 0x39, 0xe7, 0xb1, 0x03, 0x22, 0x5e, 0xc4, 0xd3, 0xf1, 0x0c, 0xec, 0x35, 0x5d, 0x50, 0xa3, 0x9d, 0xab, 0xa1, 0x7b, 0x61, 0x51, 0x8f, 0x4e }; /* * Security Key By Yubico * 5.1.X * f8a011f3-8c0a-4d15-8006-17111f9edc7d */ const unsigned char aaguid[16] = { 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, }; /* * Windows Hello by Microsoft */ const unsigned char aaguid_tpm[16] = { 0x08, 0x98, 0x70, 0x58, 0xca, 0xdc, 0x4b, 0x81, 0xb6, 0xe1, 0x30, 0xde, 0x50, 0xdc, 0xbe, 0x96, }; 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_authdata_raw_len(c) == 0); assert(fido_cred_authdata_raw_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_prot(c) == 0); 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); memset(&io_f, 0, sizeof(io_f)); 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_prot(c) == 0); 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 0); free_cred(c); } static void no_authdata(void) { fido_cred_t *c; unsigned char *unset; unset = calloc(1, sizeof(aaguid)); assert(unset != NULL); 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), unset, sizeof(aaguid)) == 0); free_cred(c); free(unset); } 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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] = (unsigned char)~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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 0); free_cred(c); free(junk); } static void junk_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_set_fmt(c, "junk") == FIDO_ERR_INVALID_ARGUMENT); assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); free_cred(c); } 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 0); free_cred(c); } static void junk_authdata(void) { fido_cred_t *c; unsigned char *junk; unsigned char *unset; junk = malloc(sizeof(authdata)); assert(junk != NULL); memcpy(junk, authdata, sizeof(authdata)); junk[0] = (unsigned char)~junk[0]; unset = calloc(1, sizeof(aaguid)); assert(unset != NULL); 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_authdata_raw_len(c) == 0); assert(fido_cred_authdata_raw_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_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), unset, sizeof(aaguid)) == 0); assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); free_cred(c); free(junk); free(unset); } 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] = (unsigned char)~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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 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] = (unsigned char)~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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 0); free_cred(c); free(junk); } /* github issue #6 */ static void invalid_type(void) { fido_cred_t *c; unsigned char *unset; unset = calloc(1, sizeof(aaguid)); assert(unset != NULL); 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), unset, sizeof(aaguid)) == 0); free_cred(c); free(unset); } /* 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); } static void wrong_credprot(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_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_set_prot(c, FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID) == FIDO_OK); assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK); assert(fido_cred_verify(c) == FIDO_ERR_INVALID_PARAM); free_cred(c); } static void raw_authdata(void) { fido_cred_t *c; cbor_item_t *item; struct cbor_load_result cbor_result; const unsigned char *ptr; unsigned char *cbor; size_t len; size_t cbor_len; size_t alloclen; 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((ptr = fido_cred_authdata_ptr(c)) != NULL); assert((len = fido_cred_authdata_len(c)) != 0); assert((item = cbor_load(ptr, len, &cbor_result)) != NULL); assert(cbor_result.read == len); assert(cbor_isa_bytestring(item)); assert((ptr = fido_cred_authdata_raw_ptr(c)) != NULL); assert((len = fido_cred_authdata_raw_len(c)) != 0); assert(cbor_bytestring_length(item) == len); assert(memcmp(ptr, cbor_bytestring_handle(item), len) == 0); assert((len = fido_cred_authdata_len(c)) != 0); assert((cbor_len = cbor_serialize_alloc(item, &cbor, &alloclen)) == len); assert((ptr = cbor_bytestring_handle(item)) != NULL); assert((len = cbor_bytestring_length(item)) != 0); assert(fido_cred_set_authdata_raw(c, ptr, len) == FIDO_OK); assert((ptr = fido_cred_authdata_ptr(c)) != NULL); assert((len = fido_cred_authdata_len(c)) != 0); assert(len == cbor_len); assert(memcmp(cbor, ptr, len) == 0); assert(cbor_len == sizeof(authdata)); assert(memcmp(cbor, authdata, cbor_len) == 0); cbor_decref(&item); free(cbor); free_cred(c); } static void fmt_none(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_fmt(c, "none") == FIDO_OK); assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_cred_prot(c) == 0); 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); assert(fido_cred_aaguid_len(c) == sizeof(aaguid)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid, sizeof(aaguid)) == 0); free_cred(c); } static void valid_tpm_rs256_cred(void) { fido_cred_t *c; c = alloc_cred(); assert(fido_cred_set_type(c, COSE_RS256) == FIDO_OK); assert(fido_cred_set_clientdata(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_tpm_rs256, sizeof(authdata_tpm_rs256)) == 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_fmt(c, "tpm") == FIDO_OK); assert(fido_cred_set_attstmt(c, attstmt_tpm_rs256, sizeof(attstmt_tpm_rs256)) == FIDO_OK); assert(fido_cred_verify(c) == FIDO_OK); assert(fido_cred_prot(c) == 0); assert(fido_cred_pubkey_len(c) == sizeof(pubkey_tpm_rs256)); assert(memcmp(fido_cred_pubkey_ptr(c), pubkey_tpm_rs256, sizeof(pubkey_tpm_rs256)) == 0); assert(fido_cred_id_len(c) == sizeof(id_tpm_rs256)); assert(memcmp(fido_cred_id_ptr(c), id_tpm_rs256, sizeof(id_tpm_rs256)) == 0); assert(fido_cred_aaguid_len(c) == sizeof(aaguid_tpm)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid_tpm, sizeof(aaguid_tpm)) == 0); free_cred(c); } static void valid_tpm_es256_cred(void) { fido_cred_t *c; c = alloc_cred(); assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK); assert(fido_cred_set_clientdata(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_tpm_es256, sizeof(authdata_tpm_es256)) == 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_fmt(c, "tpm") == FIDO_OK); assert(fido_cred_set_attstmt(c, attstmt_tpm_es256, sizeof(attstmt_tpm_es256)) == FIDO_OK); assert(fido_cred_verify(c) == FIDO_OK); assert(fido_cred_prot(c) == 0); assert(fido_cred_pubkey_len(c) == sizeof(pubkey_tpm_es256)); assert(memcmp(fido_cred_pubkey_ptr(c), pubkey_tpm_es256, sizeof(pubkey_tpm_es256)) == 0); assert(fido_cred_id_len(c) == sizeof(id_tpm_es256)); assert(memcmp(fido_cred_id_ptr(c), id_tpm_es256, sizeof(id_tpm_es256)) == 0); assert(fido_cred_aaguid_len(c) == sizeof(aaguid_tpm)); assert(memcmp(fido_cred_aaguid_ptr(c), aaguid_tpm, sizeof(aaguid_tpm)) == 0); 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_fmt(); junk_rp_id(); junk_rp_name(); junk_authdata(); junk_x509(); junk_sig(); wrong_options(); invalid_type(); bad_cbor_serialize(); duplicate_keys(); unsorted_keys(); wrong_credprot(); raw_authdata(); fmt_none(); valid_tpm_rs256_cred(); valid_tpm_es256_cred(); exit(0); } libfido2-1.10.0/regress/dev.c000066400000000000000000000227531417126203300156670ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "../fuzz/wiredata_fido2.h" #define FAKE_DEV_HANDLE ((void *)0xdeadbeef) #define REPORT_LEN (64 + 1) static uint8_t ctap_nonce[8]; static uint8_t *wiredata_ptr; static size_t wiredata_len; static int initialised; static long interval_ms; 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) { struct timespec tv; size_t n; long d; assert(handle == FAKE_DEV_HANDLE); assert(ptr != NULL); assert(len == REPORT_LEN - 1); if (wiredata_ptr == NULL) return (-1); if (!initialised) { assert(wiredata_len >= REPORT_LEN - 1); memcpy(&wiredata_ptr[7], &ctap_nonce, sizeof(ctap_nonce)); initialised = 1; } if (ms >= 0 && ms < interval_ms) d = ms; else d = interval_ms; if (d) { tv.tv_sec = d / 1000; tv.tv_nsec = (d % 1000) * 1000000; if (nanosleep(&tv, NULL) == -1) err(1, "nanosleep"); } if (d != interval_ms) return (-1); /* timeout */ if (wiredata_len < len) n = wiredata_len; else n = len; memcpy(ptr, wiredata_ptr, n); wiredata_ptr += n; wiredata_len -= n; return ((int)n); } static int dummy_write(void *handle, const unsigned char *ptr, size_t len) { struct timespec tv; assert(handle == FAKE_DEV_HANDLE); assert(ptr != NULL); assert(len == REPORT_LEN); if (!initialised) memcpy(&ctap_nonce, &ptr[8], sizeof(ctap_nonce)); if (interval_ms) { tv.tv_sec = interval_ms / 1000; tv.tv_nsec = (interval_ms % 1000) * 1000000; if (nanosleep(&tv, NULL) == -1) err(1, "nanosleep"); } return ((int)len); } static uint8_t * wiredata_setup(const uint8_t *data, size_t len) { const uint8_t ctap_init_data[] = { WIREDATA_CTAP_INIT }; assert(wiredata_ptr == NULL); assert(SIZE_MAX - len > sizeof(ctap_init_data)); assert((wiredata_ptr = malloc(sizeof(ctap_init_data) + len)) != NULL); memcpy(wiredata_ptr, ctap_init_data, sizeof(ctap_init_data)); if (len) memcpy(wiredata_ptr + sizeof(ctap_init_data), data, len); wiredata_len = sizeof(ctap_init_data) + len; return (wiredata_ptr); } static void wiredata_clear(uint8_t **wiredata) { free(*wiredata); *wiredata = NULL; wiredata_ptr = NULL; wiredata_len = 0; initialised = 0; } /* gh#56 */ static void open_iff_ok(void) { fido_dev_t *dev = NULL; fido_dev_io_t io; memset(&io, 0, sizeof(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); } static void reopen(void) { const uint8_t cbor_info_data[] = { WIREDATA_CTAP_CBOR_INFO }; uint8_t *wiredata; fido_dev_t *dev = NULL; fido_dev_io_t io; memset(&io, 0, sizeof(io)); io.open = dummy_open; io.close = dummy_close; io.read = dummy_read; io.write = dummy_write; wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data)); assert((dev = fido_dev_new()) != NULL); assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_close(dev) == FIDO_OK); wiredata_clear(&wiredata); wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data)); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_close(dev) == FIDO_OK); fido_dev_free(&dev); wiredata_clear(&wiredata); } static void double_open(void) { const uint8_t cbor_info_data[] = { WIREDATA_CTAP_CBOR_INFO }; uint8_t *wiredata; fido_dev_t *dev = NULL; fido_dev_io_t io; memset(&io, 0, sizeof(io)); io.open = dummy_open; io.close = dummy_close; io.read = dummy_read; io.write = dummy_write; wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data)); assert((dev = fido_dev_new()) != NULL); assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_open(dev, "dummy") == FIDO_ERR_INVALID_ARGUMENT); assert(fido_dev_close(dev) == FIDO_OK); fido_dev_free(&dev); wiredata_clear(&wiredata); } static void double_close(void) { const uint8_t cbor_info_data[] = { WIREDATA_CTAP_CBOR_INFO }; uint8_t *wiredata; fido_dev_t *dev = NULL; fido_dev_io_t io; memset(&io, 0, sizeof(io)); io.open = dummy_open; io.close = dummy_close; io.read = dummy_read; io.write = dummy_write; wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data)); assert((dev = fido_dev_new()) != NULL); assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK); assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_close(dev) == FIDO_OK); assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT); fido_dev_free(&dev); wiredata_clear(&wiredata); } static void is_fido2(void) { const uint8_t cbor_info_data[] = { WIREDATA_CTAP_CBOR_INFO }; uint8_t *wiredata; fido_dev_t *dev = NULL; fido_dev_io_t io; memset(&io, 0, sizeof(io)); io.open = dummy_open; io.close = dummy_close; io.read = dummy_read; io.write = dummy_write; wiredata = wiredata_setup(cbor_info_data, sizeof(cbor_info_data)); assert((dev = fido_dev_new()) != NULL); assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_is_fido2(dev) == true); assert(fido_dev_supports_pin(dev) == true); fido_dev_force_u2f(dev); assert(fido_dev_is_fido2(dev) == false); assert(fido_dev_supports_pin(dev) == false); assert(fido_dev_close(dev) == FIDO_OK); wiredata_clear(&wiredata); wiredata = wiredata_setup(NULL, 0); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_is_fido2(dev) == false); assert(fido_dev_supports_pin(dev) == false); fido_dev_force_fido2(dev); assert(fido_dev_is_fido2(dev) == true); assert(fido_dev_supports_pin(dev) == false); assert(fido_dev_close(dev) == FIDO_OK); fido_dev_free(&dev); wiredata_clear(&wiredata); } static void has_pin(void) { const uint8_t set_pin_data[] = { WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_CBOR_AUTHKEY, WIREDATA_CTAP_CBOR_STATUS, WIREDATA_CTAP_CBOR_STATUS }; uint8_t *wiredata; fido_dev_t *dev = NULL; fido_dev_io_t io; memset(&io, 0, sizeof(io)); io.open = dummy_open; io.close = dummy_close; io.read = dummy_read; io.write = dummy_write; wiredata = wiredata_setup(set_pin_data, sizeof(set_pin_data)); assert((dev = fido_dev_new()) != NULL); assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_has_pin(dev) == false); assert(fido_dev_set_pin(dev, "top secret", NULL) == FIDO_OK); assert(fido_dev_has_pin(dev) == true); assert(fido_dev_reset(dev) == FIDO_OK); assert(fido_dev_has_pin(dev) == false); assert(fido_dev_close(dev) == FIDO_OK); fido_dev_free(&dev); wiredata_clear(&wiredata); } static void timeout_rx(void) { const uint8_t timeout_rx_data[] = { WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_CBOR_STATUS }; uint8_t *wiredata; fido_dev_t *dev = NULL; fido_dev_io_t io; memset(&io, 0, sizeof(io)); io.open = dummy_open; io.close = dummy_close; io.read = dummy_read; io.write = dummy_write; wiredata = wiredata_setup(timeout_rx_data, sizeof(timeout_rx_data)); assert((dev = fido_dev_new()) != NULL); assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_set_timeout(dev, 3 * 1000) == FIDO_OK); interval_ms = 1000; assert(fido_dev_reset(dev) == FIDO_ERR_RX); assert(fido_dev_close(dev) == FIDO_OK); fido_dev_free(&dev); wiredata_clear(&wiredata); interval_ms = 0; } static void timeout_ok(void) { const uint8_t timeout_ok_data[] = { WIREDATA_CTAP_CBOR_INFO, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_KEEPALIVE, WIREDATA_CTAP_CBOR_STATUS }; uint8_t *wiredata; fido_dev_t *dev = NULL; fido_dev_io_t io; memset(&io, 0, sizeof(io)); io.open = dummy_open; io.close = dummy_close; io.read = dummy_read; io.write = dummy_write; wiredata = wiredata_setup(timeout_ok_data, sizeof(timeout_ok_data)); assert((dev = fido_dev_new()) != NULL); assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK); assert(fido_dev_open(dev, "dummy") == FIDO_OK); assert(fido_dev_set_timeout(dev, 30 * 1000) == FIDO_OK); interval_ms = 1000; assert(fido_dev_reset(dev) == FIDO_OK); assert(fido_dev_close(dev) == FIDO_OK); fido_dev_free(&dev); wiredata_clear(&wiredata); interval_ms = 0; } static void timeout_misc(void) { fido_dev_t *dev; assert((dev = fido_dev_new()) != NULL); assert(fido_dev_set_timeout(dev, -2) == FIDO_ERR_INVALID_ARGUMENT); assert(fido_dev_set_timeout(dev, 3 * 1000) == FIDO_OK); assert(fido_dev_set_timeout(dev, -1) == FIDO_OK); fido_dev_free(&dev); } int main(void) { fido_init(0); open_iff_ok(); reopen(); double_open(); double_close(); is_fido2(); has_pin(); timeout_rx(); timeout_ok(); timeout_misc(); exit(0); } libfido2-1.10.0/src/000077500000000000000000000000001417126203300140515ustar00rootroot00000000000000libfido2-1.10.0/src/CMakeLists.txt000066400000000000000000000066731417126203300166250ustar00rootroot00000000000000# Copyright (c) 2018-2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. add_definitions(-D_FIDO_INTERNAL) list(APPEND FIDO_SOURCES aes256.c assert.c authkey.c bio.c blob.c buf.c cbor.c compress.c config.c cred.c credman.c dev.c ecdh.c eddsa.c err.c es256.c hid.c info.c io.c iso7816.c largeblob.c log.c pin.c random.c reset.c rs1.c rs256.c time.c tpm.c types.c u2f.c ) if(FUZZ) list(APPEND FIDO_SOURCES ../fuzz/clock.c) list(APPEND FIDO_SOURCES ../fuzz/prng.c) list(APPEND FIDO_SOURCES ../fuzz/uniform_random.c) list(APPEND FIDO_SOURCES ../fuzz/udev.c) list(APPEND FIDO_SOURCES ../fuzz/wrap.c) endif() if(NFC_LINUX) list(APPEND FIDO_SOURCES netlink.c nfc_linux.c) endif() if(USE_HIDAPI) list(APPEND FIDO_SOURCES hid_hidapi.c) if(NOT WIN32 AND NOT APPLE) list(APPEND FIDO_SOURCES hid_unix.c) endif() elseif(WIN32) list(APPEND FIDO_SOURCES hid_win.c) if(USE_WINHELLO) list(APPEND FIDO_SOURCES winhello.c) endif() elseif(APPLE) list(APPEND FIDO_SOURCES hid_osx.c) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") list(APPEND FIDO_SOURCES hid_linux.c hid_unix.c) elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") list(APPEND FIDO_SOURCES hid_netbsd.c hid_unix.c) elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") list(APPEND FIDO_SOURCES hid_openbsd.c hid_unix.c) elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") list(APPEND FIDO_SOURCES hid_freebsd.c hid_unix.c) else() message(FATAL_ERROR "please define a hid backend for your platform") endif() if(NOT MSVC) set_source_files_properties(${FIDO_SOURCES} PROPERTIES COMPILE_FLAGS "-Wconversion -Wsign-conversion") endif() list(APPEND COMPAT_SOURCES ../openbsd-compat/bsd-getpagesize.c ../openbsd-compat/clock_gettime.c ../openbsd-compat/endian_win32.c ../openbsd-compat/explicit_bzero.c ../openbsd-compat/explicit_bzero_win32.c ../openbsd-compat/freezero.c ../openbsd-compat/recallocarray.c ../openbsd-compat/strlcat.c ../openbsd-compat/timingsafe_bcmp.c ) if(WIN32) list(APPEND BASE_LIBRARIES wsock32 ws2_32 bcrypt setupapi hid) elseif(APPLE) list(APPEND BASE_LIBRARIES "-framework CoreFoundation" "-framework IOKit") endif() list(APPEND TARGET_LIBRARIES ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES} ${UDEV_LIBRARIES} ${BASE_LIBRARIES} ${HIDAPI_LIBRARIES} ${ZLIB_LIBRARIES} ) # static library if(BUILD_STATIC_LIBS) add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES}) if(WIN32 AND NOT MINGW) set_target_properties(fido2 PROPERTIES OUTPUT_NAME fido2_static) endif() target_link_libraries(fido2 ${TARGET_LIBRARIES}) install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() # dynamic library if(BUILD_SHARED_LIBS) add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2 VERSION ${FIDO_VERSION} SOVERSION ${FIDO_MAJOR}) target_link_libraries(fido2_shared ${TARGET_LIBRARIES}) install(TARGETS fido2_shared ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() install(FILES fido.h DESTINATION include) install(DIRECTORY fido DESTINATION include) if(NOT MSVC) configure_file(libfido2.pc.in libfido2.pc @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libfido2.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") endif() libfido2-1.10.0/src/aes256.c000066400000000000000000000126611417126203300152300ustar00rootroot00000000000000/* * Copyright (c) 2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" static int aes256_cbc(const fido_blob_t *key, const u_char *iv, const fido_blob_t *in, fido_blob_t *out, int encrypt) { EVP_CIPHER_CTX *ctx = NULL; const EVP_CIPHER *cipher; int ok = -1; memset(out, 0, sizeof(*out)); if (key->len != 32) { fido_log_debug("%s: invalid key len %zu", __func__, key->len); goto fail; } if (in->len > UINT_MAX || in->len % 16 || in->len == 0) { fido_log_debug("%s: invalid input len %zu", __func__, in->len); goto fail; } out->len = in->len; if ((out->ptr = calloc(1, out->len)) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } if ((ctx = EVP_CIPHER_CTX_new()) == NULL || (cipher = EVP_aes_256_cbc()) == NULL) { fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__); goto fail; } if (EVP_CipherInit(ctx, cipher, key->ptr, iv, encrypt) == 0 || EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)out->len) < 0) { fido_log_debug("%s: EVP_Cipher", __func__); goto fail; } ok = 0; fail: if (ctx != NULL) EVP_CIPHER_CTX_free(ctx); if (ok < 0) fido_blob_reset(out); return ok; } static int aes256_cbc_proto1(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out, int encrypt) { u_char iv[16]; memset(&iv, 0, sizeof(iv)); return aes256_cbc(key, iv, in, out, encrypt); } static int aes256_cbc_fips(const fido_blob_t *secret, const fido_blob_t *in, fido_blob_t *out, int encrypt) { fido_blob_t key, cin, cout; u_char iv[16]; memset(out, 0, sizeof(*out)); if (secret->len != 64) { fido_log_debug("%s: invalid secret len %zu", __func__, secret->len); return -1; } if (in->len < sizeof(iv)) { fido_log_debug("%s: invalid input len %zu", __func__, in->len); return -1; } if (encrypt) { if (fido_get_random(iv, sizeof(iv)) < 0) { fido_log_debug("%s: fido_get_random", __func__); return -1; } cin = *in; } else { memcpy(iv, in->ptr, sizeof(iv)); cin.ptr = in->ptr + sizeof(iv); cin.len = in->len - sizeof(iv); } key.ptr = secret->ptr + 32; key.len = secret->len - 32; if (aes256_cbc(&key, iv, &cin, &cout, encrypt) < 0) return -1; if (encrypt) { if (cout.len > SIZE_MAX - sizeof(iv) || (out->ptr = calloc(1, sizeof(iv) + cout.len)) == NULL) { fido_blob_reset(&cout); return -1; } out->len = sizeof(iv) + cout.len; memcpy(out->ptr, iv, sizeof(iv)); memcpy(out->ptr + sizeof(iv), cout.ptr, cout.len); fido_blob_reset(&cout); } else *out = cout; return 0; } static int aes256_gcm(const fido_blob_t *key, const fido_blob_t *nonce, const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out, int encrypt) { EVP_CIPHER_CTX *ctx = NULL; const EVP_CIPHER *cipher; size_t textlen; int ok = -1; memset(out, 0, sizeof(*out)); if (nonce->len != 12 || key->len != 32 || aad->len > UINT_MAX) { fido_log_debug("%s: invalid params %zu, %zu, %zu", __func__, nonce->len, key->len, aad->len); goto fail; } if (in->len > UINT_MAX || in->len > SIZE_MAX - 16 || in->len < 16) { fido_log_debug("%s: invalid input len %zu", __func__, in->len); goto fail; } /* add tag to (on encrypt) or trim tag from the output (on decrypt) */ out->len = encrypt ? in->len + 16 : in->len - 16; if ((out->ptr = calloc(1, out->len)) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } if ((ctx = EVP_CIPHER_CTX_new()) == NULL || (cipher = EVP_aes_256_gcm()) == NULL) { fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__); goto fail; } if (EVP_CipherInit(ctx, cipher, key->ptr, nonce->ptr, encrypt) == 0) { fido_log_debug("%s: EVP_CipherInit", __func__); goto fail; } if (encrypt) textlen = in->len; else { textlen = in->len - 16; /* point openssl at the mac tag */ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, in->ptr + in->len - 16) == 0) { fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__); goto fail; } } /* the last EVP_Cipher() will either compute or verify the mac tag */ if (EVP_Cipher(ctx, NULL, aad->ptr, (u_int)aad->len) < 0 || EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)textlen) < 0 || EVP_Cipher(ctx, NULL, NULL, 0) < 0) { fido_log_debug("%s: EVP_Cipher", __func__); goto fail; } if (encrypt) { /* append the mac tag */ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, out->ptr + out->len - 16) == 0) { fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__); goto fail; } } ok = 0; fail: if (ctx != NULL) EVP_CIPHER_CTX_free(ctx); if (ok < 0) fido_blob_reset(out); return ok; } int aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *secret, const fido_blob_t *in, fido_blob_t *out) { return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret, in, out, 1) : aes256_cbc_proto1(secret, in, out, 1); } int aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *secret, const fido_blob_t *in, fido_blob_t *out) { return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret, in, out, 0) : aes256_cbc_proto1(secret, in, out, 0); } int aes256_gcm_enc(const fido_blob_t *key, const fido_blob_t *nonce, const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out) { return aes256_gcm(key, nonce, aad, in, out, 1); } int aes256_gcm_dec(const fido_blob_t *key, const fido_blob_t *nonce, const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out) { return aes256_gcm(key, nonce, aad, in, out, 0); } libfido2-1.10.0/src/assert.c000066400000000000000000000566051417126203300155320ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #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)); case 3: /* signature */ return (fido_blob_decode(val, &stmt->sig)); case 4: /* user attributes */ return (cbor_decode_user(val, &stmt->user)); case 7: /* large blob key */ return (fido_blob_decode(val, &stmt->largeblob_key)); 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, int *ms) { fido_blob_t f; fido_opt_t uv = assert->uv; cbor_item_t *argv[7]; const uint8_t cmd = CTAP_CBOR_ASSERT; 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; } } if (assert->ext.mask) if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh, pk)) == NULL) { fido_log_debug("%s: cbor_encode_assert_ext", __func__); r = FIDO_ERR_INTERNAL; goto fail; } /* user verification */ if (pin != NULL || (uv == FIDO_OPT_TRUE && fido_dev_supports_permissions(dev))) { if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh, pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) { fido_log_debug("%s: cbor_add_uv_params", __func__); goto fail; } uv = FIDO_OPT_OMIT; } /* options */ if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT) if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) { fido_log_debug("%s: cbor_encode_assert_opt", __func__); r = FIDO_ERR_INTERNAL; goto fail; } /* frame and transmit */ if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; fido_assert_reset_rx(assert); if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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, int *ms) { const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT }; if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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, ms)) != 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, ms)) != 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(const fido_dev_t *dev, 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->authdata_ext.hmac_secret_enc.ptr != NULL) { if (aes256_cbc_dec(dev, key, &stmt->authdata_ext.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 ms = dev->timeout_ms; int r; #ifdef USE_WINHELLO if (dev->flags & FIDO_DEV_WINHELLO) return (fido_winhello_get_assert(dev, assert, pin, ms)); #endif 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.mask != 0) return (FIDO_ERR_UNSUPPORTED_OPTION); return (u2f_authenticate(dev, assert, &ms)); } if (pin != NULL || (assert->uv == FIDO_OPT_TRUE && fido_dev_supports_permissions(dev)) || (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) { if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } } r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms); if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) if (decrypt_hmac_secrets(dev, 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) { /* XXX: largeBlobKey is not part of extensions map */ ext &= ~FIDO_EXT_LARGEBLOB_KEY; 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_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; const EVP_MD *md = NULL; EVP_MD_CTX *ctx = NULL; 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 || (md = EVP_sha256()) == NULL || (ctx = EVP_MD_CTX_new()) == NULL || EVP_DigestInit_ex(ctx, md, NULL) != 1 || EVP_DigestUpdate(ctx, authdata_ptr, authdata_len) != 1 || EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { 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); EVP_MD_CTX_free(ctx); return (ok); } int fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg, const void *pk) { unsigned char buf[1024]; /* XXX */ 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.mask, assert->ext.mask) < 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 (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh, &stmt->authdata_cbor) < 0) { fido_log_debug("%s: fido_get_signed_hash", __func__); r = FIDO_ERR_INTERNAL; goto out; } switch (cose_alg) { case COSE_ES256: ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig); break; case COSE_RS256: ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig); break; case COSE_EDDSA: ok = eddsa_pk_verify_sig(&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(fido_assert_t *assert, const unsigned char *data, size_t data_len) { if (!fido_blob_is_empty(&assert->cdh) || fido_blob_set(&assert->cd, data, data_len) < 0) { return (FIDO_ERR_INVALID_ARGUMENT); } if (fido_sha256(&assert->cdh, data, data_len) < 0) { fido_blob_reset(&assert->cd); return (FIDO_ERR_INTERNAL); } return (FIDO_OK); } int fido_assert_set_clientdata_hash(fido_assert_t *assert, const unsigned char *hash, size_t hash_len) { if (!fido_blob_is_empty(&assert->cd) || 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->ext.hmac_salt, salt, salt_len) < 0) return (FIDO_ERR_INVALID_ARGUMENT); return (FIDO_OK); } int fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx, const unsigned char *secret, size_t secret_len) { if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) || fido_blob_set(&assert->stmt[idx].hmac_secret, secret, secret_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) assert->ext.mask = 0; else { if ((ext & FIDO_EXT_ASSERT_MASK) != ext) return (FIDO_ERR_INVALID_ARGUMENT); assert->ext.mask |= 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); fido_blob_reset(&assert->cd); fido_blob_reset(&assert->cdh); fido_blob_reset(&assert->ext.hmac_salt); fido_free_blob_array(&assert->allow_list); memset(&assert->ext, 0, sizeof(assert->ext)); memset(&assert->allow_list, 0, sizeof(assert->allow_list)); assert->rp_id = NULL; assert->up = FIDO_OPT_OMIT; assert->uv = FIDO_OPT_OMIT; } static void fido_assert_reset_extattr(fido_assert_extattr_t *ext) { fido_blob_reset(&ext->hmac_secret_enc); fido_blob_reset(&ext->blob); memset(ext, 0, sizeof(*ext)); } void fido_assert_reset_rx(fido_assert_t *assert) { for (size_t i = 0; i < assert->stmt_cnt; i++) { free(assert->stmt[i].user.icon); free(assert->stmt[i].user.name); free(assert->stmt[i].user.display_name); fido_blob_reset(&assert->stmt[i].user.id); fido_blob_reset(&assert->stmt[i].id); fido_blob_reset(&assert->stmt[i].hmac_secret); fido_blob_reset(&assert->stmt[i].authdata_cbor); fido_blob_reset(&assert->stmt[i].largeblob_key); fido_blob_reset(&assert->stmt[i].sig); fido_assert_reset_extattr(&assert->stmt[i].authdata_ext); 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); } const unsigned char * fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx) { if (idx >= assert->stmt_len) return (NULL); return (assert->stmt[idx].largeblob_key.ptr); } size_t fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx) { if (idx >= assert->stmt_len) return (0); return (assert->stmt[idx].largeblob_key.len); } const unsigned char * fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx) { if (idx >= assert->stmt_len) return (NULL); return (assert->stmt[idx].authdata_ext.blob.ptr); } size_t fido_assert_blob_len(const fido_assert_t *assert, size_t idx) { if (idx >= assert->stmt_len) return (0); return (assert->stmt[idx].authdata_ext.blob.len); } static void fido_assert_clean_authdata(fido_assert_stmt *stmt) { fido_blob_reset(&stmt->authdata_cbor); fido_assert_reset_extattr(&stmt->authdata_ext); memset(&stmt->authdata, 0, sizeof(stmt->authdata)); } 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) < 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) < 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_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr, size_t len) { if (idx >= a->stmt_len || ptr == NULL || len == 0) return (FIDO_ERR_INVALID_ARGUMENT); if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0) return (FIDO_ERR_INTERNAL); 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); } libfido2-1.10.0/src/authkey.c000066400000000000000000000042161417126203300156720ustar00rootroot00000000000000/* * Copyright (c) 2018 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" 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, int *ms) { 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_encode_pin_opt(dev)) == 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, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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) { unsigned char reply[FIDO_MAXMSG]; 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, CTAP_CMD_CBOR, &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, ms)) != 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, int *ms) { return (fido_dev_authkey_wait(dev, authkey, ms)); } libfido2-1.10.0/src/bio.c000066400000000000000000000432551417126203300147770ustar00rootroot00000000000000/* * 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 "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 subcmd, cbor_item_t **sub_argv, size_t sub_argc, const char *pin, const fido_blob_t *token, int *ms) { cbor_item_t *argv[5]; es256_pk_t *pk = NULL; fido_blob_t *ecdh = NULL; fido_blob_t f; fido_blob_t hmac; const uint8_t cmd = CTAP_CBOR_BIO_ENROLL_PRE; 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(subcmd)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } /* subParams */ if (pin || token) { if (bio_prepare_hmac(subcmd, 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, ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin, NULL, &argv[4], &argv[3], ms)) != FIDO_OK) { fido_log_debug("%s: cbor_add_uv_params", __func__); goto fail; } } else if (token) { if ((argv[3] = cbor_encode_pin_opt(dev)) == NULL || (argv[4] = cbor_encode_pin_auth(dev, token, &hmac)) == NULL) { fido_log_debug("%s: encode pin", __func__); goto fail; } } /* framing and transmission */ if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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); t->name = NULL; fido_blob_reset(&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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; bio_reset_template_array(ta); if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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, ms)) != 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) { int ms = dev->timeout_ms; if (pin == NULL) return (FIDO_ERR_INVALID_ARGUMENT); return (bio_get_template_array_wait(dev, ta, pin, &ms)); } 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, ms)) != 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) { int ms = dev->timeout_ms; if (pin == NULL || t->name == NULL) return (FIDO_ERR_INVALID_ARGUMENT); return (bio_set_template_name_wait(dev, t, pin, &ms)); } 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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; bio_reset_template(t); e->remaining_samples = 0; e->last_status = 0; if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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_uint(timo_ms)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != 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 ms = dev->timeout_ms; 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, &ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh, pk, NULL, token, &ms)) != FIDO_OK) { fido_log_debug("%s: fido_dev_get_uv_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, &ms)); } static int bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int *ms) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; e->remaining_samples = 0; e->last_status = 0; if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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_uint(timo_ms)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != 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) { int ms = dev->timeout_ms; if (e->token == NULL) return (FIDO_ERR_INVALID_ARGUMENT); return (bio_enroll_continue_wait(dev, t, e, timo_ms, &ms)); } 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, ms)) != 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) { int ms = dev->timeout_ms; return (bio_enroll_cancel_wait(dev, &ms)); } 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, ms)) != 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) { int ms = dev->timeout_ms; return (bio_enroll_remove_wait(dev, t, pin, &ms)); } 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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; bio_reset_info(i); if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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, ms)) != 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) { int ms = dev->timeout_ms; return (bio_get_info_wait(dev, i, &ms)); } 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) { fido_blob_reset(&t->id); 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); } libfido2-1.10.0/src/blob.c000066400000000000000000000043431417126203300151370ustar00rootroot00000000000000/* * Copyright (c) 2018 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" fido_blob_t * fido_blob_new(void) { return calloc(1, sizeof(fido_blob_t)); } void fido_blob_reset(fido_blob_t *b) { freezero(b->ptr, b->len); explicit_bzero(b, sizeof(*b)); } int fido_blob_set(fido_blob_t *b, const u_char *ptr, size_t len) { fido_blob_reset(b); 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; } int fido_blob_append(fido_blob_t *b, const u_char *ptr, size_t len) { u_char *tmp; if (ptr == NULL || len == 0) { fido_log_debug("%s: ptr=%p, len=%zu", __func__, (const void *)ptr, len); return -1; } if (SIZE_MAX - b->len < len) { fido_log_debug("%s: overflow", __func__); return -1; } if ((tmp = realloc(b->ptr, b->len + len)) == NULL) { fido_log_debug("%s: realloc", __func__); return -1; } b->ptr = tmp; memcpy(&b->ptr[b->len], 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; fido_blob_reset(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]; freezero(b->ptr, b->len); 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; } int fido_blob_serialise(fido_blob_t *b, const cbor_item_t *item) { size_t alloc; if (!fido_blob_is_empty(b)) return -1; if ((b->len = cbor_serialize_alloc(item, &b->ptr, &alloc)) == 0) { b->ptr = NULL; return -1; } return 0; } libfido2-1.10.0/src/blob.h000066400000000000000000000017251417126203300151450ustar00rootroot00000000000000/* * 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 #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ 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 u_char *, size_t); int fido_blob_append(fido_blob_t *, const u_char *, size_t); void fido_blob_free(fido_blob_t **); void fido_blob_reset(fido_blob_t *); void fido_free_blob_array(fido_blob_array_t *); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_BLOB_H */ libfido2-1.10.0/src/buf.c000066400000000000000000000011011417126203300147620ustar00rootroot00000000000000/* * Copyright (c) 2018 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" 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); } libfido2-1.10.0/src/cbor.c000066400000000000000000001102171417126203300151440ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #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_uint8(cbor_item_t *item, const char *key, uint8_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_uint8(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); } 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, (uint8_t)(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_str_array(const fido_str_array_t *a) { cbor_item_t *array = NULL; cbor_item_t *entry = NULL; if ((array = cbor_new_definite_array(a->len)) == NULL) goto fail; for (size_t i = 0; i < a->len; i++) { if ((entry = cbor_build_string(a->ptr[i])) == NULL || cbor_array_push(array, entry) == false) goto fail; cbor_decref(&entry); } return (array); fail: if (entry != NULL) cbor_decref(&entry); if (array != NULL) cbor_decref(&array); return (NULL); } static int cbor_encode_largeblob_key_ext(cbor_item_t *map) { if (map == NULL || cbor_add_bool(map, "largeBlobKey", FIDO_OPT_TRUE) < 0) return (-1); return (0); } cbor_item_t * cbor_encode_cred_ext(const fido_cred_ext_t *ext, const fido_blob_t *blob) { cbor_item_t *item = NULL; size_t size = 0; if (ext->mask & FIDO_EXT_CRED_BLOB) size++; if (ext->mask & FIDO_EXT_HMAC_SECRET) size++; if (ext->mask & FIDO_EXT_CRED_PROTECT) size++; if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) size++; if (ext->mask & FIDO_EXT_MINPINLEN) size++; if (size == 0 || (item = cbor_new_definite_map(size)) == NULL) return (NULL); if (ext->mask & FIDO_EXT_CRED_BLOB) { if (cbor_add_bytestring(item, "credBlob", blob->ptr, blob->len) < 0) { cbor_decref(&item); return (NULL); } } if (ext->mask & FIDO_EXT_CRED_PROTECT) { if (ext->prot < 0 || ext->prot > UINT8_MAX || cbor_add_uint8(item, "credProtect", (uint8_t)ext->prot) < 0) { cbor_decref(&item); return (NULL); } } if (ext->mask & FIDO_EXT_HMAC_SECRET) { if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) { cbor_decref(&item); return (NULL); } } if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) { if (cbor_encode_largeblob_key_ext(item) < 0) { cbor_decref(&item); return (NULL); } } if (ext->mask & FIDO_EXT_MINPINLEN) { if (cbor_add_bool(item, "minPinLength", FIDO_OPT_TRUE) < 0) { cbor_decref(&item); return (NULL); } } return (item); } cbor_item_t * cbor_encode_cred_opt(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_opt(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_dev_t *dev, const fido_blob_t *secret, const fido_blob_t *data) { const EVP_MD *md = NULL; unsigned char dgst[SHA256_DIGEST_LENGTH]; unsigned int dgst_len; size_t outlen; uint8_t prot; fido_blob_t key; key.ptr = secret->ptr; key.len = secret->len; if ((prot = fido_dev_get_pin_protocol(dev)) == 0) { fido_log_debug("%s: fido_dev_get_pin_protocol", __func__); return (NULL); } /* select hmac portion of the shared secret */ if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32) key.len = 32; if ((md = EVP_sha256()) == NULL || HMAC(md, key.ptr, (int)key.len, data->ptr, data->len, dgst, &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH) return (NULL); outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len; return (cbor_build_bytestring(dgst, outlen)); } cbor_item_t * cbor_encode_pin_opt(const fido_dev_t *dev) { uint8_t prot; if ((prot = fido_dev_get_pin_protocol(dev)) == 0) { fido_log_debug("%s: fido_dev_get_pin_protocol", __func__); return (NULL); } return (cbor_build_uint8(prot)); } cbor_item_t * cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret, const fido_blob_t *new_pin_enc, const fido_blob_t *pin_hash_enc) { unsigned char dgst[SHA256_DIGEST_LENGTH]; unsigned int dgst_len; cbor_item_t *item = NULL; const EVP_MD *md = NULL; HMAC_CTX *ctx = NULL; fido_blob_t key; uint8_t prot; size_t outlen; key.ptr = secret->ptr; key.len = secret->len; if ((prot = fido_dev_get_pin_protocol(dev)) == 0) { fido_log_debug("%s: fido_dev_get_pin_protocol", __func__); goto fail; } if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32) key.len = 32; 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, new_pin_enc->ptr, new_pin_enc->len) == 0 || HMAC_Update(ctx, pin_hash_enc->ptr, pin_hash_enc->len) == 0 || HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != SHA256_DIGEST_LENGTH) { fido_log_debug("%s: HMAC", __func__); goto fail; } outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len; if ((item = cbor_build_bytestring(dgst, outlen)) == NULL) { fido_log_debug("%s: cbor_build_bytestring", __func__); goto fail; } fail: HMAC_CTX_free(ctx); return (item); } static int cbor_encode_hmac_secret_param(const fido_dev_t *dev, cbor_item_t *item, const fido_blob_t *ecdh, const es256_pk_t *pk, const fido_blob_t *salt) { cbor_item_t *param = NULL; cbor_item_t *argv[4]; struct cbor_pair pair; fido_blob_t *enc = NULL; uint8_t prot; int r; memset(argv, 0, sizeof(argv)); memset(&pair, 0, sizeof(pair)); if (item == NULL || ecdh == NULL || pk == NULL || salt->ptr == NULL) { fido_log_debug("%s: ecdh=%p, pk=%p, salt->ptr=%p", __func__, (const void *)ecdh, (const void *)pk, (const void *)salt->ptr); r = FIDO_ERR_INTERNAL; goto fail; } if (salt->len != 32 && salt->len != 64) { fido_log_debug("%s: salt->len=%zu", __func__, salt->len); r = FIDO_ERR_INTERNAL; goto fail; } if ((enc = fido_blob_new()) == NULL || aes256_cbc_enc(dev, ecdh, salt, enc) < 0) { fido_log_debug("%s: aes256_cbc_enc", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if ((prot = fido_dev_get_pin_protocol(dev)) == 0) { fido_log_debug("%s: fido_dev_get_pin_protocol", __func__); r = FIDO_ERR_INTERNAL; goto fail; } /* XXX not pin, but salt */ if ((argv[0] = es256_pk_encode(pk, 1)) == NULL || (argv[1] = fido_blob_encode(enc)) == NULL || (argv[2] = cbor_encode_pin_auth(dev, ecdh, enc)) == NULL || (prot != 1 && (argv[3] = cbor_build_uint8(prot)) == NULL)) { fido_log_debug("%s: cbor encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if ((param = cbor_flatten_vector(argv, nitems(argv))) == NULL) { fido_log_debug("%s: cbor_flatten_vector", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if ((pair.key = cbor_build_string("hmac-secret")) == NULL) { fido_log_debug("%s: cbor_build", __func__); r = FIDO_ERR_INTERNAL; goto fail; } pair.value = param; if (!cbor_map_add(item, pair)) { fido_log_debug("%s: cbor_map_add", __func__); r = FIDO_ERR_INTERNAL; goto fail; } r = FIDO_OK; fail: cbor_vector_free(argv, nitems(argv)); if (param != NULL) cbor_decref(¶m); if (pair.key != NULL) cbor_decref(&pair.key); fido_blob_free(&enc); return (r); } cbor_item_t * cbor_encode_assert_ext(fido_dev_t *dev, const fido_assert_ext_t *ext, const fido_blob_t *ecdh, const es256_pk_t *pk) { cbor_item_t *item = NULL; size_t size = 0; if (ext->mask & FIDO_EXT_CRED_BLOB) size++; if (ext->mask & FIDO_EXT_HMAC_SECRET) size++; if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) size++; if (size == 0 || (item = cbor_new_definite_map(size)) == NULL) return (NULL); if (ext->mask & FIDO_EXT_CRED_BLOB) { if (cbor_add_bool(item, "credBlob", FIDO_OPT_TRUE) < 0) { cbor_decref(&item); return (NULL); } } if (ext->mask & FIDO_EXT_HMAC_SECRET) { if (cbor_encode_hmac_secret_param(dev, item, ecdh, pk, &ext->hmac_salt) < 0) { cbor_decref(&item); return (NULL); } } if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) { if (cbor_encode_largeblob_key_ext(item) < 0) { cbor_decref(&item); return (NULL); } } 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") && strcmp(type, "none") && strcmp(type, "tpm")) { 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_xxd(*buf, *len, "%s", __func__); 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__); 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_cred_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg) { fido_cred_ext_t *authdata_ext = arg; char *type = NULL; int ok = -1; if (cbor_string_copy(key, &type) < 0) { fido_log_debug("%s: cbor type", __func__); ok = 0; /* ignore */ goto out; } if (strcmp(type, "hmac-secret") == 0) { 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__); goto out; } if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE) authdata_ext->mask |= FIDO_EXT_HMAC_SECRET; } else if (strcmp(type, "credProtect") == 0) { if (cbor_isa_uint(val) == false || cbor_int_get_width(val) != CBOR_INT_8) { fido_log_debug("%s: cbor type", __func__); goto out; } authdata_ext->mask |= FIDO_EXT_CRED_PROTECT; authdata_ext->prot = cbor_get_uint8(val); } else if (strcmp(type, "credBlob") == 0) { 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__); goto out; } if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE) authdata_ext->mask |= FIDO_EXT_CRED_BLOB; } else if (strcmp(type, "minPinLength") == 0) { if (cbor_isa_uint(val) == false || cbor_int_get_width(val) != CBOR_INT_8) { fido_log_debug("%s: cbor type", __func__); goto out; } authdata_ext->mask |= FIDO_EXT_MINPINLEN; authdata_ext->minpinlen = cbor_get_uint8(val); } ok = 0; out: free(type); return (ok); } static int decode_cred_extensions(const unsigned char **buf, size_t *len, fido_cred_ext_t *authdata_ext) { cbor_item_t *item = NULL; struct cbor_load_result cbor; int ok = -1; memset(authdata_ext, 0, sizeof(*authdata_ext)); fido_log_xxd(*buf, *len, "%s", __func__); if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { fido_log_debug("%s: cbor_load", __func__); goto fail; } if (cbor_isa_map(item) == false || cbor_map_is_definite(item) == false || cbor_map_iter(item, authdata_ext, decode_cred_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_assert_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg) { fido_assert_extattr_t *authdata_ext = arg; char *type = NULL; int ok = -1; if (cbor_string_copy(key, &type) < 0) { fido_log_debug("%s: cbor type", __func__); ok = 0; /* ignore */ goto out; } if (strcmp(type, "hmac-secret") == 0) { if (fido_blob_decode(val, &authdata_ext->hmac_secret_enc) < 0) { fido_log_debug("%s: fido_blob_decode", __func__); goto out; } authdata_ext->mask |= FIDO_EXT_HMAC_SECRET; } else if (strcmp(type, "credBlob") == 0) { if (fido_blob_decode(val, &authdata_ext->blob) < 0) { fido_log_debug("%s: fido_blob_decode", __func__); goto out; } authdata_ext->mask |= FIDO_EXT_CRED_BLOB; } ok = 0; out: free(type); return (ok); } static int decode_assert_extensions(const unsigned char **buf, size_t *len, fido_assert_extattr_t *authdata_ext) { cbor_item_t *item = NULL; struct cbor_load_result cbor; int ok = -1; fido_log_xxd(*buf, *len, "%s", __func__); if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { fido_log_debug("%s: cbor_load", __func__); goto fail; } if (cbor_isa_map(item) == false || cbor_map_is_definite(item) == false || cbor_map_iter(item, authdata_ext, decode_assert_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); } 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, fido_cred_ext_t *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_xxd(buf, len, "%s", __func__); 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_cred_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, fido_assert_extattr_t *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 ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) { if (decode_assert_extensions(&buf, &len, authdata_ext) < 0) { fido_log_debug("%s: decode_assert_extensions", __func__); return (-1); } } /* 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 (fido_blob_decode(item, x5c)); } 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_get_int(val) > UINT16_MAX) { fido_log_debug("%s: alg", __func__); goto out; } attstmt->alg = -(int)cbor_get_int(val) - 1; if (attstmt->alg != COSE_ES256 && attstmt->alg != COSE_RS256 && attstmt->alg != COSE_EDDSA && attstmt->alg != COSE_RS1) { fido_log_debug("%s: unsupported attstmt->alg=%d", __func__, attstmt->alg); goto out; } } else if (!strcmp(name, "sig")) { if (fido_blob_decode(val, &attstmt->sig) < 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; } } else if (!strcmp(name, "certInfo")) { if (fido_blob_decode(val, &attstmt->certinfo) < 0) { fido_log_debug("%s: certinfo", __func__); goto out; } } else if (!strcmp(name, "pubArea")) { if (fido_blob_decode(val, &attstmt->pubarea) < 0) { fido_log_debug("%s: pubarea", __func__); goto out; } } ok = 0; out: free(name); return (ok); } int cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt) { size_t alloc_len; 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); } if (attstmt->cbor.ptr != NULL || (attstmt->cbor.len = cbor_serialize_alloc(item, &attstmt->cbor.ptr, &alloc_len)) == 0) { fido_log_debug("%s: cbor_serialize_alloc", __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 (fido_blob_decode(val, id) < 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 (fido_blob_decode(val, &user->id) < 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); } cbor_item_t * cbor_build_uint(const uint64_t value) { if (value <= UINT8_MAX) return cbor_build_uint8((uint8_t)value); else if (value <= UINT16_MAX) return cbor_build_uint16((uint16_t)value); else if (value <= UINT32_MAX) return cbor_build_uint32((uint32_t)value); return cbor_build_uint64(value); } int cbor_array_append(cbor_item_t **array, cbor_item_t *item) { cbor_item_t **v, *ret; size_t n; if ((v = cbor_array_handle(*array)) == NULL || (n = cbor_array_size(*array)) == SIZE_MAX || (ret = cbor_new_definite_array(n + 1)) == NULL) return -1; for (size_t i = 0; i < n; i++) { if (cbor_array_push(ret, v[i]) == 0) { cbor_decref(&ret); return -1; } } if (cbor_array_push(ret, item) == 0) { cbor_decref(&ret); return -1; } cbor_decref(array); *array = ret; return 0; } int cbor_array_drop(cbor_item_t **array, size_t idx) { cbor_item_t **v, *ret; size_t n; if ((v = cbor_array_handle(*array)) == NULL || (n = cbor_array_size(*array)) == 0 || idx >= n || (ret = cbor_new_definite_array(n - 1)) == NULL) return -1; for (size_t i = 0; i < n; i++) { if (i != idx && cbor_array_push(ret, v[i]) == 0) { cbor_decref(&ret); return -1; } } cbor_decref(array); *array = ret; return 0; } libfido2-1.10.0/src/compress.c000066400000000000000000000022171417126203300160520ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include "fido.h" #define BOUND (1024UL * 1024UL) static int do_compress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz, int decomp) { u_long ilen, olen; int r; memset(out, 0, sizeof(*out)); if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND || origsiz > ULONG_MAX || (olen = decomp ? (u_long)origsiz : compressBound(ilen)) > BOUND) return FIDO_ERR_INVALID_ARGUMENT; if ((out->ptr = calloc(1, olen)) == NULL) return FIDO_ERR_INTERNAL; out->len = olen; if (decomp) r = uncompress(out->ptr, &olen, in->ptr, ilen); else r = compress(out->ptr, &olen, in->ptr, ilen); if (r != Z_OK || olen > SIZE_MAX || olen > out->len) { fido_blob_reset(out); return FIDO_ERR_COMPRESS; } out->len = olen; return FIDO_OK; } int fido_compress(fido_blob_t *out, const fido_blob_t *in) { return do_compress(out, in, 0, 0); } int fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz) { return do_compress(out, in, origsiz, 1); } libfido2-1.10.0/src/config.c000066400000000000000000000124661417126203300154730ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" #include "fido/config.h" #include "fido/es256.h" #define CMD_ENABLE_ENTATTEST 0x01 #define CMD_TOGGLE_ALWAYS_UV 0x02 #define CMD_SET_PIN_MINLEN 0x03 static int config_prepare_hmac(uint8_t subcmd, const cbor_item_t *item, fido_blob_t *hmac) { uint8_t prefix[32 + 2 * sizeof(uint8_t)], cbor[128]; size_t cbor_len; memset(prefix, 0xff, sizeof(prefix)); prefix[sizeof(prefix) - 2] = CTAP_CBOR_CONFIG; prefix[sizeof(prefix) - 1] = subcmd; if ((cbor_len = cbor_serialize(item, cbor, sizeof(cbor))) == 0) { fido_log_debug("%s: cbor_serialize", __func__); return -1; } if ((hmac->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) { fido_log_debug("%s: malloc", __func__); return -1; } memcpy(hmac->ptr, prefix, sizeof(prefix)); memcpy(hmac->ptr + sizeof(prefix), cbor, cbor_len); hmac->len = cbor_len + sizeof(prefix); return 0; } static int config_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **paramv, size_t paramc, const char *pin, int *ms) { cbor_item_t *argv[4]; es256_pk_t *pk = NULL; fido_blob_t *ecdh = NULL, f, hmac; const uint8_t cmd = CTAP_CBOR_CONFIG; 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(subcmd)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } /* pinProtocol, pinAuth */ if (pin != NULL || (fido_dev_supports_permissions(dev) && fido_dev_has_uv(dev))) { if ((argv[1] = cbor_flatten_vector(paramv, paramc)) == NULL) { fido_log_debug("%s: cbor_flatten_vector", __func__); goto fail; } if (config_prepare_hmac(subcmd, argv[1], &hmac) < 0) { fido_log_debug("%s: config_prepare_hmac", __func__); goto fail; } if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin, NULL, &argv[3], &argv[2], ms)) != FIDO_OK) { fido_log_debug("%s: cbor_add_uv_params", __func__); goto fail; } } /* framing and transmission */ if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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 int config_enable_entattest_wait(fido_dev_t *dev, const char *pin, int *ms) { int r; if ((r = config_tx(dev, CMD_ENABLE_ENTATTEST, NULL, 0, pin, ms)) != FIDO_OK) return r; return fido_rx_cbor_status(dev, ms); } int fido_dev_enable_entattest(fido_dev_t *dev, const char *pin) { int ms = dev->timeout_ms; return (config_enable_entattest_wait(dev, pin, &ms)); } static int config_toggle_always_uv_wait(fido_dev_t *dev, const char *pin, int *ms) { int r; if ((r = config_tx(dev, CMD_TOGGLE_ALWAYS_UV, NULL, 0, pin, ms)) != FIDO_OK) return r; return (fido_rx_cbor_status(dev, ms)); } int fido_dev_toggle_always_uv(fido_dev_t *dev, const char *pin) { int ms = dev->timeout_ms; return config_toggle_always_uv_wait(dev, pin, &ms); } static int config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force, const fido_str_array_t *rpid, const char *pin, int *ms) { cbor_item_t *argv[3]; int r; memset(argv, 0, sizeof(argv)); if ((rpid == NULL && len == 0 && !force) || len > UINT8_MAX) { r = FIDO_ERR_INVALID_ARGUMENT; goto fail; } if (len && (argv[0] = cbor_build_uint8((uint8_t)len)) == NULL) { fido_log_debug("%s: cbor_encode_uint8", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (rpid != NULL && (argv[1] = cbor_encode_str_array(rpid)) == NULL) { fido_log_debug("%s: cbor_encode_str_array", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (force && (argv[2] = cbor_build_bool(true)) == NULL) { fido_log_debug("%s: cbor_build_bool", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if ((r = config_tx(dev, CMD_SET_PIN_MINLEN, argv, nitems(argv), pin, ms)) != FIDO_OK) { fido_log_debug("%s: config_tx", __func__); goto fail; } fail: cbor_vector_free(argv, nitems(argv)); return r; } static int config_pin_minlen(fido_dev_t *dev, size_t len, bool force, const fido_str_array_t *rpid, const char *pin, int *ms) { int r; if ((r = config_pin_minlen_tx(dev, len, force, rpid, pin, ms)) != FIDO_OK) return r; return fido_rx_cbor_status(dev, ms); } int fido_dev_set_pin_minlen(fido_dev_t *dev, size_t len, const char *pin) { int ms = dev->timeout_ms; return config_pin_minlen(dev, len, false, NULL, pin, &ms); } int fido_dev_force_pin_change(fido_dev_t *dev, const char *pin) { int ms = dev->timeout_ms; return config_pin_minlen(dev, 0, true, NULL, pin, &ms); } int fido_dev_set_pin_minlen_rpid(fido_dev_t *dev, const char * const *rpid, size_t n, const char *pin) { fido_str_array_t sa; int ms = dev->timeout_ms; int r; memset(&sa, 0, sizeof(sa)); if (fido_str_array_pack(&sa, rpid, n) < 0) { fido_log_debug("%s: fido_str_array_pack", __func__); r = FIDO_ERR_INTERNAL; goto fail; } r = config_pin_minlen(dev, 0, false, &sa, pin, &ms); fail: fido_str_array_free(&sa); return r; } libfido2-1.10.0/src/cred.c000066400000000000000000000635511417126203300151440ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include "fido.h" #include "fido/es256.h" #ifndef FIDO_MAXMSG_CRED #define FIDO_MAXMSG_CRED 4096 #endif 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 */ if (fido_blob_decode(val, &cred->authdata_raw) < 0) { fido_log_debug("%s: fido_blob_decode", __func__); return (-1); } 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)); case 5: /* large blob key */ return (fido_blob_decode(val, &cred->largeblob_key)); 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, int *ms) { fido_blob_t f; fido_blob_t *ecdh = NULL; fido_opt_t uv = cred->uv; es256_pk_t *pk = NULL; cbor_item_t *argv[9]; const uint8_t cmd = CTAP_CBOR_MAKECRED; 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.mask) if ((argv[5] = cbor_encode_cred_ext(&cred->ext, &cred->blob)) == NULL) { fido_log_debug("%s: cbor_encode_cred_ext", __func__); r = FIDO_ERR_INTERNAL; goto fail; } /* user verification */ if (pin != NULL || (uv == FIDO_OPT_TRUE && fido_dev_supports_permissions(dev))) { if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh, pin, cred->rp.id, &argv[7], &argv[8], ms)) != FIDO_OK) { fido_log_debug("%s: cbor_add_uv_params", __func__); goto fail; } uv = FIDO_OPT_OMIT; } /* options */ if (cred->rk != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT) if ((argv[6] = cbor_encode_cred_opt(cred->rk, uv)) == NULL) { fido_log_debug("%s: cbor_encode_cred_opt", __func__); r = FIDO_ERR_INTERNAL; goto fail; } /* framing and transmission */ if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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) { unsigned char *reply; int reply_len; int r; fido_cred_reset_rx(cred); if ((reply = malloc(FIDO_MAXMSG_CRED)) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, reply, FIDO_MAXMSG_CRED, 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, cred, parse_makecred_reply)) != FIDO_OK) { fido_log_debug("%s: parse_makecred_reply", __func__); goto fail; } if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) || fido_blob_is_empty(&cred->attcred.id)) { r = FIDO_ERR_INVALID_CBOR; goto fail; } r = FIDO_OK; fail: free(reply); if (r != FIDO_OK) fido_cred_reset_rx(cred); return (r); } 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, ms)) != 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) { int ms = dev->timeout_ms; #ifdef USE_WINHELLO if (dev->flags & FIDO_DEV_WINHELLO) return (fido_winhello_make_cred(dev, cred, pin, ms)); #endif if (fido_dev_is_fido2(dev) == false) { if (pin != NULL || cred->rk == FIDO_OPT_TRUE || cred->ext.mask != 0) return (FIDO_ERR_UNSUPPORTED_OPTION); return (u2f_register(dev, cred, &ms)); } return (fido_dev_make_cred_wait(dev, cred, pin, &ms)); } static int check_extensions(const fido_cred_ext_t *authdata_ext, const fido_cred_ext_t *ext) { fido_cred_ext_t tmp; /* XXX: largeBlobKey is not part of the extensions map */ memcpy(&tmp, ext, sizeof(tmp)); tmp.mask &= ~FIDO_EXT_LARGEBLOB_KEY; return (timingsafe_bcmp(authdata_ext, &tmp, sizeof(*authdata_ext))); } 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_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 */ const EVP_MD *md = NULL; EVP_MD_CTX *ctx = NULL; int ok = -1; if (dgst->len != SHA256_DIGEST_LENGTH || (md = EVP_sha256()) == NULL || (ctx = EVP_MD_CTX_new()) == NULL || EVP_DigestInit_ex(ctx, md, NULL) != 1 || EVP_DigestUpdate(ctx, &zero, sizeof(zero)) != 1 || EVP_DigestUpdate(ctx, rp_id, rp_id_len) != 1 || EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || EVP_DigestUpdate(ctx, id->ptr, id->len) != 1 || EVP_DigestUpdate(ctx, &four, sizeof(four)) != 1 || EVP_DigestUpdate(ctx, pk->x, sizeof(pk->x)) != 1 || EVP_DigestUpdate(ctx, pk->y, sizeof(pk->y)) != 1 || EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) { fido_log_debug("%s: sha256", __func__); goto fail; } ok = 0; fail: EVP_MD_CTX_free(ctx); return (ok); } static int verify_attstmt(const fido_blob_t *dgst, const fido_attstmt_t *attstmt) { BIO *rawcert = NULL; X509 *cert = NULL; EVP_PKEY *pkey = NULL; int ok = -1; /* openssl needs ints */ if (attstmt->x5c.len > INT_MAX) { fido_log_debug("%s: x5c.len=%zu", __func__, attstmt->x5c.len); return (-1); } /* fetch key from x509 */ if ((rawcert = BIO_new_mem_buf(attstmt->x5c.ptr, (int)attstmt->x5c.len)) == NULL || (cert = d2i_X509_bio(rawcert, NULL)) == NULL || (pkey = X509_get_pubkey(cert)) == NULL) { fido_log_debug("%s: x509 key", __func__); goto fail; } switch (attstmt->alg) { case COSE_UNSPEC: case COSE_ES256: ok = es256_verify_sig(dgst, pkey, &attstmt->sig); break; case COSE_RS256: ok = rs256_verify_sig(dgst, pkey, &attstmt->sig); break; case COSE_RS1: ok = rs1_verify_sig(dgst, pkey, &attstmt->sig); break; case COSE_EDDSA: ok = eddsa_verify_sig(dgst, pkey, &attstmt->sig); break; default: fido_log_debug("%s: unknown alg %d", __func__, attstmt->alg); break; } fail: BIO_free(rawcert); X509_free(cert); 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 (fido_get_signed_hash(COSE_ES256, &dgst, &cred->cdh, &cred->authdata_cbor) < 0) { fido_log_debug("%s: fido_get_signed_hash", __func__); r = FIDO_ERR_INTERNAL; goto out; } } else if (!strcmp(cred->fmt, "fido-u2f")) { 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; } } else if (!strcmp(cred->fmt, "tpm")) { if (fido_get_signed_hash_tpm(&dgst, &cred->cdh, &cred->authdata_raw, &cred->attstmt, &cred->attcred) < 0) { fido_log_debug("%s: fido_get_signed_hash_tpm", __func__); r = FIDO_ERR_INTERNAL; goto out; } } else { fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt); r = FIDO_ERR_INVALID_ARGUMENT; goto out; } if (verify_attstmt(&dgst, &cred->attstmt) < 0) { fido_log_debug("%s: verify_attstmt", __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[1024]; /* XXX */ 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 (fido_get_signed_hash(cred->attcred.type, &dgst, &cred->cdh, &cred->authdata_cbor) < 0) { fido_log_debug("%s: fido_get_signed_hash", __func__); r = FIDO_ERR_INTERNAL; goto out; } } else if (!strcmp(cred->fmt, "fido-u2f")) { 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; } } else { fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt); r = FIDO_ERR_INVALID_ARGUMENT; goto out; } switch (cred->attcred.type) { case COSE_ES256: ok = es256_pk_verify_sig(&dgst, &cred->attcred.pubkey.es256, &cred->attstmt.sig); break; case COSE_RS256: ok = rs256_pk_verify_sig(&dgst, &cred->attcred.pubkey.rs256, &cred->attstmt.sig); break; case COSE_EDDSA: ok = eddsa_pk_verify_sig(&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) { fido_blob_reset(&cred->authdata_cbor); fido_blob_reset(&cred->authdata_raw); fido_blob_reset(&cred->attcred.id); memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext)); memset(&cred->authdata, 0, sizeof(cred->authdata)); memset(&cred->attcred, 0, sizeof(cred->attcred)); } static void fido_cred_clean_attstmt(fido_attstmt_t *attstmt) { fido_blob_reset(&attstmt->certinfo); fido_blob_reset(&attstmt->pubarea); fido_blob_reset(&attstmt->cbor); fido_blob_reset(&attstmt->x5c); fido_blob_reset(&attstmt->sig); memset(attstmt, 0, sizeof(*attstmt)); } void fido_cred_reset_tx(fido_cred_t *cred) { fido_blob_reset(&cred->cd); fido_blob_reset(&cred->cdh); fido_blob_reset(&cred->user.id); fido_blob_reset(&cred->blob); free(cred->rp.id); free(cred->rp.name); free(cred->user.icon); free(cred->user.name); free(cred->user.display_name); fido_free_blob_array(&cred->excl); memset(&cred->rp, 0, sizeof(cred->rp)); memset(&cred->user, 0, sizeof(cred->user)); memset(&cred->excl, 0, sizeof(cred->excl)); memset(&cred->ext, 0, sizeof(cred->ext)); cred->type = 0; cred->rk = FIDO_OPT_OMIT; cred->uv = FIDO_OPT_OMIT; } void fido_cred_reset_rx(fido_cred_t *cred) { free(cred->fmt); cred->fmt = NULL; fido_cred_clean_authdata(cred); fido_cred_clean_attstmt(&cred->attstmt); fido_blob_reset(&cred->largeblob_key); } 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_ERR_INVALID_ARGUMENT; fido_cred_clean_authdata(cred); if (ptr == NULL || len == 0) goto fail; if ((item = cbor_load(ptr, len, &cbor)) == NULL) { fido_log_debug("%s: cbor_load", __func__); goto fail; } if (fido_blob_decode(item, &cred->authdata_raw) < 0) { fido_log_debug("%s: fido_blob_decode", __func__); 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__); 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_ERR_INVALID_ARGUMENT; fido_cred_clean_authdata(cred); if (ptr == NULL || len == 0) goto fail; if (fido_blob_set(&cred->authdata_raw, ptr, len) < 0) { fido_log_debug("%s: fido_blob_set", __func__); r = FIDO_ERR_INTERNAL; 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__); 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_id(fido_cred_t *cred, const unsigned char *ptr, size_t len) { if (fido_blob_set(&cred->attcred.id, ptr, len) < 0) return (FIDO_ERR_INVALID_ARGUMENT); return (FIDO_OK); } int fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len) { if (fido_blob_set(&cred->attstmt.x5c, ptr, len) < 0) return (FIDO_ERR_INVALID_ARGUMENT); return (FIDO_OK); } int fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len) { if (fido_blob_set(&cred->attstmt.sig, ptr, len) < 0) return (FIDO_ERR_INVALID_ARGUMENT); return (FIDO_OK); } int fido_cred_set_attstmt(fido_cred_t *cred, const unsigned char *ptr, size_t len) { cbor_item_t *item = NULL; struct cbor_load_result cbor; int r = FIDO_ERR_INVALID_ARGUMENT; fido_cred_clean_attstmt(&cred->attstmt); if (ptr == NULL || len == 0) goto fail; if ((item = cbor_load(ptr, len, &cbor)) == NULL) { fido_log_debug("%s: cbor_load", __func__); goto fail; } if (cbor_decode_attstmt(item, &cred->attstmt) < 0) { fido_log_debug("%s: cbor_decode_attstmt", __func__); goto fail; } r = FIDO_OK; fail: if (item != NULL) cbor_decref(&item); if (r != FIDO_OK) fido_cred_clean_attstmt(&cred->attstmt); return (r); } 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(fido_cred_t *cred, const unsigned char *data, size_t data_len) { if (!fido_blob_is_empty(&cred->cdh) || fido_blob_set(&cred->cd, data, data_len) < 0) { return (FIDO_ERR_INVALID_ARGUMENT); } if (fido_sha256(&cred->cdh, data, data_len) < 0) { fido_blob_reset(&cred->cd); return (FIDO_ERR_INTERNAL); } return (FIDO_OK); } int fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash, size_t hash_len) { if (!fido_blob_is_empty(&cred->cd) || 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 && fido_blob_set(&up->id, user_id, user_id_len) < 0) goto fail; 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) cred->ext.mask = 0; else { if ((ext & FIDO_EXT_CRED_MASK) != ext) return (FIDO_ERR_INVALID_ARGUMENT); cred->ext.mask |= 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_prot(fido_cred_t *cred, int prot) { if (prot == 0) { cred->ext.mask &= ~FIDO_EXT_CRED_PROTECT; cred->ext.prot = 0; } else { if (prot != FIDO_CRED_PROT_UV_OPTIONAL && prot != FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID && prot != FIDO_CRED_PROT_UV_REQUIRED) return (FIDO_ERR_INVALID_ARGUMENT); cred->ext.mask |= FIDO_EXT_CRED_PROTECT; cred->ext.prot = prot; } return (FIDO_OK); } int fido_cred_set_pin_minlen(fido_cred_t *cred, size_t len) { if (len == 0) cred->ext.mask &= ~FIDO_EXT_MINPINLEN; else cred->ext.mask |= FIDO_EXT_MINPINLEN; cred->ext.minpinlen = len; return (FIDO_OK); } int fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len) { if (ptr == NULL || len == 0) return (FIDO_ERR_INVALID_ARGUMENT); if (fido_blob_set(&cred->blob, ptr, len) < 0) return (FIDO_ERR_INTERNAL); cred->ext.mask |= FIDO_EXT_CRED_BLOB; 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") && strcmp(fmt, "none") && strcmp(fmt, "tpm")) 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); } uint32_t fido_cred_sigcount(const fido_cred_t *cred) { return (cred->authdata.sigcount); } 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_authdata_raw_ptr(const fido_cred_t *cred) { return (cred->authdata_raw.ptr); } size_t fido_cred_authdata_raw_len(const fido_cred_t *cred) { return (cred->authdata_raw.len); } const unsigned char * fido_cred_attstmt_ptr(const fido_cred_t *cred) { return (cred->attstmt.cbor.ptr); } size_t fido_cred_attstmt_len(const fido_cred_t *cred) { return (cred->attstmt.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 unsigned char * fido_cred_aaguid_ptr(const fido_cred_t *cred) { return (cred->attcred.aaguid); } size_t fido_cred_aaguid_len(const fido_cred_t *cred) { return (sizeof(cred->attcred.aaguid)); } int fido_cred_prot(const fido_cred_t *cred) { return (cred->ext.prot); } size_t fido_cred_pin_minlen(const fido_cred_t *cred) { return (cred->ext.minpinlen); } 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); } const unsigned char * fido_cred_largeblob_key_ptr(const fido_cred_t *cred) { return (cred->largeblob_key.ptr); } size_t fido_cred_largeblob_key_len(const fido_cred_t *cred) { return (cred->largeblob_key.len); } libfido2-1.10.0/src/credman.c000066400000000000000000000413521417126203300156330ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #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 #define CMD_UPDATE_CRED 0x07 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 void *body, cbor_item_t **param, fido_blob_t *hmac_data) { cbor_item_t *param_cbor[3]; const fido_cred_t *cred; 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; if ((param_cbor[0] = fido_blob_encode(body)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } break; case CMD_DELETE_CRED: n = 2; if ((param_cbor[1] = cbor_encode_pubkey(body)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } break; case CMD_UPDATE_CRED: n = 3; cred = body; param_cbor[1] = cbor_encode_pubkey(&cred->attcred.id); param_cbor[2] = cbor_encode_user_entity(&cred->user); if (param_cbor[1] == NULL || param_cbor[2] == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } break; default: fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd); 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 subcmd, const void *param, const char *pin, const char *rp_id, fido_opt_t uv, int *ms) { fido_blob_t f; fido_blob_t *ecdh = NULL; fido_blob_t hmac; es256_pk_t *pk = NULL; cbor_item_t *argv[4]; const uint8_t cmd = CTAP_CBOR_CRED_MGMT_PRE; int r = FIDO_ERR_INTERNAL; memset(&f, 0, sizeof(f)); memset(&hmac, 0, sizeof(hmac)); memset(&argv, 0, sizeof(argv)); if (fido_dev_is_fido2(dev) == false) { fido_log_debug("%s: fido_dev_is_fido2", __func__); r = FIDO_ERR_INVALID_COMMAND; goto fail; } /* subCommand */ if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } /* pinProtocol, pinAuth */ if (pin != NULL || uv == FIDO_OPT_TRUE) { if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) { fido_log_debug("%s: credman_prepare_hmac", __func__); goto fail; } if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin, rp_id, &argv[3], &argv[2], ms)) != FIDO_OK) { fido_log_debug("%s: cbor_add_uv_params", __func__); goto fail; } } /* framing and transmission */ if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; memset(metadata, 0, sizeof(*metadata)); if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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, NULL, FIDO_OPT_TRUE, ms)) != 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) { int ms = dev->timeout_ms; return (credman_get_metadata_wait(dev, metadata, pin, &ms)); } static int credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg) { fido_cred_t *cred = arg; uint64_t prot; 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: 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); case 10: if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX || fido_cred_set_prot(cred, (int)prot) != FIDO_OK) return (-1); return (0); case 11: return (fido_blob_decode(val, &cred->largeblob_key)); 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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; credman_reset_rk(rk); if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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, rp_id, FIDO_OPT_TRUE, ms)) != 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, NULL, FIDO_OPT_FALSE, ms)) != 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) { int ms = dev->timeout_ms; return (credman_get_rk_wait(dev, rp_id, rk, pin, &ms)); } 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, NULL, FIDO_OPT_TRUE, ms)) != 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) { int ms = dev->timeout_ms; return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, &ms)); } 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; fido_blob_reset(&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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; credman_reset_rp(rp); if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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, NULL, FIDO_OPT_TRUE, ms)) != 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, NULL, FIDO_OPT_FALSE, ms)) != 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) { int ms = dev->timeout_ms; return (credman_get_rp_wait(dev, rp, pin, &ms)); } static int credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int *ms) { int r; if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL, FIDO_OPT_TRUE, ms)) != FIDO_OK || (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) return (r); return (FIDO_OK); } int fido_credman_set_dev_rk(fido_dev_t *dev, fido_cred_t *cred, const char *pin) { int ms = dev->timeout_ms; return (credman_set_dev_rk_wait(dev, cred, pin, &ms)); } 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); } libfido2-1.10.0/src/dev.c000066400000000000000000000374221417126203300150030ustar00rootroot00000000000000/* * 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" #ifndef TLS #define TLS #endif typedef struct dev_manifest_func_node { dev_manifest_func_t manifest_func; struct dev_manifest_func_node *next; } dev_manifest_func_node_t; static TLS dev_manifest_func_node_t *manifest_funcs = NULL; static TLS bool disable_u2f_fallback; static void find_manifest_func_node(dev_manifest_func_t f, dev_manifest_func_node_t **curr, dev_manifest_func_node_t **prev) { *prev = NULL; *curr = manifest_funcs; while (*curr != NULL && (*curr)->manifest_func != f) { *prev = *curr; *curr = (*curr)->next; } } #ifdef FIDO_FUZZ static void set_random_report_len(fido_dev_t *dev) { dev->rx_len = CTAP_MIN_REPORT_LEN + uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1); dev->tx_len = CTAP_MIN_REPORT_LEN + uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1); } #endif static void fido_dev_set_extension_flags(fido_dev_t *dev, const fido_cbor_info_t *info) { char * const *ptr = fido_cbor_info_extensions_ptr(info); size_t len = fido_cbor_info_extensions_len(info); for (size_t i = 0; i < len; i++) if (strcmp(ptr[i], "credProtect") == 0) dev->flags |= FIDO_DEV_CRED_PROT; } static void fido_dev_set_option_flags(fido_dev_t *dev, const fido_cbor_info_t *info) { char * const *ptr = fido_cbor_info_options_name_ptr(info); const bool *val = fido_cbor_info_options_value_ptr(info); size_t len = fido_cbor_info_options_len(info); for (size_t i = 0; i < len; i++) if (strcmp(ptr[i], "clientPin") == 0) { dev->flags |= val[i] ? FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET; } else if (strcmp(ptr[i], "credMgmt") == 0 || strcmp(ptr[i], "credentialMgmtPreview") == 0) { if (val[i]) dev->flags |= FIDO_DEV_CREDMAN; } else if (strcmp(ptr[i], "uv") == 0) { dev->flags |= val[i] ? FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET; } else if (strcmp(ptr[i], "pinUvAuthToken") == 0) { if (val[i]) dev->flags |= FIDO_DEV_TOKEN_PERMS; } } static void fido_dev_set_protocol_flags(fido_dev_t *dev, const fido_cbor_info_t *info) { const uint8_t *ptr = fido_cbor_info_protocols_ptr(info); size_t len = fido_cbor_info_protocols_len(info); for (size_t i = 0; i < len; i++) switch (ptr[i]) { case CTAP_PIN_PROTOCOL1: dev->flags |= FIDO_DEV_PIN_PROTOCOL1; break; case CTAP_PIN_PROTOCOL2: dev->flags |= FIDO_DEV_PIN_PROTOCOL2; break; default: fido_log_debug("%s: unknown protocol %u", __func__, ptr[i]); break; } } static void fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info) { fido_dev_set_extension_flags(dev, info); fido_dev_set_option_flags(dev, info); fido_dev_set_protocol_flags(dev, info); } static int fido_dev_open_tx(fido_dev_t *dev, const char *path, int *ms) { int r; 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 (dev->cid != CTAP_CID_BROADCAST) { fido_log_debug("%s: cid=0x%x", __func__, dev->cid); return (FIDO_ERR_INVALID_ARGUMENT); } if (fido_get_random(&dev->nonce, sizeof(dev->nonce)) < 0) { fido_log_debug("%s: fido_get_random", __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 (dev->io_own) { dev->rx_len = CTAP_MAX_REPORT_LEN; dev->tx_len = CTAP_MAX_REPORT_LEN; } else { dev->rx_len = fido_hid_report_in_len(dev->io_handle); dev->tx_len = fido_hid_report_out_len(dev->io_handle); } #ifdef FIDO_FUZZ set_random_report_len(dev); #endif if (dev->rx_len < CTAP_MIN_REPORT_LEN || dev->rx_len > CTAP_MAX_REPORT_LEN) { fido_log_debug("%s: invalid rx_len %zu", __func__, dev->rx_len); r = FIDO_ERR_RX; goto fail; } if (dev->tx_len < CTAP_MIN_REPORT_LEN || dev->tx_len > CTAP_MAX_REPORT_LEN) { fido_log_debug("%s: invalid tx_len %zu", __func__, dev->tx_len); r = FIDO_ERR_TX; goto fail; } if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce), ms) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } return (FIDO_OK); fail: dev->io.close(dev->io_handle); dev->io_handle = NULL; return (r); } static int fido_dev_open_rx(fido_dev_t *dev, int *ms) { fido_cbor_info_t *info = NULL; int reply_len; int r; if ((reply_len = fido_rx(dev, CTAP_CMD_INIT, &dev->attr, sizeof(dev->attr), ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; } #ifdef FIDO_FUZZ dev->attr.nonce = dev->nonce; #endif if ((size_t)reply_len != sizeof(dev->attr) || dev->attr.nonce != dev->nonce) { fido_log_debug("%s: invalid nonce", __func__); r = FIDO_ERR_RX; goto fail; } dev->flags = 0; dev->cid = dev->attr.cid; if (fido_dev_is_fido2(dev)) { if ((info = fido_cbor_info_new()) == NULL) { fido_log_debug("%s: fido_cbor_info_new", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if ((r = fido_dev_get_cbor_info_wait(dev, info, ms)) != FIDO_OK) { fido_log_debug("%s: fido_dev_cbor_info_wait: %d", __func__, r); if (disable_u2f_fallback) goto fail; fido_log_debug("%s: falling back to u2f", __func__); fido_dev_force_u2f(dev); } else { fido_dev_set_flags(dev, info); } } if (fido_dev_is_fido2(dev) && info != NULL) { dev->maxmsgsize = fido_cbor_info_maxmsgsiz(info); fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__, FIDO_MAXMSG, (unsigned long)dev->maxmsgsize); } r = FIDO_OK; fail: fido_cbor_info_free(&info); if (r != FIDO_OK) { dev->io.close(dev->io_handle); dev->io_handle = NULL; } return (r); } static int fido_dev_open_wait(fido_dev_t *dev, const char *path, int *ms) { int r; #ifdef USE_WINHELLO if (strcmp(path, FIDO_WINHELLO_PATH) == 0) return (fido_winhello_open(dev)); #endif if ((r = fido_dev_open_tx(dev, path, ms)) != FIDO_OK || (r = fido_dev_open_rx(dev, ms)) != FIDO_OK) return (r); return (FIDO_OK); } int fido_dev_register_manifest_func(const dev_manifest_func_t f) { dev_manifest_func_node_t *prev, *curr, *n; find_manifest_func_node(f, &curr, &prev); if (curr != NULL) return (FIDO_OK); if ((n = calloc(1, sizeof(*n))) == NULL) { fido_log_debug("%s: calloc", __func__); return (FIDO_ERR_INTERNAL); } n->manifest_func = f; n->next = manifest_funcs; manifest_funcs = n; return (FIDO_OK); } void fido_dev_unregister_manifest_func(const dev_manifest_func_t f) { dev_manifest_func_node_t *prev, *curr; find_manifest_func_node(f, &curr, &prev); if (curr == NULL) return; if (prev != NULL) prev->next = curr->next; else manifest_funcs = curr->next; free(curr); } int fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { dev_manifest_func_node_t *curr = NULL; dev_manifest_func_t m_func; size_t curr_olen; int r; *olen = 0; if (fido_dev_register_manifest_func(fido_hid_manifest) != FIDO_OK) return (FIDO_ERR_INTERNAL); #ifdef NFC_LINUX if (fido_dev_register_manifest_func(fido_nfc_manifest) != FIDO_OK) return (FIDO_ERR_INTERNAL); #endif #ifdef USE_WINHELLO if (fido_dev_register_manifest_func(fido_winhello_manifest) != FIDO_OK) return (FIDO_ERR_INTERNAL); #endif for (curr = manifest_funcs; curr != NULL; curr = curr->next) { curr_olen = 0; m_func = curr->manifest_func; r = m_func(devlist + *olen, ilen - *olen, &curr_olen); if (r != FIDO_OK) return (r); *olen += curr_olen; if (*olen == ilen) break; } return (FIDO_OK); } int fido_dev_open_with_info(fido_dev_t *dev) { int ms = dev->timeout_ms; if (dev->path == NULL) return (FIDO_ERR_INVALID_ARGUMENT); return (fido_dev_open_wait(dev, dev->path, &ms)); } int fido_dev_open(fido_dev_t *dev, const char *path) { int ms = dev->timeout_ms; #ifdef NFC_LINUX if (strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) == 0) { dev->io_own = true; dev->io = (fido_dev_io_t) { fido_nfc_open, fido_nfc_close, fido_nfc_read, fido_nfc_write, }; dev->transport = (fido_dev_transport_t) { fido_nfc_rx, fido_nfc_tx, }; } #endif return (fido_dev_open_wait(dev, path, &ms)); } int fido_dev_close(fido_dev_t *dev) { #ifdef USE_WINHELLO if (dev->flags & FIDO_DEV_WINHELLO) return (fido_winhello_close(dev)); #endif if (dev->io_handle == NULL || dev->io.close == NULL) return (FIDO_ERR_INVALID_ARGUMENT); dev->io.close(dev->io_handle); dev->io_handle = NULL; dev->cid = CTAP_CID_BROADCAST; return (FIDO_OK); } int fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask) { if (dev->io_handle == NULL || sigmask == NULL) return (FIDO_ERR_INVALID_ARGUMENT); #ifdef NFC_LINUX if (dev->transport.rx == fido_nfc_rx && dev->io.read == fido_nfc_read) return (fido_nfc_set_sigmask(dev->io_handle, sigmask)); #endif if (dev->transport.rx == NULL && dev->io.read == fido_hid_read) return (fido_hid_set_sigmask(dev->io_handle, sigmask)); return (FIDO_ERR_INVALID_ARGUMENT); } int fido_dev_cancel(fido_dev_t *dev) { int ms = dev->timeout_ms; #ifdef USE_WINHELLO if (dev->flags & FIDO_DEV_WINHELLO) return (fido_winhello_cancel(dev)); #endif if (fido_dev_is_fido2(dev) == false) return (FIDO_ERR_INVALID_ARGUMENT); if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0, &ms) < 0) return (FIDO_ERR_TX); return (FIDO_OK); } int fido_dev_get_touch_begin(fido_dev_t *dev) { fido_blob_t f; cbor_item_t *argv[9]; const char *clientdata = FIDO_DUMMY_CLIENTDATA; const uint8_t user_id = FIDO_DUMMY_USER_ID; unsigned char cdh[SHA256_DIGEST_LENGTH]; fido_rp_t rp; fido_user_t user; int ms = dev->timeout_ms; int r = FIDO_ERR_INTERNAL; memset(&f, 0, sizeof(f)); memset(argv, 0, sizeof(argv)); memset(cdh, 0, sizeof(cdh)); memset(&rp, 0, sizeof(rp)); memset(&user, 0, sizeof(user)); if (fido_dev_is_fido2(dev) == false) return (u2f_get_touch_begin(dev, &ms)); if (SHA256((const void *)clientdata, strlen(clientdata), cdh) != cdh) { fido_log_debug("%s: sha256", __func__); return (FIDO_ERR_INTERNAL); } if ((rp.id = strdup(FIDO_DUMMY_RP_ID)) == NULL || (user.name = strdup(FIDO_DUMMY_USER_NAME)) == NULL) { fido_log_debug("%s: strdup", __func__); goto fail; } if (fido_blob_set(&user.id, &user_id, sizeof(user_id)) < 0) { fido_log_debug("%s: fido_blob_set", __func__); goto fail; } if ((argv[0] = cbor_build_bytestring(cdh, sizeof(cdh))) == NULL || (argv[1] = cbor_encode_rp_entity(&rp)) == NULL || (argv[2] = cbor_encode_user_entity(&user)) == NULL || (argv[3] = cbor_encode_pubkey_param(COSE_ES256)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } if (fido_dev_supports_pin(dev)) { if ((argv[7] = cbor_new_definite_bytestring()) == NULL || (argv[8] = cbor_encode_pin_opt(dev)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } } if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, &ms) < 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); free(rp.id); free(user.name); free(user.id.ptr); return (r); } int fido_dev_get_touch_status(fido_dev_t *dev, int *touched, int ms) { int r; *touched = 0; if (fido_dev_is_fido2(dev) == false) return (u2f_get_touch_status(dev, touched, &ms)); switch ((r = fido_rx_cbor_status(dev, &ms))) { case FIDO_ERR_PIN_AUTH_INVALID: case FIDO_ERR_PIN_INVALID: case FIDO_ERR_PIN_NOT_SET: case FIDO_ERR_SUCCESS: *touched = 1; break; case FIDO_ERR_RX: /* ignore */ break; default: fido_log_debug("%s: fido_rx_cbor_status", __func__); return (r); } 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: non-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 = *io; dev->io_own = true; return (FIDO_OK); } int fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t) { if (dev->io_handle != NULL) { fido_log_debug("%s: non-NULL handle", __func__); return (FIDO_ERR_INVALID_ARGUMENT); } dev->transport = *t; dev->io_own = true; return (FIDO_OK); } void * fido_dev_io_handle(const fido_dev_t *dev) { return (dev->io_handle); } void fido_init(int flags) { if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL) fido_log_init(); disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK); } fido_dev_t * fido_dev_new(void) { fido_dev_t *dev; if ((dev = calloc(1, sizeof(*dev))) == NULL) return (NULL); dev->cid = CTAP_CID_BROADCAST; dev->timeout_ms = -1; dev->io = (fido_dev_io_t) { &fido_hid_open, &fido_hid_close, &fido_hid_read, &fido_hid_write, }; return (dev); } fido_dev_t * fido_dev_new_with_info(const fido_dev_info_t *di) { fido_dev_t *dev; if ((dev = calloc(1, sizeof(*dev))) == NULL) return (NULL); #if 0 if (di->io.open == NULL || di->io.close == NULL || di->io.read == NULL || di->io.write == NULL) { fido_log_debug("%s: NULL function", __func__); fido_dev_free(&dev); return (NULL); } #endif dev->io = di->io; dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL; dev->transport = di->transport; dev->cid = CTAP_CID_BROADCAST; dev->timeout_ms = -1; if ((dev->path = strdup(di->path)) == NULL) { fido_log_debug("%s: strdup", __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->path); 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); } bool fido_dev_is_winhello(const fido_dev_t *dev) { return (dev->flags & FIDO_DEV_WINHELLO); } bool fido_dev_supports_pin(const fido_dev_t *dev) { return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET)); } bool fido_dev_has_pin(const fido_dev_t *dev) { return (dev->flags & FIDO_DEV_PIN_SET); } bool fido_dev_supports_cred_prot(const fido_dev_t *dev) { return (dev->flags & FIDO_DEV_CRED_PROT); } bool fido_dev_supports_credman(const fido_dev_t *dev) { return (dev->flags & FIDO_DEV_CREDMAN); } bool fido_dev_supports_uv(const fido_dev_t *dev) { return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET)); } bool fido_dev_has_uv(const fido_dev_t *dev) { return (dev->flags & FIDO_DEV_UV_SET); } bool fido_dev_supports_permissions(const fido_dev_t *dev) { return (dev->flags & FIDO_DEV_TOKEN_PERMS); } void fido_dev_force_u2f(fido_dev_t *dev) { dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR; dev->flags = 0; } void fido_dev_force_fido2(fido_dev_t *dev) { dev->attr.flags |= FIDO_CAP_CBOR; } uint8_t fido_dev_get_pin_protocol(const fido_dev_t *dev) { if (dev->flags & FIDO_DEV_PIN_PROTOCOL2) return (CTAP_PIN_PROTOCOL2); else if (dev->flags & FIDO_DEV_PIN_PROTOCOL1) return (CTAP_PIN_PROTOCOL1); return (0); } uint64_t fido_dev_maxmsgsize(const fido_dev_t *dev) { return (dev->maxmsgsize); } int fido_dev_set_timeout(fido_dev_t *dev, int ms) { if (ms < -1) return (FIDO_ERR_INVALID_ARGUMENT); dev->timeout_ms = ms; return (FIDO_OK); } libfido2-1.10.0/src/diff_exports.sh000077500000000000000000000011771417126203300171120ustar00rootroot00000000000000#!/bin/sh -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. for f in export.gnu export.llvm export.msvc; do if [ ! -f "${f}" ]; then exit 1 fi done TMPDIR="$(mktemp -d)" GNU="${TMPDIR}/gnu" LLVM="${TMPDIR}/llvm" MSVC="${TMPDIR}/msvc" awk '/^[^*{}]+;$/' export.gnu | tr -d '\t;' | sort > "${GNU}" sed 's/^_//' export.llvm | sort > "${LLVM}" grep -v '^EXPORTS$' export.msvc | sort > "${MSVC}" diff -u "${GNU}" "${LLVM}" && diff -u "${MSVC}" "${LLVM}" ERROR=$? rm "${GNU}" "${LLVM}" "${MSVC}" rmdir "${TMPDIR}" exit ${ERROR} libfido2-1.10.0/src/ecdh.c000066400000000000000000000121531417126203300151220ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #if defined(LIBRESSL_VERSION_NUMBER) #include #else #include #endif #include "fido.h" #include "fido/es256.h" #if defined(LIBRESSL_VERSION_NUMBER) static int hkdf_sha256(uint8_t *key, const char *info, const fido_blob_t *secret) { const EVP_MD *md; uint8_t salt[32]; memset(salt, 0, sizeof(salt)); if ((md = EVP_sha256()) == NULL || HKDF(key, SHA256_DIGEST_LENGTH, md, secret->ptr, secret->len, salt, sizeof(salt), (const uint8_t *)info, strlen(info)) != 1) return -1; return 0; } #else static int hkdf_sha256(uint8_t *key, char *info, fido_blob_t *secret) { const EVP_MD *const_md; EVP_MD *md = NULL; EVP_PKEY_CTX *ctx = NULL; size_t keylen = SHA256_DIGEST_LENGTH; uint8_t salt[32]; int ok = -1; memset(salt, 0, sizeof(salt)); if (secret->len > INT_MAX || strlen(info) > INT_MAX) { fido_log_debug("%s: invalid param", __func__); goto fail; } if ((const_md = EVP_sha256()) == NULL || (md = EVP_MD_meth_dup(const_md)) == NULL || (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)) == NULL) { fido_log_debug("%s: init", __func__); goto fail; } if (EVP_PKEY_derive_init(ctx) < 1 || EVP_PKEY_CTX_set_hkdf_md(ctx, md) < 1 || EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, sizeof(salt)) < 1 || EVP_PKEY_CTX_set1_hkdf_key(ctx, secret->ptr, (int)secret->len) < 1 || EVP_PKEY_CTX_add1_hkdf_info(ctx, (void *)info, (int)strlen(info)) < 1) { fido_log_debug("%s: EVP_PKEY_CTX", __func__); goto fail; } if (EVP_PKEY_derive(ctx, key, &keylen) < 1) { fido_log_debug("%s: EVP_PKEY_derive", __func__); goto fail; } ok = 0; fail: if (md != NULL) EVP_MD_meth_free(md); if (ctx != NULL) EVP_PKEY_CTX_free(ctx); return ok; } #endif /* defined(LIBRESSL_VERSION_NUMBER) */ static int kdf(uint8_t prot, fido_blob_t *key, /* const */ fido_blob_t *secret) { char hmac_info[] = "CTAP2 HMAC key"; /* const */ char aes_info[] = "CTAP2 AES key"; /* const */ switch (prot) { case CTAP_PIN_PROTOCOL1: /* use sha256 on the resulting secret */ key->len = SHA256_DIGEST_LENGTH; if ((key->ptr = calloc(1, key->len)) == NULL || SHA256(secret->ptr, secret->len, key->ptr) != key->ptr) { fido_log_debug("%s: SHA256", __func__); return -1; } break; case CTAP_PIN_PROTOCOL2: /* use two instances of hkdf-sha256 on the resulting secret */ key->len = 2 * SHA256_DIGEST_LENGTH; if ((key->ptr = calloc(1, key->len)) == NULL || hkdf_sha256(key->ptr, hmac_info, secret) < 0 || hkdf_sha256(key->ptr + SHA256_DIGEST_LENGTH, aes_info, secret) < 0) { fido_log_debug("%s: hkdf", __func__); return -1; } break; default: fido_log_debug("%s: unknown pin protocol %u", __func__, prot); return -1; } return 0; } static int do_ecdh(const fido_dev_t *dev, 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; if ((secret = fido_blob_new()) == NULL || (*ecdh = fido_blob_new()) == NULL) goto fail; 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; } 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; } 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; } if (kdf(fido_dev_get_pin_protocol(dev), *ecdh, secret) < 0) { fido_log_debug("%s: kdf", __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, int *ms) { es256_sk_t *sk = NULL; /* our private key */ es256_pk_t *ak = NULL; /* authenticator's public key */ int r; *pk = NULL; *ecdh = NULL; 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, ms) != FIDO_OK) { fido_log_debug("%s: fido_dev_authkey", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (do_ecdh(dev, 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; } libfido2-1.10.0/src/eddsa.c000066400000000000000000000105241417126203300152770ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include "fido.h" #include "fido/eddsa.h" #if defined(LIBRESSL_VERSION_NUMBER) 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; fido_log_debug("%s: unimplemented", __func__); 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; fido_log_debug("%s: unimplemented", __func__); return (0); } #endif /* LIBRESSL_VERSION_NUMBER */ #if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3040000f 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; fido_log_debug("%s: unimplemented", __func__); return (0); } #endif /* LIBRESSL_VERSION_NUMBER < 0x3040000f */ 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; freezero(pk, sizeof(*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); } int eddsa_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey, const fido_blob_t *sig) { EVP_MD_CTX *mdctx = NULL; int ok = -1; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_ED25519) { fido_log_debug("%s: EVP_PKEY_base_id", __func__); goto fail; } /* 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 ((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: EVP_MD_CTX_free(mdctx); return (ok); } int eddsa_pk_verify_sig(const fido_blob_t *dgst, const eddsa_pk_t *pk, const fido_blob_t *sig) { EVP_PKEY *pkey; int ok = -1; if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL || eddsa_verify_sig(dgst, pkey, sig) < 0) { fido_log_debug("%s: eddsa_verify_sig", __func__); goto fail; } ok = 0; fail: EVP_PKEY_free(pkey); return (ok); } libfido2-1.10.0/src/err.c000066400000000000000000000102631417126203300150070ustar00rootroot00000000000000/* * 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_CBOR_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_FP_DATABASE_FULL: return "FIDO_ERR_FP_DATABASE_FULL"; case FIDO_ERR_LARGEBLOB_STORAGE_FULL: return "FIDO_ERR_LARGEBLOB_STORAGE_FULL"; 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_USER_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_KEY_STORE_FULL"; case FIDO_ERR_NOT_BUSY: return "FIDO_ERR_NOT_BUSY"; case FIDO_ERR_NO_OPERATION_PENDING: return "FIDO_ERR_NO_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_USER_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_PIN_AUTH_INVALID"; case FIDO_ERR_PIN_AUTH_BLOCKED: return "FIDO_ERR_PIN_AUTH_BLOCKED"; case FIDO_ERR_PIN_NOT_SET: return "FIDO_ERR_PIN_NOT_SET"; case FIDO_ERR_PIN_REQUIRED: return "FIDO_ERR_PIN_REQUIRED"; case FIDO_ERR_PIN_POLICY_VIOLATION: return "FIDO_ERR_PIN_POLICY_VIOLATION"; case FIDO_ERR_PIN_TOKEN_EXPIRED: return "FIDO_ERR_PIN_TOKEN_EXPIRED"; case FIDO_ERR_REQUEST_TOO_LARGE: return "FIDO_ERR_REQUEST_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_UV_BLOCKED: return "FIDO_ERR_UV_BLOCKED"; case FIDO_ERR_UV_INVALID: return "FIDO_ERR_UV_INVALID"; case FIDO_ERR_UNAUTHORIZED_PERM: return "FIDO_ERR_UNAUTHORIZED_PERM"; case FIDO_ERR_ERR_OTHER: return "FIDO_ERR_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_NOTFOUND: return "FIDO_ERR_NOTFOUND"; case FIDO_ERR_COMPRESS: return "FIDO_ERR_COMPRESS"; case FIDO_ERR_INTERNAL: return "FIDO_ERR_INTERNAL"; default: return "FIDO_ERR_UNKNOWN"; } } libfido2-1.10.0/src/es256.c000066400000000000000000000250501417126203300150630ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #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((uint8_t)(-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; freezero(sk, sizeof(*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; freezero(pk, sizeof(*pk)); *pkp = NULL; } int es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len) { const uint8_t *p = ptr; if (len < sizeof(*pk)) return (FIDO_ERR_INVALID_ARGUMENT); if (len == sizeof(*pk) + 1 && *p == 0x04) memcpy(pk, ++p, sizeof(*pk)); /* uncompressed format */ else memcpy(pk, ptr, sizeof(*pk)); /* libfido2 x||y format */ 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) goto fail; BN_CTX_start(bnctx); if ((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_end(bnctx); 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 *bnctx = 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 || (bnctx = BN_CTX_new()) == NULL) goto fail; BN_CTX_start(bnctx); if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL) goto fail; if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 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 (bnctx != NULL) { BN_CTX_end(bnctx); BN_CTX_free(bnctx); } return (ok); } int es256_pk_from_EVP_PKEY(es256_pk_t *pk, const EVP_PKEY *pkey) { EC_KEY *ec; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC || (ec = EVP_PKEY_get0(pkey)) == NULL) return (FIDO_ERR_INVALID_ARGUMENT); return (es256_pk_from_EC_KEY(pk, ec)); } 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) goto fail; BN_CTX_start(bnctx); if ((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_end(bnctx); 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); } int es256_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey, const fido_blob_t *sig) { EVP_PKEY_CTX *pctx = NULL; int ok = -1; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { fido_log_debug("%s: EVP_PKEY_base_id", __func__); goto fail; } if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL || EVP_PKEY_verify_init(pctx) != 1 || EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr, dgst->len) != 1) { fido_log_debug("%s: EVP_PKEY_verify", __func__); goto fail; } ok = 0; fail: EVP_PKEY_CTX_free(pctx); return (ok); } int es256_pk_verify_sig(const fido_blob_t *dgst, const es256_pk_t *pk, const fido_blob_t *sig) { EVP_PKEY *pkey; int ok = -1; if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL || es256_verify_sig(dgst, pkey, sig) < 0) { fido_log_debug("%s: es256_verify_sig", __func__); goto fail; } ok = 0; fail: EVP_PKEY_free(pkey); return (ok); } libfido2-1.10.0/src/export.gnu000066400000000000000000000140671417126203300161150ustar00rootroot00000000000000{ 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_EVP_PKEY; es256_pk_from_ptr; es256_pk_new; es256_pk_to_EVP_PKEY; fido_assert_allow_cred; fido_assert_authdata_len; fido_assert_authdata_ptr; fido_assert_blob_len; fido_assert_blob_ptr; fido_assert_clientdata_hash_len; fido_assert_clientdata_hash_ptr; fido_assert_count; fido_assert_flags; fido_assert_free; fido_assert_hmac_secret_len; fido_assert_hmac_secret_ptr; fido_assert_id_len; fido_assert_id_ptr; fido_assert_largeblob_key_len; fido_assert_largeblob_key_ptr; fido_assert_new; fido_assert_rp_id; fido_assert_set_authdata; fido_assert_set_authdata_raw; fido_assert_set_clientdata; fido_assert_set_clientdata_hash; fido_assert_set_count; fido_assert_set_extensions; fido_assert_set_hmac_salt; fido_assert_set_hmac_secret; fido_assert_set_options; fido_assert_set_rp; fido_assert_set_sig; fido_assert_set_up; fido_assert_set_uv; fido_assert_sigcount; fido_assert_sig_len; fido_assert_sig_ptr; fido_assert_user_display_name; fido_assert_user_icon; fido_assert_user_id_len; fido_assert_user_id_ptr; fido_assert_user_name; fido_assert_verify; fido_bio_dev_enroll_begin; fido_bio_dev_enroll_cancel; fido_bio_dev_enroll_continue; fido_bio_dev_enroll_remove; fido_bio_dev_get_info; fido_bio_dev_get_template_array; fido_bio_dev_set_template_name; fido_bio_enroll_free; fido_bio_enroll_last_status; fido_bio_enroll_new; fido_bio_enroll_remaining_samples; fido_bio_info_free; fido_bio_info_max_samples; fido_bio_info_new; fido_bio_info_type; fido_bio_template; fido_bio_template_array_count; fido_bio_template_array_free; fido_bio_template_array_new; fido_bio_template_free; fido_bio_template_id_len; fido_bio_template_id_ptr; fido_bio_template_name; fido_bio_template_new; fido_bio_template_set_id; fido_bio_template_set_name; fido_cbor_info_aaguid_len; fido_cbor_info_aaguid_ptr; fido_cbor_info_algorithm_cose; fido_cbor_info_algorithm_count; fido_cbor_info_algorithm_type; fido_cbor_info_extensions_len; fido_cbor_info_extensions_ptr; fido_cbor_info_free; fido_cbor_info_maxmsgsiz; fido_cbor_info_maxcredbloblen; fido_cbor_info_maxcredcntlst; fido_cbor_info_maxcredidlen; fido_cbor_info_fwversion; fido_cbor_info_new; fido_cbor_info_options_len; fido_cbor_info_options_name_ptr; fido_cbor_info_options_value_ptr; fido_cbor_info_protocols_len; fido_cbor_info_protocols_ptr; fido_cbor_info_transports_len; fido_cbor_info_transports_ptr; fido_cbor_info_versions_len; fido_cbor_info_versions_ptr; fido_cred_attstmt_len; fido_cred_attstmt_ptr; fido_cred_authdata_len; fido_cred_authdata_ptr; fido_cred_authdata_raw_len; fido_cred_authdata_raw_ptr; fido_cred_clientdata_hash_len; fido_cred_clientdata_hash_ptr; fido_cred_display_name; fido_cred_exclude; fido_cred_flags; fido_cred_largeblob_key_len; fido_cred_largeblob_key_ptr; fido_cred_sigcount; fido_cred_fmt; fido_cred_free; fido_cred_id_len; fido_cred_id_ptr; fido_cred_aaguid_len; fido_cred_aaguid_ptr; fido_credman_del_dev_rk; fido_credman_get_dev_metadata; fido_credman_get_dev_rk; fido_credman_get_dev_rp; fido_credman_metadata_free; fido_credman_metadata_new; fido_credman_rk; fido_credman_rk_count; fido_credman_rk_existing; fido_credman_rk_free; fido_credman_rk_new; fido_credman_rk_remaining; fido_credman_rp_count; fido_credman_rp_free; fido_credman_rp_id; fido_credman_rp_id_hash_len; fido_credman_rp_id_hash_ptr; fido_credman_rp_name; fido_credman_rp_new; fido_credman_set_dev_rk; fido_cred_new; fido_cred_pin_minlen; fido_cred_prot; fido_cred_pubkey_len; fido_cred_pubkey_ptr; fido_cred_rp_id; fido_cred_rp_name; fido_cred_set_attstmt; fido_cred_set_authdata; fido_cred_set_authdata_raw; fido_cred_set_blob; fido_cred_set_clientdata; fido_cred_set_clientdata_hash; fido_cred_set_extensions; fido_cred_set_fmt; fido_cred_set_id; fido_cred_set_options; fido_cred_set_pin_minlen; fido_cred_set_prot; fido_cred_set_rk; fido_cred_set_rp; fido_cred_set_sig; fido_cred_set_type; fido_cred_set_user; fido_cred_set_uv; fido_cred_set_x509; fido_cred_sig_len; fido_cred_sig_ptr; fido_cred_type; fido_cred_user_id_len; fido_cred_user_id_ptr; fido_cred_user_name; fido_cred_verify; fido_cred_verify_self; fido_cred_x5c_len; fido_cred_x5c_ptr; fido_dev_build; fido_dev_cancel; fido_dev_close; fido_dev_enable_entattest; fido_dev_flags; fido_dev_force_fido2; fido_dev_force_pin_change; fido_dev_force_u2f; fido_dev_free; fido_dev_get_assert; fido_dev_get_cbor_info; fido_dev_get_retry_count; fido_dev_get_uv_retry_count; fido_dev_get_touch_begin; fido_dev_get_touch_status; fido_dev_has_pin; fido_dev_has_uv; fido_dev_info_free; fido_dev_info_manifest; fido_dev_info_manufacturer_string; fido_dev_info_new; fido_dev_info_path; fido_dev_info_product; fido_dev_info_product_string; fido_dev_info_ptr; fido_dev_info_set; fido_dev_info_vendor; fido_dev_io_handle; fido_dev_is_fido2; fido_dev_is_winhello; fido_dev_major; fido_dev_make_cred; fido_dev_minor; fido_dev_new; fido_dev_new_with_info; fido_dev_open; fido_dev_open_with_info; fido_dev_protocol; fido_dev_reset; fido_dev_set_io_functions; fido_dev_set_pin; fido_dev_set_pin_minlen; fido_dev_set_pin_minlen_rpid; fido_dev_set_sigmask; fido_dev_set_timeout; fido_dev_set_transport_functions; fido_dev_supports_cred_prot; fido_dev_supports_credman; fido_dev_supports_permissions; fido_dev_supports_pin; fido_dev_supports_uv; fido_dev_toggle_always_uv; fido_dev_largeblob_get; fido_dev_largeblob_get_array; fido_dev_largeblob_remove; fido_dev_largeblob_set; fido_dev_largeblob_set_array; fido_init; fido_set_log_handler; fido_strerr; rs256_pk_free; rs256_pk_from_ptr; rs256_pk_from_EVP_PKEY; rs256_pk_from_RSA; rs256_pk_new; rs256_pk_to_EVP_PKEY; local: *; }; libfido2-1.10.0/src/export.llvm000066400000000000000000000130701417126203300162670ustar00rootroot00000000000000_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_EVP_PKEY _es256_pk_from_ptr _es256_pk_new _es256_pk_to_EVP_PKEY _fido_assert_allow_cred _fido_assert_authdata_len _fido_assert_authdata_ptr _fido_assert_blob_len _fido_assert_blob_ptr _fido_assert_clientdata_hash_len _fido_assert_clientdata_hash_ptr _fido_assert_count _fido_assert_flags _fido_assert_free _fido_assert_hmac_secret_len _fido_assert_hmac_secret_ptr _fido_assert_id_len _fido_assert_id_ptr _fido_assert_largeblob_key_len _fido_assert_largeblob_key_ptr _fido_assert_new _fido_assert_rp_id _fido_assert_set_authdata _fido_assert_set_authdata_raw _fido_assert_set_clientdata _fido_assert_set_clientdata_hash _fido_assert_set_count _fido_assert_set_extensions _fido_assert_set_hmac_salt _fido_assert_set_hmac_secret _fido_assert_set_options _fido_assert_set_rp _fido_assert_set_sig _fido_assert_set_up _fido_assert_set_uv _fido_assert_sigcount _fido_assert_sig_len _fido_assert_sig_ptr _fido_assert_user_display_name _fido_assert_user_icon _fido_assert_user_id_len _fido_assert_user_id_ptr _fido_assert_user_name _fido_assert_verify _fido_bio_dev_enroll_begin _fido_bio_dev_enroll_cancel _fido_bio_dev_enroll_continue _fido_bio_dev_enroll_remove _fido_bio_dev_get_info _fido_bio_dev_get_template_array _fido_bio_dev_set_template_name _fido_bio_enroll_free _fido_bio_enroll_last_status _fido_bio_enroll_new _fido_bio_enroll_remaining_samples _fido_bio_info_free _fido_bio_info_max_samples _fido_bio_info_new _fido_bio_info_type _fido_bio_template _fido_bio_template_array_count _fido_bio_template_array_free _fido_bio_template_array_new _fido_bio_template_free _fido_bio_template_id_len _fido_bio_template_id_ptr _fido_bio_template_name _fido_bio_template_new _fido_bio_template_set_id _fido_bio_template_set_name _fido_cbor_info_aaguid_len _fido_cbor_info_aaguid_ptr _fido_cbor_info_algorithm_cose _fido_cbor_info_algorithm_count _fido_cbor_info_algorithm_type _fido_cbor_info_extensions_len _fido_cbor_info_extensions_ptr _fido_cbor_info_free _fido_cbor_info_maxmsgsiz _fido_cbor_info_maxcredbloblen _fido_cbor_info_maxcredcntlst _fido_cbor_info_maxcredidlen _fido_cbor_info_fwversion _fido_cbor_info_new _fido_cbor_info_options_len _fido_cbor_info_options_name_ptr _fido_cbor_info_options_value_ptr _fido_cbor_info_protocols_len _fido_cbor_info_protocols_ptr _fido_cbor_info_transports_len _fido_cbor_info_transports_ptr _fido_cbor_info_versions_len _fido_cbor_info_versions_ptr _fido_cred_attstmt_len _fido_cred_attstmt_ptr _fido_cred_authdata_len _fido_cred_authdata_ptr _fido_cred_authdata_raw_len _fido_cred_authdata_raw_ptr _fido_cred_clientdata_hash_len _fido_cred_clientdata_hash_ptr _fido_cred_display_name _fido_cred_exclude _fido_cred_flags _fido_cred_largeblob_key_len _fido_cred_largeblob_key_ptr _fido_cred_sigcount _fido_cred_fmt _fido_cred_free _fido_cred_id_len _fido_cred_id_ptr _fido_cred_aaguid_len _fido_cred_aaguid_ptr _fido_credman_del_dev_rk _fido_credman_get_dev_metadata _fido_credman_get_dev_rk _fido_credman_get_dev_rp _fido_credman_metadata_free _fido_credman_metadata_new _fido_credman_rk _fido_credman_rk_count _fido_credman_rk_existing _fido_credman_rk_free _fido_credman_rk_new _fido_credman_rk_remaining _fido_credman_rp_count _fido_credman_rp_free _fido_credman_rp_id _fido_credman_rp_id_hash_len _fido_credman_rp_id_hash_ptr _fido_credman_rp_name _fido_credman_rp_new _fido_credman_set_dev_rk _fido_cred_new _fido_cred_pin_minlen _fido_cred_prot _fido_cred_pubkey_len _fido_cred_pubkey_ptr _fido_cred_rp_id _fido_cred_rp_name _fido_cred_set_attstmt _fido_cred_set_authdata _fido_cred_set_authdata_raw _fido_cred_set_blob _fido_cred_set_clientdata _fido_cred_set_clientdata_hash _fido_cred_set_extensions _fido_cred_set_fmt _fido_cred_set_id _fido_cred_set_options _fido_cred_set_pin_minlen _fido_cred_set_prot _fido_cred_set_rk _fido_cred_set_rp _fido_cred_set_sig _fido_cred_set_type _fido_cred_set_user _fido_cred_set_uv _fido_cred_set_x509 _fido_cred_sig_len _fido_cred_sig_ptr _fido_cred_type _fido_cred_user_id_len _fido_cred_user_id_ptr _fido_cred_user_name _fido_cred_verify _fido_cred_verify_self _fido_cred_x5c_len _fido_cred_x5c_ptr _fido_dev_build _fido_dev_cancel _fido_dev_close _fido_dev_enable_entattest _fido_dev_flags _fido_dev_force_fido2 _fido_dev_force_pin_change _fido_dev_force_u2f _fido_dev_free _fido_dev_get_assert _fido_dev_get_cbor_info _fido_dev_get_retry_count _fido_dev_get_uv_retry_count _fido_dev_get_touch_begin _fido_dev_get_touch_status _fido_dev_has_pin _fido_dev_has_uv _fido_dev_info_free _fido_dev_info_manifest _fido_dev_info_manufacturer_string _fido_dev_info_new _fido_dev_info_path _fido_dev_info_product _fido_dev_info_product_string _fido_dev_info_ptr _fido_dev_info_set _fido_dev_info_vendor _fido_dev_io_handle _fido_dev_is_fido2 _fido_dev_is_winhello _fido_dev_major _fido_dev_make_cred _fido_dev_minor _fido_dev_new _fido_dev_new_with_info _fido_dev_open _fido_dev_open_with_info _fido_dev_protocol _fido_dev_reset _fido_dev_set_io_functions _fido_dev_set_pin _fido_dev_set_pin_minlen _fido_dev_set_pin_minlen_rpid _fido_dev_set_sigmask _fido_dev_set_timeout _fido_dev_set_transport_functions _fido_dev_supports_cred_prot _fido_dev_supports_credman _fido_dev_supports_permissions _fido_dev_supports_pin _fido_dev_supports_uv _fido_dev_toggle_always_uv _fido_dev_largeblob_get _fido_dev_largeblob_get_array _fido_dev_largeblob_remove _fido_dev_largeblob_set _fido_dev_largeblob_set_array _fido_init _fido_set_log_handler _fido_strerr _rs256_pk_free _rs256_pk_from_ptr _rs256_pk_from_EVP_PKEY _rs256_pk_from_RSA _rs256_pk_new _rs256_pk_to_EVP_PKEY libfido2-1.10.0/src/export.msvc000066400000000000000000000125161417126203300162710ustar00rootroot00000000000000EXPORTS 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_EVP_PKEY es256_pk_from_ptr es256_pk_new es256_pk_to_EVP_PKEY fido_assert_allow_cred fido_assert_authdata_len fido_assert_authdata_ptr fido_assert_blob_len fido_assert_blob_ptr fido_assert_clientdata_hash_len fido_assert_clientdata_hash_ptr fido_assert_count fido_assert_flags fido_assert_free fido_assert_hmac_secret_len fido_assert_hmac_secret_ptr fido_assert_id_len fido_assert_id_ptr fido_assert_largeblob_key_len fido_assert_largeblob_key_ptr fido_assert_new fido_assert_rp_id fido_assert_set_authdata fido_assert_set_authdata_raw fido_assert_set_clientdata fido_assert_set_clientdata_hash fido_assert_set_count fido_assert_set_extensions fido_assert_set_hmac_salt fido_assert_set_hmac_secret fido_assert_set_options fido_assert_set_rp fido_assert_set_sig fido_assert_set_up fido_assert_set_uv fido_assert_sigcount fido_assert_sig_len fido_assert_sig_ptr fido_assert_user_display_name fido_assert_user_icon fido_assert_user_id_len fido_assert_user_id_ptr fido_assert_user_name fido_assert_verify fido_bio_dev_enroll_begin fido_bio_dev_enroll_cancel fido_bio_dev_enroll_continue fido_bio_dev_enroll_remove fido_bio_dev_get_info fido_bio_dev_get_template_array fido_bio_dev_set_template_name fido_bio_enroll_free fido_bio_enroll_last_status fido_bio_enroll_new fido_bio_enroll_remaining_samples fido_bio_info_free fido_bio_info_max_samples fido_bio_info_new fido_bio_info_type fido_bio_template fido_bio_template_array_count fido_bio_template_array_free fido_bio_template_array_new fido_bio_template_free fido_bio_template_id_len fido_bio_template_id_ptr fido_bio_template_name fido_bio_template_new fido_bio_template_set_id fido_bio_template_set_name fido_cbor_info_aaguid_len fido_cbor_info_aaguid_ptr fido_cbor_info_algorithm_cose fido_cbor_info_algorithm_count fido_cbor_info_algorithm_type fido_cbor_info_extensions_len fido_cbor_info_extensions_ptr fido_cbor_info_free fido_cbor_info_maxmsgsiz fido_cbor_info_maxcredbloblen fido_cbor_info_maxcredcntlst fido_cbor_info_maxcredidlen fido_cbor_info_fwversion fido_cbor_info_new fido_cbor_info_options_len fido_cbor_info_options_name_ptr fido_cbor_info_options_value_ptr fido_cbor_info_protocols_len fido_cbor_info_protocols_ptr fido_cbor_info_transports_len fido_cbor_info_transports_ptr fido_cbor_info_versions_len fido_cbor_info_versions_ptr fido_cred_attstmt_len fido_cred_attstmt_ptr fido_cred_authdata_len fido_cred_authdata_ptr fido_cred_authdata_raw_len fido_cred_authdata_raw_ptr fido_cred_clientdata_hash_len fido_cred_clientdata_hash_ptr fido_cred_display_name fido_cred_exclude fido_cred_flags fido_cred_largeblob_key_len fido_cred_largeblob_key_ptr fido_cred_sigcount fido_cred_fmt fido_cred_free fido_cred_id_len fido_cred_id_ptr fido_cred_aaguid_len fido_cred_aaguid_ptr fido_credman_del_dev_rk fido_credman_get_dev_metadata fido_credman_get_dev_rk fido_credman_get_dev_rp fido_credman_metadata_free fido_credman_metadata_new fido_credman_rk fido_credman_rk_count fido_credman_rk_existing fido_credman_rk_free fido_credman_rk_new fido_credman_rk_remaining fido_credman_rp_count fido_credman_rp_free fido_credman_rp_id fido_credman_rp_id_hash_len fido_credman_rp_id_hash_ptr fido_credman_rp_name fido_credman_rp_new fido_credman_set_dev_rk fido_cred_new fido_cred_pin_minlen fido_cred_prot fido_cred_pubkey_len fido_cred_pubkey_ptr fido_cred_rp_id fido_cred_rp_name fido_cred_set_attstmt fido_cred_set_authdata fido_cred_set_authdata_raw fido_cred_set_blob fido_cred_set_clientdata fido_cred_set_clientdata_hash fido_cred_set_extensions fido_cred_set_fmt fido_cred_set_id fido_cred_set_options fido_cred_set_pin_minlen fido_cred_set_prot fido_cred_set_rk fido_cred_set_rp fido_cred_set_sig fido_cred_set_type fido_cred_set_user fido_cred_set_uv fido_cred_set_x509 fido_cred_sig_len fido_cred_sig_ptr fido_cred_type fido_cred_user_id_len fido_cred_user_id_ptr fido_cred_user_name fido_cred_verify fido_cred_verify_self fido_cred_x5c_len fido_cred_x5c_ptr fido_dev_build fido_dev_cancel fido_dev_close fido_dev_enable_entattest fido_dev_flags fido_dev_force_fido2 fido_dev_force_pin_change fido_dev_force_u2f fido_dev_free fido_dev_get_assert fido_dev_get_cbor_info fido_dev_get_retry_count fido_dev_get_uv_retry_count fido_dev_get_touch_begin fido_dev_get_touch_status fido_dev_has_pin fido_dev_has_uv fido_dev_info_free fido_dev_info_manifest fido_dev_info_manufacturer_string fido_dev_info_new fido_dev_info_path fido_dev_info_product fido_dev_info_product_string fido_dev_info_ptr fido_dev_info_set fido_dev_info_vendor fido_dev_io_handle fido_dev_is_fido2 fido_dev_is_winhello fido_dev_major fido_dev_make_cred fido_dev_minor fido_dev_new fido_dev_new_with_info fido_dev_open fido_dev_open_with_info fido_dev_protocol fido_dev_reset fido_dev_set_io_functions fido_dev_set_pin fido_dev_set_pin_minlen fido_dev_set_pin_minlen_rpid fido_dev_set_sigmask fido_dev_set_timeout fido_dev_set_transport_functions fido_dev_supports_cred_prot fido_dev_supports_credman fido_dev_supports_permissions fido_dev_supports_pin fido_dev_supports_uv fido_dev_toggle_always_uv fido_dev_largeblob_get fido_dev_largeblob_get_array fido_dev_largeblob_remove fido_dev_largeblob_set fido_dev_largeblob_set_array fido_init fido_set_log_handler fido_strerr rs256_pk_free rs256_pk_from_ptr rs256_pk_from_EVP_PKEY rs256_pk_from_RSA rs256_pk_new rs256_pk_to_EVP_PKEY libfido2-1.10.0/src/extern.h000066400000000000000000000241641417126203300155360ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _EXTERN_H #define _EXTERN_H #ifdef __MINGW32__ #include #endif #ifdef HAVE_SIGNAL_H #include #endif #include #include "fido/types.h" #include "blob.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* aes256 */ int aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *, const fido_blob_t *, fido_blob_t *); int aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *, const fido_blob_t *, fido_blob_t *); int aes256_gcm_dec(const fido_blob_t *, const fido_blob_t *, const fido_blob_t *, const fido_blob_t *, fido_blob_t *); int aes256_gcm_enc(const fido_blob_t *, const fido_blob_t *, const fido_blob_t *, const fido_blob_t *, fido_blob_t *); /* cbor encoding functions */ cbor_item_t *cbor_build_uint(const uint64_t); cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t); cbor_item_t *cbor_encode_assert_opt(fido_opt_t, fido_opt_t); cbor_item_t *cbor_encode_change_pin_auth(const fido_dev_t *, const fido_blob_t *, const fido_blob_t *, const fido_blob_t *); cbor_item_t *cbor_encode_cred_ext(const fido_cred_ext_t *, const fido_blob_t *); cbor_item_t *cbor_encode_assert_ext(fido_dev_t *, const fido_assert_ext_t *, const fido_blob_t *, const es256_pk_t *); cbor_item_t *cbor_encode_cred_opt(fido_opt_t, fido_opt_t); cbor_item_t *cbor_encode_pin_auth(const fido_dev_t *, const fido_blob_t *, const fido_blob_t *); cbor_item_t *cbor_encode_pin_opt(const fido_dev_t *); 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_str_array(const fido_str_array_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 *, fido_cred_ext_t *); int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *, fido_authdata_t *, fido_assert_extattr_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_uv_params(fido_dev_t *, uint8_t, const fido_blob_t *, const es256_pk_t *, const fido_blob_t *, const char *, const char *, cbor_item_t **, cbor_item_t **, int *); void cbor_vector_free(cbor_item_t **, size_t); int cbor_array_append(cbor_item_t **, cbor_item_t *); int cbor_array_drop(cbor_item_t **, size_t); /* deflate */ int fido_compress(fido_blob_t *, const fido_blob_t *); int fido_uncompress(fido_blob_t *, const fido_blob_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); int fido_hid_get_usage(const uint8_t *, size_t, uint32_t *); int fido_hid_get_report_len(const uint8_t *, size_t, size_t *, size_t *); int fido_hid_unix_open(const char *); int fido_hid_unix_wait(int, int, const fido_sigset_t *); int fido_hid_set_sigmask(void *, const fido_sigset_t *); size_t fido_hid_report_in_len(void *); size_t fido_hid_report_out_len(void *); /* nfc i/o */ void *fido_nfc_open(const char *); void fido_nfc_close(void *); int fido_nfc_read(void *, unsigned char *, size_t, int); int fido_nfc_write(void *, const unsigned char *, size_t); int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int); int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t); int fido_nfc_set_sigmask(void *, const fido_sigset_t *); /* windows hello */ int fido_winhello_manifest(fido_dev_info_t *, size_t, size_t *); int fido_winhello_open(fido_dev_t *); int fido_winhello_close(fido_dev_t *); int fido_winhello_cancel(fido_dev_t *); int fido_winhello_get_assert(fido_dev_t *, fido_assert_t *, const char *, int); int fido_winhello_get_cbor_info(fido_dev_t *, fido_cbor_info_t *); int fido_winhello_make_cred(fido_dev_t *, fido_cred_t *, const char *, int); /* 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, int *); /* 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) #define fido_log_error(...) 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, const char *, ...) __attribute__((__format__ (printf, 3, 4))); void fido_log_error(int, const char *, ...) __attribute__((__format__ (printf, 2, 3))); #else void fido_log_init(void); void fido_log_debug(const char *, ...); void fido_log_xxd(const void *, size_t, const char *, ...); void fido_log_error(int, const char *, ...); #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 *); int u2f_get_touch_begin(fido_dev_t *, int *); int u2f_get_touch_status(fido_dev_t *, int *, int *); /* unexposed fido ops */ uint8_t fido_dev_get_pin_protocol(const fido_dev_t *); int fido_dev_authkey(fido_dev_t *, es256_pk_t *, int *); int fido_dev_get_cbor_info_wait(fido_dev_t *, fido_cbor_info_t *, int *); int fido_dev_get_uv_token(fido_dev_t *, uint8_t, const char *, const fido_blob_t *, const es256_pk_t *, const char *, fido_blob_t *, int *); uint64_t fido_dev_maxmsgsize(const fido_dev_t *); int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **, int *); /* types */ void fido_algo_array_free(fido_algo_array_t *); void fido_byte_array_free(fido_byte_array_t *); void fido_opt_array_free(fido_opt_array_t *); void fido_str_array_free(fido_str_array_t *); void fido_algo_free(fido_algo_t *); int fido_str_array_pack(fido_str_array_t *, const char * const *, size_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 *); void fido_cbor_info_reset(fido_cbor_info_t *); int fido_blob_serialise(fido_blob_t *, const cbor_item_t *); int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t); int fido_check_rp_id(const char *, const unsigned char *); int fido_get_random(void *, size_t); int fido_sha256(fido_blob_t *, const u_char *, size_t); int fido_time_now(struct timespec *); int fido_time_delta(const struct timespec *, int *); /* crypto */ int es256_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *); int rs256_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *); int eddsa_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *); int rs1_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *); int es256_pk_verify_sig(const fido_blob_t *, const es256_pk_t *, const fido_blob_t *); int rs256_pk_verify_sig(const fido_blob_t *, const rs256_pk_t *, const fido_blob_t *); int eddsa_pk_verify_sig(const fido_blob_t *, const eddsa_pk_t *, const fido_blob_t *); int fido_get_signed_hash(int, fido_blob_t *, const fido_blob_t *, const fido_blob_t *); int fido_get_signed_hash_tpm(fido_blob_t *, const fido_blob_t *, const fido_blob_t *, const fido_attstmt_t *, const fido_attcred_t *); /* device manifest functions */ int fido_hid_manifest(fido_dev_info_t *, size_t, size_t *); int fido_nfc_manifest(fido_dev_info_t *, size_t, size_t *); /* device manifest registration */ typedef int (*dev_manifest_func_t)(fido_dev_info_t *, size_t, size_t *); int fido_dev_register_manifest_func(const dev_manifest_func_t); void fido_dev_unregister_manifest_func(const dev_manifest_func_t); /* fuzzing instrumentation */ #ifdef FIDO_FUZZ uint32_t uniform_random(uint32_t); #endif /* internal device capability flags */ #define FIDO_DEV_PIN_SET 0x001 #define FIDO_DEV_PIN_UNSET 0x002 #define FIDO_DEV_CRED_PROT 0x004 #define FIDO_DEV_CREDMAN 0x008 #define FIDO_DEV_PIN_PROTOCOL1 0x010 #define FIDO_DEV_PIN_PROTOCOL2 0x020 #define FIDO_DEV_UV_SET 0x040 #define FIDO_DEV_UV_UNSET 0x080 #define FIDO_DEV_TOKEN_PERMS 0x100 #define FIDO_DEV_WINHELLO 0x200 /* miscellanea */ #define FIDO_DUMMY_CLIENTDATA "" #define FIDO_DUMMY_RP_ID "localhost" #define FIDO_DUMMY_USER_NAME "dummy" #define FIDO_DUMMY_USER_ID 1 #define FIDO_WINHELLO_PATH "windows://hello" #define FIDO_NFC_PREFIX "nfc:" #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_EXTERN_H */ libfido2-1.10.0/src/fido.h000066400000000000000000000261051417126203300151470ustar00rootroot00000000000000/* * 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 #ifdef _FIDO_INTERNAL #include #include #include #include "../openbsd-compat/openbsd-compat.h" #include "blob.h" #include "iso7816.h" #include "extern.h" #endif #include "fido/err.h" #include "fido/param.h" #include "fido/types.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ fido_assert_t *fido_assert_new(void); fido_cred_t *fido_cred_new(void); fido_dev_t *fido_dev_new(void); fido_dev_t *fido_dev_new_with_info(const fido_dev_info_t *); fido_dev_info_t *fido_dev_info_new(size_t); fido_cbor_info_t *fido_cbor_info_new(void); void *fido_dev_io_handle(const fido_dev_t *); 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 #define FIDO_DISABLE_U2F_FALLBACK 0x02 void fido_init(int); void fido_set_log_handler(fido_log_handler_t *); 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_largeblob_key_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); const unsigned char *fido_assert_blob_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_transports_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_cbor_info_algorithm_type(const fido_cbor_info_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_aaguid_ptr(const fido_cred_t *); const unsigned char *fido_cred_attstmt_ptr(const fido_cred_t *); const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *); const unsigned char *fido_cred_authdata_raw_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_largeblob_key_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_user_id_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(fido_assert_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_hmac_secret(fido_assert_t *, size_t, const unsigned char *, size_t); int fido_assert_set_options(fido_assert_t *, bool, bool); 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_cbor_info_algorithm_cose(const fido_cbor_info_t *, size_t); int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t); int fido_cred_prot(const fido_cred_t *); int fido_cred_set_attstmt(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_blob(fido_cred_t *, const unsigned char *, size_t); int fido_cred_set_clientdata(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_id(fido_cred_t *, const unsigned char *, size_t); int fido_cred_set_options(fido_cred_t *, bool, bool); int fido_cred_set_pin_minlen(fido_cred_t *, size_t); int fido_cred_set_prot(fido_cred_t *, int); 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_set_sigmask(fido_dev_t *, const fido_sigset_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_get_uv_retry_count(fido_dev_t *, int *); int fido_dev_get_touch_begin(fido_dev_t *); int fido_dev_get_touch_status(fido_dev_t *, int *, int); int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *); int fido_dev_info_set(fido_dev_info_t *, size_t, const char *, const char *, const char *, const fido_dev_io_t *, const fido_dev_transport_t *); int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *); int fido_dev_open_with_info(fido_dev_t *); 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 *); int fido_dev_set_transport_functions(fido_dev_t *, const fido_dev_transport_t *); int fido_dev_set_timeout(fido_dev_t *, int); 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_largeblob_key_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_assert_blob_len(const fido_assert_t *, size_t); size_t fido_cbor_info_aaguid_len(const fido_cbor_info_t *); size_t fido_cbor_info_algorithm_count(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_transports_len(const fido_cbor_info_t *); size_t fido_cbor_info_versions_len(const fido_cbor_info_t *); size_t fido_cred_aaguid_len(const fido_cred_t *); size_t fido_cred_attstmt_len(const fido_cred_t *); size_t fido_cred_authdata_len(const fido_cred_t *); size_t fido_cred_authdata_raw_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_largeblob_key_len(const fido_cred_t *); size_t fido_cred_pin_minlen(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_user_id_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 *); uint32_t fido_cred_sigcount(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 *); uint64_t fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *); uint64_t fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *); uint64_t fido_cbor_info_maxcredidlen(const fido_cbor_info_t *); uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *); bool fido_dev_has_pin(const fido_dev_t *); bool fido_dev_has_uv(const fido_dev_t *); bool fido_dev_is_fido2(const fido_dev_t *); bool fido_dev_is_winhello(const fido_dev_t *); bool fido_dev_supports_credman(const fido_dev_t *); bool fido_dev_supports_cred_prot(const fido_dev_t *); bool fido_dev_supports_permissions(const fido_dev_t *); bool fido_dev_supports_pin(const fido_dev_t *); bool fido_dev_supports_uv(const fido_dev_t *); int fido_dev_largeblob_get(fido_dev_t *, const unsigned char *, size_t, unsigned char **, size_t *); int fido_dev_largeblob_set(fido_dev_t *, const unsigned char *, size_t, const unsigned char *, size_t, const char *); int fido_dev_largeblob_remove(fido_dev_t *, const unsigned char *, size_t, const char *); int fido_dev_largeblob_get_array(fido_dev_t *, unsigned char **, size_t *); int fido_dev_largeblob_set_array(fido_dev_t *, const unsigned char *, size_t, const char *); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_H */ libfido2-1.10.0/src/fido/000077500000000000000000000000001417126203300147725ustar00rootroot00000000000000libfido2-1.10.0/src/fido/bio.h000066400000000000000000000071751417126203300157260ustar00rootroot00000000000000/* * 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 #ifdef _FIDO_INTERNAL #include "blob.h" #include "fido/err.h" #include "fido/param.h" #include "fido/types.h" #else #include #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #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 **); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_BIO_H */ libfido2-1.10.0/src/fido/config.h000066400000000000000000000015731417126203300164160ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _FIDO_CONFIG_H #define _FIDO_CONFIG_H #ifdef _FIDO_INTERNAL #include "blob.h" #include "fido/err.h" #include "fido/param.h" #include "fido/types.h" #else #include #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ int fido_dev_enable_entattest(fido_dev_t *, const char *); int fido_dev_force_pin_change(fido_dev_t *, const char *); int fido_dev_toggle_always_uv(fido_dev_t *, const char *); int fido_dev_set_pin_minlen(fido_dev_t *, size_t, const char *); int fido_dev_set_pin_minlen_rpid(fido_dev_t *, const char * const *, size_t, const char *); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_CONFIG_H */ libfido2-1.10.0/src/fido/credman.h000066400000000000000000000051221417126203300165540ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _FIDO_CREDMAN_H #define _FIDO_CREDMAN_H #include #include #ifdef _FIDO_INTERNAL #include "blob.h" #include "fido/err.h" #include "fido/param.h" #include "fido/types.h" #else #include #include #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #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 *); int fido_credman_set_dev_rk(fido_dev_t *, fido_cred_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 **); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_CREDMAN_H */ libfido2-1.10.0/src/fido/eddsa.h000066400000000000000000000022271417126203300162260ustar00rootroot00000000000000/* * 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 #ifdef _FIDO_INTERNAL #include "types.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ 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) #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 */ #endif /* _FIDO_INTERNAL */ #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_EDDSA_H */ libfido2-1.10.0/src/fido/err.h000066400000000000000000000051561417126203300157420ustar00rootroot00000000000000/* * 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_FP_DATABASE_FULL 0x17 #define FIDO_ERR_LARGEBLOB_STORAGE_FULL 0x18 #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_UV_BLOCKED 0x3c #define FIDO_ERR_UV_INVALID 0x3f #define FIDO_ERR_UNAUTHORIZED_PERM 0x40 #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 #define FIDO_ERR_NOTFOUND -10 #define FIDO_ERR_COMPRESS -11 #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ const char *fido_strerr(int); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* _FIDO_ERR_H */ libfido2-1.10.0/src/fido/es256.h000066400000000000000000000022041417126203300160050ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _FIDO_ES256_H #define _FIDO_ES256_H #include #include #include #ifdef _FIDO_INTERNAL #include "types.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ 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_EVP_PKEY(es256_pk_t *, const EVP_PKEY *); 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 #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_ES256_H */ libfido2-1.10.0/src/fido/param.h000066400000000000000000000064351417126203300162530ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #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_LARGEBLOB 0x0c #define CTAP_CBOR_CONFIG 0x0d #define CTAP_CBOR_BIO_ENROLL_PRE 0x40 #define CTAP_CBOR_CRED_MGMT_PRE 0x41 /* Supported CTAP PIN/UV Auth Protocols. */ #define CTAP_PIN_PROTOCOL1 1 #define CTAP_PIN_PROTOCOL2 2 /* 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 SW1_MORE_DATA 0x61 #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 #define CTAP_INIT_HEADER_LEN 7 #define CTAP_CONT_HEADER_LEN 5 /* Maximum length of a CTAP HID report in bytes. */ #define CTAP_MAX_REPORT_LEN 64 /* Minimum length of a CTAP HID report in bytes. */ #define CTAP_MIN_REPORT_LEN (CTAP_INIT_HEADER_LEN + 1) /* Randomness device on UNIX-like platforms. */ #ifndef FIDO_RANDOM_DEV #define FIDO_RANDOM_DEV "/dev/urandom" #endif /* Maximum message size in bytes. */ #ifndef FIDO_MAXMSG #define FIDO_MAXMSG 2048 #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_UNSPEC 0 #define COSE_ES256 -7 #define COSE_EDDSA -8 #define COSE_ECDH_ES256 -25 #define COSE_RS256 -257 #define COSE_RS1 -65535 /* 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 #define FIDO_EXT_CRED_PROTECT 0x02 #define FIDO_EXT_LARGEBLOB_KEY 0x04 #define FIDO_EXT_CRED_BLOB 0x08 #define FIDO_EXT_MINPINLEN 0x10 /* Supported credential protection policies. */ #define FIDO_CRED_PROT_UV_OPTIONAL 0x01 #define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0x02 #define FIDO_CRED_PROT_UV_REQUIRED 0x03 #ifdef _FIDO_INTERNAL #define FIDO_EXT_ASSERT_MASK (FIDO_EXT_HMAC_SECRET|FIDO_EXT_LARGEBLOB_KEY| \ FIDO_EXT_CRED_BLOB) #define FIDO_EXT_CRED_MASK (FIDO_EXT_HMAC_SECRET|FIDO_EXT_CRED_PROTECT| \ FIDO_EXT_LARGEBLOB_KEY|FIDO_EXT_CRED_BLOB| \ FIDO_EXT_MINPINLEN) #endif /* _FIDO_INTERNAL */ #endif /* !_FIDO_PARAM_H */ libfido2-1.10.0/src/fido/rs256.h000066400000000000000000000014341417126203300160260ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _FIDO_RS256_H #define _FIDO_RS256_H #include #include #include #ifdef _FIDO_INTERNAL #include "types.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ 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_EVP_PKEY(rs256_pk_t *, const EVP_PKEY *); int rs256_pk_from_RSA(rs256_pk_t *, const RSA *); int rs256_pk_from_ptr(rs256_pk_t *, const void *, size_t); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_RS256_H */ libfido2-1.10.0/src/fido/types.h000066400000000000000000000233311417126203300163110ustar00rootroot00000000000000/* * 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_TYPES_H #define _FIDO_TYPES_H #ifdef __MINGW32__ #include #endif #include #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ struct fido_dev; 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 int fido_dev_rx_t(struct fido_dev *, uint8_t, unsigned char *, size_t, int); typedef int fido_dev_tx_t(struct fido_dev *, uint8_t, 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 struct fido_dev_transport { fido_dev_rx_t *rx; fido_dev_tx_t *tx; } fido_dev_transport_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; typedef void fido_log_handler_t(const char *); #ifdef _WIN32 typedef int fido_sigset_t; #else typedef sigset_t fido_sigset_t; #endif #ifdef _FIDO_INTERNAL #include "packed.h" #include "blob.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 certinfo; /* tpm attestation TPMS_ATTEST structure */ fido_blob_t pubarea; /* tpm attestation TPMT_PUBLIC structure */ fido_blob_t cbor; /* cbor-encoded attestation statement */ fido_blob_t x5c; /* attestation certificate */ fido_blob_t sig; /* attestation signature */ int alg; /* attestation algorithm (cose) */ } 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_ext { int mask; /* enabled extensions */ int prot; /* protection policy */ size_t minpinlen; /* minimum pin length */ } fido_cred_ext_t; typedef struct fido_cred { fido_blob_t cd; /* client data */ 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 */ fido_cred_ext_t ext; /* extensions */ int type; /* cose algorithm */ char *fmt; /* credential format */ fido_cred_ext_t authdata_ext; /* decoded extensions */ fido_blob_t authdata_cbor; /* cbor-encoded payload */ fido_blob_t authdata_raw; /* cbor-decoded 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_blob_t largeblob_key; /* decoded large blob key */ fido_blob_t blob; /* CTAP 2.1 credBlob */ } fido_cred_t; typedef struct fido_assert_extattr { int mask; /* decoded extensions */ fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */ fido_blob_t blob; /* decoded CTAP 2.1 credBlob */ } fido_assert_extattr_t; typedef struct _fido_assert_stmt { fido_blob_t id; /* credential id */ fido_user_t user; /* user attributes */ fido_blob_t hmac_secret; /* hmac secret */ fido_assert_extattr_t 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_blob_t largeblob_key; /* decoded large blob key */ } fido_assert_stmt; typedef struct fido_assert_ext { int mask; /* enabled extensions */ fido_blob_t hmac_salt; /* optional hmac-secret salt */ } fido_assert_ext_t; typedef struct fido_assert { char *rp_id; /* relying party id */ fido_blob_t cd; /* client data */ fido_blob_t cdh; /* client data hash */ fido_blob_array_t allow_list; /* list of allowed credentials */ fido_opt_t up; /* user presence */ fido_opt_t uv; /* user verification */ fido_assert_ext_t 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_algo { char *type; int cose; } fido_algo_t; typedef struct fido_algo_array { fido_algo_t *ptr; size_t len; } fido_algo_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 */ fido_str_array_t transports; /* list of supported transports */ 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_algo_array_t algorithms; /* list of supported algorithms */ uint64_t maxcredcntlst; /* max number of credentials in list */ uint64_t maxcredidlen; /* max credential ID length */ uint64_t fwversion; /* firmware version */ uint64_t maxcredbloblen; /* max credBlob length */ } 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_io_t io; /* i/o functions */ fido_dev_transport_t transport; /* transport functions */ } 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 */ char *path; /* device path */ void *io_handle; /* abstract i/o handle */ fido_dev_io_t io; /* i/o functions */ bool io_own; /* device has own io/transport */ size_t rx_len; /* length of HID input reports */ size_t tx_len; /* length of HID output reports */ int flags; /* internal flags; see FIDO_DEV_* */ fido_dev_transport_t transport; /* transport functions */ uint64_t maxmsgsize; /* max message size */ int timeout_ms; /* read timeout in ms */ } fido_dev_t; #else 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_INTERNAL */ #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_TYPES_H */ libfido2-1.10.0/src/hid.c000066400000000000000000000077301417126203300147700ustar00rootroot00000000000000/* * Copyright (c) 2018 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" 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); } int fido_hid_get_usage(const uint8_t *report_ptr, size_t report_len, uint32_t *usage_page) { const uint8_t *ptr = report_ptr; size_t len = report_len; 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; } ptr += key_len; len -= key_len; } return (0); } int fido_hid_get_report_len(const uint8_t *report_ptr, size_t report_len, size_t *report_in_len, size_t *report_out_len) { const uint8_t *ptr = report_ptr; size_t len = report_len; uint32_t report_size = 0; 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 == 0x94) { report_size = key_val; } else if (key == 0x80) { *report_in_len = (size_t)report_size; } else if (key == 0x90) { *report_out_len = (size_t)report_size; } ptr += key_len; len -= key_len; } return (0); } fido_dev_info_t * fido_dev_info_new(size_t n) { return (calloc(n, sizeof(fido_dev_info_t))); } static void fido_dev_info_reset(fido_dev_info_t *di) { free(di->path); free(di->manufacturer); free(di->product); memset(di, 0, sizeof(*di)); } 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++) fido_dev_info_reset(&devlist[i]); 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]); } int fido_dev_info_set(fido_dev_info_t *devlist, size_t i, const char *path, const char *manufacturer, const char *product, const fido_dev_io_t *io, const fido_dev_transport_t *transport) { char *path_copy = NULL, *manu_copy = NULL, *prod_copy = NULL; int r; if (path == NULL || manufacturer == NULL || product == NULL || io == NULL) { r = FIDO_ERR_INVALID_ARGUMENT; goto out; } if ((path_copy = strdup(path)) == NULL || (manu_copy = strdup(manufacturer)) == NULL || (prod_copy = strdup(product)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } fido_dev_info_reset(&devlist[i]); devlist[i].path = path_copy; devlist[i].manufacturer = manu_copy; devlist[i].product = prod_copy; devlist[i].io = *io; if (transport) devlist[i].transport = *transport; r = FIDO_OK; out: if (r != FIDO_OK) { free(prod_copy); free(manu_copy); free(path_copy); } return (r); } 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); } libfido2-1.10.0/src/hid_freebsd.c000066400000000000000000000124041417126203300164540ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "fido.h" #if defined(__MidnightBSD__) #define UHID_VENDOR "MidnightBSD" #else #define UHID_VENDOR "FreeBSD" #endif #define MAX_UHID 64 struct hid_freebsd { int fd; size_t report_in_len; size_t report_out_len; sigset_t sigmask; const sigset_t *sigmaskp; }; static bool is_fido(int fd) { char buf[64]; struct usb_gen_descriptor ugd; uint32_t usage_page = 0; memset(&buf, 0, sizeof(buf)); memset(&ugd, 0, sizeof(ugd)); ugd.ugd_report_type = UHID_FEATURE_REPORT; ugd.ugd_data = buf; ugd.ugd_maxlen = sizeof(buf); if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) { fido_log_error(errno, "%s: ioctl", __func__); return (false); } if (ugd.ugd_actlen > sizeof(buf) || fido_hid_get_usage(ugd.ugd_data, ugd.ugd_actlen, &usage_page) < 0) { fido_log_debug("%s: fido_hid_get_usage", __func__); return (false); } return (usage_page == 0xf1d0); } static int copy_info(fido_dev_info_t *di, const char *path) { int fd = -1; int ok = -1; struct usb_device_info udi; memset(di, 0, sizeof(*di)); memset(&udi, 0, sizeof(udi)); if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0) goto fail; if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { fido_log_error(errno, "%s: ioctl", __func__); strlcpy(udi.udi_vendor, UHID_VENDOR, sizeof(udi.udi_vendor)); strlcpy(udi.udi_product, "uhid(4)", sizeof(udi.udi_product)); udi.udi_vendorNo = 0x0b5d; /* stolen from PCI_VENDOR_OPENBSD */ } if ((di->path = strdup(path)) == NULL || (di->manufacturer = strdup(udi.udi_vendor)) == NULL || (di->product = strdup(udi.udi_product)) == NULL) goto fail; di->vendor_id = (int16_t)udi.udi_vendorNo; di->product_id = (int16_t)udi.udi_productNo; ok = 0; fail: if (fd != -1) close(fd); if (ok < 0) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); } return (ok); } int fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { char path[64]; size_t i; *olen = 0; 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 (copy_info(&devlist[*olen], path) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_hid_open, fido_hid_close, fido_hid_read, fido_hid_write, }; ++(*olen); } } return (FIDO_OK); } void * fido_hid_open(const char *path) { char buf[64]; struct hid_freebsd *ctx; struct usb_gen_descriptor ugd; int r; memset(&buf, 0, sizeof(buf)); memset(&ugd, 0, sizeof(ugd)); if ((ctx = calloc(1, sizeof(*ctx))) == NULL) return (NULL); if ((ctx->fd = fido_hid_unix_open(path)) == -1) { free(ctx); return (NULL); } ugd.ugd_report_type = UHID_FEATURE_REPORT; ugd.ugd_data = buf; ugd.ugd_maxlen = sizeof(buf); if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) || ugd.ugd_actlen > sizeof(buf) || fido_hid_get_report_len(ugd.ugd_data, ugd.ugd_actlen, &ctx->report_in_len, &ctx->report_out_len) < 0) { if (r == -1) fido_log_error(errno, "%s: ioctl", __func__); fido_log_debug("%s: using default report sizes", __func__); ctx->report_in_len = CTAP_MAX_REPORT_LEN; ctx->report_out_len = CTAP_MAX_REPORT_LEN; } return (ctx); } void fido_hid_close(void *handle) { struct hid_freebsd *ctx = handle; if (close(ctx->fd) == -1) fido_log_error(errno, "%s: close", __func__); free(ctx); } int fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) { struct hid_freebsd *ctx = handle; ctx->sigmask = *sigmask; ctx->sigmaskp = &ctx->sigmask; return (FIDO_OK); } int fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) { struct hid_freebsd *ctx = handle; ssize_t r; if (len != ctx->report_in_len) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { fido_log_debug("%s: fd not ready", __func__); return (-1); } if ((r = read(ctx->fd, buf, len)) == -1) { fido_log_error(errno, "%s: read", __func__); return (-1); } if (r < 0 || (size_t)r != len) { fido_log_debug("%s: %zd != %zu", __func__, r, len); return (-1); } return ((int)r); } int fido_hid_write(void *handle, const unsigned char *buf, size_t len) { struct hid_freebsd *ctx = handle; ssize_t r; if (len != ctx->report_out_len + 1) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) { fido_log_error(errno, "%s: write", __func__); return (-1); } if (r < 0 || (size_t)r != len - 1) { fido_log_debug("%s: %zd != %zu", __func__, r, len - 1); return (-1); } return ((int)len); } size_t fido_hid_report_in_len(void *handle) { struct hid_freebsd *ctx = handle; return (ctx->report_in_len); } size_t fido_hid_report_out_len(void *handle) { struct hid_freebsd *ctx = handle; return (ctx->report_out_len); } libfido2-1.10.0/src/hid_hidapi.c000066400000000000000000000114371417126203300163050ustar00rootroot00000000000000/* * 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. */ #ifdef __linux__ #include #include #include #include #endif #include #include #include #include "fido.h" struct hid_hidapi { void *handle; size_t report_in_len; size_t report_out_len; }; static size_t fido_wcslen(const wchar_t *wcs) { size_t l = 0; while (*wcs++ != L'\0') l++; return l; } static char * wcs_to_cs(const wchar_t *wcs) { char *cs; size_t i; if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL) return NULL; for (i = 0; i < fido_wcslen(wcs); i++) { if (wcs[i] >= 128) { /* give up on parsing non-ASCII text */ free(cs); return strdup("hidapi device"); } cs[i] = (char)wcs[i]; } return cs; } static int copy_info(fido_dev_info_t *di, const struct hid_device_info *d) { memset(di, 0, sizeof(*di)); if (d->path != NULL) di->path = strdup(d->path); else di->path = strdup(""); if (d->manufacturer_string != NULL) di->manufacturer = wcs_to_cs(d->manufacturer_string); else di->manufacturer = strdup(""); if (d->product_string != NULL) di->product = wcs_to_cs(d->product_string); else di->product = strdup(""); if (di->path == NULL || di->manufacturer == NULL || di->product == NULL) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); return -1; } di->product_id = (int16_t)d->product_id; di->vendor_id = (int16_t)d->vendor_id; di->io = (fido_dev_io_t) { &fido_hid_open, &fido_hid_close, &fido_hid_read, &fido_hid_write, }; return 0; } #ifdef __linux__ static int get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) { int fd; int s = -1; int ok = -1; if ((fd = fido_hid_unix_open(path)) == -1) { fido_log_debug("%s: fido_hid_unix_open", __func__); return -1; } if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) < 0 || s < 0 || (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__); goto fail; } hrd->size = (unsigned)s; if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) < 0) { fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__); goto fail; } ok = 0; fail: if (fd != -1) close(fd); return ok; } static bool is_fido(const struct hid_device_info *hdi) { uint32_t usage_page = 0; struct hidraw_report_descriptor hrd; memset(&hrd, 0, sizeof(hrd)); if (get_report_descriptor(hdi->path, &hrd) < 0 || fido_hid_get_usage(hrd.value, hrd.size, &usage_page) < 0) { return false; } return usage_page == 0xf1d0; } #elif defined(_WIN32) || defined(__APPLE__) static bool is_fido(const struct hid_device_info *hdi) { return hdi->usage_page == 0xf1d0; } #else static bool is_fido(const struct hid_device_info *hdi) { (void)hdi; fido_log_debug("%s: assuming FIDO HID", __func__); return true; } #endif void * fido_hid_open(const char *path) { struct hid_hidapi *ctx; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { return (NULL); } if ((ctx->handle = hid_open_path(path)) == NULL) { free(ctx); return (NULL); } ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN; return ctx; } void fido_hid_close(void *handle) { struct hid_hidapi *ctx = handle; hid_close(ctx->handle); free(ctx); } int fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) { (void)handle; (void)sigmask; return (FIDO_ERR_INTERNAL); } int fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) { struct hid_hidapi *ctx = handle; if (len != ctx->report_in_len) { fido_log_debug("%s: len %zu", __func__, len); return -1; } return hid_read_timeout(ctx->handle, buf, len, ms); } int fido_hid_write(void *handle, const unsigned char *buf, size_t len) { struct hid_hidapi *ctx = handle; if (len != ctx->report_out_len + 1) { fido_log_debug("%s: len %zu", __func__, len); return -1; } return hid_write(ctx->handle, buf, len); } int fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { struct hid_device_info *hdi; *olen = 0; if (ilen == 0) return FIDO_OK; /* nothing to do */ if (devlist == NULL) return FIDO_ERR_INVALID_ARGUMENT; if ((hdi = hid_enumerate(0, 0)) == NULL) return FIDO_OK; /* nothing to do */ for (struct hid_device_info *d = hdi; d != NULL; d = d->next) { if (is_fido(d) == false) continue; if (copy_info(&devlist[*olen], d) == 0) { if (++(*olen) == ilen) break; } } hid_free_enumeration(hdi); return FIDO_OK; } size_t fido_hid_report_in_len(void *handle) { struct hid_hidapi *ctx = handle; return (ctx->report_in_len); } size_t fido_hid_report_out_len(void *handle) { struct hid_hidapi *ctx = handle; return (ctx->report_out_len); } libfido2-1.10.0/src/hid_linux.c000066400000000000000000000174741417126203300162150ustar00rootroot00000000000000/* * 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 "fido.h" struct hid_linux { int fd; size_t report_in_len; size_t report_out_len; sigset_t sigmask; const sigset_t *sigmaskp; }; static int get_report_descriptor(int fd, struct hidraw_report_descriptor *hrd) { int s = -1; if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) == -1) { fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__); return (-1); } if (s < 0 || (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { fido_log_debug("%s: HIDIOCGRDESCSIZE %d", __func__, s); return (-1); } hrd->size = (unsigned)s; if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) == -1) { fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__); return (-1); } return (0); } static bool is_fido(const char *path) { int fd; uint32_t usage_page = 0; struct hidraw_report_descriptor hrd; memset(&hrd, 0, sizeof(hrd)); if ((fd = fido_hid_unix_open(path)) == -1) return (false); if (get_report_descriptor(fd, &hrd) < 0 || fido_hid_get_usage(hrd.value, hrd.size, &usage_page) < 0) usage_page = 0; if (close(fd) == -1) fido_log_error(errno, "%s: close", __func__); return (usage_page == 0xf1d0); } static int parse_uevent(const char *uevent, int *bus, int16_t *vendor_id, int16_t *product_id) { char *cp; char *p; char *s; int ok = -1; short unsigned int x; short unsigned int y; short unsigned int z; if ((s = cp = strdup(uevent)) == NULL) return (-1); while ((p = strsep(&cp, "\n")) != NULL && *p != '\0') { if (strncmp(p, "HID_ID=", 7) == 0) { if (sscanf(p + 7, "%hx:%hx:%hx", &x, &y, &z) == 3) { *bus = (int)x; *vendor_id = (int16_t)y; *product_id = (int16_t)z; ok = 0; break; } } } free(s); return (ok); } static char * get_parent_attr(struct udev_device *dev, const char *subsystem, const char *devtype, const char *attr) { struct udev_device *parent; const char *value; if ((parent = udev_device_get_parent_with_subsystem_devtype(dev, subsystem, devtype)) == NULL || (value = udev_device_get_sysattr_value(parent, attr)) == NULL) return (NULL); return (strdup(value)); } static char * get_usb_attr(struct udev_device *dev, const char *attr) { return (get_parent_attr(dev, "usb", "usb_device", attr)); } static int copy_info(fido_dev_info_t *di, struct udev *udev, struct udev_list_entry *udev_entry) { const char *name; const char *path; char *uevent = NULL; struct udev_device *dev = NULL; int bus = 0; 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 ((uevent = get_parent_attr(dev, "hid", NULL, "uevent")) == NULL || parse_uevent(uevent, &bus, &di->vendor_id, &di->product_id) < 0) { fido_log_debug("%s: uevent", __func__); goto fail; } #ifndef FIDO_HID_ANY if (bus != BUS_USB) { fido_log_debug("%s: bus", __func__); goto fail; } #endif di->path = strdup(path); if ((di->manufacturer = get_usb_attr(dev, "manufacturer")) == NULL) di->manufacturer = strdup(""); if ((di->product = get_usb_attr(dev, "product")) == NULL) di->product = strdup(""); if (di->path == NULL || di->manufacturer == NULL || di->product == NULL) goto fail; ok = 0; fail: if (dev != NULL) udev_device_unref(dev); free(uevent); if (ok < 0) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); } return (ok); } int fido_hid_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) goto fail; if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) { r = FIDO_OK; /* zero hidraw devices */ goto fail; } udev_list_entry_foreach(udev_entry, udev_list) { if (copy_info(&devlist[*olen], udev, udev_entry) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_hid_open, fido_hid_close, fido_hid_read, fido_hid_write, }; 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) { struct hid_linux *ctx; struct hidraw_report_descriptor hrd; struct timespec tv_pause; long interval_ms, retries = 0; if ((ctx = calloc(1, sizeof(*ctx))) == NULL || (ctx->fd = fido_hid_unix_open(path)) == -1) { free(ctx); return (NULL); } while (flock(ctx->fd, LOCK_EX|LOCK_NB) == -1) { if (errno != EWOULDBLOCK) { fido_log_error(errno, "%s: flock", __func__); fido_hid_close(ctx); return (NULL); } if (retries++ >= 15) { fido_log_debug("%s: flock timeout", __func__); fido_hid_close(ctx); return (NULL); } interval_ms = retries * 100000000L; tv_pause.tv_sec = interval_ms / 1000000000L; tv_pause.tv_nsec = interval_ms % 1000000000L; if (nanosleep(&tv_pause, NULL) == -1) { fido_log_error(errno, "%s: nanosleep", __func__); fido_hid_close(ctx); return (NULL); } } if (get_report_descriptor(ctx->fd, &hrd) < 0 || fido_hid_get_report_len(hrd.value, hrd.size, &ctx->report_in_len, &ctx->report_out_len) < 0 || ctx->report_in_len == 0 || ctx->report_out_len == 0) { fido_log_debug("%s: using default report sizes", __func__); ctx->report_in_len = CTAP_MAX_REPORT_LEN; ctx->report_out_len = CTAP_MAX_REPORT_LEN; } return (ctx); } void fido_hid_close(void *handle) { struct hid_linux *ctx = handle; if (close(ctx->fd) == -1) fido_log_error(errno, "%s: close", __func__); free(ctx); } int fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) { struct hid_linux *ctx = handle; ctx->sigmask = *sigmask; ctx->sigmaskp = &ctx->sigmask; return (FIDO_OK); } int fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) { struct hid_linux *ctx = handle; ssize_t r; if (len != ctx->report_in_len) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { fido_log_debug("%s: fd not ready", __func__); return (-1); } if ((r = read(ctx->fd, buf, len)) == -1) { fido_log_error(errno, "%s: read", __func__); return (-1); } if (r < 0 || (size_t)r != len) { fido_log_debug("%s: %zd != %zu", __func__, r, len); return (-1); } return ((int)r); } int fido_hid_write(void *handle, const unsigned char *buf, size_t len) { struct hid_linux *ctx = handle; ssize_t r; if (len != ctx->report_out_len + 1) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if ((r = write(ctx->fd, buf, len)) == -1) { fido_log_error(errno, "%s: write", __func__); return (-1); } if (r < 0 || (size_t)r != len) { fido_log_debug("%s: %zd != %zu", __func__, r, len); return (-1); } return ((int)r); } size_t fido_hid_report_in_len(void *handle) { struct hid_linux *ctx = handle; return (ctx->report_in_len); } size_t fido_hid_report_out_len(void *handle) { struct hid_linux *ctx = handle; return (ctx->report_out_len); } libfido2-1.10.0/src/hid_netbsd.c000066400000000000000000000170271417126203300163270ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "fido.h" #define MAX_UHID 64 struct hid_netbsd { int fd; size_t report_in_len; size_t report_out_len; sigset_t sigmask; const sigset_t *sigmaskp; }; /* Hack to make this work with newer kernels even if /usr/include is old. */ #if __NetBSD_Version__ < 901000000 /* 9.1 */ #define USB_HID_GET_RAW _IOR('h', 1, int) #define USB_HID_SET_RAW _IOW('h', 2, int) #endif static bool is_fido(int fd) { struct usb_ctl_report_desc ucrd; uint32_t usage_page = 0; int raw = 1; memset(&ucrd, 0, sizeof(ucrd)); if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ucrd) == -1) { fido_log_error(errno, "%s: ioctl", __func__); return (false); } if (ucrd.ucrd_size < 0 || (size_t)ucrd.ucrd_size > sizeof(ucrd.ucrd_data) || fido_hid_get_usage(ucrd.ucrd_data, (size_t)ucrd.ucrd_size, &usage_page) < 0) { fido_log_debug("%s: fido_hid_get_usage", __func__); return (false); } if (usage_page != 0xf1d0) return (false); /* * This step is not strictly necessary -- NetBSD puts fido * devices into raw mode automatically by default, but in * principle that might change, and this serves as a test to * verify that we're running on a kernel with support for raw * mode at all so we don't get confused issuing writes that try * to set the report descriptor rather than transfer data on * the output interrupt pipe as we need. */ if (ioctl(fd, IOCTL_REQ(USB_HID_SET_RAW), &raw) == -1) { fido_log_error(errno, "%s: unable to set raw", __func__); return (false); } return (true); } static int copy_info(fido_dev_info_t *di, const char *path) { int fd = -1; int ok = -1; struct usb_device_info udi; memset(di, 0, sizeof(*di)); memset(&udi, 0, sizeof(udi)); if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0) goto fail; if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { fido_log_error(errno, "%s: ioctl", __func__); goto fail; } if ((di->path = strdup(path)) == NULL || (di->manufacturer = strdup(udi.udi_vendor)) == NULL || (di->product = strdup(udi.udi_product)) == NULL) goto fail; di->vendor_id = (int16_t)udi.udi_vendorNo; di->product_id = (int16_t)udi.udi_productNo; ok = 0; fail: if (fd != -1 && close(fd) == -1) fido_log_error(errno, "%s: close", __func__); if (ok < 0) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); } return (ok); } int fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { char path[64]; size_t i; *olen = 0; 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 (copy_info(&devlist[*olen], path) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_hid_open, fido_hid_close, fido_hid_read, fido_hid_write, }; ++(*olen); } } return (FIDO_OK); } /* * Workaround for NetBSD (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_netbsd *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_error(errno, "%s: poll", __func__); 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_xxd(data, ctx->report_out_len, "%s: got reply", __func__); return 0; } fido_log_debug("%s: no response", __func__); return -1; } void * fido_hid_open(const char *path) { struct hid_netbsd *ctx; struct usb_ctl_report_desc ucrd; int r; memset(&ucrd, 0, sizeof(ucrd)); if ((ctx = calloc(1, sizeof(*ctx))) == NULL || (ctx->fd = fido_hid_unix_open(path)) == -1) { free(ctx); return (NULL); } if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ucrd)) == -1 || ucrd.ucrd_size < 0 || (size_t)ucrd.ucrd_size > sizeof(ucrd.ucrd_data) || fido_hid_get_report_len(ucrd.ucrd_data, (size_t)ucrd.ucrd_size, &ctx->report_in_len, &ctx->report_out_len) < 0) { if (r == -1) fido_log_error(errno, "%s: ioctl", __func__); fido_log_debug("%s: using default report sizes", __func__); ctx->report_in_len = CTAP_MAX_REPORT_LEN; ctx->report_out_len = CTAP_MAX_REPORT_LEN; } /* * NetBSD 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 (!is_fido(ctx->fd) || terrible_ping_kludge(ctx) != 0) { fido_hid_close(ctx); return NULL; } return (ctx); } void fido_hid_close(void *handle) { struct hid_netbsd *ctx = handle; if (close(ctx->fd) == -1) fido_log_error(errno, "%s: close", __func__); free(ctx); } int fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) { struct hid_netbsd *ctx = handle; ctx->sigmask = *sigmask; ctx->sigmaskp = &ctx->sigmask; return (FIDO_OK); } int fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) { struct hid_netbsd *ctx = handle; ssize_t r; if (len != ctx->report_in_len) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { fido_log_debug("%s: fd not ready", __func__); return (-1); } if ((r = read(ctx->fd, buf, len)) == -1) { fido_log_error(errno, "%s: read", __func__); return (-1); } if (r < 0 || (size_t)r != len) { fido_log_error(errno, "%s: %zd != %zu", __func__, r, len); return (-1); } return ((int)r); } int fido_hid_write(void *handle, const unsigned char *buf, size_t len) { struct hid_netbsd *ctx = handle; ssize_t r; if (len != ctx->report_out_len + 1) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) { fido_log_error(errno, "%s: write", __func__); return (-1); } if (r < 0 || (size_t)r != len - 1) { fido_log_error(errno, "%s: %zd != %zu", __func__, r, len - 1); return (-1); } return ((int)len); } size_t fido_hid_report_in_len(void *handle) { struct hid_netbsd *ctx = handle; return (ctx->report_in_len); } size_t fido_hid_report_out_len(void *handle) { struct hid_netbsd *ctx = handle; return (ctx->report_out_len); } libfido2-1.10.0/src/hid_openbsd.c000066400000000000000000000142411417126203300164750ustar00rootroot00000000000000/* * 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 "fido.h" #define MAX_UHID 64 struct hid_openbsd { int fd; size_t report_in_len; size_t report_out_len; sigset_t sigmask; const sigset_t *sigmaskp; }; int fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { size_t i; char path[64]; int fd; struct usb_device_info udi; 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/fido/%zu", i); if ((fd = fido_hid_unix_open(path)) == -1) continue; memset(&udi, 0, sizeof(udi)); if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { fido_log_error(errno, "%s: get device info %s", __func__, path); if (close(fd) == -1) fido_log_error(errno, "%s: close", __func__); continue; } if (close(fd) == -1) fido_log_error(errno, "%s: close", __func__); fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x", __func__, path, udi.udi_bus, udi.udi_addr); fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"", __func__, path, udi.udi_vendor, udi.udi_product); fido_log_debug("%s: %s: productNo = 0x%04x, vendorNo = 0x%04x, " "releaseNo = 0x%04x", __func__, path, udi.udi_productNo, udi.udi_vendorNo, udi.udi_releaseNo); di = &devlist[*olen]; memset(di, 0, sizeof(*di)); di->io = (fido_dev_io_t) { fido_hid_open, fido_hid_close, fido_hid_read, fido_hid_write, }; if ((di->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 = (int16_t)udi.udi_vendorNo; di->product_id = (int16_t)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_error(errno, "%s: poll", __func__); 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_xxd(data, ctx->report_out_len, "%s: got reply", __func__); return 0; } fido_log_debug("%s: no response", __func__); return -1; } void * fido_hid_open(const char *path) { struct hid_openbsd *ret = NULL; if ((ret = calloc(1, sizeof(*ret))) == NULL || (ret->fd = fido_hid_unix_open(path)) == -1) { free(ret); return (NULL); } ret->report_in_len = ret->report_out_len = CTAP_MAX_REPORT_LEN; fido_log_debug("%s: inlen = %zu outlen = %zu", __func__, 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; if (close(ctx->fd) == -1) fido_log_error(errno, "%s: close", __func__); free(ctx); } int fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) { struct hid_openbsd *ctx = handle; ctx->sigmask = *sigmask; ctx->sigmaskp = &ctx->sigmask; return (FIDO_OK); } 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; 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 (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { fido_log_debug("%s: fd not ready", __func__); return (-1); } if ((r = read(ctx->fd, buf, len)) == -1) { fido_log_error(errno, "%s: read", __func__); return (-1); } if (r < 0 || (size_t)r != len) { fido_log_debug("%s: %zd != %zu", __func__, r, len); 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) { fido_log_error(errno, "%s: write", __func__); return (-1); } if (r < 0 || (size_t)r != len - 1) { fido_log_debug("%s: %zd != %zu", __func__, r, len - 1); return (-1); } return ((int)len); } size_t fido_hid_report_in_len(void *handle) { struct hid_openbsd *ctx = handle; return (ctx->report_in_len); } size_t fido_hid_report_out_len(void *handle) { struct hid_openbsd *ctx = handle; return (ctx->report_out_len); } libfido2-1.10.0/src/hid_osx.c000066400000000000000000000314401417126203300156540ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #include #include #include #include #include "fido.h" #if __MAC_OS_X_VERSION_MIN_REQUIRED < 120000 #define kIOMainPortDefault kIOMasterPortDefault #endif #define IOREG "ioreg://" struct hid_osx { IOHIDDeviceRef ref; CFStringRef loop_id; int report_pipe[2]; size_t report_in_len; size_t report_out_len; unsigned char report[CTAP_MAX_REPORT_LEN]; }; 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, (long)len, kCFStringEncodingUTF8) == false) { fido_log_debug("%s: CFStringGetCString", __func__); return (-1); } return (0); } static int get_report_len(IOHIDDeviceRef dev, int dir, size_t *report_len) { CFStringRef key; int32_t v; if (dir == 0) key = CFSTR(kIOHIDMaxInputReportSizeKey); else key = CFSTR(kIOHIDMaxOutputReportSizeKey); if (get_int32(dev, key, &v) < 0) { fido_log_debug("%s: get_int32/%d", __func__, dir); return (-1); } if ((*report_len = (size_t)v) > CTAP_MAX_REPORT_LEN) { fido_log_debug("%s: report_len=%zu", __func__, *report_len); return (-1); } return (0); } 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) *manufacturer = strdup(""); else *manufacturer = strdup(buf); if (get_utf8(dev, CFSTR(kIOHIDProductKey), buf, sizeof(buf)) < 0) *product = strdup(""); else *product = strdup(buf); if (*manufacturer == NULL || *product == NULL) { fido_log_debug("%s: strdup", __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; uint64_t id; char *path; if ((s = IOHIDDeviceGetService(dev)) == MACH_PORT_NULL) { fido_log_debug("%s: IOHIDDeviceGetService", __func__); return (NULL); } if (IORegistryEntryGetRegistryEntryID(s, &id) != KERN_SUCCESS) { fido_log_debug("%s: IORegistryEntryGetRegistryEntryID", __func__); return (NULL); } if (asprintf(&path, "%s%llu", IOREG, (unsigned long long)id) == -1) { fido_log_error(errno, "%s: asprintf", __func__); return (NULL); } return (path); } static bool is_fido(IOHIDDeviceRef dev) { char buf[32]; uint32_t usage_page; if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey), (int32_t *)&usage_page) < 0 || usage_page != 0xf1d0) return (false); if (get_utf8(dev, CFSTR(kIOHIDTransportKey), buf, sizeof(buf)) < 0) { fido_log_debug("%s: get_utf8 transport", __func__); return (false); } #ifndef FIDO_HID_ANY if (strcasecmp(buf, "usb") != 0) { fido_log_debug("%s: transport", __func__); return (false); } #endif return (true); } 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_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { IOHIDManagerRef manager = NULL; CFSetRef devset = NULL; size_t devcnt; CFIndex n; 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 ((n = CFSetGetCount(devset)) < 0) { fido_log_debug("%s: CFSetGetCount", __func__); goto fail; } devcnt = (size_t)n; if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } CFSetGetValues(devset, (void *)devs); for (size_t i = 0; i < devcnt; i++) { if (copy_info(&devlist[*olen], devs[i]) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_hid_open, fido_hid_close, fido_hid_read, fido_hid_write, }; if (++(*olen) == ilen) break; } } r = FIDO_OK; fail: if (manager != NULL) CFRelease(manager); if (devset != NULL) CFRelease(devset); free(devs); return (r); } static void report_callback(void *context, IOReturn result, void *dev, IOHIDReportType type, uint32_t id, uint8_t *ptr, CFIndex len) { struct hid_osx *ctx = context; ssize_t r; (void)dev; if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput || id != 0 || len < 0 || (size_t)len != ctx->report_in_len) { fido_log_debug("%s: io error", __func__); return; } if ((r = write(ctx->report_pipe[1], ptr, (size_t)len)) == -1) { fido_log_error(errno, "%s: write", __func__); return; } if (r < 0 || (size_t)r != (size_t)len) { fido_log_debug("%s: %zd != %zu", __func__, r, (size_t)len); return; } } static void removal_callback(void *context, IOReturn result, void *sender) { (void)context; (void)result; (void)sender; CFRunLoopStop(CFRunLoopGetCurrent()); } static int set_nonblock(int fd) { int flags; if ((flags = fcntl(fd, F_GETFL)) == -1) { fido_log_error(errno, "%s: fcntl F_GETFL", __func__); return (-1); } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { fido_log_error(errno, "%s: fcntl F_SETFL", __func__); return (-1); } return (0); } static int disable_sigpipe(int fd) { int disabled = 1; if (fcntl(fd, F_SETNOSIGPIPE, &disabled) == -1) { fido_log_error(errno, "%s: fcntl F_SETNOSIGPIPE", __func__); return (-1); } return (0); } static int to_uint64(const char *str, uint64_t *out) { char *ep; unsigned long long ull; errno = 0; ull = strtoull(str, &ep, 10); if (str == ep || *ep != '\0') return (-1); else if (ull == ULLONG_MAX && errno == ERANGE) return (-1); else if (ull > UINT64_MAX) return (-1); *out = (uint64_t)ull; return (0); } static io_registry_entry_t get_ioreg_entry(const char *path) { uint64_t id; if (strncmp(path, IOREG, strlen(IOREG)) != 0) return (IORegistryEntryFromPath(kIOMainPortDefault, path)); if (to_uint64(path + strlen(IOREG), &id) == -1) { fido_log_debug("%s: to_uint64", __func__); return (MACH_PORT_NULL); } return (IOServiceGetMatchingService(kIOMainPortDefault, IORegistryEntryIDMatching(id))); } void * fido_hid_open(const char *path) { struct hid_osx *ctx; io_registry_entry_t entry = MACH_PORT_NULL; char loop_id[32]; int ok = -1; int r; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } ctx->report_pipe[0] = -1; ctx->report_pipe[1] = -1; if (pipe(ctx->report_pipe) == -1) { fido_log_error(errno, "%s: pipe", __func__); goto fail; } if (set_nonblock(ctx->report_pipe[0]) < 0 || set_nonblock(ctx->report_pipe[1]) < 0) { fido_log_debug("%s: set_nonblock", __func__); goto fail; } if (disable_sigpipe(ctx->report_pipe[1]) < 0) { fido_log_debug("%s: disable_sigpipe", __func__); goto fail; } if ((entry = get_ioreg_entry(path)) == MACH_PORT_NULL) { fido_log_debug("%s: get_ioreg_entry: %s", __func__, path); goto fail; } if ((ctx->ref = IOHIDDeviceCreate(kCFAllocatorDefault, entry)) == NULL) { fido_log_debug("%s: IOHIDDeviceCreate", __func__); goto fail; } if (get_report_len(ctx->ref, 0, &ctx->report_in_len) < 0 || get_report_len(ctx->ref, 1, &ctx->report_out_len) < 0) { fido_log_debug("%s: get_report_len", __func__); goto fail; } if (ctx->report_in_len > sizeof(ctx->report)) { fido_log_debug("%s: report_in_len=%zu", __func__, ctx->report_in_len); goto fail; } if (IOHIDDeviceOpen(ctx->ref, kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) { fido_log_debug("%s: IOHIDDeviceOpen", __func__); goto fail; } if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p", (void *)ctx->ref)) < 0 || (size_t)r >= sizeof(loop_id)) { fido_log_debug("%s: snprintf", __func__); goto fail; } if ((ctx->loop_id = CFStringCreateWithCString(NULL, loop_id, kCFStringEncodingASCII)) == NULL) { fido_log_debug("%s: CFStringCreateWithCString", __func__); goto fail; } IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report, (long)ctx->report_in_len, &report_callback, ctx); IOHIDDeviceRegisterRemovalCallback(ctx->ref, &removal_callback, ctx); ok = 0; fail: if (entry != MACH_PORT_NULL) IOObjectRelease(entry); if (ok < 0 && ctx != NULL) { if (ctx->ref != NULL) CFRelease(ctx->ref); if (ctx->loop_id != NULL) CFRelease(ctx->loop_id); if (ctx->report_pipe[0] != -1) close(ctx->report_pipe[0]); if (ctx->report_pipe[1] != -1) close(ctx->report_pipe[1]); free(ctx); ctx = NULL; } return (ctx); } void fido_hid_close(void *handle) { struct hid_osx *ctx = handle; IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report, (long)ctx->report_in_len, NULL, ctx); IOHIDDeviceRegisterRemovalCallback(ctx->ref, NULL, ctx); if (IOHIDDeviceClose(ctx->ref, kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) fido_log_debug("%s: IOHIDDeviceClose", __func__); CFRelease(ctx->ref); CFRelease(ctx->loop_id); explicit_bzero(ctx->report, sizeof(ctx->report)); close(ctx->report_pipe[0]); close(ctx->report_pipe[1]); free(ctx); } int fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) { (void)handle; (void)sigmask; return (FIDO_ERR_INTERNAL); } int fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) { struct hid_osx *ctx = handle; ssize_t r; explicit_bzero(buf, len); explicit_bzero(ctx->report, sizeof(ctx->report)); if (len != ctx->report_in_len || len > sizeof(ctx->report)) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } IOHIDDeviceScheduleWithRunLoop(ctx->ref, CFRunLoopGetCurrent(), ctx->loop_id); if (ms == -1) ms = 5000; /* wait 5 seconds by default */ CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0, true); IOHIDDeviceUnscheduleFromRunLoop(ctx->ref, CFRunLoopGetCurrent(), ctx->loop_id); if ((r = read(ctx->report_pipe[0], buf, len)) == -1) { fido_log_error(errno, "%s: read", __func__); return (-1); } if (r < 0 || (size_t)r != len) { fido_log_debug("%s: %zd != %zu", __func__, r, len); return (-1); } return ((int)len); } int fido_hid_write(void *handle, const unsigned char *buf, size_t len) { struct hid_osx *ctx = handle; if (len != ctx->report_out_len + 1 || len > LONG_MAX) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if (IOHIDDeviceSetReport(ctx->ref, kIOHIDReportTypeOutput, 0, buf + 1, (long)(len - 1)) != kIOReturnSuccess) { fido_log_debug("%s: IOHIDDeviceSetReport", __func__); return (-1); } return ((int)len); } size_t fido_hid_report_in_len(void *handle) { struct hid_osx *ctx = handle; return (ctx->report_in_len); } size_t fido_hid_report_out_len(void *handle) { struct hid_osx *ctx = handle; return (ctx->report_out_len); } libfido2-1.10.0/src/hid_unix.c000066400000000000000000000025701417126203300160300ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "fido.h" #ifdef __NetBSD__ #define ppoll pollts #endif int fido_hid_unix_open(const char *path) { int fd; struct stat st; if ((fd = open(path, O_RDWR)) == -1) { if (errno != ENOENT && errno != ENXIO) fido_log_error(errno, "%s: open %s", __func__, path); return (-1); } if (fstat(fd, &st) == -1) { fido_log_error(errno, "%s: fstat %s", __func__, path); if (close(fd) == -1) fido_log_error(errno, "%s: close", __func__); return (-1); } if (S_ISCHR(st.st_mode) == 0) { fido_log_debug("%s: S_ISCHR %s", __func__, path); if (close(fd) == -1) fido_log_error(errno, "%s: close", __func__); return (-1); } return (fd); } int fido_hid_unix_wait(int fd, int ms, const fido_sigset_t *sigmask) { struct timespec ts; struct pollfd pfd; int r; memset(&pfd, 0, sizeof(pfd)); pfd.events = POLLIN; pfd.fd = fd; #ifdef FIDO_FUZZ return (0); #endif if (ms > -1) { ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; } if ((r = ppoll(&pfd, 1, ms > -1 ? &ts : NULL, sigmask)) < 1) { if (r == -1) fido_log_error(errno, "%s: ppoll", __func__); return (-1); } return (0); } libfido2-1.10.0/src/hid_win.c000066400000000000000000000306421417126203300156430ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include "fido.h" #if defined(__MINGW32__) && __MINGW64_VERSION_MAJOR < 6 WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO, PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE, DWORD, PDWORD, DWORD); #endif #if defined(__MINGW32__) DEFINE_DEVPROPKEY(DEVPKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8); #endif struct hid_win { HANDLE dev; OVERLAPPED overlap; int report_pending; size_t report_in_len; size_t report_out_len; unsigned char report[1 + CTAP_MAX_REPORT_LEN]; }; static bool is_fido(HANDLE dev) { PHIDP_PREPARSED_DATA data = NULL; HIDP_CAPS caps; int fido = 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; } fido = (uint16_t)caps.UsagePage == 0xf1d0; fail: if (data != NULL) HidD_FreePreparsedData(data); return (fido); } static int get_report_len(HANDLE dev, int dir, size_t *report_len) { PHIDP_PREPARSED_DATA data = NULL; HIDP_CAPS caps; USHORT v; int ok = -1; if (HidD_GetPreparsedData(dev, &data) == false) { fido_log_debug("%s: HidD_GetPreparsedData/%d", __func__, dir); goto fail; } if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) { fido_log_debug("%s: HidP_GetCaps/%d", __func__, dir); goto fail; } if (dir == 0) v = caps.InputReportByteLength; else v = caps.OutputReportByteLength; if ((*report_len = (size_t)v) == 0) { fido_log_debug("%s: report_len == 0", __func__); goto fail; } ok = 0; fail: if (data != NULL) HidD_FreePreparsedData(data); return (ok); } static int get_id(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 = (int16_t)attr.VendorID; *product_id = (int16_t)attr.ProductID; return (0); } static int get_manufacturer(HANDLE dev, char **manufacturer) { wchar_t buf[512]; int utf8_len; int ok = -1; *manufacturer = 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((size_t)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; } ok = 0; fail: if (ok < 0) { free(*manufacturer); *manufacturer = NULL; } return (ok); } static int get_product(HANDLE dev, char **product) { wchar_t buf[512]; int utf8_len; int ok = -1; *product = NULL; 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((size_t)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(*product); *product = NULL; } return (ok); } static char * get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *ifdata) { SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL; char *path = NULL; DWORD len = 0; /* * "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 ((path = strdup(ifdetail->DevicePath)) == NULL) { fido_log_debug("%s: strdup", __func__); goto fail; } fail: free(ifdetail); return (path); } #ifndef FIDO_HID_ANY static bool hid_ok(HDEVINFO devinfo, DWORD idx) { SP_DEVINFO_DATA devinfo_data; wchar_t *parent = NULL; DWORD parent_type = DEVPROP_TYPE_STRING; DWORD len = 0; bool ok = false; memset(&devinfo_data, 0, sizeof(devinfo_data)); devinfo_data.cbSize = sizeof(devinfo_data); if (SetupDiEnumDeviceInfo(devinfo, idx, &devinfo_data) == false) { fido_log_debug("%s: SetupDiEnumDeviceInfo", __func__); goto fail; } if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data, &DEVPKEY_Device_Parent, &parent_type, NULL, 0, &len, 0) != false || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { fido_log_debug("%s: SetupDiGetDevicePropertyW 1", __func__); goto fail; } if ((parent = malloc(len)) == NULL) { fido_log_debug("%s: malloc", __func__); goto fail; } if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data, &DEVPKEY_Device_Parent, &parent_type, (PBYTE)parent, len, NULL, 0) == false) { fido_log_debug("%s: SetupDiGetDevicePropertyW 2", __func__); goto fail; } ok = wcsncmp(parent, L"USB\\", 4) == 0; fail: free(parent); return (ok); } #endif static int copy_info(fido_dev_info_t *di, HDEVINFO devinfo, DWORD idx, SP_DEVICE_INTERFACE_DATA *ifdata) { HANDLE dev = INVALID_HANDLE_VALUE; int ok = -1; memset(di, 0, sizeof(*di)); if ((di->path = get_path(devinfo, ifdata)) == NULL) { fido_log_debug("%s: get_path", __func__); goto fail; } fido_log_debug("%s: path=%s", __func__, di->path); #ifndef FIDO_HID_ANY if (hid_ok(devinfo, idx) == false) { fido_log_debug("%s: hid_ok", __func__); goto fail; } #endif dev = CreateFileA(di->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (dev == INVALID_HANDLE_VALUE) { fido_log_debug("%s: CreateFileA", __func__); goto fail; } if (is_fido(dev) == false) { fido_log_debug("%s: is_fido", __func__); goto fail; } if (get_id(dev, &di->vendor_id, &di->product_id) < 0) { fido_log_debug("%s: get_id", __func__); goto fail; } if (get_manufacturer(dev, &di->manufacturer) < 0) { fido_log_debug("%s: get_manufacturer", __func__); di->manufacturer = strdup(""); } if (get_product(dev, &di->product) < 0) { fido_log_debug("%s: get_product", __func__); di->product = strdup(""); } if (di->manufacturer == NULL || di->product == NULL) { fido_log_debug("%s: manufacturer/product", __func__); 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_hid_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; DWORD idx; 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 ((devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) { fido_log_debug("%s: SetupDiGetClassDevsA", __func__); goto fail; } ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for (idx = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, idx, &ifdata) == true; idx++) { if (copy_info(&devlist[*olen], devinfo, idx, &ifdata) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_hid_open, fido_hid_close, fido_hid_read, fido_hid_write, }; if (++(*olen) == ilen) break; } } r = FIDO_OK; fail: if (devinfo != INVALID_HANDLE_VALUE) SetupDiDestroyDeviceInfoList(devinfo); return (r); } void * fido_hid_open(const char *path) { struct hid_win *ctx; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) return (NULL); ctx->dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (ctx->dev == INVALID_HANDLE_VALUE) { free(ctx); return (NULL); } if ((ctx->overlap.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL)) == NULL) { fido_log_debug("%s: CreateEventA", __func__); fido_hid_close(ctx); return (NULL); } if (get_report_len(ctx->dev, 0, &ctx->report_in_len) < 0 || get_report_len(ctx->dev, 1, &ctx->report_out_len) < 0) { fido_log_debug("%s: get_report_len", __func__); fido_hid_close(ctx); return (NULL); } return (ctx); } void fido_hid_close(void *handle) { struct hid_win *ctx = handle; if (ctx->overlap.hEvent != NULL) { if (ctx->report_pending) { fido_log_debug("%s: report_pending", __func__); if (CancelIoEx(ctx->dev, &ctx->overlap) == 0) fido_log_debug("%s CancelIoEx: 0x%lx", __func__, (u_long)GetLastError()); } CloseHandle(ctx->overlap.hEvent); } explicit_bzero(ctx->report, sizeof(ctx->report)); CloseHandle(ctx->dev); free(ctx); } int fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask) { (void)handle; (void)sigmask; return (FIDO_ERR_INTERNAL); } int fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) { struct hid_win *ctx = handle; DWORD n; if (len != ctx->report_in_len - 1 || len > sizeof(ctx->report) - 1) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if (ctx->report_pending == 0) { memset(&ctx->report, 0, sizeof(ctx->report)); ResetEvent(ctx->overlap.hEvent); if (ReadFile(ctx->dev, ctx->report, (DWORD)(len + 1), &n, &ctx->overlap) == 0 && GetLastError() != ERROR_IO_PENDING) { CancelIo(ctx->dev); fido_log_debug("%s: ReadFile", __func__); return (-1); } ctx->report_pending = 1; } if (ms > -1 && WaitForSingleObject(ctx->overlap.hEvent, (DWORD)ms) != WAIT_OBJECT_0) return (0); ctx->report_pending = 0; if (GetOverlappedResult(ctx->dev, &ctx->overlap, &n, TRUE) == 0) { fido_log_debug("%s: GetOverlappedResult", __func__); return (-1); } if (n != len + 1) { fido_log_debug("%s: expected %zu, got %zu", __func__, len + 1, (size_t)n); return (-1); } memcpy(buf, ctx->report + 1, len); explicit_bzero(ctx->report, sizeof(ctx->report)); return ((int)len); } int fido_hid_write(void *handle, const unsigned char *buf, size_t len) { struct hid_win *ctx = handle; OVERLAPPED overlap; DWORD n; memset(&overlap, 0, sizeof(overlap)); if (len != ctx->report_out_len) { fido_log_debug("%s: len %zu", __func__, len); return (-1); } if (WriteFile(ctx->dev, buf, (DWORD)len, NULL, &overlap) == 0 && GetLastError() != ERROR_IO_PENDING) { fido_log_debug("%s: WriteFile", __func__); return (-1); } if (GetOverlappedResult(ctx->dev, &overlap, &n, TRUE) == 0) { fido_log_debug("%s: GetOverlappedResult", __func__); return (-1); } if (n != len) { fido_log_debug("%s: expected %zu, got %zu", __func__, len, (size_t)n); return (-1); } return ((int)len); } size_t fido_hid_report_in_len(void *handle) { struct hid_win *ctx = handle; return (ctx->report_in_len - 1); } size_t fido_hid_report_out_len(void *handle) { struct hid_win *ctx = handle; return (ctx->report_out_len - 1); } libfido2-1.10.0/src/info.c000066400000000000000000000246621417126203300151620ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" static int decode_string(const cbor_item_t *item, void *arg) { fido_str_array_t *a = arg; const size_t i = a->len; /* keep ptr[x] and len consistent */ if (cbor_string_copy(item, &a->ptr[i]) < 0) { fido_log_debug("%s: cbor_string_copy", __func__); return (-1); } a->len++; return (0); } static int decode_string_array(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_string) < 0) { fido_log_debug("%s: decode_string", __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 decode_algorithm_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) { fido_algo_t *alg = 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_get_int(val) > INT_MAX || alg->cose != 0) { fido_log_debug("%s: alg", __func__); goto out; } alg->cose = -(int)cbor_get_int(val) - 1; } else if (!strcmp(name, "type")) { if (cbor_string_copy(val, &alg->type) < 0) { fido_log_debug("%s: type", __func__); goto out; } } ok = 0; out: free(name); return (ok); } static int decode_algorithm(const cbor_item_t *item, void *arg) { fido_algo_array_t *aa = arg; const size_t i = aa->len; if (cbor_isa_map(item) == false || cbor_map_is_definite(item) == false) { fido_log_debug("%s: cbor type", __func__); return (-1); } memset(&aa->ptr[i], 0, sizeof(aa->ptr[i])); if (cbor_map_iter(item, &aa->ptr[i], decode_algorithm_entry) < 0) { fido_log_debug("%s: decode_algorithm_entry", __func__); fido_algo_free(&aa->ptr[i]); return (-1); } /* keep ptr[x] and len consistent */ aa->len++; return (0); } static int decode_algorithms(const cbor_item_t *item, fido_algo_array_t *aa) { aa->ptr = NULL; aa->len = 0; if (cbor_isa_array(item) == false || cbor_array_is_definite(item) == false) { fido_log_debug("%s: cbor type", __func__); return (-1); } aa->ptr = calloc(cbor_array_size(item), sizeof(fido_algo_t)); if (aa->ptr == NULL) return (-1); if (cbor_array_iter(item, aa, decode_algorithm) < 0) { fido_log_debug("%s: decode_algorithm", __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_string_array(val, &ci->versions)); case 2: /* extensions */ return (decode_string_array(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)); case 7: /* maxCredentialCountInList */ return (cbor_decode_uint64(val, &ci->maxcredcntlst)); case 8: /* maxCredentialIdLength */ return (cbor_decode_uint64(val, &ci->maxcredidlen)); case 9: /* transports */ return (decode_string_array(val, &ci->transports)); case 10: /* algorithms */ return (decode_algorithms(val, &ci->algorithms)); case 14: /* fwVersion */ return (cbor_decode_uint64(val, &ci->fwversion)); case 15: /* maxCredBlobLen */ return (cbor_decode_uint64(val, &ci->maxcredbloblen)); default: /* ignore */ fido_log_debug("%s: cbor type", __func__); return (0); } } static int fido_dev_get_cbor_info_tx(fido_dev_t *dev, int *ms) { const unsigned char cbor[] = { CTAP_CBOR_GETINFO }; fido_log_debug("%s: dev=%p", __func__, (void *)dev); if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 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) { unsigned char reply[FIDO_MAXMSG]; int reply_len; fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev, (void *)ci, *ms); fido_cbor_info_reset(ci); if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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)); } int fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms) { int r; #ifdef USE_WINHELLO if (dev->flags & FIDO_DEV_WINHELLO) return (fido_winhello_get_cbor_info(dev, ci)); #endif if ((r = fido_dev_get_cbor_info_tx(dev, ms)) != 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) { int ms = dev->timeout_ms; return (fido_dev_get_cbor_info_wait(dev, ci, &ms)); } /* * 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))); } void fido_cbor_info_reset(fido_cbor_info_t *ci) { fido_str_array_free(&ci->versions); fido_str_array_free(&ci->extensions); fido_str_array_free(&ci->transports); fido_opt_array_free(&ci->options); fido_byte_array_free(&ci->protocols); fido_algo_array_free(&ci->algorithms); } 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; fido_cbor_info_reset(ci); 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); } char ** fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci) { return (ci->transports.ptr); } size_t fido_cbor_info_transports_len(const fido_cbor_info_t *ci) { return (ci->transports.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_maxcredbloblen(const fido_cbor_info_t *ci) { return (ci->maxcredbloblen); } uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) { return (ci->maxmsgsiz); } uint64_t fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci) { return (ci->maxcredcntlst); } uint64_t fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci) { return (ci->maxcredidlen); } uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *ci) { return (ci->fwversion); } 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); } size_t fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci) { return (ci->algorithms.len); } const char * fido_cbor_info_algorithm_type(const fido_cbor_info_t *ci, size_t idx) { if (idx >= ci->algorithms.len) return (NULL); return (ci->algorithms.ptr[idx].type); } int fido_cbor_info_algorithm_cose(const fido_cbor_info_t *ci, size_t idx) { if (idx >= ci->algorithms.len) return (0); return (ci->algorithms.ptr[idx].cose); } libfido2-1.10.0/src/io.c000066400000000000000000000170701417126203300146310ustar00rootroot00000000000000/* * Copyright (c) 2018 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" #include "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_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN]; } init; struct { uint8_t seq; uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN]; } cont; } body; }) #ifndef MIN #define MIN(x, y) ((x) > (y) ? (y) : (x)) #endif static int tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms) { struct timespec ts; int n; if (fido_time_now(&ts) != 0) return (-1); n = d->io.write(d->io_handle, pkt, len); if (fido_time_delta(&ts, ms) != 0) return (-1); return (n); } static int tx_empty(fido_dev_t *d, uint8_t cmd, int *ms) { struct frame *fp; unsigned char pkt[sizeof(*fp) + 1]; const size_t len = d->tx_len + 1; int n; memset(&pkt, 0, sizeof(pkt)); fp = (struct frame *)(pkt + 1); fp->cid = d->cid; fp->body.init.cmd = CTAP_FRAME_INIT | cmd; if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 || (size_t)n != len) return (-1); return (0); } static size_t tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms) { struct frame *fp; unsigned char pkt[sizeof(*fp) + 1]; const size_t len = d->tx_len + 1; int n; if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data)) return (0); memset(&pkt, 0, sizeof(pkt)); fp = (struct frame *)(pkt + 1); fp->cid = d->cid; fp->body.init.cmd = CTAP_FRAME_INIT | cmd; fp->body.init.bcnth = (count >> 8) & 0xff; fp->body.init.bcntl = count & 0xff; count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN); memcpy(&fp->body.init.data, buf, count); if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 || (size_t)n != len) return (0); return (count); } static size_t tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms) { struct frame *fp; unsigned char pkt[sizeof(*fp) + 1]; const size_t len = d->tx_len + 1; int n; if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data)) return (0); memset(&pkt, 0, sizeof(pkt)); fp = (struct frame *)(pkt + 1); fp->cid = d->cid; fp->body.cont.seq = seq; count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN); memcpy(&fp->body.cont.data, buf, count); if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 || (size_t)n != len) return (0); return (count); } static int tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms) { size_t n, sent; if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) { fido_log_debug("%s: tx_preamble", __func__); return (-1); } for (uint8_t seq = 0; sent < count; sent += n) { if (seq & 0x80) { fido_log_debug("%s: seq & 0x80", __func__); return (-1); } if ((n = tx_frame(d, seq++, buf + sent, count - sent, ms)) == 0) { fido_log_debug("%s: tx_frame", __func__); return (-1); } } return (0); } static int transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms) { struct timespec ts; int n; if (fido_time_now(&ts) != 0) return (-1); n = d->transport.tx(d, cmd, buf, count); if (fido_time_delta(&ts, ms) != 0) return (-1); return (n); } int fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms) { fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd); fido_log_xxd(buf, count, "%s", __func__); if (d->transport.tx != NULL) return (transport_tx(d, cmd, buf, count, ms)); if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) { fido_log_debug("%s: invalid argument", __func__); return (-1); } return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms)); } static int rx_frame(fido_dev_t *d, struct frame *fp, int *ms) { struct timespec ts; int n; memset(fp, 0, sizeof(*fp)); if (fido_time_now(&ts) != 0) return (-1); if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle, (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len) return (-1); return (fido_time_delta(&ts, ms)); } static int rx_preamble(fido_dev_t *d, uint8_t cmd, 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->cid == d->cid && fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE))); if (d->rx_len > sizeof(*fp)) return (-1); fido_log_xxd(fp, d->rx_len, "%s", __func__); #ifdef FIDO_FUZZ fp->body.init.cmd = (CTAP_FRAME_INIT | cmd); #endif if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) { fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)", __func__, fp->cid, d->cid, fp->body.init.cmd, cmd); return (-1); } return (0); } static int rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms) { struct frame f; size_t r, payload_len, init_data_len, cont_data_len; if (d->rx_len <= CTAP_INIT_HEADER_LEN || d->rx_len <= CTAP_CONT_HEADER_LEN) return (-1); init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN; cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN; if (init_data_len > sizeof(f.body.init.data) || cont_data_len > sizeof(f.body.cont.data)) return (-1); if (rx_preamble(d, cmd, &f, ms) < 0) { fido_log_debug("%s: rx_preamble", __func__); return (-1); } payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl); fido_log_debug("%s: payload_len=%zu", __func__, payload_len); if (count < payload_len) { fido_log_debug("%s: count < payload_len", __func__); return (-1); } if (payload_len < init_data_len) { memcpy(buf, f.body.init.data, payload_len); return ((int)payload_len); } memcpy(buf, f.body.init.data, init_data_len); r = init_data_len; for (int seq = 0; r < payload_len; seq++) { if (rx_frame(d, &f, ms) < 0) { fido_log_debug("%s: rx_frame", __func__); return (-1); } fido_log_xxd(&f, d->rx_len, "%s", __func__); #ifdef FIDO_FUZZ f.cid = d->cid; f.body.cont.seq = (uint8_t)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); } if (payload_len - r > cont_data_len) { memcpy(buf + r, f.body.cont.data, cont_data_len); r += cont_data_len; } else { memcpy(buf + r, f.body.cont.data, payload_len - r); r += payload_len - r; /* break */ } } return ((int)r); } static int transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms) { struct timespec ts; int n; if (fido_time_now(&ts) != 0) return (-1); n = d->transport.rx(d, cmd, buf, count, *ms); if (fido_time_delta(&ts, ms) != 0) return (-1); return (n); } int fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms) { int n; fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d, cmd, *ms); if (d->transport.rx != NULL) return (transport_rx(d, cmd, buf, count, ms)); if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) { fido_log_debug("%s: invalid argument", __func__); return (-1); } if ((n = rx(d, cmd, buf, count, ms)) >= 0) fido_log_xxd(buf, (size_t)n, "%s", __func__); return (n); } int fido_rx_cbor_status(fido_dev_t *d, int *ms) { unsigned char reply[FIDO_MAXMSG]; int reply_len; if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply), ms)) < 0 || (size_t)reply_len < 1) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } return (reply[0]); } libfido2-1.10.0/src/iso7816.c000066400000000000000000000027231417126203300153410ustar00rootroot00000000000000/* * Copyright (c) 2018 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" iso7816_apdu_t * iso7816_new(uint8_t cla, 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 + 2; /* le1 le2 */ 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.cla = cla; apdu->header.ins = ins; apdu->header.p1 = p1; apdu->header.lc2 = (uint8_t)((payload_len >> 8) & 0xff); apdu->header.lc3 = (uint8_t)(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; freezero(apdu, apdu->alloc_len); *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)(apdu->payload_len - 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 - offsetof(iso7816_apdu_t, header) - (sizeof(iso7816_apdu_t) - offsetof(iso7816_apdu_t, payload)); } libfido2-1.10.0/src/iso7816.h000066400000000000000000000020101417126203300153330ustar00rootroot00000000000000/* * 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 #include #include "packed.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ 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; }) typedef struct iso7816_apdu { size_t alloc_len; uint16_t payload_len; uint8_t *payload_ptr; iso7816_header_t header; uint8_t payload[]; } iso7816_apdu_t; 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, uint8_t, uint16_t); size_t iso7816_len(const iso7816_apdu_t *); void iso7816_free(iso7816_apdu_t **); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_ISO7816_H */ libfido2-1.10.0/src/largeblob.c000066400000000000000000000516251417126203300161570ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include "fido.h" #include "fido/es256.h" #define LARGEBLOB_DIGEST_LENGTH 16 #define LARGEBLOB_NONCE_LENGTH 12 #define LARGEBLOB_TAG_LENGTH 16 typedef struct largeblob { size_t origsiz; fido_blob_t ciphertext; fido_blob_t nonce; } largeblob_t; static largeblob_t * largeblob_new(void) { return calloc(1, sizeof(largeblob_t)); } static void largeblob_reset(largeblob_t *blob) { fido_blob_reset(&blob->ciphertext); fido_blob_reset(&blob->nonce); blob->origsiz = 0; } static void largeblob_free(largeblob_t **blob_ptr) { largeblob_t *blob; if (blob_ptr == NULL || (blob = *blob_ptr) == NULL) return; largeblob_reset(blob); free(blob); *blob_ptr = NULL; } static int largeblob_aad(fido_blob_t *aad, uint64_t size) { uint8_t buf[4 + sizeof(uint64_t)]; buf[0] = 0x62; /* b */ buf[1] = 0x6c; /* l */ buf[2] = 0x6f; /* o */ buf[3] = 0x62; /* b */ size = htole64(size); memcpy(&buf[4], &size, sizeof(uint64_t)); return fido_blob_set(aad, buf, sizeof(buf)); } static fido_blob_t * largeblob_decrypt(const largeblob_t *blob, const fido_blob_t *key) { fido_blob_t *plaintext = NULL, *aad = NULL; int ok = -1; if ((plaintext = fido_blob_new()) == NULL || (aad = fido_blob_new()) == NULL) { fido_log_debug("%s: fido_blob_new", __func__); goto fail; } if (largeblob_aad(aad, blob->origsiz) < 0) { fido_log_debug("%s: largeblob_aad", __func__); goto fail; } if (aes256_gcm_dec(key, &blob->nonce, aad, &blob->ciphertext, plaintext) < 0) { fido_log_debug("%s: aes256_gcm_dec", __func__); goto fail; } ok = 0; fail: fido_blob_free(&aad); if (ok < 0) fido_blob_free(&plaintext); return plaintext; } static int largeblob_get_nonce(largeblob_t *blob) { uint8_t buf[LARGEBLOB_NONCE_LENGTH]; int ok = -1; if (fido_get_random(buf, sizeof(buf)) < 0) { fido_log_debug("%s: fido_get_random", __func__); goto fail; } if (fido_blob_set(&blob->nonce, buf, sizeof(buf)) < 0) { fido_log_debug("%s: fido_blob_set", __func__); goto fail; } ok = 0; fail: explicit_bzero(buf, sizeof(buf)); return ok; } static int largeblob_seal(largeblob_t *blob, const fido_blob_t *body, const fido_blob_t *key) { fido_blob_t *plaintext = NULL, *aad = NULL; int ok = -1; if ((plaintext = fido_blob_new()) == NULL || (aad = fido_blob_new()) == NULL) { fido_log_debug("%s: fido_blob_new", __func__); goto fail; } if (fido_compress(plaintext, body) != FIDO_OK) { fido_log_debug("%s: fido_compress", __func__); goto fail; } if (largeblob_aad(aad, body->len) < 0) { fido_log_debug("%s: largeblob_aad", __func__); goto fail; } if (largeblob_get_nonce(blob) < 0) { fido_log_debug("%s: largeblob_get_nonce", __func__); goto fail; } if (aes256_gcm_enc(key, &blob->nonce, aad, plaintext, &blob->ciphertext) < 0) { fido_log_debug("%s: aes256_gcm_enc", __func__); goto fail; } blob->origsiz = body->len; ok = 0; fail: fido_blob_free(&plaintext); fido_blob_free(&aad); return ok; } static int largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count, int *ms) { fido_blob_t f; cbor_item_t *argv[3]; int r; memset(argv, 0, sizeof(argv)); memset(&f, 0, sizeof(f)); if ((argv[0] = cbor_build_uint(count)) == NULL || (argv[2] = cbor_build_uint(offset)) == NULL) { fido_log_debug("%s: cbor encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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 parse_largeblob_reply(const cbor_item_t *key, const cbor_item_t *val, void *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 fido_blob_decode(val, arg); } static int largeblob_get_rx(fido_dev_t *dev, fido_blob_t **chunk, int *ms) { unsigned char reply[FIDO_MAXMSG]; int reply_len, r; *chunk = NULL; if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return FIDO_ERR_RX; } if ((*chunk = fido_blob_new()) == NULL) { fido_log_debug("%s: fido_blob_new", __func__); return FIDO_ERR_INTERNAL; } if ((r = cbor_parse_reply(reply, (size_t)reply_len, *chunk, parse_largeblob_reply)) != FIDO_OK) { fido_log_debug("%s: parse_largeblob_reply", __func__); fido_blob_free(chunk); return r; } return FIDO_OK; } static cbor_item_t * largeblob_array_load(const uint8_t *ptr, size_t len) { struct cbor_load_result cbor; cbor_item_t *item; if (len < LARGEBLOB_DIGEST_LENGTH) { fido_log_debug("%s: len", __func__); return NULL; } len -= LARGEBLOB_DIGEST_LENGTH; if ((item = cbor_load(ptr, len, &cbor)) == NULL) { fido_log_debug("%s: cbor_load", __func__); return NULL; } if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) { fido_log_debug("%s: cbor type", __func__); cbor_decref(&item); return NULL; } return item; } static size_t get_chunklen(fido_dev_t *dev) { uint64_t maxchunklen; if ((maxchunklen = fido_dev_maxmsgsize(dev)) > SIZE_MAX) maxchunklen = SIZE_MAX; if (maxchunklen > FIDO_MAXMSG) maxchunklen = FIDO_MAXMSG; maxchunklen = maxchunklen > 64 ? maxchunklen - 64 : 0; return (size_t)maxchunklen; } static int largeblob_do_decode(const cbor_item_t *key, const cbor_item_t *val, void *arg) { largeblob_t *blob = arg; uint64_t origsiz; 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: /* ciphertext */ if (fido_blob_decode(val, &blob->ciphertext) < 0 || blob->ciphertext.len < LARGEBLOB_TAG_LENGTH) return -1; return 0; case 2: /* nonce */ if (fido_blob_decode(val, &blob->nonce) < 0 || blob->nonce.len != LARGEBLOB_NONCE_LENGTH) return -1; return 0; case 3: /* origSize */ if (!cbor_isa_uint(val) || (origsiz = cbor_get_int(val)) > SIZE_MAX) return -1; blob->origsiz = (size_t)origsiz; return 0; default: /* ignore */ fido_log_debug("%s: cbor type", __func__); return 0; } } static int largeblob_decode(largeblob_t *blob, const cbor_item_t *item) { if (!cbor_isa_map(item) || !cbor_map_is_definite(item)) { fido_log_debug("%s: cbor type", __func__); return -1; } if (cbor_map_iter(item, blob, largeblob_do_decode) < 0) { fido_log_debug("%s: cbor_map_iter", __func__); return -1; } if (fido_blob_is_empty(&blob->ciphertext) || fido_blob_is_empty(&blob->nonce) || blob->origsiz == 0) { fido_log_debug("%s: incomplete blob", __func__); return -1; } return 0; } static cbor_item_t * largeblob_encode(const fido_blob_t *body, const fido_blob_t *key) { largeblob_t *blob; cbor_item_t *argv[3], *item = NULL; memset(argv, 0, sizeof(argv)); if ((blob = largeblob_new()) == NULL || largeblob_seal(blob, body, key) < 0) { fido_log_debug("%s: largeblob_seal", __func__); goto fail; } if ((argv[0] = fido_blob_encode(&blob->ciphertext)) == NULL || (argv[1] = fido_blob_encode(&blob->nonce)) == NULL || (argv[2] = cbor_build_uint(blob->origsiz)) == NULL) { fido_log_debug("%s: cbor encode", __func__); goto fail; } item = cbor_flatten_vector(argv, nitems(argv)); fail: cbor_vector_free(argv, nitems(argv)); largeblob_free(&blob); return item; } static int largeblob_array_lookup(fido_blob_t *out, size_t *idx, const cbor_item_t *item, const fido_blob_t *key) { cbor_item_t **v; fido_blob_t *plaintext = NULL; largeblob_t blob; int r; memset(&blob, 0, sizeof(blob)); if (idx != NULL) *idx = 0; if ((v = cbor_array_handle(item)) == NULL) return FIDO_ERR_INVALID_ARGUMENT; for (size_t i = 0; i < cbor_array_size(item); i++) { if (largeblob_decode(&blob, v[i]) < 0 || (plaintext = largeblob_decrypt(&blob, key)) == NULL) { fido_log_debug("%s: largeblob_decode", __func__); largeblob_reset(&blob); continue; } if (idx != NULL) *idx = i; break; } if (plaintext == NULL) { fido_log_debug("%s: not found", __func__); return FIDO_ERR_NOTFOUND; } if (out != NULL) r = fido_uncompress(out, plaintext, blob.origsiz); else r = FIDO_OK; fido_blob_free(&plaintext); largeblob_reset(&blob); return r; } static int largeblob_array_digest(u_char out[LARGEBLOB_DIGEST_LENGTH], const u_char *data, size_t len) { u_char dgst[SHA256_DIGEST_LENGTH]; if (data == NULL || len == 0) return -1; if (SHA256(data, len, dgst) != dgst) return -1; memcpy(out, dgst, LARGEBLOB_DIGEST_LENGTH); return 0; } static int largeblob_array_check(const fido_blob_t *array) { u_char expected_hash[LARGEBLOB_DIGEST_LENGTH]; size_t body_len; fido_log_xxd(array->ptr, array->len, __func__); if (array->len < sizeof(expected_hash)) { fido_log_debug("%s: len %zu", __func__, array->len); return -1; } body_len = array->len - sizeof(expected_hash); if (largeblob_array_digest(expected_hash, array->ptr, body_len) < 0) { fido_log_debug("%s: largeblob_array_digest", __func__); return -1; } return timingsafe_bcmp(expected_hash, array->ptr + body_len, sizeof(expected_hash)); } static int largeblob_get_array(fido_dev_t *dev, cbor_item_t **item, int *ms) { fido_blob_t *array, *chunk = NULL; size_t n; int r; *item = NULL; if ((n = get_chunklen(dev)) == 0) return FIDO_ERR_INVALID_ARGUMENT; if ((array = fido_blob_new()) == NULL) return FIDO_ERR_INTERNAL; do { fido_blob_free(&chunk); if ((r = largeblob_get_tx(dev, array->len, n, ms)) != FIDO_OK || (r = largeblob_get_rx(dev, &chunk, ms)) != FIDO_OK) { fido_log_debug("%s: largeblob_get_wait %zu/%zu", __func__, array->len, n); goto fail; } if (fido_blob_append(array, chunk->ptr, chunk->len) < 0) { fido_log_debug("%s: fido_blob_append", __func__); r = FIDO_ERR_INTERNAL; goto fail; } } while (chunk->len == n); if (largeblob_array_check(array) != 0) *item = cbor_new_definite_array(0); /* per spec */ else *item = largeblob_array_load(array->ptr, array->len); if (*item == NULL) r = FIDO_ERR_INTERNAL; else r = FIDO_OK; fail: fido_blob_free(&array); fido_blob_free(&chunk); return r; } static int prepare_hmac(size_t offset, const u_char *data, size_t len, fido_blob_t *hmac) { uint8_t buf[32 + 2 + sizeof(uint32_t) + SHA256_DIGEST_LENGTH]; uint32_t u32_offset; if (data == NULL || len == 0) { fido_log_debug("%s: invalid data=%p, len=%zu", __func__, (const void *)data, len); return -1; } if (offset > UINT32_MAX) { fido_log_debug("%s: invalid offset=%zu", __func__, offset); return -1; } memset(buf, 0xff, 32); buf[32] = CTAP_CBOR_LARGEBLOB; buf[33] = 0x00; u32_offset = htole32((uint32_t)offset); memcpy(&buf[34], &u32_offset, sizeof(uint32_t)); if (SHA256(data, len, &buf[38]) != &buf[38]) { fido_log_debug("%s: SHA256", __func__); return -1; } return fido_blob_set(hmac, buf, sizeof(buf)); } static int largeblob_set_tx(fido_dev_t *dev, const fido_blob_t *token, const u_char *chunk, size_t chunk_len, size_t offset, size_t totalsiz, int *ms) { fido_blob_t *hmac = NULL, f; cbor_item_t *argv[6]; int r; memset(argv, 0, sizeof(argv)); memset(&f, 0, sizeof(f)); if ((argv[1] = cbor_build_bytestring(chunk, chunk_len)) == NULL || (argv[2] = cbor_build_uint(offset)) == NULL || (offset == 0 && (argv[3] = cbor_build_uint(totalsiz)) == NULL)) { fido_log_debug("%s: cbor encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (token != NULL) { if ((hmac = fido_blob_new()) == NULL || prepare_hmac(offset, chunk, chunk_len, hmac) < 0 || (argv[4] = cbor_encode_pin_auth(dev, token, hmac)) == NULL || (argv[5] = cbor_encode_pin_opt(dev)) == NULL) { fido_log_debug("%s: cbor_encode_pin_auth", __func__); r = FIDO_ERR_INTERNAL; goto fail; } } if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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(&hmac); free(f.ptr); return r; } static int largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token, int *ms) { es256_pk_t *pk = NULL; fido_blob_t *ecdh = NULL; int r; if ((*token = fido_blob_new()) == NULL) return FIDO_ERR_INTERNAL; if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_LARGEBLOB, pin, ecdh, pk, NULL, *token, ms)) != FIDO_OK) { fido_log_debug("%s: fido_dev_get_uv_token", __func__); goto fail; } r = FIDO_OK; fail: if (r != FIDO_OK) fido_blob_free(token); fido_blob_free(&ecdh); es256_pk_free(&pk); return r; } static int largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin, int *ms) { unsigned char dgst[SHA256_DIGEST_LENGTH]; fido_blob_t cbor, *token = NULL; size_t chunklen, maxchunklen, totalsize; int r; memset(&cbor, 0, sizeof(cbor)); if ((maxchunklen = get_chunklen(dev)) == 0) { fido_log_debug("%s: maxchunklen=%zu", __func__, maxchunklen); r = FIDO_ERR_INVALID_ARGUMENT; goto fail; } if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) { fido_log_debug("%s: cbor type", __func__); r = FIDO_ERR_INVALID_ARGUMENT; goto fail; } if ((fido_blob_serialise(&cbor, item)) < 0) { fido_log_debug("%s: fido_blob_serialise", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (cbor.len > SIZE_MAX - sizeof(dgst)) { fido_log_debug("%s: cbor.len=%zu", __func__, cbor.len); r = FIDO_ERR_INVALID_ARGUMENT; goto fail; } if (SHA256(cbor.ptr, cbor.len, dgst) != dgst) { fido_log_debug("%s: SHA256", __func__); r = FIDO_ERR_INTERNAL; goto fail; } totalsize = cbor.len + sizeof(dgst) - 16; /* the first 16 bytes only */ if (pin != NULL || fido_dev_supports_permissions(dev)) { if ((r = largeblob_get_uv_token(dev, pin, &token, ms)) != FIDO_OK) { fido_log_debug("%s: largeblob_get_uv_token", __func__); goto fail; } } for (size_t offset = 0; offset < cbor.len; offset += chunklen) { if ((chunklen = cbor.len - offset) > maxchunklen) chunklen = maxchunklen; if ((r = largeblob_set_tx(dev, token, cbor.ptr + offset, chunklen, offset, totalsize, ms)) != FIDO_OK || (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { fido_log_debug("%s: body", __func__); goto fail; } } if ((r = largeblob_set_tx(dev, token, dgst, sizeof(dgst) - 16, cbor.len, totalsize, ms)) != FIDO_OK || (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { fido_log_debug("%s: dgst", __func__); goto fail; } r = FIDO_OK; fail: fido_blob_free(&token); fido_blob_reset(&cbor); return r; } static int largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item, const char *pin, int *ms) { cbor_item_t *array = NULL; size_t idx; int r; if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) { fido_log_debug("%s: largeblob_get_array", __func__); goto fail; } switch (r = largeblob_array_lookup(NULL, &idx, array, key)) { case FIDO_OK: if (!cbor_array_replace(array, idx, item)) { r = FIDO_ERR_INTERNAL; goto fail; } break; case FIDO_ERR_NOTFOUND: if (cbor_array_append(&array, item) < 0) { r = FIDO_ERR_INTERNAL; goto fail; } break; default: fido_log_debug("%s: largeblob_array_lookup", __func__); goto fail; } if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) { fido_log_debug("%s: largeblob_set_array", __func__); goto fail; } r = FIDO_OK; fail: if (array != NULL) cbor_decref(&array); return r; } static int largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin, int *ms) { cbor_item_t *array = NULL; size_t idx; int r; if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) { fido_log_debug("%s: largeblob_get_array", __func__); goto fail; } if ((r = largeblob_array_lookup(NULL, &idx, array, key)) != FIDO_OK) { fido_log_debug("%s: largeblob_array_lookup", __func__); goto fail; } if (cbor_array_drop(&array, idx) < 0) { fido_log_debug("%s: cbor_array_drop", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) { fido_log_debug("%s: largeblob_set_array", __func__); goto fail; } r = FIDO_OK; fail: if (array != NULL) cbor_decref(&array); return r; } int fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr, size_t key_len, unsigned char **blob_ptr, size_t *blob_len) { cbor_item_t *item = NULL; fido_blob_t key, body; int ms = dev->timeout_ms; int r; memset(&key, 0, sizeof(key)); memset(&body, 0, sizeof(body)); if (key_len != 32) { fido_log_debug("%s: invalid key len %zu", __func__, key_len); return FIDO_ERR_INVALID_ARGUMENT; } if (blob_ptr == NULL || blob_len == NULL) { fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%p", __func__, (const void *)blob_ptr, (const void *)blob_len); return FIDO_ERR_INVALID_ARGUMENT; } *blob_ptr = NULL; *blob_len = 0; if (fido_blob_set(&key, key_ptr, key_len) < 0) { fido_log_debug("%s: fido_blob_set", __func__); return FIDO_ERR_INTERNAL; } if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) { fido_log_debug("%s: largeblob_get_array", __func__); goto fail; } if ((r = largeblob_array_lookup(&body, NULL, item, &key)) != FIDO_OK) fido_log_debug("%s: largeblob_array_lookup", __func__); else { *blob_ptr = body.ptr; *blob_len = body.len; } fail: if (item != NULL) cbor_decref(&item); fido_blob_reset(&key); return r; } int fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr, size_t key_len, const unsigned char *blob_ptr, size_t blob_len, const char *pin) { cbor_item_t *item = NULL; fido_blob_t key, body; int ms = dev->timeout_ms; int r; memset(&key, 0, sizeof(key)); memset(&body, 0, sizeof(body)); if (key_len != 32) { fido_log_debug("%s: invalid key len %zu", __func__, key_len); return FIDO_ERR_INVALID_ARGUMENT; } if (blob_ptr == NULL || blob_len == 0) { fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%zu", __func__, (const void *)blob_ptr, blob_len); return FIDO_ERR_INVALID_ARGUMENT; } if (fido_blob_set(&key, key_ptr, key_len) < 0 || fido_blob_set(&body, blob_ptr, blob_len) < 0) { fido_log_debug("%s: fido_blob_set", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if ((item = largeblob_encode(&body, &key)) == NULL) { fido_log_debug("%s: largeblob_encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if ((r = largeblob_add(dev, &key, item, pin, &ms)) != FIDO_OK) fido_log_debug("%s: largeblob_add", __func__); fail: if (item != NULL) cbor_decref(&item); fido_blob_reset(&key); fido_blob_reset(&body); return r; } int fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr, size_t key_len, const char *pin) { fido_blob_t key; int ms = dev->timeout_ms; int r; memset(&key, 0, sizeof(key)); if (key_len != 32) { fido_log_debug("%s: invalid key len %zu", __func__, key_len); return FIDO_ERR_INVALID_ARGUMENT; } if (fido_blob_set(&key, key_ptr, key_len) < 0) { fido_log_debug("%s: fido_blob_set", __func__); return FIDO_ERR_INTERNAL; } if ((r = largeblob_drop(dev, &key, pin, &ms)) != FIDO_OK) fido_log_debug("%s: largeblob_drop", __func__); fido_blob_reset(&key); return r; } int fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr, size_t *cbor_len) { cbor_item_t *item = NULL; fido_blob_t cbor; int ms = dev->timeout_ms; int r; memset(&cbor, 0, sizeof(cbor)); if (cbor_ptr == NULL || cbor_len == NULL) { fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%p", __func__, (const void *)cbor_ptr, (const void *)cbor_len); return FIDO_ERR_INVALID_ARGUMENT; } *cbor_ptr = NULL; *cbor_len = 0; if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) { fido_log_debug("%s: largeblob_get_array", __func__); return r; } if (fido_blob_serialise(&cbor, item) < 0) { fido_log_debug("%s: fido_blob_serialise", __func__); r = FIDO_ERR_INTERNAL; } else { *cbor_ptr = cbor.ptr; *cbor_len = cbor.len; } cbor_decref(&item); return r; } int fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr, size_t cbor_len, const char *pin) { cbor_item_t *item = NULL; struct cbor_load_result cbor_result; int ms = dev->timeout_ms; int r; if (cbor_ptr == NULL || cbor_len == 0) { fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%zu", __func__, (const void *)cbor_ptr, cbor_len); return FIDO_ERR_INVALID_ARGUMENT; } if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) { fido_log_debug("%s: cbor_load", __func__); return FIDO_ERR_INVALID_ARGUMENT; } if ((r = largeblob_set_array(dev, item, pin, &ms)) != FIDO_OK) fido_log_debug("%s: largeblob_set_array", __func__); cbor_decref(&item); return r; } libfido2-1.10.0/src/libfido2.pc.in000066400000000000000000000004621417126203300164760ustar00rootroot00000000000000prefix=@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} libfido2-1.10.0/src/log.c000066400000000000000000000042471417126203300150050ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #undef _GNU_SOURCE /* XSI strerror_r() */ #include #include #include "fido.h" #ifndef FIDO_NO_DIAGNOSTIC #define XXDLEN 32 #define XXDROW 128 #define LINELEN 256 #ifndef TLS #define TLS #endif static TLS int logging; static TLS fido_log_handler_t *log_handler; static void log_on_stderr(const char *str) { fprintf(stderr, "%s", str); } static void do_log(const char *suffix, const char *fmt, va_list args) { char line[LINELEN], body[LINELEN]; vsnprintf(body, sizeof(body), fmt, args); if (suffix != NULL) snprintf(line, sizeof(line), "%.180s: %.70s\n", body, suffix); else snprintf(line, sizeof(line), "%.180s\n", body); log_handler(line); } void fido_log_init(void) { logging = 1; log_handler = log_on_stderr; } void fido_log_debug(const char *fmt, ...) { va_list args; if (!logging || log_handler == NULL) return; va_start(args, fmt); do_log(NULL, fmt, args); va_end(args); } void fido_log_xxd(const void *buf, size_t count, const char *fmt, ...) { const uint8_t *ptr = buf; char row[XXDROW], xxd[XXDLEN]; va_list args; if (!logging || log_handler == NULL) return; snprintf(row, sizeof(row), "buf=%p, len=%zu", buf, count); va_start(args, fmt); do_log(row, fmt, args); va_end(args); *row = '\0'; for (size_t i = 0; i < count; i++) { *xxd = '\0'; if (i % 16 == 0) snprintf(xxd, sizeof(xxd), "%04zu: %02x", i, *ptr++); else snprintf(xxd, sizeof(xxd), " %02x", *ptr++); strlcat(row, xxd, sizeof(row)); if (i % 16 == 15 || i == count - 1) { fido_log_debug("%s", row); *row = '\0'; } } } void fido_log_error(int errnum, const char *fmt, ...) { char errstr[LINELEN]; va_list args; if (!logging || log_handler == NULL) return; if (strerror_r(errnum, errstr, sizeof(errstr)) != 0) snprintf(errstr, sizeof(errstr), "error %d", errnum); va_start(args, fmt); do_log(errstr, fmt, args); va_end(args); } void fido_set_log_handler(fido_log_handler_t *handler) { if (handler != NULL) log_handler = handler; } #endif /* !FIDO_NO_DIAGNOSTIC */ libfido2-1.10.0/src/netlink.c000066400000000000000000000400031417126203300156560ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #include "fido.h" #include "netlink.h" #ifdef FIDO_FUZZ static ssize_t (*fuzz_read)(int, void *, size_t); static ssize_t (*fuzz_write)(int, const void *, size_t); # define READ fuzz_read # define WRITE fuzz_write #else # define READ read # define WRITE write #endif #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif #define NETLINK_POLL_MS 100 /* XXX avoid signed NLA_ALIGNTO */ #undef NLA_HDRLEN #define NLA_HDRLEN NLMSG_ALIGN(sizeof(struct nlattr)) typedef struct nlmsgbuf { size_t siz; /* alloc size */ size_t len; /* of payload */ unsigned char *ptr; /* in payload */ union { struct nlmsghdr nlmsg; char buf[NLMSG_HDRLEN]; /* align */ } u; unsigned char payload[]; } nlmsgbuf_t; typedef struct genlmsgbuf { union { struct genlmsghdr genl; char buf[GENL_HDRLEN]; /* align */ } u; } genlmsgbuf_t; typedef struct nlamsgbuf { size_t siz; /* alloc size */ size_t len; /* of payload */ unsigned char *ptr; /* in payload */ union { struct nlattr nla; char buf[NLA_HDRLEN]; /* align */ } u; unsigned char payload[]; } nlamsgbuf_t; typedef struct nl_family { uint16_t id; uint32_t mcastgrp; } nl_family_t; typedef struct nl_poll { uint32_t dev; unsigned int eventcnt; } nl_poll_t; typedef struct nl_target { int found; uint32_t *value; } nl_target_t; static const void * nlmsg_ptr(const nlmsgbuf_t *m) { return (&m->u.nlmsg); } static size_t nlmsg_len(const nlmsgbuf_t *m) { return (m->u.nlmsg.nlmsg_len); } static uint16_t nlmsg_type(const nlmsgbuf_t *m) { return (m->u.nlmsg.nlmsg_type); } static nlmsgbuf_t * nlmsg_new(uint16_t type, uint16_t flags, size_t len) { nlmsgbuf_t *m; size_t siz; if (len > SIZE_MAX - sizeof(*m) || (siz = sizeof(*m) + len) > UINT16_MAX || (m = calloc(1, siz)) == NULL) return (NULL); m->siz = siz; m->len = len; m->ptr = m->payload; m->u.nlmsg.nlmsg_type = type; m->u.nlmsg.nlmsg_flags = NLM_F_REQUEST | flags; m->u.nlmsg.nlmsg_len = NLMSG_HDRLEN; return (m); } static nlamsgbuf_t * nla_from_buf(const unsigned char **ptr, size_t *len) { nlamsgbuf_t h, *a; size_t nlalen, skip; if (*len < sizeof(h.u)) return (NULL); memset(&h, 0, sizeof(h)); memcpy(&h.u, *ptr, sizeof(h.u)); if ((nlalen = h.u.nla.nla_len) < sizeof(h.u) || nlalen > *len || nlalen - sizeof(h.u) > UINT16_MAX || nlalen > SIZE_MAX - sizeof(*a) || (skip = NLMSG_ALIGN(nlalen)) > *len || (a = calloc(1, sizeof(*a) + nlalen - sizeof(h.u))) == NULL) return (NULL); memcpy(&a->u, *ptr, nlalen); a->siz = sizeof(*a) + nlalen - sizeof(h.u); a->ptr = a->payload; a->len = nlalen - sizeof(h.u); *ptr += skip; *len -= skip; return (a); } static nlamsgbuf_t * nla_getattr(nlamsgbuf_t *a) { return (nla_from_buf((void *)&a->ptr, &a->len)); } static uint16_t nla_type(const nlamsgbuf_t *a) { return (a->u.nla.nla_type); } static nlamsgbuf_t * nlmsg_getattr(nlmsgbuf_t *m) { return (nla_from_buf((void *)&m->ptr, &m->len)); } static int nla_read(nlamsgbuf_t *a, void *buf, size_t cnt) { if (cnt > a->u.nla.nla_len || fido_buf_read((void *)&a->ptr, &a->len, buf, cnt) < 0) return (-1); a->u.nla.nla_len = (uint16_t)(a->u.nla.nla_len - cnt); return (0); } static nlmsgbuf_t * nlmsg_from_buf(const unsigned char **ptr, size_t *len) { nlmsgbuf_t h, *m; size_t msglen, skip; if (*len < sizeof(h.u)) return (NULL); memset(&h, 0, sizeof(h)); memcpy(&h.u, *ptr, sizeof(h.u)); if ((msglen = h.u.nlmsg.nlmsg_len) < sizeof(h.u) || msglen > *len || msglen - sizeof(h.u) > UINT16_MAX || (skip = NLMSG_ALIGN(msglen)) > *len || (m = nlmsg_new(0, 0, msglen - sizeof(h.u))) == NULL) return (NULL); memcpy(&m->u, *ptr, msglen); *ptr += skip; *len -= skip; return (m); } static int nlmsg_read(nlmsgbuf_t *m, void *buf, size_t cnt) { if (cnt > m->u.nlmsg.nlmsg_len || fido_buf_read((void *)&m->ptr, &m->len, buf, cnt) < 0) return (-1); m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len - cnt); return (0); } static int nlmsg_write(nlmsgbuf_t *m, const void *buf, size_t cnt) { if (cnt > UINT32_MAX - m->u.nlmsg.nlmsg_len || fido_buf_write(&m->ptr, &m->len, buf, cnt) < 0) return (-1); m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len + cnt); return (0); } static int nlmsg_set_genl(nlmsgbuf_t *m, uint8_t cmd) { genlmsgbuf_t g; memset(&g, 0, sizeof(g)); g.u.genl.cmd = cmd; g.u.genl.version = NFC_GENL_VERSION; return (nlmsg_write(m, &g, sizeof(g))); } static int nlmsg_get_genl(nlmsgbuf_t *m, uint8_t cmd) { genlmsgbuf_t g; memset(&g, 0, sizeof(g)); if (nlmsg_read(m, &g, sizeof(g)) < 0 || g.u.genl.cmd != cmd) return (-1); return (0); } static int nlmsg_get_status(nlmsgbuf_t *m) { int status; if (nlmsg_read(m, &status, sizeof(status)) < 0 || status == INT_MIN) return (-1); if (status < 0) status = -status; return (status); } static int nlmsg_setattr(nlmsgbuf_t *m, uint16_t type, const void *ptr, size_t len) { int r; char *padding; size_t skip; nlamsgbuf_t a; if ((skip = NLMSG_ALIGN(len)) > UINT16_MAX - sizeof(a.u) || skip < len || (padding = calloc(1, skip - len)) == NULL) return (-1); memset(&a, 0, sizeof(a)); a.u.nla.nla_type = type; a.u.nla.nla_len = (uint16_t)(len + sizeof(a.u)); r = nlmsg_write(m, &a.u, sizeof(a.u)) < 0 || nlmsg_write(m, ptr, len) < 0 || nlmsg_write(m, padding, skip - len) < 0 ? -1 : 0; free(padding); return (r); } static int nlmsg_set_u16(nlmsgbuf_t *m, uint16_t type, uint16_t val) { return (nlmsg_setattr(m, type, &val, sizeof(val))); } static int nlmsg_set_u32(nlmsgbuf_t *m, uint16_t type, uint32_t val) { return (nlmsg_setattr(m, type, &val, sizeof(val))); } static int nlmsg_set_str(nlmsgbuf_t *m, uint16_t type, const char *val) { return (nlmsg_setattr(m, type, val, strlen(val) + 1)); } static int nla_get_u16(nlamsgbuf_t *a, uint16_t *v) { return (nla_read(a, v, sizeof(*v))); } static int nla_get_u32(nlamsgbuf_t *a, uint32_t *v) { return (nla_read(a, v, sizeof(*v))); } static char * nla_get_str(nlamsgbuf_t *a) { size_t n; char *s = NULL; if ((n = a->len) < 1 || a->ptr[n - 1] != '\0' || (s = calloc(1, n)) == NULL || nla_read(a, s, n) < 0) { free(s); return (NULL); } s[n - 1] = '\0'; return (s); } static int nlmsg_tx(int fd, const nlmsgbuf_t *m) { ssize_t r; if ((r = WRITE(fd, nlmsg_ptr(m), nlmsg_len(m))) == -1) { fido_log_error(errno, "%s: write", __func__); return (-1); } if (r < 0 || (size_t)r != nlmsg_len(m)) { fido_log_debug("%s: %zd != %zu", __func__, r, nlmsg_len(m)); return (-1); } fido_log_xxd(nlmsg_ptr(m), nlmsg_len(m), "%s", __func__); return (0); } static ssize_t nlmsg_rx(int fd, unsigned char *ptr, size_t len, int ms) { ssize_t r; if (len > SSIZE_MAX) { fido_log_debug("%s: len", __func__); return (-1); } if (fido_hid_unix_wait(fd, ms, NULL) < 0) { fido_log_debug("%s: fido_hid_unix_wait", __func__); return (-1); } if ((r = READ(fd, ptr, len)) == -1) { fido_log_error(errno, "%s: read %zd", __func__, r); return (-1); } fido_log_xxd(ptr, (size_t)r, "%s", __func__); return (r); } static int nlmsg_iter(nlmsgbuf_t *m, void *arg, int (*parser)(nlamsgbuf_t *, void *)) { nlamsgbuf_t *a; int r; while ((a = nlmsg_getattr(m)) != NULL) { r = parser(a, arg); free(a); if (r < 0) { fido_log_debug("%s: parser", __func__); return (-1); } } return (0); } static int nla_iter(nlamsgbuf_t *g, void *arg, int (*parser)(nlamsgbuf_t *, void *)) { nlamsgbuf_t *a; int r; while ((a = nla_getattr(g)) != NULL) { r = parser(a, arg); free(a); if (r < 0) { fido_log_debug("%s: parser", __func__); return (-1); } } return (0); } static int nl_parse_reply(const uint8_t *blob, size_t blob_len, uint16_t msg_type, uint8_t genl_cmd, void *arg, int (*parser)(nlamsgbuf_t *, void *)) { nlmsgbuf_t *m; int r; while (blob_len) { if ((m = nlmsg_from_buf(&blob, &blob_len)) == NULL) { fido_log_debug("%s: nlmsg", __func__); return (-1); } if (nlmsg_type(m) == NLMSG_ERROR) { r = nlmsg_get_status(m); free(m); return (r); } if (nlmsg_type(m) != msg_type || nlmsg_get_genl(m, genl_cmd) < 0) { fido_log_debug("%s: skipping", __func__); free(m); continue; } if (parser != NULL && nlmsg_iter(m, arg, parser) < 0) { fido_log_debug("%s: nlmsg_iter", __func__); free(m); return (-1); } free(m); } return (0); } static int parse_mcastgrp(nlamsgbuf_t *a, void *arg) { nl_family_t *family = arg; char *name; switch (nla_type(a)) { case CTRL_ATTR_MCAST_GRP_NAME: if ((name = nla_get_str(a)) == NULL || strcmp(name, NFC_GENL_MCAST_EVENT_NAME) != 0) { free(name); return (-1); /* XXX skip? */ } free(name); return (0); case CTRL_ATTR_MCAST_GRP_ID: if (family->mcastgrp) break; if (nla_get_u32(a, &family->mcastgrp) < 0) { fido_log_debug("%s: group", __func__); return (-1); } return (0); } fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a)); return (0); } static int parse_mcastgrps(nlamsgbuf_t *a, void *arg) { return (nla_iter(a, arg, parse_mcastgrp)); } static int parse_family(nlamsgbuf_t *a, void *arg) { nl_family_t *family = arg; switch (nla_type(a)) { case CTRL_ATTR_FAMILY_ID: if (family->id) break; if (nla_get_u16(a, &family->id) < 0) { fido_log_debug("%s: id", __func__); return (-1); } return (0); case CTRL_ATTR_MCAST_GROUPS: return (nla_iter(a, family, parse_mcastgrps)); } fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a)); return (0); } static int nl_get_nfc_family(int fd, uint16_t *type, uint32_t *mcastgrp) { nlmsgbuf_t *m; uint8_t reply[512]; nl_family_t family; ssize_t r; int ok; if ((m = nlmsg_new(GENL_ID_CTRL, 0, 64)) == NULL || nlmsg_set_genl(m, CTRL_CMD_GETFAMILY) < 0 || nlmsg_set_u16(m, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL) < 0 || nlmsg_set_str(m, CTRL_ATTR_FAMILY_NAME, NFC_GENL_NAME) < 0 || nlmsg_tx(fd, m) < 0) { free(m); return (-1); } free(m); memset(&family, 0, sizeof(family)); if ((r = nlmsg_rx(fd, reply, sizeof(reply), -1)) < 0) { fido_log_debug("%s: nlmsg_rx", __func__); return (-1); } if ((ok = nl_parse_reply(reply, (size_t)r, GENL_ID_CTRL, CTRL_CMD_NEWFAMILY, &family, parse_family)) != 0) { fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); return (-1); } if (family.id == 0 || family.mcastgrp == 0) { fido_log_debug("%s: missing attr", __func__); return (-1); } *type = family.id; *mcastgrp = family.mcastgrp; return (0); } static int parse_target(nlamsgbuf_t *a, void *arg) { nl_target_t *t = arg; if (t->found || nla_type(a) != NFC_ATTR_TARGET_INDEX) { fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a)); return (0); } if (nla_get_u32(a, t->value) < 0) { fido_log_debug("%s: target", __func__); return (-1); } t->found = 1; return (0); } int fido_nl_power_nfc(fido_nl_t *nl, uint32_t dev) { nlmsgbuf_t *m; uint8_t reply[512]; ssize_t r; int ok; if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL || nlmsg_set_genl(m, NFC_CMD_DEV_UP) < 0 || nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 || nlmsg_tx(nl->fd, m) < 0) { free(m); return (-1); } free(m); if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) { fido_log_debug("%s: nlmsg_rx", __func__); return (-1); } if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type, NFC_CMD_DEV_UP, NULL, NULL)) != 0 && ok != EALREADY) { fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); return (-1); } return (0); } static int nl_nfc_poll(fido_nl_t *nl, uint32_t dev) { nlmsgbuf_t *m; uint8_t reply[512]; ssize_t r; int ok; if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL || nlmsg_set_genl(m, NFC_CMD_START_POLL) < 0 || nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 || nlmsg_set_u32(m, NFC_ATTR_PROTOCOLS, NFC_PROTO_ISO14443_MASK) < 0 || nlmsg_tx(nl->fd, m) < 0) { free(m); return (-1); } free(m); if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) { fido_log_debug("%s: nlmsg_rx", __func__); return (-1); } if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type, NFC_CMD_START_POLL, NULL, NULL)) != 0) { fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); return (-1); } return (0); } static int nl_dump_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target, int ms) { nlmsgbuf_t *m; nl_target_t t; uint8_t reply[512]; ssize_t r; int ok; if ((m = nlmsg_new(nl->nfc_type, NLM_F_DUMP, 64)) == NULL || nlmsg_set_genl(m, NFC_CMD_GET_TARGET) < 0 || nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 || nlmsg_tx(nl->fd, m) < 0) { free(m); return (-1); } free(m); if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), ms)) < 0) { fido_log_debug("%s: nlmsg_rx", __func__); return (-1); } memset(&t, 0, sizeof(t)); t.value = target; if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type, NFC_CMD_GET_TARGET, &t, parse_target)) != 0) { fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); return (-1); } if (!t.found) { fido_log_debug("%s: target not found", __func__); return (-1); } return (0); } static int parse_nfc_event(nlamsgbuf_t *a, void *arg) { nl_poll_t *ctx = arg; uint32_t dev; if (nla_type(a) != NFC_ATTR_DEVICE_INDEX) { fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a)); return (0); } if (nla_get_u32(a, &dev) < 0) { fido_log_debug("%s: dev", __func__); return (-1); } if (dev == ctx->dev) ctx->eventcnt++; else fido_log_debug("%s: ignoring dev 0x%x", __func__, dev); return (0); } int fido_nl_get_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target) { uint8_t reply[512]; nl_poll_t ctx; ssize_t r; int ok; if (nl_nfc_poll(nl, dev) < 0) { fido_log_debug("%s: nl_nfc_poll", __func__); return (-1); } #ifndef FIDO_FUZZ if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) { fido_log_error(errno, "%s: setsockopt add", __func__); return (-1); } #endif r = nlmsg_rx(nl->fd, reply, sizeof(reply), NETLINK_POLL_MS); #ifndef FIDO_FUZZ if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) { fido_log_error(errno, "%s: setsockopt drop", __func__); return (-1); } #endif if (r < 0) { fido_log_debug("%s: nlmsg_rx", __func__); return (-1); } memset(&ctx, 0, sizeof(ctx)); ctx.dev = dev; if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type, NFC_EVENT_TARGETS_FOUND, &ctx, parse_nfc_event)) != 0) { fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); return (-1); } if (ctx.eventcnt == 0) { fido_log_debug("%s: dev 0x%x not observed", __func__, dev); return (-1); } if (nl_dump_nfc_target(nl, dev, target, -1) < 0) { fido_log_debug("%s: nl_dump_nfc_target", __func__); return (-1); } return (0); } void fido_nl_free(fido_nl_t **nlp) { fido_nl_t *nl; if (nlp == NULL || (nl = *nlp) == NULL) return; if (nl->fd != -1 && close(nl->fd) == -1) fido_log_error(errno, "%s: close", __func__); free(nl); *nlp = NULL; } fido_nl_t * fido_nl_new(void) { fido_nl_t *nl; int ok = -1; if ((nl = calloc(1, sizeof(*nl))) == NULL) return (NULL); if ((nl->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_GENERIC)) == -1) { fido_log_error(errno, "%s: socket", __func__); goto fail; } nl->saddr.nl_family = AF_NETLINK; if (bind(nl->fd, (struct sockaddr *)&nl->saddr, sizeof(nl->saddr)) == -1) { fido_log_error(errno, "%s: bind", __func__); goto fail; } if (nl_get_nfc_family(nl->fd, &nl->nfc_type, &nl->nfc_mcastgrp) < 0) { fido_log_debug("%s: nl_get_nfc_family", __func__); goto fail; } ok = 0; fail: if (ok < 0) fido_nl_free(&nl); return (nl); } #ifdef FIDO_FUZZ void set_netlink_io_functions(ssize_t (*read_f)(int, void *, size_t), ssize_t (*write_f)(int, const void *, size_t)) { fuzz_read = read_f; fuzz_write = write_f; } #endif libfido2-1.10.0/src/netlink.h000066400000000000000000000017361417126203300156750ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #ifndef _FIDO_NETLINK_H #define _FIDO_NETLINK_H #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct fido_nl { int fd; uint16_t nfc_type; uint32_t nfc_mcastgrp; struct sockaddr_nl saddr; } fido_nl_t; fido_nl_t *fido_nl_new(void); void fido_nl_free(struct fido_nl **); int fido_nl_power_nfc(struct fido_nl *, uint32_t); int fido_nl_get_nfc_target(struct fido_nl *, uint32_t , uint32_t *); #ifdef FIDO_FUZZ void set_netlink_io_functions(ssize_t (*)(int, void *, size_t), ssize_t (*)(int, const void *, size_t)); #endif #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_NETLINK_H */ libfido2-1.10.0/src/nfc_linux.c000066400000000000000000000331121417126203300162020ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #include #include #include #include #include "fido.h" #include "fido/param.h" #include "netlink.h" #include "iso7816.h" #define TX_CHUNK_SIZE 240 static const uint8_t aid[] = { 0xa0, 0x00, 0x00, 0x06, 0x47, 0x2f, 0x00, 0x01 }; static const uint8_t v_u2f[] = { 'U', '2', 'F', '_', 'V', '2' }; static const uint8_t v_fido[] = { 'F', 'I', 'D', 'O', '_', '2', '_', '0' }; struct nfc_linux { int fd; uint32_t dev; uint32_t target; sigset_t sigmask; const sigset_t *sigmaskp; struct fido_nl *nl; }; static int tx_short_apdu(fido_dev_t *d, const iso7816_header_t *h, const uint8_t *payload, uint8_t payload_len, uint8_t cla_flags) { uint8_t apdu[5 + UINT8_MAX + 1]; uint8_t sw[2]; size_t apdu_len; int ok = -1; memset(&apdu, 0, sizeof(apdu)); apdu[0] = h->cla | cla_flags; apdu[1] = h->ins; apdu[2] = h->p1; apdu[3] = h->p2; apdu[4] = payload_len; memcpy(&apdu[5], payload, payload_len); apdu_len = (size_t)(5 + payload_len + 1); if (d->io.write(d->io_handle, apdu, apdu_len) < 0) { fido_log_debug("%s: write", __func__); goto fail; } if (cla_flags & 0x10) { if (d->io.read(d->io_handle, sw, sizeof(sw), -1) != 2) { fido_log_debug("%s: read", __func__); goto fail; } if ((sw[0] << 8 | sw[1]) != SW_NO_ERROR) { fido_log_debug("%s: unexpected sw", __func__); goto fail; } } ok = 0; fail: explicit_bzero(apdu, sizeof(apdu)); return (ok); } static int nfc_do_tx(fido_dev_t *d, const uint8_t *apdu_ptr, size_t apdu_len) { iso7816_header_t h; if (fido_buf_read(&apdu_ptr, &apdu_len, &h, sizeof(h)) < 0) { fido_log_debug("%s: header", __func__); return (-1); } if (apdu_len < 2) { fido_log_debug("%s: apdu_len %zu", __func__, apdu_len); return (-1); } apdu_len -= 2; /* trim le1 le2 */ while (apdu_len > TX_CHUNK_SIZE) { if (tx_short_apdu(d, &h, apdu_ptr, TX_CHUNK_SIZE, 0x10) < 0) { fido_log_debug("%s: chain", __func__); return (-1); } apdu_ptr += TX_CHUNK_SIZE; apdu_len -= TX_CHUNK_SIZE; } if (tx_short_apdu(d, &h, apdu_ptr, (uint8_t)apdu_len, 0) < 0) { fido_log_debug("%s: tx_short_apdu", __func__); return (-1); } return (0); } int fido_nfc_tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count) { iso7816_apdu_t *apdu = NULL; const uint8_t *ptr; size_t len; int ok = -1; switch (cmd) { case CTAP_CMD_INIT: /* select */ if ((apdu = iso7816_new(0, 0xa4, 0x04, sizeof(aid))) == NULL || iso7816_add(apdu, aid, sizeof(aid)) < 0) { fido_log_debug("%s: iso7816", __func__); goto fail; } break; case CTAP_CMD_CBOR: /* wrap cbor */ if (count > UINT16_MAX || (apdu = iso7816_new(0x80, 0x10, 0x80, (uint16_t)count)) == NULL || iso7816_add(apdu, buf, count) < 0) { fido_log_debug("%s: iso7816", __func__); goto fail; } break; case CTAP_CMD_MSG: /* already an apdu */ break; default: fido_log_debug("%s: cmd=%02x", __func__, cmd); goto fail; } if (apdu != NULL) { ptr = iso7816_ptr(apdu); len = iso7816_len(apdu); } else { ptr = buf; len = count; } if (nfc_do_tx(d, ptr, len) < 0) { fido_log_debug("%s: nfc_do_tx", __func__); goto fail; } ok = 0; fail: iso7816_free(&apdu); return (ok); } static int rx_init(fido_dev_t *d, unsigned char *buf, size_t count, int ms) { fido_ctap_info_t *attr = (fido_ctap_info_t *)buf; uint8_t f[64]; int n; if (count != sizeof(*attr)) { fido_log_debug("%s: count=%zu", __func__, count); return (-1); } memset(attr, 0, sizeof(*attr)); if ((n = d->io.read(d->io_handle, f, sizeof(f), ms)) < 2 || (f[n - 2] << 8 | f[n - 1]) != SW_NO_ERROR) { fido_log_debug("%s: read", __func__); return (-1); } n -= 2; if (n == sizeof(v_u2f) && memcmp(f, v_u2f, sizeof(v_u2f)) == 0) attr->flags = FIDO_CAP_CBOR; else if (n == sizeof(v_fido) && memcmp(f, v_fido, sizeof(v_fido)) == 0) attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG; else { fido_log_debug("%s: unknown version string", __func__); #ifdef FIDO_FUZZ attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG; #else return (-1); #endif } memcpy(&attr->nonce, &d->nonce, sizeof(attr->nonce)); /* XXX */ return ((int)count); } static int tx_get_response(fido_dev_t *d, uint8_t count) { uint8_t apdu[5]; memset(apdu, 0, sizeof(apdu)); apdu[1] = 0xc0; /* GET_RESPONSE */ apdu[4] = count; if (d->io.write(d->io_handle, apdu, sizeof(apdu)) < 0) { fido_log_debug("%s: write", __func__); return (-1); } return (0); } static int rx_apdu(fido_dev_t *d, uint8_t sw[2], unsigned char **buf, size_t *count, int *ms) { uint8_t f[256 + 2]; struct timespec ts; int n, ok = -1; if (fido_time_now(&ts) != 0) goto fail; if ((n = d->io.read(d->io_handle, f, sizeof(f), *ms)) < 2) { fido_log_debug("%s: read", __func__); goto fail; } if (fido_time_delta(&ts, ms) != 0) goto fail; if (fido_buf_write(buf, count, f, (size_t)(n - 2)) < 0) { fido_log_debug("%s: fido_buf_write", __func__); goto fail; } memcpy(sw, f + n - 2, 2); ok = 0; fail: explicit_bzero(f, sizeof(f)); return (ok); } static int rx_msg(fido_dev_t *d, unsigned char *buf, size_t count, int ms) { uint8_t sw[2]; const size_t bufsiz = count; if (rx_apdu(d, sw, &buf, &count, &ms) < 0) { fido_log_debug("%s: preamble", __func__); return (-1); } while (sw[0] == SW1_MORE_DATA) if (tx_get_response(d, sw[1]) < 0 || rx_apdu(d, sw, &buf, &count, &ms) < 0) { fido_log_debug("%s: chain", __func__); return (-1); } if (fido_buf_write(&buf, &count, sw, sizeof(sw)) < 0) { fido_log_debug("%s: sw", __func__); return (-1); } if (bufsiz - count > INT_MAX) { fido_log_debug("%s: bufsiz", __func__); return (-1); } return ((int)(bufsiz - count)); } static int rx_cbor(fido_dev_t *d, unsigned char *buf, size_t count, int ms) { int r; if ((r = rx_msg(d, buf, count, ms)) < 2) return (-1); return (r - 2); } int fido_nfc_rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms) { switch (cmd) { case CTAP_CMD_INIT: return (rx_init(d, buf, count, ms)); case CTAP_CMD_CBOR: return (rx_cbor(d, buf, count, ms)); case CTAP_CMD_MSG: return (rx_msg(d, buf, count, ms)); default: fido_log_debug("%s: cmd=%02x", __func__, cmd); return (-1); } } static char * get_parent_attr(struct udev_device *dev, const char *subsystem, const char *devtype, const char *attr) { struct udev_device *parent; const char *value; if ((parent = udev_device_get_parent_with_subsystem_devtype(dev, subsystem, devtype)) == NULL || (value = udev_device_get_sysattr_value(parent, attr)) == NULL) return (NULL); return (strdup(value)); } static char * get_usb_attr(struct udev_device *dev, const char *attr) { return (get_parent_attr(dev, "usb", "usb_device", attr)); } static int to_int(const char *str, int base) { char *ep; long long ll; ll = strtoll(str, &ep, base); 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); else if (ll < 0 || ll > INT_MAX) return (-1); return ((int)ll); } static int copy_info(fido_dev_info_t *di, struct udev *udev, struct udev_list_entry *udev_entry) { const char *name; char *str; struct udev_device *dev = NULL; void *ctx = NULL; int id, 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) goto fail; if (asprintf(&di->path, "%s/%s", FIDO_NFC_PREFIX, name) == -1) goto fail; if ((di->manufacturer = get_usb_attr(dev, "manufacturer")) == NULL) di->manufacturer = strdup(""); if ((di->product = get_usb_attr(dev, "product")) == NULL) di->product = strdup(""); if (di->manufacturer == NULL || di->product == NULL) goto fail; /* XXX assumes USB for vendor/product info */ if ((str = get_usb_attr(dev, "idVendor")) != NULL && (id = to_int(str, 16)) > 0 && id <= UINT16_MAX) di->vendor_id = (int16_t)id; free(str); if ((str = get_usb_attr(dev, "idProduct")) != NULL && (id = to_int(str, 16)) > 0 && id <= UINT16_MAX) di->product_id = (int16_t)id; free(str); if ((ctx = fido_nfc_open(di->path)) == NULL) { fido_log_debug("%s: fido_nfc_open", __func__); goto fail; } ok = 0; fail: if (dev != NULL) udev_device_unref(dev); if (ctx != NULL) fido_nfc_close(ctx); if (ok < 0) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); } return (ok); } static int sysnum_from_syspath(const char *path) { struct udev *udev = NULL; struct udev_device *dev = NULL; const char *str; int idx; if ((udev = udev_new()) == NULL || (dev = udev_device_new_from_syspath(udev, path)) == NULL || (str = udev_device_get_sysnum(dev)) == NULL) idx = -1; else idx = to_int(str, 10); if (dev != NULL) udev_device_unref(dev); if (udev != NULL) udev_unref(udev); return (idx); } int fido_nfc_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); 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, "nfc") < 0 || udev_enumerate_scan_devices(udev_enum) < 0) goto fail; if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) { r = FIDO_OK; /* zero nfc devices */ goto fail; } udev_list_entry_foreach(udev_entry, udev_list) { if (copy_info(&devlist[*olen], udev, udev_entry) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_nfc_open, fido_nfc_close, fido_nfc_read, fido_nfc_write, }; devlist[*olen].transport = (fido_dev_transport_t) { fido_nfc_rx, fido_nfc_tx, }; 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); } static int nfc_target_connect(struct nfc_linux *ctx) { struct sockaddr_nfc sa; memset(&sa, 0, sizeof(sa)); sa.sa_family = AF_NFC; sa.dev_idx = ctx->dev; sa.target_idx = ctx->target; sa.nfc_protocol = NFC_PROTO_ISO14443; if ((ctx->fd = socket(AF_NFC, SOCK_SEQPACKET | SOCK_CLOEXEC, NFC_SOCKPROTO_RAW)) == -1) { fido_log_error(errno, "%s: socket", __func__); return (-1); } if (connect(ctx->fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { fido_log_error(errno, "%s: connect", __func__); if (close(ctx->fd) == -1) fido_log_error(errno, "%s: close", __func__); ctx->fd = -1; return (-1); } return (0); } static void nfc_free(struct nfc_linux **ctx_p) { struct nfc_linux *ctx; if (ctx_p == NULL || (ctx = *ctx_p) == NULL) return; if (ctx->fd != -1 && close(ctx->fd) == -1) fido_log_error(errno, "%s: close", __func__); if (ctx->nl != NULL) fido_nl_free(&ctx->nl); free(ctx); *ctx_p = NULL; } static struct nfc_linux * nfc_new(uint32_t dev) { struct nfc_linux *ctx; if ((ctx = calloc(1, sizeof(*ctx))) == NULL || (ctx->nl = fido_nl_new()) == NULL) { nfc_free(&ctx); return (NULL); } ctx->fd = -1; ctx->dev = dev; return (ctx); } void * fido_nfc_open(const char *path) { struct nfc_linux *ctx = NULL; int idx; if (strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) != 0) { fido_log_debug("%s: bad prefix", __func__); goto fail; } if ((idx = sysnum_from_syspath(path + strlen(FIDO_NFC_PREFIX))) < 0 || (ctx = nfc_new((uint32_t)idx)) == NULL) { fido_log_debug("%s: nfc_new", __func__); goto fail; } if (fido_nl_power_nfc(ctx->nl, ctx->dev) < 0 || fido_nl_get_nfc_target(ctx->nl, ctx->dev, &ctx->target) < 0 || nfc_target_connect(ctx) < 0) { fido_log_debug("%s: netlink", __func__); goto fail; } return (ctx); fail: nfc_free(&ctx); return (NULL); } void fido_nfc_close(void *handle) { struct nfc_linux *ctx = handle; nfc_free(&ctx); } int fido_nfc_set_sigmask(void *handle, const fido_sigset_t *sigmask) { struct nfc_linux *ctx = handle; ctx->sigmask = *sigmask; ctx->sigmaskp = &ctx->sigmask; return (FIDO_OK); } int fido_nfc_read(void *handle, unsigned char *buf, size_t len, int ms) { struct nfc_linux *ctx = handle; struct iovec iov[2]; uint8_t preamble; ssize_t r; memset(&iov, 0, sizeof(iov)); iov[0].iov_base = &preamble; iov[0].iov_len = sizeof(preamble); iov[1].iov_base = buf; iov[1].iov_len = len; if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) { fido_log_debug("%s: fido_hid_unix_wait", __func__); return (-1); } if ((r = readv(ctx->fd, iov, nitems(iov))) == -1) { fido_log_error(errno, "%s: read", __func__); return (-1); } if (r < 1) { fido_log_debug("%s: %zd < 1", __func__, r); return (-1); } if (preamble != 0x00) { fido_log_debug("%s: preamble", __func__); return (-1); } r--; fido_log_xxd(buf, (size_t)r, "%s", __func__); return ((int)r); } int fido_nfc_write(void *handle, const unsigned char *buf, size_t len) { struct nfc_linux *ctx = handle; ssize_t r; fido_log_xxd(buf, len, "%s", __func__); if (len > INT_MAX) { fido_log_debug("%s: len", __func__); return (-1); } if ((r = write(ctx->fd, buf, len)) == -1) { fido_log_error(errno, "%s: write", __func__); return (-1); } if (r < 0 || (size_t)r != len) { fido_log_debug("%s: %zd != %zu", __func__, r, len); return (-1); } return ((int)r); } libfido2-1.10.0/src/packed.h000066400000000000000000000010341417126203300154470ustar00rootroot00000000000000/* * 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 */ libfido2-1.10.0/src/pin.c000066400000000000000000000400441417126203300150050ustar00rootroot00000000000000/* * 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" #define CTAP21_UV_TOKEN_PERM_MAKECRED 0x01 #define CTAP21_UV_TOKEN_PERM_ASSERT 0x02 #define CTAP21_UV_TOKEN_PERM_CRED_MGMT 0x04 #define CTAP21_UV_TOKEN_PERM_BIO 0x08 #define CTAP21_UV_TOKEN_PERM_LARGEBLOB 0x10 #define CTAP21_UV_TOKEN_PERM_CONFIG 0x20 int fido_sha256(fido_blob_t *digest, const u_char *data, size_t data_len) { 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) { fido_blob_reset(digest); return (-1); } return (0); } static int pin_sha256_enc(const fido_dev_t *dev, const fido_blob_t *shared, const fido_blob_t *pin, fido_blob_t **out) { fido_blob_t *ph = NULL; int r; if ((*out = fido_blob_new()) == NULL || (ph = fido_blob_new()) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if (fido_sha256(ph, pin->ptr, pin->len) < 0 || ph->len < 16) { fido_log_debug("%s: SHA256", __func__); r = FIDO_ERR_INTERNAL; goto fail; } ph->len = 16; /* first 16 bytes */ if (aes256_cbc_enc(dev, shared, ph, *out) < 0) { fido_log_debug("%s: aes256_cbc_enc", __func__); r = FIDO_ERR_INTERNAL; goto fail; } r = FIDO_OK; fail: fido_blob_free(&ph); return (r); } 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 + 63U) & ~63U; 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 pin_pad64_enc(const fido_dev_t *dev, const fido_blob_t *shared, const char *pin, fido_blob_t **out) { fido_blob_t *ppin = NULL; int r; if ((r = pad64(pin, &ppin)) != FIDO_OK) { fido_log_debug("%s: pad64", __func__); goto fail; } if ((*out = fido_blob_new()) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if (aes256_cbc_enc(dev, shared, ppin, *out) < 0) { fido_log_debug("%s: aes256_cbc_enc", __func__); r = FIDO_ERR_INTERNAL; goto fail; } r = FIDO_OK; fail: fido_blob_free(&ppin); return (r); } static cbor_item_t * encode_uv_permission(uint8_t cmd) { switch (cmd) { case CTAP_CBOR_ASSERT: return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_ASSERT)); case CTAP_CBOR_BIO_ENROLL_PRE: return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_BIO)); case CTAP_CBOR_CONFIG: return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CONFIG)); case CTAP_CBOR_MAKECRED: return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_MAKECRED)); case CTAP_CBOR_CRED_MGMT_PRE: return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CRED_MGMT)); case CTAP_CBOR_LARGEBLOB: return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_LARGEBLOB)); default: fido_log_debug("%s: cmd 0x%02x", __func__, cmd); return (NULL); } } static int ctap20_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh, const es256_pk_t *pk, int *ms) { fido_blob_t f; fido_blob_t *p = NULL; fido_blob_t *phe = NULL; cbor_item_t *argv[6]; int r; memset(&f, 0, sizeof(f)); memset(argv, 0, sizeof(argv)); if (pin == NULL) { fido_log_debug("%s: NULL pin", __func__); r = FIDO_ERR_PIN_REQUIRED; goto fail; } 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 ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) { fido_log_debug("%s: pin_sha256_enc", __func__); goto fail; } if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL || (argv[1] = cbor_build_uint8(5)) == NULL || (argv[2] = es256_pk_encode(pk, 1)) == NULL || (argv[5] = fido_blob_encode(phe)) == NULL) { fido_log_debug("%s: cbor encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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); fido_blob_free(&phe); free(f.ptr); return (r); } static int ctap21_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh, const es256_pk_t *pk, uint8_t cmd, const char *rpid, int *ms) { fido_blob_t f; fido_blob_t *p = NULL; fido_blob_t *phe = NULL; cbor_item_t *argv[10]; uint8_t subcmd; int r; memset(&f, 0, sizeof(f)); memset(argv, 0, sizeof(argv)); if (pin != NULL) { 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 ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) { fido_log_debug("%s: pin_sha256_enc", __func__); goto fail; } subcmd = 9; /* getPinUvAuthTokenUsingPinWithPermissions */ } else { if (fido_dev_has_uv(dev) == false) { fido_log_debug("%s: fido_dev_has_uv", __func__); r = FIDO_ERR_PIN_REQUIRED; goto fail; } subcmd = 6; /* getPinUvAuthTokenUsingUvWithPermissions */ } if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL || (argv[1] = cbor_build_uint8(subcmd)) == NULL || (argv[2] = es256_pk_encode(pk, 1)) == NULL || (phe != NULL && (argv[5] = fido_blob_encode(phe)) == NULL) || (argv[8] = encode_uv_permission(cmd)) == NULL || (rpid != NULL && (argv[9] = cbor_build_string(rpid)) == NULL)) { fido_log_debug("%s: cbor encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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); fido_blob_free(&phe); free(f.ptr); return (r); } static int parse_uv_token(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 uv_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, fido_blob_t *token, int *ms) { fido_blob_t *aes_token = NULL; unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; if ((aes_token = fido_blob_new()) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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_uv_token)) != FIDO_OK) { fido_log_debug("%s: parse_uv_token", __func__); goto fail; } if (aes256_cbc_dec(dev, 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 uv_token_wait(fido_dev_t *dev, uint8_t cmd, const char *pin, const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid, fido_blob_t *token, int *ms) { int r; if (ecdh == NULL || pk == NULL) return (FIDO_ERR_INVALID_ARGUMENT); if (fido_dev_supports_permissions(dev)) r = ctap21_uv_token_tx(dev, pin, ecdh, pk, cmd, rpid, ms); else r = ctap20_uv_token_tx(dev, pin, ecdh, pk, ms); if (r != FIDO_OK) return (r); return (uv_token_rx(dev, ecdh, token, ms)); } int fido_dev_get_uv_token(fido_dev_t *dev, uint8_t cmd, const char *pin, const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid, fido_blob_t *token, int *ms) { return (uv_token_wait(dev, cmd, pin, ecdh, pk, rpid, token, ms)); } static int fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin, int *ms) { fido_blob_t f; fido_blob_t *ppine = NULL; fido_blob_t *ecdh = NULL; fido_blob_t *opin = NULL; fido_blob_t *opinhe = 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 = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } /* pad and encrypt new pin */ if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) { fido_log_debug("%s: pin_pad64_enc", __func__); goto fail; } /* hash and encrypt old pin */ if ((r = pin_sha256_enc(dev, ecdh, opin, &opinhe)) != FIDO_OK) { fido_log_debug("%s: pin_sha256_enc", __func__); goto fail; } if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL || (argv[1] = cbor_build_uint8(4)) == NULL || (argv[2] = es256_pk_encode(pk, 1)) == NULL || (argv[3] = cbor_encode_change_pin_auth(dev, ecdh, ppine, opinhe)) == NULL || (argv[4] = fido_blob_encode(ppine)) == NULL || (argv[5] = fido_blob_encode(opinhe)) == NULL) { fido_log_debug("%s: cbor encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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(&ppine); fido_blob_free(&ecdh); fido_blob_free(&opin); fido_blob_free(&opinhe); free(f.ptr); return (r); } static int fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin, int *ms) { fido_blob_t f; fido_blob_t *ppine = 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 = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { fido_log_debug("%s: fido_do_ecdh", __func__); goto fail; } if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) { fido_log_debug("%s: pin_pad64_enc", __func__); goto fail; } if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL || (argv[1] = cbor_build_uint8(3)) == NULL || (argv[2] = es256_pk_encode(pk, 1)) == NULL || (argv[3] = cbor_encode_pin_auth(dev, ecdh, ppine)) == NULL || (argv[4] = fido_blob_encode(ppine)) == NULL) { fido_log_debug("%s: cbor encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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(&ppine); 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, ms)) != FIDO_OK) { fido_log_debug("%s: fido_dev_change_pin_tx", __func__); return (r); } } else { if ((r = fido_dev_set_pin_tx(dev, pin, ms)) != 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); } if (dev->flags & FIDO_DEV_PIN_UNSET) { dev->flags &= ~FIDO_DEV_PIN_UNSET; dev->flags |= FIDO_DEV_PIN_SET; } return (FIDO_OK); } int fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin) { int ms = dev->timeout_ms; return (fido_dev_set_pin_wait(dev, pin, oldpin, &ms)); } static int parse_retry_count(const uint8_t keyval, 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) != keyval) { 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 parse_pin_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg) { return (parse_retry_count(3, key, val, arg)); } static int parse_uv_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg) { return (parse_retry_count(5, key, val, arg)); } static int fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd, int *ms) { 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(subcmd)) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 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_pin_retry_count_rx(fido_dev_t *dev, int *retries, int *ms) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; *retries = 0; if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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_pin_retry_count)) != FIDO_OK) { fido_log_debug("%s: parse_pin_retry_count", __func__); return (r); } return (FIDO_OK); } static int fido_dev_get_pin_retry_count_wait(fido_dev_t *dev, int *retries, int *ms) { int r; if ((r = fido_dev_get_retry_count_tx(dev, 1, ms)) != FIDO_OK || (r = fido_dev_get_pin_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) { int ms = dev->timeout_ms; return (fido_dev_get_pin_retry_count_wait(dev, retries, &ms)); } static int fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int *ms) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; *retries = 0; if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &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_uv_retry_count)) != FIDO_OK) { fido_log_debug("%s: parse_uv_retry_count", __func__); return (r); } return (FIDO_OK); } static int fido_dev_get_uv_retry_count_wait(fido_dev_t *dev, int *retries, int *ms) { int r; if ((r = fido_dev_get_retry_count_tx(dev, 7, ms)) != FIDO_OK || (r = fido_dev_get_uv_retry_count_rx(dev, retries, ms)) != FIDO_OK) return (r); return (FIDO_OK); } int fido_dev_get_uv_retry_count(fido_dev_t *dev, int *retries) { int ms = dev->timeout_ms; return (fido_dev_get_uv_retry_count_wait(dev, retries, &ms)); } int cbor_add_uv_params(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *hmac_data, const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, const char *rpid, cbor_item_t **auth, cbor_item_t **opt, int *ms) { fido_blob_t *token = NULL; int r; if ((token = fido_blob_new()) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if ((r = fido_dev_get_uv_token(dev, cmd, pin, ecdh, pk, rpid, token, ms)) != FIDO_OK) { fido_log_debug("%s: fido_dev_get_uv_token", __func__); goto fail; } if ((*auth = cbor_encode_pin_auth(dev, token, hmac_data)) == NULL || (*opt = cbor_encode_pin_opt(dev)) == NULL) { fido_log_debug("%s: cbor encode", __func__); r = FIDO_ERR_INTERNAL; goto fail; } r = FIDO_OK; fail: fido_blob_free(&token); return (r); } libfido2-1.10.0/src/random.c000066400000000000000000000026201417126203300154750ustar00rootroot00000000000000/* * 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 #ifdef HAVE_SYS_RANDOM_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #include "fido.h" #if defined(_WIN32) #include #include #include #include #include #include int fido_get_random(void *buf, size_t len) { NTSTATUS status; status = BCryptGenRandom(NULL, buf, (ULONG)len, BCRYPT_USE_SYSTEM_PREFERRED_RNG); if (!NT_SUCCESS(status)) return (-1); return (0); } #elif defined(HAVE_ARC4RANDOM_BUF) int fido_get_random(void *buf, size_t len) { arc4random_buf(buf, len); return (0); } #elif defined(HAVE_GETRANDOM) int fido_get_random(void *buf, size_t len) { ssize_t r; if ((r = getrandom(buf, len, 0)) < 0 || (size_t)r != len) return (-1); return (0); } #elif defined(HAVE_DEV_URANDOM) int fido_get_random(void *buf, size_t len) { int fd = -1; int ok = -1; ssize_t r; if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0) goto fail; if ((r = read(fd, buf, len)) < 0 || (size_t)r != len) goto fail; ok = 0; fail: if (fd != -1) close(fd); return (ok); } #else #error "please provide an implementation of fido_get_random() for your platform" #endif /* _WIN32 */ libfido2-1.10.0/src/reset.c000066400000000000000000000015621417126203300153430ustar00rootroot00000000000000/* * Copyright (c) 2018 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" static int fido_dev_reset_tx(fido_dev_t *dev, int *ms) { const unsigned char cbor[] = { CTAP_CBOR_RESET }; if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 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, ms)) != FIDO_OK || (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) return (r); if (dev->flags & FIDO_DEV_PIN_SET) { dev->flags &= ~FIDO_DEV_PIN_SET; dev->flags |= FIDO_DEV_PIN_UNSET; } return (FIDO_OK); } int fido_dev_reset(fido_dev_t *dev) { int ms = dev->timeout_ms; return (fido_dev_reset_wait(dev, &ms)); } libfido2-1.10.0/src/rs1.c000066400000000000000000000035371417126203300147320ustar00rootroot00000000000000/* * Copyright (c) 2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include "fido.h" #if defined(LIBRESSL_VERSION_NUMBER) static EVP_MD * rs1_get_EVP_MD(void) { const EVP_MD *from; EVP_MD *to = NULL; if ((from = EVP_sha1()) != NULL && (to = malloc(sizeof(*to))) != NULL) memcpy(to, from, sizeof(*to)); return (to); } static void rs1_free_EVP_MD(EVP_MD *md) { freezero(md, sizeof(*md)); } #elif OPENSSL_VERSION_NUMBER >= 0x30000000 static EVP_MD * rs1_get_EVP_MD(void) { return (EVP_MD_fetch(NULL, "SHA-1", NULL)); } static void rs1_free_EVP_MD(EVP_MD *md) { EVP_MD_free(md); } #else static EVP_MD * rs1_get_EVP_MD(void) { const EVP_MD *md; if ((md = EVP_sha1()) == NULL) return (NULL); return (EVP_MD_meth_dup(md)); } static void rs1_free_EVP_MD(EVP_MD *md) { EVP_MD_meth_free(md); } #endif /* LIBRESSL_VERSION_NUMBER */ int rs1_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey, const fido_blob_t *sig) { EVP_PKEY_CTX *pctx = NULL; EVP_MD *md = NULL; int ok = -1; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { fido_log_debug("%s: EVP_PKEY_base_id", __func__); goto fail; } if ((md = rs1_get_EVP_MD()) == NULL) { fido_log_debug("%s: rs1_get_EVP_MD", __func__); goto fail; } if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL || EVP_PKEY_verify_init(pctx) != 1 || EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1 || EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) { fido_log_debug("%s: EVP_PKEY_CTX", __func__); goto fail; } if (EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr, dgst->len) != 1) { fido_log_debug("%s: EVP_PKEY_verify", __func__); goto fail; } ok = 0; fail: EVP_PKEY_CTX_free(pctx); rs1_free_EVP_MD(md); return (ok); } libfido2-1.10.0/src/rs256.c000066400000000000000000000134251417126203300151030ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include "fido.h" #include "fido/rs256.h" #if defined(LIBRESSL_VERSION_NUMBER) static EVP_MD * rs256_get_EVP_MD(void) { const EVP_MD *from; EVP_MD *to = NULL; if ((from = EVP_sha256()) != NULL && (to = malloc(sizeof(*to))) != NULL) memcpy(to, from, sizeof(*to)); return (to); } static void rs256_free_EVP_MD(EVP_MD *md) { freezero(md, sizeof(*md)); } #elif OPENSSL_VERSION_NUMBER >= 0x30000000 static EVP_MD * rs256_get_EVP_MD(void) { return (EVP_MD_fetch(NULL, "SHA2-256", NULL)); } static void rs256_free_EVP_MD(EVP_MD *md) { EVP_MD_free(md); } #else static EVP_MD * rs256_get_EVP_MD(void) { const EVP_MD *md; if ((md = EVP_sha256()) == NULL) return (NULL); return (EVP_MD_meth_dup(md)); } static void rs256_free_EVP_MD(EVP_MD *md) { EVP_MD_meth_free(md); } #endif /* LIBRESSL_VERSION_NUMBER */ 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; freezero(pk, sizeof(*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); } int rs256_pk_from_EVP_PKEY(rs256_pk_t *pk, const EVP_PKEY *pkey) { RSA *rsa; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA || (rsa = EVP_PKEY_get0(pkey)) == NULL) return (FIDO_ERR_INVALID_ARGUMENT); return (rs256_pk_from_RSA(pk, rsa)); } int rs256_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey, const fido_blob_t *sig) { EVP_PKEY_CTX *pctx = NULL; EVP_MD *md = NULL; int ok = -1; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { fido_log_debug("%s: EVP_PKEY_base_id", __func__); goto fail; } if ((md = rs256_get_EVP_MD()) == NULL) { fido_log_debug("%s: rs256_get_EVP_MD", __func__); goto fail; } if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL || EVP_PKEY_verify_init(pctx) != 1 || EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1 || EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) { fido_log_debug("%s: EVP_PKEY_CTX", __func__); goto fail; } if (EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr, dgst->len) != 1) { fido_log_debug("%s: EVP_PKEY_verify", __func__); goto fail; } ok = 0; fail: EVP_PKEY_CTX_free(pctx); rs256_free_EVP_MD(md); return (ok); } int rs256_pk_verify_sig(const fido_blob_t *dgst, const rs256_pk_t *pk, const fido_blob_t *sig) { EVP_PKEY *pkey; int ok = -1; if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL || rs256_verify_sig(dgst, pkey, sig) < 0) { fido_log_debug("%s: rs256_verify_sig", __func__); goto fail; } ok = 0; fail: EVP_PKEY_free(pkey); return (ok); } libfido2-1.10.0/src/time.c000066400000000000000000000025251417126203300151570ustar00rootroot00000000000000/* * Copyright (c) 2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include "fido.h" static int timespec_to_ms(const struct timespec *ts) { int64_t x, y; if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000LL) return -1; if ((uint64_t)ts->tv_sec >= INT64_MAX / 1000LL) return -1; x = ts->tv_sec * 1000LL; y = ts->tv_nsec / 1000000LL; if (INT64_MAX - x < y || x + y > INT_MAX) return -1; return (int)(x + y); } int fido_time_now(struct timespec *ts_now) { if (clock_gettime(CLOCK_MONOTONIC, ts_now) != 0) { fido_log_error(errno, "%s: clock_gettime", __func__); return -1; } return 0; } int fido_time_delta(const struct timespec *ts_start, int *ms_remain) { struct timespec ts_end, ts_delta; int ms; if (*ms_remain < 0) return 0; if (clock_gettime(CLOCK_MONOTONIC, &ts_end) != 0) { fido_log_error(errno, "%s: clock_gettime", __func__); return -1; } if (timespeccmp(&ts_end, ts_start, <)) { fido_log_debug("%s: timespeccmp", __func__); return -1; } timespecsub(&ts_end, ts_start, &ts_delta); if ((ms = timespec_to_ms(&ts_delta)) < 0) { fido_log_debug("%s: timespec_to_ms", __func__); return -1; } if (ms > *ms_remain) ms = *ms_remain; *ms_remain -= ms; return 0; } libfido2-1.10.0/src/tpm.c000066400000000000000000000260011417126203300150140ustar00rootroot00000000000000/* * Copyright (c) 2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ /* * Trusted Platform Module (TPM) 2.0 attestation support. Documentation * references are relative to revision 01.38 of the TPM 2.0 specification. */ #include #include "packed.h" #include "fido.h" /* Part 1, 4.89: TPM_GENERATED_VALUE */ #define TPM_MAGIC 0xff544347 /* Part 2, 6.3: TPM_ALG_ID */ #define TPM_ALG_RSA 0x0001 #define TPM_ALG_SHA256 0x000b #define TPM_ALG_NULL 0x0010 #define TPM_ALG_ECC 0x0023 /* Part 2, 6.4: TPM_ECC_CURVE */ #define TPM_ECC_P256 0x0003 /* Part 2, 6.9: TPM_ST_ATTEST_CERTIFY */ #define TPM_ST_CERTIFY 0x8017 /* Part 2, 8.3: TPMA_OBJECT */ #define TPMA_RESERVED 0xfff8f309 /* reserved bits; must be zero */ #define TPMA_FIXED 0x00000002 /* object has fixed hierarchy */ #define TPMA_CLEAR 0x00000004 /* object persists */ #define TPMA_FIXED_P 0x00000010 /* object has fixed parent */ #define TPMA_SENSITIVE 0x00000020 /* data originates within tpm */ #define TPMA_SIGN 0x00040000 /* object may sign */ /* Part 2, 10.4.2: TPM2B_DIGEST */ PACKED_TYPE(tpm_sha256_digest_t, struct tpm_sha256_digest { uint16_t size; /* sizeof(body) */ uint8_t body[32]; }) /* Part 2, 10.4.3: TPM2B_DATA */ PACKED_TYPE(tpm_sha1_data_t, struct tpm_sha1_data { uint16_t size; /* sizeof(body */ uint8_t body[20]; }) /* Part 2, 10.5.3: TPM2B_NAME */ PACKED_TYPE(tpm_sha256_name_t, struct tpm_sha256_name { uint16_t size; /* sizeof(alg) + sizeof(body) */ uint16_t alg; /* TPM_ALG_SHA256 */ uint8_t body[32]; }) /* Part 2, 10.11.1: TPMS_CLOCK_INFO */ PACKED_TYPE(tpm_clock_info_t, struct tpm_clock_info { uint64_t timestamp_ms; uint32_t reset_count; /* obfuscated by tpm */ uint32_t restart_count; /* obfuscated by tpm */ uint8_t safe; /* 1 if timestamp_ms is current */ }) /* Part 2, 10.12.8 TPMS_ATTEST */ PACKED_TYPE(tpm_sha1_attest_t, struct tpm_sha1_attest { uint32_t magic; /* TPM_MAGIC */ uint16_t type; /* TPM_ST_ATTEST_CERTIFY */ tpm_sha256_name_t signer; /* full tpm path of signing key */ tpm_sha1_data_t data; /* signed sha1 */ tpm_clock_info_t clock; uint64_t fwversion; /* obfuscated by tpm */ tpm_sha256_name_t name; /* sha256 of tpm_rs256_pubarea_t */ tpm_sha256_name_t qual_name; /* full tpm path of attested key */ }) /* Part 2, 11.2.4.5: TPM2B_PUBLIC_KEY_RSA */ PACKED_TYPE(tpm_rs256_key_t, struct tpm_rs256_key { uint16_t size; /* sizeof(body) */ uint8_t body[256]; }) /* Part 2, 11.2.5.1: TPM2B_ECC_PARAMETER */ PACKED_TYPE(tpm_es256_coord_t, struct tpm_es256_coord { uint16_t size; /* sizeof(body) */ uint8_t body[32]; }) /* Part 2, 11.2.5.2: TPMS_ECC_POINT */ PACKED_TYPE(tpm_es256_point_t, struct tpm_es256_point { tpm_es256_coord_t x; tpm_es256_coord_t y; }) /* Part 2, 12.2.3.5: TPMS_RSA_PARMS */ PACKED_TYPE(tpm_rs256_param_t, struct tpm_rs256_param { uint16_t symmetric; /* TPM_ALG_NULL */ uint16_t scheme; /* TPM_ALG_NULL */ uint16_t keybits; /* 2048 */ uint32_t exponent; /* zero (meaning 2^16 + 1) */ }) /* Part 2, 12.2.3.6: TPMS_ECC_PARMS */ PACKED_TYPE(tpm_es256_param_t, struct tpm_es256_param { uint16_t symmetric; /* TPM_ALG_NULL */ uint16_t scheme; /* TPM_ALG_NULL */ uint16_t curve_id; /* TPM_ECC_P256 */ uint16_t kdf; /* TPM_ALG_NULL */ }) /* Part 2, 12.2.4: TPMT_PUBLIC */ PACKED_TYPE(tpm_rs256_pubarea_t, struct tpm_rs256_pubarea { uint16_t alg; /* TPM_ALG_RSA */ uint16_t hash; /* TPM_ALG_SHA256 */ uint32_t attr; tpm_sha256_digest_t policy; /* must be present? */ tpm_rs256_param_t param; tpm_rs256_key_t key; }) /* Part 2, 12.2.4: TPMT_PUBLIC */ PACKED_TYPE(tpm_es256_pubarea_t, struct tpm_es256_pubarea { uint16_t alg; /* TPM_ALG_ECC */ uint16_t hash; /* TPM_ALG_SHA256 */ uint32_t attr; tpm_sha256_digest_t policy; /* must be present? */ tpm_es256_param_t param; tpm_es256_point_t point; }) static int get_signed_sha1(tpm_sha1_data_t *dgst, const fido_blob_t *authdata, const fido_blob_t *clientdata) { const EVP_MD *md = NULL; EVP_MD_CTX *ctx = NULL; int ok = -1; if ((dgst->size = sizeof(dgst->body)) != SHA_DIGEST_LENGTH || (md = EVP_sha1()) == NULL || (ctx = EVP_MD_CTX_new()) == NULL || EVP_DigestInit_ex(ctx, md, NULL) != 1 || EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 || EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 || EVP_DigestFinal_ex(ctx, dgst->body, NULL) != 1) { fido_log_debug("%s: sha1", __func__); goto fail; } ok = 0; fail: EVP_MD_CTX_free(ctx); return (ok); } static int get_signed_name(tpm_sha256_name_t *name, const fido_blob_t *pubarea) { name->alg = TPM_ALG_SHA256; name->size = sizeof(name->alg) + sizeof(name->body); if (sizeof(name->body) != SHA256_DIGEST_LENGTH || SHA256(pubarea->ptr, pubarea->len, name->body) != name->body) { fido_log_debug("%s: sha256", __func__); return -1; } return 0; } static void bswap_rs256_pubarea(tpm_rs256_pubarea_t *x) { x->alg = htobe16(x->alg); x->hash = htobe16(x->hash); x->attr = htobe32(x->attr); x->policy.size = htobe16(x->policy.size); x->param.symmetric = htobe16(x->param.symmetric); x->param.scheme = htobe16(x->param.scheme); x->param.keybits = htobe16(x->param.keybits); x->key.size = htobe16(x->key.size); } static void bswap_es256_pubarea(tpm_es256_pubarea_t *x) { x->alg = htobe16(x->alg); x->hash = htobe16(x->hash); x->attr = htobe32(x->attr); x->policy.size = htobe16(x->policy.size); x->param.symmetric = htobe16(x->param.symmetric); x->param.scheme = htobe16(x->param.scheme); x->param.curve_id = htobe16(x->param.curve_id); x->param.kdf = htobe16(x->param.kdf); x->point.x.size = htobe16(x->point.x.size); x->point.y.size = htobe16(x->point.y.size); } static void bswap_sha1_certinfo(tpm_sha1_attest_t *x) { x->magic = htobe32(x->magic); x->type = htobe16(x->type); x->signer.size = htobe16(x->signer.size); x->data.size = htobe16(x->data.size); x->name.alg = htobe16(x->name.alg); x->name.size = htobe16(x->name.size); } static int check_rs256_pubarea(const fido_blob_t *buf, const rs256_pk_t *pk) { const tpm_rs256_pubarea_t *actual; tpm_rs256_pubarea_t expected; int ok; if (buf->len != sizeof(*actual)) { fido_log_debug("%s: buf->len=%zu", __func__, buf->len); return -1; } actual = (const void *)buf->ptr; memset(&expected, 0, sizeof(expected)); expected.alg = TPM_ALG_RSA; expected.hash = TPM_ALG_SHA256; expected.attr = be32toh(actual->attr); expected.attr &= ~(TPMA_RESERVED|TPMA_CLEAR); expected.attr |= (TPMA_FIXED|TPMA_FIXED_P|TPMA_SENSITIVE|TPMA_SIGN); expected.policy = actual->policy; expected.policy.size = sizeof(expected.policy.body); expected.param.symmetric = TPM_ALG_NULL; expected.param.scheme = TPM_ALG_NULL; expected.param.keybits = 2048; expected.param.exponent = 0; /* meaning 2^16+1 */ expected.key.size = sizeof(expected.key.body); memcpy(&expected.key.body, &pk->n, sizeof(expected.key.body)); bswap_rs256_pubarea(&expected); ok = timingsafe_bcmp(&expected, actual, sizeof(expected)); explicit_bzero(&expected, sizeof(expected)); return ok != 0 ? -1 : 0; } static int check_es256_pubarea(const fido_blob_t *buf, const es256_pk_t *pk) { const tpm_es256_pubarea_t *actual; tpm_es256_pubarea_t expected; int ok; if (buf->len != sizeof(*actual)) { fido_log_debug("%s: buf->len=%zu", __func__, buf->len); return -1; } actual = (const void *)buf->ptr; memset(&expected, 0, sizeof(expected)); expected.alg = TPM_ALG_ECC; expected.hash = TPM_ALG_SHA256; expected.attr = be32toh(actual->attr); expected.attr &= ~(TPMA_RESERVED|TPMA_CLEAR); expected.attr |= (TPMA_FIXED|TPMA_FIXED_P|TPMA_SENSITIVE|TPMA_SIGN); expected.policy = actual->policy; expected.policy.size = sizeof(expected.policy.body); expected.param.symmetric = TPM_ALG_NULL; expected.param.scheme = TPM_ALG_NULL; /* TCG Alg. Registry, 5.2.4 */ expected.param.curve_id = TPM_ECC_P256; expected.param.kdf = TPM_ALG_NULL; expected.point.x.size = sizeof(expected.point.x.body); expected.point.y.size = sizeof(expected.point.y.body); memcpy(&expected.point.x.body, &pk->x, sizeof(expected.point.x.body)); memcpy(&expected.point.y.body, &pk->y, sizeof(expected.point.y.body)); bswap_es256_pubarea(&expected); ok = timingsafe_bcmp(&expected, actual, sizeof(expected)); explicit_bzero(&expected, sizeof(expected)); return ok != 0 ? -1 : 0; } static int check_sha1_certinfo(const fido_blob_t *buf, const fido_blob_t *clientdata_hash, const fido_blob_t *authdata_raw, const fido_blob_t *pubarea) { const tpm_sha1_attest_t *actual; tpm_sha1_attest_t expected; tpm_sha1_data_t signed_data; tpm_sha256_name_t signed_name; int ok = -1; memset(&signed_data, 0, sizeof(signed_data)); memset(&signed_name, 0, sizeof(signed_name)); if (get_signed_sha1(&signed_data, authdata_raw, clientdata_hash) < 0 || get_signed_name(&signed_name, pubarea) < 0) { fido_log_debug("%s: get_signed_sha1/name", __func__); goto fail; } if (buf->len != sizeof(*actual)) { fido_log_debug("%s: buf->len=%zu", __func__, buf->len); goto fail; } actual = (const void *)buf->ptr; memset(&expected, 0, sizeof(expected)); expected.magic = TPM_MAGIC; expected.type = TPM_ST_CERTIFY; expected.signer = actual->signer; expected.signer.size = sizeof(expected.signer.alg) + sizeof(expected.signer.body); expected.data = signed_data; expected.clock = actual->clock; expected.clock.safe = 1; expected.fwversion = actual->fwversion; expected.name = signed_name; expected.qual_name = actual->qual_name; bswap_sha1_certinfo(&expected); ok = timingsafe_bcmp(&expected, actual, sizeof(expected)); fail: explicit_bzero(&expected, sizeof(expected)); explicit_bzero(&signed_data, sizeof(signed_data)); explicit_bzero(&signed_name, sizeof(signed_name)); return ok != 0 ? -1 : 0; } int fido_get_signed_hash_tpm(fido_blob_t *dgst, const fido_blob_t *clientdata_hash, const fido_blob_t *authdata_raw, const fido_attstmt_t *attstmt, const fido_attcred_t *attcred) { const fido_blob_t *pubarea = &attstmt->pubarea; const fido_blob_t *certinfo = &attstmt->certinfo; if (attstmt->alg != COSE_RS1) { fido_log_debug("%s: unsupported alg %d", __func__, attstmt->alg); return -1; } switch (attcred->type) { case COSE_ES256: if (check_es256_pubarea(pubarea, &attcred->pubkey.es256) < 0) { fido_log_debug("%s: check_es256_pubarea", __func__); return -1; } break; case COSE_RS256: if (check_rs256_pubarea(pubarea, &attcred->pubkey.rs256) < 0) { fido_log_debug("%s: check_rs256_pubarea", __func__); return -1; } break; default: fido_log_debug("%s: unsupported type %d", __func__, attcred->type); return -1; } if (check_sha1_certinfo(certinfo, clientdata_hash, authdata_raw, pubarea) < 0) { fido_log_debug("%s: check_sha1_certinfo", __func__); return -1; } if (dgst->len < SHA_DIGEST_LENGTH || SHA1(certinfo->ptr, certinfo->len, dgst->ptr) != dgst->ptr) { fido_log_debug("%s: sha1", __func__); return -1; } dgst->len = SHA_DIGEST_LENGTH; return 0; } libfido2-1.10.0/src/types.c000066400000000000000000000023501417126203300153610ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include "fido.h" void fido_str_array_free(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; } void fido_opt_array_free(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; } void fido_byte_array_free(fido_byte_array_t *ba) { free(ba->ptr); ba->ptr = NULL; ba->len = 0; } void fido_algo_free(fido_algo_t *a) { free(a->type); a->type = NULL; a->cose = 0; } void fido_algo_array_free(fido_algo_array_t *aa) { for (size_t i = 0; i < aa->len; i++) fido_algo_free(&aa->ptr[i]); free(aa->ptr); aa->ptr = NULL; aa->len = 0; } int fido_str_array_pack(fido_str_array_t *sa, const char * const *v, size_t n) { if ((sa->ptr = calloc(n, sizeof(char *))) == NULL) { fido_log_debug("%s: calloc", __func__); return -1; } for (size_t i = 0; i < n; i++) { if ((sa->ptr[i] = strdup(v[i])) == NULL) { fido_log_debug("%s: strdup", __func__); return -1; } sa->len++; } return 0; } libfido2-1.10.0/src/u2f.c000066400000000000000000000534711417126203300147230ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "fido.h" #include "fido/es256.h" #define U2F_PACE_MS (100) #if defined(_MSC_VER) static int usleep(unsigned int usec) { Sleep(usec / 1000); return (0); } #endif static int delay_ms(unsigned int ms, int *ms_remain) { if (*ms_remain > -1 && (unsigned int)*ms_remain < ms) ms = (unsigned int)*ms_remain; if (ms > UINT_MAX / 1000) { fido_log_debug("%s: ms=%u", __func__, ms); return (-1); } if (usleep(ms * 1000) < 0) { fido_log_error(errno, "%s: usleep", __func__); return (-1); } if (*ms_remain > -1) *ms_remain -= (int)ms; return (0); } 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__); fido_blob_reset(sig); 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) fido_blob_reset(x5c); 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); } /* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */ static int send_dummy_register(fido_dev_t *dev, int *ms) { iso7816_apdu_t *apdu = NULL; unsigned char challenge[SHA256_DIGEST_LENGTH]; unsigned char application[SHA256_DIGEST_LENGTH]; unsigned char reply[FIDO_MAXMSG]; int r; /* dummy challenge & application */ memset(&challenge, 0xff, sizeof(challenge)); memset(&application, 0xff, sizeof(application)); if ((apdu = iso7816_new(0, 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, CTAP_CMD_MSG, iso7816_ptr(apdu), iso7816_len(apdu), ms) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } if (fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms) < 2) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; } if (delay_ms(U2F_PACE_MS, ms) != 0) { fido_log_debug("%s: delay_ms", __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) { iso7816_apdu_t *apdu = NULL; unsigned char challenge[SHA256_DIGEST_LENGTH]; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; unsigned char reply[FIDO_MAXMSG]; 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(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(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, CTAP_CMD_MSG, iso7816_ptr(apdu), iso7816_len(apdu), ms) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } if (fido_rx(dev, CTAP_CMD_MSG, &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) { iso7816_apdu_t *apdu = NULL; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; unsigned char reply[FIDO_MAXMSG]; 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(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(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, CTAP_CMD_MSG, iso7816_ptr(apdu), iso7816_len(apdu), ms) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms)) < 2) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; } if (delay_ms(U2F_PACE_MS, ms) != 0) { fido_log_debug("%s: delay_ms", __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_attstmt(int cose_alg, const fido_blob_t *x5c, const fido_blob_t *sig, fido_blob_t *out) { cbor_item_t *item = NULL; cbor_item_t *x5c_cbor = NULL; const uint8_t alg_cbor = (uint8_t)(-cose_alg - 1); struct cbor_pair kv[3]; size_t alloc_len; int ok = -1; memset(&kv, 0, sizeof(kv)); memset(out, 0, sizeof(*out)); if ((item = cbor_new_definite_map(3)) == NULL) { fido_log_debug("%s: cbor_new_definite_map", __func__); goto fail; } if ((kv[0].key = cbor_build_string("alg")) == NULL || (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL || !cbor_map_add(item, kv[0])) { fido_log_debug("%s: alg", __func__); goto fail; } if ((kv[1].key = cbor_build_string("sig")) == NULL || (kv[1].value = fido_blob_encode(sig)) == NULL || !cbor_map_add(item, kv[1])) { fido_log_debug("%s: sig", __func__); goto fail; } if ((kv[2].key = cbor_build_string("x5c")) == NULL || (kv[2].value = cbor_new_definite_array(1)) == NULL || (x5c_cbor = fido_blob_encode(x5c)) == NULL || !cbor_array_push(kv[2].value, x5c_cbor) || !cbor_map_add(item, kv[2])) { fido_log_debug("%s: x5c", __func__); goto fail; } if ((out->len = cbor_serialize_alloc(item, &out->ptr, &alloc_len)) == 0) { fido_log_debug("%s: cbor_serialize_alloc", __func__); goto fail; } ok = 0; fail: if (item != NULL) cbor_decref(&item); if (x5c_cbor != NULL) cbor_decref(&x5c_cbor); for (size_t i = 0; i < nitems(kv); i++) { if (kv[i].key) cbor_decref(&kv[i].key); if (kv[i].value) cbor_decref(&kv[i].value); } 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 = htobe16(kh_len); 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); fido_blob_reset(&pk_blob); fido_blob_reset(&authdata_blob); 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; fido_blob_t stmt; 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)); memset(&stmt, 0, sizeof(stmt)); 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; } /* attstmt */ if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) { fido_log_debug("%s: encode_cred_attstmt", __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_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) { fido_log_debug("%s: fido_cred_set", __func__); r = FIDO_ERR_INTERNAL; goto fail; } r = FIDO_OK; fail: freezero(kh, kh_len); fido_blob_reset(&x5c); fido_blob_reset(&sig); fido_blob_reset(&ad); fido_blob_reset(&stmt); return (r); } int u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms) { iso7816_apdu_t *apdu = NULL; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; unsigned char reply[FIDO_MAXMSG]; int reply_len; int found; int r; 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(0, 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, CTAP_CMD_MSG, iso7816_ptr(apdu), iso7816_len(apdu), ms) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms)) < 2) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; } if (delay_ms(U2F_PACE_MS, ms) != 0) { fido_log_debug("%s: delay_ms", __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 (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) { fido_log_debug("%s: fido_blob_set", __func__); r = FIDO_ERR_INTERNAL; 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_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: fido_blob_reset(&sig); fido_blob_reset(&ad); return (r); } int u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms) { size_t nfound = 0; size_t 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++) { switch ((r = u2f_authenticate_single(dev, &fa->allow_list.ptr[i], fa, nfound, ms))) { case FIDO_OK: nauth_ok++; /* FALLTHROUGH */ case FIDO_ERR_USER_PRESENCE_REQUIRED: nfound++; break; default: 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 = nfound; if (nfound == 0) return (FIDO_ERR_NO_CREDENTIALS); if (nauth_ok == 0) return (FIDO_ERR_USER_PRESENCE_REQUIRED); return (FIDO_OK); } int u2f_get_touch_begin(fido_dev_t *dev, int *ms) { iso7816_apdu_t *apdu = NULL; const char *clientdata = FIDO_DUMMY_CLIENTDATA; const char *rp_id = FIDO_DUMMY_RP_ID; unsigned char clientdata_hash[SHA256_DIGEST_LENGTH]; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; unsigned char reply[FIDO_MAXMSG]; int r; memset(&clientdata_hash, 0, sizeof(clientdata_hash)); memset(&rp_id_hash, 0, sizeof(rp_id_hash)); if (SHA256((const void *)clientdata, strlen(clientdata), clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id, strlen(rp_id), rp_id_hash) != rp_id_hash) { fido_log_debug("%s: sha256", __func__); return (FIDO_ERR_INTERNAL); } if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * SHA256_DIGEST_LENGTH)) == NULL || iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 || iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) { fido_log_debug("%s: iso7816", __func__); r = FIDO_ERR_INTERNAL; goto fail; } if (dev->attr.flags & FIDO_CAP_WINK) { fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms); fido_rx(dev, CTAP_CMD_WINK, &reply, sizeof(reply), ms); } if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), iso7816_len(apdu), ms) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } r = FIDO_OK; fail: iso7816_free(&apdu); return (r); } int u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms) { unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms)) < 2) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_OK); /* ignore */ } switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) { case SW_CONDITIONS_NOT_SATISFIED: if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) { fido_log_debug("%s: u2f_get_touch_begin", __func__); return (r); } *touched = 0; break; case SW_NO_ERROR: *touched = 1; break; default: fido_log_debug("%s: unexpected sw", __func__); return (FIDO_ERR_RX); } return (FIDO_OK); } libfido2-1.10.0/src/webauthn.h000066400000000000000000001124011417126203300160360ustar00rootroot00000000000000// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #ifndef __WEBAUTHN_H_ #define __WEBAUTHN_H_ #pragma once #include #ifdef _MSC_VER #pragma region Desktop Family or OneCore Family #endif #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) #ifdef __cplusplus extern "C" { #endif #ifndef WINAPI #define WINAPI __stdcall #endif #ifndef INITGUID #define INITGUID #include #undef INITGUID #else #include #endif //+------------------------------------------------------------------------------------------ // API Version Information. // Caller should check for WebAuthNGetApiVersionNumber to check the presence of relevant APIs // and features for their usage. //------------------------------------------------------------------------------------------- #define WEBAUTHN_API_VERSION_1 1 // WEBAUTHN_API_VERSION_1 : Baseline Version // Data Structures and their sub versions: // - WEBAUTHN_RP_ENTITY_INFORMATION : 1 // - WEBAUTHN_USER_ENTITY_INFORMATION : 1 // - WEBAUTHN_CLIENT_DATA : 1 // - WEBAUTHN_COSE_CREDENTIAL_PARAMETER : 1 // - WEBAUTHN_COSE_CREDENTIAL_PARAMETERS : Not Applicable // - WEBAUTHN_CREDENTIAL : 1 // - WEBAUTHN_CREDENTIALS : Not Applicable // - WEBAUTHN_CREDENTIAL_EX : 1 // - WEBAUTHN_CREDENTIAL_LIST : Not Applicable // - WEBAUTHN_EXTENSION : Not Applicable // - WEBAUTHN_EXTENSIONS : Not Applicable // - WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS : 3 // - WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS : 4 // - WEBAUTHN_COMMON_ATTESTATION : 1 // - WEBAUTHN_CREDENTIAL_ATTESTATION : 3 // - WEBAUTHN_ASSERTION : 1 // Extensions: // - WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET // APIs: // - WebAuthNGetApiVersionNumber // - WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable // - WebAuthNAuthenticatorMakeCredential // - WebAuthNAuthenticatorGetAssertion // - WebAuthNFreeCredentialAttestation // - WebAuthNFreeAssertion // - WebAuthNGetCancellationId // - WebAuthNCancelCurrentOperation // - WebAuthNGetErrorName // - WebAuthNGetW3CExceptionDOMError #define WEBAUTHN_API_VERSION_2 2 // WEBAUTHN_API_VERSION_2 : Delta From WEBAUTHN_API_VERSION_1 // Added Extensions: // - WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT // #define WEBAUTHN_API_VERSION_3 3 // WEBAUTHN_API_VERSION_3 : Delta From WEBAUTHN_API_VERSION_2 // Data Structures and their sub versions: // - WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS : 4 // - WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS : 5 // - WEBAUTHN_CREDENTIAL_ATTESTATION : 4 // - WEBAUTHN_ASSERTION : 2 // Added Extensions: // - WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_BLOB // - WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH // #define WEBAUTHN_API_VERSION_4 4 // WEBAUTHN_API_VERSION_4 : Delta From WEBAUTHN_API_VERSION_3 // Data Structures and their sub versions: // - WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS : 5 // - WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS : 6 // - WEBAUTHN_ASSERTION : 3 // #define WEBAUTHN_API_CURRENT_VERSION WEBAUTHN_API_VERSION_4 //+------------------------------------------------------------------------------------------ // Information about an RP Entity //------------------------------------------------------------------------------------------- #define WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION 1 typedef struct _WEBAUTHN_RP_ENTITY_INFORMATION { // Version of this structure, to allow for modifications in the future. // This field is required and should be set to CURRENT_VERSION above. DWORD dwVersion; // Identifier for the RP. This field is required. PCWSTR pwszId; // Contains the friendly name of the Relying Party, such as "Acme Corporation", "Widgets Inc" or "Awesome Site". // This field is required. PCWSTR pwszName; // Optional URL pointing to RP's logo. PCWSTR pwszIcon; } WEBAUTHN_RP_ENTITY_INFORMATION, *PWEBAUTHN_RP_ENTITY_INFORMATION; typedef const WEBAUTHN_RP_ENTITY_INFORMATION *PCWEBAUTHN_RP_ENTITY_INFORMATION; //+------------------------------------------------------------------------------------------ // Information about an User Entity //------------------------------------------------------------------------------------------- #define WEBAUTHN_MAX_USER_ID_LENGTH 64 #define WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION 1 typedef struct _WEBAUTHN_USER_ENTITY_INFORMATION { // Version of this structure, to allow for modifications in the future. // This field is required and should be set to CURRENT_VERSION above. DWORD dwVersion; // Identifier for the User. This field is required. DWORD cbId; _Field_size_bytes_(cbId) PBYTE pbId; // Contains a detailed name for this account, such as "john.p.smith@example.com". PCWSTR pwszName; // Optional URL that can be used to retrieve an image containing the user's current avatar, // or a data URI that contains the image data. PCWSTR pwszIcon; // For User: Contains the friendly name associated with the user account by the Relying Party, such as "John P. Smith". PCWSTR pwszDisplayName; } WEBAUTHN_USER_ENTITY_INFORMATION, *PWEBAUTHN_USER_ENTITY_INFORMATION; typedef const WEBAUTHN_USER_ENTITY_INFORMATION *PCWEBAUTHN_USER_ENTITY_INFORMATION; //+------------------------------------------------------------------------------------------ // Information about client data. //------------------------------------------------------------------------------------------- #define WEBAUTHN_HASH_ALGORITHM_SHA_256 L"SHA-256" #define WEBAUTHN_HASH_ALGORITHM_SHA_384 L"SHA-384" #define WEBAUTHN_HASH_ALGORITHM_SHA_512 L"SHA-512" #define WEBAUTHN_CLIENT_DATA_CURRENT_VERSION 1 typedef struct _WEBAUTHN_CLIENT_DATA { // Version of this structure, to allow for modifications in the future. // This field is required and should be set to CURRENT_VERSION above. DWORD dwVersion; // Size of the pbClientDataJSON field. DWORD cbClientDataJSON; // UTF-8 encoded JSON serialization of the client data. _Field_size_bytes_(cbClientDataJSON) PBYTE pbClientDataJSON; // Hash algorithm ID used to hash the pbClientDataJSON field. LPCWSTR pwszHashAlgId; } WEBAUTHN_CLIENT_DATA, *PWEBAUTHN_CLIENT_DATA; typedef const WEBAUTHN_CLIENT_DATA *PCWEBAUTHN_CLIENT_DATA; //+------------------------------------------------------------------------------------------ // Information about credential parameters. //------------------------------------------------------------------------------------------- #define WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY L"public-key" #define WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256 -7 #define WEBAUTHN_COSE_ALGORITHM_ECDSA_P384_WITH_SHA384 -35 #define WEBAUTHN_COSE_ALGORITHM_ECDSA_P521_WITH_SHA512 -36 #define WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA256 -257 #define WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA384 -258 #define WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA512 -259 #define WEBAUTHN_COSE_ALGORITHM_RSA_PSS_WITH_SHA256 -37 #define WEBAUTHN_COSE_ALGORITHM_RSA_PSS_WITH_SHA384 -38 #define WEBAUTHN_COSE_ALGORITHM_RSA_PSS_WITH_SHA512 -39 #define WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION 1 typedef struct _WEBAUTHN_COSE_CREDENTIAL_PARAMETER { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Well-known credential type specifying a credential to create. LPCWSTR pwszCredentialType; // Well-known COSE algorithm specifying the algorithm to use for the credential. LONG lAlg; } WEBAUTHN_COSE_CREDENTIAL_PARAMETER, *PWEBAUTHN_COSE_CREDENTIAL_PARAMETER; typedef const WEBAUTHN_COSE_CREDENTIAL_PARAMETER *PCWEBAUTHN_COSE_CREDENTIAL_PARAMETER; typedef struct _WEBAUTHN_COSE_CREDENTIAL_PARAMETERS { DWORD cCredentialParameters; _Field_size_(cCredentialParameters) PWEBAUTHN_COSE_CREDENTIAL_PARAMETER pCredentialParameters; } WEBAUTHN_COSE_CREDENTIAL_PARAMETERS, *PWEBAUTHN_COSE_CREDENTIAL_PARAMETERS; typedef const WEBAUTHN_COSE_CREDENTIAL_PARAMETERS *PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS; //+------------------------------------------------------------------------------------------ // Information about credential. //------------------------------------------------------------------------------------------- #define WEBAUTHN_CREDENTIAL_CURRENT_VERSION 1 typedef struct _WEBAUTHN_CREDENTIAL { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Size of pbID. DWORD cbId; // Unique ID for this particular credential. _Field_size_bytes_(cbId) PBYTE pbId; // Well-known credential type specifying what this particular credential is. LPCWSTR pwszCredentialType; } WEBAUTHN_CREDENTIAL, *PWEBAUTHN_CREDENTIAL; typedef const WEBAUTHN_CREDENTIAL *PCWEBAUTHN_CREDENTIAL; typedef struct _WEBAUTHN_CREDENTIALS { DWORD cCredentials; _Field_size_(cCredentials) PWEBAUTHN_CREDENTIAL pCredentials; } WEBAUTHN_CREDENTIALS, *PWEBAUTHN_CREDENTIALS; typedef const WEBAUTHN_CREDENTIALS *PCWEBAUTHN_CREDENTIALS; //+------------------------------------------------------------------------------------------ // Information about credential with extra information, such as, dwTransports //------------------------------------------------------------------------------------------- #define WEBAUTHN_CTAP_TRANSPORT_USB 0x00000001 #define WEBAUTHN_CTAP_TRANSPORT_NFC 0x00000002 #define WEBAUTHN_CTAP_TRANSPORT_BLE 0x00000004 #define WEBAUTHN_CTAP_TRANSPORT_TEST 0x00000008 #define WEBAUTHN_CTAP_TRANSPORT_INTERNAL 0x00000010 #define WEBAUTHN_CTAP_TRANSPORT_FLAGS_MASK 0x0000001F #define WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION 1 typedef struct _WEBAUTHN_CREDENTIAL_EX { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Size of pbID. DWORD cbId; // Unique ID for this particular credential. _Field_size_bytes_(cbId) PBYTE pbId; // Well-known credential type specifying what this particular credential is. LPCWSTR pwszCredentialType; // Transports. 0 implies no transport restrictions. DWORD dwTransports; } WEBAUTHN_CREDENTIAL_EX, *PWEBAUTHN_CREDENTIAL_EX; typedef const WEBAUTHN_CREDENTIAL_EX *PCWEBAUTHN_CREDENTIAL_EX; //+------------------------------------------------------------------------------------------ // Information about credential list with extra information //------------------------------------------------------------------------------------------- typedef struct _WEBAUTHN_CREDENTIAL_LIST { DWORD cCredentials; _Field_size_(cCredentials) PWEBAUTHN_CREDENTIAL_EX *ppCredentials; } WEBAUTHN_CREDENTIAL_LIST, *PWEBAUTHN_CREDENTIAL_LIST; typedef const WEBAUTHN_CREDENTIAL_LIST *PCWEBAUTHN_CREDENTIAL_LIST; //+------------------------------------------------------------------------------------------ // PRF values. //------------------------------------------------------------------------------------------- #define WEBAUTHN_CTAP_ONE_HMAC_SECRET_LENGTH 32 typedef struct _WEBAUTHN_HMAC_SECRET_SALT { // Size of pbFirst. DWORD cbFirst; _Field_size_bytes_(cbFirst) PBYTE pbFirst; // Required // Size of pbSecond. DWORD cbSecond; _Field_size_bytes_(cbSecond) PBYTE pbSecond; } WEBAUTHN_HMAC_SECRET_SALT, *PWEBAUTHN_HMAC_SECRET_SALT; typedef const WEBAUTHN_HMAC_SECRET_SALT *PCWEBAUTHN_HMAC_SECRET_SALT; typedef struct _WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT { // Size of pbCredID. DWORD cbCredID; _Field_size_bytes_(cbCredID) PBYTE pbCredID; // Required // PRF Values for above credential PWEBAUTHN_HMAC_SECRET_SALT pHmacSecretSalt; // Required } WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT, *PWEBAUTHN_CRED_WITH_HMAC_SECRET_SALT; typedef const WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT *PCWEBAUTHN_CRED_WITH_HMAC_SECRET_SALT; typedef struct _WEBAUTHN_HMAC_SECRET_SALT_VALUES { PWEBAUTHN_HMAC_SECRET_SALT pGlobalHmacSalt; DWORD cCredWithHmacSecretSaltList; _Field_size_(cCredWithHmacSecretSaltList) PWEBAUTHN_CRED_WITH_HMAC_SECRET_SALT pCredWithHmacSecretSaltList; } WEBAUTHN_HMAC_SECRET_SALT_VALUES, *PWEBAUTHN_HMAC_SECRET_SALT_VALUES; typedef const WEBAUTHN_HMAC_SECRET_SALT_VALUES *PCWEBAUTHN_HMAC_SECRET_SALT_VALUES; //+------------------------------------------------------------------------------------------ // Hmac-Secret extension //------------------------------------------------------------------------------------------- #define WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET L"hmac-secret" // Below type definitions is for WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET // MakeCredential Input Type: BOOL. // - pvExtension must point to a BOOL with the value TRUE. // - cbExtension must contain the sizeof(BOOL). // MakeCredential Output Type: BOOL. // - pvExtension will point to a BOOL with the value TRUE if credential // was successfully created with HMAC_SECRET. // - cbExtension will contain the sizeof(BOOL). // GetAssertion Input Type: Not Supported // GetAssertion Output Type: Not Supported //+------------------------------------------------------------------------------------------ // credProtect extension //------------------------------------------------------------------------------------------- #define WEBAUTHN_USER_VERIFICATION_ANY 0 #define WEBAUTHN_USER_VERIFICATION_OPTIONAL 1 #define WEBAUTHN_USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST 2 #define WEBAUTHN_USER_VERIFICATION_REQUIRED 3 typedef struct _WEBAUTHN_CRED_PROTECT_EXTENSION_IN { // One of the above WEBAUTHN_USER_VERIFICATION_* values DWORD dwCredProtect; // Set the following to TRUE to require authenticator support for the credProtect extension BOOL bRequireCredProtect; } WEBAUTHN_CRED_PROTECT_EXTENSION_IN, *PWEBAUTHN_CRED_PROTECT_EXTENSION_IN; typedef const WEBAUTHN_CRED_PROTECT_EXTENSION_IN *PCWEBAUTHN_CRED_PROTECT_EXTENSION_IN; #define WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT L"credProtect" // Below type definitions is for WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT // MakeCredential Input Type: WEBAUTHN_CRED_PROTECT_EXTENSION_IN. // - pvExtension must point to a WEBAUTHN_CRED_PROTECT_EXTENSION_IN struct // - cbExtension will contain the sizeof(WEBAUTHN_CRED_PROTECT_EXTENSION_IN). // MakeCredential Output Type: DWORD. // - pvExtension will point to a DWORD with one of the above WEBAUTHN_USER_VERIFICATION_* values // if credential was successfully created with CRED_PROTECT. // - cbExtension will contain the sizeof(DWORD). // GetAssertion Input Type: Not Supported // GetAssertion Output Type: Not Supported //+------------------------------------------------------------------------------------------ // credBlob extension //------------------------------------------------------------------------------------------- typedef struct _WEBAUTHN_CRED_BLOB_EXTENSION { // Size of pbCredBlob. DWORD cbCredBlob; _Field_size_bytes_(cbCredBlob) PBYTE pbCredBlob; } WEBAUTHN_CRED_BLOB_EXTENSION, *PWEBAUTHN_CRED_BLOB_EXTENSION; typedef const WEBAUTHN_CRED_BLOB_EXTENSION *PCWEBAUTHN_CRED_BLOB_EXTENSION; #define WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_BLOB L"credBlob" // Below type definitions is for WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_BLOB // MakeCredential Input Type: WEBAUTHN_CRED_BLOB_EXTENSION. // - pvExtension must point to a WEBAUTHN_CRED_BLOB_EXTENSION struct // - cbExtension must contain the sizeof(WEBAUTHN_CRED_BLOB_EXTENSION). // MakeCredential Output Type: BOOL. // - pvExtension will point to a BOOL with the value TRUE if credBlob was successfully created // - cbExtension will contain the sizeof(BOOL). // GetAssertion Input Type: BOOL. // - pvExtension must point to a BOOL with the value TRUE to request the credBlob. // - cbExtension must contain the sizeof(BOOL). // GetAssertion Output Type: WEBAUTHN_CRED_BLOB_EXTENSION. // - pvExtension will point to a WEBAUTHN_CRED_BLOB_EXTENSION struct if the authenticator // returns the credBlob in the signed extensions // - cbExtension will contain the sizeof(WEBAUTHN_CRED_BLOB_EXTENSION). //+------------------------------------------------------------------------------------------ // minPinLength extension //------------------------------------------------------------------------------------------- #define WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH L"minPinLength" // Below type definitions is for WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH // MakeCredential Input Type: BOOL. // - pvExtension must point to a BOOL with the value TRUE to request the minPinLength. // - cbExtension must contain the sizeof(BOOL). // MakeCredential Output Type: DWORD. // - pvExtension will point to a DWORD with the minimum pin length if returned by the authenticator // - cbExtension will contain the sizeof(DWORD). // GetAssertion Input Type: Not Supported // GetAssertion Output Type: Not Supported //+------------------------------------------------------------------------------------------ // Information about Extensions. //------------------------------------------------------------------------------------------- typedef struct _WEBAUTHN_EXTENSION { LPCWSTR pwszExtensionIdentifier; DWORD cbExtension; PVOID pvExtension; } WEBAUTHN_EXTENSION, *PWEBAUTHN_EXTENSION; typedef const WEBAUTHN_EXTENSION *PCWEBAUTHN_EXTENSION; typedef struct _WEBAUTHN_EXTENSIONS { DWORD cExtensions; _Field_size_(cExtensions) PWEBAUTHN_EXTENSION pExtensions; } WEBAUTHN_EXTENSIONS, *PWEBAUTHN_EXTENSIONS; typedef const WEBAUTHN_EXTENSIONS *PCWEBAUTHN_EXTENSIONS; //+------------------------------------------------------------------------------------------ // Options. //------------------------------------------------------------------------------------------- #define WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY 0 #define WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM 1 #define WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM 2 #define WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2 3 #define WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY 0 #define WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED 1 #define WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED 2 #define WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED 3 #define WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY 0 #define WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE 1 #define WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT 2 #define WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT 3 #define WEBAUTHN_ENTERPRISE_ATTESTATION_NONE 0 #define WEBAUTHN_ENTERPRISE_ATTESTATION_VENDOR_FACILITATED 1 #define WEBAUTHN_ENTERPRISE_ATTESTATION_PLATFORM_MANAGED 2 #define WEBAUTHN_LARGE_BLOB_SUPPORT_NONE 0 #define WEBAUTHN_LARGE_BLOB_SUPPORT_REQUIRED 1 #define WEBAUTHN_LARGE_BLOB_SUPPORT_PREFERRED 2 #define WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_1 1 #define WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_2 2 #define WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_3 3 #define WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_4 4 #define WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_5 5 #define WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_CURRENT_VERSION WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_5 typedef struct _WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Time that the operation is expected to complete within. // This is used as guidance, and can be overridden by the platform. DWORD dwTimeoutMilliseconds; // Credentials used for exclusion. WEBAUTHN_CREDENTIALS CredentialList; // Optional extensions to parse when performing the operation. WEBAUTHN_EXTENSIONS Extensions; // Optional. Platform vs Cross-Platform Authenticators. DWORD dwAuthenticatorAttachment; // Optional. Require key to be resident or not. Defaulting to FALSE. BOOL bRequireResidentKey; // User Verification Requirement. DWORD dwUserVerificationRequirement; // Attestation Conveyance Preference. DWORD dwAttestationConveyancePreference; // Reserved for future Use DWORD dwFlags; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_2 // // Cancellation Id - Optional - See WebAuthNGetCancellationId GUID *pCancellationId; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_3 // // Exclude Credential List. If present, "CredentialList" will be ignored. PWEBAUTHN_CREDENTIAL_LIST pExcludeCredentialList; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_4 // // Enterprise Attestation DWORD dwEnterpriseAttestation; // Large Blob Support: none, required or preferred // // NTE_INVALID_PARAMETER when large blob required or preferred and // bRequireResidentKey isn't set to TRUE DWORD dwLargeBlobSupport; // Optional. Prefer key to be resident. Defaulting to FALSE. When TRUE, // overrides the above bRequireResidentKey. BOOL bPreferResidentKey; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_5 // // Optional. BrowserInPrivate Mode. Defaulting to FALSE. BOOL bBrowserInPrivateMode; } WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS, *PWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS; typedef const WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS *PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS; #define WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE 0 #define WEBAUTHN_CRED_LARGE_BLOB_OPERATION_GET 1 #define WEBAUTHN_CRED_LARGE_BLOB_OPERATION_SET 2 #define WEBAUTHN_CRED_LARGE_BLOB_OPERATION_DELETE 3 #define WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_1 1 #define WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_2 2 #define WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_3 3 #define WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_4 4 #define WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_5 5 #define WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6 6 #define WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6 typedef struct _WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Time that the operation is expected to complete within. // This is used as guidance, and can be overridden by the platform. DWORD dwTimeoutMilliseconds; // Allowed Credentials List. WEBAUTHN_CREDENTIALS CredentialList; // Optional extensions to parse when performing the operation. WEBAUTHN_EXTENSIONS Extensions; // Optional. Platform vs Cross-Platform Authenticators. DWORD dwAuthenticatorAttachment; // User Verification Requirement. DWORD dwUserVerificationRequirement; // Reserved for future Use DWORD dwFlags; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_2 // // Optional identifier for the U2F AppId. Converted to UTF8 before being hashed. Not lower cased. PCWSTR pwszU2fAppId; // If the following is non-NULL, then, set to TRUE if the above pwszU2fAppid was used instead of // PCWSTR pwszRpId; BOOL *pbU2fAppId; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_3 // // Cancellation Id - Optional - See WebAuthNGetCancellationId GUID *pCancellationId; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_4 // // Allow Credential List. If present, "CredentialList" will be ignored. PWEBAUTHN_CREDENTIAL_LIST pAllowCredentialList; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_5 // DWORD dwCredLargeBlobOperation; // Size of pbCredLargeBlob DWORD cbCredLargeBlob; _Field_size_bytes_(cbCredLargeBlob) PBYTE pbCredLargeBlob; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6 // // PRF values which will be converted into HMAC-SECRET values according to WebAuthn Spec. PWEBAUTHN_HMAC_SECRET_SALT_VALUES pHmacSecretSaltValues; // Optional. BrowserInPrivate Mode. Defaulting to FALSE. BOOL bBrowserInPrivateMode; } WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS, *PWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS; typedef const WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *PCWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS; //+------------------------------------------------------------------------------------------ // Attestation Info. // //------------------------------------------------------------------------------------------- #define WEBAUTHN_ATTESTATION_DECODE_NONE 0 #define WEBAUTHN_ATTESTATION_DECODE_COMMON 1 // WEBAUTHN_ATTESTATION_DECODE_COMMON supports format types // L"packed" // L"fido-u2f" #define WEBAUTHN_ATTESTATION_VER_TPM_2_0 L"2.0" typedef struct _WEBAUTHN_X5C { // Length of X.509 encoded certificate DWORD cbData; // X.509 encoded certificate bytes _Field_size_bytes_(cbData) PBYTE pbData; } WEBAUTHN_X5C, *PWEBAUTHN_X5C; // Supports either Self or Full Basic Attestation // Note, new fields will be added to the following data structure to // support additional attestation format types, such as, TPM. // When fields are added, the dwVersion will be incremented. // // Therefore, your code must make the following check: // "if (dwVersion >= WEBAUTHN_COMMON_ATTESTATION_CURRENT_VERSION)" #define WEBAUTHN_COMMON_ATTESTATION_CURRENT_VERSION 1 typedef struct _WEBAUTHN_COMMON_ATTESTATION { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Hash and Padding Algorithm // // The following won't be set for "fido-u2f" which assumes "ES256". PCWSTR pwszAlg; LONG lAlg; // COSE algorithm // Signature that was generated for this attestation. DWORD cbSignature; _Field_size_bytes_(cbSignature) PBYTE pbSignature; // Following is set for Full Basic Attestation. If not, set then, this is Self Attestation. // Array of X.509 DER encoded certificates. The first certificate is the signer, leaf certificate. DWORD cX5c; _Field_size_(cX5c) PWEBAUTHN_X5C pX5c; // Following are also set for tpm PCWSTR pwszVer; // L"2.0" DWORD cbCertInfo; _Field_size_bytes_(cbCertInfo) PBYTE pbCertInfo; DWORD cbPubArea; _Field_size_bytes_(cbPubArea) PBYTE pbPubArea; } WEBAUTHN_COMMON_ATTESTATION, *PWEBAUTHN_COMMON_ATTESTATION; typedef const WEBAUTHN_COMMON_ATTESTATION *PCWEBAUTHN_COMMON_ATTESTATION; #define WEBAUTHN_ATTESTATION_TYPE_PACKED L"packed" #define WEBAUTHN_ATTESTATION_TYPE_U2F L"fido-u2f" #define WEBAUTHN_ATTESTATION_TYPE_TPM L"tpm" #define WEBAUTHN_ATTESTATION_TYPE_NONE L"none" #define WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_1 1 #define WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_2 2 #define WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_3 3 #define WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4 4 #define WEBAUTHN_CREDENTIAL_ATTESTATION_CURRENT_VERSION WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4 typedef struct _WEBAUTHN_CREDENTIAL_ATTESTATION { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Attestation format type PCWSTR pwszFormatType; // Size of cbAuthenticatorData. DWORD cbAuthenticatorData; // Authenticator data that was created for this credential. _Field_size_bytes_(cbAuthenticatorData) PBYTE pbAuthenticatorData; // Size of CBOR encoded attestation information //0 => encoded as CBOR null value. DWORD cbAttestation; //Encoded CBOR attestation information _Field_size_bytes_(cbAttestation) PBYTE pbAttestation; DWORD dwAttestationDecodeType; // Following depends on the dwAttestationDecodeType // WEBAUTHN_ATTESTATION_DECODE_NONE // NULL - not able to decode the CBOR attestation information // WEBAUTHN_ATTESTATION_DECODE_COMMON // PWEBAUTHN_COMMON_ATTESTATION; PVOID pvAttestationDecode; // The CBOR encoded Attestation Object to be returned to the RP. DWORD cbAttestationObject; _Field_size_bytes_(cbAttestationObject) PBYTE pbAttestationObject; // The CredentialId bytes extracted from the Authenticator Data. // Used by Edge to return to the RP. DWORD cbCredentialId; _Field_size_bytes_(cbCredentialId) PBYTE pbCredentialId; // // Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_2 // WEBAUTHN_EXTENSIONS Extensions; // // Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_3 // // One of the WEBAUTHN_CTAP_TRANSPORT_* bits will be set corresponding to // the transport that was used. DWORD dwUsedTransport; // // Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4 // BOOL bEpAtt; BOOL bLargeBlobSupported; BOOL bResidentKey; } WEBAUTHN_CREDENTIAL_ATTESTATION, *PWEBAUTHN_CREDENTIAL_ATTESTATION; typedef const WEBAUTHN_CREDENTIAL_ATTESTATION *PCWEBAUTHN_CREDENTIAL_ATTESTATION; //+------------------------------------------------------------------------------------------ // authenticatorGetAssertion output. //------------------------------------------------------------------------------------------- #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_NONE 0 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_SUCCESS 1 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_NOT_SUPPORTED 2 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_INVALID_DATA 3 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_INVALID_PARAMETER 4 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_NOT_FOUND 5 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_MULTIPLE_CREDENTIALS 6 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_LACK_OF_SPACE 7 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_PLATFORM_ERROR 8 #define WEBAUTHN_CRED_LARGE_BLOB_STATUS_AUTHENTICATOR_ERROR 9 #define WEBAUTHN_ASSERTION_VERSION_1 1 #define WEBAUTHN_ASSERTION_VERSION_2 2 #define WEBAUTHN_ASSERTION_VERSION_3 3 #define WEBAUTHN_ASSERTION_CURRENT_VERSION WEBAUTHN_ASSERTION_VERSION_3 typedef struct _WEBAUTHN_ASSERTION { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Size of cbAuthenticatorData. DWORD cbAuthenticatorData; // Authenticator data that was created for this assertion. _Field_size_bytes_(cbAuthenticatorData) PBYTE pbAuthenticatorData; // Size of pbSignature. DWORD cbSignature; // Signature that was generated for this assertion. _Field_size_bytes_(cbSignature) PBYTE pbSignature; // Credential that was used for this assertion. WEBAUTHN_CREDENTIAL Credential; // Size of User Id DWORD cbUserId; // UserId _Field_size_bytes_(cbUserId) PBYTE pbUserId; // // Following fields have been added in WEBAUTHN_ASSERTION_VERSION_2 // WEBAUTHN_EXTENSIONS Extensions; // Size of pbCredLargeBlob DWORD cbCredLargeBlob; _Field_size_bytes_(cbCredLargeBlob) PBYTE pbCredLargeBlob; DWORD dwCredLargeBlobStatus; // // Following fields have been added in WEBAUTHN_ASSERTION_VERSION_3 // PWEBAUTHN_HMAC_SECRET_SALT pHmacSecret; } WEBAUTHN_ASSERTION, *PWEBAUTHN_ASSERTION; typedef const WEBAUTHN_ASSERTION *PCWEBAUTHN_ASSERTION; //+------------------------------------------------------------------------------------------ // APIs. //------------------------------------------------------------------------------------------- DWORD WINAPI WebAuthNGetApiVersionNumber(); HRESULT WINAPI WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable( _Out_ BOOL *pbIsUserVerifyingPlatformAuthenticatorAvailable); HRESULT WINAPI WebAuthNAuthenticatorMakeCredential( _In_ HWND hWnd, _In_ PCWEBAUTHN_RP_ENTITY_INFORMATION pRpInformation, _In_ PCWEBAUTHN_USER_ENTITY_INFORMATION pUserInformation, _In_ PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS pPubKeyCredParams, _In_ PCWEBAUTHN_CLIENT_DATA pWebAuthNClientData, _In_opt_ PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS pWebAuthNMakeCredentialOptions, _Outptr_result_maybenull_ PWEBAUTHN_CREDENTIAL_ATTESTATION *ppWebAuthNCredentialAttestation); HRESULT WINAPI WebAuthNAuthenticatorGetAssertion( _In_ HWND hWnd, _In_ LPCWSTR pwszRpId, _In_ PCWEBAUTHN_CLIENT_DATA pWebAuthNClientData, _In_opt_ PCWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS pWebAuthNGetAssertionOptions, _Outptr_result_maybenull_ PWEBAUTHN_ASSERTION *ppWebAuthNAssertion); void WINAPI WebAuthNFreeCredentialAttestation( _In_opt_ PWEBAUTHN_CREDENTIAL_ATTESTATION pWebAuthNCredentialAttestation); void WINAPI WebAuthNFreeAssertion( _In_ PWEBAUTHN_ASSERTION pWebAuthNAssertion); HRESULT WINAPI WebAuthNGetCancellationId( _Out_ GUID* pCancellationId); HRESULT WINAPI WebAuthNCancelCurrentOperation( _In_ const GUID* pCancellationId); // // Returns the following Error Names: // L"Success" - S_OK // L"InvalidStateError" - NTE_EXISTS // L"ConstraintError" - HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), // NTE_NOT_SUPPORTED, // NTE_TOKEN_KEYSET_STORAGE_FULL // L"NotSupportedError" - NTE_INVALID_PARAMETER // L"NotAllowedError" - NTE_DEVICE_NOT_FOUND, // NTE_NOT_FOUND, // HRESULT_FROM_WIN32(ERROR_CANCELLED), // NTE_USER_CANCELLED, // HRESULT_FROM_WIN32(ERROR_TIMEOUT) // L"UnknownError" - All other hr values // PCWSTR WINAPI WebAuthNGetErrorName( _In_ HRESULT hr); HRESULT WINAPI WebAuthNGetW3CExceptionDOMError( _In_ HRESULT hr); #ifdef __cplusplus } // Balance extern "C" above #endif #endif // WINAPI_FAMILY_PARTITION #ifdef _MSC_VER #pragma endregion #endif #endif // __WEBAUTHN_H_ libfido2-1.10.0/src/winhello.c000066400000000000000000000574411417126203300160510ustar00rootroot00000000000000/* * Copyright (c) 2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include "fido.h" #include "webauthn.h" #ifndef NTE_INVALID_PARAMETER #define NTE_INVALID_PARAMETER _HRESULT_TYPEDEF_(0x80090027) #endif #ifndef NTE_NOT_SUPPORTED #define NTE_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x80090029) #endif #ifndef NTE_DEVICE_NOT_FOUND #define NTE_DEVICE_NOT_FOUND _HRESULT_TYPEDEF_(0x80090035) #endif #define MAXCHARS 128 #define MAXCREDS 128 #define MAXMSEC 6000 * 1000 #define VENDORID 0x045e #define PRODID 0x0001 struct winhello_assert { WEBAUTHN_CLIENT_DATA cd; WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS opt; WEBAUTHN_ASSERTION *assert; wchar_t *rp_id; }; struct winhello_cred { WEBAUTHN_RP_ENTITY_INFORMATION rp; WEBAUTHN_USER_ENTITY_INFORMATION user; WEBAUTHN_COSE_CREDENTIAL_PARAMETER alg; WEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose; WEBAUTHN_CLIENT_DATA cd; WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS opt; WEBAUTHN_CREDENTIAL_ATTESTATION *att; wchar_t *rp_id; wchar_t *rp_name; wchar_t *user_name; wchar_t *user_icon; wchar_t *display_name; }; typedef DWORD WINAPI webauthn_get_api_version_t(void); typedef PCWSTR WINAPI webauthn_strerr_t(HRESULT); typedef HRESULT WINAPI webauthn_get_assert_t(HWND, LPCWSTR, PCWEBAUTHN_CLIENT_DATA, PCWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS, PWEBAUTHN_ASSERTION *); typedef HRESULT WINAPI webauthn_make_cred_t(HWND, PCWEBAUTHN_RP_ENTITY_INFORMATION, PCWEBAUTHN_USER_ENTITY_INFORMATION, PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS, PCWEBAUTHN_CLIENT_DATA, PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS, PWEBAUTHN_CREDENTIAL_ATTESTATION *); typedef void WINAPI webauthn_free_assert_t(PWEBAUTHN_ASSERTION); typedef void WINAPI webauthn_free_attest_t(PWEBAUTHN_CREDENTIAL_ATTESTATION); static TLS BOOL webauthn_loaded; static TLS HMODULE webauthn_handle; static TLS webauthn_get_api_version_t *webauthn_get_api_version; static TLS webauthn_strerr_t *webauthn_strerr; static TLS webauthn_get_assert_t *webauthn_get_assert; static TLS webauthn_make_cred_t *webauthn_make_cred; static TLS webauthn_free_assert_t *webauthn_free_assert; static TLS webauthn_free_attest_t *webauthn_free_attest; static int webauthn_load(void) { DWORD n = 1; if (webauthn_loaded || webauthn_handle != NULL) { fido_log_debug("%s: already loaded", __func__); return -1; } if ((webauthn_handle = LoadLibrary("webauthn.dll")) == NULL) { fido_log_debug("%s: LoadLibrary", __func__); return -1; } if ((webauthn_get_api_version = (webauthn_get_api_version_t *)GetProcAddress(webauthn_handle, "WebAuthNGetApiVersionNumber")) == NULL) { fido_log_debug("%s: WebAuthNGetApiVersionNumber", __func__); /* WebAuthNGetApiVersionNumber might not exist */ } if (webauthn_get_api_version != NULL && (n = webauthn_get_api_version()) < 1) { fido_log_debug("%s: unsupported api %lu", __func__, (u_long)n); goto fail; } fido_log_debug("%s: api version %lu", __func__, (u_long)n); if ((webauthn_strerr = (webauthn_strerr_t *)GetProcAddress(webauthn_handle, "WebAuthNGetErrorName")) == NULL) { fido_log_debug("%s: WebAuthNGetErrorName", __func__); goto fail; } if ((webauthn_get_assert = (webauthn_get_assert_t *)GetProcAddress(webauthn_handle, "WebAuthNAuthenticatorGetAssertion")) == NULL) { fido_log_debug("%s: WebAuthNAuthenticatorGetAssertion", __func__); goto fail; } if ((webauthn_make_cred = (webauthn_make_cred_t *)GetProcAddress(webauthn_handle, "WebAuthNAuthenticatorMakeCredential")) == NULL) { fido_log_debug("%s: WebAuthNAuthenticatorMakeCredential", __func__); goto fail; } if ((webauthn_free_assert = (webauthn_free_assert_t *)GetProcAddress(webauthn_handle, "WebAuthNFreeAssertion")) == NULL) { fido_log_debug("%s: WebAuthNFreeAssertion", __func__); goto fail; } if ((webauthn_free_attest = (webauthn_free_attest_t *)GetProcAddress(webauthn_handle, "WebAuthNFreeCredentialAttestation")) == NULL) { fido_log_debug("%s: WebAuthNFreeCredentialAttestation", __func__); goto fail; } webauthn_loaded = true; return 0; fail: fido_log_debug("%s: GetProcAddress", __func__); webauthn_get_api_version = NULL; webauthn_strerr = NULL; webauthn_get_assert = NULL; webauthn_make_cred = NULL; webauthn_free_assert = NULL; webauthn_free_attest = NULL; FreeLibrary(webauthn_handle); webauthn_handle = NULL; return -1; } static wchar_t * to_utf16(const char *utf8) { int nch; wchar_t *utf16; if (utf8 == NULL) { fido_log_debug("%s: NULL", __func__); return NULL; } if ((nch = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)) < 1 || (size_t)nch > MAXCHARS) { fido_log_debug("%s: MultiByteToWideChar %d", __func__, nch); return NULL; } if ((utf16 = calloc((size_t)nch, sizeof(*utf16))) == NULL) { fido_log_debug("%s: calloc", __func__); return NULL; } if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, nch) != nch) { fido_log_debug("%s: MultiByteToWideChar", __func__); free(utf16); return NULL; } return utf16; } static int to_fido(HRESULT hr) { switch (hr) { case NTE_NOT_SUPPORTED: return FIDO_ERR_UNSUPPORTED_OPTION; case NTE_INVALID_PARAMETER: return FIDO_ERR_INVALID_PARAMETER; case NTE_TOKEN_KEYSET_STORAGE_FULL: return FIDO_ERR_KEY_STORE_FULL; case NTE_DEVICE_NOT_FOUND: case NTE_NOT_FOUND: return FIDO_ERR_NOT_ALLOWED; default: fido_log_debug("%s: hr=0x%lx", __func__, (u_long)hr); return FIDO_ERR_INTERNAL; } } static int pack_cd(WEBAUTHN_CLIENT_DATA *out, const fido_blob_t *in) { if (in->ptr == NULL) { fido_log_debug("%s: NULL", __func__); return -1; } if (in->len > ULONG_MAX) { fido_log_debug("%s: in->len=%zu", __func__, in->len); return -1; } out->dwVersion = WEBAUTHN_CLIENT_DATA_CURRENT_VERSION; out->cbClientDataJSON = (DWORD)in->len; out->pbClientDataJSON = in->ptr; out->pwszHashAlgId = WEBAUTHN_HASH_ALGORITHM_SHA_256; return 0; } static int pack_credlist(WEBAUTHN_CREDENTIALS *out, const fido_blob_array_t *in) { WEBAUTHN_CREDENTIAL *c; if (in->len == 0) { return 0; /* nothing to do */ } if (in->len > MAXCREDS) { fido_log_debug("%s: in->len=%zu", __func__, in->len); return -1; } if ((out->pCredentials = calloc(in->len, sizeof(*c))) == NULL) { fido_log_debug("%s: calloc", __func__); return -1; } out->cCredentials = (DWORD)in->len; for (size_t i = 0; i < in->len; i++) { if (in->ptr[i].len > ULONG_MAX) { fido_log_debug("%s: %zu", __func__, in->ptr[i].len); return -1; } c = &out->pCredentials[i]; c->dwVersion = WEBAUTHN_CREDENTIAL_CURRENT_VERSION; c->cbId = (DWORD)in->ptr[i].len; c->pbId = in->ptr[i].ptr; c->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY; } return 0; } static int set_cred_uv(DWORD *out, fido_opt_t uv, const char *pin) { if (pin) { *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; return 0; } switch (uv) { case FIDO_OPT_OMIT: case FIDO_OPT_FALSE: *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; break; case FIDO_OPT_TRUE: *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; break; } return 0; } static int set_assert_uv(DWORD *out, fido_opt_t uv, const char *pin) { if (pin) { *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; return 0; } switch (uv) { case FIDO_OPT_OMIT: *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; break; case FIDO_OPT_FALSE: *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; break; case FIDO_OPT_TRUE: *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; break; } return 0; } static int pack_rp(wchar_t **id, wchar_t **name, WEBAUTHN_RP_ENTITY_INFORMATION *out, const fido_rp_t *in) { /* keep non-const copies of pwsz* for free() */ out->dwVersion = WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION; if ((out->pwszId = *id = to_utf16(in->id)) == NULL) { fido_log_debug("%s: id", __func__); return -1; } if (in->name && (out->pwszName = *name = to_utf16(in->name)) == NULL) { fido_log_debug("%s: name", __func__); return -1; } return 0; } static int pack_user(wchar_t **name, wchar_t **icon, wchar_t **display_name, WEBAUTHN_USER_ENTITY_INFORMATION *out, const fido_user_t *in) { if (in->id.ptr == NULL || in->id.len > ULONG_MAX) { fido_log_debug("%s: id", __func__); return -1; } out->dwVersion = WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION; out->cbId = (DWORD)in->id.len; out->pbId = in->id.ptr; /* keep non-const copies of pwsz* for free() */ if (in->name != NULL) { if ((out->pwszName = *name = to_utf16(in->name)) == NULL) { fido_log_debug("%s: name", __func__); return -1; } } if (in->icon != NULL) { if ((out->pwszIcon = *icon = to_utf16(in->icon)) == NULL) { fido_log_debug("%s: icon", __func__); return -1; } } if (in->display_name != NULL) { if ((out->pwszDisplayName = *display_name = to_utf16(in->display_name)) == NULL) { fido_log_debug("%s: display_name", __func__); return -1; } } return 0; } static int pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg, WEBAUTHN_COSE_CREDENTIAL_PARAMETERS *cose, int type) { switch (type) { case COSE_ES256: alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256; break; case COSE_EDDSA: alg->lAlg = -8; /* XXX */; break; case COSE_RS256: alg->lAlg = WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA256; break; default: fido_log_debug("%s: type %d", __func__, type); return -1; } alg->dwVersion = WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION; alg->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY; cose->cCredentialParameters = 1; cose->pCredentialParameters = alg; return 0; } static int pack_cred_ext(WEBAUTHN_EXTENSIONS *out, const fido_cred_ext_t *in) { WEBAUTHN_EXTENSION *e; WEBAUTHN_CRED_PROTECT_EXTENSION_IN *p; BOOL *b; size_t n = 0, i = 0; if (in->mask == 0) { return 0; /* nothing to do */ } if (in->mask & ~(FIDO_EXT_HMAC_SECRET | FIDO_EXT_CRED_PROTECT)) { fido_log_debug("%s: mask 0x%x", __func__, in->mask); return -1; } if (in->mask & FIDO_EXT_HMAC_SECRET) n++; if (in->mask & FIDO_EXT_CRED_PROTECT) n++; if ((out->pExtensions = calloc(n, sizeof(*e))) == NULL) { fido_log_debug("%s: calloc", __func__); return -1; } out->cExtensions = (DWORD)n; if (in->mask & FIDO_EXT_HMAC_SECRET) { if ((b = calloc(1, sizeof(*b))) == NULL) { fido_log_debug("%s: calloc", __func__); return -1; } *b = true; e = &out->pExtensions[i]; e->pwszExtensionIdentifier = WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET; e->pvExtension = b; e->cbExtension = sizeof(*b); i++; } if (in->mask & FIDO_EXT_CRED_PROTECT) { if ((p = calloc(1, sizeof(*p))) == NULL) { fido_log_debug("%s: calloc", __func__); return -1; } p->dwCredProtect = (DWORD)in->prot; p->bRequireCredProtect = true; e = &out->pExtensions[i]; e->pwszExtensionIdentifier = WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT; e->pvExtension = p; e->cbExtension = sizeof(*p); i++; } return 0; } static int unpack_assert_authdata(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { int r; if (wa->cbAuthenticatorData > SIZE_MAX) { fido_log_debug("%s: cbAuthenticatorData", __func__); return -1; } if ((r = fido_assert_set_authdata_raw(assert, 0, wa->pbAuthenticatorData, (size_t)wa->cbAuthenticatorData)) != FIDO_OK) { fido_log_debug("%s: fido_assert_set_authdata_raw: %s", __func__, fido_strerr(r)); return -1; } return 0; } static int unpack_assert_sig(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { int r; if (wa->cbSignature > SIZE_MAX) { fido_log_debug("%s: cbSignature", __func__); return -1; } if ((r = fido_assert_set_sig(assert, 0, wa->pbSignature, (size_t)wa->cbSignature)) != FIDO_OK) { fido_log_debug("%s: fido_assert_set_sig: %s", __func__, fido_strerr(r)); return -1; } return 0; } static int unpack_cred_id(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { if (wa->Credential.cbId > SIZE_MAX) { fido_log_debug("%s: Credential.cbId", __func__); return -1; } if (fido_blob_set(&assert->stmt[0].id, wa->Credential.pbId, (size_t)wa->Credential.cbId) < 0) { fido_log_debug("%s: fido_blob_set", __func__); return -1; } return 0; } static int unpack_user_id(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { if (wa->cbUserId == 0) return 0; /* user id absent */ if (wa->cbUserId > SIZE_MAX) { fido_log_debug("%s: cbUserId", __func__); return -1; } if (fido_blob_set(&assert->stmt[0].user.id, wa->pbUserId, (size_t)wa->cbUserId) < 0) { fido_log_debug("%s: fido_blob_set", __func__); return -1; } return 0; } static int translate_fido_assert(struct winhello_assert *ctx, const fido_assert_t *assert, const char *pin, int ms) { WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt; /* not supported by webauthn.h */ if (assert->up == FIDO_OPT_FALSE) { fido_log_debug("%s: up %d", __func__, assert->up); return FIDO_ERR_UNSUPPORTED_OPTION; } /* not implemented */ if (assert->ext.mask) { fido_log_debug("%s: ext 0x%x", __func__, assert->ext.mask); return FIDO_ERR_UNSUPPORTED_EXTENSION; } if ((ctx->rp_id = to_utf16(assert->rp_id)) == NULL) { fido_log_debug("%s: rp_id", __func__); return FIDO_ERR_INTERNAL; } if (pack_cd(&ctx->cd, &assert->cd) < 0) { fido_log_debug("%s: pack_cd", __func__); return FIDO_ERR_INTERNAL; } /* options */ opt = &ctx->opt; opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_1; opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms; if (pack_credlist(&opt->CredentialList, &assert->allow_list) < 0) { fido_log_debug("%s: pack_credlist", __func__); return FIDO_ERR_INTERNAL; } if (set_assert_uv(&opt->dwUserVerificationRequirement, assert->uv, pin) < 0) { fido_log_debug("%s: set_assert_uv", __func__); return FIDO_ERR_INTERNAL; } return FIDO_OK; } static int translate_winhello_assert(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { int r; if (assert->stmt_len > 0) { fido_log_debug("%s: stmt_len=%zu", __func__, assert->stmt_len); return FIDO_ERR_INTERNAL; } if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK) { fido_log_debug("%s: fido_assert_set_count: %s", __func__, fido_strerr(r)); return FIDO_ERR_INTERNAL; } if (unpack_assert_authdata(assert, wa) < 0) { fido_log_debug("%s: unpack_assert_authdata", __func__); return FIDO_ERR_INTERNAL; } if (unpack_assert_sig(assert, wa) < 0) { fido_log_debug("%s: unpack_assert_sig", __func__); return FIDO_ERR_INTERNAL; } if (unpack_cred_id(assert, wa) < 0) { fido_log_debug("%s: unpack_cred_id", __func__); return FIDO_ERR_INTERNAL; } if (unpack_user_id(assert, wa) < 0) { fido_log_debug("%s: unpack_user_id", __func__); return FIDO_ERR_INTERNAL; } return FIDO_OK; } static int translate_fido_cred(struct winhello_cred *ctx, const fido_cred_t *cred, const char *pin, int ms) { WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS *opt; if (pack_rp(&ctx->rp_id, &ctx->rp_name, &ctx->rp, &cred->rp) < 0) { fido_log_debug("%s: pack_rp", __func__); return FIDO_ERR_INTERNAL; } if (pack_user(&ctx->user_name, &ctx->user_icon, &ctx->display_name, &ctx->user, &cred->user) < 0) { fido_log_debug("%s: pack_user", __func__); return FIDO_ERR_INTERNAL; } if (pack_cose(&ctx->alg, &ctx->cose, cred->type) < 0) { fido_log_debug("%s: pack_cose", __func__); return FIDO_ERR_INTERNAL; } if (pack_cd(&ctx->cd, &cred->cd) < 0) { fido_log_debug("%s: pack_cd", __func__); return FIDO_ERR_INTERNAL; } /* options */ opt = &ctx->opt; opt->dwVersion = WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_1; opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms; opt->dwAttestationConveyancePreference = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; if (pack_credlist(&opt->CredentialList, &cred->excl) < 0) { fido_log_debug("%s: pack_credlist", __func__); return FIDO_ERR_INTERNAL; } if (pack_cred_ext(&opt->Extensions, &cred->ext) < 0) { fido_log_debug("%s: pack_cred_ext", __func__); return FIDO_ERR_UNSUPPORTED_EXTENSION; } if (set_cred_uv(&opt->dwUserVerificationRequirement, (cred->ext.mask & FIDO_EXT_CRED_PROTECT) ? FIDO_OPT_TRUE : cred->uv, pin) < 0) { fido_log_debug("%s: set_cred_uv", __func__); return FIDO_ERR_INTERNAL; } if (cred->rk == FIDO_OPT_TRUE) { opt->bRequireResidentKey = true; } return FIDO_OK; } static int decode_attobj(const cbor_item_t *key, const cbor_item_t *val, void *arg) { fido_cred_t *cred = 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 fail; } if (!strcmp(name, "fmt")) { if (cbor_decode_fmt(val, &cred->fmt) < 0) { fido_log_debug("%s: cbor_decode_fmt", __func__); goto fail; } } else if (!strcmp(name, "attStmt")) { if (cbor_decode_attstmt(val, &cred->attstmt) < 0) { fido_log_debug("%s: cbor_decode_attstmt", __func__); goto fail; } } else if (!strcmp(name, "authData")) { if (fido_blob_decode(val, &cred->authdata_raw) < 0) { fido_log_debug("%s: fido_blob_decode", __func__); goto fail; } if (cbor_decode_cred_authdata(val, cred->type, &cred->authdata_cbor, &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) { fido_log_debug("%s: cbor_decode_cred_authdata", __func__); goto fail; } } ok = 0; fail: free(name); return (ok); } static int translate_winhello_cred(fido_cred_t *cred, const WEBAUTHN_CREDENTIAL_ATTESTATION *att) { cbor_item_t *item = NULL; struct cbor_load_result cbor; int r = FIDO_ERR_INTERNAL; if (att->pbAttestationObject == NULL || att->cbAttestationObject > SIZE_MAX) { fido_log_debug("%s: pbAttestationObject", __func__); goto fail; } if ((item = cbor_load(att->pbAttestationObject, (size_t)att->cbAttestationObject, &cbor)) == NULL) { fido_log_debug("%s: cbor_load", __func__); goto fail; } if (cbor_isa_map(item) == false || cbor_map_is_definite(item) == false || cbor_map_iter(item, cred, decode_attobj) < 0) { fido_log_debug("%s: cbor type", __func__); goto fail; } r = FIDO_OK; fail: if (item != NULL) cbor_decref(&item); return r; } static int winhello_get_assert(HWND w, struct winhello_assert *ctx) { HRESULT hr; int r = FIDO_OK; if ((hr = webauthn_get_assert(w, ctx->rp_id, &ctx->cd, &ctx->opt, &ctx->assert)) != S_OK) { r = to_fido(hr); fido_log_debug("%s: %ls -> %s", __func__, webauthn_strerr(hr), fido_strerr(r)); } return r; } static int winhello_make_cred(HWND w, struct winhello_cred *ctx) { HRESULT hr; int r = FIDO_OK; if ((hr = webauthn_make_cred(w, &ctx->rp, &ctx->user, &ctx->cose, &ctx->cd, &ctx->opt, &ctx->att)) != S_OK) { r = to_fido(hr); fido_log_debug("%s: %ls -> %s", __func__, webauthn_strerr(hr), fido_strerr(r)); } return r; } static void winhello_assert_free(struct winhello_assert *ctx) { if (ctx == NULL) return; if (ctx->assert != NULL) webauthn_free_assert(ctx->assert); free(ctx->rp_id); free(ctx->opt.CredentialList.pCredentials); free(ctx); } static void winhello_cred_free(struct winhello_cred *ctx) { if (ctx == NULL) return; if (ctx->att != NULL) webauthn_free_attest(ctx->att); free(ctx->rp_id); free(ctx->rp_name); free(ctx->user_name); free(ctx->user_icon); free(ctx->display_name); free(ctx->opt.CredentialList.pCredentials); for (size_t i = 0; i < ctx->opt.Extensions.cExtensions; i++) { WEBAUTHN_EXTENSION *e; e = &ctx->opt.Extensions.pExtensions[i]; free(e->pvExtension); } free(ctx->opt.Extensions.pExtensions); free(ctx); } int fido_winhello_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { fido_dev_info_t *di; if (ilen == 0) { return FIDO_OK; } if (devlist == NULL) { return FIDO_ERR_INVALID_ARGUMENT; } if (!webauthn_loaded && webauthn_load() < 0) { fido_log_debug("%s: webauthn_load", __func__); return FIDO_OK; /* not an error */ } di = &devlist[*olen]; memset(di, 0, sizeof(*di)); di->path = strdup(FIDO_WINHELLO_PATH); di->manufacturer = strdup("Microsoft Corporation"); di->product = strdup("Windows Hello"); di->vendor_id = VENDORID; di->product_id = PRODID; if (di->path == NULL || di->manufacturer == NULL || di->product == NULL) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); return FIDO_ERR_INTERNAL; } ++(*olen); return FIDO_OK; } int fido_winhello_open(fido_dev_t *dev) { if (!webauthn_loaded && webauthn_load() < 0) { fido_log_debug("%s: webauthn_load", __func__); return FIDO_ERR_INTERNAL; } if (dev->flags != 0) return FIDO_ERR_INVALID_ARGUMENT; dev->attr.flags = FIDO_CAP_CBOR | FIDO_CAP_WINK; dev->flags = FIDO_DEV_WINHELLO | FIDO_DEV_CRED_PROT | FIDO_DEV_PIN_SET; return FIDO_OK; } int fido_winhello_close(fido_dev_t *dev) { memset(dev, 0, sizeof(*dev)); return FIDO_OK; } int fido_winhello_cancel(fido_dev_t *dev) { (void)dev; return FIDO_ERR_INTERNAL; } int fido_winhello_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin, int ms) { HWND w; struct winhello_assert *ctx; int r = FIDO_ERR_INTERNAL; (void)dev; fido_assert_reset_rx(assert); if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } if ((w = GetForegroundWindow()) == NULL) { fido_log_debug("%s: GetForegroundWindow", __func__); if ((w = GetTopWindow(NULL)) == NULL) { fido_log_debug("%s: GetTopWindow", __func__); goto fail; } } if ((r = translate_fido_assert(ctx, assert, pin, ms)) != FIDO_OK) { fido_log_debug("%s: translate_fido_assert", __func__); goto fail; } if ((r = winhello_get_assert(w, ctx)) != FIDO_OK) { fido_log_debug("%s: winhello_get_assert", __func__); goto fail; } if ((r = translate_winhello_assert(assert, ctx->assert)) != FIDO_OK) { fido_log_debug("%s: translate_winhello_assert", __func__); goto fail; } fail: winhello_assert_free(ctx); return r; } int fido_winhello_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) { const char *v[3] = { "U2F_V2", "FIDO_2_0", "FIDO_2_1_PRE" }; const char *e[2] = { "credProtect", "hmac-secret" }; const char *t[2] = { "nfc", "usb" }; const char *o[4] = { "rk", "up", "plat", "clientPin" }; (void)dev; fido_cbor_info_reset(ci); if (fido_str_array_pack(&ci->versions, v, nitems(v)) < 0 || fido_str_array_pack(&ci->extensions, e, nitems(e)) < 0 || fido_str_array_pack(&ci->transports, t, nitems(t)) < 0) { fido_log_debug("%s: fido_str_array_pack", __func__); return FIDO_ERR_INTERNAL; } if ((ci->options.name = calloc(nitems(o), sizeof(char *))) == NULL || (ci->options.value = calloc(nitems(o), sizeof(bool))) == NULL) { fido_log_debug("%s: calloc", __func__); return FIDO_ERR_INTERNAL; } for (size_t i = 0; i < nitems(o); i++) { if ((ci->options.name[i] = strdup(o[i])) == NULL) { fido_log_debug("%s: strdup", __func__); return FIDO_ERR_INTERNAL; } ci->options.value[i] = true; ci->options.len++; } return FIDO_OK; } int fido_winhello_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int ms) { HWND w; struct winhello_cred *ctx; int r = FIDO_ERR_INTERNAL; (void)dev; fido_cred_reset_rx(cred); if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } if ((w = GetForegroundWindow()) == NULL) { fido_log_debug("%s: GetForegroundWindow", __func__); if ((w = GetTopWindow(NULL)) == NULL) { fido_log_debug("%s: GetTopWindow", __func__); goto fail; } } if ((r = translate_fido_cred(ctx, cred, pin, ms)) != FIDO_OK) { fido_log_debug("%s: translate_fido_cred", __func__); goto fail; } if ((r = winhello_make_cred(w, ctx)) != FIDO_OK) { fido_log_debug("%s: winhello_make_cred", __func__); goto fail; } if ((r = translate_winhello_cred(cred, ctx->att)) != FIDO_OK) { fido_log_debug("%s: translate_winhello_cred", __func__); goto fail; } r = FIDO_OK; fail: winhello_cred_free(ctx); return r; } libfido2-1.10.0/tools/000077500000000000000000000000001417126203300144225ustar00rootroot00000000000000libfido2-1.10.0/tools/CMakeLists.txt000066400000000000000000000036031417126203300171640ustar00rootroot00000000000000# 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/bsd-getpagesize.c ../openbsd-compat/explicit_bzero.c ../openbsd-compat/freezero.c ../openbsd-compat/recallocarray.c ../openbsd-compat/strlcat.c ../openbsd-compat/strlcpy.c ../openbsd-compat/strsep.c ) if(WIN32 AND NOT CYGWIN AND NOT MSYS) list(APPEND COMPAT_SOURCES ../openbsd-compat/bsd-getline.c ../openbsd-compat/endian_win32.c ../openbsd-compat/explicit_bzero_win32.c ../openbsd-compat/getopt_long.c ../openbsd-compat/readpassphrase_win32.c ) if (BUILD_SHARED_LIBS) list(APPEND COMPAT_SOURCES ../openbsd-compat/posix_win.c) endif() else() list(APPEND COMPAT_SOURCES ../openbsd-compat/readpassphrase.c) endif() if(NOT MSVC) set_source_files_properties(assert_get.c assert_verify.c base64.c bio.c config.c cred_make.c cred_verify.c credman.c fido2-assert.c fido2-cred.c fido2-token.c pin.c token.c util.c PROPERTIES COMPILE_FLAGS "-Wconversion -Wsign-conversion") 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 config.c credman.c largeblob.c pin.c token.c util.c ${COMPAT_SOURCES} ) # set the library to link against if(BUILD_SHARED_LIBS) set(_FIDO2_LIBRARY fido2_shared) else() set(_FIDO2_LIBRARY fido2) endif() target_link_libraries(fido2-cred ${CRYPTO_LIBRARIES} ${_FIDO2_LIBRARY}) target_link_libraries(fido2-assert ${CRYPTO_LIBRARIES} ${_FIDO2_LIBRARY}) target_link_libraries(fido2-token ${CRYPTO_LIBRARIES} ${_FIDO2_LIBRARY}) install(TARGETS fido2-cred fido2-assert fido2-token DESTINATION ${CMAKE_INSTALL_BINDIR}) libfido2-1.10.0/tools/assert_get.c000066400000000000000000000161501417126203300167310ustar00rootroot00000000000000/* * 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" struct toggle { fido_opt_t up; fido_opt_t uv; fido_opt_t pin; }; static const char * opt2str(fido_opt_t v) { switch (v) { case FIDO_OPT_OMIT: return "omit"; case FIDO_OPT_TRUE: return "true"; case FIDO_OPT_FALSE: return "false"; default: return "unknown"; } } static void parse_toggle(const char *str, struct toggle *opt) { fido_opt_t *k; fido_opt_t v; char *assignment; char *key; char *val; if ((assignment = strdup(str)) == NULL) err(1, "strdup"); if ((val = strchr(assignment, '=')) == NULL) errx(1, "invalid assignment '%s'", assignment); key = assignment; *val++ = '\0'; if (!strcmp(val, "true")) v = FIDO_OPT_TRUE; else if (!strcmp(val, "false")) v = FIDO_OPT_FALSE; else errx(1, "unknown value '%s'", val); if (!strcmp(key, "up")) k = &opt->up; else if (!strcmp(key, "uv")) k = &opt->uv; else if (!strcmp(key, "pin")) k = &opt->pin; else errx(1, "unknown key '%s'", key); free(assignment); *k = v; } static fido_assert_t * prepare_assert(FILE *in_f, int flags, const struct toggle *opt) { 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); } fprintf(stderr, "up=%s\n", opt2str(opt->up)); fprintf(stderr, "uv=%s\n", opt2str(opt->uv)); fprintf(stderr, "pin=%s\n", opt2str(opt->pin)); } 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 ((r = fido_assert_set_up(assert, opt->up)) != FIDO_OK) errx(1, "fido_assert_set_up: %s", fido_strerr(r)); if ((r = fido_assert_set_uv(assert, opt->uv)) != 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_LARGEBLOB) { if ((r = fido_assert_set_extensions(assert, FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK) errx(1, "fido_assert_set_extensions: %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; char *key = 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 (flags & FLAG_LARGEBLOB) r |= base64_encode(fido_assert_largeblob_key_ptr(assert, idx), fido_assert_largeblob_key_len(assert, idx), &key); 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)); } if (key) { fprintf(out_f, "%s\n", key); explicit_bzero(key, strlen(key)); } free(key); 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; struct toggle opt; 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; opt.up = opt.uv = opt.pin = FIDO_OPT_OMIT; while ((ch = getopt(argc, argv, "bdhi:o:prt:uv")) != -1) { switch (ch) { case 'b': flags |= FLAG_LARGEBLOB; break; 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': opt.up = FIDO_OPT_TRUE; break; case 'r': flags |= FLAG_RK; break; case 't' : parse_toggle(optarg, &opt); break; case 'u': flags |= FLAG_U2F; break; case 'v': /* -v implies both pin and uv for historical reasons */ opt.pin = FIDO_OPT_TRUE; opt.uv = FIDO_OPT_TRUE; 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, &opt); dev = open_dev(argv[0]); if (flags & FLAG_U2F) fido_dev_force_u2f(dev); if (opt.pin == FIDO_OPT_TRUE) { 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); } libfido2-1.10.0/tools/assert_verify.c000066400000000000000000000105721417126203300174600ustar00rootroot00000000000000/* * 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 "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 && cose_type(argv[1], &type) < 0) 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); } libfido2-1.10.0/tools/base64.c000066400000000000000000000047031417126203300156560ustar00rootroot00000000000000/* * 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 "../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(const 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((const 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); } libfido2-1.10.0/tools/bio.c000066400000000000000000000144471417126203300153510ustar00rootroot00000000000000/* * 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 #ifdef HAVE_UNISTD_H #include #endif #include "../openbsd-compat/openbsd-compat.h" #include "extern.h" static int print_template(const fido_bio_template_array_t *ta, size_t idx) { const fido_bio_template_t *t = NULL; char *id = NULL; if ((t = fido_bio_template(ta, idx)) == NULL) { warnx("fido_bio_template"); return -1; } if (base64_encode(fido_bio_template_id_ptr(t), fido_bio_template_id_len(t), &id) < 0) { warnx("output error"); return -1; } printf("%02u: %s %s\n", (unsigned)idx, id, fido_bio_template_name(t)); free(id); return 0; } int bio_list(const char *path) { fido_bio_template_array_t *ta = NULL; fido_dev_t *dev = NULL; char *pin = NULL; int r, ok = 1; if ((ta = fido_bio_template_array_new()) == NULL) errx(1, "fido_bio_template_array_new"); dev = open_dev(path); if ((pin = get_pin(path)) == NULL) goto out; r = fido_bio_dev_get_template_array(dev, ta, pin); freezero(pin, PINBUF_LEN); pin = NULL; if (r != FIDO_OK) { warnx("fido_bio_dev_get_template_array: %s", fido_strerr(r)); goto out; } for (size_t i = 0; i < fido_bio_template_array_count(ta); i++) if (print_template(ta, i) < 0) goto out; ok = 0; out: fido_bio_template_array_free(&ta); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int bio_set_name(const char *path, const char *id, const char *name) { fido_bio_template_t *t = NULL; fido_dev_t *dev = NULL; char *pin = NULL; void *id_blob_ptr = NULL; size_t id_blob_len = 0; int r, ok = 1; 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); if ((pin = get_pin(path)) == NULL) goto out; r = fido_bio_dev_set_template_name(dev, t, pin); freezero(pin, PINBUF_LEN); pin = NULL; if (r != FIDO_OK) { warnx("fido_bio_dev_set_template_name: %s", fido_strerr(r)); goto out; } ok = 0; out: free(id_blob_ptr); fido_bio_template_free(&t); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } 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(const char *path) { fido_bio_template_t *t = NULL; fido_bio_enroll_t *e = NULL; fido_dev_t *dev = NULL; char *pin = NULL; int r, ok = 1; 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); if ((pin = get_pin(path)) == NULL) goto out; printf("Touch your security key.\n"); r = fido_bio_dev_enroll_begin(dev, t, e, 10000, pin); freezero(pin, PINBUF_LEN); pin = NULL; if (r != FIDO_OK) { warnx("fido_bio_dev_enroll_begin: %s", fido_strerr(r)); goto out; } 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) { fido_dev_cancel(dev); warnx("fido_bio_dev_enroll_continue: %s", fido_strerr(r)); goto out; } printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e))); } ok = 0; out: fido_bio_template_free(&t); fido_bio_enroll_free(&e); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int bio_delete(const char *path, const char *id) { fido_bio_template_t *t = NULL; fido_dev_t *dev = NULL; char *pin = NULL; void *id_blob_ptr = NULL; size_t id_blob_len = 0; int r, ok = 1; 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)); dev = open_dev(path); if ((pin = get_pin(path)) == NULL) goto out; r = fido_bio_dev_enroll_remove(dev, t, pin); freezero(pin, PINBUF_LEN); pin = NULL; if (r != FIDO_OK) { warnx("fido_bio_dev_enroll_remove: %s", fido_strerr(r)); goto out; } ok = 0; out: free(id_blob_ptr); fido_bio_template_free(&t); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } 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; if ((i = fido_bio_info_new()) == NULL) { warnx("fido_bio_info_new"); return; } if (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); } libfido2-1.10.0/tools/config.c000066400000000000000000000075731417126203300160470ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include "../openbsd-compat/openbsd-compat.h" #include "extern.h" int config_entattest(char *path) { fido_dev_t *dev; char *pin = NULL; int r, ok = 1; dev = open_dev(path); if ((r = fido_dev_enable_entattest(dev, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_dev_enable_entattest(dev, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_dev_enable_entattest: %s (0x%x)", fido_strerr(r), r); goto out; } ok = 0; out: fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int config_always_uv(char *path, int toggle) { fido_dev_t *dev; char *pin = NULL; int v, r, ok = 1; dev = open_dev(path); if (get_devopt(dev, "alwaysUv", &v) < 0) { warnx("%s: getdevopt", __func__); goto out; } if (v == -1) { warnx("%s: option not found", __func__); goto out; } if (v == toggle) { ok = 0; goto out; } if ((r = fido_dev_toggle_always_uv(dev, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_dev_toggle_always_uv(dev, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_dev_toggle_always_uv: %s (0x%x)", fido_strerr(r), r); goto out; } ok = 0; out: fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int config_pin_minlen(char *path, const char *pinlen) { fido_dev_t *dev; char *pin = NULL; int len, r, ok = 1; dev = open_dev(path); if ((len = base10(pinlen)) < 0 || len > 63) { warnx("%s: len > 63", __func__); goto out; } if ((r = fido_dev_set_pin_minlen(dev, (size_t)len, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_dev_set_pin_minlen(dev, (size_t)len, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_dev_set_pin_minlen: %s (0x%x)", fido_strerr(r), r); goto out; } ok = 0; out: fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int config_force_pin_change(char *path) { fido_dev_t *dev; char *pin = NULL; int r, ok = 1; dev = open_dev(path); if ((r = fido_dev_force_pin_change(dev, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_dev_force_pin_change(dev, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_dev_force_pin_change: %s (0x%x)", fido_strerr(r), r); goto out; } ok = 0; out: fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int config_pin_minlen_rpid(char *path, const char *rpids) { fido_dev_t *dev; char *otmp, *tmp, *cp; char *pin = NULL, **rpid = NULL; int r, ok = 1; size_t n; if ((tmp = strdup(rpids)) == NULL) err(1, "strdup"); otmp = tmp; for (n = 0; (cp = strsep(&tmp, ",")) != NULL; n++) { if (n == SIZE_MAX || (rpid = recallocarray(rpid, n, n + 1, sizeof(*rpid))) == NULL) err(1, "recallocarray"); if ((rpid[n] = strdup(cp)) == NULL) err(1, "strdup"); if (*rpid[n] == '\0') errx(1, "empty rpid"); } free(otmp); if (rpid == NULL || n == 0) errx(1, "could not parse rp_id"); dev = open_dev(path); if ((r = fido_dev_set_pin_minlen_rpid(dev, (const char * const *)rpid, n, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_dev_set_pin_minlen_rpid(dev, (const char * const *)rpid, n, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_dev_set_pin_minlen_rpid: %s (0x%x)", fido_strerr(r), r); goto out; } ok = 0; out: fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } libfido2-1.10.0/tools/cred_make.c000066400000000000000000000127221417126203300165040ustar00rootroot00000000000000/* * 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)); } if (flags & FLAG_LARGEBLOB) { if ((r = fido_cred_set_extensions(cred, FIDO_EXT_LARGEBLOB_KEY)) != 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; char *key = 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 (fido_cred_largeblob_key_ptr(cred) != NULL) r |= base64_encode(fido_cred_largeblob_key_ptr(cred), fido_cred_largeblob_key_len(cred), &key); 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); if (key != NULL) { fprintf(out_f, "%s\n", key); explicit_bzero(key, strlen(key)); } free(cdh); free(authdata); free(id); free(sig); free(x5c); free(key); } 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 cred_protect = -1; int ch; int r; while ((ch = getopt(argc, argv, "bc:dhi:o:qruv")) != -1) { switch (ch) { case 'b': flags |= FLAG_LARGEBLOB; break; case 'c': if ((cred_protect = base10(optarg)) < 0) errx(1, "-c: invalid argument '%s'", optarg); break; 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 && cose_type(argv[1], &type) < 0) 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); if (cred_protect > 0) { r = fido_cred_set_prot(cred, cred_protect); if (r != FIDO_OK) { errx(1, "fido_cred_set_prot: %s", fido_strerr(r)); } } 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); } libfido2-1.10.0/tools/cred_verify.c000066400000000000000000000100031417126203300170610ustar00rootroot00000000000000/* * 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 cred_prot = -1; int ch; int r; while ((ch = getopt(argc, argv, "c:dhi:o:v")) != -1) { switch (ch) { case 'c': if ((cred_prot = base10(optarg)) < 0) errx(1, "-c: invalid argument '%s'", optarg); break; 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 && cose_type(argv[0], &type) < 0) errx(1, "unknown type %s", argv[0]); fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0); cred = prepare_cred(in_f, type, flags); if (cred_prot > 0) { r = fido_cred_set_prot(cred, cred_prot); if (r != FIDO_OK) { errx(1, "fido_cred_set_prot: %s", fido_strerr(r)); } } 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); } libfido2-1.10.0/tools/credman.c000066400000000000000000000163611417126203300162060ustar00rootroot00000000000000/* * 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 #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 = NULL; int r, ok = 1; if ((metadata = fido_credman_metadata_new()) == NULL) { warnx("fido_credman_metadata_new"); goto out; } if ((r = fido_credman_get_dev_metadata(dev, metadata, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_credman_get_dev_metadata(dev, metadata, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_credman_get_dev_metadata: %s", fido_strerr(r)); goto out; } printf("existing rk(s): %u\n", (unsigned)fido_credman_rk_existing(metadata)); printf("remaining rk(s): %u\n", (unsigned)fido_credman_rk_remaining(metadata)); ok = 0; out: fido_credman_metadata_free(&metadata); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } static int 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) { warnx("output error"); return -1; } printf("%02u: %s %s\n", (unsigned)idx, rp_id_hash, fido_credman_rp_id(rp, idx)); free(rp_id_hash); return 0; } int credman_list_rp(const char *path) { fido_credman_rp_t *rp = NULL; fido_dev_t *dev = NULL; char *pin = NULL; int r, ok = 1; dev = open_dev(path); if ((rp = fido_credman_rp_new()) == NULL) { warnx("fido_credman_rp_new"); goto out; } if ((r = fido_credman_get_dev_rp(dev, rp, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_credman_get_dev_rp(dev, rp, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_credman_get_dev_rp: %s", fido_strerr(r)); goto out; } for (size_t i = 0; i < fido_credman_rp_count(rp); i++) if (print_rp(rp, i) < 0) goto out; ok = 0; out: fido_credman_rp_free(&rp); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } static int 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; const char *prot; if ((cred = fido_credman_rk(rk, idx)) == NULL) { warnx("fido_credman_rk"); return -1; } 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) { warnx("output error"); return -1; } type = cose_string(fido_cred_type(cred)); prot = prot_string(fido_cred_prot(cred)); printf("%02u: %s %s %s %s %s\n", (unsigned)idx, id, fido_cred_display_name(cred), user_id, type, prot); free(user_id); free(id); return 0; } int credman_list_rk(const char *path, const char *rp_id) { fido_dev_t *dev = NULL; fido_credman_rk_t *rk = NULL; char *pin = NULL; int r, ok = 1; dev = open_dev(path); if ((rk = fido_credman_rk_new()) == NULL) { warnx("fido_credman_rk_new"); goto out; } if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_credman_get_dev_rk(dev, rp_id, rk, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_credman_get_dev_rk: %s", fido_strerr(r)); goto out; } for (size_t i = 0; i < fido_credman_rk_count(rk); i++) if (print_rk(rk, i) < 0) goto out; ok = 0; out: fido_credman_rk_free(&rk); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int credman_print_rk(fido_dev_t *dev, const char *path, const char *rp_id, const char *cred_id) { fido_credman_rk_t *rk = NULL; const fido_cred_t *cred = NULL; char *pin = NULL; void *cred_id_ptr = NULL; size_t cred_id_len = 0; int r, ok = 1; if ((rk = fido_credman_rk_new()) == NULL) { warnx("fido_credman_rk_new"); goto out; } if (base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0) { warnx("base64_decode"); goto out; } if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_credman_get_dev_rk(dev, rp_id, rk, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_credman_get_dev_rk: %s", fido_strerr(r)); goto out; } 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) { warnx("output error"); goto out; } 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); ok = 0; goto out; } warnx("credential not found"); out: free(cred_id_ptr); fido_credman_rk_free(&rk); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int credman_delete_rk(const char *path, const char *id) { fido_dev_t *dev = NULL; char *pin = NULL; void *id_ptr = NULL; size_t id_len = 0; int r, ok = 1; dev = open_dev(path); if (base64_decode(id, &id_ptr, &id_len) < 0) { warnx("base64_decode"); goto out; } if ((r = fido_credman_del_dev_rk(dev, id_ptr, id_len, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_credman_del_dev_rk(dev, id_ptr, id_len, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_credman_del_dev_rk: %s", fido_strerr(r)); goto out; } ok = 0; out: free(id_ptr); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int credman_update_rk(const char *path, const char *user_id, const char *cred_id, const char *name, const char *display_name) { fido_dev_t *dev = NULL; fido_cred_t *cred = NULL; char *pin = NULL; void *user_id_ptr = NULL; void *cred_id_ptr = NULL; size_t user_id_len = 0; size_t cred_id_len = 0; int r, ok = 1; dev = open_dev(path); if (base64_decode(user_id, &user_id_ptr, &user_id_len) < 0 || base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0) { warnx("base64_decode"); goto out; } if ((cred = fido_cred_new()) == NULL) { warnx("fido_cred_new"); goto out; } if ((r = fido_cred_set_id(cred, cred_id_ptr, cred_id_len)) != FIDO_OK) { warnx("fido_cred_set_id: %s", fido_strerr(r)); goto out; } if ((r = fido_cred_set_user(cred, user_id_ptr, user_id_len, name, display_name, NULL)) != FIDO_OK) { warnx("fido_cred_set_user: %s", fido_strerr(r)); goto out; } if ((r = fido_credman_set_dev_rk(dev, cred, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_credman_set_dev_rk(dev, cred, pin); freezero(pin, PINBUF_LEN); pin = NULL; } if (r != FIDO_OK) { warnx("fido_credman_set_dev_rk: %s", fido_strerr(r)); goto out; } ok = 0; out: free(user_id_ptr); free(cred_id_ptr); fido_dev_close(dev); fido_dev_free(&dev); fido_cred_free(&cred); exit(ok); } libfido2-1.10.0/tools/extern.h000066400000000000000000000061431417126203300161040ustar00rootroot00000000000000/* * 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 #include #include struct blob { unsigned char *ptr; size_t len; }; #define TOKEN_OPT "CDGILPRSVabcdefi:k:l:m:n:p:ru" #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 #define FLAG_LARGEBLOB 0x80 #define PINBUF_LEN 256 EC_KEY *read_ec_pubkey(const char *); fido_dev_t *open_dev(const char *); FILE *open_read(const char *); FILE *open_write(const char *); char *get_pin(const char *); const char *plural(size_t); const char *cose_string(int); const char *prot_string(int); int assert_get(int, char **); int assert_verify(int, char **); int base64_decode(const char *, void **, size_t *); int base64_encode(const void *, size_t, char **); int base64_read(FILE *, struct blob *); int bio_delete(const char *, const char *); int bio_enroll(const char *); void bio_info(fido_dev_t *); int bio_list(const char *); int bio_set_name(const char *, const char *, const char *); int blob_clean(const char *); int blob_list(const char *); int blob_delete(const char *, const char *, const char *, const char *); int blob_get(const char *, const char *, const char *, const char *, const char *); int blob_set(const char *, const char *, const char *, const char *, const char *); int config_always_uv(char *, int); int config_entattest(char *); int config_force_pin_change(char *); int config_pin_minlen(char *, const char *); int config_pin_minlen_rpid(char *, const char *); int cose_type(const char *, int *); int cred_make(int, char **); int cred_verify(int, char **); int credman_delete_rk(const char *, const char *); int credman_update_rk(const char *, const char *, const char *, const char *, const char *); int credman_get_metadata(fido_dev_t *, const char *); int credman_list_rk(const char *, const char *); int credman_list_rp(const char *); int credman_print_rk(fido_dev_t *, const char *, const char *, const char *); int get_devopt(fido_dev_t *, const char *, int *); int pin_change(char *); int pin_set(char *); int should_retry_with_pin(const fido_dev_t *, int); int string_read(FILE *, char **); int token_config(int, char **, char *); int token_delete(int, char **, char *); int token_get(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); int read_file(const char *, u_char **, size_t *); int write_file(const char *, const u_char *, 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 usage(void); void xxd(const void *, size_t); int base10(const char *); #endif /* _EXTERN_H_ */ libfido2-1.10.0/tools/fido2-assert.c000066400000000000000000000022251417126203300170710ustar00rootroot00000000000000/* * 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 [-bdhpruv] [-t option] [-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 */ } libfido2-1.10.0/tools/fido2-attach.sh000077500000000000000000000004461417126203300172320ustar00rootroot00000000000000#!/bin/sh # Copyright (c) 2020 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. DEV="" while [ -z "${DEV}" ]; do sleep .5 DEV="$(fido2-token -L | sed 's/^\(.*\): .*$/\1/;q')" done printf '%s\n' "${DEV}" libfido2-1.10.0/tools/fido2-cred.c000066400000000000000000000021521417126203300165040ustar00rootroot00000000000000/* * 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 [-bdhqruv] [-c cred_protect] [-i input_file] [-o output_file] device [type]\n" " fido2-cred -V [-dhv] [-c cred_protect] [-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 */ } libfido2-1.10.0/tools/fido2-detach.sh000077500000000000000000000004741417126203300172170ustar00rootroot00000000000000#!/bin/sh # Copyright (c) 2020 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. DEV="$(fido2-token -L | sed 's/^\(.*\): .*$/\1/;q')" while [ -n "${DEV}" ]; do sleep .5 DEV="$(fido2-token -L | sed 's/^\(.*\): .*$/\1/;q')" done libfido2-1.10.0/tools/fido2-token.c000066400000000000000000000042271417126203300167140ustar00rootroot00000000000000/* * 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 "../openbsd-compat/openbsd-compat.h" #include "extern.h" static int action; void usage(void) { fprintf(stderr, "usage: fido2-token -C [-d] device\n" " fido2-token -Db [-k key_path] [-i cred_id -n rp_id] device\n" " fido2-token -Dei template_id device\n" " fido2-token -Du device\n" " fido2-token -Gb [-k key_path] [-i cred_id -n rp_id] blob_path device\n" " fido2-token -I [-cd] [-k rp_id -i cred_id] device\n" " fido2-token -L [-bder] [-k rp_id] [device]\n" " fido2-token -R [-d] device\n" " fido2-token -S [-adefu] [-l pin_length] [-i template_id -n template_name] device\n" " fido2-token -Sb [-k key_path] [-i cred_id -n rp_id] blob_path device\n" " fido2-token -Sc -i cred_id -k user_id -n name -p display_name device\n" " fido2-token -Sm rp_id 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 'a': case 'b': case 'c': case 'e': case 'f': case 'i': case 'k': case 'l': case 'm': case 'n': case 'p': case 'r': case 'u': break; /* ignore */ case 'd': flags = FIDO_DEBUG; break; default: setaction(ch); break; } } if (argc - optind < 1) device = NULL; else device = argv[argc - 1]; fido_init(flags); switch (action) { case 'C': return (pin_change(device)); case 'D': return (token_delete(argc, argv, device)); case 'G': return (token_get(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 */ } libfido2-1.10.0/tools/fido2-unprot.sh000077500000000000000000000055531417126203300173210ustar00rootroot00000000000000#!/bin/sh # Copyright (c) 2020 Fabian Henneke. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. if [ $(uname) != "Linux" ] ; then echo "Can only run on Linux" exit 1 fi TOKEN_VERSION=$(${FIDO_TOOLS_PREFIX}fido2-token -V 2>&1) if [ $? -ne 0 ] ; then echo "Please install libfido2 1.5.0 or higher" exit fi TOKEN_VERSION_MAJOR=$(echo "$TOKEN_VERSION" | cut -d. -f1) TOKEN_VERSION_MINOR=$(echo "$TOKEN_VERSION" | cut -d. -f2) if [ $TOKEN_VERSION_MAJOR -eq 0 -o $TOKEN_VERSION_MAJOR -eq 1 -a $TOKEN_VERSION_MINOR -lt 5 ] ; then echo "Please install libfido2 1.5.0 or higher (current version: $TOKEN_VERSION)" exit 1 fi set -e TOKEN_OUTPUT=$(${FIDO_TOOLS_PREFIX}fido2-token -L) DEV_PATH_NAMES=$(echo "$TOKEN_OUTPUT" | sed -r 's/^(.*): .*\((.*)\)$/\1 \2/g') DEV_COUNT=$(echo "$DEV_PATH_NAMES" | wc -l) for i in $(seq 1 $DEV_COUNT) do DEV_PATH_NAME=$(echo "$DEV_PATH_NAMES" | sed "${i}q;d") DEV_PATH=$(echo "$DEV_PATH_NAME" | cut -d' ' -f1) DEV_NAME=$(echo "$DEV_PATH_NAME" | cut -d' ' -f1 --complement) DEV_PRETTY=$(echo "$DEV_NAME (at '$DEV_PATH')") if expr match "$(${FIDO_TOOLS_PREFIX}fido2-token -I $DEV_PATH)" ".* credMgmt.* clientPin.*\|.* clientPin.* credMgmt.*" > /dev/null ; then printf "Enter PIN for $DEV_PRETTY once (ignore further prompts): " stty -echo read PIN stty echo printf "\n" RESIDENT_RPS=$(echo "${PIN}\n" | setsid -w ${FIDO_TOOLS_PREFIX}fido2-token -L -r $DEV_PATH | cut -d' ' -f3) printf "\n" RESIDENT_RPS_COUNT=$(echo "$RESIDENT_RPS" | wc -l) FOUND=0 for j in $(seq 1 $DEV_RESIDENT_RPS_COUNT) do RESIDENT_RP=$(echo "$RESIDENT_RPS" | sed "${j}q;d") UNPROT_CREDS=$(echo "${PIN}\n" | setsid -w ${FIDO_TOOLS_PREFIX}fido2-token -L -k $RESIDENT_RP $DEV_PATH | grep ' uvopt$' | cut -d' ' -f2,3,4) printf "\n" UNPROT_CREDS_COUNT=$(echo "$UNPROT_CREDS" | wc -l) if [ $UNPROT_CREDS_COUNT -gt 0 ] ; then FOUND=1 echo "Unprotected credentials on $DEV_PRETTY for '$RESIDENT_RP':" echo "$UNPROT_CREDS" fi done if [ $FOUND -eq 0 ] ; then echo "No unprotected credentials on $DEV_PRETTY" fi else echo "$DEV_PRETTY cannot enumerate credentials" echo "Discovering unprotected SSH credentials only..." STUB_HASH=$(echo -n "" | openssl sha256 -binary | base64) printf "$STUB_HASH\nssh:\n" | ${FIDO_TOOLS_PREFIX}fido2-assert -G -r -t up=false $DEV_PATH 2> /dev/null || ASSERT_EXIT_CODE=$? if [ $ASSERT_EXIT_CODE -eq 0 ] ; then echo "Found an unprotected SSH credential on $DEV_PRETTY!" else echo "No unprotected SSH credentials (default settings) on $DEV_PRETTY" fi fi printf "\n" done libfido2-1.10.0/tools/include_check.sh000077500000000000000000000007271417126203300175470ustar00rootroot00000000000000#!/bin/sh # 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. check() { for f in $(find $1 -maxdepth 1 -name '*.h'); do echo "#include \"$f\"" | \ cc $CFLAGS -Isrc -xc -c - -o /dev/null 2>&1 echo "$f $CFLAGS $?" done } check examples check fuzz check openbsd-compat CFLAGS="${CFLAGS} -D_FIDO_INTERNAL" check src check src/fido.h check src/fido check tools libfido2-1.10.0/tools/largeblob.c000066400000000000000000000332651417126203300165300ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "../openbsd-compat/openbsd-compat.h" #include "extern.h" struct rkmap { fido_credman_rp_t *rp; /* known rps */ fido_credman_rk_t **rk; /* rk per rp */ }; static void free_rkmap(struct rkmap *map) { if (map->rp != NULL) { for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++) fido_credman_rk_free(&map->rk[i]); fido_credman_rp_free(&map->rp); } free(map->rk); } static int map_known_rps(fido_dev_t *dev, const char *path, struct rkmap *map) { const char *rp_id; char *pin = NULL; size_t n; int r, ok = -1; if ((map->rp = fido_credman_rp_new()) == NULL) { warnx("%s: fido_credman_rp_new", __func__); goto out; } if ((pin = get_pin(path)) == NULL) goto out; if ((r = fido_credman_get_dev_rp(dev, map->rp, pin)) != FIDO_OK) { warnx("fido_credman_get_dev_rp: %s", fido_strerr(r)); goto out; } if ((n = fido_credman_rp_count(map->rp)) > UINT8_MAX) { warnx("%s: fido_credman_rp_count > UINT8_MAX", __func__); goto out; } if ((map->rk = calloc(n, sizeof(*map->rk))) == NULL) { warnx("%s: calloc", __func__); goto out; } for (size_t i = 0; i < n; i++) { if ((rp_id = fido_credman_rp_id(map->rp, i)) == NULL) { warnx("%s: fido_credman_rp_id %zu", __func__, i); goto out; } if ((map->rk[i] = fido_credman_rk_new()) == NULL) { warnx("%s: fido_credman_rk_new", __func__); goto out; } if ((r = fido_credman_get_dev_rk(dev, rp_id, map->rk[i], pin)) != FIDO_OK) { warnx("%s: fido_credman_get_dev_rk %s: %s", __func__, rp_id, fido_strerr(r)); goto out; } } ok = 0; out: freezero(pin, PINBUF_LEN); return ok; } static int lookup_key(const char *path, fido_dev_t *dev, const char *rp_id, const struct blob *cred_id, char **pin, struct blob *key) { fido_credman_rk_t *rk = NULL; const fido_cred_t *cred = NULL; size_t i, n; int r, ok = -1; if ((rk = fido_credman_rk_new()) == NULL) { warnx("%s: fido_credman_rk_new", __func__); goto out; } if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin)) != FIDO_OK && *pin == NULL && should_retry_with_pin(dev, r)) { if ((*pin = get_pin(path)) == NULL) goto out; r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin); } if (r != FIDO_OK) { warnx("%s: fido_credman_get_dev_rk: %s", __func__, fido_strerr(r)); goto out; } if ((n = fido_credman_rk_count(rk)) == 0) { warnx("%s: rp id not found", __func__); goto out; } if (n == 1 && cred_id->len == 0) { /* use the credential we found */ cred = fido_credman_rk(rk, 0); } else { if (cred_id->len == 0) { warnx("%s: multiple credentials found", __func__); goto out; } for (i = 0; i < n; i++) { const fido_cred_t *x = fido_credman_rk(rk, i); if (fido_cred_id_len(x) <= cred_id->len && !memcmp(fido_cred_id_ptr(x), cred_id->ptr, fido_cred_id_len(x))) { cred = x; break; } } } if (cred == NULL) { warnx("%s: credential not found", __func__); goto out; } if (fido_cred_largeblob_key_ptr(cred) == NULL) { warnx("%s: no associated blob key", __func__); goto out; } key->len = fido_cred_largeblob_key_len(cred); if ((key->ptr = malloc(key->len)) == NULL) { warnx("%s: malloc", __func__); goto out; } memcpy(key->ptr, fido_cred_largeblob_key_ptr(cred), key->len); ok = 0; out: fido_credman_rk_free(&rk); return ok; } static int load_key(const char *keyf, const char *cred_id64, const char *rp_id, const char *path, fido_dev_t *dev, char **pin, struct blob *key) { struct blob cred_id; FILE *fp; int r; memset(&cred_id, 0, sizeof(cred_id)); if (keyf != NULL) { if (rp_id != NULL || cred_id64 != NULL) usage(); fp = open_read(keyf); if ((r = base64_read(fp, key)) < 0) warnx("%s: base64_read %s", __func__, keyf); fclose(fp); return r; } if (rp_id == NULL) usage(); if (cred_id64 != NULL && base64_decode(cred_id64, (void *)&cred_id.ptr, &cred_id.len) < 0) { warnx("%s: base64_decode %s", __func__, cred_id64); return -1; } r = lookup_key(path, dev, rp_id, &cred_id, pin, key); free(cred_id.ptr); return r; } int blob_set(const char *path, const char *keyf, const char *rp_id, const char *cred_id64, const char *blobf) { fido_dev_t *dev; struct blob key, blob; char *pin = NULL; int r, ok = 1; dev = open_dev(path); memset(&key, 0, sizeof(key)); memset(&blob, 0, sizeof(blob)); if (read_file(blobf, &blob.ptr, &blob.len) < 0 || load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0) goto out; if ((r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr, blob.len, pin)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr, blob.len, pin); } if (r != FIDO_OK) { warnx("fido_dev_largeblob_set: %s", fido_strerr(r)); goto out; } ok = 0; /* success */ out: freezero(key.ptr, key.len); freezero(blob.ptr, blob.len); freezero(pin, PINBUF_LEN); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int blob_get(const char *path, const char *keyf, const char *rp_id, const char *cred_id64, const char *blobf) { fido_dev_t *dev; struct blob key, blob; char *pin = NULL; int r, ok = 1; dev = open_dev(path); memset(&key, 0, sizeof(key)); memset(&blob, 0, sizeof(blob)); if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0) goto out; if ((r = fido_dev_largeblob_get(dev, key.ptr, key.len, &blob.ptr, &blob.len)) != FIDO_OK) { warnx("fido_dev_largeblob_get: %s", fido_strerr(r)); goto out; } if (write_file(blobf, blob.ptr, blob.len) < 0) goto out; ok = 0; /* success */ out: freezero(key.ptr, key.len); freezero(blob.ptr, blob.len); freezero(pin, PINBUF_LEN); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } int blob_delete(const char *path, const char *keyf, const char *rp_id, const char *cred_id64) { fido_dev_t *dev; struct blob key; char *pin = NULL; int r, ok = 1; dev = open_dev(path); memset(&key, 0, sizeof(key)); if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0) goto out; if ((r = fido_dev_largeblob_remove(dev, key.ptr, key.len, pin)) != FIDO_OK && should_retry_with_pin(dev, r)) { if ((pin = get_pin(path)) == NULL) goto out; r = fido_dev_largeblob_remove(dev, key.ptr, key.len, pin); } if (r != FIDO_OK) { warnx("fido_dev_largeblob_remove: %s", fido_strerr(r)); goto out; } ok = 0; /* success */ out: freezero(key.ptr, key.len); freezero(pin, PINBUF_LEN); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } static int decompress(const struct blob *plaintext, uint64_t origsiz) { struct blob inflated; u_long ilen, plen; int ok = -1; memset(&inflated, 0, sizeof(inflated)); if (plaintext->len > ULONG_MAX) return -1; if (origsiz > ULONG_MAX || origsiz > SIZE_MAX) return -1; plen = (u_long)plaintext->len; ilen = (u_long)origsiz; inflated.len = (size_t)origsiz; if ((inflated.ptr = calloc(1, inflated.len)) == NULL) return -1; if (uncompress(inflated.ptr, &ilen, plaintext->ptr, plen) != Z_OK || ilen > SIZE_MAX || (size_t)ilen != (size_t)origsiz) goto out; ok = 0; /* success */ out: freezero(inflated.ptr, inflated.len); return ok; } static int decode(const struct blob *ciphertext, const struct blob *nonce, uint64_t origsiz, const fido_cred_t *cred) { uint8_t aad[4 + sizeof(uint64_t)]; EVP_CIPHER_CTX *ctx = NULL; const EVP_CIPHER *cipher; struct blob plaintext; uint64_t tmp; int ok = -1; memset(&plaintext, 0, sizeof(plaintext)); if (nonce->len != 12) return -1; if (cred == NULL || fido_cred_largeblob_key_ptr(cred) == NULL || fido_cred_largeblob_key_len(cred) != 32) return -1; if (ciphertext->len > UINT_MAX || ciphertext->len > SIZE_MAX - 16 || ciphertext->len < 16) return -1; plaintext.len = ciphertext->len - 16; if ((plaintext.ptr = calloc(1, plaintext.len)) == NULL) return -1; if ((ctx = EVP_CIPHER_CTX_new()) == NULL || (cipher = EVP_aes_256_gcm()) == NULL || EVP_CipherInit(ctx, cipher, fido_cred_largeblob_key_ptr(cred), nonce->ptr, 0) == 0) goto out; if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, ciphertext->ptr + ciphertext->len - 16) == 0) goto out; aad[0] = 0x62; /* b */ aad[1] = 0x6c; /* l */ aad[2] = 0x6f; /* o */ aad[3] = 0x62; /* b */ tmp = htole64(origsiz); memcpy(&aad[4], &tmp, sizeof(uint64_t)); if (EVP_Cipher(ctx, NULL, aad, (u_int)sizeof(aad)) < 0 || EVP_Cipher(ctx, plaintext.ptr, ciphertext->ptr, (u_int)plaintext.len) < 0 || EVP_Cipher(ctx, NULL, NULL, 0) < 0) goto out; if (decompress(&plaintext, origsiz) < 0) goto out; ok = 0; out: freezero(plaintext.ptr, plaintext.len); if (ctx != NULL) EVP_CIPHER_CTX_free(ctx); return ok; } static const fido_cred_t * try_rp(const fido_credman_rk_t *rk, const struct blob *ciphertext, const struct blob *nonce, uint64_t origsiz) { const fido_cred_t *cred; for (size_t i = 0; i < fido_credman_rk_count(rk); i++) if ((cred = fido_credman_rk(rk, i)) != NULL && decode(ciphertext, nonce, origsiz, cred) == 0) return cred; return NULL; } static int decode_cbor_blob(struct blob *out, const cbor_item_t *item) { if (out->ptr != NULL || cbor_isa_bytestring(item) == false || cbor_bytestring_is_definite(item) == false) return -1; out->len = cbor_bytestring_length(item); if ((out->ptr = malloc(out->len)) == NULL) return -1; memcpy(out->ptr, cbor_bytestring_handle(item), out->len); return 0; } static int decode_blob_entry(const cbor_item_t *item, struct blob *ciphertext, struct blob *nonce, uint64_t *origsiz) { struct cbor_pair *v; if (item == NULL) return -1; if (cbor_isa_map(item) == false || cbor_map_is_definite(item) == false || (v = cbor_map_handle(item)) == NULL) return -1; if (cbor_map_size(item) > UINT8_MAX) return -1; for (size_t i = 0; i < cbor_map_size(item); i++) { if (cbor_isa_uint(v[i].key) == false || cbor_int_get_width(v[i].key) != CBOR_INT_8) continue; /* ignore */ switch (cbor_get_uint8(v[i].key)) { case 1: /* ciphertext */ if (decode_cbor_blob(ciphertext, v[i].value) < 0) return -1; break; case 2: /* nonce */ if (decode_cbor_blob(nonce, v[i].value) < 0) return -1; break; case 3: /* origSize */ if (*origsiz != 0 || cbor_isa_uint(v[i].value) == false || (*origsiz = cbor_get_int(v[i].value)) > SIZE_MAX) return -1; } } if (ciphertext->ptr == NULL || nonce->ptr == NULL || *origsiz == 0) return -1; return 0; } static void print_blob_entry(size_t idx, const cbor_item_t *item, const struct rkmap *map) { struct blob ciphertext, nonce; const fido_cred_t *cred = NULL; const char *rp_id = NULL; char *cred_id = NULL; uint64_t origsiz = 0; memset(&ciphertext, 0, sizeof(ciphertext)); memset(&nonce, 0, sizeof(nonce)); if (decode_blob_entry(item, &ciphertext, &nonce, &origsiz) < 0) { printf("%02zu: \n", idx); goto out; } for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++) { if ((cred = try_rp(map->rk[i], &ciphertext, &nonce, origsiz)) != NULL) { rp_id = fido_credman_rp_id(map->rp, i); break; } } if (cred == NULL) { if ((cred_id = strdup("")) == NULL) { printf("%02zu: \n", idx); goto out; } } else { if (base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &cred_id) < 0) { printf("%02zu: \n", idx); goto out; } } if (rp_id == NULL) rp_id = ""; printf("%02zu: %4zu %4zu %s %s\n", idx, ciphertext.len, (size_t)origsiz, cred_id, rp_id); out: free(ciphertext.ptr); free(nonce.ptr); free(cred_id); } static cbor_item_t * get_cbor_array(fido_dev_t *dev) { struct cbor_load_result cbor_result; cbor_item_t *item = NULL; u_char *cbor_ptr = NULL; size_t cbor_len; int r, ok = -1; if ((r = fido_dev_largeblob_get_array(dev, &cbor_ptr, &cbor_len)) != FIDO_OK) { warnx("%s: fido_dev_largeblob_get_array: %s", __func__, fido_strerr(r)); goto out; } if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) { warnx("%s: cbor_load", __func__); goto out; } if (cbor_result.read != cbor_len) { warnx("%s: cbor_result.read (%zu) != cbor_len (%zu)", __func__, cbor_result.read, cbor_len); /* continue */ } if (cbor_isa_array(item) == false || cbor_array_is_definite(item) == false) { warnx("%s: cbor type", __func__); goto out; } if (cbor_array_size(item) > UINT8_MAX) { warnx("%s: cbor_array_size > UINT8_MAX", __func__); goto out; } if (cbor_array_size(item) == 0) { ok = 0; /* nothing to do */ goto out; } printf("total map size: %zu byte%s\n", cbor_len, plural(cbor_len)); ok = 0; out: if (ok < 0 && item != NULL) { cbor_decref(&item); item = NULL; } free(cbor_ptr); return item; } int blob_list(const char *path) { struct rkmap map; fido_dev_t *dev = NULL; cbor_item_t *item = NULL, **v; int ok = 1; memset(&map, 0, sizeof(map)); dev = open_dev(path); if (map_known_rps(dev, path, &map) < 0 || (item = get_cbor_array(dev)) == NULL) goto out; if (cbor_array_size(item) == 0) { ok = 0; /* nothing to do */ goto out; } if ((v = cbor_array_handle(item)) == NULL) { warnx("%s: cbor_array_handle", __func__); goto out; } for (size_t i = 0; i < cbor_array_size(item); i++) print_blob_entry(i, v[i], &map); ok = 0; /* success */ out: free_rkmap(&map); if (item != NULL) cbor_decref(&item); fido_dev_close(dev); fido_dev_free(&dev); exit(ok); } libfido2-1.10.0/tools/pin.c000066400000000000000000000054501417126203300153600ustar00rootroot00000000000000/* * 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; 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); } libfido2-1.10.0/tools/test.sh000077500000000000000000000352161417126203300157470ustar00rootroot00000000000000#!/bin/sh -ex # Copyright (c) 2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # usage: ./test.sh "$(mktemp -d fido2test-XXXXXXXX)" device # Please note that this test script: # - is incomplete; # - assumes CTAP 2.1-like hmac-secret; # - should pass as-is on a YubiKey with a PIN set; # - may otherwise require set +e above; # - can be executed with UV=1 to run additional UV tests; # - was last tested on 2022-01-11 with firmware 5.4.3. cd "$1" DEV="$2" make_cred() { sed /^$/d > cred_param << EOF $(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64) $1 some user name $(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64) EOF fido2-cred -M $2 "${DEV}" > "$3" < cred_param } verify_cred() { fido2-cred -V $1 > cred_out < "$2" head -1 cred_out > "$3" tail -n +2 cred_out > "$4" } get_assert() { sed /^$/d > assert_param << EOF $(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64) $1 $(cat $3) $(cat $4) EOF fido2-assert -G $2 "${DEV}" > "$5" < assert_param } verify_assert() { fido2-assert -V $1 "$2" < "$3" } dd if=/dev/urandom bs=32 count=1 | base64 > hmac-salt # u2f make_cred no.tld "-u" u2f ! make_cred no.tld "-ru" /dev/null ! make_cred no.tld "-uc1" /dev/null ! make_cred no.tld "-uc2" /dev/null verify_cred "--" u2f u2f-cred u2f-pubkey ! verify_cred "-h" u2f /dev/null /dev/null ! verify_cred "-v" u2f /dev/null /dev/null verify_cred "-c0" u2f /dev/null /dev/null ! verify_cred "-c1" u2f /dev/null /dev/null ! verify_cred "-c2" u2f /dev/null /dev/null ! verify_cred "-c3" u2f /dev/null /dev/null # wrap (non-resident) make_cred no.tld "--" wrap verify_cred "--" wrap wrap-cred wrap-pubkey ! verify_cred "-h" wrap /dev/null /dev/null ! verify_cred "-v" wrap /dev/null /dev/null verify_cred "-c0" wrap /dev/null /dev/null ! verify_cred "-c1" wrap /dev/null /dev/null ! verify_cred "-c2" wrap /dev/null /dev/null ! verify_cred "-c3" wrap /dev/null /dev/null # wrap (non-resident) + hmac-secret make_cred no.tld "-h" wrap-hs ! verify_cred "--" wrap-hs /dev/null /dev/null verify_cred "-h" wrap-hs wrap-hs-cred wrap-hs-pubkey ! verify_cred "-v" wrap-hs /dev/null /dev/null verify_cred "-hc0" wrap-hs /dev/null /dev/null ! verify_cred "-c0" wrap-hs /dev/null /dev/null ! verify_cred "-c1" wrap-hs /dev/null /dev/null ! verify_cred "-c2" wrap-hs /dev/null /dev/null ! verify_cred "-c3" wrap-hs /dev/null /dev/null # resident make_cred no.tld "-r" rk verify_cred "--" rk rk-cred rk-pubkey ! verify_cred "-h" rk /dev/null /dev/null ! verify_cred "-v" rk /dev/null /dev/null verify_cred "-c0" rk /dev/null /dev/null ! verify_cred "-c1" rk /dev/null /dev/null ! verify_cred "-c2" rk /dev/null /dev/null ! verify_cred "-c3" rk /dev/null /dev/null # resident + hmac-secret make_cred no.tld "-hr" rk-hs ! verify_cred "--" rk-hs rk-hs-cred rk-hs-pubkey verify_cred "-h" rk-hs /dev/null /dev/null ! verify_cred "-v" rk-hs /dev/null /dev/null verify_cred "-hc0" rk-hs /dev/null /dev/null ! verify_cred "-c0" rk-hs /dev/null /dev/null ! verify_cred "-c1" rk-hs /dev/null /dev/null ! verify_cred "-c2" rk-hs /dev/null /dev/null ! verify_cred "-c3" rk-hs /dev/null /dev/null # u2f get_assert no.tld "-u" u2f-cred /dev/null u2f-assert ! get_assert no.tld "-u -t up=false" u2f-cred /dev/null /dev/null verify_assert "--" u2f-pubkey u2f-assert verify_assert "-p" u2f-pubkey u2f-assert # wrap (non-resident) get_assert no.tld "--" wrap-cred /dev/null wrap-assert verify_assert "--" wrap-pubkey wrap-assert get_assert no.tld "-t pin=true" wrap-cred /dev/null wrap-assert verify_assert "--" wrap-pubkey wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t pin=false" wrap-cred /dev/null wrap-assert verify_assert "--" wrap-pubkey wrap-assert get_assert no.tld "-t up=true" wrap-cred /dev/null wrap-assert verify_assert "-p" wrap-pubkey wrap-assert get_assert no.tld "-t up=true -t pin=true" wrap-cred /dev/null wrap-assert verify_assert "--" wrap-pubkey wrap-assert verify_assert "-p" wrap-pubkey wrap-assert verify_assert "-v" wrap-pubkey wrap-assert verify_assert "-pv" wrap-pubkey wrap-assert get_assert no.tld "-t up=true -t pin=false" wrap-cred /dev/null wrap-assert verify_assert "--" wrap-pubkey wrap-assert verify_assert "-p" wrap-pubkey wrap-assert get_assert no.tld "-t up=false" wrap-cred /dev/null wrap-assert verify_assert "--" wrap-pubkey wrap-assert ! verify_assert "-p" wrap-pubkey wrap-assert get_assert no.tld "-t up=false -t pin=true" wrap-cred /dev/null wrap-assert ! verify_assert "-p" wrap-pubkey wrap-assert verify_assert "-v" wrap-pubkey wrap-assert ! verify_assert "-pv" wrap-pubkey wrap-assert get_assert no.tld "-t up=false -t pin=false" wrap-cred /dev/null wrap-assert ! verify_assert "-p" wrap-pubkey wrap-assert get_assert no.tld "-h" wrap-cred hmac-salt wrap-assert ! verify_assert "--" wrap-pubkey wrap-assert verify_assert "-h" wrap-pubkey wrap-assert get_assert no.tld "-h -t pin=true" wrap-cred hmac-salt wrap-assert ! verify_assert "--" wrap-pubkey wrap-assert verify_assert "-h" wrap-pubkey wrap-assert verify_assert "-hv" wrap-pubkey wrap-assert get_assert no.tld "-h -t pin=false" wrap-cred hmac-salt wrap-assert ! verify_assert "--" wrap-pubkey wrap-assert verify_assert "-h" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true" wrap-cred hmac-salt wrap-assert ! verify_assert "--" wrap-pubkey wrap-assert verify_assert "-h" wrap-pubkey wrap-assert verify_assert "-hp" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true -t pin=true" wrap-cred hmac-salt wrap-assert ! verify_assert "--" wrap-pubkey wrap-assert verify_assert "-h" wrap-pubkey wrap-assert verify_assert "-hp" wrap-pubkey wrap-assert verify_assert "-hv" wrap-pubkey wrap-assert verify_assert "-hpv" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true -t pin=false" wrap-cred hmac-salt wrap-assert ! verify_assert "--" wrap-pubkey wrap-assert verify_assert "-h" wrap-pubkey wrap-assert verify_assert "-hp" wrap-pubkey wrap-assert ! get_assert no.tld "-h -t up=false" wrap-cred hmac-salt wrap-assert ! get_assert no.tld "-h -t up=false -t pin=true" wrap-cred hmac-salt wrap-assert ! get_assert no.tld "-h -t up=false -t pin=false" wrap-cred hmac-salt wrap-assert if [ "x${UV}" != "x" ]; then get_assert no.tld "-t uv=true" wrap-cred /dev/null wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t uv=true -t pin=true" wrap-cred /dev/null wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t uv=true -t pin=false" wrap-cred /dev/null wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t uv=false" wrap-cred /dev/null wrap-assert verify_assert "--" wrap-pubkey wrap-assert get_assert no.tld "-t uv=false -t pin=true" wrap-cred /dev/null wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t uv=false -t pin=false" wrap-cred /dev/null wrap-assert verify_assert "--" wrap-pubkey wrap-assert get_assert no.tld "-t up=true -t uv=true" wrap-cred /dev/null wrap-assert verify_assert "-pv" wrap-pubkey wrap-assert get_assert no.tld "-t up=true -t uv=true -t pin=true" wrap-cred /dev/null wrap-assert verify_assert "-pv" wrap-pubkey wrap-assert get_assert no.tld "-t up=true -t uv=true -t pin=false" wrap-cred /dev/null wrap-assert verify_assert "-pv" wrap-pubkey wrap-assert get_assert no.tld "-t up=true -t uv=false" wrap-cred /dev/null wrap-assert verify_assert "-p" wrap-pubkey wrap-assert get_assert no.tld "-t up=true -t uv=false -t pin=true" wrap-cred /dev/null wrap-assert verify_assert "-pv" wrap-pubkey wrap-assert get_assert no.tld "-t up=true -t uv=false -t pin=false" wrap-cred /dev/null wrap-assert verify_assert "-p" wrap-pubkey wrap-assert get_assert no.tld "-t up=false -t uv=true" wrap-cred /dev/null wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t up=false -t uv=true -t pin=true" wrap-cred /dev/null wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t up=false -t uv=true -t pin=false" wrap-cred /dev/null wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t up=false -t uv=false" wrap-cred /dev/null wrap-assert ! verify_assert "--" wrap-pubkey wrap-assert get_assert no.tld "-t up=false -t uv=false -t pin=true" wrap-cred /dev/null wrap-assert verify_assert "-v" wrap-pubkey wrap-assert get_assert no.tld "-t up=false -t uv=false -t pin=false" wrap-cred /dev/null wrap-assert ! verify_assert "--" wrap-pubkey wrap-assert get_assert no.tld "-h -t uv=true" wrap-cred hmac-salt wrap-assert verify_assert "-hv" wrap-pubkey wrap-assert get_assert no.tld "-h -t uv=true -t pin=true" wrap-cred hmac-salt wrap-assert verify_assert "-hv" wrap-pubkey wrap-assert get_assert no.tld "-h -t uv=true -t pin=false" wrap-cred hmac-salt wrap-assert verify_assert "-hv" wrap-pubkey wrap-assert get_assert no.tld "-h -t uv=false" wrap-cred hmac-salt wrap-assert verify_assert "-h" wrap-pubkey wrap-assert get_assert no.tld "-h -t uv=false -t pin=true" wrap-cred hmac-salt wrap-assert verify_assert "-hv" wrap-pubkey wrap-assert get_assert no.tld "-h -t uv=false -t pin=false" wrap-cred hmac-salt wrap-assert verify_assert "-h" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true -t uv=true" wrap-cred hmac-salt wrap-assert verify_assert "-hpv" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true -t uv=true -t pin=true" wrap-cred hmac-salt wrap-assert verify_assert "-hpv" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true -t uv=true -t pin=false" wrap-cred hmac-salt wrap-assert verify_assert "-hpv" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true -t uv=false" wrap-cred hmac-salt wrap-assert verify_assert "-hp" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true -t uv=false -t pin=true" wrap-cred hmac-salt wrap-assert verify_assert "-hpv" wrap-pubkey wrap-assert get_assert no.tld "-h -t up=true -t uv=false -t pin=false" wrap-cred hmac-salt wrap-assert verify_assert "-hp" wrap-pubkey wrap-assert ! get_assert no.tld "-h -t up=false -t uv=true" wrap-cred hmac-salt wrap-assert ! get_assert no.tld "-h -t up=false -t uv=true -t pin=true" wrap-cred hmac-salt wrap-assert ! get_assert no.tld "-h -t up=false -t uv=true -t pin=false" wrap-cred hmac-salt wrap-assert ! get_assert no.tld "-h -t up=false -t uv=false" wrap-cred hmac-salt wrap-assert ! get_assert no.tld "-h -t up=false -t uv=false -t pin=true" wrap-cred hmac-salt wrap-assert ! get_assert no.tld "-h -t up=false -t uv=false -t pin=false" wrap-cred hmac-salt wrap-assert fi # resident get_assert no.tld "-r" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -h" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t pin=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t pin=false" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true -t pin=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true -t pin=false" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false -t pin=true" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false -t pin=false" /dev/null hmac-salt wrap-assert if [ "x${UV}" != "x" ]; then get_assert no.tld "-r -t uv=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t uv=true -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t uv=true -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t uv=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t uv=false -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t uv=false -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true -t uv=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true -t uv=true -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true -t uv=true -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true -t uv=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true -t uv=false -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=true -t uv=false -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false -t uv=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false -t uv=true -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false -t uv=true -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false -t uv=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false -t uv=false -t pin=true" /dev/null /dev/null wrap-assert get_assert no.tld "-r -t up=false -t uv=false -t pin=false" /dev/null /dev/null wrap-assert get_assert no.tld "-r -h -t uv=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t uv=true -t pin=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t uv=true -t pin=false" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t uv=false" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t uv=false -t pin=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t uv=false -t pin=false" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true -t uv=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true -t uv=true -t pin=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true -t uv=true -t pin=false" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true -t uv=false" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true -t uv=false -t pin=true" /dev/null hmac-salt wrap-assert get_assert no.tld "-r -h -t up=true -t uv=false -t pin=false" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false -t uv=true" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false -t uv=true -t pin=true" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false -t uv=true -t pin=false" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false -t uv=false" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false -t uv=false -t pin=true" /dev/null hmac-salt wrap-assert ! get_assert no.tld "-r -h -t up=false -t uv=false -t pin=false" /dev/null hmac-salt wrap-assert fi exit 0 libfido2-1.10.0/tools/token.c000066400000000000000000000250721417126203300157140ustar00rootroot00000000000000/* * 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_algorithms(const fido_cbor_info_t *ci) { const char *cose, *type; size_t len; if ((len = fido_cbor_info_algorithm_count(ci)) == 0) return; printf("algorithms: "); for (size_t i = 0; i < len; i++) { cose = type = "unknown"; switch (fido_cbor_info_algorithm_cose(ci, i)) { case COSE_EDDSA: cose = "eddsa"; break; case COSE_ES256: cose = "es256"; break; case COSE_RS256: cose = "rs256"; break; } if (fido_cbor_info_algorithm_type(ci, i) != NULL) type = fido_cbor_info_algorithm_type(ci, i); printf("%s%s (%s)", i > 0 ? ", " : "", cose, type); } printf("\n"); } 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_maxcredcntlst(uint64_t maxcredcntlst) { printf("maxcredcntlst: %d\n", (int)maxcredcntlst); } static void print_maxcredidlen(uint64_t maxcredidlen) { printf("maxcredlen: %d\n", (int)maxcredidlen); } static void print_fwversion(uint64_t fwversion) { printf("fwversion: 0x%x\n", (int)fwversion); } 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 supported transports */ print_str_array("transport", fido_cbor_info_transports_ptr(ci), fido_cbor_info_transports_len(ci)); /* print supported algorithms */ print_algorithms(ci); /* print aaguid */ print_aaguid(fido_cbor_info_aaguid_ptr(ci), fido_cbor_info_aaguid_len(ci)); /* print supported options */ print_opt_array("options", fido_cbor_info_options_name_ptr(ci), fido_cbor_info_options_value_ptr(ci), fido_cbor_info_options_len(ci)); /* print maximum message size */ print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci)); /* print maximum number of credentials allowed in credential lists */ print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci)); /* print maximum length of a credential ID */ print_maxcredidlen(fido_cbor_info_maxcredidlen(ci)); /* print firmware version */ print_fwversion(fido_cbor_info_fwversion(ci)); /* print supported pin protocols */ print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci), fido_cbor_info_protocols_len(ci)); if (fido_dev_get_retry_count(dev, &retrycnt) != FIDO_OK) printf("pin retries: undefined\n"); else printf("pin retries: %d\n", retrycnt); if (fido_dev_get_uv_retry_count(dev, &retrycnt) != FIDO_OK) printf("uv retries: undefined\n"); else printf("uv 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_get(int argc, char **argv, char *path) { char *id = NULL; char *key = NULL; char *name = NULL; int blob = 0; int ch; optind = 1; while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { switch (ch) { case 'b': blob = 1; break; case 'i': id = optarg; break; case 'k': key = optarg; break; case 'n': name = optarg; break; default: break; /* ignore */ } } argc -= optind; argv += optind; if (blob == 0 || argc != 2) usage(); return blob_get(path, key, name, id, argv[0]); } int token_set(int argc, char **argv, char *path) { char *id = NULL; char *key = NULL; char *len = NULL; char *display_name = NULL; char *name = NULL; char *rpid = NULL; int blob = 0; int cred = 0; int ch; int enroll = 0; int ea = 0; int uv = 0; bool force = false; optind = 1; while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { switch (ch) { case 'a': ea = 1; break; case 'b': blob = 1; break; case 'c': cred = 1; break; case 'e': enroll = 1; break; case 'f': force = true; break; case 'i': id = optarg; break; case 'k': key = optarg; break; case 'l': len = optarg; break; case 'p': display_name = optarg; break; case 'm': rpid = optarg; break; case 'n': name = optarg; break; case 'u': uv = 1; break; default: break; /* ignore */ } } argc -= optind; argv += optind; if (path == NULL) usage(); if (blob) { if (argc != 2) usage(); return (blob_set(path, key, name, id, argv[0])); } if (cred) { if (!id || !key) usage(); if (!name && !display_name) usage(); return (credman_update_rk(path, key, id, name, display_name)); } if (enroll) { if (ea || uv) usage(); if (id && name) return (bio_set_name(path, id, name)); if (!id && !name) return (bio_enroll(path)); usage(); } if (ea) { if (uv) usage(); return (config_entattest(path)); } if (len) return (config_pin_minlen(path, len)); if (rpid) return (config_pin_minlen_rpid(path, rpid)); if (force) return (config_force_pin_change(path)); if (uv) return (config_always_uv(path, 1)); 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 blobs = 0; 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 'b': blobs = 1; break; case 'e': enrolls = 1; break; case 'k': keys = 1; rp_id = optarg; break; case 'r': rplist = 1; break; default: break; /* ignore */ } } if (blobs || enrolls || keys || rplist) { if (path == NULL) usage(); if (blobs) return (blob_list(path)); if (enrolls) return (bio_list(path)); if (keys) return (credman_list_rk(path, rp_id)); if (rplist) return (credman_list_rp(path)); /* NOTREACHED */ } 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; char *key = NULL; char *name = NULL; int blob = 0; int ch; int enroll = 0; int uv = 0; optind = 1; while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { switch (ch) { case 'b': blob = 1; break; case 'e': enroll = 1; break; case 'i': id = optarg; break; case 'k': key = optarg; break; case 'n': name = optarg; break; case 'u': uv = 1; break; default: break; /* ignore */ } } if (path == NULL) usage(); if (blob) return (blob_delete(path, key, name, id)); if (id) { if (uv) usage(); if (enroll == 0) return (credman_delete_rk(path, id)); return (bio_delete(path, id)); } if (uv == 0) usage(); return (config_always_uv(path, 0)); } libfido2-1.10.0/tools/util.c000066400000000000000000000232061417126203300155460ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ #include #include #include #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" char * get_pin(const char *path) { char *pin; char prompt[1024]; int r, ok = -1; if ((pin = calloc(1, PINBUF_LEN)) == NULL) { warn("%s: calloc", __func__); return NULL; } if ((r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path)) < 0 || (size_t)r >= sizeof(prompt)) { warn("%s: snprintf", __func__); goto out; } if (!readpassphrase(prompt, pin, PINBUF_LEN, RPP_ECHO_OFF)) { warnx("%s: readpassphrase", __func__); goto out; } ok = 0; out: if (ok < 0) { freezero(pin, PINBUF_LEN); pin = NULL; } return pin; } 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); } int base10(const char *str) { char *ep; long long ll; 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); else if (ll < 0 || ll > INT_MAX) return (-1); return ((int)ll); } 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); } int get_devopt(fido_dev_t *dev, const char *name, int *val) { fido_cbor_info_t *cbor_info; char * const *names; const bool *values; int r, ok = -1; if ((cbor_info = fido_cbor_info_new()) == NULL) { warnx("fido_cbor_info_new"); goto out; } if ((r = fido_dev_get_cbor_info(dev, cbor_info)) != FIDO_OK) { warnx("fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r); goto out; } if ((names = fido_cbor_info_options_name_ptr(cbor_info)) == NULL || (values = fido_cbor_info_options_value_ptr(cbor_info)) == NULL) { warnx("fido_dev_get_cbor_info: NULL name/value pointer"); goto out; } *val = -1; for (size_t i = 0; i < fido_cbor_info_options_len(cbor_info); i++) if (strcmp(names[i], name) == 0) { *val = values[i]; break; } ok = 0; out: fido_cbor_info_free(&cbor_info); 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) { 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); } int cose_type(const char *str, int *type) { if (strcmp(str, "es256") == 0) *type = COSE_ES256; else if (strcmp(str, "rs256") == 0) *type = COSE_RS256; else if (strcmp(str, "eddsa") == 0) *type = COSE_EDDSA; else { *type = 0; return (-1); } return (0); } const char * cose_string(int type) { switch (type) { case COSE_EDDSA: return ("eddsa"); case COSE_ES256: return ("es256"); case COSE_RS256: return ("rs256"); default: return ("unknown"); } } const char * prot_string(int prot) { switch (prot) { case FIDO_CRED_PROT_UV_OPTIONAL: return ("uvopt"); case FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID: return ("uvopt+id"); case FIDO_CRED_PROT_UV_REQUIRED: return ("uvreq"); default: return ("unknown"); } } int read_file(const char *path, u_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("%s: open %s", __func__, path); goto fail; } if (fstat(fd, &st) < 0) { warn("%s: stat %s", __func__, path); goto fail; } if (st.st_size < 0) { warnx("%s: stat %s: invalid size", __func__, path); goto fail; } *len = (size_t)st.st_size; if ((*ptr = malloc(*len)) == NULL) { warn("%s: malloc", __func__); goto fail; } if ((n = read(fd, *ptr, *len)) < 0) { warn("%s: read", __func__); goto fail; } if ((size_t)n != *len) { warnx("%s: read", __func__); goto fail; } ok = 0; fail: if (fd != -1) { close(fd); } if (ok < 0) { free(*ptr); *ptr = NULL; *len = 0; } return ok; } int write_file(const char *path, const u_char *ptr, size_t len) { int fd, ok = -1; ssize_t n; if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) { warn("%s: open %s", __func__, path); goto fail; } if ((n = write(fd, ptr, len)) < 0) { warn("%s: write", __func__); goto fail; } if ((size_t)n != len) { warnx("%s: write", __func__); goto fail; } ok = 0; fail: if (fd != -1) { close(fd); } return ok; } const char * plural(size_t x) { return x == 1 ? "" : "s"; } int should_retry_with_pin(const fido_dev_t *dev, int r) { if (fido_dev_has_pin(dev) == false) { return 0; } switch (r) { case FIDO_ERR_PIN_REQUIRED: case FIDO_ERR_UNAUTHORIZED_PERM: case FIDO_ERR_UV_BLOCKED: case FIDO_ERR_UV_INVALID: return 1; } return 0; } libfido2-1.10.0/udev/000077500000000000000000000000001417126203300142255ustar00rootroot00000000000000libfido2-1.10.0/udev/70-u2f.rules000066400000000000000000000304031417126203300162210ustar00rootroot00000000000000# Copyright (c) 2020 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # This file is automatically generated, and should # be used with udev 188 or newer. ACTION!="add|change", GOTO="fido_end" # ellipticSecure MIRKey by STMicroelectronics KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ac", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by STMicroelectronics KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by STMicroelectronics KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="cdab", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Infineon FIDO by Infineon Technologies KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="058b", ATTRS{idProduct}=="022d", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Kensington VeriMark by Synaptics Inc. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="06cb", ATTRS{idProduct}=="0088", TAG+="uaccess", GROUP="plugdev", MODE="0660" # FS ePass FIDO by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0850", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0852", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0853", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0854", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0856", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0858", TAG+="uaccess", GROUP="plugdev", MODE="0660" # FS MultiPass FIDO U2F by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="085a", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="085b", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="085d", TAG+="uaccess", GROUP="plugdev", MODE="0660" # BioPass FIDO2 K33 by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0866", TAG+="uaccess", GROUP="plugdev", MODE="0660" # BioPass FIDO2 K43 by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0867", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Hypersecu HyperFIDO by Feitian Technologies Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0880", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey NEO FIDO by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0113", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey NEO OTP+FIDO by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0114", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey NEO FIDO+CCID by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0115", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey NEO OTP+FIDO+CCID by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0116", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Security Key by Yubico by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0120", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Unknown product by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0121", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Gnubby U2F by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0200", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey 4 FIDO by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0402", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey 4 OTP+FIDO by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0403", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey 4 FIDO+CCID by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0406", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey 4 OTP+FIDO+CCID by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0407", TAG+="uaccess", GROUP="plugdev", MODE="0660" # YubiKey Plus by Yubico AB KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0410", TAG+="uaccess", GROUP="plugdev", MODE="0660" # U2F Zero by Silicon Laboratories, Inc. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", MODE="0660" # SoloKeys SoloHacker by pid.codes KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="5070", TAG+="uaccess", GROUP="plugdev", MODE="0660" # SoloKeys SoloBoot by pid.codes KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="50b0", TAG+="uaccess", GROUP="plugdev", MODE="0660" # SatoshiLabs TREZOR by pid.codes KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Google Titan U2F by Google Inc. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="5026", TAG+="uaccess", GROUP="plugdev", MODE="0660" # VASCO SecureClick by VASCO Data Security NV KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1a44", ATTRS{idProduct}=="00bb", TAG+="uaccess", GROUP="plugdev", MODE="0660" # OnlyKey (FIDO2/U2F) by OpenMoko, Inc. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Neowave Keydo AES by NEOWAVE KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1e0d", ATTRS{idProduct}=="f1ae", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Neowave Keydo by NEOWAVE KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1e0d", ATTRS{idProduct}=="f1d0", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Thethis Key by Shenzhen Excelsecu Data Technology Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1ea8", ATTRS{idProduct}=="f025", TAG+="uaccess", GROUP="plugdev", MODE="0660" # ExcelSecu FIDO2 Security Key by Shenzhen Excelsecu Data Technology Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1ea8", ATTRS{idProduct}=="fc25", TAG+="uaccess", GROUP="plugdev", MODE="0660" # GoTrust Idem Key by NXP Semiconductors KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1fc9", ATTRS{idProduct}=="f143", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Nitrokey FIDO U2F by Clay Logic KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Nitrokey FIDO2 by Clay Logic KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b1", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Nitrokey 3C NFC by Clay Logic KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b2", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Safetech SafeKey by Clay Logic KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b3", TAG+="uaccess", GROUP="plugdev", MODE="0660" # CanoKey by Clay Logic KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42d4", TAG+="uaccess", GROUP="plugdev", MODE="0660" # JaCarta U2F by Aladdin Software Security R.D. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="24dc", ATTRS{idProduct}=="0101", TAG+="uaccess", GROUP="plugdev", MODE="0660" # JaCarta U2F by Aladdin Software Security R.D. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="24dc", ATTRS{idProduct}=="0501", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Happlink Security Key by Plug‐up KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Bluink Key by Bluink Ltd KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2abe", ATTRS{idProduct}=="1002", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Blue by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0000", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano S Old firmware by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano X Old firmware by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0004", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Blue by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0011", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Blue Legacy by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0015", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano S by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="1011", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano S Legacy by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="1015", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano X by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="4011", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano X Legacy by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="4015", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Hypersecu HyperFIDO by Hypersecu Information Systems, Inc. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2ccf", ATTRS{idProduct}=="0880", TAG+="uaccess", GROUP="plugdev", MODE="0660" # TrustKey Solutions FIDO2 G310 by eWBM Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="4a1a", TAG+="uaccess", GROUP="plugdev", MODE="0660" # TrustKey Solutions FIDO2 G320 by eWBM Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="4c2a", TAG+="uaccess", GROUP="plugdev", MODE="0660" # eWBM FIDO2 Goldengate G500 by eWBM Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="5c2f", TAG+="uaccess", GROUP="plugdev", MODE="0660" # TrustKey Solutions FIDO2 T120 by eWBM Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="a6e9", TAG+="uaccess", GROUP="plugdev", MODE="0660" # TrustKey Solutions FIDO2 T110 by eWBM Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="a7f9", TAG+="uaccess", GROUP="plugdev", MODE="0660" # eWBM FIDO2 Goldengate G450 by eWBM Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="f47c", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Idem Key by GoTrustID Inc. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="32a3", ATTRS{idProduct}=="3201", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Longmai mFIDO by Unknown vendor KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="4c4d", ATTRS{idProduct}=="f703", TAG+="uaccess", GROUP="plugdev", MODE="0660" # SatoshiLabs TREZOR by SatoshiLabs KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", TAG+="uaccess", GROUP="plugdev", MODE="0660" LABEL="fido_end" libfido2-1.10.0/udev/CMakeLists.txt000066400000000000000000000003621417126203300167660ustar00rootroot00000000000000# 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() libfido2-1.10.0/udev/check.sh000077500000000000000000000014321417126203300156410ustar00rootroot00000000000000#!/bin/sh -u # Copyright (c) 2020 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. sort_by_id() { awk '{ printf "%d\n", $3 }' | sort -Cnu } if ! grep '^vendor' "$1" | sort_by_id; then echo unsorted vendor section 1>&2 exit 1 fi VENDORS=$(grep '^vendor' "$1" | awk '{ print $2 }') PRODUCTS=$(grep '^product' "$1" | awk '{ print $2 }' | uniq) if [ "${VENDORS}" != "${PRODUCTS}" ]; then echo vendors: "$(echo "${VENDORS}" | tr '\n' ',')" 1>&2 echo products: "$(echo "${PRODUCTS}" | tr '\n' ',')" 1>&2 echo vendors and products in different order 1>&2 exit 2 fi for v in ${VENDORS}; do if ! grep "^product ${v}" "$1" | sort_by_id; then echo "${v}": unsorted product section 1>&2 exit 3 fi done libfido2-1.10.0/udev/fidodevs000066400000000000000000000101721417126203300157540ustar00rootroot00000000000000# Copyright (c) 2020 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # After modifying this file, regenerate 70-u2f.rules: # ./genrules.awk fidodevs > 70-u2f.rules # List of known vendors. Sorted by vendor ID. vendor STMICRO 0x0483 STMicroelectronics vendor INFINEON 0x058b Infineon Technologies vendor SYNAPTICS 0x06cb Synaptics Inc. vendor FEITIAN 0x096e Feitian Technologies Co., Ltd. vendor YUBICO 0x1050 Yubico AB vendor SILICON 0x10c4 Silicon Laboratories, Inc. vendor PIDCODES 0x1209 pid.codes vendor GOOGLE 0x18d1 Google Inc. vendor VASCO 0x1a44 VASCO Data Security NV vendor OPENMOKO 0x1d50 OpenMoko, Inc. vendor NEOWAVE 0x1e0d NEOWAVE vendor EXCELSECU 0x1ea8 Shenzhen Excelsecu Data Technology Co., Ltd. vendor NXP 0x1fc9 NXP Semiconductors vendor CLAYLOGIC 0x20a0 Clay Logic vendor ALLADIN 0x24dc Aladdin Software Security R.D. vendor PLUGUP 0x2581 Plug‐up vendor BLUINK 0x2abe Bluink Ltd vendor LEDGER 0x2c97 LEDGER vendor HYPERSECU 0x2ccf Hypersecu Information Systems, Inc. vendor EWBM 0x311f eWBM Co., Ltd. vendor GOTRUST 0x32a3 GoTrustID Inc. vendor UNKNOWN1 0x4c4d Unknown vendor vendor SATOSHI 0x534c SatoshiLabs # List of known products. Grouped by vendor; sorted by product ID. product STMICRO 0xa2ac ellipticSecure MIRKey product STMICRO 0xa2ca Unknown product product STMICRO 0xcdab Unknown product product INFINEON 0x022d Infineon FIDO product SYNAPTICS 0x0088 Kensington VeriMark product FEITIAN 0x0850 FS ePass FIDO product FEITIAN 0x0852 Unknown product product FEITIAN 0x0853 Unknown product product FEITIAN 0x0854 Unknown product product FEITIAN 0x0856 Unknown product product FEITIAN 0x0858 Unknown product product FEITIAN 0x085a FS MultiPass FIDO U2F product FEITIAN 0x085b Unknown product product FEITIAN 0x085d Unknown product product FEITIAN 0x0866 BioPass FIDO2 K33 product FEITIAN 0x0867 BioPass FIDO2 K43 product FEITIAN 0x0880 Hypersecu HyperFIDO product YUBICO 0x0113 YubiKey NEO FIDO product YUBICO 0x0114 YubiKey NEO OTP+FIDO product YUBICO 0x0115 YubiKey NEO FIDO+CCID product YUBICO 0x0116 YubiKey NEO OTP+FIDO+CCID product YUBICO 0x0120 Security Key by Yubico product YUBICO 0x0121 Unknown product product YUBICO 0x0200 Gnubby U2F product YUBICO 0x0402 YubiKey 4 FIDO product YUBICO 0x0403 YubiKey 4 OTP+FIDO product YUBICO 0x0406 YubiKey 4 FIDO+CCID product YUBICO 0x0407 YubiKey 4 OTP+FIDO+CCID product YUBICO 0x0410 YubiKey Plus product SILICON 0x8acf U2F Zero product PIDCODES 0x5070 SoloKeys SoloHacker product PIDCODES 0x50b0 SoloKeys SoloBoot product PIDCODES 0x53c1 SatoshiLabs TREZOR product GOOGLE 0x5026 Google Titan U2F product VASCO 0x00bb VASCO SecureClick product OPENMOKO 0x60fc OnlyKey (FIDO2/U2F) product NEOWAVE 0xf1ae Neowave Keydo AES product NEOWAVE 0xf1d0 Neowave Keydo product EXCELSECU 0xf025 Thethis Key product EXCELSECU 0xfc25 ExcelSecu FIDO2 Security Key product NXP 0xf143 GoTrust Idem Key product CLAYLOGIC 0x4287 Nitrokey FIDO U2F product CLAYLOGIC 0x42b1 Nitrokey FIDO2 product CLAYLOGIC 0x42b2 Nitrokey 3C NFC product CLAYLOGIC 0x42b3 Safetech SafeKey product CLAYLOGIC 0x42d4 CanoKey product ALLADIN 0x0101 JaCarta U2F product ALLADIN 0x0501 JaCarta U2F product PLUGUP 0xf1d0 Happlink Security Key product BLUINK 0x1002 Bluink Key product LEDGER 0x0000 Ledger Blue product LEDGER 0x0001 Ledger Nano S Old firmware product LEDGER 0x0004 Ledger Nano X Old firmware product LEDGER 0x0011 Ledger Blue product LEDGER 0x0015 Ledger Blue Legacy product LEDGER 0x1011 Ledger Nano S product LEDGER 0x1015 Ledger Nano S Legacy product LEDGER 0x4011 Ledger Nano X product LEDGER 0x4015 Ledger Nano X Legacy product HYPERSECU 0x0880 Hypersecu HyperFIDO product EWBM 0x4a1a TrustKey Solutions FIDO2 G310 product EWBM 0x4c2a TrustKey Solutions FIDO2 G320 product EWBM 0x5c2f eWBM FIDO2 Goldengate G500 product EWBM 0xa6e9 TrustKey Solutions FIDO2 T120 product EWBM 0xa7f9 TrustKey Solutions FIDO2 T110 product EWBM 0xf47c eWBM FIDO2 Goldengate G450 product GOTRUST 0x3201 Idem Key product UNKNOWN1 0xf703 Longmai mFIDO product SATOSHI 0x0001 SatoshiLabs TREZOR libfido2-1.10.0/udev/genrules.awk000077500000000000000000000022331417126203300165600ustar00rootroot00000000000000#!/usr/bin/awk -f # Copyright (c) 2020 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. NR == 1 { print "# Copyright (c) 2020 Yubico AB. All rights reserved." print "# Use of this source code is governed by a BSD-style" print "# license that can be found in the LICENSE file." print "" print "# This file is automatically generated, and should" print "# be used with udev 188 or newer." print "" print "ACTION!=\"add|change\", GOTO=\"fido_end\"" next } $1 == "vendor" { sub("0x", "", $3) vendors[$2, "id"] = $3 f = 4 while (f <= NF) { vendors[$2, "name"] = vendors[$2, "name"] " " $f f++ } } $1 == "product" { sub("0x", "", $3) name = "" f = 4 while (f <= NF) { name = name " " $f f++ } line = "\n#" name " by" vendors[$2, "name"]"\n" line = line"KERNEL==\"hidraw*\"" line = line", SUBSYSTEM==\"hidraw\"" line = line", ATTRS{idVendor}==\""vendors[$2, "id"]"\"" line = line", ATTRS{idProduct}==\""$3"\"" line = line", TAG+=\"uaccess\"" line = line", GROUP=\"plugdev\"" line = line", MODE=\"0660\"" print line } END { print "\nLABEL=\"fido_end\"" } libfido2-1.10.0/windows/000077500000000000000000000000001417126203300147545ustar00rootroot00000000000000libfido2-1.10.0/windows/build.ps1000066400000000000000000000162301417126203300165020ustar00rootroot00000000000000# Copyright (c) 2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. 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", [string]$WinSDK = "", [string]$Config = "Release", [string]$Arch = "x64", [string]$Type = "dynamic", [string]$Fido2Flags = "" ) $ErrorActionPreference = "Stop" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 . "$PSScriptRoot\const.ps1" Function ExitOnError() { if ($LastExitCode -ne 0) { throw "A command exited with status $LastExitCode" } } Function GitClone(${REPO}, ${BRANCH}, ${DIR}) { Write-Host "Cloning ${REPO}..." & $Git -c advice.detachedHead=false clone --quiet --depth=1 ` --branch "${BRANCH}" "${REPO}" "${DIR}" Write-Host "${REPO}'s ${BRANCH} HEAD is:" & $Git -C "${DIR}" show -s HEAD } # Find Git. $Git = $(Get-Command git -ErrorAction Ignore | ` Select-Object -ExpandProperty Source) if ([string]::IsNullOrEmpty($Git)) { $Git = $GitPath } if (-Not (Test-Path $Git)) { throw "Unable to find Git at $Git" } # Find CMake. $CMake = $(Get-Command cmake -ErrorAction Ignore | ` Select-Object -ExpandProperty Source) if ([string]::IsNullOrEmpty($CMake)) { $CMake = $CMakePath } if (-Not (Test-Path $CMake)) { throw "Unable to find CMake at $CMake" } # Find 7z. $SevenZ = $(Get-Command 7z -ErrorAction Ignore | ` Select-Object -ExpandProperty Source) if ([string]::IsNullOrEmpty($SevenZ)) { $SevenZ = $SevenZPath } if (-Not (Test-Path $SevenZ)) { throw "Unable to find 7z at $SevenZ" } # Find GPG. $GPG = $(Get-Command gpg -ErrorAction Ignore | ` Select-Object -ExpandProperty Source) if ([string]::IsNullOrEmpty($GPG)) { $GPG = $GPGPath } if (-Not (Test-Path $GPG)) { throw "Unable to find GPG at $GPG" } # Override CMAKE_SYSTEM_VERSION if $WinSDK is set. if (-Not ([string]::IsNullOrEmpty($WinSDK))) { $CMAKE_SYSTEM_VERSION = "-DCMAKE_SYSTEM_VERSION='$WinSDK'" } else { $CMAKE_SYSTEM_VERSION = '' } Write-Host "WinSDK: $WinSDK" Write-Host "Config: $Config" Write-Host "Arch: $Arch" Write-Host "Type: $Type" Write-Host "Git: $Git" Write-Host "CMake: $CMake" Write-Host "7z: $SevenZ" Write-Host "GPG: $GPG" # Create build directories. New-Item -Type Directory "${BUILD}" -Force New-Item -Type Directory "${BUILD}\${Arch}" -Force New-Item -Type Directory "${BUILD}\${Arch}\${Type}" -Force New-Item -Type Directory "${STAGE}\${LIBRESSL}" -Force New-Item -Type Directory "${STAGE}\${LIBCBOR}" -Force New-Item -Type Directory "${STAGE}\${ZLIB}" -Force # Create output directories. New-Item -Type Directory "${OUTPUT}" -Force New-Item -Type Directory "${OUTPUT}\${Arch}" -Force New-Item -Type Directory "${OUTPUT}\${Arch}\${Type}" -force # Fetch and verify dependencies. Push-Location ${BUILD} try { if (-Not (Test-Path .\${LIBRESSL})) { 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 --quiet --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})) { GitClone "${LIBCBOR_GIT}" "${LIBCBOR_BRANCH}" ".\${LIBCBOR}" } if (-Not (Test-Path .\${ZLIB})) { GitClone "${ZLIB_GIT}" "${ZLIB_BRANCH}" ".\${ZLIB}" } } catch { throw "Failed to fetch and verify dependencies" } finally { Pop-Location } # Build LibreSSL. Push-Location ${STAGE}\${LIBRESSL} try { & $CMake ..\..\..\${LIBRESSL} -A "${Arch}" ` -DBUILD_SHARED_LIBS="${SHARED}" -DLIBRESSL_TESTS=OFF ` -DCMAKE_C_FLAGS_DEBUG="${CFLAGS_DEBUG}" ` -DCMAKE_C_FLAGS_RELEASE="${CFLAGS_RELEASE}" ` -DCMAKE_INSTALL_PREFIX="${PREFIX}" "${CMAKE_SYSTEM_VERSION}"; ` ExitOnError & $CMake --build . --config ${Config} --verbose; ExitOnError & $CMake --build . --config ${Config} --target install --verbose; ` ExitOnError } catch { throw "Failed to build LibreSSL" } finally { Pop-Location } # Build libcbor. Push-Location ${STAGE}\${LIBCBOR} try { & $CMake ..\..\..\${LIBCBOR} -A "${Arch}" ` -DWITH_EXAMPLES=OFF ` -DBUILD_SHARED_LIBS="${SHARED}" ` -DCMAKE_C_FLAGS_DEBUG="${CFLAGS_DEBUG}" ` -DCMAKE_C_FLAGS_RELEASE="${CFLAGS_RELEASE}" ` -DCMAKE_INSTALL_PREFIX="${PREFIX}" "${CMAKE_SYSTEM_VERSION}"; ` ExitOnError & $CMake --build . --config ${Config} --verbose; ExitOnError & $CMake --build . --config ${Config} --target install --verbose; ` ExitOnError } catch { throw "Failed to build libcbor" } finally { Pop-Location } # Build zlib. Push-Location ${STAGE}\${ZLIB} try { & $CMake ..\..\..\${ZLIB} -A "${Arch}" ` -DBUILD_SHARED_LIBS="${SHARED}" ` -DCMAKE_C_FLAGS_DEBUG="${CFLAGS_DEBUG}" ` -DCMAKE_C_FLAGS_RELEASE="${CFLAGS_RELEASE}" ` -DCMAKE_INSTALL_PREFIX="${PREFIX}" "${CMAKE_SYSTEM_VERSION}"; ` ExitOnError & $CMake --build . --config ${Config} --verbose; ExitOnError & $CMake --build . --config ${Config} --target install --verbose; ` ExitOnError # Patch up zlib's resulting names when built with --config Debug. if ("${Config}" -eq "Debug") { if ("${Type}" -eq "Dynamic") { Copy-Item "${PREFIX}/lib/zlibd.lib" ` -Destination "${PREFIX}/lib/zlib.lib" -Force Copy-Item "${PREFIX}/bin/zlibd1.dll" ` -Destination "${PREFIX}/bin/zlib1.dll" -Force } else { Copy-Item "${PREFIX}/lib/zlibstaticd.lib" ` -Destination "${PREFIX}/lib/zlib.lib" -Force } } } catch { throw "Failed to build zlib" } finally { Pop-Location } # Build libfido2. Push-Location ${STAGE} try { & $CMake ..\..\.. -A "${Arch}" ` -DCMAKE_BUILD_TYPE="${Config}" ` -DBUILD_SHARED_LIBS="${SHARED}" ` -DCBOR_INCLUDE_DIRS="${PREFIX}\include" ` -DCBOR_LIBRARY_DIRS="${PREFIX}\lib" ` -DCBOR_BIN_DIRS="${PREFIX}\bin" ` -DZLIB_INCLUDE_DIRS="${PREFIX}\include" ` -DZLIB_LIBRARY_DIRS="${PREFIX}\lib" ` -DZLIB_BIN_DIRS="${PREFIX}\bin" ` -DCRYPTO_INCLUDE_DIRS="${PREFIX}\include" ` -DCRYPTO_LIBRARY_DIRS="${PREFIX}\lib" ` -DCRYPTO_BIN_DIRS="${PREFIX}\bin" ` -DCMAKE_C_FLAGS_DEBUG="${CFLAGS_DEBUG} ${Fido2Flags}" ` -DCMAKE_C_FLAGS_RELEASE="${CFLAGS_RELEASE} ${Fido2Flags}" ` -DCMAKE_INSTALL_PREFIX="${PREFIX}" "${CMAKE_SYSTEM_VERSION}"; ` ExitOnError & $CMake --build . --config ${Config} --verbose; ExitOnError & $CMake --build . --config ${Config} --target install --verbose; ` ExitOnError # Copy DLLs. if ("${SHARED}" -eq "ON") { "cbor.dll", "crypto-47.dll", "zlib1.dll" | ` %{ Copy-Item "${PREFIX}\bin\$_" ` -Destination "examples\${Config}" } } } catch { throw "Failed to build libfido2" } finally { Pop-Location } libfido2-1.10.0/windows/const.ps1000066400000000000000000000032621417126203300165320ustar00rootroot00000000000000# Copyright (c) 2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # LibreSSL coordinates. New-Variable -Name 'LIBRESSL_URL' ` -Value 'https://fastly.cdn.openbsd.org/pub/OpenBSD/LibreSSL' ` -Option Constant New-Variable -Name 'LIBRESSL' -Value 'libressl-3.4.2' -Option Constant # libcbor coordinates. New-Variable -Name 'LIBCBOR' -Value 'libcbor-0.9.0' -Option Constant New-Variable -Name 'LIBCBOR_BRANCH' -Value 'v0.9.0' -Option Constant New-Variable -Name 'LIBCBOR_GIT' -Value 'https://github.com/pjk/libcbor' ` -Option Constant # zlib coordinates. New-Variable -Name 'ZLIB' -Value 'zlib-1.2.11' -Option Constant New-Variable -Name 'ZLIB_BRANCH' -Value 'v1.2.11' -Option Constant New-Variable -Name 'ZLIB_GIT' -Value 'https://github.com/madler/zlib' ` -Option Constant # Work directories. New-Variable -Name 'BUILD' -Value "$PSScriptRoot\..\build" -Option Constant New-Variable -Name 'OUTPUT' -Value "$PSScriptRoot\..\output" -Option Constant # Prefixes. New-Variable -Name 'STAGE' -Value "${BUILD}\${Arch}\${Type}" -Option Constant New-Variable -Name 'PREFIX' -Value "${OUTPUT}\${Arch}\${Type}" -Option Constant # Build flags. if ("${Type}" -eq "dynamic") { New-Variable -Name 'RUNTIME' -Value '/MD' -Option Constant New-Variable -Name 'SHARED' -Value 'ON' -Option Constant } else { New-Variable -Name 'RUNTIME' -Value '/MT' -Option Constant New-Variable -Name 'SHARED' -Value 'OFF' -Option Constant } New-Variable -Name 'CFLAGS_DEBUG' -Value "${RUNTIME}d /Zi /guard:cf /sdl" ` -Option Constant New-Variable -Name 'CFLAGS_RELEASE' -Value "${RUNTIME} /Zi /guard:cf /sdl" ` -Option Constant libfido2-1.10.0/windows/cygwin.gpg000077500000000000000000000042211417126203300167550ustar00rootroot00000000000000HRon}!ze9]PM>,/l\B@7ȓzp BsI ܊i\ۓrbxݰ;Gm;ULkt g&Sa{0N ~x,Ȱh!G_&E“+_J]kO3T& EqPoa!/!gCHH,sq`Q^=ʚ"j}"s 3nզ'Sy0B{eM /u5Pk4of{cרtcff{S%kq%@f.GUwl^OꭩTerT*"Oiuh-}cE\\CK[`+@0^Ii mSA_a 7,7Cygwin ^HRo  bg`Aw}D%h0Q/N_[u<(|c^HRo  bg`Aw|3V/kV79qje[bθZL7 HRpKܩ&ꈑS:6҄+[okrMJM\͔#Ub>-/f1vIڨ?jZoF xe|AĔ-pdӼ{t;+^&S jmCv(0~;b@xCƜ9NZ=sܛ5xstjچޮ=J)+gnʔadYV*7?F>>&Tˋd8Z]8I HRp bg`Az)hX)J,!ڸCYpo BK P ^WCGрrf(vm |꾲ZC/1ITTt-̌HZ̈U>^|O<Ǫ =+HfgG"弈6f^v']ZMBo1<]) *+lCP!A`\Nex1vD{y=i!4ܨFen5ľ#*֬]wt .-D7`ـؚÄ)6o=GAE;{jIօ뭅콉 JZ9 WmrIDV,7m|_P)Wy,MMlJSM?D_ $e~Z*=8MX91X`fcmV8)As8)D(D N).;^ϝWOO\v8|>!9 fFW YBp{5WWT %:sQ lonSCygwin > (^W g   ic0tWBog[~1{n!gl?|TΎA1!&nz;8K(OAíySބ2( h iɃs_J]I1n #7<-fWU>RƧ[)c;B,ڡ"4wp 4L\pAgEʊu/`6yxUJhog_7G234 ǀy^:=,YFPU\]r?Jii1TOI6T]JĝRR- 0beqT %3Jлw~j7{! ݰՈ%ӡ"0` D{gXD̨6 e0Urp_ҡͨA+sSIT4'Ֆ}YrZHx;8qJEY*ֳ[|HFΫ ZiYHV^=h51@oTKF ^WӦ bg`A{E)Ӥt&I_q.CI-XSNlibfido2-1.10.0/windows/cygwin.ps1000077500000000000000000000036511417126203300167110ustar00rootroot00000000000000# Copyright (c) 2021 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. param( [string]$GPGPath = "C:\Program Files (x86)\GnuPG\bin\gpg.exe", [string]$Config = "Release" ) $ErrorActionPreference = "Stop" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Cygwin coordinates. $URL = 'https://www.cygwin.com' $Setup = 'setup-x86_64.exe' $Mirror = 'https://mirrors.kernel.org/sourceware/cygwin/' $Packages = 'gcc-core,pkg-config,cmake,make,libcbor-devel,libssl-devel,zlib-devel' # Work directories. $Cygwin = "$PSScriptRoot\..\cygwin" $Root = "${Cygwin}\root" # Find GPG. $GPG = $(Get-Command gpg -ErrorAction Ignore | ` Select-Object -ExpandProperty Source) if ([string]::IsNullOrEmpty($GPG)) { $GPG = $GPGPath } if (-Not (Test-Path $GPG)) { throw "Unable to find GPG at $GPG" } Write-Host "Config: $Config" Write-Host "GPG: $GPG" # Create work directories. New-Item -Type Directory "${Cygwin}" -Force New-Item -Type Directory "${Root}" -Force # Fetch and verify Cygwin. try { if (-Not (Test-Path ${Cygwin}\${Setup} -PathType leaf)) { Invoke-WebRequest ${URL}/${Setup} ` -OutFile ${Cygwin}\${Setup} } if (-Not (Test-Path ${Cygwin}\${Setup}.sig -PathType leaf)) { Invoke-WebRequest ${URL}/${Setup}.sig ` -OutFile ${Cygwin}\${Setup}.sig } & $GPG --list-keys & $GPG --quiet --no-default-keyring ` --keyring ${PSScriptRoot}/cygwin.gpg ` --verify ${Cygwin}\${Setup}.sig ${Cygwin}\${Setup} if ($LastExitCode -ne 0) { throw "GPG signature verification failed" } } catch { throw "Failed to fetch and verify Cygwin" } # Bootstrap Cygwin. Start-Process "${Cygwin}\${Setup}" -Wait -NoNewWindow ` -ArgumentList "-dnNOqW -s ${Mirror} -R ${Root} -P ${Packages}" # Build libfido2. $Env:PATH = "${Root}\bin\;" + $Env:PATH cmake "-DCMAKE_BUILD_TYPE=${Config}" -B "build-${Config}" make -C "build-${Config}" libfido2-1.10.0/windows/libressl.gpg000066400000000000000000000400511417126203300172720ustar00rootroot00000000000000 T}u=!`'=Ca5A ]`\\1z`s Fm9NYFG1:?s R/!Hj993v4VQ!Ev2oT8n\5u"}" DfǢ/ܝaWkKtFJ$PX75 q-WGټDbƤ3+!굑e*b#/(FؘR}/nVn:sО(h&-bWUW C]+KvW}G$/OHWs!"sהA^/*JkT=@:H9.5̨t O!*bRPGn&cAT=fYH{EӾ>xDj*wDLKaqR)r-j $VS5#ӹ  s iĖ;f"TŬK^ }d91 8W  Brent Cook V ÑDGZ"!" M8Vz6qӉJKAN A9|"O/O8zHu ĎfqR^zNTnT;Ah^֍r?tR6sFS~KxW5V8ccvewWf=y%^fj8f_ S1RC`{X:gj9x@-d6IJ j'&,=n`XE95EAQU [3 >y1*0f|#Ēgpp%0b߿s#m0p^JJѡO&aikgefkJ߿h.!Do 0f_FӚ|2iX(V9#lU,<8ue:+xyVL=a]$|c 9}IUv!$pvDء{9=sA`B+>/`g^_q~nBeZ[DCU[ݒ3hX|- 9`$|*K7}9g9'ZKf$<4N KkƗB4c]i#MSl.z8LՓ]Brk! fin Uʛ sf,ULC$xv|?OykuKN N":n6e6 F#h?&) V-!*Y.Y:ui:'fWm1[fdA 4VHX3=:*7H:UWEЈ !$H7w 2]EoYM{>P\`1tShln |׳ěd>EAa]1؛.Dзb Tϴ_X*z c!dU%mOrtN!Ȏ WS̋-er gu]l(:61:<-KX ,g=DѲA4 #$dTz|"@嚳xrA]s7W_& Ėa?atd P]E4m6&/ 8vυ@G'1_o''| cC| -s9 GדLMu鐈Nta?3Nmb)[OEWۉIoekIM4k'wٜRe WwD?׫VC`oE=3Ka`B2|o8BЦ9;qynEilt')+ռpBdg9CX!tj2L]oːYE6aJ|FnBƤ[FF 9\,0gײj1h_^)_)>=Hy =j΃0 t"bgϛ: ̫ JƽֵgѩvK N{D*+ !WB9>C`鴚zXFP JQ^ky1}B2* i`@#B'kp8 -~ 2:X6Q>(  XB(i  f:խm'P7~ݵ,]WV|V3WPPv 2YqK0&ex +WFgަQ9K#]`L9CmՈ,T\pRMXF  D *6I+S~-xD5x*,&4 l܇zf/ѡSDM9|No<Kzb @).L2q7xtu斝j0'&Hƛ_p,m6'31N=դKlȹ>Hl%U Wφr` u7o]]Z=hjbSTG Qy6oo,D&OZYDGv.+HdE)9*MCB0X U< ;~[8LLg6-lja*,:BX>(T} g   f:}aG/-f~\ЦsѥD +.-P&gOCV-ʆ\slTBZyƈ3҅5"Ii/U!.?eǿ,l$\h@P$:tpNw7 ;_MvQty eVD^ ㈤iR$_O[Ow\,+mc?)eC&J!*(w) H}P]|BǤ.6Cn(-7;2& (j2gb_C~ָاܔ?˵C)U\ }jvPB1e/ɰHfʹcJC ŝ`< Emd|av NbBX6B?[4ſד[ڂWdKuG y~gȸtD/53_- ؾߍZp^@ pNr<$V ʁ+hom'&~t@]+]Nk^"U?  !>+N19f:\s I f:Ֆ9M7Vt͋;;2XJ{wMR#9+!d `R B`c%8X# _zf͝B\U: <%.jȴg BPR9cp̈́0D8ˋzes&(QBܠ~'> -lAw4i^ sҳv,4 !8b}zN6v}fjziCKFd{dE^l9U^})w6f;B ڳ~ П7{OHʋ[?;\ٝ+LR'" `2쮛j5/lur?@鰑ϛI x+ppF!נޓ~{?`-\^'F7V+<)6LVp蕇f 袲rCWA5‘c<( B}o^_]KHNLY 8 2J˪<ά Z`7u`9-r?e0]^BBrent Cook V ÑDGZ"Mo*~)7á!zfMRR06{99hҨCk@ϙ40:-$c^' iNrzr O _d] q@"'tGM`oŁaMb{U0 MxBKJD!l" oR)sM?iu=՜8#ϜWr_ynQMH!Z^[|(E(?:r[HnRN:(Ba'Gnn, fHU [3 >j(mb(g5J >\u/Z-XX:(g(j7IthFbՒh |K F?Uˣoa@ igb*E9IYLfy%W9(]ji%߂,__zN &åoA'C]2WlՄ=E&7Mm}bSl;07ӕj{P"w75:)0G\-~a(0!` $D0 :0ntfPYD{db=k4fj헲<S`(bu+.Rk~`l$0M6۳w|. M0Y OXw%] TRʆo|lV e3g{Æ凓M_KfoD/OX'Fsq3@ FZ=s:[&3TxO;whУmvE| ͯ_ZWExj5Al1{GJVW}" YC'bK FI,1M)%Ul%"&D >CZ2JC!sdu:=T:@cj@VbffA:#clޞ9 W]kC1fD6/;;R#~]%yjZ$Hμ.a wqTr7p4\=4 W'&-D:"RAd,!qH~.un1o"GqlrǴg/}QMf$<%jZ;{$> q ܼ4cnS I?A/]/͠20JqLǥnC}Zʌѓi'9N֣866E2Flq{YmJ m3ZƺcQlL-,9;&ŭ, EwxG/-FE%,i2UaU (C[ƧL5$8Q(  XB(Z  f:XQp wq>E8I$pfy>|.TErq '|,G:(V8]Q59jW})doeUszR@xU>@ED/F6/F$wc-h7@f& _.8Ni]j4}O7aua}cG龗ep"l@j-zZ*d#ATxV87j=*GK޴;UR" bQz;|N@lW-F otwG|h0Ac%8# Aj,{I4fm7-Xtk?w10Nm|ZQϾiY'r50|Jd" .F3+Nϻe@> T&2_jGIx[s‚E61U0T8y4LO]u%)pIr^=?ņLL.װ.۞Ц ,M|2:" x`>(T g   f:j!_ c3*w:ͰhqԦWCS Ͳ }%NϏA}V&s1H[Ղ2)IlNwa&eF$uaвnF#R^e;뉈S h #RAmfΚ%}4Ui~]6z|x1X2bFX)QckiKtܠhWR+A?oOc9LlwiL4b1@2϶ 4ɛ&VEGѳ)4<Y=T xbU?E_Fs  MSIR'YJJ`_r zaȰma"31~$]sng0cd؅(7>2yr0p"OK32)mqŸ`& \!+ 0 hOlT.܈VW#{ch/P]/I;XJN󳛪RU?  !>+N19f:\s I f:agfmݠf$'⃔HP[ѫߞ^.=ǘ5e.U sk{P@CE9lB<#mc/3QW0=mΘ,&Y+a ^3 O>6hʇ8VVVv5|mۀQjq7s~W|ET.gW4QۃDKՙZq "*{*X=۝_#[0C{R3D V ÑDGZ"MpXCB.a!f ܲ_ X@~>nOC{+Xu/JN7rq1&xo;8I_mc3OTY_sxY|0fm\X8e Wޱ60ޗr "t7m ZJ=1~ɘXipC?(:8,Lrgn,jsʕԋN? ->'G  ScWu9F* -[{TO헭-"׫[R;`Ⱦ1{xx I~=\hI'0-Bbq`@ 9`+ x@ޖ,q:k7)gOQ~)NT؆fi{G h3#\(V=*HȧC:]7: %69d+/^eIܚ$A Y5S}r̔Zgv%Mx畤jݷvӛ8\>yQ[2%9BSK="roYdtL$G&vkO[6c;=qI?q(]^IX wTu@)́6y;p@G*${? G?q™AYc'W}" YC_e>+!ZE!#[2by wu&kL(fie[XDM/lF,GG}շhAp1qǙI?WG(X2oIXHP'`_G,K@-T J )FԗPn,&!eII 7Oߵ̸QgۥeƋy'w1١ 渄O *Q 7ɥDj"",P֣2 ks2pg|﫝EW 4MZHwb~dK\VO^멉܄N`CCˇ/\(8fV9$TC Vŷ'8Yfder&ʯӀY\/t jԩlgpaܿ ʜ ʁ %J7 /~O@ dR'=%ɿ1BO~P (^@o7#Ĝ@cCn0icrM0 uN+E>JJXuH~un'>(  XB(i  f:(>!1 &{lr&k1Up4 ?-žKFK$z|w V}Cgͤ{W? $bRVfhBªwo rDW ?3̨|nN`#ks2*=1j{,f~߷C&5L FD N^da`Z{_)aM8O[#)Wذm-nKf5] \X~,יaJw{Z H}NU^)[&ni59yE>obLl|ֳb)@s"\L&`\d,UD|c,MM6 RDuV[\^}=jʇo>MWIS*̎ڭmlI.2K:- +%x 7eA3HEu>۹]@ȔF`݂OUSQ˥cΉ>(TD g   f:S7 (JTA^jnox]ic1cI< R0aNUd8WNؽpTh3u:g[Gǔ zG.%j =EDo)'ٜYgwC'c:K )Zw ɍCMZxwAMʴ;pO;DJˍU ^a cLێ0#Uj஢Uŋh: KxGNxKS؏&$uxC6Է|Ca 00J8]`O7L2'#4exlBUA,;5itq4I>q\9h 3? utto#'ܯ=?0l8LVJdҋ4VI!JB pOI !I [A6Oa E3>~!85j_6տ I#6F0!-eU?  !>+N19f:\s I f:{L֍(60_ F0nzEj]np5Jo@MÍ-Sn\2( rMuS;Qf'pfFC^65|))Y%篢 GKힰ"ym%eUm|)K`BC֑CZY Lb'-nw6JkB`F^X<0tx)XRmk?SSٸf#nVQaUnw*he!7q{2My `[``_3k+e O|sRBȲ fbk55X9A* "$isL\ZbJ=߂b{|i#O?T`aiטR{ۿ7V ÑDGZ"5EH'C1c`/@M9EgB9XC\E?t;xɴu+F.^:GhS 5/;3gjz0Sr2oMHeLqBB4R'LDrP%;+10,Rr&kǧ(O>iE9J{\GΣ>\˜b|QSyey5V9^`|p1!Ba96ʵdg^L;,neM0;Oۃg)$دvDl%YSlҡ:ӻ-] 6y3&W5ocWL:D//nC=mU 8-e&iV,y7zI;~?7D[zhP3J"љ:9n(!5.\gc;46Hg&+2%u~,(6/̐q9Salwa& 6قHl,#꬜٩'E,lݵ_TvZL"m)o|i8,1"3%jE0f.88KuE?=,`Skd^-q};WboUw8JAKvaqW}" YC WOA;bO0U@:I7:+n4$^UBK;r搬fS[O}bS}F|fVImEN‹Yj}z;bx" %<9g܁ m%֢gXL*X)hOf rfDѦ)<\k}WvonBdmH_,FxۺQ&߯4_n0Z,_G7sdj'kK *q٨`bi 5Dt!wp-23ǁ*ӬnWgBxO uƀ^E,X>om1{6V1ͦP+s`Т^6խ;`fWw񉛖1sgoК$LJTUGB(;%NӲd_;0*Y3g8d+5#̺"$: 3J:,prN(y͉- T}   f:!s'9yrR LcZItvq֝ oѾ«G[/M#1T EնM 0_UFsjW9p+7tP+E'Zc9^R(&.~k B[HU: RY.CXaPQ3p= Pk2`0V/9q8xWGDv@`G>'9٤G\-`?Uhd,_k+*A|99,@?٠PN®fBU$-˧2'jfa?5ta_8b10D+ٰj R"|ŷ1HzqXd "H8z Są)K`Qw2%j`90Rɉ^N;oAͶL8=yI͆33   XB(i  f:[>(ZMBMzt[DD RGPl +N19f:\s I f:I!@zj!drlv $h$rξߒs0a!-YQGOR6ޥ%,'.&kG*b$`'o /&q‰!UJLp#Dn5=2r#AmNddJ\vD _,̨ ̠!ˈLKaಞ[Ŏ_BV}M]K Y#K=CI;ՙ" d-ƅC'")*F%QPooA\ܪhf5+f'SirJ!23D̉k`E o|f | PȖ6 A6m}T{up}hS_EG>dzX4jlB*FnArYgWgf/C!rL4NJ uqq8&"flb7fnYyF%r3gb~!JEYET*Q!ž)rW=+ IC2Usr {2\姦 "8 7}L(p]M;8v ƖmtPa-uRtW @BK\pQ"iwT fe&x`GVM*aVNNǯaU o4r|x/?7.G&.uz- Šh}]<& !>+N19f:\s I+ f: m£=TGvʱqXŁ},aV(Sב l<܅"xsNL/Ob˳@콾;臐8i%" / -^~W4Yx5x/%Ituၡ^Na?t(j ;m*|'ʇ^B t4#*z] gUstȜ r5/o4|ӏA&+àB"8zL@5*TgUb > UK-2zÌVm?۔{"J&k31 -`0eTS >" h*68 3d,)쮨ԅ\ɯy} ./{]zFUUVfkF)WvѦɩ"̥̾+ܪtu}d(_ATWzDmழ`_gh; KՋ,'m8*hVCy])/pqtlnҔDY TNmA$B#(׊}Re~ ^yV[o׎1=`omSvgeb-/]g\3uw w{Ւ(,Ԗ$ 4Y GVmiC`yqe&1. O P.) Zf4hE[Ьsrsdhd-(1bWhluq#7fwc X0MqZ&-2ʐHIR7Ȃ&P^.{]ǙKˤV\z{lu 3A[3"WR:r}Zdˇܵ,\ d}IU- ' j[B_u;LgU׽h>誌Kmo@Olts L@ݗ_TiICۊfzOrlу{x2V8S<-K$-dB;5ל/~YroMMXo:|ṕ'A࠙hSoQU|Z X![&!>+N19f:\s p)] TNm Kpo6y<XB VZH31*\ndkh3u<%4J /?UlTFȺDیHXrR гQS#?~T8S⣃[ig0\Ƭ 6Hgq8$m?A pNV'!"8Bf~?o)o<- x47w$OQ6Ы K*8HX:%/´[VN@ˠ٭V6rK-(Njεכiod OkVA{!^R0Ov6T#}:7)PXBqMY(Y֌X0{]䓚E8&s<#Zcu@ )q'܎Htf(aQxu}-hԻ\6G& sQܙNs:]_[Wv+)