212 lines
11 KiB
C++
212 lines
11 KiB
C++
// Copyright (c) 2021-2022, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "multisig_account.h"
|
|
|
|
#include "crypto/crypto.h"
|
|
#include "cryptonote_config.h"
|
|
#include "include_base_utils.h"
|
|
#include "multisig.h"
|
|
#include "multisig_kex_msg.h"
|
|
#include "ringct/rctOps.h"
|
|
#include "ringct/rctTypes.h"
|
|
|
|
#include <cstdint>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
|
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
|
#define MONERO_DEFAULT_LOG_CATEGORY "multisig"
|
|
|
|
namespace multisig
|
|
{
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// multisig_account: EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
multisig_account::multisig_account(const crypto::secret_key &base_privkey,
|
|
const crypto::secret_key &base_common_privkey) :
|
|
m_base_privkey{base_privkey},
|
|
m_base_common_privkey{base_common_privkey},
|
|
m_multisig_pubkey{rct::rct2pk(rct::identity())},
|
|
m_common_pubkey{rct::rct2pk(rct::identity())},
|
|
m_kex_rounds_complete{0},
|
|
m_next_round_kex_message{multisig_kex_msg{1, base_privkey, std::vector<crypto::public_key>{}, base_common_privkey}.get_msg()}
|
|
{
|
|
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(m_base_privkey, m_base_pubkey),
|
|
"Failed to derive public key");
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// multisig_account: EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
multisig_account::multisig_account(const std::uint32_t threshold,
|
|
std::vector<crypto::public_key> signers,
|
|
const crypto::secret_key &base_privkey,
|
|
const crypto::secret_key &base_common_privkey,
|
|
std::vector<crypto::secret_key> multisig_privkeys,
|
|
const crypto::secret_key &common_privkey,
|
|
const crypto::public_key &multisig_pubkey,
|
|
const crypto::public_key &common_pubkey,
|
|
const std::uint32_t kex_rounds_complete,
|
|
multisig_keyset_map_memsafe_t kex_origins_map,
|
|
std::string next_round_kex_message) :
|
|
m_base_privkey{base_privkey},
|
|
m_base_common_privkey{base_common_privkey},
|
|
m_multisig_privkeys{std::move(multisig_privkeys)},
|
|
m_common_privkey{common_privkey},
|
|
m_multisig_pubkey{multisig_pubkey},
|
|
m_common_pubkey{common_pubkey},
|
|
m_kex_rounds_complete{kex_rounds_complete},
|
|
m_kex_keys_to_origins_map{std::move(kex_origins_map)},
|
|
m_next_round_kex_message{std::move(next_round_kex_message)}
|
|
{
|
|
CHECK_AND_ASSERT_THROW_MES(kex_rounds_complete > 0, "multisig account: can't reconstruct account if its kex wasn't initialized");
|
|
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(m_base_privkey, m_base_pubkey),
|
|
"Failed to derive public key");
|
|
set_multisig_config(threshold, std::move(signers));
|
|
|
|
// kex rounds should not exceed post-kex verification round
|
|
const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(m_signers.size(), m_threshold)};
|
|
CHECK_AND_ASSERT_THROW_MES(m_kex_rounds_complete <= kex_rounds_required + 1,
|
|
"multisig account: tried to reconstruct account, but kex rounds complete counter is invalid.");
|
|
|
|
// once an account is done with kex, the 'next kex msg' is always the post-kex verification message
|
|
// i.e. the multisig account pubkey signed by the signer's privkey AND the common pubkey
|
|
if (main_kex_rounds_done())
|
|
{
|
|
m_next_round_kex_message = multisig_kex_msg{kex_rounds_required + 1,
|
|
m_base_privkey,
|
|
std::vector<crypto::public_key>{m_multisig_pubkey, m_common_pubkey}}.get_msg();
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// multisig_account: EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
bool multisig_account::account_is_active() const
|
|
{
|
|
return m_kex_rounds_complete > 0;
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// multisig_account: EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
bool multisig_account::main_kex_rounds_done() const
|
|
{
|
|
if (account_is_active())
|
|
return m_kex_rounds_complete >= multisig_kex_rounds_required(m_signers.size(), m_threshold);
|
|
else
|
|
return false;
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// multisig_account: EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
bool multisig_account::multisig_is_ready() const
|
|
{
|
|
if (main_kex_rounds_done())
|
|
return m_kex_rounds_complete >= multisig_setup_rounds_required(m_signers.size(), m_threshold);
|
|
else
|
|
return false;
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// multisig_account: INTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
void multisig_account::set_multisig_config(const std::size_t threshold, std::vector<crypto::public_key> signers)
|
|
{
|
|
// validate
|
|
CHECK_AND_ASSERT_THROW_MES(threshold > 0 && threshold <= signers.size(), "multisig account: tried to set invalid threshold.");
|
|
CHECK_AND_ASSERT_THROW_MES(signers.size() >= 2 && signers.size() <= config::MULTISIG_MAX_SIGNERS,
|
|
"multisig account: tried to set invalid number of signers.");
|
|
|
|
for (auto signer_it = signers.begin(); signer_it != signers.end(); ++signer_it)
|
|
{
|
|
// signer pubkeys must be in main subgroup, and not identity
|
|
CHECK_AND_ASSERT_THROW_MES(rct::isInMainSubgroup(rct::pk2rct(*signer_it)) && !(*signer_it == rct::rct2pk(rct::identity())),
|
|
"multisig account: tried to set signers, but a signer pubkey is invalid.");
|
|
}
|
|
|
|
// own pubkey should be in signers list
|
|
CHECK_AND_ASSERT_THROW_MES(std::find(signers.begin(), signers.end(), m_base_pubkey) != signers.end(),
|
|
"multisig account: tried to set signers, but did not find the account's base pubkey in signer list.");
|
|
|
|
// sort signers
|
|
std::sort(signers.begin(), signers.end());
|
|
|
|
// signers should all be unique
|
|
CHECK_AND_ASSERT_THROW_MES(std::adjacent_find(signers.begin(), signers.end()) == signers.end(),
|
|
"multisig account: tried to set signers, but there are duplicate signers unexpectedly.");
|
|
|
|
// set
|
|
m_threshold = threshold;
|
|
m_signers = std::move(signers);
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// multisig_account: EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
void multisig_account::initialize_kex(const std::uint32_t threshold,
|
|
std::vector<crypto::public_key> signers,
|
|
const std::vector<multisig_kex_msg> &expanded_msgs_rnd1)
|
|
{
|
|
CHECK_AND_ASSERT_THROW_MES(!account_is_active(), "multisig account: tried to initialize kex, but already initialized");
|
|
|
|
// only mutate account if update succeeds
|
|
multisig_account temp_account{*this};
|
|
temp_account.set_multisig_config(threshold, std::move(signers));
|
|
temp_account.kex_update_impl(expanded_msgs_rnd1, false);
|
|
*this = std::move(temp_account);
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// multisig_account: EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
void multisig_account::kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
|
|
const bool force_update_use_with_caution /*= false*/)
|
|
{
|
|
CHECK_AND_ASSERT_THROW_MES(account_is_active(), "multisig account: tried to update kex, but kex isn't initialized yet.");
|
|
CHECK_AND_ASSERT_THROW_MES(!multisig_is_ready(), "multisig account: tried to update kex, but kex is already complete.");
|
|
|
|
multisig_account temp_account{*this};
|
|
temp_account.kex_update_impl(expanded_msgs, force_update_use_with_caution);
|
|
*this = std::move(temp_account);
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
std::uint32_t multisig_kex_rounds_required(const std::uint32_t num_signers, const std::uint32_t threshold)
|
|
{
|
|
CHECK_AND_ASSERT_THROW_MES(num_signers >= threshold, "num_signers must be >= threshold");
|
|
CHECK_AND_ASSERT_THROW_MES(threshold >= 1, "threshold must be >= 1");
|
|
return num_signers - threshold + 1;
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// EXTERNAL
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
std::uint32_t multisig_setup_rounds_required(const std::uint32_t num_signers, const std::uint32_t threshold)
|
|
{
|
|
return multisig_kex_rounds_required(num_signers, threshold) + 1;
|
|
}
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
} //namespace multisig
|