use super::funejson::Value; fn check_bytes(value: &[i64], name: &str) { if value.iter().any(|b| !(0..=255).contains(b)) { panic!("{} must be a list of integers from 0 to 255", name); } } /// Are two byte strings equal, without stopping at the first difference? /// Every byte is visited and the differences OR-ed together, so the time /// taken says nothing about where a forged tag goes wrong. Different lengths /// answer false at once: a digest's length is not a secret. /// /// # Panics /// Panics if any value is outside 0-255. pub fn constant_time_equal(a: &[i64], b: &[i64]) -> bool { check_bytes(a, "a"); check_bytes(b, "b"); if a.len() != b.len() { return false; } let mut diff: i64 = 0; for i in 0..a.len() { diff |= a[i] ^ b[i]; } diff == 0 } fn bytes_from_value(value: &Value, name: &str) -> Vec { match value { Value::Arr(items) => items .iter() .map(|item| match item { Value::Int(i) => *i, _ => panic!("{} must be a list of integers from 0 to 255", name), }) .collect(), _ => panic!("{} must be a list of integers from 0 to 255", name), } } pub fn fune_vector(args: &[Value]) -> Value { let a = bytes_from_value(&args[0], "a"); let b = bytes_from_value(&args[1], "b"); Value::Bool(constant_time_equal(&a, &b)) }