2019-03-05 16:05:34 -05:00
// Copyright (c) 2017-2019, The Monero Project
2018-08-23 17:50:53 -04:00
//
// 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 "device_trezor.hpp"
namespace hw {
namespace trezor {
2018-11-04 18:38:58 -05:00
# ifdef WITH_DEVICE_TREZOR
2018-08-23 17:50:53 -04:00
# undef MONERO_DEFAULT_LOG_CATEGORY
# define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
# define HW_TREZOR_NAME "Trezor"
static device_trezor * trezor_device = nullptr ;
static device_trezor * ensure_trezor_device ( ) {
if ( ! trezor_device ) {
trezor_device = new device_trezor ( ) ;
trezor_device - > set_name ( HW_TREZOR_NAME ) ;
}
return trezor_device ;
}
void register_all ( std : : map < std : : string , std : : unique_ptr < device > > & registry ) {
registry . insert ( std : : make_pair ( HW_TREZOR_NAME , std : : unique_ptr < device > ( ensure_trezor_device ( ) ) ) ) ;
}
void register_all ( ) {
hw : : register_device ( HW_TREZOR_NAME , ensure_trezor_device ( ) ) ;
}
device_trezor : : device_trezor ( ) {
2019-02-23 09:28:18 -05:00
m_live_refresh_in_progress = false ;
m_live_refresh_enabled = true ;
m_live_refresh_thread_running = false ;
2018-08-23 17:50:53 -04:00
}
device_trezor : : ~ device_trezor ( ) {
try {
disconnect ( ) ;
release ( ) ;
} catch ( std : : exception const & e ) {
MWARNING ( " Could not disconnect and release: " < < e . what ( ) ) ;
}
}
2019-02-23 09:28:18 -05:00
bool device_trezor : : init ( )
{
m_live_refresh_in_progress = false ;
bool r = device_trezor_base : : init ( ) ;
if ( r & & ! m_live_refresh_thread )
{
m_live_refresh_thread_running = true ;
m_live_refresh_thread . reset ( new boost : : thread ( boost : : bind ( & device_trezor : : live_refresh_thread_main , this ) ) ) ;
}
return r ;
}
bool device_trezor : : release ( )
{
m_live_refresh_in_progress = false ;
m_live_refresh_thread_running = false ;
if ( m_live_refresh_thread )
{
m_live_refresh_thread - > join ( ) ;
m_live_refresh_thread = nullptr ;
}
return device_trezor_base : : release ( ) ;
}
bool device_trezor : : disconnect ( )
{
m_live_refresh_in_progress = false ;
return device_trezor_base : : disconnect ( ) ;
}
void device_trezor : : device_state_reset_unsafe ( )
{
require_connected ( ) ;
if ( m_live_refresh_in_progress )
{
try
{
live_refresh_finish_unsafe ( ) ;
}
catch ( const std : : exception & e )
{
MERROR ( " Live refresh could not be terminated: " < < e . what ( ) ) ;
}
}
m_live_refresh_in_progress = false ;
device_trezor_base : : device_state_reset_unsafe ( ) ;
}
void device_trezor : : live_refresh_thread_main ( )
{
while ( m_live_refresh_thread_running )
{
boost : : this_thread : : sleep_for ( boost : : chrono : : milliseconds ( 100 ) ) ;
if ( ! m_live_refresh_in_progress )
{
continue ;
}
TREZOR_AUTO_LOCK_DEVICE ( ) ;
if ( ! m_transport | | ! m_live_refresh_in_progress )
{
continue ;
}
auto current_time = std : : chrono : : steady_clock : : now ( ) ;
2019-03-29 12:22:44 -04:00
if ( current_time - m_last_live_refresh_time < = std : : chrono : : minutes ( 5 ) )
2019-02-23 09:28:18 -05:00
{
continue ;
}
MTRACE ( " Closing live refresh process due to inactivity " ) ;
try
{
live_refresh_finish ( ) ;
}
catch ( const std : : exception & e )
{
MWARNING ( " Live refresh auto-finish failed: " < < e . what ( ) ) ;
}
}
}
2018-08-23 17:50:53 -04:00
/* ======================================================================= */
/* WALLET & ADDRESS */
/* ======================================================================= */
bool device_trezor : : get_public_address ( cryptonote : : account_public_address & pubkey ) {
try {
auto res = get_address ( ) ;
cryptonote : : address_parse_info info { } ;
bool r = cryptonote : : get_account_address_from_str ( info , this - > network_type , res - > address ( ) ) ;
CHECK_AND_ASSERT_MES ( r , false , " Could not parse returned address. Address parse failed: " + res - > address ( ) ) ;
CHECK_AND_ASSERT_MES ( ! info . is_subaddress , false , " Trezor returned a sub address " ) ;
pubkey = info . address ;
return true ;
} catch ( std : : exception const & e ) {
MERROR ( " Get public address exception: " < < e . what ( ) ) ;
return false ;
}
}
bool device_trezor : : get_secret_keys ( crypto : : secret_key & viewkey , crypto : : secret_key & spendkey ) {
try {
MDEBUG ( " Loading view-only key from the Trezor. Please check the Trezor for a confirmation. " ) ;
auto res = get_view_key ( ) ;
CHECK_AND_ASSERT_MES ( res - > watch_key ( ) . size ( ) = = 32 , false , " Trezor returned invalid view key " ) ;
2018-11-20 08:40:51 -05:00
// Trezor does not make use of spendkey of the device API.
// Ledger loads encrypted spendkey, Trezor loads null key (never leaves device).
// In the test (debugging mode) we need to leave this field intact as it is already set by
// the debugging code and need to remain same for the testing purposes.
# ifndef WITH_TREZOR_DEBUGGING
2018-08-23 17:50:53 -04:00
spendkey = crypto : : null_skey ; // not given
2018-11-20 08:40:51 -05:00
# endif
2018-08-23 17:50:53 -04:00
memcpy ( viewkey . data , res - > watch_key ( ) . data ( ) , 32 ) ;
return true ;
} catch ( std : : exception const & e ) {
MERROR ( " Get secret keys exception: " < < e . what ( ) ) ;
return false ;
}
}
/* ======================================================================= */
/* Helpers */
/* ======================================================================= */
/* ======================================================================= */
/* TREZOR PROTOCOL */
/* ======================================================================= */
std : : shared_ptr < messages : : monero : : MoneroAddress > device_trezor : : get_address (
const boost : : optional < std : : vector < uint32_t > > & path ,
const boost : : optional < cryptonote : : network_type > & network_type ) {
2019-02-23 09:28:18 -05:00
TREZOR_AUTO_LOCK_CMD ( ) ;
2018-08-23 17:50:53 -04:00
require_connected ( ) ;
2018-11-28 16:22:11 -05:00
device_state_reset_unsafe ( ) ;
require_initialized ( ) ;
2018-08-23 17:50:53 -04:00
auto req = std : : make_shared < messages : : monero : : MoneroGetAddress > ( ) ;
this - > set_msg_addr < messages : : monero : : MoneroGetAddress > ( req . get ( ) , path , network_type ) ;
auto response = this - > client_exchange < messages : : monero : : MoneroAddress > ( req ) ;
MTRACE ( " Get address response received " ) ;
return response ;
}
std : : shared_ptr < messages : : monero : : MoneroWatchKey > device_trezor : : get_view_key (
const boost : : optional < std : : vector < uint32_t > > & path ,
const boost : : optional < cryptonote : : network_type > & network_type ) {
2019-02-23 09:28:18 -05:00
TREZOR_AUTO_LOCK_CMD ( ) ;
2018-08-23 17:50:53 -04:00
require_connected ( ) ;
2018-11-28 16:22:11 -05:00
device_state_reset_unsafe ( ) ;
require_initialized ( ) ;
2018-08-23 17:50:53 -04:00
auto req = std : : make_shared < messages : : monero : : MoneroGetWatchKey > ( ) ;
this - > set_msg_addr < messages : : monero : : MoneroGetWatchKey > ( req . get ( ) , path , network_type ) ;
auto response = this - > client_exchange < messages : : monero : : MoneroWatchKey > ( req ) ;
MTRACE ( " Get watch key response received " ) ;
return response ;
}
2019-02-23 09:28:18 -05:00
bool device_trezor : : is_get_tx_key_supported ( ) const
{
require_initialized ( ) ;
return get_version ( ) > pack_version ( 2 , 0 , 10 ) ;
}
void device_trezor : : load_tx_key_data ( : : hw : : device_cold : : tx_key_data_t & res , const std : : string & tx_aux_data )
{
protocol : : tx : : load_tx_key_data ( res , tx_aux_data ) ;
}
void device_trezor : : get_tx_key (
std : : vector < : : crypto : : secret_key > & tx_keys ,
const : : hw : : device_cold : : tx_key_data_t & tx_aux_data ,
const : : crypto : : secret_key & view_key_priv )
{
TREZOR_AUTO_LOCK_CMD ( ) ;
require_connected ( ) ;
device_state_reset_unsafe ( ) ;
require_initialized ( ) ;
auto req = protocol : : tx : : get_tx_key ( tx_aux_data ) ;
this - > set_msg_addr < messages : : monero : : MoneroGetTxKeyRequest > ( req . get ( ) ) ;
auto response = this - > client_exchange < messages : : monero : : MoneroGetTxKeyAck > ( req ) ;
MTRACE ( " Get TX key response received " ) ;
protocol : : tx : : get_tx_key_ack ( tx_keys , tx_aux_data . tx_prefix_hash , view_key_priv , response ) ;
}
2018-08-23 17:50:53 -04:00
void device_trezor : : ki_sync ( wallet_shim * wallet ,
const std : : vector < tools : : wallet2 : : transfer_details > & transfers ,
hw : : device_cold : : exported_key_image & ski )
{
2019-02-23 09:28:18 -05:00
# define EVENT_PROGRESS(P) do { if (m_callback) {(m_callback)->on_progress(device_cold::op_progress(P)); } }while(0)
TREZOR_AUTO_LOCK_CMD ( ) ;
2018-08-23 17:50:53 -04:00
require_connected ( ) ;
2018-11-28 16:22:11 -05:00
device_state_reset_unsafe ( ) ;
require_initialized ( ) ;
2018-08-23 17:50:53 -04:00
std : : shared_ptr < messages : : monero : : MoneroKeyImageExportInitRequest > req ;
std : : vector < protocol : : ki : : MoneroTransferDetails > mtds ;
std : : vector < protocol : : ki : : MoneroExportedKeyImage > kis ;
protocol : : ki : : key_image_data ( wallet , transfers , mtds ) ;
protocol : : ki : : generate_commitment ( mtds , transfers , req ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 0. ) ;
2018-08-23 17:50:53 -04:00
this - > set_msg_addr < messages : : monero : : MoneroKeyImageExportInitRequest > ( req . get ( ) ) ;
auto ack1 = this - > client_exchange < messages : : monero : : MoneroKeyImageExportInitAck > ( req ) ;
const auto batch_size = 10 ;
const auto num_batches = ( mtds . size ( ) + batch_size - 1 ) / batch_size ;
for ( uint64_t cur = 0 ; cur < num_batches ; + + cur ) {
auto step_req = std : : make_shared < messages : : monero : : MoneroKeyImageSyncStepRequest > ( ) ;
auto idx_finish = std : : min ( static_cast < uint64_t > ( ( cur + 1 ) * batch_size ) , static_cast < uint64_t > ( mtds . size ( ) ) ) ;
for ( uint64_t idx = cur * batch_size ; idx < idx_finish ; + + idx ) {
auto added_tdis = step_req - > add_tdis ( ) ;
CHECK_AND_ASSERT_THROW_MES ( idx < mtds . size ( ) , " Invalid transfer detail index " ) ;
* added_tdis = mtds [ idx ] ;
}
auto step_ack = this - > client_exchange < messages : : monero : : MoneroKeyImageSyncStepAck > ( step_req ) ;
auto kis_size = step_ack - > kis_size ( ) ;
kis . reserve ( static_cast < size_t > ( kis_size ) ) ;
for ( int i = 0 ; i < kis_size ; + + i ) {
auto ckis = step_ack - > kis ( i ) ;
kis . push_back ( ckis ) ;
}
MTRACE ( " Batch " < < cur < < " / " < < num_batches < < " batches processed " ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( ( double ) cur * batch_size / mtds . size ( ) ) ;
2018-08-23 17:50:53 -04:00
}
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 1. ) ;
2018-08-23 17:50:53 -04:00
auto final_req = std : : make_shared < messages : : monero : : MoneroKeyImageSyncFinalRequest > ( ) ;
auto final_ack = this - > client_exchange < messages : : monero : : MoneroKeyImageSyncFinalAck > ( final_req ) ;
ski . reserve ( kis . size ( ) ) ;
for ( auto & sub : kis ) {
2019-02-23 09:28:18 -05:00
: : crypto : : signature sig { } ;
: : crypto : : key_image ki ;
char buff [ sizeof ( ki . data ) * 3 ] ;
size_t buff_len = sizeof ( buff ) ;
2018-08-23 17:50:53 -04:00
protocol : : crypto : : chacha : : decrypt ( sub . blob ( ) . data ( ) , sub . blob ( ) . size ( ) ,
reinterpret_cast < const uint8_t * > ( final_ack - > enc_key ( ) . data ( ) ) ,
2019-02-23 09:28:18 -05:00
reinterpret_cast < const uint8_t * > ( sub . iv ( ) . data ( ) ) , buff , & buff_len ) ;
CHECK_AND_ASSERT_THROW_MES ( buff_len = = sizeof ( buff ) , " Plaintext size invalid " ) ;
2018-08-23 17:50:53 -04:00
2019-02-23 09:28:18 -05:00
memcpy ( ki . data , buff , sizeof ( ki . data ) ) ;
memcpy ( sig . c . data , buff + sizeof ( ki . data ) , sizeof ( ki . data ) ) ;
memcpy ( sig . r . data , buff + 2 * sizeof ( ki . data ) , sizeof ( ki . data ) ) ;
2018-08-23 17:50:53 -04:00
ski . push_back ( std : : make_pair ( ki , sig ) ) ;
}
2019-02-23 09:28:18 -05:00
# undef EVENT_PROGRESS
}
bool device_trezor : : is_live_refresh_supported ( ) const
{
require_initialized ( ) ;
return get_version ( ) > pack_version ( 2 , 0 , 10 ) ;
2018-08-23 17:50:53 -04:00
}
2019-02-23 09:28:18 -05:00
bool device_trezor : : is_live_refresh_enabled ( ) const
{
return is_live_refresh_supported ( ) & & ( mode = = NONE | | mode = = TRANSACTION_PARSE ) & & m_live_refresh_enabled ;
}
bool device_trezor : : has_ki_live_refresh ( ) const
{
try {
return is_live_refresh_enabled ( ) ;
} catch ( const std : : exception & e ) {
MERROR ( " Could not detect if live refresh is enabled: " < < e . what ( ) ) ;
}
return false ;
}
void device_trezor : : live_refresh_start ( )
{
TREZOR_AUTO_LOCK_CMD ( ) ;
require_connected ( ) ;
live_refresh_start_unsafe ( ) ;
}
void device_trezor : : live_refresh_start_unsafe ( )
{
device_state_reset_unsafe ( ) ;
require_initialized ( ) ;
auto req = std : : make_shared < messages : : monero : : MoneroLiveRefreshStartRequest > ( ) ;
this - > set_msg_addr < messages : : monero : : MoneroLiveRefreshStartRequest > ( req . get ( ) ) ;
this - > client_exchange < messages : : monero : : MoneroLiveRefreshStartAck > ( req ) ;
m_live_refresh_in_progress = true ;
m_last_live_refresh_time = std : : chrono : : steady_clock : : now ( ) ;
}
void device_trezor : : live_refresh (
const : : crypto : : secret_key & view_key_priv ,
const crypto : : public_key & out_key ,
const crypto : : key_derivation & recv_derivation ,
size_t real_output_index ,
const cryptonote : : subaddress_index & received_index ,
cryptonote : : keypair & in_ephemeral ,
crypto : : key_image & ki
)
{
TREZOR_AUTO_LOCK_CMD ( ) ;
require_connected ( ) ;
if ( ! m_live_refresh_in_progress )
{
live_refresh_start_unsafe ( ) ;
}
m_last_live_refresh_time = std : : chrono : : steady_clock : : now ( ) ;
auto req = std : : make_shared < messages : : monero : : MoneroLiveRefreshStepRequest > ( ) ;
req - > set_out_key ( out_key . data , 32 ) ;
req - > set_recv_deriv ( recv_derivation . data , 32 ) ;
req - > set_real_out_idx ( real_output_index ) ;
req - > set_sub_addr_major ( received_index . major ) ;
req - > set_sub_addr_minor ( received_index . minor ) ;
auto ack = this - > client_exchange < messages : : monero : : MoneroLiveRefreshStepAck > ( req ) ;
protocol : : ki : : live_refresh_ack ( view_key_priv , out_key , ack , in_ephemeral , ki ) ;
}
void device_trezor : : live_refresh_finish_unsafe ( )
{
auto req = std : : make_shared < messages : : monero : : MoneroLiveRefreshFinalRequest > ( ) ;
this - > client_exchange < messages : : monero : : MoneroLiveRefreshFinalAck > ( req ) ;
m_live_refresh_in_progress = false ;
}
void device_trezor : : live_refresh_finish ( )
{
TREZOR_AUTO_LOCK_CMD ( ) ;
require_connected ( ) ;
if ( m_live_refresh_in_progress )
{
live_refresh_finish_unsafe ( ) ;
}
}
void device_trezor : : computing_key_images ( bool started )
{
try
{
if ( ! is_live_refresh_enabled ( ) )
{
return ;
}
// React only on termination as the process can auto-start itself.
if ( ! started & & m_live_refresh_in_progress )
{
live_refresh_finish ( ) ;
}
}
catch ( const std : : exception & e )
{
MWARNING ( " KI computation state change failed, started: " < < started < < " , e: " < < e . what ( ) ) ;
}
}
bool device_trezor : : compute_key_image (
const : : cryptonote : : account_keys & ack ,
const : : crypto : : public_key & out_key ,
const : : crypto : : key_derivation & recv_derivation ,
size_t real_output_index ,
const : : cryptonote : : subaddress_index & received_index ,
: : cryptonote : : keypair & in_ephemeral ,
: : crypto : : key_image & ki )
{
if ( ! is_live_refresh_enabled ( ) )
{
return false ;
}
live_refresh ( ack . m_view_secret_key , out_key , recv_derivation , real_output_index , received_index , in_ephemeral , ki ) ;
return true ;
}
2018-08-23 17:50:53 -04:00
void device_trezor : : tx_sign ( wallet_shim * wallet ,
const tools : : wallet2 : : unsigned_tx_set & unsigned_tx ,
tools : : wallet2 : : signed_tx_set & signed_tx ,
hw : : tx_aux_data & aux_data )
{
2018-10-24 14:24:11 -04:00
CHECK_AND_ASSERT_THROW_MES ( unsigned_tx . transfers . first = = 0 , " Unsuported non zero offset " ) ;
2019-02-23 09:28:18 -05:00
TREZOR_AUTO_LOCK_CMD ( ) ;
require_connected ( ) ;
device_state_reset_unsafe ( ) ;
require_initialized ( ) ;
transaction_versions_check ( unsigned_tx , aux_data ) ;
const size_t num_tx = unsigned_tx . txes . size ( ) ;
m_num_transations_to_sign = num_tx ;
2018-08-23 17:50:53 -04:00
signed_tx . key_images . clear ( ) ;
2018-10-24 14:24:11 -04:00
signed_tx . key_images . resize ( unsigned_tx . transfers . second . size ( ) ) ;
2018-08-23 17:50:53 -04:00
for ( size_t tx_idx = 0 ; tx_idx < num_tx ; + + tx_idx ) {
std : : shared_ptr < protocol : : tx : : Signer > signer ;
tx_sign ( wallet , unsigned_tx , tx_idx , aux_data , signer ) ;
auto & cdata = signer - > tdata ( ) ;
auto aux_info_cur = signer - > store_tx_aux_info ( ) ;
aux_data . tx_device_aux . emplace_back ( aux_info_cur ) ;
// Pending tx reconstruction
signed_tx . ptx . emplace_back ( ) ;
auto & cpend = signed_tx . ptx . back ( ) ;
cpend . tx = cdata . tx ;
cpend . dust = 0 ;
cpend . fee = 0 ;
cpend . dust_added_to_fee = false ;
cpend . change_dts = cdata . tx_data . change_dts ;
cpend . selected_transfers = cdata . tx_data . selected_transfers ;
cpend . key_images = " " ;
cpend . dests = cdata . tx_data . dests ;
cpend . construction_data = cdata . tx_data ;
// Transaction check
2018-11-28 16:22:11 -05:00
try {
transaction_check ( cdata , aux_data ) ;
} catch ( const std : : exception & e ) {
throw exc : : ProtocolException ( std : : string ( " Transaction verification failed: " ) + e . what ( ) ) ;
}
2018-08-23 17:50:53 -04:00
std : : string key_images ;
bool all_are_txin_to_key = std : : all_of ( cdata . tx . vin . begin ( ) , cdata . tx . vin . end ( ) , [ & ] ( const cryptonote : : txin_v & s_e ) - > bool
{
CHECKED_GET_SPECIFIC_VARIANT ( s_e , const cryptonote : : txin_to_key , in , false ) ;
key_images + = boost : : to_string ( in . k_image ) + " " ;
return true ;
} ) ;
if ( ! all_are_txin_to_key ) {
throw std : : invalid_argument ( " Not all are txin_to_key " ) ;
}
cpend . key_images = key_images ;
// KI sync
2019-02-23 09:28:18 -05:00
for ( size_t cidx = 0 , trans_max = unsigned_tx . transfers . second . size ( ) ; cidx < trans_max ; + + cidx ) {
signed_tx . key_images [ cidx ] = unsigned_tx . transfers . second [ cidx ] . m_key_image ;
}
2018-08-23 17:50:53 -04:00
size_t num_sources = cdata . tx_data . sources . size ( ) ;
CHECK_AND_ASSERT_THROW_MES ( num_sources = = cdata . source_permutation . size ( ) , " Invalid permutation size " ) ;
CHECK_AND_ASSERT_THROW_MES ( num_sources = = cdata . tx . vin . size ( ) , " Invalid tx.vin size " ) ;
for ( size_t src_idx = 0 ; src_idx < num_sources ; + + src_idx ) {
size_t idx_mapped = cdata . source_permutation [ src_idx ] ;
CHECK_AND_ASSERT_THROW_MES ( idx_mapped < cdata . tx_data . selected_transfers . size ( ) , " Invalid idx_mapped " ) ;
CHECK_AND_ASSERT_THROW_MES ( src_idx < cdata . tx . vin . size ( ) , " Invalid idx_mapped " ) ;
size_t idx_map_src = cdata . tx_data . selected_transfers [ idx_mapped ] ;
2019-02-23 09:28:18 -05:00
CHECK_AND_ASSERT_THROW_MES ( idx_map_src > = unsigned_tx . transfers . first , " Invalid offset " ) ;
2018-08-23 17:50:53 -04:00
2019-02-23 09:28:18 -05:00
idx_map_src - = unsigned_tx . transfers . first ;
2018-08-23 17:50:53 -04:00
CHECK_AND_ASSERT_THROW_MES ( idx_map_src < signed_tx . key_images . size ( ) , " Invalid key image index " ) ;
2019-02-23 09:28:18 -05:00
const auto vini = boost : : get < cryptonote : : txin_to_key > ( cdata . tx . vin [ src_idx ] ) ;
2018-08-23 17:50:53 -04:00
signed_tx . key_images [ idx_map_src ] = vini . k_image ;
}
}
2019-02-23 09:28:18 -05:00
if ( m_callback ) {
m_callback - > on_progress ( device_cold : : tx_progress ( m_num_transations_to_sign , m_num_transations_to_sign , 1 , 1 , 1 , 1 ) ) ;
}
2018-08-23 17:50:53 -04:00
}
void device_trezor : : tx_sign ( wallet_shim * wallet ,
const tools : : wallet2 : : unsigned_tx_set & unsigned_tx ,
size_t idx ,
hw : : tx_aux_data & aux_data ,
std : : shared_ptr < protocol : : tx : : Signer > & signer )
{
2019-02-23 09:28:18 -05:00
# define EVENT_PROGRESS(S, SUB, SUBMAX) do { if (m_callback) { \
( m_callback ) - > on_progress ( device_cold : : tx_progress ( idx , m_num_transations_to_sign , S , 10 , SUB , SUBMAX ) ) ; \
} } while ( 0 )
2018-08-23 17:50:53 -04:00
require_connected ( ) ;
2019-02-23 09:28:18 -05:00
if ( idx > 0 )
device_state_reset_unsafe ( ) ;
2018-11-28 16:22:11 -05:00
require_initialized ( ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 0 , 1 , 1 ) ;
2018-08-23 17:50:53 -04:00
CHECK_AND_ASSERT_THROW_MES ( idx < unsigned_tx . txes . size ( ) , " Invalid transaction index " ) ;
signer = std : : make_shared < protocol : : tx : : Signer > ( wallet , & unsigned_tx , idx , & aux_data ) ;
const tools : : wallet2 : : tx_construction_data & cur_tx = unsigned_tx . txes [ idx ] ;
unsigned long num_sources = cur_tx . sources . size ( ) ;
unsigned long num_outputs = cur_tx . splitted_dsts . size ( ) ;
// Step: Init
auto init_msg = signer - > step_init ( ) ;
this - > set_msg_addr ( init_msg . get ( ) ) ;
2018-11-28 16:22:11 -05:00
transaction_pre_check ( init_msg ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 1 , 1 , 1 ) ;
2018-08-23 17:50:53 -04:00
auto response = this - > client_exchange < messages : : monero : : MoneroTransactionInitAck > ( init_msg ) ;
signer - > step_init_ack ( response ) ;
// Step: Set transaction inputs
for ( size_t cur_src = 0 ; cur_src < num_sources ; + + cur_src ) {
auto src = signer - > step_set_input ( cur_src ) ;
auto ack = this - > client_exchange < messages : : monero : : MoneroTransactionSetInputAck > ( src ) ;
signer - > step_set_input_ack ( ack ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 2 , cur_src , num_sources ) ;
2018-08-23 17:50:53 -04:00
}
// Step: sort
auto perm_req = signer - > step_permutation ( ) ;
if ( perm_req ) {
auto perm_ack = this - > client_exchange < messages : : monero : : MoneroTransactionInputsPermutationAck > ( perm_req ) ;
signer - > step_permutation_ack ( perm_ack ) ;
}
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 3 , 1 , 1 ) ;
2018-08-23 17:50:53 -04:00
// Step: input_vini
2019-02-23 09:28:18 -05:00
for ( size_t cur_src = 0 ; cur_src < num_sources ; + + cur_src ) {
auto src = signer - > step_set_vini_input ( cur_src ) ;
auto ack = this - > client_exchange < messages : : monero : : MoneroTransactionInputViniAck > ( src ) ;
signer - > step_set_vini_input_ack ( ack ) ;
EVENT_PROGRESS ( 4 , cur_src , num_sources ) ;
2018-08-23 17:50:53 -04:00
}
// Step: all inputs set
auto all_inputs_set = signer - > step_all_inputs_set ( ) ;
auto ack_all_inputs = this - > client_exchange < messages : : monero : : MoneroTransactionAllInputsSetAck > ( all_inputs_set ) ;
signer - > step_all_inputs_set_ack ( ack_all_inputs ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 5 , 1 , 1 ) ;
2018-08-23 17:50:53 -04:00
// Step: outputs
for ( size_t cur_dst = 0 ; cur_dst < num_outputs ; + + cur_dst ) {
auto src = signer - > step_set_output ( cur_dst ) ;
auto ack = this - > client_exchange < messages : : monero : : MoneroTransactionSetOutputAck > ( src ) ;
signer - > step_set_output_ack ( ack ) ;
2019-02-23 09:28:18 -05:00
// If BP is offloaded to host, another step with computed BP may be needed.
auto offloaded_bp = signer - > step_rsig ( cur_dst ) ;
if ( offloaded_bp ) {
auto bp_ack = this - > client_exchange < messages : : monero : : MoneroTransactionSetOutputAck > ( offloaded_bp ) ;
signer - > step_set_rsig_ack ( ack ) ;
}
EVENT_PROGRESS ( 6 , cur_dst , num_outputs ) ;
2018-08-23 17:50:53 -04:00
}
// Step: all outs set
auto all_out_set = signer - > step_all_outs_set ( ) ;
auto ack_all_out_set = this - > client_exchange < messages : : monero : : MoneroTransactionAllOutSetAck > ( all_out_set ) ;
signer - > step_all_outs_set_ack ( ack_all_out_set , * this ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 7 , 1 , 1 ) ;
2018-08-23 17:50:53 -04:00
// Step: sign each input
for ( size_t cur_src = 0 ; cur_src < num_sources ; + + cur_src ) {
auto src = signer - > step_sign_input ( cur_src ) ;
auto ack_sign = this - > client_exchange < messages : : monero : : MoneroTransactionSignInputAck > ( src ) ;
signer - > step_sign_input_ack ( ack_sign ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 8 , cur_src , num_sources ) ;
2018-08-23 17:50:53 -04:00
}
// Step: final
auto final_msg = signer - > step_final ( ) ;
auto ack_final = this - > client_exchange < messages : : monero : : MoneroTransactionFinalAck > ( final_msg ) ;
signer - > step_final_ack ( ack_final ) ;
2019-02-23 09:28:18 -05:00
EVENT_PROGRESS ( 9 , 1 , 1 ) ;
# undef EVENT_PROGRESS
}
void device_trezor : : transaction_versions_check ( const : : tools : : wallet2 : : unsigned_tx_set & unsigned_tx , hw : : tx_aux_data & aux_data )
{
auto trezor_version = get_version ( ) ;
unsigned client_version = 1 ; // default client version for tx
if ( trezor_version < = pack_version ( 2 , 0 , 10 ) ) {
client_version = 0 ;
}
if ( aux_data . client_version ) {
auto wanted_client_version = aux_data . client_version . get ( ) ;
if ( wanted_client_version > client_version ) {
throw exc : : TrezorException ( " Trezor firmware 2.0.10 and lower does not support current transaction sign protocol. Please update. " ) ;
} else {
client_version = wanted_client_version ;
}
}
aux_data . client_version = client_version ;
if ( client_version = = 0 & & aux_data . bp_version & & aux_data . bp_version . get ( ) ! = 1 ) {
throw exc : : TrezorException ( " Trezor firmware 2.0.10 and lower does not support current transaction sign protocol (BPv2+). Please update. " ) ;
}
2018-08-23 17:50:53 -04:00
}
2018-11-28 16:22:11 -05:00
void device_trezor : : transaction_pre_check ( std : : shared_ptr < messages : : monero : : MoneroTransactionInitRequest > init_msg )
{
CHECK_AND_ASSERT_THROW_MES ( init_msg , " TransactionInitRequest is empty " ) ;
CHECK_AND_ASSERT_THROW_MES ( init_msg - > has_tsx_data ( ) , " TransactionInitRequest has no transaction data " ) ;
CHECK_AND_ASSERT_THROW_MES ( m_features , " Device state not initialized " ) ; // make sure the caller did not reset features
const bool nonce_required = init_msg - > tsx_data ( ) . has_payment_id ( ) & & init_msg - > tsx_data ( ) . payment_id ( ) . size ( ) > 0 ;
2018-11-20 08:40:51 -05:00
if ( nonce_required & & init_msg - > tsx_data ( ) . payment_id ( ) . size ( ) = = 8 ) {
2018-11-28 16:22:11 -05:00
// Versions 2.0.9 and lower do not support payment ID
2018-11-20 08:40:51 -05:00
if ( get_version ( ) < = pack_version ( 2 , 0 , 9 ) ) {
2018-11-28 16:22:11 -05:00
throw exc : : TrezorException ( " Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update. " ) ;
}
}
}
void device_trezor : : transaction_check ( const protocol : : tx : : TData & tdata , const hw : : tx_aux_data & aux_data )
{
// Simple serialization check
cryptonote : : blobdata tx_blob ;
cryptonote : : transaction tx_deserialized ;
bool r = cryptonote : : t_serializable_object_to_blob ( tdata . tx , tx_blob ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Transaction serialization failed " ) ;
r = cryptonote : : parse_and_validate_tx_from_blob ( tx_blob , tx_deserialized ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " Transaction deserialization failed " ) ;
// Extras check
std : : vector < cryptonote : : tx_extra_field > tx_extra_fields ;
cryptonote : : tx_extra_nonce nonce ;
r = cryptonote : : parse_tx_extra ( tdata . tx . extra , tx_extra_fields ) ;
CHECK_AND_ASSERT_THROW_MES ( r , " tx.extra parsing failed " ) ;
const bool nonce_required = tdata . tsx_data . has_payment_id ( ) & & tdata . tsx_data . payment_id ( ) . size ( ) > 0 ;
const bool has_nonce = cryptonote : : find_tx_extra_field_by_type ( tx_extra_fields , nonce ) ;
2019-02-19 20:00:14 -05:00
CHECK_AND_ASSERT_THROW_MES ( has_nonce | | ! nonce_required , " Transaction nonce not present " ) ;
2018-11-28 16:22:11 -05:00
if ( nonce_required ) {
const std : : string & payment_id = tdata . tsx_data . payment_id ( ) ;
if ( payment_id . size ( ) = = 32 ) {
crypto : : hash payment_id_long { } ;
CHECK_AND_ASSERT_THROW_MES ( cryptonote : : get_payment_id_from_tx_extra_nonce ( nonce . nonce , payment_id_long ) , " Long payment ID not present " ) ;
} else if ( payment_id . size ( ) = = 8 ) {
crypto : : hash8 payment_id_short { } ;
CHECK_AND_ASSERT_THROW_MES ( cryptonote : : get_encrypted_payment_id_from_tx_extra_nonce ( nonce . nonce , payment_id_short ) , " Short payment ID not present " ) ;
}
}
}
2018-08-23 17:50:53 -04:00
# else //WITH_DEVICE_TREZOR
void register_all ( std : : map < std : : string , std : : unique_ptr < device > > & registry ) {
}
void register_all ( ) {
}
# endif //WITH_DEVICE_TREZOR
2018-10-24 14:24:11 -04:00
} }