Skip to content

Commit ab6df6f

Browse files
committed
Merge rust-bitcoin#396: Obfuscate shared secret when printing
cf6badf Obfuscate SharedSecret when printing (Tobin Harding) e4be664 Improve rustdocs for displaying secrets (Tobin Harding) 5c7c76e Rename serialize_secret -> secret_bytes (Tobin Harding) 4ded2c0 Use byte instead of i (Tobin Harding) 91106f5 Remove magic number (Tobin Harding) 6dca996 Mention bitcoin_hashes in obfuscated secret msg (Tobin Harding) Pull request description: 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. - Patches 1 - 5: Clean up. - Patch 6: The meat and potatoes. This is the final change needed to: Resolve: rust-bitcoin#226 ACKs for top commit: apoelstra: ACK cf6badf Tree-SHA512: df14e8c5f5815bd76c585a1cd1db42fab6858004ca2cafa9a158b8b04a44c4a11b1260374a6ff82fee540ca955f262b28efae023012de5ac3832e4f5d1d1815e
2 parents 8b2edad + cf6badf commit ab6df6f

File tree

3 files changed

+83
-39
lines changed

3 files changed

+83
-39
lines changed

src/ecdh.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ use core::borrow::Borrow;
2121
use key::{SecretKey, PublicKey};
2222
use ffi::{self, CPtr};
2323
use secp256k1_sys::types::{c_int, c_uchar, c_void};
24+
use constants;
25+
26+
// The logic for displaying shared secrets relies on this (see `secret.rs`).
27+
const SHARED_SECRET_SIZE: usize = constants::SECRET_KEY_SIZE;
2428

2529
/// Enables two parties to create a shared secret without revealing their own secrets.
2630
///
@@ -39,14 +43,15 @@ use secp256k1_sys::types::{c_int, c_uchar, c_void};
3943
/// assert_eq!(sec1, sec2);
4044
/// # }
4145
// ```
42-
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
43-
pub struct SharedSecret([u8; 32]);
46+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
47+
pub struct SharedSecret([u8; SHARED_SECRET_SIZE]);
48+
impl_display_secret!(SharedSecret);
4449

4550
impl SharedSecret {
4651
/// Creates a new shared secret from a pubkey and secret key.
4752
#[inline]
4853
pub fn new(point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
49-
let mut buf = [0u8; 32];
54+
let mut buf = [0u8; SHARED_SECRET_SIZE];
5055
let res = unsafe {
5156
ffi::secp256k1_ecdh(
5257
ffi::secp256k1_context_no_precomp,
@@ -60,6 +65,12 @@ impl SharedSecret {
6065
debug_assert_eq!(res, 1);
6166
SharedSecret(buf)
6267
}
68+
69+
/// Returns the shared secret as a byte value.
70+
#[inline]
71+
pub fn secret_bytes(&self) -> [u8; SHARED_SECRET_SIZE] {
72+
self.0
73+
}
6374
}
6475

6576
impl Borrow<[u8]> for SharedSecret {

src/key.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ impl SecretKey {
212212
SecretKey(sk)
213213
}
214214

215-
/// Serializes the secret key as byte value.
215+
/// Returns the secret key as a byte value.
216216
#[inline]
217-
pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] {
217+
pub fn secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] {
218218
self.0
219219
}
220220

@@ -299,7 +299,7 @@ impl SecretKey {
299299
impl ::serde::Serialize for SecretKey {
300300
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
301301
if s.is_human_readable() {
302-
let mut buf = [0u8; 64];
302+
let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2];
303303
s.serialize_str(::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
304304
} else {
305305
s.serialize_bytes(&self[..])
@@ -809,9 +809,9 @@ impl KeyPair {
809809
KeyPair::new(SECP256K1, rng)
810810
}
811811

812-
/// Serializes the key pair as a secret key byte value.
812+
/// Returns the secret bytes for this key pair.
813813
#[inline]
814-
pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] {
814+
pub fn secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] {
815815
*SecretKey::from_keypair(self).as_ref()
816816
}
817817

@@ -925,8 +925,8 @@ impl str::FromStr for KeyPair {
925925
impl ::serde::Serialize for KeyPair {
926926
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
927927
if s.is_human_readable() {
928-
let mut buf = [0u8; 64];
929-
s.serialize_str(::to_hex(&self.serialize_secret(), &mut buf)
928+
let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2];
929+
s.serialize_str(::to_hex(&self.secret_bytes(), &mut buf)
930930
.expect("fixed-size hex serialization"))
931931
} else {
932932
s.serialize_bytes(&self.0[..])

src/secret.rs

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
use ::core::fmt;
1818
use ::{SecretKey, KeyPair, to_hex};
19+
use ecdh::SharedSecret;
1920
use constants::SECRET_KEY_SIZE;
2021

2122
macro_rules! impl_display_secret {
@@ -35,7 +36,7 @@ macro_rules! impl_display_secret {
3536

3637
hasher.write(DEBUG_HASH_TAG);
3738
hasher.write(DEBUG_HASH_TAG);
38-
hasher.write(&self.serialize_secret());
39+
hasher.write(&self.secret_bytes());
3940
let hash = hasher.finish();
4041

4142
f.debug_tuple(stringify!($thing))
@@ -55,7 +56,7 @@ macro_rules! impl_display_secret {
5556
let tag_hash = sha256::Hash::hash(tag.as_bytes());
5657
engine.input(&tag_hash[..]);
5758
engine.input(&tag_hash[..]);
58-
engine.input(&self.serialize_secret());
59+
engine.input(&self.secret_bytes());
5960
let hash = sha256::Hash::from_engine(engine);
6061

6162
f.debug_tuple(stringify!($thing))
@@ -67,7 +68,7 @@ macro_rules! impl_display_secret {
6768
#[cfg(all(not(feature = "std"), not(feature = "bitcoin_hashes")))]
6869
impl ::core::fmt::Debug for $thing {
6970
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
70-
write!(f, "<secret requires std feature to display>")
71+
write!(f, "<secret requires std or bitcoin_hashes feature to display>")
7172
}
7273
}
7374
}
@@ -91,7 +92,7 @@ pub struct DisplaySecret {
9192
impl fmt::Debug for DisplaySecret {
9293
#[inline]
9394
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94-
let mut slice = [0u8; 64];
95+
let mut slice = [0u8; SECRET_KEY_SIZE * 2];
9596
let hex = to_hex(&self.secret, &mut slice).expect("fixed-size hex serializer failed");
9697
f.debug_tuple("DisplaySecret")
9798
.field(&hex)
@@ -101,8 +102,8 @@ impl fmt::Debug for DisplaySecret {
101102

102103
impl fmt::Display for DisplaySecret {
103104
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104-
for i in &self.secret {
105-
write!(f, "{:02x}", i)?;
105+
for byte in &self.secret {
106+
write!(f, "{:02x}", byte)?;
106107
}
107108
Ok(())
108109
}
@@ -113,33 +114,32 @@ impl SecretKey {
113114
/// little-endian hexadecimal string using the provided formatter.
114115
///
115116
/// This is the only method that outputs the actual secret key value, and, thus,
116-
/// should be used with extreme precaution.
117+
/// should be used with extreme caution.
117118
///
118-
/// # Example
119+
/// # Examples
119120
///
120121
/// ```
121-
/// # #[cfg(all(feature = "std", not(feature = "bitcoin_hashes")))] {
122-
/// use secp256k1::ONE_KEY;
123-
/// let key = ONE_KEY;
124-
/// // Normal display hides value
125-
/// assert_eq!(
126-
/// "SecretKey(#2518682f7819fb2d)",
127-
/// format!("{:?}", key)
128-
/// );
122+
/// # #[cfg(feature = "std")] {
123+
/// let key = secp256k1::ONE_KEY;
124+
///
125+
/// // Normal debug hides value (`Display` is not implemented for `SecretKey`).
126+
/// // E.g., `format!("{:?}", key)` prints "SecretKey(#2518682f7819fb2d)".
127+
///
129128
/// // Here we explicitly display the secret value:
130129
/// assert_eq!(
131130
/// "0000000000000000000000000000000000000000000000000000000000000001",
132131
/// format!("{}", key.display_secret())
133132
/// );
133+
/// // Also, we can explicitly display with `Debug`:
134134
/// assert_eq!(
135-
/// "DisplaySecret(\"0000000000000000000000000000000000000000000000000000000000000001\")",
136-
/// format!("{:?}", key.display_secret())
135+
/// format!("{:?}", key.display_secret()),
136+
/// format!("DisplaySecret(\"{}\")", key.display_secret())
137137
/// );
138138
/// # }
139139
/// ```
140140
#[inline]
141141
pub fn display_secret(&self) -> DisplaySecret {
142-
DisplaySecret { secret: self.serialize_secret() }
142+
DisplaySecret { secret: self.secret_bytes() }
143143
}
144144
}
145145

@@ -153,33 +153,66 @@ impl KeyPair {
153153
/// # Example
154154
///
155155
/// ```
156-
/// # #[cfg(all(feature = "std", not(feature = "bitcoin_hashes")))] {
156+
/// # #[cfg(feature = "std")] {
157157
/// use secp256k1::ONE_KEY;
158158
/// use secp256k1::KeyPair;
159159
/// use secp256k1::Secp256k1;
160160
///
161161
/// let secp = Secp256k1::new();
162162
/// let key = ONE_KEY;
163163
/// let key = KeyPair::from_secret_key(&secp, key);
164-
///
165-
/// // Normal display hides value
166-
/// assert_eq!(
167-
/// "KeyPair(#2518682f7819fb2d)",
168-
/// format!("{:?}", key)
169-
/// );
170164
/// // Here we explicitly display the secret value:
171165
/// assert_eq!(
172166
/// "0000000000000000000000000000000000000000000000000000000000000001",
173167
/// format!("{}", key.display_secret())
174168
/// );
169+
/// // Also, we can explicitly display with `Debug`:
170+
/// assert_eq!(
171+
/// format!("{:?}", key.display_secret()),
172+
/// format!("DisplaySecret(\"{}\")", key.display_secret())
173+
/// );
174+
/// # }
175+
/// ```
176+
#[inline]
177+
pub fn display_secret(&self) -> DisplaySecret {
178+
DisplaySecret { secret: self.secret_bytes() }
179+
}
180+
}
181+
182+
impl SharedSecret {
183+
/// Formats the explicit byte value of the shared secret kept inside the type as a
184+
/// little-endian hexadecimal string using the provided formatter.
185+
///
186+
/// This is the only method that outputs the actual shared secret value, and, thus,
187+
/// should be used with extreme caution.
188+
///
189+
/// # Examples
190+
///
191+
/// ```
192+
/// # #[cfg(not(fuzzing))]
193+
/// # #[cfg(feature = "std")] {
194+
/// # use std::str::FromStr;
195+
/// # use secp256k1::{SecretKey, PublicKey};
196+
/// use secp256k1::ecdh::SharedSecret;
197+
///
198+
/// # 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");
199+
/// # let sk = SecretKey::from_str("57f0148f94d13095cfda539d0da0d1541304b678d8b36e243980aab4e1b7cead").unwrap();
200+
///
201+
/// let secret = SharedSecret::new(&pk, &sk);
202+
/// // Here we explicitly display the secret value:
203+
/// assert_eq!(
204+
/// format!("{}", secret.display_secret()),
205+
/// "cf05ae7da039ddce6d56dd57d3000c6dd91c6f1695eae47e05389f11e2467043"
206+
/// );
207+
/// // Also, we can explicitly display with `Debug`:
175208
/// assert_eq!(
176-
/// "DisplaySecret(\"0000000000000000000000000000000000000000000000000000000000000001\")",
177-
/// format!("{:?}", key.display_secret())
209+
/// format!("{:?}", secret.display_secret()),
210+
/// format!("DisplaySecret(\"{}\")", secret.display_secret())
178211
/// );
179212
/// # }
180213
/// ```
181214
#[inline]
182215
pub fn display_secret(&self) -> DisplaySecret {
183-
DisplaySecret { secret: self.serialize_secret() }
216+
DisplaySecret { secret: self.secret_bytes() }
184217
}
185218
}

0 commit comments

Comments
 (0)