use std::sync::Arc;
use structured_asn1::{
Encoding, Limits, Parser,
memory::{Memory, MemoryAccount},
};
use structured_pkix::usage::ExtendedKeyUsage;
fn tlv(tag: u8, contents: &[u8]) -> Vec<u8> {
let mut bytes = vec![tag];
if contents.len() < 128 {
bytes.push(u8::try_from(contents.len()).unwrap());
} else {
bytes.push(0x82);
bytes.extend(u16::try_from(contents.len()).unwrap().to_be_bytes());
}
bytes.extend(contents);
bytes
}
#[test]
fn original_eku_catalogue_obeys_local_work_before_growth() {
let mut purposes = Vec::new();
for suffix in 1..=64 {
purposes.extend(tlv(6, &[42, suffix]));
}
let bytes = tlv(0x30, &purposes);
let memory = Memory::new(Arc::new(MemoryAccount::new(1_000_000, 1_000_000)));
let parser = Parser::new(
Encoding::Der,
Limits {
max_work: 4000,
..Limits::default()
},
memory.clone(),
);
let mut parent = parser.read(&bytes, 400).unwrap();
let value = parent.next_element().unwrap().unwrap();
let error = ExtendedKeyUsage::read_value(&parent, &value).unwrap_err();
assert!(error.is_resource());
assert!(error.offset() >= 400);
assert_eq!(memory.used(), 0);
assert!(memory.work_used() <= 4000);
}
fn algorithm(kind: &[u8], parameters: &[u8]) -> Vec<u8> {
let mut fields = tlv(6, kind);
fields.extend(parameters);
tlv(0x30, &fields)
}
#[test]
fn sha2_defaults_are_specific_and_original_null_encodings_survive() {
for suffix in [4, 1, 2, 3] {
let kind = [0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2, suffix];
for parameters in [&[5, 0][..], &[5, 0x81, 0][..]] {
let left = algorithm(&kind, &[]);
let right = algorithm(&kind, parameters);
let mut bytes = left.clone();
bytes.extend(&right);
let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(10000, 10000))));
let mut parent = parser.read(&bytes, 400).unwrap();
let first = parent.next_element().unwrap().unwrap();
let second = parent.next_element().unwrap().unwrap();
let left = structured_pkix::algorithm::read(&parent, &first, "left").unwrap();
let right = structured_pkix::algorithm::read(&parent, &second, "right").unwrap();
assert!(left.digest_matches_in(&right, &parent).unwrap());
assert!(!left.same_written_in(&right, &parent).unwrap());
assert_eq!(right.parameters(), Some(parameters));
assert!(right.has_null_parameter());
}
}
}
#[test]
fn unknown_parameters_keep_written_identity() {
for (left_params, right_params, expected) in [
(&[][..], &[5, 0][..], false),
(&[5, 0][..], &[5, 0x81, 0][..], true),
(&[4, 1, 7][..], &[4, 1, 7][..], true),
(&[4, 1, 7][..], &[4, 0x81, 1, 7][..], false),
(&[0x30, 0][..], &[0x30, 0][..], true),
(&[0x30, 0][..], &[0x30, 0x81, 0][..], false),
] {
let mut bytes = algorithm(&[42, 3], left_params);
bytes.extend(algorithm(&[42, 3], right_params));
let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(10000, 10000))));
let mut parent = parser.read(&bytes, 400).unwrap();
let first = parent.next_element().unwrap().unwrap();
let second = parent.next_element().unwrap().unwrap();
let left = structured_pkix::algorithm::read(&parent, &first, "left").unwrap();
let right = structured_pkix::algorithm::read(&parent, &second, "right").unwrap();
assert_eq!(left.same_written_in(&right, &parent).unwrap(), expected);
assert_eq!(left.digest_matches_in(&right, &parent).unwrap(), expected);
assert_eq!(
right.parameters(),
(!right_params.is_empty()).then_some(right_params)
);
}
}
#[test]
fn invalid_sha2_parameters_are_not_a_digest_mismatch() {
let kind = [0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2, 1];
let mut bytes = algorithm(&[42, 3], &[]);
bytes.extend(algorithm(&kind, &[4, 0]));
let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(10000, 10000))));
let mut parent = parser.read(&bytes, 400).unwrap();
let first = parent.next_element().unwrap().unwrap();
let second = parent.next_element().unwrap().unwrap();
let left = structured_pkix::algorithm::read(&parent, &first, "left").unwrap();
let right = structured_pkix::algorithm::read(&parent, &second, "right").unwrap();
let error = left.digest_matches_in(&right, &parent).unwrap_err();
assert_eq!(error.code(), structured_pkix::ErrorCode::MalformedValue);
assert_eq!(error.offset(), second.span().start);
assert!(!left.same_written_in(&right, &parent).unwrap());
}
#[test]
fn foreign_algorithm_query_refuses_before_work() {
let bytes = algorithm(&[42, 3], &[]);
let memory = Memory::new(Arc::new(MemoryAccount::new(10000, 10000)));
let parser = Parser::ber(memory.clone());
let foreign = Parser::ber(memory.clone());
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
let value = structured_pkix::algorithm::read(&parent, &element, "algorithm").unwrap();
let other = foreign.read(&[], 0).unwrap();
let before = memory.work_used();
let error = value.digest_matches_in(&value, &other).unwrap_err();
assert_eq!(error.code(), structured_pkix::ErrorCode::ForeignReader);
assert_eq!(error.offset(), 400);
assert_eq!(memory.work_used(), before);
}
#[test]
fn constructed_sha2_parameters_are_malformed_not_opaque_defaults() {
let kind = [0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2, 1];
let bytes = algorithm(&kind, &[0x30, 0]);
let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(10000, 10000))));
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
let value = structured_pkix::algorithm::read(&parent, &element, "algorithm").unwrap();
assert_eq!(value.parameters(), Some(&[0x30, 0][..]));
assert!(!value.has_null_parameter());
assert!(value.same_written_in(&value, &parent).unwrap());
let error = value.digest_matches_in(&value, &parent).unwrap_err();
assert_eq!(error.code(), structured_pkix::ErrorCode::MalformedValue);
assert_eq!(error.offset(), 400);
}
#[test]
fn original_eku_retains_only_unique_purposes_and_storage_charge() {
let bytes = tlv(0x30, &[6, 2, 42, 3, 6, 2, 42, 4, 6, 2, 42, 3]);
let memory = Memory::new(Arc::new(MemoryAccount::new(10000, 100_000)));
let parser = Parser::der(memory.clone());
let result = {
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
ExtendedKeyUsage::read_value(&parent, &element).unwrap()
};
assert_eq!(result.purposes().len(), 2);
assert_eq!(result.purposes()[0].as_bytes(), &[42, 3]);
assert_eq!(result.purposes()[1].as_bytes(), &[42, 4]);
assert!(memory.used() > 0);
drop(result);
assert_eq!(memory.used(), 0);
}
#[test]
fn original_eku_memory_refusal_rolls_back_catalogues() {
let mut purposes = Vec::new();
for suffix in 0_u16..256 {
let high = u8::try_from(suffix / 128).unwrap();
let low = u8::try_from(suffix % 128).unwrap();
purposes.extend(tlv(6, &[42, 1 + high, low]));
}
let bytes = tlv(0x30, &purposes);
let memory = Memory::new(Arc::new(MemoryAccount::new(4096, 1_000_000)));
let parser = Parser::der(memory.clone());
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
let error = ExtendedKeyUsage::read_value(&parent, &element).unwrap_err();
assert!(error.is_resource());
assert!(error.offset() >= 400);
assert_eq!(memory.used(), 0);
}
#[test]
fn original_eku_preserves_schema_and_depth_boundaries() {
for bytes in [tlv(0x30, &[]), tlv(0x31, &[6, 2, 42, 3])] {
let memory = Memory::new(Arc::new(MemoryAccount::new(10000, 10000)));
let parser = Parser::der(memory.clone());
let mut parent = parser.read(&bytes, 400).unwrap();
let value = parent.next_element().unwrap().unwrap();
assert!(ExtendedKeyUsage::read_value(&parent, &value).is_err());
assert_eq!(memory.used(), 0);
}
let bytes = tlv(0x30, &[6, 2, 42, 3]);
let memory = Memory::new(Arc::new(MemoryAccount::new(10000, 10000)));
let parser = Parser::new(
Encoding::Der,
Limits {
max_depth: 0,
..Limits::default()
},
memory.clone(),
);
let mut parent = parser.read(&bytes, 400).unwrap();
let value = parent.next_element().unwrap().unwrap();
assert!(ExtendedKeyUsage::read_value(&parent, &value).is_err());
assert_eq!(memory.used(), 0);
}
#[test]
fn malformed_primitive_null_never_becomes_cached_valid_null() {
let bytes = algorithm(&[42, 3], &[5, 1, 0]);
let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(10000, 10000))));
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
assert!(structured_pkix::algorithm::read(&parent, &element, "algorithm").is_err());
}
#[test]
fn digest_query_admits_exact_local_and_aggregate_work() {
for (aggregate, remaining, succeeds) in [
(false, 0, false),
(false, 29, false),
(false, 30, true),
(true, 0, false),
(true, 29, false),
(true, 30, true),
] {
let kind = [0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2, 1];
let mut bytes = algorithm(&kind, &[]);
bytes.extend(algorithm(&kind, &[5, 0]));
let memory = Memory::new(Arc::new(MemoryAccount::new(10000, 100_000)));
let parser = Parser::new(
Encoding::Ber,
Limits {
max_work: 100_000,
..Limits::default()
},
memory.clone(),
);
let mut parent = parser.read(&bytes, 400).unwrap();
let first = parent.next_element().unwrap().unwrap();
let second = parent.next_element().unwrap().unwrap();
let left = structured_pkix::algorithm::read(&parent, &first, "left").unwrap();
let right = structured_pkix::algorithm::read(&parent, &second, "right").unwrap();
let drain = 100_000 - u64::try_from(memory.work_used()).unwrap() - remaining;
if aggregate {
memory.charge_work(drain).unwrap();
} else {
parent.charge(drain).unwrap();
}
let before = memory.work_used();
let result = left.digest_matches_in(&right, &parent);
if succeeds {
assert!(result.unwrap());
assert_eq!(memory.work_used() - before, 30);
} else {
let error = result.unwrap_err();
assert!(error.is_resource());
assert!(error.offset() == first.span().start || error.offset() == second.span().start);
assert!(memory.work_used() - before <= usize::try_from(remaining).unwrap());
}
assert_eq!(memory.used(), 0);
}
}