diff --git a/src/lib.rs b/src/lib.rs index 69dee83..6277cb1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -152,16 +152,13 @@ pub mod constants; pub mod ecdh; pub mod ffi; pub mod key; +pub mod recovery; pub use key::SecretKey; pub use key::PublicKey; use core::marker::PhantomData; use core::ops::Deref; -/// A tag used for recovering the public key from a compact signature -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct RecoveryId(i32); - /// An ECDSA signature #[derive(Copy, Clone, PartialEq, Eq)] pub struct Signature(ffi::Signature); @@ -210,10 +207,6 @@ fn from_str(s: &str) -> Result { } } -/// An ECDSA signature with a recovery ID for pubkey recovery -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct RecoverableSignature(ffi::RecoverableSignature); - /// Trait describing something that promises to be a 32-byte random number; in particular, /// it has negligible probability of being zero or overflowing the group order. Such objects /// may be converted to `Message`s without any error paths. @@ -222,23 +215,6 @@ pub trait ThirtyTwoByteHash { fn into_32(self) -> [u8; 32]; } -impl RecoveryId { -#[inline] -/// Allows library users to create valid recovery IDs from i32. -pub fn from_i32(id: i32) -> Result { - match id { - 0 | 1 | 2 | 3 => Ok(RecoveryId(id)), - _ => Err(Error::InvalidRecoveryId) - } -} - -#[inline] -/// Allows library users to convert recovery IDs to i32. -pub fn to_i32(self) -> i32 { - self.0 -} -} - impl SerializedSignature { /// Get a pointer to the underlying data with the specified capacity. pub(crate) fn get_data_mut_ptr(&mut self) -> *mut u8 { @@ -420,79 +396,6 @@ impl From for Signature { } -impl RecoverableSignature { - #[inline] - /// Converts a compact-encoded byte slice to a signature. This - /// representation is nonstandard and defined by the libsecp256k1 - /// library. - pub fn from_compact(data: &[u8], recid: RecoveryId) -> Result { - let mut ret = unsafe { ffi::RecoverableSignature::blank() }; - - unsafe { - if data.len() != 64 { - Err(Error::InvalidSignature) - } else if ffi::secp256k1_ecdsa_recoverable_signature_parse_compact( - ffi::secp256k1_context_no_precomp, - &mut ret, - data.as_ptr(), - recid.0, - ) == 1 - { - Ok(RecoverableSignature(ret)) - } else { - Err(Error::InvalidSignature) - } - } - } - - /// Obtains a raw pointer suitable for use with FFI functions - #[inline] - pub fn as_ptr(&self) -> *const ffi::RecoverableSignature { - &self.0 as *const _ - } - - #[inline] - /// Serializes the recoverable signature in compact format - pub fn serialize_compact(&self) -> (RecoveryId, [u8; 64]) { - let mut ret = [0u8; 64]; - let mut recid = 0i32; - unsafe { - let err = ffi::secp256k1_ecdsa_recoverable_signature_serialize_compact( - ffi::secp256k1_context_no_precomp, - ret.as_mut_ptr(), - &mut recid, - self.as_ptr(), - ); - assert!(err == 1); - } - (RecoveryId(recid), ret) - } - - /// Converts a recoverable signature to a non-recoverable one (this is needed - /// for verification - #[inline] - pub fn to_standard(&self) -> Signature { - let mut ret = unsafe { ffi::Signature::blank() }; - unsafe { - let err = ffi::secp256k1_ecdsa_recoverable_signature_convert( - ffi::secp256k1_context_no_precomp, - &mut ret, - self.as_ptr(), - ); - assert!(err == 1); - } - Signature(ret) - } -} - -/// Creates a new recoverable signature from a FFI one -impl From for RecoverableSignature { - #[inline] - fn from(sig: ffi::RecoverableSignature) -> RecoverableSignature { - RecoverableSignature(sig) - } -} - #[cfg(feature = "serde")] impl ::serde::Serialize for Signature { fn serialize(&self, s: S) -> Result { @@ -767,31 +670,6 @@ impl Secp256k1 { Signature::from(ret) } - /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce - /// Requires a signing-capable context. - pub fn sign_recoverable(&self, msg: &Message, sk: &key::SecretKey) - -> RecoverableSignature { - - let mut ret = unsafe { ffi::RecoverableSignature::blank() }; - unsafe { - // We can assume the return value because it's not possible to construct - // an invalid signature from a valid `Message` and `SecretKey` - assert_eq!( - ffi::secp256k1_ecdsa_sign_recoverable( - self.ctx, - &mut ret, - msg.as_ptr(), - sk.as_ptr(), - ffi::secp256k1_nonce_function_rfc6979, - ptr::null() - ), - 1 - ); - } - - RecoverableSignature::from(ret) - } - /// Generates a random keypair. Convenience function for `key::SecretKey::new` /// and `key::PublicKey::from_secret_key`; call those functions directly for /// batch key generation. Requires a signing-capable context. Requires compilation @@ -807,23 +685,6 @@ impl Secp256k1 { } impl Secp256k1 { - - /// Determines the public key for which `sig` is a valid signature for - /// `msg`. Requires a verify-capable context. - pub fn recover(&self, msg: &Message, sig: &RecoverableSignature) - -> Result { - - let mut pk = unsafe { ffi::PublicKey::blank() }; - - unsafe { - if ffi::secp256k1_ecdsa_recover(self.ctx, &mut pk, - sig.as_ptr(), msg.as_ptr()) != 1 { - return Err(Error::InvalidSignature); - } - }; - Ok(key::PublicKey::from(pk)) - } - /// Checks that `sig` is a valid ECDSA signature for `msg` using the public /// key `pubkey`. Returns `Ok(true)` on success. Note that this function cannot /// be used for Bitcoin consensus checking since there may exist signatures @@ -877,7 +738,7 @@ mod tests { use key::{SecretKey, PublicKey}; use super::from_hex; use super::constants; - use super::{Secp256k1, Signature, RecoverableSignature, Message, RecoveryId}; + use super::{Secp256k1, Signature, Message}; use super::Error::{InvalidMessage, IncorrectSignature, InvalidSignature}; macro_rules! hex { @@ -903,22 +764,12 @@ mod tests { // Try signing assert_eq!(sign.sign(&msg, &sk), full.sign(&msg, &sk)); - assert_eq!(sign.sign_recoverable(&msg, &sk), full.sign_recoverable(&msg, &sk)); let sig = full.sign(&msg, &sk); - let sigr = full.sign_recoverable(&msg, &sk); // Try verifying assert!(vrfy.verify(&msg, &sig, &pk).is_ok()); assert!(full.verify(&msg, &sig, &pk).is_ok()); - // Try pk recovery - assert!(vrfy.recover(&msg, &sigr).is_ok()); - assert!(full.recover(&msg, &sigr).is_ok()); - - assert_eq!(vrfy.recover(&msg, &sigr), - full.recover(&msg, &sigr)); - assert_eq!(full.recover(&msg, &sigr), Ok(pk)); - // Check that we can produce keys from slices with no precomputation let (pk_slice, sk_slice) = (&pk.serialize(), &sk[..]); let new_pk = PublicKey::from_slice(pk_slice).unwrap(); @@ -927,35 +778,6 @@ mod tests { assert_eq!(pk, new_pk); } - #[test] - fn recid_sanity_check() { - let one = RecoveryId(1); - assert_eq!(one, one.clone()); - } - - #[test] - fn sign() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); - let one = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; - - let sk = SecretKey::from_slice(&one).unwrap(); - let msg = Message::from_slice(&one).unwrap(); - - let sig = s.sign_recoverable(&msg, &sk); - assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[ - 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f, - 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6, - 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65, - 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98, - 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8, - 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f, - 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06, - 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89], - RecoveryId(1))) - } - #[test] fn signature_serialize_roundtrip() { let mut s = Secp256k1::new(); @@ -1093,47 +915,12 @@ mod tests { let (sk, pk) = s.generate_keypair(&mut thread_rng()); - let sigr = s.sign_recoverable(&msg, &sk); - let sig = sigr.to_standard(); + let sig = s.sign(&msg, &sk); let mut msg = [0u8; 32]; thread_rng().fill_bytes(&mut msg); let msg = Message::from_slice(&msg).unwrap(); assert_eq!(s.verify(&msg, &sig, &pk), Err(IncorrectSignature)); - - let recovered_key = s.recover(&msg, &sigr).unwrap(); - assert!(recovered_key != pk); - } - - #[test] - fn sign_with_recovery() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); - - let mut msg = [0u8; 32]; - thread_rng().fill_bytes(&mut msg); - let msg = Message::from_slice(&msg).unwrap(); - - let (sk, pk) = s.generate_keypair(&mut thread_rng()); - - let sig = s.sign_recoverable(&msg, &sk); - - assert_eq!(s.recover(&msg, &sig), Ok(pk)); - } - - #[test] - fn bad_recovery() { - let mut s = Secp256k1::new(); - s.randomize(&mut thread_rng()); - - let msg = Message::from_slice(&[0x55; 32]).unwrap(); - - // Zero is not a valid sig - let sig = RecoverableSignature::from_compact(&[0; 64], RecoveryId(0)).unwrap(); - assert_eq!(s.recover(&msg, &sig), Err(InvalidSignature)); - // ...but 111..111 is - let sig = RecoverableSignature::from_compact(&[1; 64], RecoveryId(0)).unwrap(); - assert!(s.recover(&msg, &sig).is_ok()); } #[test] @@ -1154,62 +941,6 @@ mod tests { assert!(Message::from_slice(&[1; constants::MESSAGE_SIZE]).is_ok()); } - #[test] - fn test_debug_output() { - let sig = RecoverableSignature::from_compact(&[ - 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f, - 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6, - 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65, - 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98, - 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8, - 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f, - 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06, - 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89], - RecoveryId(1)).unwrap(); - assert_eq!(&format!("{:?}", sig), "RecoverableSignature(98882e09f4ed6dc3659e43fc771e0cafa60b1f926f2b77041f744721adff7366898cb609d0ee128d06ae9aa3c48020ff9f705e02f80e1280a8ade05216971a4c01)"); - - let msg = Message([1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 255]); - assert_eq!(&format!("{:?}", msg), "Message(0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fff)"); - } - - #[test] - fn test_recov_sig_serialize_compact() { - let recid_in = RecoveryId(1); - let bytes_in = &[ - 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f, - 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6, - 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65, - 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98, - 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8, - 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f, - 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06, - 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89]; - let sig = RecoverableSignature::from_compact( - bytes_in, - recid_in, - ).unwrap(); - let (recid_out, bytes_out) = sig.serialize_compact(); - assert_eq!(recid_in, recid_out); - assert_eq!(&bytes_in[..], &bytes_out[..]); - } - - #[test] - fn test_recov_id_conversion_between_i32() { - assert!(RecoveryId::from_i32(-1).is_err()); - assert!(RecoveryId::from_i32(0).is_ok()); - assert!(RecoveryId::from_i32(1).is_ok()); - assert!(RecoveryId::from_i32(2).is_ok()); - assert!(RecoveryId::from_i32(3).is_ok()); - assert!(RecoveryId::from_i32(4).is_err()); - let id0 = RecoveryId::from_i32(0).unwrap(); - assert_eq!(id0.to_i32(), 0); - let id1 = RecoveryId(1); - assert_eq!(id1.to_i32(), 1); - } - #[test] fn test_low_s() { // nb this is a transaction on testnet @@ -1304,19 +1035,4 @@ mod benches { black_box(res); }); } - - #[bench] - pub fn bench_recover(bh: &mut Bencher) { - let s = Secp256k1::new(); - let mut msg = [0u8; 32]; - thread_rng().fill_bytes(&mut msg); - let msg = Message::from_slice(&msg).unwrap(); - let (sk, _) = s.generate_keypair(&mut thread_rng()); - let sig = s.sign_recoverable(&msg, &sk); - - bh.iter(|| { - let res = s.recover(&msg, &sig).unwrap(); - black_box(res); - }); - } } diff --git a/src/recovery.rs b/src/recovery.rs new file mode 100644 index 0000000..c04005d --- /dev/null +++ b/src/recovery.rs @@ -0,0 +1,359 @@ +// TODO header +// Bitcoin secp256k1 bindings +// Written in 2014 by +// Dawid Ciężarkiewicz +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Recovery module +//! Provides a signing function that allows recovering the public key from the +//! signature. + +use core::ptr; +use ffi; +use key; +use super::{Secp256k1, Message, Error, Signature, Verification, Signing}; +pub use key::SecretKey; +pub use key::PublicKey; + +/// A tag used for recovering the public key from a compact signature +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct RecoveryId(i32); + +/// An ECDSA signature with a recovery ID for pubkey recovery +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct RecoverableSignature(ffi::RecoverableSignature); + +impl RecoveryId { +#[inline] +/// Allows library users to create valid recovery IDs from i32. +/// TODO +pub fn from_i32(id: i32) -> Result { + match id { + 0 | 1 | 2 | 3 => Ok(RecoveryId(id)), + _ => Err(Error::InvalidRecoveryId) + } +} + +#[inline] +/// Allows library users to convert recovery IDs to i32. +pub fn to_i32(self) -> i32 { + self.0 +} +} + +impl RecoverableSignature { + #[inline] + /// Converts a compact-encoded byte slice to a signature. This + /// representation is nonstandard and defined by the libsecp256k1 + /// library. + pub fn from_compact(data: &[u8], recid: RecoveryId) -> Result { + let mut ret = unsafe { ffi::RecoverableSignature::blank() }; + + unsafe { + if data.len() != 64 { + Err(Error::InvalidSignature) + } else if ffi::secp256k1_ecdsa_recoverable_signature_parse_compact( + ffi::secp256k1_context_no_precomp, + &mut ret, + data.as_ptr(), + recid.0, + ) == 1 + { + Ok(RecoverableSignature(ret)) + } else { + Err(Error::InvalidSignature) + } + } + } + + /// Obtains a raw pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::RecoverableSignature { + &self.0 as *const _ + } + + #[inline] + /// Serializes the recoverable signature in compact format + pub fn serialize_compact(&self) -> (RecoveryId, [u8; 64]) { + let mut ret = [0u8; 64]; + let mut recid = 0i32; + unsafe { + let err = ffi::secp256k1_ecdsa_recoverable_signature_serialize_compact( + ffi::secp256k1_context_no_precomp, + ret.as_mut_ptr(), + &mut recid, + self.as_ptr(), + ); + assert!(err == 1); + } + (RecoveryId(recid), ret) + } + + /// Converts a recoverable signature to a non-recoverable one (this is needed + /// for verification + #[inline] + pub fn to_standard(&self) -> Signature { + let mut ret = unsafe { ffi::Signature::blank() }; + unsafe { + let err = ffi::secp256k1_ecdsa_recoverable_signature_convert( + ffi::secp256k1_context_no_precomp, + &mut ret, + self.as_ptr(), + ); + assert!(err == 1); + } + Signature(ret) + } +} + +/// Creates a new recoverable signature from a FFI one +impl From for RecoverableSignature { + #[inline] + fn from(sig: ffi::RecoverableSignature) -> RecoverableSignature { + RecoverableSignature(sig) + } +} + +impl Secp256k1 { + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce + /// Requires a signing-capable context. + pub fn sign_recoverable(&self, msg: &Message, sk: &key::SecretKey) + -> RecoverableSignature { + + let mut ret = unsafe { ffi::RecoverableSignature::blank() }; + unsafe { + // We can assume the return value because it's not possible to construct + // an invalid signature from a valid `Message` and `SecretKey` + assert_eq!( + ffi::secp256k1_ecdsa_sign_recoverable( + self.ctx, + &mut ret, + msg.as_ptr(), + sk.as_ptr(), + ffi::secp256k1_nonce_function_rfc6979, + ptr::null() + ), + 1 + ); + } + + RecoverableSignature::from(ret) + } +} + +impl Secp256k1 { + /// Determines the public key for which `sig` is a valid signature for + /// `msg`. Requires a verify-capable context. + pub fn recover(&self, msg: &Message, sig: &RecoverableSignature) + -> Result { + + let mut pk = unsafe { ffi::PublicKey::blank() }; + + unsafe { + if ffi::secp256k1_ecdsa_recover(self.ctx, &mut pk, + sig.as_ptr(), msg.as_ptr()) != 1 { + return Err(Error::InvalidSignature); + } + }; + Ok(key::PublicKey::from(pk)) + } +} + + +#[cfg(test)] +mod tests { + use rand::{RngCore, thread_rng}; + + use key::{SecretKey, PublicKey}; + use super::{RecoveryId, RecoverableSignature}; + use super::super::{Secp256k1, Message}; + use super::super::Error::{IncorrectSignature, InvalidSignature}; + + #[test] + fn capabilities() { + let sign = Secp256k1::signing_only(); + let vrfy = Secp256k1::verification_only(); + let full = Secp256k1::new(); + + let mut msg = [0u8; 32]; + thread_rng().fill_bytes(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + + // Try key generation + let (sk, pk) = full.generate_keypair(&mut thread_rng()); + + // Try signing + assert_eq!(sign.sign_recoverable(&msg, &sk), full.sign_recoverable(&msg, &sk)); + let sigr = full.sign_recoverable(&msg, &sk); + + // Try pk recovery + assert!(vrfy.recover(&msg, &sigr).is_ok()); + assert!(full.recover(&msg, &sigr).is_ok()); + + assert_eq!(vrfy.recover(&msg, &sigr), + full.recover(&msg, &sigr)); + assert_eq!(full.recover(&msg, &sigr), Ok(pk)); + } + + #[test] + fn recid_sanity_check() { + let one = RecoveryId(1); + assert_eq!(one, one.clone()); + } + + #[test] + fn sign() { + let mut s = Secp256k1::new(); + s.randomize(&mut thread_rng()); + let one = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; + + let sk = SecretKey::from_slice(&one).unwrap(); + let msg = Message::from_slice(&one).unwrap(); + + let sig = s.sign_recoverable(&msg, &sk); + assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[ + 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f, + 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6, + 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65, + 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98, + 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8, + 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f, + 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06, + 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89], + RecoveryId(1))) + } + + #[test] + fn sign_and_verify_fail() { + let mut s = Secp256k1::new(); + s.randomize(&mut thread_rng()); + + let mut msg = [0u8; 32]; + thread_rng().fill_bytes(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + + let (sk, pk) = s.generate_keypair(&mut thread_rng()); + + let sigr = s.sign_recoverable(&msg, &sk); + let sig = sigr.to_standard(); + + let mut msg = [0u8; 32]; + thread_rng().fill_bytes(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + assert_eq!(s.verify(&msg, &sig, &pk), Err(IncorrectSignature)); + + let recovered_key = s.recover(&msg, &sigr).unwrap(); + assert!(recovered_key != pk); + } + + #[test] + fn sign_with_recovery() { + let mut s = Secp256k1::new(); + s.randomize(&mut thread_rng()); + + let mut msg = [0u8; 32]; + thread_rng().fill_bytes(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + + let (sk, pk) = s.generate_keypair(&mut thread_rng()); + + let sig = s.sign_recoverable(&msg, &sk); + + assert_eq!(s.recover(&msg, &sig), Ok(pk)); + } + + #[test] + fn bad_recovery() { + let mut s = Secp256k1::new(); + s.randomize(&mut thread_rng()); + + let msg = Message::from_slice(&[0x55; 32]).unwrap(); + + // Zero is not a valid sig + let sig = RecoverableSignature::from_compact(&[0; 64], RecoveryId(0)).unwrap(); + assert_eq!(s.recover(&msg, &sig), Err(InvalidSignature)); + // ...but 111..111 is + let sig = RecoverableSignature::from_compact(&[1; 64], RecoveryId(0)).unwrap(); + assert!(s.recover(&msg, &sig).is_ok()); + } + + #[test] + fn test_debug_output() { + let sig = RecoverableSignature::from_compact(&[ + 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f, + 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6, + 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65, + 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98, + 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8, + 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f, + 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06, + 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89], + RecoveryId(1)).unwrap(); + assert_eq!(&format!("{:?}", sig), "RecoverableSignature(98882e09f4ed6dc3659e43fc771e0cafa60b1f926f2b77041f744721adff7366898cb609d0ee128d06ae9aa3c48020ff9f705e02f80e1280a8ade05216971a4c01)"); + } + + #[test] + fn test_recov_sig_serialize_compact() { + let recid_in = RecoveryId(1); + let bytes_in = &[ + 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f, + 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6, + 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65, + 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98, + 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8, + 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f, + 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06, + 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89]; + let sig = RecoverableSignature::from_compact( + bytes_in, + recid_in, + ).unwrap(); + let (recid_out, bytes_out) = sig.serialize_compact(); + assert_eq!(recid_in, recid_out); + assert_eq!(&bytes_in[..], &bytes_out[..]); + } + + #[test] + fn test_recov_id_conversion_between_i32() { + assert!(RecoveryId::from_i32(-1).is_err()); + assert!(RecoveryId::from_i32(0).is_ok()); + assert!(RecoveryId::from_i32(1).is_ok()); + assert!(RecoveryId::from_i32(2).is_ok()); + assert!(RecoveryId::from_i32(3).is_ok()); + assert!(RecoveryId::from_i32(4).is_err()); + let id0 = RecoveryId::from_i32(0).unwrap(); + assert_eq!(id0.to_i32(), 0); + let id1 = RecoveryId(1); + assert_eq!(id1.to_i32(), 1); + } +} + + +#[cfg(all(test, feature = "unstable"))] +mod benches { + #[bench] + pub fn bench_recover(bh: &mut Bencher) { + let s = Secp256k1::new(); + let mut msg = [0u8; 32]; + thread_rng().fill_bytes(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + let (sk, _) = s.generate_keypair(&mut thread_rng()); + let sig = s.sign_recoverable(&msg, &sk); + + bh.iter(|| { + let res = s.recover(&msg, &sig).unwrap(); + black_box(res); + }); + } +}