Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/carrot_core/enote_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,13 @@ static void make_carrot_sender_extension_pubkey(const crypto::hash &s_sender_rec
void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
const input_context_t &input_context,
const crypto::public_key &address_spend_pubkey,
const crypto::public_key &address_view_pubkey,
const payment_id_t payment_id,
crypto::secret_key &enote_ephemeral_privkey_out)
{
// d_e = (H_64(anchor_norm, input_context, K^j_s, pid)) mod l
// d_e = (H_64(anchor_norm, input_context, K^j_s, K^j_v, pid)) mod l
const auto transcript = make_fixed_transcript<CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY>(
anchor_norm, input_context, address_spend_pubkey, payment_id);
anchor_norm, input_context, address_spend_pubkey, address_view_pubkey, payment_id);
derive_scalar(transcript.data(), transcript.size(), nullptr, &enote_ephemeral_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -625,15 +626,17 @@ bool try_get_carrot_amount(const crypto::hash &s_sender_receiver_ctx,
bool verify_carrot_normal_janus_protection(const janus_anchor_t &nominal_anchor,
const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey,
const crypto::public_key &nominal_address_view_pubkey,
const bool is_subaddress,
const payment_id_t nominal_payment_id,
const mx25519_pubkey &enote_ephemeral_pubkey)
{
// d_e' = H_n(anchor_norm, input_context, K^j_s, pid)
// d_e' = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid)
crypto::secret_key nominal_enote_ephemeral_privkey;
make_carrot_enote_ephemeral_privkey(nominal_anchor,
input_context,
nominal_address_spend_pubkey,
nominal_address_view_pubkey,
nominal_payment_id,
nominal_enote_ephemeral_privkey);

Expand Down
6 changes: 5 additions & 1 deletion src/carrot_core/enote_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,18 @@ namespace carrot

/**
* @brief Derive enote ephemeral privkey d_e for Carrot enotes
* d_e = H_n(anchor_norm, input_context, K^j_s, pid)
* d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid)
* @param anchor_norm normal Janus anchor
* @param input_context input_context
* @param address_spend_pubkey K^j_s
* @param address_view_pubkey K^j_v
* @param payment_id pid
* @param[out] enote_ephemeral_privkey_out k_e
*/
void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
const input_context_t &input_context,
const crypto::public_key &address_spend_pubkey,
const crypto::public_key &address_view_pubkey,
const payment_id_t payment_id,
crypto::secret_key &enote_ephemeral_privkey_out);
/**
Expand Down Expand Up @@ -419,6 +421,7 @@ bool try_get_carrot_amount(const crypto::hash &s_sender_receiver_ctx,
* @param nominal_anchor anchor'
* @param input_context -
* @param nominal_address_spend_pubkey K^j_s'
* @param nominal_address_view_pubkey K^j_v'
* @param is_subaddress -
* @param nominal_payment_id pid'
* @param enote_ephemeral_pubkey D_e
Expand All @@ -427,6 +430,7 @@ bool try_get_carrot_amount(const crypto::hash &s_sender_receiver_ctx,
bool verify_carrot_normal_janus_protection(const janus_anchor_t &nominal_anchor,
const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey,
const crypto::public_key &nominal_address_view_pubkey,
const bool is_subaddress,
const payment_id_t nominal_payment_id,
const mx25519_pubkey &enote_ephemeral_pubkey);
Expand Down
7 changes: 4 additions & 3 deletions src/carrot_core/payment_proposal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ static void get_normal_proposal_ecdh_parts(const CarrotPaymentProposalV1 &propos
mx25519_pubkey &enote_ephemeral_pubkey_out,
mx25519_pubkey &s_sender_receiver_out)
{
// 1. d_e = H_n(anchor_norm, input_context, K^j_s, pid)
// 1. d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid)
const crypto::secret_key enote_ephemeral_privkey = get_enote_ephemeral_privkey(proposal, input_context);

// 2. make D_e
Expand Down Expand Up @@ -193,11 +193,12 @@ bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentPro
crypto::secret_key get_enote_ephemeral_privkey(const CarrotPaymentProposalV1 &proposal,
const input_context_t &input_context)
{
// d_e = H_n(anchor_norm, input_context, K^j_s, pid)
// d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid)
crypto::secret_key enote_ephemeral_privkey;
make_carrot_enote_ephemeral_privkey(proposal.randomness,
input_context,
proposal.destination.address_spend_pubkey,
proposal.destination.address_view_pubkey,
proposal.destination.payment_id,
enote_ephemeral_privkey);

Expand Down Expand Up @@ -240,7 +241,7 @@ crypto::secret_key get_enote_ephemeral_privkey(const CarrotPaymentProposalSelfSe
mx25519_pubkey get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
const input_context_t &input_context)
{
// d_e = H_n(anchor_norm, input_context, K^j_s, pid)
// d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid)
const crypto::secret_key enote_ephemeral_privkey{get_enote_ephemeral_privkey(proposal, input_context)};

// D_e = d_e * ...
Expand Down
123 changes: 65 additions & 58 deletions src/carrot_core/scan.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2025, The Monero Project
// Copyright (c) 2025-2026, The Monero Project
//
// All rights reserved.
//
Expand Down Expand Up @@ -32,15 +32,18 @@
#include "scan.h"

//local headers
#include "crypto/generators.h"
#include "destination.h"
#include "enote_utils.h"
#include "ringct/rctOps.h"
#include "scan_unsafe.h"

//third party headers

//standard headers

#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "carrot.scan"


namespace carrot
{
Expand All @@ -56,15 +59,31 @@ static bool is_main_address_spend_pubkey(const crypto::public_key &address_spend
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static bool recover_address_view_pubkey(const crypto::public_key &address_spend_pubkey,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
const view_incoming_key_device &k_view_dev,
crypto::public_key &address_view_pubkey_out,
bool &is_subaddress_out)
{
// K^j_v = k_v K_base, where:
// [subaddress] K_base = K^j_s
// [main address] K_base = G
is_subaddress_out = !is_main_address_spend_pubkey(address_spend_pubkey, main_address_spend_pubkeys);
address_view_pubkey_out = is_subaddress_out ? address_spend_pubkey : crypto::get_G();
return k_view_dev.view_key_scalar_mult_ed25519(address_view_pubkey_out, address_view_pubkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static crypto::secret_key get_enote_ephemeral_privkey_sender(const janus_anchor_t &anchor_norm,
const CarrotDestinationV1 &destination,
const input_context_t &input_context)
{
// d_e = H_n(anchor_norm, input_context, K^j_s, pid)
// d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid)
crypto::secret_key enote_ephemeral_privkey;
make_carrot_enote_ephemeral_privkey(anchor_norm,
input_context,
destination.address_spend_pubkey,
destination.address_view_pubkey,
destination.payment_id,
enote_ephemeral_privkey);
return enote_ephemeral_privkey;
Expand All @@ -74,6 +93,7 @@ static bool try_scan_carrot_coinbase_enote_checked(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
const crypto::public_key &main_address_view_pubkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out)
Expand All @@ -92,49 +112,12 @@ static bool try_scan_carrot_coinbase_enote_checked(
return verify_carrot_normal_janus_protection(nominal_janus_anchor,
make_carrot_input_context_coinbase(enote.block_index),
address_spend_pubkey_out,
main_address_view_pubkey,
/*is_subaddress=*/false,
null_payment_id,
enote.enote_ephemeral_pubkey);
}
//-------------------------------------------------------------------------------------------------------------------
static bool try_scan_carrot_enote_external_normal_checked(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> &encrypted_payment_id,
const mx25519_pubkey &s_sender_receiver,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
payment_id_t &payment_id_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &nominal_janus_anchor_out,
bool &verified_normal_janus)
{
if (!try_scan_carrot_enote_external_no_janus(enote,
encrypted_payment_id,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
amount_out,
amount_blinding_factor_out,
payment_id_out,
enote_type_out,
nominal_janus_anchor_out))
return false;

verified_normal_janus = verify_carrot_normal_janus_protection(
make_carrot_input_context(enote.tx_first_key_image),
address_spend_pubkey_out,
!is_main_address_spend_pubkey(address_spend_pubkey_out, main_address_spend_pubkeys),
enote.enote_ephemeral_pubkey,
nominal_janus_anchor_out,
payment_id_out);

return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_make_carrot_shared_key_receiver(
const view_incoming_key_device &k_view_dev,
const mx25519_pubkey &enote_ephemeral_pubkey,
Expand Down Expand Up @@ -179,6 +162,7 @@ bool try_scan_carrot_coinbase_enote_sender(
if (!try_scan_carrot_coinbase_enote_checked(enote,
s_sender_receiver,
{&destination.address_spend_pubkey, 1},
destination.address_view_pubkey,
sender_extension_g_out,
sender_extension_t_out,
dummy_main_address_spend_pubkey))
Expand All @@ -192,13 +176,15 @@ bool try_scan_carrot_coinbase_enote_receiver(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
const crypto::public_key &main_address_view_pubkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &main_address_spend_pubkey_out)
{
return try_scan_carrot_coinbase_enote_checked(enote,
s_sender_receiver,
main_address_spend_pubkeys,
main_address_view_pubkey,
sender_extension_g_out,
sender_extension_t_out,
main_address_spend_pubkey_out);
Expand All @@ -208,6 +194,7 @@ bool try_scan_carrot_coinbase_enote_receiver(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver,
const crypto::public_key &main_address_spend_pubkey,
const crypto::public_key &main_address_view_pubkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out)
{
Expand All @@ -216,6 +203,7 @@ bool try_scan_carrot_coinbase_enote_receiver(
enote,
s_sender_receiver,
{&main_address_spend_pubkey, 1},
main_address_view_pubkey,
sender_extension_g_out,
sender_extension_t_out,
dummy_main_address_spend_pubkey);
Expand Down Expand Up @@ -291,31 +279,34 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
{
crypto::public_key recovered_address_spend_pubkey;
payment_id_t recovered_payment_id;
CarrotEnoteType recovered_enote_type;
janus_anchor_t dummy_janus_anchor;
bool verified_normal_janus = false;
if (!try_scan_carrot_enote_external_normal_checked(enote,
janus_anchor_t recovered_janus_anchor;
if (!try_scan_carrot_enote_external_no_janus(enote,
encrypted_payment_id,
s_sender_receiver,
{&destination.address_spend_pubkey, 1},
sender_extension_g_out,
sender_extension_t_out,
recovered_address_spend_pubkey,
amount_out,
amount_blinding_factor_out,
recovered_payment_id,
recovered_enote_type,
dummy_janus_anchor,
verified_normal_janus))
enote_type_out,
recovered_janus_anchor))
return false;
else if (!verified_normal_janus)

if (recovered_address_spend_pubkey != destination.address_spend_pubkey)
return false;
else if (recovered_address_spend_pubkey != destination.address_spend_pubkey)

if (!verify_carrot_normal_janus_protection(
make_carrot_input_context(enote.tx_first_key_image),
destination.address_spend_pubkey,
destination.address_view_pubkey,
destination.is_subaddress,
enote.enote_ephemeral_pubkey,
recovered_janus_anchor,
recovered_payment_id))
return false;
else if (check_pid && recovered_payment_id != destination.payment_id)
return false;
else if (recovered_enote_type != CarrotEnoteType::PAYMENT)
return false;

return true;
}
Expand All @@ -334,22 +325,38 @@ bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
CarrotEnoteType &enote_type_out)
{
janus_anchor_t nominal_janus_anchor;
bool verified_normal_janus = false;
if (!try_scan_carrot_enote_external_normal_checked(enote,
if (!try_scan_carrot_enote_external_no_janus(enote,
encrypted_payment_id,
s_sender_receiver,
main_address_spend_pubkeys,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
amount_out,
amount_blinding_factor_out,
payment_id_out,
enote_type_out,
nominal_janus_anchor,
verified_normal_janus))
nominal_janus_anchor))
return false;

// K^j_v from K^j_s
crypto::public_key address_view_pubkey;
bool is_subaddress{};
if (!recover_address_view_pubkey(address_spend_pubkey_out,
main_address_spend_pubkeys,
k_view_dev,
address_view_pubkey,
is_subaddress))
return false;

const bool verified_normal_janus = verify_carrot_normal_janus_protection(
make_carrot_input_context(enote.tx_first_key_image),
address_spend_pubkey_out,
address_view_pubkey,
is_subaddress,
enote.enote_ephemeral_pubkey,
nominal_janus_anchor,
payment_id_out);

if (!verified_normal_janus && !verify_carrot_special_janus_protection(enote.tx_first_key_image,
enote.enote_ephemeral_pubkey,
enote.onetime_address,
Expand Down
3 changes: 3 additions & 0 deletions src/carrot_core/scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ bool try_make_carrot_shared_key_receiver(
* @param enote_ephemeral_privkey d_e
* @param s_sender_receiver s_sr
* @param main_address_spend_pubkeys {K^0_s, ...}
* @param main_address_view_pubkey K^0_v
* @param[out] sender_extension_g_out k^g_o
* @param[out] sender_extension_t_out k^t_o
* @param[out] main_address_spend_pubkey_out K^0_s, which will be one of main_address_spend_pubkeys
Expand All @@ -92,13 +93,15 @@ bool try_scan_carrot_coinbase_enote_receiver(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver,
const epee::span<const crypto::public_key> main_address_spend_pubkeys,
const crypto::public_key &main_address_view_pubkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &main_address_spend_pubkey_out);
bool try_scan_carrot_coinbase_enote_receiver(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver,
const crypto::public_key &main_address_spend_pubkey,
const crypto::public_key &main_address_view_pubkey,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out);
/**
Expand Down
Loading
Loading