use std::fmt;
use std::collections::HashMap;
use rpki::ca::csr::BgpsecCsr;
use rpki::ca::idexchange::CaHandle;
use rpki::ca::publication::Base64;
use rpki::crypto::PublicKey;
use rpki::repository::cert::{
Cert, ExtendedKeyUsage, KeyUsage, Overclaim, TbsCert
};
use rpki::repository::resources::{Asn, ResourceSet};
use rpki::repository::x509::{Serial, Time};
use serde::{Deserialize, Serialize};
use crate::api::bgpsec::{
BgpSecAsnKey, BgpSecCsrInfo, BgpSecCsrInfoList, BgpSecDefinitionUpdates,
};
use crate::api::ca::ObjectName;
use crate::commons::KrillResult;
use crate::commons::error::Error;
use crate::commons::crypto::KrillSigner;
use crate::config::{Config, IssuanceTimingConfig};
use super::events::CertAuthEvent;
use super::keys::CertifiedKey;
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecDefinitions(HashMap<BgpSecAsnKey, StoredBgpSecCsr>);
impl BgpSecDefinitions {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(
&self,
) -> impl Iterator<Item = (&BgpSecAsnKey, &StoredBgpSecCsr)> {
self.0.iter()
}
pub fn create_info_list(&self) -> BgpSecCsrInfoList {
BgpSecCsrInfoList::new(
self.0
.iter()
.map(|(key, csr)| {
BgpSecCsrInfo {
asn: key.asn,
key_identifier: key.key,
csr: 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()
}
pub fn process_updates(
&self,
handle: &CaHandle,
all_resources: &ResourceSet,
updates: BgpSecDefinitionUpdates,
) -> KrillResult<(Self, Vec<CertAuthEvent>)> {
let mut events = vec![];
let mut definitions = self.clone();
for key in updates.remove {
if !definitions.remove(&key) {
return Err(Error::BgpSecDefinitionUnknown(
handle.clone(),
key,
));
} else {
events.push(CertAuthEvent::BgpSecDefinitionRemoved { key });
}
}
for definition in updates.add {
definition.csr.verify_signature().map_err(|e| {
Error::BgpSecDefinitionInvalidlySigned(
handle.clone(),
definition.clone(),
e.to_string(),
)
})?;
let key = BgpSecAsnKey::from(&definition);
let csr = StoredBgpSecCsr::from_csr(&definition.csr);
if !all_resources.contains_asn(key.asn) {
return Err(Error::BgpSecDefinitionNotEntitled(
handle.clone(),
key,
));
}
if let Some(stored_csr) = definitions.get_stored_csr(&key) {
if stored_csr != &csr {
events.push(CertAuthEvent::BgpSecDefinitionUpdated {
key,
csr: csr.clone(),
});
definitions.add_or_replace(key, csr);
}
} else {
events.push(CertAuthEvent::BgpSecDefinitionAdded {
key,
csr: csr.clone(),
});
definitions.add_or_replace(key, csr);
}
}
Ok((definitions, events))
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct StoredBgpSecCsr {
pub since: Time,
pub key: PublicKey,
pub csr: Base64,
}
impl StoredBgpSecCsr {
pub fn from_csr(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,
}
}
}
#[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 create_updates(
&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.updated.push(cert);
}
for (key, _) in self.0.iter().filter(|(k, _)| {
!definitions.has(k) || !resources.contains_asn(k.asn)
}) {
updates.removed.push(*key);
}
Ok(updates)
}
pub fn create_renewal(
&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 cert = self.make_bgpsec_cert(
cert.asn,
cert.public_key.clone(),
certified_key,
issuance_timing,
signer,
)?;
updates.updated.push(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 issuer = certified_key.incoming_cert().subject.clone();
let crl_uri = certified_key.incoming_cert().crl_uri();
let aki = certified_key.incoming_cert().key_identifier();
let aia = certified_key.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 cert = signer.sign_cert(router_cert, &certified_key.key_id())?;
Ok(BgpSecCertInfo::new(asn, cert))
}
pub fn apply_updates(&mut self, updates: BgpSecCertificateUpdates) {
for info in updates.updated {
let key = info.asn_key();
self.0.insert(key, info);
}
for key in updates.removed {
self.0.remove(&key);
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCertInfo {
pub asn: Asn,
pub public_key: PublicKey,
pub serial: Serial,
pub expires: Time,
pub 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 { asn: self.asn, key: self.public_key.key_identifier() }
}
pub fn name(&self) -> ObjectName {
ObjectName::bgpsec(self.asn, self.public_key.key_identifier())
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct BgpSecCertificateUpdates {
#[serde(skip_serializing_if = "Vec::is_empty", default)]
updated: Vec<BgpSecCertInfo>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
removed: Vec<BgpSecAsnKey>,
}
impl BgpSecCertificateUpdates {
pub fn is_empty(&self) -> bool {
self.updated.is_empty() && self.removed.is_empty()
}
pub fn updated(&self) -> &[BgpSecCertInfo] {
&self.updated
}
pub fn removed(&self) -> &[BgpSecAsnKey] {
&self.removed
}
}
impl fmt::Display for BgpSecCertificateUpdates {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.updated.is_empty() {
write!(f, " added: ")?;
for cert in &self.updated {
write!(f, "{} ", cert.name())?;
}
}
if !self.removed.is_empty() {
write!(f, " removed: ")?;
for key in &self.removed {
write!(f, "{} ", ObjectName::from(key))?;
}
}
Ok(())
}
}