1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
//! SideFuzz is an adaptive fuzzer that uses a genetic-algorithim optimizer in combination with t-statistics to find side-channel (timing) vulnerabilities in cryptography compiled to wasm. //! //! See the [README](https://github.com/phayes/sidefuzz) for complete documentation. //! //! Creating a target in rust is done in the following way: //! //! ```rust,ignore //! // lib.rs //! #[no_mangle] //! pub extern "C" fn fuzz() { //! let input = sidefuzz::fetch_input(32); // 32 bytes of of fuzzing input as a &[u8] //! sidefuzz::black_box(my_hopefully_constant_fn(input)); //! } //! ``` //! ```toml //! # Cargo.toml //! [lib] //! crate-type = ["cdylib"] //! //! [dependencies] //! sidefuzz = "0.1.2" //! ``` //! Compile and fuzz the target like so: //! //! ```bash //! cargo build --release --target wasm32-unknown-unknown # Always build in release mode //! sidefuzz fuzz ./target/wasm32-unknown-unknown/release/my_target.wasm # Fuzzing! //! ``` // This section contains utility functions used by WASM targets // ------------------------------------------------------------ /// A function that is opaque to the optimizer, to allow fuzzed functions to /// pretend to use outputs to assist in avoiding dead-code elimination. /// /// NOTE: We don't have a proper black box in stable Rust. This is /// a workaround implementation, that may have a too big performance overhead, /// depending on operation, or it may fail to properly avoid having code /// optimized out. It is good enough that it is used. #[inline(never)] pub fn black_box<D>(dummy: D) -> D { unsafe { let ret = std::ptr::read_volatile(&dummy); std::mem::forget(dummy); ret } } // Assign a 1024 byte vector to hold inputs lazy_static::lazy_static! { static ref INPUT: Vec<u8> = vec![0; 1024]; } // The actual input length (generally less than 1024) static mut INPUT_LEN: i32 = 0; /// Get an input of the desired length. /// This function should be called with a constant unchanging len argument. /// Calling it with different lengths will result in invalid fuzzing. /// /// Example: /// ```ignore /// let input = sidefuzz::fetch_input(32); // get 32 bytes of input /// sidefuzz::black_box(my_contant_time_fn(input)); /// ``` /// // This is a VERY odd fuction that provides us with a really nice external API. // 1. It is called once before fuzzing starts in order to set the size of INPUT. // 2. After it is called once, we call input_pointer and input_len from the host to get a stable pointer to INPUT. // 3. Fuzzing starts, we write data to INPUT from the host, then call the exported `fuzz` function. pub fn fetch_input(len: i32) -> &'static [u8] { // This use of unsafe since wasm is single-threaded and nothing else is accessing INPUT_LEN. unsafe { if INPUT_LEN == 0 { INPUT_LEN = len; panic!("Input length successfully set. Panicking to unwind and stop execution."); } } &INPUT[0..len as usize] } /// Get a pointer to the input array /// This needs to be public so we can call it across host/wasm boundary, /// but it should be considered a "private" function to sidefuzz. /// It's API is not stable and may be subject to change #[doc(hidden)] #[no_mangle] pub extern "C" fn input_pointer() -> i32 { INPUT.as_ptr() as i32 } /// Get the length of the input array /// This needs to be public so we can call it across host/wasm boundary, /// but it should be considered a "private" function to sidefuzz. /// It's API is not stable and may be subject to change #[doc(hidden)] #[no_mangle] pub extern "C" fn input_len() -> i32 { unsafe { INPUT_LEN } }