use std::sync::Arc;
use structured_asn1::memory::{Memory, MemoryAccount};
use structured_asn1::{Encoding, Limits, Parser};
fn memory(capacity: usize, work: usize) -> Memory {
Memory::new(Arc::new(MemoryAccount::new(capacity, work)))
}
fn tlv(tag: u8, contents: &[u8]) -> Vec<u8> {
let mut bytes = vec![tag, u8::try_from(contents.len()).unwrap()];
bytes.extend_from_slice(contents);
bytes
}
fn named(kind: &[u8], value: &[u8]) -> Vec<u8> {
let mut attribute = tlv(6, kind);
attribute.extend_from_slice(value);
tlv(0x30, &tlv(0x31, &tlv(0x30, &attribute)))
}
#[test]
fn unicode_attribute_preparation_cannot_ignore_current_local_work() {
let bytes = [
0x30, 13, 0x31, 11, 0x30, 9, 6, 3, 0x55, 4, 3, 12, 2, 0xc3, 0x9f,
];
let parser = Parser::der(memory(65536, 65536));
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
let name = structured_pkix::name::read(&parent, &element, "subject").unwrap();
let attribute = name.parts()[0].attributes()[0];
let current = memory(65536, 65536);
let query_parser = Parser::new(
Encoding::Ber,
Limits {
max_work: 0,
..Limits::default()
},
current.clone(),
);
let query = query_parser.read(&[], 900).unwrap();
let error = attribute.folds_in(&query).unwrap_err();
assert!(error.is_resource());
assert_eq!(error.offset(), 411);
assert_eq!(current.work_used(), 0);
assert_eq!(current.used(), 0);
}
#[test]
fn scoped_attribute_folding_uses_the_retained_semantic_core() {
for (kind, text, expected) in [
(&[0x55, 4, 3][..], "Example", true),
(&[0x55, 4, 3][..], "Straße", true),
(&[0x55, 4, 3][..], "\u{e000}", false),
(&[42][..], "opaque", false),
] {
let bytes = named(kind, &tlv(12, text.as_bytes()));
let origin = memory(65536, 65536);
let parser = Parser::der(origin.clone());
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
let name = structured_pkix::name::read(&parent, &element, "subject").unwrap();
let attribute = name.parts()[0].attributes()[0];
let before = (origin.used(), origin.work_used());
let retained_query = memory(65536, 65536);
assert_eq!(attribute.folds(&retained_query).unwrap(), expected);
let current = memory(65536, 65536);
let query = parent.try_clone().unwrap().with_execution(¤t);
assert_eq!(attribute.folds_in(&query).unwrap(), expected);
assert_eq!(current.work_used(), retained_query.work_used());
assert_eq!(current.used(), 0);
assert_eq!((origin.used(), origin.work_used()), before);
assert_eq!(name.as_bytes(), bytes);
}
}
#[test]
fn scoped_folding_enforces_exact_work_and_rolls_back_unicode_scratch() {
let bytes = named(&[0x55, 4, 3], &tlv(12, "Straße".as_bytes()));
let origin = memory(65536, 65536);
let parser = Parser::der(origin.clone());
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
let name = structured_pkix::name::read(&parent, &element, "subject").unwrap();
let attribute = name.parts()[0].attributes()[0];
let measured = memory(65536, 65536);
assert!(attribute.folds(&measured).unwrap());
let cost = measured.work_used();
assert!(cost > 1);
let before = (origin.used(), origin.work_used());
let mut lower = 0;
let mut upper = 65536;
while lower < upper {
let capacity = lower + (upper - lower) / 2;
let current = memory(capacity, 65536);
let query = parent.try_clone().unwrap().with_execution(¤t);
match attribute.folds_in(&query) {
Ok(folds) => {
assert!(folds);
upper = capacity;
}
Err(error) => {
assert!(error.is_resource());
lower = capacity + 1;
}
}
assert_eq!(current.used(), 0);
}
let peak = lower;
assert!(peak > 0);
for (capacity, local, aggregate, succeeds) in [
(peak, cost, cost, true),
(peak, cost - 1, cost, false),
(peak, cost, cost - 1, false),
(peak - 1, cost, cost, false),
(0, cost, cost, false),
] {
let current = memory(capacity, aggregate);
let query_parser = Parser::new(
Encoding::Der,
Limits {
max_work: u64::try_from(local).unwrap(),
..Limits::default()
},
current.clone(),
);
let query = query_parser.read(&[], 900).unwrap();
let answer = attribute.folds_in(&query);
if succeeds {
assert!(answer.unwrap());
} else {
let error = answer.unwrap_err();
assert!(error.is_resource());
assert_eq!(error.offset(), 411);
assert_eq!(error.field(), "name.attribute");
}
assert_eq!(current.used(), 0);
assert_eq!((origin.used(), origin.work_used()), before);
}
}
#[test]
fn constructed_attribute_preparation_preserves_depth_and_element_limits() {
let bytes = named(&[0x55, 4, 3], &tlv(0x2c, &[4, 1, b'a']));
let origin = memory(65536, 65536);
let parser = Parser::ber(origin.clone());
let mut parent = parser.read(&bytes, 400).unwrap();
let element = parent.next_element().unwrap().unwrap();
let name = structured_pkix::name::read(&parent, &element, "subject").unwrap();
let attribute = name.parts()[0].attributes()[0];
assert!(attribute.folds_in(&parent).unwrap());
let before = (origin.used(), origin.work_used());
for limits in [
Limits {
max_depth: 2,
..Limits::default()
},
Limits {
max_elements: 0,
..Limits::default()
},
] {
let current = memory(65536, 65536);
let query_parser = Parser::new(Encoding::Ber, limits, current.clone());
let query = query_parser.read(&[], 900).unwrap();
let error = attribute.folds_in(&query).unwrap_err();
assert!(error.is_resource());
assert_eq!(error.offset(), 411);
assert_eq!(current.used(), 0);
assert_eq!((origin.used(), origin.work_used()), before);
}
}