use super::{Encoding, Name, read};
use crate::error::{ErrorCode, Result};
use crate::fixtures::{cat, ia5, int, oid as oid_of, printable, seq, set, tlv, utf8};
use crate::oid;
use alloc::borrow::ToOwned;
use alloc::vec::Vec;
use structured_asn1::{Oid, Parser};
fn parse<'a>(parser: &'a Parser, der: &'a [u8]) -> Result<Name<'a>> {
let mut reader = parser.read(der, 0).unwrap();
let element = reader.next_element().unwrap().unwrap();
read(&reader, &element, "test")
}
#[test]
fn ia5_name_attributes_use_their_exact_case_insensitive_rules() {
for (kind, left, right, matches) in [
(
oid::EMAIL_ADDRESS,
"USER@EXAMPLE.COM",
"user@example.com",
true,
),
(
oid::EMAIL_ADDRESS,
" user@example.com",
"user@example.com",
false,
),
(
oid::EMAIL_ADDRESS,
"user@ example.com",
"user@ example.com",
false,
),
(oid::DOMAIN_COMPONENT, "EXAMPLE", "example", true),
(oid::DOMAIN_COMPONENT, "example ", "example", false),
] {
let left_der = seq(&[part(kind, ia5(left))]);
let right_der = seq(&[part(kind, ia5(right))]);
let parser = Parser::der(crate::fixtures::memory());
let left = parse(&parser, &left_der).unwrap();
let right = parse(&parser, &right_der).unwrap();
let execution = crate::fixtures::memory();
assert_eq!(left.matches(&right, &execution).unwrap(), matches);
assert_eq!(left.order(&right, &execution).unwrap().is_eq(), matches);
assert_eq!(
left.ordered(&execution)
.unwrap()
.order(&right.ordered(&execution).unwrap(), &execution)
.unwrap()
.is_eq(),
matches
);
assert_eq!(execution.used(), 0);
}
}
#[test]
fn opaque_der_walk_admits_its_stack_before_allocating() {
use alloc::sync::Arc;
use structured_asn1::memory::{Memory, MemoryAccount};
let memory = Memory::new(Arc::new(MemoryAccount::new(16, 1024)));
let parser = Parser::der(memory.clone());
let mut reader = parser.read(&[0x30, 0], 73).unwrap();
let element = reader.next_element().unwrap().unwrap();
let error = super::walk_der(&reader, &element, "opaque", super::Strings::Checked)
.expect_err("the traversal stack exceeds the remaining capacity");
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Memory
))
);
assert_eq!(error.offset(), 73);
assert_eq!(memory.used(), 0);
}
fn part(kind: Oid<'_>, value: Vec<u8>) -> Vec<u8> {
set(&[seq(&[oid_of(kind), value])])
}
#[test]
fn displayed_text_retains_its_capacity_charge() {
let memory = crate::fixtures::memory();
let parser = Parser::der(memory.clone());
let der = seq(&[part(oid::COMMON_NAME, utf8("Retained subject"))]);
let name = parse(&parser, &der).unwrap();
let before = memory.used();
let text = name
.common_name(&crate::fixtures::memory())
.unwrap()
.unwrap()
.text(&memory)
.unwrap()
.unwrap();
assert!(memory.used() > before);
drop(name);
drop(parser);
assert!(memory.used() > 0);
drop(text);
assert_eq!(memory.used(), 0);
}
#[test]
fn parsed_name_retains_its_descriptor_storage() {
let memory = crate::fixtures::memory();
let parser = Parser::der(memory.clone());
let der = seq(&[part(oid::COMMON_NAME, utf8("Subject"))]);
let mut reader = parser.read(&der, 81).unwrap();
let element = reader.next_element().unwrap().unwrap();
let name = read(&reader, &element, "subject").unwrap();
drop(reader);
assert!(memory.used() > 0);
drop(name);
drop(parser);
assert_eq!(memory.used(), 0);
}
#[test]
fn attribute_search_refuses_once_in_the_current_domain() {
use structured_asn1::memory::{Memory, MemoryAccount};
let parser = Parser::der(crate::fixtures::memory());
let der = seq(&[
part(oid::ORGANIZATION, utf8("Example")),
part(oid::COMMON_NAME, utf8("Subject")),
]);
let mut reader = parser.read(&der, 101).unwrap();
let element = reader.next_element().unwrap().unwrap();
let name = read(&reader, &element, "subject").unwrap();
let execution = Memory::new(alloc::sync::Arc::new(MemoryAccount::new(0, 0)));
let before = parser.memory().work_used();
let error = name.common_name(&execution).unwrap_err();
assert!(error.is_resource());
assert_eq!(error.offset(), 101);
let mut attributes = name.attributes_of(oid::COMMON_NAME, &execution);
assert!(attributes.next().unwrap().unwrap_err().is_resource());
for _ in 0..3 {
assert!(attributes.next().is_none());
}
assert_eq!(execution.work_used(), 0);
assert_eq!(parser.memory().work_used(), before);
let common = {
let execution = crate::fixtures::memory();
name.common_name(&execution).unwrap().unwrap()
};
assert_eq!(common.kind(), oid::COMMON_NAME);
}
#[test]
fn every_named_attribute_uses_its_equality_rule() {
for kind in [oid::INITIALS, oid::GENERATION_QUALIFIER, oid::PSEUDONYM] {
let one = seq(&[part(kind, printable("AB"))]);
let other = seq(&[part(kind, utf8("ab"))]);
let parser = Parser::der(crate::fixtures::memory());
let one = parse(&parser, &one).unwrap();
let other = parse(&parser, &other).unwrap();
assert_eq!(
one.matches(&other, &crate::fixtures::memory()).unwrap(),
kind != oid::PSEUDONYM,
"{kind:?}"
);
}
let one = seq(&[part(oid::DN_QUALIFIER, printable("X1"))]);
let other = seq(&[part(oid::DN_QUALIFIER, printable("x1"))]);
let parser = Parser::der(crate::fixtures::memory());
let one = parse(&parser, &one).unwrap();
let other = parse(&parser, &other).unwrap();
assert!(one.matches(&other, &crate::fixtures::memory()).unwrap());
let mistyped = seq(&[part(oid::DN_QUALIFIER, utf8("x1"))]);
let parser = Parser::der(crate::fixtures::memory());
let mistyped = parse(&parser, &mistyped).unwrap();
assert!(!one.matches(&mistyped, &crate::fixtures::memory()).unwrap());
}
#[test]
fn an_unknown_attribute_kind_compares_by_its_encoded_value() {
let unknown = Oid::from_bytes(&[0x2a, 0x86, 0x24, 0x05, 0x01]);
let upper = seq(&[part(unknown, printable("ID"))]);
let lower = seq(&[part(unknown, printable("id"))]);
let parser = Parser::der(crate::fixtures::memory());
let upper = parse(&parser, &upper).unwrap();
let lower = parse(&parser, &lower).unwrap();
assert!(!upper.matches(&lower, &crate::fixtures::memory()).unwrap());
let twin = seq(&[part(unknown, printable("ID"))]);
let parser = Parser::der(crate::fixtures::memory());
let twin = parse(&parser, &twin).unwrap();
assert!(upper.matches(&twin, &crate::fixtures::memory()).unwrap());
}
#[test]
fn an_encoding_the_attribute_type_does_not_permit_never_folds() {
let malformed = seq(&[part(oid::COMMON_NAME, ia5("ROOT"))]);
let genuine = seq(&[part(oid::COMMON_NAME, utf8("root"))]);
let parser = Parser::der(crate::fixtures::memory());
let left = parse(&parser, &malformed).unwrap();
let right = parse(&parser, &genuine).unwrap();
assert!(!left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(!right.matches(&left, &crate::fixtures::memory()).unwrap());
let twin = seq(&[part(oid::COMMON_NAME, ia5("ROOT"))]);
let parser = Parser::der(crate::fixtures::memory());
let twin = parse(&parser, &twin).unwrap();
assert!(left.matches(&twin, &crate::fixtures::memory()).unwrap());
let upper = seq(&[part(oid::DOMAIN_COMPONENT, ia5("EXAMPLE"))]);
let lower = seq(&[part(oid::DOMAIN_COMPONENT, ia5("example"))]);
let parser = Parser::der(crate::fixtures::memory());
let upper = parse(&parser, &upper).unwrap();
let lower = parse(&parser, &lower).unwrap();
assert!(upper.matches(&lower, &crate::fixtures::memory()).unwrap());
}
fn bmp(text: &str) -> Vec<u8> {
let mut contents = Vec::new();
for unit in text.encode_utf16() {
contents.extend_from_slice(&unit.to_be_bytes());
}
tlv(0x1e, &contents)
}
#[test]
fn a_name_is_read_as_the_parts_it_was_written_in() {
let der = seq(&[
part(oid::COUNTRY, printable("UA")),
part(oid::ORGANIZATION, utf8("Дія")),
part(oid::COMMON_NAME, utf8("Тестовий підписувач")),
]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
assert_eq!(name.parts().len(), 3);
assert_eq!(name.attributes().count(), 3);
assert!(!name.is_empty());
assert_eq!(
name.common_name(&crate::fixtures::memory())
.unwrap()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.as_ref()
.map(|text| text.as_str(&crate::fixtures::memory()).unwrap()),
Some("Тестовий підписувач")
);
assert_eq!(
name.attribute(oid::COUNTRY, &crate::fixtures::memory())
.unwrap()
.unwrap()
.value()
.encoding(),
Encoding::Printable
);
assert_eq!(name.as_bytes(), &der[..]);
}
#[test]
fn an_organization_identifier_is_found_where_a_qualified_certificate_carries_it() {
let der = seq(&[
part(oid::ORGANIZATION, utf8("Autoritatea")),
part(oid::ORGANIZATION_IDENTIFIER, printable("VATRO-12345678")),
]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
assert_eq!(
name.organization_identifier(&crate::fixtures::memory())
.unwrap()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.as_ref()
.map(|text| text.as_str(&crate::fixtures::memory()).unwrap()),
Some("VATRO-12345678")
);
assert!(
name.common_name(&crate::fixtures::memory())
.unwrap()
.is_none()
);
}
#[test]
fn several_attributes_of_one_kind_are_all_kept() {
let der = seq(&[
part(oid::ORGANIZATIONAL_UNIT, utf8("first")),
part(oid::ORGANIZATIONAL_UNIT, utf8("second")),
]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
let units: Vec<_> = name
.attributes_of(oid::ORGANIZATIONAL_UNIT, parser.memory())
.map(|attribute| attribute.unwrap())
.filter_map(|attribute| attribute.text(&crate::fixtures::memory()).unwrap())
.map(|text| text.as_str(&crate::fixtures::memory()).unwrap().to_owned())
.collect();
assert_eq!(units, ["first", "second"]);
}
#[test]
fn the_same_name_written_differently_still_matches() {
let subject = seq(&[part(oid::ORGANIZATION, printable("ACME Ltd"))]);
let issuer = seq(&[part(oid::ORGANIZATION, utf8("acme ltd"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = parse(&one, &subject).unwrap();
let right = parse(&other, &issuer).unwrap();
assert!(left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(right.matches(&left, &crate::fixtures::memory()).unwrap());
assert_ne!(left.as_bytes(), right.as_bytes());
}
#[test]
fn a_name_written_in_sixteen_bit_characters_is_read_and_matched() {
let wide = seq(&[part(oid::COMMON_NAME, bmp("Ministerul Finanțelor"))]);
let narrow = seq(&[part(oid::COMMON_NAME, utf8("ministerul finanțelor"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = parse(&one, &wide).unwrap();
let right = parse(&other, &narrow).unwrap();
assert_eq!(
left.common_name(&crate::fixtures::memory())
.unwrap()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.as_ref()
.map(|text| text.as_str(&crate::fixtures::memory()).unwrap()),
Some("Ministerul Finanțelor")
);
assert!(left.matches(&right, &crate::fixtures::memory()).unwrap());
}
#[test]
fn names_that_say_different_things_do_not_match() {
let parsers = [
Parser::der(crate::fixtures::memory()),
Parser::der(crate::fixtures::memory()),
Parser::der(crate::fixtures::memory()),
Parser::der(crate::fixtures::memory()),
];
let base = seq(&[
part(oid::COUNTRY, printable("UA")),
part(oid::ORGANIZATION, utf8("Acme")),
]);
let others = [
seq(&[
part(oid::COUNTRY, printable("RO")),
part(oid::ORGANIZATION, utf8("Acme")),
]),
seq(&[
part(oid::COUNTRY, printable("UA")),
part(oid::ORGANIZATIONAL_UNIT, utf8("Acme")),
]),
seq(&[
part(oid::ORGANIZATION, utf8("Acme")),
part(oid::COUNTRY, printable("UA")),
]),
seq(&[part(oid::COUNTRY, printable("UA"))]),
];
let name = parse(&parsers[0], &base).unwrap();
for (index, other) in others.iter().enumerate() {
let parser = Parser::der(crate::fixtures::memory());
let candidate = parse(&parser, other).unwrap();
assert!(
!name
.matches(&candidate, &crate::fixtures::memory())
.unwrap(),
"{index}"
);
assert!(
!candidate
.matches(&name, &crate::fixtures::memory())
.unwrap(),
"{index}"
);
}
}
#[test]
fn attributes_within_one_part_are_unordered_among_themselves() {
let one = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("Signer")]),
seq(&[oid_of(oid::SURNAME), utf8("Melnyk")]),
])]);
let other = seq(&[set(&[
seq(&[oid_of(oid::SURNAME), utf8("Melnyk")]),
seq(&[oid_of(oid::COMMON_NAME), utf8("Signer")]),
])]);
let left_parser = Parser::der(crate::fixtures::memory());
let right_parser = Parser::ber(crate::fixtures::memory());
let left = parse(&left_parser, &one).unwrap();
let right = parse(&right_parser, &other).unwrap();
assert_eq!(left.parts().len(), 1);
assert_eq!(left.parts()[0].attributes().len(), 2);
assert!(left.matches(&right, &crate::fixtures::memory()).unwrap());
}
#[test]
fn a_value_that_is_not_text_is_compared_as_it_was_encoded() {
let one = seq(&[part(oid::DN_QUALIFIER, int(7))]);
let same = seq(&[part(oid::DN_QUALIFIER, int(7))]);
let different = seq(&[part(oid::DN_QUALIFIER, int(8))]);
let parsers = [
Parser::der(crate::fixtures::memory()),
Parser::der(crate::fixtures::memory()),
Parser::der(crate::fixtures::memory()),
];
let left = parse(&parsers[0], &one).unwrap();
let right = parse(&parsers[1], &same).unwrap();
let other = parse(&parsers[2], &different).unwrap();
assert_eq!(
left.attributes().next().unwrap().value().encoding(),
Encoding::Other
);
assert!(
left.attributes()
.next()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.is_none()
);
assert!(left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(!left.matches(&other, &crate::fixtures::memory()).unwrap());
}
#[test]
fn an_empty_name_is_read_rather_than_refused() {
let der = seq(&[]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
assert!(name.is_empty());
assert_eq!(name.attributes().count(), 0);
}
#[test]
fn a_part_that_names_nothing_is_refused() {
let der = seq(&[set(&[])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::EmptySequence);
assert_eq!(error.field(), "test");
}
#[test]
fn a_name_that_is_not_shaped_like_one_is_refused() {
let parser = Parser::der(crate::fixtures::memory());
let der = seq(&[seq(&[seq(&[oid_of(oid::COMMON_NAME), utf8("x")])])]);
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(&[set(&[cat(&[tlv(
0x30,
&cat(&[oid_of(oid::COMMON_NAME), utf8("one"), ia5("two")]),
)])])]);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::TrailingData);
let parser = Parser::der(crate::fixtures::memory());
let der = seq(&[set(&[seq(&[oid_of(oid::COMMON_NAME)])])]);
let error = parse(&parser, &der).unwrap_err();
assert_eq!(error.code(), ErrorCode::MissingField);
}
#[test]
fn a_sixteen_bit_string_of_an_odd_length_is_not_read_as_text() {
let der = seq(&[part(oid::COMMON_NAME, tlv(0x1e, &[0x00, 0x41, 0x00]))]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
assert!(
name.common_name(&crate::fixtures::memory())
.unwrap()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.is_none()
);
assert_eq!(
name.common_name(&crate::fixtures::memory())
.unwrap()
.unwrap()
.value()
.encoding(),
Encoding::Bmp
);
}
#[test]
fn a_duplicated_attribute_cannot_match_two_different_ones() {
let doubled = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("Signer")]),
seq(&[oid_of(oid::COMMON_NAME), utf8("Signer")]),
])]);
let mixed = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("Signer")]),
seq(&[oid_of(oid::SURNAME), utf8("Melnyk")]),
])]);
let left_parser = Parser::der(crate::fixtures::memory());
let right_parser = Parser::der(crate::fixtures::memory());
let left = parse(&left_parser, &doubled).unwrap();
let right = parse(&right_parser, &mixed).unwrap();
assert!(!left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(!right.matches(&left, &crate::fixtures::memory()).unwrap());
let also_doubled = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("SIGNER")]),
seq(&[oid_of(oid::COMMON_NAME), utf8("signer")]),
])]);
let other_parser = Parser::der(crate::fixtures::memory());
let other = parse(&other_parser, &also_doubled).unwrap();
assert!(left.matches(&other, &crate::fixtures::memory()).unwrap());
}
#[test]
fn a_value_that_does_not_decode_never_matches_one_that_does() {
let malformed = seq(&[part(oid::COMMON_NAME, tlv(0x0c, &[0xc3, 0x28]))]);
let genuine = seq(&[part(oid::COMMON_NAME, utf8("\u{c3}("))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = parse(&one, &malformed).unwrap();
let right = parse(&other, &genuine).unwrap();
assert!(!left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(!right.matches(&left, &crate::fixtures::memory()).unwrap());
assert!(
left.common_name(&crate::fixtures::memory())
.unwrap()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.is_some()
);
}
#[test]
fn a_der_part_with_attributes_out_of_order_is_refused() {
let backwards = seq(&[set(&[
seq(&[oid_of(oid::SURNAME), utf8("Melnyk")]),
seq(&[oid_of(oid::COMMON_NAME), utf8("Signer")]),
])]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &backwards).unwrap_err();
assert_eq!(error.code(), ErrorCode::MalformedValue);
let parser = Parser::ber(crate::fixtures::memory());
let name = parse(&parser, &backwards).unwrap();
assert_eq!(name.parts()[0].attributes().len(), 2);
}
#[test]
fn a_value_outside_its_declared_alphabet_never_matches_a_genuine_one() {
let malformed = seq(&[part(oid::COMMON_NAME, tlv(0x13, "Sign\u{e9}".as_bytes()))]);
let genuine = seq(&[part(oid::COMMON_NAME, utf8("Sign\u{e9}"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = parse(&one, &malformed).unwrap();
let right = parse(&other, &genuine).unwrap();
assert!(!left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(!right.matches(&left, &crate::fixtures::memory()).unwrap());
assert!(
left.common_name(&crate::fixtures::memory())
.unwrap()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.is_some()
);
let same_parser = Parser::der(crate::fixtures::memory());
let same = parse(&same_parser, &malformed).unwrap();
assert!(left.matches(&same, &crate::fixtures::memory()).unwrap());
}
#[test]
fn an_attribute_outside_its_size_bounds_never_folds() {
let malformed = seq(&[part(oid::COUNTRY, printable(" US "))]);
let genuine = seq(&[part(oid::COUNTRY, printable("US"))]);
let parser = Parser::der(crate::fixtures::memory());
let left = parse(&parser, &malformed).unwrap();
let right = parse(&parser, &genuine).unwrap();
assert!(!left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(!right.matches(&left, &crate::fixtures::memory()).unwrap());
let twin = seq(&[part(oid::COUNTRY, printable(" US "))]);
let parser = Parser::der(crate::fixtures::memory());
let twin = parse(&parser, &twin).unwrap();
assert!(left.matches(&twin, &crate::fixtures::memory()).unwrap());
let lower = seq(&[part(oid::COUNTRY, printable("us"))]);
let parser = Parser::der(crate::fixtures::memory());
let lower = parse(&parser, &lower).unwrap();
assert!(right.matches(&lower, &crate::fixtures::memory()).unwrap());
}
#[test]
fn an_oversized_personal_name_attribute_never_folds() {
let fits = "a".repeat(128);
let padded = [fits.as_str(), &" ".repeat(200)].concat();
let malformed = seq(&[part(oid::PSEUDONYM, printable(&padded))]);
let genuine = seq(&[part(oid::PSEUDONYM, printable(&fits))]);
let upper = "A".repeat(128);
let folded = seq(&[part(oid::PSEUDONYM, printable(&upper))]);
let parser = Parser::der(crate::fixtures::memory());
let left = parse(&parser, &malformed).unwrap();
let right = parse(&parser, &genuine).unwrap();
let folded = parse(&parser, &folded).unwrap();
assert!(!right.matches(&folded, &crate::fixtures::memory()).unwrap());
assert!(!left.matches(&right, &crate::fixtures::memory()).unwrap());
}
#[test]
fn an_oversized_street_address_never_folds() {
let fits = "a".repeat(128);
let padded = [fits.as_str(), &" ".repeat(200)].concat();
let malformed = seq(&[part(oid::STREET_ADDRESS, printable(&padded))]);
let genuine = seq(&[part(oid::STREET_ADDRESS, printable(&fits))]);
let parser = Parser::der(crate::fixtures::memory());
let left = parse(&parser, &malformed).unwrap();
let right = parse(&parser, &genuine).unwrap();
assert!(!left.matches(&right, &crate::fixtures::memory()).unwrap());
}
#[test]
fn a_country_that_is_not_two_letters_never_reads_as_one() {
let digits = seq(&[part(oid::COUNTRY, printable("12"))]);
let letters = seq(&[part(oid::COUNTRY, printable("UA"))]);
let parser = Parser::der(crate::fixtures::memory());
let left = parse(&parser, &digits).unwrap();
let right = parse(&parser, &letters).unwrap();
assert!(!super::well_formed(left.attributes().next().unwrap()));
assert!(super::well_formed(right.attributes().next().unwrap()));
}
#[test]
fn a_constructed_universal_attribute_value_is_refused_under_der() {
let unknown = Oid::from_bytes(&[0x2a, 0x86, 0x24, 0x05, 0x03]);
let constructed = seq(&[part(unknown, tlv(0x2c, &utf8("x")))]);
let parser = Parser::der(crate::fixtures::memory());
let error = parse(&parser, &constructed).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag)
);
let primitive = seq(&[part(unknown, utf8("x"))]);
let container = seq(&[part(unknown, seq(&[int(1)]))]);
for der in [primitive, container] {
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_ok());
}
}
#[test]
fn ber_does_not_make_primitive_only_types_constructible() {
let unknown = Oid::from_bytes(&[0x2a, 3]);
for tag in [1, 2, 5, 6, 9, 10, 13, 14, 31, 32, 33, 34, 35, 36] {
let value = if tag < 31 {
tlv(0x20 | tag, &[])
} else {
alloc::vec![0x3f, tag, 0]
};
let bytes = seq(&[part(unknown, value)]);
let parser = Parser::ber(crate::fixtures::memory());
assert!(parse(&parser, &bytes).is_err(), "universal tag {tag}");
}
}
#[test]
fn ber_string_segments_use_the_base_string_tag() {
let unknown = Oid::from_bytes(&[0x2a, 3]);
let parser = Parser::ber(crate::fixtures::memory());
let valid = seq(&[part(unknown, tlv(0x2c, &tlv(4, b"A")))]);
assert!(parse(&parser, &valid).is_ok());
for child in [utf8("A"), int(1), tlv(0x84, b"A")] {
let invalid = seq(&[part(unknown, tlv(0x2c, &child))]);
assert!(parse(&parser, &invalid).is_err());
}
}
#[test]
fn ber_partial_bit_segments_must_be_last_at_every_depth() {
let unknown = Oid::from_bytes(&[0x2a, 3]);
let parser = Parser::ber(crate::fixtures::memory());
let partial = tlv(3, &[7, 0x80]);
let empty = tlv(3, &[0]);
for value in [
tlv(0x23, &[partial.clone(), empty.clone()].concat()),
tlv(0x23, &[tlv(0x23, &partial), empty].concat()),
] {
let invalid = seq(&[part(unknown, value)]);
assert!(parse(&parser, &invalid).is_err());
}
let valid = seq(&[part(unknown, tlv(0x23, &tlv(0x23, &partial)))]);
assert!(parse(&parser, &valid).is_ok());
}
#[test]
fn opaque_ber_times_keep_the_useful_types_broader_grammar() {
let unknown = Oid::from_bytes(&[0x2a, 3]);
let parser = Parser::ber(crate::fixtures::memory());
for text in [
"1985110621",
"1985110621Z",
"1985110621.1415900",
"198511062106,456Z",
"19851106210627.000-0500",
"1985110621+05",
] {
let value = seq(&[part(unknown, tlv(0x18, text.as_bytes()))]);
assert!(parse(&parser, &value).is_ok(), "{text}");
}
for text in ["8201021200Z", "8201020700-0500"] {
let value = seq(&[part(unknown, tlv(0x17, text.as_bytes()))]);
assert!(parse(&parser, &value).is_ok(), "{text}");
}
for (tag, text) in [
(0x18, "19851106240000Z"),
(0x18, "1985110621.Z"),
(0x17, "82010212Z"),
(0x17, "8201021200"),
(0x17, "8201021200+05"),
(0x17, "820102120060Z"),
] {
let value = seq(&[part(unknown, tlv(tag, text.as_bytes()))]);
assert!(parse(&parser, &value).is_err(), "{text}");
}
}
#[test]
fn ber_associated_sequence_strings_validate_their_implicit_segments() {
let unknown = Oid::from_bytes(&[0x2a, 3]);
let parser = Parser::ber(crate::fixtures::memory());
for value in [
tlv(0x28, &tlv(0xa1, &int(1))),
tlv(
0x3d,
&[tlv(0xa0, &tlv(0x85, &[])), tlv(0xa2, &int(1))].concat(),
),
] {
let invalid = seq(&[part(unknown, value)]);
assert!(parse(&parser, &invalid).is_err());
}
let valid = seq(&[part(unknown, tlv(0x28, &tlv(0xa2, &tlv(3, &[7, 0x80]))))]);
assert!(parse(&parser, &valid).is_ok());
}
#[test]
fn preparation_admits_the_complete_fixed_schema_lookup_bound() {
use alloc::sync::Arc;
use structured_asn1::memory::{Memory, MemoryAccount};
let parser = Parser::der(crate::fixtures::memory());
let bytes = seq(&[part(oid::ORGANIZATION_IDENTIFIER, utf8("A"))]);
let name = parse(&parser, &bytes).unwrap();
let attribute = *name.attributes().next().unwrap();
let bound = 64 * attribute.kind().as_bytes().len() + 4;
let refused = Memory::new(Arc::new(MemoryAccount::new(0, bound - 1)));
assert!(attribute.folds(&refused).unwrap_err().is_resource());
assert_eq!(refused.work_used(), 0);
let exact = Memory::new(Arc::new(MemoryAccount::new(0, bound)));
assert!(attribute.folds(&exact).unwrap());
assert_eq!(exact.work_used(), bound);
assert_eq!(exact.used(), 0);
}
#[test]
fn the_set_ordering_comparison_is_charged_to_the_work_budget() {
let unknown = Oid::from_bytes(&[0x2a, 0x86, 0x24, 0x05, 0x04]);
let mut big = alloc::vec![0xaau8; 50_000];
let first = seq(&[oid_of(unknown), tlv(0x04, &big)]);
*big.last_mut().unwrap() = 0xbb;
let second = seq(&[oid_of(unknown), tlv(0x04, &big)]);
let der = seq(&[set(&[first, second])]);
let limits = structured_asn1::Limits {
max_work: 4096,
..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 comparing_a_retained_name_uses_the_current_operations_budget() {
let unknown = Oid::from_bytes(&[0x2a, 0x86, 0x24, 0x05, 0x05]);
let big = alloc::vec![0xaau8; 100_000];
let der = seq(&[part(unknown, tlv(0x04, &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 name = parse(&parser, &der).unwrap();
let execution = structured_asn1::memory::Memory::new(alloc::sync::Arc::new(
structured_asn1::memory::MemoryAccount::new(1024 * 1024, 8192),
));
assert_eq!(
name.matches(&name, &execution).unwrap_err().code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
assert_eq!(execution.work_used(), 0);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
let other = Parser::der(crate::fixtures::memory());
let twin = parse(&other, &der).unwrap();
assert!(name.matches(&twin, &crate::fixtures::memory()).unwrap());
}
#[test]
fn sorting_a_part_charges_the_kinds_it_compares() {
let kinds: Vec<Vec<u8>> = (1u8..=8)
.map(|last| {
let mut bytes = alloc::vec![0x81u8; 1_000];
bytes[0] = 0x2a;
*bytes.last_mut().unwrap() = last;
bytes
})
.collect();
let members: Vec<Vec<u8>> = kinds
.iter()
.map(|kind| seq(&[tlv(0x06, kind), utf8("x")]))
.collect();
let der = seq(&[set(&members)]);
let limits = structured_asn1::Limits {
max_work: 16_384,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
let error = parse(&parser, &der).unwrap_err();
assert!(error.is_resource());
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
let execution = structured_asn1::memory::Memory::new(alloc::sync::Arc::new(
structured_asn1::memory::MemoryAccount::new(1024 * 1024, 16_384),
));
let error = name.ordered(&execution).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_part_of_several_attributes_matches_whatever_order_it_is_written_in() {
let one = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("a b")]),
seq(&[oid_of(oid::SURNAME), utf8("Melnyk")]),
])]);
let reordered = seq(&[set(&[
seq(&[oid_of(oid::SURNAME), utf8("MELNYK")]),
seq(&[
oid_of(oid::COMMON_NAME),
printable("a b"),
]),
])]);
let different = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("a b")]),
seq(&[oid_of(oid::SURNAME), utf8("Melnychuk")]),
])]);
let first = Parser::der(crate::fixtures::memory());
let second = Parser::der(crate::fixtures::memory());
let third = Parser::der(crate::fixtures::memory());
let left = parse(&first, &one).unwrap();
let right = parse(&second, &reordered).unwrap();
let other = parse(&third, &different).unwrap();
assert!(left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(right.matches(&left, &crate::fixtures::memory()).unwrap());
assert!(!left.matches(&other, &crate::fixtures::memory()).unwrap());
}
#[test]
fn a_part_of_several_attributes_keeps_undecodable_values_byte_only() {
let malformed = tlv(0x0c, &[0xc3, 0x28]);
let one = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), malformed.clone()]),
seq(&[oid_of(oid::SURNAME), utf8("Melnyk")]),
])]);
let same = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), malformed]),
seq(&[oid_of(oid::SURNAME), utf8("melnyk")]),
])]);
let genuine = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("\u{c3}(")]),
seq(&[oid_of(oid::SURNAME), utf8("Melnyk")]),
])]);
let first = Parser::der(crate::fixtures::memory());
let second = Parser::der(crate::fixtures::memory());
let third = Parser::der(crate::fixtures::memory());
let left = parse(&first, &one).unwrap();
let right = parse(&second, &same).unwrap();
let other = parse(&third, &genuine).unwrap();
assert!(left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(!left.matches(&other, &crate::fixtures::memory()).unwrap());
}
#[test]
fn two_attributes_of_one_kind_match_across_the_case_that_reorders_them() {
let one = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("B")]),
seq(&[oid_of(oid::COMMON_NAME), utf8("a")]),
])]);
let other = seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8("A")]),
seq(&[oid_of(oid::COMMON_NAME), utf8("b")]),
])]);
let first = Parser::der(crate::fixtures::memory());
let second = Parser::der(crate::fixtures::memory());
let left = parse(&first, &one).unwrap();
let right = parse(&second, &other).unwrap();
assert!(left.matches(&right, &crate::fixtures::memory()).unwrap());
assert!(right.matches(&left, &crate::fixtures::memory()).unwrap());
}
#[test]
fn an_unknown_attribute_written_as_a_character_string_is_preserved() {
let unknown = Oid::from_bytes(&[0x2a, 0x86, 0x24, 0x05, 0x03]);
let identification = tlv(0xa0, &tlv(0x85, &[]));
let character_string = tlv(0x3d, &[identification, tlv(0x82, b"value")].concat());
let der = seq(&[part(unknown, character_string)]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
assert_eq!(name.attributes().count(), 1);
}
#[test]
fn a_letterform_folds_onto_the_letter_it_presents() {
let presented = seq(&[part(oid::ORGANIZATION, utf8("\u{2102}ompany"))]);
let plain = seq(&[part(oid::ORGANIZATION, utf8("company"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &presented)
.unwrap()
.matches(&parse(&other, &plain).unwrap(), &crate::fixtures::memory())
.unwrap()
);
for (left, right) in [("\u{210E}", "h"), ("\u{2115}o", "no"), ("\u{33A1}", "m2")] {
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = seq(&[part(oid::ORGANIZATION, utf8(left))]);
let right = seq(&[part(oid::ORGANIZATION, utf8(right))]);
assert!(
parse(&one, &left)
.unwrap()
.matches(&parse(&other, &right).unwrap(), &crate::fixtures::memory())
.unwrap(),
"{left:02x?}"
);
}
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = seq(&[part(oid::ORGANIZATION, utf8("\u{2102}"))]);
let right = seq(&[part(oid::ORGANIZATION, utf8("d"))]);
assert!(
!parse(&one, &left)
.unwrap()
.matches(&parse(&other, &right).unwrap(), &crate::fixtures::memory())
.unwrap()
);
}
#[test]
fn a_segmented_value_inside_a_preserved_one_follows_the_encoding() {
let unknown = Oid::from_bytes(&[0x2a, 0x86, 0x24, 0x05, 0x06]);
let segmented = tlv(
0x2b,
&[tlv(0xa0, &tlv(0x85, &[])), tlv(0xa2, &tlv(0x04, b"d"))].concat(),
);
let der = seq(&[part(unknown, segmented)]);
let parser = Parser::der(crate::fixtures::memory());
assert!(parse(&parser, &der).is_err());
let parser = Parser::new(
structured_asn1::Encoding::Ber,
structured_asn1::Limits::default(),
crate::fixtures::memory(),
);
assert!(parse(&parser, &der).is_ok());
}
#[test]
fn the_preparation_maps_what_the_profile_says_it_maps() {
let broken = seq(&[part(oid::ORGANIZATION, utf8("ACME\nCA"))]);
let spaced = seq(&[part(oid::ORGANIZATION, utf8("ACME CA"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &broken)
.unwrap()
.matches(&parse(&other, &spaced).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let hyphenated = seq(&[part(oid::ORGANIZATION, utf8("ACME\u{00ad}CA"))]);
let joined = seq(&[part(oid::ORGANIZATION, utf8("ACMECA"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &hyphenated)
.unwrap()
.matches(&parse(&other, &joined).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let different = seq(&[part(oid::ORGANIZATION, utf8("ACME CB"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
!parse(&one, &spaced)
.unwrap()
.matches(
&parse(&other, &different).unwrap(),
&crate::fixtures::memory()
)
.unwrap()
);
}
#[test]
fn a_part_pays_for_the_comparison_its_shape_will_cost() {
let mut members = Vec::new();
for first in 0..8u8 {
let mut value = alloc::vec![b'a' + first];
value.extend(core::iter::repeat_n(b'z', 20_000));
members.push(seq(&[oid_of(oid::DOMAIN_COMPONENT), tlv(0x16, &value)]));
}
let der = seq(&[set(&members)]);
let limits = structured_asn1::Limits {
max_work: 250_000,
..structured_asn1::Limits::default()
};
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
let name = parse(&parser, &der).unwrap();
let execution = structured_asn1::memory::Memory::new(alloc::sync::Arc::new(
structured_asn1::memory::MemoryAccount::new(1024 * 1024, 250_000),
));
let error = name.ordered(&execution).unwrap_err();
assert_eq!(
error.code(),
ErrorCode::Encoding(structured_asn1::ErrorCode::LimitExceeded(
structured_asn1::Limit::Work
))
);
let mut value = alloc::vec![b'a'];
value.extend(core::iter::repeat_n(b'z', 20_000));
let single = seq(&[set(&[seq(&[
oid_of(oid::DOMAIN_COMPONENT),
tlv(0x16, &value),
])])]);
let parser = Parser::new(
structured_asn1::Encoding::Der,
limits,
crate::fixtures::memory(),
);
assert!(parse(&parser, &single).is_ok());
}
#[test]
fn case_folds_beyond_ascii_where_the_profile_requires_it() {
let upper = seq(&[part(oid::ORGANIZATION, utf8("ЦЕНТРАЛЬНИЙ ОРГАН"))]);
let lower = seq(&[part(oid::ORGANIZATION, utf8("центральний орган"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &lower).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let accented = seq(&[part(oid::ORGANIZATION, utf8("École"))]);
let plain = seq(&[part(oid::ORGANIZATION, utf8("école"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &accented)
.unwrap()
.matches(&parse(&other, &plain).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let different = seq(&[part(oid::ORGANIZATION, utf8("ecole"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
!parse(&one, &plain)
.unwrap()
.matches(
&parse(&other, &different).unwrap(),
&crate::fixtures::memory()
)
.unwrap()
);
}
#[test]
fn a_value_the_preparation_prohibits_is_compared_as_bytes() {
for prohibited in ['\u{e000}', '\u{fdd0}', '\u{fffe}', '\u{fffd}'] {
let upper = seq(&[part(
oid::ORGANIZATION,
utf8(&alloc::format!("A{prohibited}")),
)]);
let lower = seq(&[part(
oid::ORGANIZATION,
utf8(&alloc::format!("a{prohibited}")),
)]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
!parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &lower).unwrap(), &crate::fixtures::memory())
.unwrap(),
"{prohibited:?}"
);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = parse(&one, &upper).unwrap();
assert!(
left.matches(&parse(&other, &upper).unwrap(), &crate::fixtures::memory())
.unwrap(),
"{prohibited:?}"
);
assert!(
left.attributes()
.next()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.is_some()
);
}
}
#[test]
fn a_legacy_teletex_value_is_kept_rather_than_called_malformed() {
let legacy = tlv(0x14, &[0xc2, 0x65]);
let der = seq(&[part(oid::COMMON_NAME, legacy.clone())]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
assert!(super::well_formed(name.attributes().next().unwrap()));
let twin = seq(&[part(oid::COMMON_NAME, legacy)]);
let other = Parser::der(crate::fixtures::memory());
assert!(
name.matches(&parse(&other, &twin).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let different = seq(&[part(oid::COMMON_NAME, tlv(0x14, &[0xc2, 0x45]))]);
let other = Parser::der(crate::fixtures::memory());
assert!(
!name
.matches(
&parse(&other, &different).unwrap(),
&crate::fixtures::memory()
)
.unwrap()
);
let upper = seq(&[part(oid::COMMON_NAME, tlv(0x14, b"ROOT"))]);
let lower = seq(&[part(oid::COMMON_NAME, utf8("root"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &lower).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let long = seq(&[part(oid::COMMON_NAME, tlv(0x14, &[0xc2; 65]))]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &long).unwrap();
assert!(!super::well_formed(name.attributes().next().unwrap()));
}
#[test]
fn a_character_the_mapping_removes_does_not_stop_the_preparation() {
for removed in ['\u{200e}', '\u{200f}', '\u{202a}', '\u{206a}', '\u{00ad}'] {
let upper = seq(&[part(oid::ORGANIZATION, utf8(&alloc::format!("A{removed}")))]);
let lower = seq(&[part(oid::ORGANIZATION, utf8("a"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &lower).unwrap(), &crate::fixtures::memory())
.unwrap(),
"{removed:?}"
);
}
for normalized in ['\u{0340}', '\u{0341}'] {
let upper = seq(&[part(
oid::ORGANIZATION,
utf8(&alloc::format!("A{normalized}")),
)]);
let lower = seq(&[part(
oid::ORGANIZATION,
utf8(&alloc::format!("a{normalized}")),
)]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &lower).unwrap(), &crate::fixtures::memory())
.unwrap(),
"{normalized:?}"
);
}
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let marked = seq(&[part(oid::ORGANIZATION, utf8("Kyiv\u{200f}"))]);
let plain = seq(&[part(oid::ORGANIZATION, utf8("kyiv"))]);
assert!(
parse(&one, &marked)
.unwrap()
.matches(&parse(&other, &plain).unwrap(), &crate::fixtures::memory())
.unwrap()
);
}
#[test]
fn a_value_the_bidi_step_refuses_is_compared_as_bytes() {
let upper = seq(&[part(oid::ORGANIZATION, utf8("Aא"))]);
let lower = seq(&[part(oid::ORGANIZATION, utf8("aא"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
!parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &lower).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &upper).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let spaced = seq(&[part(oid::ORGANIZATION, utf8("בית הדפוס"))]);
let single = seq(&[part(oid::ORGANIZATION, utf8("בית הדפוס"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &spaced)
.unwrap()
.matches(&parse(&other, &single).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let padded = seq(&[part(oid::ORGANIZATION, utf8(" בית הדפוס "))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &padded)
.unwrap()
.matches(&parse(&other, &single).unwrap(), &crate::fixtures::memory())
.unwrap()
);
}
#[test]
fn a_character_the_profile_never_saw_is_compared_as_bytes() {
let newer = seq(&[part(oid::ORGANIZATION, utf8("STRAẞE"))]);
let older = seq(&[part(oid::ORGANIZATION, utf8("strasse"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
!parse(&one, &newer)
.unwrap()
.matches(&parse(&other, &older).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &newer)
.unwrap()
.matches(&parse(&other, &newer).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let spaced = seq(&[part(oid::ORGANIZATION, utf8("ACME CA"))]);
let single = seq(&[part(oid::ORGANIZATION, utf8("acme ca"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &spaced)
.unwrap()
.matches(&parse(&other, &single).unwrap(), &crate::fixtures::memory())
.unwrap()
);
}
#[test]
fn the_bidi_step_reads_the_value_the_normalizing_step_leaves() {
let utf8_form = seq(&[part(oid::ORGANIZATION, utf8("א℡א"))]);
let bmp_form = seq(&[part(oid::ORGANIZATION, bmp("א℡א"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
!parse(&one, &utf8_form)
.unwrap()
.matches(
&parse(&other, &bmp_form).unwrap(),
&crate::fixtures::memory()
)
.unwrap()
);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &utf8_form)
.unwrap()
.matches(
&parse(&other, &utf8_form).unwrap(),
&crate::fixtures::memory()
)
.unwrap()
);
let utf8_form = seq(&[part(oid::ORGANIZATION, utf8("בית הדפוס"))]);
let bmp_form = seq(&[part(oid::ORGANIZATION, bmp("בית הדפוס"))]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &utf8_form)
.unwrap()
.matches(
&parse(&other, &bmp_form).unwrap(),
&crate::fixtures::memory()
)
.unwrap()
);
}
#[test]
fn a_value_wider_than_a_person_reads_is_shown_up_to_the_bound() {
let wide = "a".repeat(40_000);
let der = seq(&[part(oid::ORGANIZATION_IDENTIFIER, utf8(&wide))]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
let owned = name
.attributes()
.next()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.unwrap();
let shown = owned.as_str(&crate::fixtures::memory()).unwrap();
assert_eq!(shown.chars().count(), 32_769);
assert!(shown.ends_with('…'));
assert!(shown.starts_with("aaaa"));
let narrow = "a".repeat(32_768);
let der = seq(&[part(oid::ORGANIZATION_IDENTIFIER, utf8(&narrow))]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
let owned = name
.attributes()
.next()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.unwrap();
let shown = owned.as_str(&crate::fixtures::memory()).unwrap();
assert_eq!(shown, narrow);
let der = seq(&[part(oid::COMMON_NAME, bmp(&wide))]);
let parser = Parser::der(crate::fixtures::memory());
let name = parse(&parser, &der).unwrap();
let owned = name
.attributes()
.next()
.unwrap()
.text(&crate::fixtures::memory())
.unwrap()
.unwrap();
let shown = owned.as_str(&crate::fixtures::memory()).unwrap();
assert_eq!(shown.chars().count(), 32_769);
assert!(shown.ends_with('…'));
}
#[test]
fn funded_wide_values_keep_the_same_preparation_rule() {
let wide = "\u{0301}".repeat(32_769);
let upper = seq(&[part(
oid::ORGANIZATION_IDENTIFIER,
utf8(&alloc::format!("A{wide}")),
)]);
let lower = seq(&[part(
oid::ORGANIZATION_IDENTIFIER,
utf8(&alloc::format!("a{wide}")),
)]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &lower).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let narrow = "\u{0301}".repeat(64);
let upper = seq(&[part(
oid::ORGANIZATION_IDENTIFIER,
utf8(&alloc::format!("A{narrow}")),
)]);
let lower = seq(&[part(
oid::ORGANIZATION_IDENTIFIER,
utf8(&alloc::format!("a{narrow}")),
)]);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
assert!(
parse(&one, &upper)
.unwrap()
.matches(&parse(&other, &lower).unwrap(), &crate::fixtures::memory())
.unwrap()
);
}
#[test]
fn a_name_spelled_in_either_composition_is_one_name() {
for (composed, decomposed) in [
("École", "E\u{0301}cole"),
("КИЇВ", "КИ\u{0406}\u{0308}В"),
] {
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = seq(&[part(oid::ORGANIZATION, utf8(composed))]);
let right = seq(&[part(oid::ORGANIZATION, utf8(decomposed))]);
assert!(
parse(&one, &left)
.unwrap()
.matches(&parse(&other, &right).unwrap(), &crate::fixtures::memory())
.unwrap(),
"{composed}"
);
}
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = seq(&[part(oid::ORGANIZATION, utf8("\u{FF21}\u{FF22}"))]);
let right = seq(&[part(oid::ORGANIZATION, utf8("ab"))]);
assert!(
parse(&one, &left)
.unwrap()
.matches(&parse(&other, &right).unwrap(), &crate::fixtures::memory())
.unwrap()
);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = seq(&[part(oid::ORGANIZATION, utf8("École"))]);
let right = seq(&[part(oid::ORGANIZATION, utf8("Ecole"))]);
assert!(
!parse(&one, &left)
.unwrap()
.matches(&parse(&other, &right).unwrap(), &crate::fixtures::memory())
.unwrap()
);
}
#[test]
fn a_fold_that_changes_a_letters_length_is_still_a_fold() {
for (left, right) in [
("Straße", "STRASSE"),
("ΟΔΟΣ", "οδος"),
("final", "FINAL"),
] {
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = seq(&[part(oid::ORGANIZATION, utf8(left))]);
let right = seq(&[part(oid::ORGANIZATION, utf8(right))]);
assert!(
parse(&one, &left)
.unwrap()
.matches(&parse(&other, &right).unwrap(), &crate::fixtures::memory())
.unwrap(),
"{left:02x?}"
);
}
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let capital = seq(&[part(oid::ORGANIZATION, utf8("STRAẞE"))]);
let spelled = seq(&[part(oid::ORGANIZATION, utf8("strasse"))]);
assert!(
!parse(&one, &capital)
.unwrap()
.matches(
&parse(&other, &spelled).unwrap(),
&crate::fixtures::memory()
)
.unwrap()
);
let one = Parser::der(crate::fixtures::memory());
let other = Parser::der(crate::fixtures::memory());
let left = seq(&[part(oid::ORGANIZATION, utf8("Straße"))]);
let right = seq(&[part(oid::ORGANIZATION, utf8("Stras"))]);
assert!(
!parse(&one, &left)
.unwrap()
.matches(&parse(&other, &right).unwrap(), &crate::fixtures::memory())
.unwrap()
);
}
#[test]
fn a_prepared_name_orders_the_way_the_names_themselves_do() {
let attributes = |common: &str, organization: &str| {
seq(&[set(&[
seq(&[oid_of(oid::COMMON_NAME), utf8(common)]),
seq(&[oid_of(oid::ORGANIZATION), utf8(organization)]),
])])
};
let written = attributes("Ada", "Works");
let other_way = seq(&[set(&[
seq(&[oid_of(oid::ORGANIZATION), utf8("Works")]),
seq(&[oid_of(oid::COMMON_NAME), utf8("Ada ")]),
])]);
let one = Parser::der(crate::fixtures::memory());
let two = Parser::der(crate::fixtures::memory());
let left = parse(&one, &written).unwrap();
let right = parse(&two, &other_way).unwrap();
assert!(
left.matches(&right, &crate::fixtures::memory()).unwrap(),
"a part is a set, so this is one name"
);
assert_eq!(
left.order(&right, &crate::fixtures::memory()).unwrap(),
core::cmp::Ordering::Equal
);
assert_eq!(
left.ordered(&crate::fixtures::memory())
.unwrap()
.order(
&right.ordered(&crate::fixtures::memory()).unwrap(),
&crate::fixtures::memory()
)
.unwrap(),
core::cmp::Ordering::Equal,
"and the prepared form says the same thing"
);
let three = Parser::der(crate::fixtures::memory());
let different = attributes("Ada", "Elsewhere");
let elsewhere = parse(&three, &different).unwrap();
assert_eq!(
left.ordered(&crate::fixtures::memory())
.unwrap()
.order(
&elsewhere.ordered(&crate::fixtures::memory()).unwrap(),
&crate::fixtures::memory()
)
.unwrap(),
left.order(&elsewhere, &crate::fixtures::memory()).unwrap()
);
assert!(
left.order(&elsewhere, &crate::fixtures::memory())
.unwrap()
.is_ne()
);
}