From d64e5aa71936d86f91d3f45a9da0468f97078409 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 31 Oct 2019 14:09:19 +0000 Subject: [PATCH] wallet: allow message sign/verify for subaddresses --- src/simplewallet/simplewallet.cpp | 23 +++++++++++---- src/wallet/wallet2.cpp | 18 ++++++++++-- src/wallet/wallet2.h | 2 +- src/wallet/wallet_rpc_server.cpp | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 6 +++- tests/functional_tests/sign_message.py | 31 +++++++++++++++----- utils/python-rpc/framework/wallet.py | 4 ++- 7 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 03693a57c..23452b0ca 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -214,7 +214,7 @@ namespace const char* USAGE_GET_TX_NOTE("get_tx_note "); const char* USAGE_GET_DESCRIPTION("get_description"); const char* USAGE_SET_DESCRIPTION("set_description [free text note]"); - const char* USAGE_SIGN("sign "); + const char* USAGE_SIGN("sign [,] "); const char* USAGE_VERIFY("verify
"); const char* USAGE_EXPORT_KEY_IMAGES("export_key_images [all] "); const char* USAGE_IMPORT_KEY_IMAGES("import_key_images "); @@ -3356,7 +3356,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("sign", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sign, _1), tr(USAGE_SIGN), - tr("Sign the contents of a file.")); + tr("Sign the contents of a file with the given subaddress (or the main address if not specified)")); m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::on_command, this, &simple_wallet::verify, _1), tr(USAGE_VERIFY), @@ -9593,7 +9593,7 @@ bool simple_wallet::sign(const std::vector &args) fail_msg_writer() << tr("command not supported by HW wallet"); return true; } - if (args.size() != 1) + if (args.size() != 1 && args.size() != 2) { PRINT_USAGE(USAGE_SIGN); return true; @@ -9609,7 +9609,20 @@ bool simple_wallet::sign(const std::vector &args) return true; } - std::string filename = args[0]; + subaddress_index index{0, 0}; + if (args.size() == 2) + { + unsigned int a, b; + if (sscanf(args[0].c_str(), "%u,%u", &a, &b) != 2) + { + fail_msg_writer() << tr("Invalid subaddress index format"); + return true; + } + index.major = a; + index.minor = b; + } + + const std::string &filename = args.back(); std::string data; bool r = m_wallet->load_from_file(filename, data); if (!r) @@ -9620,7 +9633,7 @@ bool simple_wallet::sign(const std::vector &args) SCOPED_WALLET_UNLOCK(); - std::string signature = m_wallet->sign(data); + std::string signature = m_wallet->sign(data, index); success_msg_writer() << signature; return true; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9b3e7e8b4..48b8bbf7e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -11777,13 +11777,27 @@ void wallet2::set_account_tag_description(const std::string& tag, const std::str m_account_tags.first[tag] = description; } -std::string wallet2::sign(const std::string &data) const +std::string wallet2::sign(const std::string &data, cryptonote::subaddress_index index) const { crypto::hash hash; crypto::cn_fast_hash(data.data(), data.size(), hash); const cryptonote::account_keys &keys = m_account.get_keys(); crypto::signature signature; - crypto::generate_signature(hash, keys.m_account_address.m_spend_public_key, keys.m_spend_secret_key, signature); + crypto::secret_key skey; + crypto::public_key pkey; + if (index.is_zero()) + { + skey = keys.m_spend_secret_key; + pkey = keys.m_account_address.m_spend_public_key; + } + else + { + skey = keys.m_spend_secret_key; + crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(keys.m_view_secret_key, index); + sc_add((unsigned char*)&skey, (unsigned char*)&m, (unsigned char*)&skey); + secret_key_to_public_key(skey, pkey); + } + crypto::generate_signature(hash, pkey, skey, signature); return std::string("SigV1") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature))); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 640565a4e..38e3afd69 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1182,7 +1182,7 @@ private: */ void set_account_tag_description(const std::string& tag, const std::string& description); - std::string sign(const std::string &data) const; + std::string sign(const std::string &data, cryptonote::subaddress_index index = {0, 0}) const; bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const; /*! diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ec21b2897..b5b572bc8 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1943,7 +1943,7 @@ namespace tools return false; } - res.signature = m_wallet->sign(req.data); + res.signature = m_wallet->sign(req.data, {req.account_index, req.address_index}); return true; } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..c823417eb 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1591,9 +1591,13 @@ namespace wallet_rpc struct request_t { std::string data; + uint32_t account_index; + uint32_t address_index; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(data); + KV_SERIALIZE(data) + KV_SERIALIZE_OPT(account_index, 0u) + KV_SERIALIZE_OPT(address_index, 0u) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; diff --git a/tests/functional_tests/sign_message.py b/tests/functional_tests/sign_message.py index de8f0cee2..9dd70f8bc 100755 --- a/tests/functional_tests/sign_message.py +++ b/tests/functional_tests/sign_message.py @@ -43,7 +43,8 @@ from framework.wallet import Wallet class MessageSigningTest(): def run_test(self): self.create() - self.check_signing() + self.check_signing(False) + self.check_signing(True) def create(self): print('Creating wallets') @@ -65,20 +66,34 @@ class MessageSigningTest(): assert res.address == self.address[i] assert res.seed == seeds[i] - def check_signing(self): - print('Signing/verifing messages') + def check_signing(self, subaddress): + print('Signing/verifing messages with ' + ('subaddress' if subaddress else 'standard address')) messages = ['foo', ''] + if subaddress: + address = [] + for i in range(2): + res = self.wallet[i].create_account() + if i == 0: + account_index = res.account_index + res = self.wallet[i].create_address(account_index = account_index) + if i == 0: + address_index = res.address_index + address.append(res.address) + else: + address = [self.address[0], self.address[1]] + account_index = 0 + address_index = 0 for message in messages: - res = self.wallet[0].sign(message) + res = self.wallet[0].sign(message, account_index = account_index, address_index = address_index) signature = res.signature for i in range(2): - res = self.wallet[i].verify(message, self.address[0], signature) + res = self.wallet[i].verify(message, address[0], signature) assert res.good - res = self.wallet[i].verify('different', self.address[0], signature) + res = self.wallet[i].verify('different', address[0], signature) assert not res.good - res = self.wallet[i].verify(message, self.address[1], signature) + res = self.wallet[i].verify(message, address[1], signature) assert not res.good - res = self.wallet[i].verify(message, self.address[0], signature + 'x') + res = self.wallet[i].verify(message, address[0], signature + 'x') assert not res.good if __name__ == '__main__': diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py index 6a3fabdc9..6371a253c 100644 --- a/utils/python-rpc/framework/wallet.py +++ b/utils/python-rpc/framework/wallet.py @@ -705,11 +705,13 @@ class Wallet(object): } return self.rpc.send_json_rpc_request(check_reserve_proof) - def sign(self, data): + def sign(self, data, account_index = 0, address_index = 0): sign = { 'method': 'sign', 'params' : { 'data': data, + 'account_index': account_index, + 'address_index': address_index, }, 'jsonrpc': '2.0', 'id': '0'