Skip to content

Commit

Permalink
Merge pull request #123 from itzmeanjan/itzmeanjan/sha256
Browse files Browse the repository at this point in the history
Implement SHA256 in Miden Assembly
  • Loading branch information
bobbinth authored Mar 17, 2022
2 parents 6257b62 + fa33733 commit bc3b3dd
Show file tree
Hide file tree
Showing 6 changed files with 6,268 additions and 30 deletions.
3 changes: 2 additions & 1 deletion processor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ winter-utils = { package = "winter-utils", version = "0.3", default-features = f

[dev-dependencies]
assembly = { package = "miden-assembly", path = "../assembly", version = "0.2", default-features = false }
blake3 = "1.3.1"
logtest = { version = "2.0.0", default-features = false }
num-bigint = "0.4"
proptest = "1.0.0"
rand-utils = { package = "winter-rand-utils", version = "0.3" }
sha2 = "0.10.2"
blake3 = "1.3.1"
36 changes: 8 additions & 28 deletions processor/src/tests/stdlib/crypto/blake3.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use crate::{execute, Felt, FieldElement, ProgramInputs, Script, MIN_STACK_DEPTH};
use super::build_test;
use crate::{Felt, MIN_STACK_DEPTH};
use vm_core::utils::IntoBytes;

#[test]
fn blake3_2_to_1_hash() {
let script = compile(
"
let source = "
use.std::crypto::hashes::blake3
begin
exec.blake3::hash
end
",
);
";

// prepare random input byte array
let i_digest_0: [u8; 32] = rand_utils::rand_array::<Felt, 4>().into_bytes();
Expand Down Expand Up @@ -43,34 +42,15 @@ fn blake3_2_to_1_hash() {
}

// finally execute miden program on VM
let inputs = ProgramInputs::new(&i_words, &[], Vec::new()).unwrap();
let trace = execute(&script, &inputs).unwrap();
let last_state = trace.last_stack_state();

// first 8 elements of stack top holds blake3 digest, while remaining 8 elements
// are zeroed
let digest_on_stack = convert_to_stack(&digest_words);
assert_eq!(digest_on_stack, last_state);
let test = build_test!(source, &i_words);
// first 8 elements of stack top holds blake3 digest,
// while remaining 8 elements are zeroed
test.expect_stack(&digest_words);
}

// HELPER FUNCTIONS
// ================================================================================================

fn compile(source: &str) -> Script {
let assembler = assembly::Assembler::new();
assembler.compile_script(source).unwrap()
}

/// Takes an array of u64 values and builds a stack, perserving their order and converting them to
/// field elements.
fn convert_to_stack(values: &[u64]) -> [Felt; MIN_STACK_DEPTH] {
let mut result = [Felt::ZERO; MIN_STACK_DEPTH];
for (&value, result) in values.iter().zip(result.iter_mut()) {
*result = Felt::new(value);
}
result
}

/// Given a slice of four consecutive little endian bytes, interprets them as 32 -bit unsigned integer
fn from_le_bytes_to_words(le_bytes: &[u8]) -> u32 {
((le_bytes[3] as u32) << 24)
Expand Down
3 changes: 3 additions & 0 deletions processor/src/tests/stdlib/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
use super::build_test;

mod blake3;
mod sha256;
60 changes: 60 additions & 0 deletions processor/src/tests/stdlib/crypto/sha256.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use super::build_test;
use crate::{Felt, MIN_STACK_DEPTH};
use sha2::{Digest, Sha256};
use vm_core::utils::IntoBytes;

#[test]
fn sha256_2_to_1_hash() {
let source = "
use.std::crypto::hashes::sha256
begin
exec.sha256::hash
end";

// prepare random input byte array
let i_digest_0: [u8; 32] = rand_utils::rand_array::<Felt, 4>().into_bytes();
let i_digest_1: [u8; 32] = rand_utils::rand_array::<Felt, 4>().into_bytes();

// two digests concatenated to form input to sha256 2-to-1 hash function
let mut i_digest = [0u8; 64];
i_digest[..32].copy_from_slice(&i_digest_0);
i_digest[32..].copy_from_slice(&i_digest_1);

// allocate space on stack so that bytes can be converted to sha256 words
let mut i_words = [0u64; MIN_STACK_DEPTH];

// convert each of four consecutive big endian bytes (of input) to sha256 words
for i in 0..MIN_STACK_DEPTH {
i_words[i] = from_be_bytes_to_words(&i_digest[i * 4..(i + 1) * 4]) as u64;
}
i_words.reverse();

let mut hasher = Sha256::new();
hasher.update(&i_digest);
let digest = hasher.finalize();

// prepare digest in desired sha256 word form so that assertion writing becomes easier
let mut digest_words = [0u64; MIN_STACK_DEPTH >> 1];
// convert each of four consecutive big endian bytes (of digest) to sha256 words
for i in 0..(MIN_STACK_DEPTH >> 1) {
digest_words[i] = from_be_bytes_to_words(&digest[i * 4..(i + 1) * 4]) as u64;
}

// finally execute miden program on VM
let test = build_test!(source, &i_words);
// first 8 elements of stack top holds sha256 digest,
// while remaining 8 elements are zeroed
test.expect_stack(&digest_words);
}

// HELPER FUNCTIONS
// ================================================================================================

/// Takes four consecutive big endian bytes and interprets them as a SHA256 word
fn from_be_bytes_to_words(be_bytes: &[u8]) -> u32 {
((be_bytes[0] as u32) << 24)
| ((be_bytes[1] as u32) << 16)
| ((be_bytes[2] as u32) << 8)
| ((be_bytes[3] as u32) << 0)
}
Loading

0 comments on commit bc3b3dd

Please sign in to comment.