diff --git a/CMakeLists.txt b/CMakeLists.txt index ca2f08f6c41..6359d2b9dd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -599,8 +599,8 @@ if (WIN32) list(APPEND OPENSSL_LIBRARIES ws2_32 crypt32) endif() -option(MONERUJO_HIDAPI "HIDAPI is handled by Monerujo" OFF) -if(NOT MONERUJO_HIDAPI) +option(MONERUJO "Building for Monerujo (Ledger & Sidekick comms handled by Monerujo)" OFF) +if(NOT MONERUJO) find_package(HIDAPI) endif() diff --git a/Makefile b/Makefile index 688f0b6fb07..d6dfbc9bdab 100644 --- a/Makefile +++ b/Makefile @@ -132,20 +132,15 @@ release-static-android-armv8: release-static-android-armv7-wallet_api: mkdir -p $(builddir)/release - cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D MONERUJO_HIDAPI=ON -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-armv7" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" -D NO_AES=true ../.. && $(MAKE) wallet_api + cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D MONERUJO=ON -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-armv7" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" -D NO_AES=true ../.. && $(MAKE) wallet_api release-static-android-armv8-wallet_api: mkdir -p $(builddir)/release - cd $(builddir)/release && CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++ cmake -D MONERUJO_HIDAPI=ON -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-armv8" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="arm64-v8a" ../.. && $(MAKE) wallet_api + cd $(builddir)/release && CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++ cmake -D MONERUJO=ON -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-armv8" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="arm64-v8a" ../.. && $(MAKE) wallet_api release-static-android-x86_64-wallet_api: mkdir -p $(builddir)/release - cd $(builddir)/release && CC=x86_64-linux-android-clang CXX=x86_64-linux-android-clang++ cmake -D MONERUJO_HIDAPI=ON -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="x86-64" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-x86_64" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="x86_64" ../.. && $(MAKE) wallet_api - -release-static-android-x86-wallet_api: - mkdir -p $(builddir)/release - cd $(builddir)/release && CC=i686-linux-android-clang CXX=i686-linux-android-clang++ cmake -D MONERUJO_HIDAPI=ON -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="i686" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-x86" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="x86" ../.. && $(MAKE) wallet_api - + cd $(builddir)/release && CC=x86_64-linux-android-clang CXX=x86_64-linux-android-clang++ cmake -D MONERUJO=ON -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="x86-64" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-x86_64" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="x86_64" ../.. && $(MAKE) wallet_api release-static-linux-armv8: mkdir -p $(builddir)/release diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index d43f9301c00..c56ef149453 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -56,19 +56,22 @@ if(HIDAPI_FOUND) ) endif() -if(MONERUJO_HIDAPI) +if(MONERUJO) if(HIDAPI_FOUND) - message(FATAL_ERROR "HIDAPI_FOUND but we want MONERUJO_HIDAPI") + message(FATAL_ERROR "HIDAPI_FOUND but we want MONERUJO") endif() add_definitions(-DHAVE_MONERUJO) set(device_sources ${device_sources} device_ledger.cpp + device_sidekick.cpp ) set(device_headers ${device_headers} device_ledger.hpp device_io_monerujo.hpp + device_sidekick.hpp + device_io_monerujo_bt.hpp ) endif() diff --git a/src/device/device.cpp b/src/device/device.cpp index e6cd358b635..256de615d05 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -29,6 +29,9 @@ #include "device.hpp" #include "device_default.hpp" +#ifdef WITH_DEVICE_SIDEKICK +#include "device_sidekick.hpp" +#endif #ifdef WITH_DEVICE_LEDGER #include "device_ledger.hpp" #endif @@ -57,6 +60,9 @@ namespace hw { device_registry::device_registry(){ hw::core::register_all(registry); + #ifdef WITH_DEVICE_SIDEKICK + hw::sidekick::register_all(registry); + #endif #ifdef WITH_DEVICE_LEDGER hw::ledger::register_all(registry); #endif diff --git a/src/device/device.hpp b/src/device/device.hpp index 97771f660d8..4d6ea2bdc5c 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -33,11 +33,16 @@ #include "crypto/chacha.h" #include "ringct/rctTypes.h" #include "cryptonote_config.h" +#include #if defined(HAVE_HIDAPI) || defined(HAVE_MONERUJO) #define WITH_DEVICE_LEDGER #endif +#if defined(HAVE_MONERUJO) +#define WITH_DEVICE_SIDEKICK +#endif + // forward declaration needed because this header is included by headers in libcryptonote_basic which depends on libdevice namespace cryptonote { @@ -94,7 +99,8 @@ namespace hw { { SOFTWARE = 0, LEDGER = 1, - TREZOR = 2 + TREZOR = 2, + SIDEKICK = 3 }; diff --git a/src/device/device_io_monerujo_bt.hpp b/src/device/device_io_monerujo_bt.hpp new file mode 100644 index 00000000000..a491ebed5fc --- /dev/null +++ b/src/device/device_io_monerujo_bt.hpp @@ -0,0 +1,78 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#if defined(HAVE_MONERUJO) + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief BtExchange - exchange data with Monerujo Device + * @param request - buffer for data to send + * @param request_len - length of data to send + * @param response - buffer for received data + * @param max_resp_len - size of receive buffer + * + * @return length of received data in response or -1 if error, -2 if response buffer too small + */ +int BtExchange(unsigned char *request, unsigned int request_len, unsigned char *response, unsigned int max_resp_len); + +#ifdef __cplusplus +} +#endif + +#include "device_io.hpp" + +#pragma once + +namespace hw { + namespace io { + class device_io_monerujo_bt: device_io { + public: + device_io_monerujo_bt() {}; + ~device_io_monerujo_bt() {}; + + void init() {}; + void release() {}; + + void connect(void *params) {}; + void disconnect() {}; + bool connected() const {return true;}; // monerujo is always connected before it gets here + + // returns number of bytes read or -1 on error + int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) { + return BtExchange(command, cmd_len, response, max_resp_len); + } + }; + }; +}; + +#endif //#if defined(HAVE_MONERUJO) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 41678d2f148..8b6642ce8b0 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -4,7 +4,7 @@ // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: -// +// display_address // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 85a5aefa32f..2b6c5783f47 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -34,10 +34,10 @@ #include #include "device.hpp" #include "log.hpp" -#ifndef HAVE_MONERUJO -#include "device_io_hid.hpp" -#else +#ifdef HAVE_MONERUJO #include "device_io_monerujo.hpp" +#else +#include "device_io_hid.hpp" #endif #include #include diff --git a/src/device/device_sidekick.cpp b/src/device/device_sidekick.cpp new file mode 100644 index 00000000000..a369c0357f8 --- /dev/null +++ b/src/device/device_sidekick.cpp @@ -0,0 +1,814 @@ +// Copyright (c) 2017-2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "version.h" +#include "device_sidekick.hpp" +#include "int-util.h" +#include "crypto/wallet/crypto.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/subaddress_index.h" +#include "cryptonote_core/cryptonote_tx_utils.h" +#include "ringct/rctOps.h" +#include "cryptonote_config.h" +#include "string_tools.h" +#include "assert.h" +#include +#include "memwipe.h" +#include "common/varint.h" + +namespace std { + inline std::ostream &operator<<(std::ostream &o, const crypto::ec_scalar &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator<<(std::ostream &o, const crypto::ec_point &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator<<(std::ostream &o, const crypto::chacha_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } +} + +namespace hw { + namespace sidekick { + + /* ===================================================================== */ + /* === Misc ==== */ + /* ===================================================================== */ + static inline unsigned char *operator &(crypto::ec_scalar &scalar) { + return &reinterpret_cast(scalar); + } + static inline const unsigned char *operator &(const crypto::ec_scalar &scalar) { + return &reinterpret_cast(scalar); + } + + static bool is_fake_view_key(const crypto::secret_key &sec) { + return memcmp(sec.data, FAKE_SECRET_VIEW_KEY, 32) == 0; + } + + bool operator==(const crypto::key_derivation &d0, const crypto::key_derivation &d1) { + static_assert(sizeof(crypto::key_derivation) == 32, "key_derivation must be 32 bytes"); + return !crypto_verify_32((const unsigned char*)&d0, (const unsigned char*)&d1); + } + + /* ===================================================================== */ + /* === Device ==== */ + /* ===================================================================== */ + + static int device_id = 0; + // logic corresponds to Ledger Protocol Version 4 + #define PROTOCOL_VERSION 4 + + device_sidekick::device_sidekick() { + this->id = device_id++; + this->mode = NONE; + this->has_view_key = false; + MDEBUG("Device " << this->id <<" created"); + } + + device_sidekick::~device_sidekick() { + this->release(); + MDEBUG("Device " <id <<" destroyed"); + } + + // helper method to disassamble the MONERO_VERSION string into a uint32 + // possible 2k problem in this method + // each version component must be <= 255 + uint32_t monero_version() { + uint32_t v = 0; + std::stringstream ss; + ss << MONERO_VERSION << '.'; + char buf[4]; + for (size_t i = 0; i < 4; i++){ + ss.getline(buf, 3, '.'); + if (ss.fail()) return 0; + v = v << 8 | std::atoi(buf); + } + return true; + } + bool device_sidekick::reset() { + LOG_PRINT_L3(">>reset"); + ResetCommand cmd; + cmd.req.version = monero_version(); + // if (v == 0) throw something + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + uint32_t &v = cmd.res.version; // device version + CHECK_AND_ASSERT_THROW_MES(v >= MIN_APP_VERSION, + "Unsupported device version: " + << VERSION_MAJOR(v)<<"."<buffer, sizeof(this->buffer), false); + CHECK_AND_ASSERT_THROW_MES(len >= 0, "error in echange"); + return std::string((char*) buffer, len); + } + + /* ======================================================================= */ + /* SETUP/TEARDOWN */ + /* ======================================================================= */ + + bool device_sidekick::set_name(const std::string &name) { + this->name = name; + return true; + } + const std::string device_sidekick::get_name() const { + if (!this->connected()) { + return std::string("name).append(">"); + } + return this->name; + } + + bool device_sidekick::init(void) { + this->release(); + hw_device.init(); + return true; + } + bool device_sidekick::release() { + this->disconnect(); + hw_device.release(); + return true; + } + + bool device_sidekick::connected(void) const { + return hw_device.connected(); + } + + bool device_sidekick::connect(void) { + this->disconnect(); + this->reset(); + crypto::secret_key vkey; + crypto::secret_key skey; + this->get_secret_keys(vkey,skey); + return true; + } + + bool device_sidekick::disconnect() { + hw_device.disconnect(); + return true; + } + + bool device_sidekick::set_mode(device_mode mode) { + LOG_PRINT_L3(">>set_mode " << mode); + switch(mode) { + case TRANSACTION_CREATE_REAL: + case TRANSACTION_CREATE_FAKE: + { + SetSigModeCommand cmd; + cmd.req.sig_mode = mode; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + CHECK_AND_ASSERT_THROW_MES(cmd.res.sig_mode == mode, "mode was not set on device"); + break; + } + case TRANSACTION_PARSE: + case NONE: + break; + default: + CHECK_AND_ASSERT_THROW_MES(false, " invalid mode: " << mode); + } + device::set_mode( mode); + LOG_PRINT_L3("< lock1(device_locker, boost::adopt_lock); \ + boost::lock_guard lock2(command_locker, boost::adopt_lock) + + // lock the device for a long sequence + void device_sidekick::lock(void) { + LOG_PRINT_L3("LOCKING " << this->name); + device_locker.lock(); + LOG_PRINT_L3("LOCKED " << this->name); + } + + // lock the device for a long sequence + bool device_sidekick::try_lock(void) { + LOG_PRINT_L3("LOCKING(try) " << this->name); + bool r = device_locker.try_lock(); + if (r) { + LOG_PRINT_L3("LOCKed(try) " << this->name); + } else { + LOG_PRINT_L3("!LOCKed(try) " << this->name); + } + return r; + } + + // lock the device for a long sequence + void device_sidekick::unlock(void) { + LOG_PRINT_L3("UNLOCKING " << this->name); + device_locker.unlock(); + LOG_PRINT_L3("UNLOCKed " << this->name); + } + + /* ======================================================================= */ + /* WALLET & ADDRESS */ + /* ======================================================================= */ + + // keys are ignored - we use our own on the device + bool device_sidekick::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) { + LOG_PRINT_L3(">>generate_chacha_key " < key + GenerateChachaKeyCommand cmd; + cmd.req.kdf_rounds = kdf_rounds; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + key = cmd.res.key; + LOG_PRINT_L3("<>get_public_address"); + // CALL () + GetPublicKeysCommand cmd; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + pubkey = cmd.res.pubkey; + LOG_PRINT_L3("<>get_secret_keys"); + GetSecretKeysCommand cmd; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + a = cmd.res.viewkey; + b = cmd.res.spendkey; + this->viewkey = a; + this->has_view_key = !is_fake_view_key(this->viewkey); + LOG_PRINT_L3("<has_view_key); + return true; + } + + void device_sidekick::display_address(const cryptonote::subaddress_index& index, const boost::optional &payment_id) { + LOG_PRINT_L3(">>display_address"); + if (payment_id) + LOG_PRINT_L3(">>display_address: payment_id=" << payment_id.value()); + // CALL (index, payment_id) - displays address on device + DisplayAddressCommand cmd; + cmd.req.index = index; + if (payment_id) + cmd.req.payment_id = payment_id.value(); + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + //TODO: what if this goes wrong? (can this go wrong?) + LOG_PRINT_L3("<>derive_subaddress_public_key p,d,i " << out_key << derivation << " " << output_index); + if ((this->mode == TRANSACTION_PARSE) && has_view_key) { + //If we are in TRANSACTION_PARSE, the given derivation has been retrieved uncrypted (wihtout the help + //of the device), so continue that way. + bool r = crypto::derive_subaddress_public_key(out_key, derivation, output_index, derived_key); + LOG_PRINT_L3("<>get_subaddress_spend_public_key " << keys.m_view_secret_key< device_sidekick::get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end) { + LOG_PRINT_L3(">>get_subaddress_spend_public_keys"); + CHECK_AND_ASSERT_THROW_MES(begin <= end, "begin > end"); + + std::vector pkeys; + for (cryptonote::subaddress_index index = {account, begin}; index.minor < end; ++index.minor) { + crypto::public_key D = this->get_subaddress_spend_public_key(keys, index); + pkeys.push_back(D); + LOG_PRINT_L3("<>get_subaddress " << keys.m_view_secret_key<>get_subaddress_secret_key " <>verify_keys " << secret_key << public_key); + // verify spend_secret => spend_public or view_secret => view_public + // CALL (secret_key/S, public_key) + VerifyKeysCommand cmd; + cmd.req.sec = secret_key; + cmd.req.pub = public_key; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + LOG_PRINT_L3("<>scalarmultKey " <>scalarmultBase " <>sc_secret_add " <>generate_keys"); + CHECK_AND_ASSERT_THROW_MES(!recover,"device generate key does not support recover"); + // CALL () => pub, sec/S + GenerateKeysCommand cmd; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + pub = cmd.res.pub; + sec = cmd.res.sec; + LOG_PRINT_L3("<>generate_key_derivation " << tx_pub << sec_view); + if ((this->mode == TRANSACTION_PARSE) && this->has_view_key) { + //A derivation is resquested in PASRE mode and we have the view key, + //so do that without the device and return the derivation unencrypted. + bool r = crypto::wallet::generate_key_derivation(tx_pub, this->viewkey, derivation); + LOG_PRINT_L3("< &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector &additional_derivations) { + LOG_PRINT_L3(">>conceal_derivation " << tx_pub_key); + const crypto::public_key *pkey = NULL; + if (derivation == main_derivation) { + pkey = &tx_pub_key; + LOG_PRINT_L3("conceal derivation with main tx pub key"); + } else { + for (size_t n = 0; n < additional_derivations.size(); ++n) { + if (derivation == additional_derivations[n]) { + pkey = &additional_tx_pub_keys[n]; + LOG_PRINT_L3("conceal derivation with additional tx pub key"); + break; + } + } + } + CHECK_AND_ASSERT_THROW_MES(pkey, "Mismatched derivation on scan info"); + LOG_PRINT_L3("<generate_key_derivation(*pkey, crypto::null_skey, derivation); + } + + bool device_sidekick::derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) { + LOG_PRINT_L3(">>derivation_to_scalar " << derivation <<" "<>derive_secret_key " << derivation<<" "<>derive_public_key " <>secret_key_to_public_key " <>generate_key_image " <>derive_view_tag " <mode == TRANSACTION_PARSE) && has_view_key) { + //If we are in TRANSACTION_PARSE, the given derivation has been retrieved uncrypted (wihtout the help + //of the device), so continue that way. + LOG_PRINT_L3( "derive_view_tag : PARSE mode with known viewkey"); + crypto::derive_view_tag(derivation, output_index, view_tag); + } else { + // CALL (derivation/S, output_index) + DeriveViewTagCommand cmd; + cmd.req.derivation = derivation; + cmd.req.output_index = output_index; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + view_tag = cmd.res.view_tag; + LOG_PRINT_L3("<(view_tag.data)); + } + return true; + } + + + /* ======================================================================= */ + /* TRANSACTION */ + /* ======================================================================= */ + + void device_sidekick::generate_tx_proof(const crypto::hash &prefix_hash, + const crypto::public_key &R, const crypto::public_key &A, const boost::optional &B, const crypto::public_key &D, const crypto::secret_key &r, + crypto::signature &sig) { + LOG_PRINT_L3(">>generate_tx_proof " << prefix_hash + << R + << A + << B.value_or(crypto::null_pkey) + << D + << r); + // CALL (prefix_hash, R, A, B, D, r/S) + GenerateTxProofCommand cmd; + cmd.req.prefix_hash = prefix_hash; + cmd.req.R = R; + cmd.req.A = A; + cmd.req.has_B = B.has_value(); + cmd.req.B = B.value_or(crypto::null_pkey); + cmd.req.D = D; + cmd.req.rr = r; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + sig = cmd.res.sig; + LOG_PRINT_L3("<>open_tx"); + this->lock(); + // CALL () => tx_key/S, (fake_b)/S, (fake_a)/S - open + OpenTxCommand cmd; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + tx_key = cmd.res.tx_key; + CHECK_AND_ASSERT_THROW_MES(!memcmp(cmd.res.spend_key.data, FAKE_SECRET_SPEND_KEY, 32), "expected fake spend key"); + CHECK_AND_ASSERT_THROW_MES(!memcmp(cmd.res.view_key.data, FAKE_SECRET_VIEW_KEY, 32), "expected fake view key"); + LOG_PRINT_L3("<>get_transaction_prefix_hash"); + // log what we have: + for (size_t i = 0; i < tx_prefix.vout.size(); i++) { + const cryptonote::txout_to_key* const out_key = boost::get(std::addressof(tx_prefix.vout[i].target)); + LOG_PRINT_L3("vout["<key); + } + // CALL (tx) + GetTxPrefixHashCommand cmd; + cmd.req.tx_prefix = tx_prefix; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + h = cmd.res.h; + LOG_PRINT_L3("<>encrypt_payment_id " < &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector &additional_tx_keys, + std::vector &additional_tx_public_keys, + std::vector &amount_keys, crypto::public_key &out_eph_public_key, + const bool use_view_tags, crypto::view_tag &view_tag) { + LOG_PRINT_L3(">>generate_output_ephemeral_keys"); + CHECK_AND_ASSERT_THROW_MES(tx_version > 1, "TX version not supported " << tx_version); + GenerateOutputEphemeralKeysCommand cmd; + cmd.req.tx_version = tx_version; + cmd.req.tx_key = tx_key; + cmd.req.tx_key_pub = tx_key_pub; + cmd.req.Aout = dst_entr.addr.m_view_public_key; + cmd.req.Bout = dst_entr.addr.m_spend_public_key; + cmd.req.output_index = output_index; + cmd.req.is_change = (change_addr && dst_entr.addr == *change_addr); + cmd.req.is_subaddress = dst_entr.is_subaddress; + cmd.req.need_additional_tx_key = need_additional_txkeys; + if (need_additional_txkeys) { + cmd.req.additional_tx_key = additional_tx_keys[output_index]; + } + cmd.req.use_view_tags = use_view_tags; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + amount_keys.push_back(rct::sk2rct(cmd.res.amount_key)); + out_eph_public_key = cmd.res.out_eph_public_key; + if (need_additional_txkeys) + additional_tx_public_keys.push_back(cmd.res.additional_tx_key_pub); + view_tag = cmd.res.view_tag; + LOG_PRINT_L3("<>genCommitmentMask " << amount_key); + // CALL (amount_key/S) + GenCommitmentMaskCommand cmd; + cmd.req.amount_key = amount_key; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + LOG_PRINT_L3("<>ecdhEncode " <>ecdhDecode " <>prehash " <=clsag_prepare"); + // CALL (p/S, z, H) => a/S, aG, aH, I, D + ClsagPrepareCommand cmd; + cmd.req.p = p; + cmd.req.z = z; + cmd.req.H = H; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + I = cmd.res.I; + D = cmd.res.D; + a = cmd.res.a; + aG = cmd.res.aG; + aH = cmd.res.aH; + LOG_PRINT_L3("<>clsag_hash"); + // CALL (data.bytes) => hash.bytes + ClsagHashCommand cmd; + cmd.req.data = data; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + hash = cmd.res.hash; + LOG_PRINT_L3("<>clsag_sign"); + // CALL (a/S, p/S, z, mu_P, mu_C) => s + ClsagSignCommand cmd; + // ignore c + cmd.req.a = a; + cmd.req.p = p; + cmd.req.z = z; + cmd.req.mu_P = mu_P; + cmd.req.mu_C = mu_C; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + s = cmd.res.s; + LOG_PRINT_L3("<>close_tx"); + // CALL() - close + CloseTxCommand cmd; + cmd.call(); + CHECK_AND_ASSERT_THROW_MES(cmd.res.rc == RC_OK, "device call failed rc=" << cmd.res.rc); + this->unlock(); + LOG_PRINT_L3("<set_name("Sidekick"); + instance->set_mode(hw::device::device_mode::NONE); + } + return instance; + }; + void register_all(std::map> ®istry) { + LOG_PRINT_L3("==register_all"); + registry.insert(std::make_pair("Sidekick", std::unique_ptr(device_sidekick::Instance()))); + } + } +} diff --git a/src/device/device_sidekick.hpp b/src/device/device_sidekick.hpp new file mode 100644 index 00000000000..3404bc0ee21 --- /dev/null +++ b/src/device/device_sidekick.hpp @@ -0,0 +1,1141 @@ +// Copyright (c) 2017-2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief ConfirmTransfers + * @param transfers - string of "fee (':' address ':' amount)+" + * + * @return true on accept, false on reject + */ +bool ConfirmTransfers(const char* transfers); + +#ifdef __cplusplus +} +#endif + +#include +#include "serialization/serialization.h" +#include "serialization/string.h" +#include +#include "device.hpp" +#include "log.hpp" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/subaddress_index.h" +#include +#ifdef HAVE_MONERUJO +#include "device_io_monerujo_bt.hpp" +#endif + +namespace hw { + namespace sidekick { + + void register_all(std::map> ®istry); + + const unsigned char FAKE_SECRET_VIEW_KEY[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const unsigned char FAKE_SECRET_SPEND_KEY[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + /* Minimal supported device version */ + #define MIN_APP_VERSION_MAJOR 0 + #define MIN_APP_VERSION_MINOR 1 + #define MIN_APP_VERSION_MICRO 0 + + #define VERSION(M,m,u) ((M)<<24|(m)<<16|(u)<<8) + #define VERSION_MAJOR(v) (((v)>>24)&0xFF) + #define VERSION_MINOR(v) (((v)>>16)&0xFF) + #define VERSION_MICRO(v) (((v)>>8)&0xFF) + + #define MIN_APP_VERSION VERSION(MIN_APP_VERSION_MAJOR, MIN_APP_VERSION_MINOR, MIN_APP_VERSION_MICRO) + + template + inline bool read(std::istream &is, T &v) { + char buf[2*sizeof(T)+1]; + is.getline(buf, sizeof(buf), ':'); + if (is.fail()) return false; + return epee::from_hex::to_buffer(epee::as_mut_byte_span(v), buf); + } + + template + inline bool write(std::ostream &os, T &v) { + epee::to_hex::buffer(os, epee::as_byte_span(v)); os<<':'; return !os.fail(); + } + + inline bool write(std::ostream &os, crypto::secret_key &v) { + rct::key k; + for (size_t i = 0; i < sizeof(rct::key); i++) { + k[i] = v.data[31-i]; + } + epee::to_hex::buffer(os, epee::as_byte_span(k)); os<<':'; return !os.fail(); + } + + // some magic return codes + static const int RC_UNDEF = -999; + static const int RC_OK = 0; + static const int RC_NOK = -1; + static const int RC_DENIED = -2; // by user + static const int RC_STATE_NOK = -10; // anything <= than this is a protocol hard error + static const int RC_FORBIDDEN = -11; // state machine says no + + enum sidekick_mode { + TRANSACTION_CREATE_REAL = device::device_mode::TRANSACTION_CREATE_REAL, + TRANSACTION_CREATE_FAKE = device::device_mode::TRANSACTION_CREATE_FAKE + }; + + std::string BluetoothTransport(std::string data); + + class device_sidekick : public hw::device { + private: + unsigned int id; + // Locker for concurrent access + mutable boost::recursive_mutex device_locker; + mutable boost::mutex command_locker; + + //IO + hw::io::device_io_monerujo_bt hw_device; + unsigned char buffer[4096]; + + // To speed up blockchain parsing the view key maybe handle here. + crypto::secret_key viewkey; + bool has_view_key; + + static device_sidekick* instance; + public: + static device_sidekick* Instance(); + device_sidekick(); + ~device_sidekick(); + + device_sidekick(const device_sidekick &device) = delete; + device_sidekick& operator=(const device_sidekick &device) = delete; + + explicit operator bool() const override { return false; }; + + std::string exchange(const std::string out); + + bool reset(void); + + /* ======================================================================= */ + /* SETUP/TEARDOWN */ + /* ======================================================================= */ + bool set_name(const std::string &name) override; + const std::string get_name() const override; + + bool init(void) override; + bool release() override; + + bool connected(void) const; + bool connect(void) override; + bool disconnect() override; + + bool set_mode(device_mode mode) override; + + device_type get_type() const override {return device_type::SIDEKICK;}; + + /* ======================================================================= */ + /* LOCKER */ + /* ======================================================================= */ + void lock(void) override; + void unlock(void) override; + bool try_lock(void) override; + + /* ======================================================================= */ + /* WALLET & ADDRESS */ + /* ======================================================================= */ + bool get_public_address(cryptonote::account_public_address &pubkey) override; + bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override; + bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) override; + void display_address(const cryptonote::subaddress_index& index, const boost::optional &payment_id) override; + + /* ======================================================================= */ + /* SUB ADDRESS */ + /* ======================================================================= */ + bool derive_subaddress_public_key(const crypto::public_key &pub, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_pub) override; + crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index) override; + std::vector get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end) override; + cryptonote::account_public_address get_subaddress(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) override; + crypto::secret_key get_subaddress_secret_key(const crypto::secret_key &sec, const cryptonote::subaddress_index &index) override; + + /* ======================================================================= */ + /* DERIVATION & KEY */ + /* ======================================================================= */ + bool verify_keys(const crypto::secret_key &secret_key, const crypto::public_key &public_key) override; + bool scalarmultKey(rct::key & aP, const rct::key &P, const rct::key &a) override; + bool scalarmultBase(rct::key &aG, const rct::key &a) override; + bool sc_secret_add(crypto::secret_key &r, const crypto::secret_key &a, const crypto::secret_key &b) override; + crypto::secret_key generate_keys(crypto::public_key &pub, crypto::secret_key &sec, const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false) override; + bool generate_key_derivation(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_derivation &derivation) override; + bool conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector &additional_derivations) override; + bool derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) override; + bool derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) override; + bool derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub) override; + bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override; + bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override; + bool derive_view_tag(const crypto::key_derivation &derivation, const std::size_t output_index, crypto::view_tag &view_tag) override; + + + /* ======================================================================= */ + /* TRANSACTION */ + /* ======================================================================= */ + + void generate_tx_proof(const crypto::hash &prefix_hash, + const crypto::public_key &R, const crypto::public_key &A, const boost::optional &B, const crypto::public_key &D, const crypto::secret_key &r, + crypto::signature &sig) override; + + bool open_tx(crypto::secret_key &tx_key) override; + void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override; + + bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; + + rct::key genCommitmentMask(const rct::key &amount_key) override; + + bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) override; + bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) override; + + bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector &additional_tx_keys, + std::vector &additional_tx_public_keys, + std::vector &amount_keys, + crypto::public_key &out_eph_public_key, + const bool use_view_tags, crypto::view_tag &view_tag) override; + + bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override; + + // only CLSAG supported + bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override {dfns();}; + bool mlsag_prepare(rct::key &a, rct::key &aG) override {dfns();}; + bool mlsag_hash(const rct::keyV &long_message, rct::key &c) override {dfns();}; + bool mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) override {dfns();}; + + bool clsag_prepare(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &aH) override; + bool clsag_hash(const rct::keyV &data, rct::key &hash) override; + bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override; + + bool close_tx(void) override; + }; + + struct RcResponse { + int rc = RC_UNDEF; + BEGIN_SERIALIZE_OBJECT() + FIELD(rc) + END_SERIALIZE() + std::string serialize() { + std::stringstream ss; + binary_archive ba(ss); + CHECK_AND_ASSERT_THROW_MES(member_do_serialize(ba), "failed to serialize response"); + return ss.str(); + } + }; + + struct Request { + Request() = default; + unsigned char commandId; + int correlationId = -1; + BEGIN_SERIALIZE_OBJECT() + FIELD(commandId) + FIELD(correlationId) + END_SERIALIZE() + }; + struct Response { + Response() = default; + int rc = RC_UNDEF; + int correlationId; + BEGIN_SERIALIZE_OBJECT() + FIELD(rc) + FIELD(correlationId) + END_SERIALIZE() + }; + template + class Command { + public: + enum {ID=CID}; + RequestType req; + ResponseType res; + Command() {req.commandId = CID;} // TODO: generate correlationId & set it + Command(const std::string &requestData) { + res.correlationId = req.correlationId; + } + + std::string serializeRequest() { // should be const but can't because auf do_serialize impl + std::stringstream ss; + binary_archive ba(ss); + CHECK_AND_ASSERT_THROW_MES(do_serialize(ba, req), "failed to serialize request"); + return ss.str(); + } + + bool deserializeResponse(const std::string &data) { + binary_archive ba{epee::strspan(data)}; + return do_serialize(ba, res); + } + + bool checkResponse(const std::string &data) { + binary_archive ba{epee::strspan(data)}; + RcResponse res; + bool r = do_serialize(ba, res); + LOG_PRINT_L3("RC_CHECK " << r << " " << res.rc); + return r && (res.rc > RC_STATE_NOK); + } + + // monero-side + void call() { + // serialize the request + LOG_PRINT_L3("call"); + std::string request = serializeRequest(); + LOG_PRINT_L3("REQ " << std::hex << ID); + + // send the request, get the response + std::string response = device_sidekick::Instance()->exchange(request); + + char logstr[4096]; + buffer_to_str(logstr, sizeof(logstr), response.c_str(), response.size()); + LOG_PRINT_L3("RES len=" << response.size() << ": " << logstr); + + CHECK_AND_ASSERT_THROW_MES(checkResponse(response), "computer says no"); + // blocking call - timeout? mark user input required + // deserialize the reply + if (!deserializeResponse(response)) + ASSERT_MES_AND_THROW("invalid data"); + LOG_PRINT_L3("RC=" << std::hex << res.rc); + } + }; + + struct SetSigModeRequest: Request { + int sig_mode; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(sig_mode) + END_SERIALIZE() + }; + struct SetSigModeResponse: Response { + int sig_mode; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(sig_mode) + END_SERIALIZE() + }; + class SetSigModeCommand: public Command { + public: + SetSigModeCommand(): Command() {}; + SetSigModeCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GenerateChachaKeyRequest: Request { + uint64_t kdf_rounds; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(kdf_rounds) + END_SERIALIZE() + }; + struct GenerateChachaKeyResponse: Response { + crypto::chacha_key key; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(key) + END_SERIALIZE() + }; + class GenerateChachaKeyCommand: public Command { + public: + GenerateChachaKeyCommand(): Command() {}; + GenerateChachaKeyCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GetPublicKeysRequest: Request { + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + END_SERIALIZE() + }; + struct GetPublicKeysResponse: Response { + cryptonote::account_public_address pubkey; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(pubkey) + END_SERIALIZE() + }; + class GetPublicKeysCommand: public Command { + public: + GetPublicKeysCommand(): Command() {}; + GetPublicKeysCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GetSecretKeysRequest: Request { + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + END_SERIALIZE() + }; + struct GetSecretKeysResponse:Response { + crypto::secret_key viewkey; + crypto::secret_key spendkey; + bool send_viewkey; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(viewkey) + FIELD(spendkey) + END_SERIALIZE() + }; + class GetSecretKeysCommand: public Command { + public: + GetSecretKeysCommand(): Command() {}; + GetSecretKeysCommand(const std::string &requestData): Command(requestData) {} + }; + + struct DisplayAddressRequest: Request { + cryptonote::subaddress_index index; + crypto::hash8 payment_id; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(index) + FIELD(payment_id) + END_SERIALIZE() + }; + struct DisplayAddressResponse: Response { + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + END_SERIALIZE() + }; + class DisplayAddressCommand: public Command { + public: + DisplayAddressCommand(): Command() {}; + DisplayAddressCommand(const std::string &requestData): Command(requestData) {} + }; + + struct DeriveSubaddressPublicKeyRequest: Request { + crypto::public_key out_key; + crypto::key_derivation derivation; + std::size_t output_index; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(out_key) + FIELD(derivation) + FIELD(output_index) + END_SERIALIZE() + }; + struct DeriveSubaddressPublicKeyResponse: Response { + crypto::public_key derived_key; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(derived_key) + END_SERIALIZE() + }; + class DeriveSubaddressPublicKeyCommand: public Command { + public: + DeriveSubaddressPublicKeyCommand(): Command() {}; + DeriveSubaddressPublicKeyCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GetSubaddressSpendPublicKeyRequest: Request { + cryptonote::subaddress_index index; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(index) + END_SERIALIZE() + }; + struct GetSubaddressSpendPublicKeyResponse: Response { + crypto::public_key D; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(D) + END_SERIALIZE() + }; + class GetSubaddressSpendPublicKeyCommand: public Command { + public: + GetSubaddressSpendPublicKeyCommand(): Command() {}; + GetSubaddressSpendPublicKeyCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GetSubaddressSecretKeyRequest: Request { + cryptonote::subaddress_index index; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(index) + END_SERIALIZE() + }; + struct GetSubaddressSecretKeyResponse: Response { + crypto::secret_key m; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(m) + END_SERIALIZE() + }; + class GetSubaddressSecretKeyCommand: public Command { + public: + GetSubaddressSecretKeyCommand(): Command() {}; + GetSubaddressSecretKeyCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GetSubaddressRequest: Request { + cryptonote::subaddress_index index; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(index) + END_SERIALIZE() + }; + struct GetSubaddressResponse: Response { + crypto::public_key C; + crypto::public_key D; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(C) + FIELD(D) + END_SERIALIZE() + }; + class GetSubaddressCommand: public Command { + public: + GetSubaddressCommand(): Command() {}; + GetSubaddressCommand(const std::string &requestData): Command(requestData) {} + }; + + struct VerifyKeysRequest: Request { + crypto::secret_key sec; + crypto::public_key pub; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(sec) + FIELD(pub) + END_SERIALIZE() + }; + struct VerifyKeysResponse: Response { + bool verified; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(verified) + END_SERIALIZE() + }; + class VerifyKeysCommand: public Command { + public: + VerifyKeysCommand(): Command() {}; + VerifyKeysCommand(const std::string &requestData): Command(requestData) {} + }; + + struct ScalarmultKeyRequest: Request { + rct::key P; + rct::key a; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(P) + FIELD(a) + END_SERIALIZE() + }; + struct ScalarmultKeyResponse: Response { + rct::key aP; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(aP) + END_SERIALIZE() + }; + class ScalarmultKeyCommand: public Command { + public: + ScalarmultKeyCommand(): Command() {}; + ScalarmultKeyCommand(const std::string &requestData): Command(requestData) {} + }; + + struct ScalarmultBaseRequest: Request { + rct::key a; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(a) + END_SERIALIZE() + }; + struct ScalarmultBaseResponse: Response { + rct::key aG; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(aG) + END_SERIALIZE() + }; + class ScalarmultBaseCommand: public Command { + public: + ScalarmultBaseCommand(): Command() {}; + ScalarmultBaseCommand(const std::string &requestData): Command(requestData) {} + }; + + struct ScSecretAddRequest: Request { + crypto::secret_key a; + crypto::secret_key b; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(a) + FIELD(b) + END_SERIALIZE() + }; + struct ScSecretAddResponse: Response { + crypto::secret_key rr; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(rr) + END_SERIALIZE() + }; + class ScSecretAddCommand: public Command { + public: + ScSecretAddCommand(): Command() {}; + ScSecretAddCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GenerateKeysRequest: Request { + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + END_SERIALIZE() + }; + struct GenerateKeysResponse: Response { + crypto::public_key pub; + crypto::secret_key sec; + crypto::secret_key rng; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(pub) + FIELD(sec) + FIELD(rng) + END_SERIALIZE() + }; + class GenerateKeysCommand: public Command { + public: + GenerateKeysCommand(): Command() {}; + GenerateKeysCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GenerateKeyDerivationRequest: Request { + crypto::public_key tx_pub; + // secret view key is the on the device - no need to transport it + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(tx_pub) + END_SERIALIZE() + }; + struct GenerateKeyDerivationResponse: Response { + crypto::key_derivation derivation; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(derivation) + END_SERIALIZE() + }; + class GenerateKeyDerivationCommand: public Command { + public: + GenerateKeyDerivationCommand(): Command() {}; + GenerateKeyDerivationCommand(const std::string &requestData): Command(requestData) {} + }; + + struct DerivationToScalarRequest: Request { + crypto::key_derivation derivation; + size_t output_index; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(derivation) + FIELD(output_index) + END_SERIALIZE() + }; + struct DerivationToScalarResponse: Response { + crypto::ec_scalar scalar; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(scalar) + END_SERIALIZE() + }; + class DerivationToScalarCommand: public Command { + public: + DerivationToScalarCommand(): Command() {}; + DerivationToScalarCommand(const std::string &requestData): Command(requestData) {} + }; + + struct DeriveSecretKeyRequest: Request { + crypto::key_derivation derivation; + std::size_t output_index; + crypto::secret_key base; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(derivation) + FIELD(output_index) + FIELD(base) + END_SERIALIZE() + }; + struct DeriveSecretKeyResponse: Response { + crypto::secret_key derived_key; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(derived_key) + END_SERIALIZE() + }; + class DeriveSecretKeyCommand: public Command { + public: + DeriveSecretKeyCommand(): Command() {}; + DeriveSecretKeyCommand(const std::string &requestData): Command(requestData) {} + }; + + struct DerivePublicKeyRequest: Request { + crypto::key_derivation derivation; + std::size_t output_index; + crypto::public_key base; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(derivation) + FIELD(output_index) + FIELD(base) + END_SERIALIZE() + }; + struct DerivePublicKeyResponse: Response { + crypto::public_key derived_key; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(derived_key) + END_SERIALIZE() + }; + class DerivePublicKeyCommand: public Command { + public: + DerivePublicKeyCommand(): Command() {}; + DerivePublicKeyCommand(const std::string &requestData): Command(requestData) {} + }; + + struct SecretToPublicKeyRequest: Request { + crypto::secret_key sec; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(sec) + END_SERIALIZE() + }; + struct SecretToPublicKeyResponse: Response { + crypto::public_key pubkey; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(pubkey) + END_SERIALIZE() + }; + class SecretToPublicKeyCommand: public Command { + public: + SecretToPublicKeyCommand(): Command() {}; + SecretToPublicKeyCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GenerateKeyImageRequest: Request { + crypto::public_key pub; + crypto::secret_key sec; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(pub) + FIELD(sec) + END_SERIALIZE() + }; + struct GenerateKeyImageResponse: Response { + crypto::key_image image; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(image) + END_SERIALIZE() + }; + class GenerateKeyImageCommand: public Command { + public: + GenerateKeyImageCommand(): Command() {}; + GenerateKeyImageCommand(const std::string &requestData): Command(requestData) {} + }; + + struct DeriveViewTagRequest: Request { + crypto::key_derivation derivation; + std::size_t output_index; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(derivation) + FIELD(output_index) + END_SERIALIZE() + }; + struct DeriveViewTagResponse: Response { + crypto::view_tag view_tag; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(view_tag) + END_SERIALIZE() + }; + class DeriveViewTagCommand: public Command { + public: + DeriveViewTagCommand(): Command() {}; + DeriveViewTagCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GenerateTxProofRequest: Request { + crypto::hash prefix_hash; + crypto::public_key R; + crypto::public_key A; + bool has_B; + crypto::public_key B; + crypto::public_key D; + crypto::secret_key rr; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(prefix_hash) + FIELD(R) + FIELD(A) + FIELD(has_B); + FIELD(B) + FIELD(D) + FIELD(rr) + END_SERIALIZE() + }; + struct GenerateTxProofResponse: Response { + crypto::signature sig; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(sig) + END_SERIALIZE() + }; + class GenerateTxProofCommand: public Command { + public: + GenerateTxProofCommand(): Command() {}; + GenerateTxProofCommand(const std::string &requestData): Command(requestData) {} + }; + + struct OpenTxRequest: Request { + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + END_SERIALIZE() + }; + struct OpenTxResponse: Response { + crypto::secret_key tx_key; + crypto::secret_key view_key; + crypto::secret_key spend_key; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(tx_key) + FIELD(view_key) + FIELD(spend_key) + END_SERIALIZE() + }; + class OpenTxCommand: public Command { + public: + OpenTxCommand(): Command() {}; + OpenTxCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GetTxPrefixHashRequest: Request { + cryptonote::transaction_prefix tx_prefix; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(tx_prefix) + END_SERIALIZE() + }; + struct GetTxPrefixHashResponse: Response { + crypto::hash h; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(h) + END_SERIALIZE() + }; + class GetTxPrefixHashCommand: public Command { + public: + GetTxPrefixHashCommand(): Command() {}; + GetTxPrefixHashCommand(const std::string &requestData): Command(requestData) {} + }; + + struct EncryptPaymentidRequest: Request { + crypto::hash8 payment_id; + crypto::public_key public_key; + crypto::secret_key secret_key; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(payment_id) + FIELD(public_key) + FIELD(secret_key) + END_SERIALIZE() + }; + struct EncryptPaymentidResponse: Response { + crypto::hash8 payment_id; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(payment_id) + END_SERIALIZE() + }; + class EncryptPaymentidCommand: public Command { + public: + EncryptPaymentidCommand(): Command() {}; + EncryptPaymentidCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GenerateOutputEphemeralKeysRequest: Request { + size_t tx_version; + crypto::secret_key tx_key; + crypto::public_key tx_key_pub; + crypto::public_key Aout; + crypto::public_key Bout; + size_t output_index; + bool is_change; + bool is_subaddress; + bool need_additional_tx_key; + crypto::secret_key additional_tx_key; + bool use_view_tags; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(tx_version) + FIELD(tx_key) + FIELD(tx_key_pub) + FIELD(Aout) + FIELD(Bout) + FIELD(output_index) + FIELD(is_change) + FIELD(is_subaddress) + FIELD(need_additional_tx_key) + FIELD(additional_tx_key) + FIELD(use_view_tags) + END_SERIALIZE() + }; + struct GenerateOutputEphemeralKeysResponse: Response { + crypto::secret_key amount_key; + crypto::public_key additional_tx_key_pub; + crypto::public_key out_eph_public_key; + crypto::view_tag view_tag; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(amount_key) + FIELD(additional_tx_key_pub) + FIELD(out_eph_public_key) + FIELD(view_tag) + END_SERIALIZE() + }; + class GenerateOutputEphemeralKeysCommand: public Command { + public: + GenerateOutputEphemeralKeysCommand(): Command() {}; + GenerateOutputEphemeralKeysCommand(const std::string &requestData): Command(requestData) {} + }; + + struct GenCommitmentMaskRequest: Request { + rct::key amount_key; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(amount_key) + END_SERIALIZE() + }; + struct GenCommitmentMaskResponse: Response { + rct::key mask; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(mask) + END_SERIALIZE() + }; + class GenCommitmentMaskCommand: public Command { + public: + GenCommitmentMaskCommand(): Command() {}; + GenCommitmentMaskCommand(const std::string &requestData): Command(requestData) {} + }; + + struct EdchEncodeRequest: Request { + rct::ecdhTuple unmasked; + rct::key sharedSec; + bool is_short_amount; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(unmasked) + FIELD(sharedSec) + FIELD(is_short_amount) + END_SERIALIZE() + }; + struct EdchEncodeResponse: Response { + rct::ecdhTuple masked; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(masked) + END_SERIALIZE() + }; + class EdchEncodeCommand: public Command { + public: + EdchEncodeCommand(): Command() {}; + EdchEncodeCommand(const std::string &requestData): Command(requestData) {} + }; + + struct EdchDecodeRequest: Request { + rct::ecdhTuple masked; + rct::key sharedSec; + bool is_short_amount; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(masked) + FIELD(sharedSec) + FIELD(is_short_amount) + END_SERIALIZE() + }; + struct EdchDecodeResponse: Response { + rct::ecdhTuple unmasked; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(unmasked) + END_SERIALIZE() + }; + class EdchDecodeCommand: public Command { + public: + EdchDecodeCommand(): Command() {}; + EdchDecodeCommand(const std::string &requestData): Command(requestData) {} + }; + + struct ClsagPrepareRequest: Request { + rct::key p; + rct::key z; + rct::key H; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(p) + FIELD(z) + FIELD(H) + END_SERIALIZE() + }; + struct ClsagPrepareResponse: Response { + rct::key I; + rct::key D; + rct::key a; + rct::key aG; + rct::key aH; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(I) + FIELD(D) + FIELD(a) + FIELD(aG) + FIELD(aH) + END_SERIALIZE() + }; + class ClsagPrepareCommand: public Command { + public: + ClsagPrepareCommand(): Command() {}; + ClsagPrepareCommand(const std::string &requestData): Command(requestData) {} + }; + + struct ClsagHashRequest: Request { + rct::keyV data; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(data) + END_SERIALIZE() + }; + struct ClsagHashResponse: Response { + rct::key hash; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(hash) + END_SERIALIZE() + }; + class ClsagHashCommand: public Command { + public: + ClsagHashCommand(): Command() {}; + ClsagHashCommand(const std::string &requestData): Command(requestData) {} + }; + + struct ClsagSignRequest: Request { + rct::key a; + rct::key p; + rct::key z; + rct::key mu_P; + rct::key mu_C; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(a) + FIELD(p) + FIELD(z) + FIELD(mu_P) + FIELD(mu_C) + END_SERIALIZE() + }; + struct ClsagSignResponse: Response { + rct::key s; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(s) + END_SERIALIZE() + }; + class ClsagSignCommand: public Command { + public: + ClsagSignCommand(): Command() {}; + ClsagSignCommand(const std::string &requestData): Command(requestData) {} + }; + + struct CloseTxRequest: Request { + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + END_SERIALIZE() + }; + struct CloseTxResponse: Response { + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + END_SERIALIZE() + }; + class CloseTxCommand: public Command { + public: + CloseTxCommand(): Command() {}; + CloseTxCommand(const std::string &requestData): Command(requestData) {} + }; + + struct ResetRequest: Request { + unsigned int version; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(version) + END_SERIALIZE() + }; + struct ResetResponse: Response { + unsigned int version; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(version) + END_SERIALIZE() + }; + class ResetCommand: public Command { + public: + ResetCommand(): Command() {}; + ResetCommand(const std::string &requestData): Command(requestData) {} + }; + + struct PrehashRequest: Request { + std::string blob; + size_t inputs_size; + size_t outputs_size; + rct::keyV hashes; + rct::ctkeyV outPk; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Request&)*this) + FIELD(blob) + FIELD(inputs_size) + FIELD(outputs_size) + FIELD(hashes) + FIELD(outPk) + END_SERIALIZE() + }; + struct PrehashResponse: Response { + rct::key prehash; + BEGIN_SERIALIZE_OBJECT() + FIELDS((Response&)*this) + FIELD(prehash) + END_SERIALIZE() + }; + class PrehashCommand: public Command { + public: + PrehashCommand(): Command() {}; + PrehashCommand(const std::string &requestData): Command(requestData) {} + }; + } +} diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index c2a4846eec6..eba1abc1e9e 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -77,7 +77,9 @@ bool do_serialize(Archive &ar, std::vector &v) return true; } +BLOB_SERIALIZER(crypto::ec_scalar); BLOB_SERIALIZER(crypto::chacha_iv); +BLOB_SERIALIZER(crypto::chacha_key); BLOB_SERIALIZER(crypto::hash); BLOB_SERIALIZER(crypto::hash8); BLOB_SERIALIZER(crypto::public_key); diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 23ab19ce60b..231ad9a522e 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -471,7 +471,8 @@ struct Wallet enum Device { Device_Software = 0, Device_Ledger = 1, - Device_Trezor = 2 + Device_Trezor = 2, + Device_Sidekick = 3 }; enum Status { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 57ff4a13a06..fe3b1c373cc 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -124,8 +124,8 @@ using namespace cryptonote; #define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f -#define SUBADDRESS_LOOKAHEAD_MAJOR 50 -#define SUBADDRESS_LOOKAHEAD_MINOR 200 +#define SUBADDRESS_LOOKAHEAD_MAJOR 5 +#define SUBADDRESS_LOOKAHEAD_MINOR 20 #define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\003" @@ -5023,7 +5023,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st r = epee::serialization::load_t_from_binary(m_account, account_data); THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); - if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) { + if (m_key_device_type != hw::device::device_type::SOFTWARE) { LOG_PRINT_L0("Account on device. Initing device..."); hw::device &hwdev = lookup_device(m_device_name); THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);