blob: 56dda7bb8f73b41a70e965c5da59f8aca71f02fb [file] [log] [blame]
//! Components needed to verify TUF metadata and targets.
use chrono::offset::Utc;
use std::collections::{HashSet, HashMap};
use std::marker::PhantomData;
use Result;
use crypto::KeyId;
use error::Error;
use interchange::DataInterchange;
use metadata::{SignedMetadata, RootMetadata, TimestampMetadata, Role, SnapshotMetadata,
MetadataPath, TargetsMetadata, VirtualTargetPath, TargetDescription, Delegations};
/// Contains trusted TUF metadata and can be used to verify other metadata and targets.
#[derive(Debug)]
pub struct Tuf<D: DataInterchange> {
root: RootMetadata,
snapshot: Option<SnapshotMetadata>,
targets: Option<TargetsMetadata>,
timestamp: Option<TimestampMetadata>,
delegations: HashMap<MetadataPath, TargetsMetadata>,
interchange: PhantomData<D>,
}
impl<D: DataInterchange> Tuf<D> {
/// Create a new `TUF` struct from a known set of pinned root keys that are used to verify the
/// signed metadata.
pub fn from_root_pinned<'a, I>(
mut signed_root: SignedMetadata<D, RootMetadata>,
root_key_ids: I,
) -> Result<Self>
where
I: IntoIterator<Item = &'a KeyId>,
{
let root_key_ids = root_key_ids.into_iter().collect::<HashSet<&KeyId>>();
signed_root.signatures_mut().retain(|s| {
root_key_ids.contains(s.key_id())
});
Self::from_root(&signed_root)
}
/// Create a new `TUF` struct from a piece of metadata that is assumed to be trusted.
///
/// **WARNING**: This is trust-on-first-use (TOFU) and offers weaker security guarantees than
/// the related method `from_root_pinned`.
pub fn from_root(signed_root: &SignedMetadata<D, RootMetadata>) -> Result<Self> {
let root = D::deserialize::<RootMetadata>(signed_root.signed())?;
let _ = signed_root.verify(
root.root().threshold(),
root.keys().iter().filter_map(
|(k, v)| if root.root()
.key_ids()
.contains(k)
{
Some(v)
} else {
None
},
),
)?;
Ok(Tuf {
root,
snapshot: None,
targets: None,
timestamp: None,
delegations: HashMap::new(),
interchange: PhantomData,
})
}
/// An immutable reference to the root metadata.
pub fn root(&self) -> &RootMetadata {
&self.root
}
/// An immutable reference to the optional snapshot metadata.
pub fn snapshot(&self) -> Option<&SnapshotMetadata> {
self.snapshot.as_ref()
}
/// An immutable reference to the optional targets metadata.
pub fn targets(&self) -> Option<&TargetsMetadata> {
self.targets.as_ref()
}
/// An immutable reference to the optional timestamp metadata.
pub fn timestamp(&self) -> Option<&TimestampMetadata> {
self.timestamp.as_ref()
}
/// An immutable reference to the delegated metadata.
pub fn delegations(&self) -> &HashMap<MetadataPath, TargetsMetadata> {
&self.delegations
}
/// Verify and update the root metadata.
pub fn update_root(&mut self, signed_root: &SignedMetadata<D, RootMetadata>) -> Result<bool> {
signed_root.verify(
self.root.root().threshold(),
self.root.keys().iter().filter_map(|(k, v)| {
if self.root.root().key_ids().contains(k) {
Some(v)
} else {
None
}
}),
)?;
let root = D::deserialize::<RootMetadata>(signed_root.signed())?;
match root.version() {
x if x == self.root.version() => {
info!(
"Attempted to update root to new metadata with the same version. \
Refusing to update."
);
return Ok(false);
}
x if x < self.root.version() => {
return Err(Error::VerificationFailure(format!(
"Attempted to roll back root metadata at version {} to {}.",
self.root.version(),
x
)))
}
_ => (),
}
let _ = signed_root.verify(
root.root().threshold(),
root.keys().iter().filter_map(
|(k, v)| if root.root()
.key_ids()
.contains(k)
{
Some(v)
} else {
None
},
),
)?;
self.purge_metadata();
self.root = root;
Ok(true)
}
/// Verify and update the timestamp metadata.
pub fn update_timestamp(
&mut self,
signed_timestamp: &SignedMetadata<D, TimestampMetadata>,
) -> Result<bool> {
signed_timestamp.verify(
self.root.timestamp().threshold(),
self.root.keys().iter().filter_map(
|(k, v)| {
if self.root.timestamp().key_ids().contains(k) {
Some(v)
} else {
None
}
},
),
)?;
let current_version = self.timestamp.as_ref().map(|t| t.version()).unwrap_or(0);
let timestamp: TimestampMetadata = D::deserialize(&signed_timestamp.signed())?;
if timestamp.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Timestamp));
}
if timestamp.version() < current_version {
Err(Error::VerificationFailure(format!(
"Attempted to roll back timestamp metadata at version {} to {}.",
current_version,
timestamp.version()
)))
} else if timestamp.version() == current_version {
Ok(false)
} else {
if self.snapshot.as_ref().map(|s| s.version()).unwrap_or(0) !=
timestamp.snapshot().version()
{
self.snapshot = None;
}
self.timestamp = Some(timestamp);
Ok(true)
}
}
/// Verify and update the snapshot metadata.
pub fn update_snapshot(
&mut self,
signed_snapshot: &SignedMetadata<D, SnapshotMetadata>,
) -> Result<bool> {
let snapshot = {
let root = self.safe_root_ref()?;
let timestamp = self.safe_timestamp_ref()?;
let current_version = self.snapshot.as_ref().map(|t| t.version()).unwrap_or(0);
if timestamp.snapshot().version() < current_version {
return Err(Error::VerificationFailure(format!(
"Attempted to roll back snapshot metadata at version {} to {}.",
current_version,
timestamp.snapshot().version()
)));
} else if timestamp.snapshot().version() == current_version {
return Ok(false);
}
signed_snapshot.verify(
root.snapshot().threshold(),
self.root.keys().iter().filter_map(
|(k, v)| {
if root.snapshot().key_ids().contains(k) {
Some(v)
} else {
None
}
},
),
)?;
let snapshot: SnapshotMetadata = D::deserialize(&signed_snapshot.signed())?;
if snapshot.version() != timestamp.snapshot().version() {
return Err(Error::VerificationFailure(format!(
"The timestamp metadata reported that the snapshot metadata should be at \
version {} but version {} was found instead.",
timestamp.snapshot().version(),
snapshot.version()
)));
}
// Note: this doesn't check the expiration because we need to be able to update it
// regardless so we can prevent rollback attacks againsts targets/delegations.
snapshot
};
if self.targets.as_ref().map(|s| s.version()).unwrap_or(0) !=
snapshot
.meta()
.get(&MetadataPath::from_role(&Role::Targets))
.map(|m| m.version())
.unwrap_or(0)
{
self.targets = None;
}
self.snapshot = Some(snapshot);
self.purge_delegations();
Ok(true)
}
fn purge_delegations(&mut self) {
let purge = {
let snapshot = match self.snapshot() {
Some(s) => s,
None => return,
};
let mut purge = HashSet::new();
for (role, definition) in snapshot.meta().iter() {
let delegation = match self.delegations.get(role) {
Some(d) => d,
None => continue,
};
if delegation.version() > definition.version() {
let _ = purge.insert(role.clone());
continue;
}
}
purge
};
for role in purge.iter() {
let _ = self.delegations.remove(role);
}
}
/// Verify and update the targets metadata.
pub fn update_targets(
&mut self,
signed_targets: &SignedMetadata<D, TargetsMetadata>,
) -> Result<bool> {
let targets = {
let root = self.safe_root_ref()?;
let snapshot = self.safe_snapshot_ref()?;
let targets_description = snapshot
.meta()
.get(&MetadataPath::from_role(&Role::Targets))
.ok_or_else(|| {
Error::VerificationFailure(
"Snapshot metadata had no description of the targets metadata".into(),
)
})?;
let current_version = self.targets.as_ref().map(|t| t.version()).unwrap_or(0);
if targets_description.version() < current_version {
return Err(Error::VerificationFailure(format!(
"Attempted to roll back targets metadata at version {} to {}.",
current_version,
targets_description.version()
)));
} else if targets_description.version() == current_version {
return Ok(false);
}
signed_targets.verify(
root.targets().threshold(),
root.keys().iter().filter_map(|(k, v)| {
if root.targets().key_ids().contains(k) {
Some(v)
} else {
None
}
}),
)?;
let targets: TargetsMetadata = D::deserialize(&signed_targets.signed())?;
if targets.version() != targets_description.version() {
return Err(Error::VerificationFailure(format!(
"The timestamp metadata reported that the targets metadata should be at \
version {} but version {} was found instead.",
targets_description.version(),
targets.version()
)));
}
if targets.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Snapshot));
}
targets
};
self.targets = Some(targets);
Ok(true)
}
/// Verify and update a delegation metadata.
pub fn update_delegation(
&mut self,
role: &MetadataPath,
signed: &SignedMetadata<D, TargetsMetadata>,
) -> Result<bool> {
let delegation = {
let _ = self.safe_root_ref()?;
let snapshot = self.safe_snapshot_ref()?;
let targets = self.safe_targets_ref()?;
let targets_delegations = match targets.delegations() {
Some(d) => d,
None => {
return Err(Error::VerificationFailure(
"Delegations not authorized".into(),
))
}
};
let delegation_description = match snapshot.meta().get(role) {
Some(d) => d,
None => {
return Err(Error::VerificationFailure(format!(
"The degated role {:?} was not present in the snapshot metadata.",
role
)))
}
};
let current_version = self.delegations.get(role).map(|t| t.version()).unwrap_or(0);
if delegation_description.version() < current_version {
return Err(Error::VerificationFailure(format!(
"Snapshot metadata did listed delegation {:?} version as {} but current\
version is {}",
role,
delegation_description.version(),
current_version
)));
} else if current_version == delegation_description.version() {
return Ok(false);
}
for (_, delegated_targets) in self.delegations.iter() {
let parent = match delegated_targets.delegations() {
Some(d) => d,
None => &targets_delegations,
};
let delegation = match parent.roles().iter().filter(|r| r.role() == role).next() {
Some(d) => d,
None => continue,
};
signed.verify(
delegation.threshold(),
parent.keys().iter().filter_map(
|(k, v)| if delegation
.key_ids()
.contains(k)
{
Some(v)
} else {
None
},
),
)?;
}
let delegation: TargetsMetadata = D::deserialize(signed.signed())?;
if delegation.version() != delegation_description.version() {
return Err(Error::VerificationFailure(format!(
"The snapshot metadata reported that the delegation {:?} should be at \
version {} but version {} was found instead.",
role,
delegation_description.version(),
delegation.version(),
)));
}
if delegation.expires() <= &Utc::now() {
// TODO this needs to be chagned to accept a MetadataPath and not Role
return Err(Error::ExpiredMetadata(Role::Targets));
}
delegation
};
let _ = self.delegations.insert(role.clone(), delegation);
Ok(true)
}
/// Get a reference to the description needed to verify the target defined by the given
/// `VirtualTargetPath`. Returns an `Error` if the target is not defined in the trusted
/// metadata. This may mean the target exists somewhere in the metadata, but the chain of trust
/// to that target may be invalid or incomplete.
pub fn target_description(&self, target_path: &VirtualTargetPath) -> Result<TargetDescription> {
let _ = self.safe_root_ref()?;
let _ = self.safe_snapshot_ref()?;
let targets = self.safe_targets_ref()?;
match targets.targets().get(target_path) {
Some(d) => return Ok(d.clone()),
None => (),
}
fn lookup<D: DataInterchange>(
tuf: &Tuf<D>,
default_terminate: bool,
current_depth: u32,
target_path: &VirtualTargetPath,
delegations: &Delegations,
parents: &[HashSet<VirtualTargetPath>],
visited: &mut HashSet<MetadataPath>,
) -> (bool, Option<TargetDescription>) {
for delegation in delegations.roles() {
if visited.contains(delegation.role()) {
return (delegation.terminating(), None);
}
let _ = visited.insert(delegation.role().clone());
let mut new_parents = parents.to_owned();
new_parents.push(delegation.paths().clone());
if current_depth > 0 && !target_path.matches_chain(&parents) {
return (delegation.terminating(), None);
}
let targets = match tuf.delegations.get(delegation.role()) {
Some(t) => t,
None => return (delegation.terminating(), None),
};
if targets.expires() <= &Utc::now() {
return (delegation.terminating(), None);
}
if let Some(d) = targets.targets().get(target_path) {
return (delegation.terminating(), Some(d.clone()));
}
if let Some(d) = targets.delegations() {
let mut new_parents = parents.to_vec();
new_parents.push(delegation.paths().clone());
let (term, res) = lookup(
tuf,
delegation.terminating(),
current_depth + 1,
target_path,
d,
&new_parents,
visited,
);
if term {
return (true, res);
} else if res.is_some() {
return (term, res);
}
}
}
(default_terminate, None)
}
match targets.delegations() {
Some(d) => {
let mut visited = HashSet::new();
lookup(self, false, 0, target_path, d, &[], &mut visited)
.1
.ok_or_else(|| Error::TargetUnavailable)
}
None => Err(Error::TargetUnavailable),
}
}
fn purge_metadata(&mut self) {
self.snapshot = None;
self.targets = None;
self.timestamp = None;
self.delegations.clear();
}
fn safe_root_ref(&self) -> Result<&RootMetadata> {
if self.root.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Root));
}
Ok(&self.root)
}
fn safe_snapshot_ref(&self) -> Result<&SnapshotMetadata> {
match &self.snapshot {
&Some(ref snapshot) => {
if snapshot.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Snapshot));
}
Ok(snapshot)
}
&None => Err(Error::MissingMetadata(Role::Snapshot)),
}
}
fn safe_targets_ref(&self) -> Result<&TargetsMetadata> {
match &self.targets {
&Some(ref targets) => {
if targets.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Targets));
}
Ok(targets)
}
&None => Err(Error::MissingMetadata(Role::Targets)),
}
}
fn safe_timestamp_ref(&self) -> Result<&TimestampMetadata> {
match &self.timestamp {
&Some(ref timestamp) => {
if timestamp.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Timestamp));
}
Ok(timestamp)
}
&None => Err(Error::MissingMetadata(Role::Timestamp)),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use chrono::prelude::*;
use crypto::{PrivateKey, SignatureScheme, HashAlgorithm};
use interchange::Json;
use metadata::{RoleDefinition, MetadataDescription};
lazy_static! {
static ref KEYS: Vec<PrivateKey> = {
let keys: &[&[u8]] = &[
include_bytes!("../tests/ed25519/ed25519-1.pk8.der"),
include_bytes!("../tests/ed25519/ed25519-2.pk8.der"),
include_bytes!("../tests/ed25519/ed25519-3.pk8.der"),
include_bytes!("../tests/ed25519/ed25519-4.pk8.der"),
include_bytes!("../tests/ed25519/ed25519-5.pk8.der"),
include_bytes!("../tests/ed25519/ed25519-6.pk8.der"),
];
keys.iter().map(|b| PrivateKey::from_pkcs8(b, SignatureScheme::Ed25519).unwrap())
.collect()
};
}
#[test]
fn root_pinned_success() {
let root_key = &KEYS[0];
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![KEYS[0].public().clone()],
RoleDefinition::new(1, hashset!(root_key.key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &root_key)
.unwrap();
assert!(Tuf::from_root_pinned(root, &[root_key.key_id().clone()]).is_ok());
}
#[test]
fn root_pinned_failure() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![KEYS[0].public().clone()],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
assert!(Tuf::from_root_pinned(root, &[KEYS[1].key_id().clone()]).is_err());
}
#[test]
fn good_root_rotation() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![KEYS[0].public().clone()],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let root = RootMetadata::new(
2,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![KEYS[1].public().clone()],
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
).unwrap();
let mut root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[1])
.unwrap();
// add the original key's signature to make it cross signed
root.add_signature(&KEYS[0]).unwrap();
assert_eq!(tuf.update_root(&root), Ok(true));
// second update should do nothing
assert_eq!(tuf.update_root(&root), Ok(false));
}
#[test]
fn no_cross_sign_root_rotation() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![KEYS[0].public().clone()],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let root = RootMetadata::new(
2,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
// include the old key to prevent short circuiting the verify logic
vec![KEYS[0].public().clone(), KEYS[1].public().clone()],
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[1])
.unwrap();
assert!(tuf.update_root(&root).is_err());
}
#[test]
fn good_timestamp_update() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![KEYS[0].public().clone(), KEYS[1].public().clone()],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let timestamp = TimestampMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
.unwrap(),
).unwrap();
let timestamp: SignedMetadata<Json, TimestampMetadata> =
SignedMetadata::new(&timestamp, &KEYS[1]).unwrap();
assert_eq!(tuf.update_timestamp(&timestamp), Ok(true));
// second update should do nothing
assert_eq!(tuf.update_timestamp(&timestamp), Ok(false))
}
#[test]
fn bad_timestamp_update_wrong_key() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![KEYS[0].public().clone(), KEYS[1].public().clone()],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let timestamp = TimestampMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
.unwrap(),
).unwrap();
// sign it with the root key
let timestamp: SignedMetadata<Json, TimestampMetadata> =
SignedMetadata::new(&timestamp, &KEYS[0]).unwrap();
assert!(tuf.update_timestamp(&timestamp).is_err())
}
#[test]
fn good_snapshot_update() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![
KEYS[0].public().clone(),
KEYS[1].public().clone(),
KEYS[2].public().clone(),
],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let timestamp = TimestampMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
.unwrap(),
).unwrap();
let timestamp: SignedMetadata<Json, TimestampMetadata> =
SignedMetadata::new(&timestamp, &KEYS[2]).unwrap();
tuf.update_timestamp(&timestamp).unwrap();
let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!())
.unwrap();
let snapshot: SignedMetadata<Json, SnapshotMetadata> =
SignedMetadata::new(&snapshot, &KEYS[1]).unwrap();
assert_eq!(tuf.update_snapshot(&snapshot), Ok(true));
// second update should do nothing
assert_eq!(tuf.update_snapshot(&snapshot), Ok(false));
}
#[test]
fn bad_snapshot_update_wrong_key() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![
KEYS[0].public().clone(),
KEYS[1].public().clone(),
KEYS[2].public().clone(),
],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let timestamp = TimestampMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
.unwrap(),
).unwrap();
let timestamp: SignedMetadata<Json, TimestampMetadata> =
SignedMetadata::new(&timestamp, &KEYS[2]).unwrap();
tuf.update_timestamp(&timestamp).unwrap();
let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!())
.unwrap();
let snapshot: SignedMetadata<Json, SnapshotMetadata> =
SignedMetadata::new(&snapshot, &KEYS[2]).unwrap();
assert!(tuf.update_snapshot(&snapshot).is_err());
}
#[test]
fn bad_snapshot_update_wrong_version() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![
KEYS[0].public().clone(),
KEYS[1].public().clone(),
KEYS[2].public().clone(),
],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let timestamp = TimestampMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
MetadataDescription::from_reader(&*vec![], 2, &[HashAlgorithm::Sha256])
.unwrap(),
).unwrap();
let timestamp: SignedMetadata<Json, TimestampMetadata> =
SignedMetadata::new(&timestamp, &KEYS[2]).unwrap();
tuf.update_timestamp(&timestamp).unwrap();
let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!())
.unwrap();
let snapshot: SignedMetadata<Json, SnapshotMetadata> =
SignedMetadata::new(&snapshot, &KEYS[1]).unwrap();
assert!(tuf.update_snapshot(&snapshot).is_err());
}
#[test]
fn good_targets_update() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![
KEYS[0].public().clone(),
KEYS[1].public().clone(),
KEYS[2].public().clone(),
KEYS[3].public().clone(),
],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[3].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let timestamp = TimestampMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
.unwrap(),
).unwrap();
let timestamp: SignedMetadata<Json, TimestampMetadata> =
SignedMetadata::new(&timestamp, &KEYS[3]).unwrap();
tuf.update_timestamp(&timestamp).unwrap();
let meta_map =
hashmap!(
MetadataPath::from_role(&Role::Targets) =>
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
);
let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)
.unwrap();
let snapshot: SignedMetadata<Json, SnapshotMetadata> =
SignedMetadata::new(&snapshot, &KEYS[1]).unwrap();
tuf.update_snapshot(&snapshot).unwrap();
let targets =
TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!(), None)
.unwrap();
let targets: SignedMetadata<Json, TargetsMetadata> =
SignedMetadata::new(&targets, &KEYS[2]).unwrap();
assert_eq!(tuf.update_targets(&targets), Ok(true));
// second update should do nothing
assert_eq!(tuf.update_targets(&targets), Ok(false));
}
#[test]
fn bad_targets_update_wrong_key() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![
KEYS[0].public().clone(),
KEYS[1].public().clone(),
KEYS[2].public().clone(),
KEYS[3].public().clone(),
],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[3].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let timestamp = TimestampMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
.unwrap(),
).unwrap();
let timestamp: SignedMetadata<Json, TimestampMetadata> =
SignedMetadata::new(&timestamp, &KEYS[3]).unwrap();
tuf.update_timestamp(&timestamp).unwrap();
let meta_map =
hashmap!(
MetadataPath::from_role(&Role::Targets) =>
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
);
let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)
.unwrap();
let snapshot: SignedMetadata<Json, SnapshotMetadata> =
SignedMetadata::new(&snapshot, &KEYS[1]).unwrap();
tuf.update_snapshot(&snapshot).unwrap();
let targets =
TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!(), None)
.unwrap();
let targets: SignedMetadata<Json, TargetsMetadata> =
SignedMetadata::new(&targets, &KEYS[3]).unwrap();
assert!(tuf.update_targets(&targets).is_err());
}
#[test]
fn bad_targets_update_wrong_version() {
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
false,
vec![
KEYS[0].public().clone(),
KEYS[1].public().clone(),
KEYS[2].public().clone(),
KEYS[3].public().clone(),
],
RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
RoleDefinition::new(1, hashset!(KEYS[3].key_id().clone())).unwrap(),
).unwrap();
let root: SignedMetadata<Json, RootMetadata> = SignedMetadata::new(&root, &KEYS[0])
.unwrap();
let mut tuf = Tuf::from_root(&root).unwrap();
let timestamp = TimestampMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
.unwrap(),
).unwrap();
let timestamp: SignedMetadata<Json, TimestampMetadata> =
SignedMetadata::new(&timestamp, &KEYS[3]).unwrap();
tuf.update_timestamp(&timestamp).unwrap();
let meta_map =
hashmap!(
MetadataPath::from_role(&Role::Targets) =>
MetadataDescription::from_reader(&*vec![], 2, &[HashAlgorithm::Sha256]).unwrap(),
);
let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)
.unwrap();
let snapshot: SignedMetadata<Json, SnapshotMetadata> =
SignedMetadata::new(&snapshot, &KEYS[1]).unwrap();
tuf.update_snapshot(&snapshot).unwrap();
let targets =
TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!(), None)
.unwrap();
let targets: SignedMetadata<Json, TargetsMetadata> =
SignedMetadata::new(&targets, &KEYS[2]).unwrap();
assert!(tuf.update_targets(&targets).is_err());
}
}