From bb6e3bbc0f0a6c4ac126f56ba75b39d0dfac01a6 Mon Sep 17 00:00:00 2001 From: cslashm Date: Wed, 1 Aug 2018 09:24:53 +0200 Subject: [PATCH] Replace USB-CCID (smartcard) by USB-HID Remove PCSC dependencies which is a bit hard (not user friendly) to install on linux and Mac Split Ledger logic and device IO --- CMakeLists.txt | 30 ++-- cmake/FindHIDAPI.cmake | 46 ++++++ cmake/FindPCSC.cmake | 54 ------- src/device/CMakeLists.txt | 19 ++- src/device/device.cpp | 4 +- src/device/device.hpp | 3 +- src/device/device_io.hpp | 56 +++++++ src/device/device_io_hid.cpp | 288 +++++++++++++++++++++++++++++++++++ src/device/device_io_hid.hpp | 112 ++++++++++++++ src/device/device_ledger.cpp | 200 ++++++++---------------- src/device/device_ledger.hpp | 38 +++-- src/device/log.cpp | 41 ++--- src/device/log.hpp | 7 +- 13 files changed, 645 insertions(+), 253 deletions(-) create mode 100644 cmake/FindHIDAPI.cmake delete mode 100644 cmake/FindPCSC.cmake create mode 100644 src/device/device_io.hpp create mode 100644 src/device/device_io_hid.cpp create mode 100644 src/device/device_io_hid.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a95e14b65..2b5a38509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,7 +435,7 @@ if(STATIC AND NOT IOS) endif() endif() -find_package(PCSC) +find_package(HIDAPI) add_definition_if_library_exists(c memset_s "string.h" HAVE_MEMSET_S) add_definition_if_library_exists(c explicit_bzero "strings.h" HAVE_EXPLICIT_BZERO) @@ -476,14 +476,14 @@ endif() include_directories(${LIBUNWIND_INCLUDE}) link_directories(${LIBUNWIND_LIBRARY_DIRS}) -# Final setup for libpcsc -if (PCSC_FOUND) - message(STATUS "Using PCSC include dir at ${PCSC_INCLUDE_DIR}") - add_definitions(-DHAVE_PCSC) - include_directories(${PCSC_INCLUDE_DIR}) - link_directories(${LIBPCSC_LIBRARY_DIRS}) -else (PCSC_FOUND) - message(STATUS "Could not find PCSC") +# Final setup for hid +if (HIDAPI_FOUND) + message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}") + add_definitions(-DHAVE_HIDAPI) + include_directories(${HIDAPI_INCLUDE_DIR}) + link_directories(${LIBHIDAPI_LIBRARY_DIRS}) +else (HIDAPI_FOUND) + message(STATUS "Could not find HIDAPI") endif() if(MSVC) @@ -881,6 +881,18 @@ endif() list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) +if (HIDAPI_FOUND) + if (APPLE) + find_library(COREFOUNDATION CoreFoundation) + find_library(IOKIT IOKit) + list(APPEND EXTRA_LIBRARIES ${IOKIT}) + list(APPEND EXTRA_LIBRARIES ${COREFOUNDATION}) + endif() + if (WIN32) + list(APPEND EXTRA_LIBRARIES setupapi) + endif() +endif() + option(USE_READLINE "Build with GNU readline support." ON) if(USE_READLINE) find_package(Readline) diff --git a/cmake/FindHIDAPI.cmake b/cmake/FindHIDAPI.cmake new file mode 100644 index 000000000..a689fb4eb --- /dev/null +++ b/cmake/FindHIDAPI.cmake @@ -0,0 +1,46 @@ +# - try to find HIDAPI library +# from http://www.signal11.us/oss/hidapi/ +# +# Cache Variables: (probably not for direct use in your scripts) +# HIDAPI_INCLUDE_DIR +# HIDAPI_LIBRARY +# +# Non-cache variables you might use in your CMakeLists.txt: +# HIDAPI_FOUND +# HIDAPI_INCLUDE_DIRS +# HIDAPI_LIBRARIES +# +# Requires these CMake modules: +# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +find_library(HIDAPI_LIBRARY + NAMES hidapi hidapi-libusb) + +find_path(HIDAPI_INCLUDE_DIR + NAMES hidapi.h + PATH_SUFFIXES + hidapi) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(HIDAPI + DEFAULT_MSG + HIDAPI_LIBRARY + HIDAPI_INCLUDE_DIR) + +if(HIDAPI_FOUND) + set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}") + + set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}") +endif() + +mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY) diff --git a/cmake/FindPCSC.cmake b/cmake/FindPCSC.cmake deleted file mode 100644 index 081919655..000000000 --- a/cmake/FindPCSC.cmake +++ /dev/null @@ -1,54 +0,0 @@ -# - Find PCSC -# Find the native PCSC includes and library -# -# PCSC_INCLUDE_DIR - where to find winscard.h, wintypes.h, etc. -# PCSC_LIBRARIES - List of libraries when using PCSC. -# PCSC_FOUND - True if PCSC found. - - -IF (PCSC_INCLUDE_DIR AND PCSC_LIBRARIES) - # Already in cache, be silent - SET(PCSC_FIND_QUIETLY TRUE) -ENDIF (PCSC_INCLUDE_DIR AND PCSC_LIBRARIES) - -IF (NOT WIN32) - FIND_PACKAGE(PkgConfig) - PKG_CHECK_MODULES(PC_PCSC libpcsclite) - - FIND_PATH(PCSC_INCLUDE_DIR winscard.h - HINTS - /usr/include/PCSC - ${PC_PCSC_INCLUDEDIR} - ${PC_PCSC_INCLUDE_DIRS} - PATH_SUFFIXES PCSC - ) - - FIND_LIBRARY(PCSC_LIBRARY NAMES pcsclite libpcsclite PCSC - HINTS - ${PC_PCSC_LIBDIR} - ${PC_PCSC_LIBRARY_DIRS} - ) - -ELSE (NOT WIN32) - IF(BUILD_64 STREQUAL "ON") - set(PCSC_INCLUDE_DIR /mingw64/x86_64-w64-mingw32/include) - set(PCSC_LIBRARY /mingw64/x86_64-w64-mingw32/lib/libwinscard.a) - ELSE(BUILD_64 STREQUAL "ON") - set(PCSC_INCLUDE_DIR /mingw32/i686-w64-mingw32/include) - set(PCSC_LIBRARY /mingw32/i686-w64-mingw32/lib/libwinscard.a) - ENDIF(BUILD_64 STREQUAL "ON") -ENDIF (NOT WIN32) - -# handle the QUIETLY and REQUIRED arguments and set PCSC_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCSC DEFAULT_MSG PCSC_LIBRARY PCSC_INCLUDE_DIR) - -IF(PCSC_FOUND) - SET( PCSC_LIBRARIES ${PCSC_LIBRARY} ) - SET(PCSC_STATIC_LIBRARIES ${PCSC_STATIC_LIBRARY}) -ELSE(PCSC_FOUND) - SET( PCSC_LIBRARIES ) -ENDIF(PCSC_FOUND) - -MARK_AS_ADVANCED( PCSC_LIBRARY PCSC_INCLUDE_DIR PCSC_STATIC_LIBRARY) diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index cc2d20a54..8f446f42a 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -32,18 +32,27 @@ set(device_sources log.cpp ) -if(PCSC_FOUND) - set(device_sources ${device_sources} device_ledger.cpp) +if(HIDAPI_FOUND) + set(device_sources + ${device_sources} + device_ledger.cpp + device_io_hid.cpp + ) endif() set(device_headers device.hpp + device_io.hpp device_default.hpp log.hpp ) -if(PCSC_FOUND) - set(device_headers ${device_headers} device_ledger.hpp) +if(HIDAPI_FOUND) + set(device_headers + ${device_headers} + device_ledger.hpp + device_io_hid.hpp + ) endif() set(device_private_headers) @@ -65,7 +74,7 @@ monero_add_library(device target_link_libraries(device PUBLIC - ${PCSC_LIBRARIES} + ${HIDAPI_LIBRARIES} cncrypto ringct_basic ${OPENSSL_CRYPTO_LIBRARIES} diff --git a/src/device/device.cpp b/src/device/device.cpp index 8a8b40061..50041baef 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -29,7 +29,7 @@ #include "device.hpp" #include "device_default.hpp" -#ifdef HAVE_PCSC +#ifdef WITH_DEVICE_LEDGER #include "device_ledger.hpp" #endif #include "misc_log_ex.h" @@ -45,7 +45,7 @@ namespace hw { device_registry::device_registry(){ hw::core::register_all(registry); - #ifdef HAVE_PCSC + #ifdef WITH_DEVICE_LEDGER hw::ledger::register_all(registry); #endif } diff --git a/src/device/device.hpp b/src/device/device.hpp index 87f1430f4..cb9117650 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -48,11 +48,12 @@ #include "crypto/chacha.h" #include "ringct/rctTypes.h" + #ifndef USE_DEVICE_LEDGER #define USE_DEVICE_LEDGER 1 #endif -#if !defined(HAVE_PCSC) +#if !defined(HAVE_HIDAPI) #undef USE_DEVICE_LEDGER #define USE_DEVICE_LEDGER 0 #endif diff --git a/src/device/device_io.hpp b/src/device/device_io.hpp new file mode 100644 index 000000000..96163a211 --- /dev/null +++ b/src/device/device_io.hpp @@ -0,0 +1,56 @@ +// Copyright (c) 2017-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. +// + + + + +#pragma once + + +namespace hw { + namespace io { + + class device_io { + + public: + + device_io() {}; + ~device_io() {}; + + virtual void init() = 0; + virtual void release() = 0; + + virtual void connect(void *parms) = 0; + virtual void disconnect() = 0; + virtual bool connected() const = 0; + + virtual int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) = 0; + }; + }; +}; diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp new file mode 100644 index 000000000..562aca8b8 --- /dev/null +++ b/src/device/device_io_hid.cpp @@ -0,0 +1,288 @@ +// 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. +// +#if defined(HAVE_HIDAPI) + +#include "log.hpp" +#include "device_io_hid.hpp" + +namespace hw { + namespace io { + + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "device.io" + + #define ASSERT_X(exp,msg) CHECK_AND_ASSERT_THROW_MES(exp, msg); + + #define MAX_BLOCK 64 + + static std::string safe_hid_error(hid_device *hwdev) { + if (hwdev) { + return std::string((char*)hid_error(hwdev)); + } + return std::string("NULL device"); + } + + static std::string safe_hid_path(const hid_device_info *hwdev_info) { + if (hwdev_info && hwdev_info->path) { + return std::string(hwdev_info->path); + } + return std::string("NULL path"); + } + + device_io_hid::device_io_hid(unsigned short c, unsigned char t, unsigned int ps, unsigned int to) : + channel(c), + tag(t), + packet_size(ps), + timeout(to), + usb_vid(0), + usb_pid(0), + usb_device(NULL) { + } + + device_io_hid::device_io_hid() : device_io_hid(DEFAULT_CHANNEL, DEFAULT_TAG, DEFAULT_PACKET_SIZE, DEFAULT_TIMEOUT) { + } + + void device_io_hid::io_hid_log(int read, unsigned char* buffer, int block_len) { + if (hid_verbose) { + char strbuffer[1024]; + hw::buffer_to_str(strbuffer, sizeof(strbuffer), (char*)buffer, block_len); + MDEBUG( "HID " << (read?"<":">") <<" : "<=0, "Unable to init hidapi library. Error "+std::to_string(r)+": "+safe_hid_error(this->usb_device)); + } + + void device_io_hid::connect(void *params) { + hid_conn_params *p = (struct hid_conn_params*)params; + this->connect(p->vid, p->pid, p->interface_number, p->usage_page, p->interface_OR_page); + } + + void device_io_hid::connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ) { + hid_device_info *hwdev_info, *hwdev_info_list; + hid_device *hwdev; + + this->disconnect(); + + hwdev_info_list = hid_enumerate(vid, pid); + ASSERT_X(hwdev_info_list, "Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device)); + hwdev = NULL; + hwdev_info = hwdev_info_list; + while (hwdev_info) { + if ((interface_OR_page && ((usage_page == 0xffa0) || (interface_number == 0))) || + ((usage_page == 0xffa0) && (interface_number == 0)) ) { + MDEBUG("HID Device found: " << safe_hid_path(hwdev_info)); + hwdev = hid_open_path(hwdev_info->path); + break; + } else { + MDEBUG("HID Device discard: " << safe_hid_path(hwdev_info) << "("+std::to_string(hwdev_info->usage_page) << "," << std::to_string(hwdev_info->interface_number) << ")"); + } + hwdev_info = hwdev_info->next; + } + hid_free_enumeration(hwdev_info_list); + ASSERT_X(hwdev, "Unable to open device "+std::to_string(pid)+":"+std::to_string(vid)); + this->usb_vid = vid; + this->usb_pid = pid; + this->usb_device = hwdev; + } + + + bool device_io_hid::connected() const { + return this->usb_device != NULL; + } + + int device_io_hid::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) { + unsigned char buffer[400]; + unsigned char padding_buffer[MAX_BLOCK+1]; + unsigned int result; + int hid_ret; + unsigned int sw_offset; + unsigned int remaining; + unsigned int offset = 0; + + ASSERT_X(this->usb_device,"No device opened"); + + //Split command in several HID packet + memset(buffer, 0, sizeof(buffer)); + result = this->wrapCommand(command, cmd_len, buffer, sizeof(buffer)); + remaining = result; + + while (remaining > 0) { + int block_size = (remaining > MAX_BLOCK ? MAX_BLOCK : remaining); + memset(padding_buffer, 0, sizeof(padding_buffer)); + memcpy(padding_buffer+1, buffer + offset, block_size); + io_hid_log(0, padding_buffer, block_size+1); + hid_ret = hid_write(this->usb_device, padding_buffer, block_size+1); + ASSERT_X(hid_ret>=0, "Unable to send hidapi command. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device)); + offset += block_size; + remaining -= block_size; + } + + //get first response + memset(buffer, 0, sizeof(buffer)); + hid_ret = hid_read_timeout(this->usb_device, buffer, MAX_BLOCK, this->timeout); + ASSERT_X(hid_ret>=0, "Unable to read hidapi response. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device)); + result = (unsigned int)hid_ret; + io_hid_log(1, buffer, result); + offset = MAX_BLOCK; + //parse first response and get others if any + for (;;) { + result = this->unwrapReponse(buffer, offset, response, max_resp_len); + if (result != 0) { + break; + } + hid_ret = hid_read_timeout(this->usb_device, buffer + offset, MAX_BLOCK, this->timeout); + ASSERT_X(hid_ret>=0, "Unable to receive hidapi response. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device)); + result = (unsigned int)hid_ret; + io_hid_log(1, buffer + offset, result); + offset += MAX_BLOCK; + } + return result; + } + + void device_io_hid::disconnect(void) { + if (this->usb_device) { + hid_close(this->usb_device); + } + this->usb_vid = 0; + this->usb_pid = 0; + this->usb_device = NULL; + } + + void device_io_hid::release() { + /* Do not exit, as the lib context is global*/ + //hid_exit(); + } + + unsigned int device_io_hid::wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len) { + unsigned int sequence_idx = 0; + unsigned int offset = 0; + unsigned int offset_out = 0; + unsigned int block_size; + + ASSERT_X(this->packet_size >= 3, "Invalid Packet size: "+std::to_string(this->packet_size)) ; + ASSERT_X(out_len >= 7, "out_len too short: "+std::to_string(out_len)); + + out_len -= 7; + out[offset_out++] = ((this->channel >> 8) & 0xff); + out[offset_out++] = (this->channel & 0xff); + out[offset_out++] = this->tag; + out[offset_out++] = ((sequence_idx >> 8) & 0xff); + out[offset_out++] = (sequence_idx & 0xff); + sequence_idx++; + out[offset_out++] = ((command_len >> 8) & 0xff); + out[offset_out++] = (command_len & 0xff); + block_size = (command_len > this->packet_size - 7 ? this->packet_size - 7 : command_len); + ASSERT_X(out_len >= block_size, "out_len too short: "+std::to_string(out_len)); + out_len -= block_size; + memcpy(out + offset_out, command + offset, block_size); + offset_out += block_size; + offset += block_size; + while (offset != command_len) { + ASSERT_X(out_len >= 5, "out_len too short: "+std::to_string(out_len)); + out_len -= 5; + out[offset_out++] = ((this->channel >> 8) & 0xff); + out[offset_out++] = (this->channel & 0xff); + out[offset_out++] = this->tag; + out[offset_out++] = ((sequence_idx >> 8) & 0xff); + out[offset_out++] = (sequence_idx & 0xff); + sequence_idx++; + block_size = ((command_len - offset) > this->packet_size - 5 ? this->packet_size - 5 : command_len - offset); + ASSERT_X(out_len >= block_size, "out_len too short: "+std::to_string(out_len)); + out_len -= block_size; + memcpy(out + offset_out, command + offset, block_size); + offset_out += block_size; + offset += block_size; + } + while ((offset_out % this->packet_size) != 0) { + ASSERT_X(out_len >= 1, "out_len too short: "+std::to_string(out_len)); + out_len--; + out[offset_out++] = 0; + } + return offset_out; + } + + /* + * return 0 if more data are needed + * >0 if response is fully available + */ + unsigned int device_io_hid::unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len) { + unsigned int sequence_idx = 0; + unsigned int offset = 0; + unsigned int offset_out = 0; + unsigned int response_len; + unsigned int block_size; + unsigned int val; + + //end? + if ((data == NULL) || (data_len < 7 + 5)) { + return 0; + } + + //check hid header + val = (data[offset]<<8) + data[offset+1]; + offset += 2; + ASSERT_X(val == this->channel, "Wrong Channel"); + val = data[offset]; + offset++; + ASSERT_X(val == this->tag, "Wrong TAG"); + val = (data[offset]<<8) + data[offset+1]; + offset += 2; + ASSERT_X(val == sequence_idx, "Wrong sequence_idx"); + + //fetch + response_len = (data[offset++] << 8); + response_len |= data[offset++]; + ASSERT_X(out_len >= response_len, "Out Buffer too short"); + if (data_len < (7 + response_len)) { + return 0; + } + block_size = (response_len > (this->packet_size - 7) ? this->packet_size - 7 : response_len); + memcpy(out + offset_out, data + offset, block_size); + offset += block_size; + offset_out += block_size; + while (offset_out != response_len) { + sequence_idx++; + if (offset == data_len) { + return 0; + } + val = (data[offset]<<8) + data[offset+1]; + offset += 2; + ASSERT_X(val == this->channel, "Wrong Channel"); + val = data[offset]; + offset++; + ASSERT_X(val == this->tag, "Wrong TAG"); + val = (data[offset]<<8) + data[offset+1]; + offset += 2; + ASSERT_X(val == sequence_idx, "Wrong sequence_idx"); + + block_size = ((response_len - offset_out) > this->packet_size - 5 ? this->packet_size - 5 : response_len - offset_out); + if (block_size > (data_len - offset)) { + return 0; + } + memcpy(out + offset_out, data + offset, block_size); + offset += block_size; + offset_out += block_size; + } + return offset_out; + } + + + } +} + +#endif //#if defined(HAVE_HIDAPI) diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp new file mode 100644 index 000000000..560208c77 --- /dev/null +++ b/src/device/device_io_hid.hpp @@ -0,0 +1,112 @@ +// Copyright (c) 2017-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. +// + +#if defined(HAVE_HIDAPI) + +#include +#include "device_io.hpp" + +#pragma once + +namespace hw { + namespace io { + + + + /** HID class base. Commands are formated as follow: + * + * |----------------------------------------------------------| + * | 2 bytes | 1 byte | 2 bytes | 2 bytes | len bytes | + * |-----------|----------|-----------|----------|------------| + * | channel | tag | sequence | len | payload | + * |----------------------------------------------------------| + */ + + + struct hid_conn_params { + unsigned int vid; + unsigned int pid; + unsigned int interface_number; + unsigned int usage_page; + bool interface_OR_page ; + }; + + + class device_io_hid: device_io { + + + private: + + + unsigned short channel; + unsigned char tag; + unsigned int packet_size; + unsigned int timeout; + + unsigned int usb_vid; + unsigned int usb_pid; + hid_device *usb_device; + + void io_hid_log(int read, unsigned char* buf, int buf_len); + void io_hid_init(); + void io_hid_exit() ; + void io_hid_open(int vid, int pid, int mode); + void io_hid_close (void); + + unsigned int wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len); + unsigned int unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len); + + + public: + bool hid_verbose = false; + + const unsigned int OR_SELECT = 1; + const unsigned int AND_SELECT = 2; + + const unsigned char DEFAULT_CHANNEL = 0x0001; + const unsigned char DEFAULT_TAG = 0x01; + const unsigned int DEFAULT_PACKET_SIZE = 64; + const unsigned int DEFAULT_TIMEOUT = 120000; + + device_io_hid(unsigned short channel, unsigned char tag, unsigned int packet_zize, unsigned int timeout); + device_io_hid(); + ~device_io_hid() {}; + + void init(); + void connect(void *params); + void connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ); + bool connected() const; + int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len); + void disconnect(); + void release(); + }; + }; +}; + +#endif //#if defined(HAVE_HIDAPI) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 9b5ea0fd7..456eda739 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -185,41 +185,8 @@ namespace hw { #define INS_GET_RESPONSE 0xc0 - void device_ledger::logCMD() { - if (apdu_verbose) { - char strbuffer[1024]; - snprintf(strbuffer, sizeof(strbuffer), "%.02x %.02x %.02x %.02x %.02x ", - this->buffer_send[0], - this->buffer_send[1], - this->buffer_send[2], - this->buffer_send[3], - this->buffer_send[4] - ); - const size_t len = strlen(strbuffer); - buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_send+5), this->length_send-5); - MDEBUG( "CMD :" << strbuffer); - } - } - - void device_ledger::logRESP() { - if (apdu_verbose) { - char strbuffer[1024]; - snprintf(strbuffer, sizeof(strbuffer), "%.02x%.02x ", - this->buffer_recv[this->length_recv-2], - this->buffer_recv[this->length_recv-1] - ); - const size_t len = strlen(strbuffer); - buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_recv), this->length_recv-2); - MDEBUG( "RESP :" << strbuffer); - - } - } - - /* -------------------------------------------------------------- */ - device_ledger::device_ledger() { + device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 10000) { this->id = device_id++; - this->hCard = 0; - this->hContext = 0; this->reset_buffer(); this->mode = NONE; this->has_view_key = false; @@ -272,10 +239,39 @@ namespace hw { MDEBUG( "Device "<name << " UNLOCKed"); } + /* ======================================================================= */ - /* MISC */ + /* IO */ /* ======================================================================= */ - int device_ledger::set_command_header(BYTE ins, BYTE p1, BYTE p2) { + + void device_ledger::logCMD() { + if (apdu_verbose) { + char strbuffer[1024]; + snprintf(strbuffer, sizeof(strbuffer), "%.02x %.02x %.02x %.02x %.02x ", + this->buffer_send[0], + this->buffer_send[1], + this->buffer_send[2], + this->buffer_send[3], + this->buffer_send[4] + ); + const size_t len = strlen(strbuffer); + buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_send+5), this->length_send-5); + MDEBUG( "CMD : " << strbuffer); + } + } + + void device_ledger::logRESP() { + if (apdu_verbose) { + char strbuffer[1024]; + snprintf(strbuffer, sizeof(strbuffer), "%.04x ", this->sw); + const size_t len = strlen(strbuffer); + buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_recv), this->length_recv); + MDEBUG( "RESP : " << strbuffer); + + } + } + + int device_ledger::set_command_header(unsigned char ins, unsigned char p1, unsigned char p2) { reset_buffer(); int offset = 0; this->buffer_send[0] = 0x00; @@ -286,7 +282,7 @@ namespace hw { return 5; } - int device_ledger::set_command_header_noopt(BYTE ins, BYTE p1, BYTE p2) { + int device_ledger::set_command_header_noopt(unsigned char ins, unsigned char p1, unsigned char p2) { int offset = set_command_header(ins, p1, p2); //options this->buffer_send[offset++] = 0; @@ -294,7 +290,7 @@ namespace hw { return offset; } - void device_ledger::send_simple(BYTE ins, BYTE p1) { + void device_ledger::send_simple(unsigned char ins, unsigned char p1) { this->length_send = set_command_header_noopt(ins, p1); this->exchange(); } @@ -305,23 +301,17 @@ namespace hw { } unsigned int device_ledger::exchange(unsigned int ok, unsigned int mask) { - LONG rv; - unsigned int sw; - - ASSERT_T0(this->length_send <= BUFFER_SEND_SIZE); logCMD(); - this->length_recv = BUFFER_RECV_SIZE; - rv = SCardTransmit(this->hCard, - SCARD_PCI_T0, this->buffer_send, this->length_send, - NULL, this->buffer_recv, &this->length_recv); - ASSERT_RV(rv); - ASSERT_T0(this->length_recv >= 2); - ASSERT_T0(this->length_recv <= BUFFER_RECV_SIZE); - logRESP(); - sw = (this->buffer_recv[this->length_recv-2]<<8) | this->buffer_recv[this->length_recv-1]; - ASSERT_SW(sw,ok,msk); - return sw; + this->length_recv = hw_device.exchange(this->buffer_send, this->length_send, this->buffer_recv, BUFFER_SEND_SIZE); + ASSERT_X(this->length_recv>=2, "Communication error, less than tow bytes received"); + + this->length_recv -= 2; + this->sw = (this->buffer_recv[length_recv]<<8) | this->buffer_recv[length_recv+1]; + ASSERT_SW(this->sw,ok,msk); + + logRESP(); + return this->sw; } void device_ledger::reset_buffer() { @@ -341,101 +331,25 @@ namespace hw { } const std::string device_ledger::get_name() const { - if (this->full_name.empty() || (this->hCard == 0)) { + if (this->full_name.empty() || !this->connected()) { return std::string("name).append(">"); } - return this->full_name; + return this->name; } bool device_ledger::init(void) { #ifdef DEBUG_HWDEVICE this->controle_device = &hw::get_device("default"); #endif - LONG rv; this->release(); - rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM,0,0, &this->hContext); - ASSERT_RV(rv); - MDEBUG( "Device "<id <<" SCardContext created: hContext="<hContext); - this->hCard = 0; - return true; - } - - bool device_ledger::release() { - this->disconnect(); - if (this->hContext) { - SCardReleaseContext(this->hContext); - MDEBUG( "Device "<id <<" SCardContext released: hContext="<hContext); - this->hContext = 0; - this->full_name.clear(); - } + hw_device.init(); + MDEBUG( "Device "<id <<" HIDUSB inited"); return true; } bool device_ledger::connect(void) { - BYTE pbAtr[MAX_ATR_SIZE]; - LPSTR mszReaders; - DWORD dwReaders; - LONG rv; - DWORD dwState, dwProtocol, dwAtrLen, dwReaderLen; - this->disconnect(); -#ifdef SCARD_AUTOALLOCATE - dwReaders = SCARD_AUTOALLOCATE; - rv = SCardListReaders(this->hContext, NULL, (LPSTR)&mszReaders, &dwReaders); -#else - dwReaders = 0; - rv = SCardListReaders(this->hContext, NULL, NULL, &dwReaders); - if (rv != SCARD_S_SUCCESS) - return false; - mszReaders = (LPSTR)calloc(dwReaders, sizeof(char)); - rv = SCardListReaders(this->hContext, NULL, mszReaders, &dwReaders); -#endif - if (rv == SCARD_S_SUCCESS) { - char* p; - const char* prefix = this->name.c_str(); - - p = mszReaders; - MDEBUG( "Looking for " << std::string(prefix)); - while (*p) { - MDEBUG( "Device Found: " << std::string(p)); - if ((strncmp(prefix, p, strlen(prefix))==0)) { - MDEBUG( "Device Match: " << std::string(p)); - if ((rv = SCardConnect(this->hContext, - p, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0, - &this->hCard, &dwProtocol))!=SCARD_S_SUCCESS) { - break; - } - MDEBUG( "Device "<id <<" Connected: hCard="<hCard); - dwAtrLen = sizeof(pbAtr); - if ((rv = SCardStatus(this->hCard, NULL, &dwReaderLen, &dwState, &dwProtocol, pbAtr, &dwAtrLen))!=SCARD_S_SUCCESS) { - break; - } - MDEBUG( "Device "<id <<" Status OK"); - rv = SCARD_S_SUCCESS ; - this->full_name = std::string(p); - break; - } - p += strlen(p) +1; - } - } - - if (rv == SCARD_S_SUCCESS && mszReaders) { - #ifdef SCARD_AUTOALLOCATE - SCardFreeMemory(this->hContext, mszReaders); - #else - free(mszReaders); - #endif - mszReaders = NULL; - } - if (rv != SCARD_S_SUCCESS) { - if ( hCard) { - SCardDisconnect(this->hCard, SCARD_UNPOWER_CARD); - MDEBUG( "Device "<id <<" disconnected: hCard="<hCard); - this->hCard = 0; - } - } - ASSERT_RV(rv); - + hw_device.connect(0x2c97,0x0001, 0, 0xffa0, hw_device.OR_SELECT); this->reset(); #ifdef DEBUG_HWDEVICE cryptonote::account_public_address pubkey; @@ -445,15 +359,21 @@ namespace hw { crypto::secret_key skey; this->get_secret_keys(vkey,skey); - return rv==SCARD_S_SUCCESS; + return true; + } + + bool device_ledger::connected(void) const { + return hw_device.connected(); } bool device_ledger::disconnect() { - if (this->hCard) { - SCardDisconnect(this->hCard, SCARD_UNPOWER_CARD); - MDEBUG( "Device "<id <<" disconnected: hCard="<hCard); - this->hCard = 0; - } + hw_device.disconnect(); + return true; + } + + bool device_ledger::release() { + this->disconnect(); + hw_device.release(); return true; } diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 4a3625b2b..87b65996d 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -33,13 +33,7 @@ #include #include #include "device.hpp" -#ifdef WIN32 -#include -#define MAX_ATR_SIZE 33 -#else -#include -#include -#endif +#include "device_io_hid.hpp" #include #include @@ -89,22 +83,23 @@ namespace hw { mutable boost::recursive_mutex device_locker; mutable boost::mutex command_locker; - //PCSC management - std::string full_name; - SCARDCONTEXT hContext; - SCARDHANDLE hCard; - DWORD length_send; - BYTE buffer_send[BUFFER_SEND_SIZE]; - DWORD length_recv; - BYTE buffer_recv[BUFFER_RECV_SIZE]; - unsigned int id; + //IO + hw::io::device_io_hid hw_device; + std::string full_name; + unsigned int length_send; + unsigned char buffer_send[BUFFER_SEND_SIZE]; + unsigned int length_recv; + unsigned char buffer_recv[BUFFER_RECV_SIZE]; + unsigned int sw; + unsigned int id; void logCMD(void); void logRESP(void); - unsigned int exchange(unsigned int ok=0x9000, unsigned int mask=0xFFFF); + unsigned int exchange(unsigned int ok=0x9000, unsigned int mask=0xFFFF); void reset_buffer(void); - int set_command_header(BYTE ins, BYTE p1 = 0x00, BYTE p2 = 0x00); - int set_command_header_noopt(BYTE ins, BYTE p1 = 0x00, BYTE p2 = 0x00); - void send_simple(BYTE ins, BYTE p1 = 0x00); + int set_command_header(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00); + int set_command_header_noopt(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00); + void send_simple(unsigned char ins, unsigned char p1 = 0x00); + // hw running mode device_mode mode; @@ -127,7 +122,7 @@ namespace hw { device_ledger(const device_ledger &device) = delete ; device_ledger& operator=(const device_ledger &device) = delete; - explicit operator bool() const override {return this->hContext != 0;} + explicit operator bool() const override {return this->connected(); } bool reset(void); @@ -141,6 +136,7 @@ namespace hw { bool release() override; bool connect(void) override; bool disconnect() override; + bool connected(void) const; bool set_mode(device_mode mode) override; diff --git a/src/device/log.cpp b/src/device/log.cpp index 1707524fb..c9d3b551b 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -32,29 +32,34 @@ namespace hw { - #ifdef WITH_DEVICE_LEDGER + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "device" + + void buffer_to_str(char *to_buff, size_t to_len, const char *buff, size_t len) { + CHECK_AND_ASSERT_THROW_MES(to_len > (len*2), "destination buffer too short. At least" << (len*2+1) << " bytes required"); + for (size_t i=0; i (len*2), "destination buffer too short. At least" << (len*2+1) << " bytes required"); - for (size_t i=0; i