Merge bitcoin/bitcoin#27479: BIP324: ElligatorSwift integrations

3168b08043 Bench test for EllSwift ECDH (Pieter Wuille)
42d759f239 Bench tests for CKey->EllSwift (dhruv)
2e5a8a437c Fuzz test for Ellswift ECDH (dhruv)
c3ac9f5cf4 Fuzz test for CKey->EllSwift->CPubKey creation/decoding (dhruv)
aae432a764 Unit test for ellswift creation/decoding roundtrip (dhruv)
eff72a0dff Add ElligatorSwift key creation and ECDH logic (Pieter Wuille)
42239f8390 Enable ellswift module in libsecp256k1 (dhruv)
901336eee7 Squashed 'src/secp256k1/' changes from 4258c54f4e..705ce7ed8c (Pieter Wuille)

Pull request description:

  This replaces #23432 and part of #23561.

  This PR introduces all of the ElligatorSwift-related changes (libsecp256k1 updates, generation, decoding, ECDH, tests, fuzzing, benchmarks) needed for BIP324.

  ElligatorSwift is a special 64-byte encoding format for public keys introduced in libsecp256k1 in https://github.com/bitcoin-core/secp256k1/pull/1129. It has the property that *every* 64-byte array is a valid encoding for some public key, and every key has approximately $2^{256}$ encodings. Furthermore, it is possible to efficiently generate a uniformly random encoding for a given public key or private key. This is used for the key exchange phase in BIP324, to achieve a byte stream that is entirely pseudorandom, even before the shared encryption key is established.

ACKs for top commit:
  instagibbs:
    reACK 3168b08043
  achow101:
    ACK 3168b08043
  theStack:
    re-ACK 3168b08043

Tree-SHA512: 308ac3d33e9a2deecb65826cbf0390480a38de201918429c35c796f3421cdf94c5501d027a043ae8f012cfaa0584656da1de6393bfba3532ab4c20f9533f06a6
This commit is contained in:
Andrew Chow
2023-06-26 16:50:21 -04:00
90 changed files with 4200 additions and 1178 deletions

View File

@@ -15,13 +15,17 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <util/chaintype.h>
#include <util/strencodings.h>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <numeric>
#include <optional>
#include <string>
#include <vector>
@@ -303,3 +307,79 @@ FUZZ_TARGET_INIT(key, initialize_key)
}
}
}
FUZZ_TARGET_INIT(ellswift_roundtrip, initialize_key)
{
FuzzedDataProvider fdp{buffer.data(), buffer.size()};
auto key_bytes = fdp.ConsumeBytes<uint8_t>(32);
key_bytes.resize(32);
CKey key;
key.Set(key_bytes.begin(), key_bytes.end(), true);
if (!key.IsValid()) return;
auto ent32 = fdp.ConsumeBytes<std::byte>(32);
ent32.resize(32);
auto encoded_ellswift = key.EllSwiftCreate(ent32);
auto decoded_pubkey = encoded_ellswift.Decode();
assert(key.VerifyPubKey(decoded_pubkey));
}
FUZZ_TARGET_INIT(bip324_ecdh, initialize_key)
{
FuzzedDataProvider fdp{buffer.data(), buffer.size()};
// We generate private key, k1.
auto rnd32 = fdp.ConsumeBytes<uint8_t>(32);
rnd32.resize(32);
CKey k1;
k1.Set(rnd32.begin(), rnd32.end(), true);
if (!k1.IsValid()) return;
// They generate private key, k2.
rnd32 = fdp.ConsumeBytes<uint8_t>(32);
rnd32.resize(32);
CKey k2;
k2.Set(rnd32.begin(), rnd32.end(), true);
if (!k2.IsValid()) return;
// We construct an ellswift encoding for our key, k1_ellswift.
auto ent32_1 = fdp.ConsumeBytes<std::byte>(32);
ent32_1.resize(32);
auto k1_ellswift = k1.EllSwiftCreate(ent32_1);
// They construct an ellswift encoding for their key, k2_ellswift.
auto ent32_2 = fdp.ConsumeBytes<std::byte>(32);
ent32_2.resize(32);
auto k2_ellswift = k2.EllSwiftCreate(ent32_2);
// They construct another (possibly distinct) ellswift encoding for their key, k2_ellswift_bad.
auto ent32_2_bad = fdp.ConsumeBytes<std::byte>(32);
ent32_2_bad.resize(32);
auto k2_ellswift_bad = k2.EllSwiftCreate(ent32_2_bad);
assert((ent32_2_bad == ent32_2) == (k2_ellswift_bad == k2_ellswift));
// Determine who is who.
bool initiating = fdp.ConsumeBool();
// We compute our shared secret using our key and their public key.
auto ecdh_secret_1 = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, initiating);
// They compute their shared secret using their key and our public key.
auto ecdh_secret_2 = k2.ComputeBIP324ECDHSecret(k1_ellswift, k2_ellswift, !initiating);
// Those must match, as everyone is behaving correctly.
assert(ecdh_secret_1 == ecdh_secret_2);
if (k1_ellswift != k2_ellswift) {
// Unless the two keys are exactly identical, acting as the wrong party breaks things.
auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, !initiating);
assert(ecdh_secret_bad != ecdh_secret_1);
}
if (k2_ellswift_bad != k2_ellswift) {
// Unless both encodings created by them are identical, using the second one breaks things.
auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift_bad, k1_ellswift, initiating);
assert(ecdh_secret_bad != ecdh_secret_1);
}
}

View File

@@ -344,4 +344,24 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
}
}
BOOST_AUTO_TEST_CASE(key_ellswift)
{
for (const auto& secret : {strSecret1, strSecret2, strSecret1C, strSecret2C}) {
CKey key = DecodeSecret(secret);
BOOST_CHECK(key.IsValid());
uint256 ent32 = InsecureRand256();
auto ellswift = key.EllSwiftCreate(AsBytes(Span{ent32}));
CPubKey decoded_pubkey = ellswift.Decode();
if (!key.IsCompressed()) {
// The decoding constructor returns a compressed pubkey. If the
// original was uncompressed, we must decompress the decoded one
// to compare.
decoded_pubkey.Decompress();
}
BOOST_CHECK(key.GetPubKey() == decoded_pubkey);
}
}
BOOST_AUTO_TEST_SUITE_END()