mirror of
https://git.wownero.com/wownero/wownero.git
synced 2024-08-15 01:03:23 +00:00
device/trezor: webusb transport added, cmake fixes
- webusb transport based on libusb added. Provides direct access to Trezor via USB, no need for Trezor bridge. - trezor protocol message handler improved, no recursion used. Ready for upcoming integration tests. - libusb (for docker) bumped from v1.0.9 to v1.0.22, newer version required for webusb transport, for device enumeration. - cmake improvements and fixes. Cmake Trezor checks are moved to a dedicated CheckTrezor.cmake file. In case of a problem Trezor is excluded from build. - ifdefs made consistent to Ledger. - UDP Transport enumeration disabled by default in release mode
This commit is contained in:
parent
84dd674cd0
commit
2ffe53d9e6
16 changed files with 971 additions and 206 deletions
|
@ -513,15 +513,8 @@ else (HIDAPI_FOUND)
|
||||||
message(STATUS "Could not find HIDAPI")
|
message(STATUS "Could not find HIDAPI")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Protobuf, optional. Required for TREZOR.
|
# Trezor support check
|
||||||
include(FindProtobuf)
|
include(CheckTrezor)
|
||||||
find_package(Protobuf)
|
|
||||||
if(Protobuf_FOUND)
|
|
||||||
set(HAVE_PROTOBUF 1)
|
|
||||||
add_definitions(-DHAVE_PROTOBUF=1)
|
|
||||||
else(Protobuf_FOUND)
|
|
||||||
message(STATUS "Could not find Protobuf")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__")
|
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__")
|
||||||
|
@ -921,7 +914,7 @@ endif()
|
||||||
|
|
||||||
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
|
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
|
||||||
|
|
||||||
if (HIDAPI_FOUND)
|
if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
if(DEPENDS)
|
if(DEPENDS)
|
||||||
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
|
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
|
||||||
|
|
79
cmake/CheckTrezor.cmake
Normal file
79
cmake/CheckTrezor.cmake
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
OPTION(USE_DEVICE_TREZOR "Trezor support compilation" ON)
|
||||||
|
OPTION(USE_DEVICE_TREZOR_LIBUSB "Trezor LibUSB compilation" ON)
|
||||||
|
OPTION(USE_DEVICE_TREZOR_UDP_RELEASE "Trezor UdpTransport in release mode" OFF)
|
||||||
|
|
||||||
|
# Use Trezor master switch
|
||||||
|
if (USE_DEVICE_TREZOR)
|
||||||
|
# Protobuf is required to build protobuf messages for Trezor
|
||||||
|
include(FindProtobuf OPTIONAL)
|
||||||
|
find_package(Protobuf)
|
||||||
|
if(NOT Protobuf_FOUND)
|
||||||
|
message(STATUS "Could not find Protobuf")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
else()
|
||||||
|
message(STATUS "Trezor support disabled by USE_DEVICE_TREZOR")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(Protobuf_FOUND AND USE_DEVICE_TREZOR)
|
||||||
|
if (NOT "$ENV{TREZOR_PYTHON}" STREQUAL "")
|
||||||
|
set(TREZOR_PYTHON "$ENV{TREZOR_PYTHON}" CACHE INTERNAL "Copied from environment variable TREZOR_PYTHON")
|
||||||
|
else()
|
||||||
|
find_package(Python QUIET COMPONENTS Interpreter) # cmake 3.12+
|
||||||
|
if(Python_Interpreter_FOUND)
|
||||||
|
set(TREZOR_PYTHON "${Python_EXECUTABLE}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TREZOR_PYTHON)
|
||||||
|
find_package(PythonInterp)
|
||||||
|
if(PYTHONINTERP_FOUND AND PYTHON_EXECUTABLE)
|
||||||
|
set(TREZOR_PYTHON "${PYTHON_EXECUTABLE}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TREZOR_PYTHON)
|
||||||
|
message(STATUS "Trezor: Python not found")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Try to build protobuf messages
|
||||||
|
if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON)
|
||||||
|
set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIRS}")
|
||||||
|
set(ENV{PROTOBUF_PROTOC_EXECUTABLE} "${Protobuf_PROTOC_EXECUTABLE}")
|
||||||
|
execute_process(COMMAND ${TREZOR_PYTHON} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR)
|
||||||
|
if(RET)
|
||||||
|
message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})."
|
||||||
|
"OUT: ${OUT}, ERR: ${ERR}."
|
||||||
|
"Please read src/device_trezor/trezor/tools/README.md")
|
||||||
|
else()
|
||||||
|
message(STATUS "Trezor protobuf messages regenerated ${OUT}")
|
||||||
|
set(DEVICE_TREZOR_READY 1)
|
||||||
|
add_definitions(-DDEVICE_TREZOR_READY=1)
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
add_definitions(-DTREZOR_DEBUG=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_DEVICE_TREZOR_UDP_RELEASE)
|
||||||
|
add_definitions(-DWITH_DEVICE_TREZOR_UDP_RELEASE=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (Protobuf_INCLUDE_DIR)
|
||||||
|
include_directories(${Protobuf_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# LibUSB support, check for particular version
|
||||||
|
# Include support only if compilation test passes
|
||||||
|
if (USE_DEVICE_TREZOR_LIBUSB)
|
||||||
|
find_package(LibUSB)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (LibUSB_COMPILE_TEST_PASSED)
|
||||||
|
add_definitions(-DHAVE_TREZOR_LIBUSB=1)
|
||||||
|
if(LibUSB_INCLUDE_DIRS)
|
||||||
|
include_directories(${LibUSB_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
140
cmake/FindLibUSB.cmake
Normal file
140
cmake/FindLibUSB.cmake
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
# - Find libusb for portable USB support
|
||||||
|
# This module will find libusb as published by
|
||||||
|
# http://libusb.sf.net and
|
||||||
|
# http://libusb-win32.sf.net
|
||||||
|
#
|
||||||
|
# It will use PkgConfig if present and supported, else search
|
||||||
|
# it on its own. If the LibUSB_ROOT_DIR environment variable
|
||||||
|
# is defined, it will be used as base path.
|
||||||
|
# The following standard variables get defined:
|
||||||
|
# LibUSB_FOUND: true if LibUSB was found
|
||||||
|
# LibUSB_HEADER_FILE: the location of the C header file
|
||||||
|
# LibUSB_INCLUDE_DIRS: the directory that contains the include file
|
||||||
|
# LibUSB_LIBRARIES: the library
|
||||||
|
# source: https://github.com/IntelRealSense/librealsense
|
||||||
|
|
||||||
|
include ( CheckLibraryExists )
|
||||||
|
include ( CheckIncludeFile )
|
||||||
|
|
||||||
|
find_package ( PkgConfig )
|
||||||
|
if ( PKG_CONFIG_FOUND )
|
||||||
|
pkg_check_modules ( PKGCONFIG_LIBUSB libusb-1.0 )
|
||||||
|
if ( NOT PKGCONFIG_LIBUSB_FOUND )
|
||||||
|
pkg_check_modules ( PKGCONFIG_LIBUSB libusb )
|
||||||
|
endif ( NOT PKGCONFIG_LIBUSB_FOUND )
|
||||||
|
endif ( PKG_CONFIG_FOUND )
|
||||||
|
|
||||||
|
if ( PKGCONFIG_LIBUSB_FOUND )
|
||||||
|
set ( LibUSB_INCLUDE_DIRS ${PKGCONFIG_LIBUSB_INCLUDE_DIRS} )
|
||||||
|
foreach ( i ${PKGCONFIG_LIBUSB_LIBRARIES} )
|
||||||
|
string ( REGEX MATCH "[^-]*" ibase "${i}" )
|
||||||
|
find_library ( ${ibase}_LIBRARY
|
||||||
|
NAMES ${i}
|
||||||
|
PATHS ${PKGCONFIG_LIBUSB_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
if ( ${ibase}_LIBRARY )
|
||||||
|
list ( APPEND LibUSB_LIBRARIES ${${ibase}_LIBRARY} )
|
||||||
|
endif ( ${ibase}_LIBRARY )
|
||||||
|
mark_as_advanced ( ${ibase}_LIBRARY )
|
||||||
|
endforeach ( i )
|
||||||
|
|
||||||
|
else ( PKGCONFIG_LIBUSB_FOUND )
|
||||||
|
find_file ( LibUSB_HEADER_FILE
|
||||||
|
NAMES
|
||||||
|
libusb.h usb.h
|
||||||
|
PATHS
|
||||||
|
$ENV{ProgramFiles}/LibUSB-Win32
|
||||||
|
$ENV{LibUSB_ROOT_DIR}
|
||||||
|
PATH_SUFFIXES
|
||||||
|
include
|
||||||
|
libusb-1.0
|
||||||
|
include/libusb-1.0
|
||||||
|
)
|
||||||
|
mark_as_advanced ( LibUSB_HEADER_FILE )
|
||||||
|
get_filename_component ( LibUSB_INCLUDE_DIRS "${LibUSB_HEADER_FILE}" PATH )
|
||||||
|
|
||||||
|
if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" )
|
||||||
|
# LibUSB-Win32 binary distribution contains several libs.
|
||||||
|
# Use the lib that got compiled with the same compiler.
|
||||||
|
if ( MSVC )
|
||||||
|
if ( WIN32 )
|
||||||
|
set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc )
|
||||||
|
else ( WIN32 )
|
||||||
|
set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc_x64 )
|
||||||
|
endif ( WIN32 )
|
||||||
|
elseif ( BORLAND )
|
||||||
|
set ( LibUSB_LIBRARY_PATH_SUFFIX lib/bcc )
|
||||||
|
elseif ( CMAKE_COMPILER_IS_GNUCC )
|
||||||
|
set ( LibUSB_LIBRARY_PATH_SUFFIX lib/gcc )
|
||||||
|
endif ( MSVC )
|
||||||
|
endif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" )
|
||||||
|
|
||||||
|
find_library ( usb_LIBRARY
|
||||||
|
NAMES
|
||||||
|
usb-1.0 libusb usb
|
||||||
|
PATHS
|
||||||
|
$ENV{ProgramFiles}/LibUSB-Win32
|
||||||
|
$ENV{LibUSB_ROOT_DIR}
|
||||||
|
PATH_SUFFIXES
|
||||||
|
${LibUSB_LIBRARY_PATH_SUFFIX}
|
||||||
|
)
|
||||||
|
mark_as_advanced ( usb_LIBRARY )
|
||||||
|
if ( usb_LIBRARY )
|
||||||
|
set ( LibUSB_LIBRARIES ${usb_LIBRARY} )
|
||||||
|
endif ( usb_LIBRARY )
|
||||||
|
|
||||||
|
endif ( PKGCONFIG_LIBUSB_FOUND )
|
||||||
|
|
||||||
|
if ( LibUSB_INCLUDE_DIRS AND LibUSB_LIBRARIES )
|
||||||
|
set ( LibUSB_FOUND true )
|
||||||
|
endif ( LibUSB_INCLUDE_DIRS AND LibUSB_LIBRARIES )
|
||||||
|
|
||||||
|
if ( LibUSB_FOUND )
|
||||||
|
set ( CMAKE_REQUIRED_INCLUDES "${LibUSB_INCLUDE_DIRS}" )
|
||||||
|
check_include_file ( "${LibUSB_HEADER_FILE}" LibUSB_FOUND )
|
||||||
|
endif ( LibUSB_FOUND )
|
||||||
|
|
||||||
|
if ( LibUSB_FOUND )
|
||||||
|
check_library_exists ( "${LibUSB_LIBRARIES}" usb_open "" LibUSB_FOUND )
|
||||||
|
check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_device_list "" LibUSB_VERSION_1.0 )
|
||||||
|
check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_port_numbers "" LibUSB_VERSION_1.0.16 )
|
||||||
|
|
||||||
|
# Library 1.0.16+ compilation test.
|
||||||
|
# The check_library_exists does not work well on Apple with shared libs.
|
||||||
|
if (APPLE OR LibUSB_VERSION_1.0.16)
|
||||||
|
if (APPLE)
|
||||||
|
if(DEPENDS)
|
||||||
|
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
|
||||||
|
else()
|
||||||
|
find_library(COREFOUNDATION CoreFoundation)
|
||||||
|
find_library(IOKIT IOKit)
|
||||||
|
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${IOKIT})
|
||||||
|
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${COREFOUNDATION})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if (WIN32)
|
||||||
|
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES setupapi)
|
||||||
|
endif()
|
||||||
|
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${LibUSB_LIBRARIES})
|
||||||
|
|
||||||
|
try_compile(LibUSB_COMPILE_TEST_PASSED
|
||||||
|
${CMAKE_BINARY_DIR}
|
||||||
|
"${CMAKE_SOURCE_DIR}/cmake/test-libusb-version.c"
|
||||||
|
CMAKE_FLAGS
|
||||||
|
"-DINCLUDE_DIRECTORIES=${LibUSB_INCLUDE_DIRS}"
|
||||||
|
"-DLINK_DIRECTORIES=${LibUSB_LIBRARIES}"
|
||||||
|
LINK_LIBRARIES ${TEST_COMPILE_EXTRA_LIBRARIES}
|
||||||
|
OUTPUT_VARIABLE OUTPUT)
|
||||||
|
unset(TEST_COMPILE_EXTRA_LIBRARIES)
|
||||||
|
message(STATUS "LibUSB Compilation test: ${LibUSB_COMPILE_TEST_PASSED}")
|
||||||
|
endif()
|
||||||
|
endif ( LibUSB_FOUND )
|
||||||
|
|
||||||
|
if ( NOT LibUSB_FOUND )
|
||||||
|
if ( NOT LibUSB_FIND_QUIETLY )
|
||||||
|
message ( STATUS "LibUSB not found, try setting LibUSB_ROOT_DIR environment variable." )
|
||||||
|
endif ( NOT LibUSB_FIND_QUIETLY )
|
||||||
|
if ( LibUSB_FIND_REQUIRED )
|
||||||
|
message ( FATAL_ERROR "" )
|
||||||
|
endif ( LibUSB_FIND_REQUIRED )
|
||||||
|
endif ( NOT LibUSB_FOUND )
|
52
cmake/test-libusb-version.c
Normal file
52
cmake/test-libusb-version.c
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) 2014-2018, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE 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.
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
|
||||||
|
#define UNUSED(expr) (void)(expr)
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
libusb_device **devs;
|
||||||
|
libusb_context *ctx = NULL;
|
||||||
|
|
||||||
|
int r = libusb_init(&ctx); UNUSED(r);
|
||||||
|
ssize_t cnt = libusb_get_device_list(ctx, &devs); UNUSED(cnt);
|
||||||
|
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
r = libusb_get_device_descriptor(devs[0], &desc); UNUSED(r);
|
||||||
|
uint8_t bus_id = libusb_get_bus_number(devs[0]); UNUSED(bus_id);
|
||||||
|
uint8_t addr = libusb_get_device_address(devs[0]); UNUSED(addr);
|
||||||
|
|
||||||
|
uint8_t tmp_path[16];
|
||||||
|
r = libusb_get_port_numbers(devs[0], tmp_path, sizeof(tmp_path));
|
||||||
|
UNUSED(r);
|
||||||
|
UNUSED(tmp_path);
|
||||||
|
|
||||||
|
libusb_free_device_list(devs, 1);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package=libusb
|
package=libusb
|
||||||
$(package)_version=1.0.9
|
$(package)_version=1.0.22
|
||||||
$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-1.0.9/
|
$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/
|
||||||
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
||||||
$(package)_sha256_hash=e920eedc2d06b09606611c99ec7304413c6784cba6e33928e78243d323195f9b
|
$(package)_sha256_hash=75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157
|
||||||
|
|
||||||
define $(package)_preprocess_cmds
|
define $(package)_preprocess_cmds
|
||||||
autoreconf -i
|
autoreconf -i
|
||||||
|
@ -10,7 +10,7 @@ endef
|
||||||
|
|
||||||
define $(package)_set_vars
|
define $(package)_set_vars
|
||||||
$(package)_config_opts=--disable-shared
|
$(package)_config_opts=--disable-shared
|
||||||
$(package)_config_opts_linux=--with-pic
|
$(package)_config_opts_linux=--with-pic --disable-udev
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define $(package)_config_cmds
|
define $(package)_config_cmds
|
||||||
|
|
|
@ -63,37 +63,20 @@ set(trezor_sources
|
||||||
set(trezor_private_headers)
|
set(trezor_private_headers)
|
||||||
|
|
||||||
|
|
||||||
include(FindProtobuf)
|
# Protobuf and LibUSB processed by CheckTrezor
|
||||||
find_package(Protobuf) # REQUIRED
|
if(DEVICE_TREZOR_READY)
|
||||||
|
|
||||||
# Test for HAVE_PROTOBUF from the parent
|
|
||||||
if(Protobuf_FOUND AND HAVE_PROTOBUF)
|
|
||||||
if ("$ENV{PYTHON3}" STREQUAL "")
|
|
||||||
set(PYTHON3 "python3")
|
|
||||||
else()
|
|
||||||
set(PYTHON3 "$ENV{PYTHON3}" CACHE INTERNAL "Copied from environment variable")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
execute_process(COMMAND ${PYTHON3} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR)
|
|
||||||
if(RET)
|
|
||||||
message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})."
|
|
||||||
"OUT: ${OUT}, ERR: ${ERR}."
|
|
||||||
"Please read src/device_trezor/trezor/tools/README.md")
|
|
||||||
else()
|
|
||||||
message(STATUS "Trezor protobuf messages regenerated ${OUT}")
|
|
||||||
set(TREZOR_PROTOBUF_GENERATED 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
if(TREZOR_PROTOBUF_GENERATED)
|
|
||||||
message(STATUS "Trezor support enabled")
|
message(STATUS "Trezor support enabled")
|
||||||
|
|
||||||
add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0)
|
add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0)
|
||||||
|
|
||||||
|
set(TREZOR_LIBUSB_LIBRARIES "")
|
||||||
|
if(LibUSB_COMPILE_TEST_PASSED)
|
||||||
|
list(APPEND TREZOR_LIBUSB_LIBRARIES ${LibUSB_LIBRARIES})
|
||||||
|
message(STATUS "Trezor compatible LibUSB found at: ${LibUSB_INCLUDE_DIRS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
monero_private_headers(device_trezor
|
monero_private_headers(device_trezor
|
||||||
${device_private_headers}
|
${device_private_headers})
|
||||||
${PROTOBUF_INCLUDE_DIR})
|
|
||||||
|
|
||||||
monero_add_library(device_trezor
|
monero_add_library(device_trezor
|
||||||
${trezor_sources}
|
${trezor_sources}
|
||||||
|
@ -109,14 +92,13 @@ if(TREZOR_PROTOBUF_GENERATED)
|
||||||
common
|
common
|
||||||
${SODIUM_LIBRARY}
|
${SODIUM_LIBRARY}
|
||||||
${Boost_CHRONO_LIBRARY}
|
${Boost_CHRONO_LIBRARY}
|
||||||
${PROTOBUF_LIBRARY}
|
${Protobuf_LIBRARY}
|
||||||
|
${TREZOR_LIBUSB_LIBRARIES}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${EXTRA_LIBRARIES})
|
${EXTRA_LIBRARIES})
|
||||||
|
|
||||||
# set(WITH_DEVICE_TREZOR 1 PARENT_SCOPE)
|
|
||||||
# add_definitions(-DWITH_DEVICE_TREZOR=1)
|
|
||||||
|
|
||||||
else()
|
else()
|
||||||
|
message(STATUS "Trezor support disabled")
|
||||||
monero_private_headers(device_trezor)
|
monero_private_headers(device_trezor)
|
||||||
monero_add_library(device_trezor device_trezor.cpp)
|
monero_add_library(device_trezor device_trezor.cpp)
|
||||||
target_link_libraries(device_trezor PUBLIC cncrypto)
|
target_link_libraries(device_trezor PUBLIC cncrypto)
|
||||||
|
|
|
@ -32,13 +32,12 @@
|
||||||
namespace hw {
|
namespace hw {
|
||||||
namespace trezor {
|
namespace trezor {
|
||||||
|
|
||||||
#if WITH_DEVICE_TREZOR
|
#ifdef WITH_DEVICE_TREZOR
|
||||||
|
|
||||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
|
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
|
||||||
|
|
||||||
#define HW_TREZOR_NAME "Trezor"
|
#define HW_TREZOR_NAME "Trezor"
|
||||||
#define HW_TREZOR_NAME_LITE "TrezorLite"
|
|
||||||
|
|
||||||
static device_trezor *trezor_device = nullptr;
|
static device_trezor *trezor_device = nullptr;
|
||||||
static device_trezor *ensure_trezor_device(){
|
static device_trezor *ensure_trezor_device(){
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace trezor {
|
||||||
void register_all();
|
void register_all();
|
||||||
void register_all(std::map<std::string, std::unique_ptr<device>> ®istry);
|
void register_all(std::map<std::string, std::unique_ptr<device>> ®istry);
|
||||||
|
|
||||||
#if WITH_DEVICE_TREZOR
|
#ifdef WITH_DEVICE_TREZOR
|
||||||
class device_trezor;
|
class device_trezor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,43 +32,11 @@
|
||||||
namespace hw {
|
namespace hw {
|
||||||
namespace trezor {
|
namespace trezor {
|
||||||
|
|
||||||
#if WITH_DEVICE_TREZOR
|
#ifdef WITH_DEVICE_TREZOR
|
||||||
|
|
||||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
|
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
|
||||||
|
|
||||||
std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_button_request(const messages::common::ButtonRequest * msg){
|
|
||||||
MDEBUG("on_button_request");
|
|
||||||
device.on_button_request();
|
|
||||||
return std::make_shared<messages::common::ButtonAck>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_pin_matrix_request(const messages::common::PinMatrixRequest * msg){
|
|
||||||
MDEBUG("on_pin_request");
|
|
||||||
epee::wipeable_string pin;
|
|
||||||
device.on_pin_request(pin);
|
|
||||||
auto resp = std::make_shared<messages::common::PinMatrixAck>();
|
|
||||||
resp->set_pin(pin.data(), pin.size());
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_passphrase_request(const messages::common::PassphraseRequest * msg){
|
|
||||||
MDEBUG("on_passhprase_request");
|
|
||||||
epee::wipeable_string passphrase;
|
|
||||||
device.on_passphrase_request(msg->on_device(), passphrase);
|
|
||||||
auto resp = std::make_shared<messages::common::PassphraseAck>();
|
|
||||||
if (!msg->on_device()){
|
|
||||||
resp->set_passphrase(passphrase.data(), passphrase.size());
|
|
||||||
}
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg){
|
|
||||||
MDEBUG("on_passhprase_state_request");
|
|
||||||
device.on_passphrase_state_request(msg->state());
|
|
||||||
return std::make_shared<messages::common::PassphraseStateAck>();
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000};
|
const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000};
|
||||||
|
|
||||||
device_trezor_base::device_trezor_base() {
|
device_trezor_base::device_trezor_base() {
|
||||||
|
@ -117,9 +85,6 @@ namespace trezor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_protocol_callback){
|
|
||||||
m_protocol_callback = std::make_shared<trezor_protocol_callback>(*this);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +210,49 @@ namespace trezor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void device_trezor_base::write_raw(const google::protobuf::Message * msg){
|
||||||
|
require_connected();
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||||
|
this->getTransport()->write(*msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericMessage device_trezor_base::read_raw(){
|
||||||
|
require_connected();
|
||||||
|
std::shared_ptr<google::protobuf::Message> msg_resp;
|
||||||
|
hw::trezor::messages::MessageType msg_resp_type;
|
||||||
|
|
||||||
|
this->getTransport()->read(msg_resp, &msg_resp_type);
|
||||||
|
return GenericMessage(msg_resp_type, msg_resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericMessage device_trezor_base::call_raw(const google::protobuf::Message * msg) {
|
||||||
|
write_raw(msg);
|
||||||
|
return read_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool device_trezor_base::message_handler(GenericMessage & input){
|
||||||
|
// Later if needed this generic message handler can be replaced by a pointer to
|
||||||
|
// a protocol message handler which by default points to the device class which implements
|
||||||
|
// the default handler.
|
||||||
|
switch(input.m_type){
|
||||||
|
case messages::MessageType_ButtonRequest:
|
||||||
|
on_button_request(input, dynamic_cast<const messages::common::ButtonRequest*>(input.m_msg.get()));
|
||||||
|
return true;
|
||||||
|
case messages::MessageType_PassphraseRequest:
|
||||||
|
on_passphrase_request(input, dynamic_cast<const messages::common::PassphraseRequest*>(input.m_msg.get()));
|
||||||
|
return true;
|
||||||
|
case messages::MessageType_PassphraseStateRequest:
|
||||||
|
on_passphrase_state_request(input, dynamic_cast<const messages::common::PassphraseStateRequest*>(input.m_msg.get()));
|
||||||
|
return true;
|
||||||
|
case messages::MessageType_PinMatrixRequest:
|
||||||
|
on_pin_request(input, dynamic_cast<const messages::common::PinMatrixRequest*>(input.m_msg.get()));
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
/* TREZOR PROTOCOL */
|
/* TREZOR PROTOCOL */
|
||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
|
@ -269,32 +277,67 @@ namespace trezor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_trezor_base::on_button_request()
|
void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg)
|
||||||
{
|
{
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||||
|
MDEBUG("on_button_request, code: " << msg->code());
|
||||||
|
|
||||||
|
messages::common::ButtonAck ack;
|
||||||
|
write_raw(&ack);
|
||||||
|
|
||||||
if (m_callback){
|
if (m_callback){
|
||||||
m_callback->on_button_request();
|
m_callback->on_button_request();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp = read_raw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_trezor_base::on_pin_request(epee::wipeable_string & pin)
|
void device_trezor_base::on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg)
|
||||||
{
|
{
|
||||||
|
MDEBUG("on_pin_request");
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||||
|
|
||||||
|
epee::wipeable_string pin;
|
||||||
|
|
||||||
if (m_callback){
|
if (m_callback){
|
||||||
m_callback->on_pin_request(pin);
|
m_callback->on_pin_request(pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove PIN from memory
|
||||||
|
messages::common::PinMatrixAck m;
|
||||||
|
m.set_pin(pin.data(), pin.size());
|
||||||
|
resp = call_raw(&m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_trezor_base::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
|
void device_trezor_base::on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg)
|
||||||
{
|
{
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||||
|
MDEBUG("on_passhprase_request, on device: " << msg->on_device());
|
||||||
|
epee::wipeable_string passphrase;
|
||||||
|
|
||||||
if (m_callback){
|
if (m_callback){
|
||||||
m_callback->on_passphrase_request(on_device, passphrase);
|
m_callback->on_passphrase_request(msg->on_device(), passphrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messages::common::PassphraseAck m;
|
||||||
|
if (!msg->on_device()){
|
||||||
|
// TODO: remove passphrase from memory
|
||||||
|
m.set_passphrase(passphrase.data(), passphrase.size());
|
||||||
|
}
|
||||||
|
resp = call_raw(&m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_trezor_base::on_passphrase_state_request(const std::string & state)
|
void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg)
|
||||||
{
|
{
|
||||||
|
MDEBUG("on_passhprase_state_request");
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||||
|
|
||||||
if (m_callback){
|
if (m_callback){
|
||||||
m_callback->on_passphrase_state_request(state);
|
m_callback->on_passphrase_state_request(msg->state());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messages::common::PassphraseStateAck m;
|
||||||
|
resp = call_raw(&m);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //WITH_DEVICE_TREZOR
|
#endif //WITH_DEVICE_TREZOR
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
namespace hw {
|
namespace hw {
|
||||||
namespace trezor {
|
namespace trezor {
|
||||||
|
|
||||||
#if WITH_DEVICE_TREZOR
|
#ifdef WITH_DEVICE_TREZOR
|
||||||
class device_trezor_base;
|
class device_trezor_base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,41 +68,6 @@ namespace trezor {
|
||||||
virtual void on_passphrase_state_request(const std::string & state) {};
|
virtual void on_passphrase_state_request(const std::string & state) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Default Trezor protocol client callback
|
|
||||||
*/
|
|
||||||
class trezor_protocol_callback {
|
|
||||||
protected:
|
|
||||||
device_trezor_base & device;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit trezor_protocol_callback(device_trezor_base & device): device(device) {}
|
|
||||||
|
|
||||||
std::shared_ptr<google::protobuf::Message> on_button_request(const messages::common::ButtonRequest * msg);
|
|
||||||
std::shared_ptr<google::protobuf::Message> on_pin_matrix_request(const messages::common::PinMatrixRequest * msg);
|
|
||||||
std::shared_ptr<google::protobuf::Message> on_passphrase_request(const messages::common::PassphraseRequest * msg);
|
|
||||||
std::shared_ptr<google::protobuf::Message> on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg);
|
|
||||||
|
|
||||||
std::shared_ptr<google::protobuf::Message> on_message(const google::protobuf::Message * msg, messages::MessageType message_type){
|
|
||||||
MDEBUG("on_general_message");
|
|
||||||
return on_message_dispatch(msg, message_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<google::protobuf::Message> on_message_dispatch(const google::protobuf::Message * msg, messages::MessageType message_type){
|
|
||||||
if (message_type == messages::MessageType_ButtonRequest){
|
|
||||||
return on_button_request(dynamic_cast<const messages::common::ButtonRequest*>(msg));
|
|
||||||
} else if (message_type == messages::MessageType_PassphraseRequest) {
|
|
||||||
return on_passphrase_request(dynamic_cast<const messages::common::PassphraseRequest*>(msg));
|
|
||||||
} else if (message_type == messages::MessageType_PassphraseStateRequest) {
|
|
||||||
return on_passphrase_state_request(dynamic_cast<const messages::common::PassphraseStateRequest*>(msg));
|
|
||||||
} else if (message_type == messages::MessageType_PinMatrixRequest) {
|
|
||||||
return on_pin_matrix_request(dynamic_cast<const messages::common::PinMatrixRequest*>(msg));
|
|
||||||
} else {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TREZOR device template with basic functions
|
* TREZOR device template with basic functions
|
||||||
*/
|
*/
|
||||||
|
@ -114,7 +79,6 @@ namespace trezor {
|
||||||
mutable boost::mutex command_locker;
|
mutable boost::mutex command_locker;
|
||||||
|
|
||||||
std::shared_ptr<Transport> m_transport;
|
std::shared_ptr<Transport> m_transport;
|
||||||
std::shared_ptr<trezor_protocol_callback> m_protocol_callback;
|
|
||||||
std::shared_ptr<trezor_callback> m_callback;
|
std::shared_ptr<trezor_callback> m_callback;
|
||||||
|
|
||||||
std::string full_name;
|
std::string full_name;
|
||||||
|
@ -129,6 +93,15 @@ namespace trezor {
|
||||||
void call_ping_unsafe();
|
void call_ping_unsafe();
|
||||||
void test_ping();
|
void test_ping();
|
||||||
|
|
||||||
|
// Communication methods
|
||||||
|
|
||||||
|
void write_raw(const google::protobuf::Message * msg);
|
||||||
|
GenericMessage read_raw();
|
||||||
|
GenericMessage call_raw(const google::protobuf::Message * msg);
|
||||||
|
|
||||||
|
// Trezor message protocol handler. Handles specific signalling messages.
|
||||||
|
bool message_handler(GenericMessage & input);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client communication wrapper, handles specific Trezor protocol.
|
* Client communication wrapper, handles specific Trezor protocol.
|
||||||
*
|
*
|
||||||
|
@ -141,8 +114,7 @@ namespace trezor {
|
||||||
const boost::optional<messages::MessageType> & resp_type = boost::none,
|
const boost::optional<messages::MessageType> & resp_type = boost::none,
|
||||||
const boost::optional<std::vector<messages::MessageType>> & resp_types = boost::none,
|
const boost::optional<std::vector<messages::MessageType>> & resp_types = boost::none,
|
||||||
const boost::optional<messages::MessageType*> & resp_type_ptr = boost::none,
|
const boost::optional<messages::MessageType*> & resp_type_ptr = boost::none,
|
||||||
bool open_session = false,
|
bool open_session = false)
|
||||||
unsigned depth=0)
|
|
||||||
{
|
{
|
||||||
// Require strictly protocol buffers response in the template.
|
// Require strictly protocol buffers response in the template.
|
||||||
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
|
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
|
||||||
|
@ -151,8 +123,12 @@ namespace trezor {
|
||||||
throw std::invalid_argument("Cannot specify list of accepted types and not using generic response");
|
throw std::invalid_argument("Cannot specify list of accepted types and not using generic response");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine type of expected message response
|
||||||
|
const messages::MessageType required_type = accepting_base ? messages::MessageType_Success :
|
||||||
|
(resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>());
|
||||||
|
|
||||||
// Open session if required
|
// Open session if required
|
||||||
if (open_session && depth == 0){
|
if (open_session){
|
||||||
try {
|
try {
|
||||||
m_transport->open();
|
m_transport->open();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -162,47 +138,37 @@ namespace trezor {
|
||||||
|
|
||||||
// Scoped session closer
|
// Scoped session closer
|
||||||
BOOST_SCOPE_EXIT_ALL(&, this) {
|
BOOST_SCOPE_EXIT_ALL(&, this) {
|
||||||
if (open_session && depth == 0){
|
if (open_session){
|
||||||
this->getTransport()->close();
|
this->getTransport()->close();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write the request
|
// Write/read the request
|
||||||
CHECK_AND_ASSERT_THROW_MES(req, "Request is null");
|
CHECK_AND_ASSERT_THROW_MES(req, "Request is null");
|
||||||
this->getTransport()->write(*req);
|
auto msg_resp = call_raw(req.get());
|
||||||
|
|
||||||
// Read the response
|
bool processed = false;
|
||||||
std::shared_ptr<google::protobuf::Message> msg_resp;
|
do {
|
||||||
hw::trezor::messages::MessageType msg_resp_type;
|
processed = message_handler(msg_resp);
|
||||||
|
} while(processed);
|
||||||
|
|
||||||
// We may have several roundtrips with the handler
|
// Response section
|
||||||
this->getTransport()->read(msg_resp, &msg_resp_type);
|
|
||||||
if (resp_type_ptr){
|
if (resp_type_ptr){
|
||||||
*(resp_type_ptr.get()) = msg_resp_type;
|
*(resp_type_ptr.get()) = msg_resp.m_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine type of expected message response
|
if (msg_resp.m_type == messages::MessageType_Failure) {
|
||||||
messages::MessageType required_type = accepting_base ? messages::MessageType_Success :
|
throw_failure_exception(dynamic_cast<messages::common::Failure *>(msg_resp.m_msg.get()));
|
||||||
(resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>());
|
|
||||||
|
|
||||||
if (msg_resp_type == messages::MessageType_Failure) {
|
} else if (!accepting_base && msg_resp.m_type == required_type) {
|
||||||
throw_failure_exception(dynamic_cast<messages::common::Failure *>(msg_resp.get()));
|
return message_ptr_retype<t_message>(msg_resp.m_msg);
|
||||||
|
|
||||||
} else if (!accepting_base && msg_resp_type == required_type) {
|
} else if (accepting_base && (!resp_types ||
|
||||||
return message_ptr_retype<t_message>(msg_resp);
|
std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp.m_type) != resp_types.get().end())) {
|
||||||
|
return message_ptr_retype<t_message>(msg_resp.m_msg);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auto resp = this->getProtocolCallback()->on_message(msg_resp.get(), msg_resp_type);
|
throw exc::UnexpectedMessageException(msg_resp.m_type, msg_resp.m_msg);
|
||||||
if (resp) {
|
|
||||||
return this->client_exchange<t_message>(resp, boost::none, resp_types, resp_type_ptr, false, depth + 1);
|
|
||||||
|
|
||||||
} else if (accepting_base && (!resp_types ||
|
|
||||||
std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp_type) != resp_types.get().end())) {
|
|
||||||
return message_ptr_retype<t_message>(msg_resp);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw exc::UnexpectedMessageException(msg_resp_type, msg_resp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,10 +218,6 @@ namespace trezor {
|
||||||
return m_transport;
|
return m_transport;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<trezor_protocol_callback> getProtocolCallback(){
|
|
||||||
return m_protocol_callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<trezor_callback> getCallback(){
|
std::shared_ptr<trezor_callback> getCallback(){
|
||||||
return m_callback;
|
return m_callback;
|
||||||
}
|
}
|
||||||
|
@ -288,10 +250,10 @@ namespace trezor {
|
||||||
bool ping();
|
bool ping();
|
||||||
|
|
||||||
// Protocol callbacks
|
// Protocol callbacks
|
||||||
void on_button_request();
|
void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg);
|
||||||
void on_pin_request(epee::wipeable_string & pin);
|
void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg);
|
||||||
void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase);
|
void on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg);
|
||||||
void on_passphrase_state_request(const std::string & state);
|
void on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
#include "trezor/trezor_defs.hpp"
|
#include "trezor/trezor_defs.hpp"
|
||||||
|
|
||||||
#ifdef HAVE_PROTOBUF
|
#ifdef WITH_DEVICE_TREZOR
|
||||||
#include "trezor/transport.hpp"
|
#include "trezor/transport.hpp"
|
||||||
#include "trezor/messages/messages.pb.h"
|
#include "trezor/messages/messages.pb.h"
|
||||||
#include "trezor/messages/messages-common.pb.h"
|
#include "trezor/messages/messages-common.pb.h"
|
||||||
|
|
|
@ -2,33 +2,28 @@
|
||||||
|
|
||||||
## Messages rebuild
|
## Messages rebuild
|
||||||
|
|
||||||
Install `protoc` for your distribution.
|
Install `protoc` for your distribution. Requirements:
|
||||||
|
|
||||||
- `protobuf-compiler`
|
- `protobuf-compiler`
|
||||||
- `libprotobuf-dev`
|
- `libprotobuf-dev`
|
||||||
- `libprotoc-dev`
|
- `python`
|
||||||
- `python-protobuf`
|
|
||||||
|
|
||||||
Python 3 is required. If you don't have python 3 quite an easy way is
|
|
||||||
to use [pyenv].
|
|
||||||
|
|
||||||
It is also advised to create own python virtual environment so dependencies
|
Soft requirement: Python 3, can be easily installed with [pyenv].
|
||||||
are installed in this project-related virtual environment.
|
|
||||||
|
### Python 2
|
||||||
|
|
||||||
|
Workaround if there is no Python3 available:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m venv /
|
pip install backports.tempfile
|
||||||
```
|
```
|
||||||
|
|
||||||
Make sure your python has `protobuf` package installed
|
### Regenerate messages
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install protobuf
|
cd src/device_trezor/trezor
|
||||||
```
|
python tools/build_protob.py
|
||||||
|
|
||||||
Regenerate messages:
|
|
||||||
|
|
||||||
```
|
|
||||||
./venv/bin/python3 src/device_trezor/trezor/tools/build_protob.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The messages regeneration is done also automatically via cmake.
|
The messages regeneration is done also automatically via cmake.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
# Converts Google's protobuf python definitions of TREZOR wire messages
|
# Converts Google's protobuf python definitions of TREZOR wire messages
|
||||||
# to plain-python objects as used in TREZOR Core and python-trezor
|
# to plain-python objects as used in TREZOR Core and python-trezor
|
||||||
|
|
||||||
|
@ -8,11 +8,19 @@ import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
import glob
|
import glob
|
||||||
import tempfile
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
try:
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
except:
|
||||||
|
# Py2 backward compatibility, optionally installed by user
|
||||||
|
# pip install backports.tempfile
|
||||||
|
try:
|
||||||
|
from backports.tempfile import TemporaryDirectory
|
||||||
|
except:
|
||||||
|
raise EnvironmentError('TemporaryDirectory could not be imported. Try: pip install backports.tempfile')
|
||||||
|
|
||||||
|
|
||||||
AUTO_HEADER = "# Automatically generated by pb2cpp\n"
|
AUTO_HEADER = "# Automatically generated by pb2cpp\n"
|
||||||
|
|
||||||
|
@ -23,6 +31,9 @@ UNDEF_STATEMENT = """
|
||||||
#endif
|
#endif
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
PROTOC = None
|
||||||
|
PROTOC_INCLUDE = None
|
||||||
|
|
||||||
|
|
||||||
def which(pgm):
|
def which(pgm):
|
||||||
path = os.getenv('PATH')
|
path = os.getenv('PATH')
|
||||||
|
@ -32,15 +43,6 @@ def which(pgm):
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
PROTOC = which("protoc")
|
|
||||||
if not PROTOC:
|
|
||||||
print("protoc command not found")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC))
|
|
||||||
PROTOC_INCLUDE = os.path.join(PROTOC_PREFIX, "include")
|
|
||||||
|
|
||||||
|
|
||||||
def namespace_file(fpath, package):
|
def namespace_file(fpath, package):
|
||||||
"""Adds / replaces package name. Simple regex parsing, may use https://github.com/ph4r05/plyprotobuf later"""
|
"""Adds / replaces package name. Simple regex parsing, may use https://github.com/ph4r05/plyprotobuf later"""
|
||||||
with open(fpath) as fh:
|
with open(fpath) as fh:
|
||||||
|
@ -82,9 +84,10 @@ def protoc(files, out_dir, additional_includes=(), package=None, force=False):
|
||||||
|
|
||||||
include_dirs = set()
|
include_dirs = set()
|
||||||
include_dirs.add(PROTOC_INCLUDE)
|
include_dirs.add(PROTOC_INCLUDE)
|
||||||
include_dirs.update(additional_includes)
|
if additional_includes:
|
||||||
|
include_dirs.update(additional_includes)
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmpdir_protob, tempfile.TemporaryDirectory() as tmpdir_out:
|
with TemporaryDirectory() as tmpdir_protob, TemporaryDirectory() as tmpdir_out:
|
||||||
include_dirs.add(tmpdir_protob)
|
include_dirs.add(tmpdir_protob)
|
||||||
|
|
||||||
new_files = []
|
new_files = []
|
||||||
|
@ -125,9 +128,9 @@ def update_message_files(tmpdir_out, out_dir, force=False):
|
||||||
dest_file = os.path.join(out_dir, bname)
|
dest_file = os.path.join(out_dir, bname)
|
||||||
if not force and os.path.exists(dest_file):
|
if not force and os.path.exists(dest_file):
|
||||||
data = open(fname, 'rb').read()
|
data = open(fname, 'rb').read()
|
||||||
data_hash = hashlib.sha3_256(data).digest()
|
data_hash = hashlib.sha256(data).digest()
|
||||||
data_dest = open(dest_file, 'rb').read()
|
data_dest = open(dest_file, 'rb').read()
|
||||||
data_dest_hash = hashlib.sha3_256(data_dest).digest()
|
data_dest_hash = hashlib.sha256(data_dest).digest()
|
||||||
if data_hash == data_dest_hash:
|
if data_hash == data_dest_hash:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -163,7 +166,8 @@ def strip_leader(s, prefix):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def main():
|
||||||
|
global PROTOC, PROTOC_INCLUDE
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
@ -179,8 +183,31 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
protoc_includes = args.protoc_include or (os.environ.get("PROTOC_INCLUDE"),)
|
protoc_includes = args.protoc_include or (os.environ.get("PROTOC_INCLUDE"),)
|
||||||
|
|
||||||
|
PROTOBUF_INCLUDE_DIRS = os.getenv("PROTOBUF_INCLUDE_DIRS", None)
|
||||||
|
PROTOBUF_PROTOC_EXECUTABLE = os.getenv("PROTOBUF_PROTOC_EXECUTABLE", None)
|
||||||
|
|
||||||
|
if PROTOBUF_PROTOC_EXECUTABLE and not os.path.exists(PROTOBUF_PROTOC_EXECUTABLE):
|
||||||
|
raise ValueError("PROTOBUF_PROTOC_EXECUTABLE set but not found: %s" % PROTOBUF_PROTOC_EXECUTABLE)
|
||||||
|
|
||||||
|
elif PROTOBUF_PROTOC_EXECUTABLE:
|
||||||
|
PROTOC = PROTOBUF_PROTOC_EXECUTABLE
|
||||||
|
|
||||||
|
else:
|
||||||
|
if os.name == "nt":
|
||||||
|
PROTOC = which("protoc.exe")
|
||||||
|
else:
|
||||||
|
PROTOC = which("protoc")
|
||||||
|
|
||||||
|
if not PROTOC:
|
||||||
|
raise ValueError("protoc command not found. Set PROTOBUF_PROTOC_EXECUTABLE env var to the protoc binary and optionally PROTOBUF_INCLUDE_DIRS")
|
||||||
|
|
||||||
|
PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC))
|
||||||
|
PROTOC_INCLUDE = PROTOBUF_INCLUDE_DIRS if PROTOBUF_INCLUDE_DIRS else os.path.join(PROTOC_PREFIX, "include")
|
||||||
|
|
||||||
protoc(
|
protoc(
|
||||||
args.proto, args.out_dir, protoc_includes, package=args.namespace, force=args.force
|
args.proto, args.out_dir, protoc_includes, package=args.namespace, force=args.force
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
|
@ -27,13 +27,21 @@
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#ifdef WITH_DEVICE_TREZOR_WEBUSB
|
||||||
|
#include <libusb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <boost/endian/conversion.hpp>
|
#include <boost/endian/conversion.hpp>
|
||||||
#include <boost/asio/io_service.hpp>
|
#include <boost/asio/io_service.hpp>
|
||||||
#include <boost/asio/ip/udp.hpp>
|
#include <boost/asio/ip/udp.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
#include "transport.hpp"
|
#include "transport.hpp"
|
||||||
#include "messages/messages-common.pb.h"
|
#include "messages/messages-common.pb.h"
|
||||||
|
|
||||||
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor.transport"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using json = rapidjson::Document;
|
using json = rapidjson::Document;
|
||||||
|
|
||||||
|
@ -581,12 +589,400 @@ namespace trezor{
|
||||||
<< ">";
|
<< ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_DEVICE_TREZOR_WEBUSB
|
||||||
|
|
||||||
|
static bool is_trezor1(libusb_device_descriptor * info){
|
||||||
|
return info->idVendor == 0x534C && info->idProduct == 0x0001;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_trezor2(libusb_device_descriptor * info){
|
||||||
|
return info->idVendor == 0x1209 && info->idProduct == 0x53C1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_trezor2_bl(libusb_device_descriptor * info){
|
||||||
|
return info->idVendor == 0x1209 && info->idProduct == 0x53C0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t get_trezor_dev_mask(libusb_device_descriptor * info){
|
||||||
|
uint8_t mask = 0;
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(info, "Empty device descriptor");
|
||||||
|
mask |= is_trezor1(info) ? 1 : 0;
|
||||||
|
mask |= is_trezor2(info) ? 2 : 0;
|
||||||
|
mask |= is_trezor2_bl(info) ? 4 : 0;
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_libusb_log(libusb_context *ctx){
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(ctx, "Null libusb context");
|
||||||
|
|
||||||
|
// http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html
|
||||||
|
#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106)
|
||||||
|
# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level)
|
||||||
|
#else
|
||||||
|
# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_debug(ctx, level)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY))
|
||||||
|
TREZOR_LIBUSB_SET_DEBUG(ctx, 3);
|
||||||
|
else if (ELPP->vRegistry()->allowed(el::Level::Warning, MONERO_DEFAULT_LOG_CATEGORY))
|
||||||
|
TREZOR_LIBUSB_SET_DEBUG(ctx, 2);
|
||||||
|
else if (ELPP->vRegistry()->allowed(el::Level::Error, MONERO_DEFAULT_LOG_CATEGORY))
|
||||||
|
TREZOR_LIBUSB_SET_DEBUG(ctx, 1);
|
||||||
|
|
||||||
|
#undef TREZOR_LIBUSB_SET_DEBUG
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_libusb_ports(libusb_device *dev, std::vector<uint8_t> &path){
|
||||||
|
uint8_t tmp_path[16];
|
||||||
|
int r = libusb_get_port_numbers(dev, tmp_path, sizeof(tmp_path));
|
||||||
|
CHECK_AND_ASSERT_MES(r != LIBUSB_ERROR_OVERFLOW, -1, "Libusb path array too small");
|
||||||
|
CHECK_AND_ASSERT_MES(r >= 0, -1, "Libusb path array error");
|
||||||
|
|
||||||
|
path.resize(r);
|
||||||
|
for (int i = 0; i < r; i++){
|
||||||
|
path[i] = tmp_path[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string get_usb_path(uint8_t bus_id, const std::vector<uint8_t> &path){
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << WebUsbTransport::PATH_PREFIX << (boost::format("%03d") % ((int)bus_id));
|
||||||
|
for(uint8_t port : path){
|
||||||
|
ss << ":" << ((int) port);
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * WebUsbTransport::PATH_PREFIX = "webusb:";
|
||||||
|
|
||||||
|
WebUsbTransport::WebUsbTransport(
|
||||||
|
boost::optional<libusb_device_descriptor*> descriptor,
|
||||||
|
boost::optional<std::shared_ptr<Protocol>> proto
|
||||||
|
): m_conn_count(0),
|
||||||
|
m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr),
|
||||||
|
m_bus_id(-1), m_device_addr(-1)
|
||||||
|
{
|
||||||
|
if (descriptor){
|
||||||
|
libusb_device_descriptor * desc = new libusb_device_descriptor;
|
||||||
|
memcpy(desc, descriptor.get(), sizeof(libusb_device_descriptor));
|
||||||
|
this->m_usb_device_desc.reset(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>();
|
||||||
|
|
||||||
|
#ifdef WITH_TREZOR_DEBUG
|
||||||
|
m_debug_mode = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
WebUsbTransport::~WebUsbTransport(){
|
||||||
|
if (m_usb_device){
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_usb_session) {
|
||||||
|
libusb_exit(m_usb_session);
|
||||||
|
m_usb_session = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebUsbTransport::require_device() const{
|
||||||
|
if (!m_usb_device_desc){
|
||||||
|
throw std::runtime_error("No USB device specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebUsbTransport::require_connected() const{
|
||||||
|
require_device();
|
||||||
|
if (!m_usb_device_handle){
|
||||||
|
throw std::runtime_error("USB Device not opened");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebUsbTransport::enumerate(t_transport_vect & res) {
|
||||||
|
int r;
|
||||||
|
libusb_device **devs;
|
||||||
|
libusb_context *ctx = nullptr;
|
||||||
|
|
||||||
|
r = libusb_init(&ctx);
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb");
|
||||||
|
|
||||||
|
set_libusb_log(ctx);
|
||||||
|
|
||||||
|
ssize_t cnt = libusb_get_device_list(ctx, &devs);
|
||||||
|
if (cnt < 0){
|
||||||
|
libusb_exit(ctx);
|
||||||
|
throw std::runtime_error("Unable to enumerate libusb devices");
|
||||||
|
}
|
||||||
|
|
||||||
|
MTRACE("Libusb devices: " << cnt);
|
||||||
|
|
||||||
|
for(ssize_t i = 0; i < cnt; i++) {
|
||||||
|
libusb_device_descriptor desc{};
|
||||||
|
r = libusb_get_device_descriptor(devs[i], &desc);
|
||||||
|
if (r < 0){
|
||||||
|
MERROR("Unable to get libusb device descriptor " << i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto trezor_mask = get_trezor_dev_mask(&desc);
|
||||||
|
if (!trezor_mask){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct << " mask " << (int)trezor_mask);
|
||||||
|
|
||||||
|
auto t = std::make_shared<WebUsbTransport>(boost::make_optional(&desc));
|
||||||
|
t->m_bus_id = libusb_get_bus_number(devs[i]);
|
||||||
|
t->m_device_addr = libusb_get_device_address(devs[i]);
|
||||||
|
|
||||||
|
// Port resolution may fail. Non-critical error, just addressing precision is decreased.
|
||||||
|
get_libusb_ports(devs[i], t->m_port_numbers);
|
||||||
|
|
||||||
|
res.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_device_list(devs, 1);
|
||||||
|
libusb_exit(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WebUsbTransport::get_path() const {
|
||||||
|
if (!m_usb_device_desc){
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_usb_path(static_cast<uint8_t>(m_bus_id), m_port_numbers);
|
||||||
|
};
|
||||||
|
|
||||||
|
void WebUsbTransport::open() {
|
||||||
|
const int interface = get_interface();
|
||||||
|
if (m_conn_count > 0){
|
||||||
|
MTRACE("Already opened, count: " << m_conn_count);
|
||||||
|
m_conn_count += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TREZOR_DESTROY_SESSION() do { libusb_exit(m_usb_session); m_usb_session = nullptr; } while(0)
|
||||||
|
|
||||||
|
int r;
|
||||||
|
libusb_device **devs = nullptr;
|
||||||
|
|
||||||
|
if (m_usb_session) {
|
||||||
|
TREZOR_DESTROY_SESSION();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = libusb_init(&m_usb_session);
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb");
|
||||||
|
set_libusb_log(m_usb_session);
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
int open_res = 0;
|
||||||
|
|
||||||
|
ssize_t cnt = libusb_get_device_list(m_usb_session, &devs);
|
||||||
|
if (cnt < 0){
|
||||||
|
TREZOR_DESTROY_SESSION();
|
||||||
|
throw std::runtime_error("Unable to enumerate libusb devices");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ssize_t i = 0; i < cnt; i++) {
|
||||||
|
libusb_device_descriptor desc{};
|
||||||
|
r = libusb_get_device_descriptor(devs[i], &desc);
|
||||||
|
if (r < 0){
|
||||||
|
MERROR("Unable to get libusb device descriptor " << i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto trezor_mask = get_trezor_dev_mask(&desc);
|
||||||
|
if (!trezor_mask) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bus_id = libusb_get_bus_number(devs[i]);
|
||||||
|
std::vector<uint8_t> path;
|
||||||
|
|
||||||
|
// Port resolution may fail. Non-critical error, just addressing precision is decreased.
|
||||||
|
get_libusb_ports(devs[i], path);
|
||||||
|
|
||||||
|
MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct
|
||||||
|
<< ", mask: " << (int)trezor_mask
|
||||||
|
<< ". path: " << get_usb_path(bus_id, path));
|
||||||
|
|
||||||
|
if (bus_id == m_bus_id && path == m_port_numbers) {
|
||||||
|
found = true;
|
||||||
|
m_usb_device = devs[i];
|
||||||
|
open_res = libusb_open(m_usb_device, &m_usb_device_handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_device_list(devs, 1);
|
||||||
|
|
||||||
|
if (!found){
|
||||||
|
TREZOR_DESTROY_SESSION();
|
||||||
|
throw exc::DeviceAcquireException("Device not found");
|
||||||
|
|
||||||
|
} else if (found && open_res != 0) {
|
||||||
|
m_usb_device_handle = nullptr;
|
||||||
|
m_usb_device = nullptr;
|
||||||
|
TREZOR_DESTROY_SESSION();
|
||||||
|
throw exc::DeviceAcquireException("Unable to open libusb device");
|
||||||
|
}
|
||||||
|
|
||||||
|
r = libusb_claim_interface(m_usb_device_handle, interface);
|
||||||
|
|
||||||
|
if (r != 0){
|
||||||
|
libusb_close(m_usb_device_handle);
|
||||||
|
m_usb_device_handle = nullptr;
|
||||||
|
m_usb_device = nullptr;
|
||||||
|
TREZOR_DESTROY_SESSION();
|
||||||
|
throw exc::DeviceAcquireException("Unable to claim libusb device");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_conn_count += 1;
|
||||||
|
m_proto->session_begin(*this);
|
||||||
|
|
||||||
|
#undef TREZOR_DESTROY_SESSION
|
||||||
|
};
|
||||||
|
|
||||||
|
void WebUsbTransport::close() {
|
||||||
|
m_conn_count -= 1;
|
||||||
|
|
||||||
|
if (m_conn_count < 0){
|
||||||
|
MERROR("Close counter is negative: " << m_conn_count);
|
||||||
|
|
||||||
|
} else if (m_conn_count == 0){
|
||||||
|
MTRACE("Closing webusb device");
|
||||||
|
|
||||||
|
m_proto->session_end(*this);
|
||||||
|
|
||||||
|
int r = libusb_release_interface(m_usb_device_handle, get_interface());
|
||||||
|
if (r != 0){
|
||||||
|
MERROR("Could not release libusb interface: " << r);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_usb_device = nullptr;
|
||||||
|
if (m_usb_device_handle) {
|
||||||
|
libusb_close(m_usb_device_handle);
|
||||||
|
m_usb_device_handle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_usb_session) {
|
||||||
|
libusb_exit(m_usb_session);
|
||||||
|
m_usb_session = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int WebUsbTransport::get_interface() const{
|
||||||
|
const int INTERFACE_NORMAL = 0;
|
||||||
|
#ifdef WITH_TREZOR_DEBUG
|
||||||
|
const int INTERFACE_DEBUG = 1;
|
||||||
|
return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL;
|
||||||
|
#else
|
||||||
|
return INTERFACE_NORMAL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char WebUsbTransport::get_endpoint() const{
|
||||||
|
const unsigned char ENDPOINT_NORMAL = 1;
|
||||||
|
#ifdef WITH_TREZOR_DEBUG
|
||||||
|
const unsigned char ENDPOINT_DEBUG = 2;
|
||||||
|
return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL;
|
||||||
|
#else
|
||||||
|
return ENDPOINT_NORMAL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebUsbTransport::write(const google::protobuf::Message &req) {
|
||||||
|
m_proto->write(*this, req);
|
||||||
|
};
|
||||||
|
|
||||||
|
void WebUsbTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
|
||||||
|
m_proto->read(*this, msg, msg_type);
|
||||||
|
};
|
||||||
|
|
||||||
|
void WebUsbTransport::write_chunk(const void * buff, size_t size) {
|
||||||
|
require_connected();
|
||||||
|
if (size != REPLEN){
|
||||||
|
throw exc::CommunicationException("Invalid chunk size: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char endpoint = get_endpoint();
|
||||||
|
endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_OUT;
|
||||||
|
|
||||||
|
int transferred = 0;
|
||||||
|
int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0);
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r);
|
||||||
|
if (transferred != (int)size){
|
||||||
|
throw exc::CommunicationException("Could not transfer chunk");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t WebUsbTransport::read_chunk(void * buff, size_t size) {
|
||||||
|
require_connected();
|
||||||
|
unsigned char endpoint = get_endpoint();
|
||||||
|
endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_IN;
|
||||||
|
|
||||||
|
int transferred = 0;
|
||||||
|
int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0);
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r);
|
||||||
|
if (transferred != (int)size){
|
||||||
|
throw exc::CommunicationException("Could not read the chunk");
|
||||||
|
}
|
||||||
|
|
||||||
|
return transferred;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& WebUsbTransport::dump(std::ostream& o) const {
|
||||||
|
o << "WebUsbTransport<path=" << get_path()
|
||||||
|
<< ", vendorId=" << (m_usb_device_desc ? std::to_string(m_usb_device_desc->idVendor) : "?")
|
||||||
|
<< ", productId=" << (m_usb_device_desc ? std::to_string(m_usb_device_desc->idProduct) : "?")
|
||||||
|
<< ", deviceType=";
|
||||||
|
|
||||||
|
if (m_usb_device_desc){
|
||||||
|
if (is_trezor1(m_usb_device_desc.get()))
|
||||||
|
o << "TrezorOne";
|
||||||
|
else if (is_trezor2(m_usb_device_desc.get()))
|
||||||
|
o << "TrezorT";
|
||||||
|
else if (is_trezor2_bl(m_usb_device_desc.get()))
|
||||||
|
o << "TrezorT BL";
|
||||||
|
} else {
|
||||||
|
o << "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
return o << ">";
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WITH_DEVICE_TREZOR_WEBUSB
|
||||||
|
|
||||||
void enumerate(t_transport_vect & res){
|
void enumerate(t_transport_vect & res){
|
||||||
BridgeTransport bt;
|
BridgeTransport bt;
|
||||||
bt.enumerate(res);
|
try{
|
||||||
|
bt.enumerate(res);
|
||||||
|
} catch (const std::exception & e){
|
||||||
|
MERROR("BridgeTransport enumeration failed:" << e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_DEVICE_TREZOR_WEBUSB
|
||||||
|
hw::trezor::WebUsbTransport btw;
|
||||||
|
try{
|
||||||
|
btw.enumerate(res);
|
||||||
|
} catch (const std::exception & e){
|
||||||
|
MERROR("WebUsbTransport enumeration failed:" << e.what());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_DEVICE_TREZOR_UDP
|
||||||
hw::trezor::UdpTransport btu;
|
hw::trezor::UdpTransport btu;
|
||||||
btu.enumerate(res);
|
try{
|
||||||
|
btu.enumerate(res);
|
||||||
|
} catch (const std::exception & e){
|
||||||
|
MERROR("UdpTransport enumeration failed:" << e.what());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Transport> transport(const std::string & path){
|
std::shared_ptr<Transport> transport(const std::string & path){
|
||||||
|
@ -633,6 +1029,9 @@ namespace trezor{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GenericMessage::GenericMessage(messages::MessageType m_type, const shared_ptr<google::protobuf::Message> &m_msg)
|
||||||
|
: m_type(m_type), m_msg(m_msg), m_empty(false) {}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){
|
std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){
|
||||||
return t.dump(o);
|
return t.dump(o);
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,6 +239,59 @@ namespace trezor {
|
||||||
udp::endpoint m_endpoint;
|
udp::endpoint m_endpoint;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef WITH_DEVICE_TREZOR_WEBUSB
|
||||||
|
#include <libusb.h>
|
||||||
|
|
||||||
|
class WebUsbTransport : public Transport {
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit WebUsbTransport(
|
||||||
|
boost::optional<libusb_device_descriptor*> descriptor = boost::none,
|
||||||
|
boost::optional<std::shared_ptr<Protocol>> proto = boost::none
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ~WebUsbTransport();
|
||||||
|
|
||||||
|
static const char * PATH_PREFIX;
|
||||||
|
|
||||||
|
std::string get_path() const override;
|
||||||
|
void enumerate(t_transport_vect & res) override;
|
||||||
|
|
||||||
|
void open() override;
|
||||||
|
void close() override;
|
||||||
|
|
||||||
|
void write(const google::protobuf::Message &req) override;
|
||||||
|
void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
|
||||||
|
|
||||||
|
void write_chunk(const void * buff, size_t size) override;
|
||||||
|
size_t read_chunk(void * buff, size_t size) override;
|
||||||
|
|
||||||
|
std::ostream& dump(std::ostream& o) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void require_device() const;
|
||||||
|
void require_connected() const;
|
||||||
|
int get_interface() const;
|
||||||
|
unsigned char get_endpoint() const;
|
||||||
|
|
||||||
|
int m_conn_count;
|
||||||
|
std::shared_ptr<Protocol> m_proto;
|
||||||
|
|
||||||
|
libusb_context *m_usb_session;
|
||||||
|
libusb_device *m_usb_device;
|
||||||
|
libusb_device_handle *m_usb_device_handle;
|
||||||
|
std::unique_ptr<libusb_device_descriptor> m_usb_device_desc;
|
||||||
|
std::vector<uint8_t> m_port_numbers;
|
||||||
|
int m_bus_id;
|
||||||
|
int m_device_addr;
|
||||||
|
|
||||||
|
#ifdef WITH_TREZOR_DEBUG
|
||||||
|
bool m_debug_mode;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// General helpers
|
// General helpers
|
||||||
//
|
//
|
||||||
|
@ -289,6 +342,20 @@ namespace trezor {
|
||||||
*/
|
*/
|
||||||
[[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure);
|
[[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic message holder, type + obj
|
||||||
|
*/
|
||||||
|
class GenericMessage {
|
||||||
|
public:
|
||||||
|
GenericMessage(): m_empty(true) {}
|
||||||
|
GenericMessage(messages::MessageType m_type, const std::shared_ptr<google::protobuf::Message> &m_msg);
|
||||||
|
bool empty() const { return m_empty; }
|
||||||
|
|
||||||
|
hw::trezor::messages::MessageType m_type;
|
||||||
|
std::shared_ptr<google::protobuf::Message> m_msg;
|
||||||
|
bool m_empty;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple wrapper for write-read message exchange with expected message response type.
|
* Simple wrapper for write-read message exchange with expected message response type.
|
||||||
*
|
*
|
||||||
|
|
|
@ -27,14 +27,41 @@
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
//
|
||||||
|
|
||||||
#if defined(HAVE_PROTOBUF) && !defined(WITHOUT_TREZOR)
|
#ifndef USE_DEVICE_TREZOR
|
||||||
#define WITH_DEVICE_TREZOR 1
|
#define USE_DEVICE_TREZOR 1
|
||||||
#else
|
|
||||||
#define WITH_DEVICE_TREZOR 0
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef WITH_DEVICE_TREZOR_LITE
|
// HAVE_TREZOR_READY set by cmake after protobuf messages
|
||||||
#define WITH_DEVICE_TREZOR_LITE 0
|
// were generated successfully and all minimal dependencies are met.
|
||||||
|
#ifndef DEVICE_TREZOR_READY
|
||||||
|
#undef USE_DEVICE_TREZOR
|
||||||
|
#define USE_DEVICE_TREZOR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_DEVICE_TREZOR
|
||||||
|
#define WITH_DEVICE_TREZOR 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WITH_DEVICE_TREZOR
|
||||||
|
#undef WITH_DEVICE_TREZOR_LITE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_TREZOR_LIBUSB) && USE_DEVICE_TREZOR
|
||||||
|
#define WITH_DEVICE_TREZOR_WEBUSB 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enable / disable UDP in the enumeration
|
||||||
|
#ifndef USE_DEVICE_TREZOR_UDP
|
||||||
|
#define USE_DEVICE_TREZOR_UDP 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enable / disable UDP in the enumeration for release
|
||||||
|
#ifndef USE_DEVICE_TREZOR_UDP_RELEASE
|
||||||
|
#define USE_DEVICE_TREZOR_UDP_RELEASE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_DEVICE_TREZOR_UDP && (USE_DEVICE_TREZOR_UDP_RELEASE || defined(TREZOR_DEBUG))
|
||||||
|
#define WITH_DEVICE_TREZOR_UDP 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Avoids protobuf undefined macro warning
|
// Avoids protobuf undefined macro warning
|
||||||
|
|
Loading…
Reference in a new issue