Add {Lower,Upper}Hex to ecdsa::Signature
What changed, and why it matters
This commit adds new ways to print ECDSA Bitcoin signatures as hexadecimal text (lowercase and uppercase), matching formatting already available for Taproot signatures. It also makes the existing Display implementation reuse the new LowerHex implementation. There is no security fix or behavior change that affects signatures themselves—only how they look when formatted as text.
No security action required; this is a routine API-consistency enhancement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch implements std::fmt::LowerHex and std::fmt::UpperHex for ecdsa::Signature under the ‘hex’ feature, and refactors fmt::Display to delegate to LowerHex. Serialization output is unchanged; only formatting traits are extended, with unit tests verifying {:x} and {:X} produce the expected hex strings.
Changed components
crypto/src/ecdsa.rsInspect captured patch +43 / −5
diff --git a/crypto/src/ecdsa.rs b/crypto/src/ecdsa.rs
index 8e6a603a..4c52f6d7 100644
--- a/crypto/src/ecdsa.rs
+++ b/crypto/src/ecdsa.rs
@@ -100,9 +100,23 @@ impl Signature {
#[cfg(feature = "hex")]
impl fmt::Display for Signature {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
+}
+
+#[cfg(feature = "hex")]
+impl fmt::LowerHex for Signature {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.serialize().fmt(f)
+ }
+}
+
+#[cfg(feature = "hex")]
+impl fmt::UpperHex for Signature {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(&self.serialize(), f)
+ self.serialize().fmt(f)
}
}
@@ -437,14 +451,20 @@ mod tests {
#[cfg(feature = "alloc")]
const TEST_SIGNATURE_HEX: &str = "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45";
- #[test]
#[cfg(feature = "hex")]
#[cfg(feature = "alloc")]
- fn iterate_serialized_signature() {
- let sig = Signature {
+ fn sig() -> Signature {
+ Signature {
signature: secp256k1::ecdsa::Signature::from_str(TEST_SIGNATURE_HEX).unwrap(),
sighash_type: EcdsaSighashType::All,
- };
+ }
+ }
+
+ #[test]
+ #[cfg(feature = "hex")]
+ #[cfg(feature = "alloc")]
+ fn iterate_serialized_signature() {
+ let sig = sig();
assert_eq!(sig.serialize().iter().copied().collect::<Vec<u8>>(), sig.to_vec());
}
@@ -465,4 +485,22 @@ mod tests {
assert_eq!(sig_format, ser_format);
assert_eq!(sig_format, "3046");
}
+
+ #[test]
+ #[cfg(feature = "hex")]
+ #[cfg(feature = "alloc")]
+ fn sig_lower_hex() {
+ let want = "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab4501";
+ let got = alloc::format!("{:x}", sig());
+ assert_eq!(got, want);
+ }
+
+ #[test]
+ #[cfg(feature = "hex")]
+ #[cfg(feature = "alloc")]
+ fn sig_upper_hex() {
+ let want = "3046022100839C1FBC5304DE944F697C9F4B1D01D1FAEBA32D751C0F7ACB21AC8A0F436A72022100E89BD46BB3A5A62ADC679F659B7CE876D83EE297C7A5587B2011C4FCC72EAB4501";
+ let got = alloc::format!("{:X}", sig());
+ assert_eq!(got, want);
+ }
}
Why this scored 20/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.