pub(crate) const TAG_UTCTIME: u8 = 0x17;
pub(crate) const TAG_GENERALIZEDTIME: u8 = 0x18;
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct DerTime {
pub tag: u8,
pub value: String,
}
impl DerTime {
pub fn is_utc_time(&self) -> bool {
self.tag == TAG_UTCTIME
}
}
pub(crate) fn der_times(der: &[u8]) -> Vec<DerTime> {
let mut out = Vec::new();
for i in 0..der.len().saturating_sub(2) {
let tag = der[i];
let expected_len = match tag {
TAG_UTCTIME => 13,
TAG_GENERALIZEDTIME => 15,
_ => continue,
};
if der[i + 1] as usize != expected_len {
continue;
}
let Some(payload) = der.get(i + 2..i + 2 + expected_len) else {
continue;
};
let (digits, zulu) = payload.split_at(expected_len - 1);
if zulu != b"Z" || !digits.iter().all(|b| b.is_ascii_digit()) {
continue;
}
out.push(DerTime {
tag,
value: String::from_utf8_lossy(payload).into_owned(),
});
}
out
}
pub(crate) fn certificate_validity(der: &[u8]) -> (DerTime, DerTime) {
for i in 0..der.len().saturating_sub(2) {
if der[i] != 0x30 {
continue;
}
let len = der[i + 1] as usize;
let Some(body) = der.get(i + 2..i + 2 + len) else {
continue;
};
let times = der_times(body);
let consumed: usize = times
.iter()
.map(|t| 2 + if t.is_utc_time() { 13 } else { 15 })
.sum();
if times.len() == 2 && consumed == body.len() {
let mut it = times.into_iter();
return (it.next().unwrap(), it.next().unwrap());
}
}
panic!("no validity sequence found in certificate DER");
}