Skip to content

Commit

Permalink
test: add test for hash(&[u8]) padding
Browse files Browse the repository at this point in the history
  • Loading branch information
Al-Kindi-0 committed Sep 12, 2024
1 parent cc65df3 commit 9e2eabb
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 13 deletions.
13 changes: 6 additions & 7 deletions src/hash/rescue/rpo/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use core::ops::Range;

use super::{
add_constants, add_constants_and_apply_inv_sbox, add_constants_and_apply_sbox, apply_inv_sbox,
apply_mds, apply_sbox, Digest, ElementHasher, Felt, FieldElement, Hasher, StarkField, ARK1,
ARK2, BINARY_CHUNK_SIZE, CAPACITY_RANGE, DIGEST_BYTES, DIGEST_RANGE, DIGEST_SIZE, INPUT1_RANGE,
INPUT2_RANGE, MDS, NUM_ROUNDS, RATE_RANGE, RATE_WIDTH, STATE_WIDTH, ZERO,
};
use core::ops::Range;

mod digest;
pub use digest::{RpoDigest, RpoDigestError};
Expand Down Expand Up @@ -98,16 +97,16 @@ impl Hasher for Rpo256 {
// every time the rate range is filled, a permutation is performed. if the final value of
// `rate_pos` is not zero, then the chunks count wasn't enough to fill the state range,
// and an additional permutation must be performed.
let mut current_element = 0_usize;
let mut current_chunk_idx = 0_usize;
// handle the case of an empty `bytes`
let last_element = if num_field_elem == 0 {
current_element
let last_chunk_idx = if num_field_elem == 0 {
current_chunk_idx
} else {
num_field_elem - 1
};
let rate_pos = bytes.chunks(BINARY_CHUNK_SIZE).fold(0, |rate_pos, chunk| {
// copy the chunk into the buffer
if current_element != last_element {
if current_chunk_idx != last_chunk_idx {
buf[..BINARY_CHUNK_SIZE].copy_from_slice(chunk);
} else {
// on the last iteration, we pad `buf` with a 1 followed by as many 0's as are
Expand All @@ -116,7 +115,7 @@ impl Hasher for Rpo256 {
buf[..chunk.len()].copy_from_slice(chunk);
buf[chunk.len()] = 1;
}
current_element += 1;
current_chunk_idx += 1;

// set the current rate element to the input. since we take at most 7 bytes, we are
// guaranteed that the inputs data will fit into a single field element.
Expand Down
26 changes: 25 additions & 1 deletion src/hash/rescue/rpo/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use super::{
super::{apply_inv_sbox, apply_sbox, ALPHA, INV_ALPHA},
Felt, FieldElement, Hasher, Rpo256, RpoDigest, StarkField, STATE_WIDTH, ZERO,
};
use crate::{Word, ONE};
use crate::{
hash::rescue::{BINARY_CHUNK_SIZE, CAPACITY_RANGE, RATE_WIDTH},
Word, ONE,
};

#[test]
fn test_sbox() {
Expand Down Expand Up @@ -126,6 +129,27 @@ fn hash_padding() {
assert_ne!(r1, r2);
}

#[test]
fn hash_padding_no_extra_permutation_call() {
use crate::hash::rescue::DIGEST_RANGE;

// Implementation
let num_bytes = BINARY_CHUNK_SIZE * RATE_WIDTH;
let mut buffer = vec![0_u8; num_bytes];
*buffer.last_mut().unwrap() = 97;
let r1 = Rpo256::hash(&buffer);

// Expected
let final_chunk = [0_u8, 0, 0, 0, 0, 0, 97, 1];
let mut state = [ZERO; STATE_WIDTH];
// padding when hashing bytes
state[CAPACITY_RANGE.start] = Felt::from(RATE_WIDTH as u8);
*state.last_mut().unwrap() = Felt::new(u64::from_le_bytes(final_chunk));
Rpo256::apply_permutation(&mut state);

assert_eq!(&r1[0..4], &state[DIGEST_RANGE]);
}

#[test]
fn hash_elements_padding() {
let e1 = [Felt::new(rand_value()); 2];
Expand Down
10 changes: 5 additions & 5 deletions src/hash/rescue/rpx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,16 @@ impl Hasher for Rpx256 {
// every time the rate range is filled, a permutation is performed. if the final value of
// `rate_pos` is not zero, then the chunks count wasn't enough to fill the state range,
// and an additional permutation must be performed.
let mut current_element = 0_usize;
let mut current_chunk_idx = 0_usize;
// handle the case of an empty `bytes`
let last_element = if num_field_elem == 0 {
current_element
let last_chunk_idx = if num_field_elem == 0 {
current_chunk_idx
} else {
num_field_elem - 1
};
let rate_pos = bytes.chunks(BINARY_CHUNK_SIZE).fold(0, |rate_pos, chunk| {
// copy the chunk into the buffer
if current_element != last_element {
if current_chunk_idx != last_chunk_idx {
buf[..BINARY_CHUNK_SIZE].copy_from_slice(chunk);
} else {
// on the last iteration, we pad `buf` with a 1 followed by as many 0's as are
Expand All @@ -122,7 +122,7 @@ impl Hasher for Rpx256 {
buf[..chunk.len()].copy_from_slice(chunk);
buf[chunk.len()] = 1;
}
current_element += 1;
current_chunk_idx += 1;

// set the current rate element to the input. since we take at most 7 bytes, we are
// guaranteed that the inputs data will fit into a single field element.
Expand Down

0 comments on commit 9e2eabb

Please sign in to comment.