use std::time::Duration;
use tacet::{helpers::InputPair, timing_test_checked, AttackerModel, Outcome, TimingOracle};
#[allow(clippy::redundant_closure)]
fn main() {
println!("Comparing constant-time vs variable-time implementations\n");
let secret = [0xABu8; 32];
println!("Testing variable-time comparison...");
let outcome = timing_test_checked! {
oracle: TimingOracle::for_attacker(AttackerModel::AdjacentNetwork)
.time_budget(Duration::from_secs(30)),
baseline: || [0xABu8; 32], sample: || rand_bytes(), measure: |data| {
variable_time_compare(&secret, data);
},
};
match outcome {
Outcome::Pass {
leak_probability, ..
} => {
println!(" Result: PASS (unexpected!)");
println!(" Leak probability: {:.1}%\n", leak_probability * 100.0);
}
Outcome::Fail {
leak_probability,
exploitability,
..
} => {
println!(" Result: FAIL (expected)");
println!(" Leak probability: {:.1}%", leak_probability * 100.0);
println!(" Exploitability: {:?}\n", exploitability);
}
Outcome::Inconclusive {
leak_probability, ..
} => {
println!(" Result: INCONCLUSIVE");
println!(" Leak probability: {:.1}%\n", leak_probability * 100.0);
}
Outcome::Unmeasurable { recommendation, .. } => {
println!(" Could not measure: {}\n", recommendation);
}
Outcome::Research(research) => {
println!(" Result: RESEARCH MODE");
println!(" Status: {:?}\n", research.status);
}
}
println!("Testing constant-time comparison...");
let inputs = InputPair::new(|| [0xABu8; 32], rand_bytes);
let outcome = TimingOracle::for_attacker(AttackerModel::AdjacentNetwork)
.time_budget(Duration::from_secs(30))
.test(inputs, |data| {
constant_time_compare(&secret, data);
});
match outcome {
Outcome::Pass {
leak_probability, ..
} => {
println!(" Result: PASS (expected)");
println!(" Leak probability: {:.1}%", leak_probability * 100.0);
}
Outcome::Fail {
leak_probability,
exploitability,
..
} => {
println!(" Result: FAIL (unexpected!)");
println!(" Leak probability: {:.1}%", leak_probability * 100.0);
println!(" Exploitability: {:?}", exploitability);
}
Outcome::Inconclusive {
leak_probability, ..
} => {
println!(" Result: INCONCLUSIVE");
println!(" Leak probability: {:.1}%", leak_probability * 100.0);
}
Outcome::Unmeasurable { recommendation, .. } => {
println!(" Could not measure: {}", recommendation);
}
Outcome::Research(research) => {
println!(" Result: RESEARCH MODE");
println!(" Status: {:?}", research.status);
}
}
}
fn variable_time_compare(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
for i in 0..a.len() {
if a[i] != b[i] {
return false;
}
}
true
}
fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut acc = 0u8;
for i in 0..a.len() {
acc |= a[i] ^ b[i];
}
acc == 0
}
fn rand_bytes() -> [u8; 32] {
let mut arr = [0u8; 32];
for byte in &mut arr {
*byte = rand::random();
}
arr
}