diff --git a/.gitignore b/.gitignore index 9de1de063..8c754c8f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ target/ */target/ */*/target/ -Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 8eb15389d..fded5476d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "blake2", "gost94", "groestl", + "jh", "md2", "md4", "md5", @@ -10,6 +11,7 @@ members = [ "sha1", "sha2", "sha3", + "skein", "streebog", "whirlpool", ] diff --git a/jh/Cargo.toml b/jh/Cargo.toml new file mode 100644 index 000000000..4bdf18757 --- /dev/null +++ b/jh/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "jh" +version = "0.2.1" +authors = ["RustCryptoDevelopers", "Kaz Wesley "] +license = "MIT/Apache-2.0" +description = "Portable JH with optimizations for x86-64" +documentation = "https://docs.rs/jh" +keywords = ["crypto", "jh", "hash", "digest"] +categories = ["cryptography", "no-std"] +repository = "https://github.com/RustCrypto/hashes" + +[dependencies] +block-buffer = "0.7" +digest = "0.8" +hex-literal = "0.1" + +[dev-dependencies] +digest = { version = "0.8", features = ["dev"] } + +[features] +default = ["std"] +std = [] +avx2 = [] +simd = ["avx2"] diff --git a/jh/benches/jh.rs b/jh/benches/jh.rs new file mode 100644 index 000000000..cc40e2319 --- /dev/null +++ b/jh/benches/jh.rs @@ -0,0 +1,7 @@ +#![no_std] +#![feature(test)] +#[macro_use] +extern crate digest; +extern crate jh; + +bench!(jh::Jh256); diff --git a/jh/src/compressor.rs b/jh/src/compressor.rs new file mode 100644 index 000000000..79d9488ef --- /dev/null +++ b/jh/src/compressor.rs @@ -0,0 +1,461 @@ +#![allow(non_upper_case_globals)] + +use core::mem; +use core::ops::{BitAnd, BitOr, BitXor, BitXorAssign}; +use core::ptr; + +const E8_BITSLICE_ROUNDCONSTANT: [[u8; 32]; 42] = [ + hex!("72d5dea2df15f8677b84150ab723155781abd6904d5a87f64e9f4fc5c3d12b40"), + hex!("ea983ae05c45fa9c03c5d29966b2999a660296b4f2bb538ab556141a88dba231"), + hex!("03a35a5c9a190edb403fb20a87c144101c051980849e951d6f33ebad5ee7cddc"), + hex!("10ba139202bf6b41dc786515f7bb27d00a2c813937aa78503f1abfd2410091d3"), + hex!("422d5a0df6cc7e90dd629f9c92c097ce185ca70bc72b44acd1df65d663c6fc23"), + hex!("976e6c039ee0b81a2105457e446ceca8eef103bb5d8e61fafd9697b294838197"), + hex!("4a8e8537db03302f2a678d2dfb9f6a958afe7381f8b8696c8ac77246c07f4214"), + hex!("c5f4158fbdc75ec475446fa78f11bb8052de75b7aee488bc82b8001e98a6a3f4"), + hex!("8ef48f33a9a36315aa5f5624d5b7f989b6f1ed207c5ae0fd36cae95a06422c36"), + hex!("ce2935434efe983d533af974739a4ba7d0f51f596f4e81860e9dad81afd85a9f"), + hex!("a7050667ee34626a8b0b28be6eb9172747740726c680103fe0a07e6fc67e487b"), + hex!("0d550aa54af8a4c091e3e79f978ef19e8676728150608dd47e9e5a41f3e5b062"), + hex!("fc9f1fec4054207ae3e41a00cef4c9844fd794f59dfa95d8552e7e1124c354a5"), + hex!("5bdf7228bdfe6e2878f57fe20fa5c4b205897cefee49d32e447e9385eb28597f"), + hex!("705f6937b324314a5e8628f11dd6e465c71b770451b920e774fe43e823d4878a"), + hex!("7d29e8a3927694f2ddcb7a099b30d9c11d1b30fb5bdc1be0da24494ff29c82bf"), + hex!("a4e7ba31b470bfff0d324405def8bc483baefc3253bbd339459fc3c1e0298ba0"), + hex!("e5c905fdf7ae090f947034124290f134a271b701e344ed95e93b8e364f2f984a"), + hex!("88401d63a06cf61547c1444b8752afff7ebb4af1e20ac6304670b6c5cc6e8ce6"), + hex!("a4d5a456bd4fca00da9d844bc83e18ae7357ce453064d1ade8a6ce68145c2567"), + hex!("a3da8cf2cb0ee11633e906589a94999a1f60b220c26f847bd1ceac7fa0d18518"), + hex!("32595ba18ddd19d3509a1cc0aaa5b4469f3d6367e4046bbaf6ca19ab0b56ee7e"), + hex!("1fb179eaa9282174e9bdf7353b3651ee1d57ac5a7550d3763a46c2fea37d7001"), + hex!("f735c1af98a4d84278edec209e6b677941836315ea3adba8fac33b4d32832c83"), + hex!("a7403b1f1c2747f35940f034b72d769ae73e4e6cd2214ffdb8fd8d39dc5759ef"), + hex!("8d9b0c492b49ebda5ba2d74968f3700d7d3baed07a8d5584f5a5e9f0e4f88e65"), + hex!("a0b8a2f436103b530ca8079e753eec5a9168949256e8884f5bb05c55f8babc4c"), + hex!("e3bb3b99f387947b75daf4d6726b1c5d64aeac28dc34b36d6c34a550b828db71"), + hex!("f861e2f2108d512ae3db643359dd75fc1cacbcf143ce3fa267bbd13c02e843b0"), + hex!("330a5bca8829a1757f34194db416535c923b94c30e794d1e797475d7b6eeaf3f"), + hex!("eaa8d4f7be1a39215cf47e094c23275126a32453ba323cd244a3174a6da6d5ad"), + hex!("b51d3ea6aff2c90883593d98916b3c564cf87ca17286604d46e23ecc086ec7f6"), + hex!("2f9833b3b1bc765e2bd666a5efc4e62a06f4b6e8bec1d43674ee8215bcef2163"), + hex!("fdc14e0df453c969a77d5ac4065858267ec1141606e0fa167e90af3d28639d3f"), + hex!("d2c9f2e3009bd20c5faace30b7d40c30742a5116f2e032980deb30d8e3cef89a"), + hex!("4bc59e7bb5f17992ff51e66e048668d39b234d57e6966731cce6a6f3170a7505"), + hex!("b17681d913326cce3c175284f805a262f42bcbb378471547ff46548223936a48"), + hex!("38df58074e5e6565f2fc7c89fc86508e31702e44d00bca86f04009a23078474e"), + hex!("65a0ee39d1f73883f75ee937e42c3abd2197b2260113f86fa344edd1ef9fdee7"), + hex!("8ba0df15762592d93c85f7f612dc42bed8a7ec7cab27b07e538d7ddaaa3ea8de"), + hex!("aa25ce93bd0269d85af643fd1a7308f9c05fefda174a19a5974d66334cfd216a"), + hex!("35b49831db411570ea1e0fbbedcd549b9ad063a151974072f6759dbf91476fe2"), +]; + +#[cfg_attr(rustfmt, rustfmt::skip)] +macro_rules! unroll7 { + ($j:ident, $body:block) => { + { const $j: usize = 0; $body } + { const $j: usize = 1; $body } + { const $j: usize = 2; $body } + { const $j: usize = 3; $body } + { const $j: usize = 4; $body } + { const $j: usize = 5; $body } + { const $j: usize = 6; $body } + }; +} + +#[cfg(not(all(feature = "simd", target_feature = "sse2")))] +mod generic { + use super::*; + #[repr(C)] + #[derive(Copy, Clone)] + pub struct U128(u64, u64); + impl U128 { + #[inline(always)] + pub fn const_ff() -> Self { + U128(0xffffffffffffffff, 0xffffffffffffffff) + } + #[inline(always)] + pub fn andnot(self, rhs: Self) -> Self { + U128(!self.0 & rhs.0, !self.1 & rhs.1) + } + #[inline(always)] + fn swap(self, l: u64, r: u64, i: u32) -> Self { + U128( + ((self.0 & l) >> i) | ((self.0 & r) << i), + ((self.1 & l) >> i) | ((self.1 & r) << i), + ) + } + #[inline(always)] + pub fn swap1(self) -> Self { + self.swap(0xaaaaaaaaaaaaaaaa, 0x5555555555555555, 1) + } + #[inline(always)] + pub fn swap2(self) -> Self { + self.swap(0xcccccccccccccccc, 0x3333333333333333, 2) + } + #[inline(always)] + pub fn swap4(self) -> Self { + self.swap(0xf0f0f0f0f0f0f0f0, 0x0f0f0f0f0f0f0f0f, 4) + } + #[inline(always)] + pub fn swap8(self) -> Self { + self.swap(0xff00ff00ff00ff00, 0x00ff00ff00ff00ff, 8) + } + #[inline(always)] + pub fn swap16(self) -> Self { + self.swap(0xffff0000ffff0000, 0x0000ffff0000ffff, 16) + } + #[inline(always)] + pub fn swap32(self) -> Self { + self.swap(0xffffffff00000000, 0x00000000ffffffff, 32) + } + #[inline(always)] + pub fn swap64(self) -> Self { + U128(self.1, self.0) + } + } + impl BitXor for U128 { + type Output = U128; + #[inline(always)] + fn bitxor(self, rhs: Self) -> Self::Output { + U128(self.0 ^ rhs.0, self.1 ^ rhs.1) + } + } + impl BitOr for U128 { + type Output = Self; + #[inline(always)] + fn bitor(self, rhs: Self) -> Self::Output { + U128(self.0 | rhs.0, self.1 | rhs.1) + } + } + impl BitAnd for U128 { + type Output = Self; + #[inline(always)] + fn bitand(self, rhs: Self) -> Self::Output { + U128(self.0 & rhs.0, self.1 & rhs.1) + } + } + impl BitXorAssign for U128 { + #[inline(always)] + fn bitxor_assign(&mut self, rhs: Self) { + *self = *self ^ rhs; + } + } +} +#[cfg(not(all(feature = "simd", target_feature = "sse2")))] +use self::generic::*; + +#[cfg(all(feature = "simd", target_feature = "sse2"))] +mod sse2 { + use super::*; + #[cfg(target_arch = "x86")] + use core::arch::x86::*; + #[cfg(target_arch = "x86_64")] + use core::arch::x86_64::*; + #[repr(transparent)] + #[derive(Copy, Clone)] + pub struct U128(__m128i); + impl U128 { + #[inline(always)] + fn const8(k: u8) -> Self { + U128(unsafe { _mm_set1_epi8(k as i8) }) + } + #[cfg(not(all(feature = "avx2", target_feature = "avx2")))] + #[inline(always)] + pub fn const_ff() -> Self { + Self::const8(0xff) + } + #[cfg(not(all(feature = "avx2", target_feature = "avx2")))] + #[inline(always)] + pub fn andnot(self, rhs: Self) -> Self { + U128(unsafe { _mm_andnot_si128(self.0, rhs.0) }) + } + #[inline(always)] + pub fn swap1(self) -> Self { + U128(unsafe { + _mm_or_si128( + _mm_srli_epi16((self & U128::const8(0xaa)).0, 1), + _mm_slli_epi16((self & U128::const8(0x55)).0, 1), + ) + }) + } + #[inline(always)] + pub fn swap2(self) -> Self { + U128(unsafe { + _mm_or_si128( + _mm_srli_epi16((self & U128::const8(0xcc)).0, 2), + _mm_slli_epi16((self & U128::const8(0x33)).0, 2), + ) + }) + } + #[inline(always)] + pub fn swap4(self) -> Self { + U128(unsafe { + _mm_or_si128( + _mm_srli_epi16((self & U128::const8(0xf0)).0, 4), + _mm_slli_epi16((self & U128::const8(0x0f)).0, 4), + ) + }) + } + #[cfg(target_feature = "ssse3")] + #[inline(always)] + pub fn swap8(self) -> Self { + U128(unsafe { + let k = _mm_set_epi64x(0x0e0f_0c0d_0a0b_0809, 0x0607_0405_0203_0001); + _mm_shuffle_epi8(self.0, k) + }) + } + #[cfg(not(target_feature = "ssse3"))] + #[inline(always)] + pub fn swap8(self) -> Self { + U128(unsafe { _mm_or_si128(_mm_slli_epi16(self.0, 8), _mm_srli_epi16(self.0, 8)) }) + } + #[cfg(target_feature = "ssse3")] + #[inline(always)] + pub fn swap16(self) -> Self { + U128(unsafe { + let k = _mm_set_epi64x(0x0d0c_0f0e_0908_0b0a, 0x0504_0706_0100_0302); + _mm_shuffle_epi8(self.0, k) + }) + } + #[cfg(not(target_feature = "ssse3"))] + #[inline(always)] + pub fn swap16(self) -> Self { + U128(unsafe { _mm_or_si128(_mm_slli_epi32(self.0, 16), _mm_srli_epi32(self.0, 16)) }) + } + #[inline(always)] + pub fn swap32(self) -> Self { + U128(unsafe { _mm_shuffle_epi32(self.0, 0b10110001) }) + } + #[inline(always)] + pub fn swap64(self) -> Self { + U128(unsafe { _mm_shuffle_epi32(self.0, 0b01001110) }) + } + #[cfg(all(feature = "avx2", target_feature = "avx2"))] + #[inline(always)] + pub fn from_raw(x: __m128i) -> Self { + U128(x) + } + #[cfg(all(feature = "avx2", target_feature = "avx2"))] + #[inline(always)] + pub fn raw(self) -> __m128i { + self.0 + } + } + impl BitXor for U128 { + type Output = U128; + #[inline(always)] + fn bitxor(self, rhs: Self) -> Self::Output { + U128(unsafe { _mm_xor_si128(self.0, rhs.0) }) + } + } + impl BitOr for U128 { + type Output = Self; + #[inline(always)] + fn bitor(self, rhs: Self) -> Self::Output { + U128(unsafe { _mm_or_si128(self.0, rhs.0) }) + } + } + impl BitAnd for U128 { + type Output = Self; + #[inline(always)] + fn bitand(self, rhs: Self) -> Self::Output { + U128(unsafe { _mm_and_si128(self.0, rhs.0) }) + } + } + impl BitXorAssign for U128 { + #[inline(always)] + fn bitxor_assign(&mut self, rhs: Self) { + *self = *self ^ rhs; + } + } +} +#[cfg(all(feature = "simd", target_feature = "sse2"))] +use self::sse2::*; + +#[cfg(not(all(feature = "avx2", target_feature = "avx2")))] +mod single_channel { + use super::*; + #[repr(C)] + #[derive(Copy, Clone)] + pub struct X2(U128, U128); + impl X2 { + #[inline(always)] + pub fn new(a: U128, b: U128) -> Self { + X2(a, b) + } + #[inline(always)] + pub fn const_ff() -> Self { + X2(U128::const_ff(), U128::const_ff()) + } + #[inline(always)] + pub fn andnot(self, rhs: Self) -> Self { + X2(self.0.andnot(rhs.0), self.1.andnot(rhs.1)) + } + #[inline(always)] + pub fn split(self) -> (U128, U128) { + (self.0, self.1) + } + } + impl BitXorAssign for X2 { + #[inline(always)] + fn bitxor_assign(&mut self, rhs: Self) { + self.0 = self.0 ^ rhs.0; + self.1 = self.1 ^ rhs.1; + } + } + impl BitOr for X2 { + type Output = Self; + #[inline(always)] + fn bitor(self, rhs: Self) -> Self::Output { + X2(self.0 | rhs.0, self.1 | rhs.1) + } + } + impl BitAnd for X2 { + type Output = Self; + #[inline(always)] + fn bitand(self, rhs: Self) -> Self::Output { + X2(self.0 & rhs.0, self.1 & rhs.1) + } + } +} +#[cfg(not(all(feature = "avx2", target_feature = "avx2")))] +use self::single_channel::X2; + +#[cfg(all(feature = "avx2", target_feature = "avx2"))] +mod double_channel { + use super::*; + #[cfg(target_arch = "x86")] + use core::arch::x86::*; + #[cfg(target_arch = "x86_64")] + use core::arch::x86_64::*; + #[repr(transparent)] + #[derive(Copy, Clone)] + pub struct X2(__m256i); + impl X2 { + #[inline(always)] + pub fn new(a: U128, b: U128) -> Self { + X2(unsafe { _mm256_inserti128_si256(_mm256_castsi128_si256(a.raw()), b.raw(), 1) }) + } + #[inline(always)] + pub fn const_ff() -> Self { + X2(unsafe { _mm256_set1_epi8(0xffu8 as i8) }) + } + #[inline(always)] + pub fn andnot(self, rhs: Self) -> Self { + X2(unsafe { _mm256_andnot_si256(self.0, rhs.0) }) + } + #[inline(always)] + pub fn split(self) -> (U128, U128) { + unsafe { + ( + U128::from_raw(_mm256_castsi256_si128(self.0)), + U128::from_raw(_mm256_extracti128_si256(self.0, 1)), + ) + } + } + } + impl BitXorAssign for X2 { + #[inline(always)] + fn bitxor_assign(&mut self, rhs: Self) { + self.0 = unsafe { _mm256_xor_si256(self.0, rhs.0) }; + } + } + impl BitOr for X2 { + type Output = Self; + #[inline(always)] + fn bitor(self, rhs: Self) -> Self::Output { + X2(unsafe { _mm256_or_si256(self.0, rhs.0) }) + } + } + impl BitAnd for X2 { + type Output = Self; + #[inline(always)] + fn bitand(self, rhs: Self) -> Self::Output { + X2(unsafe { _mm256_and_si256(self.0, rhs.0) }) + } + } +} +#[cfg(all(feature = "avx2", target_feature = "avx2"))] +use self::double_channel::X2; + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct X8(U128, U128, U128, U128, U128, U128, U128, U128); +impl X8 { + #[inline(always)] + fn unzip(self) -> (X2, X2, X2, X2) { + ( + X2::new(self.0, self.1), + X2::new(self.2, self.3), + X2::new(self.4, self.5), + X2::new(self.6, self.7), + ) + } + #[inline(always)] + fn zip((a, b, c, d): (X2, X2, X2, X2)) -> Self { + let (a, b, c, d) = (a.split(), b.split(), c.split(), d.split()); + X8(a.0, a.1, b.0, b.1, c.0, c.1, d.0, d.1) + } +} + +/// two Sboxes computed in parallel; each Sbox implements S0 and S1, selected by a constant bit +#[inline(always)] +unsafe fn ss(state: X8, mut k: X2) -> X8 { + let mut m = state.unzip(); + m.3 ^= X2::const_ff(); + m.0 ^= m.2.andnot(k); + k ^= m.0 & m.1; + m.0 ^= m.3 & m.2; + m.3 ^= m.1.andnot(m.2); + m.1 ^= m.0 & m.2; + m.2 ^= m.3.andnot(m.0); + m.0 ^= m.1 | m.3; + m.3 ^= m.1 & m.2; + m.2 ^= k; + m.1 ^= k & m.0; + X8::zip(m) +} + +#[inline(always)] +unsafe fn l(mut y: X8) -> X8 { + y.1 ^= y.2; + y.3 ^= y.4; + y.5 ^= y.6 ^ y.0; + y.7 ^= y.0; + y.0 ^= y.3; + y.2 ^= y.5; + y.4 ^= y.7 ^ y.1; + y.6 ^= y.1; + y +} + +pub unsafe fn f8(state: *mut X8, data: *const U128) { + let mut y = ptr::read_unaligned(state); + y.0 ^= ptr::read_unaligned(data); + y.1 ^= ptr::read_unaligned(data.offset(1)); + y.2 ^= ptr::read_unaligned(data.offset(2)); + y.3 ^= ptr::read_unaligned(data.offset(3)); + let roundconst: [X2; 42] = mem::transmute(E8_BITSLICE_ROUNDCONSTANT); + for rc in roundconst.chunks(7) { + unroll7!(j, { + y = ss(y, rc[j]); + y = l(y); + let f = match j { + 0 => U128::swap1, + 1 => U128::swap2, + 2 => U128::swap4, + 3 => U128::swap8, + 4 => U128::swap16, + 5 => U128::swap32, + 6 => U128::swap64, + _ => unreachable!(), + }; + y = X8(y.0, f(y.1), y.2, f(y.3), y.4, f(y.5), y.6, f(y.7)); + }); + } + y.4 ^= ptr::read_unaligned(data); + y.5 ^= ptr::read_unaligned(data.offset(1)); + y.6 ^= ptr::read_unaligned(data.offset(2)); + y.7 ^= ptr::read_unaligned(data.offset(3)); + ptr::write_unaligned(state, y); +} diff --git a/jh/src/consts.rs b/jh/src/consts.rs new file mode 100644 index 000000000..92107ccb6 --- /dev/null +++ b/jh/src/consts.rs @@ -0,0 +1,19 @@ +pub const JH224_H0: [u8; 128] = hex!(" + 2dfedd62f99a98acae7cacd619d634e7a4831005bc301216b86038c6c966149466d9899f2580706fce9ea31b1d9b1adc11e8325f7b366e10f994857f02fa06c1 + 1b4f1b5cd8c840b397f6a17f6e738099dcdf93a5adeaa3d3a431e8dec9539a6822b4a98aec86a1e4d574ac959ce56cf015960deab5ab2bbf9611dcf0dd64ea6e +"); + +pub const JH256_H0: [u8; 128] = hex!(" + eb98a3412c20d3eb92cdbe7b9cb245c11c93519160d4c7fa260082d67e508a03a4239e267726b945e0fb1a48d41a9477cdb5ab26026b177a56f024420fff2fa8 + 71a396897f2e4d751d144908f77de262277695f776248f9487d5b6574780296c5c5e272dac8e0d6c518450c657057a0f7be4d367702412ea89e3ab13d31cd769 +"); + +pub const JH384_H0: [u8; 128] = hex!(" + 481e3bc6d813398a6d3b5e894ade879b63faea68d480ad2e332ccb21480f826798aec84d9082b928d455ea304111424936f555b2924847ecc7250a93baf43ce1 + 569b7f8a27db454c9efcbd496397af0e589fc27d26aa80cd80c08b8c9deb2eda8a7981e8f8d5373af43967adddd17a71a9b4d3bda475d394976c3fba9842737f +"); + +pub const JH512_H0: [u8; 128] = hex!(" + 6fd14b963e00aa17636a2e057a15d5438a225e8d0c97ef0be9341259f2b3c361891da0c1536f801e2aa9056bea2b6d80588eccdb2075baa6a90f3a76baf83bf7 + 0169e60541e34a6946b58a8e2e6fe65a1047a7d0c1843c243b6e71b12d5ac199cf57f6ec9db1f856a706887c5716b156e3c2fcdfe68517fb545a4678cc8cdd4b +"); diff --git a/jh/src/lib.rs b/jh/src/lib.rs new file mode 100644 index 000000000..9af301379 --- /dev/null +++ b/jh/src/lib.rs @@ -0,0 +1,123 @@ +// copyright 2017 Kaz Wesley + +//! Optimized implementation of JH for x86-64 systems. + +#![cfg_attr(not(features = "std"), no_std)] + +pub extern crate digest; +#[macro_use] +extern crate hex_literal; +extern crate block_buffer; + +pub use digest::Digest; + +use block_buffer::byteorder::BigEndian; +use block_buffer::generic_array::GenericArray as BBGenericArray; +use block_buffer::BlockBuffer; +use compressor::f8; +use digest::generic_array::typenum::{Unsigned, U28, U32, U48, U64}; +use digest::generic_array::GenericArray as DGenericArray; + +mod compressor; +mod consts; + +#[derive(Clone)] +struct State([u8; 128]); + +impl State { + fn process_block(&mut self, block: &BBGenericArray) { + unsafe { + f8(core::mem::transmute(self), block.as_ptr() as *const _); + } + } +} + +impl core::fmt::Debug for State { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + f.debug_tuple("State").field(&"(array)").finish() + } +} + +macro_rules! define_hasher { + ($name:ident, $init:path, $OutputBytes:ident) => { + #[derive(Clone)] + pub struct $name { + state: State, + buffer: BlockBuffer, + datalen: usize, + } + + impl core::fmt::Debug for $name { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + f.debug_struct("Jh") + .field("state", &self.state) + .field("buffer", &"(BlockBuffer)") + .field("datalen", &self.datalen) + .finish() + } + } + + impl Default for $name { + fn default() -> Self { + Self { + state: State($init), + buffer: BlockBuffer::default(), + datalen: 0, + } + } + } + + impl digest::BlockInput for $name { + type BlockSize = U64; + } + + impl digest::Input for $name { + fn input>(&mut self, data: T) { + let data = data.as_ref(); + self.datalen += data.len(); + let state = &mut self.state; + self.buffer.input(data, |b| state.process_block(b)) + } + } + + impl digest::FixedOutput for $name { + type OutputSize = $OutputBytes; + + fn fixed_result(mut self) -> DGenericArray { + let state = &mut self.state; + let buffer = &mut self.buffer; + let len = self.datalen as u64 * 8; + if buffer.position() == 0 { + buffer.len64_padding::(len, |b| state.process_block(b)); + } else { + use block_buffer::block_padding::Iso7816; + state.process_block(buffer.pad_with::().unwrap()); + let mut last = BBGenericArray::default(); + last[56] = (len >> 56) as u8; + last[57] = (len >> 48) as u8; + last[58] = (len >> 40) as u8; + last[59] = (len >> 32) as u8; + last[60] = (len >> 24) as u8; + last[61] = (len >> 16) as u8; + last[62] = (len >> 8) as u8; + last[63] = len as u8; + state.process_block(&last); + } + let mut out = DGenericArray::default(); + out.copy_from_slice(&state.0[(128 - $OutputBytes::to_usize())..]); + out + } + } + + impl digest::Reset for $name { + fn reset(&mut self) { + *self = Self::default(); + } + } + }; +} + +define_hasher!(Jh224, consts::JH224_H0, U28); +define_hasher!(Jh256, consts::JH256_H0, U32); +define_hasher!(Jh384, consts::JH384_H0, U48); +define_hasher!(Jh512, consts::JH512_H0, U64); diff --git a/jh/tests/data/LongMsgKAT_224.blb b/jh/tests/data/LongMsgKAT_224.blb new file mode 100644 index 000000000..bbc5df73e Binary files /dev/null and b/jh/tests/data/LongMsgKAT_224.blb differ diff --git a/jh/tests/data/LongMsgKAT_256.blb b/jh/tests/data/LongMsgKAT_256.blb new file mode 100644 index 000000000..8c1d8fbde Binary files /dev/null and b/jh/tests/data/LongMsgKAT_256.blb differ diff --git a/jh/tests/data/LongMsgKAT_384.blb b/jh/tests/data/LongMsgKAT_384.blb new file mode 100644 index 000000000..155084e1b Binary files /dev/null and b/jh/tests/data/LongMsgKAT_384.blb differ diff --git a/jh/tests/data/LongMsgKAT_512.blb b/jh/tests/data/LongMsgKAT_512.blb new file mode 100644 index 000000000..87c9762d6 Binary files /dev/null and b/jh/tests/data/LongMsgKAT_512.blb differ diff --git a/jh/tests/data/ShortMsgKAT_224.blb b/jh/tests/data/ShortMsgKAT_224.blb new file mode 100644 index 000000000..ed491abc0 Binary files /dev/null and b/jh/tests/data/ShortMsgKAT_224.blb differ diff --git a/jh/tests/data/ShortMsgKAT_256.blb b/jh/tests/data/ShortMsgKAT_256.blb new file mode 100644 index 000000000..1b9155558 Binary files /dev/null and b/jh/tests/data/ShortMsgKAT_256.blb differ diff --git a/jh/tests/data/ShortMsgKAT_384.blb b/jh/tests/data/ShortMsgKAT_384.blb new file mode 100644 index 000000000..a2c8c154c Binary files /dev/null and b/jh/tests/data/ShortMsgKAT_384.blb differ diff --git a/jh/tests/data/ShortMsgKAT_512.blb b/jh/tests/data/ShortMsgKAT_512.blb new file mode 100644 index 000000000..35c31be92 Binary files /dev/null and b/jh/tests/data/ShortMsgKAT_512.blb differ diff --git a/jh/tests/lib.rs b/jh/tests/lib.rs new file mode 100644 index 000000000..4c7860e99 --- /dev/null +++ b/jh/tests/lib.rs @@ -0,0 +1,16 @@ +#![no_std] +#[macro_use] +extern crate digest; +extern crate jh; + +use digest::dev::digest_test; + +new_test!(long_224, "LongMsgKAT_224", jh::Jh224, digest_test); +new_test!(long_256, "LongMsgKAT_256", jh::Jh256, digest_test); +new_test!(long_384, "LongMsgKAT_384", jh::Jh384, digest_test); +new_test!(long_512, "LongMsgKAT_512", jh::Jh512, digest_test); + +new_test!(short_224, "ShortMsgKAT_224", jh::Jh224, digest_test); +new_test!(short_256, "ShortMsgKAT_256", jh::Jh256, digest_test); +new_test!(short_384, "ShortMsgKAT_384", jh::Jh384, digest_test); +new_test!(short_512, "ShortMsgKAT_512", jh::Jh512, digest_test); diff --git a/skein/Cargo.toml b/skein/Cargo.toml new file mode 100644 index 000000000..e54e67481 --- /dev/null +++ b/skein/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "skein" +version = "0.3.0" +authors = ["Kaz Wesley "] +license = "MIT/Apache-2.0" +description = "Skein hash functions" +repository = "https://github.com/RustCrypto/hashes" +keywords = ["crypto", "skein", "hash", "digest"] +categories = ["cryptography", "no-std"] + +[dependencies] +block-buffer = "0.7" +block-padding = "0.1.0" +digest = "0.8" +threefish = { git = "git://github.com/kazcw/RustCrypto-block-ciphers", branch = "threefish", version = "0.3.0" } + +[dev-dependencies] +digest = { version = "0.8", features = ["dev"] } diff --git a/skein/src/lib.rs b/skein/src/lib.rs new file mode 100644 index 000000000..04410b8a5 --- /dev/null +++ b/skein/src/lib.rs @@ -0,0 +1,224 @@ +// copyright 2017 Kaz Wesley + +#![no_std] + +extern crate block_buffer; +extern crate block_padding; +extern crate threefish; +pub extern crate digest; + +pub use digest::generic_array::GenericArray; +pub use digest::Digest; + +use block_buffer::BlockBuffer; +use block_buffer::byteorder::{ByteOrder, LE}; +use block_padding::ZeroPadding; +use digest::generic_array::typenum::{NonZero, PartialDiv, Unsigned, U128, U32, U64, U8}; +use digest::generic_array::ArrayLength; +use threefish::{BlockCipher, Threefish1024, Threefish256, Threefish512}; + +/// N word buffer. +#[derive(Copy, Clone)] +union Block +where + N: ArrayLength, + N: PartialDiv, + >::Output: ArrayLength, + N::ArrayType: Copy, + <>::Output as ArrayLength>::ArrayType: Copy, +{ + bytes: GenericArray, + words: GenericArray>::Output>, +} + +impl Block +where + N: ArrayLength, + N: PartialDiv, + >::Output: ArrayLength, + N::ArrayType: Copy, + <>::Output as ArrayLength>::ArrayType: Copy, +{ + fn bytes(&mut self) -> &[u8] { + self.as_byte_array().as_slice() + } + + fn as_byte_array(&self) -> &GenericArray { + unsafe { &self.bytes } + } + + fn as_byte_array_mut(&mut self) -> &mut GenericArray { + unsafe { &mut self.bytes } + } + + fn from_byte_array(block: &GenericArray) -> Self { + Block { bytes: *block } + } +} + +impl Default for Block +where + N: ArrayLength, + N: PartialDiv, + >::Output: ArrayLength, + N::ArrayType: Copy, + <>::Output as ArrayLength>::ArrayType: Copy, +{ + fn default() -> Self { + Block { words: GenericArray::default() } + } +} + +impl core::ops::BitXor> for Block +where + N: ArrayLength, + N: PartialDiv, + >::Output: ArrayLength, + N::ArrayType: Copy, + <>::Output as ArrayLength>::ArrayType: Copy, +{ + type Output = Block; + fn bitxor(mut self, rhs: Block) -> Self::Output { + // XOR is endian-agnostic + for (s, r) in unsafe { &mut self.words }.iter_mut().zip(unsafe { &rhs.words }) { + *s ^= *r; + } + self + } +} + +#[derive(Clone)] +struct State { + t: (u64, u64), + x: X, +} + +impl State { + fn new(t1: u64, x: X) -> Self { + let t = (0, t1); + State { t, x } + } +} + +impl core::fmt::Debug for State { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + f.debug_struct("State") + .field("t", &"(unknown)") + .field("x", &"(unknown)") + .finish() + } +} + +const VERSION: u64 = 1; +const ID_STRING_LE: u64 = 0x33414853; +const SCHEMA_VER: u64 = (VERSION << 32) | ID_STRING_LE; +const CFG_TREE_INFO_SEQUENTIAL: u64 = 0; +const T1_FLAG_FIRST: u64 = 1 << 62; +const T1_FLAG_FINAL: u64 = 1 << 63; +const T1_BLK_TYPE_CFG: u64 = 4 << 56; +const T1_BLK_TYPE_MSG: u64 = 48 << 56; +const T1_BLK_TYPE_OUT: u64 = 63 << 56; +const CFG_STR_LEN: usize = 4 * 8; + +macro_rules! define_hasher { + ($name:ident, $threefish:ident, $state_bytes:ty, $state_bits:expr) => { + #[derive(Clone)] + pub struct $name+NonZero+Default> { + state: State>, + buffer: BlockBuffer<$state_bytes>, + _output: core::marker::PhantomData> + } + + impl core::fmt::Debug for $name where N: Unsigned+ArrayLength+NonZero+Default { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + f.debug_struct("Skein") + .field("state", &self.state) + .field("buffer.position()", &self.buffer.position()) + .finish() + } + } + + impl $name where N: Unsigned+ArrayLength+NonZero+Default { + fn process_block(state: &mut State>, + block: &GenericArray, byte_count_add: usize) { + let block = Block::from_byte_array(block); + state.t.0 += byte_count_add as u64; + let fish = $threefish::with_tweak(state.x.as_byte_array(), state.t.0, state.t.1); + let mut x = block.clone(); + fish.encrypt_block(x.as_byte_array_mut()); + state.x = x ^ block; + state.t.1 &= !T1_FLAG_FIRST; + } + } + + impl Default for $name where N: Unsigned+ArrayLength+NonZero+Default { + fn default() -> Self { + // build and process config block + let mut state = State::new(T1_FLAG_FIRST | T1_BLK_TYPE_CFG | T1_FLAG_FINAL, Block::default()); + let mut cfg = GenericArray::::default(); + LE::write_u64(&mut cfg[..8], SCHEMA_VER); + LE::write_u64(&mut cfg[8..16], N::to_u64() * 8); + LE::write_u64(&mut cfg[16..24], CFG_TREE_INFO_SEQUENTIAL); + Self::process_block(&mut state, &cfg, CFG_STR_LEN); + + // The chaining vars ctx->X are now initialized for the given hashBitLen. + // Set up to process the data message portion of the hash (default) + state.t = Default::default(); + state.t.1 = T1_FLAG_FIRST | T1_BLK_TYPE_MSG; + Self { + state, + buffer: Default::default(), + _output: Default::default() + } + } + } + + impl digest::BlockInput for $name where N: Unsigned+ArrayLength+NonZero+Default { + type BlockSize = <$threefish as BlockCipher>::BlockSize; + } + + impl digest::Input for $name where N: Unsigned+ArrayLength+NonZero+Default { + fn input>(&mut self, data: T) { + let buffer = &mut self.buffer; + let state = &mut self.state; + buffer.input_lazy(data.as_ref(), |block| Self::process_block(state, block, $state_bits/8)); + } + } + + impl digest::FixedOutput for $name where N: Unsigned+ArrayLength+NonZero+Default { + type OutputSize = N; + + fn fixed_result(mut self) -> GenericArray { + self.state.t.1 |= T1_FLAG_FINAL; + let pos = self.buffer.position(); + let final_block = self.buffer.pad_with::().unwrap(); + Self::process_block(&mut self.state, final_block, pos); + + // run Threefish in "counter mode" to generate output + let mut output = GenericArray::default(); + for (i, chunk) in output.chunks_mut($state_bits / 8).enumerate() { + let mut ctr = State::new(T1_FLAG_FIRST | T1_BLK_TYPE_OUT | T1_FLAG_FINAL, self.state.x); + let mut b = GenericArray::::default(); + LE::write_u64(&mut b[..8], i as u64); + Self::process_block(&mut ctr, &b, 8); + let n = chunk.len(); + chunk.copy_from_slice(&ctr.x.bytes()[..n]); + } + output + } + } + + impl digest::Reset for $name where N: Unsigned+ArrayLength+NonZero+Default { + fn reset(&mut self) { + *self = Self::default(); + } + } + } +} + +#[cfg_attr(rustfmt, skip)] +define_hasher!(Skein256, Threefish256, U32, 256); +#[cfg_attr(rustfmt, skip)] +define_hasher!(Skein512, Threefish512, U64, 512); +#[cfg_attr(rustfmt, skip)] +define_hasher!(Skein1024, Threefish1024, U128, 1024); diff --git a/skein/tests/data/skein1024_32.blb b/skein/tests/data/skein1024_32.blb new file mode 100644 index 000000000..3b750ac6a Binary files /dev/null and b/skein/tests/data/skein1024_32.blb differ diff --git a/skein/tests/data/skein1024_64.blb b/skein/tests/data/skein1024_64.blb new file mode 100644 index 000000000..20b79238f Binary files /dev/null and b/skein/tests/data/skein1024_64.blb differ diff --git a/skein/tests/data/skein256_32.blb b/skein/tests/data/skein256_32.blb new file mode 100644 index 000000000..13a6f6b38 Binary files /dev/null and b/skein/tests/data/skein256_32.blb differ diff --git a/skein/tests/data/skein256_64.blb b/skein/tests/data/skein256_64.blb new file mode 100644 index 000000000..7f05f1268 Binary files /dev/null and b/skein/tests/data/skein256_64.blb differ diff --git a/skein/tests/data/skein512_32.blb b/skein/tests/data/skein512_32.blb new file mode 100644 index 000000000..972253349 Binary files /dev/null and b/skein/tests/data/skein512_32.blb differ diff --git a/skein/tests/data/skein512_64.blb b/skein/tests/data/skein512_64.blb new file mode 100644 index 000000000..58e7057ee Binary files /dev/null and b/skein/tests/data/skein512_64.blb differ diff --git a/skein/tests/lib.rs b/skein/tests/lib.rs new file mode 100644 index 000000000..c9c249ba3 --- /dev/null +++ b/skein/tests/lib.rs @@ -0,0 +1,14 @@ +#![no_std] +extern crate skein; +#[macro_use] +extern crate digest; + +use digest::dev::digest_test; +use digest::generic_array::typenum::{U32, U64}; + +new_test!(skein256_32, "skein256_32", skein::Skein256, digest_test); +new_test!(skein512_32, "skein512_32", skein::Skein512, digest_test); +new_test!(skein1024_32, "skein1024_32", skein::Skein1024, digest_test); +new_test!(skein256_64, "skein256_64", skein::Skein256, digest_test); +new_test!(skein512_64, "skein512_64", skein::Skein512, digest_test); +new_test!(skein1024_64, "skein1024_64", skein::Skein1024, digest_test);