Merge branch 'tests' into develop
diff --git a/Cargo.toml b/Cargo.toml
index ef2a49e..473df2e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -47,5 +47,6 @@
 uuid = { version = "0.5", features = [ "v4" ] }
 
 [dev-dependencies]
+lazy_static = "0.2.8"
 maplit = "0.1.4"
 tempdir = "0.3"
diff --git a/src/client.rs b/src/client.rs
index 0d2c22d..da617e5 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -49,16 +49,8 @@
     ///
     /// Returns `true` if an update occurred and `false` otherwise.
     pub fn update_local(&mut self) -> Result<bool> {
-        let r = Self::update_root(
-            &mut self.tuf,
-            &mut self.local,
-            &self.config,
-        )?;
-        let ts = match Self::update_timestamp(
-            &mut self.tuf,
-            &mut self.local,
-            &self.config,
-        ) {
+        let r = Self::update_root(&mut self.tuf, &mut self.local, &self.config)?;
+        let ts = match Self::update_timestamp(&mut self.tuf, &mut self.local, &self.config) {
             Ok(b) => b,
             Err(e) => {
                 warn!(
@@ -68,11 +60,7 @@
                 false
             }
         };
-        let sn = match Self::update_snapshot(
-            &mut self.tuf,
-            &mut self.local,
-            &self.config,
-        ) {
+        let sn = match Self::update_snapshot(&mut self.tuf, &mut self.local, &self.config) {
             Ok(b) => b,
             Err(e) => {
                 warn!(
@@ -82,11 +70,7 @@
                 false
             }
         };
-        let ta = match Self::update_targets(
-            &mut self.tuf,
-            &mut self.local,
-            &self.config,
-        ) {
+        let ta = match Self::update_targets(&mut self.tuf, &mut self.local, &self.config) {
             Ok(b) => b,
             Err(e) => {
                 warn!(
@@ -104,36 +88,16 @@
     ///
     /// Returns `true` if an update occurred and `false` otherwise.
     pub fn update_remote(&mut self) -> Result<bool> {
-        let r = Self::update_root(
-            &mut self.tuf,
-            &mut self.remote,
-            &self.config,
-        )?;
-        let ts = Self::update_timestamp(
-            &mut self.tuf,
-            &mut self.remote,
-            &self.config,
-        )?;
-        let sn = Self::update_snapshot(
-            &mut self.tuf,
-            &mut self.remote,
-            &self.config,
-        )?;
-        let ta = Self::update_targets(
-            &mut self.tuf,
-            &mut self.remote,
-            &self.config,
-        )?;
+        let r = Self::update_root(&mut self.tuf, &mut self.remote, &self.config)?;
+        let ts = Self::update_timestamp(&mut self.tuf, &mut self.remote, &self.config)?;
+        let sn = Self::update_snapshot(&mut self.tuf, &mut self.remote, &self.config)?;
+        let ta = Self::update_targets(&mut self.tuf, &mut self.remote, &self.config)?;
 
         Ok(r || ts || sn || ta)
     }
 
     /// Returns `true` if an update occurred and `false` otherwise.
-    fn update_root<T>(
-        tuf: &mut Tuf<D>,
-        repo: &mut T,
-        config: &Config,
-    ) -> Result<bool>
+    fn update_root<T>(tuf: &mut Tuf<D>, repo: &mut T, config: &Config) -> Result<bool>
     where
         T: Repository<D>,
     {
@@ -184,11 +148,7 @@
     }
 
     /// Returns `true` if an update occurred and `false` otherwise.
-    fn update_timestamp<T>(
-        tuf: &mut Tuf<D>,
-        repo: &mut T,
-        config: &Config,
-    ) -> Result<bool>
+    fn update_timestamp<T>(tuf: &mut Tuf<D>, repo: &mut T, config: &Config) -> Result<bool>
     where
         T: Repository<D>,
     {
@@ -209,16 +169,7 @@
         T: Repository<D>,
     {
         let snapshot_description = match tuf.timestamp() {
-            Some(ts) => {
-                match ts.meta().get(&MetadataPath::from_role(&Role::Snapshot)) {
-                    Some(d) => Ok(d),
-                    None => Err(Error::VerificationFailure(
-                        "Timestamp metadata did not contain a description of the \
-                                current snapshot metadata."
-                            .into(),
-                    )),
-                }
-            }
+            Some(ts) => Ok(ts.snapshot()),
             None => Err(Error::MissingMetadata(Role::Timestamp)),
         }?
             .clone();
@@ -228,7 +179,7 @@
         }
 
         let (alg, value) = crypto::hash_preference(snapshot_description.hashes())?;
-        
+
         let version = if tuf.root().consistent_snapshot() {
             MetadataVersion::Hash(value.clone())
         } else {
@@ -431,7 +382,13 @@
                             &signed_meta,
                         ) {
                             Ok(_) => (),
-                            Err(e) => warn!("Error storing metadata {:?} locally: {:?}", delegation.role(), e),
+                            Err(e) => {
+                                warn!(
+                                    "Error storing metadata {:?} locally: {:?}",
+                                    delegation.role(),
+                                    e
+                                )
+                            }
                         }
 
                         let meta = tuf.delegations().get(delegation.role()).unwrap().clone();
@@ -590,3 +547,114 @@
         }
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use chrono::prelude::*;
+    use crypto::{PrivateKey, SignatureScheme};
+    use interchange::JsonDataInterchange;
+    use metadata::{RootMetadata, SignedMetadata, RoleDefinition, MetadataPath, MetadataVersion};
+    use repository::EphemeralRepository;
+
+    lazy_static! {
+        static ref KEYS: Vec<PrivateKey> = {
+            let keys: &[&[u8]] = &[
+                include_bytes!("../tests/ed25519/ed25519-1.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-2.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-3.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-4.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-5.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-6.pk8.der"),
+            ];
+            keys.iter().map(|b| PrivateKey::from_pkcs8(b).unwrap()).collect()
+        };
+    }
+
+    #[test]
+    fn root_chain_update() {
+        let mut 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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).unwrap();
+
+        repo.store_metadata(
+            &Role::Root,
+            &MetadataPath::from_role(&Role::Root),
+            &MetadataVersion::Number(1),
+            &root,
+        ).unwrap();
+
+        let 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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[1], SignatureScheme::Ed25519).unwrap();
+
+        root.add_signature(&KEYS[0], SignatureScheme::Ed25519)
+            .unwrap();
+
+        repo.store_metadata(
+            &Role::Root,
+            &MetadataPath::from_role(&Role::Root),
+            &MetadataVersion::Number(2),
+            &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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[2], SignatureScheme::Ed25519).unwrap();
+
+        root.add_signature(&KEYS[1], SignatureScheme::Ed25519)
+            .unwrap();
+
+        repo.store_metadata(
+            &Role::Root,
+            &MetadataPath::from_role(&Role::Root),
+            &MetadataVersion::Number(3),
+            &root,
+        ).unwrap();
+        repo.store_metadata(
+            &Role::Root,
+            &MetadataPath::from_role(&Role::Root),
+            &MetadataVersion::None,
+            &root,
+        ).unwrap();
+
+        let mut client = Client::new(
+            tuf,
+            Config::build().finish().unwrap(),
+            repo,
+            EphemeralRepository::new(),
+        ).unwrap();
+        assert_eq!(client.update_local(), Ok(true));
+        assert_eq!(client.tuf.root().version(), 3);
+    }
+}
diff --git a/src/interchange/mod.rs b/src/interchange/mod.rs
index 3b01242..7635dba 100644
--- a/src/interchange/mod.rs
+++ b/src/interchange/mod.rs
@@ -14,7 +14,7 @@
 /// The format used for data interchange, serialization, and deserialization.
 pub trait DataInterchange: Debug + PartialEq + Clone {
     /// The type of data that is contained in the `signed` portion of metadata.
-    type RawData: Serialize + DeserializeOwned + Clone;
+    type RawData: Serialize + DeserializeOwned + Clone + PartialEq;
 
     /// The data interchange's extension.
     fn extension() -> &'static str;
diff --git a/src/lib.rs b/src/lib.rs
index 6243c25..cd23a47 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -70,6 +70,9 @@
 extern crate derp;
 extern crate hyper;
 extern crate itoa;
+#[cfg(test)]
+#[macro_use]
+extern crate lazy_static;
 #[macro_use]
 extern crate log;
 #[cfg(test)]
diff --git a/src/metadata.rs b/src/metadata.rs
index b2b4d0b..fef50a6 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -258,6 +258,11 @@
 
     /// Append a signature to this signed metadata. Will overwrite signature by keys with the same
     /// ID.
+    ///
+    /// **WARNING**: You should never have multiple TUF private keys on the same machine, so if
+    /// you're using this to append several signatures are once, you are doing something wrong. The
+    /// preferred method is to generate your copy of the metadata locally and use `merge_signatures`
+    /// to perform the "append" operations.
     pub fn add_signature(
         &mut self,
         private_key: &PrivateKey,
@@ -273,6 +278,32 @@
         Ok(())
     }
 
+    /// Merge the singatures from `other` into `self` if and only if
+    /// `self.signed() == other.signed()`. 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() {
+            return Err(Error::IllegalArgument(
+                "Attempted to merge unequal metadata".into(),
+            ));
+        }
+
+        let key_ids = self.signatures
+            .iter()
+            .map(|s| s.key_id().clone())
+            .collect::<HashSet<KeyId>>();
+
+        self.signatures.extend(
+            other
+                .signatures
+                .iter()
+                .filter(|s| !key_ids.contains(s.key_id()))
+                .cloned(),
+        );
+
+        Ok(())
+    }
+
     /// An immutable reference to the signatures.
     pub fn signatures(&self) -> &[Signature] {
         &self.signatures
@@ -314,7 +345,7 @@
         for sig in self.signatures.iter() {
             if !authorized_key_ids.contains(sig.key_id()) {
                 warn!(
-                    "Key ID {:?} is not authorized to sign root metadata.",
+                    "Key ID {:?} is not authorized to sign metadata.",
                     sig.key_id()
                 );
                 continue;
@@ -357,7 +388,7 @@
 }
 
 /// Metadata for the root role.
-#[derive(Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct RootMetadata {
     version: u32,
     expires: DateTime<Utc>,
@@ -633,11 +664,11 @@
 }
 
 /// Metadata for the timestamp role.
-#[derive(Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct TimestampMetadata {
     version: u32,
     expires: DateTime<Utc>,
-    meta: HashMap<MetadataPath, MetadataDescription>,
+    snapshot: MetadataDescription,
 }
 
 impl TimestampMetadata {
@@ -645,7 +676,7 @@
     pub fn new(
         version: u32,
         expires: DateTime<Utc>,
-        meta: HashMap<MetadataPath, MetadataDescription>,
+        snapshot: MetadataDescription,
     ) -> Result<Self> {
         if version < 1 {
             return Err(Error::IllegalArgument(format!(
@@ -657,7 +688,7 @@
         Ok(TimestampMetadata {
             version: version,
             expires: expires,
-            meta: meta,
+            snapshot: snapshot,
         })
     }
 
@@ -671,9 +702,9 @@
         &self.expires
     }
 
-    /// An immutable reference to the metadata paths and descriptions.
-    pub fn meta(&self) -> &HashMap<MetadataPath, MetadataDescription> {
-        &self.meta
+    /// An immutable reference to the snapshot description.
+    pub fn snapshot(&self) -> &MetadataDescription {
+        &self.snapshot
     }
 }
 
@@ -1224,6 +1255,12 @@
             return Err(Error::IllegalArgument("Cannot have threshold < 1".into()));
         }
 
+        if (key_ids.len() as u64) < (threshold as u64) {
+            return Err(Error::IllegalArgument(
+                "Cannot have threshold less than number of keys".into(),
+            ));
+        }
+
         Ok(Delegation {
             role: role,
             terminating: terminating,
@@ -1281,6 +1318,7 @@
 mod test {
     use super::*;
     use chrono::prelude::*;
+    use data_encoding::BASE64URL;
     use json;
     use interchange::JsonDataInterchange;
 
@@ -1501,27 +1539,22 @@
         let timestamp = TimestampMetadata::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(),
-            },
+            MetadataDescription::new(
+                1,
+                100,
+                hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
+            ).unwrap(),
         ).unwrap();
 
         let jsn = json!({
             "type": "timestamp",
             "version": 1,
             "expires": "2017-01-01T00:00:00Z",
-            "meta": {
-                "foo": {
-                    "version": 1,
-                    "size": 100,
-                    "hashes": {
-                        "sha256": "",
-                    },
+            "snapshot": {
+                "version": 1,
+                "size": 100,
+                "hashes": {
+                    "sha256": "",
                 },
             },
         });
@@ -1709,4 +1742,426 @@
             json::from_value(encoded).unwrap();
         assert_eq!(decoded, signed);
     }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Here there be test cases about what metadata is allowed to be parsed wherein we do all sorts
+    // of naughty things and make sure the parsers puke appropriately.
+    //                                   ______________
+    //                             ,===:'.,            `-._
+    //                                  `:.`---.__         `-._
+    //                                    `:.     `--.         `.
+    //                                      \.        `.         `.
+    //                              (,,(,    \.         `.   ____,-`.,
+    //                           (,'     `/   \.   ,--.___`.'
+    //                       ,  ,'  ,--.  `,   \.;'         `
+    //                        `{o, {    \  :    \;
+    //                          |,,'    /  /    //
+    //                          j;;    /  ,' ,-//.    ,---.      ,
+    //                          \;'   /  ,' /  _  \  /  _  \   ,'/
+    //                                \   `'  / \  `'  / \  `.' /
+    //                                 `.___,'   `.__,'   `.__,'
+    //
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+
+    fn make_root() -> json::Value {
+        let root_def = RoleDefinition::new(
+            1,
+            hashset!(PrivateKey::from_pkcs8(ED25519_1_PK8).unwrap().key_id().clone()),
+        ).unwrap();
+
+        let snapshot_def = RoleDefinition::new(
+            1,
+            hashset!(PrivateKey::from_pkcs8(ED25519_2_PK8).unwrap().key_id().clone()),
+        ).unwrap();
+
+        let targets_def = RoleDefinition::new(
+            1,
+            hashset!(PrivateKey::from_pkcs8(ED25519_3_PK8).unwrap().key_id().clone()),
+        ).unwrap();
+
+        let timestamp_def = RoleDefinition::new(
+            1,
+            hashset!(PrivateKey::from_pkcs8(ED25519_4_PK8).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).unwrap().public().clone(),
+                PrivateKey::from_pkcs8(ED25519_2_PK8).unwrap().public().clone(),
+                PrivateKey::from_pkcs8(ED25519_3_PK8).unwrap().public().clone(),
+                PrivateKey::from_pkcs8(ED25519_4_PK8).unwrap().public().clone(),
+            ),
+            root_def,
+            snapshot_def,
+            targets_def,
+            timestamp_def,
+        ).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();
+
+        json::to_value(&snapshot).unwrap()
+    }
+
+    fn make_timestamp() -> json::Value {
+        let timestamp = TimestampMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
+                .unwrap(),
+        ).unwrap();
+
+        json::to_value(&timestamp).unwrap()
+    }
+
+    fn make_targets() -> json::Value {
+        let targets =
+            TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!(), None)
+                .unwrap();
+
+        json::to_value(&targets).unwrap()
+    }
+
+    fn make_delegations() -> json::Value {
+        let key = PrivateKey::from_pkcs8(ED25519_1_PK8)
+            .unwrap()
+            .public()
+            .clone();
+        let delegations = Delegations::new(
+            vec![key.clone()],
+            vec![Delegation::new(
+                MetadataPath::new("foo".into()).unwrap(),
+                false,
+                1,
+                hashset!(key.key_id().clone()),
+                hashset!(TargetPath::new("bar".into()).unwrap()),
+            ).unwrap()],
+        ).unwrap();
+
+        json::to_value(&delegations).unwrap()
+    }
+
+    fn make_delegation() -> json::Value {
+        let key = PrivateKey::from_pkcs8(ED25519_1_PK8)
+            .unwrap()
+            .public()
+            .clone();
+        let delegation = Delegation::new(
+            MetadataPath::new("foo".into()).unwrap(),
+            false,
+            1,
+            hashset!(key.key_id().clone()),
+            hashset!(TargetPath::new("bar".into()).unwrap()),
+        ).unwrap();
+
+        json::to_value(&delegation).unwrap()
+    }
+
+    fn set_version(value: &mut json::Value, version: i64) {
+        match value.as_object_mut() {
+            Some(obj) => {
+                let _ = obj.insert("version".into(), json!(version));
+            }
+            None => panic!(),
+        }
+    }
+
+    // Refuse to deserialize root metadata if the version is not > 0
+    #[test]
+    fn deserialize_json_root_illegal_version() {
+        let mut root_json = make_root();
+        set_version(&mut root_json, 0);
+        assert!(json::from_value::<RootMetadata>(root_json.clone()).is_err());
+
+        let mut root_json = make_root();
+        set_version(&mut root_json, -1);
+        assert!(json::from_value::<RootMetadata>(root_json).is_err());
+    }
+
+    // Refuse to deserialize root metadata if any of the defined keys don't match their key ID
+    #[test]
+    fn deserialize_json_root_bad_key_ids() {
+        let mut root_json = make_root();
+        match root_json.as_object_mut() {
+            Some(obj) => {
+                match obj.get_mut("keys").unwrap().as_object_mut() {
+                    Some(keys) => {
+                        let key_id = keys.keys().next().unwrap().clone();
+                        let key = keys.get(&key_id).unwrap().clone();
+                        let mut bytes = BASE64URL.decode(key_id.as_bytes()).unwrap();
+                        bytes[0] ^= 0x01;
+                        let key_id = BASE64URL.encode(&bytes);
+                        let _ = keys.insert(key_id, key);
+                    }
+                    None => panic!(),
+                }
+            }
+            None => panic!(),
+        }
+
+        assert!(json::from_value::<RootMetadata>(root_json).is_err());
+    }
+
+    fn set_threshold(value: &mut json::Value, threshold: i32) {
+        match value.as_object_mut() {
+            Some(obj) => {
+                let _ = obj.insert("threshold".into(), json!(threshold));
+            }
+            None => panic!(),
+        }
+    }
+
+    // Refuse to deserialize role definitions with illegal thresholds
+    #[test]
+    fn deserialize_json_role_definition_illegal_threshold() {
+        let role_def = RoleDefinition::new(
+            1,
+            hashset!(PrivateKey::from_pkcs8(ED25519_1_PK8).unwrap().key_id().clone()),
+        ).unwrap();
+
+        let mut jsn = json::to_value(&role_def).unwrap();
+        set_threshold(&mut jsn, 0);
+        assert!(json::from_value::<RoleDefinition>(jsn).is_err());
+
+        let mut jsn = json::to_value(&role_def).unwrap();
+        set_threshold(&mut jsn, -1);
+        assert!(json::from_value::<RoleDefinition>(jsn).is_err());
+
+        let role_def = RoleDefinition::new(
+            2,
+            hashset!(
+                PrivateKey::from_pkcs8(ED25519_1_PK8).unwrap().key_id().clone(),
+                PrivateKey::from_pkcs8(ED25519_2_PK8).unwrap().key_id().clone(),
+            ),
+        ).unwrap();
+
+        let mut jsn = json::to_value(&role_def).unwrap();
+        set_threshold(&mut jsn, 3);
+        assert!(json::from_value::<RoleDefinition>(jsn).is_err());
+    }
+
+    // Refuse to deserialilze root metadata with wrong type field
+    #[test]
+    fn deserialize_json_root_bad_type() {
+        let mut root = make_root();
+        let _ = root.as_object_mut().unwrap().insert(
+            "type".into(),
+            json!("snapshot"),
+        );
+        assert!(json::from_value::<RootMetadata>(root).is_err());
+    }
+
+    // Refuse to deserialize role definitions with duplicated key ids
+    #[test]
+    fn deserialize_json_role_definition_duplicate_key_ids() {
+        let key_id = PrivateKey::from_pkcs8(ED25519_1_PK8)
+            .unwrap()
+            .key_id()
+            .clone();
+        let role_def = RoleDefinition::new(1, hashset!(key_id.clone())).unwrap();
+        let mut jsn = json::to_value(&role_def).unwrap();
+
+        match jsn.as_object_mut() {
+            Some(obj) => {
+                match obj.get_mut("key_ids").unwrap().as_array_mut() {
+                    Some(arr) => arr.push(json!(key_id)),
+                    None => panic!(),
+                }
+            }
+            None => panic!(),
+        }
+
+        assert!(json::from_value::<RoleDefinition>(jsn).is_err());
+    }
+
+    // Refuse to deserialize snapshot metadata with illegal versions
+    #[test]
+    fn deserialize_json_snapshot_illegal_version() {
+        let mut snapshot = make_snapshot();
+        set_version(&mut snapshot, 0);
+        assert!(json::from_value::<SnapshotMetadata>(snapshot).is_err());
+
+        let mut snapshot = make_snapshot();
+        set_version(&mut snapshot, -1);
+        assert!(json::from_value::<SnapshotMetadata>(snapshot).is_err());
+    }
+
+    // Refuse to deserialilze snapshot metadata with wrong type field
+    #[test]
+    fn deserialize_json_snapshot_bad_type() {
+        let mut snapshot = make_snapshot();
+        let _ = snapshot.as_object_mut().unwrap().insert(
+            "type".into(),
+            json!("root"),
+        );
+        assert!(json::from_value::<SnapshotMetadata>(snapshot).is_err());
+    }
+
+    // Refuse to deserialize timestamp metadata with illegal versions
+    #[test]
+    fn deserialize_json_timestamp_illegal_version() {
+        let mut timestamp = make_timestamp();
+        set_version(&mut timestamp, 0);
+        assert!(json::from_value::<TimestampMetadata>(timestamp).is_err());
+
+        let mut timestamp = make_timestamp();
+        set_version(&mut timestamp, -1);
+        assert!(json::from_value::<TimestampMetadata>(timestamp).is_err());
+    }
+
+    // Refuse to deserialilze timestamp metadata with wrong type field
+    #[test]
+    fn deserialize_json_timestamp_bad_type() {
+        let mut timestamp = make_timestamp();
+        let _ = timestamp.as_object_mut().unwrap().insert(
+            "type".into(),
+            json!("root"),
+        );
+        assert!(json::from_value::<TimestampMetadata>(timestamp).is_err());
+    }
+
+    // Refuse to deserialize targets metadata with illegal versions
+    #[test]
+    fn deserialize_json_targets_illegal_version() {
+        let mut targets = make_targets();
+        set_version(&mut targets, 0);
+        assert!(json::from_value::<TargetsMetadata>(targets).is_err());
+
+        let mut targets = make_targets();
+        set_version(&mut targets, -1);
+        assert!(json::from_value::<TargetsMetadata>(targets).is_err());
+    }
+
+    // Refuse to deserialilze targets metadata with wrong type field
+    #[test]
+    fn deserialize_json_targets_bad_type() {
+        let mut targets = make_targets();
+        let _ = targets.as_object_mut().unwrap().insert(
+            "type".into(),
+            json!("root"),
+        );
+        assert!(json::from_value::<TargetsMetadata>(targets).is_err());
+    }
+
+    // Refuse to deserialize delegations with no keys
+    #[test]
+    fn deserialize_json_delegations_no_keys() {
+        let mut delegations = make_delegations();
+        delegations
+            .as_object_mut()
+            .unwrap()
+            .get_mut("keys".into())
+            .unwrap()
+            .as_object_mut()
+            .unwrap()
+            .clear();
+        assert!(json::from_value::<Delegations>(delegations).is_err());
+    }
+
+    // Refuse to deserialize delegations with no roles
+    #[test]
+    fn deserialize_json_delegations_no_roles() {
+        let mut delegations = make_delegations();
+        delegations
+            .as_object_mut()
+            .unwrap()
+            .get_mut("roles".into())
+            .unwrap()
+            .as_array_mut()
+            .unwrap()
+            .clear();
+        assert!(json::from_value::<Delegations>(delegations).is_err());
+    }
+
+    // Refuse to deserialize delegations with duplicated roles
+    #[test]
+    fn deserialize_json_delegations_duplicated_roles() {
+        let mut delegations = make_delegations();
+        let dupe = delegations
+            .as_object()
+            .unwrap()
+            .get("roles".into())
+            .unwrap()
+            .as_array()
+            .unwrap()
+            [0]
+            .clone();
+        delegations
+            .as_object_mut()
+            .unwrap()
+            .get_mut("roles".into())
+            .unwrap()
+            .as_array_mut()
+            .unwrap()
+            .push(dupe);
+        assert!(json::from_value::<Delegations>(delegations).is_err());
+    }
+
+    // Refuse to deserialize a delegation with insufficient threshold
+    #[test]
+    fn deserialize_json_delegation_bad_threshold() {
+        let mut delegation = make_delegation();
+        set_threshold(&mut delegation, 0);
+        assert!(json::from_value::<Delegation>(delegation).is_err());
+
+        let mut delegation = make_delegation();
+        set_threshold(&mut delegation, 2);
+        assert!(json::from_value::<Delegation>(delegation).is_err());
+    }
+
+    // Refuse to deserialize a delegation with duplicate key IDs
+    #[test]
+    fn deserialize_json_delegation_duplicate_key_ids() {
+        let mut delegation = make_delegation();
+        let dupe = delegation
+            .as_object()
+            .unwrap()
+            .get("key_ids".into())
+            .unwrap()
+            .as_array()
+            .unwrap()
+            [0]
+            .clone();
+        delegation
+            .as_object_mut()
+            .unwrap()
+            .get_mut("key_ids".into())
+            .unwrap()
+            .as_array_mut()
+            .unwrap()
+            .push(dupe);
+        assert!(json::from_value::<Delegation>(delegation).is_err());
+    }
+
+    // Refuse to deserialize a delegation with duplicate paths
+    #[test]
+    fn deserialize_json_delegation_duplicate_paths() {
+        let mut delegation = make_delegation();
+        let dupe = delegation
+            .as_object()
+            .unwrap()
+            .get("paths".into())
+            .unwrap()
+            .as_array()
+            .unwrap()
+            [0]
+            .clone();
+        delegation
+            .as_object_mut()
+            .unwrap()
+            .get_mut("paths".into())
+            .unwrap()
+            .as_array_mut()
+            .unwrap()
+            .push(dupe);
+        assert!(json::from_value::<Delegation>(delegation).is_err());
+    }
 }
diff --git a/src/shims.rs b/src/shims.rs
index 7f8d2cc..6a9541d 100644
--- a/src/shims.rs
+++ b/src/shims.rs
@@ -48,12 +48,12 @@
         let mut keys = Vec::new();
         for (key_id, value) in self.keys.drain() {
             if &key_id != value.key_id() {
-                warn!(
+                return Err(Error::Encoding(format!(
                     "Received key with ID {:?} but calculated it's value as {:?}. \
                        Refusing to add it to the set of trusted keys.",
                     key_id,
                     value.key_id()
-                );
+                )));
             } else {
                 debug!(
                     "Found key with good ID {:?}. Adding it to the set of trusted keys.",
@@ -140,7 +140,7 @@
     typ: metadata::Role,
     version: u32,
     expires: DateTime<Utc>,
-    meta: HashMap<metadata::MetadataPath, metadata::MetadataDescription>,
+    snapshot: metadata::MetadataDescription,
 }
 
 impl TimestampMetadata {
@@ -149,7 +149,7 @@
             typ: metadata::Role::Timestamp,
             version: metadata.version(),
             expires: metadata.expires().clone(),
-            meta: metadata.meta().clone(),
+            snapshot: metadata.snapshot().clone(),
         })
     }
 
@@ -161,7 +161,7 @@
             )));
         }
 
-        metadata::TimestampMetadata::new(self.version, self.expires, self.meta)
+        metadata::TimestampMetadata::new(self.version, self.expires, self.snapshot)
     }
 }
 
diff --git a/src/tuf.rs b/src/tuf.rs
index 36793e9..f3af563 100644
--- a/src/tuf.rs
+++ b/src/tuf.rs
@@ -161,24 +161,15 @@
         let snapshot = {
             let root = self.safe_root_ref()?;
             let timestamp = self.safe_timestamp_ref()?;
-            let snapshot_description = timestamp
-                .meta()
-                .get(&MetadataPath::from_role(&Role::Snapshot))
-                .ok_or_else(|| {
-                    Error::VerificationFailure(
-                        "Timestamp metadata had no description of the snapshot metadata".into(),
-                    )
-                })?;
-
             let current_version = self.snapshot.as_ref().map(|t| t.version()).unwrap_or(0);
 
-            if snapshot_description.version() < current_version {
+            if timestamp.snapshot().version() < current_version {
                 return Err(Error::VerificationFailure(format!(
                     "Attempted to roll back snapshot metadata at version {} to {}.",
                     current_version,
-                    snapshot_description.version()
+                    timestamp.snapshot().version()
                 )));
-            } else if snapshot_description.version() == current_version {
+            } else if timestamp.snapshot().version() == current_version {
                 return Ok(false);
             }
 
@@ -190,18 +181,17 @@
 
             let snapshot: SnapshotMetadata = D::deserialize(&signed_snapshot.signed())?;
 
-            if snapshot.version() != snapshot_description.version() {
+            if snapshot.version() != timestamp.snapshot().version() {
                 return Err(Error::VerificationFailure(format!(
                     "The timestamp metadata reported that the snapshot metadata should be at \
                     version {} but version {} was found instead.",
-                    snapshot_description.version(),
+                    timestamp.snapshot().version(),
                     snapshot.version()
                 )));
             }
 
-            if snapshot.expires() <= &Utc::now() {
-                return Err(Error::ExpiredMetadata(Role::Snapshot));
-            }
+            // 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
         };
@@ -509,3 +499,494 @@
         }
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use chrono::prelude::*;
+    use crypto::{PrivateKey, SignatureScheme, HashAlgorithm};
+    use interchange::JsonDataInterchange;
+    use metadata::{RoleDefinition, MetadataDescription};
+
+    lazy_static! {
+        static ref KEYS: Vec<PrivateKey> = {
+            let keys: &[&[u8]] = &[
+                include_bytes!("../tests/ed25519/ed25519-1.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-2.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-3.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-4.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-5.pk8.der"),
+                include_bytes!("../tests/ed25519/ed25519-6.pk8.der"),
+            ];
+            keys.iter().map(|b| PrivateKey::from_pkcs8(b).unwrap()).collect()
+        };
+    }
+
+    #[test]
+    fn root_pinned_success() {
+        let root_key = &KEYS[0];
+        let root = RootMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            false,
+            vec![KEYS[0].public().clone()],
+            RoleDefinition::new(1, hashset!(root_key.key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+        ).unwrap();
+        let root: SignedMetadata<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &root_key, SignatureScheme::Ed25519).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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[1], SignatureScheme::Ed25519).unwrap();
+
+        // add the original key's signature to make it cross signed
+        root.add_signature(&KEYS[0], SignatureScheme::Ed25519)
+            .unwrap();
+
+        assert_eq!(tuf.update_root(root.clone()), Ok(true));
+
+        // second update should do nothing
+        assert_eq!(tuf.update_root(root), Ok(false));
+    }
+
+    #[test]
+    fn no_cross_sign_root_rotation() {
+        let root = RootMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            false,
+            vec![KEYS[0].public().clone()],
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+        ).unwrap();
+        let root: SignedMetadata<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[1], SignatureScheme::Ed25519).unwrap();
+
+        assert!(tuf.update_root(root).is_err());
+    }
+
+    #[test]
+    fn good_timestamp_update() {
+        let root = RootMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            false,
+            vec![KEYS[0].public().clone(), KEYS[1].public().clone()],
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+        ).unwrap();
+        let root: SignedMetadata<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, TimestampMetadata> =
+            SignedMetadata::new(&timestamp, &KEYS[1], SignatureScheme::Ed25519).unwrap();
+
+        assert_eq!(tuf.update_timestamp(timestamp.clone()), Ok(true));
+
+        // second update should do nothing
+        assert_eq!(tuf.update_timestamp(timestamp), Ok(false))
+    }
+
+    #[test]
+    fn bad_timestamp_update_wrong_key() {
+        let root = RootMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            false,
+            vec![KEYS[0].public().clone(), KEYS[1].public().clone()],
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+        ).unwrap();
+        let root: SignedMetadata<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).unwrap();
+
+        let mut tuf = Tuf::from_root(root).unwrap();
+
+        let timestamp = TimestampMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256])
+                .unwrap(),
+        ).unwrap();
+
+        // sign it with the root key
+        let timestamp: SignedMetadata<JsonDataInterchange, TimestampMetadata> =
+            SignedMetadata::new(&timestamp, &KEYS[0], SignatureScheme::Ed25519).unwrap();
+
+        assert!(tuf.update_timestamp(timestamp).is_err())
+    }
+
+    #[test]
+    fn good_snapshot_update() {
+        let root = RootMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            false,
+            vec![
+                KEYS[0].public().clone(),
+                KEYS[1].public().clone(),
+                KEYS[2].public().clone(),
+            ],
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
+        ).unwrap();
+        let root: SignedMetadata<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, TimestampMetadata> =
+            SignedMetadata::new(&timestamp, &KEYS[2], SignatureScheme::Ed25519).unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
+
+        let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!())
+            .unwrap();
+        let snapshot: SignedMetadata<JsonDataInterchange, SnapshotMetadata> =
+            SignedMetadata::new(&snapshot, &KEYS[1], SignatureScheme::Ed25519).unwrap();
+
+        assert_eq!(tuf.update_snapshot(snapshot.clone()), Ok(true));
+
+        // second update should do nothing
+        assert_eq!(tuf.update_snapshot(snapshot), Ok(false));
+    }
+
+    #[test]
+    fn bad_snapshot_update_wrong_key() {
+        let root = RootMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            false,
+            vec![
+                KEYS[0].public().clone(),
+                KEYS[1].public().clone(),
+                KEYS[2].public().clone(),
+            ],
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
+        ).unwrap();
+        let root: SignedMetadata<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, TimestampMetadata> =
+            SignedMetadata::new(&timestamp, &KEYS[2], SignatureScheme::Ed25519).unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
+
+        let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!())
+            .unwrap();
+        let snapshot: SignedMetadata<JsonDataInterchange, SnapshotMetadata> =
+            SignedMetadata::new(&snapshot, &KEYS[2], SignatureScheme::Ed25519).unwrap();
+
+        assert!(tuf.update_snapshot(snapshot.clone()).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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, TimestampMetadata> =
+            SignedMetadata::new(&timestamp, &KEYS[2], SignatureScheme::Ed25519).unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
+
+        let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!())
+            .unwrap();
+        let snapshot: SignedMetadata<JsonDataInterchange, SnapshotMetadata> =
+            SignedMetadata::new(&snapshot, &KEYS[1], SignatureScheme::Ed25519).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<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, TimestampMetadata> =
+            SignedMetadata::new(&timestamp, &KEYS[3], SignatureScheme::Ed25519).unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
+
+        let meta_map = hashmap!(
+            MetadataPath::from_role(&Role::Targets) =>
+                MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        );
+        let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)
+            .unwrap();
+        let snapshot: SignedMetadata<JsonDataInterchange, SnapshotMetadata> =
+            SignedMetadata::new(&snapshot, &KEYS[1], SignatureScheme::Ed25519).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<JsonDataInterchange, TargetsMetadata> =
+            SignedMetadata::new(&targets, &KEYS[2], SignatureScheme::Ed25519).unwrap();
+
+        assert_eq!(tuf.update_targets(targets.clone()), Ok(true));
+
+        // second update should do nothing
+        assert_eq!(tuf.update_targets(targets), Ok(false));
+    }
+
+    #[test]
+    fn bad_targets_update_wrong_key() {
+        let root = RootMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            false,
+            vec![
+                KEYS[0].public().clone(),
+                KEYS[1].public().clone(),
+                KEYS[2].public().clone(),
+                KEYS[3].public().clone(),
+            ],
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[3].key_id().clone())).unwrap(),
+        ).unwrap();
+        let root: SignedMetadata<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, TimestampMetadata> =
+            SignedMetadata::new(&timestamp, &KEYS[3], SignatureScheme::Ed25519).unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
+
+        let meta_map = hashmap!(
+            MetadataPath::from_role(&Role::Targets) =>
+                MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        );
+        let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)
+            .unwrap();
+        let snapshot: SignedMetadata<JsonDataInterchange, SnapshotMetadata> =
+            SignedMetadata::new(&snapshot, &KEYS[1], SignatureScheme::Ed25519).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<JsonDataInterchange, TargetsMetadata> =
+            SignedMetadata::new(&targets, &KEYS[3], SignatureScheme::Ed25519).unwrap();
+
+        assert!(tuf.update_targets(targets).is_err());
+    }
+
+    #[test]
+    fn bad_targets_update_wrong_version() {
+        let root = RootMetadata::new(
+            1,
+            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+            false,
+            vec![
+                KEYS[0].public().clone(),
+                KEYS[1].public().clone(),
+                KEYS[2].public().clone(),
+                KEYS[3].public().clone(),
+            ],
+            RoleDefinition::new(1, hashset!(KEYS[0].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[1].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[2].key_id().clone())).unwrap(),
+            RoleDefinition::new(1, hashset!(KEYS[3].key_id().clone())).unwrap(),
+        ).unwrap();
+        let root: SignedMetadata<JsonDataInterchange, RootMetadata> =
+            SignedMetadata::new(&root, &KEYS[0], SignatureScheme::Ed25519).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<JsonDataInterchange, TimestampMetadata> =
+            SignedMetadata::new(&timestamp, &KEYS[3], SignatureScheme::Ed25519).unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
+
+        let meta_map = hashmap!(
+            MetadataPath::from_role(&Role::Targets) =>
+                MetadataDescription::from_reader(&*vec![], 2, &[HashAlgorithm::Sha256]).unwrap(),
+        );
+        let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)
+            .unwrap();
+        let snapshot: SignedMetadata<JsonDataInterchange, SnapshotMetadata> =
+            SignedMetadata::new(&snapshot, &KEYS[1], SignatureScheme::Ed25519).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<JsonDataInterchange, TargetsMetadata> =
+            SignedMetadata::new(&targets, &KEYS[2], SignatureScheme::Ed25519).unwrap();
+
+        assert!(tuf.update_targets(targets).is_err());
+    }
+}
diff --git a/tests/integration.rs b/tests/integration.rs
index b7ae23d..1871162 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -1,9 +1,11 @@
 extern crate chrono;
+#[macro_use]
+extern crate maplit;
 extern crate tuf;
 
 use chrono::prelude::*;
 use chrono::offset::Utc;
-use std::collections::{HashSet, HashMap};
+use std::collections::HashMap;
 use tuf::Tuf;
 use tuf::crypto::{PrivateKey, SignatureScheme, HashAlgorithm};
 use tuf::interchange::JsonDataInterchange;
@@ -34,21 +36,10 @@
         timestamp_key.public().clone(),
     ];
 
-    let mut key_ids = HashSet::new();
-    key_ids.insert(root_key.key_id().clone());
-    let root_def = RoleDefinition::new(1, key_ids).unwrap();
-
-    let mut key_ids = HashSet::new();
-    key_ids.insert(snapshot_key.key_id().clone());
-    let snapshot_def = RoleDefinition::new(1, key_ids).unwrap();
-
-    let mut key_ids = HashSet::new();
-    key_ids.insert(targets_key.key_id().clone());
-    let targets_def = RoleDefinition::new(1, key_ids).unwrap();
-
-    let mut key_ids = HashSet::new();
-    key_ids.insert(timestamp_key.key_id().clone());
-    let timestamp_def = RoleDefinition::new(1, key_ids).unwrap();
+    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,
@@ -71,12 +62,8 @@
         Tuf::<JsonDataInterchange>::from_root_pinned(signed, &[root_key.key_id().clone()]).unwrap();
 
     //// build the timestamp ////
-    let mut meta_map = HashMap::new();
-    let path = MetadataPath::new("snapshot".into()).unwrap();
-    let desc = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let _ = meta_map.insert(path, desc);
-    let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)
-        .unwrap();
+    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::<JsonDataInterchange, TimestampMetadata>::new(
         &timestamp,
@@ -87,13 +74,13 @@
     tuf.update_timestamp(signed).unwrap();
 
     //// build the snapshot ////
-    let mut meta_map = HashMap::new();
-    let path = MetadataPath::new("targets".into()).unwrap();
-    let desc = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let _ = meta_map.insert(path, desc);
-    let path = MetadataPath::new("delegation".into()).unwrap();
-    let desc = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let _ = meta_map.insert(path, desc);
+    let meta_map =
+        hashmap! {
+        MetadataPath::new("targets".into()).unwrap() =>
+            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        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();
 
@@ -141,12 +128,11 @@
 
     //// build the delegation ////
     let target_file: &[u8] = b"bar";
-    let target_path = TargetPath::new("foo".into()).unwrap();
-    let target_description = TargetDescription::from_reader(target_file, &[HashAlgorithm::Sha256])
-        .unwrap();
-
-    let mut target_map = HashMap::new();
-    let _ = target_map.insert(target_path, target_description);
+    let target_map =
+        hashmap! {
+        TargetPath::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();
 
@@ -182,21 +168,10 @@
         timestamp_key.public().clone(),
     ];
 
-    let mut key_ids = HashSet::new();
-    key_ids.insert(root_key.key_id().clone());
-    let root_def = RoleDefinition::new(1, key_ids).unwrap();
-
-    let mut key_ids = HashSet::new();
-    key_ids.insert(snapshot_key.key_id().clone());
-    let snapshot_def = RoleDefinition::new(1, key_ids).unwrap();
-
-    let mut key_ids = HashSet::new();
-    key_ids.insert(targets_key.key_id().clone());
-    let targets_def = RoleDefinition::new(1, key_ids).unwrap();
-
-    let mut key_ids = HashSet::new();
-    key_ids.insert(timestamp_key.key_id().clone());
-    let timestamp_def = RoleDefinition::new(1, key_ids).unwrap();
+    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,
@@ -219,12 +194,8 @@
         Tuf::<JsonDataInterchange>::from_root_pinned(signed, &[root_key.key_id().clone()]).unwrap();
 
     //// build the timestamp ////
-    let mut meta_map = HashMap::new();
-    let path = MetadataPath::new("snapshot".into()).unwrap();
-    let desc = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let _ = meta_map.insert(path, desc);
-    let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)
-        .unwrap();
+    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::<JsonDataInterchange, TimestampMetadata>::new(
         &timestamp,
@@ -235,16 +206,15 @@
     tuf.update_timestamp(signed).unwrap();
 
     //// build the snapshot ////
-    let mut meta_map = HashMap::new();
-    let path = MetadataPath::new("targets".into()).unwrap();
-    let desc = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let _ = meta_map.insert(path, desc);
-    let path = MetadataPath::new("delegation-a".into()).unwrap();
-    let desc = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let _ = meta_map.insert(path, desc);
-    let path = MetadataPath::new("delegation-b".into()).unwrap();
-    let desc = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let _ = meta_map.insert(path, desc);
+    let meta_map =
+        hashmap! {
+        MetadataPath::new("targets".into()).unwrap() =>
+            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        MetadataPath::new("delegation-a".into()).unwrap() =>
+            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        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();
 
@@ -327,12 +297,11 @@
 
     //// build delegation B ////
     let target_file: &[u8] = b"bar";
-    let target_path = TargetPath::new("foo".into()).unwrap();
-    let target_description = TargetDescription::from_reader(target_file, &[HashAlgorithm::Sha256])
-        .unwrap();
-
-    let mut target_map = HashMap::new();
-    let _ = target_map.insert(target_path, target_description);
+    let target_map =
+        hashmap! {
+        TargetPath::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();
diff --git a/tests/simple_example.rs b/tests/simple_example.rs
index b9cac7b..0dd3172 100644
--- a/tests/simple_example.rs
+++ b/tests/simple_example.rs
@@ -167,12 +167,8 @@
         JsonDataInterchange::canonicalize(&JsonDataInterchange::serialize(&signed)?)?;
 
     //// build the timestamp ////
-    let meta_map =
-        hashmap! {
-        MetadataPath::new("snapshot".into())? =>
-            MetadataDescription::from_reader(&*snapshot_bytes, 1, &[HashAlgorithm::Sha256])?,
-    };
-    let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)?;
+    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::<JsonDataInterchange, TimestampMetadata>::new(
         &timestamp,