diff --git a/src/device/device_cold.hpp b/src/device/device_cold.hpp index d435b448c..07009b9d2 100644 --- a/src/device/device_cold.hpp +++ b/src/device/device_cold.hpp @@ -162,6 +162,26 @@ namespace hw { * Live refresh process termination */ virtual void live_refresh_finish() =0; + + /** + * Requests public address, uses empty passphrase if asked for. + */ + virtual bool get_public_address_with_no_passphrase(cryptonote::account_public_address &pubkey) =0; + + /** + * Reset session ID, restart with a new session. + */ + virtual void reset_session() =0; + + /** + * Returns true if device already asked for passphrase entry before (i.e., obviously supports passphrase entry) + */ + virtual bool seen_passphrase_entry_prompt() =0; + + /** + * Uses empty passphrase for all passphrase queries. + */ + virtual void set_use_empty_passphrase(bool always_use_empty_passphrase) =0; }; } diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index c2070b0d1..03e8bbba4 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -178,6 +178,15 @@ namespace trezor { } } + bool device_trezor::get_public_address_with_no_passphrase(cryptonote::account_public_address &pubkey) { + m_reply_with_empty_passphrase = true; + const auto empty_passphrase_reverter = epee::misc_utils::create_scope_leave_handler([&]() { + m_reply_with_empty_passphrase = false; + }); + + return get_public_address(pubkey); + } + 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."); @@ -206,6 +215,18 @@ namespace trezor { get_address(index, payment_id, true); } + void device_trezor::reset_session() { + m_device_session_id.clear(); + } + + bool device_trezor::seen_passphrase_entry_prompt() { + return m_seen_passphrase_entry_message; + } + + void device_trezor::set_use_empty_passphrase(bool always_use_empty_passphrase) { + m_always_use_empty_passphrase = always_use_empty_passphrase; + } + /* ======================================================================= */ /* Helpers */ /* ======================================================================= */ diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index d91d1de3f..15337d2b4 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -205,6 +205,26 @@ namespace trezor { const ::tools::wallet2::unsigned_tx_set & unsigned_tx, ::tools::wallet2::signed_tx_set & signed_tx, hw::tx_aux_data & aux_data) override; + + /** + * Requests public address, uses empty passphrase if asked for. + */ + bool get_public_address_with_no_passphrase(cryptonote::account_public_address &pubkey) override; + + /** + * Reset session ID, restart with a new session. + */ + virtual void reset_session() override; + + /** + * Returns true if device already asked for passphrase entry before (i.e., obviously supports passphrase entry) + */ + bool seen_passphrase_entry_prompt() override; + + /** + * Uses empty passphrase for all passphrase queries. + */ + void set_use_empty_passphrase(bool use_always_empty_passphrase) override; }; #endif diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index b0b4342f5..016eb2816 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -45,7 +45,10 @@ namespace trezor { const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080}; - device_trezor_base::device_trezor_base(): m_callback(nullptr), m_last_msg_type(messages::MessageType_Success) { + device_trezor_base::device_trezor_base(): m_callback(nullptr), m_last_msg_type(messages::MessageType_Success), + m_reply_with_empty_passphrase(false), + m_always_use_empty_passphrase(false), + m_seen_passphrase_entry_message(false) { #ifdef WITH_TREZOR_DEBUGGING m_debug = false; #endif @@ -155,6 +158,9 @@ namespace trezor { TREZOR_AUTO_LOCK_DEVICE(); m_device_session_id.clear(); m_features.reset(); + m_seen_passphrase_entry_message = false; + m_reply_with_empty_passphrase = false; + m_always_use_empty_passphrase = false; if (m_transport){ try { @@ -476,6 +482,7 @@ namespace trezor { return; } + m_seen_passphrase_entry_message = true; bool on_device = true; if (msg->has__on_device() && !msg->_on_device()){ on_device = false; // do not enter on device, old devices. @@ -491,19 +498,21 @@ namespace trezor { } boost::optional passphrase; - TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, on_device); + if (m_reply_with_empty_passphrase || m_always_use_empty_passphrase) { + MDEBUG("Answering passphrase prompt with an empty passphrase, always use empty: " << m_always_use_empty_passphrase); + on_device = false; + passphrase = epee::wipeable_string(""); + } else if (m_passphrase){ + MWARNING("Answering passphrase prompt with a stored passphrase (do not use; passphrase can be seen by a potential malware / attacker)"); + on_device = false; + passphrase = epee::wipeable_string(m_passphrase.get()); + } else { + TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, on_device); + } messages::common::PassphraseAck m; m.set_on_device(on_device); if (!on_device) { - if (!passphrase && m_passphrase) { - passphrase = m_passphrase; - } - - if (m_passphrase) { - m_passphrase = boost::none; - } - if (passphrase) { m.set_allocated_passphrase(new std::string(passphrase->data(), passphrase->size())); } diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 0162b23df..de49397d5 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -101,6 +101,9 @@ namespace trezor { messages::MessageType m_last_msg_type; cryptonote::network_type network_type; + bool m_reply_with_empty_passphrase; + bool m_always_use_empty_passphrase; + bool m_seen_passphrase_entry_message; #ifdef WITH_TREZOR_DEBUGGING std::shared_ptr m_debug_callback; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5a4cafc32..3b59267b2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4467,7 +4467,26 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st m_account.set_device(hwdev); account_public_address device_account_public_address; - THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address"); + bool fetch_device_address = true; + + ::hw::device_cold* dev_cold = nullptr; + if (m_key_device_type == hw::device::device_type::TREZOR && (dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev)) != nullptr) { + THROW_WALLET_EXCEPTION_IF(!dev_cold->get_public_address_with_no_passphrase(device_account_public_address), error::wallet_internal_error, "Cannot get a device address"); + if (device_account_public_address == m_account.get_keys().m_account_address) { + LOG_PRINT_L0("Wallet opened with an empty passphrase"); + fetch_device_address = false; + dev_cold->set_use_empty_passphrase(true); + } else { + fetch_device_address = true; + LOG_PRINT_L0("Wallet opening with an empty passphrase failed. Retry again: " << fetch_device_address); + dev_cold->reset_session(); + } + } + + if (fetch_device_address) { + THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address"); + } + THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. If the device uses the passphrase feature, please check whether the passphrase was entered correctly (it may have been misspelled - different passphrases generate different wallets, passphrase is case-sensitive). " "Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) + ", wallet address: " + m_account.get_public_address_str(m_nettype));