Skip to main content

x16rs_sys/
lib.rs

1#[link(name = "x16rs", kind = "static")]
2unsafe extern "C" {
3    // output must be *mut: C writes 32 bytes through this pointer.
4    // Declaring *const here is UB (write via const) and lets LLVM assume
5    // the output buffer never changes, returning all zeros under optimization.
6    fn c_x16rs_hash(loopnum: i32, input: *const u8, output: *mut u8);
7}
8
9pub const H32S: usize = 32;
10
11/// Compute the X16RS hash.
12///
13/// `indata` and the returned buffer are 32-byte digests. `loopnum` is the
14/// number of X16R rounds (typically 1..=16 for block hashing).
15pub fn x16rs_hash(loopnum: i32, indata: &[u8; H32S]) -> [u8; H32S] {
16    let mut outdata = [0u8; H32S];
17    unsafe {
18        c_x16rs_hash(loopnum, indata.as_ptr(), outdata.as_mut_ptr());
19    }
20    outdata
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26
27    fn to_hex(bytes: &[u8; H32S]) -> String {
28        bytes.iter().map(|b| format!("{:02x}", b)).collect()
29    }
30
31    #[test]
32    fn hash_is_not_all_zeros() {
33        let input = [0u8; H32S];
34        let out = x16rs_hash(1, &input);
35        assert_ne!(
36            out,
37            [0u8; H32S],
38            "x16rs_hash must not return all zeros (FFI UB regression)"
39        );
40    }
41
42    #[test]
43    fn hash_zero_input_loop1() {
44        let input = [0u8; H32S];
45        let out = x16rs_hash(1, &input);
46        assert_eq!(
47            to_hex(&out),
48            "6fe2a4b96f71518b7603e5c63702588ba816885aa1ce5908de31335e11473460"
49        );
50        assert_eq!(x16rs_hash(1, &input), out);
51    }
52
53    #[test]
54    fn hash_sequential_input_loop1() {
55        let mut input = [0u8; H32S];
56        for (i, b) in input.iter_mut().enumerate() {
57            *b = i as u8;
58        }
59        assert_eq!(
60            to_hex(&x16rs_hash(1, &input)),
61            "5f4b9c2bc542352be3bd684ce2228447ba14b3cf32a41b04d18b52290435cea5"
62        );
63    }
64
65    #[test]
66    fn hash_loop0_is_identity_copy() {
67        // With loopnum == 0, C copies input to output without hashing.
68        let mut input = [0u8; H32S];
69        input[0] = 0xab;
70        input[31] = 0xcd;
71        assert_eq!(x16rs_hash(0, &input), input);
72    }
73}