use rand::rngs::StdRng;
use rand::{thread_rng, Rng, SeedableRng};
const NUM_OF_BOXES: usize = 49; const BLOCK_SIZE: usize = 256; const KEY_SIZE: usize = 80; const ROUNDS: usize = 12;
#[derive(Clone, Debug, PartialEq)]
struct BitVec {
words: Vec<u32>,
bits: usize,
}
impl BitVec {
fn new(bits: usize) -> Self {
let words = bits.div_ceil(32);
BitVec {
words: vec![0u32; words],
bits,
}
}
fn get(&self, index: usize) -> bool {
if index >= self.bits {
return false;
}
let word_idx = index / 32;
let bit_idx = index % 32;
(self.words[word_idx] >> bit_idx) & 1 == 1
}
fn set(&mut self, index: usize, value: bool) {
if index >= self.bits {
return;
}
let word_idx = index / 32;
let bit_idx = index % 32;
if value {
self.words[word_idx] |= 1 << bit_idx;
} else {
self.words[word_idx] &= !(1 << bit_idx);
}
}
fn count_ones(&self) -> u32 {
self.words.iter().map(|w| w.count_ones()).sum()
}
fn xor_assign(&mut self, other: &BitVec) {
for (a, b) in self.words.iter_mut().zip(other.words.iter()) {
*a ^= b;
}
}
fn and(&self, other: &BitVec) -> BitVec {
let mut result = BitVec::new(self.bits);
for i in 0..self.words.len().min(other.words.len()) {
result.words[i] = self.words[i] & other.words[i];
}
result
}
fn from_u128(value: u128, bits: usize) -> BitVec {
let mut result = BitVec::new(bits);
let bytes = value.to_le_bytes();
for (i, &byte) in bytes.iter().enumerate() {
for j in 0..8 {
if i * 8 + j < bits {
result.set(i * 8 + j, (byte >> j) & 1 == 1);
}
}
}
result
}
fn to_u128(&self) -> u128 {
let mut bytes = [0u8; 16];
for (i, byte) in bytes.iter_mut().enumerate() {
for j in 0..8 {
if i * 8 + j < self.bits && self.get(i * 8 + j) {
*byte |= 1 << j;
}
}
}
u128::from_le_bytes(bytes)
}
}
pub struct LowMC {
sbox: [u8; 8],
inv_sbox: [u8; 8],
lin_matrices: Vec<Vec<BitVec>>,
inv_lin_matrices: Vec<Vec<BitVec>>,
round_constants: Vec<BitVec>,
key_matrices: Vec<Vec<BitVec>>,
round_keys: Vec<BitVec>,
key: BitVec,
}
impl LowMC {
pub fn new(key: u128) -> Self {
let mut cipher = LowMC {
sbox: [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02],
inv_sbox: [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04],
lin_matrices: Vec::new(),
inv_lin_matrices: Vec::new(),
round_constants: Vec::new(),
key_matrices: Vec::new(),
round_keys: Vec::new(),
key: BitVec::from_u128(key, KEY_SIZE),
};
cipher.instantiate_lowmc(key);
cipher.keyschedule();
cipher
}
pub fn generate_random_key() -> u128 {
let mut rng = thread_rng();
rng.gen()
}
pub fn encrypt_full(&self, low: u128, high: u128) -> (u128, u128) {
let mut state = BitVec::new(BLOCK_SIZE);
let low_bytes = low.to_le_bytes();
for (i, &byte) in low_bytes.iter().enumerate() {
for j in 0..8 {
if i * 8 + j < 128 {
state.set(i * 8 + j, (byte >> j) & 1 == 1);
}
}
}
let high_bytes = high.to_le_bytes();
for (i, &byte) in high_bytes.iter().enumerate() {
for j in 0..8 {
if 128 + i * 8 + j < BLOCK_SIZE {
state.set(128 + i * 8 + j, (byte >> j) & 1 == 1);
}
}
}
state.xor_assign(&self.round_keys[0]);
for round in 0..ROUNDS {
state = self.substitution(&state);
state = self.multiply_with_gf2_matrix(&self.lin_matrices[round], &state);
state.xor_assign(&self.round_constants[round]);
state.xor_assign(&self.round_keys[round + 1]);
}
let mut low_bytes = [0u8; 16];
let mut high_bytes = [0u8; 16];
for i in 0..16 {
for j in 0..8 {
if i * 8 + j < 128 && state.get(i * 8 + j) {
low_bytes[i] |= 1 << j;
}
if 128 + i * 8 + j < BLOCK_SIZE && state.get(128 + i * 8 + j) {
high_bytes[i] |= 1 << j;
}
}
}
(
u128::from_le_bytes(low_bytes),
u128::from_le_bytes(high_bytes),
)
}
pub fn decrypt_full(&self, low: u128, high: u128) -> (u128, u128) {
let mut state = BitVec::new(BLOCK_SIZE);
let low_bytes = low.to_le_bytes();
for (i, &byte) in low_bytes.iter().enumerate() {
for j in 0..8 {
if i * 8 + j < 128 {
state.set(i * 8 + j, (byte >> j) & 1 == 1);
}
}
}
let high_bytes = high.to_le_bytes();
for (i, &byte) in high_bytes.iter().enumerate() {
for j in 0..8 {
if 128 + i * 8 + j < BLOCK_SIZE {
state.set(128 + i * 8 + j, (byte >> j) & 1 == 1);
}
}
}
for round in (0..ROUNDS).rev() {
state.xor_assign(&self.round_keys[round + 1]);
state.xor_assign(&self.round_constants[round]);
state = self.multiply_with_gf2_matrix(&self.inv_lin_matrices[round], &state);
state = self.inv_substitution(&state);
}
state.xor_assign(&self.round_keys[0]);
let mut low_bytes = [0u8; 16];
let mut high_bytes = [0u8; 16];
for i in 0..16 {
for j in 0..8 {
if i * 8 + j < 128 && state.get(i * 8 + j) {
low_bytes[i] |= 1 << j;
}
if 128 + i * 8 + j < BLOCK_SIZE && state.get(128 + i * 8 + j) {
high_bytes[i] |= 1 << j;
}
}
}
(
u128::from_le_bytes(low_bytes),
u128::from_le_bytes(high_bytes),
)
}
pub fn encrypt(&self, message: u128) -> (u128, u128) {
self.encrypt_full(message, 0)
}
pub fn decrypt(&self, ciphertext_low: u128, ciphertext_high: u128) -> u128 {
let (plaintext_low, _plaintext_high) = self.decrypt_full(ciphertext_low, ciphertext_high);
plaintext_low
}
#[deprecated(
note = "Use encrypt() which returns the full result, or encrypt_full() for explicit 256-bit operation"
)]
pub fn encrypt_128_legacy(&self, message: u128) -> u128 {
let (low, _high) = self.encrypt_full(message, 0);
low
}
pub fn set_key(&mut self, key: u128) {
self.key = BitVec::from_u128(key, KEY_SIZE);
self.instantiate_lowmc(key);
self.keyschedule();
}
fn substitution(&self, message: &BitVec) -> BitVec {
let mut result = message.clone();
for sbox_idx in 0..NUM_OF_BOXES {
let bit_pos = sbox_idx * 3;
let input = (if result.get(bit_pos) { 1 } else { 0 })
| (if result.get(bit_pos + 1) { 2 } else { 0 })
| (if result.get(bit_pos + 2) { 4 } else { 0 });
let output = self.sbox[input as usize];
result.set(bit_pos, (output & 1) != 0);
result.set(bit_pos + 1, (output & 2) != 0);
result.set(bit_pos + 2, (output & 4) != 0);
}
result
}
fn inv_substitution(&self, message: &BitVec) -> BitVec {
let mut result = message.clone();
for sbox_idx in 0..NUM_OF_BOXES {
let bit_pos = sbox_idx * 3;
let input = (if result.get(bit_pos) { 1 } else { 0 })
| (if result.get(bit_pos + 1) { 2 } else { 0 })
| (if result.get(bit_pos + 2) { 4 } else { 0 });
let output = self.inv_sbox[input as usize];
result.set(bit_pos, (output & 1) != 0);
result.set(bit_pos + 1, (output & 2) != 0);
result.set(bit_pos + 2, (output & 4) != 0);
}
result
}
fn multiply_with_gf2_matrix(&self, matrix: &[BitVec], message: &BitVec) -> BitVec {
Self::multiply_with_gf2_matrix_static(matrix, message)
}
fn multiply_with_gf2_matrix_static(matrix: &[BitVec], message: &BitVec) -> BitVec {
let mut result = BitVec::new(matrix.len());
for (i, matrix_row) in matrix.iter().enumerate() {
let and_result = matrix_row.and(message);
let bit_result = and_result.count_ones() % 2 == 1;
result.set(i, bit_result);
}
result
}
fn keyschedule(&mut self) {
self.round_keys.clear();
for round in 0..=ROUNDS {
if round == 0 {
let mut round_key = BitVec::new(BLOCK_SIZE);
for i in 0..KEY_SIZE.min(BLOCK_SIZE) {
round_key.set(i, self.key.get(i));
}
self.round_keys.push(round_key);
} else {
let round_key =
self.multiply_with_gf2_matrix(&self.key_matrices[round - 1], &self.key);
self.round_keys.push(round_key);
}
}
}
fn instantiate_lowmc(&mut self, key_seed: u128) {
let seed = key_seed as u64 ^ (key_seed >> 64) as u64;
let mut rng = StdRng::seed_from_u64(seed);
self.lin_matrices.clear();
self.inv_lin_matrices.clear();
self.round_constants.clear();
self.key_matrices.clear();
for _ in 0..ROUNDS {
let matrix = Self::generate_matrix_with_rng(&mut rng, BLOCK_SIZE);
let inv_matrix = Self::invert_matrix(&matrix);
self.lin_matrices.push(matrix);
self.inv_lin_matrices.push(inv_matrix);
}
for _ in 0..ROUNDS {
self.round_constants
.push(Self::generate_block_with_rng(&mut rng, BLOCK_SIZE));
}
for _ in 0..ROUNDS {
let mut key_matrix = Vec::new();
for _ in 0..BLOCK_SIZE {
key_matrix.push(Self::generate_block_with_rng(&mut rng, KEY_SIZE));
}
self.key_matrices.push(key_matrix);
}
}
fn generate_matrix_with_rng<R: Rng>(rng: &mut R, size: usize) -> Vec<BitVec> {
let mut matrix = Vec::new();
for i in 0..size {
let mut row = BitVec::new(size);
row.set(i, true);
for j in (i + 1)..size {
row.set(j, rng.gen::<bool>());
}
matrix.push(row);
}
matrix
}
fn generate_block_with_rng<R: Rng>(rng: &mut R, bits: usize) -> BitVec {
let mut block = BitVec::new(bits);
for i in 0..bits {
block.set(i, rng.gen::<bool>());
}
block
}
fn invert_matrix(matrix: &[BitVec]) -> Vec<BitVec> {
let n = matrix.len();
let mut augmented = Vec::new();
for (i, row) in matrix.iter().enumerate() {
let mut aug_row = BitVec::new(2 * n);
for j in 0..n {
aug_row.set(j, row.get(j));
}
aug_row.set(n + i, true);
augmented.push(aug_row);
}
for i in 0..n {
let mut pivot_row = i;
for (k, row) in augmented.iter().enumerate().skip(i + 1) {
if row.get(i) {
pivot_row = k;
break;
}
}
if pivot_row != i {
augmented.swap(i, pivot_row);
}
let pivot_row = augmented[i].clone();
for (j, row) in augmented.iter_mut().enumerate() {
if i != j && row.get(i) {
row.xor_assign(&pivot_row);
}
}
}
let mut inverse = Vec::new();
for row in augmented.iter().take(n) {
let mut inv_row = BitVec::new(n);
for j in 0..n {
inv_row.set(j, row.get(n + j));
}
inverse.push(inv_row);
}
inverse
}
pub fn new_simple_test(key: u128) -> Self {
let mut cipher = LowMC {
sbox: [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02],
inv_sbox: [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04],
lin_matrices: Vec::new(),
inv_lin_matrices: Vec::new(),
round_constants: Vec::new(),
key_matrices: Vec::new(),
round_keys: Vec::new(),
key: BitVec::from_u128(key, KEY_SIZE),
};
for _ in 0..ROUNDS {
let mut identity = Vec::new();
for i in 0..BLOCK_SIZE {
let mut row = BitVec::new(BLOCK_SIZE);
row.set(i, true);
identity.push(row);
}
cipher.lin_matrices.push(identity.clone());
cipher.inv_lin_matrices.push(identity);
cipher.round_constants.push(BitVec::new(BLOCK_SIZE));
}
for _ in 0..ROUNDS {
let mut key_matrix = Vec::new();
for _ in 0..BLOCK_SIZE {
key_matrix.push(BitVec::new(KEY_SIZE));
}
cipher.key_matrices.push(key_matrix);
}
cipher.keyschedule();
cipher
}
pub fn new_single_round_test(key: u128) -> Self {
let mut cipher = LowMC {
sbox: [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02],
inv_sbox: [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04],
lin_matrices: Vec::new(),
inv_lin_matrices: Vec::new(),
round_constants: Vec::new(),
key_matrices: Vec::new(),
round_keys: Vec::new(),
key: BitVec::from_u128(key, KEY_SIZE),
};
let seed = key as u64;
let mut rng = StdRng::seed_from_u64(seed);
let matrix = Self::generate_matrix_with_rng(&mut rng, BLOCK_SIZE);
let inv_matrix = Self::invert_matrix(&matrix);
cipher.lin_matrices.push(matrix);
cipher.inv_lin_matrices.push(inv_matrix);
cipher
.round_constants
.push(Self::generate_block_with_rng(&mut rng, BLOCK_SIZE));
let mut key_matrix = Vec::new();
for _ in 0..BLOCK_SIZE {
key_matrix.push(Self::generate_block_with_rng(&mut rng, KEY_SIZE));
}
cipher.key_matrices.push(key_matrix);
cipher.keyschedule();
cipher
}
pub fn encrypt_single_round(&self, message: u128) -> u128 {
let mut state = BitVec::from_u128(message, BLOCK_SIZE);
state.xor_assign(&self.round_keys[0]);
state = self.substitution(&state);
state = self.multiply_with_gf2_matrix(&self.lin_matrices[0], &state);
state.xor_assign(&self.round_constants[0]);
state.xor_assign(&self.round_keys[1]);
state.to_u128()
}
pub fn decrypt_single_round(&self, message: u128) -> u128 {
let mut state = BitVec::from_u128(message, BLOCK_SIZE);
state.xor_assign(&self.round_keys[1]);
state.xor_assign(&self.round_constants[0]);
state = self.multiply_with_gf2_matrix(&self.inv_lin_matrices[0], &state);
state = self.inv_substitution(&state);
state.xor_assign(&self.round_keys[0]);
state.to_u128()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sbox_inversion() {
let sbox = [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02];
let inv_sbox = [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04];
for i in 0..8 {
let forward = sbox[i] as usize;
let backward = inv_sbox[forward] as usize;
assert_eq!(backward, i, "S-box inversion failed for input {}", i);
}
}
#[test]
fn test_substitution_layer() {
let cipher = LowMC::new(1);
let test_values = [0x0u128, 0x1u128, 0x7u128, 0xFFu128, 0xFFD5u128];
for &test_val in &test_values {
let test_bits = BitVec::from_u128(test_val, BLOCK_SIZE);
let substituted = cipher.substitution(&test_bits);
let recovered = cipher.inv_substitution(&substituted);
assert_eq!(
test_bits.to_u128(),
recovered.to_u128(),
"Substitution layer inversion failed for {:#x}",
test_val
);
}
}
#[test]
fn test_matrix_inversion() {
let mut rng = thread_rng();
let matrix = LowMC::generate_matrix_with_rng(&mut rng, BLOCK_SIZE);
let inv_matrix = LowMC::invert_matrix(&matrix);
let test_values = [0x0u128, 0x1u128, 0xFFD5u128];
for &test_val in &test_values {
let test_bits = BitVec::from_u128(test_val, BLOCK_SIZE);
let transformed = LowMC::multiply_with_gf2_matrix_static(&matrix, &test_bits);
let recovered = LowMC::multiply_with_gf2_matrix_static(&inv_matrix, &transformed);
assert_eq!(
test_bits.to_u128(),
recovered.to_u128(),
"Matrix inversion failed for {:#x}",
test_val
);
}
}
#[test]
fn test_single_round() {
let cipher = LowMC::new(1);
let test_values = [0x0u128, 0x1u128, 0xFFD5u128];
for &plaintext in &test_values {
let mut state = BitVec::from_u128(plaintext, BLOCK_SIZE);
state.xor_assign(&cipher.round_keys[0]);
state = cipher.substitution(&state);
state = cipher.multiply_with_gf2_matrix(&cipher.lin_matrices[0], &state);
state.xor_assign(&cipher.round_constants[0]);
state.xor_assign(&cipher.round_keys[1]);
let mut reverse_state = state.clone();
reverse_state.xor_assign(&cipher.round_keys[1]);
reverse_state.xor_assign(&cipher.round_constants[0]);
reverse_state =
cipher.multiply_with_gf2_matrix(&cipher.inv_lin_matrices[0], &reverse_state);
reverse_state = cipher.inv_substitution(&reverse_state);
reverse_state.xor_assign(&cipher.round_keys[0]);
let recovered = reverse_state.to_u128();
assert_eq!(
plaintext, recovered,
"Single round inversion failed for {:#x}",
plaintext
);
}
}
#[test]
fn test_bit_vector_operations() {
let mut bv = BitVec::new(32);
bv.set(0, true);
bv.set(31, true);
assert!(bv.get(0));
assert!(bv.get(31));
assert!(!bv.get(15));
let mut bv2 = BitVec::new(32);
bv2.set(0, true);
bv2.set(15, true);
bv.xor_assign(&bv2);
assert!(!bv.get(0)); assert!(bv.get(15)); assert!(bv.get(31));
assert_eq!(bv.count_ones(), 2);
}
#[test]
fn test_lowmc_parameters() {
assert_eq!(BLOCK_SIZE, 256);
assert_eq!(KEY_SIZE, 80);
assert_eq!(ROUNDS, 12);
assert_eq!(NUM_OF_BOXES, 49);
assert_eq!(BLOCK_SIZE - 3 * NUM_OF_BOXES, 109); }
#[test]
fn test_encryption_decryption_deterministic() {
let cipher = LowMC::new(1);
let plaintext = 0x123456789ABCDEFu128;
let (ciphertext_low, ciphertext_high) = cipher.encrypt(plaintext);
let recovered = cipher.decrypt(ciphertext_low, ciphertext_high);
assert_ne!(
plaintext, ciphertext_low,
"Ciphertext should differ from plaintext"
);
assert_eq!(
plaintext, recovered,
"Decryption should recover original plaintext"
);
}
#[test]
fn test_multiple_values() {
let cipher = LowMC::new(42);
let test_values = [
0x0u128,
0x1u128,
0xDEADBEEFu128,
0x123456789ABCDEFu128,
0xFFFFFFFFFFFFFFFFu128,
];
for &plaintext in &test_values {
let (ciphertext_low, ciphertext_high) = cipher.encrypt(plaintext);
let recovered = cipher.decrypt(ciphertext_low, ciphertext_high);
assert_ne!(
plaintext, ciphertext_low,
"Ciphertext should differ from plaintext for {:#x}",
plaintext
);
assert_eq!(
plaintext, recovered,
"Decryption should recover original plaintext for {:#x}",
plaintext
);
}
}
#[test]
fn test_128_bit_compatibility() {
let cipher = LowMC::new(1);
let plaintext = 0xDEADBEEFu128;
println!("=== Testing 128-bit API ===");
println!("Plaintext: {:#x}", plaintext);
let (ciphertext_low, ciphertext_high) = cipher.encrypt(plaintext);
println!(
"Ciphertext: low={:#x}, high={:#x}",
ciphertext_low, ciphertext_high
);
let recovered = cipher.decrypt(ciphertext_low, ciphertext_high);
println!("Recovered: {:#x}", recovered);
let success = plaintext == recovered;
println!("Success: {}", success);
assert_eq!(plaintext, recovered, "128-bit API should work correctly");
assert_ne!(
plaintext, ciphertext_low,
"Ciphertext should differ from plaintext"
);
println!("✅ 128-bit API works correctly!");
}
}