mod validations;
use crate::cert::error::{CertificateError, PrivateKeyError};
use crate::cert::parsing::to_certificate_vec;
use crate::cert::{Certificate, PrivateKey};
use crate::spiffe_id::SpiffeId;
use crate::svid::x509::validations::{validate_leaf_certificate, validate_signing_certificates};
use std::convert::TryFrom as _;
use std::sync::Arc;
use self::chain::CertificateChain;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct X509Svid {
spiffe_id: SpiffeId,
cert_chain: CertificateChain,
private_key: PrivateKey,
expiry_unix: i64,
hint: Option<Arc<str>>,
}
#[derive(Debug, thiserror::Error, PartialEq)]
#[non_exhaustive]
pub enum X509SvidError {
#[error("no certificates found in chain")]
EmptyChain,
#[error("leaf certificate must not have CA flag set to true")]
LeafCertificateHasCaFlag,
#[error("leaf certificate must not have 'cRLSign' set as key usage")]
LeafCertificateHasCrlSign,
#[error("leaf certificate must not have 'keyCertSign' set as key usage")]
LeafCertificateHasKeyCertSign,
#[error("leaf certificate must have 'digitalSignature' set as key usage")]
LeafCertificateMissingDigitalSignature,
#[error("signing certificate must have CA flag set to true")]
SigningCertificateMissingCaFlag,
#[error("signing certificate must have 'keyCertSign' set as key usage")]
SigningCertificateMissingKeyCertSign,
#[error("{extension} extension is present but could not be parsed")]
UnparseableExtension {
extension: &'static str,
},
#[error(transparent)]
Certificate(#[from] CertificateError),
#[error("leaf certificate SPIFFE ID must have a non-root path component")]
LeafSpiffeIdMissingPath,
#[error(transparent)]
PrivateKey(#[from] PrivateKeyError),
}
impl X509Svid {
pub fn parse_from_der(
cert_chain_der: &[u8],
private_key_der: &[u8],
) -> Result<Self, X509SvidError> {
Self::parse_from_der_with_hint(cert_chain_der, private_key_der, None)
}
pub fn parse_from_der_with_hint(
cert_chain_der: &[u8],
private_key_der: &[u8],
hint: Option<Arc<str>>,
) -> Result<Self, X509SvidError> {
let cert_chain = CertificateChain::try_from_vec(to_certificate_vec(cert_chain_der)?)?;
let (spiffe_id, expiry_unix) = validate_leaf_certificate(cert_chain.leaf())?;
validate_signing_certificates(cert_chain.intermediates())?;
let private_key = PrivateKey::try_from(private_key_der)?;
Ok(Self {
spiffe_id,
cert_chain,
private_key,
expiry_unix,
hint,
})
}
pub const fn spiffe_id(&self) -> &SpiffeId {
&self.spiffe_id
}
pub fn cert_chain(&self) -> &[Certificate] {
self.cert_chain.as_slice()
}
pub const fn leaf(&self) -> &Certificate {
self.cert_chain.leaf()
}
pub const fn private_key(&self) -> &PrivateKey {
&self.private_key
}
#[cfg(feature = "x509-source")]
pub(crate) const fn expiry_unix(&self) -> i64 {
self.expiry_unix
}
pub fn hint(&self) -> Option<&str> {
self.hint.as_deref()
}
}
mod chain {
use crate::cert::Certificate;
use crate::svid::x509::X509SvidError;
#[derive(Debug, Clone, Eq, PartialEq)]
pub(super) struct CertificateChain {
leaf: Certificate,
certs: Vec<Certificate>,
}
impl CertificateChain {
pub(super) fn try_from_vec(certs: Vec<Certificate>) -> Result<Self, X509SvidError> {
let Some(leaf) = certs.first().cloned() else {
return Err(X509SvidError::EmptyChain);
};
Ok(Self { leaf, certs })
}
pub(super) const fn leaf(&self) -> &Certificate {
&self.leaf
}
pub(super) fn intermediates(&self) -> &[Certificate] {
match self.certs.split_first() {
Some((_, intermediates)) => intermediates,
None => &[],
}
}
pub(super) fn as_slice(&self) -> &[Certificate] {
&self.certs
}
}
}