Skip to content

Commit

Permalink
device: enable to use multiple independent device wallets
Browse files Browse the repository at this point in the history
- adds a new option `--hw-device-deriv-path` to the simple wallet. Enables to specify wallet derivation path / wallet code (path avoided so it can be misinterpreted as a file path).
- devices can use different derivation mechanisms. Trezor uses standard SLIP-10 mechanism with fixed SLIP-44 prefix for Monero
- Trezor: when empty, the default derivation mechanism is used with 44'/128'/0'. When entered the derivation path is 44'/128'/PATH.
- Trezor: the path is always taken as elements are hardened (1<<31 bit turned on)
  • Loading branch information
ph4r05 committed Dec 4, 2018
1 parent 318cc78 commit d21dad7
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/device/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ namespace hw {

virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; };
virtual void set_callback(i_device_callback * callback) {};
virtual void set_derivation_path(const std::string &derivation_path) {};

/* ======================================================================= */
/* LOCKER */
Expand Down
39 changes: 38 additions & 1 deletion src/device_trezor/device_trezor_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
//

#include "device_trezor_base.hpp"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/regex.hpp>

namespace hw {
namespace trezor {
Expand All @@ -36,8 +39,9 @@ namespace trezor {

#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
#define TREZOR_BIP44_HARDENED_ZERO 0x80000000

const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000};
const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080};

device_trezor_base::device_trezor_base(): m_callback(nullptr) {

Expand Down Expand Up @@ -252,6 +256,39 @@ namespace trezor {
}
}

void device_trezor_base::ensure_derivation_path() noexcept {
if (m_wallet_deriv_path.empty()){
m_wallet_deriv_path.push_back(TREZOR_BIP44_HARDENED_ZERO); // default 0'
}
}

void device_trezor_base::set_derivation_path(const std::string &deriv_path){
this->m_wallet_deriv_path.clear();

if (deriv_path.empty() || deriv_path == "-"){
ensure_derivation_path();
return;
}

CHECK_AND_ASSERT_THROW_MES(deriv_path.size() <= 255, "Derivation path is too long");

std::vector<std::string> fields;
boost::split(fields, deriv_path, boost::is_any_of("/"));
CHECK_AND_ASSERT_THROW_MES(fields.size() <= 10, "Derivation path is too long");

boost::regex rgx("^([0-9]+)'?$");
boost::cmatch match;

this->m_wallet_deriv_path.reserve(fields.size());
for(const std::string & cur : fields){
const bool ok = boost::regex_match(cur.c_str(), match, rgx);
CHECK_AND_ASSERT_THROW_MES(ok, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur);
CHECK_AND_ASSERT_THROW_MES(match[0].length() > 0, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur);

const unsigned long cidx = std::stoul(match[0].str()) | TREZOR_BIP44_HARDENED_ZERO;
this->m_wallet_deriv_path.push_back((unsigned int)cidx);
}
}

/* ======================================================================= */
/* TREZOR PROTOCOL */
Expand Down
10 changes: 9 additions & 1 deletion src/device_trezor/device_trezor_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ namespace trezor {
i_device_callback * m_callback;

std::string full_name;
std::vector<unsigned int> m_wallet_deriv_path;

cryptonote::network_type network_type;

Expand All @@ -81,6 +82,7 @@ namespace trezor {
void require_connected();
void call_ping_unsafe();
void test_ping();
void ensure_derivation_path() noexcept;

// Communication methods

Expand Down Expand Up @@ -176,9 +178,13 @@ namespace trezor {
msg->add_address_n(x);
}
} else {
ensure_derivation_path();
for (unsigned int i : DEFAULT_BIP44_PATH) {
msg->add_address_n(i);
}
for (unsigned int i : m_wallet_deriv_path) {
msg->add_address_n(i);
}
}

if (network_type){
Expand All @@ -201,7 +207,7 @@ namespace trezor {
bool reset();

// Default derivation path for Monero
static const uint32_t DEFAULT_BIP44_PATH[3];
static const uint32_t DEFAULT_BIP44_PATH[2];

std::shared_ptr<Transport> getTransport(){
return m_transport;
Expand All @@ -215,6 +221,8 @@ namespace trezor {
return m_callback;
}

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

/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
Expand Down
2 changes: 2 additions & 0 deletions src/simplewallet/simplewallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3804,9 +3804,11 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
m_wallet->set_refresh_from_block_height(m_restore_height);

auto device_desc = tools::wallet2::device_name_option(vm);
auto device_derivation_path = tools::wallet2::device_derivation_path_option(vm);
try
{
bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
m_wallet->device_derivation_path(device_derivation_path);
m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
Expand Down
19 changes: 19 additions & 0 deletions src/wallet/wallet2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ struct options {
};
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
};

Expand Down Expand Up @@ -259,6 +260,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
auto device_name = command_line::get_arg(vm, opts.hw_device);
auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);

THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
Expand Down Expand Up @@ -314,6 +316,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
wallet->device_name(device_name);
wallet->device_derivation_path(device_derivation_path);

try
{
Expand Down Expand Up @@ -912,6 +915,11 @@ std::string wallet2::device_name_option(const boost::program_options::variables_
return command_line::get_arg(vm, options().hw_device);
}

std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm)
{
return command_line::get_arg(vm, options().hw_device_derivation_path);
}

void wallet2::init_options(boost::program_options::options_description& desc_params)
{
const options opts{};
Expand All @@ -928,6 +936,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
command_line::add_arg(desc_params, opts.kdf_rounds);
command_line::add_arg(desc_params, opts.hw_device);
command_line::add_arg(desc_params, opts.hw_device_derivation_path);
command_line::add_arg(desc_params, opts.tx_notify);
}

Expand Down Expand Up @@ -1089,6 +1098,7 @@ bool wallet2::reconnect_device()
hw::device &hwdev = lookup_device(m_device_name);
hwdev.set_name(m_device_name);
hwdev.set_network_type(m_nettype);
hwdev.set_derivation_path(m_device_derivation_path);
hwdev.set_callback(get_device_callback());
r = hwdev.init();
if (!r){
Expand Down Expand Up @@ -3160,6 +3170,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value.SetString(m_device_name.c_str(), m_device_name.size());
json.AddMember("device_name", value, json.GetAllocator());

value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
json.AddMember("device_derivation_path", value, json.GetAllocator());

// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
Expand Down Expand Up @@ -3279,6 +3292,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_device_name = "";
m_device_derivation_path = "";
m_key_device_type = hw::device::device_type::SOFTWARE;
encrypted_secret_keys = false;
}
Expand Down Expand Up @@ -3446,6 +3460,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
}
}

GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string());
m_device_derivation_path = field_device_derivation_path;
}
else
{
Expand All @@ -3460,6 +3477,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
hw::device &hwdev = lookup_device(m_device_name);
THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
hwdev.set_network_type(m_nettype);
hwdev.set_derivation_path(m_device_derivation_path);
hwdev.set_callback(get_device_callback());
THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
Expand Down Expand Up @@ -3967,6 +3985,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
auto &hwdev = lookup_device(device_name);
hwdev.set_name(device_name);
hwdev.set_network_type(m_nettype);
hwdev.set_derivation_path(m_device_derivation_path);
hwdev.set_callback(get_device_callback());

m_account.create_from_device(hwdev);
Expand Down
4 changes: 4 additions & 0 deletions src/wallet/wallet2.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ namespace tools
static bool has_testnet_option(const boost::program_options::variables_map& vm);
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
static std::string device_name_option(const boost::program_options::variables_map& vm);
static std::string device_derivation_path_option(const boost::program_options::variables_map &vm);
static void init_options(boost::program_options::options_description& desc_params);

//! Uses stdin and stdout. Returns a wallet2 if no errors.
Expand Down Expand Up @@ -978,6 +979,8 @@ namespace tools
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
const std::string & device_name() const { return m_device_name; }
void device_name(const std::string & device_name) { m_device_name = device_name; }
const std::string & device_derivation_path() const { return m_device_derivation_path; }
void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; }

bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
Expand Down Expand Up @@ -1384,6 +1387,7 @@ namespace tools
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
std::string m_device_name;
std::string m_device_derivation_path;

// Aux transaction data from device
std::unordered_map<crypto::hash, std::string> m_tx_device;
Expand Down

0 comments on commit d21dad7

Please sign in to comment.