1#![no_std]
38#![doc(
39 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
40 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
41)]
42#![warn(missing_docs, rust_2018_idioms)]
43#![deny(unsafe_code)]
44
45pub use digest::{self, consts, Digest};
46
47use core::{fmt, marker::PhantomData};
48use digest::{
49 block_buffer::Lazy,
50 consts::{U128, U32, U64},
51 core_api::{
52 AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
53 OutputSizeUser, Reset, UpdateCore,
54 },
55 generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
56 HashMarker, Output,
57};
58use threefish::{Threefish1024, Threefish256, Threefish512};
59
60const VERSION: u64 = 1;
61const ID_STRING_LE: u64 = 0x3341_4853;
62const SCHEMA_VER: u64 = (VERSION << 32) | ID_STRING_LE;
63const CFG_TREE_INFO_SEQUENTIAL: u64 = 0;
64const T1_FLAG_FIRST: u64 = 1 << 62;
65const T1_FLAG_FINAL: u64 = 1 << 63;
66const T1_BLK_TYPE_CFG: u64 = 4 << 56;
67const T1_BLK_TYPE_MSG: u64 = 48 << 56;
68const T1_BLK_TYPE_OUT: u64 = 63 << 56;
69const CFG_STR_LEN: usize = 4 * 8;
70
71macro_rules! define_hasher {
72 (
73 $name:ident, $full_name:ident, $threefish:ident,
74 $state_bytes:ty, $alg_name:expr
75 ) => {
76 #[doc = $alg_name]
77 #[doc = " core hasher state"]
78 #[derive(Clone)]
79 pub struct $name<N: ArrayLength<u8> + 'static> {
80 t: [u64; 2],
81 x: [u64; <$state_bytes>::USIZE / 8],
82 _pd: PhantomData<N>,
83 }
84
85 impl<N: ArrayLength<u8> + 'static> $name<N> {
86 fn blank_state(t1: u64, x: [u64; <$state_bytes>::USIZE / 8]) -> Self {
87 Self {
88 t: [0, t1],
89 x,
90 _pd: PhantomData,
91 }
92 }
93
94 fn process_block(
95 &mut self,
96 block: &GenericArray<u8, $state_bytes>,
97 byte_count_add: usize,
98 ) {
99 const STATE_WORDS: usize = <$state_bytes>::USIZE / 8;
100
101 self.t[0] += byte_count_add as u64;
102 let cipher = $threefish::new_with_tweak_u64(&self.x.into(), &self.t);
103
104 let mut x = [0u64; STATE_WORDS];
105 for (src, dst) in block.chunks_exact(8).zip(x.iter_mut()) {
106 *dst = u64::from_le_bytes(src.try_into().unwrap());
107 }
108 let t = x;
109
110 cipher.encrypt_block_u64(&mut x);
111
112 for i in 0..STATE_WORDS {
113 self.x[i] = t[i] ^ x[i];
114 }
115 self.t[1] &= !T1_FLAG_FIRST;
116 }
117 }
118
119 impl<N> HashMarker for $name<N> where N: ArrayLength<u8> + 'static {}
120
121 impl<N: ArrayLength<u8> + 'static> BlockSizeUser for $name<N> {
122 type BlockSize = $state_bytes;
123 }
124
125 impl<N: ArrayLength<u8> + 'static> BufferKindUser for $name<N> {
126 type BufferKind = Lazy;
127 }
128
129 impl<N: ArrayLength<u8> + 'static> OutputSizeUser for $name<N> {
130 type OutputSize = N;
131 }
132
133 impl<N: ArrayLength<u8> + 'static> UpdateCore for $name<N> {
134 #[inline]
135 fn update_blocks(&mut self, blocks: &[Block<Self>]) {
136 for block in blocks {
137 self.process_block(block, block.len())
138 }
139 }
140 }
141
142 impl<N: ArrayLength<u8> + 'static> FixedOutputCore for $name<N> {
143 #[inline]
144 fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
145 self.t[1] |= T1_FLAG_FINAL;
146 let pos = buffer.get_pos();
147 let final_block = buffer.pad_with_zeros();
148 self.process_block(final_block, pos);
149
150 let flag = T1_FLAG_FIRST | T1_BLK_TYPE_OUT | T1_FLAG_FINAL;
152 let mut block = GenericArray::<u8, $state_bytes>::default();
153 for (i, chunk) in out.chunks_mut(<$state_bytes>::USIZE).enumerate() {
154 let mut ctr = Self::blank_state(flag, self.x);
155
156 block[..8].copy_from_slice(&(i as u64).to_le_bytes());
157 Self::process_block(&mut ctr, &block, 8);
158
159 for (src, dst) in ctr.x.iter().zip(chunk.chunks_exact_mut(8)) {
160 dst.copy_from_slice(&src.to_le_bytes());
161 }
162 }
163 }
164 }
165
166 impl<N: ArrayLength<u8> + 'static> Default for $name<N> {
167 fn default() -> Self {
168 let mut state = Self::blank_state(
170 T1_FLAG_FIRST | T1_BLK_TYPE_CFG | T1_FLAG_FINAL,
171 Default::default(),
172 );
173
174 let mut cfg = GenericArray::<u8, $state_bytes>::default();
175 cfg[..8].copy_from_slice(&SCHEMA_VER.to_le_bytes());
176 cfg[8..16].copy_from_slice(&(N::to_u64() * 8).to_le_bytes());
177 cfg[16..24].copy_from_slice(&CFG_TREE_INFO_SEQUENTIAL.to_le_bytes());
178
179 state.process_block(&cfg, CFG_STR_LEN);
180
181 state.t = [0, T1_FLAG_FIRST | T1_BLK_TYPE_MSG];
184 state
185 }
186 }
187
188 impl<N: ArrayLength<u8> + 'static> Reset for $name<N> {
189 #[inline]
190 fn reset(&mut self) {
191 *self = Default::default();
192 }
193 }
194
195 impl<N: ArrayLength<u8> + 'static> AlgorithmName for $name<N> {
196 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 f.write_str(stringify!($full_name))
198 }
199 }
200
201 impl<N: ArrayLength<u8> + 'static> fmt::Debug for $name<N> {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
203 write!(f, "{}<{}> {{ .. }}", stringify!($name), N::USIZE)
204 }
205 }
206
207 #[doc = $alg_name]
208 #[doc = " hasher state"]
209 pub type $full_name<OutputSize = $state_bytes> = CoreWrapper<$name<OutputSize>>;
210 };
211}
212
213define_hasher!(Skein256Core, Skein256, Threefish256, U32, "Skein-256");
214define_hasher!(Skein512Core, Skein512, Threefish512, U64, "Skein-512");
215#[rustfmt::skip]
216define_hasher!(Skein1024Core, Skein1024, Threefish1024, U128, "Skein-1024");