Merge pull request #159 from erickt/deser2
Simplify metadata construction with builders
diff --git a/src/client.rs b/src/client.rs
index 8d965ed..67c62cf 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -54,8 +54,8 @@
use error::Error;
use interchange::DataInterchange;
use metadata::{
- MetadataPath, MetadataVersion, Role, RootMetadata, SnapshotMetadata, TargetDescription,
- TargetPath, TargetsMetadata, VirtualTargetPath,
+ Metadata, MetadataPath, MetadataVersion, Role, RootMetadata, SnapshotMetadata,
+ TargetDescription, TargetPath, TargetsMetadata, VirtualTargetPath,
};
use repository::Repository;
use tuf::Tuf;
@@ -143,7 +143,7 @@
None,
)?;
- let tuf = Tuf::from_root(&root)?;
+ let tuf = Tuf::from_root(root)?;
Ok(Client {
tuf,
@@ -252,14 +252,14 @@
V: Repository<D>,
U: PathTranslator,
{
- let latest_root = repo.fetch_metadata(
+ let latest_root = repo.fetch_metadata::<RootMetadata>(
&MetadataPath::from_role(&Role::Root),
&MetadataVersion::None,
&config.max_root_size,
config.min_bytes_per_second,
None,
)?;
- let latest_version = D::deserialize::<RootMetadata>(latest_root.signed())?.version();
+ let latest_version = latest_root.as_ref().version();
if latest_version < tuf.root().version() {
return Err(Error::VerificationFailure(format!(
@@ -282,13 +282,13 @@
config.min_bytes_per_second,
None,
)?;
- if !tuf.update_root(&signed)? {
+ if !tuf.update_root(signed)? {
error!("{}", err_msg);
return Err(Error::Programming(err_msg.into()));
}
}
- if !tuf.update_root(&latest_root)? {
+ if !tuf.update_root(latest_root)? {
error!("{}", err_msg);
return Err(Error::Programming(err_msg.into()));
}
@@ -308,7 +308,7 @@
config.min_bytes_per_second,
None,
)?;
- tuf.update_timestamp(&ts)
+ tuf.update_timestamp(ts)
}
/// Returns `true` if an update occurred and `false` otherwise.
@@ -341,7 +341,7 @@
config.min_bytes_per_second,
Some((alg, value.clone())),
)?;
- tuf.update_snapshot(&snap)
+ tuf.update_snapshot(snap)
}
/// Returns `true` if an update occurred and `false` otherwise.
@@ -381,7 +381,7 @@
config.min_bytes_per_second,
Some((alg, value.clone())),
)?;
- tuf.update_targets(&targets)
+ tuf.update_targets(targets)
}
/// Fetch a target from the remote repo and write it to the local repo.
@@ -513,7 +513,7 @@
}
};
- match tuf.update_delegation(delegation.role(), &signed_meta) {
+ match tuf.update_delegation(delegation.role(), signed_meta.clone()) {
Ok(_) => {
match local.store_metadata(
delegation.role(),
@@ -536,7 +536,7 @@
current_depth + 1,
target,
snapshot,
- Some(&meta),
+ Some(meta.as_ref()),
local,
remote,
);
@@ -743,10 +743,9 @@
#[cfg(test)]
mod test {
use super::*;
- use chrono::prelude::*;
use crypto::{PrivateKey, SignatureScheme};
use interchange::Json;
- use metadata::{MetadataPath, MetadataVersion, RoleDefinition, RootMetadata, SignedMetadata};
+ use metadata::{MetadataPath, MetadataVersion, RootMetadataBuilder};
use repository::EphemeralRepository;
lazy_static! {
@@ -768,18 +767,14 @@
#[test]
fn root_chain_update() {
let repo = EphemeralRepository::new();
- 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 root = RootMetadataBuilder::new()
+ .version(1)
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[0].public().clone())
+ .targets_key(KEYS[0].public().clone())
+ .timestamp_key(KEYS[0].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
repo.store_metadata(
&MetadataPath::from_role(&Role::Root),
@@ -787,18 +782,14 @@
&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();
+ let mut root = RootMetadataBuilder::new()
+ .version(2)
+ .root_key(KEYS[1].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[1].public().clone())
+ .timestamp_key(KEYS[1].public().clone())
+ .signed::<Json>(&KEYS[1])
+ .unwrap();
root.add_signature(&KEYS[0]).unwrap();
@@ -808,18 +799,14 @@
&root,
).unwrap();
- let root = RootMetadata::new(
- 3,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- false,
- vec![KEYS[2].public().clone()],
- RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
- RoleDefinition::new(1, hashset!(KEYS[2].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 mut root: SignedMetadata<Json, RootMetadata> =
- SignedMetadata::new(&root, &KEYS[2]).unwrap();
+ let mut root = RootMetadataBuilder::new()
+ .version(3)
+ .root_key(KEYS[2].public().clone())
+ .snapshot_key(KEYS[2].public().clone())
+ .targets_key(KEYS[2].public().clone())
+ .timestamp_key(KEYS[2].public().clone())
+ .signed::<Json>(&KEYS[2])
+ .unwrap();
root.add_signature(&KEYS[1]).unwrap();
diff --git a/src/metadata.rs b/src/metadata.rs
index 4f9f581..dbc5770 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -1,13 +1,12 @@
//! TUF metadata.
use chrono::offset::Utc;
-use chrono::DateTime;
+use chrono::{DateTime, Duration};
use serde::de::{Deserialize, DeserializeOwned, Deserializer, Error as DeserializeError};
use serde::ser::{Error as SerializeError, Serialize, Serializer};
use std::collections::{HashMap, HashSet};
use std::fmt::{self, Debug, Display};
use std::io::Read;
-use std::iter::FromIterator;
use std::marker::PhantomData;
use crypto::{self, HashAlgorithm, HashValue, KeyId, PrivateKey, PublicKey, Signature};
@@ -181,16 +180,21 @@
_ => false,
}
}
+
+ /// Return the name of the role.
+ pub fn name(&self) -> &'static str {
+ match *self {
+ Role::Root => "root",
+ Role::Snapshot => "snapshot",
+ Role::Targets => "targets",
+ Role::Timestamp => "timestamp",
+ }
+ }
}
impl Display for Role {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Role::Root => write!(f, "root"),
- Role::Snapshot => write!(f, "snapshot"),
- Role::Targets => write!(f, "targets"),
- Role::Timestamp => write!(f, "timestamp"),
- }
+ f.write_str(self.name())
}
}
@@ -220,21 +224,22 @@
pub trait Metadata: Debug + PartialEq + Serialize + DeserializeOwned {
/// The role associated with the metadata.
const ROLE: Role;
+
+ /// The version number.
+ fn version(&self) -> u32;
+
+ /// An immutable reference to the metadata's expiration `DateTime`.
+ fn expires(&self) -> &DateTime<Utc>;
}
/// A piece of raw metadata with attached signatures.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct SignedMetadata<D, M>
-where
- D: DataInterchange,
- M: Metadata,
-{
+pub struct SignedMetadata<D, M> {
signatures: Vec<Signature>,
- signed: D::RawData,
+ #[serde(rename="signed")]
+ metadata: M,
#[serde(skip_serializing, skip_deserializing)]
_interchage: PhantomData<D>,
- #[serde(skip_serializing, skip_deserializing)]
- metadata: PhantomData<M>,
}
impl<D, M> SignedMetadata<D, M>
@@ -252,31 +257,24 @@
/// # use chrono::prelude::*;
/// # use tuf::crypto::{PrivateKey, SignatureScheme, HashAlgorithm};
/// # use tuf::interchange::Json;
- /// # use tuf::metadata::{MetadataDescription, TimestampMetadata, SignedMetadata};
+ /// # use tuf::metadata::{SignedMetadata, SnapshotMetadataBuilder};
/// #
/// # fn main() {
/// # let key: &[u8] = include_bytes!("../tests/ed25519/ed25519-1.pk8.der");
/// let key = PrivateKey::from_pkcs8(&key, SignatureScheme::Ed25519).unwrap();
///
- /// let timestamp = TimestampMetadata::new(
- /// 1,
- /// Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- /// MetadataDescription::from_reader(&*vec![0x01, 0x02, 0x03], 1,
- /// &[HashAlgorithm::Sha256]).unwrap()
- /// ).unwrap();
- ///
- /// SignedMetadata::<Json, TimestampMetadata>::new(×tamp, &key).unwrap();
+ /// let snapshot = SnapshotMetadataBuilder::new().build().unwrap();
+ /// SignedMetadata::<Json, _>::new(snapshot, &key).unwrap();
/// # }
/// ```
- pub fn new(metadata: &M, private_key: &PrivateKey) -> Result<SignedMetadata<D, M>> {
- let raw = D::serialize(metadata)?;
+ pub fn new(metadata: M, private_key: &PrivateKey) -> Result<SignedMetadata<D, M>> {
+ let raw = D::serialize(&metadata)?;
let bytes = D::canonicalize(&raw)?;
let sig = private_key.sign(&bytes)?;
Ok(SignedMetadata {
signatures: vec![sig],
- signed: raw,
+ metadata,
_interchage: PhantomData,
- metadata: PhantomData,
})
}
@@ -295,7 +293,7 @@
/// # use chrono::prelude::*;
/// # use tuf::crypto::{PrivateKey, SignatureScheme, HashAlgorithm};
/// # use tuf::interchange::Json;
- /// # use tuf::metadata::{MetadataDescription, TimestampMetadata, SignedMetadata};
+ /// # use tuf::metadata::{SignedMetadata, SnapshotMetadataBuilder};
/// #
/// # fn main() {
/// let key_1: &[u8] = include_bytes!("../tests/ed25519/ed25519-1.pk8.der");
@@ -306,24 +304,18 @@
/// let key_2: &[u8] = include_bytes!("../tests/ed25519/ed25519-2.pk8.der");
/// let key_2 = PrivateKey::from_pkcs8(&key_2, SignatureScheme::Ed25519).unwrap();
///
- /// let timestamp = TimestampMetadata::new(
- /// 1,
- /// Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- /// MetadataDescription::from_reader(&*vec![0x01, 0x02, 0x03], 1,
- /// &[HashAlgorithm::Sha256]).unwrap()
- /// ).unwrap();
- /// let mut timestamp = SignedMetadata::<Json, TimestampMetadata>::new(
- /// ×tamp, &key_1).unwrap();
+ /// let snapshot = SnapshotMetadataBuilder::new().build().unwrap();
+ /// let mut snapshot = SignedMetadata::<Json, _>::new(snapshot, &key_1).unwrap();
///
- /// timestamp.add_signature(&key_2).unwrap();
- /// assert_eq!(timestamp.signatures().len(), 2);
+ /// snapshot.add_signature(&key_2).unwrap();
+ /// assert_eq!(snapshot.signatures().len(), 2);
///
- /// timestamp.add_signature(&key_2).unwrap();
- /// assert_eq!(timestamp.signatures().len(), 2);
+ /// snapshot.add_signature(&key_2).unwrap();
+ /// assert_eq!(snapshot.signatures().len(), 2);
/// # }
/// ```
pub fn add_signature(&mut self, private_key: &PrivateKey) -> Result<()> {
- let raw = D::serialize(&self.signed)?;
+ let raw = D::serialize(&self.metadata)?;
let bytes = D::canonicalize(&raw)?;
let sig = private_key.sign(&bytes)?;
self.signatures
@@ -333,10 +325,10 @@
}
/// Merge the singatures from `other` into `self` if and only if
- /// `self.signed() == other.signed()`. If `self` and `other` contain signatures from the same
+ /// `self.as_ref() == other.as_ref()`. If `self` and `other` contain signatures from the same
/// key ID, then the signatures from `self` will replace the signatures from `other`.
pub fn merge_signatures(&mut self, other: &Self) -> Result<()> {
- if self.signed() != other.signed() {
+ if self.metadata != other.metadata {
return Err(Error::IllegalArgument(
"Attempted to merge unequal metadata".into(),
));
@@ -368,11 +360,6 @@
&mut self.signatures
}
- /// An immutable reference to the raw data.
- pub fn signed(&self) -> &D::RawData {
- &self.signed
- }
-
/// Verify this metadata.
///
/// ```
@@ -384,7 +371,7 @@
/// # use chrono::prelude::*;
/// # use tuf::crypto::{PrivateKey, SignatureScheme, HashAlgorithm};
/// # use tuf::interchange::Json;
- /// # use tuf::metadata::{MetadataDescription, TimestampMetadata, SignedMetadata};
+ /// # use tuf::metadata::{SnapshotMetadataBuilder, SignedMetadata};
///
/// # fn main() {
/// let key_1: &[u8] = include_bytes!("../tests/ed25519/ed25519-1.pk8.der");
@@ -393,34 +380,28 @@
/// let key_2: &[u8] = include_bytes!("../tests/ed25519/ed25519-2.pk8.der");
/// let key_2 = PrivateKey::from_pkcs8(&key_2, SignatureScheme::Ed25519).unwrap();
///
- /// let timestamp = TimestampMetadata::new(
- /// 1,
- /// Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- /// MetadataDescription::from_reader(&*vec![0x01, 0x02, 0x03], 1,
- /// &[HashAlgorithm::Sha256]).unwrap()
- /// ).unwrap();
- /// let timestamp = SignedMetadata::<Json, TimestampMetadata>::new(
- /// ×tamp, &key_1).unwrap();
+ /// let snapshot = SnapshotMetadataBuilder::new().build().unwrap();
+ /// let snapshot = SignedMetadata::<Json, _>::new(snapshot, &key_1).unwrap();
///
- /// assert!(timestamp.verify(
+ /// assert!(snapshot.verify(
/// 1,
/// vec![key_1.public()],
/// ).is_ok());
///
/// // fail with increased threshold
- /// assert!(timestamp.verify(
+ /// assert!(snapshot.verify(
/// 2,
/// vec![key_1.public()],
/// ).is_err());
///
/// // fail when the keys aren't authorized
- /// assert!(timestamp.verify(
+ /// assert!(snapshot.verify(
/// 1,
/// vec![key_2.public()],
/// ).is_err());
///
/// // fail when the keys don't exist
- /// assert!(timestamp.verify(
+ /// assert!(snapshot.verify(
/// 1,
/// &[],
/// ).is_err());
@@ -446,7 +427,7 @@
.map(|k| (k.key_id(), k))
.collect::<HashMap<&KeyId, &PublicKey>>();
- let canonical_bytes = D::canonicalize(&self.signed)?;
+ let canonical_bytes = D::canonicalize(&D::serialize(&self.metadata)?)?;
let mut signatures_needed = threshold;
for sig in &self.signatures {
@@ -484,6 +465,195 @@
}
}
+impl<D, M> AsRef<M> for SignedMetadata<D, M> {
+ fn as_ref(&self) -> &M {
+ &self.metadata
+ }
+}
+
+impl<D, M> Metadata for SignedMetadata<D, M>
+where
+ D: Debug + PartialEq,
+ M: Metadata,
+{
+ const ROLE: Role = M::ROLE;
+
+ fn version(&self) -> u32 {
+ self.metadata.version()
+ }
+
+ fn expires(&self) -> &DateTime<Utc> {
+ self.metadata.expires()
+ }
+}
+
+/// Helper to construct `RootMetadata`.
+pub struct RootMetadataBuilder {
+ version: u32,
+ expires: DateTime<Utc>,
+ consistent_snapshot: bool,
+ keys: HashMap<KeyId, PublicKey>,
+ root_threshold: u32,
+ root_key_ids: HashSet<KeyId>,
+ snapshot_threshold: u32,
+ snapshot_key_ids: HashSet<KeyId>,
+ targets_threshold: u32,
+ targets_key_ids: HashSet<KeyId>,
+ timestamp_threshold: u32,
+ timestamp_key_ids: HashSet<KeyId>,
+}
+
+impl RootMetadataBuilder {
+ /// Create a new `RootMetadataBuilder`. It defaults to:
+ ///
+ /// * version: 1,
+ /// * expires: 365 days from the current time.
+ /// * consistent snapshot: false
+ /// * role thresholds: 1
+ pub fn new() -> Self {
+ RootMetadataBuilder {
+ version: 1,
+ expires: Utc::now() + Duration::days(365),
+ consistent_snapshot: false,
+ keys: HashMap::new(),
+ root_threshold: 1,
+ root_key_ids: HashSet::new(),
+ snapshot_threshold: 1,
+ snapshot_key_ids: HashSet::new(),
+ targets_threshold: 1,
+ targets_key_ids: HashSet::new(),
+ timestamp_threshold: 1,
+ timestamp_key_ids: HashSet::new(),
+ }
+ }
+
+ /// Set the version number for this metadata.
+ pub fn version(mut self, version: u32) -> Self {
+ self.version = version;
+ self
+ }
+
+ /// Set the time this metadata expires.
+ pub fn expires(mut self, expires: DateTime<Utc>) -> Self {
+ self.expires = expires;
+ self
+ }
+
+ /// Set this metadata to have a consistent snapshot.
+ pub fn consistent_snapshot(mut self, consistent_snapshot: bool) -> Self {
+ self.consistent_snapshot = consistent_snapshot;
+ self
+ }
+
+ /// Set the root threshold.
+ pub fn root_threshold(mut self, threshold: u32) -> Self {
+ self.root_threshold = threshold;
+ self
+ }
+
+ /// Add a root public key.
+ pub fn root_key(mut self, public_key: PublicKey) -> Self {
+ let key_id = public_key.key_id().clone();
+ self.keys.insert(key_id.clone(), public_key);
+ self.root_key_ids.insert(key_id);
+ self
+ }
+
+ /// Set the snapshot threshold.
+ pub fn snapshot_threshold(mut self, threshold: u32) -> Self {
+ self.snapshot_threshold = threshold;
+ self
+ }
+
+ /// Add a snapshot public key.
+ pub fn snapshot_key(mut self, public_key: PublicKey) -> Self {
+ let key_id = public_key.key_id().clone();
+ self.keys.insert(key_id.clone(), public_key);
+ self.snapshot_key_ids.insert(key_id);
+ self
+ }
+
+ /// Set the targets threshold.
+ pub fn targets_threshold(mut self, threshold: u32) -> Self {
+ self.targets_threshold = threshold;
+ self
+ }
+
+ /// Add a targets public key.
+ pub fn targets_key(mut self, public_key: PublicKey) -> Self {
+ let key_id = public_key.key_id().clone();
+ self.keys.insert(key_id.clone(), public_key);
+ self.targets_key_ids.insert(key_id);
+ self
+ }
+
+ /// Set the timestamp threshold.
+ pub fn timestamp_threshold(mut self, threshold: u32) -> Self {
+ self.timestamp_threshold = threshold;
+ self
+ }
+
+ /// Add a timestamp public key.
+ pub fn timestamp_key(mut self, public_key: PublicKey) -> Self {
+ let key_id = public_key.key_id().clone();
+ self.keys.insert(key_id.clone(), public_key);
+ self.timestamp_key_ids.insert(key_id);
+ self
+ }
+
+ /// Construct a new `RootMetadata`.
+ pub fn build(self) -> Result<RootMetadata> {
+ RootMetadata::new(
+ self.version,
+ self.expires,
+ self.consistent_snapshot,
+ self.keys,
+ RoleDefinition::new(
+ self.root_threshold,
+ self.root_key_ids,
+ )?,
+ RoleDefinition::new(
+ self.snapshot_threshold,
+ self.snapshot_key_ids,
+ )?,
+ RoleDefinition::new(
+ self.targets_threshold,
+ self.targets_key_ids,
+ )?,
+ RoleDefinition::new(
+ self.timestamp_threshold,
+ self.timestamp_key_ids,
+ )?,
+ )
+ }
+
+ /// Construct a new `SignedMetadata<D, RootMetadata>`.
+ pub fn signed<D>(self, private_key: &PrivateKey) -> Result<SignedMetadata<D, RootMetadata>>
+ where D: DataInterchange,
+ {
+ Ok(SignedMetadata::new(self.build()?, private_key)?)
+ }
+}
+
+impl From<RootMetadata> for RootMetadataBuilder {
+ fn from(metadata: RootMetadata) -> Self {
+ RootMetadataBuilder {
+ version: metadata.version,
+ expires: metadata.expires,
+ consistent_snapshot: metadata.consistent_snapshot,
+ keys: metadata.keys,
+ root_threshold: metadata.root.threshold,
+ root_key_ids: metadata.root.key_ids,
+ snapshot_threshold: metadata.snapshot.threshold,
+ snapshot_key_ids: metadata.snapshot.key_ids,
+ targets_threshold: metadata.targets.threshold,
+ targets_key_ids: metadata.targets.key_ids,
+ timestamp_threshold: metadata.timestamp.threshold,
+ timestamp_key_ids: metadata.timestamp.key_ids,
+ }
+ }
+}
+
/// Metadata for the root role.
#[derive(Debug, Clone, PartialEq)]
pub struct RootMetadata {
@@ -503,7 +673,7 @@
version: u32,
expires: DateTime<Utc>,
consistent_snapshot: bool,
- mut keys: Vec<PublicKey>,
+ keys: HashMap<KeyId, PublicKey>,
root: RoleDefinition,
snapshot: RoleDefinition,
targets: RoleDefinition,
@@ -516,13 +686,6 @@
)));
}
- let keys_len = keys.len();
- let keys = HashMap::from_iter(keys.drain(..).map(|k| (k.key_id().clone(), k)));
-
- if keys.len() != keys_len {
- return Err(Error::IllegalArgument("Cannot have duplicate keys".into()));
- }
-
Ok(RootMetadata {
version,
expires,
@@ -535,16 +698,6 @@
})
}
- /// The version number.
- pub fn version(&self) -> u32 {
- self.version
- }
-
- /// An immutable reference to the metadata's expiration `DateTime`.
- pub fn expires(&self) -> &DateTime<Utc> {
- &self.expires
- }
-
/// Whether or not this repository is currently implementing that TUF consistent snapshot
/// feature.
pub fn consistent_snapshot(&self) -> bool {
@@ -579,6 +732,14 @@
impl Metadata for RootMetadata {
const ROLE: Role = Role::Root;
+
+ fn version(&self) -> u32 {
+ self.version
+ }
+
+ fn expires(&self) -> &DateTime<Utc> {
+ &self.expires
+ }
}
impl Serialize for RootMetadata {
@@ -757,6 +918,78 @@
}
}
+/// Helper to construct `TimestampMetadata`.
+pub struct TimestampMetadataBuilder {
+ version: u32,
+ expires: DateTime<Utc>,
+ snapshot: MetadataDescription,
+}
+
+impl TimestampMetadataBuilder {
+ /// Create a new `TimestampMetadataBuilder` from a given snapshot. It defaults to:
+ ///
+ /// * version: 1
+ /// * expires: 1 day from the current time.
+ pub fn from_snapshot<D, M>(
+ snapshot: &SignedMetadata<D, M>,
+ hash_algs: &[HashAlgorithm],
+ ) -> Result<Self>
+ where
+ D: DataInterchange,
+ M: Metadata,
+ {
+ let bytes = D::canonicalize(&D::serialize(&snapshot)?)?;
+ let description = MetadataDescription::from_reader(
+ &*bytes,
+ snapshot.version(),
+ hash_algs,
+ )?;
+
+ Ok(Self::from_metadata_description(description))
+ }
+
+ /// Create a new `TimestampMetadataBuilder` from a given
+ /// `MetadataDescription`. It defaults to:
+ ///
+ /// * version: 1
+ /// * expires: 1 day from the current time.
+ pub fn from_metadata_description(description: MetadataDescription) -> Self {
+ TimestampMetadataBuilder {
+ version: 1,
+ expires: Utc::now() + Duration::days(1),
+ snapshot: description,
+ }
+ }
+
+ /// Set the version number for this metadata.
+ pub fn version(mut self, version: u32) -> Self {
+ self.version = version;
+ self
+ }
+
+ /// Set the time this metadata expires.
+ pub fn expires(mut self, expires: DateTime<Utc>) -> Self {
+ self.expires = expires;
+ self
+ }
+
+ /// Construct a new `TimestampMetadata`.
+ pub fn build(self) -> Result<TimestampMetadata> {
+ TimestampMetadata::new(
+ self.version,
+ self.expires,
+ self.snapshot,
+ )
+ }
+
+ /// Construct a new `SignedMetadata<D, TimestampMetadata>`.
+ pub fn signed<D>(self, private_key: &PrivateKey) -> Result<SignedMetadata<D, TimestampMetadata>>
+ where D: DataInterchange,
+ {
+ Ok(SignedMetadata::new(self.build()?, private_key)?)
+ }
+}
+
/// Metadata for the timestamp role.
#[derive(Debug, Clone, PartialEq)]
pub struct TimestampMetadata {
@@ -786,16 +1019,6 @@
})
}
- /// The version number.
- pub fn version(&self) -> u32 {
- self.version
- }
-
- /// An immutable reference to the metadata's expiration `DateTime`.
- pub fn expires(&self) -> &DateTime<Utc> {
- &self.expires
- }
-
/// An immutable reference to the snapshot description.
pub fn snapshot(&self) -> &MetadataDescription {
&self.snapshot
@@ -804,6 +1027,14 @@
impl Metadata for TimestampMetadata {
const ROLE: Role = Role::Timestamp;
+
+ fn version(&self) -> u32 {
+ self.version
+ }
+
+ fn expires(&self) -> &DateTime<Utc> {
+ &self.expires
+ }
}
impl Serialize for TimestampMetadata {
@@ -913,6 +1144,114 @@
}
}
+/// Helper to construct `SnapshotMetadata`.
+pub struct SnapshotMetadataBuilder {
+ version: u32,
+ expires: DateTime<Utc>,
+ meta: HashMap<MetadataPath, MetadataDescription>,
+}
+
+impl SnapshotMetadataBuilder {
+ /// Create a new `SnapshotMetadataBuilder`. It defaults to:
+ ///
+ /// * version: 1
+ /// * expires: 7 days from the current time.
+ pub fn new() -> Self {
+ SnapshotMetadataBuilder {
+ version: 1,
+ expires: Utc::now() + Duration::days(7),
+ meta: HashMap::new(),
+ }
+ }
+
+ /// Set the version number for this metadata.
+ pub fn version(mut self, version: u32) -> Self {
+ self.version = version;
+ self
+ }
+
+ /// Set the time this metadata expires.
+ pub fn expires(mut self, expires: DateTime<Utc>) -> Self {
+ self.expires = expires;
+ self
+ }
+
+ /// Add metadata to this snapshot metadata using the default path.
+ pub fn insert_metadata<D, M>(
+ self,
+ metadata: &SignedMetadata<D, M>,
+ hash_algs: &[HashAlgorithm],
+ ) -> Result<Self>
+ where
+ M: Metadata,
+ D: DataInterchange,
+ {
+ self.insert_metadata_with_path(
+ M::ROLE.name(),
+ metadata,
+ hash_algs,
+ )
+ }
+
+ /// Add metadata to this snapshot metadata using a custom path.
+ pub fn insert_metadata_with_path<P, D, M>(
+ self,
+ path: P,
+ metadata: &SignedMetadata<D, M>,
+ hash_algs: &[HashAlgorithm],
+ ) -> Result<Self>
+ where
+ P: Into<String>,
+ M: Metadata,
+ D: DataInterchange,
+ {
+ let bytes = D::canonicalize(&D::serialize(metadata)?)?;
+ let description = MetadataDescription::from_reader(
+ &*bytes,
+ metadata.version(),
+ hash_algs,
+ )?;
+ let path = MetadataPath::new(path.into())?;
+ Ok(self.insert_metadata_description(path, description))
+ }
+
+ /// Add `MetadataDescription` to this snapshot metadata using a custom path.
+ pub fn insert_metadata_description(
+ mut self,
+ path: MetadataPath,
+ description: MetadataDescription,
+ ) -> Self {
+ self.meta.insert(path.into(), description);
+ self
+ }
+
+ /// Construct a new `SnapshotMetadata`.
+ pub fn build(self) -> Result<SnapshotMetadata> {
+ SnapshotMetadata::new(
+ self.version,
+ self.expires,
+ self.meta,
+ )
+ }
+
+ /// Construct a new `SignedMetadata<D, SnapshotMetadata>`.
+ pub fn signed<D>(self, private_key: &PrivateKey) -> Result<SignedMetadata<D, SnapshotMetadata>>
+ where D: DataInterchange,
+ {
+ Ok(SignedMetadata::new(self.build()?, private_key)?)
+ }
+}
+
+impl From<SnapshotMetadata> for SnapshotMetadataBuilder {
+ fn from(meta: SnapshotMetadata) -> Self {
+ SnapshotMetadataBuilder {
+ version: meta.version,
+ expires: meta.expires,
+ meta: meta.meta,
+ }
+ }
+}
+
/// Metadata for the snapshot role.
#[derive(Debug, Clone, PartialEq)]
pub struct SnapshotMetadata {
@@ -942,16 +1281,6 @@
})
}
- /// The version number.
- pub fn version(&self) -> u32 {
- self.version
- }
-
- /// An immutable reference to the metadata's expiration `DateTime`.
- pub fn expires(&self) -> &DateTime<Utc> {
- &self.expires
- }
-
/// An immutable reference to the metadata paths and descriptions.
pub fn meta(&self) -> &HashMap<MetadataPath, MetadataDescription> {
&self.meta
@@ -960,6 +1289,14 @@
impl Metadata for SnapshotMetadata {
const ROLE: Role = Role::Snapshot;
+
+ fn version(&self) -> u32 {
+ self.version
+ }
+
+ fn expires(&self) -> &DateTime<Utc> {
+ &self.expires
+ }
}
impl Serialize for SnapshotMetadata {
@@ -1233,16 +1570,6 @@
})
}
- /// The version number.
- pub fn version(&self) -> u32 {
- self.version
- }
-
- /// An immutable reference to the metadata's expiration `DateTime`.
- pub fn expires(&self) -> &DateTime<Utc> {
- &self.expires
- }
-
/// An immutable reference to the descriptions of targets.
pub fn targets(&self) -> &HashMap<VirtualTargetPath, TargetDescription> {
&self.targets
@@ -1256,6 +1583,14 @@
impl Metadata for TargetsMetadata {
const ROLE: Role = Role::Targets;
+
+ fn version(&self) -> u32 {
+ self.version
+ }
+
+ fn expires(&self) -> &DateTime<Utc> {
+ &self.expires
+ }
}
impl Serialize for TargetsMetadata {
@@ -1278,6 +1613,88 @@
}
}
+/// Helper to construct `TargetsMetadata`.
+pub struct TargetsMetadataBuilder {
+ version: u32,
+ expires: DateTime<Utc>,
+ targets: HashMap<VirtualTargetPath, TargetDescription>,
+ delegations: Option<Delegations>,
+}
+
+impl TargetsMetadataBuilder {
+ /// Create a new `TargetsMetadata`. It defaults to:
+ ///
+ /// * version: 1
+ /// * expires: 90 days from the current time.
+ pub fn new() -> Self {
+ TargetsMetadataBuilder {
+ version: 1,
+ expires: Utc::now() + Duration::days(90),
+ targets: HashMap::new(),
+ delegations: None,
+ }
+ }
+
+ /// Set the version number for this metadata.
+ pub fn version(mut self, version: u32) -> Self {
+ self.version = version;
+ self
+ }
+
+ /// Set the time this metadata expires.
+ pub fn expires(mut self, expires: DateTime<Utc>) -> Self {
+ self.expires = expires;
+ self
+ }
+
+ /// Add target to the target metadata.
+ pub fn insert_target_from_reader<R>(
+ self,
+ path: VirtualTargetPath,
+ read: R,
+ hash_algs: &[HashAlgorithm],
+ ) -> Result<Self>
+ where
+ R: Read,
+ {
+ let description = TargetDescription::from_reader(read, hash_algs)?;
+ Ok(self.insert_target_description(path, description))
+ }
+
+ /// Add `TargetDescription` to this target metadata target description.
+ pub fn insert_target_description(
+ mut self,
+ path: VirtualTargetPath,
+ description: TargetDescription,
+ ) -> Self {
+ self.targets.insert(path, description);
+ self
+ }
+
+ /// Add `Delegatiuons` to this target metadata.
+ pub fn delegations(mut self, delegations: Delegations) -> Self {
+ self.delegations = Some(delegations);
+ self
+ }
+
+ /// Construct a new `TargetsMetadata`.
+ pub fn build(self) -> Result<TargetsMetadata> {
+ TargetsMetadata::new(
+ self.version,
+ self.expires,
+ self.targets,
+ self.delegations,
+ )
+ }
+
+ /// Construct a new `SignedMetadata<D, TargetsMetadata>`.
+ pub fn signed<D>(self, private_key: &PrivateKey) -> Result<SignedMetadata<D, TargetsMetadata>>
+ where D: DataInterchange,
+ {
+ Ok(SignedMetadata::new(self.build()?, private_key)?)
+ }
+}
+
/// Wrapper to described a collections of delegations.
#[derive(Debug, PartialEq, Clone)]
pub struct Delegations {
@@ -1597,29 +2014,14 @@
let timestamp_key =
PrivateKey::from_pkcs8(ED25519_4_PK8, SignatureScheme::Ed25519).unwrap();
- let keys = vec![
- root_key.public().clone(),
- snapshot_key.public().clone(),
- targets_key.public().clone(),
- timestamp_key.public().clone(),
- ];
-
- let root_def = RoleDefinition::new(1, hashset!(root_key.key_id().clone())).unwrap();
- let snapshot_def = RoleDefinition::new(1, hashset!(snapshot_key.key_id().clone())).unwrap();
- let targets_def = RoleDefinition::new(1, hashset!(targets_key.key_id().clone())).unwrap();
- let timestamp_def =
- RoleDefinition::new(1, hashset!(timestamp_key.key_id().clone())).unwrap();
-
- let root = RootMetadata::new(
- 1,
- Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- false,
- keys,
- root_def,
- snapshot_def,
- targets_def,
- timestamp_def,
- ).unwrap();
+ let root = RootMetadataBuilder::new()
+ .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+ .root_key(root_key.public().clone())
+ .snapshot_key(snapshot_key.public().clone())
+ .targets_key(targets_key.public().clone())
+ .timestamp_key(timestamp_key.public().clone())
+ .build()
+ .unwrap();
let jsn = json!({
"type": "root",
@@ -1678,16 +2080,17 @@
#[test]
fn serde_timestamp_metadata() {
- let timestamp = TimestampMetadata::new(
+ let description = MetadataDescription::new(
1,
- Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- MetadataDescription::new(
- 1,
- 100,
- hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
- ).unwrap(),
+ 100,
+ hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_metadata_description(description)
+ .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+ .build()
+ .unwrap();
+
let jsn = json!({
"type": "timestamp",
"version": 1,
@@ -1709,18 +2112,18 @@
#[test]
fn serde_snapshot_metadata() {
- let snapshot = SnapshotMetadata::new(
- 1,
- Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- hashmap! {
- MetadataPath::new("foo".into()).unwrap() =>
- MetadataDescription::new(
- 1,
- 100,
- hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
- ).unwrap(),
- },
- ).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+ .insert_metadata_description(
+ MetadataPath::new("foo".into()).unwrap(),
+ MetadataDescription::new(
+ 1,
+ 100,
+ hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
+ ).unwrap(),
+ )
+ .build()
+ .unwrap();
let jsn = json!({
"type": "snapshot",
@@ -1745,18 +2148,17 @@
#[test]
fn serde_targets_metadata() {
- let targets = TargetsMetadata::new(
- 1,
- Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- hashmap! {
- VirtualTargetPath::new("foo".into()).unwrap() =>
- TargetDescription::from_reader(
- b"foo" as &[u8],
- &[HashAlgorithm::Sha256],
- ).unwrap(),
- },
- None,
- ).unwrap();
+ let targets = TargetsMetadataBuilder::new()
+ .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+ .insert_target_description(
+ VirtualTargetPath::new("foo".into()).unwrap(),
+ TargetDescription::from_reader(
+ &b"foo"[..],
+ &[HashAlgorithm::Sha256],
+ ).unwrap(),
+ )
+ .build()
+ .unwrap();
let jsn = json!({
"type": "targets",
@@ -1794,12 +2196,11 @@
],
).unwrap();
- let targets = TargetsMetadata::new(
- 1,
- Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- HashMap::new(),
- Some(delegations),
- ).unwrap();
+ let targets = TargetsMetadataBuilder::new()
+ .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+ .delegations(delegations)
+ .build()
+ .unwrap();
let jsn = json!({
"type": "targets",
@@ -1835,22 +2236,22 @@
#[test]
fn serde_signed_metadata() {
- let snapshot = SnapshotMetadata::new(
- 1,
- Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
- hashmap! {
- MetadataPath::new("foo".into()).unwrap() =>
- MetadataDescription::new(
- 1,
- 100,
- hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
- ).unwrap(),
- },
- ).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+ .insert_metadata_description(
+ MetadataPath::new("foo".into()).unwrap(),
+ MetadataDescription::new(
+ 1,
+ 100,
+ hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
+ ).unwrap(),
+ )
+ .build()
+ .unwrap();
let key = PrivateKey::from_pkcs8(ED25519_1_PK8, SignatureScheme::Ed25519).unwrap();
- let signed = SignedMetadata::<Json, SnapshotMetadata>::new(&snapshot, &key).unwrap();
+ let signed = SignedMetadata::<Json, _>::new(snapshot, &key).unwrap();
let jsn = json!({
"signatures": [
@@ -1877,7 +2278,7 @@
});
let encoded = json::to_value(&signed).unwrap();
- assert_eq!(encoded, jsn);
+ assert_eq!(encoded, jsn, "{:#?} != {:#?}", encoded, jsn);
let decoded: SignedMetadata<Json, SnapshotMetadata> = json::from_value(encoded).unwrap();
assert_eq!(decoded, signed);
}
@@ -1906,91 +2307,50 @@
// TODO test for mismatched ed25519/rsa keys/schemes
fn make_root() -> json::Value {
- let root_def = RoleDefinition::new(
- 1,
- hashset!(
- PrivateKey::from_pkcs8(ED25519_1_PK8, SignatureScheme::Ed25519)
- .unwrap()
- .key_id()
- .clone()
- ),
- ).unwrap();
+ let root_key = PrivateKey::from_pkcs8(ED25519_1_PK8, SignatureScheme::Ed25519)
+ .unwrap();
+ let snapshot_key = PrivateKey::from_pkcs8(ED25519_2_PK8, SignatureScheme::Ed25519)
+ .unwrap();
+ let targets_key = PrivateKey::from_pkcs8(ED25519_3_PK8, SignatureScheme::Ed25519)
+ .unwrap();
+ let timestamp_key = PrivateKey::from_pkcs8(ED25519_4_PK8, SignatureScheme::Ed25519)
+ .unwrap();
- let snapshot_def = RoleDefinition::new(
- 1,
- hashset!(
- PrivateKey::from_pkcs8(ED25519_2_PK8, SignatureScheme::Ed25519)
- .unwrap()
- .key_id()
- .clone()
- ),
- ).unwrap();
-
- let targets_def = RoleDefinition::new(
- 1,
- hashset!(
- PrivateKey::from_pkcs8(ED25519_3_PK8, SignatureScheme::Ed25519)
- .unwrap()
- .key_id()
- .clone()
- ),
- ).unwrap();
-
- let timestamp_def = RoleDefinition::new(
- 1,
- hashset!(
- PrivateKey::from_pkcs8(ED25519_4_PK8, SignatureScheme::Ed25519)
- .unwrap()
- .key_id()
- .clone()
- ),
- ).unwrap();
-
- let root = RootMetadata::new(
- 1,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- false,
- vec![
- PrivateKey::from_pkcs8(ED25519_1_PK8, SignatureScheme::Ed25519)
- .unwrap()
- .public()
- .clone(),
- PrivateKey::from_pkcs8(ED25519_2_PK8, SignatureScheme::Ed25519)
- .unwrap()
- .public()
- .clone(),
- PrivateKey::from_pkcs8(ED25519_3_PK8, SignatureScheme::Ed25519)
- .unwrap()
- .public()
- .clone(),
- PrivateKey::from_pkcs8(ED25519_4_PK8, SignatureScheme::Ed25519)
- .unwrap()
- .public()
- .clone(),
- ],
- root_def,
- snapshot_def,
- targets_def,
- timestamp_def,
- ).unwrap();
+ let root = RootMetadataBuilder::new()
+ .expires(Utc.ymd(2038, 1, 1).and_hms(0, 0, 0))
+ .root_key(
+ root_key.public().clone()
+ )
+ .snapshot_key(snapshot_key.public().clone())
+ .targets_key(targets_key.public().clone())
+ .timestamp_key(timestamp_key.public().clone())
+ .build()
+ .unwrap();
json::to_value(&root).unwrap()
}
fn make_snapshot() -> json::Value {
- let snapshot =
- SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!()).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .expires(Utc.ymd(2038, 1, 1).and_hms(0, 0, 0))
+ .build()
+ .unwrap();
json::to_value(&snapshot).unwrap()
}
fn make_timestamp() -> json::Value {
- let timestamp = TimestampMetadata::new(
+ let description = MetadataDescription::from_reader(
+ &[][..],
1,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
+ &[HashAlgorithm::Sha256],
).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_metadata_description(description)
+ .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+ .build()
+ .unwrap();
+
json::to_value(×tamp).unwrap()
}
diff --git a/src/shims.rs b/src/shims.rs
index 855c7b2..890c0b8 100644
--- a/src/shims.rs
+++ b/src/shims.rs
@@ -2,10 +2,11 @@
use chrono::prelude::*;
use data_encoding::BASE64URL;
use std::collections::{HashMap, HashSet};
+use std::iter::FromIterator;
use crypto;
use error::Error;
-use metadata;
+use metadata::{self, Metadata};
use Result;
fn parse_datetime(ts: &str) -> Result<DateTime<Utc>> {
@@ -60,7 +61,7 @@
})
}
- pub fn try_into(self) -> Result<metadata::RootMetadata> {
+ pub fn try_into(mut self) -> Result<metadata::RootMetadata> {
if self.typ != metadata::Role::Root {
return Err(Error::Encoding(format!(
"Attempted to decode root metdata labeled as {:?}",
@@ -68,11 +69,18 @@
)));
}
+ let keys_len = self.keys.len();
+ let keys = HashMap::from_iter(self.keys.drain(..).map(|k| (k.key_id().clone(), k)));
+
+ if keys.len() != keys_len {
+ return Err(Error::IllegalArgument("Cannot have duplicate keys".into()));
+ }
+
metadata::RootMetadata::new(
self.version,
parse_datetime(&self.expires)?,
self.consistent_snapshot,
- self.keys,
+ keys,
self.root,
self.snapshot,
self.targets,
diff --git a/src/tuf.rs b/src/tuf.rs
index 9e06704..a5c60d6 100644
--- a/src/tuf.rs
+++ b/src/tuf.rs
@@ -8,7 +8,7 @@
use error::Error;
use interchange::DataInterchange;
use metadata::{
- Delegations, MetadataPath, Role, RootMetadata, SignedMetadata, SnapshotMetadata,
+ Delegations, Metadata, MetadataPath, Role, RootMetadata, SignedMetadata, SnapshotMetadata,
TargetDescription, TargetsMetadata, TimestampMetadata, VirtualTargetPath,
};
use Result;
@@ -16,11 +16,11 @@
/// 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>,
+ root: SignedMetadata<D, RootMetadata>,
+ snapshot: Option<SignedMetadata<D, SnapshotMetadata>>,
+ targets: Option<SignedMetadata<D, TargetsMetadata>>,
+ timestamp: Option<SignedMetadata<D, TimestampMetadata>>,
+ delegations: HashMap<MetadataPath, SignedMetadata<D, TargetsMetadata>>,
interchange: PhantomData<D>,
}
@@ -39,27 +39,29 @@
signed_root
.signatures_mut()
.retain(|s| root_key_ids.contains(s.key_id()));
- Self::from_root(&signed_root)
+ 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())?;
- signed_root.verify(
- root.root().threshold(),
- root.keys().iter().filter_map(|(k, v)| {
- if root.root().key_ids().contains(k) {
- Some(v)
- } else {
- None
- }
- }),
- )?;
+ pub fn from_root(signed_root: SignedMetadata<D, RootMetadata>) -> Result<Self> {
+ {
+ let root = signed_root.as_ref();
+ 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,
+ root: signed_root,
snapshot: None,
targets: None,
timestamp: None,
@@ -70,132 +72,182 @@
/// An immutable reference to the root metadata.
pub fn root(&self) -> &RootMetadata {
- &self.root
+ self.root.as_ref()
}
/// An immutable reference to the optional snapshot metadata.
pub fn snapshot(&self) -> Option<&SnapshotMetadata> {
- self.snapshot.as_ref()
+ self.snapshot.as_ref().map(|t| t.as_ref())
}
/// An immutable reference to the optional targets metadata.
pub fn targets(&self) -> Option<&TargetsMetadata> {
- self.targets.as_ref()
+ self.targets.as_ref().map(|t| t.as_ref())
}
/// An immutable reference to the optional timestamp metadata.
pub fn timestamp(&self) -> Option<&TimestampMetadata> {
- self.timestamp.as_ref()
+ self.timestamp.as_ref().map(|t| t.as_ref())
}
/// An immutable reference to the delegated metadata.
- pub fn delegations(&self) -> &HashMap<MetadataPath, TargetsMetadata> {
+ pub fn delegations(&self) -> &HashMap<MetadataPath, SignedMetadata<D, TargetsMetadata>> {
&self.delegations
}
+ /// An immutable reference to the root metadata.
+ pub fn signed_root(&self) -> &SignedMetadata<D, RootMetadata> {
+ &self.root
+ }
+
+ /// An immutable reference to the optional snapshot metadata.
+ pub fn signed_snapshot(&self) -> Option<&SignedMetadata<D, SnapshotMetadata>> {
+ self.snapshot.as_ref()
+ }
+
+ /// An immutable reference to the optional targets metadata.
+ pub fn signed_targets(&self) -> Option<&SignedMetadata<D, TargetsMetadata>> {
+ self.targets.as_ref()
+ }
+
+ /// An immutable reference to the optional timestamp metadata.
+ pub fn signed_timestamp(&self) -> Option<&SignedMetadata<D, TimestampMetadata>> {
+ self.timestamp.as_ref()
+ }
+
+ fn current_timestamp_version(&self) -> u32 {
+ self.timestamp.as_ref()
+ .map(|t| t.as_ref().version())
+ .unwrap_or(0)
+ }
+
+ fn current_snapshot_version(&self) -> u32 {
+ self.snapshot.as_ref()
+ .map(|t| t.as_ref().version())
+ .unwrap_or(0)
+ }
+
+ fn current_targets_version(&self) -> u32 {
+ self.targets.as_ref()
+ .map(|t| t.as_ref().version())
+ .unwrap_or(0)
+ }
+
+ fn current_delegation_version(&self, role: &MetadataPath) -> u32 {
+ self.delegations.get(role)
+ .map(|t| t.as_ref().version())
+ .unwrap_or(0)
+ }
+
/// 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
- }
- }),
- )?;
+ pub fn update_root(&mut self, signed_root: SignedMetadata<D, RootMetadata>) -> Result<bool> {
+ {
+ let old_root = self.root.as_ref();
+ let new_root = signed_root.as_ref();
- let root = D::deserialize::<RootMetadata>(signed_root.signed())?;
+ // First, check that the new root was signed by the old root.
+ signed_root.verify(
+ old_root.root().threshold(),
+ old_root.keys().iter().filter_map(|(k, v)| {
+ if old_root.root().key_ids().contains(k) {
+ Some(v)
+ } else {
+ None
+ }
+ }),
+ )?;
- match root.version() {
- x if x == self.root.version() => {
+ // Next, make sure the new root has a higher version than the old root.
+ if new_root.version() == old_root.version() {
info!(
"Attempted to update root to new metadata with the same version. \
- Refusing to update."
+ Refusing to update."
);
return Ok(false);
- }
- x if x < self.root.version() => {
+ } else if new_root.version() < old_root.version() {
return Err(Error::VerificationFailure(format!(
"Attempted to roll back root metadata at version {} to {}.",
- self.root.version(),
- x
- )))
+ old_root.version(),
+ new_root.version()
+ )));
}
- _ => (),
- }
- signed_root.verify(
- root.root().threshold(),
- root.keys().iter().filter_map(|(k, v)| {
- if root.root().key_ids().contains(k) {
- Some(v)
- } else {
- None
- }
- }),
- )?;
+ // Finally, make sure the new root was signed by the keys in the new root.
+ signed_root.verify(
+ new_root.root().threshold(),
+ new_root.keys().iter().filter_map(|(k, v)| {
+ if new_root.root().key_ids().contains(k) {
+ Some(v)
+ } else {
+ None
+ }
+ }),
+ )?;
+ }
self.purge_metadata();
- self.root = root;
+ self.root = signed_root;
Ok(true)
}
/// Verify and update the timestamp metadata.
pub fn update_timestamp(
&mut self,
- signed_timestamp: &SignedMetadata<D, TimestampMetadata>,
+ 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 root = self.root.as_ref();
+ let timestamp = signed_timestamp.as_ref();
- let current_version = self.timestamp.as_ref().map(|t| t.version()).unwrap_or(0);
- let timestamp: TimestampMetadata = D::deserialize(&signed_timestamp.signed())?;
+ // First, make sure the root signed the metadata.
+ signed_timestamp.verify(
+ root.timestamp().threshold(),
+ root.keys().iter().filter_map(|(k, v)| {
+ if root.timestamp().key_ids().contains(k) {
+ Some(v)
+ } else {
+ None
+ }
+ }),
+ )?;
- 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;
+ // Next, make sure the timestamp hasn't expired.
+ if timestamp.expires() <= &Utc::now() {
+ return Err(Error::ExpiredMetadata(Role::Timestamp));
}
- self.timestamp = Some(timestamp);
+ // Next, make sure the new metadata has a higher version than the old metadata.
+ let current_version = self.current_timestamp_version();
- Ok(true)
+ if timestamp.version() < current_version {
+ return Err(Error::VerificationFailure(format!(
+ "Attempted to roll back timestamp metadata at version {} to {}.",
+ current_version,
+ timestamp.version()
+ )));
+ } else if timestamp.version() == current_version {
+ return Ok(false);
+ }
+
+ if self.current_snapshot_version() != timestamp.snapshot().version() {
+ self.snapshot = None;
+ }
}
+
+ self.timestamp = Some(signed_timestamp);
+ Ok(true)
}
/// Verify and update the snapshot metadata.
pub fn update_snapshot(
&mut self,
- signed_snapshot: &SignedMetadata<D, SnapshotMetadata>,
+ 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);
+ let current_version = self.current_snapshot_version();
if timestamp.snapshot().version() < current_version {
return Err(Error::VerificationFailure(format!(
@@ -209,7 +261,7 @@
signed_snapshot.verify(
root.snapshot().threshold(),
- self.root.keys().iter().filter_map(|(k, v)| {
+ self.root.as_ref().keys().iter().filter_map(|(k, v)| {
if root.snapshot().key_ids().contains(k) {
Some(v)
} else {
@@ -218,7 +270,7 @@
}),
)?;
- let snapshot: SnapshotMetadata = D::deserialize(&signed_snapshot.signed())?;
+ let snapshot = signed_snapshot.as_ref();
if snapshot.version() != timestamp.snapshot().version() {
return Err(Error::VerificationFailure(format!(
@@ -231,12 +283,10 @@
// 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
+ if self.targets.as_ref().map(|s| s.as_ref().version()).unwrap_or(0)
+ != signed_snapshot.as_ref()
.meta()
.get(&MetadataPath::from_role(&Role::Targets))
.map(|m| m.version())
@@ -245,7 +295,7 @@
self.targets = None;
}
- self.snapshot = Some(snapshot);
+ self.snapshot = Some(signed_snapshot);
self.purge_delegations();
Ok(true)
}
@@ -263,7 +313,7 @@
None => continue,
};
- if delegation.version() > definition.version() {
+ if delegation.as_ref().version() > definition.version() {
let _ = purge.insert(role.clone());
continue;
}
@@ -280,9 +330,9 @@
/// Verify and update the targets metadata.
pub fn update_targets(
&mut self,
- signed_targets: &SignedMetadata<D, TargetsMetadata>,
+ 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
@@ -294,7 +344,7 @@
)
})?;
- let current_version = self.targets.as_ref().map(|t| t.version()).unwrap_or(0);
+ let current_version = self.current_targets_version();
if targets_description.version() < current_version {
return Err(Error::VerificationFailure(format!(
@@ -317,12 +367,12 @@
}),
)?;
- let targets: TargetsMetadata = D::deserialize(&signed_targets.signed())?;
+ let targets = signed_targets.as_ref();
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.",
+ version {} but version {} was found instead.",
targets_description.version(),
targets.version()
)));
@@ -331,10 +381,9 @@
if targets.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Snapshot));
}
- targets
- };
+ }
- self.targets = Some(targets);
+ self.targets = Some(signed_targets);
Ok(true)
}
@@ -342,9 +391,9 @@
pub fn update_delegation(
&mut self,
role: &MetadataPath,
- signed: &SignedMetadata<D, TargetsMetadata>,
+ signed_delegation: SignedMetadata<D, TargetsMetadata>,
) -> Result<bool> {
- let delegation = {
+ {
let _ = self.safe_root_ref()?;
let snapshot = self.safe_snapshot_ref()?;
let targets = self.safe_targets_ref()?;
@@ -367,7 +416,8 @@
}
};
- let current_version = self.delegations.get(role).map(|t| t.version()).unwrap_or(0);
+ let current_version = self.current_delegation_version(role);
+
if delegation_description.version() < current_version {
return Err(Error::VerificationFailure(format!(
"Snapshot metadata did listed delegation {:?} version as {} but current\
@@ -381,7 +431,7 @@
}
for delegated_targets in self.delegations.values() {
- let parent = match delegated_targets.delegations() {
+ let parent = match delegated_targets.as_ref().delegations() {
Some(d) => d,
None => &targets_delegations,
};
@@ -391,7 +441,7 @@
None => continue,
};
- signed.verify(
+ signed_delegation.verify(
delegation.threshold(),
parent.keys().iter().filter_map(|(k, v)| {
if delegation.key_ids().contains(k) {
@@ -403,11 +453,11 @@
)?;
}
- let delegation: TargetsMetadata = D::deserialize(signed.signed())?;
+ let delegation = signed_delegation.as_ref();
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.",
+ version {} but version {} was found instead.",
role,
delegation_description.version(),
delegation.version(),
@@ -418,11 +468,10 @@
// 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(), signed_delegation);
- let _ = self.delegations.insert(role.clone(), delegation);
Ok(true)
}
@@ -462,7 +511,7 @@
}
let targets = match tuf.delegations.get(delegation.role()) {
- Some(t) => t,
+ Some(t) => t.as_ref(),
None => return (delegation.terminating(), None),
};
@@ -515,15 +564,17 @@
}
fn safe_root_ref(&self) -> Result<&RootMetadata> {
- if self.root.expires() <= &Utc::now() {
+ let root = self.root.as_ref();
+ if root.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Root));
}
- Ok(&self.root)
+ Ok(&root)
}
fn safe_snapshot_ref(&self) -> Result<&SnapshotMetadata> {
match self.snapshot {
Some(ref snapshot) => {
+ let snapshot = snapshot.as_ref();
if snapshot.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Snapshot));
}
@@ -536,6 +587,7 @@
fn safe_targets_ref(&self) -> Result<&TargetsMetadata> {
match self.targets {
Some(ref targets) => {
+ let targets = targets.as_ref();
if targets.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Targets));
}
@@ -547,6 +599,7 @@
fn safe_timestamp_ref(&self) -> Result<&TimestampMetadata> {
match self.timestamp {
Some(ref timestamp) => {
+ let timestamp = timestamp.as_ref();
if timestamp.expires() <= &Utc::now() {
return Err(Error::ExpiredMetadata(Role::Timestamp));
}
@@ -560,10 +613,14 @@
#[cfg(test)]
mod test {
use super::*;
- use chrono::prelude::*;
use crypto::{HashAlgorithm, PrivateKey, SignatureScheme};
use interchange::Json;
- use metadata::{MetadataDescription, RoleDefinition};
+ use metadata::{
+ RootMetadataBuilder,
+ SnapshotMetadataBuilder,
+ TimestampMetadataBuilder,
+ TargetsMetadataBuilder,
+ };
lazy_static! {
static ref KEYS: Vec<PrivateKey> = {
@@ -584,450 +641,326 @@
#[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();
+ let root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[0].public().clone())
+ .targets_key(KEYS[0].public().clone())
+ .timestamp_key(KEYS[0].public().clone())
+ .signed::<Json>(&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();
+ let root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[0].public().clone())
+ .targets_key(KEYS[0].public().clone())
+ .timestamp_key(KEYS[0].public().clone())
+ .signed::<Json>(&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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[0].public().clone())
+ .targets_key(KEYS[0].public().clone())
+ .timestamp_key(KEYS[0].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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();
+ let mut root = RootMetadataBuilder::new()
+ .version(2)
+ .root_key(KEYS[1].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[1].public().clone())
+ .timestamp_key(KEYS[1].public().clone())
+ .signed::<Json>(&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));
+ assert_eq!(tuf.update_root(root.clone()), Ok(true));
// second update should do nothing
- assert_eq!(tuf.update_root(&root), Ok(false));
+ 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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[0].public().clone())
+ .targets_key(KEYS[0].public().clone())
+ .timestamp_key(KEYS[0].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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();
+ let root = RootMetadataBuilder::new()
+ .root_key(KEYS[1].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[1].public().clone())
+ .timestamp_key(KEYS[1].public().clone())
+ .signed::<Json>(&KEYS[1])
+ .unwrap();
- assert!(tuf.update_root(&root).is_err());
+ 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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[1].public().clone())
+ .timestamp_key(KEYS[1].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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(×tamp, &KEYS[1]).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .signed::<Json>(&KEYS[1])
+ .unwrap();
- assert_eq!(tuf.update_timestamp(×tamp), Ok(true));
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ .signed::<Json>(&KEYS[1])
+ .unwrap();
+
+ assert_eq!(tuf.update_timestamp(timestamp.clone()), Ok(true));
// second update should do nothing
- assert_eq!(tuf.update_timestamp(×tamp), Ok(false))
+ 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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[1].public().clone())
+ .timestamp_key(KEYS[1].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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 snapshot = SnapshotMetadataBuilder::new()
+ .signed::<Json>(&KEYS[1])
+ .unwrap();
- // sign it with the root key
- let timestamp: SignedMetadata<Json, TimestampMetadata> =
- SignedMetadata::new(×tamp, &KEYS[0]).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ // sign it with the root key
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- assert!(tuf.update_timestamp(×tamp).is_err())
+ 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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[2].public().clone())
+ .timestamp_key(KEYS[2].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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(×tamp, &KEYS[2]).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .signed(&KEYS[1])
+ .unwrap();
- tuf.update_timestamp(×tamp).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ .signed::<Json>(&KEYS[2])
+ .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();
+ tuf.update_timestamp(timestamp).unwrap();
- assert_eq!(tuf.update_snapshot(&snapshot), Ok(true));
+ assert_eq!(tuf.update_snapshot(snapshot.clone()), Ok(true));
// second update should do nothing
- assert_eq!(tuf.update_snapshot(&snapshot), Ok(false));
+ 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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[2].public().clone())
+ .timestamp_key(KEYS[2].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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(×tamp, &KEYS[2]).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .signed::<Json>(&KEYS[2])
+ .unwrap();
- tuf.update_timestamp(×tamp).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ // sign it with the targets key
+ .signed::<Json>(&KEYS[2])
+ .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();
+ tuf.update_timestamp(timestamp).unwrap();
- assert!(tuf.update_snapshot(&snapshot).is_err());
+ 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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[2].public().clone())
+ .timestamp_key(KEYS[2].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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(×tamp, &KEYS[2]).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .version(2)
+ .signed::<Json>(&KEYS[2])
+ .unwrap();
- tuf.update_timestamp(×tamp).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ .signed::<Json>(&KEYS[2])
+ .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();
+ tuf.update_timestamp(timestamp).unwrap();
- assert!(tuf.update_snapshot(&snapshot).is_err());
+ let snapshot = SnapshotMetadataBuilder::new()
+ .version(1)
+ .signed::<Json>(&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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[2].public().clone())
+ .timestamp_key(KEYS[3].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).unwrap();
+ let targets = TargetsMetadataBuilder::new()
+ .signed::<Json>(&KEYS[2])
+ .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(×tamp, &KEYS[3]).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .insert_metadata(&targets, &[HashAlgorithm::Sha256]).unwrap()
+ .signed::<Json>(&KEYS[1])
+ .unwrap();
- tuf.update_timestamp(×tamp).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ .signed::<Json>(&KEYS[3])
+ .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();
+ let mut tuf = Tuf::from_root(root).unwrap();
- tuf.update_snapshot(&snapshot).unwrap();
+ tuf.update_timestamp(timestamp).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));
+ assert_eq!(tuf.update_targets(targets.clone()), Ok(true));
// second update should do nothing
- assert_eq!(tuf.update_targets(&targets), Ok(false));
+ 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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[2].public().clone())
+ .timestamp_key(KEYS[3].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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(×tamp, &KEYS[3]).unwrap();
+ let targets = TargetsMetadataBuilder::new()
+ // sign it with the timestamp key
+ .signed::<Json>(&KEYS[3])
+ .unwrap();
- tuf.update_timestamp(×tamp).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .insert_metadata(&targets, &[HashAlgorithm::Sha256]).unwrap()
+ .signed::<Json>(&KEYS[1])
+ .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();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ .signed::<Json>(&KEYS[3])
+ .unwrap();
- tuf.update_snapshot(&snapshot).unwrap();
+ tuf.update_timestamp(timestamp).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());
+ 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 root = RootMetadataBuilder::new()
+ .root_key(KEYS[0].public().clone())
+ .snapshot_key(KEYS[1].public().clone())
+ .targets_key(KEYS[2].public().clone())
+ .timestamp_key(KEYS[3].public().clone())
+ .signed::<Json>(&KEYS[0])
+ .unwrap();
- let mut tuf = Tuf::from_root(&root).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(×tamp, &KEYS[3]).unwrap();
+ let targets = TargetsMetadataBuilder::new()
+ .version(2)
+ .signed::<Json>(&KEYS[2])
+ .unwrap();
- tuf.update_timestamp(×tamp).unwrap();
+ let snapshot = SnapshotMetadataBuilder::new()
+ .insert_metadata(&targets, &[HashAlgorithm::Sha256]).unwrap()
+ .signed::<Json>(&KEYS[1])
+ .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();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ .signed::<Json>(&KEYS[3])
+ .unwrap();
- tuf.update_snapshot(&snapshot).unwrap();
+ tuf.update_timestamp(timestamp).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();
+ let targets = TargetsMetadataBuilder::new()
+ .version(1)
+ .signed::<Json>(&KEYS[2])
+ .unwrap();
- assert!(tuf.update_targets(&targets).is_err());
+ assert!(tuf.update_targets(targets).is_err());
}
}
diff --git a/tests/integration.rs b/tests/integration.rs
index 92af93e..ac0dcd6 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -1,16 +1,13 @@
-extern crate chrono;
#[macro_use]
extern crate maplit;
extern crate tuf;
-use chrono::offset::Utc;
-use chrono::prelude::*;
-use std::collections::HashMap;
use tuf::crypto::{HashAlgorithm, PrivateKey, SignatureScheme};
use tuf::interchange::Json;
use tuf::metadata::{
- Delegation, Delegations, MetadataDescription, MetadataPath, RoleDefinition, RootMetadata,
- SignedMetadata, SnapshotMetadata, TargetDescription, TargetsMetadata, TimestampMetadata,
+ Delegation, Delegations, MetadataDescription, MetadataPath,
+ RootMetadataBuilder, SnapshotMetadataBuilder,
+ TargetsMetadataBuilder, TimestampMetadataBuilder,
VirtualTargetPath,
};
use tuf::Tuf;
@@ -31,55 +28,38 @@
let delegation_key = PrivateKey::from_pkcs8(ED25519_5_PK8, SignatureScheme::Ed25519).unwrap();
//// build the root ////
- let keys = vec![
- root_key.public().clone(),
- snapshot_key.public().clone(),
- targets_key.public().clone(),
- timestamp_key.public().clone(),
- ];
- let root_def = RoleDefinition::new(1, hashset!(root_key.key_id().clone())).unwrap();
- let snapshot_def = RoleDefinition::new(1, hashset!(snapshot_key.key_id().clone())).unwrap();
- let targets_def = RoleDefinition::new(1, hashset!(targets_key.key_id().clone())).unwrap();
- let timestamp_def = RoleDefinition::new(1, hashset!(timestamp_key.key_id().clone())).unwrap();
+ let root = RootMetadataBuilder::new()
+ .root_key(root_key.public().clone())
+ .snapshot_key(snapshot_key.public().clone())
+ .targets_key(targets_key.public().clone())
+ .timestamp_key(timestamp_key.public().clone())
+ .signed::<Json>(&root_key)
+ .unwrap();
- let root = RootMetadata::new(
- 1,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- false,
- keys,
- root_def,
- snapshot_def,
- targets_def,
- timestamp_def,
- ).unwrap();
+ let mut tuf = Tuf::<Json>::from_root_pinned(root, &[root_key.key_id().clone()]).unwrap();
- let signed = SignedMetadata::<Json, RootMetadata>::new(&root, &root_key).unwrap();
+ //// build the snapshot and timestamp ////
- let mut tuf = Tuf::<Json>::from_root_pinned(signed, &[root_key.key_id().clone()]).unwrap();
-
- //// build the timestamp ////
- let snap = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
- let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), snap).unwrap();
-
- let signed =
- SignedMetadata::<Json, TimestampMetadata>::new(×tamp, ×tamp_key).unwrap();
-
- tuf.update_timestamp(&signed).unwrap();
-
- //// build the snapshot ////
- let meta_map = hashmap! {
- MetadataPath::new("targets".into()).unwrap() =>
+ let snapshot = SnapshotMetadataBuilder::new()
+ .insert_metadata_description(
+ MetadataPath::new("targets".into()).unwrap(),
MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
- MetadataPath::new("delegation".into()).unwrap() =>
+ )
+ .insert_metadata_description(
+ MetadataPath::new("delegation".into()).unwrap(),
MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
- };
- let snapshot =
- SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map).unwrap();
+ )
+ .signed::<Json>(&snapshot_key)
+ .unwrap();
- let signed = SignedMetadata::<Json, SnapshotMetadata>::new(&snapshot, &snapshot_key).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ .signed::<Json>(×tamp_key)
+ .unwrap();
- tuf.update_snapshot(&signed).unwrap();
+ tuf.update_timestamp(timestamp).unwrap();
+ tuf.update_snapshot(snapshot).unwrap();
//// build the targets ////
let delegations = Delegations::new(
@@ -100,30 +80,25 @@
).unwrap(),
],
).unwrap();
- let targets = TargetsMetadata::new(
- 1,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- HashMap::new(),
- Some(delegations),
- ).unwrap();
+ let targets = TargetsMetadataBuilder::new()
+ .delegations(delegations)
+ .signed::<Json>(&targets_key)
+ .unwrap();
- let signed = SignedMetadata::<Json, TargetsMetadata>::new(&targets, &targets_key).unwrap();
-
- tuf.update_targets(&signed).unwrap();
+ tuf.update_targets(targets).unwrap();
//// build the delegation ////
let target_file: &[u8] = b"bar";
- let target_map = hashmap! {
- VirtualTargetPath::new("foo".into()).unwrap() =>
- TargetDescription::from_reader(target_file, &[HashAlgorithm::Sha256]).unwrap(),
- };
- let delegation =
- TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), target_map, None).unwrap();
+ let delegation = TargetsMetadataBuilder::new()
+ .insert_target_from_reader(
+ VirtualTargetPath::new("foo".into()).unwrap(),
+ target_file,
+ &[HashAlgorithm::Sha256],
+ ).unwrap()
+ .signed::<Json>(&delegation_key)
+ .unwrap();
- let signed =
- SignedMetadata::<Json, TargetsMetadata>::new(&delegation, &delegation_key).unwrap();
-
- tuf.update_delegation(&MetadataPath::new("delegation".into()).unwrap(), &signed)
+ tuf.update_delegation(&MetadataPath::new("delegation".into()).unwrap(), delegation)
.unwrap();
assert!(
@@ -142,59 +117,45 @@
let delegation_b_key = PrivateKey::from_pkcs8(ED25519_6_PK8, SignatureScheme::Ed25519).unwrap();
//// build the root ////
- let keys = vec![
- root_key.public().clone(),
- snapshot_key.public().clone(),
- targets_key.public().clone(),
- timestamp_key.public().clone(),
- ];
- let root_def = RoleDefinition::new(1, hashset!(root_key.key_id().clone())).unwrap();
- let snapshot_def = RoleDefinition::new(1, hashset!(snapshot_key.key_id().clone())).unwrap();
- let targets_def = RoleDefinition::new(1, hashset!(targets_key.key_id().clone())).unwrap();
- let timestamp_def = RoleDefinition::new(1, hashset!(timestamp_key.key_id().clone())).unwrap();
+ let root = RootMetadataBuilder::new()
+ .root_key(root_key.public().clone())
+ .snapshot_key(snapshot_key.public().clone())
+ .targets_key(targets_key.public().clone())
+ .timestamp_key(timestamp_key.public().clone())
+ .signed::<Json>(&root_key)
+ .unwrap();
- let root = RootMetadata::new(
- 1,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- false,
- keys,
- root_def,
- snapshot_def,
- targets_def,
- timestamp_def,
- ).unwrap();
+ let mut tuf = Tuf::<Json>::from_root_pinned(root, &[root_key.key_id().clone()]).unwrap();
- let signed = SignedMetadata::<Json, RootMetadata>::new(&root, &root_key).unwrap();
+ //// build the snapshot and timestamp ////
- let mut tuf = Tuf::<Json>::from_root_pinned(signed, &[root_key.key_id().clone()]).unwrap();
-
- //// build the timestamp ////
- let snap = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
- let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), snap).unwrap();
-
- let signed =
- SignedMetadata::<Json, TimestampMetadata>::new(×tamp, ×tamp_key).unwrap();
-
- tuf.update_timestamp(&signed).unwrap();
-
- //// build the snapshot ////
- let meta_map = hashmap! {
- MetadataPath::new("targets".into()).unwrap() =>
+ let snapshot = SnapshotMetadataBuilder::new()
+ .insert_metadata_description(
+ MetadataPath::new("targets".into()).unwrap(),
MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
- MetadataPath::new("delegation-a".into()).unwrap() =>
+ )
+ .insert_metadata_description(
+ MetadataPath::new("delegation-a".into()).unwrap(),
MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
- MetadataPath::new("delegation-b".into()).unwrap() =>
+ )
+ .insert_metadata_description(
+ MetadataPath::new("delegation-b".into()).unwrap(),
MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
- };
- let snapshot =
- SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map).unwrap();
+ )
+ .signed::<Json>(&snapshot_key)
+ .unwrap();
- let signed = SignedMetadata::<Json, SnapshotMetadata>::new(&snapshot, &snapshot_key).unwrap();
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+ .unwrap()
+ .signed::<Json>(×tamp_key)
+ .unwrap();
- tuf.update_snapshot(&signed).unwrap();
+ tuf.update_timestamp(timestamp).unwrap();
+ tuf.update_snapshot(snapshot).unwrap();
//// build the targets ////
+
let delegations = Delegations::new(
&hashset![delegation_a_key.public().clone()],
vec![
@@ -213,18 +174,15 @@
).unwrap(),
],
).unwrap();
- let targets = TargetsMetadata::new(
- 1,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- HashMap::new(),
- Some(delegations),
- ).unwrap();
+ let targets = TargetsMetadataBuilder::new()
+ .delegations(delegations)
+ .signed::<Json>(&targets_key)
+ .unwrap();
- let signed = SignedMetadata::<Json, TargetsMetadata>::new(&targets, &targets_key).unwrap();
-
- tuf.update_targets(&signed).unwrap();
+ tuf.update_targets(targets).unwrap();
//// build delegation A ////
+
let delegations = Delegations::new(
&hashset![delegation_b_key.public().clone()],
vec![
@@ -243,33 +201,29 @@
).unwrap(),
],
).unwrap();
- let delegation = TargetsMetadata::new(
- 1,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- HashMap::new(),
- Some(delegations),
- ).unwrap();
- let signed =
- SignedMetadata::<Json, TargetsMetadata>::new(&delegation, &delegation_a_key).unwrap();
+ let delegation = TargetsMetadataBuilder::new()
+ .delegations(delegations)
+ .signed::<Json>(&delegation_a_key)
+ .unwrap();
- tuf.update_delegation(&MetadataPath::new("delegation-a".into()).unwrap(), &signed)
+ tuf.update_delegation(&MetadataPath::new("delegation-a".into()).unwrap(), delegation)
.unwrap();
//// build delegation B ////
+
let target_file: &[u8] = b"bar";
- let target_map = hashmap! {
- VirtualTargetPath::new("foo".into()).unwrap() =>
- TargetDescription::from_reader(target_file, &[HashAlgorithm::Sha256]).unwrap(),
- };
- let delegation =
- TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), target_map, None).unwrap();
+ let delegation = TargetsMetadataBuilder::new()
+ .insert_target_from_reader(
+ VirtualTargetPath::new("foo".into()).unwrap(),
+ target_file,
+ &[HashAlgorithm::Sha256],
+ ).unwrap()
+ .signed::<Json>(&delegation_b_key)
+ .unwrap();
- let signed =
- SignedMetadata::<Json, TargetsMetadata>::new(&delegation, &delegation_b_key).unwrap();
-
- tuf.update_delegation(&MetadataPath::new("delegation-b".into()).unwrap(), &signed)
+ tuf.update_delegation(&MetadataPath::new("delegation-b".into()).unwrap(), delegation)
.unwrap();
assert!(
diff --git a/tests/simple_example.rs b/tests/simple_example.rs
index 5782fa0..2b1d639 100644
--- a/tests/simple_example.rs
+++ b/tests/simple_example.rs
@@ -1,17 +1,13 @@
extern crate chrono;
-#[macro_use]
-extern crate maplit;
extern crate tuf;
-use chrono::offset::Utc;
-use chrono::prelude::*;
use tuf::client::{Client, Config, PathTranslator};
use tuf::crypto::{HashAlgorithm, KeyId, PrivateKey, SignatureScheme};
-use tuf::interchange::{DataInterchange, Json};
+use tuf::interchange::Json;
use tuf::metadata::{
- MetadataDescription, MetadataPath, MetadataVersion, RoleDefinition, RootMetadata,
- SignedMetadata, SnapshotMetadata, TargetDescription, TargetPath, TargetsMetadata,
- TimestampMetadata, VirtualTargetPath,
+ MetadataPath, MetadataVersion, RootMetadataBuilder,
+ SnapshotMetadataBuilder, TargetPath, TargetsMetadataBuilder,
+ TimestampMetadataBuilder, VirtualTargetPath,
};
use tuf::repository::{EphemeralRepository, Repository};
use tuf::Result;
@@ -84,30 +80,12 @@
//// build the root ////
- let keys = vec![
- root_key.public().clone(),
- snapshot_key.public().clone(),
- targets_key.public().clone(),
- timestamp_key.public().clone(),
- ];
-
- let root_def = RoleDefinition::new(1, hashset!(root_key.key_id().clone()))?;
- let snapshot_def = RoleDefinition::new(1, hashset!(snapshot_key.key_id().clone()))?;
- let targets_def = RoleDefinition::new(1, hashset!(targets_key.key_id().clone()))?;
- let timestamp_def = RoleDefinition::new(1, hashset!(timestamp_key.key_id().clone()))?;
-
- let root = RootMetadata::new(
- 1,
- Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
- false,
- keys,
- root_def,
- snapshot_def,
- targets_def,
- timestamp_def,
- )?;
-
- let signed = SignedMetadata::<Json, RootMetadata>::new(&root, &root_key)?;
+ let signed = RootMetadataBuilder::new()
+ .root_key(root_key.public().clone())
+ .snapshot_key(snapshot_key.public().clone())
+ .targets_key(targets_key.public().clone())
+ .timestamp_key(timestamp_key.public().clone())
+ .signed::<Json>(&root_key)?;
remote.store_metadata(
&MetadataPath::new("root".into())?,
@@ -123,66 +101,60 @@
//// build the targets ////
let target_file: &[u8] = b"things fade, alternatives exclude";
+
let target_path = TargetPath::new("foo-bar".into())?;
- let target_description = TargetDescription::from_reader(target_file, &[HashAlgorithm::Sha256])?;
let _ = remote.store_target(target_file, &target_path);
- let target_map =
- hashmap!(config.path_translator().real_to_virtual(&target_path)? => target_description);
- let targets = TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), target_map, None)?;
-
- let signed = SignedMetadata::<Json, TargetsMetadata>::new(&targets, &targets_key)?;
+ let targets = TargetsMetadataBuilder::new()
+ .insert_target_from_reader(
+ config.path_translator().real_to_virtual(&target_path)?,
+ target_file,
+ &[HashAlgorithm::Sha256],
+ )?
+ .signed::<Json>(&targets_key)?;
remote.store_metadata(
&MetadataPath::new("targets".into())?,
&MetadataVersion::Number(1),
- &signed,
+ &targets,
)?;
remote.store_metadata(
&MetadataPath::new("targets".into())?,
&MetadataVersion::None,
- &signed,
+ &targets,
)?;
- let targets_bytes = Json::canonicalize(&Json::serialize(&signed)?)?;
-
//// build the snapshot ////
- let meta_map = hashmap! {
- MetadataPath::new("targets".into())? =>
- MetadataDescription::from_reader(&*targets_bytes, 1, &[HashAlgorithm::Sha256])?,
- };
- let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)?;
- let signed = SignedMetadata::<Json, SnapshotMetadata>::new(&snapshot, &snapshot_key)?;
+ let snapshot = SnapshotMetadataBuilder::new()
+ .insert_metadata(&targets, &[HashAlgorithm::Sha256])?
+ .signed::<Json>(&snapshot_key)?;
remote.store_metadata(
&MetadataPath::new("snapshot".into())?,
&MetadataVersion::Number(1),
- &signed,
+ &snapshot,
)?;
remote.store_metadata(
&MetadataPath::new("snapshot".into())?,
&MetadataVersion::None,
- &signed,
+ &snapshot,
)?;
- let snapshot_bytes = Json::canonicalize(&Json::serialize(&signed)?)?;
-
//// build the timestamp ////
- let snap = MetadataDescription::from_reader(&*snapshot_bytes, 1, &[HashAlgorithm::Sha256])?;
- let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), snap)?;
- let signed = SignedMetadata::<Json, TimestampMetadata>::new(×tamp, ×tamp_key)?;
+ let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])?
+ .signed::<Json>(×tamp_key)?;
remote.store_metadata(
&MetadataPath::new("timestamp".into())?,
&MetadataVersion::Number(1),
- &signed,
+ ×tamp,
)?;
remote.store_metadata(
&MetadataPath::new("timestamp".into())?,
&MetadataVersion::None,
- &signed,
+ ×tamp,
)?;
Ok(vec![root_key.key_id().clone()])