use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
use time::OffsetDateTime;
use crate::error::ProxyError;
const CERT_VALIDITY: time::Duration =
time::Duration::new(13 * 86_400 + 23 * 3_600 + 59 * 60 + 59, 0);
const ROTATION_OVERLAP: time::Duration = time::Duration::new(3_600, 0);
#[derive(Debug)]
pub struct GeneratedCert {
pub cert_der: CertificateDer<'static>,
pub key_der: PrivateKeyDer<'static>,
pub cert_hash: [u8; 32],
pub not_before: OffsetDateTime,
pub not_after: OffsetDateTime,
}
impl GeneratedCert {
pub fn from_der(
cert_bytes: Vec<u8>,
key_bytes: Vec<u8>,
cert_hash: [u8; 32],
not_before: OffsetDateTime,
not_after: OffsetDateTime,
) -> Self {
Self {
cert_der: CertificateDer::from(cert_bytes),
key_der: PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_bytes)),
cert_hash,
not_before,
not_after,
}
}
}
#[derive(Debug)]
pub struct CertPair {
pub current: GeneratedCert,
pub next: GeneratedCert,
}
impl CertPair {
pub fn generate(subject_alt_names: &[&str]) -> Result<Self, ProxyError> {
Self::generate_at(subject_alt_names, OffsetDateTime::now_utc())
}
pub fn generate_at(
subject_alt_names: &[&str],
now: OffsetDateTime,
) -> Result<Self, ProxyError> {
let current = generate_single(subject_alt_names, now)?;
let next_start = current.not_after - ROTATION_OVERLAP;
let next = generate_single(subject_alt_names, next_start)?;
Ok(Self { current, next })
}
pub fn cert_hashes(&self) -> [[u8; 32]; 2] {
[self.current.cert_hash, self.next.cert_hash]
}
}
fn generate_single(
subject_alt_names: &[&str],
not_before: OffsetDateTime,
) -> Result<GeneratedCert, ProxyError> {
use rcgen::{CertificateParams, KeyPair};
let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256)
.map_err(|e| ProxyError::CertGen(e.to_string()))?;
let mut params =
CertificateParams::new(subject_alt_names.iter().map(|s| s.to_string()).collect::<Vec<_>>())
.map_err(|e| ProxyError::CertGen(e.to_string()))?;
let not_after = not_before + CERT_VALIDITY;
params.not_before = not_before;
params.not_after = not_after;
let cert = params.self_signed(&key_pair).map_err(|e| ProxyError::CertGen(e.to_string()))?;
let cert_der_bytes = cert.der().to_vec();
let digest = ring::digest::digest(&ring::digest::SHA256, &cert_der_bytes);
let mut cert_hash = [0u8; 32];
cert_hash.copy_from_slice(digest.as_ref());
let cert_der = CertificateDer::from(cert_der_bytes);
let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));
Ok(GeneratedCert { cert_der, key_der, cert_hash, not_before, not_after })
}
pub fn format_hash_hex(hash: &[u8; 32]) -> String {
hash.iter().map(|b| format!("{b:02x}")).collect::<Vec<_>>().join(":")
}
pub fn compute_cert_hash(cert_der: &[u8]) -> [u8; 32] {
let digest = ring::digest::digest(&ring::digest::SHA256, cert_der);
let mut hash = [0u8; 32];
hash.copy_from_slice(digest.as_ref());
hash
}