use std::collections::HashMap;
use rpki::{
ca::{csr::BgpsecCsr, publication::Base64},
crypto::PublicKey,
repository::{
cert::{ExtendedKeyUsage, KeyUsage, Overclaim, TbsCert},
resources::Asn,
x509::{Serial, Time},
Cert,
},
};
use crate::{
commons::{
api::{BgpSecAsnKey, BgpSecCsrInfo, BgpSecCsrInfoList, ObjectName},
crypto::KrillSigner,
KrillResult,
},
daemon::config::{Config, IssuanceTimingConfig},
};
use super::{BgpSecCertificateUpdates, CertifiedKey};
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCertificates(HashMap<BgpSecAsnKey, BgpSecCertInfo>);
impl BgpSecCertificates {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn update(
&self,
definitions: &BgpSecDefinitions,
certified_key: &CertifiedKey,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<BgpSecCertificateUpdates> {
let mut updates = BgpSecCertificateUpdates::default();
let resources = certified_key.incoming_cert().resources();
let issuance_timing = &config.issuance_timing;
for (key, csr) in definitions
.iter()
.filter(|(k, _)| !self.0.contains_key(k) && resources.contains_asn(k.asn()))
{
let cert = self.make_bgpsec_cert(key.asn(), csr.key().clone(), certified_key, issuance_timing, signer)?;
updates.add_updated(cert);
}
for (key, _) in self
.0
.iter()
.filter(|(k, _)| !definitions.has(k) || !resources.contains_asn(k.asn()))
{
updates.add_removed(*key);
}
Ok(updates)
}
pub fn renew(
&self,
certified_key: &CertifiedKey,
renew_threshold: Option<Time>,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<BgpSecCertificateUpdates> {
let mut updates = BgpSecCertificateUpdates::default();
for cert in self.0.values().filter(|cert| {
renew_threshold
.map(|threshold| cert.expires() < threshold) .unwrap_or(true) }) {
let asn = cert.asn();
let public_key = cert.public_key().clone();
let cert = self.make_bgpsec_cert(asn, public_key, certified_key, issuance_timing, signer)?;
updates.add_updated(cert);
}
Ok(updates)
}
fn make_bgpsec_cert(
&self,
asn: Asn,
public_key: PublicKey,
certified_key: &CertifiedKey,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<BgpSecCertInfo> {
let serial_number = signer.random_serial()?;
let incoming_cert = certified_key.incoming_cert();
let issuer = incoming_cert.subject().clone();
let crl_uri = incoming_cert.crl_uri();
let aki = incoming_cert.key_identifier();
let aia = incoming_cert.uri().clone();
let subject = None;
let mut router_cert = TbsCert::new(
serial_number,
issuer,
issuance_timing.new_bgpsec_validity(),
subject,
public_key,
KeyUsage::Ee,
Overclaim::Refuse,
);
router_cert.set_extended_key_usage(Some(ExtendedKeyUsage::create_router()));
router_cert.set_authority_key_identifier(Some(aki));
router_cert.set_ca_issuer(Some(aia));
router_cert.set_crl_uri(Some(crl_uri));
router_cert.build_as_resource_blocks(|b| b.push(asn));
let signing_key = certified_key.key_id();
let cert = signer.sign_cert(router_cert, signing_key)?;
Ok(BgpSecCertInfo::new(asn, cert))
}
pub fn updated(&mut self, updates: BgpSecCertificateUpdates) {
let (updated, removed) = updates.unpack();
for info in updated {
let key = info.asn_key();
self.0.insert(key, info);
}
for key in removed {
self.0.remove(&key);
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCertInfo {
asn: Asn,
public_key: PublicKey,
serial: Serial,
expires: Time,
base64: Base64,
}
impl BgpSecCertInfo {
fn new(asn: Asn, cert: Cert) -> Self {
let public_key = cert.subject_public_key_info().clone();
let serial = cert.serial_number();
let expires = cert.validity().not_after();
let base64 = Base64::from(&cert);
BgpSecCertInfo {
asn,
public_key,
serial,
expires,
base64,
}
}
pub fn asn_key(&self) -> BgpSecAsnKey {
BgpSecAsnKey::new(self.asn, self.public_key.key_identifier())
}
pub fn asn(&self) -> Asn {
self.asn
}
pub fn public_key(&self) -> &PublicKey {
&self.public_key
}
pub fn serial(&self) -> Serial {
self.serial
}
pub fn expires(&self) -> Time {
self.expires
}
pub fn base64(&self) -> &Base64 {
&self.base64
}
pub fn name(&self) -> ObjectName {
ObjectName::bgpsec(self.asn, self.public_key.key_identifier())
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecDefinitions(HashMap<BgpSecAsnKey, StoredBgpSecCsr>);
impl BgpSecDefinitions {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&BgpSecAsnKey, &StoredBgpSecCsr)> {
self.0.iter()
}
pub fn info_list(&self) -> BgpSecCsrInfoList {
BgpSecCsrInfoList::new(
self.0
.iter()
.map(|(key, csr)| BgpSecCsrInfo::new(key.asn(), key.key_identifier(), csr.csr().clone()))
.collect(),
)
}
pub fn get_stored_csr(&self, key: &BgpSecAsnKey) -> Option<&StoredBgpSecCsr> {
self.0.get(key)
}
pub fn has(&self, key: &BgpSecAsnKey) -> bool {
self.0.contains_key(key)
}
pub fn add_or_replace(&mut self, key: BgpSecAsnKey, csr: StoredBgpSecCsr) {
self.0.insert(key, csr);
}
pub fn remove(&mut self, key: &BgpSecAsnKey) -> bool {
self.0.remove(key).is_some()
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct StoredBgpSecCsr {
since: Time,
key: PublicKey,
csr: Base64,
}
impl StoredBgpSecCsr {
pub fn key(&self) -> &PublicKey {
&self.key
}
pub fn csr(&self) -> &Base64 {
&self.csr
}
}
impl From<&BgpsecCsr> for StoredBgpSecCsr {
fn from(csr: &BgpsecCsr) -> Self {
let since = Time::now();
let key = csr.public_key().clone();
let binary = Base64::from_content(csr.to_captured().as_slice());
StoredBgpSecCsr {
since,
key,
csr: binary,
}
}
}