diff --git a/Cargo.toml b/Cargo.toml index 61e7f7d..45738ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ path = "src/lib.rs" [features] unstable = [] default = [] +fuzztarget = [] [dev-dependencies] serde = "1.0" diff --git a/src/ffi.rs b/src/ffi.rs index 6fc7af4..00c77af 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -112,6 +112,7 @@ impl SharedSecret { pub unsafe fn blank() -> SharedSecret { mem::uninitialized() } } +#[cfg(not(feature = "fuzztarget"))] extern "C" { pub static secp256k1_nonce_function_rfc6979: NonceFn; @@ -141,8 +142,8 @@ extern "C" { -> c_int; pub fn secp256k1_ec_pubkey_serialize(cx: *const Context, output: *mut c_uchar, - out_len: *mut size_t, pk: *const PublicKey -, compressed: c_uint) + out_len: *mut size_t, pk: *const PublicKey, + compressed: c_uint) -> c_int; // Signatures @@ -275,3 +276,377 @@ extern "C" { -> c_int; } +#[cfg(feature = "fuzztarget")] +mod fuzz_dummy { + use libc::{c_int, c_uchar, c_uint, c_void}; + use ffi::*; + use std::ptr; + + extern "C" { + pub static secp256k1_nonce_function_rfc6979: NonceFn; + } + + // Contexts + /// Creates a dummy context, tracking flags to ensure proper calling semantics + pub unsafe fn secp256k1_context_create(flags: c_uint) -> *mut Context { + let b = Box::new(Context(flags as i32)); + Box::into_raw(b) + } + + /// Copies a dummy context + pub unsafe fn secp256k1_context_clone(cx: *mut Context) -> *mut Context { + let b = Box::new(Context((*cx).0)); + Box::into_raw(b) + } + + /// Frees a dummy context + pub unsafe fn secp256k1_context_destroy(cx: *mut Context) { + Box::from_raw(cx); + } + + /// 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 + } + + // TODO secp256k1_context_set_illegal_callback + // TODO secp256k1_context_set_error_callback + // (Actually, I don't really want these exposed; if either of these + // are ever triggered it indicates a bug in rust-secp256k1, since + // one goal is to use Rust's type system to eliminate all possible + // bad inputs.) + + // 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 != 2 && *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!(); + } + + 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 { + unimplemented!(); + } + + /// 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_recoverable_signature_parse_compact(cx: *const Context, sig: *mut RecoverableSignature, + input64: *const c_uchar, recid: c_int) + -> c_int { + unimplemented!(); + } + + pub unsafe fn secp256k1_ecdsa_recoverable_signature_serialize_compact(cx: *const Context, output64: *const 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!(); + } + + pub unsafe fn secp256k1_ecdsa_signature_normalize(cx: *const Context, out_sig: *mut Signature, + in_sig: *const Signature) + -> c_int { + unimplemented!(); + } + + // ECDSA + /// Verifies that sig is msg32||pk[0..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 { + 1 + } + } + + /// Sets sig to msg32||sk + pub unsafe 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 { + 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 + } + + /// 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).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; } + if *sk.offset(0) > 0x7f { + (*sig).0[0] = 2; + } else { + (*sig).0[0] = 3; + } + ptr::copy(msg32, (*sig).0[1..33].as_mut_ptr(), 32); + ptr::copy(sk, (*sig).0[33..65].as_mut_ptr(), 32); + 1 + } + + pub unsafe fn secp256k1_ecdsa_recover(cx: *const Context, + pk: *mut PublicKey, + sig: *const RecoverableSignature, + msg32: *const c_uchar) + -> c_int { + unimplemented!(); + } + + // Schnorr + pub unsafe fn secp256k1_schnorr_sign(cx: *const Context, + sig64: *mut c_uchar, + msg32: *const c_uchar, + sk: *const c_uchar, + _noncefn: NonceFn, + _noncedata: *const c_void) + -> c_int { + unimplemented!(); + } + + pub unsafe fn secp256k1_schnorr_verify(cx: *const Context, + sig64: *const c_uchar, + msg32: *const c_uchar, + pk: *const PublicKey) + -> c_int { + unimplemented!(); + } + + pub unsafe fn secp256k1_schnorr_recover(cx: *const Context, + pk: *mut PublicKey, + sig64: *const c_uchar, + msg32: *const c_uchar) + -> c_int { + unimplemented!(); + } + + // 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 + } + } + + /// 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); + 1 + } + +//TODO secp256k1_ec_privkey_export +//TODO secp256k1_ec_privkey_import + + /// 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 { + 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, 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, (*pk).0[16..32].as_mut_ptr(), 16); + ptr::copy(tweak, (*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 { + 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] = 0x0d; // pk should always be valid + (*out).0[24+32] = 0x0d; // 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 SharedSecret, + point: *const PublicKey, + scalar: *const c_uchar) + -> 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, scalar) != 1 { return 0; } + (*out).0[0..16].copy_from_slice(&(*point).0[0..16]); + ptr::copy(scalar, (*out).0[16..32].as_mut_ptr(), 16); + (*out).0[16] = 0x00; // result should always be a valid secret key + 1 + } +} +#[cfg(feature = "fuzztarget")] +pub use self::fuzz_dummy::*;