Match surae's recommendation to derive multisig keys
This commit is contained in:
parent
a36c261d7a
commit
e36f5b6021
|
@ -39,18 +39,29 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
static const rct::key multisig_salt = { {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
|
||||||
|
|
||||||
namespace cryptonote
|
namespace cryptonote
|
||||||
{
|
{
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key)
|
||||||
|
{
|
||||||
|
rct::keyV data;
|
||||||
|
data.push_back(rct::sk2rct(key));
|
||||||
|
data.push_back(multisig_salt);
|
||||||
|
return rct::rct2sk(rct::hash_to_scalar(data));
|
||||||
|
}
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
|
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
|
||||||
{
|
{
|
||||||
// the multisig spend public key is the sum of all spend public keys
|
// the multisig spend public key is the sum of all spend public keys
|
||||||
multisig_keys.clear();
|
multisig_keys.clear();
|
||||||
spend_pkey = rct::pk2rct(keys.m_account_address.m_spend_public_key);
|
const crypto::secret_key spend_secret_key = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(spend_secret_key, (crypto::public_key&)spend_pkey), "Failed to derive public key");
|
||||||
for (const auto &k: spend_keys)
|
for (const auto &k: spend_keys)
|
||||||
rct::addKeys(spend_pkey, spend_pkey, rct::pk2rct(k));
|
rct::addKeys(spend_pkey, spend_pkey, rct::pk2rct(k));
|
||||||
multisig_keys.push_back(keys.m_spend_secret_key);
|
multisig_keys.push_back(spend_secret_key);
|
||||||
spend_skey = rct::sk2rct(keys.m_spend_secret_key);
|
spend_skey = rct::sk2rct(spend_secret_key);
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
|
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey)
|
||||||
|
@ -60,23 +71,19 @@ namespace cryptonote
|
||||||
spend_skey = rct::zero();
|
spend_skey = rct::zero();
|
||||||
|
|
||||||
// create all our composite private keys
|
// create all our composite private keys
|
||||||
|
crypto::secret_key blinded_skey = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
|
||||||
for (const auto &k: spend_keys)
|
for (const auto &k: spend_keys)
|
||||||
{
|
{
|
||||||
rct::keyV data;
|
rct::key sk = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey));
|
||||||
data.push_back(rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(keys.m_spend_secret_key)));
|
crypto::secret_key msk = get_multisig_blinded_secret_key(rct::rct2sk(sk));
|
||||||
static const rct::key salt = { {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g' , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
|
multisig_keys.push_back(msk);
|
||||||
data.push_back(salt);
|
sc_add(spend_skey.bytes, spend_skey.bytes, (const unsigned char*)msk.data);
|
||||||
rct::key msk = rct::hash_to_scalar(data);
|
|
||||||
multisig_keys.push_back(rct::rct2sk(msk));
|
|
||||||
sc_add(spend_skey.bytes, spend_skey.bytes, msk.bytes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys)
|
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys)
|
||||||
{
|
{
|
||||||
crypto::hash hash;
|
rct::key view_skey = rct::sk2rct(get_multisig_blinded_secret_key(skey));
|
||||||
crypto::cn_fast_hash(&skey, sizeof(crypto::hash), hash);
|
|
||||||
rct::key view_skey = rct::hash2rct(hash);
|
|
||||||
for (const auto &k: skeys)
|
for (const auto &k: skeys)
|
||||||
sc_add(view_skey.bytes, view_skey.bytes, rct::sk2rct(k).bytes);
|
sc_add(view_skey.bytes, view_skey.bytes, rct::sk2rct(k).bytes);
|
||||||
return rct::rct2sk(view_skey);
|
return rct::rct2sk(view_skey);
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace cryptonote
|
||||||
{
|
{
|
||||||
struct account_keys;
|
struct account_keys;
|
||||||
|
|
||||||
|
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key);
|
||||||
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
||||||
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
||||||
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys);
|
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys);
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
#include "rapidjson/document.h"
|
#include "rapidjson/document.h"
|
||||||
#include "common/json_util.h"
|
#include "common/json_util.h"
|
||||||
#include "ringct/rctSigs.h"
|
#include "ringct/rctSigs.h"
|
||||||
|
#include "multisig/multisig.h"
|
||||||
#include "wallet/wallet_args.h"
|
#include "wallet/wallet_args.h"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
@ -801,8 +802,8 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// people may include their own, weed it out
|
// people may include their own, weed it out
|
||||||
const crypto::secret_key local_skey = m_wallet->get_account().get_keys().m_view_secret_key;
|
const crypto::secret_key local_skey = cryptonote::get_multisig_blinded_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
|
||||||
const crypto::public_key local_pkey = m_wallet->get_account().get_keys().m_account_address.m_spend_public_key;
|
const crypto::public_key local_pkey = m_wallet->get_multisig_signer_public_key(m_wallet->get_account().get_keys().m_spend_secret_key);
|
||||||
for (size_t i = 0; i < secret_keys.size(); ++i)
|
for (size_t i = 0; i < secret_keys.size(); ++i)
|
||||||
{
|
{
|
||||||
if (secret_keys[i] == local_skey)
|
if (secret_keys[i] == local_skey)
|
||||||
|
|
|
@ -2830,8 +2830,6 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||||
// We need an extra step, so we package all the composite public keys
|
// We need an extra step, so we package all the composite public keys
|
||||||
// we know about, and make a signed string out of them
|
// we know about, and make a signed string out of them
|
||||||
std::string data;
|
std::string data;
|
||||||
const crypto::public_key &pkey = get_account().get_keys().m_account_address.m_spend_public_key;
|
|
||||||
data += std::string((const char *)&pkey, sizeof(crypto::public_key));
|
|
||||||
const crypto::public_key signer = get_multisig_signer_public_key(rct::rct2sk(spend_skey));
|
const crypto::public_key signer = get_multisig_signer_public_key(rct::rct2sk(spend_skey));
|
||||||
data += std::string((const char *)&signer, sizeof(crypto::public_key));
|
data += std::string((const char *)&signer, sizeof(crypto::public_key));
|
||||||
|
|
||||||
|
@ -2844,7 +2842,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||||
data.resize(data.size() + sizeof(crypto::signature));
|
data.resize(data.size() + sizeof(crypto::signature));
|
||||||
crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
|
crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
|
||||||
crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
|
crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
|
||||||
crypto::generate_signature(hash, pkey, get_account().get_keys().m_spend_secret_key, signature);
|
crypto::generate_signature(hash, signer, get_multisig_blinded_secret_key(rct::rct2sk(spend_skey)), signature);
|
||||||
|
|
||||||
extra_multisig_info = std::string("MultisigxV1") + tools::base58::encode(data);
|
extra_multisig_info = std::string("MultisigxV1") + tools::base58::encode(data);
|
||||||
}
|
}
|
||||||
|
@ -2958,19 +2956,18 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std
|
||||||
std::string wallet2::get_multisig_info() const
|
std::string wallet2::get_multisig_info() const
|
||||||
{
|
{
|
||||||
// It's a signed package of private view key and public spend key
|
// It's a signed package of private view key and public spend key
|
||||||
const crypto::secret_key &skey = get_account().get_keys().m_view_secret_key;
|
const crypto::secret_key skey = cryptonote::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key);
|
||||||
const crypto::public_key &pkey = get_account().get_keys().m_account_address.m_spend_public_key;
|
const crypto::public_key pkey = get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
|
||||||
crypto::hash hash;
|
crypto::hash hash;
|
||||||
|
|
||||||
std::string data;
|
std::string data;
|
||||||
crypto::cn_fast_hash(&skey, sizeof(crypto::secret_key), hash);
|
data += std::string((const char *)&skey, sizeof(crypto::secret_key));
|
||||||
data += std::string((const char *)&hash, sizeof(crypto::hash));
|
|
||||||
data += std::string((const char *)&pkey, sizeof(crypto::public_key));
|
data += std::string((const char *)&pkey, sizeof(crypto::public_key));
|
||||||
|
|
||||||
data.resize(data.size() + sizeof(crypto::signature));
|
data.resize(data.size() + sizeof(crypto::signature));
|
||||||
crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
|
crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
|
||||||
crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
|
crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
|
||||||
crypto::generate_signature(hash, pkey, get_account().get_keys().m_spend_secret_key, signature);
|
crypto::generate_signature(hash, pkey, get_multisig_blinded_secret_key(get_account().get_keys().m_spend_secret_key), signature);
|
||||||
|
|
||||||
return std::string("MultisigV1") + tools::base58::encode(data);
|
return std::string("MultisigV1") + tools::base58::encode(data);
|
||||||
}
|
}
|
||||||
|
@ -3027,28 +3024,26 @@ bool wallet2::verify_extra_multisig_info(const std::string &data, std::unordered
|
||||||
MERROR("Multisig info decoding error");
|
MERROR("Multisig info decoding error");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (decoded.size() < sizeof(crypto::public_key) + sizeof(crypto::public_key) + sizeof(crypto::signature))
|
if (decoded.size() < sizeof(crypto::public_key) + sizeof(crypto::signature))
|
||||||
{
|
{
|
||||||
MERROR("Multisig info is corrupt");
|
MERROR("Multisig info is corrupt");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::public_key) + sizeof(crypto::signature))) % sizeof(crypto::public_key))
|
if ((decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) % sizeof(crypto::public_key))
|
||||||
{
|
{
|
||||||
MERROR("Multisig info is corrupt");
|
MERROR("Multisig info is corrupt");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t n_keys = (decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::public_key) + sizeof(crypto::signature))) / sizeof(crypto::public_key);
|
const size_t n_keys = (decoded.size() - (sizeof(crypto::public_key) + sizeof(crypto::signature))) / sizeof(crypto::public_key);
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
const crypto::public_key &pkey = *(const crypto::public_key*)(decoded.data() + offset);
|
|
||||||
offset += sizeof(pkey);
|
|
||||||
signer = *(const crypto::public_key*)(decoded.data() + offset);
|
signer = *(const crypto::public_key*)(decoded.data() + offset);
|
||||||
offset += sizeof(signer);
|
offset += sizeof(signer);
|
||||||
const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset + n_keys * sizeof(crypto::public_key));
|
const crypto::signature &signature = *(const crypto::signature*)(decoded.data() + offset + n_keys * sizeof(crypto::public_key));
|
||||||
|
|
||||||
crypto::hash hash;
|
crypto::hash hash;
|
||||||
crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
|
crypto::cn_fast_hash(decoded.data(), decoded.size() - sizeof(signature), hash);
|
||||||
if (!crypto::check_signature(hash, pkey, signature))
|
if (!crypto::check_signature(hash, signer, signature))
|
||||||
{
|
{
|
||||||
MERROR("Multisig info signature is invalid");
|
MERROR("Multisig info signature is invalid");
|
||||||
return false;
|
return false;
|
||||||
|
@ -8313,13 +8308,19 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
|
||||||
crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
|
crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const
|
||||||
{
|
{
|
||||||
crypto::public_key pkey;
|
crypto::public_key pkey;
|
||||||
crypto::secret_key_to_public_key(spend_skey, pkey);
|
crypto::secret_key_to_public_key(get_multisig_blinded_secret_key(spend_skey), pkey);
|
||||||
return pkey;
|
return pkey;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
crypto::public_key wallet2::get_multisig_signer_public_key() const
|
crypto::public_key wallet2::get_multisig_signer_public_key() const
|
||||||
{
|
{
|
||||||
CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
|
CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
|
||||||
|
if (m_multisig_threshold == m_multisig_signers.size())
|
||||||
|
{
|
||||||
|
crypto::public_key signer;
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, signer), "Failed to generate signer public key");
|
||||||
|
return signer;
|
||||||
|
}
|
||||||
return get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
|
return get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -41,6 +41,7 @@ using namespace epee;
|
||||||
#include "common/i18n.h"
|
#include "common/i18n.h"
|
||||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||||
#include "cryptonote_basic/account.h"
|
#include "cryptonote_basic/account.h"
|
||||||
|
#include "multisig/multisig.h"
|
||||||
#include "wallet_rpc_server_commands_defs.h"
|
#include "wallet_rpc_server_commands_defs.h"
|
||||||
#include "misc_language.h"
|
#include "misc_language.h"
|
||||||
#include "string_coding.h"
|
#include "string_coding.h"
|
||||||
|
@ -2424,11 +2425,10 @@ namespace tools
|
||||||
}
|
}
|
||||||
|
|
||||||
// people may include their own, weed it out
|
// people may include their own, weed it out
|
||||||
crypto::hash hash;
|
crypto::secret_key local_skey = cryptonote::get_multisig_blinded_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
|
||||||
crypto::cn_fast_hash(&m_wallet->get_account().get_keys().m_view_secret_key, sizeof(crypto::secret_key), hash);
|
|
||||||
for (size_t i = 0; i < secret_keys.size(); ++i)
|
for (size_t i = 0; i < secret_keys.size(); ++i)
|
||||||
{
|
{
|
||||||
if (rct::sk2rct(secret_keys[i]) == rct::hash2rct(hash))
|
if (rct::sk2rct(secret_keys[i]) == rct::sk2rct(local_skey))
|
||||||
{
|
{
|
||||||
secret_keys[i] = secret_keys.back();
|
secret_keys[i] = secret_keys.back();
|
||||||
public_keys[i] = public_keys.back();
|
public_keys[i] = public_keys.back();
|
||||||
|
|
|
@ -521,10 +521,12 @@ inline bool do_replay_file(const std::string& filename)
|
||||||
{ \
|
{ \
|
||||||
if (msidx_inner != msidx) \
|
if (msidx_inner != msidx) \
|
||||||
{ \
|
{ \
|
||||||
crypto::hash vkh; \
|
crypto::secret_key vkh = cryptonote::get_multisig_blinded_secret_key(account[msidx_inner].get_keys().m_view_secret_key); \
|
||||||
crypto::cn_fast_hash(&account[msidx_inner].get_keys().m_view_secret_key, sizeof(crypto::secret_key), vkh); \
|
view_keys[msidx].push_back(vkh); \
|
||||||
view_keys[msidx].push_back((const crypto::secret_key&)vkh); \
|
crypto::secret_key skh = cryptonote::get_multisig_blinded_secret_key(account[msidx_inner].get_keys().m_spend_secret_key); \
|
||||||
spend_keys[msidx].push_back(account[msidx_inner].get_keys().m_account_address.m_spend_public_key); \
|
crypto::public_key pskh; \
|
||||||
|
crypto::secret_key_to_public_key(skh, pskh); \
|
||||||
|
spend_keys[msidx].push_back(pskh); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
|
Loading…
Reference in New Issue