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

chore: move sha2 functions into the hash module #5768

Merged
merged 7 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
13 changes: 4 additions & 9 deletions noir_stdlib/src/hash/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ mod poseidon;
mod mimc;
mod poseidon2;
mod keccak;
mod sha256;
mod sha512;

use crate::default::Default;
use crate::uint128::U128;
use crate::sha256::{digest, sha256_var};
use crate::collections::vec::Vec;
use crate::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul, multi_scalar_mul_slice};
use crate::meta::derive_via;

#[foreign(sha256)]
// docs:start:sha256
pub fn sha256<let N: u32>(input: [u8; N]) -> [u8; 32]
// docs:end:sha256
{}
// Kept for backwards compatibility
use sha256::{sha256, sha256_compression, sha256_var};
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved

#[foreign(blake2s)]
// docs:start:blake2s
Expand Down Expand Up @@ -137,9 +135,6 @@ pub fn keccak256<let N: u32>(input: [u8; N], message_size: u32) -> [u8; 32]
#[foreign(poseidon2_permutation)]
pub fn poseidon2_permutation<let N: u32>(_input: [Field; N], _state_length: u32) -> [Field; N] {}

#[foreign(sha256_compression)]
pub fn sha256_compression(_input: [u32; 16], _state: [u32; 8]) -> [u32; 8] {}

// Generic hashing support.
// Partially ported and impacted by rust.

Expand Down
106 changes: 106 additions & 0 deletions noir_stdlib/src/hash/sha256.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Implementation of SHA-256 mapping a byte array of variable length to
// 32 bytes.

// Deprecated in favour of `sha256_var`
#[foreign(sha256)]
// docs:start:sha256
pub fn sha256<let N: u32>(input: [u8; N]) -> [u8; 32]
// docs:end:sha256
{}

#[foreign(sha256_compression)]
pub fn sha256_compression(_input: [u32; 16], _state: [u32; 8]) -> [u32; 8] {}

// SHA-256 hash function
#[no_predicates]
pub fn digest<let N: u32>(msg: [u8; N]) -> [u8; 32] {
sha256_var(msg, N as u64)
}

// Variable size SHA-256 hash
pub fn sha256_var<let N: u32>(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 i: u64 = 0; // Message byte pointer
for k in 0..N {
if k as u64 < 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;
}
}
}
// Pad the rest such that we have a [u32; 2] block at the end representing the length
// of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]).
msg_block[i] = 1 << 7;
i = i + 1;
// If i >= 57, there aren't enough bits in the current message block to accomplish this, so
// the 1 and 0s fill up the current block, which we then compress accordingly.
if i >= 57 {
// Not enough bits (64) to store length. Fill up with zeros.
if i < 64 {
for _i in 57..64 {
if i <= 63 {
msg_block[i] = 0;
i += 1;
}
}
}
h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h);

i = 0;
}

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).
if i < 56 {
msg_block[i] = 0;
i = i + 1;
} else if i < 64 {
for j in 0..8 {
msg_block[63 - j] = len_bytes[j];
}
i += 8;
}
}
hash_final_block(msg_block, h)
}

// Convert 64-byte array to array of 16 u32s
fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] {
let mut msg32: [u32; 16] = [0; 16];

for i in 0..16 {
let mut msg_field: Field = 0;
for j in 0..4 {
msg_field = msg_field * 256 + msg[64 - 4*(i + 1) + j] as Field;
}
msg32[15 - i] = msg_field as u32;
}

msg32
}

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
}
165 changes: 165 additions & 0 deletions noir_stdlib/src/hash/sha512.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Implementation of SHA-512 mapping a byte array of variable length to
// 64 bytes.
// Internal functions act on 64-bit unsigned integers for simplicity.
// Auxiliary mappings; names as in FIPS PUB 180-4
fn rotr64(a: u64, b: u8) -> u64 // 64-bit right rotation
{
// None of the bits overlap between `(a >> b)` and `(a << (64 - b))`
// Addition is then equivalent to OR, with fewer constraints.
(a >> b) + (a << (64 - b))
}

fn sha_ch(x: u64, y: u64, z: u64) -> u64 {
(x & y) ^ (!x & z)
}

fn sha_maj(x: u64, y: u64, z: u64) -> u64 {
(x & y) ^ (x & z) ^ (y & z)
}

fn sha_bigma0(x: u64) -> u64 {
rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39)
}

fn sha_bigma1(x: u64) -> u64 {
rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41)
}

fn sha_sigma0(x: u64) -> u64 {
rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7)
}

fn sha_sigma1(x: u64) -> u64 {
rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6)
}

fn sha_w(msg: [u64; 16]) -> [u64; 80] // Expanded message blocks
{
let mut w: [u64;80] = [0; 80];

for j in 0..16 {
w[j] = msg[j];
}

for j in 16..80 {
w[j] = crate::wrapping_add(
crate::wrapping_add(sha_sigma1(w[j-2]), w[j-7]),
crate::wrapping_add(sha_sigma0(w[j-15]), w[j-16]),
);
}
w
}

// SHA-512 compression function
#[no_predicates]
fn sha_c(msg: [u64; 16], hash: [u64; 8]) -> [u64; 8] {
// noir-fmt:ignore
let K: [u64; 80] = [4794697086780616226, 8158064640168781261, 13096744586834688815, 16840607885511220156, 4131703408338449720, 6480981068601479193, 10538285296894168987, 12329834152419229976, 15566598209576043074, 1334009975649890238, 2608012711638119052, 6128411473006802146, 8268148722764581231, 9286055187155687089, 11230858885718282805, 13951009754708518548, 16472876342353939154, 17275323862435702243, 1135362057144423861, 2597628984639134821, 3308224258029322869, 5365058923640841347, 6679025012923562964, 8573033837759648693, 10970295158949994411, 12119686244451234320, 12683024718118986047, 13788192230050041572, 14330467153632333762, 15395433587784984357, 489312712824947311, 1452737877330783856, 2861767655752347644, 3322285676063803686, 5560940570517711597, 5996557281743188959, 7280758554555802590, 8532644243296465576, 9350256976987008742, 10552545826968843579, 11727347734174303076, 12113106623233404929, 14000437183269869457, 14369950271660146224, 15101387698204529176, 15463397548674623760, 17586052441742319658, 1182934255886127544, 1847814050463011016, 2177327727835720531, 2830643537854262169, 3796741975233480872, 4115178125766777443, 5681478168544905931, 6601373596472566643, 7507060721942968483, 8399075790359081724, 8693463985226723168, 9568029438360202098, 10144078919501101548, 10430055236837252648, 11840083180663258601, 13761210420658862357, 14299343276471374635, 14566680578165727644, 15097957966210449927, 16922976911328602910, 17689382322260857208, 500013540394364858, 748580250866718886, 1242879168328830382, 1977374033974150939, 2944078676154940804, 3659926193048069267, 4368137639120453308, 4836135668995329356, 5532061633213252278, 6448918945643986474, 6902733635092675308, 7801388544844847127]; // first 64 bits of fractional parts of cube roots of first 80 primes
let mut out_h: [u64; 8] = hash;
let w = sha_w(msg);
for j in 0..80 {
let out1 = crate::wrapping_add(out_h[7], sha_bigma1(out_h[4]));
let out2 = crate::wrapping_add(out1, sha_ch(out_h[4], out_h[5], out_h[6]));
let t1 = crate::wrapping_add(crate::wrapping_add(out2, K[j]), w[j]);
let t2 = crate::wrapping_add(sha_bigma0(out_h[0]), sha_maj(out_h[0], out_h[1], out_h[2]));
out_h[7] = out_h[6];
out_h[6] = out_h[5];
out_h[5] = out_h[4];
out_h[4] = crate::wrapping_add(out_h[3] , t1);
out_h[3] = out_h[2];
out_h[2] = out_h[1];
out_h[1] = out_h[0];
out_h[0] = crate::wrapping_add(t1, t2);
}

out_h
}
// Convert 128-byte array to array of 16 u64s
fn msg_u8_to_u64(msg: [u8; 128]) -> [u64; 16] {
let mut msg64: [u64; 16] = [0; 16];

for i in 0..16 {
let mut msg_field: Field = 0;
for j in 0..8 {
msg_field = msg_field * 256 + msg[128 - 8*(i + 1) + j] as Field;
}
msg64[15 - i] = msg_field as u64;
}

msg64
}
// SHA-512 hash function
pub fn digest<let N: u32>(msg: [u8; N]) -> [u8; 64] {
let mut msg_block: [u8; 128] = [0; 128];
// noir-fmt:ignore
let mut h: [u64; 8] = [7640891576956012808, 13503953896175478587, 4354685564936845355, 11912009170470909681, 5840696475078001361, 11170449401992604703, 2270897969802886507, 6620516959819538809]; // Intermediate hash, starting with the canonical initial value
let mut c: [u64; 8] = [0; 8]; // Compression of current message block as sequence of u64
let mut out_h: [u8; 64] = [0; 64]; // Digest as sequence of bytes
let mut i: u64 = 0; // Message byte pointer
for k in 0..msg.len() {
// Populate msg_block
msg_block[i] = msg[k];
i = i + 1;
if i == 128 {
// Enough to hash block
c = sha_c(msg_u8_to_u64(msg_block), h);
for j in 0..8 {
h[j] = crate::wrapping_add(h[j], c[j]);
}

i = 0;
}
}
// Pad the rest such that we have a [u64; 2] block at the end representing the length
// of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]).
msg_block[i] = 1 << 7;
i += 1;
// If i >= 113, there aren't enough bits in the current message block to accomplish this, so
// the 1 and 0s fill up the current block, which we then compress accordingly.
if i >= 113 {
// Not enough bits (128) to store length. Fill up with zeros.
if i < 128 {
for _i in 113..128 {
if i <= 127 {
msg_block[i] = 0;
i += 1;
}
}
}
c = sha_c(msg_u8_to_u64(msg_block), h);
for j in 0..8 {
h[j] = crate::wrapping_add(h[j], c[j]);
}

i = 0;
}

let len = 8 * msg.len();
let len_bytes = (len as Field).to_le_bytes(16);
for _i in 0..128 {
// In any case, fill blocks up with zeros until the last 128 (i.e. until i = 112).
if i < 112 {
msg_block[i] = 0;
i += 1;
} else if i < 128 {
for j in 0..16 {
msg_block[127 - j] = len_bytes[j];
}
i += 16; // Done.
}
}
// Hash final padded block
c = sha_c(msg_u8_to_u64(msg_block), h);
for j in 0..8 {
h[j] = crate::wrapping_add(h[j], c[j]);
}
// Return final hash as byte array
for j in 0..8 {
let h_bytes = (h[7 - j] as Field).to_le_bytes(8);
for k in 0..8 {
out_h[63 - 8*j - k] = h_bytes[k];
}
}

out_h
}
Loading
Loading