Skip to main content

krill/server/ca/
publishing.rs

1//! Management of objects published by a CA.
2
3use std::collections::HashMap;
4use std::str::FromStr;
5use std::sync::Arc;
6use chrono::Duration;
7use log::debug;
8use rpki::{rrdp, uri};
9use rpki::ca::idexchange::CaHandle;
10use rpki::ca::provisioning::ResourceClassName;
11use rpki::ca::publication::Base64;
12use rpki::crypto::{DigestAlgorithm, KeyIdentifier};
13use rpki::repository::crl::{Crl, TbsCertList};
14use rpki::repository::manifest::{FileAndHash, Manifest, ManifestContent};
15use rpki::repository::sigobj::SignedObjectBuilder;
16use rpki::repository::x509::{Name, Serial, Time, Validity};
17use serde::{Deserialize, Serialize};
18use url::Url;
19use crate::api::admin::{PublishedFile, RepositoryContact};
20use crate::api::ca::{
21    CertInfo, IssuedCertificate, ObjectName, ReceivedCert, Revocation,
22    Revocations,
23};
24use crate::api::roa::RoaInfo;
25use crate::commons::KrillResult;
26use crate::commons::crypto::KrillSigner;
27use crate::commons::error::Error;
28use crate::commons::eventsourcing::PreSaveEventListener;
29use crate::commons::storage::{Ident, KeyValueStore};
30use crate::constants::CA_OBJECTS_NS;
31use crate::config::IssuanceTimingConfig;
32use super::aspa::{AspaInfo, AspaObjectsUpdates};
33use super::bgpsec::{BgpSecCertInfo, BgpSecCertificateUpdates};
34use super::certauth::CertAuth;
35use super::child::ChildCertificateUpdates;
36use super::events::CertAuthEvent;
37use super::keys::CertifiedKey; 
38use super::roa::RoaUpdates;
39
40
41//------------ CaObjectsStore ----------------------------------------------
42
43/// The component storing the latest objects for each CA.
44///
45/// By using a stateful store for this purpose we can generate manifests and
46/// CRLs outside of the normal event-sourcing framework used to track the
47/// history and state of CAs. I.e., we treat the frequent republish cycle as
48/// something that does not intrinsically modify the CA itself.
49///
50/// In earlier generations of Krill the simple republish operation to generate
51/// new manifests and CRLs was done through the event sourcing framework.
52/// However, this led to excessive use of disk space, makes the history more
53/// difficult to inspect, and causes issues with regards to replaying CA state
54/// from scratch.
55///
56/// # Key-value store usage
57///
58/// The CA objects store uses the key-value store directly. It uses the
59/// namespace defined by [`CA_OBJECTS_NS`], currently `"ca_objects"`. For
60/// each CA, it keeps a single value under the single-element scope of the
61/// CA’s handle suffixed by `.json`.
62#[derive(Debug)]
63pub struct CaObjectsStore {
64    /// The key-value store where objects are stored.
65    store: KeyValueStore,
66
67    /// The signer used when generate objects.
68    signer: Arc<KrillSigner>,
69
70    /// Configuration for timing of object creation.
71    issuance_timing: IssuanceTimingConfig,
72}
73
74impl CaObjectsStore {
75    /// Creates a new CA objects store using the given configuration.
76    pub fn create(
77        storage_uri: &Url,
78        issuance_timing: IssuanceTimingConfig,
79        signer: Arc<KrillSigner>,
80    ) -> KrillResult<Self> {
81        let store = KeyValueStore::create(storage_uri, CA_OBJECTS_NS)?;
82        Ok(CaObjectsStore {
83            store,
84            signer,
85            issuance_timing,
86        })
87    }
88}
89
90/// React to any events on a CA that cause the set of object to change.
91impl PreSaveEventListener<CertAuth> for CaObjectsStore {
92    fn listen(
93        &self,
94        ca: &CertAuth,
95        events: &[CertAuthEvent],
96    ) -> KrillResult<()> {
97        // Note that the `CertAuth` which is passed in has already been
98        // updated with the state changes contained in the event.
99
100        self.with_ca_objects(ca.handle(), |objects| {
101            let mut force_reissue = false;
102
103            for event in events {
104                match event {
105                    CertAuthEvent::RoasUpdated {
106                        resource_class_name,
107                        updates,
108                    } => {
109                        objects.update_roas(resource_class_name, updates)?;
110                        force_reissue = true;
111                    }
112                    CertAuthEvent::AspaObjectsUpdated {
113                        resource_class_name,
114                        updates,
115                    } => {
116                        objects.update_aspas(resource_class_name, updates)?;
117                        force_reissue = true;
118                    }
119                    CertAuthEvent::BgpSecCertificatesUpdated {
120                        resource_class_name,
121                        updates,
122                    } => {
123                        objects.update_bgpsec_certs(
124                            resource_class_name,
125                            updates,
126                        )?;
127                        force_reissue = true;
128                    }
129                    CertAuthEvent::ChildCertificatesUpdated {
130                        resource_class_name,
131                        updates,
132                    } => {
133                        objects.update_certs(resource_class_name, updates)?;
134                        force_reissue = true;
135                    }
136                    CertAuthEvent::KeyPendingToActive {
137                        resource_class_name,
138                        current_key,
139                    } => {
140                        objects.add_class(
141                            resource_class_name,
142                            current_key,
143                            &self.issuance_timing,
144                            &self.signer,
145                        )?;
146                    }
147                    CertAuthEvent::KeyPendingToNew {
148                        resource_class_name,
149                        new_key,
150                    } => {
151                        objects.keyroll_stage(
152                            resource_class_name,
153                            new_key,
154                            &self.issuance_timing,
155                            &self.signer,
156                        )?;
157                    }
158                    CertAuthEvent::KeyRollActivated {
159                        resource_class_name,
160                        ..
161                    } => {
162                        objects.keyroll_activate(resource_class_name)?;
163                        force_reissue = true;
164                    }
165                    CertAuthEvent::KeyRollFinished {
166                        resource_class_name,
167                    } => {
168                        objects.keyroll_finish(resource_class_name)?;
169                    }
170                    CertAuthEvent::CertificateReceived {
171                        resource_class_name,
172                        rcvd_cert,
173                        ..
174                    } => {
175                        objects.update_received_cert(
176                            resource_class_name,
177                            rcvd_cert,
178                        )?;
179                        // this in itself constitutes no need to force
180                        // re-issuance if the new
181                        // certificate triggered that the set of objects
182                        // changed, e.g. because a ROA
183                        // became overclaiming, then we would see another
184                        // event for that which *will* result in forcing
185                        // re-issuance.
186                    }
187                    CertAuthEvent::ResourceClassRemoved {
188                        resource_class_name,
189                        ..
190                    } => {
191                        objects.remove_class(resource_class_name);
192                        force_reissue = true;
193                    }
194                    CertAuthEvent::RepoUpdated { contact } => {
195                        objects.update_repo(contact);
196                        force_reissue = true;
197                    }
198                    _ => {}
199                }
200            }
201            objects.re_issue(
202                force_reissue, &self.issuance_timing, &self.signer
203            )?;
204            Ok(())
205        })
206    }
207}
208
209impl CaObjectsStore {
210    /// Returns the key for the given CA to be used in the store.
211    fn key(ca: &CaHandle) -> Box<Ident> {
212        // XXX This seems quite expensive to do. Maybe we should use the
213        //     handle as the scope and a static identifier for the key?
214        //
215        //     This would need a migration, though.
216        Ident::builder(
217            Ident::from_handle(ca).into_owned()
218        ).finish_with_extension(
219            const { Ident::make("json") }
220        )
221    }
222
223    /// Returns all CA handles present in the object store.
224    pub fn cas(&self) -> KrillResult<Vec<CaHandle>> {
225        // XXX The previous code only added keys that could directly be
226        //     parsed as handles. We keep to this for now because we convert
227        //     both slashes and backslashes into plusses and so can’t quite
228        //     distinguish the two cases. This ought to be changed once
229        //     we got rid of backslashes.
230        Ok(
231            self.store.keys(None, ".json")?.iter().filter_map(|k| {
232                let name = k.as_str().strip_suffix(".json")?;
233                CaHandle::from_str(name).ok()
234            }).collect()
235        )
236    }
237
238    /// Removes a CA from the store.
239    pub fn remove_ca(&self, ca: &CaHandle) -> KrillResult<()> {
240        let ca_key = Self::key(ca);
241        self.store.execute(None, |kv| {
242            if kv.has(None, &ca_key)? {
243                kv.delete(None, &ca_key)
244            } else {
245                Ok(())
246            }
247        }).map_err(Error::KeyValueError)
248    }
249
250    /// Loads the CA objects for this CA.
251    ///
252    /// If the CA isn’t present in the store yet, creates a new empty
253    /// CA objects value and returns it.
254    pub fn ca_objects(&self, ca: &CaHandle) -> KrillResult<CaObjects> {
255        match self.store.get(
256            None, &Self::key(ca)
257        ).map_err(Error::KeyValueError)? {
258            None => Ok(CaObjects::new(ca.clone())),
259            Some(objects) => Ok(objects),
260        }
261    }
262
263    /// Performs an action on the CA objects for a CA.
264    ///
265    /// If the CA did not have any CA objects yet, one will be created. The
266    /// closure is executed within a store-wide write lock.
267    pub fn with_ca_objects<F, T>(
268        &self,
269        ca: &CaHandle,
270        op: F,
271    ) -> KrillResult<T>
272    where
273        F: Fn(&mut CaObjects) -> KrillResult<T>,
274    {
275        self.store.execute(None, |kv| {
276            let key = Self::key(ca);
277
278            let mut objects: CaObjects = match kv.get(None, &key)? {
279                Some(value) => value,
280                None => CaObjects::new(ca.clone()),
281            };
282
283            match op(&mut objects) {
284                Err(e) => Ok(Err(e)),
285                Ok(t) => {
286                    kv.store(None, &key, &objects)?;
287                    Ok(Ok(t))
288                }
289            }
290        }).map_err(Error::KeyValueError)?
291    }
292
293    /// Re-issues manifests and CRLs for the CA if needed.
294    ///
295    /// If `force` is `true`, forces a re-issue. Returns whether it did
296    /// re-issue.
297    pub fn reissue_if_needed(
298        &self,
299        force: bool,
300        ca_handle: &CaHandle,
301    ) -> KrillResult<bool> {
302        debug!("Re-issue for CA {ca_handle} using force: {force}");
303        self.with_ca_objects(ca_handle, |objects| {
304            objects.re_issue(
305                force,
306                &self.issuance_timing,
307                &self.signer,
308            )
309        })
310    }
311}
312
313
314//------------CaObjects ------------------------------------------------------
315
316/// All the published objects of a CA.
317//
318//  *Warning:* This type is used in stored state.
319#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
320pub struct CaObjects {
321    /// The handle of the CA.
322    ca: CaHandle,
323
324    /// The repository the CA publishes too.
325    ///
326    /// This is `None` if there isn’t a repository assigned yet.
327    repo: Option<RepositoryContact>,
328
329    /// The resource classes and their published objects.
330    classes: HashMap<ResourceClassName, ResourceClassObjects>,
331
332    /// Repositories we consider deprecated and are trying to remove.
333    #[serde(skip_serializing_if = "Vec::is_empty", default)]
334    deprecated_repos: Vec<DeprecatedRepository>,
335}
336
337impl CaObjects {
338    /// Creates a new CA objects with only the handle assigned yet.
339    pub fn new(
340        ca: CaHandle,
341    ) -> Self {
342        CaObjects {
343            ca,
344            repo: None,
345            classes: HashMap::new(),
346            deprecated_repos: Vec::new(),
347        }
348    }
349
350    /// Creates a CA objects using the given parts.
351    ///
352    /// This is only used for upgrading.
353    pub fn from_parts(
354        ca: CaHandle,
355        repo: Option<RepositoryContact>,
356        classes: HashMap<ResourceClassName, ResourceClassObjects>,
357        deprecated_repos: Vec<DeprecatedRepository>,
358    ) -> Self {
359        CaObjects {
360            ca,
361            repo,
362            classes,
363            deprecated_repos,
364        }
365    }
366
367    /// Returns all PublishedElements mapped to each RepositoryContact.
368    ///
369    /// There could be more than one repository, although usually there
370    /// isn't.
371    #[allow(clippy::mutable_key_type)]
372    pub fn repo_elements_map(
373        &self,
374    ) -> HashMap<RepositoryContact, Vec<PublishedFile>> {
375        let mut res = HashMap::new();
376
377        if let Some(repo) = &self.repo {
378            res.insert(repo.clone(), vec![]);
379
380            for resource_class_objects in self.classes.values() {
381                // Note the map 'res' will get entries for other (old)
382                // repositories if there are any keys with
383                // such repositories.
384                resource_class_objects.add_elements(&mut res, repo);
385            }
386        }
387
388        res
389    }
390
391    /// Returns all PublishedFiles in all repositories.
392    pub fn all_publish_elements(&self) -> Vec<PublishedFile> {
393        let mut all_elements = vec![];
394
395        // slightly inefficient since we drop the RepositoryContact keys
396        // again, but this leverages existing code.
397        for elements in self.repo_elements_map().values_mut() {
398            all_elements.append(elements);
399        }
400
401        all_elements
402    }
403
404    /// Returns an iterator over all deprecated repos.
405    pub fn deprecated_repos(
406        &self
407    ) -> impl Iterator<Item = &DeprecatedRepository> + '_ {
408        self.deprecated_repos.iter()
409    }
410
411    /// Removes a deprecated repository.
412    pub fn deprecated_repo_remove(&mut self, to_remove: &RepositoryContact) {
413        self.deprecated_repos.retain(|current| current.contact() != to_remove)
414    }
415
416    /// Increments the number of cleaning attempts for a deprecated repo.
417    pub fn deprecated_repo_inc_clean_attempts(
418        &mut self,
419        contact: &RepositoryContact,
420    ) {
421        for current in self.deprecated_repos.iter_mut() {
422            if current.contact() == contact {
423                current.inc_clean_attempts()
424            }
425        }
426    }
427}
428
429/// # Actions invoked by the event listener
430///
431/// Most of these methods are simple enough that they could be rolled into
432/// the event listener directly. However, we plan to get rid of listening
433/// altogether and these methods will somehow be called in `CertAuths` command
434/// processing directly. So we might as well keep them for now.
435impl CaObjects {
436    /// Adds a new resource class.
437    ///
438    /// This returns an error in case the class already exists.
439    fn add_class(
440        &mut self,
441        class_name: &ResourceClassName,
442        key: &CertifiedKey,
443        timing: &IssuanceTimingConfig,
444        signer: &KrillSigner,
445    ) -> KrillResult<()> {
446        if self.classes.contains_key(class_name) {
447            return Err(Error::publishing("Duplicate resource class"))
448        }
449
450        self.classes.insert(
451            class_name.clone(),
452            ResourceClassObjects::create(key, timing, signer)?,
453        );
454        Ok(())
455    }
456
457    /// Removes a resource class.
458    fn remove_class(&mut self, class_name: &ResourceClassName) {
459        let old_repo_opt = self.classes.get(class_name).and_then(|rco| {
460            rco.old_repo()
461        }).cloned();
462
463        self.classes.remove(class_name);
464
465        if let Some(old_repo) = old_repo_opt {
466            self.deprecate_repo_if_no_longer_used(old_repo);
467        }
468    }
469
470    /// Returns the class objects for the given class.
471    ///
472    /// Returns an error if they don’t exist.
473    fn get_class_mut(
474        &mut self,
475        rcn: &ResourceClassName,
476    ) -> KrillResult<&mut ResourceClassObjects> {
477        self.classes.get_mut(rcn).ok_or_else(|| {
478            Error::publishing("Missing resource class")
479        })
480    }
481
482    /// Adds a staging key to the set.
483    ///
484    /// This will fail in case the class is missing, or in case the class is
485    /// not in state 'current'.
486    fn keyroll_stage(
487        &mut self,
488        rcn: &ResourceClassName,
489        key: &CertifiedKey,
490        timing: &IssuanceTimingConfig,
491        signer: &KrillSigner,
492    ) -> KrillResult<()> {
493        self.get_class_mut(rcn)?.keyroll_stage(key, timing, signer)
494    }
495
496    /// Activates the keyset.
497    ///
498    /// Retires the current set and promotes the staging set to current.
499    fn keyroll_activate(
500        &mut self,
501        rcn: &ResourceClassName,
502    ) -> KrillResult<()> {
503        self.get_class_mut(rcn)?.keyroll_activate()
504    }
505
506    /// Finishes a keyroll
507    fn keyroll_finish(&mut self, rcn: &ResourceClassName) -> KrillResult<()> {
508        let resource_class_objects = self.get_class_mut(rcn)?;
509
510        // finish the key roll for this resource class objects. This will
511        // remove the old key, and return an old_repo if there was
512        // one.
513        if let Some(old_repo) = resource_class_objects.keyroll_finish()? {
514            self.deprecate_repo_if_no_longer_used(old_repo);
515        }
516
517        Ok(())
518    }
519
520    /// Updates the ROAs in the current set
521    fn update_roas(
522        &mut self,
523        rcn: &ResourceClassName,
524        roa_updates: &RoaUpdates,
525    ) -> KrillResult<()> {
526        self.get_class_mut(rcn)
527            .map(|rco| rco.update_roas(roa_updates))
528    }
529
530    /// Updates the ASPAs in the current set
531    fn update_aspas(
532        &mut self,
533        rcn: &ResourceClassName,
534        updates: &AspaObjectsUpdates,
535    ) -> KrillResult<()> {
536        self.get_class_mut(rcn).map(|rco| rco.update_aspas(updates))
537    }
538
539    /// Updates the BGPSec certificates in the current set
540    fn update_bgpsec_certs(
541        &mut self,
542        rcn: &ResourceClassName,
543        updates: &BgpSecCertificateUpdates,
544    ) -> KrillResult<()> {
545        self.get_class_mut(rcn).map(|rco| rco.update_bgpsec_certs(updates))
546    }
547
548    /// Updates the issued certificates in the current set
549    fn update_certs(
550        &mut self,
551        rcn: &ResourceClassName,
552        cert_updates: &ChildCertificateUpdates,
553    ) -> KrillResult<()> {
554        self.get_class_mut(rcn).map(|rco| rco.update_certs(cert_updates))
555    }
556
557    /// Updates the received certificate.
558    fn update_received_cert(
559        &mut self,
560        rcn: &ResourceClassName,
561        cert: &ReceivedCert,
562    ) -> KrillResult<()> {
563        self.get_class_mut(rcn)?.update_received_cert(cert)
564    }
565
566    /// Reissues the MFT and CRL
567    ///
568    /// If `force` is `true`, re-issuance will always be done. This is
569    /// to be used in case any of the content changed. Otherwise
570    /// re-issuance will only happen if it's close to the next update
571    /// time, or the AIA has changed. The latter may happen if the parent
572    /// migrated repositories.
573    fn re_issue(
574        &mut self,
575        force: bool,
576        timing: &IssuanceTimingConfig,
577        signer: &KrillSigner,
578    ) -> KrillResult<bool> {
579        let hours = timing.publish_hours_before_next();
580        let mut required = false;
581
582        for (_, resource_class_objects) in self.classes.iter_mut() {
583            if force || resource_class_objects.requires_re_issuance(hours) {
584                required = true;
585                resource_class_objects.reissue(timing, signer)?;
586            }
587        }
588
589        Ok(required)
590    }
591
592    /// Updates the repository.
593    ///
594    /// If the repository is being migrated, i.e. there already is a current
595    /// repository, then make sure that the current repository is preserved
596    /// as the old repository for existing keys.
597    fn update_repo(&mut self, repo: &RepositoryContact) {
598        if let Some(old) = &self.repo {
599            for resource_class_objects in self.classes.values_mut() {
600                resource_class_objects.set_old_repo(old);
601            }
602        }
603        self.repo = Some(repo.clone());
604    }
605
606    /// Marks a repository as deprecated unless it's in use by any key.
607    fn deprecate_repo_if_no_longer_used(
608        &mut self,
609        old_repo: RepositoryContact,
610    ) {
611        if !self.has_old_repo(&old_repo) {
612            self.deprecated_repos
613                .push(DeprecatedRepository::new(old_repo, 0));
614        }
615    }
616
617    fn has_old_repo(&self, old_repo: &RepositoryContact) -> bool {
618        self.classes.values().any(|rco| rco.has_old_repo(old_repo))
619    }
620}
621
622
623//------------ DeprecatedRepository ------------------------------------------
624
625/// A previously used repository that hasn’t been successfully cleaned out.
626//
627//  *Warning:* This type is used in stored state.
628#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
629pub struct DeprecatedRepository {
630    /// The repository contact.
631    contact: RepositoryContact,
632
633    /// The number of times we‘ve tried to clean it out.
634    clean_attempts: usize,
635}
636
637impl DeprecatedRepository {
638    /// Creates a new deprecated repository.
639    pub fn new(contact: RepositoryContact, clean_attempts: usize) -> Self {
640        DeprecatedRepository {
641            contact,
642            clean_attempts,
643        }
644    }
645
646    /// Returns the repository contact.
647    pub fn contact(&self) -> &RepositoryContact {
648        &self.contact
649    }
650
651    /// Converts the value into the repository contact.
652    pub fn into_contact(self) -> RepositoryContact {
653        self.contact
654    }
655
656    /// Returns the number of cleaning attempts.
657    pub fn clean_attempts(&self) -> usize {
658        self.clean_attempts
659    }
660
661    /// Increases the number of cleaning attempts.
662    pub fn inc_clean_attempts(&mut self) {
663        self.clean_attempts += 1;
664    }
665}
666
667
668//------------ ResourceClassObjects ------------------------------------------
669
670/// The objects for a resource class.
671//
672//  *Warning:* This type is used in stored state.
673#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
674pub struct ResourceClassObjects {
675    keys: ResourceClassKeyState,
676}
677
678impl ResourceClassObjects {
679    /// Creates a new value.
680    ///
681    /// This is only used by upgrades.
682    pub fn new(keys: ResourceClassKeyState) -> Self {
683        ResourceClassObjects { keys }
684    }
685
686    /// Creates a new resource class objects set.
687    fn create(
688        key: &CertifiedKey,
689        timing: &IssuanceTimingConfig,
690        signer: &KrillSigner,
691    ) -> KrillResult<Self> {
692        let current_set = KeyObjectSet::create(key, timing, signer)?;
693
694        Ok(ResourceClassObjects {
695            keys: ResourceClassKeyState::Current(CurrentKeyState {
696                current_set,
697            }),
698        })
699    }
700
701    /// Adds all the elements for this resource class to the map.
702    /// 
703    /// It will use the default repository, or an optional old
704    /// repository if any of the keys had one as part of a repository
705    /// migration.
706    #[allow(clippy::mutable_key_type)]
707    fn add_elements(
708        &self,
709        map: &mut HashMap<RepositoryContact, Vec<PublishedFile>>,
710        dflt_repo: &RepositoryContact,
711    ) {
712        match &self.keys {
713            ResourceClassKeyState::Current(state) => {
714                state.current_set.add_elements(map, dflt_repo)
715            }
716            ResourceClassKeyState::Staging(state) => {
717                state.current_set.add_elements(map, dflt_repo);
718                state.staging_set.add_elements(map, dflt_repo);
719            }
720            ResourceClassKeyState::Old(state) => {
721                state.current_set.add_elements(map, dflt_repo);
722                state.old_set.add_elements(map, dflt_repo);
723            }
724        }
725    }
726
727    fn keyroll_stage(
728        &mut self,
729        key: &CertifiedKey,
730        timing: &IssuanceTimingConfig,
731        signer: &KrillSigner,
732    ) -> KrillResult<()> {
733        let current_set = match &self.keys {
734            ResourceClassKeyState::Current(state) => {
735                state.current_set.clone()
736            }
737            _ => {
738                return Err(Error::publishing(
739                    "published resource class in the wrong key state",
740                ))
741            }
742        };
743
744        let staging_set = KeyObjectSet::create(key, timing, signer)?;
745
746        self.keys = ResourceClassKeyState::Staging(StagingKeyState {
747            staging_set,
748            current_set,
749        });
750
751        Ok(())
752    }
753
754    fn keyroll_activate(&mut self) -> KrillResult<()> {
755        self.keys = match &self.keys {
756            ResourceClassKeyState::Staging(state) => {
757                let old_set = state.current_set.retire()?;
758                let current_set = state.staging_set.clone();
759
760                ResourceClassKeyState::Old(OldKeyState {
761                    current_set,
762                    old_set,
763                })
764            }
765            _ => {
766                return Err(Error::publishing(
767                    "published resource class in the wrong key state",
768                ))
769            }
770        };
771
772        Ok(())
773    }
774
775    fn keyroll_finish(&mut self) -> KrillResult<Option<RepositoryContact>> {
776        match self.keys.clone() {
777            ResourceClassKeyState::Old(old) => {
778                let current_set = old.current_set;
779                self.keys = ResourceClassKeyState::current(current_set);
780                Ok(old.old_set.old_repo)
781            }
782            _ => Err(Error::publishing(
783                "published resource class in the wrong key state",
784            )),
785        }
786    }
787
788    fn update_received_cert(
789        &mut self,
790        updated_cert: &ReceivedCert,
791    ) -> KrillResult<()> {
792        self.keys.update_received_cert(updated_cert)
793    }
794
795    fn update_roas(&mut self, roa_updates: &RoaUpdates) {
796        match &mut self.keys {
797            ResourceClassKeyState::Current(state) => {
798                state.current_set.update_roas(roa_updates)
799            }
800            ResourceClassKeyState::Staging(state) => {
801                state.current_set.update_roas(roa_updates)
802            }
803            ResourceClassKeyState::Old(state) => {
804                state.current_set.update_roas(roa_updates)
805            }
806        }
807    }
808
809    fn update_aspas(&mut self, updates: &AspaObjectsUpdates) {
810        match &mut self.keys {
811            ResourceClassKeyState::Current(state) => {
812                state.current_set.update_aspas(updates)
813            }
814            ResourceClassKeyState::Staging(state) => {
815                state.current_set.update_aspas(updates)
816            }
817            ResourceClassKeyState::Old(state) => {
818                state.current_set.update_aspas(updates)
819            }
820        }
821    }
822
823    fn update_bgpsec_certs(&mut self, updates: &BgpSecCertificateUpdates) {
824        match &mut self.keys {
825            ResourceClassKeyState::Current(state) => {
826                state.current_set.update_bgpsec_certs(updates)
827            }
828            ResourceClassKeyState::Staging(state) => {
829                state.current_set.update_bgpsec_certs(updates)
830            }
831            ResourceClassKeyState::Old(state) => {
832                state.current_set.update_bgpsec_certs(updates)
833            }
834        }
835    }
836
837    fn update_certs(&mut self, cert_updates: &ChildCertificateUpdates) {
838        match &mut self.keys {
839            ResourceClassKeyState::Current(state) => {
840                state.current_set.update_certs(cert_updates)
841            }
842            ResourceClassKeyState::Staging(state) => {
843                state.current_set.update_certs(cert_updates)
844            }
845            ResourceClassKeyState::Old(state) => {
846                state.current_set.update_certs(cert_updates)
847            }
848        }
849    }
850
851    fn requires_re_issuance(&self, hours: i64) -> bool {
852        match &self.keys {
853            ResourceClassKeyState::Current(state) => {
854                state.current_set.requires_reissuance(hours)
855            }
856            ResourceClassKeyState::Old(state) => {
857                state.old_set.requires_reissuance(hours)
858                    || state.current_set.requires_reissuance(hours)
859            }
860            ResourceClassKeyState::Staging(state) => {
861                state.staging_set.requires_reissuance(hours)
862                    || state.current_set.requires_reissuance(hours)
863            }
864        }
865    }
866
867    fn reissue(
868        &mut self,
869        timing: &IssuanceTimingConfig,
870        signer: &KrillSigner,
871    ) -> KrillResult<()> {
872        match &mut self.keys {
873            ResourceClassKeyState::Current(state) => {
874                state.current_set.reissue(timing, signer)
875            }
876            ResourceClassKeyState::Staging(state) => {
877                state.staging_set.reissue(timing, signer)?;
878                state.current_set.reissue(timing, signer)
879            }
880            ResourceClassKeyState::Old(state) => {
881                state.old_set.reissue(timing, signer)?;
882                state.current_set.reissue(timing, signer)
883            }
884        }
885    }
886
887    fn set_old_repo(&mut self, repo: &RepositoryContact) {
888        match &mut self.keys {
889            ResourceClassKeyState::Current(state) => {
890                state.current_set.set_old_repo(repo)
891            }
892            ResourceClassKeyState::Staging(state) => {
893                state.staging_set.set_old_repo(repo);
894                state.current_set.set_old_repo(repo);
895            }
896            ResourceClassKeyState::Old(state) => {
897                state.old_set.set_old_repo(repo);
898                state.current_set.set_old_repo(repo);
899            }
900        }
901    }
902
903    fn has_old_repo(&self, repo: &RepositoryContact) -> bool {
904        match &self.keys {
905            ResourceClassKeyState::Current(state) => {
906                state.current_set.old_repo() == Some(repo)
907            }
908            ResourceClassKeyState::Staging(state) => {
909                state.staging_set.old_repo() == Some(repo)
910                    || state.current_set.old_repo() == Some(repo)
911            }
912            ResourceClassKeyState::Old(state) => {
913                state.old_set.old_repo() == Some(repo)
914                    || state.current_set.old_repo() == Some(repo)
915            }
916        }
917    }
918
919    fn old_repo(&self) -> Option<&RepositoryContact> {
920        // Note: we can only have 1 old repo, because new repositories can
921        // only be introduced when there is no key roll in progress.
922        // So, it's not possible to introduce a second repo until the
923        // previous old_repo is rolled out completely.
924        match &self.keys {
925            ResourceClassKeyState::Current(state) => {
926                state.current_set.old_repo()
927            }
928            ResourceClassKeyState::Staging(state) => state
929                .staging_set
930                .old_repo()
931                .or_else(|| state.current_set.old_repo()),
932            ResourceClassKeyState::Old(state) => state
933                .old_set
934                .old_repo()
935                .or_else(|| state.current_set.old_repo()),
936        }
937    }
938}
939
940
941//------------ ResourceClassKeyState -----------------------------------------
942
943//  *Warning:* This type is used in stored state.
944#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
945#[serde(tag = "type", rename_all = "snake_case")]
946pub enum ResourceClassKeyState {
947    Current(CurrentKeyState),
948    Staging(StagingKeyState),
949    Old(OldKeyState),
950}
951
952impl ResourceClassKeyState {
953    pub fn current(current_set: KeyObjectSet) -> Self {
954        ResourceClassKeyState::Current(CurrentKeyState { current_set })
955    }
956
957    pub fn staging(
958        staging_set: KeyObjectSet,
959        current_set: KeyObjectSet,
960    ) -> Self {
961        ResourceClassKeyState::Staging(StagingKeyState {
962            staging_set,
963            current_set,
964        })
965    }
966
967    pub fn old(current_set: KeyObjectSet, old_set: KeyObjectSet) -> Self {
968        ResourceClassKeyState::Old(OldKeyState {
969            current_set,
970            old_set,
971        })
972    }
973
974    fn update_received_cert(
975        &mut self,
976        cert: &ReceivedCert,
977    ) -> KrillResult<()> {
978        match self {
979            ResourceClassKeyState::Current(state) => {
980                state.current_set.update_signing_cert(cert)
981            }
982            ResourceClassKeyState::Staging(state) => {
983                if state.staging_set.update_signing_cert(cert).is_ok() {
984                    Ok(())
985                } else {
986                    state.current_set.update_signing_cert(cert)
987                }
988            }
989            ResourceClassKeyState::Old(state) => {
990                if state.old_set.update_signing_cert(cert).is_ok() {
991                    Ok(())
992                } else {
993                    state.current_set.update_signing_cert(cert)
994                }
995            }
996        }
997    }
998}
999
1000
1001//------------ CurrentKeyState -----------------------------------------------
1002
1003//  *Warning:* This type is used in stored state.
1004#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1005pub struct CurrentKeyState {
1006    current_set: KeyObjectSet,
1007}
1008
1009impl CurrentKeyState {
1010    pub fn new(current_set: KeyObjectSet) -> Self {
1011        CurrentKeyState { current_set }
1012    }
1013}
1014
1015//------------ StagingKeyState -----------------------------------------------
1016
1017//  *Warning:* This type is used in stored state.
1018#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1019pub struct StagingKeyState {
1020    staging_set: KeyObjectSet,
1021    current_set: KeyObjectSet,
1022}
1023
1024impl StagingKeyState {
1025    pub fn new(staging_set: KeyObjectSet, current_set: KeyObjectSet) -> Self {
1026        StagingKeyState {
1027            staging_set,
1028            current_set,
1029        }
1030    }
1031}
1032
1033
1034//------------ OldKeyState ---------------------------------------------------
1035
1036//  *Warning:* This type is used in stored state.
1037#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1038pub struct OldKeyState {
1039    current_set: KeyObjectSet,
1040    old_set: KeyObjectSet,
1041}
1042
1043impl OldKeyState {
1044    pub fn new(current_set: KeyObjectSet, old_set: KeyObjectSet) -> Self {
1045        OldKeyState {
1046            current_set,
1047            old_set,
1048        }
1049    }
1050}
1051
1052//------------ KeyObjectSet ------------------------------------------------
1053
1054/// Maintains the set of objects published for a key.
1055//
1056//  *Warning:* This type is used in stored state.
1057#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1058pub struct KeyObjectSet {
1059    /// The latest received certificate for the owning key.
1060    ///
1061    /// This is used when signing a new manifest and CRL.
1062    signing_cert: ReceivedCert,
1063
1064    /// The revision of the set.
1065    ///
1066    /// Its number and the "this update" and "next update" values used on the
1067    /// manifest and CRL.
1068    revision: ObjectSetRevision,
1069
1070    /// The revocations that need go on the CRL.
1071    ///
1072    /// The CRL object has no convenient access to this, so we keep that
1073    /// immutable. Whenever we re-issue, we create a new CRL using these
1074    /// revocations.
1075    ///
1076    /// When objects are replaced or removed we add a revocation. When
1077    /// publishing revocations for expired certificates are removed.
1078    revocations: Revocations,
1079
1080    /// The last manifest generated for this set.
1081    ///
1082    /// When a set is first created, we will have a manifest and a CRL, but
1083    /// it will have an empty map of "published_objects".
1084    ///
1085    /// A new manifest is generated when we re-issue the set. This may happen
1086    /// when published objects are added/updated/removed, or in case, well
1087    /// some time before, the manifest and CRL would expire.
1088    manifest: PublishedManifest,
1089
1090    /// The last CRL generated for this set.
1091    ///
1092    /// We always generate the manifest and CRL together. When we re-issue a
1093    /// set we first generate a new CRL which will revoke the previous
1094    /// manifest. The CRL (name and hash) is included in the new manifest.
1095    ///
1096    /// Strictly speaking this revocation could be considered redundant,
1097    /// because the new CRL will not be considered valid (hash mismatch)
1098    /// under the old manifest. So, a Relying Party will only consider
1099    /// the CRL when it is using the new manifest.
1100    crl: PublishedCrl,
1101
1102    /// The published objects of this set.
1103    ///
1104    /// Will be empty if the owning key is not "current". I.e., this is empty
1105    /// when a new KeyObjectSet is created (new staging key for a key roll,
1106    /// or the first certified key under a new resource class).
1107    ///
1108    /// The "current" key will see updates to the published objects.
1109    ///
1110    /// When a key becomes "old" - just before it is subsequently removed -
1111    /// when it is replaced as part of a key roll, then `retire` is called
1112    /// on the set: all objects are revoked, and then this becomes empty
1113    /// again.
1114    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
1115    published_objects: HashMap<ObjectName, PublishedObject>,
1116
1117    /// The old repository this key publishes to.
1118    ///
1119    /// We implement repository migration as a key roll where the new
1120    /// key uses the new (then default) repository. Existing keys will
1121    /// keep track of the old repository contact using this following
1122    /// field so that they can continue to publish / withdraw there,
1123    /// until they (the owning key) are complete removed when the key
1124    /// rollover is finished.
1125    #[serde(skip_serializing_if = "Option::is_none")]
1126    old_repo: Option<RepositoryContact>,
1127}
1128
1129impl KeyObjectSet {
1130    /// Creates a new key set from its components.
1131    ///
1132    /// This is only used by upgrades.
1133    pub fn new(
1134        signing_cert: ReceivedCert,
1135        revision: ObjectSetRevision,
1136        revocations: Revocations,
1137        manifest: PublishedManifest,
1138        crl: PublishedCrl,
1139        published_objects: HashMap<ObjectName, PublishedObject>,
1140        old_repo: Option<RepositoryContact>,
1141    ) -> Self {
1142        KeyObjectSet {
1143            signing_cert,
1144            revision,
1145            revocations,
1146            manifest,
1147            crl,
1148            published_objects,
1149            old_repo,
1150        }
1151    }
1152
1153    /// Creates a new key set for the given key.
1154    ///
1155    /// Creates an initial manifest and CRL but keeps the list of published
1156    /// objects empty.
1157    fn create(
1158        key: &CertifiedKey,
1159        timing: &IssuanceTimingConfig,
1160        signer: &KrillSigner,
1161    ) -> KrillResult<Self> {
1162        let signing_cert = key.incoming_cert().clone();
1163
1164        let signing_key = signing_cert.key_identifier();
1165        let issuer = signing_cert.subject.clone();
1166        let revocations = Revocations::default();
1167        let revision = ObjectSetRevision::create(timing.publish_next());
1168        let published_objects = HashMap::new();
1169
1170        let crl = PublishedCrl::build(
1171            signing_key,
1172            issuer,
1173            &revocations,
1174            revision,
1175            signer,
1176        )?;
1177        let manifest = ManifestBuilder::new(revision)
1178            .with_objects(&crl, &published_objects)
1179            .build_new_mft(&signing_cert, signer)
1180            .map(|m| m.into())?;
1181
1182
1183        Ok(KeyObjectSet {
1184            signing_cert,
1185            revision,
1186            revocations,
1187            manifest,
1188            crl,
1189            published_objects,
1190            old_repo: None,
1191        })
1192    }
1193
1194    /// Adds all the elements for this set to the map which is passed on.
1195    ///
1196    /// It will use the default repository unless this key had an old
1197    /// repository set - as part of repository migration.
1198    #[allow(clippy::mutable_key_type)]
1199    fn add_elements(
1200        &self,
1201        map: &mut HashMap<RepositoryContact, Vec<PublishedFile>>,
1202        dflt_repo: &RepositoryContact,
1203    ) {
1204        let repo = self.old_repo.as_ref().unwrap_or(dflt_repo);
1205
1206        let crl_uri = self.signing_cert.crl_uri();
1207        let mft_uri = self.signing_cert.mft_uri();
1208
1209        let elements = map.entry(repo.clone()).or_default();
1210
1211        elements.push(self.manifest.published_file(mft_uri));
1212        elements.push(self.crl.published_file(crl_uri));
1213
1214        for (name, object) in &self.published_objects {
1215            elements.push(PublishedFile {
1216                uri: self.signing_cert.uri_for_name(name),
1217                base64: object.base64.clone(),
1218            });
1219        }
1220    }
1221
1222    /// Returns whether the set needs re-issuance within the given hours.
1223    pub fn requires_reissuance(&self, hours: i64) -> bool {
1224        Time::now() > self.next_update() - Duration::hours(hours)
1225    }
1226
1227    /// Returns the next update time.
1228    pub fn next_update(&self) -> Time {
1229        self.revision.next_update
1230    }
1231
1232    /// Updates the signing certificate for the set.
1233    ///
1234    /// Returns an error in case the KeyIdentifiers don't match.
1235    fn update_signing_cert(
1236        &mut self,
1237        cert: &ReceivedCert,
1238    ) -> KrillResult<()> {
1239        if self.signing_cert.key_identifier() == cert.key_identifier() {
1240            self.signing_cert = cert.clone();
1241            Ok(())
1242        }
1243        else {
1244            Err(Error::PublishingObjects(format!(
1245                "received new cert for unknown key id: {}",
1246                cert.key_identifier()
1247            )))
1248        }
1249    }
1250
1251    /// Updates the ROAs.
1252    fn update_roas(&mut self, roa_updates: &RoaUpdates) {
1253        for (name, roa_info) in roa_updates.added_roas() {
1254            let published_object = PublishedObject::for_roa(
1255                name.clone(), roa_info
1256            );
1257            if let Some(old) = self.published_objects.insert(
1258                name, published_object
1259            ) {
1260                self.revocations.add(old.revoke());
1261            }
1262        }
1263        for name in roa_updates.removed_roas() {
1264            if let Some(old) = self.published_objects.remove(&name) {
1265                self.revocations.add(old.revoke());
1266            }
1267        }
1268    }
1269
1270    /// Updates the ASPAs.
1271    fn update_aspas(&mut self, updates: &AspaObjectsUpdates) {
1272        for aspa_info in updates.updated() {
1273            let name = ObjectName::aspa_from_customer(aspa_info.customer());
1274            let published_object =
1275                PublishedObject::for_aspa(name.clone(), aspa_info);
1276            if let Some(old) =
1277                self.published_objects.insert(name, published_object)
1278            {
1279                self.revocations.add(old.revoke());
1280            }
1281        }
1282        for removed in updates.removed() {
1283            let name = ObjectName::aspa_from_customer(*removed);
1284            if let Some(old) = self.published_objects.remove(&name) {
1285                self.revocations.add(old.revoke());
1286            }
1287        }
1288    }
1289
1290    /// Updates the BGPset router key certificates.
1291    fn update_bgpsec_certs(&mut self, updates: &BgpSecCertificateUpdates) {
1292        for bgpsec_cert_info in updates.updated() {
1293            let published_object =
1294                PublishedObject::for_bgpsec_cert_info(bgpsec_cert_info);
1295            if let Some(old) = self
1296                .published_objects
1297                .insert(bgpsec_cert_info.name(), published_object)
1298            {
1299                self.revocations.add(old.revoke());
1300            }
1301        }
1302
1303        for removed in updates.removed() {
1304            let name = ObjectName::from(removed);
1305            if let Some(old) = self.published_objects.remove(&name) {
1306                self.revocations.add(old.revoke());
1307            }
1308        }
1309    }
1310
1311    /// Updates the child CA certificates.
1312    fn update_certs(&mut self, cert_updates: &ChildCertificateUpdates) {
1313        for removed in &cert_updates.removed {
1314            let name = ObjectName::from_key(removed, "cer");
1315            if let Some(old) = self.published_objects.remove(&name) {
1316                self.revocations.add(old.revoke());
1317            }
1318        }
1319
1320        for issued in &cert_updates.issued {
1321            let published_object = PublishedObject::for_cert_info(issued);
1322            if let Some(old) = self
1323                .published_objects
1324                .insert(issued.name.clone(), published_object)
1325            {
1326                self.revocations.add(old.revoke());
1327            }
1328        }
1329
1330        // Since Krill 0.16 suspended certificates will reissued rather than 
1331        // unsuspended, so this does nothing anymore except for migrations.
1332        for cert in &cert_updates.unsuspended {
1333            let published_object = PublishedObject::for_cert_info(cert);
1334            self
1335                .published_objects
1336                .insert(cert.name.clone(), published_object);
1337        }
1338
1339        for suspended in &cert_updates.suspended {
1340            if let Some(old) = self.published_objects.remove(&suspended.name)
1341            {
1342                self.revocations.add(old.revoke());
1343            }
1344        }
1345    }
1346
1347    /// Re-issues manifest and CRL.
1348    fn reissue(
1349        &mut self,
1350        timing: &IssuanceTimingConfig,
1351        signer: &KrillSigner,
1352    ) -> KrillResult<()> {
1353        debug!(
1354            "Will re-issue for key: {}. Current revision: {} and next \
1355             update: {}",
1356            self.signing_cert.key_identifier(),
1357            self.revision.number,
1358            self.revision.next_update.to_rfc3339()
1359        );
1360
1361        self.revision.next(timing.publish_next(), None);
1362
1363        self.revocations.remove_expired();
1364        let signing_key = self.signing_cert.key_identifier();
1365        let issuer = self.signing_cert.subject.clone();
1366
1367        self.crl = PublishedCrl::build(
1368            signing_key,
1369            issuer,
1370            &self.revocations,
1371            self.revision,
1372            signer,
1373        )?;
1374
1375        self.manifest = ManifestBuilder::new(self.revision)
1376            .with_objects(&self.crl, &self.published_objects)
1377            .build_new_mft(&self.signing_cert, signer)
1378            .map(|m| m.into())?;
1379
1380        Ok(())
1381    }
1382
1383    /// Returns a retired key object set for this set.
1384    ///
1385    /// Revokes and retires all signed objects.
1386    fn retire(&self) -> KrillResult<KeyObjectSet> {
1387        let mut revocations = self.revocations.clone();
1388        for object in self.published_objects.values() {
1389            revocations.add(object.revoke());
1390        }
1391        revocations.remove_expired();
1392
1393        let retired_set = KeyObjectSet {
1394            signing_cert: self.signing_cert.clone(),
1395            revision: self.revision,
1396            revocations,
1397            manifest: self.manifest.clone(),
1398            crl: self.crl.clone(),
1399            published_objects: HashMap::new(),
1400            old_repo: self.old_repo.clone(),
1401        };
1402
1403        Ok(retired_set)
1404    }
1405
1406    /// Sets the old repository for this object set.
1407    fn set_old_repo(&mut self, repo: &RepositoryContact) {
1408        self.old_repo = Some(repo.clone())
1409    }
1410
1411    /// Returns the old repo for this object set if there is one.
1412    fn old_repo(&self) -> Option<&RepositoryContact> {
1413        self.old_repo.as_ref()
1414    }
1415}
1416
1417
1418//------------ ObjectSetRevision ---------------------------------------------
1419
1420/// The current revision information for a key object set.
1421//
1422//  *Warning:* This type is used in stored state.
1423#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
1424pub struct ObjectSetRevision {
1425    /// The manifest and CRL number.
1426    number: u64,
1427
1428    /// The issue time of this manifest and CRL.
1429    ///
1430    /// This is backdated 5 minutes to tolerate some clock skew.
1431    this_update: Time,
1432
1433    /// The next update of this manifest and CRL.
1434    next_update: Time,
1435}
1436
1437impl ObjectSetRevision {
1438    /// Creates a new revision with the given parameters.
1439    pub fn new(number: u64, this_update: Time, next_update: Time) -> Self {
1440        ObjectSetRevision {
1441            number,
1442            this_update,
1443            next_update,
1444        }
1445    }
1446
1447    /// Creates an initial revision.
1448    fn create(next_update: Time) -> Self {
1449        ObjectSetRevision {
1450            number: 1,
1451            this_update: Time::five_minutes_ago(),
1452            next_update,
1453        }
1454    }
1455
1456    pub fn number(&self) -> u64 {
1457        self.number
1458    }
1459
1460    pub fn this_update(&self) -> Time {
1461        self.this_update
1462    }
1463
1464    pub fn next_update(&self) -> Time {
1465        self.next_update
1466    }
1467
1468    /// Updates the revision to the following revision.
1469    pub fn next(
1470        &mut self,
1471        next_update: Time,
1472        mft_number_override: Option<u64>,
1473    ) {
1474        if let Some(forced_next) = mft_number_override {
1475            self.number = forced_next;
1476        } else {
1477            self.number += 1;
1478        }
1479        self.this_update = Time::five_minutes_ago();
1480        self.next_update = next_update;
1481    }
1482}
1483
1484//------------ PublishedCert -------------------------------------------------
1485
1486/// A published certificate.
1487pub type PublishedCert = IssuedCertificate;
1488
1489
1490//------------ PublishedItem -------------------------------------------------
1491
1492/// Any item published in the repository.
1493///
1494/// The concrete type of object is provided through the marker type `T`. This
1495/// is only used to make sure we add objects in the right place.
1496//
1497//  *Warning:* This type is used in stored state.
1498#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1499pub struct PublishedItem<T> {
1500    /// The name of the object.
1501    name: ObjectName,
1502
1503    /// The content of the object.
1504    base64: Base64,
1505
1506    /// The RRDP hash of the object.
1507    ///
1508    /// This is derived from `base64` but kept for faster access.
1509    hash: rrdp::Hash,
1510
1511    /// The serial number of the certificate the object is signed with.
1512    serial: Serial,
1513
1514    /// The expiry time of the object.
1515    expires: Time,
1516
1517    /// A marker for the object type.
1518    marker: std::marker::PhantomData<T>,
1519}
1520
1521impl<T> PublishedItem<T> {
1522    /// Creates a new published object.
1523    pub fn new(
1524        name: ObjectName,
1525        base64: Base64,
1526        serial: Serial,
1527        expires: Time,
1528    ) -> Self {
1529        let hash = base64.to_hash();
1530
1531        PublishedItem {
1532            name,
1533            base64,
1534            hash,
1535            serial,
1536            expires,
1537            marker: std::marker::PhantomData,
1538        }
1539    }
1540
1541    /// Returns a published file for the object.
1542    pub fn published_file(&self, uri: uri::Rsync) -> PublishedFile {
1543        PublishedFile { uri, base64: self.base64.clone() }
1544    }
1545
1546    /// Returns a revocation for the object.
1547    pub fn revoke(&self) -> Revocation {
1548        Revocation::new(self.serial, self.expires)
1549    }
1550}
1551
1552
1553//------------ PublishedManifest ---------------------------------------------
1554
1555/// A published manifest.
1556//
1557//  *Warning:* This type is used in stored state.
1558pub type PublishedManifest = PublishedItem<PublishedItemManifest>;
1559
1560#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1561pub struct PublishedItemManifest;
1562
1563impl From<Manifest> for PublishedManifest {
1564    fn from(mft: Manifest) -> Self {
1565        PublishedItem::new(
1566            ObjectName::from(&mft),
1567            Base64::from(&mft),
1568            mft.cert().serial_number(),
1569            mft.next_update(),
1570        )
1571    }
1572}
1573
1574
1575//------------ PublishedCrl --------------------------------------------------
1576
1577/// A published CRL.
1578//
1579//  *Warning:* This type is used in stored state.
1580pub type PublishedCrl = PublishedItem<PublishedItemCrl>;
1581
1582#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1583pub struct PublishedItemCrl;
1584
1585impl PublishedCrl {
1586    pub fn build(
1587        aki: KeyIdentifier,
1588        issuer: Name,
1589        revocations: &Revocations,
1590        revision: ObjectSetRevision,
1591        signer: &KrillSigner,
1592    ) -> KrillResult<Self> {
1593        let serial_number = Serial::from(revision.number);
1594
1595        let crl = TbsCertList::new(
1596            Default::default(),
1597            issuer,
1598            revision.this_update,
1599            revision.next_update,
1600            revocations.to_crl_entries(),
1601            aki,
1602            serial_number,
1603        );
1604
1605        let crl = signer.sign_crl(crl, &aki)?;
1606
1607        Ok(crl.into())
1608    }
1609}
1610
1611impl From<Crl> for PublishedCrl {
1612    fn from(crl: Crl) -> Self {
1613        PublishedItem::new(
1614            ObjectName::from(&crl),
1615            Base64::from(&crl),
1616            crl.crl_number(), // Just use this, we won't actually revoke CRLs
1617            crl.next_update(),
1618        )
1619    }
1620}
1621
1622//------------ PublishedObject -----------------------------------------------
1623
1624/// A generic published object.
1625//
1626//  *Warning:* This type is used in stored state.
1627pub type PublishedObject = PublishedItem<PublishedItemOther>;
1628
1629#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1630pub struct PublishedItemOther;
1631
1632impl PublishedObject {
1633    /// Creates a published ROA.
1634    pub fn for_roa(name: ObjectName, roa_info: &RoaInfo) -> Self {
1635        PublishedObject::new(
1636            name,
1637            roa_info.base64.clone(),
1638            roa_info.serial,
1639            roa_info.expires(),
1640        )
1641    }
1642
1643    /// Creates a published ASPA object.
1644    pub fn for_aspa(name: ObjectName, aspa_info: &AspaInfo) -> Self {
1645        PublishedObject::new(
1646            name,
1647            aspa_info.base64.clone(),
1648            aspa_info.serial,
1649            aspa_info.expires(),
1650        )
1651    }
1652
1653    /// Creates a published child CA certificate.
1654    pub fn for_cert_info<T>(cert: &CertInfo<T>) -> Self {
1655        PublishedObject::new(
1656            cert.name.clone(),
1657            cert.base64.clone(),
1658            cert.serial,
1659            cert.expires(),
1660        )
1661    }
1662
1663    /// Creates a published BGPsec router key certificate.
1664    pub fn for_bgpsec_cert_info(cert: &BgpSecCertInfo) -> Self {
1665        PublishedObject::new(
1666            cert.name(),
1667            cert.base64.clone(),
1668            cert.serial,
1669            cert.expires,
1670        )
1671    }
1672}
1673
1674
1675//------------ ManifestBuilder -----------------------------------------------
1676
1677/// A helper type to create a manifest.
1678#[allow(clippy::mutable_key_type)]
1679pub struct ManifestBuilder {
1680    /// The revision of the manifest.
1681    revision: ObjectSetRevision,
1682
1683    /// The entries of the manifest.
1684    entries: HashMap<ObjectName, rrdp::Hash>,
1685}
1686
1687impl ManifestBuilder {
1688    /// Creates a new builder with the given revision.
1689    pub fn new(revision: ObjectSetRevision) -> Self {
1690        ManifestBuilder {
1691            revision,
1692            entries: HashMap::new(),
1693        }
1694    }
1695
1696    /// Adds the given objects to the manifest’s entries.
1697    #[allow(clippy::mutable_key_type)]
1698    pub fn with_objects(
1699        mut self,
1700        crl: &PublishedCrl,
1701        published_objects: &HashMap<ObjectName, PublishedObject>,
1702    ) -> Self {
1703        // Add entry for CRL
1704        self.entries.insert(crl.name.clone(), crl.hash);
1705
1706        // Add other objects
1707        for (name, object) in published_objects {
1708            self.entries.insert(name.clone(), object.hash);
1709        }
1710
1711        self
1712    }
1713
1714    /// Buidlds a new manifest.
1715    pub fn build_new_mft(
1716        self,
1717        signing_cert: &ReceivedCert,
1718        signer: &KrillSigner,
1719    ) -> KrillResult<Manifest> {
1720        let mft_uri = signing_cert.mft_uri();
1721        let crl_uri = signing_cert.crl_uri();
1722
1723        let aia = &signing_cert.uri;
1724        let aki = signing_cert.key_identifier();
1725        let serial_number = Serial::from(self.revision.number);
1726
1727        let entries =
1728            self.entries.iter().map(|(k, v)| FileAndHash::new(k, v));
1729
1730        let manifest: Manifest = {
1731            let mft_content = ManifestContent::new(
1732                serial_number,
1733                self.revision.this_update,
1734                self.revision.next_update,
1735                DigestAlgorithm::default(),
1736                entries,
1737            );
1738            let mut object_builder = SignedObjectBuilder::new(
1739                signer.random_serial()?,
1740                Validity::new(
1741                    self.revision.this_update,
1742                    self.revision.next_update,
1743                ),
1744                crl_uri,
1745                aia.clone(),
1746                mft_uri,
1747            );
1748            object_builder.set_issuer(Some(signing_cert.subject.clone()));
1749            object_builder.set_signing_time(Time::now());
1750
1751            signer.sign_manifest(mft_content, object_builder, &aki)?
1752        };
1753
1754        Ok(manifest)
1755    }
1756}