Skip to content

Commit 6810c2b

Browse files
committed
Dedicated display_secret fn for secret-containing types
Debug-print secrets as tagged hashes Refactoring Display/Debug for secret values with display_secret
1 parent 635a6ae commit 6810c2b

File tree

5 files changed

+177
-26
lines changed

5 files changed

+177
-26
lines changed

src/key.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,15 @@ use core::{fmt, str};
2121

2222
use super::{from_hex, Secp256k1};
2323
use super::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey};
24-
use Signing;
24+
use ::{Signing};
2525
use Verification;
2626
use constants;
2727
use ffi::{self, CPtr};
2828

2929
/// Secret 256-bit key used as `x` in an ECDSA signature
3030
pub struct SecretKey([u8; constants::SECRET_KEY_SIZE]);
3131
impl_array_newtype!(SecretKey, u8, constants::SECRET_KEY_SIZE);
32-
impl_pretty_debug!(SecretKey);
33-
34-
impl fmt::LowerHex for SecretKey {
35-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36-
for ch in &self.0[..] {
37-
write!(f, "{:02x}", *ch)?;
38-
}
39-
Ok(())
40-
}
41-
}
42-
43-
impl fmt::Display for SecretKey {
44-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45-
fmt::LowerHex::fmt(self, f)
46-
}
47-
}
32+
impl_display_secret!(SecretKey);
4833

4934
impl str::FromStr for SecretKey {
5035
type Err = Error;
@@ -164,6 +149,12 @@ impl SecretKey {
164149
SecretKey(sk)
165150
}
166151

152+
/// Serialize the secret key as byte value
153+
#[inline]
154+
pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] {
155+
self.0
156+
}
157+
167158
#[inline]
168159
/// Negates one secret key.
169160
pub fn negate_assign(
@@ -233,7 +224,8 @@ impl SecretKey {
233224
impl ::serde::Serialize for SecretKey {
234225
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
235226
if s.is_human_readable() {
236-
s.collect_str(self)
227+
let mut buf = [0u8; 64];
228+
s.serialize_str(::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
237229
} else {
238230
s.serialize_bytes(&self[..])
239231
}
@@ -516,7 +508,7 @@ impl Ord for PublicKey {
516508
#[cfg(test)]
517509
mod test {
518510
use Secp256k1;
519-
use from_hex;
511+
use {from_hex, to_hex};
520512
use super::super::Error::{InvalidPublicKey, InvalidSecretKey};
521513
use super::{PublicKey, SecretKey};
522514
use super::super::constants;
@@ -710,7 +702,11 @@ mod test {
710702
let (sk, _) = s.generate_keypair(&mut DumbRng(0));
711703

712704
assert_eq!(&format!("{:?}", sk),
713-
"SecretKey(0100000000000000020000000000000003000000000000000400000000000000)");
705+
"SecretKey(#d3e0c51a23169bb5)");
706+
707+
let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2];
708+
assert_eq!(to_hex(&sk[..], &mut buf).unwrap(),
709+
"0100000000000000020000000000000003000000000000000400000000000000");
714710
}
715711

716712
#[test]
@@ -733,7 +729,7 @@ mod test {
733729
let pk = PublicKey::from_slice(&[0x02, 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66]).expect("pk");
734730

735731
assert_eq!(
736-
sk.to_string(),
732+
sk.display_secret().to_string(),
737733
"01010101010101010001020304050607ffff0000ffff00006363636363636363"
738734
);
739735
assert_eq!(

src/lib.rs

Lines changed: 4 additions & 2 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;
@@ -851,8 +853,8 @@ fn from_hex(hex: &str, target: &mut [u8]) -> Result<usize, ()> {
851853
}
852854

853855
/// Utility function used to encode hex into a target u8 buffer. Returns
854-
/// a reference to the target buffer as an str.
855-
// it returns an error if the target buffer isn't big enough
856+
/// a reference to the target buffer as an str. Returns an error if the target
857+
/// buffer isn't big enough.
856858
#[inline]
857859
fn to_hex<'a>(src: &[u8], target: &'a mut [u8]) -> Result<&'a str, ()> {
858860
let hex_len = src.len() * 2;

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: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Bitcoin secp256k1 bindings
2+
// Written in 2021 by
3+
// Maxim Orlovsky <[email protected]>
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, schnorrsig::KeyPair, to_hex};
19+
use constants::SECRET_KEY_SIZE;
20+
21+
macro_rules! impl_display_secret {
22+
// Default hasher exists only in standard library and not alloc
23+
($thing:ident) => {
24+
#[cfg(feature = "std")]
25+
impl ::core::fmt::Debug for $thing {
26+
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
27+
use ::core::hash::Hasher;
28+
const DEBUG_HASH_TAG: &[u8] = &[
29+
0x66, 0xa6, 0x77, 0x1b, 0x9b, 0x6d, 0xae, 0xa1, 0xb2, 0xee, 0x4e, 0x07, 0x49,
30+
0x4a, 0xac, 0x87, 0xa9, 0xb8, 0x5b, 0x4b, 0x35, 0x02, 0xaa, 0x6d, 0x0f, 0x79,
31+
0xcb, 0x63, 0xe6, 0xf8, 0x66, 0x22
32+
]; // =SHA256(b"rust-secp256k1DEBUG");
33+
34+
let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
35+
36+
hasher.write(DEBUG_HASH_TAG);
37+
hasher.write(DEBUG_HASH_TAG);
38+
hasher.write(&self.serialize_secret());
39+
let hash = hasher.finish();
40+
41+
f.debug_tuple(stringify!($thing))
42+
.field(&format_args!("#{:016x}", hash))
43+
.finish()
44+
}
45+
}
46+
}
47+
}
48+
49+
/// Helper struct for safely printing secrets (like [`SecretKey`] value).
50+
/// Formats the explicit byte value of the secret kept inside the type as a
51+
/// little-endian hexadecimal string using the provided formatter.
52+
///
53+
/// Secrets should not implement neither [`Debug`] and [`Display`] traits directly,
54+
/// and instead provide `fn display_secret<'a>(&'a self) -> DisplaySecret<'a>`
55+
/// function to be used in different display contexts (see "examples" below).
56+
///
57+
/// [`Display`]: fmt::Display
58+
/// [`Debug`]: fmt::Debug
59+
pub struct DisplaySecret {
60+
secret: [u8; SECRET_KEY_SIZE]
61+
}
62+
63+
impl fmt::Debug for DisplaySecret {
64+
#[inline]
65+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66+
let mut slice = [0u8; 64];
67+
let hex = to_hex(&self.secret, &mut slice).expect("fixed-size hex serializer failed");
68+
f.debug_tuple("DisplaySecret")
69+
.field(&hex)
70+
.finish()
71+
}
72+
}
73+
74+
impl fmt::Display for DisplaySecret {
75+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76+
for i in &self.secret {
77+
write!(f, "{:02x}", i)?;
78+
}
79+
Ok(())
80+
}
81+
}
82+
83+
impl SecretKey {
84+
/// Formats the explicit byte value of the secret key kept inside the type as a
85+
/// little-endian hexadecimal string using the provided formatter.
86+
///
87+
/// This is the only method that outputs the actual secret key value, and, thus,
88+
/// should be used with extreme precaution.
89+
///
90+
/// # Example
91+
///
92+
/// ```
93+
/// use secp256k1::key::ONE_KEY;
94+
/// let key = ONE_KEY;
95+
/// // Normal display hides value
96+
/// assert_eq!(
97+
/// "SecretKey(#2518682f7819fb2d)",
98+
/// format!("{:?}", key)
99+
/// );
100+
/// // Here we explicitly display the secret value:
101+
/// assert_eq!(
102+
/// "0000000000000000000000000000000000000000000000000000000000000001",
103+
/// format!("{}", key.display_secret())
104+
/// );
105+
/// assert_eq!(
106+
/// "DisplaySecret(\"0000000000000000000000000000000000000000000000000000000000000001\")",
107+
/// format!("{:?}", key.display_secret())
108+
/// );
109+
/// ```
110+
#[inline]
111+
pub fn display_secret(&self) -> DisplaySecret {
112+
DisplaySecret { secret: self.serialize_secret() }
113+
}
114+
}
115+
116+
impl KeyPair {
117+
/// Formats the explicit byte value of the secret key kept inside the type as a
118+
/// little-endian hexadecimal string using the provided formatter.
119+
///
120+
/// This is the only method that outputs the actual secret key value, and, thus,
121+
/// should be used with extreme precaution.
122+
///
123+
/// # Example
124+
///
125+
/// ```
126+
/// use secp256k1::key::ONE_KEY;
127+
/// use secp256k1::schnorrsig::KeyPair;
128+
/// use secp256k1::Secp256k1;
129+
///
130+
/// let secp = Secp256k1::new();
131+
/// let key = ONE_KEY;
132+
/// let key = KeyPair::from_secret_key(&secp, key);
133+
///
134+
/// // Normal display hides value
135+
/// assert_eq!(
136+
/// "KeyPair(#2518682f7819fb2d)",
137+
/// format!("{:?}", key)
138+
/// );
139+
/// // Here we explicitly display the secret value:
140+
/// assert_eq!(
141+
/// "0000000000000000000000000000000000000000000000000000000000000001",
142+
/// format!("{}", key.display_secret())
143+
/// );
144+
/// assert_eq!(
145+
/// "DisplaySecret(\"0000000000000000000000000000000000000000000000000000000000000001\")",
146+
/// format!("{:?}", key.display_secret())
147+
/// );
148+
#[inline]
149+
pub fn display_secret(&self) -> DisplaySecret {
150+
DisplaySecret { secret: self.serialize_secret() }
151+
}
152+
}

0 commit comments

Comments
 (0)