What changed, and why it matters
This commit adds convenience methods so that a Bitcoin keypair can sign messages directly, without callers having to reach into the underlying secp256k1 library. It is a code-cleanup and API-ergonomics change, not a fix for a known bug or vulnerability. The new methods wrap the same underlying signing functions that were already being used, so the cryptographic behavior is unchanged.
No security action required. Treat as a normal API improvement. Reviewers may optionally verify that the wrapper methods preserve the exact same feature-gating behavior (`rand` + `std`) as the replaced code.
Security signals we found
No security-relevant signal: pure API refactor wrapping existing signing primitives
No new dependencies or unsafe blocks introduced
No change to signature scheme, auxiliary randomness handling, or message hashing
Evidence from the diff
The patch introduces Keypair::raw_bip340_sign and Keypair::raw_bip340_sign_with_aux_randomness in bitcoin/src/crypto/key.rs. These thin wrappers call secp256k1::schnorr::sign, sign_no_aux_rand, or sign_with_aux_rand on self.as_inner(). Existing call sites in examples, sighash tests, and PSBT signing are refactored to use the new methods, removing duplicated #[cfg] blocks and explicit as_inner() access. No cryptographic algorithm, input validation, or key-handling logic is changed.
Changed components
bitcoin/src/crypto/key.rsbitcoin/src/crypto/sighash.rsbitcoin/src/psbt/mod.rsbitcoin/examples/sign-tx-taproot.rsbitcoin/examples/taproot-psbt.rsInspect captured patch +25 / −26
diff --git a/bitcoin/examples/sign-tx-taproot.rs b/bitcoin/examples/sign-tx-taproot.rs
index fc80ab86..67f77de5 100644
--- a/bitcoin/examples/sign-tx-taproot.rs
+++ b/bitcoin/examples/sign-tx-taproot.rs
@@ -67,8 +67,7 @@ fn main() {
// Sign the sighash using the secp256k1 library (exported by rust-bitcoin).
let tweaked: TweakedKeypair = keypair.tap_tweak(None);
- let signature =
- secp256k1::schnorr::sign(&sighash.to_byte_array(), tweaked.as_keypair().as_inner());
+ let signature = tweaked.as_keypair().raw_bip340_sign(&sighash.to_byte_array());
// Update the witness stack.
let signature = bitcoin::taproot::Signature { signature, sighash_type };
diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs
index 5da8702f..db57c423 100644
--- a/bitcoin/examples/taproot-psbt.rs
+++ b/bitcoin/examples/taproot-psbt.rs
@@ -755,7 +755,7 @@ fn sign_psbt_taproot(
Some(_) => keypair, // no tweak for script spend
};
- let signature = secp256k1::schnorr::sign(&hash.to_byte_array(), keypair.as_inner());
+ let signature = keypair.raw_bip340_sign(&hash.to_byte_array());
let final_signature = taproot::Signature { signature, sighash_type };
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index 785d5ba1..3124057e 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -446,6 +446,26 @@ impl Keypair {
/// This is equivalent to using [`XOnlyPublicKey::from_keypair`].
#[inline]
pub fn to_x_only_public_key(self) -> XOnlyPublicKey { XOnlyPublicKey::from_keypair(&self) }
+
+ /// Schnorr sign a message slice with this keypair.
+ ///
+ /// If the `rand` and `std` features are enabled, this function will randomly seed auxiliary
+ /// data. Otherwise, this will use no auxiliary data.
+ #[inline]
+ pub fn raw_bip340_sign(&self, msg: &[u8]) -> secp256k1::schnorr::Signature {
+ #[cfg(not(all(feature = "rand", feature = "std")))] {
+ secp256k1::schnorr::sign_no_aux_rand(msg, self.as_inner())
+ }
+ #[cfg(all(feature = "rand", feature = "std"))] {
+ secp256k1::schnorr::sign(msg, self.as_inner())
+ }
+ }
+
+ /// Schnorr sign a message slice with this keypair, using provided auxiliary random data.
+ #[inline]
+ pub fn raw_bip340_sign_with_aux_randomness(&self, msg: &[u8], aux_rand: &[u8; 32]) -> secp256k1::schnorr::Signature {
+ secp256k1::schnorr::sign_with_aux_rand(msg, self.as_inner(), aux_rand)
+ }
}
impl FromStr for Keypair {
diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs
index 4e57e6ec..c2f25099 100644
--- a/bitcoin/src/crypto/sighash.rs
+++ b/bitcoin/src/crypto/sighash.rs
@@ -2026,11 +2026,7 @@ mod tests {
.taproot_signature_hash(tx_ind, &Prevouts::All(&utxos), None, None, hash_ty)
.unwrap();
- let key_spend_sig = secp256k1::schnorr::sign_with_aux_rand(
- &sighash.to_byte_array(),
- tweaked_keypair.to_keypair().as_inner(),
- &[0u8; 32],
- );
+ let key_spend_sig = tweaked_keypair.to_keypair().raw_bip340_sign_with_aux_randomness(&sighash.to_byte_array(), &[0u8; 32]);
// Only compare the inner key, not the parity
assert_eq!(expected.internal_pubkey.to_inner(), internal_key.to_inner());
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index f32a609c..f7b0c517 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -431,14 +431,7 @@ impl Psbt {
.tap_tweak(input.tap_merkle_root)
.to_keypair();
- #[cfg(all(feature = "rand", feature = "std"))]
- let signature =
- secp256k1::schnorr::sign(&sighash.to_byte_array(), key_pair.as_inner());
- #[cfg(not(all(feature = "rand", feature = "std")))]
- let signature = secp256k1::schnorr::sign_no_aux_rand(
- &sighash.to_byte_array(),
- key_pair.as_inner(),
- );
+ let signature = key_pair.raw_bip340_sign(&sighash.to_byte_array());
let signature = taproot::Signature { signature, sighash_type };
input.tap_key_sig = Some(signature);
@@ -462,16 +455,7 @@ impl Psbt {
let (sighash, sighash_type) =
self.sighash_taproot(input_index, cache, Some(lh))?;
- #[cfg(all(feature = "rand", feature = "std"))]
- let signature = secp256k1::schnorr::sign(
- &sighash.to_byte_array(),
- key_pair.as_inner(),
- );
- #[cfg(not(all(feature = "rand", feature = "std")))]
- let signature = secp256k1::schnorr::sign_no_aux_rand(
- &sighash.to_byte_array(),
- key_pair.as_inner(),
- );
+ let signature = key_pair.raw_bip340_sign(&sighash.to_byte_array());
let signature = taproot::Signature { signature, sighash_type };
input.tap_script_sigs.insert((xonly, lh), signature);
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.