use super::{FOOT, common_name, foot, is_foot};
use crate::channel::material::CHAIN;
use crate::test_support::{Scratch, mint};
fn der(tag: u8, body: &[u8]) -> Vec<u8> {
assert!(body.len() < 0x80, "the suite builds short values only");
let mut out = vec![tag, u8::try_from(body.len()).expect("short form")];
out.extend_from_slice(body);
out
}
fn name(attributes: &[(Vec<u8>, u8, Vec<u8>)]) -> Vec<u8> {
let mut body = Vec::new();
for (oid, tag, value) in attributes {
let attribute = der(0x30, &[der(0x06, oid), der(*tag, value)].concat());
body.extend(der(0x31, &attribute));
}
der(0x30, &body)
}
fn certificate(subject: &[u8], leading: &[Vec<u8>]) -> Vec<u8> {
let mut tbs: Vec<u8> = leading.concat();
tbs.extend(der(0x02, &[1]));
for _ in 0..3 {
tbs.extend(der(0x30, &[]));
}
tbs.extend_from_slice(subject);
der(0x30, &der(0x30, &tbs))
}
fn cn() -> Vec<u8> {
vec![0x55, 0x04, 0x03]
}
fn ou() -> Vec<u8> {
vec![0x55, 0x04, 0x0b]
}
#[test]
fn a_minted_foot_leaf_answers_the_client_identity() {
let scratch = Scratch::new();
mint::material(scratch.path());
assert_eq!(foot(&scratch.join(CHAIN)), Ok(mint::FOOT_NAME.to_owned()));
}
#[test]
fn an_operator_grade_leaf_is_refused_and_says_how_to_fix_it() {
let scratch = Scratch::new();
mint::material(scratch.path());
let refusal =
foot(&scratch.join(&format!("{}.pem", mint::OPERATOR_NAME))).expect_err("refused");
assert!(refusal.contains(mint::OPERATOR_NAME), "{refusal}");
assert!(refusal.contains("not foot grade"), "{refusal}");
assert!(refusal.contains(&format!("OU={FOOT}")), "{refusal}");
assert!(refusal.contains("box that holds the CA"), "{refusal}");
}
#[test]
fn a_leaf_that_names_no_client_is_refused_even_when_it_says_foot() {
let scratch = Scratch::new();
mint::material(scratch.path());
let refusal = foot(&scratch.join(&format!("{}.pem", mint::NAMELESS))).expect_err("refused");
assert!(refusal.contains("states no common name"), "{refusal}");
}
#[test]
fn a_chain_that_holds_no_certificate_refuses() {
let scratch = Scratch::new();
let missing = scratch.join("nowhere.pem");
assert!(foot(&missing).expect_err("refused").contains("nowhere.pem"));
let empty = scratch.join("empty.pem");
std::fs::write(&empty, b"").expect("write");
assert!(foot(&empty).expect_err("refused").contains("empty.pem"));
}
#[test]
fn the_last_common_name_in_a_subject_wins() {
let bytes = certificate(
&name(&[
(cn(), 0x0c, b"an intermediate".to_vec()),
(cn(), 0x0c, b"the leaf".to_vec()),
]),
&[],
);
assert_eq!(common_name(&bytes).as_deref(), Some("the leaf"));
}
#[test]
fn the_grade_is_the_organizational_unit_and_nothing_else() {
let is = certificate(
&name(&[
(ou(), 0x0c, FOOT.as_bytes().to_vec()),
(cn(), 0x0c, b"box".to_vec()),
]),
&[],
);
assert!(is_foot(&is));
assert_eq!(common_name(&is).as_deref(), Some("box"));
let other = certificate(
&name(&[
(ou(), 0x0c, b"operations".to_vec()),
(cn(), 0x0c, b"box".to_vec()),
]),
&[],
);
assert!(!is_foot(&other));
}
#[test]
fn an_optional_version_field_does_not_move_the_subject() {
let versioned = certificate(&name(&[(cn(), 0x0c, b"box".to_vec())]), &[der(0xa0, &[])]);
assert_eq!(common_name(&versioned).as_deref(), Some("box"));
}
#[test]
fn an_unreadable_attribute_is_skipped_and_not_guessed() {
let utf16 = certificate(&name(&[(cn(), 0x1e, vec![0xff, 0xfe])]), &[]);
assert_eq!(common_name(&utf16), None);
let valueless = der(0x30, &der(0x31, &der(0x30, &der(0x06, &cn()))));
assert_eq!(common_name(&certificate(&valueless, &[])), None);
let mistyped = der(
0x30,
&der(
0x31,
&der(0x30, &[der(0x02, &cn()), der(0x0c, b"box")].concat()),
),
);
assert_eq!(common_name(&certificate(&mistyped, &[])), None);
}
#[test]
fn bytes_that_are_not_a_certificate_name_nobody() {
let malformed: [&[u8]; 6] = [
&[0x30, 0x80, 0x01],
&[0x30, 0x85, 0x01, 0x02, 0x03, 0x04, 0x05],
&[0x30, 0x82, 0x01],
&[0x30, 0x05, 0x01],
&[0x30],
b"not DER at all",
];
for bytes in malformed {
assert_eq!(common_name(bytes), None, "{bytes:?}");
assert!(!is_foot(bytes), "{bytes:?}");
}
}
#[test]
fn a_certificate_with_no_subject_to_reach_answers_nothing() {
let short = der(
0x30,
&der(0x30, &[der(0x02, &[1]), der(0x30, &[])].concat()),
);
assert_eq!(common_name(&short), None);
let serialless = der(0x30, &der(0x30, &der(0x30, &[])));
assert_eq!(common_name(&serialless), None);
}