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(&timestamp, &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(
-    ///     &timestamp, &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(
-    ///     &timestamp, &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(&timestamp).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(&timestamp, &KEYS[1]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
 
-        assert_eq!(tuf.update_timestamp(&timestamp), 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(&timestamp), 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(&timestamp, &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(&timestamp).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(&timestamp, &KEYS[2]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .signed(&KEYS[1])
+            .unwrap();
 
-        tuf.update_timestamp(&timestamp).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(&timestamp, &KEYS[2]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
 
-        tuf.update_timestamp(&timestamp).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(&timestamp, &KEYS[2]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .version(2)
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
 
-        tuf.update_timestamp(&timestamp).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(&timestamp, &KEYS[3]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .insert_metadata(&targets, &[HashAlgorithm::Sha256]).unwrap()
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
 
-        tuf.update_timestamp(&timestamp).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(&timestamp, &KEYS[3]).unwrap();
+        let targets = TargetsMetadataBuilder::new()
+            // sign it with the timestamp key
+            .signed::<Json>(&KEYS[3])
+            .unwrap();
 
-        tuf.update_timestamp(&timestamp).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(&timestamp, &KEYS[3]).unwrap();
+        let targets = TargetsMetadataBuilder::new()
+            .version(2)
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
 
-        tuf.update_timestamp(&timestamp).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(&timestamp, &timestamp_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>(&timestamp_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(&timestamp, &timestamp_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>(&timestamp_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(&timestamp, &timestamp_key)?;
+    let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])?
+        .signed::<Json>(&timestamp_key)?;
 
     remote.store_metadata(
         &MetadataPath::new("timestamp".into())?,
         &MetadataVersion::Number(1),
-        &signed,
+        &timestamp,
     )?;
     remote.store_metadata(
         &MetadataPath::new("timestamp".into())?,
         &MetadataVersion::None,
-        &signed,
+        &timestamp,
     )?;
 
     Ok(vec![root_key.key_id().clone()])