Skip to content

Commit

Permalink
feat: add variable size sha256 (#4920)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves #4909 

## Summary\*
Thanks to the sha256compression opcode, we can create conditionally the
blocks to be hashed. As a result there is no additional cost to support
variable input size for sha256, except of course that we pay for the
full input len (even if we hash less).


## Additional Context



## Documentation\*

Check one:
- [ ] No documentation needed.
- [X] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
guipublic committed Apr 25, 2024
1 parent 53d145f commit dbfca58
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx';
## sha256

Given an array of bytes, returns the resulting sha256 hash.
Specify a message_size to hash only the first `message_size` bytes of the input.

#include_code sha256 noir_stdlib/src/hash.nr rust

example:
#include_code sha256_var test_programs/execution_success/sha256/src/main.nr rust

```rust
fn main() {
let x = [163, 117, 178, 149]; // some random bytes
let hash = std::hash::sha256(x);
let hash = std::sha256::sha256_var(x, 4);
}
```


<BlackBoxInfo />

## blake2s
Expand Down
1 change: 1 addition & 0 deletions noir_stdlib/src/hash.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod poseidon2;

use crate::default::Default;
use crate::uint128::U128;
use crate::sha256::{digest, sha256_var};

#[foreign(sha256)]
// docs:start:sha256
Expand Down
54 changes: 33 additions & 21 deletions noir_stdlib/src/sha256.nr
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,42 @@ fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] {
}
// SHA-256 hash function
pub fn digest<N>(msg: [u8; N]) -> [u8; 32] {
sha256_var(msg, N)
}

fn hash_final_block(msg_block: [u8; 64], mut state: [u32; 8]) -> [u8; 32] {
let mut out_h: [u8; 32] = [0; 32]; // Digest as sequence of bytes

// Hash final padded block
state = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), state);

// Return final hash as byte array
for j in 0..8 {
let h_bytes = (state[7 - j] as Field).to_le_bytes(4);
for k in 0..4 {
out_h[31 - 4*j - k] = h_bytes[k];
}
}

out_h
}

// Variable size SHA-256 hash
pub fn sha256_var<N>(msg: [u8; N], message_size: u64) -> [u8; 32] {
let mut msg_block: [u8; 64] = [0; 64];
let mut h: [u32; 8] = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]; // Intermediate hash, starting with the canonical initial value
let mut out_h: [u8; 32] = [0; 32]; // Digest as sequence of bytes
let mut i: u64 = 0; // Message byte pointer
for k in 0..N {
// Populate msg_block
msg_block[i] = msg[k];
i = i + 1;
if i == 64 {
// Enough to hash block
h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h);
if k < message_size {
// Populate msg_block
msg_block[i] = msg[k];
i = i + 1;
if i == 64 {
// Enough to hash block
h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h);

i = 0;
i = 0;
}
}
}
// Pad the rest such that we have a [u32; 2] block at the end representing the length
Expand All @@ -53,7 +76,7 @@ pub fn digest<N>(msg: [u8; N]) -> [u8; 32] {
i = 0;
}

let len = 8 * msg.len();
let len = 8 * message_size;
let len_bytes = (len as Field).to_le_bytes(8);
for _i in 0..64 {
// In any case, fill blocks up with zeros until the last 64 (i.e. until i = 56).
Expand All @@ -67,16 +90,5 @@ pub fn digest<N>(msg: [u8; N]) -> [u8; 32] {
i += 8;
}
}
// Hash final padded block
h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h);

// Return final hash as byte array
for j in 0..8 {
let h_bytes = (h[7 - j] as Field).to_le_bytes(4);
for k in 0..4 {
out_h[31 - 4*j - k] = h_bytes[k];
}
}

out_h
hash_final_block(msg_block, h)
}
4 changes: 3 additions & 1 deletion test_programs/execution_success/sha256/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use dep::std;
fn main(x: Field, result: [u8; 32]) {
// We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field
// The padding is taken care of by the program
let digest = std::hash::sha256([x as u8]);
// docs:start:sha256_var
let digest = std::hash::sha256_var([x as u8], 1);
// docs:end:sha256_var
assert(digest == result);
}

0 comments on commit dbfca58

Please sign in to comment.