diff --git a/src/carrot_core/account_secrets.cpp b/src/carrot_core/account_secrets.cpp index 87a2bcd3d33..298a56a3fb7 100644 --- a/src/carrot_core/account_secrets.cpp +++ b/src/carrot_core/account_secrets.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2024, The Monero Project +// Copyright (c) 2024-2026, The Monero Project // // All rights reserved. // @@ -79,7 +79,7 @@ void make_carrot_viewbalance_secret(const crypto::secret_key &s_master, void make_carrot_generateimage_preimage(const crypto::secret_key &s_view_balance, crypto::secret_key &s_generate_image_preimage_out) { - // s_gp = H_n[s_vb]() + // s_gp = H_32[s_vb]() const auto transcript = make_fixed_transcript(); derive_bytes_32(transcript.data(), transcript.size(), &s_view_balance, to_bytes(s_generate_image_preimage_out)); } diff --git a/src/carrot_core/account_secrets.h b/src/carrot_core/account_secrets.h index 642d1b443b5..3375557c55f 100644 --- a/src/carrot_core/account_secrets.h +++ b/src/carrot_core/account_secrets.h @@ -69,7 +69,7 @@ void make_carrot_viewbalance_secret(const crypto::secret_key &s_master, crypto::secret_key &s_view_balance_out); /** * @brief Derive generate-image key preimage, for deriving the generate-image key - * s_gp = H_n[s_vb]() + * s_gp = H_32[s_vb]() * @param s_view_balance s_vb * @param[out] s_generate_image_preimage_out s_gp */ diff --git a/src/carrot_core/enote_utils.cpp b/src/carrot_core/enote_utils.cpp index 278500ce043..7f5d06f7db9 100644 --- a/src/carrot_core/enote_utils.cpp +++ b/src/carrot_core/enote_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2024, The Monero Project +// Copyright (c) 2024-2026, The Monero Project // // All rights reserved. // @@ -56,11 +56,6 @@ namespace { //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -static const ge_p3 H_p3 = crypto::get_H_p3(); -static const ge_p3 T_p3 = crypto::get_T_p3(); -static const mx25519_impl* auto_mx25519_impl = mx25519_select_impl(MX25519_TYPE_AUTO); -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- /** * @brief encrypt and encode 64-bit amount with given encryption XOR mask */ @@ -90,15 +85,36 @@ static xmr_amount dec_amount(const encrypted_amount_t &encrypted_amount, const e } //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- +/** + * @brief R = x G + y A + */ +static void ge_double_scalarmult_base(ge_p1p1 *R, const unsigned char *x, const ge_p3 *A, const unsigned char *y) +{ + ge_p3 tmp1; + ge_cached tmp2; + + ge_scalarmult_base(&tmp1, x); + ge_p3_to_cached(&tmp2, &tmp1); + ge_scalarmult_p3(&tmp1, y, A); + ge_add(R, &tmp1, &tmp2); +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- /** * @brief calculate x G + y T */ static crypto::public_key scalar_mult_gt(const crypto::ec_scalar &x, const crypto::ec_scalar &y) { - ge_p2 tmp1; - ge_double_scalarmult_base_vartime(&tmp1, to_bytes(y), &T_p3, to_bytes(x)); + static ge_p3 T_p3 = crypto::get_T_p3(); //! @TODO: remove after #10963 + + ge_p1p1 tmp1; + ge_p2 tmp2; crypto::public_key P; - ge_tobytes(to_bytes(P), &tmp1); + + ge_double_scalarmult_base(&tmp1, to_bytes(x), &T_p3, to_bytes(y)); + ge_p1p1_to_p2(&tmp2, &tmp1); + ge_tobytes(to_bytes(P), &tmp2); + return P; } //------------------------------------------------------------------------------------------------------------------- @@ -118,11 +134,13 @@ static void make_carrot_sender_extension_pubkey_coinbase(const crypto::hash &s_s { // k^o_g = H_n[s^ctx_sr]("..g..", a, K^0_s) crypto::secret_key sender_extension_g; - make_carrot_sender_extension_g_coinbase(s_sender_receiver_ctx, amount, main_address_spend_pubkey, sender_extension_g); + make_carrot_sender_extension_g_coinbase(s_sender_receiver_ctx, amount, main_address_spend_pubkey, + sender_extension_g); // k^o_t = H_n[s^ctx_sr]("..t..", a, K^0_s) crypto::secret_key sender_extension_t; - make_carrot_sender_extension_t_coinbase(s_sender_receiver_ctx, amount, main_address_spend_pubkey, sender_extension_t); + make_carrot_sender_extension_t_coinbase(s_sender_receiver_ctx, amount, main_address_spend_pubkey, + sender_extension_t); // K^o_ext = k^o_g G + k^o_t T sender_extension_pubkey_out = scalar_mult_gt(sender_extension_g, sender_extension_t); @@ -171,19 +189,22 @@ void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm, void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &enote_ephemeral_privkey, mx25519_pubkey &enote_ephemeral_pubkey_out) { + static const mx25519_impl *auto_mx25519_impl = mx25519_select_impl(MX25519_TYPE_AUTO); //! @TODO: remove after #10965 + // D_e = d_e B mx25519_scmul_base(auto_mx25519_impl, &enote_ephemeral_pubkey_out, reinterpret_cast(&enote_ephemeral_privkey)); } //------------------------------------------------------------------------------------------------------------------- -void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey, +bool try_make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey, const crypto::public_key &address_spend_pubkey, mx25519_pubkey &enote_ephemeral_pubkey_out) { // deserialize K^j_s ge_p3 address_spend_pubkey_p3; - ge_frombytes_vartime(&address_spend_pubkey_p3, to_bytes(address_spend_pubkey)); + if (0 != ge_frombytes_vartime(&address_spend_pubkey_p3, to_bytes(address_spend_pubkey))) + return false; // K_e = d_e K^j_s ge_p3 D_e_in_ed25519; @@ -191,9 +212,11 @@ void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &eno // D_e = ConvertPointE(K_e) ge_p3_to_x25519(enote_ephemeral_pubkey_out.data, &D_e_in_ed25519); + + return true; } //------------------------------------------------------------------------------------------------------------------- -void make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemeral_privkey, +bool try_make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemeral_privkey, const crypto::public_key &address_spend_pubkey, const bool is_subaddress, mx25519_pubkey &enote_ephemeral_pubkey_out) @@ -201,7 +224,7 @@ void make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemera if (is_subaddress) { // D_e = d_e ConvertPointE(K^j_s) - make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey, + return try_make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey, address_spend_pubkey, enote_ephemeral_pubkey_out); } @@ -209,6 +232,7 @@ void make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemera { // D_e = d_e B make_carrot_enote_ephemeral_pubkey_cryptonote(enote_ephemeral_privkey, enote_ephemeral_pubkey_out); + return true; } } //------------------------------------------------------------------------------------------------------------------- @@ -216,6 +240,8 @@ bool try_make_carrot_shared_key_receiver(const crypto::secret_key &k_view, const mx25519_pubkey &enote_ephemeral_pubkey, mx25519_pubkey &s_sender_receiver_out) { + static const mx25519_impl *auto_mx25519_impl = mx25519_select_impl(MX25519_TYPE_AUTO); //! @TODO: remove after #10965 + // s_sr = k_v D_e mx25519_scmul_key(auto_mx25519_impl, &s_sender_receiver_out, @@ -229,6 +255,8 @@ bool try_make_carrot_shared_key_sender(const crypto::secret_key &enote_ephemeral const crypto::public_key &address_view_pubkey, mx25519_pubkey &s_sender_receiver_out) { + static const mx25519_impl *auto_mx25519_impl = mx25519_select_impl(MX25519_TYPE_AUTO); //! @TODO: remove after #10965 + // if K^j_v not in prime order subgroup, then FAIL if (!verify_point_is_in_main_subgroup(address_view_pubkey)) return false; @@ -394,12 +422,17 @@ void make_carrot_amount_blinding_factor(const crypto::hash &s_sender_receiver_ct //------------------------------------------------------------------------------------------------------------------- amount_commitment_t commit_carrot_amount(const xmr_amount amount, const crypto::secret_key &amount_blinding_factor) { + static ge_p3 H_p3 = crypto::get_H_p3(); //! @TODO: remove after #10963 + unsigned char amount32[32] = {0}; memcpy_swap64le(amount32, &amount, 1); - ge_p2 tmp1; - ge_double_scalarmult_base_vartime(&tmp1, amount32, &H_p3, to_bytes(amount_blinding_factor)); + ge_p1p1 tmp1; + ge_double_scalarmult_base(&tmp1, to_bytes(amount_blinding_factor), &H_p3, amount32); + ge_p2 tmp2; + ge_p1p1_to_p2(&tmp2, &tmp1); + amount_commitment_t commitment; - ge_tobytes(to_bytes(commitment), &tmp1); + ge_tobytes(to_bytes(commitment), &tmp2); return commitment; } //------------------------------------------------------------------------------------------------------------------- @@ -524,22 +557,26 @@ bool try_recover_address_spend_pubkey(const crypto::public_key &onetime_address, const crypto::secret_key &sender_extension_t, crypto::public_key &address_spend_key_out) { + static ge_p3 T_p3 = crypto::get_T_p3(); //! @TODO: remove after #10963 + + ge_p1p1 tmp1; + ge_p3 tmp2; + ge_cached tmp3; + // K^o_ext = k^o_g G + k^o_t T - ge_p3 tmp1; - ge_double_scalarmult_base_vartime_p3(&tmp1, - to_bytes(sender_extension_t), &T_p3, to_bytes(sender_extension_g)); - ge_cached tmp2; - ge_p3_to_cached(&tmp2, &tmp1); + ge_double_scalarmult_base(&tmp1, to_bytes(sender_extension_g), + &T_p3, to_bytes(sender_extension_t)); + ge_p1p1_to_p3(&tmp2, &tmp1); + ge_p3_to_cached(&tmp3, &tmp2); // Ko - if (0 != ge_frombytes_vartime(&tmp1, to_bytes(onetime_address))) + if (0 != ge_frombytes_vartime(&tmp2, to_bytes(onetime_address))) return false; // K^j_s = Ko - K^o_ext - ge_p1p1 tmp3; - ge_sub(&tmp3, &tmp1, &tmp2); ge_p2 tmp4; - ge_p1p1_to_p2(&tmp4, &tmp3); + ge_sub(&tmp1, &tmp2, &tmp3); + ge_p1p1_to_p2(&tmp4, &tmp1); // compress ge_tobytes(to_bytes(address_spend_key_out), &tmp4); @@ -639,10 +676,11 @@ bool verify_carrot_normal_janus_protection(const janus_anchor_t &nominal_anchor, // recompute D_e' for d_e' and address type mx25519_pubkey nominal_enote_ephemeral_pubkey; - make_carrot_enote_ephemeral_pubkey(nominal_enote_ephemeral_privkey, - nominal_address_spend_pubkey, - is_subaddress, - nominal_enote_ephemeral_pubkey); + if (!try_make_carrot_enote_ephemeral_pubkey(nominal_enote_ephemeral_privkey, + nominal_address_spend_pubkey, + is_subaddress, + nominal_enote_ephemeral_pubkey)) + return false; // D_e' ?= D_e return 0 == memcmp(&nominal_enote_ephemeral_pubkey, &enote_ephemeral_pubkey, sizeof(mx25519_pubkey)); diff --git a/src/carrot_core/enote_utils.h b/src/carrot_core/enote_utils.h index e480b4f8b5a..4d10887c867 100644 --- a/src/carrot_core/enote_utils.h +++ b/src/carrot_core/enote_utils.h @@ -72,8 +72,10 @@ void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &eno * @param enote_ephemeral_privkey d_e * @param address_spend_pubkey K^j_s * @param[out] enote_ephemeral_pubkey_out D_e + * @return true on success, false on failure (K^j_s was invalid) */ -void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey, +bool try_make_carrot_enote_ephemeral_pubkey_subaddress( + const crypto::secret_key &enote_ephemeral_privkey, const crypto::public_key &address_spend_pubkey, mx25519_pubkey &enote_ephemeral_pubkey_out); /** @@ -84,8 +86,9 @@ void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &eno * @param address_spend_pubkey K^j_s * @param is_subaddress - * @param[out] enote_ephemeral_pubkey_out D_e + * @return true on success, false on failure (K^j_s was needed, but invalid) */ -void make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemeral_privkey, +bool try_make_carrot_enote_ephemeral_pubkey(const crypto::secret_key &enote_ephemeral_privkey, const crypto::public_key &address_spend_pubkey, const bool is_subaddress, mx25519_pubkey &enote_ephemeral_pubkey_out); diff --git a/src/carrot_core/hash_functions.cpp b/src/carrot_core/hash_functions.cpp index 275ca3ca2d9..dcd0d91bcaf 100644 --- a/src/carrot_core/hash_functions.cpp +++ b/src/carrot_core/hash_functions.cpp @@ -146,6 +146,7 @@ void derive_scalar(const void *data, const std::size_t data_length, const void * hash_base(key, data, data_length, temp, 64); sc_reduce(temp); //mod l memcpy(hash_out, temp, 32); + memwipe(temp, sizeof(temp)); } //------------------------------------------------------------------------------------------------------------------- } //namespace carrot diff --git a/src/carrot_core/payment_proposal.cpp b/src/carrot_core/payment_proposal.cpp index 336f3f1966b..7ca27354230 100644 --- a/src/carrot_core/payment_proposal.cpp +++ b/src/carrot_core/payment_proposal.cpp @@ -32,7 +32,6 @@ //local headers #include "enote_utils.h" #include "exceptions.h" -#include "misc_language.h" #include "misc_log_ex.h" //third party headers @@ -50,15 +49,6 @@ namespace carrot static const janus_anchor_t null_anchor{{0}}; //------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------- -template -static auto auto_wiper(T &obj) -{ - // @TODO: replace with scope guard - static_assert(std::is_trivially_copyable()); - return epee::misc_utils::create_scope_leave_handler([&]{ memwipe(&obj, sizeof(T)); }); -} -//------------------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------------- static void get_normal_proposal_ecdh_parts(const CarrotPaymentProposalV1 &proposal, const input_context_t &input_context, mx25519_pubkey &enote_ephemeral_pubkey_out, @@ -245,10 +235,11 @@ mx25519_pubkey get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposa // D_e = d_e * ... mx25519_pubkey enote_ephemeral_pubkey; - make_carrot_enote_ephemeral_pubkey(enote_ephemeral_privkey, + const bool r = try_make_carrot_enote_ephemeral_pubkey(enote_ephemeral_privkey, proposal.destination.address_spend_pubkey, proposal.destination.is_subaddress, enote_ephemeral_pubkey); + CARROT_CHECK_AND_THROW(r, invalid_point, "could not get D_e: address spend pubkey is invalid"); return enote_ephemeral_pubkey; } @@ -309,10 +300,11 @@ mx25519_pubkey get_enote_ephemeral_pubkey( // D_e = d_e * ... mx25519_pubkey enote_ephemeral_pubkey; - make_carrot_enote_ephemeral_pubkey(enote_ephemeral_privkey, + const bool r = try_make_carrot_enote_ephemeral_pubkey(enote_ephemeral_privkey, base_address_spend_pubkey, base_is_subaddress, enote_ephemeral_pubkey); + CARROT_CHECK_AND_THROW(r, invalid_point, "could not get self-send D_e: address spend pubkey is invalid"); return enote_ephemeral_pubkey; } @@ -333,14 +325,14 @@ void get_coinbase_enote_v1(const CarrotPaymentProposalV1 &proposal, const input_context_t input_context = make_carrot_input_context_coinbase(block_index); // 3. make D_e and do external ECDH - mx25519_pubkey s_sender_receiver; auto dhe_wiper = auto_wiper(s_sender_receiver); + tools::scrubbed s_sender_receiver; get_normal_proposal_ecdh_parts(proposal, input_context, output_enote_out.enote_ephemeral_pubkey, s_sender_receiver); // 4. build the output enote address pieces - crypto::hash s_sender_receiver_ctx; auto q_wiper = auto_wiper(s_sender_receiver_ctx); + tools::scrubbed s_sender_receiver_ctx; crypto::secret_key dummy_amount_blinding_factor; amount_commitment_t dummy_amount_commitment; encrypted_amount_t dummy_encrypted_amount; @@ -384,14 +376,14 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal, const input_context_t input_context = make_carrot_input_context(tx_first_key_image); // 3. make D_e and do external ECDH - mx25519_pubkey s_sender_receiver; auto dhe_wiper = auto_wiper(s_sender_receiver); + tools::scrubbed s_sender_receiver; get_normal_proposal_ecdh_parts(proposal, input_context, output_enote_out.enote.enote_ephemeral_pubkey, s_sender_receiver); // 4. build the output enote address pieces - crypto::hash s_sender_receiver_ctx; auto q_wiper = auto_wiper(s_sender_receiver_ctx); + tools::scrubbed s_sender_receiver_ctx; get_external_output_proposal_parts(s_sender_receiver, proposal.destination.address_spend_pubkey, proposal.destination.payment_id, @@ -453,12 +445,12 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo const input_context_t input_context = make_carrot_input_context(tx_first_key_image); // 3. s_sr = k_v D_e - mx25519_pubkey s_sender_receiver; auto ecdh_wiper = auto_wiper(s_sender_receiver); + tools::scrubbed s_sender_receiver; CARROT_CHECK_AND_THROW(k_view_dev.view_key_scalar_mult_x25519(explicit_enote_ephemeral_pubkey, s_sender_receiver), crypto_function_failed, "HW device failed to perform ECDH with ephemeral pubkey"); // 4. build the output enote address pieces - crypto::hash s_sender_receiver_ctx; auto q_wiper = auto_wiper(s_sender_receiver_ctx); + tools::scrubbed s_sender_receiver_ctx; encrypted_payment_id_t dummy_encrypted_payment_id; get_external_output_proposal_parts(s_sender_receiver, proposal.destination_address_spend_pubkey, @@ -514,7 +506,7 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop tx_first_key_image); // 4. s^ctx_sr = H_32[s_vb](D_e, input_context) - crypto::hash s_sender_receiver_ctx; auto q_wiper = auto_wiper(s_sender_receiver_ctx); + tools::scrubbed s_sender_receiver_ctx; s_view_balance_dev.make_internal_sender_receiver_secret(enote_ephemeral_pubkey, input_context, s_sender_receiver_ctx); diff --git a/src/carrot_core/scan.cpp b/src/carrot_core/scan.cpp index dfc3dfa72df..1757f8e364d 100644 --- a/src/carrot_core/scan.cpp +++ b/src/carrot_core/scan.cpp @@ -34,7 +34,6 @@ //local headers #include "destination.h" #include "enote_utils.h" -#include "ringct/rctOps.h" #include "scan_unsafe.h" //third party headers diff --git a/tests/unit_tests/carrot_convergence.cpp b/tests/unit_tests/carrot_convergence.cpp index 295da70fb4f..cd25368b3e0 100644 --- a/tests/unit_tests/carrot_convergence.cpp +++ b/tests/unit_tests/carrot_convergence.cpp @@ -263,9 +263,9 @@ TEST(carrot_convergence, make_carrot_enote_ephemeral_pubkey_cryptonote) TEST(carrot_convergence, make_carrot_enote_ephemeral_pubkey_subaddress) { mx25519_pubkey enote_ephemeral_pubkey_rc; - make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey.value, + ASSERT_TRUE(try_make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey.value, subaddress_spend_pubkey.value, - enote_ephemeral_pubkey_rc); + enote_ephemeral_pubkey_rc)); EXPECT_TRUE(enote_ephemeral_pubkey_subaddress.matches(enote_ephemeral_pubkey_rc)); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/tests/unit_tests/carrot_core.cpp b/tests/unit_tests/carrot_core.cpp index 27909fddf8d..e39452bac6b 100644 --- a/tests/unit_tests/carrot_core.cpp +++ b/tests/unit_tests/carrot_core.cpp @@ -78,7 +78,7 @@ TEST(carrot_core, ECDH_subaddress_completeness) ASSERT_NE(k_view, k_ephem); mx25519_pubkey enote_ephemeral_pubkey; - make_carrot_enote_ephemeral_pubkey_subaddress(k_ephem, spend_pubkey, enote_ephemeral_pubkey); + ASSERT_TRUE(try_make_carrot_enote_ephemeral_pubkey_subaddress(k_ephem, spend_pubkey, enote_ephemeral_pubkey)); mx25519_pubkey s_sr_sender; ASSERT_TRUE(try_make_carrot_shared_key_sender(k_ephem, view_pubkey, s_sr_sender)); @@ -946,10 +946,11 @@ static void get_output_proposal_janus_attack_v1(const JanusAttackProposalV1 &pro enote_ephemeral_privkey); // 5. make D_e - make_carrot_enote_ephemeral_pubkey(enote_ephemeral_privkey, - destination.address_spend_pubkey, - destination.is_subaddress, - output_enote_out.enote.enote_ephemeral_pubkey); + CHECK_AND_ASSERT_THROW_MES(try_make_carrot_enote_ephemeral_pubkey(enote_ephemeral_privkey, + destination.address_spend_pubkey, + destination.is_subaddress, + output_enote_out.enote.enote_ephemeral_pubkey), + "Bad given address points"); // 6. s_sr = d_e ConvertPointE(K^j_v) mx25519_pubkey s_sender_receiver; @@ -1582,10 +1583,11 @@ static void get_coinbase_output_proposal_janus_attack_v1(const JanusAttackPropos enote_ephemeral_privkey); // 5. make D_e - make_carrot_enote_ephemeral_pubkey(enote_ephemeral_privkey, - destination.address_spend_pubkey, - destination.is_subaddress, - output_enote_out.enote_ephemeral_pubkey); + CHECK_AND_ASSERT_THROW_MES(try_make_carrot_enote_ephemeral_pubkey(enote_ephemeral_privkey, + destination.address_spend_pubkey, + destination.is_subaddress, + output_enote_out.enote_ephemeral_pubkey), + "Bad given address points"); // 6. s_sr = d_e ConvertPointE(K^j_v) mx25519_pubkey s_sender_receiver;