From 0e161ac7ecf97ab7e6715691f0a8b0f400f35e3b Mon Sep 17 00:00:00 2001 From: carlos santos Date: Tue, 7 Oct 2025 22:15:07 -0300 Subject: [PATCH] bip39: implement Debug for mnemonic without leaking secrets Replace the Debug output with a manual impl that prints only the language and omits mnemonic words/entropy to avoid secret leakage in logs. Approach: use `debug_struct("Mnemonic").field("lang", &self.language()).finish()` to keep MSRV compatibility (no `finish_non_exhaustive`). Refs:#93 --- src/lib.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index bd9223e..64d42ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,7 +174,7 @@ impl error::Error for Error {} /// the Cargo features.) /// /// Supported number of words are 12, 15, 18, 21, and 24. -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))] pub struct Mnemonic { /// The language the mnemonic. @@ -655,6 +655,15 @@ impl fmt::Display for Mnemonic { } } +impl fmt::Debug for Mnemonic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Mnemonic") + .field("lang", &self.language()) + // TODO(msrv>=1.53): switch to `.finish_non_exhaustive()` once MSRV 1.41 is dropped. + .finish() + } +} + impl str::FromStr for Mnemonic { type Err = Error; @@ -1005,6 +1014,25 @@ mod tests { assert_eq!(Mnemonic::from_entropy(&vec![b'x'; 36]), Err(Error::BadEntropyBitCount(288))); } + #[test] + fn debug_does_not_leak_phrase() { + let m = Mnemonic::from_entropy(&[0u8; 16]).unwrap(); + + let dbg = format!("{:?}", m); + + assert!(dbg.starts_with("Mnemonic {") || dbg.starts_with("Mnemonic(") || dbg.contains("Mnemonic"), + "Debug output doesn't look like a struct: {}", dbg); + assert!(dbg.contains("lang"), "Debug must include language info: {}", dbg); + + for w in m.words() { + assert!( + !dbg.contains(w), + "Debug leaked sensitive word: {} (output: {})", + w, dbg + ); + } + } + #[cfg(all(feature = "japanese", feature = "std"))] #[test] fn test_vectors_japanese() {