Skip to content

Commit

Permalink
kuznyechik: implement new software backend (#443)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
newpavlov authored Aug 2, 2024
1 parent cfbb0a5 commit 5aff007
Show file tree
Hide file tree
Showing 15 changed files with 534 additions and 449 deletions.
40 changes: 8 additions & 32 deletions .github/workflows/kuznyechik.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
1 change: 1 addition & 0 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion kuznyechik/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand All @@ -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
Expand Down
170 changes: 170 additions & 0 deletions kuznyechik/src/big_soft/backends.rs
Original file line number Diff line number Diff line change
@@ -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<Self>>) {
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());
}
}
63 changes: 63 additions & 0 deletions kuznyechik/src/big_soft/mod.rs
Original file line number Diff line number Diff line change
@@ -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<EncKeys> for EncDecKeys {
fn from(enc: EncKeys) -> Self {
Self {
dec: inv_enc_keys(&enc.0),
enc: enc.0,
}
}
}

impl From<EncKeys> 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<BlockSize = BlockSize>) {
f.call(&mut EncBackend(&self.keys.enc));
}
}

impl BlockCipherDecrypt for crate::Kuznyechik {
fn decrypt_with_backend(&self, f: impl BlockClosure<BlockSize = BlockSize>) {
f.call(&mut DecBackend(&self.keys.dec));
}
}

impl BlockCipherEncrypt for crate::KuznyechikEnc {
fn encrypt_with_backend(&self, f: impl BlockClosure<BlockSize = BlockSize>) {
f.call(&mut EncBackend(&self.keys.0));
}
}

impl BlockCipherDecrypt for crate::KuznyechikDec {
fn decrypt_with_backend(&self, f: impl BlockClosure<BlockSize = BlockSize>) {
f.call(&mut DecBackend(&self.keys.0));
}
}
File renamed without changes.
File renamed without changes.
56 changes: 56 additions & 0 deletions kuznyechik/src/compact_soft/mod.rs
Original file line number Diff line number Diff line change
@@ -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<EncKeys> for EncDecKeys {
fn from(enc: EncKeys) -> Self {
Self(enc.0)
}
}

impl From<EncKeys> 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<BlockSize = BlockSize>) {
f.call(&mut EncBackend(&self.keys.0));
}
}

impl BlockCipherDecrypt for crate::Kuznyechik {
fn decrypt_with_backend(&self, f: impl BlockClosure<BlockSize = BlockSize>) {
f.call(&mut DecBackend(&self.keys.0));
}
}

impl BlockCipherEncrypt for crate::KuznyechikEnc {
fn encrypt_with_backend(&self, f: impl BlockClosure<BlockSize = BlockSize>) {
f.call(&mut EncBackend(&self.keys.0));
}
}

impl BlockCipherDecrypt for crate::KuznyechikDec {
fn decrypt_with_backend(&self, f: impl BlockClosure<BlockSize = BlockSize>) {
f.call(&mut DecBackend(&self.keys.0));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
// TODO: use u128 tables after MSRV is bumped to 1.77 or higher
#[repr(align(16))]
pub struct Align16<T>(pub T);
pub(crate) struct Align16<T>(pub T);

impl<T> Align16<T> {
#[allow(dead_code)]
pub fn as_ptr(&self) -> *const u128 {
self as *const Self as *const u128
}
}

pub type Table = Align16<[u8; 16 * 4096]>;

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 5aff007

Please sign in to comment.