Compare commits

...

3 Commits

Author SHA1 Message Date
jeffro256
d8d07334ce
hopefully better mac compiling 2024-11-26 16:15:10 -06:00
jeffro256
f9be7571c2
scan using a device interface 2024-11-26 15:57:04 -06:00
jeffro256
9a0379c1a1
remove incorrect dead function 2024-11-26 12:58:22 -06:00
22 changed files with 509 additions and 265 deletions

View File

@ -32,6 +32,7 @@ set(carrot_core_sources
carrot_enote_scan.cpp carrot_enote_scan.cpp
core_types.cpp core_types.cpp
destination.cpp destination.cpp
device_ram_borrowed.cpp
enote_utils.cpp enote_utils.cpp
hash_functions.cpp hash_functions.cpp
payment_proposal.cpp) payment_proposal.cpp)

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //
@ -51,7 +51,7 @@ void make_carrot_provespend_key(const crypto::secret_key &s_master,
{ {
// k_ps = H_n(s_m) // k_ps = H_n(s_m)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_PROVE_SPEND_KEY>(); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_PROVE_SPEND_KEY>();
derive_scalar(transcript.data(), transcript.size, &s_master, to_bytes(k_prove_spend_out)); derive_scalar(transcript.data(), transcript.size(), &s_master, to_bytes(k_prove_spend_out));
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_viewbalance_secret(const crypto::secret_key &s_master, void make_carrot_viewbalance_secret(const crypto::secret_key &s_master,
@ -59,7 +59,7 @@ void make_carrot_viewbalance_secret(const crypto::secret_key &s_master,
{ {
// s_vb = H_32(s_m) // s_vb = H_32(s_m)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_VIEW_BALANCE_SECRET>(); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_VIEW_BALANCE_SECRET>();
derive_bytes_32(transcript.data(), transcript.size, &s_master, to_bytes(s_view_balance_out)); derive_bytes_32(transcript.data(), transcript.size(), &s_master, to_bytes(s_view_balance_out));
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance, void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance,
@ -67,7 +67,7 @@ void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance,
{ {
// k_gi = H_n(s_vb) // k_gi = H_n(s_vb)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_GENERATE_IMAGE_KEY>(); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_GENERATE_IMAGE_KEY>();
derive_scalar(transcript.data(), transcript.size, &s_view_balance, to_bytes(k_generate_image_out)); derive_scalar(transcript.data(), transcript.size(), &s_view_balance, to_bytes(k_generate_image_out));
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance, void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance,
@ -75,7 +75,7 @@ void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance,
{ {
// k_v = H_n(s_vb) // k_v = H_n(s_vb)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_INCOMING_VIEW_KEY>(); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_INCOMING_VIEW_KEY>();
derive_scalar(transcript.data(), transcript.size, &s_view_balance, to_bytes(k_view_out)); derive_scalar(transcript.data(), transcript.size(), &s_view_balance, to_bytes(k_view_out));
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance, void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance,
@ -83,7 +83,7 @@ void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance
{ {
// s_ga = H_32(s_vb) // s_ga = H_32(s_vb)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_GENERATE_ADDRESS_SECRET>(); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_GENERATE_ADDRESS_SECRET>();
derive_bytes_32(transcript.data(), transcript.size, &s_view_balance, to_bytes(s_generate_address_out)); derive_bytes_32(transcript.data(), transcript.size(), &s_view_balance, to_bytes(s_generate_address_out));
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_spend_pubkey(const crypto::secret_key &k_generate_image, void make_carrot_spend_pubkey(const crypto::secret_key &k_generate_image,

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //
@ -52,7 +52,7 @@ void make_carrot_index_extension_generator(const crypto::secret_key &s_generate_
{ {
// s^j_gen = H_32[s_ga](j_major, j_minor) // s^j_gen = H_32[s_ga](j_major, j_minor)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ADDRESS_INDEX_GEN>(j_major, j_minor); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ADDRESS_INDEX_GEN>(j_major, j_minor);
derive_bytes_32(transcript.data(), transcript.size, &s_generate_address, &address_generator_out); derive_bytes_32(transcript.data(), transcript.size(), &s_generate_address, &address_generator_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_subaddress_scalar(const crypto::public_key &spend_pubkey, void make_carrot_subaddress_scalar(const crypto::public_key &spend_pubkey,
@ -64,7 +64,7 @@ void make_carrot_subaddress_scalar(const crypto::public_key &spend_pubkey,
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen) // k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR>( const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR>(
spend_pubkey, j_major, j_minor); spend_pubkey, j_major, j_minor);
derive_scalar(transcript.data(), transcript.size, &s_address_generator, subaddress_scalar_out.data); derive_scalar(transcript.data(), transcript.size(), &s_address_generator, subaddress_scalar_out.data);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_address_spend_pubkey(const crypto::public_key &spend_pubkey, void make_carrot_address_spend_pubkey(const crypto::public_key &spend_pubkey,
@ -82,20 +82,4 @@ void make_carrot_address_spend_pubkey(const crypto::public_key &spend_pubkey,
rct::pk2rct(spend_pubkey), rct::sk2rct(subaddress_scalar))); rct::pk2rct(spend_pubkey), rct::sk2rct(subaddress_scalar)));
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_address(const crypto::public_key &spend_pubkey,
const crypto::secret_key &s_generate_address,
const std::uint32_t j_major,
const std::uint32_t j_minor,
const crypto::secret_key &k_view,
crypto::public_key &address_spend_pubkey_out)
{
// K^j_s = k^j_subscal * K_s
crypto::public_key address_spend_pubkey;
make_carrot_address_spend_pubkey(spend_pubkey, s_generate_address, j_major, j_minor, address_spend_pubkey);
// K^j_v = k_v * K^j_s
address_spend_pubkey_out = rct::rct2pk(rct::scalarmultKey(
rct::pk2rct(address_spend_pubkey), rct::sk2rct(k_view)));
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot } //namespace carrot

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //
@ -92,22 +92,4 @@ void make_carrot_address_spend_pubkey(const crypto::public_key &spend_pubkey,
const std::uint32_t j_minor, const std::uint32_t j_minor,
crypto::public_key &address_spend_pubkey_out); crypto::public_key &address_spend_pubkey_out);
/**
* brief: make_carrot_address - (K^j_s, K^j_v)
* K^j_s = k^j_subscal * K_s
* K^j_v = k_v K^j_s
* param: spend_pubkey - K_s = k_gi G + k_ps U
* param: s_generate_address - s_ga
* param: j_major -
* param: j_minor -
* param: k_view - k_v
* outparam: address_spend_pubkey_out - K^j_s
*/
void make_carrot_address(const crypto::public_key &spend_pubkey,
const crypto::secret_key &s_generate_address,
const std::uint32_t j_major,
const std::uint32_t j_minor,
const crypto::secret_key &k_view,
crypto::public_key &address_spend_pubkey_out);
} //namespace carrot } //namespace carrot

View File

@ -32,6 +32,7 @@
#include "carrot_enote_scan.h" #include "carrot_enote_scan.h"
//local headers //local headers
#include "crypto/generators.h"
#include "enote_utils.h" #include "enote_utils.h"
#include "ringct/rctOps.h" #include "ringct/rctOps.h"
@ -44,10 +45,9 @@ namespace carrot
{ {
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
static bool try_scan_carrot_non_coinbase_no_janus(const CarrotEnoteV1 &enote, static bool try_scan_carrot_non_coinbase_core(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> encrypted_payment_id, const std::optional<encrypted_payment_id_t> encrypted_payment_id,
const input_context_t &input_context, const crypto::hash &s_sender_receiver,
const unsigned char s_sender_receiver_unctx[32],
crypto::secret_key &sender_extension_g_out, crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out, crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out, crypto::public_key &address_spend_pubkey_out,
@ -57,17 +57,6 @@ static bool try_scan_carrot_non_coinbase_no_janus(const CarrotEnoteV1 &enote,
CarrotEnoteType &enote_type_out, CarrotEnoteType &enote_type_out,
janus_anchor_t &nominal_janus_anchor_out) janus_anchor_t &nominal_janus_anchor_out)
{ {
// if vt' != vt, then FAIL
if (!test_carrot_view_tag(s_sender_receiver_unctx, input_context, enote.onetime_address, enote.view_tag))
return false;
// s^ctx_sr = H_32(s_sr, D_e, input_context)
crypto::hash s_sender_receiver;
make_carrot_sender_receiver_secret(s_sender_receiver_unctx,
enote.enote_ephemeral_pubkey,
input_context,
s_sender_receiver);
// if cannot recompute C_a, then FAIL // if cannot recompute C_a, then FAIL
if (!try_get_carrot_amount(s_sender_receiver, if (!try_get_carrot_amount(s_sender_receiver,
enote.amount_enc, enote.amount_enc,
@ -109,9 +98,68 @@ static bool try_scan_carrot_non_coinbase_no_janus(const CarrotEnoteV1 &enote,
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
bool verify_carrot_janus_protection(const input_context_t &input_context,
const crypto::public_key &onetime_address,
const view_incoming_key_device &k_view_dev,
const crypto::public_key &account_spend_pubkey,
const crypto::public_key &nominal_address_spend_pubkey,
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const janus_anchor_t &nominal_anchor,
payment_id_t &nominal_payment_id_inout)
{
const bool is_subaddress = nominal_address_spend_pubkey != account_spend_pubkey;
// make K^j_v'
crypto::public_key nominal_address_view_pubkey;
if (is_subaddress)
{
// K^j_v' = k_v K^j_s'
if (!k_view_dev.view_key_scalar_mult_ed25519(nominal_address_spend_pubkey, nominal_address_view_pubkey))
return false;
}
else // cryptonote address
{
// K^j_v' = k_v G
if (!k_view_dev.view_key_scalar_mult_ed25519(crypto::get_G(), nominal_address_view_pubkey))
return false;
}
// if can recompute D_e with pid', then PASS
if (verify_carrot_external_janus_protection(nominal_anchor,
input_context,
nominal_address_spend_pubkey,
nominal_address_view_pubkey,
is_subaddress,
nominal_payment_id_inout,
enote_ephemeral_pubkey))
return true;
// if can recompute D_e with null pid, then PASS
nominal_payment_id_inout = null_payment_id;
if (verify_carrot_external_janus_protection(nominal_anchor,
input_context,
nominal_address_spend_pubkey,
nominal_address_view_pubkey,
is_subaddress,
null_payment_id,
enote_ephemeral_pubkey))
return true;
// anchor_sp = H_16(D_e, input_context, Ko, k_v, K_s)
janus_anchor_t expected_special_anchor;
k_view_dev.make_janus_anchor_special(enote_ephemeral_pubkey,
input_context,
onetime_address,
account_spend_pubkey,
expected_special_anchor);
// attempt special janus check: anchor_sp ?= anchor'
return expected_special_anchor == nominal_anchor;
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_coinbase_enote(const CarrotCoinbaseEnoteV1 &enote, bool try_scan_carrot_coinbase_enote(const CarrotCoinbaseEnoteV1 &enote,
const crypto::x25519_pubkey &s_sender_receiver_unctx, const crypto::x25519_pubkey &s_sender_receiver_unctx,
const crypto::secret_key &k_view, const view_incoming_key_device &k_view_dev,
const crypto::public_key &account_spend_pubkey, const crypto::public_key &account_spend_pubkey,
crypto::secret_key &sender_extension_g_out, crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out, crypto::secret_key &sender_extension_t_out,
@ -166,7 +214,7 @@ bool try_scan_carrot_coinbase_enote(const CarrotCoinbaseEnoteV1 &enote,
payment_id_t dummy_payment_id = null_payment_id; payment_id_t dummy_payment_id = null_payment_id;
if (!verify_carrot_janus_protection(input_context, if (!verify_carrot_janus_protection(input_context,
enote.onetime_address, enote.onetime_address,
k_view, k_view_dev,
account_spend_pubkey, account_spend_pubkey,
address_spend_pubkey_out, address_spend_pubkey_out,
enote.enote_ephemeral_pubkey, enote.enote_ephemeral_pubkey,
@ -180,7 +228,7 @@ bool try_scan_carrot_coinbase_enote(const CarrotCoinbaseEnoteV1 &enote,
bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote, bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> encrypted_payment_id, const std::optional<encrypted_payment_id_t> encrypted_payment_id,
const crypto::x25519_pubkey &s_sender_receiver_unctx, const crypto::x25519_pubkey &s_sender_receiver_unctx,
const crypto::secret_key &k_view, const view_incoming_key_device &k_view_dev,
const crypto::public_key &account_spend_pubkey, const crypto::public_key &account_spend_pubkey,
crypto::secret_key &sender_extension_g_out, crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out, crypto::secret_key &sender_extension_t_out,
@ -194,12 +242,22 @@ bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote,
input_context_t input_context; input_context_t input_context;
make_carrot_input_context(enote.tx_first_key_image, input_context); make_carrot_input_context(enote.tx_first_key_image, input_context);
// test view tag
if (!test_carrot_view_tag(s_sender_receiver_unctx.data, input_context, enote.onetime_address, enote.view_tag))
return false;
// s^ctx_sr = H_32(s_sr, D_e, input_context)
crypto::hash s_sender_receiver;
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
enote.enote_ephemeral_pubkey,
input_context,
s_sender_receiver);
// do core scanning // do core scanning
janus_anchor_t nominal_anchor; janus_anchor_t nominal_anchor;
if (!try_scan_carrot_non_coinbase_no_janus(enote, if (!try_scan_carrot_non_coinbase_core(enote,
encrypted_payment_id, encrypted_payment_id,
input_context, s_sender_receiver,
s_sender_receiver_unctx.data,
sender_extension_g_out, sender_extension_g_out,
sender_extension_t_out, sender_extension_t_out,
address_spend_pubkey_out, address_spend_pubkey_out,
@ -213,7 +271,7 @@ bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote,
// verify Janus attack protection // verify Janus attack protection
if (!verify_carrot_janus_protection(input_context, if (!verify_carrot_janus_protection(input_context,
enote.onetime_address, enote.onetime_address,
k_view, k_view_dev,
account_spend_pubkey, account_spend_pubkey,
address_spend_pubkey_out, address_spend_pubkey_out,
enote.enote_ephemeral_pubkey, enote.enote_ephemeral_pubkey,
@ -225,7 +283,7 @@ bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote,
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_internal(const CarrotEnoteV1 &enote, bool try_scan_carrot_enote_internal(const CarrotEnoteV1 &enote,
const crypto::secret_key &s_view_balance, const view_balance_secret_device &s_view_balance_dev,
crypto::secret_key &sender_extension_g_out, crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out, crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out, crypto::public_key &address_spend_pubkey_out,
@ -237,13 +295,26 @@ bool try_scan_carrot_enote_internal(const CarrotEnoteV1 &enote,
input_context_t input_context; input_context_t input_context;
make_carrot_input_context(enote.tx_first_key_image, input_context); make_carrot_input_context(enote.tx_first_key_image, input_context);
// vt = H_3(s_sr || input_context || Ko)
view_tag_t nominal_view_tag;
s_view_balance_dev.make_internal_view_tag(input_context, enote.onetime_address, nominal_view_tag);
// test view tag
if (nominal_view_tag != enote.view_tag)
return false;
// s^ctx_sr = H_32(s_vb, D_e, input_context)
crypto::hash s_sender_receiver;
s_view_balance_dev.make_internal_sender_receiver_secret(enote.enote_ephemeral_pubkey,
input_context,
s_sender_receiver);
// do core scanning // do core scanning
janus_anchor_t nominal_anchor; janus_anchor_t nominal_anchor;
payment_id_t dummy_payment_id; payment_id_t dummy_payment_id;
if (!try_scan_carrot_non_coinbase_no_janus(enote, if (!try_scan_carrot_non_coinbase_core(enote,
std::nullopt, std::nullopt,
input_context, s_sender_receiver,
to_bytes(s_view_balance),
sender_extension_g_out, sender_extension_g_out,
sender_extension_t_out, sender_extension_t_out,
address_spend_pubkey_out, address_spend_pubkey_out,

View File

@ -32,6 +32,7 @@
//local headers //local headers
#include "carrot_enote_types.h" #include "carrot_enote_types.h"
#include "device.h"
//third party headers //third party headers
@ -43,9 +44,18 @@
namespace carrot namespace carrot
{ {
bool verify_carrot_janus_protection(const input_context_t &input_context,
const crypto::public_key &onetime_address,
const view_incoming_key_device &k_view_dev,
const crypto::public_key &account_spend_pubkey,
const crypto::public_key &nominal_address_spend_pubkey,
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const janus_anchor_t &nominal_anchor,
payment_id_t &nominal_payment_id_inout);
bool try_scan_carrot_coinbase_enote(const CarrotCoinbaseEnoteV1 &enote, bool try_scan_carrot_coinbase_enote(const CarrotCoinbaseEnoteV1 &enote,
const crypto::x25519_pubkey &s_sender_receiver_unctx, const crypto::x25519_pubkey &s_sender_receiver_unctx,
const crypto::secret_key &k_view, const view_incoming_key_device &k_view_dev,
const crypto::public_key &account_spend_pubkey, const crypto::public_key &account_spend_pubkey,
crypto::secret_key &sender_extension_g_out, crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out, crypto::secret_key &sender_extension_t_out,
@ -54,7 +64,7 @@ bool try_scan_carrot_coinbase_enote(const CarrotCoinbaseEnoteV1 &enote,
bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote, bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote,
const std::optional<encrypted_payment_id_t> encrypted_payment_id, const std::optional<encrypted_payment_id_t> encrypted_payment_id,
const crypto::x25519_pubkey &s_sender_receiver_unctx, const crypto::x25519_pubkey &s_sender_receiver_unctx,
const crypto::secret_key &k_view, const view_incoming_key_device &k_view_dev,
const crypto::public_key &account_spend_pubkey, const crypto::public_key &account_spend_pubkey,
crypto::secret_key &sender_extension_g_out, crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out, crypto::secret_key &sender_extension_t_out,
@ -65,7 +75,7 @@ bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote,
CarrotEnoteType &enote_type_out); CarrotEnoteType &enote_type_out);
bool try_scan_carrot_enote_internal(const CarrotEnoteV1 &enote, bool try_scan_carrot_enote_internal(const CarrotEnoteV1 &enote,
const crypto::secret_key &s_view_balance, const view_balance_secret_device &s_view_balance_dev,
crypto::secret_key &sender_extension_g_out, crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out, crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out, crypto::public_key &address_spend_pubkey_out,

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //

165
src/carrot_core/device.h Normal file
View File

@ -0,0 +1,165 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Abstract interfaces for performing scanning without revealing account keys
#pragma once
//local headers
#include "core_types.h"
#include "crypto/crypto.h"
#include "crypto/x25519.h"
//third party headers
//standard headers
#include <cstdio>
#include <stdexcept>
#include <string>
//forward declarations
/**
* These device interfaces were written primarily to be as lean as possible, for ease of the
* implementor, so long as account keys don't leak. As such, the interfaces do not shield
* sender-receiver secrets, and thus temporary access to this device interface can expose
* transaction content permanently in a provable manner. The device interface currently used in
* Monero (hw::device) also exposes transaction content, which can be saved permanently, but it
* wouldn't necessarily be provable. Thus, in the case of a breach, the original user has some
* plausible deniability with (hw::device), which cannot be said of the interfaces in this file.
* It's not impossible to make carrot scanning happen completely on-device, but it is significantly
* more involved.
*/
namespace carrot
{
/**
* brief: base exception type for reporting carrot device errors.
* note: devices should only throw this exception or derived classes
*/
struct device_error: public std::runtime_error
{
/**
* param: dev_make - e.g. "Trezor", "Ledger"
* param: dev_model - e.g. "Model T", "Nano X"
* param: func_called - verbatim device interface method name, e.g. "view_key_8_scalar_mult_x25519"
* param: msg - arbitrary error message
* param: code - arbitrary error code
*/
device_error(std::string &&dev_make,
std::string &&dev_model,
std::string &&func_called,
std::string &&msg,
const int code)
: std::runtime_error(make_formatted_message(dev_make, dev_model, func_called, msg, code)),
dev_make(dev_make), dev_model(dev_model), func_called(func_called), msg(msg), code(code)
{}
static std::string make_formatted_message(const std::string &dev_make,
const std::string &dev_model,
const std::string &func_called,
const std::string &msg,
const int code)
{
char buf[384];
snprintf(buf, sizeof(buf),
"%s %s device error (%d), at %s(): %s",
dev_make.c_str(), dev_model.c_str(), code, func_called.c_str(), msg.c_str());
return {buf};
}
const std::string dev_make;
const std::string dev_model;
const std::string func_called;
const std::string msg;
const int code;
};
struct view_incoming_key_device
{
/**
* brief: view_key_scalar_mult_ed25519 - do an Ed25519 scalar mult against the incoming view key
* param: P - Ed25519 base point
* outparam: kvP = k_v P
* return: true on success, false on failure (e.g. unable to decompress point)
*/
virtual bool view_key_scalar_mult_ed25519(const crypto::public_key &P,
crypto::public_key &kvP) const = 0;
/**
* brief: view_key_8_scalar_mult_x25519 - do an X25519 scalar mult and cofactor clear against the incoming view key
* param: D - X25519 base point
* outparam: kv8D = 8 k_v D
* return: true on success, false on failure (e.g. unable to decompress point)
*/
virtual bool view_key_8_scalar_mult_x25519(const crypto::x25519_pubkey &D,
crypto::x25519_pubkey &kv8D) const = 0;
/**
* brief: make_janus_anchor_special - make a janus anchor for "special" enotes
* param: enote_ephemeral_pubkey - D_e
* param: input_context - input_context
* param: account_spend_pubkey - K_s
* outparam: anchor_special_out - anchor_sp = anchor_sp = H_16(D_e, input_context, Ko, k_v, K_s)
*/
virtual void make_janus_anchor_special(const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::public_key &account_spend_pubkey,
janus_anchor_t &anchor_special_out) const = 0;
virtual ~view_incoming_key_device() = default;
};
struct view_balance_secret_device
{
/**
* brief: make_internal_view_tag - make an internal view tag, given non-secret data
* param: input_context - input_context
* param: onetime_address - Ko
* outparam: view_tag_out - vt = H_3(s_vb || input_context || Ko)
*/
virtual void make_internal_view_tag(const input_context_t &input_context,
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out) const = 0;
/**
* brief: make_internal_sender_receiver_secret - make internal sender-receiver secret, given non-secret data
* param: enote_ephemeral_pubkey - D_e
* param: input_context - input_context
* outparam: s_sender_receiver_out - s_sr = s^ctx_sr = H_32(s_sr, D_e, input_context)
*/
virtual void make_internal_sender_receiver_secret(const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out) const = 0;
virtual ~view_balance_secret_device() = default;
};
} //namespace carrot

View File

@ -0,0 +1,90 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//pair header
#include "device_ram_borrowed.h"
//local headers
#include "enote_utils.h"
#include "ringct/rctOps.h"
//third party headers
//standard headers
namespace carrot
{
//-------------------------------------------------------------------------------------------------------------------
bool view_incoming_key_ram_borrowed_device::view_key_scalar_mult_ed25519(const crypto::public_key &P,
crypto::public_key &kvP) const
{
kvP = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(P), rct::sk2rct(m_k_view_incoming)));
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool view_incoming_key_ram_borrowed_device::view_key_8_scalar_mult_x25519(const crypto::x25519_pubkey &D,
crypto::x25519_pubkey &kv8D) const
{
return make_carrot_uncontextualized_shared_key_receiver(m_k_view_incoming, D, kv8D);
}
//-------------------------------------------------------------------------------------------------------------------
void view_incoming_key_ram_borrowed_device::make_janus_anchor_special(
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::public_key &account_spend_pubkey,
janus_anchor_t &anchor_special_out) const
{
return make_carrot_janus_anchor_special(enote_ephemeral_pubkey,
input_context,
onetime_address,
m_k_view_incoming,
account_spend_pubkey,
anchor_special_out);
}
//-------------------------------------------------------------------------------------------------------------------
void view_balance_secret_ram_borrowed_device::make_internal_view_tag(const input_context_t &input_context,
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out) const
{
make_carrot_view_tag(to_bytes(m_s_view_balance), input_context, onetime_address, view_tag_out);
}
//-------------------------------------------------------------------------------------------------------------------
void view_balance_secret_ram_borrowed_device::make_internal_sender_receiver_secret(
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out) const
{
make_carrot_sender_receiver_secret(to_bytes(m_s_view_balance),
enote_ephemeral_pubkey,
input_context,
s_sender_receiver_out);
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot

View File

@ -0,0 +1,86 @@
// Copyright (c) 2024, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Carrot device implementations for in-memory keys & secrets
#pragma once
//local headers
#include "device.h"
//third party headers
//standard headers
//forward declarations
namespace carrot
{
class view_incoming_key_ram_borrowed_device: public view_incoming_key_device
{
public:
view_incoming_key_ram_borrowed_device(const crypto::secret_key &k_view_incoming):
m_k_view_incoming(k_view_incoming) {}
bool view_key_scalar_mult_ed25519(const crypto::public_key &P,
crypto::public_key &kvP) const override;
bool view_key_8_scalar_mult_x25519(const crypto::x25519_pubkey &D,
crypto::x25519_pubkey &kv8D) const override;
void make_janus_anchor_special(const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::public_key &account_spend_pubkey,
janus_anchor_t &anchor_special_out) const override;
private:
const crypto::secret_key &m_k_view_incoming;
};
class view_balance_secret_ram_borrowed_device: public view_balance_secret_device
{
public:
view_balance_secret_ram_borrowed_device(const crypto::secret_key &s_view_balance):
m_s_view_balance(s_view_balance) {}
void make_internal_view_tag(const input_context_t &input_context,
const crypto::public_key &onetime_address,
view_tag_t &view_tag_out) const override;
void make_internal_sender_receiver_secret(const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
crypto::hash &s_sender_receiver_out) const override;
private:
const crypto::secret_key &m_s_view_balance;
};
} //namespace carrot

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //
@ -88,7 +88,7 @@ void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
// k_e = (H_64(anchor_norm, input_context, K^j_s, K^j_v, pid)) mod l // k_e = (H_64(anchor_norm, input_context, K^j_s, K^j_v, pid)) mod l
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY>( const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY>(
anchor_norm, input_context, address_spend_pubkey, address_view_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); derive_scalar(transcript.data(), transcript.size(), nullptr, &enote_ephemeral_privkey_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &enote_ephemeral_privkey, void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &enote_ephemeral_privkey,
@ -163,7 +163,7 @@ void make_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
{ {
// vt = H_3(s_sr || input_context || Ko) // vt = H_3(s_sr || input_context || Ko)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_VIEW_TAG>(input_context, onetime_address); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_VIEW_TAG>(input_context, onetime_address);
derive_bytes_3(transcript.data(), transcript.size, s_sender_receiver_unctx, &view_tag_out); derive_bytes_3(transcript.data(), transcript.size(), s_sender_receiver_unctx, &view_tag_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_input_context_coinbase(const std::uint64_t block_index, input_context_t &input_context_out) void make_carrot_input_context_coinbase(const std::uint64_t block_index, input_context_t &input_context_out)
@ -189,7 +189,7 @@ void make_carrot_sender_receiver_secret(const unsigned char s_sender_receiver_un
// s^ctx_sr = H_32(s_sr, D_e, input_context) // s^ctx_sr = H_32(s_sr, D_e, input_context)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SENDER_RECEIVER_SECRET>( const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SENDER_RECEIVER_SECRET>(
enote_ephemeral_pubkey, input_context); enote_ephemeral_pubkey, input_context);
derive_bytes_32(transcript.data(), transcript.size, s_sender_receiver_unctx, &s_sender_receiver_out); derive_bytes_32(transcript.data(), transcript.size(), s_sender_receiver_unctx, &s_sender_receiver_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_onetime_address_extension_g(const crypto::hash &s_sender_receiver, void make_carrot_onetime_address_extension_g(const crypto::hash &s_sender_receiver,
@ -198,7 +198,7 @@ void make_carrot_onetime_address_extension_g(const crypto::hash &s_sender_receiv
{ {
// k^o_g = H_n("..g..", s^ctx_sr, C_a) // k^o_g = H_n("..g..", s^ctx_sr, C_a)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ONETIME_EXTENSION_G>(amount_commitment); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ONETIME_EXTENSION_G>(amount_commitment);
derive_scalar(transcript.data(), transcript.size, &s_sender_receiver, &sender_extension_out); derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &sender_extension_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_onetime_address_extension_t(const crypto::hash &s_sender_receiver, void make_carrot_onetime_address_extension_t(const crypto::hash &s_sender_receiver,
@ -207,7 +207,7 @@ void make_carrot_onetime_address_extension_t(const crypto::hash &s_sender_receiv
{ {
// k^o_t = H_n("..t..", s^ctx_sr, C_a) // k^o_t = H_n("..t..", s^ctx_sr, C_a)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ONETIME_EXTENSION_T>(amount_commitment); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ONETIME_EXTENSION_T>(amount_commitment);
derive_scalar(transcript.data(), transcript.size, &s_sender_receiver, &sender_extension_out); derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &sender_extension_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_onetime_address_extension_pubkey(const crypto::hash &s_sender_receiver, void make_carrot_onetime_address_extension_pubkey(const crypto::hash &s_sender_receiver,
@ -253,7 +253,7 @@ void make_carrot_amount_blinding_factor(const crypto::hash &s_sender_receiver,
// k_a = H_n(s^ctx_sr, enote_type) // k_a = H_n(s^ctx_sr, enote_type)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR>( const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR>(
static_cast<unsigned char>(enote_type)); static_cast<unsigned char>(enote_type));
derive_scalar(transcript.data(), transcript.size, &s_sender_receiver, &amount_blinding_factor_out); derive_scalar(transcript.data(), transcript.size(), &s_sender_receiver, &amount_blinding_factor_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void make_carrot_anchor_encryption_mask(const crypto::hash &s_sender_receiver, void make_carrot_anchor_encryption_mask(const crypto::hash &s_sender_receiver,
@ -262,7 +262,7 @@ void make_carrot_anchor_encryption_mask(const crypto::hash &s_sender_receiver,
{ {
// m_anchor = H_16(s^ctx_sr, Ko) // m_anchor = H_16(s^ctx_sr, Ko)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_ANCHOR>(onetime_address); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_ANCHOR>(onetime_address);
derive_bytes_16(transcript.data(), transcript.size, &s_sender_receiver, &anchor_encryption_mask_out); derive_bytes_16(transcript.data(), transcript.size(), &s_sender_receiver, &anchor_encryption_mask_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
encrypted_janus_anchor_t encrypt_carrot_anchor(const janus_anchor_t &anchor, encrypted_janus_anchor_t encrypt_carrot_anchor(const janus_anchor_t &anchor,
@ -295,7 +295,7 @@ void make_carrot_amount_encryption_mask(const crypto::hash &s_sender_receiver,
{ {
// m_a = H_8(s^ctx_sr, Ko) // m_a = H_8(s^ctx_sr, Ko)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_AMOUNT>(onetime_address); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_AMOUNT>(onetime_address);
derive_bytes_8(transcript.data(), transcript.size, &s_sender_receiver, &amount_encryption_mask_out); derive_bytes_8(transcript.data(), transcript.size(), &s_sender_receiver, &amount_encryption_mask_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
encrypted_amount_t encrypt_carrot_amount(const rct::xmr_amount amount, encrypted_amount_t encrypt_carrot_amount(const rct::xmr_amount amount,
@ -328,7 +328,7 @@ void make_carrot_payment_id_encryption_mask(const crypto::hash &s_sender_receive
{ {
// m_pid = H_8(s^ctx_sr, Ko) // m_pid = H_8(s^ctx_sr, Ko)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_PAYMENT_ID>(onetime_address); const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_ENCRYPTION_MASK_PAYMENT_ID>(onetime_address);
derive_bytes_8(transcript.data(), transcript.size, &s_sender_receiver, &payment_id_encryption_mask_out); derive_bytes_8(transcript.data(), transcript.size(), &s_sender_receiver, &payment_id_encryption_mask_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
encrypted_payment_id_t encrypt_legacy_payment_id(const payment_id_t payment_id, encrypted_payment_id_t encrypt_legacy_payment_id(const payment_id_t payment_id,
@ -365,7 +365,7 @@ void make_carrot_janus_anchor_special(const crypto::x25519_pubkey &enote_ephemer
// anchor_sp = H_16(D_e, input_context, Ko, k_v, K_s) // anchor_sp = H_16(D_e, input_context, Ko, k_v, K_s)
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_JANUS_ANCHOR_SPECIAL>( const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_JANUS_ANCHOR_SPECIAL>(
enote_ephemeral_pubkey, input_context, account_spend_pubkey); enote_ephemeral_pubkey, input_context, account_spend_pubkey);
derive_bytes_16(transcript.data(), transcript.size, &k_view, &anchor_special_out); derive_bytes_16(transcript.data(), transcript.size(), &k_view, &anchor_special_out);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void recover_address_spend_pubkey(const crypto::public_key &onetime_address, void recover_address_spend_pubkey(const crypto::public_key &onetime_address,
@ -449,7 +449,7 @@ bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
return false; return false;
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
bool verify_external_carrot_janus_protection(const janus_anchor_t &nominal_anchor, bool verify_carrot_external_janus_protection(const janus_anchor_t &nominal_anchor,
const input_context_t &input_context, const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey, const crypto::public_key &nominal_address_spend_pubkey,
const crypto::public_key &nominal_address_view_pubkey, const crypto::public_key &nominal_address_view_pubkey,
@ -480,102 +480,4 @@ bool verify_external_carrot_janus_protection(const janus_anchor_t &nominal_ancho
return nominal_enote_ephemeral_pubkey == enote_ephemeral_pubkey; return nominal_enote_ephemeral_pubkey == enote_ephemeral_pubkey;
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
bool verify_external_carrot_janus_protection_receiver(const janus_anchor_t &nominal_anchor,
const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey,
const crypto::public_key &account_spend_pubkey,
const crypto::secret_key &k_view,
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
payment_id_t &nominal_payment_id_inout)
{
const bool is_subaddress = nominal_address_spend_pubkey != account_spend_pubkey;
// make K^j_v', given K^j_s'
crypto::public_key nominal_address_view_pubkey;
if (is_subaddress)
nominal_address_view_pubkey = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(nominal_address_spend_pubkey),
rct::sk2rct(k_view)));
else // cryptonote address
nominal_address_view_pubkey = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(k_view)));
// if can recompute D_e with pid', then PASS
if (verify_external_carrot_janus_protection(nominal_anchor,
input_context,
nominal_address_spend_pubkey,
nominal_address_view_pubkey,
is_subaddress,
nominal_payment_id_inout,
enote_ephemeral_pubkey))
return true;
// if can recompute D_e with null pid, then PASS
nominal_payment_id_inout = null_payment_id;
if (verify_external_carrot_janus_protection(nominal_anchor,
input_context,
nominal_address_spend_pubkey,
nominal_address_view_pubkey,
is_subaddress,
null_payment_id,
enote_ephemeral_pubkey))
return true;
// neither D_e recompute attempt passed, so FAIL
return false;
}
//-------------------------------------------------------------------------------------------------------------------
bool verify_special_carrot_janus_protection(const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::secret_key &k_view,
const crypto::public_key &account_spend_pubkey,
const janus_anchor_t &nominal_anchor)
{
// anchor_sp' = H_16(D_e, input_context, Ko, k_v, K_s)
janus_anchor_t nominal_special_anchor;
make_carrot_janus_anchor_special(enote_ephemeral_pubkey,
input_context,
onetime_address,
k_view,
account_spend_pubkey,
nominal_special_anchor);
// anchor_sp' ?= anchor'
return nominal_special_anchor == nominal_anchor;
}
//-------------------------------------------------------------------------------------------------------------------
bool verify_carrot_janus_protection(const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::secret_key &k_view,
const crypto::public_key &account_spend_pubkey,
const crypto::public_key &nominal_address_spend_pubkey,
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const janus_anchor_t &nominal_anchor,
payment_id_t &nominal_payment_id_inout)
{
// try checking for Janus protection, normal external path
if (verify_external_carrot_janus_protection_receiver(nominal_anchor,
input_context,
nominal_address_spend_pubkey,
account_spend_pubkey,
k_view,
enote_ephemeral_pubkey,
nominal_payment_id_inout))
return true;
// pid is always null for self-send enotes
nominal_payment_id_inout = null_payment_id;
// try checking for Janus protection, special path
if (verify_special_carrot_janus_protection(enote_ephemeral_pubkey,
input_context,
onetime_address,
k_view,
account_spend_pubkey,
nominal_anchor))
return true;
// neither attempt at checking Janus protection worked
return false;
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot } //namespace carrot

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //
@ -364,7 +364,7 @@ bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
rct::xmr_amount &amount_out, rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out); crypto::secret_key &amount_blinding_factor_out);
/** /**
* brief: verify_external_carrot_janus_protection - check normal external enote is Janus safe (i.e. can recompute D_e) * brief: verify_carrot_external_janus_protection - check normal external enote is Janus safe (i.e. can recompute D_e)
* param: nominal_anchor - anchor' * param: nominal_anchor - anchor'
* param: input_context - * param: input_context -
* param: nominal_address_spend_pubkey - K^j_s' * param: nominal_address_spend_pubkey - K^j_s'
@ -374,67 +374,11 @@ bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
* param: enote_ephemeral_pubkey - D_e * param: enote_ephemeral_pubkey - D_e
* return: true if this normal external enote is safe from Janus attacks * return: true if this normal external enote is safe from Janus attacks
*/ */
bool verify_external_carrot_janus_protection(const janus_anchor_t &nominal_anchor, bool verify_carrot_external_janus_protection(const janus_anchor_t &nominal_anchor,
const input_context_t &input_context, const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey, const crypto::public_key &nominal_address_spend_pubkey,
const crypto::public_key &nominal_address_view_pubkey, const crypto::public_key &nominal_address_view_pubkey,
const bool is_subaddress, const bool is_subaddress,
const payment_id_t nominal_payment_id, const payment_id_t nominal_payment_id,
const crypto::x25519_pubkey &enote_ephemeral_pubkey); const crypto::x25519_pubkey &enote_ephemeral_pubkey);
/**
* brief: verify_external_carrot_janus_protection_receiver - check normal external enote is Janus safe (i.e. can
* recompute D_e), and perhaps set nominal pid to null
* param: nominal_anchor - anchor'
* param: input_context -
* param: nominal_address_spend_pubkey - K^j_s'
* param: account_spend_pubkey - K_s
* param: k_view - k_v
* param: enote_ephemeral_pubkey - D_e
* outparam: nominal_payment_id_inout - pid', first tries recomputing D_e with pid', then sets to null and tries again
* return: true if this normal external enote is safe from Janus attacks
*/
bool verify_external_carrot_janus_protection_receiver(const janus_anchor_t &nominal_anchor,
const input_context_t &input_context,
const crypto::public_key &nominal_address_spend_pubkey,
const crypto::public_key &account_spend_pubkey,
const crypto::secret_key &k_view,
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
payment_id_t &nominal_payment_id_inout);
/**
* brief: verify_special_carrot_janus_protection - check special enote is Janus safe (i.e. can recompute anchor_sp)
* param: enote_ephemeral_pubkey - D_e
* param: input_context -
* param: onetime_address - Ko
* param: k_view - k_v
* param: account_spend_pubkey - K_s
* param: nominal_anchor - anchor'
* return: true if this special enote is safe from Janus attacks
*/
bool verify_special_carrot_janus_protection(const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::secret_key &k_view,
const crypto::public_key &account_spend_pubkey,
const janus_anchor_t &nominal_anchor);
/**
* brief: verify_carrot_janus_protection - check whether a received Carrot enote is Janus protected (all paths)
* param: input_context -
* param: onetime_address - Ko
* param: k_view - k_v
* param: account_spend_pubkey - K_s
* param: nominal_address_spend_pubkey - K^j_s'
* param: enote_ephemeral_pubkey - D_e
* param: nominal_anchor - anchor'
* outparam: nominal_payment_id_inout - pass possible pid, set to null if the sender didn't explicitly bind to that pid
* return: true if this received enote is safe from Janus attacks
*/
bool verify_carrot_janus_protection(const input_context_t &input_context,
const crypto::public_key &onetime_address,
const crypto::secret_key &k_view,
const crypto::public_key &account_spend_pubkey,
const crypto::public_key &nominal_address_spend_pubkey,
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
const janus_anchor_t &nominal_anchor,
payment_id_t &nominal_payment_id_inout);
} //namespace carrot } //namespace carrot

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022-2024, The Monero Project // Copyright (c) 2024, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //
@ -77,9 +77,6 @@ template <std::size_t N, const unsigned char domain_sep[N], typename... Ts>
class SpFixedTranscript final class SpFixedTranscript final
{ {
public: public:
//public static member variables
static constexpr std::size_t size = 1 + SpFixedTranscript::domain_sep_size() + detail::sizeof_sum<Ts...>();
//constructors //constructors
/// normal constructor /// normal constructor
SpFixedTranscript(const Ts&... args) SpFixedTranscript(const Ts&... args)
@ -102,6 +99,11 @@ public:
//member functions //member functions
constexpr const void* data() const noexcept { return m_transcript; } constexpr const void* data() const noexcept { return m_transcript; }
static constexpr std::size_t size()
{
return 1 + domain_sep_size() + detail::sizeof_sum<Ts...>();
}
//destructors //destructors
~SpFixedTranscript() ~SpFixedTranscript()
{ {
@ -171,7 +173,7 @@ private:
//member variables //member variables
/// the transcript buffer /// the transcript buffer
unsigned char m_transcript[size]; unsigned char m_transcript[size()];
}; };
template <const auto & domain_sep, typename... Ts> template <const auto & domain_sep, typename... Ts>

View File

@ -31,6 +31,7 @@
#include "carrot_core/account_secrets.h" #include "carrot_core/account_secrets.h"
#include "carrot_core/address_utils.h" #include "carrot_core/address_utils.h"
#include "carrot_core/carrot_enote_scan.h" #include "carrot_core/carrot_enote_scan.h"
#include "carrot_core/device_ram_borrowed.h"
#include "carrot_core/enote_utils.h" #include "carrot_core/enote_utils.h"
#include "carrot_core/payment_proposal.h" #include "carrot_core/payment_proposal.h"
#include "crypto/crypto.h" #include "crypto/crypto.h"
@ -53,6 +54,12 @@ struct mock_carrot_keys
crypto::public_key account_view_pubkey; crypto::public_key account_view_pubkey;
crypto::public_key main_address_view_pubkey; crypto::public_key main_address_view_pubkey;
view_incoming_key_ram_borrowed_device k_view_dev;
view_balance_secret_ram_borrowed_device s_view_balance_dev;
mock_carrot_keys(): k_view_dev(k_view), s_view_balance_dev(s_view_balance)
{}
static mock_carrot_keys generate() static mock_carrot_keys generate()
{ {
mock_carrot_keys k; mock_carrot_keys k;
@ -183,7 +190,7 @@ TEST(carrot_core, main_address_normal_scan_completeness)
const bool scan_success = try_scan_carrot_enote_external(enote, const bool scan_success = try_scan_carrot_enote_external(enote,
encrypted_payment_id, encrypted_payment_id,
s_sender_receiver_unctx, s_sender_receiver_unctx,
keys.k_view, keys.k_view_dev,
keys.account_spend_pubkey, keys.account_spend_pubkey,
recovered_sender_extension_g, recovered_sender_extension_g,
recovered_sender_extension_t, recovered_sender_extension_t,
@ -263,7 +270,7 @@ TEST(carrot_core, subaddress_normal_scan_completeness)
const bool scan_success = try_scan_carrot_enote_external(enote, const bool scan_success = try_scan_carrot_enote_external(enote,
encrypted_payment_id, encrypted_payment_id,
s_sender_receiver_unctx, s_sender_receiver_unctx,
keys.k_view, keys.k_view_dev,
keys.account_spend_pubkey, keys.account_spend_pubkey,
recovered_sender_extension_g, recovered_sender_extension_g,
recovered_sender_extension_t, recovered_sender_extension_t,
@ -351,7 +358,7 @@ TEST(carrot_core, integrated_address_normal_scan_completeness)
const bool scan_success = try_scan_carrot_enote_external(enote, const bool scan_success = try_scan_carrot_enote_external(enote,
encrypted_payment_id, encrypted_payment_id,
s_sender_receiver_unctx, s_sender_receiver_unctx,
keys.k_view, keys.k_view_dev,
keys.account_spend_pubkey, keys.account_spend_pubkey,
recovered_sender_extension_g, recovered_sender_extension_g,
recovered_sender_extension_t, recovered_sender_extension_t,
@ -429,7 +436,7 @@ TEST(carrot_core, main_address_special_scan_completeness)
const bool scan_success = try_scan_carrot_enote_external(enote, const bool scan_success = try_scan_carrot_enote_external(enote,
std::nullopt, std::nullopt,
s_sender_receiver_unctx, s_sender_receiver_unctx,
keys.k_view, keys.k_view_dev,
keys.account_spend_pubkey, keys.account_spend_pubkey,
recovered_sender_extension_g, recovered_sender_extension_g,
recovered_sender_extension_t, recovered_sender_extension_t,
@ -516,7 +523,7 @@ TEST(carrot_core, subaddress_special_scan_completeness)
const bool scan_success = try_scan_carrot_enote_external(enote, const bool scan_success = try_scan_carrot_enote_external(enote,
std::nullopt, std::nullopt,
s_sender_receiver_unctx, s_sender_receiver_unctx,
keys.k_view, keys.k_view_dev,
keys.account_spend_pubkey, keys.account_spend_pubkey,
recovered_sender_extension_g, recovered_sender_extension_g,
recovered_sender_extension_t, recovered_sender_extension_t,
@ -599,7 +606,7 @@ TEST(carrot_core, main_address_internal_scan_completeness)
crypto::secret_key recovered_amount_blinding_factor; crypto::secret_key recovered_amount_blinding_factor;
CarrotEnoteType recovered_enote_type; CarrotEnoteType recovered_enote_type;
const bool scan_success = try_scan_carrot_enote_internal(enote, const bool scan_success = try_scan_carrot_enote_internal(enote,
keys.s_view_balance, keys.s_view_balance_dev,
recovered_sender_extension_g, recovered_sender_extension_g,
recovered_sender_extension_t, recovered_sender_extension_t,
recovered_address_spend_pubkey, recovered_address_spend_pubkey,
@ -674,7 +681,7 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
crypto::secret_key recovered_amount_blinding_factor; crypto::secret_key recovered_amount_blinding_factor;
CarrotEnoteType recovered_enote_type; CarrotEnoteType recovered_enote_type;
const bool scan_success = try_scan_carrot_enote_internal(enote, const bool scan_success = try_scan_carrot_enote_internal(enote,
keys.s_view_balance, keys.s_view_balance_dev,
recovered_sender_extension_g, recovered_sender_extension_g,
recovered_sender_extension_t, recovered_sender_extension_t,
recovered_address_spend_pubkey, recovered_address_spend_pubkey,
@ -745,7 +752,7 @@ TEST(carrot_core, main_address_coinbase_scan_completeness)
crypto::public_key recovered_address_spend_pubkey; crypto::public_key recovered_address_spend_pubkey;
const bool scan_success = try_scan_carrot_coinbase_enote(enote, const bool scan_success = try_scan_carrot_coinbase_enote(enote,
s_sender_receiver_unctx, s_sender_receiver_unctx,
keys.k_view, keys.k_view_dev,
keys.account_spend_pubkey, keys.account_spend_pubkey,
recovered_sender_extension_g, recovered_sender_extension_g,
recovered_sender_extension_t, recovered_sender_extension_t,

View File

@ -46,15 +46,15 @@ TEST(carrot_transcript_fixed, ts_size)
{ {
static constexpr const unsigned char DS1[] = "perspicacious"; static constexpr const unsigned char DS1[] = "perspicacious";
const auto transcript1 = sp::make_fixed_transcript<DS1>((uint32_t)32); const auto transcript1 = sp::make_fixed_transcript<DS1>((uint32_t)32);
EXPECT_EQ(1 + 13 + 4, transcript1.size); EXPECT_EQ(1 + 13 + 4, transcript1.size());
static constexpr const unsigned char DS2[] = "recrudescence"; static constexpr const unsigned char DS2[] = "recrudescence";
const auto transcript2 = sp::make_fixed_transcript<DS2>((uint32_t)32, (uint64_t)64); const auto transcript2 = sp::make_fixed_transcript<DS2>((uint32_t)32, (uint64_t)64);
EXPECT_EQ(1 + 13 + 4 + 8, transcript2.size); EXPECT_EQ(1 + 13 + 4 + 8, transcript2.size());
// vt = H_3(s_sr || input_context || Ko) // vt = H_3(s_sr || input_context || Ko)
const auto transcript_vt = sp::make_fixed_transcript<carrot::CARROT_DOMAIN_SEP_VIEW_TAG>( const auto transcript_vt = sp::make_fixed_transcript<carrot::CARROT_DOMAIN_SEP_VIEW_TAG>(
carrot::input_context_t{}, carrot::input_context_t{},
crypto::public_key{}); crypto::public_key{});
EXPECT_EQ(1 + 15 + 33 + 32, transcript_vt.size); EXPECT_EQ(1 + 15 + 33 + 32, transcript_vt.size());
} }