From 509b6a92aaf0b90ba1afb8a66c5dc96d16760985 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Thu, 2 Oct 2025 13:49:17 +0300 Subject: [PATCH] test: pass all tests --- des-lib/src/lib.rs | 77 ++++++++++++---- des-lib/tests/des.rs | 160 ++++++++++------------------------ des-lib/tests/key_schedule.rs | 57 ------------ 3 files changed, 105 insertions(+), 189 deletions(-) diff --git a/des-lib/src/lib.rs b/des-lib/src/lib.rs index c73f544..3c380ca 100644 --- a/des-lib/src/lib.rs +++ b/des-lib/src/lib.rs @@ -1,6 +1,6 @@ mod constants; -use crate::constants::{E_BOX, IP, PC1_TABLE, PC2_TABLE, P_BOX, ROUND_ROTATIONS, S_BOXES}; +use crate::constants::{E_BOX, FP, IP, PC1_TABLE, PC2_TABLE, P_BOX, ROUND_ROTATIONS, S_BOXES}; #[derive(Debug)] pub struct Des { @@ -39,7 +39,7 @@ impl Des { process_feistel_rounds(permutated_block, &reversed_subkeys) }; - let combined = concatenate_halves(right, left, 64); + let combined = concatenate_halves(right, left, 32); fp(combined) } } @@ -97,9 +97,10 @@ const fn shift(key: u32, shift: u8) -> u32 { } /// Concatenates two `input_bits`-bit numbers into 2*`input_bits`-bit number +#[inline] #[must_use] -fn concatenate_halves(left: u32, right: u32, input_bits: u32) -> u64 { - (u64::from(left) << input_bits) | u64::from(right) +fn concatenate_halves(left: u32, right: u32, bit_offset: u8) -> u64 { + (u64::from(left) << bit_offset) | u64::from(right) } /// Generate 16 subkeys from the 64-bit key. @@ -152,12 +153,14 @@ fn permutate(input: u64, input_bits: u32, output_bits: u32, position_table: &[u8 }) } +#[inline] #[must_use] fn ip(message: u64) -> u64 { permutate(message, 64, 64, &IP) } /// Expand the right side of the data from 32 bits to 48. +#[inline] #[must_use] fn expansion_permutation(right: u32) -> u64 { permutate(u64::from(right), 32, 48, &E_BOX) @@ -181,14 +184,16 @@ fn s_box_substitution(block: u64) -> u32 { }) } +#[inline] #[must_use] fn p_box_permutation(input: u32) -> u32 { u32::try_from(permutate(u64::from(input), 32, 32, &P_BOX)).expect("32-bit value") } +#[inline] #[must_use] -pub fn fp(input: u64) -> u64 { - todo!() +pub fn fp(block: u64) -> u64 { + permutate(block, 64, 64, &FP) } /// Process 16 Feistel rounds for ECB encryption/decryption. @@ -199,7 +204,7 @@ fn process_feistel_rounds(initial_block: u64, subkeys: &[u64]) -> (u32, u32) { (left, right) = feistel(left, right, subkey); } - (right, left) // left and right should be swapped + (left, right) } /// Feistel function: Expand, XOR with subkey, S-box, permute. /// `R_i` = `L_(i-1)` XOR f(`R_(i-1)`, `K_1`) @@ -209,9 +214,10 @@ fn feistel(left: u32, right: u32, subkey: u64) -> (u32, u32) { let new_right = left ^ function_output; // L_i = R_(i-1) let new_left = right; - (new_right, new_left) + (new_left, new_right) } +#[must_use] fn f_function(right: u32, subkey: u64) -> u32 { let expanded = expansion_permutation(right); let xored = expanded ^ subkey; @@ -222,26 +228,27 @@ fn f_function(right: u32, subkey: u64) -> u32 { #[cfg(test)] mod tests { use super::*; - use crate::constants::S_BOXES; - use claims::{assert_ge, assert_le}; + use claims::assert_ge; use rstest::rstest; const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1; - const TEST_PLAINTEXT: u64 = 0x0123_4567_89AB_CDEF; const TEST_CIPHERTEXT: u64 = 0x85E8_1354_0F0A_B405; const TEST_PC1_RESULT: u64 = 0x00F0_CCAA_F556_678F; // From calculator after PC-1 - const TEST_COMBINED_KEY: u64 = 0x00F0_CCAA_F556_678F; // From calculator after re-combination - const TEST_PC2_RESULT: u64 = 0x0000_CB3D_8B0E_17F5; // From calculator after PC-2 - #[test] - fn initial_permutation() { - let expected_ip = 0xCC00_CCFF_F0AA_F0AA; - let result = ip(TEST_PLAINTEXT); + /// Helper to create a test Des instance (use your actual key schedule) + fn des_instance() -> Des { + Des::new(TEST_KEY) + } + + #[rstest] + #[case(TEST_PLAINTEXT, 0xCC00_CCFF_F0AA_F0AA)] + fn initial_permutation(#[case] input: u64, #[case] output: u64) { + let result = ip(input); assert_eq!( - result, expected_ip, - "Initial permulation failed expected 0x{expected_ip:016X}, got 0x{result:016X}" + result, output, + "Initial permutation failed. Expected 0x{output:016X}, got 0x{result:016X}" ); } @@ -475,4 +482,36 @@ mod tests { let result = p_box_permutation(block); assert_eq!(result, output, "Expected {output:08X}, got {result:08X}"); } + + #[rstest] + #[case(0x0A4C_D995_4342_3234, 0x85E8_1354_0F0A_B405)] + fn final_permutation(#[case] input: u64, #[case] output: u64) { + let result = fp(input); + assert_eq!( + result, output, + "Final permutation failed. Expected 0x{output:016X}, got 0x{result:016X}" + ); + } + + #[test] + fn encrypt_decrypt_roundtrip() { + let des = des_instance(); + + let ciphertext = des.encrypt(TEST_PLAINTEXT); + let dectrypted = des.decrypt(ciphertext); + let re_ciphertext = des.encrypt(dectrypted); + + assert_eq!( + ciphertext, TEST_CIPHERTEXT, + "Encyption failed. Expected 0x{ciphertext:016X}, got 0x{TEST_CIPHERTEXT:016X}" + ); + assert_eq!( + dectrypted, TEST_PLAINTEXT, + "Decyption failed. Expected 0x{dectrypted:016X}, got 0x{TEST_PLAINTEXT:016X}" + ); + assert_eq!( + re_ciphertext, TEST_CIPHERTEXT, + "Re-encyption failed. Expected 0x{re_ciphertext:016X}, got 0x{TEST_CIPHERTEXT:016X}" + ); + } } diff --git a/des-lib/tests/des.rs b/des-lib/tests/des.rs index d573aed..e9fb38a 100644 --- a/des-lib/tests/des.rs +++ b/des-lib/tests/des.rs @@ -1,5 +1,4 @@ -use rand::random; -use std::time::Instant; +use rstest::rstest; use des::Des; @@ -12,7 +11,7 @@ fn des_instance() -> Des { Des::new(TEST_KEY) } -// #[test] +#[test] fn test_ecb_mode_equivalence() { // If you implement ECB mode, test it matches single block let key = 0x1334_5779_9BBC_DFF1; @@ -24,27 +23,50 @@ fn test_ecb_mode_equivalence() { // assert_eq!(single_block, ecb_result[0]); } -// #[test] -fn test_with_real_data() { - // Test with actual 8-byte data - let key_bytes = b"KGenius\x01"; - let key = u64::from_le_bytes(*key_bytes); - - let data_bytes = b"HelloDES!"; - let mut padded = [0u8; 8]; - padded[..data_bytes.len()].copy_from_slice(data_bytes); - let plaintext = u64::from_le_bytes(padded); - +#[rstest] +#[case(TEST_PLAINTEXT, TEST_CIPHERTEXT, TEST_KEY)] +fn encrypt_decrypt_roundtrip( + #[case] plaintext: u64, + #[case] expected_ciphertext: u64, + #[case] key: u64, +) { let des = Des::new(key); - let encrypted = des.encrypt(plaintext); - // Verify we can roundtrip - let decrypted = des.decrypt(encrypted); - let decrypted_bytes = decrypted.to_le_bytes(); - assert_eq!(decrypted_bytes[..data_bytes.len()], *data_bytes); + let ciphertext = des.encrypt(plaintext); + let dectrypted = des.decrypt(ciphertext); + let re_ciphertext = des.encrypt(dectrypted); + + assert_eq!( + ciphertext, expected_ciphertext, + "Encyption failed. Expected 0x{ciphertext:016X}, got 0x{expected_ciphertext:016X}" + ); + assert_eq!( + dectrypted, plaintext, + "Decyption failed. Expected 0x{dectrypted:016X}, got 0x{plaintext:016X}" + ); + assert_eq!( + re_ciphertext, expected_ciphertext, + "Re-encyption failed. Expected 0x{re_ciphertext:016X}, got 0x{expected_ciphertext:016X}" + ); } -// #[test] +#[test] +fn weak_keys_rejected() { + let weak_keys = [0x0101010101010101, 0xFEFEFEFEFEFEFEFE, 0xE001E001E001E001]; + + for key in weak_keys { + let des = Des::new(key); + let plaintext = TEST_PLAINTEXT; + let encrypted = des.encrypt(plaintext); + let dectrypted = des.decrypt(encrypted); + assert_eq!( + dectrypted, plaintext, + "Weak key {key:016X} failed roundtrip" + ); + } +} + +#[test] fn all_zero_paintext() { let des = des_instance(); @@ -54,70 +76,22 @@ fn all_zero_paintext() { assert_eq!(decrypted, plain, "All-zero plaintext failed"); } -// #[test] -#[should_panic(expected = "Invalid key size")] -fn invalid_key_size() { - let _ = Des::new(0); -} - -// #[test] -fn encrypt_decrypt_roundtrip() { - let des = des_instance(); - let plaintext = TEST_PLAINTEXT; - let ciphertext = des.encrypt(plaintext); - let dectrypted = des.decrypt(plaintext); - let re_ciphertext = des.encrypt(dectrypted); - - assert_eq!(ciphertext, TEST_CIPHERTEXT, "Encyption failed"); - assert_eq!(re_ciphertext, TEST_CIPHERTEXT, "Re-Encyption failed"); -} - -// #[test] -fn weak_keys_rejected() { - let weak_keys = [0x0101010101010101, 0xFEFEFEFEFEFEFEFE, 0xE001E001E001E001]; - - for key in weak_keys { - let des = Des::new(key); - let plaintext = TEST_PLAINTEXT; - let encrypted = des.encrypt(plaintext); - let dectrypted = des.decrypt(encrypted); - assert_eq!(dectrypted, plaintext, "Weak key {key} failed roundtrip"); - } -} - -// #[test] -fn multiple_blocks() { - let des = des_instance(); - let blocks = [ - (0x0123456789ABCDEFu64, 0x85E813540F0AB405u64), - (0xFEDCBA9876543210u64, 0xC08BF0FF627D3E6Fu64), // Another test vector - (0x0000000000000000u64, 0x474D5E3B6F8A07F8u64), // Zero block - ]; - for (plaintext, expected) in blocks { - let encrypted = des.encrypt(plaintext); - assert_eq!(encrypted, expected, "Failed on plaintext: {plaintext:016X}"); - - let dectrypted = des.decrypt(encrypted); - assert_eq!(dectrypted, plaintext, "Roundtrip failed on block"); - } -} - -// #[test] +#[test] fn all_one_paintext() { let des = des_instance(); - let plain = 1; + let plain = u64::MAX; let encrypted = des.encrypt(plain); let decrypted = des.decrypt(encrypted); assert_eq!(decrypted, plain, "All-one plaintext failed"); } -// #[test] +#[test] fn different_inputs() { let des = des_instance(); - let plain1 = 0x0000000000000001; - let plain2 = 0x0000000000000002; + let plain1 = 1; + let plain2 = 2; let enc1 = des.encrypt(plain1); let enc2 = des.encrypt(plain2); assert_ne!( @@ -125,43 +99,3 @@ fn different_inputs() { "Encryption not deterministic for different inputs" ); } - -// #[test] -fn performance() { - let des = des_instance(); - let plaintext = TEST_PLAINTEXT; - - let start = Instant::now(); - for _ in 0..10000 { - let _ = des.encrypt(plaintext); - } - let duration = start.elapsed(); - - println!("10k encryption took: {duration:?}"); - // Reasonable benchmark: should be under 1ms on modern hardware - assert!(duration.as_millis() < 100, "Performance degraded"); -} - -// #[test] -fn fuzz_properties() { - let des = des_instance(); - - for _ in 0..100 { - let plaintext = random(); - let encrypted = des.encrypt(plaintext); - let decrypted = des.decrypt(plaintext); - - assert_eq!(decrypted, encrypted, "Fuzz roundtrip failed"); - assert_ne!(encrypted, plaintext, "Encryption is identity function"); - - let key2 = random(); - if key2 != TEST_KEY { - let des2 = Des::new(key2); - let encrypted2 = des2.encrypt(plaintext); - assert_ne!( - encrypted, encrypted2, - "Different keys produced same encryption" - ); - } - } -} diff --git a/des-lib/tests/key_schedule.rs b/des-lib/tests/key_schedule.rs index ef4f12e..08daa64 100644 --- a/des-lib/tests/key_schedule.rs +++ b/des-lib/tests/key_schedule.rs @@ -24,25 +24,6 @@ const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1; #[test] fn key_schedule_generates_correct_subkeys() { - const EXPECTED_SUBKEYS: [u64; 16] = [ - 0x1B02_EFFC_7072, - 0x79AE_D9DB_C9E5, - 0x55FC_8A42_CF99, - 0x72AD_D6DB_351D, - 0x7CEC_07EB_53A8, - 0x63A5_3E50_7B2F, - 0xEC84_B7F6_18BC, - 0xF78A_3AC1_3BFB, - 0xE0DB_EBED_E781, - 0xB1F3_47BA_464F, - 0x215F_D3DE_D386, - 0x7571_F594_67E9, - 0x97C5_D1FA_BA41, - 0x5F43_B7F2_E73A, - 0xBF91_8D3D_3F0A, - 0xCB3D_8B0E_17F5, - ]; - let des = Des::new(TEST_KEY); assert_eq!( @@ -51,41 +32,3 @@ fn key_schedule_generates_correct_subkeys() { des.subkeys ); } - -// #[test] -fn test_rotation_shifts() { - // Test the left rotation logic in key schedule - let mut c: u32 = 0x0FFFFFFF; // 28 bits all 1s - c = c.rotate_left(1); - assert_eq!(c, 0x1FFFFFFF >> 4, "Single bit rotation failed"); - - // Test double shift - let mut d: u32 = 0xAAAAAAA; // 101010... pattern - d = d.rotate_left(2); - assert_eq!(d, 0x2AAAAAA, "Double rotation failed"); // Check pattern shift -} - -// #[test] -fn test_weak_key_detection() { - let weak_keys = [ - 0x0101010101010101u64, // All odd parity - 0xFEFEFEFEFEFEFEFEu64, // All even parity - 0x1F1F1F1F0E0E0E0Eu64, // Semi-weak - ]; - - for key in weak_keys { - let des = Des::new(key); - // Weak keys often produce subkeys that don't vary much - let subkeys = &des.subkeys; - let first = subkeys[0]; - let last = subkeys[15]; - // For true weak keys, many subkeys may be identical - // This is just a basic check - implement full weak key analysis if desired - println!( - "Weak key {} subkeys: first={:012X}, last={:012X}", - key, - first & 0xFFFFFFFFFFFF, - last & 0xFFFFFFFFFFFF - ); - } -}