Skip to content

Commit 44dae89

Browse files
committed
key/secret: encapsulate SecretKey in module that enforces invariants
1 parent 41bb442 commit 44dae89

File tree

1 file changed

+122
-105
lines changed

1 file changed

+122
-105
lines changed

src/key/secret.rs

Lines changed: 122 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,117 @@ use crate::{
1414
#[cfg(feature = "global-context")]
1515
use crate::{ecdsa, Message, SECP256K1};
1616

17-
/// Secret key - a 256-bit key used to create ECDSA and Taproot signatures.
18-
///
19-
/// This value should be generated using a [cryptographically secure pseudorandom number generator].
20-
///
21-
/// # Side channel attacks
22-
///
23-
/// We have attempted to reduce the side channel attack surface by implementing a constant time `eq`
24-
/// method. For similar reasons we explicitly do not implement `PartialOrd`, `Ord`, or `Hash` on
25-
/// `SecretKey`. If you really want to order secret keys then you can use `AsRef` to get at the
26-
/// underlying bytes and compare them - however this is almost certainly a bad idea.
27-
///
28-
/// # Serde support
29-
///
30-
/// Implements de/serialization with the `serde` feature enabled. We treat the byte value as a tuple
31-
/// of 32 `u8`s for non-human-readable formats. This representation is optimal for some formats
32-
/// (e.g. [`bincode`]) however other formats may be less optimal (e.g. [`cbor`]).
33-
///
34-
/// # Examples
35-
///
36-
/// Basic usage:
37-
///
38-
/// ```
39-
/// # #[cfg(all(feature = "rand", feature = "std"))] {
40-
/// use secp256k1::{rand, Secp256k1, SecretKey};
41-
///
42-
/// let secp = Secp256k1::new();
43-
/// let secret_key = SecretKey::new(&mut rand::rng());
44-
/// # }
45-
/// ```
46-
/// [`bincode`]: https://docs.rs/bincode
47-
/// [`cbor`]: https://docs.rs/cbor
48-
/// [cryptographically secure pseudorandom number generator]: https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator
49-
#[derive(Copy, Clone)]
50-
pub struct SecretKey([u8; constants::SECRET_KEY_SIZE]);
51-
impl_display_secret!(SecretKey);
52-
impl_non_secure_erase!(SecretKey, 0, [1u8; constants::SECRET_KEY_SIZE]);
17+
mod encapsulate {
18+
use crate::constants::SECRET_KEY_SIZE;
19+
use crate::ffi::{self, CPtr};
20+
use crate::Error;
21+
22+
/// Secret key - a 256-bit key used to create ECDSA and Taproot signatures.
23+
///
24+
/// This value should be generated using a [cryptographically secure pseudorandom number generator].
25+
///
26+
/// # Side channel attacks
27+
///
28+
/// We have attempted to reduce the side channel attack surface by implementing a constant time `eq`
29+
/// method. For similar reasons we explicitly do not implement `PartialOrd`, `Ord`, or `Hash` on
30+
/// `SecretKey`. If you really want to order secret keys then you can use `AsRef` to get at the
31+
/// underlying bytes and compare them - however this is almost certainly a bad idea.
32+
///
33+
/// # Serde support
34+
///
35+
/// Implements de/serialization with the `serde` feature enabled. We treat the byte value as a tuple
36+
/// of 32 `u8`s for non-human-readable formats. This representation is optimal for some formats
37+
/// (e.g. [`bincode`]) however other formats may be less optimal (e.g. [`cbor`]).
38+
///
39+
/// # Examples
40+
///
41+
/// Basic usage:
42+
///
43+
/// ```
44+
/// # #[cfg(all(feature = "rand", feature = "std"))] {
45+
/// use secp256k1::{rand, Secp256k1, SecretKey};
46+
///
47+
/// let secp = Secp256k1::new();
48+
/// let secret_key = SecretKey::new(&mut rand::rng());
49+
/// # }
50+
/// ```
51+
/// [`bincode`]: https://docs.rs/bincode
52+
/// [`cbor`]: https://docs.rs/cbor
53+
/// [cryptographically secure pseudorandom number generator]: https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator
54+
#[derive(Copy, Clone)]
55+
pub struct SecretKey([u8; SECRET_KEY_SIZE]);
56+
// FIXME these two macro call should be moved outside of the encapsulate module
57+
impl_display_secret!(SecretKey);
58+
impl_non_secure_erase!(SecretKey, 0, [1u8; SECRET_KEY_SIZE]);
59+
60+
impl SecretKey {
61+
/// Returns the secret key as a byte value.
62+
///
63+
/// # Side channel attacks
64+
///
65+
/// Using ordering functions (`PartialOrd`/`Ord`) on a reference to secret keys leaks data
66+
/// because the implementations are not constant time. Doing so will make your code vulnerable
67+
/// to side channel attacks. [`SecretKey::eq`] is implemented using a constant time algorithm,
68+
/// please consider using it to do comparisons of secret keys.
69+
#[inline]
70+
pub fn to_secret_bytes(&self) -> [u8; SECRET_KEY_SIZE] { self.0 }
71+
72+
/// Returns a reference to the secret key as a byte array.
73+
///
74+
/// See note on [`Self::to_secret_bytes`].
75+
#[inline]
76+
pub fn as_secret_bytes(&self) -> &[u8; SECRET_KEY_SIZE] { &self.0 }
77+
78+
/// Converts a 32-byte array to a secret key.
79+
///
80+
/// See note on [`Self::to_secret_bytes`].
81+
///
82+
/// # Errors
83+
///
84+
/// Returns an error when the secret key is invalid: when it is all-zeros or would exceed
85+
/// the curve order when interpreted as a big-endian unsigned integer.
86+
///
87+
/// # Examples
88+
///
89+
/// ```
90+
/// use secp256k1::SecretKey;
91+
/// let sk = SecretKey::from_byte_array([0xcd; 32]).expect("32 bytes, within curve order");
92+
/// ```
93+
#[inline]
94+
pub fn from_secret_bytes(data: [u8; SECRET_KEY_SIZE]) -> Result<SecretKey, Error> {
95+
crate::with_raw_global_context(
96+
|ctx| unsafe {
97+
if ffi::secp256k1_ec_seckey_verify(ctx.as_ptr(), data.as_c_ptr()) == 0 {
98+
return Err(Error::InvalidSecretKey);
99+
}
100+
Ok(SecretKey(data))
101+
},
102+
None,
103+
)
104+
}
105+
}
106+
107+
// Must be inside the `encapsulate` module since there is no way to obtain mutable
108+
// access to the internal array outside of the module.
109+
impl CPtr for SecretKey {
110+
type Target = u8;
111+
112+
fn as_c_ptr(&self) -> *const Self::Target { self.as_secret_bytes().as_ptr() }
113+
114+
fn as_mut_c_ptr(&mut self) -> *mut Self::Target { self.0.as_mut_ptr() }
115+
}
116+
}
117+
pub use encapsulate::SecretKey;
53118

54119
impl PartialEq for SecretKey {
55120
/// This implementation is designed to be constant time to help prevent side channel attacks.
56121
#[inline]
57122
fn eq(&self, other: &Self) -> bool {
58-
let accum = self.0.iter().zip(&other.0).fold(0, |accum, (a, b)| accum | a ^ b);
123+
let accum = self
124+
.as_secret_bytes()
125+
.iter()
126+
.zip(other.as_secret_bytes())
127+
.fold(0, |accum, (a, b)| accum | a ^ b);
59128
unsafe { core::ptr::read_volatile(&accum) == 0 }
60129
}
61130
}
@@ -65,17 +134,9 @@ impl Eq for SecretKey {}
65134
impl AsRef<[u8; constants::SECRET_KEY_SIZE]> for SecretKey {
66135
/// Gets a reference to the underlying array.
67136
///
68-
/// # Side channel attacks
69-
///
70-
/// Using ordering functions (`PartialOrd`/`Ord`) on a reference to secret keys leaks data
71-
/// because the implementations are not constant time. Doing so will make your code vulnerable
72-
/// to side channel attacks. [`SecretKey::eq`] is implemented using a constant time algorithm,
73-
/// please consider using it to do comparisons of secret keys.
137+
/// See note on [`Self::to_secret_bytes`].
74138
#[inline]
75-
fn as_ref(&self) -> &[u8; constants::SECRET_KEY_SIZE] {
76-
let SecretKey(dat) = self;
77-
dat
78-
}
139+
fn as_ref(&self) -> &[u8; constants::SECRET_KEY_SIZE] { self.as_secret_bytes() }
79140
}
80141

81142
impl<I> ops::Index<I> for SecretKey
@@ -85,26 +146,12 @@ where
85146
type Output = <[u8] as ops::Index<I>>::Output;
86147

87148
#[inline]
88-
fn index(&self, index: I) -> &Self::Output { &self.0[index] }
89-
}
90-
91-
impl ffi::CPtr for SecretKey {
92-
type Target = u8;
93-
94-
fn as_c_ptr(&self) -> *const Self::Target {
95-
let SecretKey(dat) = self;
96-
dat.as_ptr()
97-
}
98-
99-
fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
100-
let &mut SecretKey(ref mut dat) = self;
101-
dat.as_mut_ptr()
102-
}
149+
fn index(&self, index: I) -> &Self::Output { &self.as_secret_bytes()[index] }
103150
}
104151

105152
impl str::FromStr for SecretKey {
106153
type Err = Error;
107-
fn from_str(s: &str) -> Result<SecretKey, Error> {
154+
fn from_str(s: &str) -> Result<Self, Self::Err> {
108155
let mut res = [0u8; constants::SECRET_KEY_SIZE];
109156
match from_hex(s, &mut res) {
110157
Ok(constants::SECRET_KEY_SIZE) => SecretKey::from_secret_bytes(res),
@@ -126,18 +173,13 @@ impl SecretKey {
126173
/// ```
127174
#[inline]
128175
#[cfg(feature = "rand")]
129-
pub fn new<R: rand::Rng + ?Sized>(rng: &mut R) -> SecretKey {
130-
let mut data = crate::random_32_bytes(rng);
131-
unsafe {
132-
while ffi::secp256k1_ec_seckey_verify(
133-
ffi::secp256k1_context_no_precomp,
134-
data.as_c_ptr(),
135-
) == 0
136-
{
137-
data = crate::random_32_bytes(rng);
176+
pub fn new<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
177+
loop {
178+
let data = crate::random_32_bytes(rng);
179+
if let Ok(key) = Self::from_secret_bytes(data) {
180+
return key;
138181
}
139182
}
140-
SecretKey(data)
141183
}
142184

143185
/// Converts a 32-byte array to a secret key.
@@ -146,26 +188,6 @@ impl SecretKey {
146188
Self::from_secret_bytes(data)
147189
}
148190

149-
/// Converts a 32-byte array to a secret key.
150-
///
151-
/// # Examples
152-
///
153-
/// ```
154-
/// use secp256k1::SecretKey;
155-
/// let sk = SecretKey::from_secret_bytes([0xcd; 32]).expect("32 bytes, within curve order");
156-
/// ```
157-
#[inline]
158-
pub fn from_secret_bytes(data: [u8; constants::SECRET_KEY_SIZE]) -> Result<SecretKey, Error> {
159-
unsafe {
160-
if ffi::secp256k1_ec_seckey_verify(ffi::secp256k1_context_no_precomp, data.as_c_ptr())
161-
== 0
162-
{
163-
return Err(Error::InvalidSecretKey);
164-
}
165-
}
166-
Ok(SecretKey(data))
167-
}
168-
169191
/// Creates a new secret key using data from BIP-340 [`Keypair`].
170192
///
171193
/// # Examples
@@ -190,21 +212,13 @@ impl SecretKey {
190212
);
191213
debug_assert_eq!(ret, 1);
192214
}
193-
SecretKey(sk)
215+
Self::from_secret_bytes(sk).expect("a valid Keypair has a valid SecretKey")
194216
}
195217

196-
/// Returns a reference to the secret key as a byte value.
197-
#[inline]
198-
pub fn as_secret_bytes(&self) -> &[u8; constants::SECRET_KEY_SIZE] { &self.0 }
199-
200-
/// Returns the secret key as a byte value.
201-
#[inline]
202-
pub fn to_secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] { self.0 }
203-
204218
/// Returns the secret key as a byte value.
205219
#[inline]
206220
#[deprecated(since = "0.32.0", note = "use to_secret_bytes instead")]
207-
pub fn secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] { self.0 }
221+
pub fn secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] { self.to_secret_bytes() }
208222

209223
/// Negates the secret key.
210224
#[inline]
@@ -316,10 +330,13 @@ impl serde::Serialize for SecretKey {
316330
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
317331
if s.is_human_readable() {
318332
let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2];
319-
s.serialize_str(crate::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
333+
s.serialize_str(
334+
crate::to_hex(self.as_secret_bytes(), &mut buf)
335+
.expect("fixed-size hex serialization"),
336+
)
320337
} else {
321338
let mut tuple = s.serialize_tuple(constants::SECRET_KEY_SIZE)?;
322-
for byte in self.0.iter() {
339+
for byte in self.as_secret_bytes().iter() {
323340
tuple.serialize_element(byte)?;
324341
}
325342
tuple.end()

0 commit comments

Comments
 (0)