1#![no_std]
14#![doc(
15 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg",
16 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg"
17)]
18#![deny(unsafe_code)]
19#![cfg_attr(docsrs, feature(doc_cfg))]
20#![warn(missing_docs, rust_2018_idioms)]
21
22#[cfg(feature = "cipher")]
23pub use cipher;
24
25use core::fmt;
26
27#[cfg(feature = "cipher")]
28use cipher::{
29 consts::{U128, U32, U64},
30 AlgorithmName, BlockCipher, Key, KeyInit, KeySizeUser,
31};
32
33mod consts;
34
35use crate::consts::{C240, P1024, P256, P512, R1024, R256, R512};
36
37#[cfg(feature = "zeroize")]
38use zeroize::{Zeroize, ZeroizeOnDrop};
39
40fn mix(r: u8, x: (u64, u64)) -> (u64, u64) {
41 let y0 = x.0.wrapping_add(x.1);
42 let y1 = x.1.rotate_left(r as u32) ^ y0;
43 (y0, y1)
44}
45
46fn inv_mix(r: u8, y: (u64, u64)) -> (u64, u64) {
47 let x1 = (y.0 ^ y.1).rotate_right(r as u32);
48 let x0 = y.0.wrapping_sub(x1);
49 (x0, x1)
50}
51
52macro_rules! impl_threefish(
53 (
54 $name:ident, $rounds:expr, $n_w:expr, $block_size:ty,
55 $rot:expr, $perm:expr, $doc_name:expr
56 ) => (
57 #[doc=$doc_name]
58 #[doc="block cipher."]
59 #[derive(Clone)]
60 pub struct $name {
61 sk: [[u64; $n_w]; $rounds / 4 + 1]
62 }
63
64 impl $name {
65 #[inline(always)]
67 pub fn new_with_tweak(key: &[u8; $n_w*8], tweak: &[u8; 16]) -> $name {
68 let mut k = [0u64; $n_w];
69 for (kv, chunk) in k[..$n_w].iter_mut().zip(key.chunks_exact(8)) {
70 *kv = u64::from_le_bytes(chunk.try_into().unwrap());
71 }
72 let tweak = [
73 u64::from_le_bytes(tweak[..8].try_into().unwrap()),
74 u64::from_le_bytes(tweak[8..].try_into().unwrap()),
75 ];
76 Self::new_with_tweak_u64(&k, &tweak)
77 }
78
79 #[inline(always)]
82 pub fn new_with_tweak_u64(key: &[u64; $n_w], tweak: &[u64; 2]) -> $name {
83 let mut k = [0u64; $n_w + 1];
84 k[..$n_w].copy_from_slice(key);
85 k[$n_w] = key.iter().fold(C240, core::ops::BitXor::bitxor);
86 let t = [tweak[0], tweak[1], tweak[0] ^ tweak[1]];
87
88 let mut sk = [[0u64; $n_w]; $rounds / 4 + 1];
89 for s in 0..=($rounds / 4) {
90 for i in 0..$n_w {
91 sk[s][i] = k[(s + i) % ($n_w + 1)];
92 if i == $n_w - 3 {
93 sk[s][i] = sk[s][i].wrapping_add(t[s % 3]);
94 } else if i == $n_w - 2 {
95 sk[s][i] = sk[s][i].wrapping_add(t[(s + 1) % 3]);
96 } else if i == $n_w - 1 {
97 sk[s][i] = sk[s][i].wrapping_add(s as u64);
98 }
99 }
100 }
101
102 $name { sk }
103 }
104
105 #[inline(always)]
107 pub fn encrypt_block_u64(&self, block: &mut [u64; $n_w]) {
108 for d in 0..$rounds {
109 let block_prev = block.clone();
110 for j in 0..($n_w / 2) {
111 let v = (block_prev[2 * j], block_prev[2 * j + 1]);
112 let e = if d % 4 == 0 {
113 let s0 = self.sk[d / 4][2 * j];
114 let s1 = self.sk[d / 4][2 * j + 1];
115 (v.0.wrapping_add(s0), v.1.wrapping_add(s1))
116 } else {
117 v
118 };
119 let r = $rot[d % 8][j];
120 let (f0, f1) = mix(r, e);
121 let (pi0, pi1) = ($perm[2 * j], $perm[2 * j + 1]);
122 block[pi0 as usize] = f0;
123 block[pi1 as usize] = f1;
124 }
125 }
126
127 for (b, s) in block.iter_mut().zip(&self.sk[$rounds / 4]) {
128 *b = b.wrapping_add(*s);
129 }
130 }
131
132 #[inline(always)]
134 pub fn decrypt_block_u64(&self, block: &mut [u64; $n_w]) {
135 for (b, s) in block.iter_mut().zip(&self.sk[$rounds / 4]) {
136 *b = b.wrapping_sub(*s);
137 }
138
139 for d in (0..$rounds).rev() {
140 let block_prev = block.clone();
141 for j in 0..($n_w / 2) {
142 let (pi0, pi1) = ($perm[2 * j], $perm[2 * j + 1]);
143 let f = (block_prev[pi0 as usize], block_prev[pi1 as usize]);
144 let r = $rot[d % 8][j];
145 let (e0, e1) = inv_mix(r, f);
146 if d % 4 == 0 {
147 let s0 = self.sk[d / 4][2 * j];
148 let s1 = self.sk[d / 4][2 * j + 1];
149 block[2 * j] = e0.wrapping_sub(s0);
150 block[2 * j + 1] = e1.wrapping_sub(s1);
151 } else {
152 block[2 * j] = e0;
153 block[2 * j + 1] = e1;
154 }
155 }
156 }
157 }
158 }
159
160 #[cfg(feature = "cipher")]
161 #[cfg_attr(docsrs, doc(cfg(feature = "cipher")))]
162 impl BlockCipher for $name {}
163
164 #[cfg(feature = "cipher")]
165 #[cfg_attr(docsrs, doc(cfg(feature = "cipher")))]
166 impl KeySizeUser for $name {
167 type KeySize = $block_size;
168 }
169
170 #[cfg(feature = "cipher")]
171 #[cfg_attr(docsrs, doc(cfg(feature = "cipher")))]
172 impl KeyInit for $name {
173 fn new(key: &Key<Self>) -> Self {
174 let mut tmp_key = [0u8; $n_w*8];
175 tmp_key.copy_from_slice(key);
176 Self::new_with_tweak(&tmp_key, &Default::default())
177 }
178 }
179
180 impl fmt::Debug for $name {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 f.write_str(concat!(stringify!($name), " { ... }"))
183 }
184 }
185
186 #[cfg(feature = "cipher")]
187 #[cfg_attr(docsrs, doc(cfg(feature = "cipher")))]
188 impl AlgorithmName for $name {
189 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 f.write_str(stringify!($name))
191 }
192 }
193
194 #[cfg(all(feature = "zeroize"))]
195 #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
196 impl Drop for $name {
197 fn drop(&mut self) {
198 self.sk.zeroize();
199 }
200 }
201
202 #[cfg(all(feature = "zeroize"))]
203 #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
204 impl ZeroizeOnDrop for $name {}
205
206 #[cfg(feature = "cipher")]
207 cipher::impl_simple_block_encdec!(
208 $name, $block_size, cipher, block,
209 encrypt: {
210 let mut v = [0u64; $n_w];
211 let b = block.get_in();
212 for (vv, chunk) in v.iter_mut().zip(b.chunks_exact(8)) {
213 *vv = u64::from_le_bytes(chunk.try_into().unwrap());
214 }
215
216 cipher.encrypt_block_u64(&mut v);
217
218 let block = block.get_out();
219 for (chunk, vv) in block.chunks_exact_mut(8).zip(v.iter()) {
220 chunk.copy_from_slice(&vv.to_le_bytes());
221 }
222 }
223 decrypt: {
224 let mut v = [0u64; $n_w];
225 let b = block.get_in();
226 for (vv, chunk) in v.iter_mut().zip(b.chunks_exact(8)) {
227 *vv = u64::from_le_bytes(chunk.try_into().unwrap());
228 }
229
230 cipher.decrypt_block_u64(&mut v);
231
232 let block = block.get_out();
233 for (chunk, vv) in block.chunks_exact_mut(8).zip(v.iter()) {
234 chunk.copy_from_slice(&vv.to_le_bytes());
235 }
236 }
237 );
238 )
239);
240
241impl_threefish!(Threefish256, 72, 4, U32, R256, P256, "Threefish-256");
242impl_threefish!(Threefish512, 72, 8, U64, R512, P512, "Threefish-512");
243impl_threefish!(Threefish1024, 80, 16, U128, R1024, P1024, "Threefish-1024");