secp256k1_sys/
recovery.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! # FFI of the recovery module
4
5use crate::{Context, Signature, NonceFn, PublicKey, CPtr, impl_array_newtype, secp256k1_context_no_precomp};
6use crate::types::*;
7use core::fmt;
8
9/// Library-internal representation of a Secp256k1 signature + recovery ID
10#[repr(C)]
11#[derive(Copy, Clone)]
12#[cfg_attr(secp256k1_fuzz, derive(PartialEq, Eq, PartialOrd, Ord, Hash))]
13pub struct RecoverableSignature([c_uchar; 65]);
14impl_array_newtype!(RecoverableSignature, c_uchar, 65);
15
16impl RecoverableSignature {
17    /// Create a new (zeroed) signature usable for the FFI interface
18    pub fn new() -> RecoverableSignature { RecoverableSignature([0; 65]) }
19
20    /// Serializes the signature in compact format.
21    fn serialize(&self) -> [u8; 65] {
22        let mut buf = [0u8; 65];
23        let mut recid = 0;
24        unsafe {
25            let ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(
26                secp256k1_context_no_precomp,
27                buf.as_mut_c_ptr(),
28                &mut recid,
29                self,
30            );
31            debug_assert!(ret == 1);
32        }
33        buf[64] = (recid & 0xFF) as u8;
34        buf
35    }
36}
37
38impl Default for RecoverableSignature {
39    fn default() -> Self {
40        RecoverableSignature::new()
41    }
42}
43
44impl fmt::Debug for RecoverableSignature {
45    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46        let mut ret = [0u8; 64];
47        let mut recid = 0i32;
48
49        unsafe {
50            let err = secp256k1_ecdsa_recoverable_signature_serialize_compact(
51                super::secp256k1_context_no_precomp,
52                ret.as_mut_c_ptr(),
53                &mut recid,
54                self,
55            );
56            assert!(err == 1);
57        }
58
59        for byte in ret.iter() {
60            write!(f, "{:02x}", byte)?;
61        }
62        write!(f, "{:02x}", recid as u8)?;
63
64        Ok(())
65    }
66}
67
68#[cfg(not(secp256k1_fuzz))]
69impl PartialOrd for RecoverableSignature {
70    fn partial_cmp(&self, other: &RecoverableSignature) -> Option<core::cmp::Ordering> {
71        Some(self.cmp(other))
72    }
73}
74
75#[cfg(not(secp256k1_fuzz))]
76impl Ord for RecoverableSignature {
77    fn cmp(&self, other: &RecoverableSignature) -> core::cmp::Ordering {
78        let this = self.serialize();
79        let that = other.serialize();
80        this.cmp(&that)
81    }
82}
83
84#[cfg(not(secp256k1_fuzz))]
85impl PartialEq for RecoverableSignature {
86    fn eq(&self, other: &Self) -> bool {
87        self.cmp(other) == core::cmp::Ordering::Equal
88    }
89}
90
91#[cfg(not(secp256k1_fuzz))]
92impl Eq for RecoverableSignature {}
93
94#[cfg(not(secp256k1_fuzz))]
95impl core::hash::Hash for RecoverableSignature {
96    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
97        let ser = self.serialize();
98        ser.hash(state);
99    }
100}
101
102extern "C" {
103    #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_10_0_ecdsa_recoverable_signature_parse_compact")]
104    pub fn secp256k1_ecdsa_recoverable_signature_parse_compact(cx: *const Context, sig: *mut RecoverableSignature,
105                                                               input64: *const c_uchar, recid: c_int)
106                                                               -> c_int;
107
108    #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_10_0_ecdsa_recoverable_signature_serialize_compact")]
109    pub fn secp256k1_ecdsa_recoverable_signature_serialize_compact(cx: *const Context, output64: *mut c_uchar,
110                                                                   recid: *mut c_int, sig: *const RecoverableSignature)
111                                                                   -> c_int;
112
113    #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_10_0_ecdsa_recoverable_signature_convert")]
114    pub fn secp256k1_ecdsa_recoverable_signature_convert(cx: *const Context, sig: *mut Signature,
115                                                         input: *const RecoverableSignature)
116                                                         -> c_int;
117}
118
119#[cfg(not(secp256k1_fuzz))]
120extern "C" {
121    #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_10_0_ecdsa_sign_recoverable")]
122    pub fn secp256k1_ecdsa_sign_recoverable(cx: *const Context,
123                                            sig: *mut RecoverableSignature,
124                                            msg32: *const c_uchar,
125                                            sk: *const c_uchar,
126                                            noncefn: NonceFn,
127                                            noncedata: *const c_void)
128                                            -> c_int;
129
130    #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_10_0_ecdsa_recover")]
131    pub fn secp256k1_ecdsa_recover(cx: *const Context,
132                                   pk: *mut PublicKey,
133                                   sig: *const RecoverableSignature,
134                                   msg32: *const c_uchar)
135                                   -> c_int;
136}
137
138
139#[cfg(secp256k1_fuzz)]
140mod fuzz_dummy {
141    use core::slice;
142
143    use crate::{secp256k1_ec_pubkey_create, secp256k1_ec_pubkey_parse, secp256k1_ec_pubkey_serialize, SECP256K1_SER_COMPRESSED};
144    use super::*;
145
146    /// Sets sig to msg32||full pk
147    pub unsafe fn secp256k1_ecdsa_sign_recoverable(
148        cx: *const Context,
149        sig: *mut RecoverableSignature,
150        msg32: *const c_uchar,
151        sk: *const c_uchar,
152        _noncefn: NonceFn,
153        _noncedata: *const c_void,
154    ) -> c_int {
155        // Check context is built for signing (and compute pk)
156        let mut new_pk = PublicKey::new();
157        if secp256k1_ec_pubkey_create(cx, &mut new_pk, sk) != 1 {
158            return 0;
159        }
160        // Sign
161        let sig_sl = slice::from_raw_parts_mut(sig as *mut u8, 65);
162        let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32);
163        sig_sl[..32].copy_from_slice(msg_sl);
164        let mut out_len: size_t = 33;
165        secp256k1_ec_pubkey_serialize(cx, sig_sl[32..].as_mut_ptr(), &mut out_len, &new_pk, SECP256K1_SER_COMPRESSED);
166        // Encode the parity of the pubkey in the final byte as 0/1,
167        // which is the same encoding (though the parity is computed
168        // differently) as real recoverable signatures.
169        sig_sl.swap(32, 64);
170        sig_sl[64] -= 2;
171        1
172    }
173
174    pub unsafe fn secp256k1_ecdsa_recover(
175        cx: *const Context,
176        pk: *mut PublicKey,
177        sig: *const RecoverableSignature,
178        msg32: *const c_uchar
179    ) -> c_int {
180        let sig_sl = slice::from_raw_parts(sig as *const u8, 65);
181        let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32);
182
183        if sig_sl[64] >= 4 {
184            return 0;
185        }
186        // Pull the original pk out of the siganture
187        let mut pk_ser = [0u8; 33];
188        pk_ser.copy_from_slice(&sig_sl[32..]);
189        pk_ser.swap(0, 32);
190        pk_ser[0] += 2;
191        // Check that it parses (in a real sig, this would be the R value,
192        // so it is actually required to be a valid point)
193        if secp256k1_ec_pubkey_parse(cx, pk, pk_ser.as_ptr(), 33) == 0 {
194            return 0;
195        }
196        // Munge it up so that a different message will give a different pk
197        for i in 0..32 {
198            pk_ser[i + 1] ^= sig_sl[i] ^ msg_sl[i];
199        }
200        // If any munging happened, this will fail parsing half the time, so
201        // tweak-and-loop until we find a key that works.
202        let mut idx = 0;
203        while secp256k1_ec_pubkey_parse(cx, pk, pk_ser.as_ptr(), 33) == 0 {
204            pk_ser[1 + idx / 8] ^= 1 << (idx % 8);
205            idx += 1;
206        }
207        1
208    }
209}
210
211#[cfg(secp256k1_fuzz)]
212pub use self::fuzz_dummy::*;