diff --git a/Cargo.toml b/Cargo.toml index 8eb15389d..8bcda0b54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "blake", "blake2", "gost94", "groestl", diff --git a/blake/Cargo.toml b/blake/Cargo.toml new file mode 100644 index 000000000..04faf9b90 --- /dev/null +++ b/blake/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "blake" +version = "0.2.0" +authors = ["Kaz Wesley "] +license = "MIT/Apache-2.0" +description = "BLAKE hash functions" +repository = "https://github.com/RustCrypto/hashes" +keywords = ["crypto", "blake", "hash", "digest"] +categories = ["cryptography", "no-std"] + +[dependencies] +block-buffer = "0.7" +digest = "0.8" + +[dev-dependencies] +digest = { version = "0.8", features = ["dev"] } diff --git a/blake/src/consts.rs b/blake/src/consts.rs new file mode 100644 index 000000000..23e9f924b --- /dev/null +++ b/blake/src/consts.rs @@ -0,0 +1,59 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +pub const PADDING: &[u8; 129] = + b"\x80\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ + \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ + \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +pub const SIGMA: [[u8; 16]; 16] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], +]; + +pub const BLAKE256_U: [u32; 16] = [ + 0x243f_6a88, 0x85a3_08d3, 0x1319_8a2e, 0x0370_7344, + 0xa409_3822, 0x299f_31d0, 0x082e_fa98, 0xec4e_6c89, + 0x4528_21e6, 0x38d0_1377, 0xbe54_66cf, 0x34e9_0c6c, + 0xc0ac_29b7, 0xc97c_50dd, 0x3f84_d5b5, 0xb547_0917 +]; + +pub const BLAKE512_U: [u64; 16] = [ + 0x243f_6a88_85a3_08d3, 0x1319_8a2e_0370_7344, 0xa409_3822_299f_31d0, 0x082e_fa98_ec4e_6c89, + 0x4528_21e6_38d0_1377, 0xbe54_66cf_34e9_0c6c, 0xc0ac_29b7_c97c_50dd, 0x3f84_d5b5_b547_0917, + 0x9216_d5d9_8979_fb1b, 0xd131_0ba6_98df_b5ac, 0x2ffd_72db_d01a_dfb7, 0xb8e1_afed_6a26_7e96, + 0xba7c_9045_f12c_7f99, 0x24a1_9947_b391_6cf7, 0x0801_f2e2_858e_fc16, 0x6369_20d8_7157_4e69 +]; + +pub const BLAKE224_IV: [u32; 8] = [ + 0xc105_9ed8, 0x367c_d507, 0x3070_dd17, 0xf70e_5939, + 0xffc0_0b31, 0x6858_1511, 0x64f9_8fa7, 0xbefa_4fa4 +]; + +pub const BLAKE256_IV: [u32; 8] = [ + 0x6a09_e667, 0xbb67_ae85, 0x3c6e_f372, 0xa54f_f53a, + 0x510e_527f, 0x9b05_688c, 0x1f83_d9ab, 0x5be0_cd19 +]; + +pub const BLAKE384_IV: [u64; 8] = [ + 0xcbbb_9d5d_c105_9ed8, 0x629a_292a_367c_d507, 0x9159_015a_3070_dd17, 0x152f_ecd8_f70e_5939, + 0x6733_2667_ffc0_0b31, 0x8eb4_4a87_6858_1511, 0xdb0c_2e0d_64f9_8fa7, 0x47b5_481d_befa_4fa4 +]; + +pub const BLAKE512_IV: [u64; 8] = [ + 0x6a09_e667_f3bc_c908, 0xbb67_ae85_84ca_a73b, 0x3c6e_f372_fe94_f82b, 0xa54f_f53a_5f1d_36f1, + 0x510e_527f_ade6_82d1, 0x9b05_688c_2b3e_6c1f, 0x1f83_d9ab_fb41_bd6b, 0x5be0_cd19_137e_2179 +]; diff --git a/blake/src/lib.rs b/blake/src/lib.rs new file mode 100644 index 000000000..382001dc6 --- /dev/null +++ b/blake/src/lib.rs @@ -0,0 +1,230 @@ +// copyright 2017 Kaz Wesley + +//! Classic Blake in a Rustic setting + +#![no_std] + +extern crate block_buffer; +pub extern crate digest; + +mod consts; + +use block_buffer::BlockBuffer; +use core::mem; +use digest::generic_array::GenericArray; +pub use digest::Digest; + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +struct State { + h: [T; 8], + s: [T; 4], + t: [T; 2], + nullt: bool, +} + +macro_rules! define_compressor { + ($compressor:ident, $word:ident, $Bufsz:ty, $deserializer:path, $uval:expr, + $rounds:expr, $shift0:expr, $shift1:expr, $shift2: expr, $shift3: expr) => { + #[derive(Clone, Copy, Debug)] + struct $compressor { + state: State<$word>, + } + + impl $compressor { + fn increase_count(&mut self, count: $word) { + let (new_t0, carry) = self.state.t[0].overflowing_add(count * 8); + self.state.t[0] = new_t0; + if carry { self.state.t[1] += 1; } + } + + fn put_block(&mut self, block: &GenericArray) { + const U: [$word; 16] = $uval; + + #[inline(always)] + fn g(v: &mut [$word; 16], m: &[$word; 16], sigma: &[u8; 16], + a: usize, b: usize, c: usize, d: usize, e: usize) { + v[a] = v[a].wrapping_add(m[sigma[e] as usize] ^ U[sigma[e+1] as usize]) + .wrapping_add(v[b]); + v[d] = (v[d] ^ v[a]).rotate_right($shift0); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right($shift1); + v[a] = v[a].wrapping_add(m[sigma[e+1] as usize] ^ U[sigma[e] as usize]) + .wrapping_add(v[b]); + v[d] = (v[d] ^ v[a]).rotate_right($shift2); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right($shift3); + } + + let mut m = [0; 16]; + for (mx, b) in m.iter_mut().zip(block.chunks(mem::size_of::<$word>())) { + *mx = $deserializer(b); + } + + let mut v = [0; 16]; + &v[..8].copy_from_slice(&self.state.h); + &v[8..].copy_from_slice(&U[..8]); + for (vx, sx) in v[8..11].iter_mut().zip(&self.state.s) { + *vx ^= *sx; + } + + // don't xor t when the block is only padding + if !self.state.nullt { + v[12] ^= self.state.t[0]; + v[13] ^= self.state.t[0]; + v[14] ^= self.state.t[1]; + v[15] ^= self.state.t[1]; + } + + for sigma in &SIGMA[..$rounds] { + // column step + g(&mut v, &m, sigma, 0, 4, 8, 12, 0 ); + g(&mut v, &m, sigma, 1, 5, 9, 13, 2 ); + g(&mut v, &m, sigma, 2, 6, 10, 14, 4 ); + g(&mut v, &m, sigma, 3, 7, 11, 15, 6 ); + // diagonal step + g(&mut v, &m, sigma, 0, 5, 10, 15, 8 ); + g(&mut v, &m, sigma, 1, 6, 11, 12, 10 ); + g(&mut v, &m, sigma, 2, 7, 8, 13, 12 ); + g(&mut v, &m, sigma, 3, 4, 9, 14, 14 ); + } + + for (i, vx) in v.iter().enumerate() { + self.state.h[i % 8] ^= *vx; + } + + for (i, hx) in self.state.h.iter_mut().enumerate() { + *hx ^= self.state.s[i % 4]; + } + } + } + } +} + +macro_rules! define_hasher { + ($name:ident, $word:ident, $buf:expr, $Bufsz:ty, $bits:expr, $Bytes:ident, + $serializer:path, $compressor:ident, $iv:expr) => { + #[derive(Clone)] + pub struct $name { + compressor: $compressor, + buffer: BlockBuffer<$Bufsz>, + } + + impl core::fmt::Debug for $name { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + f.debug_struct("Blake") + .field("compressor", &self.compressor) + .field("buffer.position()", &self.buffer.position()) + .finish() + } + } + + impl Default for $name { + fn default() -> Self { + Self { + compressor: $compressor { + state: State::<$word> { + h: $iv, + s: [0; 4], + t: [0; 2], + nullt: false, + } + }, + buffer: BlockBuffer::default(), + } + } + } + + impl digest::BlockInput for $name { + type BlockSize = $Bytes; + } + + impl digest::Input for $name { + fn input>(&mut self, data: T) { + let compressor = &mut self.compressor; + self.buffer.input(data.as_ref(), |block| { + compressor.increase_count((mem::size_of::<$word>() * 16) as $word); + compressor.put_block(block); + }); + } + } + + impl digest::FixedOutput for $name { + type OutputSize = $Bytes; + + fn fixed_result(self) -> GenericArray { + let mut compressor = self.compressor; + let mut buffer = self.buffer; + + compressor.increase_count(buffer.position() as $word); + + let mut msglen = [0u8; $buf/8]; + $serializer(&mut msglen[..$buf/16], compressor.state.t[1]); + $serializer(&mut msglen[$buf/16..], compressor.state.t[0]); + + let footerlen = 1 + 2 * mem::size_of::<$word>(); + + // low bit indicates full-length variant + let isfull = ($bits == 8 * mem::size_of::<[$word; 8]>()) as u8; + // high bit indicates fit with no padding + let exactfit = if buffer.position() + footerlen != $buf { 0x00 } else { 0x80 }; + let magic = isfull | exactfit; + + // if header won't fit in last data block, pad to the end and start a new one + let extra_block = buffer.position() + footerlen > $buf; + if extra_block { + let pad = $buf - buffer.position(); + buffer.input(&PADDING[..pad], |block| compressor.put_block(block)); + debug_assert_eq!(buffer.position(), 0); + } + + // pad last block up to footer start point + compressor.state.nullt = buffer.position() == 0; + // skip begin-padding byte if continuing padding + let x = extra_block as usize; + let (start, end) = (x, x + ($buf - footerlen - buffer.position())); + buffer.input(&PADDING[start..end], |_| unreachable!()); + buffer.input(&[magic], |_| unreachable!()); + buffer.input(&msglen, |block| compressor.put_block(block)); + debug_assert_eq!(buffer.position(), 0); + + let mut out = GenericArray::default(); + for (h, out) in compressor.state.h.iter() + .zip(out.chunks_mut(mem::size_of::<$word>())) { + $serializer(out, *h); + } + out + } + } + + impl digest::Reset for $name { + fn reset(&mut self) { + *self = Self::default() + } + } + }; +} + +use block_buffer::byteorder::{ByteOrder, BE}; +use consts::{ + BLAKE224_IV, BLAKE256_IV, BLAKE256_U, BLAKE384_IV, BLAKE512_IV, BLAKE512_U, PADDING, SIGMA, +}; +use digest::generic_array::typenum::{U128, U28, U32, U48, U64}; + +#[cfg_attr(rustfmt, rustfmt_skip)] +define_compressor!(Compressor256, u32, U64, BE::read_u32, BLAKE256_U, 14, 16, 12, 8, 7); + +#[cfg_attr(rustfmt, rustfmt_skip)] +define_hasher!(Blake224, u32, 64, U64, 224, U28, BE::write_u32, Compressor256, BLAKE224_IV); + +#[cfg_attr(rustfmt, rustfmt_skip)] +define_hasher!(Blake256, u32, 64, U64, 256, U32, BE::write_u32, Compressor256, BLAKE256_IV); + +#[cfg_attr(rustfmt, rustfmt_skip)] +define_compressor!(Compressor512, u64, U128, BE::read_u64, BLAKE512_U, 16, 32, 25, 16, 11); + +#[cfg_attr(rustfmt, rustfmt_skip)] +define_hasher!(Blake384, u64, 128, U128, 384, U48, BE::write_u64, Compressor512, BLAKE384_IV); + +#[cfg_attr(rustfmt, rustfmt_skip)] +define_hasher!(Blake512, u64, 128, U128, 512, U64, BE::write_u64, Compressor512, BLAKE512_IV); diff --git a/blake/tests/data/blake224.blb b/blake/tests/data/blake224.blb new file mode 100644 index 000000000..33e7bbe2b Binary files /dev/null and b/blake/tests/data/blake224.blb differ diff --git a/blake/tests/data/blake256.blb b/blake/tests/data/blake256.blb new file mode 100644 index 000000000..c4b683866 Binary files /dev/null and b/blake/tests/data/blake256.blb differ diff --git a/blake/tests/data/blake384.blb b/blake/tests/data/blake384.blb new file mode 100644 index 000000000..c5f27643e Binary files /dev/null and b/blake/tests/data/blake384.blb differ diff --git a/blake/tests/data/blake512.blb b/blake/tests/data/blake512.blb new file mode 100644 index 000000000..c59162808 Binary files /dev/null and b/blake/tests/data/blake512.blb differ diff --git a/blake/tests/lib.rs b/blake/tests/lib.rs new file mode 100644 index 000000000..1faa865a2 --- /dev/null +++ b/blake/tests/lib.rs @@ -0,0 +1,10 @@ +extern crate blake; +#[macro_use] +extern crate digest; + +use digest::dev::digest_test; + +new_test!(blake224, "blake224", blake::Blake224, digest_test); +new_test!(blake256, "blake256", blake::Blake256, digest_test); +new_test!(blake384, "blake384", blake::Blake384, digest_test); +new_test!(blake512, "blake512", blake::Blake512, digest_test);