diff options
author | cslashm <cslashm@gmail.com> | 2018-02-20 17:01:27 +0100 |
---|---|---|
committer | Cédric <cslashm@gmail.com> | 2018-03-04 12:54:53 +0100 |
commit | e745c1e38da8e54032660894bb2db0e9a49cccf2 (patch) | |
tree | fba40dea29a948b8a4904b4de189d4adc605ec6e /src/crypto | |
parent | Merge pull request #3245 (diff) | |
download | monero-e745c1e38da8e54032660894bb2db0e9a49cccf2.tar.xz |
Code modifications to integrate Ledger HW device into monero-wallet-cli.
The basic approach it to delegate all sensitive data (master key, secret
ephemeral key, key derivation, ....) and related operations to the device.
As device has low memory, it does not keep itself the values
(except for view/spend keys) but once computed there are encrypted (with AES
are equivalent) and return back to monero-wallet-cli. When they need to be
manipulated by the device, they are decrypted on receive.
Moreover, using the client for storing the value in encrypted form limits
the modification in the client code. Those values are transfered from one
C-structure to another one as previously.
The code modification has been done with the wishes to be open to any
other hardware wallet. To achieve that a C++ class hw::Device has been
introduced. Two initial implementations are provided: the "default", which
remaps all calls to initial Monero code, and the "Ledger", which delegates
all calls to Ledger device.
Diffstat (limited to '')
-rw-r--r-- | src/crypto/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/crypto/chacha.h | 4 | ||||
-rw-r--r-- | src/crypto/crypto.cpp | 2 | ||||
-rw-r--r-- | src/crypto/crypto.h | 21 | ||||
-rw-r--r-- | src/crypto/crypto_device.cpp | 79 | ||||
-rw-r--r-- | src/crypto/hash-ops.h | 1 | ||||
-rw-r--r-- | src/crypto/slow-hash.c | 12 | ||||
-rw-r--r-- | src/cryptonote_basic/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/cryptonote_basic/account.cpp | 40 | ||||
-rw-r--r-- | src/cryptonote_basic/account.h | 11 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_basic.h | 6 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_format_utils.cpp | 137 | ||||
-rw-r--r-- | src/cryptonote_basic/cryptonote_format_utils.h | 16 | ||||
-rw-r--r-- | src/cryptonote_core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.cpp | 39 |
15 files changed, 327 insertions, 45 deletions
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 71dcedcab..35c099697 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -33,6 +33,7 @@ set(crypto_sources crypto-ops-data.c crypto-ops.c crypto.cpp + crypto_device.cpp groestl.c hash-extra-blake.c hash-extra-groestl.c @@ -77,6 +78,7 @@ monero_add_library(cncrypto target_link_libraries(cncrypto PUBLIC epee + device ${Boost_SYSTEM_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index f74d0c352..b45c3d7c7 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -69,10 +69,10 @@ namespace crypto { chacha20(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher); } - inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) { + inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, bool prehashed=false) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); tools::scrubbed_arr<char, HASH_SIZE> pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data()); + crypto::cn_slow_hash_pre(data, size, pwd_hash.data(), prehashed); memcpy(&key, pwd_hash.data(), sizeof(key)); } diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index d9b8b6787..afac105e4 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -436,7 +436,7 @@ namespace crypto { return sc_isnonzero(&c2) == 0; } - static void hash_to_ec(const public_key &key, ge_p3 &res) { + void crypto_ops::hash_to_ec(const public_key &key, ge_p3 &res) { hash h; ge_p2 point; ge_p1p1 point2; diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 81ebfb9e2..75b333473 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -46,6 +46,10 @@ #include "hex.h" #include "span.h" #include "hash.h" +#include "device/device_declare.hpp" +extern "C" { + #include "crypto-ops.h" +} namespace crypto { @@ -113,6 +117,9 @@ namespace crypto { void operator=(const crypto_ops &); ~crypto_ops(); + static void hash_to_ec(const public_key &key, ge_p3 &res) ; + friend void hash_to_ec(const public_key &key, ge_p3 &res) ; + static secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key = secret_key(), bool recover = false); friend secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover); static bool check_key(const public_key &); @@ -149,6 +156,17 @@ namespace crypto { const public_key *const *, std::size_t, const signature *); }; + secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover, hw::device &hwdev); + secret_key generate_keys(public_key &pub, secret_key &sec, hw::device &hwdev); + bool secret_key_to_public_key(const secret_key &sec, public_key &pub, hw::device &hwdev); + bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation, hw::device &hwdev); + void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res, hw::device &hwdev) ; + bool derive_public_key(const key_derivation &derivation, size_t output_index, const public_key &base, public_key &derived_key, hw::device &hwdev); + void derive_secret_key(const key_derivation &derivation, size_t output_index, const secret_key &base, secret_key &derived_key, hw::device &hwdev); + bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &derived_key, hw::device &hwdev); + void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image, hw::device &hwdev); + + /* Generate N random bytes */ inline void rand(size_t N, uint8_t *bytes) { @@ -166,6 +184,9 @@ namespace crypto { return res; } + inline void hash_to_ec(const public_key &key, ge_p3 &res) { + crypto_ops::hash_to_ec(key,res); + } /* Generate a new key pair */ inline secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key = secret_key(), bool recover = false) { diff --git a/src/crypto/crypto_device.cpp b/src/crypto/crypto_device.cpp new file mode 100644 index 000000000..5536857c8 --- /dev/null +++ b/src/crypto/crypto_device.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + +#include "crypto.h" +#include "device/device.hpp" +#include "device/log.hpp" + +namespace crypto { + + secret_key generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover, hw::device &hwdev) { + secret_key rng; + hwdev.generate_keys(pub, sec, recovery_key, recover, rng); + return rng; + } + + secret_key generate_keys(public_key &pub, secret_key &sec, hw::device &hwdev) { + secret_key rng; + hwdev.generate_keys(pub, sec, secret_key(), false, rng); + return rng; + } + + + bool secret_key_to_public_key(const secret_key &sec, public_key &pub, hw::device &hwdev) { + return hwdev.secret_key_to_public_key(sec, pub); + } + + bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation, hw::device &hwdev) { + return hwdev.generate_key_derivation(key1, key2, derivation); + } + + void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res, hw::device &hwdev) { + hwdev.derivation_to_scalar(derivation, output_index, res); + } + + bool derive_public_key(const key_derivation &derivation, size_t output_index, + const public_key &base, public_key &derived_key, hw::device &hwdev) { + return hwdev.derive_public_key(derivation, output_index, base, derived_key); + } + + void derive_secret_key(const key_derivation &derivation, size_t output_index, + const secret_key &base, secret_key &derived_key, hw::device &hwdev) { + hwdev.derive_secret_key(derivation, output_index, base, derived_key); + } + + bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &derived_key, hw::device &hwdev) { + return hwdev.derive_subaddress_public_key(out_key, derivation, output_index, derived_key); + } + + void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image, hw::device &hwdev) { + hwdev.generate_key_image(pub,sec,image); + } +}
\ No newline at end of file diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 47c6f6425..130bf02db 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -80,6 +80,7 @@ enum { void cn_fast_hash(const void *data, size_t length, char *hash); void cn_slow_hash(const void *data, size_t length, char *hash); +void cn_slow_hash_pre(const void *data, size_t length, char *hash, bool pre); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index f921b2455..36bfba9fd 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -515,8 +515,11 @@ void slow_hash_free_state(void) * @param length the length in bytes of the data * @param hash a pointer to a buffer in which the final 256 bit hash will be stored */ +void cn_slow_hash(const void *data, size_t length, char *hash) { + cn_slow_hash_pre(data,length,hash,false); +} -void cn_slow_hash(const void *data, size_t length, char *hash) +void cn_slow_hash_pre(const void *data, size_t length, char *hash, bool prehashed) { RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */ @@ -543,8 +546,11 @@ void cn_slow_hash(const void *data, size_t length, char *hash) slow_hash_allocate_state(); /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ - - hash_process(&state.hs, data, length); + if (prehashed) { + memcpy(&state.hs, data, length); + } else { + hash_process(&state.hs, data, length); + } memcpy(text, state.init, INIT_SIZE_BYTE); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index de986b6aa..d50a9df67 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -68,6 +68,7 @@ target_link_libraries(cryptonote_basic common cncrypto checkpoints + device ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index 1b38d99b3..26c823c2a 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -40,6 +40,7 @@ extern "C" } #include "cryptonote_basic_impl.h" #include "cryptonote_format_utils.h" +#include "device/device.hpp" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "account" @@ -50,6 +51,17 @@ DISABLE_VS_WARNINGS(4244 4345) namespace cryptonote { + + //----------------------------------------------------------------- + hw::device& account_keys::get_device() const { + return *m_device; + } + //----------------------------------------------------------------- + void account_keys::set_device( hw::device &hwdev) { + m_device = &hwdev; + MCDEBUG("device", "account_keys::set_device device type: "<<typeid(hwdev).name()); + } + //----------------------------------------------------------------- account_base::account_base() { @@ -116,6 +128,34 @@ DISABLE_VS_WARNINGS(4244 4345) if (m_creation_timestamp == (uint64_t)-1) // failure m_creation_timestamp = 0; // lowest value } + + //----------------------------------------------------------------- + void account_base::create_from_device(const std::string &device_name) + { + + hw::device &hwdev = hw::get_device(device_name); + m_keys.set_device(hwdev); + hwdev.set_name(device_name); + MCDEBUG("ledger", "device type: "<<typeid(hwdev).name()); + hwdev.init(); + hwdev.connect(); + hwdev.get_public_address(m_keys.m_account_address); + #ifdef DEBUG_HWDEVICE + hwdev.get_secret_keys(m_keys.m_view_secret_key, m_keys.m_spend_secret_key); + #endif + struct tm timestamp = {0}; + timestamp.tm_year = 2014 - 1900; // year 2014 + timestamp.tm_mon = 4 - 1; // month april + timestamp.tm_mday = 15; // 15th of april + timestamp.tm_hour = 0; + timestamp.tm_min = 0; + timestamp.tm_sec = 0; + + m_creation_timestamp = mktime(×tamp); + if (m_creation_timestamp == (uint64_t)-1) // failure + m_creation_timestamp = 0; // lowest value + } + //----------------------------------------------------------------- void account_base::create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey) { diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h index 79601f99c..d734599a3 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -33,6 +33,7 @@ #include "cryptonote_basic.h" #include "crypto/crypto.h" #include "serialization/keyvalue_serialization.h" +#include "device/device_declare.hpp" namespace cryptonote { @@ -43,6 +44,7 @@ namespace cryptonote crypto::secret_key m_spend_secret_key; crypto::secret_key m_view_secret_key; std::vector<crypto::secret_key> m_multisig_keys; + hw::device *m_device = &hw::get_device("default"); BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_account_address) @@ -50,6 +52,11 @@ namespace cryptonote KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys) END_KV_SERIALIZE_MAP() + + account_keys& operator=(account_keys const&) = default; + + hw::device& get_device() const ; + void set_device( hw::device &hwdev) ; }; /************************************************************************/ @@ -60,6 +67,7 @@ namespace cryptonote public: account_base(); crypto::secret_key generate(const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false); + void create_from_device(const std::string &device_name) ; void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey); bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys); @@ -68,6 +76,9 @@ namespace cryptonote std::string get_public_address_str(bool testnet) const; std::string get_public_integrated_address_str(const crypto::hash8 &payment_id, bool testnet) const; + hw::device& get_device() const {return m_keys.get_device();} + void set_device( hw::device &hwdev) {m_keys.set_device(hwdev);} + uint64_t get_createtime() const { return m_creation_timestamp; } void set_createtime(uint64_t val) { m_creation_timestamp = val; } diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 54227ad92..4c5f32a09 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -434,6 +434,12 @@ namespace cryptonote generate_keys(k.pub, k.sec); return k; } + static inline keypair generate(hw::device &hwdev) + { + keypair k; + generate_keys(k.pub, k.sec, hwdev); + return k; + } }; //--------------------------------------------------------------- diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index c668ceae5..f462d1ca9 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -41,6 +41,8 @@ using namespace epee; #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" +#include "device/device.hpp" +#include "device/log.hpp" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn" @@ -104,6 +106,16 @@ namespace cryptonote ge_p1p1_to_p3(&A2, &tmp3); ge_p3_tobytes(&AB, &A2); } + + // a copy of rct::scalarmultKey, since we can't link to libringct to avoid circular dependencies + static void secret_key_mult_public_key(crypto::public_key & aP, const crypto::public_key &P, const crypto::secret_key &a) { + ge_p3 A; + ge_p2 R; + //CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + ge_frombytes_vartime(&A, (const unsigned char*)P.data); + ge_scalarmult(&R, (const unsigned char*)a.data, &A); + ge_tobytes((unsigned char*)aP.data, &R); + } } namespace cryptonote @@ -171,6 +183,13 @@ namespace cryptonote crypto::hash_to_scalar(data, sizeof(data), m); return m; } + crypto::secret_key get_subaddress_secret_key(const crypto::secret_key& a, const subaddress_index& index, hw::device &hwdev) + { + crypto::secret_key m; + hwdev.get_subaddress_secret_key(a, index, m); + return m; + } + //--------------------------------------------------------------- std::vector<crypto::public_key> get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end) { @@ -210,29 +229,37 @@ namespace cryptonote } return pkeys; } + + std::vector<crypto::public_key> get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end, hw::device &hwdev) + { + std::vector<crypto::public_key> pkeys; + hwdev.get_subaddress_spend_public_keys(keys, account, begin, end, pkeys); + return pkeys; + } + //--------------------------------------------------------------- - bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) + bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev) { crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); - bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); + bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation, hwdev); CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); std::vector<crypto::key_derivation> additional_recv_derivations; for (size_t i = 0; i < additional_tx_public_keys.size(); ++i) { crypto::key_derivation additional_recv_derivation = AUTO_VAL_INIT(additional_recv_derivation); - r = crypto::generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation); + r = crypto::generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation, hwdev); CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")"); additional_recv_derivations.push_back(additional_recv_derivation); } - boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index); + boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev); CHECK_AND_ASSERT_MES(subaddr_recv_info, false, "key image helper: given output pubkey doesn't seem to belong to this address"); - return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki); + return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki, hwdev); } //--------------------------------------------------------------- - bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki) + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev) { if (ack.m_spend_secret_key == crypto::null_skey) { @@ -244,7 +271,7 @@ namespace cryptonote { // derive secret key with subaddress - step 1: original CN derivation crypto::secret_key scalar_step1; - crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, scalar_step1); // computes Hs(a*R || idx) + b + crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, scalar_step1, hwdev); // computes Hs(a*R || idx) + b // step 2: add Hs(a || index_major || index_minor) crypto::secret_key subaddr_sk; @@ -255,8 +282,8 @@ namespace cryptonote } else { - subaddr_sk = get_subaddress_secret_key(ack.m_view_secret_key, received_index); - sc_add((unsigned char*)&scalar_step2, (unsigned char*)&scalar_step1, (unsigned char*)&subaddr_sk); + hwdev.get_subaddress_secret_key(ack.m_view_secret_key, received_index, subaddr_sk); + hwdev.sc_secret_add(scalar_step2, scalar_step1,subaddr_sk); } in_ephemeral.sec = scalar_step2; @@ -264,17 +291,17 @@ namespace cryptonote if (ack.m_multisig_keys.empty()) { // when not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase - CHECK_AND_ASSERT_MES(crypto::secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub), false, "Failed to derive public key"); + CHECK_AND_ASSERT_MES(crypto::secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub, hwdev), false, "Failed to derive public key"); } else { // when in multisig, we only know the partial spend secret key. but we do know the full spend public key, so the output pubkey can be obtained by using the standard CN key derivation - CHECK_AND_ASSERT_MES(crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub), false, "Failed to derive public key"); + CHECK_AND_ASSERT_MES(crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub, hwdev), false, "Failed to derive public key"); // and don't forget to add the contribution from the subaddress part if (!received_index.is_zero()) { crypto::public_key subaddr_pk; - CHECK_AND_ASSERT_MES(crypto::secret_key_to_public_key(subaddr_sk, subaddr_pk), false, "Failed to derive public key"); + CHECK_AND_ASSERT_MES(crypto::secret_key_to_public_key(subaddr_sk, subaddr_pk, hwdev), false, "Failed to derive public key"); add_public_key(in_ephemeral.pub, in_ephemeral.pub, subaddr_pk); } } @@ -283,7 +310,7 @@ namespace cryptonote false, "key image helper precomp: given output pubkey doesn't match the derived one"); } - crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); + crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki, hwdev); return true; } //--------------------------------------------------------------- @@ -570,6 +597,17 @@ namespace cryptonote // Encryption and decryption are the same operation (xor with a key) return encrypt_payment_id(payment_id, public_key, secret_key); } + + //--------------------------------------------------------------- + bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key,hw::device &hwdev) + { + return hwdev.encrypt_payment_id(public_key, secret_key, payment_id); + } + bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key, hw::device &hwdev) + { + // Encryption and decryption are the same operation (xor with a key) + return encrypt_payment_id(payment_id, public_key, secret_key, hwdev); + } //--------------------------------------------------------------- bool get_inputs_money_amount(const transaction& tx, uint64_t& money) { @@ -670,10 +708,10 @@ namespace cryptonote bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, size_t output_index) { crypto::key_derivation derivation; - bool r = generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); + bool r = generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation, acc.get_device()); CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); crypto::public_key pk; - r = derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + r = derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk, acc.get_device()); CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key"); if (pk == out_key.key) return true; @@ -681,20 +719,20 @@ namespace cryptonote if (!additional_tx_pub_keys.empty()) { CHECK_AND_ASSERT_MES(output_index < additional_tx_pub_keys.size(), false, "wrong number of additional tx pubkeys"); - r = generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation); + r = generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation, acc.get_device()); CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); - r = derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + r = derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk, acc.get_device()); CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key"); return pk == out_key.key; } return false; } //--------------------------------------------------------------- - boost::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index) + boost::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index, hw::device &hwdev) { // try the shared tx pubkey crypto::public_key subaddress_spendkey; - derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey); + derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey,hwdev); auto found = subaddresses.find(subaddress_spendkey); if (found != subaddresses.end()) return subaddress_receive_info{ found->second, derivation }; @@ -702,7 +740,7 @@ namespace cryptonote if (!additional_derivations.empty()) { CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations"); - derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey); + derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey, hwdev); found = subaddresses.find(subaddress_spendkey); if (found != subaddresses.end()) return subaddress_receive_info{ found->second, additional_derivations[output_index] }; @@ -1102,4 +1140,63 @@ namespace cryptonote return key; } + //--------------------------------------------------------------- + #define CHACHA8_KEY_TAIL 0x8c + bool generate_chacha_key_from_secret_keys(const account_keys &keys, crypto::chacha_key &key) + { + const crypto::secret_key &view_key = keys.m_view_secret_key; + const crypto::secret_key &spend_key = keys.m_spend_secret_key; + tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1> data; + memcpy(data.data(), &view_key, sizeof(view_key)); + memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key)); + data[sizeof(data) - 1] = CHACHA8_KEY_TAIL; + crypto::generate_chacha_key(data.data(), sizeof(data), key); + return true; + } + + //--------------------------------------------------------------- + crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index) + { + if (index.is_zero()) + return keys.m_account_address.m_spend_public_key; + + // m = Hs(a || index_major || index_minor) + crypto::secret_key m = cryptonote::get_subaddress_secret_key(keys.m_view_secret_key, index); + + // M = m*G + crypto::public_key M; + crypto::secret_key_to_public_key(m, M); + + // D = B + M + crypto::public_key D; + add_public_key(D, keys.m_account_address.m_spend_public_key, M); // could have defined add_public_key() under src/crypto + return D; + } + + //--------------------------------------------------------------- + cryptonote::account_public_address get_subaddress(const cryptonote::account_keys& keys, const cryptonote::subaddress_index& index) + { + if (index.is_zero()) + return keys.m_account_address; + + crypto::public_key D = get_subaddress_spend_public_key(keys, index); + + // C = a*D + crypto::public_key C; + secret_key_mult_public_key(C, D, keys.m_view_secret_key); // could have defined secret_key_mult_public_key() under src/crypto + + // result: (C, D) + cryptonote::account_public_address address; + address.m_view_public_key = C; + address.m_spend_public_key = D; + return address; + } + + //--------------------------------------------------------------- + bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) + { + crypto::public_key pub; + bool r = crypto::secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; + } } diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 07a3ac92a..7bd34ea87 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -37,6 +37,7 @@ #include "crypto/crypto.h" #include "crypto/hash.h" #include <unordered_map> +#include "device/device_declare.hpp" namespace epee { @@ -52,7 +53,9 @@ namespace cryptonote bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx); bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); + bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key, hw::device &hwdev); bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); + bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key, hw::device &hwdev); template<typename T> bool find_tx_extra_field_by_type(const std::vector<tx_extra_field>& tx_extra_fields, T& field, size_t index = 0) @@ -87,15 +90,17 @@ namespace cryptonote subaddress_index index; crypto::key_derivation derivation; }; - boost::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index); + boost::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index, hw::device &hwdev); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_public_keys, std::vector<size_t>& outs, uint64_t& money_transfered); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered); bool get_tx_fee(const transaction& tx, uint64_t & fee); uint64_t get_tx_fee(const transaction& tx); crypto::secret_key get_subaddress_secret_key(const crypto::secret_key& a, const subaddress_index& index); + crypto::secret_key get_subaddress_secret_key(const crypto::secret_key& a, const subaddress_index& index, hw::device &hwdev); std::vector<crypto::public_key> get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end); - bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki); - bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki); + std::vector<crypto::public_key> get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end, hw::device &hwdev); + bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev); + bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev); void get_blob_hash(const blobdata& blob, crypto::hash& res); crypto::hash get_blob_hash(const blobdata& blob); std::string short_hash_str(const crypto::hash& h); @@ -238,4 +243,9 @@ namespace cryptonote CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ specific_type& variable_name = boost::get<specific_type>(variant_var); + cryptonote::account_public_address get_subaddress(const cryptonote::account_keys &keys, const cryptonote::subaddress_index& index); + crypto::public_key get_subaddress_spend_public_key(const cryptonote::account_keys &keys, const cryptonote::subaddress_index& index); + bool generate_chacha_key_from_secret_keys(const cryptonote::account_keys &keys, crypto::chacha_key &key); + bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub); + } diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index d8a21ae31..72844db66 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -61,6 +61,7 @@ target_link_libraries(cryptonote_core blockchain_db multisig ringct + device ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 4a10f7133..d641caf80 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -41,6 +41,7 @@ using namespace epee; #include "crypto/hash.h" #include "ringct/rctSigs.h" #include "multisig/multisig.h" +#include "device/device.hpp" using namespace crypto; @@ -194,6 +195,8 @@ namespace cryptonote //--------------------------------------------------------------- bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout) { + hw::device &hwdev = sender_account_keys.get_device(); + if (sources.empty()) { LOG_ERROR("Empty sources"); @@ -232,7 +235,7 @@ namespace cryptonote return false; } - if (!encrypt_payment_id(payment_id, view_key_pub, tx_key)) + if (!encrypt_payment_id(payment_id, view_key_pub, tx_key, hwdev)) { LOG_ERROR("Failed to encrypt payment id"); return false; @@ -280,7 +283,7 @@ namespace cryptonote keypair& in_ephemeral = in_contexts.back().in_ephemeral; crypto::key_image img; const auto& out_key = reinterpret_cast<const crypto::public_key&>(src_entr.outputs[src_entr.real_output].second.dest); - if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral, img)) + if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral,img, hwdev)) { LOG_ERROR("Key image generation failed!"); return false; @@ -338,11 +341,11 @@ namespace cryptonote // if this is a single-destination transfer to a subaddress, we set the tx pubkey to R=s*D if (num_stdaddresses == 0 && num_subaddresses == 1) { - txkey_pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(tx_key))); + txkey_pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(tx_key), hwdev)); } else { - txkey_pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(tx_key))); + txkey_pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(tx_key), hwdev)); } remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); add_tx_pub_key_to_extra(tx, txkey_pub); @@ -371,22 +374,22 @@ namespace cryptonote { additional_txkey.sec = additional_tx_keys[output_index]; if (dst_entr.is_subaddress) - additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec))); + additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec),hwdev)); else - additional_txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(additional_txkey.sec))); + additional_txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(additional_txkey.sec), hwdev)); } bool r; if (change_addr && dst_entr.addr == *change_addr) { // sending change to yourself; derivation = a*R - r = crypto::generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation); + r = crypto::generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation, hwdev); CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")"); } else { // sending to the recipient; derivation = r*A (or s*C in the subaddress scheme) - r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation); + r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation, hwdev); CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")"); } @@ -398,12 +401,14 @@ namespace cryptonote if (tx.version > 1) { crypto::secret_key scalar1; - crypto::derivation_to_scalar(derivation, output_index, scalar1); + crypto::derivation_to_scalar(derivation, output_index, scalar1, hwdev); amount_keys.push_back(rct::sk2rct(scalar1)); } - r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); + r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key, hwdev); CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); + hwdev.add_output_key_mapping(dst_entr.addr.m_view_public_key, dst_entr.addr.m_spend_public_key, output_index, amount_keys.back(), out_eph_public_key); + tx_out out; out.amount = dst_entr.amount; txout_to_key tk; @@ -579,9 +584,9 @@ namespace cryptonote get_transaction_prefix_hash(tx, tx_prefix_hash); rct::ctkeyV outSk; if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof, hwdev); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof, hwdev); // same index assumption CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); @@ -595,8 +600,8 @@ namespace cryptonote //--------------------------------------------------------------- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout) { - keypair txkey = keypair::generate(); - tx_key = txkey.sec; + hw::device &hwdev = sender_account_keys.get_device(); + hwdev.open_tx(tx_key); // figure out if we need to make additional tx pubkeys size_t num_stdaddresses = 0; @@ -608,10 +613,12 @@ namespace cryptonote { additional_tx_keys.clear(); for (const auto &d: destinations) - additional_tx_keys.push_back(keypair::generate().sec); + additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec); } - return construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, bulletproof, msout); + bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, bulletproof, msout); + hwdev.close_tx(); + return r; } //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) |