[master] MMS: New 'config_checksum' subcommand

This commit is contained in:
rbrunner7 2020-05-04 11:31:46 +02:00
parent 82d21f5b3c
commit 7d0b7e83ef
4 changed files with 121 additions and 27 deletions

View file

@ -250,6 +250,7 @@ namespace
const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]"); const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]");
const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config"); const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config");
const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]"); const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]");
const char* USAGE_MMS_CONFIG_CHECKSUM("mms config_checksum");
const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config"); const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config");
const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>"); const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>");
const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>"); const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>");
@ -3460,7 +3461,7 @@ simple_wallet::simple_wallet()
tr("Interface with the MMS (Multisig Messaging System)\n" tr("Interface with the MMS (Multisig Messaging System)\n"
"<subcommand> is one of:\n" "<subcommand> is one of:\n"
" init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n" " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n"
" send_signer_config, start_auto_config, stop_auto_config, auto_config\n" " send_signer_config, start_auto_config, stop_auto_config, auto_config, config_checksum\n"
"Get help about a subcommand with: help_advanced mms <subcommand>")); "Get help about a subcommand with: help_advanced mms <subcommand>"));
m_cmd_binder.set_handler("mms init", m_cmd_binder.set_handler("mms init",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
@ -3529,6 +3530,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_START_AUTO_CONFIG), tr(USAGE_MMS_START_AUTO_CONFIG),
tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels")); tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels"));
m_cmd_binder.set_handler("mms config_checksum",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_CONFIG_CHECKSUM),
tr("Get a checksum that allows signers to easily check for identical MMS configuration"));
m_cmd_binder.set_handler("mms stop_auto_config", m_cmd_binder.set_handler("mms stop_auto_config",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_STOP_AUTO_CONFIG), tr(USAGE_MMS_STOP_AUTO_CONFIG),
@ -10361,6 +10366,14 @@ bool simple_wallet::user_confirms(const std::string &question)
return !std::cin.eof() && command_line::is_yes(answer); return !std::cin.eof() && command_line::is_yes(answer);
} }
bool simple_wallet::user_confirms_auto_config()
{
message_writer(console_color_red, true) << tr("WARNING: Using MMS auto-config mechanisms is not trustless");
message_writer() << tr("A malicious auto-config manager could send you info about own wallets instead of other signers' info");
message_writer() << tr("If in doubt do not use auto-config or at least compare configs using the \"mms config_checksum\" command");
return user_confirms("Accept the risks and continue?");
}
bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound) bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound)
{ {
bool valid = false; bool valid = false;
@ -10513,7 +10526,7 @@ void simple_wallet::show_message(const mms::message &m)
case mms::message_type::additional_key_set: case mms::message_type::additional_key_set:
case mms::message_type::note: case mms::message_type::note:
display_content = true; display_content = true;
ms.get_sanitized_message_text(m, sanitized_text); sanitized_text = mms::message_store::get_sanitized_text(m.content, 1000);
break; break;
default: default:
display_content = false; display_content = false;
@ -10862,6 +10875,11 @@ void simple_wallet::mms_next(const std::vector<std::string> &args)
{ {
break; break;
} }
if (!user_confirms_auto_config())
{
message_writer() << tr("You can use the \"mms delete\" command to delete any unwanted message");
break;
}
} }
ms.process_signer_config(state, m.content); ms.process_signer_config(state, m.content);
ms.stop_auto_config(); ms.stop_auto_config();
@ -11188,6 +11206,18 @@ void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args)
list_signers(ms.get_all_signers()); list_signers(ms.get_all_signers());
} }
void simple_wallet::mms_config_checksum(const std::vector<std::string> &args)
{
if (args.size() != 0)
{
fail_msg_writer() << tr("Usage: mms config_checksum");
return;
}
mms::message_store& ms = m_wallet->get_message_store();
LOCK_IDLE_SCOPE();
message_writer() << ms.get_config_checksum();
}
void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args) void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args)
{ {
if (args.size() != 0) if (args.size() != 0)
@ -11218,6 +11248,10 @@ void simple_wallet::mms_auto_config(const std::vector<std::string> &args)
fail_msg_writer() << tr("Invalid auto-config token"); fail_msg_writer() << tr("Invalid auto-config token");
return; return;
} }
if (!user_confirms_auto_config())
{
return;
}
mms::authorized_signer me = ms.get_signer(0); mms::authorized_signer me = ms.get_signer(0);
if (me.auto_config_running) if (me.auto_config_running)
{ {
@ -11330,6 +11364,10 @@ bool simple_wallet::mms(const std::vector<std::string> &args)
{ {
mms_start_auto_config(mms_args); mms_start_auto_config(mms_args);
} }
else if (sub_command == "config_checksum")
{
mms_config_checksum(mms_args);
}
else if (sub_command == "stop_auto_config") else if (sub_command == "stop_auto_config")
{ {
mms_stop_auto_config(mms_args); mms_stop_auto_config(mms_args);

View file

@ -478,6 +478,7 @@ namespace cryptonote
void ask_send_all_ready_messages(); void ask_send_all_ready_messages();
void check_for_messages(); void check_for_messages();
bool user_confirms(const std::string &question); bool user_confirms(const std::string &question);
bool user_confirms_auto_config();
bool get_message_from_arg(const std::string &arg, mms::message &m); bool get_message_from_arg(const std::string &arg, mms::message &m);
bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound); bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound);
@ -498,6 +499,7 @@ namespace cryptonote
void mms_help(const std::vector<std::string> &args); void mms_help(const std::vector<std::string> &args);
void mms_send_signer_config(const std::vector<std::string> &args); void mms_send_signer_config(const std::vector<std::string> &args);
void mms_start_auto_config(const std::vector<std::string> &args); void mms_start_auto_config(const std::vector<std::string> &args);
void mms_config_checksum(const std::vector<std::string> &args);
void mms_stop_auto_config(const std::vector<std::string> &args); void mms_stop_auto_config(const std::vector<std::string> &args);
void mms_auto_config(const std::vector<std::string> &args); void mms_auto_config(const std::vector<std::string> &args);
}; };

View file

@ -39,6 +39,7 @@
#include "serialization/binary_utils.h" #include "serialization/binary_utils.h"
#include "common/base58.h" #include "common/base58.h"
#include "common/util.h" #include "common/util.h"
#include "common/utf8.h"
#include "string_tools.h" #include "string_tools.h"
@ -129,18 +130,18 @@ void message_store::set_signer(const multisig_wallet_state &state,
authorized_signer &m = m_signers[index]; authorized_signer &m = m_signers[index];
if (label) if (label)
{ {
m.label = label.get(); m.label = get_sanitized_text(label.get(), 50);
} }
if (transport_address) if (transport_address)
{ {
m.transport_address = transport_address.get(); m.transport_address = get_sanitized_text(transport_address.get(), 200);
} }
if (monero_address) if (monero_address)
{ {
m.monero_address_known = true; m.monero_address_known = true;
m.monero_address = monero_address.get(); m.monero_address = monero_address.get();
} }
// Save to minimize the chance to loose that info (at least while in beta) // Save to minimize the chance to loose that info
save(state); save(state);
} }
@ -202,6 +203,13 @@ void message_store::unpack_signer_config(const multisig_wallet_state &state, con
} }
uint32_t num_signers = (uint32_t)signers.size(); uint32_t num_signers = (uint32_t)signers.size();
THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers)); THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers));
for (uint32_t i = 0; i < num_signers; ++i)
{
authorized_signer &m = signers[i];
m.label = get_sanitized_text(m.label, 50);
m.transport_address = get_sanitized_text(m.transport_address, 200);
m.auto_config_token = get_sanitized_text(m.auto_config_token, 20);
}
} }
void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config) void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config)
@ -242,10 +250,10 @@ void message_store::process_signer_config(const multisig_wallet_state &state, co
} }
} }
authorized_signer &modify = m_signers[take_index]; authorized_signer &modify = m_signers[take_index];
modify.label = m.label; // ALWAYS set label, see comments above modify.label = get_sanitized_text(m.label, 50); // ALWAYS set label, see comments above
if (!modify.me) if (!modify.me)
{ {
modify.transport_address = m.transport_address; modify.transport_address = get_sanitized_text(m.transport_address, 200);
modify.monero_address_known = m.monero_address_known; modify.monero_address_known = m.monero_address_known;
if (m.monero_address_known) if (m.monero_address_known)
{ {
@ -392,6 +400,45 @@ void message_store::process_auto_config_data_message(uint32_t id)
signer.auto_config_running = false; signer.auto_config_running = false;
} }
void add_hash(crypto::hash &sum, const crypto::hash &summand)
{
for (uint32_t i = 0; i < crypto::HASH_SIZE; ++i)
{
uint32_t x = (uint32_t)sum.data[i];
uint32_t y = (uint32_t)summand.data[i];
sum.data[i] = (char)((x + y) % 256);
}
}
// Calculate a checksum that allows signers to make sure they work with an identical signer config
// by exchanging and comparing checksums out-of-band i.e. not using the MMS;
// Because different signers have a different order of signers in the config work with "adding"
// individual hashes because that operation is commutative
std::string message_store::get_config_checksum() const
{
crypto::hash sum = crypto::null_hash;
uint32_t num = SWAP32LE(m_num_authorized_signers);
add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num)));
num = SWAP32LE(m_num_required_signers);
add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num)));
for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
{
const authorized_signer &m = m_signers[i];
add_hash(sum, crypto::cn_fast_hash(m.transport_address.data(), m.transport_address.size()));
if (m.monero_address_known)
{
add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_spend_public_key, sizeof(m.monero_address.m_spend_public_key)));
add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_view_public_key, sizeof(m.monero_address.m_view_public_key)));
}
}
std::string checksum_bytes;
checksum_bytes += sum.data[0];
checksum_bytes += sum.data[1];
checksum_bytes += sum.data[2];
checksum_bytes += sum.data[3];
return epee::string_tools::buff_to_hex_nodelimer(checksum_bytes);
}
void message_store::stop_auto_config() void message_store::stop_auto_config()
{ {
for (uint32_t i = 0; i < m_num_authorized_signers; ++i) for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
@ -661,32 +708,38 @@ void message_store::delete_all_messages()
m_messages.clear(); m_messages.clear();
} }
// Make a message text, which is "attacker controlled data", reasonably safe to display // Make a text, which is "attacker controlled data", reasonably safe to display
// This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command // This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command
void message_store::get_sanitized_message_text(const message &m, std::string &sanitized_text) const std::string message_store::get_sanitized_text(const std::string &text, size_t max_length)
{ {
sanitized_text.clear();
// Restrict the size to fend of DOS-style attacks with heaps of data // Restrict the size to fend of DOS-style attacks with heaps of data
size_t length = std::min(m.content.length(), (size_t)1000); size_t length = std::min(text.length(), max_length);
std::string sanitized_text = text.substr(0, length);
for (size_t i = 0; i < length; ++i) try
{ {
char c = m.content[i]; sanitized_text = tools::utf8canonical(sanitized_text, [](wint_t c)
if ((int)c < 32)
{ {
// Strip out any controls, especially ESC for getting rid of potentially dangerous if ((c < 0x20) || (c == 0x7f) || (c >= 0x80 && c <= 0x9f))
// ANSI escape sequences that a console window might interpret {
c = ' '; // Strip out any controls, especially ESC for getting rid of potentially dangerous
} // ANSI escape sequences that a console window might interpret
else if ((c == '<') || (c == '>')) c = '?';
{ }
// Make XML or HTML impossible that e.g. might contain scripts that Qt might execute else if ((c == '<') || (c == '>'))
// when displayed in the GUI wallet {
c = ' '; // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute
} // when displayed in the GUI wallet
sanitized_text += c; c = '?';
}
return c;
});
} }
catch (const std::exception &e)
{
sanitized_text = "(Illegal UTF-8 string)";
}
return sanitized_text;
} }
void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename) void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename)

View file

@ -242,6 +242,7 @@ namespace mms
size_t add_auto_config_data_message(const multisig_wallet_state &state, size_t add_auto_config_data_message(const multisig_wallet_state &state,
const std::string &auto_config_token); const std::string &auto_config_token);
void process_auto_config_data_message(uint32_t id); void process_auto_config_data_message(uint32_t id);
std::string get_config_checksum() const;
void stop_auto_config(); void stop_auto_config();
// Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command // Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command
@ -275,7 +276,7 @@ namespace mms
void set_message_processed_or_sent(uint32_t id); void set_message_processed_or_sent(uint32_t id);
void delete_message(uint32_t id); void delete_message(uint32_t id);
void delete_all_messages(); void delete_all_messages();
void get_sanitized_message_text(const message &m, std::string &sanitized_text) const; static std::string get_sanitized_text(const std::string &text, size_t max_length);
void send_message(const multisig_wallet_state &state, uint32_t id); void send_message(const multisig_wallet_state &state, uint32_t id);
bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages); bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages);