Skip to content

Commit 39f1e65

Browse files
committed
Using dedicated safe_debug for secret key containing types
Debug-print secrets as tagged hashes Refactoring Display/Debug for secret values with display_secret
1 parent 12dd28a commit 39f1e65

File tree

5 files changed

+134
-6
lines changed

5 files changed

+134
-6
lines changed

src/key.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use ffi::{self, CPtr};
2929
/// Secret 256-bit key used as `x` in an ECDSA signature
3030
pub struct SecretKey(pub(crate) [u8; constants::SECRET_KEY_SIZE]);
3131
impl_array_newtype!(SecretKey, u8, constants::SECRET_KEY_SIZE);
32-
impl_pretty_debug!(SecretKey);
32+
impl_display_secret!(SecretKey);
3333

3434
impl fmt::LowerHex for SecretKey {
3535
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -482,7 +482,7 @@ impl Ord for PublicKey {
482482
#[cfg(test)]
483483
mod test {
484484
use Secp256k1;
485-
use from_hex;
485+
use {from_hex, to_hex};
486486
use super::super::Error::{InvalidPublicKey, InvalidSecretKey};
487487
use super::{PublicKey, SecretKey};
488488
use super::super::constants;
@@ -676,7 +676,11 @@ mod test {
676676
let (sk, _) = s.generate_keypair(&mut DumbRng(0));
677677

678678
assert_eq!(&format!("{:?}", sk),
679-
"SecretKey(0100000000000000020000000000000003000000000000000400000000000000)");
679+
"SecretKey(#d3e0c51a23169bb5)");
680+
681+
let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2];
682+
assert_eq!(to_hex(&sk[..], &mut buf).unwrap(),
683+
"0100000000000000020000000000000003000000000000000400000000000000");
680684
}
681685

682686
#[test]

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ pub use secp256k1_sys as ffi;
140140

141141
#[macro_use]
142142
mod macros;
143+
#[macro_use]
144+
mod secret;
143145
mod context;
144146
pub mod constants;
145147
pub mod ecdh;

src/macros.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ macro_rules! impl_pretty_debug {
1818
impl ::core::fmt::Debug for $thing {
1919
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
2020
write!(f, "{}(", stringify!($thing))?;
21-
for i in self[..].iter().cloned() {
21+
for i in &self[..] {
2222
write!(f, "{:02x}", i)?;
2323
}
24-
write!(f, ")")
24+
f.write_str(")")
2525
}
2626
}
2727
}

src/schnorrsig.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ impl str::FromStr for Signature {
7676
}
7777

7878
/// Opaque data structure that holds a keypair consisting of a secret and a public key.
79-
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
79+
#[derive(Clone)]
8080
pub struct KeyPair(ffi::KeyPair);
81+
impl_display_secret!(KeyPair);
8182

8283
/// A Schnorr public key, used for verification of Schnorr signatures
8384
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]

src/secret.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Bitcoin secp256k1 bindings
2+
// Written in 2021 by
3+
// Maxim Orlovsky <orlovsky@pandoracore.com>
4+
//
5+
// To the extent possible under law, the author(s) have dedicated all
6+
// copyright and related and neighboring rights to this software to
7+
// the public domain worldwide. This software is distributed without
8+
// any warranty.
9+
//
10+
// You should have received a copy of the CC0 Public Domain Dedication
11+
// along with this software.
12+
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13+
//
14+
15+
//! Helpers for displaying secret values
16+
17+
use ::core::fmt;
18+
use SecretKey;
19+
20+
macro_rules! impl_display_secret {
21+
// Default hasher exists only in standard library and not alloc
22+
($thing:ident) => {
23+
#[cfg(feature = "std")]
24+
impl ::core::fmt::Debug for $thing {
25+
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
26+
use ::core::hash::Hasher;
27+
const DEBUG_HASH_TAG: &[u8] = &[
28+
0x66, 0xa6, 0x77, 0x1b, 0x9b, 0x6d, 0xae, 0xa1, 0xb2, 0xee, 0x4e, 0x07, 0x49,
29+
0x4a, 0xac, 0x87, 0xa9, 0xb8, 0x5b, 0x4b, 0x35, 0x02, 0xaa, 0x6d, 0x0f, 0x79,
30+
0xcb, 0x63, 0xe6, 0xf8, 0x66, 0x22
31+
]; // =SHA256(b"rust-secp256k1DEBUG");
32+
33+
let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
34+
35+
hasher.write(DEBUG_HASH_TAG);
36+
hasher.write(DEBUG_HASH_TAG);
37+
hasher.write(&self.0[..]);
38+
let hash = hasher.finish();
39+
40+
f.debug_tuple(stringify!($thing))
41+
.field(&format_args!("#{:016x}", hash))
42+
.finish()
43+
}
44+
}
45+
}
46+
}
47+
48+
/// Helper struct for safely printing secrets (like [`SecretKey`] value).
49+
/// Formats the explicit byte value of the secret kept inside the type as a
50+
/// little-endian hexadecimal string using the provided formatter.
51+
///
52+
/// Secrets should not implement neither [`Debug`] and [`Display`] traits directly,
53+
/// and instead provide `fn display_secret<'a>(&'a self) -> DisplaySecret<'a>`
54+
/// function to be used in different display contexts (see "examples" below).
55+
///
56+
/// # Example
57+
///
58+
/// ```
59+
/// use secp256k1::key::ONE_KEY;
60+
/// let key = ONE_KEY;
61+
/// println!("{}", key.display_secret());
62+
/// println!("{:?}", key.display_secret());
63+
/// ```
64+
///
65+
/// [`Display`]: fmt::Display
66+
/// [`Debug`]: fmt::Debug
67+
pub struct DisplaySecret<'a> {
68+
secret: &'a [u8]
69+
}
70+
71+
impl<'a> fmt::Debug for DisplaySecret<'a> {
72+
#[inline]
73+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74+
fmt::Debug::fmt(&self.secret, f)
75+
}
76+
}
77+
78+
impl<'a> fmt::Display for DisplaySecret<'a> {
79+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80+
for i in self.secret {
81+
write!(f, "{:02x}", i)?;
82+
}
83+
Ok(())
84+
}
85+
}
86+
87+
impl SecretKey {
88+
/// Formats the explicit byte value of the secret key kept inside the type as a
89+
/// little-endian hexadecimal string using the provided formatter.
90+
///
91+
/// This is the only method that outputs the actual secret key value, and, thus,
92+
/// should be used with extreme precaution.
93+
///
94+
/// # Example
95+
///
96+
/// ```
97+
/// use secp256k1::key::ONE_KEY;
98+
/// let key = ONE_KEY;
99+
/// println!("{}", key.display_secret());
100+
/// println!("{:?}", key.display_secret());
101+
/// ```
102+
#[inline]
103+
pub fn display_secret<'a>(&'a self) -> DisplaySecret<'a> {
104+
DisplaySecret { secret: &self.0 }
105+
}
106+
}
107+
108+
// TODO: Uncomment the below once #308 got merged
109+
/*
110+
impl KeyPair {
111+
/// Formats the explicit byte value of the secret key kept inside the type as a
112+
/// little-endian hexadecimal string using the provided formatter.
113+
///
114+
/// This is the only method that outputs the actual secret key value, and, thus,
115+
/// should be used with extreme precaution.
116+
#[inline]
117+
pub fn display_secret<'a>(&'a self) -> DisplaySecret<'a> {
118+
DisplaySecret(self.serialize_sec())
119+
}
120+
}
121+
*/

0 commit comments

Comments
 (0)