From 75294189f0bec6676011128bc6e42759497baed6 Mon Sep 17 00:00:00 2001 From: SymmetricChaos <42520289+SymmetricChaos@users.noreply.github.com> Date: Sat, 19 Oct 2024 10:37:45 -0400 Subject: [PATCH] twofish --- .../block_ciphers/twofish/functions.rs | 126 +++++++++++++++ .../src/digital/block_ciphers/twofish/mod.rs | 2 + .../block_ciphers/{ => twofish}/twofish.rs | 147 ++++++++---------- 3 files changed, 193 insertions(+), 82 deletions(-) create mode 100644 ciphers/src/digital/block_ciphers/twofish/functions.rs create mode 100644 ciphers/src/digital/block_ciphers/twofish/mod.rs rename ciphers/src/digital/block_ciphers/{ => twofish}/twofish.rs (51%) diff --git a/ciphers/src/digital/block_ciphers/twofish/functions.rs b/ciphers/src/digital/block_ciphers/twofish/functions.rs new file mode 100644 index 00000000..723e205f --- /dev/null +++ b/ciphers/src/digital/block_ciphers/twofish/functions.rs @@ -0,0 +1,126 @@ +// 4-bit SBOX for q0 function +const Q0: [[u8; 16]; 4] = [ + [ + 0x8, 0x1, 0x7, 0xD, 0x6, 0xF, 0x3, 0x2, 0x0, 0xB, 0x5, 0x9, 0xE, 0xC, 0xA, 0x4, + ], + [ + 0xE, 0xC, 0xB, 0x8, 0x1, 0x2, 0x3, 0x5, 0xF, 0x4, 0xA, 0x6, 0x7, 0x0, 0x9, 0xD, + ], + [ + 0xB, 0xA, 0x5, 0xE, 0x6, 0xD, 0x9, 0x0, 0xC, 0x8, 0xF, 0x3, 0x2, 0x4, 0x7, 0x1, + ], + [ + 0xD, 0x7, 0xF, 0x4, 0x1, 0x2, 0x6, 0xE, 0x9, 0xB, 0x3, 0x0, 0x8, 0x5, 0xC, 0xA, + ], +]; + +// 4-bit SBOX for q1 function +const Q1: [[u8; 16]; 4] = [ + [ + 0x2, 0x8, 0xB, 0xD, 0xF, 0x7, 0x6, 0xE, 0x3, 0x1, 0x9, 0x4, 0x0, 0xA, 0xC, 0x5, + ], + [ + 0x1, 0xE, 0x2, 0xB, 0x4, 0xC, 0x3, 0x7, 0x6, 0xD, 0xA, 0x5, 0xF, 0x9, 0x0, 0x8, + ], + [ + 0x4, 0xC, 0x7, 0x5, 0x1, 0x6, 0x9, 0xA, 0x0, 0xE, 0xD, 0x8, 0x2, 0xB, 0x3, 0xF, + ], + [ + 0xB, 0x9, 0x5, 0x1, 0xC, 0x3, 0xD, 0xE, 0x6, 0x4, 0x7, 0xF, 0x2, 0x0, 0x8, 0xA, + ], +]; + +pub const QORD: [[usize; 5]; 4] = [ + [1, 1, 0, 0, 1], + [0, 1, 1, 0, 0], + [0, 0, 0, 1, 1], + [1, 0, 1, 1, 0], +]; + +pub(super) fn q0(n: u8) -> u8 { + let a0 = (n >> 4) & 15; + let b0 = n & 15; + let a1 = a0 ^ b0; + let b1 = a0 ^ ((b0 << 3) | (b0 >> 1)) ^ ((a0 << 3) & 15); // Because the upper 4 bits will be empty the rotation of the lower 4 bits of b0 is always correct + let a2 = Q0[0][a1 as usize]; + let b2 = Q0[1][b1 as usize]; + let a3 = a2 ^ b2; + let b3 = a2 ^ ((b2 << 3) | (b2 >> 1)) ^ ((a2 << 3) & 15); // As for b0 + let a4 = Q0[2][a3 as usize]; + let b4 = Q0[3][b3 as usize]; + (b4 << 4) | a4 +} + +pub(super) fn q1(n: u8) -> u8 { + let a0 = (n >> 4) & 15; + let b0 = n & 15; + let a1 = a0 ^ b0; + let b1 = a0 ^ ((b0 << 3) | (b0 >> 1)) ^ ((8 * a0) & 15); + let a2 = Q1[0][a1 as usize]; + let b2 = Q1[1][b1 as usize]; + let a3 = a2 ^ b2; + let b3 = a2 ^ ((b2 << 3) | (b2 >> 1)) ^ ((8 * a2) & 15); + let a4 = Q1[2][a3 as usize]; + let b4 = Q1[3][b3 as usize]; + (b4 << 4) | a4 +} + +// Maximum Distance Separable Operations +pub(super) fn gf_mult(mut a: u8, mut b: u8, p: u8) -> u8 { + let mut result = 0; + while a > 0 { + if a & 1 == 1 { + result ^= b; + } + a >>= 1; + if b & 0x80 == 0x80 { + b = (b << 1) ^ p; + } else { + b <<= 1; + } + } + result +} + +pub(super) fn mds_column_mult(x: u8, column: usize) -> u32 { + let x5b = gf_mult(x, 0x5b, 0x69); + let xef = gf_mult(x, 0xef, 0x69); + + let v = match column { + 0 => [x, x5b, xef, xef], + 1 => [xef, xef, x5b, x], + 2 => [x5b, xef, x, xef], + 3 => [x5b, x, xef, x5b], + _ => unreachable!(), + }; + u32::from_le_bytes(v) +} + +pub(super) fn mds_mult(y: [u8; 4]) -> u32 { + let mut z = 0; + for i in 0..4 { + z ^= mds_column_mult(y[i], i); + } + z +} + +const RS: [[u8; 8]; 4] = [ + [0x01, 0xa4, 0x55, 0x87, 0x5a, 0x58, 0xdb, 0x9e], + [0xa4, 0x56, 0x82, 0xf3, 0x1e, 0xc6, 0x68, 0xe5], + [0x02, 0xa1, 0xfc, 0xc1, 0x47, 0xae, 0x3d, 0x19], + [0xa4, 0x55, 0x87, 0x5a, 0x58, 0xdb, 0x9e, 0x03], +]; + +pub(super) fn rs_mult(m: &[u8], out: &mut [u8]) { + for i in 0..4 { + out[i] = 0; + for j in 0..8 { + out[i] ^= gf_mult(m[j], RS[i][j], 0x4d); + } + } +} + +// Pseudo-Hadamard Transform +pub(super) fn pht(a: u32, b: u32) -> (u32, u32) { + (a.wrapping_add(b), a.wrapping_add(b << 1)) +} diff --git a/ciphers/src/digital/block_ciphers/twofish/mod.rs b/ciphers/src/digital/block_ciphers/twofish/mod.rs new file mode 100644 index 00000000..9bf62e8c --- /dev/null +++ b/ciphers/src/digital/block_ciphers/twofish/mod.rs @@ -0,0 +1,2 @@ +pub mod functions; +pub mod twofish; diff --git a/ciphers/src/digital/block_ciphers/twofish.rs b/ciphers/src/digital/block_ciphers/twofish/twofish.rs similarity index 51% rename from ciphers/src/digital/block_ciphers/twofish.rs rename to ciphers/src/digital/block_ciphers/twofish/twofish.rs index 6daea311..16d3c2e9 100644 --- a/ciphers/src/digital/block_ciphers/twofish.rs +++ b/ciphers/src/digital/block_ciphers/twofish/twofish.rs @@ -1,76 +1,45 @@ use itertools::Itertools; use utils::byte_formatting::{fill_u32s_le, u32s_to_bytes_le, ByteFormat}; -use super::block_cipher::{BCMode, BCPadding, BlockCipher}; - -// 4-bit SBOX for q0 function -const Q0: [[u8; 16]; 4] = [ - [ - 0x8, 0x1, 0x7, 0xD, 0x6, 0xF, 0x3, 0x2, 0x0, 0xB, 0x5, 0x9, 0xE, 0xC, 0xA, 0x4, - ], - [ - 0xE, 0xC, 0xB, 0x8, 0x1, 0x2, 0x3, 0x5, 0xF, 0x4, 0xA, 0x6, 0x7, 0x0, 0x9, 0xD, - ], - [ - 0xB, 0xA, 0x5, 0xE, 0x6, 0xD, 0x9, 0x0, 0xC, 0x8, 0xF, 0x3, 0x2, 0x4, 0x7, 0x1, - ], - [ - 0xD, 0x7, 0xF, 0x4, 0x1, 0x2, 0x6, 0xE, 0x9, 0xB, 0x3, 0x0, 0x8, 0x5, 0xC, 0xA, - ], -]; - -// 4-bit SBOX for q1 function -const Q1: [[u8; 16]; 4] = [ - [ - 0x2, 0x8, 0xB, 0xD, 0xF, 0x7, 0x6, 0xE, 0x3, 0x1, 0x9, 0x4, 0x0, 0xA, 0xC, 0x5, - ], - [ - 0x1, 0xE, 0x2, 0xB, 0x4, 0xC, 0x3, 0x7, 0x6, 0xD, 0xA, 0x5, 0xF, 0x9, 0x0, 0x8, - ], - [ - 0x4, 0xC, 0x7, 0x5, 0x1, 0x6, 0x9, 0xA, 0x0, 0xE, 0xD, 0x8, 0x2, 0xB, 0x3, 0xF, - ], - [ - 0xB, 0x9, 0x5, 0x1, 0xC, 0x3, 0xD, 0xE, 0x6, 0x4, 0x7, 0xF, 0x2, 0x0, 0x8, 0xA, - ], -]; - -// Each 4-bit nibble is rotated one bit toward the LSB (to the right) -fn nibble_ror_1(x: u8) -> u8 { - (((x) >> 1) & 0x77) | (((x) & 0x11) << 3) -} +use super::{ + super::block_cipher::{BCMode, BCPadding, BlockCipher}, + functions::{mds_mult, pht, q0, q1}, +}; + +fn h(x: u32, list: &[u8], offset: usize) -> u32 { + let mut y = x.to_le_bytes(); + if K == 4 { + y[0] = q1(y[0]) ^ list[4 * (6 + offset + 0)]; + y[1] = q0(y[1]) ^ list[4 * (6 + offset + 1)]; + y[2] = q0(y[2]) ^ list[4 * (6 + offset + 2)]; + y[3] = q1(y[3]) ^ list[4 * (6 + offset + 3)]; + } -fn q0(n: u8) -> u8 { - let a0 = (n >> 4) & 15; - let b0 = n & 15; - let a1 = a0 ^ b0; - let b1 = a0 ^ nibble_ror_1(b0) ^ ((8 * a0) & 15); - let a2 = Q0[0][a1 as usize]; - let b2 = Q0[1][b1 as usize]; - let a3 = a2 ^ b2; - let b3 = a2 ^ nibble_ror_1(b2) ^ ((8 * a2) & 15); - let a4 = Q0[2][a3 as usize]; - let b4 = Q0[3][b3 as usize]; - (b4 << 4) | a4 -} + if K >= 3 { + y[0] = q1(y[0]) ^ list[4 * (4 + offset + 0)]; + y[1] = q1(y[1]) ^ list[4 * (4 + offset + 1)]; + y[2] = q0(y[2]) ^ list[4 * (4 + offset + 2)]; + y[3] = q0(y[3]) ^ list[4 * (4 + offset + 3)]; + } -fn q1(n: u8) -> u8 { - let a0 = (n >> 4) & 15; - let b0 = n & 15; - let a1 = a0 ^ b0; - let b1 = a0 ^ nibble_ror_1(b0) ^ ((8 * a0) & 15); - let a2 = Q1[0][a1 as usize]; - let b2 = Q1[1][b1 as usize]; - let a3 = a2 ^ b2; - let b3 = a2 ^ nibble_ror_1(b2) ^ ((8 * a2) & 15); - let a4 = Q1[2][a3 as usize]; - let b4 = Q1[3][b3 as usize]; - (b4 << 4) | a4 + let a = 4 * (2 + offset); + let b = 4 * offset; + + y[0] = q1(q0(q0(y[0]) ^ list[a + 0]) ^ list[b + 0]); + y[1] = q0(q1(q1(y[1]) ^ list[a + 1]) ^ list[b + 1]); + y[2] = q1(q1(q0(y[2]) ^ list[a + 2]) ^ list[b + 2]); + y[3] = q0(q1(q1(y[3]) ^ list[a + 3]) ^ list[b + 3]); + + mds_mult(y) } -// Pseudo-Hadamard Transform -fn pht(a: u32, b: u32) -> (u32, u32) { - (a.wrapping_add(b), a.wrapping_add(b << 1)) +fn g(n: u32) -> u32 { + let mut out = 0; + let mut x = n.to_le_bytes(); + + for i in 0..4 {} + + out } fn f(a: u32, b: u32, round: usize, subkeys: &[u32; 40]) -> (u32, u32) { @@ -82,11 +51,10 @@ fn f(a: u32, b: u32, round: usize, subkeys: &[u32; 40]) -> (u32, u32) { o } -fn g(n: u32) -> u32 { - let mut x = n.to_le_bytes(); - - todo!() -} +const KEY_BYTES: usize = 16; +const KEY_WORDS: usize = KEY_BYTES / 4; +const K: usize = 2; // Keylength in bits divided by 64 +const START: usize = 2; pub struct TwoFish128 { pub input_format: ByteFormat, @@ -114,11 +82,6 @@ impl Default for TwoFish128 { crate::block_cipher_builders! {TwoFish128, u128} - -const KEY_BYTES: usize = 16; -const KEY_WORDS: usize = KEY_BYTES/4; -const K: usize = 2; // Keylength in bits divided by 64 - impl TwoFish128 { pub fn sbox(&self, n: u32, i: usize) -> u32 { self.sboxes[i][n as usize] @@ -131,7 +94,7 @@ impl TwoFish128 { // Tale the even and odd words respectively let m_e = words.into_iter().step_by(2).collect_vec(); - let m_o = words.into_iter().skip(1).step_by(2).collect_vec();; + let m_o = words.into_iter().skip(1).step_by(2).collect_vec(); } pub fn ksa_u32(&mut self, key: [u32; KEY_WORDS]) {} @@ -145,10 +108,6 @@ impl TwoFish128 { self.ksa_u32(bytes); self } - - fn h(x: u32, list: [u32; K]) -> u32 { - todo!() - } } impl BlockCipher<16> for TwoFish128 { @@ -156,6 +115,18 @@ impl BlockCipher<16> for TwoFish128 { let mut block = [0; 4]; fill_u32s_le(&mut block, bytes); + // Input Whitening + for i in 0..4 { + block[i] ^= self.subkeys[i] + } + + for i in 0..8 {} + + // Output Whitening + for i in 4..8 { + block[i - 4] ^= self.subkeys[i] + } + u32s_to_bytes_le(bytes, &block); } @@ -163,10 +134,22 @@ impl BlockCipher<16> for TwoFish128 { let mut block = [0; 4]; fill_u32s_le(&mut block, bytes); + // Input Whitening + for i in 0..4 { + block[i] ^= self.subkeys[i] + } + + for i in (0..8).rev() {} + + // Output Whitening + for i in 4..8 { + block[i - 4] ^= self.subkeys[i] + } + u32s_to_bytes_le(bytes, &block); } } // crate::test_block_cipher!( -// ) \ No newline at end of file +// )