Merge pull request #15 from TheBlueMatt/master
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Fri, 23 Mar 2018 17:35:35 +0000 (13:35 -0400)
committerGitHub <noreply@github.com>
Fri, 23 Mar 2018 17:35:35 +0000 (13:35 -0400)
Add fuzz targets, bump bitcoin/secp256k1 deps

16 files changed:
.travis.yml
Cargo.toml
fuzz/Cargo.toml [new file with mode: 0644]
fuzz/fuzz_targets/channel_target.rs [new file with mode: 0644]
fuzz/fuzz_targets/full_stack_target.rs [new file with mode: 0644]
fuzz/fuzz_targets/peer_crypt_target.rs [new file with mode: 0644]
fuzz/travis-fuzz.sh [new file with mode: 0755]
src/ln/chan_utils.rs
src/ln/channel.rs
src/ln/channelmanager.rs
src/ln/channelmonitor.rs
src/ln/peer_channel_encryptor.rs
src/util/chacha20poly1305rfc.rs
src/util/mod.rs
src/util/rng.rs [new file with mode: 0644]
src/util/sha2.rs [new file with mode: 0644]

index d36f12aa7425b0682f45f13731a423e182440dc8..6a21dfa0b3a14d19d726756b5f3437b562933495 100644 (file)
@@ -1,4 +1,15 @@
 language: rust
 rust:
   - stable
+  - beta
+  - 1.22.0
 cache: cargo
+
+before_install:
+  - sudo apt-get -qq update
+  - sudo apt-get install -y binutils-dev libunwind8-dev
+
+script:
+  - cargo build --verbose
+  - cargo test --verbose
+  - if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
index b7c6e5cf521e1a306de90c44f9c4782b3aad6954..f5aa85db4aa7f4280b39f37682ce90d54dd15940 100644 (file)
@@ -12,12 +12,10 @@ Still super-early code-dump quality and is missing large chunks. See README in g
 [features]
 # Supports tracking channels with a non-bitcoin chain hashes. Currently enables all kinds of fun DoS attacks.
 non_bitcoin_chain_hash_routing = []
+fuzztarget = ["secp256k1/fuzztarget", "bitcoin/fuzztarget"]
 
 [dependencies]
-bitcoin = "0.11"
+bitcoin = "0.12"
 rust-crypto = "0.2"
 rand = "0.4"
-secp256k1 = "0.8"
-
-[patch.crates-io]
-bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin", branch = "master"  }
\ No newline at end of file
+secp256k1 = "0.9"
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
new file mode 100644 (file)
index 0000000..6b45066
--- /dev/null
@@ -0,0 +1,35 @@
+[package]
+name = "lightning-fuzz"
+version = "0.0.1"
+authors = ["Automatically generated"]
+publish = false
+
+[package.metadata]
+cargo-fuzz = true
+
+[features]
+afl_fuzz = ["afl"]
+honggfuzz_fuzz = ["honggfuzz"]
+
+[dependencies]
+lightning = { path = "..", features = ["fuzztarget"] }
+bitcoin = { version = "0.12", features = ["fuzztarget"] }
+secp256k1 = { version = "0.9", features = ["fuzztarget"] }
+honggfuzz = { version = "0.5", optional = true }
+afl = { version = "0.3", optional = true }
+
+# Prevent this from interfering with workspaces
+[workspace]
+members = ["."]
+
+[[bin]]
+name = "peer_crypt_target"
+path = "fuzz_targets/peer_crypt_target.rs"
+
+[[bin]]
+name = "channel_target"
+path = "fuzz_targets/channel_target.rs"
+
+[[bin]]
+name = "full_stack_target"
+path = "fuzz_targets/full_stack_target.rs"
diff --git a/fuzz/fuzz_targets/channel_target.rs b/fuzz/fuzz_targets/channel_target.rs
new file mode 100644 (file)
index 0000000..b8da216
--- /dev/null
@@ -0,0 +1,299 @@
+extern crate bitcoin;
+extern crate lightning;
+extern crate secp256k1;
+
+use bitcoin::blockdata::block::BlockHeader;
+use bitcoin::blockdata::transaction::Transaction;
+use bitcoin::util::hash::Sha256dHash;
+use bitcoin::network::serialize::{serialize, BitcoinHash};
+
+use lightning::ln::channel::Channel;
+use lightning::ln::channelmanager::PendingForwardHTLCInfo;
+use lightning::ln::msgs;
+use lightning::ln::msgs::MsgDecodable;
+use lightning::chain::chaininterface::{FeeEstimator, ConfirmationTarget};
+
+use secp256k1::key::PublicKey;
+use secp256k1::Secp256k1;
+
+use std::sync::atomic::{AtomicUsize,Ordering};
+
+#[inline]
+pub fn slice_to_be16(v: &[u8]) -> u16 {
+       ((v[0] as u16) << 8*1) |
+       ((v[1] as u16) << 8*0)
+}
+
+#[inline]
+pub fn slice_to_be32(v: &[u8]) -> u32 {
+       ((v[0] as u32) << 8*3) |
+       ((v[1] as u32) << 8*2) |
+       ((v[2] as u32) << 8*1) |
+       ((v[3] as u32) << 8*0)
+}
+
+#[inline]
+pub fn slice_to_be64(v: &[u8]) -> u64 {
+       ((v[0] as u64) << 8*7) |
+       ((v[1] as u64) << 8*6) |
+       ((v[2] as u64) << 8*5) |
+       ((v[3] as u64) << 8*4) |
+       ((v[4] as u64) << 8*3) |
+       ((v[5] as u64) << 8*2) |
+       ((v[6] as u64) << 8*1) |
+       ((v[7] as u64) << 8*0)
+}
+
+#[inline]
+fn slice_to_be24(v: &[u8]) -> u64 {
+       //TODO: We should probably be returning a Result for channel creation, not panic!()ing on
+       //>2**24 values...
+       ((v[0] as u64) << 8*2) |
+       ((v[1] as u64) << 8*1) |
+       ((v[2] as u64) << 8*0)
+}
+
+struct InputData<'a> {
+       data: &'a [u8],
+       read_pos: AtomicUsize,
+}
+impl<'a> InputData<'a> {
+       fn get_slice(&self, len: usize) -> Option<&'a [u8]> {
+               let old_pos = self.read_pos.fetch_add(len, Ordering::AcqRel);
+               if self.data.len() < old_pos + len {
+                       return None;
+               }
+               Some(&self.data[old_pos..old_pos + len])
+       }
+       fn get_slice_nonadvancing(&self, len: usize) -> Option<&'a [u8]> {
+               let old_pos = self.read_pos.load(Ordering::Acquire);
+               if self.data.len() < old_pos + len {
+                       return None;
+               }
+               Some(&self.data[old_pos..old_pos + len])
+       }
+}
+
+struct FuzzEstimator<'a> {
+       input: &'a InputData<'a>,
+}
+impl<'a> FeeEstimator for FuzzEstimator<'a> {
+       fn get_est_sat_per_vbyte(&self, _: ConfirmationTarget) -> u64 {
+               //TODO: We should actually be testing at least much more than 64k...
+               match self.input.get_slice(2) {
+                       Some(slice) => slice_to_be16(slice) as u64,
+                       None => 0
+               }
+       }
+}
+
+#[inline]
+pub fn do_test(data: &[u8]) {
+       let input = InputData {
+               data,
+               read_pos: AtomicUsize::new(0),
+       };
+       let fee_est = FuzzEstimator {
+               input: &input,
+       };
+
+       macro_rules! get_slice {
+               ($len: expr) => {
+                       match input.get_slice($len as usize) {
+                               Some(slice) => slice,
+                               None => return,
+                       }
+               }
+       }
+
+       macro_rules! decode_msg {
+               ($MsgType: path, $len: expr) => {
+                       match <($MsgType)>::decode(get_slice!($len)) {
+                               Ok(msg) => msg,
+                               Err(e) => match e {
+                                       msgs::DecodeError::UnknownRealmByte => return,
+                                       msgs::DecodeError::BadPublicKey => return,
+                                       msgs::DecodeError::BadSignature => return,
+                                       msgs::DecodeError::WrongLength => panic!("We picked the length..."),
+                               }
+                       }
+               }
+       }
+
+       macro_rules! decode_msg_with_len16 {
+               ($MsgType: path, $begin_len: expr, $factor: expr) => {
+                       {
+                               let extra_len = slice_to_be16(&match input.get_slice_nonadvancing($begin_len as usize + 2) {
+                                       Some(slice) => slice,
+                                       None => return,
+                               }[$begin_len..$begin_len + 2]);
+                               match <($MsgType)>::decode(get_slice!($begin_len as usize + 2 + (extra_len as usize)*$factor)) {
+                                       Ok(msg) => msg,
+                                       Err(e) => match e {
+                                               msgs::DecodeError::UnknownRealmByte => return,
+                                               msgs::DecodeError::BadPublicKey => return,
+                                               msgs::DecodeError::BadSignature => return,
+                                               msgs::DecodeError::WrongLength => panic!("We picked the length..."),
+                                       }
+                               }
+                       }
+               }
+       }
+
+       let secp_ctx = Secp256k1::new();
+       macro_rules! get_pubkey {
+               () => {
+                       match PublicKey::from_slice(&secp_ctx, get_slice!(33)) {
+                               Ok(key) => key,
+                               Err(_) => return,
+                       }
+               }
+       }
+
+       macro_rules! return_err {
+               ($expr: expr) => {
+                       match $expr {
+                               Ok(_) => {},
+                               Err(_) => return,
+                       }
+               }
+       }
+
+       let their_pubkey = get_pubkey!();
+
+       let tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new(), witness: Vec::new() };
+       let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);
+
+       let mut channel = if get_slice!(1)[0] != 0 {
+               let mut chan = Channel::new_outbound(&fee_est, their_pubkey, slice_to_be24(get_slice!(3)), get_slice!(1)[0] == 0, slice_to_be64(get_slice!(8)));
+               chan.get_open_channel(Sha256dHash::from(get_slice!(32)), &fee_est).unwrap();
+               let accept_chan = if get_slice!(1)[0] == 0 {
+                       decode_msg_with_len16!(msgs::AcceptChannel, 270, 1)
+               } else {
+                       decode_msg!(msgs::AcceptChannel, 270)
+               };
+               return_err!(chan.accept_channel(&accept_chan));
+               chan.get_outbound_funding_created(funding_output.0.clone(), funding_output.1).unwrap();
+               let funding_signed = decode_msg!(msgs::FundingSigned, 32+64);
+               return_err!(chan.funding_signed(&funding_signed));
+               chan
+       } else {
+               let open_chan = if get_slice!(1)[0] == 0 {
+                       decode_msg_with_len16!(msgs::OpenChannel, 2*32+6*8+4+2*2+6*33+1, 1)
+               } else {
+                       decode_msg!(msgs::OpenChannel, 2*32+6*8+4+2*2+6*33+1)
+               };
+               let mut chan = match Channel::new_from_req(&fee_est, their_pubkey, &open_chan, slice_to_be64(get_slice!(8)), get_slice!(1)[0] == 0) {
+                       Ok(chan) => chan,
+                       Err(_) => return,
+               };
+               chan.get_accept_channel().unwrap();
+               let mut funding_created = decode_msg!(msgs::FundingCreated, 32+32+2+64);
+               funding_created.funding_txid = funding_output.0.clone();
+               funding_created.funding_output_index = funding_output.1;
+               return_err!(chan.funding_created(&funding_created));
+               chan
+       };
+
+       let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+       channel.block_connected(&header, 1, &[&tx; 1], &[42; 1]);
+       for i in 2..100 {
+               header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+               channel.block_connected(&header, i, &[&tx; 0], &[0; 0]);
+       }
+
+       let funding_locked = decode_msg!(msgs::FundingLocked, 32+33);
+       return_err!(channel.funding_locked(&funding_locked));
+
+       loop {
+               match get_slice!(1)[0] {
+                       0 => {
+                               return_err!(channel.send_htlc(slice_to_be64(get_slice!(8)), [42; 32], slice_to_be32(get_slice!(4)), msgs::OnionPacket {
+                                       version: get_slice!(1)[0],
+                                       public_key: get_pubkey!(),
+                                       hop_data: [0; 20*65],
+                                       hmac: [0; 32],
+                               }));
+                       },
+                       1 => {
+                               return_err!(channel.send_commitment());
+                       },
+                       2 => {
+                               let update_add_htlc = decode_msg!(msgs::UpdateAddHTLC, 32+8+8+32+4+4+33+20*65+32);
+                               return_err!(channel.update_add_htlc(&update_add_htlc, PendingForwardHTLCInfo::dummy()));
+                       },
+                       3 => {
+                               let update_fulfill_htlc = decode_msg!(msgs::UpdateFulfillHTLC, 32 + 8 + 32);
+                               return_err!(channel.update_fulfill_htlc(&update_fulfill_htlc));
+                       },
+                       4 => {
+                               let update_fail_htlc = decode_msg_with_len16!(msgs::UpdateFailHTLC, 32 + 8, 1);
+                               return_err!(channel.update_fail_htlc(&update_fail_htlc));
+                       },
+                       5 => {
+                               let update_fail_malformed_htlc = decode_msg!(msgs::UpdateFailMalformedHTLC, 32+8+32+2);
+                               return_err!(channel.update_fail_malformed_htlc(&update_fail_malformed_htlc));
+                       },
+                       6 => {
+                               let commitment_signed = decode_msg_with_len16!(msgs::CommitmentSigned, 32+64, 64);
+                               return_err!(channel.commitment_signed(&commitment_signed));
+                       },
+                       7 => {
+                               let revoke_and_ack = decode_msg!(msgs::RevokeAndACK, 32+32+33);
+                               return_err!(channel.revoke_and_ack(&revoke_and_ack));
+                       },
+                       8 => {
+                               let update_fee = decode_msg!(msgs::UpdateFee, 32+4);
+                               return_err!(channel.update_fee(&fee_est, &update_fee));
+                       },
+                       _ => return,
+               }
+       }
+}
+
+#[cfg(feature = "afl")]
+extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+       afl::read_stdio_bytes(|data| {
+               do_test(&data);
+       });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+       loop {
+               fuzz!(|data| {
+                       do_test(data);
+               });
+       }
+}
+
+#[cfg(test)]
+mod tests {
+       fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
+               let mut b = 0;
+               for (idx, c) in hex.as_bytes().iter().enumerate() {
+                       b <<= 4;
+                       match *c {
+                               b'A'...b'F' => b |= c - b'A' + 10,
+                               b'a'...b'f' => b |= c - b'a' + 10,
+                               b'0'...b'9' => b |= c - b'0',
+                               _ => panic!("Bad hex"),
+                       }
+                       if (idx & 1) == 1 {
+                               out.push(b);
+                               b = 0;
+                       }
+               }
+       }
+
+       #[test]
+       fn duplicate_crash() {
+               let mut a = Vec::new();
+               extend_vec_from_hex("00", &mut a);
+               super::do_test(&a);
+       }
+}
diff --git a/fuzz/fuzz_targets/full_stack_target.rs b/fuzz/fuzz_targets/full_stack_target.rs
new file mode 100644 (file)
index 0000000..0264bbe
--- /dev/null
@@ -0,0 +1,224 @@
+extern crate bitcoin;
+extern crate lightning;
+extern crate secp256k1;
+
+use bitcoin::network::constants::Network;
+use bitcoin::util::hash::Sha256dHash;
+
+use lightning::chain::chaininterface::{ConfirmationTarget,FeeEstimator,ChainWatchInterfaceUtil};
+use lightning::ln::{channelmonitor,msgs};
+use lightning::ln::channelmanager::ChannelManager;
+use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor};
+use lightning::ln::router::Router;
+
+use secp256k1::key::{PublicKey,SecretKey};
+use secp256k1::Secp256k1;
+
+use std::sync::Arc;
+use std::sync::atomic::{AtomicUsize,Ordering};
+
+#[inline]
+pub fn slice_to_be16(v: &[u8]) -> u16 {
+       ((v[0] as u16) << 8*1) |
+       ((v[1] as u16) << 8*0)
+}
+
+#[inline]
+pub fn slice_to_be32(v: &[u8]) -> u32 {
+       ((v[0] as u32) << 8*3) |
+       ((v[1] as u32) << 8*2) |
+       ((v[2] as u32) << 8*1) |
+       ((v[3] as u32) << 8*0)
+}
+
+struct InputData {
+       data: Vec<u8>,
+       read_pos: AtomicUsize,
+}
+impl InputData {
+       fn get_slice(&self, len: usize) -> Option<&[u8]> {
+               let old_pos = self.read_pos.fetch_add(len, Ordering::AcqRel);
+               if self.data.len() < old_pos + len {
+                       return None;
+               }
+               Some(&self.data[old_pos..old_pos + len])
+       }
+       fn get_slice_nonadvancing(&self, len: usize) -> Option<&[u8]> {
+               let old_pos = self.read_pos.load(Ordering::Acquire);
+               if self.data.len() < old_pos + len {
+                       return None;
+               }
+               Some(&self.data[old_pos..old_pos + len])
+       }
+}
+
+struct FuzzEstimator {
+       input: Arc<InputData>,
+}
+impl FeeEstimator for FuzzEstimator {
+       fn get_est_sat_per_vbyte(&self, _: ConfirmationTarget) -> u64 {
+               //TODO: We should actually be testing at least much more than 64k...
+               match self.input.get_slice(2) {
+                       Some(slice) => slice_to_be16(slice) as u64,
+                       None => 0
+               }
+       }
+}
+
+struct TestChannelMonitor {}
+impl channelmonitor::ManyChannelMonitor for TestChannelMonitor {
+       fn add_update_monitor(&self, _funding_txo: (Sha256dHash, u16), _monitor: channelmonitor::ChannelMonitor) -> Result<(), msgs::HandleError> {
+               //TODO!
+               Ok(())
+       }
+}
+
+#[derive(Clone, PartialEq, Eq, Hash)]
+struct Peer {
+       id: u8,
+}
+impl SocketDescriptor for Peer {
+       fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, _resume_read: bool) -> usize {
+               assert!(write_offset < data.len());
+               data.len() - write_offset
+       }
+}
+
+#[inline]
+pub fn do_test(data: &[u8]) {
+       let input = Arc::new(InputData {
+               data: data.to_vec(),
+               read_pos: AtomicUsize::new(0),
+       });
+       let fee_est = Arc::new(FuzzEstimator {
+               input: input.clone(),
+       });
+
+       macro_rules! get_slice {
+               ($len: expr) => {
+                       match input.get_slice($len as usize) {
+                               Some(slice) => slice,
+                               None => return,
+                       }
+               }
+       }
+
+       let secp_ctx = Secp256k1::new();
+       macro_rules! get_pubkey {
+               () => {
+                       match PublicKey::from_slice(&secp_ctx, get_slice!(33)) {
+                               Ok(key) => key,
+                               Err(_) => return,
+                       }
+               }
+       }
+
+       let our_network_key = match SecretKey::from_slice(&secp_ctx, get_slice!(32)) {
+               Ok(key) => key,
+               Err(_) => return,
+       };
+
+       let monitor = Arc::new(TestChannelMonitor{});
+       let watch = Arc::new(ChainWatchInterfaceUtil::new());
+
+       let channelmanager = ChannelManager::new(our_network_key, slice_to_be32(get_slice!(4)), get_slice!(1)[0] != 0, Network::Bitcoin, fee_est.clone(), monitor.clone(), watch.clone()).unwrap();
+       let router = Arc::new(Router::new(PublicKey::from_secret_key(&secp_ctx, &our_network_key).unwrap()));
+
+       let handler = PeerManager::new(MessageHandler {
+               chan_handler: channelmanager.clone(),
+               route_handler: router.clone(),
+       }, our_network_key);
+
+       let mut peers = [false; 256];
+
+       loop {
+               match get_slice!(1)[0] {
+                       0 => {
+                               let mut new_id = 0;
+                               for i in 1..256 {
+                                       if !peers[i-1] {
+                                               new_id = i;
+                                               break;
+                                       }
+                               }
+                               if new_id == 0 { return; }
+                               peers[new_id - 1] = true;
+                               handler.new_outbound_connection(get_pubkey!(), Peer{id: (new_id - 1) as u8}).unwrap();
+                       },
+                       1 => {
+                               let mut new_id = 0;
+                               for i in 1..256 {
+                                       if !peers[i-1] {
+                                               new_id = i;
+                                               break;
+                                       }
+                               }
+                               if new_id == 0 { return; }
+                               peers[new_id - 1] = true;
+                               handler.new_inbound_connection(Peer{id: (new_id - 1) as u8}).unwrap();
+                       },
+                       2 => {
+                               let peer_id = get_slice!(1)[0];
+                               if !peers[peer_id as usize] { return; }
+                               peers[peer_id as usize] = false;
+                               handler.disconnect_event(&Peer{id: peer_id});
+                       },
+                       3 => {
+                               let peer_id = get_slice!(1)[0];
+                               if !peers[peer_id as usize] { return; }
+                               match handler.read_event(&mut Peer{id: peer_id}, get_slice!(get_slice!(1)[0]).to_vec()) {
+                                       Ok(res) => assert!(!res),
+                                       Err(_) => { peers[peer_id as usize] = false; }
+                               }
+                       },
+                       _ => return,
+               }
+       }
+}
+
+#[cfg(feature = "afl")]
+extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+       afl::read_stdio_bytes(|data| {
+               do_test(&data);
+       });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+       loop {
+               fuzz!(|data| {
+                       do_test(data);
+               });
+       }
+}
+
+#[cfg(test)]
+mod tests {
+       fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
+               let mut b = 0;
+               for (idx, c) in hex.as_bytes().iter().enumerate() {
+                       b <<= 4;
+                       match *c {
+                               b'A'...b'F' => b |= c - b'A' + 10,
+                               b'a'...b'f' => b |= c - b'a' + 10,
+                               b'0'...b'9' => b |= c - b'0',
+                               _ => panic!("Bad hex"),
+                       }
+                       if (idx & 1) == 1 {
+                               out.push(b);
+                               b = 0;
+                       }
+               }
+       }
+
+       #[test]
+       fn duplicate_crash() {
+               let mut a = Vec::new();
+               extend_vec_from_hex("00", &mut a);
+               super::do_test(&a);
+       }
+}
diff --git a/fuzz/fuzz_targets/peer_crypt_target.rs b/fuzz/fuzz_targets/peer_crypt_target.rs
new file mode 100644 (file)
index 0000000..b7287fb
--- /dev/null
@@ -0,0 +1,124 @@
+extern crate lightning;
+extern crate secp256k1;
+
+use lightning::ln::peer_channel_encryptor::PeerChannelEncryptor;
+
+use secp256k1::key::{PublicKey,SecretKey};
+use secp256k1::Secp256k1;
+
+#[inline]
+fn slice_to_be16(v: &[u8]) -> u16 {
+       ((v[0] as u16) << 8*1) |
+       ((v[1] as u16) << 8*0)
+}
+
+#[inline]
+pub fn do_test(data: &[u8]) {
+       let mut read_pos = 0;
+       macro_rules! get_slice {
+               ($len: expr) => {
+                       {
+                               let slice_len = $len as usize;
+                               if data.len() < read_pos + slice_len {
+                                       return;
+                               }
+                               read_pos += slice_len;
+                               &data[read_pos - slice_len..read_pos]
+                       }
+               }
+       }
+
+       let secp_ctx = Secp256k1::new();
+       let our_network_key = match SecretKey::from_slice(&secp_ctx, get_slice!(32)) {
+               Ok(key) => key,
+               Err(_) => return,
+       };
+
+       let mut crypter = if get_slice!(1)[0] != 0 {
+               let their_pubkey = match PublicKey::from_slice(&secp_ctx, get_slice!(33)) {
+                       Ok(key) => key,
+                       Err(_) => return,
+               };
+               let mut crypter = PeerChannelEncryptor::new_outbound(their_pubkey);
+               crypter.get_act_one();
+               match crypter.process_act_two(get_slice!(50), &our_network_key) {
+                       Ok(_) => {},
+                       Err(_) => return,
+               }
+               assert!(crypter.is_ready_for_encryption());
+               crypter
+       } else {
+               let mut crypter = PeerChannelEncryptor::new_inbound(&our_network_key);
+               match crypter.process_act_one_with_key(get_slice!(50), &our_network_key) {
+                       Ok(_) => {},
+                       Err(_) => return,
+               }
+               match crypter.process_act_three(get_slice!(66)) {
+                       Ok(_) => {},
+                       Err(_) => return,
+               }
+               assert!(crypter.is_ready_for_encryption());
+               crypter
+       };
+       loop {
+               if get_slice!(1)[0] == 0 {
+                       crypter.encrypt_message(get_slice!(slice_to_be16(get_slice!(2))));
+               } else {
+                       let len = match crypter.decrypt_length_header(get_slice!(16+2)) {
+                               Ok(len) => len,
+                               Err(_) => return,
+                       };
+                       match crypter.decrypt_message(get_slice!(len as usize + 16)) {
+                               Ok(_) => {},
+                               Err(_) => return,
+                       }
+               }
+       }
+}
+
+#[cfg(feature = "afl")]
+extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+       afl::read_stdio_bytes(|data| {
+               do_test(&data);
+       });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+       loop {
+               fuzz!(|data| {
+                       do_test(data);
+               });
+       }
+}
+
+#[cfg(test)]
+mod tests {
+       fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
+               let mut b = 0;
+               for (idx, c) in hex.as_bytes().iter().enumerate() {
+                       b <<= 4;
+                       match *c {
+                               b'A'...b'F' => b |= c - b'A' + 10,
+                               b'a'...b'f' => b |= c - b'a' + 10,
+                               b'0'...b'9' => b |= c - b'0',
+                               _ => panic!("Bad hex"),
+                       }
+                       if (idx & 1) == 1 {
+                               out.push(b);
+                               b = 0;
+                       }
+               }
+       }
+
+       #[test]
+       fn duplicate_crash() {
+               let mut a = Vec::new();
+               extend_vec_from_hex("01", &mut a);
+               super::do_test(&a);
+       }
+}
diff --git a/fuzz/travis-fuzz.sh b/fuzz/travis-fuzz.sh
new file mode 100755 (executable)
index 0000000..dfe03d1
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/bash
+cargo install honggfuzz
+set +e
+for TARGET in fuzz_targets/*; do
+    FILENAME=$(basename $TARGET)
+       FILE="${FILENAME%.*}"
+       HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" HFUZZ_RUN_ARGS="-N1000000 --exit_upon_crash -v" cargo hfuzz run $FILE
+       if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then
+               cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT
+               for CASE in hfuzz_workspace/$FILE/SIG*; do
+                       cat $CASE | xxd -p
+               done
+               exit 1
+       fi
+done
index dd5515c88046ac18b0686e7a810d5cfdeed65c3e..eb85c0e1f3e1a985d39be9161c3d9e670ed50a4e 100644 (file)
@@ -7,9 +7,10 @@ use secp256k1::Secp256k1;
 use secp256k1;
 
 use crypto::digest::Digest;
-use crypto::sha2::Sha256;
 use crypto::ripemd160::Ripemd160;
 
+use util::sha2::Sha256;
+
 // Various functions for key derivation and transaction creation for use within channels. Primarily
 // used in Channel and ChannelMonitor.
 
index 2b8b085a8e4f84979697d9df26fc7515fce2e112..6c6dacc84c3821c2285b0156a62498dc068b6a49 100644 (file)
@@ -13,7 +13,6 @@ use secp256k1;
 
 use crypto::digest::Digest;
 use crypto::hkdf::{hkdf_extract,hkdf_expand};
-use crypto::sha2::Sha256;
 
 use ln::msgs;
 use ln::msgs::{HandleError, MsgEncodable};
@@ -22,9 +21,8 @@ use ln::channelmanager::PendingForwardHTLCInfo;
 use ln::chan_utils::{TxCreationKeys,HTLCOutputInCommitment};
 use ln::chan_utils;
 use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
-use util::transaction_utils;
-
-use rand::{thread_rng,Rng};
+use util::{transaction_utils,rng};
+use util::sha2::Sha256;
 
 use std::default::Default;
 use std::cmp;
@@ -274,12 +272,11 @@ impl Channel {
                        panic!("funding value > 2^24");
                }
 
-               let mut rng = thread_rng();
                let feerate = fee_estimator.get_est_sat_per_vbyte(ConfirmationTarget::Normal);
                let background_feerate = fee_estimator.get_est_sat_per_vbyte(ConfirmationTarget::Background);
 
                let mut key_seed = [0u8; 32];
-               rng.fill_bytes(&mut key_seed);
+               rng::fill_bytes(&mut key_seed);
                let chan_keys = match ChannelKeys::new_from_seed(&key_seed) {
                        Ok(key) => key,
                        Err(_) => panic!("RNG is busted!")
@@ -296,7 +293,7 @@ impl Channel {
                Channel {
                        user_id: user_id,
 
-                       channel_id: Uint256([rng.gen(), rng.gen(), rng.gen(), rng.gen()]),
+                       channel_id: rng::rand_uint256(),
                        channel_state: ChannelState::OurInitSent as u32,
                        channel_outbound: true,
                        secp_ctx: secp_ctx,
@@ -392,9 +389,8 @@ impl Channel {
 
                let background_feerate = fee_estimator.get_est_sat_per_vbyte(ConfirmationTarget::Background);
 
-               let mut rng = thread_rng();
                let mut key_seed = [0u8; 32];
-               rng.fill_bytes(&mut key_seed);
+               rng::fill_bytes(&mut key_seed);
                let chan_keys = match ChannelKeys::new_from_seed(&key_seed) {
                        Ok(key) => key,
                        Err(_) => panic!("RNG is busted!")
index 14e089249cdd37adc089a40b772abbe486301ce0..0dddfdac6760c473b5e0efabc864eb7dc062b550 100644 (file)
@@ -17,16 +17,12 @@ use ln::channelmonitor::ManyChannelMonitor;
 use ln::router::Route;
 use ln::msgs;
 use ln::msgs::{HandleError,ChannelMessageHandler,MsgEncodable,MsgDecodable};
-use util::byte_utils;
-use util::events;
-use util::internal_traits;
-
-use rand::{thread_rng,Rng};
+use util::{byte_utils, events, internal_traits, rng};
+use util::sha2::Sha256;
 
 use crypto::mac::{Mac,MacResult};
 use crypto::hmac::Hmac;
 use crypto::digest::Digest;
-use crypto::sha2::Sha256;
 use crypto::symmetriccipher::SynchronousStreamCipher;
 use crypto::chacha20::ChaCha20;
 
@@ -46,6 +42,20 @@ pub struct PendingForwardHTLCInfo {
        amt_to_forward: u64,
        outgoing_cltv_value: u32,
 }
+//TODO: This is public, and needed to call Channel::update_add_htlc, so there needs to be a way to
+//initialize it usefully...probably make it optional in Channel instead).
+impl PendingForwardHTLCInfo {
+       pub fn dummy() -> Self {
+               Self {
+                       onion_packet: None,
+                       payment_hash: [0; 32],
+                       short_channel_id: 0,
+                       prev_short_channel_id: 0,
+                       amt_to_forward: 0,
+                       outgoing_cltv_value: 0,
+               }
+       }
+}
 
 enum PendingOutboundHTLC {
        IntermediaryHopData {
@@ -468,10 +478,9 @@ impl ChannelManager {
                        }
                }
 
-               let mut rng = thread_rng();
                let session_priv = secp_call!(SecretKey::from_slice(&self.secp_ctx, &{
                        let mut session_key = [0; 32];
-                       rng.fill_bytes(&mut session_key);
+                       rng::fill_bytes(&mut session_key);
                        session_key
                }));
 
@@ -1319,8 +1328,7 @@ impl ChannelMessageHandler for ChannelManager {
                        };
 
                        if channel_state.forward_htlcs.is_empty() {
-                               let mut rng = thread_rng();
-                               forward_event = Some(Instant::now() + Duration::from_millis(((rng.next_f32() * 4.0 + 1.0) * MIN_HTLC_RELAY_HOLDING_CELL_MILLIS as f32) as u64));
+                               forward_event = Some(Instant::now() + Duration::from_millis(((rng::rand_f32() * 4.0 + 1.0) * MIN_HTLC_RELAY_HOLDING_CELL_MILLIS as f32) as u64));
                                channel_state.next_forward = forward_event.unwrap();
                        }
                        for forward_info in forwarding_infos.drain(..) {
index d5529af9c99e579d7f169446cf50f4022b151c70..5ac9a6b0ab01cd93eb276b7a68ff29e2b7763298 100644 (file)
@@ -4,7 +4,6 @@ use bitcoin::blockdata::script::Script;
 use bitcoin::util::hash::Sha256dHash;
 use bitcoin::util::bip143;
 
-use crypto::sha2::Sha256;
 use crypto::digest::Digest;
 
 use secp256k1::{Secp256k1,Message,Signature};
@@ -14,6 +13,7 @@ use ln::msgs::HandleError;
 use ln::chan_utils;
 use ln::chan_utils::HTLCOutputInCommitment;
 use chain::chaininterface::{ChainListener, ChainWatchInterface, BroadcasterInterface};
+use util::sha2::Sha256;
 
 use std::collections::HashMap;
 use std::sync::{Arc,Mutex};
index e22d8bd3574969cdee2ff1470a1207b5f4607480..67b0a14cb168910bc97e472dce26ba2c95e4645f 100644 (file)
@@ -5,16 +5,14 @@ use secp256k1::Secp256k1;
 use secp256k1::key::{PublicKey,SecretKey};
 use secp256k1::ecdh::SharedSecret;
 
-use rand::{thread_rng,Rng};
-
 use crypto::digest::Digest;
 use crypto::hkdf::{hkdf_extract,hkdf_expand};
-use crypto::sha2::Sha256;
 
 use crypto::aead::{AeadEncryptor, AeadDecryptor};
 
 use util::chacha20poly1305rfc::ChaCha20Poly1305RFC;
-use util::byte_utils;
+use util::{byte_utils,rng};
+use util::sha2::Sha256;
 
 // Sha256("Noise_XK_secp256k1_ChaChaPoly_SHA256")
 const NOISE_CK: [u8; 32] = [0x26, 0x40, 0xf5, 0x2e, 0xeb, 0xcd, 0x9e, 0x88, 0x29, 0x58, 0x95, 0x1c, 0x79, 0x42, 0x50, 0xee, 0xdb, 0x28, 0x00, 0x2c, 0x05, 0xd7, 0xdc, 0x2e, 0xa0, 0xf1, 0x95, 0x40, 0x60, 0x42, 0xca, 0xf1];
@@ -75,9 +73,8 @@ pub struct PeerChannelEncryptor {
 
 impl PeerChannelEncryptor {
        pub fn new_outbound(their_node_id: PublicKey) -> PeerChannelEncryptor {
-               let mut rng = thread_rng();
                let mut key = [0u8; 32];
-               rng.fill_bytes(&mut key);
+               rng::fill_bytes(&mut key);
 
                let secp_ctx = Secp256k1::new();
                let sec_key = SecretKey::from_slice(&secp_ctx, &key).unwrap(); //TODO: nicer rng-is-bad error message
@@ -275,9 +272,8 @@ impl PeerChannelEncryptor {
        pub fn process_act_one_with_key(&mut self, act_one: &[u8], our_node_secret: &SecretKey) -> Result<[u8; 50], HandleError> {
                assert_eq!(act_one.len(), 50);
 
-               let mut rng = thread_rng();
                let mut key = [0u8; 32];
-               rng.fill_bytes(&mut key);
+               rng::fill_bytes(&mut key);
                let our_ephemeral_key = SecretKey::from_slice(&self.secp_ctx, &key).unwrap(); //TODO: nicer rng-is-bad error message
                self.process_act_one_with_ephemeral_key(act_one, our_node_secret, our_ephemeral_key)
        }
index f9391db01f727741c76a4c7c912da37ee30f6df8..9c86d44b4949e24e86abcc043d5c2de885cef7c1 100644 (file)
 // This is a port of Andrew Moons poly1305-donna
 // https://github.com/floodyberry/poly1305-donna
 
-use crypto::aead::{AeadEncryptor,AeadDecryptor};
-use crypto::chacha20::ChaCha20;
-use crypto::symmetriccipher::SynchronousStreamCipher;
-use crypto::poly1305::Poly1305;
-use crypto::mac::Mac;
-use crypto::util::fixed_time_eq;
-
-use util::byte_utils;
-
-#[derive(Clone, Copy)]
-pub struct ChaCha20Poly1305RFC {
-    cipher  : ChaCha20,
-    mac: Poly1305,
-    finished: bool,
-    data_len: usize,
-    aad_len: u64,
+#[cfg(not(feature = "fuzztarget"))]
+mod real_chachapoly {
+       use crypto::aead::{AeadEncryptor,AeadDecryptor};
+       use crypto::chacha20::ChaCha20;
+       use crypto::symmetriccipher::SynchronousStreamCipher;
+       use crypto::poly1305::Poly1305;
+       use crypto::mac::Mac;
+       use crypto::util::fixed_time_eq;
+
+       use util::byte_utils;
+
+       #[derive(Clone, Copy)]
+       pub struct ChaCha20Poly1305RFC {
+               cipher  : ChaCha20,
+               mac: Poly1305,
+               finished: bool,
+               data_len: usize,
+               aad_len: u64,
+       }
+
+       impl ChaCha20Poly1305RFC {
+               #[inline]
+               fn pad_mac_16(mac: &mut Poly1305, len: usize) {
+                       if len % 16 != 0 {
+                               mac.input(&[0; 16][0..16 - (len % 16)]);
+                       }
+               }
+               pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
+                       assert!(key.len() == 16 || key.len() == 32);
+                       assert!(nonce.len() == 12);
+
+                       // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
+                       assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
+
+                       let mut cipher = ChaCha20::new(key, &nonce[4..]);
+                       let mut mac_key = [0u8; 64];
+                       let zero_key = [0u8; 64];
+                       cipher.process(&zero_key, &mut mac_key);
+
+                       let mut mac = Poly1305::new(&mac_key[..32]);
+                       mac.input(aad);
+                       ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
+
+                       ChaCha20Poly1305RFC {
+                               cipher: cipher,
+                               mac: mac,
+                               finished: false,
+                               data_len: 0,
+                               aad_len: aad.len() as u64,
+                       }
+               }
+       }
+
+       impl AeadEncryptor for ChaCha20Poly1305RFC {
+               fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
+                       assert!(input.len() == output.len());
+                       assert!(self.finished == false);
+                       self.cipher.process(input, output);
+                       self.data_len += input.len();
+                       self.mac.input(output);
+                       ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
+                       self.finished = true;
+                       self.mac.input(&byte_utils::le64_to_array(self.aad_len));
+                       self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
+                       self.mac.raw_result(out_tag);
+               }
+       }
+
+       impl AeadDecryptor for ChaCha20Poly1305RFC {
+               fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
+                       assert!(input.len() == output.len());
+                       assert!(self.finished == false);
+
+                       self.finished = true;
+
+                       self.mac.input(input);
+
+                       self.data_len += input.len();
+                       ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
+                       self.mac.input(&byte_utils::le64_to_array(self.aad_len));
+                       self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
+
+                       let mut calc_tag =  [0u8; 16];
+                       self.mac.raw_result(&mut calc_tag);
+                       if fixed_time_eq(&calc_tag, tag) {
+                               self.cipher.process(input, output);
+                               true
+                       } else {
+                               false
+                       }
+               }
+       }
 }
-
-impl ChaCha20Poly1305RFC {
-  #[inline]
-  fn pad_mac_16(mac: &mut Poly1305, len: usize) {
-      if len % 16 != 0 {
-        mac.input(&[0; 16][0..16 - (len % 16)]);
-      }
-  }
-  pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
-      assert!(key.len() == 16 || key.len() == 32);
-      assert!(nonce.len() == 12);
-
-      // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
-      assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
-
-      let mut cipher = ChaCha20::new(key, &nonce[4..]);
-      let mut mac_key = [0u8; 64];
-      let zero_key = [0u8; 64];
-      cipher.process(&zero_key, &mut mac_key);
-
-      let mut mac = Poly1305::new(&mac_key[..32]);
-      mac.input(aad);
-      ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
-
-      ChaCha20Poly1305RFC {
-        cipher: cipher,
-        mac: mac,
-        finished: false,
-        data_len: 0,
-        aad_len: aad.len() as u64,
-      }
-  }
-}
-
-impl AeadEncryptor for ChaCha20Poly1305RFC {
-    fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
-        assert!(input.len() == output.len());
-        assert!(self.finished == false);
-        self.cipher.process(input, output);
-        self.data_len += input.len();
-        self.mac.input(output);
-        ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
-        self.finished = true;
-        self.mac.input(&byte_utils::le64_to_array(self.aad_len));
-        self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
-        self.mac.raw_result(out_tag);
-    }
-}
-
-impl AeadDecryptor for ChaCha20Poly1305RFC {
-    fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
-        assert!(input.len() == output.len());
-        assert!(self.finished == false);
-
-        self.finished = true;
-
-        self.mac.input(input);
-
-        self.data_len += input.len();
-        ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
-        self.mac.input(&byte_utils::le64_to_array(self.aad_len));
-        self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
-
-        let mut calc_tag =  [0u8; 16];
-        self.mac.raw_result(&mut calc_tag);
-        if fixed_time_eq(&calc_tag, tag) {
-            self.cipher.process(input, output);
-            true
-        } else {
-            false
-        }
-    }
+#[cfg(not(feature = "fuzztarget"))]
+pub use self::real_chachapoly::ChaCha20Poly1305RFC;
+
+#[cfg(feature = "fuzztarget")]
+mod fuzzy_chachapoly {
+       use crypto::aead::{AeadEncryptor,AeadDecryptor};
+
+       #[derive(Clone, Copy)]
+       pub struct ChaCha20Poly1305RFC {
+               tag: [u8; 16],
+               finished: bool,
+       }
+       impl ChaCha20Poly1305RFC {
+               pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
+                       assert!(key.len() == 16 || key.len() == 32);
+                       assert!(nonce.len() == 12);
+
+                       // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
+                       assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
+
+                       let mut tag = [0; 16];
+                       tag.copy_from_slice(&key[0..16]);
+
+                       ChaCha20Poly1305RFC {
+                               tag,
+                               finished: false,
+                       }
+               }
+       }
+
+       impl AeadEncryptor for ChaCha20Poly1305RFC {
+               fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
+                       assert!(input.len() == output.len());
+                       assert!(self.finished == false);
+
+                       output.copy_from_slice(&input);
+                       out_tag.copy_from_slice(&self.tag);
+                       self.finished = true;
+               }
+       }
+
+       impl AeadDecryptor for ChaCha20Poly1305RFC {
+               fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
+                       assert!(input.len() == output.len());
+                       assert!(self.finished == false);
+
+                       if tag[..] != self.tag[..] { return false; }
+                       output.copy_from_slice(input);
+                       self.finished = true;
+                       true
+               }
+       }
 }
+#[cfg(feature = "fuzztarget")]
+pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
index 0e92a2b27e4804bd7ddb14608c75fe1fddbad798..b7578bce407caca84ef77d675edcb4ac8135208e 100644 (file)
@@ -4,6 +4,8 @@ pub mod events;
 pub(crate) mod byte_utils;
 pub(crate) mod chacha20poly1305rfc;
 pub(crate) mod internal_traits;
+pub(crate) mod rng;
+pub(crate) mod sha2;
 
 #[cfg(test)]
 pub(crate) mod test_utils;
diff --git a/src/util/rng.rs b/src/util/rng.rs
new file mode 100644 (file)
index 0000000..f0d4492
--- /dev/null
@@ -0,0 +1,43 @@
+#[cfg(not(feature = "fuzztarget"))]
+mod real_rng {
+       use rand::{thread_rng,Rng};
+       use bitcoin::util::uint::Uint256;
+
+       pub fn fill_bytes(data: &mut [u8]) {
+               let mut rng = thread_rng();
+               rng.fill_bytes(data);
+       }
+
+       pub fn rand_uint256() -> Uint256 {
+               let mut rng = thread_rng();
+               Uint256([rng.gen(), rng.gen(), rng.gen(), rng.gen()])
+       }
+
+       pub fn rand_f32() -> f32 {
+               let mut rng = thread_rng();
+               rng.next_f32()
+       }
+}
+#[cfg(not(feature = "fuzztarget"))]
+pub use self::real_rng::*;
+
+#[cfg(feature = "fuzztarget")]
+mod fuzzy_rng {
+       use bitcoin::util::uint::Uint256;
+
+       pub fn fill_bytes(data: &mut [u8]) {
+               for i in 0..data.len() {
+                       data[i] = 0x42;
+               }
+       }
+
+       pub fn rand_uint256() -> Uint256 {
+               Uint256([0xdeadbeef, 0x1badcafe, 0xbadbeef, 0xdeadcafe])
+       }
+
+       pub fn rand_f32() -> f32 {
+               0.42
+       }
+}
+#[cfg(feature = "fuzztarget")]
+pub use self::fuzzy_rng::*;
diff --git a/src/util/sha2.rs b/src/util/sha2.rs
new file mode 100644 (file)
index 0000000..31616f5
--- /dev/null
@@ -0,0 +1,37 @@
+#[cfg(not(feature = "fuzztarget"))]
+pub use crypto::sha2::Sha256;
+
+#[cfg(feature = "fuzztarget")]
+mod fuzzy_sha {
+       use crypto::digest::Digest;
+       use crypto::sha2;
+
+       #[derive(Clone, Copy)]
+       pub struct Sha256 {
+               state: sha2::Sha256,
+       }
+
+       impl Sha256 {
+               pub fn new() -> Sha256 {
+                       Sha256 {
+                               state: sha2::Sha256::new(),
+                       }
+               }
+       }
+
+       impl Digest for Sha256 {
+               fn result(&mut self, data: &mut [u8]) {
+                       self.state.result(data);
+                       for i in 1..32 {
+                               data[i] = 0;
+                       }
+               }
+
+               fn input(&mut self, data: &[u8]) { self.state.input(data); }
+               fn reset(&mut self) { self.state.reset(); }
+               fn output_bits(&self) -> usize { self.state.output_bits() }
+               fn block_size(&self) -> usize { self.state.block_size() }
+       }
+}
+#[cfg(feature = "fuzztarget")]
+pub use self::fuzzy_sha::Sha256;