Move recovery to its own module

This commit is contained in:
Jonas Nick 2019-05-20 19:11:59 +00:00
parent b843f72955
commit 264b368ee0
2 changed files with 362 additions and 287 deletions

View File

@ -152,16 +152,13 @@ pub mod constants;
pub mod ecdh; pub mod ecdh;
pub mod ffi; pub mod ffi;
pub mod key; pub mod key;
pub mod recovery;
pub use key::SecretKey; pub use key::SecretKey;
pub use key::PublicKey; pub use key::PublicKey;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::ops::Deref; 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 /// An ECDSA signature
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub struct Signature(ffi::Signature); pub struct Signature(ffi::Signature);
@ -210,10 +207,6 @@ fn from_str(s: &str) -> Result<Signature, Error> {
} }
} }
/// 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, /// 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 /// it has negligible probability of being zero or overflowing the group order. Such objects
/// may be converted to `Message`s without any error paths. /// may be converted to `Message`s without any error paths.
@ -222,23 +215,6 @@ pub trait ThirtyTwoByteHash {
fn into_32(self) -> [u8; 32]; 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<RecoveryId, Error> {
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 { impl SerializedSignature {
/// Get a pointer to the underlying data with the specified capacity. /// Get a pointer to the underlying data with the specified capacity.
pub(crate) fn get_data_mut_ptr(&mut self) -> *mut u8 { pub(crate) fn get_data_mut_ptr(&mut self) -> *mut u8 {
@ -420,79 +396,6 @@ impl From<ffi::Signature> 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<RecoverableSignature, Error> {
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<ffi::RecoverableSignature> for RecoverableSignature {
#[inline]
fn from(sig: ffi::RecoverableSignature) -> RecoverableSignature {
RecoverableSignature(sig)
}
}
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl ::serde::Serialize for Signature { impl ::serde::Serialize for Signature {
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
@ -767,31 +670,6 @@ impl<C: Signing> Secp256k1<C> {
Signature::from(ret) 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` /// Generates a random keypair. Convenience function for `key::SecretKey::new`
/// and `key::PublicKey::from_secret_key`; call those functions directly for /// and `key::PublicKey::from_secret_key`; call those functions directly for
/// batch key generation. Requires a signing-capable context. Requires compilation /// batch key generation. Requires a signing-capable context. Requires compilation
@ -807,23 +685,6 @@ impl<C: Signing> Secp256k1<C> {
} }
impl<C: Verification> Secp256k1<C> { impl<C: Verification> Secp256k1<C> {
/// 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<key::PublicKey, Error> {
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 /// 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 /// key `pubkey`. Returns `Ok(true)` on success. Note that this function cannot
/// be used for Bitcoin consensus checking since there may exist signatures /// be used for Bitcoin consensus checking since there may exist signatures
@ -877,7 +738,7 @@ mod tests {
use key::{SecretKey, PublicKey}; use key::{SecretKey, PublicKey};
use super::from_hex; use super::from_hex;
use super::constants; use super::constants;
use super::{Secp256k1, Signature, RecoverableSignature, Message, RecoveryId}; use super::{Secp256k1, Signature, Message};
use super::Error::{InvalidMessage, IncorrectSignature, InvalidSignature}; use super::Error::{InvalidMessage, IncorrectSignature, InvalidSignature};
macro_rules! hex { macro_rules! hex {
@ -903,22 +764,12 @@ mod tests {
// Try signing // Try signing
assert_eq!(sign.sign(&msg, &sk), full.sign(&msg, &sk)); 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 sig = full.sign(&msg, &sk);
let sigr = full.sign_recoverable(&msg, &sk);
// Try verifying // Try verifying
assert!(vrfy.verify(&msg, &sig, &pk).is_ok()); assert!(vrfy.verify(&msg, &sig, &pk).is_ok());
assert!(full.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 // 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[..]);
let new_pk = PublicKey::from_slice(pk_slice).unwrap(); let new_pk = PublicKey::from_slice(pk_slice).unwrap();
@ -927,35 +778,6 @@ mod tests {
assert_eq!(pk, new_pk); 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] #[test]
fn signature_serialize_roundtrip() { fn signature_serialize_roundtrip() {
let mut s = Secp256k1::new(); let mut s = Secp256k1::new();
@ -1093,47 +915,12 @@ mod tests {
let (sk, pk) = s.generate_keypair(&mut thread_rng()); let (sk, pk) = s.generate_keypair(&mut thread_rng());
let sigr = s.sign_recoverable(&msg, &sk); let sig = s.sign(&msg, &sk);
let sig = sigr.to_standard();
let mut msg = [0u8; 32]; let mut msg = [0u8; 32];
thread_rng().fill_bytes(&mut msg); thread_rng().fill_bytes(&mut msg);
let msg = Message::from_slice(&msg).unwrap(); let msg = Message::from_slice(&msg).unwrap();
assert_eq!(s.verify(&msg, &sig, &pk), Err(IncorrectSignature)); 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] #[test]
@ -1154,62 +941,6 @@ mod tests {
assert!(Message::from_slice(&[1; constants::MESSAGE_SIZE]).is_ok()); 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] #[test]
fn test_low_s() { fn test_low_s() {
// nb this is a transaction on testnet // nb this is a transaction on testnet
@ -1304,19 +1035,4 @@ mod benches {
black_box(res); 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);
});
}
} }

359
src/recovery.rs Normal file
View File

@ -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 <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! # 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<RecoveryId, Error> {
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<RecoverableSignature, Error> {
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<ffi::RecoverableSignature> for RecoverableSignature {
#[inline]
fn from(sig: ffi::RecoverableSignature) -> RecoverableSignature {
RecoverableSignature(sig)
}
}
impl<C: Signing> Secp256k1<C> {
/// 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<C: Verification> Secp256k1<C> {
/// 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<key::PublicKey, Error> {
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);
});
}
}