Skip to content

Commit 96862b6

Browse files
committed
fuzz: implement recoverable signatures, get all tests passing, run them in CI
1 parent b811ec1 commit 96862b6

File tree

6 files changed

+84
-61
lines changed

6 files changed

+84
-61
lines changed

contrib/test.sh

+8-3
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,17 @@ if [ "$DO_FEATURE_MATRIX" = true ]; then
3333
cargo test --all --features="$feature"
3434
done
3535

36-
# Other combos
37-
RUSTFLAGS='--cfg=rust_secp_fuzz' cargo test --no-run --all
38-
RUSTFLAGS='--cfg=rust_secp_fuzz' cargo test --no-run --all --features="recovery"
36+
# Other combos
37+
RUSTFLAGS='--cfg=rust_secp_fuzz' cargo test --all
38+
RUSTFLAGS='--cfg=rust_secp_fuzz' cargo test --all --features="$FEATURES"
3939
cargo test --all --features="rand rand-std"
4040
cargo test --all --features="rand serde"
4141

42+
if [ "$DO_BENCH" = true ]; then # proxy for us having a nightly compiler
43+
cargo test --all --all-features
44+
RUSTFLAGS='--cfg=rust_secp_fuzz' RUSTDOCFLAGS='--cfg=rust_secp_fuzz' cargo test --all --all-features
45+
fi
46+
4247
# Examples
4348
cargo run --example sign_verify
4449
cargo run --example sign_verify_recovery --features=recovery

secp256k1-sys/src/lib.rs

-7
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,6 @@ pub type SchnorrNonceFn = Option<unsafe extern "C" fn(
9797
#[derive(Clone, Debug)]
9898
#[repr(C)] pub struct Context(c_int);
9999

100-
#[cfg(rust_secp_fuzz)]
101-
impl Context {
102-
pub fn flags(&self) -> u32 {
103-
self.0 as u32
104-
}
105-
}
106-
107100
/// Library-internal representation of a Secp256k1 public key
108101
#[repr(C)]
109102
pub struct PublicKey([c_uchar; 64]);

secp256k1-sys/src/recovery.rs

+72-51
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
//! # FFI of the recovery module
1717
1818
use ::types::*;
19-
#[cfg(not(rust_secp_fuzz))]
20-
use ::{Context, Signature, NonceFn, PublicKey};
19+
use {Context, Signature, NonceFn, PublicKey};
2120

2221
/// Library-internal representation of a Secp256k1 signature + recovery ID
2322
#[repr(C)]
@@ -36,7 +35,6 @@ impl Default for RecoverableSignature {
3635
}
3736
}
3837

39-
#[cfg(not(rust_secp_fuzz))]
4038
extern "C" {
4139
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdsa_recoverable_signature_parse_compact")]
4240
pub fn secp256k1_ecdsa_recoverable_signature_parse_compact(cx: *const Context, sig: *mut RecoverableSignature,
@@ -52,6 +50,10 @@ extern "C" {
5250
pub fn secp256k1_ecdsa_recoverable_signature_convert(cx: *const Context, sig: *mut Signature,
5351
input: *const RecoverableSignature)
5452
-> c_int;
53+
}
54+
55+
#[cfg(not(rust_secp_fuzz))]
56+
extern "C" {
5557
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdsa_sign_recoverable")]
5658
pub fn secp256k1_ecdsa_sign_recoverable(cx: *const Context,
5759
sig: *mut RecoverableSignature,
@@ -72,58 +74,77 @@ extern "C" {
7274

7375
#[cfg(rust_secp_fuzz)]
7476
mod fuzz_dummy {
75-
extern crate std;
76-
use self::std::ptr;
77-
use super::RecoverableSignature;
78-
use types::*;
79-
use ::{Signature, Context, PublicKey, NonceFn, secp256k1_ec_seckey_verify,
80-
SECP256K1_START_NONE, SECP256K1_START_VERIFY, SECP256K1_START_SIGN};
81-
82-
pub unsafe fn secp256k1_ecdsa_recoverable_signature_parse_compact(_cx: *const Context, _sig: *mut RecoverableSignature,
83-
_input64: *const c_uchar, _recid: c_int)
84-
-> c_int {
85-
unimplemented!();
86-
}
87-
88-
pub unsafe fn secp256k1_ecdsa_recoverable_signature_serialize_compact(_cx: *const Context, _output64: *mut c_uchar,
89-
_recid: *mut c_int, _sig: *const RecoverableSignature)
90-
-> c_int {
91-
unimplemented!();
92-
}
93-
94-
pub unsafe fn secp256k1_ecdsa_recoverable_signature_convert(_cx: *const Context, _sig: *mut Signature,
95-
_input: *const RecoverableSignature)
96-
-> c_int {
97-
unimplemented!();
98-
}
99-
100-
/// Sets sig to (2|3)||msg32||sk
101-
pub unsafe fn secp256k1_ecdsa_sign_recoverable(cx: *const Context,
102-
sig: *mut RecoverableSignature,
103-
msg32: *const c_uchar,
104-
sk: *const c_uchar,
105-
_noncefn: NonceFn,
106-
_noncedata: *const c_void)
107-
-> c_int {
108-
assert!(!cx.is_null() && (*cx).flags() & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0);
109-
assert!((*cx).flags() & SECP256K1_START_SIGN == SECP256K1_START_SIGN);
110-
if secp256k1_ec_seckey_verify(cx, sk) != 1 { return 0; }
111-
if *sk.offset(0) > 0x7f {
112-
(*sig).0[0] = 2;
113-
} else {
114-
(*sig).0[0] = 3;
77+
use super::*;
78+
use std::slice;
79+
80+
use secp256k1_ec_pubkey_create;
81+
use secp256k1_ec_pubkey_parse;
82+
use secp256k1_ec_pubkey_serialize;
83+
use SECP256K1_SER_COMPRESSED;
84+
85+
/// Sets sig to msg32||full pk
86+
pub unsafe fn secp256k1_ecdsa_sign_recoverable(
87+
cx: *const Context,
88+
sig: *mut RecoverableSignature,
89+
msg32: *const c_uchar,
90+
sk: *const c_uchar,
91+
_noncefn: NonceFn,
92+
_noncedata: *const c_void,
93+
) -> c_int {
94+
// Check context is built for signing (and compute pk)
95+
let mut new_pk = PublicKey::new();
96+
if secp256k1_ec_pubkey_create(cx, &mut new_pk, sk) != 1 {
97+
return 0;
11598
}
116-
ptr::copy(msg32, (*sig).0[1..33].as_mut_ptr(), 32);
117-
ptr::copy(sk, (*sig).0[33..65].as_mut_ptr(), 32);
99+
// Sign
100+
let sig_sl = slice::from_raw_parts_mut(sig as *mut u8, 65);
101+
let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32);
102+
sig_sl[..32].copy_from_slice(msg_sl);
103+
let mut out_len: size_t = 33;
104+
secp256k1_ec_pubkey_serialize(cx, sig_sl[32..].as_mut_ptr(), &mut out_len, &new_pk, SECP256K1_SER_COMPRESSED);
105+
// Encode the parity of the pubkey in the final byte as 0/1,
106+
// which is the same encoding (though the parity is computed
107+
// differently) as real recoverable signatures.
108+
sig_sl.swap(32, 64);
109+
sig_sl[64] -= 2;
118110
1
119111
}
120112

121-
pub unsafe fn secp256k1_ecdsa_recover(_cx: *const Context,
122-
_pk: *mut PublicKey,
123-
_sig: *const RecoverableSignature,
124-
_msg32: *const c_uchar)
125-
-> c_int {
126-
unimplemented!();
113+
pub unsafe fn secp256k1_ecdsa_recover(
114+
cx: *const Context,
115+
pk: *mut PublicKey,
116+
sig: *const RecoverableSignature,
117+
msg32: *const c_uchar
118+
) -> c_int {
119+
let sig_sl = slice::from_raw_parts(sig as *const u8, 65);
120+
let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32);
121+
println!("HMM0");
122+
123+
if sig_sl[64] > 4 {
124+
return 0;
125+
}
126+
// Pull the original pk out of the siganture
127+
let mut pk_ser = [0; 33];
128+
pk_ser.copy_from_slice(&sig_sl[32..]);
129+
pk_ser.swap(0, 32);
130+
pk_ser[0] += 2;
131+
// Check that it parses (in a real sig, this would be the R value,
132+
// so it is actually required to be a valid point)
133+
if secp256k1_ec_pubkey_parse(cx, pk, pk_ser.as_ptr(), 33) == 0 {
134+
return 0;
135+
}
136+
// Munge it up so that a different message will give a different pk
137+
for i in 0..32 {
138+
pk_ser[i + 1] ^= sig_sl[i] ^ msg_sl[i];
139+
}
140+
// If any munging happened, this will fail parsing half the time, so
141+
// tweak-and-loop until we find a key that works.
142+
let mut idx = 0;
143+
while secp256k1_ec_pubkey_parse(cx, pk, pk_ser.as_ptr(), 33) == 0 {
144+
pk_ser[1 + idx / 8] ^= 1 << (idx % 8);
145+
idx += 1;
146+
}
147+
1
127148
}
128149
}
129150
#[cfg(rust_secp_fuzz)]

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
//! 0xc9, 0x42, 0x8f, 0xca, 0x69, 0xc1, 0x32, 0xa2,
104104
//! ]).expect("compact signatures are 64 bytes; DER signatures are 68-72 bytes");
105105
//!
106+
//! # #[cfg(not(rust_secp_fuzz))]
106107
//! assert!(secp.verify(&message, &sig, &public_key).is_ok());
107108
//! ```
108109
//!
@@ -1221,6 +1222,7 @@ mod tests {
12211222
}
12221223

12231224
#[cfg(feature = "serde")]
1225+
#[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs
12241226
#[test]
12251227
fn test_signature_serde() {
12261228
use serde_test::{Configure, Token, assert_tokens};

src/recovery.rs

+1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ mod tests {
235235
}
236236

237237
#[test]
238+
#[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs
238239
fn sign() {
239240
let mut s = Secp256k1::new();
240241
s.randomize(&mut thread_rng());

src/schnorrsig.rs

+1
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ mod tests {
722722
}
723723

724724
#[cfg(feature = "serde")]
725+
#[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs
725726
#[test]
726727
fn test_signature_serde() {
727728
use serde_test::{assert_tokens, Configure, Token};

0 commit comments

Comments
 (0)