aboutsummaryrefslogtreecommitdiff
path: root/src/multisig/multisig_account_kex_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/multisig/multisig_account_kex_impl.cpp')
-rw-r--r--src/multisig/multisig_account_kex_impl.cpp173
1 files changed, 121 insertions, 52 deletions
diff --git a/src/multisig/multisig_account_kex_impl.cpp b/src/multisig/multisig_account_kex_impl.cpp
index ef0acf307..9be760e78 100644
--- a/src/multisig/multisig_account_kex_impl.cpp
+++ b/src/multisig/multisig_account_kex_impl.cpp
@@ -567,7 +567,7 @@ namespace multisig
// note: do NOT remove the local signer from the pubkey origins map, since the post-kex round can be force-updated with
// just the local signer's post-kex message (if the local signer were removed, then the post-kex message's pubkeys
- // would be completely lost)
+ // would be completely deleted)
// evaluate pubkeys collected
@@ -608,7 +608,7 @@ namespace multisig
* INTERNAL
*
* brief: multisig_kex_process_round_msgs - Process kex messages for the active kex round.
- * - A wrapper around evaluate_multisig_kex_round_msgs() -> multisig_kex_make_next_msg().
+ * - A wrapper around evaluate_multisig_kex_round_msgs() -> multisig_kex_make_round_keys().
* - In other words, evaluate the input messages and try to make a message for the next round.
* - Note: Must be called on the final round's msgs to evaluate the final key components
* recommended by other participants.
@@ -623,7 +623,7 @@ namespace multisig
* outparam: keys_to_origins_map_out - map between round keys and identity keys
* - If in the final round, these are key shares recommended by other signers for the final aggregate key.
* - Otherwise, these are the local account's DH derivations for the next round.
- * - See multisig_kex_make_next_msg() for an explanation.
+ * - See multisig_kex_make_round_keys() for an explanation.
* return: multisig kex message for next round, or empty message if 'current_round' is the final round
*/
//----------------------------------------------------------------------------------------------------------------------
@@ -684,59 +684,67 @@ namespace multisig
//----------------------------------------------------------------------------------------------------------------------
// multisig_account: INTERNAL
//----------------------------------------------------------------------------------------------------------------------
- void multisig_account::initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
- const std::uint32_t kex_rounds_required,
- std::vector<crypto::public_key> &exclude_pubkeys_out)
+ std::vector<crypto::public_key> multisig_account::get_kex_exclude_pubkeys() const
{
+ // exclude all keys the local account recommends
+ std::vector<crypto::public_key> exclude_pubkeys;
+
if (m_kex_rounds_complete == 0)
{
- // the first round of kex msgs will contain each participant's base pubkeys and ancillary privkeys
+ // in the first round, only the local pubkey is recommended by the local signer
+ exclude_pubkeys.emplace_back(m_base_pubkey);
+ }
+ else
+ {
+ // in other rounds, kex msgs will contain participants' shared keys, so ignore shared keys the account helped
+ // create for this round
+ for (const auto &shared_key_with_origins : m_kex_keys_to_origins_map)
+ exclude_pubkeys.emplace_back(shared_key_with_origins.first);
+ }
- // collect participants' base common privkey shares
- // note: duplicate privkeys are acceptable, and duplicates due to duplicate signers
- // will be blocked by duplicate-signer errors after this function is called
- std::vector<crypto::secret_key> participant_base_common_privkeys;
- participant_base_common_privkeys.reserve(expanded_msgs.size() + 1);
+ return exclude_pubkeys;
+ }
+ //----------------------------------------------------------------------------------------------------------------------
+ // multisig_account: INTERNAL
+ //----------------------------------------------------------------------------------------------------------------------
+ void multisig_account::initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
+ const std::uint32_t kex_rounds_required)
+ {
+ // initialization is only needed during the first round
+ if (m_kex_rounds_complete > 0)
+ return;
- // add local ancillary base privkey
- participant_base_common_privkeys.emplace_back(m_base_common_privkey);
+ // the first round of kex msgs will contain each participant's base pubkeys and ancillary privkeys, so we prepare
+ // them here
- // add other signers' base common privkeys
- for (const auto &expanded_msg : expanded_msgs)
- {
- if (expanded_msg.get_signing_pubkey() != m_base_pubkey)
- {
- participant_base_common_privkeys.emplace_back(expanded_msg.get_msg_privkey());
- }
- }
+ // collect participants' base common privkey shares
+ // note: duplicate privkeys are acceptable, and duplicates due to duplicate signers
+ // will be blocked by duplicate-signer errors after this function is called
+ std::vector<crypto::secret_key> participant_base_common_privkeys;
+ participant_base_common_privkeys.reserve(expanded_msgs.size() + 1);
- // make common privkey
- make_multisig_common_privkey(std::move(participant_base_common_privkeys), m_common_privkey);
+ // add local ancillary base privkey
+ participant_base_common_privkeys.emplace_back(m_base_common_privkey);
- // set common pubkey
- CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(m_common_privkey, m_common_pubkey),
- "Failed to derive public key");
+ // add other signers' base common privkeys
+ for (const multisig_kex_msg &expanded_msg : expanded_msgs)
+ {
+ if (expanded_msg.get_signing_pubkey() != m_base_pubkey)
+ participant_base_common_privkeys.emplace_back(expanded_msg.get_msg_privkey());
+ }
- // if N-of-N, then the base privkey will be used directly to make the account's share of the final key
- if (kex_rounds_required == 1)
- {
- m_multisig_privkeys.clear();
- m_multisig_privkeys.emplace_back(m_base_privkey);
- }
+ // make common privkey
+ make_multisig_common_privkey(std::move(participant_base_common_privkeys), m_common_privkey);
- // exclude all keys the local account recommends
- // - in the first round, only the local pubkey is recommended by the local signer
- exclude_pubkeys_out.emplace_back(m_base_pubkey);
- }
- else
- {
- // in other rounds, kex msgs will contain participants' shared keys
+ // set common pubkey
+ CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(m_common_privkey, m_common_pubkey),
+ "Failed to derive public key");
- // ignore shared keys the account helped create for this round
- for (const auto &shared_key_with_origins : m_kex_keys_to_origins_map)
- {
- exclude_pubkeys_out.emplace_back(shared_key_with_origins.first);
- }
+ // if N-of-N, then the base privkey will be used directly to make the account's share of the final key
+ if (kex_rounds_required == 1)
+ {
+ m_multisig_privkeys.clear();
+ m_multisig_privkeys.emplace_back(m_base_privkey);
}
}
//----------------------------------------------------------------------------------------------------------------------
@@ -771,9 +779,7 @@ namespace multisig
result_keys.reserve(result_keys_to_origins_map.size());
for (const auto &result_key_and_origins : result_keys_to_origins_map)
- {
result_keys.emplace_back(result_key_and_origins.first);
- }
// compute final aggregate key, update local multisig privkeys with aggregation coefficients applied
m_multisig_pubkey = generate_multisig_aggregate_key(std::move(result_keys), m_multisig_privkeys);
@@ -811,7 +817,8 @@ namespace multisig
// derived pubkey = multisig_key * G
crypto::public_key_memsafe derived_pubkey;
m_multisig_privkeys.push_back(
- calculate_multisig_keypair_from_derivation(derivation_and_origins.first, derived_pubkey));
+ calculate_multisig_keypair_from_derivation(derivation_and_origins.first, derived_pubkey)
+ );
// save the account's kex key mappings for this round [derived pubkey : other signers who will have the same key]
m_kex_keys_to_origins_map[derived_pubkey] = std::move(derivation_and_origins.second);
@@ -863,8 +870,7 @@ namespace multisig
"Multisig kex has already completed all required rounds (including post-kex verification).");
// initialize account update
- std::vector<crypto::public_key> exclude_pubkeys;
- initialize_kex_update(expanded_msgs, kex_rounds_required, exclude_pubkeys);
+ this->initialize_kex_update(expanded_msgs, kex_rounds_required);
// process messages into a [pubkey : {origins}] map
multisig_keyset_map_memsafe_t result_keys_to_origins_map;
@@ -875,12 +881,75 @@ namespace multisig
m_threshold,
m_signers,
expanded_msgs,
- exclude_pubkeys,
+ this->get_kex_exclude_pubkeys(),
incomplete_signer_set,
result_keys_to_origins_map);
// finish account update
- finalize_kex_update(kex_rounds_required, std::move(result_keys_to_origins_map));
+ this->finalize_kex_update(kex_rounds_required, std::move(result_keys_to_origins_map));
+ }
+ //-----------------------------------------------------------------
+ // multisig_account: EXTERNAL
+ //-----------------------------------------------------------------
+ multisig_kex_msg multisig_account::get_multisig_kex_round_booster(const std::uint32_t threshold,
+ const std::uint32_t num_signers,
+ const std::vector<multisig_kex_msg> &expanded_msgs) const
+ {
+ // the messages passed in should be required for the next kex round of this account (the round it is currently
+ // working on)
+ const std::uint32_t expected_msgs_round{m_kex_rounds_complete + 1};
+ const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(num_signers, threshold)};
+
+ CHECK_AND_ASSERT_THROW_MES(num_signers > 1, "Must be at least one other multisig signer.");
+ CHECK_AND_ASSERT_THROW_MES(num_signers <= config::MULTISIG_MAX_SIGNERS, "Too many multisig signers specified.");
+ CHECK_AND_ASSERT_THROW_MES(expected_msgs_round < kex_rounds_required,
+ "Multisig kex booster: this account has already completed all intermediate kex rounds so it can't make a kex "
+ "booster (there is no round available to boost).");
+ CHECK_AND_ASSERT_THROW_MES(expanded_msgs.size() > 0, "At least one input kex message expected.");
+
+ // sanitize pubkeys from input msgs
+ multisig_keyset_map_memsafe_t pubkey_origins_map;
+ const std::uint32_t msgs_round{
+ multisig_kex_msgs_sanitize_pubkeys(expanded_msgs, this->get_kex_exclude_pubkeys(), pubkey_origins_map)
+ };
+ CHECK_AND_ASSERT_THROW_MES(msgs_round == expected_msgs_round, "Kex messages were not for expected round.");
+
+ // remove the local signer from sanitized messages
+ remove_key_from_mapped_sets(m_base_pubkey, pubkey_origins_map);
+
+ // make DH derivations for booster message
+ multisig_keyset_map_memsafe_t derivation_to_origins_map;
+ multisig_kex_make_round_keys(m_base_privkey, std::move(pubkey_origins_map), derivation_to_origins_map);
+
+ // collect keys for booster message
+ std::vector<crypto::public_key> next_msg_keys;
+ next_msg_keys.reserve(derivation_to_origins_map.size());
+
+ if (msgs_round + 1 == kex_rounds_required)
+ {
+ // final kex round: send DH derivation pubkeys in the message
+ for (const auto &derivation_and_origins : derivation_to_origins_map)
+ {
+ // multisig_privkey = H(derivation)
+ // derived pubkey = multisig_key * G
+ crypto::public_key_memsafe derived_pubkey;
+ calculate_multisig_keypair_from_derivation(derivation_and_origins.first, derived_pubkey);
+
+ // save keys that should be recommended to other signers
+ // - The keys multisig_key*G are sent to other participants in the message, so they can be used to produce the final
+ // multisig key via generate_multisig_spend_public_key().
+ next_msg_keys.push_back(derived_pubkey);
+ }
+ }
+ else //(msgs_round + 1 < kex_rounds_required)
+ {
+ // intermediate kex round: send DH derivations directly in the message
+ for (const auto &derivation_and_origins : derivation_to_origins_map)
+ next_msg_keys.push_back(derivation_and_origins.first);
+ }
+
+ // produce a kex message for the round after the round this account is currently working on
+ return multisig_kex_msg{msgs_round + 1, m_base_privkey, std::move(next_msg_keys)}.get_msg();
}
//----------------------------------------------------------------------------------------------------------------------
} //namespace multisig