lowmc_rs/
lib.rs

1//! LowMC Block Cipher Implementation in Rust
2//!
3//! This crate provides a complete implementation of the LowMC block cipher,
4//! a family of block ciphers designed to minimize the number of AND gates
5//! in the circuit representation.
6//!
7//! # Parameters
8//! - Block size: 256 bits
9//! - Key size: 80 bits  
10//! - Number of rounds: 12
11//! - S-boxes per round: 49
12
13use rand::rngs::StdRng;
14use rand::{thread_rng, Rng, SeedableRng};
15
16// LowMC Parameters (matching the C++ implementation)
17const NUM_OF_BOXES: usize = 49; // Number of S-boxes
18const BLOCK_SIZE: usize = 256; // Block size in bits
19const KEY_SIZE: usize = 80; // Key size in bits
20const ROUNDS: usize = 12; // Number of rounds
21
22/// Simple bit vector using u32 arrays - LSB is bit 0 like std::bitset
23#[derive(Clone, Debug, PartialEq)]
24struct BitVec {
25    words: Vec<u32>,
26    bits: usize,
27}
28
29impl BitVec {
30    fn new(bits: usize) -> Self {
31        let words = bits.div_ceil(32);
32        BitVec {
33            words: vec![0u32; words],
34            bits,
35        }
36    }
37
38    fn get(&self, index: usize) -> bool {
39        if index >= self.bits {
40            return false;
41        }
42        let word_idx = index / 32;
43        let bit_idx = index % 32;
44        (self.words[word_idx] >> bit_idx) & 1 == 1
45    }
46
47    fn set(&mut self, index: usize, value: bool) {
48        if index >= self.bits {
49            return;
50        }
51        let word_idx = index / 32;
52        let bit_idx = index % 32;
53        if value {
54            self.words[word_idx] |= 1 << bit_idx;
55        } else {
56            self.words[word_idx] &= !(1 << bit_idx);
57        }
58    }
59
60    fn count_ones(&self) -> u32 {
61        self.words.iter().map(|w| w.count_ones()).sum()
62    }
63
64    fn xor_assign(&mut self, other: &BitVec) {
65        for (a, b) in self.words.iter_mut().zip(other.words.iter()) {
66            *a ^= b;
67        }
68    }
69
70    fn and(&self, other: &BitVec) -> BitVec {
71        let mut result = BitVec::new(self.bits);
72        for i in 0..self.words.len().min(other.words.len()) {
73            result.words[i] = self.words[i] & other.words[i];
74        }
75        result
76    }
77
78    fn from_u128(value: u128, bits: usize) -> BitVec {
79        let mut result = BitVec::new(bits);
80        let bytes = value.to_le_bytes();
81        for (i, &byte) in bytes.iter().enumerate() {
82            for j in 0..8 {
83                if i * 8 + j < bits {
84                    result.set(i * 8 + j, (byte >> j) & 1 == 1);
85                }
86            }
87        }
88        result
89    }
90
91    fn to_u128(&self) -> u128 {
92        let mut bytes = [0u8; 16];
93        for (i, byte) in bytes.iter_mut().enumerate() {
94            for j in 0..8 {
95                if i * 8 + j < self.bits && self.get(i * 8 + j) {
96                    *byte |= 1 << j;
97                }
98            }
99        }
100        u128::from_le_bytes(bytes)
101    }
102}
103
104/// LowMC block cipher implementation
105pub struct LowMC {
106    // S-box lookup tables
107    sbox: [u8; 8],
108    inv_sbox: [u8; 8],
109
110    // Matrices and constants
111    lin_matrices: Vec<Vec<BitVec>>,
112    inv_lin_matrices: Vec<Vec<BitVec>>,
113    round_constants: Vec<BitVec>,
114    key_matrices: Vec<Vec<BitVec>>,
115    round_keys: Vec<BitVec>,
116
117    // Master key
118    key: BitVec,
119}
120
121impl LowMC {
122    /// Create a new LowMC cipher instance with the given key
123    pub fn new(key: u128) -> Self {
124        let mut cipher = LowMC {
125            sbox: [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02],
126            inv_sbox: [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04],
127            lin_matrices: Vec::new(),
128            inv_lin_matrices: Vec::new(),
129            round_constants: Vec::new(),
130            key_matrices: Vec::new(),
131            round_keys: Vec::new(),
132            key: BitVec::from_u128(key, KEY_SIZE),
133        };
134
135        cipher.instantiate_lowmc(key);
136        cipher.keyschedule();
137        cipher
138    }
139
140    /// Generate a random key for the LowMC cipher
141    pub fn generate_random_key() -> u128 {
142        let mut rng = thread_rng();
143        rng.gen()
144    }
145
146    /// Encrypt a full 256-bit block (represented as two u128 values: low, high)
147    pub fn encrypt_full(&self, low: u128, high: u128) -> (u128, u128) {
148        let mut state = BitVec::new(BLOCK_SIZE);
149
150        // Set the low 128 bits
151        let low_bytes = low.to_le_bytes();
152        for (i, &byte) in low_bytes.iter().enumerate() {
153            for j in 0..8 {
154                if i * 8 + j < 128 {
155                    state.set(i * 8 + j, (byte >> j) & 1 == 1);
156                }
157            }
158        }
159
160        // Set the high 128 bits
161        let high_bytes = high.to_le_bytes();
162        for (i, &byte) in high_bytes.iter().enumerate() {
163            for j in 0..8 {
164                if 128 + i * 8 + j < BLOCK_SIZE {
165                    state.set(128 + i * 8 + j, (byte >> j) & 1 == 1);
166                }
167            }
168        }
169
170        // Initial key addition
171        state.xor_assign(&self.round_keys[0]);
172
173        // Main rounds
174        for round in 0..ROUNDS {
175            // S-box layer
176            state = self.substitution(&state);
177
178            // Linear layer
179            state = self.multiply_with_gf2_matrix(&self.lin_matrices[round], &state);
180
181            // Add round constant
182            state.xor_assign(&self.round_constants[round]);
183
184            // Add round key
185            state.xor_assign(&self.round_keys[round + 1]);
186        }
187
188        // Extract low and high parts
189        let mut low_bytes = [0u8; 16];
190        let mut high_bytes = [0u8; 16];
191
192        for i in 0..16 {
193            for j in 0..8 {
194                if i * 8 + j < 128 && state.get(i * 8 + j) {
195                    low_bytes[i] |= 1 << j;
196                }
197                if 128 + i * 8 + j < BLOCK_SIZE && state.get(128 + i * 8 + j) {
198                    high_bytes[i] |= 1 << j;
199                }
200            }
201        }
202
203        (
204            u128::from_le_bytes(low_bytes),
205            u128::from_le_bytes(high_bytes),
206        )
207    }
208
209    /// Decrypt a full 256-bit block (represented as two u128 values: low, high)
210    pub fn decrypt_full(&self, low: u128, high: u128) -> (u128, u128) {
211        let mut state = BitVec::new(BLOCK_SIZE);
212
213        // Set the low 128 bits
214        let low_bytes = low.to_le_bytes();
215        for (i, &byte) in low_bytes.iter().enumerate() {
216            for j in 0..8 {
217                if i * 8 + j < 128 {
218                    state.set(i * 8 + j, (byte >> j) & 1 == 1);
219                }
220            }
221        }
222
223        // Set the high 128 bits
224        let high_bytes = high.to_le_bytes();
225        for (i, &byte) in high_bytes.iter().enumerate() {
226            for j in 0..8 {
227                if 128 + i * 8 + j < BLOCK_SIZE {
228                    state.set(128 + i * 8 + j, (byte >> j) & 1 == 1);
229                }
230            }
231        }
232
233        // Reverse the rounds
234        for round in (0..ROUNDS).rev() {
235            // Remove round key
236            state.xor_assign(&self.round_keys[round + 1]);
237
238            // Remove round constant
239            state.xor_assign(&self.round_constants[round]);
240
241            // Inverse linear layer
242            state = self.multiply_with_gf2_matrix(&self.inv_lin_matrices[round], &state);
243
244            // Inverse S-box layer
245            state = self.inv_substitution(&state);
246        }
247
248        // Remove initial key
249        state.xor_assign(&self.round_keys[0]);
250
251        // Extract low and high parts
252        let mut low_bytes = [0u8; 16];
253        let mut high_bytes = [0u8; 16];
254
255        for i in 0..16 {
256            for j in 0..8 {
257                if i * 8 + j < 128 && state.get(i * 8 + j) {
258                    low_bytes[i] |= 1 << j;
259                }
260                if 128 + i * 8 + j < BLOCK_SIZE && state.get(128 + i * 8 + j) {
261                    high_bytes[i] |= 1 << j;
262                }
263            }
264        }
265
266        (
267            u128::from_le_bytes(low_bytes),
268            u128::from_le_bytes(high_bytes),
269        )
270    }
271
272    /// Encrypt a 128-bit message using the full 256-bit LowMC algorithm
273    /// The message is placed in the lower 128 bits, upper 128 bits are zero
274    /// Returns the full 256-bit result as (low_128_bits, high_128_bits)
275    pub fn encrypt(&self, message: u128) -> (u128, u128) {
276        self.encrypt_full(message, 0)
277    }
278
279    /// Decrypt a 256-bit ciphertext back to a 128-bit message
280    /// Takes the full 256-bit ciphertext as (low_128_bits, high_128_bits)
281    /// Returns only the lower 128 bits (the original message)
282    pub fn decrypt(&self, ciphertext_low: u128, ciphertext_high: u128) -> u128 {
283        let (plaintext_low, _plaintext_high) = self.decrypt_full(ciphertext_low, ciphertext_high);
284        plaintext_low
285    }
286
287    /// Legacy 128-bit encrypt (DEPRECATED - loses information!)
288    /// This method only returns the lower 128 bits and loses the upper 128 bits
289    /// Use encrypt() or encrypt_full() instead for proper encryption
290    #[deprecated(
291        note = "Use encrypt() which returns the full result, or encrypt_full() for explicit 256-bit operation"
292    )]
293    pub fn encrypt_128_legacy(&self, message: u128) -> u128 {
294        let (low, _high) = self.encrypt_full(message, 0);
295        low
296    }
297
298    /// Set a new key and regenerate key schedule
299    pub fn set_key(&mut self, key: u128) {
300        self.key = BitVec::from_u128(key, KEY_SIZE);
301        // Regenerate matrices with new key-based seed
302        self.instantiate_lowmc(key);
303        self.keyschedule();
304    }
305
306    fn substitution(&self, message: &BitVec) -> BitVec {
307        let mut result = message.clone();
308
309        // Apply S-box to first NUM_OF_BOXES 3-bit groups
310        for sbox_idx in 0..NUM_OF_BOXES {
311            let bit_pos = sbox_idx * 3;
312
313            // Extract 3 bits (LSB first like C++)
314            let input = (if result.get(bit_pos) { 1 } else { 0 })
315                | (if result.get(bit_pos + 1) { 2 } else { 0 })
316                | (if result.get(bit_pos + 2) { 4 } else { 0 });
317
318            let output = self.sbox[input as usize];
319
320            // Set the output bits
321            result.set(bit_pos, (output & 1) != 0);
322            result.set(bit_pos + 1, (output & 2) != 0);
323            result.set(bit_pos + 2, (output & 4) != 0);
324        }
325
326        result
327    }
328
329    fn inv_substitution(&self, message: &BitVec) -> BitVec {
330        let mut result = message.clone();
331
332        // Apply inverse S-box to first NUM_OF_BOXES 3-bit groups
333        for sbox_idx in 0..NUM_OF_BOXES {
334            let bit_pos = sbox_idx * 3;
335
336            // Extract 3 bits (LSB first like C++)
337            let input = (if result.get(bit_pos) { 1 } else { 0 })
338                | (if result.get(bit_pos + 1) { 2 } else { 0 })
339                | (if result.get(bit_pos + 2) { 4 } else { 0 });
340
341            let output = self.inv_sbox[input as usize];
342
343            // Set the output bits
344            result.set(bit_pos, (output & 1) != 0);
345            result.set(bit_pos + 1, (output & 2) != 0);
346            result.set(bit_pos + 2, (output & 4) != 0);
347        }
348
349        result
350    }
351
352    fn multiply_with_gf2_matrix(&self, matrix: &[BitVec], message: &BitVec) -> BitVec {
353        Self::multiply_with_gf2_matrix_static(matrix, message)
354    }
355
356    fn multiply_with_gf2_matrix_static(matrix: &[BitVec], message: &BitVec) -> BitVec {
357        let mut result = BitVec::new(matrix.len());
358
359        for (i, matrix_row) in matrix.iter().enumerate() {
360            let and_result = matrix_row.and(message);
361            let bit_result = and_result.count_ones() % 2 == 1;
362            result.set(i, bit_result);
363        }
364
365        result
366    }
367
368    fn keyschedule(&mut self) {
369        self.round_keys.clear();
370
371        // Generate round keys
372        for round in 0..=ROUNDS {
373            if round == 0 {
374                // First round key is just the master key (padded to block size)
375                let mut round_key = BitVec::new(BLOCK_SIZE);
376                for i in 0..KEY_SIZE.min(BLOCK_SIZE) {
377                    round_key.set(i, self.key.get(i));
378                }
379                self.round_keys.push(round_key);
380            } else {
381                // Subsequent round keys use key matrices
382                let round_key =
383                    self.multiply_with_gf2_matrix(&self.key_matrices[round - 1], &self.key);
384                // round_key is already BLOCK_SIZE bits from the matrix multiplication
385                self.round_keys.push(round_key);
386            }
387        }
388    }
389
390    fn instantiate_lowmc(&mut self, key_seed: u128) {
391        // Use key as seed for deterministic matrix generation
392        let seed = key_seed as u64 ^ (key_seed >> 64) as u64;
393        let mut rng = StdRng::seed_from_u64(seed);
394
395        self.lin_matrices.clear();
396        self.inv_lin_matrices.clear();
397        self.round_constants.clear();
398        self.key_matrices.clear();
399
400        // Generate linear layer matrices
401        for _ in 0..ROUNDS {
402            let matrix = Self::generate_matrix_with_rng(&mut rng, BLOCK_SIZE);
403            let inv_matrix = Self::invert_matrix(&matrix);
404            self.lin_matrices.push(matrix);
405            self.inv_lin_matrices.push(inv_matrix);
406        }
407
408        // Generate round constants
409        for _ in 0..ROUNDS {
410            self.round_constants
411                .push(Self::generate_block_with_rng(&mut rng, BLOCK_SIZE));
412        }
413
414        // Generate key matrices (BLOCK_SIZE x KEY_SIZE - each row has KEY_SIZE bits)
415        for _ in 0..ROUNDS {
416            let mut key_matrix = Vec::new();
417            for _ in 0..BLOCK_SIZE {
418                key_matrix.push(Self::generate_block_with_rng(&mut rng, KEY_SIZE));
419            }
420            self.key_matrices.push(key_matrix);
421        }
422    }
423
424    fn generate_matrix_with_rng<R: Rng>(rng: &mut R, size: usize) -> Vec<BitVec> {
425        // For now, let's use a simple but reliable approach:
426        // Generate a random upper triangular matrix with 1s on the diagonal
427        // This is guaranteed to be invertible
428
429        let mut matrix = Vec::new();
430
431        for i in 0..size {
432            let mut row = BitVec::new(size);
433            // Set diagonal to 1
434            row.set(i, true);
435            // Set random bits above the diagonal
436            for j in (i + 1)..size {
437                row.set(j, rng.gen::<bool>());
438            }
439            matrix.push(row);
440        }
441
442        matrix
443    }
444
445    fn generate_block_with_rng<R: Rng>(rng: &mut R, bits: usize) -> BitVec {
446        let mut block = BitVec::new(bits);
447        for i in 0..bits {
448            block.set(i, rng.gen::<bool>());
449        }
450        block
451    }
452
453    fn invert_matrix(matrix: &[BitVec]) -> Vec<BitVec> {
454        let n = matrix.len();
455        let mut augmented = Vec::new();
456
457        // Create augmented matrix [A|I]
458        for (i, row) in matrix.iter().enumerate() {
459            let mut aug_row = BitVec::new(2 * n);
460            // Copy original matrix
461            for j in 0..n {
462                aug_row.set(j, row.get(j));
463            }
464            // Add identity matrix
465            aug_row.set(n + i, true);
466            augmented.push(aug_row);
467        }
468
469        // Gaussian elimination
470        for i in 0..n {
471            // Find pivot
472            let mut pivot_row = i;
473            for (k, row) in augmented.iter().enumerate().skip(i + 1) {
474                if row.get(i) {
475                    pivot_row = k;
476                    break;
477                }
478            }
479
480            // Swap if needed
481            if pivot_row != i {
482                augmented.swap(i, pivot_row);
483            }
484
485            // Eliminate column
486            let pivot_row = augmented[i].clone();
487            for (j, row) in augmented.iter_mut().enumerate() {
488                if i != j && row.get(i) {
489                    row.xor_assign(&pivot_row);
490                }
491            }
492        }
493
494        // Extract inverse matrix from right half
495        let mut inverse = Vec::new();
496        for row in augmented.iter().take(n) {
497            let mut inv_row = BitVec::new(n);
498            for j in 0..n {
499                inv_row.set(j, row.get(n + j));
500            }
501            inverse.push(inv_row);
502        }
503
504        inverse
505    }
506
507    /// Create a simple test cipher with identity matrices (for testing)
508    pub fn new_simple_test(key: u128) -> Self {
509        let mut cipher = LowMC {
510            sbox: [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02],
511            inv_sbox: [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04],
512            lin_matrices: Vec::new(),
513            inv_lin_matrices: Vec::new(),
514            round_constants: Vec::new(),
515            key_matrices: Vec::new(),
516            round_keys: Vec::new(),
517            key: BitVec::from_u128(key, KEY_SIZE),
518        };
519
520        // Create identity matrices and zero constants for testing
521        for _ in 0..ROUNDS {
522            let mut identity = Vec::new();
523            for i in 0..BLOCK_SIZE {
524                let mut row = BitVec::new(BLOCK_SIZE);
525                row.set(i, true);
526                identity.push(row);
527            }
528            cipher.lin_matrices.push(identity.clone());
529            cipher.inv_lin_matrices.push(identity);
530
531            cipher.round_constants.push(BitVec::new(BLOCK_SIZE));
532        }
533
534        // Create zero key matrices
535        for _ in 0..ROUNDS {
536            let mut key_matrix = Vec::new();
537            for _ in 0..BLOCK_SIZE {
538                key_matrix.push(BitVec::new(KEY_SIZE));
539            }
540            cipher.key_matrices.push(key_matrix);
541        }
542
543        cipher.keyschedule();
544        cipher
545    }
546
547    /// Create a single-round test cipher
548    pub fn new_single_round_test(key: u128) -> Self {
549        let mut cipher = LowMC {
550            sbox: [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02],
551            inv_sbox: [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04],
552            lin_matrices: Vec::new(),
553            inv_lin_matrices: Vec::new(),
554            round_constants: Vec::new(),
555            key_matrices: Vec::new(),
556            round_keys: Vec::new(),
557            key: BitVec::from_u128(key, KEY_SIZE),
558        };
559
560        // Use key as seed for deterministic generation
561        let seed = key as u64;
562        let mut rng = StdRng::seed_from_u64(seed);
563
564        // Create one round with random matrices
565        let matrix = Self::generate_matrix_with_rng(&mut rng, BLOCK_SIZE);
566        let inv_matrix = Self::invert_matrix(&matrix);
567        cipher.lin_matrices.push(matrix);
568        cipher.inv_lin_matrices.push(inv_matrix);
569        cipher
570            .round_constants
571            .push(Self::generate_block_with_rng(&mut rng, BLOCK_SIZE));
572
573        // Create key matrix
574        let mut key_matrix = Vec::new();
575        for _ in 0..BLOCK_SIZE {
576            key_matrix.push(Self::generate_block_with_rng(&mut rng, KEY_SIZE));
577        }
578        cipher.key_matrices.push(key_matrix);
579
580        cipher.keyschedule();
581        cipher
582    }
583
584    /// Encrypt with single round (for testing)
585    pub fn encrypt_single_round(&self, message: u128) -> u128 {
586        let mut state = BitVec::from_u128(message, BLOCK_SIZE);
587        state.xor_assign(&self.round_keys[0]);
588        state = self.substitution(&state);
589        state = self.multiply_with_gf2_matrix(&self.lin_matrices[0], &state);
590        state.xor_assign(&self.round_constants[0]);
591        state.xor_assign(&self.round_keys[1]);
592        state.to_u128()
593    }
594
595    /// Decrypt with single round (for testing)
596    pub fn decrypt_single_round(&self, message: u128) -> u128 {
597        let mut state = BitVec::from_u128(message, BLOCK_SIZE);
598        state.xor_assign(&self.round_keys[1]);
599        state.xor_assign(&self.round_constants[0]);
600        state = self.multiply_with_gf2_matrix(&self.inv_lin_matrices[0], &state);
601        state = self.inv_substitution(&state);
602        state.xor_assign(&self.round_keys[0]);
603        state.to_u128()
604    }
605}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610
611    #[test]
612    fn test_sbox_inversion() {
613        let sbox = [0x00, 0x01, 0x03, 0x06, 0x07, 0x04, 0x05, 0x02];
614        let inv_sbox = [0x00, 0x01, 0x07, 0x02, 0x05, 0x06, 0x03, 0x04];
615
616        for i in 0..8 {
617            let forward = sbox[i] as usize;
618            let backward = inv_sbox[forward] as usize;
619            assert_eq!(backward, i, "S-box inversion failed for input {}", i);
620        }
621    }
622
623    #[test]
624    fn test_substitution_layer() {
625        let cipher = LowMC::new(1);
626        let test_values = [0x0u128, 0x1u128, 0x7u128, 0xFFu128, 0xFFD5u128];
627
628        for &test_val in &test_values {
629            let test_bits = BitVec::from_u128(test_val, BLOCK_SIZE);
630            let substituted = cipher.substitution(&test_bits);
631            let recovered = cipher.inv_substitution(&substituted);
632
633            assert_eq!(
634                test_bits.to_u128(),
635                recovered.to_u128(),
636                "Substitution layer inversion failed for {:#x}",
637                test_val
638            );
639        }
640    }
641
642    #[test]
643    fn test_matrix_inversion() {
644        let mut rng = thread_rng();
645        let matrix = LowMC::generate_matrix_with_rng(&mut rng, BLOCK_SIZE);
646        let inv_matrix = LowMC::invert_matrix(&matrix);
647        let test_values = [0x0u128, 0x1u128, 0xFFD5u128];
648
649        for &test_val in &test_values {
650            let test_bits = BitVec::from_u128(test_val, BLOCK_SIZE);
651            let transformed = LowMC::multiply_with_gf2_matrix_static(&matrix, &test_bits);
652            let recovered = LowMC::multiply_with_gf2_matrix_static(&inv_matrix, &transformed);
653
654            assert_eq!(
655                test_bits.to_u128(),
656                recovered.to_u128(),
657                "Matrix inversion failed for {:#x}",
658                test_val
659            );
660        }
661    }
662
663    #[test]
664    fn test_single_round() {
665        let cipher = LowMC::new(1);
666        let test_values = [0x0u128, 0x1u128, 0xFFD5u128];
667
668        for &plaintext in &test_values {
669            let mut state = BitVec::from_u128(plaintext, BLOCK_SIZE);
670
671            // Apply one round of encryption
672            state.xor_assign(&cipher.round_keys[0]);
673            state = cipher.substitution(&state);
674            state = cipher.multiply_with_gf2_matrix(&cipher.lin_matrices[0], &state);
675            state.xor_assign(&cipher.round_constants[0]);
676            state.xor_assign(&cipher.round_keys[1]);
677
678            // Now reverse it
679            let mut reverse_state = state.clone();
680            reverse_state.xor_assign(&cipher.round_keys[1]);
681            reverse_state.xor_assign(&cipher.round_constants[0]);
682            reverse_state =
683                cipher.multiply_with_gf2_matrix(&cipher.inv_lin_matrices[0], &reverse_state);
684            reverse_state = cipher.inv_substitution(&reverse_state);
685            reverse_state.xor_assign(&cipher.round_keys[0]);
686
687            let recovered = reverse_state.to_u128();
688            assert_eq!(
689                plaintext, recovered,
690                "Single round inversion failed for {:#x}",
691                plaintext
692            );
693        }
694    }
695
696    #[test]
697    fn test_bit_vector_operations() {
698        let mut bv = BitVec::new(32);
699
700        // Test basic operations
701        bv.set(0, true);
702        bv.set(31, true);
703        assert!(bv.get(0));
704        assert!(bv.get(31));
705        assert!(!bv.get(15));
706
707        // Test XOR
708        let mut bv2 = BitVec::new(32);
709        bv2.set(0, true);
710        bv2.set(15, true);
711
712        bv.xor_assign(&bv2);
713        assert!(!bv.get(0)); // Should be false after XOR
714        assert!(bv.get(15)); // Should be true
715        assert!(bv.get(31)); // Should remain true
716
717        // Test count_ones
718        assert_eq!(bv.count_ones(), 2);
719    }
720
721    #[test]
722    fn test_lowmc_parameters() {
723        assert_eq!(BLOCK_SIZE, 256);
724        assert_eq!(KEY_SIZE, 80);
725        assert_eq!(ROUNDS, 12);
726        assert_eq!(NUM_OF_BOXES, 49);
727        assert_eq!(BLOCK_SIZE - 3 * NUM_OF_BOXES, 109); // Identity bits
728    }
729
730    #[test]
731    fn test_encryption_decryption_deterministic() {
732        let cipher = LowMC::new(1);
733
734        // Test that encryption and decryption are perfect inverses
735        let plaintext = 0x123456789ABCDEFu128;
736        let (ciphertext_low, ciphertext_high) = cipher.encrypt(plaintext);
737        let recovered = cipher.decrypt(ciphertext_low, ciphertext_high);
738
739        // The algorithm should be perfectly deterministic now
740        assert_ne!(
741            plaintext, ciphertext_low,
742            "Ciphertext should differ from plaintext"
743        );
744        assert_eq!(
745            plaintext, recovered,
746            "Decryption should recover original plaintext"
747        );
748    }
749
750    #[test]
751    fn test_multiple_values() {
752        let cipher = LowMC::new(42);
753        let test_values = [
754            0x0u128,
755            0x1u128,
756            0xDEADBEEFu128,
757            0x123456789ABCDEFu128,
758            0xFFFFFFFFFFFFFFFFu128,
759        ];
760
761        for &plaintext in &test_values {
762            let (ciphertext_low, ciphertext_high) = cipher.encrypt(plaintext);
763            let recovered = cipher.decrypt(ciphertext_low, ciphertext_high);
764
765            assert_ne!(
766                plaintext, ciphertext_low,
767                "Ciphertext should differ from plaintext for {:#x}",
768                plaintext
769            );
770            assert_eq!(
771                plaintext, recovered,
772                "Decryption should recover original plaintext for {:#x}",
773                plaintext
774            );
775        }
776    }
777
778    #[test]
779    fn test_128_bit_compatibility() {
780        let cipher = LowMC::new(1);
781        let plaintext = 0xDEADBEEFu128;
782
783        println!("=== Testing 128-bit API ===");
784        println!("Plaintext: {:#x}", plaintext);
785
786        let (ciphertext_low, ciphertext_high) = cipher.encrypt(plaintext);
787        println!(
788            "Ciphertext: low={:#x}, high={:#x}",
789            ciphertext_low, ciphertext_high
790        );
791
792        let recovered = cipher.decrypt(ciphertext_low, ciphertext_high);
793        println!("Recovered: {:#x}", recovered);
794
795        let success = plaintext == recovered;
796        println!("Success: {}", success);
797
798        assert_eq!(plaintext, recovered, "128-bit API should work correctly");
799        assert_ne!(
800            plaintext, ciphertext_low,
801            "Ciphertext should differ from plaintext"
802        );
803
804        println!("✅ 128-bit API works correctly!");
805    }
806}