use super::circuits::{PERM_BIT, block_sbox};
use super::{BITS_PER_BYTE, BLOCK_BITS, BLOCK_BYTES, Word};
const ROUNDS: usize = 56;
type ByteSlice = [Word; BITS_PER_BYTE];
type BlockSlice = [ByteSlice; BLOCK_BYTES];
pub(super) struct BitslicedBlock {
sch: [u8; ROUNDS],
}
#[inline]
fn permute(s: &ByteSlice) -> ByteSlice {
let mut p = [0 as Word; BITS_PER_BYTE];
for (bit, word) in s.iter().enumerate() {
p[PERM_BIT[bit]] = *word;
}
p
}
#[inline]
fn keyed_sbox(byte: &ByteSlice, key: u8) -> ByteSlice {
let mut input = *byte;
for (bit, word) in input.iter_mut().enumerate() {
if (key >> bit) & 1 == 1 {
*word = !*word;
}
}
block_sbox(&input)
}
#[inline]
fn xor(a: &ByteSlice, b: &ByteSlice) -> ByteSlice {
let mut o = [0 as Word; BITS_PER_BYTE];
for (bit, word) in o.iter_mut().enumerate() {
*word = a[bit] ^ b[bit];
}
o
}
#[inline]
fn unpack(m: &[Word; BLOCK_BITS]) -> BlockSlice {
let mut b = [[0 as Word; BITS_PER_BYTE]; BLOCK_BYTES];
for (byte, slot) in b.iter_mut().enumerate() {
for (bit, word) in slot.iter_mut().enumerate() {
*word = m[byte * BITS_PER_BYTE + bit];
}
}
b
}
#[inline]
fn pack(b: &BlockSlice, m: &mut [Word; BLOCK_BITS]) {
for (byte, slot) in b.iter().enumerate() {
for (bit, word) in slot.iter().enumerate() {
m[byte * BITS_PER_BYTE + bit] = *word;
}
}
}
impl BitslicedBlock {
pub(super) fn new(sch: [u8; ROUNDS]) -> Self {
Self { sch }
}
pub(super) fn encrypt(&self, m: &mut [Word; BLOCK_BITS]) {
let mut b = unpack(m);
for round in 0..ROUNDS {
let s = keyed_sbox(&b[7], self.sch[round]);
let ps = permute(&s);
let old0 = b[0];
let old1 = b[1];
b[1] = xor(&b[2], &old0);
b[2] = xor(&b[3], &old0);
b[3] = xor(&b[4], &old0);
b[4] = b[5];
b[5] = xor(&b[6], &ps);
b[6] = b[7];
b[7] = xor(&old0, &s);
b[0] = old1;
}
pack(&b, m);
}
pub(super) fn decrypt(&self, m: &mut [Word; BLOCK_BITS]) {
let mut b = unpack(m);
for round in (0..ROUNDS).rev() {
let s = keyed_sbox(&b[6], self.sch[round]);
let ps = permute(&s);
let out = xor(&b[7], &s);
b[7] = b[6];
b[6] = xor(&b[5], &ps);
b[5] = b[4];
b[4] = xor(&b[3], &out);
b[3] = xor(&b[2], &out);
b[2] = xor(&b[1], &out);
b[1] = b[0];
b[0] = out;
}
pack(&b, m);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bitsliced::{LANES, transpose};
use crate::block::BlockCipher;
use crate::key::ControlWord;
#[test]
fn matches_the_scalar_block_cipher_in_every_lane() {
let cw = ControlWord::from_bytes([0x55, 0xfd, 0x78, 0x15, 0x27, 0xec, 0xa2, 0x29]);
let sch = cw.expand_block();
let scalar = BlockCipher::new(sch);
let bs = BitslicedBlock::new(sch);
let mut blocks = [[0u8; BLOCK_BYTES]; LANES];
let mut seed: u64 = 0x0123_4567_89ab_cdef;
for block in blocks.iter_mut() {
seed = seed
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
*block = seed.to_le_bytes();
}
for encrypt in [true, false] {
let mut m = [0u64; LANES];
for (lane, block) in blocks.iter().enumerate() {
m[lane] = u64::from_le_bytes(*block);
}
transpose(&mut m);
if encrypt {
bs.encrypt(&mut m);
} else {
bs.decrypt(&mut m);
}
transpose(&mut m);
for (lane, block) in blocks.iter().enumerate() {
let mut want = *block;
if encrypt {
scalar.encrypt_block(&mut want);
} else {
scalar.decrypt_block(&mut want);
}
assert_eq!(
m[lane].to_le_bytes(),
want,
"lane {lane} disagrees with the scalar path (encrypt={encrypt})"
);
}
}
}
}