Remove pub access to CompressedPublicKey inner
What changed, and why it matters
This commit is a routine API cleanup, not a security fix. It hides the internal secp256k1 public key inside the CompressedPublicKey type and replaces direct field access with a constructor (from_secp) and a getter (to_inner). That improves encapsulation but does not change how keys are validated or used. There is no evidence this fixes a vulnerability.
No security action required. Treat as a normal API-breaking change and update downstream code that previously accessed CompressedPublicKey.0 to use CompressedPublicKey::from_secp() / to_inner().
Security signals we found
No validation logic added or removed
No cryptographic operations changed
No bounds checks, parsing, or serialization code modified
No mention of vulnerability, CVE, bug, or security issue in commit message
Evidence from the diff
The patch changes CompressedPublicKey from a tuple struct with a pub secp256k1::PublicKey field to one with a private field, adding from_secp() and to_inner() methods. Call sites in examples, bip32, and tests are updated accordingly. The behavior of the underlying secp256k1::PublicKey is unchanged; only visibility and construction syntax differ.
Changed components
bitcoin/src/crypto/key.rsbitcoin/src/bip32.rsbitcoin/examples/bip32.rsbitcoin/examples/create-p2wpkh-address.rsbitcoin/examples/ecdsa-psbt-simple.rsbitcoin/examples/ecdsa-psbt.rsbitcoin/src/sign_message.rsInspect captured patch +16 / −8
diff --git a/bitcoin/examples/bip32.rs b/bitcoin/examples/bip32.rs
index 886a0303..5b86f450 100644
--- a/bitcoin/examples/bip32.rs
+++ b/bitcoin/examples/bip32.rs
@@ -40,6 +40,6 @@ fn main() {
// manually creating indexes this time
let zero = ChildNumber::ZERO_NORMAL;
let public_key = xpub.derive_xpub([zero, zero]).unwrap().public_key;
- let address = Address::p2wpkh(CompressedPublicKey(public_key), KnownHrp::Mainnet);
+ let address = Address::p2wpkh(CompressedPublicKey::from_secp(public_key), KnownHrp::Mainnet);
println!("First receiving address: {address}");
}
diff --git a/bitcoin/examples/create-p2wpkh-address.rs b/bitcoin/examples/create-p2wpkh-address.rs
index d1a32759..a036bc53 100644
--- a/bitcoin/examples/create-p2wpkh-address.rs
+++ b/bitcoin/examples/create-p2wpkh-address.rs
@@ -11,7 +11,7 @@ fn main() {
let private_key = PrivateKey::new(secret_key, Network::Bitcoin);
// Create a compressed Bitcoin public key from the secp256k1 public key.
- let public_key = CompressedPublicKey(public_key);
+ let public_key = CompressedPublicKey::from_secp(public_key);
// Create a Bitcoin P2WPKH address.
let address = Address::p2wpkh(public_key, Network::Bitcoin);
diff --git a/bitcoin/examples/ecdsa-psbt-simple.rs b/bitcoin/examples/ecdsa-psbt-simple.rs
index e57f8dcd..a0339d6f 100644
--- a/bitcoin/examples/ecdsa-psbt-simple.rs
+++ b/bitcoin/examples/ecdsa-psbt-simple.rs
@@ -186,7 +186,7 @@ fn main() {
for (idx, pk) in pk_inputs.iter().enumerate() {
let mut map = BTreeMap::new();
let fingerprint = MASTER_FINGERPRINT.parse::<Fingerprint>().expect("valid fingerprint");
- map.insert(pk.0, (fingerprint, derivation_paths[idx].clone()));
+ map.insert(pk.to_inner(), (fingerprint, derivation_paths[idx].clone()));
bip32_derivations.push(map);
}
psbt.inputs = vec![
@@ -217,7 +217,7 @@ fn main() {
.enumerate()
.map(|(idx, input)| {
let (_, sig) = input.partial_sigs.iter().next().expect("we have one sig");
- Witness::p2wpkh(*sig, pk_inputs[idx].0)
+ Witness::p2wpkh(*sig, pk_inputs[idx].to_inner())
})
.collect();
psbt.inputs.iter_mut().enumerate().for_each(|(idx, input)| {
diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs
index 70a4b3d4..2097557a 100644
--- a/bitcoin/examples/ecdsa-psbt.rs
+++ b/bitcoin/examples/ecdsa-psbt.rs
@@ -206,7 +206,7 @@ impl WatchOnly {
let fingerprint = self.master_fingerprint;
let path = input_derivation_path()?;
let mut map = BTreeMap::new();
- map.insert(pk.0, (fingerprint, path));
+ map.insert(pk.to_inner(), (fingerprint, path));
input.bip32_derivation = map;
let ty = "SIGHASH_ALL".parse::<PsbtSighashType>()?;
diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs
index b66d0b14..5bbd3de0 100644
--- a/bitcoin/src/bip32.rs
+++ b/bitcoin/src/bip32.rs
@@ -874,7 +874,7 @@ impl Xpub {
pub fn to_pub(self) -> CompressedPublicKey { self.to_public_key() }
/// Constructs a new ECDSA compressed public key matching internal public key representation.
- pub fn to_public_key(self) -> CompressedPublicKey { CompressedPublicKey(self.public_key) }
+ pub fn to_public_key(self) -> CompressedPublicKey { CompressedPublicKey::from_secp(self.public_key) }
/// Constructs a new BIP-0340 x-only public key for BIP-0340 signatures and Taproot use matching
/// the internal public key representation.
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index 6e428b71..5ad7d422 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -468,9 +468,17 @@ impl From<&PublicKey> for PubkeyHash {
/// An always-compressed Bitcoin ECDSA public key.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct CompressedPublicKey(pub secp256k1::PublicKey);
+pub struct CompressedPublicKey(secp256k1::PublicKey);
impl CompressedPublicKey {
+ /// Constructs a new compressed public key from the provided secp public key.
+ #[inline]
+ pub fn from_secp(inner: secp256k1::PublicKey) -> Self { Self(inner) }
+
+ /// Returns the inner [`secp256k1::PublicKey`].
+ #[inline]
+ pub fn to_inner(self) -> secp256k1::PublicKey { self.0 }
+
/// Returns bitcoin 160-bit hash of the public key.
pub fn pubkey_hash(&self) -> PubkeyHash { PubkeyHash(hash160::Hash::hash(&self.to_bytes())) }
diff --git a/bitcoin/src/sign_message.rs b/bitcoin/src/sign_message.rs
index 57bca879..a3ed532b 100644
--- a/bitcoin/src/sign_message.rs
+++ b/bitcoin/src/sign_message.rs
@@ -267,7 +267,7 @@ mod tests {
let p2pkh = Address::p2pkh(pubkey, Network::Bitcoin);
assert_eq!(signature2.is_signed_by_address(&p2pkh, msg_hash), Ok(true));
- assert_eq!(pubkey.0, secp256k1::PublicKey::from_secret_key(&privkey));
+ assert_eq!(pubkey.to_inner(), secp256k1::PublicKey::from_secret_key(&privkey));
let signature_base64 = signature.to_base64();
let signature_round_trip =
super::MessageSignature::from_base64(&signature_base64).expect("message 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.