605 lines
28 KiB
C++
605 lines
28 KiB
C++
// 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.
|
|
|
|
//paired header
|
|
#include "enote_utils.h"
|
|
|
|
//local headers
|
|
#include "config.h"
|
|
extern "C"
|
|
{
|
|
#include "crypto/crypto-ops.h"
|
|
}
|
|
#include "crypto/generators.h"
|
|
#include "crypto/wallet/crypto.h"
|
|
#include "hash_functions.h"
|
|
#include "int-util.h"
|
|
#include "string_tools.h"
|
|
#include "misc_language.h"
|
|
#include "ringct/rctOps.h"
|
|
#include "transcript_fixed.h"
|
|
|
|
//third party headers
|
|
|
|
//standard headers
|
|
#include <mutex>
|
|
|
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
|
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
|
|
|
namespace carrot
|
|
{
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
static const mx25519_impl* get_mx25519_impl()
|
|
{
|
|
static std::once_flag of;
|
|
static const mx25519_impl *impl;
|
|
std::call_once(of, [&](){ impl = mx25519_select_impl(MX25519_TYPE_AUTO); });
|
|
if (impl == nullptr)
|
|
throw std::runtime_error("failed to obtain a mx25519 implementation");
|
|
return impl;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
static encrypted_amount_t enc_amount(const rct::xmr_amount amount, const encrypted_amount_t &mask)
|
|
{
|
|
static_assert(sizeof(rct::xmr_amount) == sizeof(encrypted_amount_t), "");
|
|
|
|
// little_endian(amount) XOR H_8(q, Ko)
|
|
encrypted_amount_t amount_LE;
|
|
memcpy_swap64le(amount_LE.bytes, &amount, 1);
|
|
return amount_LE ^ mask;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
static rct::xmr_amount dec_amount(const encrypted_amount_t &encrypted_amount, const encrypted_amount_t &mask)
|
|
{
|
|
static_assert(sizeof(rct::xmr_amount) == sizeof(encrypted_amount_t), "");
|
|
|
|
// system_endian(encrypted_amount XOR H_8(q, Ko))
|
|
const encrypted_amount_t decryptd_amount{encrypted_amount ^ mask};
|
|
rct::xmr_amount amount;
|
|
memcpy_swap64le(&amount, &decryptd_amount, 1);
|
|
return amount;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
template <typename Pid,
|
|
typename OtherPid = std::conditional_t<std::is_same_v<Pid, payment_id_t>, encrypted_payment_id_t, payment_id_t>>
|
|
static OtherPid convert_payment_id(const Pid &v)
|
|
{
|
|
static_assert(sizeof(Pid) == PAYMENT_ID_BYTES);
|
|
OtherPid conv;
|
|
memcpy(&conv, &v, PAYMENT_ID_BYTES);
|
|
return conv;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
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 payment_id_t payment_id,
|
|
crypto::secret_key &enote_ephemeral_privkey_out)
|
|
{
|
|
// k_e = (H_64(anchor_norm, input_context, K^j_s, pid)) mod l
|
|
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY>(
|
|
anchor_norm, input_context, address_spend_pubkey, payment_id);
|
|
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,
|
|
mx25519_pubkey &enote_ephemeral_pubkey_out)
|
|
{
|
|
// D_e = d_e B
|
|
mx25519_scmul_base(get_mx25519_impl(),
|
|
&enote_ephemeral_pubkey_out,
|
|
reinterpret_cast<const mx25519_privkey*>(&enote_ephemeral_privkey));
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void 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));
|
|
|
|
// K_e = d_e K^j_s
|
|
ge_p3 D_e_in_ed25519;
|
|
ge_scalarmult_p3(&D_e_in_ed25519, to_bytes(enote_ephemeral_privkey), &address_spend_pubkey_p3);
|
|
|
|
// D_e = ConvertPointE(K_e)
|
|
ge_p3_to_x25519(enote_ephemeral_pubkey_out.data, &D_e_in_ed25519);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void 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)
|
|
{
|
|
if (is_subaddress)
|
|
{
|
|
// D_e = d_e ConvertPointE(K^j_s)
|
|
make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey,
|
|
address_spend_pubkey,
|
|
enote_ephemeral_pubkey_out);
|
|
}
|
|
else // !is_subaddress
|
|
{
|
|
// D_e = d_e B
|
|
make_carrot_enote_ephemeral_pubkey_cryptonote(enote_ephemeral_privkey, enote_ephemeral_pubkey_out);
|
|
}
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
bool make_carrot_uncontextualized_shared_key_receiver(const crypto::secret_key &k_view,
|
|
const mx25519_pubkey &enote_ephemeral_pubkey,
|
|
mx25519_pubkey &s_sender_receiver_unctx_out)
|
|
{
|
|
// s_sr = k_v D_e
|
|
mx25519_scmul_key(get_mx25519_impl(),
|
|
&s_sender_receiver_unctx_out,
|
|
reinterpret_cast<const mx25519_privkey*>(&k_view),
|
|
&enote_ephemeral_pubkey);
|
|
|
|
return true;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
bool make_carrot_uncontextualized_shared_key_sender(const crypto::secret_key &enote_ephemeral_privkey,
|
|
const crypto::public_key &address_view_pubkey,
|
|
mx25519_pubkey &s_sender_receiver_unctx_out)
|
|
{
|
|
// if K^j_v not in prime order subgroup, then FAIL
|
|
ge_p3 address_view_pubkey_p3;
|
|
if (!rct::toPointCheckOrder(&address_view_pubkey_p3, to_bytes(address_view_pubkey)))
|
|
return false;
|
|
|
|
// D^j_v = ConvertPointE(K^j_v)
|
|
mx25519_pubkey address_view_pubkey_x25519;
|
|
ge_p3_to_x25519(address_view_pubkey_x25519.data, &address_view_pubkey_p3);
|
|
|
|
// s_sr = d_e D^j_v
|
|
mx25519_scmul_key(get_mx25519_impl(),
|
|
&s_sender_receiver_unctx_out,
|
|
reinterpret_cast<const mx25519_privkey*>(&enote_ephemeral_privkey),
|
|
&address_view_pubkey_x25519);
|
|
|
|
return true;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
|
|
const input_context_t &input_context,
|
|
const crypto::public_key &onetime_address,
|
|
view_tag_t &view_tag_out)
|
|
{
|
|
// vt = H_3(s_sr || input_context || Ko)
|
|
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);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
|
|
const input_context_t &input_context,
|
|
const crypto::public_key &onetime_address,
|
|
crypto::secret_key &return_privkey_out)
|
|
{
|
|
// k_return = H_32(s_sr || input_context || Ko)
|
|
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR>(input_context, onetime_address);
|
|
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_privkey_out);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
|
|
const input_context_t &input_context,
|
|
const crypto::public_key &onetime_address,
|
|
const uint64_t idx,
|
|
crypto::secret_key &return_index_out)
|
|
{
|
|
// k_idx = H_32(s_sr || input_context || Ko || idx)
|
|
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR>(input_context, onetime_address, idx);
|
|
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_index_out);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_receiver_unctx[32],
|
|
const input_context_t &input_context,
|
|
const crypto::public_key &onetime_address,
|
|
encrypted_return_pubkey_t &return_pubkey_mask_out)
|
|
{
|
|
// m_return = H_32(s_sr || input_context || Ko)
|
|
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK>(input_context, onetime_address);
|
|
derive_bytes_32(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_pubkey_mask_out);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
|
|
const input_context_t &input_context,
|
|
const view_balance_secret_device *s_view_balance_dev,
|
|
const crypto::public_key &onetime_address,
|
|
const uint64_t idx,
|
|
encrypted_return_pubkey_t &return_pubkey_out)
|
|
{
|
|
// compute k_return
|
|
crypto::secret_key k_return;
|
|
s_view_balance_dev->make_internal_return_privkey(input_context, onetime_address, k_return);
|
|
|
|
// compute k_idx
|
|
crypto::secret_key k_idx;
|
|
make_sparc_return_index(s_sender_receiver_unctx, input_context, onetime_address, idx, k_idx);
|
|
|
|
// compute m_return
|
|
encrypted_return_pubkey_t m_return;
|
|
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx,
|
|
input_context,
|
|
onetime_address,
|
|
m_return);
|
|
|
|
#if 1
|
|
// compute SPARC K_return = k_return * G
|
|
crypto::public_key K_return;
|
|
crypto::secret_key_to_public_key(k_return, K_return);
|
|
|
|
// compute return_enc
|
|
encrypted_return_pubkey_t return_pub;
|
|
static_assert(sizeof(K_return.data) == sizeof(return_pub.bytes), "Size mismatch");
|
|
memcpy(return_pub.bytes, K_return.data, sizeof(encrypted_return_pubkey_t));
|
|
#else
|
|
// compute SPARC K_return = k_return * G + k_idx * T
|
|
rct::key K_return;
|
|
rct::addKeys2(K_return,
|
|
rct::sk2rct(k_return),
|
|
rct::sk2rct(k_idx),
|
|
rct::pk2rct(crypto::get_T()));
|
|
|
|
// compute return_enc
|
|
encrypted_return_pubkey_t return_pub;
|
|
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.bytes), "Size mismatch");
|
|
memcpy(return_pub.bytes, K_return.bytes, sizeof(encrypted_return_pubkey_t));
|
|
#endif
|
|
return_pubkey_out = return_pub ^ m_return;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
input_context_t make_carrot_input_context_coinbase(const std::uint64_t block_index)
|
|
{
|
|
// input_context = "C" || IntToBytes256(block_index)
|
|
input_context_t input_context{};
|
|
input_context.bytes[0] = CARROT_DOMAIN_SEP_INPUT_CONTEXT_COINBASE;
|
|
memcpy_swap64le(input_context.bytes + 1, &block_index, 1);
|
|
return input_context;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
input_context_t make_carrot_input_context(const crypto::key_image &first_rct_key_image)
|
|
{
|
|
// input_context = "R" || KI_1
|
|
input_context_t input_context{};
|
|
input_context.bytes[0] = CARROT_DOMAIN_SEP_INPUT_CONTEXT_RINGCT;
|
|
memcpy(input_context.bytes + 1, first_rct_key_image.data, sizeof(crypto::key_image));
|
|
return input_context;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_carrot_sender_receiver_secret(const unsigned char s_sender_receiver_unctx[32],
|
|
const mx25519_pubkey &enote_ephemeral_pubkey,
|
|
const input_context_t &input_context,
|
|
crypto::hash &s_sender_receiver_out)
|
|
{
|
|
// s^ctx_sr = H_32(s_sr, D_e, input_context)
|
|
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_SENDER_RECEIVER_SECRET>(
|
|
enote_ephemeral_pubkey, input_context);
|
|
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,
|
|
const rct::key &amount_commitment,
|
|
crypto::secret_key &sender_extension_out)
|
|
{
|
|
// 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);
|
|
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,
|
|
const rct::key &amount_commitment,
|
|
crypto::secret_key &sender_extension_out)
|
|
{
|
|
// 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);
|
|
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,
|
|
const rct::key &amount_commitment,
|
|
crypto::public_key &sender_extension_pubkey_out)
|
|
{
|
|
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
|
|
crypto::secret_key sender_extension_g;
|
|
make_carrot_onetime_address_extension_g(s_sender_receiver, amount_commitment, sender_extension_g);
|
|
|
|
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
|
|
crypto::secret_key sender_extension_t;
|
|
make_carrot_onetime_address_extension_t(s_sender_receiver, amount_commitment, sender_extension_t);
|
|
|
|
// K^o_ext = k^o_g G + k^o_t T
|
|
rct::key sender_extension_pubkey_tmp;
|
|
rct::addKeys2(sender_extension_pubkey_tmp,
|
|
rct::sk2rct(sender_extension_g),
|
|
rct::sk2rct(sender_extension_t),
|
|
rct::pk2rct(crypto::get_T()));
|
|
|
|
sender_extension_pubkey_out = rct::rct2pk(sender_extension_pubkey_tmp);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_carrot_onetime_address(const crypto::public_key &address_spend_pubkey,
|
|
const crypto::hash &s_sender_receiver,
|
|
const rct::key &amount_commitment,
|
|
crypto::public_key &onetime_address_out)
|
|
{
|
|
// K^o_ext = k^o_g G + k^o_t T
|
|
crypto::public_key sender_extension_pubkey;
|
|
make_carrot_onetime_address_extension_pubkey(s_sender_receiver, amount_commitment, sender_extension_pubkey);
|
|
|
|
// Ko = K^j_s + K^o_ext
|
|
onetime_address_out = rct::rct2pk(rct::addKeys(
|
|
rct::pk2rct(address_spend_pubkey), rct::pk2rct(sender_extension_pubkey)));
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_carrot_amount_blinding_factor(const crypto::hash &s_sender_receiver,
|
|
const rct::xmr_amount amount,
|
|
const crypto::public_key &address_spend_pubkey,
|
|
const CarrotEnoteType enote_type,
|
|
crypto::secret_key &amount_blinding_factor_out)
|
|
{
|
|
// k_a = H_n(s^ctx_sr, a, K^j_s, enote_type)
|
|
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR>(
|
|
amount, address_spend_pubkey, static_cast<unsigned char>(enote_type));
|
|
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,
|
|
const crypto::public_key &onetime_address,
|
|
encrypted_janus_anchor_t &anchor_encryption_mask_out)
|
|
{
|
|
// m_anchor = H_16(s^ctx_sr, Ko)
|
|
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);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
encrypted_janus_anchor_t encrypt_carrot_anchor(const janus_anchor_t &anchor,
|
|
const crypto::hash &s_sender_receiver,
|
|
const crypto::public_key &onetime_address)
|
|
{
|
|
// m_anchor = H_16(s^ctx_sr, Ko)
|
|
encrypted_janus_anchor_t mask;
|
|
make_carrot_anchor_encryption_mask(s_sender_receiver, onetime_address, mask);
|
|
|
|
// anchor_enc = anchor XOR m_anchor
|
|
return anchor ^ mask;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
janus_anchor_t decrypt_carrot_anchor(const encrypted_janus_anchor_t &encrypted_anchor,
|
|
const crypto::hash &s_sender_receiver,
|
|
const crypto::public_key &onetime_address)
|
|
{
|
|
// m_anchor = H_16(s^ctx_sr, Ko)
|
|
encrypted_janus_anchor_t mask;
|
|
make_carrot_anchor_encryption_mask(s_sender_receiver, onetime_address, mask);
|
|
|
|
// anchor = anchor_enc XOR m_anchor
|
|
return encrypted_anchor ^ mask;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_carrot_amount_encryption_mask(const crypto::hash &s_sender_receiver,
|
|
const crypto::public_key &onetime_address,
|
|
encrypted_amount_t &amount_encryption_mask_out)
|
|
{
|
|
// m_a = H_8(s^ctx_sr, Ko)
|
|
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);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
encrypted_amount_t encrypt_carrot_amount(const rct::xmr_amount amount,
|
|
const crypto::hash &s_sender_receiver,
|
|
const crypto::public_key &onetime_address)
|
|
{
|
|
// m_a = H_8(s^ctx_sr, Ko)
|
|
encrypted_amount_t mask;
|
|
make_carrot_amount_encryption_mask(s_sender_receiver, onetime_address, mask);
|
|
|
|
// a_enc = a XOR m_a [paying attention to system endianness]
|
|
return enc_amount(amount, mask);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
rct::xmr_amount decrypt_carrot_amount(const encrypted_amount_t encrypted_amount,
|
|
const crypto::hash &s_sender_receiver,
|
|
const crypto::public_key &onetime_address)
|
|
{
|
|
// m_a = H_8(s^ctx_sr, Ko)
|
|
encrypted_amount_t mask;
|
|
make_carrot_amount_encryption_mask(s_sender_receiver, onetime_address, mask);
|
|
|
|
// a = a_enc XOR m_a [paying attention to system endianness]
|
|
return dec_amount(encrypted_amount, mask);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_carrot_payment_id_encryption_mask(const crypto::hash &s_sender_receiver,
|
|
const crypto::public_key &onetime_address,
|
|
encrypted_payment_id_t &payment_id_encryption_mask_out)
|
|
{
|
|
// m_pid = H_8(s^ctx_sr, Ko)
|
|
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);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
encrypted_payment_id_t encrypt_legacy_payment_id(const payment_id_t payment_id,
|
|
const crypto::hash &s_sender_receiver,
|
|
const crypto::public_key &onetime_address)
|
|
{
|
|
// m_pid = H_8(s^ctx_sr, Ko)
|
|
encrypted_payment_id_t mask;
|
|
make_carrot_payment_id_encryption_mask(s_sender_receiver, onetime_address, mask);
|
|
|
|
// pid_enc = pid XOR m_pid
|
|
return convert_payment_id(payment_id) ^ mask;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
payment_id_t decrypt_legacy_payment_id(const encrypted_payment_id_t encrypted_payment_id,
|
|
const crypto::hash &s_sender_receiver,
|
|
const crypto::public_key &onetime_address)
|
|
{
|
|
// m_pid = H_8(s^ctx_sr, Ko)
|
|
encrypted_payment_id_t mask;
|
|
make_carrot_payment_id_encryption_mask(s_sender_receiver, onetime_address, mask);
|
|
|
|
// pid = pid_enc XOR m_pid
|
|
return convert_payment_id(encrypted_payment_id ^ mask);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void make_carrot_janus_anchor_special(const mx25519_pubkey &enote_ephemeral_pubkey,
|
|
const input_context_t &input_context,
|
|
const crypto::public_key &onetime_address,
|
|
const crypto::secret_key &k_view,
|
|
janus_anchor_t &anchor_special_out)
|
|
{
|
|
// anchor_sp = H_16(D_e, input_context, Ko, k_v)
|
|
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_JANUS_ANCHOR_SPECIAL>(
|
|
enote_ephemeral_pubkey, input_context, onetime_address);
|
|
derive_bytes_16(transcript.data(), transcript.size(), &k_view, &anchor_special_out);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
void recover_address_spend_pubkey(const crypto::public_key &onetime_address,
|
|
const crypto::hash &s_sender_receiver,
|
|
const rct::key &amount_commitment,
|
|
crypto::public_key &address_spend_key_out)
|
|
{
|
|
// K^o_ext = k^o_g G + k^o_t T
|
|
crypto::public_key sender_extension_pubkey;
|
|
make_carrot_onetime_address_extension_pubkey(s_sender_receiver, amount_commitment, sender_extension_pubkey);
|
|
|
|
// K^j_s = Ko - K^o_ext
|
|
rct::key res_tmp;
|
|
rct::subKeys(res_tmp, rct::pk2rct(onetime_address), rct::pk2rct(sender_extension_pubkey));
|
|
address_spend_key_out = rct::rct2pk(res_tmp);
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
|
|
const input_context_t input_context,
|
|
const crypto::public_key &onetime_address,
|
|
const view_tag_t view_tag)
|
|
{
|
|
// vt' = H_3(s_sr || input_context || Ko)
|
|
view_tag_t nominal_view_tag;
|
|
make_carrot_view_tag(s_sender_receiver_unctx, input_context, onetime_address, nominal_view_tag);
|
|
// vt' ?= vt
|
|
return nominal_view_tag == view_tag;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
bool try_recompute_carrot_amount_commitment(const crypto::hash &s_sender_receiver,
|
|
const rct::xmr_amount nominal_amount,
|
|
const crypto::public_key &nominal_address_spend_pubkey,
|
|
const CarrotEnoteType nominal_enote_type,
|
|
const rct::key &amount_commitment,
|
|
crypto::secret_key &amount_blinding_factor_out)
|
|
{
|
|
// k_a' = H_n(s^ctx_sr, a', K^j_s', enote_type')
|
|
make_carrot_amount_blinding_factor(s_sender_receiver,
|
|
nominal_amount,
|
|
nominal_address_spend_pubkey,
|
|
nominal_enote_type,
|
|
amount_blinding_factor_out);
|
|
|
|
// C_a' = k_a' G + a' H
|
|
const rct::key nominal_amount_commitment = rct::commit(nominal_amount, rct::sk2rct(amount_blinding_factor_out));
|
|
|
|
// C_a' ?= C_a
|
|
return nominal_amount_commitment == amount_commitment;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
|
|
const encrypted_amount_t &encrypted_amount,
|
|
const crypto::public_key &onetime_address,
|
|
const crypto::public_key &address_spend_pubkey,
|
|
const rct::key &amount_commitment,
|
|
CarrotEnoteType &enote_type_out,
|
|
rct::xmr_amount &amount_out,
|
|
crypto::secret_key &amount_blinding_factor_out)
|
|
{
|
|
// a' = a_enc XOR m_a
|
|
amount_out = decrypt_carrot_amount(encrypted_amount, s_sender_receiver, onetime_address);
|
|
|
|
// set enote_type <- "payment"
|
|
enote_type_out = CarrotEnoteType::PAYMENT;
|
|
|
|
// if C_a ?= k_a' G + a' H, then PASS
|
|
if (try_recompute_carrot_amount_commitment(s_sender_receiver,
|
|
amount_out,
|
|
address_spend_pubkey,
|
|
enote_type_out,
|
|
amount_commitment,
|
|
amount_blinding_factor_out))
|
|
return true;
|
|
|
|
// set enote_type <- "change"
|
|
enote_type_out = CarrotEnoteType::CHANGE;
|
|
|
|
// if C_a ?= k_a' G + a' H, then PASS
|
|
if (try_recompute_carrot_amount_commitment(s_sender_receiver,
|
|
amount_out,
|
|
address_spend_pubkey,
|
|
enote_type_out,
|
|
amount_commitment,
|
|
amount_blinding_factor_out))
|
|
return true;
|
|
|
|
// neither attempt at recomputing passed: so FAIL
|
|
return false;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
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 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))
|
|
crypto::secret_key nominal_enote_ephemeral_privkey;
|
|
make_carrot_enote_ephemeral_privkey(nominal_anchor,
|
|
input_context,
|
|
nominal_address_spend_pubkey,
|
|
nominal_payment_id,
|
|
nominal_enote_ephemeral_privkey);
|
|
|
|
// 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);
|
|
|
|
// D_e' ?= D_e
|
|
return 0 == memcmp(&nominal_enote_ephemeral_pubkey, &enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
} //namespace carrot
|