Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new hash_memory proc #1519

Merged
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@
#### Stdlib

- Added `init_no_padding` procedure to `std::crypto::hashes::native` (#1313).
- [BREAKING] `native` module was renamed to the `rpo`, `hash_memory` procedure was renamed to the `hash_memory_words` (#1368).
- Added `hash_memory` procedure to `std::crypto::hashes::rpo` (#1368).

#### VM Internals

Expand Down
2 changes: 2 additions & 0 deletions processor/src/chiplets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ mod tests;
/// - columns 3-17: unused columns padded with ZERO
///
/// The following is a pictorial representation of the chiplet module:
/// ```text
/// +---+-------------------------------------------------------+-------------+
/// | 0 | | |-------------|
/// | . | Hash chiplet | Hash chiplet |-------------|
Expand Down Expand Up @@ -111,6 +112,7 @@ mod tests;
/// | . | . | . | . |---------------------------------------------------------|
/// | 1 | 1 | 1 | 1 |---------------------------------------------------------|
/// +---+---+---+---+---------------------------------------------------------+
/// ```
pub struct Chiplets {
/// Current clock cycle of the VM.
clk: RowIndex,
Expand Down
178 changes: 170 additions & 8 deletions stdlib/asm/crypto/hashes/rpo.masm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#!
#! Input: []
#! Ouptut: [PERM, PERM, PERM, ...]
#!
#! Cycles: 12
export.init_no_padding
padw padw padw
Expand All @@ -14,7 +15,11 @@ end
#!
#! Input: [C, B, A, ...]
#! Ouptut: [HASH, ...]
#! where: For the native RPO hasher HASH is B.
#!
#! Where :
#! - `A` is the capacity word that will be used by the hashing function.
#! - `B` is the hash output.
#!
#! Cycles: 9
export.squeeze_digest
# drop the first rate word (4 cycles)
Expand All @@ -32,12 +37,14 @@ end
#! This requires that `end_addr=start_addr + 2n + 1`, otherwise the procedure will enter an infinite
#! loop. `end_addr` is not inclusive.
bobbinth marked this conversation as resolved.
Show resolved Hide resolved
#!
#! Stack transition:
#! Input: [C, B, A, start_addr, end_addr, ...]
#! Output: [C', B', A', end_addr, end_addr ...]
#! Cycles: 4 + 3 * words, where `words` is the `start_addr - end_addr - 1`
#!
#! Where `A` is the capacity word that will be used by the hashing function, and `B'` the hash output.
#! Where :
#! - `A` is the capacity word that will be used by the hashing function.
#! - `B` is the hash output.
#!
#! Cycles: 4 + 3 * words, where `words` is the `start_addr - end_addr - 1`
export.absorb_double_words_from_memory
dup.13 dup.13 neq # (4 cycles )
while.true
Expand All @@ -50,13 +57,14 @@ end
#!
#! Requires `start_addr < end_addr`, `end_addr` is not inclusive.
#!
#! Stack transition:
#! Input: [start_addr, end_addr, ...]
#! Output: [H, ...]
#!
#! Cycles:
#! even words: 49 cycles + 3 * words
#! odd words: 61 cycles + 3 * words
export.hash_memory
#! - even words: 49 cycles + 3 * words
#! - odd words: 61 cycles + 3 * words
#! where `words` is the `start_addr - end_addr - 1`
export.hash_memory_words
# enforce `start_addr < end_addr`
dup.1 dup.1 u32assert2 u32gt assert

Expand Down Expand Up @@ -100,3 +108,157 @@ export.hash_memory
# drop start_addr/end_addr (4 cycles)
movup.4 drop movup.4 drop
end

#! Computes hash of Felt values starting at the specified memory address.
#!
#! This procedure divides the hashing process into two parts: hashing pairs of words using
#! `absorb_double_words_from_memory` procedure and hashing the remaining values using the `hperm`
#! instruction.
#!
#! Inputs: [ptr, num_elements]
#! Outputs: [HASH]
#!
#! Cycles:
#! - If number of elements divides by 8: 47 cycles + 3 * words
#! - Else: 180 cycles + 3 * words
#! where `words` is the number of quads of input values.
export.hash_memory
# move number of inputs to the top of the stack
swap
# => [num_elements, ptr]

# get the number of double words
u32divmod.8 swap
# => [num_elements/8, num_elements%8, ptr]

# get the end_addr for hash_memory_even procedure (end address for pairs of words)
mul.2 dup.2 add movup.2
# => [ptr, end_addr, num_elements%8]

# get the capacity element which is equal to num_elements%8
dup.2
# => [capacity, ptr, end_addr, num_elements%8]

# prepare hasher state for RPO permutation
push.0.0.0 padw padw
# => [C, B, A, ptr, end_addr, num_elements%8]

# hash every pair of words
exec.absorb_double_words_from_memory
# => [C', B', A', ptr', end_addr, num_elements%8] where ptr' = end_addr

# hash remaining input values if there are any left
# if num_elements%8 is ZERO and there are no elements to hash
dup.14 eq.0
if.true
# clean the stack
exec.squeeze_digest
swapw drop drop drop movdn.4
# => [B']
else
# load the remaining double word
mem_stream
# => [E, D, A', ptr'+2, end_addr, num_elements%8]

# clean the stack
movup.12 drop movup.12 drop
# => [E, D, A', num_elements%8]

# get the number of elements we need to drop
# notice that drop_counter could be any number from 1 to 7
push.8 movup.13 sub movdn.12
# => [E, D, A', drop_counter]

### 0th value ########################################################

# we need to drop first value anyway, since number of values is not divisible by 8
# push the padding 0 on to the stack and move it down to the 6th position
drop push.0 movdn.6
# => [e_2, e_1, e_0, d_3, d_2, d_1, 0, d_0, A', drop_counter]

### 1st value ########################################################

# prepare the second element of the E Word for cdrop instruction
push.0 swap
# => [e_2, 0, e_1, e_0, d_3, d_2, d_1, 0, d_0, A', drop_counter]

# push latch variable onto the stack; this will be the control for the cdrop instruction
push.0
# => [latch = 0, e_2, 0, e_1, e_0, d_3, d_2, d_1, 0, d_0, A', drop_counter]

# get the flag whether the drop counter is equal 1
dup.14 eq.1
# => [drop_counter == 1, latch = 0, e_2, 0, e_1, e_0, d_3, d_2, d_1, 0, d_0, A', drop_counter]

# update the latch: if drop_counter == 1, latch will become 1
or
# => [latch', e_2, 0, e_1, e_0, d_3, d_2, d_1, 0, d_0, A', drop_counter]

# save the latch value
dup movdn.14
# => [latch', e_2, 0, e_1, e_0, d_3, d_2, d_1, 0, d_0, A', latch', drop_counter]

# if latch == 1, drop 0; otherwise drop e_1
cdrop
# => [e_2_or_0, e_1, e_0, d_3, d_2, d_1, 0, d_0, A', latch', drop_counter]

# move the calculated value down the stack
movdn.6
# => [e_1, e_0, d_3, d_2, d_1, 0, e_2_or_0, d_0, A', latch', drop_counter]

### 2nd value ########################################################

# repeat the above process but now compare drop_counter to 2
push.0 swap
movup.13 dup.14 eq.2 or
dup movdn.14
cdrop movdn.6
# => [e_0, d_3, d_2, d_1, 0, e_2_or_0, e_1_or_0, d_0, A', latch', drop_counter]

### 3rd value ########################################################

# repeat the above process but now compare drop_counter to 3
push.0 swap
movup.13 dup.14 eq.3 or
dup movdn.14
cdrop movdn.6
# => [d_3, d_2, d_1, 0, e_2_or_0, e_1_or_0, e_0_or_0, d_0, A', latch', drop_counter]

### 4th value ########################################################

# repeat the above process but now compare drop_counter to 4
push.0 swap
movup.13 dup.14 eq.4 or
dup movdn.14
cdrop movdn.6
# => [d_2, d_1, 0, e_2_or_0, e_1_or_0, e_0_or_0, d_3_or_0, d_0, A', latch', drop_counter]

### 5th value ########################################################

# repeat the above process but now compare drop_counter to 5
push.0 swap
movup.13 dup.14 eq.5 or
dup movdn.14
cdrop movdn.6
# => [d_1, 0, e_2_or_0, e_1_or_0, e_0_or_0, d_3_or_0, d_2_or_0, d_0, A', latch', drop_counter]

### 6th value ########################################################

# repeat the above process but now compare drop_counter to 6
push.0 swap
movup.13 movup.14 eq.6 or
cdrop movdn.6
# => [0, e_2_or_0, e_1_or_0, e_0_or_0, d_3_or_0, d_2_or_0, d_1_or_0, d_0, A']
# or in other words
# => [C, B, A', ... ]
# notice that we don't need to check the d_0 value: entering the else branch means that
# we have number of elements not divisible by 8, so we will have at least one element to
# hash here (which turns out to be d_0)

hperm
# => [F, E, D]

exec.squeeze_digest
# => [E]
end
end
3 changes: 2 additions & 1 deletion stdlib/docs/crypto/hashes/rpo.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ Prepares the top of the stack with the hasher initial state.<br /><br />This pro
| ----------- | ------------- |
| squeeze_digest | Given the hasher state, returns the hash output.<br /><br />Input: [C, B, A, ...]<br />Ouptut: [HASH, ...]<br />where: For the native RPO hasher HASH is B.<br />Cycles: 9<br /> |
| absorb_double_words_from_memory | Hashes the memory `start_addr` to `end_addr` given an RPO state specified by 3 words.<br /><br />This requires that `end_addr=start_addr + 2n + 1`, otherwise the procedure will enter an infinite<br />loop. `end_addr` is not inclusive.<br /><br />Stack transition:<br />Input: [C, B, A, start_addr, end_addr, ...]<br />Output: [C', B', A', end_addr, end_addr ...]<br />Cycles: 4 + 3 * words, where `words` is the `start_addr - end_addr - 1`<br /><br />Where `A` is the capacity word that will be used by the hashing function, and `B'` the hash output.<br /> |
| hash_memory | Hashes the memory `start_addr` to `end_addr`, handles odd number of elements.<br /><br />Requires `start_addr < end_addr`, `end_addr` is not inclusive.<br /><br />Stack transition:<br />Input: [start_addr, end_addr, ...]<br />Output: [H, ...]<br />Cycles:<br />even words: 49 cycles + 3 * words<br />odd words: 61 cycles + 3 * words<br /> |
| hash_memory_words | Hashes the memory `start_addr` to `end_addr`, handles odd number of elements.<br /><br />Requires `start_addr < end_addr`, `end_addr` is not inclusive.<br /><br />Stack transition:<br />Input: [start_addr, end_addr, ...]<br />Output: [H, ...]<br />Cycles:<br />even words: 49 cycles + 3 * words<br />odd words: 61 cycles + 3 * words<br /> |
| hash_memory | Computes hash of Felt values starting at the specified memory address.<br /><br />This procedure divides the hashing process into two parts: hashing pairs of words using<br />`absorb_double_words_from_memory` procedure and hashing the remaining values using the `hperm`<br />instruction.<br /><br />Inputs: [ptr, num_elements]<br />Outputs: [HASH]<br />Cycles:<br />- If number of elements divides by 8: 47 cycles + 3 * words<br />- Else: 180 cycles + 3 * words<br /><br />Panics if number of inputs equals 0.<br /> |
Loading
Loading