Skip to content

Commit

Permalink
serpent: improve code
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed Aug 14, 2024
1 parent 58e7dbb commit 23b91b2
Showing 1 changed file with 69 additions and 103 deletions.
172 changes: 69 additions & 103 deletions serpent/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,20 @@ use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
mod consts;
use consts::{PHI, ROUNDS, S, S_INVERSE};

type Key = [u8; 16];
type Subkeys = [Key; ROUNDS + 1];
type Block128 = [u8; 16];
type Word = [u8; 16];
type Words = [u32; 4];
type RoundKeys = [Words; ROUNDS + 1];

/// Serpent block cipher.
#[derive(Clone)]
pub struct Serpent {
k: Subkeys,
round_keys: RoundKeys,
}

fn get_bit(x: usize, i: usize) -> u8 {
(x >> i) as u8 & 0x01
}

fn linear_transform_bitslice(input: Block128, output: &mut Block128) {
let mut words: [u32; 4] = read_u32s(&input);

fn linear_transform_bitslice(mut words: Words) -> Words {
words[0] = words[0].rotate_left(13);
words[2] = words[2].rotate_left(3);
words[1] ^= words[0] ^ words[2];
Expand All @@ -64,13 +60,10 @@ fn linear_transform_bitslice(input: Block128, output: &mut Block128) {
words[2] = words[2] ^ words[3] ^ (words[1] << 7);
words[0] = words[0].rotate_left(5);
words[2] = words[2].rotate_left(22);

write_u32s(&words, output);
words
}

fn linear_transform_inverse_bitslice(input: Block128, output: &mut Block128) {
let mut words: [u32; 4] = read_u32s(&input);

fn linear_transform_inverse_bitslice(mut words: Words) -> Words {
words[2] = words[2].rotate_right(22);
words[0] = words[0].rotate_right(5);
words[2] = words[2] ^ words[3] ^ (words[1] << 7);
Expand All @@ -81,35 +74,7 @@ fn linear_transform_inverse_bitslice(input: Block128, output: &mut Block128) {
words[1] ^= words[0] ^ words[2];
words[2] = words[2].rotate_right(3);
words[0] = words[0].rotate_right(13);

write_u32s(&words, output);
}

fn round_bitslice(i: usize, b_i: Block128, k: Subkeys, b_output: &mut Block128) {
let xored_block = xor_block(b_i, k[i]);

let s_i = apply_s_bitslice(i, xored_block);

if i == ROUNDS - 1 {
*b_output = xor_block(s_i, k[ROUNDS]);
} else {
linear_transform_bitslice(s_i, b_output);
}
}

#[allow(clippy::useless_let_if_seq)]
fn round_inverse_bitslice(i: usize, b_i_next: Block128, k: Subkeys, b_output: &mut Block128) {
let mut s_i = [0u8; 16];

if i == ROUNDS - 1 {
s_i = xor_block(b_i_next, k[ROUNDS]);
} else {
linear_transform_inverse_bitslice(b_i_next, &mut s_i);
}

let xored = apply_s_inverse_bitslice(i, s_i);

*b_output = xor_block(xored, k[i]);
words
}

fn apply_s(index: usize, nibble: u8) -> u8 {
Expand All @@ -120,9 +85,7 @@ fn apply_s_inverse(index: usize, nibble: u8) -> u8 {
S_INVERSE[index % 8][nibble as usize]
}

fn apply_s_bitslice(index: usize, word: Word) -> Word {
let [w1, w2, w3, w4] = read_u32s(&word);

fn apply_s_bitslice(index: usize, [w1, w2, w3, w4]: Words) -> Words {
let mut words = [0u32; 4];

for i in 0..32 {
Expand All @@ -139,13 +102,10 @@ fn apply_s_bitslice(index: usize, word: Word) -> Word {
}
}

let mut output = [0u8; 16];
write_u32s(&words, &mut output);
output
words
}

fn apply_s_inverse_bitslice(index: usize, word: Word) -> Word {
let [w1, w2, w3, w4] = read_u32s(&word);
fn apply_s_inverse_bitslice(index: usize, [w1, w2, w3, w4]: Words) -> Words {
let mut words = [0u32; 4];
for i in 0..32 {
let quad = apply_s_inverse(
Expand All @@ -159,35 +119,49 @@ fn apply_s_inverse_bitslice(index: usize, word: Word) -> Word {
words[l] |= u32::from(get_bit(quad as usize, l)) << i;
}
}
let mut output = [0u8; 16];
write_u32s(&words, &mut output);
output
words
}

fn xor_block(b1: Block128, k: Key) -> Block128 {
let mut xored: Block128 = [0u8; 16];
#[inline(always)]
fn xor(b1: Words, k: Words) -> Words {
let mut res = [0u32; 4];
for (i, _) in b1.iter().enumerate() {
xored[i] = b1[i] ^ k[i];
res[i] = b1[i] ^ k[i];
}
xored
res
}

fn expand_key(source: &[u8], len_bits: usize, key: &mut [u8; 32]) {
fn expand_key(source: &[u8], len_bits: usize) -> [u8; 32] {
let mut key = [0u8; 32];
key[..source.len()].copy_from_slice(source);
if len_bits < 256 {
let byte_i = len_bits / 8;
let bit_i = len_bits % 8;
key[byte_i] |= 1 << bit_i;
}
key
}

impl KeySizeUser for Serpent {
type KeySize = U16;
}

impl Serpent {
#[allow(clippy::many_single_char_names)]
fn key_schedule(key: [u8; 32]) -> Subkeys {
impl KeyInit for Serpent {
fn new(key: &cipher::Key<Self>) -> Self {
Self::new_from_slice(key).unwrap()
}

fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
if key.len() < 16 || key.len() > 32 {
return Err(InvalidLength);
}
let key = expand_key(key, key.len() * 8);

let mut words = [0u32; 140];

let words8: [u32; 8] = read_u32s(&key);
words[..8].copy_from_slice(&words8);
for (src, dst) in key.chunks_exact(4).zip(words[..8].iter_mut()) {
*dst = u32::from_le_bytes(src.try_into().unwrap());
}

for i in 0..132 {
let slot = i + 8;
Expand Down Expand Up @@ -219,34 +193,12 @@ impl Serpent {
}
}

let mut sub_keys: Subkeys = [[0u8; 16]; ROUNDS + 1];
for i in 0..r {
let keys: [u32; 4] = k[4 * i..][..4].try_into().unwrap();
write_u32s(&keys, &mut sub_keys[i]);
let mut round_keys: RoundKeys = [[0; 4]; ROUNDS + 1];
for (src, dst) in k.chunks_exact(4).zip(round_keys.iter_mut()) {
dst.copy_from_slice(src);
}

sub_keys
}
}

impl KeySizeUser for Serpent {
type KeySize = U16;
}

impl KeyInit for Serpent {
fn new(key: &cipher::Key<Self>) -> Self {
Self::new_from_slice(key).unwrap()
}

fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
if key.len() < 16 || key.len() > 32 {
return Err(InvalidLength);
}
let mut k = [0u8; 32];
expand_key(key, key.len() * 8, &mut k);
Ok(Serpent {
k: Serpent::key_schedule(k),
})
Ok(Serpent { round_keys })
}
}

Expand All @@ -268,11 +220,19 @@ impl BlockCipherEncrypt for Serpent {
impl BlockCipherEncBackend for Serpent {
#[inline]
fn encrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let mut b = block.clone_in().into();
for i in 0..ROUNDS {
round_bitslice(i, b, self.k, &mut b);
let mut b: [u32; 4] = read_words(block.get_in().into());

for i in 0..ROUNDS - 1 {
let xb = xor(b, self.round_keys[i]);
let s = apply_s_bitslice(i, xb);
b = linear_transform_bitslice(s);
}
*block.get_out() = b.into();

let xb = xor(b, self.round_keys[ROUNDS - 1]);
let s = apply_s_bitslice(ROUNDS - 1, xb);
b = xor(s, self.round_keys[ROUNDS]);

write_words(&b, block.get_out().into());
}
}

Expand All @@ -286,11 +246,19 @@ impl BlockCipherDecrypt for Serpent {
impl BlockCipherDecBackend for Serpent {
#[inline]
fn decrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let mut b = block.clone_in().into();
for i in (0..ROUNDS).rev() {
round_inverse_bitslice(i, b, self.k, &mut b);
let mut b: [u32; 4] = read_words(block.get_in().into());

let s = xor(b, self.round_keys[ROUNDS]);
let xb = apply_s_inverse_bitslice(ROUNDS - 1, s);
b = xor(xb, self.round_keys[ROUNDS - 1]);

for i in (0..ROUNDS - 1).rev() {
let s = linear_transform_inverse_bitslice(b);
let xb = apply_s_inverse_bitslice(i, s);
b = xor(xb, self.round_keys[i]);
}
*block.get_out() = b.into();

write_words(&b, block.get_out().into());
}
}

Expand All @@ -309,24 +277,22 @@ impl AlgorithmName for Serpent {
impl Drop for Serpent {
fn drop(&mut self) {
#[cfg(feature = "zeroize")]
self.k.zeroize();
self.round_keys.zeroize();
}
}

#[cfg(feature = "zeroize")]
impl ZeroizeOnDrop for Serpent {}

fn read_u32s<const N: usize>(src: &[u8]) -> [u32; N] {
assert_eq!(src.len(), 4 * N);
let mut res = [0; N];
fn read_words(src: &[u8; 16]) -> Words {
let mut res = [0; 4];
for (src, dst) in src.chunks_exact(4).zip(res.iter_mut()) {
*dst = u32::from_le_bytes(src.try_into().unwrap());
}
res
}

fn write_u32s<const N: usize>(src: &[u32; N], dst: &mut [u8]) {
assert_eq!(4 * src.len(), dst.len());
fn write_words(src: &Words, dst: &mut [u8; 16]) {
for (src, dst) in src.iter().zip(dst.chunks_exact_mut(4)) {
dst.copy_from_slice(&src.to_le_bytes());
}
Expand Down

0 comments on commit 23b91b2

Please sign in to comment.