use super::{GeneralName, read_all};
use crate::error::{ErrorCode, Result};
use crate::fixtures::{context, implicit, int, oid as oid_of, seq, set, tlv, utf8};
use crate::oid;
use alloc::vec::Vec;
use structured_asn1::Parser;
fn parse<'a>(
parser: &'a Parser,
der: &'a [u8],
) -> Result<structured_asn1::memory::OwnedVec<GeneralName<'a>>> {
let mut reader = parser.read(der, 0).unwrap();
let element = reader.next_element().unwrap().unwrap();
read_all(&reader, &element, "test")
}
#[test]
fn each_form_of_name_is_read_as_the_tag_says() {
let directory = seq(&[set(&[seq(&[oid_of(oid::COMMON_NAME), utf8("Signer")])])]);
let der = seq(&[
implicit(1, b"signer@example.ua"),
implicit(2, b"example.ua"),
context(4, &[directory]),
implicit(6, b"https://example.ua/policy"),
implicit(7, &[192, 0, 2, 1]),
tlv(0x88, oid::OCSP.as_bytes()),
]);
let parser = Parser::der(crate::fixtures::memory());
let names = parse(&parser, &der).unwrap();
assert_eq!(names.len(), 6);
assert!(matches!(names[0], GeneralName::Email("signer@example.ua")));
assert!(matches!(names[1], GeneralName::Dns("example.ua")));
assert!(matches!(names[3], GeneralName::Uri(_)));
assert!(matches!(names[4], GeneralName::IpAddress([192, 0, 2, 1])));
let GeneralName::Directory(name) = &names[2] else {
panic!("expected a distinguished name, got {:?}", names[2]);
};
assert_eq!(
name.common_name(parser.memory())
.unwrap()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.as_ref()
.map(|text| text.as_str(&crate::fixtures::memory()).unwrap()),
Some("Signer")
);
let GeneralName::RegisteredId(identifier) = names[5] else {
panic!("expected an identifier, got {:?}", names[5]);
};
assert_eq!(identifier, oid::OCSP);
}
#[test]
fn a_location_is_read_as_text_and_left_alone() {
let der = seq(&[implicit(6, b"http://crl.example.ua/root.crl")]);
let parser = Parser::der(crate::fixtures::memory());
let names = parse(&parser, &der).unwrap();
let GeneralName::Uri(location) = names[0] else {
panic!("expected a location, got {:?}", names[0]);
};
assert_eq!(location, "http://crl.example.ua/root.crl");
}
#[test]
fn a_name_of_another_kind_keeps_its_identifier_and_value() {
let value = utf8("something");
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&value)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
let names = parse(&parser, &der).unwrap();
let GeneralName::Other {
type_id,
value: kept,
} = names[0]
else {
panic!("expected another kind of name, got {:?}", names[0]);
};
assert_eq!(type_id, oid::USER_ID);
assert_eq!(kept, &value[..]);
}
#[test]
fn a_directory_name_attribute_outside_its_syntax_is_refused() {
use crate::fixtures::null;
let with_null = seq(&[oid_of(oid::COMMON_NAME), null()]);
let with_bad_utf8 = seq(&[oid_of(oid::COMMON_NAME), tlv(0x0c, b"\xff\xfe")]);
for attribute in [with_null, with_bad_utf8] {
let der = seq(&[context(4, &[seq(&[set(&[attribute])])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
}
let unknown = structured_asn1::Oid::from_bytes(&[0x2a, 0x86, 0x24, 0x05, 0x02]);
let genuine = seq(&[context(
4,
&[seq(&[
set(&[seq(&[oid_of(oid::COMMON_NAME), utf8("Signer")])]),
set(&[seq(&[oid_of(unknown), tlv(0x04, &[0x01])])]),
])],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &genuine).is_ok());
}
#[test]
fn an_email_that_is_not_a_mailbox_is_refused() {
let bad = [
&b"not-an-email"[..],
b"two@@example.ua",
b"@example.ua",
b"signer@",
b"signer@bad domain",
b"lo cal@example.ua",
];
for value in bad {
let der = seq(&[implicit(1, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
let der = seq(&[implicit(1, b"signer+tag@example.ua")]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_location_that_is_not_an_absolute_uri_is_refused() {
let bad = [&b"not a uri"[..], b"/relative/path", b"1http://x", b"http:"];
for value in bad {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
for value in [&b"http://crl.example.ua/root.crl"[..], b"urn:oid:1.2.3"] {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
}
#[test]
fn a_local_part_that_is_not_a_dot_string_is_refused() {
let bad = [
&b"a..b@example.ua"[..],
b".a@example.ua",
b"a.@example.ua",
b"a,b@example.ua",
b"a\"b@example.ua",
];
for value in bad {
let der = seq(&[implicit(1, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
let der = seq(&[implicit(1, b"a.b+tag@example.ua")]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_location_outside_the_uri_grammar_is_refused() {
let bad = [
&b"https:%"[..],
b"http://x%zz",
b"http://x\x7f",
b"http://x%2",
];
for value in bad {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
let der = seq(&[implicit(6, b"http://x/a%2Fb?q=1")]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_domain_longer_than_the_name_limit_is_refused() {
let label = "a".repeat(63);
let short = "a".repeat(61);
let fits = [label.as_str(), &label, &label, &short].join(".");
assert_eq!(fits.len(), 253);
let der = seq(&[implicit(2, fits.as_bytes())]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
let longer = "a".repeat(62);
let over = [label.as_str(), &label, &label, &longer].join(".");
let der = seq(&[implicit(2, over.as_bytes())]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
}
#[test]
fn reversed_x400_personal_name_members_are_refused_under_der() {
let reversed = seq(&[context(
3,
&[seq(&[context(
5,
&[implicit(1, b"Given"), implicit(0, b"Surname")],
)])],
)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &reversed).unwrap_err();
assert_eq!(error.code(), ErrorCode::UnexpectedField);
let ordered = seq(&[context(
3,
&[seq(&[context(
5,
&[implicit(0, b"Surname"), implicit(1, b"Given")],
)])],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &ordered).is_ok());
}
#[test]
fn a_domain_that_is_not_the_preferred_name_syntax_is_refused() {
let bad = [
&b"bad name"[..],
b"example/com",
b"example..com",
b"-example.com",
b"example-.com",
b".example.com",
b"example.com.",
];
for value in bad {
let der = seq(&[implicit(2, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
for value in [&b"example.ua"[..], b"1a.example.ua", b"xn--80aswg.ua"] {
let der = seq(&[implicit(2, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
}
#[test]
fn a_name_outside_the_alphabet_its_field_has_is_refused() {
let der = seq(&[implicit(2, "приклад.укр".as_bytes())]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
assert_eq!(error.field(), "test");
}
#[test]
fn a_string_written_as_a_structure_is_refused() {
let der = seq(&[context(2, &[tlv(0x16, b"example.ua")])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
}
#[test]
fn a_kind_of_name_the_schema_does_not_have_is_refused() {
let parser = Parser::der(crate::fixtures::memory());
let der = seq(&[implicit(9, b"whatever")]);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
let parser = Parser::der(crate::fixtures::memory());
let der = seq(&[int(1)]);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
}
#[test]
fn a_list_of_no_names_is_refused() {
let parser = Parser::der(crate::fixtures::memory());
let der = seq(&[]);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::EmptySequence);
}
#[test]
fn an_address_of_the_wrong_width_is_refused() {
for bytes in [&[192u8, 0, 2][..], &[192, 0, 2, 1, 7][..], &[][..]] {
let der = seq(&[implicit(7, bytes)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{bytes:02x?}");
}
for bytes in [[192u8, 0, 2, 1].as_slice(), [0u8; 16].as_slice()] {
let der = seq(&[implicit(7, bytes)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{bytes:02x?}");
}
}
#[test]
fn another_kind_of_name_holds_exactly_one_value() {
let empty = seq(&[context(0, &[oid_of(oid::USER_ID), context(0, &[])])]);
let doubled = seq(&[context(
0,
&[oid_of(oid::USER_ID), context(0, &[int(1), int(2)])],
)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &empty).unwrap_err();
assert_eq!(error.code(), ErrorCode::MissingField);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &doubled).unwrap_err();
assert_eq!(error.code(), ErrorCode::TrailingData);
}
#[test]
fn an_empty_textual_name_names_nothing_and_is_refused() {
for number in [1u8, 2, 6] {
let der = seq(&[implicit(number, b"")]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "[{number}]");
}
}
#[test]
fn an_x400_address_is_decoded_into_its_attributes() {
use crate::fixtures::{null, printable};
let bad_standard = seq(&[context(3, &[seq(&[null()])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &bad_standard).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
let bad_network = seq(&[context(3, &[seq(&[implicit(0, b"12A")])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &bad_network).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
let bad_domain = seq(&[context(3, &[seq(&[]), seq(&[seq(&[null()])])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &bad_domain).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
let bad_extension = seq(&[context(
3,
&[seq(&[]), set(&[seq(&[implicit(0, &[0x01])])])],
)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &bad_extension).unwrap_err();
assert_eq!(error.code(), ErrorCode::MissingField);
let good = seq(&[context(
3,
&[
seq(&[tlv(0x61, &printable("UA")), implicit(1, b"TERMID")]),
seq(&[seq(&[printable("type"), printable("value")])]),
set(&[seq(&[
implicit(0, &[0x01]),
context(1, &[crate::fixtures::printable("v")]),
])]),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
let names = parse(&parser, &good).unwrap();
assert!(matches!(names[0], GeneralName::X400(_)));
}
#[test]
fn an_x400_extension_value_is_decoded_by_its_declared_type() {
use crate::fixtures::{null, printable};
let address = |number: &[u8], value: Vec<u8>| {
seq(&[context(
3,
&[
seq(&[]),
set(&[seq(&[implicit(0, number), context(1, &[value])])]),
],
)])
};
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &address(&[0x01], null())).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &address(&[0x00, 0xc8], printable("x"))).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
let good = [
address(&[0x01], printable("Signer")),
address(&[0x17], int(3)),
address(&[0x0a], set(&[printable("Kyiv")])),
];
for der in good {
let parser = Parser::der(crate::fixtures::memory());
let names = parse(&parser, &der).unwrap();
assert!(matches!(names[0], GeneralName::X400(_)));
}
}
#[test]
fn a_fragment_in_a_location_is_refused() {
let der = seq(&[implicit(6, b"https://example.test/#fragment")]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
}
#[test]
fn unordered_pds_parameter_members_are_refused_under_der() {
use crate::fixtures::printable;
let reversed = seq(&[context(
3,
&[
seq(&[]),
set(&[seq(&[
implicit(0, &[0x0a]),
context(1, &[set(&[tlv(0x14, b"T"), printable("P")])]),
])]),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &reversed).unwrap_err();
assert_eq!(error.code(), ErrorCode::UnexpectedField);
let ordered = seq(&[context(
3,
&[
seq(&[]),
set(&[seq(&[
implicit(0, &[0x0a]),
context(1, &[set(&[printable("P"), tlv(0x14, b"T")])]),
])]),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &ordered).is_ok());
}
#[test]
fn unordered_unformatted_address_members_are_refused_under_der() {
use crate::fixtures::printable;
let reversed = seq(&[context(
3,
&[
seq(&[]),
set(&[seq(&[
implicit(0, &[0x10]),
context(1, &[set(&[tlv(0x14, b"T"), seq(&[printable("Line")])])]),
])]),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &reversed).unwrap_err();
assert_eq!(error.code(), ErrorCode::UnexpectedField);
let ordered = seq(&[context(
3,
&[
seq(&[]),
set(&[seq(&[
implicit(0, &[0x10]),
context(1, &[set(&[seq(&[printable("Line")]), tlv(0x14, b"T")])]),
])]),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &ordered).is_ok());
}
#[test]
fn an_oversized_edi_party_name_is_refused() {
let oversized = "a".repeat(32769);
let party = tlv(0x0c, oversized.as_bytes());
let der = seq(&[context(5, &[context(1, &[party])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
let at_limit = "a".repeat(32768);
let party = tlv(0x0c, at_limit.as_bytes());
let der = seq(&[context(5, &[context(1, &[party])])]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn an_x400_country_outside_its_code_forms_is_refused() {
use crate::fixtures::printable;
let bad = [tlv(0x61, &printable("A1")), tlv(0x61, &tlv(0x12, b"1 3"))];
for country in bad {
let der = seq(&[context(3, &[seq(&[country])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
}
for country in [tlv(0x61, &printable("UA")), tlv(0x61, &tlv(0x12, b"123"))] {
let der = seq(&[context(3, &[seq(&[country])])]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
}
#[test]
fn unordered_x400_extension_attributes_are_refused_under_der() {
let smaller = seq(&[implicit(0, &[0x17]), context(1, &[int(1)])]);
let larger = seq(&[implicit(0, &[0x17]), context(1, &[int(2)])]);
let reversed = seq(&[context(
3,
&[seq(&[]), set(&[larger.clone(), smaller.clone()])],
)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &reversed).unwrap_err();
assert_eq!(error.code(), ErrorCode::UnexpectedField);
let ordered = seq(&[context(3, &[seq(&[]), set(&[smaller, larger])])]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &ordered).is_ok());
}
#[test]
fn a_constructed_x400_string_is_refused() {
let der = seq(&[context(3, &[seq(&[tlv(0x62, &tlv(0x33, &[]))])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
}
#[test]
fn an_edi_party_string_that_does_not_decode_is_refused() {
let bad_utf8 = seq(&[context(5, &[context(1, &[tlv(0x0c, b"\xff\xfe")])])]);
let odd_bmp = seq(&[context(5, &[context(1, &[tlv(0x1e, &[0x00, 0x04, 0x10])])])]);
for der in [bad_utf8, odd_bmp] {
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
}
let cyrillic = seq(&[context(
5,
&[context(1, &[tlv(0x1e, &[0x04, 0x23, 0x04, 0x32])])],
)]);
let parser = Parser::der(crate::fixtures::memory());
let names = parse(&parser, &cyrillic).unwrap();
assert!(matches!(names[0], GeneralName::EdiParty(_)));
}
#[test]
fn an_edi_party_must_name_its_party() {
let empty = seq(&[context(5, &[])]);
let assigner_only = seq(&[context(5, &[context(0, &[utf8("assigner")])])]);
for der in [empty, assigner_only] {
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MissingField);
}
let full = seq(&[context(
5,
&[
context(0, &[utf8("assigner")]),
context(1, &[utf8("party")]),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
let names = parse(&parser, &full).unwrap();
assert!(matches!(names[0], GeneralName::EdiParty(_)));
let backwards = seq(&[context(
5,
&[
context(1, &[utf8("party")]),
context(0, &[utf8("assigner")]),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &backwards).unwrap_err();
assert_eq!(error.code(), ErrorCode::TrailingData);
}
#[test]
fn an_x400_address_must_hold_its_standard_attributes() {
let empty = seq(&[context(3, &[])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &empty).unwrap_err();
assert_eq!(error.code(), ErrorCode::MissingField);
let domain = seq(&[seq(&[
crate::fixtures::printable("ab"),
crate::fixtures::printable("cd"),
])]);
let extensions = crate::fixtures::set(&[seq(&[
implicit(0, &[0x01]),
context(1, &[crate::fixtures::printable("v")]),
])]);
let shaped = seq(&[context(3, &[seq(&[]), domain.clone(), extensions.clone()])]);
let parser = Parser::der(crate::fixtures::memory());
let names = parse(&parser, &shaped).unwrap();
assert!(matches!(names[0], GeneralName::X400(_)));
let overfull = seq(&[context(3, &[seq(&[]), domain, extensions, seq(&[])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &overfull).unwrap_err();
assert_eq!(error.code(), ErrorCode::TrailingData);
}
#[test]
fn an_empty_directory_name_names_nobody_and_is_refused() {
let der = seq(&[context(4, &[seq(&[])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
}
#[test]
fn an_all_numeric_top_level_label_is_not_a_domain() {
for value in [&b"192.0.2.1"[..], b"example.123"] {
let der = seq(&[implicit(2, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
for value in [&b"example.x1"[..], b"example.1x"] {
let der = seq(&[implicit(2, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
}
#[test]
fn unordered_network_addresses_are_refused_under_der() {
use crate::fixtures::octets;
let psap = |addresses: alloc::vec::Vec<u8>| {
seq(&[context(
3,
&[
seq(&[]),
set(&[seq(&[
implicit(0, &[0x16]),
context(1, &[context(0, &[context(3, &[addresses])])]),
])]),
],
)])
};
let reversed = psap(set(&[octets(&[0x02]), octets(&[0x01])]));
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &reversed).unwrap_err();
assert_eq!(error.code(), ErrorCode::UnexpectedField);
let ordered = psap(set(&[octets(&[0x01]), octets(&[0x02])]));
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &ordered).is_ok());
}
#[test]
fn an_oversized_mailbox_local_part_is_refused() {
let local = "a".repeat(65);
let long = [local.as_str(), "example.ua"].join("@");
let der = seq(&[implicit(1, long.as_bytes())]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
let local = "a".repeat(64);
let fits = [local.as_str(), "example.ua"].join("@");
let der = seq(&[implicit(1, fits.as_bytes())]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn brackets_outside_an_ip_literal_host_are_not_a_uri() {
let bad = [
&b"x:["[..],
b"http://example.ua/a[b]",
b"http://example.ua/?q=[1]",
b"http://[example.ua/",
b"ldap://[2001:db8::1/dc=ua",
];
for value in bad {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
let good = [
&b"ldap://[2001:db8::1]/dc=ua"[..],
b"http://[::1]:8080/x",
b"http://[v1.x]/",
];
for value in good {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
}
#[test]
fn an_oversized_extension_attribute_set_is_refused() {
let attribute = |option: u32| seq(&[implicit(0, &[0x17]), context(1, &[int(option)])]);
let attributes: Vec<_> = (0..=256).map(attribute).collect();
let der = seq(&[context(3, &[seq(&[]), set(&attributes)])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
let attributes: Vec<_> = (0..256).map(attribute).collect();
let der = seq(&[context(3, &[seq(&[]), set(&attributes)])]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_bracketed_host_that_is_not_an_ip_literal_is_refused() {
let bad = [
&b"http://[garbage]/"[..],
b"http://[12345::1]/",
b"http://[1:2:3:4:5:6:7:8:9]/",
b"http://[1.2.3.4]/",
b"http://[::1::2]/",
b"http://[v.x]/",
b"http://[v1x]/",
b"http://[1:2]/",
];
for value in bad {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
let good = [
&b"http://[::1]/"[..],
b"http://[2001:db8::1]:8080/",
b"http://[1:2:3:4:5:6:7:8]/",
b"http://[::ffff:192.0.2.1]/",
b"http://[2001:db8::192.0.2.1]/",
b"http://[v1.x:y]/",
];
for value in good {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
}
#[test]
fn a_numeric_built_in_domain_defined_pair_is_refused() {
use crate::fixtures::printable;
let numeric = |digits: &str| tlv(0x12, digits.as_bytes());
let bad = [
seq(&[seq(&[numeric("1"), printable("x")])]),
seq(&[seq(&[printable("t"), numeric("2")])]),
];
for value in bad {
let der = seq(&[context(3, &[seq(&[]), value])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
}
let good = seq(&[seq(&[printable("t"), printable("v")])]);
let der = seq(&[context(3, &[seq(&[]), good])]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_malformed_uri_authority_is_refused() {
let bad = [
&b"http://a@b@c/"[..],
b"http://example.test:abc/",
b"http://example.test:80:81/",
b"http://ex@mple@test",
];
for value in bad {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
let good = [
&b"http://user@example.test/"[..],
b"http://user:pw@example.test:8080/a",
b"http://example.test:/",
b"ldap://example.test:389/dc=ua",
];
for value in good {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
}
#[test]
fn a_uri_authority_without_a_host_is_refused() {
let bad = [
&b"http:///status"[..],
b"http://user@/status",
b"http://:8080/status",
b"http://ex_ample.test/",
b"http://-bad.test/",
b"http://192.0.2.1.5/",
];
for value in bad {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue, "{value:02x?}");
}
let good = [
&b"http://example.test/a"[..],
b"http://localhost/a",
b"http://example.test./a",
b"http://example.test.:8080/a",
b"http://192.0.2.1:8080/a",
b"http://[2001:db8::1]/a",
b"urn:oid:1.2.3",
b"mailto:a@example.test",
];
for value in good {
let der = seq(&[implicit(6, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
}
#[test]
fn a_numeric_x400_organizational_unit_is_refused() {
use crate::fixtures::printable;
let numeric = seq(&[context(3, &[seq(&[context(6, &[tlv(0x12, b"12")])])])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &numeric).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
let printable_unit = seq(&[context(3, &[seq(&[context(6, &[printable("unit")])])])]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &printable_unit).is_ok());
}
#[test]
fn an_overlong_mailbox_is_refused_as_a_whole() {
let local = "a".repeat(64);
let label = "b".repeat(63);
let domain = [label.as_str(), &label, &label, "ua"].join(".");
let long = [local.as_str(), &domain].join("@");
assert!(long.len() > 254);
let der = seq(&[implicit(1, long.as_bytes())]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
}
#[test]
fn a_numeric_unformatted_postal_line_is_refused() {
use crate::fixtures::printable;
let address = |line: Vec<u8>| {
let attribute = seq(&[implicit(0, &[0x10]), context(1, &[set(&[seq(&[line])])])]);
seq(&[context(3, &[seq(&[]), set(&[attribute])])])
};
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &address(tlv(0x12, b"1"))).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &address(printable("line"))).is_ok());
}
#[test]
fn numeric_x400_common_and_pds_names_are_refused() {
use crate::fixtures::printable;
let attribute = |number: u8, value: Vec<u8>| {
let one = seq(&[implicit(0, &[number]), context(1, &[value])]);
seq(&[context(3, &[seq(&[]), set(&[one])])])
};
for number in [1u8, 7] {
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &attribute(number, tlv(0x12, b"1"))).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
"{number}"
);
let parser = Parser::der(crate::fixtures::memory());
assert!(
parse(&parser, &attribute(number, printable("name"))).is_ok(),
"{number}"
);
}
}
#[test]
fn the_network_address_ordering_is_charged_to_the_work_budget() {
use crate::fixtures::octets;
let mut big = alloc::vec![0xaau8; 50_000];
let first = octets(&big);
*big.last_mut().unwrap() = 0xbb;
let second = octets(&big);
let psap = context(0, &[context(3, &[set(&[first, second])])]);
let attribute = seq(&[implicit(0, &[0x16]), context(1, &[psap])]);
let der = seq(&[context(3, &[seq(&[]), set(&[attribute])])]);
let limits = structured_asn1::Limits {
max_work: 8192,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn edi_directory_string_decoding_is_charged_to_the_work_budget() {
let big = alloc::vec![b'a'; 100_000];
let der = seq(&[context(5, &[context(1, &[tlv(0x0c, &big)])])]);
let limits = structured_asn1::Limits {
max_work: 8192,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
}
#[test]
fn the_extension_attribute_ordering_is_charged_to_the_work_budget() {
use crate::fixtures::octets;
let mut big = alloc::vec![0xaau8; 50_000];
let psap = context(0, &[context(3, &[set(&[octets(&big)])])]);
let first = seq(&[implicit(0, &[0x16]), context(1, &[psap])]);
*big.last_mut().unwrap() = 0xbb;
let psap = context(0, &[context(3, &[set(&[octets(&big)])])]);
let second = seq(&[implicit(0, &[0x16]), context(1, &[psap])]);
let der = seq(&[context(3, &[seq(&[]), set(&[first, second])])]);
let limits = structured_asn1::Limits {
max_work: 8192,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_constructed_primitive_value_inside_another_name_is_refused() {
let inner = utf8("x");
let constructed = tlv(0x2c, &inner);
let der = seq(&[context(
0,
&[oid_of(oid::USER_ID), context(0, &[constructed])],
)]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
let held = seq(&[int(1)]);
let der = seq(&[context(0, &[oid_of(oid::USER_ID), context(0, &[held])])]);
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn the_directory_name_gate_is_charged_to_the_work_budget() {
let big = alloc::vec![b'a'; 100_000];
let attribute = seq(&[oid_of(oid::DOMAIN_COMPONENT), tlv(0x16, &big)]);
let der = seq(&[context(4, &[seq(&[set(&[attribute])])])]);
let limits = structured_asn1::Limits {
max_work: 8192,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
}
#[test]
fn a_quoted_local_part_is_a_mailbox_too() {
for value in [
&b"\"Fred Bloggs\"@example.ua"[..],
b"\"a\\\"b\"@example.ua",
b"\"a@b\"@example.ua",
] {
let der = seq(&[implicit(1, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
for value in [
&b"\"a\"b\"@example.ua"[..],
b"\"a\tb\"@example.ua",
b"\"unterminated@example.ua",
] {
let der = seq(&[implicit(1, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert_eq!(
parse(&parser, &der).unwrap_err().code(),
ErrorCode::MalformedValue,
"{value:02x?}"
);
}
}
#[test]
fn mailbox_validation_preserves_an_empty_quoted_local_part() {
let bytes = b"\x81\x0d\"\"@example.ua";
let parser = Parser::der(crate::fixtures::memory());
let mut reader = parser.read(bytes, 91).unwrap();
let element = reader.next_element().unwrap().unwrap();
let GeneralName::Email(value) = super::read(&reader, &element, "mailbox").unwrap() else {
panic!("expected a borrowed mailbox");
};
assert_eq!(value, "\"\"@example.ua");
assert_eq!(value.as_ptr(), element.contents().as_ptr());
}
#[test]
fn an_address_literal_is_a_mailbox_domain() {
for value in [
&b"postmaster@[192.0.2.1]"[..],
b"postmaster@[012.0.2.1]",
b"postmaster@[IPv6:2001:db8::1]",
b"postmaster@[IPv6:::ffff:192.0.2.1]",
b"postmaster@[ipv6:2001:db8::1]",
b"postmaster@[IPV6:2001:db8::1]",
b"postmaster@[AS400:QSYS]",
] {
let der = seq(&[implicit(1, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{value:02x?}");
}
for value in [
&b"postmaster@[192.0.2.256]"[..],
b"postmaster@[AS400:]",
b"postmaster@[example.ua]",
b"postmaster@[192.0.2.1",
b"postmaster@[ipv6:not-an-address]",
b"postmaster@[IPV6:not-an-address]",
b"postmaster@[IPv6:not-an-address]",
] {
let der = seq(&[implicit(1, value)]);
let parser = Parser::der(crate::fixtures::memory());
assert_eq!(
parse(&parser, &der).unwrap_err().code(),
ErrorCode::MalformedValue,
"{value:02x?}"
);
}
}
#[test]
fn a_preserved_other_name_is_der_all_the_way_down() {
use crate::fixtures::cat;
let non_minimal = seq(&[tlv(0x02, &[0x00, 0x7f])]);
let end_of_contents = seq(&[cat(&[int(1), tlv(0x00, &[])])]);
for payload in [non_minimal, end_of_contents] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
let sound = seq(&[int(1), utf8("x")]);
let der = seq(&[context(0, &[oid_of(oid::USER_ID), context(0, &[sound])])]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_deeply_nested_preserved_value_is_answered_rather_than_crashed() {
use structured_asn1::Limits;
let mut backwards: Vec<u8> = int(1).iter().rev().copied().collect();
for _ in 0..60_000 {
let length = backwards.len();
if let Ok(short) = u8::try_from(length)
&& short < 0x80
{
backwards.push(short);
} else {
let bytes = length.to_be_bytes();
let first = bytes.iter().position(|byte| *byte != 0).unwrap();
backwards.extend(bytes[first..].iter().rev());
backwards.push(0x80 | u8::try_from(bytes.len() - first).unwrap());
}
backwards.push(0x30);
}
backwards.reverse();
let payload = backwards;
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::new(
structured_asn1::Encoding::Der,
Limits {
max_depth: 100_000,
..Limits::default()
},
crate::fixtures::memory(),
);
let _ = parse(&parser, &der);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err());
}
#[test]
fn checking_a_preserved_string_is_charged_to_the_work_budget() {
let big = alloc::vec![b'a'; 100_000];
let payload = tlv(0x0c, &big);
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let limits = structured_asn1::Limits {
max_work: 8192,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
assert_eq!(
parse(&parser, &der).unwrap_err().code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn checking_a_preserved_time_is_charged_to_the_work_budget() {
let mut written = b"19990101000000.".to_vec();
written.extend(core::iter::repeat_n(b'1', 100_000));
written.push(b'Z');
let payload = tlv(0x18, &written);
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let limits = structured_asn1::Limits {
max_work: 8192,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
assert_eq!(
parse(&parser, &der).unwrap_err().code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
fn wide(number: u8, contents: &[u8]) -> alloc::vec::Vec<u8> {
let mut der = alloc::vec![0x1f, number];
der.extend_from_slice(&tlv(0x00, contents)[1..]);
der
}
#[test]
fn a_preserved_identifier_written_as_a_resource_name_is_held_to_its_arcs() {
for payload in [
wide(35, b"ISO/Registration"),
wide(35, b"/ISO//Registration"),
wide(35, b"/ISO/Registration Authority"),
wide(36, b"/Registration"),
wide(36, b""),
wide(35, b"/a\x00"),
wide(35, b"/01"),
wide(35, b"/-abc"),
wide(35, b"/abc-"),
wide(35, b"/ab--c"),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
for payload in [
wide(35, b"/ISO/Registration_Authority/19785.CBEFF"),
wide(36, b"Patron-formats/TLV-encoded"),
wide(35, b"/2/1024/0"),
wide(35, "/a\u{a0}b".as_bytes()),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{payload:02x?}");
}
}
#[test]
fn a_preserved_value_of_a_newer_type_is_held_to_its_notation() {
for payload in [
wide(31, b"foo"),
wide(31, b"2026133"),
wide(31, b"20261301"),
wide(31, b"20260231"),
wide(32, b"250000"),
wide(32, b"1200"),
wide(33, b"20261301120000"),
wide(33, b"20260101250000"),
wide(34, b""),
wide(34, b"1M1Y"),
wide(34, b"1Y2"),
wide(34, b"1.5Y2M"),
wide(34, b"1.5YT1H"),
wide(34, b"T1.5H30M"),
wide(34, b"01Y"),
wide(34, b"T00.5S"),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
for payload in [
wide(31, b"20240229"),
wide(32, b"240000"),
wide(33, b"20260101235960"),
wide(34, b"1Y6M4DT12H30M5S"),
wide(34, b"3W"),
wide(34, b"T0.5S"),
wide(34, b"1Y0.5M"),
wide(34, b"0Y"),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{payload:02x?}");
}
}
#[test]
fn a_preserved_unrestricted_time_is_held_to_the_notation_it_writes() {
for payload in [
tlv(0x0e, b"2026-01-01T12:00:00Z"),
tlv(0x0e, b"2026-01-01T12:00:00.500-05:00"),
tlv(0x0e, b"2026-W01-3"),
tlv(0x0e, b"2026-032"),
tlv(0x0e, b"2026-01"),
tlv(0x0e, b"2024-366"),
tlv(0x0e, b"2020-W53"),
tlv(0x0e, b"+999999999999-02-28"),
tlv(0x0e, b"24:00:00"),
tlv(0x0e, b"P1Y6M"),
tlv(0x0e, b"2026-01-01/2026-12-31"),
tlv(0x0e, b"2026-01-01T00:00:00Z/P1Y"),
tlv(0x0e, b"R5/P1Y"),
tlv(0x0e, b"R/2026-01-01/P1D"),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{payload:02x?}");
}
for payload in [
tlv(0x0e, b"foo"),
tlv(0x0e, b"---"),
tlv(0x0e, b"2026-13-01"),
tlv(0x0e, b"2026-01-32"),
tlv(0x0e, b"2026-01-00"),
tlv(0x0e, b"2023-366"),
tlv(0x0e, b"2021-W53"),
tlv(0x0e, b"+999999999999-02-31"),
tlv(0x0e, b"+999999999999-366"),
tlv(0x0e, b"25:00:00"),
tlv(0x0e, b"12:00.5:00"),
tlv(0x0e, b"24:30:00"),
tlv(0x0e, b"24.5"),
tlv(0x0e, b"24:00:00.5"),
tlv(0x0e, b"P1Y/P2Y"),
tlv(0x0e, b"R5"),
tlv(0x0e, b"2026-01-01/2026-06-01/2026-12-01"),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
}
const ARC: &[u8] = &[0x2a, 0x86, 0x24, 0x08, 0x01];
fn embedded(identification: &[u8]) -> alloc::vec::Vec<u8> {
tlv(
0x2b,
&[tlv(0xa0, identification), tlv(0x82, b"data")].concat(),
)
}
#[test]
fn a_preserved_value_of_an_associated_sequence_type_is_held_to_it() {
let identity = oid_of(structured_asn1::Oid::from_bytes(&[
0x2a, 0x86, 0x24, 0x08, 0x01,
]));
for payload in [
tlv(0x28, &[]),
tlv(0x28, &identity),
tlv(0x28, &[tlv(0x81, b"x"), identity.clone()].concat()),
tlv(0x28, &tlv(0x83, b"x")),
tlv(0x28, &[tlv(0x81, b"x"), tlv(0x81, b"y")].concat()),
tlv(0x2b, &[]),
tlv(0x2b, &tlv(0xa0, &tlv(0x85, &[]))),
tlv(
0x2b,
&[tlv(0xa0, &tlv(0x86, &[])), tlv(0x82, b"d")].concat(),
),
tlv(
0x2b,
&[tlv(0x82, b"d"), tlv(0xa0, &tlv(0x85, &[]))].concat(),
),
tlv(0x3d, &tlv(0xa0, &tlv(0x85, &[]))),
embedded(&tlv(0x81, &[0x80])),
embedded(&tlv(0x82, &[0x00, 0x01])),
embedded(&tlv(0x85, b"x")),
embedded(&tlv(0xa0, &tlv(0x80, ARC))),
embedded(&tlv(0xa1, ARC)),
tlv(0x28, &tlv(0x82, &[])),
tlv(0x28, &tlv(0xa0, &[int(1), int(2)].concat())),
tlv(
0x2b,
&[tlv(0xa0, &tlv(0x85, &[])), tlv(0xa2, &tlv(0x04, b"d"))].concat(),
),
tlv(0x28, &[tlv(0xa1, &tlv(0x04, b"d"))].concat()),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
for payload in [
tlv(0x28, &[identity.clone(), tlv(0x81, b"data")].concat()),
tlv(0x28, &tlv(0xa0, &int(1))),
tlv(
0x2b,
&[tlv(0xa0, &tlv(0x85, &[])), tlv(0x82, b"data")].concat(),
),
tlv(
0x3d,
&[tlv(0xa0, &tlv(0x81, ARC)), tlv(0x82, b"text")].concat(),
),
embedded(&tlv(0xa0, &[tlv(0x80, ARC), tlv(0x81, ARC)].concat())),
embedded(&tlv(0xa3, &[tlv(0x80, &[0x01]), tlv(0x81, ARC)].concat())),
embedded(&tlv(0x82, &[0x2a])),
tlv(0x28, &tlv(0x82, &[0x00, 0xff])),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{payload:02x?}");
}
}
#[test]
fn a_spent_budget_inside_an_associated_sequence_stays_a_spent_budget() {
let mut arcs = alloc::vec![0x81u8; 20_000];
arcs.push(0x01);
let payload = embedded(&tlv(0x81, &arcs));
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let limits = structured_asn1::Limits {
max_work: 8192,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
assert_eq!(
parse(&parser, &der).unwrap_err().code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_preserved_teletex_value_keeps_its_negotiated_repertoire() {
let legacy = tlv(0x14, &[0xc2, 0x65]);
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&legacy)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
let outside = tlv(0x16, &[0xc2, 0x65]);
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&outside)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err());
}
#[test]
fn a_preserved_value_of_no_universal_type_is_refused() {
for payload in [
tlv(0x00, &[]),
tlv(0x0f, &[]),
tlv(0x2f, &[]),
alloc::vec![0x1f, 0x25, 0x00],
alloc::vec![0x1f, 0x81, 0x00, 0x00],
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
let payload = alloc::vec![0x1f, 0x24, 0x01, b'1'];
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_preserved_real_is_held_to_its_contents() {
for payload in [
tlv(0x09, &[0x00]),
tlv(0x09, b"\x011.E+0"),
tlv(0x09, &[0x03]),
tlv(0x09, b"\x030.E+0"),
tlv(0x09, b"\x0310.E+0"),
tlv(0x09, b"\x03+1.E+0"),
tlv(0x09, b"\x031.E+1"),
tlv(0x09, b"\x031.E0"),
tlv(0x09, &[0x40, 0x41]),
tlv(0x09, &[0x44]),
tlv(0x09, &[0x80, 0x01]),
tlv(0x09, &[0x80, 0x01, 0x02]),
tlv(0x09, &[0xa0, 0x01, 0x01]),
tlv(0x09, &[0x88, 0x01, 0x01]),
tlv(0x09, &[0x81, 0x00, 0x00, 0x01]),
tlv(0x09, &[0x80, 0x01, 0x00, 0x01]),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
for payload in [
tlv(0x09, &[]),
tlv(0x09, &[0x40]),
tlv(0x09, &[0x43]),
tlv(0x09, &[0x80, 0x01, 0x01]),
tlv(0x09, &[0xc0, 0xff, 0x03]),
tlv(0x09, b"\x031.E+0"),
tlv(0x09, b"\x03-123.E-4"),
tlv(0x09, b"\x03105.E12"),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{payload:02x?}");
}
}
#[test]
fn a_preserved_enumerated_or_relative_identifier_is_held_to_its_contents() {
for payload in [
tlv(0x0a, &[0x00, 0x01]),
tlv(0x0a, &[0xff, 0x81]),
tlv(0x0a, &[]),
tlv(0x0d, &[0x80, 0x01]),
tlv(0x0d, &[0x81]),
tlv(0x0d, &[]),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
for payload in [
tlv(0x0a, &[0x01]),
tlv(0x0a, &[0x00]),
tlv(0x0d, &[0xc2, 0x7b, 0x03, 0x02]),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{payload:02x?}");
}
}
#[test]
fn a_preserved_time_is_read_by_the_encoding_rather_than_the_validity_field() {
for payload in [
tlv(0x18, b"20500101000000Z"),
tlv(0x18, b"20500101000000.5Z"),
tlv(0x18, b"20500101000000.123Z"),
tlv(0x17, b"500101000000Z"),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{payload:02x?}");
}
for payload in [
tlv(0x18, b"20500101000000.50Z"),
tlv(0x18, b"20500101000000.0Z"),
tlv(0x18, b"205001010000Z"),
tlv(0x18, b"20500101000000"),
tlv(0x18, b"20501301000000Z"),
tlv(0x17, b"501301000000Z"),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
}
#[test]
fn a_preserved_string_that_is_not_its_type_is_refused() {
for payload in [
tlv(0x0c, &[0xff, 0xfe]),
tlv(0x13, b"a*b"),
tlv(0x16, &[0x41, 0x80]),
tlv(0x12, b"12x"),
tlv(0x1e, &[0x00, 0x41, 0x00]),
tlv(0x1c, &[0x00, 0x00, 0xd8, 0x00]),
seq(&[int(1), tlv(0x0c, &[0xc0, 0x80])]),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
for payload in [
utf8("Київ"),
tlv(0x13, b"Kyiv"),
tlv(0x1e, &[0x00, 0x41, 0x00, 0x42]),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok(), "{payload:02x?}");
}
}
#[test]
fn a_container_written_primitive_under_a_preserved_value_is_refused() {
for payload in [
tlv(0x10, &[]),
tlv(0x11, &[]),
tlv(0x08, &[]),
tlv(0x0b, &[]),
tlv(0x1d, &[]),
seq(&[int(1), tlv(0x10, &[])]),
] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err(), "{payload:02x?}");
}
}
#[test]
fn a_preserved_set_out_of_order_is_refused() {
let descending = tlv(0x31, &[int(2), int(1)].concat());
let ascending = tlv(0x31, &[int(1), int(2)].concat());
for (payload, ordered) in [(descending, false), (ascending, true)] {
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert_eq!(parse(&parser, &der).is_ok(), ordered, "{payload:02x?}");
}
let payload = seq(&[int(1), tlv(0x31, &[int(2), int(1)].concat())]);
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err());
let payload = seq(&[int(2), int(1)]);
let der = seq(&[context(
0,
&[
oid_of(oid::USER_ID),
context(0, core::slice::from_ref(&payload)),
],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn a_constructed_string_under_a_preserved_other_name_is_refused() {
let split = tlv(0x2c, &[utf8("a"), utf8("b")].concat());
let der = seq(&[context(
0,
&[oid_of(oid::USER_ID), context(0, &[seq(&[int(1), split])])],
)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err());
}