snarkvm_console_algorithms/blake2xs/
mod.rs1mod hash_to_curve;
21
22pub struct Blake2Xs;
23
24impl Blake2Xs {
25    fn evaluate(input: &[u8], xof_digest_length: u16, persona: &[u8]) -> Vec<u8> {
30        assert!(xof_digest_length > 0, "Output digest must be of non-zero length");
31        assert!(persona.len() <= 8, "Personalization may be at most 8 characters");
32
33        let xof_digest_length_node_offset = (xof_digest_length as u64) << 32;
35        let input_digest = blake2s_simd::Params::new()
36            .hash_length(32)
37            .node_offset(xof_digest_length_node_offset)
38            .personal(persona)
39            .hash(input);
40
41        let mut output = vec![];
42
43        let num_rounds = xof_digest_length.saturating_add(31) / 32;
44        for node_offset in 0..num_rounds {
45            let is_final_round = node_offset == num_rounds - 1;
47            let has_remainder = xof_digest_length % 32 != 0;
48            let digest_length = match is_final_round && has_remainder {
49                true => (xof_digest_length % 32) as usize,
50                false => 32,
51            };
52
53            output.extend_from_slice(
55                blake2s_simd::Params::new()
56                    .hash_length(digest_length)
57                    .fanout(0)
58                    .max_depth(0)
59                    .max_leaf_length(32)
60                    .node_offset(xof_digest_length_node_offset | (node_offset as u64))
61                    .inner_hash_length(32)
62                    .personal(persona)
63                    .hash(input_digest.as_bytes())
64                    .as_bytes(),
65            );
66        }
67
68        output
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use crate::Blake2Xs;
75    use serde::Deserialize;
76
77    #[derive(Deserialize)]
78    struct Case {
79        hash: String,
80        #[serde(rename = "in")]
81        input: String,
82        key: String,
83        #[serde(rename = "out")]
84        output: String,
85    }
86
87    #[test]
88    fn test_blake2xs() {
89        let vectors: Vec<Case> = serde_json::from_str(include_str!("./resources/blake2-kat.json")).unwrap();
91        for case in vectors.iter().filter(|v| &v.hash == "blake2xs" && v.key.is_empty()) {
92            let input = hex::decode(case.input.as_bytes()).unwrap();
93            let xof_digest_length = u16::try_from(case.output.len()).unwrap() / 2;
94            let output = hex::encode(Blake2Xs::evaluate(&input, xof_digest_length, "".as_bytes()));
95            assert_eq!(output, case.output);
96        }
97    }
98
99    #[test]
100    fn test_blake2s() {
101        let vectors: Vec<Case> = serde_json::from_str(include_str!("./resources/blake2-kat.json")).unwrap();
103        for case in vectors.iter().filter(|v| &v.hash == "blake2s" && v.key.is_empty()) {
104            let input = hex::decode(case.input.as_bytes()).unwrap();
105            let output = hex::encode(blake2s_simd::Params::new().personal(&0u64.to_le_bytes()).hash(&input).as_bytes());
106            assert_eq!(output, case.output);
107        }
108    }
109}