skein/
lib.rs

1//! Implementation of the [Skein] family of cryptographic hash algorithms.
2//!
3//! There are 3 standard versions of the Skein hash function:
4//!
5//! * [`Skein256`]
6//! * [`Skein512`]
7//! * [`Skein1024`]
8//!
9//! Output size of the Skein hash functions is arbitrary, so it has to be
10//! fixed using additional type parameter
11//!
12//! # Examples
13//! Hash functionality is usually accessed via the [`Digest`] trait:
14//!
15//! ```
16//! use hex_literal::hex;
17//! use skein::{Digest, Skein512, consts::U32};
18//!
19//! // Create a Skein-512-256 hasher object
20//! let mut hasher = Skein512::<U32>::new();
21//!
22//! // Write input message
23//! hasher.update(b"The quick brown fox ");
24//! hasher.update(b"jumps over the lazy dog");
25//!
26//! // Read hash digest
27//! let result = hasher.finalize();
28//!
29//! let expected = hex!("b3250457e05d3060b1a4bbc1428bc75a3f525ca389aeab96cfa34638d96e492a");
30//! assert_eq!(result[..], expected[..]);
31//! ```
32//! Also see [RustCrypto/hashes] readme.
33//!
34//! [Skein]: https://schneier.com/academic/skein
35//! [RustCrypto/hashes]: https://github.com/RustCrypto/hashes
36
37#![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                // run Threefish in "counter mode" to generate output
151                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                // build and process config block
169                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                // The chaining vars ctx->X are now initialized for the given hashBitLen.
182                // Set up to process the data message portion of the hash (default)
183                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");