diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj index 16ee32d87e10c..6af626c1eb85c 100644 --- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj +++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj @@ -14,7 +14,7 @@ - ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions) + ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;ENABLE_MODULE_ELLSWIFT;%(PreprocessorDefinitions) ..\..\src\secp256k1;%(AdditionalIncludeDirectories) 4146;4244;4267;4334 diff --git a/configure.ac b/configure.ac index f2d621ec7f414..555cf01e90ff4 100644 --- a/configure.ac +++ b/configure.ac @@ -2016,7 +2016,7 @@ LIBS_TEMP="$LIBS" unset LIBS LIBS="$LIBS_TEMP" -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig" +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental --enable-module-ellswift" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 70a0ca89152f2..a63b8c19e9299 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -514,8 +514,8 @@ crypto_libbitcoin_crypto_base_la_LDFLAGS = $(AM_LDFLAGS) -static crypto_libbitcoin_crypto_base_la_SOURCES = \ crypto/aes.cpp \ crypto/aes.h \ - crypto/chacha_poly_aead.h \ - crypto/chacha_poly_aead.cpp \ + crypto/bip324_suite.h \ + crypto/bip324_suite.cpp \ crypto/chacha20.h \ crypto/chacha20.cpp \ crypto/common.h \ @@ -529,6 +529,8 @@ crypto_libbitcoin_crypto_base_la_SOURCES = \ crypto/poly1305.cpp \ crypto/muhash.h \ crypto/muhash.cpp \ + crypto/rfc8439.h \ + crypto/rfc8439.cpp \ crypto/ripemd160.cpp \ crypto/ripemd160.h \ crypto/sha1.cpp \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index e1e2066877891..b224869e11184 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -18,10 +18,11 @@ bench_bench_bitcoin_SOURCES = \ bench/bench.cpp \ bench/bench.h \ bench/bench_bitcoin.cpp \ + bench/bip324_ecdh.cpp \ + bench/bip324_suite.cpp \ bench/block_assemble.cpp \ bench/ccoins_caching.cpp \ bench/chacha20.cpp \ - bench/chacha_poly_aead.cpp \ bench/checkblock.cpp \ bench/checkqueue.cpp \ bench/crypto_hash.cpp \ @@ -29,6 +30,7 @@ bench_bench_bitcoin_SOURCES = \ bench/data.h \ bench/descriptors.cpp \ bench/duplicate_inputs.cpp \ + bench/ellswift.cpp \ bench/examples.cpp \ bench/gcs_filter.cpp \ bench/hashpadding.cpp \ @@ -42,6 +44,7 @@ bench_bench_bitcoin_SOURCES = \ bench/peer_eviction.cpp \ bench/poly1305.cpp \ bench/prevector.cpp \ + bench/rfc8439.cpp \ bench/rollingbloom.cpp \ bench/rpc_blockchain.cpp \ bench/rpc_mempool.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index fc60359efde4e..33f2a59d93a30 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -16,6 +16,7 @@ FUZZ_BINARY=test/fuzz/fuzz$(EXEEXT) JSON_TEST_FILES = \ test/data/script_tests.json \ + test/data/bip324_vectors.json \ test/data/bip341_wallet_vectors.json \ test/data/base58_encode_decode.json \ test/data/blockfilters.json \ @@ -161,7 +162,8 @@ BITCOIN_TESTS =\ test/validation_flush_tests.cpp \ test/validation_tests.cpp \ test/validationinterface_tests.cpp \ - test/versionbits_tests.cpp + test/versionbits_tests.cpp \ + test/xoroshiro128plusplus_tests.cpp if ENABLE_WALLET BITCOIN_TESTS += \ @@ -254,12 +256,13 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/crypto.cpp \ test/fuzz/crypto_aes256.cpp \ test/fuzz/crypto_aes256cbc.cpp \ + test/fuzz/crypto_bip324_suite.cpp \ test/fuzz/crypto_chacha20.cpp \ - test/fuzz/crypto_chacha20_poly1305_aead.cpp \ test/fuzz/crypto_common.cpp \ test/fuzz/crypto_diff_fuzz_chacha20.cpp \ test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \ test/fuzz/crypto_poly1305.cpp \ + test/fuzz/crypto_rfc8439.cpp \ test/fuzz/cuckoocache.cpp \ test/fuzz/decode_tx.cpp \ test/fuzz/descriptor_parse.cpp \ @@ -291,6 +294,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/netbase_dns_lookup.cpp \ test/fuzz/node_eviction.cpp \ test/fuzz/p2p_transport_serialization.cpp \ + test/fuzz/p2p_v2_transport_serialization.cpp \ test/fuzz/parse_hd_keypath.cpp \ test/fuzz/parse_numbers.cpp \ test/fuzz/parse_script.cpp \ diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include index d142572b276b2..48d0082fee919 100644 --- a/src/Makefile.test_util.include +++ b/src/Makefile.test_util.include @@ -19,7 +19,8 @@ TEST_UTIL_H = \ test/util/transaction_utils.h \ test/util/txmempool.h \ test/util/validation.h \ - test/util/wallet.h + test/util/wallet.h \ + test/util/xoroshiro128plusplus.h libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) diff --git a/src/bench/bip324_ecdh.cpp b/src/bench/bip324_ecdh.cpp new file mode 100644 index 0000000000000..33d6dd04e299a --- /dev/null +++ b/src/bench/bip324_ecdh.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include + +#include + +CKey GetRandomKey() +{ + CKey key; + key.MakeNewKey(true); + return key; +} + +int GetEll64(const CKey& key, unsigned char* ell64) +{ + std::array rnd32; + GetRandBytes(rnd32); + return secp256k1_ellswift_create(GetVerifyContext(), ell64, reinterpret_cast(key.data()), rnd32.data()); +} + +static void BIP324_ECDH(benchmark::Bench& bench) +{ + ECC_Start(); + auto our_key = GetRandomKey(); + auto their_key = GetRandomKey(); + + unsigned char our_ell64[64], their_ell64[64]; + if (!GetEll64(our_key, our_ell64)) { + assert(false); + } + + if (!GetEll64(their_key, their_ell64)) { + assert(false); + } + + bench.batch(1).unit("ecdh").run([&] { + assert(our_key.ComputeBIP324ECDHSecret({reinterpret_cast(their_ell64), 64}, + {reinterpret_cast(our_ell64), 64}, + true) + .has_value()); + }); + ECC_Stop(); +} + +BENCHMARK(BIP324_ECDH); diff --git a/src/bench/bip324_suite.cpp b/src/bench/bip324_suite.cpp new file mode 100644 index 0000000000000..2573ddd5d5e61 --- /dev/null +++ b/src/bench/bip324_suite.cpp @@ -0,0 +1,120 @@ +// Copyright (c) 2019-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include +#include // for the RFC8439_EXPANSION constant +#include + +#include +#include +#include + +/* Number of bytes to process per iteration */ +static constexpr uint64_t BUFFER_SIZE_TINY = 64; +static constexpr uint64_t BUFFER_SIZE_SMALL = 256; +static constexpr uint64_t BUFFER_SIZE_LARGE = 1024 * 1024; + +static const std::vector zero_vec(BIP324_KEY_LEN, std::byte{0x00}); + +static void BIP324_CIPHER_SUITE(benchmark::Bench& bench, size_t contents_len, bool include_decryption) +{ + BIP324Key zero_arr; + std::array zero_rekey_salt; + memcpy(zero_arr.data(), zero_vec.data(), BIP324_KEY_LEN); + memcpy(zero_rekey_salt.data(), zero_vec.data(), BIP324_REKEY_SALT_LEN); + + BIP324CipherSuite enc{zero_arr, zero_arr, zero_rekey_salt}; + BIP324CipherSuite dec{zero_arr, zero_arr, zero_rekey_salt}; + + auto packet_len = BIP324_LENGTH_FIELD_LEN + BIP324_HEADER_LEN + contents_len + RFC8439_EXPANSION; + + std::vector in(contents_len, std::byte{0x00}); + std::vector out(packet_len, std::byte{0x00}); + + BIP324HeaderFlags flags{BIP324_NONE}; + + bench.batch(contents_len).unit("byte").run([&] { + // encrypt or decrypt the buffer with a static key + const bool crypt_ok_1 = enc.Crypt({}, in, out, flags, true); + assert(crypt_ok_1); + + if (include_decryption) { + // if we decrypt, we need to decrypt the length first + std::array encrypted_pkt_len; + memcpy(encrypted_pkt_len.data(), out.data(), BIP324_LENGTH_FIELD_LEN); + (void)dec.DecryptLength(encrypted_pkt_len); + const bool crypt_ok_2 = dec.Crypt({}, {out.data() + BIP324_LENGTH_FIELD_LEN, out.size() - BIP324_LENGTH_FIELD_LEN}, in, flags, false); + assert(crypt_ok_2); + } + }); +} + +static void BIP324_CIPHER_SUITE_64BYTES_ONLY_ENCRYPT(benchmark::Bench& bench) +{ + BIP324_CIPHER_SUITE(bench, BUFFER_SIZE_TINY, false); +} + +static void BIP324_CIPHER_SUITE_256BYTES_ONLY_ENCRYPT(benchmark::Bench& bench) +{ + BIP324_CIPHER_SUITE(bench, BUFFER_SIZE_SMALL, false); +} + +static void BIP324_CIPHER_SUITE_1MB_ONLY_ENCRYPT(benchmark::Bench& bench) +{ + BIP324_CIPHER_SUITE(bench, BUFFER_SIZE_LARGE, false); +} + +static void BIP324_CIPHER_SUITE_64BYTES_ENCRYPT_DECRYPT(benchmark::Bench& bench) +{ + BIP324_CIPHER_SUITE(bench, BUFFER_SIZE_TINY, true); +} + +static void BIP324_CIPHER_SUITE_256BYTES_ENCRYPT_DECRYPT(benchmark::Bench& bench) +{ + BIP324_CIPHER_SUITE(bench, BUFFER_SIZE_SMALL, true); +} + +static void BIP324_CIPHER_SUITE_1MB_ENCRYPT_DECRYPT(benchmark::Bench& bench) +{ + BIP324_CIPHER_SUITE(bench, BUFFER_SIZE_LARGE, true); +} + +// Add Hash() (dbl-sha256) bench for comparison + +static void HASH(benchmark::Bench& bench, size_t buffersize) +{ + uint8_t hash[CHash256::OUTPUT_SIZE]; + std::vector in(buffersize, 0); + bench.batch(in.size()).unit("byte").run([&] { + CHash256().Write(in).Finalize(hash); + }); +} + +static void HASH_64BYTES(benchmark::Bench& bench) +{ + HASH(bench, BUFFER_SIZE_TINY); +} + +static void HASH_256BYTES(benchmark::Bench& bench) +{ + HASH(bench, BUFFER_SIZE_SMALL); +} + +static void HASH_1MB(benchmark::Bench& bench) +{ + HASH(bench, BUFFER_SIZE_LARGE); +} + +BENCHMARK(BIP324_CIPHER_SUITE_64BYTES_ONLY_ENCRYPT); +BENCHMARK(BIP324_CIPHER_SUITE_256BYTES_ONLY_ENCRYPT); +BENCHMARK(BIP324_CIPHER_SUITE_1MB_ONLY_ENCRYPT); +BENCHMARK(BIP324_CIPHER_SUITE_64BYTES_ENCRYPT_DECRYPT); +BENCHMARK(BIP324_CIPHER_SUITE_256BYTES_ENCRYPT_DECRYPT); +BENCHMARK(BIP324_CIPHER_SUITE_1MB_ENCRYPT_DECRYPT); +BENCHMARK(HASH_64BYTES); +BENCHMARK(HASH_256BYTES); +BENCHMARK(HASH_1MB); diff --git a/src/bench/chacha20.cpp b/src/bench/chacha20.cpp index a6f4eec4ca9e0..d7dc9a7f816c2 100644 --- a/src/bench/chacha20.cpp +++ b/src/bench/chacha20.cpp @@ -14,9 +14,9 @@ static const uint64_t BUFFER_SIZE_LARGE = 1024*1024; static void CHACHA20(benchmark::Bench& bench, size_t buffersize) { std::vector key(32,0); - ChaCha20 ctx(key.data(), key.size()); + ChaCha20 ctx(key.data()); ctx.SetIV(0); - ctx.Seek(0); + ctx.Seek64(0); std::vector in(buffersize,0); std::vector out(buffersize,0); bench.batch(in.size()).unit("byte").run([&] { diff --git a/src/bench/chacha_poly_aead.cpp b/src/bench/chacha_poly_aead.cpp deleted file mode 100644 index e994279a4d8b1..0000000000000 --- a/src/bench/chacha_poly_aead.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2019-2020 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - - -#include -#include -#include // for the POLY1305_TAGLEN constant -#include - -#include -#include - -/* Number of bytes to process per iteration */ -static constexpr uint64_t BUFFER_SIZE_TINY = 64; -static constexpr uint64_t BUFFER_SIZE_SMALL = 256; -static constexpr uint64_t BUFFER_SIZE_LARGE = 1024 * 1024; - -static const unsigned char k1[32] = {0}; -static const unsigned char k2[32] = {0}; - -static ChaCha20Poly1305AEAD aead(k1, 32, k2, 32); - -static void CHACHA20_POLY1305_AEAD(benchmark::Bench& bench, size_t buffersize, bool include_decryption) -{ - std::vector in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - std::vector out(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - uint64_t seqnr_payload = 0; - uint64_t seqnr_aad = 0; - int aad_pos = 0; - uint32_t len = 0; - bench.batch(buffersize).unit("byte").run([&] { - // encrypt or decrypt the buffer with a static key - const bool crypt_ok_1 = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true); - assert(crypt_ok_1); - - if (include_decryption) { - // if we decrypt, include the GetLength - const bool get_length_ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data()); - assert(get_length_ok); - const bool crypt_ok_2 = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true); - assert(crypt_ok_2); - } - - // increase main sequence number - seqnr_payload++; - // increase aad position (position in AAD keystream) - aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; - if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { - aad_pos = 0; - seqnr_aad++; - } - if (seqnr_payload + 1 == std::numeric_limits::max()) { - // reuse of nonce+key is okay while benchmarking. - seqnr_payload = 0; - seqnr_aad = 0; - aad_pos = 0; - } - }); -} - -static void CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT(benchmark::Bench& bench) -{ - CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_TINY, false); -} - -static void CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT(benchmark::Bench& bench) -{ - CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_SMALL, false); -} - -static void CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT(benchmark::Bench& bench) -{ - CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_LARGE, false); -} - -static void CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT(benchmark::Bench& bench) -{ - CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_TINY, true); -} - -static void CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT(benchmark::Bench& bench) -{ - CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_SMALL, true); -} - -static void CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT(benchmark::Bench& bench) -{ - CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_LARGE, true); -} - -// Add Hash() (dbl-sha256) bench for comparison - -static void HASH(benchmark::Bench& bench, size_t buffersize) -{ - uint8_t hash[CHash256::OUTPUT_SIZE]; - std::vector in(buffersize,0); - bench.batch(in.size()).unit("byte").run([&] { - CHash256().Write(in).Finalize(hash); - }); -} - -static void HASH_64BYTES(benchmark::Bench& bench) -{ - HASH(bench, BUFFER_SIZE_TINY); -} - -static void HASH_256BYTES(benchmark::Bench& bench) -{ - HASH(bench, BUFFER_SIZE_SMALL); -} - -static void HASH_1MB(benchmark::Bench& bench) -{ - HASH(bench, BUFFER_SIZE_LARGE); -} - -BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT); -BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT); -BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT); -BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT); -BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT); -BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT); -BENCHMARK(HASH_64BYTES); -BENCHMARK(HASH_256BYTES); -BENCHMARK(HASH_1MB); diff --git a/src/bench/ellswift.cpp b/src/bench/ellswift.cpp new file mode 100644 index 0000000000000..0d3c67f4ab025 --- /dev/null +++ b/src/bench/ellswift.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2016-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +#include + +static void EllSwiftEncode(benchmark::Bench& bench) +{ + ECC_Start(); + + CKey key; + key.MakeNewKey(true); + + bench.batch(1).unit("pubkey").run([&] { + key.EllSwiftEncode(); + }); + ECC_Stop(); +} + +BENCHMARK(EllSwiftEncode); diff --git a/src/bench/rfc8439.cpp b/src/bench/rfc8439.cpp new file mode 100644 index 0000000000000..342f3a2b1e458 --- /dev/null +++ b/src/bench/rfc8439.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include + +#include +#include +#include + +static constexpr uint64_t AAD_SIZE = 32; +static constexpr uint64_t PLAINTEXT_SIZE_TINY = 64; +static constexpr uint64_t PLAINTEXT_SIZE_SMALL = 256; +static constexpr uint64_t PLAINTEXT_SIZE_LARGE = 1024 * 1024; + +static std::vector zero_key(32, std::byte{0x00}); +static std::vector aad(AAD_SIZE, std::byte{0x00}); +std::array nonce = {std::byte{0x00}, std::byte{0x01}, std::byte{0x02}, std::byte{0x03}, + std::byte{0x04}, std::byte{0x05}, std::byte{0x06}, std::byte{0x07}, + std::byte{0x08}, std::byte{0x09}, std::byte{0x0a}, std::byte{0x0b}}; + +static void RFC8439_AEAD(benchmark::Bench& bench, size_t plaintext_size, bool include_decryption) +{ + std::vector plaintext_in(plaintext_size, std::byte{0x00}); + std::vector output(plaintext_size + POLY1305_TAGLEN, std::byte{0x00}); + + bench.batch(plaintext_size).unit("byte").run([&] { + RFC8439Encrypt(aad, zero_key, nonce, plaintext_in, output); + + if (include_decryption) { + std::vector decrypted_plaintext(plaintext_size); + auto authenticated = RFC8439Decrypt(aad, zero_key, nonce, output, decrypted_plaintext); + assert(authenticated); + assert(memcmp(decrypted_plaintext.data(), plaintext_in.data(), plaintext_in.size()) == 0); + } + }); +} + +static void RFC8439_AEAD_64BYTES_ONLY_ENCRYPT(benchmark::Bench& bench) +{ + RFC8439_AEAD(bench, PLAINTEXT_SIZE_TINY, false); +} + +static void RFC8439_AEAD_256BYTES_ONLY_ENCRYPT(benchmark::Bench& bench) +{ + RFC8439_AEAD(bench, PLAINTEXT_SIZE_SMALL, false); +} + +static void RFC8439_AEAD_1MB_ONLY_ENCRYPT(benchmark::Bench& bench) +{ + RFC8439_AEAD(bench, PLAINTEXT_SIZE_LARGE, false); +} + +static void RFC8439_AEAD_64BYTES_ENCRYPT_DECRYPT(benchmark::Bench& bench) +{ + RFC8439_AEAD(bench, PLAINTEXT_SIZE_TINY, true); +} + +static void RFC8439_AEAD_256BYTES_ENCRYPT_DECRYPT(benchmark::Bench& bench) +{ + RFC8439_AEAD(bench, PLAINTEXT_SIZE_SMALL, true); +} + +static void RFC8439_AEAD_1MB_ENCRYPT_DECRYPT(benchmark::Bench& bench) +{ + RFC8439_AEAD(bench, PLAINTEXT_SIZE_LARGE, true); +} + +BENCHMARK(RFC8439_AEAD_64BYTES_ONLY_ENCRYPT); +BENCHMARK(RFC8439_AEAD_256BYTES_ONLY_ENCRYPT); +BENCHMARK(RFC8439_AEAD_1MB_ONLY_ENCRYPT); +BENCHMARK(RFC8439_AEAD_64BYTES_ENCRYPT_DECRYPT); +BENCHMARK(RFC8439_AEAD_256BYTES_ENCRYPT_DECRYPT); +BENCHMARK(RFC8439_AEAD_1MB_ENCRYPT_DECRYPT); diff --git a/src/crypto/bip324_suite.cpp b/src/crypto/bip324_suite.cpp new file mode 100644 index 0000000000000..8c036a730704b --- /dev/null +++ b/src/crypto/bip324_suite.cpp @@ -0,0 +1,120 @@ +// Copyright (c) 2019-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#ifndef HAVE_TIMINGSAFE_BCMP + +int timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n) +{ + const unsigned char *p1 = b1, *p2 = b2; + int ret = 0; + + for (; n > 0; n--) + ret |= *p1++ ^ *p2++; + return (ret != 0); +} + +#endif // TIMINGSAFE_BCMP + +BIP324CipherSuite::~BIP324CipherSuite() +{ + memory_cleanse(key_P.data(), key_P.size()); +} + +void BIP324CipherSuite::CommitToKeys(const Span data, bool commit_to_L, bool commit_to_P) +{ + if (commit_to_L) { + fsc20.CommitToKey(data); + } + + if (commit_to_P) { + assert(CSHA256::OUTPUT_SIZE == BIP324_KEY_LEN); + auto hasher = rekey_hasher; + hasher << MakeUCharSpan(data) << MakeUCharSpan(key_P); + auto new_key = hasher.GetSHA256(); + memcpy(key_P.data(), new_key.data(), BIP324_KEY_LEN); + } + + set_nonce(); +} + +bool BIP324CipherSuite::Crypt(const Span aad, + const Span input, + Span output, + BIP324HeaderFlags& flags, bool encrypt) +{ + // check buffer boundaries + if ( + // if we encrypt, make sure the destination has the space for the encrypted length field, header, contents and MAC + (encrypt && (output.size() < BIP324_LENGTH_FIELD_LEN + BIP324_HEADER_LEN + input.size() + RFC8439_EXPANSION)) || + // if we decrypt, make sure the source contains at least the encrypted header + mac and the destination has the space for the input - MAC - header + (!encrypt && (input.size() < BIP324_HEADER_LEN + RFC8439_EXPANSION || output.size() < input.size() - BIP324_HEADER_LEN - RFC8439_EXPANSION))) { + return false; + } + + if (encrypt) { + // input is just the contents + // output will be encrypted contents length + encrypted (header and contents) + mac tag + uint32_t contents_len = input.size(); + WriteLE32(reinterpret_cast(&contents_len), contents_len); + + std::vector header_and_contents(BIP324_HEADER_LEN + input.size()); + + memcpy(header_and_contents.data(), &flags, BIP324_HEADER_LEN); + if (!input.empty()) { + memcpy(header_and_contents.data() + BIP324_HEADER_LEN, input.data(), input.size()); + } + + auto write_pos = output.data(); + fsc20.Crypt({reinterpret_cast(&contents_len), BIP324_LENGTH_FIELD_LEN}, + {write_pos, BIP324_LENGTH_FIELD_LEN}); + write_pos += BIP324_LENGTH_FIELD_LEN; + RFC8439Encrypt(aad, key_P, nonce, header_and_contents, {write_pos, BIP324_HEADER_LEN + input.size() + RFC8439_EXPANSION}); + } else { + // we must use BIP324CipherSuite::DecryptLength before calling BIP324CipherSuite::Crypt + // input is encrypted (header + contents) and the MAC tag i.e. the RFC8439 ciphertext blob + // decrypted header will be put in flags and output will be plaintext contents. + std::vector decrypted_header_and_contents(input.size() - RFC8439_EXPANSION); + auto authenticated = RFC8439Decrypt(aad, key_P, nonce, input, decrypted_header_and_contents); + if (!authenticated) { + return false; + } + + memcpy(&flags, decrypted_header_and_contents.data(), BIP324_HEADER_LEN); + if (!output.empty()) { + memcpy(output.data(), + decrypted_header_and_contents.data() + BIP324_HEADER_LEN, + input.size() - BIP324_HEADER_LEN - RFC8439_EXPANSION); + } + } + + packet_counter++; + if (packet_counter % REKEY_INTERVAL == 0) { + // Rekey key_P. key_L is automatically re-keyed since we're using a forward-secure version + // of ChaCha20, FSChacha20 + CommitToKeys({(std::byte*)nullptr, 0}, false, true); + } + set_nonce(); + return true; +} + +uint32_t BIP324CipherSuite::DecryptLength(const std::array& encrypted_length) +{ + std::array length_buffer; + fsc20.Crypt(encrypted_length, MakeWritableByteSpan(length_buffer)); + + return (uint32_t{length_buffer[0]}) | + (uint32_t{length_buffer[1]} << 8) | + (uint32_t{length_buffer[2]} << 16); +} diff --git a/src/crypto/bip324_suite.h b/src/crypto/bip324_suite.h new file mode 100644 index 0000000000000..0a5725c2a3abe --- /dev/null +++ b/src/crypto/bip324_suite.h @@ -0,0 +1,79 @@ +// Copyright (c) 2019-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_BIP324_SUITE_H +#define BITCOIN_CRYPTO_BIP324_SUITE_H + +#include +#include +#include +#include + +#include +#include + +static constexpr size_t BIP324_KEY_LEN = 32; // bytes +static constexpr size_t BIP324_REKEY_SALT_LEN = 23; // bytes +static constexpr size_t BIP324_GARBAGE_TERMINATOR_LEN = 16; // bytes +static constexpr size_t BIP324_HEADER_LEN = 1; // bytes +static constexpr size_t BIP324_LENGTH_FIELD_LEN = 3; // bytes +static constexpr size_t REKEY_INTERVAL = 256; // packets +static constexpr size_t NONCE_LENGTH = 12; // bytes + +enum BIP324HeaderFlags : uint8_t { + BIP324_NONE = 0, + BIP324_IGNORE = (1 << 7), +}; + +using BIP324Key = std::array; + +class BIP324CipherSuite +{ +private: + FSChaCha20 fsc20; + uint32_t packet_counter{0}; + BIP324Key key_P; + HashWriter rekey_hasher; + std::array nonce; + + void set_nonce() + { + WriteLE32(reinterpret_cast(nonce.data()), packet_counter % REKEY_INTERVAL); + WriteLE64(reinterpret_cast(nonce.data()) + 4, packet_counter / REKEY_INTERVAL); + } + +public: + BIP324CipherSuite(const BIP324Key& K_L, const BIP324Key& K_P, + const std::array& rekey_salt) + : fsc20{K_L, rekey_salt, REKEY_INTERVAL}, + key_P{K_P} + { + set_nonce(); + rekey_hasher << MakeUCharSpan(rekey_salt); + }; + + // Resets the suite keys to commit to data + void CommitToKeys(const Span data, bool commit_to_L, bool commit_to_P); + + explicit BIP324CipherSuite(const BIP324CipherSuite&) = delete; + ~BIP324CipherSuite(); + + /** Encrypts/decrypts a packet + input, contents to encrypt or the encrypted header + encrypted contents + MAC to decrypt (encrypted length is decrypted using DecryptLength() prior to calling Crypt() + encrypt, set to true if we encrypt, false to decrypt. + + Returns true upon success. Upon failure, the output should not be used. + */ + [[nodiscard]] bool Crypt(const Span aad, + const Span input, + Span output, + BIP324HeaderFlags& flags, bool encrypt); + + /** Decrypts the 3 byte encrypted length field (the packet header and contents length) and decodes it into a uint32_t field + The FSChaCha20 keystream will advance. As a result, DecryptLength() cannot be called multiple times to get the same result. The caller must cache the result for re-use. + */ + [[nodiscard]] uint32_t DecryptLength(const std::array& encrypted_length); +}; + +#endif // BITCOIN_CRYPTO_BIP324_SUITE_H diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp index c7e12b06124c0..a4709d93db31f 100644 --- a/src/crypto/chacha20.cpp +++ b/src/crypto/chacha20.cpp @@ -5,9 +5,12 @@ // Based on the public domain implementation 'merged' by D. J. Bernstein // See https://cr.yp.to/chacha.html. -#include #include +#include +#include + +#include #include constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); } @@ -20,95 +23,84 @@ constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | ( #define REPEAT10(a) do { {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; } while(0) -static const unsigned char sigma[] = "expand 32-byte k"; -static const unsigned char tau[] = "expand 16-byte k"; - -void ChaCha20::SetKey(const unsigned char* k, size_t keylen) +void ChaCha20Aligned::SetKey32(const unsigned char* k) { - const unsigned char *constants; - - input[4] = ReadLE32(k + 0); - input[5] = ReadLE32(k + 4); - input[6] = ReadLE32(k + 8); - input[7] = ReadLE32(k + 12); - if (keylen == 32) { /* recommended */ - k += 16; - constants = sigma; - } else { /* keylen == 16 */ - constants = tau; - } - input[8] = ReadLE32(k + 0); - input[9] = ReadLE32(k + 4); - input[10] = ReadLE32(k + 8); - input[11] = ReadLE32(k + 12); - input[0] = ReadLE32(constants + 0); - input[1] = ReadLE32(constants + 4); - input[2] = ReadLE32(constants + 8); - input[3] = ReadLE32(constants + 12); - input[12] = 0; - input[13] = 0; - input[14] = 0; - input[15] = 0; + input[0] = ReadLE32(k + 0); + input[1] = ReadLE32(k + 4); + input[2] = ReadLE32(k + 8); + input[3] = ReadLE32(k + 12); + input[4] = ReadLE32(k + 16); + input[5] = ReadLE32(k + 20); + input[6] = ReadLE32(k + 24); + input[7] = ReadLE32(k + 28); + input[8] = 0; + input[9] = 0; + input[10] = 0; + input[11] = 0; } -ChaCha20::ChaCha20() +ChaCha20Aligned::ChaCha20Aligned() { memset(input, 0, sizeof(input)); } -ChaCha20::ChaCha20(const unsigned char* k, size_t keylen) +ChaCha20Aligned::ChaCha20Aligned(const unsigned char* key32) { - SetKey(k, keylen); + SetKey32(key32); } -void ChaCha20::SetIV(uint64_t iv) +void ChaCha20Aligned::SetIV(uint64_t iv) { - input[14] = iv; - input[15] = iv >> 32; + input[10] = iv; + input[11] = iv >> 32; } -void ChaCha20::Seek(uint64_t pos) +void ChaCha20Aligned::Seek64(uint64_t pos) { - input[12] = pos; - input[13] = pos >> 32; + input[8] = pos; + input[9] = pos >> 32; } -void ChaCha20::Keystream(unsigned char* c, size_t bytes) +void ChaCha20Aligned::SeekRFC8439(uint32_t pos) { - uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; - unsigned char *ctarget = nullptr; - unsigned char tmp[64]; - unsigned int i; + input[8] = pos; + is_rfc8439 = true; +} - if (!bytes) return; +void ChaCha20Aligned::SetRFC8439Nonce(const std::array& nonce) +{ + auto nonce_ptr = reinterpret_cast(nonce.data()); + input[9] = ReadLE32(nonce_ptr); + input[10] = ReadLE32(nonce_ptr + 4); + input[11] = ReadLE32(nonce_ptr + 8); + is_rfc8439 = true; +} - j0 = input[0]; - j1 = input[1]; - j2 = input[2]; - j3 = input[3]; - j4 = input[4]; - j5 = input[5]; - j6 = input[6]; - j7 = input[7]; - j8 = input[8]; - j9 = input[9]; - j10 = input[10]; - j11 = input[11]; - j12 = input[12]; - j13 = input[13]; - j14 = input[14]; - j15 = input[15]; +inline void ChaCha20Aligned::Keystream64(unsigned char* c, size_t blocks) +{ + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + + if (!blocks) return; + + j4 = input[0]; + j5 = input[1]; + j6 = input[2]; + j7 = input[3]; + j8 = input[4]; + j9 = input[5]; + j10 = input[6]; + j11 = input[7]; + j12 = input[8]; + j13 = input[9]; + j14 = input[10]; + j15 = input[11]; for (;;) { - if (bytes < 64) { - ctarget = c; - c = tmp; - } - x0 = j0; - x1 = j1; - x2 = j2; - x3 = j3; + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; x4 = j4; x5 = j5; x6 = j6; @@ -134,10 +126,10 @@ void ChaCha20::Keystream(unsigned char* c, size_t bytes) QUARTERROUND( x3, x4, x9,x14); ); - x0 += j0; - x1 += j1; - x2 += j2; - x3 += j3; + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; x4 += j4; x5 += j5; x6 += j6; @@ -152,7 +144,7 @@ void ChaCha20::Keystream(unsigned char* c, size_t bytes) x15 += j15; ++j12; - if (!j12) ++j13; + if (!j12 && !is_rfc8439) ++j13; WriteLE32(c + 0, x0); WriteLE32(c + 4, x1); @@ -171,59 +163,41 @@ void ChaCha20::Keystream(unsigned char* c, size_t bytes) WriteLE32(c + 56, x14); WriteLE32(c + 60, x15); - if (bytes <= 64) { - if (bytes < 64) { - for (i = 0;i < bytes;++i) ctarget[i] = c[i]; - } - input[12] = j12; - input[13] = j13; + if (blocks == 1) { + input[8] = j12; + if (!is_rfc8439) input[9] = j13; return; } - bytes -= 64; + blocks -= 1; c += 64; } } -void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes) +inline void ChaCha20Aligned::Crypt64(const unsigned char* m, unsigned char* c, size_t blocks) { uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; - unsigned char *ctarget = nullptr; - unsigned char tmp[64]; - unsigned int i; - - if (!bytes) return; - - j0 = input[0]; - j1 = input[1]; - j2 = input[2]; - j3 = input[3]; - j4 = input[4]; - j5 = input[5]; - j6 = input[6]; - j7 = input[7]; - j8 = input[8]; - j9 = input[9]; - j10 = input[10]; - j11 = input[11]; - j12 = input[12]; - j13 = input[13]; - j14 = input[14]; - j15 = input[15]; + uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + + if (!blocks) return; + + j4 = input[0]; + j5 = input[1]; + j6 = input[2]; + j7 = input[3]; + j8 = input[4]; + j9 = input[5]; + j10 = input[6]; + j11 = input[7]; + j12 = input[8]; + j13 = input[9]; + j14 = input[10]; + j15 = input[11]; for (;;) { - if (bytes < 64) { - // if m has fewer than 64 bytes available, copy m to tmp and - // read from tmp instead - for (i = 0;i < bytes;++i) tmp[i] = m[i]; - m = tmp; - ctarget = c; - c = tmp; - } - x0 = j0; - x1 = j1; - x2 = j2; - x3 = j3; + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; x4 = j4; x5 = j5; x6 = j6; @@ -249,10 +223,10 @@ void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes) QUARTERROUND( x3, x4, x9,x14); ); - x0 += j0; - x1 += j1; - x2 += j2; - x3 += j3; + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; x4 += j4; x5 += j5; x6 += j6; @@ -284,7 +258,7 @@ void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes) x15 ^= ReadLE32(m + 60); ++j12; - if (!j12) ++j13; + if (!j12 && !is_rfc8439) ++j13; WriteLE32(c + 0, x0); WriteLE32(c + 4, x1); @@ -303,16 +277,89 @@ void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes) WriteLE32(c + 56, x14); WriteLE32(c + 60, x15); - if (bytes <= 64) { - if (bytes < 64) { - for (i = 0;i < bytes;++i) ctarget[i] = c[i]; - } - input[12] = j12; - input[13] = j13; + if (blocks == 1) { + input[8] = j12; + if (!is_rfc8439) input[9] = j13; return; } - bytes -= 64; + blocks -= 1; c += 64; m += 64; } } + +void ChaCha20::Keystream(unsigned char* c, size_t bytes) +{ + if (!bytes) return; + if (m_bufleft) { + unsigned reuse = std::min(m_bufleft, bytes); + memcpy(c, m_buffer + 64 - m_bufleft, reuse); + m_bufleft -= reuse; + bytes -= reuse; + c += reuse; + } + if (bytes >= 64) { + size_t blocks = bytes / 64; + m_aligned.Keystream64(c, blocks); + c += blocks * 64; + bytes -= blocks * 64; + } + if (bytes) { + m_aligned.Keystream64(m_buffer, 1); + memcpy(c, m_buffer, bytes); + m_bufleft = 64 - bytes; + } +} + +void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes) +{ + unsigned reuse = std::min(m_bufleft, bytes); + if (!bytes) return; + if (m_bufleft) { + for (unsigned i = 0; i < reuse; i++) { + c[i] = m[i] ^ m_buffer[64 - m_bufleft + i]; + } + m_bufleft -= reuse; + bytes -= reuse; + c += reuse; + m += reuse; + } + if (bytes >= 64) { + size_t blocks = bytes / 64; + m_aligned.Crypt64(m, c, blocks); + c += blocks * 64; + m += blocks * 64; + bytes -= blocks * 64; + } + if (bytes) { + m_aligned.Keystream64(m_buffer, 1); + for (unsigned i = 0; i < bytes; i++) { + c[i] = m[i] ^ m_buffer[i]; + } + m_bufleft = 64 - bytes; + } +} + +void FSChaCha20::Crypt(Span input, Span output) +{ + assert(input.size() == output.size()); + c20.Crypt(reinterpret_cast(input.data()), + reinterpret_cast(output.data()), input.size()); + chunk_counter++; + + if (chunk_counter % rekey_interval == 0) { + CommitToKey({(std::byte*)nullptr, 0}); + } +} + +void FSChaCha20::CommitToKey(const Span data) +{ + assert(CSHA256::OUTPUT_SIZE == FSCHACHA20_KEYLEN); + auto hasher = rekey_hasher; + hasher << MakeUCharSpan(data) << MakeUCharSpan(key); + auto new_key = hasher.GetSHA256(); + memory_cleanse(key.data(), key.size()); + memcpy(key.data(), new_key.data(), FSCHACHA20_KEYLEN); + c20.SetKey32(reinterpret_cast(key.data())); + set_nonce(); +} diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h index de16a77878c72..179967d43e9c9 100644 --- a/src/crypto/chacha20.h +++ b/src/crypto/chacha20.h @@ -5,22 +5,96 @@ #ifndef BITCOIN_CRYPTO_CHACHA20_H #define BITCOIN_CRYPTO_CHACHA20_H +#include +#include +#include +#include + +#include +#include #include #include -/** A class for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein - https://cr.yp.to/chacha/chacha-20080128.pdf */ +static constexpr size_t FSCHACHA20_KEYLEN = 32; // bytes +static constexpr size_t FSCHACHA20_REKEY_SALT_LEN = 23; // bytes + +// classes for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein +// https://cr.yp.to/chacha/chacha-20080128.pdf */ + +/** ChaCha20 cipher that only operates on multiples of 64 bytes. */ +class ChaCha20Aligned +{ +private: + uint32_t input[12]; + bool is_rfc8439{false}; + +public: + ChaCha20Aligned(); + + /** Initialize a cipher with specified 32-byte key. */ + ChaCha20Aligned(const unsigned char* key32); + + /** set 32-byte key. */ + void SetKey32(const unsigned char* key32); + + /** set the 64-bit nonce. */ + void SetIV(uint64_t iv); + + /** set the 64bit block counter (pos seeks to byte position 64*pos). */ + void Seek64(uint64_t pos); + + void SetRFC8439Nonce(const std::array& nonce); + void SeekRFC8439(uint32_t pos); + + /** outputs the keystream of size <64*blocks> into */ + void Keystream64(unsigned char* c, size_t blocks); + + /** enciphers the message of length <64*blocks> and write the enciphered representation into + * Used for encryption and decryption (XOR) + */ + void Crypt64(const unsigned char* input, unsigned char* output, size_t blocks); +}; + +/** Unrestricted ChaCha20 cipher. */ class ChaCha20 { private: - uint32_t input[16]; + ChaCha20Aligned m_aligned; + unsigned char m_buffer[64] = {0}; + unsigned m_bufleft{0}; public: - ChaCha20(); - ChaCha20(const unsigned char* key, size_t keylen); - void SetKey(const unsigned char* key, size_t keylen); //!< set key with flexible keylength; 256bit recommended */ - void SetIV(uint64_t iv); // set the 64bit nonce - void Seek(uint64_t pos); // set the 64bit block counter + ChaCha20() = default; + + /** Initialize a cipher with specified 32-byte key. */ + ChaCha20(const unsigned char* key32) : m_aligned(key32) {} + + /** set 32-byte key. */ + void SetKey32(const unsigned char* key32) + { + m_aligned.SetKey32(key32); + m_bufleft = 0; + } + + /** set the 64-bit nonce. */ + void SetIV(uint64_t iv) { m_aligned.SetIV(iv); } + + /** set the 64bit block counter (pos seeks to byte position 64*pos). */ + void Seek64(uint64_t pos) + { + m_aligned.Seek64(pos); + m_bufleft = 0; + } + + void SetRFC8439Nonce(const std::array& nonce) { + m_aligned.SetRFC8439Nonce(nonce); + m_bufleft = 0; + } + + void SeekRFC8439(uint32_t pos) { + m_aligned.SeekRFC8439(pos); + m_bufleft = 0; + } /** outputs the keystream of size into */ void Keystream(unsigned char* c, size_t bytes); @@ -31,4 +105,44 @@ class ChaCha20 void Crypt(const unsigned char* input, unsigned char* output, size_t bytes); }; +class FSChaCha20 +{ +private: + ChaCha20 c20; + size_t rekey_interval; + uint32_t chunk_counter{0}; + std::array key; + HashWriter rekey_hasher; + + void set_nonce() + { + std::array nonce; + WriteLE32(reinterpret_cast(nonce.data()), uint32_t{0}); + WriteLE64(reinterpret_cast(nonce.data()) + 4, chunk_counter / rekey_interval); + c20.SetRFC8439Nonce(nonce); + } + +public: + FSChaCha20() = delete; + FSChaCha20(const std::array& key, + const std::array& rekey_salt, + size_t rekey_interval) + : c20{reinterpret_cast(key.data())}, + rekey_interval{rekey_interval}, + key{key} + { + assert(rekey_interval > 0); + set_nonce(); + rekey_hasher << MakeUCharSpan(rekey_salt); + } + + void CommitToKey(const Span data); + + ~FSChaCha20() + { + memory_cleanse(key.data(), FSCHACHA20_KEYLEN); + } + + void Crypt(Span input, Span output); +}; #endif // BITCOIN_CRYPTO_CHACHA20_H diff --git a/src/crypto/chacha_poly_aead.cpp b/src/crypto/chacha_poly_aead.cpp deleted file mode 100644 index f736b2d867480..0000000000000 --- a/src/crypto/chacha_poly_aead.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2019-2021 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#if defined(HAVE_CONFIG_H) -#include -#endif - -#include - -#include -#include - -#include -#include - -#include -#include - -#ifndef HAVE_TIMINGSAFE_BCMP - -int timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n) -{ - const unsigned char *p1 = b1, *p2 = b2; - int ret = 0; - - for (; n > 0; n--) - ret |= *p1++ ^ *p2++; - return (ret != 0); -} - -#endif // TIMINGSAFE_BCMP - -ChaCha20Poly1305AEAD::ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len) -{ - assert(K_1_len == CHACHA20_POLY1305_AEAD_KEY_LEN); - assert(K_2_len == CHACHA20_POLY1305_AEAD_KEY_LEN); - - m_chacha_header.SetKey(K_1, CHACHA20_POLY1305_AEAD_KEY_LEN); - m_chacha_main.SetKey(K_2, CHACHA20_POLY1305_AEAD_KEY_LEN); - - // set the cached sequence number to uint64 max which hints for an unset cache. - // we can't hit uint64 max since the rekey rule (which resets the sequence number) is 1GB - m_cached_aad_seqnr = std::numeric_limits::max(); -} - -bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int aad_pos, unsigned char* dest, size_t dest_len /* length of the output buffer for sanity checks */, const unsigned char* src, size_t src_len, bool is_encrypt) -{ - // check buffer boundaries - if ( - // if we encrypt, make sure the source contains at least the expected AAD and the destination has at least space for the source + MAC - (is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN || dest_len < src_len + POLY1305_TAGLEN)) || - // if we decrypt, make sure the source contains at least the expected AAD+MAC and the destination has at least space for the source - MAC - (!is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN || dest_len < src_len - POLY1305_TAGLEN))) { - return false; - } - - unsigned char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; - memset(poly_key, 0, sizeof(poly_key)); - m_chacha_main.SetIV(seqnr_payload); - - // block counter 0 for the poly1305 key - // use lower 32bytes for the poly1305 key - // (throws away 32 unused bytes (upper 32) from this ChaCha20 round) - m_chacha_main.Seek(0); - m_chacha_main.Crypt(poly_key, poly_key, sizeof(poly_key)); - - // if decrypting, verify the tag prior to decryption - if (!is_encrypt) { - const unsigned char* tag = src + src_len - POLY1305_TAGLEN; - poly1305_auth(expected_tag, src, src_len - POLY1305_TAGLEN, poly_key); - - // constant time compare the calculated MAC with the provided MAC - if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) { - memory_cleanse(expected_tag, sizeof(expected_tag)); - memory_cleanse(poly_key, sizeof(poly_key)); - return false; - } - memory_cleanse(expected_tag, sizeof(expected_tag)); - // MAC has been successfully verified, make sure we don't convert it in decryption - src_len -= POLY1305_TAGLEN; - } - - // calculate and cache the next 64byte keystream block if requested sequence number is not yet the cache - if (m_cached_aad_seqnr != seqnr_aad) { - m_cached_aad_seqnr = seqnr_aad; - m_chacha_header.SetIV(seqnr_aad); - m_chacha_header.Seek(0); - m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); - } - // crypt the AAD (3 bytes message length) with given position in AAD cipher instance keystream - dest[0] = src[0] ^ m_aad_keystream_buffer[aad_pos]; - dest[1] = src[1] ^ m_aad_keystream_buffer[aad_pos + 1]; - dest[2] = src[2] ^ m_aad_keystream_buffer[aad_pos + 2]; - - // Set the playload ChaCha instance block counter to 1 and crypt the payload - m_chacha_main.Seek(1); - m_chacha_main.Crypt(src + CHACHA20_POLY1305_AEAD_AAD_LEN, dest + CHACHA20_POLY1305_AEAD_AAD_LEN, src_len - CHACHA20_POLY1305_AEAD_AAD_LEN); - - // If encrypting, calculate and append tag - if (is_encrypt) { - // the poly1305 tag expands over the AAD (3 bytes length) & encrypted payload - poly1305_auth(dest + src_len, dest, src_len, poly_key); - } - - // cleanse no longer required MAC and polykey - memory_cleanse(poly_key, sizeof(poly_key)); - return true; -} - -bool ChaCha20Poly1305AEAD::GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext) -{ - // enforce valid aad position to avoid accessing outside of the 64byte keystream cache - // (there is space for 21 times 3 bytes) - assert(aad_pos >= 0 && aad_pos < CHACHA20_ROUND_OUTPUT - CHACHA20_POLY1305_AEAD_AAD_LEN); - if (m_cached_aad_seqnr != seqnr_aad) { - // we need to calculate the 64 keystream bytes since we reached a new aad sequence number - m_cached_aad_seqnr = seqnr_aad; - m_chacha_header.SetIV(seqnr_aad); // use LE for the nonce - m_chacha_header.Seek(0); // block counter 0 - m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); // write keystream to the cache - } - - // decrypt the ciphertext length by XORing the right position of the 64byte keystream cache with the ciphertext - *len24_out = (ciphertext[0] ^ m_aad_keystream_buffer[aad_pos + 0]) | - (ciphertext[1] ^ m_aad_keystream_buffer[aad_pos + 1]) << 8 | - (ciphertext[2] ^ m_aad_keystream_buffer[aad_pos + 2]) << 16; - - return true; -} diff --git a/src/crypto/chacha_poly_aead.h b/src/crypto/chacha_poly_aead.h deleted file mode 100644 index 5d57b5a5e211f..0000000000000 --- a/src/crypto/chacha_poly_aead.h +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2019-2021 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H -#define BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H - -#include - -#include - -static constexpr int CHACHA20_POLY1305_AEAD_KEY_LEN = 32; -static constexpr int CHACHA20_POLY1305_AEAD_AAD_LEN = 3; /* 3 bytes length */ -static constexpr int CHACHA20_ROUND_OUTPUT = 64; /* 64 bytes per round */ -static constexpr int AAD_PACKAGES_PER_ROUND = 21; /* 64 / 3 round down*/ - -/* A AEAD class for ChaCha20-Poly1305@bitcoin. - * - * ChaCha20 is a stream cipher designed by Daniel Bernstein and described in - * [https://cr.yp.to/chacha/chacha-20080128.pdf ChaCha20]. It operates - * by permuting 128 fixed bits, 128 or 256 bits of key, a 64 bit nonce and a 64 - * bit counter into 64 bytes of output. This output is used as a keystream, with - * any unused bytes simply discarded. - * - * Poly1305 [https://cr.yp.to/mac/poly1305-20050329.pdf Poly1305], also - * by Daniel Bernstein, is a one-time Carter-Wegman MAC that computes a 128 bit - * integrity tag given a message and a single-use 256 bit secret key. - * - * The chacha20-poly1305@bitcoin combines these two primitives into an - * authenticated encryption mode. The construction used is based on that proposed - * for TLS by Adam Langley in - * [http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-03 "ChaCha20 - * and Poly1305 based Cipher Suites for TLS", Adam Langley], but differs in - * the layout of data passed to the MAC and in the addition of encryption of the - * packet lengths. - * - * ==== Detailed Construction ==== - * - * The chacha20-poly1305@bitcoin cipher requires two 256 bits of key material as - * output from the key exchange. Each key (K_1 and K_2) are used by two separate - * instances of chacha20. - * - * The instance keyed by K_1 is a stream cipher that is used only to encrypt the 3 - * byte packet length field and has its own sequence number. The second instance, - * keyed by K_2, is used in conjunction with poly1305 to build an AEAD - * (Authenticated Encryption with Associated Data) that is used to encrypt and - * authenticate the entire packet. - * - * Two separate cipher instances are used here so as to keep the packet lengths - * confidential but not create an oracle for the packet payload cipher by - * decrypting and using the packet length prior to checking the MAC. By using an - * independently-keyed cipher instance to encrypt the length, an active attacker - * seeking to exploit the packet input handling as a decryption oracle can learn - * nothing about the payload contents or its MAC (assuming key derivation, - * ChaCha20 and Poly1305 are secure). - * - * The AEAD is constructed as follows: for each packet, generate a Poly1305 key by - * taking the first 256 bits of ChaCha20 stream output generated using K_2, an IV - * consisting of the packet sequence number encoded as an LE uint64 and a ChaCha20 - * block counter of zero. The K_2 ChaCha20 block counter is then set to the - * little-endian encoding of 1 (i.e. {1, 0, 0, 0, 0, 0, 0, 0}) and this instance - * is used for encryption of the packet payload. - * - * ==== Packet Handling ==== - * - * When receiving a packet, the length must be decrypted first. When 3 bytes of - * ciphertext length have been received, they may be decrypted. - * - * A ChaCha20 round always calculates 64bytes which is sufficient to crypt 21 - * times a 3 bytes length field (21*3 = 63). The length field sequence number can - * thus be used 21 times (keystream caching). - * - * The length field must be enc-/decrypted with the ChaCha20 keystream keyed with - * K_1 defined by block counter 0, the length field sequence number in little - * endian and a keystream position from 0 to 60. - * - * Once the entire packet has been received, the MAC MUST be checked before - * decryption. A per-packet Poly1305 key is generated as described above and the - * MAC tag calculated using Poly1305 with this key over the ciphertext of the - * packet length and the payload together. The calculated MAC is then compared in - * constant time with the one appended to the packet and the packet decrypted - * using ChaCha20 as described above (with K_2, the packet sequence number as - * nonce and a starting block counter of 1). - * - * Detection of an invalid MAC MUST lead to immediate connection termination. - * - * To send a packet, first encode the 3 byte length and encrypt it using K_1 as - * described above. Encrypt the packet payload (using K_2) and append it to the - * encrypted length. Finally, calculate a MAC tag and append it. - * - * The initiating peer MUST use K_1_A, K_2_A to encrypt messages on - * the send channel, K_1_B, K_2_B MUST be used to decrypt messages on - * the receive channel. - * - * The responding peer MUST use K_1_A, K_2_A to decrypt messages on - * the receive channel, K_1_B, K_2_B MUST be used to encrypt messages - * on the send channel. - * - * Optimized implementations of ChaCha20-Poly1305@bitcoin are relatively fast in - * general, therefore it is very likely that encrypted messages require not more - * CPU cycles per bytes then the current unencrypted p2p message format - * (ChaCha20/Poly1305 versus double SHA256). - * - * The initial packet sequence numbers are 0. - * - * K_2 ChaCha20 cipher instance (payload) must never reuse a {key, nonce} for - * encryption nor may it be used to encrypt more than 2^70 bytes under the same - * {key, nonce}. - * - * K_1 ChaCha20 cipher instance (length field/AAD) must never reuse a {key, nonce, - * position-in-keystream} for encryption nor may it be used to encrypt more than - * 2^70 bytes under the same {key, nonce}. - * - * We use message sequence numbers for both communication directions. - */ - -class ChaCha20Poly1305AEAD -{ -private: - ChaCha20 m_chacha_header; // AAD cipher instance (encrypted length) and poly1305 key-derivation cipher instance - ChaCha20 m_chacha_main; // payload - unsigned char m_aad_keystream_buffer[CHACHA20_ROUND_OUTPUT]; // aad keystream cache - uint64_t m_cached_aad_seqnr; // aad keystream cache hint - -public: - ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len); - - explicit ChaCha20Poly1305AEAD(const ChaCha20Poly1305AEAD&) = delete; - - /** Encrypts/decrypts a packet - seqnr_payload, the message sequence number - seqnr_aad, the messages AAD sequence number which allows reuse of the AAD keystream - aad_pos, position to use in the AAD keystream to encrypt the AAD - dest, output buffer, must be of a size equal or larger then CHACHA20_POLY1305_AEAD_AAD_LEN + payload (+ POLY1305_TAG_LEN in encryption) bytes - destlen, length of the destination buffer - src, the AAD+payload to encrypt or the AAD+payload+MAC to decrypt - src_len, the length of the source buffer - is_encrypt, set to true if we encrypt (creates and appends the MAC instead of verifying it) - */ - bool Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int aad_pos, unsigned char* dest, size_t dest_len, const unsigned char* src, size_t src_len, bool is_encrypt); - - /** decrypts the 3 bytes AAD data and decodes it into a uint32_t field */ - bool GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext); -}; - -#endif // BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp index 7d14b7938e358..2a3c0efcd4ca4 100644 --- a/src/crypto/muhash.cpp +++ b/src/crypto/muhash.cpp @@ -299,7 +299,7 @@ Num3072 MuHash3072::ToNum3072(Span in) { unsigned char tmp[Num3072::BYTE_SIZE]; uint256 hashed_in{(HashWriter{} << in).GetSHA256()}; - ChaCha20(hashed_in.data(), hashed_in.size()).Keystream(tmp, Num3072::BYTE_SIZE); + ChaCha20Aligned(hashed_in.data()).Keystream64(tmp, Num3072::BYTE_SIZE / 64); Num3072 out{tmp}; return out; diff --git a/src/crypto/rfc8439.cpp b/src/crypto/rfc8439.cpp new file mode 100644 index 0000000000000..6ff691afa7c0c --- /dev/null +++ b/src/crypto/rfc8439.cpp @@ -0,0 +1,102 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +#include + +#ifndef RFC8439_TIMINGSAFE_BCMP +#define RFC8439_TIMINGSAFE_BCMP + +int rfc8439_timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n) +{ + const unsigned char *p1 = b1, *p2 = b2; + int ret = 0; + + for (; n > 0; n--) + ret |= *p1++ ^ *p2++; + return (ret != 0); +} + +#endif // RFC8439_TIMINGSAFE_BCMP + +inline size_t padded16_size(size_t len) +{ + return (len % 16 == 0) ? len : (len / 16 + 1) * 16; +} + +void ComputeRFC8439Tag(const std::array& polykey, + Span aad, Span ciphertext, + Span tag_out) +{ + assert(tag_out.size() == POLY1305_TAGLEN); + std::vector bytes_to_authenticate; + auto padded_aad_size = padded16_size(aad.size()); + auto padded_ciphertext_size = padded16_size(ciphertext.size()); + bytes_to_authenticate.resize(padded_aad_size + padded_ciphertext_size + 16, std::byte{0x00}); + std::copy(aad.begin(), aad.end(), bytes_to_authenticate.begin()); + std::copy(ciphertext.begin(), ciphertext.end(), bytes_to_authenticate.begin() + padded_aad_size); + WriteLE64(reinterpret_cast(bytes_to_authenticate.data()) + padded_aad_size + padded_ciphertext_size, aad.size()); + WriteLE64(reinterpret_cast(bytes_to_authenticate.data()) + padded_aad_size + padded_ciphertext_size + 8, ciphertext.size()); + + poly1305_auth(reinterpret_cast(tag_out.data()), + reinterpret_cast(bytes_to_authenticate.data()), + bytes_to_authenticate.size(), + reinterpret_cast(polykey.data())); +} + +std::array GetPoly1305Key(ChaCha20& c20) +{ + c20.SeekRFC8439(0); + std::array polykey; + c20.Keystream(reinterpret_cast(polykey.data()), POLY1305_KEYLEN); + return polykey; +} + +void RFC8439Crypt(ChaCha20& c20, const Span in_bytes, Span out_bytes) +{ + assert(in_bytes.size() <= out_bytes.size()); + c20.SeekRFC8439(1); + c20.Crypt(reinterpret_cast(in_bytes.data()), reinterpret_cast(out_bytes.data()), in_bytes.size()); +} + +void RFC8439Encrypt(const Span aad, const Span key, const std::array& nonce, const Span plaintext, Span output) +{ + assert(key.size() == RFC8439_KEYLEN); + assert(output.size() >= plaintext.size() + POLY1305_TAGLEN); + + ChaCha20 c20{reinterpret_cast(key.data())}; + c20.SetRFC8439Nonce(nonce); + + std::array polykey{GetPoly1305Key(c20)}; + + RFC8439Crypt(c20, plaintext, output); + ComputeRFC8439Tag(polykey, aad, + {output.data(), plaintext.size()}, + {output.data() + plaintext.size(), POLY1305_TAGLEN}); +} + +bool RFC8439Decrypt(const Span aad, const Span key, const std::array& nonce, const Span input, Span plaintext) +{ + assert(key.size() == RFC8439_KEYLEN); + assert(plaintext.size() >= input.size() - POLY1305_TAGLEN); + + ChaCha20 c20{reinterpret_cast(key.data())}; + c20.SetRFC8439Nonce(nonce); + + std::array polykey{GetPoly1305Key(c20)}; + std::array tag; + + ComputeRFC8439Tag(polykey, aad, {input.data(), input.size() - POLY1305_TAGLEN}, tag); + + if (rfc8439_timingsafe_bcmp(reinterpret_cast(input.data() + input.size() - POLY1305_TAGLEN), + reinterpret_cast(tag.data()), POLY1305_TAGLEN) != 0) { + return false; + } + + RFC8439Crypt(c20, {input.data(), input.size() - POLY1305_TAGLEN}, plaintext); + return true; +} diff --git a/src/crypto/rfc8439.h b/src/crypto/rfc8439.h new file mode 100644 index 0000000000000..fa18e4ff2df39 --- /dev/null +++ b/src/crypto/rfc8439.h @@ -0,0 +1,30 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_RFC8439_H +#define BITCOIN_CRYPTO_RFC8439_H + +#include +#include +#include + +#include +#include +#include + +constexpr static size_t RFC8439_KEYLEN = 32; +constexpr static size_t RFC8439_EXPANSION = POLY1305_TAGLEN; + +void RFC8439Encrypt(const Span aad, const Span key, const std::array& nonce, const Span plaintext, Span output); + +// returns false if authentication fails +bool RFC8439Decrypt(const Span aad, const Span key, const std::array& nonce, const Span input, Span plaintext); + +void ComputeRFC8439Tag(const std::array& polykey, + Span aad, Span ciphertext, + Span tag_out); + +std::array GetPoly1305Key(ChaCha20& c20); + +#endif // BITCOIN_CRYPTO_RFC8439_H diff --git a/src/init.cpp b/src/init.cpp index cf4102e6a7f32..68aead41589dc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -475,6 +475,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-i2psam=", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-onlynet=", "Make automatic outbound connections only to network (" + Join(GetNetworkNames(), ", ") + "). Inbound and manual connections are not affected by this option. It can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-v2transport", strprintf("Support v2 transport (default: %u)", DEFAULT_V2_TRANSPORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); // TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from @@ -871,6 +872,11 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb } } + // Signal NODE_P2P_V2 if BIP324 v2 transport is enabled. + if (args.GetBoolArg("-v2transport", DEFAULT_V2_TRANSPORT)) { + nLocalServices = ServiceFlags(nLocalServices | NODE_P2P_V2); + } + // Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index are both enabled. if (args.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) { if (g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) { diff --git a/src/key.cpp b/src/key.cpp index 199808505d1b9..db376a0da72ef 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -9,8 +9,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -332,6 +334,51 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const return ret; } +std::optional CKey::EllSwiftEncode() const +{ + EllSwiftPubKey encoded_pubkey; + if (!secp256k1_ellswift_create(secp256k1_context_sign, + reinterpret_cast(encoded_pubkey.data()), + keydata.data(), nullptr)) { + return {}; + } + return encoded_pubkey; +} + +// Returns just the x-coordinate in big endian +static int bip324_ecdh_hash(unsigned char* output, const unsigned char* x32, const unsigned char* ours, const unsigned char* theirs, void* data) +{ + memcpy(output, x32, 32); + return 1; +} + +std::optional CKey::ComputeBIP324ECDHSecret(const Span their_ellswift, + const Span our_ellswift, + bool initiating) const +{ + unsigned char xonly_ecdh[ECDH_SECRET_SIZE]; + if (!secp256k1_ellswift_xdh(secp256k1_context_sign, xonly_ecdh, reinterpret_cast(their_ellswift.data()), reinterpret_cast(our_ellswift.data()), keydata.data(), bip324_ecdh_hash, nullptr)) { + return {}; + } + + HashWriter hasher(HASHER_BIP324_ECDH); + Span initiator_ellswift, responder_ellswift; + if (initiating) { + initiator_ellswift = our_ellswift; + responder_ellswift = their_ellswift; + } else { + initiator_ellswift = their_ellswift; + responder_ellswift = our_ellswift; + } + hasher << MakeUCharSpan(initiator_ellswift) << MakeUCharSpan(responder_ellswift) << xonly_ecdh; + auto secret_uint256 = hasher.GetSHA256(); + + ECDHSecret secret; + memcpy(secret.data(), &secret_uint256, CSHA256::OUTPUT_SIZE); + memory_cleanse(xonly_ecdh, ECDH_SECRET_SIZE); + return secret; +} + bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const { if (nDepth == std::numeric_limits::max()) return false; out.nDepth = nDepth + 1; diff --git a/src/key.h b/src/key.h index e9b78ebd44899..b56cf6401dc7a 100644 --- a/src/key.h +++ b/src/key.h @@ -7,11 +7,17 @@ #ifndef BITCOIN_KEY_H #define BITCOIN_KEY_H +#include +#include #include #include +#include #include #include +#include +#include +#include #include #include @@ -22,6 +28,12 @@ */ typedef std::vector > CPrivKey; +constexpr static size_t ECDH_SECRET_SIZE = CSHA256::OUTPUT_SIZE; +const auto HASHER_BIP324_ECDH = TaggedHash("bip324_ellswift_xonly_ecdh"); + +// Used to represent a ECDH secret (ECDH_SECRET_SIZE bytes) +using ECDHSecret = std::array; + /** An encapsulated private key. */ class CKey { @@ -156,6 +168,13 @@ class CKey //! Load private key and check that public key matches. bool Load(const CPrivKey& privkey, const CPubKey& vchPubKey, bool fSkipCheck); + + std::optional EllSwiftEncode() const; + + // Returns false if an invalid public key is provided + std::optional ComputeBIP324ECDHSecret(const Span their_ellswift, + const Span our_ellswift, + bool initiating) const; }; struct CExtKey { diff --git a/src/net.cpp b/src/net.cpp index 3c28b9eddf9c2..b10268e1c384d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -15,19 +15,22 @@ #include #include #include +#include #include -#include #include #include #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -63,6 +66,8 @@ static_assert (MAX_BLOCK_RELAY_ONLY_ANCHORS <= static_cast(MAX_BLOCK_REL /** Anchor IP address database file name */ const char* const ANCHORS_DATABASE_FILENAME = "anchors.dat"; +static constexpr uint64_t V2_MAX_CONTENTS_LENGTH = 0x01000000 - 1; // 2^24 - 1 + // How often to dump addresses to peers.dat static constexpr std::chrono::minutes DUMP_PEERS_INTERVAL{15}; @@ -108,6 +113,9 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*"; static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8] static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8] static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8] + +static constexpr uint8_t V2_MAX_MSG_TYPE_LEN = 12; // maximum length for V2 (BIP324) string message types +static constexpr size_t V2_MAX_GARBAGE_BYTES = 4095; // maximum length for V2 (BIP324) shapable handshake // // Global state variables // @@ -404,7 +412,10 @@ CNode* CConnman::FindNode(const CService& addr) bool CConnman::AlreadyConnectedToAddress(const CAddress& addr) { - return FindNode(static_cast(addr)) || FindNode(addr.ToStringIPPort()); + CNode* found_by_addr = FindNode(static_cast(addr)); + CNode* found_by_ip_port = FindNode(addr.ToStringIPPort()); + return (found_by_addr && !found_by_addr->fDisconnect) || + (found_by_ip_port && !found_by_ip_port->fDisconnect); } bool CConnman::CheckIncomingNonce(uint64_t nonce) @@ -433,6 +444,35 @@ static CAddress GetBindAddress(const Sock& sock) return addr_bind; } +void DeriveBIP324Session(ECDHSecret&& ecdh_secret, BIP324Session& session) +{ + std::string salt{"bitcoin_v2_shared_secret"}; + salt += std::string{reinterpret_cast(Params().MessageStart()), CMessageHeader::MESSAGE_START_SIZE}; + + CHKDF_HMAC_SHA256_L32 hkdf(reinterpret_cast(ecdh_secret.data()), ecdh_secret.size(), salt); + + hkdf.Expand32("initiator_L", reinterpret_cast(session.initiator_L.data())); + hkdf.Expand32("initiator_P", reinterpret_cast(session.initiator_P.data())); + hkdf.Expand32("responder_L", reinterpret_cast(session.responder_L.data())); + hkdf.Expand32("responder_P", reinterpret_cast(session.responder_P.data())); + hkdf.Expand32("session_id", reinterpret_cast(session.session_id.data())); + + unsigned char hkdf_32_okm[32]; + hkdf.Expand32("rekey_salt", hkdf_32_okm); + memcpy(session.rekey_salt.data(), hkdf_32_okm, BIP324_REKEY_SALT_LEN); + + hkdf.Expand32("garbage_terminators", hkdf_32_okm); + memcpy(session.initiator_garbage_terminator.data(), + hkdf_32_okm, + BIP324_GARBAGE_TERMINATOR_LEN); + memcpy(session.responder_garbage_terminator.data(), + hkdf_32_okm + BIP324_GARBAGE_TERMINATOR_LEN, + BIP324_GARBAGE_TERMINATOR_LEN); + + memory_cleanse(hkdf_32_okm, 32); + memory_cleanse(ecdh_secret.data(), ecdh_secret.size()); +} + CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type) { assert(conn_type != ConnectionType::INBOUND); @@ -443,8 +483,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo // Look for an existing connection CNode* pnode = FindNode(static_cast(addrConnect)); - if (pnode) - { + if (pnode && !pnode->fDisconnect) { LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } @@ -461,7 +500,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo std::vector resolved; if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { const CService rnd{resolved[GetRand(resolved.size())]}; - addrConnect = CAddress{MaybeFlipIPv6toCJDNS(rnd), NODE_NONE}; + addrConnect = CAddress{MaybeFlipIPv6toCJDNS(rnd), addrConnect.nServices}; if (!addrConnect.IsValid()) { LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToString(), pszDest); return nullptr; @@ -470,7 +509,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo // In that case, drop the connection that was just created. LOCK(m_nodes_mutex); CNode* pnode = FindNode(static_cast(addrConnect)); - if (pnode) { + if (pnode && !pnode->fDisconnect) { LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } @@ -546,6 +585,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (!addr_bind.IsValid()) { addr_bind = GetBindAddress(*sock); } + + bool prefer_p2p_v2 = (addrConnect.nServices & GetLocalServices() & NODE_P2P_V2); CNode* pnode = new CNode(id, std::move(sock), addrConnect, @@ -555,7 +596,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo pszDest ? pszDest : "", conn_type, /*inbound_onion=*/false, - CNodeOptions{ .i2p_sam_session = std::move(i2p_transient_session) }); + CNodeOptions{ .i2p_sam_session = std::move(i2p_transient_session) }, + prefer_p2p_v2); pnode->AddRef(); // We're making a new connection, harvest entropy from the time (and our peer count) @@ -646,11 +688,48 @@ void CNode::CopyStats(CNodeStats& stats) stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : ""; X(m_conn_type); + X(m_transport_type); + X(m_v2_session_id); } #undef X +void CNode::InitV2P2P(const Span their_ellswift, const Span our_ellswift, bool initiating) +{ + auto ecdh_secret = v2_priv_key.ComputeBIP324ECDHSecret(their_ellswift, our_ellswift, initiating); + + BIP324Session v2_keys; + DeriveBIP324Session(std::move(ecdh_secret.value()), v2_keys); + + if (initiating) { + m_deserializer = std::make_unique(GetId(), v2_keys.responder_L, v2_keys.responder_P, v2_keys.rekey_salt); + m_serializer = std::make_unique(v2_keys.initiator_L, v2_keys.initiator_P, v2_keys.rekey_salt); + v2_sent_garbage_terminator = v2_keys.initiator_garbage_terminator; + v2_recv_garbage_terminator = v2_keys.responder_garbage_terminator; + } else { + m_deserializer = std::make_unique(GetId(), v2_keys.initiator_L, v2_keys.initiator_P, v2_keys.rekey_salt); + m_serializer = std::make_unique(v2_keys.responder_L, v2_keys.responder_P, v2_keys.rekey_salt); + v2_sent_garbage_terminator = v2_keys.responder_garbage_terminator; + v2_recv_garbage_terminator = v2_keys.initiator_garbage_terminator; + } + // Both peers must keep around a copy of the garbage terminator for the BIP324 shapable handshake + v2_keys_derived = true; + m_v2_session_id = HexStr(v2_keys.session_id); +} + +void CNode::EnsureInitV2Key(bool initiating) +{ + while (!v2_priv_key.IsValid()) { + v2_priv_key.MakeNewKey(true); + ellswift_pubkey = v2_priv_key.EllSwiftEncode().value(); + } +} + bool CNode::ReceiveMsgBytes(Span msg_bytes, bool& complete) { + if (!m_deserializer) { + return false; + } + complete = false; const auto time = GetTime(); LOCK(cs_vRecv); @@ -667,7 +746,31 @@ bool CNode::ReceiveMsgBytes(Span msg_bytes, bool& complete) if (m_deserializer->Complete()) { // decompose a transport agnostic CNetMessage from the deserializer bool reject_message{false}; - CNetMessage msg = m_deserializer->GetMessage(time, reject_message); + bool disconnect{false}; + + std::vector aad; + if (PreferV2Conn() && !m_authenticated_v2_garbage) { + std::copy(v2_garbage_bytes_recd.begin(), v2_garbage_bytes_recd.end(), std::back_inserter(aad)); + } + CNetMessage msg = m_deserializer->GetMessage(time, reject_message, disconnect, aad); + + if (disconnect) { + // v2 p2p incorrect MAC tag. Disconnect from peer. + return false; + } + + if (!aad.empty()) { + // first message to authenticate garbage + m_authenticated_v2_garbage = true; + memory_cleanse(v2_garbage_bytes_recd.data(), v2_garbage_bytes_recd.size()); + } + + // inbound clients do not know whether the peer is trying to talk v1 or v2. + // if the first message is not VERSION, we reinterpret the bytes as v2 ellswift + if (!m_prefer_p2p_v2 && IsInboundConn() && nVersion == 0 && msg.m_type != NetMsgType::VERSION) { + return false; + } + if (reject_message) { // Message deserialization failed. Drop the message but don't disconnect the peer. // store the size of the corrupt message @@ -703,7 +806,17 @@ int V1TransportDeserializer::readHeader(Span msg_bytes) memcpy(&hdrbuf[nHdrPos], msg_bytes.data(), nCopy); nHdrPos += nCopy; - // if header incomplete, exit + if (validated_magic_len < CMessageHeader::MESSAGE_START_SIZE) { + auto available = std::min((size_t)nHdrPos, CMessageHeader::MESSAGE_START_SIZE); + if (memcmp(hdrbuf.data(), m_chain_params.MessageStart(), available) != 0) { + LogPrint(BCLog::NET, "Header error: Wrong MessageStart %s received, peer=%d\n", HexStr(hdrbuf), m_node_id); + return -1; + } else { + validated_magic_len = available; + } + } + + // don't have complete header yet, exit if (nHdrPos < CMessageHeader::HEADER_SIZE) return nCopy; @@ -716,12 +829,6 @@ int V1TransportDeserializer::readHeader(Span msg_bytes) return -1; } - // Check start string, network magic - if (memcmp(hdr.pchMessageStart, m_chain_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) { - LogPrint(BCLog::NET, "Header error: Wrong MessageStart %s received, peer=%d\n", HexStr(hdr.pchMessageStart), m_node_id); - return -1; - } - // reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH if (hdr.nMessageSize > MAX_SIZE || hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) { LogPrint(BCLog::NET, "Header error: Size too large (%s, %u bytes), peer=%d\n", SanitizeString(hdr.GetCommand()), hdr.nMessageSize, m_node_id); @@ -759,10 +866,15 @@ const uint256& V1TransportDeserializer::GetMessageHash() const return data_hash; } -CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, bool& reject_message) +CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, + bool& reject_message, + bool& disconnect, + Span aad) { // Initialize out parameter reject_message = false; + disconnect = false; + // decompose a single CNetMessage from the TransportDeserializer CNetMessage msg(std::move(vRecv)); @@ -784,6 +896,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds HexStr(Span{hash}.first(CMessageHeader::CHECKSUM_SIZE)), HexStr(hdr.pchChecksum), m_node_id); + // TODO: Should we disconnect the v1 peer in this case? reject_message = true; } else if (!hdr.IsCommandValid()) { LogPrint(BCLog::NET, "Header error: Invalid message type (%s, %u bytes), peer=%d\n", @@ -796,7 +909,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds return msg; } -void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vector& header) const +bool V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vector& header) const { // create dbl-sha256 checksum uint256 hash = Hash(msg.data); @@ -808,6 +921,191 @@ void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vec // serialize header header.reserve(CMessageHeader::HEADER_SIZE); CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, header, 0, hdr}; + return true; +} + +int V2TransportDeserializer::readHeader(Span pkt_bytes) +{ + // copy data to temporary parsing buffer + const size_t remaining = BIP324_LENGTH_FIELD_LEN - m_hdr_pos; + const size_t copy_bytes = std::min(remaining, pkt_bytes.size()); + + memcpy(&vRecv[m_hdr_pos], pkt_bytes.data(), copy_bytes); + m_hdr_pos += copy_bytes; + + // if we don't have the encrypted length yet, exit + if (m_hdr_pos < BIP324_LENGTH_FIELD_LEN) { + return copy_bytes; + } + + // we have the 3 bytes encrypted packet length at this point + std::array encrypted_pkt_len; + memcpy(encrypted_pkt_len.data(), vRecv.data(), BIP324_LENGTH_FIELD_LEN); + + // the encrypted packet data = bip324 header + contents (message type + message payload) + m_contents_size = m_cipher_suite->DecryptLength(encrypted_pkt_len); + + // m_contents_size is the size of the p2p message + if (m_contents_size > V2_MAX_CONTENTS_LENGTH) { + return -1; + } + + // switch state to reading message data + m_in_data = true; + + return copy_bytes; +} + +int V2TransportDeserializer::readData(Span pkt_bytes) +{ + // Read the BIP324 encrypted packet data. + const size_t remaining = BIP324_HEADER_LEN + m_contents_size + RFC8439_EXPANSION - m_data_pos; + const size_t copy_bytes = std::min(remaining, pkt_bytes.size()); + + // extend buffer, respect previous copied encrypted length + if (vRecv.size() < BIP324_LENGTH_FIELD_LEN + m_data_pos + copy_bytes) { + // Allocate up to 256 KiB ahead, but never more than the total message size. + vRecv.resize(BIP324_LENGTH_FIELD_LEN + std::min(BIP324_HEADER_LEN + m_contents_size, m_data_pos + copy_bytes + 256 * 1024) + RFC8439_EXPANSION, std::byte{0x00}); + } + + memcpy(&vRecv[BIP324_LENGTH_FIELD_LEN + m_data_pos], pkt_bytes.data(), copy_bytes); + m_data_pos += copy_bytes; + + return copy_bytes; +} + +CNetMessage V2TransportDeserializer::GetMessage(const std::chrono::microseconds time, + bool& reject_message, + bool& disconnect, + Span aad) +{ + // BIP324 1-byte message type id is the minimum payload after the v2 transport version placeholder has been received + size_t min_contents_size = m_processed_version_placeholder ? 1 : 0; + + // Initialize out parameters + reject_message = (vRecv.size() < V2_MIN_PACKET_LENGTH + min_contents_size); + disconnect = false; + + // In v2, vRecv contains: + // 3 bytes of encrypted packet length + // 1-byte encrypted bip324 header + // variable length encrypted contents(message type and message payload) and + // mac tag + assert(Complete()); + + std::string msg_type; + + BIP324HeaderFlags flags; + size_t msg_type_size = 1; // at least one byte needed for message type + if (m_cipher_suite->Crypt(aad, + Span{reinterpret_cast(vRecv.data() + BIP324_LENGTH_FIELD_LEN), BIP324_HEADER_LEN + m_contents_size + RFC8439_EXPANSION}, + Span{reinterpret_cast(vRecv.data()), m_contents_size}, flags, false)) { + // MAC check was successful + vRecv.resize(m_contents_size); + reject_message = reject_message || (BIP324HeaderFlags(BIP324_IGNORE & flags) != BIP324_NONE); + + if (!aad.empty()) { + // This only happens for the first encrypted message received by an inbound client + // which is meant to authenticate the garbage bytes for the BIP324 shapable handshake + // That message is not to be passed to the p2p application layer. + reject_message = true; + } else if (!m_processed_version_placeholder) { + // BIP324 transport version placeholder message. + // Discard it for v2.0 clients. + reject_message = true; + m_processed_version_placeholder = true; + } + + if (!reject_message) { + uint8_t size_or_shortid = 0; + try { + vRecv >> size_or_shortid; + } catch (const std::ios_base::failure&) { + LogPrint(BCLog::NET, "Invalid message type, peer=%d\n", m_node_id); + reject_message = true; + } + + if (size_or_shortid > 0 && size_or_shortid <= V2_MAX_MSG_TYPE_LEN && vRecv.size() >= size_or_shortid) { + // first byte is a number between 1 and 12. Must be a string command. + // use direct read since we already read the varlen size + msg_type.resize(size_or_shortid); + vRecv.read(MakeWritableByteSpan(msg_type)); + msg_type_size += size_or_shortid; + } else { + auto mtype = GetMessageTypeFromShortID(size_or_shortid); + if (mtype.has_value()) { + msg_type = mtype.value(); + } else { + // unknown-short-id results in a valid but unknown message (will be skipped) + msg_type = "unknown-" + ToString(size_or_shortid); + } + } + } + } else { + // Invalid mac tag + LogPrint(BCLog::NET, "Invalid v2 mac tag, peer=%d\n", m_node_id); + disconnect = true; + reject_message = true; + } + + // we'll always return a CNetMessage (even if decryption fails) + // decompose a single CNetMessage from the TransportDeserializer + CNetMessage msg(std::move(vRecv)); + msg.m_type = msg_type; + msg.m_time = time; + + if (!reject_message) { + msg.m_message_size = m_contents_size - msg_type_size; + msg.m_raw_message_size = V2_MIN_PACKET_LENGTH + m_contents_size; // raw wire size + } + + Reset(); + + return msg; +} + +bool V2TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vector& header) const +{ + // When dealing with a message other than the transport version placeholder message, serialize the command. + if (!msg.m_type.empty() || !msg.data.empty()) { + size_t serialized_msg_type_size = 1; // short-IDs are 1 byte + std::optional short_msg_type = GetShortIDFromMessageType(msg.m_type); + + if (!short_msg_type) { + // message type without an assigned short-ID + assert(msg.m_type.size() <= V2_MAX_MSG_TYPE_LEN); + serialized_msg_type_size = ::GetSerializeSize(msg.m_type, PROTOCOL_VERSION); + } + + std::vector msg_type_bytes(serialized_msg_type_size); + CVectorWriter vector_writer(SER_NETWORK, INIT_PROTO_VERSION, msg_type_bytes, 0); + // append the short-ID or the varstr of the msg type + if (short_msg_type) { + vector_writer << short_msg_type.value(); + } else { + vector_writer << msg.m_type; + } + // insert message type directly into the CSerializedNetMsg data buffer (insert at begin) + // TODO: if we refactor the BIP324CipherSuite::Crypt() function to allow separate buffers for + // the message type and payload we could avoid a insert and thus a potential reallocation + msg.data.insert(msg.data.begin(), msg_type_bytes.begin(), msg_type_bytes.end()); + } + + auto contents_size = msg.data.size(); + auto encrypted_pkt_size = V2_MIN_PACKET_LENGTH + contents_size; + // resize the message buffer to make space for the MAC tag + msg.data.resize(encrypted_pkt_size, 0); + + BIP324HeaderFlags flags{BIP324_NONE}; + // encrypt the payload, this should always succeed (controlled buffers, don't check the MAC during encrypting) + auto success = m_cipher_suite->Crypt(msg.aad, + Span{reinterpret_cast(msg.data.data()), contents_size}, + Span{reinterpret_cast(msg.data.data()), encrypted_pkt_size}, + flags, true); + if (!success) { + LogPrint(BCLog::NET, "error in v2 p2p encryption for message type: %s\n", msg.m_type); + } + return success; } size_t CConnman::SocketSendData(CNode& node) const @@ -1017,6 +1315,9 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr&& sock, } const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end(); + + // A listening v2 peer does not know the advertised services for the initiating peer at this point. + // Assume a v1 connection for now. CNode* pnode = new CNode(id, std::move(sock), addr, @@ -1029,7 +1330,9 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr&& sock, CNodeOptions{ .permission_flags = permission_flags, .prefer_evict = discouraged, - }); + }, + /*prefer_p2p_v2*/ false); + pnode->m_transport_type = TransportProtocolType::V1; pnode->AddRef(); m_msgproc->InitializeNode(*pnode, nodeServices); @@ -1046,6 +1349,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr&& sock, bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type) { + AssertLockNotHeld(m_total_bytes_sent_mutex); std::optional max_connections; switch (conn_type) { case ConnectionType::INBOUND: @@ -1287,7 +1591,7 @@ void CConnman::SocketHandlerConnected(const std::vector& nodes, { // typical socket buffer is 8K-64K uint8_t pchBuf[0x10000]; - int nBytes = 0; + ssize_t nBytes = 0; { LOCK(pnode->m_sock_mutex); if (!pnode->m_sock) { @@ -1295,13 +1599,117 @@ void CConnman::SocketHandlerConnected(const std::vector& nodes, } nBytes = pnode->m_sock->Recv(pchBuf, sizeof(pchBuf), MSG_DONTWAIT); } - if (nBytes > 0) - { + uint8_t* ptr = pchBuf; + if (nBytes > 0) { bool notify = false; - if (!pnode->ReceiveMsgBytes({pchBuf, (size_t)nBytes}, notify)) { - pnode->CloseSocketDisconnect(); + size_t num_bytes = (size_t)nBytes; + + // for a v2 outbound peer: + // prior to InitV2P2P(), pnode->m_deserializer is not init and ReceiveMsgBytes() will return false + // for a v2 inbound peer: + // prior to InitV2P2P(), pnode->m_deserializer is init to V1TransportDeserializer, ReceiveMsgBytes will fail due to a malformed header according to the v1 protocol + // for all v2 peers: + // after InitV2P2P(), keys are derived, but m_deserializer would simply disconnect on MAC failure until + // the key exchage phase ends with the garbage terminator + // after garbage terminator, ReceiveMsgBytes() should work on valid v2 encrypted packets + if ((pnode->v2_keys_derived && !pnode->v2_garbage_terminated) || + !pnode->ReceiveMsgBytes({ptr, num_bytes}, notify)) { + // when we cannot understand the received bytes, disconnect if: + // 1. we don't support BIP324 v2, or + // 2. v2 key exchange is complete, we should understand the bytes, or + // 3. we've previously received a v1 (or v2) VERSION message from the peer + if (!gArgs.GetBoolArg("-v2transport", DEFAULT_V2_TRANSPORT) || + pnode->v2_key_exchange_complete || + pnode->nVersion != 0) { + pnode->CloseSocketDisconnect(); + } else { + pnode->EnsureInitV2Key(!pnode->IsInboundConn()); + + if (!pnode->v2_keys_derived) { + // If we're the inbound peer, upon receiving any ellswift bytes we send our ellswift key + if (pnode->IsInboundConn() && pnode->peer_ellswift_buf.empty()) { + PushV2EllSwiftPubkey(pnode); + // We now know the peer prefers a BIP324 v2 connection + pnode->m_prefer_p2p_v2 = true; + pnode->m_transport_type = TransportProtocolType::V2; + } + + // Keys are not derived because we don't have the peer ellswift yet, keep buffering. + auto old_ellswift_sz = pnode->peer_ellswift_buf.size(); + auto more_ellswift_bytes = std::min(ELLSWIFT_ENCODED_SIZE - old_ellswift_sz, num_bytes); + pnode->peer_ellswift_buf.resize(old_ellswift_sz + more_ellswift_bytes); + memcpy(pnode->peer_ellswift_buf.data() + old_ellswift_sz, ptr, more_ellswift_bytes); + + ptr += more_ellswift_bytes; + num_bytes -= more_ellswift_bytes; + + if (pnode->peer_ellswift_buf.size() < ELLSWIFT_ENCODED_SIZE) + continue; + + // At this point we have the entire peer ellswift, we can derive keys + // and instantiate the v2 encryption session + pnode->InitV2P2P(pnode->peer_ellswift_buf, MakeByteSpan(pnode->ellswift_pubkey), !pnode->IsInboundConn()); + + // After keys are exchanged, both peers send the garbage terminator + // followed by the v2 encrypted message that authenticates the garbage + // which is currently empty as the mechanism is unused in bitcoin core. + PushV2GarbageTerminator(pnode); + CSerializedNetMsg garbage_auth_msg; + PushMessage(pnode, std::move(garbage_auth_msg)); + // Send empty message again for transport version placeholder + CSerializedNetMsg transport_version_msg; + PushMessage(pnode, std::move(transport_version_msg)); + } + + if (pnode->v2_keys_derived && !pnode->v2_garbage_terminated && num_bytes > 0) { + // Keep buffering bytes until the garbage terminator + auto old_size = pnode->v2_garbage_bytes_recd.size(); + auto new_size = old_size + num_bytes; + + // Might be better to just allocate to V2_MAX_GARBAGE_BYTES at start once the mechanism + // is actually used + pnode->v2_garbage_bytes_recd.resize(std::min(new_size, V2_MAX_GARBAGE_BYTES)); + memcpy(pnode->v2_garbage_bytes_recd.data() + old_size, ptr, (new_size - old_size)); + auto it = std::search(pnode->v2_garbage_bytes_recd.begin(), pnode->v2_garbage_bytes_recd.end(), + pnode->v2_recv_garbage_terminator.begin(), pnode->v2_recv_garbage_terminator.end()); + + if (it != pnode->v2_garbage_bytes_recd.end()) { + // Found the terminator... + auto garbage_size = it - pnode->v2_garbage_bytes_recd.begin(); + pnode->v2_garbage_bytes_recd.erase(it, pnode->v2_garbage_bytes_recd.end()); + if (garbage_size <= long{V2_MAX_GARBAGE_BYTES}) { + // ...in less than the maximum allowed bytes + auto fwd = garbage_size + pnode->v2_recv_garbage_terminator.size() - old_size; + ptr += fwd; + num_bytes -= fwd; + pnode->v2_garbage_terminated = true; + } else { + // ..but more than the maximum allowed bytes were used + pnode->CloseSocketDisconnect(); + } + memory_cleanse(pnode->v2_recv_garbage_terminator.data(), pnode->v2_recv_garbage_terminator.size()); + } else if (pnode->v2_garbage_bytes_recd.size() >= V2_MAX_GARBAGE_BYTES) { + pnode->CloseSocketDisconnect(); + memory_cleanse(pnode->v2_recv_garbage_terminator.data(), pnode->v2_recv_garbage_terminator.size()); + } + } + + // after a successful ECDH and shapable handshake garbage termination, process the remaining bytes. + if (pnode->v2_garbage_terminated) { + if (num_bytes > 0 && !pnode->ReceiveMsgBytes({ptr, num_bytes}, notify)) { + pnode->CloseSocketDisconnect(); + } else { + pnode->v2_key_exchange_complete = true; + pnode->m_transport_type = TransportProtocolType::V2; + if (!pnode->IsInboundConn()) { + // Outbound peer has completed key exchange and can start the P2P protocol + m_msgproc->InitP2P(*pnode, nLocalServices); + } + } + } + } } - RecordBytesRecv(nBytes); + RecordBytesRecv(num_bytes); if (notify) { size_t nSizeAdded = 0; auto it(pnode->vRecvMsg.begin()); @@ -1318,17 +1726,20 @@ void CConnman::SocketHandlerConnected(const std::vector& nodes, } WakeMessageHandler(); } - } - else if (nBytes == 0) - { + } else if (nBytes == 0) { // socket closed gracefully if (!pnode->fDisconnect) { LogPrint(BCLog::NET, "socket closed for peer=%d\n", pnode->GetId()); } pnode->CloseSocketDisconnect(); - } - else if (nBytes < 0) - { + + if (pnode->PreferV2Conn() && !pnode->v2_key_exchange_complete && !pnode->IsInboundConn()) { + CAddress addr = pnode->addr; + addr.nServices = ServiceFlags(addr.nServices & ~NODE_P2P_V2); + OpenNetworkConnection(addr, false, &pnode->grantOutbound, addr.ToStringIPPort().c_str(), pnode->m_conn_type); + pnode->v2_key_exchange_complete = true; + } + } else if (nBytes < 0) { // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) @@ -1508,6 +1919,7 @@ void CConnman::DumpAddresses() void CConnman::ProcessAddrFetch() { + AssertLockNotHeld(m_total_bytes_sent_mutex); std::string strDest; { LOCK(m_addr_fetches_mutex); @@ -1576,6 +1988,7 @@ int CConnman::GetExtraBlockRelayCount() const void CConnman::ThreadOpenConnections(const std::vector connect) { + AssertLockNotHeld(m_total_bytes_sent_mutex); SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION); FastRandomContext rng; // Connect to specific addresses @@ -1875,7 +2288,7 @@ std::vector CConnman::GetAddedNodeInfo() const { std::vector ret; - std::list lAddresses(0); + std::list lAddresses(0); { LOCK(m_added_nodes_mutex); ret.reserve(m_added_nodes.size()); @@ -1899,9 +2312,9 @@ std::vector CConnman::GetAddedNodeInfo() const } } - for (const std::string& strAddNode : lAddresses) { - CService service(LookupNumeric(strAddNode, Params().GetDefaultPort(strAddNode))); - AddedNodeInfo addedNode{strAddNode, CService(), false, false}; + for (const auto& addr : lAddresses) { + CService service(LookupNumeric(addr.m_added_node, Params().GetDefaultPort(addr.m_added_node))); + AddedNodeInfo addedNode{addr, CService(), false, false}; if (service.IsValid()) { // strAddNode is an IP:port auto it = mapConnected.find(service); @@ -1912,7 +2325,7 @@ std::vector CConnman::GetAddedNodeInfo() const } } else { // strAddNode is a name - auto it = mapConnectedByName.find(strAddNode); + auto it = mapConnectedByName.find(addr.m_added_node); if (it != mapConnectedByName.end()) { addedNode.resolvedAddress = it->second.second; addedNode.fConnected = true; @@ -1927,6 +2340,7 @@ std::vector CConnman::GetAddedNodeInfo() const void CConnman::ThreadOpenAddedConnections() { + AssertLockNotHeld(m_total_bytes_sent_mutex); SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION); while (true) { @@ -1942,7 +2356,12 @@ void CConnman::ThreadOpenAddedConnections() } tried = true; CAddress addr(CService(), NODE_NONE); - OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), ConnectionType::MANUAL); + + if (info.m_params.m_use_p2p_v2) { + addr.nServices = ServiceFlags(addr.nServices | NODE_P2P_V2); + } + + OpenNetworkConnection(addr, false, &grant, info.m_params.m_added_node.c_str(), ConnectionType::MANUAL); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; } @@ -1956,6 +2375,7 @@ void CConnman::ThreadOpenAddedConnections() // if successful, this moves the passed grant to the constructed node void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, ConnectionType conn_type) { + AssertLockNotHeld(m_total_bytes_sent_mutex); assert(conn_type != ConnectionType::INBOUND); // @@ -1972,8 +2392,10 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) { return; } - } else if (FindNode(std::string(pszDest))) - return; + } else { + auto existing_node = FindNode(std::string(pszDest)); + if (existing_node && !existing_node->fDisconnect) return; + } CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type); @@ -1982,6 +2404,13 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai if (grantOutbound) grantOutbound->MoveTo(pnode->grantOutbound); + // Only the outbound peer knows that both sides support BIP324 transport + if (pnode->PreferV2Conn()) { + PushV2EllSwiftPubkey(pnode); + pnode->m_transport_type = TransportProtocolType::V2; + } else { + pnode->m_transport_type = TransportProtocolType::V1; + } m_msgproc->InitializeNode(*pnode, nLocalServices); { LOCK(m_nodes_mutex); @@ -2523,22 +2952,22 @@ std::vector CConnman::GetAddresses(CNode& requestor, size_t max_addres return cache_entry.m_addrs_response_cache; } -bool CConnman::AddNode(const std::string& strNode) +bool CConnman::AddNode(const AddedNodeParams& added_node_params) { LOCK(m_added_nodes_mutex); - for (const std::string& it : m_added_nodes) { - if (strNode == it) return false; + for (const auto& it : m_added_nodes) { + if (added_node_params.m_added_node == it.m_added_node) return false; } - m_added_nodes.push_back(strNode); + m_added_nodes.push_back(added_node_params); return true; } bool CConnman::RemoveAddedNode(const std::string& strNode) { LOCK(m_added_nodes_mutex); - for(std::vector::iterator it = m_added_nodes.begin(); it != m_added_nodes.end(); ++it) { - if (strNode == *it) { + for (auto it = m_added_nodes.begin(); it != m_added_nodes.end(); ++it) { + if (strNode == it->m_added_node) { m_added_nodes.erase(it); return true; } @@ -2733,10 +3162,10 @@ CNode::CNode(NodeId idIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion, - CNodeOptions&& node_opts) - : m_deserializer{std::make_unique(V1TransportDeserializer(Params(), idIn, SER_NETWORK, INIT_PROTO_VERSION))}, - m_serializer{std::make_unique(V1TransportSerializer())}, - m_permission_flags{node_opts.permission_flags}, + CNodeOptions&& node_opts, + bool prefer_p2p_v2, + TransportProtocolType transport_type_in) + : m_permission_flags{node_opts.permission_flags}, m_sock{sock}, m_connected{GetTime()}, addr{addrIn}, @@ -2745,15 +3174,18 @@ CNode::CNode(NodeId idIn, m_inbound_onion{inbound_onion}, m_prefer_evict{node_opts.prefer_evict}, nKeyedNetGroup{nKeyedNetGroupIn}, + m_transport_type{transport_type_in}, id{idIn}, nLocalHostNonce{nLocalHostNonceIn}, m_conn_type{conn_type_in}, - m_i2p_sam_session{std::move(node_opts.i2p_sam_session)} + m_i2p_sam_session{std::move(node_opts.i2p_sam_session)}, + m_prefer_p2p_v2(prefer_p2p_v2) { if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND); - for (const std::string &msg : getAllNetMessageTypes()) - mapRecvBytesPerMsgType[msg] = 0; + for (const auto& msg : getAllNetMessageTypes()) { + mapRecvBytesPerMsgType[msg.second] = 0; + } mapRecvBytesPerMsgType[NET_MESSAGE_TYPE_OTHER] = 0; if (fLogIPs) { @@ -2761,6 +3193,13 @@ CNode::CNode(NodeId idIn, } else { LogPrint(BCLog::NET, "Added connection peer=%d\n", id); } + + if (PreferV2Conn()) { + EnsureInitV2Key(!IsInboundConn()); + } else { + m_deserializer = std::make_unique(V1TransportDeserializer(Params(), id, SER_NETWORK, INIT_PROTO_VERSION)); + m_serializer = std::make_unique(V1TransportSerializer()); + } } bool CConnman::NodeFullyConnected(const CNode* pnode) @@ -2771,8 +3210,6 @@ bool CConnman::NodeFullyConnected(const CNode* pnode) void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) { AssertLockNotHeld(m_total_bytes_sent_mutex); - size_t nMessageSize = msg.data.size(); - LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId()); if (gArgs.GetBoolArg("-capturemessages", false)) { CaptureMessage(pnode->addr, msg.m_type, msg.data, /*is_incoming=*/false); } @@ -2786,9 +3223,19 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) msg.data.data() ); + if (!pnode->m_serializer) { + return; + } + // make sure we use the appropriate network transport format std::vector serializedHeader; - pnode->m_serializer->prepareForTransport(msg, serializedHeader); + if (!pnode->m_serializer->prepareForTransport(msg, serializedHeader)) { + return; + } + + size_t nMessageSize = msg.data.size(); + LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId()); + size_t nTotalSize = nMessageSize + serializedHeader.size(); size_t nBytesSent = 0; @@ -2801,7 +3248,9 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) pnode->nSendSize += nTotalSize; if (pnode->nSendSize > nSendBufferMaxSize) pnode->fPauseSend = true; - pnode->vSendMsg.push_back(std::move(serializedHeader)); + + // The serializedHeader is empty for v2 p2p messages since all the bytes are in msg.data + if (!serializedHeader.empty()) pnode->vSendMsg.push_back(std::move(serializedHeader)); if (nMessageSize) pnode->vSendMsg.push_back(std::move(msg.data)); // If write queue empty, attempt "optimistic write" @@ -2810,6 +3259,41 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) if (nBytesSent) RecordBytesSent(nBytesSent); } +void CConnman::PushV2EllSwiftPubkey(CNode* pnode) +{ + AssertLockNotHeld(m_total_bytes_sent_mutex); + std::vector ellswift_bytes; + ellswift_bytes.resize(ELLSWIFT_ENCODED_SIZE); + memcpy(ellswift_bytes.data(), pnode->ellswift_pubkey.data(), ELLSWIFT_ENCODED_SIZE); + size_t nBytesSent; + { + LOCK(pnode->cs_vSend); + pnode->nSendSize += ellswift_bytes.size(); + pnode->vSendMsg.push_back(ellswift_bytes); + LogPrint(BCLog::NET, "sending 64 byte v2 p2p ellswift key to peer=%d\n", pnode->GetId()); + + // Send immediately. + nBytesSent = SocketSendData(*pnode); + } + + if (nBytesSent) RecordBytesSent(nBytesSent); +} + +void CConnman::PushV2GarbageTerminator(CNode* pnode) +{ + std::vector terminator_uchars; + terminator_uchars.resize(pnode->v2_sent_garbage_terminator.size()); + memcpy(terminator_uchars.data(), pnode->v2_sent_garbage_terminator.data(), terminator_uchars.size()); + { + LOCK(pnode->cs_vSend); + pnode->nSendSize += pnode->v2_sent_garbage_terminator.size(); + + // We do not have to send immediately because this is followed shortly by the + // transport version message + pnode->vSendMsg.push_back(terminator_uchars); + } +} + bool CConnman::ForNode(NodeId id, std::function func) { CNode* found = nullptr; diff --git a/src/net.h b/src/net.h index 11bfc4c9fb2da..cd545ec4ecec7 100644 --- a/src/net.h +++ b/src/net.h @@ -9,20 +9,25 @@ #include #include #include -#include #include +#include +#include #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include +#include #include #include #include @@ -31,6 +36,7 @@ #include #include +#include #include #include #include @@ -39,6 +45,7 @@ #include #include #include +#include #include class AddrMan; @@ -89,11 +96,18 @@ static constexpr bool DEFAULT_FIXEDSEEDS{true}; static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000; +static constexpr size_t V2_MIN_PACKET_LENGTH = BIP324_LENGTH_FIELD_LEN + BIP324_HEADER_LEN + RFC8439_EXPANSION; +static constexpr bool DEFAULT_V2_TRANSPORT{false}; + typedef int64_t NodeId; -struct AddedNodeInfo -{ - std::string strAddedNode; +struct AddedNodeParams { + std::string m_added_node; + bool m_use_p2p_v2; +}; + +struct AddedNodeInfo { + AddedNodeParams m_params; CService resolvedAddress; bool fConnected; bool fInbound; @@ -119,6 +133,7 @@ struct CSerializedNetMsg { } std::vector data; + std::vector aad; // associated authenticated data for encrypted BIP324 (v2) transport std::string m_type; }; @@ -218,6 +233,8 @@ class CNodeStats Network m_network; uint32_t m_mapped_as; ConnectionType m_conn_type; + TransportProtocolType m_transport_type; + std::string m_v2_session_id; }; @@ -254,7 +271,10 @@ class TransportDeserializer { /** read and deserialize data, advances msg_bytes data pointer */ virtual int Read(Span& msg_bytes) = 0; // decomposes a message from the context - virtual CNetMessage GetMessage(std::chrono::microseconds time, bool& reject_message) = 0; + virtual CNetMessage GetMessage(std::chrono::microseconds time, + bool& reject_message, + bool& disconnect, + Span aad) = 0; virtual ~TransportDeserializer() {} }; @@ -271,6 +291,7 @@ class V1TransportDeserializer final : public TransportDeserializer CDataStream vRecv; // received message data unsigned int nHdrPos; unsigned int nDataPos; + uint8_t validated_magic_len{0}; const uint256& GetMessageHash() const; int readHeader(Span msg_bytes); @@ -318,7 +339,73 @@ class V1TransportDeserializer final : public TransportDeserializer } return ret; } - CNetMessage GetMessage(std::chrono::microseconds time, bool& reject_message) override; + CNetMessage GetMessage(std::chrono::microseconds time, + bool& reject_message, + bool& disconnect, + Span aad) override; +}; + +/** V2TransportDeserializer is a transport deserializer after BIP324 */ +class V2TransportDeserializer final : public TransportDeserializer +{ +private: + std::unique_ptr m_cipher_suite; + const NodeId m_node_id; // Only for logging + bool m_in_data = false; // parsing header (false) or data (true) + size_t m_contents_size = 0; // expected message size + CDataStream vRecv; // received message data (encrypted length, encrypted contents and MAC tag) + size_t m_hdr_pos = 0; // read pos in header + size_t m_data_pos = 0; // read pos in data + bool m_processed_version_placeholder = false; // BIP324 transport version message has been received + +public: + V2TransportDeserializer(const NodeId node_id, + const BIP324Key& key_l, + const BIP324Key& key_p, + const std::array& rekey_salt) + : m_cipher_suite(new BIP324CipherSuite(key_l, key_p, rekey_salt)), + m_node_id(node_id), + vRecv(SER_NETWORK, INIT_PROTO_VERSION) + { + Reset(); + } + + void Reset() + { + vRecv.clear(); + vRecv.resize(BIP324_LENGTH_FIELD_LEN); + m_in_data = false; + m_hdr_pos = 0; + m_contents_size = 0; + m_data_pos = 0; + } + bool Complete() const override + { + if (!m_in_data) { + return false; + } + return (BIP324_HEADER_LEN + m_contents_size + RFC8439_EXPANSION == m_data_pos); + } + void SetVersion(int nVersionIn) override + { + vRecv.SetVersion(nVersionIn); + } + int readHeader(Span pkt_bytes); + int readData(Span pkt_bytes); + int Read(Span& pkt_bytes) override + { + int ret = m_in_data ? readData(pkt_bytes) : readHeader(pkt_bytes); + if (ret < 0) { + Reset(); + } else { + pkt_bytes = pkt_bytes.subspan(ret); + } + return ret; + } + CNetMessage GetMessage(const std::chrono::microseconds time, + bool& reject_message, + bool& disconnect, + Span aad) override; }; /** The TransportSerializer prepares messages for the network transport @@ -326,13 +413,28 @@ class V1TransportDeserializer final : public TransportDeserializer class TransportSerializer { public: // prepare message for transport (header construction, error-correction computation, payload encryption, etc.) - virtual void prepareForTransport(CSerializedNetMsg& msg, std::vector& header) const = 0; + virtual bool prepareForTransport(CSerializedNetMsg& msg, std::vector& header) const = 0; virtual ~TransportSerializer() {} }; -class V1TransportSerializer : public TransportSerializer { +class V1TransportSerializer : public TransportSerializer +{ public: - void prepareForTransport(CSerializedNetMsg& msg, std::vector& header) const override; + bool prepareForTransport(CSerializedNetMsg& msg, std::vector& header) const override; +}; + +class V2TransportSerializer : public TransportSerializer +{ +private: + std::unique_ptr m_cipher_suite; + +public: + V2TransportSerializer(const BIP324Key& key_L, + const BIP324Key& key_P, + const std::array& rekey_salt) + : m_cipher_suite(new BIP324CipherSuite(key_L, key_P, rekey_salt)) {} + // prepare for next message + bool prepareForTransport(CSerializedNetMsg& msg, std::vector& header) const override; }; struct CNodeOptions @@ -342,15 +444,28 @@ struct CNodeOptions bool prefer_evict = false; }; +struct BIP324Session { + BIP324Key initiator_L; + BIP324Key initiator_P; + BIP324Key responder_L; + BIP324Key responder_P; + BIP324Key session_id; + std::array rekey_salt; + std::array initiator_garbage_terminator; + std::array responder_garbage_terminator; +}; + +void DeriveBIP324Session(ECDHSecret&& ecdh_secret, BIP324Session& session); + /** Information about a peer */ class CNode { friend class CConnman; - friend struct ConnmanTestMsg; + friend class ConnmanTestMsg; public: - const std::unique_ptr m_deserializer; // Used only by SocketHandler thread - const std::unique_ptr m_serializer; + std::unique_ptr m_deserializer; // Used only by SocketHandler thread + std::unique_ptr m_serializer; const NetPermissionFlags m_permission_flags; @@ -414,6 +529,10 @@ class CNode const uint64_t nKeyedNetGroup; std::atomic_bool fPauseRecv{false}; std::atomic_bool fPauseSend{false}; + TransportProtocolType m_transport_type; + std::atomic_bool v2_key_exchange_complete{false}; + std::atomic_bool m_authenticated_v2_garbage{false}; + bool v2_garbage_terminated{false}; bool IsOutboundOrBlockRelayConn() const { switch (m_conn_type) { @@ -454,6 +573,11 @@ class CNode return m_conn_type == ConnectionType::INBOUND; } + bool PreferV2Conn() const + { + return m_prefer_p2p_v2; + } + bool ExpectServicesFromConn() const { switch (m_conn_type) { case ConnectionType::INBOUND: @@ -519,6 +643,9 @@ class CNode * criterium in CConnman::AttemptToEvictConnection. */ std::atomic m_min_ping_time{std::chrono::microseconds::max()}; + EllSwiftPubKey ellswift_pubkey; + std::vector peer_ellswift_buf; + CNode(NodeId id, std::shared_ptr sock, const CAddress& addrIn, @@ -528,7 +655,9 @@ class CNode const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion, - CNodeOptions&& node_opts = {}); + CNodeOptions&& node_opts = {}, + bool prefer_p2p_v2 = false, + TransportProtocolType transport_type_in = TransportProtocolType::V1); CNode(const CNode&) = delete; CNode& operator=(const CNode&) = delete; @@ -594,6 +723,9 @@ class CNode m_min_ping_time = std::min(m_min_ping_time.load(), ping_time); } + void InitV2P2P(const Span initiator_ellswift, const Span responder_ellswift, bool initiating); + void EnsureInitV2Key(bool initiating); + private: const NodeId id; const uint64_t nLocalHostNonce; @@ -601,6 +733,11 @@ class CNode std::atomic m_greatest_common_version{INIT_PROTO_VERSION}; std::list vRecvMsg; // Used only by SocketHandler thread + CKey v2_priv_key; + std::array v2_sent_garbage_terminator; + std::array v2_recv_garbage_terminator; + std::vector v2_garbage_bytes_recd; + bool v2_keys_derived{false}; // Our address, as reported by the peer CService addrLocal GUARDED_BY(m_addr_local_mutex); @@ -620,6 +757,10 @@ class CNode * Otherwise this unique_ptr is empty. */ std::unique_ptr m_i2p_sam_session GUARDED_BY(m_sock_mutex); + + // Peer prefers a BIP324(v2) p2p transport + bool m_prefer_p2p_v2; + std::string m_v2_session_id; }; /** @@ -634,6 +775,9 @@ class NetEventsInterface /** Initialize a peer (setup state, queue any initial messages) */ virtual void InitializeNode(CNode& node, ServiceFlags our_services) = 0; + /** Initialize the P2P protocol with a peer */ + virtual void InitP2P(CNode& pnode, ServiceFlags our_services) = 0; + /** Handle removal of a peer (clear state) */ virtual void FinalizeNode(const CNode& node) = 0; @@ -721,15 +865,18 @@ class CConnman vWhitelistedRange = connOptions.vWhitelistedRange; { LOCK(m_added_nodes_mutex); - m_added_nodes = connOptions.m_added_nodes; + + for (const std::string& strAddedNode : connOptions.m_added_nodes) { + // -addnode cli arg does not currently have a way to signal BIP324 support + m_added_nodes.push_back({strAddedNode, false}); + } } m_onion_binds = connOptions.onion_binds; } CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, const NetGroupManager& netgroupman, bool network_active = true); - - ~CConnman(); + virtual ~CConnman(); bool Start(CScheduler& scheduler, const Options& options) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !m_added_nodes_mutex, !m_addr_fetches_mutex, !mutexMsgProc); @@ -745,12 +892,14 @@ class CConnman bool GetNetworkActive() const { return fNetworkActive; }; bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; }; void SetNetworkActive(bool active); - void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type); + void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); bool CheckIncomingNonce(uint64_t nonce); bool ForNode(NodeId id, std::function func); void PushMessage(CNode* pnode, CSerializedNetMsg&& msg) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); + void PushV2EllSwiftPubkey(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); + void PushV2GarbageTerminator(CNode* pnode); using NodeFn = std::function; void ForEachNode(const NodeFn& func) @@ -805,7 +954,7 @@ class CConnman // Count the number of block-relay-only peers we have over our limit. int GetExtraBlockRelayCount() const; - bool AddNode(const std::string& node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); + bool AddNode(const AddedNodeParams& added_node_params) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); bool RemoveAddedNode(const std::string& node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); std::vector GetAddedNodeInfo() const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); @@ -821,7 +970,7 @@ class CConnman * - Max total outbound connection capacity filled * - Max connection capacity for type is filled */ - bool AddConnection(const std::string& address, ConnectionType conn_type); + bool AddConnection(const std::string& address, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); size_t GetNodeCount(ConnectionDirection) const; void GetNodeStats(std::vector& vstats) const; @@ -835,7 +984,7 @@ class CConnman //! //! The data returned by this is used in CNode construction, //! which is used to advertise which services we are offering - //! that peer during `net_processing.cpp:PushNodeVersion()`. + //! that peer during `net_processing.cpp:InitP2P()`. ServiceFlags GetLocalServices() const; uint64_t GetMaxOutboundTarget() const EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); @@ -887,10 +1036,10 @@ class CConnman bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions); bool InitBinds(const Options& options); - void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); + void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_total_bytes_sent_mutex); void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex); - void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex); - void ThreadOpenConnections(std::vector connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex); + void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_total_bytes_sent_mutex); + void ThreadOpenConnections(std::vector connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex, !m_total_bytes_sent_mutex); void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc); void ThreadI2PAcceptIncoming(); void AcceptConnection(const ListenSocket& hListenSocket); @@ -1006,7 +1155,10 @@ class CConnman const NetGroupManager& m_netgroupman; std::deque m_addr_fetches GUARDED_BY(m_addr_fetches_mutex); Mutex m_addr_fetches_mutex; - std::vector m_added_nodes GUARDED_BY(m_added_nodes_mutex); + + // connection string and whether to use v2 p2p + std::vector m_added_nodes GUARDED_BY(m_added_nodes_mutex); + mutable Mutex m_added_nodes_mutex; std::vector m_nodes GUARDED_BY(m_nodes_mutex); std::list m_nodes_disconnected; @@ -1166,7 +1318,7 @@ class CConnman }; friend struct CConnmanTest; - friend struct ConnmanTestMsg; + friend class ConnmanTestMsg; }; /** Dump binary message to file, with timestamp */ diff --git a/src/net_processing.cpp b/src/net_processing.cpp index c87b0e7cd2b79..f4af367723810 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -514,6 +514,7 @@ class PeerManagerImpl final : public PeerManager EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex); bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, g_msgproc_mutex); + void InitP2P(CNode& pnode, ServiceFlags our_services) override; /** Implement PeerManager */ void StartScheduledTasks(CScheduler& scheduler) override; @@ -669,9 +670,6 @@ class PeerManagerImpl final : public PeerManager void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - /** Send a version message to a peer */ - void PushNodeVersion(CNode& pnode, const Peer& peer); - /** Send a ping message every PING_INTERVAL or if requested via RPC. May * mark the peer to be disconnected if a ping has timed out. * We use mockable time for ping timeouts, so setmocktime may cause pings @@ -1368,9 +1366,9 @@ void PeerManagerImpl::FindNextBlocksToDownload(const Peer& peer, unsigned int co } // namespace -void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer) +void PeerManagerImpl::InitP2P(CNode& pnode, ServiceFlags our_services) { - uint64_t my_services{peer.m_our_services}; + uint64_t my_services{our_services}; const int64_t nTime{count_seconds(GetTime())}; uint64_t nonce = pnode.GetLocalNonce(); const int nNodeStartingHeight{m_best_height}; @@ -1440,8 +1438,9 @@ void PeerManagerImpl::InitializeNode(CNode& node, ServiceFlags our_services) LOCK(m_peer_mutex); m_peer_map.emplace_hint(m_peer_map.end(), nodeid, peer); } - if (!node.IsInboundConn()) { - PushNodeVersion(node, *peer); + if (!node.IsInboundConn() && !node.PreferV2Conn()) { + // Initializing a V1(pre-BIP324) node, so we can start the P2P protocol with a VERSION message + InitP2P(node, peer->m_our_services); } } @@ -3221,7 +3220,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // Inbound peers send us their version message when they connect. // We send our version message in response. if (pfrom.IsInboundConn()) { - PushNodeVersion(pfrom, *peer); + InitP2P(pfrom, peer->m_our_services); } // Change version diff --git a/src/node/connection_types.cpp b/src/node/connection_types.cpp index 904f4371aafd1..366e321825dfd 100644 --- a/src/node/connection_types.cpp +++ b/src/node/connection_types.cpp @@ -24,3 +24,15 @@ std::string ConnectionTypeAsString(ConnectionType conn_type) assert(false); } + +std::string TransportTypeAsString(TransportProtocolType transport_type) +{ + switch (transport_type) { + case TransportProtocolType::V1: + return "v1"; + case TransportProtocolType::V2: + return "v2"; + } // no default case, so the compiler can warn about missing cases + + assert(false); +} diff --git a/src/node/connection_types.h b/src/node/connection_types.h index 5e1abcace67d1..f2977abdfd943 100644 --- a/src/node/connection_types.h +++ b/src/node/connection_types.h @@ -79,4 +79,13 @@ enum class ConnectionType { /** Convert ConnectionType enum to a string value */ std::string ConnectionTypeAsString(ConnectionType conn_type); +/** Transport layer version */ +enum class TransportProtocolType { + V1, // unencrypted, plaintext protocol + V2, // BIP324 protocol +}; + +/** Convert TransportProtocolType enum to a string value */ +std::string TransportTypeAsString(TransportProtocolType transport_type); + #endif // BITCOIN_NODE_CONNECTION_TYPES_H diff --git a/src/protocol.cpp b/src/protocol.cpp index 23c68b335bff9..660b01e294f4c 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -47,47 +47,47 @@ const char *WTXIDRELAY="wtxidrelay"; const char *SENDTXRCNCL="sendtxrcncl"; } // namespace NetMsgType -/** All known message types. Keep this in the same order as the list of - * messages above and in protocol.h. +/** All known message types including the short-ID (as initially defined in BIP324). + * Keep this in the same order as the list of messages above and in protocol.h. */ -const static std::string allNetMessageTypes[] = { - NetMsgType::VERSION, - NetMsgType::VERACK, - NetMsgType::ADDR, - NetMsgType::ADDRV2, - NetMsgType::SENDADDRV2, - NetMsgType::INV, - NetMsgType::GETDATA, - NetMsgType::MERKLEBLOCK, - NetMsgType::GETBLOCKS, - NetMsgType::GETHEADERS, - NetMsgType::TX, - NetMsgType::HEADERS, - NetMsgType::BLOCK, - NetMsgType::GETADDR, - NetMsgType::MEMPOOL, - NetMsgType::PING, - NetMsgType::PONG, - NetMsgType::NOTFOUND, - NetMsgType::FILTERLOAD, - NetMsgType::FILTERADD, - NetMsgType::FILTERCLEAR, - NetMsgType::SENDHEADERS, - NetMsgType::FEEFILTER, - NetMsgType::SENDCMPCT, - NetMsgType::CMPCTBLOCK, - NetMsgType::GETBLOCKTXN, - NetMsgType::BLOCKTXN, - NetMsgType::GETCFILTERS, - NetMsgType::CFILTER, - NetMsgType::GETCFHEADERS, - NetMsgType::CFHEADERS, - NetMsgType::GETCFCHECKPT, - NetMsgType::CFCHECKPT, - NetMsgType::WTXIDRELAY, - NetMsgType::SENDTXRCNCL, -}; -const static std::vector allNetMessageTypesVec(std::begin(allNetMessageTypes), std::end(allNetMessageTypes)); +const static std::map allNetMessageTypes = { + {37, NetMsgType::VERSION}, + {36, NetMsgType::VERACK}, + {13, NetMsgType::ADDR}, + {45, NetMsgType::ADDRV2}, + {46, NetMsgType::SENDADDRV2}, + {27, NetMsgType::INV}, + {24, NetMsgType::GETDATA}, + {29, NetMsgType::MERKLEBLOCK}, + {22, NetMsgType::GETBLOCKS}, + {25, NetMsgType::GETHEADERS}, + {35, NetMsgType::TX}, + {26, NetMsgType::HEADERS}, + {14, NetMsgType::BLOCK}, + {21, NetMsgType::GETADDR}, + {28, NetMsgType::MEMPOOL}, + {31, NetMsgType::PING}, + {32, NetMsgType::PONG}, + {30, NetMsgType::NOTFOUND}, + {20, NetMsgType::FILTERLOAD}, + {18, NetMsgType::FILTERADD}, + {19, NetMsgType::FILTERCLEAR}, + {34, NetMsgType::SENDHEADERS}, + {17, NetMsgType::FEEFILTER}, + {33, NetMsgType::SENDCMPCT}, + {16, NetMsgType::CMPCTBLOCK}, + {23, NetMsgType::GETBLOCKTXN}, + {15, NetMsgType::BLOCKTXN}, + {38, NetMsgType::GETCFILTERS}, + {39, NetMsgType::CFILTER}, + {40, NetMsgType::GETCFHEADERS}, + {41, NetMsgType::CFHEADERS}, + {42, NetMsgType::GETCFCHECKPT}, + {43, NetMsgType::CFCHECKPT}, + {44, NetMsgType::WTXIDRELAY}, + {47, NetMsgType::SENDTXRCNCL}}; + +static std::map msgTypeShortIDs; CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn) { @@ -178,9 +178,9 @@ std::string CInv::ToString() const } } -const std::vector &getAllNetMessageTypes() +const std::map& getAllNetMessageTypes() { - return allNetMessageTypesVec; + return allNetMessageTypes; } /** @@ -198,6 +198,7 @@ static std::string serviceFlagToStr(size_t bit) case NODE_WITNESS: return "WITNESS"; case NODE_COMPACT_FILTERS: return "COMPACT_FILTERS"; case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED"; + case NODE_P2P_V2: return "P2P_V2"; // Not using default, so we get warned when a case is missing } @@ -222,3 +223,27 @@ GenTxid ToGenTxid(const CInv& inv) assert(inv.IsGenTxMsg()); return inv.IsMsgWtx() ? GenTxid::Wtxid(inv.hash) : GenTxid::Txid(inv.hash); } + +std::optional GetShortIDFromMessageType(const std::string& message_type) +{ + if (msgTypeShortIDs.size() != allNetMessageTypes.size()) { + for (const std::pair entry : allNetMessageTypes) { + msgTypeShortIDs[entry.second] = entry.first; + } + } + + auto it = msgTypeShortIDs.find(message_type); + if (it != msgTypeShortIDs.end()) { + return it->second; + } + return {}; +} + +std::optional GetMessageTypeFromShortID(const uint8_t shortID) +{ + auto it = allNetMessageTypes.find(shortID); + if (it != allNetMessageTypes.end()) { + return it->second; + } + return {}; +} diff --git a/src/protocol.h b/src/protocol.h index 17a363b1d3247..3d3a13d40e02a 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -268,8 +268,17 @@ extern const char* WTXIDRELAY; extern const char* SENDTXRCNCL; }; // namespace NetMsgType -/* Get a vector of all valid message types (see above) */ -const std::vector& getAllNetMessageTypes(); +/* Get a map of all valid message types (see above) */ +const std::map& getAllNetMessageTypes(); + +/** Short message type IDs are a low bandwidth representations of a message type + * The mapping is a peer to peer agreement (initially defined in BIP324) + * + * returns the short ID for a message type (if known) */ +std::optional GetShortIDFromMessageType(const std::string& message_type); + +/** returns the message type (string) from a short ID (as initially defined in BIP324) */ +std::optional GetMessageTypeFromShortID(const uint8_t shortID); /** nServices flags */ enum ServiceFlags : uint64_t { @@ -294,6 +303,9 @@ enum ServiceFlags : uint64_t { // See BIP159 for details on how this is implemented. NODE_NETWORK_LIMITED = (1 << 10), + // NODE_P2P_V2 means the node supports BIP324 transport + NODE_P2P_V2 = (1 << 11), + // Bits 24-31 are reserved for temporary experiments. Just pick a bit that // isn't getting used, or one not being used much, and notify the // bitcoin-development mailing list. Remember that service bits are just @@ -399,7 +411,7 @@ class CAddress : public CService // be hashed (except through CHashWriter in addrdb.cpp, which sets SER_DISK), and it's // ambiguous what that would mean. Make sure no code relying on that is introduced: assert(!(s.GetType() & SER_GETHASH)); - bool use_v2; + bool use_addr_v2; if (s.GetType() & SER_DISK) { // In the disk serialization format, the encoding (v1 or v2) is determined by a flag version // that's part of the serialization itself. ADDRV2_FORMAT in the stream version only determines @@ -409,10 +421,10 @@ class CAddress : public CService READWRITE(stored_format_version); stored_format_version &= ~DISK_VERSION_IGNORE_MASK; // ignore low bits if (stored_format_version == 0) { - use_v2 = false; + use_addr_v2 = false; } else if (stored_format_version == DISK_VERSION_ADDRV2 && (s.GetVersion() & ADDRV2_FORMAT)) { // Only support v2 deserialization if ADDRV2_FORMAT is set. - use_v2 = true; + use_addr_v2 = true; } else { throw std::ios_base::failure("Unsupported CAddress disk format version"); } @@ -421,12 +433,12 @@ class CAddress : public CService // the value of ADDRV2_FORMAT in the stream version, as no explicitly encoded version // exists in the stream. assert(s.GetType() & SER_NETWORK); - use_v2 = s.GetVersion() & ADDRV2_FORMAT; + use_addr_v2 = s.GetVersion() & ADDRV2_FORMAT; } READWRITE(Using>(obj.nTime)); // nServices is serialized as CompactSize in V2; as uint64_t in V1. - if (use_v2) { + if (use_addr_v2) { uint64_t services_tmp; SER_WRITE(obj, services_tmp = obj.nServices); READWRITE(Using>(services_tmp)); @@ -435,7 +447,7 @@ class CAddress : public CService READWRITE(Using>(obj.nServices)); } // Invoke V1/V2 serializer for CService parent object. - OverrideStream os(&s, s.GetType(), use_v2 ? ADDRV2_FORMAT : 0); + OverrideStream os(&s, s.GetType(), use_addr_v2 ? ADDRV2_FORMAT : 0); SerReadWriteMany(os, ser_action, ReadWriteAsHelper(obj)); } diff --git a/src/pubkey.h b/src/pubkey.h index 0485a38f7215d..370a973465de1 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include #include @@ -29,6 +31,9 @@ class CKeyID : public uint160 typedef uint256 ChainCode; +constexpr size_t ELLSWIFT_ENCODED_SIZE = 64; +using EllSwiftPubKey = std::array; + /** An encapsulated public key. */ class CPubKey { diff --git a/src/random.cpp b/src/random.cpp index eab54630b18f2..9a6841c1da76d 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -599,18 +599,15 @@ uint256 GetRandHash() noexcept void FastRandomContext::RandomSeed() { uint256 seed = GetRandHash(); - rng.SetKey(seed.begin(), 32); + rng.SetKey32(seed.begin()); requires_seed = false; } uint256 FastRandomContext::rand256() noexcept { - if (bytebuf_size < 32) { - FillByteBuffer(); - } + if (requires_seed) RandomSeed(); uint256 ret; - memcpy(ret.begin(), bytebuf + 64 - bytebuf_size, 32); - bytebuf_size -= 32; + rng.Keystream(ret.data(), ret.size()); return ret; } @@ -624,9 +621,9 @@ std::vector FastRandomContext::randbytes(size_t len) return ret; } -FastRandomContext::FastRandomContext(const uint256& seed) noexcept : requires_seed(false), bytebuf_size(0), bitbuf_size(0) +FastRandomContext::FastRandomContext(const uint256& seed) noexcept : requires_seed(false), bitbuf_size(0) { - rng.SetKey(seed.begin(), 32); + rng.SetKey32(seed.begin()); } bool Random_SanityCheck() @@ -675,25 +672,22 @@ bool Random_SanityCheck() return true; } -FastRandomContext::FastRandomContext(bool fDeterministic) noexcept : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) +FastRandomContext::FastRandomContext(bool fDeterministic) noexcept : requires_seed(!fDeterministic), bitbuf_size(0) { if (!fDeterministic) { return; } uint256 seed; - rng.SetKey(seed.begin(), 32); + rng.SetKey32(seed.begin()); } FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexcept { requires_seed = from.requires_seed; rng = from.rng; - std::copy(std::begin(from.bytebuf), std::end(from.bytebuf), std::begin(bytebuf)); - bytebuf_size = from.bytebuf_size; bitbuf = from.bitbuf; bitbuf_size = from.bitbuf_size; from.requires_seed = true; - from.bytebuf_size = 0; from.bitbuf_size = 0; return *this; } diff --git a/src/random.h b/src/random.h index 5fe20c5f760aa..15772500f926d 100644 --- a/src/random.h +++ b/src/random.h @@ -145,23 +145,11 @@ class FastRandomContext bool requires_seed; ChaCha20 rng; - unsigned char bytebuf[64]; - int bytebuf_size; - uint64_t bitbuf; int bitbuf_size; void RandomSeed(); - void FillByteBuffer() - { - if (requires_seed) { - RandomSeed(); - } - rng.Keystream(bytebuf, sizeof(bytebuf)); - bytebuf_size = sizeof(bytebuf); - } - void FillBitBuffer() { bitbuf = rand64(); @@ -185,10 +173,10 @@ class FastRandomContext /** Generate a random 64-bit integer. */ uint64_t rand64() noexcept { - if (bytebuf_size < 8) FillByteBuffer(); - uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size); - bytebuf_size -= 8; - return ret; + if (requires_seed) RandomSeed(); + unsigned char buf[8]; + rng.Keystream(buf, 8); + return ReadLE64(buf); } /** Generate a random (bits)-bit integer. */ diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 8688263ef547e..e7401f5b0e2ce 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -213,6 +213,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "addpeeraddress", 1, "port"}, { "addpeeraddress", 2, "tried"}, { "stop", 0, "wait" }, + { "addnode", 2, "p2p_v2" }, }; // clang-format on diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 8d7f4e7f5b25e..8e1168ff616b9 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -44,6 +44,10 @@ const std::vector CONNECTION_TYPE_DOC{ "feeler (short-lived automatic connection for testing addresses)" }; +const std::vector TRANSPORT_TYPE_DOC{ + "v1 (plaintext transport protocol)", + "v2 (BIP324 encrypted transport protocol)"}; + static RPCHelpMan getconnectioncount() { return RPCHelpMan{"getconnectioncount", @@ -163,6 +167,8 @@ static RPCHelpMan getpeerinfo() {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n" "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n" "best capture connection behaviors."}, + {RPCResult::Type::STR, "transport_protocol_type", "Type of transport protocol: \n" + Join(TRANSPORT_TYPE_DOC, ",\n") + ".\n"}, + {RPCResult::Type::STR, "v2_session_id", /* optional */ true, "BIP324 session id for an encrypted v2 connection.\n"}, }}, }}, }, @@ -263,6 +269,10 @@ static RPCHelpMan getpeerinfo() } obj.pushKV("bytesrecv_per_msg", recvPerMsgType); obj.pushKV("connection_type", ConnectionTypeAsString(stats.m_conn_type)); + obj.pushKV("transport_protocol_type", TransportTypeAsString(stats.m_transport_type)); + if (stats.m_transport_type == TransportProtocolType::V2) { + obj.pushKV("v2_session_id", stats.m_v2_session_id); + } ret.push_back(obj); } @@ -284,11 +294,12 @@ static RPCHelpMan addnode() { {"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The node (see getpeerinfo for nodes)"}, {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"}, + {"p2p_v2", RPCArg::Type::BOOL, RPCArg::Default{false}, "Peer supports BIP324 v2 transport protocol"}, }, RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{ - HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") - + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"") + HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\" true") + + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\" true") }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { @@ -304,17 +315,21 @@ static RPCHelpMan addnode() CConnman& connman = EnsureConnman(node); std::string strNode = request.params[0].get_str(); + bool use_p2p_v2 = !request.params[2].isNull() && request.params[2].get_bool(); if (strCommand == "onetry") { CAddress addr; + if (use_p2p_v2) { + addr.nServices = ServiceFlags(addr.nServices | NODE_P2P_V2); + } connman.OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), ConnectionType::MANUAL); return UniValue::VNULL; } if (strCommand == "add") { - if (!connman.AddNode(strNode)) { + if (!connman.AddNode({strNode, use_p2p_v2})) { throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); } } @@ -473,7 +488,7 @@ static RPCHelpMan getaddednodeinfo() if (!request.params[0].isNull()) { bool found = false; for (const AddedNodeInfo& info : vInfo) { - if (info.strAddedNode == request.params[0].get_str()) { + if (info.m_params.m_added_node == request.params[0].get_str()) { vInfo.assign(1, info); found = true; break; @@ -488,7 +503,7 @@ static RPCHelpMan getaddednodeinfo() for (const AddedNodeInfo& info : vInfo) { UniValue obj(UniValue::VOBJ); - obj.pushKV("addednode", info.strAddedNode); + obj.pushKV("addednode", info.m_params.m_added_node); obj.pushKV("connected", info.fConnected); UniValue addresses(UniValue::VARR); if (info.fConnected) { diff --git a/src/secp256k1/.cirrus.yml b/src/secp256k1/.cirrus.yml index a2e7f36d1fa3d..4e2429779efec 100644 --- a/src/secp256k1/.cirrus.yml +++ b/src/secp256k1/.cirrus.yml @@ -18,6 +18,7 @@ env: ECDH: no RECOVERY: no SCHNORRSIG: no + ELLSWIFT: no ### test options SECP256K1_TEST_ITERS: BENCH: yes @@ -67,11 +68,11 @@ task: << : *LINUX_CONTAINER matrix: &ENV_MATRIX - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes} - env: {WIDEMUL: int128} - - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes, ELLSWIFT: yes} - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes} - - env: {WIDEMUL: int128, ASM: x86_64} + - env: {WIDEMUL: int128, ASM: x86_64 , ELLSWIFT: yes} - env: { RECOVERY: yes, SCHNORRSIG: yes} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} @@ -178,6 +179,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no << : *MERGE_BASE test_script: @@ -197,6 +199,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no matrix: - env: {} @@ -217,6 +220,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no << : *MERGE_BASE test_script: @@ -234,6 +238,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no << : *MERGE_BASE test_script: @@ -241,17 +246,59 @@ task: << : *CAT_LOGS task: - name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)" << : *LINUX_CONTAINER env: - WRAPPER_CMD: wine64-stable - SECP256K1_TEST_ITERS: 16 - HOST: x86_64-w64-mingw32 + WRAPPER_CMD: wine + WITH_VALGRIND: no + ECDH: yes + RECOVERY: yes + SCHNORRSIG: yes + CTIMETEST: no + matrix: + - name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)" + env: + HOST: x86_64-w64-mingw32 + - name: "i686 (mingw32-w64): Windows (Debian stable, Wine)" + env: + HOST: i686-w64-mingw32 + << : *MERGE_BASE + test_script: + - ./ci/cirrus.sh + << : *CAT_LOGS + +task: + << : *LINUX_CONTAINER + env: + WRAPPER_CMD: wine + WERROR_CFLAGS: -WX WITH_VALGRIND: no ECDH: yes RECOVERY: yes + EXPERIMENTAL: yes SCHNORRSIG: yes + ELLSWIFT: yes + ELLSWIFT: yes CTIMETEST: no + # Set non-essential options that affect the CLI messages here. + # (They depend on the user's taste, so we don't want to set them automatically in configure.ac.) + CFLAGS: -nologo -diagnostics:caret + LDFLAGS: -XCClinker -nologo -XCClinker -diagnostics:caret + # Use a MinGW-w64 host to tell ./configure we're building for Windows. + # This will detect some MinGW-w64 tools but then make will need only + # the MSVC tools CC, AR and NM as specified below. + matrix: + - name: "x86_64 (MSVC): Windows (Debian stable, Wine)" + env: + HOST: x86_64-w64-mingw32 + CC: /opt/msvc/bin/x64/cl + AR: /opt/msvc/bin/x64/lib + NM: /opt/msvc/bin/x64/dumpbin -symbols -headers + - name: "i686 (MSVC): Windows (Debian stable, Wine)" + env: + HOST: i686-w64-mingw32 + CC: /opt/msvc/bin/x86/cl + AR: /opt/msvc/bin/x86/lib + NM: /opt/msvc/bin/x86/dumpbin -symbols -headers << : *MERGE_BASE test_script: - ./ci/cirrus.sh @@ -264,6 +311,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no matrix: - name: "Valgrind (memcheck)" @@ -302,22 +350,30 @@ task: << : *CAT_LOGS task: - name: "C++ -fpermissive" + name: "C++ -fpermissive (entire project)" << : *LINUX_CONTAINER env: - # ./configure correctly errors out when given CC=g++. - # We hack around this by passing CC=g++ only to make. - CC: gcc - MAKEFLAGS: -j4 CC=g++ CFLAGS=-fpermissive\ -g + CC: g++ + CFLAGS: -fpermissive -g + CPPFLAGS: -DSECP256K1_CPLUSPLUS_TEST_OVERRIDE WERROR_CFLAGS: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS +task: + name: "C++ (public headers)" + << : *LINUX_CONTAINER + test_script: + - g++ -Werror include/*.h + - clang -Werror -x c++-header include/*.h + - /opt/msvc/bin/x64/cl.exe -c -WX -TP include/*.h + task: name: "sage prover" << : *LINUX_CONTAINER diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am index 51c5960301a49..145baee617755 100644 --- a/src/secp256k1/Makefile.am +++ b/src/secp256k1/Makefile.am @@ -58,7 +58,6 @@ noinst_HEADERS += src/hash_impl.h noinst_HEADERS += src/field.h noinst_HEADERS += src/field_impl.h noinst_HEADERS += src/bench.h -noinst_HEADERS += src/basic-config.h noinst_HEADERS += contrib/lax_der_parsing.h noinst_HEADERS += contrib/lax_der_parsing.c noinst_HEADERS += contrib/lax_der_privatekey_parsing.h @@ -87,7 +86,7 @@ endif endif libsecp256k1_la_SOURCES = src/secp256k1.c -libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) +libsecp256k1_la_CPPFLAGS = $(SECP_INCLUDES) libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB) libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE) @@ -112,7 +111,7 @@ TESTS = if USE_TESTS noinst_PROGRAMS += tests tests_SOURCES = src/tests.c -tests_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) +tests_CPPFLAGS = $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) if VALGRIND_ENABLED tests_CPPFLAGS += -DVALGRIND noinst_PROGRAMS += valgrind_ctime_test @@ -228,3 +227,7 @@ endif if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif + +if ENABLE_MODULE_ELLSWIFT +include src/modules/ellswift/Makefile.am.include +endif diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md index f5db915e83e7b..ffdc9aeaee5b8 100644 --- a/src/secp256k1/README.md +++ b/src/secp256k1/README.md @@ -2,6 +2,8 @@ libsecp256k1 ============ [![Build Status](https://api.cirrus-ci.com/github/bitcoin-core/secp256k1.svg?branch=master)](https://cirrus-ci.com/github/bitcoin-core/secp256k1) +![Dependencies: None](https://img.shields.io/badge/dependencies-none-success) +[![irc.libera.chat #secp256k1](https://img.shields.io/badge/irc.libera.chat-%23secp256k1-success)](https://web.libera.chat/#secp256k1) Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1. @@ -15,6 +17,7 @@ Features: * Derandomized ECDSA (via RFC6979 or with a caller provided function.) * Very efficient implementation. * Suitable for embedded systems. +* No runtime dependencies. * Optional module for public key recovery. * Optional module for ECDH key exchange. * Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). @@ -72,11 +75,12 @@ To compile optional modules (such as Schnorr signatures), you need to run `./con Usage examples ----------- - Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. +Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. * [ECDSA example](examples/ecdsa.c) * [Schnorr signatures example](examples/schnorr.c) * [Deriving a shared secret (ECDH) example](examples/ecdh.c) - To compile the Schnorr signature and ECDH examples, you also need to configure with `--enable-module-schnorrsig` and `--enable-module-ecdh`. + +To compile the Schnorr signature and ECDH examples, you also need to configure with `--enable-module-schnorrsig` and `--enable-module-ecdh`. Test coverage ----------- diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 index 9cb54de098129..98be915b67aec 100644 --- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 +++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 @@ -10,6 +10,7 @@ AC_MSG_RESULT([$has_64bit_asm]) ]) AC_DEFUN([SECP_VALGRIND_CHECK],[ +AC_MSG_CHECKING([for valgrind support]) if test x"$has_valgrind" != x"yes"; then CPPFLAGS_TEMP="$CPPFLAGS" CPPFLAGS="$VALGRIND_CPPFLAGS $CPPFLAGS" @@ -21,6 +22,7 @@ if test x"$has_valgrind" != x"yes"; then #endif ]])], [has_valgrind=yes; AC_DEFINE(HAVE_VALGRIND,1,[Define this symbol if valgrind is installed, and it supports the host platform])]) fi +AC_MSG_RESULT($has_valgrind) ]) dnl SECP_TRY_APPEND_CFLAGS(flags, VAR) diff --git a/src/secp256k1/ci/cirrus.sh b/src/secp256k1/ci/cirrus.sh index b85f012d3f3c0..8779b6fa52c00 100755 --- a/src/secp256k1/ci/cirrus.sh +++ b/src/secp256k1/ci/cirrus.sh @@ -5,10 +5,20 @@ set -x export LC_ALL=C +# Start persistent wineserver if necessary. +# This speeds up jobs with many invocations of wine (e.g., ./configure with MSVC) tremendously. +case "$WRAPPER_CMD" in + *wine*) + # This is apparently only reliable when we run a dummy command such as "hh.exe" afterwards. + wineserver -p && wine hh.exe + ;; +esac + env >> test_env.log $CC -v || true valgrind --version || true +$WRAPPER_CMD --version || true ./autogen.sh @@ -18,6 +28,7 @@ valgrind --version || true --with-ecmult-window="$ECMULTWINDOW" \ --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-ellswift="$ELLSWIFT" \ --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-examples="$EXAMPLES" \ --with-valgrind="$WITH_VALGRIND" \ @@ -63,6 +74,9 @@ then make precomp fi +# Shutdown wineserver again +wineserver -k || true + # Check that no repo files have been modified by the build. # (This fails for example if the precomp files need to be updated in the repo.) git diff --exit-code diff --git a/src/secp256k1/ci/linux-debian.Dockerfile b/src/secp256k1/ci/linux-debian.Dockerfile index 5cccbb5565f03..a83a4e36db040 100644 --- a/src/secp256k1/ci/linux-debian.Dockerfile +++ b/src/secp256k1/ci/linux-debian.Dockerfile @@ -1,15 +1,14 @@ FROM debian:stable -RUN dpkg --add-architecture i386 -RUN dpkg --add-architecture s390x -RUN dpkg --add-architecture armhf -RUN dpkg --add-architecture arm64 -RUN dpkg --add-architecture ppc64el -RUN apt-get update +RUN dpkg --add-architecture i386 && \ + dpkg --add-architecture s390x && \ + dpkg --add-architecture armhf && \ + dpkg --add-architecture arm64 && \ + dpkg --add-architecture ppc64el # dkpg-dev: to make pkg-config work in cross-builds # llvm: for llvm-symbolizer, which is used by clang's UBSan for symbolized stack traces -RUN apt-get install --no-install-recommends --no-upgrade -y \ +RUN apt-get update && apt-get install --no-install-recommends -y \ git ca-certificates \ make automake libtool pkg-config dpkg-dev valgrind qemu-user \ gcc clang llvm libc6-dbg \ @@ -19,8 +18,20 @@ RUN apt-get install --no-install-recommends --no-upgrade -y \ gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \ gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \ gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \ - wine gcc-mingw-w64-x86-64 \ + gcc-mingw-w64-x86-64-win32 wine64 wine \ + gcc-mingw-w64-i686-win32 wine32 \ sagemath -# Run a dummy command in wine to make it set up configuration -RUN wine64-stable xcopy || true +WORKDIR /root +# The "wine" package provides a convience wrapper that we need +RUN apt-get update && apt-get install --no-install-recommends -y \ + git ca-certificates wine64 wine python3-simplejson python3-six msitools winbind procps && \ + git clone https://github.com/mstorsjo/msvc-wine && \ + mkdir /opt/msvc && \ + python3 msvc-wine/vsdownload.py --accept-license --dest /opt/msvc Microsoft.VisualStudio.Workload.VCTools && \ + msvc-wine/install.sh /opt/msvc + +# Initialize the wine environment. Wait until the wineserver process has +# exited before closing the session, to avoid corrupting the wine prefix. +RUN wine64 wineboot --init && \ + while (ps -A | grep wineserver) > /dev/null; do sleep 1; done diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac index 2db59a8ff32e1..cf4019e06f5ba 100644 --- a/src/secp256k1/configure.ac +++ b/src/secp256k1/configure.ac @@ -33,12 +33,14 @@ AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_PROG_CC -if test x"$ac_cv_prog_cc_c89" = x"no"; then - AC_MSG_ERROR([c89 compiler support required]) -fi AM_PROG_AS AM_PROG_AR +# Clear some cache variables as a workaround for a bug that appears due to a bad +# interaction between AM_PROG_AR and LT_INIT when combining MSVC's archiver lib.exe. +# https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54421 +AS_UNSET(ac_cv_prog_AR) +AS_UNSET(ac_cv_prog_ac_ct_AR) LT_INIT([win32-dll]) build_windows=no @@ -87,23 +89,35 @@ esac # # TODO We should analogously not touch CPPFLAGS and LDFLAGS but currently there are no issues. AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [ - # Try to append -Werror=unknown-warning-option to CFLAGS temporarily. Otherwise clang will - # not error out if it gets unknown warning flags and the checks here will always succeed - # no matter if clang knows the flag or not. - SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS" - SECP_TRY_APPEND_CFLAGS([-Werror=unknown-warning-option], CFLAGS) - - SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic. - SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic. - SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers - SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall. - SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions. - SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95 - SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0 - SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only - SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0 - - CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS" + # GCC and compatible (incl. clang) + if test "x$GCC" = "xyes"; then + # Try to append -Werror=unknown-warning-option to CFLAGS temporarily. Otherwise clang will + # not error out if it gets unknown warning flags and the checks here will always succeed + # no matter if clang knows the flag or not. + SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS" + SECP_TRY_APPEND_CFLAGS([-Werror=unknown-warning-option], CFLAGS) + + SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic. + SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic. + SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers + SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall. + SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions. + SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95 + SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0 + SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only + SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0 + + CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS" + fi + + # MSVC + # Assume MSVC if we're building for Windows but not with GCC or compatible; + # libtool makes the same assumption internally. + # Note that "/opt" and "-opt" are equivalent for MSVC; we use "-opt" because "/opt" looks like a path. + if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then + SECP_TRY_APPEND_CFLAGS([-W2 -wd4146], $1) # Moderate warning level, disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned" + SECP_TRY_APPEND_CFLAGS([-external:anglebrackets -external:W0], $1) # Suppress warnings from #include <...> files + fi ]) SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS) @@ -156,6 +170,11 @@ AC_ARG_ENABLE(module_schnorrsig, AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])]) +AC_ARG_ENABLE(module_ellswift, + AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module (experimental)]), + [enable_module_ellswift=$enableval], + [enable_module_ellswift=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) @@ -326,7 +345,9 @@ if test x"$enable_valgrind" = x"yes"; then SECP_INCLUDES="$SECP_INCLUDES $VALGRIND_CPPFLAGS" fi -# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI) +# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI). +# We don't want to set the user variable CFLAGS in CI because this would disable +# autoconf's logic for setting default CFLAGS, which we would like to test in CI. SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" ### @@ -346,6 +367,10 @@ if test x"$enable_module_schnorrsig" = x"yes"; then enable_module_extrakeys=yes fi +if test x"$enable_module_ellswift" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ELLSWIFT, 1, [Define this symbol to enable the ElligatorSwift module]) +fi + # Test if extrakeys is set after the schnorrsig module to allow the schnorrsig # module to set enable_module_extrakeys=yes if test x"$enable_module_extrakeys" = x"yes"; then @@ -391,6 +416,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) @@ -411,6 +437,7 @@ echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" +echo " module ellswift = $enable_module_ellswift" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/src/secp256k1/doc/safegcd_implementation.md b/src/secp256k1/doc/safegcd_implementation.md index 063aa8efae05a..c1cdd0cfe178f 100644 --- a/src/secp256k1/doc/safegcd_implementation.md +++ b/src/secp256k1/doc/safegcd_implementation.md @@ -1,7 +1,7 @@ # The safegcd implementation in libsecp256k1 explained -This document explains the modular inverse implementation in the `src/modinv*.h` files. It is based -on the paper +This document explains the modular inverse and Jacobi symbol implementations in the `src/modinv*.h` files. +It is based on the paper ["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd) by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version. @@ -769,3 +769,30 @@ def modinv_var(M, Mi, x): d, e = update_de(d, e, t, M, Mi) return normalize(f, d, Mi) ``` + +## 8. From GCDs to Jacobi symbol + +We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we make corresponding updates to *j* using [properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties). In particular, we update *j* whenever we divide *g* by *2* or swap *f* and *g*; these updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied very quickly. Overall, this calculation is slightly simpler than the one for modular inverse because we no longer need to keep track of *d* and *e*. + +However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative values. We resolve this by using the following modified steps: + +```python + # Before + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g - f) // 2 + + # After + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g + f) // 2 +``` + +The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4 and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm will converge. The justification for posdivsteps is completely empirical: in practice, it appears that the vast majority of inputs converge to *f=g=gcd(f0, g0)* in a number of steps proportional to their logarithm. + +Note that: +- We require inputs to satisfy *gcd(x, M) = 1*. +- We need to update the termination condition from *g=0* to *f=1*. +- We deal with the case where *g=0* on input specially. + +We account for the possibility of nonconvergence by only performing a bounded number of posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not yet been found. + +The optimizations in sections 3-7 above are described in the context of the original divsteps, but in the C implementation we also adapt most of them (not including "avoiding modulus operations", since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate Jacobi symbols for secret data) to the posdivsteps version. diff --git a/src/secp256k1/include/secp256k1_ellswift.h b/src/secp256k1/include/secp256k1_ellswift.h new file mode 100644 index 0000000000000..cda5bac2e3b51 --- /dev/null +++ b/src/secp256k1/include/secp256k1_ellswift.h @@ -0,0 +1,175 @@ +#ifndef SECP256K1_ELLSWIFT_H +#define SECP256K1_ELLSWIFT_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This module provides an implementation of ElligatorSwift as well as + * a version of x-only ECDH using it. + * + * ElligatorSwift is described in https://eprint.iacr.org/2022/759 by + * Chavez-Saab, Rodriguez-Henriquez, and Tibouchi. It permits encoding + * public keys in 64-byte objects which are indistinguishable from + * uniformly random. + * + * Let f be the function from pairs of field elements to point X coordinates, + * defined as follows (all operations modulo p = 2^256 - 2^32 - 977) + * f(u,t): + * - Let C = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852, + * a square root of -3. + * - If u=0, set u=1 instead. + * - If t=0, set t=1 instead. + * - If u^3 + t^2 + 7 = 0, multiply t by 2. + * - Let p = u^3 + t^2 + 7 + * - Let m = u^3 - t^2 + 7 + * - Let v = (C * m / p - 1) * u / 2 + * - Let w = p / (C * t * u) + * - Let x1 = v + * - Let x2 = -u - v + * - Let x3 = u + w^2 + * - Return the first of [x3,x2,x1] that is an X coordinate on the curve + * (at least one of them is, for any inputs u and t). + * + * Then an ElligatorSwift encoding of x consists of the 32-byte big-endian + * encodings of field elements u and t concatenated, where f(u,t) = x. + * The encoding algorithm is described in the paper, and effectively picks a + * uniformly random pair (u,t) among those which encode x. + * + * If the Y coordinate is relevant, it is given the same parity as t. + * + * Changes w.r.t. the the paper: + * - The u=0, t=0, and u^3+t^2+7=0 conditions result in decoding to the point + * at infinity in the paper. Here they are remapped to finite points. + * - The paper uses an additional encoding bit for the parity of y. Here the + * parity of t is used (negating t does not affect the decoded x coordinate, + * so this is possible). + */ + +/** A pointer to a function used for hashing the shared X coordinate along + * with the encoded public keys to a uniform shared secret. + * + * Returns: 1 if a shared secret was was successfully computed. + * 0 will cause secp256k1_ellswift_xdh to fail and return 0. + * Other return values are not allowed, and the behaviour of + * secp256k1_ellswift_xdh is undefined for other return values. + * Out: output: pointer to an array to be filled by the function + * In: x32: pointer to the 32-byte serialized X coordinate + * of the resulting shared point + * ours64: pointer to the 64-byte encoded public key we sent + * to the other party + * theirs64: pointer to the 64-byte encoded public key we received + * from the other party + * data: arbitrary data pointer that is passed through + */ +typedef int (*secp256k1_ellswift_xdh_hash_function)( + unsigned char *output, + const unsigned char *x32, + const unsigned char *ours64, + const unsigned char *theirs64, + void *data +); + +/** An implementation of an secp256k1_ellswift_xdh_hash_function which uses + * SHA256(key1 || key2 || x32), where (key1, key2) = sorted([ours64, theirs64]), and + * ignores data. The sorting is lexicographic. */ +SECP256K1_API extern const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_sha256; + +/** A default secp256k1_ellswift_xdh_hash_function, currently secp256k1_ellswift_xdh_hash_function_sha256. */ +SECP256K1_API extern const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_default; + +/* Construct a 64-byte ElligatorSwift encoding of a given pubkey. + * + * Returns: 1 when pubkey is valid. + * Args: ctx: pointer to a context object + * Out: ell64: pointer to a 64-byte array to be filled + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key + * rnd32: pointer to 32 bytes of entropy (must be unpredictable) + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellswift_encode( + const secp256k1_context* ctx, + unsigned char *ell64, + const secp256k1_pubkey *pubkey, + const unsigned char *rnd32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Decode a 64-bytes ElligatorSwift encoded public key. + * + * Returns: always 1 + * Args: ctx: pointer to a context object + * Out: pubkey: pointer to a secp256k1_pubkey that will be filled + * In: ell64: pointer to a 64-byte array to decode + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellswift_decode( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *ell64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute an ElligatorSwift public key for a secret key. + * + * Returns: 1: secret was valid, public key was stored. + * 0: secret was invalid, try again. + * Args: ctx: pointer to a context object, initialized for signing. + * Out: ell64: pointer to a 64-byte area to receive the ElligatorSwift public key + * In: seckey32: pointer to a 32-byte secret key. + * auxrand32: (optional) pointer to 32 bytes of additional randomness + * + * Constant time in seckey and auxrand32, but not in the resulting public key. + * + * This function can be used instead of calling secp256k1_ec_pubkey_create followed + * by secp256k1_ellswift_encode. It is safer, as it can use the secret key as + * entropy for the encoding. That means that if the secret key itself is + * unpredictable, no additional auxrand32 is needed to achieve indistinguishability + * of the encoding. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create( + const secp256k1_context* ctx, + unsigned char *ell64, + const unsigned char *seckey32, + const unsigned char *auxrand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Given a private key, and ElligatorSwift public keys sent in both directions, + * compute a shared secret using x-only Diffie-Hellman. + * + * Returns: 1: shared secret was succesfully computed + * 0: secret was invalid or hashfp returned 0 + * Args: ctx: pointer to a context object. + * Out: output: pointer to an array to be filled by hashfp. + * In: theirs64: a pointer to the 64-byte ElligatorSquare public key received from the other party. + * ours64: a pointer to the 64-byte ElligatorSquare public key sent to the other party. + * seckey32: a pointer to the 32-byte private key corresponding to ours64. + * hashfp: pointer to a hash function. If NULL, + * secp256k1_elswift_xdh_hash_function_default is used + * (in which case, 32 bytes will be written to output). + * data: arbitrary data pointer that is passed through to hashfp + * (ignored for secp256k1_ellswift_xdh_hash_function_default). + * + * Constant time in seckey32. + * + * This function is more efficient than decoding the public keys, and performing ECDH on them. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_xdh( + const secp256k1_context* ctx, + unsigned char *output, + const unsigned char* theirs64, + const unsigned char* ours64, + const unsigned char* seckey32, + secp256k1_ellswift_xdh_hash_function hashfp, + void *data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ELLSWIFT_H */ diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h deleted file mode 100644 index 6f7693cb8fd04..0000000000000 --- a/src/secp256k1/src/basic-config.h +++ /dev/null @@ -1,17 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_BASIC_CONFIG_H -#define SECP256K1_BASIC_CONFIG_H - -#ifdef USE_BASIC_CONFIG - -#define ECMULT_WINDOW_SIZE 15 -#define ECMULT_GEN_PREC_BITS 4 - -#endif /* USE_BASIC_CONFIG */ - -#endif /* SECP256K1_BASIC_CONFIG_H */ diff --git a/src/secp256k1/src/bench.c b/src/secp256k1/src/bench.c index d5937b763f08f..68cb163b1375f 100644 --- a/src/secp256k1/src/bench.c +++ b/src/secp256k1/src/bench.c @@ -121,6 +121,22 @@ static void bench_sign_run(void* arg, int iters) { } } +static void bench_keygen_run(void* arg, int iters) { + int i; + bench_sign_data *data = (bench_sign_data*)arg; + + for (i = 0; i < iters; i++) { + unsigned char pub33[33]; + size_t len = 33; + secp256k1_pubkey pubkey; + CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->key)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pub33, &len, &pubkey, SECP256K1_EC_COMPRESSED)); + memcpy(data->key, pub33 + 1, 32); + data->key[17] ^= i; + } +} + + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/bench_impl.h" #endif @@ -133,6 +149,10 @@ static void bench_sign_run(void* arg, int iters) { # include "modules/schnorrsig/bench_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/bench_impl.h" +#endif + int main(int argc, char** argv) { int i; secp256k1_pubkey pubkey; @@ -212,6 +232,7 @@ int main(int argc, char** argv) { data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); if (d || have_flag(argc, argv, "ecdsa") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "ecdsa_sign")) run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ec") || have_flag(argc, argv, "keygen") || have_flag(argc, argv, "ec_keygen")) run_benchmark("ec_keygen", bench_keygen_run, bench_sign_setup, NULL, &data, 10, iters); secp256k1_context_destroy(data.ctx); @@ -230,5 +251,10 @@ int main(int argc, char** argv) { run_schnorrsig_bench(iters, argc, argv); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + /* ElligatorSwift benchmarks */ + run_ellswift_bench(iters, argc, argv); +#endif + return 0; } diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h index aa275fe919d71..611ba11f04968 100644 --- a/src/secp256k1/src/bench.h +++ b/src/secp256k1/src/bench.h @@ -7,15 +7,31 @@ #ifndef SECP256K1_BENCH_H #define SECP256K1_BENCH_H +#include #include #include #include -#include "sys/time.h" + +#if (defined(_MSC_VER) && _MSC_VER >= 1900) +# include +#else +# include "sys/time.h" +#endif static int64_t gettime_i64(void) { +#if (defined(_MSC_VER) && _MSC_VER >= 1900) + /* C11 way to get wallclock time */ + struct timespec tv; + if (!timespec_get(&tv, TIME_UTC)) { + fputs("timespec_get failed!", stderr); + exit(1); + } + return (int64_t)tv.tv_nsec / 1000 + (int64_t)tv.tv_sec * 1000000LL; +#else struct timeval tv; gettimeofday(&tv, NULL); return (int64_t)tv.tv_usec + (int64_t)tv.tv_sec * 1000000LL; +#endif } #define FP_EXP (6) diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c index 7eb3af28d7317..27af24b1a0955 100644 --- a/src/secp256k1/src/bench_internal.c +++ b/src/secp256k1/src/bench_internal.c @@ -218,6 +218,17 @@ void bench_field_sqrt(void* arg, int iters) { CHECK(j <= iters); } +void bench_field_jacobi_var(void* arg, int iters) { + int i, j = 0; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + j += secp256k1_fe_jacobi_var(&data->fe[0]); + secp256k1_fe_add(&data->fe[0], &data->fe[1]); + } + CHECK(j <= iters); +} + void bench_group_double_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -379,6 +390,7 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "jacobi")) run_benchmark("field_jacobi_var", bench_field_jacobi_var, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, iters*10); diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h index b47d8f494a803..e28c60250671a 100644 --- a/src/secp256k1/src/ecmult.h +++ b/src/secp256k1/src/ecmult.h @@ -11,6 +11,17 @@ #include "scalar.h" #include "scratch.h" +#ifndef ECMULT_WINDOW_SIZE +# define ECMULT_WINDOW_SIZE 15 +# ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_MSG("ECMULT_WINDOW_SIZE undefined, assuming default value") +# endif +#endif + +#ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_DEF(ECMULT_WINDOW_SIZE) +#endif + /* Noone will ever need more than a window size of 24. The code might * be correct for larger values of ECMULT_WINDOW_SIZE but this is not * tested. diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h index f891f3f306709..aae902743b0d3 100644 --- a/src/secp256k1/src/ecmult_const.h +++ b/src/secp256k1/src/ecmult_const.h @@ -18,4 +18,23 @@ */ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits); +/** + * Same as secp256k1_ecmult_const, but takes in an x coordinate of the base point + * only, specified as fraction n/d. Only the x coordinate of the result is returned. + * + * If known_on_curve is 0, a verification is performed that n/d is a valid X + * coordinate, and 0 is returned if not. Otherwise, 1 is returned. + * + * d being NULL is interpreted as d=1. + * + * Constant time in the value of q, but not any other inputs. + */ +static int secp256k1_ecmult_const_xonly( + secp256k1_fe* r, + const secp256k1_fe *n, + const secp256k1_fe *d, + const secp256k1_scalar *q, + int bits, + int known_on_curve); + #endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h index 12dbcc6c5b692..1940ee7f08cde 100644 --- a/src/secp256k1/src/ecmult_const_impl.h +++ b/src/secp256k1/src/ecmult_const_impl.h @@ -228,4 +228,58 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons secp256k1_fe_mul(&r->z, &r->z, &Z); } +static int secp256k1_ecmult_const_xonly(secp256k1_fe* r, const secp256k1_fe *n, const secp256k1_fe *d, const secp256k1_scalar *q, int bits, int known_on_curve) { + + /* This algorithm is a generalization of Peter Dettman's technique for + * avoiding the square root in a random-basepoint x-only multiplication + * on a Weierstrass curve: + * https://mailarchive.ietf.org/arch/msg/cfrg/7DyYY6gg32wDgHAhgSb6XxMDlJA/ + */ + secp256k1_fe g, i; + secp256k1_ge p; + secp256k1_gej rj; + + /* Compute g = (n^3 + B*d^3). */ + secp256k1_fe_sqr(&g, n); + secp256k1_fe_mul(&g, &g, n); + if (d) { + secp256k1_fe b; + secp256k1_fe_sqr(&b, d); + secp256k1_fe_mul(&b, &b, d); + secp256k1_fe_mul(&b, &b, &secp256k1_fe_const_b); + secp256k1_fe_add(&g, &b); + if (!known_on_curve) { + secp256k1_fe c; + secp256k1_fe_mul(&c, &g, d); + if (secp256k1_fe_jacobi_var(&c) < 0) return 0; + } + } else { + secp256k1_fe_add(&g, &secp256k1_fe_const_b); + if (!known_on_curve) { + if (secp256k1_fe_jacobi_var(&g) < 0) return 0; + } + } + + /* Compute base point P = (n*g, g^2), the effective affine version of + * (n*g, g^2, sqrt(d*g)), which has corresponding affine X coordinate + * n/d. */ + secp256k1_fe_mul(&p.x, &g, n); + secp256k1_fe_sqr(&p.y, &g); + p.infinity = 0; + + /* Perform x-only EC multiplication of P with q. */ + secp256k1_ecmult_const(&rj, &p, q, bits); + + /* The resulting (X, Y, Z) point on the effective-affine isomorphic curve + * corresponds to (X, Y, Z*sqrt(d*g)) on the secp256k1 curve. The affine + * version of that has X coordinate (X / (Z^2*d*g)). */ + secp256k1_fe_sqr(&i, &rj.z); + secp256k1_fe_mul(&i, &i, &g); + if (d) secp256k1_fe_mul(&i, &i, d); + secp256k1_fe_inv(&i, &i); + secp256k1_fe_mul(r, &rj.x, &i); + + return 1; +} + #endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h index f48f266461e5f..a430e8d5d9223 100644 --- a/src/secp256k1/src/ecmult_gen.h +++ b/src/secp256k1/src/ecmult_gen.h @@ -10,9 +10,21 @@ #include "scalar.h" #include "group.h" +#ifndef ECMULT_GEN_PREC_BITS +# define ECMULT_GEN_PREC_BITS 4 +# ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_MSG("ECMULT_GEN_PREC_BITS undefined, assuming default value") +# endif +#endif + +#ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_DEF(ECMULT_GEN_PREC_BITS) +#endif + #if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8 # error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8." #endif + #define ECMULT_GEN_PREC_G(bits) (1 << bits) #define ECMULT_GEN_PREC_N(bits) (256 / bits) diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h index 2c8a503acc483..4f5ea9f3c0858 100644 --- a/src/secp256k1/src/ecmult_gen_impl.h +++ b/src/secp256k1/src/ecmult_gen_impl.h @@ -88,31 +88,31 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char nonce32[32]; secp256k1_rfc6979_hmac_sha256 rng; int overflow; - unsigned char keydata[64] = {0}; + unsigned char keydata[64]; if (seed32 == NULL) { /* When seed is NULL, reset the initial point and blinding value. */ secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); secp256k1_gej_neg(&ctx->initial, &ctx->initial); secp256k1_scalar_set_int(&ctx->blind, 1); + return; } /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ - secp256k1_scalar_get_b32(nonce32, &ctx->blind); + secp256k1_scalar_get_b32(keydata, &ctx->blind); /** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, * and guards against weak or adversarial seeds. This is a simpler and safer interface than * asking the caller for blinding values directly and expecting them to retry on failure. */ - memcpy(keydata, nonce32, 32); - if (seed32 != NULL) { - memcpy(keydata + 32, seed32, 32); - } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); + VERIFY_CHECK(seed32 != NULL); + memcpy(keydata + 32, seed32, 32); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64); memset(keydata, 0, sizeof(keydata)); /* Accept unobservably small non-uniformity. */ secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); overflow = !secp256k1_fe_set_b32(&s, nonce32); overflow |= secp256k1_fe_is_zero(&s); secp256k1_fe_cmov(&s, &secp256k1_fe_one, overflow); - /* Randomize the projection to defend against multiplier sidechannels. */ + /* Randomize the projection to defend against multiplier sidechannels. + Do this before our own call to secp256k1_ecmult_gen below. */ secp256k1_gej_rescale(&ctx->initial, &s); secp256k1_fe_clear(&s); secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); @@ -121,6 +121,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b)); secp256k1_rfc6979_hmac_sha256_finalize(&rng); memset(nonce32, 0, 32); + /* The random projection in ctx->initial ensures that gb will have a random projection. */ secp256k1_ecmult_gen(ctx, &gb, &b); secp256k1_scalar_negate(&b, &b); ctx->blind = b; diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h index 2584a494eeb09..c9bafeb481fe5 100644 --- a/src/secp256k1/src/field.h +++ b/src/secp256k1/src/field.h @@ -139,4 +139,7 @@ static void secp256k1_fe_half(secp256k1_fe *r); * magnitude set to 'm' and is normalized if (and only if) 'm' is zero. */ static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m); +/** Compute the Jacobi symbol of a / p. 0 if a=0; 1 if a square; -1 if a non-square. */ +static int secp256k1_fe_jacobi_var(const secp256k1_fe *a); + #endif /* SECP256K1_FIELD_H */ diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h index 21742bf6eb6c4..61a86190c5371 100644 --- a/src/secp256k1/src/field_10x26_impl.h +++ b/src/secp256k1/src/field_10x26_impl.h @@ -1364,4 +1364,32 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); } +static int secp256k1_fe_jacobi_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv32_signed30 s; + int ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + secp256k1_fe_to_signed30(&s, &tmp); + ret = secp256k1_jacobi32_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (ret == -2) { + /* secp256k1_jacobi32_maybe_var failed to compute the Jacobi symbol. Fall back + * to computing a square root. This should be extremely rare with random + * input. */ + secp256k1_fe dummy; + ret = 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1; +#ifdef VERIFY + } else { + secp256k1_fe dummy; + if (secp256k1_fe_is_zero(&tmp)) { + VERIFY_CHECK(ret == 0); + } else { + VERIFY_CHECK(ret == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); + } +#endif + } + return ret; +} + #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h index 6bd202f587130..26e89123a084d 100644 --- a/src/secp256k1/src/field_5x52_impl.h +++ b/src/secp256k1/src/field_5x52_impl.h @@ -667,4 +667,32 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { #endif } +static int secp256k1_fe_jacobi_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv64_signed62 s; + int ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + secp256k1_fe_to_signed62(&s, &tmp); + ret = secp256k1_jacobi64_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (ret == -2) { + /* secp256k1_jacobi64_maybe_var failed to compute the Jacobi symbol. Fall back + * to computing a square root. This should be extremely rare with random + * input. */ + secp256k1_fe dummy; + ret = 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1; +#ifdef VERIFY + } else { + secp256k1_fe dummy; + if (secp256k1_fe_is_zero(&tmp)) { + VERIFY_CHECK(ret == 0); + } else { + VERIFY_CHECK(ret == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); + } +#endif + } + return ret; +} + #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h index bb7dae1cf781e..585457d93b1c3 100644 --- a/src/secp256k1/src/group.h +++ b/src/secp256k1/src/group.h @@ -23,7 +23,7 @@ typedef struct { #define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} /** A group element of the secp256k1 curve, in jacobian coordinates. - * Note: For exhastive test mode, sepc256k1 is replaced by a small subgroup of a different curve. + * Note: For exhastive test mode, secp256k1 is replaced by a small subgroup of a different curve. */ typedef struct { secp256k1_fe x; /* actual X: x/z^2 */ diff --git a/src/secp256k1/src/modinv32.h b/src/secp256k1/src/modinv32.h index 0efdda9ab5e2e..263bda20b8a3d 100644 --- a/src/secp256k1/src/modinv32.h +++ b/src/secp256k1/src/modinv32.h @@ -39,4 +39,8 @@ static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256 /* Same as secp256k1_modinv32_var, but constant time in x (not in the modulus). */ static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); +/* Compute the Jacobi symbol for (x | modinfo->modulus). Either x must be 0, or x must be coprime with + * modulus. All limbs of x must be non-negative. Returns -2 if the result cannot be computed. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); + #endif /* SECP256K1_MODINV32_H */ diff --git a/src/secp256k1/src/modinv32_impl.h b/src/secp256k1/src/modinv32_impl.h index 661c5fc04c988..93bc576675eb6 100644 --- a/src/secp256k1/src/modinv32_impl.h +++ b/src/secp256k1/src/modinv32_impl.h @@ -232,6 +232,21 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ return zeta; } +/* inv256[i] = -(2*i+1)^-1 (mod 256) */ +static const uint8_t secp256k1_modinv32_inv256[128] = { + 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, + 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, + 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, + 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, + 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, + 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, + 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, + 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, + 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, + 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, + 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 +}; + /* Compute the transition matrix and eta for 30 divsteps (variable time). * * Input: eta: initial eta @@ -243,21 +258,6 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ * Implements the divsteps_n_matrix_var function from the explanation. */ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) { - /* inv256[i] = -(2*i+1)^-1 (mod 256) */ - static const uint8_t inv256[128] = { - 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, - 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, - 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, - 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, - 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, - 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, - 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, - 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, - 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, - 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, - 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 - }; - /* Transformation matrix; see comments in secp256k1_modinv32_divsteps_30. */ uint32_t u = 1, v = 0, q = 0, r = 1; uint32_t f = f0, g = g0, m; @@ -297,7 +297,7 @@ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint VERIFY_CHECK(limit > 0 && limit <= 30); m = (UINT32_MAX >> (32 - limit)) & 255U; /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ - w = (g * inv256[(f >> 1) & 127]) & m; + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; /* Do so. */ g += f * w; q += u * w; @@ -317,6 +317,83 @@ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint return eta; } +/* Compute the transition matrix and eta for 30 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^32 rather than 2^30, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final eta + */ +static int32_t secp256k1_modinv32_posdivsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t, int *jacp) { + /* Transformation matrix. */ + uint32_t u = 1, v = 0, q = 0, r = 1; + uint32_t f = f0, g = g0, m; + uint16_t w; + int i = 30, limit, zeros; + int jac = *jacp; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 30 posdivsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint32_t tmp; + eta = -eta; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + tmp = f; f = g; g = tmp; + tmp = u; u = q; q = tmp; + tmp = v; v = r; r = tmp; + } + /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more + * than i can be cancelled out (as we'd be done before that point), and no more than eta+1 + * can be done as its sign will flip once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */ + VERIFY_CHECK(limit > 0 && limit <= 30); + m = (UINT32_MAX >> (32 - limit)) & 255U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; + /* Do so. */ + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int32_t)u; + t->v = (int32_t)v; + t->q = (int32_t)q; + t->r = (int32_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2 or -2, + * the aggregate of 30 of them will have determinant 2^30 or -2^30. */ + VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30 || + (int64_t)t->u * t->r - (int64_t)t->v * t->q == -(((int64_t)1) << 30)); + *jacp = jac; + return eta; +} + /* Compute (t/2^30) * [d, e] mod modulus, where t is a transition matrix for 30 divsteps. * * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range @@ -584,4 +661,69 @@ static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256 *x = d; } +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1, or x must be 0. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv32_signed30 f = modinfo->modulus; + secp256k1_modinv32_signed30 g = *x; + int j, len = 9; + int32_t eta = -1; /* eta = -delta; delta is initially 1 */ + int32_t cond, fn, gn; + int jac = 0; + int count; + + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0 && g.v[5] >= 0 && g.v[6] >= 0 && g.v[7] >= 0 && g.v[8] >= 0); + + /* The loop below does not converge for input g=0. Deal with this case specifically. */ + if (!(g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4] | g.v[5] | g.v[6] | g.v[7] | g.v[8])) return 0; + + /* Do up to 50 iterations of 30 posdivsteps (up to 1500 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (750, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY + for (count = 0; count < 25; ++count) { +#else + for (count = 0; count < 50; ++count) { +#endif + /* Compute transition matrix and new eta after 30 posdivsteps. */ + secp256k1_modinv32_trans2x2 t; + eta = secp256k1_modinv32_posdivsteps_30_var(eta, f.v[0] | ((uint32_t)f.v[1] << 30), g.v[0] | ((uint32_t)g.v[1] << 30), &t, &jac); + /* Update f,g using that transition matrix. */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t); + /* If the bottom limb of f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int32_t)len - 2) >> 31; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + } + + /* The loop failed to converge to f=g after 1500 iterations. Return -2, indicating unknown result. */ + return -2; +} + #endif /* SECP256K1_MODINV32_IMPL_H */ diff --git a/src/secp256k1/src/modinv64.h b/src/secp256k1/src/modinv64.h index da506dfa9f722..e432fcbe8d57a 100644 --- a/src/secp256k1/src/modinv64.h +++ b/src/secp256k1/src/modinv64.h @@ -43,4 +43,8 @@ static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256 /* Same as secp256k1_modinv64_var, but constant time in x (not in the modulus). */ static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); +/* Compute the Jacobi symbol for (x | modinfo->modulus). Either x must be 0, or x must be coprime with + * modulus. All limbs of x must be non-negative. Returns -2 if the result cannot be computed. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); + #endif /* SECP256K1_MODINV64_H */ diff --git a/src/secp256k1/src/modinv64_impl.h b/src/secp256k1/src/modinv64_impl.h index 0743a9c8210d2..2d0d33d77767a 100644 --- a/src/secp256k1/src/modinv64_impl.h +++ b/src/secp256k1/src/modinv64_impl.h @@ -256,7 +256,7 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint tmp = v; v = r; r = -tmp; /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled * out (as we'd be done before that point), and no more than eta+1 can be done as its - * will flip again once that happens. */ + * sign will flip again once that happens. */ limit = ((int)eta + 1) > i ? i : ((int)eta + 1); VERIFY_CHECK(limit > 0 && limit <= 62); /* m is a mask for the bottom min(limit, 6) bits. */ @@ -294,6 +294,94 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint return eta; } +/* Compute the transition matrix and eta for 62 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^64 rather than 2^62, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final eta + */ +static int64_t secp256k1_modinv64_posdivsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t, int *jacp) { + /* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */ + uint64_t u = 1, v = 0, q = 0, r = 1; + uint64_t f = f0, g = g0, m; + uint32_t w; + int i = 62, limit, zeros; + int jac = *jacp; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 62 posdivsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint64_t tmp; + eta = -eta; + tmp = f; f = g; g = tmp; + tmp = u; u = q; q = tmp; + tmp = v; v = r; r = tmp; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled + * out (as we'd be done before that point), and no more than eta+1 can be done as its + * sign will flip again once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 6) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 63U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 6) + * bits. */ + w = (f * g * (f * f - 2)) & m; + } else { + /* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as + * eta tends to be smaller here. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 4) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 15U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 4) + * bits. */ + w = f + (((f + 1) & 4) << 1); + w = (-w * g) & m; + } + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int64_t)u; + t->v = (int64_t)v; + t->q = (int64_t)q; + t->r = (int64_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2 or -2, + * the aggregate of 62 of them will have determinant 2^62 or -2^62. */ + VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 62 || + (int128_t)t->u * t->r - (int128_t)t->v * t->q == -(((int128_t)1) << 62)); + *jacp = jac; + return eta; +} + /* Compute (t/2^62) * [d, e] mod modulus, where t is a transition matrix scaled by 2^62. * * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range @@ -590,4 +678,69 @@ static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256 *x = d; } +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1, or x must be 0. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv64_signed62 f = modinfo->modulus; + secp256k1_modinv64_signed62 g = *x; + int j, len = 5; + int64_t eta = -1; /* eta = -delta; delta is initially 1 */ + int64_t cond, fn, gn; + int jac = 0; + int count; + + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0); + + /* The loop below does not converge for input g=0. Deal with this case specifically. */ + if (!(g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4])) return 0; + + /* Do up to 25 iterations of 62 posdivsteps (up to 1550 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (744, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY + for (count = 0; count < 12; ++count) { +#else + for (count = 0; count < 25; ++count) { +#endif + /* Compute transition matrix and new eta after 62 posdivsteps. */ + secp256k1_modinv64_trans2x2 t; + eta = secp256k1_modinv64_posdivsteps_62_var(eta, f.v[0] | ((uint64_t)f.v[1] << 62), g.v[0] | ((uint64_t)g.v[1] << 62), &t, &jac); + /* Update f,g using that transition matrix. */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t); + /* If the bottom limb of f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int64_t)len - 2) >> 63; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + } + + /* The loop failed to converge to f=g after 1550 iterations. Return -2, indicating unknown result. */ + return -2; +} + #endif /* SECP256K1_MODINV64_IMPL_H */ diff --git a/src/secp256k1/src/modules/ecdh/bench_impl.h b/src/secp256k1/src/modules/ecdh/bench_impl.h index 94d833462fe0f..8df15bcf43c92 100644 --- a/src/secp256k1/src/modules/ecdh/bench_impl.h +++ b/src/secp256k1/src/modules/ecdh/bench_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_ECDH_BENCH_H #define SECP256K1_MODULE_ECDH_BENCH_H -#include "../include/secp256k1_ecdh.h" +#include "../../../include/secp256k1_ecdh.h" typedef struct { secp256k1_context *ctx; diff --git a/src/secp256k1/src/modules/ellswift/Makefile.am.include b/src/secp256k1/src/modules/ellswift/Makefile.am.include new file mode 100644 index 0000000000000..e7efea29819b2 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/Makefile.am.include @@ -0,0 +1,4 @@ +include_HEADERS += include/secp256k1_ellswift.h +noinst_HEADERS += src/modules/ellswift/bench_impl.h +noinst_HEADERS += src/modules/ellswift/main_impl.h +noinst_HEADERS += src/modules/ellswift/tests_impl.h diff --git a/src/secp256k1/src/modules/ellswift/bench_impl.h b/src/secp256k1/src/modules/ellswift/bench_impl.h new file mode 100644 index 0000000000000..1befd0a4a2891 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/bench_impl.h @@ -0,0 +1,94 @@ +/*********************************************************************** + * Copyright (c) 2022 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_BENCH_H +#define SECP256K1_MODULE_ELLSWIFT_BENCH_H + +#include "../include/secp256k1_ellswift.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point; + unsigned char rnd64[64]; +} bench_ellswift_data; + +static void bench_ellswift_setup(void* arg) { + bench_ellswift_data *data = (bench_ellswift_data*)arg; + static const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f + }; + memcpy(data->rnd64, point, 32); + memcpy(data->rnd64 + 32, point + 1, 32); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1); +} + +static void bench_ellswift_encode(void* arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + data->rnd64[19] ^= 247; + data->rnd64[47] ^= 113; + CHECK(secp256k1_ellswift_encode(data->ctx, data->rnd64, &data->point, data->rnd64 + 16) == 1); + } +} + +static void bench_ellswift_create(void* arg, int iters) { + int i, j; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + unsigned char out64[64]; + CHECK(secp256k1_ellswift_create(data->ctx, out64, data->rnd64, data->rnd64 + 32) == 1); + for (j = 0; j < 64; j++) data->rnd64[j] ^= out64[j]; + } +} + +static void bench_ellswift_decode(void* arg, int iters) { + int i; + secp256k1_pubkey out; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + data->rnd64[13] ^= 247; + data->rnd64[49] ^= 113; + CHECK(secp256k1_ellswift_decode(data->ctx, &out, data->rnd64) == 1); + memcpy(data->rnd64 + 16, &out.data, 32); + } +} + +static void bench_ellswift_xdh(void* arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + data->rnd64[13] ^= 247; + data->rnd64[49] ^= 113; + CHECK(secp256k1_ellswift_xdh(data->ctx, data->rnd64 + 16, data->rnd64, data->rnd64, data->rnd64 + 13, NULL, NULL) == 1); + } +} + +void run_ellswift_bench(int iters, int argc, char** argv) { + bench_ellswift_data data; + int d = argc == 1; + + /* create a context with signing capabilities */ + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + memset(data.rnd64, 11, sizeof(data.rnd64)); + + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "encode") || have_flag(argc, argv, "ellswift_encode")) run_benchmark("ellswift_encode", bench_ellswift_encode, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_decode")) run_benchmark("ellswift_decode", bench_ellswift_decode, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "create") || have_flag(argc, argv, "ellswift_create")) run_benchmark("ellswift_create", bench_ellswift_create, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "xdh") || have_flag(argc, argv, "ellswift_xdh")) run_benchmark("ellswift_xdh", bench_ellswift_xdh, bench_ellswift_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); +} + +#endif /* SECP256K1_MODULE_ellswift_BENCH_H */ diff --git a/src/secp256k1/src/modules/ellswift/main_impl.h b/src/secp256k1/src/modules/ellswift/main_impl.h new file mode 100644 index 0000000000000..cbfe0e2cf8752 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/main_impl.h @@ -0,0 +1,394 @@ +/*********************************************************************** + * Copyright (c) 2022 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_MAIN_H +#define SECP256K1_MODULE_ELLSWIFT_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_ellswift.h" +#include "../../hash.h" + +/** c1 = the square root of -3 ((-3)**((p+1)/4)). */ +static const secp256k1_fe secp256k1_ellswift_c1 = SECP256K1_FE_CONST(0x0a2d2ba9, 0x3507f1df, 0x233770c2, 0xa797962c, 0xc61f6d15, 0xda14ecd4, 0x7d8d27ae, 0x1cd5f852); +/** c2 = -1/2 * (c1 - 1). */ +static const secp256k1_fe secp256k1_ellswift_c2 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ef); + +/** Decode ElligatorSwift encoding (u, t) to a fraction xn/xd representing a curve X coordinate. */ +static void secp256k1_ellswift_fe2_to_gexfrac_var(secp256k1_fe* xn, secp256k1_fe* xd, const secp256k1_fe* u, const secp256k1_fe* t) { + secp256k1_fe v1 = *u, v2 = *t; + secp256k1_fe v3, v4, v5, v6, v7, v8; + secp256k1_fe_normalize_var(&v1); + secp256k1_fe_normalize_var(&v2); + if (secp256k1_fe_is_zero(&v1)) v1 = secp256k1_fe_one; + if (secp256k1_fe_is_zero(&v2)) v2 = secp256k1_fe_one; + secp256k1_fe_sqr(&v3, &v1); + secp256k1_fe_mul(&v3, &v3, &v1); + secp256k1_fe_add(&v3, &secp256k1_fe_const_b); + secp256k1_fe_sqr(&v4, &v2); + v5 = v3; + secp256k1_fe_add(&v5, &v4); + if (secp256k1_fe_normalizes_to_zero_var(&v5)) { + secp256k1_fe_add(&v2, &v2); + secp256k1_fe_sqr(&v4, &v2); + v5 = v3; + secp256k1_fe_add(&v5, &v4); + } + secp256k1_fe_mul(&v6, &v1, &secp256k1_ellswift_c1); + secp256k1_fe_negate(&v4, &v4, 1); + secp256k1_fe_add(&v4, &v3); + secp256k1_fe_mul(&v4, &v4, &v6); + secp256k1_fe_mul(&v2, &v2, &v6); + secp256k1_fe_sqr(&v2, &v2); + secp256k1_fe_sqr(&v8, &v5); + secp256k1_fe_mul(&v3, &v1, &v2); + secp256k1_fe_add(&v3, &v8); + secp256k1_fe_sqr(&v6, &v2); + secp256k1_fe_sqr(&v6, &v6); + secp256k1_fe_mul_int(&v6, 7); + secp256k1_fe_sqr(&v7, &v3); + secp256k1_fe_mul(&v7, &v7, &v3); + secp256k1_fe_mul(&v7, &v7, &v2); + secp256k1_fe_add(&v7, &v6); + if (secp256k1_fe_jacobi_var(&v7) >= 0) { + *xn = v3; + *xd = v2; + return; + } + secp256k1_fe_mul(&v1, &v1, &v5); + secp256k1_fe_add(&v1, &v4); + secp256k1_fe_half(&v1); + secp256k1_fe_negate(&v1, &v1, 3); + secp256k1_fe_sqr(&v6, &v8); + secp256k1_fe_mul_int(&v6, 7); + secp256k1_fe_sqr(&v7, &v1); + secp256k1_fe_mul(&v7, &v7, &v1); + secp256k1_fe_mul(&v7, &v7, &v5); + secp256k1_fe_add(&v7, &v6); + *xd = v5; + secp256k1_fe_inv_var(&v5, &v5); + if (secp256k1_fe_jacobi_var(&v7) >= 0) { + *xn = v1; + return; + } + secp256k1_fe_add(&v1, &v4); + *xn = v1; +} + +/** Decode ElligatorSwift encoding (u, t) to X coordinate. */ +static void secp256k1_ellswift_fe2_to_gex_var(secp256k1_fe* x, const secp256k1_fe* u, const secp256k1_fe* t) { + secp256k1_fe xn, xd; + secp256k1_ellswift_fe2_to_gexfrac_var(&xn, &xd, u, t); + secp256k1_fe_inv_var(&xd, &xd); + secp256k1_fe_mul(x, &xn, &xd); +} + +/** Decode ElligatorSwift encoding (u, t) to point P. */ +static void secp256k1_ellswift_fe2_to_ge_var(secp256k1_ge* p, const secp256k1_fe* u, const secp256k1_fe* t) { + secp256k1_fe x; + secp256k1_ellswift_fe2_to_gex_var(&x, u, t); + secp256k1_ge_set_xo_var(p, &x, secp256k1_fe_is_odd(t)); +} + +/* Try to complete an ElligatorSwift encoding (u, t) for X coordinate x, given u and x. + * + * There may be up to 8 distinct t values such that (u, t) decodes back to x, but also + * fewer, or none at all. Each such partial inverse can be accessed individually using a + * distinct input argument i (in range 0-7), and some or all of these may return failure. + * The following guarantees exist: + * - Given (x, u), no two distinct i values give the same successful result t. + * - Every successful result maps back to x through secp256k1_ellswift_fe2_to_gex_var. + * - Given (x, u), all t values that map back to x can be reached by combining the + * successful results from this function over all i values, with the exception of: + * - this function cannot be called with u=0 + * - no result with t=0 will be returned + * - no result for which u^3 + t^2 + 7 = 0 will be returned. + */ +static int secp256k1_ellswift_fegex_to_fe_var(secp256k1_fe* t, const secp256k1_fe* x, const secp256k1_fe* u, int i) { + secp256k1_fe xm = *x, um = *u; + secp256k1_fe g, s, w2, w; + secp256k1_fe_normalize_weak(&xm); + secp256k1_fe_normalize_weak(&um); + secp256k1_fe_sqr(&g, u); + secp256k1_fe_mul(&g, &g, u); + secp256k1_fe_add(&g, &secp256k1_fe_const_b); + if ((i & 2) == 0) { + secp256k1_fe o; + s = xm; + secp256k1_fe_add(&s, &um); + secp256k1_fe_sqr(&o, &s); + secp256k1_fe_mul(&o, &o, &s); + secp256k1_fe_negate(&o, &o, 1); + secp256k1_fe_add(&o, &secp256k1_fe_const_b); + if (secp256k1_fe_jacobi_var(&o) >= 0) return 0; + if (i & 1) { + secp256k1_fe_add(&xm, &um); + secp256k1_fe_negate(&xm, &xm, 2); + } + o = um; + secp256k1_fe_add(&o, &xm); + secp256k1_fe_sqr(&o, &o); + secp256k1_fe_negate(&o, &o, 1); + secp256k1_fe_mul(&w2, &um, &xm); + secp256k1_fe_add(&w2, &o); + secp256k1_fe_inv_var(&w2, &w2); + secp256k1_fe_mul(&w2, &w2, &g); + } else { + secp256k1_fe r2, r; + secp256k1_fe_negate(&w2, &um, 1); + secp256k1_fe_add(&w2, &xm); + if (secp256k1_fe_normalizes_to_zero_var(&w2)) return 0; + secp256k1_fe_normalize_weak(&g); + secp256k1_fe_mul_int(&g, 4); + secp256k1_fe_sqr(&r2, &um); + secp256k1_fe_mul_int(&r2, 3); + secp256k1_fe_mul(&r2, &r2, &w2); + secp256k1_fe_add(&r2, &g); + secp256k1_fe_mul(&r2, &r2, &w2); + secp256k1_fe_negate(&r2, &r2, 1); + if (!secp256k1_fe_sqrt(&r, &r2)) return 0; + if (i & 1) { + if (secp256k1_fe_normalizes_to_zero_var(&r)) return 0; + } else { + secp256k1_fe_negate(&r, &r, 1); + } + secp256k1_fe_inv_var(&xm, &w2); + secp256k1_fe_mul(&xm, &xm, &r); + secp256k1_fe_add(&xm, &um); + secp256k1_fe_half(&xm); + secp256k1_fe_negate(&xm, &xm, 2); + } + if (!secp256k1_fe_sqrt(&w, &w2)) return 0; + if ((i & 4) == 0) secp256k1_fe_negate(&w, &w, 1); + secp256k1_fe_mul(&um, &um, &secp256k1_ellswift_c2); + secp256k1_fe_add(&um, &xm); + secp256k1_fe_mul(t, &w, &um); + return 1; +} + +/** Find an ElligatorSwift encoding (u, t) for X coordinate x. + * + * hasher is a SHA256 object which a incrementing 4-byte counter is added to to + * generate randomness for the rejection sampling in this function. Its size plus + * 4 (for the counter) plus 9 (for the SHA256 padding) must be a multiple of 64 + * for efficiency reasons. + */ +static void secp256k1_ellswift_gex_to_fe2_var(secp256k1_fe* u, secp256k1_fe* t, const secp256k1_fe* x, const secp256k1_sha256* hasher) { + /* Pool of 3-bit branch values. */ + unsigned char branch_hash[32]; + /* Number of 3-bit values in branch_hash left. */ + int branches_left = 0; + /* Field elements u and branch values are extracted from + * SHA256(hasher || cnt) for consecutive values of cnt. cnt==0 + * is first used to populate a pool of 64 4-bit branch values. The 64 cnt + * values that follow are used to generate field elements u. cnt==65 (and + * multiples thereof) are used to repopulate the pool and start over, if + * that were ever necessary. */ + uint32_t cnt = 0; + VERIFY_CHECK((hasher->bytes + 4 + 9) % 64 == 0); + while (1) { + int branch; + /* If the pool of branch values is empty, populate it. */ + if (branches_left == 0) { + secp256k1_sha256 hash = *hasher; + unsigned char buf4[4]; + buf4[0] = cnt; + buf4[1] = cnt >> 8; + buf4[2] = cnt >> 16; + buf4[3] = cnt >> 24; + ++cnt; + secp256k1_sha256_write(&hash, buf4, 4); + secp256k1_sha256_finalize(&hash, branch_hash); + branches_left = 64; + } + /* Take a 3-bit branch value from the branch pool (top bit is discarded). */ + --branches_left; + branch = (branch_hash[branches_left >> 1] >> ((branches_left & 1) << 2)) & 7; + /* Compute a new u value by hashing. */ + { + secp256k1_sha256 hash = *hasher; + unsigned char buf4[4]; + unsigned char u32[32]; + buf4[0] = cnt; + buf4[1] = cnt >> 8; + buf4[2] = cnt >> 16; + buf4[3] = cnt >> 24; + ++cnt; + secp256k1_sha256_write(&hash, buf4, 4); + secp256k1_sha256_finalize(&hash, u32); + if (!secp256k1_fe_set_b32(u, u32)) continue; + if (secp256k1_fe_is_zero(u)) continue; + } + /* Find a remainder t, and return it if found. */ + if (secp256k1_ellswift_fegex_to_fe_var(t, x, u, branch)) { + secp256k1_fe_normalize_var(t); + break; + } + } +} + +/** Find an ElligatorSwift encoding (u, t) for point P. */ +static void secp256k1_ellswift_ge_to_fe2_var(secp256k1_fe* u, secp256k1_fe* t, const secp256k1_ge* p, const secp256k1_sha256* hasher) { + secp256k1_ellswift_gex_to_fe2_var(u, t, &p->x, hasher); + if (secp256k1_fe_is_odd(t) != secp256k1_fe_is_odd(&p->y)) { + secp256k1_fe_negate(t, t, 1); + secp256k1_fe_normalize_var(t); + } +} + +int secp256k1_ellswift_encode(const secp256k1_context* ctx, unsigned char *ell64, const secp256k1_pubkey *pubkey, const unsigned char *rnd32) { + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(rnd32 != NULL); + + if (secp256k1_pubkey_load(ctx, &p, pubkey)) { + static const unsigned char PREFIX[128 - 9 - 4 - 32 - 33] = "secp256k1_ellswift_encode"; + secp256k1_fe u, t; + unsigned char p33[33]; + secp256k1_sha256 hash; + + /* Set up hasher state */ + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, PREFIX, sizeof(PREFIX)); + secp256k1_sha256_write(&hash, rnd32, 32); + secp256k1_fe_get_b32(p33, &p.x); + p33[32] = secp256k1_fe_is_odd(&p.y); + secp256k1_sha256_write(&hash, p33, sizeof(p33)); + VERIFY_CHECK(hash.bytes == 128 - 9 - 4); + + /* Compute ElligatorSwift encoding and construct output. */ + secp256k1_ellswift_ge_to_fe2_var(&u, &t, &p, &hash); + secp256k1_fe_get_b32(ell64, &u); + secp256k1_fe_get_b32(ell64 + 32, &t); + return 1; + } + /* Only returned in case the provided pubkey is invalid. */ + return 0; +} + +int secp256k1_ellswift_create(const secp256k1_context* ctx, unsigned char *ell64, const unsigned char *seckey32, const unsigned char *rnd32) { + secp256k1_ge p; + secp256k1_fe u, t; + secp256k1_sha256 hash; + secp256k1_scalar seckey_scalar; + static const unsigned char PREFIX[32] = "secp256k1_ellswift_create"; + static const unsigned char ZERO[32] = {0}; + int ret = 0; + + /* Sanity check inputs. */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + memset(ell64, 0, 64); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey32 != NULL); + + /* Compute (affine) public key */ + ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey32); + secp256k1_fe_normalize_var(&p.x); + secp256k1_fe_normalize_var(&p.y); + + /* Set up hasher state */ + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, PREFIX, sizeof(PREFIX)); + secp256k1_sha256_write(&hash, seckey32, 32); + secp256k1_sha256_write(&hash, rnd32 ? rnd32 : ZERO, 32); + secp256k1_sha256_write(&hash, ZERO, 32 - 9 - 4); + + /* Compute ElligatorSwift encoding and construct output. */ + secp256k1_ellswift_ge_to_fe2_var(&u, &t, &p, &hash); + secp256k1_fe_get_b32(ell64, &u); + secp256k1_fe_get_b32(ell64 + 32, &t); + + secp256k1_memczero(ell64, 64, !ret); + secp256k1_scalar_clear(&seckey_scalar); + + return ret; +} + +int secp256k1_ellswift_decode(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *ell64) { + secp256k1_fe u, t; + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(ell64 != NULL); + + secp256k1_fe_set_b32(&u, ell64); + secp256k1_fe_normalize_var(&u); + secp256k1_fe_set_b32(&t, ell64 + 32); + secp256k1_fe_normalize_var(&t); + secp256k1_ellswift_fe2_to_ge_var(&p, &u, &t); + secp256k1_pubkey_save(pubkey, &p); + return 1; +} + +static int ellswift_xdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *ours64, const unsigned char *theirs64, void *data) { + secp256k1_sha256 sha; + + (void)data; + + secp256k1_sha256_initialize(&sha); + if (secp256k1_memcmp_var(ours64, theirs64, 64) <= 0) { + secp256k1_sha256_write(&sha, ours64, 64); + secp256k1_sha256_write(&sha, theirs64, 64); + } else { + secp256k1_sha256_write(&sha, theirs64, 64); + secp256k1_sha256_write(&sha, ours64, 64); + } + secp256k1_sha256_write(&sha, x32, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + +const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_sha256 = ellswift_xdh_hash_function_sha256; +const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_default = ellswift_xdh_hash_function_sha256; + +int secp256k1_ellswift_xdh(const secp256k1_context* ctx, unsigned char *output, const unsigned char* theirs64, const unsigned char* ours64, const unsigned char* seckey32, secp256k1_ellswift_xdh_hash_function hashfp, void *data) { + int ret = 0; + int overflow; + secp256k1_scalar s; + secp256k1_fe xn, xd, px, u, t; + unsigned char sx[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(theirs64 != NULL); + ARG_CHECK(ours64 != NULL); + ARG_CHECK(seckey32 != NULL); + + if (hashfp == NULL) { + hashfp = secp256k1_ellswift_xdh_hash_function_default; + } + + /* Load remote public key (as fraction). */ + secp256k1_fe_set_b32(&u, theirs64); + secp256k1_fe_normalize_var(&u); + secp256k1_fe_set_b32(&t, theirs64 + 32); + secp256k1_fe_normalize_var(&t); + secp256k1_ellswift_fe2_to_gexfrac_var(&xn, &xd, &u, &t); + + /* Load private key (using one if invalid). */ + secp256k1_scalar_set_b32(&s, seckey32, &overflow); + overflow = secp256k1_scalar_is_zero(&s); + secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow); + + /* Compute shared X coordinate. */ + secp256k1_ecmult_const_xonly(&px, &xn, &xd, &s, 256, 1); + secp256k1_fe_normalize(&px); + secp256k1_fe_get_b32(sx, &px); + + /* Invoke hasher */ + ret = hashfp(output, sx, ours64, theirs64, data); + + memset(sx, 0, 32); + secp256k1_fe_clear(&px); + secp256k1_scalar_clear(&s); + + return !!ret & !overflow; +} + +#endif diff --git a/src/secp256k1/src/modules/ellswift/tests_impl.h b/src/secp256k1/src/modules/ellswift/tests_impl.h new file mode 100644 index 0000000000000..699a9496d0a76 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/tests_impl.h @@ -0,0 +1,193 @@ +/*********************************************************************** + * Copyright (c) 2022 Pieter Wuile * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_TESTS_H +#define SECP256K1_MODULE_ELLSWIFT_TESTS_H + +#include "../../../include/secp256k1_ellswift.h" + +struct ellswift_test_vec { + int enc_bitmap; + secp256k1_fe u; + secp256k1_fe x; + secp256k1_fe encs[8]; +}; + +/* Set of (point, encodings) test vectors, selected to maximize branch coverage. + * Created using an independent implementation, and tested against paper author's code. */ +static const struct ellswift_test_vec ellswift_tests[] = { + {0x33, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), {SECP256K1_FE_CONST(0x2c8864a8, 0xc34e87d7, 0x53ee7300, 0x8bbed54a, 0x47b37907, 0x56d0b747, 0x10341b37, 0xf598a5fe), SECP256K1_FE_CONST(0x15908d62, 0x2377bedc, 0x0fecf55f, 0xcc6425c9, 0xde992fcb, 0x01af2628, 0xac40f220, 0x88de01f0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xd3779b57, 0x3cb17828, 0xac118cff, 0x74412ab5, 0xb84c86f8, 0xa92f48b8, 0xefcbe4c7, 0x0a675631), SECP256K1_FE_CONST(0xea6f729d, 0xdc884123, 0xf0130aa0, 0x339bda36, 0x2166d034, 0xfe50d9d7, 0x53bf0dde, 0x7721fa3f), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x44, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x4218f20a, 0xe6c646b3, 0x63db6860, 0x5822fb14, 0x264ca8d2, 0x587fdd6f, 0xbc750d58, 0x7e76a7ee), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xbde70df5, 0x1939b94c, 0x9c24979f, 0xa7dd04eb, 0xd9b3572d, 0xa7802290, 0x438af2a6, 0x81895441), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0x31d8011e, 0x503be7cd, 0x04ed2465, 0x4f09771e, 0x721346f2, 0x2c5b5fee, 0x14f5c5c1, 0x56167823), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0xb8438fb4, 0x2a2cead9, 0xace238da, 0x755840bf, 0x6ca51d4c, 0x6eb4074c, 0x43b215de, 0x5711e680), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0xf5df3913, 0x4f41d9f0, 0xa9c7c4ad, 0xa1c76e02, 0xc92d9e3f, 0xd5de26f4, 0x7e39e55e, 0xef6d1717), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x1db9675d, 0x54da4f19, 0x8bc3ba39, 0xc91d945a, 0x30eb2963, 0xc63eb119, 0x606d6a45, 0xc857dbe0), SECP256K1_FE_CONST(0x3b9efb64, 0xe9d56bf7, 0xee4bc029, 0x288e000e, 0x875be218, 0xd92fca16, 0xda6b82fe, 0xb7035c86), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xe24698a2, 0xab25b0e6, 0x743c45c6, 0x36e26ba5, 0xcf14d69c, 0x39c14ee6, 0x9f9295b9, 0x37a8204f), SECP256K1_FE_CONST(0xc461049b, 0x162a9408, 0x11b43fd6, 0xd771fff1, 0x78a41de7, 0x26d035e9, 0x25947d00, 0x48fc9fa9)}}, + {0x00, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0x7975920f, 0x7dd28f06, 0x0b90de63, 0xaa069e8c, 0x34858639, 0xf4a77e0d, 0x9774649e, 0xb9087bac), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0x3125472c, 0x4bca81e7, 0xfa8493d7, 0x253f29c8, 0x8a51d3ec, 0x7afefaae, 0x19f87a91, 0xc6c35775), {SECP256K1_FE_CONST(0x3a14b35f, 0x5b086a06, 0xf6b746cb, 0x79730ca2, 0x202855e7, 0xe1bbfdca, 0x1aa809bd, 0x810ff058), SECP256K1_FE_CONST(0xe116acef, 0x46c0d624, 0x6dc90c90, 0x714ad693, 0x47b24bdc, 0x2b07c677, 0xa7a24d13, 0xcba4d6ec), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xc5eb4ca0, 0xa4f795f9, 0x0948b934, 0x868cf35d, 0xdfd7aa18, 0x1e440235, 0xe557f641, 0x7ef00bd7), SECP256K1_FE_CONST(0x1ee95310, 0xb93f29db, 0x9236f36f, 0x8eb5296c, 0xb84db423, 0xd4f83988, 0x585db2eb, 0x345b2543), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0x7f39a9ef, 0x29f9d846, 0x5a1a18e1, 0x3ed5d07b, 0x613f8094, 0x96700779, 0xd81d8e89, 0x59b2e8c5), {SECP256K1_FE_CONST(0x1788e280, 0x7a2a0adc, 0xeb6cfa2e, 0xa176478b, 0xaee9b178, 0xbd2c3819, 0xe56e54c2, 0x6e4fccbd), SECP256K1_FE_CONST(0xc5983497, 0x8137ee51, 0xb41566c7, 0xb56c7df1, 0xe9ccd528, 0xfe0db5da, 0x33c95ff8, 0xf1b96212), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xe8771d7f, 0x85d5f523, 0x149305d1, 0x5e89b874, 0x51164e87, 0x42d3c7e6, 0x1a91ab3c, 0x91b02f72), SECP256K1_FE_CONST(0x3a67cb68, 0x7ec811ae, 0x4bea9938, 0x4a93820e, 0x16332ad7, 0x01f24a25, 0xcc36a006, 0x0e469a1d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0xf30a866b, 0x849cd237, 0x534f9089, 0xaed6bfcf, 0x8dd9952b, 0xd77346f6, 0xd426158b, 0xc82be41a), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xff, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0xdd7328f6, 0x725a645a, 0x4224d125, 0x455291fb, 0x3eeabb13, 0x6151926f, 0x5ca6d4c2, 0x849e3ef6), {SECP256K1_FE_CONST(0x362565da, 0x03102cb1, 0x084ab68f, 0xb28babcc, 0x3f9165e2, 0x4070e29a, 0x238ca4d1, 0x88b0c8ad), SECP256K1_FE_CONST(0xa3e8fec6, 0x1c9c7267, 0xda96f709, 0x958f8065, 0xaf5a59c2, 0xe2375058, 0x4b7ccc68, 0x6f31cf07), SECP256K1_FE_CONST(0x38c4364d, 0x829d26d1, 0xfd5d0080, 0xf399db60, 0xe3ff1836, 0xaff5d615, 0x42fc04b5, 0xdc690ffd), SECP256K1_FE_CONST(0x6d6333ac, 0x7a4cbac0, 0x458657c3, 0x898bf188, 0x30d4ba43, 0xf7ce7115, 0x54f3d846, 0x6023d718), SECP256K1_FE_CONST(0xc9da9a25, 0xfcefd34e, 0xf7b54970, 0x4d745433, 0xc06e9a1d, 0xbf8f1d65, 0xdc735b2d, 0x774f3382), SECP256K1_FE_CONST(0x5c170139, 0xe3638d98, 0x256908f6, 0x6a707f9a, 0x50a5a63d, 0x1dc8afa7, 0xb4833396, 0x90ce2d28), SECP256K1_FE_CONST(0xc73bc9b2, 0x7d62d92e, 0x02a2ff7f, 0x0c66249f, 0x1c00e7c9, 0x500a29ea, 0xbd03fb49, 0x2396ec32), SECP256K1_FE_CONST(0x929ccc53, 0x85b3453f, 0xba79a83c, 0x76740e77, 0xcf2b45bc, 0x08318eea, 0xab0c27b8, 0x9fdc2517)}}, + {0xcc, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), SECP256K1_FE_CONST(0xf0f46c7e, 0x8c23f563, 0x18550c00, 0x2ef33695, 0x01220ba3, 0xe25cb308, 0x4013711f, 0xb679743f), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x8e574be1, 0xbba447e9, 0x85f3ee1f, 0x4940c0ee, 0x27087f6d, 0xfb739fdd, 0x05aa1bb3, 0xfbc5b224), SECP256K1_FE_CONST(0xd1c89542, 0x677cfeb2, 0xf20712a2, 0x35033c21, 0x2b7a7446, 0xbc99894f, 0xd2d0651f, 0x20b75905), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x71a8b41e, 0x445bb816, 0x7a0c11e0, 0xb6bf3f11, 0xd8f78092, 0x048c6022, 0xfa55e44b, 0x043a4a0b), SECP256K1_FE_CONST(0x2e376abd, 0x9883014d, 0x0df8ed5d, 0xcafcc3de, 0xd4858bb9, 0x436676b0, 0x2d2f9adf, 0xdf48a32a)}}, + {0x33, SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), {SECP256K1_FE_CONST(0x2bd4bfb6, 0x851f02c7, 0xb9e42ee0, 0x1243906f, 0x0272ec4e, 0xad1781cc, 0x345affbc, 0x83aa54ef), SECP256K1_FE_CONST(0x3750ab59, 0xd50a6745, 0x5be4edb0, 0x71f0e82f, 0x370010ec, 0xd7a84a5b, 0x66549448, 0x3a07a6f6), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xd42b4049, 0x7ae0fd38, 0x461bd11f, 0xedbc6f90, 0xfd8d13b1, 0x52e87e33, 0xcba50042, 0x7c55a740), SECP256K1_FE_CONST(0xc8af54a6, 0x2af598ba, 0xa41b124f, 0x8e0f17d0, 0xc8ffef13, 0x2857b5a4, 0x99ab6bb6, 0xc5f85539), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffe18), SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc13), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xefbd23d5, 0x2ebf879f, 0x228dbeb0, 0x5c85881a, 0xdb886b53, 0x23bda366, 0x4520a05e, 0x6c549854), SECP256K1_FE_CONST(0x1326b8de, 0x9cad16c3, 0xc859d692, 0xfbc6c22a, 0x78698964, 0x86e0b713, 0x174982af, 0x7d28eb8d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x1042dc2a, 0xd1407860, 0xdd72414f, 0xa37a77e5, 0x247794ac, 0xdc425c99, 0xbadf5fa0, 0x93ab63db), SECP256K1_FE_CONST(0xecd94721, 0x6352e93c, 0x37a6296d, 0x04393dd5, 0x8796769b, 0x791f48ec, 0xe8b67d4f, 0x82d710a2)}}, + {0xff, SECP256K1_FE_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffe17), SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc13), {SECP256K1_FE_CONST(0x6342f23b, 0x31f75ef3, 0x861b36fc, 0x33383cfb, 0x43d08212, 0xe42ad82b, 0x5b397b00, 0x005ebee7), SECP256K1_FE_CONST(0xf3d7c0d4, 0x14a2d008, 0x4251039d, 0x4ad2978e, 0x0c5a2094, 0x5f21755b, 0xf3873e00, 0x2c359f65), SECP256K1_FE_CONST(0xa14a6f4e, 0x4006cb83, 0x7201f076, 0x58ca4e2e, 0x369402df, 0xa5b9a6a2, 0x6522fd67, 0x3916dfa4), SECP256K1_FE_CONST(0x8beba960, 0x40d7d2bd, 0xb9af082d, 0xfc7ff55f, 0x29e55f15, 0xa6826848, 0x6dd89b37, 0x3cb586b1), SECP256K1_FE_CONST(0x9cbd0dc4, 0xce08a10c, 0x79e4c903, 0xccc7c304, 0xbc2f7ded, 0x1bd527d4, 0xa4c684fe, 0xffa13d48), SECP256K1_FE_CONST(0x0c283f2b, 0xeb5d2ff7, 0xbdaefc62, 0xb52d6871, 0xf3a5df6b, 0xa0de8aa4, 0x0c78c1fe, 0xd3ca5cca), SECP256K1_FE_CONST(0x5eb590b1, 0xbff9347c, 0x8dfe0f89, 0xa735b1d1, 0xc96bfd20, 0x5a46595d, 0x9add0297, 0xc6e91c8b), SECP256K1_FE_CONST(0x7414569f, 0xbf282d42, 0x4650f7d2, 0x03800aa0, 0xd61aa0ea, 0x597d97b7, 0x922764c7, 0xc34a757e)}}, + {0x00, SECP256K1_FE_CONST(0x6e340b9c, 0xffb37a98, 0x9ca544e6, 0xbb780a2c, 0x78901d3f, 0xb3373876, 0x8511a306, 0x17afa01d), SECP256K1_FE_CONST(0x91cbf463, 0x004c8567, 0x635abb19, 0x4487f5d3, 0x876fe2c0, 0x4cc8c789, 0x7aee5cf8, 0xe8505c12), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x77, SECP256K1_FE_CONST(0x6e340b9c, 0xffb37a98, 0x9ca544e6, 0xbb780a2c, 0x78901d3f, 0xb3373876, 0x8511a306, 0x17afa01d), SECP256K1_FE_CONST(0x161462dd, 0x57fffa52, 0x1137bcd7, 0x9ed6981a, 0x726e402a, 0xc56b081c, 0x2bbe912e, 0x3132360d), {SECP256K1_FE_CONST(0x51fe8154, 0x3cba720f, 0x207dab99, 0x1262b65e, 0xa1b89324, 0x25fd389b, 0xcdb6a339, 0x7b045976), SECP256K1_FE_CONST(0x866f19a8, 0xdda199c9, 0x22157b84, 0x46ded073, 0xa4d67b2e, 0x893675dd, 0xd99aaaba, 0xe7bf1a25), SECP256K1_FE_CONST(0xae574801, 0x101b2890, 0xd3c2d4ba, 0xc6cb4559, 0x0d9ebe59, 0x6e75638a, 0xa8d65f54, 0xc56f6004), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xae017eab, 0xc3458df0, 0xdf825466, 0xed9d49a1, 0x5e476cdb, 0xda02c764, 0x32495cc5, 0x84fba2b9), SECP256K1_FE_CONST(0x7990e657, 0x225e6636, 0xddea847b, 0xb9212f8c, 0x5b2984d1, 0x76c98a22, 0x26655544, 0x1840e20a), SECP256K1_FE_CONST(0x51a8b7fe, 0xefe4d76f, 0x2c3d2b45, 0x3934baa6, 0xf26141a6, 0x918a9c75, 0x5729a0aa, 0x3a909c2b), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x6e340b9c, 0xffb37a98, 0x9ca544e6, 0xbb780a2c, 0x78901d3f, 0xb3373876, 0x8511a306, 0x17afa01d), SECP256K1_FE_CONST(0x2c1c4d0d, 0x41ecda63, 0xb4131edb, 0x65fef49e, 0xf3f6b770, 0x00de1432, 0xc21355a4, 0x2ad19091), {SECP256K1_FE_CONST(0xa1b6e32d, 0x9a3b31b5, 0xecad712f, 0x72bfe460, 0x587dcea9, 0x5c6c65c1, 0xaa1dad5a, 0xa4cf57c0), SECP256K1_FE_CONST(0xf5696de5, 0x3dba0943, 0xafe12a72, 0x0049b0a8, 0x6f6cde0e, 0xd4a5eb64, 0xb7f52a8b, 0x464cbedb), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x5e491cd2, 0x65c4ce4a, 0x13528ed0, 0x8d401b9f, 0xa7823156, 0xa3939a3e, 0x55e252a4, 0x5b30a46f), SECP256K1_FE_CONST(0x0a96921a, 0xc245f6bc, 0x501ed58d, 0xffb64f57, 0x909321f1, 0x2b5a149b, 0x480ad573, 0xb9b33d54), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0x4bf5122f, 0x344554c5, 0x3bde2ebb, 0x8cd2b7e3, 0xd1600ad6, 0x31c385a5, 0xd7cce23c, 0x7785459a), SECP256K1_FE_CONST(0x71a59aaa, 0x83bff3a0, 0x53323c20, 0xa43aa0ff, 0x3b17f582, 0xd245ba85, 0xb2ad61cf, 0x91df00bf), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xaa89b76b, 0x710916f7, 0x7e57e7bf, 0xd726ad9d, 0x27e90d86, 0x18903b0a, 0x1852b680, 0x478b687c), SECP256K1_FE_CONST(0x6fa74a38, 0x06a04766, 0xdd1d2ed9, 0x81466c12, 0x8ec84ade, 0x00ff9883, 0xb4354956, 0x0834fde1), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x55764894, 0x8ef6e908, 0x81a81840, 0x28d95262, 0xd816f279, 0xe76fc4f5, 0xe7ad497e, 0xb87493b3), SECP256K1_FE_CONST(0x9058b5c7, 0xf95fb899, 0x22e2d126, 0x7eb993ed, 0x7137b521, 0xff00677c, 0x4bcab6a8, 0xf7cafe4e)}}, + {0xcc, SECP256K1_FE_CONST(0xe52d9c50, 0x8c502347, 0x344d8c07, 0xad91cbd6, 0x068afc75, 0xff6292f0, 0x62a09ca3, 0x81c89e71), SECP256K1_FE_CONST(0x1ad263af, 0x73afdcb8, 0xcbb273f8, 0x526e3429, 0xf975038a, 0x009d6d0f, 0x9d5f635b, 0x7e375dbe), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb8d1cbd7, 0x33ae8215, 0x642c30af, 0x7cedc7ef, 0x73be8269, 0xfbcc1fb5, 0x44ab3dee, 0xdbea1af4), SECP256K1_FE_CONST(0x945290ca, 0x86af703c, 0x1e0bed9d, 0xf1514972, 0x4357fb2b, 0x8d2382ce, 0x6c2794bf, 0xd14efe9c), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x472e3428, 0xcc517dea, 0x9bd3cf50, 0x83123810, 0x8c417d96, 0x0433e04a, 0xbb54c210, 0x2415e13b), SECP256K1_FE_CONST(0x6bad6f35, 0x79508fc3, 0xe1f41262, 0x0eaeb68d, 0xbca804d4, 0x72dc7d31, 0x93d86b3f, 0x2eb0fd93)}}, + {0x00, SECP256K1_FE_CONST(0xe52d9c50, 0x8c502347, 0x344d8c07, 0xad91cbd6, 0x068afc75, 0xff6292f0, 0x62a09ca3, 0x81c89e71), SECP256K1_FE_CONST(0x2a58379a, 0x649cd129, 0x3b83c6f8, 0x59fa83fe, 0xa9850a31, 0x5bd1d7aa, 0xfda9b4b3, 0x25e37402), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0x67586e98, 0xfad27da0, 0xb9968bc0, 0x39a1ef34, 0xc939b9b8, 0xe523a8be, 0xf89d4786, 0x08c5ecf6), SECP256K1_FE_CONST(0x98a79167, 0x052d825f, 0x4669743f, 0xc65e10cb, 0x36c64647, 0x1adc5741, 0x0762b878, 0xf73a0f39), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0x67586e98, 0xfad27da0, 0xb9968bc0, 0x39a1ef34, 0xc939b9b8, 0xe523a8be, 0xf89d4786, 0x08c5ecf6), SECP256K1_FE_CONST(0x15a4118b, 0x47af907e, 0xd47bc9cd, 0x722f3641, 0x134228bd, 0x78c1934b, 0x615136f8, 0x3a35675c), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x44, SECP256K1_FE_CONST(0x01ba4719, 0xc80b6fe9, 0x11b091a7, 0xc05124b6, 0x4eeece96, 0x4e09c058, 0xef8f9805, 0xdaca546b), SECP256K1_FE_CONST(0xbcfa28ac, 0x31453f0e, 0x2ea23512, 0x37dac2de, 0x92e5bbb5, 0xaebfd7bc, 0x3c5a2cae, 0x57c5440f), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x6fa317d3, 0x93c1632e, 0x73ea7133, 0xb38b0904, 0x670b85c1, 0xf48efa45, 0xcc8c2459, 0xd1463610), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x905ce82c, 0x6c3e9cd1, 0x8c158ecc, 0x4c74f6fb, 0x98f47a3e, 0x0b7105ba, 0x3373dba5, 0x2eb9c61f), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0xef6cbd21, 0x61eaea79, 0x43ce8693, 0xb9824d23, 0xd1793ffb, 0x1c0fca05, 0xb600d389, 0x9b44c977), SECP256K1_FE_CONST(0x129374d7, 0x73a60424, 0x662d54d6, 0xe8eba424, 0x38c8c9a7, 0x701a59dc, 0x8fbd0fbb, 0xe6094899), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x1b65b9d9, 0x1eda84f6, 0x9a068601, 0x70c9c4e9, 0xd43cd0f2, 0x53ec9b13, 0x0f6c2b3d, 0xd949b672), SECP256K1_FE_CONST(0xb5139bd2, 0x4e8e8a6e, 0x5d0875f3, 0x58e3d884, 0xf7f9836d, 0x893fdae8, 0x86cbc6f9, 0x1fd2f993), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xe49a4626, 0xe1257b09, 0x65f979fe, 0x8f363b16, 0x2bc32f0d, 0xac1364ec, 0xf093d4c1, 0x26b645bd), SECP256K1_FE_CONST(0x4aec642d, 0xb1717591, 0xa2f78a0c, 0xa71c277b, 0x08067c92, 0x76c02517, 0x79343905, 0xe02d029c)}}, + {0x33, SECP256K1_FE_CONST(0xdc0e9c36, 0x58a1a3ed, 0x1ec94274, 0xd8b19925, 0xc93e1abb, 0x7ddba294, 0x923ad9bd, 0xe30f8cb8), SECP256K1_FE_CONST(0x23f163c9, 0xa75e5c12, 0xe136bd8b, 0x274e66da, 0x36c1e544, 0x82245d6b, 0x6dc52641, 0x1cf06f77), {SECP256K1_FE_CONST(0x8245e76e, 0xd8605614, 0xa33447db, 0xdcd4b712, 0x3b80c63f, 0xd0809c87, 0x7b134540, 0x63732da2), SECP256K1_FE_CONST(0x9e474ae5, 0x1714465b, 0x33293068, 0x569fe336, 0x174fb0dc, 0x259049d7, 0x6917ce59, 0x07b6dcff), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x7dba1891, 0x279fa9eb, 0x5ccbb824, 0x232b48ed, 0xc47f39c0, 0x2f7f6378, 0x84ecbabe, 0x9c8cce8d), SECP256K1_FE_CONST(0x61b8b51a, 0xe8ebb9a4, 0xccd6cf97, 0xa9601cc9, 0xe8b04f23, 0xda6fb628, 0x96e831a5, 0xf8491f30), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xff, SECP256K1_FE_CONST(0xc555eab4, 0x5d08845a, 0xe9f10d45, 0x2a99bfcb, 0x06f74a50, 0xb988fe7e, 0x48dd3237, 0x89b88ee3), SECP256K1_FE_CONST(0x356b434d, 0x1ef1666c, 0xf7d19635, 0x94a3bdad, 0x982f46ab, 0x3cbdd705, 0x9e1bd9ab, 0x0b7e1686), {SECP256K1_FE_CONST(0x5439b77f, 0x597b2e6f, 0xe48c3f46, 0x599a18fa, 0x0ae89a7a, 0xf778c1dc, 0x886793c8, 0x0fe616ee), SECP256K1_FE_CONST(0xbd6ec9c1, 0x2a329529, 0xf15dfc85, 0xc4526169, 0x5d0767c7, 0x7b4f13ea, 0x91395718, 0x07f3b290), SECP256K1_FE_CONST(0x55715e7f, 0x5d440cc8, 0x3a4010d0, 0x34221026, 0xbcee7131, 0x6217b016, 0xb90dfee7, 0x60a48608), SECP256K1_FE_CONST(0x7e2af404, 0x24b93bf0, 0x1d143213, 0x30df30a2, 0x09678d47, 0xccd5135e, 0x739e4028, 0x26844028), SECP256K1_FE_CONST(0xabc64880, 0xa684d190, 0x1b73c0b9, 0xa665e705, 0xf5176585, 0x08873e23, 0x77986c36, 0xf019e541), SECP256K1_FE_CONST(0x4291363e, 0xd5cd6ad6, 0x0ea2037a, 0x3bad9e96, 0xa2f89838, 0x84b0ec15, 0x6ec6a8e6, 0xf80c499f), SECP256K1_FE_CONST(0xaa8ea180, 0xa2bbf337, 0xc5bfef2f, 0xcbddefd9, 0x43118ece, 0x9de84fe9, 0x46f20117, 0x9f5b7627), SECP256K1_FE_CONST(0x81d50bfb, 0xdb46c40f, 0xe2ebcdec, 0xcf20cf5d, 0xf69872b8, 0x332aeca1, 0x8c61bfd6, 0xd97bbc07)}}, + {0x33, SECP256K1_FE_CONST(0xab897fbd, 0xedfa502b, 0x2d839b6a, 0x56100887, 0xdccdc507, 0x555c282e, 0x59589e06, 0x300a62e2), SECP256K1_FE_CONST(0x3119ceb1, 0xe5e26b7b, 0x1a85520b, 0xaec3ad2c, 0x5661a453, 0xec37f4c6, 0xfae6be04, 0x905fed19), {SECP256K1_FE_CONST(0xa7698f80, 0xa9f7d4f1, 0x4973086b, 0x258934fb, 0x85f056a1, 0xcc824068, 0x70555d65, 0xa5c77c9d), SECP256K1_FE_CONST(0xf5284d8f, 0xc6ee63f5, 0x9511b121, 0xf4fb6105, 0x11b38678, 0x577a2a74, 0xe151f484, 0xfa980ce7), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x5896707f, 0x56082b0e, 0xb68cf794, 0xda76cb04, 0x7a0fa95e, 0x337dbf97, 0x8faaa299, 0x5a387f92), SECP256K1_FE_CONST(0x0ad7b270, 0x39119c0a, 0x6aee4ede, 0x0b049efa, 0xee4c7987, 0xa885d58b, 0x1eae0b7a, 0x0567ef48), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0xbd4fc42a, 0x21f1f860, 0xa1030e6e, 0xba23d53e, 0xcab71bd1, 0x9297ab6c, 0x074381d4, 0xecee0018), SECP256K1_FE_CONST(0x503cffcb, 0xc1e36f3c, 0x517b387a, 0xd7cbc856, 0x576627d1, 0x4c500c68, 0x33d17039, 0xbb652c96), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0x8a331fdd, 0xe7032f33, 0xa71e1b2e, 0x257d8016, 0x6e348e00, 0xfcb17914, 0xf48bdb57, 0xa1c63007), SECP256K1_FE_CONST(0x5796039b, 0xb8d1bc43, 0x7e7be940, 0x5259919f, 0xc3436f9c, 0xcfd03f91, 0x4c655809, 0x066c7412), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xf4ec92d2, 0xa1c536c0, 0x3ec32f1b, 0x189f29a2, 0x928ca492, 0x00e81d6e, 0x3e21d469, 0x5458ce50), SECP256K1_FE_CONST(0xf746d123, 0x702173df, 0x05b05807, 0x67a764fe, 0x71c5d1dc, 0xb9aba858, 0xa862814f, 0xa31faf3d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x0b136d2d, 0x5e3ac93f, 0xc13cd0e4, 0xe760d65d, 0x6d735b6d, 0xff17e291, 0xc1de2b95, 0xaba72ddf), SECP256K1_FE_CONST(0x08b92edc, 0x8fde8c20, 0xfa4fa7f8, 0x98589b01, 0x8e3a2e23, 0x465457a7, 0x579d7eaf, 0x5ce04cf2)}}, + {0xff, SECP256K1_FE_CONST(0x8a5edab2, 0x82632443, 0x219e051e, 0x4ade2d1d, 0x5bbc671c, 0x781051bf, 0x1437897c, 0xbdfea0f1), SECP256K1_FE_CONST(0x75a1254d, 0x7d9cdbbc, 0xde61fae1, 0xb521d2e2, 0xa44398e3, 0x87efae40, 0xebc87682, 0x42015b3e), {SECP256K1_FE_CONST(0xd7a081ca, 0xf5521f4d, 0xb4f7c478, 0x35ab68a8, 0x217980e6, 0xdad52704, 0xc70b9ba2, 0x14ee14b0), SECP256K1_FE_CONST(0x195dcf2d, 0xa9578581, 0xefa8f64c, 0xa9d6ed4b, 0x8d95e4d0, 0x058fec92, 0x789ad40d, 0xa38c63bb), SECP256K1_FE_CONST(0x52235236, 0x41d891c9, 0x536b9668, 0x46f2af60, 0x028fbd88, 0xac20cad5, 0xc6890a04, 0x886ccc5b), SECP256K1_FE_CONST(0x84e5bc30, 0x521d45e4, 0x0783f049, 0x740067b1, 0x57bfb6d7, 0x71484329, 0x6daba2c9, 0xfc8949fd), SECP256K1_FE_CONST(0x285f7e35, 0x0aade0b2, 0x4b083b87, 0xca549757, 0xde867f19, 0x252ad8fb, 0x38f4645c, 0xeb11e77f), SECP256K1_FE_CONST(0xe6a230d2, 0x56a87a7e, 0x105709b3, 0x562912b4, 0x726a1b2f, 0xfa70136d, 0x87652bf1, 0x5c739874), SECP256K1_FE_CONST(0xaddcadc9, 0xbe276e36, 0xac946997, 0xb90d509f, 0xfd704277, 0x53df352a, 0x3976f5fa, 0x77932fd4), SECP256K1_FE_CONST(0x7b1a43cf, 0xade2ba1b, 0xf87c0fb6, 0x8bff984e, 0xa8404928, 0x8eb7bcd6, 0x92545d35, 0x0376b232)}}, + {0x00, SECP256K1_FE_CONST(0xe7f6c011, 0x776e8db7, 0xcd330b54, 0x174fd76f, 0x7d0216b6, 0x12387a5f, 0xfcfb81e6, 0xf0919683), SECP256K1_FE_CONST(0x2838007a, 0x22e59acb, 0xe2f7e413, 0xd2327157, 0x1c83200f, 0xca4a029d, 0x5b84990f, 0xc3b96177), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}} +}; + +/** This is a hasher for ellswift_xdh which just returns the shared X coordinate. + * + * This is generally a bad idea as it means changes to the encoding of the + * exchanged public keys do not affect the shared secret. However, it's used here + * in tests to be able to verify the X coordinate through other means. + */ +static int ellswift_xdh_hash_x32(unsigned char *output, const unsigned char *x32, const unsigned char *ours64, const unsigned char *theirs64, void *data) { + (void)ours64; + (void)theirs64; + (void)data; + memcpy(output, x32, 32); + return 1; +} + +void run_ellswift_tests(void) { + int i = 0; + /* Test vectors. */ + for (i = 0; (unsigned)i < sizeof(ellswift_tests) / sizeof(ellswift_tests[0]); ++i) { + const struct ellswift_test_vec* testcase = &ellswift_tests[i]; + int c; + for (c = 0; c < 8; ++c) { + secp256k1_fe t; + int ret = secp256k1_ellswift_fegex_to_fe_var(&t, &testcase->x, &testcase->u, c); + CHECK(ret == ((testcase->enc_bitmap >> c) & 1)); + if (ret) { + secp256k1_fe x2; + CHECK(check_fe_equal(&t, &testcase->encs[c])); + secp256k1_ellswift_fe2_to_gex_var(&x2, &testcase->u, &testcase->encs[c]); + CHECK(check_fe_equal(&testcase->x, &x2)); + } + } + } + /* Verify that secp256k1_ellswift_encode + decode roundtrips. */ + for (i = 0; i < 1000 * count; i++) { + unsigned char rnd32[32]; + unsigned char ell64[64]; + secp256k1_ge g, g2; + secp256k1_pubkey pubkey, pubkey2; + /* Generate random public key and random randomizer. */ + random_group_element_test(&g); + secp256k1_pubkey_save(&pubkey, &g); + secp256k1_testrand256(rnd32); + /* Convert the public key to ElligatorSwift and back. */ + secp256k1_ellswift_encode(ctx, ell64, &pubkey, rnd32); + secp256k1_ellswift_decode(ctx, &pubkey2, ell64); + secp256k1_pubkey_load(ctx, &g2, &pubkey2); + /* Compare with original. */ + ge_equals_ge(&g, &g2); + } + /* Verify the behavior of secp256k1_ellswift_create */ + for (i = 0; i < 400 * count; i++) { + unsigned char rnd32[32], sec32[32]; + secp256k1_scalar sec; + secp256k1_gej res; + secp256k1_ge dec; + secp256k1_pubkey pub; + unsigned char ell64[64]; + int ret; + /* Generate random secret key and random randomizer. */ + secp256k1_testrand256_test(rnd32); + random_scalar_order_test(&sec); + secp256k1_scalar_get_b32(sec32, &sec); + /* Construct ElligatorSwift-encoded public keys for that key. */ + ret = secp256k1_ellswift_create(ctx, ell64, sec32, rnd32); + CHECK(ret); + /* Decode it, and compare with traditionally-computed public key. */ + secp256k1_ellswift_decode(ctx, &pub, ell64); + secp256k1_pubkey_load(ctx, &dec, &pub); + secp256k1_ecmult(&res, NULL, &secp256k1_scalar_zero, &sec); + ge_equals_gej(&dec, &res); + } + /* Verify that secp256k1_ellswift_xdh computes the right shared X coordinate. */ + for (i = 0; i < 800 * count; i++) { + unsigned char ell64[64], sec32[32], share32[32]; + secp256k1_scalar sec; + secp256k1_ge dec, res; + secp256k1_fe share_x; + secp256k1_gej decj, resj; + secp256k1_pubkey pub; + int ret; + /* Generate random secret key. */ + random_scalar_order_test(&sec); + secp256k1_scalar_get_b32(sec32, &sec); + /* Generate random ElligatorSwift encoding for the remote key and decode it. */ + secp256k1_testrand256_test(ell64); + secp256k1_testrand256_test(ell64 + 32); + secp256k1_ellswift_decode(ctx, &pub, ell64); + secp256k1_pubkey_load(ctx, &dec, &pub); + secp256k1_gej_set_ge(&decj, &dec); + /* Compute the X coordinate of seckey*pubkey using ellswift_xdh. Note that we + * pass ell64 as claimed (but incorrect) encoding for sec32 here; this works + * because the "hasher" function we use here ignores the ours64 argument. */ + ret = secp256k1_ellswift_xdh(ctx, share32, ell64, ell64, sec32, &ellswift_xdh_hash_x32, NULL); + CHECK(ret); + secp256k1_fe_set_b32(&share_x, share32); + /* Compute seckey*pubkey directly. */ + secp256k1_ecmult(&resj, &decj, &sec, NULL); + secp256k1_ge_set_gej(&res, &resj); + /* Compare. */ + CHECK(check_fe_equal(&res.x, &share_x)); + } + /* Verify the joint behavior of secp256k1_ellswift_xdh */ + for (i = 0; i < 200 * count; i++) { + unsigned char rnd32a[32], rnd32b[32], sec32a[32], sec32b[32]; + secp256k1_scalar seca, secb; + unsigned char ell64a[64], ell64b[64]; + unsigned char share32a[32], share32b[32]; + int ret; + /* Generate random secret keys and random randomizers. */ + secp256k1_testrand256_test(rnd32a); + secp256k1_testrand256_test(rnd32b); + random_scalar_order_test(&seca); + random_scalar_order_test(&secb); + secp256k1_scalar_get_b32(sec32a, &seca); + secp256k1_scalar_get_b32(sec32b, &secb); + /* Construct ElligatorSwift-encoded public keys for those keys. */ + ret = secp256k1_ellswift_create(ctx, ell64a, sec32a, rnd32a); + CHECK(ret); + ret = secp256k1_ellswift_create(ctx, ell64b, sec32b, rnd32b); + CHECK(ret); + /* Compute the shared secret both ways and compare with each other. */ + ret = secp256k1_ellswift_xdh(ctx, share32a, ell64a, ell64b, sec32b, NULL, NULL); + CHECK(ret); + ret = secp256k1_ellswift_xdh(ctx, share32b, ell64b, ell64a, sec32a, NULL, NULL); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32a, share32b, 32) == 0); + /* Verify that the shared secret doesn't match if a secret key or remote pubkey changes. */ + secp256k1_testrand_flip(ell64a, 64); + ret = secp256k1_ellswift_xdh(ctx, share32a, ell64a, ell64b, sec32b, NULL, NULL); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32a, share32b, 32) != 0); + secp256k1_testrand_flip(sec32a, 32); + ret = secp256k1_ellswift_xdh(ctx, share32a, ell64a, ell64b, sec32b, NULL, NULL); + CHECK(!ret || secp256k1_memcmp_var(share32a, share32b, 32) != 0); + } +} + +#endif diff --git a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h index d4a2f5bdf4050..5ecc90d50fca1 100644 --- a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h +++ b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h @@ -7,8 +7,8 @@ #ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H #define SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H -#include "src/modules/extrakeys/main_impl.h" #include "../../../include/secp256k1_extrakeys.h" +#include "main_impl.h" static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp256k1_ge* group) { secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1]; diff --git a/src/secp256k1/src/modules/recovery/bench_impl.h b/src/secp256k1/src/modules/recovery/bench_impl.h index 4a9e886910b8e..e1cf4924d3717 100644 --- a/src/secp256k1/src/modules/recovery/bench_impl.h +++ b/src/secp256k1/src/modules/recovery/bench_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_RECOVERY_BENCH_H #define SECP256K1_MODULE_RECOVERY_BENCH_H -#include "../include/secp256k1_recovery.h" +#include "../../../include/secp256k1_recovery.h" typedef struct { secp256k1_context *ctx; diff --git a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h index 590a972ed3f74..ed9386b6f8a57 100644 --- a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h +++ b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H #define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H -#include "src/modules/recovery/main_impl.h" +#include "main_impl.h" #include "../../../include/secp256k1_recovery.h" void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { diff --git a/src/secp256k1/src/modules/schnorrsig/bench_impl.h b/src/secp256k1/src/modules/schnorrsig/bench_impl.h index 41f393c84d7f0..84a172742f1f8 100644 --- a/src/secp256k1/src/modules/schnorrsig/bench_impl.h +++ b/src/secp256k1/src/modules/schnorrsig/bench_impl.h @@ -91,10 +91,12 @@ void run_schnorrsig_bench(int iters, int argc, char** argv) { free((void *)data.msgs[i]); free((void *)data.sigs[i]); } - free(data.keypairs); - free(data.pk); - free(data.msgs); - free(data.sigs); + + /* Casting to (void *) avoids a stupid warning in MSVC. */ + free((void *)data.keypairs); + free((void *)data.pk); + free((void *)data.msgs); + free((void *)data.sigs); secp256k1_context_destroy(data.ctx); } diff --git a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h index d8df9dd2df742..55f9028a6386f 100644 --- a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h +++ b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h @@ -8,7 +8,7 @@ #define SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H #include "../../../include/secp256k1_schnorrsig.h" -#include "src/modules/schnorrsig/main_impl.h" +#include "main_impl.h" static const unsigned char invalid_pubkey_bytes[][32] = { /* 0 */ diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h index 688e18eb66208..f71a20b9637d0 100644 --- a/src/secp256k1/src/scratch_impl.h +++ b/src/secp256k1/src/scratch_impl.h @@ -25,11 +25,11 @@ static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* err static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) { if (scratch != NULL) { - VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */ if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { secp256k1_callback_call(error_callback, "invalid scratch space"); return; } + VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */ memset(scratch->magic, 0, sizeof(scratch->magic)); free(scratch); } diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c index 8f34c35283d3d..df9bd1e5d7666 100644 --- a/src/secp256k1/src/secp256k1.c +++ b/src/secp256k1/src/secp256k1.c @@ -4,6 +4,17 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ +/* This is a C project. It should not be compiled with a C++ compiler, + * and we error out if we detect one. + * + * We still want to be able to test the project with a C++ compiler + * because it is still good to know if this will lead to real trouble, so + * there is a possibility to override the check. But be warned that + * compiling with a C++ compiler is not supported. */ +#if defined(__cplusplus) && !defined(SECP256K1_CPLUSPLUS_TEST_OVERRIDE) +#error Trying to compile a C project with a C++ compiler. +#endif + #define SECP256K1_BUILD #include "../include/secp256k1.h" @@ -765,3 +776,7 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, #ifdef ENABLE_MODULE_SCHNORRSIG # include "modules/schnorrsig/main_impl.h" #endif + +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/main_impl.h" +#endif diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c index dd53173930796..9494a2cdc9b2f 100644 --- a/src/secp256k1/src/tests.c +++ b/src/secp256k1/src/tests.c @@ -942,12 +942,32 @@ void test_modinv32_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod uint16_to_signed30(&x, in); nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4] | x.v[5] | x.v[6] | x.v[7] | x.v[8]) != 0; uint16_to_signed30(&m.modulus, mod); - mutate_sign_signed30(&m.modulus); /* compute 1/modulus mod 2^30 */ m.modulus_inv30 = modinv2p64(m.modulus.v[0]) & 0x3fffffff; CHECK(((m.modulus_inv30 * m.modulus.v[0]) & 0x3fffffff) == 1); + /* Test secp256k1_jacobi32_maybe_var. */ + { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed30(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 0 or 1 (or uncomputable). */ + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == -2 || jac == nonzero); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed30(&x, sqr); + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == -2 || jac == (1 - (mod[0] & 2)) * nonzero); + } + + uint16_to_signed30(&x, in); + mutate_sign_signed30(&m.modulus); for (vartime = 0; vartime < 2; ++vartime) { /* compute inverse */ (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m); @@ -1015,12 +1035,32 @@ void test_modinv64_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod uint16_to_signed62(&x, in); nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4]) != 0; uint16_to_signed62(&m.modulus, mod); - mutate_sign_signed62(&m.modulus); /* compute 1/modulus mod 2^62 */ m.modulus_inv62 = modinv2p64(m.modulus.v[0]) & M62; CHECK(((m.modulus_inv62 * m.modulus.v[0]) & M62) == 1); + /* Test secp256k1_jacobi64_maybe_var. */ + { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed62(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 0 or 1 (or uncomputable). */ + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == -2 || jac == nonzero); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed62(&x, sqr); + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == -2 || jac == (1 - (mod[0] & 2)) * nonzero); + } + + uint16_to_signed62(&x, in); + mutate_sign_signed62(&m.modulus); for (vartime = 0; vartime < 2; ++vartime) { /* compute inverse */ (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m); @@ -2854,8 +2894,10 @@ void run_sqrt(void) { for (j = 0; j < count; j++) { random_fe(&x); secp256k1_fe_sqr(&s, &x); + CHECK(secp256k1_fe_jacobi_var(&s) == 1); test_sqrt(&s, &x); secp256k1_fe_negate(&t, &s, 1); + CHECK(secp256k1_fe_jacobi_var(&t) == -1); test_sqrt(&t, NULL); secp256k1_fe_mul(&t, &s, &ns); test_sqrt(&t, NULL); @@ -3986,6 +4028,68 @@ void ecmult_const_mult_zero_one(void) { ge_equals_ge(&res2, &point); } +void ecmult_const_mult_xonly(void) { + int i; + + /* Test correspondence between secp256k1_ecmult_const and secp256k1_ecmult_const_xonly. */ + for (i = 0; i < 2*count; ++i) { + secp256k1_ge base; + secp256k1_gej basej, resj; + secp256k1_fe n, d, resx, v; + secp256k1_scalar q; + int res; + /* Random base point. */ + random_group_element_test(&base); + /* Random scalar to multiply it with. */ + random_scalar_order_test(&q); + /* If i is odd, n=d*base.x for random non-zero d */ + if (i & 1) { + do { + random_field_element_test(&d); + } while (secp256k1_fe_normalizes_to_zero_var(&d)); + secp256k1_fe_mul(&n, &base.x, &d); + } else { + n = base.x; + } + /* Perform x-only multiplication. */ + res = secp256k1_ecmult_const_xonly(&resx, &n, (i & 1) ? &d : NULL, &q, 256, i & 2); + CHECK(res); + /* Perform normal multiplication. */ + secp256k1_gej_set_ge(&basej, &base); + secp256k1_ecmult(&resj, &basej, &q, NULL); + /* Check that resj's X coordinate corresponds with resx. */ + secp256k1_fe_sqr(&v, &resj.z); + secp256k1_fe_mul(&v, &v, &resx); + CHECK(check_fe_equal(&v, &resj.x)); + } + + /* Test that secp256k1_ecmult_const_xonly correctly rejects X coordinates not on curve. */ + for (i = 0; i < 2*count; ++i) { + secp256k1_fe x, n, d, c, r; + int res; + secp256k1_scalar q; + random_scalar_order_test(&q); + /* Generate random X coordinate not on the curve. */ + do { + random_field_element_test(&x); + secp256k1_fe_sqr(&c, &x); + secp256k1_fe_mul(&c, &c, &x); + secp256k1_fe_add(&c, &secp256k1_fe_const_b); + } while (secp256k1_fe_jacobi_var(&c) >= 0); + /* If i is odd, n=d*x for random non-zero d. */ + if (i & 1) { + do { + random_field_element_test(&d); + } while (secp256k1_fe_normalizes_to_zero_var(&d)); + secp256k1_fe_mul(&n, &x, &d); + } else { + n = x; + } + res = secp256k1_ecmult_const_xonly(&r, &n, (i & 1) ? &d : NULL, &q, 256, 0); + CHECK(res == 0); + } +} + void ecmult_const_chain_multiply(void) { /* Check known result (randomly generated test problem from sage) */ const secp256k1_scalar scalar = SECP256K1_SCALAR_CONST( @@ -4017,6 +4121,7 @@ void run_ecmult_const_tests(void) { ecmult_const_random_mult(); ecmult_const_commutativity(); ecmult_const_chain_multiply(); + ecmult_const_mult_xonly(); } typedef struct { @@ -6872,6 +6977,10 @@ void run_ecdsa_edge_cases(void) { # include "modules/schnorrsig/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/tests_impl.h" +#endif + void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -7086,11 +7195,15 @@ int main(int argc, char **argv) { run_context_tests(0); run_context_tests(1); run_scratch_tests(); + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - if (secp256k1_testrand_bits(1)) { + /* Randomize the context only with probability 15/16 + to make sure we test without context randomization from time to time. + TODO Reconsider this when recalibrating the tests. */ + if (secp256k1_testrand_bits(4)) { unsigned char rand32[32]; secp256k1_testrand256(rand32); - CHECK(secp256k1_context_randomize(ctx, secp256k1_testrand_bits(1) ? rand32 : NULL)); + CHECK(secp256k1_context_randomize(ctx, rand32)); } run_rand_bits(); @@ -7172,6 +7285,10 @@ int main(int argc, char **argv) { run_schnorrsig_tests(); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + run_ellswift_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); run_secp256k1_byteorder_tests(); diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c index 6a4e2340f2856..225bbddffc31d 100644 --- a/src/secp256k1/src/tests_exhaustive.c +++ b/src/secp256k1/src/tests_exhaustive.c @@ -342,15 +342,15 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou } #ifdef ENABLE_MODULE_RECOVERY -#include "src/modules/recovery/tests_exhaustive_impl.h" +#include "modules/recovery/tests_exhaustive_impl.h" #endif #ifdef ENABLE_MODULE_EXTRAKEYS -#include "src/modules/extrakeys/tests_exhaustive_impl.h" +#include "modules/extrakeys/tests_exhaustive_impl.h" #endif #ifdef ENABLE_MODULE_SCHNORRSIG -#include "src/modules/schnorrsig/tests_exhaustive_impl.h" +#include "modules/schnorrsig/tests_exhaustive_impl.h" #endif int main(int argc, char** argv) { diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h index dac86bd77f228..0921e34f1603a 100644 --- a/src/secp256k1/src/util.h +++ b/src/secp256k1/src/util.h @@ -16,6 +16,11 @@ #include #include +#define STR_(x) #x +#define STR(x) STR_(x) +#define DEBUG_CONFIG_MSG(x) "DEBUG_CONFIG: " x +#define DEBUG_CONFIG_DEF(x) DEBUG_CONFIG_MSG(#x "=" STR(x)) + typedef struct { void (*fn)(const char *text, void* data); const void* data; diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 6148edf1150bc..620e3c38b5e8c 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -3,27 +3,33 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include -#include #include #include #include +#include #include +#include #include #include #include #include #include -#include #include +#include #include #include #include +#include +#include #include #include +static constexpr size_t CHACHA20_ROUND_OUTPUT = 64; // bytes; + BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup) template @@ -133,10 +139,11 @@ static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, b static void TestChaCha20(const std::string &hex_message, const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout) { std::vector key = ParseHex(hexkey); + assert(key.size() == 32); std::vector m = ParseHex(hex_message); - ChaCha20 rng(key.data(), key.size()); + ChaCha20 rng(key.data()); rng.SetIV(nonce); - rng.Seek(seek); + rng.Seek64(seek); std::vector out = ParseHex(hexout); std::vector outres; outres.resize(out.size()); @@ -148,17 +155,96 @@ static void TestChaCha20(const std::string &hex_message, const std::string &hexk } else { rng.Keystream(outres.data(), outres.size()); } - BOOST_CHECK(out == outres); + BOOST_CHECK_EQUAL(hexout, HexStr(outres)); if (!hex_message.empty()) { // Manually XOR with the keystream and compare the output rng.SetIV(nonce); - rng.Seek(seek); + rng.Seek64(seek); std::vector only_keystream(outres.size()); rng.Keystream(only_keystream.data(), only_keystream.size()); for (size_t i = 0; i != m.size(); i++) { outres[i] = m[i] ^ only_keystream[i]; } - BOOST_CHECK(out == outres); + BOOST_CHECK_EQUAL(hexout, HexStr(outres)); + } +} + +static void TestFSChaCha20(const std::string& hex_plaintext, const std::string& hexkey, const std::string& hex_rekey_salt, size_t rekey_interval, const std::string& ciphertext_after_rotation) +{ + auto key_vec = ParseHex(hexkey); + BOOST_CHECK_EQUAL(FSCHACHA20_KEYLEN, key_vec.size()); + std::array key; + memcpy(key.data(), key_vec.data(), FSCHACHA20_KEYLEN); + + auto salt_vec = ParseHex(hex_rekey_salt); + BOOST_CHECK_EQUAL(FSCHACHA20_REKEY_SALT_LEN, salt_vec.size()); + std::array rekey_salt; + memcpy(rekey_salt.data(), salt_vec.data(), FSCHACHA20_REKEY_SALT_LEN); + + auto plaintext = ParseHex(hex_plaintext); + + auto fsc20 = FSChaCha20{key, rekey_salt, rekey_interval}; + auto c20 = ChaCha20{reinterpret_cast(key.data())}; + + std::vector fsc20_output; + fsc20_output.resize(plaintext.size()); + + std::vector c20_output; + c20_output.resize(plaintext.size()); + + for (size_t i = 0; i < rekey_interval; i++) { + fsc20.Crypt(MakeByteSpan(plaintext), fsc20_output); + c20.Crypt(plaintext.data(), c20_output.data(), plaintext.size()); + BOOST_CHECK_EQUAL(0, memcmp(c20_output.data(), fsc20_output.data(), plaintext.size())); + } + + // At the rotation interval, the outputs will no longer match + fsc20.Crypt(MakeByteSpan(plaintext), fsc20_output); + c20.Crypt(plaintext.data(), c20_output.data(), plaintext.size()); + BOOST_CHECK(memcmp(c20_output.data(), fsc20_output.data(), plaintext.size()) != 0); + + unsigned char new_key[FSCHACHA20_KEYLEN]; + auto hasher = CSHA256().Write(UCharCast(rekey_salt.data()), rekey_salt.size()); + hasher.Write(UCharCast(key.data()), key.size()).Finalize(new_key); + c20.SetKey32(reinterpret_cast(new_key)); + + std::array new_nonce; + WriteLE32(reinterpret_cast(new_nonce.data()), 0); + WriteLE64(reinterpret_cast(new_nonce.data()) + 4, 1); + c20.SetRFC8439Nonce(new_nonce); + + // Outputs should match again after simulating key rotation + c20.Crypt(plaintext.data(), c20_output.data(), plaintext.size()); + BOOST_CHECK_EQUAL(0, memcmp(c20_output.data(), fsc20_output.data(), plaintext.size())); + + BOOST_CHECK_EQUAL(HexStr(fsc20_output), ciphertext_after_rotation); +} + +static void TestChaCha20RFC8439(const std::string& hex_key, const std::array& nonce, uint32_t seek, const std::string& hex_expected_keystream, const std::string& hex_input, const std::string& hex_expected_output) +{ + auto key = ParseHex(hex_key); + + if (!hex_expected_keystream.empty()) { + ChaCha20 c20(key.data()); + c20.SetRFC8439Nonce(nonce); + c20.SeekRFC8439(seek); + std::vector keystream; + keystream.resize(CHACHA20_ROUND_OUTPUT); + c20.Keystream(keystream.data(), CHACHA20_ROUND_OUTPUT); + BOOST_CHECK_EQUAL(HexStr(keystream).substr(0, hex_expected_keystream.size()), hex_expected_keystream); + } + + if (!hex_input.empty()) { + assert(hex_input.size() == hex_expected_output.size()); + ChaCha20 c20(key.data()); + c20.SetRFC8439Nonce(nonce); + c20.SeekRFC8439(seek); + + auto input = ParseHex(hex_input); + std::vector output; + output.resize(input.size()); + c20.Crypt(input.data(), output.data(), input.size()); + BOOST_CHECK_EQUAL(HexStr(output).substr(0, hex_expected_output.size()), hex_expected_output); } } @@ -460,7 +546,88 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) { BOOST_AUTO_TEST_CASE(chacha20_testvector) { - // Test vector from RFC 7539 + // RFC 7539/8439 A.1 Test Vector #1: + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000000", + 0, 0, + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" + "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); + + // RFC 7539/8439 A.1 Test Vector #2: + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000000", + 0, 1, + "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed" + "29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f"); + + // RFC 7539/8439 A.1 Test Vector #3: + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000001", + 0, 1, + "3aeb5224ecf849929b9d828db1ced4dd832025e8018b8160b82284f3c949aa5a" + "8eca00bbb4a73bdad192b5c42f73f2fd4e273644c8b36125a64addeb006c13a0"); + + // RFC 7539/8439 A.1 Test Vector #4: + TestChaCha20("", + "00ff000000000000000000000000000000000000000000000000000000000000", + 0, 2, + "72d54dfbf12ec44b362692df94137f328fea8da73990265ec1bbbea1ae9af0ca" + "13b25aa26cb4a648cb9b9d1be65b2c0924a66c54d545ec1b7374f4872e99f096"); + + // RFC 7539/8439 A.1 Test Vector #5: + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000000", + 0x200000000000000, 0, + "c2c64d378cd536374ae204b9ef933fcd1a8b2288b3dfa49672ab765b54ee27c7" + "8a970e0e955c14f3a88e741b97c286f75f8fc299e8148362fa198a39531bed6d"); + + // RFC 7539/8439 A.2 Test Vector #1: + TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + 0, 0, + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" + "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); + + // RFC 7539/8439 A.2 Test Vector #2: + TestChaCha20("416e79207375626d697373696f6e20746f20746865204945544620696e74656e" + "6465642062792074686520436f6e7472696275746f7220666f72207075626c69" + "636174696f6e20617320616c6c206f722070617274206f6620616e2049455446" + "20496e7465726e65742d4472616674206f722052464320616e6420616e792073" + "746174656d656e74206d6164652077697468696e2074686520636f6e74657874" + "206f6620616e204945544620616374697669747920697320636f6e7369646572" + "656420616e20224945544620436f6e747269627574696f6e222e205375636820" + "73746174656d656e747320696e636c756465206f72616c2073746174656d656e" + "747320696e20494554462073657373696f6e732c2061732077656c6c20617320" + "7772697474656e20616e6420656c656374726f6e696320636f6d6d756e696361" + "74696f6e73206d61646520617420616e792074696d65206f7220706c6163652c" + "207768696368206172652061646472657373656420746f", + "0000000000000000000000000000000000000000000000000000000000000001", + 0x200000000000000, 1, + "a3fbf07df3fa2fde4f376ca23e82737041605d9f4f4f57bd8cff2c1d4b7955ec" + "2a97948bd3722915c8f3d337f7d370050e9e96d647b7c39f56e031ca5eb6250d" + "4042e02785ececfa4b4bb5e8ead0440e20b6e8db09d881a7c6132f420e527950" + "42bdfa7773d8a9051447b3291ce1411c680465552aa6c405b7764d5e87bea85a" + "d00f8449ed8f72d0d662ab052691ca66424bc86d2df80ea41f43abf937d3259d" + "c4b2d0dfb48a6c9139ddd7f76966e928e635553ba76c5c879d7b35d49eb2e62b" + "0871cdac638939e25e8a1e0ef9d5280fa8ca328b351c3c765989cbcf3daa8b6c" + "cc3aaf9f3979c92b3720fc88dc95ed84a1be059c6499b9fda236e7e818b04b0b" + "c39c1e876b193bfe5569753f88128cc08aaa9b63d1a16f80ef2554d7189c411f" + "5869ca52c5b83fa36ff216b9c1d30062bebcfd2dc5bce0911934fda79a86f6e6" + "98ced759c3ff9b6477338f3da4f9cd8514ea9982ccafb341b2384dd902f3d1ab" + "7ac61dd29c6f21ba5b862f3730e37cfdc4fd806c22f221"); + + // RFC 7539/8439 A.2 Test Vector #3: + TestChaCha20("2754776173206272696c6c69672c20616e642074686520736c6974687920746f" + "7665730a446964206779726520616e642067696d626c6520696e207468652077" + "6162653a0a416c6c206d696d737920776572652074686520626f726f676f7665" + "732c0a416e6420746865206d6f6d65207261746873206f757467726162652e", + "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", + 0x200000000000000, 42, + "62e6347f95ed87a45ffae7426f27a1df5fb69110044c0d73118effa95b01e5cf" + "166d3df2d721caf9b21e5fb14c616871fd84c54f9d65b283196c7fe4f60553eb" + "f39c6402c42234e32a356b3e764312a61a5532055716ead6962568f87d3f3f77" + "04c6a8d1bcd1bf4d50d6154b6da731b187b58dfd728afa36757a797ac188d1"); // test encryption TestChaCha20("4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756" @@ -498,6 +665,82 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector) "a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5" "360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78" "fab78c9"); + + // Test vectors from https://datatracker.ietf.org/doc/html/rfc8439#section-2.4.2 + const auto rfc8439_nonce0 = std::array{std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x00}}; + const auto rfc8439_nonce1 = std::array{std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x4a}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x00}}; + const auto rfc8439_nonce2 = std::array{std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x02}}; + TestChaCha20RFC8439("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + rfc8439_nonce1, 1, "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cba40c63e34cdea212c4cf07d41b7", "", ""); + TestChaCha20RFC8439("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + rfc8439_nonce1, 2, "69a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a832c89c167eacd901d7e2bf363740373201aa188fbbce83991c4ed", "", ""); + + // Test vectors from https://datatracker.ietf.org/doc/html/rfc8439#appendix-A.1 + TestChaCha20RFC8439("0000000000000000000000000000000000000000000000000000000000000000", + rfc8439_nonce0, 0, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586", "", ""); + TestChaCha20RFC8439("0000000000000000000000000000000000000000000000000000000000000000", + rfc8439_nonce0, 1, "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f", "", ""); + TestChaCha20RFC8439("0000000000000000000000000000000000000000000000000000000000000001", + rfc8439_nonce0, 1, "3aeb5224ecf849929b9d828db1ced4dd832025e8018b8160b82284f3c949aa5a8eca00bbb4a73bdad192b5c42f73f2fd4e273644c8b36125a64addeb006c13a0", "", ""); + TestChaCha20RFC8439("00ff000000000000000000000000000000000000000000000000000000000000", + rfc8439_nonce0, 2, "72d54dfbf12ec44b362692df94137f328fea8da73990265ec1bbbea1ae9af0ca13b25aa26cb4a648cb9b9d1be65b2c0924a66c54d545ec1b7374f4872e99f096", "", ""); + TestChaCha20RFC8439("0000000000000000000000000000000000000000000000000000000000000000", + rfc8439_nonce2, 0, "c2c64d378cd536374ae204b9ef933fcd1a8b2288b3dfa49672ab765b54ee27c78a970e0e955c14f3a88e741b97c286f75f8fc299e8148362fa198a39531bed6d", "", ""); + + // Test vectors from https://datatracker.ietf.org/doc/html/rfc8439#appendix-A.2 + TestChaCha20RFC8439("0000000000000000000000000000000000000000000000000000000000000000", + rfc8439_nonce0, 0, "", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); + TestChaCha20RFC8439("0000000000000000000000000000000000000000000000000000000000000001", + rfc8439_nonce2, 1, "", "416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e20224945544620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c2073746174656d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207768696368206172652061646472657373656420746f", "a3fbf07df3fa2fde4f376ca23e82737041605d9f4f4f57bd8cff2c1d4b7955ec2a97948bd3722915c8f3d337f7d370050e9e96d647b7c39f56e031ca5eb6250d4042e02785ececfa4b4bb5e8ead0440e20b6e8db09d881a7c6132f420e52795042bdfa7773d8a9051447b3291ce1411c680465552aa6c405b7764d5e87bea85ad00f8449ed8f72d0d662ab052691ca66424bc86d2df80ea41f43abf937d3259dc4b2d0dfb48a6c9139ddd7f76966e928e635553ba76c5c879d7b35d49eb2e62b0871cdac638939e25e8a1e0ef9d5280fa8ca328b351c3c765989cbcf3daa8b6ccc3aaf9f3979c92b3720fc88dc95ed84a1be059c6499b9fda236e7e818b04b0bc39c1e876b193bfe5569753f88128cc08aaa9b63d1a16f80ef2554d7189c411f5869ca52c5b83fa36ff216b9c1d30062bebcfd2dc5bce0911934fda79a86f6e698ced759c3ff9b6477338f3da4f9cd8514ea9982ccafb341b2384dd902f3d1ab7ac61dd29c6f21ba5b862f3730e37cfdc4fd806c22f221"); + TestChaCha20RFC8439("1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", + rfc8439_nonce2, 42, "", "2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e642067696d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e6420746865206d6f6d65207261746873206f757467726162652e", "62e6347f95ed87a45ffae7426f27a1df5fb69110044c0d73118effa95b01e5cf166d3df2d721caf9b21e5fb14c616871fd84c54f9d65b283196c7fe4f60553ebf39c6402c42234e32a356b3e764312a61a5532055716ead6962568f87d3f3f7704c6a8d1bcd1bf4d50d6154b6da731b187b58dfd728afa36757a797ac188d1"); + + // Test vectors from https://datatracker.ietf.org/doc/html/rfc8439#appendix-A.4 + TestChaCha20RFC8439("0000000000000000000000000000000000000000000000000000000000000000", + rfc8439_nonce0, 0, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7", "", ""); + TestChaCha20RFC8439("0000000000000000000000000000000000000000000000000000000000000001", + rfc8439_nonce2, 0, "ecfa254f845f647473d3cb140da9e87606cb33066c447b87bc2666dde3fbb739", "", ""); + TestChaCha20RFC8439("1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", + rfc8439_nonce2, 0, "965e3bc6f9ec7ed9560808f4d229f94b137ff275ca9b3fcbdd59deaad23310ae", "", ""); + + // Forward secure ChaCha20 + TestFSChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000", 256, + "4cf63894c8507adffa163a742db5fdcc9a861187b1c94a5a4c002d1bb7f2223c"); + TestFSChaCha20("01", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "0000000000000000000000000000000000000000000000", 5, "e0"); + TestFSChaCha20("e93fdb5c762804b9a706816aca31e35b11d2aa3080108ef46a5b1f1508819c0a", + "8ec4c3ccdaea336bdeb245636970be01266509b33f3d2642504eaf412206207a", + "8bb571662db12d38ee4e2630d4434f6f626cb0e6007e3c", 4096, + "30a36b4833331bf83bc16fcff408c771044e239b80472edd2e89ba9eb1845f34"); +} + +BOOST_AUTO_TEST_CASE(chacha20_midblock) +{ + auto key = ParseHex("0000000000000000000000000000000000000000000000000000000000000000"); + ChaCha20 c20{key.data()}; + // get one block of keystream + unsigned char block[64]; + c20.Keystream(block, CHACHA20_ROUND_OUTPUT); + unsigned char b1[5], b2[7], b3[52]; + c20 = ChaCha20{key.data()}; + c20.Keystream(b1, 5); + c20.Keystream(b2, 7); + c20.Keystream(b3, 52); + + BOOST_CHECK_EQUAL(0, memcmp(b1, block, 5)); + BOOST_CHECK_EQUAL(0, memcmp(b2, block + 5, 7)); + BOOST_CHECK_EQUAL(0, memcmp(b3, block + 12, 52)); } BOOST_AUTO_TEST_CASE(poly1305_testvector) @@ -590,129 +833,109 @@ BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests) "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d"); } -static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aad_length, const std::string& hex_m, const std::string& hex_k1, const std::string& hex_k2, const std::string& hex_aad_keystream, const std::string& hex_encrypted_message, const std::string& hex_encrypted_message_seq_999) +static void TestBIP324CipherSuite(const std::string& hex_aad, const std::string& hex_contents, const std::string& hex_key_L, const std::string& hex_key_P, const std::string& hex_rekey_salt, const std::string& hex_expected_output_seq_0, const std::string& hex_expected_output_seq_999) { - // we need two sequence numbers, one for the payload cipher instance... - uint32_t seqnr_payload = 0; - // ... and one for the AAD (length) cipher instance - uint32_t seqnr_aad = 0; - // we need to keep track of the position in the AAD cipher instance - // keystream since we use the same 64byte output 21 times - // (21 times 3 bytes length < 64) - int aad_pos = 0; - - std::vector aead_K_1 = ParseHex(hex_k1); - std::vector aead_K_2 = ParseHex(hex_k2); - std::vector plaintext_buf = ParseHex(hex_m); - std::vector expected_aad_keystream = ParseHex(hex_aad_keystream); - std::vector expected_ciphertext_and_mac = ParseHex(hex_encrypted_message); - std::vector expected_ciphertext_and_mac_sequence999 = ParseHex(hex_encrypted_message_seq_999); - - std::vector ciphertext_buf(plaintext_buf.size() + POLY1305_TAGLEN, 0); - std::vector plaintext_buf_new(plaintext_buf.size(), 0); - std::vector cmp_ctx_buffer(64); + auto key_L_vec = ParseHex(hex_key_L); + BIP324Key key_L; + memcpy(key_L.data(), key_L_vec.data(), BIP324_KEY_LEN); + + auto key_P_vec = ParseHex(hex_key_P); + BIP324Key key_P; + memcpy(key_P.data(), key_P_vec.data(), BIP324_KEY_LEN); + + auto rekey_salt_vec = ParseHex(hex_rekey_salt); + std::array rekey_salt; + memcpy(rekey_salt.data(), rekey_salt_vec.data(), BIP324_REKEY_SALT_LEN); + + auto aad = ParseHex(hex_aad); + + const auto original_contents_bytes = ParseHex(hex_contents); + auto contents_buf = original_contents_bytes; + + std::vector encrypted_pkt(BIP324_LENGTH_FIELD_LEN + BIP324_HEADER_LEN + contents_buf.size() + RFC8439_EXPANSION, 0); + std::vector contents_buf_dec(contents_buf.size(), 0); uint32_t out_len = 0; - // create the AEAD instance - ChaCha20Poly1305AEAD aead(aead_K_1.data(), aead_K_1.size(), aead_K_2.data(), aead_K_2.size()); - - // create a chacha20 instance to compare against - ChaCha20 cmp_ctx(aead_K_1.data(), 32); - - // encipher - bool res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true); - // make sure the operation succeeded if expected to succeed - BOOST_CHECK_EQUAL(res, must_succeed); - if (!res) return; - - // verify ciphertext & mac against the test vector - BOOST_CHECK_EQUAL(expected_ciphertext_and_mac.size(), ciphertext_buf.size()); - BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac.data(), ciphertext_buf.size()) == 0); - - // manually construct the AAD keystream - cmp_ctx.SetIV(seqnr_aad); - cmp_ctx.Seek(0); - cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64); - BOOST_CHECK(memcmp(expected_aad_keystream.data(), cmp_ctx_buffer.data(), expected_aad_keystream.size()) == 0); - // crypt the 3 length bytes and compare the length - uint32_t len_cmp = 0; - len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) | - (ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 | - (ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16; - BOOST_CHECK_EQUAL(len_cmp, expected_aad_length); - - // encrypt / decrypt 1000 packets + BIP324CipherSuite suite_enc(key_L, key_P, rekey_salt); + BIP324CipherSuite suite_dec(key_L, key_P, rekey_salt); + + BIP324HeaderFlags flags{BIP324_NONE}; + std::array encrypted_pkt_len; + + // encrypt / decrypt the packet 1000 times for (size_t i = 0; i < 1000; ++i) { - res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true); - BOOST_CHECK(res); - BOOST_CHECK(aead.GetLength(&out_len, seqnr_aad, aad_pos, ciphertext_buf.data())); - BOOST_CHECK_EQUAL(out_len, expected_aad_length); - res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, plaintext_buf_new.data(), plaintext_buf_new.size(), ciphertext_buf.data(), ciphertext_buf.size(), false); + // encrypt + auto res = suite_enc.Crypt(MakeByteSpan(aad), MakeByteSpan(contents_buf), MakeWritableByteSpan(encrypted_pkt), flags, true); BOOST_CHECK(res); + // verify ciphertext & mac against the test vector + if (i == 0) { + BOOST_CHECK_EQUAL(HexStr(encrypted_pkt), hex_expected_output_seq_0); + } else if (i == 999) { + BOOST_CHECK_EQUAL(HexStr(encrypted_pkt), hex_expected_output_seq_999); + } - // make sure we repetitive get the same plaintext - BOOST_CHECK(memcmp(plaintext_buf.data(), plaintext_buf_new.data(), plaintext_buf.size()) == 0); + memcpy(encrypted_pkt_len.data(), encrypted_pkt.data(), BIP324_LENGTH_FIELD_LEN); + out_len = suite_dec.DecryptLength(encrypted_pkt_len); + BOOST_CHECK_EQUAL(out_len, contents_buf.size()); - // compare sequence number 999 against the test vector - if (seqnr_payload == 999) { - BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac_sequence999.data(), expected_ciphertext_and_mac_sequence999.size()) == 0); - } - // set nonce and block counter, output the keystream - cmp_ctx.SetIV(seqnr_aad); - cmp_ctx.Seek(0); - cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64); - - // crypt the 3 length bytes and compare the length - len_cmp = 0; - len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) | - (ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 | - (ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16; - BOOST_CHECK_EQUAL(len_cmp, expected_aad_length); - - // increment the sequence number(s) - // always increment the payload sequence number - // increment the AAD keystream position by its size (3) - // increment the AAD sequence number if we would hit the 64 byte limit - seqnr_payload++; - aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; - if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { - aad_pos = 0; - seqnr_aad++; + res = suite_dec.Crypt(MakeByteSpan(aad), {reinterpret_cast(encrypted_pkt.data()) + BIP324_LENGTH_FIELD_LEN, encrypted_pkt.size() - BIP324_LENGTH_FIELD_LEN}, MakeWritableByteSpan(contents_buf_dec), flags, false); + BOOST_CHECK(res); + BOOST_CHECK_EQUAL(flags, BIP324_NONE); + + // make sure we always get the same plaintext + BOOST_CHECK_EQUAL(contents_buf_dec.size(), original_contents_bytes.size()); + if (!original_contents_bytes.empty()) { + BOOST_CHECK_EQUAL(0, memcmp(contents_buf_dec.data(), original_contents_bytes.data(), original_contents_bytes.size())); } } } -BOOST_AUTO_TEST_CASE(chacha20_poly1305_aead_testvector) +BOOST_AUTO_TEST_CASE(bip324_cipher_suite_testvectors) { - /* test chacha20poly1305@bitcoin AEAD */ - - // must fail with no message - TestChaCha20Poly1305AEAD(false, 0, - "", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", "", "", ""); - - TestChaCha20Poly1305AEAD(true, 0, - /* m */ "0000000000000000000000000000000000000000000000000000000000000000", - /* k1 (AAD) */ "0000000000000000000000000000000000000000000000000000000000000000", - /* k2 (payload) */ "0000000000000000000000000000000000000000000000000000000000000000", - /* AAD keystream */ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586", - /* encrypted message & MAC */ "76b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32d2fc11829c1b6c1df1f551cd6131ff08", - /* encrypted message & MAC at sequence 999 */ "b0a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3aaa7aa16ec62c5e24f040c08bb20c3598"); - TestChaCha20Poly1305AEAD(true, 1, - "0100000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586", - "77b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32baf0c85b6dff8602b06cf52a6aefc62e", - "b1a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3a8bd94d54b5ecabbc41ffbb0c90924080"); - TestChaCha20Poly1305AEAD(true, 255, - "ff0000f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9", - "ff0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", - "c640c1711e3ee904ac35c57ab9791c8a1c408603a90b77a83b54f6c844cb4b06d94e7fc6c800e165acd66147e80ec45a567f6ce66d05ec0cae679dceeb890017", - "3940c1e92da4582ff6f92a776aeb14d014d384eeb30f660dacf70a14a23fd31e91212701334e2ce1acf5199dc84f4d61ddbe6571bca5af874b4c9226c26e650995d157644e1848b96ed6c2102d5489a050e71d29a5a66ece11de5fb5c9558d54da28fe45b0bc4db4e5b88030bfc4a352b4b7068eccf656bae7ad6a35615315fc7c49d4200388d5eca67c2e822e069336c69b40db67e0f3c81209c50f3216a4b89fb3ae1b984b7851a2ec6f68ab12b101ab120e1ea7313bb93b5a0f71185c7fea017ddb92769861c29dba4fbc432280d5dff21b36d1c4c790128b22699950bb18bf74c448cdfe547d8ed4f657d8005fdc0cd7a050c2d46050a44c4376355858981fbe8b184288276e7a93eabc899c4a", - "f039c6689eaeef0456685200feaab9d54bbd9acde4410a3b6f4321296f4a8ca2604b49727d8892c57e005d799b2a38e85e809f20146e08eec75169691c8d4f54a0d51a1e1c7b381e0474eb02f994be9415ef3ffcbd2343f0601e1f3b172a1d494f838824e4df570f8e3b0c04e27966e36c82abd352d07054ef7bd36b84c63f9369afe7ed79b94f953873006b920c3fa251a771de1b63da927058ade119aa898b8c97e42a606b2f6df1e2d957c22f7593c1e2002f4252f4c9ae4bf773499e5cfcfe14dfc1ede26508953f88553bf4a76a802f6a0068d59295b01503fd9a600067624203e880fdf53933b96e1f4d9eb3f4e363dd8165a278ff667a41ee42b9892b077cefff92b93441f7be74cf10e6cd"); + /* test bip324 cipher suite */ + + // encrypting an empty message should result in 20 bytes: + // 3 bytes of encrypted length, 1 byte header and 16 bytes MAC + TestBIP324CipherSuite(/* aad */ "", + /* plaintext */ "", + /* k_l */ "0000000000000000000000000000000000000000000000000000000000000000", + /* k_p */ "0000000000000000000000000000000000000000000000000000000000000000", + /* rekey_salt */ "0000000000000000000000000000000000000000000000", + /* ciphertext_and_mac_0 */ "76b8e09fbedcfd1809ff3c10adf8277fcc0581b8", + /* ciphertext_and_mac_999 */ "66712b97e33e72c0e908f5a7ce99279cb3cb6769"); + + TestBIP324CipherSuite("", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000", + "56b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed29e7e38bb44c94b6a43c525ffca66c79e9", + "46712b9741ee5bde86518fee0ce0778aa97cf58c1ee3c587ab3dce47de77b25f202b4807e074989c86c4bb8493e76cda937e0aad"); + + TestBIP324CipherSuite("", + "0100000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000", + "56b8e09f06e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed2929449b86c1e4e213676824f2c48e5336", + "46712b9740ee5bde86518fee0ce0778aa97cf58c1ee3c587ab3dce47de77b25f202b48079f2cc4249bd112ea04cccf99a211dfdb"); + + TestBIP324CipherSuite("", + "fc0000f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9", + "ff0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "6f5ef19ed6f1a5e2db2b119494f21d8c2de638a4c6ec3b", + "3940c1184442315c7340b89171039acb48f95287e66e56f7afa7cf00f95044d26fb69d46ac5c16a2d57a1cadc39160644717559e73480734410a3f543c5f231a7d7ed77af2a64681f6a7417283cef85504ac5de9fdea100e6c67ef7a1bfcd888a92a5f1ef2c9074b44b572aa748f29ff61a850ce40470004dff5cc1d926c5abe25ace47a12c5373094a26bab027e008154fb630aa062490b5421e96691a3f79557f7a79e3cfd9100796671ea241703ddf326f113adf1694bbd6e0ca032e16f936e7bfbf174e7ef4af5b53a6a9102e6fa41a8e589290f39a7bc7a6003088c612a43a36c2e9f2e740797ad3a2a1a80e0f67157fb9abc40487077368e94751a266a0b2dac24f0adabd5c6d7ba54316eee951da560", + "bcc270293211abbeac7a26af3f3d200f8ce44de3d3e86cdbf449dcf7fedb7b5a489629deaae0c87471b1331b2fce7aba3dfabf6f1867e1a534cececba0cdc9e6150e92cb145567401f08778eeb646b2a70165061423b30ca21e754d3e0a0db4de59dd74093b0fc0fc78a598d522571525ab172592620f770b3303c65ee4a35504e4991e8f1d8904c9679824140642c70a184b4449d1ffdf11b8bee4e831a5b3d986006f5119a0912bacb939886abcb279be2437ecbf1f56528ef397f6459f0fd895031c7a8a2a815a3e68199dc1a9b0c7fef3df72c470f9e8e5524049e7e712da407a6b8ab9a3c0a4ae40cc187952b1062e646b8aebc2808a381530791e46b7220a1af222022952872decc6ad5fad2a7b242a7"); + + // Repeat test with non-empty aad - only mac tags (last 16 bytes) in the expected outputs change + TestBIP324CipherSuite("c6d7bc3a5079ae98fec7094bdfb42aac61d3ba64af179d672c7c33fd4a139647", + "fc0000f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9", + "ff0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "6f5ef19ed6f1a5e2db2b119494f21d8c2de638a4c6ec3b5b4d43f3196152ea10", + "3940c1184442315c7340b89171039acb48f95287e66e56f7afa7cf00f95044d26fb69d46ac5c16a2d57a1cadc39160644717559e73480734410a3f543c5f231a7d7ed77af2a64681f6a7417283cef85504ac5de9fdea100e6c67ef7a1bfcd888a92a5f1ef2c9074b44b572aa748f29ff61a850ce40470004dff5cc1d926c5abe25ace47a12c5373094a26bab027e008154fb630aa062490b5421e96691a3f79557f7a79e3cfd9100796671ea241703ddf326f113adf1694bbd6e0ca032e16f936e7bfbf174e7ef4af5b53a6a9102e6fa41a8e589290f39a7bc7a6003088c612a43a36c2e9f2e740797ad3a2a1a80e0f67157fb9abc40487077368e94751a266a0b2dac4d382097b958da569f3b6fae3faaaaf2", + "bcc270293211abbeac7a26af3f3d200f8ce44de3d3e86cdbf449dcf7fedb7b5a489629deaae0c87471b1331b2fce7aba3dfabf6f1867e1a534cececba0cdc9e6150e92cb145567401f08778eeb646b2a70165061423b30ca21e754d3e0a0db4de59dd74093b0fc0fc78a598d522571525ab172592620f770b3303c65ee4a35504e4991e8f1d8904c9679824140642c70a184b4449d1ffdf11b8bee4e831a5b3d986006f5119a0912bacb939886abcb279be2437ecbf1f56528ef397f6459f0fd895031c7a8a2a815a3e68199dc1a9b0c7fef3df72c470f9e8e5524049e7e712da407a6b8ab9a3c0a4ae40cc187952b1062e646b8aebc2808a381530791e46b7220a1afd31f9f544f9ae60720005dca1ded9ac6"); } BOOST_AUTO_TEST_CASE(countbits_tests) @@ -947,4 +1170,42 @@ BOOST_AUTO_TEST_CASE(muhash_tests) BOOST_CHECK_EQUAL(HexStr(out4), "3a31e6903aff0de9f62f9a9f7f8b861de76ce2cda09822b90014319ae5dc2271"); } +static void TestRFC8439AEAD(const std::string& hex_aad, const std::string& hex_key, const std::string& hex_nonce, const std::string& hex_plaintext, const std::string& hex_expected_ciphertext, const std::string& hex_expected_auth_tag) +{ + auto aad = ParseHex(hex_aad); + auto key = ParseHex(hex_key); + auto nonce = ParseHex(hex_nonce); + std::array nonce_arr; + memcpy(nonce_arr.data(), nonce.data(), 12); + auto plaintext = ParseHex(hex_plaintext); + std::vector output(plaintext.size() + POLY1305_TAGLEN, std::byte{0x00}); + RFC8439Encrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, MakeByteSpan(plaintext), output); + + BOOST_CHECK_EQUAL(HexStr({output.data(), output.size() - POLY1305_TAGLEN}), hex_expected_ciphertext); + BOOST_CHECK_EQUAL(HexStr({output.data() + output.size() - POLY1305_TAGLEN, POLY1305_TAGLEN}), hex_expected_auth_tag); + + std::vector decrypted_plaintext(plaintext.size(), std::byte{0x00}); + auto authenticated = RFC8439Decrypt(MakeByteSpan(aad), MakeByteSpan(key), nonce_arr, output, decrypted_plaintext); + BOOST_CHECK(authenticated); + BOOST_CHECK_EQUAL(0, memcmp(decrypted_plaintext.data(), plaintext.data(), plaintext.size())); +} + +BOOST_AUTO_TEST_CASE(rfc8439_tests) +{ + // Test vector from https://datatracker.ietf.org/doc/html/rfc8439#section-2.8.2 + TestRFC8439AEAD("50515253c0c1c2c3c4c5c6c7", + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "070000004041424344454647", + "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e", + "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116", + "1ae10b594f09e26a7e902ecbd0600691"); + + // Test vector from https://datatracker.ietf.org/doc/html/rfc8439#appendix-A.5 + TestRFC8439AEAD("f33388860000000000004e91", + "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", + "000000000102030405060708", + "496e7465726e65742d4472616674732061726520647261667420646f63756d656e74732076616c696420666f722061206d6178696d756d206f6620736978206d6f6e74687320616e64206d617920626520757064617465642c207265706c616365642c206f72206f62736f6c65746564206279206f7468657220646f63756d656e747320617420616e792074696d652e20497420697320696e617070726f70726961746520746f2075736520496e7465726e65742d447261667473206173207265666572656e6365206d6174657269616c206f7220746f2063697465207468656d206f74686572207468616e206173202fe2809c776f726b20696e2070726f67726573732e2fe2809d", + "64a0861575861af460f062c79be643bd5e805cfd345cf389f108670ac76c8cb24c6cfc18755d43eea09ee94e382d26b0bdb7b73c321b0100d4f03b7f355894cf332f830e710b97ce98c8a84abd0b948114ad176e008d33bd60f982b1ff37c8559797a06ef4f0ef61c186324e2b3506383606907b6a7c02b0f9f6157b53c867e4b9166c767b804d46a59b5216cde7a4e99040c5a40433225ee282a1b0a06c523eaf4534d7f83fa1155b0047718cbc546a0d072b04b3564eea1b422273f548271a0bb2316053fa76991955ebd63159434ecebb4e466dae5a1073a6727627097a1049e617d91d361094fa68f0ff77987130305beaba2eda04df997b714d6c6f2c29a6ad5cb4022b02709b", + "eead9d67890cbb22392336fea1851f38"); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/data/bip324_vectors.json b/src/test/data/bip324_vectors.json new file mode 100644 index 0000000000000..e3ad4c1aa38ec --- /dev/null +++ b/src/test/data/bip324_vectors.json @@ -0,0 +1,170 @@ +[ + { + "in_idx": 1, + "in_priv_ours": "e79f04ad4c684525ff3e3cf7c19f4cdb11193d387d7f3fe82a948a0c3165f5fe", + "in_ellswift_ours": "4eff53ea1945e57b5d170565e760d771e5d496101c1005c302fd687af80d8b858c5a681c474fc4c0546ee464a1b95899ecb9d4f50c2c0854fe029fc054ae6777", + "in_ellswift_theirs": "824a1da0530fa695b95b375fee2a56ec96ce375ddd2fcf8367cc8ec1c0b751a3304bbef5dc96543acaac50c24be8cb39906ef8521727de0c6e96c2060a026bd7", + "in_initiating": 1, + "in_contents": "1f", + "in_multiply": 1, + "in_aad": "", + "in_ignore": 0, + "mid_x_ours": "b63341693587b4944b865485cffcc34707b3e0760a6ba8d1d402fc1c996c4e91", + "mid_x_shared": "961fa6c9491bde887a6e67898c9335579d1931b435af16d969f3c9f12e58c698", + "mid_shared_secret": "05ac09c882b2cddd57fd4a1a14f93ae78d78a2ab2adb8cb6d67176cdb3101e15", + "mid_initiator_l": "c845747c92a2f0d83058ccceee69322cfd84cf3b086c30790f2a8789b303defc", + "mid_initiator_p": "3d0c55fa080de10236da830c7b092f63f6897c5243259adcedc4288a847ee9bc", + "mid_responder_l": "f32949925481a88ad5246c408aeb8b9c0dc5dbc21fd6e5484331e1c0cd0ef6d6", + "mid_responder_p": "5b94f3fce2b276e9d7ef5646657e61dbbc8ea27a9bf531ce23c553de1fdfd27d", + "mid_rekey_salt": "0e8a665616c8828d9cb702d4ed1178b7e3958704909abd", + "mid_send_garbage_terminator": "38d7b65a6d58a08b2506e799caacd4f6", + "mid_recv_garbage_terminator": "d6156094782d98fa27f37758dcd0677f", + "mid_session_id": "4418cf03c0d7cd5849022a7976014f234b4d4f5c18a6d3a6540c11e01a7180c1", + "out_ciphertext": "d88ea86cdd6b61592e038741b474eeef0de0802412", + "out_ciphertext_endswith": "" + }, + { + "in_idx": 999, + "in_priv_ours": "53a915e6d8f6c5bbb93e537081085e9e642dc525649acf05be74e3a825e20921", + "in_ellswift_ours": "715d7b8357dd4d15a07b3f8c6764ebd9927140f75047fb50478c33ea8889d710511cdf39aa0fd6b1e94ea660d87f9d97bffc548646f9e2aaebc1468fe563fb97", + "in_ellswift_theirs": "cea7abc3112397b894bd3e89a615248af473f897bbdd30997c20e53e7ce499a295e55c4c5655cd3d21191e76c8af0aa1bbea10fd1b540a42d0fbed388dc59877", + "in_initiating": 0, + "in_contents": "e4", + "in_multiply": 1, + "in_aad": "", + "in_ignore": 0, + "mid_x_ours": "533bbaefc711f303be5e8271fdecfd31d03abd42128ff73479e2a427059e4a74", + "mid_x_shared": "ed679745127d32714ff84bcce7651a14b9f275bf4b18bc8e880ff51cd7ac012b", + "mid_shared_secret": "08cbe6a86ff92a9757b2aeea92b8b2f4d78426786a2caec99c3cc73d7dda47ab", + "mid_initiator_l": "953a372f0d3933afef342fec533fa4fed644cc1cbc9252d31883e10fbca9b81a", + "mid_initiator_p": "e8c1759d865d59f37e5cf0404296fb0530f0fc601ad783853791e5459ca7f135", + "mid_responder_l": "52925c95091e7dac8e580e9a0548e18915e01919a416c5aac22096b8bab17c31", + "mid_responder_p": "ba778684047d705839590c466daef7c0ac53a3f8790b9744f154712e5644c647", + "mid_rekey_salt": "b0d15cd56a8cd5dddad169093a5eef58083bebf28e3921", + "mid_send_garbage_terminator": "0442c66254045c5018d828d42afaa69f", + "mid_recv_garbage_terminator": "930c6d7cbf8c7c2006ed287c2ba6d941", + "mid_session_id": "9619dd4731d6160617da82da407c80732455a67b59ad7be07f86252aadb79440", + "out_ciphertext": "543ab974d50bf7a122cc118b546830f54e18f838b4", + "out_ciphertext_endswith": "" + }, + { + "in_idx": 0, + "in_priv_ours": "dcf45714d450a32cdd05997b75acd34e8786969dfcff34295c287a13ba3225f0", + "in_ellswift_ours": "feaa1ef8bb05296a8d5153855357868835fc5bc59d96dcbccb108e0e2dd671676c5683ad566ee4290ede5236ba6259ab47668e25853d64c0678641d27d5d0eed", + "in_ellswift_theirs": "c8067851ccc5df7331e9f137e66ebce1220306c4644266ff3e38436a1036ea36582dac8ce2c30de59a890f81611f866f659c1334bdd82cce1ea20ab0128b7f56", + "in_initiating": 1, + "in_contents": "d3ed40688778a439928b38f1f67e04afb94843625ce1932590fe0606fcabffad25df7d2476214dc1d06aa368a4dc1e6d940a974307836e291c54f6948623ce", + "in_multiply": 1, + "in_aad": "3c208efa88a545dbd29d71bbf41268fe3d123eac7e20e92be7f227b93166ee3cfd2f26639b27f20513ec5fe25c4e762096b3e49c29110a5b0d170eb9292e1ecdd3d3bab3660f9970811330d6a0f7112709666bb9d3443c9f35a84e7bde9bf2b5e29b903c64e86aca86bb5799fcf6a7416f48d98668749852c49e4cb2f3325c44d63995cea48af98bb42bcf4fbe03681bb552cf5d51b424bd53f1ffb6d6cb70af606debbe07124aeb14b28f2b26e63f3421c6ac512d5cdb7f97540f2ae64487d84dc57fb8c97f5785b60bcf12a22225988007c4dcc8895301a6e36c59f3a33ab5fa099945b1dd4d6e0a3822ffea087b01f9fb27b38c7711211c523b0e153422a5e06c8200ed45560ed4eb1317b7b730b5d31096bd3ac5cfc1e876a178851d297bec38b7a753013d7590533b5f6d654add3b38ca046d932d9739a5f0fbe6cda89cc97597a046d08b66fde7533fcb0528562960832f623dadbbe35d2d54ff15e1a9a260477169e2d8a95ec66e6ed8ccc2366f5ded2fddc2b351d6475c4b3831b6dd5c330917ea1b0b229ce719b5bd6d839861e5374f51a176b500e7546a2b4b1325be9615305d591605783279a73f28b9a281b17a45bb79029a30bf3c0b248945f4523979bbb57d69df4881d8030a4146080dbf13255b712919fb4fa016c66dd323d7a5c56b3ceeb57c4120fd554719a3851b9f3e810c4ad35b141ebff6b7c7038fdfa784681f20dcf99773721d4ec07e58a312d638fa7a90c1afbb282f30ea74d7002288441c93263eebb593f2b36cf795a8a0ee7cc119d28caa13332fdb6f2f5a6011e26e1f33941fa275d2d75af014c7739b9a999f018c7c96b854332225cb9104c016f94ff255191ab93df7c1ac29e566d672e8ccb1a4b1f53f201d583da6c410df98fc502f3b366d7624cd812cb9dedce92329b6e591366bb83c7b84c1fb30301f0199ee9ec727a33d3a9a0382af1fcf1f42e945a5acf1ea8db30a03856b8d4e0361c08028bcfc05c5204fd508e67b9924600526a1f297efd091e3a0bdc6de5a46229792069c08af9b7961ec890a3ecd6af6b08ae08f3892084a8224dd5ac50b99a7034ba48b6f599ab3e3f9fe30734229558694b8e3091161f8bd425cc2e63635c1ff82e6b6b6c89d3ea4a0dc4193b80e6014f87571bc8499107576a910f0aa50d471fb35fa07dfd0488a404ef9907ee767b1f3786415561c445e0487feed348da527bd5e8adad8de928159fb32cfac4430f51dcf710507baedf326597d8039cbecd07e9cde781d0e307d589d52b5ac262a69b366541efc78e9398dc69345f00004268c9c6ae079f09aa4e4afb830ae35715623451ee2520ef201cb5885030aec9a24d9968f8c6f25d234c69cb2f569ae2a91029b6401110fed798b2289de1e7e8621dcbe466a2fef44f09176e252dae6707ecc35695a156f6b97ab4cbaabd325fd8eb941248490824f2520f49aae6eaab95a1cf92c061c57c90910873f1866b8763b78f90b2b737020527ad16c33938ab438f0eb7845a034aec20253e71ab717c4243dbcc084f4b09a87ceeb8ef124755ac8802a66b02cf9396a700a661e16b418cf838ea1262935bb7bdeec1648f0cacb1c3236ec1856d93f172c5a9a938246606db326336425c27e2adfa428f347b9f7aa4cf1648dd4e7e66f5e8a858d499c95dc2092a0a9c8b80a05c93bd359ba5b1acc714be5a1c26be118abe8abafce45d0b9bb668a8204fccf166bb85ce17f70330c0c8ca8dc7394beee22a52624558b69b81e223c779447ee09d822dc44226432a0c86f9b25e7d85a98e187d879a4ddeeb2463d12bce3eb137e227d41e2a3df55dad1219cf18129dd23ad50930286b732037cbe7d474d06ae9f7d93f7285d2db44a8e72011460e3d34381bfe6d3221f49915312d00a5a2c7ef98ab829c995081abf9dc5551f472f34f9b39607b8e55bdb047e68582d27d2582afb9a07549c7a134a34f7d0e5a1a046cb2c3959feb98fe7a698291a2defa5fbb32473b9c9ed02d02284559325e12cda1bf0bc24ea9849fc4c87eaed71f2537242b8635fdc969872b1017d218943647fca47f9430af3b78ae2c39882118612c288d474b0298de3c3ec1efd23c65554f6d6813b4ccbf604250d295f795b679fe0ecceef5b4739eb6b418376d2fd47f95685a259cd2e2e7c00c18e93329a80a467a2a0aeb22e361732c943beb0c3dc7ca57699b690d26e6314d0b28082f8f26894c249d1587b8895dd1c5e8863bbe31cdebbce06b1d6aa17f64a125f4cbad47f2e02761259973ea164b22a580d74413d900023f03d2b2587bca60501f639050262b61a67bec83246d6ec7726f5a8a62d99c582a159db6a539ae88732f8d101d324550c9fa2dd9ba60c449d329cba823ec4b3d80d613d8df5256b3ce6ffec42ab23e5e27c001aec29d080fb519a6cce5b974f65375870c22577a2f6e76760870de047ff65dca90bfd6a303b4605b6f90631d57a8f66969ec7b96749c1f1a4ee70e8915396e114100fde651700416239d82ce7e32be5f20e5886510a37b3cd3eefe78c40ccb0425f03bcaab1aa9eeffa2f4681b7938a813f49f10a34c280de09345ffe00af340a451ebdae630ddade51a6510fdda44c3fcacfaf3f2f748d8bfe48ff71871eaec9b1790df27d4ae913d1ad16b0b33a04266540a49cb5acb8d2e16df7a35933bba7c5af1ac1f522419e76c58cddee219cf5d3229d5a27af4411ae890b4329ae5699820766b0d73841baddc11c24802684c7f7c738bf4843595dd508850b69dbd38671a385ea39188a04e7b489daabf860f4747a7172cb72a578b9903ce161e7b8a7f31ea7e8630f97571bae93f51186b94cf3c697839a6dcb2f53de9893e53a7bc2f94c9a33b2dd112cdae81f60019b7c20aa7c4921f72492329afb8841a4c80a42d1a12fcc09cf7094cadf6a48ffd6f200bd64ec17906cf4ceda6f10313bdc9ce23eba2d8d8bdba728fac90321b4893a0a61e443f611ce16486aa477ed59ccd27b58bf01a5d6d396ffa249c0728eaf87f1c41dd56c7cbc1491b369aec1a576e286fceb37746891aa37da87fa77db443bbc2df63c589f928532c6e8f4e6e3186441dca4c7118d1bfdd0a1e24087837d7dabff8d64f7df1d4cd1628eb312d6c631f092ac0172642de2f2c3c1a71573de41d2165b8f3164db59070593d8c16dd6c161303a6523a33da328fd88929e434da4d102c0ef7303eafeb6a0c3583f74bc277ae1fac36efe68b98af3340a766b0ddb61e268f3dfdc6d858a90aab491e00e010ef195bbe12435a6f225395a1c3d0ba05a094cb2c6610353fb5bdda198c6c6a1564ddf21ab51b2e13e735969d109acf4ff05ac0071d018e98b42b18692ff2dff6bb1fdc6aa630d2a978b652fa6944fbf693a251db943e10cdabfd7e26276ef16844024432286400d36d15c6acf43cb6247c6316b0729ad2a465ed215a97c937e9f9a405ae9f520d91a198cd18077e7d6134f268741b73c6ad16d1e2164102e8bd276237599ff5811837faa0b1b4ac6b61092c14da64ee83904d62c57d336b80f4476f5a9fa6f0ce6ba6b34454906bf5af60b5e42170bc358bb90927c20f753b8d69d80e5aff23daee4727bdf16e01cb549783816b9a066ade19c26bf2e30aad9f3faf35c5e17c81062f53fdb9f57c2608f6c638f0f2e8c071581107bba371eb0cfec67bd823c3d892d21d14e65b366343839bddb2b7da0d7af7e7b3b927c4d71441ecbf14f5276d0da11cf33723a10fbf1e5a97b1f95072de9872932d7bfc200080043cec2dbf2f0840be85933d8f0cb6862316d1dc18292b90b8a583df01464fe1d6a4720ea361ff2280398df7008023f364989692c86bd2ceb94e466febe2fae6bb2635c1cfcdde45c4d3b6ff55106a432a64e6827f52cc0a25f4581a058c962e99ee9b3ca2c10e32eb93729690b3066d9b9343be9863faaa0599b99daddb2e53704002cc3dc09d5df8f3bb783b37e3ef85f01b272d84073576352428720ee08b9bc527020413346c0bb7f7040fd9b149197bdc4988eebc4e012e73d75bc4f4d2d2a3cc30dae96836c590e5a5976f0392102e1c592da1fd373be252fc3b0d34a6dec22725dd697ac55d4d50e07ccba88f70a44b75abb5dfececacb1b5483760e5d40b84255455fdb93faecbc24ae3b30d73311d6afd5fbad5b6e61e64530e7645cc43adc3053a40d6fc0e63d6c664df163f197d2d3efbb5a5abcf4a7ad69d07bb866876fc22d9c7a2fe529260153283cdbe1fc5768d625ad09a9bdb60442a744d22a827264d2722baae4549097f745bcf0de7f21c80951cc5ed8a48e4099663dbf6ac1f995e7ab3d6c8fe2c3e817e13b26741f2a7f33bad56bb002ebf0c69282dc763e1e9cb44bc0a0814170116d2dc16fdf1e08158fc571c5a5285242ab6376f9c213953e3a0a315eff722831cc27e426b0fe299fb325933ba9631be00ffd51f7bb3d13d962f5b6a0a01ad0c60455993699b9ec594cb749c82c870dc443c7466db518e4a5ebc7c530539ae48bea63d77c0e175d673edff001511bbdbddf30f2c231cba0565f043aa52c584fc05069728a95e040893d4e9c6c8d3b7bae906be38b9e1480f8e4665f157f4b347be01d1c355c94576b32a335864f6cd341857a696fe5f8af10a4ae87246cd119d33205bb969009655e36db394f750382fd58648aee80179bb3f1df7f291b4de27edf056c3968aca546e0e81d131b3fb52ae8b3fdedf297289420c4aa1aa74560aae150f623e120b08592c0304dbf7ff2e432e737227ac1e7e9e65485b30d058b30f81807a18fba48822baaf2e7ec6c88a604495a000e62340273db9079c2a7123a8a197b63d9616e7e9ebef572ba3efec09fb038a57adceffeffcbbb6c4e0e9bd73c1d1e82d26d39bda2420a45fb2463218b2988b5fbd316b3f4e81e7b0728d700637333fe5448b395e384169c4bfc57ff6062c962ac4f13e66cd5b6166a7a049b6459291d2d2a632d749060d8070b9e2db3159db8047e8024bc12c92562c7b4ee953b426c551d1b9064551833c758683693a57e724bed8a5c05e55d8e5f27d22b4adc246116747f23cc8ec35b51148168cd27dfb8a466cb8f4b1980209735694e411184875da7254a2ff850a73377c1f9f0b87f808b76a9f0882231fcc4446ed01c9876edf92fc505040583d9ccd0fcaf3935f215dd45022a9464835a505022cdd54de634fe207b5e202f7bcad463e00312952d3d4b421cc77be0cac587426f0239b8d3d94c0c24141b10cc8ab33aa655388fa5a323016e7a1fe2470d4dad92cb19acf779ce1c48f64bf588c99a766010252b983edcd4725ff16c15683bd7be62323c39405358358fcff306182a3a147c025dc91801258f4d71e1888b0a19f4e52a8c5885cc99474e94448c0081d1ce2a7110e1a6c7e23184cd76a44c806b0f1ea3de321cfdf8569dd35221e9ab1ccbcf6dee81cc611d0731226228d71e98abf05a0db761bf6807d4c0400a39835f33b144f85ddcdee7945e63027d3cd25d44b7e9fdda2548284497af9b57dc72b3e026d49e86950b434fc9a9713f5be858500b54614d5d4c79186289d744fa0df3d655f9fb79ec60f49dc72eea538515b53f74c578f9d4ffc9e79063e19f3a38d9dde42f7c3910ac5ca16f60afefcccd4a49b1d93032b7e82189caebd2993991f7f4f1610d3b7cd4b8dbf895523b28d0a3e672232412145649b1a28570d9c114a851f10e6761f52ade202167d94fb69135f3d2932849a85267b998a23f6c6229697e0098097cb6a6dc4f84cff4c7da997d5631b694", + "in_ignore": 0, + "mid_x_ours": "d23f19ad26c7ab984c682c5b1475f5a7df0452a05dd29a164974d2027a6e488a", + "mid_x_shared": "6f8420ebf786ee13dcf65855b11428db43061cb599e6fe9ed79e59d9f273dc44", + "mid_shared_secret": "33a1ef8464def100bf031dae882ae4b7837b481f6adbc63c44baa8ef61c102eb", + "mid_initiator_l": "8edefd89c5ddb77db6333981740b83fd4d9c246639e0dcfd1ea2a26d4a26f508", + "mid_initiator_p": "6b0aae6192e7a92c7a3b5de8ab1349d26637b17e500da162cf25df13c85d35df", + "mid_responder_l": "87a8da66207605720c64d5267b5ce90becb01248a60808a921c5eff59013d59f", + "mid_responder_p": "e9292dc44be3294db04cf689a3141bbe0d0dbaca2dd8d8fb606a45b8d7d8e089", + "mid_rekey_salt": "b6194760814976e42e00d7cb8e52634b32ec48c6493c18", + "mid_send_garbage_terminator": "57b8c1343aea943714ff848970b2a89b", + "mid_recv_garbage_terminator": "ce7988ab7ce5c93b89328ff815ae803b", + "mid_session_id": "a306c86bea9e6befa7f72bd5cde761d5f4f86cfbc8749c1e844100c540a6cefc", + "out_ciphertext": "a844ddeb465f302c4b408a6ab972d4ff4035109bc25fb8fe88d4892bccefc238cf93e5a61459f9c0cb4877cb03967d592c5d8dc6d4c4b5f489005a645b2f7a2ee368749f0b172b9ddbcaa76f20485a272b2347", + "out_ciphertext_endswith": "" + }, + { + "in_idx": 255, + "in_priv_ours": "f360308206fb441c7713b1a24ba4096a2fa0d5401411198b7008f82ae85257fe", + "in_ellswift_ours": "3d4aa6a71a8a734e014b4d9e6b62989f022c1e2238ae476b11465cb86fe8cf7243db6f9472e188590705cf9b35121e06e57cb106a6043ab147b7c73871da8222", + "in_ellswift_theirs": "f0ebc3c872339622fbf2ab1b8aa2117c4d752b64eac9203962c03a47497a89c0dd298348e5bb63d3433c48ed6da6bc2e2dbe78f7b91247dd29f1f51a858d464a", + "in_initiating": 0, + "in_contents": "b766200432932708a77dd15337bf70ca58397907a1cd31ecd1b43fab27fa0de0e85d5c4dea99543e79a75b149d325044934efccf679a764f683b2fbcb7c6dc4baea9797554779aff937f5195c172892c38dc423bc809885e5a19d07733e7c5f5806ffb865074cf2c46fbb5be4f9b5833c6829b8217ccb301dad7cb49e4fbcad1", + "in_multiply": 1, + "in_aad": "", + "in_ignore": 1, + "mid_x_ours": "29bf8fe2cbd12835d3e6652840b8b1a2acf99d7b44610e738aafd7fd207c9b25", + "mid_x_shared": "8f088a98122e3ffae5fee255fbfdf653a1f830f85dfe595707a1f4a7f74dab76", + "mid_shared_secret": "07eff272cd331aeb14db342b82f6df98303b01471f4dbe71028b127577216d9b", + "mid_initiator_l": "74ee2a5f9221cb386e587ac9a0c724bc6f65ea04cf3b0686c10b3e39e66bca02", + "mid_initiator_p": "35d93b912c54f3d28f834ee96a787f76f84b543fc231a45242fa4953b89cfc9f", + "mid_responder_l": "70c4abb811bb6c209c6f108be383f67c8a14cd2dd25815f6156e6c3edd7d1e3d", + "mid_responder_p": "3a16c6f3ff6f2b735b89d2af77c94b9dd328f5330e1283627835d32c5c856446", + "mid_rekey_salt": "77db9b59b22f86b1687c45fc5d7582bcab7d3d373a9ec7", + "mid_send_garbage_terminator": "e09f7df050a3f76bc8d5a21697fefb9b", + "mid_recv_garbage_terminator": "b8951fd528743d2e8625de363d9ae0e7", + "mid_session_id": "b1d1e3ae7d9337f31a410986fe6e377726eb0f2334f24d66b38ec4d29529d1a5", + "out_ciphertext": "8a4af8b26e56cb461e021551a73f5a8179c0b28181ed1d18fa911358462c4667e51225d18cf4a1d8d73601982d03422065ee781ada48eb4c72ab31dc3a4ab451f8bf2e82b4ab3c4ea3f946b3bb351e3ae9723e35192c59c3a66477ecc9bff100b180a49ce21aed6f0b7c99ae57de2827de5389c93f549f6d9fbb2fa0e62a3a2cd1309b9f05fbb3b8c3fa5a383166b3ae57079432", + "out_ciphertext_endswith": "" + }, + { + "in_idx": 512, + "in_priv_ours": "073f53403256e2b699e32cd4cebadfab63d9076a7cd541d9656e27b1baff66bb", + "in_ellswift_ours": "5c9c90c4e216f8a4c2642535d1f699364f9054ca01bca0239fa65bcaf0ec0278e5a36bc85eae96850feff889b66c9106c170fe655f973f533ee3557bd3cb6e64", + "in_ellswift_theirs": "e9221c9698cb69b63c5a4e224132389d346fd2f0f3262218301be51687b8de846c652ea8aa7425a5c08a7bd52078168d28ecf42196e33e155e215553ce6aee83", + "in_initiating": 1, + "in_contents": "196391ebfe9af3a9186d82692a00e485d6653c8408050e2efbfd864642860022af68053ee4230f69ea565fee775778bc231307b5b534545878f864364bb2690acc9e8991fed5a777eb2850c3c54cca94a5bc2cb92729bcd4e27c57ebddbdf14974030115bc5a8a600b207a4884f568d6a9d3d9367977410577674601d1cc5f1f672f4e54d4944e1f7807416159eb5ece5b805c8a23e6735351a8cf8de654d87228ba21baf815190ef026b29612498121fe85480d9e0050d4781fa9f0e60cab5195", + "in_multiply": 1, + "in_aad": "", + "in_ignore": 0, + "mid_x_ours": "0ce4860107efd6deb9e8fc71cfc2522b4a5a0dc171746fb995ce485570449ab5", + "mid_x_shared": "d87966cc3cbdcc14ce06a1ef6c28125cec3136647a499d4b4465c925a5f85efd", + "mid_shared_secret": "88cf0eb49ee4b7b46cd39fb3f4126b285750d897fdcba72b08d9da351167e227", + "mid_initiator_l": "df6857d7fbb822069d3f284973cdc6677a539d81844247ca2ac174b818623550", + "mid_initiator_p": "4f1c0b4b834681298ef127067369747074e571c68fc69966f3ad20673f786b82", + "mid_responder_l": "5b9aa4239daed582b04d316f0550fdf1efc5309544349b01d62ea4f045e3425f", + "mid_responder_p": "0c1d30189bf24a5032a93add5335fcbf351b2071870fd1626b66e23be99e1df5", + "mid_rekey_salt": "02a476dddbdee8e475639f3c668c156c5c72a807c324e2", + "mid_send_garbage_terminator": "ece7e13c65def883efc1f0dac6d55974", + "mid_recv_garbage_terminator": "efca4cefee7a95602ebab090875e4212", + "mid_session_id": "afde967955012c17bfa7bea218bff3aa1aef10a49139c66080e9e7c567706409", + "out_ciphertext": "7d674fcca8ff3a3146bda19d2404e2411b8d724ca775c365b0956819264c95553526befed63ab5d6cbcbe81c15a6fddd09eb11e410846477635f47f1d78e842ad4b5e14f9ce40ddaaac3cba343dc9422b671d3beb084df04d2814a6ee387bb07c48ed5f06e76114430c9557b9dba9832353c15560587485dd61800d19af1c3593b2d1dff044ac2b72363d595f755b9fdfc5f00fed12ec4a87b1555c06fa8038c6dd3acc748584b2af591ff6789af43d31cb434b6e1396ff49d8d029194b7ae6167d397158551c0768b31a1e12b3c01bb97f9470ad1", + "out_ciphertext_endswith": "" + }, + { + "in_idx": 769, + "in_priv_ours": "810159fc6708a85aa9d6c8e7c4f8c587056594910174c0309b2c323055efaa6a", + "in_ellswift_ours": "2a875927b98fd441d8c469f6902c167eb440e52d3331a4b7406f21cad216e80917401ddb8795a9706fe42a7ddfca70fee7240b1e918b9f32414b8b26e611d571", + "in_ellswift_theirs": "758bcf0381c0a1c35a174503a43b08f79eb407aa61eb9e6579a62400d4a13f8d3cef884728c34cb79c7f088b4d4e735ce796361e71bd8b66d637f56326b49af3", + "in_initiating": 0, + "in_contents": "0f492bc40bc635ca20a46c6fd97b7de536f8326e677e0dcf9ed91e4c7873ec903198010f7cf0ff1595", + "in_multiply": 97561, + "in_aad": "", + "in_ignore": 0, + "mid_x_ours": "27cdbeb6c8ef5f97982a7af00018a16ba243176fe6bb5d5c30c8a466a0193a47", + "mid_x_shared": "85bbb016a00056ffd3878b2a5cfac4352d581bfec5a0407f0733585513d4b463", + "mid_shared_secret": "17624a0ed455d13430bf27337e27a1d55402cb1c7ebb32a4a66b3ef2f9947f1d", + "mid_initiator_l": "aeb3befd6841f8ec867e154424b5c6d49ad3fc1f789e7d3048750302c5d89c09", + "mid_initiator_p": "80033fb9014f1a407e46d35cf36a7c4245e55e6a810abaaad88ec5d25f89443f", + "mid_responder_l": "65b0f93ac2a34327a4aacf14c2c54d92210294b425c3b6bb4ced8c44dad80457", + "mid_responder_p": "4893a9b0ec706fcc7a05c1fed011ced86afd7324c63537e49ec2974df481a83c", + "mid_rekey_salt": "de7a5be308f7a21f80df8a0258d2e191f5a06486ce7e2c", + "mid_send_garbage_terminator": "330b7aae0e4d13f5806bc1f696042155", + "mid_recv_garbage_terminator": "d514d7861e65ced36c7ea6ad2a8f6938", + "mid_session_id": "2ad4f52fd2a9cd078c0664dc4ba33904095d4affb5227fbc779662ca6140376c", + "out_ciphertext": "", + "out_ciphertext_endswith": "09afb85b3677a099e44afe8147c515804558fa128fc772cd125b3089b124977f79e7d11e1a1a03095979d4bddf40adbda5a837e84e82916bfb504fe605dd25531be008701b314c030c04ba3ee808ac68b837c3e7035b0d4847a2d448b218f91cce1fff03b35935a4661088e6ea5d5cded7ca21d3c0f75796debc593f1730b698b901044b3989e1ea425668ef2fdcfe0af4942d24f893857b693b53599039e3c814e797d7b2aed37806510aaa6dc9dda44aee0fa0d814b9459111a73503538230a905f016650169d4c9ca531bbd58eb2ae5c40c9a11aefd7d117d690706855d5ce30ff74865de26112096a88d445397e2e2bf02fd149663d92ff764156f09709e" + }, + { + "in_idx": 1024, + "in_priv_ours": "f7c11ca3b55137fa6124c49b25dea0287815b887760e1b986497e976444eb5bb", + "in_ellswift_ours": "5a2a0ce42feb17e70a96db1e55bc380e26a40a589dfc453d6c2e82bbc4c2b161716a05df9039ccffb6df45183480119deb69299e44d48e38de746ad084156edd", + "in_ellswift_theirs": "2c402d53332d914e4f5501c8db682bec6738ed53c7410655d9346db99bae37642beb6a1d48394502941965ea7140e1172ce591923235640f964c0d597caae304", + "in_initiating": 1, + "in_contents": "bc5fdee9e87d05b9723871a35823f643cc53c400851686d69d87433028c19cc295ceea1513d8056021800c86d886f27f6018e4053121df4db5216bff17160f7b1bc4d3be67354f0551f55a353de5f15add353605e2a0761bb0c1b3bb8a37787e797e619ebf902a8ed4fcf1c741c897c8469cf54565b21c85e2b4fac5ef6e948f2c36df269f7189d1f293d2a95a08a1b9d00528521ff407ff992726712c8c9d6273a878d1700912ff2ef5b1b6899e9b88f6999bc628c8a390b37d9be99bd742e16a1eb83b0d1b8bfb6858e3fddb33ce2d9ef40afefd29833fd59dbc0aef25c35771b17d62595b5de9dc7426f1d976deea78", + "in_multiply": 69615, + "in_aad": "", + "in_ignore": 1, + "mid_x_ours": "6d89eb9e87319fd088ee1e77fa5b36d2d2d9e66fae8d11ab7bd70407d4aaba29", + "mid_x_shared": "0b6c87c76983e5c84428bd646d24c133d9321af5f7cb4624ca1524bcab828ffd", + "mid_shared_secret": "2a4d8d7ab2d2e92c429c8c4cb0d78f0f170a244410d588b92c7fd8baf7c27e59", + "mid_initiator_l": "001f5a1254d1734551f7895221f370aa5eb2ea382dfefd586941becac178d8a2", + "mid_initiator_p": "acf0ffecff0851e065af2a149f1c0a0db21c8a885613ebd1c445957b56c3d705", + "mid_responder_l": "42b3699eec52f5f4648bdb40c25c956944f0a3e9986552917a3498078a8de907", + "mid_responder_p": "a721aa5cf8d2b4ce9f46f20693438d94f75130585b6ab137894b273a436c90d4", + "mid_rekey_salt": "1512ee97517ecf95bf4a66689852ea011b8810891b4b0b", + "mid_send_garbage_terminator": "a5e8c7130b7b8cb34e303bc3da911230", + "mid_recv_garbage_terminator": "0169b4c927c5a809bd3b0981887e7e0b", + "mid_session_id": "462c77437e78cb8620e0572e5a401d151e6ae45aed57d7638d924f569eccd73e", + "out_ciphertext": "", + "out_ciphertext_endswith": "de5b3741446139c453cb8d88696018f02473c3b84a27fc10ed78f21860398a0de3abc9b3dccad4eacfb604034e541c201adcaba5ceffb98502cc7e98f6f6d896e3a4bd122102c43ab491923164c41c12e2ff7bd63f39255c671a87e49596ed06dcf58293febebe3ff3ada97826b0ad2258b46817275c2f95fbd4b0fa5e1cbca33e5f0e5a97a2e2493060eea6033cdb9c8002aaf17d73afc7fc968258acbb3b83cdf2272682fb4cbda84e8c3ca2cece5e24d0606761a3cd611de58976a1cd448e34b67d0a4dd19a1bcfe59e5c7c215747d9e8ef3504edbd1eaf0ca8c272247d6b9cc137d0299e48191a18f839018ac312928e76fe1b6c13f7175bd6266fb4febb" + } +] diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 4406779015183..10011e4828799 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -54,7 +54,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman) random_string = fuzzed_data_provider.ConsumeRandomLengthString(64); }, [&] { - connman.AddNode(random_string); + connman.AddNode({random_string, fuzzed_data_provider.ConsumeBool()}); }, [&] { connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral()); diff --git a/src/test/fuzz/crypto_bip324_suite.cpp b/src/test/fuzz/crypto_bip324_suite.cpp new file mode 100644 index 0000000000000..f4ce012470bdf --- /dev/null +++ b/src/test/fuzz/crypto_bip324_suite.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2020-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +void get_key(FuzzedDataProvider& fdp, Span key) +{ + auto key_vec = fdp.ConsumeBytes(key.size()); + key_vec.resize(key.size()); + memcpy(key.data(), key_vec.data(), key.size()); +} + + +FUZZ_TARGET(crypto_bip324_suite) +{ + FuzzedDataProvider fdp{buffer.data(), buffer.size()}; + + BIP324Key key_L, key_P; + get_key(fdp, key_L); + get_key(fdp, key_P); + + std::array rekey_salt; + get_key(fdp, rekey_salt); + + BIP324CipherSuite suite(key_L, key_P, rekey_salt); + + size_t contents_size = fdp.ConsumeIntegralInRange(0, 4096); + std::vector in(BIP324_LENGTH_FIELD_LEN + BIP324_HEADER_LEN + contents_size + RFC8439_EXPANSION, std::byte{0x00}); + std::vector out(BIP324_LENGTH_FIELD_LEN + BIP324_HEADER_LEN + contents_size + RFC8439_EXPANSION, std::byte{0x00}); + bool is_encrypt = fdp.ConsumeBool(); + BIP324HeaderFlags flags{fdp.ConsumeIntegralInRange(0, 255)}; + size_t aad_size = fdp.ConsumeIntegralInRange(0, 255); + auto aad = fdp.ConsumeBytes(aad_size); + LIMITED_WHILE(fdp.ConsumeBool(), 10000) + { + CallOneOf( + fdp, + [&] { + contents_size = fdp.ConsumeIntegralInRange(64, 4096); + in = std::vector(BIP324_LENGTH_FIELD_LEN + BIP324_HEADER_LEN + contents_size + RFC8439_EXPANSION, std::byte{0x00}); + out = std::vector(BIP324_LENGTH_FIELD_LEN + BIP324_HEADER_LEN + contents_size + RFC8439_EXPANSION, std::byte{0x00}); + }, + [&] { + flags = BIP324HeaderFlags{fdp.ConsumeIntegralInRange(0, 255)}; + }, + [&] { + (void)suite.Crypt(aad, in, out, flags, is_encrypt); + }, + [&] { + std::array encrypted_pkt_len; + memcpy(encrypted_pkt_len.data(), in.data(), BIP324_LENGTH_FIELD_LEN); + (void)suite.DecryptLength(encrypted_pkt_len); + }, + [&] { + is_encrypt = fdp.ConsumeBool(); + }); + } +} diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp index 3f552a8cda8ef..3ba4081bb6739 100644 --- a/src/test/fuzz/crypto_chacha20.cpp +++ b/src/test/fuzz/crypto_chacha20.cpp @@ -6,7 +6,10 @@ #include #include #include +#include +#include +#include #include #include @@ -16,21 +19,21 @@ FUZZ_TARGET(crypto_chacha20) ChaCha20 chacha20; if (fuzzed_data_provider.ConsumeBool()) { - const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange(16, 32)); - chacha20 = ChaCha20{key.data(), key.size()}; + const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32); + chacha20 = ChaCha20{key.data()}; } LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { CallOneOf( fuzzed_data_provider, [&] { - const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange(16, 32)); - chacha20.SetKey(key.data(), key.size()); + std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32); + chacha20.SetKey32(key.data()); }, [&] { chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral()); }, [&] { - chacha20.Seek(fuzzed_data_provider.ConsumeIntegral()); + chacha20.Seek64(fuzzed_data_provider.ConsumeIntegral()); }, [&] { std::vector output(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); @@ -43,3 +46,135 @@ FUZZ_TARGET(crypto_chacha20) }); } } + +namespace +{ + +/** Fuzzer that invokes ChaCha20::Crypt() or ChaCha20::Keystream multiple times: + once for a large block at once, and then the same data in chunks, comparing + the outcome. + + If UseCrypt, seeded Xoroshiro128++ output is used as input to Crypt(). + If not, Keystream() is used directly, or sequences of 0x00 are encrypted. +*/ +template +void ChaCha20SplitFuzz(FuzzedDataProvider& provider) +{ + // Determine key, iv, start position, length. + unsigned char key[32] = {0}; + auto key_bytes = provider.ConsumeBytes(32); + std::copy(key_bytes.begin(), key_bytes.end(), key); + uint64_t iv = provider.ConsumeIntegral(); + uint64_t total_bytes = provider.ConsumeIntegralInRange(0, 1000000); + uint64_t seek = provider.ConsumeIntegralInRange(0, ~(total_bytes >> 6)); + + // Initialize two ChaCha20 ciphers, with the same key/iv/position. + ChaCha20 crypt1(key); + ChaCha20 crypt2(key); + crypt1.SetIV(iv); + crypt1.Seek64(seek); + crypt2.SetIV(iv); + crypt2.Seek64(seek); + + // Construct vectors with data. + std::vector data1, data2; + data1.resize(total_bytes); + data2.resize(total_bytes); + + // If using Crypt(), initialize data1 and data2 with the same Xoroshiro128++ based + // stream. + if constexpr (UseCrypt) { + uint64_t seed = provider.ConsumeIntegral(); + XoRoShiRo128PlusPlus rng(seed); + uint64_t bytes = 0; + while (bytes < (total_bytes & ~uint64_t{7})) { + uint64_t val = rng(); + WriteLE64(data1.data() + bytes, val); + WriteLE64(data2.data() + bytes, val); + bytes += 8; + } + if (bytes < total_bytes) { + unsigned char valbytes[8]; + uint64_t val = rng(); + WriteLE64(valbytes, val); + std::copy(valbytes, valbytes + (total_bytes - bytes), data1.data() + bytes); + std::copy(valbytes, valbytes + (total_bytes - bytes), data2.data() + bytes); + } + } + + // Whether UseCrypt is used or not, the two byte arrays must match. + assert(data1 == data2); + + // Encrypt data1, the whole array at once. + if constexpr (UseCrypt) { + crypt1.Crypt(data1.data(), data1.data(), total_bytes); + } else { + crypt1.Keystream(data1.data(), total_bytes); + } + + // Encrypt data2, in at most 256 chunks. + uint64_t bytes2 = 0; + int iter = 0; + while (true) { + bool is_last = (iter == 255) || provider.ConsumeBool(); + ++iter; + // Determine how many bytes to encrypt in this chunk: a fuzzer-determined + // amount for all but the last chunk (which processes all remaining bytes). + uint64_t now = is_last ? total_bytes - bytes2 : + provider.ConsumeIntegralInRange(0, total_bytes - bytes2); + // For each chunk, consider using Crypt() even when UseCrypt is false. + // This tests that Keystream() has the same behavior as Crypt() applied + // to 0x00 input bytes. + if (UseCrypt || provider.ConsumeBool()) { + crypt2.Crypt(data2.data() + bytes2, data2.data() + bytes2, now); + } else { + crypt2.Keystream(data2.data() + bytes2, now); + } + bytes2 += now; + if (is_last) break; + } + // We should have processed everything now. + assert(bytes2 == total_bytes); + // And the result should match. + assert(data1 == data2); +} + +} // namespace + +FUZZ_TARGET(chacha20_split_crypt) +{ + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + ChaCha20SplitFuzz(provider); +} + +FUZZ_TARGET(chacha20_split_keystream) +{ + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + ChaCha20SplitFuzz(provider); +} + +FUZZ_TARGET(crypto_fschacha20) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + + auto key_vec = ConsumeFixedLengthByteVector(fuzzed_data_provider, FSCHACHA20_KEYLEN); + key_vec.resize(FSCHACHA20_KEYLEN); + auto salt_vec = ConsumeFixedLengthByteVector(fuzzed_data_provider, FSCHACHA20_KEYLEN); + salt_vec.resize(FSCHACHA20_KEYLEN); + + std::array key; + memcpy(key.data(), key_vec.data(), FSCHACHA20_KEYLEN); + + std::array salt; + memcpy(salt.data(), salt_vec.data(), FSCHACHA20_REKEY_SALT_LEN); + + auto fsc20 = FSChaCha20{key, salt, fuzzed_data_provider.ConsumeIntegralInRange(1, 1024)}; + + LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) + { + auto input = fuzzed_data_provider.ConsumeBytes(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + std::vector output; + output.resize(input.size()); + fsc20.Crypt(input, output); + } +} diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp deleted file mode 100644 index 596614a71bf85..0000000000000 --- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2020-2021 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -FUZZ_TARGET(crypto_chacha20_poly1305_aead) -{ - FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; - - const std::vector k1 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN); - const std::vector k2 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN); - - ChaCha20Poly1305AEAD aead(k1.data(), k1.size(), k2.data(), k2.size()); - uint64_t seqnr_payload = 0; - uint64_t seqnr_aad = 0; - int aad_pos = 0; - size_t buffer_size = fuzzed_data_provider.ConsumeIntegralInRange(0, 4096); - std::vector in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - std::vector out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - bool is_encrypt = fuzzed_data_provider.ConsumeBool(); - LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { - CallOneOf( - fuzzed_data_provider, - [&] { - buffer_size = fuzzed_data_provider.ConsumeIntegralInRange(64, 4096); - in = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - out = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - }, - [&] { - (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt); - }, - [&] { - uint32_t len = 0; - const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data()); - assert(ok); - }, - [&] { - if (AdditionOverflow(seqnr_payload, static_cast(1))) { - return; - } - seqnr_payload += 1; - aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; - if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { - aad_pos = 0; - if (AdditionOverflow(seqnr_aad, static_cast(1))) { - return; - } - seqnr_aad += 1; - } - }, - [&] { - seqnr_payload = fuzzed_data_provider.ConsumeIntegral(); - }, - [&] { - seqnr_aad = fuzzed_data_provider.ConsumeIntegral(); - }, - [&] { - is_encrypt = fuzzed_data_provider.ConsumeBool(); - }); - } -} diff --git a/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp b/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp index 1b89d557735ea..78fee48de6598 100644 --- a/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp +++ b/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp @@ -267,32 +267,33 @@ void ECRYPT_keystream_bytes(ECRYPT_ctx* x, u8* stream, u32 bytes) FUZZ_TARGET(crypto_diff_fuzz_chacha20) { + static const unsigned char ZEROKEY[32] = {0}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; ChaCha20 chacha20; ECRYPT_ctx ctx; - // D. J. Bernstein doesn't initialise ctx to 0 while Bitcoin Core initialises chacha20 to 0 in the constructor - for (int i = 0; i < 16; i++) { - ctx.input[i] = 0; - } if (fuzzed_data_provider.ConsumeBool()) { - const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange(16, 32)); - chacha20 = ChaCha20{key.data(), key.size()}; + const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32); + chacha20 = ChaCha20{key.data()}; ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0); - // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey() does - uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - ECRYPT_ivsetup(&ctx, iv); + } else { + // The default ChaCha20 constructor is equivalent to using the all-0 key. + ECRYPT_keysetup(&ctx, ZEROKEY, 256, 0); } + // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey32() does + static const uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + ECRYPT_ivsetup(&ctx, iv); + LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 3000) { CallOneOf( fuzzed_data_provider, [&] { - const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange(16, 32)); - chacha20.SetKey(key.data(), key.size()); + const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32); + chacha20.SetKey32(key.data()); ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0); - // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey() does + // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey32() does uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; ECRYPT_ivsetup(&ctx, iv); }, @@ -304,26 +305,32 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20) }, [&] { uint64_t counter = fuzzed_data_provider.ConsumeIntegral(); - chacha20.Seek(counter); + chacha20.Seek64(counter); ctx.input[12] = counter; ctx.input[13] = counter >> 32; }, [&] { uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange(0, 4096); + // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that. + uint64_t pos = ctx.input[12] + (((uint64_t)ctx.input[13]) << 32) + ((integralInRange + 63) >> 6); std::vector output(integralInRange); chacha20.Keystream(output.data(), output.size()); std::vector djb_output(integralInRange); ECRYPT_keystream_bytes(&ctx, djb_output.data(), djb_output.size()); assert(output == djb_output); + chacha20.Seek64(pos); }, [&] { uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange(0, 4096); + // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that. + uint64_t pos = ctx.input[12] + (((uint64_t)ctx.input[13]) << 32) + ((integralInRange + 63) >> 6); std::vector output(integralInRange); const std::vector input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size()); chacha20.Crypt(input.data(), output.data(), input.size()); std::vector djb_output(integralInRange); ECRYPT_encrypt_bytes(&ctx, input.data(), djb_output.data(), input.size()); assert(output == djb_output); + chacha20.Seek64(pos); }); } } diff --git a/src/test/fuzz/crypto_rfc8439.cpp b/src/test/fuzz/crypto_rfc8439.cpp new file mode 100644 index 0000000000000..10f9af3d16e82 --- /dev/null +++ b/src/test/fuzz/crypto_rfc8439.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include +#include + +FUZZ_TARGET(crypto_rfc8439) +{ + FuzzedDataProvider fdp{buffer.data(), buffer.size()}; + auto aad_len = fdp.ConsumeIntegralInRange(0, 1023); + auto aad = fdp.ConsumeBytes(aad_len); + aad.resize(aad_len); + + auto key = fdp.ConsumeBytes(RFC8439_KEYLEN); + key.resize(RFC8439_KEYLEN); + + auto nonce_vec = fdp.ConsumeBytes(12); + nonce_vec.resize(12); + std::array nonce; + memcpy(nonce.data(), nonce_vec.data(), 12); + + auto plaintext_len = fdp.ConsumeIntegralInRange(0, 4095); + auto plaintext = fdp.ConsumeBytes(plaintext_len); + plaintext.resize(plaintext_len); + + std::vector output(plaintext.size() + POLY1305_TAGLEN, std::byte{0x00}); + RFC8439Encrypt(aad, key, nonce, plaintext, output); + + auto bit_flip_attack = !plaintext.empty() && fdp.ConsumeBool(); + if (bit_flip_attack) { + auto byte_to_flip = fdp.ConsumeIntegralInRange(0, static_cast(output.size() - POLY1305_TAGLEN - 1)); + output[byte_to_flip] = output[byte_to_flip] ^ std::byte{0xFF}; + } + + std::vector decrypted_plaintext(plaintext.size(), std::byte{0x00}); + auto authenticated = RFC8439Decrypt(aad, key, nonce, output, decrypted_plaintext); + if (bit_flip_attack) { + assert(!authenticated); + } else { + assert(authenticated); + if (!plaintext.empty()) { + assert(memcmp(decrypted_plaintext.data(), plaintext.data(), plaintext.size()) == 0); + } + } +} diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp index a76901e4731e6..a708d20127c13 100644 --- a/src/test/fuzz/key.cpp +++ b/src/test/fuzz/key.cpp @@ -15,13 +15,19 @@ #include