arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/hash/rpo/digest.rs
src/hash/rpo/digest.rsRaw
  1use super::DIGEST_SIZE;
  2use crate::{
  3    ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, Felt, Serializable,
  4    StarkField, String, ZERO,
  5};
  6use core::{cmp::Ordering, ops::Deref};
  7
  8// DIGEST TRAIT IMPLEMENTATIONS
  9// ================================================================================================
 10
 11#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 12pub struct RpoDigest256([Felt; DIGEST_SIZE]);
 13
 14impl RpoDigest256 {
 15    pub fn new(value: [Felt; DIGEST_SIZE]) -> Self {
 16        Self(value)
 17    }
 18
 19    pub fn as_elements(&self) -> &[Felt] {
 20        self.as_ref()
 21    }
 22
 23    pub fn digests_as_elements<'a, I>(digests: I) -> impl Iterator<Item = &'a Felt>
 24    where
 25        I: Iterator<Item = &'a Self>,
 26    {
 27        digests.flat_map(|d| d.0.iter())
 28    }
 29}
 30
 31impl Digest for RpoDigest256 {
 32    fn as_bytes(&self) -> [u8; 32] {
 33        let mut result = [0; 32];
 34
 35        result[..8].copy_from_slice(&self.0[0].as_int().to_le_bytes());
 36        result[8..16].copy_from_slice(&self.0[1].as_int().to_le_bytes());
 37        result[16..24].copy_from_slice(&self.0[2].as_int().to_le_bytes());
 38        result[24..].copy_from_slice(&self.0[3].as_int().to_le_bytes());
 39
 40        result
 41    }
 42}
 43
 44impl Default for RpoDigest256 {
 45    fn default() -> Self {
 46        RpoDigest256([Felt::default(); DIGEST_SIZE])
 47    }
 48}
 49
 50impl Serializable for RpoDigest256 {
 51    fn write_into<W: ByteWriter>(&self, target: &mut W) {
 52        target.write_u8_slice(&self.as_bytes());
 53    }
 54}
 55
 56impl Deserializable for RpoDigest256 {
 57    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
 58        let mut inner: [Felt; DIGEST_SIZE] = [ZERO; DIGEST_SIZE];
 59        for inner in inner.iter_mut() {
 60            let e = source.read_u64()?;
 61            if e >= Felt::MODULUS {
 62                return Err(DeserializationError::InvalidValue(String::from(
 63                    "Value not in the appropriate range",
 64                )));
 65            }
 66            *inner = Felt::new(e);
 67        }
 68
 69        Ok(Self(inner))
 70    }
 71}
 72
 73impl From<[Felt; DIGEST_SIZE]> for RpoDigest256 {
 74    fn from(value: [Felt; DIGEST_SIZE]) -> Self {
 75        Self(value)
 76    }
 77}
 78
 79impl From<RpoDigest256> for [Felt; DIGEST_SIZE] {
 80    fn from(value: RpoDigest256) -> Self {
 81        value.0
 82    }
 83}
 84
 85impl From<RpoDigest256> for [u8; 32] {
 86    fn from(value: RpoDigest256) -> Self {
 87        value.as_bytes()
 88    }
 89}
 90
 91impl Deref for RpoDigest256 {
 92    type Target = [Felt; DIGEST_SIZE];
 93
 94    fn deref(&self) -> &Self::Target {
 95        &self.0
 96    }
 97}
 98
 99impl Ord for RpoDigest256 {
100    fn cmp(&self, other: &Self) -> Ordering {
101        // compare the inner u64 of both elements.
102        //
103        // it will iterate the elements and will return the first computation different than
104        // `Equal`. Otherwise, the ordering is equal.
105        //
106        // the endianness is irrelevant here because since, this being a cryptographically secure
107        // hash computation, the digest shouldn't have any ordered property of its input.
108        //
109        // finally, we use `Felt::inner` instead of `Felt::as_int` so we avoid performing a
110        // montgomery reduction for every limb. that is safe because every inner element of the
111        // digest is guaranteed to be in its canonical form (that is, `x in [0,p)`).
112        self.0
113            .iter()
114            .map(Felt::inner)
115            .zip(other.0.iter().map(Felt::inner))
116            .fold(Ordering::Equal, |ord, (a, b)| match ord {
117                Ordering::Equal => a.cmp(&b),
118                _ => ord,
119            })
120    }
121}
122
123impl PartialOrd for RpoDigest256 {
124    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
125        Some(self.cmp(other))
126    }
127}
128
129// TESTS
130// ================================================================================================
131
132#[cfg(test)]
133mod tests {
134
135    use super::RpoDigest256;
136    use crate::{Deserializable, Felt, Serializable, SliceReader};
137    use rand_utils::rand_value;
138
139    #[test]
140    fn digest_serialization() {
141        let e1 = Felt::new(rand_value());
142        let e2 = Felt::new(rand_value());
143        let e3 = Felt::new(rand_value());
144        let e4 = Felt::new(rand_value());
145
146        let d1 = RpoDigest256([e1, e2, e3, e4]);
147
148        let mut bytes = vec![];
149        d1.write_into(&mut bytes);
150        assert_eq!(32, bytes.len());
151
152        let mut reader = SliceReader::new(&bytes);
153        let d2 = RpoDigest256::read_from(&mut reader).unwrap();
154
155        assert_eq!(d1, d2);
156    }
157}