threefish/
lib.rs

1//! Pure Rust implementation of the [Threefish] block ciphers.
2//!
3//! # ⚠️ Security Warning: Hazmat!
4//!
5//! This crate implements only the low-level block cipher function, and is intended
6//! for use for implementing higher-level constructions *only*. It is NOT
7//! intended for direct use in applications.
8//!
9//! USE AT YOUR OWN RISK!
10//!
11//! [Threefish]: https://en.wikipedia.org/wiki/Threefish
12
13#![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            /// Create new block cipher instance with the given key and tweak.
66            #[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            /// Create new block cipher instance with the given key and tweak
80            /// represented in the form of array of `u64`s.
81            #[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            /// Encrypt block in the form of array of `u64`s
106            #[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            /// Decrypt block in the form of array of `u64`s
133            #[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");