Skip to content

Commit cebb08b

Browse files
committed
refactor: move SignSchnorr to KeyPair
Move `SignSchnorr` to `KeyPair`. This makes `CKey::SignSchnorr` now compute a `KeyPair` object and then call `KeyPair::SignSchorr`. The notable changes are: * Move the merkle_root tweaking out of the sign function and into the KeyPair constructor * Remove the temporary secp256k1_keypair object and have the functions access m_keypair->data() directly
1 parent c39fd39 commit cebb08b

File tree

2 files changed

+46
-28
lines changed

2 files changed

+46
-28
lines changed

src/key.cpp

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -271,27 +271,8 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
271271

272272
bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root, const uint256& aux) const
273273
{
274-
assert(sig.size() == 64);
275-
secp256k1_keypair keypair;
276-
if (!secp256k1_keypair_create(secp256k1_context_sign, &keypair, UCharCast(begin()))) return false;
277-
if (merkle_root) {
278-
secp256k1_xonly_pubkey pubkey;
279-
if (!secp256k1_keypair_xonly_pub(secp256k1_context_sign, &pubkey, nullptr, &keypair)) return false;
280-
unsigned char pubkey_bytes[32];
281-
if (!secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, pubkey_bytes, &pubkey)) return false;
282-
uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
283-
if (!secp256k1_keypair_xonly_tweak_add(secp256k1_context_static, &keypair, tweak.data())) return false;
284-
}
285-
bool ret = secp256k1_schnorrsig_sign32(secp256k1_context_sign, sig.data(), hash.data(), &keypair, aux.data());
286-
if (ret) {
287-
// Additional verification step to prevent using a potentially corrupted signature
288-
secp256k1_xonly_pubkey pubkey_verify;
289-
ret = secp256k1_keypair_xonly_pub(secp256k1_context_static, &pubkey_verify, nullptr, &keypair);
290-
ret &= secp256k1_schnorrsig_verify(secp256k1_context_static, sig.data(), hash.begin(), 32, &pubkey_verify);
291-
}
292-
if (!ret) memory_cleanse(sig.data(), sig.size());
293-
memory_cleanse(&keypair, sizeof(keypair));
294-
return ret;
274+
KeyPair kp = ComputeKeyPair(merkle_root);
275+
return kp.SignSchnorr(hash, sig, aux);
295276
}
296277

297278
bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
@@ -363,9 +344,9 @@ ECDHSecret CKey::ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, c
363344
return output;
364345
}
365346

366-
KeyPair CKey::ComputeKeyPair() const
347+
KeyPair CKey::ComputeKeyPair(const uint256* merkle_root) const
367348
{
368-
return KeyPair(*this);
349+
return KeyPair(*this, merkle_root);
369350
}
370351

371352
CKey GenerateRandomKey(bool compressed) noexcept
@@ -425,16 +406,39 @@ void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
425406
if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || code[41] != 0) key = CKey();
426407
}
427408

428-
KeyPair::KeyPair(const CKey& key)
409+
KeyPair::KeyPair(const CKey& key, const uint256* merkle_root)
429410
{
430411
static_assert(std::tuple_size<KeyType>() == sizeof(secp256k1_keypair));
431412
MakeKeyPairData();
432413
auto keypair = reinterpret_cast<secp256k1_keypair*>(m_keypair->data());
433-
434414
bool success = secp256k1_keypair_create(secp256k1_context_sign, keypair, UCharCast(key.data()));
415+
if (success && merkle_root) {
416+
secp256k1_xonly_pubkey pubkey;
417+
if (!secp256k1_keypair_xonly_pub(secp256k1_context_sign, &pubkey, nullptr, keypair)) return;
418+
unsigned char pubkey_bytes[32];
419+
if (!secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, pubkey_bytes, &pubkey)) return;
420+
uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
421+
success = secp256k1_keypair_xonly_tweak_add(secp256k1_context_static, keypair, tweak.data());
422+
}
435423
if (!success) ClearKeyPairData();
436424
}
437425

426+
bool KeyPair::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256& aux) const
427+
{
428+
assert(sig.size() == 64);
429+
if (!IsValid()) return false;
430+
auto keypair = reinterpret_cast<const secp256k1_keypair*>(m_keypair->data());
431+
bool ret = secp256k1_schnorrsig_sign32(secp256k1_context_sign, sig.data(), hash.data(), keypair, aux.data());
432+
if (ret) {
433+
// Additional verification step to prevent using a potentially corrupted signature
434+
secp256k1_xonly_pubkey pubkey_verify;
435+
ret = secp256k1_keypair_xonly_pub(secp256k1_context_static, &pubkey_verify, nullptr, keypair);
436+
ret &= secp256k1_schnorrsig_verify(secp256k1_context_static, sig.data(), hash.begin(), 32, &pubkey_verify);
437+
}
438+
if (!ret) memory_cleanse(sig.data(), sig.size());
439+
return ret;
440+
}
441+
438442
bool ECC_InitSanityCheck() {
439443
CKey key = GenerateRandomKey();
440444
CPubKey pubkey = key.GetPubKey();

src/key.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,19 @@ class CKey
207207
/** Compute a KeyPair
208208
*
209209
* Wraps a `secp256k1_keypair` type.
210+
*
211+
* `merkle_root` is used to optionally perform tweaking of
212+
* the internal key, as specified in BIP341:
213+
*
214+
* - If merkle_root == nullptr: no tweaking is done, use the internal key directly (this is
215+
* used for signatures in BIP342 script).
216+
* - If merkle_root->IsNull(): tweak the internal key with H_TapTweak(pubkey) (this is used for
217+
* key path spending when no scripts are present).
218+
* - Otherwise: tweak the internal key with H_TapTweak(pubkey || *merkle_root)
219+
* (this is used for key path spending with the
220+
* Merkle root of the script tree).
210221
*/
211-
KeyPair ComputeKeyPair() const;
222+
KeyPair ComputeKeyPair(const uint256* merkle_root) const;
212223
};
213224

214225
CKey GenerateRandomKey(bool compressed = true) noexcept;
@@ -249,6 +260,9 @@ struct CExtKey {
249260
* be negated by checking the parity of the public key. This class primarily intended for passing
250261
* secret keys to libsecp256k1 functions expecting a `secp256k1_keypair`. For all other cases,
251262
* CKey should be preferred.
263+
*
264+
* A KeyPair can be created from a CKey with an optional merkle_root tweak (per BIP342). See
265+
* CKey::ComputeKeyPair for more details.
252266
*/
253267
class KeyPair
254268
{
@@ -271,14 +285,14 @@ class KeyPair
271285

272286
KeyPair(const KeyPair& other) { *this = other; }
273287

274-
friend KeyPair CKey::ComputeKeyPair() const;
288+
friend KeyPair CKey::ComputeKeyPair(const uint256* merkle_root) const;
275289
[[nodiscard]] bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256& aux) const;
276290

277291
//! Check whether this keypair is valid.
278292
bool IsValid() const { return !!m_keypair; }
279293

280294
private:
281-
KeyPair(const CKey& key);
295+
KeyPair(const CKey& key, const uint256* merkle_root);
282296

283297
using KeyType = std::array<unsigned char, 96>;
284298
secure_unique_ptr<KeyType> m_keypair;

0 commit comments

Comments
 (0)