Replace Signature Display with call to SerializedSignature
What changed, and why it matters
This commit fixes a formatting bug in how Bitcoin ECDSA signatures are printed as text. Previously, the signature was printed in two separate pieces (the DER-encoded signature and the sighash byte), which could mishandle formatting options like width or precision. Now it prints a single pre-serialized object, so formatting behaves consistently. This is a correctness bug, not a cryptographic vulnerability, and there is no evidence it enables code execution or theft of funds.
No immediate security action required. Treat as a normal bug-fix release. If downstream code relied on the old Display output under format flags, review those call sites for behavioral changes, though the new behavior is the correct one.
Security signals we found
Incorrect format-flag handling in Display impl
Behavioral inconsistency between Signature::fmt and SerializedSignature::fmt
Regression test added for format-string parity
Evidence from the diff
The Display implementation for ecdsa::Signature previously called fmt::LowerHex::fmt independently on the DER-serialized signature and on the sighash_type byte. Because each call receives the original Formatter, format flags (e.g., precision, width, fill) could be applied incorrectly or inconsistently across the two parts. The patch replaces the two-step formatting with a single call to Display::fmt on self.serialize(), which returns an ecdsa::SerializedSignature whose own Display impl handles the whole byte string uniformly. A regression test verifies that format!(“{:.4}”, sig) now yields the same truncated output as formatting the serialized form directly.
Changed components
crypto/src/ecdsa.rsecdsa::Signature Display formatterInspect captured patch +18 / −2
diff --git a/crypto/src/ecdsa.rs b/crypto/src/ecdsa.rs
index 4ca2a9c6..8e6a603a 100644
--- a/crypto/src/ecdsa.rs
+++ b/crypto/src/ecdsa.rs
@@ -102,8 +102,7 @@ impl Signature {
impl fmt::Display for Signature {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::LowerHex::fmt(&self.signature.serialize_der().as_hex(), f)?;
- fmt::LowerHex::fmt(&[self.sighash_type as u8].as_hex(), f)
+ fmt::Display::fmt(&self.serialize(), f)
}
}
@@ -449,4 +448,21 @@ mod tests {
assert_eq!(sig.serialize().iter().copied().collect::<Vec<u8>>(), sig.to_vec());
}
+
+ #[test]
+ #[cfg(feature = "hex")]
+ #[cfg(feature = "alloc")]
+ fn signature_display_matches_serialized_signature() {
+ use alloc::format;
+
+ let sig = Signature {
+ signature: secp256k1::ecdsa::Signature::from_str(TEST_SIGNATURE_HEX).unwrap(),
+ sighash_type: EcdsaSighashType::All,
+ };
+
+ let sig_format = format!("{:.4}", sig);
+ let ser_format = format!("{:.4}", sig.serialize());
+ assert_eq!(sig_format, ser_format);
+ assert_eq!(sig_format, "3046");
+ }
}
Why this scored 26/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.