test: add parametrized tests

This commit is contained in:
Kristofers Solo 2025-10-01 17:31:49 +03:00
parent 1d099406c7
commit 5468d90a17
Signed by: kristoferssolo
GPG Key ID: 8687F2D3EEE6F0ED
5 changed files with 478 additions and 104 deletions

252
Cargo.lock generated
View File

@ -2,6 +2,15 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.3" version = "1.0.3"
@ -20,6 +29,56 @@ version = "0.1.0"
dependencies = [ dependencies = [
"claims", "claims",
"rand", "rand",
"rstest",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
] ]
[[package]] [[package]]
@ -34,12 +93,52 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
name = "indexmap"
version = "2.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.176" version = "0.2.176"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.21" version = "0.2.21"
@ -49,6 +148,15 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "proc-macro-crate"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [
"toml_edit",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.101" version = "1.0.101"
@ -102,6 +210,111 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "regex"
version = "1.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "relative-path"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "rstest"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
dependencies = [
"futures-timer",
"futures-util",
"rstest_macros",
]
[[package]]
name = "rstest_macros"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
dependencies = [
"cfg-if",
"glob",
"proc-macro-crate",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn",
"unicode-ident",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.106" version = "2.0.106"
@ -113,6 +326,36 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "toml_datetime"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b"
dependencies = [
"indexmap",
"toml_datetime",
"toml_parser",
"winnow",
]
[[package]]
name = "toml_parser"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
dependencies = [
"winnow",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.19" version = "1.0.19"
@ -137,6 +380,15 @@ dependencies = [
"wit-bindgen", "wit-bindgen",
] ]
[[package]]
name = "winnow"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.46.0" version = "0.46.0"

View File

@ -9,6 +9,7 @@ edition = "2024"
[dev-dependencies] [dev-dependencies]
claims = "0.8" claims = "0.8"
rand = "0.9" rand = "0.9"
rstest = "0.26"
[lints.clippy] [lints.clippy]
pedantic = "warn" pedantic = "warn"

View File

@ -11,9 +11,8 @@ impl Des {
/// Create a new DES instance from a 64-bit key (8 bytes). /// Create a new DES instance from a 64-bit key (8 bytes).
#[must_use] #[must_use]
pub fn new(key: u64) -> Self { pub fn new(key: u64) -> Self {
let mut des = Self { subkeys: [0; 16] }; let subkeys = generate_subkeys(key);
des.generate_subkeys(key); Self { subkeys }
des
} }
/// Encrypt a 64-bit block. /// Encrypt a 64-bit block.
@ -48,19 +47,6 @@ impl Des {
todo!() todo!()
} }
/// Generate 16 subkeys from the 64-bit key.
fn generate_subkeys(&mut self, key: u64) {
let reduced_key = pc1(key);
let (mut left, mut right) = split_key(reduced_key);
for (idx, &shift_num) in ROUND_ROTATIONS.iter().enumerate() {
left = shift(left, shift_num);
right = shift(right, shift_num);
let combined = contatinate_keys(left, right);
let subkey = pc2(combined);
self.subkeys[idx] = subkey;
}
}
/// Helper functions for permutations (bit manipulation) /// Helper functions for permutations (bit manipulation)
#[must_use] #[must_use]
fn permutate(&self, input: u32, table: &[u8], n: usize) -> u32 { fn permutate(&self, input: u32, table: &[u8], n: usize) -> u32 {
@ -167,10 +153,28 @@ const fn shift(key: u32, shift: u8) -> u32 {
(main_shifted | wrapped_bits) & MASK (main_shifted | wrapped_bits) & MASK
} }
/// Concatinates two 28-bit numbers into 56-bit number /// Concatenates two 28-bit numbers into 56-bit number
#[must_use] #[must_use]
fn contatinate_keys(left: u32, right: u32) -> u64 { fn concatenate_keys(left: u32, right: u32) -> u64 {
(u64::from(right) << 28) | u64::from(left) (u64::from(left) << 28) | u64::from(right)
}
/// Generate 16 subkeys from the 64-bit key.
fn generate_subkeys(key: u64) -> [u64; 16] {
let reduced_key = pc1(key); // C_0, D_0
let (mut left, mut right) = split_key(reduced_key);
ROUND_ROTATIONS
.iter()
.map(|&shift_amount| {
left = shift(left, shift_amount); // C_(n-1) -> C_n
right = shift(right, shift_amount); // D_(n-1) -> D_n
let combined = concatenate_keys(left, right);
pc2(combined)
})
.collect::<Vec<_>>()
.try_into()
.expect("Exactly 16 subkeys expected")
} }
/// Encrypts data using ECB mode. /// Encrypts data using ECB mode.
@ -211,17 +215,22 @@ pub fn decrypt_ecb(data: &[u8], key: &[u8; 8]) -> Vec<u8> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::constants::S_BOXES;
use super::*; use super::*;
use claims::assert_le; use crate::constants::S_BOXES;
use claims::{assert_ge, assert_le};
use rand::random; use rand::random;
use rstest::rstest;
use std::time::Instant; use std::time::Instant;
const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1; const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1;
const RIGHT_KEY: u32 = 0x12345678;
const TEST_PLAINTEXT: u64 = 0x0123456789ABCDEF; const RIGHT_KEY: u32 = 0x1234_5678;
const TEST_CIPHERTEXT: u64 = 0x85E813540F0AB405; 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
impl Des { impl Des {
fn apply_sboxes(&self, input: u64) -> u32 { fn apply_sboxes(&self, input: u64) -> u32 {
@ -278,65 +287,162 @@ mod tests {
} }
} }
// #[test]
fn key_schedule_generates_correct_subkeys() {
let expected_subkeys = [
0xF3FDFBF373848CF5u64,
0xF3738CF548C4F3F5u64,
0x848C4F3F5F373848u64,
];
let des = des_instance();
let generated = des.subkeys;
for (idx, &expected) in expected_subkeys.iter().enumerate() {
let masked_gen = generated[idx];
let masked_exp = expected;
assert_eq!(
masked_gen, masked_exp,
"Subkey {idx} mismatch: expected {masked_exp:012X}, got {masked_gen:012X}"
);
}
}
// #[test] // #[test]
fn initial_permutation() { fn initial_permutation() {
let input = TEST_KEY;
let expected_ip = 0xC2B093C7A3A7C24A; let expected_ip = 0xC2B093C7A3A7C24A;
let result = des_instance().ip(input); let result = des_instance().ip(TEST_KEY);
assert_eq!(result, expected_ip, "Initial permulation failed"); assert_eq!(result, expected_ip, "Initial permulation failed");
} }
#[test] #[test]
fn pc1_permutaion_correct() { fn pc1_permutaion_correct() {
let key = TEST_KEY; let result = pc1(TEST_KEY);
let expected_pc1 = 0x00F0_CCAA_F556_678F; // Truncated 56 bits from spec
let result = pc1(key); assert_eq!(result, TEST_PC1_RESULT, "PC1 permutation failed");
assert_ge!(
result.leading_zeros(),
0,
"PC1 result should have leading 8 bits as 0"
);
}
assert_eq!(result, expected_pc1, "PC1 permutation failed"); #[rstest]
assert_eq!(result >> 56, 0, "PC1 result should have high 8 bits as 0"); #[case(0x00F0_CCAA_F556_678F, 0xCB3D_8B0E_17F5)] // K_0
assert_eq!( #[case(0x00E1_9955_FAAC_CF1E, 0x1B02_EFFC_7072)] // K_1
result & 0x00FF_FFFF_FFFF_FFFF, #[case(0x00C3_32AB_F559_9E3D, 0x79AE_D9DB_C9E5)] // K_2
expected_pc1, #[case(0x000C_CAAF_F566_78F5, 0x55FC_8A42_CF99)] // K_3
"PC1 should be 56 bits or less" #[case(0x0033_2ABF_C599_E3D5, 0x72AD_D6DB_351D)] // K_4
#[case(0x00CC_AAFF_0667_8F55, 0x7CEC_07EB_53A8)] // K_5
#[case(0x0032_ABFC_399E_3D55, 0x63A5_3E50_7B2F)] // K_6
#[case(0x00CA_AFF0_C678_F556, 0xEC84_B7F6_18BC)] // K_7
#[case(0x002A_BFC3_39E3_D559, 0xF78A_3AC1_3BFB)] // K_8
#[case(0x0055_7F86_63C7_AAB3, 0xE0DB_EBED_E781)] // K_9
#[case(0x0055_FE19_9F1E_AACC, 0xB1F3_47BA_464F)] // K_10
#[case(0x0057_F866_5C7A_AB33, 0x215F_D3DE_D386)] // K_11
#[case(0x005F_E199_51EA_ACCF, 0x7571_F594_67E9)] // K_12
#[case(0x007F_8665_57AA_B33C, 0x97C5_D1FA_BA41)] // K_13
#[case(0x00FE_1995_5EAA_CCF1, 0x5F43_B7F2_E73A)] // K_14
#[case(0x00F8_6655_7AAB_33C7, 0xBF91_8D3D_3F0A)] // K_15
#[case(0x00F0_CCAA_F556_678F, 0xCB3D_8B0E_17F5)] // K_16
fn pc2_permutaion_correct(#[case] before: u64, #[case] after: u64) {
let result = pc2(before);
assert_eq!(result, after, "PC2 permutation failed");
assert_ge!(
result.leading_zeros(),
16,
"PC2 result should have leading 16 bits as 0"
); );
} }
#[test] #[test]
fn pc2_permutaion_correct() { fn split_key_56_bits() {
let combined = 0x00F0_CCAA_F556_678F; // [D_0 << 28] | C_0 let (left, right) = split_key(TEST_PC1_RESULT);
let expected_subkey = 0x0000_CB3D_8B0E_17F5; // Expected 48-bit result
let result = pc2(combined); assert_eq!(left, 0x0F0C_CAAF, "split_key left half mismatch",);
assert_eq!(right, 0x0556_678F, "split_key right half mismatch",);
assert_eq!(result, expected_subkey, "PC1 permutation failed"); // Verify 28-bit values have 4 leading zeros in u32
assert_eq!(result >> 56, 0, "PC2 result should have high 8 bits as 0"); assert_ge!(
assert_eq!( left.leading_zeros(),
result & 0x00FF_FFFF_FFFF_FFFF, 4,
expected_subkey, "Left should be 28-bit value in u32"
"PC2 should be 56 bits or less"
); );
assert_ge!(
right.leading_zeros(),
4,
"Right should be 28-bit value in u32"
);
}
#[rstest]
#[case(0x0F0C_CAAF, 0x0E19_955F, 1)] // C_1
#[case(0x0E19_955F, 0x0C33_2ABF, 1)] // C_2
#[case(0x0C33_2ABF, 0x00CC_AAFF, 2)] // C_3
#[case(0x00CC_AAFF, 0x0332_ABFC, 2)] // C_4
#[case(0x0332_ABFC, 0x0CCA_AFF0, 2)] // C_5
#[case(0x0CCA_AFF0, 0x032A_BFC3, 2)] // C_6
#[case(0x032A_BFC3, 0x0CAA_FF0C, 2)] // C_7
#[case(0x0CAA_FF0C, 0x02AB_FC33, 2)] // C_8
#[case(0x02AB_FC33, 0x0557_F866, 1)] // C_9
#[case(0x0557_F866, 0x055F_E199, 2)] // C_10
#[case(0x055F_E199, 0x057F_8665, 2)] // C_11
#[case(0x057F_8665, 0x05FE_1995, 2)] // C_12
#[case(0x05FE_1995, 0x07F8_6655, 2)] // C_13
#[case(0x07F8_6655, 0x0FE1_9955, 2)] // C_14
#[case(0x0FE1_9955, 0x0F86_6557, 2)] // C_15
#[case(0x0F86_6557, 0x0F0C_CAAF, 1)] // C_16
#[case(0x0556_678F, 0x0AAC_CF1E, 1)] // D_1
#[case(0x0AAC_CF1E, 0x0559_9E3D, 1)] // D_2
#[case(0x0559_9E3D, 0x0566_78F5, 2)] // D_3
#[case(0x0566_78F5, 0x0599_E3D5, 2)] // D_4
#[case(0x0599_E3D5, 0x0667_8F55, 2)] // D_5
#[case(0x0667_8F55, 0x099E_3D55, 2)] // D_6
#[case(0x099E_3D55, 0x0678_F556, 2)] // D_7
#[case(0x0678_F556, 0x09E3_D559, 2)] // D_8
#[case(0x09E3_D559, 0x03C7_AAB3, 1)] // D_9
#[case(0x03C7_AAB3, 0x0F1E_AACC, 2)] // D_10
#[case(0x0F1E_AACC, 0x0C7A_AB33, 2)] // D_11
#[case(0x0C7A_AB33, 0x01EA_ACCF, 2)] // D_12
#[case(0x01EA_ACCF, 0x07AA_B33C, 2)] // D_13
#[case(0x07AA_B33C, 0x0EAA_CCF1, 2)] // D_14
#[case(0x0EAA_CCF1, 0x0AAB_33C7, 2)] // D_15
#[case(0x0AAB_33C7, 0x0556_678F, 1)] // D_16
fn shift_rotation(#[case] key: u32, #[case] expected_output: u32, #[case] shift_amount: u8) {
let result = shift(key, shift_amount);
assert_eq!(
result, expected_output,
"shift(0x{key:08X}, {shift_amount}) should equal 0x{expected_output:08X}"
);
// Verify result is still 28 bits
assert_eq!(
result & 0x0FFF_FFFF,
expected_output,
"Shift result should preserve 28 bits"
);
assert_ge!(
result.leading_zeros(),
4,
"Shift result should be 28-bit value in u32"
);
}
#[rstest]
#[case(0x0F0C_CAAF, 0x0556_678F, 0x00F0_CCAA_F556_678F)] // CD_0
#[case(0x0E19_955F, 0x0AAC_CF1E, 0x00E1_9955_FAAC_CF1E)] // CD_1
#[case(0x0C33_2ABF, 0x0559_9E3D, 0x00C3_32AB_F559_9E3D)] // CD_2
#[case(0x00CC_AAFF, 0x0566_78F5, 0x000C_CAAF_F566_78F5)] // CD_3
#[case(0x0332_ABFC, 0x0599_E3D5, 0x0033_2ABF_C599_E3D5)] // CD_4
#[case(0x0CCA_AFF0, 0x0667_8F55, 0x00CC_AAFF_0667_8F55)] // CD_5
#[case(0x032A_BFC3, 0x099E_3D55, 0x0032_ABFC_399E_3D55)] // CD_6
#[case(0x0CAA_FF0C, 0x0678_F556, 0x00CA_AFF0_C678_F556)] // CD_7
#[case(0x02AB_FC33, 0x09E3_D559, 0x002A_BFC3_39E3_D559)] // CD_8
#[case(0x0557_F866, 0x03C7_AAB3, 0x0055_7F86_63C7_AAB3)] // CD_9
#[case(0x055F_E199, 0x0F1E_AACC, 0x0055_FE19_9F1E_AACC)] // CD_10
#[case(0x057F_8665, 0x0C7A_AB33, 0x0057_F866_5C7A_AB33)] // CD_11
#[case(0x05FE_1995, 0x01EA_ACCF, 0x005F_E199_51EA_ACCF)] // CD_12
#[case(0x07F8_6655, 0x07AA_B33C, 0x007F_8665_57AA_B33C)] // CD_13
#[case(0x0FE1_9955, 0x0EAA_CCF1, 0x00FE_1995_5EAA_CCF1)] // CD_14
#[case(0x0F86_6557, 0x0AAB_33C7, 0x00F8_6655_7AAB_33C7)] // CD_15
#[case(0x0F0C_CAAF, 0x0556_678F, 0x00F0_CCAA_F556_678F)] // CD_16
fn key_concatenation(#[case] left: u32, #[case] right: u32, #[case] combined: u64) {
let result = concatenate_keys(left, right);
assert_eq!(result, combined, "{result:016X} != {combined:016X}");
// Verify correct bit layout
assert_eq!(
(result >> 28) & 0x0FFF_FFFF_FFFF,
left as u64,
"High 28 bits should be left"
);
assert_eq!(
result & 0x0FFF_FFFF,
right as u64,
"Low 28 bits should be right"
);
assert_eq!(result >> 56, 0, "Combined should fit in 56 bits");
} }
// #[test] // #[test]

View File

@ -1,10 +1,10 @@
use des::DES; use des::Des;
#[test] // #[test]
fn test_ecb_mode_equivalence() { fn test_ecb_mode_equivalence() {
// If you implement ECB mode, test it matches single block // If you implement ECB mode, test it matches single block
let key = 0x1334_5779_9BBC_DFF1; let key = 0x1334_5779_9BBC_DFF1;
let des = DES::new(key); let des = Des::new(key);
let plain = 0x0123_4567_89AB_CDEF; let plain = 0x0123_4567_89AB_CDEF;
let _single_block = des.encrypt(plain); let _single_block = des.encrypt(plain);
@ -12,7 +12,7 @@ fn test_ecb_mode_equivalence() {
// assert_eq!(single_block, ecb_result[0]); // assert_eq!(single_block, ecb_result[0]);
} }
#[test] // #[test]
fn test_with_real_data() { fn test_with_real_data() {
// Test with actual 8-byte data // Test with actual 8-byte data
let key_bytes = b"KGenius\x01"; let key_bytes = b"KGenius\x01";
@ -23,7 +23,7 @@ fn test_with_real_data() {
padded[..data_bytes.len()].copy_from_slice(data_bytes); padded[..data_bytes.len()].copy_from_slice(data_bytes);
let plaintext = u64::from_le_bytes(padded); let plaintext = u64::from_le_bytes(padded);
let des = DES::new(key); let des = Des::new(key);
let encrypted = des.encrypt(plaintext); let encrypted = des.encrypt(plaintext);
// Verify we can roundtrip // Verify we can roundtrip

View File

@ -1,43 +1,58 @@
use des::DES; use des::Des;
// Full expected subkeys for TEST_KEY (48 bits each, from FIPS spec) // Full expected subkeys for TEST_KEY (48 bits each, from FIPS spec)
const EXPECTED_SUBKEYS: [u64; 16] = [ const EXPECTED_SUBKEYS: [u64; 16] = [
0xF3FDFBF373848CF5u64, 0x1B02_EFFC_7072,
0xF3738CF548C4F3F5u64, 0x79AE_D9DB_C9E5,
0x848C4F3F5F373848u64, 0x55FC_8A42_CF99,
0xC4F3F5F373848CCFu64, 0x72AD_D6DB_351D,
0xF3F5F373848CCF39u64, 0x7CEC_07EB_53A8,
0x5F373848CCF39A7Au64, 0x63A5_3E50_7B2F,
0x373848CCF39A7A29u64, 0xEC84_B7F6_18BC,
0x848CCF39A7A29D6Bu64, 0xF78A_3AC1_3BFB,
0xCCF39A7A29D6B3E6u64, 0xE0DB_EBED_E781,
0xF39A7A29D6B3E674u64, 0xB1F3_47BA_464F,
0x9A7A29D6B3E674F1u64, 0x215F_D3DE_D386,
0x7A29D6B3E674F1D3u64, 0x7571_F594_67E9,
0x29D6B3E674F1D39Bu64, 0x97C5_D1FA_BA41,
0xD6B3E674F1D39BFAu64, 0x5F43_B7F2_E73A,
0xB3E674F1D39BFACFu64, 0xBF91_8D3D_3F0A,
0xE674F1D39BFACF3Fu64, 0xCB3D_8B0E_17F5,
]; ];
const TEST_KEY: u64 = 0x133457799BBCDFF1; const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1;
#[test] #[test]
fn test_full_key_schedule() { fn key_schedule_generates_correct_subkeys() {
let des = DES::new(TEST_KEY); 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,
];
for (i, &expected) in EXPECTED_SUBKEYS.iter().enumerate() { let des = Des::new(TEST_KEY);
let masked_gen = des.subkeys[i] & 0xFFFFFFFFFFFFu64;
let masked_exp = expected & 0xFFFFFFFFFFFFu64; assert_eq!(
assert_eq!( des.subkeys, EXPECTED_SUBKEYS,
masked_gen, masked_exp, "Subkey generation failed. Expected: {EXPECTED_SUBKEYS:?}, Got: {:?}",
"Subkey {} failed: expected {:012X}, got {:012X}", des.subkeys
i, masked_exp, masked_gen );
);
}
} }
#[test] // #[test]
fn test_rotation_shifts() { fn test_rotation_shifts() {
// Test the left rotation logic in key schedule // Test the left rotation logic in key schedule
let mut c: u32 = 0x0FFFFFFF; // 28 bits all 1s let mut c: u32 = 0x0FFFFFFF; // 28 bits all 1s
@ -50,7 +65,7 @@ fn test_rotation_shifts() {
assert_eq!(d, 0x2AAAAAA, "Double rotation failed"); // Check pattern shift assert_eq!(d, 0x2AAAAAA, "Double rotation failed"); // Check pattern shift
} }
#[test] // #[test]
fn test_weak_key_detection() { fn test_weak_key_detection() {
let weak_keys = [ let weak_keys = [
0x0101010101010101u64, // All odd parity 0x0101010101010101u64, // All odd parity
@ -59,7 +74,7 @@ fn test_weak_key_detection() {
]; ];
for key in weak_keys { for key in weak_keys {
let des = DES::new(key); let des = Des::new(key);
// Weak keys often produce subkeys that don't vary much // Weak keys often produce subkeys that don't vary much
let subkeys = &des.subkeys; let subkeys = &des.subkeys;
let first = subkeys[0]; let first = subkeys[0];