taproot-primitives: Add hex feature and correctly gate hex usage
What changed, and why it matters
This commit is a feature-gating cleanup, not a fix for an active security flaw. The taproot-primitives crate was always enabling hex (base-16 string) support for its hash types, even when downstream users did not ask for it. The change adds an explicit 'hex' feature, turns it off by default, and makes the bitcoin crate request it explicitly. This gives users more control and avoids pulling in unneeded functionality, but it does not patch a vulnerability that can be directly exploited.
No urgent action. Treat as a normal dependency/feature cleanup. Downstream users who relied on default hex support from taproot-primitives may need to enable the new 'hex' feature explicitly if they no longer use the default feature set.
Security signals we found
Feature-gating reduces default dependency surface and attack surface
No change to cryptographic algorithms, hashing, parsing, or consensus rules
No input validation, memory safety, or serialization correctness fixes present
Commit message frames change as feature/dependency cleanup, not as a security fix
Evidence from the diff
The patch introduces a new ‘hex’ feature in taproot-primitives/Cargo.toml and removes the unconditional ‘hex’ feature from the bitcoin_hashes dependency. It then gates hashes::impl_hex_for_newtype! and the hex-dependent serde macros for TapLeafHash, TapNodeHash, and TapTweakHash behind #[cfg(feature = “hex”)]. The bitcoin crate opts into the feature so its own behavior is unchanged. This is a build-configuration hygiene change; no cryptographic or consensus code is altered.
Changed components
taproot-primitives/Cargo.tomltaproot-primitives/src/lib.rsbitcoin/Cargo.tomlInspect captured patch +10 / −4
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 49b93478..f995a117 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -38,7 +38,7 @@ io = { package = "bitcoin-io", path = "../io", version = "0.5.0", default-featur
network = { package = "bitcoin-network-kind", path = "../network", version = "0.1.0", default-features = false, features = ["alloc"]}
primitives = { package = "bitcoin-primitives", path = "../primitives", version = "0.102.0", default-features = false, features = ["alloc", "hex"] }
secp256k1 = { version = "0.32.0-beta.2", default-features = false, features = ["alloc"] }
-taproot-primitives = { package = "bitcoin-taproot-primitives", path = "../taproot-primitives", version = "0.1.0", default-features = false, features = ["alloc"] }
+taproot-primitives = { package = "bitcoin-taproot-primitives", path = "../taproot-primitives", version = "0.1.0", default-features = false, features = ["alloc", "hex"] }
units = { package = "bitcoin-units", path = "../units", version = "0.3.0", default-features = false, features = ["alloc"] }
arbitrary = { version = "1.4.1", optional = true }
diff --git a/taproot-primitives/Cargo.toml b/taproot-primitives/Cargo.toml
index 939e5d03..12bda6c6 100644
--- a/taproot-primitives/Cargo.toml
+++ b/taproot-primitives/Cargo.toml
@@ -13,15 +13,16 @@ rust-version = "1.74.0"
exclude = ["tests", "contrib"]
[features]
-default = ["std"]
+default = ["std", "hex"]
std = ["alloc", "crypto/std", "hashes/std", "internals/std", "serde?/std", "secp256k1/std"]
alloc = ["crypto/alloc", "hashes/alloc", "internals/alloc", "secp256k1/alloc"]
serde = ["dep:serde", "crypto/serde", "hashes/serde", "internals/serde", "secp256k1/serde"]
arbitrary = ["crypto/arbitrary", "dep:arbitrary", "hashes/arbitrary", "secp256k1/arbitrary"]
+hex = ["hashes/hex"]
[dependencies]
crypto = { package = "bitcoin-crypto", path = "../crypto", default-features = false }
-hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.20.0", default-features = false, features = ["hex"] }
+hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.20.0", default-features = false }
internals = { package = "bitcoin-internals", path = "../internals", version = "0.5.0" }
arbitrary = { version = "1.4.1", optional = true }
diff --git a/taproot-primitives/src/lib.rs b/taproot-primitives/src/lib.rs
index 41085662..9375d3f5 100644
--- a/taproot-primitives/src/lib.rs
+++ b/taproot-primitives/src/lib.rs
@@ -66,9 +66,10 @@ hash_newtype! {
/// This is used for computing tapscript script spend hash.
pub struct TapLeafHash(sha256t::Hash<TapLeafTag>);
}
-
+#[cfg(feature = "hex")]
hashes::impl_hex_for_newtype!(TapLeafHash);
#[cfg(feature = "serde")]
+#[cfg(feature = "hex")]
hashes::impl_serde_for_newtype!(TapLeafHash);
sha256t_tag! {
@@ -83,8 +84,10 @@ hash_newtype! {
pub struct TapNodeHash(sha256t::Hash<TapBranchTag>);
}
+#[cfg(feature = "hex")]
hashes::impl_hex_for_newtype!(TapNodeHash);
#[cfg(feature = "serde")]
+#[cfg(feature = "hex")]
hashes::impl_serde_for_newtype!(TapNodeHash);
sha256t_tag! {
@@ -98,8 +101,10 @@ hash_newtype! {
pub struct TapTweakHash(sha256t::Hash<TapTweakTag>);
}
+#[cfg(feature = "hex")]
hashes::impl_hex_for_newtype!(TapTweakHash);
#[cfg(feature = "serde")]
+#[cfg(feature = "hex")]
hashes::impl_serde_for_newtype!(TapTweakHash);
impl From<TapLeafHash> for TapNodeHash {
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.