arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/hash/rescue/rpo/digest.rs
src/hash/rescue/rpo/digest.rsRaw
  1use super::{Digest, Felt, StarkField, DIGEST_BYTES, DIGEST_SIZE, ZERO};
  2use crate::utils::{
  3    bytes_to_hex_string, hex_to_bytes, string::String, ByteReader, ByteWriter, Deserializable,
  4    DeserializationError, HexParseError, Serializable,
  5};
  6use core::{cmp::Ordering, fmt::Display, ops::Deref};
  7use winter_utils::Randomizable;
  8
  9// DIGEST TRAIT IMPLEMENTATIONS
 10// ================================================================================================
 11
 12#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
 13#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
 14#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
 15pub struct RpoDigest([Felt; DIGEST_SIZE]);
 16
 17impl RpoDigest {
 18    pub const fn new(value: [Felt; DIGEST_SIZE]) -> Self {
 19        Self(value)
 20    }
 21
 22    pub fn as_elements(&self) -> &[Felt] {
 23        self.as_ref()
 24    }
 25
 26    pub fn as_bytes(&self) -> [u8; DIGEST_BYTES] {
 27        <Self as Digest>::as_bytes(self)
 28    }
 29
 30    pub fn digests_as_elements<'a, I>(digests: I) -> impl Iterator<Item = &'a Felt>
 31    where
 32        I: Iterator<Item = &'a Self>,
 33    {
 34        digests.flat_map(|d| d.0.iter())
 35    }
 36
 37    /// Returns hexadecimal representation of this digest prefixed with `0x`.
 38    pub fn to_hex(&self) -> String {
 39        bytes_to_hex_string(self.as_bytes())
 40    }
 41}
 42
 43impl Digest for RpoDigest {
 44    fn as_bytes(&self) -> [u8; DIGEST_BYTES] {
 45        let mut result = [0; DIGEST_BYTES];
 46
 47        result[..8].copy_from_slice(&self.0[0].as_int().to_le_bytes());
 48        result[8..16].copy_from_slice(&self.0[1].as_int().to_le_bytes());
 49        result[16..24].copy_from_slice(&self.0[2].as_int().to_le_bytes());
 50        result[24..].copy_from_slice(&self.0[3].as_int().to_le_bytes());
 51
 52        result
 53    }
 54}
 55
 56impl Deref for RpoDigest {
 57    type Target = [Felt; DIGEST_SIZE];
 58
 59    fn deref(&self) -> &Self::Target {
 60        &self.0
 61    }
 62}
 63
 64impl Ord for RpoDigest {
 65    fn cmp(&self, other: &Self) -> Ordering {
 66        // compare the inner u64 of both elements.
 67        //
 68        // it will iterate the elements and will return the first computation different than
 69        // `Equal`. Otherwise, the ordering is equal.
 70        //
 71        // the endianness is irrelevant here because since, this being a cryptographically secure
 72        // hash computation, the digest shouldn't have any ordered property of its input.
 73        //
 74        // finally, we use `Felt::inner` instead of `Felt::as_int` so we avoid performing a
 75        // montgomery reduction for every limb. that is safe because every inner element of the
 76        // digest is guaranteed to be in its canonical form (that is, `x in [0,p)`).
 77        self.0.iter().map(Felt::inner).zip(other.0.iter().map(Felt::inner)).fold(
 78            Ordering::Equal,
 79            |ord, (a, b)| match ord {
 80                Ordering::Equal => a.cmp(&b),
 81                _ => ord,
 82            },
 83        )
 84    }
 85}
 86
 87impl PartialOrd for RpoDigest {
 88    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 89        Some(self.cmp(other))
 90    }
 91}
 92
 93impl Display for RpoDigest {
 94    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 95        let encoded: String = self.into();
 96        write!(f, "{}", encoded)?;
 97        Ok(())
 98    }
 99}
100
101impl Randomizable for RpoDigest {
102    const VALUE_SIZE: usize = DIGEST_BYTES;
103
104    fn from_random_bytes(bytes: &[u8]) -> Option<Self> {
105        let bytes_array: Option<[u8; 32]> = bytes.try_into().ok();
106        if let Some(bytes_array) = bytes_array {
107            Self::try_from(bytes_array).ok()
108        } else {
109            None
110        }
111    }
112}
113
114// CONVERSIONS: FROM RPO DIGEST
115// ================================================================================================
116
117impl From<&RpoDigest> for [Felt; DIGEST_SIZE] {
118    fn from(value: &RpoDigest) -> Self {
119        value.0
120    }
121}
122
123impl From<RpoDigest> for [Felt; DIGEST_SIZE] {
124    fn from(value: RpoDigest) -> Self {
125        value.0
126    }
127}
128
129impl From<&RpoDigest> for [u64; DIGEST_SIZE] {
130    fn from(value: &RpoDigest) -> Self {
131        [
132            value.0[0].as_int(),
133            value.0[1].as_int(),
134            value.0[2].as_int(),
135            value.0[3].as_int(),
136        ]
137    }
138}
139
140impl From<RpoDigest> for [u64; DIGEST_SIZE] {
141    fn from(value: RpoDigest) -> Self {
142        [
143            value.0[0].as_int(),
144            value.0[1].as_int(),
145            value.0[2].as_int(),
146            value.0[3].as_int(),
147        ]
148    }
149}
150
151impl From<&RpoDigest> for [u8; DIGEST_BYTES] {
152    fn from(value: &RpoDigest) -> Self {
153        value.as_bytes()
154    }
155}
156
157impl From<RpoDigest> for [u8; DIGEST_BYTES] {
158    fn from(value: RpoDigest) -> Self {
159        value.as_bytes()
160    }
161}
162
163impl From<RpoDigest> for String {
164    /// The returned string starts with `0x`.
165    fn from(value: RpoDigest) -> Self {
166        value.to_hex()
167    }
168}
169
170impl From<&RpoDigest> for String {
171    /// The returned string starts with `0x`.
172    fn from(value: &RpoDigest) -> Self {
173        (*value).into()
174    }
175}
176
177// CONVERSIONS: TO RPO DIGEST
178// ================================================================================================
179
180#[derive(Copy, Clone, Debug)]
181pub enum RpoDigestError {
182    /// The provided u64 integer does not fit in the field's moduli.
183    InvalidInteger,
184}
185
186impl From<&[Felt; DIGEST_SIZE]> for RpoDigest {
187    fn from(value: &[Felt; DIGEST_SIZE]) -> Self {
188        Self(*value)
189    }
190}
191
192impl From<[Felt; DIGEST_SIZE]> for RpoDigest {
193    fn from(value: [Felt; DIGEST_SIZE]) -> Self {
194        Self(value)
195    }
196}
197
198impl TryFrom<[u8; DIGEST_BYTES]> for RpoDigest {
199    type Error = HexParseError;
200
201    fn try_from(value: [u8; DIGEST_BYTES]) -> Result<Self, Self::Error> {
202        // Note: the input length is known, the conversion from slice to array must succeed so the
203        // `unwrap`s below are safe
204        let a = u64::from_le_bytes(value[0..8].try_into().unwrap());
205        let b = u64::from_le_bytes(value[8..16].try_into().unwrap());
206        let c = u64::from_le_bytes(value[16..24].try_into().unwrap());
207        let d = u64::from_le_bytes(value[24..32].try_into().unwrap());
208
209        if [a, b, c, d].iter().any(|v| *v >= Felt::MODULUS) {
210            return Err(HexParseError::OutOfRange);
211        }
212
213        Ok(RpoDigest([Felt::new(a), Felt::new(b), Felt::new(c), Felt::new(d)]))
214    }
215}
216
217impl TryFrom<&[u8; DIGEST_BYTES]> for RpoDigest {
218    type Error = HexParseError;
219
220    fn try_from(value: &[u8; DIGEST_BYTES]) -> Result<Self, Self::Error> {
221        (*value).try_into()
222    }
223}
224
225impl TryFrom<&[u8]> for RpoDigest {
226    type Error = HexParseError;
227
228    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
229        (*value).try_into()
230    }
231}
232
233impl TryFrom<[u64; DIGEST_SIZE]> for RpoDigest {
234    type Error = RpoDigestError;
235
236    fn try_from(value: [u64; DIGEST_SIZE]) -> Result<Self, RpoDigestError> {
237        Ok(Self([
238            value[0].try_into().map_err(|_| RpoDigestError::InvalidInteger)?,
239            value[1].try_into().map_err(|_| RpoDigestError::InvalidInteger)?,
240            value[2].try_into().map_err(|_| RpoDigestError::InvalidInteger)?,
241            value[3].try_into().map_err(|_| RpoDigestError::InvalidInteger)?,
242        ]))
243    }
244}
245
246impl TryFrom<&[u64; DIGEST_SIZE]> for RpoDigest {
247    type Error = RpoDigestError;
248
249    fn try_from(value: &[u64; DIGEST_SIZE]) -> Result<Self, RpoDigestError> {
250        (*value).try_into()
251    }
252}
253
254impl TryFrom<&str> for RpoDigest {
255    type Error = HexParseError;
256
257    /// Expects the string to start with `0x`.
258    fn try_from(value: &str) -> Result<Self, Self::Error> {
259        hex_to_bytes(value).and_then(|v| v.try_into())
260    }
261}
262
263impl TryFrom<String> for RpoDigest {
264    type Error = HexParseError;
265
266    /// Expects the string to start with `0x`.
267    fn try_from(value: String) -> Result<Self, Self::Error> {
268        value.as_str().try_into()
269    }
270}
271
272impl TryFrom<&String> for RpoDigest {
273    type Error = HexParseError;
274
275    /// Expects the string to start with `0x`.
276    fn try_from(value: &String) -> Result<Self, Self::Error> {
277        value.as_str().try_into()
278    }
279}
280
281// SERIALIZATION / DESERIALIZATION
282// ================================================================================================
283
284impl Serializable for RpoDigest {
285    fn write_into<W: ByteWriter>(&self, target: &mut W) {
286        target.write_bytes(&self.as_bytes());
287    }
288}
289
290impl Deserializable for RpoDigest {
291    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
292        let mut inner: [Felt; DIGEST_SIZE] = [ZERO; DIGEST_SIZE];
293        for inner in inner.iter_mut() {
294            let e = source.read_u64()?;
295            if e >= Felt::MODULUS {
296                return Err(DeserializationError::InvalidValue(String::from(
297                    "Value not in the appropriate range",
298                )));
299            }
300            *inner = Felt::new(e);
301        }
302
303        Ok(Self(inner))
304    }
305}
306
307// ITERATORS
308// ================================================================================================
309impl IntoIterator for RpoDigest {
310    type Item = Felt;
311    type IntoIter = <[Felt; 4] as IntoIterator>::IntoIter;
312
313    fn into_iter(self) -> Self::IntoIter {
314        self.0.into_iter()
315    }
316}
317
318// TESTS
319// ================================================================================================
320
321#[cfg(test)]
322mod tests {
323    use super::{Deserializable, Felt, RpoDigest, Serializable, DIGEST_BYTES, DIGEST_SIZE};
324    use crate::utils::{string::String, SliceReader};
325    use rand_utils::rand_value;
326
327    #[test]
328    fn digest_serialization() {
329        let e1 = Felt::new(rand_value());
330        let e2 = Felt::new(rand_value());
331        let e3 = Felt::new(rand_value());
332        let e4 = Felt::new(rand_value());
333
334        let d1 = RpoDigest([e1, e2, e3, e4]);
335
336        let mut bytes = vec![];
337        d1.write_into(&mut bytes);
338        assert_eq!(DIGEST_BYTES, bytes.len());
339
340        let mut reader = SliceReader::new(&bytes);
341        let d2 = RpoDigest::read_from(&mut reader).unwrap();
342
343        assert_eq!(d1, d2);
344    }
345
346    #[test]
347    fn digest_encoding() {
348        let digest = RpoDigest([
349            Felt::new(rand_value()),
350            Felt::new(rand_value()),
351            Felt::new(rand_value()),
352            Felt::new(rand_value()),
353        ]);
354
355        let string: String = digest.into();
356        let round_trip: RpoDigest = string.try_into().expect("decoding failed");
357
358        assert_eq!(digest, round_trip);
359    }
360
361    #[test]
362    fn test_conversions() {
363        let digest = RpoDigest([
364            Felt::new(rand_value()),
365            Felt::new(rand_value()),
366            Felt::new(rand_value()),
367            Felt::new(rand_value()),
368        ]);
369
370        let v: [Felt; DIGEST_SIZE] = digest.into();
371        let v2: RpoDigest = v.into();
372        assert_eq!(digest, v2);
373
374        let v: [Felt; DIGEST_SIZE] = (&digest).into();
375        let v2: RpoDigest = v.into();
376        assert_eq!(digest, v2);
377
378        let v: [u64; DIGEST_SIZE] = digest.into();
379        let v2: RpoDigest = v.try_into().unwrap();
380        assert_eq!(digest, v2);
381
382        let v: [u64; DIGEST_SIZE] = (&digest).into();
383        let v2: RpoDigest = v.try_into().unwrap();
384        assert_eq!(digest, v2);
385
386        let v: [u8; DIGEST_BYTES] = digest.into();
387        let v2: RpoDigest = v.try_into().unwrap();
388        assert_eq!(digest, v2);
389
390        let v: [u8; DIGEST_BYTES] = (&digest).into();
391        let v2: RpoDigest = v.try_into().unwrap();
392        assert_eq!(digest, v2);
393
394        let v: String = digest.into();
395        let v2: RpoDigest = v.try_into().unwrap();
396        assert_eq!(digest, v2);
397
398        let v: String = (&digest).into();
399        let v2: RpoDigest = v.try_into().unwrap();
400        assert_eq!(digest, v2);
401
402        let v: [u8; DIGEST_BYTES] = digest.into();
403        let v2: RpoDigest = (&v).try_into().unwrap();
404        assert_eq!(digest, v2);
405
406        let v: [u8; DIGEST_BYTES] = (&digest).into();
407        let v2: RpoDigest = (&v).try_into().unwrap();
408        assert_eq!(digest, v2);
409    }
410}