Skip to content

Commit 89067f9

Browse files
committed
multisig: use multisig entropy for sal shared nonces
1 parent 3882805 commit 89067f9

7 files changed

Lines changed: 48 additions & 8 deletions

File tree

src/multisig/multisig_sal.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ void make_sal_multisig_proposal(
9696
const rct::key &kU,
9797
const crypto::key_image &KI,
9898
const fcmp_pp::RerandomizedEnote &rr_enote,
99+
const crypto::secret_key &entropy,
99100
SalProofMultisigProposal &proposal_out
100101
){
101102
/// assemble proposal
@@ -104,7 +105,7 @@ void make_sal_multisig_proposal(
104105
proposal_out.kU = kU;
105106
proposal_out.KI = KI;
106107
proposal_out.rr_enote = rr_enote;
107-
proposal_out.entropy = rct::rct2sk(rct::skGen());
108+
proposal_out.entropy = entropy;
108109
}
109110
//-------------------------------------------------------------------------------------------------------------------
110111
// reference: https://github.com/monero-oxide/monero-oxide/blob/fcmp%2B%2B/monero-oxide/ringct/fcmp%2B%2B/src/sal/legacy_multisig.rs

src/multisig/multisig_sal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ struct SalProofMultisigPartial final
9090
* param: kU - proof key k U
9191
* param: KI - key image
9292
* param: rr_enote - enote with blinding factors
93+
* param: entropy - entropy used to generate shared nonces
9394
* outparam: proposal_out - proposal
9495
*/
9596
void make_sal_multisig_proposal(
@@ -98,6 +99,7 @@ void make_sal_multisig_proposal(
9899
const rct::key &kU,
99100
const crypto::key_image &KI,
100101
const fcmp_pp::RerandomizedEnote &rr_enote,
102+
const crypto::secret_key &entropy,
101103
SalProofMultisigProposal &proposal_out
102104
);
103105
/**

src/wallet/tx_builder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ struct pending_tx
162162
// TODO: move multisig pieces into separate struct?
163163
std::vector<multisig_sig> multisig_sigs;
164164
// ringct (clsag): used to generate enote privkeys
165-
// fcmp (sal): unused since sender-receiver secrets are bound to input context, preventing enote burning
165+
// fcmp (sal): used to generate shared SAL nonces
166166
crypto::secret_key multisig_tx_key_entropy;
167167
// fcmp_pp::FcmpRerandomizedOutputCompressed (just the r_* values) for each input 'i'.
168168
// Equivalent to [r_o | r_i | r_r_i | r_c]

src/wallet/tx_builder_multisig.cpp

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
#include "tx_builder_multisig.h"
3131

3232
//local headers
33+
#include "carrot_core/hash_functions.h"
3334
#include "carrot_core/output_set_finalization.h"
35+
#include "carrot_core/transcript_fixed.h"
3436
#include "carrot_impl/address_utils.h"
3537
#include "carrot_impl/tx_builder_outputs.h"
3638
#include "carrot_impl/tx_proposal.h"
@@ -64,6 +66,8 @@ namespace tools
6466
{
6567
namespace wallet
6668
{
69+
static constexpr const unsigned char SAL_MULTISIG_DOMAIN_SEP_SHARED_ENTROPY[] = "SAL multisig shared entropy";
70+
6771
//-------------------------------------------------------------------------------------------------------------------
6872
//-------------------------------------------------------------------------------------------------------------------
6973
static void get_sorted_key_images(const std::vector<crypto::key_image> &key_images,
@@ -94,13 +98,34 @@ static void get_sorted_key_images(const std::vector<crypto::key_image> &key_imag
9498
}
9599
//-------------------------------------------------------------------------------------------------------------------
96100
//-------------------------------------------------------------------------------------------------------------------
101+
static crypto::secret_key get_sal_entropy(const crypto::secret_key &root_entropy,
102+
const crypto::key_image &first_ki,
103+
const crypto::public_key &K,
104+
const std::uint32_t signing_attempt)
105+
{
106+
const auto transcript = carrot::make_fixed_transcript<SAL_MULTISIG_DOMAIN_SEP_SHARED_ENTROPY>(
107+
rct::sk2rct(root_entropy),
108+
first_ki,
109+
K,
110+
signing_attempt
111+
);
112+
rct::key hash;
113+
carrot::derive_scalar(transcript.data(), transcript.size(), nullptr, hash.bytes);
114+
CHECK_AND_ASSERT_THROW_MES(sc_isnonzero(hash.bytes),
115+
"multisig sal proof entropy: entropy must be nonzero!");
116+
117+
return rct::rct2sk(hash);
118+
}
119+
//-------------------------------------------------------------------------------------------------------------------
120+
//-------------------------------------------------------------------------------------------------------------------
97121
// NOTE: Only supports legacy multisig, where subaddress extensions are additive and
98122
// keys are shared on `G` while `T` is a placeholder.
99123
static void prepare_legacy_multisig_input_signing_attempt(
100124
const carrot::OutputOpeningHintVariant &opening_hint,
101125
const std::set<crypto::public_key> &ignore_set,
102126
// Should only include 'active' signers, and `ignore_set` excludes active signers referenced here.
103127
const std::vector<wallet2_basic::multisig_info> &multisig_infos,
128+
const crypto::public_key &local_signer_pubkey,
104129
const std::vector<crypto::secret_key> &local_multisig_keys,
105130
const size_t threshold,
106131
const carrot::address_device &addr_dev,
@@ -207,6 +232,9 @@ static void prepare_legacy_multisig_input_signing_attempt(
207232
size_t n_signers_used = 1;
208233
for (const auto &multisig_info : multisig_infos)
209234
{
235+
if (multisig_info.m_signer == local_signer_pubkey)
236+
continue;
237+
210238
// Ignored signers
211239
if (ignore_set.find(multisig_info.m_signer) != ignore_set.end())
212240
continue;
@@ -476,6 +504,7 @@ pending_tx tx_proposal_to_multisig_pending_tx(
476504
const std::vector<std::set<crypto::public_key>> &ignore_sets,
477505
const std::vector<const std::vector<wallet2_basic::multisig_info>*> &multisig_infos,
478506
const size_t threshold,
507+
const crypto::public_key &local_signer_pubkey,
479508
const std::vector<crypto::secret_key> &local_multisig_keys,
480509
const carrot::address_device &addr_dev,
481510
const carrot::view_incoming_key_device &k_view_incoming_dev,
@@ -556,8 +585,9 @@ pending_tx tx_proposal_to_multisig_pending_tx(
556585
std::vector<multisig_sig> multisig_sigs;
557586
std::unordered_set<rct::key> all_used_L{};
558587
multisig_sigs.reserve(num_signing_attempts);
588+
const crypto::secret_key root_entropy = rct::rct2sk(rct::zero());
559589

560-
for (size_t s = 0; s < num_signing_attempts; ++s)
590+
for (uint32_t s = 0; s < num_signing_attempts; ++s)
561591
{
562592
auto &partial_sigs = saved_partial_sigs_out.emplace_back();
563593

@@ -596,6 +626,7 @@ pending_tx tx_proposal_to_multisig_pending_tx(
596626
input_proposal,
597627
ignore_sets.at(s),
598628
*multisig_infos.at(i),
629+
local_signer_pubkey,
599630
local_multisig_keys,
600631
threshold,
601632
addr_dev,
@@ -626,6 +657,7 @@ pending_tx tx_proposal_to_multisig_pending_tx(
626657
kU,
627658
key_image,
628659
fcmp_pp::rerandomized_enote_from_raw(rerandomized_outputs.at(i)),
660+
get_sal_entropy(root_entropy, expected_key_images_sorted.at(0), onetime_address_ref(input_proposal), s),
629661
proposal
630662
);
631663

@@ -684,7 +716,7 @@ pending_tx tx_proposal_to_multisig_pending_tx(
684716

685717
// Add multisig pieces to pending_tx
686718
ptx.multisig_sigs = multisig_sigs;
687-
ptx.multisig_tx_key_entropy = rct::rct2sk(rct::zero()); // not needed for Carrot txs
719+
ptx.multisig_tx_key_entropy = root_entropy;
688720
ptx.multisig_enote_rr = multisig_enote_rr;
689721

690722
return ptx;
@@ -771,9 +803,12 @@ void sign_multisig_partial_tx(
771803

772804
// Update each tx attempt
773805
saved_partial_sigs_out.reserve(ptx_inout.multisig_sigs.size());
806+
const crypto::secret_key root_entropy = ptx_inout.multisig_tx_key_entropy;
774807

775-
for (multisig_sig &sig : ptx_inout.multisig_sigs)
808+
for (uint32_t sig_idx = 0; sig_idx < ptx_inout.multisig_sigs.size(); ++sig_idx)
776809
{
810+
multisig_sig &sig = ptx_inout.multisig_sigs[sig_idx];
811+
777812
// Add an entry to the partial sigs
778813
// This can be empty if the local signer is ignored by this attempt. It just needs to align with
779814
// `ptx_inout.multisig_sigs`.
@@ -836,6 +871,7 @@ void sign_multisig_partial_tx(
836871
sig.total_kU.at(i),
837872
key_images.at(i),
838873
fcmp_pp::rerandomized_enote_from_raw(rerandomized_outputs.at(i)),
874+
get_sal_entropy(root_entropy, key_images_sorted.at(0), onetime_address_ref(input_proposal), sig_idx),
839875
proposal
840876
);
841877

src/wallet/tx_builder_multisig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pending_tx tx_proposal_to_multisig_pending_tx(
6565
const std::vector<std::set<crypto::public_key>> &ignore_sets,
6666
const std::vector<const std::vector<wallet2_basic::multisig_info>*> &multisig_infos,
6767
const size_t threshold,
68+
const crypto::public_key &local_signer_pubkey,
6869
const std::vector<crypto::secret_key> &local_multisig_keys,
6970
const carrot::address_device &addr_dev,
7071
const carrot::view_incoming_key_device &k_view_incoming_dev,

src/wallet/wallet2.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ tools::wallet::pending_tx transfer_details_and_tx_proposal_to_multisig_pending_t
11031103
ignore_sets,
11041104
all_multisig_info,
11051105
threshold,
1106+
w.get_multisig_signer_public_key(),
11061107
w.get_account().get_multisig_keys(),
11071108
*w.get_address_device(),
11081109
*w.get_view_incoming_key_device(),

tests/unit_tests/multisig.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ TEST(multisig, sal_1_of_1)
585585

586586
// Second make a multisig proof with the C++ API.
587587
multisig::SalProofMultisigProposal proposal;
588-
multisig::make_sal_multisig_proposal(message, K, kU, KI, rr_enote, proposal);
588+
multisig::make_sal_multisig_proposal(message, K, kU, KI, rr_enote, rct::rct2sk(rct::skGen()), proposal);
589589

590590
multisig::SalProofMultisigPartial partial_sig;
591591
multisig::make_sal_multisig_partial_sig(
@@ -687,7 +687,7 @@ TEST(multisig, sal_2_of_3)
687687

688688
// Proposal
689689
multisig::SalProofMultisigProposal proposal;
690-
multisig::make_sal_multisig_proposal(message, K, kU, KI, rr_enote, proposal);
690+
multisig::make_sal_multisig_proposal(message, K, kU, KI, rr_enote, rct::rct2sk(rct::skGen()), proposal);
691691

692692
// Partial sigs from signers
693693
std::vector<multisig::SalProofMultisigPartial> partial_sigs;
@@ -697,7 +697,6 @@ TEST(multisig, sal_2_of_3)
697697
num_signers,
698698
proposal,
699699
keys_for_signing[i],
700-
// i == 0 ? rct::rct2sk(t_ext) : rct::rct2sk(rct::Z),
701700
rct::rct2sk(t_ext),
702701
total_alpha_G,
703702
total_alpha_H,

0 commit comments

Comments
 (0)