Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzz: implement Arbitrary trait for fuzzed types #74

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ bullet-proof-sizing = []
dev = ["clippy"]

[dependencies]
arbitrary = { version = "0.4.7", optional = true, features = ["derive"] }
arrayvec = "0.3"
clippy = {version = "0.0", optional = true}
rand = "0.5"
Expand Down
1 change: 1 addition & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ libfuzzer-sys = "0.3"

[dependencies.grin_secp256k1zkp]
path = ".."
features = ["arbitrary"]

# Prevent this from interfering with workspaces
[workspace]
Expand Down
46 changes: 21 additions & 25 deletions fuzz/fuzz_targets/fuzz_aggsig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,54 +13,50 @@ use secp256k1zkp::{
};

use secp256k1zkp::aggsig::AggSigContext;
use secp256k1zkp::rand::{Rng, thread_rng};

fuzz_target!(|data: &[u8]| {
let numkeys = 3;
if data.len() < (numkeys + 1) * 32 {
return ();
}
fuzz_target!(|keys_msg: (Vec<(SecretKey, PublicKey)>, Message)| {
let (keys, msg) = keys_msg;
let numkeys = keys.len();

if numkeys == 0 { return }

let mut rng = thread_rng();
let secp = Secp256k1::with_caps(ContextFlag::Full);

// public keys for valid verification
let mut pks: Vec<PublicKey> = Vec::with_capacity(numkeys);
let mut keypairs: Vec<(SecretKey, PublicKey)> = Vec::with_capacity(numkeys);

for i in 0..numkeys {
if let Ok(sk) = SecretKey::from_slice(&secp, &data[i*32..(i+1)*32]) {
let pk = PublicKey::from_secret_key(&secp, &sk).unwrap();
pks.push(pk.clone());
keypairs.push((sk, pk));
} else {
let (sk, pk) = secp.generate_keypair(&mut rng).unwrap();
pks.push(pk.clone());
keypairs.push((sk, pk));
}
for (sk, _) in keys.iter() {
let pk = PublicKey::from_secret_key(&secp, &sk).unwrap();
pks.push(pk.clone());
}

let aggsig = AggSigContext::new(&secp, &pks);

// generate signature nonces
for i in 0..numkeys {
if aggsig.generate_nonce(i) != true {
panic!("failed to generate aggsig nonce: {}", i);
}
}

let mut msg_in = [0u8; 32];
rng.fill(&mut msg_in);
let msg = Message::from_slice(&msg_in).unwrap();

let mut partial_sigs: Vec<AggSigPartialSignature> = vec![];

for (i, (ss, _)) in keypairs.iter().enumerate() {
match aggsig.partial_sign(msg.clone(), ss.clone(), i) {
// create partial signatures
for (i, (sk, _)) in keys.iter().enumerate() {
match aggsig.partial_sign(msg.clone(), sk.clone(), i) {
Ok(res) => partial_sigs.push(res),
Err(e) => panic!("error creating partial signature: {:?}", e),
}
}

// aggregate signatures
match aggsig.combine_signatures(&partial_sigs) {
Ok(full_sig) => { let _ = aggsig.verify(full_sig, msg.clone(), &pks); () },
Ok(full_sig) => {
// verify with valid keys
assert!(aggsig.verify(full_sig, msg.clone(), &pks));
// verify with random keys, unlikely to ever return true
assert_eq!(aggsig.verify(full_sig, msg.clone(), &keys.iter().map(|(_, pk)| *pk).collect::<Vec<PublicKey>>()), false);
},
Err(e) => panic!("error combining signatures: {:?}", e),
}
});
15 changes: 3 additions & 12 deletions fuzz/fuzz_targets/fuzz_ecdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,9 @@ extern crate secp256k1zkp;
use secp256k1zkp::{Secp256k1, PublicKey, SecretKey};
use secp256k1zkp::ecdh::SharedSecret;

fuzz_target!(|data: &[u8]| {
if data.len() < 32 {
return ();
}

fuzz_target!(|keys: (SecretKey, PublicKey)| {
let s = Secp256k1::new();
let (sk, pk) = keys;

if let Ok(sk) = SecretKey::from_slice(&s, &data[..32]) {
match PublicKey::from_secret_key(&s, &sk) {
Ok(pk) => { let _ = SharedSecret::new(&s, &pk, &sk); () },
Err(e) => panic!("cannot create public key from secret: {}", e),
}
}
let _ = SharedSecret::new(&s, &pk, &sk);
});

25 changes: 9 additions & 16 deletions fuzz/fuzz_targets/fuzz_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,17 @@ extern crate secp256k1zkp;

use secp256k1zkp::{Message, Secp256k1, PublicKey, SecretKey};

fuzz_target!(|data: &[u8]| {
if data.len() < 64 {
return ();
}

fuzz_target!(|sk_msg: (SecretKey, Message)| {
let (sk, msg) = sk_msg;
let s = Secp256k1::new();

let msg = Message::from_slice(&data[..32]).unwrap();

if let Ok(sk) = SecretKey::from_slice(&s, &data[32..64]) {
match s.sign(&msg, &sk) {
Ok(sig) => {
match PublicKey::from_secret_key(&s, &sk) {
Ok(pk) => s.verify(&msg, &sig, &pk).unwrap(),
Err(e) => panic!("cannot create public key from secret: {}", e),
}
match s.sign(&msg, &sk) {
Ok(sig) => {
match PublicKey::from_secret_key(&s, &sk) {
Ok(pk) => s.verify(&msg, &sig, &pk).unwrap(),
Err(e) => panic!("cannot create public key from secret: {}", e),
}
Err(e) => panic!("error creating signature: {}", e),
}
},
Err(e) => panic!("error creating signature: {}", e),
}
});
17 changes: 17 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,23 @@ impl Signature {
pub unsafe fn blank() -> Signature { mem::MaybeUninit::uninit().assume_init() }
}

#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary for Signature
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let mut sig_bytes = [0 as c_uchar; 64];

let iter = u.arbitrary_iter::<c_uchar>()?;

for (i, byte) in iter.enumerate() {
if i == 64 { break; }
sig_bytes[i] = byte?;
}

Ok(Signature(sig_bytes))
}
}

impl RecoverableSignature {
/// Create a new (zeroed) signature usable for the FFI interface
pub fn new() -> RecoverableSignature { RecoverableSignature([0; 65]) }
Expand Down
36 changes: 36 additions & 0 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,24 @@ impl SecretKey {
}
}

#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary for SecretKey
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let mut sec_bytes = [0_u8; constants::SECRET_KEY_SIZE];

let s = Secp256k1::new();
let iter = u.arbitrary_iter::<u8>()?;

for (i, byte) in iter.enumerate() {
if i == constants::SECRET_KEY_SIZE { break; }
sec_bytes[i] = byte?;
}

SecretKey::from_slice(&s, &sec_bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
}
}

impl PublicKey {
/// Creates a new zeroed out public key
#[inline]
Expand Down Expand Up @@ -402,6 +420,24 @@ impl Serialize for PublicKey {
}
}

#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary for PublicKey
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let mut pub_bytes = [0_u8; constants::PUBLIC_KEY_SIZE];

let s = Secp256k1::new();
let iter = u.arbitrary_iter::<u8>()?;

for (i, byte) in iter.enumerate() {
if i == constants::PUBLIC_KEY_SIZE { break; }
pub_bytes[i] = byte?;
}

PublicKey::from_slice(&s, &pub_bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
}
}

#[cfg(test)]
mod test {
extern crate rand_core;
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub struct RecoveryId(i32);

/// An ECDSA signature
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Signature(ffi::Signature);

impl std::convert::AsRef<[u8]> for Signature {
Expand Down Expand Up @@ -423,6 +424,7 @@ impl ops::Index<ops::RangeFull> for Signature {
}

/// A (hashed) message input to an ECDSA signature
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Message([u8; constants::MESSAGE_SIZE]);
impl Copy for Message {}
impl_array_newtype!(Message, u8, constants::MESSAGE_SIZE);
Expand Down
36 changes: 36 additions & 0 deletions src/pedersen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,23 @@ impl Commitment {

}

#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary for Commitment
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let mut com_bytes = [0u8; constants::PEDERSEN_COMMITMENT_SIZE];

let iter = u.arbitrary_iter::<u8>()?;

for (i, byte) in iter.enumerate() {
if i == constants::PEDERSEN_COMMITMENT_SIZE { break; }
com_bytes[i] = byte?;
}

Ok(Commitment(com_bytes))
}
}

/// A range proof. Typically much larger in memory that the above (~5k).
#[derive(Copy)]
pub struct RangeProof {
Expand Down Expand Up @@ -249,6 +266,25 @@ impl RangeProof {
}
}

#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary for RangeProof
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let mut proof_bytes = [0u8; constants::MAX_PROOF_SIZE];
let mut plen = 0;

let iter = u.arbitrary_iter::<u8>()?;

for (i, byte) in iter.enumerate() {
if i == constants::MAX_PROOF_SIZE { plen = i; break; }
proof_bytes[i] = byte?;
plen = i + 1;
}

Ok(RangeProof{ proof: proof_bytes, plen: plen })
}
}

/// A message included in a range proof.
/// The message is recoverable by rewinding a range proof
/// passing in the same nonce that was used to originally create the range proof.
Expand Down