use chrono::Duration;
use serde::{Deserialize, Serialize};
use rpki::cert::Cert;
use rpki::crypto::KeyIdentifier;
use rpki::x509::{Time, Validity};
use crate::{
commons::{
api::{
EntitlementClass, Handle, HexEncodedHash, IssuanceRequest, IssuedCert, ParentHandle, RcvdCert,
ReplacedObject, RepoInfo, RequestResourceLimit, ResourceClassInfo, ResourceClassName, ResourceSet,
Revocation, RevocationRequest,
},
crypto::{CsrInfo, KrillSigner, SignSupport},
error::Error,
KrillResult,
},
daemon::{
ca::events::{ChildCertificateUpdates, RoaUpdates},
ca::{
self, ta_handle, CaEvtDet, CertifiedKey, ChildCertificates, CurrentKey, KeyState, NewKey, OldKey,
PendingKey, Roas, Routes,
},
config::{Config, IssuanceTimingConfig},
},
};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ResourceClass {
name: ResourceClassName,
name_space: String,
parent_handle: ParentHandle,
parent_rc_name: ResourceClassName,
roas: Roas,
certificates: ChildCertificates,
last_key_change: Time,
key_state: KeyState,
}
impl ResourceClass {
pub fn create(
name: ResourceClassName,
name_space: String,
parent_handle: ParentHandle,
parent_rc_name: ResourceClassName,
pending_key: KeyIdentifier,
) -> Self {
ResourceClass {
name,
name_space,
parent_handle,
parent_rc_name,
roas: Roas::default(),
certificates: ChildCertificates::default(),
last_key_change: Time::now(),
key_state: KeyState::create(pending_key),
}
}
pub fn for_ta(parent_rc_name: ResourceClassName, pending_key: KeyIdentifier) -> Self {
ResourceClass {
name: parent_rc_name.clone(),
name_space: parent_rc_name.to_string(),
parent_handle: ta_handle(),
parent_rc_name,
roas: Roas::default(),
certificates: ChildCertificates::default(),
last_key_change: Time::now(),
key_state: KeyState::create(pending_key),
}
}
}
impl ResourceClass {
pub fn name_space(&self) -> &str {
&self.name_space
}
pub fn parent_handle(&self) -> &ParentHandle {
&self.parent_handle
}
pub fn parent_rc_name(&self) -> &ResourceClassName {
&self.parent_rc_name
}
pub fn add_request(&mut self, key_id: KeyIdentifier, req: IssuanceRequest) {
self.key_state.add_request(key_id, req);
}
pub fn current_certificate(&self) -> Option<&RcvdCert> {
self.current_key().map(|k| k.incoming_cert())
}
pub fn current_resources(&self) -> Option<&ResourceSet> {
self.current_certificate().map(|c| c.resources())
}
pub fn current_key(&self) -> Option<&CurrentKey> {
match &self.key_state {
KeyState::Active(current)
| KeyState::RollPending(_, current)
| KeyState::RollNew(_, current)
| KeyState::RollOld(current, _) => Some(current),
_ => None,
}
}
pub fn get_current_key(&self) -> KrillResult<&CurrentKey> {
self.current_key().ok_or(Error::KeyUseNoCurrentKey)
}
pub fn key_roll_possible(&self) -> bool {
matches!(&self.key_state, KeyState::Active(_))
}
pub fn get_new_key(&self) -> KrillResult<&NewKey> {
if let KeyState::RollNew(new_key, _) = &self.key_state {
Ok(new_key)
} else {
Err(Error::KeyUseNoNewKey)
}
}
pub fn as_info(&self) -> ResourceClassInfo {
ResourceClassInfo::new(
self.name_space.clone(),
self.parent_handle.clone(),
self.key_state.as_info(),
)
}
}
impl ResourceClass {
pub fn set_old_repo(&mut self, repo: &RepoInfo) {
self.key_state.set_old_repo_if_in_active_state(repo);
}
}
impl ResourceClass {
pub fn update_received_cert(
&self,
handle: &Handle,
rcvd_cert: RcvdCert,
routes: &Routes,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvtDet>> {
let rcvd_cert_ki = rcvd_cert.cert().subject_key_identifier();
match &self.key_state {
KeyState::Pending(pending) => {
if rcvd_cert_ki != pending.key_id() {
Err(Error::KeyUseNoMatch(rcvd_cert_ki))
} else {
info!(
"Received certificate for CA '{}' under RC '{}', with resources: '{}' valid until: '{}'",
handle,
self.name,
rcvd_cert.resources(),
rcvd_cert.validity().not_after().to_rfc3339()
);
let current_key = CertifiedKey::create(rcvd_cert);
Ok(vec![CaEvtDet::KeyPendingToActive {
resource_class_name: self.name.clone(),
current_key,
}])
}
}
KeyState::Active(current) => {
self.update_rcvd_cert_current(handle, current, rcvd_cert, routes, config, signer)
}
KeyState::RollPending(pending, current) => {
if rcvd_cert_ki == pending.key_id() {
let new_key = CertifiedKey::create(rcvd_cert);
Ok(vec![CaEvtDet::KeyPendingToNew {
resource_class_name: self.name.clone(),
new_key,
}])
} else {
self.update_rcvd_cert_current(handle, current, rcvd_cert, routes, config, signer)
}
}
KeyState::RollNew(new, current) => {
if rcvd_cert_ki == new.key_id() {
Ok(vec![CaEvtDet::CertificateReceived {
resource_class_name: self.name.clone(),
ki: rcvd_cert_ki,
rcvd_cert,
}])
} else {
self.update_rcvd_cert_current(handle, current, rcvd_cert, routes, config, signer)
}
}
KeyState::RollOld(current, _old) => {
self.update_rcvd_cert_current(handle, current, rcvd_cert, routes, config, signer)
}
}
}
fn update_rcvd_cert_current(
&self,
handle: &Handle,
current_key: &CurrentKey,
rcvd_cert: RcvdCert,
routes: &Routes,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvtDet>> {
let ki = rcvd_cert.cert().subject_key_identifier();
if ki != current_key.key_id() {
return Err(ca::Error::KeyUseNoMatch(ki));
}
let rcvd_resources = rcvd_cert.resources();
let mut res = vec![CaEvtDet::CertificateReceived {
resource_class_name: self.name.clone(),
ki,
rcvd_cert: rcvd_cert.clone(),
}];
let rcvd_resources_diff = rcvd_resources.difference(current_key.incoming_cert().resources());
if !rcvd_resources_diff.is_empty() {
info!(
"Received new certificate under CA '{}' under RC '{}' with changed resources: '{}', valid until: {}",
handle,
self.name,
rcvd_resources_diff,
rcvd_cert.validity().not_after().to_rfc3339()
);
let mut updates = ChildCertificateUpdates::default();
for issued in self.certificates.overclaiming(rcvd_resources) {
let remaining_resources = issued.resource_set().intersection(rcvd_resources);
if remaining_resources.is_empty() {
updates.remove(issued.subject_key_identifier());
} else {
let re_issued = self.re_issue(
issued,
Some(remaining_resources),
current_key,
None,
&config.issuance_timing,
signer,
)?;
updates.issue(re_issued);
}
}
if !updates.is_empty() {
res.push(CaEvtDet::ChildCertificatesUpdated {
resource_class_name: self.name.clone(),
updates,
});
}
let updated_key = CertifiedKey::create(rcvd_cert);
let updates = self.roas.update(routes, &updated_key, config, signer)?;
if !updates.is_empty() {
res.push(CaEvtDet::RoasUpdated {
resource_class_name: self.name.clone(),
updates,
});
}
} else {
info!(
"Received new certificate for CA '{}' under RC '{}', valid until: {}",
handle,
self.name,
rcvd_cert.validity().not_after().to_rfc3339()
)
}
Ok(res)
}
pub fn make_entitlement_events(
&self,
handle: &Handle,
entitlement: &EntitlementClass,
base_repo: &RepoInfo,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvtDet>> {
self.key_state.make_entitlement_events(
handle,
self.name.clone(),
entitlement,
base_repo,
&self.name_space,
signer,
)
}
pub fn make_request_events_new_repo(
&self,
base_repo: &RepoInfo,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvtDet>> {
self.key_state
.request_certs_new_repo(self.name.clone(), base_repo, &self.name_space, signer)
}
pub fn cert_requests(&self) -> Vec<IssuanceRequest> {
self.key_state.cert_requests()
}
pub fn revoke_request(&self) -> Option<&RevocationRequest> {
self.key_state.revoke_request()
}
pub fn has_pending_requests(&self) -> bool {
!self.cert_requests().is_empty() || self.revoke_request().is_some()
}
}
impl ResourceClass {
pub fn revoke(&self, signer: &KrillSigner) -> KrillResult<Vec<RevocationRequest>> {
self.key_state.revoke(self.parent_rc_name.clone(), signer)
}
}
impl ResourceClass {
pub fn received_cert(&mut self, key_id: KeyIdentifier, cert: RcvdCert) {
match &mut self.key_state {
KeyState::Pending(_pending) => panic!("Would have received KeyPendingToActive event"),
KeyState::Active(current) => {
current.set_incoming_cert(cert);
}
KeyState::RollPending(_pending, current) => {
current.set_incoming_cert(cert);
}
KeyState::RollNew(new, current) => {
if new.key_id() == &key_id {
new.set_incoming_cert(cert);
} else {
current.set_incoming_cert(cert);
}
}
KeyState::RollOld(current, old) => {
if current.key_id() == &key_id {
current.set_incoming_cert(cert);
} else {
old.set_incoming_cert(cert);
}
}
}
}
pub fn pending_key_id_added(&mut self, key_id: KeyIdentifier) {
match &self.key_state {
KeyState::Active(current) => {
let pending = PendingKey::new(key_id);
self.key_state = KeyState::RollPending(pending, current.clone())
}
_ => panic!("Should never create event to add key when roll in progress"),
}
}
pub fn pending_key_to_new(&mut self, new: CertifiedKey) {
match &self.key_state {
KeyState::RollPending(_pending, current) => {
self.key_state = KeyState::RollNew(new, current.clone());
}
_ => panic!("Cannot move pending to new, if state is not roll pending"),
}
}
pub fn pending_key_to_active(&mut self, new: CertifiedKey) {
match &self.key_state {
KeyState::Pending(_pending) => {
self.key_state = KeyState::Active(new);
}
_ => panic!("Cannot move pending to active, if state is not pending"),
}
}
pub fn new_key_activated(&mut self, revoke_req: RevocationRequest) {
match &self.key_state {
KeyState::RollNew(new, current) => {
let old_key = OldKey::new(current.clone(), revoke_req);
self.key_state = KeyState::RollOld(new.clone(), old_key);
}
_ => panic!("Should never create event to activate key when no roll in progress"),
}
}
pub fn old_key_removed(&mut self) {
match &self.key_state {
KeyState::RollOld(current, _old) => {
self.key_state = KeyState::Active(current.clone());
}
_ => panic!("Should never create event to remove old key, when there is none"),
}
}
pub fn keyroll_initiate(
&self,
base_repo: &RepoInfo,
duration: Duration,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvtDet>> {
if duration > Duration::seconds(0) && self.last_key_change + duration > Time::now() {
return Ok(vec![]);
}
self.key_state.keyroll_initiate(
self.name.clone(),
self.parent_rc_name.clone(),
base_repo,
&self.name_space,
signer,
)
}
pub fn keyroll_activate(
&self,
staging_time: Duration,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<Vec<CaEvtDet>> {
if let Some(new_key) = self.key_state.new_key() {
if staging_time > Duration::seconds(0) && self.last_key_change + staging_time > Time::now() {
Ok(vec![])
} else {
let key_activated =
self.key_state
.keyroll_activate(self.name.clone(), self.parent_rc_name.clone(), signer)?;
let roa_updates = self.roas.activate_key(new_key, issuance_timing, signer)?;
let roas_updated = CaEvtDet::RoasUpdated {
resource_class_name: self.name.clone(),
updates: roa_updates,
};
let mut cert_updates = ChildCertificateUpdates::default();
for issued in self.certificates.iter() {
let re_issued = self.re_issue(issued, None, new_key, None, issuance_timing, signer)?;
cert_updates.issue(re_issued);
}
let certs_updated = CaEvtDet::ChildCertificatesUpdated {
resource_class_name: self.name.clone(),
updates: cert_updates,
};
Ok(vec![key_activated, roas_updated, certs_updated])
}
} else {
Ok(vec![])
}
}
pub fn keyroll_finish(&self) -> KrillResult<CaEvtDet> {
match &self.key_state {
KeyState::RollOld(_current, _old) => Ok(CaEvtDet::KeyRollFinished {
resource_class_name: self.name.clone(),
}),
_ => Err(Error::KeyUseNoOldKey),
}
}
}
impl ResourceClass {
pub fn issue_cert(
&self,
csr: CsrInfo,
child_resources: &ResourceSet,
limit: RequestResourceLimit,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<IssuedCert> {
let signing_key = self.get_current_key()?;
let parent_resources = signing_key.incoming_cert().resources();
let resources = parent_resources.intersection(child_resources);
let replaces = self.certificates.get(&csr.key_id()).map(ReplacedObject::from);
let issued = SignSupport::make_issued_cert(
csr,
&resources,
limit,
replaces,
signing_key,
issuance_timing.timing_child_certificate_valid_weeks,
signer,
)?;
Ok(issued)
}
fn re_issue(
&self,
previous: &IssuedCert,
updated_resources: Option<ResourceSet>,
signing_key: &CertifiedKey,
csr_info_opt: Option<CsrInfo>,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<IssuedCert> {
let (_uri, limit, resource_set, cert) = previous.clone().unpack();
let csr = csr_info_opt.unwrap_or_else(|| CsrInfo::from(&cert));
let resource_set = updated_resources.unwrap_or(resource_set);
let replaced = ReplacedObject::new(Revocation::from(&cert), HexEncodedHash::from(&cert));
let re_issued = SignSupport::make_issued_cert(
csr,
&resource_set,
limit,
Some(replaced),
signing_key,
issuance_timing.timing_child_certificate_valid_weeks,
signer,
)?;
Ok(re_issued)
}
pub fn certificate_issued(&mut self, issued: IssuedCert) {
self.certificates.certificate_issued(issued);
}
pub fn issued(&self, ki: &KeyIdentifier) -> Option<&IssuedCert> {
self.certificates.get(ki)
}
pub fn key_revoked(&mut self, key: &KeyIdentifier) {
self.certificates.key_revoked(key);
}
}
impl ResourceClass {
pub fn renew_roas(&self, issuance_timing: &IssuanceTimingConfig, signer: &KrillSigner) -> KrillResult<RoaUpdates> {
let key = self.get_current_key()?;
self.roas.renew(key, issuance_timing, signer)
}
pub fn active_key_roas(
&self,
issuance_timing: &IssuanceTimingConfig,
signer: &KrillSigner,
) -> KrillResult<RoaUpdates> {
let key = self.get_new_key()?;
self.roas.activate_key(key, issuance_timing, signer)
}
pub fn update_roas(
&self,
routes: &Routes,
new_resources: Option<&ResourceSet>,
config: &Config,
signer: &KrillSigner,
) -> KrillResult<RoaUpdates> {
let key = self.get_current_key()?;
let resources = new_resources.unwrap_or_else(|| key.incoming_cert().resources());
let routes = routes.filter(resources);
self.roas.update(&routes, key, config, signer)
}
pub fn roas_updated(&mut self, updates: RoaUpdates) {
self.roas.updated(updates);
}
}
impl ResourceClass {
pub fn create_rta_ee(
&self,
resources: &ResourceSet,
validity: Validity,
key: KeyIdentifier,
signer: &KrillSigner,
) -> KrillResult<Cert> {
let current = self
.current_key()
.ok_or_else(|| Error::custom("No current key to sign RTA with"))?;
if !current.incoming_cert().resources().contains(resources) {
return Err(Error::custom("Resources for RTA not held"));
}
let pub_key = signer.get_key_info(&key).map_err(Error::signer)?;
let ee = SignSupport::make_rta_ee_cert(resources, ¤t, validity, pub_key, signer)?;
Ok(ee)
}
}