├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── COPYING ├── README.md ├── VERSION ├── build.sh ├── cert └── readme.txt ├── cmake ├── Platform-lin32.cmake ├── Platform-lin64.cmake ├── Platform-mac64.cmake ├── Platform-win32.cmake ├── Platform-win64.cmake └── modules │ ├── Findjsoncpp.cmake │ └── Findlibmicrohttpd.cmake ├── release ├── linux │ ├── .gitignore │ ├── Dockerfile │ ├── Makefile │ ├── dpkg-sig │ ├── fpm.after-install.sh │ ├── fpm.before-install.sh │ ├── fpm.before-remove.sh │ ├── release.sh │ ├── trezor.rules │ ├── trezord.init │ └── trezord.service ├── mac │ ├── Dockerfile │ ├── Makefile │ ├── dist │ │ └── Library │ │ │ └── LaunchAgents │ │ │ └── com.bitcointrezor.trezorBridge.trezord.plist │ ├── installer │ │ ├── TREZOR Bridge.pkgproj │ │ └── after-install.sh │ └── release.sh └── windows │ ├── .gitignore │ ├── Dockerfile │ ├── Makefile │ ├── release.sh │ └── trezord.nsis ├── src ├── config │ ├── config.pb.cc │ ├── config.pb.h │ └── keys.h ├── core.hpp ├── crypto.hpp ├── glibc_compat.c ├── hid.hpp ├── http_api.hpp ├── http_client.hpp ├── http_server.hpp ├── main.cpp ├── protobuf │ ├── json_codec.hpp │ ├── state.hpp │ └── wire_codec.hpp ├── trezord.ico ├── trezord.rc ├── utils.hpp └── wire.hpp ├── tarball.sh ├── test ├── fixtures │ ├── messages.hpp │ ├── messages.py │ ├── messages.txt │ └── trezor.bin ├── functional │ ├── .gitignore │ ├── call_initialize.json │ └── test.sh └── protobuf_codecs.cpp └── vendor ├── easyloggingpp └── easylogging++.h └── hidapi ├── AUTHORS.txt ├── CMakeLists.txt ├── LICENSE-bsd.txt ├── LICENSE-gpl3.txt ├── LICENSE-orig.txt ├── LICENSE.txt ├── README.txt ├── hidapi └── hidapi.h ├── libusb ├── CMakeLists.txt └── hid.c ├── mac ├── CMakeLists.txt └── hid.c └── windows ├── CMakeLists.txt └── hid.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build-*/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/jsoncpp"] 2 | path = vendor/jsoncpp 3 | url = https://github.com/open-source-parsers/jsoncpp.git 4 | [submodule "vendor/trezor-crypto"] 5 | path = vendor/trezor-crypto 6 | url = https://github.com/trezor/trezor-crypto.git 7 | [submodule "vendor/macdylibbundler"] 8 | path = vendor/macdylibbundler 9 | url = https://github.com/jpochyla/macdylibbundler.git 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: c 4 | 5 | before_install: 6 | - sudo add-apt-repository -y ppa:ondrej/pkg-nlnetlabs 7 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 8 | - sudo apt-get update -q 9 | - sudo apt-get install -y gcc-5 g++-5 10 | - sudo apt-get install -y libprotobuf-dev 11 | 12 | install: 13 | - export CXX="g++-5" CC="gcc-5" 14 | 15 | addons: 16 | apt: 17 | packages: 18 | - libboost-all-dev 19 | - libmicrohttpd-dev 20 | - libusb-1.0-0-dev 21 | 22 | script: 23 | - ./build.sh 24 | 25 | notifications: 26 | webhooks: 27 | urls: 28 | - http://ci-bot.satoshilabs.com:5000/travis 29 | on_success: always 30 | on_failure: always 31 | on_start: always 32 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # CMake build file for trezord 3 | # 4 | 5 | cmake_minimum_required(VERSION 2.8) 6 | 7 | project(trezord) 8 | 9 | file (STRINGS "VERSION" VERSION) 10 | 11 | option(BUILD_TESTS "Build tests?" off) 12 | 13 | include_directories(src) 14 | 15 | set (SRCS 16 | src/main.cpp 17 | src/http_api.hpp 18 | src/http_server.hpp 19 | src/http_client.hpp 20 | src/core.hpp 21 | src/wire.hpp 22 | src/utils.hpp 23 | src/protobuf/state.hpp 24 | src/protobuf/json_codec.hpp 25 | src/protobuf/wire_codec.hpp 26 | src/config/config.pb.cc 27 | src/config/config.pb.h) 28 | 29 | if (WIN32) 30 | set (SRCS src/trezord.rc ${SRCS}) 31 | endif(WIN32) 32 | 33 | IF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") 34 | SET(FREEBSD TRUE) 35 | ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") 36 | 37 | if (UNIX AND NOT APPLE AND NOT FREEBSD) 38 | set (SRCS src/glibc_compat.c ${SRCS}) 39 | endif(UNIX AND NOT APPLE) 40 | 41 | add_executable(trezord ${SRCS}) 42 | 43 | # use c++11, add version macro 44 | set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS} -DVERSION='\"${VERSION}\"'") 45 | 46 | # use vendored cmake modules 47 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules") 48 | 49 | if (WIN32) 50 | set(OS_LIBRARIES wsock32 ws2_32 z) 51 | add_definitions(-D_WIN32_WINNT=0x6000) 52 | else(WIN32) 53 | if (APPLE) 54 | set(OS_LIBRARIES pthread) 55 | elseif(FREEBSD) 56 | set(OS_LIBRARIES pthread z) 57 | else(APPLE) 58 | set(OS_LIBRARIES pthread dl z) 59 | endif(APPLE) 60 | endif(WIN32) 61 | 62 | target_link_libraries(trezord ${OS_LIBRARIES}) 63 | 64 | # add dynamic libs 65 | find_package(CURL REQUIRED) 66 | find_package(libmicrohttpd REQUIRED) 67 | 68 | # add static libs 69 | if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 70 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") 71 | set(BUILD_SHARED_LIBS off) 72 | set(Boost_USE_STATIC_LIBS on) 73 | set(CMAKE_FIND_STATIC FIRST) 74 | endif(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 75 | find_package(Boost 1.53.0 REQUIRED 76 | regex thread system unit_test_framework program_options chrono) 77 | find_package(Protobuf 2.5.0 REQUIRED) 78 | find_package(jsoncpp REQUIRED) 79 | 80 | # add vendored libs 81 | add_subdirectory(vendor/hidapi) 82 | add_subdirectory(vendor/trezor-crypto) 83 | 84 | include_directories( 85 | ${Boost_INCLUDE_DIRS} 86 | ${LIBMICROHTTPD_INCLUDE_DIRS} 87 | ${PROTOBUF_INCLUDE_DIRS} 88 | ${JSONCPP_INCLUDE_DIRS} 89 | ${CURL_INCLUDE_DIRS} 90 | vendor/hidapi/hidapi 91 | vendor/trezor-crypto 92 | vendor/easyloggingpp) 93 | 94 | target_link_libraries(trezord 95 | ${Boost_LIBRARIES} 96 | ${LIBMICROHTTPD_LIBRARIES} 97 | ${CURL_LIBRARIES} 98 | ${PROTOBUF_LIBRARIES} 99 | ${JSONCPP_LIBRARIES} 100 | hidapi 101 | TrezorCrypto) 102 | 103 | if(BUILD_TESTS) 104 | 105 | include_directories(test) 106 | 107 | add_executable(test-protobuf_codecs test/protobuf_codecs.cpp) 108 | 109 | target_link_libraries(test-protobuf_codecs 110 | ${Boost_LIBRARIES} 111 | ${PROTOBUF_LIBRARIES} 112 | ${JSONCPP_LIBRARIES}) 113 | 114 | enable_testing() 115 | add_test(ProtobufCodecs test-protobuf_codecs) 116 | 117 | endif(BUILD_TESTS) 118 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # trezord 2 | 3 | [![Build Status](https://travis-ci.org/trezor/trezord.svg?branch=master)](https://travis-ci.org/trezor/trezord) [![gitter](https://badges.gitter.im/trezor/community.svg)](https://gitter.im/trezor/community) 4 | 5 | TREZOR Communication Daemon aka TREZOR Bridge 6 | 7 | **DO NOT USE! This version has been obsoleted by a new generation of TREZOR Bridge available from here: [trezor/trezord-go](https://github.com/trezor/trezord-go)** 8 | 9 | ## What trezord does 10 | 11 | `trezord` (short for TREZOR Daemon), or TREZOR Bridge, is a small piece of software, used for websites (such as our own [TREZOR Wallet](https://wallet.trezor.io)), to talk with TREZOR devices. 12 | 13 | `trezord` starts a local webserver, with which both external applications and local applications can communicate. This webserver then communicates with TREZOR devices and returns their replies as JSON. `trezord` also manages access to devices - two applications cannot use the same device at the same time. 14 | 15 | Communicating with devices using `trezord` is more high-level than communicating with devices directly - `trezord` abstracts away USB communication, Protobuf serialization and platform differences. However, you still need to process individual messages. 16 | 17 | **For development of web apps for TREZOR, it is recommended to use [trezor.js](https://github.com/trezor/trezor.js) javascript API, which has separate javascript calls for most common usecases; or [TREZOR Connect](https://github.com/trezor/connect), which is even more high level.** (`trezor.js` communicates with `trezord` under the hood.) 18 | 19 | ## API documentation 20 | 21 | `trezord` starts server on `localhost`, with port `21324`. You can use `https`, by using `https://localback.net:21324` which redirects to localhost. You can call this web address with standard AJAX calls from websites (see the note about whitelisting). 22 | 23 | Server supports following API calls: 24 | 25 | | url
method | parameters | result type | description | 26 | |-------------|------------|-------------|-------------| 27 | | `/`
GET | | {`version`: string,
`configured`: boolean,
`validUntil`: timestamp} | Returns current version of bridge and info about configuration.
See `/configure` for more info. | 28 | | `/configure`
POST | request body: config, as hex string | {} | Before any advanced call, configuration file needs to be loaded to bridge.
Configuration file is signed by SatoshiLabs and the validity of the signature is limited.
Current config should be [in this repo](https://github.com/trezor/webwallet-data/blob/master/config_signed.bin), or [on AWS here](https://wallet.trezor.io/data/config_signed.bin). | 29 | | `/enumerate`
GET | | Array<{`path`: string,
`session`: string | null}> | Lists devices.
`path` uniquely defines device between more connected devices. It might or might not be unique over time; on some platform it changes, on others given USB port always returns the same path.
If `session` is null, nobody else is using the device; if it's string, it identifies who is using it. | 30 | | `/listen`
POST | request body: previous, as JSON | like `enumerate` | Listen to changes and returns either on change or after 30 second timeout. Compares change from `previous` that is sent as a parameter. "Change" is both connecting/disconnecting and session change. | 31 | | `/acquire/PATH/PREVIOUS`
POST | `PATH`: path of device
`PREVNOUS`: previous session (or string "null") | {`session`: string} | Acquires the device at `PATH`. By "acquiring" the device, you are claiming the device for yourself.
Before acquiring, checks that the current session is `PREVIOUS`.
If two applications call `acquire` on a newly connected device at the same time, only one of them succeed. | 32 | | `/release/SESSION`
POST | `SESSION`: session to release | {} | Releases the device with the given session.
By "releasing" the device, you claim that you don't want to use the device anymore. | 33 | | `/call/SESSION`
POST | `SESSION`: session to call

request body: JSON
{`type`: string, `message`: object} | {`type`: string, `body`: object} | Calls the message and returns the response from TREZOR.
Messages are defined in [this protobuf file](https://github.com/trezor/trezor-common/blob/master/protob/messages.proto).
`type` in request is, for example, `GetFeatures`; `type` in response is, for example, `Features` | 34 | 35 | ### Whitelisting 36 | 37 | You cannot connect to `trezord` from anywhere on the internet. Your URL needs to be specifically whitelisted; whitelist is in the signed config file, that is sent during `configure/` call. 38 | 39 | `localhost` is specifically whitelisted, so you can experiment on `http://localhost`. If you want to add your url in order to make a TREZOR web app, [make a pull request to this file](https://github.com/trezor/trezor-common/blob/master/signer/config.json). 40 | 41 | ## Download latest binary 42 | 43 | Latest build packages are on https://wallet.trezor.io/data/bridge/latest/index.html 44 | 45 | ## Checking out sources 46 | 47 | ``` 48 | git clone https://github.com/trezor/trezord.git 49 | cd trezord 50 | git submodule update --init 51 | ``` 52 | 53 | ## Building 54 | 55 | Change into `release/linux` or `release/windows` directory and run: `make`. 56 | 57 | Or run `build.sh` to build locally using local dependencies. 58 | 59 | In the latter case, here is a rough list of Debian package build dependencies: 60 | 61 | `build-essential cmake curl libcurl4-gnutls-dev libprotobuf-dev pkg-config libusb-1.0-0 libusb-1.0-0-dev libmicrohttpd-dev libboost-all-dev protobuf-compiler` 62 | 63 | or Fedora (as of 26) build dependencies: 64 | 65 | `boost-devel-static protobuf-compiler cmake gcc-c++ libcurl-devel protobuf-devel libusbx-devel libmicrohttpd-devel protobuf-static` 66 | 67 | Also you might need to regenerate protobuf files if you are using protobuf-3.x: 68 | 69 | ``` 70 | cd src/config 71 | wget https://raw.githubusercontent.com/trezor/trezor-common/master/protob/config.proto 72 | protoc -I/usr/include -I. --cpp_out=. config.proto 73 | ``` 74 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.2.1 2 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | 7 | TARGET=$1 8 | BUILDDIR=build${TARGET:+-$TARGET} 9 | BUILDTYPE=${2-Debug} 10 | 11 | case "$TARGET" in 12 | lin32 | lin64 | win32 | win64 | mac64 ) # cross build 13 | PLATFORM_FILE="-C $(pwd)/cmake/Platform-$TARGET.cmake" 14 | ;; 15 | * ) # native build 16 | JOBS="-j 4" 17 | ;; 18 | esac 19 | 20 | # Compile jsoncpp 21 | if [ \! -f $BUILDDIR/lib/jsoncpp/lib/libjson.a ]; then 22 | mkdir -p $BUILDDIR/lib/jsoncpp && cd $BUILDDIR/lib/jsoncpp 23 | cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE -DJSONCPP_WITH_TESTS=OFF $PLATFORM_FILE ../../../vendor/jsoncpp 24 | make $JOBS 25 | cd ../../.. 26 | fi 27 | 28 | mkdir -p $BUILDDIR && cd $BUILDDIR 29 | cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE $PLATFORM_FILE .. 30 | make $JOBS 31 | -------------------------------------------------------------------------------- /cert/readme.txt: -------------------------------------------------------------------------------- 1 | certs are in webwallet-data repository in /bridge/cert directory 2 | -------------------------------------------------------------------------------- /cmake/Platform-lin32.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_C_FLAGS "-m32" CACHE STRING "") 2 | set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "") 3 | set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -Wl,--wrap=memcpy -Wl,--wrap=secure_getenv" CACHE STRING "") 4 | -------------------------------------------------------------------------------- /cmake/Platform-lin64.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -Wl,--wrap=memcpy -Wl,--wrap=secure_getenv" CACHE STRING "") 2 | -------------------------------------------------------------------------------- /cmake/Platform-mac64.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME "Darwin" CACHE STRING "") 2 | set(CMAKE_SYSTEM_VERSION "10.8" CACHE STRING "") 3 | set(TARGET_ARCH "x86_64" CACHE STRING "") 4 | 5 | set(CMAKE_C_COMPILER "o64-gcc" CACHE STRING "") 6 | set(CMAKE_CXX_COMPILER "o64-g++" CACHE STRING "") 7 | set(CMAKE_AR "x86_64-apple-darwin12-ar" CACHE STRING "") 8 | set(CMAKE_RANLIB "x86_64-apple-darwin12-ranlib" CACHE STRING "") 9 | set(PKG_CONFIG_EXECUTABLE "x86_64-apple-darwin12-pkg-config" CACHE STRING "") 10 | set(CMAKE_CXX_FLAGS "-v" CACHE STRING "") 11 | 12 | set(CMAKE_OSX_SYSROOT "/opt/osxcross/target/SDK/MacOSX10.8.sdk" CACHE STRING "") 13 | set(CMAKE_FIND_ROOT_PATH "/opt/osxcross/target/macports/pkgs/opt/local" CACHE STRING "") 14 | 15 | include_directories("/opt/osxcross/target/macports/pkgs/opt/local/include") 16 | link_directories("/opt/osxcross/target/macports/pkgs/opt/local/lib") 17 | -------------------------------------------------------------------------------- /cmake/Platform-win32.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME "Windows" CACHE STRING "") 2 | set(TARGET_ARCH "i686-w64-mingw32" CACHE STRING "") 3 | set(CMAKE_C_COMPILER "${TARGET_ARCH}-gcc" CACHE STRING "") 4 | set(CMAKE_CXX_COMPILER "${TARGET_ARCH}-g++" CACHE STRING "") 5 | set(CMAKE_RC_COMPILER_INIT "${TARGET_ARCH}-windres" CACHE STRING "") 6 | set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "" CACHE STRING "") 7 | set(CMAKE_FIND_ROOT_PATH "/usr/${TARGET_ARCH}/sys-root/mingw" CACHE STRING "") 8 | set(CMAKE_EXE_LINKER_FLAGS "-mwindows" CACHE STRING "") 9 | -------------------------------------------------------------------------------- /cmake/Platform-win64.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME "Windows" CACHE STRING "") 2 | set(TARGET_ARCH "x86_64-w64-mingw32" CACHE STRING "") 3 | set(CMAKE_C_COMPILER "${TARGET_ARCH}-gcc" CACHE STRING "") 4 | set(CMAKE_CXX_COMPILER "${TARGET_ARCH}-g++" CACHE STRING "") 5 | set(CMAKE_RC_COMPILER_INIT "${TARGET_ARCH}-windres" CACHE STRING "") 6 | set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "" CACHE STRING "") 7 | set(CMAKE_FIND_ROOT_PATH "/usr/${TARGET_ARCH}/sys-root/mingw" CACHE STRING "") 8 | set(CMAKE_EXE_LINKER_FLAGS "-mwindows" CACHE STRING "") 9 | -------------------------------------------------------------------------------- /cmake/modules/Findjsoncpp.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake package file for jsoncpp 3 | # 4 | 5 | find_path(JSONCPP_INCLUDE_DIR json 6 | HINTS "${CMAKE_SOURCE_DIR}/vendor/jsoncpp/include") 7 | find_library(JSONCPP_LIBRARY NAMES jsoncpp 8 | HINTS "${CMAKE_BINARY_DIR}/lib/jsoncpp/src/lib_json") 9 | 10 | set(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY}) 11 | set(JSONCPP_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR}) 12 | 13 | include(FindPackageHandleStandardArgs) 14 | find_package_handle_standard_args(JSONCPP 15 | DEFAULT_MSG JSONCPP_LIBRARY JSONCPP_INCLUDE_DIR) 16 | -------------------------------------------------------------------------------- /cmake/modules/Findlibmicrohttpd.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libmicrohttpd 2 | # Once done this will define 3 | # 4 | # MICROHTTPD_FOUND - system has libmicrohttpd 5 | # MICROHTTPD_INCLUDE_DIRS - the libmicrohttpd include directory 6 | # MICROHTTPD_LIBRARIES - Link these to use libmicrohttpd 7 | # MICROHTTPD_DEFINITIONS - Compiler switches required for using libmicrohttpd 8 | # 9 | # Copyright (c) 2011 Wesley Moore 10 | # 11 | # Redistribution and use is allowed according to the terms of the New 12 | # BSD license. 13 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 14 | # 15 | 16 | 17 | if (LIBMICROHTTPD_LIBRARIES AND LIBMICROHTTPD_INCLUDE_DIRS) 18 | # in cache already 19 | set(LIBMICROHTTPD_FOUND TRUE) 20 | else (LIBMICROHTTPD_LIBRARIES AND LIBMICROHTTPD_INCLUDE_DIRS) 21 | # use pkg-config to get the directories and then use these values 22 | # in the FIND_PATH() and FIND_LIBRARY() calls 23 | if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 24 | include(UsePkgConfig) 25 | pkgconfig(libmicrohttpd _LIBMICROHTTPD_INCLUDEDIR _LIBMICROHTTPD_LIBDIR _LIBMICROHTTPD_LDFLAGS _LIBMICROHTTPD_CFLAGS) 26 | else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 27 | find_package(PkgConfig) 28 | if (PKG_CONFIG_FOUND) 29 | pkg_check_modules(_LIBMICROHTTPD libmicrohttpd) 30 | endif (PKG_CONFIG_FOUND) 31 | endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 32 | find_path(LIBMICROHTTPD_INCLUDE_DIR 33 | NAMES 34 | microhttpd.h 35 | PATHS 36 | ${_LIBMICROHTTPD_INCLUDEDIR} 37 | /usr/include 38 | /usr/local/include 39 | /usr/pkg/include 40 | /opt/local/include 41 | /sw/include 42 | ) 43 | 44 | find_library(LIBMICROHTTPD_LIBRARY 45 | NAMES 46 | microhttpd 47 | PATHS 48 | ${_LIBMICROHTTPD_LIBDIR} 49 | /usr/lib 50 | /usr/local/lib 51 | /usr/pkg/lib 52 | /opt/local/lib 53 | /sw/lib 54 | ) 55 | 56 | if (LIBMICROHTTPD_LIBRARY) 57 | set(LIBMICROHTTPD_FOUND TRUE) 58 | endif (LIBMICROHTTPD_LIBRARY) 59 | 60 | set(LIBMICROHTTPD_INCLUDE_DIRS 61 | ${LIBMICROHTTPD_INCLUDE_DIR} 62 | ) 63 | 64 | if (LIBMICROHTTPD_FOUND) 65 | set(LIBMICROHTTPD_LIBRARIES 66 | ${LIBMICROHTTPD_LIBRARIES} 67 | ${LIBMICROHTTPD_LIBRARY} 68 | ) 69 | endif (LIBMICROHTTPD_FOUND) 70 | 71 | if (LIBMICROHTTPD_INCLUDE_DIRS AND LIBMICROHTTPD_LIBRARIES) 72 | set(LIBMICROHTTPD_FOUND TRUE) 73 | endif (LIBMICROHTTPD_INCLUDE_DIRS AND LIBMICROHTTPD_LIBRARIES) 74 | 75 | if (LIBMICROHTTPD_FOUND) 76 | if (NOT LIBMICROHTTPD_FIND_QUIETLY) 77 | message(STATUS "Found libmicrohttpd: ${LIBMICROHTTPD_LIBRARIES}") 78 | endif (NOT LIBMICROHTTPD_FIND_QUIETLY) 79 | else (LIBMICROHTTPD_FOUND) 80 | if (LIBMICROHTTPD_FIND_REQUIRED) 81 | message(FATAL_ERROR "Could not find libmicrohttpd") 82 | endif (LIBMICROHTTPD_FIND_REQUIRED) 83 | endif (LIBMICROHTTPD_FOUND) 84 | 85 | # show the LIBMICROHTTPD_INCLUDE_DIRS and LIBMICROHTTPD_LIBRARIES variables only in the advanced view 86 | mark_as_advanced(LIBMICROHTTPD_INCLUDE_DIRS LIBMICROHTTPD_LIBRARIES) 87 | 88 | endif (LIBMICROHTTPD_LIBRARIES AND LIBMICROHTTPD_INCLUDE_DIRS) 89 | 90 | -------------------------------------------------------------------------------- /release/linux/.gitignore: -------------------------------------------------------------------------------- 1 | privkey.asc 2 | -------------------------------------------------------------------------------- /release/linux/Dockerfile: -------------------------------------------------------------------------------- 1 | # initialize from the image 2 | 3 | FROM fedora:25 4 | 5 | # update package repositories 6 | 7 | RUN dnf update -y 8 | 9 | # install tools 10 | 11 | RUN dnf install -y cmake make wget 12 | RUN dnf install -y gcc gcc-c++ git make patchutils pkgconfig wget 13 | 14 | # install dependencies for Linux packaging 15 | 16 | RUN dnf install -y ruby-devel rubygems rpm-build 17 | RUN gem install fpm --no-document 18 | 19 | # install dependencies for Linux build 20 | 21 | RUN dnf install -y glibc-devel glibc-static libgcc libstdc++-static zlib-devel 22 | RUN dnf install -y boost-static libusbx-devel protobuf-static 23 | 24 | RUN dnf install -y glibc-devel.i686 glibc-static.i686 libgcc.i686 libstdc++-static.i686 zlib-devel.i686 25 | RUN dnf install -y boost-static.i686 libusbx-devel.i686 protobuf-static.i686 26 | 27 | # install used networking libraries 28 | 29 | RUN dnf install -y libcurl-devel libmicrohttpd-devel 30 | RUN dnf install -y libcurl.i686 libcurl-devel.i686 libmicrohttpd.i686 libmicrohttpd-devel.i686 31 | 32 | # install package signing tools 33 | RUN dnf install -y rpm-sign 34 | RUN ln -s gpg2 /usr/bin/gpg 35 | -------------------------------------------------------------------------------- /release/linux/Makefile: -------------------------------------------------------------------------------- 1 | PLATFORM = linux 2 | VOL_MOUNT = -v $(shell pwd)/../..:/trezord-src 3 | IMAGETAG = trezord-build-env-$(PLATFORM) 4 | 5 | all: .package64 .package32 6 | 7 | clean: 8 | $(info Cleaning...) 9 | sudo rm -rf ../../build-lin* 10 | 11 | .package64: .binary64 12 | $(info Packaging ...) 13 | docker run -i -t $(VOL_MOUNT) $(IMAGETAG) /trezord-src/release/$(PLATFORM)/release.sh lin64 14 | 15 | .binary64: .docker-image 16 | $(info Building in docker ...) 17 | docker run -t $(VOL_MOUNT) $(IMAGETAG) /trezord-src/build.sh lin64 Release 18 | 19 | .package32: .binary32 20 | $(info Packaging ...) 21 | docker run -i -t $(VOL_MOUNT) $(IMAGETAG) /trezord-src/release/$(PLATFORM)/release.sh lin32 22 | 23 | .binary32: .docker-image 24 | $(info Building in docker ...) 25 | docker run -t $(VOL_MOUNT) $(IMAGETAG) /trezord-src/build.sh lin32 Release 26 | 27 | .docker-image: 28 | $(info Preparing docker image ...) 29 | docker build -t $(IMAGETAG) . 30 | 31 | shell: .docker-image 32 | docker run -i -t $(VOL_MOUNT) $(IMAGETAG) /bin/bash 33 | 34 | privkey: 35 | gpg --armor --export-secret-key > privkey.asc 36 | -------------------------------------------------------------------------------- /release/linux/fpm.after-install.sh: -------------------------------------------------------------------------------- 1 | if which systemctl > /dev/null ; then 2 | systemctl enable trezord.service 3 | systemctl start trezord.service 4 | else 5 | chkconfig --add trezord || update-rc.d trezord defaults 6 | service trezord start 7 | fi 8 | -------------------------------------------------------------------------------- /release/linux/fpm.before-install.sh: -------------------------------------------------------------------------------- 1 | getent group trezord >/dev/null || groupadd -r trezord 2 | getent passwd trezord >/dev/null || useradd -r -g trezord -d /var -s /sbin/nologin -c "TREZOR Bridge" trezord 3 | touch /var/log/trezord.log 4 | chown trezord:trezord /var/log/trezord.log 5 | chmod 660 /var/log/trezord.log 6 | -------------------------------------------------------------------------------- /release/linux/fpm.before-remove.sh: -------------------------------------------------------------------------------- 1 | if which systemctl > /dev/null ; then 2 | systemctl stop trezord.service 3 | systemctl disable trezord.service 4 | else 5 | service trezord stop 6 | chkconfig --del trezord || update-rc.d -f trezord remove 7 | fi 8 | -------------------------------------------------------------------------------- /release/linux/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | 7 | GPGSIGNKEY=86E6792FC27BFD478860C11091F3B339B9A02A3D 8 | TARGET=$1 9 | BUILDDIR=build${TARGET:+-$TARGET} 10 | VERSION=$(cat ../../VERSION) 11 | 12 | cd ../../$BUILDDIR 13 | 14 | install -D -m 0755 trezord ./usr/bin/trezord 15 | install -D -m 0644 ../release/linux/trezor.rules ./lib/udev/rules.d/51-trezor.rules 16 | install -D -m 0755 ../release/linux/trezord.init ./etc/init.d/trezord 17 | install -D -m 0644 ../release/linux/trezord.service ./usr/lib/systemd/system/trezord.service 18 | 19 | strip ./usr/bin/trezord 20 | 21 | # prepare GPG signing environment 22 | GPG_PRIVKEY=../release/linux/privkey.asc 23 | if [ -r $GPG_PRIVKEY ]; then 24 | export GPG_TTY=$(tty) 25 | export LC_ALL=en_US.UTF-8 26 | gpg --import ../release/linux/privkey.asc 27 | GPG_SIGN=gpg 28 | fi 29 | 30 | NAME=trezor-bridge 31 | 32 | rm -f *.deb *.rpm *.tar.bz2 33 | tar -cjf $NAME-$VERSION.tar.bz2 --exclude=./lib/jsoncpp ./etc ./usr ./lib 34 | 35 | for TYPE in "deb" "rpm"; do 36 | case "$TARGET-$TYPE" in 37 | lin32-deb) 38 | ARCH=i386 39 | DEPS="-d libcurl3 -d libmicrohttpd12 -d libusb-1.0-0" 40 | ;; 41 | lin64-deb) 42 | ARCH=amd64 43 | DEPS="-d libcurl3 -d libmicrohttpd12 -d libusb-1.0-0" 44 | ;; 45 | lin32-rpm) 46 | ARCH=i386 47 | DEPS="--rpm-autoreq" 48 | ;; 49 | lin64-rpm) 50 | ARCH=x86_64 51 | DEPS="--rpm-autoreq" 52 | ;; 53 | esac 54 | fpm \ 55 | -s tar \ 56 | -t $TYPE \ 57 | -a $ARCH \ 58 | -n $NAME \ 59 | -v $VERSION \ 60 | --license "LGPL-3.0" \ 61 | --vendor "SatoshiLabs" \ 62 | --description "Communication daemon for TREZOR" \ 63 | --maintainer "SatoshiLabs " \ 64 | --url "https://trezor.io/" \ 65 | --category "Productivity/Security" \ 66 | --before-install ../release/linux/fpm.before-install.sh \ 67 | --after-install ../release/linux/fpm.after-install.sh \ 68 | --before-remove ../release/linux/fpm.before-remove.sh \ 69 | $DEPS \ 70 | $NAME-$VERSION.tar.bz2 71 | case "$TYPE-$GPG_SIGN" in 72 | deb-gpg) 73 | ../release/linux/dpkg-sig -k $GPGSIGNKEY --sign builder trezor-bridge_${VERSION}_${ARCH}.deb 74 | ;; 75 | rpm-gpg) 76 | rpm --addsign -D "%_gpg_name $GPGSIGNKEY" trezor-bridge-${VERSION}-1.${ARCH}.rpm 77 | ;; 78 | esac 79 | done 80 | 81 | 82 | rm -rf ./etc ./usr ./lib 83 | -------------------------------------------------------------------------------- /release/linux/trezor.rules: -------------------------------------------------------------------------------- 1 | # TREZOR: The Original Hardware Wallet 2 | # https://trezor.io/ 3 | # Put this file into /usr/lib/udev/rules.d 4 | 5 | # TREZOR 6 | SUBSYSTEM=="usb", ATTR{idVendor}=="534c", ATTR{idProduct}=="0001", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n" 7 | KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl" 8 | 9 | # TREZOR v2 10 | SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c0", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n" 11 | SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c1", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n" 12 | KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c0", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl" 13 | KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl" 14 | -------------------------------------------------------------------------------- /release/linux/trezord.init: -------------------------------------------------------------------------------- 1 | ### BEGIN INIT INFO 2 | # Provides: trezord 3 | # Required-Start: $local_fs $network $named 4 | # Required-Stop: $local_fs $network $named 5 | # Default-Start: 2 3 4 5 6 | # Default-Stop: 0 1 6 7 | # Short-Description: TREZOR Bridge 8 | # Description: This is a daemon that allows webpages to communicate 9 | # with TREZOR devices. 10 | ### END INIT INFO 11 | 12 | . /lib/lsb/init-functions 13 | 14 | NAME=trezord 15 | DAEMON=/usr/bin/trezord 16 | USER=trezord 17 | 18 | test -x $DAEMON || exit 5 19 | 20 | case $1 in 21 | 22 | start) 23 | if pidof $DAEMON > /dev/null ; then 24 | log_warning_msg "$NAME already running" 25 | exit 26 | fi 27 | if su - $USER -s /bin/sh -c $DAEMON > /dev/null ; then 28 | log_success_msg "$NAME started" 29 | else 30 | log_failure_msg "$NAME start failed" 31 | fi 32 | ;; 33 | stop) 34 | if pidof $DAEMON > /dev/null ; then 35 | if killall $DAEMON > /dev/null ; then 36 | log_success_msg "$NAME stopped" 37 | else 38 | log_failure_msg "$NAME stop failed" 39 | fi 40 | else 41 | log_warning_msg "$NAME not running" 42 | fi 43 | ;; 44 | restart) 45 | $0 stop 46 | sleep 2 47 | $0 start 48 | ;; 49 | status) 50 | if pidof $DAEMON > /dev/null ; then 51 | log_success_msg "$NAME is running" 52 | else 53 | log_success_msg "$NAME is not running" 54 | fi 55 | ;; 56 | *) 57 | echo "Usage: $0 {start|stop|restart|status}" 58 | ;; 59 | esac 60 | -------------------------------------------------------------------------------- /release/linux/trezord.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=TREZOR Bridge 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/trezord -f 8 | User=trezord 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /release/mac/Dockerfile: -------------------------------------------------------------------------------- 1 | # initialize from base image 2 | 3 | FROM ubuntu:latest 4 | 5 | # install dependencies 6 | 7 | RUN apt-get update && apt-get install -y \ 8 | git wget cmake \ 9 | gcc g++ zlib1g-dev libmpc-dev libmpfr-dev libgmp-dev 10 | 11 | # build and install osxcross and osx sdk 12 | 13 | RUN git clone https://github.com/jpochyla/osxcross.git /opt/osxcross 14 | COPY ./MacOSX10.8.sdk.tar.xz /opt/osxcross/tarballs/ 15 | 16 | WORKDIR /opt/osxcross 17 | 18 | ENV MACOSX_DEPLOYMENT_TARGET 10.8 19 | ENV PATH /opt/osxcross/target/bin:$PATH 20 | 21 | RUN ./tools/get_dependencies.sh 22 | RUN echo | ./build.sh 23 | RUN echo | GCC_VERSION=4.9.1 ./build_gcc.sh 24 | 25 | # install trezord dependencies from macports 26 | 27 | RUN osxcross-macports install \ 28 | gdbm-1.11 zlib-1.2.8 xz-5.2.1 libgpg-error-1.19 \ 29 | protobuf-cpp-2.5.0 curl boost libmicrohttpd 30 | 31 | # make cmake and dylibbundler happy 32 | 33 | RUN mkdir /Applications 34 | RUN ln -s x86_64-apple-darwin12-otool /opt/osxcross/target/bin/otool 35 | RUN ln -s x86_64-apple-darwin12-install_name_tool /opt/osxcross/target/bin/install_name_tool 36 | RUN ln -s /opt/osxcross/target/macports/pkgs/opt/local /opt/local 37 | -------------------------------------------------------------------------------- /release/mac/Makefile: -------------------------------------------------------------------------------- 1 | PLATFORM = mac 2 | BITS = 64 3 | TARGET = mac$(BITS) 4 | VOL_MOUNT = -v $(shell pwd)/../..:/trezord-src 5 | IMAGETAG = trezord-build-env-$(PLATFORM) 6 | SDK_FILE = MacOSX10.8.sdk.tar.xz 7 | SDK_URL = https://github.com/phracker/MacOSX-SDKs/releases/download/MacOSX10.11.sdk/MacOSX10.8.sdk.tar.xz 8 | 9 | all: .package 10 | 11 | .package: .binary 12 | $(info Packaging ...) 13 | docker run -t $(VOL_MOUNT) $(IMAGETAG) /trezord-src/release/$(PLATFORM)/release.sh $(TARGET) 14 | 15 | .binary: .docker-image 16 | $(info Building in docker ...) 17 | docker run -t $(VOL_MOUNT) $(IMAGETAG) /trezord-src/build.sh $(TARGET) Release 18 | 19 | .docker-image: $(SDK_FILE) 20 | $(info Preparing docker image ...) 21 | docker build -t $(IMAGETAG) . 22 | 23 | $(SDK_FILE): 24 | $(info Downloading OS X SDK ...) 25 | curl -o $(SDK_FILE) -L $(SDK_URL) 26 | 27 | shell: .docker-image 28 | docker run -i -t $(VOL_MOUNT) $(IMAGETAG) /bin/bash 29 | -------------------------------------------------------------------------------- /release/mac/dist/Library/LaunchAgents/com.bitcointrezor.trezorBridge.trezord.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.bitcointrezor.trezorBridge.trezord 7 | 8 | KeepAlive 9 | 10 | 11 | ProgramArguments 12 | 13 | sh 14 | -c 15 | /Applications/Utilities/TREZOR\ Bridge/trezord 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /release/mac/installer/TREZOR Bridge.pkgproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PROJECT 6 | 7 | PACKAGE_FILES 8 | 9 | DEFAULT_INSTALL_LOCATION 10 | / 11 | HIERARCHY 12 | 13 | CHILDREN 14 | 15 | 16 | CHILDREN 17 | 18 | 19 | CHILDREN 20 | 21 | 22 | CHILDREN 23 | 24 | 25 | CHILDREN 26 | 27 | GID 28 | 80 29 | PATH 30 | ../../../build-mac64/libs 31 | PATH_TYPE 32 | 1 33 | PERMISSIONS 34 | 493 35 | TYPE 36 | 3 37 | UID 38 | 0 39 | 40 | 41 | CHILDREN 42 | 43 | GID 44 | 80 45 | PATH 46 | ../../../build-mac64/trezord 47 | PATH_TYPE 48 | 1 49 | PERMISSIONS 50 | 493 51 | TYPE 52 | 3 53 | UID 54 | 0 55 | 56 | 57 | GID 58 | 80 59 | PATH 60 | TREZOR Bridge 61 | PATH_TYPE 62 | 0 63 | PERMISSIONS 64 | 493 65 | TYPE 66 | 2 67 | UID 68 | 0 69 | 70 | 71 | GID 72 | 80 73 | PATH 74 | Utilities 75 | PATH_TYPE 76 | 0 77 | PERMISSIONS 78 | 493 79 | TYPE 80 | 1 81 | UID 82 | 0 83 | 84 | 85 | GID 86 | 80 87 | PATH 88 | Applications 89 | PATH_TYPE 90 | 0 91 | PERMISSIONS 92 | 509 93 | TYPE 94 | 1 95 | UID 96 | 0 97 | 98 | 99 | CHILDREN 100 | 101 | 102 | CHILDREN 103 | 104 | GID 105 | 80 106 | PATH 107 | Application Support 108 | PATH_TYPE 109 | 0 110 | PERMISSIONS 111 | 493 112 | TYPE 113 | 1 114 | UID 115 | 0 116 | 117 | 118 | CHILDREN 119 | 120 | GID 121 | 0 122 | PATH 123 | Automator 124 | PATH_TYPE 125 | 0 126 | PERMISSIONS 127 | 493 128 | TYPE 129 | 1 130 | UID 131 | 0 132 | 133 | 134 | CHILDREN 135 | 136 | GID 137 | 0 138 | PATH 139 | Documentation 140 | PATH_TYPE 141 | 0 142 | PERMISSIONS 143 | 493 144 | TYPE 145 | 1 146 | UID 147 | 0 148 | 149 | 150 | CHILDREN 151 | 152 | GID 153 | 0 154 | PATH 155 | Filesystems 156 | PATH_TYPE 157 | 0 158 | PERMISSIONS 159 | 493 160 | TYPE 161 | 1 162 | UID 163 | 0 164 | 165 | 166 | CHILDREN 167 | 168 | GID 169 | 0 170 | PATH 171 | Frameworks 172 | PATH_TYPE 173 | 0 174 | PERMISSIONS 175 | 493 176 | TYPE 177 | 1 178 | UID 179 | 0 180 | 181 | 182 | CHILDREN 183 | 184 | GID 185 | 0 186 | PATH 187 | Input Methods 188 | PATH_TYPE 189 | 0 190 | PERMISSIONS 191 | 493 192 | TYPE 193 | 1 194 | UID 195 | 0 196 | 197 | 198 | CHILDREN 199 | 200 | GID 201 | 0 202 | PATH 203 | Internet Plug-Ins 204 | PATH_TYPE 205 | 0 206 | PERMISSIONS 207 | 493 208 | TYPE 209 | 1 210 | UID 211 | 0 212 | 213 | 214 | CHILDREN 215 | 216 | 217 | CHILDREN 218 | 219 | GID 220 | 0 221 | PATH 222 | ../dist/Library/LaunchAgents/com.bitcointrezor.trezorBridge.trezord.plist 223 | PATH_TYPE 224 | 1 225 | PERMISSIONS 226 | 420 227 | TYPE 228 | 3 229 | UID 230 | 0 231 | 232 | 233 | GID 234 | 0 235 | PATH 236 | LaunchAgents 237 | PATH_TYPE 238 | 0 239 | PERMISSIONS 240 | 493 241 | TYPE 242 | 1 243 | UID 244 | 0 245 | 246 | 247 | CHILDREN 248 | 249 | GID 250 | 0 251 | PATH 252 | LaunchDaemons 253 | PATH_TYPE 254 | 0 255 | PERMISSIONS 256 | 493 257 | TYPE 258 | 1 259 | UID 260 | 0 261 | 262 | 263 | CHILDREN 264 | 265 | GID 266 | 0 267 | PATH 268 | PreferencePanes 269 | PATH_TYPE 270 | 0 271 | PERMISSIONS 272 | 493 273 | TYPE 274 | 1 275 | UID 276 | 0 277 | 278 | 279 | CHILDREN 280 | 281 | GID 282 | 0 283 | PATH 284 | Preferences 285 | PATH_TYPE 286 | 0 287 | PERMISSIONS 288 | 493 289 | TYPE 290 | 1 291 | UID 292 | 0 293 | 294 | 295 | CHILDREN 296 | 297 | GID 298 | 80 299 | PATH 300 | Printers 301 | PATH_TYPE 302 | 0 303 | PERMISSIONS 304 | 493 305 | TYPE 306 | 1 307 | UID 308 | 0 309 | 310 | 311 | CHILDREN 312 | 313 | GID 314 | 0 315 | PATH 316 | PrivilegedHelperTools 317 | PATH_TYPE 318 | 0 319 | PERMISSIONS 320 | 493 321 | TYPE 322 | 1 323 | UID 324 | 0 325 | 326 | 327 | CHILDREN 328 | 329 | GID 330 | 0 331 | PATH 332 | QuickLook 333 | PATH_TYPE 334 | 0 335 | PERMISSIONS 336 | 493 337 | TYPE 338 | 1 339 | UID 340 | 0 341 | 342 | 343 | CHILDREN 344 | 345 | GID 346 | 0 347 | PATH 348 | QuickTime 349 | PATH_TYPE 350 | 0 351 | PERMISSIONS 352 | 493 353 | TYPE 354 | 1 355 | UID 356 | 0 357 | 358 | 359 | CHILDREN 360 | 361 | GID 362 | 0 363 | PATH 364 | Screen Savers 365 | PATH_TYPE 366 | 0 367 | PERMISSIONS 368 | 493 369 | TYPE 370 | 1 371 | UID 372 | 0 373 | 374 | 375 | CHILDREN 376 | 377 | GID 378 | 0 379 | PATH 380 | Scripts 381 | PATH_TYPE 382 | 0 383 | PERMISSIONS 384 | 493 385 | TYPE 386 | 1 387 | UID 388 | 0 389 | 390 | 391 | CHILDREN 392 | 393 | GID 394 | 0 395 | PATH 396 | Services 397 | PATH_TYPE 398 | 0 399 | PERMISSIONS 400 | 493 401 | TYPE 402 | 1 403 | UID 404 | 0 405 | 406 | 407 | CHILDREN 408 | 409 | GID 410 | 0 411 | PATH 412 | Widgets 413 | PATH_TYPE 414 | 0 415 | PERMISSIONS 416 | 493 417 | TYPE 418 | 1 419 | UID 420 | 0 421 | 422 | 423 | GID 424 | 0 425 | PATH 426 | Library 427 | PATH_TYPE 428 | 0 429 | PERMISSIONS 430 | 493 431 | TYPE 432 | 1 433 | UID 434 | 0 435 | 436 | 437 | CHILDREN 438 | 439 | 440 | CHILDREN 441 | 442 | 443 | CHILDREN 444 | 445 | GID 446 | 0 447 | PATH 448 | Extensions 449 | PATH_TYPE 450 | 0 451 | PERMISSIONS 452 | 493 453 | TYPE 454 | 1 455 | UID 456 | 0 457 | 458 | 459 | GID 460 | 0 461 | PATH 462 | Library 463 | PATH_TYPE 464 | 0 465 | PERMISSIONS 466 | 493 467 | TYPE 468 | 1 469 | UID 470 | 0 471 | 472 | 473 | GID 474 | 0 475 | PATH 476 | System 477 | PATH_TYPE 478 | 0 479 | PERMISSIONS 480 | 493 481 | TYPE 482 | 1 483 | UID 484 | 0 485 | 486 | 487 | CHILDREN 488 | 489 | 490 | CHILDREN 491 | 492 | GID 493 | 0 494 | PATH 495 | Shared 496 | PATH_TYPE 497 | 0 498 | PERMISSIONS 499 | 1023 500 | TYPE 501 | 1 502 | UID 503 | 0 504 | 505 | 506 | GID 507 | 80 508 | PATH 509 | Users 510 | PATH_TYPE 511 | 0 512 | PERMISSIONS 513 | 493 514 | TYPE 515 | 1 516 | UID 517 | 0 518 | 519 | 520 | GID 521 | 0 522 | PATH 523 | / 524 | PATH_TYPE 525 | 0 526 | PERMISSIONS 527 | 493 528 | TYPE 529 | 1 530 | UID 531 | 0 532 | 533 | PAYLOAD_TYPE 534 | 0 535 | SPLIT_FORKS 536 | 537 | VERSION 538 | 3 539 | 540 | PACKAGE_SCRIPTS 541 | 542 | POSTINSTALL_PATH 543 | 544 | PATH 545 | after-install.sh 546 | PATH_TYPE 547 | 1 548 | 549 | RESOURCES 550 | 551 | 552 | PACKAGE_SETTINGS 553 | 554 | AUTHENTICATION 555 | 556 | CONCLUSION_ACTION 557 | 0 558 | IDENTIFIER 559 | com.bitcointrezor.pkg.TREZORBridge 560 | NAME 561 | TREZOR Bridge 562 | OVERWRITE_PERMISSIONS 563 | 564 | USE_HFS+_COMPRESSION 565 | 566 | VERSION 567 | 1.1.0 568 | 569 | PROJECT_COMMENTS 570 | 571 | NOTES 572 | 573 | PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1M 574 | IDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQv 575 | c3RyaWN0LmR0ZCI+CjxodG1sPgo8aGVhZD4KPG1ldGEgaHR0cC1l 576 | cXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7 577 | IGNoYXJzZXQ9VVRGLTgiPgo8bWV0YSBodHRwLWVxdWl2PSJDb250 578 | ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp 579 | dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u 580 | dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD 581 | b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjEzNDcuNTciPgo8c3R5bGUg 582 | dHlwZT0idGV4dC9jc3MiPgo8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5 583 | Pgo8L2JvZHk+CjwvaHRtbD4K 584 | 585 | 586 | PROJECT_SETTINGS 587 | 588 | BUILD_PATH 589 | 590 | PATH 591 | ../dist 592 | PATH_TYPE 593 | 1 594 | 595 | EXCLUDED_FILES 596 | 597 | 598 | PATTERNS_ARRAY 599 | 600 | 601 | REGULAR_EXPRESSION 602 | 603 | STRING 604 | .DS_Store 605 | TYPE 606 | 0 607 | 608 | 609 | PROTECTED 610 | 611 | PROXY_NAME 612 | Remove .DS_Store files 613 | PROXY_TOOLTIP 614 | Remove ".DS_Store" files created by the Finder. 615 | STATE 616 | 617 | 618 | 619 | PATTERNS_ARRAY 620 | 621 | 622 | REGULAR_EXPRESSION 623 | 624 | STRING 625 | .pbdevelopment 626 | TYPE 627 | 0 628 | 629 | 630 | PROTECTED 631 | 632 | PROXY_NAME 633 | Remove .pbdevelopment files 634 | PROXY_TOOLTIP 635 | Remove ".pbdevelopment" files created by ProjectBuilder or Xcode. 636 | STATE 637 | 638 | 639 | 640 | PATTERNS_ARRAY 641 | 642 | 643 | REGULAR_EXPRESSION 644 | 645 | STRING 646 | CVS 647 | TYPE 648 | 1 649 | 650 | 651 | REGULAR_EXPRESSION 652 | 653 | STRING 654 | .cvsignore 655 | TYPE 656 | 0 657 | 658 | 659 | REGULAR_EXPRESSION 660 | 661 | STRING 662 | .cvspass 663 | TYPE 664 | 0 665 | 666 | 667 | REGULAR_EXPRESSION 668 | 669 | STRING 670 | .svn 671 | TYPE 672 | 1 673 | 674 | 675 | REGULAR_EXPRESSION 676 | 677 | STRING 678 | .git 679 | TYPE 680 | 1 681 | 682 | 683 | REGULAR_EXPRESSION 684 | 685 | STRING 686 | .gitignore 687 | TYPE 688 | 0 689 | 690 | 691 | PROTECTED 692 | 693 | PROXY_NAME 694 | Remove SCM metadata 695 | PROXY_TOOLTIP 696 | Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems. 697 | STATE 698 | 699 | 700 | 701 | PATTERNS_ARRAY 702 | 703 | 704 | REGULAR_EXPRESSION 705 | 706 | STRING 707 | classes.nib 708 | TYPE 709 | 0 710 | 711 | 712 | REGULAR_EXPRESSION 713 | 714 | STRING 715 | designable.db 716 | TYPE 717 | 0 718 | 719 | 720 | REGULAR_EXPRESSION 721 | 722 | STRING 723 | info.nib 724 | TYPE 725 | 0 726 | 727 | 728 | PROTECTED 729 | 730 | PROXY_NAME 731 | Optimize nib files 732 | PROXY_TOOLTIP 733 | Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles. 734 | STATE 735 | 736 | 737 | 738 | PATTERNS_ARRAY 739 | 740 | 741 | REGULAR_EXPRESSION 742 | 743 | STRING 744 | Resources Disabled 745 | TYPE 746 | 1 747 | 748 | 749 | PROTECTED 750 | 751 | PROXY_NAME 752 | Remove Resources Disabled folders 753 | PROXY_TOOLTIP 754 | Remove "Resources Disabled" folders. 755 | STATE 756 | 757 | 758 | 759 | SEPARATOR 760 | 761 | 762 | 763 | NAME 764 | TREZOR Bridge 765 | 766 | 767 | TYPE 768 | 1 769 | VERSION 770 | 1 771 | 772 | 773 | -------------------------------------------------------------------------------- /release/mac/installer/after-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | 6 | # find out which user is running the installation 7 | inst_user=`stat /dev/console | cut -f 5 -d ' '` 8 | 9 | # load the agent file into launchd with correct user 10 | 11 | agent_file=/Library/LaunchAgents/com.bitcointrezor.trezorBridge.trezord.plist 12 | 13 | set +e 14 | sudo -u $inst_user launchctl unload $agent_file 15 | set -e 16 | sudo -u $inst_user launchctl load $agent_file 17 | -------------------------------------------------------------------------------- /release/mac/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | set -x 5 | 6 | cd $(dirname $0) 7 | 8 | TARGET=$1 9 | BUILDDIR=build${TARGET:+-$TARGET} 10 | VERSION=$(cat ../../VERSION) 11 | 12 | cd ../../$BUILDDIR 13 | 14 | # bundle dependencies 15 | 16 | make -C ../vendor/macdylibbundler 17 | 18 | ../vendor/macdylibbundler/dylibbundler \ 19 | -od -b -x trezord -d libs/ \ 20 | -p @executable_path/libs/ 21 | 22 | # strip binary and libs 23 | 24 | x86_64-apple-darwin12-strip trezord 25 | 26 | # fix libs permissions 27 | 28 | chmod a+r libs/* 29 | -------------------------------------------------------------------------------- /release/windows/.gitignore: -------------------------------------------------------------------------------- 1 | authenticode.* 2 | -------------------------------------------------------------------------------- /release/windows/Dockerfile: -------------------------------------------------------------------------------- 1 | # initialize from the image 2 | 3 | FROM fedora:24 4 | 5 | # update package repositories 6 | 7 | RUN dnf update -y 8 | 9 | # install tools 10 | 11 | RUN dnf install -y cmake make wget 12 | RUN dnf install -y osslsigncode mingw32-nsis 13 | 14 | # install dependencies for Windows build 15 | 16 | RUN dnf install -y mingw32-boost-static 17 | RUN dnf install -y mingw32-curl 18 | RUN dnf install -y mingw32-libmicrohttpd 19 | RUN dnf install -y mingw32-winpthreads 20 | RUN dnf install -y mingw32-zlib-static 21 | 22 | # install dependencies from COPR 23 | 24 | RUN wget https://copr-be.cloud.fedoraproject.org/results/prusnak/private/fedora-24-x86_64/00365081-mingw-protobuf/mingw32-protobuf{,-static}-2.6.1-3.fc24.noarch.rpm 25 | RUN dnf install -y mingw32-protobuf{,-static}-2.6.1-3.fc24.noarch.rpm 26 | -------------------------------------------------------------------------------- /release/windows/Makefile: -------------------------------------------------------------------------------- 1 | PLATFORM = windows 2 | BITS = 32 3 | TARGET = win$(BITS) 4 | VOL_MOUNT = -v $(shell pwd)/../..:/trezord-src 5 | IMAGETAG = trezord-build-env-$(PLATFORM) 6 | 7 | all: .package 8 | 9 | .package: .binary 10 | $(info Packaging ...) 11 | docker run -t $(VOL_MOUNT) $(IMAGETAG) /trezord-src/release/$(PLATFORM)/release.sh $(TARGET) 12 | 13 | .binary: .docker-image 14 | $(info Building in docker ...) 15 | docker run -t $(VOL_MOUNT) $(IMAGETAG) /trezord-src/build.sh $(TARGET) 16 | 17 | .docker-image: 18 | $(info Preparing docker image ...) 19 | docker build -t $(IMAGETAG) . 20 | 21 | shell: .docker-image 22 | docker run -i -t $(VOL_MOUNT) $(IMAGETAG) /bin/bash 23 | -------------------------------------------------------------------------------- /release/windows/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | 7 | TARGET=$1 8 | BUILDDIR=build${TARGET:+-$TARGET} 9 | VERSION=$(cat ../../VERSION) 10 | 11 | INSTALLER=trezor-bridge-$VERSION-$TARGET-install.exe 12 | 13 | cd ../../$BUILDDIR 14 | 15 | cp ../release/windows/trezord.nsis trezord.nsis 16 | for i in \ 17 | iconv.dll \ 18 | libcrypto-10.dll \ 19 | libcurl-4.dll \ 20 | libffi-6.dll \ 21 | libgcc_s_sjlj-1.dll \ 22 | libgcrypt-20.dll \ 23 | libgmp-10.dll \ 24 | libgnutls-30.dll \ 25 | libgpg-error-0.dll \ 26 | libhogweed-4-2.dll \ 27 | libidn-11.dll \ 28 | libintl-8.dll \ 29 | libmicrohttpd-12.dll \ 30 | libnettle-6-2.dll \ 31 | libp11-kit-0.dll \ 32 | libssh2-1.dll \ 33 | libssl-10.dll \ 34 | libstdc++-6.dll \ 35 | libtasn1-6.dll \ 36 | libwinpthread-1.dll \ 37 | zlib1.dll \ 38 | ; do 39 | if [ $TARGET = win32 ]; then 40 | cp /usr/i686-w64-mingw32/sys-root/mingw/bin/$i . 41 | else 42 | cp /usr/x86_64-w64-mingw32/sys-root/mingw/bin/$i . 43 | fi 44 | done 45 | 46 | mingw-strip *.dll *.exe 47 | 48 | SIGNKEY=../release/windows/authenticode 49 | 50 | if [ -r $SIGNKEY.der ]; then 51 | mv trezord.exe trezord.exe.unsigned 52 | osslsigncode sign -certs $SIGNKEY.p7b -key $SIGNKEY.der -n "TREZOR Bridge" -i "https://trezor.io/" -t http://timestamp.comodoca.com -in trezord.exe.unsigned -out trezord.exe 53 | osslsigncode verify -in trezord.exe 54 | fi 55 | 56 | if [ $TARGET = win32 ]; then 57 | makensis -X"OutFile $INSTALLER" -X'InstallDir "$PROGRAMFILES32\TREZOR Bridge"' trezord.nsis 58 | else 59 | makensis -X"OutFile $INSTALLER" -X'InstallDir "$PROGRAMFILES64\TREZOR Bridge"' trezord.nsis 60 | fi 61 | 62 | if [ -r $SIGNKEY.der ]; then 63 | mv $INSTALLER $INSTALLER.unsigned 64 | osslsigncode sign -certs $SIGNKEY.p7b -key $SIGNKEY.der -n "TREZOR Bridge" -i "https://trezor.io/" -t http://timestamp.comodoca.com -in $INSTALLER.unsigned -out $INSTALLER 65 | osslsigncode verify -in $INSTALLER 66 | fi 67 | -------------------------------------------------------------------------------- /release/windows/trezord.nsis: -------------------------------------------------------------------------------- 1 | !include MUI2.nsh 2 | 3 | RequestExecutionLevel admin 4 | 5 | SetCompressor bzip2 6 | 7 | Name "TREZOR Bridge" 8 | InstallDirRegKey HKLM Software\TREZOR\Bridge InstallDir 9 | 10 | ShowInstDetails hide 11 | ShowUninstDetails hide 12 | 13 | XPStyle on 14 | 15 | Page directory 16 | Page instfiles 17 | 18 | DirText "Please select the installation folder." 19 | 20 | Section "TREZOR Bridge" 21 | SectionIn RO 22 | 23 | DetailPrint "Stopping previous TREZOR Bridge" 24 | nsExec::Exec "taskkill /IM trezord.exe /F" 25 | 26 | SetOutPath "$INSTDIR" 27 | File "iconv.dll" 28 | File "libcrypto-10.dll" 29 | File "libcurl-4.dll" 30 | File "libffi-6.dll" 31 | File "libgcc_s_sjlj-1.dll" 32 | File "libgcrypt-20.dll" 33 | File "libgmp-10.dll" 34 | File "libgnutls-30.dll" 35 | File "libgpg-error-0.dll" 36 | File "libhogweed-4-2.dll" 37 | File "libidn-11.dll" 38 | File "libintl-8.dll" 39 | File "libmicrohttpd-12.dll" 40 | File "libnettle-6-2.dll" 41 | File "libp11-kit-0.dll" 42 | File "libssh2-1.dll" 43 | File "libssl-10.dll" 44 | File "libstdc++-6.dll" 45 | File "libtasn1-6.dll" 46 | File "libwinpthread-1.dll" 47 | File "zlib1.dll" 48 | File "trezord.exe" 49 | SectionEnd 50 | 51 | Section "Start Menu Shortcuts" 52 | CreateDirectory "$SMPROGRAMS\TREZOR Bridge" 53 | CreateShortCut "$SMPROGRAMS\TREZOR Bridge\Uninstall.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 54 | CreateShortCut "$SMPROGRAMS\TREZOR Bridge\TREZOR Bridge.lnk" "$INSTDIR\trezord.exe" "" "$INSTDIR\trezord.exe" 0 55 | CreateShortCut "$SMSTARTUP\TREZOR Bridge.lnk" "$INSTDIR\trezord.exe" "" "$INSTDIR\trezord.exe" 0 56 | SectionEnd 57 | 58 | Section "Uninstall" 59 | ExecWait "taskkill /f /im trezord.exe" 60 | 61 | Delete /rebootok "$SMSTARTUP\TREZOR Bridge.lnk" 62 | Delete /rebootok "$SMPROGRAMS\TREZOR Bridge\TREZOR Bridge.lnk" 63 | Delete /rebootok "$SMPROGRAMS\TREZOR Bridge\Uninstall.lnk" 64 | RMDir "$SMPROGRAMS\TREZOR Bridge" 65 | 66 | Delete /rebootok "$INSTDIR\trezord.exe" 67 | Delete /rebootok "$INSTDIR\Uninstall.exe" 68 | RMDir "$INSTDIR" 69 | SectionEnd 70 | 71 | Section -post 72 | WriteUninstaller "$INSTDIR\Uninstall.exe" 73 | SectionEnd 74 | 75 | !define MUI_FINISHPAGE_RUN 76 | !define MUI_FINISHPAGE_RUN_TEXT "Start TREZOR Bridge" 77 | !define MUI_FINISHPAGE_RUN_FUNCTION "LaunchApplication" 78 | !insertmacro MUI_PAGE_FINISH 79 | 80 | Function LaunchApplication 81 | ExecShell "" "$INSTDIR\trezord.exe" 82 | FunctionEnd 83 | -------------------------------------------------------------------------------- /src/config/keys.h: -------------------------------------------------------------------------------- 1 | // sample public key "correct horse battery staple" 2 | // "\x04\x78\xd4\x30\x27\x4f\x8c\x5e\xc1\x32\x13\x38\x15\x1e\x9f\x27\xf4\xc6\x76\xa0\x08\xbd\xf8\x63\x8d\x07\xc0\xb6\xbe\x9a\xb3\x5c\x71\xa1\x51\x80\x63\x24\x3a\xcd\x4d\xfe\x96\xb6\x6e\x3f\x2e\xc8\x01\x3c\x8e\x07\x2c\xd0\x9b\x38\x34\xa1\x9f\x81\xf6\x59\xcc\x34\x55" 3 | 4 | // production public keys 5 | "\x04\xd5\x71\xb7\xf1\x48\xc5\xe4\x23\x2c\x38\x14\xf7\x77\xd8\xfa\xea\xf1\xa8\x42\x16\xc7\x8d\x56\x9b\x71\x04\x1f\xfc\x76\x8a\x5b\x2d\x81\x0f\xc3\xbb\x13\x4d\xd0\x26\xb5\x7e\x65\x00\x52\x75\xae\xde\xf4\x3e\x15\x5f\x48\xfc\x11\xa3\x2e\xc7\x90\xa9\x33\x12\xbd\x58", 6 | "\x04\x63\x27\x9c\x0c\x08\x66\xe5\x0c\x05\xc7\x99\xd3\x2b\xd6\xba\xb0\x18\x8b\x6d\xe0\x65\x36\xd1\x10\x9d\x2e\xd9\xce\x76\xcb\x33\x5c\x49\x0e\x55\xae\xe1\x0c\xc9\x01\x21\x51\x32\xe8\x53\x09\x7d\x54\x32\xed\xa0\x6b\x79\x20\x73\xbd\x77\x40\xc9\x4c\xe4\x51\x6c\xb1", 7 | "\x04\x43\xae\xdb\xb6\xf7\xe7\x1c\x56\x3f\x8e\xd2\xef\x64\xec\x99\x81\x48\x25\x19\xe7\xef\x4f\x4a\xa9\x8b\x27\x85\x4e\x8c\x49\x12\x6d\x49\x56\xd3\x00\xab\x45\xfd\xc3\x4c\xd2\x6b\xc8\x71\x0d\xe0\xa3\x1d\xbd\xf6\xde\x74\x35\xfd\x0b\x49\x2b\xe7\x0a\xc7\x5f\xde\x58", 8 | "\x04\x87\x7c\x39\xfd\x7c\x62\x23\x7e\x03\x82\x35\xe9\xc0\x75\xda\xb2\x61\x63\x0f\x78\xee\xb8\xed\xb9\x24\x87\x15\x9f\xff\xed\xfd\xf6\x04\x6c\x6f\x8b\x88\x1f\xa4\x07\xc4\xa4\xce\x6c\x28\xde\x0b\x19\xc1\xf4\xe2\x9f\x1f\xcb\xc5\xa5\x8f\xfd\x14\x32\xa3\xe0\x93\x8a", 9 | "\x04\x73\x84\xc5\x1a\xe8\x1a\xdd\x0a\x52\x3a\xdb\xb1\x86\xc9\x1b\x90\x6f\xfb\x64\xc2\xc7\x65\x80\x2b\xf2\x6d\xbd\x13\xbd\xf1\x2c\x31\x9e\x80\xc2\x21\x3a\x13\x6c\x8e\xe0\x3d\x78\x74\xfd\x22\xb7\x0d\x68\xe7\xde\xe4\x69\xde\xcf\xbb\xb5\x10\xee\x9a\x46\x0c\xda\x45", 10 | -------------------------------------------------------------------------------- /src/core.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #include "protobuf/json_codec.hpp" 21 | #include "protobuf/wire_codec.hpp" 22 | #include "config/config.pb.h" 23 | #include "crypto.hpp" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | namespace trezord 35 | { 36 | namespace core 37 | { 38 | 39 | struct device_kernel 40 | { 41 | using device_path_type = std::string; 42 | 43 | device_path_type device_path; 44 | 45 | device_kernel(device_path_type const &dp) 46 | : device_path{dp} 47 | {} 48 | 49 | void 50 | open() 51 | { 52 | if (device.get() == nullptr) { 53 | CLOG(INFO, "core.device") << "opening: " << device_path; 54 | device.reset(new wire::device{device_path.c_str()}); 55 | } 56 | } 57 | 58 | void 59 | close() 60 | { 61 | CLOG(INFO, "core.device") << "closing: " << device_path; 62 | device.reset(); 63 | } 64 | 65 | void 66 | call(wire::message const &msg_in, wire::message &msg_out) 67 | { 68 | CLOG(INFO, "core.device") << "calling: " << device_path; 69 | if (!device.get()) { 70 | open(); 71 | } 72 | try { 73 | msg_in.write_to(*device); 74 | msg_out.read_from(*device); 75 | } 76 | catch (std::exception const &e) { 77 | CLOG(ERROR, "core.device") << e.what(); 78 | close(); 79 | throw; 80 | } 81 | } 82 | 83 | private: 84 | 85 | std::unique_ptr< wire::device > device; 86 | }; 87 | 88 | struct kernel_config 89 | { 90 | struct invalid_config 91 | : public std::invalid_argument 92 | { using std::invalid_argument::invalid_argument; }; 93 | 94 | Configuration c; 95 | 96 | void 97 | parse_from_signed_string(std::string const &str) 98 | { 99 | auto data = verify_signature(str); 100 | c.ParseFromArray(data.first, data.second); 101 | } 102 | 103 | bool 104 | is_initialized() 105 | { 106 | return c.IsInitialized(); 107 | } 108 | 109 | bool 110 | is_unexpired() 111 | { 112 | auto current_time = std::time(nullptr); 113 | return !c.has_valid_until() || c.valid_until() > current_time; 114 | } 115 | 116 | bool 117 | is_url_allowed(std::string const &url) 118 | { 119 | bool whitelisted = std::any_of( 120 | c.whitelist_urls().begin(), 121 | c.whitelist_urls().end(), 122 | [&] (std::string const &pattern) { 123 | return boost::regex_match(url, boost::regex{pattern}); 124 | }); 125 | 126 | bool blacklisted = std::any_of( 127 | c.blacklist_urls().begin(), 128 | c.blacklist_urls().end(), 129 | [&] (std::string const &pattern) { 130 | return boost::regex_match(url, boost::regex{pattern}); 131 | }); 132 | 133 | return whitelisted && !blacklisted; 134 | } 135 | 136 | std::string 137 | get_debug_string() 138 | { 139 | Configuration c_copy{c}; 140 | c_copy.clear_wire_protocol(); 141 | return c_copy.DebugString(); 142 | } 143 | 144 | private: 145 | 146 | std::pair 147 | verify_signature(std::string const &str) 148 | { 149 | static const std::size_t sig_size = 64; 150 | if (str.size() <= sig_size) { 151 | throw invalid_config{"configuration string is malformed"}; 152 | } 153 | auto sig = reinterpret_cast(str.data()); 154 | auto msg = sig + sig_size; 155 | auto msg_len = str.size() - sig_size; 156 | 157 | static const char *sig_keys[] = { 158 | #include "config/keys.h" 159 | }; 160 | auto keys = reinterpret_cast(sig_keys); 161 | auto keys_len = sizeof(sig_keys) / sizeof(sig_keys[0]); 162 | 163 | if (!crypto::verify_signature(sig, msg, msg_len, keys, keys_len)) { 164 | throw invalid_config{"configuration signature is invalid"}; 165 | } 166 | 167 | return std::make_pair(msg, msg_len); 168 | } 169 | }; 170 | 171 | struct kernel 172 | { 173 | using session_id_type = std::string; 174 | using device_path_type = std::string; 175 | using device_enumeration_type = std::vector< 176 | std::pair 177 | >; 178 | 179 | struct missing_config 180 | : public std::logic_error 181 | { using std::logic_error::logic_error; }; 182 | 183 | struct unknown_session 184 | : public std::invalid_argument 185 | { using std::invalid_argument::invalid_argument; }; 186 | 187 | struct wrong_previous_session 188 | : public std::invalid_argument 189 | { using std::invalid_argument::invalid_argument; }; 190 | 191 | public: 192 | 193 | kernel() 194 | : pb_state{new protobuf::state{}}, 195 | pb_wire_codec{new protobuf::wire_codec{pb_state.get()}}, 196 | pb_json_codec{new protobuf::json_codec{pb_state.get()}} 197 | { 198 | hid::init(); 199 | } 200 | 201 | ~kernel() 202 | { 203 | hid::exit(); 204 | } 205 | 206 | std::string 207 | get_version() 208 | { return VERSION; } 209 | 210 | bool 211 | has_config() 212 | { return config.is_initialized(); } 213 | 214 | kernel_config const & 215 | get_config() 216 | { return config; } 217 | 218 | void 219 | set_config(kernel_config const &new_config) 220 | { 221 | lock_type lock{mutex}; 222 | 223 | config = new_config; 224 | 225 | pb_state.reset(new protobuf::state{}); 226 | pb_state->load_from_set(config.c.wire_protocol()); 227 | 228 | pb_wire_codec.reset(new protobuf::wire_codec{pb_state.get()}); 229 | pb_wire_codec->load_protobuf_state(); 230 | 231 | pb_json_codec.reset(new protobuf::json_codec{pb_state.get()}); 232 | } 233 | 234 | bool 235 | is_allowed(std::string const &url) 236 | { 237 | lock_type lock{mutex}; 238 | 239 | if (!has_config()) { 240 | return true; 241 | } 242 | 243 | return config.is_unexpired() && config.is_url_allowed(url); 244 | } 245 | 246 | // device enumeration 247 | 248 | session_id_type 249 | find_session_by_path(device_path_type const &path) { 250 | auto it = sessions.find(path); 251 | if (it != sessions.end()) { 252 | return it->second; 253 | } else { 254 | return ""; 255 | } 256 | } 257 | 258 | device_enumeration_type 259 | enumerate_devices() 260 | { 261 | lock_type lock{mutex}; 262 | 263 | if (!has_config()) { 264 | throw missing_config{"not configured"}; 265 | } 266 | 267 | device_enumeration_type list; 268 | 269 | for (auto const &i: enumerate_supported_devices()) { 270 | auto session_id = find_session_by_path(i.path); 271 | list.emplace_back(i, session_id); 272 | } 273 | 274 | return list; 275 | } 276 | 277 | // device kernels 278 | 279 | device_kernel * 280 | get_device_kernel(device_path_type const &device_path) 281 | { 282 | lock_type lock{mutex}; 283 | 284 | if (!has_config()) { 285 | throw missing_config{"not configured"}; 286 | } 287 | 288 | auto kernel_r = device_kernels.emplace( 289 | std::piecewise_construct, 290 | std::forward_as_tuple(device_path), 291 | std::forward_as_tuple(device_path)); 292 | 293 | return &kernel_r.first->second; 294 | } 295 | 296 | device_kernel * 297 | get_device_kernel_by_session_id(session_id_type const &session_id) 298 | { 299 | lock_type lock{mutex}; 300 | 301 | if (!has_config()) { 302 | throw missing_config{"not configured"}; 303 | } 304 | 305 | auto session_it = std::find_if( 306 | sessions.begin(), 307 | sessions.end(), 308 | [&] (decltype(sessions)::value_type const &kv) { 309 | return kv.second == session_id; 310 | }); 311 | 312 | if (session_it == sessions.end()) { 313 | throw unknown_session{"session not found"}; 314 | } 315 | 316 | return get_device_kernel(session_it->first); 317 | } 318 | 319 | // session management 320 | 321 | session_id_type 322 | acquire_session(device_path_type const &device_path) 323 | { 324 | lock_type lock{mutex}; 325 | 326 | if (!has_config()) { 327 | throw missing_config{"not configured"}; 328 | } 329 | 330 | CLOG(INFO, "core.kernel") << "acquiring session for: " << device_path; 331 | return sessions[device_path] = generate_session_id(); 332 | } 333 | 334 | void 335 | release_session(session_id_type const &session_id) 336 | { 337 | lock_type lock{mutex}; 338 | 339 | if (!has_config()) { 340 | throw missing_config{"not configured"}; 341 | } 342 | 343 | auto session_it = std::find_if( 344 | sessions.begin(), 345 | sessions.end(), 346 | [&] (decltype(sessions)::value_type const &kv) { 347 | return kv.second == session_id; 348 | }); 349 | 350 | if (session_it != sessions.end()) { 351 | CLOG(INFO, "core.kernel") << "releasing session: " << session_id; 352 | sessions.erase(session_it); 353 | } 354 | } 355 | 356 | 357 | session_id_type 358 | open_and_acquire_session(device_path_type const &device_path, 359 | session_id_type const &previous_id, 360 | bool check_previous) 361 | { 362 | lock_type lock{mutex}; 363 | 364 | auto real_previous_id = find_session_by_path(device_path); 365 | if (check_previous && real_previous_id != previous_id) { 366 | CLOG(INFO, "core.kernel") << "not acquiring session for: " << device_path << " , wrong previous"; 367 | throw wrong_previous_session{"wrong previous session"}; 368 | } 369 | if (!(real_previous_id.empty())) { 370 | close_and_release_session(real_previous_id); 371 | } 372 | 373 | get_device_kernel(device_path)->open(); 374 | return acquire_session(device_path); 375 | } 376 | 377 | void 378 | close_and_release_session(session_id_type const &session_id) 379 | { 380 | lock_type lock{mutex}; 381 | get_device_kernel_by_session_id(session_id)->close(); 382 | release_session(session_id); 383 | } 384 | 385 | void 386 | call_device(device_kernel *device, wire::message const &msg_in, wire::message &msg_out) 387 | { 388 | lock_type lock{mutex}; 389 | device->call(msg_in, msg_out); 390 | } 391 | 392 | // protobuf <-> json codec 393 | 394 | void 395 | json_to_wire(Json::Value const &json, wire::message &wire) 396 | { 397 | lock_type lock{mutex}; 398 | protobuf_ptr pbuf{pb_json_codec->typed_json_to_protobuf(json)}; 399 | pb_wire_codec->protobuf_to_wire(*pbuf, wire); 400 | } 401 | 402 | void 403 | wire_to_json(wire::message const &wire, Json::Value &json) 404 | { 405 | lock_type lock{mutex}; 406 | protobuf_ptr pbuf{pb_wire_codec->wire_to_protobuf(wire)}; 407 | json = pb_json_codec->protobuf_to_typed_json(*pbuf); 408 | } 409 | 410 | private: 411 | 412 | using protobuf_ptr = std::unique_ptr; 413 | using lock_type = boost::unique_lock; 414 | 415 | boost::recursive_mutex mutex; 416 | 417 | kernel_config config; 418 | std::unique_ptr pb_state; 419 | std::unique_ptr pb_wire_codec; 420 | std::unique_ptr pb_json_codec; 421 | 422 | std::map device_kernels; 423 | std::map sessions; 424 | boost::uuids::random_generator uuid_generator; 425 | 426 | session_id_type 427 | generate_session_id() 428 | { 429 | return boost::lexical_cast(uuid_generator()); 430 | } 431 | 432 | wire::device_info_list 433 | enumerate_supported_devices() 434 | { 435 | return wire::enumerate_connected_devices( 436 | [&] (hid_device_info const *i) { 437 | return is_device_supported(i); 438 | }); 439 | } 440 | 441 | bool 442 | is_device_supported(hid_device_info const *info) 443 | { 444 | return std::any_of( 445 | config.c.known_devices().begin(), 446 | config.c.known_devices().end(), 447 | [&] (DeviceDescriptor const &dd) { 448 | return (!dd.has_vendor_id() 449 | || dd.vendor_id() == info->vendor_id) 450 | && (!dd.has_product_id() 451 | || dd.product_id() == info->product_id); 452 | }); 453 | } 454 | }; 455 | 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /src/crypto.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #ifdef _MSC_VER // trezor-crypto gets compiled as C++ on MSVC 21 | extern "C++" { 22 | #else 23 | extern "C" { 24 | #endif 25 | #include 26 | #include 27 | } 28 | 29 | namespace trezord 30 | { 31 | namespace crypto 32 | { 33 | 34 | bool 35 | verify_signature(std::uint8_t const *sig, 36 | std::uint8_t const *msg, 37 | std::size_t msg_len, 38 | std::uint8_t const **keys, 39 | std::size_t keys_len) 40 | { 41 | for (std::size_t i = 0; i < keys_len; i++) { 42 | int ret = ecdsa_verify(&secp256k1, keys[i], sig, msg, msg_len); 43 | if (ret == 0) { 44 | return true; 45 | } 46 | } 47 | return false; 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/glibc_compat.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __amd64__ 4 | #define GLIBC_COMPAT_SYMBOL(X) __asm__(".symver __" #X "_compat," #X "@GLIBC_2.2.5"); 5 | #else 6 | #define GLIBC_COMPAT_SYMBOL(X) __asm__(".symver __" #X "_compat," #X "@GLIBC_2.0"); 7 | #endif 8 | 9 | // memcpy 10 | 11 | GLIBC_COMPAT_SYMBOL(memcpy) 12 | 13 | void *__memcpy_compat(void *, const void *, size_t); 14 | 15 | void *__wrap_memcpy(void *dest, const void *src, size_t n) 16 | { 17 | return __memcpy_compat(dest, src, n); 18 | } 19 | 20 | // secure_getenv 21 | 22 | GLIBC_COMPAT_SYMBOL(__secure_getenv) 23 | 24 | char *____secure_getenv_compat(const char *); 25 | 26 | char *__wrap_secure_getenv(const char *name) 27 | { 28 | return ____secure_getenv_compat(name); 29 | } 30 | -------------------------------------------------------------------------------- /src/hid.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | namespace trezord 23 | { 24 | namespace hid 25 | { 26 | 27 | static std::unique_ptr< utils::async_executor > hid_executor; 28 | 29 | // Init/exit 30 | 31 | void 32 | init() 33 | { 34 | hid_init(); 35 | hid_executor.reset(new utils::async_executor()); 36 | } 37 | 38 | void 39 | exit() 40 | { 41 | hid_exit(); 42 | hid_executor.reset(); 43 | } 44 | 45 | // Enumeration 46 | 47 | hid_device_info * 48 | enumerate(unsigned short vendor_id, unsigned short product_id) 49 | { 50 | return hid_executor->await([=] { 51 | return hid_enumerate(vendor_id, product_id); 52 | }); 53 | } 54 | 55 | void 56 | free_enumeration(hid_device_info *devs) 57 | { 58 | return hid_executor->await([=] { 59 | return hid_free_enumeration(devs); 60 | }); 61 | } 62 | 63 | // Open/close 64 | 65 | hid_device * 66 | open_path(char const *path) 67 | { 68 | return hid_executor->await([=] { return hid_open_path(path); }); 69 | } 70 | 71 | void 72 | close(hid_device *device) 73 | { 74 | return hid_executor->await([=] { return hid_close(device); }); 75 | } 76 | 77 | // Communication 78 | 79 | int 80 | write(hid_device *device, unsigned char const *data, size_t length) 81 | { 82 | return hid_executor->await([=] { 83 | return hid_write(device, data, length); 84 | }); 85 | } 86 | 87 | int 88 | read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds) 89 | { 90 | return hid_executor->await([=] { 91 | return hid_read_timeout(device, data, length, milliseconds); 92 | }); 93 | } 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/http_api.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace trezord 30 | { 31 | namespace http_api 32 | { 33 | 34 | /** 35 | * JSON support 36 | */ 37 | 38 | using json_pair = std::pair; 39 | using json_list = std::initializer_list; 40 | 41 | Json::Value 42 | json_value(json_list const &list) 43 | { 44 | Json::Value json{Json::objectValue}; 45 | for (auto const &kv: list) { 46 | json[kv.first] = kv.second; 47 | } 48 | return json; 49 | } 50 | 51 | http_server::response_data 52 | json_response(int status, Json::Value const &body) 53 | { 54 | http_server::response_data response{status, body.toStyledString()}; 55 | response.add_header("Content-Type", "application/json"); 56 | return response; 57 | } 58 | 59 | http_server::response_data 60 | json_response(int status, json_list const &list) 61 | { 62 | return json_response(status, json_value(list)); 63 | } 64 | 65 | /** 66 | * Generic error support 67 | */ 68 | 69 | struct response_error : public std::runtime_error 70 | { 71 | int status_code; 72 | 73 | response_error(int status, char const *message) 74 | : status_code{status}, 75 | runtime_error{message} 76 | { } 77 | }; 78 | 79 | http_server::response_data 80 | json_error_response(std::exception_ptr eptr) 81 | { 82 | static const auto default_status_code = 500; 83 | static const auto default_message = "internal error"; 84 | 85 | try { 86 | if (eptr) { 87 | std::rethrow_exception(eptr); 88 | } else { 89 | throw std::runtime_error{default_message}; 90 | } 91 | } 92 | catch (response_error const &e) { 93 | return json_response(e.status_code, {{"error", e.what()}}); 94 | } 95 | catch (std::exception const &e) { 96 | return json_response(default_status_code, {{"error", e.what()}}); 97 | } 98 | catch (...) { 99 | return json_response(default_status_code, {{"error", default_message}}); 100 | } 101 | } 102 | 103 | /** 104 | * Device types encoding/decoding 105 | */ 106 | 107 | core::device_kernel::device_path_type 108 | decode_device_path(std::string const &hex) 109 | { 110 | return utils::hex_decode(hex); 111 | } 112 | 113 | std::string 114 | encode_device_path(core::device_kernel::device_path_type const &path) 115 | { 116 | return utils::hex_encode(path); 117 | } 118 | 119 | Json::Value 120 | devices_to_json(core::kernel::device_enumeration_type const &devices) 121 | { 122 | Json::Value nil; 123 | Json::Value item{Json::objectValue}; 124 | Json::Value list{Json::arrayValue}; 125 | 126 | for (auto const &d: devices) { 127 | auto const &i = d.first; 128 | auto const &s = d.second; 129 | 130 | item["path"] = encode_device_path(i.path); 131 | item["vendor"] = i.vendor_id; 132 | item["product"] = i.product_id; 133 | item["session"] = s.empty() ? nil : s; 134 | list.append(item); 135 | } 136 | 137 | return list; 138 | } 139 | 140 | const core::kernel::device_enumeration_type 141 | json_to_devices(std::string const &json_body) 142 | { 143 | Json::Value json_message; 144 | Json::Reader json_reader; 145 | json_reader.parse(json_body, json_message); 146 | core::kernel::device_enumeration_type list; 147 | for (auto const &item: json_message) { 148 | auto path = decode_device_path(item["path"].asString()); 149 | auto vendor = static_cast(item["vendor"].asUInt()); 150 | auto product = static_cast(item["product"].asUInt()); 151 | 152 | auto json_session = item["session"]; 153 | auto session = json_session.isNull() ? "" : json_session.asString(); 154 | 155 | auto device_info = wire::device_info{ 156 | vendor, 157 | product, 158 | path}; 159 | 160 | list.emplace_back(device_info, session); 161 | } 162 | return list; 163 | } 164 | 165 | /** 166 | * Request handlers 167 | */ 168 | 169 | struct handler 170 | { 171 | std::unique_ptr kernel; 172 | 173 | bool 174 | is_origin_allowed(std::string const &origin) 175 | { 176 | return kernel->is_allowed(origin); 177 | } 178 | 179 | http_server::response_data 180 | handle_404(http_server::request_data const &request) 181 | { 182 | return http_server::response_data{404, "Not Found"}; 183 | } 184 | 185 | http_server::response_data 186 | handle_index(http_server::request_data const &request) 187 | { 188 | try { 189 | Json::Value nil; 190 | 191 | auto version = kernel->get_version(); 192 | auto configured = kernel->has_config(); 193 | auto valid_until = kernel->get_config().c.has_valid_until() 194 | ? kernel->get_config().c.valid_until() 195 | : nil; 196 | 197 | return json_response(200, { 198 | {"version", version}, 199 | {"configured", configured}, 200 | {"validUntil", valid_until} 201 | }); 202 | } 203 | catch (...) { 204 | return json_error_response(std::current_exception()); 205 | } 206 | } 207 | 208 | http_server::response_data 209 | handle_configure(http_server::request_data const &request) 210 | { 211 | try { 212 | auto body = request.body.str(); 213 | auto origin = request.get_header("Origin"); 214 | 215 | core::kernel_config config; 216 | 217 | try { 218 | config.parse_from_signed_string(utils::hex_decode(body)); 219 | LOG(INFO) 220 | << "parsed configuration: \n" 221 | << config.get_debug_string(); 222 | } 223 | catch (core::kernel_config::invalid_config const &e) { 224 | throw response_error{400, e.what()}; 225 | } 226 | 227 | if (!config.is_initialized()) { 228 | throw response_error{400, "configuration is incomplete"}; 229 | } 230 | if (!config.is_unexpired()) { 231 | throw response_error{400, "configuration is expired"}; 232 | } 233 | if (origin && !config.is_url_allowed(origin)) { 234 | throw response_error{400, "origin not allowed"}; 235 | } 236 | 237 | kernel->set_config(config); 238 | return json_response(200, {}); 239 | } 240 | catch (...) { 241 | return json_error_response(std::current_exception()); 242 | } 243 | } 244 | 245 | http_server::response_data 246 | handle_listen(http_server::request_data const &request) 247 | { 248 | static const auto iter_max = 60; 249 | static const auto iter_delay = boost::posix_time::milliseconds(500); 250 | 251 | try { 252 | auto body = request.body.str(); 253 | 254 | auto devices = body.empty() ? kernel->enumerate_devices() : json_to_devices(body); 255 | 256 | for (int i = 0; i < iter_max; i++) { 257 | auto updated_devices = kernel->enumerate_devices(); 258 | 259 | if (updated_devices == devices) { 260 | boost::this_thread::sleep(iter_delay); 261 | } 262 | else { 263 | devices = updated_devices; 264 | break; 265 | } 266 | } 267 | 268 | return json_response(200, devices_to_json(devices)); 269 | } 270 | catch (...) { 271 | return json_error_response(std::current_exception()); 272 | } 273 | } 274 | 275 | http_server::response_data 276 | handle_enumerate(http_server::request_data const &request) 277 | { 278 | try { 279 | auto devices = kernel->enumerate_devices(); 280 | return json_response(200, devices_to_json(devices)); 281 | } 282 | catch (...) { 283 | return json_error_response(std::current_exception()); 284 | } 285 | } 286 | 287 | http_server::response_data 288 | handle_acquire(http_server::request_data const &request) 289 | { 290 | try { 291 | auto device_path = decode_device_path(request.url_params.str(1)); 292 | 293 | // slight hack to keep the types correct 294 | auto check_previous = request.url_params.size() > 2; 295 | auto previous_or_null = check_previous ? request.url_params.str(2) : "null"; 296 | auto previous = previous_or_null == "null" ? "" : previous_or_null; 297 | 298 | auto session_id = kernel->open_and_acquire_session(device_path, previous, check_previous); 299 | return json_response(200, {{"session", session_id}}); 300 | } 301 | catch (...) { 302 | return json_error_response(std::current_exception()); 303 | } 304 | } 305 | 306 | http_server::response_data 307 | handle_release(http_server::request_data const &request) 308 | { 309 | try { 310 | auto session_id = request.url_params.str(1); 311 | try { 312 | kernel->close_and_release_session(session_id); 313 | } 314 | catch (core::kernel::unknown_session const &e) { 315 | throw response_error{404, e.what()}; 316 | } 317 | return json_response(200, {}); 318 | } 319 | catch (...) { 320 | return json_error_response(std::current_exception()); 321 | } 322 | } 323 | 324 | http_server::response_data 325 | handle_call(http_server::request_data const &request) 326 | { 327 | try { 328 | auto session_id = request.url_params.str(1); 329 | auto body = request.body.str(); 330 | 331 | Json::Value json_message; 332 | Json::Reader json_reader; 333 | json_reader.parse(body, json_message); 334 | 335 | wire::message wire_in; 336 | wire::message wire_out; 337 | 338 | core::device_kernel *device; 339 | try { 340 | device = kernel->get_device_kernel_by_session_id(session_id); 341 | } 342 | catch (core::kernel::unknown_session const &e) { 343 | throw response_error{404, e.what()}; 344 | } 345 | 346 | kernel->json_to_wire(json_message, wire_in); 347 | kernel->call_device(device, wire_in, wire_out); 348 | kernel->wire_to_json(wire_out, json_message); 349 | 350 | return json_response(200, json_message); 351 | } 352 | catch (...) { 353 | return json_error_response(std::current_exception()); 354 | } 355 | } 356 | }; 357 | 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/http_client.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace trezord 27 | { 28 | namespace http_client 29 | { 30 | 31 | std::size_t 32 | write_to_stream(void *data, 33 | std::size_t size, 34 | std::size_t nmemb, 35 | std::stringstream *stream) 36 | { 37 | stream->write(static_cast(data), size * nmemb); 38 | return size * nmemb; 39 | } 40 | 41 | void 42 | request_uri_to_stream(std::string const &uri, 43 | std::stringstream *stream) 44 | { 45 | CURL *curl = curl_easy_init(); 46 | if (!curl) { 47 | throw std::runtime_error{"CURL init failed"}; 48 | } 49 | 50 | CLOG(INFO, "http.client") << "requesting " << uri; 51 | 52 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); 53 | curl_easy_setopt(curl, CURLOPT_URL, uri.c_str()); 54 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_stream); 55 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream); 56 | 57 | CURLcode res = curl_easy_perform(curl); 58 | curl_easy_cleanup(curl); 59 | if (res != CURLE_OK) { 60 | throw std::runtime_error{curl_easy_strerror(res)}; 61 | } 62 | } 63 | 64 | std::string 65 | request_uri_to_string(std::string const &uri) 66 | { 67 | std::stringstream stream; 68 | request_uri_to_stream(uri, &stream); 69 | return stream.str(); 70 | } 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/http_server.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | #ifdef _WIN32 23 | #include 24 | #include 25 | #else 26 | #include 27 | #endif 28 | 29 | namespace trezord 30 | { 31 | namespace http_server 32 | { 33 | 34 | struct request_data 35 | { 36 | MHD_Connection *connection; 37 | std::string url; 38 | std::string method; 39 | std::stringstream body; 40 | boost::smatch url_params; 41 | 42 | char const * 43 | get_header(char const *name) const 44 | { 45 | return MHD_lookup_connection_value( 46 | connection, MHD_HEADER_KIND, name); 47 | } 48 | }; 49 | 50 | struct response_data 51 | { 52 | using ptr = std::unique_ptr< 53 | MHD_Response, decltype(&MHD_destroy_response)>; 54 | 55 | int status_code; 56 | ptr response; 57 | 58 | response_data(int status, std::string const &body) 59 | : status_code{status}, 60 | response{mhd_response_from_string(body.c_str()), &MHD_destroy_response} 61 | { } 62 | 63 | response_data(int status, char const *body) 64 | : status_code{status}, 65 | response{mhd_response_from_string(body), &MHD_destroy_response} 66 | { } 67 | 68 | int 69 | add_header(char const *name, char const *value) 70 | { 71 | if (name && value) { 72 | return MHD_add_response_header(response.get(), name, value); 73 | } 74 | else { 75 | return MHD_NO; 76 | } 77 | } 78 | 79 | int 80 | respond_to(request_data *request) 81 | { 82 | if (request) { 83 | return MHD_queue_response( 84 | request->connection, status_code, response.get()); 85 | } 86 | else { 87 | return MHD_NO; 88 | } 89 | } 90 | 91 | private: 92 | 93 | static 94 | MHD_Response * 95 | mhd_response_from_string(char const *body) 96 | { 97 | // MHD_create_response_from_buffer has many modes of operation, 98 | // but the buffer for MHD_RESPMEM_MUST_COPY mode is effectively 99 | // const char *, so this conversion is safe 100 | auto body_buffer = static_cast(const_cast(body)); 101 | 102 | return MHD_create_response_from_buffer( 103 | std::strlen(body), body_buffer, MHD_RESPMEM_MUST_COPY); 104 | } 105 | }; 106 | 107 | using request_handler = std::function; 108 | 109 | struct regex_route 110 | { 111 | boost::regex method; 112 | boost::regex url; 113 | 114 | regex_route(char const *method_pattern, 115 | char const *url_pattern) 116 | : method{method_pattern}, 117 | url{url_pattern} 118 | { } 119 | 120 | bool 121 | match_request(request_data *request) const 122 | { 123 | return (boost::regex_match(request->method, method)) 124 | && (boost::regex_match(request->url, request->url_params, url)); 125 | } 126 | }; 127 | 128 | using route_entry = std::pair; 129 | using route_table = std::vector; 130 | using cors_validator = std::function; 131 | 132 | response_data 133 | handle_cors_and_delegate(cors_validator validator, 134 | request_handler handler, 135 | request_data const &request) 136 | { 137 | auto origin = request.get_header("Origin"); 138 | 139 | if (!origin) { 140 | // not a cors request, delegate 141 | return handler(request); 142 | } 143 | 144 | if (!validator(origin)) { 145 | // origin is not allowed, forbid further processing 146 | response_data response{403, "Origin Not Allowed"}; 147 | return response; 148 | } 149 | 150 | if (request.method == "OPTIONS") { 151 | // allowed pre-flight request, thumbs up 152 | response_data response{200, "Enjoy Your Flight"}; 153 | auto req_method = request.get_header("Access-Control-Request-Method"); 154 | auto req_headers = request.get_header("Access-Control-Request-Headers"); 155 | response.add_header("Access-Control-Allow-Methods", req_method); 156 | response.add_header("Access-Control-Allow-Headers", req_headers); 157 | response.add_header("Access-Control-Allow-Origin", origin); 158 | return response; 159 | } 160 | else { 161 | // allowed ordinary request, delegate 162 | response_data response = handler(request); 163 | response.add_header("Access-Control-Allow-Origin", origin); 164 | return response; 165 | } 166 | } 167 | 168 | struct server 169 | { 170 | route_table const &routes; 171 | cors_validator validator; 172 | 173 | server(route_table const &table, cors_validator cors) 174 | : routes(table), 175 | validator{cors} 176 | { } 177 | 178 | ~server() { stop(); } 179 | 180 | void 181 | start(unsigned int port, 182 | char const *address, 183 | char const *key, 184 | char const *cert) 185 | { 186 | sockaddr_in addr; 187 | 188 | addr.sin_family = AF_INET; 189 | addr.sin_port = htons(port); 190 | inet_pton(AF_INET, address, &addr.sin_addr); 191 | 192 | MHD_set_panic_func(&server::panic_callback, this); 193 | 194 | daemon = MHD_start_daemon( 195 | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL, 196 | port, 197 | nullptr, nullptr, 198 | &server::request_callback, this, 199 | MHD_OPTION_SOCK_ADDR, &addr, 200 | MHD_OPTION_HTTPS_MEM_KEY, key, 201 | MHD_OPTION_HTTPS_MEM_CERT, cert, 202 | MHD_OPTION_NOTIFY_COMPLETED, &server::completed_callback, this, 203 | MHD_OPTION_EXTERNAL_LOGGER, &server::log_callback, this, 204 | MHD_OPTION_CONNECTION_TIMEOUT, 0, 205 | MHD_OPTION_END); 206 | 207 | if (daemon) { 208 | CLOG(INFO, "http.server") 209 | << "listening at https://" << address << ":" << port; 210 | } 211 | else { 212 | throw std::runtime_error{"failed to start server"}; 213 | } 214 | } 215 | 216 | void 217 | stop() 218 | { 219 | if (daemon) { 220 | MHD_stop_daemon(daemon); 221 | daemon = nullptr; 222 | } 223 | } 224 | 225 | private: 226 | 227 | MHD_Daemon *daemon = nullptr; 228 | 229 | static 230 | int 231 | request_callback(void *cls, 232 | MHD_Connection *connection, 233 | char const *url, 234 | char const *method, 235 | char const *version, 236 | char const *upload_data, 237 | std::size_t *upload_data_size, 238 | void **con_cls) 239 | { 240 | server *self = static_cast(cls); 241 | request_data *request = static_cast(*con_cls); 242 | 243 | if (!request) { 244 | request = new request_data{ 245 | connection, 246 | url, 247 | method, 248 | std::stringstream(), 249 | boost::smatch() 250 | }; 251 | *con_cls = request; 252 | return MHD_YES; 253 | } 254 | 255 | try { 256 | // buffer body data 257 | 258 | if (*upload_data_size > 0) { 259 | request->body.write(upload_data, *upload_data_size); 260 | *upload_data_size = 0; 261 | return MHD_YES; 262 | } 263 | 264 | CLOG(INFO, "http.server") << "<- " << method << " " << url; 265 | 266 | // find matching request handler 267 | 268 | request_handler handler = nullptr; 269 | 270 | for (auto const &r : self->routes) { 271 | if (r.first.match_request(request)) { 272 | handler = r.second; 273 | break; 274 | } 275 | } 276 | 277 | // handle the request 278 | 279 | if (handler) { 280 | auto response = handle_cors_and_delegate( 281 | self->validator, handler, *request); 282 | CLOG(INFO, "http.server") << "-> " << response.status_code; 283 | return response.respond_to(request); 284 | } 285 | else { 286 | return MHD_NO; 287 | } 288 | } 289 | catch (...) { 290 | return MHD_NO; 291 | } 292 | } 293 | 294 | static 295 | void 296 | completed_callback(void *cls, 297 | MHD_Connection *connection, 298 | void **con_cls, 299 | MHD_RequestTerminationCode toe) 300 | { 301 | request_data *request = static_cast(*con_cls); 302 | 303 | if (request) { 304 | delete request; 305 | } 306 | } 307 | 308 | static 309 | void 310 | panic_callback(void *cls, char const *file, unsigned int line, char const *reason) 311 | { 312 | CLOG(FATAL, "http.server") << file << ":" << line << ": " << reason; 313 | } 314 | 315 | static 316 | void 317 | log_callback(void *cls, char const *fm, va_list ap) 318 | { 319 | char message[4096]; 320 | std::vsnprintf(message, sizeof(message), fm, ap); 321 | CLOG(INFO, "http.server") << message; 322 | } 323 | }; 324 | 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #ifdef __linux__ 21 | #include 22 | #endif 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #define _ELPP_THREAD_SAFE 1 30 | #define _ELPP_FORCE_USE_STD_THREAD 1 31 | #define _ELPP_NO_DEFAULT_LOG_FILE 32 | 33 | #include 34 | 35 | #include "utils.hpp" 36 | #include "hid.hpp" 37 | #include "wire.hpp" 38 | #include "core.hpp" 39 | #include "http_client.hpp" 40 | #include "http_server.hpp" 41 | #include "http_api.hpp" 42 | 43 | _INITIALIZE_EASYLOGGINGPP 44 | 45 | static const auto server_port = 21324; 46 | static const auto server_address = "127.0.0.1"; 47 | 48 | static const auto https_cert_uri = "https://wallet.trezor.io/data/bridge/cert/localback.crt"; 49 | static const auto https_privkey_uri = "https://wallet.trezor.io/data/bridge/cert/localback.key"; 50 | 51 | static const auto sleep_time = boost::chrono::seconds(10); 52 | 53 | std::string 54 | get_default_log_path() 55 | { 56 | #ifdef _WIN32 57 | if (auto app_data = std::getenv("APPDATA")) { 58 | return std::string{app_data} + "\\TREZOR Bridge\\trezord.log"; 59 | } 60 | else { 61 | throw std::runtime_error{"environment variable APPDATA not found"}; 62 | } 63 | #elif __APPLE__ 64 | if (auto home = std::getenv("HOME")) { 65 | return std::string{home} + "/Library/Logs/trezord.log"; 66 | } 67 | else { 68 | throw std::runtime_error{"environment variable HOME not found"}; 69 | } 70 | #else 71 | return "/var/log/trezord.log"; 72 | #endif 73 | } 74 | 75 | void 76 | configure_logging() 77 | { 78 | const auto default_log_path = get_default_log_path(); 79 | const auto default_format = "%datetime %level [%logger] [%thread] %msg"; 80 | const auto max_log_file_size = "2097152"; // 2mb, log gets truncated after that 81 | 82 | el::Configurations cfg; 83 | cfg.setToDefault(); 84 | cfg.setGlobally(el::ConfigurationType::MaxLogFileSize, max_log_file_size); 85 | cfg.setGlobally(el::ConfigurationType::Filename, default_log_path); 86 | cfg.setGlobally(el::ConfigurationType::Format, default_format); 87 | cfg.set(el::Level::Debug, el::ConfigurationType::Format, default_format); 88 | cfg.set(el::Level::Trace, el::ConfigurationType::Format, default_format); 89 | cfg.set(el::Level::Verbose, el::ConfigurationType::Format, default_format); 90 | 91 | // we need to explicitly construct all loggers used in the app 92 | // easylogging++ prints warnings if we dont 93 | el::Loggers::getLogger("http.client"); 94 | el::Loggers::getLogger("http.server"); 95 | el::Loggers::getLogger("core.device"); 96 | el::Loggers::getLogger("core.config"); 97 | el::Loggers::getLogger("core.kernel"); 98 | el::Loggers::getLogger("wire.enumerate"); 99 | 100 | // configure all created loggers 101 | el::Loggers::reconfigureAllLoggers(cfg); 102 | } 103 | 104 | void 105 | start_server(std::string const &cert_data, 106 | std::string const &privkey_data, 107 | std::string const &address, 108 | unsigned int port) 109 | { 110 | using namespace trezord; 111 | 112 | using std::bind; 113 | using std::placeholders::_1; 114 | using http_api::handler; 115 | 116 | http_api::handler api_handler{ 117 | std::unique_ptr{new core::kernel}}; 118 | http_server::route_table api_routes = { 119 | {{"GET", "/"}, bind(&handler::handle_index, &api_handler, _1) }, 120 | {{"GET", "/listen"}, bind(&handler::handle_listen, &api_handler, _1) }, 121 | {{"GET", "/enumerate"}, bind(&handler::handle_enumerate, &api_handler, _1) }, 122 | {{"POST", "/listen"}, bind(&handler::handle_listen, &api_handler, _1) }, 123 | {{"POST", "/configure"}, bind(&handler::handle_configure, &api_handler, _1) }, 124 | {{"POST", "/acquire/([^/]+)"}, bind(&handler::handle_acquire, &api_handler, _1) }, 125 | {{"POST", "/acquire/([^/]+)/([^/]+)"}, bind(&handler::handle_acquire, &api_handler, _1) }, 126 | {{"POST", "/release/(.+)"}, bind(&handler::handle_release, &api_handler, _1) }, 127 | {{"POST", "/call/(.+)"}, bind(&handler::handle_call, &api_handler, _1) }, 128 | {{".*", ".*"}, bind(&handler::handle_404, &api_handler, _1) } 129 | }; 130 | http_server::server server{api_routes, [&] (char const *origin) { 131 | return api_handler.is_origin_allowed(origin); 132 | }}; 133 | 134 | server.start(port, address.c_str(), privkey_data.c_str(), cert_data.c_str()); 135 | for (;;) { 136 | boost::this_thread::sleep_for(sleep_time); 137 | } 138 | server.stop(); 139 | } 140 | 141 | int 142 | main(int argc, char *argv[]) 143 | { 144 | configure_logging(); 145 | 146 | if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { 147 | LOG(ERROR) << "could not init curl"; 148 | return 1; 149 | } 150 | 151 | namespace po = boost::program_options; 152 | po::options_description desc("Options"); 153 | desc.add_options() 154 | ("foreground,f", "run in foreground, don't fork into background") 155 | ("help,h", "produce help message") 156 | ; 157 | po::variables_map vm; 158 | po::store(po::parse_command_line(argc, argv, desc), vm); 159 | po::notify(vm); 160 | 161 | if (vm.count("help")) { 162 | std::cout << desc << "\n"; 163 | return 1; 164 | } 165 | 166 | #if defined(__linux__) || defined(__FreeBSD__) 167 | if (!vm.count("foreground")) { 168 | if (daemon(0, 0) < 0) { 169 | LOG(ERROR) << "could not daemonize"; 170 | return 1; 171 | } 172 | } 173 | #endif 174 | 175 | std::string cert_data; 176 | std::string privkey_data; 177 | 178 | start_fetch: 179 | try { 180 | using namespace trezord; 181 | cert_data = http_client::request_uri_to_string(https_cert_uri); 182 | privkey_data = http_client::request_uri_to_string(https_privkey_uri); 183 | } 184 | catch (std::exception const &e) { 185 | LOG(ERROR) << e.what(); 186 | LOG(INFO) << "sleeping for " << sleep_time.count() << "s"; 187 | boost::this_thread::sleep_for(sleep_time); 188 | goto start_fetch; 189 | } 190 | 191 | start_serve: 192 | try { 193 | start_server(cert_data, 194 | privkey_data, 195 | server_address, 196 | server_port); 197 | } 198 | catch (std::exception const &e) { 199 | LOG(ERROR) << e.what(); 200 | LOG(INFO) << "sleeping for " << sleep_time.count() << "s"; 201 | boost::this_thread::sleep_for(sleep_time); 202 | goto start_serve; 203 | } 204 | 205 | return 1; 206 | } 207 | -------------------------------------------------------------------------------- /src/protobuf/json_codec.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "protobuf/state.hpp" 23 | 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | namespace trezord 32 | { 33 | namespace protobuf 34 | { 35 | 36 | namespace pb = google::protobuf; 37 | 38 | struct json_codec 39 | { 40 | json_codec(state *s) 41 | : protobuf_state(s) 42 | {} 43 | 44 | Json::Value 45 | protobuf_to_typed_json(pb::Message const &msg) 46 | { 47 | Json::Value val(Json::objectValue); 48 | val["type"] = msg.GetDescriptor()->name(); 49 | val["message"] = protobuf_to_json(msg); 50 | return val; 51 | } 52 | 53 | pb::Message * 54 | typed_json_to_protobuf(Json::Value const &val) 55 | { 56 | auto name = val["type"]; 57 | auto data = val["message"]; 58 | 59 | if (!name.isString()) { 60 | throw std::invalid_argument("expecting JSON string"); 61 | } 62 | 63 | auto descriptor = protobuf_state->descriptor_pool 64 | .FindMessageTypeByName(name.asString()); 65 | if (!descriptor) { 66 | throw std::invalid_argument("unknown message"); 67 | } 68 | 69 | auto prototype = protobuf_state->message_factory 70 | .GetPrototype(descriptor); 71 | 72 | pb::Message *msg = prototype->New(); 73 | json_to_protobuf(data, *msg); 74 | return msg; 75 | } 76 | 77 | private: 78 | 79 | state *protobuf_state; 80 | 81 | Json::Value 82 | protobuf_to_json(pb::Message const &msg) 83 | { 84 | Json::Value val(Json::objectValue); 85 | 86 | auto md = msg.GetDescriptor(); 87 | auto ref = msg.GetReflection(); 88 | 89 | for (int i = 0; i < md->field_count(); i++) { 90 | auto fd = md->field(i); 91 | auto fname = fd->name(); 92 | 93 | try { 94 | if (fd->is_repeated()) { 95 | val[fname] = serialize_repeated_field(msg, *ref, *fd); 96 | // no empty arrays for repeated fields 97 | if (val[fname].empty()) { 98 | val.removeMember(fname); 99 | } 100 | } 101 | else if (ref->HasField(msg, fd)) { 102 | val[fname] = serialize_single_field(msg, *ref, *fd); 103 | } 104 | } 105 | catch (std::exception const &e) { 106 | throw std::invalid_argument("error while serializing " 107 | + fd->full_name() 108 | + ", caused by: " 109 | + e.what()); 110 | } 111 | } 112 | 113 | return val; 114 | } 115 | 116 | void 117 | json_to_protobuf(Json::Value const &val, 118 | pb::Message &msg) 119 | { 120 | if (!val.isObject()) { 121 | throw std::invalid_argument("expecting JSON object"); 122 | } 123 | 124 | auto md = msg.GetDescriptor(); 125 | auto ref = msg.GetReflection(); 126 | 127 | for (int i = 0; i < md->field_count(); i++) { 128 | auto fd = md->field(i); 129 | auto fname = fd->name(); 130 | 131 | if (!val.isMember(fname)) { 132 | continue; 133 | } 134 | try { 135 | if (fd->is_repeated()) { 136 | ref->ClearField(&msg, fd); 137 | parse_repeated_field(msg, *ref, *fd, val[fname]); 138 | } 139 | else { 140 | parse_single_field(msg, *ref, *fd, val[fname]); 141 | } 142 | } 143 | catch (std::exception const &e) { 144 | throw std::invalid_argument("error while parsing " 145 | + fd->full_name() 146 | + ", caused by: " 147 | + e.what()); 148 | } 149 | } 150 | } 151 | 152 | Json::Value 153 | serialize_single_field(const pb::Message &msg, 154 | const pb::Reflection &ref, 155 | const pb::FieldDescriptor &fd) 156 | { 157 | switch (fd.type()) { 158 | 159 | case pb::FieldDescriptor::TYPE_DOUBLE: 160 | return ref.GetDouble(msg, &fd); 161 | 162 | case pb::FieldDescriptor::TYPE_FLOAT: 163 | return ref.GetFloat(msg, &fd); 164 | 165 | case pb::FieldDescriptor::TYPE_INT64: 166 | case pb::FieldDescriptor::TYPE_SFIXED64: 167 | case pb::FieldDescriptor::TYPE_SINT64: 168 | return Json::Value::Int64(ref.GetInt64(msg, &fd)); 169 | 170 | case pb::FieldDescriptor::TYPE_UINT64: 171 | case pb::FieldDescriptor::TYPE_FIXED64: 172 | return Json::Value::UInt64(ref.GetUInt64(msg, &fd)); 173 | 174 | case pb::FieldDescriptor::TYPE_INT32: 175 | case pb::FieldDescriptor::TYPE_SFIXED32: 176 | case pb::FieldDescriptor::TYPE_SINT32: 177 | return ref.GetInt32(msg, &fd); 178 | 179 | case pb::FieldDescriptor::TYPE_UINT32: 180 | case pb::FieldDescriptor::TYPE_FIXED32: 181 | return ref.GetUInt32(msg, &fd); 182 | 183 | case pb::FieldDescriptor::TYPE_BOOL: 184 | return ref.GetBool(msg, &fd); 185 | 186 | case pb::FieldDescriptor::TYPE_STRING: 187 | return ref.GetString(msg, &fd); 188 | 189 | case pb::FieldDescriptor::TYPE_BYTES: 190 | return utils::hex_encode(ref.GetString(msg, &fd)); 191 | 192 | case pb::FieldDescriptor::TYPE_ENUM: 193 | return ref.GetEnum(msg, &fd)->name(); 194 | 195 | case pb::FieldDescriptor::TYPE_MESSAGE: 196 | return protobuf_to_json(ref.GetMessage(msg, &fd)); 197 | 198 | default: 199 | throw std::invalid_argument("field of unsupported type"); 200 | } 201 | } 202 | 203 | Json::Value 204 | serialize_repeated_field(const pb::Message &msg, 205 | const pb::Reflection &ref, 206 | const pb::FieldDescriptor &fd) 207 | { 208 | Json::Value result(Json::arrayValue); 209 | int field_size = ref.FieldSize(msg, &fd); 210 | result.resize(field_size); 211 | 212 | for (int i = 0; i < field_size; i++) { 213 | result[i] = serialize_repeated_field_item(msg, ref, fd, i); 214 | } 215 | 216 | return result; 217 | } 218 | 219 | Json::Value 220 | serialize_repeated_field_item(const pb::Message &msg, 221 | const pb::Reflection &ref, 222 | const pb::FieldDescriptor &fd, 223 | int i) 224 | { 225 | switch (fd.type()) { 226 | 227 | case pb::FieldDescriptor::TYPE_DOUBLE: 228 | return ref.GetRepeatedDouble(msg, &fd, i); 229 | 230 | case pb::FieldDescriptor::TYPE_FLOAT: 231 | return ref.GetRepeatedFloat(msg, &fd, i); 232 | 233 | case pb::FieldDescriptor::TYPE_INT64: 234 | case pb::FieldDescriptor::TYPE_SFIXED64: 235 | case pb::FieldDescriptor::TYPE_SINT64: 236 | return Json::Value::Int64(ref.GetRepeatedInt64(msg, &fd, i)); 237 | 238 | case pb::FieldDescriptor::TYPE_UINT64: 239 | case pb::FieldDescriptor::TYPE_FIXED64: 240 | return Json::Value::UInt64(ref.GetRepeatedUInt64(msg, &fd, i)); 241 | 242 | case pb::FieldDescriptor::TYPE_INT32: 243 | case pb::FieldDescriptor::TYPE_SFIXED32: 244 | case pb::FieldDescriptor::TYPE_SINT32: 245 | return ref.GetRepeatedInt32(msg, &fd, i); 246 | 247 | case pb::FieldDescriptor::TYPE_UINT32: 248 | case pb::FieldDescriptor::TYPE_FIXED32: 249 | return ref.GetRepeatedUInt32(msg, &fd, i); 250 | 251 | case pb::FieldDescriptor::TYPE_BOOL: 252 | return ref.GetRepeatedBool(msg, &fd, i); 253 | 254 | case pb::FieldDescriptor::TYPE_STRING: 255 | return ref.GetRepeatedString(msg, &fd, i); 256 | 257 | case pb::FieldDescriptor::TYPE_BYTES: 258 | return utils::hex_encode(ref.GetRepeatedString(msg, &fd, i)); 259 | 260 | case pb::FieldDescriptor::TYPE_ENUM: 261 | return ref.GetRepeatedEnum(msg, &fd, i)->name(); 262 | 263 | case pb::FieldDescriptor::TYPE_MESSAGE: 264 | return protobuf_to_json(ref.GetRepeatedMessage(msg, &fd, i)); 265 | 266 | default: 267 | throw std::invalid_argument("field of unsupported type"); 268 | } 269 | } 270 | 271 | void 272 | parse_single_field(pb::Message &msg, 273 | const pb::Reflection &ref, 274 | const pb::FieldDescriptor &fd, 275 | const Json::Value &val) 276 | { 277 | switch (fd.type()) { 278 | 279 | case pb::FieldDescriptor::TYPE_DOUBLE: 280 | ref.SetDouble(&msg, &fd, val.asDouble()); 281 | break; 282 | 283 | case pb::FieldDescriptor::TYPE_FLOAT: 284 | ref.SetFloat(&msg, &fd, val.asFloat()); 285 | break; 286 | 287 | case pb::FieldDescriptor::TYPE_INT64: 288 | case pb::FieldDescriptor::TYPE_SFIXED64: 289 | case pb::FieldDescriptor::TYPE_SINT64: 290 | ref.SetInt64(&msg, &fd, val.asInt64()); 291 | break; 292 | 293 | case pb::FieldDescriptor::TYPE_UINT64: 294 | case pb::FieldDescriptor::TYPE_FIXED64: 295 | ref.SetUInt64(&msg, &fd, val.asUInt64()); 296 | break; 297 | 298 | case pb::FieldDescriptor::TYPE_INT32: 299 | case pb::FieldDescriptor::TYPE_SFIXED32: 300 | case pb::FieldDescriptor::TYPE_SINT32: 301 | ref.SetInt32(&msg, &fd, val.asInt()); 302 | break; 303 | 304 | case pb::FieldDescriptor::TYPE_UINT32: 305 | case pb::FieldDescriptor::TYPE_FIXED32: 306 | ref.SetUInt32(&msg, &fd, val.asUInt()); 307 | break; 308 | 309 | case pb::FieldDescriptor::TYPE_BOOL: 310 | ref.SetBool(&msg, &fd, val.asBool()); 311 | break; 312 | 313 | case pb::FieldDescriptor::TYPE_STRING: 314 | ref.SetString(&msg, &fd, val.asString()); 315 | break; 316 | 317 | case pb::FieldDescriptor::TYPE_BYTES: 318 | ref.SetString(&msg, &fd, utils::hex_decode(val.asString())); 319 | break; 320 | 321 | case pb::FieldDescriptor::TYPE_ENUM: { 322 | auto ed = fd.enum_type(); 323 | auto evd = ed->FindValueByName(val.asString()); 324 | if (!evd) { 325 | throw std::invalid_argument("unknown enum value"); 326 | } 327 | ref.SetEnum(&msg, &fd, evd); 328 | break; 329 | } 330 | 331 | case pb::FieldDescriptor::TYPE_MESSAGE: { 332 | auto mf = &protobuf_state->message_factory; 333 | auto fm = ref.MutableMessage(&msg, &fd, mf); 334 | json_to_protobuf(val, *fm); 335 | break; 336 | } 337 | 338 | default: 339 | throw std::invalid_argument("field of unsupported type"); 340 | } 341 | } 342 | 343 | void 344 | parse_repeated_field(pb::Message &msg, 345 | const pb::Reflection &ref, 346 | const pb::FieldDescriptor &fd, 347 | const Json::Value &val) 348 | { 349 | if (!val.isArray()) { 350 | throw std::invalid_argument("expecting JSON array"); 351 | } 352 | for (auto v: val) { 353 | parse_repeated_field_item(msg, ref, fd, v); 354 | } 355 | } 356 | 357 | void 358 | parse_repeated_field_item(pb::Message &msg, 359 | const pb::Reflection &ref, 360 | const pb::FieldDescriptor &fd, 361 | const Json::Value &val) 362 | { 363 | switch (fd.type()) { 364 | 365 | case pb::FieldDescriptor::TYPE_DOUBLE: 366 | ref.AddDouble(&msg, &fd, val.asDouble()); 367 | break; 368 | 369 | case pb::FieldDescriptor::TYPE_FLOAT: 370 | ref.AddFloat(&msg, &fd, val.asFloat()); 371 | break; 372 | 373 | case pb::FieldDescriptor::TYPE_INT64: 374 | case pb::FieldDescriptor::TYPE_SFIXED64: 375 | case pb::FieldDescriptor::TYPE_SINT64: 376 | ref.AddInt64(&msg, &fd, val.asInt64()); 377 | break; 378 | 379 | case pb::FieldDescriptor::TYPE_UINT64: 380 | case pb::FieldDescriptor::TYPE_FIXED64: 381 | ref.AddUInt64(&msg, &fd, val.asUInt64()); 382 | break; 383 | 384 | case pb::FieldDescriptor::TYPE_INT32: 385 | case pb::FieldDescriptor::TYPE_SFIXED32: 386 | case pb::FieldDescriptor::TYPE_SINT32: 387 | ref.AddInt32(&msg, &fd, val.asInt()); 388 | break; 389 | 390 | case pb::FieldDescriptor::TYPE_UINT32: 391 | case pb::FieldDescriptor::TYPE_FIXED32: 392 | ref.AddUInt32(&msg, &fd, val.asUInt()); 393 | break; 394 | 395 | case pb::FieldDescriptor::TYPE_BOOL: 396 | ref.AddBool(&msg, &fd, val.asBool()); 397 | break; 398 | 399 | case pb::FieldDescriptor::TYPE_STRING: 400 | ref.AddString(&msg, &fd, val.asString()); 401 | break; 402 | 403 | case pb::FieldDescriptor::TYPE_BYTES: 404 | ref.AddString(&msg, &fd, utils::hex_decode(val.asString())); 405 | break; 406 | 407 | case pb::FieldDescriptor::TYPE_ENUM: { 408 | auto ed = fd.enum_type(); 409 | auto evd = ed->FindValueByName(val.asString()); 410 | if (!evd) { 411 | throw std::invalid_argument("unknown enum value"); 412 | } 413 | ref.AddEnum(&msg, &fd, evd); 414 | break; 415 | } 416 | 417 | case pb::FieldDescriptor::TYPE_MESSAGE: { 418 | auto mf = &protobuf_state->message_factory; 419 | auto fm = ref.AddMessage(&msg, &fd, mf); 420 | json_to_protobuf(val, *fm); 421 | break; 422 | } 423 | 424 | default: 425 | throw std::invalid_argument("field of unsupported type"); 426 | } 427 | } 428 | }; 429 | 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /src/protobuf/state.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace trezord 28 | { 29 | namespace protobuf 30 | { 31 | 32 | namespace pb = google::protobuf; 33 | 34 | struct state 35 | { 36 | pb::DescriptorPool descriptor_pool; 37 | pb::DynamicMessageFactory message_factory; 38 | 39 | state(state const&) = delete; 40 | state &operator=(state const&) = delete; 41 | 42 | state() 43 | : descriptor_pool( 44 | pb::DescriptorPool::generated_pool()) 45 | {} 46 | 47 | void 48 | load_from_set(pb::FileDescriptorSet const &set) 49 | { 50 | for (int i = 0; i < set.file_size(); i++) { 51 | descriptor_pool.BuildFile(set.file(i)); 52 | } 53 | } 54 | }; 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/protobuf/wire_codec.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "protobuf/state.hpp" 23 | 24 | namespace trezord 25 | { 26 | namespace protobuf 27 | { 28 | 29 | namespace pb = google::protobuf; 30 | 31 | struct wire_codec 32 | { 33 | typedef pb::Message pbuf_type; 34 | typedef pb::Message *pbuf_type_ptr; 35 | typedef wire::message wire_type; 36 | 37 | wire_codec(state *s) 38 | : protobuf_state(s) 39 | {} 40 | 41 | void 42 | load_protobuf_state() 43 | { 44 | static const std::string enum_name = "MessageType"; 45 | static const std::string enum_prefix = "MessageType_"; 46 | 47 | auto e = protobuf_state->descriptor_pool.FindEnumTypeByName(enum_name); 48 | if (!e) { 49 | throw std::invalid_argument("invalid file descriptor set"); 50 | } 51 | 52 | for (int i = 0; i < e->value_count(); i++) { 53 | auto ev = e->value(i); 54 | auto name = ev->name().substr( 55 | enum_prefix.size()); // skip prefix 56 | 57 | descriptor_index[ev->number()] = 58 | protobuf_state->descriptor_pool.FindMessageTypeByName(name); 59 | } 60 | } 61 | 62 | pbuf_type_ptr 63 | wire_to_protobuf(wire_type const &wire) 64 | { 65 | auto descriptor = descriptor_index.at(wire.id); 66 | auto prototype = protobuf_state->message_factory 67 | .GetPrototype(descriptor); 68 | 69 | pbuf_type_ptr pbuf = prototype->New(); 70 | pbuf->ParseFromArray(wire.data.data(), 71 | wire.data.size()); 72 | return pbuf; 73 | } 74 | 75 | void 76 | protobuf_to_wire(pbuf_type const &pbuf, wire_type &wire) 77 | { 78 | auto size = pbuf.ByteSize(); 79 | auto name = pbuf.GetDescriptor()->name(); 80 | 81 | wire.id = find_wire_id(name); 82 | wire.data.resize(size); 83 | pbuf.SerializeToArray(wire.data.data(), 84 | wire.data.size()); 85 | } 86 | 87 | private: 88 | 89 | typedef std::map< 90 | int, pb::Descriptor const * 91 | > id_map; 92 | 93 | state *protobuf_state; 94 | id_map descriptor_index; 95 | 96 | int 97 | find_wire_id(std::string const &name) 98 | { 99 | for (auto &kv: descriptor_index) { 100 | if (kv.second->name() == name) { 101 | return kv.first; 102 | } 103 | } 104 | throw std::invalid_argument("missing wire id for message"); 105 | } 106 | }; 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/trezord.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trezor-graveyard/trezord/ea6e020788cd2a34be3a218cce2a9f609645f668/src/trezord.ico -------------------------------------------------------------------------------- /src/trezord.rc: -------------------------------------------------------------------------------- 1 | idi_icon ICON "trezord.ico" 2 | -------------------------------------------------------------------------------- /src/utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | namespace trezord 31 | { 32 | namespace utils 33 | { 34 | 35 | template 36 | struct blocking_queue 37 | { 38 | void 39 | put(T item) 40 | { 41 | boost::unique_lock lock{mutex}; 42 | queue.push(std::move(item)); 43 | cond_var.notify_one(); 44 | } 45 | 46 | T 47 | take() 48 | { 49 | boost::unique_lock lock{mutex}; 50 | while (queue.empty()) { 51 | cond_var.wait(lock); 52 | } 53 | T item = std::move(queue.front()); 54 | queue.pop(); 55 | return std::move(item); 56 | } 57 | 58 | private: 59 | 60 | std::queue queue; 61 | boost::mutex mutex; 62 | boost::condition_variable cond_var; 63 | }; 64 | 65 | struct async_executor 66 | { 67 | async_executor() 68 | : thread{boost::bind(&async_executor::run, this)} 69 | { } 70 | 71 | ~async_executor() 72 | { 73 | thread.interrupt(); 74 | thread.join(); 75 | } 76 | 77 | template 78 | boost::unique_future::type> 79 | add(Callable callable) 80 | { 81 | using result_type = typename std::result_of::type; 82 | using task_type = boost::packaged_task; 83 | 84 | auto task = std::make_shared(callable); 85 | auto future = task->get_future(); 86 | 87 | queue.put(std::bind(&task_type::operator(), task)); 88 | return std::move(future); 89 | } 90 | 91 | template 92 | typename std::result_of::type 93 | await(Callable callable) 94 | { 95 | return add(callable).get(); 96 | } 97 | 98 | private: 99 | 100 | void 101 | run() 102 | { 103 | while (!boost::this_thread::interruption_requested()) { 104 | queue.take()(); 105 | } 106 | } 107 | 108 | blocking_queue< 109 | std::function > queue; 110 | boost::thread thread; 111 | }; 112 | 113 | std::string 114 | hex_encode(std::string const &str) 115 | { 116 | try { 117 | std::ostringstream stream; 118 | std::ostream_iterator iterator{stream}; 119 | boost::algorithm::hex(str, iterator); 120 | std::string hex{stream.str()}; 121 | boost::algorithm::to_lower(hex); 122 | return hex; 123 | } 124 | catch (std::exception const &e) { 125 | throw std::invalid_argument{"cannot encode value to hex"}; 126 | } 127 | } 128 | 129 | std::string 130 | hex_decode(std::string const &hex) 131 | { 132 | try { 133 | std::ostringstream stream; 134 | std::ostream_iterator iterator{stream}; 135 | boost::algorithm::unhex(hex, iterator); 136 | return stream.str(); 137 | } 138 | catch (std::exception const &e) { 139 | throw std::invalid_argument{"cannot decode value from hex"}; 140 | } 141 | } 142 | 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/wire.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TREZOR project. 3 | * 4 | * Copyright (C) 2014 SatoshiLabs 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | #ifdef _WIN32 21 | #include 22 | #else 23 | #include 24 | #endif 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace trezord 34 | { 35 | namespace wire 36 | { 37 | 38 | struct device_info 39 | { 40 | std::uint16_t vendor_id; 41 | std::uint16_t product_id; 42 | std::string path; 43 | 44 | bool 45 | operator==(device_info const &rhs) const 46 | { 47 | return (vendor_id == rhs.vendor_id) 48 | && (product_id == rhs.product_id) 49 | && (path == rhs.path); 50 | } 51 | }; 52 | 53 | typedef std::vector device_info_list; 54 | 55 | template 56 | device_info_list 57 | enumerate_connected_devices(F filter) 58 | { 59 | device_info_list list; 60 | auto *infos = hid::enumerate(0x00, 0x00); 61 | 62 | for (auto i = infos; i != nullptr; i = i->next) { 63 | // skip unsupported devices 64 | if (!filter(i)) { 65 | continue; 66 | } 67 | // skip foreign interfaces 68 | if (i->interface_number > 0) { 69 | CLOG(DEBUG, "wire.enumerate") << "skipping, invalid device"; 70 | continue; 71 | } 72 | // skip debug interface 73 | if (i->usage_page == 0xFF01) { 74 | CLOG(DEBUG, "wire.enumerate") << "skipping, debug interface"; 75 | continue; 76 | } 77 | // skip fido interface 78 | if (i->usage_page == 0xF1D0) { 79 | CLOG(DEBUG, "wire.enumerate") << "skipping, fido interface"; 80 | continue; 81 | } 82 | list.emplace_back( 83 | device_info{ 84 | i->vendor_id, 85 | i->product_id, 86 | i->path}); 87 | } 88 | 89 | hid::free_enumeration(infos); 90 | return list; 91 | } 92 | 93 | struct device 94 | { 95 | typedef std::uint8_t char_type; 96 | typedef std::size_t size_type; 97 | 98 | struct open_error 99 | : public std::runtime_error 100 | { using std::runtime_error::runtime_error; }; 101 | 102 | struct read_error 103 | : public std::runtime_error 104 | { using std::runtime_error::runtime_error; }; 105 | 106 | struct write_error 107 | : public std::runtime_error 108 | { using std::runtime_error::runtime_error; }; 109 | 110 | device(device const&) = delete; 111 | device &operator=(device const&) = delete; 112 | 113 | device(char const *path) 114 | { 115 | hid = hid::open_path(path); 116 | if (!hid) { 117 | throw open_error("HID device open failed"); 118 | } 119 | hid_version = try_hid_version(); 120 | if (hid_version <= 0) { 121 | throw open_error("Unknown HID version"); 122 | } 123 | } 124 | 125 | ~device() { hid::close(hid); } 126 | 127 | // try writing packet that will be discarded to figure out hid version 128 | int try_hid_version() { 129 | int r; 130 | report_type report; 131 | 132 | // try version 2 133 | report.fill(0xFF); 134 | report[0] = 0x00; 135 | report[1] = 0x3F; 136 | r = hid::write(hid, report.data(), 65); 137 | if (r == 65) { 138 | return 2; 139 | } 140 | 141 | // try version 1 142 | report.fill(0xFF); 143 | report[0] = 0x3F; 144 | r = hid::write(hid, report.data(), 64); 145 | if (r == 64) { 146 | return 1; 147 | } 148 | 149 | // unknown version 150 | return 0; 151 | } 152 | 153 | void 154 | read_buffered(char_type *data, 155 | size_type len) 156 | { 157 | for (;;) { 158 | if (read_buffer.empty()) { 159 | buffer_report(); 160 | } 161 | size_type n = read_report_from_buffer(data, len); 162 | if (n < len) { 163 | data += n; 164 | len -= n; 165 | } else { 166 | break; 167 | } 168 | } 169 | } 170 | 171 | void 172 | write(char_type const *data, 173 | size_type len) 174 | { 175 | for (;;) { 176 | size_type n = write_report(data, len); 177 | if (n < len) { 178 | data += n; 179 | len -= n; 180 | } else { 181 | break; 182 | } 183 | } 184 | } 185 | 186 | private: 187 | 188 | size_type 189 | read_report_from_buffer(char_type *data, 190 | size_type len) 191 | { 192 | using namespace std; 193 | 194 | size_type n = min(read_buffer.size(), len); 195 | auto r1 = read_buffer.begin(); 196 | auto r2 = read_buffer.begin() + n; 197 | 198 | copy(r1, r2, data); // copy to data 199 | read_buffer.erase(r1, r2); // shift from buffer 200 | 201 | return n; 202 | } 203 | 204 | void 205 | buffer_report() 206 | { 207 | using namespace std; 208 | 209 | report_type report; 210 | int r; 211 | 212 | do { 213 | r = hid::read_timeout(hid, report.data(), report.size(), 50); 214 | } while (r == 0); 215 | 216 | if (r < 0) { 217 | throw read_error("HID device read failed"); 218 | } 219 | if (r > 0) { 220 | // copy to the buffer, skip the report number 221 | char_type rn = report[0]; 222 | size_type n = min(static_cast(rn), 223 | static_cast(r - 1)); 224 | copy(report.begin() + 1, 225 | report.begin() + 1 + n, 226 | back_inserter(read_buffer)); 227 | } 228 | } 229 | 230 | size_type 231 | write_report(char_type const *data, 232 | size_type len) 233 | { 234 | using namespace std; 235 | 236 | report_type report; 237 | report.fill(0x00); 238 | 239 | size_type n = min(static_cast(63), len); 240 | size_type report_size = 63 + hid_version; 241 | 242 | switch (hid_version) { 243 | case 1: 244 | report[0] = 0x3F; 245 | copy(data, data + n, report.begin() + 1); 246 | break; 247 | case 2: 248 | report[0] = 0x00; 249 | report[1] = 0x3F; 250 | copy(data, data + n, report.begin() + 2); 251 | break; 252 | } 253 | 254 | int r = hid::write(hid, report.data(), report_size); 255 | if (r < 0) { 256 | throw write_error{"HID device write failed"}; 257 | } 258 | if (r < report_size) { 259 | throw write_error{"HID device write was insufficient"}; 260 | } 261 | 262 | return n; 263 | } 264 | 265 | typedef std::vector buffer_type; 266 | typedef std::array report_type; 267 | 268 | hid_device *hid; 269 | buffer_type read_buffer; 270 | int hid_version; 271 | }; 272 | 273 | struct message 274 | { 275 | std::uint16_t id; 276 | std::vector data; 277 | 278 | typedef device::read_error header_read_error; 279 | 280 | void 281 | read_from(device &device) 282 | { 283 | device::char_type buf[6]; 284 | std::uint32_t size; 285 | 286 | device.read_buffered(buf, 1); 287 | while (buf[0] != '#') { 288 | device.read_buffered(buf, 1); 289 | } 290 | 291 | device.read_buffered(buf, 1); 292 | if (buf[0] != '#') { 293 | throw header_read_error{"header bytes are malformed"}; 294 | } 295 | 296 | device.read_buffered(buf, 6); 297 | 298 | id = ntohs((buf[0] << 0) | (buf[1] << 8)); 299 | size = ntohl((buf[2] << 0) | (buf[3] << 8) | 300 | (buf[4] << 16) | (buf[5] << 24)); 301 | 302 | // 1MB of the message size treshold 303 | static const std::uint32_t max_size = 1024 * 1024; 304 | if (size > max_size) { 305 | throw header_read_error{"message is too big"}; 306 | } 307 | 308 | data.resize(size); 309 | device.read_buffered(data.data(), data.size()); 310 | } 311 | 312 | void 313 | write_to(device &device) const 314 | { 315 | std::size_t buf_size = 8 + data.size(); 316 | device::char_type buf[buf_size]; 317 | 318 | buf[0] = '#'; 319 | buf[1] = '#'; 320 | 321 | std::uint16_t id_ = htons(id); 322 | buf[2] = (id_ >> 0) & 0xFF; 323 | buf[3] = (id_ >> 8) & 0xFF; 324 | 325 | std::uint32_t size_ = htonl(data.size()); 326 | buf[4] = (size_ >> 0) & 0xFF; 327 | buf[5] = (size_ >> 8) & 0xFF; 328 | buf[6] = (size_ >> 16) & 0xFF; 329 | buf[7] = (size_ >> 24) & 0xFF; 330 | 331 | std::copy(data.begin(), data.end(), &buf[8]); 332 | device.write(buf, buf_size); 333 | } 334 | }; 335 | 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /tarball.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | 7 | VERSION=$(cat VERSION) 8 | DISTNAME=trezord-$VERSION 9 | TARBALL=$DISTNAME.tar.bz2 10 | 11 | echo -n "Creating $TARBALL tarball..." 12 | 13 | git submodule update --init 14 | 15 | tar -c --exclude='*.tar.bz2' --exclude='.git*' -s "|^|$DISTNAME/|" -j -f $TARBALL * 16 | 17 | echo " Done!" 18 | -------------------------------------------------------------------------------- /test/fixtures/messages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import json 5 | 6 | 7 | def parse_line(line): 8 | cols = line.split('\t') 9 | msg = {'wire': {'type': cols[0].strip(), 10 | 'message': cols[4].strip()}, 11 | 'parsed': {'type': cols[1].strip(), 12 | 'message': json.loads(cols[3].strip())}} 13 | return msg 14 | 15 | 16 | def dump_str(s): 17 | shex = s.encode('hex') 18 | pairs = [shex[i:i+2] for i in range(0, len(shex), 2)] 19 | dump = '\\x'.join([''] + pairs) 20 | return dump 21 | 22 | 23 | def dump_wire(wire): 24 | id = wire['type'] 25 | data = wire['message'] 26 | data = data.decode('hex') 27 | code = '{%s, std::string("%s", %s)}' % (id, dump_str(data), len(data)) 28 | return code 29 | 30 | 31 | def dump_parsed(parsed): 32 | parsed_json = json.dumps(parsed) 33 | parsed_str = dump_str(parsed_json) 34 | parsed_len = len(parsed_json) 35 | code = 'std::string("%s", %s)' % (parsed_str, parsed_len) 36 | return code 37 | 38 | 39 | def dump_message(message): 40 | wire = dump_wire(message['wire']) 41 | parsed = dump_parsed(message['parsed']) 42 | code = '{%s, %s}' % (wire, parsed) 43 | return code 44 | 45 | 46 | def dump_file(messages): 47 | content = [dump_message(m) for m in messages] 48 | content = ",\n".join(content) 49 | return """ 50 | #include 51 | #include 52 | 53 | static const std::pair< 54 | std::pair< 55 | std::uint16_t, 56 | std::string 57 | >, 58 | std::string 59 | > message_encoding_sample[] = { 60 | %s 61 | }; 62 | """ % content 63 | 64 | 65 | messages = [parse_line(l) for l in sys.stdin] 66 | 67 | print dump_file(messages) 68 | -------------------------------------------------------------------------------- /test/fixtures/trezor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trezor-graveyard/trezord/ea6e020788cd2a34be3a218cce2a9f609645f668/test/fixtures/trezor.bin -------------------------------------------------------------------------------- /test/functional/.gitignore: -------------------------------------------------------------------------------- 1 | config.bin 2 | -------------------------------------------------------------------------------- /test/functional/call_initialize.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Initialize", 3 | "message": {} 4 | } 5 | -------------------------------------------------------------------------------- /test/functional/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -x 5 | 6 | url=https://127.0.0.1:21324 7 | 8 | device_index=${1-0} 9 | 10 | if [ \! -f config.bin ]; then 11 | curl https://wallet.trezor.io/data/config_signed.bin | xxd -p -r > config.bin 12 | fi 13 | 14 | curl -k $url/ 15 | 16 | curl -ksS -X POST --data-binary @config.bin $url/configure 17 | 18 | path=$(curl -ksS $url/enumerate | jq -r ".[$device_index].path") 19 | 20 | session=$(curl -ksS -X POST $url/acquire/$path | jq -r ".session") 21 | 22 | curl -ksS -X POST --data-binary @call_initialize.json $url/call/$session 23 | 24 | curl -ksS -X POST $url/release/$session 25 | -------------------------------------------------------------------------------- /test/protobuf_codecs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "utils.hpp" 4 | #include "wire.hpp" 5 | 6 | #include "protobuf/state.hpp" 7 | #include "protobuf/json_codec.hpp" 8 | #include "protobuf/wire_codec.hpp" 9 | 10 | #include "fixtures/messages.hpp" 11 | 12 | #include 13 | #include 14 | 15 | #define BOOST_TEST_MODULE ProtobufCodecs 16 | 17 | #include 18 | 19 | _INITIALIZE_EASYLOGGINGPP 20 | 21 | using namespace trezord; 22 | 23 | struct empty_state_fixture 24 | { 25 | protobuf::state protobuf_state; 26 | }; 27 | 28 | struct loaded_state_fixture 29 | : public empty_state_fixture 30 | { 31 | loaded_state_fixture() 32 | { 33 | // load config file 34 | std::string cpath("../test/fixtures/trezor.bin"); 35 | std::ifstream config(cpath, std::ios::in | std::ios::binary); 36 | BOOST_CHECK(config.good()); 37 | 38 | // parse to FileDescriptorSet 39 | protobuf::pb::FileDescriptorSet descriptor_set; 40 | descriptor_set.ParseFromIstream(&config); 41 | BOOST_CHECK(descriptor_set.file_size() > 0); 42 | 43 | // initialize the protobuf state 44 | protobuf_state.load_from_set(descriptor_set); 45 | } 46 | }; 47 | 48 | BOOST_FIXTURE_TEST_CASE(wire_codec_with_empty_state_fails, 49 | empty_state_fixture) 50 | { 51 | // fails because of missing MessageType enum 52 | protobuf::wire_codec wc(protobuf_state); 53 | BOOST_CHECK_THROW(wc.load_protobuf_state(), std::invalid_argument); 54 | } 55 | 56 | BOOST_FIXTURE_TEST_CASE(json_to_wire_conversion, 57 | loaded_state_fixture) 58 | { 59 | protobuf::json_codec json_codec(protobuf_state); 60 | protobuf::wire_codec wire_codec(protobuf_state); 61 | 62 | wire_codec.load_protobuf_state(); 63 | 64 | for (auto &row: message_encoding_sample) { 65 | std::uint16_t expected_wire_id = row.first.first; 66 | std::string expected_wire_str = row.first.second; 67 | std::string json_str = row.second; 68 | 69 | Json::Value json; 70 | Json::Reader reader; 71 | reader.parse(json_str, json); 72 | 73 | std::unique_ptr pbuf( 74 | json_codec.typed_json_to_protobuf(json)); 75 | wire::message wire; 76 | wire_codec.protobuf_to_wire(*pbuf, wire); 77 | 78 | BOOST_REQUIRE_EQUAL(wire.id, 79 | expected_wire_id); 80 | BOOST_REQUIRE_EQUAL(wire.data.size(), 81 | expected_wire_str.size()); 82 | 83 | std::vector expected_wire_data( 84 | expected_wire_str.begin(), 85 | expected_wire_str.end()); 86 | 87 | BOOST_CHECK_EQUAL_COLLECTIONS( 88 | wire.data.begin(), 89 | wire.data.end(), 90 | expected_wire_data.begin(), 91 | expected_wire_data.end()); 92 | } 93 | } 94 | 95 | BOOST_FIXTURE_TEST_CASE(wire_to_json_conversion, 96 | loaded_state_fixture) 97 | { 98 | protobuf::json_codec json_codec(protobuf_state); 99 | protobuf::wire_codec wire_codec(protobuf_state); 100 | 101 | wire_codec.load_protobuf_state(); 102 | 103 | for (auto &row: message_encoding_sample) { 104 | std::uint16_t wire_id = row.first.first; 105 | std::string wire_str = row.first.second; 106 | std::string expected_json_str = row.second; 107 | 108 | wire::message wire{ 109 | wire_id, {wire_str.begin(), wire_str.end()} 110 | }; 111 | std::unique_ptr pbuf( 112 | wire_codec.wire_to_protobuf(wire)); 113 | 114 | Json::Value json = json_codec.protobuf_to_typed_json(*pbuf); 115 | Json::Value expected_json; 116 | Json::Reader reader; 117 | reader.parse(expected_json_str, expected_json); 118 | 119 | BOOST_REQUIRE_EQUAL( 120 | json.toStyledString(), 121 | expected_json.toStyledString()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /vendor/hidapi/AUTHORS.txt: -------------------------------------------------------------------------------- 1 | 2 | HIDAPI Authors: 3 | 4 | Alan Ott : 5 | Original Author and Maintainer 6 | Linux, Windows, and Mac implementations 7 | 8 | Ludovic Rousseau : 9 | Formatting for Doxygen documentation 10 | Bug fixes 11 | Correctness fixes 12 | 13 | 14 | For a comprehensive list of contributions, see the commit list at github: 15 | http://github.com/signal11/hidapi/commits/master 16 | 17 | -------------------------------------------------------------------------------- /vendor/hidapi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # CMake build file for hidapi 3 | # 4 | 5 | cmake_minimum_required(VERSION 2.8) 6 | 7 | project(hidapi) 8 | include_directories(hidapi) 9 | 10 | if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 11 | add_subdirectory(mac) 12 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 13 | add_subdirectory(windows) 14 | else() 15 | add_subdirectory(libusb) 16 | endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 17 | -------------------------------------------------------------------------------- /vendor/hidapi/LICENSE-bsd.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Alan Ott, Signal 11 Software 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of Signal 11 Software nor the names of its 13 | contributors may be used to endorse or promote products derived from 14 | this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /vendor/hidapi/LICENSE-orig.txt: -------------------------------------------------------------------------------- 1 | HIDAPI - Multi-Platform library for 2 | communication with HID devices. 3 | 4 | Copyright 2009, Alan Ott, Signal 11 Software. 5 | All Rights Reserved. 6 | 7 | This software may be used by anyone for any reason so 8 | long as the copyright notice in the source files 9 | remains intact. 10 | -------------------------------------------------------------------------------- /vendor/hidapi/LICENSE.txt: -------------------------------------------------------------------------------- 1 | HIDAPI can be used under one of three licenses. 2 | 3 | 1. The GNU General Public License, version 3.0, in LICENSE-gpl3.txt 4 | 2. A BSD-Style License, in LICENSE-bsd.txt. 5 | 3. The more liberal original HIDAPI license. LICENSE-orig.txt 6 | 7 | The license chosen is at the discretion of the user of HIDAPI. For example: 8 | 1. An author of GPL software would likely use HIDAPI under the terms of the 9 | GPL. 10 | 11 | 2. An author of commercial closed-source software would likely use HIDAPI 12 | under the terms of the BSD-style license or the original HIDAPI license. 13 | 14 | -------------------------------------------------------------------------------- /vendor/hidapi/README.txt: -------------------------------------------------------------------------------- 1 | HIDAPI library for Windows, Linux, FreeBSD and Mac OS X 2 | ========================================================= 3 | 4 | About 5 | ====== 6 | 7 | HIDAPI is a multi-platform library which allows an application to interface 8 | with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and Mac 9 | OS X. HIDAPI can be either built as a shared library (.so or .dll) or 10 | can be embedded directly into a target application by adding a single source 11 | file (per platform) and a single header. 12 | 13 | HIDAPI has four back-ends: 14 | * Windows (using hid.dll) 15 | * Linux/hidraw (using the Kernel's hidraw driver) 16 | * Linux/libusb (using libusb-1.0) 17 | * FreeBSD (using libusb-1.0) 18 | * Mac (using IOHidManager) 19 | 20 | On Linux, either the hidraw or the libusb back-end can be used. There are 21 | tradeoffs, and the functionality supported is slightly different. 22 | 23 | Linux/hidraw (linux/hid.c): 24 | This back-end uses the hidraw interface in the Linux kernel. While this 25 | back-end will support both USB and Bluetooth, it has some limitations on 26 | kernels prior to 2.6.39, including the inability to send or receive feature 27 | reports. In addition, it will only communicate with devices which have 28 | hidraw nodes associated with them. Keyboards, mice, and some other devices 29 | which are blacklisted from having hidraw nodes will not work. Fortunately, 30 | for nearly all the uses of hidraw, this is not a problem. 31 | 32 | Linux/FreeBSD/libusb (libusb/hid-libusb.c): 33 | This back-end uses libusb-1.0 to communicate directly to a USB device. This 34 | back-end will of course not work with Bluetooth devices. 35 | 36 | HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses 37 | Fox Toolkit (http://www.fox-toolkit.org). It will build on every platform 38 | which HIDAPI supports. Since it relies on a 3rd party library, building it 39 | is optional but recommended because it is so useful when debugging hardware. 40 | 41 | What Does the API Look Like? 42 | ============================= 43 | The API provides the the most commonly used HID functions including sending 44 | and receiving of input, output, and feature reports. The sample program, 45 | which communicates with a heavily hacked up version of the Microchip USB 46 | Generic HID sample looks like this (with error checking removed for 47 | simplicity): 48 | 49 | #ifdef WIN32 50 | #include 51 | #endif 52 | #include 53 | #include 54 | #include "hidapi.h" 55 | 56 | #define MAX_STR 255 57 | 58 | int main(int argc, char* argv[]) 59 | { 60 | int res; 61 | unsigned char buf[65]; 62 | wchar_t wstr[MAX_STR]; 63 | hid_device *handle; 64 | int i; 65 | 66 | // Initialize the hidapi library 67 | res = hid_init(); 68 | 69 | // Open the device using the VID, PID, 70 | // and optionally the Serial number. 71 | handle = hid_open(0x4d8, 0x3f, NULL); 72 | 73 | // Read the Manufacturer String 74 | res = hid_get_manufacturer_string(handle, wstr, MAX_STR); 75 | wprintf(L"Manufacturer String: %s\n", wstr); 76 | 77 | // Read the Product String 78 | res = hid_get_product_string(handle, wstr, MAX_STR); 79 | wprintf(L"Product String: %s\n", wstr); 80 | 81 | // Read the Serial Number String 82 | res = hid_get_serial_number_string(handle, wstr, MAX_STR); 83 | wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr); 84 | 85 | // Read Indexed String 1 86 | res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); 87 | wprintf(L"Indexed String 1: %s\n", wstr); 88 | 89 | // Toggle LED (cmd 0x80). The first byte is the report number (0x0). 90 | buf[0] = 0x0; 91 | buf[1] = 0x80; 92 | res = hid_write(handle, buf, 65); 93 | 94 | // Request state (cmd 0x81). The first byte is the report number (0x0). 95 | buf[0] = 0x0; 96 | buf[1] = 0x81; 97 | res = hid_write(handle, buf, 65); 98 | 99 | // Read requested state 100 | res = hid_read(handle, buf, 65); 101 | 102 | // Print out the returned buffer. 103 | for (i = 0; i < 4; i++) 104 | printf("buf[%d]: %d\n", i, buf[i]); 105 | 106 | // Finalize the hidapi library 107 | res = hid_exit(); 108 | 109 | return 0; 110 | } 111 | 112 | If you have your own simple test programs which communicate with standard 113 | hardware development boards (such as those from Microchip, TI, Atmel, 114 | FreeScale and others), please consider sending me something like the above 115 | for inclusion into the HIDAPI source. This will help others who have the 116 | same hardware as you do. 117 | 118 | License 119 | ======== 120 | HIDAPI may be used by one of three licenses as outlined in LICENSE.txt. 121 | 122 | Download 123 | ========= 124 | HIDAPI can be downloaded from github 125 | git clone git://github.com/signal11/hidapi.git 126 | 127 | Build Instructions 128 | =================== 129 | 130 | This section is long. Don't be put off by this. It's not long because it's 131 | complicated to build HIDAPI; it's quite the opposite. This section is long 132 | because of the flexibility of HIDAPI and the large number of ways in which 133 | it can be built and used. You will likely pick a single build method. 134 | 135 | HIDAPI can be built in several different ways. If you elect to build a 136 | shared library, you will need to build it from the HIDAPI source 137 | distribution. If you choose instead to embed HIDAPI directly into your 138 | application, you can skip the building and look at the provided platform 139 | Makefiles for guidance. These platform Makefiles are located in linux/ 140 | libusb/ mac/ and windows/ and are called Makefile-manual. In addition, 141 | Visual Studio projects are provided. Even if you're going to embed HIDAPI 142 | into your project, it is still beneficial to build the example programs. 143 | 144 | 145 | Prerequisites: 146 | --------------- 147 | 148 | Linux: 149 | ------- 150 | On Linux, you will need to install development packages for libudev, 151 | libusb and optionally Fox-toolkit (for the test GUI). On 152 | Debian/Ubuntu systems these can be installed by running: 153 | sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev 154 | 155 | If you downloaded the source directly from the git repository (using 156 | git clone), you'll need Autotools: 157 | sudo apt-get install autotools-dev autoconf automake libtool 158 | 159 | FreeBSD: 160 | --------- 161 | On FreeBSD you will need to install GNU make, libiconv, and 162 | optionally Fox-Toolkit (for the test GUI). This is done by running 163 | the following: 164 | pkg_add -r gmake libiconv fox16 165 | 166 | If you downloaded the source directly from the git repository (using 167 | git clone), you'll need Autotools: 168 | pkg_add -r autotools 169 | 170 | Mac: 171 | ----- 172 | On Mac, you will need to install Fox-Toolkit if you wish to build 173 | the Test GUI. There are two ways to do this, and each has a slight 174 | complication. Which method you use depends on your use case. 175 | 176 | If you wish to build the Test GUI just for your own testing on your 177 | own computer, then the easiest method is to install Fox-Toolkit 178 | using ports: 179 | sudo port install fox 180 | 181 | If you wish to build the TestGUI app bundle to redistribute to 182 | others, you will need to install Fox-toolkit from source. This is 183 | because the version of fox that gets installed using ports uses the 184 | ports X11 libraries which are not compatible with the Apple X11 185 | libraries. If you install Fox with ports and then try to distribute 186 | your built app bundle, it will simply fail to run on other systems. 187 | To install Fox-Toolkit manually, download the source package from 188 | http://www.fox-toolkit.org, extract it, and run the following from 189 | within the extracted source: 190 | ./configure && make && make install 191 | 192 | Windows: 193 | --------- 194 | On Windows, if you want to build the test GUI, you will need to get 195 | the hidapi-externals.zip package from the download site. This 196 | contains pre-built binaries for Fox-toolkit. Extract 197 | hidapi-externals.zip just outside of hidapi, so that 198 | hidapi-externals and hidapi are on the same level, as shown: 199 | 200 | Parent_Folder 201 | | 202 | +hidapi 203 | +hidapi-externals 204 | 205 | Again, this step is not required if you do not wish to build the 206 | test GUI. 207 | 208 | 209 | Building HIDAPI into a shared library on Unix Platforms: 210 | --------------------------------------------------------- 211 | 212 | On Unix-like systems such as Linux, FreeBSD, Mac, and even Windows, using 213 | Mingw or Cygwin, the easiest way to build a standard system-installed shared 214 | library is to use the GNU Autotools build system. If you checked out the 215 | source from the git repository, run the following: 216 | 217 | ./bootstrap 218 | ./configure 219 | make 220 | make install <----- as root, or using sudo 221 | 222 | If you downloaded a source package (ie: if you did not run git clone), you 223 | can skip the ./bootstrap step. 224 | 225 | ./configure can take several arguments which control the build. The two most 226 | likely to be used are: 227 | --enable-testgui 228 | Enable build of the Test GUI. This requires Fox toolkit to 229 | be installed. Instructions for installing Fox-Toolkit on 230 | each platform are in the Prerequisites section above. 231 | 232 | --prefix=/usr 233 | Specify where you want the output headers and libraries to 234 | be installed. The example above will put the headers in 235 | /usr/include and the binaries in /usr/lib. The default is to 236 | install into /usr/local which is fine on most systems. 237 | 238 | Building the manual way on Unix platforms: 239 | ------------------------------------------- 240 | 241 | Manual Makefiles are provided mostly to give the user and idea what it takes 242 | to build a program which embeds HIDAPI directly inside of it. These should 243 | really be used as examples only. If you want to build a system-wide shared 244 | library, use the Autotools method described above. 245 | 246 | To build HIDAPI using the manual makefiles, change to the directory 247 | of your platform and run make. For example, on Linux run: 248 | cd linux/ 249 | make -f Makefile-manual 250 | 251 | To build the Test GUI using the manual makefiles: 252 | cd testgui/ 253 | make -f Makefile-manual 254 | 255 | Building on Windows: 256 | --------------------- 257 | 258 | To build the HIDAPI DLL on Windows using Visual Studio, build the .sln file 259 | in the windows/ directory. 260 | 261 | To build the Test GUI on windows using Visual Studio, build the .sln file in 262 | the testgui/ directory. 263 | 264 | To build HIDAPI using MinGW or Cygwin using Autotools, use the instructions 265 | in the section titled "Building HIDAPI into a shared library on Unix 266 | Platforms" above. Note that building the Test GUI with MinGW or Cygwin will 267 | require the Windows procedure in the Prerequisites section above (ie: 268 | hidapi-externals.zip). 269 | 270 | To build HIDAPI using MinGW using the Manual Makefiles, see the section 271 | "Building the manual way on Unix platforms" above. 272 | 273 | HIDAPI can also be built using the Windows DDK (now also called the Windows 274 | Driver Kit or WDK). This method was originally required for the HIDAPI build 275 | but not anymore. However, some users still prefer this method. It is not as 276 | well supported anymore but should still work. Patches are welcome if it does 277 | not. To build using the DDK: 278 | 279 | 1. Install the Windows Driver Kit (WDK) from Microsoft. 280 | 2. From the Start menu, in the Windows Driver Kits folder, select Build 281 | Environments, then your operating system, then the x86 Free Build 282 | Environment (or one that is appropriate for your system). 283 | 3. From the console, change directory to the windows/ddk_build/ directory, 284 | which is part of the HIDAPI distribution. 285 | 4. Type build. 286 | 5. You can find the output files (DLL and LIB) in a subdirectory created 287 | by the build system which is appropriate for your environment. On 288 | Windows XP, this directory is objfre_wxp_x86/i386. 289 | 290 | Cross Compiling 291 | ================ 292 | 293 | This section talks about cross compiling HIDAPI for Linux using autotools. 294 | This is useful for using HIDAPI on embedded Linux targets. These 295 | instructions assume the most raw kind of embedded Linux build, where all 296 | prerequisites will need to be built first. This process will of course vary 297 | based on your embedded Linux build system if you are using one, such as 298 | OpenEmbedded or Buildroot. 299 | 300 | For the purpose of this section, it will be assumed that the following 301 | environment variables are exported. 302 | 303 | $ export STAGING=$HOME/out 304 | $ export HOST=arm-linux 305 | 306 | STAGING and HOST can be modified to suit your setup. 307 | 308 | Prerequisites 309 | -------------- 310 | 311 | Note that the build of libudev is the very basic configuration. 312 | 313 | Build Libusb. From the libusb source directory, run: 314 | ./configure --host=$HOST --prefix=$STAGING 315 | make 316 | make install 317 | 318 | Build libudev. From the libudev source directory, run: 319 | ./configure --disable-gudev --disable-introspection --disable-hwdb \ 320 | --host=$HOST --prefix=$STAGING 321 | make 322 | make install 323 | 324 | Building HIDAPI 325 | ---------------- 326 | 327 | Build HIDAPI: 328 | 329 | PKG_CONFIG_DIR= \ 330 | PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \ 331 | PKG_CONFIG_SYSROOT_DIR=$STAGING \ 332 | ./configure --host=$HOST --prefix=$STAGING 333 | 334 | 335 | Signal 11 Software - 2010-04-11 336 | 2010-07-28 337 | 2011-09-10 338 | 2012-05-01 339 | 2012-07-03 340 | -------------------------------------------------------------------------------- /vendor/hidapi/hidapi/hidapi.h: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | HIDAPI - Multi-Platform library for 3 | communication with HID devices. 4 | 5 | Alan Ott 6 | Signal 11 Software 7 | 8 | 8/22/2009 9 | 10 | Copyright 2009, All Rights Reserved. 11 | 12 | At the discretion of the user of this library, 13 | this software may be licensed under the terms of the 14 | GNU General Public License v3, a BSD-Style license, or the 15 | original HIDAPI license as outlined in the LICENSE.txt, 16 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt 17 | files located at the root of the source distribution. 18 | These files may also be found in the public source 19 | code repository located at: 20 | http://github.com/signal11/hidapi . 21 | ********************************************************/ 22 | 23 | /** @file 24 | * @defgroup API hidapi API 25 | */ 26 | 27 | #ifndef HIDAPI_H__ 28 | #define HIDAPI_H__ 29 | 30 | #include 31 | 32 | #ifdef _WIN32 33 | #define HID_API_EXPORT __declspec(dllexport) 34 | #define HID_API_CALL 35 | #else 36 | #define HID_API_EXPORT /**< API export macro */ 37 | #define HID_API_CALL /**< API call macro */ 38 | #endif 39 | 40 | #define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | struct hid_device_; 46 | typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ 47 | 48 | /** hidapi info structure */ 49 | struct hid_device_info { 50 | /** Platform-specific device path */ 51 | char *path; 52 | /** Device Vendor ID */ 53 | unsigned short vendor_id; 54 | /** Device Product ID */ 55 | unsigned short product_id; 56 | /** Serial Number */ 57 | wchar_t *serial_number; 58 | /** Device Release Number in binary-coded decimal, 59 | also known as Device Version Number */ 60 | unsigned short release_number; 61 | /** Manufacturer String */ 62 | wchar_t *manufacturer_string; 63 | /** Product string */ 64 | wchar_t *product_string; 65 | /** Usage Page for this Device/Interface 66 | (Windows/Mac only). */ 67 | unsigned short usage_page; 68 | /** Usage for this Device/Interface 69 | (Windows/Mac only).*/ 70 | unsigned short usage; 71 | /** The USB interface which this logical device 72 | represents. Valid on both Linux implementations 73 | in all cases, and valid on the Windows implementation 74 | only if the device contains more than one interface. */ 75 | int interface_number; 76 | 77 | /** Pointer to the next device */ 78 | struct hid_device_info *next; 79 | }; 80 | 81 | 82 | /** @brief Initialize the HIDAPI library. 83 | 84 | This function initializes the HIDAPI library. Calling it is not 85 | strictly necessary, as it will be called automatically by 86 | hid_enumerate() and any of the hid_open_*() functions if it is 87 | needed. This function should be called at the beginning of 88 | execution however, if there is a chance of HIDAPI handles 89 | being opened by different threads simultaneously. 90 | 91 | @ingroup API 92 | 93 | @returns 94 | This function returns 0 on success and -1 on error. 95 | */ 96 | int HID_API_EXPORT HID_API_CALL hid_init(void); 97 | 98 | /** @brief Finalize the HIDAPI library. 99 | 100 | This function frees all of the static data associated with 101 | HIDAPI. It should be called at the end of execution to avoid 102 | memory leaks. 103 | 104 | @ingroup API 105 | 106 | @returns 107 | This function returns 0 on success and -1 on error. 108 | */ 109 | int HID_API_EXPORT HID_API_CALL hid_exit(void); 110 | 111 | /** @brief Enumerate the HID Devices. 112 | 113 | This function returns a linked list of all the HID devices 114 | attached to the system which match vendor_id and product_id. 115 | If @p vendor_id is set to 0 then any vendor matches. 116 | If @p product_id is set to 0 then any product matches. 117 | If @p vendor_id and @p product_id are both set to 0, then 118 | all HID devices will be returned. 119 | 120 | @ingroup API 121 | @param vendor_id The Vendor ID (VID) of the types of device 122 | to open. 123 | @param product_id The Product ID (PID) of the types of 124 | device to open. 125 | 126 | @returns 127 | This function returns a pointer to a linked list of type 128 | struct #hid_device, containing information about the HID devices 129 | attached to the system, or NULL in the case of failure. Free 130 | this linked list by calling hid_free_enumeration(). 131 | */ 132 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); 133 | 134 | /** @brief Free an enumeration Linked List 135 | 136 | This function frees a linked list created by hid_enumerate(). 137 | 138 | @ingroup API 139 | @param devs Pointer to a list of struct_device returned from 140 | hid_enumerate(). 141 | */ 142 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); 143 | 144 | /** @brief Open a HID device using a Vendor ID (VID), Product ID 145 | (PID) and optionally a serial number. 146 | 147 | If @p serial_number is NULL, the first device with the 148 | specified VID and PID is opened. 149 | 150 | @ingroup API 151 | @param vendor_id The Vendor ID (VID) of the device to open. 152 | @param product_id The Product ID (PID) of the device to open. 153 | @param serial_number The Serial Number of the device to open 154 | (Optionally NULL). 155 | 156 | @returns 157 | This function returns a pointer to a #hid_device object on 158 | success or NULL on failure. 159 | */ 160 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); 161 | 162 | /** @brief Open a HID device by its path name. 163 | 164 | The path name be determined by calling hid_enumerate(), or a 165 | platform-specific path name can be used (eg: /dev/hidraw0 on 166 | Linux). 167 | 168 | @ingroup API 169 | @param path The path name of the device to open 170 | 171 | @returns 172 | This function returns a pointer to a #hid_device object on 173 | success or NULL on failure. 174 | */ 175 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); 176 | 177 | /** @brief Write an Output report to a HID device. 178 | 179 | The first byte of @p data[] must contain the Report ID. For 180 | devices which only support a single report, this must be set 181 | to 0x0. The remaining bytes contain the report data. Since 182 | the Report ID is mandatory, calls to hid_write() will always 183 | contain one more byte than the report contains. For example, 184 | if a hid report is 16 bytes long, 17 bytes must be passed to 185 | hid_write(), the Report ID (or 0x0, for devices with a 186 | single report), followed by the report data (16 bytes). In 187 | this example, the length passed in would be 17. 188 | 189 | hid_write() will send the data on the first OUT endpoint, if 190 | one exists. If it does not, it will send the data through 191 | the Control Endpoint (Endpoint 0). 192 | 193 | @ingroup API 194 | @param device A device handle returned from hid_open(). 195 | @param data The data to send, including the report number as 196 | the first byte. 197 | @param length The length in bytes of the data to send. 198 | 199 | @returns 200 | This function returns the actual number of bytes written and 201 | -1 on error. 202 | */ 203 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); 204 | 205 | /** @brief Read an Input report from a HID device with timeout. 206 | 207 | Input reports are returned 208 | to the host through the INTERRUPT IN endpoint. The first byte will 209 | contain the Report number if the device uses numbered reports. 210 | 211 | @ingroup API 212 | @param device A device handle returned from hid_open(). 213 | @param data A buffer to put the read data into. 214 | @param length The number of bytes to read. For devices with 215 | multiple reports, make sure to read an extra byte for 216 | the report number. 217 | @param milliseconds timeout in milliseconds or -1 for blocking wait. 218 | 219 | @returns 220 | This function returns the actual number of bytes read and 221 | -1 on error. If no packet was available to be read within 222 | the timeout period, this function returns 0. 223 | */ 224 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); 225 | 226 | /** @brief Read an Input report from a HID device. 227 | 228 | Input reports are returned 229 | to the host through the INTERRUPT IN endpoint. The first byte will 230 | contain the Report number if the device uses numbered reports. 231 | 232 | @ingroup API 233 | @param device A device handle returned from hid_open(). 234 | @param data A buffer to put the read data into. 235 | @param length The number of bytes to read. For devices with 236 | multiple reports, make sure to read an extra byte for 237 | the report number. 238 | 239 | @returns 240 | This function returns the actual number of bytes read and 241 | -1 on error. If no packet was available to be read and 242 | the handle is in non-blocking mode, this function returns 0. 243 | */ 244 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); 245 | 246 | /** @brief Set the device handle to be non-blocking. 247 | 248 | In non-blocking mode calls to hid_read() will return 249 | immediately with a value of 0 if there is no data to be 250 | read. In blocking mode, hid_read() will wait (block) until 251 | there is data to read before returning. 252 | 253 | Nonblocking can be turned on and off at any time. 254 | 255 | @ingroup API 256 | @param device A device handle returned from hid_open(). 257 | @param nonblock enable or not the nonblocking reads 258 | - 1 to enable nonblocking 259 | - 0 to disable nonblocking. 260 | 261 | @returns 262 | This function returns 0 on success and -1 on error. 263 | */ 264 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); 265 | 266 | /** @brief Send a Feature report to the device. 267 | 268 | Feature reports are sent over the Control endpoint as a 269 | Set_Report transfer. The first byte of @p data[] must 270 | contain the Report ID. For devices which only support a 271 | single report, this must be set to 0x0. The remaining bytes 272 | contain the report data. Since the Report ID is mandatory, 273 | calls to hid_send_feature_report() will always contain one 274 | more byte than the report contains. For example, if a hid 275 | report is 16 bytes long, 17 bytes must be passed to 276 | hid_send_feature_report(): the Report ID (or 0x0, for 277 | devices which do not use numbered reports), followed by the 278 | report data (16 bytes). In this example, the length passed 279 | in would be 17. 280 | 281 | @ingroup API 282 | @param device A device handle returned from hid_open(). 283 | @param data The data to send, including the report number as 284 | the first byte. 285 | @param length The length in bytes of the data to send, including 286 | the report number. 287 | 288 | @returns 289 | This function returns the actual number of bytes written and 290 | -1 on error. 291 | */ 292 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); 293 | 294 | /** @brief Get a feature report from a HID device. 295 | 296 | Set the first byte of @p data[] to the Report ID of the 297 | report to be read. Make sure to allow space for this 298 | extra byte in @p data[]. Upon return, the first byte will 299 | still contain the Report ID, and the report data will 300 | start in data[1]. 301 | 302 | @ingroup API 303 | @param device A device handle returned from hid_open(). 304 | @param data A buffer to put the read data into, including 305 | the Report ID. Set the first byte of @p data[] to the 306 | Report ID of the report to be read, or set it to zero 307 | if your device does not use numbered reports. 308 | @param length The number of bytes to read, including an 309 | extra byte for the report ID. The buffer can be longer 310 | than the actual report. 311 | 312 | @returns 313 | This function returns the number of bytes read plus 314 | one for the report ID (which is still in the first 315 | byte), or -1 on error. 316 | */ 317 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); 318 | 319 | /** @brief Close a HID device. 320 | 321 | @ingroup API 322 | @param device A device handle returned from hid_open(). 323 | */ 324 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); 325 | 326 | /** @brief Get The Manufacturer String from a HID device. 327 | 328 | @ingroup API 329 | @param device A device handle returned from hid_open(). 330 | @param string A wide string buffer to put the data into. 331 | @param maxlen The length of the buffer in multiples of wchar_t. 332 | 333 | @returns 334 | This function returns 0 on success and -1 on error. 335 | */ 336 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); 337 | 338 | /** @brief Get The Product String from a HID device. 339 | 340 | @ingroup API 341 | @param device A device handle returned from hid_open(). 342 | @param string A wide string buffer to put the data into. 343 | @param maxlen The length of the buffer in multiples of wchar_t. 344 | 345 | @returns 346 | This function returns 0 on success and -1 on error. 347 | */ 348 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); 349 | 350 | /** @brief Get The Serial Number String from a HID device. 351 | 352 | @ingroup API 353 | @param device A device handle returned from hid_open(). 354 | @param string A wide string buffer to put the data into. 355 | @param maxlen The length of the buffer in multiples of wchar_t. 356 | 357 | @returns 358 | This function returns 0 on success and -1 on error. 359 | */ 360 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); 361 | 362 | /** @brief Get a string from a HID device, based on its string index. 363 | 364 | @ingroup API 365 | @param device A device handle returned from hid_open(). 366 | @param string_index The index of the string to get. 367 | @param string A wide string buffer to put the data into. 368 | @param maxlen The length of the buffer in multiples of wchar_t. 369 | 370 | @returns 371 | This function returns 0 on success and -1 on error. 372 | */ 373 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); 374 | 375 | /** @brief Get a string describing the last error which occurred. 376 | 377 | @ingroup API 378 | @param device A device handle returned from hid_open(). 379 | 380 | @returns 381 | This function returns a string containing the last error 382 | which occurred or NULL if none has occurred. 383 | */ 384 | HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); 385 | 386 | #ifdef __cplusplus 387 | } 388 | #endif 389 | 390 | #endif 391 | 392 | -------------------------------------------------------------------------------- /vendor/hidapi/libusb/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # CMake build file for libusb hidapi 3 | # 4 | 5 | add_library(hidapi hid.c) 6 | 7 | find_package(PkgConfig) 8 | pkg_check_modules(LIBUSB_1 REQUIRED libusb-1.0) 9 | 10 | include_directories(${LIBUSB_1_INCLUDE_DIRS}) 11 | 12 | target_link_libraries(hidapi 13 | ${LIBUSB_1_LIBRARIES} 14 | rt) 15 | -------------------------------------------------------------------------------- /vendor/hidapi/mac/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # CMake build file for Mac OS X hidapi 3 | # 4 | 5 | add_library(hidapi hid.c) 6 | 7 | find_library(COREFOUNDATION_LIBRARY CoreFoundation) 8 | find_library(IOKIT_LIBRARY IOKit) 9 | 10 | target_link_libraries(hidapi 11 | ${COREFOUNDATION_LIBRARY} 12 | ${IOKIT_LIBRARY}) 13 | -------------------------------------------------------------------------------- /vendor/hidapi/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # CMake build file for Windows hidapi 3 | # 4 | 5 | add_library(hidapi hid.c) 6 | target_link_libraries(hidapi setupapi) -------------------------------------------------------------------------------- /vendor/hidapi/windows/hid.c: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | HIDAPI - Multi-Platform library for 3 | communication with HID devices. 4 | 5 | Alan Ott 6 | Signal 11 Software 7 | 8 | 8/22/2009 9 | 10 | Copyright 2009, All Rights Reserved. 11 | 12 | At the discretion of the user of this library, 13 | this software may be licensed under the terms of the 14 | GNU General Public License v3, a BSD-Style license, or the 15 | original HIDAPI license as outlined in the LICENSE.txt, 16 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt 17 | files located at the root of the source distribution. 18 | These files may also be found in the public source 19 | code repository located at: 20 | http://github.com/signal11/hidapi . 21 | ********************************************************/ 22 | 23 | #include 24 | 25 | #ifndef _NTDEF_ 26 | typedef LONG NTSTATUS; 27 | #endif 28 | 29 | #ifdef __MINGW32__ 30 | #include 31 | #include 32 | #endif 33 | 34 | #ifdef __CYGWIN__ 35 | #include 36 | #define _wcsdup wcsdup 37 | #endif 38 | 39 | /* The maximum number of characters that can be passed into the 40 | HidD_Get*String() functions without it failing.*/ 41 | #define MAX_STRING_WCHARS 0xFFF 42 | 43 | /*#define HIDAPI_USE_DDK*/ 44 | 45 | #ifdef __cplusplus 46 | extern "C" { 47 | #endif 48 | #include 49 | #include 50 | #ifdef HIDAPI_USE_DDK 51 | #include 52 | #endif 53 | 54 | /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ 55 | #define HID_OUT_CTL_CODE(id) \ 56 | CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) 57 | #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) 58 | 59 | #ifdef __cplusplus 60 | } /* extern "C" */ 61 | #endif 62 | 63 | #include 64 | #include 65 | 66 | 67 | #include "hidapi.h" 68 | 69 | #undef MIN 70 | #define MIN(x,y) ((x) < (y)? (x): (y)) 71 | 72 | #ifdef _MSC_VER 73 | /* Thanks Microsoft, but I know how to use strncpy(). */ 74 | #pragma warning(disable:4996) 75 | #endif 76 | 77 | #ifdef __cplusplus 78 | extern "C" { 79 | #endif 80 | 81 | #ifndef HIDAPI_USE_DDK 82 | /* Since we're not building with the DDK, and the HID header 83 | files aren't part of the SDK, we have to define all this 84 | stuff here. In lookup_functions(), the function pointers 85 | defined below are set. */ 86 | typedef struct _HIDD_ATTRIBUTES{ 87 | ULONG Size; 88 | USHORT VendorID; 89 | USHORT ProductID; 90 | USHORT VersionNumber; 91 | } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; 92 | 93 | typedef USHORT USAGE; 94 | typedef struct _HIDP_CAPS { 95 | USAGE Usage; 96 | USAGE UsagePage; 97 | USHORT InputReportByteLength; 98 | USHORT OutputReportByteLength; 99 | USHORT FeatureReportByteLength; 100 | USHORT Reserved[17]; 101 | USHORT fields_not_used_by_hidapi[10]; 102 | } HIDP_CAPS, *PHIDP_CAPS; 103 | typedef void* PHIDP_PREPARSED_DATA; 104 | #define HIDP_STATUS_SUCCESS 0x110000 105 | 106 | typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); 107 | typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); 108 | typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); 109 | typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); 110 | typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); 111 | typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); 112 | typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); 113 | typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); 114 | typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); 115 | typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); 116 | typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); 117 | 118 | static HidD_GetAttributes_ HidD_GetAttributes; 119 | static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; 120 | static HidD_GetManufacturerString_ HidD_GetManufacturerString; 121 | static HidD_GetProductString_ HidD_GetProductString; 122 | static HidD_SetFeature_ HidD_SetFeature; 123 | static HidD_GetFeature_ HidD_GetFeature; 124 | static HidD_GetIndexedString_ HidD_GetIndexedString; 125 | static HidD_GetPreparsedData_ HidD_GetPreparsedData; 126 | static HidD_FreePreparsedData_ HidD_FreePreparsedData; 127 | static HidP_GetCaps_ HidP_GetCaps; 128 | static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; 129 | 130 | static HMODULE lib_handle = NULL; 131 | static BOOLEAN initialized = FALSE; 132 | #endif /* HIDAPI_USE_DDK */ 133 | 134 | struct hid_device_ { 135 | HANDLE device_handle; 136 | BOOL blocking; 137 | USHORT output_report_length; 138 | size_t input_report_length; 139 | void *last_error_str; 140 | DWORD last_error_num; 141 | BOOL read_pending; 142 | char *read_buf; 143 | OVERLAPPED ol; 144 | }; 145 | 146 | static hid_device *new_hid_device() 147 | { 148 | hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); 149 | dev->device_handle = INVALID_HANDLE_VALUE; 150 | dev->blocking = TRUE; 151 | dev->output_report_length = 0; 152 | dev->input_report_length = 0; 153 | dev->last_error_str = NULL; 154 | dev->last_error_num = 0; 155 | dev->read_pending = FALSE; 156 | dev->read_buf = NULL; 157 | memset(&dev->ol, 0, sizeof(dev->ol)); 158 | dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); 159 | 160 | return dev; 161 | } 162 | 163 | static void free_hid_device(hid_device *dev) 164 | { 165 | CloseHandle(dev->ol.hEvent); 166 | CloseHandle(dev->device_handle); 167 | LocalFree(dev->last_error_str); 168 | free(dev->read_buf); 169 | free(dev); 170 | } 171 | 172 | static void register_error(hid_device *device, const char *op) 173 | { 174 | WCHAR *ptr, *msg; 175 | 176 | FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | 177 | FORMAT_MESSAGE_FROM_SYSTEM | 178 | FORMAT_MESSAGE_IGNORE_INSERTS, 179 | NULL, 180 | GetLastError(), 181 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 182 | (LPVOID)&msg, 0/*sz*/, 183 | NULL); 184 | 185 | /* Get rid of the CR and LF that FormatMessage() sticks at the 186 | end of the message. Thanks Microsoft! */ 187 | ptr = msg; 188 | while (*ptr) { 189 | if (*ptr == '\r') { 190 | *ptr = 0x0000; 191 | break; 192 | } 193 | ptr++; 194 | } 195 | 196 | /* Store the message off in the Device entry so that 197 | the hid_error() function can pick it up. */ 198 | LocalFree(device->last_error_str); 199 | device->last_error_str = msg; 200 | } 201 | 202 | #ifndef HIDAPI_USE_DDK 203 | static int lookup_functions() 204 | { 205 | lib_handle = LoadLibraryA("hid.dll"); 206 | if (lib_handle) { 207 | #define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; 208 | RESOLVE(HidD_GetAttributes); 209 | RESOLVE(HidD_GetSerialNumberString); 210 | RESOLVE(HidD_GetManufacturerString); 211 | RESOLVE(HidD_GetProductString); 212 | RESOLVE(HidD_SetFeature); 213 | RESOLVE(HidD_GetFeature); 214 | RESOLVE(HidD_GetIndexedString); 215 | RESOLVE(HidD_GetPreparsedData); 216 | RESOLVE(HidD_FreePreparsedData); 217 | RESOLVE(HidP_GetCaps); 218 | RESOLVE(HidD_SetNumInputBuffers); 219 | #undef RESOLVE 220 | } 221 | else 222 | return -1; 223 | 224 | return 0; 225 | } 226 | #endif 227 | 228 | static HANDLE open_device(const char *path, BOOL enumerate) 229 | { 230 | HANDLE handle; 231 | DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ); 232 | DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; 233 | 234 | handle = CreateFileA(path, 235 | desired_access, 236 | share_mode, 237 | NULL, 238 | OPEN_EXISTING, 239 | FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ 240 | 0); 241 | 242 | return handle; 243 | } 244 | 245 | int HID_API_EXPORT hid_init(void) 246 | { 247 | #ifndef HIDAPI_USE_DDK 248 | if (!initialized) { 249 | if (lookup_functions() < 0) { 250 | hid_exit(); 251 | return -1; 252 | } 253 | initialized = TRUE; 254 | } 255 | #endif 256 | return 0; 257 | } 258 | 259 | int HID_API_EXPORT hid_exit(void) 260 | { 261 | #ifndef HIDAPI_USE_DDK 262 | if (lib_handle) 263 | FreeLibrary(lib_handle); 264 | lib_handle = NULL; 265 | initialized = FALSE; 266 | #endif 267 | return 0; 268 | } 269 | 270 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) 271 | { 272 | BOOL res; 273 | struct hid_device_info *root = NULL; /* return object */ 274 | struct hid_device_info *cur_dev = NULL; 275 | 276 | /* Windows objects for interacting with the driver. */ 277 | GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; 278 | SP_DEVINFO_DATA devinfo_data; 279 | SP_DEVICE_INTERFACE_DATA device_interface_data; 280 | SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; 281 | HDEVINFO device_info_set = INVALID_HANDLE_VALUE; 282 | int device_index = 0; 283 | int i; 284 | 285 | if (hid_init() < 0) 286 | return NULL; 287 | 288 | /* Initialize the Windows objects. */ 289 | memset(&devinfo_data, 0x0, sizeof(devinfo_data)); 290 | devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); 291 | device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 292 | 293 | /* Get information for all the devices belonging to the HID class. */ 294 | device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 295 | 296 | /* Iterate over each device in the HID class, looking for the right one. */ 297 | 298 | for (;;) { 299 | HANDLE write_handle = INVALID_HANDLE_VALUE; 300 | DWORD required_size = 0; 301 | HIDD_ATTRIBUTES attrib; 302 | 303 | res = SetupDiEnumDeviceInterfaces(device_info_set, 304 | NULL, 305 | &InterfaceClassGuid, 306 | device_index, 307 | &device_interface_data); 308 | 309 | if (!res) { 310 | /* A return of FALSE from this function means that 311 | there are no more devices. */ 312 | break; 313 | } 314 | 315 | /* Call with 0-sized detail size, and let the function 316 | tell us how long the detail struct needs to be. The 317 | size is put in &required_size. */ 318 | res = SetupDiGetDeviceInterfaceDetailA(device_info_set, 319 | &device_interface_data, 320 | NULL, 321 | 0, 322 | &required_size, 323 | NULL); 324 | 325 | /* Allocate a long enough structure for device_interface_detail_data. */ 326 | device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); 327 | device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); 328 | 329 | /* Get the detailed data for this device. The detail data gives us 330 | the device path for this device, which is then passed into 331 | CreateFile() to get a handle to the device. */ 332 | res = SetupDiGetDeviceInterfaceDetailA(device_info_set, 333 | &device_interface_data, 334 | device_interface_detail_data, 335 | required_size, 336 | NULL, 337 | NULL); 338 | 339 | if (!res) { 340 | /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); 341 | Continue to the next device. */ 342 | goto cont; 343 | } 344 | 345 | /* Make sure this device is of Setup Class "HIDClass" and has a 346 | driver bound to it. */ 347 | for (i = 0; ; i++) { 348 | char driver_name[256]; 349 | 350 | /* Populate devinfo_data. This function will return failure 351 | when there are no more interfaces left. */ 352 | res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); 353 | if (!res) 354 | goto cont; 355 | 356 | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, 357 | SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); 358 | if (!res) 359 | goto cont; 360 | 361 | if (strcmp(driver_name, "HIDClass") == 0) { 362 | /* See if there's a driver bound. */ 363 | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, 364 | SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); 365 | if (res) 366 | break; 367 | } 368 | } 369 | 370 | //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); 371 | 372 | /* Open a handle to the device */ 373 | write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); 374 | 375 | /* Check validity of write_handle. */ 376 | if (write_handle == INVALID_HANDLE_VALUE) { 377 | /* Unable to open the device. */ 378 | //register_error(dev, "CreateFile"); 379 | goto cont_close; 380 | } 381 | 382 | 383 | /* Get the Vendor ID and Product ID for this device. */ 384 | attrib.Size = sizeof(HIDD_ATTRIBUTES); 385 | HidD_GetAttributes(write_handle, &attrib); 386 | //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); 387 | 388 | /* Check the VID/PID to see if we should add this 389 | device to the enumeration list. */ 390 | if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && 391 | (product_id == 0x0 || attrib.ProductID == product_id)) { 392 | 393 | #define WSTR_LEN 512 394 | const char *str; 395 | struct hid_device_info *tmp; 396 | PHIDP_PREPARSED_DATA pp_data = NULL; 397 | HIDP_CAPS caps; 398 | BOOLEAN res; 399 | NTSTATUS nt_res; 400 | wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ 401 | size_t len; 402 | 403 | /* VID/PID match. Create the record. */ 404 | tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); 405 | if (cur_dev) { 406 | cur_dev->next = tmp; 407 | } 408 | else { 409 | root = tmp; 410 | } 411 | cur_dev = tmp; 412 | 413 | /* Get the Usage Page and Usage for this device. */ 414 | res = HidD_GetPreparsedData(write_handle, &pp_data); 415 | if (res) { 416 | nt_res = HidP_GetCaps(pp_data, &caps); 417 | if (nt_res == HIDP_STATUS_SUCCESS) { 418 | cur_dev->usage_page = caps.UsagePage; 419 | cur_dev->usage = caps.Usage; 420 | } 421 | 422 | HidD_FreePreparsedData(pp_data); 423 | } 424 | 425 | /* Fill out the record */ 426 | cur_dev->next = NULL; 427 | str = device_interface_detail_data->DevicePath; 428 | if (str) { 429 | len = strlen(str); 430 | cur_dev->path = (char*) calloc(len+1, sizeof(char)); 431 | strncpy(cur_dev->path, str, len+1); 432 | cur_dev->path[len] = '\0'; 433 | } 434 | else 435 | cur_dev->path = NULL; 436 | 437 | /* Serial Number */ 438 | res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); 439 | wstr[WSTR_LEN-1] = 0x0000; 440 | if (res) { 441 | cur_dev->serial_number = _wcsdup(wstr); 442 | } 443 | 444 | /* Manufacturer String */ 445 | res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); 446 | wstr[WSTR_LEN-1] = 0x0000; 447 | if (res) { 448 | cur_dev->manufacturer_string = _wcsdup(wstr); 449 | } 450 | 451 | /* Product String */ 452 | res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); 453 | wstr[WSTR_LEN-1] = 0x0000; 454 | if (res) { 455 | cur_dev->product_string = _wcsdup(wstr); 456 | } 457 | 458 | /* VID/PID */ 459 | cur_dev->vendor_id = attrib.VendorID; 460 | cur_dev->product_id = attrib.ProductID; 461 | 462 | /* Release Number */ 463 | cur_dev->release_number = attrib.VersionNumber; 464 | 465 | /* Interface Number. It can sometimes be parsed out of the path 466 | on Windows if a device has multiple interfaces. See 467 | http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or 468 | search for "Hardware IDs for HID Devices" at MSDN. If it's not 469 | in the path, it's set to -1. */ 470 | cur_dev->interface_number = -1; 471 | if (cur_dev->path) { 472 | char *interface_component = strstr(cur_dev->path, "&mi_"); 473 | if (interface_component) { 474 | char *hex_str = interface_component + 4; 475 | char *endptr = NULL; 476 | cur_dev->interface_number = strtol(hex_str, &endptr, 16); 477 | if (endptr == hex_str) { 478 | /* The parsing failed. Set interface_number to -1. */ 479 | cur_dev->interface_number = -1; 480 | } 481 | } 482 | } 483 | } 484 | 485 | cont_close: 486 | CloseHandle(write_handle); 487 | cont: 488 | /* We no longer need the detail data. It can be freed */ 489 | free(device_interface_detail_data); 490 | 491 | device_index++; 492 | 493 | } 494 | 495 | /* Close the device information handle. */ 496 | SetupDiDestroyDeviceInfoList(device_info_set); 497 | 498 | return root; 499 | 500 | } 501 | 502 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) 503 | { 504 | /* TODO: Merge this with the Linux version. This function is platform-independent. */ 505 | struct hid_device_info *d = devs; 506 | while (d) { 507 | struct hid_device_info *next = d->next; 508 | free(d->path); 509 | free(d->serial_number); 510 | free(d->manufacturer_string); 511 | free(d->product_string); 512 | free(d); 513 | d = next; 514 | } 515 | } 516 | 517 | 518 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 519 | { 520 | /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ 521 | struct hid_device_info *devs, *cur_dev; 522 | const char *path_to_open = NULL; 523 | hid_device *handle = NULL; 524 | 525 | devs = hid_enumerate(vendor_id, product_id); 526 | cur_dev = devs; 527 | while (cur_dev) { 528 | if (cur_dev->vendor_id == vendor_id && 529 | cur_dev->product_id == product_id) { 530 | if (serial_number) { 531 | if (wcscmp(serial_number, cur_dev->serial_number) == 0) { 532 | path_to_open = cur_dev->path; 533 | break; 534 | } 535 | } 536 | else { 537 | path_to_open = cur_dev->path; 538 | break; 539 | } 540 | } 541 | cur_dev = cur_dev->next; 542 | } 543 | 544 | if (path_to_open) { 545 | /* Open the device */ 546 | handle = hid_open_path(path_to_open); 547 | } 548 | 549 | hid_free_enumeration(devs); 550 | 551 | return handle; 552 | } 553 | 554 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) 555 | { 556 | hid_device *dev; 557 | HIDP_CAPS caps; 558 | PHIDP_PREPARSED_DATA pp_data = NULL; 559 | BOOLEAN res; 560 | NTSTATUS nt_res; 561 | 562 | if (hid_init() < 0) { 563 | return NULL; 564 | } 565 | 566 | dev = new_hid_device(); 567 | 568 | /* Open a handle to the device */ 569 | dev->device_handle = open_device(path, FALSE); 570 | 571 | /* Check validity of write_handle. */ 572 | if (dev->device_handle == INVALID_HANDLE_VALUE) { 573 | /* Unable to open the device. */ 574 | register_error(dev, "CreateFile"); 575 | goto err; 576 | } 577 | 578 | /* Set the Input Report buffer size to 64 reports. */ 579 | res = HidD_SetNumInputBuffers(dev->device_handle, 64); 580 | if (!res) { 581 | register_error(dev, "HidD_SetNumInputBuffers"); 582 | goto err; 583 | } 584 | 585 | /* Get the Input Report length for the device. */ 586 | res = HidD_GetPreparsedData(dev->device_handle, &pp_data); 587 | if (!res) { 588 | register_error(dev, "HidD_GetPreparsedData"); 589 | goto err; 590 | } 591 | nt_res = HidP_GetCaps(pp_data, &caps); 592 | if (nt_res != HIDP_STATUS_SUCCESS) { 593 | register_error(dev, "HidP_GetCaps"); 594 | goto err_pp_data; 595 | } 596 | dev->output_report_length = caps.OutputReportByteLength; 597 | dev->input_report_length = caps.InputReportByteLength; 598 | HidD_FreePreparsedData(pp_data); 599 | 600 | dev->read_buf = (char*) malloc(dev->input_report_length); 601 | 602 | return dev; 603 | 604 | err_pp_data: 605 | HidD_FreePreparsedData(pp_data); 606 | err: 607 | free_hid_device(dev); 608 | return NULL; 609 | } 610 | 611 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) 612 | { 613 | DWORD bytes_written; 614 | BOOL res; 615 | 616 | OVERLAPPED ol; 617 | unsigned char *buf; 618 | memset(&ol, 0, sizeof(ol)); 619 | 620 | /* Make sure the right number of bytes are passed to WriteFile. Windows 621 | expects the number of bytes which are in the _longest_ report (plus 622 | one for the report number) bytes even if the data is a report 623 | which is shorter than that. Windows gives us this value in 624 | caps.OutputReportByteLength. If a user passes in fewer bytes than this, 625 | create a temporary buffer which is the proper size. */ 626 | if (length >= dev->output_report_length) { 627 | /* The user passed the right number of bytes. Use the buffer as-is. */ 628 | buf = (unsigned char *) data; 629 | } else { 630 | /* Create a temporary buffer and copy the user's data 631 | into it, padding the rest with zeros. */ 632 | buf = (unsigned char *) malloc(dev->output_report_length); 633 | memcpy(buf, data, length); 634 | memset(buf + length, 0, dev->output_report_length - length); 635 | length = dev->output_report_length; 636 | } 637 | 638 | res = WriteFile(dev->device_handle, buf, length, NULL, &ol); 639 | 640 | if (!res) { 641 | if (GetLastError() != ERROR_IO_PENDING) { 642 | /* WriteFile() failed. Return error. */ 643 | register_error(dev, "WriteFile"); 644 | bytes_written = -1; 645 | goto end_of_function; 646 | } 647 | } 648 | 649 | /* Wait here until the write is done. This makes 650 | hid_write() synchronous. */ 651 | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); 652 | if (!res) { 653 | /* The Write operation failed. */ 654 | register_error(dev, "WriteFile"); 655 | bytes_written = -1; 656 | goto end_of_function; 657 | } 658 | 659 | end_of_function: 660 | if (buf != data) 661 | free(buf); 662 | 663 | return bytes_written; 664 | } 665 | 666 | 667 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 668 | { 669 | DWORD bytes_read = 0; 670 | size_t copy_len = 0; 671 | BOOL res; 672 | 673 | /* Copy the handle for convenience. */ 674 | HANDLE ev = dev->ol.hEvent; 675 | 676 | if (!dev->read_pending) { 677 | /* Start an Overlapped I/O read. */ 678 | dev->read_pending = TRUE; 679 | memset(dev->read_buf, 0, dev->input_report_length); 680 | ResetEvent(ev); 681 | res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); 682 | 683 | if (!res) { 684 | if (GetLastError() != ERROR_IO_PENDING) { 685 | /* ReadFile() has failed. 686 | Clean up and return error. */ 687 | CancelIo(dev->device_handle); 688 | dev->read_pending = FALSE; 689 | goto end_of_function; 690 | } 691 | } 692 | } 693 | 694 | if (milliseconds >= 0) { 695 | /* See if there is any data yet. */ 696 | res = WaitForSingleObject(ev, milliseconds); 697 | if (res != WAIT_OBJECT_0) { 698 | /* There was no data this time. Return zero bytes available, 699 | but leave the Overlapped I/O running. */ 700 | return 0; 701 | } 702 | } 703 | 704 | /* Either WaitForSingleObject() told us that ReadFile has completed, or 705 | we are in non-blocking mode. Get the number of bytes read. The actual 706 | data has been copied to the data[] array which was passed to ReadFile(). */ 707 | res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); 708 | 709 | /* Set pending back to false, even if GetOverlappedResult() returned error. */ 710 | dev->read_pending = FALSE; 711 | 712 | if (res && bytes_read > 0) { 713 | if (dev->read_buf[0] == 0x0) { 714 | /* If report numbers aren't being used, but Windows sticks a report 715 | number (0x0) on the beginning of the report anyway. To make this 716 | work like the other platforms, and to make it work more like the 717 | HID spec, we'll skip over this byte. */ 718 | bytes_read--; 719 | copy_len = length > bytes_read ? bytes_read : length; 720 | memcpy(data, dev->read_buf+1, copy_len); 721 | } 722 | else { 723 | /* Copy the whole buffer, report number and all. */ 724 | copy_len = length > bytes_read ? bytes_read : length; 725 | memcpy(data, dev->read_buf, copy_len); 726 | } 727 | } 728 | 729 | end_of_function: 730 | if (!res) { 731 | register_error(dev, "GetOverlappedResult"); 732 | return -1; 733 | } 734 | 735 | return copy_len; 736 | } 737 | 738 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) 739 | { 740 | return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); 741 | } 742 | 743 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) 744 | { 745 | dev->blocking = !nonblock; 746 | return 0; /* Success */ 747 | } 748 | 749 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 750 | { 751 | BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); 752 | if (!res) { 753 | register_error(dev, "HidD_SetFeature"); 754 | return -1; 755 | } 756 | 757 | return length; 758 | } 759 | 760 | 761 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 762 | { 763 | BOOL res; 764 | #if 0 765 | res = HidD_GetFeature(dev->device_handle, data, length); 766 | if (!res) { 767 | register_error(dev, "HidD_GetFeature"); 768 | return -1; 769 | } 770 | return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ 771 | #else 772 | DWORD bytes_returned; 773 | 774 | OVERLAPPED ol; 775 | memset(&ol, 0, sizeof(ol)); 776 | 777 | res = DeviceIoControl(dev->device_handle, 778 | IOCTL_HID_GET_FEATURE, 779 | data, length, 780 | data, length, 781 | &bytes_returned, &ol); 782 | 783 | if (!res) { 784 | if (GetLastError() != ERROR_IO_PENDING) { 785 | /* DeviceIoControl() failed. Return error. */ 786 | register_error(dev, "Send Feature Report DeviceIoControl"); 787 | return -1; 788 | } 789 | } 790 | 791 | /* Wait here until the write is done. This makes 792 | hid_get_feature_report() synchronous. */ 793 | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); 794 | if (!res) { 795 | /* The operation failed. */ 796 | register_error(dev, "Send Feature Report GetOverLappedResult"); 797 | return -1; 798 | } 799 | 800 | /* bytes_returned does not include the first byte which contains the 801 | report ID. The data buffer actually contains one more byte than 802 | bytes_returned. */ 803 | bytes_returned++; 804 | 805 | return bytes_returned; 806 | #endif 807 | } 808 | 809 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) 810 | { 811 | if (!dev) 812 | return; 813 | CancelIo(dev->device_handle); 814 | free_hid_device(dev); 815 | } 816 | 817 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 818 | { 819 | BOOL res; 820 | 821 | res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); 822 | if (!res) { 823 | register_error(dev, "HidD_GetManufacturerString"); 824 | return -1; 825 | } 826 | 827 | return 0; 828 | } 829 | 830 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 831 | { 832 | BOOL res; 833 | 834 | res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); 835 | if (!res) { 836 | register_error(dev, "HidD_GetProductString"); 837 | return -1; 838 | } 839 | 840 | return 0; 841 | } 842 | 843 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 844 | { 845 | BOOL res; 846 | 847 | res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); 848 | if (!res) { 849 | register_error(dev, "HidD_GetSerialNumberString"); 850 | return -1; 851 | } 852 | 853 | return 0; 854 | } 855 | 856 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 857 | { 858 | BOOL res; 859 | 860 | res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); 861 | if (!res) { 862 | register_error(dev, "HidD_GetIndexedString"); 863 | return -1; 864 | } 865 | 866 | return 0; 867 | } 868 | 869 | 870 | HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 871 | { 872 | return (wchar_t*)dev->last_error_str; 873 | } 874 | 875 | 876 | /*#define PICPGM*/ 877 | /*#define S11*/ 878 | #define P32 879 | #ifdef S11 880 | unsigned short VendorID = 0xa0a0; 881 | unsigned short ProductID = 0x0001; 882 | #endif 883 | 884 | #ifdef P32 885 | unsigned short VendorID = 0x04d8; 886 | unsigned short ProductID = 0x3f; 887 | #endif 888 | 889 | 890 | #ifdef PICPGM 891 | unsigned short VendorID = 0x04d8; 892 | unsigned short ProductID = 0x0033; 893 | #endif 894 | 895 | 896 | #if 0 897 | int __cdecl main(int argc, char* argv[]) 898 | { 899 | int res; 900 | unsigned char buf[65]; 901 | 902 | UNREFERENCED_PARAMETER(argc); 903 | UNREFERENCED_PARAMETER(argv); 904 | 905 | /* Set up the command buffer. */ 906 | memset(buf,0x00,sizeof(buf)); 907 | buf[0] = 0; 908 | buf[1] = 0x81; 909 | 910 | 911 | /* Open the device. */ 912 | int handle = open(VendorID, ProductID, L"12345"); 913 | if (handle < 0) 914 | printf("unable to open device\n"); 915 | 916 | 917 | /* Toggle LED (cmd 0x80) */ 918 | buf[1] = 0x80; 919 | res = write(handle, buf, 65); 920 | if (res < 0) 921 | printf("Unable to write()\n"); 922 | 923 | /* Request state (cmd 0x81) */ 924 | buf[1] = 0x81; 925 | write(handle, buf, 65); 926 | if (res < 0) 927 | printf("Unable to write() (2)\n"); 928 | 929 | /* Read requested state */ 930 | read(handle, buf, 65); 931 | if (res < 0) 932 | printf("Unable to read()\n"); 933 | 934 | /* Print out the returned buffer. */ 935 | for (int i = 0; i < 4; i++) 936 | printf("buf[%d]: %d\n", i, buf[i]); 937 | 938 | return 0; 939 | } 940 | #endif 941 | 942 | #ifdef __cplusplus 943 | } /* extern "C" */ 944 | #endif 945 | --------------------------------------------------------------------------------