use std::sync::Arc;
use structured_asn1::memory::{Memory, MemoryAccount};
use structured_asn1::{Encoding, Limits, Parser};
use structured_cms::{ErrorCode, Octets};
#[test]
fn ia5_implicit_tag_changes_only_the_outer_encoding() {
let account = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::ber(account.clone());
let valid = [0x36, 3, 4, 1, b'a'];
let mut reader = parser.read(&valid, 500).unwrap();
let element = reader.next_element().unwrap().unwrap();
let value = Octets::read_ia5(&reader, &element, "uri").unwrap();
assert_eq!(value.as_one(), Some(&b"a"[..]));
assert_eq!(value.pieces()[0].span().start, 504);
drop(value);
assert_eq!(account.used(), 0);
let invalid = [0x36, 3, 0x16, 1, b'a'];
let mut reader = parser.read(&invalid, 800).unwrap();
let element = reader.next_element().unwrap().unwrap();
let error = Octets::read_ia5(&reader, &element, "uri").unwrap_err();
assert_eq!(error.code(), ErrorCode::UnexpectedTag);
assert_eq!(error.offset(), 802);
assert_eq!(account.used(), 0);
let parser = Parser::der(account);
let mut reader = parser.read(&valid, 900).unwrap();
let element = reader.next_element().unwrap().unwrap();
assert!(Octets::read_ia5(&reader, &element, "uri").is_err());
}
#[test]
fn ia5_contents_preserve_borrowed_fragments_and_reject_non_ascii() {
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::ber(memory.clone());
let primitive = [22, 2, b'a', b'b'];
let mut reader = parser.read(&primitive, 100).unwrap();
let element = reader.next_element().unwrap().unwrap();
let value = Octets::read_ia5(&reader, &element, "policy.uri").unwrap();
assert_eq!(
value.pieces()[0].as_bytes().as_ptr(),
primitive[2..].as_ptr()
);
assert_eq!(memory.used(), 0);
drop(value);
let constructed = [0x36, 0x80, 4, 1, b'a', 4, 1, b'b', 0, 0];
let mut reader = parser.read(&constructed, 500).unwrap();
let element = reader.next_element().unwrap().unwrap();
let value = Octets::read_ia5(&reader, &element, "policy.uri").unwrap();
assert_eq!(value.len(), 2);
assert_eq!(value.pieces()[0].span().start, 504);
assert_eq!(value.pieces()[1].span().start, 507);
assert!(memory.used() > 0);
drop(value);
assert_eq!(memory.used(), 0);
let bad = [22, 1, 0xff];
let mut reader = parser.read(&bad, 700).unwrap();
let element = reader.next_element().unwrap().unwrap();
let error = Octets::read_ia5(&reader, &element, "policy.uri").unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
assert_eq!(error.offset(), 702);
}
#[test]
fn empty_ia5_still_requires_current_work() {
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::ber(memory.clone());
let bytes = [22, 0];
let mut reader = parser.read(&bytes, 900).unwrap();
let element = reader.next_element().unwrap().unwrap();
memory.charge_work(memory.work_remaining() as u64).unwrap();
let error = Octets::read_ia5(&reader, &element, "policy.uri").unwrap_err();
assert!(error.is_terminal());
assert_eq!(memory.used(), 0);
assert_eq!(element.as_bytes(), bytes);
}
#[test]
fn primitive_content_is_borrowed_without_storage() {
let memory = Memory::new(Arc::new(MemoryAccount::new(0, 1024)));
let parser = Parser::ber(memory.clone());
let data = [4, 2, b'a', b'b'];
let mut reader = parser.read(&data, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let octets = Octets::read(&reader, &element, "content").unwrap();
assert_eq!(octets.len(), 2);
assert_eq!(octets.pieces()[0].as_bytes().as_ptr(), data[2..].as_ptr());
assert_eq!(octets.pieces()[0].span().start, 402);
assert_eq!(memory.used(), 0);
}
#[test]
fn nested_ber_retains_original_ranges_and_catalogue_charge() {
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::ber(memory.clone());
let data = [0x24, 0x80, 4, 2, b'a', b'b', 0x24, 3, 4, 1, b'c', 0, 0];
let octets;
{
let mut reader = parser.read(&data, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
octets = Octets::read(&reader, &element, "content").unwrap();
}
assert_eq!(octets.len(), 3);
assert_eq!(octets.pieces().len(), 2);
assert_eq!(octets.pieces()[0].as_bytes(), b"ab");
assert_eq!(octets.pieces()[1].as_bytes(), b"c");
assert_eq!(octets.pieces()[0].span().start, 404);
assert_eq!(octets.pieces()[1].span().start, 410);
assert!(memory.used() > 0);
drop(octets);
assert_eq!(memory.used(), 0);
}
#[test]
fn unrelated_fragment_is_refused_and_scratch_released() {
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::ber(memory.clone());
let data = [0x24, 6, 4, 1, b'a', 2, 1, 7];
let mut reader = parser.read(&data, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let error = Octets::read(&reader, &element, "content").unwrap_err();
assert_eq!(error.code(), ErrorCode::UnexpectedTag);
assert_eq!(error.offset(), 405);
assert_eq!(memory.used(), 0);
}
#[test]
fn original_local_work_refuses_before_catalogue_construction() {
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::new(
Encoding::Ber,
Limits {
max_work: 2,
..Limits::default()
},
memory.clone(),
);
let data = [0x24, 3, 4, 1, b'a'];
let mut reader = parser.read(&data, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let error = Octets::read(&reader, &element, "content").unwrap_err();
assert!(error.is_resource());
assert_eq!(error.field(), "content");
assert_eq!(memory.used(), 0);
assert_eq!(memory.work_used(), 2);
}
#[test]
fn der_refuses_constructed_content() {
let parser = Parser::der(Memory::new(Arc::new(MemoryAccount::new(65536, 65536))));
let data = [0x24, 3, 4, 1, b'a'];
let mut reader = parser.read(&data, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
assert_eq!(
Octets::read(&reader, &element, "content")
.unwrap_err()
.code(),
ErrorCode::UnexpectedTag
);
}
#[test]
fn copied_content_retains_exact_capacity_independently_of_parser() {
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::ber(memory.clone());
let bytes = [0x24, 7, 4, 1, b'a', 4, 2, b'b', b'c'];
let mut reader = parser.read(&bytes, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let content = Octets::read(&reader, &element, "content").unwrap();
let copy = content
.copy_in(&reader, "tstInfo.hashedMessage", 400)
.unwrap();
assert_eq!(copy.capacity(), 3);
assert_eq!(&*copy, b"abc");
drop(content);
drop(reader);
drop(parser);
assert_eq!(memory.used(), 3);
assert_eq!(&*copy, b"abc");
drop(copy);
assert_eq!(memory.used(), 0);
}
#[test]
fn foreign_copy_refuses_before_work_or_capacity_admission() {
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::ber(memory.clone());
let foreign = Parser::ber(memory.clone());
let bytes = [4, 1, b'a'];
let mut reader = parser.read(&bytes, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let content = Octets::read(&reader, &element, "content").unwrap();
let other = foreign.read(&[], 0).unwrap();
let work = memory.work_used();
let error = content
.copy_in(&other, "tstInfo.hashedMessage", 400)
.unwrap_err();
assert!(error.is_terminal());
assert!(!error.is_resource());
assert_eq!(
error.code(),
ErrorCode::Pkix(structured_pkix::ErrorCode::ForeignReader)
);
assert_eq!(error.offset(), 400);
assert_eq!(memory.work_used(), work);
assert_eq!(memory.used(), 0);
assert_eq!(content.as_one(), Some(&b"a"[..]));
}
#[test]
fn empty_fragment_fanout_is_paid_even_without_output_capacity() {
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::ber(memory.clone());
let bytes = [0x24, 6, 4, 0, 4, 0, 4, 0];
let mut reader = parser.read(&bytes, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let content = Octets::read(&reader, &element, "content").unwrap();
assert_eq!(content.len(), 0);
assert_eq!(content.pieces().len(), 3);
let held = memory.used();
memory
.charge_work((memory.work_remaining() - 2) as u64)
.unwrap();
let error = content
.copy_in(&reader, "tstInfo.hashedMessage", 400)
.unwrap_err();
assert!(error.is_resource());
assert_eq!(error.field(), "tstInfo.hashedMessage");
assert_eq!(memory.used(), held);
assert_eq!(content.pieces().len(), 3);
}
#[test]
fn spare_capacity_copy_refusal_releases_partial_output_and_keeps_source() {
let bytes = [0x24, 8, 4, 2, b'a', b'b', 4, 2, b'c', b'd'];
let measured = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let measuring_parser = Parser::ber(measured.clone());
let mut measuring_reader = measuring_parser.read(&bytes, 400).unwrap();
let element = measuring_reader.next_element().unwrap().unwrap();
let measured_content = Octets::read(&measuring_reader, &element, "content").unwrap();
let parsing_work = measured.work_used();
drop(measured_content);
let memory = Memory::new(Arc::new(MemoryAccount::new(65536, 65536)));
let parser = Parser::new(
Encoding::Ber,
Limits {
max_work: u64::try_from(parsing_work + 8).unwrap(),
..Limits::default()
},
memory.clone(),
);
let mut reader = parser.read(&bytes, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let content = Octets::read(&reader, &element, "content").unwrap();
let held = memory.used();
let error = content
.copy_in(&reader, "tstInfo.hashedMessage", 400)
.unwrap_err();
assert!(error.is_resource());
assert_eq!(memory.work_used(), parsing_work + 8);
assert!(memory.work_remaining() > 1000);
assert_eq!(memory.used(), held);
assert_eq!(content.pieces()[0].as_bytes(), b"ab");
assert_eq!(content.pieces()[1].as_bytes(), b"cd");
}