commit df1b66988ff664020e8c8805a68e3ba46ded6c47 Author: Token2 Date: Fri May 24 11:40:07 2024 +0200 Add files via upload diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8a0e95a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,498 @@ +# Copyright (c) 2018-2022 Yubico AB. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. +# SPDX-License-Identifier: BSD-2-Clause +cmake_minimum_required(VERSION 3.7) + +# detect AppleClang; needs to come before project() +cmake_policy(SET CMP0025 NEW) + +project(libfido2 C) +# Set PIE flags for POSITION_INDEPENDENT_CODE targets, added in CMake 3.14. +if(POLICY CMP0083) + cmake_policy(SET CMP0083 NEW) +endif() + +include(CheckCCompilerFlag) +include(CheckFunctionExists) +include(CheckLibraryExists) +include(CheckSymbolExists) +include(CheckIncludeFiles) +include(CheckTypeSize) +include(GNUInstallDirs) +include(CheckPIESupported OPTIONAL RESULT_VARIABLE CHECK_PIE_SUPPORTED) +if(CHECK_PIE_SUPPORTED) + check_pie_supported(LANGUAGES C) +endif() + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_COLOR_MAKEFILE OFF) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(FIDO_MAJOR "1") +set(FIDO_MINOR "15") +set(FIDO_PATCH "0") +set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH}) + +option(BUILD_TESTS "Build the regress tests" ON) +option(BUILD_EXAMPLES "Build example programs" ON) +option(BUILD_MANPAGES "Build man pages" ON) +option(BUILD_SHARED_LIBS "Build a shared library" ON) +option(BUILD_STATIC_LIBS "Build a static library" ON) +option(BUILD_TOOLS "Build tool programs" ON) +option(FUZZ "Enable fuzzing instrumentation" OFF) +option(USE_HIDAPI "Use hidapi as the HID backend" OFF) +option(USE_PCSC "Enable experimental PCSC support" ON) +option(USE_WINHELLO "Abstract Windows Hello as a FIDO device" ON) +option(NFC_LINUX "Enable NFC support on Linux" ON) + +add_definitions(-D_FIDO_MAJOR=${FIDO_MAJOR}) +add_definitions(-D_FIDO_MINOR=${FIDO_MINOR}) +add_definitions(-D_FIDO_PATCH=${FIDO_PATCH}) + +if(BUILD_SHARED_LIBS) + set(_FIDO2_LIBRARY fido2_shared) +elseif(BUILD_STATIC_LIBS) + set(_FIDO2_LIBRARY fido2) +else() + message(FATAL_ERROR "Nothing to build (BUILD_*_LIBS=OFF)") +endif() + +if(CYGWIN OR MSYS OR MINGW) + set(WIN32 1) +endif() + +if(WIN32) + add_definitions(-DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600) +endif() + +if(APPLE) + set(CMAKE_INSTALL_NAME_DIR + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") +endif() + +if(NOT MSVC) + set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_POSIX_C_SOURCE=200809L") + set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_BSD_SOURCE") + if(APPLE) + set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_DARWIN_C_SOURCE") + set(FIDO_CFLAGS "${FIDO_CFLAGS} -D__STDC_WANT_LIB_EXT1__=1") + elseif((CMAKE_SYSTEM_NAME STREQUAL "Linux") OR MINGW OR CYGWIN) + set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_GNU_SOURCE") + set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_DEFAULT_SOURCE") + elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") + set(FIDO_CFLAGS "${FIDO_CFLAGS} -D__BSD_VISIBLE=1") + elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") + set(FIDO_CFLAGS "${FIDO_CFLAGS} -D_NETBSD_SOURCE") + endif() + set(FIDO_CFLAGS "${FIDO_CFLAGS} -std=c99") + set(CMAKE_C_FLAGS "${FIDO_CFLAGS} ${CMAKE_C_FLAGS}") +endif() + +check_c_compiler_flag("-Wshorten-64-to-32" HAVE_SHORTEN_64_TO_32) +check_c_compiler_flag("-Werror -fstack-protector-all" HAVE_STACK_PROTECTOR_ALL) + +check_include_files(cbor.h HAVE_CBOR_H) +check_include_files(endian.h HAVE_ENDIAN_H) +check_include_files(err.h HAVE_ERR_H) +check_include_files(openssl/opensslv.h HAVE_OPENSSLV_H) +check_include_files(signal.h HAVE_SIGNAL_H) +check_include_files(sys/random.h HAVE_SYS_RANDOM_H) +check_include_files(unistd.h HAVE_UNISTD_H) + +check_symbol_exists(arc4random_buf stdlib.h HAVE_ARC4RANDOM_BUF) +check_symbol_exists(asprintf stdio.h HAVE_ASPRINTF) +check_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME) +check_symbol_exists(explicit_bzero string.h HAVE_EXPLICIT_BZERO) +check_symbol_exists(freezero stdlib.h HAVE_FREEZERO) +check_symbol_exists(getline stdio.h HAVE_GETLINE) +check_symbol_exists(getopt unistd.h HAVE_GETOPT) +check_symbol_exists(getpagesize unistd.h HAVE_GETPAGESIZE) +check_symbol_exists(getrandom sys/random.h HAVE_GETRANDOM) +check_symbol_exists(memset_s string.h HAVE_MEMSET_S) +check_symbol_exists(readpassphrase readpassphrase.h HAVE_READPASSPHRASE) +check_symbol_exists(recallocarray stdlib.h HAVE_RECALLOCARRAY) +check_symbol_exists(strlcat string.h HAVE_STRLCAT) +check_symbol_exists(strlcpy string.h HAVE_STRLCPY) +check_symbol_exists(strsep string.h HAVE_STRSEP) +check_symbol_exists(sysconf unistd.h HAVE_SYSCONF) +check_symbol_exists(timespecsub sys/time.h HAVE_TIMESPECSUB) +check_symbol_exists(timingsafe_bcmp string.h HAVE_TIMINGSAFE_BCMP) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +try_compile(HAVE_POSIX_IOCTL + "${CMAKE_CURRENT_BINARY_DIR}/posix_ioctl_check.o" + "${CMAKE_CURRENT_SOURCE_DIR}/openbsd-compat/posix_ioctl_check.c" + COMPILE_DEFINITIONS "-Werror -Woverflow -Wsign-conversion") + +list(APPEND CHECK_VARIABLES + HAVE_ARC4RANDOM_BUF + HAVE_ASPRINTF + HAVE_CBOR_H + HAVE_CLOCK_GETTIME + HAVE_ENDIAN_H + HAVE_ERR_H + HAVE_FREEZERO + HAVE_GETLINE + HAVE_GETOPT + HAVE_GETPAGESIZE + HAVE_GETRANDOM + HAVE_MEMSET_S + HAVE_OPENSSLV_H + HAVE_POSIX_IOCTL + HAVE_READPASSPHRASE + HAVE_RECALLOCARRAY + HAVE_SIGNAL_H + HAVE_STRLCAT + HAVE_STRLCPY + HAVE_STRSEP + HAVE_SYSCONF + HAVE_SYS_RANDOM_H + HAVE_TIMESPECSUB + HAVE_TIMINGSAFE_BCMP + HAVE_UNISTD_H +) + +foreach(v ${CHECK_VARIABLES}) + if (${v}) + add_definitions(-D${v}) + endif() +endforeach() + +if(HAVE_EXPLICIT_BZERO AND NOT FUZZ) + add_definitions(-DHAVE_EXPLICIT_BZERO) +endif() + +if(UNIX) + add_definitions(-DHAVE_DEV_URANDOM) +endif() + + +if(MSVC) + if((NOT CBOR_INCLUDE_DIRS) OR (NOT CBOR_LIBRARY_DIRS) OR + (NOT CRYPTO_INCLUDE_DIRS) OR (NOT CRYPTO_LIBRARY_DIRS) OR + (NOT ZLIB_INCLUDE_DIRS) OR (NOT ZLIB_LIBRARY_DIRS)) + message(FATAL_ERROR "please define " + "{CBOR,CRYPTO,ZLIB}_{INCLUDE,LIBRARY}_DIRS when " + "building under msvc") + endif() + if(BUILD_TESTS AND BUILD_SHARED_LIBS AND + ((NOT CBOR_BIN_DIRS) OR (NOT ZLIB_BIN_DIRS) OR (NOT CRYPTO_BIN_DIRS))) + message(FATAL_ERROR "please define {CBOR,CRYPTO,ZLIB}_BIN_DIRS " + "when building tests") + endif() + if(NOT CBOR_LIBRARIES) + set(CBOR_LIBRARIES cbor) + endif() + if(NOT ZLIB_LIBRARIES) + set(ZLIB_LIBRARIES zlib1) + endif() + if(NOT CRYPTO_LIBRARIES) + set(CRYPTO_LIBRARIES crypto) + endif() + + set(MSVC_DISABLED_WARNINGS_LIST + "C4152" # nonstandard extension used: function/data pointer + # conversion in expression; + "C4200" # nonstandard extension used: zero-sized array in + # struct/union; + "C4201" # nonstandard extension used: nameless struct/union; + "C4204" # nonstandard extension used: non-constant aggregate + # initializer; + "C4706" # assignment within conditional expression; + "C4996" # The POSIX name for this item is deprecated. Instead, + # use the ISO C and C++ conformant name; + "C6287" # redundant code: the left and right subexpressions are identical + ) + # The construction in the following 3 lines was taken from LibreSSL's + # CMakeLists.txt. + string(REPLACE "C" " -wd" MSVC_DISABLED_WARNINGS_STR + ${MSVC_DISABLED_WARNINGS_LIST}) + string(REGEX REPLACE "[/-]W[1234][ ]?" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MP -W4 -WX ${MSVC_DISABLED_WARNINGS_STR}") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Z7 /guard:cf /sdl /RTCcsu") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi /guard:cf /sdl") + if(USE_WINHELLO) + add_definitions(-DUSE_WINHELLO) + endif() + set(NFC_LINUX OFF) +else() + include(FindPkgConfig) + pkg_search_module(CBOR libcbor) + pkg_search_module(CRYPTO libcrypto) + pkg_search_module(ZLIB zlib) + + if(NOT CBOR_FOUND AND NOT HAVE_CBOR_H) + message(FATAL_ERROR "could not find libcbor") + endif() + if(NOT CRYPTO_FOUND AND NOT HAVE_OPENSSLV_H) + message(FATAL_ERROR "could not find libcrypto") + endif() + if(NOT ZLIB_FOUND) + message(FATAL_ERROR "could not find zlib") + endif() + + if(NOT CBOR_LIBRARIES) + set(CBOR_LIBRARIES "cbor") + endif() + if(NOT CRYPTO_LIBRARIES) + set(CRYPTO_LIBRARIES "crypto") + endif() + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + pkg_search_module(UDEV libudev REQUIRED) + set(UDEV_NAME "udev") + # If using hidapi, use hidapi-hidraw. + set(HIDAPI_SUFFIX -hidraw) + if(NOT HAVE_CLOCK_GETTIME) + # Look for clock_gettime in librt. + check_library_exists(rt clock_gettime "time.h" + HAVE_CLOCK_GETTIME) + if (HAVE_CLOCK_GETTIME) + add_definitions(-DHAVE_CLOCK_GETTIME) + set(BASE_LIBRARIES ${BASE_LIBRARIES} rt) + endif() + endif() + else() + set(NFC_LINUX OFF) + endif() + + if(MINGW) + # MinGW is stuck with a flavour of C89. + add_definitions(-DFIDO_NO_DIAGNOSTIC) + add_definitions(-DWC_ERR_INVALID_CHARS=0x80) + add_compile_options(-Wno-unused-parameter) + endif() + + if(FUZZ) + set(USE_PCSC ON) + add_definitions(-DFIDO_FUZZ) + endif() + + # If building with PCSC, look for pcsc-lite. + if(USE_PCSC AND NOT (APPLE OR CYGWIN OR MSYS OR MINGW)) + pkg_search_module(PCSC libpcsclite REQUIRED) + set(PCSC_LIBRARIES pcsclite) + endif() + + if(USE_HIDAPI) + add_definitions(-DUSE_HIDAPI) + pkg_search_module(HIDAPI hidapi${HIDAPI_SUFFIX} REQUIRED) + set(HIDAPI_LIBRARIES hidapi${HIDAPI_SUFFIX}) + endif() + + if(NFC_LINUX) + add_definitions(-DUSE_NFC) + endif() + + if(WIN32) + if(USE_WINHELLO) + add_definitions(-DUSE_WINHELLO) + endif() + else() + set(USE_WINHELLO OFF) + endif() + + add_compile_options(-Wall) + add_compile_options(-Wextra) + add_compile_options(-Werror) + add_compile_options(-Wshadow) + add_compile_options(-Wcast-qual) + add_compile_options(-Wwrite-strings) + add_compile_options(-Wmissing-prototypes) + add_compile_options(-Wbad-function-cast) + add_compile_options(-Wimplicit-fallthrough) + add_compile_options(-pedantic) + add_compile_options(-pedantic-errors) + + set(EXTRA_CFLAGS "-Wconversion -Wsign-conversion") + + if(WIN32) + add_compile_options(-Wno-type-limits) + add_compile_options(-Wno-cast-function-type) + endif() + + if(HAVE_SHORTEN_64_TO_32) + add_compile_options(-Wshorten-64-to-32) + endif() + + if(HAVE_STACK_PROTECTOR_ALL) + add_compile_options(-fstack-protector-all) + endif() + + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g2") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2") + + if(CRYPTO_VERSION VERSION_GREATER_EQUAL 3.0) + add_definitions(-DOPENSSL_API_COMPAT=0x10100000L) + endif() + + if(NOT FUZZ) + set(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wframe-larger-than=2047") + endif() +endif() + +# Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 +if(CMAKE_COMPILER_IS_GNUCC) + add_compile_options(-Wno-unused-result) +endif() + +# Decide which keyword to use for thread-local storage. +if(CMAKE_COMPILER_IS_GNUCC OR + CMAKE_C_COMPILER_ID STREQUAL "Clang" OR + CMAKE_C_COMPILER_ID STREQUAL "AppleClang") + set(TLS "__thread") +elseif(WIN32) + set(TLS "__declspec(thread)") +endif() +add_definitions(-DTLS=${TLS}) + +if(USE_PCSC) + add_definitions(-DUSE_PCSC) +endif() + +# export list +if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR + CMAKE_C_COMPILER_ID STREQUAL "AppleClang")) + # clang + lld + string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} + " -exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/src/export.llvm") +elseif(NOT MSVC) + # clang/gcc + gnu ld + if(FUZZ) + string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} + " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/export.gnu") + else() + string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} + " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/export.gnu") + endif() + if(NOT WIN32) + string(CONCAT CMAKE_SHARED_LINKER_FLAGS + ${CMAKE_SHARED_LINKER_FLAGS} + " -Wl,-z,noexecstack -Wl,-z,relro,-z,now") + string(CONCAT CMAKE_EXE_LINKER_FLAGS + ${CMAKE_EXE_LINKER_FLAGS} + " -Wl,-z,noexecstack -Wl,-z,relro,-z,now") + if(FUZZ) + file(STRINGS fuzz/wrapped.sym WRAPPED_SYMBOLS) + foreach(s ${WRAPPED_SYMBOLS}) + string(CONCAT CMAKE_SHARED_LINKER_FLAGS + ${CMAKE_SHARED_LINKER_FLAGS} + " -Wl,--wrap=${s}") + endforeach() + endif() + endif() +else() + string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} + " /def:\"${CMAKE_CURRENT_SOURCE_DIR}/src/export.msvc\"") +endif() + +include_directories(${PROJECT_SOURCE_DIR}/src) +include_directories(${CBOR_INCLUDE_DIRS}) +include_directories(${CRYPTO_INCLUDE_DIRS}) +include_directories(${HIDAPI_INCLUDE_DIRS}) +include_directories(${PCSC_INCLUDE_DIRS}) +include_directories(${UDEV_INCLUDE_DIRS}) +include_directories(${ZLIB_INCLUDE_DIRS}) + +link_directories(${CBOR_LIBRARY_DIRS}) +link_directories(${CRYPTO_LIBRARY_DIRS}) +link_directories(${HIDAPI_LIBRARY_DIRS}) +link_directories(${PCSC_LIBRARY_DIRS}) +link_directories(${UDEV_LIBRARY_DIRS}) +link_directories(${ZLIB_LIBRARY_DIRS}) + +message(STATUS "BASE_LIBRARIES: ${BASE_LIBRARIES}") +message(STATUS "BUILD_EXAMPLES: ${BUILD_EXAMPLES}") +message(STATUS "BUILD_MANPAGES: ${BUILD_MANPAGES}") +message(STATUS "BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}") +message(STATUS "BUILD_STATIC_LIBS: ${BUILD_STATIC_LIBS}") +message(STATUS "BUILD_TOOLS: ${BUILD_TOOLS}") +message(STATUS "CBOR_INCLUDE_DIRS: ${CBOR_INCLUDE_DIRS}") +message(STATUS "CBOR_LIBRARIES: ${CBOR_LIBRARIES}") +message(STATUS "CBOR_LIBRARY_DIRS: ${CBOR_LIBRARY_DIRS}") +if(BUILD_TESTS) + message(STATUS "CBOR_BIN_DIRS: ${CBOR_BIN_DIRS}") +endif() +message(STATUS "CBOR_VERSION: ${CBOR_VERSION}") +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") +message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}") +message(STATUS "CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}") +message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") +message(STATUS "CMAKE_CROSSCOMPILING: ${CMAKE_CROSSCOMPILING}") +message(STATUS "CMAKE_GENERATOR_PLATFORM: ${CMAKE_GENERATOR_PLATFORM}") +message(STATUS "CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}") +message(STATUS "CMAKE_HOST_SYSTEM_PROCESSOR: ${CMAKE_HOST_SYSTEM_PROCESSOR}") +message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}") +message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") +message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") +message(STATUS "CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}") +message(STATUS "CRYPTO_INCLUDE_DIRS: ${CRYPTO_INCLUDE_DIRS}") +message(STATUS "CRYPTO_LIBRARIES: ${CRYPTO_LIBRARIES}") +message(STATUS "CRYPTO_LIBRARY_DIRS: ${CRYPTO_LIBRARY_DIRS}") +if(BUILD_TESTS) + message(STATUS "CRYPTO_BIN_DIRS: ${CRYPTO_BIN_DIRS}") +endif() +message(STATUS "CRYPTO_VERSION: ${CRYPTO_VERSION}") +message(STATUS "FIDO_VERSION: ${FIDO_VERSION}") +message(STATUS "FUZZ: ${FUZZ}") +if(FUZZ) + message(STATUS "FUZZ_LDFLAGS: ${FUZZ_LDFLAGS}") +endif() +message(STATUS "ZLIB_INCLUDE_DIRS: ${ZLIB_INCLUDE_DIRS}") +message(STATUS "ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}") +message(STATUS "ZLIB_LIBRARY_DIRS: ${ZLIB_LIBRARY_DIRS}") +if(BUILD_TESTS) + message(STATUS "ZLIB_BIN_DIRS: ${ZLIB_BIN_DIRS}") +endif() +message(STATUS "ZLIB_VERSION: ${ZLIB_VERSION}") +if(USE_HIDAPI) + message(STATUS "HIDAPI_INCLUDE_DIRS: ${HIDAPI_INCLUDE_DIRS}") + message(STATUS "HIDAPI_LIBRARIES: ${HIDAPI_LIBRARIES}") + message(STATUS "HIDAPI_LIBRARY_DIRS: ${HIDAPI_LIBRARY_DIRS}") + message(STATUS "HIDAPI_VERSION: ${HIDAPI_VERSION}") +endif() +message(STATUS "PCSC_INCLUDE_DIRS: ${PCSC_INCLUDE_DIRS}") +message(STATUS "PCSC_LIBRARIES: ${PCSC_LIBRARIES}") +message(STATUS "PCSC_LIBRARY_DIRS: ${PCSC_LIBRARY_DIRS}") +message(STATUS "PCSC_VERSION: ${PCSC_VERSION}") +message(STATUS "TLS: ${TLS}") +message(STATUS "UDEV_INCLUDE_DIRS: ${UDEV_INCLUDE_DIRS}") +message(STATUS "UDEV_LIBRARIES: ${UDEV_LIBRARIES}") +message(STATUS "UDEV_LIBRARY_DIRS: ${UDEV_LIBRARY_DIRS}") +message(STATUS "UDEV_RULES_DIR: ${UDEV_RULES_DIR}") +message(STATUS "UDEV_VERSION: ${UDEV_VERSION}") +message(STATUS "USE_HIDAPI: ${USE_HIDAPI}") +message(STATUS "USE_PCSC: ${USE_PCSC}") +message(STATUS "USE_WINHELLO: ${USE_WINHELLO}") +message(STATUS "NFC_LINUX: ${NFC_LINUX}") + +if(BUILD_TESTS) + enable_testing() +endif() + +add_subdirectory(src) + +if(BUILD_TESTS) + add_subdirectory(regress) +endif() +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif() +if(BUILD_TOOLS) + add_subdirectory(tools) +endif() +if(BUILD_MANPAGES AND NOT MSVC) + add_subdirectory(man) +endif() + +if(NOT WIN32) + if(FUZZ) + add_subdirectory(fuzz) + endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_subdirectory(udev) + endif() +endif() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bfafcbe --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2018-2024 Yubico AB. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +SPDX-License-Identifier: BSD-2-Clause diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..d2ecc58 --- /dev/null +++ b/NEWS @@ -0,0 +1,269 @@ +* Version 1.15.0 (unreleased) + ** 1.15.0 will be the last release to support OpenSSL 1.1. + +* Version 1.14.0 (2023-11-13) + ** fido2-cred -M, fido2-token -G: support raw client data via -w flag. + ** winhello: support U2F AppID extension for assertions. + ** winhello: fix restrictive parsing of the hmac-secret on assertions. + ** winhello: translate NTE_USER_CANCELLED to FIDO_ERR_OPERATION_DENIED; gh#685. + ** New API calls: + - fido_assert_authdata_raw_len; + - fido_assert_authdata_raw_ptr; + - fido_assert_set_winhello_appid. + +* Version 1.13.0 (2023-02-20) + ** Support for linking against OpenSSL on Windows; gh#668. + ** New API calls: + - fido_assert_empty_allow_list; + - fido_cred_empty_exclude_list. + ** fido2-token: fix issue when listing large blobs. + ** Improved support for different fuzzing engines. + +* Version 1.12.0 (2022-09-22) + ** Support for COSE_ES384. + ** Support for hidraw(4) on FreeBSD; gh#597. + ** Improved support for FIDO 2.1 authenticators. + ** New API calls: + - es384_pk_free; + - es384_pk_from_EC_KEY; + - es384_pk_from_EVP_PKEY; + - es384_pk_from_ptr; + - es384_pk_new; + - es384_pk_to_EVP_PKEY; + - fido_cbor_info_certs_len; + - fido_cbor_info_certs_name_ptr; + - fido_cbor_info_certs_value_ptr; + - fido_cbor_info_maxrpid_minpinlen; + - fido_cbor_info_minpinlen; + - fido_cbor_info_new_pin_required; + - fido_cbor_info_rk_remaining; + - fido_cbor_info_uv_attempts; + - fido_cbor_info_uv_modality. + ** Documentation and reliability fixes. + +* Version 1.11.0 (2022-05-03) + ** Experimental PCSC support; enable with -DUSE_PCSC. + ** Improved OpenSSL 3.0 compatibility. + ** Use RFC1951 raw deflate to compress CTAP 2.1 largeBlobs. + ** winhello: advertise "uv" instead of "clientPin". + ** winhello: support hmac-secret in fido_dev_get_assert(). + ** New API calls: + - fido_cbor_info_maxlargeblob. + ** Documentation and reliability fixes. + ** Separate build and regress targets. + +* Version 1.10.0 (2022-01-17) + ** hid_osx: handle devices with paths > 511 bytes; gh#462. + ** bio: fix CTAP2 canonical CBOR encoding in fido_bio_dev_enroll_*(); gh#480. + ** winhello: fallback to GetTopWindow() if GetForegroundWindow() fails. + ** winhello: fallback to hid_win.c if webauthn.dll isn't available. + ** New API calls: + - fido_dev_info_set; + - fido_dev_io_handle; + - fido_dev_new_with_info; + - fido_dev_open_with_info. + ** Cygwin and NetBSD build fixes. + ** Documentation and reliability fixes. + ** Support for TPM 2.0 attestation of COSE_ES256 credentials. + +* Version 1.9.0 (2021-10-27) + ** Enabled NFC support on Linux. + ** Added OpenSSL 3.0 compatibility. + ** Removed OpenSSL 1.0 compatibility. + ** Support for FIDO 2.1 "minPinLength" extension. + ** Support for COSE_EDDSA, COSE_ES256, and COSE_RS1 attestation. + ** Support for TPM 2.0 attestation. + ** Support for device timeouts; see fido_dev_set_timeout(). + ** New API calls: + - es256_pk_from_EVP_PKEY; + - fido_cred_attstmt_len; + - fido_cred_attstmt_ptr; + - fido_cred_pin_minlen; + - fido_cred_set_attstmt; + - fido_cred_set_pin_minlen; + - fido_dev_set_pin_minlen_rpid; + - fido_dev_set_timeout; + - rs256_pk_from_EVP_PKEY. + ** Reliability and portability fixes. + ** Better handling of HID devices without identification strings; gh#381. + ** Fixed detection of Windows's native webauthn API; gh#382. + +* Version 1.8.0 (2021-07-22) + ** Dropped 'Requires.private' entry from pkg-config file. + ** Better support for FIDO 2.1 authenticators. + ** Support for Windows's native webauthn API. + ** Support for attestation format 'none'. + ** New API calls: + - fido_assert_set_clientdata; + - fido_cbor_info_algorithm_cose; + - fido_cbor_info_algorithm_count; + - fido_cbor_info_algorithm_type; + - fido_cbor_info_transports_len; + - fido_cbor_info_transports_ptr; + - fido_cred_set_clientdata; + - fido_cred_set_id; + - fido_credman_set_dev_rk; + - fido_dev_is_winhello. + ** fido2-token: new -Sc option to update a resident credential. + ** Documentation and reliability fixes. + ** HID access serialisation on Linux. + +* Version 1.7.0 (2021-03-29) + ** New dependency on zlib. + ** Fixed musl build; gh#259. + ** hid_win: detect devices with vendor or product IDs > 0x7fff; gh#264. + ** Support for FIDO 2.1 authenticator configuration. + ** Support for FIDO 2.1 UV token permissions. + ** Support for FIDO 2.1 "credBlobs" and "largeBlobs" extensions. + ** New API calls: + - fido_assert_blob_len; + - fido_assert_blob_ptr; + - fido_assert_largeblob_key_len; + - fido_assert_largeblob_key_ptr; + - fido_assert_set_hmac_secret; + - fido_cbor_info_maxcredbloblen; + - fido_cred_largeblob_key_len; + - fido_cred_largeblob_key_ptr; + - fido_cred_set_blob; + - fido_dev_enable_entattest; + - fido_dev_force_pin_change; + - fido_dev_has_uv; + - fido_dev_largeblob_get; + - fido_dev_largeblob_get_array; + - fido_dev_largeblob_remove; + - fido_dev_largeblob_set; + - fido_dev_largeblob_set_array; + - fido_dev_set_pin_minlen; + - fido_dev_set_sigmask; + - fido_dev_supports_credman; + - fido_dev_supports_permissions; + - fido_dev_supports_uv; + - fido_dev_toggle_always_uv. + ** New fido_init flag to disable fido_dev_open's U2F fallback; gh#282. + ** Experimental NFC support on Linux; enable with -DNFC_LINUX. + +* Version 1.6.0 (2020-12-22) + ** Fix OpenSSL 1.0 and Cygwin builds. + ** hid_linux: fix build on 32-bit systems. + ** hid_osx: allow reads from spawned threads. + ** Documentation and reliability fixes. + ** New API calls: + - fido_cred_authdata_raw_len; + - fido_cred_authdata_raw_ptr; + - fido_cred_sigcount; + - fido_dev_get_uv_retry_count; + - fido_dev_supports_credman. + ** Hardened Windows build. + ** Native FreeBSD and NetBSD support. + ** Use CTAP2 canonical CBOR when combining hmac-secret and credProtect. + +* Version 1.5.0 (2020-09-01) + ** hid_linux: return FIDO_OK if no devices are found. + ** hid_osx: + - repair communication with U2F tokens, gh#166; + - reliability fixes. + ** fido2-{assert,cred}: new options to explicitly toggle UP, UV. + ** Support for configurable report lengths. + ** New API calls: + - fido_cbor_info_maxcredcntlst; + - fido_cbor_info_maxcredidlen; + - fido_cred_aaguid_len; + - fido_cred_aaguid_ptr; + - fido_dev_get_touch_begin; + - fido_dev_get_touch_status. + ** Use COSE_ECDH_ES256 with CTAP_CBOR_CLIENT_PIN; gh#154. + ** Allow CTAP messages up to 2048 bytes; gh#171. + ** Ensure we only list USB devices by default. + +* Version 1.4.0 (2020-04-15) + ** hid_hidapi: hidapi backend; enable with -DUSE_HIDAPI=1. + ** Fall back to U2F if the key claims to, but does not support FIDO2. + ** FIDO2 credential protection (credprot) support. + ** New API calls: + - fido_cbor_info_fwversion; + - fido_cred_prot; + - fido_cred_set_prot; + - fido_dev_set_transport_functions; + - fido_set_log_handler. + ** Support for FreeBSD. + ** Support for C++. + ** Support for MSYS. + ** Fixed EdDSA and RSA self-attestation. + +* Version 1.3.1 (2020-02-19) + ** fix zero-ing of le1 and le2 when talking to a U2F device. + ** dropping sk-libfido2 middleware, please find it in the openssh tree. + +* Version 1.3.0 (2019-11-28) + ** assert/hmac: encode public key as per spec, gh#60. + ** fido2-cred: fix creation of resident keys. + ** fido2-{assert,cred}: support for hmac-secret extension. + ** hid_osx: detect device removal, gh#56. + ** hid_osx: fix device detection in MacOS Catalina. + ** New API calls: + - fido_assert_set_authdata_raw; + - fido_assert_sigcount; + - fido_cred_set_authdata_raw; + - fido_dev_cancel. + ** Middleware library for use by OpenSSH. + ** Support for biometric enrollment. + ** Support for OpenBSD. + ** Support for self-attestation. + +* Version 1.2.0 (released 2019-07-26) + ** Credential management support. + ** New API reflecting FIDO's 3-state booleans (true, false, absent): + - fido_assert_set_up; + - fido_assert_set_uv; + - fido_cred_set_rk; + - fido_cred_set_uv. + ** Command-line tools for Windows. + ** Documentation and reliability fixes. + ** fido_{assert,cred}_set_options() are now marked as deprecated. + +* Version 1.1.0 (released 2019-05-08) + ** MacOS: fix IOKit crash on HID read. + ** Windows: fix contents of release file. + ** EdDSA (Ed25519) support. + ** fido_dev_make_cred: fix order of CBOR map keys. + ** fido_dev_get_assert: plug memory leak when operating on U2F devices. + +* Version 1.0.0 (released 2019-03-21) + ** Native HID support on Linux, MacOS, and Windows. + ** fido2-{assert,cred}: new -u option to force U2F on dual authenticators. + ** fido2-assert: support for multiple resident keys with the same RP. + ** Strict checks for CTAP2 compliance on received CBOR payloads. + ** Better fuzzing harnesses. + ** Documentation and reliability fixes. + +* Version 0.4.0 (released 2019-01-07) + ** fido2-assert: print the user id for resident credentials. + ** Fix encoding of COSE algorithms when making a credential. + ** Rework purpose of fido_cred_set_type; no ABI change. + ** Minor documentation and code fixes. + +* Version 0.3.0 (released 2018-09-11) + ** Various reliability fixes. + ** Merged fuzzing instrumentation. + ** Added regress tests. + ** Added support for FIDO 2's hmac-secret extension. + ** New API calls: + - fido_assert_hmac_secret_len; + - fido_assert_hmac_secret_ptr; + - fido_assert_set_extensions; + - fido_assert_set_hmac_salt; + - fido_cred_set_extensions; + - fido_dev_force_fido2. + ** Support for native builds with Microsoft Visual Studio 17. + +* Version 0.2.0 (released 2018-06-20) + ** Added command-line tools. + ** Added a couple of missing get functions. + +* Version 0.1.1 (released 2018-06-05) + ** Added documentation. + ** Added OpenSSL 1.0 support. + ** Minor fixes. + +* Version 0.1.0 (released 2018-05-18) + ** First beta release. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a591e38 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# fido2-manage + +*fido2-manage* is a tool allowing to manage FIDO2.1 devices over USB or NFC, including Passkey (resident keys) management +![image](https://repository-images.githubusercontent.com/803272315/648a5edf-2324-4f45-ba40-089f830a919b) + +# License + +*fido2-manage* is licensed under the BSD 2-clause license. See the LICENSE +file for the full license text. + +# Supported Platforms + +*fido2-manage* should work on any Linux distribution, but we develop and test using Ubuntu. This library is partially forked from [libfido2](https://github.com/Yubico/libfido2) to provide a FIDO2.1 key management tool under the Linux platform (we already have a solution for Windows). + +# Supported devices +FIDO2.1 (PRE or FINAL) keys from any brand can be used. However, with FIDO2.0 keys, no passkey management is possible. As a result, only basic information will be shown with 2.0 devices. + +# Installation +If you haven't installed Git yet, please do so (`sudo apt install git`) + +```bash +git clone https://github.com/Token2/fido2-manage.git + +cd fido2-manage + +sudo apt install -y zlib1g-dev pkg-config + +sudo apt install -y cmake libcbor-dev libpcsclite-dev libssl-dev libudev-dev + +rm -rf build && mkdir build && cd build && cmake -USE_PCSC=ON .. + +cd .. + +make -C build + +sudo make -C build install + +sudo ldconfig + +chmod 755 fido2-manage.sh +``` + +### Test the shell script + +`./fido2-manage.sh -list` + + ### GUI +The GUI wrapper (`gui.py`) created with Python3 is included in the package and should be ready for use on the latest Ubuntu releases. The only requirement is the tkinter module that can be installed as follows: + +`sudo apt install -y python3-tk` + +To run the script, execute it using Python from the same folder: + +`python3 gui.py` + + + + +## Automated installation script +You can download the installer bash script to run all commands in one go +```bash +wget https://raw.githubusercontent.com/token2/fido2-manage/main/install-fido2-manage.sh +``` + + +```bash +chmod +x ./install-fido2-manage.sh & ./install-fido2-manage.sh +``` + + +If no errors are shown, then you can launch the GUI: + +```bash +cd fido2-manage +``` +```bash +python2 gui.py +``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..8071bc9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Reporting libfido2 Security Issues + +To report security issues in libfido2, please contact security@yubico.com. +A PGP public key can be found at +https://www.yubico.com/support/issue-rating-system/. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..13ab729 --- /dev/null +++ b/TODO.md @@ -0,0 +1,3 @@ +add Linux-style aliases for arguments (i.e. `--device` in addition to `-device`) + +rename output tool (instead of `fido2-token` , `fido2-token2` or similar) diff --git a/fido2-manage.sh b/fido2-manage.sh new file mode 100644 index 0000000..b507e45 --- /dev/null +++ b/fido2-manage.sh @@ -0,0 +1,220 @@ +#!/bin/bash + +FIDO2_TOKEN_CMD="/usr/local/bin/fido2-token" + +list=false +info=false +device="" +pin="" +storage=false +residentKeys=false +domain="" +delete=false +credential="" +changePIN=false +setPIN=false +reset=false +uvs=false +uvd=false +help=false + +show_message() { + local message=$1 + local type=${2:-"Info"} + echo "[$type] $message" +} + +while [[ "$#" -gt 0 ]]; do + case $1 in + -list) list=true ;; + -info) info=true ;; + -device) device="$2"; shift ;; + -pin) pin="$2"; shift ;; + -storage) storage=true ;; + -residentKeys) residentKeys=true ;; + -domain) domain="$2"; shift ;; + -delete) delete=true ;; + -credential) credential="$2"; shift ;; + -changePIN) changePIN=true ;; + -setPIN) setPIN=true ;; + -reset) reset=true ;; + -uvs) uvs=true ;; + -uvd) uvd=true ;; + -help) help=true ;; + *) show_message "Unknown parameter: $1" "Error"; exit 1 ;; + esac + shift +done + + + +show_help() { + cat << EOF +FIDO2 Token Management Tool +v 0.2.2 +This is a wrapper for libfido2 library + +(c) Token2 Sarl + +Usage: ./fido2-manage.sh [-list] [-info -device ] [-storage -device ] [-residentKeys -device -domain ] [-uvs] [-uvd] [-delete -device -credential ] [-help] + +Examples: +- List available devices: + ./fido2-manage.sh -list + +- Retrieve information about a specific device: + ./fido2-manage.sh -info -device 1 + +- Retrieve storage data for credentials (number of resident keys stored and available) on a specific device: + ./fido2-manage.sh -storage -device 2 + +- Retrieve resident keys on a specific device for a domain: + ./fido2-manage.sh -residentKeys -device 1 -domain login.microsoft.com + +- Enforce user verification to be always requested on a specific device: + ./fido2-manage.sh -uvs -device 1 + +- Disable enforcing user verification to be always requested on a specific device: + ./fido2-manage.sh -uvd -device 1 + +- Sets PIN of a specific device: + ./fido2-manage.sh -setPIN -device 1 + +- Perform a factory reset on a specific device: + ./fido2-manage.sh -reset -device 1 + +- Change PIN of a specific device: + ./fido2-manage.sh -changePIN -device 1 + +- Delete a credential on a specific device: + ./fido2-manage.sh -delete -device 2 -credential Y+Dh/tSy/Q2IdZt6PW/G1A== + +- Display script help information: + ./fido2-manage.sh -help +EOF +} + +# Display help if -help parameter is provided +if $help; then + show_help + exit 0 +fi + +# Check if no arguments are specified, then show help +if ! $list && ! $info && [[ -z $device ]] && ! $storage && ! $residentKeys && [[ -z $domain ]] && ! $delete && [[ -z $credential ]] && ! $changePIN && ! $setPIN && ! $reset && ! $uvs && ! $uvd && ! $help; then + show_help + exit 1 +fi + +if $list; then + command_output=$($FIDO2_TOKEN_CMD -L 2>&1) + if [ $? -ne 0 ]; then + show_message "Error executing $FIDO2_TOKEN_CMD -L: $command_output" "Error" + exit 1 + fi + + device_count=1 + echo "$command_output" | while read -r line; do + if [[ $line =~ ^([^:]+) ]]; then + + echo "Device [$device_count] : $(echo "${line}" | grep -oP '\(([^)]+)\)' | sed 's/(\(.*\))/\1/')" + + device_count=$((device_count + 1)) + fi + done + exit 0 +fi + +if [[ -n $device ]]; then + device_index=$((device - 1)) + command_output=$($FIDO2_TOKEN_CMD -L 2>&1) + if [ $? -ne 0 ]; then + show_message "Error executing $FIDO2_TOKEN_CMD -L: $command_output" "Error" + exit 1 + fi + + if [[ $command_output =~ pcsc://slot0: ]]; then + device_string="pcsc://slot0" + else + device_string=$(echo "$command_output" | sed -n "$((device_index + 1))p" | cut -d ':' -f 1) + fi + + if $reset; then + show_message "WARNING: Factory reset will remove all data and settings of the device, including its PIN, fingerprints, and passkeys stored. The factory reset process is irreversible. Are you sure you want to proceed? (Y/N)" + read -r confirmation + if [[ $confirmation =~ [Yy] ]]; then + show_message "Touch or press the security key button when it starts blinking." + output=$($FIDO2_TOKEN_CMD -R "$device_string" 2>&1) + if [[ $output == *"FIDO_ERR_NOT_ALLOWED"* ]]; then + show_message "Error: Factory reset not allowed. Factory reset is only allowed within 10 seconds of powering up of the security key. Please unplug and plug the device back in and retry within 10 seconds after plugging in." + else + show_message "Factory reset completed." + fi + else + show_message "Factory reset canceled." + fi + exit 0 + fi + + if $changePIN; then + show_message "Enter the old and new PIN below." + $FIDO2_TOKEN_CMD -C "$device_string" + exit 0 + fi + + if $uvs; then + show_message "Enforcing user verification." + $FIDO2_TOKEN_CMD -Su "$device_string" + exit 0 + fi + + if $uvd; then + show_message "Disabling user verification." + $FIDO2_TOKEN_CMD -Du "$device_string" + exit 0 + fi + + if $setPIN; then + show_message "Enter and confirm the PIN as prompted below." + $FIDO2_TOKEN_CMD -S "$device_string" + exit 0 + fi + + if $delete && [[ -n $credential ]]; then + show_message "WARNING: Deleting a credential is irreversible. Are you sure you want to proceed? (Y/N)" + read -r confirmation + if [[ $confirmation =~ [Yy] ]]; then + $FIDO2_TOKEN_CMD -D -i "$credential" "$device_string" + show_message "Credential deleted successfully." + else + show_message "Deletion canceled." + fi + exit 0 + fi + + if $storage; then + $FIDO2_TOKEN_CMD -I -c "$device_string" $([[ -n $pin ]] && echo "-w $pin ") + exit 0 + elif $residentKeys; then + if [[ -n $domain ]]; then + output=$($FIDO2_TOKEN_CMD -L -k "$domain" "$device_string" $([[ -n $pin ]] && echo "-w $pin")) + echo "$output" | grep -oP '(\d+): (\S+) (.+)' | while read -r line; do + key_id=$(echo "$line" | awk '{print $1}') + credential_id=$(echo "$line" | awk '{print $2}') + user=$(echo "$line" | grep -oP '(\S+\s+\S+).+ es256' | head -1) + show_message "Credential ID: $credential_id, User: $user" + done + else + $FIDO2_TOKEN_CMD -L -r "$device_string" $(if [[ -n $pin ]]; then echo "-w $pin"; fi) + + fi + exit 0 + fi + + if $info; then + command_output=$($FIDO2_TOKEN_CMD -I "$device_string") + show_message "Device $device Information:" + echo "$command_output" + exit 0 + fi +fi diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..ba9f400 --- /dev/null +++ b/gui.py @@ -0,0 +1,372 @@ +import subprocess +import sys +import tkinter as tk +from tkinter import ttk, messagebox, simpledialog +import re + +# Define the command to execute +FIDO_COMMAND = './fido2-manage.sh' +#Command below for Windows +#FIDO_COMMAND = 'fido2-manage-ui.exe' +# Global variable to store the PIN +pin = None + +# Function to get device list from fido2-manage-ui.exe +def get_device_list(): + + try: + # Execute the command with '-list' argument and capture the output + result = subprocess.run([FIDO_COMMAND, '-list'], capture_output=True, text=True) + # Split the output into lines and return as a list + device_list = result.stdout.strip().split('\n') + + return device_list + + except Exception as e: + # Handle exceptions (e.g., file not found or command error) + print(f"Error executing device list command: {e}") + + return [] + +# Function to execute storage command and prepend its output to the grid +def execute_storage_command(device_digit): + global pin + command = [FIDO_COMMAND, '-storage', '-pin', pin, '-device', device_digit] + + try: + result = subprocess.run(command, capture_output=True, text=True) + # Check if the subprocess was executed successfully + #print (result) + if result.returncode == 0: + # Parse the output and insert into the treeview + for line in reversed(result.stdout.splitlines()): # Insert in reversed order to prepend + if ": " in line: + key, value = line.split(": ", 1) + tree.insert("", 0, values=(key, value)) # Insert at the top of the grid + + else: + raise subprocess.CalledProcessError(result.returncode, command) + except Exception as e: + messagebox.showerror("Error", f"Command execution failed: {e}\nOutput: {result.stderr}") + +# Function to execute info command and append its output to the grid +def execute_info_command(device_digit): + global pin + tree.delete(*tree.get_children()) + command = [FIDO_COMMAND, '-storage', '-pin', pin, '-device', device_digit] + try: + result = subprocess.run(command, capture_output=True, text=True) + + if result.stderr.find("FIDO_ERR_PIN_INVALID")!= -1: + #exit + messagebox.showerror("Error", f"Invalid PIN provided") + return + +# Check FIDO_ERR_PIN_AUTH_BLOCKED + if result.stderr.find("FIDO_ERR_PIN_AUTH_BLOCKED")!= -1: + #exit + messagebox.showerror("Error", f"Wrong PIN provided to many times. Reinsert the key") + return + +# Check FIDO_ERR_PIN_REQUIRED + if result.stderr.find("FIDO_ERR_PIN_REQUIRED")!= -1: + #exit + messagebox.showerror("Error", f"No PIN set for this key. Passkeys can be managed only with a PIN set. You will be prompted to create a PIN on the next window") + command = [FIDO_COMMAND, '-setPIN', '-device', device_digit] + if sys.platform.startswith('win'): + subprocess.Popen(['start', 'cmd', '/c'] + command, shell=True) + elif sys.platform.startswith('linux'): + subprocess.Popen(['x-terminal-emulator', '-e'] + command) + + return + + +# Check FIDO version + if result.stderr.find("FIDO_ERR_INVALID_CBOR")!= -1: + #exit + messagebox.showerror("Error", f"This is an older key (probably FIDO2.0). No passkey management is possible with this key. Only basic information will be shown.") + + + + + # Check if the subprocess was executed successfully + if result.returncode == 0: + # Parse the output and insert into the treeview + for line in result.stdout.splitlines(): + if ": " in line: + key, value = line.split(": ", 1) + tree.insert("", tk.END, values=(key, value)) # Append to the end of the grid + else: + raise subprocess.CalledProcessError(result.returncode, command) + except Exception as e: + messagebox.showerror("Error", f"Command execution failed: {e}\nOutput: {result.stderr}") + command = [FIDO_COMMAND, '-info', '-pin', pin, '-device', device_digit] + try: + result = subprocess.run(command, capture_output=True, text=True) + # Check if the subprocess was executed successfully + if result.returncode == 0: + # Parse the output and insert into the treeview + for line in result.stdout.splitlines(): + if ": " in line: + key, value = line.split(": ", 1) + tree.insert("", tk.END, values=(key, value)) # Append to the end of the grid + else: + raise subprocess.CalledProcessError(result.returncode, command) + except Exception as e: + messagebox.showerror("Error", f"Command execution failed: {e}\nOutput: {result.stderr}") + + +# Function to set the PIN +def set_pin(): + global pin + pin = simpledialog.askstring("PIN Code", "Enter PIN code (enter 0000 if no PIN is set/known):", show='*') + + + +# Function to handle selection event +def on_device_selected(event): + global pin + selected_device = device_var.get() + # Extract the digit inside the first pair of square brackets + match = re.search(r'\[(\d+)\]', selected_device) + pin = None + + set_pin() + #print (pin) + if match: + device_digit = match.group(1) + + if pin is not None: + execute_info_command(device_digit) + check_passkeys_button_state() + check_changepin_button_state() + else: + messagebox.showinfo("Device Selected", "No digit found in the selected device") + +# Function to check if the "passkeys" button should be enabled +def check_passkeys_button_state(): + passkeys_button_state = tk.DISABLED + for child in tree.get_children(): + values = tree.item(child, 'values') + if values and len(values) == 2 and values[0] == 'existing rk(s)': + try: + rk_count = int(values[1]) + if rk_count > 0: + passkeys_button_state = tk.NORMAL + break + except ValueError: + pass + + passkeys_button.config(state=passkeys_button_state) + + +# Function to check if the "passkeys" button should be enabled +def check_changepin_button_state(): + passkeys_button_state = tk.DISABLED + for child in tree.get_children(): + values = tree.item(child, 'values') + if values and len(values) == 2 and values[0] == 'remaining rk(s)': + try: + rk_count = int(values[1]) + if rk_count > 0: + passkeys_button_state = tk.NORMAL + break + except ValueError: + pass + + change_pin_button.config(state=passkeys_button_state) + + + + + + +# Function to handle "passkeys" button click +def on_passkeys_button_click(): + global pin + # Get the selected device and PIN + selected_device = device_var.get() + match = re.search(r'\[(\d+)\]', selected_device) + if match: + device_digit = match.group(1) + #pin = simpledialog.askstring("PIN Code", "Enter PIN code (enter 0000 if no PIN is set/known):", show='*') + if pin is not None: + # Execute the command to get resident keys + command = [FIDO_COMMAND, '-residentKeys', '-pin', pin, '-device', device_digit] + try: + result = subprocess.run(command, capture_output=True, text=True) + if result.returncode == 0: + # Parse the domains from the output + domains = [] + for line in result.stdout.splitlines(): + match = re.search(r'= (.+)$', line) + if match: + domains.append(match.group(1)) + + # Execute the command for each domain + cumulated_output = "" + for domain in domains: + + domain_command = [FIDO_COMMAND, '-residentKeys', '-domain', domain, '-pin', pin, '-device', device_digit] + domain_result = subprocess.run(domain_command, capture_output=True, text=True) + + if domain_result.returncode == 0: + cumulated_output += domain_result.stdout + else: + raise subprocess.CalledProcessError(domain_result.returncode, domain_command) + + # Show the cumulated output in a new window + show_output_in_new_window(cumulated_output, device_digit) + else: + raise subprocess.CalledProcessError(result.returncode, command) + except Exception as e: + messagebox.showerror("Error", f"Command execution failed: {e}\nOutput: {result.stderr}") + else: + messagebox.showinfo("Device Selected", "No digit found in the selected device") + +def change_pin(): + # Get the selected device and PIN + selected_device = device_var.get() + # Extract the digit inside the first pair of square brackets + match = re.search(r'\[(\d+)\]', selected_device) + if match: + device_digit = match.group(1) + command = [FIDO_COMMAND, '-changePIN', '-device', device_digit] + if sys.platform.startswith('win'): + subprocess.Popen(['start', 'cmd', '/c'] + command, shell=True) + elif sys.platform.startswith('linux'): + subprocess.Popen(['x-terminal-emulator', '-e'] + command) + + + + + + pass + + +def refresh_combobox(): + # Implement your refresh logic here + # For example, you can update the values in the combobox + # based on some external data source or trigger a refresh action. + device_combobox.set("") # Clear the selected value + tree.delete(*tree.get_children()) + passkeys_button.config(state=tk.DISABLED) + change_pin_button.config(state=tk.DISABLED) + device_list = get_device_list() # Assuming you have a function to get the device list + if not device_list: + print("No devices found.") + device_combobox['values'] = device_list # Update the combobox values + + +# Function to show the output in a new window +def show_output_in_new_window(output,device_digit): + # Create a new window + new_window = tk.Toplevel(root) + new_window.geometry("500x450") + new_window.title("Resident Keys / Passkeys") + + # Create a Treeview widget for displaying output + tree_new_window = ttk.Treeview(new_window, columns=("Credential ID", "User"), show="headings") + # Set column headings + tree_new_window.heading("Credential ID", text="Credential ID") + tree_new_window.heading("User", text="User") + tree_new_window.pack(expand=True, fill=tk.BOTH, padx=10, pady=10) + + # Add scrollbars to the Treeview + tree_scrollbar_y = ttk.Scrollbar(new_window, orient="vertical", command=tree_new_window.yview) + tree_scrollbar_y.pack(side="right", fill="y") + tree_new_window.configure(yscrollcommand=tree_scrollbar_y.set) + tree_scrollbar_x = ttk.Scrollbar(new_window, orient="horizontal", command=tree_new_window.xview) + tree_scrollbar_x.pack(side="bottom", fill="x") + tree_new_window.configure(xscrollcommand=tree_scrollbar_x.set) + + # Parse the output and insert into the Treeview + for line in output.splitlines(): + if "Credential ID: " in line and "User: " in line: + credential_id = line.split("Credential ID: ")[1].split(",")[0].strip() + user = line.split("User: ")[1].strip() + tree_new_window.insert("", tk.END, values=(credential_id, user)) + + # Function to handle show value button click + def show_selected_value(): + selected_item = tree_new_window.selection() + if selected_item: + value = tree_new_window.item(selected_item, 'values')[0] # Get the value of the selected item + new_window.destroy() + command = [FIDO_COMMAND, '-delete', '-device', device_digit, '-credential', value] + if sys.platform.startswith('win'): + subprocess.Popen(['start', 'cmd', '/c'] + command, shell=True) + elif sys.platform.startswith('linux'): + subprocess.Popen(['x-terminal-emulator', '-e'] + command) + + # Create the "Show Value" button + show_value_button = tk.Button(new_window, text="delete passkey", command=show_selected_value) + show_value_button.pack(pady=10) + + + + +def show_about_message(): + messagebox.showinfo("About", "The FIDO2.1 Security Key Management Tool is a utility designed to manage and interact with FIDO2.1 security keys.\r\nIt provides functionalities to view information, manage relying parties, and perform various operations on connected FIDO2.1 devices.\r\n\r\n(c)TOKEN2 Sarl\r\nVersoix, Switzerland") + + +# Create the main application window +root = tk.Tk() +root.geometry("700x500") # Width x Height +root.title("FIDO2.1 Manager - Python version 0.1 - (c) Token2") + +# Create a frame for the first three elements +top_frame = ttk.Frame(root) +top_frame.pack(side=tk.TOP, fill=tk.X) + +# Create a label for the dropdown +label = tk.Label(top_frame, text="Select Device:") +label.pack(side=tk.LEFT, padx=10, pady=10) + +# Create a ComboBox (dropdown) and populate it with device list +device_list = get_device_list() +if not device_list: + device_list = "No devices found." +device_var = tk.StringVar() +device_combobox = ttk.Combobox(top_frame, textvariable=device_var, values=device_list, width=60) +device_combobox.pack(side=tk.LEFT, padx=10, pady=10) +device_combobox.bind("<>", on_device_selected) +# Create the refresh button +refresh_button = tk.Button(top_frame, text="Refresh", command=refresh_combobox) +refresh_button.pack(side=tk.LEFT, padx=10, pady=10) + + +# Create a Treeview widget for displaying output with scrollbars +tree_frame = ttk.Frame(root) +tree_frame.pack(expand=True, fill=tk.BOTH, padx=10, pady=10) +tree_scrollbar_y = ttk.Scrollbar(tree_frame, orient="vertical") +tree_scrollbar_x = ttk.Scrollbar(tree_frame, orient="horizontal") +tree = ttk.Treeview(tree_frame, columns=("Key", "Value"), show="headings", yscrollcommand=tree_scrollbar_y.set, xscrollcommand=tree_scrollbar_x.set) +tree_scrollbar_y.config(command=tree.yview) +tree_scrollbar_x.config(command=tree.xview) +tree_scrollbar_y.pack(side="right", fill="y") +tree_scrollbar_x.pack(side="bottom", fill="x") +# Set column headings +tree.heading("Key", text="Key") +tree.heading("Value", text="Value") +tree.pack(expand=True, fill=tk.BOTH) + +# Create the "passkeys" button +passkeys_button = ttk.Button(root, text="Passkeys", state=tk.DISABLED, command=on_passkeys_button_click) +passkeys_button.pack(side=tk.LEFT, padx=5, pady=10) + +# Create the "Change PIN" button +change_pin_button = ttk.Button(root, text="Change PIN", state=tk.DISABLED, command=change_pin) +change_pin_button.pack(side=tk.LEFT, padx=5, pady=10) + + + +about_button = ttk.Button(root, text="About", command=show_about_message) +about_button.pack(side=tk.RIGHT, padx=5, pady=10) + + + + +# Run the Tkinter main loop +root.mainloop() + diff --git a/install-fido2-manage.sh b/install-fido2-manage.sh new file mode 100644 index 0000000..c8edf45 --- /dev/null +++ b/install-fido2-manage.sh @@ -0,0 +1,11 @@ +#!/bin/bash +git clone https://github.com/Token2/fido2-manage.git +cd fido2-manage +sudo apt install -y zlib1g-dev pkg-config cmake libcbor-dev libpcsclite-dev libssl-dev libudev-dev +rm -rf build && mkdir build && cd build && cmake -USE_PCSC=ON .. +cd .. +make -C build +sudo make -C build install +sudo ldconfig +chmod 755 fido2-manage.sh +sudo apt-get -y install python3-tk