diff --git a/src/seraphis_crypto/CMakeLists.txt b/src/seraphis_crypto/CMakeLists.txt index aeae61ad1..a4d9a1987 100644 --- a/src/seraphis_crypto/CMakeLists.txt +++ b/src/seraphis_crypto/CMakeLists.txt @@ -27,7 +27,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(seraphis_crypto_sources - dummy.cpp) + sp_crypto_utils.cpp) monero_find_all_headers(seraphis_crypto_headers, "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/src/seraphis_crypto/dummy.cpp b/src/seraphis_crypto/dummy.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/seraphis_crypto/sp_crypto_utils.cpp b/src/seraphis_crypto/sp_crypto_utils.cpp new file mode 100644 index 000000000..8eed03bed --- /dev/null +++ b/src/seraphis_crypto/sp_crypto_utils.cpp @@ -0,0 +1,286 @@ +// 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 "sp_crypto_utils.h" + +//local headers +#include "crypto/crypto.h" +extern "C" +{ +#include "crypto/crypto-ops.h" +} +#include "crypto/x25519.h" +#include "misc_log_ex.h" +#include "ringct/rctOps.h" +#include "ringct/rctTypes.h" + +//third party headers +#include "boost/multiprecision/cpp_int.hpp" + +//standard headers +#include + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "seraphis" + +namespace sp +{ + +/// File-scope data + +// Useful scalar and group constants +static const rct::key ZERO = rct::zero(); +static const rct::key ONE = rct::identity(); +/// scalar: -1 mod q +static const rct::key MINUS_ONE = { {0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, + 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10} }; + +//------------------------------------------------------------------------------------------------------------------- +// Helper function for scalar inversion +// return: x*(y^2^n) +//------------------------------------------------------------------------------------------------------------------- +static rct::key sm(rct::key y, int n, const rct::key &x) +{ + while (n--) + sc_mul(y.bytes, y.bytes, y.bytes); + sc_mul(y.bytes, y.bytes, x.bytes); + return y; +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +rct::key minus_one() +{ + return MINUS_ONE; +} +//------------------------------------------------------------------------------------------------------------------- +rct::key invert(const rct::key &x) +{ + CHECK_AND_ASSERT_THROW_MES(!(x == ZERO), "Cannot invert zero!"); + + rct::key _1, _10, _100, _11, _101, _111, _1001, _1011, _1111; + + _1 = x; + sc_mul(_10.bytes, _1.bytes, _1.bytes); + sc_mul(_100.bytes, _10.bytes, _10.bytes); + sc_mul(_11.bytes, _10.bytes, _1.bytes); + sc_mul(_101.bytes, _10.bytes, _11.bytes); + sc_mul(_111.bytes, _10.bytes, _101.bytes); + sc_mul(_1001.bytes, _10.bytes, _111.bytes); + sc_mul(_1011.bytes, _10.bytes, _1001.bytes); + sc_mul(_1111.bytes, _100.bytes, _1011.bytes); + + rct::key inv; + sc_mul(inv.bytes, _1111.bytes, _1.bytes); + + inv = sm(inv, 123 + 3, _101); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 4, _1001); + inv = sm(inv, 2, _11); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 1 + 3, _101); + inv = sm(inv, 3 + 3, _101); + inv = sm(inv, 3, _111); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 2 + 3, _111); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 1 + 4, _1011); + inv = sm(inv, 2 + 4, _1011); + inv = sm(inv, 6 + 4, _1001); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 3 + 2, _11); + inv = sm(inv, 3 + 2, _11); + inv = sm(inv, 1 + 4, _1001); + inv = sm(inv, 1 + 3, _111); + inv = sm(inv, 2 + 4, _1111); + inv = sm(inv, 1 + 4, _1011); + inv = sm(inv, 3, _101); + inv = sm(inv, 2 + 4, _1111); + inv = sm(inv, 3, _101); + inv = sm(inv, 1 + 2, _11); + + // Confirm inversion + rct::key temp; + sc_mul(temp.bytes, x.bytes, inv.bytes); + CHECK_AND_ASSERT_THROW_MES(temp == ONE, "Scalar inversion failed!"); + + return inv; +} +//------------------------------------------------------------------------------------------------------------------- +void decompose(const std::size_t val, const std::size_t base, const std::size_t size, std::vector &r_out) +{ + CHECK_AND_ASSERT_THROW_MES(base > 1, "Bad decomposition parameters!"); + CHECK_AND_ASSERT_THROW_MES(size > 0, "Bad decomposition parameters!"); + CHECK_AND_ASSERT_THROW_MES(r_out.size() == size, "Bad decomposition result vector size!"); + + std::size_t temp = val; + + for (std::size_t i = 0; i < size; ++i) + { + std::size_t slot = std::pow(base, size - i - 1); + r_out[size - i - 1] = temp/slot; + temp -= slot*r_out[size - i - 1]; + } +} +//------------------------------------------------------------------------------------------------------------------- +rct::key kronecker_delta(const std::size_t x, const std::size_t y) +{ + if (x == y) + return ONE; + else + return ZERO; +} +//------------------------------------------------------------------------------------------------------------------- +rct::keyV convolve(const rct::keyV &x, const rct::keyV &y, const std::size_t m) +{ + CHECK_AND_ASSERT_THROW_MES(x.size() >= m, "Bad convolution parameters!"); + CHECK_AND_ASSERT_THROW_MES(y.size() == 2, "Bad convolution parameters!"); + + rct::key temp; + rct::keyV result; + result.resize(m + 1, ZERO); + + for (std::size_t i = 0; i < m; ++i) + { + for (std::size_t j = 0; j < 2; ++j) + { + sc_mul(temp.bytes, x[i].bytes, y[j].bytes); + sc_add(result[i + j].bytes, result[i + j].bytes, temp.bytes); + } + } + + return result; +} +//------------------------------------------------------------------------------------------------------------------- +rct::keyV powers_of_scalar(const rct::key &scalar, const std::size_t num_pows, const bool negate_all) +{ + if (num_pows == 0) + return rct::keyV{}; + + rct::keyV pows; + pows.resize(num_pows); + + if (negate_all) + pows[0] = MINUS_ONE; + else + pows[0] = ONE; + + for (std::size_t i = 1; i < num_pows; ++i) + { + sc_mul(pows[i].bytes, pows[i - 1].bytes, scalar.bytes); + } + + return pows; +} +//------------------------------------------------------------------------------------------------------------------- +void generate_proof_nonce(const rct::key &base, crypto::secret_key &nonce_out, rct::key &nonce_pub_out) +{ + // make proof nonce as crypto::secret_key + CHECK_AND_ASSERT_THROW_MES(!(base == rct::identity()), "Bad base for generating proof nonce!"); + + nonce_out = rct::rct2sk(ZERO); + + while (nonce_out == rct::rct2sk(ZERO) || nonce_pub_out == rct::identity()) + { + nonce_out = rct::rct2sk(rct::skGen()); + rct::scalarmultKey(nonce_pub_out, base, rct::sk2rct(nonce_out)); + } +} +//------------------------------------------------------------------------------------------------------------------- +void generate_proof_nonce(const rct::key &base, rct::key &nonce_out, rct::key &nonce_pub_out) +{ + // make proof nonce as rct::key + crypto::secret_key temp; + generate_proof_nonce(base, temp, nonce_pub_out); + nonce_out = rct::sk2rct(temp); +} +//------------------------------------------------------------------------------------------------------------------- +void subtract_secret_key_vectors(const std::vector &keys_A, + const std::vector &keys_B, + crypto::secret_key &result_out) +{ + result_out = rct::rct2sk(rct::zero()); + + // add keys_A + for (const crypto::secret_key &key_A : keys_A) + sc_add(to_bytes(result_out), to_bytes(result_out), to_bytes(key_A)); + + // subtract keys_B + for (const crypto::secret_key &key_B : keys_B) + sc_sub(to_bytes(result_out), to_bytes(result_out), to_bytes(key_B)); +} +//------------------------------------------------------------------------------------------------------------------- +void mask_key(const crypto::secret_key &mask, const rct::key &key, rct::key &masked_key_out) +{ + // K" = mask G + K + rct::addKeys1(masked_key_out, rct::sk2rct(mask), key); +} +//------------------------------------------------------------------------------------------------------------------- +crypto::secret_key add_secrets(const crypto::secret_key &a, const crypto::secret_key &b) +{ + crypto::secret_key temp; + sc_add(to_bytes(temp), to_bytes(a), to_bytes(b)); + return temp; +} +//------------------------------------------------------------------------------------------------------------------- +bool key_domain_is_prime_subgroup(const rct::key &check_key) +{ + // l*K ?= identity + ge_p3 check_key_p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&check_key_p3, check_key.bytes) == 0, "ge_frombytes_vartime failed"); + ge_scalarmult_p3(&check_key_p3, rct::curveOrder().bytes, &check_key_p3); + + return (ge_p3_is_point_at_infinity_vartime(&check_key_p3) != 0); +} +//------------------------------------------------------------------------------------------------------------------- +bool balance_check_equality(const rct::keyV &commitment_set1, const rct::keyV &commitment_set2) +{ + // balance check method chosen from perf test: tests/performance_tests/balance_check.h + return rct::equalKeys(rct::addKeys(commitment_set1), rct::addKeys(commitment_set2)); +} +//------------------------------------------------------------------------------------------------------------------- +bool balance_check_in_out_amnts(const std::vector &input_amounts, + const std::vector &output_amounts, + const rct::xmr_amount transaction_fee) +{ + boost::multiprecision::uint128_t input_sum{0}; + boost::multiprecision::uint128_t output_sum{0}; + + for (const auto amnt : input_amounts) + input_sum += amnt; + + for (const auto amnt : output_amounts) + output_sum += amnt; + output_sum += transaction_fee; + + return input_sum == output_sum; +} +//------------------------------------------------------------------------------------------------------------------- +} //namespace sp diff --git a/src/seraphis_crypto/sp_crypto_utils.h b/src/seraphis_crypto/sp_crypto_utils.h new file mode 100644 index 000000000..26c9bf14e --- /dev/null +++ b/src/seraphis_crypto/sp_crypto_utils.h @@ -0,0 +1,170 @@ +// 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. + +// Miscellaneous crypto utils for seraphis (also includes some more basic math utils). + +#pragma once + +//local headers +#include "crypto/crypto.h" +#include "ringct/rctTypes.h" + +//third party headers + +//standard headers +#include +#include + +//forward declarations + + +namespace sp +{ + +/// sortable key (e.g. for hash maps) +struct sortable_key +{ + unsigned char bytes[32]; + + sortable_key() = default; + sortable_key(const rct::key &rct_key) { memcpy(bytes, rct_key.bytes, 32); } + bool operator<(const sortable_key &other) const { return memcmp(bytes, other.bytes, 32) < 0; } +}; +inline const rct::key& sortable2rct(const sortable_key &k) { return reinterpret_cast(k); } + +/** +* brief: minus_one - -1 mod q +* return: -1 mod q +*/ +rct::key minus_one(); +/** +* brief: invert - invert a nonzero scalar +* param: x - scalar to invert +* return: (1/x) mod l +*/ +rct::key invert(const rct::key &x); +/** +* brief: decompose - decompose an integer with a fixed base and size +* val -> [_, _, ... ,_] +* - num slots = 'size' +* - numeric base = 'base' +* - PRECONDITION: 'size' must be large enough to accomodate all digits of the decomposed value; if it is too small then +* the last digit will not be mod the base +* e.g. if base = 2 then convert val to binary, if base = 10 then put its decimal digits into the return vector +* param: val - value to decompose +* param: base - numeric base for decomposing the value +* param: size - number of digits to record the value in +* outparam: r_out - decomposed val (little endian) +*/ +void decompose(const std::size_t val, const std::size_t base, const std::size_t size, std::vector &r_out); +/** +* brief: kronecker_delta - Kronecker delta +* param: x - first integer +* param: y - second integer +* return: 1 if x == y, else 0 +*/ +rct::key kronecker_delta(const std::size_t x, const std::size_t y); +/** +* brief: convolve - compute a convolution with a degree-one polynomial +* param: x - x_1, x_2, ..., x_m +* param: y - a, b +* param: m - number of elements to look at from x (only access up to x[m-1] in case x.size() > m) +* return: [a*x_1], [b*x_1 + a*x_2], ..., [b*x_{m - 2} + a*x_{m - 1}], [b*x_m] +*/ +rct::keyV convolve(const rct::keyV &x, const rct::keyV &y, const std::size_t m); +/** +* brief: powers_of_scalar - powers of a scalar +* param: scalar - scalar to take powers of +* param: num_pows - number of powers to take (0-indexed) +* param: negate_all - bool flag for negating all returned values +* return: (negate ? -1 : 1)*([scalar^0], [scalar^1], ..., [scalar^{num_pows - 1}]) +*/ +rct::keyV powers_of_scalar(const rct::key &scalar, const std::size_t num_pows, const bool negate_all = false); +/** +* brief: generate_proof_nonce - generate a random scalar and corresponding pubkey for use in a Schnorr-like signature +* opening +* param: base - base EC pubkey for the nonce term +* outparam: nonce_out - private key 'nonce' +* outparam: nonce_pub_out - public key 'nonce * base' +*/ +void generate_proof_nonce(const rct::key &base, crypto::secret_key &nonce_out, rct::key &nonce_pub_out); +void generate_proof_nonce(const rct::key &base, rct::key &nonce_out, rct::key &nonce_pub_out); +/** +* brief: subtract_secret_key_vectors - subtract one vector of secret keys from another +* sum(A) - sum(B) +* param: keys_A - first vector (addors) +* param: keys_B - second vector (subtractors) +* outparam: result_out - 'sum(A) - sum(B)' +*/ +void subtract_secret_key_vectors(const std::vector &keys_A, + const std::vector &keys_B, + crypto::secret_key &result_out); +/** +* brief: mask_key - commit to an EC key +* K" = mask G + K +* param: mask - commitment mask/blinding factor +* param: key - EC key to commit to +* outparam: masked_key_out - K", the masked key +*/ +void mask_key(const crypto::secret_key &mask, const rct::key &key, rct::key &masked_key_out); +/** +* brief: add_secrets - v = a + b +* K" = mask G + K +* param: mask - commitment mask/blinding factor +* param: key - EC key to commit to +* outparam: masked_key_out - K", the masked key +*/ +crypto::secret_key add_secrets(const crypto::secret_key &a, const crypto::secret_key &b); +/** +* brief: key_domain_is_prime_subgroup - check that input key is in the prime order EC subgroup +* l*K ?= identity +* param: check_key - key to check +* result: true if input key is in prime order EC subgroup +*/ +bool key_domain_is_prime_subgroup(const rct::key &check_key); +/** +* brief: balance_check_equality - balance check between two commitment sets using an equality test +* - i.e. sum(inputs) ?= sum(outputs) +* param: commitment_set1 - +* param: commitment_set2 - +* return: true/false on balance check result +*/ +bool balance_check_equality(const rct::keyV &commitment_set1, const rct::keyV &commitment_set2); +/** +* brief: balance_check_in_out_amnts - balance check between two sets of amounts +* - i.e. sum(inputs) ?= sum(outputs) + transaction_fee +* param: input_amounts - +* param: output_amounts - +* param: transaction_fee - +* return: true/false on balance check result +*/ +bool balance_check_in_out_amnts(const std::vector &input_amounts, + const std::vector &output_amounts, + const rct::xmr_amount transaction_fee); + +} //namespace sp