Merge bitcoin/bitcoin#33399: key: use static context for libsecp256k1 calls where applicable

1ff9e92948 key: use static context for libsecp256k1 calls where applicable (Sebastian Falbesoner)

Pull request description:

  The dynamically created [signing context](2d6a0c4649/src/key.cpp (L19)) for libsecp256k1 calls is only needed for functions that involve generator point multiplication with a secret key, i.e. different variants of public key creation and signing. The API docs hint to those by stating "[(not secp256k1_context_static)](b475654302/include/secp256k1.h (L645))" for the context parameter. In our case that applies to the following calls:
  - `secp256k1_ec_pubkey_create`
  - `secp256k1_keypair_create`
  - `secp256k1_ellswift_create`
  - `secp256k1_ecdsa_sign`
  - `secp256k1_ecdsa_sign_recoverable`
  - `secp256k1_schnorrsig_sign32`
  - `ec_seckey_export_der` (not a direct secp256k1 function, but calls `secp256k1_ec_pubkey_create` inside)

  For all the other secp256k1 calls we can simply use the static context. This is done for consistency to other calls that already use `secp256k1_context_static`, and also to reduce dependencies on the global signing context variable. Looked closer at this in the course of reviewing #29675, where some functions used the signing context that didn't need to, avoiding a move to another module (see https://github.com/bitcoin/bitcoin/pull/29675#discussion_r2333831377).

ACKs for top commit:
  Eunovo:
    ACK 1ff9e92948
  furszy:
    ACK 1ff9e92948
  rkrux:
    crACK 1ff9e92948

Tree-SHA512: f091efa56c358057828f3455d4ca9ce40ec0d35f3e38ab147fe3928bb5dbf7ffbc27dbf97b71937828ab95ea4e9be5f96d89a2d29e2aa18df4542aae1b33e258
This commit is contained in:
merge-script 2025-09-26 11:44:29 -04:00
commit 7e08445449
No known key found for this signature in database
GPG Key ID: BA03F4DBE0C63FB4
3 changed files with 16 additions and 16 deletions

View File

@ -155,7 +155,7 @@ int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, si
} }
bool CKey::Check(const unsigned char *vch) { bool CKey::Check(const unsigned char *vch) {
return secp256k1_ec_seckey_verify(secp256k1_context_sign, vch); return secp256k1_ec_seckey_verify(secp256k1_context_static, vch);
} }
void CKey::MakeNewKey(bool fCompressedIn) { void CKey::MakeNewKey(bool fCompressedIn) {
@ -186,7 +186,7 @@ CPubKey CKey::GetPubKey() const {
CPubKey result; CPubKey result;
int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, UCharCast(begin())); int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, UCharCast(begin()));
assert(ret); assert(ret);
secp256k1_ec_pubkey_serialize(secp256k1_context_sign, (unsigned char*)result.begin(), &clen, &pubkey, fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); secp256k1_ec_pubkey_serialize(secp256k1_context_static, (unsigned char*)result.begin(), &clen, &pubkey, fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
assert(result.size() == clen); assert(result.size() == clen);
assert(result.IsValid()); assert(result.IsValid());
return result; return result;
@ -196,7 +196,7 @@ CPubKey CKey::GetPubKey() const {
bool SigHasLowR(const secp256k1_ecdsa_signature* sig) bool SigHasLowR(const secp256k1_ecdsa_signature* sig)
{ {
unsigned char compact_sig[64]; unsigned char compact_sig[64];
secp256k1_ecdsa_signature_serialize_compact(secp256k1_context_sign, compact_sig, sig); secp256k1_ecdsa_signature_serialize_compact(secp256k1_context_static, compact_sig, sig);
// In DER serialization, all values are interpreted as big-endian, signed integers. The highest bit in the integer indicates // In DER serialization, all values are interpreted as big-endian, signed integers. The highest bit in the integer indicates
// its signed-ness; 0 is positive, 1 is negative. When the value is interpreted as a negative integer, it must be converted // its signed-ness; 0 is positive, 1 is negative. When the value is interpreted as a negative integer, it must be converted
@ -222,7 +222,7 @@ bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool gr
ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, extra_entropy); ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, extra_entropy);
} }
assert(ret); assert(ret);
secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, vchSig.data(), &nSigLen, &sig); secp256k1_ecdsa_signature_serialize_der(secp256k1_context_static, vchSig.data(), &nSigLen, &sig);
vchSig.resize(nSigLen); vchSig.resize(nSigLen);
// Additional verification step to prevent using a potentially corrupted signature // Additional verification step to prevent using a potentially corrupted signature
secp256k1_pubkey pk; secp256k1_pubkey pk;
@ -254,7 +254,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
secp256k1_ecdsa_recoverable_signature rsig; secp256k1_ecdsa_recoverable_signature rsig;
int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &rsig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, nullptr); int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &rsig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, nullptr);
assert(ret); assert(ret);
ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, &vchSig[1], &rec, &rsig); ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_static, &vchSig[1], &rec, &rsig);
assert(ret); assert(ret);
assert(rec != -1); assert(rec != -1);
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0); vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
@ -277,7 +277,7 @@ bool CKey::SignSchnorr(const uint256& hash, std::span<unsigned char> sig, const
bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) { bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
MakeKeyData(); MakeKeyData();
if (!ec_seckey_import_der(secp256k1_context_sign, (unsigned char*)begin(), seckey.data(), seckey.size())) { if (!ec_seckey_import_der(secp256k1_context_static, (unsigned char*)begin(), seckey.data(), seckey.size())) {
ClearKeyData(); ClearKeyData();
return false; return false;
} }
@ -303,7 +303,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
} }
memcpy(ccChild.begin(), vout.data()+32, 32); memcpy(ccChild.begin(), vout.data()+32, 32);
keyChild.Set(begin(), begin() + 32, true); keyChild.Set(begin(), begin() + 32, true);
bool ret = secp256k1_ec_seckey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data()); bool ret = secp256k1_ec_seckey_tweak_add(secp256k1_context_static, (unsigned char*)keyChild.begin(), vout.data());
if (!ret) keyChild.ClearKeyData(); if (!ret) keyChild.ClearKeyData();
return ret; return ret;
} }
@ -331,7 +331,7 @@ ECDHSecret CKey::ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, c
ECDHSecret output; ECDHSecret output;
// BIP324 uses the initiator as party A, and the responder as party B. Remap the inputs // BIP324 uses the initiator as party A, and the responder as party B. Remap the inputs
// accordingly: // accordingly:
bool success = secp256k1_ellswift_xdh(secp256k1_context_sign, bool success = secp256k1_ellswift_xdh(secp256k1_context_static,
UCharCast(output.data()), UCharCast(output.data()),
UCharCast(initiating ? our_ellswift.data() : their_ellswift.data()), UCharCast(initiating ? our_ellswift.data() : their_ellswift.data()),
UCharCast(initiating ? their_ellswift.data() : our_ellswift.data()), UCharCast(initiating ? their_ellswift.data() : our_ellswift.data()),
@ -415,8 +415,8 @@ KeyPair::KeyPair(const CKey& key, const uint256* merkle_root)
if (success && merkle_root) { if (success && merkle_root) {
secp256k1_xonly_pubkey pubkey; secp256k1_xonly_pubkey pubkey;
unsigned char pubkey_bytes[32]; unsigned char pubkey_bytes[32];
assert(secp256k1_keypair_xonly_pub(secp256k1_context_sign, &pubkey, nullptr, keypair)); assert(secp256k1_keypair_xonly_pub(secp256k1_context_static, &pubkey, nullptr, keypair));
assert(secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, pubkey_bytes, &pubkey)); assert(secp256k1_xonly_pubkey_serialize(secp256k1_context_static, pubkey_bytes, &pubkey));
uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root); uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
success = secp256k1_keypair_xonly_tweak_add(secp256k1_context_static, keypair, tweak.data()); success = secp256k1_keypair_xonly_tweak_add(secp256k1_context_static, keypair, tweak.data());
} }

View File

@ -17,22 +17,22 @@ int ec_seckey_export_der(const secp256k1_context* ctx, unsigned char* seckey, si
FUZZ_TARGET(secp256k1_ec_seckey_import_export_der) FUZZ_TARGET(secp256k1_ec_seckey_import_export_der)
{ {
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
secp256k1_context* secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
{ {
std::vector<uint8_t> out32(32); std::vector<uint8_t> out32(32);
(void)ec_seckey_import_der(secp256k1_context_sign, out32.data(), ConsumeFixedLengthByteVector(fuzzed_data_provider, CKey::SIZE).data(), CKey::SIZE); (void)ec_seckey_import_der(secp256k1_context_static, out32.data(), ConsumeFixedLengthByteVector(fuzzed_data_provider, CKey::SIZE).data(), CKey::SIZE);
} }
{ {
std::vector<uint8_t> seckey(CKey::SIZE); std::vector<uint8_t> seckey(CKey::SIZE);
const std::vector<uint8_t> key32 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32); const std::vector<uint8_t> key32 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
size_t seckeylen = CKey::SIZE; size_t seckeylen = CKey::SIZE;
const bool compressed = fuzzed_data_provider.ConsumeBool(); const bool compressed = fuzzed_data_provider.ConsumeBool();
secp256k1_context* secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
const bool exported = ec_seckey_export_der(secp256k1_context_sign, seckey.data(), &seckeylen, key32.data(), compressed); const bool exported = ec_seckey_export_der(secp256k1_context_sign, seckey.data(), &seckeylen, key32.data(), compressed);
secp256k1_context_destroy(secp256k1_context_sign);
if (exported) { if (exported) {
std::vector<uint8_t> out32(32); std::vector<uint8_t> out32(32);
const bool imported = ec_seckey_import_der(secp256k1_context_sign, out32.data(), seckey.data(), seckey.size()) == 1; const bool imported = ec_seckey_import_der(secp256k1_context_static, out32.data(), seckey.data(), seckey.size()) == 1;
assert(imported && key32 == out32); assert(imported && key32 == out32);
} }
} }
secp256k1_context_destroy(secp256k1_context_sign);
} }

View File

@ -376,9 +376,9 @@ BOOST_AUTO_TEST_CASE(key_schnorr_tweak_smoke_test)
secp256k1_keypair keypair; secp256k1_keypair keypair;
BOOST_CHECK(secp256k1_keypair_create(secp256k1_context_sign, &keypair, UCharCast(key.begin()))); BOOST_CHECK(secp256k1_keypair_create(secp256k1_context_sign, &keypair, UCharCast(key.begin())));
secp256k1_xonly_pubkey xonly_pubkey; secp256k1_xonly_pubkey xonly_pubkey;
BOOST_CHECK(secp256k1_keypair_xonly_pub(secp256k1_context_sign, &xonly_pubkey, nullptr, &keypair)); BOOST_CHECK(secp256k1_keypair_xonly_pub(secp256k1_context_static, &xonly_pubkey, nullptr, &keypair));
unsigned char xonly_bytes[32]; unsigned char xonly_bytes[32];
BOOST_CHECK(secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, xonly_bytes, &xonly_pubkey)); BOOST_CHECK(secp256k1_xonly_pubkey_serialize(secp256k1_context_static, xonly_bytes, &xonly_pubkey));
uint256 tweak_old = XOnlyPubKey(xonly_bytes).ComputeTapTweakHash(&merkle_root); uint256 tweak_old = XOnlyPubKey(xonly_bytes).ComputeTapTweakHash(&merkle_root);
// CPubKey // CPubKey