mirror of
https://github.com/monero-project/monero.git
synced 2024-12-23 12:09:54 +00:00
device: enable to use multiple independent device wallets
- 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)
This commit is contained in:
parent
318cc78457
commit
d21dad70dd
6 changed files with 73 additions and 2 deletions
|
@ -138,6 +138,7 @@ namespace hw {
|
||||||
|
|
||||||
virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; };
|
virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; };
|
||||||
virtual void set_callback(i_device_callback * callback) {};
|
virtual void set_callback(i_device_callback * callback) {};
|
||||||
|
virtual void set_derivation_path(const std::string &derivation_path) {};
|
||||||
|
|
||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
/* LOCKER */
|
/* LOCKER */
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "device_trezor_base.hpp"
|
#include "device_trezor_base.hpp"
|
||||||
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
namespace hw {
|
namespace hw {
|
||||||
namespace trezor {
|
namespace trezor {
|
||||||
|
@ -36,8 +39,9 @@ namespace trezor {
|
||||||
|
|
||||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
|
#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) {
|
device_trezor_base::device_trezor_base(): m_callback(nullptr) {
|
||||||
|
|
||||||
|
@ -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 */
|
/* TREZOR PROTOCOL */
|
||||||
|
|
|
@ -71,6 +71,7 @@ namespace trezor {
|
||||||
i_device_callback * m_callback;
|
i_device_callback * m_callback;
|
||||||
|
|
||||||
std::string full_name;
|
std::string full_name;
|
||||||
|
std::vector<unsigned int> m_wallet_deriv_path;
|
||||||
|
|
||||||
cryptonote::network_type network_type;
|
cryptonote::network_type network_type;
|
||||||
|
|
||||||
|
@ -81,6 +82,7 @@ namespace trezor {
|
||||||
void require_connected();
|
void require_connected();
|
||||||
void call_ping_unsafe();
|
void call_ping_unsafe();
|
||||||
void test_ping();
|
void test_ping();
|
||||||
|
void ensure_derivation_path() noexcept;
|
||||||
|
|
||||||
// Communication methods
|
// Communication methods
|
||||||
|
|
||||||
|
@ -176,9 +178,13 @@ namespace trezor {
|
||||||
msg->add_address_n(x);
|
msg->add_address_n(x);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
ensure_derivation_path();
|
||||||
for (unsigned int i : DEFAULT_BIP44_PATH) {
|
for (unsigned int i : DEFAULT_BIP44_PATH) {
|
||||||
msg->add_address_n(i);
|
msg->add_address_n(i);
|
||||||
}
|
}
|
||||||
|
for (unsigned int i : m_wallet_deriv_path) {
|
||||||
|
msg->add_address_n(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (network_type){
|
if (network_type){
|
||||||
|
@ -201,7 +207,7 @@ namespace trezor {
|
||||||
bool reset();
|
bool reset();
|
||||||
|
|
||||||
// Default derivation path for Monero
|
// 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(){
|
std::shared_ptr<Transport> getTransport(){
|
||||||
return m_transport;
|
return m_transport;
|
||||||
|
@ -215,6 +221,8 @@ namespace trezor {
|
||||||
return m_callback;
|
return m_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_derivation_path(const std::string &deriv_path) override;
|
||||||
|
|
||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
/* SETUP/TEARDOWN */
|
/* SETUP/TEARDOWN */
|
||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
|
|
|
@ -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);
|
m_wallet->set_refresh_from_block_height(m_restore_height);
|
||||||
|
|
||||||
auto device_desc = tools::wallet2::device_name_option(vm);
|
auto device_desc = tools::wallet2::device_name_option(vm);
|
||||||
|
auto device_derivation_path = tools::wallet2::device_derivation_path_option(vm);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
|
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);
|
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: ")
|
message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ")
|
||||||
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||||
|
|
|
@ -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<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 = {"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" , "" };
|
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" , "" };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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_host = command_line::get_arg(vm, opts.daemon_host);
|
||||||
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
|
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
|
||||||
auto device_name = command_line::get_arg(vm, opts.hw_device);
|
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,
|
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"));
|
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
|
||||||
|
@ -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);
|
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
||||||
wallet->set_ring_database(ringdb_path.string());
|
wallet->set_ring_database(ringdb_path.string());
|
||||||
wallet->device_name(device_name);
|
wallet->device_name(device_name);
|
||||||
|
wallet->device_derivation_path(device_derivation_path);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -912,6 +915,11 @@ std::string wallet2::device_name_option(const boost::program_options::variables_
|
||||||
return command_line::get_arg(vm, options().hw_device);
|
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)
|
void wallet2::init_options(boost::program_options::options_description& desc_params)
|
||||||
{
|
{
|
||||||
const options opts{};
|
const options opts{};
|
||||||
|
@ -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.shared_ringdb_dir);
|
||||||
command_line::add_arg(desc_params, opts.kdf_rounds);
|
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);
|
||||||
|
command_line::add_arg(desc_params, opts.hw_device_derivation_path);
|
||||||
command_line::add_arg(desc_params, opts.tx_notify);
|
command_line::add_arg(desc_params, opts.tx_notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1089,6 +1098,7 @@ bool wallet2::reconnect_device()
|
||||||
hw::device &hwdev = lookup_device(m_device_name);
|
hw::device &hwdev = lookup_device(m_device_name);
|
||||||
hwdev.set_name(m_device_name);
|
hwdev.set_name(m_device_name);
|
||||||
hwdev.set_network_type(m_nettype);
|
hwdev.set_network_type(m_nettype);
|
||||||
|
hwdev.set_derivation_path(m_device_derivation_path);
|
||||||
hwdev.set_callback(get_device_callback());
|
hwdev.set_callback(get_device_callback());
|
||||||
r = hwdev.init();
|
r = hwdev.init();
|
||||||
if (!r){
|
if (!r){
|
||||||
|
@ -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());
|
value.SetString(m_device_name.c_str(), m_device_name.size());
|
||||||
json.AddMember("device_name", value, json.GetAllocator());
|
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
|
// Serialize the JSON object
|
||||||
rapidjson::StringBuffer buffer;
|
rapidjson::StringBuffer buffer;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||||
|
@ -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_major = SUBADDRESS_LOOKAHEAD_MAJOR;
|
||||||
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
|
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
|
||||||
m_device_name = "";
|
m_device_name = "";
|
||||||
|
m_device_derivation_path = "";
|
||||||
m_key_device_type = hw::device::device_type::SOFTWARE;
|
m_key_device_type = hw::device::device_type::SOFTWARE;
|
||||||
encrypted_secret_keys = false;
|
encrypted_secret_keys = false;
|
||||||
}
|
}
|
||||||
|
@ -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";
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -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);
|
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);
|
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_network_type(m_nettype);
|
||||||
|
hwdev.set_derivation_path(m_device_derivation_path);
|
||||||
hwdev.set_callback(get_device_callback());
|
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.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);
|
THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
|
||||||
|
@ -3967,6 +3985,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
|
||||||
auto &hwdev = lookup_device(device_name);
|
auto &hwdev = lookup_device(device_name);
|
||||||
hwdev.set_name(device_name);
|
hwdev.set_name(device_name);
|
||||||
hwdev.set_network_type(m_nettype);
|
hwdev.set_network_type(m_nettype);
|
||||||
|
hwdev.set_derivation_path(m_device_derivation_path);
|
||||||
hwdev.set_callback(get_device_callback());
|
hwdev.set_callback(get_device_callback());
|
||||||
|
|
||||||
m_account.create_from_device(hwdev);
|
m_account.create_from_device(hwdev);
|
||||||
|
|
|
@ -191,6 +191,7 @@ namespace tools
|
||||||
static bool has_testnet_option(const boost::program_options::variables_map& vm);
|
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 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_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);
|
static void init_options(boost::program_options::options_description& desc_params);
|
||||||
|
|
||||||
//! Uses stdin and stdout. Returns a wallet2 if no errors.
|
//! Uses stdin and stdout. Returns a wallet2 if no errors.
|
||||||
|
@ -978,6 +979,8 @@ namespace tools
|
||||||
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
|
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; }
|
const std::string & device_name() const { return m_device_name; }
|
||||||
void device_name(const std::string & device_name) { m_device_name = 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;
|
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);
|
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
|
||||||
|
@ -1384,6 +1387,7 @@ namespace tools
|
||||||
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
|
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
|
||||||
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
|
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
|
||||||
std::string m_device_name;
|
std::string m_device_name;
|
||||||
|
std::string m_device_derivation_path;
|
||||||
|
|
||||||
// Aux transaction data from device
|
// Aux transaction data from device
|
||||||
std::unordered_map<crypto::hash, std::string> m_tx_device;
|
std::unordered_map<crypto::hash, std::string> m_tx_device;
|
||||||
|
|
Loading…
Reference in a new issue