use std::{collections::HashMap, fmt::Debug};
use rpki::{
ca::publication::Base64,
repository::{
aspa::{Aspa, AspaBuilder},
sigobj::SignedObjectBuilder,
x509::{Serial, Time, Validity},
},
rrdp::Hash,
uri,
};
use crate::{
commons::{
api::{AspaCustomer, AspaDefinition, AspaProvidersUpdate, ObjectName},
crypto::KrillSigner,
error::Error,
KrillResult,
},
daemon::{
ca::{AspaObjectsUpdates, CertifiedKey},
config::{Config, IssuanceTimingConfig},
},
};
pub fn make_aspa_object(
aspa_def: AspaDefinition,
certified_key: &CertifiedKey,
validity: Validity,
signer: &KrillSigner,
) -> KrillResult<Aspa> {
let name = ObjectName::from(&aspa_def);
let aspa_builder = {
let (customer_as, providers) = aspa_def.unpack();
AspaBuilder::new(customer_as, providers).map_err(|e| Error::Custom(format!("Cannot use aspa config: {}", e)))
}?;
let object_builder = {
let incoming_cert = certified_key.incoming_cert();
let crl_uri = incoming_cert.crl_uri();
let aspa_uri = incoming_cert.uri_for_name(&name);
let ca_issuer = incoming_cert.uri().clone();
let mut object_builder =
SignedObjectBuilder::new(signer.random_serial()?, validity, crl_uri, ca_issuer, aspa_uri);
object_builder.set_issuer(Some(incoming_cert.subject().clone()));
object_builder.set_signing_time(Some(Time::now()));
object_builder
};
Ok(signer.sign_aspa(aspa_builder, object_builder, certified_key.key_id())?)
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct AspaDefinitions {
attestations: HashMap<AspaCustomer, AspaDefinition>,
}
impl AspaDefinitions {
pub fn add_or_replace(&mut self, aspa_def: AspaDefinition) {
let customer = aspa_def.customer();
self.attestations.insert(customer, aspa_def);
}
pub fn remove(&mut self, customer: AspaCustomer) {
self.attestations.remove(&customer);
}
pub fn apply_update(&mut self, customer: AspaCustomer, update: &AspaProvidersUpdate) {
let current = self.attestations.get_mut(&customer).unwrap();
current.apply_update(update);
}
pub fn all(&self) -> impl Iterator<Item = &AspaDefinition> {
self.attestations.values()
}
}
impl AspaDefinitions {
pub fn get(&self, customer: AspaCustomer) -> Option<&AspaDefinition> {
self.attestations.get(&customer)
}
pub fn has(&self, customer: AspaCustomer) -> bool {
self.attestations.contains_key(&customer)
}
pub fn len(&self) -> usize {
self.attestations.len()
}
pub fn is_empty(&self) -> bool {
self.attestations.is_empty()
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct AspaObjects(HashMap<AspaCustomer, AspaInfo>);
impl AspaObjects {
pub fn make_aspa(
&self,
aspa_def: AspaDefinition,
certified_key: &CertifiedKey,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<AspaInfo> {
let aspa = make_aspa_object(
aspa_def.clone(),
certified_key,
issuance_timing.new_aspa_validity(),
signer,
)?;
Ok(AspaInfo::new_aspa(aspa_def, aspa))
}
pub fn update(
&self,
all_aspa_defs: &AspaDefinitions,
certified_key: &CertifiedKey,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<AspaObjectsUpdates> {
let mut object_updates = AspaObjectsUpdates::default();
let resources = certified_key.incoming_cert().resources();
for relevant_aspa in all_aspa_defs
.all()
.filter(|aspa| resources.contains_asn(aspa.customer()))
{
let need_to_issue = self
.0
.get(&relevant_aspa.customer())
.map(|existing| existing.definition() != relevant_aspa)
.unwrap_or(true);
if need_to_issue {
let aspa_info =
self.make_aspa(relevant_aspa.clone(), certified_key, &config.issuance_timing, signer)?;
object_updates.add_updated(aspa_info);
}
}
for customer in self.0.keys() {
if !all_aspa_defs.has(*customer) || !resources.contains_asn(*customer) {
object_updates.add_removed(*customer);
}
}
Ok(object_updates)
}
pub fn renew(
&self,
certified_key: &CertifiedKey,
renew_threshold: Option<Time>,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<AspaObjectsUpdates> {
let mut updates = AspaObjectsUpdates::default();
for aspa in self.0.values() {
let renew = renew_threshold
.map(|threshold| aspa.expires() < threshold)
.unwrap_or(true);
if renew {
let aspa_definition = aspa.definition().clone();
let new_aspa = self.make_aspa(aspa_definition, certified_key, issuance_timing, signer)?;
updates.add_updated(new_aspa);
}
}
Ok(updates)
}
pub fn updated(&mut self, updates: AspaObjectsUpdates) {
let (updated, removed) = updates.unpack();
for aspa_info in updated {
let customer = aspa_info.customer();
self.0.insert(customer, aspa_info);
}
for customer in removed {
self.0.remove(&customer);
}
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct AspaInfo {
definition: AspaDefinition,
validity: Validity,
serial: Serial,
uri: uri::Rsync,
base64: Base64,
hash: Hash,
}
impl AspaInfo {
pub fn new(definition: AspaDefinition, aspa: Aspa) -> Self {
let validity = aspa.cert().validity();
let serial = aspa.cert().serial_number();
let uri = aspa.cert().signed_object().unwrap().clone(); let base64 = Base64::from(&aspa);
let hash = base64.to_hash();
AspaInfo {
definition,
validity,
serial,
uri,
base64,
hash,
}
}
pub fn new_aspa(definition: AspaDefinition, aspa: Aspa) -> Self {
AspaInfo::new(definition, aspa)
}
pub fn definition(&self) -> &AspaDefinition {
&self.definition
}
pub fn customer(&self) -> AspaCustomer {
self.definition.customer()
}
pub fn expires(&self) -> Time {
self.validity.not_after()
}
pub fn serial(&self) -> Serial {
self.serial
}
pub fn uri(&self) -> &uri::Rsync {
&self.uri
}
pub fn base64(&self) -> &Base64 {
&self.base64
}
pub fn hash(&self) -> Hash {
self.hash
}
}