#![allow(clippy::many_single_char_names)] use crate::consts::BLOCK_LEN; use core::convert::TryInto; #[inline(always)] fn shl(v: [u32; 4], o: u32) -> [u32; 4] { [v[0] >> o, v[1] >> o, v[2] >> o, v[3] >> o] } #[inline(always)] fn shr(v: [u32; 4], o: u32) -> [u32; 4] { [v[0] << o, v[1] << o, v[2] << o, v[3] << o] } #[inline(always)] fn or(a: [u32; 4], b: [u32; 4]) -> [u32; 4] { [a[0] | b[0], a[1] | b[1], a[2] | b[2], a[3] | b[3]] } #[inline(always)] fn xor(a: [u32; 4], b: [u32; 4]) -> [u32; 4] { [a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]] } #[inline(always)] fn add(a: [u32; 4], b: [u32; 4]) -> [u32; 4] { [ a[0].wrapping_add(b[0]), a[1].wrapping_add(b[1]), a[2].wrapping_add(b[2]), a[3].wrapping_add(b[3]), ] } fn sha256load(v2: [u32; 4], v3: [u32; 4]) -> [u32; 4] { [v3[3], v2[0], v2[1], v2[2]] } fn sha256swap(v0: [u32; 4]) -> [u32; 4] { [v0[2], v0[3], v0[0], v0[1]] } fn sha256msg1(v0: [u32; 4], v1: [u32; 4]) -> [u32; 4] { // sigma 0 on vectors #[inline] fn sigma0x4(x: [u32; 4]) -> [u32; 4] { let t1 = or(shl(x, 7), shr(x, 25)); let t2 = or(shl(x, 18), shr(x, 14)); let t3 = shl(x, 3); xor(xor(t1, t2), t3) } add(v0, sigma0x4(sha256load(v0, v1))) } fn sha256msg2(v4: [u32; 4], v3: [u32; 4]) -> [u32; 4] { macro_rules! sigma1 { ($a:expr) => { $a.rotate_right(17) ^ $a.rotate_right(19) ^ ($a >> 10) }; } let [x3, x2, x1, x0] = v4; let [w15, w14, _, _] = v3; let w16 = x0.wrapping_add(sigma1!(w14)); let w17 = x1.wrapping_add(sigma1!(w15)); let w18 = x2.wrapping_add(sigma1!(w16)); let w19 = x3.wrapping_add(sigma1!(w17)); [w19, w18, w17, w16] } fn sha256_digest_round_x2(cdgh: [u32; 4], abef: [u32; 4], wk: [u32; 4]) -> [u32; 4] { macro_rules! big_sigma0 { ($a:expr) => { ($a.rotate_right(2) ^ $a.rotate_right(13) ^ $a.rotate_right(22)) }; } macro_rules! big_sigma1 { ($a:expr) => { ($a.rotate_right(6) ^ $a.rotate_right(11) ^ $a.rotate_right(25)) }; } macro_rules! bool3ary_202 { ($a:expr, $b:expr, $c:expr) => { $c ^ ($a & ($b ^ $c)) }; } // Choose, MD5F, SHA1C macro_rules! bool3ary_232 { ($a:expr, $b:expr, $c:expr) => { ($a & $b) ^ ($a & $c) ^ ($b & $c) }; } // Majority, SHA1M let [_, _, wk1, wk0] = wk; let [a0, b0, e0, f0] = abef; let [c0, d0, g0, h0] = cdgh; // a round let x0 = big_sigma1!(e0) .wrapping_add(bool3ary_202!(e0, f0, g0)) .wrapping_add(wk0) .wrapping_add(h0); let y0 = big_sigma0!(a0).wrapping_add(bool3ary_232!(a0, b0, c0)); let (a1, b1, c1, d1, e1, f1, g1, h1) = ( x0.wrapping_add(y0), a0, b0, c0, x0.wrapping_add(d0), e0, f0, g0, ); // a round let x1 = big_sigma1!(e1) .wrapping_add(bool3ary_202!(e1, f1, g1)) .wrapping_add(wk1) .wrapping_add(h1); let y1 = big_sigma0!(a1).wrapping_add(bool3ary_232!(a1, b1, c1)); let (a2, b2, _, _, e2, f2, _, _) = ( x1.wrapping_add(y1), a1, b1, c1, x1.wrapping_add(d1), e1, f1, g1, ); [a2, b2, e2, f2] } fn schedule(v0: [u32; 4], v1: [u32; 4], v2: [u32; 4], v3: [u32; 4]) -> [u32; 4] { let t1 = sha256msg1(v0, v1); let t2 = sha256load(v2, v3); let t3 = add(t1, t2); sha256msg2(t3, v3) } macro_rules! rounds4 { ($abef:ident, $cdgh:ident, $rest:expr, $i:expr) => {{ let t1 = add($rest, crate::consts::K32X4[$i]); $cdgh = sha256_digest_round_x2($cdgh, $abef, t1); let t2 = sha256swap(t1); $abef = sha256_digest_round_x2($abef, $cdgh, t2); }}; } macro_rules! schedule_rounds4 { ( $abef:ident, $cdgh:ident, $w0:expr, $w1:expr, $w2:expr, $w3:expr, $w4:expr, $i: expr ) => {{ $w4 = schedule($w0, $w1, $w2, $w3); rounds4!($abef, $cdgh, $w4, $i); }}; } /// Process a block with the SHA-256 algorithm. fn sha256_digest_block_u32(state: &mut [u32; 8], block: &[u32; 16]) { let mut abef = [state[0], state[1], state[4], state[5]]; let mut cdgh = [state[2], state[3], state[6], state[7]]; // Rounds 0..64 let mut w0 = [block[3], block[2], block[1], block[0]]; let mut w1 = [block[7], block[6], block[5], block[4]]; let mut w2 = [block[11], block[10], block[9], block[8]]; let mut w3 = [block[15], block[14], block[13], block[12]]; let mut w4; rounds4!(abef, cdgh, w0, 0); rounds4!(abef, cdgh, w1, 1); rounds4!(abef, cdgh, w2, 2); rounds4!(abef, cdgh, w3, 3); schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 4); schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 5); schedule_rounds4!(abef, cdgh, w2, w3, w4, w0, w1, 6); schedule_rounds4!(abef, cdgh, w3, w4, w0, w1, w2, 7); schedule_rounds4!(abef, cdgh, w4, w0, w1, w2, w3, 8); schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 9); schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 10); schedule_rounds4!(abef, cdgh, w2, w3, w4, w0, w1, 11); schedule_rounds4!(abef, cdgh, w3, w4, w0, w1, w2, 12); schedule_rounds4!(abef, cdgh, w4, w0, w1, w2, w3, 13); schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 14); schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 15); let [a, b, e, f] = abef; let [c, d, g, h] = cdgh; state[0] = state[0].wrapping_add(a); state[1] = state[1].wrapping_add(b); state[2] = state[2].wrapping_add(c); state[3] = state[3].wrapping_add(d); state[4] = state[4].wrapping_add(e); state[5] = state[5].wrapping_add(f); state[6] = state[6].wrapping_add(g); state[7] = state[7].wrapping_add(h); } pub fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) { let mut block_u32 = [0u32; BLOCK_LEN]; // since LLVM can't properly use aliasing yet it will make // unnecessary state stores without this copy let mut state_cpy = *state; for block in blocks { for (o, chunk) in block_u32.iter_mut().zip(block.chunks_exact(4)) { *o = u32::from_be_bytes(chunk.try_into().unwrap()); } sha256_digest_block_u32(&mut state_cpy, &block_u32); } *state = state_cpy; }