Skip to content

Commit

Permalink
threefish: add u64-based methods and make cipher optional
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed Jun 8, 2023
1 parent efd9d8a commit 47cc2e9
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 64 deletions.
7 changes: 4 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions threefish/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-08)
### Added
- `new_with_tweak_u64`, `encrypt_block_u64`, and `decrypt_block_u64` methods ([#364])

### Changed
- `cipher` is now enabled by default optional dependsncy ([#364])

[#364]: https://github.com/RustCrypto/block-ciphers/pull/364

## 0.5.1 (2022-02-17)
### Fixed
- Minimal versions build ([#303])
Expand Down
9 changes: 5 additions & 4 deletions threefish/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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
Expand Down
148 changes: 91 additions & 57 deletions threefish/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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>) -> Self {
let mut tmp_key = [0u8; $n_w*8];
Expand All @@ -108,24 +183,27 @@ 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) {
self.sk.zeroize();
}
}

#[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: {
Expand All @@ -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()) {
Expand All @@ -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()) {
Expand Down
1 change: 1 addition & 0 deletions threefish/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down

0 comments on commit 47cc2e9

Please sign in to comment.