Skip to content

Commit

Permalink
Obfuscate SharedSecret when printing
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tcharding committed Feb 9, 2022
1 parent c841696 commit d12edb6
Showing 1 changed file with 70 additions and 2 deletions.
72 changes: 70 additions & 2 deletions src/ecdh.rs
Expand Up @@ -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;
Expand All @@ -48,7 +49,7 @@ pub struct SharedSecret {
data: [u8; BUF_SIZE],
len: usize,
}
impl_raw_debug!(SharedSecret);
impl_display_secret!(SharedSecret);

// This implements `From<N>` 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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)]
Expand Down

0 comments on commit d12edb6

Please sign in to comment.