mirror of
https://github.com/monero-project/monero.git
synced 2024-12-04 23:51:08 +02:00
Merge d8d07334ce
into 893916ad09
This commit is contained in:
commit
8b3dbb3920
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -14,3 +14,6 @@
|
|||||||
path = external/supercop
|
path = external/supercop
|
||||||
url = https://github.com/monero-project/supercop
|
url = https://github.com/monero-project/supercop
|
||||||
branch = monero
|
branch = monero
|
||||||
|
[submodule "external/mx25519"]
|
||||||
|
path = external/mx25519
|
||||||
|
url = https://github.com/tevador/mx25519
|
||||||
|
@ -377,6 +377,7 @@ if(NOT MANUAL_SUBMODULES)
|
|||||||
check_submodule(external/trezor-common)
|
check_submodule(external/trezor-common)
|
||||||
check_submodule(external/randomx)
|
check_submodule(external/randomx)
|
||||||
check_submodule(external/supercop)
|
check_submodule(external/supercop)
|
||||||
|
check_submodule(external/mx25519)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -463,7 +464,7 @@ elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
|
|||||||
set(BSDI TRUE)
|
set(BSDI TRUE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include)
|
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
cmake_policy(SET CMP0042 NEW)
|
cmake_policy(SET CMP0042 NEW)
|
||||||
|
1
external/CMakeLists.txt
vendored
1
external/CMakeLists.txt
vendored
@ -70,3 +70,4 @@ add_subdirectory(db_drivers)
|
|||||||
add_subdirectory(easylogging++)
|
add_subdirectory(easylogging++)
|
||||||
add_subdirectory(qrcodegen)
|
add_subdirectory(qrcodegen)
|
||||||
add_subdirectory(randomx EXCLUDE_FROM_ALL)
|
add_subdirectory(randomx EXCLUDE_FROM_ALL)
|
||||||
|
add_subdirectory(mx25519)
|
||||||
|
1
external/mx25519
vendored
Submodule
1
external/mx25519
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 84ca1290fa344351c95692f20f41a174b70e0c7b
|
@ -83,6 +83,7 @@ endfunction ()
|
|||||||
include(Version)
|
include(Version)
|
||||||
monero_add_library(version SOURCES ${CMAKE_BINARY_DIR}/version.cpp DEPENDS genversion)
|
monero_add_library(version SOURCES ${CMAKE_BINARY_DIR}/version.cpp DEPENDS genversion)
|
||||||
|
|
||||||
|
add_subdirectory(carrot_core)
|
||||||
add_subdirectory(common)
|
add_subdirectory(common)
|
||||||
add_subdirectory(crypto)
|
add_subdirectory(crypto)
|
||||||
add_subdirectory(ringct)
|
add_subdirectory(ringct)
|
||||||
|
59
src/carrot_core/CMakeLists.txt
Normal file
59
src/carrot_core/CMakeLists.txt
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
set(carrot_core_sources
|
||||||
|
account_secrets.cpp
|
||||||
|
address_utils.cpp
|
||||||
|
carrot_enote_scan.cpp
|
||||||
|
core_types.cpp
|
||||||
|
destination.cpp
|
||||||
|
device_ram_borrowed.cpp
|
||||||
|
enote_utils.cpp
|
||||||
|
hash_functions.cpp
|
||||||
|
payment_proposal.cpp)
|
||||||
|
|
||||||
|
monero_find_all_headers(carrot_core_headers, "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
|
||||||
|
monero_add_library(carrot_core
|
||||||
|
${carrot_core_sources}
|
||||||
|
${carrot_core_headers})
|
||||||
|
|
||||||
|
target_link_libraries(carrot_core
|
||||||
|
PUBLIC
|
||||||
|
cncrypto
|
||||||
|
epee
|
||||||
|
ringct
|
||||||
|
seraphis_crypto
|
||||||
|
PRIVATE
|
||||||
|
${EXTRA_LIBRARIES})
|
||||||
|
|
||||||
|
target_include_directories(carrot_core
|
||||||
|
PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
PRIVATE
|
||||||
|
${Boost_INCLUDE_DIRS})
|
102
src/carrot_core/account_secrets.cpp
Normal file
102
src/carrot_core/account_secrets.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// 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 "account_secrets.h"
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "config.h"
|
||||||
|
#include "crypto/generators.h"
|
||||||
|
#include "hash_functions.h"
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
#include "transcript_fixed.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_provespend_key(const crypto::secret_key &s_master,
|
||||||
|
crypto::secret_key &k_prove_spend_out)
|
||||||
|
{
|
||||||
|
// k_ps = H_n(s_m)
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_viewbalance_secret(const crypto::secret_key &s_master,
|
||||||
|
crypto::secret_key &s_view_balance_out)
|
||||||
|
{
|
||||||
|
// s_vb = H_32(s_m)
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance,
|
||||||
|
crypto::secret_key &k_generate_image_out)
|
||||||
|
{
|
||||||
|
// k_gi = H_n(s_vb)
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance,
|
||||||
|
crypto::secret_key &k_view_out)
|
||||||
|
{
|
||||||
|
// k_v = H_n(s_vb)
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance,
|
||||||
|
crypto::secret_key &s_generate_address_out)
|
||||||
|
{
|
||||||
|
// s_ga = H_32(s_vb)
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_spend_pubkey(const crypto::secret_key &k_generate_image,
|
||||||
|
const crypto::secret_key &k_prove_spend,
|
||||||
|
crypto::public_key &spend_pubkey_out)
|
||||||
|
{
|
||||||
|
// k_ps T
|
||||||
|
rct::key tmp;
|
||||||
|
rct::scalarmultKey(tmp, rct::pk2rct(crypto::get_T()), rct::sk2rct(k_prove_spend));
|
||||||
|
|
||||||
|
// K_s = k_gi G + k_ps T
|
||||||
|
rct::addKeys1(tmp, rct::sk2rct(k_generate_image), tmp);
|
||||||
|
spend_pubkey_out = rct::rct2pk(tmp);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace carrot
|
103
src/carrot_core/account_secrets.h
Normal file
103
src/carrot_core/account_secrets.h
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
////
|
||||||
|
// Core implementation details for making Carrot privkeys, secrets, and pubkeys.
|
||||||
|
// - Carrot is a specification for FCMP-RingCT compatible addressing
|
||||||
|
//
|
||||||
|
// references:
|
||||||
|
// * https://github.com/jeffro256/carrot/blob/master/carrot.md
|
||||||
|
///
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_provespend_key - prove-spend key, for signing input proofs to spend enotes
|
||||||
|
* k_ps = H_n(s_m)
|
||||||
|
* param: s_master - s_m
|
||||||
|
* outparam: k_prove_spend_out - k_ps
|
||||||
|
*/
|
||||||
|
void make_carrot_provespend_key(const crypto::secret_key &s_master,
|
||||||
|
crypto::secret_key &k_prove_spend_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_viewbalance_secret - view-balance secret, for viewing all balance information
|
||||||
|
* s_vb = H_n(s_m)
|
||||||
|
* param: s_master - s_m
|
||||||
|
* outparam: s_view_balance_out - s_vb
|
||||||
|
*/
|
||||||
|
void make_carrot_viewbalance_secret(const crypto::secret_key &s_master,
|
||||||
|
crypto::secret_key &s_view_balance_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_generateimage_key - generate-image key, for identifying enote spends
|
||||||
|
* k_gi = H_n(s_vb)
|
||||||
|
* param: s_view_balance - s_vb
|
||||||
|
* outparam: k_generate_image_out - k_gi
|
||||||
|
*/
|
||||||
|
void make_carrot_generateimage_key(const crypto::secret_key &s_view_balance,
|
||||||
|
crypto::secret_key &k_generate_image_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_viewincoming_key - view-incoming key, for identifying received external enotes
|
||||||
|
* k_v = H_n(s_vb)
|
||||||
|
* param: s_view_balance - s_vb
|
||||||
|
* outparam: k_view_out - k_v
|
||||||
|
*/
|
||||||
|
void make_carrot_viewincoming_key(const crypto::secret_key &s_view_balance,
|
||||||
|
crypto::secret_key &k_view_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_generateaddress_secret - generate-address secret, for generating addresses
|
||||||
|
* s_ga = H_32(s_vb)
|
||||||
|
* param: s_view_balance - s_vb
|
||||||
|
* outparam: s_generate_address_out - s_ga
|
||||||
|
*/
|
||||||
|
void make_carrot_generateaddress_secret(const crypto::secret_key &s_view_balance,
|
||||||
|
crypto::secret_key &s_generate_address_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_spend_pubkey - base public spendkey for rerandomizable RingCT
|
||||||
|
* K_s = k_gi G + k_ps T
|
||||||
|
* param: k_generate_image - k_gi
|
||||||
|
* param: k_prove_spend - k_ps
|
||||||
|
* outparam: spend_pubkey_out - K_s
|
||||||
|
*/
|
||||||
|
void make_carrot_spend_pubkey(const crypto::secret_key &k_generate_image,
|
||||||
|
const crypto::secret_key &k_prove_spend,
|
||||||
|
crypto::public_key &spend_pubkey_out);
|
||||||
|
|
||||||
|
} //namespace carrot
|
85
src/carrot_core/address_utils.cpp
Normal file
85
src/carrot_core/address_utils.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// 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 "address_utils.h"
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "config.h"
|
||||||
|
#include "hash_functions.h"
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
#include "transcript_fixed.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_index_extension_generator(const crypto::secret_key &s_generate_address,
|
||||||
|
const std::uint32_t j_major,
|
||||||
|
const std::uint32_t j_minor,
|
||||||
|
crypto::secret_key &address_generator_out)
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
derive_bytes_32(transcript.data(), transcript.size(), &s_generate_address, &address_generator_out);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_subaddress_scalar(const crypto::public_key &spend_pubkey,
|
||||||
|
const crypto::secret_key &s_address_generator,
|
||||||
|
const std::uint32_t j_major,
|
||||||
|
const std::uint32_t j_minor,
|
||||||
|
crypto::secret_key &subaddress_scalar_out)
|
||||||
|
{
|
||||||
|
// 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>(
|
||||||
|
spend_pubkey, j_major, j_minor);
|
||||||
|
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,
|
||||||
|
const crypto::secret_key &s_generate_address,
|
||||||
|
const std::uint32_t j_major,
|
||||||
|
const std::uint32_t j_minor,
|
||||||
|
crypto::public_key &address_spend_pubkey_out)
|
||||||
|
{
|
||||||
|
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||||
|
crypto::secret_key subaddress_scalar;
|
||||||
|
make_carrot_subaddress_scalar(spend_pubkey, s_generate_address, j_major, j_minor, subaddress_scalar);
|
||||||
|
|
||||||
|
// K^j_s = k^j_subscal * K_s
|
||||||
|
address_spend_pubkey_out = rct::rct2pk(rct::scalarmultKey(
|
||||||
|
rct::pk2rct(spend_pubkey), rct::sk2rct(subaddress_scalar)));
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace carrot
|
95
src/carrot_core/address_utils.h
Normal file
95
src/carrot_core/address_utils.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Utilities for building carrot addresses.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: is_main_address_index - determine whether j=(j_major, j_minor) represents the main address
|
||||||
|
*/
|
||||||
|
static constexpr bool is_main_address_index(const std::uint32_t j_major, const std::uint32_t j_minor)
|
||||||
|
{
|
||||||
|
return !(j_major || j_minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_index_extension_generator - s^j_gen
|
||||||
|
* s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||||
|
* param: s_generate_address - s_ga
|
||||||
|
* param: j_major -
|
||||||
|
* param: j_minor -
|
||||||
|
* outparam: address_generator_out - s^j_gen
|
||||||
|
*/
|
||||||
|
void make_carrot_index_extension_generator(const crypto::secret_key &s_generate_address,
|
||||||
|
const std::uint32_t j_major,
|
||||||
|
const std::uint32_t j_minor,
|
||||||
|
crypto::secret_key &address_generator_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_address_privkey - d^j_a
|
||||||
|
* k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||||
|
* param: spend_pubkey - K_s = k_vb X + k_m U
|
||||||
|
* param: s_address_generator - s^j_gen
|
||||||
|
* param: j_major -
|
||||||
|
* param: j_minor -
|
||||||
|
* outparam: subaddress_scalar_out - k^j_subscal
|
||||||
|
*/
|
||||||
|
void make_carrot_subaddress_scalar(const crypto::public_key &spend_pubkey,
|
||||||
|
const crypto::secret_key &s_address_generator,
|
||||||
|
const std::uint32_t j_major,
|
||||||
|
const std::uint32_t j_minor,
|
||||||
|
crypto::secret_key &subaddress_scalar_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_address_spend_pubkey - K^j_s
|
||||||
|
* K^j_s = k^j_subscal * K_s
|
||||||
|
* param: spend_pubkey - K_s = k_gi G + k_ps U
|
||||||
|
* param: s_generate_address - s_ga
|
||||||
|
* param: j_major -
|
||||||
|
* param: j_minor -
|
||||||
|
* outparam: address_spend_pubkey_out - K^j_s
|
||||||
|
*/
|
||||||
|
void make_carrot_address_spend_pubkey(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,
|
||||||
|
crypto::public_key &address_spend_pubkey_out);
|
||||||
|
|
||||||
|
} //namespace carrot
|
333
src/carrot_core/carrot_enote_scan.cpp
Normal file
333
src/carrot_core/carrot_enote_scan.cpp
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Utilities for scanning carrot enotes
|
||||||
|
|
||||||
|
//paired header
|
||||||
|
#include "carrot_enote_scan.h"
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "crypto/generators.h"
|
||||||
|
#include "enote_utils.h"
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
static bool try_scan_carrot_non_coinbase_core(const CarrotEnoteV1 &enote,
|
||||||
|
const std::optional<encrypted_payment_id_t> encrypted_payment_id,
|
||||||
|
const crypto::hash &s_sender_receiver,
|
||||||
|
crypto::secret_key &sender_extension_g_out,
|
||||||
|
crypto::secret_key &sender_extension_t_out,
|
||||||
|
crypto::public_key &address_spend_pubkey_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out,
|
||||||
|
payment_id_t &payment_id_out,
|
||||||
|
CarrotEnoteType &enote_type_out,
|
||||||
|
janus_anchor_t &nominal_janus_anchor_out)
|
||||||
|
{
|
||||||
|
// if cannot recompute C_a, then FAIL
|
||||||
|
if (!try_get_carrot_amount(s_sender_receiver,
|
||||||
|
enote.amount_enc,
|
||||||
|
enote.onetime_address,
|
||||||
|
enote.amount_commitment,
|
||||||
|
enote_type_out,
|
||||||
|
amount_out,
|
||||||
|
amount_blinding_factor_out))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
|
||||||
|
make_carrot_onetime_address_extension_g(s_sender_receiver,
|
||||||
|
enote.amount_commitment,
|
||||||
|
sender_extension_g_out);
|
||||||
|
|
||||||
|
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
|
||||||
|
make_carrot_onetime_address_extension_t(s_sender_receiver,
|
||||||
|
enote.amount_commitment,
|
||||||
|
sender_extension_t_out);
|
||||||
|
|
||||||
|
// K^j_s = Ko - K^o_ext = Ko - (k^o_g G + k^o_t T)
|
||||||
|
recover_address_spend_pubkey(enote.onetime_address,
|
||||||
|
s_sender_receiver,
|
||||||
|
enote.amount_commitment,
|
||||||
|
address_spend_pubkey_out);
|
||||||
|
|
||||||
|
// pid = pid_enc XOR m_pid, if applicable
|
||||||
|
if (encrypted_payment_id)
|
||||||
|
payment_id_out = decrypt_legacy_payment_id(*encrypted_payment_id, s_sender_receiver, enote.onetime_address);
|
||||||
|
else
|
||||||
|
payment_id_out = null_payment_id;
|
||||||
|
|
||||||
|
// anchor = anchor_enc XOR m_anchor
|
||||||
|
nominal_janus_anchor_out = decrypt_carrot_anchor(enote.anchor_enc,
|
||||||
|
s_sender_receiver,
|
||||||
|
enote.onetime_address);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
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,
|
||||||
|
const crypto::x25519_pubkey &s_sender_receiver_unctx,
|
||||||
|
const view_incoming_key_device &k_view_dev,
|
||||||
|
const crypto::public_key &account_spend_pubkey,
|
||||||
|
crypto::secret_key &sender_extension_g_out,
|
||||||
|
crypto::secret_key &sender_extension_t_out,
|
||||||
|
crypto::public_key &address_spend_pubkey_out)
|
||||||
|
{
|
||||||
|
// input_context
|
||||||
|
input_context_t input_context;
|
||||||
|
make_carrot_input_context_coinbase(enote.block_index, input_context);
|
||||||
|
|
||||||
|
// if vt' != vt, then FAIL
|
||||||
|
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);
|
||||||
|
|
||||||
|
// C_a = G + a H
|
||||||
|
const rct::key implied_amount_commitment = rct::zeroCommit(enote.amount);
|
||||||
|
|
||||||
|
// k^o_g = H_n("..g..", s^ctx_sr, C_a)
|
||||||
|
make_carrot_onetime_address_extension_g(s_sender_receiver,
|
||||||
|
implied_amount_commitment,
|
||||||
|
sender_extension_g_out);
|
||||||
|
|
||||||
|
// k^o_t = H_n("..t..", s^ctx_sr, C_a)
|
||||||
|
make_carrot_onetime_address_extension_t(s_sender_receiver,
|
||||||
|
implied_amount_commitment,
|
||||||
|
sender_extension_t_out);
|
||||||
|
|
||||||
|
// K^j_s = Ko - K^o_ext = Ko - (k^o_g G + k^o_t T)
|
||||||
|
recover_address_spend_pubkey(enote.onetime_address,
|
||||||
|
s_sender_receiver,
|
||||||
|
implied_amount_commitment,
|
||||||
|
address_spend_pubkey_out);
|
||||||
|
|
||||||
|
// if K^j_s != K^s, then FAIL
|
||||||
|
// - We have no "hard target" in the amount commitment, so if we want deterministic enote
|
||||||
|
// scanning without a subaddress table, we reject all non-main addresses in coinbase enotes
|
||||||
|
if (address_spend_pubkey_out != account_spend_pubkey)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// anchor = anchor_enc XOR m_anchor
|
||||||
|
const janus_anchor_t nominal_anchor = decrypt_carrot_anchor(enote.anchor_enc,
|
||||||
|
s_sender_receiver,
|
||||||
|
enote.onetime_address);
|
||||||
|
|
||||||
|
// verify Janus attack protection
|
||||||
|
payment_id_t dummy_payment_id = null_payment_id;
|
||||||
|
if (!verify_carrot_janus_protection(input_context,
|
||||||
|
enote.onetime_address,
|
||||||
|
k_view_dev,
|
||||||
|
account_spend_pubkey,
|
||||||
|
address_spend_pubkey_out,
|
||||||
|
enote.enote_ephemeral_pubkey,
|
||||||
|
nominal_anchor,
|
||||||
|
dummy_payment_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote,
|
||||||
|
const std::optional<encrypted_payment_id_t> encrypted_payment_id,
|
||||||
|
const crypto::x25519_pubkey &s_sender_receiver_unctx,
|
||||||
|
const view_incoming_key_device &k_view_dev,
|
||||||
|
const crypto::public_key &account_spend_pubkey,
|
||||||
|
crypto::secret_key &sender_extension_g_out,
|
||||||
|
crypto::secret_key &sender_extension_t_out,
|
||||||
|
crypto::public_key &address_spend_pubkey_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out,
|
||||||
|
payment_id_t &payment_id_out,
|
||||||
|
CarrotEnoteType &enote_type_out)
|
||||||
|
{
|
||||||
|
// input_context
|
||||||
|
input_context_t 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
|
||||||
|
janus_anchor_t nominal_anchor;
|
||||||
|
if (!try_scan_carrot_non_coinbase_core(enote,
|
||||||
|
encrypted_payment_id,
|
||||||
|
s_sender_receiver,
|
||||||
|
sender_extension_g_out,
|
||||||
|
sender_extension_t_out,
|
||||||
|
address_spend_pubkey_out,
|
||||||
|
amount_out,
|
||||||
|
amount_blinding_factor_out,
|
||||||
|
payment_id_out,
|
||||||
|
enote_type_out,
|
||||||
|
nominal_anchor))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// verify Janus attack protection
|
||||||
|
if (!verify_carrot_janus_protection(input_context,
|
||||||
|
enote.onetime_address,
|
||||||
|
k_view_dev,
|
||||||
|
account_spend_pubkey,
|
||||||
|
address_spend_pubkey_out,
|
||||||
|
enote.enote_ephemeral_pubkey,
|
||||||
|
nominal_anchor,
|
||||||
|
payment_id_out))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool try_scan_carrot_enote_internal(const CarrotEnoteV1 &enote,
|
||||||
|
const view_balance_secret_device &s_view_balance_dev,
|
||||||
|
crypto::secret_key &sender_extension_g_out,
|
||||||
|
crypto::secret_key &sender_extension_t_out,
|
||||||
|
crypto::public_key &address_spend_pubkey_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out,
|
||||||
|
CarrotEnoteType &enote_type_out)
|
||||||
|
{
|
||||||
|
// input_context
|
||||||
|
input_context_t 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
|
||||||
|
janus_anchor_t nominal_anchor;
|
||||||
|
payment_id_t dummy_payment_id;
|
||||||
|
if (!try_scan_carrot_non_coinbase_core(enote,
|
||||||
|
std::nullopt,
|
||||||
|
s_sender_receiver,
|
||||||
|
sender_extension_g_out,
|
||||||
|
sender_extension_t_out,
|
||||||
|
address_spend_pubkey_out,
|
||||||
|
amount_out,
|
||||||
|
amount_blinding_factor_out,
|
||||||
|
dummy_payment_id,
|
||||||
|
enote_type_out,
|
||||||
|
nominal_anchor))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// janus protection checks are not needed for internal scans
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace carrot
|
86
src/carrot_core/carrot_enote_scan.h
Normal file
86
src/carrot_core/carrot_enote_scan.h
Normal 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 Utilities for scanning carrot enotes
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "carrot_enote_types.h"
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
const crypto::x25519_pubkey &s_sender_receiver_unctx,
|
||||||
|
const view_incoming_key_device &k_view_dev,
|
||||||
|
const crypto::public_key &account_spend_pubkey,
|
||||||
|
crypto::secret_key &sender_extension_g_out,
|
||||||
|
crypto::secret_key &sender_extension_t_out,
|
||||||
|
crypto::public_key &address_spend_pubkey_out);
|
||||||
|
|
||||||
|
bool try_scan_carrot_enote_external(const CarrotEnoteV1 &enote,
|
||||||
|
const std::optional<encrypted_payment_id_t> encrypted_payment_id,
|
||||||
|
const crypto::x25519_pubkey &s_sender_receiver_unctx,
|
||||||
|
const view_incoming_key_device &k_view_dev,
|
||||||
|
const crypto::public_key &account_spend_pubkey,
|
||||||
|
crypto::secret_key &sender_extension_g_out,
|
||||||
|
crypto::secret_key &sender_extension_t_out,
|
||||||
|
crypto::public_key &address_spend_pubkey_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out,
|
||||||
|
payment_id_t &payment_id_out,
|
||||||
|
CarrotEnoteType &enote_type_out);
|
||||||
|
|
||||||
|
bool try_scan_carrot_enote_internal(const CarrotEnoteV1 &enote,
|
||||||
|
const view_balance_secret_device &s_view_balance_dev,
|
||||||
|
crypto::secret_key &sender_extension_g_out,
|
||||||
|
crypto::secret_key &sender_extension_t_out,
|
||||||
|
crypto::public_key &address_spend_pubkey_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out,
|
||||||
|
CarrotEnoteType &enote_type_out);
|
||||||
|
|
||||||
|
} //namespace carrot
|
106
src/carrot_core/carrot_enote_types.h
Normal file
106
src/carrot_core/carrot_enote_types.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Seraphis core types.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "crypto/x25519.h"
|
||||||
|
#include "core_types.h"
|
||||||
|
#include "ringct/rctTypes.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
////
|
||||||
|
// CarrotEnoteV1
|
||||||
|
// - onetime address
|
||||||
|
// - amount commitment
|
||||||
|
// - encrypted amount
|
||||||
|
// - encrypted janus anchor
|
||||||
|
// - view tag
|
||||||
|
///
|
||||||
|
struct CarrotEnoteV1 final
|
||||||
|
{
|
||||||
|
/// K_o
|
||||||
|
crypto::public_key onetime_address;
|
||||||
|
/// C_a
|
||||||
|
rct::key amount_commitment;
|
||||||
|
/// a_enc
|
||||||
|
encrypted_amount_t amount_enc;
|
||||||
|
/// anchor_enc
|
||||||
|
encrypted_janus_anchor_t anchor_enc;
|
||||||
|
/// view_tag
|
||||||
|
view_tag_t view_tag;
|
||||||
|
/// D_e
|
||||||
|
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||||
|
/// L_0
|
||||||
|
crypto::key_image tx_first_key_image;
|
||||||
|
};
|
||||||
|
|
||||||
|
////
|
||||||
|
// CarrotCoinbaseEnoteV1
|
||||||
|
// - onetime address
|
||||||
|
// - cleartext amount
|
||||||
|
// - encrypted janus anchor
|
||||||
|
// - view tag
|
||||||
|
///
|
||||||
|
struct CarrotCoinbaseEnoteV1 final
|
||||||
|
{
|
||||||
|
/// K_o
|
||||||
|
crypto::public_key onetime_address;
|
||||||
|
/// a
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
/// anchor_enc
|
||||||
|
encrypted_janus_anchor_t anchor_enc;
|
||||||
|
/// view_tag
|
||||||
|
view_tag_t view_tag;
|
||||||
|
/// D_e
|
||||||
|
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||||
|
/// block_index
|
||||||
|
std::uint64_t block_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: gen_carrot_enote_v1() - generate a carrot v1 enote (all random)
|
||||||
|
*/
|
||||||
|
CarrotEnoteV1 gen_carrot_enote_v1();
|
||||||
|
/**
|
||||||
|
* brief: gen_carrot_coinbase_enote_v1() - generate a carrot coinbase v1 enote (all random)
|
||||||
|
*/
|
||||||
|
CarrotCoinbaseEnoteV1 gen_carrot_coinbase_enote_v1();
|
||||||
|
|
||||||
|
} //namespace carrot
|
69
src/carrot_core/config.h
Normal file
69
src/carrot_core/config.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// 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 Constants used in Carrot
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
// Carrot addressing protocol domain separators
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR[] = "Carrot commitment mask";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_ONETIME_EXTENSION_G[] = "Carrot key extension G";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_ONETIME_EXTENSION_T[] = "Carrot key extension T";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_ANCHOR[] = "Carrot encryption mask anchor";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_AMOUNT[] = "Carrot encryption mask a";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_ENCRYPTION_MASK_PAYMENT_ID[] = "Carrot encryption mask pid";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_JANUS_ANCHOR_SPECIAL[] = "Carrot janus anchor special";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_EPHEMERAL_PRIVKEY[] = "Carrot sending key normal";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_VIEW_TAG[] = "Carrot view tag";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_SENDER_RECEIVER_SECRET[] = "Carrot sender-receiver secret";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_INPUT_CONTEXT_COINBASE = 'C';
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_INPUT_CONTEXT_RINGCT = 'R';
|
||||||
|
|
||||||
|
// Carrot account secret domain separators
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_PROVE_SPEND_KEY[] = "Carrot prove-spend key";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_VIEW_BALANCE_SECRET[] = "Carrot view-balance secret";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_GENERATE_IMAGE_KEY[] = "Carrot generate-image key";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_INCOMING_VIEW_KEY[] = "Carrot incoming view key";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_GENERATE_ADDRESS_SECRET[] = "Carrot generate-address secret";
|
||||||
|
|
||||||
|
// Carrot address domain separators
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_ADDRESS_INDEX_GEN[] = "Carrot address index generator";
|
||||||
|
static constexpr const unsigned char CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR[] = "Carrot subaddress scalar";
|
||||||
|
|
||||||
|
} //namespace carrot
|
121
src/carrot_core/core_types.cpp
Normal file
121
src/carrot_core/core_types.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// 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 "core_types.h"
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template <std::size_t Sz>
|
||||||
|
static void xor_bytes(const unsigned char(&a)[Sz], const unsigned char(&b)[Sz], unsigned char(&c_out)[Sz])
|
||||||
|
{
|
||||||
|
for (std::size_t i{0}; i < Sz; ++i)
|
||||||
|
c_out[i] = a[i] ^ b[i];
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
|
static T xor_bytes(const T &a, const T &b)
|
||||||
|
{
|
||||||
|
T temp;
|
||||||
|
xor_bytes(a.bytes, b.bytes, temp.bytes);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b)
|
||||||
|
{
|
||||||
|
return memcmp(&a, &b, sizeof(janus_anchor_t)) == 0;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
janus_anchor_t operator^(const janus_anchor_t &a, const janus_anchor_t &b)
|
||||||
|
{
|
||||||
|
return xor_bytes(a, b);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool operator==(const encrypted_amount_t &a, const encrypted_amount_t &b)
|
||||||
|
{
|
||||||
|
return memcmp(&a, &b, sizeof(encrypted_amount_t)) == 0;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
encrypted_amount_t operator^(const encrypted_amount_t &a, const encrypted_amount_t &b)
|
||||||
|
{
|
||||||
|
return xor_bytes(a, b);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool operator==(const payment_id_t &a, const payment_id_t &b)
|
||||||
|
{
|
||||||
|
return memcmp(&a, &b, sizeof(payment_id_t)) == 0;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
payment_id_t operator^(const payment_id_t &a, const payment_id_t &b)
|
||||||
|
{
|
||||||
|
return xor_bytes(a, b);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool operator==(const input_context_t &a, const input_context_t &b)
|
||||||
|
{
|
||||||
|
return memcmp(&a, &b, sizeof(input_context_t)) == 0;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool operator==(const view_tag_t &a, const view_tag_t &b)
|
||||||
|
{
|
||||||
|
return memcmp(&a, &b, sizeof(view_tag_t)) == 0;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
janus_anchor_t gen_janus_anchor()
|
||||||
|
{
|
||||||
|
return crypto::rand<janus_anchor_t>();
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
payment_id_t gen_payment_id()
|
||||||
|
{
|
||||||
|
return crypto::rand<payment_id_t>();
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
view_tag_t gen_view_tag()
|
||||||
|
{
|
||||||
|
return crypto::rand<view_tag_t>();
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
input_context_t gen_input_context()
|
||||||
|
{
|
||||||
|
return crypto::rand<input_context_t>();
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace carrot
|
130
src/carrot_core/core_types.h
Normal file
130
src/carrot_core/core_types.h
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// 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 Supporting types for Carrot (anchor, view tag, etc.).
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr std::size_t JANUS_ANCHOR_BYTES{16};
|
||||||
|
|
||||||
|
/// either encodes randomness the private key of, or an HMAC of, the ephemeral pubkey
|
||||||
|
struct janus_anchor_t final
|
||||||
|
{
|
||||||
|
unsigned char bytes[JANUS_ANCHOR_BYTES];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// carrot janus anchor XORd with a user-defined secret
|
||||||
|
using encrypted_janus_anchor_t = janus_anchor_t;
|
||||||
|
|
||||||
|
/// carrot enote types
|
||||||
|
enum class CarrotEnoteType : unsigned char
|
||||||
|
{
|
||||||
|
PAYMENT = 0,
|
||||||
|
CHANGE = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/// carrot encrypted amount
|
||||||
|
constexpr std::size_t ENCRYPTED_AMOUNT_BYTES{8};
|
||||||
|
struct encrypted_amount_t final
|
||||||
|
{
|
||||||
|
unsigned char bytes[ENCRYPTED_AMOUNT_BYTES];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// legacy payment ID
|
||||||
|
constexpr std::size_t PAYMENT_ID_BYTES{8};
|
||||||
|
struct payment_id_t final
|
||||||
|
{
|
||||||
|
unsigned char bytes[PAYMENT_ID_BYTES];
|
||||||
|
};
|
||||||
|
static constexpr payment_id_t null_payment_id{{0}};
|
||||||
|
|
||||||
|
/// legacy encrypted payment ID
|
||||||
|
using encrypted_payment_id_t = payment_id_t;
|
||||||
|
|
||||||
|
/// carrot view tags
|
||||||
|
constexpr std::size_t VIEW_TAG_BYTES{3};
|
||||||
|
struct view_tag_t final
|
||||||
|
{
|
||||||
|
unsigned char bytes[VIEW_TAG_BYTES];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(view_tag_t) < 32, "uint8_t cannot index all view tag bits");
|
||||||
|
|
||||||
|
/// carrot input context
|
||||||
|
constexpr std::size_t INPUT_CONTEXT_BYTES{1 + 32};
|
||||||
|
struct input_context_t final
|
||||||
|
{
|
||||||
|
unsigned char bytes[INPUT_CONTEXT_BYTES];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// overloaded operators: address tag
|
||||||
|
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b);
|
||||||
|
static inline bool operator!=(const janus_anchor_t &a, const janus_anchor_t &b) { return !(a == b); }
|
||||||
|
janus_anchor_t operator^(const janus_anchor_t &a, const janus_anchor_t &b);
|
||||||
|
|
||||||
|
/// overloaded operators: encrypted amount
|
||||||
|
bool operator==(const encrypted_amount_t &a, const encrypted_amount_t &b);
|
||||||
|
static inline bool operator!=(const encrypted_amount_t &a, const encrypted_amount_t &b) { return !(a == b); }
|
||||||
|
encrypted_amount_t operator^(const encrypted_amount_t &a, const encrypted_amount_t &b);
|
||||||
|
|
||||||
|
/// overloaded operators: payment ID
|
||||||
|
bool operator==(const payment_id_t &a, const payment_id_t &b);
|
||||||
|
static inline bool operator!=(const payment_id_t &a, const payment_id_t &b) { return !(a == b); }
|
||||||
|
payment_id_t operator^(const payment_id_t &a, const payment_id_t &b);
|
||||||
|
|
||||||
|
/// overloaded operators: input context
|
||||||
|
bool operator==(const input_context_t &a, const input_context_t &b);
|
||||||
|
static inline bool operator!=(const input_context_t &a, const input_context_t &b) { return !(a == b); }
|
||||||
|
|
||||||
|
/// overloaded operators: view tag
|
||||||
|
bool operator==(const view_tag_t &a, const view_tag_t &b);
|
||||||
|
static inline bool operator!=(const view_tag_t &a, const view_tag_t &b) { return !(a == b); }
|
||||||
|
|
||||||
|
/// generate a random janus anchor
|
||||||
|
janus_anchor_t gen_janus_anchor();
|
||||||
|
/// generate a random (non-zero) payment ID
|
||||||
|
payment_id_t gen_payment_id();
|
||||||
|
/// generate a random view tag
|
||||||
|
view_tag_t gen_view_tag();
|
||||||
|
/// generate a random input context
|
||||||
|
input_context_t gen_input_context();
|
||||||
|
|
||||||
|
} //namespace carrot
|
149
src/carrot_core/destination.cpp
Normal file
149
src/carrot_core/destination.cpp
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// 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 "destination.h"
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "address_utils.h"
|
||||||
|
#include "misc_log_ex.h"
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool operator==(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b)
|
||||||
|
{
|
||||||
|
return a.address_spend_pubkey == b.address_spend_pubkey &&
|
||||||
|
a.address_view_pubkey == b.address_view_pubkey &&
|
||||||
|
a.is_subaddress == b.is_subaddress &&
|
||||||
|
a.payment_id == b.payment_id;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_main_address_v1(const crypto::public_key &account_spend_pubkey,
|
||||||
|
const crypto::public_key &primary_address_view_pubkey,
|
||||||
|
CarrotDestinationV1 &destination_out)
|
||||||
|
{
|
||||||
|
destination_out = CarrotDestinationV1{
|
||||||
|
.address_spend_pubkey = account_spend_pubkey,
|
||||||
|
.address_view_pubkey = primary_address_view_pubkey,
|
||||||
|
.is_subaddress = false,
|
||||||
|
.payment_id = null_payment_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_subaddress_v1(const crypto::public_key &account_spend_pubkey,
|
||||||
|
const crypto::public_key &account_view_pubkey,
|
||||||
|
const crypto::secret_key &s_generate_address,
|
||||||
|
const std::uint32_t &j_major,
|
||||||
|
const std::uint32_t &j_minor,
|
||||||
|
CarrotDestinationV1 &destination_out)
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(j_major != 0 || j_minor,
|
||||||
|
"make carrot subaddress v1: j cannot be 0 for a subaddress, only for main addresses");
|
||||||
|
|
||||||
|
// s^j_gen = H_32[s_ga](j_major, j_minor)
|
||||||
|
crypto::secret_key address_index_generator;
|
||||||
|
make_carrot_index_extension_generator(s_generate_address, j_major, j_minor, address_index_generator);
|
||||||
|
|
||||||
|
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
|
||||||
|
crypto::secret_key subaddress_scalar;
|
||||||
|
make_carrot_subaddress_scalar(account_spend_pubkey, address_index_generator, j_major, j_minor, subaddress_scalar);
|
||||||
|
|
||||||
|
// K^j_s = k^j_subscal * K_s
|
||||||
|
const rct::key address_spend_pubkey =
|
||||||
|
rct::scalarmultKey(rct::pk2rct(account_spend_pubkey), rct::sk2rct(subaddress_scalar));
|
||||||
|
|
||||||
|
// K^j_v = k^j_subscal * K_v
|
||||||
|
const rct::key address_view_pubkey =
|
||||||
|
rct::scalarmultKey(rct::pk2rct(account_view_pubkey), rct::sk2rct(subaddress_scalar));
|
||||||
|
|
||||||
|
destination_out = CarrotDestinationV1{
|
||||||
|
.address_spend_pubkey = rct::rct2pk(address_spend_pubkey),
|
||||||
|
.address_view_pubkey = rct::rct2pk(address_view_pubkey),
|
||||||
|
.is_subaddress = true,
|
||||||
|
.payment_id = null_payment_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_integrated_address_v1(const crypto::public_key &account_spend_pubkey,
|
||||||
|
const crypto::public_key &primary_address_view_pubkey,
|
||||||
|
const payment_id_t payment_id,
|
||||||
|
CarrotDestinationV1 &destination_out)
|
||||||
|
{
|
||||||
|
destination_out = CarrotDestinationV1{
|
||||||
|
.address_spend_pubkey = account_spend_pubkey,
|
||||||
|
.address_view_pubkey = primary_address_view_pubkey,
|
||||||
|
.is_subaddress = false,
|
||||||
|
.payment_id = payment_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
CarrotDestinationV1 gen_carrot_main_address_v1()
|
||||||
|
{
|
||||||
|
return CarrotDestinationV1{
|
||||||
|
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
|
||||||
|
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
|
||||||
|
.is_subaddress = false,
|
||||||
|
.payment_id = null_payment_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
CarrotDestinationV1 gen_carrot_subaddress_v1()
|
||||||
|
{
|
||||||
|
return CarrotDestinationV1{
|
||||||
|
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
|
||||||
|
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
|
||||||
|
.is_subaddress = true,
|
||||||
|
.payment_id = null_payment_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
CarrotDestinationV1 gen_carrot_integrated_address_v1()
|
||||||
|
{
|
||||||
|
// force generate non-zero payment id
|
||||||
|
payment_id_t payment_id{gen_payment_id()};
|
||||||
|
while (payment_id == null_payment_id)
|
||||||
|
payment_id = gen_payment_id();
|
||||||
|
|
||||||
|
return CarrotDestinationV1{
|
||||||
|
.address_spend_pubkey = rct::rct2pk(rct::pkGen()),
|
||||||
|
.address_view_pubkey = rct::rct2pk(rct::pkGen()),
|
||||||
|
.is_subaddress = false,
|
||||||
|
.payment_id = payment_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace carrot
|
116
src/carrot_core/destination.h
Normal file
116
src/carrot_core/destination.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// A 'payment proposal' is a proposal to make an enote sending funds to a Carrot address.
|
||||||
|
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "core_types.h"
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
////
|
||||||
|
// CarrotDestinationV1
|
||||||
|
// - for creating an output proposal to send an amount to someone
|
||||||
|
///
|
||||||
|
struct CarrotDestinationV1 final
|
||||||
|
{
|
||||||
|
/// K^j_s
|
||||||
|
crypto::public_key address_spend_pubkey;
|
||||||
|
/// K^j_v
|
||||||
|
crypto::public_key address_view_pubkey;
|
||||||
|
/// is a subaddress?
|
||||||
|
bool is_subaddress;
|
||||||
|
/// legacy payment id pid: null for main addresses and subaddresses
|
||||||
|
payment_id_t payment_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// equality operators
|
||||||
|
bool operator==(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b);
|
||||||
|
static inline bool operator!=(const CarrotDestinationV1 &a, const CarrotDestinationV1 &b) { return !(a == b); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_main_address_v1 - make a destination address
|
||||||
|
* param: account_spend_pubkey - K_s = k_gi X + k_ps T
|
||||||
|
* param: primary_address_view_pubkey - K^0_v = k_v G
|
||||||
|
* outparam: destination_out - the full main address
|
||||||
|
*/
|
||||||
|
void make_carrot_main_address_v1(const crypto::public_key &account_spend_pubkey,
|
||||||
|
const crypto::public_key &primary_address_view_pubkey,
|
||||||
|
CarrotDestinationV1 &destination_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_subaddress_v1 - make a destination address
|
||||||
|
* param: account_spend_pubkey - K_s = k_gi X + k_ps T
|
||||||
|
* param: account_view_pubkey - K_v = k_v K_s
|
||||||
|
* param: s_generate_address - s_ga
|
||||||
|
* param: j_major -
|
||||||
|
* param: j_minor -
|
||||||
|
* outparam: destination_out - the full subaddress
|
||||||
|
*/
|
||||||
|
void make_carrot_subaddress_v1(const crypto::public_key &account_spend_pubkey,
|
||||||
|
const crypto::public_key &account_view_pubkey,
|
||||||
|
const crypto::secret_key &s_generate_address,
|
||||||
|
const std::uint32_t &j_major,
|
||||||
|
const std::uint32_t &j_minor,
|
||||||
|
CarrotDestinationV1 &destination_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_integrated_address_v1 - make a destination address
|
||||||
|
* param: account_spend_pubkey - K_s = k_gi X + k_ps T
|
||||||
|
* param: primary_address_view_pubkey - K^0_v = k_v G
|
||||||
|
* param: payment_id - pid
|
||||||
|
* outparam: destination_out - the full main address
|
||||||
|
*/
|
||||||
|
void make_carrot_integrated_address_v1(const crypto::public_key &account_spend_pubkey,
|
||||||
|
const crypto::public_key &primary_address_view_pubkey,
|
||||||
|
const payment_id_t payment_id,
|
||||||
|
CarrotDestinationV1 &destination_out);
|
||||||
|
/**
|
||||||
|
* brief: gen_carrot_main_address_v1 - generate a random main address
|
||||||
|
*/
|
||||||
|
CarrotDestinationV1 gen_carrot_main_address_v1();
|
||||||
|
/**
|
||||||
|
* brief: gen_carrot_main_address_v1 - generate a random subaddress
|
||||||
|
*/
|
||||||
|
CarrotDestinationV1 gen_carrot_subaddress_v1();
|
||||||
|
/**
|
||||||
|
* brief: gen_carrot_main_address_v1 - generate a random integrated address
|
||||||
|
*/
|
||||||
|
CarrotDestinationV1 gen_carrot_integrated_address_v1();
|
||||||
|
|
||||||
|
} //namespace carrot
|
165
src/carrot_core/device.h
Normal file
165
src/carrot_core/device.h
Normal 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
|
90
src/carrot_core/device_ram_borrowed.cpp
Normal file
90
src/carrot_core/device_ram_borrowed.cpp
Normal 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
|
86
src/carrot_core/device_ram_borrowed.h
Normal file
86
src/carrot_core/device_ram_borrowed.h
Normal 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
|
185
src/carrot_core/enote_record_types.h
Normal file
185
src/carrot_core/enote_record_types.h
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "legacy_enote_types.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
struct IntermediateEnoteV1Record final
|
||||||
|
{
|
||||||
|
/// original enote
|
||||||
|
LegacyEnoteV1 enote;
|
||||||
|
|
||||||
|
/// i: legacy address index
|
||||||
|
cryptonote::subaddress_index subaddr_index;
|
||||||
|
/// pid: payment ID
|
||||||
|
jamtis::payment_id_t payment_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntermediateEnoteV2Record final
|
||||||
|
{
|
||||||
|
/// original enote
|
||||||
|
LegacyEnoteV2 enote;
|
||||||
|
|
||||||
|
/// i: legacy address index (false if unknown)
|
||||||
|
std::optional<cryptonote::subaddress_index> subaddr_index;
|
||||||
|
/// pid: payment ID
|
||||||
|
jamtis::payment_id_t payment_id;
|
||||||
|
/// a: the enote's amount
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntermediateEnoteV3Record final
|
||||||
|
{
|
||||||
|
/// original enote
|
||||||
|
LegacyEnoteV3 enote;
|
||||||
|
|
||||||
|
/// i: legacy address index (false if unknown)
|
||||||
|
std::optional<cryptonote::subaddress_index> subaddr_index;
|
||||||
|
/// pid: payment ID
|
||||||
|
jamtis::payment_id_t payment_id;
|
||||||
|
/// a: the enote's amount
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntermediateEnoteV4Record final
|
||||||
|
{
|
||||||
|
/// original enote
|
||||||
|
LegacyEnoteV4 enote;
|
||||||
|
|
||||||
|
/// i: legacy address index
|
||||||
|
cryptonote::subaddress_index subaddr_index;
|
||||||
|
/// pid: payment ID
|
||||||
|
jamtis::payment_id_t payment_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntermediateEnoteV5Record final
|
||||||
|
{
|
||||||
|
/// original enote
|
||||||
|
LegacyEnoteV5 enote;
|
||||||
|
|
||||||
|
/// i: legacy address index
|
||||||
|
std::optional<cryptonote::subaddress_index> subaddr_index;
|
||||||
|
/// pid: payment ID
|
||||||
|
jamtis::payment_id_t payment_id;
|
||||||
|
/// a: the enote's amount
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntermediateEnoteV6Record final
|
||||||
|
{
|
||||||
|
/// original enote
|
||||||
|
SpCoinbaseEnoteV1 enote;
|
||||||
|
/// the enote's ephemeral pubkey
|
||||||
|
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||||
|
/// the enote's input context
|
||||||
|
jamtis::input_context_t input_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntermediateEnoteV7Record final
|
||||||
|
{
|
||||||
|
/// original enote
|
||||||
|
SpCoinbaseEnoteV1 enote;
|
||||||
|
/// the enote's ephemeral pubkey
|
||||||
|
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||||
|
/// the enote's input context
|
||||||
|
jamtis::input_context_t input_context;
|
||||||
|
|
||||||
|
/// i: legacy address index
|
||||||
|
std::optional<cryptonote::subaddress_index> subaddr_index;
|
||||||
|
/// pid: payment ID
|
||||||
|
jamtis::payment_id_t payment_id;
|
||||||
|
/// a: the enote's amount
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
/// enote_type: the enote's type
|
||||||
|
jamtis::JamtisEnoteType enote_type;
|
||||||
|
/// is internal: true if used an internal secret for enote scanning
|
||||||
|
bool is_internal;
|
||||||
|
};
|
||||||
|
|
||||||
|
using IntermediateEnoteRecordVariant = tools::variant<
|
||||||
|
IntermediateEnoteV1Record,
|
||||||
|
IntermediateEnoteV2Record,
|
||||||
|
IntermediateEnoteV3Record,
|
||||||
|
IntermediateEnoteV4Record,
|
||||||
|
IntermediateEnoteV5Record,
|
||||||
|
IntermediateEnoteV6Record,
|
||||||
|
IntermediateEnoteV7Record>;
|
||||||
|
|
||||||
|
rct::xmr_amount amount_ref(const IntermediateEnoteRecordVariant &enote_record)
|
||||||
|
{
|
||||||
|
struct mierv_amount_visitor: tools::variant_static_visitor<rct::xmr_amount>
|
||||||
|
{
|
||||||
|
rct::xmr_amount operator()(const IntermediateEnoteV1Record &v) const { return v.enote.amount; }
|
||||||
|
rct::xmr_amount operator()(const IntermediateEnoteV2Record &v) const { return v.amount; }
|
||||||
|
rct::xmr_amount operator()(const IntermediateEnoteV3Record &v) const { return v.amount; }
|
||||||
|
rct::xmr_amount operator()(const IntermediateEnoteV4Record &v) const { return v.enote.amount; }
|
||||||
|
rct::xmr_amount operator()(const IntermediateEnoteV5Record &v) const { return v.amount; }
|
||||||
|
rct::xmr_amount operator()(const IntermediateEnoteV6Record &v) const { return v.enote.core.amount; }
|
||||||
|
rct::xmr_amount operator()(const IntermediateEnoteV7Record &v) const { return v.amount; }
|
||||||
|
};
|
||||||
|
|
||||||
|
return enote_record.visit(mierv_amount_visitor());
|
||||||
|
}
|
||||||
|
|
||||||
|
const crypto::public_key& onetime_address_ref(const IntermediateEnoteRecordVariant &enote_record)
|
||||||
|
{
|
||||||
|
struct mierv_onetime_address_visitor: tools::variant_static_visitor<const crypto::public_key&>
|
||||||
|
{
|
||||||
|
const crypto::public_key& operator()(const IntermediateEnoteV1Record &v) const { return v.enote.onetime_address; }
|
||||||
|
const crypto::public_key& operator()(const IntermediateEnoteV2Record &v) const { return v.enote.onetime_address; }
|
||||||
|
const crypto::public_key& operator()(const IntermediateEnoteV3Record &v) const { return v.enote.onetime_address; }
|
||||||
|
const crypto::public_key& operator()(const IntermediateEnoteV4Record &v) const { return v.enote.onetime_address; }
|
||||||
|
const crypto::public_key& operator()(const IntermediateEnoteV5Record &v) const { return v.enote.onetime_address; }
|
||||||
|
const crypto::public_key& operator()(const IntermediateEnoteV6Record &v) const { return v.enote.core.onetime_address; }
|
||||||
|
const crypto::public_key& operator()(const IntermediateEnoteV7Record &v) const { return v.enote.core.onetime_address; }
|
||||||
|
};
|
||||||
|
|
||||||
|
return enote_record.visit(mierv_amount_visitor());
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_legacy_enote_identifier(const IntermediateEnoteRecordVariant &enote_record, crypto::hash &identifier_out)
|
||||||
|
{
|
||||||
|
rct::key identifier_rk;
|
||||||
|
get_legacy_enote_identifier(onetime_address_ref(enote_record), amount_ref(enote_record), identifier_rk);
|
||||||
|
|
||||||
|
memcpy(&identifier_out, identifier_rk, sizeof(crypto::hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace carrot
|
483
src/carrot_core/enote_utils.cpp
Normal file
483
src/carrot_core/enote_utils.cpp
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
// 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 "cryptonote_config.h"
|
||||||
|
#include "hash_functions.h"
|
||||||
|
#include "int-util.h"
|
||||||
|
#include "misc_language.h"
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
#include "transcript_fixed.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
const crypto::public_key &address_spend_pubkey,
|
||||||
|
const crypto::public_key &address_view_pubkey,
|
||||||
|
const payment_id_t payment_id,
|
||||||
|
crypto::secret_key &enote_ephemeral_privkey_out)
|
||||||
|
{
|
||||||
|
// 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>(
|
||||||
|
anchor_norm, input_context, address_spend_pubkey, address_view_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,
|
||||||
|
crypto::x25519_pubkey &enote_ephemeral_pubkey_out)
|
||||||
|
{
|
||||||
|
// K_e = d_e G
|
||||||
|
ge_p3 D_e_in_ed25519;
|
||||||
|
ge_scalarmult_base(&D_e_in_ed25519, to_bytes(enote_ephemeral_privkey));
|
||||||
|
|
||||||
|
// D_e = ConvertPointE(K_e)
|
||||||
|
ge_p3_to_x25519(enote_ephemeral_pubkey_out.data, &D_e_in_ed25519);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey,
|
||||||
|
const crypto::public_key &address_spend_pubkey,
|
||||||
|
crypto::x25519_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);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool make_carrot_uncontextualized_shared_key_receiver(const crypto::secret_key &k_view,
|
||||||
|
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
|
||||||
|
crypto::x25519_pubkey &s_sender_receiver_unctx_out)
|
||||||
|
{
|
||||||
|
// @TODO: this is slower than a turtle on morphine, and will cripple scan speed, but should be correct
|
||||||
|
|
||||||
|
// K_e = ConvertPointM(D_e)
|
||||||
|
ge_p3 D_e_in_ed25519_p3;
|
||||||
|
if (ge_fromx25519_vartime(&D_e_in_ed25519_p3, enote_ephemeral_pubkey.data) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// serialize K_e
|
||||||
|
crypto::public_key D_e_in_ed25519;
|
||||||
|
ge_p3_tobytes(to_bytes(D_e_in_ed25519), &D_e_in_ed25519_p3);
|
||||||
|
|
||||||
|
// do ECDH exchange 8 k_v D_e
|
||||||
|
return make_carrot_uncontextualized_shared_key_sender(k_view, D_e_in_ed25519, s_sender_receiver_unctx_out);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool make_carrot_uncontextualized_shared_key_sender(const crypto::secret_key &enote_ephemeral_privkey,
|
||||||
|
const crypto::public_key &address_view_pubkey,
|
||||||
|
crypto::x25519_pubkey &s_sender_receiver_unctx_out)
|
||||||
|
{
|
||||||
|
// [ed25519] s_sr = 8 d_e K^j_v
|
||||||
|
crypto::key_derivation s_sr_in_ed25519;
|
||||||
|
if (!crypto::wallet::generate_key_derivation(address_view_pubkey, enote_ephemeral_privkey, s_sr_in_ed25519))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// deserialize s_sr
|
||||||
|
ge_p3 s_sr_in_ed25519_p3;
|
||||||
|
ge_frombytes_vartime(&s_sr_in_ed25519_p3, to_bytes(s_sr_in_ed25519));
|
||||||
|
|
||||||
|
// ConvertPointE(s_sr)
|
||||||
|
ge_p3_to_x25519(s_sender_receiver_unctx_out.data, &s_sr_in_ed25519_p3);
|
||||||
|
|
||||||
|
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_carrot_input_context_coinbase(const std::uint64_t block_index, input_context_t &input_context_out)
|
||||||
|
{
|
||||||
|
// input_context = "C" || IntToBytes256(block_index)
|
||||||
|
memset(input_context_out.bytes, 0, sizeof(input_context_t));
|
||||||
|
input_context_out.bytes[0] = CARROT_DOMAIN_SEP_INPUT_CONTEXT_COINBASE;
|
||||||
|
memcpy_swap64le(input_context_out.bytes + 1, &block_index, 1);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_input_context(const crypto::key_image &first_rct_key_image, input_context_t &input_context_out)
|
||||||
|
{
|
||||||
|
// input_context = "R" || KI_1
|
||||||
|
input_context_out.bytes[0] = CARROT_DOMAIN_SEP_INPUT_CONTEXT_RINGCT;
|
||||||
|
memcpy(input_context_out.bytes + 1, first_rct_key_image.data, sizeof(crypto::key_image));
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_sender_receiver_secret(const unsigned char s_sender_receiver_unctx[32],
|
||||||
|
const crypto::x25519_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 CarrotEnoteType enote_type,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out)
|
||||||
|
{
|
||||||
|
// k_a = H_n(s^ctx_sr, enote_type)
|
||||||
|
const auto transcript = sp::make_fixed_transcript<CARROT_DOMAIN_SEP_AMOUNT_BLINDING_FACTOR>(
|
||||||
|
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 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 encrypted_payment_id ^ mask;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void make_carrot_janus_anchor_special(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,
|
||||||
|
janus_anchor_t &anchor_special_out)
|
||||||
|
{
|
||||||
|
// 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>(
|
||||||
|
enote_ephemeral_pubkey, input_context, account_spend_pubkey);
|
||||||
|
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 CarrotEnoteType nominal_enote_type,
|
||||||
|
const rct::xmr_amount nominal_amount,
|
||||||
|
const rct::key &amount_commitment,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out)
|
||||||
|
{
|
||||||
|
// k_a' = H_n(s^ctx_sr, enote_type')
|
||||||
|
make_carrot_amount_blinding_factor(s_sender_receiver, 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 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,
|
||||||
|
enote_type_out,
|
||||||
|
amount_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,
|
||||||
|
enote_type_out,
|
||||||
|
amount_out,
|
||||||
|
amount_commitment,
|
||||||
|
amount_blinding_factor_out))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// neither attempt at recomputing passed: so FAIL
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool verify_carrot_external_janus_protection(const janus_anchor_t &nominal_anchor,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
const crypto::public_key &nominal_address_spend_pubkey,
|
||||||
|
const crypto::public_key &nominal_address_view_pubkey,
|
||||||
|
const bool is_subaddress,
|
||||||
|
const payment_id_t nominal_payment_id,
|
||||||
|
const crypto::x25519_pubkey &enote_ephemeral_pubkey)
|
||||||
|
{
|
||||||
|
// d_e' = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid))
|
||||||
|
crypto::secret_key nominal_enote_ephemeral_privkey;
|
||||||
|
make_carrot_enote_ephemeral_privkey(nominal_anchor,
|
||||||
|
input_context,
|
||||||
|
nominal_address_spend_pubkey,
|
||||||
|
nominal_address_view_pubkey,
|
||||||
|
nominal_payment_id,
|
||||||
|
nominal_enote_ephemeral_privkey);
|
||||||
|
|
||||||
|
// recompute D_e' for d_e' and address type
|
||||||
|
crypto::x25519_pubkey nominal_enote_ephemeral_pubkey;
|
||||||
|
if (is_subaddress)
|
||||||
|
make_carrot_enote_ephemeral_pubkey_subaddress(nominal_enote_ephemeral_privkey,
|
||||||
|
nominal_address_spend_pubkey,
|
||||||
|
nominal_enote_ephemeral_pubkey);
|
||||||
|
else // cryptonote address
|
||||||
|
make_carrot_enote_ephemeral_pubkey_cryptonote(nominal_enote_ephemeral_privkey,
|
||||||
|
nominal_enote_ephemeral_pubkey);
|
||||||
|
|
||||||
|
// D_e' ?= D_e
|
||||||
|
return nominal_enote_ephemeral_pubkey == enote_ephemeral_pubkey;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace carrot
|
384
src/carrot_core/enote_utils.h
Normal file
384
src/carrot_core/enote_utils.h
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
// 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 Utilities for making and handling enotes with carrot.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
#include "crypto/x25519.h"
|
||||||
|
#include "core_types.h"
|
||||||
|
#include "ringct/rctTypes.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_enote_ephemeral_privkey - enote ephemeral privkey k_e for Carrot enotes
|
||||||
|
* d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid))
|
||||||
|
* param: anchor_norm - anchor_norm
|
||||||
|
* param: input_context - input_context
|
||||||
|
* param: address_spend_pubkey - K^j_s
|
||||||
|
* param: address_view_pubkey - K^j_v
|
||||||
|
* param: payment_id - pid
|
||||||
|
* outparam: enote_ephemeral_privkey_out - k_e
|
||||||
|
*/
|
||||||
|
void make_carrot_enote_ephemeral_privkey(const janus_anchor_t &anchor_norm,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
const crypto::public_key &address_spend_pubkey,
|
||||||
|
const crypto::public_key &address_view_pubkey,
|
||||||
|
const payment_id_t payment_id,
|
||||||
|
crypto::secret_key &enote_ephemeral_privkey_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_enote_ephemeral_pubkey_main - make enote ephemeral pubkey D_e for a main address
|
||||||
|
* D_e = d_e B
|
||||||
|
* param: enote_ephemeral_privkey - d_e
|
||||||
|
* outparam: enote_ephemeral_pubkey_out - D_e
|
||||||
|
*/
|
||||||
|
void make_carrot_enote_ephemeral_pubkey_cryptonote(const crypto::secret_key &enote_ephemeral_privkey,
|
||||||
|
crypto::x25519_pubkey &enote_ephemeral_pubkey_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_enote_ephemeral_pubkey_subaddress - make enote ephemeral pubkey D_e for a subaddress
|
||||||
|
* D_e = d_e ConvertPointE(K^j_s)
|
||||||
|
* param: enote_ephemeral_privkey - d_e
|
||||||
|
* param: address_spend_pubkey - K^j_s
|
||||||
|
* outparam: enote_ephemeral_pubkey_out - D_e
|
||||||
|
*/
|
||||||
|
void make_carrot_enote_ephemeral_pubkey_subaddress(const crypto::secret_key &enote_ephemeral_privkey,
|
||||||
|
const crypto::public_key &address_spend_pubkey,
|
||||||
|
crypto::x25519_pubkey &enote_ephemeral_pubkey_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_uncontextualized_shared_key_receiver - perform the receiver-side ECDH exchange for Carrot enotes
|
||||||
|
* s_sr = 8 k_v D_e
|
||||||
|
* param: k_view - k_v
|
||||||
|
* param: enote_ephemeral_pubkey - D_e
|
||||||
|
* outparam: s_sender_receiver_unctx_out - s_sr
|
||||||
|
* return: true if successful, false if a failure occurred in point decompression
|
||||||
|
*/
|
||||||
|
bool make_carrot_uncontextualized_shared_key_receiver(const crypto::secret_key &k_view,
|
||||||
|
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
|
||||||
|
crypto::x25519_pubkey &s_sender_receiver_unctx_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_uncontextualized_shared_key_sender - perform the sender-side ECDH exchange for Carrot enotes
|
||||||
|
* s_sr = 8 d_e ConvertPointE(K^j_v)
|
||||||
|
* param: enote_ephemeral_privkey - d_e
|
||||||
|
* param: address_view_pubkey - K^j_v
|
||||||
|
* outparam: s_sender_receiver_unctx_out - s_sr
|
||||||
|
* return: true if successful, false if a failure occurred in point decompression
|
||||||
|
*/
|
||||||
|
bool make_carrot_uncontextualized_shared_key_sender(const crypto::secret_key &enote_ephemeral_privkey,
|
||||||
|
const crypto::public_key &address_view_pubkey,
|
||||||
|
crypto::x25519_pubkey &s_sender_receiver_unctx_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_view_tag - used for optimized identification of enotes
|
||||||
|
* vt = H_3(s_sr || input_context || Ko)
|
||||||
|
* param: s_sender_receiver_unctx - s_sr
|
||||||
|
* param: input_context - input_context
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* outparam: view_tag_out - vt
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_input_context_coinbase - input context for a sender-receiver secret (coinbase txs)
|
||||||
|
* input_context = "C" || IntToBytes256(block_index)
|
||||||
|
* param: block_index - block index of the coinbase tx
|
||||||
|
* outparam: input_context_out - "C" || IntToBytes256(block_index)
|
||||||
|
*/
|
||||||
|
void make_carrot_input_context_coinbase(const std::uint64_t block_index, input_context_t &input_context_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_input_context - input context for a sender-receiver secret (standard RingCT txs)
|
||||||
|
* input_context = "R" || KI_1
|
||||||
|
* param: first_rct_key_image - KI_1, the first spent RingCT key image in a tx
|
||||||
|
* outparam: input_context_out - "S" || KI_1
|
||||||
|
*/
|
||||||
|
void make_carrot_input_context(const crypto::key_image &first_rct_key_image, input_context_t &input_context_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_sender_receiver_secret - contextualized sender-receiver secret s^ctx_sr
|
||||||
|
* s^ctx_sr = H_32(s_sr, D_e, input_context)
|
||||||
|
* param: s_sender_receiver_unctx - s_sr
|
||||||
|
* param: enote_ephemeral_pubkey - D_e
|
||||||
|
* param: input_context - [standard: KI_1] [coinbase: block index]
|
||||||
|
* outparam: s_sender_receiver_out - s^ctx_sr
|
||||||
|
* - note: this is 'crypto::hash' instead of 'crypto::secret_key' for better performance in multithreaded environments
|
||||||
|
*/
|
||||||
|
void make_carrot_sender_receiver_secret(const unsigned char s_sender_receiver_unctx[32],
|
||||||
|
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
crypto::hash &s_sender_receiver_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_onetime_address_extension_g - extension for transforming a receiver's spendkey into an
|
||||||
|
* enote one-time address
|
||||||
|
* k^o_g = H_n("..g..", s^ctx_sr, C_a)
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: amount_commitment - C_a
|
||||||
|
* outparam: sender_extension_out - k^o_g
|
||||||
|
*/
|
||||||
|
void make_carrot_onetime_address_extension_g(const crypto::hash &s_sender_receiver,
|
||||||
|
const rct::key &amount_commitment,
|
||||||
|
crypto::secret_key &sender_extension_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_onetime_address_extension_t - extension for transforming a receiver's spendkey into an
|
||||||
|
* enote one-time address
|
||||||
|
* k^o_t = H_n("..t..", s^ctx_sr, C_a)
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: amount_commitment - C_a
|
||||||
|
* outparam: sender_extension_out - k^o_t
|
||||||
|
*/
|
||||||
|
void make_carrot_onetime_address_extension_t(const crypto::hash &s_sender_receiver,
|
||||||
|
const rct::key &amount_commitment,
|
||||||
|
crypto::secret_key &sender_extension_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_onetime_address_extension_pubkey - create a FCMP++ onetime address extension pubkey
|
||||||
|
* K^o_ext = k^o_g G + k^o_t T
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: amount_commitment - C_a
|
||||||
|
* outparam: sender_extension_pubkey_out - K^o_ext
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_onetime_address - create a FCMP++ onetime address
|
||||||
|
* Ko = K^j_s + K^o_ext = K^j_s + (k^o_g G + k^o_t T)
|
||||||
|
* param: address_spend_pubkey - K^j_s
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: amount_commitment - C_a
|
||||||
|
* outparam: onetime_address_out - Ko
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_amount_blinding_factor - create blinding factor for enote's amount commitment C_a
|
||||||
|
* k_a = H_n(s^ctx_sr, enote_type)
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: enote_type - enote_type
|
||||||
|
* outparam: amount_blinding_factor_out - k_a
|
||||||
|
*/
|
||||||
|
void make_carrot_amount_blinding_factor(const crypto::hash &s_sender_receiver,
|
||||||
|
const CarrotEnoteType enote_type,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_anchor_encryption_mask - create XOR encryption mask for enote's anchor
|
||||||
|
* m_anchor = H_16(s^ctx_sr, Ko)
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* outparam: anchor_encryption_mask_out - m_anchor
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: encrypt_carrot_anchor - encrypt a Janus anchor for an enote
|
||||||
|
* anchor_enc = anchor XOR m_anchor
|
||||||
|
* param: anchor -
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* return: anchor_enc
|
||||||
|
*/
|
||||||
|
encrypted_janus_anchor_t encrypt_carrot_anchor(const janus_anchor_t &anchor,
|
||||||
|
const crypto::hash &s_sender_receiver,
|
||||||
|
const crypto::public_key &onetime_address);
|
||||||
|
/**
|
||||||
|
* brief: decrypt_carrot_address_tag - decrypt a Janus anchor from an enote
|
||||||
|
* anchor = anchor_enc XOR m_anchor
|
||||||
|
* param: encrypted_anchor - anchor_enc
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* return: anchor
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_amount_encryption_mask - create XOR encryption mask for enote's amount
|
||||||
|
* m_a = H_16(s^ctx_sr, Ko)
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* outparam: amount_encryption_mask_out - m_a
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: encrypt_carrot_amount - encrypt an amount for an enote
|
||||||
|
* a_enc = a XOR m_a
|
||||||
|
* param: amount - a
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* return: a_enc
|
||||||
|
*/
|
||||||
|
encrypted_amount_t encrypt_carrot_amount(const rct::xmr_amount amount,
|
||||||
|
const crypto::hash &s_sender_receiver,
|
||||||
|
const crypto::public_key &onetime_address);
|
||||||
|
/**
|
||||||
|
* brief: decrypt_carrot_amount - decrypt an amount from an enote
|
||||||
|
* a = a_enc XOR m_a
|
||||||
|
* param: encrypted_amount - a_enc
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* return: a
|
||||||
|
*/
|
||||||
|
rct::xmr_amount decrypt_carrot_amount(const encrypted_amount_t encrypted_amount,
|
||||||
|
const crypto::hash &s_sender_receiver,
|
||||||
|
const crypto::public_key &onetime_address);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_payment_id_encryption_mask - create XOR encryption mask for enote's payment ID
|
||||||
|
* m_pid = H_8(s^ctx_sr, Ko)
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* outparam: payment_id_encryption_mask_out - m_pid
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: encrypt_legacy_payment_id - encrypt a payment ID from an enote
|
||||||
|
* pid_enc = pid XOR m_pid
|
||||||
|
* param: payment_id - pid
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* return: pid_enc
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: decrypt_legacy_payment_id - decrypt a payment ID from an enote
|
||||||
|
* pid = pid_enc XOR m_pid
|
||||||
|
* param: encrypted_payment_id - pid_enc
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* return: pid
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: make_carrot_janus_anchor_special - make a janus anchor for "special" enotes
|
||||||
|
* anchor_sp = H_16(D_e, input_context, Ko, k_v, K_s)
|
||||||
|
* param: enote_ephemeral_pubkey - D_e
|
||||||
|
* param: input_context -
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* param: k_view - k_v
|
||||||
|
* param: account_spend_pubkey - K_s
|
||||||
|
* outparam: anchor_special_out - anchor_sp
|
||||||
|
*/
|
||||||
|
void make_carrot_janus_anchor_special(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,
|
||||||
|
janus_anchor_t &anchor_special_out);
|
||||||
|
/**
|
||||||
|
* brief: recover_address_spend_pubkey - get the receiver's spend key for which this RingCT onetime address
|
||||||
|
* can be reconstructed as 'owned' by
|
||||||
|
* K^j_s = Ko - K^o_ext = Ko - (k^o_g G + k^o_t U)
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: amount_commitment - C_a
|
||||||
|
* outparam: address_spend_key_out: - K^j_s
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: test_carrot_view_tag - test carrot view tag
|
||||||
|
* param: s_sender_receiver_unctx - s_sr
|
||||||
|
* param: input_context -
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* param: view_tag - vt
|
||||||
|
* return: true if successfully recomputed the view tag
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
/**
|
||||||
|
* brief: try_recompute_carrot_amount_commitment - test recreating the amount commitment for given enote_type and amount
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: nominal_enote_type - enote_type'
|
||||||
|
* param: nominal_amount - a'
|
||||||
|
* param: amount_commitment - C_a
|
||||||
|
* outparam: amount_blinding_factor_out - k_a' = H_n(s^ctx_sr, enote_type')
|
||||||
|
* return: true if successfully recomputed the amount commitment (C_a ?= k_a' G + a' H)
|
||||||
|
*/
|
||||||
|
bool try_recompute_carrot_amount_commitment(const crypto::hash &s_sender_receiver,
|
||||||
|
const CarrotEnoteType nominal_enote_type,
|
||||||
|
const rct::xmr_amount nominal_amount,
|
||||||
|
const rct::key &amount_commitment,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out);
|
||||||
|
/**
|
||||||
|
* brief: try_get_amount - test decrypting the amount and recomputing the amount commitment
|
||||||
|
* param: s_sender_receiver - s^ctx_sr
|
||||||
|
* param: encrypted_amount - a_enc
|
||||||
|
* param: onetime_address - Ko
|
||||||
|
* param: amount_commitment - C_a
|
||||||
|
* outparam: enote_type_out - enote_type'
|
||||||
|
* outparam: amount_out - a' = a_enc XOR m_a
|
||||||
|
* outparam: amount_blinding_factor_out - k_a' = H_n(s^ctx_sr, enote_type')
|
||||||
|
* return: true if successfully recomputed the amount commitment (C_a ?= k_a' G + a' H)
|
||||||
|
*/
|
||||||
|
bool try_get_carrot_amount(const crypto::hash &s_sender_receiver,
|
||||||
|
const encrypted_amount_t &encrypted_amount,
|
||||||
|
const crypto::public_key &onetime_address,
|
||||||
|
const rct::key &amount_commitment,
|
||||||
|
CarrotEnoteType &enote_type_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out);
|
||||||
|
/**
|
||||||
|
* brief: verify_carrot_external_janus_protection - check normal external enote is Janus safe (i.e. can recompute D_e)
|
||||||
|
* param: nominal_anchor - anchor'
|
||||||
|
* param: input_context -
|
||||||
|
* param: nominal_address_spend_pubkey - K^j_s'
|
||||||
|
* param: nominal_address_view_pubkey - K^j_v'
|
||||||
|
* param: is_subaddress -
|
||||||
|
* param: nominal_payment_id - pid'
|
||||||
|
* param: enote_ephemeral_pubkey - D_e
|
||||||
|
* return: true if this normal external enote is safe from Janus attacks
|
||||||
|
*/
|
||||||
|
bool verify_carrot_external_janus_protection(const janus_anchor_t &nominal_anchor,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
const crypto::public_key &nominal_address_spend_pubkey,
|
||||||
|
const crypto::public_key &nominal_address_view_pubkey,
|
||||||
|
const bool is_subaddress,
|
||||||
|
const payment_id_t nominal_payment_id,
|
||||||
|
const crypto::x25519_pubkey &enote_ephemeral_pubkey);
|
||||||
|
} //namespace carrot
|
109
src/carrot_core/hash_functions.cpp
Normal file
109
src/carrot_core/hash_functions.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// 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 "hash_functions.h"
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include "crypto/crypto-ops.h"
|
||||||
|
}
|
||||||
|
#include "crypto/blake2b.h"
|
||||||
|
#include "misc_log_ex.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
// H_x[k](data)
|
||||||
|
// - if derivation_key == nullptr, then the hash is NOT keyed
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
static void hash_base(const void *derivation_key, //32 bytes
|
||||||
|
const void *data,
|
||||||
|
const std::size_t data_length,
|
||||||
|
void *hash_out,
|
||||||
|
const std::size_t out_length)
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(blake2b(hash_out,
|
||||||
|
out_length,
|
||||||
|
data,
|
||||||
|
data_length,
|
||||||
|
derivation_key,
|
||||||
|
derivation_key ? 32 : 0) == 0,
|
||||||
|
"carrot hash base: blake2b failed.");
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void derive_bytes_3(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||||
|
{
|
||||||
|
// H_3(x): 2-byte output
|
||||||
|
hash_base(key, data, data_length, hash_out, 3);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void derive_bytes_8(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||||
|
{
|
||||||
|
// H_8(x): 8-byte output
|
||||||
|
hash_base(key, data, data_length, hash_out, 8);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void derive_bytes_16(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||||
|
{
|
||||||
|
// H_16(x): 16-byte output
|
||||||
|
hash_base(key, data, data_length, hash_out, 16);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void derive_bytes_32(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||||
|
{
|
||||||
|
// H_32(x): 32-byte output
|
||||||
|
hash_base(key, data, data_length, hash_out, 32);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void derive_bytes_64(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||||
|
{
|
||||||
|
// H_64(x): 64-byte output
|
||||||
|
hash_base(key, data, data_length, hash_out, 64);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void derive_scalar(const void *data, const std::size_t data_length, const void *key, void *hash_out)
|
||||||
|
{
|
||||||
|
// H_n(x): Ed25519 group scalar output (32 bytes)
|
||||||
|
// note: hash to 64 bytes then mod l
|
||||||
|
unsigned char temp[64];
|
||||||
|
hash_base(key, data, data_length, temp, 64);
|
||||||
|
sc_reduce(temp); //mod l
|
||||||
|
memcpy(hash_out, temp, 32);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace carrot
|
59
src/carrot_core/hash_functions.h
Normal file
59
src/carrot_core/hash_functions.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Core hash functions for Seraphis (note: this implementation satisfies the Jamtis specification).
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
/// H_3(x): 3-byte output
|
||||||
|
void derive_bytes_3(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||||
|
/// H_8(x): 8-byte output
|
||||||
|
void derive_bytes_8(const void *data, const std::size_t data_length, const void* key, void *hash_out);
|
||||||
|
/// H_16(x): 16-byte output
|
||||||
|
void derive_bytes_16(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||||
|
/// H_32(x): 32-byte output
|
||||||
|
void derive_bytes_32(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||||
|
/// H_64(x): 64-byte output
|
||||||
|
void derive_bytes_64(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||||
|
/// H_n(x): unclamped Curve25519/Ed25519 group scalar output (32 bytes)
|
||||||
|
void derive_scalar(const void *data, const std::size_t data_length, const void *key, void *hash_out);
|
||||||
|
|
||||||
|
} //namespace carrot
|
408
src/carrot_core/payment_proposal.cpp
Normal file
408
src/carrot_core/payment_proposal.cpp
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
// 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 "payment_proposal.h"
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "int-util.h"
|
||||||
|
#include "enote_utils.h"
|
||||||
|
#include "misc_language.h"
|
||||||
|
#include "misc_log_ex.h"
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
|
||||||
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
#define MONERO_DEFAULT_LOG_CATEGORY "carrot"
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
static const janus_anchor_t null_anchor{{0}};
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
|
static auto auto_wiper(T &obj)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_copyable<T>());
|
||||||
|
return epee::misc_utils::create_scope_leave_handler([&]{ memwipe(&obj, sizeof(T)); });
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
static crypto::secret_key get_enote_ephemeral_privkey(const CarrotPaymentProposalV1 &proposal,
|
||||||
|
const input_context_t &input_context)
|
||||||
|
{
|
||||||
|
// d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid))
|
||||||
|
crypto::secret_key enote_ephemeral_privkey;
|
||||||
|
make_carrot_enote_ephemeral_privkey(proposal.randomness,
|
||||||
|
input_context,
|
||||||
|
proposal.destination.address_spend_pubkey,
|
||||||
|
proposal.destination.address_view_pubkey,
|
||||||
|
proposal.destination.payment_id,
|
||||||
|
enote_ephemeral_privkey);
|
||||||
|
|
||||||
|
return enote_ephemeral_privkey;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
static void get_normal_proposal_ecdh_parts(const CarrotPaymentProposalV1 &proposal,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
crypto::x25519_pubkey &enote_ephemeral_pubkey_out,
|
||||||
|
crypto::x25519_pubkey &s_sender_receiver_unctx_out)
|
||||||
|
{
|
||||||
|
// 1. d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid))
|
||||||
|
const crypto::secret_key enote_ephemeral_privkey = get_enote_ephemeral_privkey(proposal, input_context);
|
||||||
|
|
||||||
|
// 2. make D_e
|
||||||
|
get_enote_ephemeral_pubkey(proposal, input_context, enote_ephemeral_pubkey_out);
|
||||||
|
|
||||||
|
// 3. s_sr = 8 d_e ConvertPointE(K^j_v)
|
||||||
|
make_carrot_uncontextualized_shared_key_sender(enote_ephemeral_privkey,
|
||||||
|
proposal.destination.address_view_pubkey,
|
||||||
|
s_sender_receiver_unctx_out);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
static void get_output_proposal_parts(const unsigned char s_sender_receiver_unctx[32],
|
||||||
|
const crypto::public_key &destination_spend_pubkey,
|
||||||
|
const payment_id_t payment_id,
|
||||||
|
const rct::xmr_amount amount,
|
||||||
|
const CarrotEnoteType enote_type,
|
||||||
|
const crypto::x25519_pubkey &enote_ephemeral_pubkey,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
const bool coinbase_amount_commitment,
|
||||||
|
crypto::hash &s_sender_receiver_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out,
|
||||||
|
rct::key &amount_commitment_out,
|
||||||
|
crypto::public_key &onetime_address_out,
|
||||||
|
encrypted_amount_t &encrypted_amount_out,
|
||||||
|
encrypted_payment_id_t &encrypted_payment_id_out,
|
||||||
|
view_tag_t &view_tag_out)
|
||||||
|
{
|
||||||
|
// 1. s^ctx_sr = H_32(s_sr, D_e, input_context)
|
||||||
|
make_carrot_sender_receiver_secret(s_sender_receiver_unctx,
|
||||||
|
enote_ephemeral_pubkey,
|
||||||
|
input_context,
|
||||||
|
s_sender_receiver_out);
|
||||||
|
|
||||||
|
// 2. k_a = H_n(s^ctx_sr, enote_type) if !coinbase, else 1
|
||||||
|
if (coinbase_amount_commitment)
|
||||||
|
amount_blinding_factor_out = rct::rct2sk(rct::I);
|
||||||
|
else
|
||||||
|
make_carrot_amount_blinding_factor(s_sender_receiver_out,
|
||||||
|
enote_type,
|
||||||
|
amount_blinding_factor_out);
|
||||||
|
|
||||||
|
// 3. C_a = k_a G + a H
|
||||||
|
amount_commitment_out = rct::commit(amount, rct::sk2rct(amount_blinding_factor_out));
|
||||||
|
|
||||||
|
// 4. Ko = K^j_s + K^o_ext = K^j_s + (k^o_g G + k^o_t T)
|
||||||
|
make_carrot_onetime_address(destination_spend_pubkey,
|
||||||
|
s_sender_receiver_out,
|
||||||
|
amount_commitment_out,
|
||||||
|
onetime_address_out);
|
||||||
|
|
||||||
|
// 5. a_enc = a XOR m_a
|
||||||
|
encrypted_amount_out = encrypt_carrot_amount(amount,
|
||||||
|
s_sender_receiver_out,
|
||||||
|
onetime_address_out);
|
||||||
|
|
||||||
|
// 6. pid_enc = pid XOR m_pid
|
||||||
|
encrypted_payment_id_out = encrypt_legacy_payment_id(payment_id, s_sender_receiver_out, onetime_address_out);
|
||||||
|
|
||||||
|
// 7. view tag: vt = H_3(s_sr || input_context || Ko)
|
||||||
|
make_carrot_view_tag(s_sender_receiver_unctx, input_context, onetime_address_out, view_tag_out);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1 &b)
|
||||||
|
{
|
||||||
|
return a.destination == b.destination &&
|
||||||
|
a.amount == b.amount &&
|
||||||
|
a.randomness == b.randomness;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentProposalSelfSendV1 &b)
|
||||||
|
{
|
||||||
|
return a.destination_address_spend_pubkey == b.destination_address_spend_pubkey &&
|
||||||
|
a.amount == b.amount &&
|
||||||
|
a.enote_type == b.enote_type &&
|
||||||
|
a.enote_ephemeral_pubkey == b.enote_ephemeral_pubkey;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
crypto::x25519_pubkey &enote_ephemeral_pubkey_out)
|
||||||
|
{
|
||||||
|
// d_e = H_n(anchor_norm, input_context, K^j_s, K^j_v, pid))
|
||||||
|
const crypto::secret_key enote_ephemeral_privkey{get_enote_ephemeral_privkey(proposal, input_context)};
|
||||||
|
|
||||||
|
if (proposal.destination.is_subaddress)
|
||||||
|
// D_e = d_e ConvertPointE(K^j_s)
|
||||||
|
make_carrot_enote_ephemeral_pubkey_subaddress(enote_ephemeral_privkey,
|
||||||
|
proposal.destination.address_spend_pubkey,
|
||||||
|
enote_ephemeral_pubkey_out);
|
||||||
|
else
|
||||||
|
// D_e = d_e B
|
||||||
|
make_carrot_enote_ephemeral_pubkey_cryptonote(enote_ephemeral_privkey,
|
||||||
|
enote_ephemeral_pubkey_out);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||||
|
const std::uint64_t block_index,
|
||||||
|
CarrotCoinbaseEnoteV1 &output_enote_out)
|
||||||
|
{
|
||||||
|
// 1. sanity checks
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(proposal.randomness != null_anchor,
|
||||||
|
"get coinbase output proposal v1: invalid randomness for janus anchor (zero).");
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(!proposal.destination.is_subaddress,
|
||||||
|
"get coinbase output proposal v1: subaddresses aren't allowed as destinations of coinbase outputs");
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(proposal.destination.payment_id == null_payment_id,
|
||||||
|
"get coinbase output proposal v1: integrated addresses aren't allowed as destinations of coinbase outputs");
|
||||||
|
|
||||||
|
// 2. coinbase input context
|
||||||
|
input_context_t input_context;
|
||||||
|
make_carrot_input_context_coinbase(block_index, input_context);
|
||||||
|
|
||||||
|
// 3. make D_e and do external ECDH
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
|
||||||
|
get_normal_proposal_ecdh_parts(proposal,
|
||||||
|
input_context,
|
||||||
|
output_enote_out.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
// 4. build the output enote address pieces
|
||||||
|
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||||
|
crypto::secret_key dummy_amount_blinding_factor;
|
||||||
|
rct::key dummy_amount_commitment;
|
||||||
|
encrypted_amount_t dummy_encrypted_amount;
|
||||||
|
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||||
|
get_output_proposal_parts(s_sender_receiver_unctx.data,
|
||||||
|
proposal.destination.address_spend_pubkey,
|
||||||
|
null_payment_id,
|
||||||
|
proposal.amount,
|
||||||
|
CarrotEnoteType::PAYMENT,
|
||||||
|
output_enote_out.enote_ephemeral_pubkey,
|
||||||
|
input_context,
|
||||||
|
true,
|
||||||
|
s_sender_receiver,
|
||||||
|
dummy_amount_blinding_factor,
|
||||||
|
dummy_amount_commitment,
|
||||||
|
output_enote_out.onetime_address,
|
||||||
|
dummy_encrypted_amount,
|
||||||
|
dummy_encrypted_payment_id,
|
||||||
|
output_enote_out.view_tag);
|
||||||
|
|
||||||
|
// 5. anchor_enc = anchor XOR m_anchor
|
||||||
|
output_enote_out.anchor_enc = encrypt_carrot_anchor(proposal.randomness,
|
||||||
|
s_sender_receiver,
|
||||||
|
output_enote_out.onetime_address);
|
||||||
|
|
||||||
|
// 6. save the amount and block index
|
||||||
|
output_enote_out.amount = proposal.amount;
|
||||||
|
output_enote_out.block_index = block_index;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||||
|
const crypto::key_image &tx_first_key_image,
|
||||||
|
CarrotEnoteV1 &output_enote_out,
|
||||||
|
encrypted_payment_id_t &encrypted_payment_id_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out)
|
||||||
|
{
|
||||||
|
// 1. sanity checks
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(proposal.randomness != null_anchor,
|
||||||
|
"jamtis payment proposal: invalid randomness for janus anchor (zero).");
|
||||||
|
|
||||||
|
// 2. input context: input_context = "R" || KI_1
|
||||||
|
input_context_t input_context;
|
||||||
|
make_carrot_input_context(tx_first_key_image, input_context);
|
||||||
|
|
||||||
|
// 3. make D_e and do external ECDH
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
|
||||||
|
get_normal_proposal_ecdh_parts(proposal,
|
||||||
|
input_context,
|
||||||
|
output_enote_out.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
// 4. build the output enote address pieces
|
||||||
|
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||||
|
get_output_proposal_parts(s_sender_receiver_unctx.data,
|
||||||
|
proposal.destination.address_spend_pubkey,
|
||||||
|
proposal.destination.payment_id,
|
||||||
|
proposal.amount,
|
||||||
|
CarrotEnoteType::PAYMENT,
|
||||||
|
output_enote_out.enote_ephemeral_pubkey,
|
||||||
|
input_context,
|
||||||
|
false,
|
||||||
|
s_sender_receiver,
|
||||||
|
amount_blinding_factor_out,
|
||||||
|
output_enote_out.amount_commitment,
|
||||||
|
output_enote_out.onetime_address,
|
||||||
|
output_enote_out.amount_enc,
|
||||||
|
encrypted_payment_id_out,
|
||||||
|
output_enote_out.view_tag);
|
||||||
|
|
||||||
|
// 5. anchor_enc = anchor XOR m_anchor
|
||||||
|
output_enote_out.anchor_enc = encrypt_carrot_anchor(proposal.randomness,
|
||||||
|
s_sender_receiver,
|
||||||
|
output_enote_out.onetime_address);
|
||||||
|
|
||||||
|
// 6. save the amount and first key image
|
||||||
|
amount_out = proposal.amount;
|
||||||
|
output_enote_out.tx_first_key_image = tx_first_key_image;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||||
|
const crypto::secret_key &k_view,
|
||||||
|
const crypto::public_key &primary_address_spend_pubkey,
|
||||||
|
const crypto::key_image &tx_first_key_image,
|
||||||
|
CarrotEnoteV1 &output_enote_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out)
|
||||||
|
{
|
||||||
|
// 1. sanity checks
|
||||||
|
// @TODO
|
||||||
|
|
||||||
|
// 2. input context: input_context = "R" || KI_1
|
||||||
|
input_context_t input_context;
|
||||||
|
make_carrot_input_context(tx_first_key_image, input_context);
|
||||||
|
|
||||||
|
// 3. s_sr = 8 * k_v * D_e
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||||
|
make_carrot_uncontextualized_shared_key_receiver(k_view,
|
||||||
|
proposal.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
// 4. build the output enote address pieces
|
||||||
|
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||||
|
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||||
|
get_output_proposal_parts(s_sender_receiver_unctx.data,
|
||||||
|
proposal.destination_address_spend_pubkey,
|
||||||
|
null_payment_id,
|
||||||
|
proposal.amount,
|
||||||
|
proposal.enote_type,
|
||||||
|
proposal.enote_ephemeral_pubkey,
|
||||||
|
input_context,
|
||||||
|
false,
|
||||||
|
s_sender_receiver,
|
||||||
|
amount_blinding_factor_out,
|
||||||
|
output_enote_out.amount_commitment,
|
||||||
|
output_enote_out.onetime_address,
|
||||||
|
output_enote_out.amount_enc,
|
||||||
|
dummy_encrypted_payment_id,
|
||||||
|
output_enote_out.view_tag);
|
||||||
|
|
||||||
|
// 5. make special janus anchor: anchor_sp = H_16(D_e, input_context, Ko, k_v, K_s)
|
||||||
|
janus_anchor_t janus_anchor_special;
|
||||||
|
make_carrot_janus_anchor_special(proposal.enote_ephemeral_pubkey,
|
||||||
|
input_context,
|
||||||
|
output_enote_out.onetime_address,
|
||||||
|
k_view,
|
||||||
|
primary_address_spend_pubkey,
|
||||||
|
janus_anchor_special);
|
||||||
|
|
||||||
|
// 6. encrypt special anchor: anchor_enc = anchor XOR m_anchor
|
||||||
|
output_enote_out.anchor_enc = encrypt_carrot_anchor(janus_anchor_special,
|
||||||
|
s_sender_receiver,
|
||||||
|
output_enote_out.onetime_address);
|
||||||
|
|
||||||
|
// 7. save the enote ephemeral pubkey, first tx key image, and amount
|
||||||
|
output_enote_out.enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey;
|
||||||
|
output_enote_out.tx_first_key_image = tx_first_key_image;
|
||||||
|
amount_out = proposal.amount;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||||
|
const crypto::secret_key &s_view_balance,
|
||||||
|
const crypto::key_image &tx_first_key_image,
|
||||||
|
CarrotEnoteV1 &output_enote_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out)
|
||||||
|
{
|
||||||
|
// 1. sanity checks
|
||||||
|
// @TODO
|
||||||
|
|
||||||
|
// 2. input context: input_context = "R" || KI_1
|
||||||
|
input_context_t input_context;
|
||||||
|
make_carrot_input_context(tx_first_key_image, input_context);
|
||||||
|
|
||||||
|
// 3. build the output enote address pieces
|
||||||
|
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
|
||||||
|
encrypted_payment_id_t dummy_encrypted_payment_id;
|
||||||
|
get_output_proposal_parts(to_bytes(s_view_balance),
|
||||||
|
proposal.destination_address_spend_pubkey,
|
||||||
|
null_payment_id,
|
||||||
|
proposal.amount,
|
||||||
|
proposal.enote_type,
|
||||||
|
proposal.enote_ephemeral_pubkey,
|
||||||
|
input_context,
|
||||||
|
false,
|
||||||
|
s_sender_receiver,
|
||||||
|
amount_blinding_factor_out,
|
||||||
|
output_enote_out.amount_commitment,
|
||||||
|
output_enote_out.onetime_address,
|
||||||
|
output_enote_out.amount_enc,
|
||||||
|
dummy_encrypted_payment_id,
|
||||||
|
output_enote_out.view_tag);
|
||||||
|
|
||||||
|
// 4. generate random encrypted anchor
|
||||||
|
output_enote_out.anchor_enc = gen_janus_anchor();
|
||||||
|
|
||||||
|
// 5. save the enote ephemeral pubkey, first tx key image, and amount
|
||||||
|
output_enote_out.enote_ephemeral_pubkey = proposal.enote_ephemeral_pubkey;
|
||||||
|
output_enote_out.tx_first_key_image = tx_first_key_image;
|
||||||
|
amount_out = proposal.amount;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
|
||||||
|
const bool has_payment_id,
|
||||||
|
const rct::xmr_amount amount,
|
||||||
|
const std::size_t num_random_memo_elements)
|
||||||
|
{
|
||||||
|
CarrotPaymentProposalV1 temp;
|
||||||
|
|
||||||
|
if (is_subaddress)
|
||||||
|
temp.destination = gen_carrot_subaddress_v1();
|
||||||
|
else if (has_payment_id)
|
||||||
|
temp.destination = gen_carrot_integrated_address_v1();
|
||||||
|
else
|
||||||
|
temp.destination = gen_carrot_main_address_v1();
|
||||||
|
|
||||||
|
temp.amount = amount;
|
||||||
|
temp.randomness = gen_janus_anchor();
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace carrot
|
165
src/carrot_core/payment_proposal.h
Normal file
165
src/carrot_core/payment_proposal.h
Normal 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.
|
||||||
|
|
||||||
|
// A 'payment proposal' is a proposal to make an enote sending funds to a Carrot address.
|
||||||
|
// Carrot: Cryptonote Address For Rerandomizable-RingCT-Output Transactions
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "carrot_enote_types.h"
|
||||||
|
#include "crypto/x25519.h"
|
||||||
|
#include "destination.h"
|
||||||
|
#include "ringct/rctTypes.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace carrot
|
||||||
|
{
|
||||||
|
|
||||||
|
////
|
||||||
|
// CarrotPaymentProposalV1
|
||||||
|
// - for creating an output proposal to send an amount to someone
|
||||||
|
///
|
||||||
|
struct CarrotPaymentProposalV1 final
|
||||||
|
{
|
||||||
|
/// user address
|
||||||
|
CarrotDestinationV1 destination;
|
||||||
|
/// b
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
/// anchor_norm: secret 16-byte randomness for Janus anchor
|
||||||
|
janus_anchor_t randomness;
|
||||||
|
};
|
||||||
|
|
||||||
|
////
|
||||||
|
// CarrotPaymentProposalSelfSendV1
|
||||||
|
// - for creating an output proposal to send an change to yourself
|
||||||
|
///
|
||||||
|
struct CarrotPaymentProposalSelfSendV1 final
|
||||||
|
{
|
||||||
|
/// one of our own address spend pubkeys: K^j_s
|
||||||
|
crypto::public_key destination_address_spend_pubkey;
|
||||||
|
/// a
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
|
||||||
|
/// enote_type
|
||||||
|
CarrotEnoteType enote_type;
|
||||||
|
/// enote ephemeral pubkey: xr G
|
||||||
|
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// equality operators
|
||||||
|
bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1 &b);
|
||||||
|
/// equality operators
|
||||||
|
bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentProposalSelfSendV1 &b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: get_enote_ephemeral_pubkey - get the proposal's enote ephemeral pubkey D_e
|
||||||
|
* param: proposal -
|
||||||
|
* param: input_context -
|
||||||
|
* outparam: enote_ephemeral_pubkey_out -
|
||||||
|
*/
|
||||||
|
void get_enote_ephemeral_pubkey(const CarrotPaymentProposalV1 &proposal,
|
||||||
|
const input_context_t &input_context,
|
||||||
|
crypto::x25519_pubkey &enote_ephemeral_pubkey_out);
|
||||||
|
/**
|
||||||
|
* brief: get_coinbase_output_proposal_v1 - convert the carrot proposal to a coinbase output proposal
|
||||||
|
* param: proposal -
|
||||||
|
* param: block_index - index of the coinbase tx's block
|
||||||
|
* outparam: output_enote_out -
|
||||||
|
* outparam: partial_memo_out -
|
||||||
|
*/
|
||||||
|
void get_coinbase_output_proposal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||||
|
const std::uint64_t block_index,
|
||||||
|
CarrotCoinbaseEnoteV1 &output_enote_out);
|
||||||
|
/**
|
||||||
|
* brief: get_output_proposal_normal_v1 - convert the carrot proposal to an output proposal
|
||||||
|
* param: proposal -
|
||||||
|
* param: tx_first_key_image -
|
||||||
|
* outparam: output_enote_out -
|
||||||
|
* outparam: encrypted_payment_id_out - pid_enc
|
||||||
|
* outparam: amount_out - used to open commitment C_a
|
||||||
|
* outparam: amount_blinding_factor_out - used to open commitment C_a
|
||||||
|
*/
|
||||||
|
void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
|
||||||
|
const crypto::key_image &tx_first_key_image,
|
||||||
|
CarrotEnoteV1 &output_enote_out,
|
||||||
|
encrypted_payment_id_t &encrypted_payment_id_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out);
|
||||||
|
/**
|
||||||
|
* brief: get_output_proposal_v1 - convert the carrot proposal to an output proposal (external selfsend)
|
||||||
|
* param: proposal -
|
||||||
|
* param: k_view -
|
||||||
|
* param: primary_address_spend_pubkey -
|
||||||
|
* param: tx_first_key_image -
|
||||||
|
* outparam: output_enote_out -
|
||||||
|
* outparam: amount_out - used to open commitment C_a
|
||||||
|
* outparam: amount_blinding_factor_out - used to open commitment C_a
|
||||||
|
*/
|
||||||
|
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||||
|
const crypto::secret_key &k_view,
|
||||||
|
const crypto::public_key &primary_address_spend_pubkey,
|
||||||
|
const crypto::key_image &tx_first_key_image,
|
||||||
|
CarrotEnoteV1 &output_enote_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out);
|
||||||
|
/**
|
||||||
|
* brief: get_output_proposal_internal_v1 - convert the carrot proposal to an output proposal (internal)
|
||||||
|
* param: proposal -
|
||||||
|
* param: s_view_balance -
|
||||||
|
* param: primary_address_spend_pubkey -
|
||||||
|
* param: tx_first_key_image -
|
||||||
|
* outparam: output_enote_out -
|
||||||
|
* outparam: partial_memo_out -
|
||||||
|
* outparam: amount_out - used to open commitment C_a
|
||||||
|
* outparam: amount_blinding_factor_out - used to open commitment C_a
|
||||||
|
*/
|
||||||
|
void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
|
||||||
|
const crypto::secret_key &s_view_balance,
|
||||||
|
const crypto::key_image &tx_first_key_image,
|
||||||
|
CarrotEnoteV1 &output_enote_out,
|
||||||
|
rct::xmr_amount &amount_out,
|
||||||
|
crypto::secret_key &amount_blinding_factor_out);
|
||||||
|
/**
|
||||||
|
* brief: gen_jamtis_payment_proposal_v1 - generate a random proposal
|
||||||
|
* param: is_subaddress - whether to generate a proposal to subaddress
|
||||||
|
* param: has_payment_id - true to generate non-zero payment ID, false for null payment ID
|
||||||
|
* param: amount -
|
||||||
|
* return: a random proposal
|
||||||
|
*/
|
||||||
|
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
|
||||||
|
const bool has_payment_id,
|
||||||
|
const rct::xmr_amount amount);
|
||||||
|
|
||||||
|
} //namespace carrot
|
185
src/carrot_core/transcript_fixed.h
Normal file
185
src/carrot_core/transcript_fixed.h
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Transcript class for assembling data that needs to be hashed.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "int-util.h"
|
||||||
|
#include "memwipe.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace sp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template <typename... Ts>
|
||||||
|
constexpr size_t sizeof_sum()
|
||||||
|
{
|
||||||
|
return (sizeof(Ts) + ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr size_t sizeof_sum<>()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} //namespace detail
|
||||||
|
|
||||||
|
////
|
||||||
|
// SpFixedTranscript
|
||||||
|
// - build a transcript of a fixed bytesize and input types, enforced at compile time
|
||||||
|
// - written to be the simplest correct transcript of data possible
|
||||||
|
// - requires domain separators at compile-time as well
|
||||||
|
// - ensures that no two transcripts with different domain separators will ever be equal
|
||||||
|
// - does not use dynamic allocation
|
||||||
|
// - unsigned integers are added to the transcript in little-endian form
|
||||||
|
// - signed integers are not allowed
|
||||||
|
// - domain separator is length-prefixed with a single unsigned byte at the beginning
|
||||||
|
// - passed domain separator can be null terminated or not, null bytes and after will be dropped
|
||||||
|
///
|
||||||
|
template <std::size_t N, const unsigned char domain_sep[N], typename... Ts>
|
||||||
|
class SpFixedTranscript final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//constructors
|
||||||
|
/// normal constructor
|
||||||
|
SpFixedTranscript(const Ts&... args)
|
||||||
|
{
|
||||||
|
// copy domain separator length prefix
|
||||||
|
m_transcript[0] = static_cast<unsigned char>(domain_sep_size());
|
||||||
|
|
||||||
|
// copy domain separator
|
||||||
|
memcpy(m_transcript + 1, domain_sep, domain_sep_size());
|
||||||
|
|
||||||
|
// copy types into buffer
|
||||||
|
append<1 + domain_sep_size()>(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
//overloaded operators
|
||||||
|
/// disable copy/move
|
||||||
|
SpFixedTranscript& operator=(const SpFixedTranscript&) = delete;
|
||||||
|
SpFixedTranscript& operator=(SpFixedTranscript&&) = delete;
|
||||||
|
|
||||||
|
//member functions
|
||||||
|
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
|
||||||
|
~SpFixedTranscript()
|
||||||
|
{
|
||||||
|
// wipe the buffer on leave in case it contains sensitive data
|
||||||
|
memwipe(m_transcript, sizeof(m_transcript));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//member functions
|
||||||
|
template <std::size_t>
|
||||||
|
void append() {}
|
||||||
|
|
||||||
|
template <std::size_t offset, typename U0, typename... Us>
|
||||||
|
void append(const U0 &arg0, const Us&... args)
|
||||||
|
{
|
||||||
|
// write current argument to buffer
|
||||||
|
write<offset>(arg0);
|
||||||
|
|
||||||
|
// call append for next argument
|
||||||
|
static constexpr size_t new_offset = offset + sizeof(arg0);
|
||||||
|
append<new_offset>(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t offset, typename U>
|
||||||
|
void write(const U &val)
|
||||||
|
{
|
||||||
|
static_assert(std::has_unique_object_representations_v<U>);
|
||||||
|
static_assert(std::is_standard_layout_v<U>);
|
||||||
|
static_assert(!std::is_signed_v<U> || std::is_same_v<U, char>);
|
||||||
|
static_assert(alignof(U) == 1);
|
||||||
|
static_assert(!std::is_pointer_v<U>);
|
||||||
|
|
||||||
|
memcpy(m_transcript + offset, &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t offset>
|
||||||
|
void write(std::uint16_t val)
|
||||||
|
{
|
||||||
|
val = SWAP16LE(val);
|
||||||
|
memcpy(m_transcript + offset, &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t offset>
|
||||||
|
void write(std::uint32_t val)
|
||||||
|
{
|
||||||
|
val = SWAP32LE(val);
|
||||||
|
memcpy(m_transcript + offset, &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t offset>
|
||||||
|
void write(std::uint64_t val)
|
||||||
|
{
|
||||||
|
val = SWAP64LE(val);
|
||||||
|
memcpy(m_transcript + offset, &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::size_t domain_sep_size()
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
|
if (domain_sep[i] == '\0')
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(domain_sep_size() <= 255, "domain separator must be less than 256 characters long");
|
||||||
|
|
||||||
|
//member variables
|
||||||
|
/// the transcript buffer
|
||||||
|
unsigned char m_transcript[size()];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <const auto & domain_sep, typename... Ts>
|
||||||
|
auto make_fixed_transcript(const Ts&... args)
|
||||||
|
{
|
||||||
|
return SpFixedTranscript<std::size(domain_sep), domain_sep, Ts...>(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace sp
|
@ -74,15 +74,14 @@ struct variant_static_visitor : public boost::static_visitor<ResultT>
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename... Types>
|
template <typename... Types>
|
||||||
class variant final
|
class variant
|
||||||
{
|
{
|
||||||
using VType = boost::variant<boost::blank, Types...>;
|
using VType = boost::variant<Types...>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//constructors
|
//constructors
|
||||||
/// default constructor
|
/// default constructor
|
||||||
variant() = default;
|
variant() = default;
|
||||||
variant(boost::none_t) : variant{} {} //act like boost::optional
|
|
||||||
|
|
||||||
/// construct from variant type (use enable_if to avoid issues with copy/move constructor)
|
/// construct from variant type (use enable_if to avoid issues with copy/move constructor)
|
||||||
template <typename T,
|
template <typename T,
|
||||||
@ -95,14 +94,7 @@ public:
|
|||||||
>::type = true>
|
>::type = true>
|
||||||
variant(T &&value) : m_value{std::forward<T>(value)} {}
|
variant(T &&value) : m_value{std::forward<T>(value)} {}
|
||||||
|
|
||||||
//overloaded operators
|
|
||||||
/// boolean operator: true if the variant isn't empty/uninitialized
|
|
||||||
explicit operator bool() const noexcept { return !this->is_empty(); }
|
|
||||||
|
|
||||||
//member functions
|
//member functions
|
||||||
/// check if empty/uninitialized
|
|
||||||
bool is_empty() const noexcept { return m_value.which() == 0; }
|
|
||||||
|
|
||||||
/// check the variant type
|
/// check the variant type
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool is_type() const noexcept { return this->index() == this->type_index_of<T>(); }
|
bool is_type() const noexcept { return this->index() == this->type_index_of<T>(); }
|
||||||
@ -136,7 +128,7 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr int type_index_of() noexcept
|
static constexpr int type_index_of() noexcept
|
||||||
{
|
{
|
||||||
using types = boost::mpl::vector<boost::blank, Types...>;
|
using types = typename VType::types;
|
||||||
using elem = typename boost::mpl::find<types, T>::type;
|
using elem = typename boost::mpl::find<types, T>::type;
|
||||||
using begin = typename boost::mpl::begin<types>::type;
|
using begin = typename boost::mpl::begin<types>::type;
|
||||||
return boost::mpl::distance<begin, elem>::value;
|
return boost::mpl::distance<begin, elem>::value;
|
||||||
@ -162,6 +154,41 @@ private:
|
|||||||
//member variables
|
//member variables
|
||||||
/// variant of all value types
|
/// variant of all value types
|
||||||
VType m_value;
|
VType m_value;
|
||||||
|
|
||||||
|
//friend functions
|
||||||
|
template <class Archive, typename... Ts>
|
||||||
|
friend bool do_serialize(Archive &ar, variant<Ts...> &v);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Types>
|
||||||
|
class optional_variant: public variant<boost::blank, Types...>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//constructors
|
||||||
|
/// default constructor
|
||||||
|
optional_variant() = default;
|
||||||
|
|
||||||
|
/// construct from variant type (use enable_if to avoid issues with copy/move constructor)
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<
|
||||||
|
!std::is_same<
|
||||||
|
std::remove_cv_t<std::remove_reference_t<T>>,
|
||||||
|
optional_variant<Types...>
|
||||||
|
>::value,
|
||||||
|
bool
|
||||||
|
>::type = true>
|
||||||
|
optional_variant(T &&value) : variant<boost::blank, Types...>(std::forward<T>(value)) {}
|
||||||
|
|
||||||
|
// construct like boost::optional
|
||||||
|
optional_variant(boost::none_t) {}
|
||||||
|
|
||||||
|
//overloaded operators
|
||||||
|
/// boolean operator: true if the variant isn't empty/uninitialized
|
||||||
|
explicit operator bool() const noexcept { return !this->is_empty(); }
|
||||||
|
|
||||||
|
//member functions
|
||||||
|
/// check if empty/uninitialized
|
||||||
|
bool is_empty() const noexcept { return this->index() == 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} //namespace tools
|
} //namespace tools
|
||||||
|
@ -50,7 +50,8 @@ set(crypto_sources
|
|||||||
slow-hash.c
|
slow-hash.c
|
||||||
rx-slow-hash.c
|
rx-slow-hash.c
|
||||||
CryptonightR_JIT.c
|
CryptonightR_JIT.c
|
||||||
tree-hash.c)
|
tree-hash.c
|
||||||
|
x25519.cpp)
|
||||||
|
|
||||||
if(ARCH_ID STREQUAL "i386" OR ARCH_ID STREQUAL "x86_64" OR ARCH_ID STREQUAL "x86-64" OR ARCH_ID STREQUAL "amd64")
|
if(ARCH_ID STREQUAL "i386" OR ARCH_ID STREQUAL "x86_64" OR ARCH_ID STREQUAL "x86-64" OR ARCH_ID STREQUAL "amd64")
|
||||||
list(APPEND crypto_sources CryptonightR_template.S)
|
list(APPEND crypto_sources CryptonightR_template.S)
|
||||||
@ -71,11 +72,15 @@ monero_add_library(cncrypto
|
|||||||
target_link_libraries(cncrypto
|
target_link_libraries(cncrypto
|
||||||
PUBLIC
|
PUBLIC
|
||||||
epee
|
epee
|
||||||
|
mx25519_static
|
||||||
randomx
|
randomx
|
||||||
${Boost_SYSTEM_LIBRARY}
|
${Boost_SYSTEM_LIBRARY}
|
||||||
${sodium_LIBRARIES}
|
${sodium_LIBRARIES}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${EXTRA_LIBRARIES})
|
${EXTRA_LIBRARIES})
|
||||||
|
target_include_directories(cncrypto
|
||||||
|
PRIVATE
|
||||||
|
${MX25519_INCLUDE})
|
||||||
|
|
||||||
if (ARM)
|
if (ARM)
|
||||||
option(NO_OPTIMIZED_MULTIPLY_ON_ARM
|
option(NO_OPTIMIZED_MULTIPLY_ON_ARM
|
||||||
|
@ -3829,6 +3829,199 @@ int sc_isnonzero(const unsigned char *s) {
|
|||||||
s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1;
|
s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void edwardsYZ_to_x25519(unsigned char *xbytes, const fe Y, const fe Z) {
|
||||||
|
// y = Y/Z
|
||||||
|
// x_mont = (1 + y) / (1 - y)
|
||||||
|
// = (1 + Y/Z) / (1 - Y/Z)
|
||||||
|
// = (Z + Y) / (Z - Y)
|
||||||
|
|
||||||
|
fe tmp0;
|
||||||
|
fe tmp1;
|
||||||
|
fe_add(tmp0, Z, Y); // Z + Y
|
||||||
|
fe_sub(tmp1, Z, Y); // Z - Y
|
||||||
|
fe_invert(tmp1, tmp1); // 1/(Z - Y)
|
||||||
|
fe_mul(tmp0, tmp0, tmp1); // (Z + Y) / (Z - Y)
|
||||||
|
fe_tobytes(xbytes, tmp0); // tobytes((Z + Y) / (Z - Y))
|
||||||
|
}
|
||||||
|
|
||||||
|
void ge_p2_to_x25519(unsigned char *xbytes, const ge_p2 *h)
|
||||||
|
{
|
||||||
|
edwardsYZ_to_x25519(xbytes, h->Y, h->Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h)
|
||||||
|
{
|
||||||
|
edwardsYZ_to_x25519(xbytes, h->Y, h->Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s)
|
||||||
|
{
|
||||||
|
/* From fe_frombytes.c */
|
||||||
|
|
||||||
|
int64_t h0 = load_4(s);
|
||||||
|
int64_t h1 = load_3(s + 4) << 6;
|
||||||
|
int64_t h2 = load_3(s + 7) << 5;
|
||||||
|
int64_t h3 = load_3(s + 10) << 3;
|
||||||
|
int64_t h4 = load_3(s + 13) << 2;
|
||||||
|
int64_t h5 = load_4(s + 16);
|
||||||
|
int64_t h6 = load_3(s + 20) << 7;
|
||||||
|
int64_t h7 = load_3(s + 23) << 5;
|
||||||
|
int64_t h8 = load_3(s + 26) << 4;
|
||||||
|
int64_t h9 = (load_3(s + 29) & 8388607) << 2;
|
||||||
|
int64_t carry0;
|
||||||
|
int64_t carry1;
|
||||||
|
int64_t carry2;
|
||||||
|
int64_t carry3;
|
||||||
|
int64_t carry4;
|
||||||
|
int64_t carry5;
|
||||||
|
int64_t carry6;
|
||||||
|
int64_t carry7;
|
||||||
|
int64_t carry8;
|
||||||
|
int64_t carry9;
|
||||||
|
|
||||||
|
/* Validate the number to be canonical */
|
||||||
|
if (h9 == 33554428 && h8 == 268435440 && h7 == 536870880 && h6 == 2147483520 &&
|
||||||
|
h5 == 4294967295 && h4 == 67108860 && h3 == 134217720 && h2 == 536870880 &&
|
||||||
|
h1 == 1073741760 && h0 >= 4294967277) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
|
||||||
|
carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
|
||||||
|
carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
|
||||||
|
carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
|
||||||
|
carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
|
||||||
|
|
||||||
|
carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||||
|
carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
|
||||||
|
carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||||
|
carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
|
||||||
|
carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
|
||||||
|
|
||||||
|
fe Y;
|
||||||
|
Y[0] = h0;
|
||||||
|
Y[1] = h1;
|
||||||
|
Y[2] = h2;
|
||||||
|
Y[3] = h3;
|
||||||
|
Y[4] = h4;
|
||||||
|
Y[5] = h5;
|
||||||
|
Y[6] = h6;
|
||||||
|
Y[7] = h7;
|
||||||
|
Y[8] = h8;
|
||||||
|
Y[9] = h9;
|
||||||
|
|
||||||
|
fe Z;
|
||||||
|
fe_1(Z);
|
||||||
|
|
||||||
|
edwardsYZ_to_x25519(xbytes, Y, Z);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ge_fromx25519_vartime(ge_p3 *h, const unsigned char *s) {
|
||||||
|
fe u;
|
||||||
|
fe v;
|
||||||
|
fe vxx;
|
||||||
|
fe check;
|
||||||
|
|
||||||
|
/* From fe_frombytes.c */
|
||||||
|
|
||||||
|
int64_t h0 = load_4(s);
|
||||||
|
int64_t h1 = load_3(s + 4) << 6;
|
||||||
|
int64_t h2 = load_3(s + 7) << 5;
|
||||||
|
int64_t h3 = load_3(s + 10) << 3;
|
||||||
|
int64_t h4 = load_3(s + 13) << 2;
|
||||||
|
int64_t h5 = load_4(s + 16);
|
||||||
|
int64_t h6 = load_3(s + 20) << 7;
|
||||||
|
int64_t h7 = load_3(s + 23) << 5;
|
||||||
|
int64_t h8 = load_3(s + 26) << 4;
|
||||||
|
int64_t h9 = (load_3(s + 29) & 8388607) << 2;
|
||||||
|
int64_t carry0;
|
||||||
|
int64_t carry1;
|
||||||
|
int64_t carry2;
|
||||||
|
int64_t carry3;
|
||||||
|
int64_t carry4;
|
||||||
|
int64_t carry5;
|
||||||
|
int64_t carry6;
|
||||||
|
int64_t carry7;
|
||||||
|
int64_t carry8;
|
||||||
|
int64_t carry9;
|
||||||
|
|
||||||
|
/* Validate the number to be canonical */
|
||||||
|
if (h9 == 33554428 && h8 == 268435440 && h7 == 536870880 && h6 == 2147483520 &&
|
||||||
|
h5 == 4294967295 && h4 == 67108860 && h3 == 134217720 && h2 == 536870880 &&
|
||||||
|
h1 == 1073741760 && h0 >= 4294967277) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
|
||||||
|
carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
|
||||||
|
carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
|
||||||
|
carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
|
||||||
|
carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
|
||||||
|
|
||||||
|
carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
|
||||||
|
carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
|
||||||
|
carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
|
||||||
|
carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
|
||||||
|
carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
|
||||||
|
|
||||||
|
/* End fe_frombytes.c */
|
||||||
|
|
||||||
|
/* Start ge_fromx25519_vartime specific code */
|
||||||
|
|
||||||
|
check[0] = h0;
|
||||||
|
check[1] = h1;
|
||||||
|
check[2] = h2;
|
||||||
|
check[3] = h3;
|
||||||
|
check[4] = h4;
|
||||||
|
check[5] = h5;
|
||||||
|
check[6] = h6;
|
||||||
|
check[7] = h7;
|
||||||
|
check[8] = h8;
|
||||||
|
check[9] = h9; /* load s into `check`, where s is the X coordinate of the X25519 point */
|
||||||
|
|
||||||
|
fe_1(v); /* v = 1 */
|
||||||
|
fe_copy(u, v); /* u = 1 */
|
||||||
|
u[0] = -1; /* u = -1 */
|
||||||
|
fe_add(u, u, check); /* u = s - 1 */
|
||||||
|
fe_add(v, v, check); /* v = s + 1 */
|
||||||
|
fe_invert(v, v); /* v = 1 / (s + 1) */
|
||||||
|
fe_mul(h->Y, u, v); /* Y = (s - 1) / (s + 1) */
|
||||||
|
|
||||||
|
/* End ge_fromx25519_vartime specific code */
|
||||||
|
|
||||||
|
fe_1(h->Z);
|
||||||
|
fe_sq(u, h->Y);
|
||||||
|
fe_mul(v, u, fe_d);
|
||||||
|
fe_sub(u, u, h->Z); /* u = y^2-1 */
|
||||||
|
fe_add(v, v, h->Z); /* v = dy^2+1 */
|
||||||
|
|
||||||
|
fe_divpowm1(h->X, u, v); /* x = uv^3(uv^7)^((q-5)/8) */
|
||||||
|
|
||||||
|
fe_sq(vxx, h->X);
|
||||||
|
fe_mul(vxx, vxx, v);
|
||||||
|
fe_sub(check, vxx, u); /* vx^2-u */
|
||||||
|
if (fe_isnonzero(check)) {
|
||||||
|
fe_add(check, vxx, u); /* vx^2+u */
|
||||||
|
if (fe_isnonzero(check)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fe_mul(h->X, h->X, fe_sqrtm1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fe_isnegative(h->X)) {
|
||||||
|
/* If x = 0, the sign must be positive */
|
||||||
|
if (!fe_isnonzero(h->X)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fe_neg(h->X, h->X);
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_mul(h->T, h->X, h->Y);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
|
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
|
||||||
// https://eprint.iacr.org/2008/522
|
// https://eprint.iacr.org/2008/522
|
||||||
// X == T == 0 and Y/Z == 1
|
// X == T == 0 and Y/Z == 1
|
||||||
|
@ -156,6 +156,18 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b,
|
|||||||
int sc_check(const unsigned char *);
|
int sc_check(const unsigned char *);
|
||||||
int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
|
int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: Convert Ed25519 y-coord to X25519 x-coord, AKA "ConvertPointE()" in the Carrot spec
|
||||||
|
*/
|
||||||
|
void ge_p2_to_x25519(unsigned char *xbytes, const ge_p2 *h);
|
||||||
|
void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h);
|
||||||
|
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: Convert X25519 x-coord to Ed25519 point with positive sign
|
||||||
|
*/
|
||||||
|
int ge_fromx25519_vartime(ge_p3 *h, const unsigned char *s);
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
uint64_t load_3(const unsigned char *in);
|
uint64_t load_3(const unsigned char *in);
|
||||||
uint64_t load_4(const unsigned char *in);
|
uint64_t load_4(const unsigned char *in);
|
||||||
|
@ -39,6 +39,7 @@ extern "C"
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace crypto
|
namespace crypto
|
||||||
{
|
{
|
||||||
@ -68,14 +69,35 @@ constexpr public_key G = bytes_to<public_key>({ 0x58, 0x66, 0x66, 0x66, 0x66, 0x
|
|||||||
//pedersen commitment generator H: toPoint(cn_fast_hash(G))
|
//pedersen commitment generator H: toPoint(cn_fast_hash(G))
|
||||||
constexpr public_key H = bytes_to<public_key>({ 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1,
|
constexpr public_key H = bytes_to<public_key>({ 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1,
|
||||||
0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94 });
|
0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94 });
|
||||||
|
//seraphis generator T: keccak_to_pt(keccak("Monero Generator T"))
|
||||||
|
constexpr public_key T = bytes_to<public_key>({ 0x96, 0x6f, 0xc6, 0x6b, 0x82, 0xcd, 0x56, 0xcf, 0x85, 0xea, 0xec, 0x80, 0x1c,
|
||||||
|
0x42, 0x84, 0x5f, 0x5f, 0x40, 0x88, 0x78, 0xd1, 0x56, 0x1e, 0x00, 0xd3, 0xd7, 0xde, 0xd2, 0x79, 0x4d, 0x09, 0x4f });
|
||||||
static ge_p3 G_p3;
|
static ge_p3 G_p3;
|
||||||
static ge_p3 H_p3;
|
static ge_p3 H_p3;
|
||||||
|
static ge_p3 T_p3;
|
||||||
static ge_cached G_cached;
|
static ge_cached G_cached;
|
||||||
static ge_cached H_cached;
|
static ge_cached H_cached;
|
||||||
|
static ge_cached T_cached;
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
static std::once_flag init_gens_once_flag;
|
static std::once_flag init_gens_once_flag;
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
// hash-to-point: H_p(x) = 8*point_from_bytes(keccak(x))
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
static void hash_to_point(const hash &x, crypto::ec_point &point_out)
|
||||||
|
{
|
||||||
|
hash h;
|
||||||
|
ge_p3 temp_p3;
|
||||||
|
ge_p2 temp_p2;
|
||||||
|
ge_p1p1 temp_p1p1;
|
||||||
|
|
||||||
|
cn_fast_hash(reinterpret_cast<const unsigned char*>(&x), sizeof(hash), h);
|
||||||
|
ge_fromfe_frombytes_vartime(&temp_p2, reinterpret_cast<const unsigned char*>(&h));
|
||||||
|
ge_mul8(&temp_p1p1, &temp_p2);
|
||||||
|
ge_p1p1_to_p3(&temp_p3, &temp_p1p1);
|
||||||
|
ge_p3_tobytes(to_bytes(point_out), &temp_p3);
|
||||||
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
static public_key reproduce_generator_G()
|
static public_key reproduce_generator_G()
|
||||||
@ -120,6 +142,18 @@ static public_key reproduce_generator_H()
|
|||||||
return reproduced_H;
|
return reproduced_H;
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
static public_key reproduce_generator_T()
|
||||||
|
{
|
||||||
|
// U = H_p(keccak("Monero Generator T"))
|
||||||
|
const std::string_view T_seed{"Monero Generator T"};
|
||||||
|
const hash T_temp_hash{cn_fast_hash(T_seed.data(), T_seed.size())};
|
||||||
|
public_key reproduced_T;
|
||||||
|
hash_to_point(T_temp_hash, reproduced_T);
|
||||||
|
|
||||||
|
return reproduced_T;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
// Make generators, but only once
|
// Make generators, but only once
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
static void init_gens()
|
static void init_gens()
|
||||||
@ -130,21 +164,26 @@ static void init_gens()
|
|||||||
// sanity check the generators
|
// sanity check the generators
|
||||||
static_assert(static_cast<unsigned char>(G.data[0]) == 0x58, "compile-time constant sanity check");
|
static_assert(static_cast<unsigned char>(G.data[0]) == 0x58, "compile-time constant sanity check");
|
||||||
static_assert(static_cast<unsigned char>(H.data[0]) == 0x8b, "compile-time constant sanity check");
|
static_assert(static_cast<unsigned char>(H.data[0]) == 0x8b, "compile-time constant sanity check");
|
||||||
|
static_assert(static_cast<unsigned char>(T.data[0]) == 0x96, "compile-time constant sanity check");
|
||||||
|
|
||||||
// build ge_p3 representations of generators
|
// build ge_p3 representations of generators
|
||||||
const int G_deserialize = ge_frombytes_vartime(&G_p3, to_bytes(G));
|
const int G_deserialize = ge_frombytes_vartime(&G_p3, to_bytes(G));
|
||||||
const int H_deserialize = ge_frombytes_vartime(&H_p3, to_bytes(H));
|
const int H_deserialize = ge_frombytes_vartime(&H_p3, to_bytes(H));
|
||||||
|
const int T_deserialize = ge_frombytes_vartime(&T_p3, to_bytes(T));
|
||||||
|
|
||||||
(void)G_deserialize; assert(G_deserialize == 0);
|
(void)G_deserialize; assert(G_deserialize == 0);
|
||||||
(void)H_deserialize; assert(H_deserialize == 0);
|
(void)H_deserialize; assert(H_deserialize == 0);
|
||||||
|
(void)T_deserialize; assert(T_deserialize == 0);
|
||||||
|
|
||||||
// get cached versions
|
// get cached versions
|
||||||
ge_p3_to_cached(&G_cached, &G_p3);
|
ge_p3_to_cached(&G_cached, &G_p3);
|
||||||
ge_p3_to_cached(&H_cached, &H_p3);
|
ge_p3_to_cached(&H_cached, &H_p3);
|
||||||
|
ge_p3_to_cached(&T_cached, &T_p3);
|
||||||
|
|
||||||
// in debug mode, check that generators are reproducible
|
// in debug mode, check that generators are reproducible
|
||||||
(void)reproduce_generator_G; assert(reproduce_generator_G() == G);
|
(void)reproduce_generator_G; assert(reproduce_generator_G() == G);
|
||||||
(void)reproduce_generator_H; assert(reproduce_generator_H() == H);
|
(void)reproduce_generator_H; assert(reproduce_generator_H() == H);
|
||||||
|
(void)reproduce_generator_T; assert(reproduce_generator_T() == T);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -159,6 +198,11 @@ public_key get_H()
|
|||||||
return H;
|
return H;
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
public_key get_T()
|
||||||
|
{
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
ge_p3 get_G_p3()
|
ge_p3 get_G_p3()
|
||||||
{
|
{
|
||||||
init_gens();
|
init_gens();
|
||||||
@ -171,6 +215,12 @@ ge_p3 get_H_p3()
|
|||||||
return H_p3;
|
return H_p3;
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
ge_p3 get_T_p3()
|
||||||
|
{
|
||||||
|
init_gens();
|
||||||
|
return T_p3;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
ge_cached get_G_cached()
|
ge_cached get_G_cached()
|
||||||
{
|
{
|
||||||
init_gens();
|
init_gens();
|
||||||
@ -183,4 +233,10 @@ ge_cached get_H_cached()
|
|||||||
return H_cached;
|
return H_cached;
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
ge_cached get_T_cached()
|
||||||
|
{
|
||||||
|
init_gens();
|
||||||
|
return T_cached;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
} //namespace crypto
|
} //namespace crypto
|
||||||
|
@ -39,9 +39,12 @@ namespace crypto
|
|||||||
|
|
||||||
public_key get_G();
|
public_key get_G();
|
||||||
public_key get_H();
|
public_key get_H();
|
||||||
|
public_key get_T();
|
||||||
ge_p3 get_G_p3();
|
ge_p3 get_G_p3();
|
||||||
ge_p3 get_H_p3();
|
ge_p3 get_H_p3();
|
||||||
|
ge_p3 get_T_p3();
|
||||||
ge_cached get_G_cached();
|
ge_cached get_G_cached();
|
||||||
ge_cached get_H_cached();
|
ge_cached get_H_cached();
|
||||||
|
ge_cached get_T_cached();
|
||||||
|
|
||||||
} //namespace crypto
|
} //namespace crypto
|
||||||
|
119
src/crypto/x25519.cpp
Normal file
119
src/crypto/x25519.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// Copyright (c) 2022, 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 "x25519.h"
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include "mx25519.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
#define MONERO_DEFAULT_LOG_CATEGORY "crypto"
|
||||||
|
|
||||||
|
namespace crypto
|
||||||
|
{
|
||||||
|
|
||||||
|
/// File-scope data
|
||||||
|
static const x25519_scalar X25519_EIGHT{ mx25519_privkey{ .data = { 8 } } };
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
x25519_scalar x25519_eight()
|
||||||
|
{
|
||||||
|
return X25519_EIGHT;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
x25519_secret_key x25519_secret_key_gen()
|
||||||
|
{
|
||||||
|
x25519_secret_key privkey;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
crypto::rand(32, privkey.data);
|
||||||
|
privkey.data[0] &= 255 - 7;
|
||||||
|
privkey.data[31] &= 127;
|
||||||
|
} while (privkey == x25519_secret_key{});
|
||||||
|
|
||||||
|
return privkey;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
x25519_pubkey x25519_pubkey_gen()
|
||||||
|
{
|
||||||
|
const x25519_secret_key privkey{x25519_secret_key_gen()};
|
||||||
|
x25519_pubkey pubkey;
|
||||||
|
x25519_scmul_base(privkey, pubkey);
|
||||||
|
|
||||||
|
return pubkey;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool x25519_scalar_is_canonical(const x25519_scalar &test_scalar)
|
||||||
|
{
|
||||||
|
//todo: is this constant time?
|
||||||
|
return (test_scalar.data[0] & 7) == 0 &&
|
||||||
|
(test_scalar.data[31] & 128) == 0;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void x25519_scmul_base(const x25519_scalar &scalar, x25519_pubkey &result_out)
|
||||||
|
{
|
||||||
|
static const mx25519_impl *impl{mx25519_select_impl(mx25519_type::MX25519_TYPE_AUTO)};
|
||||||
|
mx25519_scmul_base(impl, &result_out, &scalar);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void x25519_scmul_key(const x25519_scalar &scalar, const x25519_pubkey &pubkey, x25519_pubkey &result_out)
|
||||||
|
{
|
||||||
|
static const mx25519_impl *impl{mx25519_select_impl(mx25519_type::MX25519_TYPE_AUTO)};
|
||||||
|
mx25519_scmul_key(impl, &result_out, &scalar, &pubkey);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
void x25519_invmul_key(std::vector<x25519_secret_key> privkeys_to_invert,
|
||||||
|
const x25519_pubkey &initial_pubkey,
|
||||||
|
x25519_pubkey &result_out)
|
||||||
|
{
|
||||||
|
// 1. (1/({privkey1 * privkey2 * ...}))
|
||||||
|
// note: mx25519_invkey() will error if the resulting X25519 scalar is >= 2^255, so we 'search' for a valid solution
|
||||||
|
x25519_secret_key inverted_xkey;
|
||||||
|
result_out = initial_pubkey;
|
||||||
|
|
||||||
|
while (mx25519_invkey(&inverted_xkey, privkeys_to_invert.data(), privkeys_to_invert.size()) != 0)
|
||||||
|
{
|
||||||
|
privkeys_to_invert.emplace_back(X25519_EIGHT); //add 8 to keys to invert
|
||||||
|
x25519_scmul_key(X25519_EIGHT, result_out, result_out); //xK = 8 * xK
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. (1/([8*8*...*8] * {privkey1 * privkey2 * ...})) * [8*8*...*8] * xK
|
||||||
|
x25519_scmul_key(inverted_xkey, result_out, result_out);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
} //namespace crypto
|
127
src/crypto/x25519.h
Normal file
127
src/crypto/x25519.h
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// Copyright (c) 2022, 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.
|
||||||
|
|
||||||
|
// Interface for an x25519 implementation (mx25519).
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//local headers
|
||||||
|
#include "crypto.h"
|
||||||
|
#include "generic-ops.h"
|
||||||
|
#include "memwipe.h"
|
||||||
|
#include "mlocker.h"
|
||||||
|
|
||||||
|
//third party headers
|
||||||
|
|
||||||
|
//standard headers
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//forward declarations
|
||||||
|
|
||||||
|
|
||||||
|
namespace crypto
|
||||||
|
{
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include "mx25519.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct x25519_pubkey : public mx25519_pubkey
|
||||||
|
{
|
||||||
|
x25519_pubkey() = default;
|
||||||
|
x25519_pubkey(const mx25519_pubkey &other) { *this = other; }
|
||||||
|
x25519_pubkey& operator=(const mx25519_pubkey &other) { memcpy(data, other.data, 32); return *this; }
|
||||||
|
};
|
||||||
|
struct x25519_scalar : public mx25519_privkey
|
||||||
|
{
|
||||||
|
x25519_scalar() = default;
|
||||||
|
x25519_scalar(const mx25519_privkey &other) { *this = other; }
|
||||||
|
x25519_scalar& operator=(const mx25519_privkey &other) { memcpy(data, other.data, 32); return *this; }
|
||||||
|
};
|
||||||
|
struct x25519_secret_key : public epee::mlocked<tools::scrubbed<x25519_scalar>>
|
||||||
|
{
|
||||||
|
x25519_secret_key() = default;
|
||||||
|
x25519_secret_key(const x25519_scalar &other) { *this = other; }
|
||||||
|
x25519_secret_key& operator=(const x25519_scalar &other) { memcpy(data, other.data, 32); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* brief: x25519_eight - scalar 8
|
||||||
|
* return: scalar 8
|
||||||
|
*/
|
||||||
|
x25519_scalar x25519_eight();
|
||||||
|
/**
|
||||||
|
* brief: x25519_secret_key_gen - generate a random x25519 privkey
|
||||||
|
* return: random canonical x25519 privkey
|
||||||
|
*/
|
||||||
|
x25519_secret_key x25519_secret_key_gen();
|
||||||
|
/**
|
||||||
|
* brief: x25519_pubkey_gen - generate a random x25519 pubkey
|
||||||
|
* return: random x25519 pubkey
|
||||||
|
*/
|
||||||
|
x25519_pubkey x25519_pubkey_gen();
|
||||||
|
/**
|
||||||
|
* brief: x25519_scalar_is_canonical - check that an X25519 scalar is canonical
|
||||||
|
* - expect: 2^255 > scalar >= 8 (i.e. last bit and first three bits not set)
|
||||||
|
* result: true if input scalar is canonical
|
||||||
|
*/
|
||||||
|
bool x25519_scalar_is_canonical(const x25519_scalar &test_scalar);
|
||||||
|
/**
|
||||||
|
* brief: x25519_scmul_base - compute scalar * xG
|
||||||
|
* param: scalar - scalar to multiply
|
||||||
|
* result: scalar * xG
|
||||||
|
*/
|
||||||
|
void x25519_scmul_base(const x25519_scalar &scalar, x25519_pubkey &result_out);
|
||||||
|
/**
|
||||||
|
* brief: x25519_scmul_key - compute scalar * pubkey
|
||||||
|
* param: scalar - scalar to multiply
|
||||||
|
* param: pubkey - public key to multiple against
|
||||||
|
* result: scalar * pubkey
|
||||||
|
*/
|
||||||
|
void x25519_scmul_key(const x25519_scalar &scalar, const x25519_pubkey &pubkey, x25519_pubkey &result_out);
|
||||||
|
/**
|
||||||
|
* brief: x25519_invmul_key - compute (1/({privkey1 * privkey2 * ...})) * initial_pubkey
|
||||||
|
* param: privkeys_to_invert - {privkey1, privkey2, ...}
|
||||||
|
* param: initial_pubkey - base key for inversion
|
||||||
|
* result: (1/({privkey1 * privkey2 * ...})) * initial_pubkey
|
||||||
|
*/
|
||||||
|
void x25519_invmul_key(std::vector<x25519_secret_key> privkeys_to_invert,
|
||||||
|
const x25519_pubkey &initial_pubkey,
|
||||||
|
x25519_pubkey &result_out);
|
||||||
|
|
||||||
|
} //namespace crypto
|
||||||
|
|
||||||
|
inline const unsigned char* to_bytes(const crypto::x25519_pubkey &point) { return &reinterpret_cast<const unsigned char&>(point); }
|
||||||
|
inline const unsigned char* to_bytes(const crypto::x25519_scalar &scalar) { return &reinterpret_cast<const unsigned char&>(scalar); }
|
||||||
|
inline const unsigned char* to_bytes(const crypto::x25519_secret_key &skey) { return &reinterpret_cast<const unsigned char&>(skey); }
|
||||||
|
|
||||||
|
/// upgrade x25519 keys
|
||||||
|
CRYPTO_MAKE_HASHABLE(x25519_pubkey)
|
||||||
|
CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(x25519_scalar)
|
||||||
|
CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(x25519_secret_key)
|
@ -38,6 +38,8 @@ set(unit_tests_sources
|
|||||||
bulletproofs.cpp
|
bulletproofs.cpp
|
||||||
bulletproofs_plus.cpp
|
bulletproofs_plus.cpp
|
||||||
canonical_amounts.cpp
|
canonical_amounts.cpp
|
||||||
|
carrot_core.cpp
|
||||||
|
carrot_transcript_fixed.cpp
|
||||||
chacha.cpp
|
chacha.cpp
|
||||||
checkpoints.cpp
|
checkpoints.cpp
|
||||||
command_line.cpp
|
command_line.cpp
|
||||||
@ -113,6 +115,7 @@ monero_add_minimal_executable(unit_tests
|
|||||||
target_link_libraries(unit_tests
|
target_link_libraries(unit_tests
|
||||||
PRIVATE
|
PRIVATE
|
||||||
ringct
|
ringct
|
||||||
|
carrot_core
|
||||||
cryptonote_protocol
|
cryptonote_protocol
|
||||||
cryptonote_core
|
cryptonote_core
|
||||||
daemon_messages
|
daemon_messages
|
||||||
|
774
tests/unit_tests/carrot_core.cpp
Normal file
774
tests/unit_tests/carrot_core.cpp
Normal file
@ -0,0 +1,774 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "carrot_core/account_secrets.h"
|
||||||
|
#include "carrot_core/address_utils.h"
|
||||||
|
#include "carrot_core/carrot_enote_scan.h"
|
||||||
|
#include "carrot_core/device_ram_borrowed.h"
|
||||||
|
#include "carrot_core/enote_utils.h"
|
||||||
|
#include "carrot_core/payment_proposal.h"
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
#include "crypto/generators.h"
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
|
||||||
|
using namespace carrot;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
struct mock_carrot_keys
|
||||||
|
{
|
||||||
|
crypto::secret_key s_master;
|
||||||
|
crypto::secret_key k_prove_spend;
|
||||||
|
crypto::secret_key s_view_balance;
|
||||||
|
crypto::secret_key k_generate_image;
|
||||||
|
crypto::secret_key k_view;
|
||||||
|
crypto::secret_key s_generate_address;
|
||||||
|
crypto::public_key account_spend_pubkey;
|
||||||
|
crypto::public_key account_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()
|
||||||
|
{
|
||||||
|
mock_carrot_keys k;
|
||||||
|
crypto::generate_random_bytes_thread_safe(sizeof(crypto::secret_key), to_bytes(k.s_master));
|
||||||
|
make_carrot_provespend_key(k.s_master, k.k_prove_spend);
|
||||||
|
make_carrot_viewbalance_secret(k.s_master, k.s_view_balance);
|
||||||
|
make_carrot_generateimage_key(k.s_view_balance, k.k_generate_image);
|
||||||
|
make_carrot_viewincoming_key(k.s_view_balance, k.k_view);
|
||||||
|
make_carrot_generateaddress_secret(k.s_view_balance, k.s_generate_address);
|
||||||
|
make_carrot_spend_pubkey(k.k_generate_image, k.k_prove_spend, k.account_spend_pubkey);
|
||||||
|
k.account_view_pubkey = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(k.account_spend_pubkey),
|
||||||
|
rct::sk2rct(k.k_view)));
|
||||||
|
k.main_address_view_pubkey = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(k.k_view)));
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
static bool can_open_fcmp_onetime_address(const crypto::secret_key &k_prove_spend,
|
||||||
|
const crypto::secret_key &k_generate_image,
|
||||||
|
const crypto::secret_key &subaddr_scalar,
|
||||||
|
const crypto::secret_key &sender_extension_g,
|
||||||
|
const crypto::secret_key &sender_extension_t,
|
||||||
|
const crypto::public_key &onetime_address)
|
||||||
|
{
|
||||||
|
// K_s = k_gi G + k_ps T
|
||||||
|
// K^j_s = k^j_subscal * K_s
|
||||||
|
// Ko = K^j_s + k^o_g G + k^o_t T
|
||||||
|
// = (k^o_g + k^j_subscal * k_gi) + (k^o_t + k^j_subscal * k_ps)
|
||||||
|
|
||||||
|
// combined_g = k^o_g + k^j_subscal * k_gi
|
||||||
|
rct::key combined_g;
|
||||||
|
sc_muladd(combined_g.bytes, to_bytes(subaddr_scalar), to_bytes(k_generate_image), to_bytes(sender_extension_g));
|
||||||
|
|
||||||
|
// combined_t = k^o_t + k^j_subscal * k_ps
|
||||||
|
rct::key combined_t;
|
||||||
|
sc_muladd(combined_t.bytes, to_bytes(subaddr_scalar), to_bytes(k_prove_spend), to_bytes(sender_extension_t));
|
||||||
|
|
||||||
|
// Ko' = combined_g G + combined_t T
|
||||||
|
rct::key recomputed_onetime_address;
|
||||||
|
rct::addKeys2(recomputed_onetime_address, combined_g, combined_t, rct::pk2rct(crypto::get_T()));
|
||||||
|
|
||||||
|
// Ko' ?= Ko
|
||||||
|
return recomputed_onetime_address == onetime_address;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, ECDH_cryptonote_completeness)
|
||||||
|
{
|
||||||
|
crypto::secret_key k_view = rct::rct2sk(rct::skGen());
|
||||||
|
crypto::public_key view_pubkey = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(k_view)));
|
||||||
|
crypto::secret_key k_ephem = rct::rct2sk(rct::skGen());
|
||||||
|
ASSERT_NE(k_view, k_ephem);
|
||||||
|
|
||||||
|
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||||
|
make_carrot_enote_ephemeral_pubkey_cryptonote(k_ephem, enote_ephemeral_pubkey);
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sr_sender;
|
||||||
|
ASSERT_TRUE(make_carrot_uncontextualized_shared_key_sender(k_ephem, view_pubkey, s_sr_sender));
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sr_receiver;
|
||||||
|
ASSERT_TRUE(make_carrot_uncontextualized_shared_key_receiver(k_view, enote_ephemeral_pubkey, s_sr_receiver));
|
||||||
|
|
||||||
|
EXPECT_EQ(s_sr_sender, s_sr_receiver);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, ECDH_subaddress_completeness)
|
||||||
|
{
|
||||||
|
crypto::secret_key k_view = rct::rct2sk(rct::skGen());
|
||||||
|
crypto::public_key spend_pubkey = rct::rct2pk(rct::pkGen());
|
||||||
|
crypto::public_key view_pubkey = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(spend_pubkey), rct::sk2rct(k_view)));
|
||||||
|
crypto::secret_key k_ephem = rct::rct2sk(rct::skGen());
|
||||||
|
ASSERT_NE(k_view, k_ephem);
|
||||||
|
|
||||||
|
crypto::x25519_pubkey enote_ephemeral_pubkey;
|
||||||
|
make_carrot_enote_ephemeral_pubkey_subaddress(k_ephem, spend_pubkey, enote_ephemeral_pubkey);
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sr_sender;
|
||||||
|
ASSERT_TRUE(make_carrot_uncontextualized_shared_key_sender(k_ephem, view_pubkey, s_sr_sender));
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sr_receiver;
|
||||||
|
ASSERT_TRUE(make_carrot_uncontextualized_shared_key_receiver(k_view, enote_ephemeral_pubkey, s_sr_receiver));
|
||||||
|
|
||||||
|
EXPECT_EQ(s_sr_sender, s_sr_receiver);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, main_address_normal_scan_completeness)
|
||||||
|
{
|
||||||
|
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||||
|
|
||||||
|
CarrotDestinationV1 main_address;
|
||||||
|
make_carrot_main_address_v1(keys.account_spend_pubkey, keys.main_address_view_pubkey, main_address);
|
||||||
|
|
||||||
|
const CarrotPaymentProposalV1 proposal = CarrotPaymentProposalV1{
|
||||||
|
.destination = main_address,
|
||||||
|
.amount = crypto::rand<rct::xmr_amount>(),
|
||||||
|
.randomness = gen_janus_anchor()
|
||||||
|
};
|
||||||
|
|
||||||
|
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||||
|
|
||||||
|
CarrotEnoteV1 enote;
|
||||||
|
encrypted_payment_id_t encrypted_payment_id;
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
crypto::secret_key amount_blinding_factor;
|
||||||
|
get_output_proposal_normal_v1(proposal,
|
||||||
|
tx_first_key_image,
|
||||||
|
enote,
|
||||||
|
encrypted_payment_id,
|
||||||
|
amount,
|
||||||
|
amount_blinding_factor);
|
||||||
|
|
||||||
|
ASSERT_EQ(proposal.amount, amount);
|
||||||
|
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||||
|
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||||
|
enote.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
crypto::secret_key recovered_sender_extension_g;
|
||||||
|
crypto::secret_key recovered_sender_extension_t;
|
||||||
|
crypto::public_key recovered_address_spend_pubkey;
|
||||||
|
rct::xmr_amount recovered_amount;
|
||||||
|
crypto::secret_key recovered_amount_blinding_factor;
|
||||||
|
encrypted_payment_id_t recovered_payment_id;
|
||||||
|
CarrotEnoteType recovered_enote_type;
|
||||||
|
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||||
|
encrypted_payment_id,
|
||||||
|
s_sender_receiver_unctx,
|
||||||
|
keys.k_view_dev,
|
||||||
|
keys.account_spend_pubkey,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
recovered_address_spend_pubkey,
|
||||||
|
recovered_amount,
|
||||||
|
recovered_amount_blinding_factor,
|
||||||
|
recovered_payment_id,
|
||||||
|
recovered_enote_type);
|
||||||
|
|
||||||
|
ASSERT_TRUE(scan_success);
|
||||||
|
|
||||||
|
// check recovered data
|
||||||
|
EXPECT_EQ(proposal.destination.address_spend_pubkey, recovered_address_spend_pubkey);
|
||||||
|
EXPECT_EQ(amount, recovered_amount);
|
||||||
|
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||||
|
EXPECT_EQ(null_payment_id, recovered_payment_id);
|
||||||
|
EXPECT_EQ(CarrotEnoteType::PAYMENT, recovered_enote_type);
|
||||||
|
|
||||||
|
// check spendability
|
||||||
|
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||||
|
keys.k_generate_image,
|
||||||
|
rct::rct2sk(rct::I),
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
enote.onetime_address));
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, subaddress_normal_scan_completeness)
|
||||||
|
{
|
||||||
|
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||||
|
|
||||||
|
const uint32_t j_major = crypto::rand<uint32_t>();
|
||||||
|
const uint32_t j_minor = crypto::rand<uint32_t>();
|
||||||
|
|
||||||
|
CarrotDestinationV1 subaddress;
|
||||||
|
make_carrot_subaddress_v1(keys.account_spend_pubkey,
|
||||||
|
keys.account_view_pubkey,
|
||||||
|
keys.s_generate_address,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
subaddress);
|
||||||
|
|
||||||
|
const CarrotPaymentProposalV1 proposal = CarrotPaymentProposalV1{
|
||||||
|
.destination = subaddress,
|
||||||
|
.amount = crypto::rand<rct::xmr_amount>(),
|
||||||
|
.randomness = gen_janus_anchor()
|
||||||
|
};
|
||||||
|
|
||||||
|
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||||
|
|
||||||
|
CarrotEnoteV1 enote;
|
||||||
|
encrypted_payment_id_t encrypted_payment_id;
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
crypto::secret_key amount_blinding_factor;
|
||||||
|
get_output_proposal_normal_v1(proposal,
|
||||||
|
tx_first_key_image,
|
||||||
|
enote,
|
||||||
|
encrypted_payment_id,
|
||||||
|
amount,
|
||||||
|
amount_blinding_factor);
|
||||||
|
|
||||||
|
ASSERT_EQ(proposal.amount, amount);
|
||||||
|
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||||
|
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||||
|
enote.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
crypto::secret_key recovered_sender_extension_g;
|
||||||
|
crypto::secret_key recovered_sender_extension_t;
|
||||||
|
crypto::public_key recovered_address_spend_pubkey;
|
||||||
|
rct::xmr_amount recovered_amount;
|
||||||
|
crypto::secret_key recovered_amount_blinding_factor;
|
||||||
|
encrypted_payment_id_t recovered_payment_id;
|
||||||
|
CarrotEnoteType recovered_enote_type;
|
||||||
|
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||||
|
encrypted_payment_id,
|
||||||
|
s_sender_receiver_unctx,
|
||||||
|
keys.k_view_dev,
|
||||||
|
keys.account_spend_pubkey,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
recovered_address_spend_pubkey,
|
||||||
|
recovered_amount,
|
||||||
|
recovered_amount_blinding_factor,
|
||||||
|
recovered_payment_id,
|
||||||
|
recovered_enote_type);
|
||||||
|
|
||||||
|
ASSERT_TRUE(scan_success);
|
||||||
|
|
||||||
|
// check recovered data
|
||||||
|
EXPECT_EQ(proposal.destination.address_spend_pubkey, recovered_address_spend_pubkey);
|
||||||
|
EXPECT_EQ(amount, recovered_amount);
|
||||||
|
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||||
|
EXPECT_EQ(null_payment_id, recovered_payment_id);
|
||||||
|
EXPECT_EQ(CarrotEnoteType::PAYMENT, recovered_enote_type);
|
||||||
|
|
||||||
|
// check spendability
|
||||||
|
crypto::secret_key address_generator;
|
||||||
|
make_carrot_index_extension_generator(keys.s_generate_address,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
address_generator);
|
||||||
|
|
||||||
|
crypto::secret_key subaddr_scalar;
|
||||||
|
make_carrot_subaddress_scalar(keys.account_spend_pubkey,
|
||||||
|
address_generator,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
subaddr_scalar);
|
||||||
|
|
||||||
|
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||||
|
keys.k_generate_image,
|
||||||
|
subaddr_scalar,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
enote.onetime_address));
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, integrated_address_normal_scan_completeness)
|
||||||
|
{
|
||||||
|
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||||
|
|
||||||
|
CarrotDestinationV1 integrated_address;
|
||||||
|
make_carrot_integrated_address_v1(keys.account_spend_pubkey,
|
||||||
|
keys.main_address_view_pubkey,
|
||||||
|
gen_payment_id(),
|
||||||
|
integrated_address);
|
||||||
|
|
||||||
|
const CarrotPaymentProposalV1 proposal = CarrotPaymentProposalV1{
|
||||||
|
.destination = integrated_address,
|
||||||
|
.amount = crypto::rand<rct::xmr_amount>(),
|
||||||
|
.randomness = gen_janus_anchor()
|
||||||
|
};
|
||||||
|
|
||||||
|
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||||
|
|
||||||
|
CarrotEnoteV1 enote;
|
||||||
|
encrypted_payment_id_t encrypted_payment_id;
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
crypto::secret_key amount_blinding_factor;
|
||||||
|
get_output_proposal_normal_v1(proposal,
|
||||||
|
tx_first_key_image,
|
||||||
|
enote,
|
||||||
|
encrypted_payment_id,
|
||||||
|
amount,
|
||||||
|
amount_blinding_factor);
|
||||||
|
|
||||||
|
ASSERT_EQ(proposal.amount, amount);
|
||||||
|
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||||
|
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||||
|
enote.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
crypto::secret_key recovered_sender_extension_g;
|
||||||
|
crypto::secret_key recovered_sender_extension_t;
|
||||||
|
crypto::public_key recovered_address_spend_pubkey;
|
||||||
|
rct::xmr_amount recovered_amount;
|
||||||
|
crypto::secret_key recovered_amount_blinding_factor;
|
||||||
|
encrypted_payment_id_t recovered_payment_id;
|
||||||
|
CarrotEnoteType recovered_enote_type;
|
||||||
|
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||||
|
encrypted_payment_id,
|
||||||
|
s_sender_receiver_unctx,
|
||||||
|
keys.k_view_dev,
|
||||||
|
keys.account_spend_pubkey,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
recovered_address_spend_pubkey,
|
||||||
|
recovered_amount,
|
||||||
|
recovered_amount_blinding_factor,
|
||||||
|
recovered_payment_id,
|
||||||
|
recovered_enote_type);
|
||||||
|
|
||||||
|
ASSERT_TRUE(scan_success);
|
||||||
|
|
||||||
|
// check recovered data
|
||||||
|
EXPECT_EQ(proposal.destination.address_spend_pubkey, recovered_address_spend_pubkey);
|
||||||
|
EXPECT_EQ(amount, recovered_amount);
|
||||||
|
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||||
|
EXPECT_EQ(integrated_address.payment_id, recovered_payment_id);
|
||||||
|
EXPECT_EQ(CarrotEnoteType::PAYMENT, recovered_enote_type);
|
||||||
|
|
||||||
|
// check spendability
|
||||||
|
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||||
|
keys.k_generate_image,
|
||||||
|
rct::rct2sk(rct::I),
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
enote.onetime_address));
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, main_address_special_scan_completeness)
|
||||||
|
{
|
||||||
|
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||||
|
|
||||||
|
CarrotDestinationV1 main_address;
|
||||||
|
make_carrot_main_address_v1(keys.account_spend_pubkey, keys.main_address_view_pubkey, main_address);
|
||||||
|
|
||||||
|
// try once with PAYMENT, once with CHANGE
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
const CarrotEnoteType enote_type = i ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE;
|
||||||
|
|
||||||
|
const CarrotPaymentProposalSelfSendV1 proposal = CarrotPaymentProposalSelfSendV1{
|
||||||
|
.destination_address_spend_pubkey = keys.account_spend_pubkey,
|
||||||
|
.amount = crypto::rand<rct::xmr_amount>(),
|
||||||
|
.enote_type = enote_type,
|
||||||
|
.enote_ephemeral_pubkey = crypto::x25519_pubkey_gen(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||||
|
|
||||||
|
CarrotEnoteV1 enote;
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
crypto::secret_key amount_blinding_factor;
|
||||||
|
get_output_proposal_special_v1(proposal,
|
||||||
|
keys.k_view,
|
||||||
|
keys.account_spend_pubkey,
|
||||||
|
tx_first_key_image,
|
||||||
|
enote,
|
||||||
|
amount,
|
||||||
|
amount_blinding_factor);
|
||||||
|
|
||||||
|
ASSERT_EQ(proposal.amount, amount);
|
||||||
|
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||||
|
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||||
|
enote.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
crypto::secret_key recovered_sender_extension_g;
|
||||||
|
crypto::secret_key recovered_sender_extension_t;
|
||||||
|
crypto::public_key recovered_address_spend_pubkey;
|
||||||
|
rct::xmr_amount recovered_amount;
|
||||||
|
crypto::secret_key recovered_amount_blinding_factor;
|
||||||
|
encrypted_payment_id_t recovered_payment_id;
|
||||||
|
CarrotEnoteType recovered_enote_type;
|
||||||
|
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||||
|
std::nullopt,
|
||||||
|
s_sender_receiver_unctx,
|
||||||
|
keys.k_view_dev,
|
||||||
|
keys.account_spend_pubkey,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
recovered_address_spend_pubkey,
|
||||||
|
recovered_amount,
|
||||||
|
recovered_amount_blinding_factor,
|
||||||
|
recovered_payment_id,
|
||||||
|
recovered_enote_type);
|
||||||
|
|
||||||
|
ASSERT_TRUE(scan_success);
|
||||||
|
|
||||||
|
// check recovered data
|
||||||
|
EXPECT_EQ(proposal.destination_address_spend_pubkey, recovered_address_spend_pubkey);
|
||||||
|
EXPECT_EQ(amount, recovered_amount);
|
||||||
|
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||||
|
EXPECT_EQ(null_payment_id, recovered_payment_id);
|
||||||
|
EXPECT_EQ(enote_type, recovered_enote_type);
|
||||||
|
|
||||||
|
// check spendability
|
||||||
|
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||||
|
keys.k_generate_image,
|
||||||
|
rct::rct2sk(rct::I),
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
enote.onetime_address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, subaddress_special_scan_completeness)
|
||||||
|
{
|
||||||
|
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||||
|
|
||||||
|
const uint32_t j_major = crypto::rand<uint32_t>();
|
||||||
|
const uint32_t j_minor = crypto::rand<uint32_t>();
|
||||||
|
|
||||||
|
CarrotDestinationV1 subaddress;
|
||||||
|
make_carrot_subaddress_v1(keys.account_spend_pubkey,
|
||||||
|
keys.account_view_pubkey,
|
||||||
|
keys.s_generate_address,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
subaddress);
|
||||||
|
|
||||||
|
// try once with PAYMENT, once with CHANGE
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
const CarrotEnoteType enote_type = i ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE;
|
||||||
|
|
||||||
|
const CarrotPaymentProposalSelfSendV1 proposal = CarrotPaymentProposalSelfSendV1{
|
||||||
|
.destination_address_spend_pubkey = subaddress.address_spend_pubkey,
|
||||||
|
.amount = crypto::rand<rct::xmr_amount>(),
|
||||||
|
.enote_type = enote_type,
|
||||||
|
.enote_ephemeral_pubkey = crypto::x25519_pubkey_gen(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||||
|
|
||||||
|
CarrotEnoteV1 enote;
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
crypto::secret_key amount_blinding_factor;
|
||||||
|
get_output_proposal_special_v1(proposal,
|
||||||
|
keys.k_view,
|
||||||
|
keys.account_spend_pubkey,
|
||||||
|
tx_first_key_image,
|
||||||
|
enote,
|
||||||
|
amount,
|
||||||
|
amount_blinding_factor);
|
||||||
|
|
||||||
|
ASSERT_EQ(proposal.amount, amount);
|
||||||
|
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||||
|
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||||
|
enote.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
crypto::secret_key recovered_sender_extension_g;
|
||||||
|
crypto::secret_key recovered_sender_extension_t;
|
||||||
|
crypto::public_key recovered_address_spend_pubkey;
|
||||||
|
rct::xmr_amount recovered_amount;
|
||||||
|
crypto::secret_key recovered_amount_blinding_factor;
|
||||||
|
encrypted_payment_id_t recovered_payment_id;
|
||||||
|
CarrotEnoteType recovered_enote_type;
|
||||||
|
const bool scan_success = try_scan_carrot_enote_external(enote,
|
||||||
|
std::nullopt,
|
||||||
|
s_sender_receiver_unctx,
|
||||||
|
keys.k_view_dev,
|
||||||
|
keys.account_spend_pubkey,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
recovered_address_spend_pubkey,
|
||||||
|
recovered_amount,
|
||||||
|
recovered_amount_blinding_factor,
|
||||||
|
recovered_payment_id,
|
||||||
|
recovered_enote_type);
|
||||||
|
|
||||||
|
ASSERT_TRUE(scan_success);
|
||||||
|
|
||||||
|
// check recovered data
|
||||||
|
EXPECT_EQ(proposal.destination_address_spend_pubkey, recovered_address_spend_pubkey);
|
||||||
|
EXPECT_EQ(amount, recovered_amount);
|
||||||
|
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||||
|
EXPECT_EQ(null_payment_id, recovered_payment_id);
|
||||||
|
EXPECT_EQ(enote_type, recovered_enote_type);
|
||||||
|
|
||||||
|
// check spendability
|
||||||
|
crypto::secret_key address_generator;
|
||||||
|
make_carrot_index_extension_generator(keys.s_generate_address,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
address_generator);
|
||||||
|
|
||||||
|
crypto::secret_key subaddr_scalar;
|
||||||
|
make_carrot_subaddress_scalar(keys.account_spend_pubkey,
|
||||||
|
address_generator,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
subaddr_scalar);
|
||||||
|
|
||||||
|
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||||
|
keys.k_generate_image,
|
||||||
|
subaddr_scalar,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
enote.onetime_address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, main_address_internal_scan_completeness)
|
||||||
|
{
|
||||||
|
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||||
|
|
||||||
|
CarrotDestinationV1 main_address;
|
||||||
|
make_carrot_main_address_v1(keys.account_spend_pubkey, keys.main_address_view_pubkey, main_address);
|
||||||
|
|
||||||
|
// try once with PAYMENT, once with CHANGE
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
const CarrotEnoteType enote_type = i ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE;
|
||||||
|
|
||||||
|
const CarrotPaymentProposalSelfSendV1 proposal = CarrotPaymentProposalSelfSendV1{
|
||||||
|
.destination_address_spend_pubkey = main_address.address_spend_pubkey,
|
||||||
|
.amount = crypto::rand<rct::xmr_amount>(),
|
||||||
|
.enote_type = enote_type,
|
||||||
|
.enote_ephemeral_pubkey = crypto::x25519_pubkey_gen()
|
||||||
|
};
|
||||||
|
|
||||||
|
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||||
|
|
||||||
|
CarrotEnoteV1 enote;
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
crypto::secret_key amount_blinding_factor;
|
||||||
|
get_output_proposal_internal_v1(proposal,
|
||||||
|
keys.s_view_balance,
|
||||||
|
tx_first_key_image,
|
||||||
|
enote,
|
||||||
|
amount,
|
||||||
|
amount_blinding_factor);
|
||||||
|
|
||||||
|
ASSERT_EQ(proposal.amount, amount);
|
||||||
|
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||||
|
|
||||||
|
crypto::secret_key recovered_sender_extension_g;
|
||||||
|
crypto::secret_key recovered_sender_extension_t;
|
||||||
|
crypto::public_key recovered_address_spend_pubkey;
|
||||||
|
rct::xmr_amount recovered_amount;
|
||||||
|
crypto::secret_key recovered_amount_blinding_factor;
|
||||||
|
CarrotEnoteType recovered_enote_type;
|
||||||
|
const bool scan_success = try_scan_carrot_enote_internal(enote,
|
||||||
|
keys.s_view_balance_dev,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
recovered_address_spend_pubkey,
|
||||||
|
recovered_amount,
|
||||||
|
recovered_amount_blinding_factor,
|
||||||
|
recovered_enote_type);
|
||||||
|
|
||||||
|
ASSERT_TRUE(scan_success);
|
||||||
|
|
||||||
|
// check recovered data
|
||||||
|
EXPECT_EQ(proposal.destination_address_spend_pubkey, recovered_address_spend_pubkey);
|
||||||
|
EXPECT_EQ(amount, recovered_amount);
|
||||||
|
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||||
|
EXPECT_EQ(enote_type, recovered_enote_type);
|
||||||
|
|
||||||
|
// check spendability
|
||||||
|
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||||
|
keys.k_generate_image,
|
||||||
|
rct::rct2sk(rct::I),
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
enote.onetime_address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, subaddress_internal_scan_completeness)
|
||||||
|
{
|
||||||
|
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||||
|
|
||||||
|
const uint32_t j_major = crypto::rand<uint32_t>();
|
||||||
|
const uint32_t j_minor = crypto::rand<uint32_t>();
|
||||||
|
|
||||||
|
CarrotDestinationV1 subaddress;
|
||||||
|
make_carrot_subaddress_v1(keys.account_spend_pubkey,
|
||||||
|
keys.account_view_pubkey,
|
||||||
|
keys.s_generate_address,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
subaddress);
|
||||||
|
|
||||||
|
// try once with PAYMENT, once with CHANGE
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
const CarrotEnoteType enote_type = i ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE;
|
||||||
|
|
||||||
|
const CarrotPaymentProposalSelfSendV1 proposal = CarrotPaymentProposalSelfSendV1{
|
||||||
|
.destination_address_spend_pubkey = subaddress.address_spend_pubkey,
|
||||||
|
.amount = crypto::rand<rct::xmr_amount>(),
|
||||||
|
.enote_type = enote_type,
|
||||||
|
.enote_ephemeral_pubkey = crypto::x25519_pubkey_gen()
|
||||||
|
};
|
||||||
|
|
||||||
|
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
|
||||||
|
|
||||||
|
CarrotEnoteV1 enote;
|
||||||
|
rct::xmr_amount amount;
|
||||||
|
crypto::secret_key amount_blinding_factor;
|
||||||
|
get_output_proposal_internal_v1(proposal,
|
||||||
|
keys.s_view_balance,
|
||||||
|
tx_first_key_image,
|
||||||
|
enote,
|
||||||
|
amount,
|
||||||
|
amount_blinding_factor);
|
||||||
|
|
||||||
|
ASSERT_EQ(proposal.amount, amount);
|
||||||
|
ASSERT_EQ(enote.amount_commitment, rct::commit(amount, rct::sk2rct(amount_blinding_factor)));
|
||||||
|
|
||||||
|
crypto::secret_key recovered_sender_extension_g;
|
||||||
|
crypto::secret_key recovered_sender_extension_t;
|
||||||
|
crypto::public_key recovered_address_spend_pubkey;
|
||||||
|
rct::xmr_amount recovered_amount;
|
||||||
|
crypto::secret_key recovered_amount_blinding_factor;
|
||||||
|
CarrotEnoteType recovered_enote_type;
|
||||||
|
const bool scan_success = try_scan_carrot_enote_internal(enote,
|
||||||
|
keys.s_view_balance_dev,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
recovered_address_spend_pubkey,
|
||||||
|
recovered_amount,
|
||||||
|
recovered_amount_blinding_factor,
|
||||||
|
recovered_enote_type);
|
||||||
|
|
||||||
|
ASSERT_TRUE(scan_success);
|
||||||
|
|
||||||
|
// check recovered data
|
||||||
|
EXPECT_EQ(proposal.destination_address_spend_pubkey, recovered_address_spend_pubkey);
|
||||||
|
EXPECT_EQ(amount, recovered_amount);
|
||||||
|
EXPECT_EQ(amount_blinding_factor, recovered_amount_blinding_factor);
|
||||||
|
EXPECT_EQ(enote_type, recovered_enote_type);
|
||||||
|
|
||||||
|
// check spendability
|
||||||
|
crypto::secret_key address_generator;
|
||||||
|
make_carrot_index_extension_generator(keys.s_generate_address,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
address_generator);
|
||||||
|
|
||||||
|
crypto::secret_key subaddr_scalar;
|
||||||
|
make_carrot_subaddress_scalar(keys.account_spend_pubkey,
|
||||||
|
address_generator,
|
||||||
|
j_major,
|
||||||
|
j_minor,
|
||||||
|
subaddr_scalar);
|
||||||
|
|
||||||
|
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||||
|
keys.k_generate_image,
|
||||||
|
subaddr_scalar,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
enote.onetime_address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
TEST(carrot_core, main_address_coinbase_scan_completeness)
|
||||||
|
{
|
||||||
|
const mock_carrot_keys keys = mock_carrot_keys::generate();
|
||||||
|
|
||||||
|
CarrotDestinationV1 main_address;
|
||||||
|
make_carrot_main_address_v1(keys.account_spend_pubkey, keys.main_address_view_pubkey, main_address);
|
||||||
|
|
||||||
|
const CarrotPaymentProposalV1 proposal = CarrotPaymentProposalV1{
|
||||||
|
.destination = main_address,
|
||||||
|
.amount = crypto::rand<rct::xmr_amount>(),
|
||||||
|
.randomness = gen_janus_anchor()
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint64_t block_index = crypto::rand<uint64_t>();
|
||||||
|
|
||||||
|
CarrotCoinbaseEnoteV1 enote;
|
||||||
|
get_coinbase_output_proposal_v1(proposal,
|
||||||
|
block_index,
|
||||||
|
enote);
|
||||||
|
|
||||||
|
ASSERT_EQ(proposal.amount, enote.amount);
|
||||||
|
|
||||||
|
crypto::x25519_pubkey s_sender_receiver_unctx;
|
||||||
|
make_carrot_uncontextualized_shared_key_receiver(keys.k_view,
|
||||||
|
enote.enote_ephemeral_pubkey,
|
||||||
|
s_sender_receiver_unctx);
|
||||||
|
|
||||||
|
crypto::secret_key recovered_sender_extension_g;
|
||||||
|
crypto::secret_key recovered_sender_extension_t;
|
||||||
|
crypto::public_key recovered_address_spend_pubkey;
|
||||||
|
const bool scan_success = try_scan_carrot_coinbase_enote(enote,
|
||||||
|
s_sender_receiver_unctx,
|
||||||
|
keys.k_view_dev,
|
||||||
|
keys.account_spend_pubkey,
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
recovered_address_spend_pubkey);
|
||||||
|
|
||||||
|
ASSERT_TRUE(scan_success);
|
||||||
|
|
||||||
|
// check recovered data
|
||||||
|
EXPECT_EQ(proposal.destination.address_spend_pubkey, recovered_address_spend_pubkey);
|
||||||
|
|
||||||
|
// check spendability
|
||||||
|
EXPECT_TRUE(can_open_fcmp_onetime_address(keys.k_prove_spend,
|
||||||
|
keys.k_generate_image,
|
||||||
|
rct::rct2sk(rct::I),
|
||||||
|
recovered_sender_extension_g,
|
||||||
|
recovered_sender_extension_t,
|
||||||
|
enote.onetime_address));
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
60
tests/unit_tests/carrot_transcript_fixed.cpp
Normal file
60
tests/unit_tests/carrot_transcript_fixed.cpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "carrot_core/config.h"
|
||||||
|
#include "carrot_core/core_types.h"
|
||||||
|
#include "carrot_core/transcript_fixed.h"
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
TEST(carrot_transcript_fixed, sizeof_sum)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(0, sp::detail::sizeof_sum<>());
|
||||||
|
EXPECT_EQ(1, sp::detail::sizeof_sum<unsigned char>());
|
||||||
|
EXPECT_EQ(12, (sp::detail::sizeof_sum<uint64_t, uint32_t>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(carrot_transcript_fixed, ts_size)
|
||||||
|
{
|
||||||
|
static constexpr const unsigned char DS1[] = "perspicacious";
|
||||||
|
const auto transcript1 = sp::make_fixed_transcript<DS1>((uint32_t)32);
|
||||||
|
EXPECT_EQ(1 + 13 + 4, transcript1.size());
|
||||||
|
|
||||||
|
static constexpr const unsigned char DS2[] = "recrudescence";
|
||||||
|
const auto transcript2 = sp::make_fixed_transcript<DS2>((uint32_t)32, (uint64_t)64);
|
||||||
|
EXPECT_EQ(1 + 13 + 4 + 8, transcript2.size());
|
||||||
|
|
||||||
|
// vt = H_3(s_sr || input_context || Ko)
|
||||||
|
const auto transcript_vt = sp::make_fixed_transcript<carrot::CARROT_DOMAIN_SEP_VIEW_TAG>(
|
||||||
|
carrot::input_context_t{},
|
||||||
|
crypto::public_key{});
|
||||||
|
EXPECT_EQ(1 + 15 + 33 + 32, transcript_vt.size());
|
||||||
|
}
|
@ -36,6 +36,7 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#include "crypto/crypto-ops.h"
|
#include "crypto/crypto-ops.h"
|
||||||
}
|
}
|
||||||
|
#include "crypto/x25519.h"
|
||||||
#include "crypto/generators.h"
|
#include "crypto/generators.h"
|
||||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||||
#include "cryptonote_basic/merge_mining.h"
|
#include "cryptonote_basic/merge_mining.h"
|
||||||
@ -345,3 +346,104 @@ TEST(Crypto, generator_consistency)
|
|||||||
// ringct/rctTypes.h
|
// ringct/rctTypes.h
|
||||||
ASSERT_TRUE(memcmp(H.data, rct::H.bytes, 32) == 0);
|
ASSERT_TRUE(memcmp(H.data, rct::H.bytes, 32) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Crypto, ConvertPointE_Base)
|
||||||
|
{
|
||||||
|
const crypto::public_key G = crypto::get_G();
|
||||||
|
const crypto::x25519_pubkey B_expected = {{9}};
|
||||||
|
|
||||||
|
crypto::x25519_pubkey B_actual;
|
||||||
|
edwards_bytes_to_x25519_vartime(B_actual.data, to_bytes(G));
|
||||||
|
|
||||||
|
EXPECT_EQ(B_expected, B_actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Crypto, ConvertPointE_PreserveScalarMultBase)
|
||||||
|
{
|
||||||
|
// *clamped* private key a
|
||||||
|
const crypto::x25519_secret_key a = crypto::x25519_secret_key_gen();
|
||||||
|
rct::key a_key;
|
||||||
|
memcpy(&a_key, &a, sizeof(rct::key));
|
||||||
|
|
||||||
|
// P_ed = a G
|
||||||
|
const rct::key P_edward = rct::scalarmultBase(a_key);
|
||||||
|
|
||||||
|
// P_mont = a B
|
||||||
|
crypto::x25519_pubkey P_mont;
|
||||||
|
crypto::x25519_scmul_base(a, P_mont);
|
||||||
|
|
||||||
|
// P_mont' = ConvertPointE(P_ed)
|
||||||
|
crypto::x25519_pubkey P_mont_converted;
|
||||||
|
edwards_bytes_to_x25519_vartime(P_mont_converted.data, P_edward.bytes);
|
||||||
|
|
||||||
|
// P_mont' ?= P_mont
|
||||||
|
EXPECT_EQ(P_mont_converted, P_mont);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Crypto, ConvertPointE_PreserveScalarMultBase_gep3)
|
||||||
|
{
|
||||||
|
// compared to ConvertPointE_PreserveScalarMultBase, this test will use Z != 1 (probably)
|
||||||
|
|
||||||
|
// *clamped* private key a
|
||||||
|
const crypto::x25519_secret_key a = crypto::x25519_secret_key_gen();
|
||||||
|
rct::key a_key;
|
||||||
|
memcpy(&a_key, &a, sizeof(rct::key));
|
||||||
|
|
||||||
|
// P_ed = a G
|
||||||
|
ge_p3 P_p3;
|
||||||
|
ge_scalarmult_base(&P_p3, a.data);
|
||||||
|
|
||||||
|
// check that Z != 1, otherwise this test is a dup of ConvertPointE_PreserveScalarMultBase
|
||||||
|
const unsigned char one_bytes[32] = {1};
|
||||||
|
unsigned char Z_bytes[32];
|
||||||
|
fe_tobytes(Z_bytes, P_p3.Z);
|
||||||
|
ASSERT_TRUE(memcmp(Z_bytes, one_bytes, 32)); // check Z != 1
|
||||||
|
|
||||||
|
// P_mont = a B
|
||||||
|
crypto::x25519_pubkey P_mont;
|
||||||
|
crypto::x25519_scmul_base(a, P_mont);
|
||||||
|
|
||||||
|
// P_mont' = ConvertPointE(P_ed)
|
||||||
|
crypto::x25519_pubkey P_mont_converted;
|
||||||
|
ge_p3_to_x25519(P_mont_converted.data, &P_p3);
|
||||||
|
|
||||||
|
// P_mont' ?= P_mont
|
||||||
|
EXPECT_EQ(P_mont_converted, P_mont);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Crypto, ConvertPointE_EraseSign)
|
||||||
|
{
|
||||||
|
// generate a random point P and test that ConvertPointE(P) == ConvertPointE(-P)
|
||||||
|
|
||||||
|
const rct::key P = rct::pkGen();
|
||||||
|
rct::key negP;
|
||||||
|
rct::subKeys(negP, rct::I, P);
|
||||||
|
|
||||||
|
crypto::x25519_pubkey P_mont;
|
||||||
|
edwards_bytes_to_x25519_vartime(P_mont.data, P.bytes);
|
||||||
|
|
||||||
|
crypto::x25519_pubkey negP_mont;
|
||||||
|
edwards_bytes_to_x25519_vartime(negP_mont.data, negP.bytes);
|
||||||
|
|
||||||
|
EXPECT_EQ(P_mont, negP_mont);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Crypto, ge_fromx25519_vartime_Base)
|
||||||
|
{
|
||||||
|
const crypto::x25519_pubkey B = {{9}};
|
||||||
|
|
||||||
|
crypto::public_key G_actual;
|
||||||
|
ge_p3 G_actual_p3;
|
||||||
|
ge_fromx25519_vartime(&G_actual_p3, B.data);
|
||||||
|
ge_p3_tobytes(to_bytes(G_actual), &G_actual_p3);
|
||||||
|
|
||||||
|
EXPECT_EQ(crypto::get_G(), G_actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Crypto, ge_fromx25519_vartime_RandomPointNominalSuccess)
|
||||||
|
{
|
||||||
|
const crypto::x25519_pubkey P = crypto::x25519_pubkey_gen();
|
||||||
|
|
||||||
|
ge_p3 h;
|
||||||
|
EXPECT_EQ(0, ge_fromx25519_vartime(&h, P.data));
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
using tools::optional_variant;
|
||||||
using tools::variant;
|
using tools::variant;
|
||||||
using tools::variant_static_visitor;
|
using tools::variant_static_visitor;
|
||||||
|
|
||||||
@ -239,7 +240,7 @@ struct test_stringify_visitor: public variant_static_visitor<std::string>
|
|||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
TEST(variant, operatorbool)
|
TEST(variant, operatorbool)
|
||||||
{
|
{
|
||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
optional_variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
EXPECT_FALSE(v);
|
EXPECT_FALSE(v);
|
||||||
v = (int16_t) 2023;
|
v = (int16_t) 2023;
|
||||||
EXPECT_TRUE(v);
|
EXPECT_TRUE(v);
|
||||||
@ -251,7 +252,7 @@ TEST(variant, operatorbool)
|
|||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
TEST(variant, is_empty)
|
TEST(variant, is_empty)
|
||||||
{
|
{
|
||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
optional_variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
EXPECT_TRUE(v.is_empty());
|
EXPECT_TRUE(v.is_empty());
|
||||||
v = (int16_t) 2023;
|
v = (int16_t) 2023;
|
||||||
EXPECT_FALSE(v.is_empty());
|
EXPECT_FALSE(v.is_empty());
|
||||||
@ -260,7 +261,7 @@ TEST(variant, is_empty)
|
|||||||
v = boost::blank{};
|
v = boost::blank{};
|
||||||
EXPECT_TRUE(v.is_empty());
|
EXPECT_TRUE(v.is_empty());
|
||||||
|
|
||||||
variant<> v2;
|
optional_variant<> v2;
|
||||||
EXPECT_TRUE(v2.is_empty());
|
EXPECT_TRUE(v2.is_empty());
|
||||||
v2 = boost::blank{};
|
v2 = boost::blank{};
|
||||||
EXPECT_TRUE(v2.is_empty());
|
EXPECT_TRUE(v2.is_empty());
|
||||||
@ -269,7 +270,7 @@ TEST(variant, is_empty)
|
|||||||
TEST(variant, is_type)
|
TEST(variant, is_type)
|
||||||
{
|
{
|
||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
EXPECT_TRUE(v.is_type<boost::blank>());
|
EXPECT_TRUE(v.is_type<int8_t>());
|
||||||
v = (int16_t) 2023;
|
v = (int16_t) 2023;
|
||||||
EXPECT_TRUE(v.is_type<int16_t>());
|
EXPECT_TRUE(v.is_type<int16_t>());
|
||||||
|
|
||||||
@ -279,7 +280,7 @@ TEST(variant, is_type)
|
|||||||
TEST(variant, try_unwrap)
|
TEST(variant, try_unwrap)
|
||||||
{
|
{
|
||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
EXPECT_FALSE(v.try_unwrap<int8_t>());
|
EXPECT_TRUE(v.try_unwrap<int8_t>());
|
||||||
v = (int16_t) 5252;
|
v = (int16_t) 5252;
|
||||||
ASSERT_TRUE(v.try_unwrap<int16_t>());
|
ASSERT_TRUE(v.try_unwrap<int16_t>());
|
||||||
EXPECT_EQ(5252, *v.try_unwrap<int16_t>());
|
EXPECT_EQ(5252, *v.try_unwrap<int16_t>());
|
||||||
@ -290,7 +291,7 @@ TEST(variant, try_unwrap)
|
|||||||
TEST(variant, unwrap)
|
TEST(variant, unwrap)
|
||||||
{
|
{
|
||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
EXPECT_THROW(v.unwrap<int8_t>(), std::runtime_error);
|
EXPECT_EQ(0, v.unwrap<int8_t>());
|
||||||
v = (int16_t) 5252;
|
v = (int16_t) 5252;
|
||||||
EXPECT_EQ(5252, v.unwrap<int16_t>());
|
EXPECT_EQ(5252, v.unwrap<int16_t>());
|
||||||
EXPECT_THROW(v.unwrap<uint16_t>(), std::runtime_error);
|
EXPECT_THROW(v.unwrap<uint16_t>(), std::runtime_error);
|
||||||
@ -321,35 +322,55 @@ TEST(variant, index)
|
|||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
EXPECT_EQ(0, v.index());
|
EXPECT_EQ(0, v.index());
|
||||||
v = (int8_t) 7;
|
v = (int8_t) 7;
|
||||||
EXPECT_EQ(1, v.index());
|
EXPECT_EQ(0, v.index());
|
||||||
v = (uint8_t) 7;
|
v = (uint8_t) 7;
|
||||||
EXPECT_EQ(2, v.index());
|
EXPECT_EQ(1, v.index());
|
||||||
v = (int16_t) 7;
|
v = (int16_t) 7;
|
||||||
EXPECT_EQ(3, v.index());
|
EXPECT_EQ(2, v.index());
|
||||||
v = (uint16_t) 7;
|
v = (uint16_t) 7;
|
||||||
EXPECT_EQ(4, v.index());
|
EXPECT_EQ(3, v.index());
|
||||||
v = "verifiable variant vying for vengence versus visa";
|
v = "verifiable variant vying for vengence versus visa";
|
||||||
EXPECT_EQ(5, v.index());
|
EXPECT_EQ(4, v.index());
|
||||||
|
|
||||||
|
optional_variant<int8_t, uint8_t, int16_t, uint16_t, std::string> vo;
|
||||||
|
EXPECT_EQ(0, vo.index());
|
||||||
|
vo = (int8_t) 7;
|
||||||
|
EXPECT_EQ(1, vo.index());
|
||||||
|
vo = (uint8_t) 7;
|
||||||
|
EXPECT_EQ(2, vo.index());
|
||||||
|
vo = (int16_t) 7;
|
||||||
|
EXPECT_EQ(3, vo.index());
|
||||||
|
vo = (uint16_t) 7;
|
||||||
|
EXPECT_EQ(4, vo.index());
|
||||||
|
vo = "verifiable variant vying for vengence versus visa";
|
||||||
|
EXPECT_EQ(5, vo.index());
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
TEST(variant, type_index_of)
|
TEST(variant, type_index_of)
|
||||||
{
|
{
|
||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
EXPECT_EQ(0, decltype(v)::type_index_of<boost::blank>());
|
EXPECT_EQ(0, decltype(v)::type_index_of<int8_t>());
|
||||||
EXPECT_EQ(1, decltype(v)::type_index_of<int8_t>());
|
EXPECT_EQ(1, decltype(v)::type_index_of<uint8_t>());
|
||||||
EXPECT_EQ(2, decltype(v)::type_index_of<uint8_t>());
|
EXPECT_EQ(2, decltype(v)::type_index_of<int16_t>());
|
||||||
EXPECT_EQ(3, decltype(v)::type_index_of<int16_t>());
|
EXPECT_EQ(3, decltype(v)::type_index_of<uint16_t>());
|
||||||
EXPECT_EQ(4, decltype(v)::type_index_of<uint16_t>());
|
EXPECT_EQ(4, decltype(v)::type_index_of<std::string>());
|
||||||
EXPECT_EQ(5, decltype(v)::type_index_of<std::string>());
|
|
||||||
|
optional_variant<int8_t, uint8_t, int16_t, uint16_t, std::string> vo;
|
||||||
|
EXPECT_EQ(0, decltype(vo)::type_index_of<boost::blank>());
|
||||||
|
EXPECT_EQ(1, decltype(vo)::type_index_of<int8_t>());
|
||||||
|
EXPECT_EQ(2, decltype(vo)::type_index_of<uint8_t>());
|
||||||
|
EXPECT_EQ(3, decltype(vo)::type_index_of<int16_t>());
|
||||||
|
EXPECT_EQ(4, decltype(vo)::type_index_of<uint16_t>());
|
||||||
|
EXPECT_EQ(5, decltype(vo)::type_index_of<std::string>());
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
TEST(variant, constexpr_type_index_of)
|
TEST(variant, constexpr_type_index_of)
|
||||||
{
|
{
|
||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
constexpr int TINDEX0 = decltype(v)::type_index_of<boost::blank>();
|
constexpr int TINDEX2 = decltype(v)::type_index_of<int16_t>();
|
||||||
EXPECT_EQ(0, TINDEX0);
|
EXPECT_EQ(2, TINDEX2);
|
||||||
constexpr int TINDEX5 = decltype(v)::type_index_of<std::string>();
|
constexpr int TINDEX4 = decltype(v)::type_index_of<std::string>();
|
||||||
EXPECT_EQ(5, TINDEX5);
|
EXPECT_EQ(4, TINDEX4);
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
TEST(variant, same_type)
|
TEST(variant, same_type)
|
||||||
@ -362,7 +383,6 @@ TEST(variant, same_type)
|
|||||||
TEST(variant, visit)
|
TEST(variant, visit)
|
||||||
{
|
{
|
||||||
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
|
||||||
EXPECT_THROW(v.visit(test_stringify_visitor()), std::runtime_error);
|
|
||||||
|
|
||||||
v = "Rev";
|
v = "Rev";
|
||||||
test_stringify_visitor::test_visitation(v, std::string("Rev"));
|
test_stringify_visitor::test_visitation(v, std::string("Rev"));
|
||||||
@ -377,7 +397,7 @@ TEST(variant, ad_hoc_recursion)
|
|||||||
struct left_t;
|
struct left_t;
|
||||||
struct right_t;
|
struct right_t;
|
||||||
|
|
||||||
using twisty = variant<boost::recursive_wrapper<left_t>, boost::recursive_wrapper<right_t>>;
|
using twisty = optional_variant<boost::recursive_wrapper<left_t>, boost::recursive_wrapper<right_t>>;
|
||||||
|
|
||||||
struct left_t
|
struct left_t
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user