use std::fmt::Display;
use thiserror::Error;
use valuable::Valuable;
use x509_parser::extensions::ParsedExtension;
use x509_parser::prelude::X509Certificate;
use crate::certificate::id::{CertId, DangerousComparableId, InvalidLength};
#[derive(Debug, Error)]
#[error("no Authority Key Identifier found")]
pub struct ErrorNoAKI;
#[derive(Debug, Error)]
pub enum InvalidIssuerCertId {
#[error(transparent)]
NoAKI(#[from] ErrorNoAKI),
#[error(transparent)]
InvalidLength(#[from] InvalidLength),
}
#[derive(Debug, Hash, Clone)] pub struct IssuerCertId(CertId);
impl IssuerCertId {
pub fn as_hex_str(&self) -> &str {
self.0.as_hex_str()
}
pub(super) fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
pub fn as_cert_id(&self) -> &CertId {
&self.0
}
pub fn as_dangerous_comparable(&self) -> DangerousComparableId<'_, Self> {
DangerousComparableId::from(self)
}
}
impl Display for IssuerCertId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_hex_str())
}
}
impl Valuable for IssuerCertId {
fn as_value(&self) -> valuable::Value<'_> {
self.0.as_value()
}
fn visit(&self, visit: &mut dyn valuable::Visit) {
self.0.visit(visit);
}
}
impl TryFrom<&[u8]> for IssuerCertId {
type Error = InvalidLength;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Ok(IssuerCertId(CertId::try_from(value)?))
}
}
impl TryFrom<Vec<u8>> for IssuerCertId {
type Error = InvalidLength;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Ok(IssuerCertId(CertId::try_from(value)?))
}
}
impl<'a> TryFrom<&X509Certificate<'a>> for IssuerCertId {
type Error = InvalidIssuerCertId;
fn try_from(cert: &X509Certificate<'a>) -> Result<Self, Self::Error> {
let bytes = cert
.iter_extensions()
.find_map(|v| match v.parsed_extension() {
ParsedExtension::AuthorityKeyIdentifier(aki) => {
aki.key_identifier.as_ref().map(|kid| kid.0)
}
_ => None,
})
.ok_or(ErrorNoAKI)?;
Ok(IssuerCertId(CertId::try_from(bytes)?))
}
}
#[cfg(test)]
mod tests {
use rc_x509_test_helpers::assert_valuable_repr;
use static_assertions::assert_not_impl_any;
use x509_parser::prelude::FromDer as _;
use super::*;
use crate::certificate::tests::cert_fixture;
const FIXTURE_AKI_STR: &str = "20:6c:8e:cf:e4:21:a7:ff:ed:23:c8:3d:37:0f:77:81:84:71:0e:15";
assert_not_impl_any!(IssuerCertId: PartialEq, Eq);
fn fixture_aki() -> IssuerCertId {
let der = cert_fixture().as_der();
let cert = X509Certificate::from_der(&der).expect("valid DER").1;
IssuerCertId::try_from(&cert).expect("extract AKI")
}
#[test]
fn test_fixture() {
let aki = fixture_aki();
assert_eq!(aki.as_hex_str(), FIXTURE_AKI_STR,);
assert_eq!(aki.as_cert_id().as_hex_str(), FIXTURE_AKI_STR);
}
#[test]
fn test_valuable_repr() {
let aki = fixture_aki();
assert_valuable_repr(&aki, FIXTURE_AKI_STR);
}
#[test]
fn test_danger_eq() {
let aki = fixture_aki();
assert_eq!(aki.as_dangerous_comparable(), aki);
}
}