Skip to main content

secp256k1_sys/
recovery.rs

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