// 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 key; use super::{Secp256k1, Message, Error, Signature, Verification, Signing}; use super::ffi as super_ffi; pub use key::SecretKey; pub use key::PublicKey; mod ffi; /// 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. 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( super_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( super_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 { super_ffi::Signature::blank() }; unsafe { let err = ffi::secp256k1_ecdsa_recoverable_signature_convert( super_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(), super_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 { super_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; 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); }); } }