From 5aff0079100b26d833708807c0331e401890c930 Mon Sep 17 00:00:00 2001 From: Artyom Pavlov Date: Sat, 3 Aug 2024 00:43:54 +0300 Subject: [PATCH] kuznyechik: implement new software backend (#443) The new software backend implements the same approach as in the SSE2 backend, but uses `u128` instead of `__m128i`. The resulting performance is slightly worse compared to SSE2, but the code is cross-platform and should be much faster than the old "compact" software backend. --- .github/workflows/kuznyechik.yml | 40 +--- Cargo.lock | 1 + kuznyechik/Cargo.toml | 3 +- kuznyechik/src/big_soft/backends.rs | 170 ++++++++++++++ kuznyechik/src/big_soft/mod.rs | 63 +++++ .../src/{soft => compact_soft}/backends.rs | 0 .../src/{soft => compact_soft}/consts.rs | 0 kuznyechik/src/compact_soft/mod.rs | 56 +++++ .../src/{sse2 => fused_tables}/consts.rs | 10 +- .../src/{sse2 => fused_tables}/dec_table.bin | Bin .../src/{sse2 => fused_tables}/enc_table.bin | Bin .../src/{sse2 => fused_tables}/rkey_gen.bin | 0 kuznyechik/src/lib.rs | 214 +++++++++++++++-- kuznyechik/src/soft/mod.rs | 210 ----------------- kuznyechik/src/sse2/mod.rs | 216 +++--------------- 15 files changed, 534 insertions(+), 449 deletions(-) create mode 100644 kuznyechik/src/big_soft/backends.rs create mode 100644 kuznyechik/src/big_soft/mod.rs rename kuznyechik/src/{soft => compact_soft}/backends.rs (100%) rename kuznyechik/src/{soft => compact_soft}/consts.rs (100%) create mode 100644 kuznyechik/src/compact_soft/mod.rs rename kuznyechik/src/{sse2 => fused_tables}/consts.rs (55%) rename kuznyechik/src/{sse2 => fused_tables}/dec_table.bin (100%) rename kuznyechik/src/{sse2 => fused_tables}/enc_table.bin (100%) rename kuznyechik/src/{sse2 => fused_tables}/rkey_gen.bin (100%) delete mode 100644 kuznyechik/src/soft/mod.rs diff --git a/.github/workflows/kuznyechik.yml b/.github/workflows/kuznyechik.yml index a77983ec..91d3c91e 100644 --- a/.github/workflows/kuznyechik.yml +++ b/.github/workflows/kuznyechik.yml @@ -39,44 +39,15 @@ jobs: - env: RUSTFLAGS: "-Dwarnings --cfg kuznyechik_force_soft" run: cargo build --target ${{ matrix.target }} + - env: + RUSTFLAGS: "-Dwarnings --cfg kuznyechik_force_soft --cfg kuznyechik_compact_soft" + run: cargo build --target ${{ matrix.target }} minimal-versions: uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master with: working-directory: ${{ github.workflow }} - sse2: - runs-on: ubuntu-latest - strategy: - matrix: - rust: - - 1.65.0 # MSRV - - stable - target: - - i686-unknown-linux-gnu - - x86_64-unknown-linux-gnu - include: - - target: i686-unknown-linux-gnu - deps: sudo apt update && sudo apt install gcc-multilib - steps: - - uses: actions/checkout@v3 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - run: ${{ matrix.deps }} - - env: - RUSTFLAGS: "-Dwarnings -C target-feature=+sse2" - run: | - cargo test --target ${{ matrix.target }} - cargo test --target ${{ matrix.target }} --all-features - - env: - RUSTFLAGS: "-Dwarnings -C target-feature=+sse2 --cfg kuznyechik_force_soft" - run: | - cargo test --target ${{ matrix.target }} - cargo test --target ${{ matrix.target }} --all-features - test: runs-on: ubuntu-latest strategy: @@ -98,3 +69,8 @@ jobs: run: | cargo test cargo test --all-features + - env: + RUSTFLAGS: "-Dwarnings --cfg kuznyechik_force_soft --cfg kuznyechik_compact_soft" + run: | + cargo test + cargo test --all-features diff --git a/Cargo.lock b/Cargo.lock index e624b6f9..25e0977c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,6 +158,7 @@ dependencies = [ name = "kuznyechik" version = "0.9.0-pre.1" dependencies = [ + "cfg-if", "cipher", "hex-literal", ] diff --git a/kuznyechik/Cargo.toml b/kuznyechik/Cargo.toml index 5517fbc7..11cbaacc 100644 --- a/kuznyechik/Cargo.toml +++ b/kuznyechik/Cargo.toml @@ -14,6 +14,7 @@ categories = ["cryptography", "no-std"] [dependencies] cipher = "=0.5.0-pre.6" +cfg-if = "1" [dev-dependencies] cipher = { version = "=0.5.0-pre.6", features = ["dev"] } @@ -24,7 +25,7 @@ zeroize = ["cipher/zeroize"] [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = ["cfg(kuznyechik_force_soft)"] +check-cfg = ["cfg(kuznyechik_force_soft)", "cfg(kuznyechik_compact_soft)"] [package.metadata.docs.rs] all-features = true diff --git a/kuznyechik/src/big_soft/backends.rs b/kuznyechik/src/big_soft/backends.rs new file mode 100644 index 00000000..2611123b --- /dev/null +++ b/kuznyechik/src/big_soft/backends.rs @@ -0,0 +1,170 @@ +use super::consts::{Table, DEC_TABLE, ENC_TABLE, RKEY_GEN}; +use crate::{ + consts::{P, P_INV}, + Block, Key, +}; +use cipher::{ + array::Array, consts, inout::InOut, BlockBackend, BlockSizeUser, ParBlocks, ParBlocksSizeUser, +}; + +pub(super) type RoundKeys = [u128; 10]; +type ParBlocksSize = consts::U3; + +#[rustfmt::skip] +macro_rules! unroll_par { + ($var:ident, $body:block) => { + { let $var: usize = 0; $body; } + { let $var: usize = 1; $body; } + { let $var: usize = 2; $body; } + }; +} + +#[inline(always)] +fn sub_bytes(block: u128, sbox: &[u8; 256]) -> u128 { + u128::from_le_bytes(block.to_le_bytes().map(|v| sbox[v as usize])) +} + +#[inline(always)] +fn transform(block: u128, table: &Table) -> u128 { + let table: &[[u128; 256]; 16] = unsafe { &*(table.as_ptr().cast()) }; + let block = block.to_le_bytes(); + let mut res = 0u128; + for i in 0..16 { + res ^= table[i][block[i] as usize]; + } + #[cfg(target_endian = "big")] + let res = res.swap_bytes(); + res +} + +pub(super) fn expand_enc_keys(key: &Key) -> RoundKeys { + #[inline(always)] + fn next_const(i: usize) -> u128 { + // correct alignment of `p` is guaranteed since the table is aligned to 16 bytes + let t: &[u128; 32] = unsafe { &*(RKEY_GEN.as_ptr().cast()) }; + let val = t[i]; + #[cfg(target_endian = "big")] + let val = val.swap_bytes(); + val + } + + let mut enc_keys = [0; 10]; + + let mut k1 = u128::from_le_bytes(key[..16].try_into().unwrap()); + let mut k2 = u128::from_le_bytes(key[16..].try_into().unwrap()); + + enc_keys[0] = k1; + enc_keys[1] = k2; + + let mut cidx = 0; + for i in 1..5 { + for _ in 0..4 { + let mut t = k1 ^ next_const(cidx); + cidx += 1; + t = transform(t, &ENC_TABLE); + k2 ^= t; + + let mut t = k2 ^ next_const(cidx); + cidx += 1; + t = transform(t, &ENC_TABLE); + k1 ^= t; + } + + enc_keys[2 * i] = k1; + enc_keys[2 * i + 1] = k2; + } + + enc_keys +} + +pub(super) fn inv_enc_keys(enc_keys: &RoundKeys) -> RoundKeys { + let mut dec_keys = [0; 10]; + + dec_keys[0] = enc_keys[9]; + for i in 1..9 { + let k = sub_bytes(enc_keys[i], &P); + dec_keys[9 - i] = transform(k, &DEC_TABLE); + } + dec_keys[9] = enc_keys[0]; + + dec_keys +} + +pub(crate) struct EncBackend<'a>(pub(crate) &'a RoundKeys); + +impl<'a> BlockSizeUser for EncBackend<'a> { + type BlockSize = consts::U16; +} + +impl<'a> ParBlocksSizeUser for EncBackend<'a> { + type ParBlocksSize = ParBlocksSize; +} + +impl<'a> BlockBackend for EncBackend<'a> { + #[inline] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let k = self.0; + + let mut b: u128 = u128::from_le_bytes(block.get_in().0); + + for i in 0..9 { + b ^= k[i]; + b = transform(b, &ENC_TABLE); + } + b ^= k[9]; + + *block.get_out() = Array(b.to_le_bytes()); + } + + #[inline] + fn proc_par_blocks(&mut self, mut blocks: InOut<'_, '_, ParBlocks>) { + let k = self.0; + + let mut bs = blocks.get_in().0.map(|b| u128::from_le_bytes(b.0)); + + for i in 0..9 { + unroll_par!(j, { + bs[j] ^= k[i]; + bs[j] = transform(bs[j], &ENC_TABLE); + }); + } + + let blocks_out = blocks.get_out(); + unroll_par!(i, { + bs[i] ^= k[9]; + blocks_out[i].0 = u128::to_le_bytes(bs[i]); + }); + } +} + +pub(crate) struct DecBackend<'a>(pub(crate) &'a RoundKeys); + +impl<'a> BlockSizeUser for DecBackend<'a> { + type BlockSize = consts::U16; +} + +impl<'a> ParBlocksSizeUser for DecBackend<'a> { + type ParBlocksSize = consts::U1; +} + +impl<'a> BlockBackend for DecBackend<'a> { + #[inline] + fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { + let k = self.0; + + let mut b: u128 = u128::from_le_bytes(block.get_in().0); + + b ^= k[0]; + b = sub_bytes(b, &P); + b = transform(b, &DEC_TABLE); + + for i in 1..9 { + b = transform(b, &DEC_TABLE); + b ^= k[i]; + } + b = sub_bytes(b, &P_INV); + b ^= k[9]; + + *block.get_out() = Array(b.to_le_bytes()); + } +} diff --git a/kuznyechik/src/big_soft/mod.rs b/kuznyechik/src/big_soft/mod.rs new file mode 100644 index 00000000..b96f7f5d --- /dev/null +++ b/kuznyechik/src/big_soft/mod.rs @@ -0,0 +1,63 @@ +use crate::{BlockSize, Key}; +use cipher::{BlockCipherDecrypt, BlockCipherEncrypt, BlockClosure}; + +mod backends; +#[path = "../fused_tables/consts.rs"] +mod consts; + +use backends::{expand_enc_keys, inv_enc_keys, DecBackend, EncBackend, RoundKeys}; + +#[derive(Clone)] +pub(crate) struct EncDecKeys { + enc: RoundKeys, + dec: RoundKeys, +} +#[derive(Clone)] +pub(crate) struct EncKeys(RoundKeys); +#[derive(Clone)] +pub(crate) struct DecKeys(RoundKeys); + +impl EncKeys { + pub fn new(key: &Key) -> Self { + Self(expand_enc_keys(key)) + } +} + +impl From for EncDecKeys { + fn from(enc: EncKeys) -> Self { + Self { + dec: inv_enc_keys(&enc.0), + enc: enc.0, + } + } +} + +impl From for DecKeys { + fn from(enc: EncKeys) -> Self { + Self(inv_enc_keys(&enc.0)) + } +} + +impl BlockCipherEncrypt for crate::Kuznyechik { + fn encrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut EncBackend(&self.keys.enc)); + } +} + +impl BlockCipherDecrypt for crate::Kuznyechik { + fn decrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut DecBackend(&self.keys.dec)); + } +} + +impl BlockCipherEncrypt for crate::KuznyechikEnc { + fn encrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut EncBackend(&self.keys.0)); + } +} + +impl BlockCipherDecrypt for crate::KuznyechikDec { + fn decrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut DecBackend(&self.keys.0)); + } +} diff --git a/kuznyechik/src/soft/backends.rs b/kuznyechik/src/compact_soft/backends.rs similarity index 100% rename from kuznyechik/src/soft/backends.rs rename to kuznyechik/src/compact_soft/backends.rs diff --git a/kuznyechik/src/soft/consts.rs b/kuznyechik/src/compact_soft/consts.rs similarity index 100% rename from kuznyechik/src/soft/consts.rs rename to kuznyechik/src/compact_soft/consts.rs diff --git a/kuznyechik/src/compact_soft/mod.rs b/kuznyechik/src/compact_soft/mod.rs new file mode 100644 index 00000000..e68c1b78 --- /dev/null +++ b/kuznyechik/src/compact_soft/mod.rs @@ -0,0 +1,56 @@ +use crate::{BlockSize, Key}; +use cipher::{BlockCipherDecrypt, BlockCipherEncrypt, BlockClosure}; + +mod backends; +mod consts; + +use backends::{expand, DecBackend, EncBackend, RoundKeys}; + +#[derive(Clone)] +pub(crate) struct EncDecKeys(RoundKeys); +#[derive(Clone)] +pub(crate) struct EncKeys(RoundKeys); +#[derive(Clone)] +pub(crate) struct DecKeys(RoundKeys); + +impl From for EncDecKeys { + fn from(enc: EncKeys) -> Self { + Self(enc.0) + } +} + +impl From for DecKeys { + fn from(enc: EncKeys) -> Self { + Self(enc.0) + } +} + +impl EncKeys { + pub fn new(key: &Key) -> Self { + Self(expand(key)) + } +} + +impl BlockCipherEncrypt for crate::Kuznyechik { + fn encrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut EncBackend(&self.keys.0)); + } +} + +impl BlockCipherDecrypt for crate::Kuznyechik { + fn decrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut DecBackend(&self.keys.0)); + } +} + +impl BlockCipherEncrypt for crate::KuznyechikEnc { + fn encrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut EncBackend(&self.keys.0)); + } +} + +impl BlockCipherDecrypt for crate::KuznyechikDec { + fn decrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut DecBackend(&self.keys.0)); + } +} diff --git a/kuznyechik/src/sse2/consts.rs b/kuznyechik/src/fused_tables/consts.rs similarity index 55% rename from kuznyechik/src/sse2/consts.rs rename to kuznyechik/src/fused_tables/consts.rs index a6908416..bbdd1330 100644 --- a/kuznyechik/src/sse2/consts.rs +++ b/kuznyechik/src/fused_tables/consts.rs @@ -1,5 +1,13 @@ +// TODO: use u128 tables after MSRV is bumped to 1.77 or higher #[repr(align(16))] -pub struct Align16(pub T); +pub(crate) struct Align16(pub T); + +impl Align16 { + #[allow(dead_code)] + pub fn as_ptr(&self) -> *const u128 { + self as *const Self as *const u128 + } +} pub type Table = Align16<[u8; 16 * 4096]>; diff --git a/kuznyechik/src/sse2/dec_table.bin b/kuznyechik/src/fused_tables/dec_table.bin similarity index 100% rename from kuznyechik/src/sse2/dec_table.bin rename to kuznyechik/src/fused_tables/dec_table.bin diff --git a/kuznyechik/src/sse2/enc_table.bin b/kuznyechik/src/fused_tables/enc_table.bin similarity index 100% rename from kuznyechik/src/sse2/enc_table.bin rename to kuznyechik/src/fused_tables/enc_table.bin diff --git a/kuznyechik/src/sse2/rkey_gen.bin b/kuznyechik/src/fused_tables/rkey_gen.bin similarity index 100% rename from kuznyechik/src/sse2/rkey_gen.bin rename to kuznyechik/src/fused_tables/rkey_gen.bin diff --git a/kuznyechik/src/lib.rs b/kuznyechik/src/lib.rs index d94ef04d..9f31834c 100644 --- a/kuznyechik/src/lib.rs +++ b/kuznyechik/src/lib.rs @@ -13,6 +13,7 @@ //! You can modify crate using the following configuration flag: //! //! - `kuznyechik_force_soft`: force software implementation. +//! - `kuznyechik_compact_soft`: use compact software implementation. //! //! It can be enabled using `RUSTFLAGS` environmental variable //! (e.g. `RUSTFLAGS="--cfg kuznyechik_force_soft"`) or by modifying @@ -25,7 +26,7 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg" )] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![warn(missing_docs, rust_2018_idioms)] #![allow(clippy::needless_range_loop, clippy::transmute_ptr_to_ptr)] @@ -33,27 +34,31 @@ pub use cipher; use cipher::{ array::Array, consts::{U16, U32}, + zeroize::ZeroizeOnDrop, + AlgorithmName, BlockCipher, BlockSizeUser, KeyInit, KeySizeUser, }; +use core::fmt; mod consts; -#[cfg(all( - any(target_arch = "x86_64", target_arch = "x86"), - target_feature = "sse2", - not(kuznyechik_force_soft), -))] -#[path = "sse2/mod.rs"] -mod imp; +cfg_if::cfg_if!( + if #[cfg(all( + any(target_arch = "x86_64", target_arch = "x86"), + target_feature = "sse2", + not(kuznyechik_force_soft), + ))] { + mod sse2; + use sse2 as imp; + } else if #[cfg(kuznyechik_compact_soft)] { + mod compact_soft; + use compact_soft as imp; + } else { + mod big_soft; + use big_soft as imp; + } +); -#[cfg(not(all( - any(target_arch = "x86_64", target_arch = "x86"), - target_feature = "sse2", - not(kuznyechik_force_soft), -)))] -#[path = "soft/mod.rs"] -mod imp; - -pub use imp::{Kuznyechik, KuznyechikDec, KuznyechikEnc}; +use imp::{DecKeys, EncDecKeys, EncKeys}; type BlockSize = U16; type KeySize = U32; @@ -62,3 +67,178 @@ type KeySize = U32; pub type Block = Array; /// 256-bit Kuznyechik key pub type Key = Array; + +/// Kuznyechik (GOST R 34.12-2015) block cipher +#[derive(Clone)] +pub struct Kuznyechik { + keys: EncDecKeys, +} + +impl BlockCipher for Kuznyechik {} + +impl KeySizeUser for Kuznyechik { + type KeySize = KeySize; +} + +impl BlockSizeUser for Kuznyechik { + type BlockSize = BlockSize; +} + +impl KeyInit for Kuznyechik { + fn new(key: &Key) -> Self { + let enc_keys = EncKeys::new(key); + let keys = enc_keys.into(); + Self { keys } + } +} + +impl From for Kuznyechik { + #[inline] + fn from(enc: KuznyechikEnc) -> Kuznyechik { + let keys = enc.keys.clone().into(); + Self { keys } + } +} + +impl From<&KuznyechikEnc> for Kuznyechik { + #[inline] + fn from(enc: &KuznyechikEnc) -> Kuznyechik { + let keys = enc.keys.clone().into(); + Self { keys } + } +} + +impl fmt::Debug for Kuznyechik { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Kuznyechik { ... }") + } +} + +impl AlgorithmName for Kuznyechik { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Kuznyechik") + } +} + +impl Drop for Kuznyechik { + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + unsafe { + cipher::zeroize::zeroize_flat_type(self) + } + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for Kuznyechik {} + +/// Kuznyechik (GOST R 34.12-2015) block cipher (encrypt-only) +#[derive(Clone)] +pub struct KuznyechikEnc { + keys: EncKeys, +} + +impl BlockCipher for KuznyechikEnc {} + +impl KeySizeUser for KuznyechikEnc { + type KeySize = KeySize; +} + +impl BlockSizeUser for KuznyechikEnc { + type BlockSize = BlockSize; +} + +impl KeyInit for KuznyechikEnc { + fn new(key: &Key) -> Self { + let keys = EncKeys::new(key); + Self { keys } + } +} + +impl fmt::Debug for KuznyechikEnc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("KuznyechikEnc { ... }") + } +} + +impl AlgorithmName for KuznyechikEnc { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Kuznyechik") + } +} + +impl Drop for KuznyechikEnc { + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + unsafe { + cipher::zeroize::zeroize_flat_type(self) + } + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for KuznyechikEnc {} + +/// Kuznyechik (GOST R 34.12-2015) block cipher (decrypt-only) +#[derive(Clone)] +pub struct KuznyechikDec { + keys: DecKeys, +} + +impl BlockCipher for KuznyechikDec {} + +impl KeySizeUser for KuznyechikDec { + type KeySize = KeySize; +} + +impl BlockSizeUser for KuznyechikDec { + type BlockSize = BlockSize; +} + +impl KeyInit for KuznyechikDec { + fn new(key: &Key) -> Self { + let enc_keys = EncKeys::new(key); + let keys = enc_keys.into(); + Self { keys } + } +} + +impl From for KuznyechikDec { + #[inline] + fn from(enc: KuznyechikEnc) -> KuznyechikDec { + let keys = enc.keys.clone().into(); + Self { keys } + } +} + +impl From<&KuznyechikEnc> for KuznyechikDec { + #[inline] + fn from(enc: &KuznyechikEnc) -> KuznyechikDec { + let keys = enc.keys.clone().into(); + Self { keys } + } +} + +impl fmt::Debug for KuznyechikDec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("KuznyechikDec { ... }") + } +} + +impl AlgorithmName for KuznyechikDec { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Kuznyechik") + } +} + +impl Drop for KuznyechikDec { + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + unsafe { + cipher::zeroize::zeroize_flat_type(self) + } + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for KuznyechikDec {} diff --git a/kuznyechik/src/soft/mod.rs b/kuznyechik/src/soft/mod.rs deleted file mode 100644 index 8cb35ef1..00000000 --- a/kuznyechik/src/soft/mod.rs +++ /dev/null @@ -1,210 +0,0 @@ -use crate::{BlockSize, Key, KeySize}; -use cipher::{ - AlgorithmName, BlockCipher, BlockCipherDecrypt, BlockCipherEncrypt, BlockClosure, - BlockSizeUser, KeyInit, KeySizeUser, -}; -use core::fmt; - -#[cfg(feature = "zeroize")] -use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; - -mod backends; -mod consts; - -use backends::{DecBackend, EncBackend, RoundKeys}; - -/// Kuznyechik (GOST R 34.12-2015) block cipher -#[derive(Clone)] -pub struct Kuznyechik { - keys: RoundKeys, -} - -impl BlockCipher for Kuznyechik {} - -impl KeySizeUser for Kuznyechik { - type KeySize = KeySize; -} - -impl BlockSizeUser for Kuznyechik { - type BlockSize = BlockSize; -} - -impl KeyInit for Kuznyechik { - fn new(key: &Key) -> Self { - Self { - keys: backends::expand(key), - } - } -} - -impl From for Kuznyechik { - #[inline] - fn from(enc: KuznyechikEnc) -> Kuznyechik { - Self { keys: enc.keys } - } -} - -impl From<&KuznyechikEnc> for Kuznyechik { - #[inline] - fn from(enc: &KuznyechikEnc) -> Kuznyechik { - Self { keys: enc.keys } - } -} - -impl BlockCipherEncrypt for Kuznyechik { - fn encrypt_with_backend(&self, f: impl BlockClosure) { - f.call(&mut EncBackend(&self.keys)); - } -} - -impl BlockCipherDecrypt for Kuznyechik { - fn decrypt_with_backend(&self, f: impl BlockClosure) { - f.call(&mut DecBackend(&self.keys)); - } -} - -impl fmt::Debug for Kuznyechik { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("Kuznyechik { ... }") - } -} - -impl AlgorithmName for Kuznyechik { - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Kuznyechik") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for Kuznyechik { - fn drop(&mut self) { - self.keys.iter_mut().for_each(|key| key.zeroize()); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for Kuznyechik {} - -/// Kuznyechik (GOST R 34.12-2015) block cipher (encrypt-only) -#[derive(Clone)] -pub struct KuznyechikEnc { - keys: RoundKeys, -} - -impl BlockCipher for KuznyechikEnc {} - -impl KeySizeUser for KuznyechikEnc { - type KeySize = KeySize; -} - -impl BlockSizeUser for KuznyechikEnc { - type BlockSize = BlockSize; -} - -impl KeyInit for KuznyechikEnc { - fn new(key: &Key) -> Self { - Self { - keys: backends::expand(key), - } - } -} - -impl BlockCipherEncrypt for KuznyechikEnc { - fn encrypt_with_backend(&self, f: impl BlockClosure) { - f.call(&mut EncBackend(&self.keys)); - } -} - -impl fmt::Debug for KuznyechikEnc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("KuznyechikEnc { ... }") - } -} - -impl AlgorithmName for KuznyechikEnc { - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Kuznyechik") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for KuznyechikEnc { - fn drop(&mut self) { - self.keys.iter_mut().for_each(|key| key.zeroize()); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for KuznyechikEnc {} - -/// Kuznyechik (GOST R 34.12-2015) block cipher (decrypt-only) -#[derive(Clone)] -pub struct KuznyechikDec { - keys: RoundKeys, -} - -impl BlockCipher for KuznyechikDec {} - -impl KeySizeUser for KuznyechikDec { - type KeySize = KeySize; -} - -impl BlockSizeUser for KuznyechikDec { - type BlockSize = BlockSize; -} - -impl KeyInit for KuznyechikDec { - fn new(key: &Key) -> Self { - Self { - keys: backends::expand(key), - } - } -} - -impl From for KuznyechikDec { - #[inline] - fn from(enc: KuznyechikEnc) -> KuznyechikDec { - Self { keys: enc.keys } - } -} - -impl From<&KuznyechikEnc> for KuznyechikDec { - #[inline] - fn from(enc: &KuznyechikEnc) -> KuznyechikDec { - Self { keys: enc.keys } - } -} - -impl BlockCipherDecrypt for KuznyechikDec { - fn decrypt_with_backend(&self, f: impl BlockClosure) { - f.call(&mut DecBackend(&self.keys)); - } -} - -impl fmt::Debug for KuznyechikDec { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("KuznyechikDec { ... }") - } -} - -impl AlgorithmName for KuznyechikDec { - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Kuznyechik") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for KuznyechikDec { - fn drop(&mut self) { - self.keys.iter_mut().for_each(|key| key.zeroize()); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for KuznyechikDec {} diff --git a/kuznyechik/src/sse2/mod.rs b/kuznyechik/src/sse2/mod.rs index 812ef08d..26f27004 100644 --- a/kuznyechik/src/sse2/mod.rs +++ b/kuznyechik/src/sse2/mod.rs @@ -1,225 +1,65 @@ //! SSE2-based implementation based on -use crate::{BlockSize, Key, KeySize}; -use cipher::{ - AlgorithmName, BlockCipher, BlockCipherDecrypt, BlockCipherEncrypt, BlockClosure, - BlockSizeUser, KeyInit, KeySizeUser, -}; -use core::fmt; - -#[cfg(feature = "zeroize")] -use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; +use crate::{BlockSize, Key}; +use cipher::{BlockCipherDecrypt, BlockCipherEncrypt, BlockClosure}; mod backends; +#[path = "../fused_tables/consts.rs"] mod consts; use backends::{expand_enc_keys, inv_enc_keys, DecBackend, EncBackend, RoundKeys}; -/// Kuznyechik (GOST R 34.12-2015) block cipher #[derive(Clone)] -pub struct Kuznyechik { - enc_keys: RoundKeys, - dec_keys: RoundKeys, -} - -impl BlockCipher for Kuznyechik {} - -impl KeySizeUser for Kuznyechik { - type KeySize = KeySize; -} - -impl BlockSizeUser for Kuznyechik { - type BlockSize = BlockSize; +pub(crate) struct EncDecKeys { + enc: RoundKeys, + dec: RoundKeys, } +#[derive(Clone)] +pub(crate) struct EncKeys(RoundKeys); +#[derive(Clone)] +pub(crate) struct DecKeys(RoundKeys); -impl KeyInit for Kuznyechik { - fn new(key: &Key) -> Self { - let enc_keys = expand_enc_keys(key); - let dec_keys = inv_enc_keys(&enc_keys); - Self { dec_keys, enc_keys } +impl EncKeys { + pub fn new(key: &Key) -> Self { + Self(expand_enc_keys(key)) } } -impl From for Kuznyechik { - #[inline] - fn from(enc: KuznyechikEnc) -> Kuznyechik { +impl From for EncDecKeys { + fn from(enc: EncKeys) -> Self { Self { - dec_keys: inv_enc_keys(&enc.keys), - enc_keys: enc.keys, + dec: inv_enc_keys(&enc.0), + enc: enc.0, } } } -impl From<&KuznyechikEnc> for Kuznyechik { - #[inline] - fn from(enc: &KuznyechikEnc) -> Kuznyechik { - Self { - dec_keys: inv_enc_keys(&enc.keys), - enc_keys: enc.keys, - } +impl From for DecKeys { + fn from(enc: EncKeys) -> Self { + Self(inv_enc_keys(&enc.0)) } } -impl BlockCipherEncrypt for Kuznyechik { +impl BlockCipherEncrypt for crate::Kuznyechik { fn encrypt_with_backend(&self, f: impl BlockClosure) { - f.call(&mut EncBackend(&self.enc_keys)); + f.call(&mut EncBackend(&self.keys.enc)); } } -impl BlockCipherDecrypt for Kuznyechik { +impl BlockCipherDecrypt for crate::Kuznyechik { fn decrypt_with_backend(&self, f: impl BlockClosure) { - f.call(&mut DecBackend(&self.dec_keys)); - } -} - -impl fmt::Debug for Kuznyechik { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("Kuznyechik { ... }") - } -} - -impl AlgorithmName for Kuznyechik { - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Kuznyechik") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for Kuznyechik { - fn drop(&mut self) { - self.enc_keys.zeroize(); - self.dec_keys.zeroize(); + f.call(&mut DecBackend(&self.keys.dec)); } } -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for Kuznyechik {} - -/// Kuznyechik (GOST R 34.12-2015) block cipher (encrypt-only) -#[derive(Clone)] -pub struct KuznyechikEnc { - keys: RoundKeys, -} - -impl BlockCipher for KuznyechikEnc {} - -impl KeySizeUser for KuznyechikEnc { - type KeySize = KeySize; -} - -impl BlockSizeUser for KuznyechikEnc { - type BlockSize = BlockSize; -} - -impl KeyInit for KuznyechikEnc { - fn new(key: &Key) -> Self { - Self { - keys: expand_enc_keys(key), - } - } -} - -impl BlockCipherEncrypt for KuznyechikEnc { +impl BlockCipherEncrypt for crate::KuznyechikEnc { fn encrypt_with_backend(&self, f: impl BlockClosure) { - f.call(&mut EncBackend(&self.keys)); - } -} - -impl fmt::Debug for KuznyechikEnc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("KuznyechikEnc { ... }") - } -} - -impl AlgorithmName for KuznyechikEnc { - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Kuznyechik") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for KuznyechikEnc { - fn drop(&mut self) { - self.keys.zeroize(); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for KuznyechikEnc {} - -/// Kuznyechik (GOST R 34.12-2015) block cipher (decrypt-only) -#[derive(Clone)] -pub struct KuznyechikDec { - keys: RoundKeys, -} - -impl BlockCipher for KuznyechikDec {} - -impl KeySizeUser for KuznyechikDec { - type KeySize = KeySize; -} - -impl BlockSizeUser for KuznyechikDec { - type BlockSize = BlockSize; -} - -impl KeyInit for KuznyechikDec { - fn new(key: &Key) -> Self { - let enc_keys = expand_enc_keys(key); - Self { - keys: inv_enc_keys(&enc_keys), - } - } -} - -impl From for KuznyechikDec { - #[inline] - fn from(enc: KuznyechikEnc) -> KuznyechikDec { - Self { - keys: inv_enc_keys(&enc.keys), - } - } -} - -impl From<&KuznyechikEnc> for KuznyechikDec { - #[inline] - fn from(enc: &KuznyechikEnc) -> KuznyechikDec { - Self { - keys: inv_enc_keys(&enc.keys), - } + f.call(&mut EncBackend(&self.keys.0)); } } -impl BlockCipherDecrypt for KuznyechikDec { +impl BlockCipherDecrypt for crate::KuznyechikDec { fn decrypt_with_backend(&self, f: impl BlockClosure) { - f.call(&mut DecBackend(&self.keys)); + f.call(&mut DecBackend(&self.keys.0)); } } - -impl fmt::Debug for KuznyechikDec { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("KuznyechikDec { ... }") - } -} - -impl AlgorithmName for KuznyechikDec { - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Kuznyechik") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for KuznyechikDec { - fn drop(&mut self) { - self.keys.zeroize(); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for KuznyechikDec {}