Add support for signing strings in cli wallet

This adds support for directly signing a short string in the
command-line wallet.  Usage:

    [wallet T6T3FN]: sign_value hello
    SigV1g5v5dQrMrr1BdSsw9mA6UUd4wvvzbwfw2QqzdnVYGN9uRDzuYfH6JHCPxVgdT1m3qAgXFS3CPeKcR1JwWDj39H8E

    [wallet T6T3FN]: verify_value T6T3FNnRVvXDeck3BWLQ8NeLGUr4U3tuPPbcizdhUTyk7JY5YWExXA4d6kN2ZVuKB13TsYXBzdbvzQwpiGc8Zsi917GVuq8FK SigV1g5v5dQrMrr1BdSsw9mA6UUd4wvvzbwfw2QqzdnVYGN9uRDzuYfH6JHCPxVgdT1m3qAgXFS3CPeKcR1JwWDj39H8E hello
    Good signature from T6T3FNnRVvXDeck3BWLQ8NeLGUr4U3tuPPbcizdhUTyk7JY5YWExXA4d6kN2ZVuKB13TsYXBzdbvzQwpiGc8Zsi917GVuq8FK
    [wallet T6T3FN]: verify_value T6T3FNnRVvXDeck3BWLQ8NeLGUr4U3tuPPbcizdhUTyk7JY5YWExXA4d6kN2ZVuKB13TsYXBzdbvzQwpiGc8Zsi917GVuq8FK SigV1g5v5dQrMrr1BdSsw9mA6UUd4wvvzbwfw2QqzdnVYGN9uRDzuYfH6JHCPxVgdT1m3qAgXFS3CPeKcR1JwWDj39H8E hell
    Error: Bad signature from T6T3FNnRVvXDeck3BWLQ8NeLGUr4U3tuPPbcizdhUTyk7JY5YWExXA4d6kN2ZVuKB13TsYXBzdbvzQwpiGc8Zsi917GVuq8FK
This commit is contained in:
Jason Rhinelander 2020-11-13 18:34:50 -04:00
parent 69db2f2a6e
commit 4a57589215
2 changed files with 90 additions and 38 deletions

View file

@ -193,7 +193,9 @@ namespace
const char* USAGE_GET_DESCRIPTION("get_description");
const char* USAGE_SET_DESCRIPTION("set_description [free text note]");
const char* USAGE_SIGN("sign [<account_index>,<address_index>] <filename>");
const char* USAGE_SIGN_VALUE("sign_value [<account_index>,<address_index>] <value>");
const char* USAGE_VERIFY("verify <filename> <address> <signature>");
const char* USAGE_VERIFY_VALUE("verify_value <address> <signature> <value>");
const char* USAGE_EXPORT_KEY_IMAGES("export_key_images <filename> [requested-only]");
const char* USAGE_IMPORT_KEY_IMAGES("import_key_images <filename>");
const char* USAGE_HW_KEY_IMAGES_SYNC("hw_key_images_sync");
@ -2884,10 +2886,18 @@ Pending or Failed: "failed"|"pending", "out", Lock, Checkpointed, Time, Amount*
[this](const auto& x) { return sign(x); },
tr(USAGE_SIGN),
tr("Sign the contents of a file with the given subaddress (or the main address if not specified)"));
m_cmd_binder.set_handler("sign_value",
[this](const auto& x) { return sign_value(x); },
tr(USAGE_SIGN_VALUE),
tr("Sign a short string value with the given subaddress (or the main address if not specified)"));
m_cmd_binder.set_handler("verify",
[this](const auto& x) { return verify(x); },
tr(USAGE_VERIFY),
tr("Verify a signature on the contents of a file."));
m_cmd_binder.set_handler("verify_value",
[this](const auto& x) { return verify_value(x); },
tr(USAGE_VERIFY_VALUE),
tr("Verify a signature on the given short string value."));
m_cmd_binder.set_handler("export_key_images",
[this](const auto& x) { return export_key_images(x); },
tr(USAGE_EXPORT_KEY_IMAGES),
@ -9606,58 +9616,69 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args)
m_wallet->nettype() == cryptonote::DEVNET ? tr("Devnet") : tr("Mainnet"));
return true;
}
bool simple_wallet::sign_string(std::string_view value, const subaddress_index& index)
{
if (m_wallet->key_on_device())
fail_msg_writer() << tr("command not supported by HW wallet");
else if (m_wallet->watch_only())
fail_msg_writer() << tr("wallet is watch-only and cannot sign");
else if (m_wallet->multisig())
fail_msg_writer() << tr("This wallet is multisig and cannot sign");
else
{
SCOPED_WALLET_UNLOCK();
std::string signature = m_wallet->sign(value, index);
success_msg_writer() << signature;
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sign(const std::vector<std::string> &args)
{
if (m_wallet->key_on_device())
{
fail_msg_writer() << tr("command not supported by HW wallet");
return true;
}
if (args.size() != 1 && args.size() != 2)
{
PRINT_USAGE(USAGE_SIGN);
return true;
}
if (m_wallet->watch_only())
{
fail_msg_writer() << tr("wallet is watch-only and cannot sign");
return true;
}
if (m_wallet->multisig())
{
fail_msg_writer() << tr("This wallet is multisig and cannot sign");
return true;
}
subaddress_index index{0, 0};
if (args.size() == 2)
{
unsigned int a, b;
if (sscanf(args[0].c_str(), "%u,%u", &a, &b) != 2)
auto pieces = tools::split(args[0], ",");
if (pieces.size() != 2 || !tools::parse_int(pieces[0], index.major) || !tools::parse_int(pieces[1], index.minor))
{
fail_msg_writer() << tr("Invalid subaddress index format");
return true;
}
index.major = a;
index.minor = b;
}
const fs::path filename = fs::u8path(args.back());
std::string data;
bool r = m_wallet->load_from_file(filename, data);
if (!r)
if (!m_wallet->load_from_file(filename, data))
{
fail_msg_writer() << tr("failed to read file ") << filename.u8string();
return true;
}
SCOPED_WALLET_UNLOCK();
return sign_string(data, index);
}
bool simple_wallet::verify_string(std::string_view value, std::string_view address, std::string_view signature)
{
cryptonote::address_parse_info info;
if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address, oa_prompter))
fail_msg_writer() << tr("failed to parse address");
else if (!m_wallet->verify(value, info.address, signature))
fail_msg_writer() << tr("Bad signature from ") << address;
else
success_msg_writer() << tr("Good signature from ") << address;
std::string signature = m_wallet->sign(data, index);
success_msg_writer() << signature;
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::verify(const std::vector<std::string> &args)
{
@ -9667,35 +9688,61 @@ bool simple_wallet::verify(const std::vector<std::string> &args)
return true;
}
fs::path filename = fs::u8path(args[0]);
std::string address_string = args[1];
std::string signature= args[2];
std::string data;
bool r = m_wallet->load_from_file(filename, data);
if (!r)
if (!m_wallet->load_from_file(filename, data))
{
fail_msg_writer() << tr("failed to read file ") << filename.u8string();
return true;
}
return verify_string(data, args[1], args[2]);
}
cryptonote::address_parse_info info;
if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address_string, oa_prompter))
bool simple_wallet::sign_value(const std::vector<std::string> &args)
{
if (args.size() < 1)
{
fail_msg_writer() << tr("failed to parse address");
PRINT_USAGE(USAGE_SIGN_VALUE);
return true;
}
r = m_wallet->verify(data, info.address, signature);
if (!r)
auto begin = args.begin(), end = args.end();
subaddress_index index{0, 0};
if (args[0].find(',') != std::string::npos && args[0].find_first_not_of(",0123456789") == std::string::npos)
{
fail_msg_writer() << tr("Bad signature from ") << address_string;
// First argument is a x,y account/subaddress string, so consume it.
auto pieces = tools::split(args[0], ",");
if (pieces.size() != 2 || !tools::parse_int(pieces[0], index.major) || !tools::parse_int(pieces[1], index.minor))
{
fail_msg_writer() << tr("Invalid subaddress index format");
return true;
}
begin++;
}
else
if (begin == end)
{
success_msg_writer() << tr("Good signature from ") << address_string;
PRINT_USAGE(USAGE_SIGN_VALUE);
return true;
}
return true;
// Argument parsing has split it up on spaces, so rejoin it. This will break if you have multiple
// sequential spaces or tabs or something, but in that case you should be using the `sign` (file)
// command.
std::string value = tools::join(" ", begin, end);
return sign_string(value, index);
}
bool simple_wallet::verify_value(const std::vector<std::string> &args)
{
if (args.size() < 3)
{
PRINT_USAGE(USAGE_VERIFY_VALUE);
return true;
}
return verify_string(tools::join(" ", args.begin()+2, args.end()), args[0], args[1]);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::export_key_images(const std::vector<std::string> &args)
{

View file

@ -40,6 +40,7 @@
#include <optional>
#include <mutex>
#include <condition_variable>
#include <string_view>
#include <thread>
#include <boost/program_options/variables_map.hpp>
@ -233,8 +234,12 @@ namespace cryptonote
bool status(const std::vector<std::string> &args);
bool wallet_info(const std::vector<std::string> &args);
bool set_default_priority(const std::vector<std::string> &args);
bool sign_string(std::string_view value, const subaddress_index& index);
bool verify_string(std::string_view value, std::string_view address, std::string_view signature);
bool sign(const std::vector<std::string> &args);
bool verify(const std::vector<std::string> &args);
bool sign_value(const std::vector<std::string> &args);
bool verify_value(const std::vector<std::string> &args);
bool export_key_images(const std::vector<std::string> &args);
bool import_key_images(const std::vector<std::string> &args);
bool hw_key_images_sync(const std::vector<std::string> &args);