diff --git a/Cargo.toml b/Cargo.toml index 05ee0d0d5..b7fe7c894 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,8 @@ rand = "0.6" rand_core = "0.4" serde_test = "1.0" bitcoin_hashes = "0.7" +llvm-ir = "^0.5" +pitchfork = { package = "haybale-pitchfork", version = "^0.2" } [target.wasm32-unknown-unknown.dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/README_PITCHFORK.md b/README_PITCHFORK.md new file mode 100644 index 000000000..eb3460f88 --- /dev/null +++ b/README_PITCHFORK.md @@ -0,0 +1,16 @@ +### Build test to emit LLVM bitcode + +Haybale-pitchfork requires llvm-sys (LLVM Rust bindings) and boolector to be +installed as shared libraries prior to generating bitcode and compiling this test. + +Generate LLVM bitcode: + +``` +CARGO_INCREMENTAL="" cargo rustc -- -g --emit llvm-bc +``` + +Run test: + +``` +cargo test --test pitchfork +``` diff --git a/tests/mod.rs b/tests/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/tests/pitchfork.rs b/tests/pitchfork.rs new file mode 100644 index 000000000..9b7225eb0 --- /dev/null +++ b/tests/pitchfork.rs @@ -0,0 +1,90 @@ +extern crate llvm_ir; +extern crate pitchfork; + +use pitchfork::{ + AbstractData, + AbstractValue, + Config, + PitchforkConfig, + Project, + StructDescriptions, + check_for_ct_violation, +}; + +fn run_pitchfork(fn_name: &String, + args: &Option>, + struct_desc: &StructDescriptions) { + // Path to generated bitcode + let mut bc_path = std::env::current_exe().unwrap(); + bc_path.pop(); + + let project = Project::from_bc_dir(&bc_path, "bc").unwrap(); + + // Get all mangled function names for ConstantTimeEq implementations + let ct_func_names = project + .all_functions() + .filter(|x| x.0.name.contains(fn_name)) + .collect::>(); + + let mut config = Config::default(); + config.loop_bound = 100; + + // Test each function for constant-time violations + for func in ct_func_names { + let result = check_for_ct_violation(&func.0.name, + &project, + args.clone(), + &struct_desc, + config.clone(), + &PitchforkConfig::default()); + + if result.path_results.len() != 0 { + panic!("Constant-time result:\n\n{}", &result); + } + } +} + +#[test] +fn test_ct_abs64() { + let args = Some(vec![ + AbstractData::pub_pointer_to(AbstractData::sec_integer(64)), + AbstractData::sec_i64(), + ]); + + let sd = StructDescriptions::new(); + + run_pitchfork(&String::from("secp256k1_sign_and_abs64"), &args, &sd); +} + +#[test] +fn test_ct_sign() { + let args = Some(vec![ + AbstractData::pub_pointer_to(AbstractData::default()), + AbstractData::pub_pointer_to(AbstractData::default()), + AbstractData::pub_pointer_to(AbstractData::default()), + AbstractData::pub_pointer_to(AbstractData::secret()), + AbstractData::pub_pointer_to(AbstractData::default()), + AbstractData::pub_pointer_to(AbstractData::default()), + AbstractData::pub_pointer_to(AbstractData::pub_i8(AbstractValue::Unconstrained)), + ]); + + let sd = StructDescriptions::new(); + + run_pitchfork(&String::from("secp256k1_ecdsa_sign"), &args, &sd); +} + +#[test] +fn test_ct_ecdh() { + let args = Some(vec![ + AbstractData::pub_pointer_to(AbstractData::default()), + AbstractData::pub_pointer_to(AbstractData::default()), + AbstractData::pub_pointer_to(AbstractData::default()), + AbstractData::pub_pointer_to(AbstractData::secret()), + AbstractData::default(), + AbstractData::pub_pointer_to(AbstractData::default()), + ]); + + let sd = StructDescriptions::new(); + + run_pitchfork(&String::from("secp256k1_ecdh"), &args, &sd); +}