diff --git a/Cargo.toml b/Cargo.toml
index e545b2ee9..93b80d3b9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,6 +27,7 @@ recovery = ["secp256k1-sys/recovery"]
lowmemory = ["secp256k1-sys/lowmemory"]
global-context = ["std", "rand-std", "global-context-less-secure"]
global-context-less-secure = []
+serde-secrets = ["serde", "std"]
[dependencies]
secp256k1-sys = { version = "0.4.1", default-features = false, path = "./secp256k1-sys" }
diff --git a/contrib/test.sh b/contrib/test.sh
index dc936ab75..0ba977a06 100755
--- a/contrib/test.sh
+++ b/contrib/test.sh
@@ -1,6 +1,6 @@
#!/bin/sh -ex
-FEATURES="bitcoin_hashes global-context lowmemory rand rand-std recovery serde"
+FEATURES="bitcoin_hashes global-context lowmemory rand rand-std recovery serde serde-secrets"
# Use toolchain if explicitly specified
if [ -n "$TOOLCHAIN" ]
diff --git a/secp256k1-sys/src/lib.rs b/secp256k1-sys/src/lib.rs
index 7577eca5b..2956acd35 100644
--- a/secp256k1-sys/src/lib.rs
+++ b/secp256k1-sys/src/lib.rs
@@ -99,6 +99,7 @@ pub type SchnorrNonceFn = Option.
//
-// This is a macro that routinely comes in handy
+/// Implements newtype wrapping methods
#[macro_export]
-macro_rules! impl_array_newtype {
- ($thing:ident, $ty:ty, $len:expr) => {
- impl Copy for $thing {}
-
+macro_rules! impl_ptr_newtype {
+ ($thing:ident, $ty:ty) => {
impl $thing {
#[inline]
+ #[allow(unused)]
/// Converts the object to a raw pointer for FFI interfacing
- pub fn as_ptr(&self) -> *const $ty {
+ pub(crate) fn as_ptr(&self) -> *const $ty {
let &$thing(ref dat) = self;
dat.as_ptr()
}
#[inline]
+ #[allow(unused)]
/// Converts the object to a mutable raw pointer for FFI interfacing
- pub fn as_mut_ptr(&mut self) -> *mut $ty {
+ pub(crate) fn as_mut_ptr(&mut self) -> *mut $ty {
+ let &mut $thing(ref mut dat) = self;
+ dat.as_mut_ptr()
+ }
+ }
+
+ impl $crate::CPtr for $thing {
+ type Target = $ty;
+
+ fn as_c_ptr(&self) -> *const Self::Target {
+ let &$thing(ref dat) = self;
+ dat.as_ptr()
+ }
+
+ fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
let &mut $thing(ref mut dat) = self;
dat.as_mut_ptr()
}
+ }
+ }
+}
+/// Generates implementation of main array accessing methods for a newtype wrapping some inner array
+/// type
+#[macro_export]
+macro_rules! impl_array_newtype {
+ ($thing:ident, $ty:ty, $len:expr) => {
+ impl_ptr_newtype!($thing, $ty);
+
+ impl $thing {
#[inline]
/// Returns the length of the object as an array
pub fn len(&self) -> usize { $len }
@@ -43,6 +68,14 @@ macro_rules! impl_array_newtype {
pub fn is_empty(&self) -> bool { false }
}
+ impl Clone for $thing {
+ #[inline]
+ fn clone(&self) -> $thing {
+ let &$thing(ref dat) = self;
+ $thing(dat.clone())
+ }
+ }
+
impl AsRef<[$ty; $len]> for $thing {
#[inline]
/// Gets a reference to the underlying array
@@ -55,7 +88,7 @@ macro_rules! impl_array_newtype {
impl PartialEq for $thing {
#[inline]
fn eq(&self, other: &$thing) -> bool {
- &self[..] == &other[..]
+ &self.0[..] == &other[..]
}
}
@@ -75,14 +108,6 @@ macro_rules! impl_array_newtype {
}
}
- impl Clone for $thing {
- #[inline]
- fn clone(&self) -> $thing {
- let &$thing(ref dat) = self;
- $thing(dat.clone())
- }
- }
-
impl ::core::ops::Index for $thing {
type Output = $ty;
@@ -132,24 +157,6 @@ macro_rules! impl_array_newtype {
&dat[..]
}
}
- impl $crate::CPtr for $thing {
- type Target = $ty;
- fn as_c_ptr(&self) -> *const Self::Target {
- if self.is_empty() {
- ::core::ptr::null()
- } else {
- self.as_ptr()
- }
- }
-
- fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
- if self.is_empty() {
- ::core::ptr::null::() as *mut _
- } else {
- self.as_mut_ptr()
- }
- }
- }
}
}
@@ -158,7 +165,7 @@ macro_rules! impl_raw_debug {
($thing:ident) => {
impl ::core::fmt::Debug for $thing {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- for i in self[..].iter().cloned() {
+ for i in &self[..] {
write!(f, "{:02x}", i)?;
}
Ok(())
diff --git a/secp256k1-sys/src/recovery.rs b/secp256k1-sys/src/recovery.rs
index db4f5b334..ba975975a 100644
--- a/secp256k1-sys/src/recovery.rs
+++ b/secp256k1-sys/src/recovery.rs
@@ -20,6 +20,7 @@ use {Context, Signature, NonceFn, PublicKey};
/// Library-internal representation of a Secp256k1 signature + recovery ID
#[repr(C)]
+#[derive(Copy)]
pub struct RecoverableSignature([c_uchar; 65]);
impl_array_newtype!(RecoverableSignature, c_uchar, 65);
impl_raw_debug!(RecoverableSignature);
diff --git a/src/context.rs b/src/context.rs
index ec8b193bc..59831d38a 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -1,5 +1,5 @@
use core::marker::PhantomData;
-use core::mem::{self, ManuallyDrop};
+use core::mem::ManuallyDrop;
use ffi::{self, CPtr, types::AlignedType};
use ffi::types::{c_uint, c_void};
use Error;
@@ -105,7 +105,7 @@ mod alloc_only {
impl private::Sealed for VerifyOnly {}
use super::*;
- const ALIGN_TO: usize = mem::align_of::();
+ const ALIGN_TO: usize = ::core::mem::align_of::();
/// Represents the set of capabilities needed for signing.
pub enum SignOnly {}
diff --git a/src/key.rs b/src/key.rs
index 834137c8d..238a702f3 100644
--- a/src/key.rs
+++ b/src/key.rs
@@ -27,22 +27,14 @@ use constants;
use ffi::{self, CPtr};
/// Secret 256-bit key used as `x` in an ECDSA signature
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SecretKey(pub(crate) [u8; constants::SECRET_KEY_SIZE]);
-impl_array_newtype!(SecretKey, u8, constants::SECRET_KEY_SIZE);
-impl_pretty_debug!(SecretKey);
+impl_ptr_newtype!(SecretKey, u8);
+impl_safe_debug!(SecretKey);
-impl fmt::LowerHex for SecretKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- for ch in &self.0[..] {
- write!(f, "{:02x}", *ch)?;
- }
- Ok(())
- }
-}
-
-impl fmt::Display for SecretKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::LowerHex::fmt(self, f)
+impl Drop for SecretKey {
+ fn drop(&mut self) {
+ self.0.copy_from_slice(&[0u8; constants::SECRET_KEY_SIZE]);
}
}
@@ -214,13 +206,14 @@ impl SecretKey {
}
}
-#[cfg(feature = "serde")]
+#[cfg(all(feature = "serde", feature = "serde-secrets"))]
impl ::serde::Serialize for SecretKey {
fn serialize(&self, s: S) -> Result {
if s.is_human_readable() {
- s.collect_str(self)
+ #[allow(deprecated)]
+ s.serialize_str(&self.format_secret_key())
} else {
- s.serialize_bytes(&self[..])
+ s.serialize_bytes(&self.0[..])
}
}
}
@@ -529,7 +522,7 @@ mod test {
let s = Secp256k1::new();
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
- assert_eq!(SecretKey::from_slice(&sk1[..]), Ok(sk1));
+ assert_eq!(SecretKey::from_slice(&sk1.0[..]), Ok(sk1));
assert_eq!(PublicKey::from_slice(&pk1.serialize()[..]), Ok(pk1));
assert_eq!(PublicKey::from_slice(&pk1.serialize_uncompressed()[..]), Ok(pk1));
}
@@ -675,8 +668,15 @@ mod test {
let s = Secp256k1::new();
let (sk, _) = s.generate_keypair(&mut DumbRng(0));
+ #[cfg(all(feature = "bitcoin_hashes", not(fuzzing)))]
assert_eq!(&format!("{:?}", sk),
- "SecretKey(0100000000000000020000000000000003000000000000000400000000000000)");
+ "SecretKey(#73e200e2...29d234a4)");
+ #[cfg(all(not(feature = "bitcoin_hashes"), feature = "std"))]
+ assert_eq!(&format!("{:?}", sk),
+ "SecretKey(#a463cd1b7ffe86b0)");
+ #[allow(deprecated)] {
+ assert_eq!(sk.format_secret_key(),
+ "0100000000000000020000000000000003000000000000000400000000000000"); };
}
#[test]
@@ -698,10 +698,11 @@ mod test {
#[cfg(fuzzing)]
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");
+ #[allow(deprecated)] {
assert_eq!(
- sk.to_string(),
+ sk.format_secret_key(),
"01010101010101010001020304050607ffff0000ffff00006363636363636363"
- );
+ ) };
assert_eq!(
SecretKey::from_str("01010101010101010001020304050607ffff0000ffff00006363636363636363").unwrap(),
sk
@@ -777,13 +778,13 @@ mod test {
let (mut sk2, mut pk2) = s.generate_keypair(&mut thread_rng());
assert_eq!(PublicKey::from_secret_key(&s, &sk1), pk1);
- assert!(sk1.add_assign(&sk2[..]).is_ok());
- assert!(pk1.add_exp_assign(&s, &sk2[..]).is_ok());
+ assert!(sk1.add_assign(&sk2.0[..]).is_ok());
+ assert!(pk1.add_exp_assign(&s, &sk2.0[..]).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk1), pk1);
assert_eq!(PublicKey::from_secret_key(&s, &sk2), pk2);
- assert!(sk2.add_assign(&sk1[..]).is_ok());
- assert!(pk2.add_exp_assign(&s, &sk1[..]).is_ok());
+ assert!(sk2.add_assign(&sk1.0[..]).is_ok());
+ assert!(pk2.add_exp_assign(&s, &sk1.0[..]).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk2), pk2);
}
@@ -795,13 +796,13 @@ mod test {
let (mut sk2, mut pk2) = s.generate_keypair(&mut thread_rng());
assert_eq!(PublicKey::from_secret_key(&s, &sk1), pk1);
- assert!(sk1.mul_assign(&sk2[..]).is_ok());
- assert!(pk1.mul_assign(&s, &sk2[..]).is_ok());
+ assert!(sk1.mul_assign(&sk2.0[..]).is_ok());
+ assert!(pk1.mul_assign(&s, &sk2.0[..]).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk1), pk1);
assert_eq!(PublicKey::from_secret_key(&s, &sk2), pk2);
- assert!(sk2.mul_assign(&sk1[..]).is_ok());
- assert!(pk2.mul_assign(&s, &sk1[..]).is_ok());
+ assert!(sk2.mul_assign(&sk1.0[..]).is_ok());
+ assert!(pk2.mul_assign(&s, &sk1.0[..]).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk2), pk2);
}
@@ -811,7 +812,7 @@ mod test {
let (mut sk, mut pk) = s.generate_keypair(&mut thread_rng());
- let original_sk = sk;
+ let original_sk = sk.clone();
let original_pk = pk;
assert_eq!(PublicKey::from_secret_key(&s, &sk), pk);
@@ -906,7 +907,7 @@ mod test {
assert!(sum2.is_ok());
assert_eq!(sum1, sum2);
- assert!(sk1.add_assign(&sk2.as_ref()[..]).is_ok());
+ assert!(sk1.add_assign(&sk2.0[..]).is_ok());
let sksum = PublicKey::from_secret_key(&s, &sk1);
assert_eq!(Ok(sksum), sum1);
}
@@ -935,7 +936,7 @@ mod test {
#[cfg(feature = "serde")]
#[test]
- fn test_serde() {
+ fn test_serde_pk() {
use serde_test::{Configure, Token, assert_tokens};
static SK_BYTES: [u8; 32] = [
1, 1, 1, 1, 1, 1, 1, 1,
@@ -943,9 +944,6 @@ mod test {
0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0,
99, 99, 99, 99, 99, 99, 99, 99
];
- static SK_STR: &'static str = "\
- 01010101010101010001020304050607ffff0000ffff00006363636363636363\
- ";
static PK_BYTES: [u8; 33] = [
0x02,
0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f,
@@ -967,14 +965,6 @@ mod test {
#[cfg(fuzzing)]
let pk = PublicKey::from_slice(&PK_BYTES).expect("pk");
- assert_tokens(&sk.compact(), &[Token::BorrowedBytes(&SK_BYTES[..])]);
- assert_tokens(&sk.compact(), &[Token::Bytes(&SK_BYTES)]);
- assert_tokens(&sk.compact(), &[Token::ByteBuf(&SK_BYTES)]);
-
- assert_tokens(&sk.readable(), &[Token::BorrowedStr(SK_STR)]);
- assert_tokens(&sk.readable(), &[Token::Str(SK_STR)]);
- assert_tokens(&sk.readable(), &[Token::String(SK_STR)]);
-
assert_tokens(&pk.compact(), &[Token::BorrowedBytes(&PK_BYTES[..])]);
assert_tokens(&pk.compact(), &[Token::Bytes(&PK_BYTES)]);
assert_tokens(&pk.compact(), &[Token::ByteBuf(&PK_BYTES)]);
@@ -982,6 +972,30 @@ mod test {
assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]);
assert_tokens(&pk.readable(), &[Token::Str(PK_STR)]);
assert_tokens(&pk.readable(), &[Token::String(PK_STR)]);
+ }
+
+ #[cfg(all(feature = "serde", feature = "serde-secrets"))]
+ #[test]
+ fn test_serde_sk() {
+ use serde_test::{Configure, Token, assert_tokens};
+ static SK_BYTES: [u8; 32] = [
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0,
+ 99, 99, 99, 99, 99, 99, 99, 99
+ ];
+ static SK_STR: &'static str = "\
+ 01010101010101010001020304050607ffff0000ffff00006363636363636363\
+ ";
+
+ let sk = SecretKey::from_slice(&SK_BYTES).unwrap();
+
+ assert_tokens(&sk.clone().compact(), &[Token::BorrowedBytes(&SK_BYTES[..])]);
+ assert_tokens(&sk.clone().compact(), &[Token::Bytes(&SK_BYTES)]);
+ assert_tokens(&sk.clone().compact(), &[Token::ByteBuf(&SK_BYTES)]);
+ assert_tokens(&sk.clone().readable(), &[Token::BorrowedStr(SK_STR)]);
+ assert_tokens(&sk.clone().readable(), &[Token::Str(SK_STR)]);
+ assert_tokens(&sk.clone().readable(), &[Token::String(SK_STR)]);
}
}
diff --git a/src/lib.rs b/src/lib.rs
index a32a2afe2..4c6329dd4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -458,6 +458,7 @@ impl<'de> ::serde::Deserialize<'de> for Signature {
}
/// A (hashed) message input to an ECDSA signature
+#[derive(Copy)]
pub struct Message([u8; constants::MESSAGE_SIZE]);
impl_array_newtype!(Message, u8, constants::MESSAGE_SIZE);
impl_pretty_debug!(Message);
@@ -795,7 +796,7 @@ impl Secp256k1 {
/// which OpenSSL would verify but not libsecp256k1, or vice-versa. Requires a
/// verify-capable context.
///
- /// ```rust
+ /// ```
/// # #[cfg(feature="rand")] {
/// # use secp256k1::rand::rngs::OsRng;
/// # use secp256k1::{Secp256k1, Message, Error};
@@ -995,7 +996,7 @@ mod tests {
assert!(full.verify(&msg, &sig, &pk).is_ok());
// Check that we can produce keys from slices with no precomputation
- let (pk_slice, sk_slice) = (&pk.serialize(), &sk[..]);
+ let (pk_slice, sk_slice) = (&pk.serialize(), &sk.0[..]);
let new_pk = PublicKey::from_slice(pk_slice).unwrap();
let new_sk = SecretKey::from_slice(sk_slice).unwrap();
assert_eq!(sk, new_sk);
diff --git a/src/macros.rs b/src/macros.rs
index bfd41b7bb..684bf102e 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -18,10 +18,80 @@ macro_rules! impl_pretty_debug {
impl ::core::fmt::Debug for $thing {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "{}(", stringify!($thing))?;
- for i in self[..].iter().cloned() {
+ for i in &self[..] {
write!(f, "{:02x}", i)?;
}
- write!(f, ")")
+ f.write_str(")")
+ }
+ }
+ }
+}
+
+macro_rules! impl_safe_debug {
+ ($thing:ident) => {
+ #[cfg(feature = "bitcoin_hashes")]
+ impl ::core::fmt::Debug for $thing {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ use ::bitcoin_hashes::{Hash, sha256};
+ write!(f, "{}(#", stringify!($thing))?;
+ let hash = sha256::Hash::hash(&self.0[..]);
+ for i in &hash[..4] {
+ write!(f, "{:02x}", i)?;
+ }
+ f.write_str("...")?;
+ for i in &hash[28..] {
+ write!(f, "{:02x}", i)?;
+ }
+ f.write_str(")")
+ }
+ }
+
+ #[cfg(all(not(feature = "bitcoin_hashes"), feature = "std"))]
+ impl ::core::fmt::Debug for $thing {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ use ::core::hash::Hasher;
+ let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
+
+ hasher.write(&self.0[..]);
+ let hash = hasher.finish();
+
+ write!(f, "{}(#{:016x})", stringify!($thing), hash)
+ }
+ }
+
+ impl $thing {
+ /// Formats the explicit byte value of the secret key kept inside the type as a
+ /// little-endian hexadecimal string using the provided formatter.
+ ///
+ /// This is the only method that outputs the actual secret key value, and, thus,
+ /// should be used with extreme precaution.
+ #[deprecated(
+ note = "Caution: you are explicitly outputting secret key value! This can be done
+ only in debug environment and that's why always considered as ``deprecated''"
+ )]
+ pub fn fmt_secret_key(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ for i in &self.0[..] {
+ write!(f, "{:02x}", i)?;
+ }
+ Ok(())
+ }
+
+ /// Formats the explicit byte value of the secret key kept inside the type as a
+ /// little-endian hexadecimal string.
+ ///
+ /// This is the only method that outputs the actual secret key value, and, thus,
+ /// should be used with extreme precaution.
+ #[deprecated(
+ note = "Caution: you are explicitly outputting secret key value! This can be done
+ only in debug environment and that's why always considered as ``deprecated''"
+ )]
+ #[cfg(feature = "std")]
+ pub fn format_secret_key(&self) -> String {
+ let mut s = Vec::with_capacity(self.0.len() * 2);
+ for i in &self.0[..] {
+ s.push(format!("{:02x}", i));
+ }
+ s.join("")
}
}
}
diff --git a/src/schnorrsig.rs b/src/schnorrsig.rs
index d8c48a07f..9c160db48 100644
--- a/src/schnorrsig.rs
+++ b/src/schnorrsig.rs
@@ -16,6 +16,7 @@ use {Message, Signing, Verification};
use SecretKey;
/// Represents a Schnorr signature.
+#[derive(Copy)]
pub struct Signature([u8; constants::SCHNORRSIG_SIGNATURE_SIZE]);
impl_array_newtype!(Signature, u8, constants::SCHNORRSIG_SIGNATURE_SIZE);
impl_pretty_debug!(Signature);
@@ -76,8 +77,51 @@ impl str::FromStr for Signature {
}
/// Opaque data structure that holds a keypair consisting of a secret and a public key.
-#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
+#[derive(Clone)]
pub struct KeyPair(ffi::KeyPair);
+impl_safe_debug!(KeyPair);
+
+impl ::core::str::FromStr for KeyPair {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result {
+ let ctx = unsafe {
+ Secp256k1::from_raw_all(ffi::secp256k1_context_no_precomp as *mut ffi::Context)
+ };
+ KeyPair::from_seckey_str(&ctx, s)
+ }
+}
+
+#[cfg(all(feature = "serde", feature = "serde-secrets"))]
+impl ::serde::Serialize for KeyPair {
+ fn serialize(&self, s: S) -> Result {
+ if s.is_human_readable() {
+ #[allow(deprecated)]
+ s.serialize_str(&self.format_secret_key())
+ } else {
+ s.serialize_bytes(&self.0[..])
+ }
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> ::serde::Deserialize<'de> for KeyPair {
+ fn deserialize>(d: D) -> Result {
+ if d.is_human_readable() {
+ d.deserialize_str(super::serde_util::FromStrVisitor::new(
+ "a hex string representing 32 byte KeyPair"
+ ))
+ } else {
+ d.deserialize_bytes(super::serde_util::BytesVisitor::new(
+ "raw 32 bytes KeyPair",
+ |data| unsafe {
+ let ctx = Secp256k1::from_raw_all(ffi::secp256k1_context_no_precomp as *mut ffi::Context);
+ KeyPair::from_seckey_slice(&ctx, data)
+ }
+ ))
+ }
+ }
+}
/// A Schnorr public key, used for verification of Schnorr signatures
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]