pax_global_header00006660000000000000000000000064146325145400014516gustar00rootroot0000000000000052 comment=f87c19c9487c0131531314d9ccb475ea5325794e libfido2-1.15.0/000077500000000000000000000000001463251454000132745ustar00rootroot00000000000000libfido2-1.15.0/CMakeLists.txt000066400000000000000000000376211463251454000160450ustar00rootroot00000000000000# Copyright (c) 2018-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. # SPDX-License-Identifier: BSD-2-Clause cmake_minimum_required(VERSION 3.7) # detect AppleClang; needs to come before project() cmake_policy(SET CMP0025 NEW) project(libfido2 C) # 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 "15") set(FIDO_PATCH "0") set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH}) option(BUILD_TESTS "Build the regress tests" ON) option(BUILD_EXAMPLES "Build example programs" ON) option(BUILD_MANPAGES "Build man pages" ON) option(BUILD_SHARED_LIBS "Build a shared library" ON) option(BUILD_STATIC_LIBS "Build a static library" ON) option(BUILD_TOOLS "Build tool programs" ON) option(FUZZ "Enable fuzzing instrumentation" OFF) option(USE_HIDAPI "Use hidapi as the HID backend" OFF) option(USE_PCSC "Enable experimental PCSC support" 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(BUILD_SHARED_LIBS) set(_FIDO2_LIBRARY fido2_shared) elseif(BUILD_STATIC_LIBS) set(_FIDO2_LIBRARY fido2) else() message(FATAL_ERROR "Nothing to build (BUILD_*_LIBS=OFF)") endif() 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") OR MINGW OR CYGWIN) 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(asprintf stdio.h HAVE_ASPRINTF) 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_ASPRINTF 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 FUZZ) 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 CRYPTO_INCLUDE_DIRS) OR (NOT CRYPTO_LIBRARY_DIRS) OR (NOT ZLIB_INCLUDE_DIRS) OR (NOT ZLIB_LIBRARY_DIRS)) message(FATAL_ERROR "please define " "{CBOR,CRYPTO,ZLIB}_{INCLUDE,LIBRARY}_DIRS when " "building under msvc") endif() if(BUILD_TESTS AND BUILD_SHARED_LIBS AND ((NOT CBOR_BIN_DIRS) OR (NOT ZLIB_BIN_DIRS) OR (NOT CRYPTO_BIN_DIRS))) message(FATAL_ERROR "please define {CBOR,CRYPTO,ZLIB}_BIN_DIRS " "when building tests") endif() if(NOT CBOR_LIBRARIES) set(CBOR_LIBRARIES cbor) endif() if(NOT ZLIB_LIBRARIES) set(ZLIB_LIBRARIES zlib1) endif() if(NOT CRYPTO_LIBRARIES) set(CRYPTO_LIBRARIES crypto) endif() 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() if(NOT CBOR_LIBRARIES) set(CBOR_LIBRARIES "cbor") endif() if(NOT CRYPTO_LIBRARIES) set(CRYPTO_LIBRARIES "crypto") endif() 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(FUZZ) set(USE_PCSC ON) add_definitions(-DFIDO_FUZZ) endif() # If building with PCSC, look for pcsc-lite. if(USE_PCSC AND NOT (APPLE OR CYGWIN OR MSYS OR MINGW)) pkg_search_module(PCSC libpcsclite REQUIRED) set(PCSC_LIBRARIES pcsclite) 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(-DUSE_NFC) 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(-Wimplicit-fallthrough) add_compile_options(-pedantic) add_compile_options(-pedantic-errors) set(EXTRA_CFLAGS "-Wconversion -Wsign-conversion") 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(NOT FUZZ) set(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wframe-larger-than=2047") 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}) if(USE_PCSC) add_definitions(-DUSE_PCSC) endif() # 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(${PROJECT_SOURCE_DIR}/src) include_directories(${CBOR_INCLUDE_DIRS}) include_directories(${CRYPTO_INCLUDE_DIRS}) include_directories(${HIDAPI_INCLUDE_DIRS}) include_directories(${PCSC_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(${PCSC_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}") if(BUILD_TESTS) message(STATUS "CBOR_BIN_DIRS: ${CBOR_BIN_DIRS}") endif() 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_CROSSCOMPILING: ${CMAKE_CROSSCOMPILING}") message(STATUS "CMAKE_GENERATOR_PLATFORM: ${CMAKE_GENERATOR_PLATFORM}") message(STATUS "CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}") message(STATUS "CMAKE_HOST_SYSTEM_PROCESSOR: ${CMAKE_HOST_SYSTEM_PROCESSOR}") 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_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") 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}") if(BUILD_TESTS) message(STATUS "CRYPTO_BIN_DIRS: ${CRYPTO_BIN_DIRS}") endif() message(STATUS "CRYPTO_VERSION: ${CRYPTO_VERSION}") message(STATUS "FIDO_VERSION: ${FIDO_VERSION}") message(STATUS "FUZZ: ${FUZZ}") if(FUZZ) message(STATUS "FUZZ_LDFLAGS: ${FUZZ_LDFLAGS}") endif() message(STATUS "ZLIB_INCLUDE_DIRS: ${ZLIB_INCLUDE_DIRS}") message(STATUS "ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}") message(STATUS "ZLIB_LIBRARY_DIRS: ${ZLIB_LIBRARY_DIRS}") if(BUILD_TESTS) message(STATUS "ZLIB_BIN_DIRS: ${ZLIB_BIN_DIRS}") endif() 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 "PCSC_INCLUDE_DIRS: ${PCSC_INCLUDE_DIRS}") message(STATUS "PCSC_LIBRARIES: ${PCSC_LIBRARIES}") message(STATUS "PCSC_LIBRARY_DIRS: ${PCSC_LIBRARY_DIRS}") message(STATUS "PCSC_VERSION: ${PCSC_VERSION}") 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_PCSC: ${USE_PCSC}") message(STATUS "USE_WINHELLO: ${USE_WINHELLO}") message(STATUS "NFC_LINUX: ${NFC_LINUX}") if(BUILD_TESTS) enable_testing() endif() add_subdirectory(src) if(BUILD_TESTS) add_subdirectory(regress) endif() if(BUILD_EXAMPLES) add_subdirectory(examples) endif() if(BUILD_TOOLS) add_subdirectory(tools) endif() if(BUILD_MANPAGES AND NOT MSVC) add_subdirectory(man) endif() if(NOT WIN32) if(FUZZ) add_subdirectory(fuzz) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") add_subdirectory(udev) endif() endif() libfido2-1.15.0/LICENSE000066400000000000000000000025211463251454000143010ustar00rootroot00000000000000Copyright (c) 2018-2024 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. SPDX-License-Identifier: BSD-2-Clause libfido2-1.15.0/NEWS000066400000000000000000000232511463251454000137760ustar00rootroot00000000000000* Version 1.15.0 (2024-06-13) ** 1.15.0 will be the last release to support OpenSSL 1.1. ** bio, credman: improved CTAP 2.1 support. ** hid_osx: fix issue where fido_hid_read() may block unnecessarily; gh#757. ** fido2-token -I: print maxcredbloblen. ** hid_linux: improved support for uhid devices. ** New API calls: - fido_cred_set_attobj; - fido_cred_x5c_list_count; - fido_cred_x5c_list_len; - fido_cred_x5c_list_ptr. * Version 1.14.0 (2023-11-13) ** fido2-cred -M, fido2-token -G: support raw client data via -w flag. ** winhello: support U2F AppID extension for assertions. ** winhello: fix restrictive parsing of the hmac-secret on assertions. ** winhello: translate NTE_USER_CANCELLED to FIDO_ERR_OPERATION_DENIED; gh#685. ** New API calls: - fido_assert_authdata_raw_len; - fido_assert_authdata_raw_ptr; - fido_assert_set_winhello_appid. * Version 1.13.0 (2023-02-20) ** Support for linking against OpenSSL on Windows; gh#668. ** New API calls: - fido_assert_empty_allow_list; - fido_cred_empty_exclude_list. ** fido2-token: fix issue when listing large blobs. ** Improved support for different fuzzing engines. * Version 1.12.0 (2022-09-22) ** Support for COSE_ES384. ** Support for hidraw(4) on FreeBSD; gh#597. ** Improved support for FIDO 2.1 authenticators. ** New API calls: - es384_pk_free; - es384_pk_from_EC_KEY; - es384_pk_from_EVP_PKEY; - es384_pk_from_ptr; - es384_pk_new; - es384_pk_to_EVP_PKEY; - fido_cbor_info_certs_len; - fido_cbor_info_certs_name_ptr; - fido_cbor_info_certs_value_ptr; - fido_cbor_info_maxrpid_minpinlen; - fido_cbor_info_minpinlen; - fido_cbor_info_new_pin_required; - fido_cbor_info_rk_remaining; - fido_cbor_info_uv_attempts; - fido_cbor_info_uv_modality. ** Documentation and reliability fixes. * Version 1.11.0 (2022-05-03) ** Experimental PCSC support; enable with -DUSE_PCSC. ** Improved OpenSSL 3.0 compatibility. ** Use RFC1951 raw deflate to compress CTAP 2.1 largeBlobs. ** winhello: advertise "uv" instead of "clientPin". ** winhello: support hmac-secret in fido_dev_get_assert(). ** New API calls: - fido_cbor_info_maxlargeblob. ** Documentation and reliability fixes. ** Separate build and regress targets. * 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.15.0/README.adoc000066400000000000000000000123141463251454000150620ustar00rootroot00000000000000== 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 or NFC, 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. === 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] === Releases The current release of *libfido2* is 1.15.0. Signed release tarballs are available at Yubico's https://developers.yubico.com/libfido2/Releases[release page]. === Dependencies *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. === Installation ==== Fedora 35 and 34 $ sudo dnf install libfido2 libfido2-devel fido2-tools ==== Ubuntu 22.04 (Jammy) and 20.04 (Focal) $ sudo apt install libfido2-1 libfido2-dev libfido2-doc fido2-tools 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-1 libfido2-dev libfido2-doc fido2-tools On Linux, you may need to add a udev rule to be able to access the FIDO device. 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" ---- ==== macOS $ brew install libfido2 ==== Windows Please consult Yubico's https://developers.yubico.com/libfido2/Releases[release page] for ARM, ARM64, Win32, and Win64 artefacts. === Building 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. For complete, OS-specific build instructions, please refer to the `.actions/` (Linux, macOS, BSD) and `windows/` directories. === Build-time Customisation *libfido2* supports a number of CMake options. Some of the options require additional dependencies. Options that are disabled by default are not officially supported. [%autowidth.stretch] |=== |*Option* |*Description* |*Default* | BUILD_EXAMPLES | Build example programs | ON | BUILD_MANPAGES | Build man pages | ON | BUILD_SHARED_LIBS | Build a shared library | ON | BUILD_STATIC_LIBS | Build a static library | ON | BUILD_TOOLS | Build auxiliary tools | ON | FUZZ | Enable fuzzing instrumentation | OFF | NFC_LINUX | Enable netlink NFC support on Linux | ON | USE_HIDAPI | Use hidapi as the HID backend | OFF | USE_PCSC | Enable experimental PCSC support | OFF | USE_WINHELLO | Abstract Windows Hello as a FIDO device | ON |=== The USE_HIDAPI option requires https://github.com/libusb/hidapi[hidapi]. The USE_PCSC option requires https://github.com/LudovicRousseau/PCSC[pcsc-lite] on Linux. === Development Please use https://github.com/Yubico/libfido2/discussions[GitHub Discussions] to ask questions and suggest features, and https://github.com/Yubico/libfido2/pulls[GitHub pull-requests] for code contributions. === Reporting bugs Please use https://github.com/Yubico/libfido2/issues[GitHub Issues] to report bugs. To report security issues, 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.15.0/SECURITY.md000066400000000000000000000003071463251454000150650ustar00rootroot00000000000000# 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/issue-rating-system/. libfido2-1.15.0/examples/000077500000000000000000000000001463251454000151125ustar00rootroot00000000000000libfido2-1.15.0/examples/CMakeLists.txt000066400000000000000000000032201463251454000176470ustar00rootroot00000000000000# 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. # SPDX-License-Identifier: BSD-2-Clause 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() # 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.15.0/examples/README.adoc000066400000000000000000000071051463251454000167020ustar00rootroot00000000000000= 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 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 es256|es384|rs256|eddsa] [-k pubkey] [-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-hruv] [-c cred_protect] 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 . If the option -c is specified the the generated credential will be bound by the specified protection policy. - assert [-t es256|es384|rs256|eddsa] [-a cred_id] [-h hmac_secret] [-P pin] [-s hmac_salt] [-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 and checks whether the user presence bit was signed by the authenticator. The -v option requests user verification and checks whether the user verification bit was signed by the authenticator. 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.15.0/examples/assert.c000066400000000000000000000210421463251454000165560ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #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 es256|es384|rs256|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; es384_pk_t *es384_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_ES384: if ((ec = read_ec_pubkey(key)) == NULL) errx(1, "read_ec_pubkey"); if ((es384_pk = es384_pk_new()) == NULL) errx(1, "es384_pk_new"); if (es384_pk_from_EC_KEY(es384_pk, ec) != FIDO_OK) errx(1, "es384_pk_from_EC_KEY"); pk = es384_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); es384_pk_free(&es384_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, "es256") == 0) type = COSE_ES256; else if (strcmp(optarg, "es384") == 0) type = COSE_ES384; else if (strcmp(optarg, "rs256") == 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.15.0/examples/cred.c000066400000000000000000000211401463251454000161710ustar00rootroot00000000000000/* * Copyright (c) 2018-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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 es256|es384|rs256|eddsa] [-k pubkey] " "[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-c cred_protect] [-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, int cred_protect, 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); /* credProt */ if (cred_protect != 0 && (r = fido_cred_set_prot(cred, cred_protect)) != FIDO_OK) errx(1, "fido_cred_set_prot: %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_es256_pubkey(key_out, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)) < 0) errx(1, "write_es256_pubkey"); } else if (type == COSE_ES384) { if (write_es384_pubkey(key_out, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)) < 0) errx(1, "write_es384_pubkey"); } else if (type == COSE_RS256) { if (write_rs256_pubkey(key_out, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)) < 0) errx(1, "write_rs256_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; long long cred_protect = 0; if ((cred = fido_cred_new()) == NULL) errx(1, "fido_cred_new"); while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uvc:")) != -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 'c': if (base10(optarg, &cred_protect) < 0) errx(1, "base10: %s", optarg); if (cred_protect <= 0 || cred_protect > 3) errx(1, "-c: %s must be in (1,3)", optarg); ext |= FIDO_EXT_CRED_PROTECT; break; case 'i': id_out = optarg; break; case 'k': key_out = optarg; break; case 'r': rk = true; break; case 't': if (strcmp(optarg, "es256") == 0) type = COSE_ES256; else if (strcmp(optarg, "es384") == 0) type = COSE_ES384; else if (strcmp(optarg, "rs256") == 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); /* credProt */ if (cred_protect != 0 && (r = fido_cred_set_prot(cred, (int)cred_protect)) != FIDO_OK) errx(1, "fido_cred_set_prot: %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, fido_cred_prot(cred), 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.15.0/examples/extern.h000066400000000000000000000015571463251454000166000ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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_es256_pubkey(const char *, const void *, size_t); int write_es384_pubkey(const char *, const void *, size_t); int write_rs256_pubkey(const char *, const void *, size_t); int write_eddsa_pubkey(const char *, const void *, size_t); #endif /* _EXTERN_H_ */ libfido2-1.15.0/examples/info.c000066400000000000000000000204671463251454000162220ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 (char *, uint64_t) pairs on stdout. */ static void print_cert_array(const char *label, char * const *name, const uint64_t *value, size_t len) { if (len == 0) return; printf("%s: ", label); for (size_t i = 0; i < len; i++) printf("%s%s %llu", i > 0 ? ", " : "", name[i], (unsigned long long)value[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_ES256: cose = "es256"; break; case COSE_ES384: cose = "es384"; break; case COSE_RS256: cose = "rs256"; break; case COSE_EDDSA: cose = "eddsa"; 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 the maximum size of an authenticator's * serialized largeBlob array. */ static void print_maxlargeblob(uint64_t maxlargeblob) { printf("maxlargeblob: %d\n", (int)maxlargeblob); } /* * Auxiliary function to print the authenticator's estimated number of * remaining resident credentials. */ static void print_rk_remaining(int64_t rk_remaining) { printf("remaining rk(s): "); if (rk_remaining == -1) printf("undefined\n"); else printf("%d\n", (int)rk_remaining); } /* * Auxiliary function to print the minimum pin length observed by the * authenticator. */ static void print_minpinlen(uint64_t minpinlen) { printf("minpinlen: %d\n", (int)minpinlen); } /* * Auxiliary function to print the authenticator's preferred (platform) * UV attempts. */ static void print_uv_attempts(uint64_t uv_attempts) { printf("platform uv attempt(s): %d\n", (int)uv_attempts); } /* * 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 certifications */ print_cert_array("certifications", fido_cbor_info_certs_name_ptr(ci), fido_cbor_info_certs_value_ptr(ci), fido_cbor_info_certs_len(ci)); /* print firmware version */ print_fwversion(fido_cbor_info_fwversion(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 maximum length of largeBlob array */ print_maxlargeblob(fido_cbor_info_maxlargeblob(ci)); /* print number of remaining resident credentials */ print_rk_remaining(fido_cbor_info_rk_remaining(ci)); /* print minimum pin length */ print_minpinlen(fido_cbor_info_minpinlen(ci)); /* print supported pin protocols */ print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci), fido_cbor_info_protocols_len(ci)); /* print whether a new pin is required */ printf("pin change required: %s\n", fido_cbor_info_new_pin_required(ci) ? "true" : "false"); /* print platform uv attempts */ print_uv_attempts(fido_cbor_info_uv_attempts(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.15.0/examples/manifest.c000066400000000000000000000020031463251454000170570ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/examples/reset.c000066400000000000000000000017641463251454000164100ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ /* * 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.15.0/examples/retries.c000066400000000000000000000017571463251454000167450ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ /* * 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_dev_get_retry_count: %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.15.0/examples/select.c000066400000000000000000000111411463251454000165330ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #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((DWORD)(rqtp->tv_sec * 1000) + (DWORD)(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.15.0/examples/setpin.c000066400000000000000000000022111463251454000165540ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ /* * 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_dev_set_pin: %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.15.0/examples/util.c000066400000000000000000000156211463251454000162400ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #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_es256_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); } int write_es384_pubkey(const char *path, const void *ptr, size_t len) { FILE *fp = NULL; EVP_PKEY *pkey = NULL; es384_pk_t *pk = NULL; int fd = -1; int ok = -1; if ((pk = es384_pk_new()) == NULL) { warnx("es384_pk_new"); goto fail; } if (es384_pk_from_ptr(pk, ptr, len) != FIDO_OK) { warnx("es384_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 = es384_pk_to_EVP_PKEY(pk)) == NULL) { warnx("es384_pk_to_EVP_PKEY"); goto fail; } if (PEM_write_PUBKEY(fp, pkey) == 0) { warnx("PEM_write_PUBKEY"); goto fail; } ok = 0; fail: es384_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_rs256_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.15.0/fuzz/000077500000000000000000000000001463251454000142725ustar00rootroot00000000000000libfido2-1.15.0/fuzz/CMakeLists.txt000066400000000000000000000060021463251454000170300ustar00rootroot00000000000000# Copyright (c) 2019-2024 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # SPDX-License-Identifier: BSD-2-Clause list(APPEND COMPAT_SOURCES ../openbsd-compat/strlcpy.c ../openbsd-compat/strlcat.c ) list(APPEND COMMON_SOURCES libfuzzer.c mutator_aux.c ) # XXX: OSS-Fuzz require linking using CXX set(FUZZ_LINKER_LANGUAGE "C" CACHE STRING "Linker language for fuzz harnesses") mark_as_advanced(FUZZ_LINKER_LANGUAGE) enable_language(${FUZZ_LINKER_LANGUAGE}) # fuzz_cred add_executable(fuzz_cred fuzz_cred.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_cred PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_cred fido2_shared) # fuzz_assert add_executable(fuzz_assert fuzz_assert.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_assert PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_assert fido2_shared) # fuzz_mgmt add_executable(fuzz_mgmt fuzz_mgmt.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_mgmt PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_mgmt fido2_shared) # fuzz_credman add_executable(fuzz_credman fuzz_credman.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_credman PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_credman fido2_shared) # fuzz_bio add_executable(fuzz_bio fuzz_bio.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_bio PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_bio fido2_shared) # fuzz_hid add_executable(fuzz_hid fuzz_hid.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_hid PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_hid fido2_shared) # fuzz_netlink add_executable(fuzz_netlink fuzz_netlink.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_netlink PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_netlink fido2_shared) # fuzz_largeblob add_executable(fuzz_largeblob fuzz_largeblob.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_largeblob PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_largeblob fido2_shared) # fuzz_pcsc add_executable(fuzz_pcsc fuzz_pcsc.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_pcsc PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_pcsc fido2_shared) # fuzz_attobj add_executable(fuzz_attobj fuzz_attobj.c ${COMMON_SOURCES} ${COMPAT_SOURCES}) set_target_properties(fuzz_attobj PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS} LINKER_LANGUAGE ${FUZZ_LINKER_LANGUAGE}) target_link_libraries(fuzz_attobj fido2_shared) libfido2-1.15.0/fuzz/Dockerfile000066400000000000000000000011721463251454000162650ustar00rootroot00000000000000# Copyright (c) 2019-2023 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # SPDX-License-Identifier: BSD-2-Clause FROM alpine:latest ENV CC=clang ENV CXX=clang++ RUN apk -q update RUN apk add build-base clang clang-analyzer cmake compiler-rt coreutils RUN apk add eudev-dev git linux-headers llvm openssl-dev pcsc-lite-dev RUN apk add sudo tar zlib-dev RUN git clone --branch v0.11.0 --depth=1 https://github.com/PJK/libcbor RUN git clone --depth=1 https://github.com/yubico/libfido2 WORKDIR /libfido2 RUN ./fuzz/build-coverage /libcbor /libfido2 libfido2-1.15.0/fuzz/Makefile000066400000000000000000000056531463251454000157430ustar00rootroot00000000000000# Copyright (c) 2019-2023 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # SPDX-License-Identifier: BSD-2-Clause IMAGE := libfido2-coverage:1.15.0 RUNNER := libfido2-runner PROFDATA := llvm-profdata COV := llvm-cov TARGETS := fuzz_assert fuzz_attobj fuzz_bio fuzz_cred fuzz_credman \ fuzz_hid fuzz_largeblob fuzz_netlink fuzz_mgmt fuzz_pcsc 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=/$< \ -ignore-filename-regex=pcsclite.h --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 \ -ignore-filename-regex=pcsclite.h --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 \ -ignore-filename-regex=pcsclite.h -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=) $@ fetch-oss-fuzz: ${CORPORA} find ${TARGETS} -type f -size +8192c -print0 | xargs -0 rm fetch-franz: ssh franz tar -C corpus -cf- . | tar -xf- corpus.tgz: tar zcf $@ ${TARGETS} .PHONY: build run sync corpus ${TARGETS} ${CORPORA} .PHONY: report.tgz summary.txt functions.txt .PHONY: fetch-oss-fuzz fetch-franz corpus.tgz libfido2-1.15.0/fuzz/README000066400000000000000000000036771463251454000151670ustar00rootroot00000000000000libfido2 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 -DCMAKE_C_FLAGS=-fsanitize=fuzzer-no-link -DFUZZ_LDFLAGS=-fsanitize=fuzzer -DFUZZ=ON. If -DFUZZ=ON is enabled, symbols listed in wrapped.sym are wrapped in the resulting shared object. The wrapper functions simulate failure according to a deterministic RNG and probabilities defined in wrap.c. Harnesses wishing to use this functionality should call prng_init() with a seed obtained from the corpus. To mutate only the seed part of a libFuzzer harness's corpora, use '-reduce_inputs=0 --fido-mutate=seed'. 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. N.B., the patch below is relative to libcbor 0.10.1. diff --git src/cbor/internal/memory_utils.c src/cbor/internal/memory_utils.c index bbea63c..3f7c9af 100644 --- src/cbor/internal/memory_utils.c +++ src/cbor/internal/memory_utils.c @@ -41,7 +41,11 @@ size_t _cbor_safe_signaling_add(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.15.0/fuzz/build-coverage000077500000000000000000000021721463251454000171120ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause 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 CFLAGS="${CFLAGS} -fsanitize=fuzzer-no-link" export LDFLAGS="${CFLAGS}" export FUZZ_LDFLAGS="${LDFLAGS} -fsanitize=fuzzer" (cd "${LIBFIDO2}/build" && cmake -DFUZZ=ON -DFUZZ_LDFLAGS="${FUZZ_LDFLAGS}" \ -DCMAKE_BUILD_TYPE=Debug ..) make -C "${LIBFIDO2}/build" libfido2-1.15.0/fuzz/clock.c000066400000000000000000000034031463251454000155310ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/fuzz/dummy.h000066400000000000000000000167631463251454000156130ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 char dummy_pcsc_list[] = "reader1\0reader2\0reader3\0\0"; const char dummy_pcsc_path[] = "pcsc://slot7"; 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.15.0/fuzz/export.gnu000066400000000000000000000160071463251454000163320ustar00rootroot00000000000000{ 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; es384_pk_free; es384_pk_from_EC_KEY; es384_pk_from_EVP_PKEY; es384_pk_from_ptr; es384_pk_new; es384_pk_to_EVP_PKEY; fido_assert_allow_cred; fido_assert_authdata_len; fido_assert_authdata_ptr; fido_assert_authdata_raw_len; fido_assert_authdata_raw_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_certs_len; fido_cbor_info_certs_name_ptr; fido_cbor_info_certs_value_ptr; fido_cbor_info_extensions_len; fido_cbor_info_extensions_ptr; fido_cbor_info_free; fido_cbor_info_fwversion; fido_cbor_info_maxcredbloblen; fido_cbor_info_maxcredcntlst; fido_cbor_info_maxcredidlen; fido_cbor_info_maxlargeblob; fido_cbor_info_maxmsgsiz; fido_cbor_info_maxrpid_minpinlen; fido_cbor_info_minpinlen; fido_cbor_info_new; fido_cbor_info_new_pin_required; 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_rk_remaining; fido_cbor_info_transports_len; fido_cbor_info_transports_ptr; fido_cbor_info_uv_attempts; fido_cbor_info_uv_modality; 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_attobj; 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_list_count; fido_cred_x5c_list_len; fido_cred_x5c_list_ptr; 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_pcsc; 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_pcsc_close; fido_pcsc_manifest; fido_pcsc_open; fido_pcsc_read; fido_pcsc_rx; fido_pcsc_tx; fido_pcsc_write; 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; prng_up; fuzz_clock_reset; fuzz_save_corpus; set_netlink_io_functions; set_pcsc_parameters; set_pcsc_io_functions; set_udev_parameters; uniform_random; local: *; }; libfido2-1.15.0/fuzz/functions.txt000066400000000000000000002403761463251454000170570ustar00rootroot00000000000000File '/libfido2/src/aes256.c': Name Regions Miss Cover Lines Miss Cover -------------------------------------------------------------------------------------------------------- aes256_cbc_enc 4 0 100.00% 4 0 100.00% aes256_cbc_dec 4 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 1 96.15% 42 4 90.48% 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 52 1 98.08% 60 4 93.33% -------------------------------------------------------------------------------------------------------- TOTAL 118 3 97.46% 157 11 92.99% 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 20 1 95.00% 34 3 91.18% fido_assert_verify 50 4 92.00% 70 7 90.00% 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_set_winhello_appid 2 2 0.00% 5 5 0.00% fido_assert_allow_cred 13 2 84.62% 22 3 86.36% fido_assert_empty_allow_list 2 0 100.00% 5 0 100.00% fido_assert_set_extensions 14 0 100.00% 10 0 100.00% fido_assert_set_options 8 8 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% 13 0 100.00% fido_assert_reset_rx 4 0 100.00% 20 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_authdata_raw_ptr 4 0 100.00% 5 0 100.00% fido_assert_authdata_raw_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 28 0 100.00% 33 0 100.00% fido_assert_set_authdata_raw 28 0 100.00% 32 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 27 0 100.00% 36 0 100.00% assert.c:adjust_assert_count 24 0 100.00% 26 0 100.00% assert.c:parse_assert_reply 15 0 100.00% 28 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 23 2 91.30% 29 5 82.76% assert.c:decrypt_hmac_secrets 9 0 100.00% 15 0 100.00% assert.c:get_es256_hash 16 0 100.00% 17 0 100.00% assert.c:get_es384_hash 16 0 100.00% 17 0 100.00% assert.c:get_eddsa_hash 6 0 100.00% 9 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% 6 0 100.00% ----------------------------------------------------------------------------------------------------------------- TOTAL 628 45 92.83% 782 51 93.48% 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 14 0 100.00% 21 0 100.00% authkey.c:parse_authkey 8 0 100.00% 10 0 100.00% ----------------------------------------------------------------------------------------------------------------- TOTAL 52 0 100.00% 66 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 1 83.33% fido_bio_dev_set_template_name 7 0 100.00% 6 0 100.00% fido_bio_dev_enroll_begin 25 2 92.00% 31 1 96.77% fido_bio_dev_enroll_continue 5 2 60.00% 6 1 83.33% 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 42 0 100.00% 55 0 100.00% bio.c:bio_get_cmd 8 0 100.00% 5 0 100.00% bio.c:bio_prepare_hmac 18 0 100.00% 29 0 100.00% bio.c:bio_rx_template_array 19 0 100.00% 24 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 23 0 100.00% 31 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 19 0 100.00% 25 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 19 0 100.00% 24 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 458 20 95.63% 592 24 95.95% 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 0 100.00% 8 0 100.00% ----------------------------------------------------------------------------------------------------------------- TOTAL 8 0 100.00% 16 0 100.00% 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 21 1 95.24% 22 3 86.36% cbor_encode_pin_opt 4 0 100.00% 8 0 100.00% cbor_encode_change_pin_auth 32 1 96.88% 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 26 1 96.15% 36 2 94.44% cbor_decode_attobj 8 0 100.00% 9 0 100.00% cbor_decode_cred_authdata 31 1 96.77% 35 3 91.43% cbor_decode_assert_authdata 21 1 95.24% 32 3 90.62% 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_decode_bool 10 0 100.00% 11 0 100.00% cbor_build_uint 10 1 90.00% 9 1 88.89% cbor_array_append 17 0 100.00% 21 0 100.00% cbor_array_drop 18 0 100.00% 17 0 100.00% 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 46 0 100.00% 45 0 100.00% cbor.c:find_cose_alg 35 0 100.00% 33 0 100.00% cbor.c:decode_attobj 23 0 100.00% 37 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 41 0 100.00% 45 0 100.00% 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% 49 0 100.00% cbor.c:decode_x5c_array 9 1 88.89% 12 3 75.00% cbor.c:decode_x5c 10 1 90.00% 22 3 86.36% 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 1112 14 98.74% 1330 34 97.44% File '/libfido2/src/compress.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------ fido_compress 1 0 100.00% 3 0 100.00% fido_uncompress 6 0 100.00% 5 0 100.00% compress.c:rfc1951_deflate 33 4 87.88% 47 6 87.23% compress.c:rfc1950_inflate 27 2 92.59% 22 4 81.82% compress.c:rfc1951_inflate 38 8 78.95% 45 14 68.89% ------------------------------------------------------------------------------------------------------------------ TOTAL 105 14 86.67% 122 24 80.33% 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 39 0 100.00% 49 0 100.00% config.c:config_prepare_hmac 10 0 100.00% 21 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 112 0 100.00% 154 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 59 2 96.61% 75 4 94.67% fido_cred_verify_self 60 4 93.33% 87 7 91.95% fido_cred_new 1 0 100.00% 3 0 100.00% fido_cred_reset_tx 1 0 100.00% 18 0 100.00% fido_cred_reset_rx 1 0 100.00% 4 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 14 2 85.71% 21 3 85.71% 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_set_attobj 17 0 100.00% 21 0 100.00% fido_cred_exclude 14 2 85.71% 19 3 84.21% fido_cred_empty_exclude_list 2 0 100.00% 5 0 100.00% 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 8 8 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 0 100.00% 8 0 100.00% fido_cred_set_fmt 20 4 80.00% 12 2 83.33% fido_cred_set_type 23 2 91.30% 9 1 88.89% fido_cred_type 1 0 100.00% 3 0 100.00% fido_cred_flags 1 0 100.00% 3 0 100.00% fido_cred_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_x5c_list_count 1 0 100.00% 3 0 100.00% fido_cred_x5c_list_ptr 4 0 100.00% 5 0 100.00% fido_cred_x5c_list_len 4 0 100.00% 5 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 11 0 100.00% 21 0 100.00% fido_cred_pubkey_len 11 0 100.00% 21 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% 27 0 100.00% cred.c:verify_attstmt 28 3 89.29% 48 10 79.17% cred.c:fido_cred_clean_attobj 1 0 100.00% 6 0 100.00% 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 691 39 94.36% 911 46 94.95% 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 1 87.50% 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 1 85.71% 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 1 87.50% 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 35 0 100.00% 50 0 100.00% credman.c:credman_get_cmd 7 0 100.00% 5 0 100.00% credman.c:credman_prepare_hmac 31 1 96.77% 50 2 96.00% credman.c:credman_rx_metadata 19 0 100.00% 24 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 27 0 100.00% 35 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 23 2 91.30% 29 5 82.76% 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 27 0 100.00% 35 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 23 2 91.30% 29 5 82.76% 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 428 10 97.66% 562 20 96.44% File '/libfido2/src/dev.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_dev_info_manifest 2 0 100.00% 11 0 100.00% fido_dev_open_with_info 5 5 0.00% 6 6 0.00% fido_dev_open 13 4 69.23% 16 6 62.50% fido_dev_close 9 2 77.78% 8 1 87.50% 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_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 3 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:run_manifest 10 0 100.00% 13 0 100.00% dev.c:fido_dev_open_wait 10 0 100.00% 7 0 100.00% dev.c:fido_dev_open_tx 56 11 80.36% 56 20 64.29% 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 42 0 100.00% 25 0 100.00% dev.c:fido_dev_set_protocol_flags 11 0 100.00% 17 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 344 65 81.10% 383 80 79.11% 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 10 0 100.00% 12 0 100.00% eddsa_pk_to_EVP_PKEY 3 0 100.00% 7 0 100.00% eddsa_pk_from_EVP_PKEY 18 2 88.89% 12 1 91.67% 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 88 5 94.32% 114 9 92.11% 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 15 0 100.00% 17 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% 40 0 100.00% es256_pk_to_EVP_PKEY 42 0 100.00% 53 0 100.00% es256_pk_from_EC_KEY 42 2 95.24% 47 4 91.49% es256_pk_from_EVP_PKEY 8 0 100.00% 7 0 100.00% es256_sk_to_EVP_PKEY 28 0 100.00% 39 0 100.00% es256_derive_pk 25 0 100.00% 29 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 315 5 98.41% 372 11 97.04% File '/libfido2/src/es384.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- es384_pk_decode 8 0 100.00% 9 0 100.00% es384_pk_new 1 0 100.00% 3 0 100.00% es384_pk_free 6 0 100.00% 7 0 100.00% es384_pk_from_ptr 15 0 100.00% 17 0 100.00% es384_pk_to_EVP_PKEY 42 0 100.00% 53 0 100.00% es384_pk_from_EC_KEY 42 2 95.24% 47 4 91.49% es384_pk_from_EVP_PKEY 8 0 100.00% 7 0 100.00% es384_verify_sig 12 2 83.33% 19 5 73.68% es384_pk_verify_sig 7 1 85.71% 13 2 84.62% es384.c:decode_pubkey_point 9 0 100.00% 13 0 100.00% es384.c:decode_coord 8 0 100.00% 10 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 158 5 96.84% 198 11 94.44% File '/libfido2/src/extern.h': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- File '/libfido2/src/fallthrough.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 2 95.12% fido_hid_open 33 33 0.00% 51 51 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 38 0 100.00% 53 0 100.00% hid_linux.c:is_fido 15 1 93.33% 16 1 93.75% hid_linux.c:get_parent_attr 6 0 100.00% 9 0 100.00% hid_linux.c:parse_uevent 26 0 100.00% 29 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 202 73 63.86% 277 115 58.48% 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 11 10 9.09% 21 12 42.86% ------------------------------------------------------------------------------------------------------------------- TOTAL 29 21 27.59% 43 26 39.53% 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 4 0 100.00% 7 0 100.00% fido_cbor_info_reset 1 0 100.00% 10 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_maxlargeblob 1 0 100.00% 3 0 100.00% fido_cbor_info_fwversion 1 0 100.00% 3 0 100.00% fido_cbor_info_minpinlen 1 0 100.00% 3 0 100.00% fido_cbor_info_maxrpid_minpinlen 1 0 100.00% 3 0 100.00% fido_cbor_info_uv_attempts 1 0 100.00% 3 0 100.00% fido_cbor_info_uv_modality 1 0 100.00% 3 0 100.00% fido_cbor_info_rk_remaining 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% fido_cbor_info_new_pin_required 1 0 100.00% 3 0 100.00% fido_cbor_info_certs_name_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_certs_value_ptr 1 0 100.00% 3 0 100.00% fido_cbor_info_certs_len 1 0 100.00% 3 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 14 0 100.00% 21 0 100.00% info.c:parse_reply_element 32 0 100.00% 59 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 7 0 100.00% 15 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% info.c:decode_certs 11 0 100.00% 15 0 100.00% info.c:decode_cert 7 0 100.00% 15 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 232 0 100.00% 409 0 100.00% File '/libfido2/src/io.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_tx 14 0 100.00% 11 0 100.00% fido_rx 13 1 92.31% 14 3 78.57% fido_rx_cbor_status 16 0 100.00% 19 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 17 1 94.12% 20 1 95.00% io.c:tx_frame 16 1 93.75% 18 1 94.44% io.c:transport_rx 7 0 100.00% 10 0 100.00% io.c:rx 40 2 95.00% 52 2 96.15% 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 193 7 96.37% 230 12 94.78% 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 1 87.50% 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 1 97.37% 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 10 1 90.00% 9 1 88.89% largeblob.c:largeblob_get_tx 19 0 100.00% 24 0 100.00% largeblob.c:largeblob_get_rx 26 0 100.00% 30 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 7 76.67% 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 0 100.00% 16 0 100.00% 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 525 18 96.57% 693 43 93.80% 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 1 87.50% fido_log_xxd 16 1 93.75% 24 1 95.83% fido_log_error 8 2 75.00% 11 2 81.82% 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 7 88.89% File '/libfido2/src/netlink.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_nl_power_nfc 18 0 100.00% 24 0 100.00% fido_nl_get_nfc_target 17 0 100.00% 31 0 100.00% fido_nl_free 10 2 80.00% 9 2 77.78% 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 15 1 93.33% 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 2 81.82% 17 6 64.71% 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 0 100.00% 25 0 100.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 0 100.00% 31 0 100.00% netlink.c:parse_target 9 0 100.00% 13 0 100.00% netlink.c:nl_get_nfc_family 23 0 100.00% 33 0 100.00% 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 329 8 97.57% 498 15 96.99% File '/libfido2/src/nfc.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_nfc_tx 28 0 100.00% 43 0 100.00% fido_nfc_rx 8 0 100.00% 13 0 100.00% nfc_is_fido 13 1 92.31% 21 3 85.71% fido_is_nfc 3 0 100.00% 3 0 100.00% fido_dev_set_nfc 4 1 75.00% 18 3 83.33% nfc.c:nfc_do_tx 20 0 100.00% 25 0 100.00% nfc.c:tx_short_apdu 14 0 100.00% 32 0 100.00% nfc.c:rx_init 25 0 100.00% 27 0 100.00% nfc.c:rx_cbor 4 0 100.00% 6 0 100.00% nfc.c:rx_msg 18 1 94.44% 23 3 86.96% nfc.c:rx_apdu 14 0 100.00% 22 0 100.00% nfc.c:tx_get_response 4 0 100.00% 11 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 155 3 98.06% 244 9 96.31% File '/libfido2/src/nfc_linux.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_nfc_manifest 35 7 80.00% 45 15 66.67% fido_nfc_open 20 2 90.00% 23 4 82.61% 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:copy_info 39 22 43.59% 44 16 63.64% nfc_linux.c:get_usb_attr 1 1 0.00% 3 3 0.00% nfc_linux.c:get_parent_attr 6 6 0.00% 9 9 0.00% nfc_linux.c:sysnum_from_syspath 15 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 6 33.33% 21 9 57.14% nfc_linux.c:nfc_free 12 0 100.00% 11 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 172 73 57.56% 242 114 52.89% File '/libfido2/src/pcsc.c': Name Regions Miss Cover Lines Miss Cover ------------------------------------------------------------------------------------------------------------------- fido_pcsc_manifest 51 0 100.00% 55 0 100.00% fido_pcsc_open 32 0 100.00% 43 0 100.00% fido_pcsc_close 6 0 100.00% 9 0 100.00% fido_pcsc_read 8 0 100.00% 16 0 100.00% fido_pcsc_write 8 0 100.00% 22 0 100.00% fido_pcsc_tx 1 0 100.00% 3 0 100.00% fido_pcsc_rx 1 0 100.00% 3 0 100.00% fido_is_pcsc 3 0 100.00% 3 0 100.00% fido_dev_set_pcsc 4 1 75.00% 18 3 83.33% pcsc.c:list_readers 24 0 100.00% 24 0 100.00% pcsc.c:copy_info 30 0 100.00% 41 0 100.00% pcsc.c:get_reader 25 0 100.00% 28 0 100.00% pcsc.c:prepare_io_request 11 0 100.00% 17 0 100.00% ------------------------------------------------------------------------------------------------------------------- TOTAL 204 1 99.51% 282 3 98.94% 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 1 91.67% 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 24 1 95.83% 21 3 85.71% pin.c:ctap20_uv_token_tx 37 0 100.00% 45 0 100.00% pin.c:uv_token_rx 27 0 100.00% 34 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% 20 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 19 0 100.00% 24 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 19 0 100.00% 24 0 100.00% pin.c:parse_uv_retry_count 1 0 100.00% 3 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 430 3 99.30% 516 4 99.22% File '/libfido2/src/random.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- fido_get_random 6 0 100.00% 6 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 6 0 100.00% 6 0 100.00% 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 1 95.00% 29 3 89.66% rs1.c:rs1_get_EVP_MD 1 0 100.00% 6 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 21 1 95.24% 35 3 91.43% 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 10 0 100.00% 12 0 100.00% rs256_pk_to_EVP_PKEY 35 0 100.00% 43 0 100.00% rs256_pk_from_RSA 32 6 81.25% 26 9 65.38% rs256_pk_from_EVP_PKEY 8 0 100.00% 7 0 100.00% rs256_verify_sig 20 1 95.00% 29 2 93.10% 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 1 0 100.00% 6 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 145 8 94.48% 178 13 92.70% 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 2 84.62% --------------------------------------------------------------------------------------------------------------------- TOTAL 43 3 93.02% 43 2 95.35% File '/libfido2/src/touch.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- 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% --------------------------------------------------------------------------------------------------------------------- TOTAL 67 0 100.00% 79 0 100.00% 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 19 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 17 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 15 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 103 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% 9 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_cert_array_free 4 0 100.00% 9 0 100.00% fido_str_array_pack 11 0 100.00% 14 0 100.00% --------------------------------------------------------------------------------------------------------------------- TOTAL 29 0 100.00% 56 0 100.00% File '/libfido2/src/u2f.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- u2f_register 76 0 100.00% 81 0 100.00% u2f_authenticate 33 0 100.00% 37 0 100.00% u2f_get_touch_begin 37 0 100.00% 45 0 100.00% u2f_get_touch_status 26 0 100.00% 36 0 100.00% u2f.c:key_lookup 51 0 100.00% 65 0 100.00% u2f.c:send_dummy_register 37 0 100.00% 45 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 56 0 100.00% 67 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 572 4 99.30% 726 12 98.35% File '/libfido2/src/util.c': Name Regions Miss Cover Lines Miss Cover --------------------------------------------------------------------------------------------------------------------- fido_to_uint64 14 1 92.86% 14 1 92.86% --------------------------------------------------------------------------------------------------------------------- TOTAL 14 1 92.86% 14 1 92.86% libfido2-1.15.0/fuzz/fuzz_assert.c000066400000000000000000000316071463251454000170240ustar00rootroot00000000000000/* * Copyright (c) 2019-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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)) == 0 || cbor_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[MAXCORPUS]; 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 es384_pk_from_EVP_PKEY(). */ static void es384_convert(const es384_pk_t *k) { EVP_PKEY *pkey = NULL; es384_pk_t *pk = NULL; int r; if ((pkey = es384_pk_to_EVP_PKEY(k)) == NULL || (pk = es384_pk_new()) == NULL) goto out; r = es384_pk_from_EVP_PKEY(pk, pkey); consume(&r, sizeof(r)); out: es384_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; es384_pk_t *es384_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; case 2: cose_alg = COSE_ES384; if ((es384_pk = es384_pk_new()) == NULL) return; /* XXX reuse p->es256 as es384 */ es384_pk_from_ptr(es384_pk, p->es256.body, p->es256.len); pk = es384_pk; es384_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_authdata_raw_ptr(assert, i), fido_assert_authdata_raw_len(assert, i)); 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); es384_pk_free(&es384_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.15.0/fuzz/fuzz_attobj.c000066400000000000000000000250241463251454000170020ustar00rootroot00000000000000/* * Copyright (c) 2024 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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" struct param { int seed; char rp_id[MAXSTR]; struct blob cdh; struct blob attobj; uint8_t type; }; static const uint8_t dummy_attestation_object[] = { 0xa3, 0x63, 0x66, 0x6d, 0x74, 0x66, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x67, 0x61, 0x74, 0x74, 0x53, 0x74, 0x6d, 0x74, 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x26, 0x63, 0x73, 0x69, 0x67, 0x58, 0x46, 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, 0x63, 0x78, 0x35, 0x63, 0x81, 0x59, 0x02, 0xe6, 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, 0x68, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, 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 }; 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->rp_id) < 0 || unpack_blob(v[2], &p->cdh) < 0 || unpack_blob(v[3], &p->attobj) < 0 || unpack_byte(v[4], &p->type) < 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(17)) == NULL || (argv[0] = pack_int(p->seed)) == NULL || (argv[1] = pack_string(p->rp_id)) == NULL || (argv[2] = pack_blob(&p->cdh)) == NULL || (argv[3] = pack_blob(&p->attobj)) == NULL || (argv[4] = pack_byte(p->type)) == 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)) == 0 || cbor_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[MAXCORPUS]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); dummy.type = 1; strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id)); dummy.cdh.len = sizeof(dummy_cdh); dummy.attobj.len = sizeof(dummy_attestation_object); memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len); memcpy(&dummy.attobj.body, dummy_attestation_object, dummy.attobj.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 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->type); p->attobj.len = sizeof(dummy_attestation_object); memcpy(&p->attobj.body, &dummy_attestation_object, p->attobj.len); mutate_blob(&p->attobj); } } void test(const struct param *p) { fido_cred_t *cred = NULL; int r, cose_alg; prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); 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; case 2: cose_alg = COSE_ES384; break; default: cose_alg = COSE_EDDSA; break; } r = fido_cred_set_type(cred, cose_alg); consume(&r, sizeof(r)); r = fido_cred_set_rp(cred, p->rp_id, NULL); consume(&r, sizeof(r)); r = fido_cred_set_clientdata_hash(cred, p->cdh.body, p->cdh.len); consume(&r, sizeof(r)); r = fido_cred_set_attobj(cred, p->attobj.body, p->attobj.len); consume(&r, sizeof(r)); consume_str(fido_cred_fmt(cred)); consume(fido_cred_attstmt_ptr(cred), fido_cred_attstmt_len(cred)); consume(fido_cred_authdata_ptr(cred), fido_cred_authdata_len(cred)); r = fido_cred_verify(cred); consume(&r, sizeof(r)); fido_cred_free(&cred); } libfido2-1.15.0/fuzz/fuzz_bio.c000066400000000000000000000241331463251454000162700ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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)) == 0 || cbor_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[MAXCORPUS]; 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.15.0/fuzz/fuzz_cred.c000066400000000000000000000321561463251454000164400ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #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)) == 0 || cbor_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[MAXCORPUS]; 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); /* XXX +1 on purpose */ for (size_t i = 0; i < fido_cred_x5c_list_count(cred) + 1; i++) consume(fido_cred_x5c_list_ptr(cred, i), fido_cred_x5c_list_len(cred, i)); /* 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; case 2: cose_alg = COSE_ES384; 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.15.0/fuzz/fuzz_credman.c000066400000000000000000000225111463251454000171260ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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)) == 0 || cbor_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[MAXCORPUS]; 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.15.0/fuzz/fuzz_hid.c000066400000000000000000000136361463251454000162710ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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)) == 0 || cbor_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[MAXCORPUS]; 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.15.0/fuzz/fuzz_largeblob.c000066400000000000000000000133701463251454000174510ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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)) == 0 || cbor_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[MAXCORPUS]; 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.15.0/fuzz/fuzz_mgmt.c000066400000000000000000000302571463251454000164670ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #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)) == 0 || cbor_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[MAXCORPUS]; 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; bool v; 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)); } for (size_t i = 0; i < fido_cbor_info_certs_len(ci); i++) { char * const *na = fido_cbor_info_certs_name_ptr(ci); const uint64_t *va = fido_cbor_info_certs_value_ptr(ci); consume(na[i], strlen(na[i])); consume(&va[i], sizeof(va[i])); } 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_maxlargeblob(ci); consume(&n, sizeof(n)); n = fido_cbor_info_fwversion(ci); consume(&n, sizeof(n)); n = fido_cbor_info_minpinlen(ci); consume(&n, sizeof(n)); n = fido_cbor_info_maxrpid_minpinlen(ci); consume(&n, sizeof(n)); n = fido_cbor_info_uv_attempts(ci); consume(&n, sizeof(n)); n = fido_cbor_info_uv_modality(ci); consume(&n, sizeof(n)); n = (uint64_t)fido_cbor_info_rk_remaining(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)); v = fido_cbor_info_new_pin_required(ci); consume(&v, sizeof(v)); 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.15.0/fuzz/fuzz_netlink.c000066400000000000000000000064301463251454000171630ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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)) == 0 || cbor_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[MAXCORPUS]; 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.15.0/fuzz/fuzz_pcsc.c000066400000000000000000000143251463251454000164510ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #define _FIDO_INTERNAL #include #include #include #include #include #include #include "mutator_aux.h" #include "wiredata_fido2.h" #include "dummy.h" #include "../src/extern.h" struct param { int seed; char path[MAXSTR]; struct blob pcsc_list; struct blob tx_apdu; struct blob wiredata_init; struct blob wiredata_msg; }; static const uint8_t dummy_tx_apdu[] = { WIREDATA_CTAP_EXTENDED_APDU }; static const uint8_t dummy_wiredata_init[] = { WIREDATA_CTAP_NFC_INIT }; static const uint8_t dummy_wiredata_msg[] = { WIREDATA_CTAP_NFC_MSG }; 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) != 6 || (v = cbor_array_handle(item)) == NULL) goto fail; if (unpack_int(v[0], &p->seed) < 0 || unpack_string(v[1], p->path) < 0 || unpack_blob(v[2], &p->pcsc_list) < 0 || unpack_blob(v[3], &p->tx_apdu) < 0 || unpack_blob(v[4], &p->wiredata_init) < 0 || unpack_blob(v[5], &p->wiredata_msg) < 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[6], *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(6)) == NULL || (argv[0] = pack_int(p->seed)) == NULL || (argv[1] = pack_string(p->path)) == NULL || (argv[2] = pack_blob(&p->pcsc_list)) == NULL || (argv[3] = pack_blob(&p->tx_apdu)) == NULL || (argv[4] = pack_blob(&p->wiredata_init)) == NULL || (argv[5] = pack_blob(&p->wiredata_msg)) == NULL) goto fail; for (size_t i = 0; i < 6; i++) if (cbor_array_push(array, argv[i]) == false) goto fail; if ((cbor_len = cbor_serialize_alloc(array, &cbor, &cbor_alloc_len)) == 0 || cbor_len > len) { cbor_len = 0; goto fail; } memcpy(ptr, cbor, cbor_len); fail: for (size_t i = 0; i < 6; 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[MAXCORPUS]; size_t blob_len; memset(&dummy, 0, sizeof(dummy)); strlcpy(dummy.path, dummy_pcsc_path, sizeof(dummy.path)); dummy.pcsc_list.len = sizeof(dummy_pcsc_list); memcpy(&dummy.pcsc_list.body, &dummy_pcsc_list, dummy.pcsc_list.len); dummy.tx_apdu.len = sizeof(dummy_tx_apdu); memcpy(&dummy.tx_apdu.body, &dummy_tx_apdu, dummy.tx_apdu.len); dummy.wiredata_init.len = sizeof(dummy_wiredata_init); memcpy(&dummy.wiredata_init.body, &dummy_wiredata_init, dummy.wiredata_init.len); dummy.wiredata_msg.len = sizeof(dummy_wiredata_msg); memcpy(&dummy.wiredata_msg.body, &dummy_wiredata_msg, dummy.wiredata_msg.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 test_manifest(void) { size_t ndevs, nfound; fido_dev_info_t *devlist = NULL; int16_t vendor_id, product_id; int r; r = fido_pcsc_manifest(NULL, 0, &nfound); assert(r == FIDO_OK && nfound == 0); r = fido_pcsc_manifest(NULL, 1, &nfound); assert(r == FIDO_ERR_INVALID_ARGUMENT); ndevs = uniform_random(64); if ((devlist = fido_dev_info_new(ndevs)) == NULL || fido_pcsc_manifest(devlist, ndevs, &nfound) != FIDO_OK) goto out; for (size_t i = 0; i < nfound; i++) { const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); consume_str(fido_dev_info_path(di)); consume_str(fido_dev_info_manufacturer_string(di)); consume_str(fido_dev_info_product_string(di)); vendor_id = fido_dev_info_vendor(di); product_id = fido_dev_info_product(di); consume(&vendor_id, sizeof(vendor_id)); consume(&product_id, sizeof(product_id)); } out: fido_dev_info_free(&devlist, ndevs); } static void test_tx(const char *path, const struct blob *apdu, uint8_t cmd, u_char *rx_buf, size_t rx_len) { fido_dev_t dev; const u_char *tx_ptr = NULL; size_t tx_len = 0; int n; memset(&dev, 0, sizeof(dev)); if (fido_dev_set_pcsc(&dev) < 0) return; if ((dev.io_handle = fido_pcsc_open(path)) == NULL) return; if (apdu) { tx_ptr = apdu->body; tx_len = apdu->len; } fido_pcsc_tx(&dev, cmd, tx_ptr, tx_len); if ((n = fido_pcsc_rx(&dev, cmd, rx_buf, rx_len, -1)) >= 0) consume(rx_buf, n); fido_pcsc_close(dev.io_handle); } static void test_misc(void) { assert(fido_pcsc_open(NULL) == NULL); assert(fido_pcsc_write(NULL, NULL, INT_MAX + 1LL) == -1); } void test(const struct param *p) { u_char buf[512]; prng_init((unsigned int)p->seed); fuzz_clock_reset(); fido_init(FIDO_DEBUG); fido_set_log_handler(consume_str); set_pcsc_parameters(&p->pcsc_list); set_pcsc_io_functions(nfc_read, nfc_write, consume); set_wire_data(p->wiredata_init.body, p->wiredata_init.len); test_manifest(); test_misc(); set_wire_data(p->wiredata_init.body, p->wiredata_init.len); test_tx(p->path, NULL, CTAP_CMD_INIT, buf, uniform_random(20)); set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len); test_tx(p->path, &p->tx_apdu, CTAP_CMD_MSG, buf, sizeof(buf)); set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len); test_tx(p->path, &p->tx_apdu, CTAP_CMD_CBOR, buf, sizeof(buf)); set_wire_data(p->wiredata_msg.body, p->wiredata_msg.len); test_tx(p->path, &p->tx_apdu, CTAP_CMD_LOCK, buf, sizeof(buf)); } 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->path); mutate_blob(&p->pcsc_list); mutate_blob(&p->tx_apdu); } if (flags & MUTATE_WIREDATA) { mutate_blob(&p->wiredata_init); mutate_blob(&p->wiredata_msg); } } libfido2-1.15.0/fuzz/libfuzzer.c000066400000000000000000000112141463251454000164510ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include "mutator_aux.h" extern int fuzz_save_corpus; 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 = MAXCORPUS; 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 int save_corpus(const struct param *p) { uint8_t blob[MAXCORPUS], dgst[SHA256_DIGEST_LENGTH]; size_t blob_len; char path[PATH_MAX]; int r, fd; if ((blob_len = pack(blob, sizeof(blob), p)) == 0 || blob_len > sizeof(blob)) { warnx("pack"); return -1; } if (SHA256(blob, blob_len, dgst) != dgst) { warnx("sha256"); return -1; } if ((r = snprintf(path, sizeof(path), "saved_corpus_%02x%02x%02x%02x" "%02x%02x%02x%02x", dgst[0], dgst[1], dgst[2], dgst[3], dgst[4], dgst[5], dgst[6], dgst[7])) < 0 || (size_t)r >= sizeof(path)) { warnx("snprintf"); return -1; } if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0644)) == -1) { warn("open %s", path); return -1; } if (write(fd, blob, blob_len) != (ssize_t)blob_len) { warn("write"); r = -1; } else { warnx("wrote %s", path); r = 0; } close(fd); return r; } 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 > MAXCORPUS) 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 { fuzz_save_corpus = 0; test(p); if (fuzz_save_corpus && save_corpus(p) < 0) fprintf(stderr, "%s: failed to save corpus\n", __func__); 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[MAXCORPUS]; 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.15.0/fuzz/mutator_aux.c000066400000000000000000000131601463251454000170070ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include "mutator_aux.h" 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); extern int prng_up; 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)); #ifdef WITH_MSAN __msan_unpoison(b, sizeof(*b)); #endif } void mutate_int(int *i) { LLVMFuzzerMutate((uint8_t *)i, sizeof(*i), sizeof(*i)); #ifdef WITH_MSAN __msan_unpoison(i, sizeof(*i)); #endif } 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'; } static int buf_read(unsigned char *ptr, size_t len, int ms) { size_t n; (void)ms; if (prng_up && uniform_random(400) < 1) { errno = EIO; return -1; } 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 (prng_up && 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); } int nfc_read(void *handle, unsigned char *ptr, size_t len, int ms) { assert(handle == (void *)NFC_DEV_HANDLE); assert(len > 0 && len <= 264); return buf_read(ptr, len, ms); } 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.15.0/fuzz/mutator_aux.h000066400000000000000000000057061463251454000170230ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #ifndef _MUTATOR_AUX_H #define _MUTATOR_AUX_H #include #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/es384.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 #define MAXCORPUS 8192 #define HID_DEV_HANDLE 0x68696421 #define NFC_DEV_HANDLE 0x6e666321 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); int nfc_read(void *, unsigned char *, size_t, int); int nfc_write(void *, const unsigned char *, 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); void set_pcsc_parameters(const struct blob *); void set_pcsc_io_functions(int (*)(void *, u_char *, size_t, int), int (*)(void *, const u_char *, size_t), void (*)(const void *, size_t)); #endif /* !_MUTATOR_AUX_H */ libfido2-1.15.0/fuzz/pcsc.c000066400000000000000000000075721463251454000154010ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include "mutator_aux.h" static const struct blob *reader_list; static int (*xread)(void *, u_char *, size_t, int); static int (*xwrite)(void *, const u_char *, size_t); static void (*xconsume)(const void *, size_t); LONG __wrap_SCardEstablishContext(DWORD, LPCVOID, LPCVOID, LPSCARDCONTEXT); LONG __wrap_SCardListReaders(SCARDCONTEXT, LPCSTR, LPSTR, LPDWORD); LONG __wrap_SCardReleaseContext(SCARDCONTEXT); LONG __wrap_SCardConnect(SCARDCONTEXT, LPCSTR, DWORD, DWORD, LPSCARDHANDLE, LPDWORD); LONG __wrap_SCardDisconnect(SCARDHANDLE, DWORD); LONG __wrap_SCardTransmit(SCARDHANDLE, const SCARD_IO_REQUEST *, LPCBYTE, DWORD, SCARD_IO_REQUEST *, LPBYTE, LPDWORD); LONG __wrap_SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext) { assert(dwScope == SCARD_SCOPE_SYSTEM); assert(pvReserved1 == NULL); assert(pvReserved2 == NULL); *phContext = 1; if (uniform_random(400) < 1) return SCARD_E_NO_SERVICE; if (uniform_random(400) < 1) return SCARD_E_NO_SMARTCARD; if (uniform_random(400) < 1) return SCARD_E_NO_MEMORY; if (uniform_random(400) < 1) *phContext = 0; return SCARD_S_SUCCESS; } LONG __wrap_SCardListReaders(SCARDCONTEXT hContext, LPCSTR mszGroups, LPSTR mszReaders, LPDWORD pcchReaders) { assert(hContext == 1); assert(mszGroups == NULL); assert(mszReaders != NULL); assert(pcchReaders != 0); if (reader_list == NULL || uniform_random(400) < 1) return SCARD_E_NO_READERS_AVAILABLE; if (uniform_random(400) < 1) return SCARD_E_NO_MEMORY; memcpy(mszReaders, reader_list->body, reader_list->len > *pcchReaders ? *pcchReaders : reader_list->len); *pcchReaders = (DWORD)reader_list->len; /* on purpose */ return SCARD_S_SUCCESS; } LONG __wrap_SCardReleaseContext(SCARDCONTEXT hContext) { assert(hContext == 1); return SCARD_S_SUCCESS; } LONG __wrap_SCardConnect(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) { uint32_t r; assert(hContext == 1); xconsume(szReader, strlen(szReader) + 1); assert(dwShareMode == SCARD_SHARE_SHARED); assert(dwPreferredProtocols == SCARD_PROTOCOL_ANY); assert(phCard != NULL); assert(pdwActiveProtocol != NULL); if ((r = uniform_random(400)) < 1) return SCARD_E_UNEXPECTED; *phCard = 1; *pdwActiveProtocol = (r & 1) ? SCARD_PROTOCOL_T0 : SCARD_PROTOCOL_T1; if (uniform_random(400) < 1) *pdwActiveProtocol = SCARD_PROTOCOL_RAW; return SCARD_S_SUCCESS; } LONG __wrap_SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition) { assert(hCard == 1); assert(dwDisposition == SCARD_LEAVE_CARD); return SCARD_S_SUCCESS; } extern void consume(const void *body, size_t len); LONG __wrap_SCardTransmit(SCARDHANDLE hCard, const SCARD_IO_REQUEST *pioSendPci, LPCBYTE pbSendBuffer, DWORD cbSendLength, SCARD_IO_REQUEST *pioRecvPci, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength) { void *ioh = (void *)NFC_DEV_HANDLE; int n; assert(hCard == 1); xconsume(pioSendPci, sizeof(*pioSendPci)); xwrite(ioh, pbSendBuffer, cbSendLength); assert(pioRecvPci == NULL); if (uniform_random(400) < 1 || (n = xread(ioh, pbRecvBuffer, *pcbRecvLength, -1)) == -1) return SCARD_E_UNEXPECTED; *pcbRecvLength = (DWORD)n; return SCARD_S_SUCCESS; } void set_pcsc_parameters(const struct blob *reader_list_ptr) { reader_list = reader_list_ptr; } void set_pcsc_io_functions(int (*read_f)(void *, u_char *, size_t, int), int (*write_f)(void *, const u_char *, size_t), void (*consume_f)(const void *, size_t)) { xread = read_f; xwrite = write_f; xconsume = consume_f; } libfido2-1.15.0/fuzz/preload-fuzz.c000066400000000000000000000036741463251454000170720ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ /* * 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.15.0/fuzz/preload-snoop.c000066400000000000000000000101551463251454000172220ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ /* * 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.15.0/fuzz/prng.c000066400000000000000000000076161463251454000154160ustar00rootroot00000000000000/* 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.15.0/fuzz/report.tgz000066400000000000000000013140041463251454000163360ustar00rootroot00000000000000 sF0lmDv$3/%qn)֖|%9={S,,)3lP3 k7%ř0ӃE6-g_tOEůk{2Q'dEO^|Dj[E|r}Eyb6B(n92֗ ?_]G3BP|<5@?P ojN s0l>=)z_=>lg9g6}m6{q\(_4Ϧ>GQ~N:l_&YBi^>'"3C} QoOk׏fwӼ:n:~)qw;Yp=^,nZtlL'/{V4hFUT 3b4K?g}8zk\7H*Ah6Ƀs?d𷻫pN&A#K}.l DQ&(_Yf:oˠV[Lq)?\+/pCbadoy0L,ֽbYX_z?Z?Z#_x׽e{'#=˽n n3oIde>C| +MMmҺyxļ͗(*IZ9FJada%ðXE-s2 wn1ήw% /$$-ىͿ D)IV)III##Ug7gכYpwjC77ʒogaYW(*(Ka7~*/ nߚ?l^E?ꍖ ;T힋 {5 =nQSUS|׮X||JQ(_*_)?j=^/>p/0jjʒն= Qf Z2^~cv巰o)fPB X ?N߼|_|2el!-ŭêOѹ~xOhK ɹd\L޸G ,XHa#RZX| Gh_\܆V/$߭h8sV/|̻嵰k׻q}/JiQ"7kh/ >_ tP,&0%JlƭȜ3D(EU7nnkj:O~cJkFfP֊o^c, = "v\oOG ,XDK`U믋GY,XDb+_WJqQ"KsnCto$Ýb#(FU6hwyfr ?0vbJ*~R)E(9UrH ֹLd<j{QF4iE>e?c&*MƏ&c&*M~柵(U:@\DGpi ge~ye7(Q]9;D؝HKm01ʂqcZlӼO瓁q~t+p9Qb#% @0{ۼ'-F0a[ƒ*%EN1vvŰJs'OZ3"* &4H|vkXpG%(%UK˦"?[>\~Sn_˴ФJ mۙyed0plRلھIlRلHT7_>)v=~@6rmB\QzF-fo=[/W_A: */'Lh6g\T8ݙ&3&᭙޴/a7(:C{0vy(է=۝l>;|zZ{K_]>E?2~J0]|xZ%)fv%JکcfPNt~Wp(UM fQLn.xJ\zf3S'*O J 2E'dH@6px>8W$l^QWX(@ Q||ݞ%bHa$lY"֌pFy\:W`Hl${$G†ub$16:? БHv#7kD~6sS0Emi(f8D7-Q<ҿB!en#E"Ad5u[D0"AԈڵ([C!aӆ>-<-W]Dh"Ah7`6x SC!aÆmv[N=6e7"DE)f98_0 OQzTT9J_3Oˋ1n<ͫ;E F#A4hOlL>cHg$^kMͿmI H$a3Di yI$AtcyHx$A<#Ge3~m9<|?eBѲ̆Ej}?aI$")r}CqPFEnp?I6 m*6I((z[m{I%FWvL%ADwj0%0ooa)I$lVʇ 2 Hz{1o$7DeCF$#A7a)V{/0"d EUCY"a#z' P|,W  0j7\pY M.Y "aZJ3k3MbqYi-=C~0n HL#k<[j]NǀH #/ӱK$&6{wh\1Gŗdo0i$4DԈ dfGxߔyPs^SpH#fX&v~Hx$a7Xiou K%a#<Z%"c,f3 wb5&yLO $`D iz(3?cY_8Ղ$S[{K%ޒޒoIڼ-N%F$I҆&2,2}:s $GDݍ 3 JL!I$BM:!dGn s2G$ @$I֛B7XJI ;F%(PJYѢ/1M$&DxG:ML&NW?yx|+c$16Ǩ}G#It JIЏ$Q?\X?I~$#`!uA>$ L1IMAG<;Qu&1$]TtbsI 0I$`bqjIX&ǖ;?`5I{ԝYO'i' O^ܱ%AD .CB!Jג$6$Ol>ڗX>T)|Z)sĉt(iӡ4gEO,Rp)IĥbshdXNI:ňyyt)Iԥ g8̓%m>Tk~`?ItwH AI$jNLQ' $Nmǹ&IbTOհ$m6>& H$#M9޼Z Ad6i g1$O"nQt'%1$ND:\M&IS3GvqEF '=sVۥ9O-z  F#I9;itH6-:2_ W;̡׳ݤw@2He"Jn:Nm@IAH Z$jAYoٴ\!_WzqtՓ|0_\k@q[Hl{G_ַ4ݴ:ō3(HE&uj6z κIZտC9#I$(=9S8GQG3WKxE^/y`WShc{I$IVǜqFJ@-t&@rDɉvPˍZm2Hęi6=Ϳ .NA$=5>SlMI'k(A6ayX~ÛհWjV1(%6P!ta zxߘ|]Z HMI$SP3;j* p$Uhͧ!MD?U;aVHZ%ƫ$UWA"ܖ`UUQZ^ c`PIA9mǂ"@NTIR 6 #ӳӣcg}ЭMjGp +\ .ETڴyz;oK_Ʈt.~]R6`fuŠ{^pZ-LٴCq08"`wy(:nP uCi04"`~e:ެRXS)ƈuIa?L~ibQn +:QeÏ^ث0S`)Flld?7ÂL1cc_O<+|2!d|}2>d|2rzTLaL\rep]%lLٲ6-&,)˔M,DcLEl٣@? cc 1 R۫Wl}lcF4heŪ} [d ,2emF{ +ae 2E6(]E{n VAaLfl0S6f,_TڭL3@V} @GS69z]fNP`)KSG@)@]DZ\aiMܙj5ÖbV$`5yp5ð6f"mxͧW\!*)1T[n<k6<< S #m 6eCښnˡAyjxfm^t 6E7=Hl(B!z2l b53OnS;)@ak72W S5WXRS )Ƹ])y>YGavL;l!b$1"cFV,)ǔMc몋{(U2*d ]@Tr"eF9u]u1a0SDŒrSј+)̔M3>`LQlD1\Œ /34 f d3EHuy}u6Mʔ +kzld#~2 "*dCY,g7h;< )#cT.ը//O^z{G#SDLOfꕝ`\\_b+`u/)ȔM#s/{ߜߖU6S)"lƉ|vDӼ62&h()@ Y;E5jcU/kZȦl T>e6e3"l9 lȳqN"c7O@fS61pq̵)k;bm]&mH y0MgMQeS))͉goN_#5e3#a qAKS6-q1=cvHS`)txåڅpWlSN ݽ|izLϾpbpM5J^&ˌXUSDTKW+ )Ք T~ g 3Et(=ss1}cLl%%6S`)m K`-XǢ ce 2ELø+c 1eSƶpqL`m>K/Gl7E(+[ wOG|}0@agΕCD@~.&ĸWWHĽڴV~vC s( 1/EoQ̛~6^qQ8 .roٯ  mV=*/,КPԋpq01C1l_[YHph8nf&cJqL)Cla`aD r d,fe?Cdd,np+c?CL]@]6a9Ɇ^-m11Fܹ6B0BYl-8φy6 7NCNsQ61D߼ 88)#S`HDCr4Ǐ Hnc1-r}>b<*<*$QE1d31BEED,%|qFS!S!b?eOm199Rɩ* |>M2i5wyϦgdF cd*d*$"SUchT9 AXa**QV~q(ܫ^=NHXMr'|O`ͽCBOQX A }Ρro1+buSߊBzjɷ<[y6dW1(n[98aQy>;wnvƀVVH]iۢƴBB1R-s0Z!U"41gba+a+$ [{Z!xZ!Ӣ-N6-&{1 -(ܖ0g?غ :)b**$Um wɩȩFNmT&Th3N!Nw"4O>Xdf,o|q[m$O!O!J?<]SSHZ4 Llxׁj;3  M(b&NmTThӠYRfB!BB^&Q`/ϛ6; | mTg|Vo[П>]چ mョmOچX A "q)Go=_8V6ƙ409A<*W>@a6 1>*b++$Xanx${i3X!0X!"oBrGG?YvxjVhjc>{" {E{ED+ f"`"la-7vkw8 K탨tYԿ &" ""Eikt"G"}E}E6nN5#EE6Om5a ed&m )0zY:oīdԿg|1Y)i}aaj>er#TsgZN@E6BGhպWQXBGwh zMA65[eXe*cDsced(1p} XXD4-AbS,S,b&Z7->-iqhVe̯^qū]EJ]:a--lM/Iߒ6Nc--"zlkXmjc}D""Fh9{é4Ȧuŵĵ&5D_q."["[D,19Ba--"kGa)^>""{ ~ab3-k\#`^S~qT-jݏXH@HBH:a$-$-""im9nzYYd϶pq,dtƈm߻b桭x6 fX @ ljqDž EEDKZȜ{E{EDޫ]+[%0wć#}E}ED@"Άp"+"]vQ`{+{+[ȘЊЊ(0_0I-vIHokXڊ@ڊҖ>3 ,g)b;V1_wFlW=Y/謎UL|E@|ED"""Ei:wUTrɘ&xhMtcݕ}iDa`{Q5rf'H䮈(wɞ`}qv|QF|u?tWtWD, Wdcu sAa,,dq_Mr> ј򊈔W,ͽl4c oE6yk׊׊|UD(Mg16b0bA8Ԓ{Z23Uw| "ƚU ULԬ|QWrr-c + +IX^p1ֲbвbpvp;#h'5=Z7ڼT'}lh`2$OK1bbe~S:8ww[1[MNiփFͽ~p.zdR|dR82W)WXA }ZE+zn[l)Ý`0,0,a^`n ==1q>"XAmؘSùsDֿagsk=)qQ5tY,sʂt]5 Y YLd#| LYf˻|ȗy/tZemxYLhq>$*{J{Mb TQcc,c,cTK'[2Y 2YLɜbw1RRS2Vܡ{e1xe1+v[<xʓQ^c-zCS9ŲX_?e1e1,K"k1v"E~V ;+\t`P(gD} wcuD -$&'P|vF<Eq> 6:ud6{w7)ūcAq(z8l'XY XYL"p̪{GɱȱHQZG^"OC#'81+6Y 6Yl:Fal4. v#f1 f 1hئuc̠6SZdwdizgoN_GK<4-&i ?RpW̷6mlfms[bb0Z} ^kj1hjMS۪cD-D-&"jl; Y YLڴ6qpQBp߾(Qye1eM-|M0PP21| "Uy1.$+K+KX'\[e e (49JIIM2yoҍ"r!Ӓq~?u`,,f~vG ny-'ޛo9)4 Qm<,ݵ$ %%6 6=e#bo]3b 0b kŰİ(уpKKXs{9־о% ;Hutɐ1$KK'\s;ha Q 4yp.+_;b++! ]|(`++!Z\mnw$XJ@Jj[Y XY ʢUD h%h%6@*e{W xW ѻqHFUm4Tbb?I0x(BGm]!*pHPp**Pć n΂h"C^d–`**!Pj$zSzSBԛڴ$nM7%7%D~X<48Ɂ%'Jۥ>+o0fd" O yΈhJhJlFS{K xK [C\ jŹt+1#1b η@)%6Ji N$%T%*o%k3XNJ@NJlrRk3d<2y`"l,Q }%a))!LW7˗%nJnJv!"-֛ЛޤB|54>+QV_jĆ8-WJ8ḺUUBĮZ20Xu1JJPlTTbç6,T TͧrTŽ5ćrX%6́cO%O%DJ3=TTBڵ6[ #T T r> V%V%D@;0GG9*zLA@Ju(1(Qj7Ƥ&5BB-\.83)b#S S ^qa**!UȻX9ĽKºUUB֭bb GJJGЭLVbcDn>;xxA\;^ 8^zG՘JJl$ǃ]7WWB4h@Q5,KK,ǭldtXtXB;}KK=XK@KlSÙcZǴz3ŝNbg,g,%:c!;}#c) c)KK' ޟb,,% d[AKkchrPa4ncKך0oHp J,jlwXbh,h,%BcakE54.'+R̅6.U_)_)]WKK env8ڠ-\8Q/ڡu'b++%\ \)\ ԓ*bp+p+[n[E٭[$4W 4Wjqeb++%j]jעqJ1˕˕RYVmc0ĕĕ .ߛb++%]0])]M~d\W \Wj㺜bG{R||/np+_)vRpRi~}KK>VXKAKȝ<,L@D*cNfKK=LoeXKAKmʘSn:c)s?S׃1LLa2s5g=e)e飲e)fR`R"[Fм/a,,eW, DYl$q䰔(Aa)aM ‰ƙd${u(utkֆǽuMԷxp&쬔dgZ+[džS YY6ȪITXTTDM kF)RRWE/h-Ǚ ȪHVQZ vRpRKE }̱//|ƢRR"E9x\x~A@JmlSJHiUKm㕂*t}J}Jma1*^Awl4ŞT Tj󤶈„hfT_زJJm MNNu*t=9M0UU*zݰT)T)QQSXXD*,yWSaaD RREi9w)X)X)Q4 ?Yc p{FR@RY7VQ*x^VJķz}[)[j it\)\)Qb<09ցwL1֕֕.ve/)RRxp38 _'(gAJm—[_3^)0^'Ƌ(WJm1u`++%Z\m&쾃sH\My9 hFkmRR=ԩq7'li`i6KJSccT+T+Z[e~lll`ldcMkY(lk`kd[2_ kf6w߳ipԆk9S*qqd;1b++\ݧlc`cDx[? o'Yd"V4v6{6_@>[.?I+~՟_ߋ0=!/z"EAnAb6m?~>bf\l9q>p^/͗z>}O04ςb8nlǞ,F샹P2y6͟a .~>|~2T4KfI ?ý'X^3R\/NΏ.ͥݑ;9Onן͠QCӥ.t{NOThzpcpsɭydNJ.xjHaN0 ާ$(4mӆxvPkpMB<?~f.\ \Mˤ4RPF$ NbdK*l^}ou{dƧcƂa%~{T^2 r`Ƃ$DR|}3WzHf (-qI$.N>ӟ.Wb D;W$d6)o6ȥq3ӑ=b2%DԒ>=~ l $/s}o2_0á$ &oyfP W@P&dr9W};<{}\"ysi0ͦy3#p^¾ȫ sOu?! N ;mbQXzt|vs@'6Sfszk}ǧ'gE wNu߬pxqѼg$a2;(DsF^t˳g~ G2AO^ :|):f$(I?9,W#dK2 @% *f"6(Р6̈ ȏr Έ/:;?U3#b\lQ-}f):fF"(6S 9[ k1 lʈQr>w3!bNΎ.^sq1NxgϊtIab|N"䎂X2d;Dߏ_H~9r %w8g"Md=Ӻg ClM]"nG53܁$R'gKt&|LYNP9('>9:F [pI589Y;wF$X`krnu o.8-:- V6٤K~Zt [ol tˍKyK N8g *1OL [pID8#>{yr/'g/W3tڐN_Oǿ^Ӟ {RaOƨe.KY0oI0 ƧIàRYP릾naޏ`0P"ʚ4ҔM +jSE020g"&z[2yU2g/Fz~bIgv[` G-Յ3`l+4(Бb:L>7uf0!n;Kgs{3Za*.bX\1=Mj$]"Er#F$D¼Kas"SV$]f`yZN7~̌ik>VYӘq[T'6Ɣ12m~sغ{qR{TǼO=dU(tك\U TIJ$ISArk4oo,-H )klTlEJHQj goN_l!Sp6JFSzGg~7`fv /jEQEԆ^)瓻?(i? m> [L`vz";MG@ ɾ'@6dzPjT:Q|-;~ v p;a s>B=L,y[ߦ4ln=̑#y o=l%=f=l%y5#~~Ǝ9,NL"MSs?OT==}    ?P^`  h`@AJ  h`@C1Ŧ?| N7N^0z2xgO {k._ỏwv=2oDg+u ~䞕/">x?& X,0Kʍ~v?POڈc'/,. dza׬]#s5byuK9]΄y#\)5mPNNO ]TW) >pZ~ b bT>: b[>xNA_Oiuȵ]? &&o6QUoS Q'3/GL0|Țo#9/M<@kx L,?_xv?G5EO+b9zyoTv#Kؔ:s4|0aSڿS0YbsJ~89|qJ2bSghQ/cpO.7-Lؔ!ygb |3&ET_y^N<@⃔{#%z~v >H@xx9-ށo|Fd9qK5H=h8( 4'/clL@I؞`,O^8?;|q@q^>̈dR쇂x#LJ/Ma|އR*TCqp>%B5iƫm0ɦo+\<1J/=~]n5}p1xu46 溔Tϱ{2t ׇ@R_q@Oj;t!ظ%B)F_,߭2|LrMɱq >'cU#wZՄzZ:fGSCY`>[~Wq_TG%PL=om&0@`v7.SVòDhnBä[o*>>*Wјm9RkH&I5-(ҋ62i,u,jEQ7/.VK\_45eKnG/~^=nxQ|j ^Qi:2F;m'zj=GW=Nkf>58=j=J{q?θkʓmNkt?d%ԨdÚ˿\Ţ15&dbs#ޒpIĥ~о:'sJ<% T6"YDoú GF5gz!m&F†5.(ZpHؘ'M"!4^(Ń%nqHxby!u8Sc4Kyp!aӅNLj~a^< DD"a#Irq_Kaئ}|tlc,  Yt6}b o.  4j~6 ImZ:W'ON!+j~~]<G#aSmj01xq tj>c ֍F0gʸYZa,БHh_PK6x؊33G¦5Q{s-,ayN ' OtQ8$|Hjeѭ*k2[%$ ZHoSzuxQ;41Ws~Wq#$aTHJH)E1Px4~Dyi%AL /j+@ Ϧ'z t76!5ӳS4U:e:35n~(a\:\]fy`2  )aC_3Kѫ"ĤSJP)>A+S)as\/Jx7oU`?c~5]*{(Qq^]$N}T#W1yb0l|]pC#v5^{Z%5hjĿYffe(+??5T_WAQ3<,?)KܗʇipejMGl˓Ӌc}k/^ueI4z-); ayTtVpr{R^oqFԆT`a)òQkbu/˗eTޓ{APh7 ׊+c2{eWwowʻ/:_wMlϿ]ݠ߿֧eQ q{ |v悧O!u{eu'-Vvn93{yw_CInvf= pT{QENhJussԲ܎vku3[D=[Tmv;wiGInRp+On;lڤ=Q6Mjavr)06nɥY҃*IĴ|qwЌ&U|_Q;|;dQKLD~Low'Mw.SkRw9L.QkS{nPvC߯-r5dugGj/bFj5=:TILZxҶ!버zr'=3|Nk) XJC/(| jvt7+txȡͳAEA- EᡴsA?. N>J9n]v{ŚtqLn2s^B?V_K5V'4;)l2ixZ(I 2}8t}Pz1:TGfGDž-m6>s (Cɢ̯2j7)pܮ li< ?! ;^0o.vƋNKGO^RMO?EϹ=/nƍޤr=Ӭ6/EPqX Y2{ͩ{S 6楆؏[< ~jkc56*;xQx =^`?5$'Cbso·-ҫKec/Vj=ܵo+?Pr7+YYtZ>'ή?4eBz]V0yGbEV}HهN(aA>_ѵof{=Tw8uejAyX⃠fd{*8 v=|[)wۢ;ԂjN9jy{Q032D{cvzO6ǐ MtutXS:Ϟt 7{aG \~˄3jџ|yTl-wh下 \0=~7\Ikqx5p Zlwf/(a0Y rY\M( r+G.Ѵjr /e;Ҩɧev@*s PK{t_lCA\ϕC͍6#FgZ$cem`onÝ;ԴQ].c|7mp- \ޗjoLA>`&RrImrQ vqv%B|KKA av &+/Qwc 5X-읞lDȾx4xjv^y0Y DCYj!ȅ!6Ika;}|Rս"ѯ'+Zm+CA I{_8Łd/֙h~^IP e}37Q8wV)\[5!4uøG̙CY,E.kAUoxPFAjH̛?d[E3y3\-;!P99¢mAZ?MX=*lpڼ9*۠qw'oCjc> y8:(h*|4uFa1\oz`E#,PcCqNzn@-P6i?6-ae{P`1T23f;HEIyd֒&IBtI>P|(Mov)51?&<}eO~5,>m7#WS:>ւZP: Xa|\MW'zMa>[^SZ+M7:,l)'ZD)rv/\t!0Ӡ"׺d;ӽZg ֛a]br0wAG_R+{Wqk9ЫlREweƃb daOvu\fTqCOzζ{2F)C:~,ԌieT^p;ڼ0mb^ r*aSiitX3S͗VmP}X5d}vY3+ǿf^18ʎ._~8;_!d?m0[(A|E}~>2i9n%a^  #qw6nսdb<7Ҙk>u\9.hUp6uw:Ze]Kn$so'_xz1n G7Sq< fiq(8]w5lE_%+,޾{}S&=b$r­yb7ay)4COION/O  ⓠOÌ_or]0$6kf,š?;D4O†?y I8ghp7Jx(Aȋ0/Eb4te:'ꓰO '<^ޞPaI$lp7//O^^:<}qMrC7,6 lrnaoI@!= O'A5ai'Au:e0$v; ; w2Blkpxl< @Qy !{OwRח7QPd(a[#T-vV՟TؠP B9Ϗ ۠ P_\bR>Xm.{G 2#d3L0Z0˴X93EE8; zM|w1Ƙ "cURb1"=x~ D5~̆0` as`a¦u$0&D L= j宋v4  b*Y+6DՋ-mMc.\ z]M]M~<8%8I%n߭8=%lWc7v@](ugái.Aen&KT-n6?k/$~n7)`Y>0/^hbx}&>L] "IoO}U*ADZTbJB% 0Mu 0S%6!;T *AU^nvt[o]䯋`JE%l[$0eA?GMΟy' U)RMObOs03%Th#*f$oE_ J*8SS Щ8&pRIk2Pw`{-N'Ju+K Pl(Iڷv~>qc\Jys2O߲o%$S(LOd[휜Ev;ON !^">koA{~%֥$RҦKojRI"HEH.,C%DEC < <>&!%A(e ) Re=wjqJ);5ܔnJڸ)^1nV6*\ҁ,JX*{Y7~qfJlVI0ͬ~nAp* fU[pBJ*\׻.֯ŸaJO%m>1NuLI3x͚σۢ(Nᯯ.~RHJ*BGAbJN%:=b.g'HMrj7w+ XqUFW5)s^ŢyuioCV +i\o o$f$0W\9Fo X,Ia©+I0 VCثUIWz CzJ${'vag,gSMϸ $JV̧9O$v$VZy6bJx%w0l%6ʭܑ cW+iî02l r$U#WbJO%>U,}UrW:%A6u&l>gl+#Τ`TIQ) Δ:S폫.D( V$ZQ-w`I-hu6ikq8IO,a`sVt(Bmͮ %AD*jqϝUbI$S>%l4IpiLh0!L-*/WtbNA_8<-ZW2ĀI' ͧ {RbI$mhj ?,ĦX gQp$щ$&{P<(ii4ID5YB`M9AZDiΝaII$m7N%IDhE# Fvofg&w.2Q )Fn=8" R$JE]K$%D:˻]tWǷN<,$PCF u?D`H $&®6fԯol H.SV~$(?Ҧly i|h vd,QXt~3PbH$m PUEU HӮg:TEW H"C _o/f6Hi $:{;;ItvhN_mᑀH"s{~QbG#m~=? $?8H@~ h)>(D!F H}$H]}| $*>߻/-zh( 4 NH"Ck ~ʺ†CI %9>ۧ}~v@.Ldvj8#;FaIҾT=vSX=R)z$c7=t 6&/Yr/^_kؑH#!!V$WǿqH#?-FR>Ihc0NR6:=%a|g|*)pUrnAq)xI%B\j|NXS-J*ﰝNR6;i Q D'#mpgw|;2t:I- sH 8$e㐚V&Ec֑H#k㽹m;RDR@")"D _q95 $Hi8R)p+H[4&K֑H٬#>1$F"I`̧>1pI!$fX3& &G(}{IsISaI2/LK cI $EĒhpLR&GI&yOY|zq+oc9IrͬcୃcSIS+:Iu*!) CBgƙ#e;sEgZ>NyJ⪇W7ȽsƒcI%JSoSbkgФ I&~aw]8礈'ZN 9)xy'Nݏ_pR8)"0:eQtR`:)sاT'(?uRX'Ul2)0' Y)#jmRmR6)"ڔsFM6ia=VPpR`8)z% xO=9f0N8u?aIƤ`WYR,)CsI'^|PX_R/)DzĤ@bRDiغ>I|GYndIy"&e;#m1>(7) ojzߝ O 'ED)@RD|q#P (eC R6qN 'e3xϵ-` Y6ҷ VuR:)4t&yLBN@C) w,Q0'R6NqO5UE*xRD`H34SNQZK,@R6YkQ (eӢ`it= R l)EZ&ӽqjw9VPB%+9k:߸s6F)pэ4g4[äRJI)I@yKRK)",ʼnu<`J)J˹#*PhC 0z0CS-QFDu @sRD͉t4'-iR4)" IQ&³HK&-m%E$(ֹЈŠEI6݌;bHxlQE "e#-C !eÇxR|{S0L&RDHֈhDkc5ܟ!ĐCrL) r GKB $!e0&"@CA!A!b "ĺPPhӅxcb((DNf?B  "nBܨE"쎏"dQdQH$ -\\"F~U' ' >΃QBÉkpCrhf헃8gِ`bn(n($rCIm4gŻ7г'w`Z-1_cb& m4ѽb~z 5ۅ#nPQh:;*[ u I IHLV&B눠<'T'א*zқBD rѨ}b**$Pͧ!fB`B"V~uCGG6b))M[lqv)$"LC4SSh㘘;Fa))Lݟl((6GiOHCL@6mvmY#ҕj=]COM!DRh#28̺cI!`I Kj T n &BBԮeQ<|ƨ%{B(ETڴ?(8˟cGSG!?"EЗ7ĚQQhӌ0JQHUhGTΊmxd5T.#)d;7ލyyf[F,' 'D9of[G8xJ!S1VBPGUB**6Uɹg~Yq%at)t)KZ_>^}XÎgapB 'QB"D%7vz?KNN4tLPq)$:LPͧ\Fy}ٿ<s^d!fB`B"DvJ8{fFp666icu|qpІ;uͱ̦敘f<6/şa))$O:&B¶C񯗻f? THXF_^°Ru(v`CYxS?hR!Q ~i)4ڤ)g&UH4(@`J*J*QR'??DI@=~ ~ Sxb))O[88zt t =Nxovύg@BD\Ŭ|nfèSSHD]JSJSHU ŷK/W>fwyvpz)K['pU)JUjhzVcsJq=BBĎF0ڀ/L.@.6ri1ڸ$CQ6CgEe~RRRhIeU{~Ua ++DXIU U mpZ{)Km7)vBpB'@BD;jEZ3W. mOtH H mDR[F!XF!2]κmjx gJ`B"{$hֽI//O^z1 >Rhp1Qh/&׋Ǘjy)eJ!J!TEj>]=RRHgnoxk;)Bh4rͧ=O'fGřB33DI/a<ƙBB"7'gM!Mhrf|11J9 ;N!8N'lj((Ao mzt mlj3}qWD0`-k@[ mV1%ºY/]RRWj`))$:LDUUy%{a`+u>^DXM@Mjn.̲6{Zk0%va))iL[R"ܨ'ݹG::lc}|.Aq6γ[`e`X_?ea-s0)$/{a(("GX &|q7돡ۉ9Mag81wDGGl>q6LeW;z/d]"'E'ED=L()#s捒~>a+)+)YIMa= G舃s,Bj'׆Щd"""/Di)9NlO;Ōg~Mq۱**U!`[{n0 J{7byO,oAA I?ʮw.a+(+("ZA?BbPPdc<91c(("DM##Q[4PD"~edȓ,ZV0w PdC8/e:ߩo۩5L E@ EDj( ?Ϣqg\lPk31k=خ`<z_WA寿O&1XX~JXYU4uSVrrٔdȏ 'K"l-ORPV`'G>ikX8$bSuՌ!Ҡ)EDM!wFf$u?QQdYq )"I/1tt#JWs=L؇cF`F3ja((!E7,E ED"&&l6Ãpv7ͩdpQd;O[b((DMw y:&" ""QԦy=cJEED|YoCvnUCC ~04P4PdxgY>^ư$ s3`EED?z΋ED@T (/d`(("@Iy9""" 9""D ;OOd0D ODt|$TWQĊz( E俏Ыq(1@M;<1ֹ..Qu!v$PD7ۙ22=2ae(e()C[EgܐCX >QOl,ǯ;l~(PNjgnЌ"fT5xUƜ˶F44lQ2Dzϸ s""؅N@ED|q3E0E)f"`"S8ȓ@TgOw<,im""OҖiK1j\i w ! !i!C~Tݢܢu? `v(v(&Cc7RW&^蟿pnpU-7>@%>)'a/oWU7Ml|3P PLV '̌"*mVQ#P &b bi}fQ fQL4"N7Iz~ G1G8jDok;bzY X8q6)I⬍\?9tan)n)qKª-p1,0 0DIcghhFeھ;|iHB1Š16i gb`bts`>8 )6 |cn)n)~Tn)R Rl/`3)3)Iv*'lX l*`*DS{l'Š'D=z}Z"lvoܡJ1JJ1bb"D9P޺a &BwLG6@b"&8`(HMA5sF6,&b baX]V-̚_-b((dUâ,vbpbھ>_F20||SVmI098u?`((&Dģh[xqnP:Yl`Ddm,bbLD8i=b󴶁10؆52j:j`((Gw;F 8F 1j^;8Q8QBĉ] ^Ħm+(γgv & OP&z=옃Wyxm{`((!jD߻KC HCMBKqRh9yMqɣ]wu;,c((@_XJ@Jl w'_mvO l4P⎹XJ@JP #wOOBڵ޸P)/ؽ ,`P`PB p(yT( P Pb20@IlC7;_ӧ9{D Xm}DJPJPbSGxJJ<9BFPBDhGxW_>b*/nyJ 7`|os9~eNYǾrp2I(IB[rp"(!@+g;}հ1>ۥ8 !Z]v JPBU:`((!@w2,$ $6x74I<>S.:5fen P PBeAwS$$D]쿃a < < Qi~nNNbwqU':QPm@yˮ?}d8YƓ0պPv8"dOBxڵOawq``'`';M$cOj|+n4pp}MMbl'[i-J^ztqw&3시60$$ We@$h{ 8$l$`$63ƹK|I|IK>!㒀6--/Et,$ $DMpq- -h~\KKcqXB$K$Kb#Y x,{ `/c%%!-WOגג6-l\Pǻ=wDlKlKlKٖؖȶP{e̎.nv̼$$6؎UTQU0 0Mqfxv\L\Lbb_NXI@Il I``Ħu7q^KKz\>xiŪ)q`$$D,˙^^"BK4zIK`$6e `$4a, ,Meq`<¸ihHt9.`T%T%!*>8)K* H* QRr` J JbSP0kkXvߛ;,Sb$$%t]ĆIIb3L_vY @i!m\$K;I;IINNGN̝$$I$$]x'k;/w X3I@3I~?I54Cͤяy$$$1I1I:tL"=zž޹F$$6ԈQ[' X':qn6v5NBIBIlHRuh1|`$6ıON8t %;I;IlI & &'i?/MMl0yx?*sKK"]"̑$$D]7o~pq$!#8+<+bW$W$"%m .6m7H HjFThb{${$#6:;7VI VIjJvwmm.{yS, 6d FC7q 3xu̦8?qI jks3GED&l:8:FfCRCRO|Ƚq_:u`{${$#[OIIm2u`$$%$:0VVڰ Dho0me A%Im*֓FJR@JROH Q8PzJ\R 6x~3X'ٴ쁵t=0HH@eI IJ4L\G=a$$%Y% 썤6oԱ2΅`dԁ=[.ϲh9OůSUH/DD Gs"Qa-Jn||r}4 O,(:wfݓx~}0WFTϳiQ~|8&{`=7+̌i{fΣwz89;퟾yJ)]zfR<=%znj3MO JO;- O:9=fUMX2a(< (ԬVuͳfQG2WS3Qx$hDV[j.poMMv#-D nz*Pomwbp+^lpr<|G %#YG=6`ď ᩡ0yk)O~yz67syy?E"yS?(焞2g=1!<%=$d9?_GȻ+nQ":ȟPPSUfP<΄1*"cfdÄ#W$z&p(2h|<{ w9!*"_Uxa %J'{($TL^fl1cE0Җ/`NiIbE <00[cZϟzPV͠ !.` l }phPVRe6#ZJk&V@uO 6>6 <)FCRU%hyuF])5o]'1pd\"?oZuջS>Y~0殃[v}ߺ:b 5j xV@Ǹ?/ }NL_Vm\eVph쭹a\*X`( g~&0PEԥ!֐ t iM*GT8EZY~ÛյoS+--.o l  =7( w. zWrP>g #TY_{GW׺e lbdbuը=wEA<8U8PV`[$pX`L/e<9eʁASw ;=K@[f'Z\J+P g\""ɳN 7OVO(\6&P&Ч![5䂭₟E>lbmvf"@ SnPeA,D^ŕZ*pUBц}POA)lTwJT%= /TCaic}V+! ȕ@ UB`qc߫Ѻ47QPFA(#v A k˳@Yi|kWilu`0" iٚ\/Df _ؽ'2C'@(@6B`ق>5N  -Hy9Ǔgs7×'/?yu|zo+Ob Q8{7"5 Z^ 5#nS, Iģ.f6FbeV¡5pnjfzJ*~5c Sw0()V_O%Q(fxÁZTMPq;kŪVExrU¹daQ{u`QH{< O Q83y"lQ3N ;~!H7vrC a3TC6{{n]ñ"Y6ei=\Ω-g- m6 L@ZMiq -as[ G"r$nj̏A"Da*Ҽ]ϞQM[oiZ ??_L"s6ͅBJ}B{쾏!-Q.LG q] Y<*U${cE"H/|TWeo}\>zxZĖLF5ioKq/O$"{$FQJc sPn8D# }ٰyy|~zVg3Ϥ!E7]x??'HLAg3h͢Bg͞iFگZRBF6W7c3܌ތpFę`VF+#l }6㷫ubMj220|wZ7.ӍqqGYl@jMiZH?[X@Ula5P!ۻ< IVݺX} B_33{s;ƀ6f Nc`FzXi7-`"jC|~w5^ oGvfJvqJFe G[#*Lk ahϞifx\A f 5ho5Jq[UlԿezuyBaMTK;qNFz?v2_v A1-v3P;Z+iiK jSTx&D=ޣ[8i6<FʝjξT܊i> yt61za-H$l^8eX Խ NAA[8# 9I;WOe?vK8bD vC/ 6 @hrXZ=}ù{z{%|$`>҆4C\j zzr SU-n=AhQbݜd;HA I$o=$6{S#BHڄ"ǦǤ4>fda" 0D[co+=F" zE- H:R1 IЊjEkE"iӊj1{x!cHq$IwmCbHt$mۀ66hO%$E\.ԑHR#B=?j9Is# *FJ %%$GF9f>v&#c' /z=I$qY'7v?E& "LbuyI%JӹX` 0Ia S$W]ObI$$S~K%Id8swBb_I$WW+ID8]LbgI$IΒgjb2>_n_~|ȟn6{6_+-gr?7!7L8 _)~!X&J?'ًz}`_,fs?fCS]#(.VKg̅򕹍ͳiQ~qg71h`oG78/.g}~y6&=⽞ zF;ݑaH1eEn7|OTF1H]/Tʯ17.jj,4/vxInGOHl%=c88T bh6Ƀsqx?Άw`f,]| ǓHTugi9]p2 .uw[fو3<}Y:/Ƿ毙S0~xW -RugR0ѣTǑ `WYp{(Oűz8UM'_ bdG8[|Ssw4-Q^ERA]GzTȏu~gZɩ*1x:n֨^]=0Yr`Vj\b=YZ\SӫIv&~[+feH/ǰUMG'={Lw>jPGݫY{ϿjR;b19ۏdǮC"?d}ms^{fck 4l>X nݚ\{EYر-U{c)Xh6(f_ON|R#??NN>q7~Ϡ<őNAG4?2ی&$O xgQXioQ4:?俏ѓaHϝS- Ff ssyžXGFSa*/޼<> dVQX Σ+eb4,eM|R,o" R0F-JU8>m^mPDX%v\(CBy[u; {PVТe3 󔀻Ԁzr N/,0?2gøTUܟO_<.pm,ʎPnVmY}s|q{p T_jx})quw0U`4}֚>ɸ8q (aAhmB n6 aNE{=.22^-{|gY;%w,.U8lE8E|"[Z܊r~z\jwx*Q5M/qIebqcO֎W0+` P!wՊտ@qa(YCibeӼ~kg[Ww vi6zRX1Uk7ǥCrpN+@![!Z,ޛ<{ͤĞ#5ϖWv%=GL/w򙞣<<:wǡ|z^r6 VP=Bήށ1O6Cwy6Oڽ.U5E55ymwee(a]k@u [qoq A,A; ]jЋGT_O^$l@ [ vjejTTp"ĠֆRr͔ʦ saWӀbZA+9K4FV@yLRw=T  D(fd_xٳd;6l6#4 㰟ܵn\jqJqt>"PCXBvݗAqM (A!Hٺv151$"~Jh9|1΋#ꟻ)X. L .54l%4|/fye}n ziD)r؊rx sWm^s`Tqe(Aˡd?Lƣ8{_./SnC)Jq*qxu@)eتe879.܅EyoM(&E7>pPVqGkƀt!tTΠ 4?p= (AK$zv=i&[0y0.& VĈa,c*"e1U1$+UT'dt7/OwD3l3GڽQB:elA-T u/e/Hm\l &@}~}(g +걾gW lT+NtPeA8=e6}k͎g5cs}Eys;vq]?6㧹=H;NkǦ8Gж _g5'd`':NkZ}['y~-rWGn¾sEkX^}znn:0-&aE7rT-xG|vT-yHXރ&]-E·Vhٟ6ޫaI$8#t$U3ڝt#Hxo1u$:6мl?^v,1* wri ƣ{vl @~݋{H@$amLܬJI'D`"  ӑѳk{zJi) e$36ȹŦ`[8#Dq38G9v;fzGOSa ?EG88̏SБ'(@6ɽ,ZnjOχ?_po1$@J6*9uBlj1%ĒKM85A`I$l]z= rSXs0<9/Vޕٹ2,Ja{S)ADw<nJ)B]'yPj*Dg/T"}FuioB}OXi|cj62FVZ (h7Wh\1BxpPNW+A!:ؾ_ ~Eh9w(bJؚͪ|=쏗}s->Qtd6XTQI ݑ ZəucgN]} rn%TzG`BJ!%}x۷ %DCv^}p\wS-ҽ<>o~N؜N :E?Zw]?kmj%<y;KT(*A܃ٗ ΫT RጔNW \-|2?ŬxiWH_BC1i%6Ԫf8VY9A3vI,] ͺbaUЮ'(6Ê( W "7}:͒A{CL)jQQe z-kT8*Aڵq=rE4Jp*Ɯ3+ 2VmzJ*Z׻\W lZTyD_秇 > Be@dZrdar4)a㤶p$q.I'5o\tXII'/Xoʢ}=ԪˆS_=djL'ړrO.M'~9|yxӛWǧ8;$lS]M&A~B| vW9McutWU@qI'ZSl8slA1_tXO=)tO;ܸA, F "QuSPhB1_rg_rrt`(J 2Q@L/wnLxJR%\TͶL(t7?\b1[k! M Цb,GL`|J>%T;$f$QFqBrzS-:%ADuJ0V0gG۪%$U&Wy A^ '%D^cL&K,^lßĂAK-綳G5>KsZ8-ieJ}C`%6$[hn0 S"KwqqWcуfm0ȃ/y_%@uI*]Y?9ͳ){<@$p2[f.Iĺdr(;[2aK%~}9_҆uMΏ(@ D5q&xK͔w}cf3o I3U[JIpɤ%tNSjLIʤ*sRۊ8c&uj1 Ę$cA(7@I"-7pĂAL1ڃya0I&fW$&$bHvj[TbSy6ˬa6F΍Gu$v$b.x@$*bmF7nR6L> & &6Ш[\bK%m%$>K}dY6kZ4-IԴڵ ɹ=ZH-I$h_ &^wS\C -IĶh!x|CbQK%Z, B$ Yj%v$8YdEΒgI"Ekӫ5CW+i٘@WI"]E;U*i3pq~J)~U*IhO%f$0VX7KLXI $bDm>a%TÊzWYHbJi%m1od~sGObQJ(%\i1۬n[5.nR{rJȩgG"'~k KYI$ѳE܏1n%D܊xnJ+onu’`aIE |KcI$ǢAtK,kI$U֢G>N|rZ[$,I$3uXϒgI~g sՒjIֹ`eK%mʖ[˹][-ItWI)d6a?qZ%$\hsϐ`vG5$6$]fv5_%A_eEb^}DqK`/o7B].ITx CnÊ=$p^y ^/IhMvz. l$]2Tb. X$b]e'XbK%P}c$pK./u$^h{ C _/i#Aqneۍ)`Ҧu?5K/\ҹ}(qvKv{k)/ Q^ݟnlqI'6 8b*^)7 IR1%֒dX5]B!K,ڔaSyW+iБpJ%+ڑ]9;#6$UhVZ "8M~JR*qaJS%L٘CM+U)ЪY* Ւ5տ-T@RDaY"{oč.r+s~0R6*j sBJvu&~M靹Evrv;( vƞ`Oʆ=5^0g O 'EڴY¢IE'Znn4QvJ*юbr7T+l))R"y4r>Ӎ/ߚ * -)Zg>M<0>oWaR0)"${ Ge+ 7)nĸZJ 2Lsg\UR*)"tǏ{zYT6w"Kof q &E(Am L D&E(=;z~`I%͌~0`R6XIh%7xH!pV(GʦmT ~E[H "}xAm܈oǹ&R`)M|F@#RD]; LH٘" 7y #F #EDhMvzXaH`lQCxE {ӓ_7/R)"^+pwX9R)rT='Y#F΍9v@;R6h @3vo0CR69B$MR&)z">i>`@ZR%npII%%-b \ I(-yZXRD`v^v\ORAՖݠ8AQ' CaIɤ$-!sR&^"zMK KO 'Eao7bJ #Li"PBZΝ#bJlTN}(pQg,@)Mr>~L)@ @(Eh'c5;p`ISF0ϤgR6i gIPM]6&Jk22npoGݿ=KN}`3)$UwP*IUvm 32.Ύ9 LǾ xE}Z5q/tx;1x,4Ǔ_w ě(E?V(A;,`GlO|`>ßA`GluT0eCCmprGh}:{` E${ڵ?F7YcK|0eʄfƾqG\{z]_ja*'MkWN H!Aŏע]bŽGH!11RDb>|qޕ!֐ZC.@RD]Fm0p3RDg5 B x!E\Cp04R6h\?+)`%-VF@#RD3tWYaH_l|Q݀[XaHtlQgoVDR")x=[H ,$e{H#\M)+:R`)ungΌh^j6o'LJYdf~Vd|eFl>g#[}sxaX?_Xdc(RH8" 2,l~s|lgAх[S6˫^~}0WqM_}m^tp0ɞ|Pfx{fꡇwzwC9:[n5o.gw 9{8TSsob*_ѯ>_?2uqQw=VyFy` xYm5|\}CɼJ~>EWKli1~{;'鞾FT𷻫pN&A#K}S>3S&JUțe̮f 40??5yS0~xW!^ (W)a#p0 ZwQ0/f˓Ӌc}!~t\/Y x׽e{'#=˽n<6I)x"t[>sYt_[y"t8=s1MfcjyEcRKXZh,_:oҤ>r  gRFcQ4f&96y(Wd\h86&lQ %l\Ev%a OdmoD_U\qݚO&A1 oX!"*Db? ;{M! P֖V'%~Z>xt(DthQqXP~[dM ^ư<~R[u= ٵ+.樃`e|<dO 1n ]E͑bXK ZGC 'DhO4"rJΟG?,c>=TsT DQ20BB5ڴ,]y+X+ZH,ʊzc[3\Ng? JUqP1%vu5j1H:we)cWUM .n.Nͺպ'#?+U@޼'YU)[F>PkU3b2͋7 77{A^$faB/QCLqFL1Ut埊/%fUĬL|EzCuVTK~lM@C/WzT] J`+i%^bކ(/+،;;gaUUVɦ*5Ǫ}M\QkbyQ\"4{Q;F'u{Q"ֈuIy;_ [dc?( GD(G$0DD@;XpQ! 6dÆ @ yfkY|iO0. ['a0!jڄ5xZ6zTaC x!LHd#1VO' FDÈ߇.`{L\pv-ESJaoVOb#qEzߘv/ʬ#J?0#[aHD):[S$S4oF|?;x,%aJh;NE̮VT6~_B9$$βvio8~Xcf1y }M`6&/g?5DĚm[UT}گ̿|N'П'di(9{xOD)û*wSbk/=(lc7/6crd),4FDQX;ɂA/Wwy"4}fP9`9LLM|*W~8CIq&,0W__y~LAE @ZD+-Uzp7ɿp9*Шlcӫ20N6c LRHEhƧ"Sm JP_,D('*ɦ?qhGȳmoZݍJV0gX fA_(oLsB G7dn~֮1NJ>\94PDMjyeu/pG_A"O9tgjֻs F ("E?5ߓ߸Gi(Ej~Yb%՛/܎8<|/^\_rA(\nQKFIuC1Rv;j zP}prAkhkJUz=xB(%T)@;,7YS~35_faߑyPẽ2ż?/|Q}׿} @,F~8>Ͽ\y4Uat:W3t69e)71=ټJv:*La"G\1|PEs]fG//O^z`Gבy;3R`*e?= Uhϫe̾kE&=@LzDM`ޡ)G× 0aäGԣA۵vlpSÙK.vɮ8'j&rUl_Lfi*j,\MgJ3XDӟ$ֵֺl\W#*_n;X,̚pGlKqb_+`6S/! ?z}*N|仚'lX&H0*5- ֤55F" k,%#dѸE&%cD͏" E8B[39eQmTM\ĕz^>;T1Ҙ Rc]g!̦1fb Ǥܨ ,2A8Q2 Jd+>rdȓ95aLQ&HC49e2aD$8Im&lYKd(2axŶZAAn1*&@DV,IOwLҘUQi쀨Xח7e( 'vQ hb]nn,lq;^.iZ\P5V{j5Z_TI~0vY&|bn`Hdz Bm>ƭH' )]>, H;>Umn5 JjA;gS GcDͦ7 "p&{[`?{um%0u&:DhcLx&U<<` zƛfⰆ2z e1ac~nWKy;zL 0- Ƅ4_$;z}cWGf8X փʽ>jFy0k=/FkƮy LpMP-$]6xL3 QP16qly"8L嶙">y<5[=5ԹqLƛ!oq,^wkR` f60'D0b-a\{$Zauernz]Nx-598A⢰K/s-HpHyhoę87Aφze;s{ìMa7һy&j[;8&yz "F9Kg8pvk3LpGW ۄMn© 5aֶZ7V8r]9OFjz|siȧbc Q<޻V|`/X$pgQZZz} h4A5\,0e&26̬c1ATKgvL -Z D8tgdlsW3f8bџ#{]bT+L0z]쎽=UT^T&2cK%l:#|w "3LQoѼ|h7@=uu k^8/AB{%%<~ pbDX+xX hP0L9an E O|Q8&Xq~,fzr<ՃjKG̅ „ k h6llG'O|~&DAt`eL3&=!'pP뼚CLDc;@4n8"&K0@Y0aú &T04F[RƯ!0&@ D6r!H%ZbYNM] (F ֋SN /~Cަ. ]'Z<2hec`L0&HĘaTbK%IWa|&ӗO]g;].isAڏ/doVb}K%mcsUbxK%mV[4^%&$]Ft qxxI"Eh8sxqI"ĥw4 i`Kڀ-7*FQmTwFI `I$jHw,(+ ؕbW,glG5 aIBXCX ,i\7,`I"զqW[Iͷ© (*iHbBJ!%mugU4>Z Z!AI"z3ILBhKҦ-k}]RϤwŦVfM|3˧EYGXc1IN<$\6sɑ38kL4&iӘ:0$W6\VwcMM8En8$V,F6I-$eD\S8E|Q p$M`S7I oBY&i$sq̇${``_?drݫvvI& $L7aI$*r'VXX ,IԴGn._󧍘j@5I^r xGx߸7$N;14b"]~3S} RQ7v88$x\^ 7,zl`e8$ʓCLS\N$;LwDk(2-X@I%{ǜFva܈8)i㤺|`s[0NPTFQuP5XE@,X|)ƔSq`M. qCChc0+;)НQwo]+MĄ{q DqcJٌ)FӤb0d gqY=`JyMGkf03R6fE(""P*b*G ׿Τł20 RDq*J/z~V/ZxRDByӾ~'x;#T *eCDzO:9ɋOo^^C9+E$!qoX@R6j NҀT)R<% n˾4Qe0rt WMþ\=o͢`|ǝ` Kl@Vvν{Ӌ7__蟽<9;ee,d)Q:U*;y7ai>>*.- l)آ] sZ 8-e㴺0RD"K싘6^mtk!Zb!Yb;Bx+86o>109D\=& & m0% =a!^ mym!"ڸ-zlM^?P I msk&NVxP_+D_+$_+dZ#f.D>+$>+dY \ppG8^R66ڴ)T/׏nfuPn3jqmP\AڙE*$*T;J uwsP I J^mb<moĹ]\Zy ѿM v+9qX% m8V9Ц]9~Dws6*Y!)Y!Sc>G81}󧧏<}}j A@ jy۳)]!]4.0*_:(~xIm(|$|6-$i/ Г%mnBյ&03V ./}2_N_=SהWHWTp)[B,$,`~3g|OI mli1b&-BB}B+$+d_$cLr_WHWdNӍŔ\W\WH\WhzCBBBh^,Zj^Y/K+Ͳ;*Z!)Z!Sf_Фu᫐_߾ fEl`z:XyVHVt9utE˫ʣW/ҼVHVİ8s0b Xڔ]~ h>mb,LztBrB%|9̜>Pr  ڸ.K{HxDx/i]G￳{7UTЦr ӥTO5[![xVHxVhó3̗Q \\;4MBBuAڝE*$*dW$mjV!iV!Sگu_~D°hR2*^󫆏'QQ6<%Em9)H#G2_8O|xT8}?DFEFE\6]M\=qQC0 Q w;o<'"i; "_ͧEdB<&Ǜ5cr+İB IYVFU>^/RyFoDrg84o}G1S]14gtR&-mbDVDVĴBH4">sOVDVd!X. "t"r"ӵ^ ãqG+"+\"d"b"&;>oD""ABMVԴxy,'B/+"/+bzYiJ@+"+:yR\Gs"İ"°"OV5fQ9XXGhNUDUd#v_^H*휡||+a o$'B!+"!+b YljlUTq읣8&xA*"*bTa)2UDUĔءt6`UD`Ud< WW jXiXSm?c7dEdEL Kq#7HdEDdEL"K0w#2X1Xr+m; Ŋl,A&FHeEDeEL*+_p{VGA|Ա@Vdhh1E+zby'*iU=R,dIELKsHlЃȃ>%G (ٸO5NNMtsTL&q#6@E@E6#6:E:ELI:N!1}Z毟/ znoj""& Vڥr;?z% --f+'JU!ci$))JI/ѫQkmaJRdGX- 71z+OSdCqq㐻wmmllnH;h8Ed8E6éS喈F4a E3E3E`hƫqm&@TD@T[|qe/_(B5*"5*bQ]a98Ap#NNp: ,W}T,޺S.ls?x2"$ T1,%T|P|H|X>NguF9姈䧈)?ٸhO^>q"#""RʩXlluajʮ o߰Xɟl8-{ik4aI,""GauEШȨF$J 2qT׏W^B"&B}g۾9]UXɜ$J K8S͘+2?M߯şųf./-iW ũĩ&N #)/>i hÞAiVKur>AX`E`ELk"d"b"e^+}ZVDZVdӲ*찖W 9="yRM0Vd/BT4VD4VĤ* -X|c$*WG*"*$Xy\Z矏-=fɧ$i谨(,;$^lcbb&m'{ bbbX_u3&gess75AŐrnULnUtxq*<3 ssRULRU̔xܾW"/Ns,bbbDŋ1 S1 S1St~ETLTlS:1P1P1SZsn H|83lyF-#bzc@ mXTO1O|rSS`f<U3r|4ne\(K4sc{[׷8Z,$j6Qkuw_Ƿfs(v$v\Kt'3ĺbºb.% ȵ:WL:WչXmvPފIފmV=bbN}nm@>lEZAdV$xa6ýj|g`5څ{M>[HbDbLKWb&%53U,FåUTئbuZQImՎF;G7OƫZ=I!s-$nI K:i[MjI ;dt\sNPW̅:1F+&+>ۼ]I5V|Irim:d(TbRbkEU-C  l0?P($L1LkBH,&H,fBbK3r`1q`!Ca%+f]YJj-iB5}MRWLWlӾC52ChAbb~b:-xb&&w+ݾ9}xR*`1)`1SDi m[åѯЯ؆~uiQIW:_Jv6F+&+y_\Oc$b"b Su^5)JP8722۔1?QImخ1l$?Ocbbi֥eVuia%y,fcX-bb&0Ɛ,ݭ+kgBm,&m,fjcP.,6Wl.%Z2ﰸ9F{,&{,fcVK K?c ۵exƓ}Kcbb=v%Ϋrml P`ghFuv(tS\1 ,dehn0=}|o|qxl4 4mYgǦ~w[#uu33t؆u?TYLTY̤8 w3(m6v"mc/v /m~cmg\XW۸2!rn}g܂"b&qߊ"J{șęLΌ0*t=Mףw/f1.<,,mdYI98͑qp:g-EE3-}r"##N6VDRbև#0ף:tWw,F+&+_̕M|)]*F+&+@]Wt,ܿkj_]?x~?\tasu9*.M_?՜؆u,)_~^ess|G,&,yg?CNQhBǚihu9q+|Ewm1mlvвL>|wq)E[b37 ,`.W`؆uGU.&U.fr!C{]f4ŅBRbb'\/*t{~t'N GG<:/ϐ9Ii( CQ=}BÃdD]BD]$}v&%%L}n6~>G ]B ]dx{POU]dtu+IШKȨKF:ߺl%LЬKȬKj%h%d%L.Qku_Y~@3l{FmE~'%%utrKKGR.!R.rj|6Zgkx=421'/6t)H%D%LR.:!H
s@f-!f-1kfwOȮ%Į%6v  /i iL{8>3 ,id%6LpP}=% mmh3Ȕ%Ĕ%, 0 a a K ӍWBWRN7] ]I/f-sfKLWx%x%,O8Ӊk(XJlrjckΑش/~k?{o&frIч|x5$޷:A ,! ,a`wJJlW/v\Ꮳc-<1 cC ,! ,i`u'MJJR8A+!+_&0\o1'TRgzT\(D1P,n<=OZQXHKl2ʯhDo@!],v%,'bG2Wb׭QؘL~(x~y۽s"JJgY= ױϋnVBnVҫUJ|<Ƅ WH˱?NUBNUr' '#iX`v0,B9xH#r 3Ƙ7px6^.ǟHewfPi%$&IJ@Q݅~=xG!~GBUBBUbv;x.Z55ج)O`5; dM%6kʱP%P%*YRԧRҧR>z֩ow޺ߋ\ CC*נ̴E τ)T)T Q!5USTRRR2O0M@DTJDT$84V~lluB> I@qN)O)O)~ګ,,h)O)OA委SSJSځT| b(<$<6in0vSJSĝk#%OsM3IDRBR& ŋ 3wFCÕNjeEd2)'IA)%)MK+ M)Moc#)LTRRO4:w}RSSJSjv\$ P'qq8fJJХ(7$76i^ƚOȆٻViC)%)MNAY>ihJ S$R"R`vʋތ~}ǯ|,qm$e(]ŜlQJQjӈvz)stG啤& 9G# TIQIIImNQ` QPԆkTIQIII廊襤;뵭n7|DxRBxR̰OIIm6YP3nmDn/ћWoOOwn38RԅR҅R.`=> )H[ߐ"oKVJVJY M|JQ`JI`JmS]ĕR•RK݅+\0\K++LX);RTRRR_\K!EJEJ"""LwH}=2].}&C)%)=zr ԓt+~'OJ OJmxa]BE)%)1H6ձ KJKJ=qIFa")9H·WBE)%)9HK*rG)qG'H(D>-zDQ>|gyTEuf H)HG50?4͢}}#qs۳ɥ,EԒRҒR$CBJ BJ=AHFa"(UkMrEQ8Y>$` #)1Ha"6|`VpV=R>zGRIS6 ! !wӭRRx_aF 3=5-TRRRR$n_-mE)%)H>Cܧ&&L0IBԌHBJBJm@> 墔& %$%y--zVzz8< 33ژ!g";eA`5@స10 uGGlG/BG8e~GL\5QIIIҏ`$'e:? qOsߩ'OJO4:I Iddd6짗њ 8eњ &Bt70C'#'=)cbmiسD#Z_Gg?A,eddLG)oӿ_9-24222?(>yP PP܊;tu=bp)ǭ0Ӿ,ctDD[&0Dݐ=vtma"٘߬/Ig>KK]:XGy!)cL9//b?p;ujz!1;eLI/^qN1vrHxl“߄w G>fEeEe|,fW'M~BTfj ٩ةN`Tn z222P= )FJ1UBެReRe61'$2"29ϘRR 9 mki34r͐ʈʘT24222 %#@zO}’FzSԛxGsgt]PQ}ȬtSFSfvu?4S鵂&TF&T4;C;MЇȇʘ>kK8! )sƂITfc}HQeDQe6RRKьȌXfݍrYW!IgTCCM{]EPFPf󆼭'N_HeDe62@@Am ml̓ K$*E"@(#(c@.̐ʈʘ4J A AM{Ae`PF`PfzXHlQ7:PH< @beK~'i2@;(#;(A't2r2# ̳~qX ppeLG(< oz 74220}@8(#8(A1^ߊky#xRE졌iu[?lP5B2Oą2…2OPKE^|iK HQfCz! e eB22&@tPFPfsO e e6hR呸^F--j RHQHB꽖 ww1`a$)cHzFA)#)H' d2b2&sY!qyaQFQ$-$ee6ƨk DM :3 BBr+>SʘPѠР}%9٠ؠuJʈʘP奸SlpP̓#$m<ƒ2Oxc j@i@MQNzxTAD(#D(c"Bݖ22&22&tGt2r2CGOfz%O*C䃆 m|PߵdА$'IH((S;<  4!A7=<  4d@V!*?CR~L(C} P( I2HʨQQE}]K 2nk 9C2r6#+=!y6Cgs !4CAP<"53$jfȤf- ɋ2x 3$ fȤ`82DeH/2d .+S.C]LѥʁՍ̖lJ@eH Ц)!a+C",$I! f`:-CrZ6#h vl!u!.CCFːɹx,%Ӊ3.C]6ݥJː {BFB& ːMaAPc2dj,ݖV$ WR:_2d+zAeHt.P\2+ 'Z mXKuՖ!-COjQXa=2$ehXeh== 2$eȤY%H fh 2$eh#]$_D =/Fa#eE\͆ N" I}vHk .CB]6ԥ ).COӳZ.C\,]ED` mLEe!.Nd΋0C"`LP$h |̗sR.C\6ʥ T̐(!鴂 2$ehc]z2丸{C4]d =.H䮫J0C`6 *la'KU!!0C&sG_ mK=!y.CG!qrp̐4!W鴌 2$ehc]z2x(#h tz2]nv]F2/\D ;\/#X|2͗t^ mK!-CC1g$ Ke !.Cõ̖'CAeH~Гr஼]_ mK5!i.N4 /Cb_L!Q/C} AeH&PN|l=DfH̐tZVt2.=\XHpz\|zGeH낵;*0CR`6ꂞː<MMZ1$M -Zi]iӮ-o.O0EY++Z_fji!M@JjJ'QhD00z_5?A*j*֨VITMI֨VTMd2z)*)64\*)6BS*)\WBZP5Цqq j '!3nUETEOr ֔Fn VUMUĎVMd:3|ѭiezH*U uS mjLOpi}bmݪ\۪[Q7&qn'MFjnj'VV&lΰ62aTNL*aS6&jV :ishzH!aM 3}'UEærnkL*aS m MHM$Ϭ,NMl)&g|.F?ZɳE*QS8`M'-"kШ)l)c3Y}^/C*5U8x_}ҎA\=P[;j 7/TqYZ7j 顬Er5~3Vōiլ||j:ԪQS=9.9Dz^R.Q0GMaf29JKU.3ZYg?SoGu~Ub7_ flNV9rrXqU ׈Vٺ:UxbJl]o)6kȭUT{bjڤ)L;HN1/95wH6yi^Cj)Ly~g>3U'MmgEHԛW/VEOnc܃pLiMgRF3S*iSmFQ)mڴ6hwfb68T7dQ.5IP<ؗJqڔby5oyVNmv̴e}HWDM/2mu"ҦԘ$yH֭NAt lݪiSJ-A*YSڒ>I3r~gZ:kj5S\߉*YSmr waq[=k*M_r~֨?un/Ps61ɳx*YS=MvjUĿF܍kU82% ]Lf5F0q*_T>|/e*5U/Wmz*æ(,UM'9Jܰ)qfqgr [oؔ>OSYຜ1laS^V6ŏi-^v:cتæ2%VD)65? [sOGroaS`jMMn?Iyҭv]tn]M~P-I5bN`zP-I5O}\1q]q.( -ڗmբTC=)+X٭m[(QG#~Q(0QDpV-;J5vQNm>'NJyjR6Hj!LM{kZֻl2zyhZj4)Ԥ<椝QM-J50bTM-J5 bTM-J5YTMTSZ%Z>j|*e5C|P-J5蕲W;=Y%hFR6AS|P->K5|bYtKRL"TCf)Cn1XaqYdRL$k's9e]ƺRL;cƃj XMP|BTf)Oh]jpYey>|vP-K5,bXY t1BTh)Õ°Ta)&ŹR:K,Q}斆 K4>x5VsP-J5nVmsXM/nMohl{8Vk,2bT`)շQZĕj+$\wBdRL k{ұTc)Oto58Vm,CVc'U{K` [KRboP^amFR6J|?5-J5ؕ]*t/7׋kU䆽R+n[ R hlClq3_a^ӂT_)&|g'{}Zj4+Ԭkf ܟ)\ׂTW)\Ci)TQS~z>ϫ-nu5攲SΗI^pTd_TRL'>-J5Hb"U3ESR60jqU*nt[.Vmd(ŔxGQ˄R &~Yvznf2~(n1\rͿ>|b~X^Vlt\a \Ǡ$QTZ|ڿONQQ \JU} Eiܬe|\,ֶ~dq|WNWGs|k^ѷIaz?(s.=$a0~gyH|)Lb-MZo7"Hs( /[<[fժT?H4]}_E \]ftYޔ`V}tFu)4/k(Y S?y)bc^Q}9σwyp^`:/ϟ=9}؋_~x$L)ק=x^٤H"i^xˏ6͓8Fn/Ov$?/"_FOF>ѧ{Ap_G7H8Fo~󙴪Hϑu98Fm;7\GKl :OrPζLѶͲ˳b9Ďjm5gบVH[k $pQ-c.ۧy֮:1b )|\$1Wq1YPFe+B>Z\Vq \/7Q}WHg=Z\j]]hm ?PEis_ir>Zg[yIŵx϶oTOGJmߨdI=m*p)--@% Z2wc!TXZg['[ *&-lO"(v^j=).vmz7G׋:75xy!=Z]w!=hqwi^ @94~_QP\a&Uwζ[ j9Z9lȋ+Cr8b8V,K.05sNA~r.(ތ(&^NdQ|ůep --Gk+fjt}vurS\0Yyp--۵O l\ܚ;s=| Y&*:۲:o_ۭtkvߗ?׳7'Khmyݮ4ɽ϶:dhѝmݮ9[pq-c.KO7NZg[*4_ |uoUoFQy Z .z%oo:rOunʍm|X_N5pζ1zP(bsusxr#־?v b8KVV15.w1 tYhw VQ\ Gk*88Fzn26"0JZiA4H l$x/hsY:aQJ(uQJyEim"#E MuJyh`w e&LzH,%%SR=#l.+3Ηy~)k=e%JJWzZUHX⑽~/uI:)etx cqeY>x׫AqSxȠheu9|Aʓ*O2ܿ'lEq)P-.q̠4euiʘ//ia3(]Y]2 {XX3aY]òCGɠHeu.1hx-~!<:sȗ b9ULNKw*Q#6T堵Y~|cG+e{MlD}@k7m=lwEM0_ ?]ϦggQ5EZ%?`ݞ\E?e+'&)<Z^?8䳫-bE˹ˁbh/{7[3wQ_ck'f#{>.2'rc`z/Geܭ穊y\vv7 ޶v=xwI:}ڛQ%h6/FOgOn#[vRAH~E^(%ů/xCQk-zyO vʹ—{duo<_r/ZRs5s:z&Z{7[3w|Q˷A\坹;/|~Y8j݆{;aX3lQ׭}ootkbadt&87ۈ.L`832H5ߛmL c;S򆃛"=# d;d>GISHkGfCx~!T|o|gN" x7w󣻖<\᛽ /Mlw{qkgfcySȽvo6a7~bt@7{PKZ[7;{rR;fm"srTuT ;zytvLˎ^<{:Zga- ںٹp,+Ef^,7E>^JovnT ܌hRSkfz.Ï95o;-|kf{y-},-5ˇ |{|nQF?t}tqg,KA QΆrri=k2 lz6z.m_Cwty%_Ů< HvOXQLtiE[GlN[!`mgtf.E$d "G1!N8e*(~hz1UҨ,uE<<xe#~y?x9ŭEE^K6IA$mf{&f?7|ڤZmgU)M pc$G1.'>t}{sgrwvbAm6:=7 ߹36o*<y'oZ7sE YQ`l6=QH+JkGfC{}kNlNzbQ:~V#}!=s?$⿖X{իH:h<)>-mhP7#BA ŤWD=\X>~:JyA Wx\Z"SHP!/!xr"|H!KK#jD8"#6 K#Ciz01S|c#uHR)MP) Q݈m#  B 2bƐ"dH1=~j( )D{=C!Ŵ\"p-)҉'ru')Ҍ3rY9KP>RD)}6j>O⶷WnW=:]($H 1ھ{]0scu&"I1U$xM$E(H.POR')}7M$E(bH ph\k:(#)Fr |ec#jK%Qv %EĒXr~n"8Ij,d$)&?C&GFH}vIw;LM7:YΤ2dief&H̢Qa\d-UZWݣ@ _]Uߚ5q1FSZ_ލvӷ'<[Gg+H@p:Nέٺp=4) :4sb0b XOB8h/'M)NEK4Ph(m"-{MhPhUwd:fj^sfh`^/2Ț6kG;ϻudYNI\ݫr*Ei!Js+v:Zoɷ\ߪN0VH+%~QҤViZC2NfT/ɿȻ9AT QqB|+ϭPü9*MqT_*Ki4brt~T?&Cq1TRiK{vobL6UQҤ_i~ѵ#kFK$V#E,M"XΦ҄diCBÂLWnK7`_Y|Ʊ`4kh4qW] &JT[/ĉa+MV_hVi2ͬrgeFJm5Z҄Wi&^%}MFJO>#i@dJ2mT.4ӏb~{FQR&>Xƈ6V3e (m3﬑q8i$\;wx&q/ +M)6_"Z&I3&FL@@ q&I35h#&I0,z&i&$I\b))>X-PLCi;@@:IJ'i4IF'@ImQh5S-ޙNb>`H!~ ?6MS[qI0E"ċ˳)="T4)EBөf9ku` #MFQx C#x <6𨇤"m\=! A6S~(+iMV紡¤Ia6kӆ&I$Jd%͔hS/+M*a|^0%MfKZqӧ2vp5!d2i?2YEzUvY>?L_%=i&čZ^QOJ|>i&č`SHoo葆DCi& % =Pp(ġ8M>0#%4P(m#E>|3!.'MfO\.vd;i$}T./;Qx$i.9KD+ͤ>с@i|nC2c5af6yu(M:P̸ϓA{{DMJ& VJĤ4aRI1.}D&JmLTi 'MO_%bP0(màׁ%Pt(ա}wXɀ6 *MK61h6iRMmaznf2~(n1\r2^,c5ΦLW˳曆1(>I QZ}D(]J`)FXRArX~Kwu*-a4ʷu׏8/MӳAci:=Xg#S5?r\OL/#b,_]y'V]e`V0`h6{x_!rzq׆>(.l<$x<召 u/?Y е;6fޮ`q^.V]cŅs0~|AybNݿӻ)bc^ı˃⦘ycf;4NN$^opҸn"f o"p\!K*K.%;<82E ew\ITIML))_5kxor1o#D u±Xw \5Uf9|>ˣd?@4Hg Dp"u̜ )P6Os"'D1-4P6[*QP6 js0f޺u_yWrZ8C9ͿObP"TfJ9g>v~UVgt߂AP"U4]'WB̊,+&e%5[!qEb4[г"ꠚbVdY1)vfvV\H[WUwνdHb"W /t)WEv+U5,b;6&JҨI-Y658LxQsD{wrYL772Q{7„HJ „G r> 3Xb1Mn&aFȊ+c4*E˔͋'a/XdgW\=?ˠ1i$HPNTw "C@|*hYgU5\he㳗f>?Z_x=iZELSR|NcO-sD1-:թB#҉+:u3JL1O>1lR3rT"Qd V8w;_l >L,~U|}z:?ޅk g]ܿZ 4L3qu.$L$3qa&V+|ʉ].mH3d('W`L{9eX!^M.jI|MoDp.m:Re*];nFIΓyr'ۙt|0Oxe{SzoN_x,/" %lW_WfP:PLJ]1{y(P b:QPn[<($FB/(IL,>6='|"^4IPL?vQ ^xw3MFe?R+ȁb2P̧J팪zB'b<.?Z}(⡘: DڈEeܚ-@=(;m"E L[s%JPA1"h^͌r@5Ё"ʦ@9Pn-Gv`, 7wSK1y)YC6gi)lԮ%WBe{\*׹@i֥y+͂K >0,&6T ZCC>V t07p[EuY\gٱܧszpqT= ثc T)#3|yT(مjuygQ1NMRY< 1 5bn5@7b@pĀWeA+J" Z1)?e++{0 Vjn4*M/%J-|?r¿!O H0' oػw}½Q6"sY F5bs° iiR($x3U^&c¤MI%Jz^uFFE--!QoNy|6nGC[e4}T(ƢbbT //V2{ [tu|i*[%@3(ULs[[Ƣ:(FPRQ)OU}Ȋ1_ez9t1?J#VHR6jgS)njun&;Dz^CKni -H)u?Ušrqp~RD;?)S-̮ +E֕bbW*HsX"-K1,Ii.o~՜0,԰$8W6T1X`q.-\)MP`%Jb̑!\|ֱVl\x.?"J1+Jީ]ę!+EbRVɺe0ayXtɨR6\&3&47Ls3ʦyNVZ׭X ɞR6|܍"YJqi)79J,$M)5%8R"{JN7<hNCIlsUٖe=*l7YJGzM&“pJ8"I1yJrMiGI٤lhsۛfQQkSfW^bx5}m"N+Mi74"H`R6􋾒"`Iل~/,'\vIz:BIʤ,S͂4#HWRl^|XGչӒ)~)6Ѽ /C"SIqQˇ~{(hGR9R]UR*J ]%EbJ\jNGT~8 -Хǭ]tmtxb-n7Lz~D1)$AZQfRD3)5%e×zD!K%^JARh}xv\Mʥ҂"IJrR6Iv'pq&Nf;u椈sRLωsht5. -ԗ$~od"Iq&F(4_DQ./7_ݣtx3ioR7)ߴ3(oi%fhoissw״Q?IӋb˧H HRL("JRB)Mu'C>Ngldnj}7,R )eCvGq6.+Pd(eڽ}6jUݴG)(E\bzQ]rS~^@#ڌa/FgϷK9,?3XRLgsK&bR4)e㤺ϚC)ҡb53mlʬ{~ibI 9P=v,d9)3Ll& gzbތxx>6zx@FI뤘-/='ڤmR6%E LG|5>2WdMF79BEE8r"ntN); QyR<)`R?P6o67g]ZcR1)}AjIS+r߹G[3߯O)oM2_&~:l8 󻷦s: XɈR,$ahNQaY'PJD)Q1,lT9O @qCShoO ` (E`bQ|̰ʏP")J1(~ ?"Q(do#ȇR6 ;[I6 jםB ?Aa=ZQ(Ɋ6Jc4580P&msm%_F#ۤm6ScbjqZױ1f&3I$rmZ4K)8?Ӌm:C!iSJ}i4AF V<>&H|SC!GY/)"MfRDPgY/Aa#H3"QPy&TQ!ҤiBW80di9$ H&OH3=!N ~A mszHXXL(߄oԪSt]$Ui ;VHT&zH]{DD&Hsh44CkuxP Ҥi}AGO$z=bֽ­W$;֗ʯw di}lRrNDzH#X ,,W\^i4G&}e4G#mӏOHi4:%ǣ֪.?YkWvlp-~U4CLI'&I .",i.lݪ]c#mz8X26ü-v;X:H#i4FRݽD&TI3Q%A>d– [rk&M0 &uIԥ]t%nDiIHzY,iÉH~I}mD!ImBӞ3^:.wvND'i&}V=t4I&X炛jz1ϋ/W}%MJngxY>K/ynRt,l-i}CaIxI\R&MI4,7BrR͓zqwWW#z?_^a7&GI3%<^9}%H*i"4TQ싛XSLOW h&i2L^ĪGL1In{t[A?tA &MLΦo8iRMq3+^&MJf*M滦!4Mi7u?:&ITgҨ3iҙ4SgJyS*-*zL;^ʡBidXU kLYݕ59&MVfZMW-_̡w\D!ݤnLIs^ OXfbMfšA&X&I3p9FI̤2WU)ܘF¤IaL hʗn8# Z6j%Mf*J!FI041C4zH<$mGHwms8@xKbEX%k^ l7=@IImΎ藟PJĔ4aJچ) [HO!ZIK6-@& I3$1[_ˁՒ/w8N "&AI%h͇Fz|+DM&I35Tww/i◴_IfJ8hbA%I3U}.ui7 #MG=oziOΑԑ&Hۨ#FcA;J=P9Ҥir#I,:D"msz)6SCjU(Wy?ZISBT!}PUH*ILUa*.VשߨiRM#^WPzYyW?ś;{/6d i%ņ% T8[}flOi-%ne#%!m# 6ok#DM($M(  6W9Ӛd;s{aW%D($( D Q$ I$ m"Ѯ,l6hcf&9Fp{N^!E!E'Hڨ Q5q7ᕅ\!+6Ш0QH0Qh\b3$X{ד O魝ix0D($(d2Gq2DzɻlAwv`"~~r#wu>}\ QC IC ?rx:1G!?zEQHQh3zPXg# Q33ԮOߌ^<8*b#*%KQHQ䎸/fjh5x}NybŊMR҈̺l98!ItNLtiVE($(FgU$B"Bak*CM3BBB&pO?\¹k! D! DM `!?[;WɰiW+gԃB҃BŒIOha`7qRGjOxP'D'$'=}  mq~< =;ev6D'$'dr,yRvB;#2A <OHO$x`[k,Lo=q=R2! m)?BM59^;%zK!1cu'y(dC3rD!qD!#5_l$?̤^ QH Qh}zzTE($(d"F- QHQhVE4($4(dAyOІ OTcA$9(A=jp6Ϸ=HDHtӻtBrBO 11ڈT<]A!A Փ._W8Ny mlP)qigL m^c(+Б72!QPAdP$(F~($~(C: iuІy sᡐࡐ qvP I mzPI5B0("0(A`5c17F #t"r"3$L@K Bj("j(D IbBjױePDPd_n&ȼ/} IIAAMbvwqe"$"""& wPDPdC-s]:#Ԃ"҂"im:#$""";x$$$3??ued&pL1!1J"B'"'b< f^HD(D$Dz"z"z"^1uG/B'"'b= 7'߉߉NO*:):MfD'"'b8!cˡ7oډډlήW=1A~oUT0.EX  DDDL/k.@'"'b>g+wR@Q@Uvݸ/;WlА]RDQD"rl43!))y"‚J|PTd"ffji,QDQ$8Q##l+t/?aU'(bJGw44:#Ĺ 8AEA )ptFHEDELzh1 CCMrtXQDXQ +6 ) EM(r<2g4QD4Qĥͤ a`p$3٘>_y#""yBp{ͥ]^z#AA"//ͬPYOkp"@"i qȤݻ݉"""?A9z•B[mO~B;"]P-EhEdE#iRِ"ǃvKjFiFM3c$(bF[[{79ǻR""&l__.DJHIHMB~jIiISKڿ.RHQHBE("(bRF@X?L{z(:xkB]\q6 x{:i#6<<xLX^7JJlTJJT4]̤Vb"&!FF6ڧk䰽OQDQĈOl!KKX^""&=9 E EL^AD""$éE\Pd'B'"'> ~\6lODlOdc{:AOODO4|:V B'"'=<dt~3XAx/NpX鉸L+ \_FM.@'"'b=qOk`$'bz> nvTODOĤ}c/B'"':(!1[d(yxw&+B (" (a@AF߯?ͷ~wuqoU5$QQDQd\GumR$A~|}Tݿsa#j|wӤ<ň&ń&64I-F%)&%)f*Ifb)Eν6+" 2 q]A8mDRI1QIJ2^XHT:Qm?icb⃂J1J1J1T޽f[ ;=cbb&鞽1J1JAUUTئ*&F!)&!) Ib[l1_O7=G1F7)&7)IigFzBRLBRha90oHRLR̔c4b2bq$}22Q! !mQ( C1SRB=т1D1D$r|:c$b"b&i$P`RLR%4b2båi ! RRLR̄& bbb.3BBmムw+F:)&:)D'I&9X9("$"6Ivs;btF(&(F}P@(&(E2~pU-Q/F(&(!F_%^^>ėZ]Mbb.eX0 0mk_$^F=b}ĈS+F(&(yG=$w,Ls;b$b"bYF(&(DW1 ^2UQoLzAbbb&ly,[G :PbbbmďyZ5E1E1-ڿҮ=E1E(!9a%(iD\% =Stt3#NeԤxtbtbrb#^IHI*ItV''6?ɱtRLRlCOW&Ť&L5QRtbrb$@X)&X)J>~abbbOQXk Sm!v0E/)&/)fzI"۰4(b}ԇRee,#G ]"qqۈNFKLΨAR$b"bYaHί#qqۈs*E1)EM)0LZ,,GRQLQlzzI3M"Ij%C(&(fE)ǠOO|/ĈbˆbO_dbbbK$|;;^:Ej(&j(QC´y;wM:JzPlӃ5"@V/ fONt<6XΉ'mQj{PKzlޟy;_;M)$GYfè[&V޻?u͢7!)NYC bN aN s,PSP R?Vc9s:A*!*PϝM[zq"<>btlzMOJJlT# BQ AQ r-}{O; Q Q͈Jv G`Kt(L%$L%6ap}MLfJy?F`7WNb%*a)TJhU%dU%6TBTbzJJJspП 0,x螤NOJJX$@ VB VT8M&(w}B*!*aZVj{ V V&'\%\%LT̈́mlf[pbu%*aTCfΜNJJl 0NgTtnwEr)!r)K ĩPcJHcJj$7?5Q+<>;e!gmR3C;!,d9%LiȟGWrm oYihIC9]'Jx'iE,*!,*abQ {+R R nߋ[TĦJuA)3q(X Jlc{JJlծ{!31V VͷLUBU41H31W W <0QUBUbï㔐QJHJ:V$?̤%a%a%6 ˱GStr[l%Qhf0MQJHJT~aJJlK4T T'J(,lQ%4*w(L%L%,g o|?^t{0yUS SE0P Pr~z3_ ?TssL{J{JlܓUlծx (!,ŭǂFR29,h%6n'JK[PJP|ŠĠ&_ыGl8;}*եHD6*hcqkPJPjoPJPjӀ-ǥHDRRRvqfzEY5ɛǿ̺tV,*k)YfV 9ܮ)Ŗ799 RRR=)VNz8!0\B)B rl4#!,,`! Ԧ:|sX̯g= y0Z߫2V0'\"p d3"IHF %lߙAqV_ayeRzRzR-D R>)Q>)a^wA'%'>=W,$Lt9)>X] Im888TzRG'%'a<}2)<)<'\{WUCE'%'=PQIIIm_%E'%'DHx-"-ggJhdLӧ۴dOJdOzP'E'%'=F)%m +BwR#ɿt&xI3/<)<5 $L˼HD6GPI֦%,OJOj|͗fd{Rb{R}@'%'9=,О)M6)L'5)tfГڄ a:9)N<%`}#'i>,qDHaPIIImA9iB4RD65y'9-,itIImPq@'%'e<wRwRC EpNjszXIm܍S/ϣ|}g"~Ƥ8`"&e7$n-OMJMjcomShIPIIIB~헦oRoR}:@&%&7Y&xhLJLf O$ .rbRt9,`$Ĥ6!mH1#2}}u98ɱFAVpLeФa`tWIWIm Qr=EA%%A% *:>@^Np-NXXIIDŽqEzJSkck]i℀pJJJJUxGLH,.zQ͊Rˆ^I^I>IOۥEzɒɒdIdIdIm&nNCc[R[RE]ZUxɳpZ|'{Oh䵤LeFK{<($6{ K+Xǃ|JJ|JO]e딽q'˭ N:9**MUNo Ft~ceHdDdLBwXV8wm2T2T2&}wm2T2T2cbjZO7;7Gki!Cg%#g%c:+~)69&3g賚|د>>ɘ~l2M2M2m-lIFlIfcK!RRyBJnWVs×SD޵j9QV-l,y$$'ᴼicCo$#o$y#=$,_DdLJs]X^57*K& 7е@%#%(_Kdd|ńZWDڽ@$#$Q$=tsd6<ㄴ ƅt;~\4,,do6BHFHdFi!֙␖{= h:{9M|^91E|9,đdH2H2H2G+&_:ՅQ2V\-[K2K2^"A@p@IBlP8Ɉ<#tǜ==hݣB"A"٧w E>9C}$#}$#Ĺ a$'D(,OdZvuț[PHɘHC>)####'rD(,Vd̦o|{HFHFɘg@ɈlHi#'uD(,Qd ŐĐ&8 7s 98ɉtt1C2$#2$c!¥QΥ=r"q"A9 98ƉxYbu#HFHfFv!cBtP,m$d6q+!# 5s{HFD$#D$!"=S,bd678by#U$"M.3@a$#a$ #**TPdD2bD2#O7 ̆ڝTB$#$ w  lX.=JoxE2E2OQX:AAZ`M;XddL?D 9w   dddL09d3"HF"HAxM?u 4\l8HW(ZYmUȚz8 ,t>_5 cGFG$?“N q{6/ao y2rFVHfBı.bC2OlQXl H_iaǐpH Gf#ޛ!!A CneYCtA .^mܝHS9!q C&_S|e2Pbr}5}'}b~X^l$p<{8]-L%'g'ԊAIlJD*9IM0睟F\7bm/}MgfHP޽o[*_ 4h.ou>_?t|4?Lo^z:=Xg#?,B]OL@8{j]y'V9u~Fޮ`q/FA ǘ>O,xp[=_\ŭWı*&EOw17+a'ϻCYЪY] Ou3~:?wR9'տ [DkqYC.żσ#Tjv3?f]6knnW_zTS1`\uY(s"I| .PfbG,*鱒]%Q|yRgD#qy FeP1)=ken%m?TkǸޘSgg;:ݫt`\2˸pca+VA'제jfq-3n7dqMP7h5vPi 1rlX/V#A I][}sH:cU1BlخOt1l>ZɚwKr;Y ?]yP\"_K-: \Km0= @\3oFr: &[,Gu~UnůǛW$]NGgҌ ǝ frK~xT8~ [WB ~>ϯ`\~3dte7wyMFrkUY9o;Hoc }T{/ob4\UVۑW9Ff~_N󐒏;*4U|,DvqltwNw6`7VHu5{%Kbz a~Ꮔ7c [uoxCːF>ͻgOu7q Yu9΁r"`yY㳃˲t}ӹrE?VM۾q/Ɍඕ..X:Y/|8s@iw*YRW3ަ?^O],i}x>/ ,UDu8/Bԅ*>JYZ7 :o8/:u0=4 WZ9ugs[,fGmY_PPҺܝ{1Z_eZ˔$T(fvbXUPVӺ}Bq 6mzș=)%8 XֲږW iZck 6 mmx;BN낝2r3OfeD!݆7ЦY3.™O=z{Iڴ6(?U H5%7٨SO~|jͫ7ޞJWi{n^28iC>f6mEA զ]liawԆ5iֆڛ?tgl5(Lc|V[jӎq76dxl:nS6m?enMZ5kՐ=n,!Ta]VCVúZ %c#m!a]Ć=OIE3Pu iQ_[!d$U@Fۅ'5@I`@Oj6pf2j&Ε67j|cAZ<0`WaFzl!iw p!nb U4 Y͔Md ;0 :`pP}4Wj\/b֡bxvxThI< r{6:fnO i4ܬX]9Ӏ<yZ$Ocpwfփeדj=݀ =.Lig/ތ29Ӳy4sӿGXby bWDG̣4 !ߣQ\ ,1Z?e~T,)@5'}ޠQRo,^zI=K5:;7>D$}uk+Do M E(b-́ mW/,)]N"(H٤ o#*h )„sn_o)RT!i <<"HyTft}y5Nڶ]ZkfO](} (F"G1*)74]GG£ /j֠| "G1!^t, FGˣ (y28lXT_9,)& ğʩ<0SЎ/R )&'$ 7 (&3W+{ĢG"bDZ✄"e3z8߭Lu~"HyBtF"E`E{"9B!R\h6:n<AHVZ+D$q35G#қ#MG=n,f$iTFvH;=C/-uit4E O_&H35ڸT}k$4CI[#$ 6H$B&!H3 nݷFGH#ms8o&H$]^iZ"MXfbE=E"diE46h%!Yrkx|{<}$-$MfZHY3f$[CSI$VwJP%D8V2T4)JFEIRvcF,IX~H|p|mQk!QEF9‚&H4"Y\wFH_m~Ѯ^Ik&Hs9*wlgNӊ4E"EXX3F#M)F'4D9fZp'!j 5L(&Jo=4F)IFH\p'iW"ML1E=ܹXk$6hJQwg^w1i47ұg62ר"iR4SEF:Imtc һ=%M)I%IQYk4yI%LMNmLW|kb%ZI3i%I4NBP[Ҥ-i单%x}$MrfIZi6C&I$id41I$9\ؑ&H۰#pŗ悴I(5ik4k^P Z -Lh3;Q-ҤiZo5:F#msq JG#mziXˆ+Ls,Z(Di&Q 4:\"i&DUWQ!ҤiBt 4C!߯@#;6v܃Z&-H3$>wsn&7H3 ~~ I2' MffA s3o{k44AiLC@Hmŏ!}PnH#76nHҼ)}*,mNPfzO|BBB;h9s^;`!K!K!]bmb}ompB 2 fi$IeuS!D a<ɪo"֫Eq9C).H/VRv.UA$D;︯WwY-b)eOKm˳֓xdq3_??BĜBœBN;+ҳzLH?D?\iъ>&R~ m s4'o:sCi*$i*JSnW׋!O";;r)VwI{ |a?W9)p°#BUh㮼/;NSNW=ӅBkچѭN3|YdŢ8y>^^?aܬfqBfǙ ]Þ [!ߊF?˳g/\BB&ʼnv-k2ωž] =M񏫢k)}cyqʜLLUR:-~2/^uq3pAWȄ\7%Nv? ' a3{S0쁐#]?%9{볧?; L-ѕk.9d!!7_56R|<_د\xί]& & 0'$ 0oBu8{!!,9f;;܌Y|e1)zk>g!g!=D ch\Hh\Dx+i3P m?m`\>9ľas!W v% mJiBn.$n.rs4UD,ȅ6F9!o!o }qO[H[h+$P}g/~96!V>!Bo!Ao z5;g_kiWݷܷF咪(6yf93O oEq%CBB+֔0x{hD.$.dpOϞz5z< ,Ȅ…L?\H\xP.D.$.d:p=pN?O=`B_>tBƒjp!jp!ipW4;:]+hÅdÅLn;NZP I :/+;9҅!q!q!\O~9R28c#0.<("28!ӃY Vrzoı4jd˅L[M7)ӤK饄\H\h亿P~ I~ RT~`̒,VGBg̣"[J+{1U<&L[HL[hczðV5F~?=קşN?)m'V#rB&؎e&q|>˚~~13eȾsXygm_YFEEL؂G*L) TxDEELMG.vvٴs~YD~Y{9}$$ Ys"""7IOQ)5O!w#$"""ܥޯ7-XChH1_?:X_A忞Og48,xDNE6rp[TDTdCz@fټ'nnUWa3ttlc9G@)")bLEWu`ZRDR4k#ג#""̈́.RD.Rtx~(=l huS{b=# )bZH/vwlGQdzj^O1}"/S"""HÁFtPĤxD/("/(bzAeigtlnQG m+w"#Tq du"bu".4Tp"Rp"W[)t^VV8ڹu8`"&b7^c vQa#&b7^U\?q"&Ïjq+zqHX=IƉ2?w]15[eHB3'"3'b9 ~WTzLNĤta*Nsq"&Êh1@CO:NqAڏC'"'8<988p"p"OVnw~ZړC-'"-'i9;=%J{rDDLg&v1.FNDFNd3rl1-9J zY SUH88SISKF|u=(D$D8eBti3:n"pj(tq[qٚNg JJPV;g"OL6ܗB&"&14D=1^/*Q44ɗliQPȆ[x{5tRqC&"&)6(i! d#m*g"?pҦaI"v&3,wMK&XH>-R0V5f"4#B;ؙ)Ra" [gŗėȓu7cuT+MHDDD6 F;As:DDL?fH 98twwlK_["["%t1@%"% /DDLe6~9tPqHq~H",!, a> X:ȦDĦD66a.糖JDJdUv\{l+DDLdV*h΀S8`#7%)=,WĠD~b(a( C 1pM,yF[D'@U%"U%*8}|ȓ"m)BQ"D?@%"%(0_L' LLlfJkhPPlt˓7U"JN4S"2S"OfQXH=<'.݂Փԓئ8t bMbMb.mJJ3U_oJ,OO|~@XILXIlJ:1*$1)$M!k F}$&}$l#w. .mpIbDLbBLbb=L&RQ."ĈĄ6ILI #nwLh#LL`a<*-DD3%nZ$1Y$"*-B//m~Iya&'D(,^ĞtHbrHbOKbKb^钘F~X1QPt^1F$&$&gJbJbORق>ĹŒĒ&sY߽-uP'I':I6aȳ72KbY$1$AEED& mS.Z]$1$ 'i**mVw&1&5!Ą6<\IL\IlJzXmsl],TčLn.!1!1qT@ 3.{HD6LpGLpGl;hpdp69L'< 31.Z1Y$g@ mxGy)'C(S$s6<%Lfr8xyG.):bs@#&#fjny΀83}.:19> ` 1 Mp>nvԈԈFI 347bwFG 6 iM/  t~=w8FLF̴4hidi6Ks7> FLFl7zppĞi:$3&B4b,NXӈFy@PtFp =AFa"O#y4b4b!O7F:^:H(i$iLIDFLFls4&8,FLFls7X߈=Fa"E#)=fPg6>) \-&t,EgtPψIψmz,Pq@m#&m#i=g47b27bO憴QXΈmto3_$jFLjFlS3v,tFLtF̤3:XΈmv,g@۰627bO憴QXΈmt_&(A&߲3F<#&<#t@t)ht; fhRH-݈݈mF Q܈I܈=Fa"<#}-:B5#&5#>A7CHhDhLBlu؆ht \F ވ ވmF A#!#$pHQg%$h$h$6EA,W-nL2NHHFunNſO'Ĺa )2&_1]HHlun040Q;CHGGb8E j i'C(,o$n$Lq#Q;ʿWO̝+?X H<>W܍,mm$\ڛDk#!k#dmXzR Qx`y##a _I@#!#aR.c is$s$6 GB Gb9:涕e,y$6̣ʁGBGI6  `4h҆aI##y1?2ϛit0.GB.Gҁ!-r$r$6{K8 bD9A#!#ALPHHH|,ay]/;GsUSUűJoT4TSV7Wj]<,7YWcAyg>.UՇi\(EYo?N'G,Px:كx?20O٫O޾}Ng]?eiţor\xr_';&Q ֥ ^s}]|=2w y0;U(}(Y1Z\-nEPAGFD'庨S_oMOdzYPȪVC>9ϺYק=x^OU٤$|]'JxAvOhE8~mEt^N>'Aqa <)NsfyBT`ZfY&̴\5x\אEyXYWErYtWM-GMaPPizmv$( Tmdi8,3diYѨ7z:x7Ӡ__˳OZ|4x׿zzOxُo߼|Z]iZ׿bǯ-_~}uuU_?+B-bśgg/<ً"7T5OJ=˳7E߼<.OP?z⷏|ٛgo^zJthm/_yǯ_߾\O~_Ng83_N_ ^kU]f uwOiq2Ԝ }7RlIqO04 l8Og'ϊ7iq!>~MO߷EËoO_NO'e^k;8 zׯ"OO|s5yvam|wpmOGoO?'6QhaPi.Ou [ݲ% ;z{޲%j*_fMsYe/Kh-۶[yhֆ|19Y_N6A] nE;vyڰk㏖A C#4y"7PO'Kt\k%k)O yRLj}u띌{Phl'Oӗ {Th\'OӾQx1N`'IvBd_xvAvRl/A-۱Q6:=4wH)r'Hj;zyҦ'nj; 35>QIUʓ^>N-ùAy0}2'Y:Nd^: MOe,ʲX>4>k)O 5Z uq`m'Ky¸vTÂZ"KyvERvnz$_uw a0 p#H S]'a3% 0ʣil৚fy}R]8PD$mҶDv˻)ܔ'ʭouI7E򄺵_mT/q{:3-E<ݎz:]M|)imPPU@nv= HSA\Җ$b'vP4luCP <р+yeT*O@^oaʓֺ@קpD jwk{CAE$ ?w'*Oi AED>7Be ő viZ[N7{NwQgv7,D*OVak۬~~}fg/NJۋ5 CI2cnևmJx%wCʊp =T*O˾0 5CEx~ϼ CE=3/^k0Qʓejq,_m]ݼCPet b 'V$]~1{[^DC4x32 'İ2ouB)U>v=J5I@H+T[q]Omה+ {D*Of᭘|)󔠱@~ގrz/nvtAht 5ړ[(mF)jR3#Mc1;a4bA d, 1Swĵ-I<C($?}WHs b " TA `X #hR&hѴe0Swf b8C ON-`tB Ryu Ԟ BZ&|P{elU$OAagATus@̠ 6&&7.=Ⴛcnyړ';&$6'=qC { jOhˆn"NIړ$؊hЕۡ8δШjR'5v{= P{oqǀ{- P{rc {, P{op7@d {k?w+è{0X Ӟ[Q`'O{nGQޣN$݊b`{XӞܿ[!1~x?FiDK9~;\~&v9~ړww$w1$4'me"'5u>_:BO=~&P[p 5}>AGۧF*MB$OJ]:HGXӞ0/QNb2MDݎj[[r&O{v0{3|ړw;w7kX ӞнJG`&OCT `36ܾѐDi$߮X6Jk7|Gma܌sDOΧ=|wt5dj9d>MR$}!z|d>IBPI7' =|gFDŽ&O{,1].|a> NL@0Xa@> +楏Wb'b2Mן; ;i8_vGjcLDx&N{oEN[=uMM?a,_/{Hi'N(,Di4yZ.*B&N{w_QӤi\l/脠LIӞdfM]T#MӾi*r?՜: ۆ g 5QtEwwE"NN;BO^аlK=tFa&hN{Fg׳XɗӞ|fr,|R(l0q8U3CX,iOQXIӞb=\W&?-&_.Wk,sLB֔u>_?t|TNg7t=ƳɅajq!=훟2Yd2=v*&EJOw17+aǬ!# OT!Y~5/7.<|}rY8U?LyfzD n8ѺKC[;<^ ZTaZԮ0}. G#Byno%9}j7@uD:Oa#[eCVQ̛7?ˁAvX 0s[>?s-E8ZCL^tԒ9_K?e94T+\czsW^tڹ9Z=R-{¸T"Ņ̀kٍbWd›"lBθ- e< A#`+8 W.ŨxpX _+G!= Nl8C!Q}TRl:S<¾ $.x-IOpQ=L /%ۃG (OT6 <;x =^@6:U-fJx_#:@'r@"*cZ']eOC"8xKY&JPo)?}|5gTw?Q1u^e>nY{Ѓ Q5YCkѳf?6Wɥy&lr.~z^<?69zW_?g/_^W*ᄣG0s'i̤=8g U]z;l&4CNdqk|ıy:ݟ'twh)(vQn7Fkrp&ca<˯_&bri>uS+b5꿋 -攞 ,ʯ̋s쎃M5Uw95gj<*Mnw/x(xsAO~Q^qg?~3z~7:&Ź(Vzl~Ϗ ^jp 8EU){^$ mi>N?i5=vMc*B'_<u w5ǜXn$@87U_Bƞ;wK_GOV'|TZI0x 7/9wx1 y-Yǎ<{fߊǬV{ˉE3_E(k_۽;hNƳ$/Wjv$amH+l1Nۑv#q NǽGh#^ǣK}Gl8c>0/OGWr4 }m;'rNkf S#Hl3Z|T%mIl 8dž&f 0p7LĶ#NxinI~Gr1mPz mp̧uHĄdsÂOۓv'$,UeUdUջum'.#-F7n=v9r6A?dЏ>,.m=\_W.96N훲#쐷.Z` mbSO PTl5[XpsŶ5F;U^)ǚ>0}3Ŷeʮ3tpsTQ{?p^/MuBa:٠Sl;5[z@߁R񴕊EPi(;h[iJ_<'m#6(mbbpD͓Piۦ%wz|E,􅛓$I{3xrf%J8o:ˣo$IiKa"-D/܊{1u,ܼOJV1[𥩻eBPhO;ZО"q${"v)nirsrkBHم/j"&$m ]lɠ"Աc:mr.gC)nyWOCU?yF/+^ƫxjwO_JaV쐥0Rե0B_}.#ss3Y]3^=NT̸Pƫ뫗o^>y\ 2\J+AOe6⪽)^ eةʙՕ3=,D)nLu>J.C,rglqv1^/^PÇu HeAOu4E9{aBe~忮?wUא ú0d ˣt? yA?C(úOc[c3J.C^zw :> .e?(ǧY2y:>°# y/f j#xJ*.H걲Ah, ^B* ylCW ȯ# pǺ1O.O о~1TVVLmYxxf_ȜPo 1`; R8q s!HS1 cU`ݎހٷ=Oqqiֳ 1 cpw7yp=c,[|5@Nd@Ƞϩ2&$®]9ISBjC.V-ƪqE'Y~(ZU\1喇O6:~x-ԪQܶ.9IH {j|,%nc :/+aY͝4[B\`"ȋaz(,ls˰[-ߒ6 +%A[yX JbǼ̈"]u=?XPRDu)'䮐Scq%K17k\+\=_q*6]Ũ.w}\gs)s>^pujjza٭BKlIn*^ۘ'2a0$P] qN4^"ZL1Ocf| \;$Q{kuqw.<~ e3Efd_ZVex !aw3ΆW.F4i4ầ9LnH"%M1dXvM6k7wv )SZZZSQYSĬ)Ərܞg;>"M1?;*j'l("uN)c\@›bown&14la:~kj6w\lOqo/Ainz ~V"Mٴ6O-y M\6ǦKol&u?"EM15k_ }4E@b i:,oW)8nFZ+$f'` 7u u֛Aڿ!sĒviX9,)&t? BLeYq(YeOB70aQSD)51ԨV)9BzSd)vrXP RLKһ<+?Xo R6ce/ľxUPhx)BSگO(L'M]L6 Zl1%O"QKy"Gͤ[pʜ3cnq9{J\ϥ@W|]\P.Ujc ړ%ff\/ܬSx)bU]RVXٽl^,}|Xq\߼MI_0MoՐ/e3Ϗw)b"8NBKץl^{=LCKꥸ-),fG9xk2x6m5l6{,yQӳ/G^xscibMpa~pE2Llh[˥GQbf3tO:E53Ǜa35YvP)//'S)O;Ez{f"L12Y^fp3d8de /X S6lW[ÓY'ǖ*(Հ~"L13^8t'? 7"vf 3^!a}'L1dmcaKd MH4oMd- M1E4Y Ή 3EbgKf3$OyÛ^qr dl2wx()&NGLRJ,;d)&ZkNORQeݏ>S6]_!>XgISLVla$)S  [ܾ8(fq~F^K+a ki ^LQ)5;Os/nK_i4Ydy|p=~73_L?||.~\_~_=>z\?,O"83)A?\-^O'g'h:DQki"ѩoT4TS O1Z?7x,7Y?_Ay|g>.|]Gs|k(u>_?t|T`$J"jÐ۾,i`(V9ΊgdkSN+QOG塲qIf3hic֖Oʭ ,j-NS8Z"޹ezm S?~9}O>}^::JG#S;ay>Ҫ7oN_-ZGփwȢ[1oKE""4yMU^GB=YLO|Ghdz0 '֫~^>-U4P+ 8‹Dݸc!=8U5Jx+->~1_ C^O;j_'U ʤn& ^TU#*/3_eW5' x@/1v `E*W0d?dGe1Y_ =g|͊\6;l_m:-o%5epq)QJ,jn"Bj2d3Ԯ*NG_P}1A7[(zyȅ%b>Ʉ|!t{8I0O&s3zټ̱t9dR>y*ZߦӕN6ng )d26i|fх d3y@#$LDf'L:GZ&2L $t&[,!6e*J ͮMU97U"_R q$\LoھA.&a1mTL?4BH$&W!JHL&ClB&q4m֍1Dc`LN KRn14 6BcȘ%5Hೈ )<Q-mP-^ Βh6%b?L}@$}V''fI84L/q^<+-@@FjrWۏSbst>}џn?8R,搯1TKP I꘵ E D*qn@⩁Շ&@l'€р <7AAj2ܻE!!!fRkEЙXHB[%* $$!«yRDR6\-*i4[u6  A~wX*RLX_d(D)b)IpFFH(!i޺0pej̎);?z{ܣB?;ټU_I62͌r S&)')&9cFyHZRZRL֒@[zy߽<=+: Ggk&E&D51$[2)2)i rssM涬k)ϧʳ`VW]ɐt%uTߟaBN%d.I4u^ˈaRaR6Ōx&E|&e4e.(71J@ܓ"ޓb^[b~ $QkiQ%'7Kp 'R'E'SB"bҟ.8y!$8+jl?N'f$G RDRL4OQRiOUD_y~pyX.^7(X!^7#*E*FT9gzXUذlJccl +G]5'T(tR6Zp'"JJU[.W*M*dU ?,sՑlld+i5Ш& ڼ}^i%! H^Fr#;"wH@#JJ31Vt.\8Ff[4"4! A}BP~ ]3P54 <@U;@Ո҄6 T(o*9 TT˦Z!P \zɩTT,kjF&fbTpMLHDLZGi#KLG+[\FHzUPi&Z*M*mP tJtJ۠SBRFJ&JfRZxM6pU39U8Uɩb-eB׎`TTƧZ[l1kO#JJsW?ռ,%ӒBiDniBnir˯midvibvi˯VGa^`^$9^8^ ̦]$iii&6iĉi‰i&NAa%L1ML1d 4.)ăiƒi&L1P3>KU2t_5ZxF/̂ObtA1̈́qTgT1N hfBYGP_`0 JfRZ0ig0L f .ӈӄL.m4FLL444s:B' 70M01?60XLjltt|/M|/{qt3/[Jz6^^چ[Y__چ򡙹YU;i2#W]1:{zi۳<*:^K*guǷVg T%xxaa Kxk!c cT2MT2m9$E`&`$F>ӞcR0: fRŠF϶ 444LQӼv\vh h@].;Ј;ӄ;Lܙ@E?U9g8g9nwѻL3f ɥvAf@fz 3 3M 31A2M2̈́mRnD2MD2m#HjQfPf2(5@&f̜w(4@Z&ZfX LLd[y:1H LLd2;-nH#D#6YObbچf4 o֟g),3 ާ)a+G&fbXLf2u PϦqzizik +! <e o)44SBU>u'AyFqvwA}i&KQUyiyi&+0#/"4!4%Q_:Kd:OwHD6""Ҡ1a0aچ kߴ"KK3^`3`廆iyV -ꥉ꥙Tx&%:ѹ0WK۸Z om<,xT9;rnKz,M,md1g` 0n{Z4b4a %q]m^i֫pLgigi>>L%0U tz|8KBq:.8WɹrTA\+M+mCXkK KX*搖i:_Y Y*{;׬ YiYiʏηwȼļLCri+M+m\(CDZ HbQj梬M<i/aIȵ k2V;^!Bb[LdDV!h a*)*DUH ɰ*$sj'9_S|h?Z8' 2XZP`{K A+<0K>!ҶBm6ږ'-1~L3 WH z"jIBJ@ #I򰿺BI/T|iLHZ!Ѽ~Cdl 댦ެ*BBq\@Sg\!]l;g+q`П;+dCS+5; ?F9.BVHZ7G:{rS} ]4B.K]!AB.K%BPWHr]j+ѻڷD 2\aܳCm dC$eD ,*!+$Vd`15){:޹˵@K̫ƼDGI sRn,XV!e吚\P m7BBD6HB$@D m(I(p/@ #BTh#D@k.@$ThA1[0☘"*$&TdBE \ҝ 2Bb<\F2VyMѴ;n8K!`,=dӵTtu\ m;k>o{}6O?j~^N;ه|N!=l60C#]4B&ȉS${51JX|NFFX *>qtH& L2DQFR;8D!CwH' N2D.?ױ ^XD!KFu:LL8SQ t( "g-\;+, YژEz﬘H+8 p2G F_.O0B&B;? GcB;)I!QB%ɏ^)C pH[!?2GUf×fvSHH!Bi<=A{݌CB-LґDVb 2QL=?")$S$0 q9ERHĥI\bH.=@jRHԤFMj")$R% he=Nu0mKu>VI/PU>JYA)i*F~KX5BB2\(@O祌K!]ѓ)))DSH'Y'C7dyEdL!Bi̓Oͦ:It*dN|!tj mu>p!tD| '69P!qB&S*(znb<6S&M!BIfu7HM!o^ٽA UrB:6Ӻ:V QQ!B&*BWN0 \pBMІ{ڂB'K$IrZlѺ2DSH~rSͼl+#ZE1Q"ZY!@*"TH oD/Fk"`J5Io^[w%2w,DBҌ%H4[lCB"XE6:SМ2B*VDTIh!*"U\pl"WED%6R"TE6JUMS?n^ޔ"bWE6v֔"bcEL6Ǔ!ύ)#cEƊ`,A/})#DaEŠ(hEHl1VhEc0V\$VEDlĪujsDh1VR8X"$REDD*vr[;])#NED)Q4u|/SA"&tJ2 4A>1S:-\lXT0{J ' b1!V>T8i*b"XJ88o*wU1QUt>L*)*B0UD` oȠATB"NES]"OES+&Go^V-!*"dUdCVnlA*1PRb"LE6̔g}TKE<1SNjWKEM1T-YU"Q9"B[ELGo "YEX0+>R!"&B3-D1Q-D ِP[!A 1O%P".g?6#EE(ȊXQKwH R[X>1O3;ʽ"D*Am={!*".UdRy%#*"rUd#WI.]HDy"QIBLFZg H{5k*Ⲧx li*ҩ$Z(PTE%QN^EN<ƳFyrgPX c@ɀ֊Z~ Z G gdZŐ\`+wHΊ1Y!aɀ֊1Z -kɀኈq9\<&"+"DWEt^y2 ""sE\2,.urE\ W=3A"t}hZ nQd@$oEDފMj]Y+b7i-%"`+"VdlU ,X\% X[媃S2 ""iELK$4bEŊlX-nzDt+H'!*"UZml.yV񬢝"YEijl #RhM2 U}B )&Sl#:Qe?ϼMx3O% j2?1bBALGi৘O ԾeESLqReeygJLhg]`˧:,jtz'^ts -nĄL(>Pr *沠6]ё)))FSL৘ ~6=IHw")&Sl=ma8K)܆Tdv`M t )& Sl2mI$zRl'm( I1b!}b98iJ1b\ijۀ#"i)f6A{(ތd'#R*md )& Rl"o2u(i2p(D8n-b&l`mG1qb6h[؛@QLܣ=zX+@I0ڂ@Gڪ@FĠF ܣ;k%N ]1RbLJ>ё(f26q8j#(&P_RvA 䬃7u,XB%ԾCOL蟘 i (i>1|b&GWuԞ=ڳY~X<1\xkЗȇQ'fzx |YT ?1~bgvtF0PL`j!'&Od:D/G`[#N̤09-U17x: _>囟}\"x'&N<11ybNj= q{&c< 1x *'!TNDcV𣵧xŇXK"Q^ ; ei*|%Iϓ |&GAJPDg2D.J%IwLD'aBtgfi^n֙n홫^2YAw~X~6)].ApOB G{V]o? &GqBPB(Ćr]j( ~@ n R$l&1EC䟄? ÉEtv) S  '!Op>AOB$I`IMb|$;$I|!1> a|O!'>$Il@ -n>ArOB䞄ILF ډ$I@>[Xf譈ʓ0T̟?Iw> ]j'}7IHIؓ0=1'a|8:.-$Ilu u7?М֛mEvPB전l.(a8Mh%;%J0y@OHs'a~6\rړ'aB{x-$IlО-x$Loh/Γ'ydMjԓ'z9T'AuyB웄7}~ )[I{07Pm&am6k-$$DIlD-2tVDIlQ8e1e 8 !q&y[+[I0a:]zy < s zT$I|dlܴ@C}y}o2g&.v\Koj]t).QxL sHD=Ve~]EN;]&cG U]t ebtZv%~NaH.H-klaAvQ. ;o-$)EZecB LP:ZGv =ͫTǤGՆ7;sEN;]&tGuBN:]&@'@!1KĜӾB8N8]g-:f1|tN/veka:u%Nk^{f{F]@ <]ttp-lP)Z'Oh5DO>]Ǐ=LCrEON̟ӾEO8?]g0EPH@] HtH""KH hQtKl%O׆EI.Z.ڀ?vP=cpb:\vߌ׿kΦI9W $qLS(*/^Uw?(Pu.>0Wtiiy-c5NsAyj~k/LM(h&ʷ#O'ɷGAh2GA6}anG+t`b/l,x6/2t'0R8Rw&Ih2/df'k%.",YUn'ч=7As(G&)PXpTt >Fe&2%:$ٶh5_ WEOߜ:{,)\nƒ#k.^?0,Fp7O>=*QNK?G(#gS'&ұԣ4J9bm2.C =S. G*:)ϡXAz=~˧{٣KGIN=6;h"?nô3,2wqt4N9Ddׄa $o rVϞQj24-e+aĴ iwaT=5g菗)މ6»Lۗx{}d]E'B&'A1Bdx۸/2VDmBcو'U!2F:;ٹlv&*e^aLؾhI!88b3QQ['fFmo݂!b17ܴ(\/g}Ykj)Fv;۹ #;E#6.$ ɥ9y_' .` Nu|}͔;:N9SB;qmvxjh߻ / nc .o,])A:muDfȤB:=i,4Agg5u3!ѡ[C#L2f:o}SX: n#;:3v$(+As"º #:V~@:/i54](xpzӛ~>RQJ~`($+"+&q| U_ÚAO? XMqeoaea7ŕ-8&0(gu*.@m\1A=!S?{yzVBAǕ :DW\޶N-,Y7ǀA58%+P'RTdE"a1$UkVD3+2զnό3Y5x&+hnq3=X (L@LQB0 k"xBHB"p)BbwEf=zE*挨ACYPl<.!(QQQ'8* v]蚫]tEe*l X!/E0E1ݛXo6yw;QTQUQLb7O ݫ2s]AXǏg?{ ñCBl"n[+*.wqVN9?Pb)E/(½(&9fCf: W'H>``#H*wmҢl-Lsd;QN'bb4 '{k)<E@$+ -C $(B(fR d((eƏK]5!hA&tlEpel6%b2eaݦ 8z73f9j fXe\|P9:ʨ EťlBڌ"܌bfO[VHQQ6nL9jWkO'~^5MXA2 "O'WYҪ/EeƬ7K$=#BeQe+ÓuESjBl"nbcxi|1<3.NtiеMF1q2СՌ&00<!̛A6"8ikBb*L<~a("(G"čb2n8^^&t 66 ޡB*",ri  FF1i4]~y"fg*((&_?7Z,z,X1GwGdDfQGH\tqDlR5~}ȣ$nk|a+Dň* @kK((fa?tHQQ6N<$} GG18. \l(((.E N6 $?oQ6:wકCWybb #V3LjIiDF<eʬ[M\ I]ee0øFv`EEθzPETeVaz3(Q;}RBƍ"ȍD 6a Ng?18=v lT;Cn>0HcN+*a@K8e6 uVkt/[upFCUGaN:ts݈#;HQbN™b֫,F”&”l^J3U|M+44?zE~&~_҄L4TE 1,a\ 4)M4)mIVGN}iz][A6 Gm S6Uվ9->EK*U*j|1O{7å7J57A&f<Ӣ< 4EKwOΞ(YIjf1X0Xچjߓ JJ3iU /䖵].b)Ft,TQĶLCQ"M9G++M+mC^+7K-M-h 4u6E҄LdCr-B&`JxUœzX%UU ] ]m*KH k )QrF<&<tw2;NcZiZin/4pɁ~pVچ}%Ҫ4ѪV%InǦ{ić$+M$+m#YVV Zo.SS $3] kdUibUi&#?UiUi})M)NӓU~*M*Pq+=2VƼ,K;IdTibTi&u+y[io{I\i\iʓΛIaiai. !t7+M+m_m,;%f$R4Q%.Idfibfi3SlԈ҄6TVXXM|DL"(K(K@Yo&yg8Pz1~.~o?|<RjпJG'|dr9:->Ϳ&7:+]TNu.>;x+*ky|5NsAyt~k/M (h&ʷf3ۏa~}2L?Až٠?NO)?⯼{Ӂ1Ŝz_ɷYyfiZd#ɷyYV/'Gw/µr\|_G?<<~uI:a9\XN0 r~\llJ8G^z2͝EEk\;Ώj `ri-kiw$Z?~|>q[*n]~<˭'}Q񃓬#~IsY DZ z-?5: G+'QZ\b2*EF= hˣ¤ȏY ^y`vKPFv-oC>Gk~-'in!m$vQl=RlZޝ~4r jX.y@2{H Β*mܮhXM߶oW-Ld$²p l. d*=| %$S2t&|~ݬp)b:=}Sͷ1Fe*n4V<1^{Tm+vUѼ*+Kc \Du~)s qfxxU<%˵4 é.Y fB^UGx&F7Y)ۀӟ~]>2 2,qnn~[ .Ryҁ;jpn18Kra98VS1|yd#Kd)geImUԯ{a wj׬}\-RETs,9iL 82tf:Kp*O笂sA#zr' uU1)ǪwZzxoi.DaZVKW#XtO5[)G$ͧC~-?qQm37`өm*hYvչvyGuw# a_ o/n,xr:>t~D^\{Q8m?d7ŰXOX%TK_ܞ>DGuy?wUi:_5d~4,]SgA J[^>-cT⽍ fodZC_ Kh4lAp~TGL9-V=1u| r+ۖ:81Bmmo C}\ی}Y!iS݌ߴc=x >zסq c8yȽ37\_;ZCt\LEOkŤ(#==F^sv{: 0ΊbJrs)2r:鴘T0U:tZ̳YuAN;m>*Zz:-fԯl<L&I:ѧüh$QteU CNNsp=};Ut 7Ta?tv?`YZ5Z5Ar+sVT%L-}Trt+ 96 QI4(s+̜3'6 wrTKtUԺƭsUA8'ZnQ\%,OI$ igcڹ nr ]UoyIB߶"Pvcik|j{g"xyl;Wk|=T- E8M}z!cy\Svm-7vx;t^X˷c(] OOX~ OXY;sX#OOyIBM?eczǑ IP[;#v}"l@@`#':>c34 <1B!PPٸSbyQX81}/ (Zz @E$@ (AJ_wW!OOyUA?]Y<`ͪ#XUJjlE%I{e RaS^ZȔyJuWMPp^U0oAGAec Lg6HTT668lʲׯ{>SB" ~F t/3BdB_%b!d ɪ 5GhTf1R;E;v/cr1vj|;;E;eCy?X̱]!NN4*/Ξ .Nx xx22$󔍘笌ya @KIB"ahw^A )z0z?!NِvkJ8ê`zb'mH]Pl=%h11sԦc٠?Yf+}k+=7 Fd+;'T(لS6G[Ļܮ60sVUՊ>E>Dm|~6`4MA^!TSSLh_#>E>Ťm.\Q}X}k"yOzO޻aW(T>4L+iEF-,V)TفABQ f \N;8j9|sjLgĭB"b[s0f`PW0dCW=1 (w}8TD8T6a1kч؇?ܶI6ͥw=^ *!* q+XKܾDWQ_"ň`l]'+߫LhhZB (9*9*qks^/ %ƀ6"r,B"Q^GEG?z)#QQRQ٘OY<푊! Q s(p( * *W61FrXn^>Wyx'ܞ=ǁ^8yrGC12AK$1vy /C"Ķ[X\5 iiH_(i)")~Tn$u;/>'B"Dt$l)Bl)cbe9t?65X-E\- %  4~7DǦ7Y_N*A"b"{s? Zb _j(7;bїcK1![,FBX vR6Ȗh|:M~W7TEPl.Ml.mcsni4b4a4ܸ4Hc Sͫg9߿<}ӷ?7xu3aF`.M`.s1Wz9H#KK3\ j^djn"]]$T1B#KK3\hriri|5zG ~w+ ?xj} }F&qZ;~h.w݊u##]\4qcc]^۱ضcifE#MMl^I3Y,7_s ,iͻNn_)p(pFk?JG&qݼ;:w0x"䛶!߾F&曶1߶0g0!g/iO6bYlo&<ՈvӄvL:mjDiBi&m3k[B&_PJJLVoTq3]|@رP# M Msih_V.EtBoK1m}iӄLVľ#%T T2~ߩէvtlg8@&q|e5'Kec!&M&Ms1i]xAh@h B.E LL`e3ccZVi>n=HKmX2׉gUxnm:cB2m#yP}M 9DtjG Gm82km^??AȴDIEgl1Ml1mc9.xH D ,b}4t zJ-I_#KK@]R?HD6ҖwH-MH-mCj NqWpWچZ u@I)O$m )橪T0tXҞQM^u{ݟ ǩ@RRچw Z. S@7K ,mc`Iny]4ѴIYEr}.Vեեm/%@@&@? m୭6$0!nIaZ&psesqI#DliBlib @B$jD YD/9 +ѹRpTԁoZiJVtvX@ŧ7*DBRili殺DKDx"b@Bdn m-#b ?Gp]a U4 czp}]}}-.j% {VOV)G6̌`Uc1U=WG<x*dg&L**LJ\+Ѱ>>GMMg]g_l"*$UUU7zΉ  +d| 'DY(PZa ( rb}[Ъ5ar=LXet_"g!u[3 +d1{,f 'pb_-d! A ˈS8e۵ʽ"-$>Zhm߽rn/``-d־Ռ|l/-Aڶ]rYyp!Bn'Qq!B*_r.Qu4S m֯Bľ} Y7W+3?8p6'.qOZ> t| 6`s;Ҝsgo{/1B",^bd `~_9!qB_fO @! !Wy}Ufx}U5 W0 wv;U!C_*,1dԵŐ!Z?}b:o)s^DG Yֽ*F蛿/\rUFq!2$ehQ~!+C_0`FLƴl, !a+CZ5*[*f  30?|/Ct:o)h_&|d8=1Q#)z۟ơ0c W=>6?:Ml86O?1j+yx1M?<[Nu9g󴟧A'9A;s.S|ڑ,tJ+纴lJ8GkVKmZKCZy/ 4ϦEI7?Z}FߋY`lyySt ց'R0;L.K8x6}`o蠘 :b4O< AY:}}).Ke_@P|ye~úS,d0^+4#x]Y&)4LȁWTzw0 ޾x#y#?wdgDuK%pd8~ XuDx *bU/ѲOct Gz }ٻ{JaxHT*.VVKOrUࣖ(JPX(Ҽ7Oӛ^6ձǼz%'֗Pyd)RE.y#b(e O<;>뷽 ۋ^,ek1]N7WWT`wEigk!zjyO Vxj<$FJɍS|~KTh{CgcIѕ~wk駼/ b:&SMÅZ9?Շ9FuՂ)򛗃]yݶx5^X$ahQeRU9U4"ZEOjaF0&dzr`Tvoe^DH8$XhuwZ, V'\ |%QHl܃ 7ćG08O{yׯJW^}7f_4(otG9wBЀ<6y,nE T`+Iv%TA`+ RP=gt luJڕ'~%o.-]|CQ:MѐSֽ-~m<bKxtʢ%ѯU(dLu/~9;9}||ytCX@95FCA8K˫zJeׯۊ@hv`:eے)w56/ !SZ-+}% nKN_B<1MKrĔ$IbW֒&⃟ ۲UkCqȞC\O}vˎ!($fPmhE~v>Ru|⓰+2##y@o ?7@pI N"ՆMO'xe2&$ufk3@G‹?jf&<{uN\ eIR' A DIe$(S)y 8vr?Nj?p??O`:{n9r]wvkmz-)w^?+.8nt_izwۭmw]cv3:Fw9vke&(\HMu]vkwㄯ *s髹c Pګvy{oeO xq7.i_Z`'ޭxVǂe~i,ݭvEzf>#G?b{'\fڗ1}a(@b gxC1/Erׯ{Q:$ÑLA3IiZzOT{#wi?]s,];#G?INhX.L_H2xӣڛL18ځAYt#ky~ZQ>1w0w2kxf^]pǵ{F FzŋGknB +%Fx *+l\W;w1Ϭ5''mΞ9bU; lʻ>膂"{-]-o))&9NՍl8Ep8Ťñ/ntĉS@qRЕ$N(q['ӏu~rʆAA4{ xV&|b).ֲquxukmug&.z4I;vvʆk}a[zXԻ2?}P)Aqwxw Zx#NN1 v,mR#:E:Em4NtS;%)D)b)g8i:?! =7w# ՈGZ8cF" a<|G`9e#}iL輲jKB"br~!o/ OO1!|bo/`AL>e/܏_~ʆ۱_x۞_X$)B)Vz~1}8} cr䟪*g`[UaBL"Nb>&sF<">bٯ;&nȔ4LX K> hrecA=i!;ON?Nȉo\Y8_SVMܚ4>FJ"Lr8NU;TD)b).#k_t6ljʠ#SF:9")2}7E7ŅF$HҔy>8uhh7SDSL\Nrݻw WyJ kqq ༡GV"XbxKJ))}87E<7EG<!MM1Yn\?s4-d̗_Kg0"1ҶpI B"ض0}m_M1kP*Nd3"MM1Ql<_ߚmmʆo[tKݼHSS6.7U=ȎSS6z7C)).'%44@`"b!ڷH}S}SL[pI5ɻ!.*a&4hXDxS6])yxv.QpXp[G ,\G,nMҏ{>j/&'l< NxCcossɝsѺBL:腉R:u\}Fr&r< vv+ N:vDZ+|8jiiΛU2~\'(6rithtF[w2<3ga}|3\;j_'k*HD6ڜw]ʜQֿ]JQ#NN`u´N1idϗiE#3W<*01(-C#NN@t(n,t>(KmFx&x㼩$:ii4Ι}Xss )& m9B,+Fx,ƛﭵF&o[ wK9WejEgO5mîyMMve{N 544&S^X/' 䛱Cnn tcPr?[L$ M3Qn<5ņ;ڴ ־C"&"ٜ ̼#MM3me8kĮi®i&v%_rHɐ"SMSMۘj[褉l4v^ˁ 4Ҵ.i{ؑ{`ykxk[sUKŁF&fxH 4մxhC*||m3G>miiFN&Nfqʵ:B#LL{b5#ˁ;i$i"i&ANaa Z{'t0Mt0mmae#"[f{s<}TNtBD6:CN|>Uj{<}}Gm L``5n{\|MxZ*B:Y/ˤqbHD6ʖ~:Wj"ݺ jDgiBgi:UAZ`]2sW:Qgi?kb :9Cr&r)9K#9K9K3Y?XXIhK#eYiYi3G?I4)͢I=)˻Uk$f@i@ijj; nlP& [ [تdl 4 d*C&X#8\eSzQ]*jDzS@I*EVm0L*0>SijkOhr9h>tjqB@UHɻjR)DVUHƪŒ@OІƩCܧO$9u(~nQB? m'q!DTH8ІO$4o6CB!??i!ȇ P+͉$H zC9YdDĈ (>q O ;%1 UBKx;WFnT!1B}ϊpR!.śqBOğ m)x} W2qU2v5UcbMϞy+)V!QBŪ9تU [B[D mtmcBD\ =!B&HUhTme dTL%BltuwY]||` V!BJzx48W!s%Du n9(D,WHXrV uRB䀅 =qB!+$Wzm0U#+$WhC}yόhI$MYTC4tk l<?xU dAUO#5Eol+m-DYH86jAm,d1\FeVW Fv mh@3";_T.Au12%\ȹ/0BB]H pNjbd܅ĸ =1B!.$D] Q'+#Z.$\hCˉA:u߆Xs!5w|x}2BbʅLf2Z/B"ͅ;%ͅH 4Hs;D\H<Ɠ2).eV2q!B9n[ 2QqLkqW:GY*1H 2)pa,޸X3B⽅L[x2CyEgOϟ/wQAgJU'JDJxw#'ro?+tr m(RBLJ>{duEZ`usqw#z:yE,n+*myuE#X<2rKGdYDȲL*l#e,"XdI.Jkw戻7^ !8,"pXdL+q"nI܊[B"hE6p.K{3<]^,k R"pE\ Wt(l!IZI^Wz嚁Q6وOWX*| /Q"E,@.TD\ƅrK;4BtTD計jI}Q O'b4ɏzBNO $ fi>L~K*bB0[d!)"Sd? %{M~?(""BFE6dPGDB"b"|6GkI0x8%^W! }!C$&HSH "! mJs["FHL$FYW!=lqc`" ,"6.K=z>:U’6,:XެD7W* TsA%]s:RR⧅('1r?-;m-|t7m|gG|(w%PJ@JL E*Pb6 ӿ'#$&IlÛ1n+rʯkGIL` 6Rm$M2wfGJL0C!^{HBD;aU,\膈r('"_6K}7xK"IDQd$8Il8Cp $AIXv)sׯ{gzy߽<=IM3ZQ, XO(P! PE_=ϳ4(_~a nҼHFG3Q m`No?IGFb4 鉉=1ϋhwo:0jar|mu?Hqm~*cI_CcJ+txr:i?ONr w}#X蔖K̦y|kӹfƴ4>0M[\lho)"x'P&6,ȧN|^ɋ !lF?O!P4?AZ8x6}`oLA qP~$+fj?C"աBwYL/z, +c&ĸM~I&&wԧ~o9M\DqlX0Rgclho~::ݎ`!VB;' |ɐH @~Oz:{pѼ_N뙓tnuk.>?PAp݄cQٵK){tOobO G ?oەH#\FI.S^>h' M_Xjq6973]*7xwY[IL@IeLH?ѯu?}Σxdi^7S)13_ #쟍Ji>:&.oxOXkMhCMIJ ,rSز3Dz @!%< =2C"&m@&h02A_+MTڨ^x"{}gPƄ6bP(R"2Lo *8=8)}ٻOt?Z0UfGvLrXUӫbuګɾ J/^xʽ6Lv!_Rem8C7"%ue0 lhK1([u>).%3mU԰mXy5߿}M$r)n;nKr{x߾/.L]&5!f(6Ȣ`ۋrF_,.{ޜi/&v pׅ+Vy$#(n7~$#[HG&8 \订#*8Ñp7Io=N@:7 Ƃ2KH,Ij*LHOHDXiB_ 7F$<&_kXXvYƂhF"3ΛףqV練zrHFqD6 Ɗ73f4_޽8F;$ N._?z*ILIRҳvyd$% =%,Kj>ƥ^)OkE:m #ɤHJX/j?&|$PC:gGlB-mq ~mGTs䫓18xJ2y%]?B&1iCL-\H$'P"pD&׮#~OJ%-DKnfr\"%m\1x7bD "4wfu0FWaP6% Ex ILqT &4iM$罼(%8 ߽x!_ *|.14x + #r^S"(!( A>oˠ!!Nѐ ѐАd΀|pP+$B*"B*rvÈ, J {ggo3Y:NP!GRGR19b *!Êd:ySڼʃBQ7ԧ}]Tn+=Q BH1R#ߞZl):{jE!h DW)qjal x"+6 ,x|scB9O;B"!֝A'b=''E'e<ۃ2¯az_sa=  U/o't9Xf-~2l(O:R@)"@)6s!ȂRR6T'CvRDvRl74_WT*W%Ug&S:(OQwѯJ1PѫiSn_£%"b"8Kw@ǻN(;)";)6ډkWO*d`| ažўwHSoWdB)B)&Js'E'Ť?qDVDuR;:):);)&ىTҤҤkm%E%$.r .i*d/)/)}%E%e-9kᶊpݟ7[fR%E%e.mig"0XiilP#xO=xN9[Mlu73e7R7RLf26AޟOrZpNiw9nnx#nuN_HV*RD*RLTOp3\&R&R68#b2HW_(~$9!HH Ek<ʗ*-n<*g+ +~B"b8K!)!)i %lvvut7q+R lDGG = oh^_>7IAl%guk -hcGnB" } /9UHuRuR67 ^l,-ثFU97=T "!*AⴅaDJ%,񆱼D0}K^{TV4kfW{OـKK^b 3v?={f~|Z:ǤȤD% tlտ)osDƓ"ȓbRi+Xr囟7AUQKbbi`'Ed'eC;!IIqILѝ6d.*C|.k|ѣ믿3ᤈl-<DjRLTy, h,F7H|&4mt"^l- :m_^9{[))"))&JJɩ!`IaIl%ܼ9dc;g#IIM"~7TmRnRLr@?m!Đ\!IIٸM`8&M8&1iBߑŤ Ť(MlAk.i.R4R4Q^#[I[IJ|ʒzOL}۝F~cH|,ΰ]]L> ˰HJDJLRf2\UI@KHH@bI MyLA?BHznRG2r5ggm<2414YκQcu˜ge.zQ#HH3)FgZXXmĢ- tDLP13Ŋǐ_&޿h i i6n(d4xp 3J#rHrH3C2 \i&E'4E^&^񂶰qH3@G3ha:/sBݬ=zS"i&)Hcy&#HHۀ@hh&8Ӯ:ZJW,GTAAR9s}o7K M D u_腉 Rx Mx S"4pboMȴtMKLOxG&)H#HH@ےe¶wfJ|ۧRO M m9HYZ 544$ѫ\5谉9!%8qzz톭@ @"E.44E$QAj}EEw"""LQm^|6 ~y}pn"M"r>[ Ox"n?4NŋDzh"h"Mh"D%9f.w:;kC= fI[7uDL"͂>lJdjvQHHچFjMu\N:&OGЏHGii&c M1ii&h-~DDzL"L"ML"mcmDD"N>9G&f 2/"4!49!*m**mTW44EBjk!mlA?@? 9`πMmCla"&f2y]`57*DwFXl2.::XG*4bhOLEx&x)}N3t/7f 9<8<uU6X$dG:3dLFO^zv~VMm8K"/:uR3MpB5ͥңk$Nfrzx}$&#AI`m۪R+* (ݧ>? D 6bкEnϭͯF@&@f8ׯLΐDQm441244@b3]HB 6NCt, &q'GYCޫRH}ϯA9B4A4 [L%E&f}6^f F78 .,#qDLrKO,7:h&%${4{N=!{BL|ozLҏ{<ݩzd>! m PK_i!B"6B7M]C$ D m$!gᅱEXB'P-ݓǫi2 K ς:l|6["7($nPu9xjhV6@Y^_a'WBzqO*ECΧo}E2_S"($M ~(я0a(d2X<5c+J/ m"2F63 f`F?l m!NsԻ7IEDDؠІ ʑnT@ e&=בnx@ fEǭ 2aAxd|b ޯb~y}ٯb}eABF`j+6[30C[0[裈%XBH ;(&Yvq/5 8Ak}؞Q@jPHԠI HD<Ex5I$ D mm4Hs4*D~QH/򥿧ҨG!BhjD$H m$uGk[u" )$RȄ!/$_B\~ԎpI!Ңa LjYRU5)dR/. /)$^Rh%yW'RD+_r 'jRȤ& ($Qd lN>Ot7Lb)d"'A7LI.}.=0єB&MiKs-!B2"BLk;e- [2E,=@PHb6˓u Oؼ3}(~?]7/Q=)gI*}2]dAMpph$ߋWwhDwCQ.ލ :><Os}_ '/M֨?>qzbG#,-ʻ?gPآ|ivO6R|e;<|RW~|XR\-ou~ó"w蠓t s ~_12Niu4qlJ8G6lF_xQ{fw}M/{XAp7KY m/~ًoyX&;,;ql:<]] yq1L?OHV,HB ۾y2ȯGYP| (2?Ɣ_|oHlH툧-`G~σA\e(hR&8{vjzY_ # $p/=xQ)qp6,rο+\<pa/RDlaQ7vۀFsMd0^+@B^kI ۋzsl%ey=`Q!ggm%H#}?aS4Ef3Pop_azYWΟ{޽(w[7F;iuJ:p_+؛?LĖγGbyf)Е΅醔mhK6䠹.8ľy & LGC)N_ OI12 x7N"1-_\% 2/26| ~mY[h*(gf{׽rulIT;qN;D5! Ѥy& J*6(k? hGէ+,7ZKqi:mZkP"pF=H7eObAŝRG:2‘fVjdXq[|yuOO_==}󻗧o##˺#EehMGb\sdf'`OUT ̀z/Ɍ6w˅/%/X\R+缱ܚCH@]UIb1ZKHɛݦѫ[ɾl1-]moIޫ?Fz*%e˙CRn +yZ PZ /+Ol7Y=o:fltfJתVr |B/]!+.ʎ@r*^7R%0aex~s%93FJY;mL OSA)#>apɔil E)ݔ9~80ٺ,3);SK׳tLK[%,],S5)S/0e26 ۥt IJdfkn.gѝ18 #7$틏9$lݲbGhk4|˓r OU$ud6d@RG[g&PfO[M],;ݺ0M<\`GjZrO޸@E Erm $$}ǵbسZVcvm--VZ][`J^ik^lSe6z s޾Y7\]]npǜF hlI5m5w3V2Sԩ֨K[zQϝ.ݶlx(Ɵ dp|ROnO^Im?]?>^.vf^p~ʋmJԋU*;rjɒK/u2Ō V72ۂ })èΠƠ;HlJ}@mm@Fv𽊻QO읚cvMLԋy("%RkR[gR1̮)5"e!m=ts4d-,rR/R[+YY0ة2>켅,lJXmX!:e0uK:UXdEp:*[GUAr7M=bi45ѭ5PUQoTUrOB"bu5lo5&*))a;V!MMn__kݔ qt ׾Dě"ƛbBޘ-V!MMٸnb^h겵˔j@6ED6eC1rhHNPiJ>f TV)")l멬+SD+S6\5+|#bbۺ)@"17RR,W[[z@m'DE-echu(DD"FAd6[=#6af4) c j &^}P;n章S^,σt>E_=ϳ4(W_~\oҼFG3Q5kTVK,£<[ cUMHFWYO7K"VfbvU ΣO|B;Q%u3?3bh/ڱU|,= a$ςW/^KEoF}>xaUيZXG5{= .@ /BqQ5AaGЏXe~m \C^lzBizgcd˶v⃕T(hlmg^V+'E< Tq۴b  j[VnRiWMޣqனRV('M%AG,*6/Vfx@ez4w%DyѲf5[ׅ[G']d XԼ3.D: ∟L:Bd֭#渟v!t֡[_Mºnuw߅h[GS]*ok@Lխc6b{'ob/k"k^¸um~_;j&gj[;ׯVD P5"@!j6T$P* o_(QU |[%Q67U~E?KKȤsL|YM5LWyltetrUۻ F\`xN=8 :h.Oy;O?6Ig !,/UYeh{?f? V5-r@Ifo=y: ޽x˧ީ6>}S38`Џ=.3)/{ߝ-\TCϥ"8^UG׽: {$wn]:Y |S'׎7@wgՍm~vSooW@m\~{ :x<_ߞ>vd:ϗwCؗg?WŐm |DFK|vw.>iO_l\]Mn6.SEw19Yy 4Cg*$觙xx`T*zAo(3L?ⷵ-`tiԐ56^7~dOEpOK"=5Rsp C ùLZy8/8y_b;suwp1<;5btM7/.L2]scR]'M<>|:k|q@^3_J9^t|\zrmkh3Ol4Ch5a7ŏșD+yD_;E|6{5tʗkM{kpU!2nM~C1 ν-ųOAF X\O?~w.I#.ףFcxbṴ^g={7&VC{}j:-}UgQx5|E.X >{wdqk`\ޯTaz;UhkP[y7oLehf^E2uQ vPb:W|Ag:m"a?W+^mw=ڮR])9˦ھڟGL>"ڟOg4qi|[MU/];f~0 ĝ27&~6MFSxC3Ia,uᎧB[J8mS[)E+"x["Mq>Dì?Pa.ƻ੻L[|Gj?;.ow]p)w=;p$ (Q픏<;Ryʮ2& -*>G~| n~ȳ5CW_{T{#ϛdP8Qːu񣵟j.vGZ] MTǩj#_Cُj~ٳ?8 سGRFoƵ=6Y *P] gl`ee?,-% i]D# \q=uy&j\Ukcpǵ?h?_\c>Rc8|?ǵ?1r*T{lT|8: LŊSWpӛli[IV[tpw]NʸNhA F?>X*r/:x"_,1cª10:<><ڠ1S ?{Gko84:xi:A>廙2:~Za/"ftViqY8X]-r7:۟b۳TiIiv1ܩAwZ|bFO`;NNճ-D!ioZ{>NU?%*r'R#XnN;M:\AI%x&вdRgoIy.nQ(2}޶ne0K 9K@K'iiukx, &E$& ŴL(tDZRPK'st]W @K#isKk=m<:j0Rbq?Syhֈa҄aҞ0L]u|5]J7F&_Z`wt7tШ&<2**[6yC:_=' " " 8ؕw+ hhҞM$wr'$4 k,pN<׈҄ҞPPRP#[GQ6ppM(1wyl3OM~%:?s5i5iO{=ʏnN \9lk>'M't[ܛF}rnK%,kAiAi rPQ壛7~+B ,8LS$9 ;'UJETtxD6J:E@gO~y #?%T:*ΑxRƓZH1SHJUa(?AO*mVU$3H#r)VXtҞ0URyJ{"T-s?I0UUƨZ3#d~{^}b~ VZhA_zgY)2i;酄+M+m#\kuݛ׽/OWZ 774BM+m]Rѓ*ut뇐+M+ rՐvѽ_O_KwOcg.WZ%̸~ gr9M%t6ֺa˳7u1:&&Ē >pXxbW&a̙i?~;h_i_iO+d-:VJ(Ҫ4ѪwZ׽_ߞ>|/Ct:o5uA'хO6?s7-G'u0hTWw__J^,σt>E_=  (_~nҼ^FG3Q5y:O8'ՍAž(レm?giW޽b,qzt1}M{]~/nfA> .G,jiCq?H91O{hGR{?=g:P4xat<ftH2 ү|?'4^( ŗWǘZA?rtо=e{>.l =~ " .0 F4xqi1ً/:Dz/*%Άw9Jߕc ):);!#] bu_!xQH?HHO?TE<S"- G,صDop5nΒ6>2P?@U].FWZ"^vo/<;027L :?ޟ4!TYUkT3*> T~x!] ^l f(~!b> ZA5twy n:pEnr~-rgI\p{RyLTWbhN{D\Ș>9i/'8k ]|RD Z^42jiXV㠘)l\̐b> 5)KE6)xCw8Ĺ6*领&8ˑ4ʙ"zw*wUKEP>pT](x4NT.ަb:{Ag}qBs3~NK˩VX.]`<7ufx쏽_}u~l?XxIyH_Gsse^)'oӭoAgD{='HtoJmΒK-58A>T|.Fw[Ϻ#|X NZp>a, pOeBQշoPxFwi~.6.;o<&B/Tm^$,$3blŶbK/; K\>/E۶mW=ɤ҂gmOBQLyhmETm+^##(RT˽RnBn[M-s<GfN8XMR#d';S=8\k؎Tͬ- 鰺m]T,g.f t@G5 J۱V5[^-VRKbnKx[͐[T6Jm٭/U,k[R'mw..pLj*V?JY$Ͷ{7kf)5QJ #MS4hZaejm%_H]SٵQnmJu׶,8C*fVNZ^;`4H3# )-MJ~zh*UC>mͮ%Nt_vsbLb52=v}%_Wy1ɛymR ];9sxMzTzfrOKWYD/gńPɇ%^qr?^J<+$)[#~!qؙ6MZ`>^Cm@Ѹ|(-VN K6#QJC7faI^{žg}{}yϯy/a٣Č+'#0J(%zCi%Gߊ( ySjo1 ?Zf70zf>׽r!N_.;zL!9ي{z/\Y6.\kE%QW&5B|ԈrDD"M-b(0zFnick{ @l(vzHGQ"(2#(թ<٥mը-n?a{0H1(g-%]Lcp-y1KI GgGFGuqu9t9@Q҇Zd73Y'{%,695-9'eւIr=_RM/cGeTjY̜f~*e^{".Ss>tWZ:*rTƂci> PI*<@sU,JQO+[i]x7vS~FQ@?YW}*w2}L<-toZ-u8cw|{ .ȍW.Xmz(ILDra?]{R\[\3R`s\5P"fS[ׄlvKn="NAEy?d`Np_ɥ6t6tc1QxZvsvG<縑|*u@wvG]S;s[Eu8  KsNX ?RBjxLLU%G=bqTʮP_%'XHe{N/H[g>=ҋVc/>}.;^=]q .Ԥf/Y>.f{U:4 k|᪢oݝ1 nt nv<hsc9-t @mT?*:[CmճlП\sANC?}VO?/{9|.C D2TKhC;إNfH#р ^ /*& T@XM^t@>_HfAJB0 f|ᤒLu!B4q ة/(w0y/O;AW= MMpZbfge`NI*:`5)uԃ<)g# &Y"IIy=IBoJ' #Eb"d1"`.b߮ȷ<X]lD'mG9չv~c+"f$lbr6[lBIO[0!ЈʤlX-&*.)/)y!`:M.{eZ+;(xpVʤʤMҏƜw]*_ EQqRlWQʚ⏓oqtB@)ʇv5rT^RMdV]d;^ܯ~\h,6_ (<B%tbTU/P-'ElQiO(_zETb y5k2a,-4Um 5R1, bX9DU*A5)E*E*WG{8K 9V@VF򦣳C"bBxK+del,4ҨGQBFaztUdj?=TlRL/x$ݛ޻7?:F#U'8nq!3^B9V?q4%Sݪ&̔q[tRL><ڠ@wE0 *E*ePo45l)O}Y]a60N O|*7?tn2+nUFȪآ*S&B)J!JJ1)Uq7.wD[|-ӪiVpVɳUݙjnA䱁w/j|<#3K4K٨YNK2r!:K;KYkTY]Wr-uRRL)ݶVR-K1\$2lK1\gSg\dZ}p^ Ky/E/ex KK1Y\8-/{7RDRLF)i {}|ۀyGi.ޑ$*n1)d(/-;MKD-ּ(+-X#%tbbӍXpDR;Eg)dg)g sF(GAj+wdGf/xn9&clg̝E5ErIʱy> OKTtt5^9ݍƯQ"'2>J+Vr_e%Up ďWC@Q ?uQ "G MϦ?բZ;cf8TQ"b%v$RJ"%$P(CRdzKlBI0$фگ;> QAY%$HK'-Q~"1$K>,.bzH,5R鬘W~ĦSIRM>)ݐS)Inc8S6G+$aTϨ(TƛNhPC"o | 6$Oj*89½X[výw%T6$~J|\ہT~+1 aW1'JRIP$QEn>"7(lmȁJlVI0Ϭn\>תt$ժ" ;PFuucVw*r+_I|k|U/<H>".$YgE *OK+[Xp,IıH/n~XKNWn$"&(/eփT3 '5-.$$@ZiqcMf/`1 9`%}&W< Y,9Z/ )^T͎@>ѫ}YbK%W~]M7SU0 *$`a[1&D ,&Q4ӤicMlI0$wnN˻@`N +/ ܗlb7v༤j׈A. $\pk.[҇ogVdKqI$O ʰ[\,.鳸ϭyzqs,yI$Q❓Ӂ+'\sgtؓ?X%}VyY,Ihyf^+1%ɒD&k-{ÒaɃrXsX8,NLJ*?3=x|f\>_$cyk:F$R҇JEkzKi)ןgrP\$%}Tqƿbej,kr5 D j+X/dq_pJfYZW>g)ZiѸA ؕaW;],fee2 q)K ,JA`kqQJīh\A7oj0My#g7;\Iru{`~͐޵[Ge-x6[ zUG|"a_K%IV2^;.[x=~U:ROk˰&> h>[ ɱ &A>no&}X}%IIпO \y{z9|^f('<~t?_{܂{?I&}hXZע/&'ϛ^C/L֎M-bUfSot p/ 3κ9?J½;ҤKk=ʘ j5Z)5 Qj:tq5 j{Ov䫯S&浏7F$m҇uoUƮf;A#q@Iܘ{IYgnq9N^Ɲ-Ϛk-s2}7S~^PܡNĺNh3m+.ʀq_۠ã,O$ݰ6$xduRb=+e9DۗM5lŌOP.J,X J$9tJ@ %tJ /lovw^(}za!JC}[Fq! Ul(}a{NUwA>cDn"NchP% .;6-euF:錭N9F"qΣd]7BVu6sB))!ج su<.U}iͱCӹWW ].#5syЪ7[s|Ps8]?l;TU*}zmзIq>mUE`禘Ac!=_ ?qóGoO~|{78aTQ|FiW n@,ʧ~f}S̩ŜFbcT|i^oRB/UbQcy/UG0ƊEU '} MA}S}Sķ 5REKoحYm|2+/)l*D^M,*GO;kuRVH(ʧ0ecUT*|hnTv"筁\J*խObxV.TH'ESOF`Tv|AܩT;U;U*w@ XRcI9uWr0@/$`TK|,҇0T`V DSK41u:U>4h:(ۛ<z*puTӶ[9O "E+3H'õRsK[ozpTϥQSw@0L&U>ENjr: t󸞗e^L*9c>+R )^qw>pqT<,*KI,mu<Ǧx1Ua?Y7^ߞ-:6?`U˪|,k9|ofy"]绶+BUn'\ίYygCU0U>4t>1[=&Gx{g$~~i Z#k;i U>ضC *djZ:ZUoc*!){sQanWHn tϳ4s' /WL0~Y`,"6}ӗVAkǏQf9a~?'ĭ0=VêSp|;LX0QʰjA?_5t^f`XH:`zX=bFÊ$ ` \_Er}x0R sMvhm7dcTW:Eux.]ȮmVv(*b~WZw>i^*9Fw A`EWTcW:%qTcWHnk赪9 PBl P\> YXU窃RVn(S嶭Zn"S3Z`f)7?[Э:t{yHl*pU n wU+eVh ó Y<{h XՉ8EW"/ܷUUnpTEStUFy @T X1BU$5x0UU$T8 *NU)G.B*`Tf:LҹRT\ciT4Ih;\c(T#Av.8F>Xՠ4\cS;e>|NM9;xL)yO>otsތOg;OS㯫<(MOM”_Wg_onźw3#avPɴv3[uQ?\\I?Zz3^%V{7ř9IuiŸCMs~ f-ƦodstR|O/f:=YgSǨfƺ(骖w2̞qˢ(ͦO)>z$^_홟;j`}^??^r..??_M1&}'}ߘ7G˟!}f*oSzq*4f5f*ԩWessWkwQNwnQo} i_Y#;&5)8k1c:?]wR$ew^KD37qȉ~LTPMo_4p$ a}9Wͨrn}]c;@#+/} swO*>Nճ-C[_}=&ßw$? \9?.Cmp*>9R=Vj&̱>vO=t:Av&m[1IcjbSoz[3죙w~kc`%]vb$cd#4q 6ӘcIqĀ愝9ik>~Mij+2v˝m/Ӫhhz˫tgaG3uVQ^fvt\zV\昄MLlc 9M⌴NͦA,:ܮW?ڗZFGky8kXN# c.j0v1 / }bY`]Fv.0$>],P^,3:7cd[l{2:lY:)cqnJǬ,>T6́#`}l-EH E Wn63bփlC| )='!o|MX-gvC(>}tWdq@9#Gw[V1aRX=Qb20  m8(+tH `M(S q 7+A$mT`,I$Ir$g8Ͳq+0#qW`>#wOĨgQ*0#q z*qq A$AS]<|(5,qV 18;DЖz)H }dGx)H }duu0u츫mFcɌ"I_Eltz:vIr|a2m?a1]r.YV!6񧎜glqv4> jΤG 73G W!x3=>7IIGĬ#ybpr5*|)0[DBG6)b59ڮu8]FMqe@?5t>݌0Bd2KB? ďDW/7i, ɿ (B |l0el c .6ۘx܇Qdr/O)Qc="0"tc<PBh9W$TdׁR@_Wg˛#Sn߰N {ܮ+Eʄ"*c|>8%lڞKc% %Cq3Z.F긖?Żdz?;^]w L[Nå"%>Hq:h<\,Mg<0/.E^^zd%2:Lu8egoގ^~?I7/Ce/q2/VSiȜ2Z:ݜ]n}s\͠Vé(fJGXw6.oP&>%t>\1Rpg*Z ^~:,>:W:W71m tnnsE`/7$\$qKU eS=}W, Q0 Wa{i{D ~eB\PKp$0(h!zѦXշ‡dƣ}>L6cv4M(Āe8?PAPZ器L U}pY(l^{h 30L/ғ5v>vhu92q҇Y.5{Ĵ^Jڅxc,2/~X5L*"yqlnS1d &\/rP\ȧdIk=CcEf>Y݉Mƈ-B #O&)m b(.[w귴jZZlᵍL0ږ<޾qHywlvXM7WUz]L;Od<''AQY^P_fʎrq}qY^7Mls=n9l06"szɻb/`ʛqL忬שj|c^ɿW_;l0nʫx3=Φe%źaSćv3{ CTWM]*m9!*æXR2,{&L+d5*尩CVσ1{8lvȁ/e}I?n -GrZ~6p-Ln/ >yjnc8}d/1{=}v?} Ǒ~@~$&ǀ]>}𲧉<{VHHۏrb_@:K Ϧ$1}# AЧ"wo ;V1;ҏ>Ԏv8ڎv"y;(|'ٱʎ}Do|'D;NJ<>'8<|^_6Kky37 pH AuvB`! |~ةZD$( Q4'z.9 t{Ʊg$4&F‡_]ZX%[=΢$hh`ܬ! C"msa!B6޾{ x!^|yD`HL$4~o{\>xW"4H̆aG#|v?_N{S?A~ڽ1#@DvDZ#u{{=HW)..յHmE")O.:pMG|ub(/  HP V E=>Ā?<GNCG*&B%0#T(d=0X0#T@MTEdWv*sf$J19x `7G#|r5MmX@n;h=ʎp9H ,oH`a$Ԏ Z;r#  `VG#|NL`"SV&ª0 ,``0D#GZ٠Hln9Cr:hq!FD2oNF^`q5ZnVG^ )7*{n}, nϸaWzadPsn.&GT]QQJ+nd9' nU)oz npqFYT>/Nr/ތNޚOgo釓6n:XR)BIJDB?Uj1{[5spvvo;Sjyg0''F9_:a&'IMO -OMY 6 W[ pp-7|lm1jj906fD#G])*껢 `wa[|ZΦg4P<XAuyW#DwH)=$+\>{g7.gk%&z$=2m(b:֛GSKh8Q٣l1o q 88oQq"  d@]ѧ{抮ż|]WOF&oݭ7ITo(t2`gRIlHi$ѦN$6h$4hfnN֌kFԚؚ`H ?b_F/#} 7S/IlHd$ђi3bF# ?'7s ?bE"}K7[Hrh/ҧtSqEO5Z$-'tS1"V>Z٪oJHPR$QI>׹{(<Pm3gSB  $*q|gH"‹q $+A| W=W$_QN<.IW8wjz\ZDh;{qI,HV$QX l}Ni+3W`^E"}Jՙgjw~t.ILH [$lꔴx5(#}Y~Ӥ.@H"9mf\ϮWѼ<&fjЋ$B/N6j5c?zH*k}{'XOx%a$0 0 %_$/ cE"ʽH̽H^${)_T՛2^"FEZlNn*GP`>*0m!ajF5#}̮bWq 6B ^@HmtnºFt.H*g)W3.MTi>D\j# ).9@ꠖ0wlsp G,8о}=*TGGmhTq<'G󼣌zQvЎA;Q#>hrh(k|$H>'zJF\(yaH$'Z} >$@sk$x@HR= #DKH~$o@K H~2'p\tZ_Oh^$>$ =+A S\fpx1&$>L(̠sͬB}74CqJ$"*ΰAWX ,jcuH:$h-2 IDpy%H.QI$#D)IIbn襺8^0L$&DhS{tZLI bQvNy|:_" l$E@m DZPU$T9߼| peHԢ vqbБ$BG{l/piH I"{D v}O?A\k-H"{f$0J2l6$Jg(Xܤ[uA> $$I'U*IŁ+*(I$^x/I$yP#Ib#I$FV7.Fx$ `$I;r VYI;Sx 5@%:q I-AOy9<ܾ ||pcI$ -mt⻹MMp#1$ND8I>У=h4IФHAV@+I"Di:w[I$鳒OO4I"h4 œ8Z6T/A0Ooeˎ) h0$8X[K.V륳x( /2 ȑ$"G̃ii;exKL$}@R((IDh93-)L&) A$$d"I^׷l5Y/"d !%"BJp"1lXMX8ʊ){P?U8XR͌`M5.OgӳR2:$vUXtR :)kZ (Z+*E)rhURC)*Ej<;渭9vpO=B{)q,h@R q/R@URj)iz1oƛ0'&@RDM*88pq*8TqIXR X)`rnB¤JE"p*EĩRRF S=$:Y.wb_(>.d¬JX8.S)N|p+,W)QI^n*Z"jU'/'PVGYEya>ۧ|dLaJ}|U EQbPAYQp Xc0RD~+#l-K ,K+"WFQ{C͌NuqJnG\RDƊ~E;hj$\-mRDJCW(\1b_l@R>*+ڕiWߐXR S)LE!7:.Y\RD|j ^yQq\zR> VaIˤ|,{nm{qᆂ0M4 ϫܲ&@M6"c١0uRTԉHe M &E՛‚XaR0)D n;*o(+)Wa"؞w HK(-т l?O K-4qPIѡ79kO޾~ƿpqIэ dpa j>µP%CGbHwI{`KG#[Q J %3:8>阶VX,IQ$R@Ui( %J}S$rI_DR@")*{ķt=Zl=/&Qr*n,M)O L?ݦ??9b02p^|zUon|;]!d+"WYmFط2#d ^,l"Y1RD*rCba)ఔj`JmPOvb2e`; W\͍ {6kW +Ӯ:`pJ*!f7.Is$_TGW +僯wj6ƄU]ĖbK-#$ڋIXR o) 2bzK|V`ӹ{\ <.OnXRl)&,Ŷo|>ĥ~xBޗ_K}-ޚz?+j aZ\|NVg^@¢QKE-fYf$c)"EظR`\)q*zU[1WR@Y)e.yV'[upq-J(NWcMJ&|yu߳ n@p~XV1C)чڣm{:؋EzQ{Q(MGzҼbu:^fw5M<{@c4Jh!jʠB.UP'4F4PBgbw\n ؄`Bi }F(ژNoas|wAcYt>&hh#=fCcIݤj7il7inp[%& ILmyAh .i4\JܖGc#IF~mtǔ6xyKc2Id-h)iS "6L&M&6 $XKg-yi& 4LW/iЗ4Q_"ضQh`4i$GEcIϤ} kRÎXk6Y9`YI}w(V-i@=b`HO}SL6@6Tj4|40bJbJ}SJ)J)Vگ(7nqiҌ=<1qͧYJYJw]RR-*㋽+f/qɁcl4`4>7pRŜ`}|#q }ruɹa2$R#99b))%BHB>#' 1[xh4_Wܒh%Q{VKH)HHOBj?a((%RFÊ|+;cȏɢȢHQ3;PXf}Ó?ĠQ QJqyǓ6&R &R3"Pb((%BE\!JD7/gœc n:=YgSɪx.yb_>)uͬX_Eo6xM&'z__j$uV~|\\~*ƛbM"R?gO*1\Y? dSQ?m3zq*4f5s4 JuWevukw?Ϋ-4u%5s4jFWq&f5$Gg߯OgٷlT?]W޺X},&=^ x=$]An`vVKF`A7k?`o#HT~`o#߾yӍ>(HK>Bn}V}~%t#ҾY­g1,.{gI?"O{'9*s7]_-~z@vJfտgn/"1xFO>a캼̚Ų=ew<[7ޝ|:u=Xv/v6ǻ>3U_ѫ߽+"NiI̶ⴽ7綞:E;\˻n՛›Uތ܀XU݁my~3_",l*qy*87* oEga5|͏nxW+2 /1 oS__N%c䇁2UE|)85?T]_wVsyb_=2C0Fx.1>NE|J$qˁ,m_;2z5TUڿp|,LG퍽._|}?~d8=Y֣͊cӡO(*oJ_R~r\Xᷫ*v[No:q{`ʆߵW|oZ UߪReO>.{j^ޯGxq/\޷ |A4`ghg?ؑ?vD6f ؈"VǻmA[A>MU;4t?)j_mOm;tGv_;[=vIxs ݾMBL1|')jRKfp.hy- qic/e؜^Ogͦ(t~Qϡ >'9"aOOTq _4Sfq榙gX6ά\g\CtȮ&D5+Du;fWG߼;+tÑ4A_b14Q'u̖ZB00B V)Xk}F3nvT팺u6UެY5E5#5m?48/o~=Ͼ?yqR?6 j]?,7S̰8& yg~UË*WJotNdgMψϮ##-@t2;=k|FwtMΉCֱݼ)yR74R)3iӜl1CgZ74'N{OUUcqYlw~[,GE8opN|~Lj~Uu-F%4oJhN|TLmq̓b1D,LvQ˛*lٶz: >P˛rծASE8 !v)*-};ڷQ͂#\~3 ][v{3ko׷_:?~_# fDXĝ[}a oj{/w}طwYe6W 7Wf5ؒ޷#}0\=ay1o\ ƻæݓoI;7^:ݜ]ՅXiT{̞{Öׅѳuߞz9[Î  !D>hֿ~~=d9 ܃O޽6aS[JhhP#hS6 0?۟ceD?,dz1_ >E>q"¡s`}Dن}]`v|unBG<om/`k{>N7`/`{>ƜxzՋnT/F6č`|?X]q}NYH4%B4U)niC+y;; ;mNj|6 ^i:(E-Ҡf?=Kl)$802/)?ݓhiϽ׌CGsM觗'W'տČ8Jp޽w+ JO-YlQ3%,wt:8M'crbh-n@s?cc7SM@ono=il<@t~gźAr0._d>%ٺ)7X۩,?>^sG^?3zWn0CJc:sqAd\!Wd;3ye҅ו!_1|Ic.;R3iyQؾ632V {>RTua1 YA/mCz/Ҙz%'8\)Q/Qɝ'J=#Ж"S$[w꽃jaAӡ(C 9>fKVSEe"*#C߯d֘u3Z\Ͽb.UAc\OPj$|碹 ɋ펩1l@ZrSx#]8e?*k 4-1q3jy|2ێq?cf2ZfW908geyDѫI Ւv[BSă]i6%逇7cp{ $ l]8dXǔ֫cmͅr;A)&kQpeHhTʌhb<'^ͅLϓ%]s.9MI@)qx5ɋKƀ>Ӝw_ټxu~PM]{z"`ݛb5ޔe|r1w -#أaW} QO1R^ 4'n;GI ~\o:{0hg}vܯMz|p}v}h<;Phc>J܇7POu`/NɈ5cܿ7ܽopt#$Y"g$Z 37>IPUbtY8:¡Øv.@9r kxQm'=jAڊ$  bPD *,e!EDxap+>Y>uqٵzoҤh&nrnbeeZN>̩X]D$qauI$|urei ѪXn3&(YNx|1.rD$'VgXJ+$v>iMV`I$|Mz=!| qV0QDǃIyg:Pv %|JTp fvz*%"RFyRᇶ4) ;R )A2c{Mt;O=lS Q [?C5*Ax!\,?؍E*ӪY* !< N P}[MG$5SPU挅S˾;?g\D v:R:%>w*R4 T|*A(MSLK j?bJ%R=iHm 1%IB " 2cD-n?v~?P[F2%|Tx.Q "HEk<;#J 8.%0/%D`JzW S)A4{F`OX@M 5YF5b;\WP@O].8+G D B Y D$ʊ(\2>*hVX* T n͜t]U)RF܆XEU7lSq*nm }'hf<RL}:>2QHFμwɰ'd0VPBI\3ll8a!J%FR^.d,xRJonaJ34VQ"_[(xQ}z)bysۛ>>>8 vV8NP %(P@ۈs9M3GlI ]y/TzmLx&/ _ j:7c]I$|R H$A5'i$|QGH)? "Di<7`Hz$|Q9;G#ᓎv߄yIR9"Gz3 F9 $L6 @ L >obI$|;W/\C> -1$?> ]# Ff>2y$&$GG*^<I"xW[>`sF8#:RܨE|r$xHsLbHV$}Xg2E&1i$4DҨ)2" jkLE "$@L^v7oׅ}bG#ꏊN HA Fw*ր$h@ZξSq H@I"{Ԫ]*'#DgĄGR rl:kܑ'Vy$<;bs9{`G#*,TFy(GRQ}&p!G@H"C;X0<{emnâQGD7.wH,o$7'p&`|up G]`k^/Gؤ3XΑ HC L&t$@:鴟##AȑD!v,ۛJ%GTɑXɑHC\ĘLG1ZpsL&Ō1u% I<^!{xJ$%}$>' 2xB?Z!bG#}O {><I|ʻKH|$Q.1#DSH"%}$>򠲏IJGd;5l?3=KHp{$wB.#Dˇ2SH"< AC~lHz$ݙ6u >?HzO <"L]bG#~aG#}>O;udc80pXkG^Us40I Ar$ʑt=|lH0raQ-#ӑTO@yKHpsi?A`F~# X7oE "8b\@Dxv5oq 88+0M~[o:1 $;,&w$;D1xj=I4zڟ*GTHóN&SaGޣ|xϮ H((Qަ ? Eo86UQ(m0Q>'6 TQ`(_!`SaG8@QD3 > PED}g!d0Q>' L B A?(p~] ;> Et|6#< Cx:8߸|Qx3=K k: 4IӁy벅&2 TQ("Ǔ {SaSG>-l@L ":_*l(sAv"9#b1GĜ0\(zv4w@QDE;sY'X .vp;hRlODTQ(%)l(0} j8OƏdp'`=ʇoZ^GؚCyJ,(QsPXQ (Ӻ0Q}}>HשRbGޣ|x32G5"j>X %fz0=}z?? "<34ǪUGUJI,(vOi?aUG|Naf; ETw1a:k\ހQ>yI(mϷKW; o&!1svp88cfOy!׏jIʘN|57$\ Q>+@nɽg꺞ݍ*(@ru.Vw;ʧD2x< `y[cgG|Nw.("S޹ 2eFzl(0uib1G|bNmi;; Etv}{34GHqg?(pz40Q>t=rʡl6u"7B&ϱqQd# >Fț|Oc+G|VN7Vo7.XQ(b_&Y(jϫ|6d8PA5ƛM1},6ڏ&*rgw9MxZwksQ`(ù랝1Q>4'Ru2v\pQD[v0C9 r:pYF5KWNjQٯ>,F|M'W0gT$y^ 3 gcU_l:[t85q=hhPi4Q{M2KƦSFM3<bČbFZe&8CAD;ҿۘsv!"Y!l8 %š Ѳ=I8$pŤ΍F ƈFSm5!njqeDcFk rNd>صhkq 84¬Ce]I4U^.j ho4! +UO~%^3ACk hq4c[^^s ?G\A>e'NyS<(xZyih@yt$(\uDS u{b&-7+zU'*hpiG-nDW4.h-0;_(5JXhPy4QP^b¸[K-8<:[ޠcGP-vԘh"@%`4GiO}VQADu:ъ ~>D?viw%hĶFSmҡ _7lATfcFjhhPmOvݨƎFj h,nnjQ,۾ZrS?n47ȞhoƜF9%,>XF.\Dv`1^?'ÇȘhW#0V 1ih`miRlFƥZV餞ݭ'5h[~ձ=K:4]1>'8~i7߽u ̷(g£A>gCVEwF &^k6h=ڧ #N~%hMyZvIcGУ}@O bPw4Q)o>}vb ; >ã}Og{:<Ӊ1\Gpz]v"o7r\h#{=n,np8Gvw$'ƨ;M4wfDgRFSgsm^psGNddLMll!li/;F\A>ql#X:hcB>&zG]nχ D=_*`hRdRc]CӛMn1DvDQ͸e{\nsHA#{ir$#f_H>B( 1DS<_[&Q(D(0`0>(NQ4HG(ZFbHp}Q@3Dͨ ɣL`Hg~ {1R)E4F4 Eڇu'# GWwhZoD}+54GGg>G2p@I%zhME'm|^d^v00PD@>4Ivv;IB!(pE_^S` &Gm$▉Ǿ qI]o++gǶR)y/V`+i$za nsqOI=%Zs:9#K:5}R9֔4hJ)sH}#p% K.z%' &JN r`9iDh9wNN@'}PIcI}[4;{qe,!4PPGAGT n=oB.Ogӳ'evϤqJJ<3n3:-חgp&z{6sR)pR)Y)ŀT TZSAAT 'p}I)RR׀>uN)N)v"OyߤsJsJ}SbT|]O)O)Q͎$|=l&S SS장XJAJ"6`7ER~iS SJtRRL*@*DR6l7qT'MH,B3=N6&e 6E=z6pQ)%LŽg>pQ)=(֔b))bM$" M)M)Uhk(-aJaJ}Sg3J)0Ji$F)"J)JT Zut O K)K)XJJ)J)WگNVmqdH?[K)XKZ&å ؤ&j:rH1&Zxpz#[)=lLA_S:>)Q?˟nT nTJtM4s3RR"%4 !q RR7x"0U 0Uꃩa**Tq9S)*saڜKpRG_"eu\JM؛JJ}T}y GG>8*셊tӡQrfR`R#^-XJAJmҝoثQ199ȩVRPRD0Lab))%MuI)IO|:QÌ:NQ NQsXͶo'ܤ^ǦRRTnRcO`a((%F*8\@;J}QF)F7 >{Ma_a-)-)iIO_aD)D)(q H)GҤWHWTJTJV="7CaȳVZJZJ/THwg>o䥔*/in{\]JRy9QQ}Zء R3C1ŔŔ(-6R0R>A3W^J^J \aA)A) JL} RR$L9I`))%zHse H)HODTm"O_aE)E))J$?\@J}Re #I) I)I*T퇓b((Go>Шf^<\@%J}*sj6^]Ղ+9rޮ]5S >mhǵ푰#1L@DzhjPRR/DiO,Vo˫fq/ތ~O'z/~@ Dj0O;Gck(k(YC?\J}ЮG89}ءssKfR`R#ΰndX@|bڕ}I]샚{&{=62SWU Ţ죉=`V #? ?ٯN1t՟ ԟbN? C@@@ odQ4r<OɫCoOݕ8ۓo_}38"Nxtb\#_}žBh7udgGNs}:8!E\J9ɈO Əo߼?y> ʀ |LЎr+tu>pI(#JBqǫgo;1j'f`2"36b PFD➐^W'\M?PPFCO e>U22DB H_ ^aC(C(BQ.B&3l e` e>kK;B8B*/“{܅!22=oJTgkf3Cbp: Qs"El>-J Rv=AbEb>Q5 kHhHQCG%ya.).)#rI Ӈf2`2x;[7XIJj!7m50IIDp|bc% R) (H!q"teMɝJciqf<<ʈ7Bؾ:  }&ju ( Rg kHhHOC /-H$ŭ3xړR|RFTh_<ǹj|dm9>?ru)LN璿pw4̧9E&N=2%nVSSFT(7X픑m˝fX{@{ʈړ ݻ322 }Fʀ|,TMn).`EeD+TDx6{g2}B?j:q:U/6-u0ɨ%}a$*$*#"QN_ʀʈLTw kPhPQ=Z TvP *TTF(95F22*WgV2U̇'܇ssʈ? q/V2P2lSp#.ŤSS#ڿ0ؔؔ&a:HOOx2[- ,gA& :'Z5zQ#|>4]f?HTT惦7?6 ?DUDUF$qʈՁVia**#UOb**;Ka**#TjH;TTF:ܳM L젦TM LgJuC>T>T=gjs~GSSFԟdcfn‚SSFha[}t 8e8e>{ #2"D9ѱڔڔԦ(wQXw@wʈQ%N2BQQ"Yѱ==C1CC1==ؙ|TQQόy|ѱ**QU=lR T:8۸ЂEb1E⣜}zsŊIѴc'VQ/.TT&O.ag*g*#:S۸;TTFk#0/׏& NeNeDt|=\WLN޽:y W<2- k:w pe*)SAlY\oBBLhRC S2"PE a]૲U9rr_|Pc[nVeki+}Uʛэw昱ʁ}URШ6j5Qv.MU3_SQrkJrq N9NOtXy9_t2$rSrSW&2!]חE88Diyn@S@SN!1'NL\wwʉ!' 9fr`r2%iiʉN!J|gO1 :SNԙ1D\7c))'L[9⪝M9M9hڻqMXnAn}rS5..DwpPc/zRzRNԓ\&Y`))'H,RRC/PSPSNb2|<Ƅ78Oy`))Od \x}Sxti rrk>nXڂ)(<%mCTXa@3.0oK[`C*C*RDW~r&JuB^ΧQɜ#Z訚ovFk=T9UbDr2uh.@fOHZѪh wcċ<8W9ѹ|<íWn].XD׃J1HHA*\Sy(rr"TEwy @r"RùϿ0RR1RRS;.ÖUU:8世_C~y`x6L10܇aEm͔;Î:d(N,i iTIԉ=ШfES ̋h9^]t[!. XTXT 2Վ%?Y TqZ alÝTTNtDaۅTTN$ianBi>K.prrGV8h&XX>*0|}Z+| KW9HWO ʔ愔Y.¶UURI,^ ^DFd·] "VI`**'W{U\͑5IZ82ǠUUNaT&X Us:Np*'JU5r \)m |Vx"=&E u .B43'UNQ5c\=M+>? ޝz]o ɼ̚e*8NN7U? K6l@tɨyz~<1z-Z6eaOl#3=qg{ƊK,V_6 G@4jU䋧scuw. g|;LI}6m@AFa6mlǦE "x|ij"ù# Mܴ; |Pڮ|D{|ԬllXm=o5o 6{mv=펛n/>0n%ظw@j(A#\Ab%!bm 6Qm@{ntPܸ] Kmi%?%߈6}m@bg*hlDlDl0DdkA2uTQU?zU8][uWOT6 jiUN/&+'jm7 |Z(&n k_`qm'u` HZD1`ffkJ`Y6f|>^68^6zQ/gx, @ 2 W~xǬBUHAI&@ $mz0t 2ҹH&fx޼URþ.>6}l@`;4= *6 b,O.y@ |dX7(vz *V<%cV\A+.p 6-k @u=z?>#Vh |VgW8 Ȭ 5?kCbe͑oKki+Y쬪 PnQa;s`Y"O 򋙎ؿ5 W[?ʶ Ͽj?aj]Mӧ,3Ex^ۓo_}TրHeIF\!_}ž>pQ=k@ճ"zwR쓁*Z}s205zk@"W~wb'ls m.iwu:pak#":*q⠖ơS3cݫ_Ea řqEa 0npk೽v^87' z A/?bnd2:],fi3mٹn@?IvX 6*a䠝,H a 6"ah󸼃6` 6 B`;R\A/M((A=կ_կ;^gݨY=5wi0 6l"i>LTq>28cYl؀() e`, @p`l l8Q3. Zt-% SGKA Fe<Cl( M5\ҡja!`C"  6$`+ 6lHT:+6 lH(7 TCK9b lؐ/^?Ė,b|q sh!Wn5jHҭoۭbjBՐ$T=ȴ򦒝_m ]>9ǏaN S5jsv\{iCV ٪xs,-1T5j胪ڿ15cjH4hziP3shX*5$R>_Fb~!SC=O"?5$Ss #O O ?5jH7ݾU|v<2wj3=}(r}:cTjЇJE,> k:`G }vTG5Qamfg I Fu!Q*RdFeG\쓎K2PC!p\c= Pjtchg6Z 33̮5j4F ,asý1`4h.؍ąa$7lH\d@34#B#n.3 }P70." }HP;N13g;'*Įy2488o|.y4$ARD{y4@6њߞ&*4B3$? {{fjyQ>~` -&co*Lds =MH1< OhaUįjӄ *Px]$;ACp>'(ZᅨʪP]lYbohА  6 %1w~6᛼\T)GX+V4iE|wҬ1 AF(!B ;ƺtO7jO!hECVC~sk?M(Z˛SEĢ !JC"^uhEu2 AeU&{?o5VԱ4iSډ6BQ!{ [14i裞/-m4$Meia}k?MɓDpl< x?5 }ZSTٱ4iH8t>iWU/߿N Sw\؄ 5PછY.F ٨2,4wkC;"\Frٿ~QX&,j=>Nb\Ū`m5ROnhóŹǰu5j賮km)tŽ>@q\խ wR!^F|_`35SkH5Xg[5EkHThVܗ8kxP8k!YC"%uN5X{=cn\N7.X }U%0W5j3IҷV~3,yp`j@ՐT=4,W1!`TC"F_[ˆ/D!HSC4M!RC |`unq?Q=6>PԐHH5$Es! PC% ,!rMgfN^M!NCD FZflhAaiӐ?)VJkoUb8jpԐG,k`FjԐHqtPCDxnWOC>d٦!MC"ۤ{Ct8,޵Veߢ4M.dw'w2͸14iHěhMw*^skΪsPpiHXQU'LO5QXk Wq D≠] tbV}]&>6_D) PV8a3`։iim&V^F`*Oղim'dRq>Ve#zm[O5KMZKeS'ԆE|5y!T p*k>}?åd1-~1pvȪ=vZN@>JTra+ lMo&-&:[Md0r'g!˩xS;w@1]'HO rY(ؙK:}iD&K }%2/m'"YOlX)ivV<1|ܼ5|99ui˺jCw*ۧn9#Q2S-D܄m-m\]\Nɕ0K[ wjPNUFrꢲuvuelu!^ۋ}[)*[ #1^v9NrIb,[#eQ$ ~`e Y)h4b˲5NRp$(e MwՍe7xM9SقIt87iRj[)}xX`];SC:HکVG" F+͢]˾[S4-D8dK;UJľx eG*MT;YLZwʭmBaNe׶SٯSDK^m N)ֶ.Zc-tIBB"=ЮXXm"%{zw,:v |Xܠcdw@aX R' jaR;a9,u}j=Q d2g0+⎗2#َ;++luD2 2b?P`Nl'Jaiϛ?ٮ0?3;g0y73@ Z/8݁v|XkGm\6w 3Q1,4;ANG! d~tr ^}TVxԵw&&p.λSds[d#A\R涤T.B OǛYb^L=Ok<<}ٱ9E7Egw1˪l3 wjknkyA+ rt/v3wO#l#{8n`KO ALdx2Z벋Vʏũ[(-Cֹ1dTh9ՓȇѢ1)hgH\lSST+N"Jh>MJP#o>pVヺbީ[>O_2'Gᖶ JC8ΘΘ :cD\9O݇;0!rǪ?v).L5z&wg""LXL=!f߻"LXL3FQR[4?'}HdJd'uqv l\L3׺SVR]zD9QJx^&,^&x'΋f3~./}C;((dЎ*&*& +vH~η(Bw1a1A4Ɣ/x;bdqp;uܪdQZϿBn!2ȺBn1ADT/'^ew!ةٖD^lЏDjW~919ytgȍAƄEDn>wS5N=c’cHqb P>&>& c‚cuTLXTLQ12T$_ÌA/ZLh 'QYK !uTTk F;>`eۜrk0f  d4:įF_M   >,,x@|^{ـzjK0ec c'up: bk\|8Nb.1&,1&"cv9Eb"/Z8PPAaeՋڅU+eN˜ cq뱇c&,&h~tb͂pgY˃N- z`(+ FkYTW8888,&LXLq0bJ&j:-7GU:uA:LX:LP0:ne4NmƘc0bXa8bb1!qP0aQ0ADgKXK.RSb%Wn(4y]jSQHrE,S2V?]n8oGH&&BX ୃ/:tt aA.D ;9 _'0ߗ21ԻMa a(^J|eW(Zovb[ZLi1M_AJۈ#`]2AtB. cO!NwanRx3Cu=zlqڀQH奈w!800 d-HF fšfhO m&m&Ywp2a2A%z5p]& E 0E)ci>d }dG?މ%8}O1:_&& =;u{os(;lb)5A6EXIM$D(NgE] LXLu3Z72XLV7n&n&)B,)(Q?9ğTN z|@c,qH3aI3#ط?S9u*f§uq:e2cq?Gϫnq_xdd§u۟p1a1Ƙݿ~k DtL.&&,&&|M*qt=]բ(׿8"d1*22j;˘"Bk =9MھV 瞊0 u3a3Atx].p<3a=3Axx ݡ˄.EP8ct(RLXRLI1 : >^gƄƄO;Eת`§uae?zZŧM1_OCv/a/AЊ}4>H6szdD"O}}B&; `@¡kKXKt /i /3BrȥII&jvɕ%%}W0#P &- &0#z?im'}۹5\Һ\ru2]mCuB[6+/gOfZJE\n#ӗ`^pX% {fNl:16hړu!I$nUt/i/IDmtYFs]-2o߼=۟N$_Ҫ_ҧ~ʀ Z s5^H4oF'?gGN=yϿRo1Idͮ ݑbSJ&&Jr&^/pNKGVT E:l.Ⱦށ/_ BΙT$Ratt9lp1i1ƢF^e$%`Nc).2ccňpYF5IdA49]kI5k׷ihdjdpee҇m;AJTMZMReyGGTmFb}=ԑ#q$QdDrg!Q/Ȕ&&}2[iפ$Q_j]lp9NQ}ޡפפ^;hQ9L|tkwdJGAVA>R I"lFJ')MZMR!zsHh҂h `oHff8ydˎ1&1&un+7KT~՗II6Ǝʾv5v"wža/`_b_}j GVDk@%KZK/%Ǝwұ~@ÃI˃Iŭ4I"E>bkWdq/i/Ia1 LZ L-0F_ LZ L{&-&#av9URaG:bg>IIe}u?Ν&QEH<&-<&X /tx2iy2If#I+ILiD&-D&}ٮ{M_ėPi,i%._Ullx J4#))rFuMYMM4~СR944"h R9BB i;.zgZ#)+)F2rmA))zO =`SYYf>3)&S&SD4jrp2eq2:RVLYVLY18J9JJ"*aa8_:_|uqҝf.壺|mKYmKQmWKYWK\-AwDyGH9Z]6haeڧMNk!YʪYfQj!YYdQ"i롔r4,e5,Ӱ:Hdd wŠr*e*38NU"BTkV#W)+W)\% \bJYJ +NcS)kS)MEi= S"SʇLup;;NN":QYxPʲPBQ~Q[(GRVRD`Dv(e(EԢkqRpܕQAJYAJ)J)رς";UO?|g\Q,<^]Q,|TTׄCC)KC)" E9S , |,s`r(e(Et#(RRD,@wWcJ)kJÚR15ϔ >b<,p)e)哦9]8JY8JErʘedz`PbPʇA17>o]o&?`?%pL(eM(38O u)ـx9?"ZJl(sͦ:S_Ҕ#D)+D)+ERRD0jfBA zׁOC'JY'JO~7?ZZJiHg(( ZLJR&,&@;y7~c'G)G)E z6$ޣOxM63N[PJ@;Ň7ڹ۷ oV,8((TDt`HUh`XJҠ#W)+W)\EON~kjQUQg!!])UQt RTjfEexނT)T)Sox8JYJq+Fnf6}XJgWKYK 2[)[)"oUb[]ٶƾ(JYJȫPEͪͰBrjʢ䬧󴴚ʁp uѢvxt^^g{ǀcqb]g?/6z~A) z(LY(LQ0V,o" OoAidt ,= cSSTltٵáĔĔ"}9ת`ʧZd<!92bȈ0Kamء;:cBG>a849"c3m2SSDzvٹƔƔO 9$5?Z$l,W&)/Ul.Ǜz>7u\-lN9@@e]v[[LQm mx}Tf)"ǿBm5/Ӽ8NݵB ]ݬms .e!.僸k[*n,cƩK[K8.ʰ]|Y^;&B\-x~lR`+Ĵ#ci+ciEb4QȊJ 1YYg_ ^i^i{E;mӎ}BVpX ~bOBL;y^bViӇiiFO-ӎR9vBL;ҘҘIcQpv2m2svB񞌷BL;9hq"07L[Ls(CiKimEFv 4Q3+om U{+ĴiifF bAʹEʹ5q|2m}2ɂ~=׷qw7z,z 8/N9h]X[L,m-D}oWiJJD(iggȟvH`7):>묋)ۖ5>lM?um1Ҵ5ҴH;ڙڙ&jg_GRM[M 4ɁWR{D@N&Rh:`alk ;f`Y4MdBb6iiHDX:zzi`MVӖVDZ6ujԴOR85i:ì-}ZԴ4SS=IXk͎v5m5MT뜰o:SS>O@H8ZaҴeҴIo^)=NnAӴE4M+=.SvaӴeӴMc7U ƫtmAiM[iMM=˄i<{i{iŚgayok˿K[K/bC~iK~in&ޖ)!m m3L[3L̰_\/HbJb$}#]"Ӗ">:&bd["SaHkL[kL1ZDSETk߻pbrbȉE\lB7(c*cf ii:v5ȎCCYyd.8OvB[988&deIp:>H~|i|ii?OǛzsjg_Cqs,ƂGV>kkw 0m!0tJRs6I{{Җ>+8dǦ=KZ)L[)L0Z(f}"a8eՊa'u&j0#os޾zXUy7aƴeƴ޾~wveښegEsGY+=/Vsd5RI)j{RGK:1J㹝ԱRk>{l:Xj( #nEήG)~EG$:Xj%(Uae];*u1(RK-)HhUEAXf3"[D Zx .#uRe)* 4<6bUԑR+>)l~M`;ΨwѣԓM4,hXC⟉7NF'd!G:\Oy,XǢ]އŋwb a?"d8YjɳJѻz\6vO: Zjʠ7K܊_,YJ" .fZ,%bfR+Dl '7XO#,Yv'?„KzK5[) IF-ZJdD h81Z^-%jg GTKD813V[KZ?)>gqR먥>G-&SY;ST9tzeK.`%E8 ˰D- t&,5F t&,Y5ڍ@8+VE<(,Ŗ(]+6eF<-Щp"o)y0(e {K-' DžK R]r㻥wKxU1OZ-% nn5ex#d oK-ߖ6:`1a8 DkxpzvK=~p:VvK72X-=:[je'~KXѹ]m㵥kK}^{ My[}nS7UQTe-v[:^-X[ڂhy!:rnsciDn5H]NJ[S:2udlOf`YwxЮomrՖZ-%ZmU/u:l)aۯM^nY`l5lc`K:kh/yZz-kOOh鶔Hą^ɩsal8nuR&L eƩ#VpK'oZ-%n;>Z-=:[j h^&:\ryuS:^[jd>w#jiqR봥>-Jwl\] SlK̶ٖ]2nnmR뻥>-N1K gcGsK4ұVaK m ~B:Zj헒)ÖUK}Z)PK#jZF-1j벝zΞrp*j)QQӲSIK4v3wю愤Z`-%kBПu9r,R1CKwڕvg2,ߕ.#T>oM2{eDK{nL@I2"Tf"O$P09Rf-h!kzgdTo@#ZGBʬ%8{*cd[Kʬq'eOʈ~RwF\(JU239”F{o 1eDIts$CM2"D$ae$O՟248I8Tf@E $"*DTF$T/'z ILl|:Jp(RR}}s6Qt,,KXn8Iit0wʔ+.VsjJfvY*#jWChȀ^X+#X׏0($2̱2kie>Kkh1$^ph\ +WFԸ!meʈ>7)X)+IY8Ufe([i:5gY*#rVMt,ZVϲ 2]e>쪋)֬fXHT2DϝNT%YK*YRxG|ʬħnp:Sf5̧9qٗ5{>eV}|8Tf@L܋ng cCeֆʈ6-iw Ie|TNsg"N'2Se>jׄ'#n,t8aXVu2kEh>;Ufq̇[uP*FUF5zYLSE2*:W*w[[*R]wZF*Ht@̂R\-M՘nnx89BSY$he+Lv*TFeË!2KLedbjU./f92pm#PeV|U`,uȪ̒U $RGʬjERre2"CE:*uUQbeہ2 [eD؊fUjY*#V08Uf%'Y}ttVO"95TQ82+PeD*t$2SeDE1:Uf%(Yp[eַ|az8Uf髌J_)j.V:Vf h`ї"u +VF԰ZHqW9Uf_2=8_eD~r b>NWW5ry–x~[*'WWxu[*YWsH;r-(.iWjE\D8w|[*W;rmA%1rk` ,ܑr+Y>Ɋ(<=~Zr'sǽʭ{+-~k۫*UNĬinsuCX喰ʉ%iuUGxUrWŏʮX-aEǭ'sCr+VDv8Ŝ;Una[ѣ#wRW j=;Unܧ_uP*UNĭ4a?]}3 Ϲ#_V#Wv9uzWy$*0UerS|.]#YVʉU.\20Uer"S_3wgftYuS*dUN$haAGl3toގͳ~HxN᳔UW,'C`ʉDbʉ /.Ǻʭu+Z"V5riAvzU9ѫJ=jG̲-vmsUϯvIRlv4cq۟N׸a}VS-fS1+ETrM_' ZY*R]w>Z.*qQnqe7)m}@٤{æU&4Y*'Pi{vߔ[)'M5өVjO^~?gvtL9QgR=MGVBuA ?FGvܔ[)M=Exc>U" ;WN1SN4D/'\ss%d-Z)'Na3{ϕaʩjʉT%R>Se`g)e}.Sr->lN/Oxϻh?S-ϔyJ `KŖr5HrͤV3}6^7rY'GNq㻓,Frm9Qn5&iqLGrpzϢlhBZ(G]Y('Gi: G0RߡdV7jRLV; RnA"Q"3t޽?'S̤mݼzԡ{)^ʉ}93-3wK9_Js2*9ڦiݴ^2/WH)HSCv†7z}i.?TWYJy-bTlQDR*6TqJr՝;.Tn]Bn~cjoѧ>|KkI]Ty=T|D{n99p:M9hj r1DIOT C4h}DS`k٫9Sn g8u0qM9h cr 6>wSޔGқrJ5HfS.\#K OVf19$SnIH2qpHܒL9dڧN%6pLr"D x̡rK/>zhJՔrx0s΃&$FxCbt*rD'Q)"SN10Ler"ÔOMa̔[)'LrۇijVQV7rV7GNrϑ9&TnMhBrqr@D92u-Q'Mo4SN$aֈS˔[)'LG"ppLggJrD8S[))'JI2}%ջwכ|g2XbrSqϪX.V1(MO:Cq;뙀~ɴ,?Y?\1{?awZ,6{ϟ, XIuiŸMs} F?V=gœc3 gOgYԾ_a`uQ#?ӱ*?{fV/7VIENiyGڏz*M7%d{w?ɫ:'/'=9oc.<Î*MG%\<[FfH/›!xwc:?]wRI۽6u3>g>.+@OU)vR|lzͧ#^k*86z|fK1z/JMDi:t8m;nDhMC0`pM>)8;;­MGe D'zR8MDq!ꡕyݳ>*έnkTi""M~U\M48 HGNFl"M L!QZ A='9Irjd9tO_X\]d9^MZwG;e,*&_}ż[h6O@?'ZhS& 4,IX;FVHQt'JE58c 1$*_^x3:y[M }."bQ( Eh-g0ƓOxj ` &+&'lVja/䳗v" 8PtX㨖>_)lc0[-R܌(ТVqfPvQeʧ@zn1 R"c/=03*]Kel's:|#|QȣՎG9;VĄ>GDhKżFDڈVbYk_CG#Z< )_A::"2Gͧi7I#?G_K5҈юi9/:&8"Fļg]gQ5?.~DvFy:H>)N⚻I 'ܤV3QD)l!1'Ry֛y-8Ag5e!!`|P3 5g̨:n:SŜ`BDJ5 4GEY͝ØPB>H[ U#pJc}z壺pr԰)S6^ݱ5//oɽ?Q6uoH|#7$R -\X0b/':8Ǟ@@>q.w.2@hA>>D߇qNP&D+}`mcY$wpvۨ]#_Lg]|~7sW-M&%GeNï21놧&2̪z;;;h\g\,s}.Y!mdEF0:Iu5 l2 lh|>)3'/-`ʞcr?w;n׺?̘1P5@.9]Tѯ뒊uRY=2z5`l:tmd/kѶZ{:kSRdgJڗ"z#ږJV#1U H+Y*%cq F  ApJ ~wlu9fߣ1̿&>.Q l[٭̈Xe5[A">1|9-8$,ΊΊy6oSC%ێ+<1랗&jk"( 9+Eq 67љH̒G wHqs n(ӣbKC6h>d})7wn5+zk†ffm6E5='⪘Xk`CJ lݿ"ȇijVgk6^]N}"OO ש|8;OlM%Y kynCedvGpiCW.FܯRu]-*Y#KYkBlwKwfyс/sfoMAacȆ7l$ cW2cߏ(aPgˤ&E҃X,7O?7ǮH[X~<1lDRwrw!%x5!^pvevZgߟ\J+GNk`d*/hA-G cl∀,4L;e%5.ĝ4??/!0'-O.+,zb j5F{J(j|dL9ߞzbw?p=7#=Pמ 7kQ-5*%xDȝ'-o[I@iQVVR]Vˑycss+2FGnvb;VFp"Ѫ;]WMݎܚ`KHuf0e$KF3GIh6IF"y{7w&UGդad %Rrgeb1c1x Bv[=6]on9m*;B4E6Vv %@A8 *v~ZB)b/drgͨIڄ]]ܛ-P=Q.S6942-Jq0#.[E91,od3n(- Io>#TeW̤QRcY]娶*G|7t9'GGGfQC~O'uvY$S$jVSZj#{-I9sȬ]̂/wZ~t tR+_īHĪg4reCF -FG$VUkh𠼔o˴:ИƈԘwTbiq\Fa6I8*e|.ٮ;O*@Zg=gQ9$.zi>0-j0e1,Sբ-#H` ~,M`4^9/{E2Oj<_o.uE:7qUہy㜰a , ZE Yv{ziyEDj.+l6At؄Q<&ڮlT6Ad8!:=wV5 kXkQ.2& , \>(\ogɸ肱&~KXܓU`SM&I?v0&MD8. L]pmmO|,6UwxpM#MP4I11T~qL3 f*e&Cf$3A܂%2Ydn\>#KP_/3@~G} `K%|+t=_O/A$+杛0%TWMrȎوDA-naQ[k]Gtuqj "E;6=6&ј[-#:8ā%ZVG]g Jb< K$]TX؜Ft8]‡wE "x^^`/ᓽaK%")]FB6 \fj4k{wҗK/JS- N~N"nj=s1&@D J߲1%D,3_~ZN%|Wi].A&%v}c V6giy^MX@y +ҊEL S:]FKX  Ɖ78?a*L&|XX`ӹ b 1C:HP.vEbaߝhfdZ[ ` L&%l[7WV&DL2]$魳m\DIpV&LU0B˹I ``0qPL`L &8XDX@} 2:v@],uf2͓Ϻ>z%|V*c E%Y^.>vb]΅Ǎ8+W\vm%qYPCq,] Ϻbf^nJD l+í|$*xUaO7r!0S%>Ӎ.RHJׅR*J('~Rq(:P\>*lko5W7\7{hK ॄϗʉ 1L%@dJ0a*2T#ؑI $Emds-'.E *"aI$|SW@j"47I [\>$|bӮ^>hׁ'N6^պ,!Uތ케+!N';~I,2IOd"4?ɆMJ,IOj?+`G##q?aq?ƫ&bcRny,W%ݑ I|\Wg*967%B!I&(є U6SNjy^T)T.-\uTg)H|OZszAX呠H*O]j9N/N7<]ח }$>(ް f|$0>&Ǻ,6Cw}f/;yW&߽/T ?߆H"C;1MϦ?9䐠3~$H?'tpqGA7|t3<2gY͊t;xæGLض^sIqG<^@I*t}u{"mH!#afH3$#1CFa\H.$}{*ty+C!IThϮnaHC$~{yciH4$}P7.I-$@2 tQlFu}nѵ\.!;; lg%w#OA" DÇw%0+$DVh6y{͗w`T1_f?I$}&Q` uG\l)4\b0TÈo`G1DҧIڐmH!a5+[14T&3W$aaH0$%*9TsC#-`C CoX{ůq"I$@^L/|LljҢVxpHA,{zESǔtK4_< G#IŏXwm886\=T(Jl^%j[ߋ1$&R1"P$QAxK.*ܒ[w(@KD-)NՉ6P@Z>i(onGy%$t?ʿLEU7h`4I$OW0$jD`(o$1$fDr,+v$8L0!):㔧;)ܣ& V<$$jDr15{ 3M&IeȚHHIR&KNى [M&yPIbI$V6콣$F$ M4sw3,.b/ɃKK%×|% ʒ)K敿6=7jsQ~U`_OO0$g>il ۱ZI&v\7NjYNqcI$}Sp(?J8pI8LCMh$%%>' ԓQO<9b뙉W3iϛo"=vx%}hԝcn~ann?o]? KI.AqWޅD9) qRѣw`ȃ)%jJIlJI0ϔVXz̋b6w_+9) $sRԴvn]nVҫJ^~ovi!aJN%I8Un{8v&# aIBp= $ 8%}gqw-C:H%#Z(tWf/3 R>c+J?rliZ}<)plcToڣ>% ] .Cℶ=y9thYNAtm/c*2zy}Z~ :Κ _ /ETD0Y=2Qz@0Sd e3B/ė"_3'^x^v[nwkTFg0hωg"y݉~kkGuJX -?y'ӌp\RDSM:o`l@GS KU;5bKɥ&'U1n6t)ӵ٬J 4 /僼}*[E-`E 3u/峿vY%6(S _Kj]E1E:p6Lذ]&l /*0,S>X,ٷ.*%1ҘJc0.*L)  *}/^{ƾp6Lذfo2YpL!2za@)%Ɯm{wFvRgo&dcISyR /'Gf6Ϯw;S$,1.m8wIS>'-p<ϝLCt@:SDl̀ e 2Cb.Wn'ef p3Eɶk 5eE[BV7ߔe~qL9\`&SDvLWM+ ) 'r3,b'upqLh.Q%0$Yogqڷr1WD+L)  zZ"ZaK~mlsWP*X}ϭ&/"_p{1#5n6Ll^E|Xώ3ĔwcY1Иq0aDŽQsSs'v);= )@u0sx &",o6Yy+` 0ET9<9ݿ&սc3N[ōYQ[rmQ{;eu5`)%H{I@qoL@W2UXS):1h/#ݨ]qܖu\Y^՟sE\3EtxaOb-CI*>r}w<׏e%?S)ƏN)% Sl (6آDYSDgmvsXkg@MSd5e|g֬zS"w hj)q(>.ct|jNF;c: k 5#:p3M̴(W̰n$,C^lU k 5EqЄXU[{knA?pwATXcS)-db@US>UmWpB߻LG`vM|Zܰo]ޟ}L*,)ؔO`Mhkm 6`MԦR%';)fDo'<8cMͦ|6ΓB8woƞ1ŦbS>-R]}aMƦ|[`o4xxz;'5ԫD6fmb|-z;x5cyS>-nqk|>\hkT*NS8z̚1}DM4߳mWNcM%sߨfTuxFcN 'ͬƿf*䞒\4V4qƩ_߻ w9*Sr(9MBY 44rhD\ r@#إ`<ӺRؤ`iIw8j,]ߵ@gAcNv}]{Js%hK'B }^kGep3ZcCO}^44y(fOmP`iG= :M@i*NJuw/cǂ{iNJ%= "zs;J`i~v2OSe\osiP4Qedj?vT;MTht3:gZ;t; ԝQw\L07͊(qNd: R؛i7T&o7h i۴n gFbvXt iE&B1xDm66wz{N8Z8)8 &Rp\vԘ@i" נ郂pp@8MhG4A DXf3NԌ،`Hf\s 3h44rڇ˅4st8:MhϽ4w;MȠј@i"}hFcO ߒAAD5Ԡјi5 > v&}1#bh,i4Qk!6 @ )GۂA1,>X0Zm4CAMy>h4P@Z۹ӊ@i|X>MddC 4 ~?M"%hiP4Qc]d > &|PS 8yGupNe;1lƬNY;JSi"dg4f4w}(OPAi׺>1u>Q: (&tbZ3u:Mvnhi4GBr…< &z|`+ iGo?>&xƻmpNl IgL2<ӝobɱ6<3?/ߴeDVgw&~hpAx IKKno`܏0=A[($A/!/q}bݫ peG&$A.0!.0ar򠃦:4T`B`•wLL< Xg:q ǡ~ 8-$%L/Ќ|&I]DB&.| |eۋYyA>@d/!d/a"{=ɨ.[Wi1|M nx6IM:7Jè%%L5Olo׶Bx Ax f16Av K_m&qKd]$fʢL ͤLOOIѷKɷKfmygRRR.Vǟ#,.%>.큏k{SONGW3xb)ro)qo^7trXY9n=/Kwg3"$$ PAn~O?=]=߷>g߂Z]JZ]ohӥdӥLn; @NΥΥ.vΛ,KQKIK]Z&>R&Fǻބ^D]JD]"4MWW꤅|GX][Ew% cz" S/j)U ||_Դ3f)f)7]cm4*32!g)9.P=<"`%, de1K1K]PfMOO2}80Ll2R[fNrqGT,%T,ebw$Ib)Sc^4.b)ϫqH82̕.L4 NNps a)aKc# /QRFǀ !^) / 2!ʸ[C+%+ui_s&%1a) ry->XJ>XBFd (_ G+%+eR]k!z<"2-#LJVʔ|"B&]ҶR}KWcOѧW,hmdm\ksVpdv5 g%$%iq"yyr+%qq R>ㆦUJU4f[*%>* G]YAc*%c*eS m蘽/_IIlNǭ'y\ɋJ^6q3̀@.j32R 3!N qgc/:ᥰ;30b aRY Jl%ô+l/m dX2.bvdO2'Ļ́KrlU*$=xR6Õyݠd2.h%H~ S]^V~ ?%(|KD٣D٩/Aǐc\BϦ-t>SfLZ1P1$䳃t{Ԡ`L`s>YAki6RAc\Ϧ퇪6!:Ǡ d2LHlK>a@K2 22A CLq1Aox{HtV10V=ˠ7d2.ohSE ׊_`"CbaEH22F#d/)Md&2.!ȸ0MdTz(LMwl" G##ۄ[D Q%CqJ.A;eSa'0!&Ag2LQrqG[AG$Ԓ cYh7=$CqyHh[9A"dDAz rG#xtx^Qbwn 4?d2L_5^j`$ȸ\?9H$|Q#C I $#㒌72l.2D&]]Jb EEQ2$<ƒ ~Ii)X/*a#Fȸ!FlA@E d(v CΏq9? /N2D& ċ^3"d 2.$h]f8RK 2D @P2.?ȿ6ψ!H0!=gDaȐ0d\P CpaA*~֤m?FG<4 Ci MEy!@d 2LTų֐!k0!~ms+Dy\؃&!2IqyDOt6,W[ZbE"TBxr$CaHpD > 4 Gi"CrqEDڸްzEqd'ndH2L#)^dHU2LUiR2d)Uj& L꿓CȐ|dQh+^ F1|^"yd<2L?u/FH#$I~¾E]d\tfXK R1BELd0uբVdH+2.hUCi^ QCI mM9!Cΐ٫3d2 @ΐM/2C!oz]oDEEdQّhw Ed"2Lh2bpN&qbB$0D OD_wRK)AOX"b6:,!j "ҋmA^0ddAFBeee.h%C(#(sD1=*v`222]C222.eU;*IGa!XX1"yZyX-pYh!aat ePLuqm*oJQFJQUUfQOx!ee(Ԣ UP{JX1l[ \322o98ş m욡򑤅tJ8R‘6d. >l/LJ?߉[^._&d=qII\Jҍ;`,Y+ÿ 8QKZ-ЦA5n_x_ܔ矞!(̦e63A1KY Йhq19fz,odPlHlʘb'><'7Lj 2 P5aPPOqhp@Ttl˾P̅BЉȉ\NT],NNv>{zo{9 1A'N`e2&fq0x\fg2yMRg>e>eLi$+0kՔo8JJU5IIKMOQ/ow=CRFR–v>CRFRƵBL{n* I-1t 4zRFzRғv`#(cFy.T~A(#(cF۵iA(#(۫llldd22]$nkYգԣeԍ2ҍ2n]iTߟqq#x3t2r2d{zRFzRғ.@@\ep^]m{3(*J)JKQƔIHRD<̐K'6}DzKyKY oIZ(̝-e.liL>Cq)#q)sKSo>C)#)cMf_r2(5b%c)sK/cq-M݈S ݦܦ6u3fk2(\I@q.mѨxG~QF~QeeLYO__1=r ^^T ̒$e.(HT>FFW5JCG(#(G [fZr2sYtGG?R|ؿ6ilSnޱ3h#ed#eLI mˈ22,^ﶂ$SF$S" /m-4e4e&i01u% }̓#1R<9rIqIK%B̓\$ᄑ'GL)#L)saJ;hw:IIʘJjOz$ao!dsDrB@P P{srr|$|<]'[wNC,zH'D'\:I7陣sumiqzDD]&RZ#t*B1p.*N*[b)G('(߫zzzs#ue][w{~rrr|;cY-G('(߫{{{3#vM6/($I/}OQNQoh}y0r !G9"G9!G^{-OэnF($Ll5pQ3d((G('(gGq"8rC( iIB($Lt%$%?![[آ$v[@hdLэ>Q8I8]j XrT{m 9zF9yF93*7;L.hYEy H>\QN\Q+էfsh[ͩN;F9F64^ 8I:A('(gDە: Lψuynx0c;TrRrB}C%ĊrŠr&V]٥YXcJ#(ydjD(tr%Yu04ԌrҌ@mUWwa/$$]&Q)'ωRrrU|N:U;1]ckd|vy믞3,G>e]I,냯_>zɷa.'|)wKQnInYrF)')w9OC8{-F:57{/ ЈMjCwn G\X./i X/@+'++#p,uݮ=so h\ jb.k RҲr)ܫ|UUNUt ?uWoJqabb.k{q>G:+':+gY=;n{TDw 1Z9⯄K lllL`WuB=ͣ34;}}3- xЋUNU΄ 3!,FC ʙXVV. =YY `TⶀVAVB 6 hk ffr [|(dgLUvT1SUЕ*ȕ*A3 ^mXsKcfP<(S$SLl/7q:T9Tӡ*9[_^Ҵ@ɪ ɪpIV{~%]LKFiwK p &uŋ~6luu S<9XT#`^',`bY .sզa웥.v0Upu}% U***\檓|E0WA0WႹ6U 㡢TT 53m!-P?*H?*\]^Vc Oiս4hgDDQADQ"6^~o'D yMUq:j_:أ"KTKTXd CܽM(P.*H.*\rѦd?(!7ͯ՝ǂ y"M  $i0ɒ\TM@U 3Č Œ &f]t̫.h7&QD"ɈeG|mý7,Z[4 TT w ą … .\}l1; \]cguӫooӗ~DbTRATRᢒW]wwB@,-6H*XRoٷ/Z KUKUX*EE t r ;MfB@h5~ݦ1CrLQ~gN:`@f f2SB7QTATt+HߦP*H*"zC! ! Q[]TATvp11T@tH4D4.I-g:RMMKo8;@̩ ̩`bNەq@꽴#TTKKS_]P)jJiJSSڮҎݤܤbnRnRAnRrvґTTL$qGzo8[~mi&EL*L*dR>H{"TT0yʈu_\_TT0}t 0ջ6_?z%0ǑT0%!ַK~]\*yO7..Lu)hN Az*_Lݓǁ,SA,Sdd1cɫ? <+_ ﴣX_OoH QQˈԸ{ 6₾kGf fLI yeDmIZ4UU~[/q,8D K`ZRXhJdJ.SPZO9l#Ąq =T⫶4WxN1u[}NS-WVaYS-ZGkӋb6&犕N;m8h}1qY)iK~ՖBײYY[tnf+k.x`ɳ<{zL6DΚb<ۧ,qN4o76]tn'S@|jYNmuJ;FuFRRj!_NmuH֝ۜt8:xKwn22ڦt'6,*TTbv3ڊ;1nK%N7c_6+rQ;t2[f6.SU1rﵗYĝlٌeA;y/n*AOwrW.D}eʓ+ pq\bp?Aۆ}c[mN4.}'zIqF]*.]'smGz\-N/m;_NM xWm.wOoBtRuܦjĺs3d˰<_&q@Ҏ\tI;ثT;Mpq'v=nZe;ccp/? 7vWwI;pPԗ7v"U_Xt~&}II](nKNN4rďOן̗hVܑ̚i?mw4MoL9e{TqɅi ٲS&zO;0m!u:0}Kn8ONL۬՟XAWS Cz?~P^q;8m1 yvr}zd$v{&wRū>UGv,WȪwi;pYVޗ7ti_{6E1aykO0ZGѠ>!dl.HO:Y.kK/AuW֦/Lb-NڼH%ۓ*|ZY\Nڬ"z5t];nw{Y\6‹6 6P#Q?q2N.VF˳j;I-o9*k\;I-oK:.d|s]YN$‘q; 6$Mp.Qzty%=C'mNcbH} 9  qPtr`@gsPt_f?a䱢c.wh_C͊6 QG1=ePtX1&sPtX1sކtEPG_IE\P}E'm>t?+:h3($MhԞk2Au|>9ZG1`Cu/|;qT(Au 2@*$.W)WҖ+4W:jI"}䝃LUZG1џ|8U:j~+Sru7)Cu4j>ls(:,buRu;*)CuDՊ<%G R8>Q.gss\azT(&ӳ]1q4Ń~CR')tb= 8gMwXmNw}Gگ:j]r}2g)/ӿ{t ZHL q+gZG\]ԊNl)|v6 =z K-Q\vT (&ZfGHu<z<:5uI^z<%y7t;ʅVt]@N'£ZGBx:KxO:Vjzv?QxT(+ʻ7p=zyQ.gDQ-Q;~ZpGq ZtGН= 7%+x :bjs:Yl˳AfT(7C~Q-/mg,#eБbT+(cIG8ZE1Ó7ÿQ.Eh0`KE_p-\FE lFt0b0ʅsQ.k aT(saT(ΡC|Q.e_ZEb1`gQLǎZXE`;v%:jrPvaQTˢ(ZDrNDډri'C㉎|ZD1哞|ZD䓞DLQ-\dZ>ESꤵEQ.e_ÉZEtYwQO2(QGɣãGQ.'DUQ\<:j}W㦨MQ.7eg#Z.EADGIQJJ]CGIQ\JJ] ëWQ.^e]CGYQ)+ru2ZK(ADPQ-\zn1DLQfJ)5SL~>WGX{ZYEUQQ.D.?LW'gyPat"*A|Ĩ#|oťVQL[WSulnK &2k)ZRFIIU[gF HQ-?3ru2l (<})1yqgt+h8/׮;ngC薌.2fW#Vс]=L+[VFXv/[_F|yzny=ܴYCL@拘>F|f1{UFv2fm\-.y4i˧2t-W]\1ץ;0nä́iA[֪2:ZI-hͮt-[ltGѭV]ZM)³Lf3^薵Lf3^cֺ.&ФvTޚ9e| ^.Ogߓ^L4G6gTר#5$LKg3^L4g73^C%rt-Dlg08ep4yƫept-@Gw28:83Kw*8ਣٍ[G3y;1([G3wG/;n% 8OcBcL>ZG3 p}װCL>ZGG !ptKUOC[G3~4Gf8GȻ~[F3~3Fޯ~;n%7V_cбotkh}wId}_mu=[G3[GGL/ĝC-ws7yct-qM:(z};čn$n+ÅZF3iw6z6m@m[Fm.Ej[F3Q|{j[F3QqY ymt+hh]4n#VmtGѭh6~ Fٜ't6lt-`w ؔw/֬_Y;fn_Fwݚ5:Y/5ktpfq9Fff5wc֬L[\|;Pn̈́jt-T T[Fjn%nɭxaDUWJ㎤JTvateRbaDG9[$v)'z>$nᓘ CtENb&rRt\uMbk"!Fof;oOV?f?rXݯbܟMz/'qT~LT.?#m)c7QݟDY+}O,;U?'ʞOVMy?|wbwكdrpz1]MG{˓l&꯰C~=;*[һn4,&oxcY˦~u`bLU?.8VgUt2LӲQEIɣg/_6Ya b<=d\v#dmuGRxaҌT_ƒvo'%\W}Y^NNRz$irJn+x*T7ttޗ4۝ËNՃHؾ$eqn#!,!K$3),)r6f=^1>,Alt1M?X.'.ˌeoY2w[x2[w{?ooM 5B#3),4b9](ƪ! Ls,\ &.z{r&.5sCo2 DeC7̆Q$S%jn\wǽ$Yj xm]+?wO/-aMb5&݅ m8DDJ:&L{M~zS6e4ep2 N^g#9 +ˉ('.bOΗU=*FGQybN;]vw e>:wlݖ'LF,OcjvUdۉ}6(kH<FzFxO{E˓$OVgS\_-_O7NqT0TńhPPN^w.}$R/.3t X%d(..*lP 2 z$[eBU]#2E@gڟ$r?m&F|ؽ ʄAUz{DLPU?T"J(egJC uaH().c7`O=HYLIw-ˡil^-xj˱k1&%ԅ޸/[^.߫v۵CAX(%œ92/Y.ԷV4Mz)٥.7^f)7%eGpeSrM}Q J*@Z,oZ5z۪DU#>JL0*EnVF; HDU-jEIa)p/!m&q P'zcn?g*tqTn $J+1L=4 Z$2WVhV2[b xOaJ^W4\Ipu.fXGOׁ+Q.UZ}3ȴ"Zϐ!cui DdUʛ M.'4 ڬ$\M5oq "ZElp3p-}ù+se|ȯ-t_YT굕^]kDvm]W}Eo։AkbPueAe[\6-+-2DbYիBաd[I ɲBePu־a@R9=O9+CqWHt\rjZ?,g[Džh(eoRzi=Ba"obN{uRݧ}-+X 4:OkП /ھbw3OO GZ(-r}6ՕΑ]]=b0Ӻ{dkP&&;'mm]}b%ڵ3+E)9=:guGuu}D;sssWT_ƁDr[z`#K|{V%E|ZZlloԶo1pxZ02 /*xw,̚OB9(lqJ.hi;/xURWd4P7y][q!`[NllBܣ3;秴ǧklԍ0XwNbibekCLWڣWxSLH2fR6f~:*GT:Gꯤav+kO+q?&q$ 3^ʖ|?h_I%EbJ> PWR+)(i_"~I1%V#s `I^ӑʎ欪Rcv$RITR*)RUb\>K"]I1y1TR.SN"(I$F!)Bki$E6rH;ݘH1R.ƈQ(}"E@r EC:n7ùӹw_k6RLh"~v~G󃣏_g^ݩs+I8bH?~ VDWI*+)Jʅ+^qX?;ySiFI¤\ Cڙm ¤HaRlHy5MveWǟORg%"I1?}k8x?=h|r IRLJj15O'evR$;)sg{'w}}wV/)o:0h߲0 *k lRq ^SaR1@?LT&b[ܲ M)M&E(bLq"%i^DpIVElps-)S\bkj-%EriJ; 0"78nmy#ErG;#'g?L(R$)Qicǘ-#EriF dC FwK⮷jhvSb28NL1&CҊ+bEl?׶ܱ^*Î&9UiAHv\цM>Xi@fFR.i s)GIG^ͥt _dL$%)$m'I*$-&YT 1I#ErG'mhO6$ML2Ig l4ZH,$͵8!t~RtI:\SZ,`v¦Nc@1yf (\Hi"i^'&Iħk4qM5f}j&MFM}Näa\i"6Ag.^z̓FIʤ](w뺱8.GfѸ~|heK җ0]S} LT&U8lOR&MLf2M綸Ҩ0cŤ]ӿLt&ҙv00ɒa`uWirc&xIs%Nt>5qt e. z&=I3$vOۭT;*iB4Ub*!63I4K%—z\t49MzNFIӤN,6IE(9i4SruJo^Ai˃ hE(GiK.tZ)MHI Y'dXsPVS/q=褶eۊi.֠#ɑLGJz\܍ S=SEO_Ki/z;VxiũA*M fTJ BB盃 5P$Wi\%9@@J^]xwYM+MvV;hv.j]FJPN[lm,)Rh$cbbŻ}C#3 N[deϩ\ܬ5D xVNVLV̄!|`VL`Vnb@ޕx#yy+1t(]$]\jihK[*XXĪ,LS."2Nca~Ҹ1s,NSY*U1)U1K ^]TwKpB%=y?^^tflg#ЧpƄ>{vbDbBb"eWm.{BbZg1V1VKˆիԫإ^h1-jff.7kCXE1y.~MgWLW G$g}YឞҊҊϨfŤf.5ULU?eKbԫbҫb^B\zL_S.j=/Bb*I |4O3ɏ/(D'RQPRi;2*/gJ S1S1DvG󫕸%I]_c b⽂Q1Q1Q1*[$}L"kq,/zx_畷4 9Q2V¤b&& ~uUʣ KTT̤f{CP*&P*fR ;닟!`"&g*v9SB[1_G39*^٥+*R1)RK{E)&)vIO}1fc~9#3)ieVZLLD=L7<]L&,דC)&)vM;#d$6M1L1L g~g<}_(u:7}~l`}8XqT~LT.?3QY.O,3\-Wv7|_W$*wNFe<~:`+]`Ճ}WgƓӓɽCLG{˓lf꯰AI?y{vS6wes=ri6YM&lKwDz:.MK WĶu9xы:??ytqY˦/+? DaH%e_{O =]t:Y|[]s}lt^v40&IyQT<dvU$;2,rV}-+,<7amm,G'>˴^ٵ\˓iurUlJ//^gaa!{Ѫҋ irʿFikg aZ|Z{)f]N44oq!2Cv-CbbAƁV߸"B6ǁpu0-v BeV0Ꮟ#@e ¤jv%~σR&veRXs&kS<-nunѣh</m5Zh4=nAL?齿{s5ax&B֘-">y4;˳:/bjK1Ӵ[ 6Uћk׃;OS0yC@)Tf3GOb.T.WU^|n*LOh /ÍDMaLYS魋m*ѫw/OVzMxRkB1293΢&܍Uwe=kW_,{.it@h9N4ɋ{WKnh '_uSKzSء;d TwNGәg@Vpo@~p--uCղX{T+C8µG q-e.CM 0JB\|Kko]KoWTj|{G|R p=--uuB̅}ڮuǝ8^L>Tinw`02HδY3<"Z?U"U`Dc4Kbbűػ,{r4ӋULQ9*K5O&$_ZB=7LkZLu:0qޖyh70PY]]~Y]K-x5p!.垡Nbu4ZEEur 5;}CŮ u _ _6;- @#]'Jt>VѭrKt ޻,/8,'AF1!6ǾF1!vylWM}NaDzny aC1 OYCAx{bvEʾ}|qn`ǀ\ѿ__⚫H{qعzo_}vҤGn'9D{6O@,skק׽ҫ=p;zɷWܱG#ZaȧoxxrZ{+WGԞZi'{|ͷ9e:|aIW.S_v͘F/ZOn̎ n,w(__Qe ^>:|vg: 3HX_=&$߆/#u$k~;矣Q5Yy'?<{\棱\LPk/W s2Q qQ([xQ~Jf%ES&}u{}Zz$,\L?a\I/>(nMSEzt+cbҩL6? 7Ea]w}^tjZGi_=mY~|e^Nmvnkqmq-犑g#EAolz`K]_ԃu%3$w?Jm,cCv鎭}/\H>~0MM6Èthth@3M3aލ=<OgX).kR\6]M/VʔIn: RY֤,p*+K범عKiY!qeM$7\^2[Y}( #eMFdr>[ d4YLC~Ɠ &k2MS@MO.V'͟.OLuL$lI&$5I&+ɬFd&aL{YExsH4yhDZRH3yfiCɛ43f&azQ%2Ld_r&aRNL"Llrm&ce*n&uz3]|i[/ KM*x D PE}, M*@T#|Kup.͏`T=G'^]RXǰh b$2jmȁ:E8E[Jhm h|ѵO3,/90E BZ(Lh3͸؎rbl /(TK%J. #L/W? 1`*m /3A8̯-#Əe(Ϟ8~>~*Wc4JLf;>q@[cVBC֡?*s8ߡPLyXwhֆkDjCY<<~ F[Ce 5o9ch}6⣄0]61 Ť+cHVtBCȡ?zQPް|7St(:^E~H"C1Q~5Oȕolp%P{(&22AP~@ꇴPP.cSЃur_}!U;۱[ :=wcpuYp0_$\ >RB ƶ9;r^^r??禮u.ѯH$RH^'lNdP)QĔ(S2ۦLGS(bM5 ҁ*d-a޹b&D0qO6蛍BEtr)n`/=E $H ٝR6MSGKMC|1Ǔ\pw4ɭ];gqyO30q]4nøhMlMԏvQ?_ʦD@Px91>.`&GseoPB}!hד c vF҈hB~ =Ijf={rJУILgH:(AiG]n7&Lrt -{Pn&7Gb\&ht4)hFFOG]&vsM*(hyK3PڮF(h}4x|]4WmYoi_؏&GQ}4>j z@< vp0]ʏ= LhRK.hR~KvܣIс > 2|Cd5Gk_X;Dh&ݓ'Rh#-b譴#٣.g 3:갮&2x ފ_ktt49:*j`+ƠlnVh9j Mbf9—Ҟ zϞrRU.'j&F3I%nw Q[J̍&FiP!7BovP0ɑ]]v.(hhKk&{F3(MMf2<* UMv2;hP.2r1@eF2]LMf"2k'ƴ:Chhٴu^lhkt FZ(whR'j4Q5IlWj4Q5Evcr#VFX]oP$@L;Z43g6J?E#8 .pF\FV&sF̙["3Dfrsʭ.Ne/?yjgJ"&F3E(@EFT]R;&F8F.wFG]NnX=w328B[ U0vBC&nu4EW'NtX|„Nڎfj;DsÊ{j76:WU7գJhz4Q=Inb2iRc&Gx&;Bw{a!hRy4Sn'AqG]n7Urst 7{bMv :eW|fb|?IHhq4fS:΍&Fslw%Eq7o PQ`B#Ftсߎ"F&&&v4"[tvGG#HH3A]z𙘉lWƽt125115G(+bTkbRkbZ{_#JJPgxad۾b۞z^ߧ0b4[XĜKM,~b(~"&R}Eou]J71I7K鿫@&&&va5*PI] G`)v Ĥ.Ufj~lWƛP{K{{!"21!2 #0)Lˆ]dNL_ā@8`n"U&v2+6@Z&&Z&v22@]&&]&f2ۅ#PI]L010˄[ZBXĸ,]21-1- h䚶H lSk .+ \,q]e`HD\nLcd.eM+1\\j)}]:Uv%ι]Η_HD.3V9Xvj#/il>6h`b`biB ^ˊwLjĄ.$&LC@11111ӈa^:@)&&)&fJ1i}ӔdƤJfLW3&F3&&3&f1J*3)11٦CV̙.fs&a.q qYXźgW q3bGivKLvK̵[JG%&%v-SY]"F%&%DH y@^ji_œWֵҾ|eI14_b2_b9-Ŭ:N0~ۨZXy y嘘䘘)"_#K"2121S\l,YlX _b%|&f3?fomSɛmc?hqW@&&&v7]n >6˷ ?=Cq7˸mT?~\$1~휘e?@'&'f8;hd.+GZhiA9'&9'$H )8~[ELM3LќМ؅H MLe߶ B!&:{O"Hݏ'+)U{ōɑ4إ Q؉I؉YΞǍLتw6.FU5r<[-37)>K} yy7H/A0% AFF4 |qqiô$$C1C1E7-<0k"~B(&(f[FxLElle#NńL(SÝ)dd.9F1F4;c$b"b&yU*͘ +]Xn6Srb+7ŁܠzcU }zZh,tm6wG:QLQ q#(B1B1SڮM]+ >`TPLTP좂!PIO:`Ql:C9Fo}5&sx`(Օ!՝]Y0┇ZOLZOzEQɋLk/=M}boi.v3 H<  3!I"rB1qBn"b%e( I ɔle mzǯhdLCh1vc$D(v!B;ݘ ].d ?v1owzPLzP҃6-Md EAc&+sLvPBvPⲃz ~~&Ԁq1#zy)&A(!(q@V $kbC=$ % %.L(X~O1 BC AC  V O1 B B SJ /)&A(!(aE>?$%%LHp JJP"&".+Y@VPJI(J\BܯoO1 D D &n"ݘ>&J\4n7fC0çml$-t1Vp1}:5Gc}3a jӠߤa.$( DvՄbwx oox#]H^:w^I{:&l*ףE EK.C(!(qQE*ɬGChh샜!ޑ.òBק%q=H/MqoP]` H+%D+%LZ/O /%/%LItpSBSX^AP'FSivm0LgG & B+sy¼MSԝBq! ڔڔ0&ϻ.A)!)aL(A@ 2D; !TR$ ~ڦm2,2M 1Mic=zW/?>z? =[ivJ\S~j,F:bۥaa$ *qaPª*jqr~Yw=gO7/^UcxD#*!#*aQ}!{JJO ݿFwK|LTR%3"AA0 *X{ BJ\T?N )b (nK#OKP P˅H=N2iFұ4P P Ӈ7b2z'PI{J\7nzMw{~=<9m #6v1 6SBS`^I< bCDB6ım%$"&{_iL/%.gS&3/oRt7.|WL$/%{Ĺ _&]9L$/%{/D^JP^JH^JK ebgQVJHVJveiw.%.%Lti:K 9K^,?DfR2ACg9+%m,qg9HK"mUƦ3UTĥ"߰0J0J}xQBxQċ gdvm?>y7 3.gĊi`Jf:#%%.h;B*C)!)aHjK);Sel*X|RB|RzX8PX/%/%.)lfȐkJkJ\\ScFഹ?#ДД0&=Ѕkz)")N)N)pq&S{]xZRRR&[DSJSW)E)%)e*O al\fTJTꂠvp#R,TJebލ'S$R"R&] ˘Q@BX fw"E9)%9)uI<Ďk/NO) :3IV4Iu+q}RJRʔ2fRJRElzGRJR4XnhJO]\xLD/Lzyic&#)uL=a1E)%)uMe\: "B¤FRҗbWO|8V>y^"ӔӔM;>_Xٔٔ&a ^]"Z|""¬DRSʔ8Y> E)%)BOru(A eDy]'J Jp~m` ~4O"$$2!)"[K{ZCa*%a*e S<""2.nIJc4f^_ԟRҟRF)%)eMu"ߔߔ2-DiE)%)uM;ј_J]PԔRҔR(W۹.|1sJ1O7c=lЋ2E%8toѠ!eȐ2.C*L6 RS)㢦.|1NB T=;4 RkJ1 yg4!pi/w be2\j  +CVoԪ iUU%ۿAʐne#F901ke\֦JƦ-y-CaZC2L>Sxq,,㲰FʐZeja4ae!{p-,JbI`qQ;^6B M//9F,3ZoV`KAB!e2_.{HF" ڦMOb e2LWv[J\lL[d=(vis!p'F} M&UE6Gɐd\rS %CؒabK?j%CޒzK878on!׻'XU2*$mh/ ^|q&C8aL~/— _oxDNdR; I$ÄT d I'q ,@.G%CڒajKA'@Fd\So M 6 di2LI1Bw%V@ :N'r};SƅO?K!@0)PL(4N7)O%?Fc#ɸ]BɐdiN Ž&'AN!0ݧ,|f3d 1OeTN7C?7 T)SKUgd 1VX . bW+Į/9VeH2L*ɶ/me%o˱D5'jP2RJfSjzrq]4NaSl&DZXJ.+(V0LqyTЙ2L3i S)Nmz!S)"ըAҠKڅOkp2D8$iIvUTx2<&ԣ`Pv2$;|dHl2L=4eK/L0' Oڝb!1ɸĤtG2.)*<,"dT2LRI߃AQa20$ 3#M&< /,dHm2ԦNO/Ɠ'_%-%fJ›L rZO/Wgǯ>yhǏ?/'" NE; Bj 4B ?-A]a%ʸ`(a|.jX?/QS7ýGF#e\G!:(LdG%(SlMLq1QNir1ʊD(Md7|) "S!S }^ЈȈ\FTy?' eD+% D&1P!C*#*sP{ OO}]fww\2CE˩h̝7L2eO7C(#(cG[1p]؎l6Q*H*ʘRRZըd4OW,ĝ;Td2b2&c#U5D'bKa` ku&߷˯^>዇~㟏I_6"cc1#V,"'eV7Ǭw=ym7Y򳫳۽ir `&k)sYK]<:Mg1a^')saLcgk\\o[w-x؉tgF[h<-L/ƸR`Z\deUҮeE*SW"12;^`/Пȟʘ,Pd!S^TFTB6͝ƣ]uoYUFU5hT^>w/P ү2~%ֿeW.nX8N *sqU?LL1elFz?o22&=U[i11-"1)(OZn̲$HeLAw:%kwi(Le$Le.alTFlTdX@񹔘 ʘP6=cE*#*sP;٘pN؏SFSvզԦ̥6qa;{7Ϋj:X4d49^޷WH;zzAO#&bP'epaHsPPڄ (q)]C[oM]}].RPQP5e׵G\~LtEeL(jKPP ߙy5z3Oߗ|3*u:7rXݯlƎbt1N6<$]~;TF%Fg:R=GG¼s\Q|r~/DUnϲwUO'lEC>Lǫ^bf'M+_qrfqׯgG(6kOl2)f[[M>ql`pW'Uߙãu^Tչ.Qى&o#}{GQ/O+052IJu.W2H{)YEln-MQuuOVӲvͯbW;Bo{%T 7]z  /ȯB!eq?m_|h MW&캰~y&œ(H=O]{W/_=|ed¶ikm v%D|d;&/ziwFUОxwӦ{{ny,hpL[igkc-'I}5u<Җ vʻ6C]~p;y}C6O]w~v>W}Ѵ-ڵ+z'4)tHcǤ>s,}Ϯq+2DvmD\=c .b&bQi KfBQ21٘)MιU/'HUFW?{7H71uE\µc{{Lah h7.Xr{vv欰 g)^w˦Da!ۤߑؔa.bOe;j$b|yk t,i^A 8L&!H~~ |pW/mu=Hw>F^*]^x9r1?OE4=/w>Xpǝ_-hU~q4Ү_צ_ݨ^i{wst7T#{.>ng?ҴP?s\}u~t\E}DA8dvovou m ]ݢž-vE$ts|&We *ZFw˟ _SЍHcc_DlK^BRWUTJ~ W6G>o(l(0a[ FV ٺخZgjwix/vCًl`mnB"ޅ66zYUE-.=n#>6#v:ߗnݼ<{ӬޱtKnqDܽta:9gOigoPr5P)6oG·}ťQJ%x`o V7蝴ܽf2UPj[ku~9Rsw*WnP4eY-썹V?ŝ[W߫vՂNwqw ϧwnՀqRxr:u%ػi9 ׌8Xs7{ʤ{e,,wJI&cݖ7 wXa]Uz6WkϦaiv`-9`dtY*e=:6uUh٩Q3yV]F d]O=Vni MjAdl:DZ](Rc՛+}<b,| ˲R=i{,,wrH}u# OMwcsЫ_E/] ZX"v`ԾiBjBSݼټT2ï #n jTN/(p p A1P!HqSDܭכg=rYۧoxb;ߦ{.pmݛRf4}go|[Xz'ܓݙ|nv1'4ͮ7¬ :G׾=ZylgSE |吮&]tM^2Wd85;?{EB mjbz,!#MF򰀌W4`ff,7^@6,lX0W 8&`4Z4i=Bf/ M-xvRLF&|+ )MR,xIQvL0&a)+|idɂ'W?4{hc'wxR%4:low3קʹ#ܸD;[ [`s7Ѯ#CAݾG#ܜDx*A*X`ڙs/w#^iAg p?%dS&kp3?ݺ#ҙTg;;h  $p ;{hsP!oDצުR:.K'Px|uZ`gsƪcXSˑ Ti5&# k-uZQI"WbLͭbsvP1 ӷڥRզemvS⛍mKtLq6wJrxq:ZDF\\t4LN(&W - Fw\vmxfZEsG`Q.F'@Fj\M=5rkMôDj t+rW+etw<wQ.f|L %0?t\ҍMpL{vzr:7{c(&l#/ttRvo8o7x܁||u7Ex|rnxzQ:-U&Fr. i;iuEr7Bym6ۈSG՞ndr5eG%7do^ĈI^wd(&v]'k.0y\9"mӴ]X;JN>{iuуwߐodp98 ᴏ[?O'b6_]Q(&`Sfj:VkUoe/?}^`#F1qHlL_{<^;/S1{}DFN<~b_>}x<~= Q.u+vGoH8Z bG!3\N] 2:tt/֝;B<$Ťx&uL!bOHb*=pܯx6j=zګK2_xP<*CG\O(d(&(tQEr>;C;g66rEEr>ԣ8 ̷)&$;(7HA#HJ4"eq$R.O(d$N)!‡oL8) (.Oq@_\҉' ,.6f\S$nW (TDWtJQDi cիr'ʼn8KSƒ30CB\~D)E`X""H1M"^3m^US#>HRL~h[|e1g5\:ǜG d 0i1,\|Bo] ^"^H}ly􆀐"AH mxՇ~"@H!qSBe 7Z?~xJOFGϣ]>鿟\/V6~<&G3y^W'iy4<[nN@# LI}M>v<; P-O2,[FeczV. r ;kP` *#wӣ X_>mM6hl^5&F4>hj4a5څՈ;m.W5ʹkz}(hh2kˬ PbFFsMYMv6;0sc96!L[d^L&?#|I.f=j7n & G31igp5z3Oߗ|3*u:7l}ܟM؆/'d`/$]~NHߨ,VGJLDGbt~׌~WE?}5>]N|dT6ɪi陸nfXM.V~a:^=(;^bf'Ӫ~=f~\}MgI7Rh3i.̹43.y]/$2Ǔ||ϗտ/h*%n&60Žg]|GT}ds/Z|)wfj`;ΗL[@RB I2TVUg?>vн,"p{GD i&!oGnMdKFjғb8ea^oґ:$P3j~/@M˟*Ej]u-3)LdZ59P&O{@/ujA $R{_W)faGxi;]|F =ѤG£gFA~3o6.= ᄍ:%l"}G|i=OXnР&%P ״Wcu}tnu 'ʝg$*/d!^r"5.3AlC?'oC :&5.34~A4`ʈa&SG;u#pM5X4W5q} {xl@j_UnAk]u# fyP?:DUPi)-)wr/,9I03eol2ˁͱa͞dž69e6kQQ=eL*PPIVfʜwg,=%-6$&]pr=2Ȥ 3dօE᠚"zN7$2wfz~4@;^T=[4`f ̡.C^3πpm6a ~qk&٥5@%y/ ݿ[6eϫ)ӓjK ޮ(.9qä AdG2aLkXO6CU+3ʀ흻'7"#Ow LB3hl?G9!ݫ)C?ij843479:3=gIMDLЙSn$$:@A%م4˲F''xzvIp3 d eGmz'Ff : q7LybdIuf*\<1B3=h~](B3=K-@&z 4zA4M] WMXI1I]2plfğ~f!s5bj}ؐGfj~oX6qY0h} ՠlZ&+:o[ $,SGEZ")B{bԈmJ{r׉";HuD!ʅ@^~{RU BYϣx OG&H>..atU<2LING:ZNjݵ OX6}6Z hELb2۔ h4v_a:4U}&]gڻL+0gaQ_4j`hIcz3i0s:ksTvGKAهti%@P[tIIs.Cuy?wygZ54tVaҜ7Tgw޵Hm6.ܦu˳QyCMdB8. Y2:N@}T(RrAGlۯ,d5-}j?UF(Z 3edu:*#-MuKջ)7V15f s1y<xFڌ"#z$T(|K,qR.(HRA ‚&F R a R)&N 2EWC!RFy(!Ŵ<٭-_w%O~8~jw +IR.^(p`˙3_ S*1Eq{q1W\^Q"ED d5׬̹UVuz"Oh)BK5":>;dUr9)=*y,.3"xĆyd$墑v07 s G~|<|qÓ_T^""I1M$FhEARL ꖭg*lHBR$!)&&N/ͣDRd")&ps}%k"LRL4I0#X/?y|xɳ;yPz깡"i$wZT#9J[IPIAI"\aW aC,@)bˁoYVi`}GtAO%= ^,sg{'/]wR.)H̀'6z[_jXxpz-ЉRE)yw p]ZR0)Ԥ<" /}P(<PR@)%@+꤈uRL VAz:NW^ |R>)$8[~pr i¦K  ޞ ӹhH)48чl'~9{4߉~WA/:|'<rˇcqcAaJ1\THB)2,k?/$&jMR$B) fqNe߲Ϸ⋌(d1?9"\)傥v0i$6O )e>KH :Q(W)J!ȊR.,j+45PP(B{7TA.o.܀@,oJFjĢ4aQ:Uuvy~KM5"R)D iSF3Jfve.8(KiK򎣏55*MviTi٩FJe]UZ#ppQpM^pk>uahD4!W\1;PKh5X,z1h4V [1/itPWTKչZ7U+MfWֱѳm3b|:_8[:A#K]FVVwm-M̖f2[- çO^QY[em wOǥLk 4[-vb&&&K3fſF!KBƉ+EU'7|&RDj4biIyI4a)*n*__<W/9a1~Q'bG-lKfb*XŴ ۏLǓl%:X .lL6\]L>^NNVq!<Y4c\荃1Y,t4a I \-5i 50Mvi`d}\Q Ӥi|0MPBaXak4I`+BaFK]=4]\].M.f\t[ofc"]Kt]4F K]xr2]ΆU>"^Nvƛv KCGvWƓc׽]uSdסHˏ+DU$VP+D)aӍ]+Y,->&9"4?m=yYi⬴CyJ<ڷ "?{{1R1R1ӖݩܩN~TLTt~"0F<<\؞_c~~S'#/4OT`&C*fR7(D$D.!)=Q&\ܓ3'^q"#3)(YU`1jP1iP1SMM H CJJ.V*hy}|KLI\HJܷЪ}VɚȩON ;f_1T1T ڴ-vv3+H 1X1X1ûkC+&+fX}BX1AX1:Ҳm<> ϗvˮlA X1X ٶpm㠀oo8J +v\A|Lw.˻3p6M!\ NW좼B ̪M6v_zר Aݡq*4I=<%|7lr*hdL;Lw?fXLfX2J.]z"L$|Lkm>WL>W$;1\1\1Ѓ># pz۳1]1]IigWLWrvp1I]nG +& +vYX❼/}Чpr2e]/nv%mObkj42L]_.% UK]ma!J\ń\.JX¦ך,2wջ'^%^%Lj W W^ ^iܞQ h`wg؋0 ,N0Ar~,C[^<{.UOJJ\xV>m-X(A+!+q\ŰHy%Dy%.XrՕՕ},LJJ\V+:[ 9[ Ҍyf<\PiytsĿ쭄ioq'k%k%._^VB^V4C.MK󠞑XN>׻6-|3L6b/?j&pDJJFz%z%L֫^ ^^]]\zi}^ͽo6Q2ɵ[𭄉o}z[ y[꿪0--:C $k%k%.X+@&*Y''u$J8qR`"K+qYZy>h^J64W!جkUD`%.k/`(^8:,x8ZՁ}u2fo7r)(EY7Jii0M+| $jV iVKtkn`!޶#(,߈:KdS&"5*Fhk%dk%L[r:Yշr4]PKѬЬf⸾6[k$JJ'k$Z tr{H}|Sq./ʲޣ(XMJJ 2-c UGTBT¥"B%B%L:W]Cw:(9<]!޸ Dg5lZih7ZxOk55Lk"??^̟G%L>J'<ͺ9Sӄ\Mga9ޙ^4UVHqⴟuGI7ҋ[;8=hAbdMTT1~UEUDhè G׋ةN;ۈbeU λE*!*azVu dd ;Z< %ui)(IQql:ZvzNb0?0)npʧE*!*a T-3IJ:?8381U ھ3ISK꿇E,*!,*bQmM?~LOHE%DE%\*gg(y-O*H J7B @ʟ GK%\\Y@4Z%>%Lz U5Jw~n,{3L$B%L{.$?/풊[|# **PM@a~7^ĕ#ST|znmLڏЪąV'܂`%*qT gJ`&*aV"6"ee(^ɦJ6#`HѡJɡJ{p:Ee*%e*u)Sᨔ4廃!EB*%B* DHuv0\ƣhm5| |wvH_;SRR/%@EzS&< /HmrHJJT~`"TJ"T8յmjn'2 %PcPV,F(8)`GH Hn{k$T|A*%* T\eKoIQJIJ#d\'rG9LyG$EG*%G*e:RA,"--2i)/PmIJ JOOO\|qyq:5nNRDRBR.:%R1{1a6'_*R1ݸWxDP )E*%*+Q"QQr*&Q=뵛[^.IW>n,MUn\j_sד­/EWՕ+|LDW.WL J]@UwRU;T˥ vHD3g%8bS$R"R&yB/Fy-BGIJ Jh b))BZ)AZ)b\:݌bVJbVW1+E1+%1+YNeCyY.W {F|MLmjޣ5#AUhyV /JJ]Vgf뉐JJ\:"$R"R&ٵ~9]^ᮔwml_>jۥEs~_=>|jר/_ zq+y-|KXBŻ=_]<HD/aPJIJv7-e0q94R2R Ʃ;P2 V7e7h0U LT%Lz\֋qkڙ ߻>}Rߕlk{1BҔzz\Ջjqf;wÛRB0aL؄v.~jCۄ|#`aD^w6` \LWw>m^S$R"ReKK] X Ixi^y:~XLW1jΎh'[7^n`敒25/N9my7 CďP]+)D%&f8S}6tX.lﱺ.,4R2RqſB,%,uf_ڻ,RRffD,%,e"dxp} g"grYJrYʔ_;ؼ1g)g>2Ə裥䣥LycRRo֌rRK֮ڮ5oAH8XW,E,%,e`<.E+%+J_[$i.F+%+u1^kԶRҶRMhjdj.SQb9)r5Y}gYUt6Uwݭ~'۱4F~q.kdh>?> ΕΕ2qmJx]{{DuL˫e^)*h,.<7RRwW+q1ޕ2.No^7:?$-?])])*JOKJJ]nW Kr<!JJΗOwߝy@G+%+e^n;G+%+u^JJ&L{-,ì0sK8WĹT߶Tz#tKW+c=V¢֕֕wL[R~ aBR&ʼnž//~ e2.^Ǡe2l ޿هeaGwKKJ_ⴳ6WRtAHQNk?T5YUy 2Ō zijɪ|^~Ƞ,fH3.Y,Lo a0%N=d 1bň#ʰPjwV6 cs*>gk ].d^/OGGːe>w.&q=HYO][Z=pAːez'hJoǣT2v&0ujb;6p'Ae\#zH50DQ2r['zQd ]v}!(^l/ôvЋeH2.^-Cؖb[mm4'[Ud5<-G:۟޻ҧWڄj]?82n'e Fq͊:/e\x(8?]ԣOvq]=o?wBā&&ɳW/=.5 WG^3āWrizA"f\DX.Cz]GN#\ķ5[Inm|cA3ːefkb2d1?Nk+Rm v8̵TRGv8LRJG8LdRIMw2DXaύҔ!i0)~j``Ќ2dFM&iġ PƅCVc#  bP0( *0iu@:ۧF6Tۍ]wϗ'CғaJOEՔG*d 1N8mSB Xγ\r|eݵgg23&ĉƙ0G ?g?tWAdOh5 yw2;[nɴax@k2A&B e2LˎI(BOA b]P{ygW8?zخ_sJQ(㒢6nmez_yp/2P&Cŏ~GO ӟ◿Dg/_ۊ3yL艡2.+&e*C2qTwW(O S*r{ʫO]Ngx}U*<%;Dэn7 }3Xl,㲱6m ^{.c~pS8nZ/2.l+Lmcl^owq!w0-A7z'Ç/~xy.CPaB]9\L.2vБcf'j0tւܴUmO>̪#7xeI2LzՁ}TH=<慮fq 2LKTTNW(z SR9gsS{{GeO( S 2|%)>0e\צ/n9 x 0f|s(Kp }4d63fXǿ{Yci&@/ El1ôŴ73O??zcm  \YPx;222N{XPM Űİ)ek+322bR*/r.[3\5I~}d$\-'kTnxXFxX<Ŷ~)0eeeLp,J˳Qy3C=.eȓeēe.,H|%H H222eDŽXFXƵ o&:ɇ2 x'/ZQݕƆi(Iqb űı)qBquo,0TdeeL?Vlnv99\٦pIf} e22$h222{n#C-#-c hcee,?3;;q1¢fyMULJ?gȌeČeLf,<bbjC7hedeLklfee.|l/#q2Ȳ@P* ˘p<$I˘QV=cѫ?UOe7yHrpZēeeLOLc:;?Sb_y:˘.l2X`ɍdWғA222E4 ＀nYFnYr˂ eeLL޷6UbEiI+˸Zs=w}_C,#,cdlI;E $eL#0geee.,l]HC\Z}&22V6Xַ'ѧNqٕ{#14Cg~pƏBZFBZQ>H>˸>hh@YG-#-s9h-чlRi1/GDG=B{'v篆/z'ZPZHZ\ZȢeĢeL{R]p9N{$hh@d?whfdYFYdΒ4 n\2&w&׻F,#,cgK{`4222ikM*C,#,sfK\ZDvK\!01~13N$t46(0]4gg?c\ZggY䙸_IYvp1Q2FyӓHeee.Y6vrQr ӋM^ZdަX:B,#, d=k[G_n)C,#,cejn1(ܡWW1֭,obfaf 3 ^TP=H=\w8>O'I&MrrW%)}i Fb'*gZU>1|Wzƀ)U,EN.DZLJծU9U9b2z'.>&vrH'aj4r2rA%)xc o;K6ШɨʙFSvIͣrrru"n~6A*'*wV=UNUΤ|b>pp+&Ͱsn4}{1_LsVl+͖l]Η_ի]ہiȪIV,-+U9U˨98Q'`7rrrT)O1B4,*-7Lr7f=1KqA*'*gRUs7>7$yU9ӫ p24 IUW*G*'*w)W^K;WniC*'*XI leSdi zT9yTˣb.y;Y W󫓳5Kiʉʙ$U~>zL܏z( UK ^ًO(srrp bY>8NM\w*Kr&oHV _6wqY]USF|VN|V䳘}5N ܅`?@*'*gUK۵ƀ۵)qp[csx6+ a{Űrİr°r&lkpD_-YtG(g$gL9Q=Dt㕨K1Z9*yoYU;xu:~)Ws=2lT޽`+Ðʉʹ?32Mq(ss./b]9a] _FC+'C+gZefɋɇ2 Y/xUn]itʙt/:4mϺGE+'E+g*Zx(tt.+PW   &% wT UU0Y/Ni%ͩ44 &ԥ]?VUUl.V UU0.4q#t Ct   _ _ _Kv %4-^^KdEWBv@ p]U߫-N_}hvdvLk)G…tyOn^(P*H*sb2}hG uF!.=5 ?fVAfV4լԬfwh¿@l lpa[A :_UU0u.~գj%.7&aB &}#+Ա ұ UAU~ r5;aY)&] dz: IlO;{m ֍UU0-Y8^T(P*H*, e.T**\,:Ṳ\L*f"q\EUS@毨{L}*~b^uO,rˇ***#n:\~wV)1ԙf"Y!YbT!`iihIkɃQ*H*. fif/P*H*ꕠ>z\UAUĭ5 iUAUPWc'̪paVa WW0aSU0*m/0X RURU0*N?.&/'',?KT(\$\.*P/!P*H*$ VA Vb'TMU+VܫzO82W4 kG |2(Z(?PT?|n)7;a?4lq28ݮ:}ԁqt ƷAQEw8'&d&Vr84FǃH;ib7 ^`416ǏtCSgߥ ˧3yԅz4EO' :hz: 1U 8ooy8i5\hhNyj[go=JmƼ0\ɘh1.xN'hZN꟔Fি2aӛ葔l]M&N$>ei*-NH``5G=e ˜Yڗ__o~ *M模_\4ZfZRDAB@^>bkGh/> Jj\Ϛ'Q$>xDA6S1qEH w)u;|Y@ SA^ˡ? ڷw~gk-oGbCʂmB7 %Wymgx%;"oq$;X:8;`JFwH!2;p b2CH!!/ˡ.J˜&Fg@Be>rRG38e2H4!' /%`;?\ϻo3}yxA_ i'承s_qzd%X\F^ þ+'@Nmq"$?i t/N%-o䵇Ïnp2XYٞ:۩;z'j'?\oىasvoyb8)8 6-k`۵)v˄]83,NOȮ;CpinIw} RX)K)(#I䀴9_0sݝ/<H-vmOhEtAx5 {v˷ $N,˴B|rmoPS;6^\vnv3E7S nEHCv^Imװrficrpeym\,BLpI䯜\C$Tֶ,my%QBxQ۟N4mQH+lCm5H94[D emnۻ1LCm˛MHqPv^#&.o խ["0쟍# z;$AG5JZ/9e˨j5{mgy-a/r<|q{8dž9t@OS69tBP۲ L7`#`\Ɔy]6W` ˆZٖ+ey֥FnᄦP9۲ppqMi{KZބ{``@yjێ,dX2v6K9v#:qSzg+9GͿxߩLen(i7T,2>j:~'Lqrp9l*PnA4p-O(iybQ=瓦f[dWzlaO;UgݞR)X(؂ˎhhH% yz9(0/4;b.>OLE;|ABJ-I,fK,fPݖճ-&1M,5)pUl(W{aĩu b트RVvWuVyDRWPhAț`pmV!rj,gp3F7fwf.# ؀"2ȵx@7S8 ,M0,, (r-\O}%szZMjOKK Q%./-+/9P8Z^%WP-Z2U}D}?a?xNK(:--N/2>2&ʲr6º$I)QLԌU( .$ϐv[egIHR() /k~Sj>Dʒ"%+q_5G=-=*R.Ԗ?{6㴒];!&$K:9Nȝ: f(T8QyU$$ Ova~ni5|aB]lBep3^b:gwc<( }7OZlZbd8ٵmZ wwxoP&7<9@;Iaz{yt^qyU0sItIQIۃ;g\|\*$%bi9*5.c(-g#*c$pdHAIIdي9y.*g"0IIZ"*cHH2^(HTݴXz֑#O10HHZ)I< 9 I&}nqDxD2HDY. [aZ\;" "iY~>Ô# #9AXì0GGqy(wA6eC* QQyx^{Ya`UQU0Mfo3&~oR(<QbLVX@TQ`!&`.OyNcm&(*Daҏҏ'W0GG}a^8 8 *~azzJ#qyGi0!? ?S`n000a> `>*泒0< <*>5nH<&َx>ctF7ǴGY<؞&( </ާA? @?cs_1 H Hٳ,ݠ\)LQ@QOJQ?X} 3' }}=\vdǙkNs߹$T T GZ]پI~U\L ) <w)<.7e))[쐅.))kО_xDF)@а0 F_a6vͤJ:L^0{6o6V! 81Tz/ђă$8ܪ_WY_GWBUe\-{|}?1IdB>&#i(ziZ0ZxG$&?{Cc6&&ٽV-hl&K +q$@D//;&t>,Qij B6[m7 R1 ;QS^O?2u>4-I'%n pOy2\0Xp! SՑDbr}~^Iv' [膚Ȍ@Z%Z:8.v"?Gl,e`&6/i_M|%2REJF .QJ 8qM=9;uܙMk@i&DAq$VMl%#`C.Z5K,K/$7A'1lj~yJW~*^ODH I^E+z\jؖ>e Ʌ(yPH9I^瑍ICSI*TE+BxF(LeQ!&LeMP\'1=\Z26e'$Ir8N.@G,ˏ,g fsJ8q (AW4h3TvA˂%@ C T"yB$ !ɫB0 $[T*QbYd5.!́j>vx?8q(W1 s@]˲vhV`ߥ1TiI+o@፼tӑWA$IPZò3*Zx:6܆e ;ۧ[ ỳE3>wF/, _,g4qZ@Mŏ1$kP"RJ0.ȫJA|ҝ` bs}?}O@ T P5²hr~ܺ_02k#,0c4_wSIuQ%p(aY= y&.&$JIp`YgP!óg P!C}i Tm,`W`^ح_f6E2$ɐOZ$C"dH"VmY^ĕ0$Ty0ve3$͐ye38"!20OMJQWߐPCZz\ZhEov}8 ~HD2ŲjmenYz R O7,3\%\we2XXlj e|yZK2þ0q_ˌyIAOM 3aV}^f03.L:t&31ƲD+{ލ 1TFUFZ(JĘ AXdLbx=W-B}Cc;ꊵ8sZya#5yQb~ְgZ9:bO8c|^CY'(׺<yq8>[=pX3Qjm@[f=#'c^r|2K6Z_hwܞ-{a9\4av3=yL]q{xpgq~r8jlA} "RyU#>h7yޠĬIn?CNX\`B2⓱fd\r{6rgs}yqxx۾A}@""ܞ- tly高'ܞ :}r٧rl'ח,x4r{>uYQyn~h%OX\GNO.OZgg&27~h'Bw l s{N xzw~[wGԩ͝t8QtT?xstݺ8=w{Fp"bܞ*x8`l r{F }r\;=k9-)c_Y>4|NNgPuX6ܞ Ky5jy1(` 9='7-MĚDxK|r{: 84pNkS"E>GnO>uvrzr~QI΅5K88Qc5Zl)o}zvAߴ.M+'d]Ɓs֖GZ#'Pa&8ӎhO@8=0>=gReuA"7q=tAQnw@+J*:r{F&xgoz)}tyQq8=:IgQygq{& (*cxcn Ge 6A=ۂ~yu0`,'P킼O C ͼOnO 1bjPd,XgQ~C;Q&0ԱpcgG?ڿ6O3+Xcټ_nOxƍ?'g.fۅN=f@u9MHRC )c[3lX(0Pt^8s0q33`AqyTكosW sq`\8v(Bxyogs4*!7r|JUB8'EtGhT!z٢Θꃰ?ObHuv;y <\$+,Gs/Dl{8w}LB*R{#~`*_b lxjP0W 7|z(VhڴOW'`\{<4}ǟixN>3vqi9Zu <f̃&>QЍ1TҒX;[ǍZS%`%wq`cls#`0<Sy89lia#ϸ(+ɋۃgPY0 eE{=?}wzyR\S>L<;H,]'%'euc3^ZEu ywxRZb/};8?!Fj1+ka!k~⑛cd`V_vkҋOy)-~ErO7A.%/-Ҫf (ZJZJ[R>[ύywTJk{־\[is4VR3o@ ZPTo@E/"bi÷S,b~+)(5`VSw}NVcJeGQ2Q8dhd$:;+㷩7ETxJjo?c3)ж~ bl)˒ĺ@hcà['v,4Jf֕Ek[JTm4@:۵.#Z| ږ+zTغ[yeGcm{m sZ8G;R?xea}`oۿ֏.RwoĶ7a/J˞Z,l+n+hv-pz>~V}$ZP"~3o{C]UkSۯ3y_c;oQY-lg}_?qx}FyDm\.l WRE% ۇ%(7Vţn:߶eߺ$O'v$6/  SdeI6X,ؖ{鴡>}k^П>CGj=zܰҡtx҃՗/`V\'q7Gfh@wң^.>6Ct"quE;|#ԹPãF|C?h19>rCѝdidh8]?`ބ}? sݚO~6ߟS+ӇοOvκdd]L\T,NMm󽞣[Ѷ̣guC"K onzf78jG;Dm:ZXu7Һ "uI#u90UnG_6hEk9 `%(^L)]'HYz \QQb!yBꢔrPÁEmbkARD\ƾ@+,=E)(U|.51P)rexf.urTzC9*N. Zz4 ٗG7Q$Zr!lrrsL\>]Pʾ\Nw/A?ܵq]'ɚQH RiBT)9J OTP5}**IN_'U!T>zvb_蹩 Gm# :ѧvgL2@w0vD|OPQ͇]Rw$%x\L}5JzL86^pܛK34OnO3P?(0{Ynk.Y sOSR$GNYs'?ߋ"?+NfG{+SV'+@8yjN(a6tUsB-աcCG-X")W1*E+E2RRsSSL@9&H@H& v'Ts[[Lf'l z~cDLUUL\QX"*1x#l%JE2Tn\oϳ@ċ81}t 0DUope>'ѭ!c d"QN/a̘MX", U/t:  $b!Zcዻx-"5~aHJô8X>=E'<<`nO5}ؕdE2a\Fam㸸62pT0&uNN%mkS.]q?fCCRռkFD ګHpp,pcd8Ñdv|2pedXxQ;bwݝ؝I$F1hXjeELg\zx:ϔ -l772zC/_6\E p$pd syMX#:O#FښIkpwdx&Ui:>+,yD̅d=,9;,y>ĵ\?e`?24GǦ= , bL[xZ  "Catv|Rx r) R RLd<\8A A*p=@1A 0A*DI  Gb}0VHVHaxDNa#xRTNaʃɜ##G<`$`$Fb,<[1ݻCk cjF+)@+<S QcAd!3<7#.1zhx}H )ȤLDψq!K K D5 ? b}2iK hKL|N0L%ujK vJTT_ha6 Ujw! -T~SAP3o= &Gab$Q8J~a44C3e.I/ >%XM*KVtI塛Ν.w5NMc|#uրO O*7g('('r"ٗFmr'~'aN `N*L_aЉ.!yRyRy''dοԲj 30jG 8<[^-dҫ*o/* vRvRy`b/"G_[;ȳ̍RRyܨgqEPacK=AiR hR*&tЛ=o q/Ӿ2$cvFL)@L)&(Ry ) AoTvDy'C{ER*(Qjq)pުS=))G`zȒנ0UJUJQCrh6V2ݏ1VJVJakmSo7Z_owxԨº )ǝb:Nk>Ki5NZ͋母'tWXJ屪x\MmuDq>V*oJ):4K”,,Gbr5T-Gbu'=fHZ̮+d'ZʖEu"-gCT}Gzsi\ h\ʖe9GN๔-f~gN"ÏVI]x$/eKZEY yԓ&z) z)[.N:wO2'`\aEyaԾ2s ^+PX݁Υl\KtG~R"g*RR⎲09K9K味nnV ,,RRH*t0 %{j `0;Κ0JJAzܕ9'%,R+ <++ǿZApĂ+ei0JJ6P %_ 0cKcK2 ?¤--Gr<]ܱt)t)&@(ŀRyX..` %ƧGdKY2H>nahhvўE1[ХХ,]t;t.t.eI{#.RRH.\,[y̅] ]ʒEs. RRy0/fuߋz$΃pwDT`HH981qhÓ @)K?I%K?s `<_Ȥ]> nqO=ZeDc7<g*z>sxUcs{)KS4x?'))[)<ǞǦǦpl/1[M[Mٲ,I>1zMzM٢B*>1}M}MVЩiḟlhKlUS 1LLQVܱBLVyIe HeTf? 6QĊ  2y@Khi Ia.=CYl].I8z6ͳƦ-CdW<. L kZüM7U|XR$yM{So)i[yR3<`yy̴…à3@g5lŇ\s.6yaЙ3t1fgmgS^h0JgM)s;_aqdNzOz@-a3afWr.Σoxd̳$Yg^^{/\Z8)uh%U{v0fy>,g>+S0g5qɼ"j#+~G{#w9ȾA Wͳ$1L OXYО`f#$ğFAs_|M_?t}x\z`2N؍¨ţ%qL|G: gp`X~Њ]cZ6 Ƒo!'~fFJo9|!QxsζR1 ;QS^Oğu7OADp$S>\1m8IWoZowC-e}bCOG##*zA?i9x~Z0 P<&EIS~gt?$av%;ǯ@{n'[RƯgهyw}Cw%΅WN[2dsstl7$1)T|\F%K̒]+N:qQuL 'Db @Lcq]( `g9Ŗ OԶy<3_:PZF+~}kK?O~/ ´Bj*\Zah#\-k.P}f ?Ǖ PAl^؟k?e}\]}g!kso~pY#d |0^UHG_NzZrុ Ubj婠jDjmQgTpA?pՄ;oFOAd6.V|zv13Kywz>8 Ґzjk`\MͶ+vZiwxp4adHlߑ3Va<|ت5s4q?Lq㯷&mI5e TjQZ}&P~ۂ+2`,yR]#HFʒ"ea612`$ARH-žŊlRaRm!D*ŲbzM'@D)xF9Dzr C D(K {.D%O~ƇL|H@mxTMn#79w$R)p,NEda\К`M?d$bUc*%,)DJ~QRD\nH% *F\X`Е,JƩݗ_dX$ xJy8C&%(ɒd׎tYZώOu=QEH"K"]N]CicDҨ@mf$e)-Qk1DyX3@V+T<MkxҎbA =X~8l%J-泌yWH&c&|utcǒcL})ʖ _0\ (٣l77Τ/sj4~2.3īyE5*֑F,3*`ER*CxU+xXDg[&I@<s @@@4{sL::y3C+; dj¯<¤m9DFfY 'Q`WyB6ơIIK rYsfwavYXpjD\p5 t5Wsv׼lxf6Go+&-;Z(*LSܕ;X$d KwM3 IK$pҒ'K5|3$@dmzˣ;''p̖bb3$@%uyeSw9&ǂ$dnw`; ;iɼ;1NNXhLy :1D5SzzV{IOc,.9kSApHpU1/$ޤ%J{Ym`mҒfl^_$I IKcp-#1MMZRI.鮘&&8o=gh6>Z Fbʚ̚-QjXj2%i2fa=$ dI >D)I B*D"~23[b b gAa @aIAa T(l%Tf?LS@SL' ~BB(dvG }DD@dD )><2<2##0콌y~t]?(Fo7\ww͕B?z'WYO3ZVjjϓe**=USUtd#!4 kZ_FAS2=h)5;I-etj.Į'CKM {H Qx=g^T&)u&-v~қ`Uj\(8y86-!5>N|JQig R7lX[Hb2Xe P_7{m{סI:H {LW9L{ٰ{v1ot(s2~ 0z"l)㲁mюvEV=[%\R@0Vxj &Np:7m7l(DɴNFͭŧAϏ/Y`4zz=OCLt?Ρnww`Lc}u&qgc^4w/O/^̺x|Hi(|ڳN'Sgп#p7&9H`1@-EO[ܾ|jEZ \<,[PhCD)P Ä0E6t[c0`VwM__ײ? |gʿ)JuoRvso?llgvS/Qxsζ~scWH/*<@oץ~/vS<[.@EbLutem8uz0?p.'`atk`$Awrս:htwp4vC=VIw%e$C?t܎OL~4q~gf)66wg秿66N[KQ?yxxl՛-Q?:O. ͋~8>h?g>]mhY~8}j7ͣy׼8iZ%89_'8ܛ8j5ĻwUV?CюgËSK~\oyW>/~_N9=uytaz޻cqt2M\Eݸ~'[?y~lC;hù8'ux|89lgNc.N^aGϛ- V]iگFeR̘O+~Ns=뱧!(C^%([`>FWz>2~wh=wue\t bLoN*k{w!ضZ5뻼d5fjb"vyyyn$^]`vճ`]oJ:wm/Ƶdm S ެ[:f+I u8Gſ] 0d1b3˲\rUJIaiyvFxV;,z3"ϊ :+=3Oأ=+I=,zcbXH̓}CL#_/ɇ@oGam2/ˆX*~89݁9=:Fa0k13$Q0q2& tPfkHk T_=$&ӈ-q5|w.[[fLr5$T^kO]CXKձfoZE$-mڗBi&M`ڦ#$TJkvRjlrsLxm6~LSR gNk:S!Kexn|b&TK5)^{OCJ^k,B;؍vنC*^*~s/=;nnLp$qj q)d ĩ>T0y`|!6޵kc+sk'vŃTv ]d?9ZwA־y^yOW-3d:bhY-lrݢ;>ͼ"=7oWa/6#^+C'UxQU'SOuqN$Ƀ2`maL ˓( xD8=F7cm^Rgͧ:7{|;^En2M~j\Gd-*ۇgEAM^./V6 y‡G>>=98=i ->9Cߟ7G.CתnOP|K'v2-N}@x ѢB!-G`7._cl tvo1FN$a/VV `U_bܘQcP7DC ddA))B@K&~wA+!|~;bbi<ܞ-Z?9Zỹ5Q#kz-Sؚ̼d/>8_eI@̼q.?[uC88XǟQWla4ŏp7JΖU?\HKo^ ֔+xrɂ5fe|+z3W09Vef$89?:eVX3n~daND I_yl.35YkďK_V) RyNW 5-58|9?œl)<3|w|y9ck~@ſ-HC~u[uB_`KY&t0EBtf{(:_ѱJU{PS[*^?5c~+x_)c՝@?&Wag oJGƺ3ѧKr9 ZmZ582?`*ܘ_^Mn NG`TYȌ~_ nlV?|\ > Qږ+#Ɓ"sK=h{"V.l{7VY[`QEف" cjG98PdV3?"s[Z oO°V^Y6?t>g wL+"c ԪEfθ Oϱ `2;[`>˂ٛiݞ,u2,Ih^&h䲢fjX=J [`w3&]Efx-*01 lcoo~nߜ%7/+2. Pa mfQؿq2չn/svO/XrGazk9q[#uhZ{aG"nZ.Z1gJwʳ?KS΅f Υb;sWNj=I]r =rKRbϝ.xhQ[vle4EV^$+:P"PۚN=Qy~_Sj-zje(KVX]U-Zx.c%ߝ7Ѻhmg4uu (U 7.5=Ѷ4%q^6}m QxҋT/'ӫ3TBoăV q#}9H倭yXȎ-6ڤ[IL~,A+eː)[~,2ŖZ|#' .+p00H` _tu/nF1 >ݠP{ ׿*[QPYWBi5OL~4Zþ3 !Q:$ #6wg秿66N-f򥨟~y\m㖨L>EѢnGnּ ڇSvuؓQS7#q9PW;u8?Mi5/лI*iɩh814Q!ޝSQso?ݬv8>_iOOZofD D욾~:O\хyOit6qjhuvkGxnlY^/'p.I457Nƙ؃s_f˴}վ :i$^!Ō)8׃{.bJT$</K@^Mw3noԏ^ <4K˄pYHoĥJȿ c`_EE| JT)K@,oݙyݼ\+Rpǧ*ZI|> L'&-<S'9JZ1T+ES]gw(ebxq@jF4{A[?0uC.iK]`/~q+{eUԷ}/bnh%Qޘſz㯸0ٹR >°Or[a%{O`T°%|(y1=:i݋<0O'3ӓ<֡جjm8:;uX?k.5~ 7w;!<6-l|U9m6a[@wӨ<Ӱ-.O~~a]Uϣö<ǧPmeޅöjq`02Eνe|;EWڭFEBj[ٗ`wKz{<N7-GpL\Y7YOJbNߤց-HJ+ln]lܚp&zC\u/LڭMi Iǣ[;i`rZ!^7VY$=0vz戺T.I[O` \fEV=&Zd[xa/=~ Oh5Lw{47N۶,b-CYjd-3Q~2>0w Lȶs.k2*L_TYyKغ:3yG.|-3"ۂ;4'rYdE]|N͢[Iܞ,tUgFѠ3.DesoPkjV^8+L:k> LC$GjGbD o_ڹ9׽.7"lY@G#7ŏtwJs= F_M[vy7g^'sTMr, i+ֻ&Zd[ym᭑xhqsغHv!×' -" h-X,^LumċBwSe{T .M5ʾ(R{U4_onShyoQe 7Y[{C;Dzn[-q*Rɛ<̈&ΡaRDcTZ1Gcv˜,˘##JP² qnY- @yhna`46|R8t xiy(3' <<3)#F&K;5F#j  iIE9K @摖hlV) ǂli Yj(d3$21CGDGQtV <Gqq( 3㭄}mdkoNH˔idn_S2FH`HKxqצO>vՑy\wۧ\[l?C< ~ʯ$V̯^T:y?40-i*J ;}qz( Aj- F7UTdudz`tʖq` q7O=PD74E֮&Q>#SrVES0H` .yսa@Ǟ1 >]=,"5_VM2HGd8{Gau3 33  K6wg秿66N+[KQ?yxxl՛-Q?:O.ŠRjk^|sA9m}F쨩#NUکmbMyGt5/NVI;%NNEɅh}0!7G zA K}c]Bh5#-~Ŏ)Jpxz2ץ#{:zS֩~2G..L{w~z,N[˖6Twp^C?sd's|  lyzb<0yR'GÆq4\ekvD2pzyAlԾ :i$^!Ō)8׃{.b2TdN2_@'wAkrr4.&qU`M y]I3'qx7AF7lq5X /{I P?푽{.튤2 c닉^WIxYdKofn; U菓7RYhJj)hnˤ_ѽY0;Y`GlWx?EԞ%/گ$ W;rwzSϸo DaBLx%ǎ^y8? D7.w ,e˂>m6܊;8W~a3 ?$SV41:ҭz_Ӡ&F"e'ELKDl YZM8:'CuKU+.UX$ϸx;."v/S=.;ܒq^&SW\2}<hG떯!f^/_IJ_㷨QҮs ^]ܿ3UU,[$u m5% S&shLz.v^9XՋk[襌YŭO[W95L eKUy`n!ze[^Ѡ#括699 OEosb맟CI;!!d.8%Wp-KK\+CR d?}]#m]:[O^Z` KE )n:L8V.gk߫z}w7D={Yֲ^Rv{ow/Ѣ>h늼p4`?KsEލ%ZTX jr+,[-bXm{Mvd#yKpKSy0PRݯTTCt2B~tDqH{R;.5xgrl]'7/Ru_bO3g}Luon 3 р3 \H+ĭZxB|v7Xu[flOkxk ]{<ߏ9"f{rMȽ~G~D^^־ v+|tĖχm ;gގy!ͬYǶd짨 n5*_#5TskLJ5Tk|'yCnyεTk<##E'7 J/$TkL4h )q-U?=][ާ2Re(}Jaao"lMQ^nhx=B^K%#cnΏ:h:P>Z*5$XԹsGoqOFz|/=GN3ŖvDڷ#Jx{fhv\rh?:Jǣpt'{z_x`7RTx=T~P0-A:Sf!KexۂHgS@:6ֹ {]!K5voFS׈;{HR޳&+oGree;gq}$H Y&U}5J.WyBЉ2ا=FΘGre7G T-+8hqh>TkJg:Joy(]޷{kaPSΖLjh^vR!OE~Q¿owl_xxL gcB:S'#O{Rmڙw4 u?HRݗ1H9@}S! =HhRʃT)xV}s q ,?HRy<1 E;HNі/g:uR Kyq#fyӕry;JjgArvفkof lvjҹnMGN'~Gw?!h`Rl n`Ru˙<;"_w`Օtp;0T#D([V@/-fy;Q0: 섲Ưt1P1h S2F*PiG)&ec](ɱ,cBp 'V\l<)^`PYFRo80PREVBfp@3Pl'柴58AhH*@ſ'6*i-`>A͘Cy!Ԩ(DL"QXg!&߁Іf!&ސeߝ1jf}¤' 'zZ[kZa6$dje3ߙx( |(rzR(LHvj SS ;E5 k`$g!bdbJp@@yHE(Y9bmDeT-cb1(0%( =T+ X+iɵZyjTĸ* *TjN@OI&eT0UC d3&-UȭĀT(g(' 'i}U^;%9I9IK% IZ!W7X}$O*D$$Ov ?V' 'i[- L0(QXN$aO&6I@6I&fBlLՔY;ˬ$$-K.eSdDX$KTX:I汓\=Ab8:% %QZxb~]#$0dTJ'GΊ0NIOI2Faḥ&6-:*"7%9@"<&uR$$HTH2gPmT(AV8<.btygC0$1$CdL!p7A$h I vAdSCFH`v>ś{so*QQ[<; ww%}<$nWC${ |ʫ8 vvT`ǹ }+ؕ bL-d0lGlGvVjبyw(QQL؝Uz̙71|;In|< <Ϣ ̷:5r`dd*c2&KQ|swpwTwA; @; COKQ`;3;< <*ʳ : :ʒ' ww%| HKY5;*2Eš aK3}0}TYe==ʒSI)^=1PGPGuV8_)wpcSapp,u,8V*LQ@QVbیE:*CmorpXń!8 8*cla8 `8*cW= QR#(K,y QQlL'?^ Hii6VO6Q0 jh0/nG]?Xp8TG!p9I7$.`ܨ< Pm%Ʈ!RDŽ APkk,6z@ Q ^`88Y?lM%(j!vf2x *%+=݊) R Ry !uiB hB*&T5 rZLAT@@qnB0GGY~:n0GG}V<G1|N[1GGq1}(9j#FhS*a4"f(QQLx;_WŃYzA? @?#K ~~%g9X urªlq6Jo)RRlz_FUϼ ?6gJeW&=_/&o?R-(FYjJ~YUeORS{,KNrUI]^!4 kwП |ݛ*cS|6#s^9FFw^_aM./G={G`|7+^_tw\*Lh|bFSl`2n/+?W/zWBUe\-{|ʜOF)W?O/p<)v0c?oa!IJґ0٨8d/_ q^xX%5E4.G^~Fb<]?&Wag oJGƺѧ[9Dgbp-p,'5սśx\|@3t3>tXk?[_Oi ĵ]8j6NZ qaMM9\:{QnCxWqÞ?Y9ӜZͳYS޿@StBͳ}xע}|yQ8=o/o&鴙bB3,FfJ)7w5SxDEu|gf|?~iҭ9z Z) Z2Rx'~QWwDTIMQyS~dj&Aj*dL\ FNfͩO `&SxctAY:/͔[a s+KcUjw$;ۭ|፛Dp/gS۹5A;-WWcU9ZZέs ,+gsG m9O-VAly?fٙ~C-|pF3d1WәѠwCSyASuEHk!)}Z$qݭv?q^ej2Dʭ$~Ǿ :PF dy dVfVNj;e;4r~'p3c>=fgt+Kj(W(hx;ɣI&>\eLgּ0MMEeLeߋ !2:߻؅Ox)peYFP :,HANX^JU_ˎwH +/kU $gYgtH{ */p5ڭFm_(ӫ ʪ2֞SkZ$%$EymnE NLe笭%2?My߈ 5AZSeqEHz a8t,E_k pIf־9:}zrh-&HZ}T.ZSq0em'o&k0 L R%YmԃwZͣzӄ KIp1 ļ@y@q^ﷵ_52N Ġ^6;zTxNL!DRmBIBL9BDB`ȊƏs $rzRzo0$)Cl0b,ggO*LK2-L۶zjQR΁܊8] FKѱQm2_&|j('-I[-QngVChW8zPChT):iv4_6}ޖ8jIKZTpR5ԌL5!;C!@*C˼dA Bmz=u0PLZ<**us^lO'Α. AC̸!L+ ](Of~_{AC&%|WYܫ*x}Mo:ZҀE[: Mzu8W0'z!Cf*  ho7LE dG vFS**0^'B׿0E F ;Mݠ?#)Z ʿ MecQ7m؟=+$=3ת. dht|6mbI$OAEb2H͠dۘZzHf 2rZ=9J "®s  fk*).Lƽ nq\/-8rcQACN!YhQiK:A* -B:rAYߢ  L3S?QxM+H$ZT$om /ù|zOin$Uϳnt}3.nwCs'ƷIkvnF~{VO{ɄzȰ'^yFP兲X*k5w`\ގwC$ה-tRfx6)EZ kqttz}\}yӟ-͛>(l[E>OLqEc_Y.ďb fSsQGNO2WdnLD?( AFNݏc(`&Wk#.De䁝 "q79))8e~xS,/ô.tUUw5G^~|x4ucÚsQ-pj1a%ebKK,ELA[좳5و `$ZT d1iEO_ 6У)L꾛c?ǧ''Cj;3㡞M.;IZܳEmT [B"̼OiWR@GE LG.Gg{u@׫h"ADlَM9>UW=k+H@#yV$.gְDžS]|Kڲnx+W"Dla Cb>aF36-2:7>ZCAEH`U*¸@Ӗ(\R0YF* L]pgO$yp5{l\$p?ϴ_3%tNʲ,+޷ߕN.Co2Zҁϕ $V_:Wꧮ~K뵷[Z--V{?~ig3{Ns}`X"ڛi@c{/N X`BsOC'{ ^hwL_EffZ0ϝLyv`s_fDLXZdEzdjSjhD L-Qd*.Ѣo ֹ7m%<ލvg0Nrk 7*4x3 {]a&;@51a=8v^[6Ɲѧ"i_}$)Cg'%*%_ Ḑ}!^oy,ڻn0nݽVT~i^&* Wz?'Iȴ*bߙ}IݱM]umq:qf2-ylB.$1wQ_+YSizͿ!xQ]ͤ%'/\E2R2y8u1ݥwlARkv[چ:ʐUmnI]xJ"}CcJWd#5תo1D\+> f=!qd.\zo_~Lf .J-?XJR~vRe3αuR"*ʯ5ӡ|+ΦTcUj)RCY$2eZɔU\c:eغ R=2mQRu0˓$Zƭ5)r6sZ'y鑫rAlZ3iZƵg#Lhԝ 7G*jo#+sI< (;Ib(+LوM%1kstjD4XCZʯ5)U*9Z['ꚫcƯ5ө|{6:}oJck,U3T&锅W$RvrU1]XZιSͳN4m` f.s,5t`jyIc+;LsU?9juT5TK ]co\YxNze+W} +LsyU FiՕS,FV} J'dsG̊vG,[?Z¹gUgܱ([͑+~#x jR3X6rU**8N,Z3ZƵg*QözTZZ4:RRɪ}5*J++MrYj^U/?$]f]-XjK}jNlYU9![Vl-ܳ㷛wBjckTr#/ZxNZe+W˭@LsYi&G~Z=z[kV^O葫rcUe֚2=C2lDReCU82%7R,Z'땫reQeؚr=Cv(XZ-XjU9ƠVvZ-3Twa_7ԫK ]cM+ IzY]Y8fzsFޜ¾oLUs,5te+o­u*~*8pVP-ܳ*S& *mirPJmƭ*~*X8nBsIe`Ufbs,5t`3­uR*~*8Ϊ[3ZιTVuPnR-Xj+V7QlZ'뗫rʱ5S{NJY*URRK9*Z JzcIaغiR=:o7KdK ]cR{=)KɦO1ck&Q9$jl+ofrN}NRmfcTNzL.3G_J jjw/+.+ƺ5]J2} g7G9AiCr,5tso3,Z'뗫rlSr٬& & ,xK ]g6S,Z+ꗫrC[7Zʹg)XQɏ ۡlhrll$«u-^*8v([3Zι$Zg͓ͺgT9J%ky3­u*~*xXU9ʔ~#`0}߿۠=B=K9V-B IzYlmp⥜{>.ިŖzckTvrUq(cTK9JpxAB6sgp9RCX6rc«5sljSvY%ckXlZ)U\ceغ)R=W(SK ]gȽ@ L6ʱ<t#XYhk9RCX˛Y^fmf-;O֩(Lr\}J'dsG6uaZ.@>^ٰЫK9¥62}«.^*X6p-ͫ%K]α5V+m\ٸNze/WG ckW9lv7Gަ^Xʱ5}e: ]\c ,+Ls V76.7u}ck,Vn ~œ+;MtuՆ6rb6򞠅WXvrU1(ckX9lkDa6G%ۗs,5tUX^jU9ƠZvj-S-Hx}ao+HŲj=fɅ3.6c|QJzm5eJ\cHL鑫rA!mZ3}\ƵgʤѦZi3/#.XjSu#O,Z'땫rƱ5S{V:v窳9bWK ]cR԰jʮW1slj9瞥Xt6GT7:s,5tJn«u+^*8[3Zιg#V뇛P^LZα5V( e:)]\cSVB-ܳRͣ$Jv%G nd$D;HWs`zTSKCQMʬ5XCQKƫWCQKuƣWCJQKƣD@R{!ZQъ%ՐvRhӤ ш%ՐRA C!9rRchKr#"QjCQK#ĸTb/U= Za/=q^҉T'xWhԝ "KecG6`vq$STBx%!!KcY?荇b/=^ F=${L5!MK5aG!)K`G (7N#da?}M%:TPBϼL&0_M?FGߐ>ҔTSy5eWRCz>6[["QOEeWTqq ~*( D(t+J敉aGEHR:(r\?@sl# ;5%RTxUs8İ~ U!Z q HqR9v'":HEWw +t#`{`#*9l>p,dV"z `>^mFBEHiFBCzHEChHp;JCxHޕlW=mŚ"AS$c0H!% #yEǼna| ̏~$V( %y\X/zA<1@@AV` d&7Scbc0brtc  y2/%˼}['/!/_2%PXr%dK}NN8m_B޾dNwG} ;s܂8__B¾dاF@/!__2%S# ї/Fa|ɔ:ĉ2%S>|oÛ`{'/!_2gԩ0z1;:~+^$Wo'' " {h  /HHf@r4DwܚK$3 * T < @Du *  %vl.V,Hf`û!>t x GOL,|wխ5Z@2AK $S4M@2Fad MLpAr#$$3lౘ1\` dɌ& GLL##<$b&؜XO Ԑ P@ L M T@2 C&VH&RÐ.=&H Hftà9w@uם]F$$3<1@@23 iֱ1@2C  $ $À:1@@2 Fa~dġM1 $ $ koNLuEͬm n [ ̏1#mQ@ad cadFlwFuiL01j Ʉ1J +@+LQXVS @LL,,L&u$H$HH&\Lc`v $3L%o p M]+ p $3((&n1(*&P&PLhQ (3 xxń'@\p \abbb"M 0@@1 & 3 0 7{`8QOOx:Fabb$1颧``&K  %O X0@@qs -=@|ȇ @5 e "CÈńzQ30330$@$@1CQC@0@@1FaŔ P8_AbO;6)abB0<@PLd  @y1Ԙ+jé RS?(,ۯr?Wۯrq4=\|)li0:f_##&( (&B@6ˆŌ ܏]K 03 qr~ŔO 8_ANbRSkĩ RS>&݉ Wӯrafs>(b(֮ȣc` |v0@@1E-i]vrl GQF@PLTr$D +  HZbY`t<>mcj,OPL&RVܘ+nLP@P-F*Ō FC _1S!W˯rFa ~ŔM{O/"VHWIӨ)oFr_d+,D'mXq '+HWL g+WLФNRs?50 )ßq4}ŔO5 k$+|]OqZ|Ŝp~eg+WLEG,>ȯ9#WկY#NWگS+HWL3d+|QXG _1k=0{9>'+HWLt"Oo+<}ES>4c3ba}ŜO8S_AbԧDh ^1%S)t+HW ܿ Fݰ'w4θWq2c$ @//wG NWƯSW'8_ABbJφ[TrsiQU௘A+~r)ChNWz~t|+t|8Yvx|)5 I Q_1%s3q־}ŔHoA'+HWLS+|QX( ^1ݻN1q½{ŜpaO@xCDx8ރ{)|sxII.8y8Y߃d})Yu}=}h]6ujM끹É${L }r=}Q5lT b<Ig{1gC#&CzUg{1e?g'{1%;EA*ǔ?phAA*ǜO8߃~) qʾ)S>(,1eBAǜ? bY})e0JɼT=p~S~{`BS?;±!@¿ǜO8߃T)՟qrSr?(,%1GA6ǜg;" c/`=H"x@,E1 @<&9b)h cw8߃<9A4$=1%JD p/cp1;vFH 1{1SbIS?=s{n'Wp)(P  fVOV,,z-Lj{A0PH}s80]Dևq㺶*V(`xj`ňbX1 @5 X(x1V=x&)7U.1FDŽMސ%JcF P#$Fx  <&B(,&<@xhd-cdHA<&KpĺT*0kҮSh<'@ >cPC#fxTc[b"DOT<&@@ޮe;U,1`pĨPj s{bnj GN,>ȉ@hfx <x̸t <&<1]Dp3gc HoA-@)X<&5bt @5 <&Zs,0 Aj5xyI%'nL41#fxٙ[;bnjFI-1Qc<xLQXP cJw8߃<9{ӌ{10cX9"b@ 2cx3 1$DŽB 1AFaـ)5=9C=c<xL9 2VTU u\À`NďU< A13TZ p*LQ (3)K TPa+Ƿ>u`W0? ? 观i ThZ„Vo{2 H GlX2#`~PR@0~Z#ۉ&fO *Lq Tp*0*@0C*0AFa@ 0g1+*4c:;WP8@ 1* *#bn@&n{ *j Č 0*Lj *L QX3 ”1 3›X,10q!VT(#* 0!!TPaP!fTPabP!N@)j H0%ǁp2ad s!B,*_aJAa> f?ypY + 0M|zBpT *Ll\.W0z 3zE0c 7c i{ܻ~C׼Θ&P@&@5LIDfTPabP² " 5NL 1D ^ `*LI=&T Pa&8H,>@ pS-Uf85a0@&05a@8&(,_aJw@U `*LLkè|![U&6 ؙxrg";τ1N8 3N X&w96a@`fzl*0P)SbU@)@X&Pa P>T"PaPR C5c@`&@&_~DMկ`@f?G~]=P@=@"P@"ΤA0*Lj *Lr  8jT*02]JŒ[]F8f T5Paf %& T,Pa& $4SSb@3@ 0P@0@ ,P@,@5 *L`SBLmDH\C2 T[MO*#0\^1o 3o`+=$TԈi ThԈ) T(Td< *,0 \# T6Pa l95"VT䘈h&1&V1E *EjFI0P#&V1W \*W`ALXń*̄y^^пnCU@TqUT[ig [[kN# 2!!s[V=N@2B&TFPe, UT[D'>r*FTIPeFE $ $rlF,/$23  @$2 @"2FaA D&XM؏bAUf>5(b:AU&:5(b*AU&*(%2A "TSPe<%YU`TYqT55`UfXSX**31,bZAhUfZ5,bVAXU&V5,bFAU&F( (2! "TZPedboSslYPdAYvǃɨ3aLm{h҉fNGx!>DL=pcPAc[ݵq@+ GV,N%2a "2Fa@,Y1c *c`s*L58fTIPebpJ $ $plbAXUf5$b A(U& 9$b@<@5 LUTD  @$2 WUPTA U1 *31(bRAHUfR5(b@XU&5(b@U&(2"VTsASdsFʄEO9E^ʄ5P@51r ȁ*3r1g *g1, *,j@T|A2bB6k LLPU܃"VT(,A LHU TԠyU TxԠU@TT~">2!=ɰIU TIw=L5 L`ӄ,321R7ޘ=PB@-=m\Ċ*3&+1 T*+qLT@)=Vb}\*s?.>$B)q4f/?;?ԑ]äjLHY܀pjL܀drDZ1T┦!5Ԙ 0Yù51Sb`3;D$טcSv7f<&adkpǦ昦5Ԙ$"fԀ PcfP"PcP"NA)j1E_tsà,Fc @1h#,bN@ 85&NSX3#DE_S?( _c 8 5aљnV0jL0.%k P4@ 1''ZނXVPcP"Pc"H1%Sy5<6O<4_s ({ZtcSlqAlnW I~q5Fd2Ƅ 5Nm;0SLS ZOQ5@ ԘQӍ,튥 5f5c*@ 5&*5c@ H5&(G_cwDV951:Po Ɣ8|2wL,\Wo`ptIY1yc~@ 5&~@.Q sj 1sL6Kbm~@@P`@ @PH@ @5 @jLP5Ԙi7 ԓPcfA}%|9=7oH0݉}?5k2P@281]t3]`+my]3j13q3j11qsj1qFan@717܀7 fGAFM6 jL07nh]ZSK A77a4N?;!1 Ԙx!Vh}ӺBh;p[DcA 05f[qq~~zN+0 j cM;UڡpgR`aA 5fAi03~zPAz@U?L;D;`5&؁Q?lO%fnC1=vHGu (5& n.]XwPc<1Ѽ7ߒ;+`!Xs-Xm(jy(0[Q=S1׃]{k۪zOBn {XPc),roi(&|xsj]q<'N,nb1R^}=[8 f/TO=C:E"ċTW$jX1<˘0QunO~k#{-.'<OW5'o.`"D-4#1P!jLTQXcQ˃A8鳟&d@ "QD-"A 0SLSjVIGԘh㏇#V"7#Jc D*A5 !0#jL"j@1"yzO.J: _$=o%&JԀ(Q#JPML#' uCXbZph%Fd{Ģ  5 P΍y0FVYQCV,K_q$߭vK7WApM@\QrE-\tB+cE-iluäi(zi\8Z$'?}p6& ֋>h*F~n(Gz b)zZ%]l|!hF`^fŧ93wp[1(ExnbU.򥪕hEHa;:uս?Np|MF+Zq>sGɝZ'YHg_vkp07:wݸW;SY{kM;2؈L bI3M'H<ĝ"Z`SLЍg8ĆzrfqKFc.fL$c3ЀB *&fUڛcQdg#~YLֿdToi/c'gVKQS}?\4<9<|vKmeEyzozgY#IM;=C;Jt T)3c*z; m빉ȫ!#n= /P$:,㕝457NSoVc[Ȧ~V2&4꧗|$ 6FьhAv.DMhh5:ӐBG3`1!Ј9͘fDЌgf/{w> ^|z]@}Hv:F˗?.mi Kh`NI6|O__ Rk11Ҵ3^XQpgCO41T jLH5jfBF" Ϙg߇No ⛴`;I"gL𳌩,E3&ټT| <435^LkdNh㷈6#8ړ0 hL`c`yJh}sE]W;D{j+ ;5U[y*1xM4iӘiDy8 F@?i(ŋcy @YlW/D ¼'K›ݢ4T[j%eal]$oEAyܽ<qz7#=@+|r:F--oT[ޭ˳yvLu70 _M0ԭݑ2'%dpP(j~oo3W^/sߋQO)6&lɊK^2qJk]c®QjGLrןOM& n)#Sb]>QƮ7.G6pR٥Lmԥ'MaT)*&nYb84c6,bto[~?&WA6Cz,.9S!5R22МL/TVOtx_=Uި+ݒ8+r-6w^{a0*  9.5O`2N qzw;Fk=^&CrjKfҠ߻XL룗.!\ZRR/I"Ƀ4lΖ5GѱPPJL?Όu~=eM_b@lsK7=~q$-?d :%n9{ 3r %)sr0 (oL7=l#0^%%4ؠCKygO~>. gKvݔMO*L8 Y۱$N1 oy7&d[0pyp8'o(7eSR<ځR}B28&VTX.HX91A!i\NΩΕ3D/ D|:FF:," zS# œ%`Je;Ǐ~hsy{NSfQ K%gav38ﺟsmFI*/J)(@lsV^ 9Ĺ<"i,s30^xA l:K46 X Y=/[ǻ'ۅdyJ3HlǵsǕٺfLdgԭO" f!N̐OyW1i ۶|poB1 ,/o2q2FK-QZRh2!6 U>_1+}`vD姼`$˘PCW0u,PCRu6z e34`#,m]թCYh2v* ;#՝ AiPc(1w}rf'E,eK3ReɩA`̰f8kUeq75Wh{~3ޟ} z d~~jfł߸߿ [mcխ < kM&bܯ_^ߌocA_hӿߛMu'KCM"` _+H]yA- :qGWD:fW0=iN>" 頡Cdg߷lCC2MwgW͇]Uة١` գ0P4wv$A3`uŞ~{;BvT[Ro?̑$nhD&%/2ƑqVU#M(_M>^6jY~m^̕c6Zy Րu70,?N(l9pHDb(b̸Oya_Z;޸@Y,Vϩb[Z0,'tr`霫,0QLߢ;]HhN]dLDb"}PtycB"„DrN6m<=AB>̑$z*;Ae d̋2(19X!6۽bH nB%!349;!CN]6D~f oXT "X y<[ Sxb" 4 ^@d\f 70 s&lj}AUԣ7B1º DHB򺔊\,A(F{)̱"C["QԸ'hW`I axƜGs]2u0K+37Ib!kJ]lzf5?.v+F7Ek7w<41c{YպC@@I}((n>/̄S(܌K$oT "'mBIߤn 9#"$C5rzB $1͸& ҷc.~i% \b> ?٘҉m~RV/IcOYԭ p2L99s5گ_PZ6iQ{T*$XveHrL[V1/ߎCz}ٛ/:]qV?*ڙ]x*L!ڔgUi;KX _M{céIѾIð_kPĽ<7\&Ȯ~3p/zviQ4<*k +d֫ kתVøV|VW'٘ A+"QO ,R.iIKZҒ%-iIKZҒ%-iIKZҒ6H:libfido2-1.15.0/fuzz/summary.txt000066400000000000000000000227251463251454000165400ustar00rootroot00000000000000Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ fuzz/clock.c 24 1 95.83% 4 0 100.00% 35 1 97.14% fuzz/pcsc.c 59 0 100.00% 8 0 100.00% 75 12 84.00% fuzz/prng.c 31 0 100.00% 2 0 100.00% 35 1 97.14% fuzz/udev.c 110 2 98.18% 17 0 100.00% 126 12 90.48% fuzz/uniform_random.c 7 1 85.71% 1 0 100.00% 12 1 91.67% fuzz/wrap.c 23 0 100.00% 3 0 100.00% 29 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/timingsafe_bcmp.c 4 0 100.00% 1 0 100.00% 7 0 100.00% src/aes256.c 118 3 97.46% 8 0 100.00% 157 11 92.99% src/assert.c 628 45 92.83% 63 4 93.65% 782 51 93.48% src/authkey.c 52 0 100.00% 5 0 100.00% 66 0 100.00% src/bio.c 458 20 95.63% 50 2 96.00% 592 24 95.95% src/blob.c 53 2 96.23% 10 0 100.00% 83 4 95.18% src/buf.c 8 0 100.00% 2 0 100.00% 16 0 100.00% src/cbor.c 1112 14 98.74% 58 0 100.00% 1330 34 97.44% src/compress.c 105 14 86.67% 5 0 100.00% 122 24 80.33% src/config.c 112 0 100.00% 11 0 100.00% 154 0 100.00% src/cred.c 691 39 94.36% 75 2 97.33% 911 46 94.95% src/credman.c 428 10 97.66% 41 0 100.00% 562 20 96.44% src/dev.c 344 65 81.10% 41 6 85.37% 383 80 79.11% src/ecdh.c 117 2 98.29% 4 0 100.00% 146 5 96.58% src/eddsa.c 88 5 94.32% 10 0 100.00% 114 9 92.11% src/err.c 122 10 91.80% 1 0 100.00% 126 10 92.06% src/es256.c 315 5 98.41% 19 0 100.00% 372 11 97.04% src/es384.c 158 5 96.84% 11 0 100.00% 198 11 94.44% src/hid.c 87 2 97.70% 14 0 100.00% 145 3 97.93% src/hid_linux.c 202 73 63.86% 14 7 50.00% 277 115 58.48% src/hid_unix.c 29 21 27.59% 2 0 100.00% 43 26 39.53% src/info.c 232 0 100.00% 51 0 100.00% 409 0 100.00% src/io.c 193 7 96.37% 13 0 100.00% 230 12 94.78% src/iso7816.c 18 1 94.44% 5 0 100.00% 38 1 97.37% src/largeblob.c 525 18 96.57% 30 0 100.00% 693 43 93.80% src/log.c 39 5 87.18% 7 1 85.71% 63 7 88.89% src/netlink.c 329 8 97.57% 40 0 100.00% 498 15 96.99% src/nfc.c 155 3 98.06% 12 0 100.00% 244 9 96.31% src/nfc_linux.c 172 73 57.56% 13 6 53.85% 242 114 52.89% src/pcsc.c 204 1 99.51% 13 0 100.00% 282 3 98.94% src/pin.c 430 3 99.30% 26 0 100.00% 516 4 99.22% src/random.c 6 0 100.00% 1 0 100.00% 6 0 100.00% src/reset.c 24 0 100.00% 3 0 100.00% 23 0 100.00% src/rs1.c 21 1 95.24% 2 0 100.00% 35 3 91.43% src/rs256.c 145 8 94.48% 12 0 100.00% 178 13 92.70% src/time.c 43 3 93.02% 3 0 100.00% 43 2 95.35% src/touch.c 67 0 100.00% 2 0 100.00% 79 0 100.00% src/tpm.c 103 0 100.00% 9 0 100.00% 194 0 100.00% src/types.c 29 0 100.00% 7 0 100.00% 56 0 100.00% src/u2f.c 572 4 99.30% 17 0 100.00% 726 12 98.35% src/util.c 14 1 92.86% 1 0 100.00% 14 1 92.86% Files which contain no functions: fuzz/mutator_aux.h 0 0 - 0 0 - 0 0 - 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/fallthrough.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 8855 482 94.56% 750 28 96.27% 11516 757 93.43% libfido2-1.15.0/fuzz/udev.c000066400000000000000000000151501463251454000154030ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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 (IOCTL_REQ(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.15.0/fuzz/uniform_random.c000066400000000000000000000034031463251454000174550ustar00rootroot00000000000000/* * 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.15.0/fuzz/wiredata_fido2.h000066400000000000000000001034231463251454000173310ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 #define WIREDATA_CTAP_NFC_INIT \ 0x55, 0x32, 0x46, 0x5f, 0x56, 0x32, 0x90, 0x00 #define WIREDATA_CTAP_NFC_MSG \ 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00 #define WIREDATA_CTAP_EXTENDED_APDU \ 0x00, 0xa4, 0x04, 0x00, 0x00, 0x02, 0x00, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, \ 0x00 #endif /* _WIREDATA_FIDO2_H */ libfido2-1.15.0/fuzz/wiredata_u2f.h000066400000000000000000000162371463251454000170300ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/fuzz/wrap.c000066400000000000000000000222031463251454000154060ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mutator_aux.h" extern int prng_up; int fuzz_save_corpus; /* * Build wrappers around functions of interest, and have them fail * in a pseudo-random manner. A uniform probability of 0.25% (1/400) * allows for a depth of log(0.5)/log(399/400) > 276 operations * before simulated errors become statistically more likely. */ #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(ssize_t, getrandom, (void *buf, size_t buflen, unsigned int flags), -1, (buf, buflen, flags), 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 ) WRAP(int, deflateInit2_, (z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size), Z_STREAM_ERROR, (strm, level, method, windowBits, memLevel, strategy, version, stream_size), 1 ) int __wrap_deflate(z_streamp, int); int __real_deflate(z_streamp, int); int __wrap_deflate(z_streamp strm, int flush) { if (prng_up && uniform_random(400) < 1) { return Z_BUF_ERROR; } /* should never happen, but we check for it */ if (prng_up && uniform_random(400) < 1) { strm->avail_out = UINT_MAX; return Z_STREAM_END; } return __real_deflate(strm, flush); } int __wrap_asprintf(char **, const char *, ...); int __wrap_asprintf(char **strp, const char *fmt, ...) { va_list ap; int r; if (prng_up && uniform_random(400) < 1) { *strp = (void *)0xdeadbeef; return -1; } va_start(ap, fmt); r = vasprintf(strp, fmt, ap); va_end(ap); return r; } libfido2-1.15.0/fuzz/wrapped.sym000066400000000000000000000033111463251454000164640ustar00rootroot00000000000000asprintf bind 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 deflate deflateInit2_ 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 getrandom HMAC HMAC_CTX_new HMAC_Final HMAC_Init_ex HMAC_Update ioctl malloc realloc RSA_new RSA_pkey_ctx_ctrl RSA_set0_key SCardConnect SCardDisconnect SCardEstablishContext SCardListReaders SCardReleaseContext SCardTransmit 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.15.0/man/000077500000000000000000000000001463251454000140475ustar00rootroot00000000000000libfido2-1.15.0/man/CMakeLists.txt000066400000000000000000000360011463251454000166070ustar00rootroot00000000000000# Copyright (c) 2018-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. # SPDX-License-Identifier: BSD-2-Clause 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 es384_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 es384_pk_new es384_pk_free es384_pk_new es384_pk_from_EC_KEY es384_pk_new es384_pk_from_EVP_PKEY es384_pk_new es384_pk_from_ptr es384_pk_new es384_pk_to_EVP_PKEY fido_assert_allow_cred fido_assert_empty_allow_list fido_assert_new fido_assert_authdata_len fido_assert_new fido_assert_authdata_ptr fido_assert_new fido_assert_authdata_raw_len fido_assert_new fido_assert_authdata_raw_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_assert_set_authdata fido_assert_set_winhello_appid 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_certs_len fido_cbor_info_new fido_cbor_info_certs_name_ptr fido_cbor_info_new fido_cbor_info_certs_value_ptr fido_cbor_info_new fido_cbor_info_extensions_len fido_cbor_info_new fido_cbor_info_extensions_ptr fido_cbor_info_new fido_cbor_info_free fido_cbor_info_new fido_cbor_info_fwversion 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_maxlargeblob fido_cbor_info_new fido_cbor_info_maxmsgsiz fido_cbor_info_new fido_cbor_info_maxrpid_minpinlen fido_cbor_info_new fido_cbor_info_minpinlen fido_cbor_info_new fido_cbor_info_new_pin_required 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_rk_remaining 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_uv_attempts fido_cbor_info_new fido_cbor_info_uv_modality 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_exclude fido_cred_empty_exclude_list 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_list_count fido_cred_new fido_cred_x5c_list_len fido_cred_new fido_cred_x5c_list_ptr 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_attobj 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 ${PROJECT_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 ${PROJECT_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 ${PROJECT_SOURCE_DIR}/man/style.css DESTINATION "${CMAKE_INSTALL_DOCDIR}/html") foreach(f ${MAN_SOURCES}) string(REGEX REPLACE "\\.[13]$" "" f ${f}) install(FILES ${PROJECT_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 ${PROJECT_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 ${PROJECT_BINARY_DIR}/man/${f}.gz DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") elseif(${f} MATCHES ".3$") install(FILES ${PROJECT_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 ${PROJECT_BINARY_DIR}/man/${DST}.3.gz DESTINATION "${CMAKE_INSTALL_MANDIR}/man3") endforeach() else() add_dependencies(man man_symlink) foreach(f ${MAN_SOURCES}) if (${f} MATCHES ".1$") install(FILES ${PROJECT_BINARY_DIR}/man/${f} DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") elseif(${f} MATCHES ".3$") install(FILES ${PROJECT_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 ${PROJECT_BINARY_DIR}/man/${DST}.3 DESTINATION "${CMAKE_INSTALL_MANDIR}/man3") endforeach() endif() libfido2-1.15.0/man/NOTES000066400000000000000000000003521463251454000146620ustar00rootroot00000000000000To 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.15.0/man/check.sh000077500000000000000000000034151463251454000154660ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause 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.15.0/man/dyc.css000066400000000000000000000011151463251454000153360ustar00rootroot00000000000000 libfido2-1.15.0/man/eddsa_pk_new.3000066400000000000000000000071101463251454000165550ustar00rootroot00000000000000.\" Copyright (c) 2019-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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: July 15 2022 $ .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 es384_pk_new 3 , .Xr fido_assert_verify 3 , .Xr fido_cred_pubkey_ptr 3 , .Xr rs256_pk_new 3 libfido2-1.15.0/man/es256_pk_new.3000066400000000000000000000076421463251454000163530ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: July 15 2022 $ .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 es384_pk_new 3 , .Xr fido_assert_verify 3 , .Xr fido_cred_pubkey_ptr 3 , .Xr rs256_pk_new 3 libfido2-1.15.0/man/es384_pk_new.3000066400000000000000000000076351463251454000163570ustar00rootroot00000000000000.\" Copyright (c) 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: July 15 2022 $ .Dt ES384_PK_NEW 3 .Os .Sh NAME .Nm es384_pk_new , .Nm es384_pk_free , .Nm es384_pk_from_EC_KEY , .Nm es384_pk_from_EVP_PKEY , .Nm es384_pk_from_ptr , .Nm es384_pk_to_EVP_PKEY .Nd FIDO2 COSE ES384 API .Sh SYNOPSIS .In openssl/ec.h .In fido/es384.h .Ft es384_pk_t * .Fn es384_pk_new "void" .Ft void .Fn es384_pk_free "es384_pk_t **pkp" .Ft int .Fn es384_pk_from_EC_KEY "es384_pk_t *pk" "const EC_KEY *ec" .Ft int .Fn es384_pk_from_EVP_PKEY "es384_pk_t *pk" "const EVP_PKEY *pkey" .Ft int .Fn es384_pk_from_ptr "es384_pk_t *pk" "const void *ptr" "size_t len" .Ft EVP_PKEY * .Fn es384_pk_to_EVP_PKEY "const es384_pk_t *pk" .Sh DESCRIPTION ES384 is the name given in the CBOR Object Signing and Encryption (COSE) RFC to ECDSA over P-384 with SHA-384. The COSE ES384 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 , ES384 public keys are abstracted by the .Vt es384_pk_t type. .Pp The .Fn es384_pk_new function returns a pointer to a newly allocated, empty .Vt es384_pk_t type. If memory cannot be allocated, NULL is returned. .Pp The .Fn es384_pk_free function releases the memory backing .Fa *pkp , where .Fa *pkp must have been previously allocated by .Fn es384_pk_new . On return, .Fa *pkp is set to NULL. Either .Fa pkp or .Fa *pkp may be NULL, in which case .Fn es384_pk_free is a NOP. .Pp The .Fn es384_pk_from_EC_KEY function fills .Fa pk with the contents of .Fa ec . No references to .Fa ec are kept. .Pp The .Fn es384_pk_from_EVP_PKEY function fills .Fa pk with the contents of .Fa pkey . No references to .Fa pkey are kept. .Pp The .Fn es384_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 es384_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 es384_pk_to_EVP_PKEY returns NULL. .Sh RETURN VALUES The .Fn es384_pk_from_EC_KEY , .Fn es384_pk_from_EVP_PKEY , and .Fn es384_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 , .Xr rs256_pk_new 3 libfido2-1.15.0/man/fido2-assert.1000066400000000000000000000151541463251454000164410ustar00rootroot00000000000000.\" Copyright (c) 2018-2023 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: July 3 2023 $ .Dt FIDO2-ASSERT 1 .Os .Sh NAME .Nm fido2-assert .Nd get/verify a FIDO2 assertion .Sh SYNOPSIS .Nm .Fl G .Op Fl bdhpruvw .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. .It Fl w Tells .Nm that the first line of input when obtaining an assertion shall be interpreted as unhashed client data. This is required by Windows Hello, which calculates the client data hash internally. .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.15.0/man/fido2-cred.1000066400000000000000000000153571463251454000160620ustar00rootroot00000000000000.\" Copyright (c) 2018-2023 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: July 3 2023 $ .Dt FIDO2-CRED 1 .Os .Sh NAME .Nm fido2-cred .Nd make/verify a FIDO2 credential .Sh SYNOPSIS .Nm .Fl M .Op Fl bdhqruvw .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. .It Fl w Tells .Nm that the first line of input when making a credential shall be interpreted as unhashed client data. This is required by Windows Hello, which calculates the client data hash internally. .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.15.0/man/fido2-token.1000066400000000000000000000215161463251454000162570ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: April 11 2022 $ .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 holds the blob's base64-encoded 32-byte AES-256 GCM 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 holds the blob's base64-encoded 32-byte AES-256 GCM 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 a CTAP 2.1 .Dq largeBlob encrypted with .Ar key_path on .Ar device , where .Ar key_path holds the blob's base64-encoded 32-byte AES-256 GCM encryption key. The blob is read from .Fa blob_path . 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 a CTAP 2.1 .Dq largeBlob associated with .Ar rp_id on .Ar device . The blob is read from .Fa blob_path . 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.15.0/man/fido_assert_allow_cred.3000066400000000000000000000051531463251454000206340ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: December 1 2022 $ .Dt FIDO_ASSERT_ALLOW_CRED 3 .Os .Sh NAME .Nm fido_assert_allow_cred , .Nm fido_assert_empty_allow_list .Nd manage allow lists 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" .Ft int .Fn fido_assert_empty_allow_list "fido_assert_t *assert" .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. .Pp The .Fn fido_assert_empty_allow_list function empties the list of credentials allowed in .Fa assert . .Sh RETURN VALUES The error codes returned by .Fn fido_assert_allow_cred and .Fn fido_assert_empty_allow_list 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.15.0/man/fido_assert_new.3000066400000000000000000000213241463251454000173100ustar00rootroot00000000000000.\" Copyright (c) 2018-2023 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: June 19 2023 $ .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_authdata_raw_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_authdata_raw_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_authdata_raw_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_authdata_raw_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. The user display name, icon, and name attributes will typically only be returned by the authenticator if user verification was performed by the authenticator and multiple resident/discoverable credentials were involved in the assertion. .Pp The .Fn fido_assert_authdata_ptr , .Fn fido_assert_authdata_raw_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 and raw 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. Note that the resulting hmac-secret varies according to whether user verification was performed by the authenticator. .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_authdata_raw_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.15.0/man/fido_assert_set_authdata.3000066400000000000000000000203731463251454000211700ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: April 8 2023 $ .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 , .Nm fido_assert_set_winhello_appid .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" .Ft int .Fn fido_assert_set_winhello_appid "fido_assert_t *assert" "const char *id" .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. Note that the resulting hmac-secret varies according to whether user verification was performed by the authenticator. 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 The .Fn fido_assert_set_winhello_appid function sets the U2F application .Fa id .Pq Dq U2F AppID 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. The .Fn fido_assert_set_winhello_appid function is a no-op unless .Fa assert is passed to .Xr fido_dev_get_assert 3 with a device .Fa dev on which .Xr fido_dev_is_winhello 3 holds true. In this case, .Em libfido2 will instruct Windows Hello to try the assertion twice, first with the .Fa id passed to .Fn fido_assert_set_rp , and a second time with the .Fa id passed to .Fn fido_assert_set_winhello_appid . If the second assertion succeeds, .Xr fido_assert_rp_id 3 will point to the U2F AppID once .Xr fido_dev_get_assert 3 completes. This mechanism exists in Windows Hello to ensure U2F backwards compatibility without the application inadvertently prompting the user twice. Note that .Fn fido_assert_set_winhello_appid is not needed on platforms offering CTAP primitives, since the authenticator can be silently probed for the existence of U2F credentials. .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 , .Xr fido_dev_is_winhello 3 libfido2-1.15.0/man/fido_assert_verify.3000066400000000000000000000056561463251454000200350ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: July 15 2022 $ .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_ES384 , .Dv COSE_RS256 , or .Dv COSE_EDDSA , and .Fa pk points to a .Vt es256_pk_t , .Vt es384_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.15.0/man/fido_bio_dev_get_info.3000066400000000000000000000106441463251454000204220ustar00rootroot00000000000000.\" Copyright (c) 2019 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_bio_enroll_new.3000066400000000000000000000073221463251454000201350ustar00rootroot00000000000000.\" Copyright (c) 2019 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_bio_info_new.3000066400000000000000000000060251463251454000175740ustar00rootroot00000000000000.\" Copyright (c) 2019 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_bio_template.3000066400000000000000000000125711463251454000176060ustar00rootroot00000000000000.\" Copyright (c) 2019 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_cbor_info_new.3000066400000000000000000000265511463251454000177560ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: April 22 2022 $ .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_certs_name_ptr , .Nm fido_cbor_info_certs_value_ptr , .Nm fido_cbor_info_certs_len , .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_maxlargeblob , .Nm fido_cbor_info_maxrpid_minpinlen , .Nm fido_cbor_info_minpinlen , .Nm fido_cbor_info_fwversion , .Nm fido_cbor_info_uv_attempts , .Nm fido_cbor_info_uv_modality , .Nm fido_cbor_info_rk_remaining , .Nm fido_cbor_info_new_pin_required .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 char ** .Fn fido_cbor_info_certs_name_ptr "const fido_cbor_info_t *ci" .Ft const uint64_t * .Fn fido_cbor_info_certs_value_ptr "const fido_cbor_info_t *ci" .Ft size_t .Fn fido_cbor_info_certs_len "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_maxlargeblob "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_maxrpid_minpinlen "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_minpinlen "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_fwversion "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_uv_attempts "const fido_cbor_info_t *ci" .Ft uint64_t .Fn fido_cbor_info_uv_modality "const fido_cbor_info_t *ci" .Ft int64_t .Fn fido_cbor_info_rk_remaining "const fido_cbor_info_t *ci" .Ft bool .Fn fido_cbor_info_new_pin_required "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_certs_name_ptr and .Fn fido_cbor_info_certs_value_ptr functions return pointers to the array of certification names and their respective values in .Fa ci . The length of the certifications array is returned by .Fn fido_cbor_info_certs_len . .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_maxrpid_minpinlen function returns the maximum number of RP IDs that may be passed to .Xr fido_dev_set_pin_minlen_rpid 3 , as reported in .Fa ci . The minimum PIN length attribute is a CTAP 2.1 addition. If the attribute is not advertised by the authenticator, the .Fn fido_cbor_info_maxrpid_minpinlen function returns zero. .Pp The .Fn fido_cbor_info_maxlargeblob function returns the maximum length in bytes of an authenticator's serialized largeBlob array as reported in .Fa ci . .Pp The .Fn fido_cbor_info_minpinlen function returns the minimum PIN length enforced by the authenticator as reported in .Fa ci . The minimum PIN length attribute is a CTAP 2.1 addition. If the attribute is not advertised by the authenticator, the .Fn fido_cbor_info_minpinlen function returns zero. .Pp The .Fn fido_cbor_info_fwversion function returns the firmware version attribute of .Fa ci . .Pp The .Fn fido_cbor_info_uv_attempts function returns the number of UV attempts that the platform may attempt before falling back to PIN authentication. If 1, then all .Xr fido_dev_get_uv_retry_count 3 retries are handled internally by the authenticator and the platform may only attempt non-PIN UV once. The UV attempts attribute is a CTAP 2.1 addition. If the attribute is not advertised by the authenticator, the .Fn fido_cbor_info_uv_attempts function returns zero. .Pp The .Fn fido_cbor_info_uv_modality function returns a bitmask representing different UV modes supported by the authenticator, as defined in the FIDO Registry of Predefined Values and reported in .Fa ci . See the .Em FIDO_UV_MODE_* definitions in .In fido/param.h for the set of values defined by libfido2 and a brief description of each. The UV modality attribute is a CTAP 2.1 addition. If the attribute is not advertised by the authenticator, the .Fn fido_cbor_info_uv_modality function returns zero. .Pp The .Fn fido_cbor_info_rk_remaining function returns the estimated number of additional resident/discoverable credentials that can be stored on the authenticator as reported in .Fa ci . The estimated number of remaining resident credentials is a CTAP 2.1 addition. If the attribute is not advertised by the authenticator, the .Fn fido_cbor_info_rk_remaining function returns -1. .Pp The .Fn fido_cbor_info_new_pin_required function returns whether a new PIN is required by the authenticator as reported in .Fa ci . If .Fn fido_cbor_info_new_pin_required returns true, operations requiring PIN authentication will fail until a new PIN is set on the authenticator. The .Xr fido_dev_set_pin 3 function can be used to set a new PIN. .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_get_uv_retry_count 3 , .Xr fido_dev_open 3 , .Xr fido_dev_set_pin 3 , .Xr fido_dev_set_pin_minlen_rpid 3 .Rs .%D 2021-05-25 .%O Review Draft, Version 2.2 .%Q FIDO Alliance .%R FIDO Registry of Predefined Values .%U https://fidoalliance.org/specs/common-specs/fido-registry-v2.2-rd-20210525.html .Re libfido2-1.15.0/man/fido_cred_exclude.3000066400000000000000000000053751463251454000175740ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: December 2 2022 $ .Dt FIDO_CRED_EXCLUDE 3 .Os .Sh NAME .Nm fido_cred_exclude , .Nm fido_cred_empty_exclude_list .Nd manage exclude lists in a FIDO2 credential .Sh SYNOPSIS .In fido.h .Ft int .Fn fido_cred_exclude "fido_cred_t *cred" "const unsigned char *ptr" "size_t len" .Ft int .Fn fido_cred_empty_exclude_list "fido_cred_t *cred" .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. .Pp The .Fn fido_cred_empty_exclude_list function empties the list of credentials excluded by .Fa cred . .Sh RETURN VALUES The error codes returned by .Fn fido_cred_exclude and .Fn fido_cred_empty_exclude_list 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.15.0/man/fido_cred_new.3000066400000000000000000000232231463251454000167240ustar00rootroot00000000000000.\" Copyright (c) 2018-2024 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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_list_count , .Nm fido_cred_x5c_list_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_list_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 size_t .Fn fido_cred_x5c_list_count "const fido_cred_t *cred" .Ft const unsigned char * .Fn fido_cred_x5c_list_ptr "const fido_cred_t *cred" "size_t idx" .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_list_len "const fido_cred_t *cred" "size_t idx" .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 attestation statement format identifier 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 leaf 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 .Fn fido_cred_x5c_list_count function returns the length of the x509 certificate chain in .Fa cred and the .Fn fido_cred_x5c_list_ptr and .Fn fido_cred_x5c_list_len functions return a pointer to and length of the x509 certificate at index .Fa idx respectively. Please note that the leaf certificate has an .Fa idx (index) value of 0 and calling .Fn fido_cred_x5c_list_ptr cred 0 and .Fn fido_cred_x5c_list_len cred 0 is equivalent to .Fn fido_cred_x5c_ptr cred and .Fn fido_cred_x5c_len cred respectively. If .Fa idx exceeds the return value of .Fn fido_cred_x5c_list_count , .Fn fido_cred_x5c_list_ptr returns NULL and .Fn fido_cred_x5c_list_len returns 0. .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.15.0/man/fido_cred_set_authdata.3000066400000000000000000000242161463251454000206040ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: July 15 2022 $ .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_attobj , .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_attobj "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_attobj , .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 object, 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 attestation object passed to .Fn fido_cred_set_attobj must be a CBOR-encoded map containing .Dq authData , .Dq fmt , and .Dq attStmt . An application calling .Fn fido_cred_set_attobj does not need to call .Fn fido_cred_set_fmt , .Fn fido_cred_set_attstmt , .Fn fido_cred_set_authdata , or .Fn fido_cred_set_authdata_raw . .Fn fido_cred_set_attobj may be useful in applications interfacing with the WebAuthn API, removing the need to first parse the attestation object to verify the credential. .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 statement format identifier 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 in U2F , .Vt "tpm" .Pq the format used by TPM-based authenticators , 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 only be able to generate .Vt fido-u2f attestation statements. .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_ES384 , .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, COSE_ES384, 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.15.0/man/fido_cred_verify.3000066400000000000000000000071041463251454000174370ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_credman_metadata_new.3000066400000000000000000000211321463251454000212550ustar00rootroot00000000000000.\" Copyright (c) 2019-2021 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_dev_enable_entattest.3000066400000000000000000000106161463251454000213170ustar00rootroot00000000000000.\" Copyright (c) 2020-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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: March 30 2022 $ .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 function 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. The maximum value of .Fa n supported by the authenticator can be obtained using .Xr fido_cbor_info_maxrpid_minpinlen 3 . .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_cbor_info_maxrpid_minpinlen 3 , .Xr fido_cred_pin_minlen 3 , .Xr fido_dev_get_cbor_info 3 , .Xr fido_dev_reset 3 libfido2-1.15.0/man/fido_dev_get_assert.3000066400000000000000000000057161463251454000201430ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_dev_get_touch_begin.3000066400000000000000000000057471463251454000211340ustar00rootroot00000000000000.\" Copyright (c) 2020 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_dev_info_manifest.3000066400000000000000000000133451463251454000206210ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: March 30 2022 $ .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 function 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.15.0/man/fido_dev_largeblob_get.3000066400000000000000000000141211463251454000205610ustar00rootroot00000000000000.\" Copyright (c) 2020 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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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_get_dev_rk 3 , .Xr fido_credman_get_dev_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.15.0/man/fido_dev_make_cred.3000066400000000000000000000055621463251454000177140ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_dev_open.3000066400000000000000000000155501463251454000167410ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_dev_set_io_functions.3000066400000000000000000000166031463251454000213520ustar00rootroot00000000000000.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_dev_set_pin.3000066400000000000000000000070571463251454000174440ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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 SEE ALSO .Xr fido_cbor_info_uv_attempts 3 .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.15.0/man/fido_init.3000066400000000000000000000052441463251454000161040ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/fido_strerr.3000066400000000000000000000035041463251454000164570ustar00rootroot00000000000000.\" Copyright (c) 2018 Yubico AB. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .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.15.0/man/rs256_pk_new.3000066400000000000000000000074561463251454000163730ustar00rootroot00000000000000.\" Copyright (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. .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd $Mdocdate: July 15 2022 $ .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 es384_pk_new 3 , .Xr fido_assert_verify 3 , .Xr fido_cred_pubkey_ptr 3 libfido2-1.15.0/man/style.css000066400000000000000000000012521463251454000157210ustar00rootroot00000000000000* { 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.15.0/openbsd-compat/000077500000000000000000000000001463251454000162075ustar00rootroot00000000000000libfido2-1.15.0/openbsd-compat/bsd-asprintf.c000066400000000000000000000042241463251454000207510ustar00rootroot00000000000000/* * Copyright (c) 2004 Darren Tucker. * * Based originally on asprintf.c from OpenBSD: * Copyright (c) 1997 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. */ #include "openbsd-compat.h" #ifndef HAVE_ASPRINTF #include #include /* for INT_MAX */ #include #include /* for vsnprintf */ #include #define VA_COPY(dest, src) va_copy(dest, src) #define INIT_SZ 128 int vasprintf(char **str, const char *fmt, va_list ap) { int ret; va_list ap2; char *string, *newstr; size_t len; if ((string = malloc(INIT_SZ)) == NULL) goto fail; VA_COPY(ap2, ap); ret = vsnprintf(string, INIT_SZ, fmt, ap2); va_end(ap2); if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */ *str = string; } else if (ret == INT_MAX || ret < 0) { /* Bad length */ free(string); goto fail; } else { /* bigger than initial, realloc allowing for nul */ len = (size_t)ret + 1; if ((newstr = realloc(string, len)) == NULL) { free(string); goto fail; } VA_COPY(ap2, ap); ret = vsnprintf(newstr, len, fmt, ap2); va_end(ap2); if (ret < 0 || (size_t)ret >= len) { /* failed with realloc'ed string */ free(newstr); goto fail; } *str = newstr; } return (ret); fail: *str = NULL; errno = ENOMEM; return (-1); } int asprintf(char **str, const char *fmt, ...) { va_list ap; int ret; *str = NULL; va_start(ap, fmt); ret = vasprintf(str, fmt, ap); va_end(ap); return ret; } #endif libfido2-1.15.0/openbsd-compat/bsd-getline.c000066400000000000000000000057011463251454000205530ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/bsd-getpagesize.c000066400000000000000000000010011463251454000214200ustar00rootroot00000000000000/* 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.15.0/openbsd-compat/clock_gettime.c000066400000000000000000000012551463251454000211670ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/openbsd-compat/endian_win32.c000066400000000000000000000025061463251454000206360ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/openbsd-compat/err.h000066400000000000000000000024451463251454000171550ustar00rootroot00000000000000/* * 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.15.0/openbsd-compat/explicit_bzero.c000066400000000000000000000022311463251454000213730ustar00rootroot00000000000000/* 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.15.0/openbsd-compat/explicit_bzero_win32.c000066400000000000000000000005161463251454000224210ustar00rootroot00000000000000/* * 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.15.0/openbsd-compat/freezero.c000066400000000000000000000017421463251454000202000ustar00rootroot00000000000000/* * 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.15.0/openbsd-compat/getopt.h000066400000000000000000000053221463251454000176640ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/getopt_long.c000066400000000000000000000343021463251454000206760ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/openbsd-compat.h000066400000000000000000000050721463251454000212770ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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 #if !defined(HAVE_ASPRINTF) int asprintf(char **, const char *, ...); #endif #endif /* !_OPENBSD_COMPAT_H */ libfido2-1.15.0/openbsd-compat/posix_ioctl_check.c000066400000000000000000000001241463251454000220410ustar00rootroot00000000000000#include int posix_ioctl_check(int fd) { return ioctl(fd, -1, 0); } libfido2-1.15.0/openbsd-compat/posix_win.c000066400000000000000000000015311463251454000203720ustar00rootroot00000000000000/* * 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.15.0/openbsd-compat/posix_win.h000066400000000000000000000016141463251454000204010ustar00rootroot00000000000000/* * 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.15.0/openbsd-compat/readpassphrase.c000066400000000000000000000135071463251454000213660ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/readpassphrase.h000066400000000000000000000034011463251454000213630ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/readpassphrase_win32.c000066400000000000000000000067751463251454000224210ustar00rootroot00000000000000/* * 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.15.0/openbsd-compat/recallocarray.c000066400000000000000000000046631463251454000212070ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/strlcat.c000066400000000000000000000034441463251454000200340ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/strlcpy.c000066400000000000000000000032521463251454000200550ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/strsep.c000066400000000000000000000051431463251454000176760ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/time.h000066400000000000000000000025421463251454000173210ustar00rootroot00000000000000/* * 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.15.0/openbsd-compat/timingsafe_bcmp.c000066400000000000000000000023111463251454000214770ustar00rootroot00000000000000/* $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.15.0/openbsd-compat/types.h000066400000000000000000000023301463251454000175220ustar00rootroot00000000000000/* * 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.15.0/regress/000077500000000000000000000000001463251454000147465ustar00rootroot00000000000000libfido2-1.15.0/regress/CMakeLists.txt000066400000000000000000000036571463251454000175210ustar00rootroot00000000000000# Copyright (c) 2018-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. # SPDX-License-Identifier: BSD-2-Clause add_custom_target(regress) macro(add_regress_test NAME SOURCES LIB) add_executable(${NAME} ${SOURCES}) add_test(${NAME} ${NAME}) add_dependencies(regress ${NAME}) target_link_libraries(${NAME} ${LIB}) endmacro() if(MSVC AND BUILD_SHARED_LIBS) add_custom_command(TARGET regress POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy "${CBOR_BIN_DIRS}/${CBOR_LIBRARIES}.dll" "${CRYPTO_BIN_DIRS}/${CRYPTO_LIBRARIES}.dll" "${ZLIB_BIN_DIRS}/${ZLIB_LIBRARIES}.dll" "$" "${CMAKE_CURRENT_BINARY_DIR}") endif() if(CYGWIN AND BUILD_SHARED_LIBS) add_custom_command(TARGET regress POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy "$" "${CMAKE_CURRENT_BINARY_DIR}") endif() if(CMAKE_CROSSCOMPILING OR (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64" AND CMAKE_GENERATOR_PLATFORM MATCHES "^ARM.*$")) add_custom_command(TARGET regress POST_BUILD COMMAND "${CMAKE_COMMAND}" -E echo "Cross-compilation detected. Skipping regress tests.") else() add_custom_command(TARGET regress POST_BUILD COMMAND "${CMAKE_CTEST_COMMAND}" --output-on-failure WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) endif() add_regress_test(regress_assert assert.c ${_FIDO2_LIBRARY}) add_regress_test(regress_cred cred.c ${_FIDO2_LIBRARY}) add_regress_test(regress_dev dev.c ${_FIDO2_LIBRARY}) add_regress_test(regress_eddsa eddsa.c ${_FIDO2_LIBRARY}) add_regress_test(regress_es256 es256.c ${_FIDO2_LIBRARY}) add_regress_test(regress_es384 es384.c ${_FIDO2_LIBRARY}) add_regress_test(regress_rs256 rs256.c ${_FIDO2_LIBRARY}) if(BUILD_STATIC_LIBS) add_regress_test(regress_compress compress.c fido2) endif() if(MINGW) # needed for nanosleep() in mingw target_link_libraries(regress_dev winpthread) endif() libfido2-1.15.0/regress/assert.c000066400000000000000000000450551463251454000164240ustar00rootroot00000000000000/* * Copyright (c) 2018-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #undef NDEBUG #include #include #define _FIDO_INTERNAL #include #include #include #include static int fake_dev_handle; 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_authdata_raw_len(a, idx) == 0); assert(fido_assert_authdata_raw_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); assert(fido_assert_authdata_ptr(a, 0) == NULL); assert(fido_assert_authdata_len(a, 0) == 0); assert(fido_assert_authdata_raw_ptr(a, 0) == NULL); assert(fido_assert_authdata_raw_len(a, 0) == 0); 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); } static void raw_authdata(void) { fido_assert_t *a; 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; 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((ptr = fido_assert_authdata_ptr(a, 0)) != NULL); assert((len = fido_assert_authdata_len(a, 0)) != 0); assert((item = cbor_load(ptr, len, &cbor_result)) != NULL); assert(cbor_result.read == len); assert(cbor_isa_bytestring(item)); assert((ptr = fido_assert_authdata_raw_ptr(a, 0)) != NULL); assert((len = fido_assert_authdata_raw_len(a, 0)) != 0); assert(cbor_bytestring_length(item) == len); assert(memcmp(ptr, cbor_bytestring_handle(item), len) == 0); assert((len = fido_assert_authdata_len(a, 0)) != 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_assert_set_authdata_raw(a, 0, ptr, len) == FIDO_OK); assert((ptr = fido_assert_authdata_ptr(a, 0)) != NULL); assert((len = fido_assert_authdata_len(a, 0)) != 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_assert(a); } int main(void) { fido_init(0); empty_assert_tests(); valid_assert(); no_cdh(); no_rp(); no_authdata(); no_sig(); junk_cdh(); junk_rp(); junk_authdata(); junk_sig(); wrong_options(); bad_cbor_serialize(); rs256_PKEY(); es256_PKEY(); raw_authdata(); exit(0); } libfido2-1.15.0/regress/compress.c000066400000000000000000000234311463251454000167500ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #undef NDEBUG #include #include #include #define _FIDO_INTERNAL #include /* * zlib compressed data (RFC1950); see https://www.ietf.org/rfc/rfc6713.txt */ static /* const */ unsigned char rfc1950_blob[694] = { 0x78, 0x9c, 0xb5, 0x52, 0x3b, 0x6f, 0xdb, 0x30, 0x10, 0xde, 0xf5, 0x2b, 0x0e, 0x99, 0x12, 0x40, 0x75, 0x13, 0x4f, 0x45, 0x3b, 0xd1, 0x12, 0x6d, 0x1d, 0x20, 0x8b, 0x2a, 0x49, 0xd9, 0xf5, 0x28, 0x4b, 0x4c, 0x42, 0xc0, 0x12, 0x03, 0x3d, 0x12, 0xe4, 0xdf, 0xf7, 0xc8, 0x3a, 0x88, 0xd3, 0x0c, 0x9d, 0xea, 0xc1, 0x3e, 0xf3, 0x8e, 0xdf, 0xeb, 0x98, 0xb8, 0xa7, 0xd7, 0xc1, 0x3e, 0x3c, 0x4e, 0x70, 0xdd, 0xdc, 0xc0, 0xf2, 0xf6, 0xee, 0xdb, 0x97, 0xe5, 0xed, 0x72, 0x09, 0x87, 0xf9, 0x68, 0x1b, 0x07, 0x6c, 0xb5, 0x00, 0x76, 0x3a, 0x41, 0x18, 0x19, 0x61, 0x30, 0xa3, 0x19, 0x9e, 0x4d, 0xbb, 0x88, 0x22, 0x69, 0x5a, 0x3b, 0x4e, 0x83, 0x3d, 0xce, 0x93, 0x75, 0x3d, 0xd4, 0x7d, 0x0b, 0xf3, 0x68, 0xc0, 0xf6, 0x30, 0xba, 0x79, 0x68, 0x4c, 0x38, 0x39, 0xda, 0xbe, 0x1e, 0x5e, 0xe1, 0xde, 0x0d, 0xdd, 0x18, 0xc3, 0x8b, 0x9d, 0x1e, 0xc1, 0x0d, 0xe1, 0xd7, 0xcd, 0x53, 0xd4, 0xb9, 0xd6, 0xde, 0xdb, 0xa6, 0xf6, 0x00, 0x31, 0xd4, 0x83, 0x81, 0x27, 0x33, 0x74, 0x76, 0x9a, 0x4c, 0x0b, 0x4f, 0x83, 0x7b, 0xb6, 0x2d, 0x15, 0xd3, 0x63, 0x3d, 0xd1, 0x97, 0x21, 0x90, 0xd3, 0xc9, 0xbd, 0xd8, 0xfe, 0x01, 0x1a, 0xd7, 0xb7, 0xd6, 0x5f, 0x1a, 0xfd, 0xa5, 0xa8, 0x33, 0xd3, 0xf7, 0x28, 0x02, 0x80, 0xbb, 0x05, 0x7c, 0x54, 0x35, 0x82, 0xbb, 0x7f, 0x93, 0xd3, 0xb8, 0xd6, 0x40, 0x37, 0x8f, 0x13, 0x99, 0x98, 0x6a, 0x92, 0xe9, 0x31, 0xeb, 0xa3, 0x7b, 0xf6, 0xad, 0x73, 0x06, 0x1e, 0x84, 0x3e, 0xbd, 0x9b, 0x6c, 0x63, 0x62, 0x9a, 0xb0, 0x23, 0x9c, 0x08, 0xcf, 0xc3, 0x5c, 0x92, 0xf6, 0xed, 0x5f, 0x8a, 0x88, 0xb4, 0x39, 0xd5, 0xb6, 0x33, 0xc3, 0xc2, 0x63, 0x2c, 0x3f, 0x0b, 0x21, 0xc2, 0x8b, 0x30, 0xde, 0x84, 0x90, 0xcb, 0x76, 0x26, 0x71, 0xff, 0x47, 0x0b, 0x91, 0x9e, 0x51, 0xfc, 0x44, 0xeb, 0x9a, 0xb9, 0x33, 0xfd, 0x54, 0xbf, 0xed, 0xeb, 0x2b, 0xad, 0xc2, 0x51, 0x67, 0x80, 0xae, 0x9e, 0xcc, 0x60, 0xeb, 0xd3, 0xf8, 0x1e, 0x7b, 0xd8, 0x15, 0x35, 0xcf, 0x00, 0x97, 0x66, 0x68, 0xf9, 0x3a, 0x43, 0x05, 0x4a, 0xac, 0xf5, 0x9e, 0x49, 0x0e, 0x54, 0x97, 0x52, 0xec, 0x30, 0xe5, 0x29, 0xac, 0x0e, 0xa0, 0x33, 0x0e, 0x89, 0x28, 0x0f, 0x12, 0x37, 0x99, 0x86, 0x4c, 0xe4, 0x29, 0x97, 0x0a, 0x58, 0x91, 0xd2, 0x69, 0xa1, 0x25, 0xae, 0x2a, 0x2d, 0xa4, 0x8a, 0xae, 0x98, 0xa2, 0x9b, 0x57, 0xa1, 0xc1, 0x8a, 0x03, 0xf0, 0x5f, 0xa5, 0xe4, 0x4a, 0x81, 0x90, 0x80, 0xdb, 0x32, 0x47, 0x02, 0x23, 0x74, 0xc9, 0x0a, 0x8d, 0x5c, 0xc5, 0x80, 0x45, 0x92, 0x57, 0x29, 0x16, 0x9b, 0x18, 0x08, 0x00, 0x0a, 0xa1, 0xa3, 0x1c, 0xb7, 0xa8, 0x69, 0x4c, 0x8b, 0x38, 0x90, 0x7e, 0xbe, 0x06, 0x62, 0x0d, 0x5b, 0x2e, 0x93, 0x8c, 0xfe, 0xb2, 0x15, 0xe6, 0xa8, 0x0f, 0x81, 0x6f, 0x8d, 0xba, 0xf0, 0x5c, 0x6b, 0x21, 0x23, 0x06, 0x25, 0x93, 0x1a, 0x93, 0x2a, 0x67, 0x12, 0xca, 0x4a, 0x96, 0x42, 0x71, 0xf0, 0xb6, 0x52, 0x54, 0x49, 0xce, 0x70, 0xcb, 0xd3, 0x05, 0xb1, 0x13, 0x23, 0xf0, 0x1d, 0x2f, 0x34, 0xa8, 0x8c, 0xe5, 0xf9, 0x47, 0x97, 0xd1, 0x1f, 0x97, 0x5e, 0xfb, 0xa5, 0x47, 0x58, 0x71, 0xc8, 0x91, 0xad, 0x72, 0xee, 0x99, 0x82, 0xcb, 0x14, 0x25, 0x4f, 0xb4, 0xb7, 0xf3, 0x5e, 0x25, 0x94, 0x1c, 0xe9, 0xcb, 0xe3, 0x48, 0x95, 0x3c, 0x41, 0x2a, 0x28, 0x0c, 0x4e, 0x66, 0x98, 0x3c, 0xc4, 0x67, 0x4c, 0xc5, 0x7f, 0x56, 0x34, 0x44, 0x4d, 0x48, 0xd9, 0x96, 0x6d, 0xc8, 0xdb, 0xf5, 0x3f, 0x22, 0xa1, 0x9d, 0x24, 0x95, 0xe4, 0x5b, 0xaf, 0x99, 0x72, 0x50, 0xd5, 0x4a, 0x69, 0xd4, 0x95, 0xe6, 0xb0, 0x11, 0x22, 0x0d, 0x41, 0x2b, 0x2e, 0x77, 0x98, 0x70, 0xf5, 0x03, 0x72, 0xa1, 0x42, 0x5a, 0x95, 0xe2, 0x71, 0x94, 0x32, 0xcd, 0x02, 0x31, 0x41, 0x50, 0x54, 0xd4, 0xa6, 0x7a, 0x55, 0x29, 0x0c, 0xa1, 0x61, 0xa1, 0xb9, 0x94, 0x55, 0xa9, 0x51, 0x14, 0x37, 0xb4, 0xdf, 0x3d, 0xc5, 0x42, 0x1a, 0x19, 0x5d, 0x4d, 0x43, 0xba, 0xa2, 0xf0, 0x56, 0xe9, 0x91, 0x70, 0x21, 0x0f, 0x1e, 0xd4, 0x67, 0x10, 0xc2, 0x8f, 0x61, 0x9f, 0x71, 0x3a, 0x97, 0x3e, 0xd0, 0x90, 0x14, 0xf3, 0x11, 0x28, 0x4a, 0x2c, 0xd1, 0x97, 0x63, 0xc4, 0x47, 0x01, 0xea, 0xe8, 0xdd, 0x23, 0x14, 0x7c, 0x93, 0xe3, 0x86, 0x17, 0x09, 0xf7, 0x5d, 0xe1, 0x51, 0xf6, 0xa8, 0xf8, 0x0d, 0xed, 0x0a, 0x95, 0x1f, 0xc0, 0x40, 0x4b, 0xdb, 0x27, 0xce, 0x2a, 0x58, 0xf6, 0x3b, 0x22, 0x55, 0x51, 0x28, 0x2f, 0x5e, 0x6c, 0x1c, 0x36, 0x09, 0xb8, 0x06, 0x96, 0xee, 0xd0, 0xcb, 0x3e, 0x0f, 0xd3, 0xee, 0x15, 0x9e, 0xdf, 0x49, 0x88, 0x2c, 0xc9, 0xce, 0x71, 0x2f, 0xa2, 0xdf, 0xdf, 0xd7, 0x8e, 0x9c, }; /* * expected sha256 of rfc1950_blob after decompression */ static const unsigned char rfc1950_blob_hash[SHA256_DIGEST_LENGTH] = { 0x61, 0xc0, 0x4e, 0x14, 0x01, 0xb6, 0xc5, 0x2d, 0xba, 0x15, 0xf6, 0x27, 0x4c, 0xa1, 0xcc, 0xfc, 0x39, 0xed, 0xd7, 0x12, 0xb6, 0x02, 0x3d, 0xb6, 0xd9, 0x85, 0xd0, 0x10, 0x9f, 0xe9, 0x3e, 0x75, }; static const size_t rfc1950_blob_origsiz = 1322; static /* const */ unsigned char random_words[515] = { 0x61, 0x74, 0x68, 0x69, 0x72, 0x73, 0x74, 0x20, 0x54, 0x68, 0x6f, 0x20, 0x63, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x6f, 0x66, 0x66, 0x79, 0x20, 0x4a, 0x61, 0x76, 0x61, 0x6e, 0x20, 0x62, 0x72, 0x65, 0x64, 0x65, 0x73, 0x20, 0x4c, 0x41, 0x4d, 0x20, 0x6d, 0x69, 0x73, 0x2d, 0x68, 0x75, 0x6d, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x73, 0x70, 0x69, 0x67, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x76, 0x6f, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x6c, 0x79, 0x20, 0x49, 0x6f, 0x64, 0x61, 0x6d, 0x6f, 0x65, 0x62, 0x61, 0x20, 0x68, 0x79, 0x70, 0x6f, 0x68, 0x79, 0x64, 0x72, 0x6f, 0x63, 0x68, 0x6c, 0x6f, 0x72, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x74, 0x74, 0x65, 0x20, 0x61, 0x63, 0x72, 0x69, 0x64, 0x69, 0x6e, 0x65, 0x20, 0x68, 0x6f, 0x77, 0x6c, 0x20, 0x45, 0x75, 0x72, 0x79, 0x67, 0x61, 0x65, 0x61, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x20, 0x74, 0x65, 0x74, 0x72, 0x61, 0x70, 0x6c, 0x6f, 0x69, 0x64, 0x20, 0x61, 0x75, 0x78, 0x65, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x69, 0x70, 0x65, 0x2d, 0x67, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x79, 0x63, 0x6f, 0x63, 0x65, 0x63, 0x69, 0x64, 0x69, 0x75, 0x6d, 0x20, 0x50, 0x65, 0x64, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x74, 0x72, 0x61, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x4c, 0x65, 0x6e, 0x67, 0x6c, 0x65, 0x6e, 0x20, 0x70, 0x72, 0x65, 0x73, 0x62, 0x79, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65, 0x20, 0x6c, 0x65, 0x63, 0x79, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x64, 0x72, 0x69, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x6b, 0x75, 0x72, 0x74, 0x69, 0x63, 0x20, 0x75, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x73, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x20, 0x70, 0x73, 0x79, 0x63, 0x68, 0x6f, 0x6b, 0x79, 0x6d, 0x65, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x20, 0x63, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x68, 0x20, 0x52, 0x65, 0x69, 0x63, 0x68, 0x73, 0x74, 0x61, 0x67, 0x20, 0x75, 0x6e, 0x63, 0x68, 0x6c, 0x6f, 0x72, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x20, 0x6c, 0x6f, 0x67, 0x6f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x65, 0x72, 0x20, 0x4c, 0x61, 0x69, 0x74, 0x68, 0x20, 0x74, 0x77, 0x6f, 0x2d, 0x66, 0x61, 0x63, 0x65, 0x20, 0x4d, 0x75, 0x70, 0x68, 0x72, 0x69, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x72, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x65, 0x74, 0x74, 0x69, 0x73, 0x74, 0x20, 0x49, 0x62, 0x69, 0x62, 0x69, 0x6f, 0x20, 0x72, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x67, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x2d, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x20, 0x73, 0x79, 0x6e, 0x61, 0x70, 0x74, 0x65, 0x6e, 0x65, 0x20, 0x68, 0x6f, 0x6c, 0x6f, 0x6d, 0x6f, 0x72, 0x70, 0x68, 0x20, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x4d, 0x49, 0x54, 0x53, 0x20, 0x4c, 0x75, 0x6b, 0x61, 0x73, 0x68, 0x20, 0x48, 0x6f, 0x72, 0x73, 0x65, 0x79, 0x20, 0x0a, }; static void rfc1950_inflate(void) { fido_blob_t in, out, dgst; memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); memset(&dgst, 0, sizeof(dgst)); in.ptr = rfc1950_blob; in.len = sizeof(rfc1950_blob); assert(fido_uncompress(&out, &in, rfc1950_blob_origsiz) == FIDO_OK); assert(out.len == rfc1950_blob_origsiz); assert(fido_sha256(&dgst, out.ptr, out.len) == 0); assert(dgst.len == sizeof(rfc1950_blob_hash)); assert(memcmp(rfc1950_blob_hash, dgst.ptr, dgst.len) == 0); free(out.ptr); free(dgst.ptr); } static void rfc1951_inflate(void) { fido_blob_t in, out, dgst; memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); memset(&dgst, 0, sizeof(dgst)); in.ptr = rfc1950_blob + 2; /* trim header */ in.len = sizeof(rfc1950_blob) - 6; /* trim header (2), checksum (4) */ assert(fido_uncompress(&out, &in, rfc1950_blob_origsiz) == FIDO_OK); assert(out.len == rfc1950_blob_origsiz); assert(fido_sha256(&dgst, out.ptr, out.len) == 0); assert(dgst.len == sizeof(rfc1950_blob_hash)); assert(memcmp(rfc1950_blob_hash, dgst.ptr, dgst.len) == 0); free(out.ptr); free(dgst.ptr); } static void rfc1951_reinflate(void) { fido_blob_t in, out; memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); in.ptr = random_words; in.len = sizeof(random_words); assert(fido_compress(&out, &in) == FIDO_OK); in.ptr = out.ptr; in.len = out.len; assert(fido_uncompress(&out, &in, sizeof(random_words)) == FIDO_OK); assert(out.len == sizeof(random_words)); assert(memcmp(out.ptr, random_words, out.len) == 0); free(in.ptr); free(out.ptr); } int main(void) { fido_init(0); rfc1950_inflate(); rfc1951_inflate(); rfc1951_reinflate(); exit(0); } libfido2-1.15.0/regress/cred.c000066400000000000000000003400731463251454000160360ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #undef NDEBUG #include #include #include #include #define _FIDO_INTERNAL #include static int fake_dev_handle; 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 }; const unsigned char x509_0_tpm_es256[1476] = { 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 }; const unsigned char x509_1_tpm_es256[1775] = { 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 }; /* * 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(bool xfail) { 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); // XXX: RHEL9 has deprecated SHA-1 for signing. assert(fido_cred_verify(c) == (xfail ? FIDO_ERR_INVALID_SIG : 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(bool xfail) { 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_x5c_list_count(c) == 2); assert(fido_cred_x5c_list_len(c, 0) == sizeof(x509_0_tpm_es256)); assert(memcmp(fido_cred_x5c_list_ptr(c, 0), x509_0_tpm_es256, sizeof(x509_0_tpm_es256)) == 0); assert(fido_cred_x5c_list_len(c, 1) == sizeof(x509_1_tpm_es256)); assert(memcmp(fido_cred_x5c_list_ptr(c, 1), x509_1_tpm_es256, sizeof(x509_1_tpm_es256)) == 0); assert(fido_cred_x5c_list_len(c, 2) == 0); assert(fido_cred_x5c_list_ptr(c, 2) == NULL); // XXX: RHEL9 has deprecated SHA-1 for signing. assert(fido_cred_verify(c) == (xfail ? FIDO_ERR_INVALID_SIG : 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); } static void push_kv(cbor_item_t *map, const char *key, cbor_item_t *value) { struct cbor_pair kv; cbor_item_t *tmp; memset(&kv, 0, sizeof(kv)); assert(map != NULL && key != NULL && value != NULL); assert((tmp = cbor_build_string(key)) != NULL); /* XXX transfers ownership */ kv.key = cbor_move(tmp); kv.value = cbor_move(value); assert(cbor_map_add(map, kv)); } static void attestation_object(void) { struct cbor_load_result cbor; unsigned char *attobj = NULL; size_t len, alloclen = 0; cbor_item_t *map; fido_cred_t *c; assert((map = cbor_new_definite_map(3)) != NULL); push_kv(map, "fmt", cbor_build_string("tpm")); push_kv(map, "attStmt", cbor_load(attstmt_tpm_es256, sizeof(attstmt_tpm_es256), &cbor)); push_kv(map, "authData", cbor_load(authdata_tpm_es256, sizeof(authdata_tpm_es256), &cbor)); assert((len = cbor_serialize_alloc(map, &attobj, &alloclen))); cbor_decref(&map); 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_rk(c, FIDO_OPT_FALSE) == FIDO_OK); assert(fido_cred_set_uv(c, FIDO_OPT_TRUE) == FIDO_OK); assert(fido_cred_set_attobj(c, attobj, len) == FIDO_OK); assert(strcmp(fido_cred_fmt(c), "tpm") == 0); assert(fido_cred_attstmt_len(c) == sizeof(attstmt_tpm_es256)); assert(memcmp(fido_cred_attstmt_ptr(c), attstmt_tpm_es256, sizeof(attstmt_tpm_es256)) == 0); assert(fido_cred_authdata_len(c) == sizeof(authdata_tpm_es256)); assert(memcmp(fido_cred_authdata_ptr(c), authdata_tpm_es256, sizeof(authdata_tpm_es256)) == 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); free(attobj); } int main(void) { bool xfail = getenv("FIDO_REGRESS_RS1_XFAIL") != NULL; 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(xfail); valid_tpm_es256_cred(xfail); attestation_object(); exit(0); } libfido2-1.15.0/regress/dev.c000066400000000000000000000236361463251454000157020ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #undef NDEBUG #include #include #include #define _FIDO_INTERNAL #include #include "../fuzz/wiredata_fido2.h" #define REPORT_LEN (64 + 1) static uint8_t ctap_nonce[8]; static uint8_t *wiredata_ptr; static size_t wiredata_len; static int fake_dev_handle; static int initialised; static long interval_ms; #if defined(_MSC_VER) static int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { if (rmtp != NULL) { errno = EINVAL; return (-1); } Sleep((DWORD)(rqtp->tv_sec * 1000) + (DWORD)(rqtp->tv_nsec / 1000000)); return (0); } #endif 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); #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:6386) #endif memcpy(wiredata_ptr, ctap_init_data, sizeof(ctap_init_data)); #if defined(_MSC_VER) #pragma warning(pop) #endif 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.15.0/regress/eddsa.c000066400000000000000000000073031463251454000161750ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #undef NDEBUG #include #include #define _FIDO_INTERNAL #include #include #include #include #define ASSERT_NOT_NULL(e) assert((e) != NULL) #define ASSERT_NULL(e) assert((e) == NULL) #define ASSERT_INVAL(e) assert((e) == FIDO_ERR_INVALID_ARGUMENT) #define ASSERT_OK(e) assert((e) == FIDO_OK) static const char ecdsa[] = \ "-----BEGIN PUBLIC KEY-----\n" "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOwiq14c80b7C1Jzsx5w1zMvk2GgW\n" "5kfGMOKXjwF/U+51ZfBDKehs3ivdeXAJBkxIh7E3iA32s+HyNqk+ntl9fg==\n" "-----END PUBLIC KEY-----\n"; static const char eddsa[] = \ "-----BEGIN PUBLIC KEY-----\n" "MCowBQYDK2VwAyEADt/RHErAxAHxH9FUmsjOhQ2ALl6Y8nE0m3zQxkEE2iM=\n" "-----END PUBLIC KEY-----\n"; static const unsigned char eddsa_raw[] = { 0x0e, 0xdf, 0xd1, 0x1c, 0x4a, 0xc0, 0xc4, 0x01, 0xf1, 0x1f, 0xd1, 0x54, 0x9a, 0xc8, 0xce, 0x85, 0x0d, 0x80, 0x2e, 0x5e, 0x98, 0xf2, 0x71, 0x34, 0x9b, 0x7c, 0xd0, 0xc6, 0x41, 0x04, 0xda, 0x23, }; static EVP_PKEY * EVP_PKEY_from_PEM(const char *ptr, size_t len) { BIO *bio = NULL; EVP_PKEY *pkey = NULL; if ((bio = BIO_new(BIO_s_mem())) == NULL) { warnx("BIO_new"); goto out; } if (len > INT_MAX || BIO_write(bio, ptr, (int)len) != (int)len) { warnx("BIO_write"); goto out; } if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) warnx("PEM_read_bio_PUBKEY"); out: BIO_free(bio); return pkey; } static int eddsa_pk_cmp(const char *ptr, size_t len) { EVP_PKEY *pkA = NULL; EVP_PKEY *pkB = NULL; eddsa_pk_t *k = NULL; int r, ok = -1; if ((pkA = EVP_PKEY_from_PEM(ptr, len)) == NULL) { warnx("EVP_PKEY_from_PEM"); goto out; } if ((k = eddsa_pk_new()) == NULL) { warnx("eddsa_pk_new"); goto out; } if ((r = eddsa_pk_from_EVP_PKEY(k, pkA)) != FIDO_OK) { warnx("eddsa_pk_from_EVP_PKEY: 0x%x", r); goto out; } if ((pkB = eddsa_pk_to_EVP_PKEY(k)) == NULL) { warnx("eddsa_pk_to_EVP_PKEY"); goto out; } if ((r = EVP_PKEY_cmp(pkA, pkB)) != 1) { warnx("EVP_PKEY_cmp: %d", r); goto out; } ok = 0; out: EVP_PKEY_free(pkA); EVP_PKEY_free(pkB); eddsa_pk_free(&k); return ok; } static void invalid_key(void) { EVP_PKEY *pkey; eddsa_pk_t *pk; ASSERT_NOT_NULL((pkey = EVP_PKEY_from_PEM(ecdsa, sizeof(ecdsa)))); ASSERT_NOT_NULL((pk = eddsa_pk_new())); ASSERT_INVAL(eddsa_pk_from_EVP_PKEY(pk, pkey)); EVP_PKEY_free(pkey); eddsa_pk_free(&pk); } static void valid_key(void) { EVP_PKEY *pkeyA = NULL; EVP_PKEY *pkeyB = NULL; eddsa_pk_t *pkA = NULL; eddsa_pk_t *pkB = NULL; #if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3070000f /* incomplete support; test what we can */ ASSERT_NULL(EVP_PKEY_from_PEM(eddsa, sizeof(eddsa))); ASSERT_NOT_NULL((pkB = eddsa_pk_new())); ASSERT_INVAL(eddsa_pk_from_ptr(pkB, eddsa_raw, sizeof(eddsa_raw))); ASSERT_NULL(eddsa_pk_to_EVP_PKEY((const eddsa_pk_t *)eddsa_raw)); assert(eddsa_pk_cmp(eddsa, sizeof(eddsa)) < 0); #else ASSERT_NOT_NULL((pkeyA = EVP_PKEY_from_PEM(eddsa, sizeof(eddsa)))); ASSERT_NOT_NULL((pkA = eddsa_pk_new())); ASSERT_NOT_NULL((pkB = eddsa_pk_new())); ASSERT_OK(eddsa_pk_from_EVP_PKEY(pkA, pkeyA)); ASSERT_OK(eddsa_pk_from_ptr(pkB, eddsa_raw, sizeof(eddsa_raw))); ASSERT_NOT_NULL((pkeyB = eddsa_pk_to_EVP_PKEY((const eddsa_pk_t *)eddsa_raw))); assert(EVP_PKEY_cmp(pkeyA, pkeyB) == 1); assert(eddsa_pk_cmp(eddsa, sizeof(eddsa)) == 0); #endif EVP_PKEY_free(pkeyA); EVP_PKEY_free(pkeyB); eddsa_pk_free(&pkA); eddsa_pk_free(&pkB); } int main(void) { fido_init(0); invalid_key(); valid_key(); exit(0); } libfido2-1.15.0/regress/es256.c000066400000000000000000000117671463251454000157720ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #undef NDEBUG #include #include #define _FIDO_INTERNAL #include #include #include #include #define ASSERT_NOT_NULL(e) assert((e) != NULL) #define ASSERT_NULL(e) assert((e) == NULL) #define ASSERT_INVAL(e) assert((e) == FIDO_ERR_INVALID_ARGUMENT) #define ASSERT_OK(e) assert((e) == FIDO_OK) static const char short_x[] = \ "-----BEGIN PUBLIC KEY-----\n" "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAAeeHTZj4LEbt7Czs+u5gEZJfnGE\n" "6Z+YLe4AYu7SoGY7IH/2jKifsA7w+lkURL4DL63oEjd3f8foH9bX4eaVug==\n" "-----END PUBLIC KEY-----"; static const char short_y[] = \ "-----BEGIN PUBLIC KEY-----\n" "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL8CWUP1r0tpJ5QmkzLc69O74C/Ti\n" "83hTiys/JFNVkp0ArW3pKt5jNRrgWSZYE4S/D3AMtpqifFXz/FLCzJqojQ==\n" "-----END PUBLIC KEY-----\n"; static const char p256k1[] = \ "-----BEGIN PUBLIC KEY-----\n" "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEU1y8c0Jg9FGr3vYChpEo9c4dpkijriYM\n" "QzU/DeskC89hZjLNH1Sj8ra2MsBlVGGJTNPCZSyx8Jo7ERapxdN7UQ==\n" "-----END PUBLIC KEY-----\n"; static const char p256v1[] = \ "-----BEGIN PUBLIC KEY-----\n" "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOwiq14c80b7C1Jzsx5w1zMvk2GgW\n" "5kfGMOKXjwF/U+51ZfBDKehs3ivdeXAJBkxIh7E3iA32s+HyNqk+ntl9fg==\n" "-----END PUBLIC KEY-----\n"; static const unsigned char p256k1_raw[] = { 0x04, 0x53, 0x5c, 0xbc, 0x73, 0x42, 0x60, 0xf4, 0x51, 0xab, 0xde, 0xf6, 0x02, 0x86, 0x91, 0x28, 0xf5, 0xce, 0x1d, 0xa6, 0x48, 0xa3, 0xae, 0x26, 0x0c, 0x43, 0x35, 0x3f, 0x0d, 0xeb, 0x24, 0x0b, 0xcf, 0x61, 0x66, 0x32, 0xcd, 0x1f, 0x54, 0xa3, 0xf2, 0xb6, 0xb6, 0x32, 0xc0, 0x65, 0x54, 0x61, 0x89, 0x4c, 0xd3, 0xc2, 0x65, 0x2c, 0xb1, 0xf0, 0x9a, 0x3b, 0x11, 0x16, 0xa9, 0xc5, 0xd3, 0x7b, 0x51, }; static const unsigned char p256v1_raw[] = { 0x04, 0x3b, 0x08, 0xaa, 0xd7, 0x87, 0x3c, 0xd1, 0xbe, 0xc2, 0xd4, 0x9c, 0xec, 0xc7, 0x9c, 0x35, 0xcc, 0xcb, 0xe4, 0xd8, 0x68, 0x16, 0xe6, 0x47, 0xc6, 0x30, 0xe2, 0x97, 0x8f, 0x01, 0x7f, 0x53, 0xee, 0x75, 0x65, 0xf0, 0x43, 0x29, 0xe8, 0x6c, 0xde, 0x2b, 0xdd, 0x79, 0x70, 0x09, 0x06, 0x4c, 0x48, 0x87, 0xb1, 0x37, 0x88, 0x0d, 0xf6, 0xb3, 0xe1, 0xf2, 0x36, 0xa9, 0x3e, 0x9e, 0xd9, 0x7d, 0x7e, }; static EVP_PKEY * EVP_PKEY_from_PEM(const char *ptr, size_t len) { BIO *bio = NULL; EVP_PKEY *pkey = NULL; if ((bio = BIO_new(BIO_s_mem())) == NULL) { warnx("BIO_new"); goto out; } if (len > INT_MAX || BIO_write(bio, ptr, (int)len) != (int)len) { warnx("BIO_write"); goto out; } if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) warnx("PEM_read_bio_PUBKEY"); out: BIO_free(bio); return pkey; } static int es256_pk_cmp(const char *ptr, size_t len) { EVP_PKEY *pkA = NULL; EVP_PKEY *pkB = NULL; es256_pk_t *k = NULL; int r, ok = -1; if ((pkA = EVP_PKEY_from_PEM(ptr, len)) == NULL) { warnx("EVP_PKEY_from_PEM"); goto out; } if ((k = es256_pk_new()) == NULL) { warnx("es256_pk_new"); goto out; } if ((r = es256_pk_from_EVP_PKEY(k, pkA)) != FIDO_OK) { warnx("es256_pk_from_EVP_PKEY: 0x%x", r); goto out; } if ((pkB = es256_pk_to_EVP_PKEY(k)) == NULL) { warnx("es256_pk_to_EVP_PKEY"); goto out; } if ((r = EVP_PKEY_cmp(pkA, pkB)) != 1) { warnx("EVP_PKEY_cmp: %d", r); goto out; } ok = 0; out: EVP_PKEY_free(pkA); EVP_PKEY_free(pkB); es256_pk_free(&k); return ok; } static void short_coord(void) { assert(es256_pk_cmp(short_x, sizeof(short_x)) == 0); assert(es256_pk_cmp(short_y, sizeof(short_y)) == 0); } static void invalid_curve(const unsigned char *raw, size_t raw_len) { EVP_PKEY *pkey; es256_pk_t *pk; ASSERT_NOT_NULL((pkey = EVP_PKEY_from_PEM(p256k1, sizeof(p256k1)))); ASSERT_NOT_NULL((pk = es256_pk_new())); ASSERT_INVAL(es256_pk_from_EVP_PKEY(pk, pkey)); ASSERT_INVAL(es256_pk_from_ptr(pk, raw, raw_len)); ASSERT_NULL(es256_pk_to_EVP_PKEY((const es256_pk_t *)raw)); EVP_PKEY_free(pkey); es256_pk_free(&pk); } static void full_coord(void) { assert(es256_pk_cmp(p256v1, sizeof(p256v1)) == 0); } static void valid_curve(const unsigned char *raw, size_t raw_len) { EVP_PKEY *pkeyA; EVP_PKEY *pkeyB; es256_pk_t *pkA; es256_pk_t *pkB; ASSERT_NOT_NULL((pkeyA = EVP_PKEY_from_PEM(p256v1, sizeof(p256v1)))); ASSERT_NOT_NULL((pkA = es256_pk_new())); ASSERT_NOT_NULL((pkB = es256_pk_new())); ASSERT_OK(es256_pk_from_EVP_PKEY(pkA, pkeyA)); ASSERT_OK(es256_pk_from_ptr(pkB, raw, raw_len)); ASSERT_NOT_NULL((pkeyB = es256_pk_to_EVP_PKEY(pkB))); assert(EVP_PKEY_cmp(pkeyA, pkeyB) == 1); EVP_PKEY_free(pkeyA); EVP_PKEY_free(pkeyB); es256_pk_free(&pkA); es256_pk_free(&pkB); } int main(void) { fido_init(0); short_coord(); full_coord(); invalid_curve(p256k1_raw, sizeof(p256k1_raw)); /* uncompressed */ invalid_curve(p256k1_raw + 1, sizeof(p256k1_raw) - 1); /* libfido2 */ valid_curve(p256v1_raw, sizeof(p256v1_raw)); /* uncompressed */ valid_curve(p256v1_raw + 1, sizeof(p256v1_raw) - 1); /* libfido2 */ exit(0); } libfido2-1.15.0/regress/es384.c000066400000000000000000000133161463251454000157640ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #undef NDEBUG #include #include #define _FIDO_INTERNAL #include #include #include #include #define ASSERT_NOT_NULL(e) assert((e) != NULL) #define ASSERT_NULL(e) assert((e) == NULL) #define ASSERT_INVAL(e) assert((e) == FIDO_ERR_INVALID_ARGUMENT) #define ASSERT_OK(e) assert((e) == FIDO_OK) static const char short_x[] = \ "-----BEGIN PUBLIC KEY-----\n" "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEAAZ/VVCUmFU6aH9kJdDnUHCCglkatFTX\n" "onMwIvNYyS8BW/HOoZiOQLs2Hg+qifwaP1pHKILzCVfFmWuZMhxhtmjNXFuOPDnS\n" "Wa1PMdkCoWXA2BbXxnqL9v36gIOcFBil\n" "-----END PUBLIC KEY-----"; static const char short_y[] = \ "-----BEGIN PUBLIC KEY-----\n" "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuDpRBAg87cnWVhxbWnaWlnj100w9pm5k\n" "6T4eYToISaIhEK70TnGwULHX0+qHCYEGACOM7B/ZJbqjo6I7MIXaKZLemGi+tqvy\n" "ajBAsTVSyrYBLQjTMMcaFmYmsxvFx7pK\n" "-----END PUBLIC KEY-----\n"; static const char brainpoolP384r1[] = \ "-----BEGIN PUBLIC KEY-----\n" "MHowFAYHKoZIzj0CAQYJKyQDAwIIAQELA2IABFKswbBzqqyZ4h1zz8rivqHzJxAO\n" "XC2aLyC9x5gwBM7GVu8k6jkX7VypRpg3yyCneiIQ+vVCNXgbDchJ0cPVuhwm3Zru\n" "AK49dezUPahWF0YiJRFVeV+KyB/MEaaZvinzqw==\n" "-----END PUBLIC KEY-----\n"; static const char secp384r1[] = \ "-----BEGIN PUBLIC KEY-----\n" "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdJN9DoqPtTNAOmjnECHBIqnJgyBW0rct\n" "tbUSqQjb6UG2lldmrQJbgCP/ywuXvkkJl4yfXxOr0UP3rgcnqTVA1/46s2TG+R5u\n" "NSQbCM1JPQuvTyFlAn5mdR8ZJJ8yPBQm\n" "-----END PUBLIC KEY-----\n"; static const unsigned char brainpoolP384r1_raw[] = { 0x04, 0x52, 0xac, 0xc1, 0xb0, 0x73, 0xaa, 0xac, 0x99, 0xe2, 0x1d, 0x73, 0xcf, 0xca, 0xe2, 0xbe, 0xa1, 0xf3, 0x27, 0x10, 0x0e, 0x5c, 0x2d, 0x9a, 0x2f, 0x20, 0xbd, 0xc7, 0x98, 0x30, 0x04, 0xce, 0xc6, 0x56, 0xef, 0x24, 0xea, 0x39, 0x17, 0xed, 0x5c, 0xa9, 0x46, 0x98, 0x37, 0xcb, 0x20, 0xa7, 0x7a, 0x22, 0x10, 0xfa, 0xf5, 0x42, 0x35, 0x78, 0x1b, 0x0d, 0xc8, 0x49, 0xd1, 0xc3, 0xd5, 0xba, 0x1c, 0x26, 0xdd, 0x9a, 0xee, 0x00, 0xae, 0x3d, 0x75, 0xec, 0xd4, 0x3d, 0xa8, 0x56, 0x17, 0x46, 0x22, 0x25, 0x11, 0x55, 0x79, 0x5f, 0x8a, 0xc8, 0x1f, 0xcc, 0x11, 0xa6, 0x99, 0xbe, 0x29, 0xf3, 0xab, }; static const unsigned char secp384r1_raw[] = { 0x04, 0x74, 0x93, 0x7d, 0x0e, 0x8a, 0x8f, 0xb5, 0x33, 0x40, 0x3a, 0x68, 0xe7, 0x10, 0x21, 0xc1, 0x22, 0xa9, 0xc9, 0x83, 0x20, 0x56, 0xd2, 0xb7, 0x2d, 0xb5, 0xb5, 0x12, 0xa9, 0x08, 0xdb, 0xe9, 0x41, 0xb6, 0x96, 0x57, 0x66, 0xad, 0x02, 0x5b, 0x80, 0x23, 0xff, 0xcb, 0x0b, 0x97, 0xbe, 0x49, 0x09, 0x97, 0x8c, 0x9f, 0x5f, 0x13, 0xab, 0xd1, 0x43, 0xf7, 0xae, 0x07, 0x27, 0xa9, 0x35, 0x40, 0xd7, 0xfe, 0x3a, 0xb3, 0x64, 0xc6, 0xf9, 0x1e, 0x6e, 0x35, 0x24, 0x1b, 0x08, 0xcd, 0x49, 0x3d, 0x0b, 0xaf, 0x4f, 0x21, 0x65, 0x02, 0x7e, 0x66, 0x75, 0x1f, 0x19, 0x24, 0x9f, 0x32, 0x3c, 0x14, 0x26, }; static EVP_PKEY * EVP_PKEY_from_PEM(const char *ptr, size_t len) { BIO *bio = NULL; EVP_PKEY *pkey = NULL; if ((bio = BIO_new(BIO_s_mem())) == NULL) { warnx("BIO_new"); goto out; } if (len > INT_MAX || BIO_write(bio, ptr, (int)len) != (int)len) { warnx("BIO_write"); goto out; } if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) warnx("PEM_read_bio_PUBKEY"); out: BIO_free(bio); return pkey; } static int es384_pk_cmp(const char *ptr, size_t len) { EVP_PKEY *pkA = NULL; EVP_PKEY *pkB = NULL; es384_pk_t *k = NULL; int r, ok = -1; if ((pkA = EVP_PKEY_from_PEM(ptr, len)) == NULL) { warnx("EVP_PKEY_from_PEM"); goto out; } if ((k = es384_pk_new()) == NULL) { warnx("es384_pk_new"); goto out; } if ((r = es384_pk_from_EVP_PKEY(k, pkA)) != FIDO_OK) { warnx("es384_pk_from_EVP_PKEY: 0x%x", r); goto out; } if ((pkB = es384_pk_to_EVP_PKEY(k)) == NULL) { warnx("es384_pk_to_EVP_PKEY"); goto out; } if ((r = EVP_PKEY_cmp(pkA, pkB)) != 1) { warnx("EVP_PKEY_cmp: %d", r); goto out; } ok = 0; out: EVP_PKEY_free(pkA); EVP_PKEY_free(pkB); es384_pk_free(&k); return ok; } static void short_coord(void) { assert(es384_pk_cmp(short_x, sizeof(short_x)) == 0); assert(es384_pk_cmp(short_y, sizeof(short_y)) == 0); } static void invalid_curve(const unsigned char *raw, size_t raw_len) { EVP_PKEY *pkey; es384_pk_t *pk; pkey = EVP_PKEY_from_PEM(brainpoolP384r1, sizeof(brainpoolP384r1)); if (pkey == NULL) return; /* assume no brainpool support in libcrypto */ ASSERT_NOT_NULL((pk = es384_pk_new())); ASSERT_INVAL(es384_pk_from_EVP_PKEY(pk, pkey)); ASSERT_INVAL(es384_pk_from_ptr(pk, raw, raw_len)); ASSERT_NULL(es384_pk_to_EVP_PKEY((const es384_pk_t *)raw)); EVP_PKEY_free(pkey); es384_pk_free(&pk); } static void full_coord(void) { assert(es384_pk_cmp(secp384r1, sizeof(secp384r1)) == 0); } static void valid_curve(const unsigned char *raw, size_t raw_len) { EVP_PKEY *pkeyA; EVP_PKEY *pkeyB; es384_pk_t *pkA; es384_pk_t *pkB; ASSERT_NOT_NULL((pkeyA = EVP_PKEY_from_PEM(secp384r1, sizeof(secp384r1)))); ASSERT_NOT_NULL((pkA = es384_pk_new())); ASSERT_NOT_NULL((pkB = es384_pk_new())); ASSERT_OK(es384_pk_from_EVP_PKEY(pkA, pkeyA)); ASSERT_OK(es384_pk_from_ptr(pkB, raw, raw_len)); ASSERT_NOT_NULL((pkeyB = es384_pk_to_EVP_PKEY(pkB))); assert(EVP_PKEY_cmp(pkeyA, pkeyB) == 1); EVP_PKEY_free(pkeyA); EVP_PKEY_free(pkeyB); es384_pk_free(&pkA); es384_pk_free(&pkB); } int main(void) { fido_init(0); short_coord(); full_coord(); invalid_curve(brainpoolP384r1_raw, sizeof(brainpoolP384r1_raw)); /* uncompressed */ invalid_curve(brainpoolP384r1_raw + 1, sizeof(brainpoolP384r1_raw) - 1); /* libfido2 */ valid_curve(secp384r1_raw, sizeof(secp384r1_raw)); /* uncompressed */ valid_curve(secp384r1_raw + 1, sizeof(secp384r1_raw) - 1); /* libfido2 */ exit(0); } libfido2-1.15.0/regress/rs256.c000066400000000000000000000135131463251454000157760ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #undef NDEBUG #include #include #define _FIDO_INTERNAL #include #include #include #include #define ASSERT_NOT_NULL(e) assert((e) != NULL) #define ASSERT_NULL(e) assert((e) == NULL) #define ASSERT_INVAL(e) assert((e) == FIDO_ERR_INVALID_ARGUMENT) #define ASSERT_OK(e) assert((e) == FIDO_OK) static char rsa1024[] = \ "-----BEGIN PUBLIC KEY-----\n" "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCw92gn9Ku/bEfFj1AutaZyltpf\n" "zzXrg70kQFymNq+spMt/HlxKiImw8TZU08zWW4ZLE/Ch4JYjMW6ETAdQFhSC63Ih\n" "Wecui0JJ1f+2CsUVg+h7lO1877LZYUpdNiJrbqMb5Yc4N3FPtvdl3NoLIIQsF76H\n" "VRvpjQgkWipRfZ97JQIDAQAB\n" "-----END PUBLIC KEY-----"; static char rsa2048[] = \ "-----BEGIN PUBLIC KEY-----\n" "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApvIq/55ZodBIxzo/8BnE\n" "UQN1fo1hmJ6V20hQHSzJq5tHyxRCcvKikuJ1ZvR4RdZlEzdTdbEfMBdZ8sxve0/U\n" "yYEjH92CG0vgTCYuUaFLJTaWZSvWa96G8Lw+V4VyNFDRCM7sflOaSVH5pAsz8OEc\n" "TLZfM4NhnDsJAM+mQ6X7Tza0sczPchgDA+9KByXo/VIqyuBQs17rlKC2reMa8NkY\n" "rBRQZJLNzi68d5/BHH1flGWE1l8wJ9dr1Ex93H/KdzX+7/28TWUC98nneUo8RfRx\n" "FwUt/EInDMHOORCaCHSs28U/9IUyMjqLB1rxKhIp09yGXMiTrrT+p+Pcn8dO01HT\n" "vQIDAQAB\n" "-----END PUBLIC KEY-----"; static char rsa3072[] = \ "-----BEGIN PUBLIC KEY-----\n" "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwZunKrMs/o92AniLPNTF\n" "Ta4EYfhy5NDmMvQvRFT/eTYItLrOTPmYMap68KLyZYmgz/AdaxAL/992QWre7XTY\n" "gqLwtZT+WsSu7xPHWKTTXrlVohKBeLHQ0I7Zy0NSMUxhlJEMrBAjSyFAS86zWm5w\n" "ctC3pNCqfUKugA07BVj+d5Mv5fziwgMR86kuhkVuMYfsR4IYwX4+va0pyLzxx624\n" "s9nJ107g+A+3MUk4bAto3lruFeeZPUI2AFzFQbGg5By6VtvVi3gKQ7lUNtAr0Onu\n" "I6Fb+yz8sbFcvDpJcu5CXW20GrKMVP4KY5pn2LCajWuZjBl/dXWayPfm4UX5Y2O4\n" "73tzPpUBNwnEdz79His0v80Vmvjwn5IuF2jAoimrBNPJFFwCCuVNy8kgj2vllk1l\n" "RvLOG6hf8VnlDb40QZS3QAQ09xFfF+xlVLb8cHH6wllaAGEM230TrmawpC7xpz4Z\n" "sTuwJwI0AWEi//noMsRz2BuF2fCp//aORYJQU2S8kYk3AgMBAAE=\n" "-----END PUBLIC KEY-----"; static const unsigned char rsa2048_raw[] = { 0xa6, 0xf2, 0x2a, 0xff, 0x9e, 0x59, 0xa1, 0xd0, 0x48, 0xc7, 0x3a, 0x3f, 0xf0, 0x19, 0xc4, 0x51, 0x03, 0x75, 0x7e, 0x8d, 0x61, 0x98, 0x9e, 0x95, 0xdb, 0x48, 0x50, 0x1d, 0x2c, 0xc9, 0xab, 0x9b, 0x47, 0xcb, 0x14, 0x42, 0x72, 0xf2, 0xa2, 0x92, 0xe2, 0x75, 0x66, 0xf4, 0x78, 0x45, 0xd6, 0x65, 0x13, 0x37, 0x53, 0x75, 0xb1, 0x1f, 0x30, 0x17, 0x59, 0xf2, 0xcc, 0x6f, 0x7b, 0x4f, 0xd4, 0xc9, 0x81, 0x23, 0x1f, 0xdd, 0x82, 0x1b, 0x4b, 0xe0, 0x4c, 0x26, 0x2e, 0x51, 0xa1, 0x4b, 0x25, 0x36, 0x96, 0x65, 0x2b, 0xd6, 0x6b, 0xde, 0x86, 0xf0, 0xbc, 0x3e, 0x57, 0x85, 0x72, 0x34, 0x50, 0xd1, 0x08, 0xce, 0xec, 0x7e, 0x53, 0x9a, 0x49, 0x51, 0xf9, 0xa4, 0x0b, 0x33, 0xf0, 0xe1, 0x1c, 0x4c, 0xb6, 0x5f, 0x33, 0x83, 0x61, 0x9c, 0x3b, 0x09, 0x00, 0xcf, 0xa6, 0x43, 0xa5, 0xfb, 0x4f, 0x36, 0xb4, 0xb1, 0xcc, 0xcf, 0x72, 0x18, 0x03, 0x03, 0xef, 0x4a, 0x07, 0x25, 0xe8, 0xfd, 0x52, 0x2a, 0xca, 0xe0, 0x50, 0xb3, 0x5e, 0xeb, 0x94, 0xa0, 0xb6, 0xad, 0xe3, 0x1a, 0xf0, 0xd9, 0x18, 0xac, 0x14, 0x50, 0x64, 0x92, 0xcd, 0xce, 0x2e, 0xbc, 0x77, 0x9f, 0xc1, 0x1c, 0x7d, 0x5f, 0x94, 0x65, 0x84, 0xd6, 0x5f, 0x30, 0x27, 0xd7, 0x6b, 0xd4, 0x4c, 0x7d, 0xdc, 0x7f, 0xca, 0x77, 0x35, 0xfe, 0xef, 0xfd, 0xbc, 0x4d, 0x65, 0x02, 0xf7, 0xc9, 0xe7, 0x79, 0x4a, 0x3c, 0x45, 0xf4, 0x71, 0x17, 0x05, 0x2d, 0xfc, 0x42, 0x27, 0x0c, 0xc1, 0xce, 0x39, 0x10, 0x9a, 0x08, 0x74, 0xac, 0xdb, 0xc5, 0x3f, 0xf4, 0x85, 0x32, 0x32, 0x3a, 0x8b, 0x07, 0x5a, 0xf1, 0x2a, 0x12, 0x29, 0xd3, 0xdc, 0x86, 0x5c, 0xc8, 0x93, 0xae, 0xb4, 0xfe, 0xa7, 0xe3, 0xdc, 0x9f, 0xc7, 0x4e, 0xd3, 0x51, 0xd3, 0xbd, 0x01, 0x00, 0x01, }; static EVP_PKEY * EVP_PKEY_from_PEM(const char *ptr, size_t len) { BIO *bio = NULL; EVP_PKEY *pkey = NULL; if ((bio = BIO_new(BIO_s_mem())) == NULL) { warnx("BIO_new"); goto out; } if (len > INT_MAX || BIO_write(bio, ptr, (int)len) != (int)len) { warnx("BIO_write"); goto out; } if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) warnx("PEM_read_bio_PUBKEY"); out: BIO_free(bio); return pkey; } static int rs256_pk_cmp(const char *ptr, size_t len) { EVP_PKEY *pkA = NULL; EVP_PKEY *pkB = NULL; rs256_pk_t *k = NULL; int r, ok = -1; if ((pkA = EVP_PKEY_from_PEM(ptr, len)) == NULL) { warnx("EVP_PKEY_from_PEM"); goto out; } if ((k = rs256_pk_new()) == NULL) { warnx("rs256_pk_new"); goto out; } if ((r = rs256_pk_from_EVP_PKEY(k, pkA)) != FIDO_OK) { warnx("rs256_pk_from_EVP_PKEY: 0x%x", r); goto out; } if ((pkB = rs256_pk_to_EVP_PKEY(k)) == NULL) { warnx("rs256_pk_to_EVP_PKEY"); goto out; } if ((r = EVP_PKEY_cmp(pkA, pkB)) != 1) { warnx("EVP_PKEY_cmp: %d", r); goto out; } ok = 0; out: EVP_PKEY_free(pkA); EVP_PKEY_free(pkB); rs256_pk_free(&k); return ok; } static void invalid_size(const char *pem) { EVP_PKEY *pkey; rs256_pk_t *pk; ASSERT_NOT_NULL((pkey = EVP_PKEY_from_PEM(pem, strlen(pem)))); ASSERT_NOT_NULL((pk = rs256_pk_new())); ASSERT_INVAL(rs256_pk_from_EVP_PKEY(pk, pkey)); EVP_PKEY_free(pkey); rs256_pk_free(&pk); } static void valid_size(const char *pem, const unsigned char *raw, size_t raw_len) { EVP_PKEY *pkeyA; EVP_PKEY *pkeyB; rs256_pk_t *pkA; rs256_pk_t *pkB; ASSERT_NOT_NULL((pkeyA = EVP_PKEY_from_PEM(pem, strlen(pem)))); ASSERT_NOT_NULL((pkA = rs256_pk_new())); ASSERT_NOT_NULL((pkB = rs256_pk_new())); ASSERT_OK(rs256_pk_from_EVP_PKEY(pkA, pkeyA)); ASSERT_OK(rs256_pk_from_ptr(pkB, raw, raw_len)); ASSERT_NOT_NULL((pkeyB = rs256_pk_to_EVP_PKEY(pkB))); assert(EVP_PKEY_cmp(pkeyA, pkeyB) == 1); assert(rs256_pk_cmp(pem, strlen(pem)) == 0); EVP_PKEY_free(pkeyA); EVP_PKEY_free(pkeyB); rs256_pk_free(&pkA); rs256_pk_free(&pkB); } int main(void) { fido_init(0); invalid_size(rsa1024); invalid_size(rsa3072); valid_size(rsa2048, rsa2048_raw, sizeof(rsa2048_raw)); exit(0); } libfido2-1.15.0/src/000077500000000000000000000000001463251454000140635ustar00rootroot00000000000000libfido2-1.15.0/src/CMakeLists.txt000066400000000000000000000074041463251454000166300ustar00rootroot00000000000000# Copyright (c) 2018-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. # SPDX-License-Identifier: BSD-2-Clause 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 es384.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 touch.c tpm.c types.c u2f.c util.c ) if(FUZZ) list(APPEND FIDO_SOURCES ../fuzz/clock.c) list(APPEND FIDO_SOURCES ../fuzz/pcsc.c) list(APPEND FIDO_SOURCES ../fuzz/prng.c) list(APPEND FIDO_SOURCES ../fuzz/udev.c) list(APPEND FIDO_SOURCES ../fuzz/uniform_random.c) list(APPEND FIDO_SOURCES ../fuzz/wrap.c) endif() if(NFC_LINUX) list(APPEND FIDO_SOURCES netlink.c nfc.c nfc_linux.c) endif() if(USE_PCSC) list(APPEND FIDO_SOURCES nfc.c pcsc.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 "${EXTRA_CFLAGS}") endif() list(APPEND COMPAT_SOURCES ../openbsd-compat/bsd-asprintf.c ../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) if(USE_PCSC) list(APPEND BASE_LIBRARIES winscard) endif() elseif(APPLE) list(APPEND BASE_LIBRARIES "-framework CoreFoundation" "-framework IOKit") if(USE_PCSC) list(APPEND BASE_LIBRARIES "-framework PCSC") endif() endif() list(APPEND TARGET_LIBRARIES ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES} ${UDEV_LIBRARIES} ${BASE_LIBRARIES} ${HIDAPI_LIBRARIES} ${ZLIB_LIBRARIES} ${PCSC_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) configure_file(libfido2.pc.in libfido2.pc @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libfido2.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") libfido2-1.15.0/src/aes256.c000066400000000000000000000127321463251454000152410ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/assert.c000066400000000000000000000644611463251454000155430ustar00rootroot00000000000000/* * Copyright (c) 2018-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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 */ if (fido_blob_decode(val, &stmt->authdata_raw) < 0) { fido_log_debug("%s: fido_blob_decode", __func__); return (-1); } 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 *msg; int msglen; int r; fido_assert_reset_rx(assert); if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } /* start with room for a single assertion */ if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } assert->stmt_len = 0; assert->stmt_cnt = 1; /* adjust as needed */ if ((r = cbor_parse_reply(msg, (size_t)msglen, assert, adjust_assert_count)) != FIDO_OK) { fido_log_debug("%s: adjust_assert_count", __func__); goto out; } /* parse the first assertion */ if ((r = cbor_parse_reply(msg, (size_t)msglen, &assert->stmt[0], parse_assert_reply)) != FIDO_OK) { fido_log_debug("%s: parse_assert_reply", __func__); goto out; } assert->stmt_len = 1; r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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 *msg; int msglen; int r; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } /* 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); r = FIDO_ERR_INTERNAL; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) { fido_log_debug("%s: parse_assert_reply", __func__); goto out; } r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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); } static int get_es256_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, const fido_blob_t *authdata) { const EVP_MD *md; EVP_MD_CTX *ctx = NULL; 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) { EVP_MD_CTX_free(ctx); return (-1); } dgst->len = SHA256_DIGEST_LENGTH; EVP_MD_CTX_free(ctx); return (0); } static int get_es384_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, const fido_blob_t *authdata) { const EVP_MD *md; EVP_MD_CTX *ctx = NULL; if (dgst->len < SHA384_DIGEST_LENGTH || (md = EVP_sha384()) == 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) { EVP_MD_CTX_free(ctx); return (-1); } dgst->len = SHA384_DIGEST_LENGTH; EVP_MD_CTX_free(ctx); return (0); } static int get_eddsa_hash(fido_blob_t *dgst, const fido_blob_t *clientdata, const fido_blob_t *authdata) { if (SIZE_MAX - authdata->len < clientdata->len || dgst->len < authdata->len + clientdata->len) return (-1); memcpy(dgst->ptr, authdata->ptr, authdata->len); memcpy(dgst->ptr + authdata->len, clientdata->ptr, clientdata->len); dgst->len = authdata->len + clientdata->len; 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; fido_blob_t authdata; struct cbor_load_result cbor; int ok = -1; fido_log_debug("%s: cose_alg=%d", __func__, cose_alg); 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); switch (cose_alg) { case COSE_ES256: case COSE_RS256: ok = get_es256_hash(dgst, clientdata, &authdata); break; case COSE_ES384: ok = get_es384_hash(dgst, clientdata, &authdata); break; case COSE_EDDSA: ok = get_eddsa_hash(dgst, clientdata, &authdata); break; default: fido_log_debug("%s: unknown cose_alg", __func__); break; } fail: if (item != NULL) cbor_decref(&item); 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_ES384: ok = es384_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); } #ifdef USE_WINHELLO int fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id) { if (assert->appid != NULL) { free(assert->appid); assert->appid = NULL; } if (id == NULL) return (FIDO_ERR_INVALID_ARGUMENT); if ((assert->appid = strdup(id)) == NULL) return (FIDO_ERR_INTERNAL); return (FIDO_OK); } #else int fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id) { (void)assert; (void)id; return (FIDO_ERR_UNSUPPORTED_EXTENSION); } #endif /* USE_WINHELLO */ 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_empty_allow_list(fido_assert_t *assert) { fido_free_blob_array(&assert->allow_list); memset(&assert->allow_list, 0, sizeof(assert->allow_list)); return (FIDO_OK); } 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); free(assert->appid); fido_blob_reset(&assert->cd); fido_blob_reset(&assert->cdh); fido_blob_reset(&assert->ext.hmac_salt); fido_assert_empty_allow_list(assert); memset(&assert->ext, 0, sizeof(assert->ext)); assert->rp_id = NULL; assert->appid = 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].authdata_raw); 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_authdata_raw_ptr(const fido_assert_t *assert, size_t idx) { if (idx >= assert->stmt_len) return (NULL); return (assert->stmt[idx].authdata_raw.ptr); } size_t fido_assert_authdata_raw_len(const fido_assert_t *assert, size_t idx) { if (idx >= assert->stmt_len) return (0); return (assert->stmt[idx].authdata_raw.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_blob_reset(&stmt->authdata_raw); 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 (fido_blob_decode(item, &stmt->authdata_raw) < 0) { fido_log_debug("%s: fido_blob_decode", __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_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 (fido_blob_set(&stmt->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_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.15.0/src/authkey.c000066400000000000000000000044461463251454000157110ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 *msg; int msglen; int r; fido_log_debug("%s: dev=%p, authkey=%p, ms=%d", __func__, (void *)dev, (void *)authkey, *ms); memset(authkey, 0, sizeof(*authkey)); if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } r = cbor_parse_reply(msg, (size_t)msglen, authkey, parse_authkey); out: freezero(msg, FIDO_MAXMSG); return (r); } 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.15.0/src/bio.c000066400000000000000000000444231463251454000150070ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 uint8_t bio_get_cmd(const fido_dev_t *dev) { if (dev->flags & (FIDO_DEV_BIO_SET|FIDO_DEV_BIO_UNSET)) return (CTAP_CBOR_BIO_ENROLL); return (CTAP_CBOR_BIO_ENROLL_PRE); } 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 = bio_get_cmd(dev); 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 *msg; int msglen; int r; bio_reset_template_array(ta); if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, ta, bio_parse_template_array)) != FIDO_OK) { fido_log_debug("%s: bio_parse_template_array" , __func__); goto out; } r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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 *msg; int msglen; int r; bio_reset_template(t); e->remaining_samples = 0; e->last_status = 0; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, e, bio_parse_enroll_status)) != FIDO_OK) { fido_log_debug("%s: bio_parse_enroll_status", __func__); goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, &t->id, bio_parse_template_id)) != FIDO_OK) { fido_log_debug("%s: bio_parse_template_id", __func__); goto out; } r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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 *msg; int msglen; int r; e->remaining_samples = 0; e->last_status = 0; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, e, bio_parse_enroll_status)) != FIDO_OK) { fido_log_debug("%s: bio_parse_enroll_status", __func__); goto out; } r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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 *msg; int msglen; int r; bio_reset_info(i); if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, i, bio_parse_info)) != FIDO_OK) { fido_log_debug("%s: bio_parse_info" , __func__); goto out; } r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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.15.0/src/blob.c000066400000000000000000000044141463251454000151500ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/blob.h000066400000000000000000000017761463251454000151650ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/buf.c000066400000000000000000000011521463251454000150020ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/cbor.c000066400000000000000000001152531463251454000151630ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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_ES384: if (cose_key.kty != COSE_KTY_EC2 || cose_key.crv != COSE_P384) { 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_ES384: if (es384_pk_decode(item, key) < 0) { fido_log_debug("%s: es384_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_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); } /* XXX introduce fido_attobj_t? */ int cbor_decode_attobj(const cbor_item_t *item, fido_cred_t *cred) { 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__); return (-1); } return (0); } 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_decode_bool(val, NULL) < 0) { fido_log_debug("%s: cbor_decode_bool", __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_decode_bool(val, NULL) < 0) { fido_log_debug("%s: cbor_decode_bool", __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_array_t *x5c = arg; fido_blob_t *list_ptr = NULL; fido_blob_t x5c_blob; memset(&x5c_blob, 0, sizeof(x5c_blob)); if (fido_blob_decode(item, &x5c_blob) < 0) { fido_log_debug("%s: fido_blob_decode", __func__); return (-1); } if (x5c->len == SIZE_MAX) { fido_blob_reset(&x5c_blob); return (-1); } if ((list_ptr = recallocarray(x5c->ptr, x5c->len, x5c->len + 1, sizeof(x5c_blob))) == NULL) { fido_blob_reset(&x5c_blob); return (-1); } list_ptr[x5c->len++] = x5c_blob; x5c->ptr = list_ptr; return (0); } static int decode_x5c_array(const cbor_item_t *item, fido_blob_array_t *arr) { if (arr->len) { fido_log_debug("%s: dup", __func__); return (-1); } if (cbor_isa_array(item) == false || cbor_array_is_definite(item) == false) { fido_log_debug("%s: cbor", __func__); return (-1); } return (cbor_array_iter(item, arr, decode_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_ES384 && 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 (decode_x5c_array(val, &attstmt->x5c)) { 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); } int cbor_decode_bool(const cbor_item_t *item, bool *v) { if (cbor_isa_float_ctrl(item) == false || cbor_float_get_width(item) != CBOR_FLOAT_0 || cbor_is_bool(item) == false) { fido_log_debug("%s: cbor type", __func__); return (-1); } if (v != NULL) *v = cbor_ctrl_value(item) == CBOR_CTRL_TRUE; 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.15.0/src/compress.c000066400000000000000000000075741463251454000160770ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include "fido.h" #define BOUND (1024UL * 1024UL) /* zlib inflate (raw + headers) */ static int rfc1950_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz) { u_long ilen, olen; int z; memset(out, 0, sizeof(*out)); if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND || origsiz > ULONG_MAX || (olen = (u_long)origsiz) > BOUND) { fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__, in->len, origsiz); return FIDO_ERR_INVALID_ARGUMENT; } if ((out->ptr = calloc(1, olen)) == NULL) return FIDO_ERR_INTERNAL; out->len = olen; if ((z = uncompress(out->ptr, &olen, in->ptr, ilen)) != Z_OK || olen > SIZE_MAX || olen != out->len) { fido_log_debug("%s: uncompress: %d, olen=%lu, out->len=%zu", __func__, z, olen, out->len); fido_blob_reset(out); return FIDO_ERR_COMPRESS; } return FIDO_OK; } /* raw inflate */ static int rfc1951_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz) { z_stream zs; u_int ilen, olen; int r, z; memset(&zs, 0, sizeof(zs)); memset(out, 0, sizeof(*out)); if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND || origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) { fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__, in->len, origsiz); return FIDO_ERR_INVALID_ARGUMENT; } if ((z = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) { fido_log_debug("%s: inflateInit2: %d", __func__, z); return FIDO_ERR_COMPRESS; } if ((out->ptr = calloc(1, olen)) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } out->len = olen; zs.next_in = in->ptr; zs.avail_in = ilen; zs.next_out = out->ptr; zs.avail_out = olen; if ((z = inflate(&zs, Z_FINISH)) != Z_STREAM_END) { fido_log_debug("%s: inflate: %d", __func__, z); r = FIDO_ERR_COMPRESS; goto fail; } if (zs.avail_out != 0) { fido_log_debug("%s: %u != 0", __func__, zs.avail_out); r = FIDO_ERR_COMPRESS; goto fail; } r = FIDO_OK; fail: if ((z = inflateEnd(&zs)) != Z_OK) { fido_log_debug("%s: inflateEnd: %d", __func__, z); r = FIDO_ERR_COMPRESS; } if (r != FIDO_OK) fido_blob_reset(out); return r; } /* raw deflate */ static int rfc1951_deflate(fido_blob_t *out, const fido_blob_t *in) { z_stream zs; u_int ilen, olen; int r, z; memset(&zs, 0, sizeof(zs)); memset(out, 0, sizeof(*out)); if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) { fido_log_debug("%s: in->len=%zu", __func__, in->len); return FIDO_ERR_INVALID_ARGUMENT; } if ((z = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) != Z_OK) { fido_log_debug("%s: deflateInit2: %d", __func__, z); return FIDO_ERR_COMPRESS; } olen = BOUND; if ((out->ptr = calloc(1, olen)) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } out->len = olen; zs.next_in = in->ptr; zs.avail_in = ilen; zs.next_out = out->ptr; zs.avail_out = olen; if ((z = deflate(&zs, Z_FINISH)) != Z_STREAM_END) { fido_log_debug("%s: inflate: %d", __func__, z); r = FIDO_ERR_COMPRESS; goto fail; } if (zs.avail_out >= out->len) { fido_log_debug("%s: %u > %zu", __func__, zs.avail_out, out->len); r = FIDO_ERR_COMPRESS; goto fail; } out->len -= zs.avail_out; r = FIDO_OK; fail: if ((z = deflateEnd(&zs)) != Z_OK) { fido_log_debug("%s: deflateEnd: %d", __func__, z); r = FIDO_ERR_COMPRESS; } if (r != FIDO_OK) fido_blob_reset(out); return r; } int fido_compress(fido_blob_t *out, const fido_blob_t *in) { return rfc1951_deflate(out, in); } int fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz) { if (rfc1950_inflate(out, in, origsiz) == FIDO_OK) return FIDO_OK; /* backwards compat with libfido2 < 1.11 */ return rfc1951_inflate(out, in, origsiz); } libfido2-1.15.0/src/config.c000066400000000000000000000126551463251454000155050ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 = 0; memset(prefix, 0xff, sizeof(prefix)); prefix[sizeof(prefix) - 2] = CTAP_CBOR_CONFIG; prefix[sizeof(prefix) - 1] = subcmd; if (item != NULL) { 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; } /* subCommandParams */ if (paramc != 0 && (argv[1] = cbor_flatten_vector(paramv, paramc)) == NULL) { fido_log_debug("%s: cbor_flatten_vector", __func__); goto fail; } /* pinProtocol, pinAuth */ if (pin != NULL || (fido_dev_supports_permissions(dev) && fido_dev_has_uv(dev))) { 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.15.0/src/cred.c000066400000000000000000000704401463251454000151510ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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; } dgst->len = SHA256_DIGEST_LENGTH; 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; if (!attstmt->x5c.len) { fido_log_debug("%s: x5c.len=%zu", __func__, attstmt->x5c.len); return (-1); } /* openssl needs ints */ if (attstmt->x5c.ptr[0].len > INT_MAX) { fido_log_debug("%s: x5c[0].len=%zu", __func__, attstmt->x5c.ptr[0].len); return (-1); } /* fetch key from x509 */ if ((rawcert = BIO_new_mem_buf(attstmt->x5c.ptr[0].ptr, (int)attstmt->x5c.ptr[0].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_ES384: ok = es384_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[1024]; /* XXX */ fido_blob_t dgst; int cose_alg; 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 ((cose_alg = cred->attstmt.alg) == COSE_UNSPEC) cose_alg = COSE_ES256; /* backwards compat */ if (!strcmp(cred->fmt, "packed")) { if (fido_get_signed_hash(cose_alg, &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_ES384: ok = es384_pk_verify_sig(&dgst, &cred->attcred.pubkey.es384, &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_free_blob_array(&attstmt->x5c); fido_blob_reset(&attstmt->sig); memset(attstmt, 0, sizeof(*attstmt)); } static void fido_cred_clean_attobj(fido_cred_t *cred) { free(cred->fmt); cred->fmt = NULL; fido_cred_clean_authdata(cred); fido_cred_clean_attstmt(&cred->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_cred_empty_exclude_list(cred); memset(&cred->rp, 0, sizeof(cred->rp)); memset(&cred->user, 0, sizeof(cred->user)); 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) { fido_cred_clean_attobj(cred); 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) { fido_blob_t x5c_blob; fido_blob_t *list_ptr = NULL; memset(&x5c_blob, 0, sizeof(x5c_blob)); fido_free_blob_array(&cred->attstmt.x5c); if (fido_blob_set(&x5c_blob, ptr, len) < 0) return (FIDO_ERR_INVALID_ARGUMENT); if (cred->attstmt.x5c.len == SIZE_MAX) { fido_blob_reset(&x5c_blob); return (FIDO_ERR_INVALID_ARGUMENT); } if ((list_ptr = recallocarray(cred->attstmt.x5c.ptr, cred->attstmt.x5c.len, cred->attstmt.x5c.len + 1, sizeof(x5c_blob))) == NULL) { fido_blob_reset(&x5c_blob); return (FIDO_ERR_INTERNAL); } list_ptr[cred->attstmt.x5c.len++] = x5c_blob; cred->attstmt.x5c.ptr = list_ptr; 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_set_attobj(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_attobj(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 (cbor_decode_attobj(item, cred) != 0) { fido_log_debug("%s: cbor_decode_attobj", __func__); goto fail; } r = FIDO_OK; fail: if (item != NULL) cbor_decref(&item); 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_empty_exclude_list(fido_cred_t *cred) { fido_free_blob_array(&cred->excl); memset(&cred->excl, 0, sizeof(cred->excl)); 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 (cred->type != 0) return (FIDO_ERR_INVALID_ARGUMENT); if (cose_alg != COSE_ES256 && cose_alg != COSE_ES384 && cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA) 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 (fido_cred_x5c_list_ptr(cred, 0)); } size_t fido_cred_x5c_len(const fido_cred_t *cred) { return (fido_cred_x5c_list_len(cred, 0)); } size_t fido_cred_x5c_list_count(const fido_cred_t *cred) { return (cred->attstmt.x5c.len); } const unsigned char * fido_cred_x5c_list_ptr(const fido_cred_t *cred, size_t i) { if (i >= cred->attstmt.x5c.len) return (NULL); return (cred->attstmt.x5c.ptr[i].ptr); } size_t fido_cred_x5c_list_len(const fido_cred_t *cred, size_t i) { if (i >= cred->attstmt.x5c.len) return (0); return (cred->attstmt.x5c.ptr[i].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_ES384: ptr = &cred->attcred.pubkey.es384; 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_ES384: len = sizeof(cred->attcred.pubkey.es384); 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.15.0/src/credman.c000066400000000000000000000426751463251454000156560ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #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, const 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 uint8_t credman_get_cmd(const fido_dev_t *dev) { if (dev->flags & FIDO_DEV_CREDMAN) return (CTAP_CBOR_CRED_MGMT); return (CTAP_CBOR_CRED_MGMT_PRE); } 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 = credman_get_cmd(dev); 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 *msg; int msglen; int r; memset(metadata, 0, sizeof(*metadata)); if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, metadata, credman_parse_metadata)) != FIDO_OK) { fido_log_debug("%s: credman_parse_metadata", __func__); goto out; } r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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 *msg; int msglen; int r; credman_reset_rk(rk); if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } /* adjust as needed */ if ((r = cbor_parse_reply(msg, (size_t)msglen, rk, credman_parse_rk_count)) != FIDO_OK) { fido_log_debug("%s: credman_parse_rk_count", __func__); goto out; } if (rk->n_alloc == 0) { fido_log_debug("%s: n_alloc=0", __func__); r = FIDO_OK; goto out; } /* parse the first rk */ if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[0], credman_parse_rk)) != FIDO_OK) { fido_log_debug("%s: credman_parse_rk", __func__); goto out; } rk->n_rx = 1; r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } static int credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms) { unsigned char *msg; int msglen; int r; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } /* 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); r = FIDO_ERR_INTERNAL; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[rk->n_rx], credman_parse_rk)) != FIDO_OK) { fido_log_debug("%s: credman_parse_rk", __func__); goto out; } r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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 *msg; int msglen; int r; credman_reset_rp(rp); if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } /* adjust as needed */ if ((r = cbor_parse_reply(msg, (size_t)msglen, rp, credman_parse_rp_count)) != FIDO_OK) { fido_log_debug("%s: credman_parse_rp_count", __func__); goto out; } if (rp->n_alloc == 0) { fido_log_debug("%s: n_alloc=0", __func__); r = FIDO_OK; goto out; } /* parse the first rp */ if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[0], credman_parse_rp)) != FIDO_OK) { fido_log_debug("%s: credman_parse_rp", __func__); goto out; } rp->n_rx = 1; r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } static int credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms) { unsigned char *msg; int msglen; int r; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } /* 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); r = FIDO_ERR_INTERNAL; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[rp->n_rx], credman_parse_rp)) != FIDO_OK) { fido_log_debug("%s: credman_parse_rp", __func__); goto out; } r = FIDO_OK; out: freezero(msg, FIDO_MAXMSG); return (r); } 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.15.0/src/dev.c000066400000000000000000000312261463251454000150110ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include "fido.h" #ifndef TLS #define TLS #endif static TLS bool disable_u2f_fallback; #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) { if (val[i]) dev->flags |= FIDO_DEV_CREDMAN; } else if (strcmp(ptr[i], "credentialMgmtPreview") == 0) { if (val[i]) dev->flags |= FIDO_DEV_CREDMAN_PRE; } 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; } else if (strcmp(ptr[i], "bioEnroll") == 0) { dev->flags |= val[i] ? FIDO_DEV_BIO_SET : FIDO_DEV_BIO_UNSET; } } 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); } static void run_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen, const char *type, int (*manifest)(fido_dev_info_t *, size_t, size_t *)) { size_t ndevs = 0; int r; if (*olen >= ilen) { fido_log_debug("%s: skipping %s", __func__, type); return; } if ((r = manifest(devlist + *olen, ilen - *olen, &ndevs)) != FIDO_OK) fido_log_debug("%s: %s: 0x%x", __func__, type, r); fido_log_debug("%s: found %zu %s device%s", __func__, ndevs, type, ndevs == 1 ? "" : "s"); *olen += ndevs; } int fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { *olen = 0; run_manifest(devlist, ilen, olen, "hid", fido_hid_manifest); #ifdef USE_NFC run_manifest(devlist, ilen, olen, "nfc", fido_nfc_manifest); #endif #ifdef USE_PCSC run_manifest(devlist, ilen, olen, "pcsc", fido_pcsc_manifest); #endif #ifdef USE_WINHELLO run_manifest(devlist, ilen, olen, "winhello", fido_winhello_manifest); #endif 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 USE_NFC if (fido_is_nfc(path) && fido_dev_set_nfc(dev) < 0) { fido_log_debug("%s: fido_dev_set_nfc", __func__); return FIDO_ERR_INTERNAL; } #endif #ifdef USE_PCSC if (fido_is_pcsc(path) && fido_dev_set_pcsc(dev) < 0) { fido_log_debug("%s: fido_dev_set_pcsc", __func__); return FIDO_ERR_INTERNAL; } #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 USE_NFC 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_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|FIDO_DEV_CREDMAN_PRE)); } 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.15.0/src/diff_exports.sh000077500000000000000000000012471463251454000171220ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause 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.15.0/src/ecdh.c000066400000000000000000000122241463251454000151330ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/eddsa.c000066400000000000000000000112711463251454000153110ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include "fido.h" #include "fido/eddsa.h" #if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3070000f 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) { EVP_PKEY *pkey; if (len < sizeof(*pk)) return (FIDO_ERR_INVALID_ARGUMENT); memcpy(pk, ptr, sizeof(*pk)); if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) { fido_log_debug("%s: eddsa_pk_to_EVP_PKEY", __func__); return (FIDO_ERR_INVALID_ARGUMENT); } EVP_PKEY_free(pkey); 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_base_id(pkey) != EVP_PKEY_ED25519) return (FIDO_ERR_INVALID_ARGUMENT); 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.15.0/src/err.c000066400000000000000000000103341463251454000150200ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/es256.c000066400000000000000000000262511463251454000151010ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include "fido.h" #include "fido/es256.h" #if OPENSSL_VERSION_NUMBER >= 0x30000000 #define get0_EC_KEY(x) EVP_PKEY_get0_EC_KEY((x)) #else #define get0_EC_KEY(x) EVP_PKEY_get0((x)) #endif static const int es256_nid = NID_X9_62_prime256v1; 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; EVP_PKEY *pkey; 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 */ if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) { fido_log_debug("%s: es256_pk_to_EVP_PKEY", __func__); explicit_bzero(pk, sizeof(*pk)); return (FIDO_ERR_INVALID_ARGUMENT); } EVP_PKEY_free(pkey); 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; 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, es256_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; 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(es256_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; EC_GROUP *g = NULL; size_t dx; size_t dy; int ok = FIDO_ERR_INTERNAL; int nx; int ny; if ((q = EC_KEY_get0_public_key(ec)) == NULL || (g = EC_GROUP_new_by_curve_name(es256_nid)) == 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_is_on_curve(g, q, bnctx) != 1) { fido_log_debug("%s: EC_POINT_is_on_curve", __func__); ok = FIDO_ERR_INVALID_ARGUMENT; goto fail; } if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 || (nx = BN_num_bytes(x)) < 0 || (size_t)nx > sizeof(pk->x) || (ny = BN_num_bytes(y)) < 0 || (size_t)ny > sizeof(pk->y)) { fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp", __func__); goto fail; } dx = sizeof(pk->x) - (size_t)nx; dy = sizeof(pk->y) - (size_t)ny; if ((nx = BN_bn2bin(x, pk->x + dx)) < 0 || (size_t)nx > sizeof(pk->x) || (ny = BN_bn2bin(y, pk->y + dy)) < 0 || (size_t)ny > sizeof(pk->y)) { fido_log_debug("%s: BN_bn2bin", __func__); goto fail; } ok = FIDO_OK; fail: EC_GROUP_free(g); 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) { const EC_KEY *ec; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC || (ec = get0_EC_KEY(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; 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(es256_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; int ok = -1; if ((d = BN_bin2bn(sk->d, (int)sizeof(sk->d), NULL)) == NULL || (ec = EC_KEY_new_by_curve_name(es256_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.15.0/src/es384.c000066400000000000000000000142511463251454000151000ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include "fido.h" #include "fido/es384.h" #if OPENSSL_VERSION_NUMBER >= 0x30000000 #define get0_EC_KEY(x) EVP_PKEY_get0_EC_KEY((x)) #else #define get0_EC_KEY(x) EVP_PKEY_get0((x)) #endif 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) { es384_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 es384_pk_decode(const cbor_item_t *item, es384_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); } es384_pk_t * es384_pk_new(void) { return (calloc(1, sizeof(es384_pk_t))); } void es384_pk_free(es384_pk_t **pkp) { es384_pk_t *pk; if (pkp == NULL || (pk = *pkp) == NULL) return; freezero(pk, sizeof(*pk)); *pkp = NULL; } int es384_pk_from_ptr(es384_pk_t *pk, const void *ptr, size_t len) { const uint8_t *p = ptr; EVP_PKEY *pkey; 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 */ if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL) { fido_log_debug("%s: es384_pk_to_EVP_PKEY", __func__); explicit_bzero(pk, sizeof(*pk)); return (FIDO_ERR_INVALID_ARGUMENT); } EVP_PKEY_free(pkey); return (FIDO_OK); } EVP_PKEY * es384_pk_to_EVP_PKEY(const es384_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; 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_secp384r1)) == 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 es384_pk_from_EC_KEY(es384_pk_t *pk, const EC_KEY *ec) { BN_CTX *bnctx = NULL; BIGNUM *x = NULL; BIGNUM *y = NULL; const EC_POINT *q = NULL; EC_GROUP *g = NULL; size_t dx; size_t dy; int ok = FIDO_ERR_INTERNAL; int nx; int ny; if ((q = EC_KEY_get0_public_key(ec)) == NULL || (g = EC_GROUP_new_by_curve_name(NID_secp384r1)) == 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_is_on_curve(g, q, bnctx) != 1) { fido_log_debug("%s: EC_POINT_is_on_curve", __func__); ok = FIDO_ERR_INVALID_ARGUMENT; goto fail; } if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 || (nx = BN_num_bytes(x)) < 0 || (size_t)nx > sizeof(pk->x) || (ny = BN_num_bytes(y)) < 0 || (size_t)ny > sizeof(pk->y)) { fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp", __func__); goto fail; } dx = sizeof(pk->x) - (size_t)nx; dy = sizeof(pk->y) - (size_t)ny; if ((nx = BN_bn2bin(x, pk->x + dx)) < 0 || (size_t)nx > sizeof(pk->x) || (ny = BN_bn2bin(y, pk->y + dy)) < 0 || (size_t)ny > sizeof(pk->y)) { fido_log_debug("%s: BN_bn2bin", __func__); goto fail; } ok = FIDO_OK; fail: EC_GROUP_free(g); if (bnctx != NULL) { BN_CTX_end(bnctx); BN_CTX_free(bnctx); } return (ok); } int es384_pk_from_EVP_PKEY(es384_pk_t *pk, const EVP_PKEY *pkey) { const EC_KEY *ec; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC || (ec = get0_EC_KEY(pkey)) == NULL) return (FIDO_ERR_INVALID_ARGUMENT); return (es384_pk_from_EC_KEY(pk, ec)); } int es384_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 es384_pk_verify_sig(const fido_blob_t *dgst, const es384_pk_t *pk, const fido_blob_t *sig) { EVP_PKEY *pkey; int ok = -1; if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL || es384_verify_sig(dgst, pkey, sig) < 0) { fido_log_debug("%s: es384_verify_sig", __func__); goto fail; } ok = 0; fail: EVP_PKEY_free(pkey); return (ok); } libfido2-1.15.0/src/export.gnu000066400000000000000000000153751463251454000161320ustar00rootroot00000000000000{ 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; es384_pk_free; es384_pk_from_EC_KEY; es384_pk_from_EVP_PKEY; es384_pk_from_ptr; es384_pk_new; es384_pk_to_EVP_PKEY; fido_assert_allow_cred; fido_assert_authdata_len; fido_assert_authdata_ptr; fido_assert_authdata_raw_len; fido_assert_authdata_raw_ptr; fido_assert_blob_len; fido_assert_blob_ptr; fido_assert_clientdata_hash_len; fido_assert_clientdata_hash_ptr; fido_assert_count; fido_assert_empty_allow_list; 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_set_winhello_appid; 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_certs_len; fido_cbor_info_certs_name_ptr; fido_cbor_info_certs_value_ptr; fido_cbor_info_extensions_len; fido_cbor_info_extensions_ptr; fido_cbor_info_free; fido_cbor_info_fwversion; fido_cbor_info_maxcredbloblen; fido_cbor_info_maxcredcntlst; fido_cbor_info_maxcredidlen; fido_cbor_info_maxlargeblob; fido_cbor_info_maxmsgsiz; fido_cbor_info_maxrpid_minpinlen; fido_cbor_info_minpinlen; fido_cbor_info_new; fido_cbor_info_new_pin_required; 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_rk_remaining; fido_cbor_info_transports_len; fido_cbor_info_transports_ptr; fido_cbor_info_uv_attempts; fido_cbor_info_uv_modality; 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_empty_exclude_list; 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_attobj; 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_list_count; fido_cred_x5c_list_len; fido_cred_x5c_list_ptr; 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.15.0/src/export.llvm000066400000000000000000000143141463251454000163030ustar00rootroot00000000000000_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 _es384_pk_free _es384_pk_from_EC_KEY _es384_pk_from_EVP_PKEY _es384_pk_from_ptr _es384_pk_new _es384_pk_to_EVP_PKEY _fido_assert_allow_cred _fido_assert_authdata_len _fido_assert_authdata_ptr _fido_assert_authdata_raw_len _fido_assert_authdata_raw_ptr _fido_assert_blob_len _fido_assert_blob_ptr _fido_assert_clientdata_hash_len _fido_assert_clientdata_hash_ptr _fido_assert_count _fido_assert_empty_allow_list _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_set_winhello_appid _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_certs_len _fido_cbor_info_certs_name_ptr _fido_cbor_info_certs_value_ptr _fido_cbor_info_extensions_len _fido_cbor_info_extensions_ptr _fido_cbor_info_free _fido_cbor_info_fwversion _fido_cbor_info_maxcredbloblen _fido_cbor_info_maxcredcntlst _fido_cbor_info_maxcredidlen _fido_cbor_info_maxlargeblob _fido_cbor_info_maxmsgsiz _fido_cbor_info_maxrpid_minpinlen _fido_cbor_info_minpinlen _fido_cbor_info_new _fido_cbor_info_new_pin_required _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_rk_remaining _fido_cbor_info_transports_len _fido_cbor_info_transports_ptr _fido_cbor_info_uv_attempts _fido_cbor_info_uv_modality _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_empty_exclude_list _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_attobj _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_list_count _fido_cred_x5c_list_len _fido_cred_x5c_list_ptr _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.15.0/src/export.msvc000066400000000000000000000137111463251454000163010ustar00rootroot00000000000000EXPORTS 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 es384_pk_free es384_pk_from_EC_KEY es384_pk_from_EVP_PKEY es384_pk_from_ptr es384_pk_new es384_pk_to_EVP_PKEY fido_assert_allow_cred fido_assert_authdata_len fido_assert_authdata_ptr fido_assert_authdata_raw_len fido_assert_authdata_raw_ptr fido_assert_blob_len fido_assert_blob_ptr fido_assert_clientdata_hash_len fido_assert_clientdata_hash_ptr fido_assert_count fido_assert_empty_allow_list 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_set_winhello_appid 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_certs_len fido_cbor_info_certs_name_ptr fido_cbor_info_certs_value_ptr fido_cbor_info_extensions_len fido_cbor_info_extensions_ptr fido_cbor_info_free fido_cbor_info_fwversion fido_cbor_info_maxcredbloblen fido_cbor_info_maxcredcntlst fido_cbor_info_maxcredidlen fido_cbor_info_maxlargeblob fido_cbor_info_maxmsgsiz fido_cbor_info_maxrpid_minpinlen fido_cbor_info_minpinlen fido_cbor_info_new fido_cbor_info_new_pin_required 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_rk_remaining fido_cbor_info_transports_len fido_cbor_info_transports_ptr fido_cbor_info_uv_attempts fido_cbor_info_uv_modality 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_empty_exclude_list 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_attobj 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_list_count fido_cred_x5c_list_len fido_cred_x5c_list_ptr 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.15.0/src/extern.h000066400000000000000000000260561463251454000155520ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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_attobj(const cbor_item_t *, fido_cred_t *); int cbor_decode_bool(const cbor_item_t *, bool *); 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 es384_pk_decode(const cbor_item_t *, es384_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 */ bool fido_is_nfc(const char *); bool nfc_is_fido(const char *); 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 *); int fido_dev_set_nfc(fido_dev_t *); /* pcsc i/o */ bool fido_is_pcsc(const char *); void *fido_pcsc_open(const char *); void fido_pcsc_close(void *); int fido_pcsc_read(void *, unsigned char *, size_t, int); int fido_pcsc_write(void *, const unsigned char *, size_t); int fido_pcsc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int); int fido_pcsc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t); int fido_dev_set_pcsc(fido_dev_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_cert_array_free(fido_cert_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 *); int fido_to_uint64(const char *, int, uint64_t *); /* crypto */ int es256_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *); int es384_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 es384_pk_verify_sig(const fido_blob_t *, const es384_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 *); int fido_pcsc_manifest(fido_dev_info_t *, size_t, size_t *); /* fuzzing instrumentation */ #ifdef FIDO_FUZZ uint32_t uniform_random(uint32_t); #endif /* internal device capability flags */ #define FIDO_DEV_PIN_SET 0x0001 #define FIDO_DEV_PIN_UNSET 0x0002 #define FIDO_DEV_CRED_PROT 0x0004 #define FIDO_DEV_CREDMAN 0x0008 #define FIDO_DEV_PIN_PROTOCOL1 0x0010 #define FIDO_DEV_PIN_PROTOCOL2 0x0020 #define FIDO_DEV_UV_SET 0x0040 #define FIDO_DEV_UV_UNSET 0x0080 #define FIDO_DEV_TOKEN_PERMS 0x0100 #define FIDO_DEV_WINHELLO 0x0200 #define FIDO_DEV_CREDMAN_PRE 0x0400 #define FIDO_DEV_BIO_SET 0x0800 #define FIDO_DEV_BIO_UNSET 0x1000 /* 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:" #define FIDO_PCSC_PREFIX "pcsc:" #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_EXTERN_H */ libfido2-1.15.0/src/fallthrough.h000066400000000000000000000007441463251454000165600ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #ifndef _FALLTHROUGH_H #define _FALLTHROUGH_H #if defined(__GNUC__) #if __has_attribute(fallthrough) #define FALLTHROUGH __attribute__((fallthrough)); #endif #endif /* __GNUC__ */ #ifndef FALLTHROUGH #define FALLTHROUGH /* FALLTHROUGH */ #endif #endif /* !_FALLTHROUGH_H */ libfido2-1.15.0/src/fido.h000066400000000000000000000330571463251454000151650ustar00rootroot00000000000000/* * Copyright (c) 2018-2022 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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_authdata_raw_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_certs_name_ptr(const fido_cbor_info_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 uint64_t *fido_cbor_info_certs_value_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 *); const unsigned char *fido_cred_x5c_list_ptr(const fido_cred_t *, size_t); int fido_assert_allow_cred(fido_assert_t *, const unsigned char *, size_t); int fido_assert_empty_allow_list(fido_assert_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_set_winhello_appid(fido_assert_t *, const char *); 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_empty_exclude_list(fido_cred_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_attobj(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 *); #ifdef _FIDO_SIGSET_DEFINED int fido_dev_set_sigmask(fido_dev_t *, const fido_sigset_t *); #endif 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_authdata_raw_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_certs_len(const fido_cbor_info_t *); size_t fido_cbor_info_extensions_len(const fido_cbor_info_t *); size_t fido_cbor_info_options_len(const fido_cbor_info_t *); size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *); size_t fido_cbor_info_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 *); size_t fido_cred_x5c_list_count(const fido_cred_t *); size_t fido_cred_x5c_list_len(const fido_cred_t *, size_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_fwversion(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_maxlargeblob(const fido_cbor_info_t *); uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *); uint64_t fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *); uint64_t fido_cbor_info_minpinlen(const fido_cbor_info_t *); uint64_t fido_cbor_info_uv_attempts(const fido_cbor_info_t *); uint64_t fido_cbor_info_uv_modality(const fido_cbor_info_t *); int64_t fido_cbor_info_rk_remaining(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 *); bool fido_cbor_info_new_pin_required(const fido_cbor_info_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.15.0/src/fido/000077500000000000000000000000001463251454000150045ustar00rootroot00000000000000libfido2-1.15.0/src/fido/bio.h000066400000000000000000000115651463251454000157360ustar00rootroot00000000000000/* * Copyright (c) 2019 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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.15.0/src/fido/config.h000066400000000000000000000041631463251454000164260ustar00rootroot00000000000000/* * Copyright (c) 2020 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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.15.0/src/fido/credman.h000066400000000000000000000075121463251454000165730ustar00rootroot00000000000000/* * Copyright (c) 2019-2021 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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.15.0/src/fido/eddsa.h000066400000000000000000000046671463251454000162520ustar00rootroot00000000000000/* * Copyright (c) 2019 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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) && LIBRESSL_VERSION_NUMBER < 0x3070000f #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.15.0/src/fido/err.h000066400000000000000000000075461463251454000157610ustar00rootroot00000000000000/* * Copyright (c) 2018 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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.15.0/src/fido/es256.h000066400000000000000000000045741463251454000160330ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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.15.0/src/fido/es384.h000066400000000000000000000040241463251454000160230ustar00rootroot00000000000000/* * Copyright (c) 2022 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #ifndef _FIDO_ES384_H #define _FIDO_ES384_H #include #include #include #ifdef _FIDO_INTERNAL #include "types.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ es384_pk_t *es384_pk_new(void); void es384_pk_free(es384_pk_t **); EVP_PKEY *es384_pk_to_EVP_PKEY(const es384_pk_t *); int es384_pk_from_EC_KEY(es384_pk_t *, const EC_KEY *); int es384_pk_from_EVP_PKEY(es384_pk_t *, const EVP_PKEY *); int es384_pk_from_ptr(es384_pk_t *, const void *, size_t); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* !_FIDO_ES384_H */ libfido2-1.15.0/src/fido/param.h000066400000000000000000000127771463251454000162730ustar00rootroot00000000000000/* * Copyright (c) 2018-2022 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #ifndef _FIDO_PARAM_H #define _FIDO_PARAM_H /* Authentication data flags. */ #define CTAP_AUTHDATA_USER_PRESENT 0x01 #define CTAP_AUTHDATA_USER_VERIFIED 0x04 #define CTAP_AUTHDATA_ATT_CRED 0x40 #define CTAP_AUTHDATA_EXT_DATA 0x80 /* CTAPHID command opcodes. */ #define CTAP_CMD_PING 0x01 #define CTAP_CMD_MSG 0x03 #define CTAP_CMD_LOCK 0x04 #define CTAP_CMD_INIT 0x06 #define CTAP_CMD_WINK 0x08 #define CTAP_CMD_CBOR 0x10 #define CTAP_CMD_CANCEL 0x11 #define CTAP_KEEPALIVE 0x3b #define CTAP_FRAME_INIT 0x80 /* CTAPHID CBOR command opcodes. */ #define CTAP_CBOR_MAKECRED 0x01 #define CTAP_CBOR_ASSERT 0x02 #define CTAP_CBOR_GETINFO 0x04 #define CTAP_CBOR_CLIENT_PIN 0x06 #define CTAP_CBOR_RESET 0x07 #define CTAP_CBOR_NEXT_ASSERT 0x08 #define CTAP_CBOR_BIO_ENROLL 0x09 #define CTAP_CBOR_CRED_MGMT 0x0a #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_ES384 -35 #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_P384 2 #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 */ /* Recognised UV modes. */ #define FIDO_UV_MODE_TUP 0x0001 /* internal test of user presence */ #define FIDO_UV_MODE_FP 0x0002 /* internal fingerprint check */ #define FIDO_UV_MODE_PIN 0x0004 /* internal pin check */ #define FIDO_UV_MODE_VOICE 0x0008 /* internal voice recognition */ #define FIDO_UV_MODE_FACE 0x0010 /* internal face recognition */ #define FIDO_UV_MODE_LOCATION 0x0020 /* internal location check */ #define FIDO_UV_MODE_EYE 0x0040 /* internal eyeprint check */ #define FIDO_UV_MODE_DRAWN 0x0080 /* internal drawn pattern check */ #define FIDO_UV_MODE_HAND 0x0100 /* internal handprint verification */ #define FIDO_UV_MODE_NONE 0x0200 /* TUP/UV not required */ #define FIDO_UV_MODE_ALL 0x0400 /* all supported UV modes required */ #define FIDO_UV_MODE_EXT_PIN 0x0800 /* external pin verification */ #define FIDO_UV_MODE_EXT_DRAWN 0x1000 /* external drawn pattern check */ #endif /* !_FIDO_PARAM_H */ libfido2-1.15.0/src/fido/rs256.h000066400000000000000000000040241463251454000160360ustar00rootroot00000000000000/* * Copyright (c) 2018-2021 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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.15.0/src/fido/types.h000066400000000000000000000300101463251454000163130ustar00rootroot00000000000000/* * Copyright (c) 2018-2022 Yubico AB. All rights reserved. * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #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 *); #undef _FIDO_SIGSET_DEFINED #define _FIDO_SIGSET_DEFINED #ifdef _WIN32 typedef int fido_sigset_t; #elif defined(SIG_BLOCK) typedef sigset_t fido_sigset_t; #else #undef _FIDO_SIGSET_DEFINED #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 ES384 (ECDSA over P-384 with SHA-384) public key */ typedef struct es384_pk { unsigned char x[48]; unsigned char y[48]; } es384_pk_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; es384_pk_t es384; 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_array_t x5c; /* attestation certificate chain */ 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_blob_t authdata_raw; /* raw authdata */ 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 */ char *appid; /* winhello u2f appid */ 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_cert_array { char **name; uint64_t *value; size_t len; } fido_cert_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 credentials in list */ uint64_t maxcredidlen; /* max credential ID length */ uint64_t fwversion; /* firmware version */ uint64_t maxcredbloblen; /* max credBlob length */ uint64_t maxlargeblob; /* max largeBlob array length */ uint64_t maxrpid_minlen; /* max rpid in set_pin_minlen_rpid */ uint64_t minpinlen; /* min pin len enforced */ uint64_t uv_attempts; /* platform uv attempts */ uint64_t uv_modality; /* bitmask of supported uv types */ int64_t rk_remaining; /* remaining resident credentials */ bool new_pin_reqd; /* new pin required */ fido_cert_array_t certs; /* associated certifications */ } 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 es384_pk es384_pk_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.15.0/src/hid.c000066400000000000000000000100011463251454000147630ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/hid_freebsd.c000066400000000000000000000166471463251454000165030ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #if __FreeBSD_version >= 1300500 #include #define USE_HIDRAW /* see usbhid(4) and hidraw(4) on FreeBSD 13+ */ #endif #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); } #ifdef USE_HIDRAW static int copy_info_hidraw(fido_dev_info_t *di, const char *path) { int fd = -1; int ok = -1; struct usb_device_info udi; struct hidraw_devinfo devinfo; char rawname[129]; memset(di, 0, sizeof(*di)); memset(&udi, 0, sizeof(udi)); memset(&devinfo, 0, sizeof(devinfo)); memset(rawname, 0, sizeof(rawname)); if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0) goto fail; if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { if (ioctl(fd, IOCTL_REQ(HIDIOCGRAWINFO), &devinfo) == -1 || ioctl(fd, IOCTL_REQ(HIDIOCGRAWNAME(128)), rawname) == -1 || (di->path = strdup(path)) == NULL || (di->manufacturer = strdup(UHID_VENDOR)) == NULL || (di->product = strdup(rawname)) == NULL) goto fail; di->vendor_id = devinfo.vendor; di->product_id = devinfo.product; } else { 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 %s", __func__, path); if (ok < 0) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); } return (ok); } #endif /* USE_HIDRAW */ static int copy_info_uhid(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) == -1) fido_log_error(errno, "%s: close %s", __func__, path); 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; if (ilen == 0) return (FIDO_OK); /* nothing to do */ if (devlist == NULL || olen == NULL) return (FIDO_ERR_INVALID_ARGUMENT); *olen = 0; #ifdef USE_HIDRAW for (i = 0; i < MAX_UHID && *olen < ilen; i++) { snprintf(path, sizeof(path), "/dev/hidraw%zu", i); if (copy_info_hidraw(&devlist[*olen], path) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_hid_open, fido_hid_close, fido_hid_read, fido_hid_write, }; ++(*olen); } } /* hidraw(4) is preferred over uhid(4) */ if (*olen != 0) return (FIDO_OK); #endif /* USE_HIDRAW */ for (i = 0; i < MAX_UHID && *olen < ilen; i++) { snprintf(path, sizeof(path), "/dev/uhid%zu", i); if (copy_info_uhid(&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); /* * N.B. if ctx->fd is an hidraw(4) device, the ioctl() below puts it in * uhid(4) compat mode, which we need to keep fido_hid_write() as-is. */ 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.15.0/src/hid_hidapi.c000066400000000000000000000115441463251454000163160ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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; if ((hrd = calloc(1, sizeof(*hrd))) == NULL || get_report_descriptor(hdi->path, hrd) < 0 || fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0) usage_page = 0; free(hrd); 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.15.0/src/hid_linux.c000066400000000000000000000211001463251454000162040ustar00rootroot00000000000000/* * Copyright (c) 2019-2024 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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 = -1; uint32_t usage_page = 0; struct hidraw_report_descriptor *hrd = NULL; if ((hrd = calloc(1, sizeof(*hrd))) == NULL || (fd = fido_hid_unix_open(path)) == -1) goto out; if (get_report_descriptor(fd, hrd) < 0 || fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0) usage_page = 0; out: free(hrd); if (fd != -1 && 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 **hid_name) { char *cp; char *p; char *s; bool found_id = false; bool found_name = false; 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 (!found_id && 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; found_id = true; } } else if (!found_name && strncmp(p, "HID_NAME=", 9) == 0) { if ((*hid_name = strdup(p + 9)) != NULL) found_name = true; } } free(s); if (!found_name || !found_id) return (-1); return (0); } 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; char *hid_name = NULL; 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, &hid_name) < 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); di->manufacturer = get_usb_attr(dev, "manufacturer"); di->product = get_usb_attr(dev, "product"); if (di->manufacturer == NULL && di->product == NULL) { di->product = hid_name; /* fallback */ hid_name = NULL; } if (di->manufacturer == NULL) di->manufacturer = strdup(""); if (di->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); free(hid_name); 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; bool looped; retry: looped = false; 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); } looped = true; if (retries++ >= 20) { 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 (looped) { fido_log_debug("%s: retrying", __func__); fido_hid_close(ctx); goto retry; } if ((hrd = calloc(1, sizeof(*hrd))) == NULL || 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; } free(hrd); 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.15.0/src/hid_netbsd.c000066400000000000000000000171001463251454000163310ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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; if (devlist == NULL || olen == NULL) return (FIDO_ERR_INVALID_ARGUMENT); *olen = 0; if (ilen == 0) return (FIDO_OK); /* nothing to do */ 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.15.0/src/hid_openbsd.c000066400000000000000000000143631463251454000165140ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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; }; static int copy_info(fido_dev_info_t *di, const char *path) { int fd = -1, 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) goto fail; if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) { fido_log_error(errno, "%s: ioctl %s", __func__, path); goto fail; } 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); 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 %s", __func__, path); 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) { size_t i; char path[64]; 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 (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 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.15.0/src/hid_osx.c000066400000000000000000000315011463251454000156640ustar00rootroot00000000000000/* * Copyright (c) 2019-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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 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 (fido_to_uint64(path + strlen(IOREG), 10, &id) == -1) { fido_log_debug("%s: fido_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); } static void schedule_io_loop(struct hid_osx *ctx, int ms) { 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); } 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); } /* check for pending frame */ if ((r = read(ctx->report_pipe[0], buf, len)) == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { fido_log_error(errno, "%s: read", __func__); return (-1); } schedule_io_loop(ctx, ms); 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.15.0/src/hid_unix.c000066400000000000000000000026411463251454000160410ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/hid_win.c000066400000000000000000000307531463251454000156600ustar00rootroot00000000000000/* * Copyright (c) 2019-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. * SPDX-License-Identifier: BSD-2-Clause */ #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__) && __MINGW64_VERSION_MAJOR < 8 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.15.0/src/info.c000066400000000000000000000326471463251454000151760ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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_decode_bool(val, NULL) < 0) { fido_log_debug("%s: cbor_decode_bool", __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 decode_cert(const cbor_item_t *key, const cbor_item_t *val, void *arg) { fido_cert_array_t *c = arg; const size_t i = c->len; if (cbor_is_int(val) == false) { fido_log_debug("%s: cbor_is_int", __func__); return (0); /* ignore */ } if (cbor_string_copy(key, &c->name[i]) < 0) { fido_log_debug("%s: cbor_string_copy", __func__); return (0); /* ignore */ } /* keep name/value and len consistent */ c->value[i] = cbor_get_int(val); c->len++; return (0); } static int decode_certs(const cbor_item_t *item, fido_cert_array_t *c) { c->name = NULL; c->value = NULL; c->len = 0; if (cbor_isa_map(item) == false || cbor_map_is_definite(item) == false) { fido_log_debug("%s: cbor type", __func__); return (-1); } c->name = calloc(cbor_map_size(item), sizeof(char *)); c->value = calloc(cbor_map_size(item), sizeof(uint64_t)); if (c->name == NULL || c->value == NULL) return (-1); return (cbor_map_iter(item, c, decode_cert)); } static int parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg) { fido_cbor_info_t *ci = 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 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 11: /* maxSerializedLargeBlobArray */ return (cbor_decode_uint64(val, &ci->maxlargeblob)); case 12: /* forcePINChange */ return (cbor_decode_bool(val, &ci->new_pin_reqd)); case 13: /* minPINLength */ return (cbor_decode_uint64(val, &ci->minpinlen)); case 14: /* fwVersion */ return (cbor_decode_uint64(val, &ci->fwversion)); case 15: /* maxCredBlobLen */ return (cbor_decode_uint64(val, &ci->maxcredbloblen)); case 16: /* maxRPIDsForSetMinPINLength */ return (cbor_decode_uint64(val, &ci->maxrpid_minlen)); case 17: /* preferredPlatformUvAttempts */ return (cbor_decode_uint64(val, &ci->uv_attempts)); case 18: /* uvModality */ return (cbor_decode_uint64(val, &ci->uv_modality)); case 19: /* certifications */ return (decode_certs(val, &ci->certs)); case 20: /* remainingDiscoverableCredentials */ if (cbor_decode_uint64(val, &x) < 0 || x > INT64_MAX) { fido_log_debug("%s: cbor_decode_uint64", __func__); return (-1); } ci->rk_remaining = (int64_t)x; return (0); default: /* ignore */ fido_log_debug("%s: cbor type: 0x%02x", __func__, cbor_get_uint8(key)); 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 *msg; int msglen; int r; fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev, (void *)ci, *ms); fido_cbor_info_reset(ci); if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } r = cbor_parse_reply(msg, (size_t)msglen, ci, parse_reply_element); out: freezero(msg, FIDO_MAXMSG); return (r); } 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) { fido_cbor_info_t *ci; if ((ci = calloc(1, sizeof(fido_cbor_info_t))) == NULL) return (NULL); fido_cbor_info_reset(ci); return (ci); } 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); fido_cert_array_free(&ci->certs); ci->rk_remaining = -1; } 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_maxlargeblob(const fido_cbor_info_t *ci) { return (ci->maxlargeblob); } uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *ci) { return (ci->fwversion); } uint64_t fido_cbor_info_minpinlen(const fido_cbor_info_t *ci) { return (ci->minpinlen); } uint64_t fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *ci) { return (ci->maxrpid_minlen); } uint64_t fido_cbor_info_uv_attempts(const fido_cbor_info_t *ci) { return (ci->uv_attempts); } uint64_t fido_cbor_info_uv_modality(const fido_cbor_info_t *ci) { return (ci->uv_modality); } int64_t fido_cbor_info_rk_remaining(const fido_cbor_info_t *ci) { return (ci->rk_remaining); } 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); } bool fido_cbor_info_new_pin_required(const fido_cbor_info_t *ci) { return (ci->new_pin_reqd); } char ** fido_cbor_info_certs_name_ptr(const fido_cbor_info_t *ci) { return (ci->certs.name); } const uint64_t * fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *ci) { return (ci->certs.value); } size_t fido_cbor_info_certs_len(const fido_cbor_info_t *ci) { return (ci->certs.len); } libfido2-1.15.0/src/io.c000066400000000000000000000173321463251454000146440ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 *msg; int msglen; int r; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(d, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0 || (size_t)msglen < 1) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } r = msg[0]; out: freezero(msg, FIDO_MAXMSG); return (r); } libfido2-1.15.0/src/iso7816.c000066400000000000000000000027741463251454000153610ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/iso7816.h000066400000000000000000000020611463251454000153530ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/largeblob.c000066400000000000000000000521051463251454000161630ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 *msg; int msglen, r; *chunk = NULL; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto out; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto out; } if ((*chunk = fido_blob_new()) == NULL) { fido_log_debug("%s: fido_blob_new", __func__); r = FIDO_ERR_INTERNAL; goto out; } if ((r = cbor_parse_reply(msg, (size_t)msglen, *chunk, parse_largeblob_reply)) != FIDO_OK) { fido_log_debug("%s: parse_largeblob_reply", __func__); goto out; } r = FIDO_OK; out: if (r != FIDO_OK) fido_blob_free(chunk); freezero(msg, FIDO_MAXMSG); return r; } 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.15.0/src/libfido2.pc.in000066400000000000000000000004621463251454000165100ustar00rootroot00000000000000prefix=@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.15.0/src/log.c000066400000000000000000000043201463251454000150070ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/netlink.c000066400000000000000000000400541463251454000156760ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/netlink.h000066400000000000000000000020071463251454000156770ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/nfc.c000066400000000000000000000160531463251454000150020ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include "fido.h" #include "fido/param.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' }; 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, 0x00, (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; } } bool nfc_is_fido(const char *path) { bool fido = false; fido_dev_t *d; int r; if ((d = fido_dev_new()) == NULL) { fido_log_debug("%s: fido_dev_new", __func__); goto fail; } /* fido_dev_open selects the fido applet */ if ((r = fido_dev_open(d, path)) != FIDO_OK) { fido_log_debug("%s: fido_dev_open: 0x%x", __func__, r); goto fail; } if ((r = fido_dev_close(d)) != FIDO_OK) { fido_log_debug("%s: fido_dev_close: 0x%x", __func__, r); goto fail; } fido = true; fail: fido_dev_free(&d); return fido; } #ifdef USE_NFC bool fido_is_nfc(const char *path) { return strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) == 0; } int fido_dev_set_nfc(fido_dev_t *d) { if (d->io_handle != NULL) { fido_log_debug("%s: device open", __func__); return -1; } d->io_own = true; d->io = (fido_dev_io_t) { fido_nfc_open, fido_nfc_close, fido_nfc_read, fido_nfc_write, }; d->transport = (fido_dev_transport_t) { fido_nfc_rx, fido_nfc_tx, }; return 0; } #endif /* USE_NFC */ libfido2-1.15.0/src/nfc_linux.c000066400000000000000000000167651463251454000162330ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include "fido.h" #include "fido/param.h" #include "netlink.h" #include "iso7816.h" struct nfc_linux { int fd; uint32_t dev; uint32_t target; sigset_t sigmask; const sigset_t *sigmaskp; struct fido_nl *nl; }; 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; char *str; struct udev_device *dev = NULL; uint64_t id; 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) goto fail; if (asprintf(&di->path, "%s/%s", FIDO_NFC_PREFIX, name) == -1) { di->path = NULL; goto fail; } if (nfc_is_fido(di->path) == false) { fido_log_debug("%s: nfc_is_fido: %s", __func__, di->path); 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 && fido_to_uint64(str, 16, &id) == 0 && id <= UINT16_MAX) di->vendor_id = (int16_t)id; free(str); if ((str = get_usb_attr(dev, "idProduct")) != NULL && fido_to_uint64(str, 16, &id) == 0 && id <= UINT16_MAX) di->product_id = (int16_t)id; free(str); ok = 0; fail: if (dev != NULL) udev_device_unref(dev); if (ok < 0) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); } return ok; } static int sysnum_from_syspath(const char *path) { struct udev *udev = NULL; struct udev_device *dev = NULL; const char *str; uint64_t idx64; int idx = -1; if ((udev = udev_new()) != NULL && (dev = udev_device_new_from_syspath(udev, path)) != NULL && (str = udev_device_get_sysnum(dev)) != NULL && fido_to_uint64(str, 10, &idx64) == 0 && idx64 < INT_MAX) idx = (int)idx64; 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.15.0/src/packed.h000066400000000000000000000011051463251454000154600ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/pcsc.c000066400000000000000000000210241463251454000151560ustar00rootroot00000000000000/* * Copyright (c) 2022 Micro Focus or one of its affiliates. * 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. * SPDX-License-Identifier: BSD-2-Clause */ #if __APPLE__ #include #include #else #include #endif /* __APPLE__ */ #include #include "fido.h" #include "fido/param.h" #include "iso7816.h" #if defined(_WIN32) && !defined(__MINGW32__) #define SCardConnect SCardConnectA #define SCardListReaders SCardListReadersA #endif #ifndef SCARD_PROTOCOL_Tx #define SCARD_PROTOCOL_Tx SCARD_PROTOCOL_ANY #endif #define BUFSIZE 1024 /* in bytes; passed to SCardListReaders() */ #define APDULEN 264 /* 261 rounded up to the nearest multiple of 8 */ #define READERS 8 /* maximum number of readers */ struct pcsc { SCARDCONTEXT ctx; SCARDHANDLE h; SCARD_IO_REQUEST req; uint8_t rx_buf[APDULEN]; size_t rx_len; }; static LONG list_readers(SCARDCONTEXT ctx, char **buf) { LONG s; DWORD len; len = BUFSIZE; if ((*buf = calloc(1, len)) == NULL) goto fail; if ((s = SCardListReaders(ctx, NULL, *buf, &len)) != SCARD_S_SUCCESS) { fido_log_debug("%s: SCardListReaders 0x%lx", __func__, (long)s); goto fail; } /* sanity check "multi-string" */ if (len > BUFSIZE || len < 2) { fido_log_debug("%s: bogus len=%u", __func__, (unsigned)len); goto fail; } if ((*buf)[len - 1] != 0 || (*buf)[len - 2] != '\0') { fido_log_debug("%s: bogus buf", __func__); goto fail; } return (LONG)SCARD_S_SUCCESS; fail: free(*buf); *buf = NULL; return (LONG)SCARD_E_NO_READERS_AVAILABLE; } static char * get_reader(SCARDCONTEXT ctx, const char *path) { char *reader = NULL, *buf = NULL; const char prefix[] = FIDO_PCSC_PREFIX "//slot"; uint64_t n; if (path == NULL) goto out; if (strncmp(path, prefix, strlen(prefix)) != 0 || fido_to_uint64(path + strlen(prefix), 10, &n) < 0 || n > READERS - 1) { fido_log_debug("%s: invalid path %s", __func__, path); goto out; } if (list_readers(ctx, &buf) != SCARD_S_SUCCESS) { fido_log_debug("%s: list_readers", __func__); goto out; } for (const char *name = buf; *name != 0; name += strlen(name) + 1) { if (n == 0) { reader = strdup(name); goto out; } n--; } fido_log_debug("%s: failed to find reader %s", __func__, path); out: free(buf); return reader; } static int prepare_io_request(DWORD prot, SCARD_IO_REQUEST *req) { switch (prot) { case SCARD_PROTOCOL_T0: req->dwProtocol = SCARD_PCI_T0->dwProtocol; req->cbPciLength = SCARD_PCI_T0->cbPciLength; break; case SCARD_PROTOCOL_T1: req->dwProtocol = SCARD_PCI_T1->dwProtocol; req->cbPciLength = SCARD_PCI_T1->cbPciLength; break; default: fido_log_debug("%s: unknown protocol %lu", __func__, (u_long)prot); return -1; } return 0; } static int copy_info(fido_dev_info_t *di, SCARDCONTEXT ctx, const char *reader, size_t idx) { SCARDHANDLE h = 0; SCARD_IO_REQUEST req; DWORD prot = 0; LONG s; int ok = -1; memset(di, 0, sizeof(*di)); memset(&req, 0, sizeof(req)); if ((s = SCardConnect(ctx, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_Tx, &h, &prot)) != SCARD_S_SUCCESS) { fido_log_debug("%s: SCardConnect 0x%lx", __func__, (long)s); goto fail; } if (prepare_io_request(prot, &req) < 0) { fido_log_debug("%s: prepare_io_request", __func__); goto fail; } if (asprintf(&di->path, "%s//slot%zu", FIDO_PCSC_PREFIX, idx) == -1) { di->path = NULL; fido_log_debug("%s: asprintf", __func__); goto fail; } if (nfc_is_fido(di->path) == false) { fido_log_debug("%s: nfc_is_fido: %s", __func__, di->path); goto fail; } if ((di->manufacturer = strdup("PC/SC")) == NULL || (di->product = strdup(reader)) == NULL) goto fail; ok = 0; fail: if (h != 0) SCardDisconnect(h, SCARD_LEAVE_CARD); if (ok < 0) { free(di->path); free(di->manufacturer); free(di->product); explicit_bzero(di, sizeof(*di)); } return ok; } int fido_pcsc_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { SCARDCONTEXT ctx = 0; char *buf = NULL; LONG s; size_t idx = 0; int r = FIDO_ERR_INTERNAL; *olen = 0; if (ilen == 0) return FIDO_OK; if (devlist == NULL) return FIDO_ERR_INVALID_ARGUMENT; if ((s = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &ctx)) != SCARD_S_SUCCESS || ctx == 0) { fido_log_debug("%s: SCardEstablishContext 0x%lx", __func__, (long)s); if (s == (LONG)SCARD_E_NO_SERVICE || s == (LONG)SCARD_E_NO_SMARTCARD) r = FIDO_OK; /* suppress error */ goto out; } if ((s = list_readers(ctx, &buf)) != SCARD_S_SUCCESS) { fido_log_debug("%s: list_readers 0x%lx", __func__, (long)s); if (s == (LONG)SCARD_E_NO_READERS_AVAILABLE) r = FIDO_OK; /* suppress error */ goto out; } for (const char *name = buf; *name != 0; name += strlen(name) + 1) { if (idx == READERS) { fido_log_debug("%s: stopping at %zu readers", __func__, idx); r = FIDO_OK; goto out; } if (copy_info(&devlist[*olen], ctx, name, idx++) == 0) { devlist[*olen].io = (fido_dev_io_t) { fido_pcsc_open, fido_pcsc_close, fido_pcsc_read, fido_pcsc_write, }; devlist[*olen].transport = (fido_dev_transport_t) { fido_pcsc_rx, fido_pcsc_tx, }; if (++(*olen) == ilen) break; } } r = FIDO_OK; out: free(buf); if (ctx != 0) SCardReleaseContext(ctx); return r; } void * fido_pcsc_open(const char *path) { char *reader = NULL; struct pcsc *dev = NULL; SCARDCONTEXT ctx = 0; SCARDHANDLE h = 0; SCARD_IO_REQUEST req; DWORD prot = 0; LONG s; memset(&req, 0, sizeof(req)); if ((s = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &ctx)) != SCARD_S_SUCCESS || ctx == 0) { fido_log_debug("%s: SCardEstablishContext 0x%lx", __func__, (long)s); goto fail; } if ((reader = get_reader(ctx, path)) == NULL) { fido_log_debug("%s: get_reader(%s)", __func__, path); goto fail; } if ((s = SCardConnect(ctx, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_Tx, &h, &prot)) != SCARD_S_SUCCESS) { fido_log_debug("%s: SCardConnect 0x%lx", __func__, (long)s); goto fail; } if (prepare_io_request(prot, &req) < 0) { fido_log_debug("%s: prepare_io_request", __func__); goto fail; } if ((dev = calloc(1, sizeof(*dev))) == NULL) goto fail; dev->ctx = ctx; dev->h = h; dev->req = req; ctx = 0; h = 0; fail: if (h != 0) SCardDisconnect(h, SCARD_LEAVE_CARD); if (ctx != 0) SCardReleaseContext(ctx); free(reader); return dev; } void fido_pcsc_close(void *handle) { struct pcsc *dev = handle; if (dev->h != 0) SCardDisconnect(dev->h, SCARD_LEAVE_CARD); if (dev->ctx != 0) SCardReleaseContext(dev->ctx); explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf)); free(dev); } int fido_pcsc_read(void *handle, unsigned char *buf, size_t len, int ms) { struct pcsc *dev = handle; int r; (void)ms; if (dev->rx_len == 0 || dev->rx_len > len || dev->rx_len > sizeof(dev->rx_buf)) { fido_log_debug("%s: rx_len", __func__); return -1; } fido_log_xxd(dev->rx_buf, dev->rx_len, "%s: reading", __func__); memcpy(buf, dev->rx_buf, dev->rx_len); explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf)); r = (int)dev->rx_len; dev->rx_len = 0; return r; } int fido_pcsc_write(void *handle, const unsigned char *buf, size_t len) { struct pcsc *dev = handle; DWORD n; LONG s; if (len > INT_MAX) { fido_log_debug("%s: len", __func__); return -1; } explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf)); dev->rx_len = 0; n = (DWORD)sizeof(dev->rx_buf); fido_log_xxd(buf, len, "%s: writing", __func__); if ((s = SCardTransmit(dev->h, &dev->req, buf, (DWORD)len, NULL, dev->rx_buf, &n)) != SCARD_S_SUCCESS) { fido_log_debug("%s: SCardTransmit 0x%lx", __func__, (long)s); explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf)); return -1; } dev->rx_len = (size_t)n; fido_log_xxd(dev->rx_buf, dev->rx_len, "%s: read", __func__); return (int)len; } int fido_pcsc_tx(fido_dev_t *d, uint8_t cmd, const u_char *buf, size_t count) { return fido_nfc_tx(d, cmd, buf, count); } int fido_pcsc_rx(fido_dev_t *d, uint8_t cmd, u_char *buf, size_t count, int ms) { return fido_nfc_rx(d, cmd, buf, count, ms); } bool fido_is_pcsc(const char *path) { return strncmp(path, FIDO_PCSC_PREFIX, strlen(FIDO_PCSC_PREFIX)) == 0; } int fido_dev_set_pcsc(fido_dev_t *d) { if (d->io_handle != NULL) { fido_log_debug("%s: device open", __func__); return -1; } d->io_own = true; d->io = (fido_dev_io_t) { fido_pcsc_open, fido_pcsc_close, fido_pcsc_read, fido_pcsc_write, }; d->transport = (fido_dev_transport_t) { fido_pcsc_rx, fido_pcsc_tx, }; return 0; } libfido2-1.15.0/src/pin.c000066400000000000000000000406631463251454000150260ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 > 63) { 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: case CTAP_CBOR_BIO_ENROLL: 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: case CTAP_CBOR_CRED_MGMT: 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 *msg = NULL; int msglen; int r; if ((aes_token = fido_blob_new()) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; } if ((r = cbor_parse_reply(msg, (size_t)msglen, 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); freezero(msg, FIDO_MAXMSG); 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 *msg; int msglen; int r; *retries = 0; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; } if ((r = cbor_parse_reply(msg, (size_t)msglen, retries, parse_pin_retry_count)) != FIDO_OK) { fido_log_debug("%s: parse_pin_retry_count", __func__); goto fail; } r = FIDO_OK; fail: freezero(msg, FIDO_MAXMSG); return (r); } 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 *msg; int msglen; int r; *retries = 0; if ((msg = malloc(FIDO_MAXMSG)) == NULL) { r = FIDO_ERR_INTERNAL; goto fail; } if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; } if ((r = cbor_parse_reply(msg, (size_t)msglen, retries, parse_uv_retry_count)) != FIDO_OK) { fido_log_debug("%s: parse_uv_retry_count", __func__); goto fail; } r = FIDO_OK; fail: freezero(msg, FIDO_MAXMSG); return (r); } 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.15.0/src/random.c000066400000000000000000000026711463251454000155150ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/reset.c000066400000000000000000000016331463251454000153540ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/rs1.c000066400000000000000000000026041463251454000147360ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include "fido.h" #if defined(__GNUC__) #define PRAGMA(s) _Pragma(s) #else #define PRAGMA(s) #endif static EVP_MD * rs1_get_EVP_MD(void) { PRAGMA("GCC diagnostic push") PRAGMA("GCC diagnostic ignored \"-Wcast-qual\"") return ((EVP_MD *)EVP_sha1()); PRAGMA("GCC diagnostic pop") } 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); return (ok); } libfido2-1.15.0/src/rs256.c000066400000000000000000000133251463251454000151140ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include "fido.h" #include "fido/rs256.h" #if OPENSSL_VERSION_NUMBER >= 0x30000000 #define get0_RSA(x) EVP_PKEY_get0_RSA((x)) #else #define get0_RSA(x) EVP_PKEY_get0((x)) #endif #if defined(__GNUC__) #define PRAGMA(s) _Pragma(s) #else #define PRAGMA(s) #endif static EVP_MD * rs256_get_EVP_MD(void) { PRAGMA("GCC diagnostic push") PRAGMA("GCC diagnostic ignored \"-Wcast-qual\"") return ((EVP_MD *)EVP_sha256()); PRAGMA("GCC diagnostic pop") } 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) { EVP_PKEY *pkey; if (len < sizeof(*pk)) return (FIDO_ERR_INVALID_ARGUMENT); memcpy(pk, ptr, sizeof(*pk)); if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) { fido_log_debug("%s: rs256_pk_to_EVP_PKEY", __func__); return (FIDO_ERR_INVALID_ARGUMENT); } EVP_PKEY_free(pkey); 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 (RSA_bits(rsa) != 2048) { fido_log_debug("%s: invalid key length", __func__); goto fail; } 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) { const RSA *rsa; if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA || (rsa = get0_RSA(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); 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.15.0/src/time.c000066400000000000000000000025761463251454000151770ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/src/touch.c000066400000000000000000000051451463251454000153560ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include "fido.h" 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); } libfido2-1.15.0/src/tpm.c000066400000000000000000000260531463251454000150350ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ /* * 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.15.0/src/types.c000066400000000000000000000027471463251454000154050ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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; oa->len = 0; } 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; } void fido_cert_array_free(fido_cert_array_t *ca) { for (size_t i = 0; i < ca->len; i++) free(ca->name[i]); free(ca->name); free(ca->value); ca->name = NULL; ca->value = NULL; ca->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.15.0/src/u2f.c000066400000000000000000000554341463251454000147360ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "fido.h" #include "fido/es256.h" #include "fallthrough.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 *reply = NULL; unsigned char challenge[SHA256_DIGEST_LENGTH]; unsigned char application[SHA256_DIGEST_LENGTH]; 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; } if ((reply = malloc(FIDO_MAXMSG)) == NULL) { fido_log_debug("%s: malloc", __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, FIDO_MAXMSG, 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); freezero(reply, FIDO_MAXMSG); 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 *reply = NULL; unsigned char challenge[SHA256_DIGEST_LENGTH]; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; 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 ((reply = malloc(FIDO_MAXMSG)) == NULL) { fido_log_debug("%s: malloc", __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, FIDO_MAXMSG, 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); freezero(reply, FIDO_MAXMSG); 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 *reply = NULL; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; 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; } if ((reply = malloc(FIDO_MAXMSG)) == NULL) { fido_log_debug("%s: malloc", __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, FIDO_MAXMSG, 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); freezero(reply, FIDO_MAXMSG); 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 = NULL; 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; } if ((reply = malloc(FIDO_MAXMSG)) == NULL) { fido_log_debug("%s: malloc", __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, FIDO_MAXMSG, 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); freezero(reply, FIDO_MAXMSG); 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 *reply = NULL; unsigned char clientdata_hash[SHA256_DIGEST_LENGTH]; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; 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 ((reply = malloc(FIDO_MAXMSG)) == NULL) { fido_log_debug("%s: malloc", __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, FIDO_MAXMSG, 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); freezero(reply, FIDO_MAXMSG); return (r); } int u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms) { unsigned char *reply; int reply_len; int r; if ((reply = malloc(FIDO_MAXMSG)) == NULL) { fido_log_debug("%s: malloc", __func__); r = FIDO_ERR_INTERNAL; goto out; } if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms)) < 2) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_OK; /* ignore */ goto out; } 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__); goto out; } *touched = 0; break; case SW_NO_ERROR: *touched = 1; break; default: fido_log_debug("%s: unexpected sw", __func__); r = FIDO_ERR_RX; goto out; } r = FIDO_OK; out: freezero(reply, FIDO_MAXMSG); return (r); } libfido2-1.15.0/src/util.c000066400000000000000000000011461463251454000152060ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include "fido.h" int fido_to_uint64(const char *str, int base, uint64_t *out) { char *ep; unsigned long long ull; errno = 0; ull = strtoull(str, &ep, base); 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; } libfido2-1.15.0/src/webauthn.h000066400000000000000000001330421463251454000160540ustar00rootroot00000000000000// 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 // Transports: // - WEBAUTHN_CTAP_TRANSPORT_USB // - WEBAUTHN_CTAP_TRANSPORT_NFC // - WEBAUTHN_CTAP_TRANSPORT_BLE // - WEBAUTHN_CTAP_TRANSPORT_INTERNAL #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 // - WEBAUTHN_CREDENTIAL_DETAILS : 1 // APIs: // - WebAuthNGetPlatformCredentialList // - WebAuthNFreePlatformCredentialList // - WebAuthNDeletePlatformCredential // #define WEBAUTHN_API_VERSION_5 5 // WEBAUTHN_API_VERSION_5 : Delta From WEBAUTHN_API_VERSION_4 // Data Structures and their sub versions: // - WEBAUTHN_CREDENTIAL_DETAILS : 2 // Extension Changes: // - Enabled LARGE_BLOB Support // #define WEBAUTHN_API_VERSION_6 6 // WEBAUTHN_API_VERSION_6 : Delta From WEBAUTHN_API_VERSION_5 // Data Structures and their sub versions: // - WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS : 6 // - WEBAUTHN_CREDENTIAL_ATTESTATION : 5 // - WEBAUTHN_ASSERTION : 4 // Transports: // - WEBAUTHN_CTAP_TRANSPORT_HYBRID #define WEBAUTHN_API_VERSION_7 7 // WEBAUTHN_API_VERSION_7 : Delta From WEBAUTHN_API_VERSION_6 // Data Structures and their sub versions: // - WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS : 7 // - WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS : 7 // - WEBAUTHN_CREDENTIAL_ATTESTATION : 6 // - WEBAUTHN_ASSERTION : 5 #define WEBAUTHN_API_CURRENT_VERSION WEBAUTHN_API_VERSION_7 //+------------------------------------------------------------------------------------------ // 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_HYBRID 0x00000020 #define WEBAUTHN_CTAP_TRANSPORT_FLAGS_MASK 0x0000003F #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; //+------------------------------------------------------------------------------------------ // Information about linked devices //------------------------------------------------------------------------------------------- #define CTAPCBOR_HYBRID_STORAGE_LINKED_DATA_VERSION_1 1 #define CTAPCBOR_HYBRID_STORAGE_LINKED_DATA_CURRENT_VERSION CTAPCBOR_HYBRID_STORAGE_LINKED_DATA_VERSION_1 typedef struct _CTAPCBOR_HYBRID_STORAGE_LINKED_DATA { // Version DWORD dwVersion; // Contact Id DWORD cbContactId; _Field_size_bytes_(cbContactId) PBYTE pbContactId; // Link Id DWORD cbLinkId; _Field_size_bytes_(cbLinkId) PBYTE pbLinkId; // Link secret DWORD cbLinkSecret; _Field_size_bytes_(cbLinkSecret) PBYTE pbLinkSecret; // Authenticator Public Key DWORD cbPublicKey; _Field_size_bytes_(cbPublicKey) PBYTE pbPublicKey; // Authenticator Name PCWSTR pwszAuthenticatorName; // Tunnel server domain WORD wEncodedTunnelServerDomain; } CTAPCBOR_HYBRID_STORAGE_LINKED_DATA, *PCTAPCBOR_HYBRID_STORAGE_LINKED_DATA; typedef const CTAPCBOR_HYBRID_STORAGE_LINKED_DATA *PCCTAPCBOR_HYBRID_STORAGE_LINKED_DATA; //+------------------------------------------------------------------------------------------ // Credential Information for WebAuthNGetPlatformCredentialList API //------------------------------------------------------------------------------------------- #define WEBAUTHN_CREDENTIAL_DETAILS_VERSION_1 1 #define WEBAUTHN_CREDENTIAL_DETAILS_VERSION_2 2 #define WEBAUTHN_CREDENTIAL_DETAILS_CURRENT_VERSION WEBAUTHN_CREDENTIAL_DETAILS_VERSION_2 typedef struct _WEBAUTHN_CREDENTIAL_DETAILS { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Size of pbCredentialID. DWORD cbCredentialID; _Field_size_bytes_(cbCredentialID) PBYTE pbCredentialID; // RP Info PWEBAUTHN_RP_ENTITY_INFORMATION pRpInformation; // User Info PWEBAUTHN_USER_ENTITY_INFORMATION pUserInformation; // Removable or not. BOOL bRemovable; // // The following fields have been added in WEBAUTHN_CREDENTIAL_DETAILS_VERSION_2 // // Backed Up or not. BOOL bBackedUp; } WEBAUTHN_CREDENTIAL_DETAILS, *PWEBAUTHN_CREDENTIAL_DETAILS; typedef const WEBAUTHN_CREDENTIAL_DETAILS *PCWEBAUTHN_CREDENTIAL_DETAILS; typedef struct _WEBAUTHN_CREDENTIAL_DETAILS_LIST { DWORD cCredentialDetails; _Field_size_(cCredentialDetails) PWEBAUTHN_CREDENTIAL_DETAILS *ppCredentialDetails; } WEBAUTHN_CREDENTIAL_DETAILS_LIST, *PWEBAUTHN_CREDENTIAL_DETAILS_LIST; typedef const WEBAUTHN_CREDENTIAL_DETAILS_LIST *PCWEBAUTHN_CREDENTIAL_DETAILS_LIST; #define WEBAUTHN_GET_CREDENTIALS_OPTIONS_VERSION_1 1 #define WEBAUTHN_GET_CREDENTIALS_OPTIONS_CURRENT_VERSION WEBAUTHN_GET_CREDENTIALS_OPTIONS_VERSION_1 typedef struct _WEBAUTHN_GET_CREDENTIALS_OPTIONS { // Version of this structure, to allow for modifications in the future. DWORD dwVersion; // Optional. LPCWSTR pwszRpId; // Optional. BrowserInPrivate Mode. Defaulting to FALSE. BOOL bBrowserInPrivateMode; } WEBAUTHN_GET_CREDENTIALS_OPTIONS, *PWEBAUTHN_GET_CREDENTIALS_OPTIONS; typedef const WEBAUTHN_GET_CREDENTIALS_OPTIONS *PCWEBAUTHN_GET_CREDENTIALS_OPTIONS; //+------------------------------------------------------------------------------------------ // PRF values. //------------------------------------------------------------------------------------------- #define WEBAUTHN_CTAP_ONE_HMAC_SECRET_LENGTH 32 // SALT values below by default are converted into RAW Hmac-Secret values as per PRF extension. // - SHA-256(UTF8Encode("WebAuthn PRF") || 0x00 || Value) // // Set WEBAUTHN_AUTHENTICATOR_HMAC_SECRET_VALUES_FLAG in dwFlags in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS, // if caller wants to provide RAW Hmac-Secret SALT values directly. In that case, // values if provided MUST be of WEBAUTHN_CTAP_ONE_HMAC_SECRET_LENGTH size. 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_VERSION_6 6 #define WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7 7 #define WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_CURRENT_VERSION WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7 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; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_6 // // Enable PRF BOOL bEnablePrf; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_7 // // Optional. Linked Device Connection Info. PCTAPCBOR_HYBRID_STORAGE_LINKED_DATA pLinkedDevice; // Size of pbJsonExt DWORD cbJsonExt; _Field_size_bytes_(cbJsonExt) PBYTE pbJsonExt; } 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_VERSION_7 7 #define WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_7 /* Information about flags. */ #define WEBAUTHN_AUTHENTICATOR_HMAC_SECRET_VALUES_FLAG 0x00100000 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; // Flags 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; // // The following fields have been added in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_7 // // Optional. Linked Device Connection Info. PCTAPCBOR_HYBRID_STORAGE_LINKED_DATA pLinkedDevice; // Optional. Allowlist MUST contain 1 credential applicable for Hybrid transport. BOOL bAutoFill; // Size of pbJsonExt DWORD cbJsonExt; _Field_size_bytes_(cbJsonExt) PBYTE pbJsonExt; } 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_VERSION_5 5 #define WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_6 6 #define WEBAUTHN_CREDENTIAL_ATTESTATION_CURRENT_VERSION WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_6 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; // // Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_5 // BOOL bPrfEnabled; // // Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_6 // DWORD cbUnsignedExtensionOutputs; _Field_size_bytes_(cbUnsignedExtensionOutputs) PBYTE pbUnsignedExtensionOutputs; } 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_VERSION_4 4 #define WEBAUTHN_ASSERTION_VERSION_5 5 #define WEBAUTHN_ASSERTION_CURRENT_VERSION WEBAUTHN_ASSERTION_VERSION_5 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; // // Following fields have been added in WEBAUTHN_ASSERTION_VERSION_4 // // 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_ASSERTION_VERSION_5 // DWORD cbUnsignedExtensionOutputs; _Field_size_bytes_(cbUnsignedExtensionOutputs) PBYTE pbUnsignedExtensionOutputs; } 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 NTE_NOT_FOUND when credentials are not found. HRESULT WINAPI WebAuthNGetPlatformCredentialList( _In_ PCWEBAUTHN_GET_CREDENTIALS_OPTIONS pGetCredentialsOptions, _Outptr_result_maybenull_ PWEBAUTHN_CREDENTIAL_DETAILS_LIST *ppCredentialDetailsList); void WINAPI WebAuthNFreePlatformCredentialList( _In_ PWEBAUTHN_CREDENTIAL_DETAILS_LIST pCredentialDetailsList); HRESULT WINAPI WebAuthNDeletePlatformCredential( _In_ DWORD cbCredentialId, _In_reads_bytes_(cbCredentialId) const BYTE *pbCredentialId ); // // 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.15.0/src/winhello.c000066400000000000000000000643171463251454000160630ustar00rootroot00000000000000/* * Copyright (c) 2021-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. * SPDX-License-Identifier: BSD-2-Clause */ #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 #ifndef NTE_USER_CANCELLED #define NTE_USER_CANCELLED _HRESULT_TYPEDEF_(0x80090036L) #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; wchar_t *appid; }; 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(TEXT("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; case __HRESULT_FROM_WIN32(ERROR_CANCELLED): case NTE_USER_CANCELLED: return FIDO_ERR_OPERATION_DENIED; 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_ES384: alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P384_WITH_SHA384; 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) { /* * NOTE: webauthn.dll ignores requests to enable hmac-secret * unless a discoverable credential is also requested. */ 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 pack_assert_ext(WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *out, const fido_assert_ext_t *in) { WEBAUTHN_HMAC_SECRET_SALT_VALUES *v; WEBAUTHN_HMAC_SECRET_SALT *s; if (in->mask == 0) { return 0; /* nothing to do */ } if (in->mask != FIDO_EXT_HMAC_SECRET) { fido_log_debug("%s: mask 0x%x", __func__, in->mask); return -1; } if (in->hmac_salt.ptr == NULL || in->hmac_salt.len != WEBAUTHN_CTAP_ONE_HMAC_SECRET_LENGTH) { fido_log_debug("%s: salt %p/%zu", __func__, (const void *)in->hmac_salt.ptr, in->hmac_salt.len); return -1; } if ((v = calloc(1, sizeof(*v))) == NULL || (s = calloc(1, sizeof(*s))) == NULL) { free(v); fido_log_debug("%s: calloc", __func__); return -1; } s->cbFirst = (DWORD)in->hmac_salt.len; s->pbFirst = in->hmac_salt.ptr; v->pGlobalHmacSalt = s; out->pHmacSecretSaltValues = v; out->dwFlags |= WEBAUTHN_AUTHENTICATOR_HMAC_SECRET_VALUES_FLAG; out->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6; return 0; } static int pack_appid(WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt, const char *id, wchar_t **appid) { if (id == NULL) return 0; /* nothing to do */ if ((opt->pbU2fAppId = calloc(1, sizeof(*opt->pbU2fAppId))) == NULL) { fido_log_debug("%s: calloc", __func__); return -1; } if ((*appid = to_utf16(id)) == NULL) { fido_log_debug("%s: to_utf16", __func__); return -1; } fido_log_debug("%s: using %s", __func__, id); opt->pwszU2fAppId = *appid; opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_2; return 0; } static void unpack_appid(fido_assert_t *assert, const WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt) { if (assert->appid == NULL || opt->pbU2fAppId == NULL) return; /* nothing to do */ if (*opt->pbU2fAppId == false) { fido_log_debug("%s: not used", __func__); return; } fido_log_debug("%s: %s -> %s", __func__, assert->rp_id, assert->appid); free(assert->rp_id); assert->rp_id = assert->appid; assert->appid = NULL; } static int unpack_assert_authdata(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { int r; if ((r = fido_assert_set_authdata_raw(assert, 0, wa->pbAuthenticatorData, 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 ((r = fido_assert_set_sig(assert, 0, wa->pbSignature, 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 (fido_blob_set(&assert->stmt[0].id, wa->Credential.pbId, 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 (fido_blob_set(&assert->stmt[0].user.id, wa->pbUserId, wa->cbUserId) < 0) { fido_log_debug("%s: fido_blob_set", __func__); return -1; } return 0; } static int unpack_hmac_secret(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { if (wa->dwVersion < WEBAUTHN_ASSERTION_VERSION_3) { fido_log_debug("%s: dwVersion %u", __func__, (unsigned)wa->dwVersion); return 0; /* proceed without hmac-secret */ } if (wa->pHmacSecret == NULL || wa->pHmacSecret->cbFirst == 0 || wa->pHmacSecret->pbFirst == NULL) { fido_log_debug("%s: hmac-secret absent", __func__); return 0; /* proceed without hmac-secret */ } if (wa->pHmacSecret->cbSecond != 0 || wa->pHmacSecret->pbSecond != NULL) { fido_log_debug("%s: 64-byte hmac-secret", __func__); return 0; /* proceed without hmac-secret */ } if (!fido_blob_is_empty(&assert->stmt[0].hmac_secret)) { fido_log_debug("%s: fido_blob_is_empty", __func__); return -1; } if (fido_blob_set(&assert->stmt[0].hmac_secret, wa->pHmacSecret->pbFirst, wa->pHmacSecret->cbFirst) < 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; } 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_appid(opt, assert->appid, &ctx->appid) < 0) { fido_log_debug("%s: pack_appid" , __func__); return FIDO_ERR_INTERNAL; } if (pack_credlist(&opt->CredentialList, &assert->allow_list) < 0) { fido_log_debug("%s: pack_credlist", __func__); return FIDO_ERR_INTERNAL; } if (pack_assert_ext(opt, &assert->ext) < 0) { fido_log_debug("%s: pack_assert_ext", __func__); return FIDO_ERR_UNSUPPORTED_EXTENSION; } 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 struct winhello_assert *ctx) { const WEBAUTHN_ASSERTION *wa = ctx->assert; const WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt = &ctx->opt; 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; } unpack_appid(assert, opt); 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; } if (assert->ext.mask & FIDO_EXT_HMAC_SECRET && unpack_hmac_secret(assert, wa) < 0) { fido_log_debug("%s: unpack_hmac_secret", __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 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) { fido_log_debug("%s: pbAttestationObject", __func__); goto fail; } if ((item = cbor_load(att->pbAttestationObject, att->cbAttestationObject, &cbor)) == NULL) { fido_log_debug("%s: cbor_load", __func__); goto fail; } if (cbor_decode_attobj(item, cred) != 0) { fido_log_debug("%s: cbor_decode_attobj", __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->appid); free(ctx->opt.CredentialList.pCredentials); if (ctx->opt.pHmacSecretSaltValues != NULL) free(ctx->opt.pHmacSecretSaltValues->pGlobalHmacSalt); free(ctx->opt.pHmacSecretSaltValues); 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)) != 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", "uv", "plat" }; (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.15.0/tools/000077500000000000000000000000001463251454000144345ustar00rootroot00000000000000libfido2-1.15.0/tools/CMakeLists.txt000066400000000000000000000034341463251454000172000ustar00rootroot00000000000000# Copyright (c) 2018-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. # SPDX-License-Identifier: BSD-2-Clause 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 "${EXTRA_CFLAGS}") 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} ) 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.15.0/tools/assert_get.c000066400000000000000000000166531463251454000167530ustar00rootroot00000000000000/* * Copyright (c) 2018-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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%s:\n", flags & FLAG_CD ? "" : " hash"); 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 (flags & FLAG_CD) r = fido_assert_set_clientdata(assert, cdh.ptr, cdh.len); else r = fido_assert_set_clientdata_hash(assert, cdh.ptr, cdh.len); if (r != 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 prompt[1024]; char pin[128]; 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:uvw")) != -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; case 'w': flags |= FLAG_CD; 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"); if (strlen(pin) < 4 || strlen(pin) > 63) { explicit_bzero(pin, sizeof(pin)); errx(1, "invalid PIN length"); } 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.15.0/tools/assert_verify.c000066400000000000000000000114641463251454000174730ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "../openbsd-compat/openbsd-compat.h" #include "extern.h" static fido_assert_t * prepare_assert(FILE *in_f, int flags) { fido_assert_t *assert = NULL; struct blob cdh; struct blob authdata; struct blob sig; char *rpid = NULL; int r; memset(&cdh, 0, sizeof(cdh)); memset(&authdata, 0, sizeof(authdata)); memset(&sig, 0, sizeof(sig)); r = base64_read(in_f, &cdh); r |= string_read(in_f, &rpid); r |= base64_read(in_f, &authdata); r |= base64_read(in_f, &sig); if (r < 0) errx(1, "input error"); if (flags & FLAG_DEBUG) { fprintf(stderr, "client data hash:\n"); xxd(cdh.ptr, cdh.len); fprintf(stderr, "relying party id: %s\n", rpid); fprintf(stderr, "authenticator data:\n"); xxd(authdata.ptr, authdata.len); fprintf(stderr, "signature:\n"); xxd(sig.ptr, sig.len); } if ((assert = fido_assert_new()) == NULL) errx(1, "fido_assert_new"); if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK) errx(1, "fido_assert_count: %s", fido_strerr(r)); if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr, cdh.len)) != FIDO_OK || (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK || (r = fido_assert_set_authdata(assert, 0, authdata.ptr, authdata.len)) != FIDO_OK || (r = fido_assert_set_sig(assert, 0, sig.ptr, sig.len)) != FIDO_OK) errx(1, "fido_assert_set: %s", fido_strerr(r)); if (flags & FLAG_UP) { if ((r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK) errx(1, "fido_assert_set_up: %s", fido_strerr(r)); } if (flags & FLAG_UV) { if ((r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) errx(1, "fido_assert_set_uv: %s", fido_strerr(r)); } if (flags & FLAG_HMAC) { if ((r = fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET)) != FIDO_OK) errx(1, "fido_assert_set_extensions: %s", fido_strerr(r)); } free(cdh.ptr); free(authdata.ptr); free(sig.ptr); free(rpid); return (assert); } static void * load_pubkey(int type, const char *file) { EC_KEY *ec = NULL; RSA *rsa = NULL; EVP_PKEY *eddsa = NULL; es256_pk_t *es256_pk = NULL; es384_pk_t *es384_pk = NULL; rs256_pk_t *rs256_pk = NULL; eddsa_pk_t *eddsa_pk = NULL; void *pk = NULL; switch (type) { case 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); break; case COSE_ES384: if ((ec = read_ec_pubkey(file)) == NULL) errx(1, "read_ec_pubkey"); if ((es384_pk = es384_pk_new()) == NULL) errx(1, "es384_pk_new"); if (es384_pk_from_EC_KEY(es384_pk, ec) != FIDO_OK) errx(1, "es384_pk_from_EC_KEY"); pk = es384_pk; EC_KEY_free(ec); break; case 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); break; case 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); break; default: errx(1, "invalid type %d", type); } 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.15.0/tools/base64.c000066400000000000000000000047541463251454000156760ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/tools/bio.c000066400000000000000000000145201463251454000153530ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/tools/config.c000066400000000000000000000076441463251454000160600ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/tools/cred_make.c000066400000000000000000000134221463251454000165140ustar00rootroot00000000000000/* * Copyright (c) 2018-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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%s:\n", flags & FLAG_CD ? "" : " hash"); 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 (flags & FLAG_CD) r = fido_cred_set_clientdata(cred, cdh.ptr, cdh.len); else r = fido_cred_set_clientdata_hash(cred, cdh.ptr, cdh.len); if (r != FIDO_OK || (r = fido_cred_set_type(cred, type)) != 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[128]; 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:qruvw")) != -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; case 'w': flags |= FLAG_CD; 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"); if (strlen(pin) < 4 || strlen(pin) > 63) { explicit_bzero(pin, sizeof(pin)); errx(1, "invalid PIN length"); } 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.15.0/tools/cred_verify.c000066400000000000000000000100541463251454000171010ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/tools/credman.c000066400000000000000000000164631463251454000162230ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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; int r = -1; 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"); goto out; } 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); r = 0; out: free(user_id); free(id); return r; } 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.15.0/tools/extern.h000066400000000000000000000063511463251454000161170ustar00rootroot00000000000000/* * Copyright (c) 2018-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ #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 0x001 #define FLAG_QUIET 0x002 #define FLAG_RK 0x004 #define FLAG_UV 0x008 #define FLAG_U2F 0x010 #define FLAG_HMAC 0x020 #define FLAG_UP 0x040 #define FLAG_LARGEBLOB 0x080 #define FLAG_CD 0x100 #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_es256_pubkey(FILE *, const void *, size_t); int write_es384_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.15.0/tools/fido2-assert.c000066400000000000000000000023041463251454000171010ustar00rootroot00000000000000/* * Copyright (c) 2018-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ /* * 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 [-bdhpruvw] [-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.15.0/tools/fido2-attach.sh000077500000000000000000000005161463251454000172420ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause DEV="" while [ -z "${DEV}" ]; do sleep .5 DEV="$(fido2-token -L | sed 's/^\(.*\): .*$/\1/;q')" done printf '%s\n' "${DEV}" libfido2-1.15.0/tools/fido2-cred.c000066400000000000000000000022311463251454000165140ustar00rootroot00000000000000/* * Copyright (c) 2018-2023 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. * SPDX-License-Identifier: BSD-2-Clause */ /* * 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 [-bdhqruvw] [-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.15.0/tools/fido2-detach.sh000077500000000000000000000005441463251454000172270ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause 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.15.0/tools/fido2-token.c000066400000000000000000000043001463251454000167160ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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.15.0/tools/fido2-unprot.sh000077500000000000000000000056231463251454000173310ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause 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.15.0/tools/include_check.sh000077500000000000000000000007771463251454000175660ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause 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.15.0/tools/largeblob.c000066400000000000000000000341571463251454000165430ustar00rootroot00000000000000/* * Copyright (c) 2020-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. * SPDX-License-Identifier: BSD-2-Clause */ #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" #define BOUND (1024UL * 1024UL) 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 try_decompress(const struct blob *in, uint64_t origsiz, int wbits) { struct blob out; z_stream zs; u_int ilen, olen; int ok = -1; memset(&zs, 0, sizeof(zs)); memset(&out, 0, sizeof(out)); if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) return -1; if (origsiz > SIZE_MAX || origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) return -1; if (inflateInit2(&zs, wbits) != Z_OK) return -1; if ((out.ptr = calloc(1, olen)) == NULL) goto fail; out.len = olen; zs.next_in = in->ptr; zs.avail_in = ilen; zs.next_out = out.ptr; zs.avail_out = olen; if (inflate(&zs, Z_FINISH) != Z_STREAM_END) goto fail; if (zs.avail_out != 0) goto fail; ok = 0; fail: if (inflateEnd(&zs) != Z_OK) ok = -1; freezero(out.ptr, out.len); return ok; } static int decompress(const struct blob *plaintext, uint64_t origsiz) { if (try_decompress(plaintext, origsiz, MAX_WBITS) == 0) /* rfc1950 */ return 0; return try_decompress(plaintext, origsiz, -MAX_WBITS); /* rfc1951 */ } 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.15.0/tools/pin.c000066400000000000000000000061731463251454000153750ustar00rootroot00000000000000/* * 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. * SPDX-License-Identifier: BSD-2-Clause */ #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[128]; char pin2[128]; 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 (strlen(pin1) < 4 || strlen(pin1) > 63) { fprintf(stderr, "invalid PIN length\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[128]; char pin1[128]; char pin2[128]; 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; } if (strlen(pin0) < 4 || strlen(pin0) > 63) { warnx("invalid PIN length"); 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 (strlen(pin1) < 4 || strlen(pin1) > 63) { fprintf(stderr, "invalid PIN length\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.15.0/tools/test.sh000077500000000000000000000355351463251454000157650ustar00rootroot00000000000000#!/bin/sh -ex # Copyright (c) 2021-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. # SPDX-License-Identifier: BSD-2-Clause # 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" TYPE="es256" #TYPE="es384" #TYPE="eddsa" 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}" "${TYPE}" > "$3" < cred_param } verify_cred() { fido2-cred -V $1 "${TYPE}" > 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" "${TYPE}" < "$3" } dd if=/dev/urandom bs=32 count=1 | base64 > hmac-salt # u2f if [ "x${TYPE}" = "xes256" ]; then 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 fi # 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 if [ "x${TYPE}" = "xes256" ]; then 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 fi # 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.15.0/tools/token.c000066400000000000000000000336651463251454000157350ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #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_cert_array(const char *label, char * const *name, const uint64_t *value, size_t len) { if (len == 0) return; printf("%s: ", label); for (size_t i = 0; i < len; i++) printf("%s%s %llu", i > 0 ? ", " : "", name[i], (unsigned long long)value[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_ES256: cose = "es256"; break; case COSE_ES384: cose = "es384"; break; case COSE_RS256: cose = "rs256"; break; case COSE_EDDSA: cose = "eddsa"; 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_maxcredblob(uint64_t maxcredblob) { printf("maxcredblob: %d\n", (int)maxcredblob); } static void print_maxcredidlen(uint64_t maxcredidlen) { printf("maxcredlen: %d\n", (int)maxcredidlen); } static void print_maxlargeblob(uint64_t maxlargeblob) { printf("maxlargeblob: %d\n", (int)maxlargeblob); } static void print_maxrpid_minpinlen(uint64_t maxrpid) { if (maxrpid > 0) printf("maxrpids in minpinlen: %d\n", (int)maxrpid); } static void print_minpinlen(uint64_t minpinlen) { if (minpinlen > 0) printf("minpinlen: %d\n", (int)minpinlen); } static void print_uv_attempts(uint64_t uv_attempts) { if (uv_attempts > 0) printf("platform uv attempt(s): %d\n", (int)uv_attempts); } static void print_uv_modality(uint64_t uv_modality) { uint64_t mode; bool printed = false; if (uv_modality == 0) return; printf("uv modality: 0x%x (", (int)uv_modality); for (size_t i = 0; i < 64; i++) { mode = 1ULL << i; if ((uv_modality & mode) == 0) continue; if (printed) printf(", "); switch (mode) { case FIDO_UV_MODE_TUP: printf("test of user presence"); break; case FIDO_UV_MODE_FP: printf("fingerprint check"); break; case FIDO_UV_MODE_PIN: printf("pin check"); break; case FIDO_UV_MODE_VOICE: printf("voice recognition"); break; case FIDO_UV_MODE_FACE: printf("face recognition"); break; case FIDO_UV_MODE_LOCATION: printf("location check"); break; case FIDO_UV_MODE_EYE: printf("eyeprint check"); break; case FIDO_UV_MODE_DRAWN: printf("drawn pattern check"); break; case FIDO_UV_MODE_HAND: printf("handprint verification"); break; case FIDO_UV_MODE_NONE: printf("none"); break; case FIDO_UV_MODE_ALL: printf("all required"); break; case FIDO_UV_MODE_EXT_PIN: printf("external pin"); break; case FIDO_UV_MODE_EXT_DRAWN: printf("external drawn pattern check"); break; default: printf("unknown 0x%llx", (unsigned long long)mode); break; } printed = true; } printf(")\n"); } static void print_rk_remaining(int64_t rk_remaining) { if (rk_remaining != -1) printf("remaining rk(s): %d\n", (int)rk_remaining); } 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 certifications */ print_cert_array("certifications", fido_cbor_info_certs_name_ptr(ci), fido_cbor_info_certs_value_ptr(ci), fido_cbor_info_certs_len(ci)); /* print firmware version */ print_fwversion(fido_cbor_info_fwversion(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 maximum length of credBlob */ print_maxcredblob(fido_cbor_info_maxcredbloblen(ci)); /* print maximum length of serialized largeBlob array */ print_maxlargeblob(fido_cbor_info_maxlargeblob(ci)); /* print maximum number of RP IDs in fido_dev_set_pin_minlen_rpid() */ print_maxrpid_minpinlen(fido_cbor_info_maxrpid_minpinlen(ci)); /* print estimated number of resident credentials */ print_rk_remaining(fido_cbor_info_rk_remaining(ci)); /* print minimum pin length */ print_minpinlen(fido_cbor_info_minpinlen(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); printf("pin change required: %s\n", fido_cbor_info_new_pin_required(ci) ? "true" : "false"); if (fido_dev_get_uv_retry_count(dev, &retrycnt) != FIDO_OK) printf("uv retries: undefined\n"); else printf("uv retries: %d\n", retrycnt); /* print platform uv attempts */ print_uv_attempts(fido_cbor_info_uv_attempts(ci)); /* print supported uv mechanisms */ print_uv_modality(fido_cbor_info_uv_modality(ci)); 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.15.0/tools/util.c000066400000000000000000000247661463251454000155740ustar00rootroot00000000000000/* * Copyright (c) 2018-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. * SPDX-License-Identifier: BSD-2-Clause */ #include #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_es256_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); } int write_es384_pubkey(FILE *f, const void *ptr, size_t len) { EVP_PKEY *pkey = NULL; es384_pk_t *pk = NULL; int ok = -1; if ((pk = es384_pk_new()) == NULL) { warnx("es384_pk_new"); goto fail; } if (es384_pk_from_ptr(pk, ptr, len) != FIDO_OK) { warnx("es384_pk_from_ptr"); goto fail; } if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL) { warnx("es384_pk_to_EVP_PKEY"); goto fail; } if (PEM_write_PUBKEY(f, pkey) == 0) { warnx("PEM_write_PUBKEY"); goto fail; } ok = 0; fail: es384_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); switch (type) { case COSE_ES256: write_es256_pubkey(out_f, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)); break; case COSE_ES384: write_es384_pubkey(out_f, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)); break; case COSE_RS256: write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)); break; case COSE_EDDSA: write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred)); break; default: 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, "es384") == 0) *type = COSE_ES384; 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_ES256: return ("es256"); case COSE_ES384: return ("es384"); case COSE_RS256: return ("rs256"); case COSE_EDDSA: return ("eddsa"); 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.15.0/udev/000077500000000000000000000000001463251454000142375ustar00rootroot00000000000000libfido2-1.15.0/udev/70-u2f.rules000066400000000000000000000363331463251454000162430ustar00rootroot00000000000000# Copyright (c) 2020 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. # # SPDX-License-Identifier: BSD-2-Clause # 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" # SoloKeys v2 by pid.codes KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="beee", 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 HID+U2F by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="1005", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano S HID+WEBUSB by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="1011", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano S HID+U2F+WEBUSB by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="1015", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano X HID+U2F by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="4005", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano X HID+WEBUSB by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="4011", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano X HID+U2F+WEBUSB by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="4015", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano S+ HID+U2F by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="5005", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano S+ HID+WEBUSB by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="5011", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Nano S+ HID+U2F+WEBUSB by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="5015", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Stax HID+U2F by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="6005", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger Stax HID+WEBUSB by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="6011", TAG+="uaccess", GROUP="plugdev", MODE="0660" # Ledger stax HID+U2F+WEBUSB by LEDGER KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="6015", 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 G310H/G320H by eWBM Co., Ltd. KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="4a2a", 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.15.0/udev/CMakeLists.txt000066400000000000000000000004321463251454000167760ustar00rootroot00000000000000# 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. # SPDX-License-Identifier: BSD-2-Clause if(UDEV_RULES_DIR) install(FILES 70-u2f.rules DESTINATION ${UDEV_RULES_DIR}) endif() libfido2-1.15.0/udev/check.sh000077500000000000000000000015021463251454000156510ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause 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.15.0/udev/fidodevs000066400000000000000000000112371463251454000157710ustar00rootroot00000000000000# 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. # SPDX-License-Identifier: BSD-2-Clause # 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 PIDCODES 0xbeee SoloKeys v2 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 0x1005 Ledger Nano S HID+U2F product LEDGER 0x1011 Ledger Nano S HID+WEBUSB product LEDGER 0x1015 Ledger Nano S HID+U2F+WEBUSB product LEDGER 0x4005 Ledger Nano X HID+U2F product LEDGER 0x4011 Ledger Nano X HID+WEBUSB product LEDGER 0x4015 Ledger Nano X HID+U2F+WEBUSB product LEDGER 0x5005 Ledger Nano S+ HID+U2F product LEDGER 0x5011 Ledger Nano S+ HID+WEBUSB product LEDGER 0x5015 Ledger Nano S+ HID+U2F+WEBUSB product LEDGER 0x6005 Ledger Stax HID+U2F product LEDGER 0x6011 Ledger Stax HID+WEBUSB product LEDGER 0x6015 Ledger stax HID+U2F+WEBUSB product HYPERSECU 0x0880 Hypersecu HyperFIDO product EWBM 0x4a1a TrustKey Solutions FIDO2 G310 product EWBM 0x4a2a TrustKey Solutions FIDO2 G310H/G320H 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.15.0/udev/genrules.awk000077500000000000000000000051671463251454000166030ustar00rootroot00000000000000#!/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. # SPDX-License-Identifier: BSD-2-Clause NR == 1 { print "# Copyright (c) 2020 Yubico AB. All rights reserved." print "#" print "# Redistribution and use in source and binary forms, with or without" print "# modification, are permitted provided that the following conditions are" print "# met:" print "# " print "# 1. Redistributions of source code must retain the above copyright" print "# notice, this list of conditions and the following disclaimer." print "# 2. Redistributions in binary form must reproduce the above copyright" print "# notice, this list of conditions and the following disclaimer in" print "# the documentation and/or other materials provided with the" print "# distribution." print "# " print "# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS" print "# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT" print "# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR" print "# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT" print "# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL," print "# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT" print "# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE," print "# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY" print "# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" print "# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" print "# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." print "#" print "# SPDX-License-Identifier: BSD-2-Clause" print "" print "# This file is automatically generated, and should be used with udev 188" print "# 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.15.0/windows/000077500000000000000000000000001463251454000147665ustar00rootroot00000000000000libfido2-1.15.0/windows/build.ps1000066400000000000000000000175501463251454000165220ustar00rootroot00000000000000# Copyright (c) 2021-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. # SPDX-License-Identifier: BSD-2-Clause 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 GNUPGHOME with an empty common.conf to disable use-keyboxd. # Recent default is to enable keyboxd which in turn ignores --keyring # arguments. $GpgHome = "${BUILD}\.gnupg" New-Item -Type Directory "${GpgHome}" -Force New-Item -Type File "${GpgHome}\common.conf" -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 --homedir ${GpgHome} --list-keys & $GPG --homedir ${GpgHome} --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 ` -DLIBRESSL_APPS=OFF -DCMAKE_C_FLAGS_DEBUG="${CFLAGS_DEBUG}" ` -DCMAKE_C_FLAGS_RELEASE="${CFLAGS_RELEASE}" ` -DCMAKE_MSVC_RUNTIME_LIBRARY="${CMAKE_MSVC_RUNTIME_LIBRARY}" ` -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} /wd4703" ` -DCMAKE_C_FLAGS_RELEASE="${CFLAGS_RELEASE} /wd4703" ` -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_MSVC_RUNTIME_LIBRARY="${CMAKE_MSVC_RUNTIME_LIBRARY}" ` -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 various names. if ("${Type}" -eq "Dynamic") { ((Get-ChildItem -Path "${PREFIX}/lib") -Match "zlib[d]?.lib") | Copy-Item -Destination "${PREFIX}/lib/zlib1.lib" -Force ((Get-ChildItem -Path "${PREFIX}/bin") -Match "zlibd1.dll") | Copy-Item -Destination "${PREFIX}/bin/zlib1.dll" -Force } else { ((Get-ChildItem -Path "${PREFIX}/lib") -Match "zlibstatic[d]?.lib") | Copy-item -Destination "${PREFIX}/lib/zlib1.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" ` -DCRYPTO_LIBRARIES="${CRYPTO_LIBRARIES}" ` -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 regress --verbose; ` ExitOnError & $CMake --build . --config ${Config} --target install --verbose; ` ExitOnError # Copy DLLs. if ("${SHARED}" -eq "ON") { "cbor.dll", "${CRYPTO_LIBRARIES}.dll", "zlib1.dll" | ` %{ Copy-Item "${PREFIX}\bin\$_" ` -Destination "examples\${Config}" } } } catch { throw "Failed to build libfido2" } finally { Pop-Location } libfido2-1.15.0/windows/const.ps1000066400000000000000000000040221463251454000165370ustar00rootroot00000000000000# Copyright (c) 2021-2024 Yubico AB. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # SPDX-License-Identifier: BSD-2-Clause # LibreSSL coordinates. New-Variable -Name 'LIBRESSL_URL' ` -Value 'https://ftp.openbsd.org/pub/OpenBSD/LibreSSL' ` -Option Constant New-Variable -Name 'LIBRESSL' -Value 'libressl-3.9.2' -Option Constant New-Variable -Name 'CRYPTO_LIBRARIES' -Value 'crypto' -Option Constant # libcbor coordinates. New-Variable -Name 'LIBCBOR' -Value 'libcbor-0.11.0' -Option Constant New-Variable -Name 'LIBCBOR_BRANCH' -Value 'v0.11.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.3.1' -Option Constant New-Variable -Name 'ZLIB_BRANCH' -Value 'v1.3.1' -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 New-Variable -Name 'CMAKE_MSVC_RUNTIME_LIBRARY' -Option Constant ` -Value 'MultiThreaded$<$:Debug>DLL' } else { New-Variable -Name 'RUNTIME' -Value '/MT' -Option Constant New-Variable -Name 'SHARED' -Value 'OFF' -Option Constant New-Variable -Name 'CMAKE_MSVC_RUNTIME_LIBRARY' -Option Constant ` -Value 'MultiThreaded$<$:Debug>' } 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.15.0/windows/cygwin.gpg000077500000000000000000000042211463251454000167670ustar00rootroot00000000000000HRon}!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.15.0/windows/cygwin.ps1000077500000000000000000000044651463251454000167270ustar00rootroot00000000000000# 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. # SPDX-License-Identifier: BSD-2-Clause 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 # Create GNUPGHOME with an empty common.conf to disable use-keyboxd. # Recent default is to enable keyboxd which in turn ignores --keyring # arguments. $GpgHome = "${Cygwin}\.gnupg" New-Item -Type Directory "${GpgHome}" -Force New-Item -Type File "${GpgHome}\common.conf" -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 --homedir ${GpgHome} --list-keys & $GPG --homedir ${GpgHome} --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}" make -C "build-${Config}" regress libfido2-1.15.0/windows/libressl.gpg000066400000000000000000000400511463251454000173040ustar00rootroot00000000000000 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+)