1use {
4 crate::{
5 crypto::{self, HashAlgorithm, PrivateKey, PublicKey},
6 database::Database,
7 error::{Error, Result},
8 metadata::{
9 Delegation, DelegationsBuilder, Metadata, MetadataDescription, MetadataPath,
10 MetadataVersion, RawSignedMetadata, RawSignedMetadataSet, RawSignedMetadataSetBuilder,
11 RootMetadata, RootMetadataBuilder, SignedMetadataBuilder, SnapshotMetadata,
12 SnapshotMetadataBuilder, TargetDescription, TargetPath, TargetsMetadata,
13 TargetsMetadataBuilder, TimestampMetadata, TimestampMetadataBuilder,
14 },
15 pouf::Pouf,
16 repository::RepositoryStorage,
17 verify::Verified,
18 },
19 chrono::{DateTime, Duration, Utc},
20 futures_io::{AsyncRead, AsyncSeek},
21 futures_util::AsyncSeekExt as _,
22 std::{collections::HashMap, io::SeekFrom, marker::PhantomData},
23};
24
25mod private {
26 use super::*;
27
28 pub trait Sealed {}
32
33 impl Sealed for Root {}
34 impl<D: Pouf> Sealed for Targets<D> {}
35 impl<D: Pouf> Sealed for Snapshot<D> {}
36 impl<D: Pouf> Sealed for Timestamp<D> {}
37 impl<D: Pouf> Sealed for Done<D> {}
38}
39
40const DEFAULT_ROOT_EXPIRATION: Duration = Duration::days(365);
41const DEFAULT_TARGETS_EXPIRATION: Duration = Duration::days(90);
42const DEFAULT_SNAPSHOT_EXPIRATION: Duration = Duration::days(7);
43const DEFAULT_TIMESTAMP_EXPIRATION: Duration = Duration::days(1);
44
45pub trait State: private::Sealed {}
52
53#[doc(hidden)]
55pub struct Root {
56 builder: RootMetadataBuilder,
57}
58
59impl State for Root {}
60
61#[doc(hidden)]
63pub struct Targets<D: Pouf> {
64 staged_root: Option<Staged<D, RootMetadata>>,
65 targets: HashMap<TargetPath, TargetDescription>,
66 delegation_keys: Vec<PublicKey>,
67 delegation_roles: Vec<Delegation>,
68 file_hash_algorithms: Vec<HashAlgorithm>,
69 inherit_from_trusted_targets: bool,
70}
71
72impl<D: Pouf> Targets<D> {
73 fn new(staged_root: Option<Staged<D, RootMetadata>>) -> Self {
74 Self {
75 staged_root,
76 targets: HashMap::new(),
77 delegation_keys: vec![],
78 delegation_roles: vec![],
79 file_hash_algorithms: vec![HashAlgorithm::Sha256],
80 inherit_from_trusted_targets: true,
81 }
82 }
83}
84
85impl<D: Pouf> State for Targets<D> {}
86
87#[doc(hidden)]
89pub struct Snapshot<D: Pouf> {
90 staged_root: Option<Staged<D, RootMetadata>>,
91 staged_targets: Option<Staged<D, TargetsMetadata>>,
92 include_targets_length: bool,
93 targets_hash_algorithms: Vec<HashAlgorithm>,
94 inherit_from_trusted_snapshot: bool,
95}
96
97impl<D: Pouf> State for Snapshot<D> {}
98
99impl<D: Pouf> Snapshot<D> {
100 fn new(
101 staged_root: Option<Staged<D, RootMetadata>>,
102 staged_targets: Option<Staged<D, TargetsMetadata>>,
103 ) -> Self {
104 Self {
105 staged_root,
106 staged_targets,
107 include_targets_length: false,
108 targets_hash_algorithms: vec![],
109 inherit_from_trusted_snapshot: true,
110 }
111 }
112
113 fn targets_description(&self) -> Result<Option<MetadataDescription<TargetsMetadata>>> {
114 if let Some(ref targets) = self.staged_targets {
115 let length = if self.include_targets_length {
116 Some(targets.raw.as_bytes().len())
117 } else {
118 None
119 };
120
121 let hashes = if self.targets_hash_algorithms.is_empty() {
122 HashMap::new()
123 } else {
124 crypto::calculate_hashes_from_slice(
125 targets.raw.as_bytes(),
126 &self.targets_hash_algorithms,
127 )?
128 };
129
130 Ok(Some(MetadataDescription::new(
131 targets.metadata.version(),
132 length,
133 hashes,
134 )?))
135 } else {
136 Ok(None)
137 }
138 }
139}
140
141pub struct Timestamp<D: Pouf> {
143 staged_root: Option<Staged<D, RootMetadata>>,
144 staged_targets: Option<Staged<D, TargetsMetadata>>,
145 staged_snapshot: Option<Staged<D, SnapshotMetadata>>,
146 include_snapshot_length: bool,
147 snapshot_hash_algorithms: Vec<HashAlgorithm>,
148}
149
150impl<D: Pouf> Timestamp<D> {
151 fn new(state: Snapshot<D>, staged_snapshot: Option<Staged<D, SnapshotMetadata>>) -> Self {
152 Self {
153 staged_root: state.staged_root,
154 staged_targets: state.staged_targets,
155 staged_snapshot,
156 include_snapshot_length: false,
157 snapshot_hash_algorithms: vec![],
158 }
159 }
160
161 fn snapshot_description(&self) -> Result<Option<MetadataDescription<SnapshotMetadata>>> {
162 if let Some(ref snapshot) = self.staged_snapshot {
163 let length = if self.include_snapshot_length {
164 Some(snapshot.raw.as_bytes().len())
165 } else {
166 None
167 };
168
169 let hashes = if self.snapshot_hash_algorithms.is_empty() {
170 HashMap::new()
171 } else {
172 crypto::calculate_hashes_from_slice(
173 snapshot.raw.as_bytes(),
174 &self.snapshot_hash_algorithms,
175 )?
176 };
177
178 Ok(Some(MetadataDescription::new(
179 snapshot.metadata.version(),
180 length,
181 hashes,
182 )?))
183 } else {
184 Ok(None)
185 }
186 }
187}
188
189impl<D: Pouf> State for Timestamp<D> {}
190
191pub struct Done<D: Pouf> {
193 staged_root: Option<Staged<D, RootMetadata>>,
194 staged_targets: Option<Staged<D, TargetsMetadata>>,
195 staged_snapshot: Option<Staged<D, SnapshotMetadata>>,
196 staged_timestamp: Option<Staged<D, TimestampMetadata>>,
197}
198
199impl<D: Pouf> State for Done<D> {}
200
201struct Staged<D: Pouf, M: Metadata> {
202 metadata: M,
203 raw: RawSignedMetadata<D, M>,
204}
205
206struct RepoContext<'a, D, R>
207where
208 D: Pouf,
209 R: RepositoryStorage<D>,
210{
211 repo: R,
212 db: Option<&'a Database<D>>,
213 current_time: DateTime<Utc>,
214 signing_root_keys: Vec<&'a dyn PrivateKey>,
215 signing_targets_keys: Vec<&'a dyn PrivateKey>,
216 signing_snapshot_keys: Vec<&'a dyn PrivateKey>,
217 signing_timestamp_keys: Vec<&'a dyn PrivateKey>,
218 trusted_root_keys: Vec<&'a dyn PrivateKey>,
219 trusted_targets_keys: Vec<&'a dyn PrivateKey>,
220 trusted_snapshot_keys: Vec<&'a dyn PrivateKey>,
221 trusted_timestamp_keys: Vec<&'a dyn PrivateKey>,
222 time_version: Option<u32>,
223 root_expiration_duration: Duration,
224 targets_expiration_duration: Duration,
225 snapshot_expiration_duration: Duration,
226 timestamp_expiration_duration: Duration,
227 _pouf: PhantomData<D>,
228}
229
230impl<D, R> RepoContext<'_, D, R>
231where
232 D: Pouf,
233 R: RepositoryStorage<D>,
234{
235 fn root_keys_changed(&self, root: &Verified<RootMetadata>) -> bool {
236 let root_keys_count = root.root_keys().count();
237 if root_keys_count != self.trusted_root_keys.len() {
238 return true;
239 }
240
241 for key in &self.trusted_root_keys {
242 if root.root().key_ids().get(key.public().key_id()).is_none() {
243 return true;
244 }
245 }
246
247 false
248 }
249 fn targets_keys_changed(&self, root: &Verified<RootMetadata>) -> bool {
250 for key in &self.trusted_targets_keys {
251 if root
252 .targets()
253 .key_ids()
254 .get(key.public().key_id())
255 .is_none()
256 {
257 return true;
258 }
259 }
260
261 false
262 }
263
264 fn snapshot_keys_changed(&self, root: &Verified<RootMetadata>) -> bool {
265 for key in &self.trusted_snapshot_keys {
266 if root
267 .snapshot()
268 .key_ids()
269 .get(key.public().key_id())
270 .is_none()
271 {
272 return true;
273 }
274 }
275
276 false
277 }
278
279 fn timestamp_keys_changed(&self, root: &Verified<RootMetadata>) -> bool {
280 for key in &self.trusted_timestamp_keys {
281 if root
282 .timestamp()
283 .key_ids()
284 .get(key.public().key_id())
285 .is_none()
286 {
287 return true;
288 }
289 }
290
291 false
292 }
293
294 fn non_root_initial_version(&self) -> u32 {
296 self.time_version.unwrap_or(1)
297 }
298
299 fn update_time_version(&mut self) {
303 let timestamp = self.current_time.timestamp();
306 if timestamp > 0 {
307 self.time_version = timestamp.try_into().ok();
308 } else {
309 self.time_version = None;
310 }
311 }
312
313 fn non_root_next_version(
315 &self,
316 current_version: u32,
317 path: fn() -> MetadataPath,
318 ) -> Result<u32> {
319 if let Some(time_version) = self.time_version {
320 if current_version < time_version {
323 return Ok(time_version);
324 }
325 }
326
327 current_version
328 .checked_add(1)
329 .ok_or_else(|| Error::MetadataVersionMustBeSmallerThanMaxU32(path()))
330 }
331}
332
333fn sign<'a, D, I, M>(meta: &M, keys: I) -> Result<RawSignedMetadata<D, M>>
334where
335 D: Pouf,
336 M: Metadata,
337 I: IntoIterator<Item = &'a &'a dyn PrivateKey>,
338{
339 let mut signed_builder = SignedMetadataBuilder::<D, _>::from_metadata(meta)?;
341 let mut has_key = false;
342 for key in keys {
343 has_key = true;
344 signed_builder = signed_builder.sign(*key)?;
345 }
346
347 if !has_key {
349 return Err(Error::MissingPrivateKey {
350 role: M::ROLE.into(),
351 });
352 }
353
354 signed_builder.build().to_raw()
355}
356
357pub struct RepoBuilder<'a, D, R, S = Root>
359where
360 D: Pouf,
361 R: RepositoryStorage<D>,
362 S: State,
363{
364 ctx: RepoContext<'a, D, R>,
365 state: S,
366}
367
368impl<'a, D, R> RepoBuilder<'a, D, R, Root>
369where
370 D: Pouf,
371 R: RepositoryStorage<D>,
372{
373 pub fn create(repo: R) -> Self {
405 Self {
406 ctx: RepoContext {
407 repo,
408 db: None,
409 current_time: Utc::now(),
410 signing_root_keys: vec![],
411 signing_targets_keys: vec![],
412 signing_snapshot_keys: vec![],
413 signing_timestamp_keys: vec![],
414 trusted_root_keys: vec![],
415 trusted_targets_keys: vec![],
416 trusted_snapshot_keys: vec![],
417 trusted_timestamp_keys: vec![],
418 time_version: None,
419 root_expiration_duration: DEFAULT_ROOT_EXPIRATION,
420 targets_expiration_duration: DEFAULT_TARGETS_EXPIRATION,
421 snapshot_expiration_duration: DEFAULT_SNAPSHOT_EXPIRATION,
422 timestamp_expiration_duration: DEFAULT_TIMESTAMP_EXPIRATION,
423 _pouf: PhantomData,
424 },
425 state: Root {
426 builder: RootMetadataBuilder::new()
427 .consistent_snapshot(true)
428 .root_threshold(1)
429 .targets_threshold(1)
430 .snapshot_threshold(1)
431 .timestamp_threshold(1),
432 },
433 }
434 }
435
436 pub fn from_database(repo: R, db: &'a Database<D>) -> Self {
483 let builder = {
484 let trusted_root = db.trusted_root();
485
486 RootMetadataBuilder::new()
487 .consistent_snapshot(trusted_root.consistent_snapshot())
488 .root_threshold(trusted_root.root().threshold())
489 .targets_threshold(trusted_root.targets().threshold())
490 .snapshot_threshold(trusted_root.snapshot().threshold())
491 .timestamp_threshold(trusted_root.timestamp().threshold())
492 };
493
494 Self {
495 ctx: RepoContext {
496 repo,
497 db: Some(db),
498 current_time: Utc::now(),
499 signing_root_keys: vec![],
500 signing_targets_keys: vec![],
501 signing_snapshot_keys: vec![],
502 signing_timestamp_keys: vec![],
503 trusted_root_keys: vec![],
504 trusted_targets_keys: vec![],
505 trusted_snapshot_keys: vec![],
506 trusted_timestamp_keys: vec![],
507 time_version: None,
508 root_expiration_duration: DEFAULT_ROOT_EXPIRATION,
509 targets_expiration_duration: DEFAULT_TARGETS_EXPIRATION,
510 snapshot_expiration_duration: DEFAULT_SNAPSHOT_EXPIRATION,
511 timestamp_expiration_duration: DEFAULT_TIMESTAMP_EXPIRATION,
512 _pouf: PhantomData,
513 },
514 state: Root { builder },
515 }
516 }
517
518 pub fn current_time(mut self, current_time: DateTime<Utc>) -> Self {
523 self.ctx.current_time = current_time;
524
525 if self.ctx.time_version.is_some() {
527 self.ctx.update_time_version();
528 }
529
530 self
531 }
532
533 pub fn time_versioning(mut self, time_versioning: bool) -> Self {
536 if time_versioning {
537 self.ctx.update_time_version();
538 } else {
539 self.ctx.time_version = None;
540 }
541 self
542 }
543
544 pub fn root_expiration_duration(mut self, duration: Duration) -> Self {
552 self.ctx.root_expiration_duration = duration;
553 self
554 }
555
556 pub fn targets_expiration_duration(mut self, duration: Duration) -> Self {
564 self.ctx.targets_expiration_duration = duration;
565 self
566 }
567
568 pub fn snapshot_expiration_duration(mut self, duration: Duration) -> Self {
576 self.ctx.snapshot_expiration_duration = duration;
577 self
578 }
579
580 pub fn timestamp_expiration_duration(mut self, duration: Duration) -> Self {
589 self.ctx.timestamp_expiration_duration = duration;
590 self
591 }
592
593 pub fn signing_root_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
596 for key in keys {
597 self.ctx.signing_root_keys.push(*key);
598 }
599 self
600 }
601
602 pub fn signing_targets_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
605 for key in keys {
606 self.ctx.signing_targets_keys.push(*key);
607 }
608 self
609 }
610
611 pub fn signing_snapshot_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
614 for key in keys {
615 self.ctx.signing_snapshot_keys.push(*key);
616 }
617 self
618 }
619
620 pub fn signing_timestamp_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
623 for key in keys {
624 self.ctx.signing_timestamp_keys.push(*key);
625 }
626 self
627 }
628
629 pub fn trusted_root_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
632 for key in keys {
633 self.ctx.trusted_root_keys.push(*key);
634 self.state.builder = self.state.builder.root_key(key.public().clone());
635 }
636 self
637 }
638
639 pub fn trusted_targets_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
642 for key in keys {
643 self.ctx.trusted_targets_keys.push(*key);
644 self.state.builder = self.state.builder.targets_key(key.public().clone());
645 }
646 self
647 }
648
649 pub fn trusted_snapshot_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
652 for key in keys {
653 self.ctx.trusted_snapshot_keys.push(*key);
654 self.state.builder = self.state.builder.snapshot_key(key.public().clone());
655 }
656 self
657 }
658
659 pub fn trusted_timestamp_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
662 for key in keys {
663 self.ctx.trusted_timestamp_keys.push(*key);
664 self.state.builder = self.state.builder.timestamp_key(key.public().clone());
665 }
666 self
667 }
668
669 pub fn stage_root(self) -> Result<RepoBuilder<'a, D, R, Targets<D>>> {
691 self.stage_root_with_builder(|builder| builder)
692 }
693
694 pub fn stage_root_if_necessary(self) -> Result<RepoBuilder<'a, D, R, Targets<D>>> {
700 if self.need_new_root() {
701 self.stage_root()
702 } else {
703 Ok(self.skip_root())
704 }
705 }
706
707 pub fn skip_root(self) -> RepoBuilder<'a, D, R, Targets<D>> {
710 RepoBuilder {
711 ctx: self.ctx,
712 state: Targets::new(None),
713 }
714 }
715
716 pub fn stage_root_with_builder<F>(self, f: F) -> Result<RepoBuilder<'a, D, R, Targets<D>>>
740 where
741 F: FnOnce(RootMetadataBuilder) -> RootMetadataBuilder,
742 {
743 let next_version = if let Some(db) = self.ctx.db {
744 db.trusted_root().version().checked_add(1).ok_or_else(|| {
745 Error::MetadataVersionMustBeSmallerThanMaxU32(MetadataPath::root())
746 })?
747 } else {
748 1
749 };
750
751 let root_builder = self
752 .state
753 .builder
754 .version(next_version)
755 .expires(self.ctx.current_time + self.ctx.root_expiration_duration);
756 let root = f(root_builder).build()?;
757
758 let raw_root = sign(
759 &root,
760 self.ctx
761 .signing_root_keys
762 .iter()
763 .chain(&self.ctx.trusted_root_keys),
764 )?;
765
766 Ok(RepoBuilder {
767 ctx: self.ctx,
768 state: Targets::new(Some(Staged {
769 metadata: root,
770 raw: raw_root,
771 })),
772 })
773 }
774
775 pub async fn add_target<Rd>(
782 self,
783 target_path: TargetPath,
784 reader: Rd,
785 ) -> Result<RepoBuilder<'a, D, R, Targets<D>>>
786 where
787 Rd: AsyncRead + AsyncSeek + Unpin + Send,
788 {
789 self.stage_root_if_necessary()?
790 .add_target(target_path, reader)
791 .await
792 }
793
794 pub async fn commit(self) -> Result<RawSignedMetadataSet<D>> {
800 self.stage_root_if_necessary()?.commit().await
801 }
802
803 fn need_new_root(&self) -> bool {
805 let trusted_root = if let Some(db) = self.ctx.db {
807 db.trusted_root()
808 } else {
809 return true;
810 };
811
812 if trusted_root.expires() <= &self.ctx.current_time {
814 return true;
815 }
816
817 if !self.ctx.signing_root_keys.is_empty() {
819 return true;
820 }
821
822 self.ctx.root_keys_changed(trusted_root)
824 || self.ctx.targets_keys_changed(trusted_root)
825 || self.ctx.snapshot_keys_changed(trusted_root)
826 || self.ctx.timestamp_keys_changed(trusted_root)
827 }
828}
829
830impl<'a, D, R> RepoBuilder<'a, D, R, Targets<D>>
831where
832 D: Pouf,
833 R: RepositoryStorage<D>,
834{
835 pub fn target_hash_algorithms(mut self, algorithms: &[HashAlgorithm]) -> Self {
840 self.state.file_hash_algorithms = algorithms.to_vec();
841 self
842 }
843
844 pub fn inherit_from_trusted_targets(mut self, inherit: bool) -> Self {
849 self.state.inherit_from_trusted_targets = inherit;
850 self
851 }
852
853 pub fn stage_targets(self) -> Result<RepoBuilder<'a, D, R, Snapshot<D>>> {
855 self.stage_targets_with_builder(|builder| builder)
856 }
857
858 pub fn stage_targets_if_necessary(self) -> Result<RepoBuilder<'a, D, R, Snapshot<D>>> {
863 if self.need_new_targets() {
864 self.stage_targets_with_builder(|builder| builder)
865 } else {
866 Ok(self.skip_targets())
867 }
868 }
869
870 pub fn skip_targets(self) -> RepoBuilder<'a, D, R, Snapshot<D>> {
872 RepoBuilder {
873 ctx: self.ctx,
874 state: Snapshot::new(self.state.staged_root, None),
875 }
876 }
877
878 pub async fn add_target<Rd>(
883 self,
884 target_path: TargetPath,
885 reader: Rd,
886 ) -> Result<RepoBuilder<'a, D, R, Targets<D>>>
887 where
888 Rd: AsyncRead + AsyncSeek + Unpin + Send,
889 {
890 self.add_target_with_custom(target_path, reader, HashMap::new())
891 .await
892 }
893
894 pub async fn add_target_with_custom<Rd>(
899 mut self,
900 target_path: TargetPath,
901 mut reader: Rd,
902 custom: HashMap<String, serde_json::Value>,
903 ) -> Result<RepoBuilder<'a, D, R, Targets<D>>>
904 where
905 Rd: AsyncRead + AsyncSeek + Unpin + Send,
906 {
907 let consistent_snapshot = if let Some(ref staged_root) = self.state.staged_root {
908 staged_root.metadata.consistent_snapshot()
909 } else if let Some(db) = self.ctx.db {
910 db.trusted_root().consistent_snapshot()
911 } else {
912 return Err(Error::MetadataNotFound {
913 path: MetadataPath::root(),
914 version: MetadataVersion::None,
915 });
916 };
917
918 let target_description = TargetDescription::from_reader_with_custom(
919 &mut reader,
920 &self.state.file_hash_algorithms,
921 custom,
922 )
923 .await?;
924
925 if consistent_snapshot {
928 for digest in target_description.hashes().values() {
929 reader.seek(SeekFrom::Start(0)).await?;
930
931 let hash_prefixed_path = target_path.with_hash_prefix(digest)?;
932
933 self.ctx
934 .repo
935 .store_target(&hash_prefixed_path, &mut reader)
936 .await?;
937 }
938 } else {
939 reader.seek(SeekFrom::Start(0)).await?;
940
941 self.ctx
942 .repo
943 .store_target(&target_path, &mut reader)
944 .await?;
945 }
946
947 self.state.targets.insert(target_path, target_description);
948
949 Ok(self)
950 }
951
952 pub fn add_delegation_key(mut self, key: PublicKey) -> Self {
954 self.state.delegation_keys.push(key);
955 self
956 }
957
958 pub fn add_delegation_role(mut self, delegation: Delegation) -> Self {
960 self.state.delegation_roles.push(delegation);
961 self
962 }
963
964 pub fn stage_targets_with_builder<F>(self, f: F) -> Result<RepoBuilder<'a, D, R, Snapshot<D>>>
973 where
974 F: FnOnce(TargetsMetadataBuilder) -> TargetsMetadataBuilder,
975 {
976 let mut targets_builder = TargetsMetadataBuilder::new()
977 .expires(self.ctx.current_time + self.ctx.targets_expiration_duration);
978
979 let mut delegations_builder = DelegationsBuilder::new();
980
981 if let Some(trusted_targets) = self.ctx.db.and_then(|db| db.trusted_targets()) {
982 let next_version = self
983 .ctx
984 .non_root_next_version(trusted_targets.version(), MetadataPath::targets)?;
985
986 targets_builder = targets_builder.version(next_version);
987
988 if self.state.inherit_from_trusted_targets {
990 for (target_path, target_description) in trusted_targets.targets() {
991 targets_builder = targets_builder
992 .insert_target_description(target_path.clone(), target_description.clone());
993 }
994
995 for key in trusted_targets.delegations().keys().values() {
996 delegations_builder = delegations_builder.key(key.clone());
997 }
998
999 for role in trusted_targets.delegations().roles() {
1000 delegations_builder = delegations_builder.role(role.clone());
1001 }
1002 }
1003 } else {
1004 targets_builder = targets_builder.version(self.ctx.non_root_initial_version());
1005 }
1006
1007 for (target_path, target_description) in self.state.targets {
1009 targets_builder = targets_builder
1010 .insert_target_description(target_path.clone(), target_description.clone());
1011 }
1012
1013 for key in self.state.delegation_keys {
1015 delegations_builder = delegations_builder.key(key);
1016 }
1017
1018 for role in self.state.delegation_roles {
1020 delegations_builder = delegations_builder.role(role);
1021 }
1022
1023 targets_builder = targets_builder.delegations(delegations_builder.build()?);
1024
1025 let targets = f(targets_builder).build()?;
1026
1027 let raw_targets = sign(
1029 &targets,
1030 self.ctx
1031 .signing_targets_keys
1032 .iter()
1033 .chain(&self.ctx.trusted_targets_keys),
1034 )?;
1035
1036 Ok(RepoBuilder {
1037 ctx: self.ctx,
1038 state: Snapshot::new(
1039 self.state.staged_root,
1040 Some(Staged {
1041 metadata: targets,
1042 raw: raw_targets,
1043 }),
1044 ),
1045 })
1046 }
1047
1048 pub async fn commit(self) -> Result<RawSignedMetadataSet<D>> {
1054 self.stage_targets_if_necessary()?.commit().await
1055 }
1056
1057 fn need_new_targets(&self) -> bool {
1058 if !self.state.targets.is_empty() {
1060 return true;
1061 }
1062
1063 if self.state.staged_root.is_some() {
1065 return true;
1066 }
1067
1068 let db = if let Some(ref db) = self.ctx.db {
1070 db
1071 } else {
1072 return true;
1073 };
1074
1075 let trusted_targets = if let Some(trusted_targets) = db.trusted_targets() {
1077 trusted_targets
1078 } else {
1079 return true;
1080 };
1081
1082 if trusted_targets.expires() <= &self.ctx.current_time {
1084 return true;
1085 }
1086
1087 self.ctx.targets_keys_changed(db.trusted_root())
1089 }
1090}
1091
1092impl<'a, D, R> RepoBuilder<'a, D, R, Snapshot<D>>
1093where
1094 D: Pouf,
1095 R: RepositoryStorage<D>,
1096{
1097 pub fn snapshot_includes_length(mut self, include_targets_lengths: bool) -> Self {
1102 self.state.include_targets_length = include_targets_lengths;
1103 self
1104 }
1105
1106 pub fn snapshot_includes_hashes(mut self, hashes: &[HashAlgorithm]) -> Self {
1111 self.state.targets_hash_algorithms = hashes.to_vec();
1112 self
1113 }
1114
1115 pub fn inherit_from_trusted_snapshot(mut self, inherit: bool) -> Self {
1119 self.state.inherit_from_trusted_snapshot = inherit;
1120 self
1121 }
1122
1123 pub fn stage_snapshot(self) -> Result<RepoBuilder<'a, D, R, Timestamp<D>>> {
1125 self.stage_snapshot_with_builder(|builder| builder)
1126 }
1127
1128 pub fn stage_snapshot_if_necessary(self) -> Result<RepoBuilder<'a, D, R, Timestamp<D>>> {
1133 if self.need_new_snapshot() {
1134 self.stage_snapshot()
1135 } else {
1136 Ok(self.skip_snapshot())
1137 }
1138 }
1139
1140 pub fn skip_snapshot(self) -> RepoBuilder<'a, D, R, Timestamp<D>> {
1142 RepoBuilder {
1143 ctx: self.ctx,
1144 state: Timestamp::new(self.state, None),
1145 }
1146 }
1147
1148 pub fn stage_snapshot_with_builder<F>(self, f: F) -> Result<RepoBuilder<'a, D, R, Timestamp<D>>>
1157 where
1158 F: FnOnce(SnapshotMetadataBuilder) -> SnapshotMetadataBuilder,
1159 {
1160 let mut snapshot_builder = SnapshotMetadataBuilder::new()
1161 .expires(self.ctx.current_time + self.ctx.snapshot_expiration_duration);
1162
1163 if let Some(trusted_snapshot) = self.ctx.db.and_then(|db| db.trusted_snapshot()) {
1164 let next_version = self
1165 .ctx
1166 .non_root_next_version(trusted_snapshot.version(), MetadataPath::snapshot)?;
1167
1168 snapshot_builder = snapshot_builder.version(next_version);
1169
1170 if self.state.inherit_from_trusted_snapshot {
1172 for (path, description) in trusted_snapshot.meta() {
1173 snapshot_builder = snapshot_builder
1174 .insert_metadata_description(path.clone(), description.clone());
1175 }
1176 }
1177 } else {
1178 snapshot_builder = snapshot_builder.version(self.ctx.non_root_initial_version());
1179 }
1180
1181 if let Some(targets_description) = self.state.targets_description()? {
1183 snapshot_builder = snapshot_builder
1184 .insert_metadata_description(MetadataPath::targets(), targets_description);
1185 };
1186
1187 let snapshot = f(snapshot_builder).build()?;
1188 let raw_snapshot = sign(
1189 &snapshot,
1190 self.ctx
1191 .signing_snapshot_keys
1192 .iter()
1193 .chain(&self.ctx.trusted_snapshot_keys),
1194 )?;
1195
1196 Ok(RepoBuilder {
1197 ctx: self.ctx,
1198 state: Timestamp::new(
1199 self.state,
1200 Some(Staged {
1201 metadata: snapshot,
1202 raw: raw_snapshot,
1203 }),
1204 ),
1205 })
1206 }
1207
1208 pub async fn commit(self) -> Result<RawSignedMetadataSet<D>> {
1214 self.stage_snapshot_if_necessary()?.commit().await
1215 }
1216
1217 fn need_new_snapshot(&self) -> bool {
1218 if self.state.staged_root.is_some() {
1220 return true;
1221 }
1222
1223 if self.state.staged_targets.is_some() {
1225 return true;
1226 }
1227
1228 let db = if let Some(ref db) = self.ctx.db {
1230 db
1231 } else {
1232 return true;
1233 };
1234
1235 let trusted_snapshot = if let Some(trusted_snapshot) = db.trusted_snapshot() {
1237 trusted_snapshot
1238 } else {
1239 return true;
1240 };
1241
1242 if trusted_snapshot.expires() <= &self.ctx.current_time {
1244 return true;
1245 }
1246
1247 self.ctx.snapshot_keys_changed(db.trusted_root())
1249 }
1250}
1251
1252impl<'a, D, R> RepoBuilder<'a, D, R, Timestamp<D>>
1253where
1254 D: Pouf,
1255 R: RepositoryStorage<D>,
1256{
1257 pub fn timestamp_includes_length(mut self, include_snapshot_lengths: bool) -> Self {
1260 self.state.include_snapshot_length = include_snapshot_lengths;
1261 self
1262 }
1263
1264 pub fn timestamp_includes_hashes(mut self, hashes: &[HashAlgorithm]) -> Self {
1267 self.state.snapshot_hash_algorithms = hashes.to_vec();
1268 self
1269 }
1270
1271 pub fn stage_timestamp(self) -> Result<RepoBuilder<'a, D, R, Done<D>>> {
1278 self.stage_timestamp_with_builder(|builder| builder)
1279 }
1280
1281 pub fn stage_timestamp_if_necessary(self) -> Result<RepoBuilder<'a, D, R, Done<D>>> {
1286 if self.need_new_timestamp() {
1287 self.stage_timestamp()
1288 } else {
1289 Ok(self.skip_timestamp())
1290 }
1291 }
1292
1293 pub fn skip_timestamp(self) -> RepoBuilder<'a, D, R, Done<D>> {
1295 RepoBuilder {
1296 ctx: self.ctx,
1297 state: Done {
1298 staged_root: self.state.staged_root,
1299 staged_targets: self.state.staged_targets,
1300 staged_snapshot: self.state.staged_snapshot,
1301 staged_timestamp: None,
1302 },
1303 }
1304 }
1305
1306 pub fn stage_timestamp_with_builder<F>(self, f: F) -> Result<RepoBuilder<'a, D, R, Done<D>>>
1315 where
1316 F: FnOnce(TimestampMetadataBuilder) -> TimestampMetadataBuilder,
1317 {
1318 let next_version = if let Some(db) = self.ctx.db {
1319 if let Some(trusted_timestamp) = db.trusted_timestamp() {
1320 self.ctx
1321 .non_root_next_version(trusted_timestamp.version(), MetadataPath::timestamp)?
1322 } else {
1323 self.ctx.non_root_initial_version()
1324 }
1325 } else {
1326 self.ctx.non_root_initial_version()
1327 };
1328
1329 let description = if let Some(description) = self.state.snapshot_description()? {
1330 description
1331 } else {
1332 self.ctx
1333 .db
1334 .and_then(|db| db.trusted_timestamp())
1335 .map(|timestamp| timestamp.snapshot().clone())
1336 .ok_or_else(|| Error::MetadataNotFound {
1337 path: MetadataPath::snapshot(),
1338 version: MetadataVersion::None,
1339 })?
1340 };
1341
1342 let timestamp_builder = TimestampMetadataBuilder::from_metadata_description(description)
1343 .version(next_version)
1344 .expires(self.ctx.current_time + self.ctx.timestamp_expiration_duration);
1345
1346 let timestamp = f(timestamp_builder).build()?;
1347 let raw_timestamp = sign(
1348 ×tamp,
1349 self.ctx
1350 .signing_timestamp_keys
1351 .iter()
1352 .chain(&self.ctx.trusted_timestamp_keys),
1353 )?;
1354
1355 Ok(RepoBuilder {
1356 ctx: self.ctx,
1357 state: Done {
1358 staged_root: self.state.staged_root,
1359 staged_targets: self.state.staged_targets,
1360 staged_snapshot: self.state.staged_snapshot,
1361 staged_timestamp: Some(Staged {
1362 metadata: timestamp,
1363 raw: raw_timestamp,
1364 }),
1365 },
1366 })
1367 }
1368
1369 pub async fn commit(self) -> Result<RawSignedMetadataSet<D>> {
1371 self.stage_timestamp_if_necessary()?.commit().await
1372 }
1373
1374 fn need_new_timestamp(&self) -> bool {
1375 if self.state.staged_root.is_some() {
1377 return true;
1378 }
1379
1380 if self.state.staged_snapshot.is_some() {
1382 return true;
1383 }
1384
1385 let db = if let Some(ref db) = self.ctx.db {
1387 db
1388 } else {
1389 return true;
1390 };
1391
1392 let trusted_timestamp = if let Some(trusted_timestamp) = db.trusted_timestamp() {
1394 trusted_timestamp
1395 } else {
1396 return true;
1397 };
1398
1399 if trusted_timestamp.expires() <= &self.ctx.current_time {
1401 return true;
1402 }
1403
1404 self.ctx.timestamp_keys_changed(db.trusted_root())
1406 }
1407}
1408
1409impl<D, R> RepoBuilder<'_, D, R, Done<D>>
1410where
1411 D: Pouf,
1412 R: RepositoryStorage<D>,
1413{
1414 pub async fn commit(mut self) -> Result<RawSignedMetadataSet<D>> {
1418 self.validate_built_metadata()?;
1419 self.write_repo().await?;
1420
1421 let mut builder = RawSignedMetadataSetBuilder::new();
1422
1423 if let Some(root) = self.state.staged_root {
1424 builder = builder.root(root.raw);
1425 }
1426
1427 if let Some(targets) = self.state.staged_targets {
1428 builder = builder.targets(targets.raw);
1429 }
1430
1431 if let Some(snapshot) = self.state.staged_snapshot {
1432 builder = builder.snapshot(snapshot.raw);
1433 }
1434
1435 if let Some(timestamp) = self.state.staged_timestamp {
1436 builder = builder.timestamp(timestamp.raw);
1437 }
1438
1439 Ok(builder.build())
1440 }
1441
1442 fn validate_built_metadata(&self) -> Result<()> {
1445 let mut db = if let Some(db) = self.ctx.db {
1449 let mut db = db.clone();
1450
1451 if let Some(ref root) = self.state.staged_root {
1452 db.update_root(&root.raw)?;
1453 }
1454
1455 db
1456 } else if let Some(ref root) = self.state.staged_root {
1457 Database::from_trusted_root(&root.raw)?
1458 } else {
1459 return Err(Error::MetadataNotFound {
1460 path: MetadataPath::root(),
1461 version: MetadataVersion::None,
1462 });
1463 };
1464
1465 if let Some(ref timestamp) = self.state.staged_timestamp {
1466 db.update_timestamp(&self.ctx.current_time, ×tamp.raw)?;
1467 }
1468
1469 if let Some(ref snapshot) = self.state.staged_snapshot {
1470 db.update_snapshot(&self.ctx.current_time, &snapshot.raw)?;
1471 }
1472
1473 if let Some(ref targets) = self.state.staged_targets {
1474 db.update_targets(&self.ctx.current_time, &targets.raw)?;
1475 }
1476
1477 Ok(())
1478 }
1479
1480 async fn write_repo(&mut self) -> Result<()> {
1481 let consistent_snapshot = if let Some(ref root) = self.state.staged_root {
1482 self.ctx
1483 .repo
1484 .store_metadata(
1485 &MetadataPath::root(),
1486 MetadataVersion::Number(root.metadata.version()),
1487 &mut root.raw.as_bytes(),
1488 )
1489 .await?;
1490
1491 self.ctx
1492 .repo
1493 .store_metadata(
1494 &MetadataPath::root(),
1495 MetadataVersion::None,
1496 &mut root.raw.as_bytes(),
1497 )
1498 .await?;
1499
1500 root.metadata.consistent_snapshot()
1501 } else if let Some(db) = self.ctx.db {
1502 db.trusted_root().consistent_snapshot()
1503 } else {
1504 return Err(Error::MetadataNotFound {
1505 path: MetadataPath::root(),
1506 version: MetadataVersion::None,
1507 });
1508 };
1509
1510 if let Some(ref targets) = self.state.staged_targets {
1511 let path = MetadataPath::targets();
1512 self.ctx
1513 .repo
1514 .store_metadata(
1515 &path.clone(),
1516 MetadataVersion::None,
1517 &mut targets.raw.as_bytes(),
1518 )
1519 .await?;
1520
1521 if consistent_snapshot {
1522 self.ctx
1523 .repo
1524 .store_metadata(
1525 &path,
1526 MetadataVersion::Number(targets.metadata.version()),
1527 &mut targets.raw.as_bytes(),
1528 )
1529 .await?;
1530 }
1531 }
1532
1533 if let Some(ref snapshot) = self.state.staged_snapshot {
1534 let path = MetadataPath::snapshot();
1535 self.ctx
1536 .repo
1537 .store_metadata(&path, MetadataVersion::None, &mut snapshot.raw.as_bytes())
1538 .await?;
1539
1540 if consistent_snapshot {
1541 self.ctx
1542 .repo
1543 .store_metadata(
1544 &path,
1545 MetadataVersion::Number(snapshot.metadata.version()),
1546 &mut snapshot.raw.as_bytes(),
1547 )
1548 .await?;
1549 }
1550 }
1551
1552 if let Some(ref timestamp) = self.state.staged_timestamp {
1553 self.ctx
1554 .repo
1555 .store_metadata(
1556 &MetadataPath::timestamp(),
1557 MetadataVersion::None,
1558 &mut timestamp.raw.as_bytes(),
1559 )
1560 .await?;
1561 }
1562
1563 Ok(())
1564 }
1565}
1566
1567#[cfg(test)]
1568mod tests {
1569 use {
1570 super::*,
1571 crate::{
1572 client::{Client, Config},
1573 crypto::Ed25519PrivateKey,
1574 metadata::SignedMetadata,
1575 pouf::Pouf1,
1576 repository::{EphemeralRepository, RepositoryProvider},
1577 },
1578 assert_matches::assert_matches,
1579 chrono::{
1580 offset::{TimeZone as _, Utc},
1581 DateTime,
1582 },
1583 futures_executor::block_on,
1584 futures_util::io::{AsyncReadExt, Cursor},
1585 maplit::hashmap,
1586 pretty_assertions::assert_eq,
1587 std::collections::BTreeMap,
1588 std::sync::LazyLock,
1589 };
1590
1591 static KEYS: LazyLock<Vec<Ed25519PrivateKey>> = LazyLock::new(|| {
1592 let keys: &[&[u8]] = &[
1593 include_bytes!("../tests/ed25519/ed25519-1.pk8.der"),
1594 include_bytes!("../tests/ed25519/ed25519-2.pk8.der"),
1595 include_bytes!("../tests/ed25519/ed25519-3.pk8.der"),
1596 include_bytes!("../tests/ed25519/ed25519-4.pk8.der"),
1597 include_bytes!("../tests/ed25519/ed25519-5.pk8.der"),
1598 include_bytes!("../tests/ed25519/ed25519-6.pk8.der"),
1599 ];
1600 keys.iter()
1601 .map(|b| Ed25519PrivateKey::from_pkcs8(b).unwrap())
1602 .collect()
1603 });
1604
1605 fn create_root(
1606 version: u32,
1607 consistent_snapshot: bool,
1608 expires: DateTime<Utc>,
1609 ) -> SignedMetadata<Pouf1, RootMetadata> {
1610 let root = RootMetadataBuilder::new()
1611 .version(version)
1612 .consistent_snapshot(consistent_snapshot)
1613 .expires(expires)
1614 .root_threshold(2)
1615 .root_key(KEYS[0].public().clone())
1616 .root_key(KEYS[1].public().clone())
1617 .root_key(KEYS[2].public().clone())
1618 .targets_threshold(2)
1619 .targets_key(KEYS[1].public().clone())
1620 .targets_key(KEYS[2].public().clone())
1621 .targets_key(KEYS[3].public().clone())
1622 .snapshot_threshold(2)
1623 .snapshot_key(KEYS[2].public().clone())
1624 .snapshot_key(KEYS[3].public().clone())
1625 .snapshot_key(KEYS[4].public().clone())
1626 .timestamp_threshold(2)
1627 .timestamp_key(KEYS[3].public().clone())
1628 .timestamp_key(KEYS[4].public().clone())
1629 .timestamp_key(KEYS[5].public().clone())
1630 .build()
1631 .unwrap();
1632
1633 SignedMetadataBuilder::from_metadata(&root)
1634 .unwrap()
1635 .sign(&KEYS[0])
1636 .unwrap()
1637 .sign(&KEYS[1])
1638 .unwrap()
1639 .sign(&KEYS[2])
1640 .unwrap()
1641 .build()
1642 }
1643
1644 fn create_targets(
1645 version: u32,
1646 expires: DateTime<Utc>,
1647 ) -> SignedMetadata<Pouf1, TargetsMetadata> {
1648 let targets = TargetsMetadataBuilder::new()
1649 .version(version)
1650 .expires(expires)
1651 .build()
1652 .unwrap();
1653 SignedMetadataBuilder::<Pouf1, _>::from_metadata(&targets)
1654 .unwrap()
1655 .sign(&KEYS[1])
1656 .unwrap()
1657 .sign(&KEYS[2])
1658 .unwrap()
1659 .sign(&KEYS[3])
1660 .unwrap()
1661 .build()
1662 }
1663
1664 fn create_snapshot(
1665 version: u32,
1666 expires: DateTime<Utc>,
1667 targets: &SignedMetadata<Pouf1, TargetsMetadata>,
1668 include_length_and_hashes: bool,
1669 ) -> SignedMetadata<Pouf1, SnapshotMetadata> {
1670 let description = if include_length_and_hashes {
1671 let raw_targets = targets.to_raw().unwrap();
1672 let hashes = crypto::calculate_hashes_from_slice(
1673 raw_targets.as_bytes(),
1674 &[HashAlgorithm::Sha256],
1675 )
1676 .unwrap();
1677
1678 MetadataDescription::new(version, Some(raw_targets.as_bytes().len()), hashes).unwrap()
1679 } else {
1680 MetadataDescription::new(version, None, HashMap::new()).unwrap()
1681 };
1682
1683 let snapshot = SnapshotMetadataBuilder::new()
1684 .insert_metadata_description(MetadataPath::targets(), description)
1685 .version(version)
1686 .expires(expires)
1687 .build()
1688 .unwrap();
1689 SignedMetadataBuilder::<Pouf1, _>::from_metadata(&snapshot)
1690 .unwrap()
1691 .sign(&KEYS[2])
1692 .unwrap()
1693 .sign(&KEYS[3])
1694 .unwrap()
1695 .sign(&KEYS[4])
1696 .unwrap()
1697 .build()
1698 }
1699
1700 fn create_timestamp(
1701 version: u32,
1702 expires: DateTime<Utc>,
1703 snapshot: &SignedMetadata<Pouf1, SnapshotMetadata>,
1704 include_length_and_hashes: bool,
1705 ) -> SignedMetadata<Pouf1, TimestampMetadata> {
1706 let description = if include_length_and_hashes {
1707 let raw_snapshot = snapshot.to_raw().unwrap();
1708 let hashes = crypto::calculate_hashes_from_slice(
1709 raw_snapshot.as_bytes(),
1710 &[HashAlgorithm::Sha256],
1711 )
1712 .unwrap();
1713
1714 MetadataDescription::new(version, Some(raw_snapshot.as_bytes().len()), hashes).unwrap()
1715 } else {
1716 MetadataDescription::new(version, None, HashMap::new()).unwrap()
1717 };
1718
1719 let timestamp = TimestampMetadataBuilder::from_metadata_description(description)
1720 .version(version)
1721 .expires(expires)
1722 .build()
1723 .unwrap();
1724 SignedMetadataBuilder::<Pouf1, _>::from_metadata(×tamp)
1725 .unwrap()
1726 .sign(&KEYS[3])
1727 .unwrap()
1728 .sign(&KEYS[4])
1729 .unwrap()
1730 .sign(&KEYS[5])
1731 .unwrap()
1732 .build()
1733 }
1734
1735 fn assert_metadata(
1736 metadata: &RawSignedMetadataSet<Pouf1>,
1737 expected_root: Option<&RawSignedMetadata<Pouf1, RootMetadata>>,
1738 expected_targets: Option<&RawSignedMetadata<Pouf1, TargetsMetadata>>,
1739 expected_snapshot: Option<&RawSignedMetadata<Pouf1, SnapshotMetadata>>,
1740 expected_timestamp: Option<&RawSignedMetadata<Pouf1, TimestampMetadata>>,
1741 ) {
1742 assert_eq!(
1743 metadata.root().map(|m| m.parse_untrusted().unwrap()),
1744 expected_root.map(|m| m.parse_untrusted().unwrap())
1745 );
1746 assert_eq!(
1747 metadata.targets().map(|m| m.parse_untrusted().unwrap()),
1748 expected_targets.map(|m| m.parse_untrusted().unwrap())
1749 );
1750 assert_eq!(
1751 metadata.snapshot().map(|m| m.parse_untrusted().unwrap()),
1752 expected_snapshot.map(|m| m.parse_untrusted().unwrap())
1753 );
1754 assert_eq!(
1755 metadata.timestamp().map(|m| m.parse_untrusted().unwrap()),
1756 expected_timestamp.map(|m| m.parse_untrusted().unwrap())
1757 );
1758 }
1759
1760 fn assert_repo(
1761 repo: &EphemeralRepository<Pouf1>,
1762 expected_metadata: &BTreeMap<(MetadataPath, MetadataVersion), &[u8]>,
1763 ) {
1764 let actual_metadata = repo
1765 .metadata()
1766 .iter()
1767 .map(|(k, v)| (k.clone(), String::from_utf8_lossy(v).to_string()))
1768 .collect::<BTreeMap<_, _>>();
1769
1770 let expected_metadata = expected_metadata
1771 .iter()
1772 .map(|(k, v)| (k.clone(), String::from_utf8_lossy(v).to_string()))
1773 .collect::<BTreeMap<_, _>>();
1774
1775 assert_eq!(
1776 actual_metadata.keys().collect::<Vec<_>>(),
1777 expected_metadata.keys().collect::<Vec<_>>()
1778 );
1779 assert_eq!(actual_metadata, expected_metadata);
1780 }
1781
1782 #[test]
1783 fn test_stage_and_update_repo_not_consistent_snapshot() {
1784 block_on(check_stage_and_update_repo(false));
1785 }
1786
1787 #[test]
1788 fn test_stage_and_update_repo_consistent_snapshot() {
1789 block_on(check_stage_and_update_repo(true));
1790 }
1791
1792 async fn check_stage_and_update_repo(consistent_snapshot: bool) {
1793 let mut remote = EphemeralRepository::<Pouf1>::new();
1795
1796 let expires1 = Utc.with_ymd_and_hms(2038, 1, 1, 0, 0, 0).unwrap();
1798 let metadata1 = RepoBuilder::create(&mut remote)
1799 .trusted_root_keys(&[&KEYS[0], &KEYS[1], &KEYS[2]])
1800 .trusted_targets_keys(&[&KEYS[1], &KEYS[2], &KEYS[3]])
1801 .trusted_snapshot_keys(&[&KEYS[2], &KEYS[3], &KEYS[4]])
1802 .trusted_timestamp_keys(&[&KEYS[3], &KEYS[4], &KEYS[5]])
1803 .stage_root_with_builder(|builder| {
1804 builder
1805 .expires(expires1)
1806 .consistent_snapshot(consistent_snapshot)
1807 .root_threshold(2)
1808 .targets_threshold(2)
1809 .snapshot_threshold(2)
1810 .timestamp_threshold(2)
1811 })
1812 .unwrap()
1813 .stage_targets_with_builder(|builder| builder.expires(expires1))
1814 .unwrap()
1815 .snapshot_includes_length(true)
1816 .snapshot_includes_hashes(&[HashAlgorithm::Sha256])
1817 .stage_snapshot_with_builder(|builder| builder.expires(expires1))
1818 .unwrap()
1819 .timestamp_includes_length(true)
1820 .timestamp_includes_hashes(&[HashAlgorithm::Sha256])
1821 .stage_timestamp_with_builder(|builder| builder.expires(expires1))
1822 .unwrap()
1823 .commit()
1824 .await
1825 .unwrap();
1826
1827 let signed_root1 = create_root(1, consistent_snapshot, expires1);
1830 let signed_targets1 = create_targets(1, expires1);
1831 let signed_snapshot1 = create_snapshot(1, expires1, &signed_targets1, true);
1832 let signed_timestamp1 = create_timestamp(1, expires1, &signed_snapshot1, true);
1833
1834 let raw_root1 = signed_root1.to_raw().unwrap();
1835 let raw_targets1 = signed_targets1.to_raw().unwrap();
1836 let raw_snapshot1 = signed_snapshot1.to_raw().unwrap();
1837 let raw_timestamp1 = signed_timestamp1.to_raw().unwrap();
1838
1839 assert_metadata(
1840 &metadata1,
1841 Some(&raw_root1),
1842 Some(&raw_targets1),
1843 Some(&raw_snapshot1),
1844 Some(&raw_timestamp1),
1845 );
1846
1847 let mut expected_metadata: BTreeMap<_, _> = vec![
1849 (
1850 (MetadataPath::root(), MetadataVersion::Number(1)),
1851 raw_root1.as_bytes(),
1852 ),
1853 (
1854 (MetadataPath::root(), MetadataVersion::None),
1855 raw_root1.as_bytes(),
1856 ),
1857 (
1858 (MetadataPath::targets(), MetadataVersion::None),
1859 raw_targets1.as_bytes(),
1860 ),
1861 (
1862 (MetadataPath::snapshot(), MetadataVersion::None),
1863 raw_snapshot1.as_bytes(),
1864 ),
1865 (
1866 (MetadataPath::timestamp(), MetadataVersion::None),
1867 raw_timestamp1.as_bytes(),
1868 ),
1869 ]
1870 .into_iter()
1871 .collect();
1872
1873 if consistent_snapshot {
1874 expected_metadata.extend(vec![
1875 (
1876 (MetadataPath::targets(), MetadataVersion::Number(1)),
1877 raw_targets1.as_bytes(),
1878 ),
1879 (
1880 (MetadataPath::snapshot(), MetadataVersion::Number(1)),
1881 raw_snapshot1.as_bytes(),
1882 ),
1883 ]);
1884 }
1885
1886 assert_repo(&remote, &expected_metadata);
1887
1888 let mut client = Client::with_trusted_root(
1891 Config::default(),
1892 metadata1.root().unwrap(),
1893 EphemeralRepository::new(),
1894 remote,
1895 )
1896 .await
1897 .unwrap();
1898 client.update().await.unwrap();
1899 assert_eq!(client.database().trusted_root().version(), 1);
1900 assert_eq!(
1901 client.database().trusted_targets().map(|m| m.version()),
1902 Some(1)
1903 );
1904 assert_eq!(
1905 client.database().trusted_snapshot().map(|m| m.version()),
1906 Some(1)
1907 );
1908 assert_eq!(
1909 client.database().trusted_timestamp().map(|m| m.version()),
1910 Some(1)
1911 );
1912
1913 let expires2 = Utc.with_ymd_and_hms(2038, 1, 2, 0, 0, 0).unwrap();
1916 let mut parts = client.into_parts();
1917 let metadata2 = RepoBuilder::from_database(&mut parts.remote, &parts.database)
1918 .trusted_root_keys(&[&KEYS[0], &KEYS[1], &KEYS[2]])
1919 .trusted_targets_keys(&[&KEYS[1], &KEYS[2], &KEYS[3]])
1920 .trusted_snapshot_keys(&[&KEYS[2], &KEYS[3], &KEYS[4]])
1921 .trusted_timestamp_keys(&[&KEYS[3], &KEYS[4], &KEYS[5]])
1922 .stage_root_with_builder(|builder| builder.expires(expires2))
1923 .unwrap()
1924 .stage_targets_with_builder(|builder| builder.expires(expires2))
1925 .unwrap()
1926 .snapshot_includes_length(false)
1927 .snapshot_includes_hashes(&[])
1928 .stage_snapshot_with_builder(|builder| builder.expires(expires2))
1929 .unwrap()
1930 .timestamp_includes_length(false)
1931 .timestamp_includes_hashes(&[])
1932 .stage_timestamp_with_builder(|builder| builder.expires(expires2))
1933 .unwrap()
1934 .commit()
1935 .await
1936 .unwrap();
1937
1938 let signed_root2 = create_root(2, consistent_snapshot, expires2);
1940 let signed_targets2 = create_targets(2, expires2);
1941 let signed_snapshot2 = create_snapshot(2, expires2, &signed_targets2, false);
1942 let signed_timestamp2 = create_timestamp(2, expires2, &signed_snapshot2, false);
1943
1944 let raw_root2 = signed_root2.to_raw().unwrap();
1945 let raw_targets2 = signed_targets2.to_raw().unwrap();
1946 let raw_snapshot2 = signed_snapshot2.to_raw().unwrap();
1947 let raw_timestamp2 = signed_timestamp2.to_raw().unwrap();
1948
1949 assert_metadata(
1950 &metadata2,
1951 Some(&raw_root2),
1952 Some(&raw_targets2),
1953 Some(&raw_snapshot2),
1954 Some(&raw_timestamp2),
1955 );
1956
1957 expected_metadata.extend(vec![
1959 (
1960 (MetadataPath::root(), MetadataVersion::Number(2)),
1961 raw_root2.as_bytes(),
1962 ),
1963 (
1964 (MetadataPath::root(), MetadataVersion::None),
1965 raw_root2.as_bytes(),
1966 ),
1967 (
1968 (MetadataPath::targets(), MetadataVersion::None),
1969 raw_targets2.as_bytes(),
1970 ),
1971 (
1972 (MetadataPath::snapshot(), MetadataVersion::None),
1973 raw_snapshot2.as_bytes(),
1974 ),
1975 (
1976 (MetadataPath::timestamp(), MetadataVersion::None),
1977 raw_timestamp2.as_bytes(),
1978 ),
1979 ]);
1980
1981 if consistent_snapshot {
1982 expected_metadata.extend(vec![
1983 (
1984 (MetadataPath::targets(), MetadataVersion::Number(2)),
1985 raw_targets2.as_bytes(),
1986 ),
1987 (
1988 (MetadataPath::snapshot(), MetadataVersion::Number(2)),
1989 raw_snapshot2.as_bytes(),
1990 ),
1991 ]);
1992 }
1993
1994 assert_repo(&parts.remote, &expected_metadata);
1995
1996 let mut client = Client::from_parts(parts);
1998 client.update().await.unwrap();
1999 assert_eq!(client.database().trusted_root().version(), 2);
2000 assert_eq!(
2001 client.database().trusted_targets().map(|m| m.version()),
2002 Some(2)
2003 );
2004 assert_eq!(
2005 client.database().trusted_snapshot().map(|m| m.version()),
2006 Some(2)
2007 );
2008 assert_eq!(
2009 client.database().trusted_timestamp().map(|m| m.version()),
2010 Some(2)
2011 );
2012 }
2013
2014 #[test]
2015 fn commit_does_nothing_if_nothing_changed_not_consistent_snapshot() {
2016 block_on(commit_does_nothing_if_nothing_changed(false))
2017 }
2018
2019 #[test]
2020 fn commit_does_nothing_if_nothing_changed_consistent_snapshot() {
2021 block_on(commit_does_nothing_if_nothing_changed(true))
2022 }
2023
2024 async fn commit_does_nothing_if_nothing_changed(consistent_snapshot: bool) {
2025 let mut repo = EphemeralRepository::<Pouf1>::new();
2026 let metadata1 = RepoBuilder::create(&mut repo)
2027 .trusted_root_keys(&[&KEYS[0]])
2028 .trusted_targets_keys(&[&KEYS[0]])
2029 .trusted_snapshot_keys(&[&KEYS[0]])
2030 .trusted_timestamp_keys(&[&KEYS[0]])
2031 .stage_root_with_builder(|builder| builder.consistent_snapshot(consistent_snapshot))
2032 .unwrap()
2033 .commit()
2034 .await
2035 .unwrap();
2036
2037 let client_repo = EphemeralRepository::new();
2038 let mut client = Client::with_trusted_root(
2039 Config::default(),
2040 metadata1.root().unwrap(),
2041 client_repo,
2042 repo,
2043 )
2044 .await
2045 .unwrap();
2046
2047 assert!(client.update().await.unwrap());
2048 assert_eq!(client.database().trusted_root().version(), 1);
2049
2050 let mut parts = client.into_parts();
2052 let metadata2 = RepoBuilder::from_database(&mut parts.remote, &parts.database)
2053 .trusted_root_keys(&[&KEYS[0]])
2054 .trusted_targets_keys(&[&KEYS[0]])
2055 .trusted_snapshot_keys(&[&KEYS[0]])
2056 .trusted_timestamp_keys(&[&KEYS[0]])
2057 .commit()
2058 .await
2059 .unwrap();
2060
2061 assert_metadata(&metadata2, None, None, None, None);
2062
2063 let mut client = Client::from_parts(parts);
2064 assert!(!client.update().await.unwrap());
2065 assert_eq!(client.database().trusted_root().version(), 1);
2066 }
2067
2068 #[test]
2069 fn root_chain_update_not_consistent() {
2070 block_on(check_root_chain_update(false));
2071 }
2072
2073 #[test]
2074 fn root_chain_update_consistent() {
2075 block_on(check_root_chain_update(true));
2076 }
2077
2078 async fn check_root_chain_update(consistent_snapshot: bool) {
2079 let mut repo = EphemeralRepository::<Pouf1>::new();
2080
2081 let metadata1 = RepoBuilder::create(&mut repo)
2084 .trusted_root_keys(&[&KEYS[1]])
2085 .trusted_targets_keys(&[&KEYS[0]])
2086 .trusted_snapshot_keys(&[&KEYS[0]])
2087 .trusted_timestamp_keys(&[&KEYS[0]])
2088 .stage_root_with_builder(|builder| builder.consistent_snapshot(consistent_snapshot))
2089 .unwrap()
2090 .commit()
2091 .await
2092 .unwrap();
2093
2094 let client_repo = EphemeralRepository::new();
2095 let mut client = Client::with_trusted_root(
2096 Config::default(),
2097 metadata1.root().unwrap(),
2098 client_repo,
2099 repo,
2100 )
2101 .await
2102 .unwrap();
2103
2104 assert!(client.update().await.unwrap());
2105 assert_eq!(client.database().trusted_root().version(), 1);
2106 assert_eq!(
2107 client
2108 .database()
2109 .trusted_root()
2110 .root_keys()
2111 .collect::<Vec<_>>(),
2112 vec![KEYS[1].public()],
2113 );
2114
2115 assert!(!client.update().await.unwrap());
2117 assert_eq!(client.database().trusted_root().version(), 1);
2118
2119 let mut parts = client.into_parts();
2122 let _metadata2 = RepoBuilder::from_database(&mut parts.remote, &parts.database)
2123 .signing_root_keys(&[&KEYS[1]])
2124 .trusted_root_keys(&[&KEYS[2]])
2125 .trusted_targets_keys(&[&KEYS[0]])
2126 .trusted_snapshot_keys(&[&KEYS[0]])
2127 .trusted_timestamp_keys(&[&KEYS[0]])
2128 .commit()
2129 .await
2130 .unwrap();
2131
2132 let mut client = Client::from_parts(parts);
2133 assert!(client.update().await.unwrap());
2134 assert_eq!(client.database().trusted_root().version(), 2);
2135 assert_eq!(
2136 client.database().trusted_root().consistent_snapshot(),
2137 consistent_snapshot
2138 );
2139 assert_eq!(
2140 client
2141 .database()
2142 .trusted_root()
2143 .root_keys()
2144 .collect::<Vec<_>>(),
2145 vec![KEYS[2].public()],
2146 );
2147
2148 assert!(!client.update().await.unwrap());
2150 assert_eq!(client.database().trusted_root().version(), 2);
2151
2152 let mut parts = client.into_parts();
2155 let _metadata3 = RepoBuilder::from_database(&mut parts.remote, &parts.database)
2156 .trusted_root_keys(&[&KEYS[2]])
2157 .trusted_targets_keys(&[&KEYS[0]])
2158 .trusted_snapshot_keys(&[&KEYS[0]])
2159 .trusted_timestamp_keys(&[&KEYS[0]])
2160 .stage_root()
2161 .unwrap()
2162 .commit()
2163 .await
2164 .unwrap();
2165
2166 let mut client = Client::from_parts(parts);
2167 assert!(client.update().await.unwrap());
2168 assert_eq!(client.database().trusted_root().version(), 3);
2169 assert_eq!(
2170 client
2171 .database()
2172 .trusted_root()
2173 .root_keys()
2174 .collect::<Vec<_>>(),
2175 vec![KEYS[2].public()],
2176 );
2177
2178 assert!(!client.update().await.unwrap());
2180 assert_eq!(client.database().trusted_root().version(), 3);
2181 }
2182
2183 #[test]
2184 fn test_from_database_root_must_be_one_after_the_last() {
2185 block_on(async {
2186 let mut repo = EphemeralRepository::<Pouf1>::new();
2187 let metadata = RepoBuilder::create(&mut repo)
2188 .trusted_root_keys(&[&KEYS[0]])
2189 .trusted_targets_keys(&[&KEYS[0]])
2190 .trusted_snapshot_keys(&[&KEYS[0]])
2191 .trusted_timestamp_keys(&[&KEYS[0]])
2192 .commit()
2193 .await
2194 .unwrap();
2195
2196 let db = Database::from_trusted_metadata(&metadata).unwrap();
2197
2198 assert_matches!(
2199 RepoBuilder::from_database(&mut repo, &db)
2200 .trusted_root_keys(&[&KEYS[0]])
2201 .trusted_targets_keys(&[&KEYS[0]])
2202 .trusted_snapshot_keys(&[&KEYS[0]])
2203 .trusted_timestamp_keys(&[&KEYS[0]])
2204 .stage_root_with_builder(|builder| builder.version(3))
2205 .unwrap()
2206 .commit()
2207 .await,
2208 Err(Error::AttemptedMetadataRollBack {
2209 role,
2210 trusted_version: 1,
2211 new_version: 3,
2212 })
2213 if role == MetadataPath::root()
2214 );
2215 })
2216 }
2217
2218 #[test]
2219 fn test_add_target_not_consistent_snapshot() {
2220 block_on(async move {
2221 let mut repo = EphemeralRepository::<Pouf1>::new();
2222
2223 let hash_algs = &[HashAlgorithm::Sha256, HashAlgorithm::Sha512];
2224
2225 let target_path1 = TargetPath::new("foo/default").unwrap();
2226 let target_path1_hashed = TargetPath::new(
2227 "foo/522dd05a607a520657daa19c061a0271224030307117c2e661505e14601d1e44.default",
2228 )
2229 .unwrap();
2230 let target_file1: &[u8] = b"things fade, alternatives exclude";
2231
2232 let target_path2 = TargetPath::new("foo/custom").unwrap();
2233 let target_path2_hashed = TargetPath::new(
2234 "foo/b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9.custom",
2235 )
2236 .unwrap();
2237 let target_file2: &[u8] = b"hello world";
2238
2239 let metadata = RepoBuilder::create(&mut repo)
2240 .trusted_root_keys(&[&KEYS[0]])
2241 .trusted_targets_keys(&[&KEYS[0]])
2242 .trusted_snapshot_keys(&[&KEYS[0]])
2243 .trusted_timestamp_keys(&[&KEYS[0]])
2244 .add_target(target_path1.clone(), Cursor::new(target_file1))
2245 .await
2246 .unwrap()
2247 .target_hash_algorithms(hash_algs)
2248 .add_target(target_path2.clone(), Cursor::new(target_file2))
2249 .await
2250 .unwrap()
2251 .commit()
2252 .await
2253 .unwrap();
2254
2255 let mut rdr = repo.fetch_target(&target_path1_hashed).await.unwrap();
2257 let mut buf = vec![];
2258 rdr.read_to_end(&mut buf).await.unwrap();
2259 drop(rdr);
2260
2261 assert_eq!(&buf, target_file1);
2262
2263 let mut rdr = repo.fetch_target(&target_path2_hashed).await.unwrap();
2264 let mut buf = vec![];
2265 rdr.read_to_end(&mut buf).await.unwrap();
2266 drop(rdr);
2267
2268 assert_eq!(&buf, target_file2);
2269
2270 let mut client = Client::with_trusted_root(
2271 Config::default(),
2272 metadata.root().unwrap(),
2273 EphemeralRepository::new(),
2274 repo,
2275 )
2276 .await
2277 .unwrap();
2278
2279 client.update().await.unwrap();
2280
2281 assert_eq!(
2283 client
2284 .fetch_target_description(&target_path1)
2285 .await
2286 .unwrap(),
2287 TargetDescription::from_slice(target_file1, &[HashAlgorithm::Sha256]).unwrap(),
2288 );
2289
2290 assert_eq!(
2291 client
2292 .fetch_target_description(&target_path2)
2293 .await
2294 .unwrap(),
2295 TargetDescription::from_slice(target_file2, hash_algs).unwrap(),
2296 );
2297
2298 let mut rdr = client.fetch_target(&target_path1).await.unwrap();
2300 let mut buf = vec![];
2301 rdr.read_to_end(&mut buf).await.unwrap();
2302 assert_eq!(&buf, target_file1);
2303 drop(rdr);
2304
2305 let mut rdr = client.fetch_target(&target_path2).await.unwrap();
2306 let mut buf = vec![];
2307 rdr.read_to_end(&mut buf).await.unwrap();
2308 assert_eq!(&buf, target_file2);
2309 })
2310 }
2311
2312 #[test]
2313 fn test_add_target_consistent_snapshot() {
2314 block_on(async move {
2315 let mut repo = EphemeralRepository::<Pouf1>::new();
2316
2317 let hash_algs = &[HashAlgorithm::Sha256, HashAlgorithm::Sha512];
2318
2319 let target_path1 = TargetPath::new("foo/bar").unwrap();
2320 let target_file1: &[u8] = b"things fade, alternatives exclude";
2321
2322 let target_path2 = TargetPath::new("baz").unwrap();
2323 let target_file2: &[u8] = b"hello world";
2324 let target_custom2 = hashmap! {
2325 "hello".into() => "world".into(),
2326 };
2327
2328 let metadata = RepoBuilder::create(&mut repo)
2329 .trusted_root_keys(&[&KEYS[0]])
2330 .trusted_targets_keys(&[&KEYS[0]])
2331 .trusted_snapshot_keys(&[&KEYS[0]])
2332 .trusted_timestamp_keys(&[&KEYS[0]])
2333 .stage_root_with_builder(|builder| builder.consistent_snapshot(true))
2334 .unwrap()
2335 .target_hash_algorithms(hash_algs)
2336 .add_target(target_path1.clone(), Cursor::new(target_file1))
2337 .await
2338 .unwrap()
2339 .add_target_with_custom(
2340 target_path2.clone(),
2341 Cursor::new(target_file2),
2342 target_custom2.clone(),
2343 )
2344 .await
2345 .unwrap()
2346 .commit()
2347 .await
2348 .unwrap();
2349
2350 for (target_path, target_file) in &[
2352 (&target_path1, &target_file1),
2353 (&target_path2, &target_file2),
2354 ] {
2355 for hash_alg in hash_algs {
2356 let hash = crypto::calculate_hash(target_file, hash_alg);
2357 let target_path = target_path.with_hash_prefix(&hash).unwrap();
2358
2359 let mut rdr = repo.fetch_target(&target_path).await.unwrap();
2360 let mut buf = vec![];
2361 rdr.read_to_end(&mut buf).await.unwrap();
2362
2363 assert_eq!(&buf, *target_file);
2364 }
2365 }
2366
2367 let mut client = Client::with_trusted_root(
2368 Config::default(),
2369 metadata.root().unwrap(),
2370 EphemeralRepository::new(),
2371 repo,
2372 )
2373 .await
2374 .unwrap();
2375
2376 client.update().await.unwrap();
2377
2378 assert_eq!(
2380 client
2381 .fetch_target_description(&target_path1)
2382 .await
2383 .unwrap(),
2384 TargetDescription::from_slice(target_file1, hash_algs).unwrap(),
2385 );
2386
2387 assert_eq!(
2388 client
2389 .fetch_target_description(&target_path2)
2390 .await
2391 .unwrap(),
2392 TargetDescription::from_slice_with_custom(target_file2, hash_algs, target_custom2)
2393 .unwrap(),
2394 );
2395
2396 for (target_path, target_file) in &[
2398 (&target_path1, &target_file1),
2399 (&target_path2, &target_file2),
2400 ] {
2401 let mut rdr = client.fetch_target(target_path).await.unwrap();
2402 let mut buf = vec![];
2403 rdr.read_to_end(&mut buf).await.unwrap();
2404 assert_eq!(&buf, *target_file);
2405 }
2406 })
2407 }
2408
2409 #[test]
2410 fn test_do_not_require_all_keys_to_be_online() {
2411 block_on(async {
2412 let mut remote = EphemeralRepository::<Pouf1>::new();
2413
2414 let expires1 = Utc.with_ymd_and_hms(2038, 1, 1, 0, 0, 0).unwrap();
2416 let metadata1 = RepoBuilder::create(&mut remote)
2417 .trusted_root_keys(&[&KEYS[0]])
2418 .trusted_targets_keys(&[&KEYS[1]])
2419 .trusted_snapshot_keys(&[&KEYS[2]])
2420 .trusted_timestamp_keys(&[&KEYS[3]])
2421 .stage_root_with_builder(|builder| {
2422 builder.consistent_snapshot(true).expires(expires1)
2423 })
2424 .unwrap()
2425 .stage_targets_with_builder(|builder| builder.expires(expires1))
2426 .unwrap()
2427 .stage_snapshot_with_builder(|builder| builder.expires(expires1))
2428 .unwrap()
2429 .stage_timestamp_with_builder(|builder| builder.expires(expires1))
2430 .unwrap()
2431 .commit()
2432 .await
2433 .unwrap();
2434
2435 assert!(metadata1.root().is_some());
2437 assert!(metadata1.timestamp().is_some());
2438 assert!(metadata1.snapshot().is_some());
2439 assert!(metadata1.targets().is_some());
2440
2441 let mut expected_metadata: BTreeMap<_, _> = vec![
2442 (
2443 (MetadataPath::root(), MetadataVersion::Number(1)),
2444 metadata1.root().unwrap().as_bytes(),
2445 ),
2446 (
2447 (MetadataPath::root(), MetadataVersion::None),
2448 metadata1.root().unwrap().as_bytes(),
2449 ),
2450 (
2451 (MetadataPath::targets(), MetadataVersion::Number(1)),
2452 metadata1.targets().unwrap().as_bytes(),
2453 ),
2454 (
2455 (MetadataPath::targets(), MetadataVersion::None),
2456 metadata1.targets().unwrap().as_bytes(),
2457 ),
2458 (
2459 (MetadataPath::snapshot(), MetadataVersion::Number(1)),
2460 metadata1.snapshot().unwrap().as_bytes(),
2461 ),
2462 (
2463 (MetadataPath::snapshot(), MetadataVersion::None),
2464 metadata1.snapshot().unwrap().as_bytes(),
2465 ),
2466 (
2467 (MetadataPath::timestamp(), MetadataVersion::None),
2468 metadata1.timestamp().unwrap().as_bytes(),
2469 ),
2470 ]
2471 .into_iter()
2472 .collect();
2473
2474 assert_repo(&remote, &expected_metadata);
2475
2476 let mut db = Database::from_trusted_metadata(&metadata1).unwrap();
2477
2478 let expires2 = Utc.with_ymd_and_hms(2038, 1, 2, 0, 0, 0).unwrap();
2480 let metadata2 = RepoBuilder::from_database(&mut remote, &db)
2481 .trusted_targets_keys(&[&KEYS[1]])
2482 .trusted_snapshot_keys(&[&KEYS[2]])
2483 .trusted_timestamp_keys(&[&KEYS[3]])
2484 .skip_root()
2485 .stage_targets_with_builder(|builder| builder.expires(expires2))
2486 .unwrap()
2487 .stage_snapshot_with_builder(|builder| builder.expires(expires2))
2488 .unwrap()
2489 .stage_timestamp_with_builder(|builder| builder.expires(expires2))
2490 .unwrap()
2491 .commit()
2492 .await
2493 .unwrap();
2494
2495 assert!(db.update_metadata(&metadata2).unwrap());
2496
2497 assert!(metadata2.root().is_none());
2498 assert!(metadata2.targets().is_some());
2499 assert!(metadata2.snapshot().is_some());
2500 assert!(metadata2.timestamp().is_some());
2501
2502 expected_metadata.extend(vec![
2503 (
2504 (MetadataPath::targets(), MetadataVersion::Number(2)),
2505 metadata2.targets().unwrap().as_bytes(),
2506 ),
2507 (
2508 (MetadataPath::targets(), MetadataVersion::None),
2509 metadata2.targets().unwrap().as_bytes(),
2510 ),
2511 (
2512 (MetadataPath::snapshot(), MetadataVersion::Number(2)),
2513 metadata2.snapshot().unwrap().as_bytes(),
2514 ),
2515 (
2516 (MetadataPath::snapshot(), MetadataVersion::None),
2517 metadata2.snapshot().unwrap().as_bytes(),
2518 ),
2519 (
2520 (MetadataPath::timestamp(), MetadataVersion::None),
2521 metadata2.timestamp().unwrap().as_bytes(),
2522 ),
2523 ]);
2524
2525 assert_repo(&remote, &expected_metadata);
2526
2527 let expires3 = Utc.with_ymd_and_hms(2038, 1, 3, 0, 0, 0).unwrap();
2529 let metadata3 = RepoBuilder::from_database(&mut remote, &db)
2530 .trusted_snapshot_keys(&[&KEYS[2]])
2531 .trusted_timestamp_keys(&[&KEYS[3]])
2532 .skip_root()
2533 .skip_targets()
2534 .stage_snapshot_with_builder(|builder| builder.expires(expires3))
2535 .unwrap()
2536 .stage_timestamp_with_builder(|builder| builder.expires(expires3))
2537 .unwrap()
2538 .commit()
2539 .await
2540 .unwrap();
2541
2542 assert!(db.update_metadata(&metadata3).unwrap());
2543
2544 assert!(metadata3.root().is_none());
2546 assert!(metadata3.targets().is_none());
2547 assert!(metadata3.snapshot().is_some());
2548 assert!(metadata3.timestamp().is_some());
2549
2550 expected_metadata.extend(vec![
2551 (
2552 (MetadataPath::snapshot(), MetadataVersion::Number(3)),
2553 metadata3.snapshot().unwrap().as_bytes(),
2554 ),
2555 (
2556 (MetadataPath::snapshot(), MetadataVersion::None),
2557 metadata3.snapshot().unwrap().as_bytes(),
2558 ),
2559 (
2560 (MetadataPath::timestamp(), MetadataVersion::None),
2561 metadata3.timestamp().unwrap().as_bytes(),
2562 ),
2563 ]);
2564
2565 assert_repo(&remote, &expected_metadata);
2566
2567 let expires4 = Utc.with_ymd_and_hms(2038, 1, 4, 0, 0, 0).unwrap();
2569 let metadata4 = RepoBuilder::from_database(&mut remote, &db)
2570 .trusted_timestamp_keys(&[&KEYS[3]])
2571 .skip_root()
2572 .skip_targets()
2573 .skip_snapshot()
2574 .stage_timestamp_with_builder(|builder| builder.expires(expires4))
2575 .unwrap()
2576 .commit()
2577 .await
2578 .unwrap();
2579
2580 assert!(db.update_metadata(&metadata4).unwrap());
2581
2582 assert!(metadata4.root().is_none());
2584 assert!(metadata4.targets().is_none());
2585 assert!(metadata4.snapshot().is_none());
2586 assert!(metadata4.timestamp().is_some());
2587
2588 expected_metadata.extend(vec![(
2589 (MetadataPath::timestamp(), MetadataVersion::None),
2590 metadata4.timestamp().unwrap().as_bytes(),
2591 )]);
2592
2593 assert_repo(&remote, &expected_metadata);
2594 })
2595 }
2596
2597 #[test]
2598 fn test_builder_inherits_from_trusted_targets() {
2599 block_on(async move {
2600 let mut repo = EphemeralRepository::<Pouf1>::new();
2601
2602 let expires = Utc.with_ymd_and_hms(2038, 1, 4, 0, 0, 0).unwrap();
2603 let hash_algs = &[HashAlgorithm::Sha256, HashAlgorithm::Sha512];
2604 let delegation_key = &KEYS[0];
2605 let delegation_path = MetadataPath::new("delegations").unwrap();
2606
2607 let target_path1 = TargetPath::new("target1").unwrap();
2608 let target_file1: &[u8] = b"target1 file";
2609
2610 let delegated_target_path1 = TargetPath::new("delegations/delegation1").unwrap();
2611 let delegated_target_file1: &[u8] = b"delegation1 file";
2612
2613 let delegation1 = Delegation::builder(delegation_path.clone())
2614 .key(delegation_key.public())
2615 .delegate_path(TargetPath::new("delegations/").unwrap())
2616 .build()
2617 .unwrap();
2618
2619 let delegated_targets1 = TargetsMetadataBuilder::new()
2620 .insert_target_from_slice(
2621 delegated_target_path1,
2622 delegated_target_file1,
2623 &[HashAlgorithm::Sha256],
2624 )
2625 .unwrap()
2626 .signed::<Pouf1>(delegation_key)
2627 .unwrap();
2628 let raw_delegated_targets = delegated_targets1.to_raw().unwrap();
2629
2630 let metadata1 = RepoBuilder::create(&mut repo)
2631 .trusted_root_keys(&[&KEYS[0]])
2632 .trusted_targets_keys(&[&KEYS[0]])
2633 .trusted_snapshot_keys(&[&KEYS[0]])
2634 .trusted_timestamp_keys(&[&KEYS[0]])
2635 .stage_root()
2636 .unwrap()
2637 .target_hash_algorithms(hash_algs)
2638 .add_target(target_path1.clone(), Cursor::new(target_file1))
2639 .await
2640 .unwrap()
2641 .add_delegation_key(delegation_key.public().clone())
2642 .add_delegation_role(delegation1.clone())
2643 .stage_targets()
2644 .unwrap()
2645 .stage_snapshot_with_builder(|builder| {
2646 builder.insert_metadata_description(
2647 delegation_path.clone(),
2648 MetadataDescription::from_slice(
2649 raw_delegated_targets.as_bytes(),
2650 1,
2651 &[HashAlgorithm::Sha256],
2652 )
2653 .unwrap(),
2654 )
2655 })
2656 .unwrap()
2657 .commit()
2658 .await
2659 .unwrap();
2660
2661 let mut database = Database::from_trusted_metadata(&metadata1).unwrap();
2664
2665 let target_path2 = TargetPath::new("bar").unwrap();
2666 let target_file2: &[u8] = b"bar file";
2667
2668 let delegated_target_path2 = TargetPath::new("delegations/delegation2").unwrap();
2669 let delegated_target_file2: &[u8] = b"delegation2 file";
2670
2671 let delegation2 = Delegation::builder(delegation_path.clone())
2672 .key(delegation_key.public())
2673 .delegate_path(TargetPath::new("delegations/").unwrap())
2674 .build()
2675 .unwrap();
2676
2677 let delegated_targets2 = TargetsMetadataBuilder::new()
2678 .insert_target_from_slice(
2679 delegated_target_path2,
2680 delegated_target_file2,
2681 &[HashAlgorithm::Sha256],
2682 )
2683 .unwrap()
2684 .signed::<Pouf1>(delegation_key)
2685 .unwrap();
2686 let raw_delegated_targets = delegated_targets2.to_raw().unwrap();
2687
2688 let metadata2 = RepoBuilder::from_database(&mut repo, &database)
2689 .trusted_root_keys(&[&KEYS[0]])
2690 .trusted_targets_keys(&[&KEYS[0]])
2691 .trusted_snapshot_keys(&[&KEYS[0]])
2692 .trusted_timestamp_keys(&[&KEYS[0]])
2693 .stage_root()
2694 .unwrap()
2695 .target_hash_algorithms(hash_algs)
2696 .add_target(target_path2.clone(), Cursor::new(target_file2))
2697 .await
2698 .unwrap()
2699 .add_delegation_role(delegation2.clone())
2700 .stage_targets_with_builder(|b| b.expires(expires))
2701 .unwrap()
2702 .stage_snapshot_with_builder(|builder| {
2703 builder.insert_metadata_description(
2704 delegation_path.clone(),
2705 MetadataDescription::from_slice(
2706 raw_delegated_targets.as_bytes(),
2707 1,
2708 &[HashAlgorithm::Sha256],
2709 )
2710 .unwrap(),
2711 )
2712 })
2713 .unwrap()
2714 .commit()
2715 .await
2716 .unwrap();
2717
2718 database.update_metadata(&metadata2).unwrap();
2719
2720 assert_eq!(
2721 &**database.trusted_targets().unwrap(),
2722 &TargetsMetadataBuilder::new()
2723 .version(2)
2724 .expires(expires)
2725 .insert_target_from_slice(target_path1.clone(), target_file1, hash_algs)
2726 .unwrap()
2727 .insert_target_from_slice(target_path2.clone(), target_file2, hash_algs)
2728 .unwrap()
2729 .delegations(
2730 DelegationsBuilder::new()
2731 .key(delegation_key.public().clone())
2732 .role(delegation1)
2733 .role(delegation2)
2734 .build()
2735 .unwrap()
2736 )
2737 .build()
2738 .unwrap()
2739 )
2740 })
2741 }
2742
2743 #[test]
2744 fn test_builder_rotating_keys_refreshes_metadata() {
2745 block_on(async move {
2746 let mut repo = EphemeralRepository::<Pouf1>::new();
2747
2748 let metadata1 = RepoBuilder::create(&mut repo)
2749 .trusted_root_keys(&[&KEYS[0]])
2750 .trusted_targets_keys(&[&KEYS[0]])
2751 .trusted_snapshot_keys(&[&KEYS[0]])
2752 .trusted_timestamp_keys(&[&KEYS[0]])
2753 .commit()
2754 .await
2755 .unwrap();
2756
2757 let mut db = Database::from_trusted_metadata(&metadata1).unwrap();
2758
2759 let metadata2 = RepoBuilder::from_database(&mut repo, &db)
2766 .trusted_root_keys(&[&KEYS[0]])
2767 .trusted_targets_keys(&[&KEYS[0]])
2768 .trusted_snapshot_keys(&[&KEYS[0]])
2769 .trusted_timestamp_keys(&[&KEYS[1]])
2770 .commit()
2771 .await
2772 .unwrap();
2773
2774 assert!(metadata2.root().is_some());
2775 assert!(metadata2.targets().is_some());
2776 assert!(metadata2.snapshot().is_some());
2777 assert!(metadata2.timestamp().is_some());
2778
2779 db.update_metadata(&metadata2).unwrap();
2780
2781 assert_eq!(db.trusted_root().version(), 2);
2782 assert_eq!(db.trusted_targets().unwrap().version(), 2);
2783 assert_eq!(db.trusted_snapshot().unwrap().version(), 2);
2784 assert_eq!(db.trusted_timestamp().unwrap().version(), 2);
2785
2786 let metadata3 = RepoBuilder::from_database(&mut repo, &db)
2790 .trusted_root_keys(&[&KEYS[0]])
2791 .trusted_targets_keys(&[&KEYS[0]])
2792 .trusted_snapshot_keys(&[&KEYS[1]])
2793 .trusted_timestamp_keys(&[&KEYS[1]])
2794 .commit()
2795 .await
2796 .unwrap();
2797
2798 assert!(metadata3.root().is_some());
2799 assert!(metadata2.targets().is_some());
2800 assert!(metadata2.snapshot().is_some());
2801 assert!(metadata2.timestamp().is_some());
2802
2803 db.update_metadata(&metadata3).unwrap();
2804
2805 assert_eq!(db.trusted_root().version(), 3);
2806 assert_eq!(db.trusted_targets().unwrap().version(), 3);
2807 assert_eq!(db.trusted_snapshot().unwrap().version(), 3);
2808 assert_eq!(db.trusted_timestamp().unwrap().version(), 3);
2809
2810 let metadata4 = RepoBuilder::from_database(&mut repo, &db)
2812 .trusted_root_keys(&[&KEYS[0]])
2813 .trusted_targets_keys(&[&KEYS[1]])
2814 .trusted_snapshot_keys(&[&KEYS[1]])
2815 .trusted_timestamp_keys(&[&KEYS[1]])
2816 .commit()
2817 .await
2818 .unwrap();
2819
2820 assert!(metadata4.root().is_some());
2821 assert!(metadata4.targets().is_some());
2822 assert!(metadata4.snapshot().is_some());
2823 assert!(metadata4.timestamp().is_some());
2824
2825 db.update_metadata(&metadata4).unwrap();
2826
2827 assert_eq!(db.trusted_root().version(), 4);
2828 assert_eq!(db.trusted_targets().unwrap().version(), 4);
2829 assert_eq!(db.trusted_snapshot().unwrap().version(), 4);
2830 assert_eq!(db.trusted_timestamp().unwrap().version(), 4);
2831
2832 let metadata5 = RepoBuilder::from_database(&mut repo, &db)
2834 .signing_root_keys(&[&KEYS[0]])
2835 .trusted_root_keys(&[&KEYS[1]])
2836 .trusted_targets_keys(&[&KEYS[1]])
2837 .trusted_snapshot_keys(&[&KEYS[1]])
2838 .trusted_timestamp_keys(&[&KEYS[1]])
2839 .commit()
2840 .await
2841 .unwrap();
2842
2843 assert!(metadata5.root().is_some());
2844 assert!(metadata5.targets().is_some());
2845 assert!(metadata5.snapshot().is_some());
2846 assert!(metadata5.timestamp().is_some());
2847
2848 db.update_metadata(&metadata5).unwrap();
2849
2850 assert_eq!(db.trusted_root().version(), 5);
2851 assert_eq!(db.trusted_targets().unwrap().version(), 5);
2852 assert_eq!(db.trusted_snapshot().unwrap().version(), 5);
2853 assert_eq!(db.trusted_timestamp().unwrap().version(), 5);
2854 })
2855 }
2856
2857 #[test]
2858 fn test_builder_expired_metadata_refreshes_metadata() {
2859 block_on(async move {
2860 let mut repo = EphemeralRepository::<Pouf1>::new();
2861
2862 let epoch = Utc.timestamp_opt(0, 0).unwrap();
2863 let root_expires = Duration::seconds(40);
2864 let targets_expires = Duration::seconds(30);
2865 let snapshot_expires = Duration::seconds(20);
2866 let timestamp_expires = Duration::seconds(10);
2867
2868 let current_time = epoch;
2869 let metadata1 = RepoBuilder::create(&mut repo)
2870 .current_time(current_time)
2871 .trusted_root_keys(&[&KEYS[0]])
2872 .trusted_targets_keys(&[&KEYS[0]])
2873 .trusted_snapshot_keys(&[&KEYS[0]])
2874 .trusted_timestamp_keys(&[&KEYS[0]])
2875 .root_expiration_duration(root_expires)
2876 .targets_expiration_duration(targets_expires)
2877 .snapshot_expiration_duration(snapshot_expires)
2878 .timestamp_expiration_duration(timestamp_expires)
2879 .commit()
2880 .await
2881 .unwrap();
2882
2883 let mut db =
2884 Database::from_trusted_metadata_with_start_time(&metadata1, ¤t_time).unwrap();
2885
2886 let current_time = epoch + timestamp_expires + Duration::seconds(1);
2888 let metadata2 = RepoBuilder::from_database(&mut repo, &db)
2889 .current_time(current_time)
2890 .trusted_root_keys(&[&KEYS[0]])
2891 .trusted_targets_keys(&[&KEYS[0]])
2892 .trusted_snapshot_keys(&[&KEYS[0]])
2893 .trusted_timestamp_keys(&[&KEYS[0]])
2894 .commit()
2895 .await
2896 .unwrap();
2897
2898 assert!(metadata2.root().is_none());
2899 assert!(metadata2.targets().is_none());
2900 assert!(metadata2.snapshot().is_none());
2901 assert!(metadata2.timestamp().is_some());
2902
2903 db.update_metadata_with_start_time(&metadata2, ¤t_time)
2904 .unwrap();
2905
2906 assert_eq!(db.trusted_root().version(), 1);
2907 assert_eq!(db.trusted_targets().unwrap().version(), 1);
2908 assert_eq!(db.trusted_snapshot().unwrap().version(), 1);
2909 assert_eq!(db.trusted_timestamp().unwrap().version(), 2);
2910
2911 let current_time = epoch + snapshot_expires + Duration::seconds(1);
2913 let metadata3 = RepoBuilder::from_database(&mut repo, &db)
2914 .current_time(current_time)
2915 .trusted_root_keys(&[&KEYS[0]])
2916 .trusted_targets_keys(&[&KEYS[0]])
2917 .trusted_snapshot_keys(&[&KEYS[0]])
2918 .trusted_timestamp_keys(&[&KEYS[0]])
2919 .commit()
2920 .await
2921 .unwrap();
2922
2923 assert!(metadata3.root().is_none());
2924 assert!(metadata3.targets().is_none());
2925 assert!(metadata3.snapshot().is_some());
2926 assert!(metadata3.timestamp().is_some());
2927
2928 db.update_metadata_with_start_time(&metadata3, ¤t_time)
2929 .unwrap();
2930
2931 assert_eq!(db.trusted_root().version(), 1);
2932 assert_eq!(db.trusted_targets().unwrap().version(), 1);
2933 assert_eq!(db.trusted_snapshot().unwrap().version(), 2);
2934 assert_eq!(db.trusted_timestamp().unwrap().version(), 3);
2935
2936 let current_time = epoch + targets_expires + Duration::seconds(1);
2938 let metadata4 = RepoBuilder::from_database(&mut repo, &db)
2939 .current_time(current_time)
2940 .trusted_root_keys(&[&KEYS[0]])
2941 .trusted_targets_keys(&[&KEYS[0]])
2942 .trusted_snapshot_keys(&[&KEYS[0]])
2943 .trusted_timestamp_keys(&[&KEYS[0]])
2944 .commit()
2945 .await
2946 .unwrap();
2947
2948 assert!(metadata4.root().is_none());
2949 assert!(metadata4.targets().is_some());
2950 assert!(metadata4.snapshot().is_some());
2951 assert!(metadata4.timestamp().is_some());
2952
2953 db.update_metadata_with_start_time(&metadata4, ¤t_time)
2954 .unwrap();
2955
2956 assert_eq!(db.trusted_root().version(), 1);
2957 assert_eq!(db.trusted_targets().unwrap().version(), 2);
2958 assert_eq!(db.trusted_snapshot().unwrap().version(), 3);
2959 assert_eq!(db.trusted_timestamp().unwrap().version(), 4);
2960
2961 let current_time = epoch + root_expires + Duration::seconds(1);
2968 let metadata5 = RepoBuilder::from_database(&mut repo, &db)
2969 .current_time(current_time)
2970 .trusted_root_keys(&[&KEYS[0]])
2971 .trusted_targets_keys(&[&KEYS[0]])
2972 .trusted_snapshot_keys(&[&KEYS[0]])
2973 .trusted_timestamp_keys(&[&KEYS[0]])
2974 .commit()
2975 .await
2976 .unwrap();
2977
2978 assert!(metadata5.root().is_some());
2979 assert!(metadata5.targets().is_some());
2980 assert!(metadata5.snapshot().is_some());
2981 assert!(metadata5.timestamp().is_some());
2982
2983 db.update_metadata_with_start_time(&metadata5, ¤t_time)
2984 .unwrap();
2985
2986 assert_eq!(db.trusted_root().version(), 2);
2987 assert_eq!(db.trusted_targets().unwrap().version(), 3);
2988 assert_eq!(db.trusted_snapshot().unwrap().version(), 4);
2989 assert_eq!(db.trusted_timestamp().unwrap().version(), 5);
2990 })
2991 }
2992
2993 #[test]
2994 fn test_adding_target_refreshes_metadata() {
2995 block_on(async move {
2996 let mut repo = EphemeralRepository::<Pouf1>::new();
2997
2998 let metadata1 = RepoBuilder::create(&mut repo)
2999 .trusted_root_keys(&[&KEYS[0]])
3000 .trusted_targets_keys(&[&KEYS[0]])
3001 .trusted_snapshot_keys(&[&KEYS[0]])
3002 .trusted_timestamp_keys(&[&KEYS[0]])
3003 .commit()
3004 .await
3005 .unwrap();
3006
3007 let mut db = Database::from_trusted_metadata(&metadata1).unwrap();
3008
3009 let target_path = TargetPath::new("foo").unwrap();
3010 let target_file: &[u8] = b"foo file";
3011
3012 let metadata2 = RepoBuilder::from_database(&mut repo, &db)
3013 .trusted_root_keys(&[&KEYS[0]])
3014 .trusted_targets_keys(&[&KEYS[0]])
3015 .trusted_snapshot_keys(&[&KEYS[0]])
3016 .trusted_timestamp_keys(&[&KEYS[0]])
3017 .add_target(target_path, Cursor::new(target_file))
3018 .await
3019 .unwrap()
3020 .commit()
3021 .await
3022 .unwrap();
3023
3024 assert!(metadata2.root().is_none());
3025 assert!(metadata2.targets().is_some());
3026 assert!(metadata2.snapshot().is_some());
3027 assert!(metadata2.timestamp().is_some());
3028
3029 db.update_metadata(&metadata2).unwrap();
3030
3031 assert_eq!(db.trusted_root().version(), 1);
3032 assert_eq!(db.trusted_targets().unwrap().version(), 2);
3033 assert_eq!(db.trusted_snapshot().unwrap().version(), 2);
3034 assert_eq!(db.trusted_timestamp().unwrap().version(), 2);
3035 })
3036 }
3037
3038 #[test]
3039 fn test_time_versioning() {
3040 block_on(async move {
3041 let mut repo = EphemeralRepository::<Pouf1>::new();
3042
3043 let current_time = Utc.timestamp_opt(5, 0).unwrap();
3044 let metadata = RepoBuilder::create(&mut repo)
3045 .current_time(current_time)
3046 .time_versioning(true)
3047 .trusted_root_keys(&[&KEYS[0]])
3048 .trusted_targets_keys(&[&KEYS[0]])
3049 .trusted_snapshot_keys(&[&KEYS[0]])
3050 .trusted_timestamp_keys(&[&KEYS[0]])
3051 .commit()
3052 .await
3053 .unwrap();
3054
3055 let mut db =
3056 Database::from_trusted_metadata_with_start_time(&metadata, ¤t_time).unwrap();
3057
3058 assert_eq!(db.trusted_root().version(), 1);
3060 assert_eq!(db.trusted_targets().map(|m| m.version()), Some(5));
3061 assert_eq!(db.trusted_snapshot().map(|m| m.version()), Some(5));
3062 assert_eq!(db.trusted_timestamp().map(|m| m.version()), Some(5));
3063
3064 let metadata = RepoBuilder::from_database(&mut repo, &db)
3066 .current_time(current_time)
3067 .time_versioning(true)
3068 .trusted_root_keys(&[&KEYS[0]])
3069 .trusted_targets_keys(&[&KEYS[0]])
3070 .trusted_snapshot_keys(&[&KEYS[0]])
3071 .trusted_timestamp_keys(&[&KEYS[0]])
3072 .stage_root()
3073 .unwrap()
3074 .stage_targets()
3075 .unwrap()
3076 .commit()
3077 .await
3078 .unwrap();
3079
3080 db.update_metadata_with_start_time(&metadata, ¤t_time)
3081 .unwrap();
3082
3083 assert_eq!(db.trusted_root().version(), 2);
3084 assert_eq!(db.trusted_targets().map(|m| m.version()), Some(6));
3085 assert_eq!(db.trusted_snapshot().map(|m| m.version()), Some(6));
3086 assert_eq!(db.trusted_timestamp().map(|m| m.version()), Some(6));
3087
3088 let current_time = Utc.timestamp_opt(10, 0).unwrap();
3090 let metadata = RepoBuilder::from_database(&mut repo, &db)
3091 .current_time(current_time)
3092 .time_versioning(true)
3093 .trusted_root_keys(&[&KEYS[0]])
3094 .trusted_targets_keys(&[&KEYS[0]])
3095 .trusted_snapshot_keys(&[&KEYS[0]])
3096 .trusted_timestamp_keys(&[&KEYS[0]])
3097 .stage_root()
3098 .unwrap()
3099 .stage_targets()
3100 .unwrap()
3101 .commit()
3102 .await
3103 .unwrap();
3104
3105 db.update_metadata_with_start_time(&metadata, ¤t_time)
3106 .unwrap();
3107
3108 assert_eq!(db.trusted_root().version(), 3);
3109 assert_eq!(db.trusted_targets().map(|m| m.version()), Some(10));
3110 assert_eq!(db.trusted_snapshot().map(|m| m.version()), Some(10));
3111 assert_eq!(db.trusted_timestamp().map(|m| m.version()), Some(10));
3112 })
3113 }
3114
3115 #[test]
3116 fn test_time_versioning_falls_back_to_monotonic() {
3117 block_on(async move {
3118 let mut repo = EphemeralRepository::<Pouf1>::new();
3119
3120 let current_time = Utc.timestamp_opt(0, 0).unwrap();
3122 let metadata = RepoBuilder::create(&mut repo)
3123 .current_time(current_time)
3124 .time_versioning(true)
3125 .trusted_root_keys(&[&KEYS[0]])
3126 .trusted_targets_keys(&[&KEYS[0]])
3127 .trusted_snapshot_keys(&[&KEYS[0]])
3128 .trusted_timestamp_keys(&[&KEYS[0]])
3129 .commit()
3130 .await
3131 .unwrap();
3132
3133 let mut db =
3134 Database::from_trusted_metadata_with_start_time(&metadata, ¤t_time).unwrap();
3135
3136 assert_eq!(db.trusted_root().version(), 1);
3137 assert_eq!(db.trusted_targets().map(|m| m.version()), Some(1));
3138 assert_eq!(db.trusted_snapshot().map(|m| m.version()), Some(1));
3139 assert_eq!(db.trusted_timestamp().map(|m| m.version()), Some(1));
3140
3141 let current_time = Utc.timestamp_opt(0, 3).unwrap();
3143 let metadata = RepoBuilder::from_database(&mut repo, &db)
3144 .current_time(current_time)
3145 .time_versioning(true)
3146 .trusted_root_keys(&[&KEYS[0]])
3147 .trusted_targets_keys(&[&KEYS[0]])
3148 .trusted_snapshot_keys(&[&KEYS[0]])
3149 .trusted_timestamp_keys(&[&KEYS[0]])
3150 .stage_root()
3151 .unwrap()
3152 .stage_targets()
3153 .unwrap()
3154 .commit()
3155 .await
3156 .unwrap();
3157
3158 db.update_metadata_with_start_time(&metadata, ¤t_time)
3159 .unwrap();
3160
3161 assert_eq!(db.trusted_root().version(), 2);
3162 assert_eq!(db.trusted_targets().map(|m| m.version()), Some(2));
3163 assert_eq!(db.trusted_snapshot().map(|m| m.version()), Some(2));
3164 assert_eq!(db.trusted_timestamp().map(|m| m.version()), Some(2));
3165 })
3166 }
3167
3168 #[test]
3169 fn test_builder_errs_if_no_keys() {
3170 block_on(async move {
3171 let repo = EphemeralRepository::<Pouf1>::new();
3172
3173 let metadata = RepoBuilder::create(&repo)
3174 .trusted_root_keys(&[&KEYS[0]])
3175 .trusted_targets_keys(&[&KEYS[0]])
3176 .trusted_snapshot_keys(&[&KEYS[0]])
3177 .trusted_timestamp_keys(&[&KEYS[0]])
3178 .commit()
3179 .await
3180 .unwrap();
3181
3182 let db = Database::from_trusted_metadata(&metadata).unwrap();
3183
3184 match RepoBuilder::from_database(&repo, &db).stage_root() {
3185 Err(Error::MetadataRoleDoesNotHaveEnoughKeyIds {
3186 role,
3187 key_ids: 0,
3188 threshold: 1,
3189 }) if role == MetadataPath::root() => {}
3190 Err(err) => panic!("unexpected error: {}", err),
3191 Ok(_) => panic!("unexpected success"),
3192 }
3193
3194 match RepoBuilder::from_database(&repo, &db)
3195 .trusted_root_keys(&[&KEYS[0]])
3196 .stage_root_if_necessary()
3197 .unwrap()
3198 .stage_targets()
3199 {
3200 Err(Error::MissingPrivateKey { role }) if role == MetadataPath::targets() => {}
3201 Err(err) => panic!("unexpected error: {}", err),
3202 Ok(_) => panic!("unexpected success"),
3203 }
3204
3205 match RepoBuilder::from_database(&repo, &db)
3206 .trusted_root_keys(&[&KEYS[0]])
3207 .trusted_targets_keys(&[&KEYS[0]])
3208 .stage_root_if_necessary()
3209 .unwrap()
3210 .stage_targets_if_necessary()
3211 .unwrap()
3212 .stage_snapshot()
3213 {
3214 Err(Error::MissingPrivateKey { role }) if role == MetadataPath::snapshot() => {}
3215 Err(err) => panic!("unexpected error: {}", err),
3216 Ok(_) => panic!("unexpected success"),
3217 }
3218
3219 match RepoBuilder::from_database(&repo, &db)
3220 .trusted_root_keys(&[&KEYS[0]])
3221 .trusted_targets_keys(&[&KEYS[0]])
3222 .trusted_snapshot_keys(&[&KEYS[0]])
3223 .stage_root_if_necessary()
3224 .unwrap()
3225 .stage_targets_if_necessary()
3226 .unwrap()
3227 .stage_snapshot_if_necessary()
3228 .unwrap()
3229 .stage_timestamp()
3230 {
3231 Err(Error::MissingPrivateKey { role }) if role == MetadataPath::timestamp() => {}
3232 Err(err) => panic!("unexpected error: {}", err),
3233 Ok(_) => panic!("unexpected success"),
3234 }
3235 })
3236 }
3237}