Skip to content

Commit

Permalink
device/trezor: correct device initialization, status check
Browse files Browse the repository at this point in the history
- checks if the device is in the correct usable state
- implements check for the v2.0.9 firmware which does not support payment IDs
- simple transacttion check, payment id fmt consistency
  • Loading branch information
ph4r05 committed Dec 4, 2018
1 parent 65b9bca commit 40acb75
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 13 deletions.
74 changes: 64 additions & 10 deletions src/device_trezor/device_trezor.cpp
Expand Up @@ -121,7 +121,8 @@ namespace trezor {
const boost::optional<cryptonote::network_type> & network_type){
AUTO_LOCK_CMD();
require_connected();
test_ping();
device_state_reset_unsafe();
require_initialized();

auto req = std::make_shared<messages::monero::MoneroGetAddress>();
this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type);
Expand All @@ -136,7 +137,8 @@ namespace trezor {
const boost::optional<cryptonote::network_type> & network_type){
AUTO_LOCK_CMD();
require_connected();
test_ping();
device_state_reset_unsafe();
require_initialized();

auto req = std::make_shared<messages::monero::MoneroGetWatchKey>();
this->set_msg_addr<messages::monero::MoneroGetWatchKey>(req.get(), path, network_type);
Expand All @@ -152,7 +154,8 @@ namespace trezor {
{
AUTO_LOCK_CMD();
require_connected();
test_ping();
device_state_reset_unsafe();
require_initialized();

std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req;

Expand Down Expand Up @@ -238,12 +241,11 @@ namespace trezor {
cpend.construction_data = cdata.tx_data;

// Transaction check
cryptonote::blobdata tx_blob;
cryptonote::transaction tx_deserialized;
bool r = cryptonote::t_serializable_object_to_blob(cpend.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");
try {
transaction_check(cdata, aux_data);
} catch(const std::exception &e){
throw exc::ProtocolException(std::string("Transaction verification failed: ") + e.what());
}

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
Expand Down Expand Up @@ -283,7 +285,8 @@ namespace trezor {
{
AUTO_LOCK_CMD();
require_connected();
test_ping();
device_state_reset_unsafe();
require_initialized();

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);
Expand All @@ -294,6 +297,7 @@ namespace trezor {
// Step: Init
auto init_msg = signer->step_init();
this->set_msg_addr(init_msg.get());
transaction_pre_check(init_msg);

auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg);
signer->step_init_ack(response);
Expand Down Expand Up @@ -351,6 +355,56 @@ namespace trezor {
signer->step_final_ack(ack_final);
}

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;

if (nonce_required){
// Versions 2.0.9 and lower do not support payment ID
CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information");
if (m_features->major_version() <= 2 && m_features->minor_version() <= 0 && m_features->patch_version() <= 9){
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);
CHECK_AND_ASSERT_THROW_MES(has_nonce == nonce_required, "Transaction nonce presence inconsistent");

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");
}
}
}

#else //WITH_DEVICE_TREZOR

void register_all(std::map<std::string, std::unique_ptr<device>> &registry) {
Expand Down
5 changes: 2 additions & 3 deletions src/device_trezor/device_trezor.hpp
Expand Up @@ -57,9 +57,8 @@ namespace trezor {
*/
class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold {
protected:
// To speed up blockchain parsing the view key maybe handle here.
crypto::secret_key viewkey;
bool has_view_key;
void transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg);
void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data);

public:
device_trezor();
Expand Down
48 changes: 48 additions & 0 deletions src/device_trezor/device_trezor_base.cpp
Expand Up @@ -139,6 +139,9 @@ namespace trezor {
}

bool device_trezor_base::disconnect() {
m_device_state.clear();
m_features.reset();

if (m_transport){
try {
m_transport->close();
Expand Down Expand Up @@ -193,6 +196,25 @@ namespace trezor {
}
}

void device_trezor_base::require_initialized(){
if (!m_features){
throw exc::TrezorException("Device state not initialized");
}

if (m_features->has_bootloader_mode() && m_features->bootloader_mode()){
throw exc::TrezorException("Device is in the bootloader mode");
}

if (m_features->has_firmware_present() && !m_features->firmware_present()){
throw exc::TrezorException("Device has no firmware loaded");
}

// Hard requirement on initialized field, has to be there.
if (!m_features->has_initialized() || !m_features->initialized()){
throw exc::TrezorException("Device is not initialized");
}
}

void device_trezor_base::call_ping_unsafe(){
auto pingMsg = std::make_shared<messages::management::Ping>();
pingMsg->set_message("PING");
Expand Down Expand Up @@ -314,6 +336,25 @@ namespace trezor {
return false;
}

void device_trezor_base::device_state_reset_unsafe()
{
require_connected();
auto initMsg = std::make_shared<messages::management::Initialize>();

if(!m_device_state.empty()) {
initMsg->set_allocated_state(&m_device_state);
}

m_features = this->client_exchange<messages::management::Features>(initMsg);
initMsg->release_state();
}

void device_trezor_base::device_state_reset()
{
AUTO_LOCK_CMD();
device_state_reset_unsafe();
}

void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg)
{
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
Expand Down Expand Up @@ -361,14 +402,21 @@ namespace trezor {
// TODO: remove passphrase from memory
m.set_passphrase(passphrase.data(), passphrase.size());
}

if (!m_device_state.empty()){
m.set_allocated_state(&m_device_state);
}

resp = call_raw(&m);
m.release_state();
}

void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg)
{
MDEBUG("on_passhprase_state_request");
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");

m_device_state = msg->state();
messages::common::PassphraseStateAck m;
resp = call_raw(&m);
}
Expand Down
13 changes: 13 additions & 0 deletions src/device_trezor/device_trezor_base.hpp
Expand Up @@ -72,6 +72,8 @@ namespace trezor {

std::string full_name;
std::vector<unsigned int> m_wallet_deriv_path;
std::string m_device_state; // returned after passphrase entry, session
std::shared_ptr<messages::management::Features> m_features; // features from the last device reset

cryptonote::network_type network_type;

Expand All @@ -80,8 +82,10 @@ namespace trezor {
//

void require_connected();
void require_initialized();
void call_ping_unsafe();
void test_ping();
void device_state_reset_unsafe();
void ensure_derivation_path() noexcept;

// Communication methods
Expand Down Expand Up @@ -221,6 +225,10 @@ namespace trezor {
return m_callback;
}

std::shared_ptr<messages::management::Features> & get_features() {
return m_features;
}

void set_derivation_path(const std::string &deriv_path) override;

/* ======================================================================= */
Expand Down Expand Up @@ -250,6 +258,11 @@ namespace trezor {
*/
bool ping();

/**
* Performs Initialize call to the Trezor, resets to known state.
*/
void device_state_reset();

// Protocol callbacks
void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg);
void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg);
Expand Down

0 comments on commit 40acb75

Please sign in to comment.