-
Notifications
You must be signed in to change notification settings - Fork 92
test: Add Wycheproof-based AES-CBC-PKCS5 tests #363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -485,3 +485,164 @@ fn aes_gcm_message_wycheproof() -> TestResult { | |
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Test AES-CBC with PKCS5 padding using Wycheproof test vectors | ||
| #[test] | ||
| #[serial] | ||
| fn aes_cbc_pkcs5_wycheproof() -> TestResult { | ||
| let (pkcs11, slot) = init_pins(); | ||
| let session = pkcs11.open_rw_session(slot)?; | ||
| session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; | ||
|
|
||
| // Load Wycheproof AES-CBC-PKCS5 test vectors | ||
| let test_set = wycheproof::cipher::TestSet::load(wycheproof::cipher::TestName::AesCbcPkcs5)?; | ||
|
|
||
| let mut passed = 0; | ||
| let mut failed = 0; | ||
| let mut skipped = 0; | ||
|
|
||
| for test_group in &test_set.test_groups { | ||
| let key_size = test_group.key_size; | ||
|
|
||
| // Only test key sizes we support (128, 192, 256 bits) | ||
| if ![128, 192, 256].contains(&key_size) { | ||
| skipped += test_group.tests.len(); | ||
| continue; | ||
| } | ||
|
|
||
| for test in &test_group.tests { | ||
| // Skip tests with nonce (IV) sizes other than 16 bytes (AES block size) | ||
| if test.nonce.len() != 16 { | ||
| skipped += 1; | ||
| continue; | ||
| } | ||
|
|
||
| // Import the test key | ||
| let key_template = vec![ | ||
| Attribute::Class(cryptoki::object::ObjectClass::SECRET_KEY), | ||
| Attribute::KeyType(cryptoki::object::KeyType::AES), | ||
| Attribute::Token(false), | ||
| Attribute::Sensitive(false), | ||
| Attribute::Extractable(true), | ||
| Attribute::Encrypt(true), | ||
| Attribute::Decrypt(true), | ||
| Attribute::Value(test.key.to_vec()), | ||
| ]; | ||
|
|
||
| let key = match session.create_object(&key_template) { | ||
| Ok(k) => k, | ||
| Err(e) => { | ||
| eprintln!("Test {}: Failed to create key: {:?}", test.tc_id, e); | ||
| failed += 1; | ||
| continue; | ||
| } | ||
| }; | ||
|
|
||
| // Convert IV to fixed-size array for AesCbcPad | ||
| let mut iv = [0u8; 16]; | ||
| iv.copy_from_slice(&test.nonce[0..16]); | ||
|
|
||
| // Test encryption | ||
| let mechanism = Mechanism::AesCbcPad(iv); | ||
| let encrypt_result = session.encrypt(&mechanism, key, &test.pt[..]); | ||
|
|
||
| match (&test.result, encrypt_result) { | ||
| // Valid test should succeed | ||
| (wycheproof::TestResult::Valid, Ok(ciphertext)) => { | ||
| if ciphertext == test.ct[..] { | ||
| println!( | ||
| "✓ Test {}: {:?} - Key: {}-bit, IV: {}, PT: {}, CT: {}", | ||
| test.tc_id, | ||
| test.result, | ||
| key_size, | ||
| test.nonce.len(), | ||
| test.pt.len(), | ||
| test.ct.len() | ||
| ); | ||
| passed += 1; | ||
| } else { | ||
| eprintln!( | ||
| "✗ Test {}: Encryption output mismatch (expected valid)", | ||
| test.tc_id | ||
| ); | ||
| eprintln!( | ||
| " Key size: {}, IV len: {}, PT len: {}, CT len: {}", | ||
| key_size, | ||
| test.nonce.len(), | ||
| test.pt.len(), | ||
| test.ct.len() | ||
| ); | ||
| eprintln!(" Expected: {}", hex::encode(&test.ct[..])); | ||
| eprintln!(" Got: {}", hex::encode(&ciphertext)); | ||
| failed += 1; | ||
| } | ||
| } | ||
| // Invalid/Acceptable tests may fail - this is good | ||
| (wycheproof::TestResult::Invalid | wycheproof::TestResult::Acceptable, Err(_)) => { | ||
| println!( | ||
| "✓ Test {}: {:?} (expected failure) - Key: {}-bit, IV: {}, PT: {}", | ||
| test.tc_id, | ||
| test.result, | ||
| key_size, | ||
| test.nonce.len(), | ||
| test.pt.len() | ||
| ); | ||
| passed += 1; | ||
| } | ||
| // Invalid test that succeeded - Note: HSM may not catch all invalid cases | ||
| (wycheproof::TestResult::Invalid, Ok(_)) => { | ||
| println!( | ||
| "✓ Test {}: {:?} (HSM accepted, which is OK) - Key: {}-bit, IV: {}, PT: {}", | ||
| test.tc_id, | ||
| test.result, | ||
| key_size, | ||
| test.nonce.len(), | ||
| test.pt.len() | ||
| ); | ||
| passed += 1; | ||
| } | ||
| // Valid test that failed - this shouldn't happen | ||
| (wycheproof::TestResult::Valid, Err(e)) => { | ||
| eprintln!("✗ Test {}: Valid test FAILED: {:?}", test.tc_id, e); | ||
| eprintln!( | ||
| " Key size: {}, IV len: {}, PT len: {}, CT len: {}", | ||
| key_size, | ||
| test.nonce.len(), | ||
| test.pt.len(), | ||
| test.ct.len() | ||
| ); | ||
| failed += 1; | ||
| } | ||
| // Acceptable tests can go either way | ||
| (wycheproof::TestResult::Acceptable, Ok(_)) => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move the // Acceptable tests can go either way
(wycheproof::TestResult::Acceptable, Ok(_) | Err(_)) => {
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going through the source file for this input, I do not see any tests marked Acceptable: https://github.com/randombit/wycheproof-rs/blob/master/src/data/aes_cbc_pkcs5_test.json Could we prune the branches that are not executed? |
||
| println!( | ||
| "✓ Test {}: {:?} (HSM accepted) - Key: {}-bit, IV: {}, PT: {}", | ||
| test.tc_id, | ||
| test.result, | ||
| key_size, | ||
| test.nonce.len(), | ||
| test.pt.len() | ||
| ); | ||
| passed += 1; | ||
| } | ||
| } | ||
|
|
||
| // Clean up | ||
| let _ = session.destroy_object(key); | ||
| } | ||
| } | ||
|
|
||
| println!( | ||
| "AES-CBC-PKCS5 Wycheproof results: {} passed, {} failed, {} skipped", | ||
| passed, failed, skipped | ||
| ); | ||
|
|
||
| // Always clean up resources before asserting, so if assert panics, cleanup still happened | ||
| session.close()?; | ||
| pkcs11.finalize()?; | ||
|
|
||
| // The main requirement is that Valid tests pass | ||
| assert_eq!(failed, 0, "Some valid Wycheproof tests failed"); | ||
|
|
||
| Ok(()) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that actually happen with some of the test cases? Maybe it would be safe to double-check those and filter-out on invalid cases we do not support?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think most of the Invalid tasks have BadPadding flag. which can be used to filter it more precisely.
https://github.com/randombit/wycheproof-rs/blob/master/src/data/aes_cbc_pkcs5_test.json#L424