use super::*;
use crate::test_support::wire::mint;
use rustls::pki_types::CertificateDer;
use rustls::pki_types::pem::PemObject;
use tempfile::TempDir;
const SEQ: u8 = 0x30;
const SET: u8 = 0x31;
const UTF8: u8 = 0x0C;
fn tlv_of(tag: u8, body: &[u8]) -> Vec<u8> {
let mut out = vec![tag];
if body.len() < 0x80 {
out.push(u8::try_from(body.len()).unwrap_or(0));
} else {
let len = u32::try_from(body.len()).unwrap_or(0).to_be_bytes();
out.push(0x84);
out.extend_from_slice(&len);
}
out.extend_from_slice(body);
out
}
fn cat(parts: &[Vec<u8>]) -> Vec<u8> {
parts.concat()
}
fn rdn(oid: &[u8], tag: u8, value: &[u8]) -> Vec<u8> {
tlv_of(
SET,
&tlv_of(SEQ, &cat(&[tlv_of(OID, oid), tlv_of(tag, value)])),
)
}
fn certificate(subject: Vec<u8>) -> Vec<u8> {
let tbs = tlv_of(
SEQ,
&cat(&[
tlv_of(0xA0, &tlv_of(INTEGER, &[2])),
tlv_of(INTEGER, &[1]),
tlv_of(SEQ, &[]),
tlv_of(SEQ, &rdn(&COMMON_NAME, UTF8, b"the-issuer")),
tlv_of(SEQ, &[]),
subject,
]),
);
tlv_of(SEQ, &cat(&[tbs, tlv_of(SEQ, &[]), tlv_of(0x03, &[0])]))
}
#[test]
fn a_real_leaf_names_its_own_subject_and_not_its_issuer() {
let tmp = TempDir::new().expect("tmp");
mint(tmp.path());
let der = CertificateDer::from_pem_file(tmp.path().join("client.pem")).expect("pem");
assert_eq!(common_name(&der).as_deref(), Some("yog-client"));
let ca = CertificateDer::from_pem_file(tmp.path().join("ca.pem")).expect("pem");
assert_eq!(common_name(&ca).as_deref(), Some("yog-ca"));
let server = CertificateDer::from_pem_file(tmp.path().join("server.pem")).expect("pem");
assert_eq!(common_name(&server).as_deref(), Some("yog-server"));
let window = CertificateDer::from_pem_file(tmp.path().join("window.pem")).expect("pem");
assert_eq!(
common_name(&window).as_deref(),
Some(crate::registry::WINDOW)
);
}
#[test]
fn a_long_form_length_reads_like_a_short_one() {
let mut name = b"x".repeat(200);
name.extend_from_slice(b"-tail");
let der = certificate(tlv_of(SEQ, &rdn(&COMMON_NAME, UTF8, &name)));
assert_eq!(
common_name(&der),
Some(String::from_utf8(name).expect("utf8"))
);
}
#[test]
fn the_last_common_name_wins_and_other_attributes_are_passed_over() {
let organisation = [0x55, 0x04, 0x0A];
let der = certificate(tlv_of(
SEQ,
&cat(&[
rdn(&organisation, UTF8, b"an-org"),
rdn(&COMMON_NAME, UTF8, b"first"),
rdn(&COMMON_NAME, UTF8, b"last"),
]),
));
assert_eq!(common_name(&der).as_deref(), Some("last"));
}
#[test]
fn a_subject_without_a_common_name_names_nobody() {
let organisation = [0x55, 0x04, 0x0A];
let der = certificate(tlv_of(SEQ, &rdn(&organisation, UTF8, b"an-org")));
assert_eq!(common_name(&der), None);
}
#[test]
fn a_malformed_attribute_is_skipped() {
let lone = tlv_of(SET, &tlv_of(SEQ, &tlv_of(OID, &COMMON_NAME)));
assert_eq!(common_name(&certificate(tlv_of(SEQ, &lone))), None);
let mistyped = tlv_of(
SET,
&tlv_of(
SEQ,
&cat(&[tlv_of(UTF8, &COMMON_NAME), tlv_of(UTF8, b"nope")]),
),
);
assert_eq!(common_name(&certificate(tlv_of(SEQ, &mistyped))), None);
}
#[test]
fn a_name_that_is_not_utf8_is_skipped() {
let der = certificate(tlv_of(SEQ, &rdn(&COMMON_NAME, 0x1E, &[0xFF, 0xFE])));
assert_eq!(common_name(&der), None);
}
#[test]
fn bytes_that_are_not_a_certificate_name_nobody() {
assert_eq!(common_name(&[]), None);
assert_eq!(common_name(&[SEQ]), None);
assert_eq!(common_name(&tlv_of(SEQ, &[0xFF])), None);
assert_eq!(common_name(&tlv_of(SEQ, &tlv_of(SEQ, &[]))), None);
let short = tlv_of(
SEQ,
&tlv_of(SEQ, &cat(&[tlv_of(INTEGER, &[1]), tlv_of(SEQ, &[])])),
);
assert_eq!(common_name(&short), None);
}
#[test]
fn a_length_der_forbids_ends_the_read() {
assert_eq!(tlv(&[SEQ, 0x80, 0x00, 0x00]), None);
assert_eq!(tlv(&[SEQ, 0x85, 0, 0, 0, 0, 0]), None);
assert_eq!(tlv(&[SEQ, 0x82, 0x01]), None);
assert_eq!(tlv(&[SEQ, 0x40, 0x01]), None);
}
#[test]
fn a_malformed_tail_ends_the_element_walk() {
let body = cat(&[tlv_of(INTEGER, &[7]), vec![SEQ, 0x7F]]);
let read = elements(&body);
assert_eq!(read.len(), 1);
assert_eq!(read.first().map(|(tag, _)| *tag), Some(INTEGER));
}
#[test]
fn a_minted_foot_reads_back_as_one_and_every_other_leaf_does_not() {
let tmp = TempDir::new().expect("tmp");
mint(tmp.path());
crate::wire::provision::issue(tmp.path(), "host", crate::registry::Grade::Foot)
.expect("a foot leaf");
crate::wire::provision::issue(tmp.path(), "desk", crate::registry::Grade::Operator)
.expect("an operator leaf");
let read = |name: &str| {
let der =
CertificateDer::from_pem_file(tmp.path().join(format!("{name}.pem"))).expect("pem");
(common_name(&der), grade(&der))
};
assert_eq!(
read("host"),
(Some("host".to_owned()), crate::registry::Grade::Foot),
"the grade rides beside the identity, not instead of it"
);
assert_eq!(
read("desk"),
(Some("desk".to_owned()), crate::registry::Grade::Operator)
);
assert_eq!(read("client").1, crate::registry::Grade::Operator);
assert_eq!(read("ca").1, crate::registry::Grade::Operator);
}
#[test]
fn only_the_grades_own_word_demotes_and_unreadable_bytes_do_not() {
let footed = certificate(tlv_of(
SEQ,
&cat(&[
rdn(&ORG_UNIT, UTF8, crate::registry::peer::FOOT.as_bytes()),
rdn(&COMMON_NAME, UTF8, b"host"),
]),
));
assert_eq!(grade(&footed), crate::registry::Grade::Foot);
assert_eq!(common_name(&footed).as_deref(), Some("host"));
let other = certificate(tlv_of(
SEQ,
&cat(&[
rdn(&ORG_UNIT, UTF8, b"engineering"),
rdn(&COMMON_NAME, UTF8, b"host"),
]),
));
assert_eq!(grade(&other), crate::registry::Grade::Operator);
assert_eq!(grade(&[]), crate::registry::Grade::Operator);
}