diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index e17584c90..5dfc121ce 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -33,6 +33,7 @@ set(crypto_sources crypto-ops-data.c crypto-ops.c crypto.cpp + generators.cpp groestl.c hash-extra-blake.c hash-extra-groestl.c diff --git a/src/crypto/generators.cpp b/src/crypto/generators.cpp new file mode 100644 index 000000000..a4539f473 --- /dev/null +++ b/src/crypto/generators.cpp @@ -0,0 +1,186 @@ +// 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. + +#include "generators.h" + +#include "crypto.h" +extern "C" +{ +#include "crypto-ops.h" +} +#include "hash.h" + +#include +#include +#include +#include + +namespace crypto +{ + +/// constexpr assert for old gcc bug: https://stackoverflow.com/questions/34280729/throw-in-constexpr-function +/// - this function won't compile in a constexpr context if b == false +constexpr void constexpr_assert(const bool b) { b ? 0 : throw std::runtime_error("constexpr assert failed"); }; + +/// constexpr paste bytes into an array-of-bytes type +template +constexpr T bytes_to(const std::initializer_list bytes) +{ + T out{}; // zero-initialize trailing bytes + + auto current = std::begin(out.data); + constexpr_assert(static_cast(bytes.size()) <= std::end(out.data) - current); + + for (const unsigned char byte : bytes) + *current++ = byte; + return out; +} + +// generators +//standard ed25519 generator G: {x, 4/5} (positive x when decompressing y = 4/5) +constexpr public_key G = bytes_to({ 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 }); +//pedersen commitment generator H: toPoint(cn_fast_hash(G)) +constexpr public_key H = bytes_to({ 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 }); +static ge_p3 G_p3; +static ge_p3 H_p3; +static ge_cached G_cached; +static ge_cached H_cached; + +// misc +static std::once_flag init_gens_once_flag; + +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +static public_key reproduce_generator_G() +{ + // G = {x, 4/5 mod q} + fe four, five, inv_five, y; + fe_0(four); + fe_0(five); + four[0] = 4; + five[0] = 5; + fe_invert(inv_five, five); + fe_mul(y, four, inv_five); + + public_key reproduced_G; + fe_tobytes(to_bytes(reproduced_G), y); + + return reproduced_G; +} +//------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------- +static public_key reproduce_generator_H() +{ + // H = 8*to_point(keccak(G)) + // note: this does not use the point_from_bytes() function found in H_p(), instead directly interpreting the + // input bytes as a compressed point (this can fail, so should not be used generically) + // note2: to_point(keccak(G)) is known to succeed for the canonical value of G (it will fail 7/8ths of the time + // normally) + ge_p3 temp_p3; + ge_p2 temp_p2; + ge_p1p1 temp_p1p1; + + hash H_temp_hash{cn_fast_hash(to_bytes(G), sizeof(ec_point))}; + (void)H_temp_hash; //suppress unused warning + assert(ge_frombytes_vartime(&temp_p3, reinterpret_cast(&H_temp_hash)) == 0); + ge_p3_to_p2(&temp_p2, &temp_p3); + ge_mul8(&temp_p1p1, &temp_p2); + ge_p1p1_to_p3(&temp_p3, &temp_p1p1); + + public_key reproduced_H; + ge_p3_tobytes(to_bytes(reproduced_H), &temp_p3); + + return reproduced_H; +} +//------------------------------------------------------------------------------------------------------------------- +// Make generators, but only once +//------------------------------------------------------------------------------------------------------------------- +static void init_gens() +{ + std::call_once(init_gens_once_flag, + [&](){ + + // sanity check the generators + static_assert(static_cast(G.data[0]) == 0x58, "compile-time constant sanity check"); + static_assert(static_cast(H.data[0]) == 0x8b, "compile-time constant sanity check"); + + // build ge_p3 representations of generators + const int G_deserialize = ge_frombytes_vartime(&G_p3, to_bytes(G)); + const int H_deserialize = ge_frombytes_vartime(&H_p3, to_bytes(H)); + + (void)G_deserialize; assert(G_deserialize == 0); + (void)H_deserialize; assert(H_deserialize == 0); + + // get cached versions + ge_p3_to_cached(&G_cached, &G_p3); + ge_p3_to_cached(&H_cached, &H_p3); + + // in debug mode, check that generators are reproducible + (void)reproduce_generator_G; assert(reproduce_generator_G() == G); + (void)reproduce_generator_H; assert(reproduce_generator_H() == H); + + }); +} +//------------------------------------------------------------------------------------------------------------------- +public_key get_G() +{ + return G; +} +//------------------------------------------------------------------------------------------------------------------- +public_key get_H() +{ + return H; +} +//------------------------------------------------------------------------------------------------------------------- +ge_p3 get_G_p3() +{ + init_gens(); + return G_p3; +} +//------------------------------------------------------------------------------------------------------------------- +ge_p3 get_H_p3() +{ + init_gens(); + return H_p3; +} +//------------------------------------------------------------------------------------------------------------------- +ge_cached get_G_cached() +{ + init_gens(); + return G_cached; +} +//------------------------------------------------------------------------------------------------------------------- +ge_cached get_H_cached() +{ + init_gens(); + return H_cached; +} +//------------------------------------------------------------------------------------------------------------------- +} //namespace crypto diff --git a/src/crypto/generators.h b/src/crypto/generators.h new file mode 100644 index 000000000..797336203 --- /dev/null +++ b/src/crypto/generators.h @@ -0,0 +1,47 @@ +// 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. + +#pragma once + +extern "C" +{ +#include "crypto-ops.h" +} +#include "crypto.h" + +namespace crypto +{ + +public_key get_G(); +public_key get_H(); +ge_p3 get_G_p3(); +ge_p3 get_H_p3(); +ge_cached get_G_cached(); +ge_cached get_H_cached(); + +} //namespace crypto diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 4cac0b89f..e41f3955f 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -32,8 +32,15 @@ #include #include +extern "C" +{ +#include "crypto/crypto-ops.h" +} +#include "crypto/generators.h" #include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/merge_mining.h" +#include "ringct/rctOps.h" +#include "ringct/rctTypes.h" namespace { @@ -312,3 +319,20 @@ TEST(Crypto, tree_branch) } } } + +TEST(Crypto, generator_consistency) +{ + // crypto/generators.h + const crypto::public_key G{crypto::get_G()}; + const crypto::public_key H{crypto::get_H()}; + const ge_p3 H_p3 = crypto::get_H_p3(); + + // crypto/crypto-ops.h + ASSERT_TRUE(memcmp(&H_p3, &ge_p3_H, sizeof(ge_p3)) == 0); + + // ringct/rctOps.h + ASSERT_TRUE(memcmp(G.data, rct::G.bytes, 32) == 0); + + // ringct/rctTypes.h + ASSERT_TRUE(memcmp(H.data, rct::H.bytes, 32) == 0); +}