#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
compile_error!("the sha_ni backend needs an x86 or x86_64 target");
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use super::{REVERSE, expand_rol2, spill};
use crate::Schedule;
use crate::block::rounds;
use crate::mem::{load_u8x16, load_u32x4, store_u32x4};
use crate::ubc_check::RecompressFrom;
#[inline]
#[target_feature(enable = "sse2,ssse3")]
fn msg<const I: usize>(block: &[u8; 64], swap: __m128i) -> __m128i {
const { assert!(I + 16 <= 64, "the load runs past the block") }
let raw = load_u8x16(block[I..I + 16].try_into().unwrap());
_mm_shuffle_epi8(raw, swap)
}
#[inline]
#[target_feature(enable = "sse2,ssse3")]
fn words<const T: usize>(m1: &Schedule, dm: &[u32; 80]) -> __m128i {
const { assert!(T + 4 <= 80, "the group runs past the schedule") }
let spilled = load_u32x4(m1.window::<T, 4>());
let diff = load_u32x4(dm[T..T + 4].try_into().unwrap());
_mm_xor_si128(spilled, _mm_shuffle_epi32(diff, REVERSE))
}
#[inline]
#[target_feature(enable = "sse2")]
fn load_abcd(state: &[u32; 5]) -> __m128i {
_mm_shuffle_epi32(load_u32x4(state.first_chunk().unwrap()), REVERSE)
}
#[inline]
#[target_feature(enable = "sse2")]
fn store_abcd(state: &mut [u32; 5], abcd: __m128i) {
store_u32x4(
state.first_chunk_mut().unwrap(),
_mm_shuffle_epi32(abcd, REVERSE),
);
}
#[inline]
#[target_feature(enable = "sha,sse2")]
fn expand_ni(a: __m128i, b: __m128i, c: __m128i, d: __m128i) -> __m128i {
_mm_sha1msg2_epu32(_mm_xor_si128(_mm_sha1msg1_epu32(a, b), c), d)
}
fn has_sha_ni() -> bool {
#[cfg(feature = "std")]
{
std::arch::is_x86_feature_detected!("sha")
&& std::arch::is_x86_feature_detected!("sse2")
&& std::arch::is_x86_feature_detected!("ssse3")
&& std::arch::is_x86_feature_detected!("sse4.1")
}
#[cfg(not(feature = "std"))]
{
cfg!(all(
target_feature = "sha",
target_feature = "sse2",
target_feature = "ssse3",
target_feature = "sse4.1"
))
}
}
fn has_avx() -> bool {
#[cfg(feature = "std")]
{
std::arch::is_x86_feature_detected!("avx")
}
#[cfg(not(feature = "std"))]
{
cfg!(target_feature = "avx")
}
}
#[derive(Clone, Copy)]
pub(crate) struct ShaNi {
avx: bool,
}
impl ShaNi {
pub(crate) fn detect() -> Option<Self> {
has_sha_ni().then(|| Self { avx: has_avx() })
}
#[inline]
pub(crate) fn compress_spill(
self,
state: &mut [u32; 5],
block: &[u8; 64],
w: &mut Schedule,
at_60: &mut [u32; 5],
at_64: &mut [u32; 5],
) {
if self.avx {
unsafe { compress_spill_avx(state, block, w, at_60, at_64) };
} else {
unsafe { compress_spill(state, block, w, at_60, at_64) };
}
}
#[inline]
pub(crate) fn recompress(
self,
step: RecompressFrom,
m1: &Schedule,
dm: &[u32; 80],
state: &[u32; 5],
chaining_out: &[u32; 5],
) -> bool {
unsafe { recompress(step, m1, dm, state, chaining_out) }
}
}
macro_rules! rounds {
($w:ident, $v:ident, $abcd:ident, $t:literal, $k:expr, $live:ident, $held:ident) => {{
spill::<$t>($w, $v[$t / 4]);
$live = _mm_sha1nexte_epu32($live, $v[$t / 4]);
$held = $abcd;
$abcd = _mm_sha1rnds4_epu32($abcd, $live, $k);
}};
($w:ident, $v:ident, $abcd:ident, $t:literal, $k:expr, $live:ident, $held:ident => $at:ident) => {{
spill::<$t>($w, $v[$t / 4]);
$live = _mm_sha1nexte_epu32($live, $v[$t / 4]);
store_u32x4((&mut $at[1..5]).try_into().unwrap(), $live);
store_u32x4($at.first_chunk_mut().unwrap(), $abcd);
$held = $abcd;
$abcd = _mm_sha1rnds4_epu32($abcd, $live, $k);
}};
}
macro_rules! rol2 {
($v:ident, $k:literal) => {
$v[$k] = expand_rol2($v[$k - 8], $v[$k - 7], $v[$k - 4], $v[$k - 2], $v[$k - 1])
};
}
macro_rules! compress_spill_fn {
($(#[$doc:meta])* $name:ident, $features:literal) => {
$(#[$doc])*
#[inline]
#[target_feature(enable = $features)]
fn $name(
state: &mut [u32; 5],
block: &[u8; 64],
w: &mut Schedule,
at_60: &mut [u32; 5],
at_64: &mut [u32; 5],
) {
let swap = _mm_set_epi64x(0x0001_0203_0405_0607, 0x0809_0A0B_0C0D_0E0F);
let mut abcd = load_abcd(state);
let abcd_in = abcd;
let e_in = _mm_set_epi32(state[4] as i32, 0, 0, 0);
let mut v = [_mm_setzero_si128(); 20];
v[0] = msg::<0>(block, swap);
v[1] = msg::<16>(block, swap);
v[2] = msg::<32>(block, swap);
v[3] = msg::<48>(block, swap);
spill::<0>(w, v[0]);
let mut e0 = _mm_add_epi32(e_in, v[0]);
let mut e1 = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, e0, 0);
v[4] = expand_ni(v[0], v[1], v[2], v[3]);
rounds!(w, v, abcd, 4, 0, e1, e0);
v[5] = expand_ni(v[1], v[2], v[3], v[4]);
rounds!(w, v, abcd, 8, 0, e0, e1);
v[6] = expand_ni(v[2], v[3], v[4], v[5]);
rounds!(w, v, abcd, 12, 0, e1, e0);
v[7] = expand_ni(v[3], v[4], v[5], v[6]);
rounds!(w, v, abcd, 16, 0, e0, e1);
rol2!(v, 8);
rounds!(w, v, abcd, 20, 1, e1, e0);
rol2!(v, 9);
rounds!(w, v, abcd, 24, 1, e0, e1);
rol2!(v, 10);
rounds!(w, v, abcd, 28, 1, e1, e0);
rol2!(v, 11);
rounds!(w, v, abcd, 32, 1, e0, e1);
rol2!(v, 12);
rounds!(w, v, abcd, 36, 1, e1, e0);
rol2!(v, 13);
rounds!(w, v, abcd, 40, 2, e0, e1);
rol2!(v, 14);
rounds!(w, v, abcd, 44, 2, e1, e0);
rol2!(v, 15);
rounds!(w, v, abcd, 48, 2, e0, e1);
rol2!(v, 16);
rounds!(w, v, abcd, 52, 2, e1, e0);
rol2!(v, 17);
rounds!(w, v, abcd, 56, 2, e0, e1);
rol2!(v, 18);
rounds!(w, v, abcd, 60, 3, e1, e0 => at_60);
rol2!(v, 19);
rounds!(w, v, abcd, 64, 3, e0, e1 => at_64);
rounds!(w, v, abcd, 68, 3, e1, e0);
rounds!(w, v, abcd, 72, 3, e0, e1);
rounds!(w, v, abcd, 76, 3, e1, e0);
e0 = _mm_sha1nexte_epu32(e0, e_in);
abcd = _mm_add_epi32(abcd, abcd_in);
store_abcd(state, abcd);
state[4] = _mm_extract_epi32(e0, 3) as u32;
}
};
}
compress_spill_fn!(
compress_spill,
"sha,sse2,ssse3,sse4.1"
);
compress_spill_fn!(
compress_spill_avx,
"sha,sse2,ssse3,sse4.1,avx"
);
#[inline]
#[target_feature(enable = "sha,sse2,ssse3,sse4.1")]
fn recompress(
step: RecompressFrom,
m1: &Schedule,
dm: &[u32; 80],
state: &[u32; 5],
chaining_out: &[u32; 5],
) -> bool {
let at = rounds::partner_boundary(step, m1, dm, state);
let mut abcd = load_abcd(&at);
let mut held: __m128i;
macro_rules! group {
(first $e:expr, $g:expr, $k:expr) => {{
let live = _mm_add_epi32($e, words::<{ 4 * $g }>(m1, dm));
held = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, live, $k);
}};
($g:expr, $k:expr) => {{
let live = _mm_sha1nexte_epu32(held, words::<{ 4 * $g }>(m1, dm));
held = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, live, $k);
}};
}
let e_at = _mm_set_epi32(at[4] as i32, 0, 0, 0);
match step {
RecompressFrom::Step58 => {
group!(first e_at, 15, 3);
group!(16, 3);
}
RecompressFrom::Step65 => group!(first e_at, 16, 3),
}
group!(17, 3);
group!(18, 3);
group!(19, 3);
let e_80 = _mm_sha1nexte_epu32(held, _mm_setzero_si128());
abcd = _mm_sub_epi32(load_abcd(chaining_out), abcd);
let e_in = _mm_sub_epi32(_mm_set_epi32(chaining_out[4] as i32, 0, 0, 0), e_80);
group!(first e_in, 0, 0);
group!(1, 0);
group!(2, 0);
group!(3, 0);
group!(4, 0);
group!(5, 1);
group!(6, 1);
group!(7, 1);
group!(8, 1);
group!(9, 1);
group!(10, 2);
group!(11, 2);
group!(12, 2);
group!(13, 2);
group!(14, 2);
if let RecompressFrom::Step65 = step {
group!(15, 3);
}
let mut reached = [0u32; 5];
store_abcd(&mut reached, abcd);
reached[4] = _mm_extract_epi32(_mm_sha1nexte_epu32(held, _mm_setzero_si128()), 3) as u32;
crate::block::xor(&reached, &at) == 0
}