diff --git a/contrib/test.sh b/contrib/test.sh index 7befc21..dd8430e 100755 --- a/contrib/test.sh +++ b/contrib/test.sh @@ -33,12 +33,17 @@ if [ "$DO_FEATURE_MATRIX" = true ]; then cargo test --all --features="$feature" done - # Other combos - RUSTFLAGS='--cfg=rust_secp_fuzz' cargo test --no-run --all - RUSTFLAGS='--cfg=rust_secp_fuzz' cargo test --no-run --all --features="recovery" + # Other combos + RUSTFLAGS='--cfg=rust_secp_fuzz' RUSTDOCFLAGS=$RUSTFLAGS cargo test --all + RUSTFLAGS='--cfg=rust_secp_fuzz' RUSTDOCFLAGS=$RUSTFLAGS cargo test --all --features="$FEATURES" cargo test --all --features="rand rand-std" cargo test --all --features="rand serde" + if [ "$DO_BENCH" = true ]; then # proxy for us having a nightly compiler + cargo test --all --all-features + RUSTFLAGS='--cfg=rust_secp_fuzz' RUSTDOCFLAGS='--cfg=rust_secp_fuzz' cargo test --all --all-features + fi + # Examples cargo run --example sign_verify cargo run --example sign_verify_recovery --features=recovery diff --git a/secp256k1-sys/src/lib.rs b/secp256k1-sys/src/lib.rs index 3828435..72fe4ec 100644 --- a/secp256k1-sys/src/lib.rs +++ b/secp256k1-sys/src/lib.rs @@ -58,36 +58,37 @@ pub const SECP256K1_SER_COMPRESSED: c_uint = (1 << 1) | (1 << 8); /// around the FFI functions to use it. And it's an unsafe type. /// Nonces are generated deterministically by RFC6979 by /// default; there should be no need to ever change this. -pub type NonceFn = unsafe extern "C" fn(nonce32: *mut c_uchar, - msg32: *const c_uchar, - key32: *const c_uchar, - algo16: *const c_uchar, - data: *mut c_void, - attempt: c_uint, -) -> c_int; +pub type NonceFn = Option c_int>; /// Hash function to use to post-process an ECDH point to get /// a shared secret. -pub type EcdhHashFn = unsafe extern "C" fn( +pub type EcdhHashFn = Option c_int; +) -> c_int>; /// Same as secp256k1_nonce function with the exception of accepting an /// additional pubkey argument and not requiring an attempt argument. The pubkey /// argument can protect signature schemes with key-prefixed challenge hash /// inputs against reusing the nonce when signing with the wrong precomputed /// pubkey. -pub type SchnorrNonceFn = unsafe extern "C" fn( +pub type SchnorrNonceFn = Option c_int; +) -> c_int>; /// A Secp256k1 context, containing various precomputed values and such /// needed to do elliptic curve computations. If you create one of these @@ -96,13 +97,6 @@ pub type SchnorrNonceFn = unsafe extern "C" fn( #[derive(Clone, Debug)] #[repr(C)] pub struct Context(c_int); -#[cfg(rust_secp_fuzz)] -impl Context { - pub fn flags(&self) -> u32 { - self.0 as u32 - } -} - /// Library-internal representation of a Secp256k1 public key #[repr(C)] pub struct PublicKey([c_uchar; 64]); @@ -263,7 +257,6 @@ impl hash::Hash for KeyPair { } } -#[cfg(not(rust_secp_fuzz))] extern "C" { /// Default ECDH hash function #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdh_hash_function_default")] @@ -275,6 +268,9 @@ extern "C" { #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_nonce_function_default")] pub static secp256k1_nonce_function_default: NonceFn; + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_nonce_function_bip340")] + pub static secp256k1_nonce_function_bip340: SchnorrNonceFn; + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_context_no_precomp")] pub static secp256k1_context_no_precomp: *const Context; @@ -342,35 +338,10 @@ extern "C" { in_sig: *const Signature) -> c_int; - // ECDSA - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdsa_verify")] - pub fn secp256k1_ecdsa_verify(cx: *const Context, - sig: *const Signature, - msg32: *const c_uchar, - pk: *const PublicKey) - -> c_int; - - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdsa_sign")] - pub fn secp256k1_ecdsa_sign(cx: *const Context, - sig: *mut Signature, - msg32: *const c_uchar, - sk: *const c_uchar, - noncefn: NonceFn, - noncedata: *const c_void) - -> c_int; - - // EC #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_seckey_verify")] pub fn secp256k1_ec_seckey_verify(cx: *const Context, sk: *const c_uchar) -> c_int; - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_pubkey_create")] - pub fn secp256k1_ec_pubkey_create(cx: *const Context, pk: *mut PublicKey, - sk: *const c_uchar) -> c_int; - -//TODO secp256k1_ec_privkey_export -//TODO secp256k1_ec_privkey_import - #[deprecated(since = "0.2.0",note = "Please use the secp256k1_ec_seckey_tweak_add function instead")] #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_privkey_negate")] pub fn secp256k1_ec_privkey_negate(cx: *const Context, @@ -380,10 +351,6 @@ extern "C" { pub fn secp256k1_ec_seckey_negate(cx: *const Context, sk: *mut c_uchar) -> c_int; - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_pubkey_negate")] - pub fn secp256k1_ec_pubkey_negate(cx: *const Context, - pk: *mut PublicKey) -> c_int; - #[deprecated(since = "0.2.0",note = "Please use the secp256k1_ec_seckey_tweak_add function instead")] #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_privkey_tweak_add")] pub fn secp256k1_ec_privkey_tweak_add(cx: *const Context, @@ -397,12 +364,6 @@ extern "C" { tweak: *const c_uchar) -> c_int; - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_pubkey_tweak_add")] - pub fn secp256k1_ec_pubkey_tweak_add(cx: *const Context, - pk: *mut PublicKey, - tweak: *const c_uchar) - -> c_int; - #[deprecated(since = "0.2.0",note = "Please use the secp256k1_ec_seckey_tweak_mul function instead")] #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_privkey_tweak_mul")] pub fn secp256k1_ec_privkey_tweak_mul(cx: *const Context, @@ -416,6 +377,23 @@ extern "C" { tweak: *const c_uchar) -> c_int; + // EC + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_pubkey_create")] + pub fn secp256k1_ec_pubkey_create(cx: *const Context, pk: *mut PublicKey, + sk: *const c_uchar) -> c_int; + + + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_pubkey_negate")] + pub fn secp256k1_ec_pubkey_negate(cx: *const Context, + pk: *mut PublicKey) -> c_int; + + + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_pubkey_tweak_add")] + pub fn secp256k1_ec_pubkey_tweak_add(cx: *const Context, + pk: *mut PublicKey, + tweak: *const c_uchar) + -> c_int; + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ec_pubkey_tweak_mul")] pub fn secp256k1_ec_pubkey_tweak_mul(cx: *const Context, pk: *mut PublicKey, @@ -439,31 +417,7 @@ extern "C" { data: *mut c_void, ) -> c_int; - - // Schnorr Signatures - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_nonce_function_bip340")] - pub static secp256k1_nonce_function_bip340: SchnorrNonceFn; - - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_schnorrsig_sign")] - pub fn secp256k1_schnorrsig_sign( - cx: *const Context, - sig: *mut c_uchar, - msg32: *const c_uchar, - keypair: *const KeyPair, - noncefp: SchnorrNonceFn, - noncedata: *const c_void - ) -> c_int; - - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_schnorrsig_verify")] - pub fn secp256k1_schnorrsig_verify( - cx: *const Context, - sig64: *const c_uchar, - msg32: *const c_uchar, - pubkey: *const XOnlyPublicKey, - ) -> c_int; - // Extra keys - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_keypair_create")] pub fn secp256k1_keypair_create( cx: *const Context, @@ -526,6 +480,45 @@ extern "C" { ) -> c_int; } +#[cfg(not(rust_secp_fuzz))] +extern "C" { + // ECDSA + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdsa_verify")] + pub fn secp256k1_ecdsa_verify(cx: *const Context, + sig: *const Signature, + msg32: *const c_uchar, + pk: *const PublicKey) + -> c_int; + + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdsa_sign")] + pub fn secp256k1_ecdsa_sign(cx: *const Context, + sig: *mut Signature, + msg32: *const c_uchar, + sk: *const c_uchar, + noncefn: NonceFn, + noncedata: *const c_void) + -> c_int; + + // Schnorr Signatures + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_schnorrsig_sign")] + pub fn secp256k1_schnorrsig_sign( + cx: *const Context, + sig: *mut c_uchar, + msg32: *const c_uchar, + keypair: *const KeyPair, + noncefp: SchnorrNonceFn, + noncedata: *const c_void + ) -> c_int; + + #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_schnorrsig_verify")] + pub fn secp256k1_schnorrsig_verify( + cx: *const Context, + sig64: *const c_uchar, + msg32: *const c_uchar, + pubkey: *const XOnlyPublicKey, + ) -> c_int; +} + /// A reimplementation of the C function `secp256k1_context_create` in rust. /// @@ -674,221 +667,31 @@ impl CPtr for [T] { } } - - - #[cfg(rust_secp_fuzz)] mod fuzz_dummy { - extern crate std; - use self::std::{ptr, mem}; - use self::std::boxed::Box; - use types::*; - use {Signature, Context, NonceFn, EcdhHashFn, PublicKey, - SchnorrNonceFn, XOnlyPublicKey, KeyPair, - SECP256K1_START_NONE, SECP256K1_START_VERIFY, SECP256K1_START_SIGN, - SECP256K1_SER_COMPRESSED, SECP256K1_SER_UNCOMPRESSED}; - - #[allow(non_upper_case_globals)] - pub static secp256k1_context_no_precomp: &Context = &Context(0); - - extern "C" { - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdh_hash_function_default")] - pub static secp256k1_ecdh_hash_function_default: EcdhHashFn; - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_nonce_function_rfc6979")] - pub static secp256k1_nonce_function_rfc6979: NonceFn; - #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_nonce_function_bip340")] - pub static secp256k1_nonce_function_bip340: SchnorrNonceFn; - } - - // Contexts - /// Creates a dummy context, tracking flags to ensure proper calling semantics - pub unsafe fn secp256k1_context_preallocated_create(_ptr: *mut c_void, flags: c_uint) -> *mut Context { - let b = Box::new(Context(flags as i32)); - Box::into_raw(b) - } - - /// Return dummy size of context struct. - pub unsafe fn secp256k1_context_preallocated_size(_flags: c_uint) -> size_t { - mem::size_of::() - } - - /// Return dummy size of context struct. - pub unsafe fn secp256k1_context_preallocated_clone_size(_cx: *mut Context) -> size_t { - mem::size_of::() - } - - /// Copies a dummy context - pub unsafe fn secp256k1_context_preallocated_clone(cx: *const Context, prealloc: *mut c_void) -> *mut Context { - let ret = prealloc as *mut Context; - *ret = (*cx).clone(); - ret - } - - /// "Destroys" a dummy context - pub unsafe fn secp256k1_context_preallocated_destroy(cx: *mut Context) { - (*cx).0 = 0; - } - - /// Asserts that cx is properly initialized - pub unsafe fn secp256k1_context_randomize(cx: *mut Context, - _seed32: *const c_uchar) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - 1 - } - - // Pubkeys - /// Parse 33/65 byte pubkey into PublicKey, losing compressed information - pub unsafe fn secp256k1_ec_pubkey_parse(cx: *const Context, pk: *mut PublicKey, - input: *const c_uchar, in_len: size_t) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - match in_len { - 33 => { - if (*input.offset(1) > 0x7f && *input != 2) || (*input.offset(1) <= 0x7f && *input != 3) { - 0 - } else { - ptr::copy(input.offset(1), (*pk).0[0..32].as_mut_ptr(), 32); - ptr::copy(input.offset(1), (*pk).0[32..64].as_mut_ptr(), 32); - test_pk_validate(cx, pk) - } - }, - 65 => { - if *input != 4 && *input != 6 && *input != 7 { - 0 - } else { - ptr::copy(input.offset(1), (*pk).0.as_mut_ptr(), 64); - test_pk_validate(cx, pk) - } - }, - _ => 0 - } - } - - /// Serialize PublicKey back to 33/65 byte pubkey - pub unsafe fn secp256k1_ec_pubkey_serialize(cx: *const Context, output: *mut c_uchar, - out_len: *mut size_t, pk: *const PublicKey, - compressed: c_uint) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if test_pk_validate(cx, pk) != 1 { return 0; } - if compressed == SECP256K1_SER_COMPRESSED { - assert_eq!(*out_len, 33); - if (*pk).0[0] > 0x7f { - *output = 2; - } else { - *output = 3; - } - ptr::copy((*pk).0.as_ptr(), output.offset(1), 32); - } else if compressed == SECP256K1_SER_UNCOMPRESSED { - assert_eq!(*out_len, 65); - *output = 4; - ptr::copy((*pk).0.as_ptr(), output.offset(1), 64); - } else { - panic!("Bad flags"); - } - 1 - } - - // Signatures - pub unsafe fn secp256k1_ecdsa_signature_parse_der(_cx: *const Context, _sig: *mut Signature, - _input: *const c_uchar, _in_len: size_t) - -> c_int { - unimplemented!(); - } - - /// Copies input64 to sig, checking the pubkey part is valid - pub unsafe fn secp256k1_ecdsa_signature_parse_compact(cx: *const Context, sig: *mut Signature, - input64: *const c_uchar) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if secp256k1_ec_seckey_verify(cx, input64.offset(32)) != 1 { return 0; } // sig should be msg32||sk - ptr::copy(input64, (*sig).0[..].as_mut_ptr(), 64); - 1 - } - - pub unsafe fn ecdsa_signature_parse_der_lax(_cx: *const Context, _sig: *mut Signature, - _input: *const c_uchar, _in_len: size_t) - -> c_int { - unimplemented!(); - } - - /// Copies up to 72 bytes into output from sig - pub unsafe fn secp256k1_ecdsa_signature_serialize_der(cx: *const Context, output: *mut c_uchar, - out_len: *mut size_t, sig: *const Signature) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - - let mut len_r = 33; - if *(*sig).0.as_ptr().offset(0) < 0x80 { - len_r -= 1; - } - let mut len_s = 33; - if *(*sig).0.as_ptr().offset(32) < 0x80 { - len_s -= 1; - } - - assert!(*out_len >= (6 + len_s + len_r) as usize); - - *output.offset(0) = 0x30; - *output.offset(1) = 4 + len_r + len_s; - *output.offset(2) = 0x02; - *output.offset(3) = len_r; - if len_r == 33 { - *output.offset(4) = 0; - ptr::copy((*sig).0[..].as_ptr(), output.offset(5), 32); - } else { - ptr::copy((*sig).0[..].as_ptr(), output.offset(4), 32); - } - *output.offset(4 + len_r as isize) = 0x02; - *output.offset(5 + len_r as isize) = len_s; - if len_s == 33 { - *output.offset(6 + len_r as isize) = 0; - ptr::copy((*sig).0[..].as_ptr().offset(32), output.offset(7 + len_r as isize), 32); - } else { - ptr::copy((*sig).0[..].as_ptr().offset(32), output.offset(6 + len_r as isize), 32); - } - 1 - } - - /// Copies sig to output64 - pub unsafe fn secp256k1_ecdsa_signature_serialize_compact(cx: *const Context, output64: *mut c_uchar, - sig: *const Signature) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - ptr::copy((*sig).0[..].as_ptr(), output64, 64); - 1 - } - - pub unsafe fn secp256k1_ecdsa_signature_normalize(_cx: *const Context, _out_sig: *mut Signature, - _in_sig: *const Signature) - -> c_int { - unimplemented!(); - } + use super::*; // ECDSA - /// Verifies that sig is msg32||pk[0..32] + /// Verifies that sig is msg32||pk[..32] pub unsafe fn secp256k1_ecdsa_verify(cx: *const Context, sig: *const Signature, msg32: *const c_uchar, pk: *const PublicKey) -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - assert!((*cx).0 as u32 & SECP256K1_START_VERIFY == SECP256K1_START_VERIFY); - if test_pk_validate(cx, pk) != 1 { return 0; } - for i in 0..32 { - if (*sig).0[i] != *msg32.offset(i as isize) { - return 0; - } - } - if (*sig).0[32..64] != (*pk).0[0..32] { - 0 - } else { + // Check context is built for verification + let mut new_pk = (*pk).clone(); + let _ = secp256k1_ec_pubkey_tweak_add(cx, &mut new_pk, msg32); + // Actually verify + let sig_sl = slice::from_raw_parts(sig as *const u8, 64); + let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32); + if &sig_sl[..32] == msg_sl && sig_sl[32..] == (*pk).0[0..32] { 1 + } else { + 0 } } - /// Sets sig to msg32||sk + /// Sets sig to msg32||pk[..32] pub unsafe fn secp256k1_ecdsa_sign(cx: *const Context, sig: *mut Signature, msg32: *const c_uchar, @@ -896,274 +699,66 @@ mod fuzz_dummy { _noncefn: NonceFn, _noncedata: *const c_void) -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - assert!((*cx).0 as u32 & SECP256K1_START_SIGN == SECP256K1_START_SIGN); - if secp256k1_ec_seckey_verify(cx, sk) != 1 { return 0; } - ptr::copy(msg32, (*sig).0[0..32].as_mut_ptr(), 32); - ptr::copy(sk, (*sig).0[32..64].as_mut_ptr(), 32); - 1 - } - - // EC - /// Checks that pk != 0xffff...ffff and pk[0..32] == pk[32..64] - pub unsafe fn test_pk_validate(cx: *const Context, - pk: *const PublicKey) -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if (*pk).0[0..32] != (*pk).0[32..64] || secp256k1_ec_seckey_verify(cx, (*pk).0[0..32].as_ptr()) == 0 { - 0 - } else { - 1 + // Check context is built for signing (and compute pk) + let mut new_pk = PublicKey::new(); + if secp256k1_ec_pubkey_create(cx, &mut new_pk, sk) != 1 { + return 0; } - } - - /// Checks that sk != 0xffff...ffff - pub unsafe fn secp256k1_ec_seckey_verify(cx: *const Context, - sk: *const c_uchar) -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - let mut res = 0; - for i in 0..32 { - if *sk.offset(i as isize) != 0xff { res = 1 }; - } - res - } - - /// Sets pk to sk||sk - pub unsafe fn secp256k1_ec_pubkey_create(cx: *const Context, pk: *mut PublicKey, - sk: *const c_uchar) -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if secp256k1_ec_seckey_verify(cx, sk) != 1 { return 0; } - ptr::copy(sk, (*pk).0[0..32].as_mut_ptr(), 32); - ptr::copy(sk, (*pk).0[32..64].as_mut_ptr(), 32); + // Sign + let sig_sl = slice::from_raw_parts_mut(sig as *mut u8, 64); + let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32); + sig_sl[..32].copy_from_slice(msg_sl); + sig_sl[32..].copy_from_slice(&new_pk.0[..32]); 1 } -//TODO secp256k1_ec_privkey_export -//TODO secp256k1_ec_privkey_import - - #[deprecated(since = "0.2.0",note = "Please use the secp256k1_ec_seckey_negate function instead")] - pub unsafe fn secp256k1_ec_privkey_negate(cx: *const Context, - sk: *mut c_uchar) -> c_int { - secp256k1_ec_seckey_negate(cx, sk) - } - - pub unsafe fn secp256k1_ec_seckey_negate(cx: *const Context, - sk: *mut c_uchar) -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if secp256k1_ec_seckey_verify(cx, sk) != 1 { return 0; } - 1 - } - - pub unsafe fn secp256k1_ec_pubkey_negate(cx: *const Context, - pk: *mut PublicKey) -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if test_pk_validate(cx, pk) != 1 { return 0; } - 1 - } - - /// Copies the first 16 bytes of tweak into the last 16 bytes of sk - pub unsafe fn secp256k1_ec_privkey_tweak_add(cx: *const Context, - sk: *mut c_uchar, - tweak: *const c_uchar) - -> c_int { - secp256k1_ec_seckey_tweak_add(cx, sk, tweak) - } - - pub unsafe fn secp256k1_ec_seckey_tweak_add(cx: *const Context, - sk: *mut c_uchar, - tweak: *const c_uchar) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if secp256k1_ec_seckey_verify(cx, sk) != 1 { return 0; } - ptr::copy(tweak.offset(16), sk.offset(16), 16); - *sk.offset(24) = 0x7f; // Ensure sk remains valid no matter what tweak was - 1 - } - - /// The PublicKey equivalent of secp256k1_ec_privkey_tweak_add - pub unsafe fn secp256k1_ec_pubkey_tweak_add(cx: *const Context, - pk: *mut PublicKey, - tweak: *const c_uchar) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if test_pk_validate(cx, pk) != 1 { return 0; } - ptr::copy(tweak.offset(16), (*pk).0[16..32].as_mut_ptr(), 16); - ptr::copy(tweak.offset(16), (*pk).0[16+32..64].as_mut_ptr(), 16); - (*pk).0[24] = 0x7f; // Ensure pk remains valid no matter what tweak was - (*pk).0[24+32] = 0x7f; // Ensure pk remains valid no matter what tweak was - 1 - } - - /// Copies the last 16 bytes of tweak into the last 16 bytes of sk - pub unsafe fn secp256k1_ec_privkey_tweak_mul(cx: *const Context, - sk: *mut c_uchar, - tweak: *const c_uchar) - -> c_int { - secp256k1_ec_seckey_tweak_mul(cx, sk, tweak) - } - - /// Copies the last 16 bytes of tweak into the last 16 bytes of sk - pub unsafe fn secp256k1_ec_seckey_tweak_mul(cx: *const Context, - sk: *mut c_uchar, - tweak: *const c_uchar) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if secp256k1_ec_seckey_verify(cx, sk) != 1 { return 0; } - ptr::copy(tweak.offset(16), sk.offset(16), 16); - *sk.offset(24) = 0x00; // Ensure sk remains valid no matter what tweak was - 1 - } - - /// The PublicKey equivalent of secp256k1_ec_privkey_tweak_mul - pub unsafe fn secp256k1_ec_pubkey_tweak_mul(cx: *const Context, - pk: *mut PublicKey, - tweak: *const c_uchar) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if test_pk_validate(cx, pk) != 1 { return 0; } - ptr::copy(tweak.offset(16), (*pk).0[16..32].as_mut_ptr(), 16); - ptr::copy(tweak.offset(16), (*pk).0[16+32..64].as_mut_ptr(), 16); - (*pk).0[24] = 0x00; // Ensure pk remains valid no matter what tweak was - (*pk).0[24+32] = 0x00; // Ensure pk remains valid no matter what tweak was - 1 - } - - pub unsafe fn secp256k1_ec_pubkey_combine(cx: *const Context, - out: *mut PublicKey, - ins: *const *const PublicKey, - n: c_int) - -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - assert!(n <= 32 && n >= 0); //TODO: Remove this restriction? - for i in 0..n { - if test_pk_validate(cx, *ins.offset(i as isize)) != 1 { return 0; } - (*out).0[(i*32/n) as usize..((i+1)*32/n) as usize].copy_from_slice(&(**ins.offset(i as isize)).0[(i*32/n) as usize..((i+1)*32/n) as usize]); - } - ptr::copy((*out).0[0..32].as_ptr(), (*out).0[32..64].as_mut_ptr(), 32); - (*out).0[24] = 0x7f; // pk should always be valid - (*out).0[24+32] = 0x7f; // pk should always be valid - test_pk_validate(cx, out) - } - - /// Sets out to point[0..16]||scalar[0..16] - pub unsafe fn secp256k1_ecdh( - cx: *const Context, - out: *mut c_uchar, - point: *const PublicKey, - scalar: *const c_uchar, - _hashfp: EcdhHashFn, - _data: *mut c_void, - ) -> c_int { - assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - if secp256k1_ec_seckey_verify(cx, scalar) != 1 { return 0; } - - let mut scalar_prefix = [0; 16]; - ptr::copy(scalar, scalar_prefix[..].as_mut_ptr(), 16); - - if (*point).0[0..16] > scalar_prefix[0..16] { - ptr::copy((*point).as_ptr(), out, 16); - ptr::copy(scalar, out.offset(16), 16); - } else { - ptr::copy(scalar, out, 16); - ptr::copy((*point).as_ptr(), out.offset(16), 16); - } - (*out.offset(16)) = 0x00; // result should always be a valid secret key - 1 - } - - pub unsafe fn secp256k1_schnorrsig_sign( - _cx: *const Context, - _sig: *mut c_uchar, - _msg32: *const c_uchar, - _keypair: *const KeyPair, - _noncefp: SchnorrNonceFn, - _noncedata: *const c_void - ) -> c_int { - unimplemented!(); - } - + /// Verifies that sig is msg32||pk[32..] pub unsafe fn secp256k1_schnorrsig_verify( - _cx: *const Context, - _sig64: *const c_uchar, - _msg32: *const c_uchar, - _pubkey: *const XOnlyPublicKey, + cx: *const Context, + sig64: *const c_uchar, + msg32: *const c_uchar, + pubkey: *const XOnlyPublicKey, ) -> c_int { - unimplemented!(); + // Check context is built for verification + let mut new_pk = PublicKey::new(); + let _ = secp256k1_xonly_pubkey_tweak_add(cx, &mut new_pk, pubkey, msg32); + // Actually verify + let sig_sl = slice::from_raw_parts(sig64 as *const u8, 64); + let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32); + if &sig_sl[..32] == msg_sl && sig_sl[32..] == (*pubkey).0[..32] { + 1 + } else { + 0 + } } - pub fn secp256k1_xonly_pubkey_parse( - _cx: *const Context, - _pubkey: *mut XOnlyPublicKey, - _input32: *const c_uchar, + /// Sets sig to msg32||pk[..32] + pub unsafe fn secp256k1_schnorrsig_sign( + cx: *const Context, + sig64: *mut c_uchar, + msg32: *const c_uchar, + keypair: *const KeyPair, + noncefp: SchnorrNonceFn, + noncedata: *const c_void ) -> c_int { - unimplemented!(); - } - - pub fn secp256k1_xonly_pubkey_serialize( - _cx: *const Context, - _output32: *mut c_uchar, - _pubkey: *const XOnlyPublicKey, - ) -> c_int { - unimplemented!(); - } - - pub unsafe fn secp256k1_xonly_pubkey_from_pubkey( - _cx: *const Context, - _xonly_pubkey: *mut XOnlyPublicKey, - _pk_parity: *mut c_int, - _pubkey: *const PublicKey, - ) -> c_int { - unimplemented!(); - } - - pub unsafe fn secp256k1_keypair_create( - _cx: *const Context, - _keypair: *mut KeyPair, - _seckey: *const c_uchar, - ) -> c_int { - unimplemented!(); - } - - pub unsafe fn secp256k1_xonly_pubkey_tweak_add( - _cx: *const Context, - _output_pubkey: *mut PublicKey, - _internal_pubkey: *const XOnlyPublicKey, - _tweak32: *const c_uchar, - ) -> c_int { - unimplemented!(); - } - - pub unsafe fn secp256k1_keypair_xonly_pub( - _cx: *const Context, - _pubkey: *mut XOnlyPublicKey, - _pk_parity: *mut c_int, - _keypair: *const KeyPair - ) -> c_int { - unimplemented!(); - } - - pub unsafe fn secp256k1_keypair_xonly_tweak_add( - _cx: *const Context, - _keypair: *mut KeyPair, - _tweak32: *const c_uchar, - ) -> c_int { - unimplemented!(); - } - - pub unsafe fn secp256k1_xonly_pubkey_tweak_add_check( - _cx: *const Context, - _tweaked_pubkey32: *const c_uchar, - _tweaked_pubkey_parity: c_int, - _internal_pubkey: *const XOnlyPublicKey, - _tweak32: *const c_uchar, - ) -> c_int { - unimplemented!(); + // Check context is built for signing + let mut new_kp = KeyPair::new(); + if secp256k1_keypair_create(cx, &mut new_kp, (*keypair).0.as_ptr()) != 1 { + return 0; + } + assert_eq!(new_kp, *keypair); + // Sign + let sig_sl = slice::from_raw_parts_mut(sig64 as *mut u8, 64); + let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32); + sig_sl[..32].copy_from_slice(msg_sl); + sig_sl[32..].copy_from_slice(&new_kp.0[32..64]); + 1 } } #[cfg(rust_secp_fuzz)] pub use self::fuzz_dummy::*; - #[cfg(test)] mod tests { #[no_mangle] diff --git a/secp256k1-sys/src/recovery.rs b/secp256k1-sys/src/recovery.rs index 9b9464b..dec4f73 100644 --- a/secp256k1-sys/src/recovery.rs +++ b/secp256k1-sys/src/recovery.rs @@ -16,8 +16,7 @@ //! # FFI of the recovery module use ::types::*; -#[cfg(not(rust_secp_fuzz))] -use ::{Context, Signature, NonceFn, PublicKey}; +use {Context, Signature, NonceFn, PublicKey}; /// Library-internal representation of a Secp256k1 signature + recovery ID #[repr(C)] @@ -36,7 +35,6 @@ impl Default for RecoverableSignature { } } -#[cfg(not(rust_secp_fuzz))] extern "C" { #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdsa_recoverable_signature_parse_compact")] pub fn secp256k1_ecdsa_recoverable_signature_parse_compact(cx: *const Context, sig: *mut RecoverableSignature, @@ -52,6 +50,10 @@ extern "C" { pub fn secp256k1_ecdsa_recoverable_signature_convert(cx: *const Context, sig: *mut Signature, input: *const RecoverableSignature) -> c_int; +} + +#[cfg(not(rust_secp_fuzz))] +extern "C" { #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_3_1_ecdsa_sign_recoverable")] pub fn secp256k1_ecdsa_sign_recoverable(cx: *const Context, sig: *mut RecoverableSignature, @@ -72,58 +74,76 @@ extern "C" { #[cfg(rust_secp_fuzz)] mod fuzz_dummy { - extern crate std; - use self::std::ptr; - use super::RecoverableSignature; - use types::*; - use ::{Signature, Context, PublicKey, NonceFn, secp256k1_ec_seckey_verify, - SECP256K1_START_NONE, SECP256K1_START_VERIFY, SECP256K1_START_SIGN}; + use super::*; + use std::slice; - pub unsafe fn secp256k1_ecdsa_recoverable_signature_parse_compact(_cx: *const Context, _sig: *mut RecoverableSignature, - _input64: *const c_uchar, _recid: c_int) - -> c_int { - unimplemented!(); - } + use secp256k1_ec_pubkey_create; + use secp256k1_ec_pubkey_parse; + use secp256k1_ec_pubkey_serialize; + use SECP256K1_SER_COMPRESSED; - pub unsafe fn secp256k1_ecdsa_recoverable_signature_serialize_compact(_cx: *const Context, _output64: *mut c_uchar, - _recid: *mut c_int, _sig: *const RecoverableSignature) - -> c_int { - unimplemented!(); - } - - pub unsafe fn secp256k1_ecdsa_recoverable_signature_convert(_cx: *const Context, _sig: *mut Signature, - _input: *const RecoverableSignature) - -> c_int { - unimplemented!(); - } - - /// Sets sig to (2|3)||msg32||sk - pub unsafe fn secp256k1_ecdsa_sign_recoverable(cx: *const Context, - sig: *mut RecoverableSignature, - msg32: *const c_uchar, - sk: *const c_uchar, - _noncefn: NonceFn, - _noncedata: *const c_void) - -> c_int { - assert!(!cx.is_null() && (*cx).flags() & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); - assert!((*cx).flags() & SECP256K1_START_SIGN == SECP256K1_START_SIGN); - if secp256k1_ec_seckey_verify(cx, sk) != 1 { return 0; } - if *sk.offset(0) > 0x7f { - (*sig).0[0] = 2; - } else { - (*sig).0[0] = 3; + /// Sets sig to msg32||full pk + pub unsafe fn secp256k1_ecdsa_sign_recoverable( + cx: *const Context, + sig: *mut RecoverableSignature, + msg32: *const c_uchar, + sk: *const c_uchar, + _noncefn: NonceFn, + _noncedata: *const c_void, + ) -> c_int { + // Check context is built for signing (and compute pk) + let mut new_pk = PublicKey::new(); + if secp256k1_ec_pubkey_create(cx, &mut new_pk, sk) != 1 { + return 0; } - ptr::copy(msg32, (*sig).0[1..33].as_mut_ptr(), 32); - ptr::copy(sk, (*sig).0[33..65].as_mut_ptr(), 32); + // Sign + let sig_sl = slice::from_raw_parts_mut(sig as *mut u8, 65); + let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32); + sig_sl[..32].copy_from_slice(msg_sl); + let mut out_len: size_t = 33; + secp256k1_ec_pubkey_serialize(cx, sig_sl[32..].as_mut_ptr(), &mut out_len, &new_pk, SECP256K1_SER_COMPRESSED); + // Encode the parity of the pubkey in the final byte as 0/1, + // which is the same encoding (though the parity is computed + // differently) as real recoverable signatures. + sig_sl.swap(32, 64); + sig_sl[64] -= 2; 1 } - pub unsafe fn secp256k1_ecdsa_recover(_cx: *const Context, - _pk: *mut PublicKey, - _sig: *const RecoverableSignature, - _msg32: *const c_uchar) - -> c_int { - unimplemented!(); + pub unsafe fn secp256k1_ecdsa_recover( + cx: *const Context, + pk: *mut PublicKey, + sig: *const RecoverableSignature, + msg32: *const c_uchar + ) -> c_int { + let sig_sl = slice::from_raw_parts(sig as *const u8, 65); + let msg_sl = slice::from_raw_parts(msg32 as *const u8, 32); + + if sig_sl[64] >= 4 { + return 0; + } + // Pull the original pk out of the siganture + let mut pk_ser = [0; 33]; + pk_ser.copy_from_slice(&sig_sl[32..]); + pk_ser.swap(0, 32); + pk_ser[0] += 2; + // Check that it parses (in a real sig, this would be the R value, + // so it is actually required to be a valid point) + if secp256k1_ec_pubkey_parse(cx, pk, pk_ser.as_ptr(), 33) == 0 { + return 0; + } + // Munge it up so that a different message will give a different pk + for i in 0..32 { + pk_ser[i + 1] ^= sig_sl[i] ^ msg_sl[i]; + } + // If any munging happened, this will fail parsing half the time, so + // tweak-and-loop until we find a key that works. + let mut idx = 0; + while secp256k1_ec_pubkey_parse(cx, pk, pk_ser.as_ptr(), 33) == 0 { + pk_ser[1 + idx / 8] ^= 1 << (idx % 8); + idx += 1; + } + 1 } } #[cfg(rust_secp_fuzz)] diff --git a/src/ecdh.rs b/src/ecdh.rs index e76b54f..51ec005 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -151,7 +151,7 @@ impl SharedSecret { xy.as_mut_ptr(), point.as_ptr(), scalar.as_ptr(), - c_callback, + Some(c_callback), ptr::null_mut(), ) }; diff --git a/src/lib.rs b/src/lib.rs index 9016d0b..61d12c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,6 +103,7 @@ //! 0xc9, 0x42, 0x8f, 0xca, 0x69, 0xc1, 0x32, 0xa2, //! ]).expect("compact signatures are 64 bytes; DER signatures are 68-72 bytes"); //! +//! # #[cfg(not(rust_secp_fuzz))] //! assert!(secp.verify(&message, &sig, &public_key).is_ok()); //! ``` //! @@ -735,6 +736,10 @@ impl Secp256k1 { } entropy_p = extra_entropy.as_ptr() as *const ffi::types::c_void; + + // When fuzzing, these checks will usually spinloop forever, so just short-circuit them. + #[cfg(rust_secp_fuzz)] + return Signature::from(ret); } } } @@ -1097,9 +1102,12 @@ mod tests { if compact[0] < 0x80 { assert_eq!(sig, low_r_sig); } else { + #[cfg(not(rust_secp_fuzz))] // mocked sig generation doesn't produce low-R sigs assert_ne!(sig, low_r_sig); } + #[cfg(not(rust_secp_fuzz))] // mocked sig generation doesn't produce low-R sigs assert!(super::compact_sig_has_zero_first_bit(&low_r_sig.0)); + #[cfg(not(rust_secp_fuzz))] // mocked sig generation doesn't produce low-R sigs assert!(super::der_length_check(&grind_r_sig.0, 70)); } } @@ -1172,6 +1180,7 @@ mod tests { } #[test] + #[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs fn test_low_s() { // nb this is a transaction on testnet // txid 8ccc87b72d766ab3128f03176bb1c98293f2d1f85ebfaf07b82cc81ea6891fa9 @@ -1193,6 +1202,7 @@ mod tests { } #[test] + #[cfg(not(rust_secp_fuzz))] // fuzz-sigs have fixed size/format fn test_low_r() { let secp = Secp256k1::new(); let msg = hex!("887d04bb1cf1b1554f1b268dfe62d13064ca67ae45348d50d1392ce2d13418ac"); @@ -1207,6 +1217,7 @@ mod tests { } #[test] + #[cfg(not(rust_secp_fuzz))] // fuzz-sigs have fixed size/format fn test_grind_r() { let secp = Secp256k1::new(); let msg = hex!("ef2d5b9a7c61865a95941d0f04285420560df7e9d76890ac1b8867b12ce43167"); @@ -1220,6 +1231,7 @@ mod tests { } #[cfg(feature = "serde")] + #[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs #[test] fn test_signature_serde() { use serde_test::{Configure, Token, assert_tokens}; diff --git a/src/recovery.rs b/src/recovery.rs index 5eb6f57..e598ca8 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -235,6 +235,7 @@ mod tests { } #[test] + #[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs fn sign() { let mut s = Secp256k1::new(); s.randomize(&mut thread_rng()); diff --git a/src/schnorrsig.rs b/src/schnorrsig.rs index c55fca1..12ab2f2 100644 --- a/src/schnorrsig.rs +++ b/src/schnorrsig.rs @@ -561,6 +561,7 @@ mod tests { } #[test] + #[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs fn test_schnorrsig_sign() { let secp = Secp256k1::new(); @@ -582,6 +583,7 @@ mod tests { } #[test] + #[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs fn test_schnorrsig_verify() { let secp = Secp256k1::new(); @@ -720,6 +722,7 @@ mod tests { } #[cfg(feature = "serde")] + #[cfg(not(rust_secp_fuzz))] // fixed sig vectors can't work with fuzz-sigs #[test] fn test_signature_serde() { use serde_test::{assert_tokens, Configure, Token};