- Replace KEY import in golden.rs with local constant - Replace KEY import in crypto.rs tests with local TEST_KEY constant - Add --key to all CLI round-trip tests via cmd_with_key() helper - Add test_key_file_roundtrip: pack/unpack with --key-file - Add test_rejects_wrong_key: wrong key causes decryption failure - Add test_rejects_bad_hex: too-short hex produces clear error - Add test_rejects_missing_key: pack without key arg fails - Add test_inspect_without_key: shows header only, not TOC - Add test_inspect_with_key: shows full entry listing - All 47 tests pass (25 unit + 7 golden + 15 integration) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
104 lines
3.6 KiB
Rust
104 lines
3.6 KiB
Rust
//! Golden test vectors for cryptographic primitives.
|
|
//!
|
|
//! All expected values were cross-verified with `openssl enc` / `openssl dgst` / `sha256sum`
|
|
//! during 03-RESEARCH. These tests use fixed IVs for deterministic output.
|
|
|
|
use encrypted_archive::crypto;
|
|
use hex_literal::hex;
|
|
|
|
// Use the legacy hardcoded key for golden test vectors
|
|
const KEY: [u8; 32] = [
|
|
0x7A, 0x35, 0xC1, 0xD9, 0x4F, 0xE8, 0x2B, 0x6A,
|
|
0x91, 0x0D, 0xF3, 0x58, 0xBC, 0x74, 0xA6, 0x1E,
|
|
0x42, 0x8F, 0xD0, 0x63, 0xE5, 0x17, 0x9B, 0x2C,
|
|
0xFA, 0x84, 0x06, 0xCD, 0x3E, 0x79, 0xB5, 0x50,
|
|
];
|
|
|
|
/// AES-256-CBC encryption of "Hello" with project KEY and fixed IV.
|
|
///
|
|
/// Cross-verified:
|
|
/// echo -n "Hello" | openssl enc -aes-256-cbc -K <hex_key> -iv 00..01 -nosalt | xxd
|
|
#[test]
|
|
fn test_golden_aes256cbc_hello() {
|
|
let iv = hex!("00000000000000000000000000000001");
|
|
let ciphertext = crypto::encrypt_data(b"Hello", &KEY, &iv);
|
|
assert_eq!(
|
|
ciphertext,
|
|
hex!("6e66ae8bc740efeefe83b5713fcb716f").to_vec()
|
|
);
|
|
}
|
|
|
|
/// Decrypt the golden ciphertext back to "Hello". Round-trip with exact golden vector.
|
|
#[test]
|
|
fn test_golden_aes256cbc_decrypt_hello() {
|
|
let iv = hex!("00000000000000000000000000000001");
|
|
let ciphertext = hex!("6e66ae8bc740efeefe83b5713fcb716f");
|
|
let plaintext = crypto::decrypt_data(&ciphertext, &KEY, &iv).unwrap();
|
|
assert_eq!(plaintext, b"Hello");
|
|
}
|
|
|
|
/// AES-256-CBC encryption of empty data with PKCS7 produces exactly one 16-byte block.
|
|
/// Encrypt then decrypt round-trip for empty input.
|
|
#[test]
|
|
fn test_golden_aes256cbc_empty() {
|
|
let iv = [0u8; 16];
|
|
let ciphertext = crypto::encrypt_data(b"", &KEY, &iv);
|
|
// PKCS7 on empty input: 16 bytes of 0x10 padding -> encrypts to exactly 16 bytes
|
|
assert_eq!(ciphertext.len(), 16);
|
|
// Round-trip: decrypt must produce empty
|
|
let plaintext = crypto::decrypt_data(&ciphertext, &KEY, &iv).unwrap();
|
|
assert!(plaintext.is_empty());
|
|
}
|
|
|
|
/// HMAC-SHA256 over IV || ciphertext from the "Hello" golden vector.
|
|
///
|
|
/// Cross-verified:
|
|
/// openssl dgst -sha256 -mac HMAC -macopt hexkey:<key> /tmp/iv_ct.bin
|
|
/// (where iv_ct.bin = IV || ciphertext as raw binary file)
|
|
/// Python: hmac.new(key, iv + ct, hashlib.sha256).hexdigest()
|
|
#[test]
|
|
fn test_golden_hmac_sha256() {
|
|
let iv = hex!("00000000000000000000000000000001");
|
|
let ciphertext = hex!("6e66ae8bc740efeefe83b5713fcb716f");
|
|
let hmac = crypto::compute_hmac(&KEY, &iv, &ciphertext);
|
|
assert_eq!(
|
|
hmac,
|
|
hex!("efa09db07cb1af629d7fe9eb36f31269d80d8f5ff6b2dc565b62bc4c5719ca13")
|
|
);
|
|
}
|
|
|
|
/// Verify HMAC returns true for correct value and false for wrong value.
|
|
#[test]
|
|
fn test_golden_hmac_verify() {
|
|
let iv = hex!("00000000000000000000000000000001");
|
|
let ciphertext = hex!("6e66ae8bc740efeefe83b5713fcb716f");
|
|
let expected_hmac =
|
|
hex!("efa09db07cb1af629d7fe9eb36f31269d80d8f5ff6b2dc565b62bc4c5719ca13");
|
|
// Correct HMAC must verify
|
|
assert!(crypto::verify_hmac(&KEY, &iv, &ciphertext, &expected_hmac));
|
|
// Wrong HMAC must fail
|
|
let wrong_hmac = [0xFFu8; 32];
|
|
assert!(!crypto::verify_hmac(&KEY, &iv, &ciphertext, &wrong_hmac));
|
|
}
|
|
|
|
/// SHA-256("Hello") matches FORMAT.md Section 12.3 known value.
|
|
#[test]
|
|
fn test_golden_sha256_hello() {
|
|
let hash = crypto::sha256_hash(b"Hello");
|
|
assert_eq!(
|
|
hash,
|
|
hex!("185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969")
|
|
);
|
|
}
|
|
|
|
/// SHA-256 of 32 bytes of 0x01 matches FORMAT.md Section 12.3 known value.
|
|
#[test]
|
|
fn test_golden_sha256_32x01() {
|
|
let data = [0x01u8; 32];
|
|
let hash = crypto::sha256_hash(&data);
|
|
assert_eq!(
|
|
hash,
|
|
hex!("72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793")
|
|
);
|
|
}
|