use super::{OID, PUBLIC_KEY_SIZE, PublicKey};
use crate::pem;
use crate::x509::{self, ValidityCheck};
use crate::xdsa;
use der::Encode;
use x509_cert::ext::pkix::{KeyUsage, KeyUsages};
pub fn issue_cert_der(
subject: &PublicKey,
issuer: &xdsa::SecretKey,
template: &x509::Certificate,
) -> x509::Result<Vec<u8>> {
if !matches!(template.role, x509::Role::Leaf) {
return Err(x509::Error::MustBeLeaf);
}
let default_key_usage = KeyUsage(KeyUsages::KeyAgreement.into());
Ok(x509::issue_cert(
&subject.to_bytes(),
OID,
default_key_usage,
issuer,
template,
)?
.to_der()?)
}
pub fn issue_cert_pem(
subject: &PublicKey,
issuer: &xdsa::SecretKey,
template: &x509::Certificate,
) -> x509::Result<String> {
let der = issue_cert_der(subject, issuer, template)?;
Ok(pem::encode("CERTIFICATE", &der))
}
pub fn verify_cert_der(
der: &[u8],
issuer: &xdsa::PublicKey,
validity: ValidityCheck,
) -> x509::Result<x509::Verified<PublicKey>> {
let (key_bytes, cert, key_usage) = x509::verify_cert::<PUBLIC_KEY_SIZE>(der, issuer, validity)?;
if matches!(cert.role, x509::Role::Authority { .. }) {
return Err(x509::Error::MustBeLeaf);
}
if key_usage & (1 << 4) == 0 {
return Err(x509::Error::InvalidKeyUsage {
details: "xHPKE requires keyAgreement",
});
}
Ok(x509::Verified {
public_key: PublicKey::from_bytes(&key_bytes)?,
cert,
})
}
pub fn verify_cert_pem(
pem_data: &str,
issuer: &xdsa::PublicKey,
validity: ValidityCheck,
) -> x509::Result<x509::Verified<PublicKey>> {
let (tag, der) = pem::decode(pem_data.as_bytes())?;
if tag != "CERTIFICATE" {
return Err(x509::Error::External(
format!("invalid PEM tag {}", tag).into(),
));
}
verify_cert_der(&der, issuer, validity)
}
pub fn verify_cert_der_with_issuer(
der: &[u8],
issuer_cert: &x509::Verified<xdsa::PublicKey>,
validity: ValidityCheck,
) -> x509::Result<x509::Verified<PublicKey>> {
let cert = verify_cert_der(der, &issuer_cert.public_key, validity)?;
enforce_issuer_chaining(&cert, issuer_cert)?;
Ok(cert)
}
pub fn verify_cert_pem_with_issuer(
pem_data: &str,
issuer_cert: &x509::Verified<xdsa::PublicKey>,
validity: ValidityCheck,
) -> x509::Result<x509::Verified<PublicKey>> {
let cert = verify_cert_pem(pem_data, &issuer_cert.public_key, validity)?;
enforce_issuer_chaining(&cert, issuer_cert)?;
Ok(cert)
}
fn enforce_issuer_chaining(
cert: &x509::Verified<PublicKey>,
issuer_cert: &x509::Verified<xdsa::PublicKey>,
) -> x509::Result<()> {
if !matches!(issuer_cert.cert.role, x509::Role::Authority { .. }) {
return Err(x509::Error::InvalidIssuer {
details: "not a CA",
});
}
if cert.cert.issuer != issuer_cert.cert.subject {
return Err(x509::Error::InvalidIssuer {
details: "issuer DN does not match",
});
}
Ok(())
}