|
30 | 30 | #include "tx_builder_multisig.h" |
31 | 31 |
|
32 | 32 | //local headers |
| 33 | +#include "carrot_core/hash_functions.h" |
33 | 34 | #include "carrot_core/output_set_finalization.h" |
| 35 | +#include "carrot_core/transcript_fixed.h" |
34 | 36 | #include "carrot_impl/address_utils.h" |
35 | 37 | #include "carrot_impl/tx_builder_outputs.h" |
36 | 38 | #include "carrot_impl/tx_proposal.h" |
@@ -64,6 +66,8 @@ namespace tools |
64 | 66 | { |
65 | 67 | namespace wallet |
66 | 68 | { |
| 69 | +static constexpr const unsigned char SAL_MULTISIG_DOMAIN_SEP_SHARED_ENTROPY[] = "SAL multisig shared entropy"; |
| 70 | + |
67 | 71 | //------------------------------------------------------------------------------------------------------------------- |
68 | 72 | //------------------------------------------------------------------------------------------------------------------- |
69 | 73 | 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 |
94 | 98 | } |
95 | 99 | //------------------------------------------------------------------------------------------------------------------- |
96 | 100 | //------------------------------------------------------------------------------------------------------------------- |
| 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 | +//------------------------------------------------------------------------------------------------------------------- |
97 | 121 | // NOTE: Only supports legacy multisig, where subaddress extensions are additive and |
98 | 122 | // keys are shared on `G` while `T` is a placeholder. |
99 | 123 | static void prepare_legacy_multisig_input_signing_attempt( |
100 | 124 | const carrot::OutputOpeningHintVariant &opening_hint, |
101 | 125 | const std::set<crypto::public_key> &ignore_set, |
102 | 126 | // Should only include 'active' signers, and `ignore_set` excludes active signers referenced here. |
103 | 127 | const std::vector<wallet2_basic::multisig_info> &multisig_infos, |
| 128 | + const crypto::public_key &local_signer_pubkey, |
104 | 129 | const std::vector<crypto::secret_key> &local_multisig_keys, |
105 | 130 | const size_t threshold, |
106 | 131 | const carrot::address_device &addr_dev, |
@@ -207,6 +232,9 @@ static void prepare_legacy_multisig_input_signing_attempt( |
207 | 232 | size_t n_signers_used = 1; |
208 | 233 | for (const auto &multisig_info : multisig_infos) |
209 | 234 | { |
| 235 | + if (multisig_info.m_signer == local_signer_pubkey) |
| 236 | + continue; |
| 237 | + |
210 | 238 | // Ignored signers |
211 | 239 | if (ignore_set.find(multisig_info.m_signer) != ignore_set.end()) |
212 | 240 | continue; |
@@ -476,6 +504,7 @@ pending_tx tx_proposal_to_multisig_pending_tx( |
476 | 504 | const std::vector<std::set<crypto::public_key>> &ignore_sets, |
477 | 505 | const std::vector<const std::vector<wallet2_basic::multisig_info>*> &multisig_infos, |
478 | 506 | const size_t threshold, |
| 507 | + const crypto::public_key &local_signer_pubkey, |
479 | 508 | const std::vector<crypto::secret_key> &local_multisig_keys, |
480 | 509 | const carrot::address_device &addr_dev, |
481 | 510 | const carrot::view_incoming_key_device &k_view_incoming_dev, |
@@ -556,8 +585,9 @@ pending_tx tx_proposal_to_multisig_pending_tx( |
556 | 585 | std::vector<multisig_sig> multisig_sigs; |
557 | 586 | std::unordered_set<rct::key> all_used_L{}; |
558 | 587 | multisig_sigs.reserve(num_signing_attempts); |
| 588 | + const crypto::secret_key root_entropy = rct::rct2sk(rct::zero()); |
559 | 589 |
|
560 | | - for (size_t s = 0; s < num_signing_attempts; ++s) |
| 590 | + for (uint32_t s = 0; s < num_signing_attempts; ++s) |
561 | 591 | { |
562 | 592 | auto &partial_sigs = saved_partial_sigs_out.emplace_back(); |
563 | 593 |
|
@@ -596,6 +626,7 @@ pending_tx tx_proposal_to_multisig_pending_tx( |
596 | 626 | input_proposal, |
597 | 627 | ignore_sets.at(s), |
598 | 628 | *multisig_infos.at(i), |
| 629 | + local_signer_pubkey, |
599 | 630 | local_multisig_keys, |
600 | 631 | threshold, |
601 | 632 | addr_dev, |
@@ -626,6 +657,7 @@ pending_tx tx_proposal_to_multisig_pending_tx( |
626 | 657 | kU, |
627 | 658 | key_image, |
628 | 659 | 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), |
629 | 661 | proposal |
630 | 662 | ); |
631 | 663 |
|
@@ -684,7 +716,7 @@ pending_tx tx_proposal_to_multisig_pending_tx( |
684 | 716 |
|
685 | 717 | // Add multisig pieces to pending_tx |
686 | 718 | 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; |
688 | 720 | ptx.multisig_enote_rr = multisig_enote_rr; |
689 | 721 |
|
690 | 722 | return ptx; |
@@ -771,9 +803,12 @@ void sign_multisig_partial_tx( |
771 | 803 |
|
772 | 804 | // Update each tx attempt |
773 | 805 | saved_partial_sigs_out.reserve(ptx_inout.multisig_sigs.size()); |
| 806 | + const crypto::secret_key root_entropy = ptx_inout.multisig_tx_key_entropy; |
774 | 807 |
|
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) |
776 | 809 | { |
| 810 | + multisig_sig &sig = ptx_inout.multisig_sigs[sig_idx]; |
| 811 | + |
777 | 812 | // Add an entry to the partial sigs |
778 | 813 | // This can be empty if the local signer is ignored by this attempt. It just needs to align with |
779 | 814 | // `ptx_inout.multisig_sigs`. |
@@ -836,6 +871,7 @@ void sign_multisig_partial_tx( |
836 | 871 | sig.total_kU.at(i), |
837 | 872 | key_images.at(i), |
838 | 873 | 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), |
839 | 875 | proposal |
840 | 876 | ); |
841 | 877 |
|
|
0 commit comments