From 23b91b2a5a3d201e6a1f091337a06b2fc18f2f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 14 Aug 2024 15:45:39 +0300 Subject: [PATCH] serpent: improve code --- serpent/src/lib.rs | 172 ++++++++++++++++++--------------------------- 1 file changed, 69 insertions(+), 103 deletions(-) diff --git a/serpent/src/lib.rs b/serpent/src/lib.rs index 1f3cf371..90f2ab21 100644 --- a/serpent/src/lib.rs +++ b/serpent/src/lib.rs @@ -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]; @@ -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); @@ -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 { @@ -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 { @@ -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( @@ -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::new_from_slice(key).unwrap() + } + + fn new_from_slice(key: &[u8]) -> Result { + 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; @@ -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::new_from_slice(key).unwrap() - } - - fn new_from_slice(key: &[u8]) -> Result { - 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 }) } } @@ -268,11 +220,19 @@ impl BlockCipherEncrypt for Serpent { impl BlockCipherEncBackend for Serpent { #[inline] fn encrypt_block(&self, mut block: InOut<'_, '_, Block>) { - 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()); } } @@ -286,11 +246,19 @@ impl BlockCipherDecrypt for Serpent { impl BlockCipherDecBackend for Serpent { #[inline] fn decrypt_block(&self, mut block: InOut<'_, '_, Block>) { - 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()); } } @@ -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(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(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()); }