1use core::ptr;
8
9use self::super_ffi::CPtr;
10use super::ffi as super_ffi;
11use crate::ecdsa::Signature;
12use crate::ffi::recovery as ffi;
13use crate::{key, Error, Message, Secp256k1, Signing, Verification};
14
15#[derive(Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
17pub struct RecoveryId(i32);
18
19#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
21pub struct RecoverableSignature(ffi::RecoverableSignature);
22
23impl RecoveryId {
24 #[inline]
25 pub fn from_i32(id: i32) -> Result<RecoveryId, Error> {
27 match id {
28 0..=3 => Ok(RecoveryId(id)),
29 _ => Err(Error::InvalidRecoveryId),
30 }
31 }
32
33 #[inline]
34 pub fn to_i32(self) -> i32 { self.0 }
36}
37
38impl RecoverableSignature {
39 #[inline]
40 pub fn from_compact(data: &[u8], recid: RecoveryId) -> Result<RecoverableSignature, Error> {
43 if data.is_empty() {
44 return Err(Error::InvalidSignature);
45 }
46
47 let mut ret = ffi::RecoverableSignature::new();
48
49 unsafe {
50 if data.len() != 64 {
51 Err(Error::InvalidSignature)
52 } else if ffi::secp256k1_ecdsa_recoverable_signature_parse_compact(
53 super_ffi::secp256k1_context_no_precomp,
54 &mut ret,
55 data.as_c_ptr(),
56 recid.0,
57 ) == 1
58 {
59 Ok(RecoverableSignature(ret))
60 } else {
61 Err(Error::InvalidSignature)
62 }
63 }
64 }
65
66 #[inline]
68 #[deprecated(since = "0.25.0", note = "Use Self::as_c_ptr if you need to access the FFI layer")]
69 pub fn as_ptr(&self) -> *const ffi::RecoverableSignature { self.as_c_ptr() }
70
71 #[inline]
73 #[deprecated(
74 since = "0.25.0",
75 note = "Use Self::as_mut_c_ptr if you need to access the FFI layer"
76 )]
77 pub fn as_mut_ptr(&mut self) -> *mut ffi::RecoverableSignature { self.as_mut_c_ptr() }
78
79 #[inline]
80 pub fn serialize_compact(&self) -> (RecoveryId, [u8; 64]) {
82 let mut ret = [0u8; 64];
83 let mut recid = 0i32;
84 unsafe {
85 let err = ffi::secp256k1_ecdsa_recoverable_signature_serialize_compact(
86 super_ffi::secp256k1_context_no_precomp,
87 ret.as_mut_c_ptr(),
88 &mut recid,
89 self.as_c_ptr(),
90 );
91 assert!(err == 1);
92 }
93 (RecoveryId(recid), ret)
94 }
95
96 #[inline]
99 pub fn to_standard(&self) -> Signature {
100 unsafe {
101 let mut ret = super_ffi::Signature::new();
102 let err = ffi::secp256k1_ecdsa_recoverable_signature_convert(
103 super_ffi::secp256k1_context_no_precomp,
104 &mut ret,
105 self.as_c_ptr(),
106 );
107 assert!(err == 1);
108 Signature(ret)
109 }
110 }
111
112 #[inline]
115 #[cfg(feature = "global-context")]
116 pub fn recover(&self, msg: &Message) -> Result<key::PublicKey, Error> {
117 crate::SECP256K1.recover_ecdsa(msg, self)
118 }
119}
120
121impl CPtr for RecoverableSignature {
122 type Target = ffi::RecoverableSignature;
123 fn as_c_ptr(&self) -> *const Self::Target { &self.0 }
124
125 fn as_mut_c_ptr(&mut self) -> *mut Self::Target { &mut self.0 }
126}
127
128impl From<ffi::RecoverableSignature> for RecoverableSignature {
130 #[inline]
131 fn from(sig: ffi::RecoverableSignature) -> RecoverableSignature { RecoverableSignature(sig) }
132}
133
134impl<C: Signing> Secp256k1<C> {
135 fn sign_ecdsa_recoverable_with_noncedata_pointer(
136 &self,
137 msg: &Message,
138 sk: &key::SecretKey,
139 noncedata_ptr: *const super_ffi::types::c_void,
140 ) -> RecoverableSignature {
141 let mut ret = ffi::RecoverableSignature::new();
142 unsafe {
143 assert_eq!(
146 ffi::secp256k1_ecdsa_sign_recoverable(
147 self.ctx.as_ptr(),
148 &mut ret,
149 msg.as_c_ptr(),
150 sk.as_c_ptr(),
151 super_ffi::secp256k1_nonce_function_rfc6979,
152 noncedata_ptr
153 ),
154 1
155 );
156 }
157
158 RecoverableSignature::from(ret)
159 }
160
161 pub fn sign_ecdsa_recoverable(
164 &self,
165 msg: &Message,
166 sk: &key::SecretKey,
167 ) -> RecoverableSignature {
168 self.sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, ptr::null())
169 }
170
171 pub fn sign_ecdsa_recoverable_with_noncedata(
177 &self,
178 msg: &Message,
179 sk: &key::SecretKey,
180 noncedata: &[u8; 32],
181 ) -> RecoverableSignature {
182 let noncedata_ptr = noncedata.as_ptr() as *const super_ffi::types::c_void;
183 self.sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, noncedata_ptr)
184 }
185}
186
187impl<C: Verification> Secp256k1<C> {
188 pub fn recover_ecdsa(
191 &self,
192 msg: &Message,
193 sig: &RecoverableSignature,
194 ) -> Result<key::PublicKey, Error> {
195 unsafe {
196 let mut pk = super_ffi::PublicKey::new();
197 if ffi::secp256k1_ecdsa_recover(
198 self.ctx.as_ptr(),
199 &mut pk,
200 sig.as_c_ptr(),
201 msg.as_c_ptr(),
202 ) != 1
203 {
204 return Err(Error::InvalidSignature);
205 }
206 Ok(key::PublicKey::from(pk))
207 }
208 }
209}
210
211#[cfg(test)]
212#[allow(unused_imports)]
213mod tests {
214 #[cfg(target_arch = "wasm32")]
215 use wasm_bindgen_test::wasm_bindgen_test as test;
216
217 use super::{RecoverableSignature, RecoveryId};
218 use crate::constants::ONE;
219 use crate::{Error, Message, Secp256k1, SecretKey};
220
221 #[test]
222 #[cfg(feature = "rand-std")]
223 fn capabilities() {
224 let sign = Secp256k1::signing_only();
225 let vrfy = Secp256k1::verification_only();
226 let full = Secp256k1::new();
227
228 let msg = crate::random_32_bytes(&mut rand::thread_rng());
229 let msg = Message::from_digest_slice(&msg).unwrap();
230
231 let (sk, pk) = full.generate_keypair(&mut rand::thread_rng());
233
234 assert_eq!(sign.sign_ecdsa_recoverable(&msg, &sk), full.sign_ecdsa_recoverable(&msg, &sk));
236 let sigr = full.sign_ecdsa_recoverable(&msg, &sk);
237
238 assert!(vrfy.recover_ecdsa(&msg, &sigr).is_ok());
240 assert!(full.recover_ecdsa(&msg, &sigr).is_ok());
241
242 assert_eq!(vrfy.recover_ecdsa(&msg, &sigr), full.recover_ecdsa(&msg, &sigr));
243 assert_eq!(full.recover_ecdsa(&msg, &sigr), Ok(pk));
244 }
245
246 #[test]
247 fn recid_sanity_check() {
248 let one = RecoveryId(1);
249 assert_eq!(one, one.clone());
250 }
251
252 #[test]
253 #[cfg(not(secp256k1_fuzz))] #[cfg(feature = "rand-std")]
255 #[rustfmt::skip]
256 fn sign() {
257 let mut s = Secp256k1::new();
258 s.randomize(&mut rand::thread_rng());
259
260 let sk = SecretKey::from_slice(&ONE).unwrap();
261 let msg = Message::from_digest_slice(&ONE).unwrap();
262
263 let sig = s.sign_ecdsa_recoverable(&msg, &sk);
264
265 assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[
266 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
267 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6,
268 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65,
269 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98,
270 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8,
271 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f,
272 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06,
273 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89],
274 RecoveryId(1)))
275 }
276
277 #[test]
278 #[cfg(not(secp256k1_fuzz))] #[cfg(feature = "rand-std")]
280 #[rustfmt::skip]
281 fn sign_with_noncedata() {
282 let mut s = Secp256k1::new();
283 s.randomize(&mut rand::thread_rng());
284
285 let sk = SecretKey::from_slice(&ONE).unwrap();
286 let msg = Message::from_digest_slice(&ONE).unwrap();
287 let noncedata = [42u8; 32];
288
289 let sig = s.sign_ecdsa_recoverable_with_noncedata(&msg, &sk, &noncedata);
290
291 assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[
292 0xb5, 0x0b, 0xb6, 0x79, 0x5f, 0x31, 0x74, 0x8a,
293 0x4d, 0x37, 0xc3, 0xa9, 0x7e, 0xbd, 0x06, 0xa2,
294 0x2e, 0xa3, 0x37, 0x71, 0x04, 0x0f, 0x5c, 0x05,
295 0xd6, 0xe2, 0xbb, 0x2d, 0x38, 0xc6, 0x22, 0x7c,
296 0x34, 0x3b, 0x66, 0x59, 0xdb, 0x96, 0x99, 0x59,
297 0xd9, 0xfd, 0xdb, 0x44, 0xbd, 0x0d, 0xd9, 0xb9,
298 0xdd, 0x47, 0x66, 0x6a, 0xb5, 0x28, 0x71, 0x90,
299 0x1d, 0x17, 0x61, 0xeb, 0x82, 0xec, 0x87, 0x22],
300 RecoveryId(0)))
301 }
302
303 #[test]
304 #[cfg(feature = "rand-std")]
305 fn sign_and_verify_fail() {
306 let mut s = Secp256k1::new();
307 s.randomize(&mut rand::thread_rng());
308
309 let msg = crate::random_32_bytes(&mut rand::thread_rng());
310 let msg = Message::from_digest_slice(&msg).unwrap();
311
312 let (sk, pk) = s.generate_keypair(&mut rand::thread_rng());
313
314 let sigr = s.sign_ecdsa_recoverable(&msg, &sk);
315 let sig = sigr.to_standard();
316
317 let msg = crate::random_32_bytes(&mut rand::thread_rng());
318 let msg = Message::from_digest_slice(&msg).unwrap();
319 assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Err(Error::IncorrectSignature));
320
321 let recovered_key = s.recover_ecdsa(&msg, &sigr).unwrap();
322 assert!(recovered_key != pk);
323 }
324
325 #[test]
326 #[cfg(feature = "rand-std")]
327 fn sign_with_recovery() {
328 let mut s = Secp256k1::new();
329 s.randomize(&mut rand::thread_rng());
330
331 let msg = crate::random_32_bytes(&mut rand::thread_rng());
332 let msg = Message::from_digest_slice(&msg).unwrap();
333
334 let (sk, pk) = s.generate_keypair(&mut rand::thread_rng());
335
336 let sig = s.sign_ecdsa_recoverable(&msg, &sk);
337
338 assert_eq!(s.recover_ecdsa(&msg, &sig), Ok(pk));
339 }
340
341 #[test]
342 #[cfg(feature = "rand-std")]
343 fn sign_with_recovery_and_noncedata() {
344 let mut s = Secp256k1::new();
345 s.randomize(&mut rand::thread_rng());
346
347 let msg = crate::random_32_bytes(&mut rand::thread_rng());
348 let msg = Message::from_digest_slice(&msg).unwrap();
349
350 let noncedata = [42u8; 32];
351
352 let (sk, pk) = s.generate_keypair(&mut rand::thread_rng());
353
354 let sig = s.sign_ecdsa_recoverable_with_noncedata(&msg, &sk, &noncedata);
355
356 assert_eq!(s.recover_ecdsa(&msg, &sig), Ok(pk));
357 }
358
359 #[test]
360 #[cfg(feature = "rand-std")]
361 fn bad_recovery() {
362 let mut s = Secp256k1::new();
363 s.randomize(&mut rand::thread_rng());
364
365 let msg = Message::from_digest_slice(&[0x55; 32]).unwrap();
366
367 let sig = RecoverableSignature::from_compact(&[0; 64], RecoveryId(0)).unwrap();
369 assert_eq!(s.recover_ecdsa(&msg, &sig), Err(Error::InvalidSignature));
370 let sig = RecoverableSignature::from_compact(&[1; 64], RecoveryId(0)).unwrap();
372 assert!(s.recover_ecdsa(&msg, &sig).is_ok());
373 }
374
375 #[test]
376 fn test_debug_output() {
377 #[rustfmt::skip]
378 let sig = RecoverableSignature::from_compact(&[
379 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
380 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6,
381 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65,
382 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98,
383 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8,
384 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f,
385 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06,
386 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89],
387 RecoveryId(1)).unwrap();
388 assert_eq!(&format!("{:?}", sig), "RecoverableSignature(6673ffad2147741f04772b6f921f0ba6af0c1e77fc439e65c36dedf4092e88984c1a971652e0ada880120ef8025e709fff2080c4a39aae068d12eed009b68c8901)");
389 }
390
391 #[test]
392 fn test_recov_sig_serialize_compact() {
393 let recid_in = RecoveryId(1);
394 #[rustfmt::skip]
395 let bytes_in = &[
396 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
397 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6,
398 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65,
399 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98,
400 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8,
401 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f,
402 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06,
403 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89];
404 let sig = RecoverableSignature::from_compact(bytes_in, recid_in).unwrap();
405 let (recid_out, bytes_out) = sig.serialize_compact();
406 assert_eq!(recid_in, recid_out);
407 assert_eq!(&bytes_in[..], &bytes_out[..]);
408 }
409
410 #[test]
411 fn test_recov_id_conversion_between_i32() {
412 assert!(RecoveryId::from_i32(-1).is_err());
413 assert!(RecoveryId::from_i32(0).is_ok());
414 assert!(RecoveryId::from_i32(1).is_ok());
415 assert!(RecoveryId::from_i32(2).is_ok());
416 assert!(RecoveryId::from_i32(3).is_ok());
417 assert!(RecoveryId::from_i32(4).is_err());
418 let id0 = RecoveryId::from_i32(0).unwrap();
419 assert_eq!(id0.to_i32(), 0);
420 let id1 = RecoveryId(1);
421 assert_eq!(id1.to_i32(), 1);
422 }
423}
424
425#[cfg(bench)]
426#[cfg(feature = "rand-std")] mod benches {
428 use test::{black_box, Bencher};
429
430 use super::{Message, Secp256k1};
431
432 #[bench]
433 pub fn bench_recover(bh: &mut Bencher) {
434 let s = Secp256k1::new();
435 let msg = crate::random_32_bytes(&mut rand::thread_rng());
436 let msg = Message::from_digest_slice(&msg).unwrap();
437 let (sk, _) = s.generate_keypair(&mut rand::thread_rng());
438 let sig = s.sign_ecdsa_recoverable(&msg, &sk);
439
440 bh.iter(|| {
441 let res = s.recover_ecdsa(&msg, &sig).unwrap();
442 black_box(res);
443 });
444 }
445}