use std::collections::HashMap;
use std::fmt;
use rpki::crypto::KeyIdentifier;
use crate::{
commons::{
api::{
ChildHandle, Handle, IssuanceRequest, IssuedCert, ObjectName, ParentCaContact, ParentHandle,
ParentResourceClassName, RcvdCert, RepositoryContact, ResourceClassName, ResourceSet, RevocationRequest,
RevokedObject, RoaAggregateKey, RtaName, TaCertDetails,
},
crypto::{IdCert, KrillSigner},
eventsourcing::StoredEvent,
KrillResult,
},
daemon::ca::{
AggregateRoaInfo, CertifiedKey, PreparedRta, PublishedRoa, Rfc8183Id, RoaInfo, RouteAuthorization, SignedRta,
},
};
pub type Ini = StoredEvent<IniDet>;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct IniDet {
id: Rfc8183Id,
}
impl IniDet {
pub fn unpack(self) -> Rfc8183Id {
self.id
}
}
impl IniDet {
pub fn new(handle: &Handle, id: Rfc8183Id) -> Ini {
Ini::new(handle, 0, IniDet { id })
}
pub fn init(handle: &Handle, signer: &KrillSigner) -> KrillResult<Ini> {
let id = Rfc8183Id::generate(signer)?;
Ok(Self::new(handle, id))
}
}
impl fmt::Display for IniDet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Initialized with ID key hash: {}", self.id.key_hash())?;
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RoaUpdates {
#[serde(
skip_serializing_if = "HashMap::is_empty",
default = "HashMap::new",
with = "updated_sorted_map"
)]
updated: HashMap<RouteAuthorization, RoaInfo>,
#[serde(
skip_serializing_if = "HashMap::is_empty",
default = "HashMap::new",
with = "removed_sorted_map"
)]
removed: HashMap<RouteAuthorization, RevokedObject>,
#[serde(
skip_serializing_if = "HashMap::is_empty",
default = "HashMap::new",
with = "aggregate_updated_sorted_map"
)]
aggregate_updated: HashMap<RoaAggregateKey, AggregateRoaInfo>,
#[serde(
skip_serializing_if = "HashMap::is_empty",
default = "HashMap::new",
with = "aggregate_removed_sorted_map"
)]
aggregate_removed: HashMap<RoaAggregateKey, RevokedObject>,
}
mod updated_sorted_map {
use super::*;
use serde::de::{Deserialize, Deserializer};
use serde::ser::Serializer;
#[derive(Debug, Deserialize)]
struct Item {
auth: RouteAuthorization,
roa: RoaInfo,
}
#[derive(Debug, Serialize)]
struct ItemRef<'a> {
auth: &'a RouteAuthorization,
roa: &'a RoaInfo,
}
pub fn serialize<S>(map: &HashMap<RouteAuthorization, RoaInfo>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut sorted_vec: Vec<ItemRef> = map.iter().map(|(auth, roa)| ItemRef { auth, roa }).collect();
sorted_vec.sort_by_key(|el| el.auth);
serializer.collect_seq(sorted_vec)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<RouteAuthorization, RoaInfo>, D::Error>
where
D: Deserializer<'de>,
{
let mut map = HashMap::new();
for item in Vec::<Item>::deserialize(deserializer)? {
map.insert(item.auth, item.roa);
}
Ok(map)
}
}
mod aggregate_updated_sorted_map {
use super::*;
use serde::de::{Deserialize, Deserializer};
use serde::ser::Serializer;
#[derive(Debug, Deserialize)]
struct Item {
agg: RoaAggregateKey,
roa: AggregateRoaInfo,
}
#[derive(Debug, Serialize)]
struct ItemRef<'a> {
agg: &'a RoaAggregateKey,
roa: &'a AggregateRoaInfo,
}
pub fn serialize<S>(map: &HashMap<RoaAggregateKey, AggregateRoaInfo>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut sorted_vec: Vec<ItemRef> = map.iter().map(|(agg, roa)| ItemRef { agg, roa }).collect();
sorted_vec.sort_by_key(|el| el.agg);
serializer.collect_seq(sorted_vec)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<RoaAggregateKey, AggregateRoaInfo>, D::Error>
where
D: Deserializer<'de>,
{
let mut map = HashMap::new();
for item in Vec::<Item>::deserialize(deserializer)? {
map.insert(item.agg, item.roa);
}
Ok(map)
}
}
mod removed_sorted_map {
use super::*;
use serde::de::{Deserialize, Deserializer};
use serde::ser::Serializer;
#[derive(Debug, Deserialize)]
struct Item {
auth: RouteAuthorization,
removed: RevokedObject,
}
#[derive(Debug, Serialize)]
struct ItemRef<'a> {
auth: &'a RouteAuthorization,
removed: &'a RevokedObject,
}
pub fn serialize<S>(map: &HashMap<RouteAuthorization, RevokedObject>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut sorted_vec: Vec<ItemRef> = map.iter().map(|(auth, removed)| ItemRef { auth, removed }).collect();
sorted_vec.sort_by_key(|el| el.auth);
serializer.collect_seq(sorted_vec)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<RouteAuthorization, RevokedObject>, D::Error>
where
D: Deserializer<'de>,
{
let mut map = HashMap::new();
for item in Vec::<Item>::deserialize(deserializer)? {
map.insert(item.auth, item.removed);
}
Ok(map)
}
}
mod aggregate_removed_sorted_map {
use super::*;
use serde::de::{Deserialize, Deserializer};
use serde::ser::Serializer;
#[derive(Debug, Deserialize)]
struct Item {
agg: RoaAggregateKey,
removed: RevokedObject,
}
#[derive(Debug, Serialize)]
struct ItemRef<'a> {
agg: &'a RoaAggregateKey,
removed: &'a RevokedObject,
}
pub fn serialize<S>(map: &HashMap<RoaAggregateKey, RevokedObject>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut sorted_vec: Vec<ItemRef> = map.iter().map(|(agg, removed)| ItemRef { agg, removed }).collect();
sorted_vec.sort_by_key(|el| el.agg);
serializer.collect_seq(sorted_vec)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<RoaAggregateKey, RevokedObject>, D::Error>
where
D: Deserializer<'de>,
{
let mut map = HashMap::new();
for item in Vec::<Item>::deserialize(deserializer)? {
map.insert(item.agg, item.removed);
}
Ok(map)
}
}
impl Default for RoaUpdates {
fn default() -> Self {
RoaUpdates {
updated: HashMap::new(),
removed: HashMap::new(),
aggregate_updated: HashMap::new(),
aggregate_removed: HashMap::new(),
}
}
}
impl RoaUpdates {
pub fn is_empty(&self) -> bool {
self.updated.is_empty()
&& self.removed.is_empty()
&& self.aggregate_updated.is_empty()
&& self.aggregate_removed.is_empty()
}
pub fn contains_changes(&self) -> bool {
!self.is_empty()
}
pub fn update(&mut self, auth: RouteAuthorization, roa: RoaInfo) {
self.updated.insert(auth, roa);
}
pub fn remove(&mut self, auth: RouteAuthorization, revoke: RevokedObject) {
self.removed.insert(auth, revoke);
}
pub fn remove_aggregate(&mut self, key: RoaAggregateKey, revoke: RevokedObject) {
self.aggregate_removed.insert(key, revoke);
}
pub fn update_aggregate(&mut self, key: RoaAggregateKey, aggregate: AggregateRoaInfo) {
self.aggregate_updated.insert(key, aggregate);
}
pub fn added_roas(&self) -> KrillResult<HashMap<ObjectName, PublishedRoa>> {
let mut res = HashMap::new();
for (auth, simple) in &self.updated {
let roa = simple.roa().clone();
let name = ObjectName::from(auth);
res.insert(name, PublishedRoa::new(roa));
}
for (agg_key, agg_info) in &self.aggregate_updated {
let roa = agg_info.roa_info().roa().clone();
let name = ObjectName::from(agg_key);
res.insert(name, PublishedRoa::new(roa));
}
Ok(res)
}
pub fn removed_roas(&self) -> Vec<ObjectName> {
let mut res = vec![];
for simple in self.removed.keys() {
res.push(ObjectName::from(simple))
}
for agg in self.aggregate_removed.keys() {
res.push(ObjectName::from(agg))
}
res
}
#[allow(clippy::type_complexity)]
pub fn unpack(
self,
) -> (
HashMap<RouteAuthorization, RoaInfo>,
HashMap<RouteAuthorization, RevokedObject>,
HashMap<RoaAggregateKey, AggregateRoaInfo>,
HashMap<RoaAggregateKey, RevokedObject>,
) {
(
self.updated,
self.removed,
self.aggregate_updated,
self.aggregate_removed,
)
}
}
impl fmt::Display for RoaUpdates {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.updated.is_empty() {
write!(f, "Added single VRP ROAs: ")?;
for roa in self.updated.keys() {
write!(f, "{} ", ObjectName::from(roa))?;
}
}
if !self.removed.is_empty() {
write!(f, "Removed single VRP ROAs: ")?;
for roa in self.removed.keys() {
write!(f, "{} ", ObjectName::from(roa))?;
}
}
if !self.aggregate_updated.is_empty() {
write!(f, "Added ASN aggregated ROAs: ")?;
for roa in self.aggregate_updated.keys() {
write!(f, "{} ", ObjectName::from(roa))?;
}
}
if !self.aggregate_removed.is_empty() {
write!(f, "Removed ASN aggregated ROAs: ")?;
for roa in self.aggregate_removed.keys() {
write!(f, "{} ", ObjectName::from(roa))?;
}
}
Ok(())
}
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct ChildCertificateUpdates {
issued: Vec<IssuedCert>,
removed: Vec<KeyIdentifier>,
}
impl ChildCertificateUpdates {
pub fn new(issued: Vec<IssuedCert>, removed: Vec<KeyIdentifier>) -> Self {
ChildCertificateUpdates { issued, removed }
}
pub fn is_empty(&self) -> bool {
self.issued.is_empty() && self.removed.is_empty()
}
pub fn issue(&mut self, new: IssuedCert) {
self.issued.push(new);
}
pub fn remove(&mut self, ki: KeyIdentifier) {
self.removed.push(ki);
}
pub fn issued(&self) -> &Vec<IssuedCert> {
&self.issued
}
pub fn removed(&self) -> &Vec<KeyIdentifier> {
&self.removed
}
pub fn unpack(self) -> (Vec<IssuedCert>, Vec<KeyIdentifier>) {
(self.issued, self.removed)
}
}
pub type CaEvt = StoredEvent<CaEvtDet>;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[allow(clippy::large_enum_variant)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
pub enum CaEvtDet {
TrustAnchorMade {
ta_cert_details: TaCertDetails,
},
ChildAdded {
child: ChildHandle,
id_cert: IdCert,
resources: ResourceSet,
},
ChildCertificateIssued {
child: ChildHandle,
resource_class_name: ResourceClassName,
ki: KeyIdentifier,
},
ChildKeyRevoked {
child: ChildHandle,
resource_class_name: ResourceClassName,
ki: KeyIdentifier,
},
ChildCertificatesUpdated {
resource_class_name: ResourceClassName,
updates: ChildCertificateUpdates,
},
ChildUpdatedIdCert {
child: ChildHandle,
id_cert: IdCert,
},
ChildUpdatedResources {
child: ChildHandle,
resources: ResourceSet,
},
ChildRemoved {
child: ChildHandle,
},
IdUpdated {
id: Rfc8183Id,
},
ParentAdded {
parent: ParentHandle,
contact: ParentCaContact,
},
ParentUpdated {
parent: ParentHandle,
contact: ParentCaContact,
},
ParentRemoved {
parent: ParentHandle,
},
ResourceClassAdded {
resource_class_name: ResourceClassName,
parent: ParentHandle,
parent_resource_class_name: ParentResourceClassName,
pending_key: KeyIdentifier,
},
ResourceClassRemoved {
resource_class_name: ResourceClassName,
parent: ParentHandle,
revoke_requests: Vec<RevocationRequest>,
},
CertificateRequested {
resource_class_name: ResourceClassName,
req: IssuanceRequest,
ki: KeyIdentifier, },
CertificateReceived {
resource_class_name: ResourceClassName,
rcvd_cert: RcvdCert,
ki: KeyIdentifier, },
KeyRollPendingKeyAdded {
resource_class_name: ResourceClassName,
pending_key_id: KeyIdentifier,
},
KeyPendingToNew {
resource_class_name: ResourceClassName,
new_key: CertifiedKey, },
KeyPendingToActive {
resource_class_name: ResourceClassName,
current_key: CertifiedKey, },
KeyRollActivated {
resource_class_name: ResourceClassName,
revoke_req: RevocationRequest,
},
KeyRollFinished {
resource_class_name: ResourceClassName,
},
UnexpectedKeyFound {
resource_class_name: ResourceClassName,
revoke_req: RevocationRequest,
},
RouteAuthorizationAdded {
auth: RouteAuthorization,
},
RouteAuthorizationRemoved {
auth: RouteAuthorization,
},
RoasUpdated {
resource_class_name: ResourceClassName,
updates: RoaUpdates,
},
RepoUpdated {
contact: RepositoryContact,
},
RtaSigned {
name: RtaName,
rta: SignedRta,
},
RtaPrepared {
name: RtaName,
prepared: PreparedRta,
},
}
impl CaEvtDet {
pub(super) fn id_updated(handle: &Handle, version: u64, id: Rfc8183Id) -> CaEvt {
StoredEvent::new(handle, version, CaEvtDet::IdUpdated { id })
}
pub(super) fn parent_added(handle: &Handle, version: u64, parent: ParentHandle, contact: ParentCaContact) -> CaEvt {
StoredEvent::new(handle, version, CaEvtDet::ParentAdded { parent, contact })
}
pub(super) fn parent_updated(
handle: &Handle,
version: u64,
parent: ParentHandle,
contact: ParentCaContact,
) -> CaEvt {
StoredEvent::new(handle, version, CaEvtDet::ParentUpdated { parent, contact })
}
pub(super) fn child_added(
handle: &Handle,
version: u64,
child: ChildHandle,
id_cert: IdCert,
resources: ResourceSet,
) -> CaEvt {
StoredEvent::new(
handle,
version,
CaEvtDet::ChildAdded {
child,
id_cert,
resources,
},
)
}
pub(super) fn child_updated_cert(handle: &Handle, version: u64, child: ChildHandle, id_cert: IdCert) -> CaEvt {
StoredEvent::new(handle, version, CaEvtDet::ChildUpdatedIdCert { child, id_cert })
}
pub(super) fn child_updated_resources(
handle: &Handle,
version: u64,
child: ChildHandle,
resources: ResourceSet,
) -> CaEvt {
StoredEvent::new(handle, version, CaEvtDet::ChildUpdatedResources { child, resources })
}
pub(super) fn child_certificate_issued(
handle: &Handle,
version: u64,
child: ChildHandle,
resource_class_name: ResourceClassName,
ki: KeyIdentifier,
) -> CaEvt {
StoredEvent::new(
handle,
version,
CaEvtDet::ChildCertificateIssued {
child,
resource_class_name,
ki,
},
)
}
pub(super) fn child_revoke_key(
handle: &Handle,
version: u64,
child: ChildHandle,
resource_class_name: ResourceClassName,
ki: KeyIdentifier,
) -> CaEvt {
StoredEvent::new(
handle,
version,
CaEvtDet::ChildKeyRevoked {
child,
resource_class_name,
ki,
},
)
}
pub(super) fn child_certificates_updated(
handle: &Handle,
version: u64,
resource_class_name: ResourceClassName,
updates: ChildCertificateUpdates,
) -> CaEvt {
StoredEvent::new(
handle,
version,
CaEvtDet::ChildCertificatesUpdated {
resource_class_name,
updates,
},
)
}
pub(super) fn child_removed(handle: &Handle, version: u64, child: ChildHandle) -> CaEvt {
StoredEvent::new(handle, version, CaEvtDet::ChildRemoved { child })
}
}
impl fmt::Display for CaEvtDet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CaEvtDet::TrustAnchorMade { ta_cert_details } => write!(
f,
"turn into TA with key (hash) {}",
ta_cert_details.cert().subject_key_identifier()
),
CaEvtDet::ChildAdded {
child,
id_cert,
resources,
} => {
write!(
f,
"added child '{}' with resources '{}, id (hash): {}",
child,
resources,
id_cert.ski_hex()
)
}
CaEvtDet::ChildCertificateIssued {
child,
resource_class_name,
ki,
} => write!(
f,
"issued certificate to child '{}' for class '{}' and pub key '{}'",
child, resource_class_name, ki
),
CaEvtDet::ChildCertificatesUpdated {
resource_class_name,
updates,
} => {
write!(
f,
"updated child certificates in resource class {}",
resource_class_name
)?;
let issued = updates.issued();
if !issued.is_empty() {
write!(f, " (re-)issued keys: ")?;
for iss in issued {
write!(f, " {}", iss.subject_key_identifier())?;
}
}
let revoked = updates.removed();
if !revoked.is_empty() {
write!(f, " revoked keys: ")?;
for rev in revoked {
write!(f, " {}", rev)?;
}
}
Ok(())
}
CaEvtDet::ChildKeyRevoked {
child,
resource_class_name,
ki,
} => write!(
f,
"revoked certificate for child '{}' in resource class '{}' with key(hash) '{}'",
child, resource_class_name, ki
),
CaEvtDet::ChildUpdatedIdCert { child, id_cert } => {
write!(f, "updated child '{}' id (hash) '{}'", child, id_cert.ski_hex())
}
CaEvtDet::ChildUpdatedResources { child, resources } => {
write!(f, "updated child '{}' resources to '{}'", child, resources)
}
CaEvtDet::ChildRemoved { child } => write!(f, "removed child '{}'", child),
CaEvtDet::IdUpdated { id } => write!(f, "updated RFC8183 id to key '{}'", id.key_hash()),
CaEvtDet::ParentAdded { parent, contact } => {
let contact_str = match contact {
ParentCaContact::Ta(_) => "TA proxy",
ParentCaContact::Rfc6492(_) => "RFC6492",
};
write!(f, "added {} parent '{}' ", contact_str, parent)
}
CaEvtDet::ParentUpdated { parent, contact } => {
let contact_str = match contact {
ParentCaContact::Ta(_) => "TA proxy",
ParentCaContact::Rfc6492(_) => "RFC6492",
};
write!(f, "updated parent '{}' contact to '{}' ", parent, contact_str)
}
CaEvtDet::ParentRemoved { parent } => write!(f, "removed parent '{}'", parent),
CaEvtDet::ResourceClassAdded {
resource_class_name, ..
} => write!(f, "added resource class with name '{}'", resource_class_name),
CaEvtDet::ResourceClassRemoved {
resource_class_name,
parent,
..
} => write!(
f,
"removed resource class with name '{}' under parent '{}'",
resource_class_name, parent
),
CaEvtDet::CertificateRequested {
resource_class_name,
ki,
..
} => write!(
f,
"requested certificate for key (hash) '{}' under resource class '{}'",
ki, resource_class_name
),
CaEvtDet::CertificateReceived {
resource_class_name,
ki,
..
} => write!(
f,
"received certificate for key (hash) '{}' under resource class '{}'",
ki, resource_class_name
),
CaEvtDet::KeyRollPendingKeyAdded {
resource_class_name,
pending_key_id,
} => {
write!(
f,
"key roll: added pending key '{}' under resource class '{}'",
pending_key_id, resource_class_name
)
}
CaEvtDet::KeyPendingToNew {
resource_class_name,
new_key,
} => write!(
f,
"key roll: moving pending key '{}' to new state under resource class '{}'",
new_key.key_id(),
resource_class_name
),
CaEvtDet::KeyPendingToActive {
resource_class_name,
current_key,
} => write!(
f,
"activating pending key '{}' under resource class '{}'",
current_key.key_id(),
resource_class_name
),
CaEvtDet::KeyRollActivated {
resource_class_name,
revoke_req,
} => write!(
f,
"key roll: activated new key, requested revocation of '{}' under resource class '{}'",
revoke_req.key(),
resource_class_name
),
CaEvtDet::KeyRollFinished { resource_class_name } => {
write!(f, "key roll: finished for resource class '{}'", resource_class_name)
}
CaEvtDet::UnexpectedKeyFound {
resource_class_name,
revoke_req,
} => write!(
f,
"Found unexpected key in resource class '{}', will try to revoke key id: '{}'",
resource_class_name,
revoke_req.key()
),
CaEvtDet::RouteAuthorizationAdded { auth } => write!(f, "added ROA: '{}'", auth),
CaEvtDet::RouteAuthorizationRemoved { auth } => write!(f, "removed ROA: '{}'", auth),
CaEvtDet::RoasUpdated {
resource_class_name,
updates,
} => {
write!(f, "updated ROAs under resource class '{}'", resource_class_name)?;
if !updates.updated.is_empty() {
write!(f, " added: ")?;
for auth in updates.updated.keys() {
write!(f, "{} ", auth)?;
}
}
if !updates.removed.is_empty() {
write!(f, " removed: ")?;
for auth in updates.removed.keys() {
write!(f, "{} ", auth)?;
}
}
Ok(())
}
CaEvtDet::RepoUpdated { contact } => {
write!(f, "updated repository to remote server: {}", contact.service_uri())
}
CaEvtDet::RtaPrepared { name, prepared } => {
write!(f, "Prepared RTA '{}' for resources: {}", name, prepared.resources())
}
CaEvtDet::RtaSigned { name, rta } => {
write!(f, "Signed RTA '{}' for resources: {}", name, rta.resources())
}
}
}
}