Skip to main content

frost_bluepallas/
hasher.rs

1//! Mina-compatible hashing utilities for FROST using the Pallas curve.
2
3use alloc::string::String;
4use ark_ff::PrimeField;
5use frost_core::Field;
6use mina_hasher::{create_legacy, Hashable, Hasher, ROInput};
7
8use crate::PallasScalarField;
9
10/// This is a Hashable interface for an array of bytes
11/// This allows us to provide a easy-to-read interface for hashing FROST elements in H1, H3, H4, H5
12#[derive(Clone, Debug)]
13pub(crate) struct PallasHashElement<'a> {
14    value: &'a [&'a [u8]],
15}
16
17// Implement a hashable trait for a u8 slice
18impl Hashable for PallasHashElement<'_> {
19    type D = ();
20
21    fn to_roinput(&self) -> ROInput {
22        let mut roi = ROInput::new();
23
24        for val in self.value {
25            roi = roi.append_bytes(val);
26        }
27
28        roi
29    }
30
31    // As of right now, assume domain string is included in the input
32    fn domain_string(_domain_param: Self::D) -> Option<String> {
33        None
34    }
35}
36
37type Fq = <PallasScalarField as Field>::Scalar;
38
39// Maps poseidon hash of input to a scalar field element
40pub fn hash_to_scalar(input: &[&[u8]]) -> Fq {
41    let wrap = PallasHashElement { value: input };
42    let mut hasher = create_legacy::<PallasHashElement>(());
43
44    // Convert from base field to scalar field
45    // This is performed in the mina-signer crate
46    // https://github.com/o1-labs/proof-systems/blob/6d2ac796205456d314d7ea2a3db6e0e816d60a99/signer/src/schnorr.rs#L145-L158
47    Fq::from(hasher.hash(&wrap).into_bigint())
48}
49
50// Maps poseidon hash of input to a 32-byte array
51pub fn hash_to_array(input: &[&[u8]]) -> <PallasScalarField as frost_core::Field>::Serialization {
52    let scalar = hash_to_scalar(input);
53
54    PallasScalarField::serialize(&scalar)
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn test_hash_to_scalar_is_deterministic_and_differs() {
63        let input = &[&b"abc"[..]];
64        let s1 = hash_to_scalar(input);
65        let s2 = hash_to_scalar(input);
66        assert_eq!(s1, s2, "same input must yield same scalar");
67
68        let other = &[&b"def"[..]];
69        let s3 = hash_to_scalar(other);
70        assert_ne!(s1, s3, "different input must yield a different scalar");
71    }
72
73    #[test]
74    fn test_hash_to_array_length() {
75        let arr = hash_to_array(&[&b"hello"[..]]);
76        // Serialization for PallasScalarField is 32 bytes
77        assert_eq!(arr.len(), 32);
78    }
79}