1 use std::ops::{Deref, DerefMut};
2 
3 use serde::{Deserialize, Serialize};
4 
5 use rpki::repository::{crypto::KeyIdentifier, x509::Time};
6 
7 use crate::{
8     commons::{
9         api::{
10             ActiveInfo, CertifiedKeyInfo, EntitlementClass, Handle, IssuanceRequest, PendingInfo, PendingKeyInfo,
11             RcvdCert, RepoInfo, RequestResourceLimit, ResourceClassKeysInfo, ResourceClassName, ResourceSet,
12             RevocationRequest, RollNewInfo, RollOldInfo, RollPendingInfo,
13         },
14         crypto::KrillSigner,
15         error::Error,
16         KrillResult,
17     },
18     daemon::ca::CaEvtDet,
19 };
20 
21 //------------ CertifiedKey --------------------------------------------------
22 
23 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
24 /// Describes a Key that is certified. I.e. it received an incoming certificate
25 /// and has at least a MFT and CRL.
26 pub struct CertifiedKey {
27     key_id: KeyIdentifier,
28     incoming_cert: RcvdCert,
29     request: Option<IssuanceRequest>,
30     #[serde(skip_serializing_if = "Option::is_none")]
31     old_repo: Option<RepoInfo>,
32 }
33 
34 impl CertifiedKey {
new(key_id: KeyIdentifier, incoming_cert: RcvdCert, request: Option<IssuanceRequest>) -> Self35     pub fn new(key_id: KeyIdentifier, incoming_cert: RcvdCert, request: Option<IssuanceRequest>) -> Self {
36         CertifiedKey {
37             key_id,
38             incoming_cert,
39             request,
40             old_repo: None,
41         }
42     }
43 
create(incoming_cert: RcvdCert) -> Self44     pub fn create(incoming_cert: RcvdCert) -> Self {
45         let key_id = incoming_cert.subject_key_identifier();
46         CertifiedKey {
47             key_id,
48             incoming_cert,
49             request: None,
50             old_repo: None,
51         }
52     }
53 
as_info(&self) -> CertifiedKeyInfo54     pub fn as_info(&self) -> CertifiedKeyInfo {
55         CertifiedKeyInfo::new(self.key_id, self.incoming_cert.clone())
56     }
57 
key_id(&self) -> &KeyIdentifier58     pub fn key_id(&self) -> &KeyIdentifier {
59         &self.key_id
60     }
incoming_cert(&self) -> &RcvdCert61     pub fn incoming_cert(&self) -> &RcvdCert {
62         &self.incoming_cert
63     }
set_incoming_cert(&mut self, incoming_cert: RcvdCert)64     pub fn set_incoming_cert(&mut self, incoming_cert: RcvdCert) {
65         self.request = None;
66         self.incoming_cert = incoming_cert;
67     }
68 
request(&self) -> Option<&IssuanceRequest>69     pub fn request(&self) -> Option<&IssuanceRequest> {
70         self.request.as_ref()
71     }
add_request(&mut self, req: IssuanceRequest)72     pub fn add_request(&mut self, req: IssuanceRequest) {
73         self.request = Some(req)
74     }
75 
set_old_repo(&mut self, repo: &RepoInfo)76     pub fn set_old_repo(&mut self, repo: &RepoInfo) {
77         self.old_repo = Some(repo.clone())
78     }
79 
wants_update( &self, handle: &Handle, rcn: &ResourceClassName, new_resources: &ResourceSet, new_not_after: Time, ) -> bool80     pub fn wants_update(
81         &self,
82         handle: &Handle,
83         rcn: &ResourceClassName,
84         new_resources: &ResourceSet,
85         new_not_after: Time,
86     ) -> bool {
87         // If resources have changed, then we need to request a new certificate.
88         let resources_diff = new_resources.difference(self.incoming_cert.resources());
89 
90         if !resources_diff.is_empty() {
91             info!(
92                 "Will request new certificate for CA '{}' under RC '{}'. Resources have changed: '{}'",
93                 handle, rcn, resources_diff
94             );
95             return true;
96         }
97 
98         // If the validity time eligibility has changed, then we *may* want to ask for a new
99         // certificate, but only if:
100         // a) our current certificate expires *after* the eligible time, because we probably should
101         //    know..
102         // b) the new not after time is significantly better than our current time, because we
103         //    do not want to ask for new certificates every hour if the parent uses a simple
104         //    strategy like: not-after = now + 1 year..
105         //
106         // See issue #95
107 
108         let not_after = self.incoming_cert().cert().validity().not_after();
109 
110         let now = Time::now().timestamp_millis();
111         let until_not_after_millis = not_after.timestamp_millis() - now;
112         let until_new_not_after_millis = new_not_after.timestamp_millis() - now;
113 
114         if until_new_not_after_millis < 0 {
115             // New not after time is in the past!
116             //
117             // This is rather odd. The parent should just exclude the resource class in the
118             // eligible entitlements instead. So, we will essentially just ignore this until
119             // they do.
120             warn!(
121                 "Will NOT request certificate for CA '{}' under RC '{}', the eligible not after time is set in the past: {}",
122                 handle,
123                 rcn,
124                 new_not_after.to_rfc3339()
125             );
126             false
127         } else if until_not_after_millis == until_new_not_after_millis {
128             debug!(
129                 "Will not request new certificate for CA '{}' under RC '{}'. Resources and not after time are unchanged.",
130                 handle,
131                 rcn,
132             );
133             false
134         } else if until_new_not_after_millis < until_not_after_millis {
135             warn!(
136                 "Parent of CA '{}' reduced not after time for certificate under RC '{}'",
137                 handle, rcn,
138             );
139             true
140         } else if until_not_after_millis <= 0
141             || (until_new_not_after_millis as f64 / until_not_after_millis as f64) > 1.1_f64
142         {
143             info!(
144                 "Will request new certificate for CA '{}' under RC '{}'. Not after time increased to: {}",
145                 handle,
146                 rcn,
147                 new_not_after.to_rfc3339()
148             );
149             true
150         } else {
151             debug!(
152                 "Will not request new certificate for CA '{}' under RC '{}'. Not after time increased by less than 10%: {}",
153                 handle,
154                 rcn,
155                 new_not_after.to_rfc3339()
156             );
157             false
158         }
159     }
160 }
161 
162 pub type NewKey = CertifiedKey;
163 pub type CurrentKey = CertifiedKey;
164 
165 //------------ PendingKey ----------------------------------------------------
166 
167 /// A Pending Key in a resource class. Should usually have an open
168 /// IssuanceRequest, and will be move to a 'new' or 'current' CertifiedKey
169 /// when a certificate is received.
170 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
171 pub struct PendingKey {
172     key_id: KeyIdentifier,
173     request: Option<IssuanceRequest>,
174 }
175 
176 impl PendingKey {
new(key_id: KeyIdentifier) -> Self177     pub fn new(key_id: KeyIdentifier) -> Self {
178         PendingKey { key_id, request: None }
179     }
180 
as_info(&self) -> PendingKeyInfo181     pub fn as_info(&self) -> PendingKeyInfo {
182         PendingKeyInfo::new(self.key_id)
183     }
184 
unwrap(self) -> (KeyIdentifier, Option<IssuanceRequest>)185     pub fn unwrap(self) -> (KeyIdentifier, Option<IssuanceRequest>) {
186         (self.key_id, self.request)
187     }
188 
key_id(&self) -> &KeyIdentifier189     pub fn key_id(&self) -> &KeyIdentifier {
190         &self.key_id
191     }
request(&self) -> Option<&IssuanceRequest>192     pub fn request(&self) -> Option<&IssuanceRequest> {
193         self.request.as_ref()
194     }
add_request(&mut self, req: IssuanceRequest)195     pub fn add_request(&mut self, req: IssuanceRequest) {
196         self.request = Some(req)
197     }
clear_request(&mut self)198     pub fn clear_request(&mut self) {
199         self.request = None
200     }
201 }
202 
203 //------------ OldKey --------------------------------------------------------
204 
205 #[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)]
206 pub struct OldKey {
207     key: CertifiedKey,
208     revoke_req: RevocationRequest,
209 }
210 
211 impl OldKey {
new(key: CertifiedKey, revoke_req: RevocationRequest) -> Self212     pub fn new(key: CertifiedKey, revoke_req: RevocationRequest) -> Self {
213         OldKey { key, revoke_req }
214     }
215 
key(&self) -> &CertifiedKey216     pub fn key(&self) -> &CertifiedKey {
217         &self.key
218     }
revoke_req(&self) -> &RevocationRequest219     pub fn revoke_req(&self) -> &RevocationRequest {
220         &self.revoke_req
221     }
222 }
223 
224 impl Deref for OldKey {
225     type Target = CertifiedKey;
226 
deref(&self) -> &Self::Target227     fn deref(&self) -> &Self::Target {
228         &self.key
229     }
230 }
231 
232 impl DerefMut for OldKey {
deref_mut(&mut self) -> &mut Self::Target233     fn deref_mut(&mut self) -> &mut Self::Target {
234         &mut self.key
235     }
236 }
237 
238 //------------ KeyState ------------------------------------------------------
239 
240 /// This type contains the keys for a resource class and guards that keys
241 /// are created, activated, rolled and retired properly.
242 #[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)]
243 #[allow(clippy::large_enum_variant)]
244 #[serde(rename_all = "snake_case")]
245 pub enum KeyState {
246     Pending(PendingKey),
247     Active(CurrentKey),
248     RollPending(PendingKey, CurrentKey),
249     RollNew(NewKey, CurrentKey),
250     RollOld(CurrentKey, OldKey),
251 }
252 
253 impl KeyState {
create(pending_key: KeyIdentifier) -> Self254     pub fn create(pending_key: KeyIdentifier) -> Self {
255         KeyState::Pending(PendingKey::new(pending_key))
256     }
257 
add_request(&mut self, key_id: KeyIdentifier, req: IssuanceRequest)258     pub fn add_request(&mut self, key_id: KeyIdentifier, req: IssuanceRequest) {
259         match self {
260             KeyState::Pending(pending) => pending.add_request(req),
261             KeyState::Active(current) => current.add_request(req),
262             KeyState::RollPending(pending, current) => {
263                 if pending.key_id() == &key_id {
264                     pending.add_request(req)
265                 } else {
266                     current.add_request(req)
267                 }
268             }
269             KeyState::RollNew(new, current) => {
270                 if new.key_id() == &key_id {
271                     new.add_request(req)
272                 } else {
273                     current.add_request(req)
274                 }
275             }
276             KeyState::RollOld(current, old) => {
277                 if current.key_id() == &key_id {
278                     current.add_request(req)
279                 } else {
280                     old.add_request(req)
281                 }
282             }
283         }
284     }
285 
286     /// Revoke all current keys
revoke(&self, class_name: ResourceClassName, signer: &KrillSigner) -> KrillResult<Vec<RevocationRequest>>287     pub fn revoke(&self, class_name: ResourceClassName, signer: &KrillSigner) -> KrillResult<Vec<RevocationRequest>> {
288         match self {
289             KeyState::Pending(_pending) => Ok(vec![]), // nothing to revoke
290             KeyState::Active(current) | KeyState::RollPending(_, current) => {
291                 let revoke_current = Self::revoke_key(class_name, current.key_id(), signer)?;
292                 Ok(vec![revoke_current])
293             }
294             KeyState::RollNew(new, current) => {
295                 let revoke_new = Self::revoke_key(class_name.clone(), new.key_id(), signer)?;
296                 let revoke_current = Self::revoke_key(class_name, current.key_id(), signer)?;
297                 Ok(vec![revoke_new, revoke_current])
298             }
299             KeyState::RollOld(current, old) => {
300                 let revoke_current = Self::revoke_key(class_name, current.key_id(), signer)?;
301                 let revoke_old = old.revoke_req().clone();
302                 Ok(vec![revoke_current, revoke_old])
303             }
304         }
305     }
306 
revoke_key( class_name: ResourceClassName, key_id: &KeyIdentifier, signer: &KrillSigner, ) -> KrillResult<RevocationRequest>307     fn revoke_key(
308         class_name: ResourceClassName,
309         key_id: &KeyIdentifier,
310         signer: &KrillSigner,
311     ) -> KrillResult<RevocationRequest> {
312         let ki = signer.get_key_info(key_id).map_err(Error::signer)?.key_identifier();
313 
314         Ok(RevocationRequest::new(class_name, ki))
315     }
316 
make_entitlement_events( &self, handle: &Handle, rcn: ResourceClassName, entitlement: &EntitlementClass, base_repo: &RepoInfo, name_space: &str, signer: &KrillSigner, ) -> KrillResult<Vec<CaEvtDet>>317     pub fn make_entitlement_events(
318         &self,
319         handle: &Handle,
320         rcn: ResourceClassName,
321         entitlement: &EntitlementClass,
322         base_repo: &RepoInfo,
323         name_space: &str,
324         signer: &KrillSigner,
325     ) -> KrillResult<Vec<CaEvtDet>> {
326         let mut keys_for_requests = vec![];
327 
328         match self {
329             KeyState::Pending(pending) => {
330                 keys_for_requests.push((base_repo, pending.key_id()));
331             }
332             KeyState::Active(current) => {
333                 if current.wants_update(handle, &rcn, entitlement.resource_set(), entitlement.not_after()) {
334                     let repo = current.old_repo.as_ref().unwrap_or(base_repo);
335                     keys_for_requests.push((repo, current.key_id()));
336                 }
337             }
338             KeyState::RollPending(pending, current) => {
339                 keys_for_requests.push((base_repo, pending.key_id()));
340                 if current.wants_update(handle, &rcn, entitlement.resource_set(), entitlement.not_after()) {
341                     let repo = current.old_repo.as_ref().unwrap_or(base_repo);
342                     keys_for_requests.push((repo, current.key_id()));
343                 }
344             }
345             KeyState::RollNew(new, current) => {
346                 if new.wants_update(handle, &rcn, entitlement.resource_set(), entitlement.not_after()) {
347                     let repo = new.old_repo.as_ref().unwrap_or(base_repo);
348                     keys_for_requests.push((repo, new.key_id()));
349                 }
350                 if current.wants_update(handle, &rcn, entitlement.resource_set(), entitlement.not_after()) {
351                     let repo = current.old_repo.as_ref().unwrap_or(base_repo);
352                     keys_for_requests.push((repo, current.key_id()));
353                 }
354             }
355             KeyState::RollOld(current, old) => {
356                 if current.wants_update(handle, &rcn, entitlement.resource_set(), entitlement.not_after()) {
357                     let repo = current.old_repo.as_ref().unwrap_or(base_repo);
358                     keys_for_requests.push((repo, current.key_id()));
359                 }
360                 if old.wants_update(handle, &rcn, entitlement.resource_set(), entitlement.not_after()) {
361                     let repo = old.old_repo.as_ref().unwrap_or(base_repo);
362                     keys_for_requests.push((repo, current.key_id()));
363                 }
364             }
365         }
366 
367         let mut res = vec![];
368 
369         for (base_repo, key_id) in keys_for_requests.into_iter() {
370             let req =
371                 self.create_issuance_req(base_repo, name_space, entitlement.class_name().clone(), key_id, signer)?;
372 
373             res.push(CaEvtDet::CertificateRequested {
374                 resource_class_name: rcn.clone(),
375                 req,
376                 ki: *key_id,
377             });
378         }
379 
380         for key in entitlement.issued().iter().map(|c| c.subject_key_identifier()) {
381             if !self.knows_key(key) {
382                 let revoke_req = RevocationRequest::new(entitlement.class_name().clone(), key);
383                 res.push(CaEvtDet::UnexpectedKeyFound {
384                     resource_class_name: rcn.clone(),
385                     revoke_req,
386                 });
387             }
388         }
389 
390         Ok(res)
391     }
392 
request_certs_new_repo( &self, rcn: ResourceClassName, base_repo: &RepoInfo, name_space: &str, signer: &KrillSigner, ) -> KrillResult<Vec<CaEvtDet>>393     pub fn request_certs_new_repo(
394         &self,
395         rcn: ResourceClassName,
396         base_repo: &RepoInfo,
397         name_space: &str,
398         signer: &KrillSigner,
399     ) -> KrillResult<Vec<CaEvtDet>> {
400         let mut res = vec![];
401 
402         let keys = match self {
403             KeyState::Pending(pending) => vec![pending.key_id()],
404             KeyState::Active(current) => vec![current.key_id()],
405             KeyState::RollPending(pending, current) => vec![pending.key_id(), current.key_id()],
406             KeyState::RollNew(new, current) => vec![new.key_id(), current.key_id()],
407             KeyState::RollOld(current, old) => vec![current.key_id(), old.key_id()],
408         };
409 
410         for ki in keys {
411             let req = self.create_issuance_req(base_repo, name_space, rcn.clone(), ki, signer)?;
412             res.push(CaEvtDet::CertificateRequested {
413                 resource_class_name: rcn.clone(),
414                 req,
415                 ki: *ki,
416             });
417         }
418 
419         Ok(res)
420     }
421 
422     /// Returns all open certificate requests
cert_requests(&self) -> Vec<IssuanceRequest>423     pub fn cert_requests(&self) -> Vec<IssuanceRequest> {
424         let mut res = vec![];
425         match self {
426             KeyState::Pending(pending) => {
427                 if let Some(r) = pending.request() {
428                     res.push(r.clone())
429                 }
430             }
431             KeyState::Active(current) => {
432                 if let Some(r) = current.request() {
433                     res.push(r.clone())
434                 }
435             }
436             KeyState::RollPending(pending, current) => {
437                 if let Some(r) = pending.request() {
438                     res.push(r.clone())
439                 }
440                 if let Some(r) = current.request() {
441                     res.push(r.clone())
442                 }
443             }
444             KeyState::RollNew(new, current) => {
445                 if let Some(r) = new.request() {
446                     res.push(r.clone())
447                 }
448                 if let Some(r) = current.request() {
449                     res.push(r.clone())
450                 }
451             }
452             KeyState::RollOld(current, old) => {
453                 if let Some(r) = current.request() {
454                     res.push(r.clone())
455                 }
456                 if let Some(r) = old.request() {
457                     res.push(r.clone())
458                 }
459             }
460         }
461         res
462     }
463 
464     /// Creates a Csr for the given key.
create_issuance_req( &self, base_repo: &RepoInfo, name_space: &str, class_name: ResourceClassName, key: &KeyIdentifier, signer: &KrillSigner, ) -> KrillResult<IssuanceRequest>465     fn create_issuance_req(
466         &self,
467         base_repo: &RepoInfo,
468         name_space: &str,
469         class_name: ResourceClassName,
470         key: &KeyIdentifier,
471         signer: &KrillSigner,
472     ) -> KrillResult<IssuanceRequest> {
473         let csr = signer.sign_csr(base_repo, name_space, key)?;
474         Ok(IssuanceRequest::new(class_name, RequestResourceLimit::default(), csr))
475     }
476 
477     /// Returns the revoke request if there is an old key.
revoke_request(&self) -> Option<&RevocationRequest>478     pub fn revoke_request(&self) -> Option<&RevocationRequest> {
479         match self {
480             KeyState::RollOld(_current, old) => Some(old.revoke_req()),
481             _ => None,
482         }
483     }
484 
as_info(&self) -> ResourceClassKeysInfo485     pub fn as_info(&self) -> ResourceClassKeysInfo {
486         match self.clone() {
487             KeyState::Pending(p) => ResourceClassKeysInfo::Pending(PendingInfo {
488                 _pending_key: p.as_info(),
489             }),
490             KeyState::Active(c) => ResourceClassKeysInfo::Active(ActiveInfo {
491                 _active_key: c.as_info(),
492             }),
493             KeyState::RollPending(p, c) => ResourceClassKeysInfo::RollPending(RollPendingInfo {
494                 _pending_key: p.as_info(),
495                 _active_key: c.as_info(),
496             }),
497             KeyState::RollNew(n, c) => ResourceClassKeysInfo::RollNew(RollNewInfo {
498                 _new_key: n.as_info(),
499                 _active_key: c.as_info(),
500             }),
501             KeyState::RollOld(c, o) => ResourceClassKeysInfo::RollOld(RollOldInfo {
502                 _old_key: o.as_info(),
503                 _active_key: c.as_info(),
504             }),
505         }
506     }
507 }
508 
509 /// # Key Life Cycle
510 ///
511 impl KeyState {
512     /// Initiates a key roll if the current state is 'Active'. This will return event details
513     /// for a newly create pending key and requested certificate for it.
keyroll_initiate( &self, resource_class_name: ResourceClassName, parent_class_name: ResourceClassName, base_repo: &RepoInfo, name_space: &str, signer: &KrillSigner, ) -> KrillResult<Vec<CaEvtDet>>514     pub fn keyroll_initiate(
515         &self,
516         resource_class_name: ResourceClassName,
517         parent_class_name: ResourceClassName,
518         base_repo: &RepoInfo,
519         name_space: &str,
520         signer: &KrillSigner,
521     ) -> KrillResult<Vec<CaEvtDet>> {
522         match self {
523             KeyState::Active(_current) => {
524                 let pending_key_id = signer.create_key()?;
525 
526                 let req =
527                     self.create_issuance_req(base_repo, name_space, parent_class_name, &pending_key_id, signer)?;
528 
529                 Ok(vec![
530                     CaEvtDet::KeyRollPendingKeyAdded {
531                         resource_class_name: resource_class_name.clone(),
532                         pending_key_id,
533                     },
534                     CaEvtDet::CertificateRequested {
535                         resource_class_name,
536                         req,
537                         ki: pending_key_id,
538                     },
539                 ])
540             }
541             _ => Ok(vec![]),
542         }
543     }
544 
545     /// Marks the new key as current, and the current key as old, and requests revocation of
546     /// the old key.
keyroll_activate( &self, resource_class_name: ResourceClassName, parent_class_name: ResourceClassName, signer: &KrillSigner, ) -> KrillResult<CaEvtDet>547     pub fn keyroll_activate(
548         &self,
549         resource_class_name: ResourceClassName,
550         parent_class_name: ResourceClassName,
551         signer: &KrillSigner,
552     ) -> KrillResult<CaEvtDet> {
553         match self {
554             KeyState::RollNew(_new, current) => {
555                 let revoke_req = Self::revoke_key(parent_class_name, current.key_id(), signer)?;
556                 Ok(CaEvtDet::KeyRollActivated {
557                     resource_class_name,
558                     revoke_req,
559                 })
560             }
561             _ => Err(Error::KeyUseNoNewKey),
562         }
563     }
564 
565     /// Returns the new key, iff there is a key roll in progress and there is a new key.
new_key(&self) -> Option<&CertifiedKey>566     pub fn new_key(&self) -> Option<&CertifiedKey> {
567         match self {
568             KeyState::RollNew(new, _) => Some(new),
569             _ => None,
570         }
571     }
572 
knows_key(&self, key_id: KeyIdentifier) -> bool573     fn knows_key(&self, key_id: KeyIdentifier) -> bool {
574         match self {
575             KeyState::Pending(pending) => pending.key_id == key_id,
576             KeyState::Active(current) => current.key_id == key_id,
577             KeyState::RollPending(pending, current) => pending.key_id == key_id || current.key_id == key_id,
578             KeyState::RollNew(new, current) => new.key_id == key_id || current.key_id == key_id,
579             KeyState::RollOld(current, old) => current.key_id == key_id || old.key_id == key_id,
580         }
581     }
582 }
583 
584 /// # Migrate repositories
585 ///
586 impl KeyState {
587     /// Mark an old_repo for the current key, so that a new repo can be introduced in a pending
588     /// key and a keyroll can be done.
set_old_repo_if_in_active_state(&mut self, repo: &RepoInfo)589     pub fn set_old_repo_if_in_active_state(&mut self, repo: &RepoInfo) {
590         if let KeyState::Active(current) = self {
591             current.set_old_repo(repo);
592         }
593     }
594 }
595