use rcgen::{
BasicConstraints, CertificateParams, CidrSubnet, DistinguishedName, DnType,
ExtendedKeyUsagePurpose, GeneralSubtree, IsCa, Issuer, KeyPair, KeyUsagePurpose,
NameConstraints, PKCS_ECDSA_P256_SHA256, SanType,
};
use zeroize::Zeroizing;
use super::TlsError;
use super::date::Day;
const CA_NAME: &str = "OpenMoat proxy CA";
const ORGANISATION: &str = "OpenMoat";
pub(super) const CA_DAYS: i64 = 365;
pub(super) struct NewCa {
pub(super) cert: String,
pub(super) key: Zeroizing<String>,
}
fn subject() -> DistinguishedName {
let mut name = DistinguishedName::new();
name.push(DnType::CommonName, CA_NAME);
name.push(DnType::OrganizationName, ORGANISATION);
name
}
fn ca_params() -> CertificateParams {
let mut params = CertificateParams::default();
params.distinguished_name = subject();
params.is_ca = IsCa::Ca(BasicConstraints::Constrained(0));
params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
params
}
pub(super) fn generate(hosts: &[String], today: Day) -> Result<NewCa, TlsError> {
if hosts.is_empty() {
return Err(TlsError::NoHosts);
}
let key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).map_err(|e| TlsError::rcgen(&e))?;
let mut params = ca_params();
valid(&mut params, today.add(-1)?, today.add(CA_DAYS)?)?;
params.name_constraints = Some(NameConstraints {
permitted_subtrees: hosts.iter().cloned().map(GeneralSubtree::DnsName).collect(),
excluded_subtrees: vec![
GeneralSubtree::IpAddress(CidrSubnet::V4([0; 4], [0; 4])),
GeneralSubtree::IpAddress(CidrSubnet::V6([0; 16], [0; 16])),
],
});
let cert = params.self_signed(&key).map_err(|e| TlsError::rcgen(&e))?;
Ok(NewCa {
cert: cert.pem(),
key: Zeroizing::new(key.serialize_pem()),
})
}
pub(super) struct Signer {
issuer: Issuer<'static, KeyPair>,
}
impl Signer {
pub(super) fn new(key_pem: &str) -> Result<Self, TlsError> {
let key = KeyPair::from_pem(key_pem).map_err(|_| TlsError::Key)?;
if !key.is_compatible(&PKCS_ECDSA_P256_SHA256) {
return Err(TlsError::Key);
}
Ok(Self {
issuer: Issuer::new(ca_params(), key),
})
}
pub(super) fn leaf(
&self,
host: &str,
today: Day,
) -> Result<(Vec<u8>, Zeroizing<Vec<u8>>), TlsError> {
let key =
KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).map_err(|e| TlsError::rcgen(&e))?;
let mut params = CertificateParams::default();
let mut name = DistinguishedName::new();
name.push(DnType::CommonName, host);
params.distinguished_name = name;
let dns = host
.try_into()
.map_err(|_| TlsError::Host(host.to_owned()))?;
params.subject_alt_names = vec![SanType::DnsName(dns)];
params.key_usages = vec![KeyUsagePurpose::DigitalSignature];
params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth];
params.use_authority_key_identifier_extension = true;
valid(&mut params, today.add(-1)?, today.add(2)?)?;
let cert = params
.signed_by(&key, &self.issuer)
.map_err(|e| TlsError::rcgen(&e))?;
Ok((cert.der().to_vec(), Zeroizing::new(key.serialize_der())))
}
}
fn valid(params: &mut CertificateParams, from: Day, to: Day) -> Result<(), TlsError> {
let (y, m, d) = from.ymd()?;
params.not_before = rcgen::date_time_ymd(y, m, d);
let (y, m, d) = to.ymd()?;
params.not_after = rcgen::date_time_ymd(y, m, d);
Ok(())
}