#![cfg(feature = "std")]
use proptest::prelude::*;
use uselesskey_core::negative::{CorruptPem, corrupt_pem, truncate_der};
use uselesskey_core::{ArtifactId, DerivationVersion, Factory, Seed};
fn spec_bytes(bits: u32, e: u32) -> Vec<u8> {
let mut v = Vec::new();
v.extend_from_slice(&bits.to_be_bytes());
v.extend_from_slice(&e.to_be_bytes());
v
}
fn seed_array<const N: usize>(seed: Seed) -> [u8; N] {
let mut buf = [0u8; N];
seed.fill_bytes(&mut buf);
buf
}
fn derive_seed_for_test(master: &Seed, domain: &'static str, label: &str, variant: &str) -> Seed {
let _id = ArtifactId::new(domain, label, &[0u8; 8], variant, DerivationVersion::V1);
let fx = Factory::deterministic(*master);
let val = fx.get_or_init(domain, label, &[0u8; 8], variant, seed_array::<32>);
Seed::new(*val)
}
#[test]
fn deterministic_is_order_independent_for_cache_keys() {
let seed = Seed::new([42u8; 32]);
let fx = Factory::deterministic(seed);
let a = fx.get_or_init("domain:a", "label", &spec_bytes(1, 2), "good", |_rng| {
123u32
});
let b = fx.get_or_init("domain:b", "label", &spec_bytes(3, 4), "good", |_rng| {
456u32
});
fx.clear_cache();
let b2 = fx.get_or_init("domain:b", "label", &spec_bytes(3, 4), "good", |_rng| {
456u32
});
let a2 = fx.get_or_init("domain:a", "label", &spec_bytes(1, 2), "good", |_rng| {
123u32
});
assert_eq!(*a, *a2);
assert_eq!(*b, *b2);
}
proptest! {
#[test]
fn deterministic_factory_returns_same_value_for_same_id(seed_bytes in any::<[u8;32]>(), label in "[-_a-zA-Z0-9]{1,32}") {
let fx = Factory::deterministic(Seed::new(seed_bytes));
let spec = spec_bytes(2048, 65537);
let v1 = fx.get_or_init("domain:test", &label, &spec, "good", |_rng| 7u32);
let v2 = fx.get_or_init("domain:test", &label, &spec, "good", |_rng| 7u32);
prop_assert_eq!(*v1, *v2);
}
#[test]
fn seed_from_env_value_handles_arbitrary_strings(s in ".*") {
let _result = Seed::from_env_value(&s);
}
#[test]
fn seed_from_env_value_non_64_byte_ascii_strings_always_ok(s in "[a-zA-Z0-9!@#$%^&*()]{0,63}|[a-zA-Z0-9!@#$%^&*()]{65,200}") {
let trimmed = s.trim();
let after_prefix = trimmed
.strip_prefix("0x")
.or_else(|| trimmed.strip_prefix("0X"))
.unwrap_or(trimmed);
prop_assume!(after_prefix.len() != 64);
let result = Seed::from_env_value(&s);
prop_assert!(result.is_ok(), "Non-64-byte strings should always be Ok, got: {:?}", result);
}
#[test]
fn seed_from_env_value_valid_hex_produces_valid_seeds(hex_bytes in any::<[u8; 32]>()) {
let hex: String = hex_bytes.iter().map(|b| format!("{:02x}", b)).collect();
prop_assert_eq!(hex.len(), 64);
let result = Seed::from_env_value(&hex);
prop_assert!(result.is_ok());
let seed = result.unwrap();
prop_assert_eq!(seed.bytes(), &hex_bytes);
}
#[test]
fn seed_from_env_value_valid_hex_with_prefix(hex_bytes in any::<[u8; 32]>()) {
let hex: String = format!("0x{}", hex_bytes.iter().map(|b| format!("{:02x}", b)).collect::<String>());
let result = Seed::from_env_value(&hex);
prop_assert!(result.is_ok());
let seed = result.unwrap();
prop_assert_eq!(seed.bytes(), &hex_bytes);
}
#[test]
fn seed_from_env_value_valid_hex_with_uppercase_prefix(hex_bytes in any::<[u8; 32]>()) {
let hex: String = format!("0X{}", hex_bytes.iter().map(|b| format!("{:02x}", b)).collect::<String>());
let result = Seed::from_env_value(&hex);
prop_assert!(result.is_ok());
let seed = result.unwrap();
prop_assert_eq!(seed.bytes(), &hex_bytes);
}
#[test]
fn different_seeds_produce_different_derived_seeds(
seed1 in any::<[u8; 32]>(),
seed2 in any::<[u8; 32]>()
) {
prop_assume!(seed1 != seed2);
let master1 = Seed::new(seed1);
let master2 = Seed::new(seed2);
let derived1 = derive_seed_for_test(&master1, "domain:test", "label", "variant");
let derived2 = derive_seed_for_test(&master2, "domain:test", "label", "variant");
prop_assert_ne!(derived1.bytes(), derived2.bytes());
}
#[test]
fn different_labels_produce_different_derived_seeds(
seed in any::<[u8; 32]>(),
label1 in "[a-zA-Z0-9]{1,16}",
label2 in "[a-zA-Z0-9]{1,16}"
) {
prop_assume!(label1 != label2);
let master = Seed::new(seed);
let derived1 = derive_seed_for_test(&master, "domain:test", &label1, "variant");
let derived2 = derive_seed_for_test(&master, "domain:test", &label2, "variant");
prop_assert_ne!(derived1.bytes(), derived2.bytes());
}
#[test]
fn different_variants_produce_different_derived_seeds(
seed in any::<[u8; 32]>(),
variant1 in "[a-zA-Z0-9]{1,16}",
variant2 in "[a-zA-Z0-9]{1,16}"
) {
prop_assume!(variant1 != variant2);
let master = Seed::new(seed);
let derived1 = derive_seed_for_test(&master, "domain:test", "label", &variant1);
let derived2 = derive_seed_for_test(&master, "domain:test", "label", &variant2);
prop_assert_ne!(derived1.bytes(), derived2.bytes());
}
#[test]
fn truncate_der_with_length_gte_original_returns_original(
der in prop::collection::vec(any::<u8>(), 1..256),
extra in 0usize..100
) {
let len = der.len() + extra;
let result = truncate_der(&der, len);
prop_assert_eq!(result, der);
}
#[test]
fn truncate_der_with_length_lt_original_returns_truncated(
der in prop::collection::vec(any::<u8>(), 2..256),
divisor in 1usize..10
) {
let len = der.len() / divisor.max(1);
prop_assume!(len < der.len());
let result = truncate_der(&der, len);
prop_assert_eq!(result.len(), len);
prop_assert_eq!(result, &der[..len]);
}
#[test]
fn corrupt_pem_all_variants_differ_from_input(
body in "[A-Za-z0-9+/]{64,256}"
) {
let pem = format!("-----BEGIN TEST KEY-----\n{body}\n-----END TEST KEY-----");
let variants = [
CorruptPem::BadHeader,
CorruptPem::BadFooter,
CorruptPem::BadBase64,
CorruptPem::Truncate { bytes: pem.len() / 2 },
CorruptPem::ExtraBlankLine,
];
for variant in variants {
let corrupted = corrupt_pem(&pem, variant);
prop_assert_ne!(
corrupted, pem.clone(),
"CorruptPem::{:?} should produce output different from input",
variant
);
}
}
#[test]
fn prop_order_independence(
seed_bytes in any::<[u8; 32]>(),
label_a in "[a-zA-Z]{1,8}",
label_b in "[a-zA-Z]{1,8}",
) {
prop_assume!(label_a != label_b);
let spec = spec_bytes(2048, 65537);
let fx1 = Factory::deterministic(Seed::new(seed_bytes));
let a1 = fx1.get_or_init("domain:ord", &label_a, &spec, "good", seed_array::<32>);
let b1 = fx1.get_or_init("domain:ord", &label_b, &spec, "good", seed_array::<32>);
let fx2 = Factory::deterministic(Seed::new(seed_bytes));
let b2 = fx2.get_or_init("domain:ord", &label_b, &spec, "good", seed_array::<32>);
let a2 = fx2.get_or_init("domain:ord", &label_a, &spec, "good", seed_array::<32>);
prop_assert_eq!(*a1, *a2, "label_a value should be identical regardless of order");
prop_assert_eq!(*b1, *b2, "label_b value should be identical regardless of order");
}
#[test]
fn prop_cache_hit_returns_same_arc(
seed_bytes in any::<[u8; 32]>(),
label in "[a-zA-Z0-9]{1,16}",
) {
let fx = Factory::deterministic(Seed::new(seed_bytes));
let spec = spec_bytes(2048, 65537);
let first = fx.get_or_init("domain:arc", &label, &spec, "good", seed_array::<32>);
let second = fx.get_or_init("domain:arc", &label, &spec, "good", seed_array::<32>);
prop_assert!(
std::sync::Arc::ptr_eq(&first, &second),
"cache hit should return the same Arc pointer"
);
}
#[test]
fn prop_derived_seed_is_32_bytes_nonzero(
seed_bytes in any::<[u8; 32]>(),
label in "[a-zA-Z0-9]{1,16}",
) {
let master = Seed::new(seed_bytes);
let derived = derive_seed_for_test(&master, "domain:sz", &label, "good");
prop_assert_eq!(derived.bytes().len(), 32);
prop_assert!(
derived.bytes().iter().any(|&b| b != 0),
"derived seed should not be all-zero"
);
}
}