Skip to content

Commit 3345151

Browse files
committed
tough: detect rollback in snapshot metadata
For each role in a previous snapshot, the role must be present in the new snapshot and must have a version greater than or equal to the version in the previous snapshot. TUF specification v1.0.33 section 5.5.5.
1 parent 9b400e1 commit 3345151

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

tough/src/error.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,40 @@ pub enum Error {
284284
timestamp_old: u64,
285285
},
286286

287+
/// The snapshot meta must contain targets.json
288+
#[snafu(display("Snapshot version {} does not contain targets.json", version))]
289+
SnapshotTargetsMetaMissing { version: u64 },
290+
291+
/// Any role in the trusted snapshot meta must also appear in the new snapshot meta
292+
#[snafu(display(
293+
"Role {} appears in snapshot version {} but not version {}",
294+
role,
295+
old_version,
296+
new_version
297+
))]
298+
SnapshotRoleMissing {
299+
role: String,
300+
old_version: u64,
301+
new_version: u64,
302+
},
303+
304+
/// Role version in trusted snapshot must be less than or equal to version in new snapshot
305+
#[snafu(display(
306+
"Role {} version {} in snapshot {} is greater than version {} in snapshot {}",
307+
role,
308+
old_role_version,
309+
old_snapshot_version,
310+
new_role_version,
311+
new_snapshot_version
312+
))]
313+
SnapshotRoleRollback {
314+
role: String,
315+
old_role_version: u64,
316+
old_snapshot_version: u64,
317+
new_role_version: u64,
318+
new_snapshot_version: u64,
319+
},
320+
287321
/// The library failed to parse a metadata file, either because it was not valid JSON or it did
288322
/// not conform to the expected schema.
289323
///

tough/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ use async_recursion::async_recursion;
6565
pub use async_trait::async_trait;
6666
pub use bytes::Bytes;
6767
use chrono::{DateTime, Utc};
68+
use error::SnapshotTargetsMetaMissingSnafu;
6869
use futures::StreamExt;
6970
use futures_core::Stream;
7071
use log::warn;
@@ -1060,6 +1061,13 @@ async fn load_snapshot(
10601061
role: RoleType::Snapshot,
10611062
})?;
10621063

1064+
// 4.4 Check that snapshot.meta contains at least targets.json
1065+
ensure!(
1066+
snapshot.signed.meta.contains_key("targets.json"),
1067+
SnapshotTargetsMetaMissingSnafu {
1068+
version: snapshot.signed.version,
1069+
}
1070+
);
10631071
// 3.3. Check for a rollback attack.
10641072
//
10651073
// 3.3.1. Note that the trusted snapshot metadata file may be checked for authenticity, but its
@@ -1090,6 +1098,35 @@ async fn load_snapshot(
10901098
// metadata file, if any, MUST continue to be listed in the new snapshot metadata
10911099
// file. If any of these conditions are not met, discard the new snapshot metadata
10921100
// file, abort the update cycle, and report the failure.
1101+
1102+
// Ensure that the trusted snapshot has at least targets.json
1103+
ensure!(
1104+
old_snapshot.signed.meta.contains_key("targets.json"),
1105+
error::SnapshotTargetsMetaMissingSnafu {
1106+
version: old_snapshot.signed.version,
1107+
}
1108+
);
1109+
for (name, meta) in &old_snapshot.signed.meta {
1110+
ensure!(
1111+
snapshot.signed.meta.contains_key(name),
1112+
error::SnapshotRoleMissingSnafu {
1113+
role: name,
1114+
old_version: old_snapshot.signed.version,
1115+
new_version: snapshot.signed.version,
1116+
}
1117+
);
1118+
let new_meta = snapshot.signed.meta.get(name).unwrap();
1119+
ensure!(
1120+
meta.version <= new_meta.version,
1121+
error::SnapshotRoleRollbackSnafu {
1122+
role: name,
1123+
old_role_version: meta.version,
1124+
old_snapshot_version: old_snapshot.signed.version,
1125+
new_role_version: new_meta.version,
1126+
new_snapshot_version: snapshot.signed.version,
1127+
}
1128+
);
1129+
}
10931130
if let Some(old_targets_meta) = old_snapshot.signed.meta.get("targets.json") {
10941131
let targets_meta =
10951132
snapshot

0 commit comments

Comments
 (0)