From 8f25f0d460d697214b48f0d62e90998ea4c49df9 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 26 Jun 2024 21:15:44 +0200 Subject: [PATCH 01/37] bitcoin-secp256k1 submodule added + native crypto test --- .gitmodules | 3 ++ contrib/CMakeLists.txt | 11 +++++ src/CMakeLists.txt | 2 + tests/functional_tests/crypto_tests.cpp | 60 +++++++++++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/.gitmodules b/.gitmodules index 97a855bd..57896bbb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "contrib/jwt-cpp"] path = contrib/jwt-cpp url = https://github.com/Thalhammer/jwt-cpp.git +[submodule "contrib/bitcoin-secp256k1"] + path = contrib/bitcoin-secp256k1 + url = https://github.com/bitcoin-core/secp256k1.git diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index abfc4885..c1a5535f 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -5,6 +5,14 @@ add_subdirectory(zlib) add_subdirectory(db) add_subdirectory(ethereum) +option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." OFF) +option(SECP256K1_BUILD_TESTS "Build tests." OFF) +option(SECP256K1_BUILD_EXHAUSTIVE_TESTS "Build exhaustive tests." OFF) +option(SECP256K1_BUILD_CTIME_TESTS "Build constant-time tests." OFF) +option(SECP256K1_BUILD_EXAMPLES "Build examples." OFF) +set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) +add_subdirectory(bitcoin-secp256k1) + if( NOT DISABLE_TOR) add_subdirectory(tor-connect) endif() @@ -23,6 +31,9 @@ set_property(TARGET libminiupnpc-static PROPERTY FOLDER "contrib") set_property(TARGET zlibstatic PROPERTY FOLDER "contrib") set_property(TARGET mdbx PROPERTY FOLDER "contrib") set_property(TARGET lmdb PROPERTY FOLDER "contrib") +set_property(TARGET secp256k1 PROPERTY FOLDER "contrib") +set_property(TARGET secp256k1_precomputed PROPERTY FOLDER "contrib") + if( NOT DISABLE_TOR) set_property(TARGET tor-connect PROPERTY FOLDER "contrib") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 200096f3..563b4266 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -116,6 +116,8 @@ else() endif() add_library(crypto ${CRYPTO}) +add_dependencies(crypto secp256k1) +target_link_libraries(crypto secp256k1) add_library(currency_core ${CURRENCY_CORE}) add_dependencies(currency_core version ${PCH_LIB_NAME}) diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index 546fff99..6cd876b5 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1896,6 +1896,66 @@ TEST(crypto, generators_precomp) #undef CHECK_PRECOMP } +#include "bitcoin-secp256k1/include/secp256k1.h" +TEST(crypto, secp256k1_ecdsa_native) +{ + bool r = false; + + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + uint8_t randomness[32]; + crypto::generate_random_bytes(sizeof randomness, randomness); + secp256k1_context_randomize(ctx, randomness); + + uint8_t seckey[32] = {}; + while(true) + { + crypto::generate_random_bytes(sizeof seckey, seckey); + if (secp256k1_ec_seckey_verify(ctx, seckey)) + break; + } + + secp256k1_pubkey pubkey{}; + ASSERT_TRUE(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)); + + uint8_t compressed_pubkey[33] = {}; + size_t output_len = sizeof compressed_pubkey; + ASSERT_TRUE(secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &output_len, &pubkey, SECP256K1_EC_COMPRESSED)); + ASSERT_TRUE(output_len == sizeof compressed_pubkey); + + + secp256k1_ecdsa_signature secp256k1_ecdsa_sig{}; + hash msg_hash = hash_helper_t::h("message"); + ASSERT_TRUE(secp256k1_ecdsa_sign(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)&msg_hash, seckey, NULL, NULL)); + + // Serialize the signature in a compact form. + unsigned char secp256k1_ecdsa_sig_serialized[64] = {}; + ASSERT_TRUE(secp256k1_ecdsa_signature_serialize_compact(ctx, secp256k1_ecdsa_sig_serialized, &secp256k1_ecdsa_sig)); + + // + // Verification + // + + secp256k1_ecdsa_sig = secp256k1_ecdsa_signature{}; + pubkey = secp256k1_pubkey{}; + + // Deserialize the signature. + ASSERT_TRUE(secp256k1_ecdsa_signature_parse_compact(ctx, &secp256k1_ecdsa_sig, secp256k1_ecdsa_sig_serialized)); + + // Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */ + ASSERT_TRUE(secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))); + + // verify a signature + ASSERT_TRUE(secp256k1_ecdsa_verify(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)&msg_hash, &pubkey)); + + // verify using a static context + ASSERT_TRUE(secp256k1_ecdsa_verify(secp256k1_context_static, &secp256k1_ecdsa_sig, (const unsigned char*)&msg_hash, &pubkey)); + + + // Epilogue + secp256k1_context_destroy(ctx); + return true; +} + // From 3b6fa728b7fd392bf878b824468567a7a9fd68cf Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 1 Jul 2024 14:14:00 +0200 Subject: [PATCH 02/37] added bitcoin-secp256k1 submodule at version 5.0 --- contrib/bitcoin-secp256k1 | 1 + 1 file changed, 1 insertion(+) create mode 160000 contrib/bitcoin-secp256k1 diff --git a/contrib/bitcoin-secp256k1 b/contrib/bitcoin-secp256k1 new file mode 160000 index 00000000..e3a885d4 --- /dev/null +++ b/contrib/bitcoin-secp256k1 @@ -0,0 +1 @@ +Subproject commit e3a885d42a7800c1ccebad94ad1e2b82c4df5c65 From 486fb05f73dbdf13fa3c86e34a80f01cca36c78a Mon Sep 17 00:00:00 2001 From: sowle Date: Mon, 1 Jul 2024 21:35:27 +0200 Subject: [PATCH 03/37] updated submodule bitcoin-secp256k1 in attempt to fix gcc compilation issue --- contrib/bitcoin-secp256k1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/bitcoin-secp256k1 b/contrib/bitcoin-secp256k1 index e3a885d4..a5269373 160000 --- a/contrib/bitcoin-secp256k1 +++ b/contrib/bitcoin-secp256k1 @@ -1 +1 @@ -Subproject commit e3a885d42a7800c1ccebad94ad1e2b82c4df5c65 +Subproject commit a5269373fa13ff845f654d81b90629dd78495641 From f05d14a944df040cb3d8e944009aa8700f66f80e Mon Sep 17 00:00:00 2001 From: sowle Date: Tue, 2 Jul 2024 13:15:48 +0200 Subject: [PATCH 04/37] crypto: basic eth signature implementation + functional test --- src/crypto/eth_signature.cpp | 125 ++++++++++++++++++++++++ src/crypto/eth_signature.h | 39 ++++++++ tests/functional_tests/crypto_tests.cpp | 27 +++++ 3 files changed, 191 insertions(+) create mode 100644 src/crypto/eth_signature.cpp create mode 100644 src/crypto/eth_signature.h diff --git a/src/crypto/eth_signature.cpp b/src/crypto/eth_signature.cpp new file mode 100644 index 00000000..3aaec568 --- /dev/null +++ b/src/crypto/eth_signature.cpp @@ -0,0 +1,125 @@ +// Copyright (c) 2024 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "eth_signature.h" +#include "crypto.h" +#include "bitcoin-secp256k1/include/secp256k1.h" +#include "random.h" +#include "misc_language.h" + + +namespace crypto +{ + bool generate_eth_key_pair(eth_secret_key& sec_key, eth_public_key& pub_key) noexcept + { + try + { + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){ + secp256k1_context_destroy(ctx); + ctx = nullptr; + }); + + uint8_t randomness[32]; + crypto::generate_random_bytes(sizeof randomness, randomness); + if (!secp256k1_context_randomize(ctx, randomness)) + return false; + + for(size_t i = 1024; i != 0; --i) + { + crypto::generate_random_bytes(sizeof sec_key, sec_key.data); + if (secp256k1_ec_seckey_verify(ctx, sec_key.data)) + break; + if (i == 1) + return false; + } + + secp256k1_pubkey uncompressed_pub_key{}; + if (!secp256k1_ec_pubkey_create(ctx, &uncompressed_pub_key, sec_key.data)) + return false; + + size_t output_len = sizeof pub_key; + if (!secp256k1_ec_pubkey_serialize(ctx, pub_key.data, &output_len, &uncompressed_pub_key, SECP256K1_EC_COMPRESSED)) + return false; + + return true; + } + catch(...) + { + return false; + } + } + + + // generates secp256k1 ECDSA signature + bool generate_eth_signature(const hash& m, const eth_secret_key& sec_key, eth_signature& sig) noexcept + { + try + { + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){ + secp256k1_context_destroy(ctx); + ctx = nullptr; + }); + + uint8_t randomness[32]; + crypto::generate_random_bytes(sizeof randomness, randomness); + if (!secp256k1_context_randomize(ctx, randomness)) + return false; + + secp256k1_ecdsa_signature secp256k1_ecdsa_sig{}; + if (!secp256k1_ecdsa_sign(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)m.data, sec_key.data, NULL, NULL)) + return false; + + if (!secp256k1_ecdsa_signature_serialize_compact(ctx, sig.data, &secp256k1_ecdsa_sig)) + return false; + + return true; + } + catch(...) + { + return false; + } + } + + // verifies secp256k1 ECDSA signature + bool verify_eth_signature(const hash& m, const eth_public_key& pub_key, const eth_signature& sig) noexcept + { + try + { + // TODO (performance) consider using secp256k1_context_static for verification -- sowle + + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){ + secp256k1_context_destroy(ctx); + ctx = nullptr; + }); + + uint8_t randomness[32]; + crypto::generate_random_bytes(sizeof randomness, randomness); + if (!secp256k1_context_randomize(ctx, randomness)) + return false; + + secp256k1_ecdsa_signature secp256k1_ecdsa_sig{}; + secp256k1_pubkey uncompressed_pub_key{}; + + if (!secp256k1_ecdsa_signature_parse_compact(ctx, &secp256k1_ecdsa_sig, sig.data)) + return false; + + if (!secp256k1_ec_pubkey_parse(ctx, &uncompressed_pub_key, pub_key.data, sizeof pub_key)) + return false; + + // verify a signature + if (!secp256k1_ecdsa_verify(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)m.data, &uncompressed_pub_key)) + return false; + + return true; + } + catch(...) + { + return false; + } + } + + +} // namespace crypto diff --git a/src/crypto/eth_signature.h b/src/crypto/eth_signature.h new file mode 100644 index 00000000..c0434525 --- /dev/null +++ b/src/crypto/eth_signature.h @@ -0,0 +1,39 @@ +// Copyright (c) 2024 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once +#include +#include "hash.h" + +namespace crypto +{ + + // secp256k1 public key in serialized (compressed) form that is used in Etherium + struct eth_public_key + { + uint8_t data[33]; + }; + + // secp256k1 secret key + struct eth_secret_key + { + uint8_t data[32]; + }; + + // secp256k1 ECDSA signature is serialized (compressed) form that is used in Etherium + struct eth_signature + { + uint8_t data[64]; + }; + + // generates secp256k1 keypair + bool generate_eth_key_pair(eth_secret_key& sec_key, eth_public_key& pub_key) noexcept; + + // generates secp256k1 ECDSA signature + bool generate_eth_signature(const hash& m, const eth_secret_key& sec_key, eth_signature& sig) noexcept; + + // verifies secp256k1 ECDSA signature + bool verify_eth_signature(const hash& m, const eth_public_key& pub_key, const eth_signature& sig) noexcept; + + +} // namespace crypto diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index 6cd876b5..a6907bef 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1957,6 +1957,33 @@ TEST(crypto, secp256k1_ecdsa_native) } +TEST(crypto, eth_signature_basics) +{ + eth_secret_key sk{}; + eth_public_key pk{}; + + ASSERT_TRUE(generate_eth_key_pair(sk, pk)); + + eth_signature sig{}; + hash m = hash_helper_t::h("How many of you have ever felt personally victimized by elliptic curves?"); + + ASSERT_TRUE(generate_eth_signature(m, sk, sig)); + + const eth_signature const_sig = sig; + ASSERT_TRUE(verify_eth_signature(m, pk, const_sig)); + + for(size_t i = 0; i < sizeof sig; ++i) + { + eth_signature bad_sig = sig; + bad_sig.data[i] ^= 1 + (rand() % 254); // xor with a number fom [1; 255] to make sure this byte will change + ASSERT_FALSE(verify_eth_signature(m, pk, bad_sig)); + } + + return true; +} + + + // // test's runner From d7cf27033c6f4cd8e218771b1a9d8bc2c2aa4777 Mon Sep 17 00:00:00 2001 From: sowle Date: Tue, 2 Jul 2024 13:16:30 +0200 Subject: [PATCH 05/37] forgotten include --- tests/functional_tests/crypto_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index a6907bef..d4217d47 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -20,6 +20,7 @@ #include "crypto/range_proofs.h" #include "../core_tests/random_helper.h" #include "crypto_torsion_elements.h" +#include "crypto/eth_signature.h" using namespace crypto; From ef7320496046bd4086e1e6a1cc93301ceffe2f5a Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 17 Jul 2024 19:43:53 +0200 Subject: [PATCH 06/37] added support for std::optional to src/serialization and boost serialization --- .../keyvalue_serialization_overloads.h | 23 ++++++++ src/serialization/boost_types.h | 55 ++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index cb88897f..23ad8fce 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -473,6 +473,29 @@ namespace epee return r; } //------------------------------------------------------------------------------------------------------------------- + //std::optional + template + bool kv_serialize(const std::optional& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if(d.has_value()) + { + return kv_serialize(*d, stg, hparent_section, pname); + } + return true; + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_unserialize(std::optional& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + d = t_type{}; + bool r = kv_unserialize(*d, stg, hparent_section, pname); + if (!r) + { + d = std::nullopt; + } + return r; + } + //------------------------------------------------------------------------------------------------------------------- //boost::shared_ptr template bool kv_serialize(const boost::shared_ptr& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) diff --git a/src/serialization/boost_types.h b/src/serialization/boost_types.h index 154705b8..ca3e3d7b 100644 --- a/src/serialization/boost_types.h +++ b/src/serialization/boost_types.h @@ -1,3 +1,4 @@ +// Copyright (c) 2018-2024 Zano Project // Copyright (c) 2014-2017 The The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -7,7 +8,7 @@ #include - +// boost::optional template