arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/hash/rescue/rpx/tests.rs
src/hash/rescue/rpx/tests.rsRaw
  1use alloc::{collections::BTreeSet, vec::Vec};
  2
  3use proptest::prelude::*;
  4use rand_utils::rand_value;
  5
  6use super::{Felt, Hasher, Rpx256, StarkField, ZERO};
  7use crate::{hash::rescue::RpxDigest, ONE};
  8
  9#[test]
 10fn hash_elements_vs_merge() {
 11    let elements = [Felt::new(rand_value()); 8];
 12
 13    let digests: [RpxDigest; 2] = [
 14        RpxDigest::new(elements[..4].try_into().unwrap()),
 15        RpxDigest::new(elements[4..].try_into().unwrap()),
 16    ];
 17
 18    let m_result = Rpx256::merge(&digests);
 19    let h_result = Rpx256::hash_elements(&elements);
 20    assert_eq!(m_result, h_result);
 21}
 22
 23#[test]
 24fn merge_vs_merge_in_domain() {
 25    let elements = [Felt::new(rand_value()); 8];
 26
 27    let digests: [RpxDigest; 2] = [
 28        RpxDigest::new(elements[..4].try_into().unwrap()),
 29        RpxDigest::new(elements[4..].try_into().unwrap()),
 30    ];
 31    let merge_result = Rpx256::merge(&digests);
 32
 33    // ----- merge with domain = 0 ----------------------------------------------------------------
 34
 35    // set domain to ZERO. This should not change the result.
 36    let domain = ZERO;
 37
 38    let merge_in_domain_result = Rpx256::merge_in_domain(&digests, domain);
 39    assert_eq!(merge_result, merge_in_domain_result);
 40
 41    // ----- merge with domain = 1 ----------------------------------------------------------------
 42
 43    // set domain to ONE. This should change the result.
 44    let domain = ONE;
 45
 46    let merge_in_domain_result = Rpx256::merge_in_domain(&digests, domain);
 47    assert_ne!(merge_result, merge_in_domain_result);
 48}
 49
 50#[test]
 51fn hash_elements_vs_merge_with_int() {
 52    let tmp = [Felt::new(rand_value()); 4];
 53    let seed = RpxDigest::new(tmp);
 54
 55    // ----- value fits into a field element ------------------------------------------------------
 56    let val: Felt = Felt::new(rand_value());
 57    let m_result = Rpx256::merge_with_int(seed, val.as_int());
 58
 59    let mut elements = seed.as_elements().to_vec();
 60    elements.push(val);
 61    let h_result = Rpx256::hash_elements(&elements);
 62
 63    assert_eq!(m_result, h_result);
 64
 65    // ----- value does not fit into a field element ----------------------------------------------
 66    let val = Felt::MODULUS + 2;
 67    let m_result = Rpx256::merge_with_int(seed, val);
 68
 69    let mut elements = seed.as_elements().to_vec();
 70    elements.push(Felt::new(val));
 71    elements.push(ONE);
 72    let h_result = Rpx256::hash_elements(&elements);
 73
 74    assert_eq!(m_result, h_result);
 75}
 76
 77#[test]
 78fn hash_padding() {
 79    // adding a zero bytes at the end of a byte string should result in a different hash
 80    let r1 = Rpx256::hash(&[1_u8, 2, 3]);
 81    let r2 = Rpx256::hash(&[1_u8, 2, 3, 0]);
 82    assert_ne!(r1, r2);
 83
 84    // same as above but with bigger inputs
 85    let r1 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6]);
 86    let r2 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 0]);
 87    assert_ne!(r1, r2);
 88
 89    // same as above but with input splitting over two elements
 90    let r1 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 7]);
 91    let r2 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 7, 0]);
 92    assert_ne!(r1, r2);
 93
 94    // same as above but with multiple zeros
 95    let r1 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 7, 0, 0]);
 96    let r2 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0]);
 97    assert_ne!(r1, r2);
 98}
 99
100#[test]
101fn hash_elements_padding() {
102    let e1 = [Felt::new(rand_value()); 2];
103    let e2 = [e1[0], e1[1], ZERO];
104
105    let r1 = Rpx256::hash_elements(&e1);
106    let r2 = Rpx256::hash_elements(&e2);
107    assert_ne!(r1, r2);
108}
109
110#[test]
111fn hash_elements() {
112    let elements = [
113        ZERO,
114        ONE,
115        Felt::new(2),
116        Felt::new(3),
117        Felt::new(4),
118        Felt::new(5),
119        Felt::new(6),
120        Felt::new(7),
121    ];
122
123    let digests: [RpxDigest; 2] = [
124        RpxDigest::new(elements[..4].try_into().unwrap()),
125        RpxDigest::new(elements[4..8].try_into().unwrap()),
126    ];
127
128    let m_result = Rpx256::merge(&digests);
129    let h_result = Rpx256::hash_elements(&elements);
130    assert_eq!(m_result, h_result);
131}
132
133#[test]
134fn hash_empty() {
135    let elements: Vec<Felt> = vec![];
136
137    let zero_digest = RpxDigest::default();
138    let h_result = Rpx256::hash_elements(&elements);
139    assert_eq!(zero_digest, h_result);
140}
141
142#[test]
143fn hash_empty_bytes() {
144    let bytes: Vec<u8> = vec![];
145
146    let zero_digest = RpxDigest::default();
147    let h_result = Rpx256::hash(&bytes);
148    assert_eq!(zero_digest, h_result);
149}
150
151#[test]
152fn sponge_bytes_with_remainder_length_wont_panic() {
153    // this test targets to assert that no panic will happen with the edge case of having an inputs
154    // with length that is not divisible by the used binary chunk size. 113 is a non-negligible
155    // input length that is prime; hence guaranteed to not be divisible by any choice of chunk
156    // size.
157    //
158    // this is a preliminary test to the fuzzy-stress of proptest.
159    Rpx256::hash(&[0; 113]);
160}
161
162#[test]
163fn sponge_collision_for_wrapped_field_element() {
164    let a = Rpx256::hash(&[0; 8]);
165    let b = Rpx256::hash(&Felt::MODULUS.to_le_bytes());
166    assert_ne!(a, b);
167}
168
169#[test]
170fn sponge_zeroes_collision() {
171    let mut zeroes = Vec::with_capacity(255);
172    let mut set = BTreeSet::new();
173    (0..255).for_each(|_| {
174        let hash = Rpx256::hash(&zeroes);
175        zeroes.push(0);
176        // panic if a collision was found
177        assert!(set.insert(hash));
178    });
179}
180
181proptest! {
182    #[test]
183    fn rpo256_wont_panic_with_arbitrary_input(ref bytes in any::<Vec<u8>>()) {
184        Rpx256::hash(bytes);
185    }
186}