Import libfido2 1.7.0; changes:

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.
This commit is contained in:
christos 2021-06-17 00:38:06 +00:00
parent 38862c874c
commit 95dbdf3206
137 changed files with 10750 additions and 3003 deletions

View File

@ -7,6 +7,10 @@ cmake_policy(SET CMP0025 NEW)
project(libfido2 C)
cmake_minimum_required(VERSION 3.0)
# Set PIE flags for POSITION_INDEPENDENT_CODE targets, added in CMake 3.14.
if(POLICY CMP0083)
cmake_policy(SET CMP0083 NEW)
endif()
include(CheckCCompilerFlag)
include(CheckFunctionExists)
@ -15,16 +19,29 @@ 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_COLOR_MAKEFILE off)
set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_COLOR_MAKEFILE OFF)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(FIDO_MAJOR "1")
set(FIDO_MINOR "5")
set(FIDO_MINOR "7")
set(FIDO_PATCH "0")
set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH})
option(BUILD_EXAMPLES "Build example programs" ON)
option(BUILD_MANPAGES "Build man pages" ON)
option(BUILD_SHARED_LIBS "Build the shared library" ON)
option(BUILD_STATIC_LIBS "Build the static library" ON)
option(BUILD_TOOLS "Build tool programs" ON)
option(FUZZ "Enable fuzzing instrumentation" OFF)
option(LIBFUZZER "Build libfuzzer harnesses" OFF)
option(USE_HIDAPI "Use hidapi as the HID backend" OFF)
option(NFC_LINUX "Experimental NFC support on Linux" OFF)
add_definitions(-D_FIDO_MAJOR=${FIDO_MAJOR})
add_definitions(-D_FIDO_MINOR=${FIDO_MINOR})
add_definitions(-D_FIDO_PATCH=${FIDO_PATCH})
@ -43,14 +60,116 @@ if(APPLE)
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()
if(NOT MSVC)
set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_POSIX_C_SOURCE=200809L")
set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_BSD_SOURCE")
if(APPLE)
set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_DARWIN_C_SOURCE")
set(FIDO_CFLAGS "${FIDO_CFLAGS} -D__STDC_WANT_LIB_EXT1__=1")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_GNU_SOURCE")
set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_DEFAULT_SOURCE")
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(FIDO_CFLAGS "${FIDO_CFLAGS} -D__BSD_VISIBLE=1")
endif()
set(FIDO_CFLAGS "${FIDO_CFLAGS} -std=c99")
set(CMAKE_C_FLAGS "${FIDO_CFLAGS} ${CMAKE_C_FLAGS}")
endif()
check_c_compiler_flag("-Wshorten-64-to-32" HAVE_SHORTEN_64_TO_32)
check_c_compiler_flag("-fstack-protector-all" HAVE_STACK_PROTECTOR_ALL)
check_include_files(cbor.h HAVE_CBOR_H)
check_include_files(endian.h HAVE_ENDIAN_H)
check_include_files(err.h HAVE_ERR_H)
check_include_files(openssl/opensslv.h HAVE_OPENSSLV_H)
check_include_files(signal.h HAVE_SIGNAL_H)
check_include_files(sys/random.h HAVE_SYS_RANDOM_H)
check_include_files(unistd.h HAVE_UNISTD_H)
check_symbol_exists(arc4random_buf stdlib.h HAVE_ARC4RANDOM_BUF)
check_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME)
check_symbol_exists(explicit_bzero string.h HAVE_EXPLICIT_BZERO)
check_symbol_exists(freezero stdlib.h HAVE_FREEZERO)
check_symbol_exists(getline stdio.h HAVE_GETLINE)
check_symbol_exists(getopt unistd.h HAVE_GETOPT)
check_symbol_exists(getpagesize unistd.h HAVE_GETPAGESIZE)
check_symbol_exists(getrandom sys/random.h HAVE_GETRANDOM)
check_symbol_exists(memset_s string.h HAVE_MEMSET_S)
check_symbol_exists(readpassphrase readpassphrase.h HAVE_READPASSPHRASE)
check_symbol_exists(recallocarray stdlib.h HAVE_RECALLOCARRAY)
check_symbol_exists(sigaction signal.h HAVE_SIGACTION)
check_symbol_exists(strlcat string.h HAVE_STRLCAT)
check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
check_symbol_exists(sysconf unistd.h HAVE_SYSCONF)
check_symbol_exists(timespecsub sys/time.h HAVE_TIMESPECSUB)
check_symbol_exists(timingsafe_bcmp string.h HAVE_TIMINGSAFE_BCMP)
set(CMAKE_EXTRA_INCLUDE_FILES signal.h)
check_type_size("sig_atomic_t" HAVE_SIG_ATOMIC_T)
set(CMAKE_EXTRA_INCLUDE_FILES)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
try_compile(HAVE_POSIX_IOCTL
"${CMAKE_CURRENT_BINARY_DIR}/posix_ioctl_check.o"
"${CMAKE_CURRENT_SOURCE_DIR}/openbsd-compat/posix_ioctl_check.c"
COMPILE_DEFINITIONS "-Werror -Woverflow -Wsign-conversion")
list(APPEND CHECK_VARIABLES
HAVE_ARC4RANDOM_BUF
HAVE_CBOR_H
HAVE_CLOCK_GETTIME
HAVE_ENDIAN_H
HAVE_ERR_H
HAVE_FREEZERO
HAVE_GETLINE
HAVE_GETOPT
HAVE_GETPAGESIZE
HAVE_GETRANDOM
HAVE_MEMSET_S
HAVE_OPENSSLV_H
HAVE_POSIX_IOCTL
HAVE_READPASSPHRASE
HAVE_RECALLOCARRAY
HAVE_SIGACTION
HAVE_SIGNAL_H
HAVE_STRLCAT
HAVE_STRLCPY
HAVE_SYSCONF
HAVE_SYS_RANDOM_H
HAVE_TIMESPECSUB
HAVE_TIMINGSAFE_BCMP
HAVE_UNISTD_H
)
foreach(v ${CHECK_VARIABLES})
if (${v})
add_definitions(-D${v})
endif()
endforeach()
if(HAVE_EXPLICIT_BZERO AND NOT LIBFUZZER)
add_definitions(-DHAVE_EXPLICIT_BZERO)
endif()
if(HAVE_SIGACTION AND (NOT HAVE_SIG_ATOMIC_T STREQUAL ""))
add_definitions(-DSIGNAL_EXAMPLE)
endif()
if(UNIX)
add_definitions(-DHAVE_DEV_URANDOM)
endif()
if(MSVC)
if((NOT CBOR_INCLUDE_DIRS) OR (NOT CBOR_LIBRARY_DIRS) OR
(NOT CRYPTO_INCLUDE_DIRS) OR (NOT CRYPTO_LIBRARY_DIRS))
(NOT CRYPTO_INCLUDE_DIRS) OR (NOT CRYPTO_LIBRARY_DIRS) OR
(NOT ZLIB_INCLUDE_DIRS) OR (NOT ZLIB_LIBRARY_DIRS))
message(FATAL_ERROR "please provide definitions for "
"{CBOR,CRYPTO}_{INCLUDE,LIBRARY}_DIRS when building "
"{CBOR,CRYPTO,ZLIB}_{INCLUDE,LIBRARY}_DIRS when building "
"under msvc")
endif()
set(CBOR_LIBRARIES cbor)
set(ZLIB_LIBRARIES zlib)
set(CRYPTO_LIBRARIES crypto-46)
set(MSVC_DISABLED_WARNINGS_LIST
"C4200" # nonstandard extension used: zero-sized array in
@ -67,45 +186,40 @@ if(MSVC)
${MSVC_DISABLED_WARNINGS_LIST})
string(REGEX REPLACE "[/-]W[1234][ ]?" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MP -W4 -WX ${MSVC_DISABLED_WARNINGS_STR}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Z7")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Z7 /guard:cf /sdl /RTCcsu")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi /guard:cf /sdl")
else()
include(FindPkgConfig)
pkg_search_module(CBOR libcbor)
pkg_search_module(CRYPTO libcrypto)
pkg_search_module(ZLIB zlib)
# XXX workaround libcbor's missing .pc file
if(NOT CBOR_FOUND)
check_include_files(cbor.h HAVE_CBOR_H)
if(NOT HAVE_CBOR_H)
message(FATAL_ERROR "could not find cbor header files")
endif()
set(CBOR_LIBRARIES "cbor")
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()
# XXX workaround libcrypto's missing .pc file
if(NOT CRYPTO_FOUND)
check_include_files(openssl/opensslv.h HAVE_OPENSSLV_H)
if(NOT HAVE_OPENSSLV_H)
message(FATAL_ERROR "could not find crypto header files")
endif()
set(CRYPTO_LIBRARIES "crypto")
endif()
set(CBOR_LIBRARIES "cbor")
set(CRYPTO_LIBRARIES "crypto")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
pkg_search_module(UDEV libudev REQUIRED)
set(UDEV_NAME "udev")
include_directories(${UDEV_INCLUDE_DIRS})
link_directories(${UDEV_LIBRARY_DIRS})
# Define be32toh().
add_definitions(-D_GNU_SOURCE)
# If using hidapi, use hidapi-hidraw.
set(HIDAPI_SUFFIX -hidraw)
# Look for clock_gettime in librt.
check_library_exists(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME)
if(HAVE_CLOCK_GETTIME)
set(BASE_LIBRARIES ${BASE_LIBRARIES} rt)
add_definitions(-DHAVE_CLOCK_GETTIME)
if(NOT HAVE_CLOCK_GETTIME)
# Look for clock_gettime in librt.
check_library_exists(rt clock_gettime "time.h"
HAVE_CLOCK_GETTIME)
if (HAVE_CLOCK_GETTIME)
add_definitions(-DHAVE_CLOCK_GETTIME)
set(BASE_LIBRARIES ${BASE_LIBRARIES} rt)
endif()
endif()
endif()
@ -113,57 +227,56 @@ else()
# MinGW is stuck with a flavour of C89.
add_definitions(-DFIDO_NO_DIAGNOSTIC)
add_definitions(-DWC_ERR_INVALID_CHARS=0x80)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter")
add_compile_options(-Wno-unused-parameter)
endif()
if(USE_HIDAPI)
add_definitions(-DUSE_HIDAPI)
pkg_search_module(HIDAPI hidapi${HIDAPI_SUFFIX} REQUIRED)
if(HIDAPI_FOUND)
include_directories(${HIDAPI_INCLUDE_DIRS})
link_directories(${HIDAPI_LIBRARY_DIRS})
set(HIDAPI_LIBRARIES hidapi${HIDAPI_SUFFIX})
endif()
set(HIDAPI_LIBRARIES hidapi${HIDAPI_SUFFIX})
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wwrite-strings")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-prototypes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wbad-function-cast")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors")
check_c_compiler_flag("-fstack-protector-all" HAVE_STACK_PROTECTOR_ALL)
if(FUZZ)
set(NFC_LINUX ON)
endif()
if(NFC_LINUX)
add_definitions(-DNFC_LINUX)
endif()
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-Werror)
add_compile_options(-Wshadow)
add_compile_options(-Wcast-qual)
add_compile_options(-Wwrite-strings)
add_compile_options(-Wmissing-prototypes)
add_compile_options(-Wbad-function-cast)
add_compile_options(-pedantic)
add_compile_options(-pedantic-errors)
if(HAVE_SHORTEN_64_TO_32)
add_compile_options(-Wshorten-64-to-32)
endif()
if(HAVE_STACK_PROTECTOR_ALL)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-all")
add_compile_options(-fstack-protector-all)
endif()
add_definitions(-D_DEFAULT_SOURCE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g2")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2")
if(FUZZ)
if(LIBFUZZER)
set(FUZZ_LDFLAGS "-fsanitize=fuzzer")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=fuzzer-no-link")
endif()
add_definitions(-DFIDO_FUZZ)
endif()
endif()
# Use -Wshorten-64-to-32 if available.
check_c_compiler_flag("-Wshorten-64-to-32" HAVE_SHORTEN_64_TO_32)
if(HAVE_SHORTEN_64_TO_32)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshorten-64-to-32")
if(LIBFUZZER)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=fuzzer-no-link")
endif()
endif()
# Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-result")
add_compile_options(-Wno-unused-result)
endif()
# Decide which keyword to use for thread-local storage.
@ -174,161 +287,8 @@ if(CMAKE_COMPILER_IS_GNUCC OR
elseif(WIN32)
set(TLS "__declspec(thread)")
endif()
add_definitions(-DTLS=${TLS})
# endian.h
check_include_files(endian.h HAVE_ENDIAN_H)
if(HAVE_ENDIAN_H)
add_definitions(-DHAVE_ENDIAN_H)
endif()
# err.h
check_include_files(err.h HAVE_ERR_H)
if(HAVE_ERR_H)
add_definitions(-DHAVE_ERR_H)
endif()
# unistd.h
check_include_files(unistd.h HAVE_UNISTD_H)
if(HAVE_UNISTD_H)
add_definitions(-DHAVE_UNISTD_H)
endif()
# signal.h
check_include_files(signal.h HAVE_SIGNAL_H)
if(HAVE_SIGNAL_H)
add_definitions(-DHAVE_SIGNAL_H)
endif()
# sys/random.h
check_include_files(sys/random.h HAVE_SYS_RANDOM_H)
if(HAVE_SYS_RANDOM_H)
add_definitions(-DHAVE_SYS_RANDOM_H)
endif()
# strlcpy
check_function_exists(strlcpy HAVE_STRLCPY)
if(HAVE_STRLCPY)
add_definitions(-DHAVE_STRLCPY)
endif()
# strlcat
check_function_exists(strlcpy HAVE_STRLCAT)
if(HAVE_STRLCAT)
add_definitions(-DHAVE_STRLCAT)
endif()
# recallocarray
check_function_exists(recallocarray HAVE_RECALLOCARRAY)
if(HAVE_RECALLOCARRAY)
add_definitions(-DHAVE_RECALLOCARRAY)
endif()
# XXX getpagesize is incorrectly detected when cross-compiling
# with mingw on Linux. Avoid.
if(NOT WIN32)
check_function_exists(getpagesize HAVE_GETPAGESIZE)
endif()
if(HAVE_GETPAGESIZE)
add_definitions(-DHAVE_GETPAGESIZE)
endif()
# sysconf
check_function_exists(sysconf HAVE_SYSCONF)
if(HAVE_SYSCONF)
add_definitions(-DHAVE_SYSCONF)
endif()
# memset_s
if(APPLE)
add_definitions(-D__STDC_WANT_LIB_EXT1__=1)
endif()
check_function_exists(memset_s HAVE_MEMSET_S)
if(HAVE_MEMSET_S)
add_definitions(-DHAVE_MEMSET_S)
endif()
# explicit_bzero
if(NOT LIBFUZZER)
check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO)
if(HAVE_EXPLICIT_BZERO)
add_definitions(-DHAVE_EXPLICIT_BZERO)
endif()
endif()
# timingsafe_bcmp
check_function_exists(timingsafe_bcmp HAVE_TIMINGSAFE_BCMP)
if(HAVE_TIMINGSAFE_BCMP)
add_definitions(-DHAVE_TIMINGSAFE_BCMP)
endif()
# readpassphrase
check_function_exists(readpassphrase HAVE_READPASSPHRASE)
if(HAVE_READPASSPHRASE)
add_definitions(-DHAVE_READPASSPHRASE)
endif()
# getline
check_function_exists(getline HAVE_GETLINE)
if(HAVE_GETLINE)
add_definitions(-DHAVE_GETLINE)
endif()
# getopt
check_function_exists(getopt HAVE_GETOPT)
if(HAVE_GETOPT)
add_definitions(-DHAVE_GETOPT)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual")
else()
if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-discarded-qualifiers")
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types-discards-qualifiers")
endif()
endif()
# usable sigaction
set(CMAKE_EXTRA_INCLUDE_FILES signal.h)
check_function_exists(sigaction HAVE_SIGACTION)
check_type_size("sig_atomic_t" HAVE_SIG_ATOMIC_T)
if(HAVE_SIGACTION AND (NOT HAVE_SIG_ATOMIC_T STREQUAL ""))
add_definitions(-DSIGNAL_EXAMPLE)
endif()
set(CMAKE_EXTRA_INCLUDE_FILES)
# arc4random_buf
check_function_exists(arc4random_buf HAVE_ARC4RANDOM_BUF)
if(HAVE_ARC4RANDOM_BUF)
add_definitions(-DHAVE_ARC4RANDOM_BUF)
endif()
# getrandom
check_function_exists(getrandom HAVE_GETRANDOM)
if(HAVE_GETRANDOM)
add_definitions(-DHAVE_GETRANDOM)
endif()
# /dev/urandom
if(UNIX)
add_definitions(-DHAVE_DEV_URANDOM)
endif()
# clock_gettime
if(NOT HAVE_CLOCK_GETTIME)
check_function_exists(clock_gettime HAVE_CLOCK_GETTIME)
if(HAVE_CLOCK_GETTIME)
add_definitions(-DHAVE_CLOCK_GETTIME)
endif()
endif()
# timespecsub
check_symbol_exists(timespecsub sys/time.h HAVE_TIMESPECSUB)
if(HAVE_TIMESPECSUB)
add_definitions(-DHAVE_TIMESPECSUB)
endif()
# export list
if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR
CMAKE_C_COMPILER_ID STREQUAL "AppleClang"))
@ -368,28 +328,47 @@ endif()
include_directories(${CMAKE_SOURCE_DIR}/src)
include_directories(${CBOR_INCLUDE_DIRS})
include_directories(${CRYPTO_INCLUDE_DIRS})
include_directories(${HIDAPI_INCLUDE_DIRS})
include_directories(${UDEV_INCLUDE_DIRS})
include_directories(${ZLIB_INCLUDE_DIRS})
link_directories(${CBOR_LIBRARY_DIRS})
link_directories(${CRYPTO_LIBRARY_DIRS})
link_directories(${HIDAPI_LIBRARY_DIRS})
link_directories(${UDEV_LIBRARY_DIRS})
link_directories(${ZLIB_LIBRARY_DIRS})
message(STATUS "BASE_LIBRARIES: ${BASE_LIBRARIES}")
message(STATUS "BUILD_EXAMPLES: ${BUILD_EXAMPLES}")
message(STATUS "BUILD_MANPAGES: ${BUILD_MANPAGES}")
message(STATUS "BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}")
message(STATUS "BUILD_STATIC_LIBS: ${BUILD_STATIC_LIBS}")
message(STATUS "BUILD_TOOLS: ${BUILD_TOOLS}")
message(STATUS "CBOR_INCLUDE_DIRS: ${CBOR_INCLUDE_DIRS}")
message(STATUS "CBOR_LIBRARIES: ${CBOR_LIBRARIES}")
message(STATUS "CBOR_LIBRARY_DIRS: ${CBOR_LIBRARY_DIRS}")
message(STATUS "CBOR_VERSION: ${CBOR_VERSION}")
message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")
message(STATUS "CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}")
message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}")
message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}")
message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
message(STATUS "CRYPTO_INCLUDE_DIRS: ${CRYPTO_INCLUDE_DIRS}")
message(STATUS "CRYPTO_LIBRARIES: ${CRYPTO_LIBRARIES}")
message(STATUS "CRYPTO_LIBRARY_DIRS: ${CRYPTO_LIBRARY_DIRS}")
message(STATUS "CRYPTO_VERSION: ${CRYPTO_VERSION}")
message(STATUS "FIDO_VERSION: ${FIDO_VERSION}")
message(STATUS "FUZZ: ${FUZZ}")
message(STATUS "ZLIB_INCLUDE_DIRS: ${ZLIB_INCLUDE_DIRS}")
message(STATUS "ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}")
message(STATUS "ZLIB_LIBRARY_DIRS: ${ZLIB_LIBRARY_DIRS}")
message(STATUS "ZLIB_VERSION: ${ZLIB_VERSION}")
if(USE_HIDAPI)
message(STATUS "HIDAPI_INCLUDE_DIRS: ${HIDAPI_INCLUDE_DIRS}")
message(STATUS "HIDAPI_LIBRARIES: ${HIDAPI_LIBRARIES}")
message(STATUS "HIDAPI_LIBRARY_DIRS: ${HIDAPI_LIBRARY_DIRS}")
message(STATUS "HIDAPI_VERSION: ${HIDAPI_VERSION}")
endif()
message(STATUS "LIBFUZZER: ${LIBFUZZER}")
message(STATUS "TLS: ${TLS}")
@ -397,12 +376,20 @@ 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 "NFC_LINUX: ${NFC_LINUX}")
subdirs(src)
subdirs(examples)
subdirs(tools)
subdirs(man)
if(BUILD_EXAMPLES)
subdirs(examples)
endif()
if(BUILD_TOOLS)
subdirs(tools)
endif()
if(BUILD_MANPAGES)
subdirs(man)
endif()
if(NOT WIN32)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
@ -413,7 +400,6 @@ if(NOT WIN32)
if(FUZZ)
subdirs(fuzz)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
subdirs(udev)
endif()

View File

@ -1,4 +1,4 @@
Copyright (c) 2018 Yubico AB. All rights reserved.
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

View File

@ -1,3 +1,52 @@
* 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:

View File

@ -21,7 +21,7 @@ file for the full license text.
=== Supported Platforms
*libfido2* is known to work on Linux, MacOS, Windows, and OpenBSD.
*libfido2* is known to work on Linux, MacOS, Windows, OpenBSD, and FreeBSD.
=== Documentation
@ -29,11 +29,18 @@ Documentation is available in troff and HTML formats. An
https://developers.yubico.com/libfido2/Manuals/[online mirror of *libfido2*'s documentation]
is also available.
=== Bindings
* .NET: https://github.com/borrrden/Fido2Net[Fido2Net]
* Go: https://github.com/keys-pub/go-libfido2[go-libfido2]
* Perl: https://github.com/jacquesg/p5-FIDO-Raw[p5-FIDO-Raw]
* Rust: https://github.com/PvdBerg1998/libfido2[libfido2]
=== Installation
==== Releases
The current release of *libfido2* is 1.5.0. Please consult Yubico's
The current release of *libfido2* is 1.7.0. Please consult Yubico's
https://developers.yubico.com/libfido2/Releases[release page] for source
and binary releases.
@ -43,6 +50,9 @@ and binary releases.
$ sudo apt install libfido2-dev
$ sudo apt install libfido2-doc
Alternatively, newer versions of *libfido2* are available in Yubico's PPA.
Follow the instructions for Ubuntu 18.04 (Bionic) and 16.04 (Xenial) below.
==== Ubuntu 18.04 (Bionic) and 16.04 (Xenial)
$ sudo apt install software-properties-common
@ -64,9 +74,10 @@ Depending on the platform,
https://www.freedesktop.org/wiki/Software/pkg-config/[pkg-config] may need to
be installed, or the PKG_CONFIG_PATH environment variable set.
*libfido2* depends on https://github.com/pjk/libcbor[libcbor] and
https://www.openssl.org[OpenSSL]. On Linux, libudev (part of
https://www.freedesktop.org/wiki/Software/systemd[systemd]) is also required.
*libfido2* depends on https://github.com/pjk/libcbor[libcbor],
https://www.openssl.org[OpenSSL], and https://zlib.net[zlib]. On Linux, libudev
(part of https://www.freedesktop.org/wiki/Software/systemd[systemd]) is also
required.
For complete, OS-specific installation instructions, please refer to the
`.actions/` (Linux, MacOS) and `windows/` directories.

View File

@ -9,12 +9,20 @@ list(APPEND COMPAT_SOURCES
../openbsd-compat/strlcpy.c
)
if(WIN32 AND NOT CYGWIN AND NOT MSYS)
if(WIN32 AND BUILD_SHARED_LIBS AND NOT CYGWIN AND NOT MSYS)
list(APPEND COMPAT_SOURCES ../openbsd-compat/posix_win.c)
endif()
# drop -rdynamic
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
# set the library to link against
if(BUILD_STATIC_LIBS)
# drop -rdynamic
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
set(_FIDO2_LIBRARY fido2)
elseif(BUILD_SHARED_LIBS)
set(_FIDO2_LIBRARY fido2_shared)
else()
set(_FIDO2_LIBRARY ${CRYPTO_LIBRARIES} fido2)
endif()
# enable -Wconversion -Wsign-conversion
if(NOT MSVC)
@ -25,35 +33,36 @@ endif()
# manifest
add_executable(manifest manifest.c ${COMPAT_SOURCES})
target_link_libraries(manifest fido2)
target_link_libraries(manifest ${_FIDO2_LIBRARY})
# info
add_executable(info info.c ${COMPAT_SOURCES})
target_link_libraries(info fido2)
target_link_libraries(info ${_FIDO2_LIBRARY})
# reset
add_executable(reset reset.c util.c ${COMPAT_SOURCES})
target_link_libraries(reset fido2)
target_link_libraries(reset ${_FIDO2_LIBRARY})
# cred
add_executable(cred cred.c util.c ${COMPAT_SOURCES})
target_link_libraries(cred fido2)
target_link_libraries(cred ${_FIDO2_LIBRARY})
# assert
add_executable(assert assert.c util.c ${COMPAT_SOURCES})
target_link_libraries(assert fido2)
target_link_libraries(assert ${_FIDO2_LIBRARY})
# setpin
add_executable(setpin setpin.c ${COMPAT_SOURCES})
target_link_libraries(setpin fido2)
target_link_libraries(setpin ${_FIDO2_LIBRARY})
# retries
add_executable(retries retries.c ${COMPAT_SOURCES})
target_link_libraries(retries fido2)
target_link_libraries(retries ${_FIDO2_LIBRARY})
# select
add_executable(select select.c ${COMPAT_SOURCES})
target_link_libraries(select fido2)
target_link_libraries(select ${_FIDO2_LIBRARY})
if(MINGW)
# needed for nanosleep() in mingw
target_link_libraries(select winpthread)

View File

@ -23,6 +23,10 @@ The following definitions are used in the description below:
The file system path of a file containing a NIST P-256 public key in
PEM format.
- <blobkey>
A credential's associated FIDO2.1 "largeBlob" symmetric key.
=== Description
The following examples are provided:
@ -45,7 +49,7 @@ The following examples are provided:
the device's PIN is changed from [oldpin] to <pin>.
- cred [-t ecdsa|rsa|eddsa] [-k pubkey] [-ei cred_id] [-P pin] [-T seconds]
[-hruv] <device>
[-b blobkey] [-hruv] <device>
Creates a new credential on <device> and verify that the credential
was signed by the authenticator. The device's attestation certificate
@ -58,10 +62,12 @@ The following examples are provided:
will involve a resident key. User verification may be requested
through the -v option. If option -u is specified, the credential
is generated using U2F (CTAP1) instead of FIDO2 (CTAP2) commands.
The -T option may be used to enforce a timeout of <seconds>.
The -T option may be used to enforce a timeout of <seconds>. If the
option -b is specified, the credential's "largeBlob" key is stored in
<blobkey>.
- assert [-t ecdsa|rsa|eddsa] [-a cred_id] [-h hmac_secret] [-s hmac_salt]
[-P pin] [-T seconds] [-puv] <pubkey> <device>
[-P pin] [-T seconds] [-b blobkey] [-puv] <pubkey> <device>
Asks <device> for a FIDO2 assertion corresponding to [cred_id],
which may be omitted for resident keys. The obtained assertion
@ -72,7 +78,9 @@ The following examples are provided:
specified, a FIDO2 hmac-secret is requested from the authenticator,
and the contents of <hmac_salt> are used as the salt. If option -h
is specified, the resulting hmac-secret is stored in <hmac_secret>.
The -T option may be used to enforce a timeout of <seconds>.
The -T option may be used to enforce a timeout of <seconds>. If the
option -b specified, the credential's "largeBlob" key is stored in
<blobkey>.
- retries <device>
Get the number of PIN attempts left on <device> before lockout.

View File

@ -4,7 +4,10 @@
* license that can be found in the LICENSE file.
*/
#include <openssl/ec.h>
#include <fido.h>
#include <fido/es256.h>
#include <fido/rs256.h>
#include <fido/eddsa.h>
#include <stdbool.h>
#include <stdio.h>
@ -14,12 +17,8 @@
#include <unistd.h>
#endif
#include "fido.h"
#include "fido/es256.h"
#include "fido/rs256.h"
#include "fido/eddsa.h"
#include "extern.h"
#include "../openbsd-compat/openbsd-compat.h"
#include "extern.h"
static const unsigned char cdh[32] = {
0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
@ -32,8 +31,8 @@ static void
usage(void)
{
fprintf(stderr, "usage: assert [-t ecdsa|rsa|eddsa] [-a cred_id] "
"[-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] [-puv] "
"<pubkey> <device>\n");
"[-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] "
"[-b blobkey] [-puv] <pubkey> <device>\n");
exit(EXIT_FAILURE);
}
@ -164,6 +163,7 @@ main(int argc, char **argv)
fido_dev_t *dev = NULL;
fido_assert_t *assert = NULL;
const char *pin = NULL;
const char *blobkey_out = NULL;
const char *hmac_out = NULL;
unsigned char *body = NULL;
long long seconds = 0;
@ -176,7 +176,7 @@ main(int argc, char **argv)
if ((assert = fido_assert_new()) == NULL)
errx(1, "fido_assert_new");
while ((ch = getopt(argc, argv, "P:T:a:h:ps:t:uv")) != -1) {
while ((ch = getopt(argc, argv, "P:T:a:b:h:ps:t:uv")) != -1) {
switch (ch) {
case 'P':
pin = optarg;
@ -202,6 +202,10 @@ main(int argc, char **argv)
free(body);
body = NULL;
break;
case 'b':
ext |= FIDO_EXT_LARGEBLOB_KEY;
blobkey_out = optarg;
break;
case 'h':
hmac_out = optarg;
break;
@ -209,7 +213,7 @@ main(int argc, char **argv)
up = true;
break;
case 's':
ext = FIDO_EXT_HMAC_SECRET;
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,
@ -324,6 +328,14 @@ main(int argc, char **argv)
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);

View File

@ -4,10 +4,8 @@
* license that can be found in the LICENSE file.
*/
#include <openssl/ec.h>
#include <openssl/pem.h>
#include <errno.h>
#include <fido.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -16,9 +14,8 @@
#include <unistd.h>
#endif
#include "fido.h"
#include "extern.h"
#include "../openbsd-compat/openbsd-compat.h"
#include "extern.h"
static const unsigned char cdh[32] = {
0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
@ -38,7 +35,8 @@ static void
usage(void)
{
fprintf(stderr, "usage: cred [-t ecdsa|rsa|eddsa] [-k pubkey] "
"[-ei cred_id] [-P pin] [-T seconds] [-hruv] <device>\n");
"[-ei cred_id] [-P pin] [-T seconds] [-b blobkey] [-hruv] "
"<device>\n");
exit(EXIT_FAILURE);
}
@ -164,6 +162,7 @@ main(int argc, char **argv)
fido_dev_t *dev;
fido_cred_t *cred = NULL;
const char *pin = NULL;
const char *blobkey_out = NULL;
const char *key_out = NULL;
const char *id_out = NULL;
const char *path = NULL;
@ -180,7 +179,7 @@ main(int argc, char **argv)
if ((cred = fido_cred_new()) == NULL)
errx(1, "fido_cred_new");
while ((ch = getopt(argc, argv, "P:T:e:hi:k:rt:uv")) != -1) {
while ((ch = getopt(argc, argv, "P:T:b:e:hi:k:rt:uv")) != -1) {
switch (ch) {
case 'P':
pin = optarg;
@ -196,6 +195,10 @@ main(int argc, char **argv)
errx(1, "-T: %s must be in (0,30]", optarg);
break;
#endif
case 'b':
ext |= FIDO_EXT_LARGEBLOB_KEY;
blobkey_out = optarg;
break;
case 'e':
if (read_blob(optarg, &body, &len) < 0)
errx(1, "read_blob: %s", optarg);
@ -207,7 +210,7 @@ main(int argc, char **argv)
body = NULL;
break;
case 'h':
ext = FIDO_EXT_HMAC_SECRET;
ext |= FIDO_EXT_HMAC_SECRET;
break;
case 'i':
id_out = optarg;
@ -324,6 +327,13 @@ main(int argc, char **argv)
fido_cred_x5c_len(cred), fido_cred_sig_ptr(cred),
fido_cred_sig_len(cred), rk, uv, ext, key_out, id_out);
if (blobkey_out != NULL) {
/* extract the "largeBlob" key */
if (write_blob(blobkey_out, fido_cred_largeblob_key_ptr(cred),
fido_cred_largeblob_key_len(cred)) < 0)
errx(1, "write_blob");
}
fido_cred_free(&cred);
exit(0);

View File

@ -4,13 +4,13 @@
* license that can be found in the LICENSE file.
*/
#include <fido.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fido.h"
#include "../openbsd-compat/openbsd-compat.h"
/*

View File

@ -4,11 +4,10 @@
* license that can be found in the LICENSE file.
*/
#include <stdbool.h>
#include <fido.h>
#include <stdio.h>
#include <stdlib.h>
#include "fido.h"
#include "../openbsd-compat/openbsd-compat.h"
int

View File

@ -8,14 +8,12 @@
* Perform a factory reset on a given authenticator.
*/
#include <stdbool.h>
#include <stdint.h>
#include <fido.h>
#include <stdio.h>
#include <stdlib.h>
#include "fido.h"
#include "extern.h"
#include "../openbsd-compat/openbsd-compat.h"
#include "extern.h"
int
main(int argc, char **argv)

View File

@ -8,11 +8,10 @@
* Get an authenticator's number of PIN attempts left.
*/
#include <stdbool.h>
#include <fido.h>
#include <stdio.h>
#include <stdlib.h>
#include "fido.h"
#include "../openbsd-compat/openbsd-compat.h"
int

View File

@ -5,12 +5,11 @@
*/
#include <errno.h>
#include <stdbool.h>
#include <fido.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "fido.h"
#include "../openbsd-compat/openbsd-compat.h"
#define FIDO_POLL_MS 50

View File

@ -8,12 +8,10 @@
* Configure a PIN on a given authenticator.
*/
#include <stdbool.h>
#include <stdint.h>
#include <fido.h>
#include <stdio.h>
#include <stdlib.h>
#include "fido.h"
#include "../openbsd-compat/openbsd-compat.h"
static void

View File

@ -11,10 +11,14 @@
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <fido.h>
#include <fido/es256.h>
#include <fido/rs256.h>
#include <fido/eddsa.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SIGNAL_H
@ -26,13 +30,8 @@
#ifdef _MSC_VER
#include "../openbsd-compat/posix_win.h"
#endif
#include "fido.h"
#include "fido/es256.h"
#include "fido/rs256.h"
#include "fido/eddsa.h"
#include "extern.h"
#include "../openbsd-compat/openbsd-compat.h"
#include "extern.h"
#ifdef SIGNAL_EXAMPLE
volatile sig_atomic_t got_signal = 0;
@ -81,7 +80,7 @@ write_blob(const char *path, const unsigned char *ptr, size_t len)
int fd, ok = -1;
ssize_t n;
if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) {
warn("open %s", path);
goto fail;
}

View File

@ -12,6 +12,8 @@ list(APPEND COMMON_SOURCES
mutator_aux.c
)
set(FUZZ_LDFLAGS "-fsanitize=fuzzer")
# fuzz_cred
add_executable(fuzz_cred fuzz_cred.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
target_compile_options(fuzz_cred PRIVATE ${FUZZ_LDFLAGS})
@ -41,3 +43,21 @@ add_executable(fuzz_bio fuzz_bio.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
target_compile_options(fuzz_bio PRIVATE ${FUZZ_LDFLAGS})
set_target_properties(fuzz_bio PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
target_link_libraries(fuzz_bio fido2_shared)
# fuzz_hid
add_executable(fuzz_hid fuzz_hid.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
target_compile_options(fuzz_hid PRIVATE ${FUZZ_LDFLAGS})
set_target_properties(fuzz_hid PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
target_link_libraries(fuzz_hid fido2_shared)
# fuzz_netlink
add_executable(fuzz_netlink fuzz_netlink.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
target_compile_options(fuzz_netlink PRIVATE ${FUZZ_LDFLAGS})
set_target_properties(fuzz_netlink PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
target_link_libraries(fuzz_netlink fido2_shared)
# fuzz_largeblob
add_executable(fuzz_largeblob fuzz_largeblob.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
target_compile_options(fuzz_largeblob PRIVATE ${FUZZ_LDFLAGS})
set_target_properties(fuzz_largeblob PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
target_link_libraries(fuzz_largeblob fido2_shared)

View File

@ -5,7 +5,8 @@
FROM ubuntu:focal
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y clang-10 cmake git libssl-dev libudev-dev make pkg-config
RUN git clone --branch v0.7.0 https://github.com/PJK/libcbor
RUN apt-get install -y clang-11 cmake git libssl-dev libudev-dev make pkg-config
RUN apt-get install -y zlib1g-dev
RUN git clone --branch v0.8.0 https://github.com/PJK/libcbor
RUN git clone https://github.com/yubico/libfido2
RUN CC=clang-10 CXX=clang++-10 /libfido2/fuzz/build-coverage /libcbor /libfido2
RUN CC=clang-11 CXX=clang++-11 /libfido2/fuzz/build-coverage /libcbor /libfido2

View File

@ -2,11 +2,12 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
IMAGE := libfido2-coverage:1.5.0
IMAGE := libfido2-coverage:1.7.0
RUNNER := libfido2-runner
PROFDATA := llvm-profdata-10
COV := llvm-cov-10
TARGETS := fuzz_assert fuzz_bio fuzz_cred fuzz_credman fuzz_mgmt
PROFDATA := llvm-profdata-11
COV := llvm-cov-11
TARGETS := fuzz_assert fuzz_bio fuzz_cred fuzz_credman fuzz_hid \
fuzz_largeblob fuzz_netlink fuzz_mgmt
CORPORA := $(foreach f,${TARGETS},${f}/corpus)
MINIFY := $(foreach f,${TARGETS},/minify/${f}/corpus)
REMOTE := gs://libfido2-corpus.clusterfuzz-external.appspot.com

View File

@ -3,12 +3,12 @@ ASAN/MSAN/UBSAN.
AFL is more convenient when fuzzing the path from the authenticator to
libfido2 in an existing application. To do so, use preload-snoop.c with a real
authenticator to obtain an initial corpus, rebuild libfido2 with -DFUZZ=1, and
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, and fuzz_mgmt.c for examples. To build these harnesses,
use -DFUZZ=1 -DLIBFUZZER=1.
fuzz_assert.c, fuzz_hid.c, and fuzz_mgmt.c for examples. To build these
harnesses, use -DFUZZ=ON -DLIBFUZZER=ON.
To run under ASAN/MSAN/UBSAN, libfido2 needs to be linked against flavours of
libcbor and OpenSSL built with the respective sanitiser. In order to keep

View File

@ -18,13 +18,14 @@ 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 -DCMAKE_INSTALL_PREFIX="${LIBCBOR}/install" ..)
make -C "${LIBCBOR}/build" all install
(cd "${LIBCBOR}/build" && cmake -DBUILD_SHARED_LIBS=ON \
-DCMAKE_INSTALL_PREFIX="${LIBCBOR}/install" ..)
make -C "${LIBCBOR}/build" VERBOSE=1 all install
# Build libfido2.
mkdir -p "${LIBFIDO2}/build"
export CFLAGS="-fprofile-instr-generate -fcoverage-mapping"
export LDFLAGS="${CFLAGS}"
(cd "${LIBFIDO2}/build" && cmake -DFUZZ=1 -DLIBFUZZER=1 \
(cd "${LIBFIDO2}/build" && cmake -DFUZZ=ON -DLIBFUZZER=ON \
-DCMAKE_BUILD_TYPE=Debug ..)
make -C "${LIBFIDO2}/build"

View File

@ -13,6 +13,8 @@
fido_assert_allow_cred;
fido_assert_authdata_len;
fido_assert_authdata_ptr;
fido_assert_blob_len;
fido_assert_blob_ptr;
fido_assert_clientdata_hash_len;
fido_assert_clientdata_hash_ptr;
fido_assert_count;
@ -22,6 +24,8 @@
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;
@ -30,6 +34,7 @@
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;
@ -76,6 +81,7 @@
fido_cbor_info_extensions_ptr;
fido_cbor_info_free;
fido_cbor_info_maxmsgsiz;
fido_cbor_info_maxcredbloblen;
fido_cbor_info_maxcredcntlst;
fido_cbor_info_maxcredidlen;
fido_cbor_info_fwversion;
@ -89,11 +95,16 @@
fido_cbor_info_versions_ptr;
fido_cred_authdata_len;
fido_cred_authdata_ptr;
fido_cred_authdata_raw_len;
fido_cred_authdata_raw_ptr;
fido_cred_clientdata_hash_len;
fido_cred_clientdata_hash_ptr;
fido_cred_display_name;
fido_cred_exclude;
fido_cred_flags;
fido_cred_largeblob_key_len;
fido_cred_largeblob_key_ptr;
fido_cred_sigcount;
fido_cred_fmt;
fido_cred_free;
fido_cred_id_len;
@ -127,6 +138,7 @@
fido_cred_rp_name;
fido_cred_set_authdata;
fido_cred_set_authdata_raw;
fido_cred_set_blob;
fido_cred_set_clientdata_hash;
fido_cred_set_extensions;
fido_cred_set_fmt;
@ -152,16 +164,20 @@
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;
@ -181,10 +197,28 @@
fido_dev_reset;
fido_dev_set_io_functions;
fido_dev_set_pin;
fido_dev_set_pin_minlen;
fido_dev_set_transport_functions;
fido_dev_supports_cred_prot;
fido_dev_supports_credman;
fido_dev_supports_permissions;
fido_dev_supports_pin;
fido_dev_supports_uv;
fido_dev_toggle_always_uv;
fido_dev_largeblob_get;
fido_dev_largeblob_get_array;
fido_dev_largeblob_remove;
fido_dev_largeblob_set;
fido_dev_largeblob_set_array;
fido_hid_get_report_len;
fido_hid_get_usage;
fido_init;
fido_nfc_rx;
fido_nfc_tx;
fido_nl_free;
fido_nl_get_nfc_target;
fido_nl_new;
fido_nl_power_nfc;
fido_set_log_handler;
fido_strerr;
rs256_pk_free;
@ -193,6 +227,7 @@
rs256_pk_new;
rs256_pk_to_EVP_PKEY;
prng_init;
set_netlink_io_functions;
uniform_random;
local:
*;

File diff suppressed because it is too large Load Diff

View File

@ -5,22 +5,16 @@
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "mutator_aux.h"
#include "wiredata_fido2.h"
#include "wiredata_u2f.h"
#include "dummy.h"
#include "fido.h"
#include "fido/es256.h"
#include "fido/rs256.h"
#include "fido/eddsa.h"
#include "../openbsd-compat/openbsd-compat.h"
/* Parameter set defining a FIDO2 get assertion operation. */
@ -37,7 +31,7 @@ struct param {
struct blob wire_data;
uint8_t cred_count;
uint8_t type;
uint8_t u2f;
uint8_t opt;
uint8_t up;
uint8_t uv;
};
@ -86,7 +80,7 @@ unpack(const uint8_t *ptr, size_t len)
if (unpack_byte(v[0], &p->uv) < 0 ||
unpack_byte(v[1], &p->up) < 0 ||
unpack_byte(v[2], &p->u2f) < 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 ||
@ -126,7 +120,7 @@ pack(uint8_t *ptr, size_t len, const struct param *p)
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->u2f)) == 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 ||
@ -208,33 +202,25 @@ pack_dummy(uint8_t *ptr, size_t len)
}
static void
get_assert(fido_assert_t *assert, uint8_t u2f, const struct blob *cdh,
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;
fido_dev_io_t io;
memset(&io, 0, sizeof(io));
io.open = dev_open;
io.close = dev_close;
io.read = dev_read;
io.write = dev_write;
if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
&io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
fido_dev_free(&dev);
if ((dev = open_dev(opt & 2)) == NULL)
return;
}
if (u2f & 1)
if (opt & 1)
fido_dev_force_u2f(dev);
if (ext & 1)
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 (u2f &1)
else if (opt & 1)
fido_assert_set_up(assert, FIDO_OPT_FALSE);
if (uv & 1)
fido_assert_set_uv(assert, FIDO_OPT_TRUE);
@ -255,7 +241,7 @@ get_assert(fido_assert_t *assert, uint8_t u2f, const struct blob *cdh,
if (strlen(pin) == 0)
pin = NULL;
fido_dev_get_assert(dev, assert, u2f & 1 ? NULL : pin);
fido_dev_get_assert(dev, assert, (opt & 1) ? NULL : pin);
fido_dev_cancel(dev);
fido_dev_close(dev);
@ -408,7 +394,7 @@ test(const struct param *p)
set_wire_data(p->wire_data.body, p->wire_data.len);
get_assert(assert, p->u2f, &p->cdh, p->rp_id, p->ext, p->up, p->uv,
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 */
@ -433,6 +419,10 @@ test(const struct param *p)
xstrlen(fido_assert_user_name(assert, i)));
consume(fido_assert_user_display_name(assert, i),
xstrlen(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);
@ -456,7 +446,7 @@ mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
if (flags & MUTATE_PARAM) {
mutate_byte(&p->uv);
mutate_byte(&p->up);
mutate_byte(&p->u2f);
mutate_byte(&p->opt);
mutate_byte(&p->type);
mutate_byte(&p->cred_count);
mutate_int(&p->ext);
@ -470,7 +460,7 @@ mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
}
if (flags & MUTATE_WIREDATA) {
if (p->u2f & 1) {
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);

View File

@ -6,17 +6,14 @@
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "mutator_aux.h"
#include "wiredata_fido2.h"
#include "dummy.h"
#include "fido.h"
#include "fido/bio.h"
#include "../openbsd-compat/openbsd-compat.h"
/* Parameter set defining a FIDO2 credential management operation. */
@ -223,21 +220,10 @@ static fido_dev_t *
prepare_dev(void)
{
fido_dev_t *dev;
fido_dev_io_t io;
bool x;
memset(&io, 0, sizeof(io));
io.open = dev_open;
io.close = dev_close;
io.read = dev_read;
io.write = dev_write;
if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
&io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
fido_dev_free(&dev);
if ((dev = open_dev(0)) == NULL)
return NULL;
}
x = fido_dev_is_fido2(dev);
consume(&x, sizeof(x));
@ -245,6 +231,10 @@ prepare_dev(void)
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;
}
@ -313,7 +303,7 @@ enroll(const struct param *p)
(e = fido_bio_enroll_new()) == NULL)
goto done;
fido_bio_dev_enroll_begin(dev, t, e, p->seed, p->pin);
fido_bio_dev_enroll_begin(dev, t, e, (uint32_t)p->seed, p->pin);
consume_template(t);
consume_enroll(e);

View File

@ -6,15 +6,14 @@
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "mutator_aux.h"
#include "wiredata_fido2.h"
#include "wiredata_u2f.h"
#include "dummy.h"
#include "fido.h"
#include "../openbsd-compat/openbsd-compat.h"
@ -35,7 +34,7 @@ struct param {
uint8_t excl_count;
uint8_t rk;
uint8_t type;
uint8_t u2f;
uint8_t opt;
uint8_t uv;
};
@ -87,7 +86,7 @@ unpack(const uint8_t *ptr, size_t len)
if (unpack_byte(v[0], &p->rk) < 0 ||
unpack_byte(v[1], &p->type) < 0 ||
unpack_byte(v[2], &p->u2f) < 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 ||
@ -129,7 +128,7 @@ pack(uint8_t *ptr, size_t len, const struct param *p)
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->u2f)) == 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 ||
@ -211,29 +210,17 @@ pack_dummy(uint8_t *ptr, size_t len)
}
static void
make_cred(fido_cred_t *cred, uint8_t u2f, int type, const struct blob *cdh,
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;
fido_dev_io_t io;
memset(&io, 0, sizeof(io));
io.open = dev_open;
io.close = dev_close;
io.read = dev_read;
io.write = dev_write;
if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
&io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
fido_dev_free(&dev);
if ((dev = open_dev(opt & 2)) == NULL)
return;
}
if (u2f & 1)
if (opt & 1)
fido_dev_force_u2f(dev);
for (uint8_t i = 0; i < excl_count; i++)
@ -244,7 +231,12 @@ make_cred(fido_cred_t *cred, uint8_t u2f, int type, const struct blob *cdh,
fido_cred_set_rp(cred, rp_id, rp_name);
fido_cred_set_user(cred, user_id->body, user_id->len, user_name,
user_nick, user_icon);
fido_cred_set_extensions(cred, ext);
if (ext & FIDO_EXT_HMAC_SECRET)
fido_cred_set_extensions(cred, FIDO_EXT_HMAC_SECRET);
if (ext & FIDO_EXT_CRED_BLOB)
fido_cred_set_blob(cred, user_id->body, user_id->len);
if (ext & FIDO_EXT_LARGEBLOB_KEY)
fido_cred_set_extensions(cred, FIDO_EXT_LARGEBLOB_KEY);
if (rk & 1)
fido_cred_set_rk(cred, FIDO_OPT_TRUE);
@ -263,7 +255,7 @@ make_cred(fido_cred_t *cred, uint8_t u2f, int type, const struct blob *cdh,
if (strlen(pin) == 0)
pin = NULL;
fido_dev_make_cred(dev, cred, u2f & 1 ? NULL : pin);
fido_dev_make_cred(dev, cred, (opt & 1) ? NULL : pin);
fido_dev_cancel(dev);
fido_dev_close(dev);
@ -273,12 +265,14 @@ make_cred(fido_cred_t *cred, uint8_t u2f, int type, const struct blob *cdh,
static void
verify_cred(int type, const unsigned char *cdh_ptr, size_t cdh_len,
const char *rp_id, const char *rp_name, const unsigned char *authdata_ptr,
size_t authdata_len, int ext, uint8_t rk, uint8_t uv,
size_t authdata_len, const unsigned char *authdata_raw_ptr,
size_t authdata_raw_len, int ext, uint8_t rk, uint8_t uv,
const unsigned char *x5c_ptr, size_t x5c_len, const unsigned char *sig_ptr,
size_t sig_len, const char *fmt, int prot)
{
fido_cred_t *cred;
uint8_t flags;
uint32_t sigcount;
if ((cred = fido_cred_new()) == NULL)
return;
@ -286,8 +280,11 @@ verify_cred(int type, const unsigned char *cdh_ptr, size_t cdh_len,
fido_cred_set_type(cred, type);
fido_cred_set_clientdata_hash(cred, cdh_ptr, cdh_len);
fido_cred_set_rp(cred, rp_id, rp_name);
consume(authdata_ptr, authdata_len);
consume(authdata_raw_ptr, authdata_raw_len);
if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK)
fido_cred_set_authdata_raw(cred, authdata_ptr, authdata_len);
fido_cred_set_authdata_raw(cred, authdata_raw_ptr,
authdata_raw_len);
fido_cred_set_extensions(cred, ext);
fido_cred_set_x509(cred, x5c_ptr, x5c_len);
fido_cred_set_sig(cred, sig_ptr, sig_len);
@ -316,9 +313,13 @@ verify_cred(int type, const unsigned char *cdh_ptr, size_t cdh_len,
consume(fido_cred_user_name(cred), xstrlen(fido_cred_user_name(cred)));
consume(fido_cred_display_name(cred),
xstrlen(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));
@ -348,7 +349,7 @@ test_cred(const struct param *p)
set_wire_data(p->wire_data.body, p->wire_data.len);
make_cred(cred, p->u2f, cose_alg, &p->cdh, p->rp_id, p->rp_name,
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);
@ -356,7 +357,8 @@ test_cred(const struct param *p)
fido_cred_clientdata_hash_ptr(cred),
fido_cred_clientdata_hash_len(cred), fido_cred_rp_id(cred),
fido_cred_rp_name(cred), fido_cred_authdata_ptr(cred),
fido_cred_authdata_len(cred), p->ext, p->rk, p->uv,
fido_cred_authdata_len(cred), fido_cred_authdata_raw_ptr(cred),
fido_cred_authdata_raw_len(cred), p->ext, p->rk, p->uv,
fido_cred_x5c_ptr(cred), fido_cred_x5c_len(cred),
fido_cred_sig_ptr(cred), fido_cred_sig_len(cred),
fido_cred_fmt(cred), fido_cred_prot(cred));
@ -368,26 +370,14 @@ static void
test_touch(const struct param *p)
{
fido_dev_t *dev;
fido_dev_io_t io;
int r;
int touched;
memset(&io, 0, sizeof(io));
io.open = dev_open;
io.close = dev_close;
io.read = dev_read;
io.write = dev_write;
set_wire_data(p->wire_data.body, p->wire_data.len);
if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
&io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
fido_dev_free(&dev);
if ((dev = open_dev(p->opt & 2)) == NULL)
return;
}
if (p->u2f & 1)
if (p->opt & 1)
fido_dev_force_u2f(dev);
r = fido_dev_get_touch_begin(dev);
@ -421,7 +411,7 @@ mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
if (flags & MUTATE_PARAM) {
mutate_byte(&p->rk);
mutate_byte(&p->type);
mutate_byte(&p->u2f);
mutate_byte(&p->opt);
mutate_byte(&p->uv);
mutate_byte(&p->excl_count);
mutate_int(&p->ext);
@ -437,7 +427,7 @@ mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
}
if (flags & MUTATE_WIREDATA) {
if (p->u2f & 1) {
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);

View File

@ -6,17 +6,14 @@
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "mutator_aux.h"
#include "wiredata_fido2.h"
#include "dummy.h"
#include "fido.h"
#include "fido/credman.h"
#include "../openbsd-compat/openbsd-compat.h"
/* Parameter set defining a FIDO2 credential management operation. */
@ -207,26 +204,17 @@ static fido_dev_t *
prepare_dev(void)
{
fido_dev_t *dev;
fido_dev_io_t io;
bool x;
memset(&io, 0, sizeof(io));
io.open = dev_open;
io.close = dev_close;
io.read = dev_read;
io.write = dev_write;
if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
&io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
fido_dev_free(&dev);
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;
}

View File

@ -0,0 +1,173 @@
/*
* Copyright (c) 2020 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "../openbsd-compat/openbsd-compat.h"
#include "mutator_aux.h"
extern int fido_hid_get_usage(const uint8_t *, size_t, uint32_t *);
extern int fido_hid_get_report_len(const uint8_t *, size_t, size_t *, size_t *);
struct param {
int seed;
struct blob report_descriptor;
};
/*
* Sample HID report descriptor from the FIDO HID interface of a YubiKey 5.
*/
static const uint8_t dummy_report_descriptor[] = {
0x06, 0xd0, 0xf1, 0x09, 0x01, 0xa1, 0x01, 0x09,
0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08,
0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00,
0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x40, 0x91,
0x02, 0xc0
};
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) != 2 ||
(v = cbor_array_handle(item)) == NULL)
goto fail;
if (unpack_int(v[0], &p->seed) < 0 ||
unpack_blob(v[1], &p->report_descriptor) < 0)
goto fail;
ok = 0;
fail:
if (ok < 0) {
free(p);
p = NULL;
}
if (item)
cbor_decref(&item);
return p;
}
size_t
pack(uint8_t *ptr, size_t len, const struct param *p)
{
cbor_item_t *argv[2], *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(2)) == NULL ||
(argv[0] = pack_int(p->seed)) == NULL ||
(argv[1] = pack_blob(&p->report_descriptor)) == NULL)
goto fail;
for (size_t i = 0; i < 2; i++)
if (cbor_array_push(array, argv[i]) == false)
goto fail;
if ((cbor_len = cbor_serialize_alloc(array, &cbor,
&cbor_alloc_len)) > len) {
cbor_len = 0;
goto fail;
}
memcpy(ptr, cbor, cbor_len);
fail:
for (size_t i = 0; i < 2; i++)
if (argv[i])
cbor_decref(&argv[i]);
if (array)
cbor_decref(&array);
free(cbor);
return cbor_len;
}
size_t
pack_dummy(uint8_t *ptr, size_t len)
{
struct param dummy;
uint8_t blob[4096];
size_t blob_len;
memset(&dummy, 0, sizeof(dummy));
dummy.report_descriptor.len = sizeof(dummy_report_descriptor);
memcpy(&dummy.report_descriptor.body, &dummy_report_descriptor,
dummy.report_descriptor.len);
assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
if (blob_len > len) {
memcpy(ptr, blob, len);
return 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));
}
void
test(const struct param *p)
{
prng_init((unsigned int)p->seed);
fido_init(FIDO_DEBUG);
fido_set_log_handler(consume_str);
get_usage(p);
get_report_len(p);
}
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);
}

View File

@ -0,0 +1,270 @@
/*
* Copyright (c) 2020 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mutator_aux.h"
#include "wiredata_fido2.h"
#include "dummy.h"
#include "../openbsd-compat/openbsd-compat.h"
/* Parameter set defining a FIDO2 "large blob" operation. */
struct param {
char pin[MAXSTR];
int seed;
struct blob key;
struct blob get_wiredata;
struct blob set_wiredata;
};
/*
* Collection of HID reports from an authenticator issued with a FIDO2
* 'authenticatorLargeBlobs' 'get' command.
*/
static const uint8_t dummy_get_wiredata[] = {
WIREDATA_CTAP_INIT,
WIREDATA_CTAP_CBOR_INFO,
WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY
};
/*
* Collection of HID reports from an authenticator issued with a FIDO2
* 'authenticatorLargeBlobs' 'set' command.
*/
static const uint8_t dummy_set_wiredata[] = {
WIREDATA_CTAP_INIT,
WIREDATA_CTAP_CBOR_INFO,
WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY,
WIREDATA_CTAP_CBOR_AUTHKEY,
WIREDATA_CTAP_CBOR_PINTOKEN,
WIREDATA_CTAP_CBOR_STATUS
};
/*
* XXX this needs to match the encrypted blob embedded in
* WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY.
*/
static const uint8_t dummy_key[] = {
0xa9, 0x1b, 0xc4, 0xdd, 0xfc, 0x9a, 0x93, 0x79,
0x75, 0xba, 0xf7, 0x7f, 0x4d, 0x57, 0xfc, 0xa6,
0xe1, 0xf8, 0x06, 0x43, 0x23, 0x99, 0x51, 0x32,
0xce, 0x6e, 0x19, 0x84, 0x50, 0x13, 0x2d, 0x7b
};
struct param *
unpack(const uint8_t *ptr, size_t len)
{
cbor_item_t *item = NULL, **v;
struct cbor_load_result cbor;
struct param *p;
int ok = -1;
if ((p = calloc(1, sizeof(*p))) == NULL ||
(item = cbor_load(ptr, len, &cbor)) == NULL ||
cbor.read != len ||
cbor_isa_array(item) == false ||
cbor_array_is_definite(item) == false ||
cbor_array_size(item) != 5 ||
(v = cbor_array_handle(item)) == NULL)
goto fail;
if (unpack_int(v[0], &p->seed) < 0 ||
unpack_string(v[1], p->pin) < 0 ||
unpack_blob(v[2], &p->key) < 0 ||
unpack_blob(v[3], &p->get_wiredata) < 0 ||
unpack_blob(v[4], &p->set_wiredata) < 0)
goto fail;
ok = 0;
fail:
if (ok < 0) {
free(p);
p = NULL;
}
if (item)
cbor_decref(&item);
return p;
}
size_t
pack(uint8_t *ptr, size_t len, const struct param *p)
{
cbor_item_t *argv[5], *array = NULL;
size_t cbor_alloc_len, cbor_len = 0;
unsigned char *cbor = NULL;
memset(argv, 0, sizeof(argv));
if ((array = cbor_new_definite_array(5)) == NULL ||
(argv[0] = pack_int(p->seed)) == NULL ||
(argv[1] = pack_string(p->pin)) == NULL ||
(argv[2] = pack_blob(&p->key)) == NULL ||
(argv[3] = pack_blob(&p->get_wiredata)) == NULL ||
(argv[4] = pack_blob(&p->set_wiredata)) == NULL)
goto fail;
for (size_t i = 0; i < 5; i++)
if (cbor_array_push(array, argv[i]) == false)
goto fail;
if ((cbor_len = cbor_serialize_alloc(array, &cbor,
&cbor_alloc_len)) > len) {
cbor_len = 0;
goto fail;
}
memcpy(ptr, cbor, cbor_len);
fail:
for (size_t i = 0; i < 5; i++)
if (argv[i])
cbor_decref(&argv[i]);
if (array)
cbor_decref(&array);
free(cbor);
return cbor_len;
}
size_t
pack_dummy(uint8_t *ptr, size_t len)
{
struct param dummy;
uint8_t blob[4096];
size_t blob_len;
memset(&dummy, 0, sizeof(dummy));
strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
dummy.get_wiredata.len = sizeof(dummy_get_wiredata);
dummy.set_wiredata.len = sizeof(dummy_set_wiredata);
dummy.key.len = sizeof(dummy_key);
memcpy(&dummy.get_wiredata.body, &dummy_get_wiredata,
dummy.get_wiredata.len);
memcpy(&dummy.set_wiredata.body, &dummy_set_wiredata,
dummy.set_wiredata.len);
memcpy(&dummy.key.body, &dummy_key, dummy.key.len);
assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
if (blob_len > len) {
memcpy(ptr, blob, len);
return len;
}
memcpy(ptr, blob, blob_len);
return blob_len;
}
static fido_dev_t *
prepare_dev(void)
{
fido_dev_t *dev;
if ((dev = open_dev(0)) == NULL)
return NULL;
return dev;
}
static void
get_blob(const struct param *p, int array)
{
fido_dev_t *dev;
u_char *ptr = NULL;
size_t len = 0;
set_wire_data(p->get_wiredata.body, p->get_wiredata.len);
if ((dev = prepare_dev()) == NULL)
return;
if (array)
fido_dev_largeblob_get_array(dev, &ptr, &len);
else
fido_dev_largeblob_get(dev, p->key.body, p->key.len, &ptr, &len);
consume(ptr, len);
free(ptr);
fido_dev_close(dev);
fido_dev_free(&dev);
}
static void
set_blob(const struct param *p, int op)
{
fido_dev_t *dev;
const char *pin;
set_wire_data(p->set_wiredata.body, p->set_wiredata.len);
if ((dev = prepare_dev()) == NULL)
return;
pin = p->pin;
if (strlen(pin) == 0)
pin = NULL;
switch (op) {
case 0:
fido_dev_largeblob_remove(dev, p->key.body, p->key.len, pin);
break;
case 1:
/* XXX reuse p->get_wiredata as the blob to be set */
fido_dev_largeblob_set(dev, p->key.body, p->key.len,
p->get_wiredata.body, p->get_wiredata.len, pin);
break;
case 2:
/* XXX reuse p->get_wiredata as the body of the cbor array */
fido_dev_largeblob_set_array(dev, p->get_wiredata.body,
p->get_wiredata.len, pin);
}
fido_dev_close(dev);
fido_dev_free(&dev);
}
void
test(const struct param *p)
{
prng_init((unsigned int)p->seed);
fido_init(FIDO_DEBUG);
fido_set_log_handler(consume_str);
get_blob(p, 0);
get_blob(p, 1);
set_blob(p, 0);
set_blob(p, 1);
set_blob(p, 2);
}
void
mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
{
if (flags & MUTATE_SEED)
p->seed = (int)seed;
if (flags & MUTATE_PARAM) {
mutate_blob(&p->key);
mutate_string(p->pin);
}
if (flags & MUTATE_WIREDATA) {
mutate_blob(&p->get_wiredata);
mutate_blob(&p->set_wiredata);
}
}

View File

@ -13,7 +13,6 @@
#include "mutator_aux.h"
#include "wiredata_fido2.h"
#include "dummy.h"
#include "fido.h"
#include "../openbsd-compat/openbsd-compat.h"
@ -25,6 +24,7 @@ struct param {
struct blob set_pin_wire_data;
struct blob change_pin_wire_data;
struct blob retry_wire_data;
struct blob config_wire_data;
int seed;
};
@ -34,7 +34,7 @@ static const uint8_t dummy_reset_wire_data[] = {
WIREDATA_CTAP_KEEPALIVE,
WIREDATA_CTAP_KEEPALIVE,
WIREDATA_CTAP_KEEPALIVE,
WIREDATA_CTAP_CBOR_RESET,
WIREDATA_CTAP_CBOR_STATUS,
};
static const uint8_t dummy_info_wire_data[] = {
@ -63,6 +63,12 @@ static const uint8_t dummy_retry_wire_data[] = {
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)
{
@ -76,7 +82,7 @@ unpack(const uint8_t *ptr, size_t len)
cbor.read != len ||
cbor_isa_array(item) == false ||
cbor_array_is_definite(item) == false ||
cbor_array_size(item) != 8 ||
cbor_array_size(item) != 9 ||
(v = cbor_array_handle(item)) == NULL)
goto fail;
@ -87,7 +93,8 @@ unpack(const uint8_t *ptr, size_t len)
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[7], &p->retry_wire_data) < 0 ||
unpack_blob(v[8], &p->config_wire_data) < 0)
goto fail;
ok = 0;
@ -106,13 +113,13 @@ fail:
size_t
pack(uint8_t *ptr, size_t len, const struct param *p)
{
cbor_item_t *argv[8], *array = NULL;
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(8)) == NULL ||
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 ||
@ -120,10 +127,11 @@ pack(uint8_t *ptr, size_t len, const struct param *p)
(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[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 < 8; i++)
for (size_t i = 0; i < 9; i++)
if (cbor_array_push(array, argv[i]) == false)
goto fail;
@ -135,7 +143,7 @@ pack(uint8_t *ptr, size_t len, const struct param *p)
memcpy(ptr, cbor, cbor_len);
fail:
for (size_t i = 0; i < 8; i++)
for (size_t i = 0; i < 9; i++)
if (argv[i])
cbor_decref(&argv[i]);
@ -164,6 +172,7 @@ pack_dummy(uint8_t *ptr, size_t len)
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);
@ -175,6 +184,8 @@ pack_dummy(uint8_t *ptr, size_t len)
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);
@ -188,28 +199,6 @@ pack_dummy(uint8_t *ptr, size_t len)
return blob_len;
}
static fido_dev_t *
prepare_dev(void)
{
fido_dev_t *dev;
fido_dev_io_t io;
memset(&io, 0, sizeof(io));
io.open = dev_open;
io.close = dev_close;
io.read = dev_read;
io.write = dev_write;
if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
&io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
fido_dev_free(&dev);
return NULL;
}
return dev;
}
static void
dev_reset(const struct param *p)
{
@ -217,7 +206,7 @@ dev_reset(const struct param *p)
set_wire_data(p->reset_wire_data.body, p->reset_wire_data.len);
if ((dev = prepare_dev()) == NULL)
if ((dev = open_dev(0)) == NULL)
return;
fido_dev_reset(dev);
@ -235,7 +224,7 @@ dev_get_cbor_info(const struct param *p)
set_wire_data(p->info_wire_data.body, p->info_wire_data.len);
if ((dev = prepare_dev()) == NULL)
if ((dev = open_dev(0)) == NULL)
return;
proto = fido_dev_protocol(dev);
@ -275,6 +264,9 @@ dev_get_cbor_info(const struct param *p)
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));
@ -302,7 +294,7 @@ dev_set_pin(const struct param *p)
set_wire_data(p->set_pin_wire_data.body, p->set_pin_wire_data.len);
if ((dev = prepare_dev()) == NULL)
if ((dev = open_dev(0)) == NULL)
return;
fido_dev_set_pin(dev, p->pin1, NULL);
@ -317,7 +309,7 @@ dev_change_pin(const struct param *p)
set_wire_data(p->change_pin_wire_data.body, p->change_pin_wire_data.len);
if ((dev = prepare_dev()) == NULL)
if ((dev = open_dev(0)) == NULL)
return;
fido_dev_set_pin(dev, p->pin2, p->pin1);
@ -333,7 +325,7 @@ dev_get_retry_count(const struct param *p)
set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len);
if ((dev = prepare_dev()) == NULL)
if ((dev = open_dev(0)) == NULL)
return;
fido_dev_get_retry_count(dev, &n);
@ -342,6 +334,99 @@ dev_get_retry_count(const struct param *p)
fido_dev_free(&dev);
}
static void
dev_get_uv_retry_count(const struct param *p)
{
fido_dev_t *dev;
int n = 0;
set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len);
if ((dev = open_dev(0)) == NULL)
return;
fido_dev_get_uv_retry_count(dev, &n);
consume(&n, sizeof(n));
fido_dev_close(dev);
fido_dev_free(&dev);
}
static void
dev_enable_entattest(const struct param *p)
{
fido_dev_t *dev;
const char *pin;
int r;
set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
if ((dev = open_dev(0)) == NULL)
return;
pin = p->pin1;
if (strlen(pin) == 0)
pin = NULL;
r = fido_dev_enable_entattest(dev, pin);
consume_str(fido_strerr(r));
fido_dev_close(dev);
fido_dev_free(&dev);
}
static void
dev_toggle_always_uv(const struct param *p)
{
fido_dev_t *dev;
const char *pin;
int r;
set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
if ((dev = open_dev(0)) == NULL)
return;
pin = p->pin1;
if (strlen(pin) == 0)
pin = NULL;
r = fido_dev_toggle_always_uv(dev, pin);
consume_str(fido_strerr(r));
fido_dev_close(dev);
fido_dev_free(&dev);
}
static void
dev_force_pin_change(const struct param *p)
{
fido_dev_t *dev;
const char *pin;
int r;
set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
if ((dev = open_dev(0)) == NULL)
return;
pin = p->pin1;
if (strlen(pin) == 0)
pin = NULL;
r = fido_dev_force_pin_change(dev, pin);
consume_str(fido_strerr(r));
fido_dev_close(dev);
fido_dev_free(&dev);
}
static void
dev_set_pin_minlen(const struct param *p)
{
fido_dev_t *dev;
const char *pin;
int r;
set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
if ((dev = open_dev(0)) == NULL)
return;
pin = p->pin1;
if (strlen(pin) == 0)
pin = NULL;
r = fido_dev_set_pin_minlen(dev, strlen(p->pin2), pin);
consume_str(fido_strerr(r));
fido_dev_close(dev);
fido_dev_free(&dev);
}
void
test(const struct param *p)
{
@ -354,6 +439,11 @@ test(const struct param *p)
dev_set_pin(p);
dev_change_pin(p);
dev_get_retry_count(p);
dev_get_uv_retry_count(p);
dev_enable_entattest(p);
dev_toggle_always_uv(p);
dev_force_pin_change(p);
dev_set_pin_minlen(p);
}
void

View File

@ -0,0 +1,249 @@
/*
* Copyright (c) 2020 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "../openbsd-compat/openbsd-compat.h"
#include "mutator_aux.h"
struct param {
int seed;
int dev;
struct blob wiredata;
};
/*
* Sample netlink messages. These are unlikely to get the harness very far in
* terms of coverage, but serve to give libFuzzer a sense of the underlying
* structure.
*/
static const uint8_t sample_netlink_wiredata[] = {
0xd8, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x9d, 0x2e, 0x00, 0x00,
0x01, 0x02, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x6e, 0x66, 0x63, 0x00, 0x06, 0x00, 0x01, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x80, 0x01, 0x06, 0x00,
0x14, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00,
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00,
0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x14, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, 0x00,
0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x00,
0x08, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x14, 0x00, 0x05, 0x00, 0x08, 0x00, 0x01, 0x00,
0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x06, 0x00,
0x08, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x14, 0x00, 0x07, 0x00, 0x08, 0x00, 0x01, 0x00,
0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x08, 0x00,
0x08, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x14, 0x00, 0x09, 0x00, 0x08, 0x00, 0x01, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0a, 0x00,
0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x14, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x01, 0x00,
0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0c, 0x00,
0x08, 0x00, 0x01, 0x00, 0x15, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x14, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x01, 0x00,
0x11, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0e, 0x00,
0x08, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x14, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x01, 0x00,
0x1a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x10, 0x00,
0x08, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x01, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00,
0x08, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x14, 0x00, 0x13, 0x00, 0x08, 0x00, 0x01, 0x00,
0x1e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x07, 0x00,
0x18, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x01, 0x00,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x9d, 0x2e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x9d, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x05, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x09, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x9d, 0x2e, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00,
0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00,
0x06, 0x00, 0x05, 0x00, 0x44, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x20, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x07, 0x00, 0x27, 0x00, 0x00, 0x00,
0x93, 0xb9, 0x25, 0x00
};
struct param *
unpack(const uint8_t *ptr, size_t len)
{
cbor_item_t *item = NULL, **v;
struct cbor_load_result cbor;
struct param *p;
int ok = -1;
if ((p = calloc(1, sizeof(*p))) == NULL ||
(item = cbor_load(ptr, len, &cbor)) == NULL ||
cbor.read != len ||
cbor_isa_array(item) == false ||
cbor_array_is_definite(item) == false ||
cbor_array_size(item) != 3 ||
(v = cbor_array_handle(item)) == NULL)
goto fail;
if (unpack_int(v[0], &p->seed) < 0 ||
unpack_int(v[1], &p->dev) < 0 ||
unpack_blob(v[2], &p->wiredata) < 0)
goto fail;
ok = 0;
fail:
if (ok < 0) {
free(p);
p = NULL;
}
if (item)
cbor_decref(&item);
return p;
}
size_t
pack(uint8_t *ptr, size_t len, const struct param *p)
{
cbor_item_t *argv[3], *array = NULL;
size_t cbor_alloc_len, cbor_len = 0;
unsigned char *cbor = NULL;
memset(argv, 0, sizeof(argv));
if ((array = cbor_new_definite_array(3)) == NULL ||
(argv[0] = pack_int(p->seed)) == NULL ||
(argv[1] = pack_int(p->dev)) == NULL ||
(argv[2] = pack_blob(&p->wiredata)) == NULL)
goto fail;
for (size_t i = 0; i < 3; i++)
if (cbor_array_push(array, argv[i]) == false)
goto fail;
if ((cbor_len = cbor_serialize_alloc(array, &cbor,
&cbor_alloc_len)) > len) {
cbor_len = 0;
goto fail;
}
memcpy(ptr, cbor, cbor_len);
fail:
for (size_t i = 0; i < 3; i++)
if (argv[i])
cbor_decref(&argv[i]);
if (array)
cbor_decref(&array);
free(cbor);
return cbor_len;
}
size_t
pack_dummy(uint8_t *ptr, size_t len)
{
struct param dummy;
uint8_t blob[4096];
size_t blob_len;
memset(&dummy, 0, sizeof(dummy));
dummy.wiredata.len = sizeof(sample_netlink_wiredata);
memcpy(&dummy.wiredata.body, &sample_netlink_wiredata,
dummy.wiredata.len);
assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
if (blob_len > len) {
memcpy(ptr, blob, len);
return len;
}
memcpy(ptr, blob, blob_len);
return blob_len;
}
void
test(const struct param *p)
{
fido_nl_t *nl;
uint32_t target;
prng_init((unsigned int)p->seed);
fido_init(FIDO_DEBUG);
fido_set_log_handler(consume_str);
set_netlink_io_functions(fd_read, fd_write);
set_wire_data(p->wiredata.body, p->wiredata.len);
if ((nl = fido_nl_new()) == NULL)
return;
consume(&nl->fd, sizeof(nl->fd));
consume(&nl->nfc_type, sizeof(nl->nfc_type));
consume(&nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp));
consume(&nl->saddr, sizeof(nl->saddr));
fido_nl_power_nfc(nl, (uint32_t)p->dev);
if (fido_nl_get_nfc_target(nl, (uint32_t)p->dev, &target) == 0)
consume(&target, sizeof(target));
fido_nl_free(&nl);
}
void
mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
{
if (flags & MUTATE_SEED)
p->seed = (int)seed;
if (flags & MUTATE_PARAM)
mutate_int(&p->dev);
if (flags & MUTATE_WIREDATA)
mutate_blob(&p->wiredata);
}

View File

@ -116,13 +116,16 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
struct param *p;
if (size > 4096)
return 0;
if (++test_total % 100000 == 0 && debug) {
double r = (double)test_fail/(double)test_total * 100.0;
fprintf(stderr, "%s: %llu/%llu (%.2f%%)\n", __func__,
test_fail, test_total, r);
}
if (size > 4096 || (p = unpack(data, size)) == NULL)
if ((p = unpack(data, size)) == NULL)
test_fail++;
else {
test(p);

View File

@ -6,15 +6,20 @@
#include <assert.h>
#include <cbor.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fido.h"
#include "mutator_aux.h"
#define HID_DEV_HANDLE 0x68696421
#define NFC_DEV_HANDLE 0x6e666321
int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int);
int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t);
size_t LLVMFuzzerMutate(uint8_t *, size_t, size_t);
static const uint8_t *wire_data_ptr = NULL;
@ -161,30 +166,14 @@ mutate_string(char *s)
s[n] = '\0';
}
void *
dev_open(const char *path)
{
(void)path;
return (void *)0xdeadbeef;
}
void
dev_close(void *handle)
{
assert(handle == (void *)0xdeadbeef);
}
int
dev_read(void *handle, unsigned char *ptr, size_t len, int ms)
/* XXX should fail, but doesn't */
static int
buf_read(unsigned char *ptr, size_t len, int ms)
{
size_t n;
(void)ms;
assert(handle == (void *)0xdeadbeef);
assert(len >= CTAP_MIN_REPORT_LEN && len <= CTAP_MAX_REPORT_LEN);
if (wire_data_len < len)
n = wire_data_len;
else
@ -198,19 +187,143 @@ dev_read(void *handle, unsigned char *ptr, size_t len, int ms)
return (int)n;
}
int
dev_write(void *handle, const unsigned char *ptr, size_t len)
static int
buf_write(const unsigned char *ptr, size_t len)
{
assert(handle == (void *)0xdeadbeef);
consume(ptr, len);
if (uniform_random(400) < 1) {
errno = EIO;
return -1;
}
return (int)len;
}
static void *
hid_open(const char *path)
{
(void)path;
return (void *)HID_DEV_HANDLE;
}
static void
hid_close(void *handle)
{
assert(handle == (void *)HID_DEV_HANDLE);
}
static int
hid_read(void *handle, unsigned char *ptr, size_t len, int ms)
{
assert(handle == (void *)HID_DEV_HANDLE);
assert(len >= CTAP_MIN_REPORT_LEN && len <= CTAP_MAX_REPORT_LEN);
return buf_read(ptr, len, ms);
}
static int
hid_write(void *handle, const unsigned char *ptr, size_t len)
{
assert(handle == (void *)HID_DEV_HANDLE);
assert(len >= CTAP_MIN_REPORT_LEN + 1 &&
len <= CTAP_MAX_REPORT_LEN + 1);
consume(ptr, len);
return buf_write(ptr, len);
}
if (uniform_random(400) < 1)
return -1;
static void *
nfc_open(const char *path)
{
(void)path;
return (int)len;
return (void *)NFC_DEV_HANDLE;
}
static void
nfc_close(void *handle)
{
assert(handle == (void *)NFC_DEV_HANDLE);
}
static int
nfc_read(void *handle, unsigned char *ptr, size_t len, int ms)
{
assert(handle == (void *)NFC_DEV_HANDLE);
assert(len > 0 && len <= 256 + 2);
return buf_read(ptr, len, ms);
}
static int
nfc_write(void *handle, const unsigned char *ptr, size_t len)
{
assert(handle == (void *)NFC_DEV_HANDLE);
assert(len > 0 && len <= 256 + 2);
return buf_write(ptr, len);
}
ssize_t
fd_read(int fd, void *ptr, size_t len)
{
assert(fd != -1);
return buf_read(ptr, len, -1);
}
ssize_t
fd_write(int fd, const void *ptr, size_t len)
{
assert(fd != -1);
return buf_write(ptr, len);
}
fido_dev_t *
open_dev(int nfc)
{
fido_dev_t *dev;
fido_dev_io_t io;
fido_dev_transport_t t;
memset(&io, 0, sizeof(io));
memset(&t, 0, sizeof(t));
if ((dev = fido_dev_new()) == NULL)
return NULL;
if (nfc) {
io.open = nfc_open;
io.close = nfc_close;
io.read = nfc_read;
io.write = nfc_write;
} else {
io.open = hid_open;
io.close = hid_close;
io.read = hid_read;
io.write = hid_write;
}
if (fido_dev_set_io_functions(dev, &io) != FIDO_OK)
goto fail;
if (nfc) {
t.rx = fido_nfc_rx;
t.tx = fido_nfc_tx;
if (fido_dev_set_transport_functions(dev, &t) != FIDO_OK)
goto fail;
}
if (fido_dev_open(dev, "nodev") != FIDO_OK)
goto fail;
return dev;
fail:
fido_dev_free(&dev);
return NULL;
}
void

View File

@ -11,6 +11,16 @@
#include <stdint.h>
#include <cbor.h>
#include "../src/fido.h"
#include "../src/fido/bio.h"
#include "../src/fido/config.h"
#include "../src/fido/credman.h"
#include "../src/fido/eddsa.h"
#include "../src/fido/es256.h"
#include "../src/fido/es256.h"
#include "../src/fido/rs256.h"
#include "../src/netlink.h"
/*
* As of LLVM 10.0.0, MSAN support in libFuzzer was still experimental.
* We therefore have to be careful when using our custom mutator, or
@ -73,11 +83,11 @@ void mutate_int(int *);
void mutate_blob(struct blob *);
void mutate_string(char *);
void *dev_open(const char *);
void dev_close(void *);
ssize_t fd_read(int, void *, size_t);
ssize_t fd_write(int, const void *, size_t);
fido_dev_t *open_dev(int);
void set_wire_data(const uint8_t *, size_t);
int dev_read(void *, unsigned char *, size_t, int);
int dev_write(void *, const unsigned char *, size_t);
void prng_init(unsigned long);
unsigned long prng_uint32(void);

Binary file not shown.

View File

@ -3,40 +3,48 @@ Filename Regions Missed Regions Cover Funct
fuzz/prng.c 31 0 100.00% 2 0 100.00% 49 0 100.00%
fuzz/uniform_random.c 7 1 85.71% 1 0 100.00% 23 1 95.65%
fuzz/wrap.c 6 0 100.00% 1 0 100.00% 7 0 100.00%
openbsd-compat/explicit_bzero.c 4 0 100.00% 1 0 100.00% 12 0 100.00%
openbsd-compat/explicit_bzero.c 4 0 100.00% 1 0 100.00% 13 0 100.00%
openbsd-compat/freezero.c 4 0 100.00% 1 0 100.00% 6 0 100.00%
openbsd-compat/recallocarray.c 41 7 82.93% 1 0 100.00% 49 7 85.71%
openbsd-compat/strlcat.c 12 1 91.67% 1 0 100.00% 25 1 96.00%
openbsd-compat/timingsafe_bcmp.c 4 0 100.00% 1 0 100.00% 8 0 100.00%
src/aes256.c 56 0 100.00% 2 0 100.00% 82 0 100.00%
src/assert.c 566 24 95.76% 53 1 98.11% 900 50 94.44%
src/aes256.c 115 4 96.52% 8 0 100.00% 175 14 92.00%
src/assert.c 593 34 94.27% 58 2 96.55% 914 52 94.31%
src/authkey.c 44 0 100.00% 5 0 100.00% 75 0 100.00%
src/bio.c 418 20 95.22% 49 2 95.92% 661 22 96.67%
src/blob.c 39 0 100.00% 7 0 100.00% 73 0 100.00%
src/bio.c 419 20 95.23% 49 2 95.92% 663 22 96.68%
src/blob.c 53 3 94.34% 10 0 100.00% 96 7 92.71%
src/buf.c 8 1 87.50% 2 0 100.00% 20 1 95.00%
src/cbor.c 883 6 99.32% 52 0 100.00% 1364 17 98.75%
src/cred.c 536 23 95.71% 57 1 98.25% 836 29 96.53%
src/credman.c 385 18 95.32% 38 0 100.00% 595 16 97.31%
src/dev.c 334 90 73.05% 33 8 75.76% 466 140 69.96%
src/ecdh.c 68 0 100.00% 2 0 100.00% 104 0 100.00%
src/eddsa.c 54 0 100.00% 8 0 100.00% 79 0 100.00%
src/err.c 112 8 92.86% 1 0 100.00% 116 8 93.10%
src/es256.c 280 0 100.00% 16 0 100.00% 398 0 100.00%
src/hid.c 16 16 0.00% 8 8 0.00% 38 38 0.00%
src/hid_linux.c 235 235 0.00% 19 19 0.00% 416 416 0.00%
src/info.c 152 0 100.00% 34 0 100.00% 319 0 100.00%
src/io.c 156 9 94.23% 10 0 100.00% 236 11 95.34%
src/iso7816.c 18 1 94.44% 5 0 100.00% 47 0 100.00%
src/log.c 34 2 94.12% 5 1 80.00% 50 3 94.00%
src/pin.c 248 0 100.00% 16 0 100.00% 365 0 100.00%
src/reset.c 19 0 100.00% 3 0 100.00% 22 0 100.00%
src/rs256.c 102 6 94.12% 8 0 100.00% 140 13 90.71%
src/u2f.c 491 9 98.17% 15 0 100.00% 774 18 97.67%
src/cbor.c 984 34 96.54% 53 0 100.00% 1425 49 96.56%
src/compress.c 34 4 88.24% 3 0 100.00% 30 3 90.00%
src/config.c 93 2 97.85% 10 0 100.00% 148 3 97.97%
src/cred.c 563 23 95.91% 63 1 98.41% 880 26 97.05%
src/credman.c 370 10 97.30% 38 0 100.00% 596 16 97.32%
src/dev.c 421 112 73.40% 42 8 80.95% 561 162 71.12%
src/ecdh.c 117 2 98.29% 4 0 100.00% 161 5 96.89%
src/eddsa.c 54 0 100.00% 8 0 100.00% 77 0 100.00%
src/err.c 122 10 91.80% 1 0 100.00% 126 10 92.06%
src/es256.c 280 0 100.00% 16 0 100.00% 394 0 100.00%
src/hid.c 60 16 73.33% 12 8 33.33% 133 38 71.43%
src/hid_linux.c 159 159 0.00% 14 14 0.00% 281 281 0.00%
src/hid_unix.c 30 27 10.00% 2 1 50.00% 52 40 23.08%
src/info.c 154 0 100.00% 35 0 100.00% 324 0 100.00%
src/io.c 156 7 95.51% 10 0 100.00% 228 11 95.18%
src/iso7816.c 18 1 94.44% 5 0 100.00% 42 0 100.00%
src/largeblob.c 511 23 95.50% 30 0 100.00% 759 51 93.28%
src/log.c 39 5 87.18% 7 1 85.71% 73 4 94.52%
src/netlink.c 327 15 95.41% 40 0 100.00% 565 35 93.81%
src/nfc_linux.c 304 187 38.49% 23 14 39.13% 520 309 40.58%
src/pin.c 406 3 99.26% 27 0 100.00% 583 3 99.49%
src/random.c 6 1 83.33% 1 0 100.00% 8 1 87.50%
src/reset.c 24 0 100.00% 3 0 100.00% 27 0 100.00%
src/rs256.c 102 4 96.08% 8 0 100.00% 138 6 95.65%
src/u2f.c 473 6 98.73% 15 0 100.00% 745 9 98.79%
Files which contain no functions:
openbsd-compat/time.h 0 0 - 0 0 - 0 0 -
openbsd-compat/openbsd-compat.h 0 0 - 0 0 - 0 0 -
src/extern.h 0 0 - 0 0 - 0 0 -
src/fido.h 0 0 - 0 0 - 0 0 -
src/fido/err.h 0 0 - 0 0 - 0 0 -
src/fido/param.h 0 0 - 0 0 - 0 0 -
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL 5359 477 91.10% 456 40 91.23% 8349 791 90.53%
TOTAL 7148 722 89.90% 611 51 91.65% 10999 1167 89.39%

View File

@ -61,16 +61,6 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#define WIREDATA_CTAP_CBOR_RESET \
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_AUTHKEY \
0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0x51, 0x00, \
0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, \
@ -80,7 +70,7 @@
0x47, 0xfe, 0x4b, 0x87, 0xe5, 0xcf, 0x3f, 0x05, \
0x0b, 0x39, 0xda, 0x17, 0x49, 0x22, 0x58, 0x20, \
0x15, 0x1b, 0xbe, 0x08, 0x78, 0x60, 0x4d, 0x3c, \
0x00, 0x22, 0x00, 0x03, 0x00, 0x3f, 0xf1, 0x60, \
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, \
@ -566,4 +556,78 @@
0x6e, 0x67, 0x65, 0x72, 0x33, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#define WIREDATA_CTAP_CBOR_LARGEBLOB_GET_ARRAY \
0x89, 0xc9, 0x8d, 0x28, 0x90, 0x01, 0xe6, 0x00, \
0xa1, 0x01, 0x59, 0x01, 0xe0, 0x81, 0xa3, 0x01, \
0x59, 0x01, 0xb8, 0xb3, 0x26, 0x24, 0x99, 0xde, \
0x06, 0x3f, 0xca, 0xde, 0x98, 0x8d, 0x9d, 0xc5, \
0x3f, 0x26, 0x6c, 0xc7, 0x40, 0x93, 0xc4, 0x88, \
0x06, 0x51, 0x4f, 0xb9, 0x61, 0xf2, 0xc9, 0x8d, \
0xbc, 0xce, 0x79, 0x08, 0xec, 0x90, 0xc5, 0x5b, \
0xe5, 0x0a, 0x72, 0x08, 0x7b, 0xe1, 0xf9, 0x16, \
0x89, 0xc9, 0x8d, 0x28, 0x00, 0x06, 0x8b, 0x76, \
0x32, 0xa0, 0xae, 0x55, 0xb2, 0x39, 0x71, 0xce, \
0x34, 0x4b, 0x6e, 0x6b, 0x89, 0xa6, 0x5e, 0x69, \
0x07, 0xac, 0xf6, 0x01, 0x3c, 0xba, 0x45, 0x7a, \
0x75, 0x25, 0x3a, 0xbd, 0x95, 0x22, 0x9d, 0xc3, \
0xe4, 0x42, 0x31, 0x5c, 0xb5, 0xf4, 0x64, 0x6a, \
0x56, 0x1d, 0xab, 0xc7, 0x6e, 0x96, 0x75, 0xe7, \
0xb3, 0x22, 0x0b, 0x82, 0xac, 0x57, 0x78, 0xdf, \
0x89, 0xc9, 0x8d, 0x28, 0x01, 0x57, 0x06, 0xc5, \
0x4b, 0x61, 0x0b, 0x4d, 0xa1, 0x66, 0xa0, 0x89, \
0xad, 0x19, 0x8f, 0xd8, 0x96, 0x55, 0x22, 0x5f, \
0xca, 0x2e, 0xc1, 0xd7, 0xbd, 0xa1, 0x83, 0x66, \
0x4d, 0x85, 0xcb, 0x01, 0x60, 0x3f, 0xf7, 0xf7, \
0xa3, 0x7a, 0xfa, 0x99, 0xa0, 0x1e, 0x25, 0x90, \
0xd0, 0xd0, 0x3b, 0x54, 0x90, 0x77, 0x94, 0xa6, \
0x88, 0xea, 0xc3, 0x6b, 0xa0, 0x59, 0x5e, 0x69, \
0x89, 0xc9, 0x8d, 0x28, 0x02, 0x78, 0x0b, 0x2b, \
0xab, 0x5b, 0x04, 0x2f, 0x78, 0x15, 0x86, 0x2b, \
0x0f, 0x63, 0xb2, 0xd7, 0xc9, 0xe9, 0xac, 0x0e, \
0xbc, 0x17, 0xe4, 0x19, 0x88, 0xe0, 0xe6, 0x13, \
0xf8, 0x15, 0x08, 0xa7, 0xe1, 0x6e, 0x71, 0x5c, \
0xef, 0x3e, 0xc1, 0x0f, 0x74, 0xdb, 0xdc, 0x52, \
0x9c, 0xfc, 0xe9, 0xa9, 0xf3, 0x0d, 0x52, 0xbc, \
0x0c, 0xe8, 0xba, 0xd1, 0x76, 0x46, 0x87, 0xb5, \
0x89, 0xc9, 0x8d, 0x28, 0x03, 0x30, 0xe6, 0x9d, \
0xa1, 0x2b, 0xa5, 0x9e, 0x3b, 0x86, 0xb3, 0x5f, \
0xe3, 0x81, 0xa6, 0x76, 0x32, 0x9d, 0xf9, 0xc5, \
0x07, 0x93, 0xb3, 0xdf, 0x64, 0xe2, 0x78, 0x9c, \
0x00, 0xc7, 0x86, 0x79, 0xd6, 0x67, 0xa2, 0xfb, \
0xf2, 0x8d, 0xea, 0xe9, 0xc8, 0xfc, 0x43, 0xd2, \
0x0f, 0x2f, 0x7d, 0x9d, 0xd3, 0x8f, 0x9c, 0xdd, \
0xa2, 0x9f, 0x42, 0x76, 0x40, 0xcc, 0x4a, 0xd0, \
0x89, 0xc9, 0x8d, 0x28, 0x04, 0xb4, 0x87, 0x18, \
0x06, 0xc3, 0xc7, 0x89, 0x98, 0x72, 0xcc, 0x1a, \
0xd1, 0xd8, 0x78, 0xb9, 0x75, 0x0b, 0x92, 0xe3, \
0xcc, 0xed, 0x38, 0x39, 0x4b, 0xa9, 0xcf, 0x30, \
0xd6, 0xb5, 0xa1, 0x3f, 0xfa, 0x4f, 0x29, 0x99, \
0xa9, 0x03, 0x77, 0xf6, 0x53, 0xfa, 0xd8, 0x32, \
0xce, 0xf4, 0xf6, 0x0a, 0x3c, 0xe8, 0x9c, 0x3d, \
0xaa, 0xe0, 0x7b, 0x2c, 0xa5, 0x28, 0xe1, 0xdd, \
0x89, 0xc9, 0x8d, 0x28, 0x05, 0x51, 0xbf, 0xe1, \
0xd4, 0xf5, 0x5e, 0x38, 0x2c, 0xec, 0xab, 0xdd, \
0xb8, 0x5c, 0x13, 0x43, 0x62, 0xc2, 0xb6, 0x02, \
0x18, 0xce, 0x9a, 0x62, 0x67, 0x6a, 0xeb, 0x99, \
0xf6, 0x2f, 0xf1, 0xf1, 0xec, 0x3e, 0x74, 0xfa, \
0xf8, 0x16, 0x43, 0xea, 0x1e, 0xef, 0x5d, 0x37, \
0x6c, 0x13, 0xf9, 0x7f, 0x65, 0x09, 0xab, 0x60, \
0x38, 0xda, 0x0f, 0xe7, 0xfa, 0x9e, 0x17, 0x10, \
0x89, 0xc9, 0x8d, 0x28, 0x06, 0xdc, 0x4c, 0x4d, \
0xae, 0x5c, 0xb4, 0x0d, 0x6b, 0x05, 0x6d, 0x25, \
0x3f, 0x78, 0x5d, 0xf3, 0x34, 0x33, 0xa4, 0x89, \
0x34, 0x0e, 0x88, 0x66, 0x40, 0x57, 0x6b, 0x34, \
0x83, 0xfd, 0x39, 0xe7, 0xfb, 0x84, 0x09, 0xb3, \
0x16, 0x8f, 0x80, 0xdf, 0x1b, 0xe0, 0x02, 0x4c, \
0xde, 0x31, 0x2a, 0x32, 0x58, 0x5b, 0xa3, 0x23, \
0x8e, 0x2a, 0xa6, 0xaf, 0x03, 0x19, 0x02, 0x7a, \
0x89, 0xc9, 0x8d, 0x28, 0x07, 0xf8, 0xbf, 0xa6, \
0xad, 0xf9, 0xd1, 0xdc, 0xbd, 0x6e, 0xb3, 0xc1, \
0xfb, 0x65, 0xd8, 0x5f, 0x2e, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#endif /* _WIREDATA_FIDO2_H */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Yubico AB. All rights reserved.
* 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.
*/
@ -9,8 +9,6 @@
#include <openssl/sha.h>
#include <cbor.h>
#include <fido.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@ -60,6 +58,23 @@ WRAP(char *,
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),
@ -68,7 +83,8 @@ WRAP(EVP_CIPHER_CTX *,
1
)
WRAP(int, EVP_EncryptInit_ex,
WRAP(int,
EVP_EncryptInit_ex,
(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl,
const unsigned char *key, const unsigned char *iv),
0,
@ -93,6 +109,15 @@ WRAP(int,
1
)
WRAP(int,
EVP_CipherInit,
(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
const unsigned char *key, const unsigned char *iv, int enc),
0,
(ctx, cipher, key, iv, enc),
1
)
WRAP(int,
EVP_DecryptInit_ex,
(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl,
@ -336,6 +361,14 @@ WRAP(EVP_PKEY_CTX *,
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),

View File

@ -24,8 +24,11 @@ EC_KEY_get0_private_key
EC_KEY_new_by_curve_name
EC_POINT_get_affine_coordinates_GFp
EC_POINT_new
EVP_Cipher
EVP_CIPHER_CTX_ctrl
EVP_CIPHER_CTX_new
EVP_CIPHER_CTX_set_padding
EVP_CipherInit
EVP_DecryptInit_ex
EVP_DecryptUpdate
EVP_DigestVerifyInit
@ -35,6 +38,7 @@ EVP_MD_CTX_new
EVP_PKEY_assign
EVP_PKEY_CTX_new
EVP_PKEY_CTX_new_id
EVP_PKEY_derive
EVP_PKEY_derive_init
EVP_PKEY_derive_set_peer
EVP_PKEY_get0_EC_KEY

View File

@ -29,9 +29,11 @@ list(APPEND MAN_SOURCES
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
@ -50,6 +52,8 @@ list(APPEND MAN_ALIAS
es256_pk_new es256_pk_to_EVP_PKEY
fido_assert_new fido_assert_authdata_len
fido_assert_new fido_assert_authdata_ptr
fido_assert_new fido_assert_blob_len
fido_assert_new fido_assert_blob_ptr
fido_assert_new fido_assert_clientdata_hash_len
fido_assert_new fido_assert_clientdata_hash_ptr
fido_assert_new fido_assert_count
@ -59,6 +63,8 @@ list(APPEND MAN_ALIAS
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
@ -72,6 +78,7 @@ list(APPEND MAN_ALIAS
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
@ -104,6 +111,7 @@ list(APPEND MAN_ALIAS
fido_cbor_info_new fido_cbor_info_extensions_ptr
fido_cbor_info_new fido_cbor_info_free
fido_cbor_info_new fido_cbor_info_maxmsgsiz
fido_cbor_info_new fido_cbor_info_maxcredbloblen
fido_cbor_info_new fido_cbor_info_maxcredcntlst;
fido_cbor_info_new fido_cbor_info_maxcredidlen;
fido_cbor_info_new fido_cbor_info_fwversion
@ -117,16 +125,21 @@ list(APPEND MAN_ALIAS
fido_cbor_info_new fido_dev_get_cbor_info
fido_cred_new fido_cred_authdata_len
fido_cred_new fido_cred_authdata_ptr
fido_cred_new fido_cred_authdata_raw_len
fido_cred_new fido_cred_authdata_raw_ptr
fido_cred_new fido_cred_clientdata_hash_len
fido_cred_new fido_cred_clientdata_hash_ptr
fido_cred_new fido_cred_display_name
fido_cred_new fido_cred_flags
fido_cred_new fido_cred_sigcount
fido_cred_new fido_cred_fmt
fido_cred_new fido_cred_free
fido_cred_new fido_cred_id_len
fido_cred_new fido_cred_id_ptr
fido_cred_new fido_cred_aaguid_len
fido_cred_new fido_cred_aaguid_ptr
fido_cred_new fido_cred_largeblob_key_len
fido_cred_new fido_cred_largeblob_key_ptr
fido_cred_new fido_cred_prot
fido_cred_new fido_cred_pubkey_len
fido_cred_new fido_cred_pubkey_ptr
@ -159,6 +172,7 @@ list(APPEND MAN_ALIAS
fido_credman_metadata_new fido_credman_rp_name
fido_credman_metadata_new fido_credman_rp_new
fido_cred_set_authdata fido_cred_set_authdata_raw
fido_cred_set_authdata fido_cred_set_blob
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
@ -170,6 +184,10 @@ list(APPEND MAN_ALIAS
fido_cred_set_authdata fido_cred_set_user
fido_cred_set_authdata fido_cred_set_uv
fido_cred_set_authdata fido_cred_set_x509
fido_dev_enable_entattest fido_dev_toggle_always_uv
fido_dev_enable_entattest fido_dev_force_pin_change
fido_dev_enable_entattest fido_dev_set_pin_minlen
fido_dev_get_touch_begin fido_dev_get_touch_status
fido_dev_info_manifest fido_dev_info_free
fido_dev_info_manifest fido_dev_info_manufacturer_string
fido_dev_info_manifest fido_dev_info_new
@ -186,14 +204,23 @@ list(APPEND MAN_ALIAS
fido_dev_open fido_dev_force_u2f
fido_dev_open fido_dev_free
fido_dev_open fido_dev_is_fido2
fido_dev_open fido_dev_supports_cred_prot
fido_dev_open fido_dev_supports_pin
fido_dev_open fido_dev_major
fido_dev_open fido_dev_minor
fido_dev_open fido_dev_new
fido_dev_open fido_dev_protocol
fido_dev_open fido_dev_supports_cred_prot
fido_dev_open fido_dev_supports_credman
fido_dev_open fido_dev_supports_pin
fido_dev_open fido_dev_supports_uv
fido_dev_open fido_dev_has_uv
fido_dev_set_pin fido_dev_get_retry_count
fido_dev_set_pin fido_dev_get_uv_retry_count
fido_dev_set_pin fido_dev_reset
fido_dev_set_io_functions fido_dev_set_sigmask
fido_dev_largeblob_get fido_dev_largeblob_set
fido_dev_largeblob_get fido_dev_largeblob_remove
fido_dev_largeblob_get fido_dev_largeblob_get_array
fido_dev_largeblob_get fido_dev_largeblob_set_array
rs256_pk_new rs256_pk_free
rs256_pk_new rs256_pk_from_ptr
rs256_pk_new rs256_pk_from_RSA

View File

@ -11,7 +11,7 @@
.Sh SYNOPSIS
.Nm
.Fl G
.Op Fl dhpruv
.Op Fl bdhpruv
.Op Fl t Ar option
.Op Fl i Ar input_file
.Op Fl o Ar output_file
@ -75,6 +75,10 @@ If
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.
@ -111,6 +115,9 @@ 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 FIDO2.1.
.It Fl t Ar option
Toggles a key/value
.Ar option ,
@ -219,6 +226,10 @@ 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,

View File

@ -11,7 +11,7 @@
.Sh SYNOPSIS
.Nm
.Fl M
.Op Fl dhqruv
.Op Fl bdhqruv
.Op Fl c Ar cred_protect
.Op Fl i Ar input_file
.Op Fl o Ar output_file
@ -91,9 +91,19 @@ to make a new credential on
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 .
.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 .
@ -131,6 +141,9 @@ is specified,
will fail.
.It Fl r
Create a resident credential.
Resident credentials are called
.Dq discoverable credentials
in FIDO2.1.
.It Fl u
Create a U2F credential.
By default,
@ -212,6 +225,10 @@ credential id (base64 blob);
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,
@ -244,3 +261,7 @@ verify it, and save the id and the public key of the credential in
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.

View File

@ -10,14 +10,49 @@
.Nd find and manage a FIDO 2 authenticator
.Sh SYNOPSIS
.Nm
.Op Fl CR
.Fl C
.Op Fl d
.Ar device
.Nm
.Fl D
.Op Fl de
.Op Fl d
.Fl i
.Ar id
.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 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
@ -26,13 +61,41 @@
.Ar device
.Nm
.Fl L
.Op Fl der
.Op Fl bder
.Op Fl k Ar rp_id
.Op device
.Nm
.Fl R
.Op Fl d
.Ar device
.Nm
.Fl S
.Op Fl de
.Op Fl i Ar template_id Fl n Ar template_name
.Op Fl adeu
.Ar device
.Nm
.Fl S
.Op Fl d
.Fl i Ar template_id
.Fl n Ar template_name
.Nm
.Fl S
.Op Fl d
.Fl l Ar pin_length
.Ar device
.Nm
.Fl S
.Fl b
.Op Fl d
.Fl k Ar key_path
.Ar blob_path
.Ar device
.Nm
.Fl S
.Fl b
.Op Fl d
.Fl n Ar rp_id
.Op Fl i Ar cred_id
.Ar blob_path
.Ar device
.Nm
.Fl V
@ -55,6 +118,34 @@ where
.Ar id
is the credential's base64-encoded id.
The user will be prompted for the PIN.
.It Fl D Fl b Fl k Ar key_path Ar device
Deletes a
.Dq largeBlob
encrypted with
.Ar key_path
from
.Ar device ,
where
.Ar key_path
must hold the blob's base64-encoded encryption key.
A PIN or equivalent user-verification gesture is required.
.It Fl D Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar device
Deletes a
.Dq largeBlob
corresponding to
.Ar rp_id
from
.Ar device .
If
.Ar rp_id
has multiple credentials enrolled on
.Ar device ,
the credential ID must be specified using
.Fl i Ar cred_id ,
where
.Ar cred_id
is a base64-encoded blob.
A PIN or equivalent user-verification gesture is required.
.It Fl D Fl e Fl i Ar id Ar device
Deletes the biometric enrollment specified by
.Ar id
@ -64,6 +155,43 @@ where
.Ar id
is the enrollment's template base64-encoded id.
The user will be prompted for the PIN.
.It Fl D Fl u Ar device
Disables the FIDO 2.1
.Dq user verification always
feature on
.Ar device .
.It Fl G Fl b Fl k Ar key_path Ar blob_path Ar device
Gets a FIDO 2.1
.Dq largeBlob
encrypted with
.Ar key_path
from
.Ar device ,
where
.Ar key_path
must hold the blob's base64-encoded encryption key.
The blob is written to
.Ar blob_path .
A PIN or equivalent user-verification gesture is required.
.It Fl G Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar blob_path Ar device
Gets a FIDO 2.1
.Dq largeBlob
associated with
.Ar rp_id
from
.Ar device .
If
.Ar rp_id
has multiple credentials enrolled on
.Ar device ,
the credential ID must be specified using
.Fl i Ar cred_id ,
where
.Ar cred_id
is a base64-encoded blob.
The blob is written to
.Ar blob_path .
A PIN or equivalent user-verification gesture is required.
.It Fl I Ar device
Retrieves information on
.Ar device .
@ -85,6 +213,12 @@ is a base64-encoded credential id.
The user will be prompted for the PIN.
.It Fl L
Produces a list of authenticators found by the operating system.
.It Fl L Fl b Ar device
Produces a list of FIDO 2.1
.Dq largeBlobs
on
.Ar device .
A PIN or equivalent user-verification gesture is required.
.It Fl L Fl e Ar device
Produces a list of biometric enrollments on
.Ar device .
@ -109,6 +243,43 @@ will NOT prompt for confirmation.
Sets the PIN of
.Ar device .
The user will be prompted for the PIN.
.It Fl S Fl a Ar device
Enables FIDO 2.1 Enterprise Attestation on
.Ar device .
.It Fl S Fl b Fl k Ar key_path Ar blob_path Ar device
Sets
.Ar blob_path
as a FIDO 2.1
.Dq largeBlob
encrypted with
.Ar key_path
on
.Ar device ,
where
.Ar blob_path
holds the blob's plaintext, and
.Ar key_path
the blob's base64-encoded encryption.
A PIN or equivalent user-verification gesture is required.
.It Fl S Fl b Fl n Ar rp_id Oo Fl i Ar cred_id Oc Ar blob_path Ar device
Sets
.Ar blob_path
as a FIDO 2.1
.Dq largeBlob
associated with
.Ar rp_id
on
.Ar device .
If
.Ar rp_id
has multiple credentials enrolled on
.Ar device ,
the credential ID must be specified using
.Fl i Ar cred_id ,
where
.Ar cred_id
is a base64-encoded blob.
A PIN or equivalent user-verification gesture is required.
.It Fl S Fl e Ar device
Performs a new biometric enrollment on
.Ar device .
@ -126,6 +297,21 @@ is base64-encoded and
.Ar template_name
is a UTF-8 string.
The user will be prompted for the PIN.
.It Fl S Fl f Ar device
Forces a PIN change on
.Ar device .
The user will be prompted for the PIN.
.It Fl S Fl l Ar pin_length Ar device
Sets the minimum PIN length of
.Ar device
to
.Ar pin_length .
The user will be prompted for the PIN.
.It Fl S Fl u Ar device
Enables the FIDO 2.1
.Dq user verification always
feature on
.Ar device .
.It Fl V
Prints version information.
.It Fl d
@ -158,3 +344,12 @@ 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 FIDO2.1.
.Pp
Whether the FIDO 2.1
.Dq user verification always
feature is activated or deactivated after an authenticator reset
is vendor-specific.

View File

@ -14,14 +14,18 @@
.Nm fido_assert_user_icon ,
.Nm fido_assert_user_name ,
.Nm fido_assert_authdata_ptr ,
.Nm fido_assert_blob_ptr ,
.Nm fido_assert_clientdata_hash_ptr ,
.Nm fido_assert_hmac_secret_ptr ,
.Nm fido_assert_largeblob_key_ptr ,
.Nm fido_assert_user_id_ptr ,
.Nm fido_assert_sig_ptr ,
.Nm fido_assert_id_ptr ,
.Nm fido_assert_authdata_len ,
.Nm fido_assert_blob_len ,
.Nm fido_assert_clientdata_hash_len ,
.Nm fido_assert_hmac_secret_len ,
.Nm fido_assert_largeblob_key_len ,
.Nm fido_assert_user_id_len ,
.Nm fido_assert_sig_len ,
.Nm fido_assert_id_len ,
@ -49,8 +53,12 @@
.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"
.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"
@ -61,8 +69,12 @@
.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"
.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"
@ -143,19 +155,26 @@ NUL-terminated UTF-8 strings.
The
.Fn fido_assert_user_id_ptr ,
.Fn fido_assert_authdata_ptr ,
.Fn fido_assert_blob_ptr ,
.Fn fido_assert_hmac_secret_ptr ,
.Fn fido_assert_largeblob_key_ptr ,
.Fn fido_assert_sig_ptr ,
and
.Fn fido_assert_id_ptr
functions return pointers to the user ID, authenticator data,
hmac-secret, signature, and credential ID attributes of statement
functions return pointers to the user ID, CBOR-encoded
authenticator data, cred blob, hmac-secret,
.Dq largeBlobKey ,
signature, and credential ID attributes of statement
.Fa idx
in
.Fa assert .
.Pp
The
.Fn fido_assert_user_id_len ,
.Fn fido_assert_authdata_len ,
.Fn fido_assert_blob_len ,
.Fn fido_assert_hmac_secret_len ,
.Fn fido_assert_largeblob_key_len ,
.Fn fido_assert_sig_len ,
and
.Fn fido_assert_id_len
@ -192,12 +211,18 @@ function returns a pointer to the client data hash of
The corresponding length can be obtained by
.Fn fido_assert_clientdata_hash_len .
.Sh RETURN VALUES
The authenticator data returned by
.Fn fido_assert_authdata_ptr
is a CBOR-encoded byte string, as obtained from the authenticator.
.Pp
The
.Fn fido_assert_user_display_name ,
.Fn fido_assert_user_icon ,
.Fn fido_assert_user_name ,
.Fn fido_assert_authdata_ptr ,
.Fn fido_assert_clientdata_hash_ptr ,
.Fn fido_assert_hmac_secret_ptr ,
.Fn fido_assert_largeblob_key_ptr ,
.Fn fido_assert_user_id_ptr ,
and
.Fn fido_assert_sig_ptr
@ -214,4 +239,5 @@ qualifier is invoked.
.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_get_assert 3 ,
.Xr fido_dev_largeblob_get 3

View File

@ -12,6 +12,7 @@
.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 ,
@ -39,6 +40,8 @@ typedef enum {
.Ft int
.Fn fido_assert_set_hmac_salt "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
.Ft int
.Fn fido_assert_set_hmac_secret "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
.Ft int
.Fn fido_assert_set_up "fido_assert_t *assert" "fido_opt_t up"
.Ft int
.Fn fido_assert_set_uv "fido_assert_t *assert" "fido_opt_t uv"
@ -100,9 +103,10 @@ Alternatively, a raw binary blob may be passed to
.Fn fido_assert_set_authdata_raw .
.Pp
The
.Fn fido_assert_set_clientdata_hash
.Fn fido_assert_set_clientdata_hash ,
.Fn fido_assert_set_hmac_salt ,
and
.Fn fido_assert_set_hmac_salt
.Fn fido_assert_set_hmac_secret
functions set the client data hash and hmac-salt parts of
.Fa assert
to
@ -136,8 +140,11 @@ function sets the extensions of
to the bitmask
.Fa flags .
At the moment, only the
.Dv FIDO_EXT_HMAC_SECRET
extension is supported.
.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
@ -178,6 +185,11 @@ set of functions can be found in the
.Pa examples/assert.c
file shipped with
.Em libfido2 .
.Pp
.Fn fido_assert_set_hmac_secret
is not normally useful in a FIDO client or server \(em it is provided
to enable testing other functionality that relies on retrieving the
HMAC secret from an assertion obtained from an authenticator.
.Sh RETURN VALUES
The
.Nm

View File

@ -58,6 +58,8 @@
.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"
@ -137,6 +139,13 @@ 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

View File

@ -15,23 +15,28 @@
.Nm fido_cred_user_name ,
.Nm fido_cred_display_name ,
.Nm fido_cred_authdata_ptr ,
.Nm fido_cred_authdata_raw_ptr ,
.Nm fido_cred_clientdata_hash_ptr ,
.Nm fido_cred_id_ptr ,
.Nm fido_cred_aaguid_ptr ,
.Nm fido_cred_largeblob_key_ptr ,
.Nm fido_cred_pubkey_ptr ,
.Nm fido_cred_sig_ptr ,
.Nm fido_cred_user_id_ptr ,
.Nm fido_cred_x5c_ptr ,
.Nm fido_cred_authdata_len ,
.Nm fido_cred_authdata_raw_len ,
.Nm fido_cred_clientdata_hash_len ,
.Nm fido_cred_id_len ,
.Nm fido_cred_aaguid_len ,
.Nm fido_cred_largeblob_key_len ,
.Nm fido_cred_pubkey_len ,
.Nm fido_cred_sig_len ,
.Nm fido_cred_user_id_len ,
.Nm fido_cred_x5c_len ,
.Nm fido_cred_type ,
.Nm fido_cred_flags
.Nm fido_cred_flags ,
.Nm fido_cred_sigcount
.Nd FIDO 2 credential API
.Sh SYNOPSIS
.In fido.h
@ -54,12 +59,16 @@
.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"
@ -70,12 +79,16 @@
.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"
@ -87,6 +100,8 @@
.Fn fido_cred_type "const fido_cred_t *cred"
.Ft uint8_t
.Fn fido_cred_flags "const fido_cred_t *cred"
.Ft uint32_t
.Fn fido_cred_sigcount "const fido_cred_t *cred"
.Sh DESCRIPTION
FIDO 2 credentials are abstracted in
.Em libfido2
@ -163,25 +178,30 @@ or NULL if the respective entry is not set.
.Pp
The
.Fn fido_cred_authdata_ptr ,
.Fn fido_cred_authdata_raw_ptr ,
.Fn fido_cred_clientdata_hash_ptr ,
.Fn fido_cred_id_ptr ,
.Fn fido_cred_aaguid_ptr ,
.Fn fido_cred_largeblob_key_ptr ,
.Fn fido_cred_pubkey_ptr ,
.Fn fido_cred_sig_ptr ,
.Fn fido_cred_user_id_ptr ,
and
.Fn fido_cred_x5c_ptr
functions return pointers to the authenticator data, client data
hash, ID, authenticator attestation GUID, public key, signature,
user ID, and x509 certificate parts of
functions return pointers to the CBOR-encoded and raw authenticator
data, client data hash, ID, authenticator attestation GUID,
.Dq largeBlobKey ,
public key, signature, user ID, and x509 certificate parts of
.Fa cred ,
or NULL if the respective entry is not set.
.Pp
The corresponding length can be obtained by
.Fn fido_cred_authdata_len ,
.Fn fido_cred_authdata_raw_len ,
.Fn fido_cred_clientdata_hash_len ,
.Fn fido_cred_id_len ,
.Fn fido_cred_aaguid_len ,
.Fn fido_cred_largeblob_key_len ,
.Fn fido_cred_pubkey_len ,
.Fn fido_cred_sig_len ,
.Fn fido_cred_user_id_len ,
@ -200,10 +220,17 @@ 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 ,
@ -211,6 +238,7 @@ If not NULL, pointers returned by
.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
@ -225,4 +253,5 @@ qualifier is invoked.
.Xr fido_cred_set_authdata 3 ,
.Xr fido_cred_verify 3 ,
.Xr fido_credman_metadata_new 3 ,
.Xr fido_dev_largeblob_get 3 ,
.Xr fido_dev_make_cred 3

View File

@ -14,6 +14,7 @@
.Nm fido_cred_set_rp ,
.Nm fido_cred_set_user ,
.Nm fido_cred_set_extensions ,
.Nm fido_cred_set_blob ,
.Nm fido_cred_set_prot ,
.Nm fido_cred_set_rk ,
.Nm fido_cred_set_uv ,
@ -46,6 +47,8 @@ typedef enum {
.Ft int
.Fn fido_cred_set_extensions "fido_cred_t *cred" "int flags"
.Ft int
.Fn fido_cred_set_blob "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
.Ft int
.Fn fido_cred_set_prot "fido_cred_t *cred" "int prot"
.Ft int
.Fn fido_cred_set_rk "fido_cred_t *cred" "fido_opt_t rk"
@ -151,9 +154,11 @@ function sets the extensions of
to the bitmask
.Fa flags .
At the moment, only the
.Dv FIDO_EXT_HMAC_SECRET
.Dv FIDO_EXT_CRED_BLOB ,
.Dv FIDO_EXT_CRED_PROTECT ,
.Dv FIDO_EXT_HMAC_SECRET ,
and
.Dv FIDO_EXT_CRED_PROTECT
.Dv FIDO_EXT_LARGEBLOB_KEY
extensions are supported.
If
.Fa flags
@ -162,6 +167,18 @@ is zero, the extensions of
are cleared.
.Pp
The
.Fn fido_cred_set_blob
function sets the
.Dq credBlob
to be stored with
.Fa cred
to the data pointed to by
.Fa ptr ,
which must be
.Fa len
bytes long.
.Pp
The
.Fn fido_cred_set_prot
function sets the protection of
.Fa cred
@ -185,10 +202,10 @@ and
.Fn fido_cred_set_uv
functions set the
.Em rk
(resident key)
.Pq resident/discoverable key
and
.Em uv
(user verification)
.Pq user verification
attributes of
.Fa cred .
Both are
@ -205,10 +222,10 @@ where
.Fa fmt
must be either
.Vt "packed"
(the format used in FIDO 2)
.Pq the format used in FIDO2
or
.Vt "fido-u2f"
(the format used by U2F).
.Pq the format used by U2F .
A copy of
.Fa fmt
is made, and no references to the passed pointer are kept.

View File

@ -29,7 +29,7 @@ A brief description follows:
The
.Fn fido_cred_verify
function verifies whether the client data hash, relying party ID,
credential ID, type, and resident key and user verification
credential ID, type, and resident/discoverable key and user verification
attributes of
.Fa cred
have been attested by the holder of the private counterpart of

View File

@ -306,3 +306,7 @@ Applications using credential management should ensure it is
supported by the authenticator prior to using the API.
Since FIDO 2.1 hasn't been finalised, there is a chance the
functionality and associated data structures may change.
.Pp
Resident credentials are called
.Dq discoverable credentials
in FIDO2.1.

View File

@ -0,0 +1,95 @@
.\" Copyright (c) 2020 Yubico AB. All rights reserved.
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
.Dd $Mdocdate: September 22 2020 $
.Dt FIDO_DEV_ENABLE_ENTATTEST 3
.Os
.Sh NAME
.Nm fido_dev_enable_entattest ,
.Nm fido_dev_toggle_always_uv ,
.Nm fido_dev_force_pin_change ,
.Nm fido_dev_set_pin_minlen
.Nd FIDO 2.1 configuration authenticator API
.Sh SYNOPSIS
.In fido.h
.In fido/config.h
.Ft int
.Fn fido_dev_enable_entattest "fido_dev_t *dev" "const char *pin"
.Ft int
.Fn fido_dev_toggle_always_uv "fido_dev_t *dev" "const char *pin"
.Ft int
.Fn fido_dev_force_pin_change "fido_dev_t *dev" "const char *pin"
.Ft int
.Fn fido_dev_set_pin_minlen "fido_dev_t *dev" "size_t len" "const char *pin"
.Sh DESCRIPTION
The functions described in this page allow configuration of a
FIDO 2.1 authenticator.
.Pp
The
.Fn fido_dev_enable_entattest
function enables the
.Em Enterprise Attestation
feature on
.Fa dev .
.Em Enterprise Attestation
instructs the authenticator to include uniquely identifying
information in subsequent attestation statements.
The
.Fa pin
parameter may be NULL if
.Fa dev
does not have a PIN set.
.Pp
The
.Fn fido_dev_toggle_always_uv
function toggles the
.Dq user verification always
feature on
.Fa dev .
When set, this toggle enforces user verification at the
authenticator level for all known credentials.
If
.Fa dev
supports U2F (CTAP1) and the user verification methods supported by
the authenticator do not allow protection of U2F credentials, the
U2F subsystem will be disabled by the authenticator.
The
.Fa pin
parameter may be NULL if
.Fa dev
does not have a PIN set.
.Pp
The
.Fn fido_dev_force_pin_change
instructs
.Fa dev
to require a PIN change.
Subsequent PIN authentication attempts against
.Fa dev
will fail until its PIN is changed.
.Pp
The
.Fn fido_dev_set_pin_minlen
function sets the minimum PIN length of
.Fa dev
to
.Fa len .
Minimum PIN lengths may only be increased.
.Pp
Configuration settings are reflected in the payload returned by the
authenticator in response to a
.Xr fido_dev_get_cbor_info 3
call.
.Sh SEE ALSO
.Xr fido_dev_get_cbor_info 3 ,
.Xr fido_dev_reset 3
.Sh CAVEATS
Authenticator configuration is a tentative feature of FIDO 2.1.
Applications willing to strictly abide by FIDO 2.0 should refrain
from using authenticator configuration.
Applications using authenticator configuration should ensure the
feature is supported by the authenticator prior to using the
corresponding API.
Since FIDO 2.1 hasn't been finalised, there is a chance the
functionality and associated data structures may change.

View File

@ -0,0 +1,194 @@
.\" Copyright (c) 2020 Yubico AB. All rights reserved.
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
.Dd $Mdocdate: October 26 2020 $
.Dt FIDO_LARGEBLOB_GET 3
.Os
.Sh NAME
.Nm fido_dev_largeblob_get ,
.Nm fido_dev_largeblob_set ,
.Nm fido_dev_largeblob_remove ,
.Nm fido_dev_largeblob_get_array ,
.Nm fido_dev_largeblob_set_array
.Nd FIDO 2 large blob API
.Sh SYNOPSIS
.In fido.h
.Ft int
.Fn fido_dev_largeblob_get "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "unsigned char **blob_ptr" "size_t *blob_len"
.Ft int
.Fn fido_dev_largeblob_set "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "const unsigned char *blob_ptr" "size_t blob_len" "const char *pin"
.Ft int
.Fn fido_dev_largeblob_remove "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "const char *pin"
.Ft int
.Fn fido_dev_largeblob_get_array "fido_dev_t *dev" "unsigned char **cbor_ptr" "size_t *cbor_len"
.Ft int
.Fn fido_dev_largeblob_set_array "fido_dev_t *dev" "const unsigned char *cbor_ptr" "size_t cbor_len" "const char *pin"
.Sh DESCRIPTION
The
.Dq largeBlobs
API of
.Em libfido2
allows binary blobs residing on a FIDO 2.1 authenticator to be
read, written, and inspected.
.Dq largeBlobs
is a FIDO 2.1 extension.
.Pp
.Dq largeBlobs
are stored as elements of a CBOR array.
Confidentiality is ensured by encrypting each element with a
distinct, credential-bound 256-bit AES-GCM key.
The array is otherwise shared between different credentials and
FIDO2 relying parties.
.Pp
Retrieval of a credential's encryption key is possible during
enrollment with
.Xr fido_cred_set_extensions 3
and
.Xr fido_cred_largeblob_key_ptr 3 ,
during assertion with
.Xr fido_assert_set_extensions 3
and
.Xr fido_assert_largeblob_key_ptr 3 ,
or, in the case of a resident credential, via
.Em libfido2's
credential management API.
.Pp
The
.Dq largeBlobs
CBOR array is opaque to the authenticator.
Management of the array is left at the discretion of FIDO2 clients.
For further details on FIDO 2.1's
.Dq largeBlobs
extension, please refer to the FIDO 2.1 spec.
.Pp
The
.Fn fido_dev_largeblob_get
function retrieves the authenticator's
.Dq largeBlobs
CBOR array and, on success, returns the first blob
.Pq iterating from array index zero
that can be
decrypted by
.Fa key_ptr ,
where
.Fa key_ptr
points to
.Fa key_len
bytes.
On success,
.Fn fido_dev_largeblob_get
sets
.Fa blob_ptr
to the body of the decrypted blob, and
.Fa blob_len
to the length of the decrypted blob in bytes.
It is the caller's responsibility to free
.Fa blob_ptr .
.Pp
The
.Fn fido_dev_largeblob_set
function uses
.Fa key_ptr
to encrypt
.Fa blob_ptr
and inserts the result in the authenticator's
.Dq largeBlobs
CBOR array.
Insertion happens at the end of the array if no existing element
can be decrypted by
.Fa key_ptr ,
or at the position of the first element
.Pq iterating from array index zero
that can be decrypted by
.Fa key_ptr .
.Fa key_len
holds the length of
.Fa key_ptr
in bytes, and
.Fa blob_len
the length of
.Fa blob_ptr
in bytes.
A
.Fa pin
or equivalent user-verification gesture is required.
.Pp
The
.Fn fido_dev_largeblob_remove
function retrieves the authenticator's
.Dq largeBlobs
CBOR array and, on success, drops the first blob
.Pq iterating from array index zero
that can be decrypted by
.Fa key_ptr ,
where
.Fa key_ptr
points to
.Fa key_len
bytes.
A
.Fa pin
or equivalent user-verification gesture is required.
.Pp
The
.Fn fido_dev_largeblob_get_array
function retrieves the authenticator's
.Dq largeBlobs
CBOR array and, on success,
sets
.Fa cbor_ptr
to the body of the CBOR array, and
.Fa cbor_len
to its corresponding length in bytes.
It is the caller's responsibility to free
.Fa cbor_ptr .
.Pp
Finally, the
.Fn fido_dev_largeblob_set_array
function sets the authenticator's
.Dq largeBlobs
CBOR array to the data pointed to by
.Fa cbor_ptr ,
where
.Fa cbor_ptr
points to
.Fa cbor_len
bytes.
A
.Fa pin
or equivalent user-verification gesture is required.
.Sh RETURN VALUES
The functions
.Fn fido_dev_largeblob_set ,
.Fn fido_dev_largeblob_get ,
.Fn fido_dev_largeblob_remove ,
.Fn fido_dev_largeblob_get_array ,
and
.Fn fido_dev_largeblob_set_array
return
.Dv FIDO_OK
on success.
On error, an error code defined in
.In fido/err.h
is returned.
.Sh SEE ALSO
.Xr fido_assert_largeblob_key_len 3 ,
.Xr fido_assert_largeblob_key_ptr 3 ,
.Xr fido_assert_set_extensions 3 ,
.Xr fido_cred_largeblob_key_len 3 ,
.Xr fido_cred_largeblob_key_ptr 3 ,
.Xr fido_cred_set_extensions 3 ,
.Xr fido_credman_dev_get_rk 3 ,
.Xr fido_credman_dev_get_rp 3 ,
.Xr fido_dev_get_assert 3 ,
.Xr fido_dev_make_cred 3
.Sh CAVEATS
The
.Dq largeBlobs
extension is not meant to be used to store sensitive data.
When retrieved, a credential's
.Dq largeBlobs
encryption key is transmitted in the clear, and an authenticator's
.Dq largeBlobs
CBOR array can be read without user interaction or verification.

View File

@ -33,7 +33,7 @@ defined in
.It
.Nm list of excluded credential IDs ;
.It
.Nm resident key and user verification attributes .
.Nm resident/discoverable key and user verification attributes .
.El
.Pp
See

View File

@ -14,9 +14,12 @@
.Nm fido_dev_force_fido2 ,
.Nm fido_dev_force_u2f ,
.Nm fido_dev_is_fido2 ,
.Nm fido_dev_supports_credman ,
.Nm fido_dev_supports_cred_prot ,
.Nm fido_dev_supports_pin ,
.Nm fido_dev_has_pin ,
.Nm fido_dev_supports_uv ,
.Nm fido_dev_has_uv ,
.Nm fido_dev_protocol ,
.Nm fido_dev_build ,
.Nm fido_dev_flags ,
@ -42,11 +45,17 @@
.Ft bool
.Fn fido_dev_is_fido2 "const fido_dev_t *dev"
.Ft bool
.Fn fido_dev_supports_credman "const fido_dev_t *dev"
.Ft bool
.Fn fido_dev_supports_cred_prot "const fido_dev_t *dev"
.Ft bool
.Fn fido_dev_supports_pin "const fido_dev_t *dev"
.Ft bool
.Fn fido_dev_has_pin "const fido_dev_t *dev"
.Ft bool
.Fn fido_dev_supports_uv "const fido_dev_t *dev"
.Ft bool
.Fn fido_dev_has_uv "const fido_dev_t *dev"
.Ft uint8_t
.Fn fido_dev_protocol "const fido_dev_t *dev"
.Ft uint8_t
@ -66,6 +75,18 @@ where
.Fa dev
is a freshly allocated or otherwise closed
.Vt fido_dev_t .
If
.Fa dev
claims to be FIDO2,
.Em libfido2
will attempt to speak FIDO2 to
.Fa dev .
If that fails,
.Em libfido2
will fallback to U2F unless the
.Dv FIDO_DISABLE_U2F_FALLBACK
flag was set in
.Xr fido_init 3 .
.Pp
The
.Fn fido_dev_close
@ -126,6 +147,14 @@ if
is a FIDO 2 device.
.Pp
The
.Fn fido_dev_supports_credman
function returns
.Dv true
if
.Fa dev
supports FIDO 2.1 Credential Management.
.Pp
The
.Fn fido_dev_supports_cred_prot
function returns
.Dv true
@ -150,6 +179,23 @@ if
has a FIDO 2.0 Client PIN set.
.Pp
The
.Fn fido_dev_supports_uv
function returns
.Dv true
if
.Fa dev
supports a built-in user verification method.
.Pp
The
.Fn fido_dev_has_uv
function returns
.Dv true
if
.Fa dev
supports built-in user verification and its user verification
feature is configured.
.Pp
The
.Fn fido_dev_protocol
function returns the CTAPHID protocol version identifier of
.Fa dev .
@ -189,4 +235,5 @@ On error, a different error code defined in
is returned.
.Sh SEE ALSO
.Xr fido_dev_info_manifest 3 ,
.Xr fido_dev_set_io_functions 3
.Xr fido_dev_set_io_functions 3 ,
.Xr fido_init 3

View File

@ -6,7 +6,8 @@
.Dt FIDO_DEV_SET_IO_FUNCTIONS 3
.Os
.Sh NAME
.Nm fido_dev_set_io_functions
.Nm fido_dev_set_io_functions ,
.Nm fido_dev_set_sigmask
.Nd FIDO 2 device I/O interface
.Sh SYNOPSIS
.In fido.h
@ -15,137 +16,118 @@ 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_io_rx_t(struct fido_dev *, uint8_t, unsigned char *, size_t, int);
typedef int fido_dev_io_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_rx_t *rx;
fido_dev_io_tx_t *tx;
} fido_dev_io_t;
#ifdef _WIN32
typedef int fido_sigset_t;
#else
typedef sigset_t fido_sigset_t;
#endif
.Ed
.Ft int
.Fn fido_dev_set_io_functions "fido_dev_t *dev" "const fido_dev_io_t *io"
.Ft int
.Fn fido_dev_set_sigmask "fido_dev_t *dev" "const fido_sigset_t *sigmask"
.Sh DESCRIPTION
The
.Nm
interface defines the I/O and transmission handlers used to talk to
.Fa dev .
Its usage is optional.
By default,
.Fn fido_dev_set_io_functions
function sets the I/O handlers used by
.Em libfido2
will use the operating system's native HID interface to talk CTAP2 to
a FIDO device.
.Pp
A
.Vt fido_dev_io_open_t
function is expected to return a non-NULL opaque pointer on success,
and NULL on error.
The returned opaque pointer is never dereferenced by
.Em libfido2 .
.Pp
A
.Vt fido_dev_io_close_t
function receives the opaque handle obtained from
.Vt fido_dev_io_open_t .
It is not expected to be idempotent.
.Pp
A
.Vt fido_dev_io_read_t
function reads a single HID report from
to talk to
.Fa dev .
The first parameter taken is the opaque handle obtained from
.Vt fido_dev_io_open_t .
The read buffer is pointed to by the second parameter, and the
third parameter holds its size.
The last argument passed to
.Vt fido_dev_io_read_t
is the number of milliseconds the caller is willing to sleep,
should the call need to block.
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_io_read_t
.Vt fido_dev_read_t
may block indefinitely.
The number of bytes read is returned.
On success, the number of bytes read is returned.
On error, -1 is returned.
.Pp
A
.Vt fido_dev_io_write_t
function writes a single HID report to
.It Vt fido_dev_write_t
Writes a single transmission unit (HID report, APDU) to
.Fa dev .
The first parameter taken is the opaque handle returned by
.Vt fido_dev_io_open_t .
The write buffer is pointed to by the second parameter, and the
third parameter holds its size.
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_io_write_t
function may block.
The number of bytes written is returned.
On error, -1 is returned.
.Pp
A
.Vt fido_dev_io_rx_t
function receives a complete CTAP2 message from
.Fa dev .
The first parameter taken is a pointer to
.Fa dev .
The second parameter holds the expected CTAP2 command byte.
The read buffer is pointed to by the third parameter, and the
fourth parameter holds its size.
The last argument passed to
.Vt fido_dev_io_rx_t
is the number of milliseconds the caller is willing to sleep,
should the call need to block.
If this value holds -1,
.Vt fido_dev_io_rx_t
may block indefinitely.
The number of bytes read is returned.
On error, -1 is returned.
.Pp
A
.Vt fido_dev_io_tx_t
function transmits a complete CTAP2 message to
.Fa dev .
The first parameter taken is a pointer to
.Fa dev .
The second parameter holds the CTAP2 command byte.
The write buffer is pointed to by the third parameter, and the
fourth parameter holds its size.
A
.Vt fido_dev_io_tx_t
function may block.
On success, 0 is returned.
.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
.Fa read ,
and
.Fa write
fields of
.Fa io
may not be NULL.
Either
.Fa rx
or
.Fa tx
may be NULL, in which case
.Em libfido2
uses its corresponding CTAP2 HID transport method.
.Pp
No references to
.Fa io
are held by
.Fn fido_dev_set_io_functions .
.Pp
The
.Fn fido_dev_set_sigmask
function may be used to specify a non-NULL signal mask
.Fa sigmask
to be used while
.Em libfido2's
default I/O handlers wait on
.Fa dev .
On UNIX-like operating systems,
.Vt fido_sigset_t
is defined as
.Vt sigset_t .
On Windows,
.Vt fido_sigset_t
is defined as
.Vt int
and
.Fn fido_dev_set_sigmask
is a no-op.
.Pp
No references to
.Fa sigmask
are held by
.Fn fido_dev_set_sigmask .
.Sh RETURN VALUES
On success,
.Fn fido_dev_set_io_functions
returns
and
.Fn fido_dev_set_sigmask
return
.Dv FIDO_OK .
On error, a different error code defined in
.In fido/err.h

View File

@ -8,6 +8,7 @@
.Sh NAME
.Nm fido_dev_set_pin ,
.Nm fido_dev_get_retry_count ,
.Nm fido_dev_get_uv_retry_count ,
.Nm fido_dev_reset
.Nd FIDO 2 device management functions
.Sh SYNOPSIS
@ -17,6 +18,8 @@
.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
@ -51,6 +54,16 @@ before lock-out, where
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 ,
@ -60,6 +73,7 @@ device.
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.
@ -67,6 +81,7 @@ are synchronous and will block if necessary.
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

View File

@ -20,7 +20,8 @@ function initialises the
library.
Its invocation must precede that of any other
.Em libfido2
function.
function in the context of the executing thread.
.Pp
If
.Dv FIDO_DEBUG
is set in
@ -33,6 +34,17 @@ on
Alternatively, the
.Ev FIDO_DEBUG
environment variable may be set.
.Pp
If
.Dv FIDO_DISABLE_U2F_FALLBACK
is set in
.Fa flags ,
then
.Em libfido2
will not fallback to U2F in
.Xr fido_dev_open 3
if a device claims to be FIDO2 but fails to respond to a
FIDO2 command.
.Sh SEE ALSO
.Xr fido_assert_new 3 ,
.Xr fido_cred_new 3 ,

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include "openbsd-compat.h"
#if defined(_WIN32) && !defined(HAVE_ENDIAN_H)
/*
* Hopefully, if the endianness differs from the end result, the compiler
* optimizes these functions with some type of bswap instruction. Or,
* otherwise, to just return the input value unmodified. GCC and clang
* both does these optimization at least. This should be preferred over
* relying on some BYTE_ORDER macro, which may or may not be defined.
*/
uint32_t
htole32(uint32_t in)
{
uint32_t out = 0;
uint8_t *b = (uint8_t *)&out;
b[0] = (uint8_t)((in >> 0) & 0xff);
b[1] = (uint8_t)((in >> 8) & 0xff);
b[2] = (uint8_t)((in >> 16) & 0xff);
b[3] = (uint8_t)((in >> 24) & 0xff);
return (out);
}
uint64_t
htole64(uint64_t in)
{
uint64_t out = 0;
uint8_t *b = (uint8_t *)&out;
b[0] = (uint8_t)((in >> 0) & 0xff);
b[1] = (uint8_t)((in >> 8) & 0xff);
b[2] = (uint8_t)((in >> 16) & 0xff);
b[3] = (uint8_t)((in >> 24) & 0xff);
b[4] = (uint8_t)((in >> 32) & 0xff);
b[5] = (uint8_t)((in >> 40) & 0xff);
b[6] = (uint8_t)((in >> 48) & 0xff);
b[7] = (uint8_t)((in >> 56) & 0xff);
return (out);
}
#endif /* WIN32 && !HAVE_ENDIAN_H */

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "openbsd-compat.h"
#ifndef HAVE_FREEZERO
void
freezero(void *ptr, size_t sz)
{
if (ptr == NULL)
return;
explicit_bzero(ptr, sz);
free(ptr);
}
#endif /* HAVE_FREEZERO */

View File

@ -0,0 +1,124 @@
/* $OpenBSD: hkdf.c,v 1.4 2019/11/21 20:02:20 tim Exp $ */
/* Copyright (c) 2014, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "openbsd-compat.h"
#include "fido.h"
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#include <assert.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#define CRYPTOerror(r) CRYPTOerr(ERR_LIB_CRYPTO, (r))
/* https://tools.ietf.org/html/rfc5869#section-2 */
int
HKDF(uint8_t *out_key, size_t out_len, const EVP_MD *digest,
const uint8_t *secret, size_t secret_len, const uint8_t *salt,
size_t salt_len, const uint8_t *info, size_t info_len)
{
uint8_t prk[EVP_MAX_MD_SIZE];
size_t prk_len;
if (!HKDF_extract(prk, &prk_len, digest, secret, secret_len, salt,
salt_len))
return 0;
if (!HKDF_expand(out_key, out_len, digest, prk, prk_len, info,
info_len))
return 0;
return 1;
}
/* https://tools.ietf.org/html/rfc5869#section-2.2 */
int
HKDF_extract(uint8_t *out_key, size_t *out_len,
const EVP_MD *digest, const uint8_t *secret, size_t secret_len,
const uint8_t *salt, size_t salt_len)
{
unsigned int len;
/*
* If salt is not given, HashLength zeros are used. However, HMAC does
* that internally already so we can ignore it.
*/
if (salt_len > INT_MAX || HMAC(digest, salt, (int)salt_len, secret,
secret_len, out_key, &len) == NULL) {
CRYPTOerror(ERR_R_CRYPTO_LIB);
return 0;
}
*out_len = len;
return 1;
}
/* https://tools.ietf.org/html/rfc5869#section-2.3 */
int
HKDF_expand(uint8_t *out_key, size_t out_len,
const EVP_MD *digest, const uint8_t *prk, size_t prk_len,
const uint8_t *info, size_t info_len)
{
const size_t digest_len = EVP_MD_size(digest);
uint8_t previous[EVP_MAX_MD_SIZE];
size_t n, done = 0;
unsigned int i;
int ret = 0;
HMAC_CTX hmac;
/* Expand key material to desired length. */
n = (out_len + digest_len - 1) / digest_len;
if (out_len + digest_len < out_len || n > 255 || prk_len > INT_MAX) {
CRYPTOerror(EVP_R_TOO_LARGE);
return 0;
}
HMAC_CTX_init(&hmac);
if (!HMAC_Init_ex(&hmac, prk, (int)prk_len, digest, NULL))
goto out;
for (i = 0; i < n; i++) {
uint8_t ctr = i + 1;
size_t todo;
if (i != 0 && (!HMAC_Init_ex(&hmac, NULL, 0, NULL, NULL) ||
!HMAC_Update(&hmac, previous, digest_len)))
goto out;
if (!HMAC_Update(&hmac, info, info_len) ||
!HMAC_Update(&hmac, &ctr, 1) ||
!HMAC_Final(&hmac, previous, NULL))
goto out;
todo = digest_len;
if (done + todo > out_len)
todo = out_len - done;
memcpy(out_key + done, previous, todo);
done += todo;
}
ret = 1;
out:
HMAC_CTX_cleanup(&hmac);
explicit_bzero(previous, sizeof(previous));
if (ret != 1)
CRYPTOerror(ERR_R_CRYPTO_LIB);
return ret;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */

View File

@ -0,0 +1,65 @@
/* $OpenBSD: hkdf.h,v 1.2 2018/04/03 13:33:53 tb Exp $ */
/* Copyright (c) 2014, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#ifndef OPENSSL_HEADER_HKDF_H
#define OPENSSL_HEADER_HKDF_H
#include <openssl/evp.h>
#if defined(__cplusplus)
extern "C" {
#endif
/*
* HKDF computes HKDF (as specified by RFC 5869) of initial keying
* material |secret| with |salt| and |info| using |digest|, and
* outputs |out_len| bytes to |out_key|. It returns one on success and
* zero on error.
*
* HKDF is an Extract-and-Expand algorithm. It does not do any key
* stretching, and as such, is not suited to be used alone to generate
* a key from a password.
*/
int HKDF(uint8_t *out_key, size_t out_len, const struct env_md_st *digest,
const uint8_t *secret, size_t secret_len, const uint8_t *salt,
size_t salt_len, const uint8_t *info, size_t info_len);
/*
* HKDF_extract computes a HKDF PRK (as specified by RFC 5869) from
* initial keying material |secret| and salt |salt| using |digest|,
* and outputs |out_len| bytes to |out_key|. The maximum output size
* is |EVP_MAX_MD_SIZE|. It returns one on success and zero on error.
*/
int HKDF_extract(uint8_t *out_key, size_t *out_len,
const struct env_md_st *digest, const uint8_t *secret,
size_t secret_len, const uint8_t *salt, size_t salt_len);
/*
* HKDF_expand computes a HKDF OKM (as specified by RFC 5869) of
* length |out_len| from the PRK |prk| and info |info| using |digest|,
* and outputs the result to |out_key|. It returns one on success and
* zero on error.
*/
int HKDF_expand(uint8_t *out_key, size_t out_len,
const EVP_MD *digest, const uint8_t *prk, size_t prk_len,
const uint8_t *info, size_t info_len);
#if defined(__cplusplus)
} /* extern C */
#endif
#endif /* OPENSSL_HEADER_HKDF_H */

View File

@ -20,9 +20,12 @@
#define be16toh(x) OSSwapBigToHostInt16((x))
#define htobe16(x) OSSwapHostToBigInt16((x))
#define be32toh(x) OSSwapBigToHostInt32((x))
#define htole32(x) OSSwapHostToLittleInt32((x))
#define htole64(x) OSSwapHostToLittleInt64((x))
#endif /* __APPLE__ && !HAVE_ENDIAN_H */
#if defined(_WIN32) && !defined(HAVE_ENDIAN_H)
#include <stdint.h>
#include <winsock2.h>
#if !defined(_MSC_VER)
#include <sys/param.h>
@ -30,6 +33,8 @@
#define be16toh(x) ntohs((x))
#define htobe16(x) htons((x))
#define be32toh(x) ntohl((x))
uint32_t htole32(uint32_t);
uint64_t htole64(uint64_t);
#endif /* _WIN32 && !HAVE_ENDIAN_H */
#if defined(__FreeBSD__) && !defined(HAVE_ENDIAN_H)
@ -37,6 +42,7 @@
#endif
#include <stdlib.h>
#include <string.h>
#if !defined(HAVE_STRLCAT)
size_t strlcat(char *, const char *, size_t);
@ -54,6 +60,10 @@ void *recallocarray(void *, size_t, size_t, size_t);
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
@ -68,7 +78,11 @@ int timingsafe_bcmp(const void *, const void *, size_t);
#include <readpassphrase.h>
#endif
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#include <stdint.h>
#include "hkdf.h"
#define EVP_PKEY_get0_EC_KEY(x) ((x)->pkey.ec)
#define EVP_PKEY_get0_RSA(x) ((x)->pkey.rsa)
#endif
@ -90,6 +104,16 @@ int timingsafe_bcmp(const void *, const void *, size_t);
ssize_t getline(char **, size_t *, FILE *);
#endif
#if defined(_MSC_VER)
#define strerror_r(e, b, l) strerror_s((b), (l), (e))
#endif
#include "time.h"
#if !defined(HAVE_POSIX_IOCTL)
#define IOCTL_REQ(x) (x)
#else
#define IOCTL_REQ(x) ((int)(x))
#endif
#endif /* !_OPENBSD_COMPAT_H */

View File

@ -0,0 +1,7 @@
#include <sys/ioctl.h>
int
posix_ioctl_check(int fd)
{
return ioctl(fd, -1, 0);
}

View File

@ -32,15 +32,30 @@ int clock_gettime(clockid_t, struct timespec *);
#endif
#ifndef HAVE_TIMESPECSUB
#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 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 */

View File

@ -23,6 +23,11 @@ typedef uint32_t uid_t;
#endif
#ifdef _MSC_VER
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;
#include <basetsd.h>
typedef SSIZE_T ssize_t;

View File

@ -8,6 +8,7 @@
#include <fido.h>
#include <fido/es256.h>
#include <fido/rs256.h>
#include <fido/eddsa.h>
#include <string.h>
#define FAKE_DEV_HANDLE ((void *)0xdeadbeef)
@ -159,11 +160,30 @@ free_rs256_pk(rs256_pk_t *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);
@ -183,6 +203,7 @@ empty_assert(fido_dev_t *d, fido_assert_t *a, size_t idx)
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);
@ -191,8 +212,12 @@ empty_assert(fido_dev_t *d, fido_assert_t *a, size_t idx)
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);
@ -201,11 +226,16 @@ empty_assert(fido_dev_t *d, fido_assert_t *a, size_t idx)
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
@ -244,11 +274,15 @@ static void
valid_assert(void)
{
fido_assert_t *a;
es256_pk_t *pk;
es256_pk_t *es256;
rs256_pk_t *rs256;
eddsa_pk_t *eddsa;
a = alloc_assert();
pk = alloc_es256_pk();
assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
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);
@ -257,9 +291,13 @@ valid_assert(void)
assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK);
assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_OK);
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(pk);
free_es256_pk(es256);
free_rs256_pk(rs256);
free_eddsa_pk(eddsa);
}
static void

View File

@ -5,6 +5,7 @@
*/
#include <assert.h>
#include <cbor.h>
#include <fido.h>
#include <string.h>
@ -327,6 +328,8 @@ empty_cred(void)
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);
@ -696,6 +699,8 @@ junk_authdata(void)
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);
@ -866,6 +871,46 @@ wrong_credprot(void)
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);
}
int
main(void)
{
@ -892,6 +937,7 @@ main(void)
duplicate_keys();
unsorted_keys();
wrong_credprot();
raw_authdata();
exit(0);
}

View File

@ -8,9 +8,16 @@
#include <fido.h>
#include <string.h>
#include "../fuzz/wiredata_fido2.h"
#define FAKE_DEV_HANDLE ((void *)0xdeadbeef)
#define REPORT_LEN (64 + 1)
static uint8_t ctap_nonce[8];
static uint8_t *wiredata_ptr;
static size_t wiredata_len;
static int initialised;
static void *
dummy_open(const char *path)
{
@ -28,13 +35,33 @@ dummy_close(void *handle)
static int
dummy_read(void *handle, unsigned char *ptr, size_t len, int ms)
{
(void)ptr;
(void)len;
size_t n;
(void)ms;
assert(handle == FAKE_DEV_HANDLE);
assert(ptr != NULL);
assert(len == REPORT_LEN - 1);
return (-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 (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
@ -44,9 +71,41 @@ dummy_write(void *handle, const unsigned char *ptr, size_t len)
assert(ptr != NULL);
assert(len == REPORT_LEN);
if (!initialised)
memcpy(&ctap_nonce, &ptr[8], sizeof(ctap_nonce));
return ((int)len);
}
static uint8_t *
wiredata_setup(const uint8_t *data, size_t len)
{
const uint8_t ctap_init_data[] = { WIREDATA_CTAP_INIT };
assert(wiredata_ptr == NULL);
assert(SIZE_MAX - len > sizeof(ctap_init_data));
assert((wiredata_ptr = malloc(sizeof(ctap_init_data) + len)) != NULL);
memcpy(wiredata_ptr, ctap_init_data, sizeof(ctap_init_data));
if (len)
memcpy(wiredata_ptr + sizeof(ctap_init_data), data, len);
wiredata_len = sizeof(ctap_init_data) + len;
return (wiredata_ptr);
}
static void
wiredata_clear(uint8_t **wiredata)
{
free(*wiredata);
*wiredata = NULL;
wiredata_ptr = NULL;
wiredata_len = 0;
initialised = 0;
}
/* gh#56 */
static void
open_iff_ok(void)
@ -69,12 +128,139 @@ open_iff_ok(void)
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);
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);
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);
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);
wiredata_clear(&wiredata);
}
int
main(void)
{
fido_init(0);
open_iff_ok();
reopen();
double_open();
is_fido2();
has_pin();
exit(0);
}

View File

@ -12,6 +12,8 @@ list(APPEND FIDO_SOURCES
blob.c
buf.c
cbor.c
compress.c
config.c
cred.c
credman.c
dev.c
@ -23,8 +25,10 @@ list(APPEND FIDO_SOURCES
info.c
io.c
iso7816.c
largeblob.c
log.c
pin.c
random.c
reset.c
rs256.c
u2f.c
@ -35,6 +39,9 @@ if(FUZZ)
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_linux.c)
endif()
if(USE_HIDAPI)
list(APPEND FIDO_SOURCES hid_hidapi.c)
@ -43,9 +50,13 @@ elseif(WIN32)
elseif(APPLE)
list(APPEND FIDO_SOURCES hid_osx.c)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND FIDO_SOURCES hid_linux.c)
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)
list(APPEND FIDO_SOURCES hid_openbsd.c hid_unix.c)
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
list(APPEND FIDO_SOURCES hid_freebsd.c hid_unix.c)
else()
message(FATAL_ERROR "please define a hid backend for your platform")
endif()
@ -57,53 +68,60 @@ endif()
list(APPEND COMPAT_SOURCES
../openbsd-compat/bsd-getpagesize.c
../openbsd-compat/endian_win32.c
../openbsd-compat/explicit_bzero.c
../openbsd-compat/explicit_bzero_win32.c
../openbsd-compat/freezero.c
../openbsd-compat/hkdf.c
../openbsd-compat/recallocarray.c
../openbsd-compat/strlcat.c
../openbsd-compat/timingsafe_bcmp.c
)
# static library
add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES})
target_link_libraries(fido2 ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES}
${UDEV_LIBRARIES} ${BASE_LIBRARIES} ${HIDAPI_LIBRARIES})
if(WIN32)
if (MINGW)
target_link_libraries(fido2 wsock32 ws2_32 bcrypt setupapi hid)
else()
target_link_libraries(fido2 wsock32 ws2_32 bcrypt SetupAPI hid)
set_target_properties(fido2 PROPERTIES OUTPUT_NAME fido2_static)
if(BUILD_STATIC_LIBS)
add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES})
target_link_libraries(fido2 ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES}
${UDEV_LIBRARIES} ${BASE_LIBRARIES} ${HIDAPI_LIBRARIES} ${ZLIB_LIBRARIES})
if(WIN32)
if (MINGW)
target_link_libraries(fido2 wsock32 ws2_32 bcrypt setupapi hid)
else()
target_link_libraries(fido2 wsock32 ws2_32 bcrypt SetupAPI hid)
set_target_properties(fido2 PROPERTIES OUTPUT_NAME fido2_static)
endif()
elseif(APPLE)
target_link_libraries(fido2 "-framework CoreFoundation"
"-framework IOKit")
endif()
elseif(APPLE)
target_link_libraries(fido2 "-framework CoreFoundation"
"-framework IOKit")
install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
# dynamic library
add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES})
target_link_libraries(fido2_shared ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES}
${UDEV_LIBRARIES} ${BASE_LIBRARIES} ${HIDAPI_LIBRARIES})
if(WIN32)
if (MINGW)
target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt
setupapi hid)
else()
target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt
SetupAPI hid)
if(BUILD_SHARED_LIBS)
add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES})
target_link_libraries(fido2_shared ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES}
${UDEV_LIBRARIES} ${BASE_LIBRARIES} ${HIDAPI_LIBRARIES} ${ZLIB_LIBRARIES})
if(WIN32)
if (MINGW)
target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt
setupapi hid)
else()
target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt
SetupAPI hid)
endif()
elseif(APPLE)
target_link_libraries(fido2_shared "-framework CoreFoundation"
"-framework IOKit")
endif()
elseif(APPLE)
target_link_libraries(fido2_shared "-framework CoreFoundation"
"-framework IOKit")
set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2
VERSION ${FIDO_VERSION} SOVERSION ${FIDO_MAJOR})
install(TARGETS fido2_shared
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2
VERSION ${FIDO_VERSION} SOVERSION ${FIDO_MAJOR})
install(TARGETS fido2_shared
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES fido.h DESTINATION include)
install(DIRECTORY fido DESTINATION include)

View File

@ -1,98 +1,215 @@
/*
* Copyright (c) 2018 Yubico AB. All rights reserved.
* Copyright (c) 2021 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include <openssl/evp.h>
#include <string.h>
#include "fido.h"
int
aes256_cbc_enc(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
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;
unsigned char iv[32];
int len;
int ok = -1;
EVP_CIPHER_CTX *ctx = NULL;
const EVP_CIPHER *cipher;
int ok = -1;
memset(iv, 0, sizeof(iv));
out->ptr = NULL;
out->len = 0;
memset(out, 0, sizeof(*out));
/* sanity check */
if (in->len > INT_MAX || (in->len % 16) != 0 ||
(out->ptr = calloc(1, in->len)) == NULL) {
fido_log_debug("%s: in->len=%zu", __func__, in->len);
if (key->len != 32) {
fido_log_debug("%s: invalid key len %zu", __func__, key->len);
goto fail;
}
if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
!EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
!EVP_CIPHER_CTX_set_padding(ctx, 0) ||
!EVP_EncryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
len < 0 || (size_t)len != in->len) {
fido_log_debug("%s: EVP_Encrypt", __func__);
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;
}
out->len = (size_t)len;
ok = 0;
fail:
if (ctx != NULL)
EVP_CIPHER_CTX_free(ctx);
if (ok < 0)
fido_blob_reset(out);
if (ok < 0) {
free(out->ptr);
out->ptr = NULL;
out->len = 0;
}
return (ok);
return ok;
}
int
aes256_cbc_dec(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
static int
aes256_cbc_proto1(const fido_blob_t *key, const fido_blob_t *in,
fido_blob_t *out, int encrypt)
{
EVP_CIPHER_CTX *ctx = NULL;
unsigned char iv[32];
int len;
int ok = -1;
u_char iv[16];
memset(iv, 0, sizeof(iv));
out->ptr = NULL;
out->len = 0;
memset(&iv, 0, sizeof(iv));
/* sanity check */
if (in->len > INT_MAX || (in->len % 16) != 0 ||
(out->ptr = calloc(1, in->len)) == NULL) {
fido_log_debug("%s: in->len=%zu", __func__, in->len);
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 ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
!EVP_CIPHER_CTX_set_padding(ctx, 0) ||
!EVP_DecryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
len < 0 || (size_t)len > in->len + 32) {
fido_log_debug("%s: EVP_Decrypt", __func__);
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;
}
out->len = (size_t)len;
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);
if (ok < 0) {
free(out->ptr);
out->ptr = NULL;
out->len = 0;
}
return (ok);
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);
}

View File

@ -1,15 +1,12 @@
/*
* Copyright (c) 2018 Yubico AB. All rights reserved.
* Copyright (c) 2018-2021 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <string.h>
#include "fido.h"
#include "fido/es256.h"
#include "fido/rs256.h"
@ -67,12 +64,13 @@ parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
return (cbor_decode_cred_id(val, &stmt->id));
case 2: /* authdata */
return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
&stmt->authdata, &stmt->authdata_ext,
&stmt->hmac_secret_enc));
&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);
@ -85,6 +83,7 @@ fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
{
fido_blob_t f;
cbor_item_t *argv[7];
const uint8_t cmd = CTAP_CBOR_ASSERT;
int r;
memset(argv, 0, sizeof(argv));
@ -115,44 +114,33 @@ fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
}
}
/* hmac-secret extension */
if (assert->ext & FIDO_EXT_HMAC_SECRET)
if ((argv[3] = cbor_encode_hmac_secret_param(ecdh, pk,
&assert->hmac_salt)) == NULL) {
fido_log_debug("%s: cbor_encode_hmac_secret_param",
__func__);
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;
}
/* options */
if (assert->up != FIDO_OPT_OMIT || assert->uv != FIDO_OPT_OMIT)
if ((argv[4] = cbor_encode_assert_options(assert->up,
if ((argv[4] = cbor_encode_assert_opt(assert->up,
assert->uv)) == NULL) {
fido_log_debug("%s: cbor_encode_assert_options",
__func__);
fido_log_debug("%s: cbor_encode_assert_opt", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
}
/* pin authentication */
if (pin) {
if (pk == NULL || ecdh == NULL) {
fido_log_debug("%s: pin=%p, pk=%p, ecdh=%p", __func__,
(const void *)pin, (const void *)pk,
(const void *)ecdh);
r = FIDO_ERR_INVALID_ARGUMENT;
/* user verification */
if (fido_dev_can_get_uv_token(dev, pin, assert->uv))
if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh,
pin, assert->rp_id, &argv[5], &argv[6])) != FIDO_OK) {
fido_log_debug("%s: cbor_add_uv_params", __func__);
goto fail;
}
if ((r = cbor_add_pin_params(dev, &assert->cdh, pk, ecdh, pin,
&argv[5], &argv[6])) != FIDO_OK) {
fido_log_debug("%s: cbor_add_pin_params", __func__);
goto fail;
}
}
/* frame and transmit */
if (cbor_build_frame(CTAP_CBOR_ASSERT, argv, nitems(argv), &f) < 0 ||
if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
fido_log_debug("%s: fido_tx", __func__);
r = FIDO_ERR_TX;
@ -271,12 +259,14 @@ fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
}
static int
decrypt_hmac_secrets(fido_assert_t *assert, const fido_blob_t *key)
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->hmac_secret_enc.ptr != NULL) {
if (aes256_cbc_dec(key, &stmt->hmac_secret_enc,
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);
@ -302,12 +292,13 @@ fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
}
if (fido_dev_is_fido2(dev) == false) {
if (pin != NULL || assert->ext != 0)
if (pin != NULL || assert->ext.mask != 0)
return (FIDO_ERR_UNSUPPORTED_OPTION);
return (u2f_authenticate(dev, assert, -1));
}
if (pin != NULL || assert->ext != 0) {
if (fido_dev_can_get_uv_token(dev, pin, assert->uv) ||
(assert->ext.mask & FIDO_EXT_HMAC_SECRET)) {
if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
@ -315,8 +306,8 @@ fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
}
r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
if (r == FIDO_OK && assert->ext & FIDO_EXT_HMAC_SECRET)
if (decrypt_hmac_secrets(assert, ecdh) < 0) {
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;
@ -353,6 +344,8 @@ fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
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);
@ -363,8 +356,8 @@ check_extensions(int authdata_ext, int ext)
}
int
fido_get_signed_hash(int cose_alg, fido_blob_t *dgst, const fido_blob_t *clientdata,
const fido_blob_t *authdata_cbor)
fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor)
{
cbor_item_t *item = NULL;
unsigned char *authdata_ptr = NULL;
@ -566,7 +559,7 @@ fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
goto out;
}
if (check_extensions(stmt->authdata_ext, assert->ext) < 0) {
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;
@ -627,7 +620,19 @@ fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
size_t salt_len)
{
if ((salt_len != 32 && salt_len != 64) ||
fido_blob_set(&assert->hmac_salt, salt, salt_len) < 0)
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);
@ -686,10 +691,13 @@ fail:
int
fido_assert_set_extensions(fido_assert_t *assert, int ext)
{
if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET)
return (FIDO_ERR_INVALID_ARGUMENT);
assert->ext = ext;
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);
}
@ -741,42 +749,40 @@ void
fido_assert_reset_tx(fido_assert_t *assert)
{
free(assert->rp_id);
free(assert->cdh.ptr);
free(assert->hmac_salt.ptr);
fido_blob_reset(&assert->cdh);
fido_blob_reset(&assert->ext.hmac_salt);
fido_free_blob_array(&assert->allow_list);
memset(&assert->cdh, 0, sizeof(assert->cdh));
memset(&assert->hmac_salt, 0, sizeof(assert->hmac_salt));
memset(&assert->ext, 0, sizeof(assert->ext));
memset(&assert->allow_list, 0, sizeof(assert->allow_list));
assert->rp_id = NULL;
assert->up = FIDO_OPT_OMIT;
assert->uv = FIDO_OPT_OMIT;
assert->ext = 0;
}
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.id.ptr);
free(assert->stmt[i].user.icon);
free(assert->stmt[i].user.name);
free(assert->stmt[i].user.display_name);
free(assert->stmt[i].id.ptr);
if (assert->stmt[i].hmac_secret.ptr != NULL) {
explicit_bzero(assert->stmt[i].hmac_secret.ptr,
assert->stmt[i].hmac_secret.len);
}
free(assert->stmt[i].hmac_secret.ptr);
free(assert->stmt[i].hmac_secret_enc.ptr);
free(assert->stmt[i].authdata_cbor.ptr);
free(assert->stmt[i].sig.ptr);
fido_blob_reset(&assert->stmt[i].user.id);
fido_blob_reset(&assert->stmt[i].id);
fido_blob_reset(&assert->stmt[i].hmac_secret);
fido_blob_reset(&assert->stmt[i].authdata_cbor);
fido_blob_reset(&assert->stmt[i].largeblob_key);
fido_blob_reset(&assert->stmt[i].sig);
fido_assert_reset_extattr(&assert->stmt[i].authdata_ext);
memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
}
free(assert->stmt);
assert->stmt = NULL;
assert->stmt_len = 0;
assert->stmt_cnt = 0;
@ -789,12 +795,9 @@ fido_assert_free(fido_assert_t **assert_p)
if (assert_p == NULL || (assert = *assert_p) == NULL)
return;
fido_assert_reset_tx(assert);
fido_assert_reset_rx(assert);
free(assert);
*assert_p = NULL;
}
@ -945,16 +948,48 @@ fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
return (assert->stmt[idx].hmac_secret.len);
}
static void
fido_assert_clean_authdata(fido_assert_stmt *as)
const unsigned char *
fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx)
{
free(as->authdata_cbor.ptr);
free(as->hmac_secret_enc.ptr);
if (idx >= assert->stmt_len)
return (NULL);
memset(&as->authdata_ext, 0, sizeof(as->authdata_ext));
memset(&as->authdata_cbor, 0, sizeof(as->authdata_cbor));
memset(&as->authdata, 0, sizeof(as->authdata));
memset(&as->hmac_secret_enc, 0, sizeof(as->hmac_secret_enc));
return (assert->stmt[idx].largeblob_key.ptr);
}
size_t
fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx)
{
if (idx >= assert->stmt_len)
return (0);
return (assert->stmt[idx].largeblob_key.len);
}
const unsigned char *
fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx)
{
if (idx >= assert->stmt_len)
return (NULL);
return (assert->stmt[idx].authdata_ext.blob.ptr);
}
size_t
fido_assert_blob_len(const fido_assert_t *assert, size_t idx)
{
if (idx >= assert->stmt_len)
return (0);
return (assert->stmt[idx].authdata_ext.blob.len);
}
static void
fido_assert_clean_authdata(fido_assert_stmt *stmt)
{
fido_blob_reset(&stmt->authdata_cbor);
fido_assert_reset_extattr(&stmt->authdata_ext);
memset(&stmt->authdata, 0, sizeof(stmt->authdata));
}
int
@ -979,7 +1014,7 @@ fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
}
if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
&stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
&stmt->authdata, &stmt->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
@ -1017,7 +1052,7 @@ fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
}
if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
&stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
&stmt->authdata, &stmt->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
@ -1034,14 +1069,6 @@ fail:
return (r);
}
static void
fido_assert_clean_sig(fido_assert_stmt *as)
{
free(as->sig.ptr);
as->sig.ptr = NULL;
as->sig.len = 0;
}
int
fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
size_t len)
@ -1051,7 +1078,7 @@ fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
if (idx >= a->stmt_len || ptr == NULL || len == 0)
return (FIDO_ERR_INVALID_ARGUMENT);
fido_assert_clean_sig(&a->stmt[idx]);
fido_blob_reset(&a->stmt[idx].sig);
if ((sig = malloc(len)) == NULL)
return (FIDO_ERR_INTERNAL);

View File

@ -4,7 +4,6 @@
* license that can be found in the LICENSE file.
*/
#include <string.h>
#include "fido.h"
static int
@ -35,7 +34,7 @@ fido_dev_authkey_tx(fido_dev_t *dev)
memset(argv, 0, sizeof(argv));
/* add command parameters */
if ((argv[0] = cbor_build_uint8(1)) == NULL ||
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;

View File

@ -4,8 +4,6 @@
* license that can be found in the LICENSE file.
*/
#include <string.h>
#include "fido.h"
#include "fido/bio.h"
#include "fido/es256.h"
@ -59,7 +57,7 @@ fail:
}
static int
bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
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)
{
cbor_item_t *argv[5];
@ -67,6 +65,7 @@ bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
fido_blob_t *ecdh = NULL;
fido_blob_t f;
fido_blob_t hmac;
const uint8_t cmd = CTAP_CBOR_BIO_ENROLL_PRE;
int r = FIDO_ERR_INTERNAL;
memset(&f, 0, sizeof(f));
@ -75,14 +74,14 @@ bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
/* modality, subCommand */
if ((argv[0] = cbor_build_uint8(1)) == NULL ||
(argv[1] = cbor_build_uint8(cmd)) == 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(cmd, sub_argv, sub_argc, &argv[2],
if (bio_prepare_hmac(subcmd, sub_argv, sub_argc, &argv[2],
&hmac) < 0) {
fido_log_debug("%s: bio_prepare_hmac", __func__);
goto fail;
@ -95,22 +94,22 @@ bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
&argv[4], &argv[3])) != FIDO_OK) {
fido_log_debug("%s: cbor_add_pin_params", __func__);
if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
NULL, &argv[4], &argv[3])) != FIDO_OK) {
fido_log_debug("%s: cbor_add_uv_params", __func__);
goto fail;
}
} else if (token) {
if ((argv[3] = cbor_encode_pin_opt()) == NULL ||
(argv[4] = cbor_encode_pin_auth(token, &hmac)) == NULL) {
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(CTAP_CBOR_BIO_ENROLL_PRE, argv, nitems(argv),
&f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
fido_log_debug("%s: fido_tx", __func__);
r = FIDO_ERR_TX;
goto fail;
@ -461,8 +460,9 @@ fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
goto fail;
}
if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
fido_log_debug("%s: fido_dev_get_pin_token", __func__);
if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh,
pk, NULL, token)) != FIDO_OK) {
fido_log_debug("%s: fido_dev_get_uv_token", __func__);
goto fail;
}

View File

@ -4,41 +4,66 @@
* license that can be found in the LICENSE file.
*/
#include <string.h>
#include "fido.h"
fido_blob_t *
fido_blob_new(void)
{
return (calloc(1, sizeof(fido_blob_t)));
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 unsigned char *ptr, size_t len)
fido_blob_set(fido_blob_t *b, const u_char *ptr, size_t len)
{
if (b->ptr != NULL) {
explicit_bzero(b->ptr, b->len);
free(b->ptr);
b->ptr = NULL;
}
b->len = 0;
fido_blob_reset(b);
if (ptr == NULL || len == 0) {
fido_log_debug("%s: ptr=%p, len=%zu", __func__,
(const void *)ptr, len);
return (-1);
return -1;
}
if ((b->ptr = malloc(len)) == NULL) {
fido_log_debug("%s: malloc", __func__);
return (-1);
return -1;
}
memcpy(b->ptr, ptr, len);
b->len = len;
return (0);
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
@ -49,14 +74,8 @@ fido_blob_free(fido_blob_t **bp)
if (bp == NULL || (b = *bp) == NULL)
return;
if (b->ptr) {
explicit_bzero(b->ptr, b->len);
free(b->ptr);
}
explicit_bzero(b, sizeof(*b));
fido_blob_reset(b);
free(b);
*bp = NULL;
}
@ -68,11 +87,8 @@ fido_free_blob_array(fido_blob_array_t *array)
for (size_t i = 0; i < array->len; i++) {
fido_blob_t *b = &array->ptr[i];
if (b->ptr != NULL) {
explicit_bzero(b->ptr, b->len);
free(b->ptr);
b->ptr = NULL;
}
freezero(b->ptr, b->len);
b->ptr = NULL;
}
free(array->ptr);
@ -84,19 +100,34 @@ cbor_item_t *
fido_blob_encode(const fido_blob_t *b)
{
if (b == NULL || b->ptr == NULL)
return (NULL);
return NULL;
return (cbor_build_bytestring(b->ptr, b->len));
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));
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);
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;
}

View File

@ -28,8 +28,10 @@ cbor_item_t *fido_blob_encode(const fido_blob_t *);
fido_blob_t *fido_blob_new(void);
int fido_blob_decode(const cbor_item_t *, fido_blob_t *);
int fido_blob_is_empty(const fido_blob_t *);
int fido_blob_set(fido_blob_t *, const unsigned char *, size_t);
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

View File

@ -4,7 +4,6 @@
* license that can be found in the LICENSE file.
*/
#include <string.h>
#include "fido.h"
int

View File

@ -4,11 +4,8 @@
* license that can be found in the LICENSE file.
*/
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <string.h>
#include "fido.h"
static int
@ -563,21 +560,37 @@ fail:
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_extensions(const fido_cred_ext_t *ext)
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 (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
return (NULL);
if (ext->mask & FIDO_EXT_HMAC_SECRET) {
if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
if (ext->mask & FIDO_EXT_CRED_BLOB) {
if (cbor_add_bytestring(item, "credBlob", blob->ptr,
blob->len) < 0) {
cbor_decref(&item);
return (NULL);
}
@ -590,18 +603,29 @@ cbor_encode_extensions(const fido_cred_ext_t *ext)
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);
}
}
return (item);
}
cbor_item_t *
cbor_encode_options(fido_opt_t rk, fido_opt_t uv)
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);
@ -612,13 +636,12 @@ cbor_encode_options(fido_opt_t rk, fido_opt_t uv)
}
cbor_item_t *
cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv)
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);
@ -629,62 +652,55 @@ cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv)
}
cbor_item_t *
cbor_encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data)
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;
if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr,
(int)hmac_key->len, data->ptr, data->len, dgst,
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);
return (cbor_build_bytestring(dgst, 16));
outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
return (cbor_build_bytestring(dgst, outlen));
}
cbor_item_t *
cbor_encode_pin_opt(void)
cbor_encode_pin_opt(const fido_dev_t *dev)
{
return (cbor_build_uint8(1));
}
uint8_t prot;
cbor_item_t *
cbor_encode_pin_enc(const fido_blob_t *key, const fido_blob_t *pin)
{
fido_blob_t pe;
cbor_item_t *item = NULL;
if (aes256_cbc_enc(key, pin, &pe) < 0)
if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
return (NULL);
item = cbor_build_bytestring(pe.ptr, pe.len);
free(pe.ptr);
return (item);
}
static int
sha256(const unsigned char *data, size_t data_len, fido_blob_t *digest)
{
if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
return (-1);
digest->len = SHA256_DIGEST_LENGTH;
if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
free(digest->ptr);
digest->ptr = NULL;
digest->len = 0;
return (-1);
}
return (0);
return (cbor_build_uint8(prot));
}
cbor_item_t *
cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
const fido_blob_t *pin)
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;
@ -695,65 +711,54 @@ cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
#else
HMAC_CTX *ctx = NULL;
#endif
fido_blob_t *npe = NULL; /* new pin, encrypted */
fido_blob_t *ph = NULL; /* pin hash */
fido_blob_t *phe = NULL; /* pin hash, encrypted */
fido_blob_t key;
uint8_t prot;
size_t outlen;
if ((npe = fido_blob_new()) == NULL ||
(ph = fido_blob_new()) == NULL ||
(phe = fido_blob_new()) == NULL)
goto fail;
key.ptr = secret->ptr;
key.len = secret->len;
if (aes256_cbc_enc(key, new_pin, npe) < 0) {
fido_log_debug("%s: aes256_cbc_enc 1", __func__);
if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
goto fail;
}
if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
fido_log_debug("%s: sha256", __func__);
goto fail;
}
ph->len = 16; /* first 16 bytes */
if (aes256_cbc_enc(key, ph, phe) < 0) {
fido_log_debug("%s: aes256_cbc_enc 2", __func__);
goto fail;
}
if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
key.len = 32;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
HMAC_CTX_init(&ctx);
if ((md = EVP_sha256()) == NULL ||
HMAC_Init_ex(&ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
HMAC_Update(&ctx, npe->ptr, (int)npe->len) == 0 ||
HMAC_Update(&ctx, phe->ptr, (int)phe->len) == 0 ||
HMAC_Final(&ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
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;
}
#else
if ((ctx = HMAC_CTX_new()) == NULL ||
(md = EVP_sha256()) == NULL ||
HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
HMAC_Update(ctx, npe->ptr, npe->len) == 0 ||
HMAC_Update(ctx, phe->ptr, phe->len) == 0 ||
HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
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;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
if ((item = cbor_build_bytestring(dgst, 16)) == NULL) {
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:
fido_blob_free(&npe);
fido_blob_free(&ph);
fido_blob_free(&phe);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (ctx != NULL)
HMAC_CTX_free(ctx);
@ -762,112 +767,59 @@ fail:
return (item);
}
cbor_item_t *
cbor_encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin)
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)
{
const EVP_MD *md = NULL;
unsigned char dgst[SHA256_DIGEST_LENGTH];
unsigned int dgst_len;
cbor_item_t *item = NULL;
fido_blob_t *pe = NULL;
if ((pe = fido_blob_new()) == NULL)
goto fail;
if (aes256_cbc_enc(key, pin, pe) < 0) {
fido_log_debug("%s: aes256_cbc_enc", __func__);
goto fail;
}
if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr,
(int)key->len, pe->ptr, pe->len, dgst, &dgst_len) == NULL ||
dgst_len != SHA256_DIGEST_LENGTH) {
fido_log_debug("%s: HMAC", __func__);
goto fail;
}
item = cbor_build_bytestring(dgst, 16);
fail:
fido_blob_free(&pe);
return (item);
}
cbor_item_t *
cbor_encode_pin_hash_enc(const fido_blob_t *shared, const fido_blob_t *pin)
{
cbor_item_t *item = NULL;
fido_blob_t *ph = NULL;
fido_blob_t *phe = NULL;
if ((ph = fido_blob_new()) == NULL || (phe = fido_blob_new()) == NULL)
goto fail;
if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
fido_log_debug("%s: SHA256", __func__);
goto fail;
}
ph->len = 16; /* first 16 bytes */
if (aes256_cbc_enc(shared, ph, phe) < 0) {
fido_log_debug("%s: aes256_cbc_enc", __func__);
goto fail;
}
item = cbor_build_bytestring(phe->ptr, phe->len);
fail:
fido_blob_free(&ph);
fido_blob_free(&phe);
return (item);
}
cbor_item_t *
cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk,
const fido_blob_t *hmac_salt)
{
cbor_item_t *item = NULL;
cbor_item_t *param = NULL;
cbor_item_t *argv[3];
cbor_item_t *argv[4];
struct cbor_pair pair;
fido_blob_t *enc = NULL;
int r;
memset(argv, 0, sizeof(argv));
memset(&pair, 0, sizeof(pair));
if (ecdh == NULL || pk == NULL || hmac_salt->ptr == NULL) {
fido_log_debug("%s: ecdh=%p, pk=%p, hmac_salt->ptr=%p",
__func__, (const void *)ecdh, (const void *)pk,
(const void *)hmac_salt->ptr);
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 (hmac_salt->len != 32 && hmac_salt->len != 64) {
fido_log_debug("%s: hmac_salt->len=%zu", __func__,
hmac_salt->len);
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;
}
/* XXX not pin, but salt */
if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
(argv[1] = cbor_encode_pin_enc(ecdh, hmac_salt)) == NULL ||
(argv[2] = cbor_encode_set_pin_auth(ecdh, hmac_salt)) == NULL) {
(argv[1] = fido_blob_encode(enc)) == NULL ||
(argv[2] = cbor_encode_pin_auth(dev, ecdh, enc)) == NULL ||
(argv[3] = cbor_encode_pin_opt(dev)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
}
if ((param = cbor_flatten_vector(argv, 3)) == NULL) {
if ((param = cbor_flatten_vector(argv, nitems(argv))) == NULL) {
fido_log_debug("%s: cbor_flatten_vector", __func__);
goto fail;
}
if ((item = cbor_new_definite_map(1)) == NULL) {
fido_log_debug("%s: cbor_new_definite_map", __func__);
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;
}
@ -875,21 +827,61 @@ cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk,
if (!cbor_map_add(item, pair)) {
fido_log_debug("%s: cbor_map_add", __func__);
cbor_decref(&item);
item = NULL;
r = FIDO_ERR_INTERNAL;
goto fail;
}
r = FIDO_OK;
fail:
for (size_t i = 0; i < 3; i++)
if (argv[i] != NULL)
cbor_decref(&argv[i]);
cbor_vector_free(argv, nitems(argv));
if (param != NULL)
cbor_decref(&param);
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);
}
@ -1058,8 +1050,7 @@ decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
uint16_t id_len;
int ok = -1;
fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
*len);
fido_log_xxd(*buf, *len, "%s", __func__);
if (fido_buf_read(buf, len, &attcred->aaguid,
sizeof(attcred->aaguid)) < 0) {
@ -1085,7 +1076,6 @@ decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
fido_log_debug("%s: cbor_load", __func__);
fido_log_xxd(*buf, *len);
goto fail;
}
@ -1112,7 +1102,7 @@ fail:
}
static int
decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
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;
@ -1141,6 +1131,15 @@ decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
}
authdata_ext->mask |= FIDO_EXT_CRED_PROTECT;
authdata_ext->prot = cbor_get_uint8(val);
} else if (strcmp(type, "credBlob") == 0) {
if (cbor_isa_float_ctrl(val) == false ||
cbor_float_get_width(val) != CBOR_FLOAT_0 ||
cbor_is_bool(val) == false) {
fido_log_debug("%s: cbor type", __func__);
goto out;
}
if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
}
ok = 0;
@ -1151,28 +1150,25 @@ out:
}
static int
decode_extensions(const unsigned char **buf, size_t *len,
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;
fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
*len);
fido_log_xxd(*buf, *len);
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__);
fido_log_xxd(*buf, *len);
goto fail;
}
if (cbor_isa_map(item) == false ||
cbor_map_is_definite(item) == false ||
cbor_map_iter(item, authdata_ext, decode_extension) < 0) {
cbor_map_iter(item, authdata_ext, decode_cred_extension) < 0) {
fido_log_debug("%s: cbor type", __func__);
goto fail;
}
@ -1189,19 +1185,34 @@ fail:
}
static int
decode_hmac_secret_aux(const cbor_item_t *key, const cbor_item_t *val, void *arg)
decode_assert_extension(const cbor_item_t *key, const cbor_item_t *val,
void *arg)
{
fido_blob_t *out = arg;
char *type = NULL;
int ok = -1;
fido_assert_extattr_t *authdata_ext = arg;
char *type = NULL;
int ok = -1;
if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) {
if (cbor_string_copy(key, &type) < 0) {
fido_log_debug("%s: cbor type", __func__);
ok = 0; /* ignore */
goto out;
}
ok = cbor_bytestring_copy(val, &out->ptr, &out->len);
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);
@ -1209,25 +1220,23 @@ out:
}
static int
decode_hmac_secret(const unsigned char **buf, size_t *len, fido_blob_t *out)
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_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
*len);
fido_log_xxd(*buf, *len, "%s", __func__);
if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
fido_log_debug("%s: cbor_load", __func__);
fido_log_xxd(*buf, *len);
goto fail;
}
if (cbor_isa_map(item) == false ||
cbor_map_is_definite(item) == false ||
cbor_map_size(item) != 1 ||
cbor_map_iter(item, out, decode_hmac_secret_aux) < 0) {
cbor_map_iter(item, authdata_ext, decode_assert_extension) < 0) {
fido_log_debug("%s: cbor type", __func__);
goto fail;
}
@ -1267,9 +1276,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
buf = cbor_bytestring_handle(item);
len = cbor_bytestring_length(item);
fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
fido_log_xxd(buf, len);
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__);
@ -1286,7 +1293,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
if (authdata_ext != NULL) {
if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
decode_extensions(&buf, &len, authdata_ext) < 0)
decode_cred_extensions(&buf, &len, authdata_ext) < 0)
return (-1);
}
@ -1297,7 +1304,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
int
cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
fido_authdata_t *authdata, int *authdata_ext, fido_blob_t *hmac_secret_enc)
fido_authdata_t *authdata, fido_assert_extattr_t *authdata_ext)
{
const unsigned char *buf = NULL;
size_t len;
@ -1328,14 +1335,12 @@ cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
authdata->sigcount = be32toh(authdata->sigcount);
*authdata_ext = 0;
if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
/* XXX semantic leap: extensions -> hmac_secret */
if (decode_hmac_secret(&buf, &len, hmac_secret_enc) < 0) {
fido_log_debug("%s: decode_hmac_secret", __func__);
if (decode_assert_extensions(&buf, &len, authdata_ext) < 0) {
fido_log_debug("%s: decode_assert_extensions",
__func__);
return (-1);
}
*authdata_ext = FIDO_EXT_HMAC_SECRET;
}
/* XXX we should probably ensure that len == 0 at this point */
@ -1351,7 +1356,7 @@ decode_x5c(const cbor_item_t *item, void *arg)
if (x5c->len)
return (0); /* ignore */
return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len));
return (fido_blob_decode(item, x5c));
}
static int
@ -1381,8 +1386,7 @@ decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
goto out;
}
} else if (!strcmp(name, "sig")) {
if (cbor_bytestring_copy(val, &attstmt->sig.ptr,
&attstmt->sig.len) < 0) {
if (fido_blob_decode(val, &attstmt->sig) < 0) {
fido_log_debug("%s: sig", __func__);
goto out;
}
@ -1442,7 +1446,7 @@ decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
}
if (!strcmp(name, "id"))
if (cbor_bytestring_copy(val, &id->ptr, &id->len) < 0) {
if (fido_blob_decode(val, id) < 0) {
fido_log_debug("%s: cbor_bytestring_copy", __func__);
goto out;
}
@ -1496,7 +1500,7 @@ decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
goto out;
}
} else if (!strcmp(name, "id")) {
if (cbor_bytestring_copy(val, &user->id.ptr, &user->id.len) < 0) {
if (fido_blob_decode(val, &user->id) < 0) {
fido_log_debug("%s: id", __func__);
goto out;
}
@ -1567,3 +1571,64 @@ cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
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;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include <zlib.h>
#include "fido.h"
#define BOUND (1024UL * 1024UL)
static int
do_compress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz, int decomp)
{
u_long ilen, olen;
int r;
memset(out, 0, sizeof(*out));
if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND ||
origsiz > ULONG_MAX || (olen = decomp ? (u_long)origsiz :
compressBound(ilen)) > BOUND)
return FIDO_ERR_INVALID_ARGUMENT;
if ((out->ptr = calloc(1, olen)) == NULL)
return FIDO_ERR_INTERNAL;
out->len = olen;
if (decomp)
r = uncompress(out->ptr, &olen, in->ptr, ilen);
else
r = compress(out->ptr, &olen, in->ptr, ilen);
if (r != Z_OK || olen > SIZE_MAX || olen > out->len) {
fido_blob_reset(out);
return FIDO_ERR_COMPRESS;
}
out->len = olen;
return FIDO_OK;
}
int
fido_compress(fido_blob_t *out, const fido_blob_t *in)
{
return do_compress(out, in, 0, 0);
}
int
fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
{
return do_compress(out, in, origsiz, 1);
}

193
external/bsd/libfido2/dist/src/config.c vendored Normal file
View File

@ -0,0 +1,193 @@
/*
* Copyright (c) 2020 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include "fido.h"
#include "fido/config.h"
#include "fido/es256.h"
#define CMD_ENABLE_ENTATTEST 0x01
#define CMD_TOGGLE_ALWAYS_UV 0x02
#define CMD_SET_PIN_MINLEN 0x03
static int
config_prepare_hmac(uint8_t subcmd, const cbor_item_t *item, fido_blob_t *hmac)
{
uint8_t prefix[32 + 2 * sizeof(uint8_t)], cbor[128];
size_t cbor_len;
memset(prefix, 0xff, sizeof(prefix));
prefix[sizeof(prefix) - 2] = CTAP_CBOR_CONFIG;
prefix[sizeof(prefix) - 1] = subcmd;
if (item == NULL)
return fido_blob_set(hmac, prefix, sizeof(prefix));
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)
{
cbor_item_t *argv[4];
es256_pk_t *pk = NULL;
fido_blob_t *ecdh = NULL, f, hmac;
const uint8_t cmd = CTAP_CBOR_CONFIG;
int r = FIDO_ERR_INTERNAL;
memset(&f, 0, sizeof(f));
memset(&hmac, 0, sizeof(hmac));
memset(&argv, 0, sizeof(argv));
/* subCommand */
if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
goto fail;
}
/* pinProtocol, pinAuth */
if (fido_dev_can_get_uv_token(dev, pin, FIDO_OPT_OMIT)) {
if ((argv[1] = cbor_flatten_vector(paramv, paramc)) == NULL) {
fido_log_debug("%s: cbor_flatten_vector", __func__);
goto fail;
}
if (config_prepare_hmac(subcmd, argv[1], &hmac) < 0) {
fido_log_debug("%s: config_prepare_hmac", __func__);
goto fail;
}
if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != 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])) != 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) < 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)) != FIDO_OK)
return r;
return fido_rx_cbor_status(dev, ms);
}
int
fido_dev_enable_entattest(fido_dev_t *dev, const char *pin)
{
return (config_enable_entattest_wait(dev, pin, -1));
}
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)) != FIDO_OK)
return r;
return (fido_rx_cbor_status(dev, ms));
}
int
fido_dev_toggle_always_uv(fido_dev_t *dev, const char *pin)
{
return config_toggle_always_uv_wait(dev, pin, -1);
}
static int
config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force, const char *pin)
{
cbor_item_t *argv[3];
int r;
memset(argv, 0, sizeof(argv));
if ((!len && !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 (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)) != 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 char *pin,
int ms)
{
int r;
if ((r = config_pin_minlen_tx(dev, len, force, pin)) != 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)
{
return config_pin_minlen(dev, len, false, pin, -1);
}
int
fido_dev_force_pin_change(fido_dev_t *dev, const char *pin)
{
return config_pin_minlen(dev, 0, true, pin, -1);
}

View File

@ -4,12 +4,9 @@
* license that can be found in the LICENSE file.
*/
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include <string.h>
#include "fido.h"
#include "fido/es256.h"
@ -28,11 +25,17 @@ parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
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);
@ -46,6 +49,7 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
fido_blob_t *ecdh = NULL;
es256_pk_t *pk = NULL;
cbor_item_t *argv[9];
const uint8_t cmd = CTAP_CBOR_MAKECRED;
int r;
memset(&f, 0, sizeof(f));
@ -77,36 +81,37 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
/* extensions */
if (cred->ext.mask)
if ((argv[5] = cbor_encode_extensions(&cred->ext)) == NULL) {
fido_log_debug("%s: cbor_encode_extensions", __func__);
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;
}
/* options */
if (cred->rk != FIDO_OPT_OMIT || cred->uv != FIDO_OPT_OMIT)
if ((argv[6] = cbor_encode_options(cred->rk,
if ((argv[6] = cbor_encode_cred_opt(cred->rk,
cred->uv)) == NULL) {
fido_log_debug("%s: cbor_encode_options", __func__);
fido_log_debug("%s: cbor_encode_cred_opt", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
}
/* pin authentication */
if (pin) {
/* user verification */
if (fido_dev_can_get_uv_token(dev, pin, cred->uv)) {
if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
if ((r = cbor_add_pin_params(dev, &cred->cdh, pk, ecdh, pin,
&argv[7], &argv[8])) != FIDO_OK) {
fido_log_debug("%s: cbor_add_pin_params", __func__);
if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh,
pin, cred->rp.id, &argv[7], &argv[8])) != FIDO_OK) {
fido_log_debug("%s: cbor_add_uv_params", __func__);
goto fail;
}
}
/* framing and transmission */
if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 ||
if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
fido_log_debug("%s: fido_tx", __func__);
r = FIDO_ERR_TX;
@ -155,7 +160,8 @@ fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
}
static int
fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int ms)
fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
int ms)
{
int r;
@ -180,9 +186,16 @@ fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
}
static int
check_extensions(const fido_cred_ext_t *authdata_ext, const fido_cred_ext_t *ext)
check_extensions(const fido_cred_ext_t *authdata_ext,
const fido_cred_ext_t *ext)
{
return (timingsafe_bcmp(authdata_ext, ext, sizeof(*authdata_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
@ -448,10 +461,12 @@ static void
fido_cred_clean_authdata(fido_cred_t *cred)
{
free(cred->authdata_cbor.ptr);
free(cred->authdata_raw.ptr);
free(cred->attcred.id.ptr);
memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
memset(&cred->authdata_cbor, 0, sizeof(cred->authdata_cbor));
memset(&cred->authdata_raw, 0, sizeof(cred->authdata_raw));
memset(&cred->authdata, 0, sizeof(cred->authdata));
memset(&cred->attcred, 0, sizeof(cred->attcred));
}
@ -472,6 +487,9 @@ fido_cred_reset_tx(fido_cred_t *cred)
memset(&cred->rp, 0, sizeof(cred->rp));
memset(&cred->user, 0, sizeof(cred->user));
memset(&cred->excl, 0, sizeof(cred->excl));
fido_blob_reset(&cred->blob);
memset(&cred->ext, 0, sizeof(cred->ext));
cred->type = 0;
@ -500,10 +518,10 @@ fido_cred_reset_rx(fido_cred_t *cred)
{
free(cred->fmt);
cred->fmt = NULL;
fido_cred_clean_authdata(cred);
fido_cred_clean_x509(cred);
fido_cred_clean_sig(cred);
fido_blob_reset(&cred->largeblob_key);
}
void
@ -513,12 +531,9 @@ fido_cred_free(fido_cred_t **cred_p)
if (cred_p == NULL || (cred = *cred_p) == NULL)
return;
fido_cred_reset_tx(cred);
fido_cred_reset_rx(cred);
free(cred);
*cred_p = NULL;
}
@ -527,25 +542,26 @@ 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;
int r = FIDO_ERR_INVALID_ARGUMENT;
fido_cred_clean_authdata(cred);
if (ptr == NULL || len == 0) {
r = FIDO_ERR_INVALID_ARGUMENT;
if (ptr == NULL || len == 0)
goto fail;
}
if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
fido_log_debug("%s: cbor_load", __func__);
r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
}
if (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__);
r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
}
@ -565,13 +581,17 @@ int
fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
size_t len)
{
cbor_item_t *item = NULL;
int r;
cbor_item_t *item = NULL;
int r = FIDO_ERR_INVALID_ARGUMENT;
fido_cred_clean_authdata(cred);
if (ptr == NULL || len == 0) {
r = FIDO_ERR_INVALID_ARGUMENT;
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;
}
@ -584,7 +604,6 @@ fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
&cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
}
@ -766,8 +785,7 @@ fido_cred_set_extensions(fido_cred_t *cred, int ext)
if (ext == 0)
cred->ext.mask = 0;
else {
if (ext != FIDO_EXT_HMAC_SECRET &&
ext != FIDO_EXT_CRED_PROTECT)
if ((ext & FIDO_EXT_CRED_MASK) != ext)
return (FIDO_ERR_INVALID_ARGUMENT);
cred->ext.mask |= ext;
}
@ -819,6 +837,19 @@ fido_cred_set_prot(fido_cred_t *cred, int prot)
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)
{
@ -861,6 +892,12 @@ 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)
{
@ -909,6 +946,18 @@ 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_pubkey_ptr(const fido_cred_t *cred)
{
@ -1026,3 +1075,15 @@ 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);
}

View File

@ -6,8 +6,6 @@
#include <openssl/sha.h>
#include <string.h>
#include "fido.h"
#include "fido/credman.h"
#include "fido/es256.h"
@ -99,14 +97,15 @@ fail:
}
static int
credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param,
const char *pin)
credman_tx(fido_dev_t *dev, uint8_t subcmd, const fido_blob_t *param,
const char *pin, const char *rp_id, fido_opt_t uv)
{
fido_blob_t f;
fido_blob_t *ecdh = NULL;
fido_blob_t hmac;
es256_pk_t *pk = NULL;
cbor_item_t *argv[4];
const uint8_t cmd = CTAP_CBOR_CRED_MGMT_PRE;
int r = FIDO_ERR_INTERNAL;
memset(&f, 0, sizeof(f));
@ -114,14 +113,14 @@ credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param,
memset(&argv, 0, sizeof(argv));
/* subCommand */
if ((argv[0] = cbor_build_uint8(cmd)) == NULL) {
if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
goto fail;
}
/* pinProtocol, pinAuth */
if (pin != NULL) {
if (credman_prepare_hmac(cmd, param, &argv[1], &hmac) < 0) {
if (fido_dev_can_get_uv_token(dev, pin, uv)) {
if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) {
fido_log_debug("%s: credman_prepare_hmac", __func__);
goto fail;
}
@ -129,16 +128,16 @@ credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param,
fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
&argv[3], &argv[2])) != FIDO_OK) {
fido_log_debug("%s: cbor_add_pin_params", __func__);
if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
rp_id, &argv[3], &argv[2])) != FIDO_OK) {
fido_log_debug("%s: cbor_add_uv_params", __func__);
goto fail;
}
}
/* framing and transmission */
if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, nitems(argv),
&f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
fido_log_debug("%s: fido_tx", __func__);
r = FIDO_ERR_TX;
goto fail;
@ -208,7 +207,8 @@ credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
{
int r;
if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin)) != FIDO_OK ||
if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL,
FIDO_OPT_OMIT)) != FIDO_OK ||
(r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK)
return (r);
@ -221,8 +221,6 @@ fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata
{
if (fido_dev_is_fido2(dev) == false)
return (FIDO_ERR_INVALID_COMMAND);
if (pin == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);
return (credman_get_metadata_wait(dev, metadata, pin, -1));
}
@ -240,7 +238,7 @@ credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
}
switch (cbor_get_uint8(key)) {
case 6: /* user entity */
case 6:
return (cbor_decode_user(val, &cred->user));
case 7:
return (cbor_decode_cred_id(val, &cred->attcred.id));
@ -255,6 +253,8 @@ credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
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 */
@ -387,12 +387,14 @@ credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
rp_dgst.ptr = dgst;
rp_dgst.len = sizeof(dgst);
if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin)) != FIDO_OK ||
if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id,
FIDO_OPT_OMIT)) != FIDO_OK ||
(r = credman_rx_rk(dev, rk, ms)) != FIDO_OK)
return (r);
while (rk->n_rx < rk->n_alloc) {
if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL)) != FIDO_OK ||
if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL, NULL,
FIDO_OPT_FALSE)) != FIDO_OK ||
(r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK)
return (r);
rk->n_rx++;
@ -407,8 +409,6 @@ fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
{
if (fido_dev_is_fido2(dev) == false)
return (FIDO_ERR_INVALID_COMMAND);
if (pin == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);
return (credman_get_rk_wait(dev, rp_id, rk, pin, -1));
}
@ -425,7 +425,8 @@ credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
if (fido_blob_set(&cred, cred_id, cred_id_len) < 0)
return (FIDO_ERR_INVALID_ARGUMENT);
if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin)) != FIDO_OK ||
if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL,
FIDO_OPT_OMIT)) != FIDO_OK ||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
goto fail;
@ -442,8 +443,6 @@ fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
{
if (fido_dev_is_fido2(dev) == false)
return (FIDO_ERR_INVALID_COMMAND);
if (pin == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);
return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1));
}
@ -591,12 +590,14 @@ credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
{
int r;
if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin)) != FIDO_OK ||
if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL,
FIDO_OPT_OMIT)) != FIDO_OK ||
(r = credman_rx_rp(dev, rp, ms)) != FIDO_OK)
return (r);
while (rp->n_rx < rp->n_alloc) {
if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL)) != FIDO_OK ||
if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL, NULL,
FIDO_OPT_FALSE)) != FIDO_OK ||
(r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK)
return (r);
rp->n_rx++;
@ -610,8 +611,6 @@ fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
{
if (fido_dev_is_fido2(dev) == false)
return (FIDO_ERR_INVALID_COMMAND);
if (pin == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);
return (credman_get_rp_wait(dev, rp, pin, -1));
}

View File

@ -4,86 +4,9 @@
* license that can be found in the LICENSE file.
*/
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_RANDOM_H
#include <sys/random.h>
#endif
#include <openssl/sha.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "fido.h"
#if defined(_WIN32)
#include <windows.h>
#include <winternl.h>
#include <winerror.h>
#include <stdio.h>
#include <bcrypt.h>
#include <sal.h>
static int
obtain_nonce(uint64_t *nonce)
{
NTSTATUS status;
status = BCryptGenRandom(NULL, (unsigned char *)nonce, sizeof(*nonce),
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (!NT_SUCCESS(status))
return (-1);
return (0);
}
#elif defined(HAVE_ARC4RANDOM_BUF)
static int
obtain_nonce(uint64_t *nonce)
{
arc4random_buf(nonce, sizeof(*nonce));
return (0);
}
#elif defined(HAVE_GETRANDOM)
static int
obtain_nonce(uint64_t *nonce)
{
if (getrandom(nonce, sizeof(*nonce), 0) < 0)
return (-1);
return (0);
}
#elif defined(HAVE_DEV_URANDOM)
static int
obtain_nonce(uint64_t *nonce)
{
int fd = -1;
int ok = -1;
ssize_t r;
if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0)
goto fail;
if ((r = read(fd, nonce, sizeof(*nonce))) < 0 ||
(size_t)r != sizeof(*nonce))
goto fail;
ok = 0;
fail:
if (fd != -1)
close(fd);
return (ok);
}
#else
#error "please provide an implementation of obtain_nonce() for your platform"
#endif /* _WIN32 */
#ifndef TLS
#define TLS
#endif
@ -94,6 +17,7 @@ typedef struct dev_manifest_func_node {
} dev_manifest_func_node_t;
static TLS dev_manifest_func_node_t *manifest_funcs = NULL;
static TLS bool disable_u2f_fallback;
static void
find_manifest_func_node(dev_manifest_func_t f, dev_manifest_func_node_t **curr,
@ -120,32 +44,67 @@ set_random_report_len(fido_dev_t *dev)
#endif
static void
fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
fido_dev_set_extension_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
{
char * const *ptr;
const bool *val;
size_t len;
ptr = fido_cbor_info_extensions_ptr(info);
len = fido_cbor_info_extensions_len(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;
}
ptr = fido_cbor_info_options_name_ptr(info);
val = fido_cbor_info_options_value_ptr(info);
len = fido_cbor_info_options_len(info);
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) {
if (val[i] == true)
dev->flags |= FIDO_DEV_PIN_SET;
else
dev->flags |= FIDO_DEV_PIN_UNSET;
dev->flags |= val[i] ? FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET;
} else if (strcmp(ptr[i], "credMgmt") == 0 ||
strcmp(ptr[i], "credentialMgmtPreview") == 0) {
if (val[i])
dev->flags |= FIDO_DEV_CREDMAN;
} else if (strcmp(ptr[i], "uv") == 0) {
dev->flags |= val[i] ? FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET;
} else if (strcmp(ptr[i], "pinUvAuthToken") == 0) {
if (val[i])
dev->flags |= FIDO_DEV_TOKEN_PERMS;
}
}
static void
fido_dev_set_protocol_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
{
const uint8_t *ptr = fido_cbor_info_protocols_ptr(info);
size_t len = fido_cbor_info_protocols_len(info);
for (size_t i = 0; i < len; i++)
switch (ptr[i]) {
case CTAP_PIN_PROTOCOL1:
dev->flags |= FIDO_DEV_PIN_PROTOCOL1;
break;
case CTAP_PIN_PROTOCOL2:
dev->flags |= FIDO_DEV_PIN_PROTOCOL2;
break;
default:
fido_log_debug("%s: unknown protocol %u", __func__,
ptr[i]);
break;
}
}
static void
fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
{
fido_dev_set_extension_flags(dev, info);
fido_dev_set_option_flags(dev, info);
fido_dev_set_protocol_flags(dev, info);
}
static int
fido_dev_open_tx(fido_dev_t *dev, const char *path)
{
@ -162,8 +121,13 @@ fido_dev_open_tx(fido_dev_t *dev, const char *path)
return (FIDO_ERR_INVALID_ARGUMENT);
}
if (obtain_nonce(&dev->nonce) < 0) {
fido_log_debug("%s: obtain_nonce", __func__);
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);
}
@ -246,7 +210,12 @@ fido_dev_open_rx(fido_dev_t *dev, int ms)
r = FIDO_ERR_INTERNAL;
goto fail;
}
if (fido_dev_get_cbor_info_wait(dev, info, ms) != FIDO_OK) {
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 {
@ -255,8 +224,9 @@ fido_dev_open_rx(fido_dev_t *dev, int ms)
}
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)fido_cbor_info_maxmsgsiz(info));
FIDO_MAXMSG, (unsigned long)dev->maxmsgsize);
}
r = FIDO_OK;
@ -333,6 +303,11 @@ fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
if (fido_dev_register_manifest_func(fido_hid_manifest) != FIDO_OK)
return (FIDO_ERR_INTERNAL);
#ifdef NFC_LINUX
if (fido_dev_register_manifest_func(fido_nfc_manifest) != FIDO_OK)
return (FIDO_ERR_INTERNAL);
#endif
for (curr = manifest_funcs; curr != NULL; curr = curr->next) {
curr_olen = 0;
m_func = curr->manifest_func;
@ -359,6 +334,28 @@ fido_dev_open_with_info(fido_dev_t *dev)
int
fido_dev_open(fido_dev_t *dev, const char *path)
{
#ifdef NFC_LINUX
/*
* this is a hack to get existing applications up and running with nfc;
* it will *NOT* be part of a libfido2 release. to support nfc in your
* application, please change it to use fido_dev_open_with_info().
*/
if (strncmp(path, "/sys", strlen("/sys")) == 0 && strlen(path) > 4 &&
path[strlen(path) - 4] == 'n' && path[strlen(path) - 3] == 'f' &&
path[strlen(path) - 2] == 'c') {
dev->io_own = true;
dev->io = (fido_dev_io_t) {
fido_nfc_open,
fido_nfc_close,
fido_nfc_read,
fido_nfc_write,
};
dev->transport = (fido_dev_transport_t) {
fido_nfc_rx,
fido_nfc_tx,
};
}
#endif
return (fido_dev_open_wait(dev, path, -1));
}
@ -370,10 +367,24 @@ fido_dev_close(fido_dev_t *dev)
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_own || dev->io_handle == NULL || sigmask == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);
#ifdef NFC_LINUX
if (dev->transport.rx == fido_nfc_rx)
return (fido_nfc_set_sigmask(dev->io_handle, sigmask));
#endif
return (fido_hid_set_sigmask(dev->io_handle, sigmask));
}
int
fido_dev_cancel(fido_dev_t *dev)
{
@ -433,7 +444,7 @@ fido_dev_get_touch_begin(fido_dev_t *dev)
if (fido_dev_supports_pin(dev)) {
if ((argv[7] = cbor_new_definite_bytestring()) == NULL ||
(argv[8] = cbor_encode_pin_opt()) == NULL) {
(argv[8] = cbor_encode_pin_opt(dev)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
goto fail;
}
@ -524,6 +535,8 @@ 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 *
@ -553,8 +566,6 @@ fido_dev_new_with_info(const fido_dev_info_t *di)
if ((dev = calloc(1, sizeof(*dev))) == NULL)
return (NULL);
dev->cid = CTAP_CID_BROADCAST;
if (di->io.open == NULL || di->io.close == NULL ||
di->io.read == NULL || di->io.write == NULL) {
fido_log_debug("%s: NULL function", __func__);
@ -563,7 +574,9 @@ fido_dev_new_with_info(const fido_dev_info_t *di)
}
dev->io = di->io;
dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL;
dev->transport = di->transport;
dev->cid = CTAP_CID_BROADCAST;
if ((dev->path = strdup(di->path)) == NULL) {
fido_log_debug("%s: strdup", __func__);
@ -642,6 +655,30 @@ fido_dev_supports_cred_prot(const fido_dev_t *dev)
return (dev->flags & FIDO_DEV_CRED_PROT);
}
bool
fido_dev_supports_credman(const fido_dev_t *dev)
{
return (dev->flags & FIDO_DEV_CREDMAN);
}
bool
fido_dev_supports_uv(const fido_dev_t *dev)
{
return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET));
}
bool
fido_dev_has_uv(const fido_dev_t *dev)
{
return (dev->flags & FIDO_DEV_UV_SET);
}
bool
fido_dev_supports_permissions(const fido_dev_t *dev)
{
return (dev->flags & FIDO_DEV_TOKEN_PERMS);
}
void
fido_dev_force_u2f(fido_dev_t *dev)
{
@ -654,3 +691,20 @@ 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);
}

View File

@ -1,59 +1,149 @@
/*
* Copyright (c) 2018 Yubico AB. All rights reserved.
* Copyright (c) 2018-2021 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include <openssl/evp.h>
#include <openssl/sha.h>
#if defined(LIBRESSL_VERSION_NUMBER)
#include <openssl/hkdf.h>
#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
#include <openssl/kdf.h>
#endif
#include "fido.h"
#include "fido/es256.h"
#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L
static int
do_ecdh(const es256_sk_t *sk, const es256_pk_t *pk, fido_blob_t **ecdh)
hkdf_sha256(uint8_t *key, const char *info, const fido_blob_t *secret)
{
EVP_PKEY *pk_evp = NULL;
EVP_PKEY *sk_evp = NULL;
EVP_PKEY_CTX *ctx = NULL;
fido_blob_t *secret = NULL;
int ok = -1;
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, 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) || OPENSSL_VERSION_NUMBER < 0x10100000L */
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;
/* allocate blobs for secret & ecdh */
if ((secret = fido_blob_new()) == NULL ||
(*ecdh = fido_blob_new()) == NULL)
goto fail;
/* wrap the keys as openssl objects */
if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
(sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
fido_log_debug("%s: es256_to_EVP_PKEY", __func__);
goto fail;
}
/* set ecdh parameters */
if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
EVP_PKEY_derive_init(ctx) <= 0 ||
EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
fido_log_debug("%s: EVP_PKEY_derive_init", __func__);
goto fail;
}
/* perform ecdh */
if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
(secret->ptr = calloc(1, secret->len)) == NULL ||
EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
fido_log_debug("%s: EVP_PKEY_derive", __func__);
goto fail;
}
/* use sha256 as a kdf on the resulting secret */
(*ecdh)->len = SHA256_DIGEST_LENGTH;
if (((*ecdh)->ptr = calloc(1, (*ecdh)->len)) == NULL ||
SHA256(secret->ptr, secret->len, (*ecdh)->ptr) != (*ecdh)->ptr) {
fido_log_debug("%s: sha256", __func__);
if (kdf(fido_dev_get_pin_protocol(dev), *ecdh, secret) < 0) {
fido_log_debug("%s: kdf", __func__);
goto fail;
}
@ -70,38 +160,34 @@ fail:
fido_blob_free(&secret);
return (ok);
return ok;
}
int
fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh)
{
es256_sk_t *sk = NULL; /* our private key */
es256_pk_t *ak = NULL; /* authenticator's public key */
int r;
*pk = NULL; /* our public key; returned */
*ecdh = NULL; /* shared ecdh secret; returned */
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) != FIDO_OK) {
fido_log_debug("%s: fido_dev_authkey", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
}
if (do_ecdh(sk, ak, ecdh) < 0) {
if (do_ecdh(dev, sk, ak, ecdh) < 0) {
fido_log_debug("%s: do_ecdh", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
@ -117,5 +203,5 @@ fail:
fido_blob_free(ecdh);
}
return (r);
return r;
}

View File

@ -5,11 +5,8 @@
*/
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <string.h>
#include "fido.h"
#include "fido/eddsa.h"
@ -132,9 +129,7 @@ eddsa_pk_free(eddsa_pk_t **pkp)
if (pkp == NULL || (pk = *pkp) == NULL)
return;
explicit_bzero(pk, sizeof(*pk));
free(pk);
freezero(pk, sizeof(*pk));
*pkp = NULL;
}

View File

@ -40,6 +40,8 @@ fido_strerr(int n)
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:
@ -98,6 +100,10 @@ fido_strerr(int n)
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:
@ -118,6 +124,10 @@ fido_strerr(int n)
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:

View File

@ -5,11 +5,8 @@
*/
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <string.h>
#include "fido.h"
#include "fido/es256.h"
@ -147,9 +144,7 @@ es256_sk_free(es256_sk_t **skp)
if (skp == NULL || (sk = *skp) == NULL)
return;
explicit_bzero(sk, sizeof(*sk));
free(sk);
freezero(sk, sizeof(*sk));
*skp = NULL;
}
@ -167,9 +162,7 @@ es256_pk_free(es256_pk_t **pkp)
if (pkp == NULL || (pk = *pkp) == NULL)
return;
explicit_bzero(pk, sizeof(*pk));
free(pk);
freezero(pk, sizeof(*pk));
*pkp = NULL;
}

View File

@ -13,6 +13,8 @@
fido_assert_allow_cred;
fido_assert_authdata_len;
fido_assert_authdata_ptr;
fido_assert_blob_len;
fido_assert_blob_ptr;
fido_assert_clientdata_hash_len;
fido_assert_clientdata_hash_ptr;
fido_assert_count;
@ -22,6 +24,8 @@
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;
@ -30,6 +34,7 @@
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;
@ -76,6 +81,7 @@
fido_cbor_info_extensions_ptr;
fido_cbor_info_free;
fido_cbor_info_maxmsgsiz;
fido_cbor_info_maxcredbloblen;
fido_cbor_info_maxcredcntlst;
fido_cbor_info_maxcredidlen;
fido_cbor_info_fwversion;
@ -89,11 +95,16 @@
fido_cbor_info_versions_ptr;
fido_cred_authdata_len;
fido_cred_authdata_ptr;
fido_cred_authdata_raw_len;
fido_cred_authdata_raw_ptr;
fido_cred_clientdata_hash_len;
fido_cred_clientdata_hash_ptr;
fido_cred_display_name;
fido_cred_exclude;
fido_cred_flags;
fido_cred_largeblob_key_len;
fido_cred_largeblob_key_ptr;
fido_cred_sigcount;
fido_cred_fmt;
fido_cred_free;
fido_cred_id_len;
@ -127,6 +138,7 @@
fido_cred_rp_name;
fido_cred_set_authdata;
fido_cred_set_authdata_raw;
fido_cred_set_blob;
fido_cred_set_clientdata_hash;
fido_cred_set_extensions;
fido_cred_set_fmt;
@ -152,16 +164,20 @@
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;
@ -181,9 +197,20 @@
fido_dev_reset;
fido_dev_set_io_functions;
fido_dev_set_pin;
fido_dev_set_pin_minlen;
fido_dev_set_sigmask;
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;

View File

@ -11,6 +11,8 @@ _es256_pk_to_EVP_PKEY
_fido_assert_allow_cred
_fido_assert_authdata_len
_fido_assert_authdata_ptr
_fido_assert_blob_len
_fido_assert_blob_ptr
_fido_assert_clientdata_hash_len
_fido_assert_clientdata_hash_ptr
_fido_assert_count
@ -20,6 +22,8 @@ _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
@ -28,6 +32,7 @@ _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
@ -74,6 +79,7 @@ _fido_cbor_info_extensions_len
_fido_cbor_info_extensions_ptr
_fido_cbor_info_free
_fido_cbor_info_maxmsgsiz
_fido_cbor_info_maxcredbloblen
_fido_cbor_info_maxcredcntlst
_fido_cbor_info_maxcredidlen
_fido_cbor_info_fwversion
@ -87,11 +93,16 @@ _fido_cbor_info_versions_len
_fido_cbor_info_versions_ptr
_fido_cred_authdata_len
_fido_cred_authdata_ptr
_fido_cred_authdata_raw_len
_fido_cred_authdata_raw_ptr
_fido_cred_clientdata_hash_len
_fido_cred_clientdata_hash_ptr
_fido_cred_display_name
_fido_cred_exclude
_fido_cred_flags
_fido_cred_largeblob_key_len
_fido_cred_largeblob_key_ptr
_fido_cred_sigcount
_fido_cred_fmt
_fido_cred_free
_fido_cred_id_len
@ -125,6 +136,7 @@ _fido_cred_rp_id
_fido_cred_rp_name
_fido_cred_set_authdata
_fido_cred_set_authdata_raw
_fido_cred_set_blob
_fido_cred_set_clientdata_hash
_fido_cred_set_extensions
_fido_cred_set_fmt
@ -150,16 +162,20 @@ _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
@ -179,9 +195,20 @@ _fido_dev_protocol
_fido_dev_reset
_fido_dev_set_io_functions
_fido_dev_set_pin
_fido_dev_set_pin_minlen
_fido_dev_set_sigmask
_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

View File

@ -12,6 +12,8 @@ es256_pk_to_EVP_PKEY
fido_assert_allow_cred
fido_assert_authdata_len
fido_assert_authdata_ptr
fido_assert_blob_len
fido_assert_blob_ptr
fido_assert_clientdata_hash_len
fido_assert_clientdata_hash_ptr
fido_assert_count
@ -21,6 +23,8 @@ 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
@ -29,6 +33,7 @@ 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
@ -75,6 +80,7 @@ fido_cbor_info_extensions_len
fido_cbor_info_extensions_ptr
fido_cbor_info_free
fido_cbor_info_maxmsgsiz
fido_cbor_info_maxcredbloblen
fido_cbor_info_maxcredcntlst
fido_cbor_info_maxcredidlen
fido_cbor_info_fwversion
@ -88,11 +94,16 @@ fido_cbor_info_versions_len
fido_cbor_info_versions_ptr
fido_cred_authdata_len
fido_cred_authdata_ptr
fido_cred_authdata_raw_len
fido_cred_authdata_raw_ptr
fido_cred_clientdata_hash_len
fido_cred_clientdata_hash_ptr
fido_cred_display_name
fido_cred_exclude
fido_cred_flags
fido_cred_largeblob_key_len
fido_cred_largeblob_key_ptr
fido_cred_sigcount
fido_cred_fmt
fido_cred_free
fido_cred_id_len
@ -126,6 +137,7 @@ fido_cred_rp_id
fido_cred_rp_name
fido_cred_set_authdata
fido_cred_set_authdata_raw
fido_cred_set_blob
fido_cred_set_clientdata_hash
fido_cred_set_extensions
fido_cred_set_fmt
@ -151,16 +163,20 @@ 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
@ -180,9 +196,20 @@ fido_dev_protocol
fido_dev_reset
fido_dev_set_io_functions
fido_dev_set_pin
fido_dev_set_pin_minlen
fido_dev_set_sigmask
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

View File

@ -7,6 +7,14 @@
#ifndef _EXTERN_H
#define _EXTERN_H
#ifdef __MINGW32__
#include <sys/types.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include <stdint.h>
#include "fido/types.h"
@ -17,27 +25,32 @@ extern "C" {
#endif /* __cplusplus */
/* aes256 */
int aes256_cbc_dec(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
int aes256_cbc_enc(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
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_options(fido_opt_t, fido_opt_t);
cbor_item_t *cbor_encode_change_pin_auth(const fido_blob_t *,
const fido_blob_t *, const fido_blob_t *);
cbor_item_t *cbor_encode_extensions(const fido_cred_ext_t *);
cbor_item_t *cbor_encode_hmac_secret_param(const fido_blob_t *,
const es256_pk_t *, const fido_blob_t *);
cbor_item_t *cbor_encode_options(fido_opt_t, fido_opt_t);
cbor_item_t *cbor_encode_pin_auth(const fido_blob_t *, const fido_blob_t *);
cbor_item_t *cbor_encode_pin_enc(const fido_blob_t *, const fido_blob_t *);
cbor_item_t *cbor_encode_pin_hash_enc(const fido_blob_t *, const fido_blob_t *);
cbor_item_t *cbor_encode_pin_opt(void);
cbor_item_t *cbor_encode_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_set_pin_auth(const fido_blob_t *, const fido_blob_t *);
cbor_item_t *cbor_encode_user_entity(const fido_user_t *);
cbor_item_t *es256_pk_encode(const es256_pk_t *, int);
@ -46,7 +59,7 @@ int cbor_decode_attstmt(const cbor_item_t *, fido_attstmt_t *);
int cbor_decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *,
fido_authdata_t *, fido_attcred_t *, fido_cred_ext_t *);
int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *,
fido_authdata_t *, int *, 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 *);
@ -71,9 +84,16 @@ int cbor_map_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
int cbor_string_copy(const cbor_item_t *, char **);
int cbor_parse_reply(const unsigned char *, size_t, void *,
int(*)(const cbor_item_t *, const cbor_item_t *, void *));
int cbor_add_pin_params(fido_dev_t *, const fido_blob_t *, const es256_pk_t *,
const fido_blob_t *,const char *, cbor_item_t **, cbor_item_t **);
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 **);
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]))
@ -88,9 +108,23 @@ void *fido_hid_open(const char *);
void fido_hid_close(void *);
int fido_hid_read(void *, unsigned char *, size_t, int);
int fido_hid_write(void *, const unsigned char *, size_t);
int fido_hid_get_usage(const uint8_t *, size_t, uint32_t *);
int fido_hid_get_report_len(const uint8_t *, size_t, size_t *, size_t *);
int fido_hid_unix_open(const char *);
int fido_hid_unix_wait(int, int, const fido_sigset_t *);
int fido_hid_set_sigmask(void *, const fido_sigset_t *);
size_t fido_hid_report_in_len(void *);
size_t fido_hid_report_out_len(void *);
/* nfc i/o */
void *fido_nfc_open(const char *);
void fido_nfc_close(void *);
int fido_nfc_read(void *, unsigned char *, size_t, int);
int fido_nfc_write(void *, const unsigned char *, size_t);
int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int);
int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t);
int fido_nfc_set_sigmask(void *, const fido_sigset_t *);
/* generic i/o */
int fido_rx_cbor_status(fido_dev_t *, int);
int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int);
@ -101,16 +135,21 @@ int fido_tx(fido_dev_t *, uint8_t, const void *, size_t);
#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);
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);
void fido_log_xxd(const void *, size_t, const char *, ...);
void fido_log_error(int, const char *, ...);
#endif /* __GNUC__ */
#endif /* FIDO_NO_DIAGNOSTIC */
@ -121,11 +160,15 @@ int u2f_get_touch_begin(fido_dev_t *);
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 fido_dev_get_cbor_info_wait(fido_dev_t *, fido_cbor_info_t *, int);
int fido_dev_get_pin_token(fido_dev_t *, const char *, const fido_blob_t *,
const es256_pk_t *, fido_blob_t *);
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 *);
uint64_t fido_dev_maxmsgsize(const fido_dev_t *);
int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **);
bool fido_dev_supports_permissions(const fido_dev_t *);
bool fido_dev_can_get_uv_token(const fido_dev_t *, const char *, fido_opt_t);
/* misc */
void fido_assert_reset_rx(fido_assert_t *);
@ -134,6 +177,8 @@ void fido_cred_reset_rx(fido_cred_t *);
void fido_cred_reset_tx(fido_cred_t *);
int fido_check_rp_id(const char *, const unsigned char *);
int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t);
int fido_get_random(void *, size_t);
int fido_blob_serialise(fido_blob_t *, const cbor_item_t *);
/* crypto */
int fido_verify_sig_es256(const fido_blob_t *, const es256_pk_t *,
@ -145,8 +190,9 @@ int fido_verify_sig_eddsa(const fido_blob_t *, const eddsa_pk_t *,
int fido_get_signed_hash(int, fido_blob_t *, const fido_blob_t *,
const fido_blob_t *);
/* hid device manifest */
/* device manifest functions */
int fido_hid_manifest(fido_dev_info_t *, size_t, size_t *);
int fido_nfc_manifest(fido_dev_info_t *, size_t, size_t *);
/* device manifest registration */
typedef int (*dev_manifest_func_t)(fido_dev_info_t *, size_t, size_t *);
@ -159,9 +205,15 @@ uint32_t uniform_random(uint32_t);
#endif
/* internal device capability flags */
#define FIDO_DEV_PIN_SET 0x01
#define FIDO_DEV_PIN_UNSET 0x02
#define FIDO_DEV_CRED_PROT 0x04
#define FIDO_DEV_PIN_SET 0x001
#define FIDO_DEV_PIN_UNSET 0x002
#define FIDO_DEV_CRED_PROT 0x004
#define FIDO_DEV_CREDMAN 0x008
#define FIDO_DEV_PIN_PROTOCOL1 0x010
#define FIDO_DEV_PIN_PROTOCOL2 0x020
#define FIDO_DEV_UV_SET 0x040
#define FIDO_DEV_UV_UNSET 0x080
#define FIDO_DEV_TOKEN_PERMS 0x100
/* miscellanea */
#define FIDO_DUMMY_CLIENTDATA ""

View File

@ -15,11 +15,13 @@
#include <stdlib.h>
#ifdef _FIDO_INTERNAL
#include <sys/types.h>
#include <cbor.h>
#include <limits.h>
#include "blob.h"
#include "../openbsd-compat/openbsd-compat.h"
#include "blob.h"
#include "iso7816.h"
#include "extern.h"
#endif
@ -32,12 +34,6 @@
extern "C" {
#endif /* __cplusplus */
#ifdef _MSC_VER
#define FIDO_DEPRECATED(reason) __declspec(deprecated(reason))
#else
#define FIDO_DEPRECATED(reason) __attribute__((__deprecated__(reason)))
#endif
fido_assert_t *fido_assert_new(void);
fido_cred_t *fido_cred_new(void);
fido_dev_t *fido_dev_new(void);
@ -55,6 +51,7 @@ 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 *);
@ -63,8 +60,10 @@ const unsigned char *fido_assert_authdata_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *);
const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_largeblob_key_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_sig_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_user_id_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_blob_ptr(const fido_assert_t *, size_t);
char **fido_cbor_info_extensions_ptr(const fido_cbor_info_t *);
char **fido_cbor_info_options_name_ptr(const fido_cbor_info_t *);
@ -86,6 +85,7 @@ const fido_dev_info_t *fido_dev_info_ptr(const fido_dev_info_t *, size_t);
const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *);
const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *);
const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
const unsigned char *fido_cred_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_aaguid_ptr(const fido_cred_t *);
@ -93,6 +93,7 @@ const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *);
const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);
const unsigned char *fido_cred_largeblob_key_ptr(const fido_cred_t *);
int fido_assert_allow_cred(fido_assert_t *, const unsigned char *, size_t);
int fido_assert_set_authdata(fido_assert_t *, size_t, const unsigned char *,
@ -104,7 +105,8 @@ int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *,
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);
FIDO_DEPRECATED("use fido_assert_set_up/fido_assert_set_uv")
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);
@ -115,10 +117,10 @@ int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t);
int fido_cred_prot(const fido_cred_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_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 *);
FIDO_DEPRECATED("use fido_cred_set_rk/fido_cred_set_uv")
int fido_cred_set_options(fido_cred_t *, bool, bool);
int fido_cred_set_prot(fido_cred_t *, int);
int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
@ -132,11 +134,13 @@ int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t,
int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t);
int fido_cred_verify(const fido_cred_t *);
int fido_cred_verify_self(const fido_cred_t *);
int fido_dev_set_sigmask(fido_dev_t *, const fido_sigset_t *);
int fido_dev_cancel(fido_dev_t *);
int fido_dev_close(fido_dev_t *);
int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *);
int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
int fido_dev_get_retry_count(fido_dev_t *, int *);
int fido_dev_get_uv_retry_count(fido_dev_t *, int *);
int fido_dev_get_touch_begin(fido_dev_t *);
int fido_dev_get_touch_status(fido_dev_t *, int *, int);
int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
@ -153,14 +157,17 @@ 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_extensions_len(const fido_cbor_info_t *);
size_t fido_cbor_info_options_len(const fido_cbor_info_t *);
size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *);
size_t fido_cbor_info_versions_len(const fido_cbor_info_t *);
size_t fido_cred_authdata_len(const fido_cred_t *);
size_t fido_cred_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_aaguid_len(const fido_cred_t *);
@ -168,10 +175,12 @@ size_t fido_cred_user_id_len(const fido_cred_t *);
size_t fido_cred_pubkey_len(const fido_cred_t *);
size_t fido_cred_sig_len(const fido_cred_t *);
size_t fido_cred_x5c_len(const fido_cred_t *);
size_t fido_cred_largeblob_key_len(const fido_cred_t *);
uint8_t fido_assert_flags(const fido_assert_t *, size_t);
uint32_t fido_assert_sigcount(const fido_assert_t *, size_t);
uint8_t fido_cred_flags(const fido_cred_t *);
uint32_t fido_cred_sigcount(const fido_cred_t *);
uint8_t fido_dev_protocol(const fido_dev_t *);
uint8_t fido_dev_major(const fido_dev_t *);
uint8_t fido_dev_minor(const fido_dev_t *);
@ -180,14 +189,28 @@ uint8_t fido_dev_flags(const fido_dev_t *);
int16_t fido_dev_info_vendor(const fido_dev_info_t *);
int16_t fido_dev_info_product(const fido_dev_info_t *);
uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *);
uint64_t fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *);
uint64_t fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *);
uint64_t fido_cbor_info_maxcredidlen(const fido_cbor_info_t *);
uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *);
bool fido_dev_has_pin(const fido_dev_t *);
bool fido_dev_has_uv(const fido_dev_t *);
bool fido_dev_is_fido2(const fido_dev_t *);
bool fido_dev_supports_pin(const fido_dev_t *);
bool fido_dev_supports_cred_prot(const fido_dev_t *);
bool fido_dev_supports_credman(const fido_dev_t *);
bool fido_dev_supports_uv(const fido_dev_t *);
int fido_dev_largeblob_get(fido_dev_t *, const unsigned char *, size_t,
unsigned char **, size_t *);
int fido_dev_largeblob_set(fido_dev_t *, const unsigned char *, size_t,
const unsigned char *, size_t, const char *);
int fido_dev_largeblob_remove(fido_dev_t *, const unsigned char *, size_t,
const char *);
int fido_dev_largeblob_get_array(fido_dev_t *, unsigned char **, size_t *);
int fido_dev_largeblob_set_array(fido_dev_t *, const unsigned char *, size_t,
const char *);
#ifdef __cplusplus
} /* extern "C" */

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2020 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#ifndef _FIDO_CONFIG_H
#define _FIDO_CONFIG_H
#ifdef _FIDO_INTERNAL
#include "blob.h"
#include "fido/err.h"
#include "fido/param.h"
#include "fido/types.h"
#else
#include <fido.h>
#include <fido/err.h>
#include <fido/param.h>
#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 *);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* !_FIDO_CONFIG_H */

View File

@ -7,7 +7,7 @@
#ifndef _FIDO_ERR_H
#define _FIDO_ERR_H
#define FIDO_ERR_SUCCESS 0x00
#define FIDO_ERR_SUCCESS 0x00
#define FIDO_ERR_INVALID_COMMAND 0x01
#define FIDO_ERR_INVALID_PARAMETER 0x02
#define FIDO_ERR_INVALID_LENGTH 0x03
@ -22,6 +22,7 @@
#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
@ -51,6 +52,8 @@
#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
@ -65,6 +68,8 @@
#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" {

View File

@ -31,9 +31,15 @@
#define CTAP_CBOR_CLIENT_PIN 0x06
#define CTAP_CBOR_RESET 0x07
#define CTAP_CBOR_NEXT_ASSERT 0x08
#define CTAP_CBOR_LARGEBLOB 0x0c
#define CTAP_CBOR_CONFIG 0x0d
#define CTAP_CBOR_BIO_ENROLL_PRE 0x40
#define CTAP_CBOR_CRED_MGMT_PRE 0x41
/* Supported CTAP PIN/UV Auth Protocols. */
#define CTAP_PIN_PROTOCOL1 1
#define CTAP_PIN_PROTOCOL2 2
/* U2F command opcodes. */
#define U2F_CMD_REGISTER 0x01
#define U2F_CMD_AUTH 0x02
@ -43,6 +49,7 @@
#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
@ -92,10 +99,19 @@
/* 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
/* 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)
#endif /* _FIDO_INTERNAL */
#endif /* !_FIDO_PARAM_H */

View File

@ -7,6 +7,11 @@
#ifndef _FIDO_TYPES_H
#define _FIDO_TYPES_H
#ifdef __MINGW32__
#include <sys/types.h>
#endif
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
@ -43,6 +48,12 @@ typedef enum {
typedef void fido_log_handler_t(const char *);
#ifdef _WIN32
typedef int fido_sigset_t;
#else
typedef sigset_t fido_sigset_t;
#endif
#ifdef _FIDO_INTERNAL
#include "packed.h"
#include "blob.h"
@ -128,31 +139,44 @@ typedef struct fido_cred {
int type; /* cose algorithm */
char *fmt; /* credential format */
fido_cred_ext_t authdata_ext; /* decoded extensions */
fido_blob_t authdata_cbor; /* raw cbor payload */
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; /* FIDO 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 FIDO 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_enc; /* hmac secret, encrypted */
fido_blob_t hmac_secret; /* hmac secret */
int authdata_ext; /* decoded extensions */
fido_blob_t authdata_cbor; /* raw cbor payload */
fido_authdata_t authdata; /* decoded authdata payload */
fido_blob_t sig; /* signature of cdh + authdata */
fido_blob_t id; /* credential id */
fido_user_t user; /* user attributes */
fido_blob_t hmac_secret; /* hmac secret */
fido_assert_extattr_t authdata_ext; /* decoded extensions */
fido_blob_t authdata_cbor; /* raw cbor payload */
fido_authdata_t authdata; /* decoded authdata payload */
fido_blob_t sig; /* signature of cdh + authdata */
fido_blob_t largeblob_key; /* decoded large blob key */
} fido_assert_stmt;
typedef struct fido_assert_ext {
int mask; /* enabled extensions */
fido_blob_t hmac_salt; /* optional hmac-secret salt */
} fido_assert_ext_t;
typedef struct fido_assert {
char *rp_id; /* relying party id */
fido_blob_t cdh; /* client data hash */
fido_blob_t hmac_salt; /* optional hmac-secret salt */
fido_blob_array_t allow_list; /* list of allowed credentials */
fido_opt_t up; /* user presence */
fido_opt_t uv; /* user verification */
int ext; /* enabled extensions */
fido_assert_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 */
@ -184,6 +208,7 @@ typedef struct fido_cbor_info {
uint64_t maxcredcntlst; /* max number of credentials in list */
uint64_t maxcredidlen; /* max credential ID length */
uint64_t fwversion; /* firmware version */
uint64_t maxcredbloblen; /* max credBlob length */
} fido_cbor_info_t;
typedef struct fido_dev_info {
@ -209,17 +234,18 @@ struct fido_ctap_info {
})
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 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 */
} fido_dev_t;
#else

View File

@ -4,9 +4,117 @@
* license that can be found in the LICENSE file.
*/
#include <string.h>
#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)
{

View File

@ -0,0 +1,253 @@
/*
* Copyright (c) 2020 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include <sys/types.h>
#include <dev/usb/usb_ioctl.h>
#include <dev/usb/usbhid.h>
#include <errno.h>
#include <unistd.h>
#include "fido.h"
#define MAX_UHID 64
struct hid_freebsd {
int fd;
size_t report_in_len;
size_t report_out_len;
sigset_t sigmask;
const sigset_t *sigmaskp;
};
static bool
is_fido(int fd)
{
char buf[64];
struct usb_gen_descriptor ugd;
uint32_t usage_page = 0;
memset(&buf, 0, sizeof(buf));
memset(&ugd, 0, sizeof(ugd));
ugd.ugd_report_type = UHID_FEATURE_REPORT;
ugd.ugd_data = buf;
ugd.ugd_maxlen = sizeof(buf);
if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) {
fido_log_error(errno, "%s: ioctl", __func__);
return (false);
}
if (ugd.ugd_actlen > sizeof(buf) || fido_hid_get_usage(ugd.ugd_data,
ugd.ugd_actlen, &usage_page) < 0) {
fido_log_debug("%s: fido_hid_get_usage", __func__);
return (false);
}
return (usage_page == 0xf1d0);
}
static int
copy_info(fido_dev_info_t *di, const char *path)
{
int fd = -1;
int ok = -1;
struct usb_device_info udi;
memset(di, 0, sizeof(*di));
memset(&udi, 0, sizeof(udi));
if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
goto fail;
if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
fido_log_error(errno, "%s: ioctl", __func__);
strlcpy(udi.udi_vendor, "FreeBSD", sizeof(udi.udi_vendor));
strlcpy(udi.udi_product, "uhid(4)", sizeof(udi.udi_product));
udi.udi_vendorNo = 0x0b5d; /* stolen from PCI_VENDOR_OPENBSD */
}
if ((di->path = strdup(path)) == NULL ||
(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
(di->product = strdup(udi.udi_product)) == NULL)
goto fail;
di->vendor_id = (int16_t)udi.udi_vendorNo;
di->product_id = (int16_t)udi.udi_productNo;
ok = 0;
fail:
if (fd != -1)
close(fd);
if (ok < 0) {
free(di->path);
free(di->manufacturer);
free(di->product);
explicit_bzero(di, sizeof(*di));
}
return (ok);
}
int
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
{
char path[64];
size_t i;
*olen = 0;
if (ilen == 0)
return (FIDO_OK); /* nothing to do */
if (devlist == NULL || olen == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);
for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
snprintf(path, sizeof(path), "/dev/uhid%zu", i);
if (copy_info(&devlist[*olen], path) == 0) {
devlist[*olen].io = (fido_dev_io_t) {
fido_hid_open,
fido_hid_close,
fido_hid_read,
fido_hid_write,
};
++(*olen);
}
}
return (FIDO_OK);
}
void *
fido_hid_open(const char *path)
{
char buf[64];
struct hid_freebsd *ctx;
struct usb_gen_descriptor ugd;
int r;
memset(&buf, 0, sizeof(buf));
memset(&ugd, 0, sizeof(ugd));
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
return (NULL);
if ((ctx->fd = fido_hid_unix_open(path)) == -1) {
free(ctx);
return (NULL);
}
ugd.ugd_report_type = UHID_FEATURE_REPORT;
ugd.ugd_data = buf;
ugd.ugd_maxlen = sizeof(buf);
if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) ||
ugd.ugd_actlen > sizeof(buf) ||
fido_hid_get_report_len(ugd.ugd_data, ugd.ugd_actlen,
&ctx->report_in_len, &ctx->report_out_len) < 0) {
if (r == -1)
fido_log_error(errno, "%s: ioctl", __func__);
fido_log_debug("%s: using default report sizes", __func__);
ctx->report_in_len = CTAP_MAX_REPORT_LEN;
ctx->report_out_len = CTAP_MAX_REPORT_LEN;
}
return (ctx);
}
void
fido_hid_close(void *handle)
{
struct hid_freebsd *ctx = handle;
if (close(ctx->fd) == -1)
fido_log_error(errno, "%s: close", __func__);
free(ctx);
}
int
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
{
struct hid_freebsd *ctx = handle;
ctx->sigmask = *sigmask;
ctx->sigmaskp = &ctx->sigmask;
return (FIDO_OK);
}
int
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
{
struct hid_freebsd *ctx = handle;
ssize_t r;
if (len != ctx->report_in_len) {
fido_log_debug("%s: len %zu", __func__, len);
return (-1);
}
if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
fido_log_debug("%s: fd not ready", __func__);
return (-1);
}
if ((r = read(ctx->fd, buf, len)) == -1) {
fido_log_error(errno, "%s: read", __func__);
return (-1);
}
if (r < 0 || (size_t)r != len) {
fido_log_debug("%s: %zd != %zu", __func__, r, len);
return (-1);
}
return ((int)r);
}
int
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
{
struct hid_freebsd *ctx = handle;
ssize_t r;
if (len != ctx->report_out_len + 1) {
fido_log_debug("%s: len %zu", __func__, len);
return (-1);
}
if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) {
fido_log_error(errno, "%s: write", __func__);
return (-1);
}
if (r < 0 || (size_t)r != len - 1) {
fido_log_debug("%s: %zd != %zu", __func__, r, len - 1);
return (-1);
}
return ((int)len);
}
size_t
fido_hid_report_in_len(void *handle)
{
struct hid_freebsd *ctx = handle;
return (ctx->report_in_len);
}
size_t
fido_hid_report_out_len(void *handle)
{
struct hid_freebsd *ctx = handle;
return (ctx->report_out_len);
}

View File

@ -11,9 +11,8 @@
#include <fcntl.h>
#endif
#include <errno.h>
#include <hidapi.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "fido.h"
@ -97,81 +96,6 @@ copy_info(fido_dev_info_t *di, const struct hid_device_info *d)
}
#ifdef __linux__
static int
get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
{
*key = tag & 0xfc;
if ((*key & 0xf0) == 0xf0) {
fido_log_debug("%s: *key=0x%02x", __func__, *key);
return -1;
}
*key_len = tag & 0x3;
if (*key_len == 3) {
*key_len = 4;
}
return 0;
}
static int
get_key_val(const void *body, size_t key_len, uint32_t *val)
{
const uint8_t *ptr = body;
switch (key_len) {
case 0:
*val = 0;
break;
case 1:
*val = ptr[0];
break;
case 2:
*val = (uint32_t)((ptr[1] << 8) | ptr[0]);
break;
default:
fido_log_debug("%s: key_len=%zu", __func__, key_len);
return -1;
}
return 0;
}
static int
get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page,
uint32_t *usage)
{
const uint8_t *ptr = hrd->value;
size_t len = hrd->size;
while (len > 0) {
const uint8_t tag = ptr[0];
ptr++;
len--;
uint8_t key;
size_t key_len;
uint32_t key_val;
if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
get_key_val(ptr, key_len, &key_val) < 0) {
return -1;
}
if (key == 0x4) {
*usage_page = key_val;
} else if (key == 0x8) {
*usage = key_val;
}
ptr += key_len;
len -= key_len;
}
return 0;
}
static int
get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
{
@ -179,21 +103,21 @@ get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
int s = -1;
int ok = -1;
if ((fd = open(path, O_RDONLY)) < 0) {
fido_log_debug("%s: open", __func__);
if ((fd = fido_hid_unix_open(path)) == -1) {
fido_log_debug("%s: fido_hid_unix_open", __func__);
return -1;
}
if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 ||
if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) < 0 || s < 0 ||
(unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__);
fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__);
goto fail;
}
hrd->size = (unsigned)s;
if (ioctl(fd, HIDIOCGRDESC, hrd) < 0) {
fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__);
if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) < 0) {
fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
goto fail;
}
@ -208,14 +132,13 @@ fail:
static bool
is_fido(const struct hid_device_info *hdi)
{
uint32_t usage = 0;
uint32_t usage_page = 0;
struct hidraw_report_descriptor hrd;
memset(&hrd, 0, sizeof(hrd));
if (get_report_descriptor(hdi->path, &hrd) < 0 ||
get_usage_info(&hrd, &usage_page, &usage) < 0) {
fido_hid_get_usage(hrd.value, hrd.size, &usage_page) < 0) {
return false;
}
@ -265,6 +188,15 @@ fido_hid_close(void *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)
{

View File

@ -11,146 +11,38 @@
#include <linux/input.h>
#include <errno.h>
#include <fcntl.h>
#include <libudev.h>
#include <poll.h>
#include <string.h>
#include <unistd.h>
#include "fido.h"
struct hid_linux {
int fd;
size_t report_in_len;
size_t report_out_len;
int fd;
size_t report_in_len;
size_t report_out_len;
sigset_t sigmask;
const sigset_t *sigmaskp;
};
static int
get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
{
*key = tag & 0xfc;
if ((*key & 0xf0) == 0xf0) {
fido_log_debug("%s: *key=0x%02x", __func__, *key);
return (-1);
}
*key_len = tag & 0x3;
if (*key_len == 3) {
*key_len = 4;
}
return (0);
}
static int
get_key_val(const void *body, size_t key_len, uint32_t *val)
{
const uint8_t *ptr = body;
switch (key_len) {
case 0:
*val = 0;
break;
case 1:
*val = ptr[0];
break;
case 2:
*val = (uint32_t)((ptr[1] << 8) | ptr[0]);
break;
default:
fido_log_debug("%s: key_len=%zu", __func__, key_len);
return (-1);
}
return (0);
}
static int
get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page,
uint32_t *usage)
{
const uint8_t *ptr = hrd->value;
size_t len = hrd->size;
while (len > 0) {
const uint8_t tag = ptr[0];
ptr++;
len--;
uint8_t key;
size_t key_len;
uint32_t key_val;
if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
get_key_val(ptr, key_len, &key_val) < 0) {
return (-1);
}
if (key == 0x4) {
*usage_page = key_val;
} else if (key == 0x8) {
*usage = key_val;
}
ptr += key_len;
len -= key_len;
}
return (0);
}
static int
get_report_sizes(const struct hidraw_report_descriptor *hrd,
size_t *report_in_len, size_t *report_out_len)
{
const uint8_t *ptr = hrd->value;
size_t len = hrd->size;
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);
}
static int
get_report_descriptor(int fd, struct hidraw_report_descriptor *hrd)
{
int s = -1;
if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 ||
(unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__);
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, HIDIOCGRDESC, hrd) < 0) {
fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__);
if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) == -1) {
fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
return (-1);
}
@ -161,24 +53,20 @@ static bool
is_fido(const char *path)
{
int fd;
uint32_t usage = 0;
uint32_t usage_page = 0;
struct hidraw_report_descriptor hrd;
memset(&hrd, 0, sizeof(hrd));
if ((fd = open(path, O_RDONLY)) == -1) {
fido_log_debug("%s: open", __func__);
if ((fd = fido_hid_unix_open(path)) == -1)
return (false);
}
if (get_report_descriptor(fd, &hrd) < 0 ||
get_usage_info(&hrd, &usage_page, &usage) < 0) {
close(fd);
return (false);
}
fido_hid_get_usage(hrd.value, hrd.size, &usage_page) < 0)
usage_page = 0;
close(fd);
if (close(fd) == -1)
fido_log_error(errno, "%s: close", __func__);
return (usage_page == 0xf1d0);
}
@ -354,14 +242,15 @@ fido_hid_open(const char *path)
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
return (NULL);
if ((ctx->fd = open(path, O_RDWR)) < 0) {
if ((ctx->fd = fido_hid_unix_open(path)) == -1) {
free(ctx);
return (NULL);
}
if (get_report_descriptor(ctx->fd, &hrd) < 0 || get_report_sizes(&hrd,
&ctx->report_in_len, &ctx->report_out_len) < 0 ||
ctx->report_in_len == 0 || ctx->report_out_len == 0) {
if (get_report_descriptor(ctx->fd, &hrd) < 0 ||
fido_hid_get_report_len(hrd.value, hrd.size, &ctx->report_in_len,
&ctx->report_out_len) < 0 || ctx->report_in_len == 0 ||
ctx->report_out_len == 0) {
fido_log_debug("%s: using default report sizes", __func__);
ctx->report_in_len = CTAP_MAX_REPORT_LEN;
ctx->report_out_len = CTAP_MAX_REPORT_LEN;
@ -375,73 +264,21 @@ fido_hid_close(void *handle)
{
struct hid_linux *ctx = handle;
close(ctx->fd);
if (close(ctx->fd) == -1)
fido_log_error(errno, "%s: close", __func__);
free(ctx);
}
static int
timespec_to_ms(const struct timespec *ts, int upper_bound)
int
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
{
int64_t x;
int64_t y;
struct hid_linux *ctx = handle;
if (ts->tv_sec < 0 || ts->tv_sec > INT64_MAX / 1000LL ||
ts->tv_nsec < 0 || ts->tv_nsec / 1000000LL > INT64_MAX)
return (upper_bound);
ctx->sigmask = *sigmask;
ctx->sigmaskp = &ctx->sigmask;
x = ts->tv_sec * 1000LL;
y = ts->tv_nsec / 1000000LL;
if (INT64_MAX - x < y || x + y > upper_bound)
return (upper_bound);
return (int)(x + y);
}
static int
waitfd(int fd, int ms)
{
struct timespec ts_start;
struct timespec ts_now;
struct timespec ts_delta;
struct pollfd pfd;
int ms_remain;
int r;
if (ms < 0)
return (0);
memset(&pfd, 0, sizeof(pfd));
pfd.events = POLLIN;
pfd.fd = fd;
if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
fido_log_debug("%s: clock_gettime: %s", __func__,
strerror(errno));
return (-1);
}
for (ms_remain = ms; ms_remain > 0;) {
if ((r = poll(&pfd, 1, ms_remain)) > 0)
return (0);
else if (r == 0)
break;
else if (errno != EINTR) {
fido_log_debug("%s: poll: %s", __func__,
strerror(errno));
return (-1);
}
/* poll interrupted - subtract time already waited */
if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
fido_log_debug("%s: clock_gettime: %s", __func__,
strerror(errno));
return (-1);
}
timespecsub(&ts_now, &ts_start, &ts_delta);
ms_remain = ms - timespec_to_ms(&ts_delta, ms);
}
return (-1);
return (FIDO_OK);
}
int
@ -455,13 +292,18 @@ fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
return (-1);
}
if (waitfd(ctx->fd, ms) < 0) {
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)) < 0 || (size_t)r != len) {
fido_log_debug("%s: read", __func__);
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);
}
@ -479,8 +321,13 @@ fido_hid_write(void *handle, const unsigned char *buf, size_t len)
return (-1);
}
if ((r = write(ctx->fd, buf, len)) < 0 || (size_t)r != len) {
fido_log_debug("%s: write", __func__);
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);
}

View File

@ -11,23 +11,24 @@
#include <dev/usb/usbhid.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <usbhid.h>
#include "fido.h"
#define MAX_UHID 64
struct hid_netbsd {
int fd;
size_t report_in_len;
size_t report_out_len;
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. */
@ -39,47 +40,39 @@ struct hid_netbsd {
static bool
is_fido(int fd)
{
report_desc_t rdesc;
hid_data_t hdata;
hid_item_t hitem;
bool isfido;
struct usb_ctl_report_desc ucrd;
uint32_t usage_page = 0;
int raw = 1;
if ((rdesc = hid_get_report_desc(fd)) == NULL) {
fido_log_debug("%s: failed to get report descriptor",
__func__);
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 ((hdata = hid_start_parse(rdesc, 1 << hid_collection, -1))
== NULL) {
fido_log_debug("%s: failed to parse report descriptor",
__func__);
hid_dispose_report_desc(rdesc);
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);
}
isfido = false;
while ((hid_get_item(hdata, &hitem)) > 0) {
if (HID_PAGE(hitem.usage) == 0xf1d0) {
isfido = true;
break;
}
}
hid_end_parse(hdata);
hid_dispose_report_desc(rdesc);
if (!isfido)
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.
* 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, USB_HID_SET_RAW, &raw) == -1) {
fido_log_debug("%s: unable to set raw", __func__);
if (ioctl(fd, IOCTL_REQ(USB_HID_SET_RAW), &raw) == -1) {
fido_log_error(errno, "%s: unable to set raw", __func__);
return (false);
}
@ -96,17 +89,13 @@ copy_info(fido_dev_info_t *di, const char *path)
memset(di, 0, sizeof(*di));
memset(&udi, 0, sizeof(udi));
if ((fd = open(path, O_RDWR)) == -1) {
if (errno != EBUSY && errno != ENOENT)
fido_log_debug("%s: open %s: %s", __func__, path,
strerror(errno));
goto fail;
}
if (!is_fido(fd))
if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
goto fail;
if (ioctl(fd, USB_GET_DEVICEINFO, &udi) == -1)
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 ||
@ -118,8 +107,8 @@ copy_info(fido_dev_info_t *di, const char *path)
ok = 0;
fail:
if (fd != -1)
close(fd);
if (fd != -1 && close(fd) == -1)
fido_log_error(errno, "%s: close", __func__);
if (ok < 0) {
free(di->path);
@ -196,7 +185,7 @@ terrible_ping_kludge(struct hid_netbsd *ctx)
pfd.fd = ctx->fd;
pfd.events = POLLIN;
if ((n = poll(&pfd, 1, 100)) == -1) {
fido_log_debug("%s: poll: %d", __func__, errno);
fido_log_error(errno, "%s: poll", __func__);
return -1;
} else if (n == 0) {
fido_log_debug("%s: timed out", __func__);
@ -209,8 +198,8 @@ terrible_ping_kludge(struct hid_netbsd *ctx)
* so we might get an error, but we don't care - we're
* synched now.
*/
fido_log_debug("%s: got reply", __func__);
fido_log_xxd(data, ctx->report_out_len);
fido_log_xxd(data, ctx->report_out_len, "%s: got reply",
__func__);
return 0;
}
fido_log_debug("%s: no response", __func__);
@ -221,58 +210,40 @@ void *
fido_hid_open(const char *path)
{
struct hid_netbsd *ctx;
report_desc_t rdesc = NULL;
hid_data_t hdata;
int len, report_id = 0;
struct usb_ctl_report_desc ucrd;
int r;
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
goto fail0;
if ((ctx->fd = open(path, O_RDWR)) == -1)
goto fail1;
if (ioctl(ctx->fd, USB_GET_REPORT_ID, &report_id) == -1) {
fido_log_debug("%s: failed to get report ID: %s", __func__,
strerror(errno));
goto fail2;
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 ((rdesc = hid_get_report_desc(ctx->fd)) == NULL) {
fido_log_debug("%s: failed to get report descriptor",
__func__);
goto fail2;
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;
}
if ((hdata = hid_start_parse(rdesc, 1 << hid_collection, -1))
== NULL) {
fido_log_debug("%s: failed to parse report descriptor",
__func__);
goto fail3;
}
if ((len = hid_report_size(rdesc, hid_input, report_id)) <= 0 ||
(size_t)len > CTAP_MAX_REPORT_LEN) {
fido_log_debug("%s: bad input report size %d", __func__, len);
goto fail3;
}
ctx->report_in_len = (size_t)len;
if ((len = hid_report_size(rdesc, hid_output, report_id)) <= 0 ||
(size_t)len > CTAP_MAX_REPORT_LEN) {
fido_log_debug("%s: bad output report size %d", __func__, len);
goto fail3;
}
ctx->report_out_len = (size_t)len;
hid_dispose_report_desc(rdesc);
/*
* 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)
goto fail2;
if (!is_fido(ctx->fd) || terrible_ping_kludge(ctx) != 0) {
fido_hid_close(ctx);
return NULL;
}
return (ctx);
fail3: hid_dispose_report_desc(rdesc);
fail2: close(ctx->fd);
fail1: free(ctx);
fail0: return (NULL);
}
void
@ -280,86 +251,21 @@ fido_hid_close(void *handle)
{
struct hid_netbsd *ctx = handle;
close(ctx->fd);
if (close(ctx->fd) == -1)
fido_log_error(errno, "%s: close", __func__);
free(ctx);
}
static void
xstrerror(int errnum, char *buf, size_t len)
int
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
{
if (len < 1)
return;
struct hid_netbsd *ctx = handle;
memset(buf, 0, len);
ctx->sigmask = *sigmask;
ctx->sigmaskp = &ctx->sigmask;
if (strerror_r(errnum, buf, len - 1) != 0)
snprintf(buf, len - 1, "error %d", errnum);
}
static int
timespec_to_ms(const struct timespec *ts, int upper_bound)
{
int64_t x;
int64_t y;
if (ts->tv_sec < 0 || (uint64_t)ts->tv_sec > INT64_MAX / 1000LL ||
ts->tv_nsec < 0 || (uint64_t)ts->tv_nsec / 1000000LL > INT64_MAX)
return (upper_bound);
x = ts->tv_sec * 1000LL;
y = ts->tv_nsec / 1000000LL;
if (INT64_MAX - x < y || x + y > upper_bound)
return (upper_bound);
return (int)(x + y);
}
static int
fido_hid_unix_wait(int fd, int ms)
{
char ebuf[128];
struct timespec ts_start;
struct timespec ts_now;
struct timespec ts_delta;
struct pollfd pfd;
int ms_remain;
int r;
if (ms < 0)
return (0);
memset(&pfd, 0, sizeof(pfd));
pfd.events = POLLIN;
pfd.fd = fd;
if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
xstrerror(errno, ebuf, sizeof(ebuf));
fido_log_debug("%s: clock_gettime: %s", __func__, ebuf);
return (-1);
}
for (ms_remain = ms; ms_remain > 0;) {
if ((r = poll(&pfd, 1, ms_remain)) > 0)
return (0);
else if (r == 0)
break;
else if (errno != EINTR) {
xstrerror(errno, ebuf, sizeof(ebuf));
fido_log_debug("%s: poll: %s", __func__, ebuf);
return (-1);
}
/* poll interrupted - subtract time already waited */
if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
xstrerror(errno, ebuf, sizeof(ebuf));
fido_log_debug("%s: clock_gettime: %s", __func__, ebuf);
return (-1);
}
timespecsub(&ts_now, &ts_start, &ts_delta);
ms_remain = ms - timespec_to_ms(&ts_delta, ms);
}
return (-1);
return (FIDO_OK);
}
int
@ -373,13 +279,18 @@ fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
return (-1);
}
if (fido_hid_unix_wait(ctx->fd, ms) < 0) {
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 || (size_t)r != len) {
fido_log_debug("%s: read", __func__);
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);
}
@ -397,9 +308,13 @@ fido_hid_write(void *handle, const unsigned char *buf, size_t len)
return (-1);
}
if ((r = write(ctx->fd, buf + 1, len - 1)) == -1 ||
(size_t)r != len - 1) {
fido_log_debug("%s: write", __func__);
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);
}

View File

@ -11,9 +11,9 @@
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include <unistd.h>
#include "fido.h"
@ -42,22 +42,18 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
snprintf(path, sizeof(path), "/dev/fido/%zu", i);
if ((fd = open(path, O_RDWR)) == -1) {
if (errno != ENOENT && errno != ENXIO) {
fido_log_debug("%s: open %s: %s", __func__,
path, strerror(errno));
}
if ((fd = fido_hid_unix_open(path)) == -1)
continue;
}
memset(&udi, 0, sizeof(udi));
if (ioctl(fd, USB_GET_DEVICEINFO, &udi) != 0) {
fido_log_debug("%s: get device info %s: %s", __func__,
path, strerror(errno));
close(fd);
if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
fido_log_error(errno, "%s: get device info %s",
__func__, path);
if (close(fd) == -1)
fido_log_error(errno, "%s: close", __func__);
continue;
}
close(fd);
if (close(fd) == -1)
fido_log_error(errno, "%s: close", __func__);
fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x",
__func__, path, udi.udi_bus, udi.udi_addr);
@ -127,7 +123,7 @@ terrible_ping_kludge(struct hid_openbsd *ctx)
pfd.fd = ctx->fd;
pfd.events = POLLIN;
if ((n = poll(&pfd, 1, 100)) == -1) {
fido_log_debug("%s: poll: %s", __func__, strerror(errno));
fido_log_error(errno, "%s: poll", __func__);
return -1;
} else if (n == 0) {
fido_log_debug("%s: timed out", __func__);
@ -140,8 +136,8 @@ terrible_ping_kludge(struct hid_openbsd *ctx)
* so we might get an error, but we don't care - we're
* synched now.
*/
fido_log_debug("%s: got reply", __func__);
fido_log_xxd(data, ctx->report_out_len);
fido_log_xxd(data, ctx->report_out_len, "%s: got reply",
__func__);
return 0;
}
fido_log_debug("%s: no response", __func__);
@ -154,7 +150,7 @@ fido_hid_open(const char *path)
struct hid_openbsd *ret = NULL;
if ((ret = calloc(1, sizeof(*ret))) == NULL ||
(ret->fd = open(path, O_RDWR)) < 0) {
(ret->fd = fido_hid_unix_open(path)) == -1) {
free(ret);
return (NULL);
}
@ -180,10 +176,21 @@ fido_hid_close(void *handle)
{
struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
close(ctx->fd);
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)
{
(void)handle;
(void)sigmask;
return (FIDO_ERR_INTERNAL);
}
int
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
{
@ -197,10 +204,17 @@ fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
len, ctx->report_in_len);
return (-1);
}
if ((r = read(ctx->fd, buf, len)) == -1 || (size_t)r != len) {
fido_log_debug("%s: read: %s", __func__, strerror(errno));
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);
}
@ -215,11 +229,17 @@ fido_hid_write(void *handle, const unsigned char *buf, size_t len)
len, ctx->report_out_len);
return (-1);
}
if ((r = write(ctx->fd, buf + 1, len - 1)) == -1 ||
(size_t)r != len - 1) {
fido_log_debug("%s: write: %s", __func__, strerror(errno));
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);
}

Some files were not shown because too many files have changed in this diff Show More