aztec_crypto/
sha512.rs

1//! SHA-512 to Grumpkin scalar conversion.
2
3use aztec_core::types::{Fr, GrumpkinScalar};
4use sha2::{Digest, Sha512};
5
6/// Compute SHA-512 of the concatenated inputs and reduce to a Grumpkin scalar.
7///
8/// This is used for master key derivation where uniform distribution is
9/// required (not just collision resistance). The 512-bit hash output
10/// ensures negligible bias after modular reduction.
11///
12/// Mirrors TS `sha512ToGrumpkinScalar([secretKey, domainSeparator])`.
13pub fn sha512_to_grumpkin_scalar(secret_key: &Fr, domain_separator: u32) -> GrumpkinScalar {
14    // 1. Serialize: [secret_key (32 bytes BE)] ++ [domain_separator (4 bytes BE)]
15    let mut input = [0u8; 36];
16    input[..32].copy_from_slice(&secret_key.to_be_bytes());
17    input[32..36].copy_from_slice(&domain_separator.to_be_bytes());
18
19    // 2. SHA-512 hash
20    let hash = Sha512::digest(input); // 64 bytes
21
22    // 3. Reduce mod Fq::MODULUS
23    GrumpkinScalar::from_be_bytes_mod_order(&hash)
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29    use aztec_core::constants::domain_separator;
30
31    #[test]
32    fn sha512_input_buffer_is_36_bytes() {
33        // Verify the serialization layout: 32 bytes Fr + 4 bytes separator
34        let sk = Fr::from(8923u64);
35        let sep = domain_separator::NHK_M;
36
37        let mut expected_input = [0u8; 36];
38        expected_input[..32].copy_from_slice(&sk.to_be_bytes());
39        expected_input[32..36].copy_from_slice(&sep.to_be_bytes());
40
41        // The function produces a non-zero result
42        let result = sha512_to_grumpkin_scalar(&sk, sep);
43        assert!(!result.is_zero());
44    }
45
46    #[test]
47    fn sha512_deterministic() {
48        let sk = Fr::from(42u64);
49        let r1 = sha512_to_grumpkin_scalar(&sk, 123);
50        let r2 = sha512_to_grumpkin_scalar(&sk, 123);
51        assert_eq!(r1, r2);
52    }
53
54    #[test]
55    fn sha512_different_separators_produce_different_results() {
56        let sk = Fr::from(42u64);
57        let r1 = sha512_to_grumpkin_scalar(&sk, domain_separator::NHK_M);
58        let r2 = sha512_to_grumpkin_scalar(&sk, domain_separator::IVSK_M);
59        assert_ne!(r1, r2);
60    }
61}