skein_hash/
lib.rs

1// copyright 2017 Kaz Wesley
2
3#![no_std]
4
5extern crate block_buffer;
6pub extern crate digest;
7extern crate threefish_cipher;
8
9pub use digest::generic_array::GenericArray;
10pub use digest::Digest;
11
12use block_buffer::block_padding::ZeroPadding;
13use block_buffer::BlockBuffer;
14use digest::generic_array::typenum::{NonZero, PartialDiv, Unsigned, U128, U32, U64, U8};
15use digest::generic_array::ArrayLength;
16use threefish_cipher::{BlockCipher, Threefish1024, Threefish256, Threefish512};
17
18/// N word buffer.
19#[derive(Copy, Clone)]
20union Block<N>
21where
22    N: ArrayLength<u8>,
23    N: PartialDiv<U8>,
24    <N as PartialDiv<U8>>::Output: ArrayLength<u64>,
25    N::ArrayType: Copy,
26    <<N as PartialDiv<U8>>::Output as ArrayLength<u64>>::ArrayType: Copy,
27{
28    bytes: GenericArray<u8, N>,
29    words: GenericArray<u64, <N as PartialDiv<U8>>::Output>,
30}
31
32impl<N> Block<N>
33where
34    N: ArrayLength<u8>,
35    N: PartialDiv<U8>,
36    <N as PartialDiv<U8>>::Output: ArrayLength<u64>,
37    N::ArrayType: Copy,
38    <<N as PartialDiv<U8>>::Output as ArrayLength<u64>>::ArrayType: Copy,
39{
40    fn bytes(&mut self) -> &[u8] {
41        self.as_byte_array().as_slice()
42    }
43
44    fn as_byte_array(&self) -> &GenericArray<u8, N> {
45        unsafe { &self.bytes }
46    }
47
48    fn as_byte_array_mut(&mut self) -> &mut GenericArray<u8, N> {
49        unsafe { &mut self.bytes }
50    }
51
52    fn from_byte_array(block: &GenericArray<u8, N>) -> Self {
53        Block { bytes: *block }
54    }
55}
56
57impl<N> Default for Block<N>
58where
59    N: ArrayLength<u8>,
60    N: PartialDiv<U8>,
61    <N as PartialDiv<U8>>::Output: ArrayLength<u64>,
62    N::ArrayType: Copy,
63    <<N as PartialDiv<U8>>::Output as ArrayLength<u64>>::ArrayType: Copy,
64{
65    fn default() -> Self {
66        Block {
67            words: GenericArray::default(),
68        }
69    }
70}
71
72impl<N> core::ops::BitXor<Block<N>> for Block<N>
73where
74    N: ArrayLength<u8>,
75    N: PartialDiv<U8>,
76    <N as PartialDiv<U8>>::Output: ArrayLength<u64>,
77    N::ArrayType: Copy,
78    <<N as PartialDiv<U8>>::Output as ArrayLength<u64>>::ArrayType: Copy,
79{
80    type Output = Block<N>;
81    fn bitxor(mut self, rhs: Block<N>) -> Self::Output {
82        // XOR is endian-agnostic
83        for (s, r) in unsafe { &mut self.words }
84            .iter_mut()
85            .zip(unsafe { &rhs.words })
86        {
87            *s ^= *r;
88        }
89        self
90    }
91}
92
93#[derive(Clone)]
94struct State<X> {
95    t: (u64, u64),
96    x: X,
97}
98
99impl<X> State<X> {
100    fn new(t1: u64, x: X) -> Self {
101        let t = (0, t1);
102        State { t, x }
103    }
104}
105
106impl<X> core::fmt::Debug for State<X> {
107    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
108        f.debug_struct("State<X>")
109            .field("t", &"(unknown)")
110            .field("x", &"(unknown)")
111            .finish()
112    }
113}
114
115const VERSION: u64 = 1;
116const ID_STRING_LE: u64 = 0x3341_4853;
117const SCHEMA_VER: u64 = (VERSION << 32) | ID_STRING_LE;
118const CFG_TREE_INFO_SEQUENTIAL: u64 = 0;
119const T1_FLAG_FIRST: u64 = 1 << 62;
120const T1_FLAG_FINAL: u64 = 1 << 63;
121const T1_BLK_TYPE_CFG: u64 = 4 << 56;
122const T1_BLK_TYPE_MSG: u64 = 48 << 56;
123const T1_BLK_TYPE_OUT: u64 = 63 << 56;
124const CFG_STR_LEN: usize = 4 * 8;
125
126macro_rules! define_hasher {
127    ($name:ident, $threefish:ident, $state_bytes:ty, $state_bits:expr) => {
128        #[derive(Clone)]
129        pub struct $name<N: Unsigned + ArrayLength<u8> + NonZero + Default> {
130            state: State<Block<$state_bytes>>,
131            buffer: BlockBuffer<$state_bytes>,
132            _output: core::marker::PhantomData<GenericArray<u8, N>>,
133        }
134
135        impl<N> core::fmt::Debug for $name<N>
136        where
137            N: Unsigned + ArrayLength<u8> + NonZero + Default,
138        {
139            fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
140                f.debug_struct("Skein")
141                    .field("state", &self.state)
142                    .field("buffer.position()", &self.buffer.position())
143                    .finish()
144            }
145        }
146
147        impl<N> $name<N>
148        where
149            N: Unsigned + ArrayLength<u8> + NonZero + Default,
150        {
151            fn process_block(
152                state: &mut State<Block<$state_bytes>>,
153                block: &GenericArray<u8, $state_bytes>,
154                byte_count_add: usize,
155            ) {
156                let block = Block::from_byte_array(block);
157                state.t.0 += byte_count_add as u64;
158                let fish = $threefish::with_tweak(state.x.as_byte_array(), state.t.0, state.t.1);
159                let mut x = block.clone();
160                fish.encrypt_block(x.as_byte_array_mut());
161                state.x = x ^ block;
162                state.t.1 &= !T1_FLAG_FIRST;
163            }
164        }
165
166        impl<N> Default for $name<N>
167        where
168            N: Unsigned + ArrayLength<u8> + NonZero + Default,
169        {
170            fn default() -> Self {
171                // build and process config block
172                let mut state = State::new(
173                    T1_FLAG_FIRST | T1_BLK_TYPE_CFG | T1_FLAG_FINAL,
174                    Block::default(),
175                );
176                let mut cfg = GenericArray::<u8, $state_bytes>::default();
177                cfg[..8].copy_from_slice(&SCHEMA_VER.to_le_bytes());
178                cfg[8..16].copy_from_slice(&(N::to_u64() * 8).to_le_bytes());
179                cfg[16..24].copy_from_slice(&CFG_TREE_INFO_SEQUENTIAL.to_le_bytes());
180                Self::process_block(&mut state, &cfg, CFG_STR_LEN);
181
182                // The chaining vars ctx->X are now initialized for the given hashBitLen.
183                // Set up to process the data message portion of the hash (default)
184                state.t = Default::default();
185                state.t.1 = T1_FLAG_FIRST | T1_BLK_TYPE_MSG;
186                Self {
187                    state,
188                    buffer: Default::default(),
189                    _output: Default::default(),
190                }
191            }
192        }
193
194        impl<N> digest::BlockInput for $name<N>
195        where
196            N: Unsigned + ArrayLength<u8> + NonZero + Default,
197        {
198            type BlockSize = <$threefish as BlockCipher>::BlockSize;
199        }
200
201        impl<N> digest::Update for $name<N>
202        where
203            N: Unsigned + ArrayLength<u8> + NonZero + Default,
204        {
205            fn update(&mut self, data: impl AsRef<[u8]>) {
206                let buffer = &mut self.buffer;
207                let state = &mut self.state;
208                buffer.input_lazy(data.as_ref(), |block| {
209                    Self::process_block(state, block, $state_bits / 8)
210                });
211            }
212        }
213
214        impl<N> digest::FixedOutputDirty for $name<N>
215        where
216            N: Unsigned + ArrayLength<u8> + NonZero + Default,
217        {
218            type OutputSize = N;
219
220            fn finalize_into_dirty(&mut self, output: &mut GenericArray<u8, Self::OutputSize>) {
221                self.state.t.1 |= T1_FLAG_FINAL;
222                let pos = self.buffer.position();
223                let final_block = self.buffer.pad_with::<ZeroPadding>().unwrap();
224                Self::process_block(&mut self.state, final_block, pos);
225
226                // run Threefish in "counter mode" to generate output
227                for (i, chunk) in output.chunks_mut($state_bits / 8).enumerate() {
228                    let mut ctr = State::new(
229                        T1_FLAG_FIRST | T1_BLK_TYPE_OUT | T1_FLAG_FINAL,
230                        self.state.x,
231                    );
232                    let mut b = GenericArray::<u8, $state_bytes>::default();
233                    b[..8].copy_from_slice(&(i as u64).to_le_bytes());
234                    Self::process_block(&mut ctr, &b, 8);
235                    let n = chunk.len();
236                    chunk.copy_from_slice(&ctr.x.bytes()[..n]);
237                }
238            }
239        }
240
241        impl<N> digest::Reset for $name<N>
242        where
243            N: Unsigned + ArrayLength<u8> + NonZero + Default,
244        {
245            fn reset(&mut self) {
246                *self = Self::default();
247            }
248        }
249    };
250}
251
252#[rustfmt::skip]
253define_hasher!(Skein256, Threefish256, U32, 256);
254#[rustfmt::skip]
255define_hasher!(Skein512, Threefish512, U64, 512);
256#[rustfmt::skip]
257define_hasher!(Skein1024, Threefish1024, U128, 1024);