From d12edb605273ec90d042f958de5f90ce3a909732 Mon Sep 17 00:00:00 2001 From: Tobin Harding Date: Wed, 9 Feb 2022 05:33:09 +0000 Subject: [PATCH] Obfuscate SharedSecret when printing Currently printing the `SharedSecret` using `Display` or `Debug` prints the real secret, this is sub-optimal. We have a solution for other secrets in the project where printing is obfuscated and we provide a `display_secret` method for explicitly printing. Mirror the logic for other secrets and obfuscate the `SharedSecret` when printing. --- src/ecdh.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/src/ecdh.rs b/src/ecdh.rs index fd442522c..450fd02e7 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -16,12 +16,13 @@ //! Support for shared secret computations //! -use core::ptr; +use core::{fmt, ptr}; use core::ops::{FnMut, Deref}; use key::{SecretKey, PublicKey}; use ffi::{self, CPtr}; use secp256k1_sys::types::{c_int, c_uchar, c_void}; +use to_hex; /// The buffer size (in bytes) used by `SharedSecret`. const BUF_SIZE: usize = 256; @@ -48,7 +49,7 @@ pub struct SharedSecret { data: [u8; BUF_SIZE], len: usize, } -impl_raw_debug!(SharedSecret); +impl_display_secret!(SharedSecret); // This implements `From` for all `[u8; N]` arrays from 128bits(16 byte) to 2048bits allowing known hash lengths. // Lower than 128 bits isn't resistant to collisions any more. @@ -88,6 +89,11 @@ impl SharedSecret { debug_assert!(len <= self.data.len()); self.len = len; } + + /// Serializes the whole shared secret buffer as byte value. + pub(crate) fn serialize_secret(&self) -> [u8; BUF_SIZE] { + self.data + } } impl PartialEq for SharedSecret { @@ -183,6 +189,68 @@ impl SharedSecret { y.copy_from_slice(&xy[32..]); hash_function(x, y) } + + /// Formats the explicit byte value of the shared secret kept inside the type as a + /// little-endian hexadecimal string using the provided formatter. + /// + /// This is the only method that outputs the actual shared secret value, and, thus, + /// should be used with extreme precaution. + /// + /// # Example + /// + /// ``` + /// # #[cfg(all(feature = "std", not(feature = "bitcoin_hashes")))] { + /// # use std::str::FromStr; + /// # use secp256k1::{SecretKey, PublicKey}; + /// use secp256k1::ecdh::SharedSecret; + /// # let pk = PublicKey::from_slice(&[3, 23, 183, 225, 206, 31, 159, 148, 195, 42, 67, 115, 146, 41, 248, 140, 11, 3, 51, 41, 111, 180, 110, 143, 114, 134, 88, 73, 198, 174, 52, 184, 78]).expect("hard coded slice should parse correctly"); + /// # let sk = SecretKey::from_str("57f0148f94d13095cfda539d0da0d1541304b678d8b36e243980aab4e1b7cead").unwrap(); + /// let secret = SharedSecret::new(&pk, &sk); + /// // Normal display hides the value. + /// assert_eq!(format!("{:?}", secret), "SharedSecret(#6b778f6da1cd7d46)"); + /// // Here we explicitly display the secret value: + /// assert_eq!( + /// format!("{}", secret.display_secret()), + /// "cf05ae7da039ddce6d56dd57d3000c6dd91c6f1695eae47e05389f11e24670430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + /// // Can explicitly display with `{}` and `{:?}`. + /// assert_eq!( + /// format!("{:?}", secret.display_secret()), + /// format!("DisplaySecret(\"{}\")", secret.display_secret())); + /// # } + /// ``` + #[inline] + pub fn display_secret(&self) -> DisplaySecret { + DisplaySecret { secret: self.serialize_secret() } + } +} + +/// Helper struct for safely printing a shared secret. +/// +/// Formats the explicit byte value of the secret as a little-endian hexadecimal string using the +/// provided formatter. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DisplaySecret { + secret: [u8; BUF_SIZE] +} + +impl fmt::Debug for DisplaySecret { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut slice = [0u8; BUF_SIZE * 2]; + let hex = to_hex(&self.secret, &mut slice).expect("fixed-size hex serializer failed"); + f.debug_tuple("DisplaySecret") + .field(&hex) + .finish() + } +} + +impl fmt::Display for DisplaySecret { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for i in &self.secret { + write!(f, "{:02x}", i)?; + } + Ok(()) + } } #[cfg(test)]