From 85652359e89b4ead08fd8cb1a5e398cc2347e90d Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 12 Feb 2025 03:30:31 +1100 Subject: [PATCH 1/2] hashes: Derive Copy and Clone for Hkdf Currently the `Hkdf` type does not derive any traits. We would like to derive the common set of traits but there are a bunch reasons we can't; - Don't want to leak secrets in `Debug`. - Don't want to enable timing attacks with Eq/Ord and friends. For now just derive `Copy` and `Clone`. We will then implement `Debug` manually. --- hashes/src/hkdf/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hashes/src/hkdf/mod.rs b/hashes/src/hkdf/mod.rs index f9f027857..b2cba0838 100644 --- a/hashes/src/hkdf/mod.rs +++ b/hashes/src/hkdf/mod.rs @@ -32,6 +32,7 @@ impl fmt::Display for MaxLengthError { impl std::error::Error for MaxLengthError {} /// HMAC-based Extract-and-Expand Key Derivation Function (HKDF). +#[derive(Copy, Clone)] pub struct Hkdf { /// Pseudorandom key based on the extract step. prk: Hmac, From da8b85ed7cf34c0510c0b64c67477d3819bee369 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 17 Feb 2025 12:20:40 +1100 Subject: [PATCH 2/2] Implement Debug for Hkdf We would like to implement `Debug` for `Hkdf` but the inner field is secret so we cannot derive an impl. Use a tagged hash engine to hash the secret. --- hashes/src/hkdf/mod.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/hashes/src/hkdf/mod.rs b/hashes/src/hkdf/mod.rs index b2cba0838..92085df76 100644 --- a/hashes/src/hkdf/mod.rs +++ b/hashes/src/hkdf/mod.rs @@ -106,6 +106,26 @@ where } } +impl fmt::Debug for Hkdf { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + use crate::{sha256t, sha256t_tag}; + + struct Fingerprint([u8; 8]); // Print 16 hex characters as a fingerprint. + + impl fmt::Debug for Fingerprint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { crate::debug_hex(&self.0, f) } + } + + sha256t_tag! { + pub struct Tag = hash_str("bitcoin_hashes1DEBUG"); + } + + let hash = sha256t::Hash::::hash(self.prk.as_ref()); + let fingerprint = Fingerprint(core::array::from_fn(|i| hash.as_byte_array()[i])); + f.debug_tuple("Hkdf").field(&format_args!("#{:?}", fingerprint)).finish() + } +} + #[cfg(test)] #[cfg(feature = "alloc")] mod tests { @@ -192,4 +212,15 @@ mod tests { "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865" ); } + + #[test] + fn debug() { + let salt = Vec::from_hex("000102030405060708090a0b0c").unwrap(); + let ikm = Vec::from_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); + + let hkdf = Hkdf::::new(&salt, &ikm); + let debug = alloc::format!("{:?}", hkdf); + + assert_eq!(debug, "Hkdf(#ec7bd36ab2ed4045)"); + } }