Functional Weave
Code in Python

crypto.constant-time-equal@1.0.0

impl/rust.rs

1,416 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one

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<i64> {
    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))
}