#[inline]
fn ch(b: u32, c: u32, d: u32) -> u32 {
(b & c) | (!b & d)
}
#[inline]
fn parity(b: u32, c: u32, d: u32) -> u32 {
b ^ c ^ d
}
#[inline]
fn maj(b: u32, c: u32, d: u32) -> u32 {
(b & c) | (b & d) | (c & d)
}
pub fn sha1rnds4(abcd: [u32; 4], msg: [u32; 4], func: u32) -> [u32; 4] {
let (k, f): (u32, fn(u32, u32, u32) -> u32) = match func & 3 {
0 => (0x5A82_7999, ch),
1 => (0x6ED9_EBA1, parity),
2 => (0x8F1B_BCDC, maj),
_ => (0xCA62_C1D6, parity),
};
let mut a = abcd[3];
let mut b = abcd[2];
let mut c = abcd[1];
let mut d = abcd[0];
let w = [msg[3], msg[2], msg[1], msg[0]];
let mut e = 0u32;
for &wi in &w {
let t = f(b, c, d)
.wrapping_add(a.rotate_left(5))
.wrapping_add(wi)
.wrapping_add(k)
.wrapping_add(e);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = t;
}
[d, c, b, a]
}
pub fn sha1msg1(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
[
b[2] ^ a[0], b[3] ^ a[1], a[0] ^ a[2], a[1] ^ a[3], ]
}
pub fn sha1msg2(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
let w16 = (a[3] ^ b[2]).rotate_left(1); let w17 = (a[2] ^ b[1]).rotate_left(1); let w18 = (a[1] ^ b[0]).rotate_left(1); let w19 = (a[0] ^ w16).rotate_left(1); [w19, w18, w17, w16]
}
pub fn sha1nexte(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
[b[0], b[1], b[2], b[3].wrapping_add(a[3].rotate_left(30))]
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(target_arch = "x86_64")]
fn hw_available() -> bool {
std::is_x86_feature_detected!("sha")
&& std::is_x86_feature_detected!("ssse3")
&& std::is_x86_feature_detected!("sse4.1")
}
#[cfg(target_arch = "x86_64")]
#[test]
fn software_sha_ops_equal_the_hardware_intrinsics() {
if !hw_available() {
eprintln!("skipping: no SHA-NI on this CPU");
return;
}
use core::arch::x86_64::*;
#[target_feature(enable = "sha,sse2,ssse3,sse4.1")]
unsafe fn check(a: [u32; 4], b: [u32; 4]) {
let va = _mm_loadu_si128(a.as_ptr() as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr() as *const __m128i);
let store = |v: __m128i| {
let mut o = [0u32; 4];
_mm_storeu_si128(o.as_mut_ptr() as *mut __m128i, v);
o
};
assert_eq!(store(_mm_sha1msg1_epu32(va, vb)), sha1msg1(a, b), "sha1msg1");
assert_eq!(store(_mm_sha1msg2_epu32(va, vb)), sha1msg2(a, b), "sha1msg2");
assert_eq!(store(_mm_sha1nexte_epu32(va, vb)), sha1nexte(a, b), "sha1nexte");
assert_eq!(store(_mm_sha1rnds4_epu32(va, vb, 0)), sha1rnds4(a, b, 0), "rnds4 f0");
assert_eq!(store(_mm_sha1rnds4_epu32(va, vb, 1)), sha1rnds4(a, b, 1), "rnds4 f1");
assert_eq!(store(_mm_sha1rnds4_epu32(va, vb, 2)), sha1rnds4(a, b, 2), "rnds4 f2");
assert_eq!(store(_mm_sha1rnds4_epu32(va, vb, 3)), sha1rnds4(a, b, 3), "rnds4 f3");
}
let mut s: u64 = 0x0123_4567_89ab_cdef;
let mut next = || {
s = s.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
(s >> 32) as u32
};
for _ in 0..20_000 {
let a = [next(), next(), next(), next()];
let b = [next(), next(), next(), next()];
unsafe { check(a, b) };
}
}
}