From bcb937730e1ea607aaa2a59d51cca2d5282d0aed Mon Sep 17 00:00:00 2001 From: Artyom Pavlov Date: Fri, 9 Jun 2023 03:18:05 +0300 Subject: [PATCH] threefish: add u64-based methods and make cipher optional (#364) --- Cargo.lock | 7 +- threefish/CHANGELOG.md | 9 +++ threefish/Cargo.toml | 9 +-- threefish/src/lib.rs | 148 +++++++++++++++++++++++++---------------- threefish/tests/mod.rs | 1 + 5 files changed, 110 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96d2df67..14bf8b5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.142" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "magma" @@ -203,10 +203,11 @@ dependencies = [ [[package]] name = "threefish" -version = "0.5.1" +version = "0.5.2" dependencies = [ "cipher", "hex-literal", + "zeroize", ] [[package]] diff --git a/threefish/CHANGELOG.md b/threefish/CHANGELOG.md index ba3f4d2c..6509403a 100644 --- a/threefish/CHANGELOG.md +++ b/threefish/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.5.2 (2022-06-09) +### Added +- `new_with_tweak_u64`, `encrypt_block_u64`, and `decrypt_block_u64` methods ([#364]) + +### Changed +- `cipher` is now an (enabled by default) optional dependency ([#364]) + +[#364]: https://github.com/RustCrypto/block-ciphers/pull/364 + ## 0.5.1 (2022-02-17) ### Fixed - Minimal versions build ([#303]) diff --git a/threefish/Cargo.toml b/threefish/Cargo.toml index 02c5e2c8..f8545b2f 100644 --- a/threefish/Cargo.toml +++ b/threefish/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "threefish" -version = "0.5.1" +version = "0.5.2" description = "Threefish block cipher" authors = ["The Rust-Crypto Project Developers"] license = "MIT OR Apache-2.0" @@ -13,14 +13,15 @@ keywords = ["crypto", "threefish", "block-cipher"] categories = ["cryptography", "no-std"] [dependencies] -cipher = "0.4.2" +cipher = { version = "0.4.2", optional = true } +zeroize = { version = "1.6", optional = true, default-features = false } [dev-dependencies] -cipher = { version = "0.4.2", features = ["dev"] } +cipher = { version = "0.4.2", features = ["dev"]} hex-literal = "0.3.3" [features] -zeroize = ["cipher/zeroize"] +default = ["cipher"] [package.metadata.docs.rs] all-features = true diff --git a/threefish/src/lib.rs b/threefish/src/lib.rs index 57427499..14e8ee85 100644 --- a/threefish/src/lib.rs +++ b/threefish/src/lib.rs @@ -19,19 +19,23 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs, rust_2018_idioms)] +#[cfg(feature = "cipher")] pub use cipher; -mod consts; +use core::fmt; -use crate::consts::{C240, P1024, P256, P512, R1024, R256, R512}; +#[cfg(feature = "cipher")] use cipher::{ consts::{U128, U32, U64}, AlgorithmName, BlockCipher, Key, KeyInit, KeySizeUser, }; -use core::fmt; + +mod consts; + +use crate::consts::{C240, P1024, P256, P512, R1024, R256, R512}; #[cfg(feature = "zeroize")] -use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; +use zeroize::{Zeroize, ZeroizeOnDrop}; fn mix(r: u8, x: (u64, u64)) -> (u64, u64) { let y0 = x.0.wrapping_add(x.1); @@ -59,16 +63,27 @@ macro_rules! impl_threefish( impl $name { /// Create new block cipher instance with the given key and tweak. + #[inline(always)] pub fn new_with_tweak(key: &[u8; $n_w*8], tweak: &[u8; 16]) -> $name { - let mut k = [0u64; $n_w + 1]; + let mut k = [0u64; $n_w]; for (kv, chunk) in k[..$n_w].iter_mut().zip(key.chunks_exact(8)) { *kv = u64::from_le_bytes(chunk.try_into().unwrap()); } - k[$n_w] = k[..$n_w].iter().fold(C240, core::ops::BitXor::bitxor); + let tweak = [ + u64::from_le_bytes(tweak[..8].try_into().unwrap()), + u64::from_le_bytes(tweak[8..].try_into().unwrap()), + ]; + Self::new_with_tweak_u64(&k, &tweak) + } - let t0 = u64::from_le_bytes(tweak[..8].try_into().unwrap()); - let t1 = u64::from_le_bytes(tweak[8..].try_into().unwrap()); - let t = [t0, t1, t0 ^ t1]; + /// Create new block cipher instance with the given key and tweak + /// represented in the form of array of `u64`s. + #[inline(always)] + pub fn new_with_tweak_u64(key: &[u64; $n_w], tweak: &[u64; 2]) -> $name { + let mut k = [0u64; $n_w + 1]; + k[..$n_w].copy_from_slice(key); + k[$n_w] = key.iter().fold(C240, core::ops::BitXor::bitxor); + let t = [tweak[0], tweak[1], tweak[0] ^ tweak[1]]; let mut sk = [[0u64; $n_w]; $rounds / 4 + 1]; for s in 0..=($rounds / 4) { @@ -86,14 +101,74 @@ macro_rules! impl_threefish( $name { sk } } + + /// Encrypt block in the form of array of `u64`s + #[inline(always)] + pub fn encrypt_block_u64(&self, block: &mut [u64; $n_w]) { + for d in 0..$rounds { + let block_prev = block.clone(); + for j in 0..($n_w / 2) { + let v = (block_prev[2 * j], block_prev[2 * j + 1]); + let e = if d % 4 == 0 { + let s0 = self.sk[d / 4][2 * j]; + let s1 = self.sk[d / 4][2 * j + 1]; + (v.0.wrapping_add(s0), v.1.wrapping_add(s1)) + } else { + v + }; + let r = $rot[d % 8][j]; + let (f0, f1) = mix(r, e); + let (pi0, pi1) = ($perm[2 * j], $perm[2 * j + 1]); + block[pi0 as usize] = f0; + block[pi1 as usize] = f1; + } + } + + for (b, s) in block.iter_mut().zip(&self.sk[$rounds / 4]) { + *b = b.wrapping_add(*s); + } + } + + /// Decrypt block in the form of array of `u64`s + #[inline(always)] + pub fn decrypt_block_u64(&self, block: &mut [u64; $n_w]) { + for (b, s) in block.iter_mut().zip(&self.sk[$rounds / 4]) { + *b = b.wrapping_sub(*s); + } + + for d in (0..$rounds).rev() { + let block_prev = block.clone(); + for j in 0..($n_w / 2) { + let (pi0, pi1) = ($perm[2 * j], $perm[2 * j + 1]); + let f = (block_prev[pi0 as usize], block_prev[pi1 as usize]); + let r = $rot[d % 8][j]; + let (e0, e1) = inv_mix(r, f); + if d % 4 == 0 { + let s0 = self.sk[d / 4][2 * j]; + let s1 = self.sk[d / 4][2 * j + 1]; + block[2 * j] = e0.wrapping_sub(s0); + block[2 * j + 1] = e1.wrapping_sub(s1); + } else { + block[2 * j] = e0; + block[2 * j + 1] = e1; + } + } + } + } } + #[cfg(feature = "cipher")] + #[cfg_attr(docsrs, doc(cfg(feature = "cipher")))] impl BlockCipher for $name {} + #[cfg(feature = "cipher")] + #[cfg_attr(docsrs, doc(cfg(feature = "cipher")))] impl KeySizeUser for $name { type KeySize = $block_size; } + #[cfg(feature = "cipher")] + #[cfg_attr(docsrs, doc(cfg(feature = "cipher")))] impl KeyInit for $name { fn new(key: &Key) -> Self { let mut tmp_key = [0u8; $n_w*8]; @@ -108,13 +183,15 @@ macro_rules! impl_threefish( } } + #[cfg(feature = "cipher")] + #[cfg_attr(docsrs, doc(cfg(feature = "cipher")))] impl AlgorithmName for $name { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(stringify!($name)) } } - #[cfg(feature = "zeroize")] + #[cfg(all(feature = "zeroize"))] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl Drop for $name { fn drop(&mut self) { @@ -122,10 +199,11 @@ macro_rules! impl_threefish( } } - #[cfg(feature = "zeroize")] + #[cfg(all(feature = "zeroize"))] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl ZeroizeOnDrop for $name {} + #[cfg(feature = "cipher")] cipher::impl_simple_block_encdec!( $name, $block_size, cipher, block, encrypt: { @@ -135,29 +213,7 @@ macro_rules! impl_threefish( *vv = u64::from_le_bytes(chunk.try_into().unwrap()); } - for d in 0..$rounds { - let v_tmp = v.clone(); - for j in 0..($n_w / 2) { - let (v0, v1) = (v_tmp[2 * j], v_tmp[2 * j + 1]); - let (e0, e1) = - if d % 4 == 0 { - (v0.wrapping_add(cipher.sk[d / 4][2 * j]), - v1.wrapping_add(cipher.sk[d / 4][2 * j + 1])) - } else { - (v0, v1) - }; - let r = $rot[d % 8][j]; - let (f0, f1) = mix(r, (e0, e1)); - let (pi0, pi1) = - ($perm[2 * j], $perm[2 * j + 1]); - v[pi0 as usize] = f0; - v[pi1 as usize] = f1; - } - } - - for i in 0..$n_w { - v[i] = v[i].wrapping_add(cipher.sk[$rounds / 4][i]); - } + cipher.encrypt_block_u64(&mut v); let block = block.get_out(); for (chunk, vv) in block.chunks_exact_mut(8).zip(v.iter()) { @@ -171,29 +227,7 @@ macro_rules! impl_threefish( *vv = u64::from_le_bytes(chunk.try_into().unwrap()); } - for i in 0..$n_w { - v[i] = v[i].wrapping_sub(cipher.sk[$rounds / 4][i]); - } - - for d in (0..$rounds).rev() { - let v_tmp = v.clone(); - for j in 0..($n_w / 2) { - let (inv_pi0, inv_pi1) = - ($perm[2 * j] as usize, $perm[2 * j + 1] as usize); - let (f0, f1) = (v_tmp[inv_pi0], v_tmp[inv_pi1]); - let r = $rot[d % 8][j]; - let (e0, e1) = inv_mix(r, (f0, f1)); - let (v0, v1) = - if d % 4 == 0 { - (e0.wrapping_sub(cipher.sk[d / 4][2 * j]), - e1.wrapping_sub(cipher.sk[d / 4][2 * j + 1])) - } else { - (e0, e1) - }; - v[2 * j] = v0; - v[2 * j + 1] = v1; - } - } + cipher.decrypt_block_u64(&mut v); let block = block.get_out(); for (chunk, vv) in block.chunks_exact_mut(8).zip(v.iter()) { diff --git a/threefish/tests/mod.rs b/threefish/tests/mod.rs index a9d16d83..4436ecc4 100644 --- a/threefish/tests/mod.rs +++ b/threefish/tests/mod.rs @@ -1,5 +1,6 @@ //! Test vectors from: //! https://github.com/weidai11/cryptopp/blob/master/TestVectors/threefish.txt +#![cfg(featue = "cipher")] use cipher::{Block, BlockDecrypt, BlockEncrypt, KeyInit}; use hex_literal::hex; use threefish::{Threefish1024, Threefish256, Threefish512};