Skip to content

Commit ddab2d6

Browse files
committed
Allow expiration duration to be customized
Changing the expiration duration is one of the main causes for users to call the `stage_*_with_builder`. This allows more use cases to avoid needing to call those functions by allowing the user to change the expiration duration without forcing a new metadata to be generated.
1 parent 34e2fd3 commit ddab2d6

File tree

1 file changed

+87
-27
lines changed

1 file changed

+87
-27
lines changed

tuf/src/repo_builder.rs

Lines changed: 87 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ mod private {
3737
impl<D: DataInterchange> Sealed for Done<D> {}
3838
}
3939

40+
const DEFAULT_ROOT_EXPIRATION_DAYS: i64 = 365;
41+
const DEFAULT_TARGETS_EXPIRATION_DAYS: i64 = 90;
42+
const DEFAULT_SNAPSHOT_EXPIRATION_DAYS: i64 = 7;
43+
const DEFAULT_TIMESTAMP_EXPIRATION_DAYS: i64 = 1;
44+
4045
/// Trait to track each of the [RepoBuilder] building states.
4146
///
4247
/// This trait is [sealed] to make
@@ -214,6 +219,10 @@ where
214219
trusted_targets_keys: Vec<&'a dyn PrivateKey>,
215220
trusted_snapshot_keys: Vec<&'a dyn PrivateKey>,
216221
trusted_timestamp_keys: Vec<&'a dyn PrivateKey>,
222+
root_expiration_duration: Duration,
223+
targets_expiration_duration: Duration,
224+
snapshot_expiration_duration: Duration,
225+
timestamp_expiration_duration: Duration,
217226
_interchange: PhantomData<D>,
218227
}
219228

@@ -358,6 +367,10 @@ where
358367
trusted_targets_keys: vec![],
359368
trusted_snapshot_keys: vec![],
360369
trusted_timestamp_keys: vec![],
370+
root_expiration_duration: Duration::days(DEFAULT_ROOT_EXPIRATION_DAYS),
371+
targets_expiration_duration: Duration::days(DEFAULT_TARGETS_EXPIRATION_DAYS),
372+
snapshot_expiration_duration: Duration::days(DEFAULT_SNAPSHOT_EXPIRATION_DAYS),
373+
timestamp_expiration_duration: Duration::days(DEFAULT_TIMESTAMP_EXPIRATION_DAYS),
361374
_interchange: PhantomData,
362375
},
363376
state: Root {
@@ -442,6 +455,10 @@ where
442455
trusted_targets_keys: vec![],
443456
trusted_snapshot_keys: vec![],
444457
trusted_timestamp_keys: vec![],
458+
root_expiration_duration: Duration::days(DEFAULT_ROOT_EXPIRATION_DAYS),
459+
targets_expiration_duration: Duration::days(DEFAULT_TARGETS_EXPIRATION_DAYS),
460+
snapshot_expiration_duration: Duration::days(DEFAULT_SNAPSHOT_EXPIRATION_DAYS),
461+
timestamp_expiration_duration: Duration::days(DEFAULT_TIMESTAMP_EXPIRATION_DAYS),
445462
_interchange: PhantomData,
446463
},
447464
state: Root { builder },
@@ -457,6 +474,55 @@ where
457474
self
458475
}
459476

477+
/// Sets that the root metadata will expire after after this duration past the current time.
478+
///
479+
/// Defaults to 365 days.
480+
///
481+
/// Note: calling this function will only change what is the metadata expiration we'll use if we
482+
/// create a new root metadata if we call [RepoBuilder::stage_root], or we decide a new one is
483+
/// needed when we call [RepoBuilder::stage_root_if_necessary].
484+
pub fn root_expiration_duration(mut self, duration: Duration) -> Self {
485+
self.ctx.root_expiration_duration = duration;
486+
self
487+
}
488+
489+
/// Sets that the targets metadata will expire after after this duration past the current time.
490+
///
491+
/// Defaults to 90 days.
492+
///
493+
/// Note: calling this function will only change what is the metadata expiration we'll use if we
494+
/// create a new targets metadata if we call [RepoBuilder::stage_targets], or we decide a new
495+
/// one is needed when we call [RepoBuilder::stage_targets_if_necessary].
496+
pub fn targets_expiration_duration(mut self, duration: Duration) -> Self {
497+
self.ctx.targets_expiration_duration = duration;
498+
self
499+
}
500+
501+
/// Sets that the snapshot metadata will expire after after this duration past the current time.
502+
///
503+
/// Defaults to 7 days.
504+
///
505+
/// Note: calling this function will only change what is the metadata expiration we'll use if we
506+
/// create a new snapshot metadata if we call [RepoBuilder::stage_snapshot], or we decide a new
507+
/// one is needed when we call [RepoBuilder::stage_snapshot_if_necessary].
508+
pub fn snapshot_expiration_duration(mut self, duration: Duration) -> Self {
509+
self.ctx.snapshot_expiration_duration = duration;
510+
self
511+
}
512+
513+
/// Sets that the timestamp metadata will expire after after this duration past the current
514+
/// time.
515+
///
516+
/// Defaults to 1 day.
517+
///
518+
/// Note: calling this function will only change what is the metadata expiration we'll use if we
519+
/// create a new timestamp metadata if we call [RepoBuilder::stage_timestamp], or we decide a
520+
/// new one is needed when we call [RepoBuilder::stage_timestamp_if_necessary].
521+
pub fn timestamp_expiration_duration(mut self, duration: Duration) -> Self {
522+
self.ctx.timestamp_expiration_duration = duration;
523+
self
524+
}
525+
460526
/// Sign the root metadata with `keys`, but do not include the keys as trusted root keys in the
461527
/// root metadata. This is typically used to support root key rotation.
462528
pub fn signing_root_keys(mut self, keys: &[&'a dyn PrivateKey]) -> Self {
@@ -619,7 +685,7 @@ where
619685
.state
620686
.builder
621687
.version(next_version)
622-
.expires(self.ctx.current_time + Duration::days(365));
688+
.expires(self.ctx.current_time + self.ctx.root_expiration_duration);
623689
let root = f(root_builder).build()?;
624690

625691
let raw_root = sign(
@@ -840,7 +906,9 @@ where
840906
where
841907
F: FnOnce(TargetsMetadataBuilder) -> TargetsMetadataBuilder,
842908
{
843-
let mut targets_builder = TargetsMetadataBuilder::new();
909+
let mut targets_builder = TargetsMetadataBuilder::new()
910+
.expires(self.ctx.current_time + self.ctx.targets_expiration_duration);
911+
844912
let mut delegations_builder = DelegationsBuilder::new();
845913

846914
if let Some(trusted_targets) = self.ctx.db.and_then(|db| db.trusted_targets()) {
@@ -1020,8 +1088,8 @@ where
10201088
where
10211089
F: FnOnce(SnapshotMetadataBuilder) -> SnapshotMetadataBuilder,
10221090
{
1023-
let mut snapshot_builder =
1024-
SnapshotMetadataBuilder::new().expires(self.ctx.current_time + Duration::days(7));
1091+
let mut snapshot_builder = SnapshotMetadataBuilder::new()
1092+
.expires(self.ctx.current_time + self.ctx.snapshot_expiration_duration);
10251093

10261094
if let Some(trusted_snapshot) = self.ctx.db.and_then(|db| db.trusted_snapshot()) {
10271095
let next_version = trusted_snapshot.version().checked_add(1).ok_or_else(|| {
@@ -1203,7 +1271,7 @@ where
12031271

12041272
let timestamp_builder = TimestampMetadataBuilder::from_metadata_description(description)
12051273
.version(next_version)
1206-
.expires(self.ctx.current_time + Duration::days(1));
1274+
.expires(self.ctx.current_time + self.ctx.timestamp_expiration_duration);
12071275

12081276
let timestamp = f(timestamp_builder).build()?;
12091277
let raw_timestamp = sign(
@@ -1424,18 +1492,14 @@ where
14241492

14251493
#[cfg(test)]
14261494
mod tests {
1427-
use chrono::NaiveDateTime;
1428-
1429-
use crate::repository::RepositoryProvider;
1430-
14311495
use {
14321496
super::*,
14331497
crate::{
14341498
client::{Client, Config},
14351499
crypto::Ed25519PrivateKey,
14361500
interchange::Json,
14371501
metadata::SignedMetadata,
1438-
repository::EphemeralRepository,
1502+
repository::{EphemeralRepository, RepositoryProvider},
14391503
},
14401504
assert_matches::assert_matches,
14411505
chrono::{
@@ -2732,11 +2796,11 @@ mod tests {
27322796
block_on(async move {
27332797
let mut repo = EphemeralRepository::<Json>::new();
27342798

2735-
let epoch = DateTime::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc);
2736-
let root_expires = epoch + Duration::seconds(40);
2737-
let targets_expires = epoch + Duration::seconds(30);
2738-
let snapshot_expires = epoch + Duration::seconds(20);
2739-
let timestamp_expires = epoch + Duration::seconds(10);
2799+
let epoch = Utc.timestamp(0, 0);
2800+
let root_expires = Duration::seconds(40);
2801+
let targets_expires = Duration::seconds(30);
2802+
let snapshot_expires = Duration::seconds(20);
2803+
let timestamp_expires = Duration::seconds(10);
27402804

27412805
let current_time = epoch;
27422806
let metadata1 = RepoBuilder::create(&mut repo)
@@ -2745,14 +2809,10 @@ mod tests {
27452809
.trusted_targets_keys(&[&KEYS[0]])
27462810
.trusted_snapshot_keys(&[&KEYS[0]])
27472811
.trusted_timestamp_keys(&[&KEYS[0]])
2748-
.stage_root_with_builder(|builder| builder.expires(root_expires))
2749-
.unwrap()
2750-
.stage_targets_with_builder(|builder| builder.expires(targets_expires))
2751-
.unwrap()
2752-
.stage_snapshot_with_builder(|builder| builder.expires(snapshot_expires))
2753-
.unwrap()
2754-
.stage_timestamp_with_builder(|builder| builder.expires(timestamp_expires))
2755-
.unwrap()
2812+
.root_expiration_duration(root_expires)
2813+
.targets_expiration_duration(targets_expires)
2814+
.snapshot_expiration_duration(snapshot_expires)
2815+
.timestamp_expiration_duration(timestamp_expires)
27562816
.commit()
27572817
.await
27582818
.unwrap();
@@ -2761,7 +2821,7 @@ mod tests {
27612821
Database::from_trusted_metadata_with_start_time(&metadata1, &current_time).unwrap();
27622822

27632823
// Advance time to past the timestamp expiration.
2764-
let current_time = timestamp_expires + Duration::seconds(1);
2824+
let current_time = epoch + timestamp_expires + Duration::seconds(1);
27652825
let metadata2 = RepoBuilder::from_database(&mut repo, &db)
27662826
.current_time(current_time)
27672827
.trusted_root_keys(&[&KEYS[0]])
@@ -2786,7 +2846,7 @@ mod tests {
27862846
assert_eq!(db.trusted_timestamp().unwrap().version(), 2);
27872847

27882848
// Advance time to past the snapshot expiration.
2789-
let current_time = snapshot_expires + Duration::seconds(1);
2849+
let current_time = epoch + snapshot_expires + Duration::seconds(1);
27902850
let metadata3 = RepoBuilder::from_database(&mut repo, &db)
27912851
.current_time(current_time)
27922852
.trusted_root_keys(&[&KEYS[0]])
@@ -2811,7 +2871,7 @@ mod tests {
28112871
assert_eq!(db.trusted_timestamp().unwrap().version(), 3);
28122872

28132873
// Advance time to past the targets expiration.
2814-
let current_time = targets_expires + Duration::seconds(1);
2874+
let current_time = epoch + targets_expires + Duration::seconds(1);
28152875
let metadata4 = RepoBuilder::from_database(&mut repo, &db)
28162876
.current_time(current_time)
28172877
.trusted_root_keys(&[&KEYS[0]])
@@ -2841,7 +2901,7 @@ mod tests {
28412901
// snapshot.
28422902
//
28432903
// [update-root]: https://theupdateframework.github.io/specification/v1.0.30/#update-root
2844-
let current_time = root_expires + Duration::seconds(1);
2904+
let current_time = epoch + root_expires + Duration::seconds(1);
28452905
let metadata5 = RepoBuilder::from_database(&mut repo, &db)
28462906
.current_time(current_time)
28472907
.trusted_root_keys(&[&KEYS[0]])

0 commit comments

Comments
 (0)