Merge branch 'develop'
diff --git a/Cargo.toml b/Cargo.toml
index d1ffee2..1a0bbb9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "tuf"
-version = "0.1.5"
+version = "0.1.6"
 authors = [ "heartsucker <heartsucker@autistici.org>" ]
 description = "Library for The Update Framework (TUF)"
 homepage = "https://github.com/heartsucker/rust-tuf"
@@ -37,6 +37,7 @@
 hyper = "0.10.10"
 itoa = "0.3"
 log = "0.3"
+pem = "0.4"
 ring = "0.9.4"
 serde = "1"
 serde_derive = "1"
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..09f45e0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+.PHONY: help dev-docs
+.DEFAULT_GOAL := help
+
+dev-docs: ## Generate the documentation for all modules (dev friendly)
+	@cargo rustdoc --all-features -- --no-defaults --passes "collapse-docs" --passes "unindent-comments"
+
+help: ## Print this message
+	@awk 'BEGIN {FS = ":.*?## "} /^[0-9a-zA-Z_-]+:.*?## / {printf "\033[36m%16s\033[0m : %s\n", $$1, $$2}' $(MAKEFILE_LIST)
diff --git a/README.md b/README.md
index 4d6bf1a..79f836b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # rust-tuf
 
-[![Travis build Status](https://travis-ci.org/heartsucker/rust-tuf.svg?branch=master)](https://travis-ci.org/heartsucker/rust-tuf) [![Appveyor build status](https://ci.appveyor.com/api/projects/status/kfyvpkdvn5ap7dqc/branch/master?svg=true)](https://ci.appveyor.com/project/heartsucker/rust-tuf/branch/master)[![codecov](https://codecov.io/gh/heartsucker/rust-tuf/branch/master/graph/badge.svg)](https://codecov.io/gh/heartsucker/rust-tuf)
+[![Travis build Status](https://travis-ci.org/heartsucker/rust-tuf.svg?branch=master)](https://travis-ci.org/heartsucker/rust-tuf) [![Appveyor build status](https://ci.appveyor.com/api/projects/status/kfyvpkdvn5ap7dqc/branch/master?svg=true)](https://ci.appveyor.com/project/heartsucker/rust-tuf/branch/master) [![codecov](https://codecov.io/gh/heartsucker/rust-tuf/branch/master/graph/badge.svg)](https://codecov.io/gh/heartsucker/rust-tuf)
 
 A Rust implementation of [The Update Framework (TUF)](https://theupdateframework.github.io/).
 
diff --git a/src/cjson.rs b/src/cjson.rs
index 470ed70..b624980 100644
--- a/src/cjson.rs
+++ b/src/cjson.rs
@@ -3,7 +3,7 @@
 use std::collections::BTreeMap;
 use std::io;
 
-pub fn canonicalize(jsn: json::Value) -> Result<Vec<u8>, String> {
+pub fn canonicalize(jsn: &json::Value) -> Result<Vec<u8>, String> {
     let converted = convert(jsn)?;
     let mut buf = Vec::new();
     let _ = converted.write(&mut buf); // Vec<u8> impl always succeeds (or panics).
@@ -74,32 +74,32 @@
     U64(u64),
 }
 
-fn convert(jsn: json::Value) -> Result<Value, String> {
+fn convert(jsn: &json::Value) -> Result<Value, String> {
     match jsn {
-        json::Value::Null => Ok(Value::Null),
-        json::Value::Bool(b) => Ok(Value::Bool(b)),
-        json::Value::Number(n) => {
+        &json::Value::Null => Ok(Value::Null),
+        &json::Value::Bool(b) => Ok(Value::Bool(b)),
+        &json::Value::Number(ref n) => {
             n.as_i64()
                 .map(Number::I64)
                 .or(n.as_u64().map(Number::U64))
                 .map(Value::Number)
                 .ok_or_else(|| String::from("only i64 and u64 are supported"))
         }
-        json::Value::Array(arr) => {
+        &json::Value::Array(ref arr) => {
             let mut out = Vec::new();
-            for res in arr.iter().cloned().map(|v| convert(v)) {
+            for res in arr.iter().map(|v| convert(v)) {
                 out.push(res?)
             }
             Ok(Value::Array(out))
         }
-        json::Value::Object(obj) => {
+        &json::Value::Object(ref obj) => {
             let mut out = BTreeMap::new();
             for (k, v) in obj.iter() {
-                let _ = out.insert(k.clone(), convert(v.clone())?);
+                let _ = out.insert(k.clone(), convert(v)?);
             }
             Ok(Value::Object(out))
         }
-        json::Value::String(s) => Ok(Value::String(s)),
+        &json::Value::String(ref s) => Ok(Value::String(s.clone())),
     }
 }
 
@@ -202,10 +202,17 @@
     #[test]
     fn root_json() {
         let mut file = File::open("./tests/cjson/root.json").expect("couldn't open root.json");
-        let mut buf = Vec::new();
-        file.read_to_end(&mut buf).expect("couldn't read root.json");
-        let jsn = json::from_slice(&buf).expect("not json");
+        let mut buf = String::new();
+        file.read_to_string(&mut buf).expect("couldn't read root.json");
+
+        let mut file = File::open("./tests/cjson/root.cjson").expect("couldn't open root.cjson");
+        let mut cjsn = String::new();
+        file.read_to_string(&mut cjsn).expect("couldn't read root.cjson");
+
+        let ref jsn = json::from_str(&buf).expect("not json");
         let out = canonicalize(jsn).expect("couldn't canonicalize");
-        assert_eq!(out, buf);
+        let out = ::std::str::from_utf8(&out).expect("not utf-8");
+
+        assert_eq!(out, cjsn);
     }
 }
diff --git a/src/error.rs b/src/error.rs
index f21873a..f2d0518 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -8,6 +8,8 @@
 /// Error type for all TUF related errors.
 #[derive(Debug, PartialEq, Eq)]
 pub enum Error {
+    /// ASN.1 parse errors.
+    Asn1,
     /// Errors for converting JSON to canonical JSON.
     CanonicalJsonError(String),
     /// The metadata for the given role has expired.
diff --git a/src/lib.rs b/src/lib.rs
index 6b453cc..292ee33 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -143,11 +143,11 @@
 extern crate itoa;
 #[macro_use]
 extern crate log;
+extern crate pem;
 extern crate ring;
 extern crate serde;
 #[macro_use]
 extern crate serde_derive;
-#[macro_use]
 extern crate serde_json as json;
 extern crate url;
 extern crate untrusted;
@@ -157,9 +157,10 @@
 mod util;
 
 mod cjson;
+mod error;
 mod http;
 mod metadata;
-mod error;
+mod rsa;
 mod tuf;
 
 pub use tuf::*;
diff --git a/src/metadata.rs b/src/metadata.rs
index e90a983..7d77a93 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -1,9 +1,10 @@
 use chrono::{DateTime, UTC};
 use data_encoding::HEXLOWER;
 use json;
+use pem;
 use ring;
 use ring::digest::{digest, SHA256};
-use ring::signature::ED25519;
+use ring::signature::{ED25519, RSA_PSS_2048_8192_SHA256, RSA_PSS_2048_8192_SHA512};
 use serde::de::{Deserialize, DeserializeOwned, Deserializer, Error as DeserializeError};
 use std::collections::HashMap;
 use std::fmt::{self, Display, Formatter, Debug};
@@ -13,6 +14,7 @@
 
 use cjson::canonicalize;
 use error::Error;
+use rsa::convert_to_pkcs1;
 
 static HASH_PREFERENCES: &'static [HashType] = &[HashType::Sha512, HashType::Sha256];
 
@@ -51,11 +53,11 @@
     }
 }
 
-pub trait RoleType: Debug {
+pub trait RoleType: Debug + Clone{
     fn matches(role: &Role) -> bool;
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Root {}
 impl RoleType for Root {
     fn matches(role: &Role) -> bool {
@@ -66,7 +68,7 @@
     }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Targets {}
 impl RoleType for Targets {
     fn matches(role: &Role) -> bool {
@@ -77,7 +79,7 @@
     }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Timestamp {}
 impl RoleType for Timestamp {
     fn matches(role: &Role) -> bool {
@@ -88,7 +90,7 @@
     }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Snapshot {}
 impl RoleType for Snapshot {
     fn matches(role: &Role) -> bool {
@@ -99,8 +101,8 @@
     }
 }
 
-#[derive(Debug)]
-pub struct SignedMetadata<R: RoleType> {
+#[derive(Debug, Clone)]
+pub struct SignedMetadata<R: RoleType + Clone> {
     pub signatures: Vec<Signature>,
     pub signed: json::Value,
     _role: PhantomData<R>,
@@ -498,6 +500,8 @@
 pub enum KeyType {
     /// [Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519) signature scheme.
     Ed25519,
+    /// [RSA](https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29)
+    Rsa,
     /// Internal representation of an unsupported key type.
     Unsupported(String),
 }
@@ -506,6 +510,8 @@
     fn supports(&self, scheme: &SignatureScheme) -> bool {
         match (self, scheme) {
             (&KeyType::Ed25519, &SignatureScheme::Ed25519) => true,
+            (&KeyType::Rsa, &SignatureScheme::RsaSsaPssSha256) => true,
+            (&KeyType::Rsa, &SignatureScheme::RsaSsaPssSha512) => true,
             _ => false,
         }
     }
@@ -517,6 +523,7 @@
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
             "ed25519" => Ok(KeyType::Ed25519),
+            "rsa" => Ok(KeyType::Rsa),
             typ => Ok(KeyType::Unsupported(typ.into())),
         }
     }
@@ -535,15 +542,25 @@
 
 /// The raw bytes of a public key.
 #[derive(Clone, PartialEq, Debug)]
-pub struct KeyValue(pub Vec<u8>);
+pub struct KeyValue {
+    /// The key's raw bytes.
+    pub value: Vec<u8>,
+    /// The key's original value, needed for ID calculation
+    pub original: String,
+    /// The key's type,
+    pub typ: KeyType,
+}
 
 impl KeyValue {
     /// Calculates the `KeyId` of the public key.
     pub fn key_id(&self) -> KeyId {
-        // TODO this only works because we're using ed25519
-        // and this will break when we add RSA.
-        let key_value_hex = canonicalize(json!(HEXLOWER.encode(&self.0))).unwrap(); // TODO unwrap
-        KeyId(HEXLOWER.encode(digest(&SHA256, &key_value_hex).as_ref()))
+        match self.typ {
+            KeyType::Unsupported(_) => KeyId(String::from("error")), // TODO this feels wrong, but we check this everywhere else
+            _ => {
+                let key_value = canonicalize(&json::Value::String(self.original.clone())).unwrap(); // TODO unwrap
+                KeyId(HEXLOWER.encode(digest(&SHA256, &key_value).as_ref()))
+            }
+        }
     }
 }
 
@@ -551,12 +568,32 @@
     fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
         match Deserialize::deserialize(de)? {
             json::Value::String(ref s) => {
-                // TODO this is shit because we can't tell what type of key it is
-                // e.g., ed25519 => hex, rsa => PEM
-                // need to add this into the type/struct so it can be accessed here
-                HEXLOWER.decode(s.as_ref())
-                    .map(KeyValue)
-                    .map_err(|e| DeserializeError::custom(format!("Key value was not hex: {}", e)))
+                // TODO this is pretty shaky
+                if s.starts_with("-----") {
+                    pem::parse(s)
+                        .map(|p| {
+                            KeyValue {
+                                value: p.contents,
+                                original: s.clone(),
+                                typ: KeyType::Rsa,
+                            }
+                        })
+                        .map_err(|e| {
+                            DeserializeError::custom(format!("Key was not PEM encoded: {}", e))
+                        })
+                } else {
+                    HEXLOWER.decode(s.as_ref())
+                        .map(|v| {
+                            KeyValue {
+                                value: v,
+                                original: s.clone(),
+                                typ: KeyType::Ed25519,
+                            }
+                        })
+                        .map_err(|e| {
+                            DeserializeError::custom(format!("Key value was not hex: {}", e))
+                        })
+                }
             }
             json::Value::Object(mut object) => {
                 json::from_value::<KeyValue>(object.remove("public")
@@ -609,23 +646,25 @@
 #[derive(Clone, PartialEq, Debug)]
 pub enum SignatureScheme {
     Ed25519,
+    RsaSsaPssSha256,
+    RsaSsaPssSha512,
     Unsupported(String),
 }
 
 impl SignatureScheme {
     fn verify(&self, pub_key: &KeyValue, msg: &[u8], sig: &SignatureValue) -> Result<(), Error> {
-        match self {
-            &SignatureScheme::Ed25519 => {
-                ring::signature::verify(&ED25519,
-                                        Input::from(&pub_key.0),
-                                        Input::from(msg),
-                                        Input::from(&sig.0))
-                    .map_err(|_| Error::VerificationFailure("Bad signature".into()))
-            }
+        let alg: &ring::signature::VerificationAlgorithm = match self {
+            &SignatureScheme::Ed25519 => &ED25519,
+            &SignatureScheme::RsaSsaPssSha256 => &RSA_PSS_2048_8192_SHA256,
+            &SignatureScheme::RsaSsaPssSha512 => &RSA_PSS_2048_8192_SHA512,
             &SignatureScheme::Unsupported(ref s) => {
-                Err(Error::UnsupportedSignatureScheme(s.clone()))
+                return Err(Error::UnsupportedSignatureScheme(s.clone()));
             }
-        }
+        };
+
+        ring::signature::verify(alg, Input::from(&convert_to_pkcs1(&pub_key.value)),
+                                Input::from(msg), Input::from(&sig.0))
+            .map_err(|_| Error::VerificationFailure("Bad signature".into()))
     }
 }
 
@@ -635,6 +674,8 @@
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
             "ed25519" => Ok(SignatureScheme::Ed25519),
+            "rsassa-pss-sha256" => Ok(SignatureScheme::RsaSsaPssSha256),
+            "rsassa-pss-sha512" => Ok(SignatureScheme::RsaSsaPssSha512),
             typ => Ok(SignatureScheme::Unsupported(typ.into())),
         }
     }
diff --git a/src/rsa/der.rs b/src/rsa/der.rs
new file mode 100644
index 0000000..6b465a6
--- /dev/null
+++ b/src/rsa/der.rs
@@ -0,0 +1,338 @@
+// Copyright 2015 Brian Smith.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+//! Building blocks for parsing DER-encoded ASN.1 structures.
+//!
+//! This module contains the foundational parts of an ASN.1 DER parser.
+
+use ring;
+use std::io::{self, Write};
+use untrusted;
+
+pub const CONSTRUCTED: u8 = 1 << 5;
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+#[repr(u8)]
+pub enum Tag {
+    EOC = 0x00,
+    Integer = 0x02,
+    BitString = 0x03,
+    Null = 0x05,
+    OID = 0x06,
+    Sequence = CONSTRUCTED | 0x10, // 0x30
+}
+
+pub fn expect_tag_and_get_value<'a>(input: &mut untrusted::Reader<'a>,
+                                    tag: Tag)
+                                    -> Result<untrusted::Input<'a>, ring::error::Unspecified> {
+
+    let (actual_tag, inner) = read_tag_and_get_value(input)?;
+    if (tag as usize) != (actual_tag as usize) {
+        return Err(ring::error::Unspecified);
+    }
+    Ok(inner)
+}
+
+pub fn read_tag_and_get_value<'a>
+    (input: &mut untrusted::Reader<'a>)
+     -> Result<(u8, untrusted::Input<'a>), ring::error::Unspecified> {
+    let tag = input.read_byte()?;
+
+    if tag as usize == Tag::EOC as usize {
+        return Ok((tag, untrusted::Input::from(&[])))
+    }
+
+    if (tag & 0x1F) == 0x1F {
+        return Err(ring::error::Unspecified); // High tag number form is not allowed.
+    }
+
+    // If the high order bit of the first byte is set to zero then the length
+    // is encoded in the seven remaining bits of that byte. Otherwise, those
+    // seven bits represent the number of bytes used to encode the length.
+    let length = match input.read_byte()? {
+        n if (n & 0x80) == 0 => n as usize,
+        0x81 => {
+            let second_byte = input.read_byte()?;
+            if second_byte < 128 {
+                return Err(ring::error::Unspecified); // Not the canonical encoding.
+            }
+            second_byte as usize
+        }
+        0x82 => {
+            let second_byte = input.read_byte()? as usize;
+            let third_byte = input.read_byte()? as usize;
+            let combined = (second_byte << 8) | third_byte;
+
+            if combined < 256 {
+                return Err(ring::error::Unspecified); // Not the canonical encoding.
+            }
+            combined
+        }
+        _ => {
+            return Err(ring::error::Unspecified); // We don't support longer lengths.
+        }
+    };
+
+
+    let inner = input.skip_and_get_input(length)?;
+    Ok((tag, inner))
+}
+
+// TODO: investigate taking decoder as a reference to reduce generated code
+// size.
+pub fn nested<'a, F, R, E: Copy>(input: &mut untrusted::Reader<'a>,
+                                 tag: Tag,
+                                 error: E,
+                                 decoder: F)
+                                 -> Result<R, E>
+    where F: FnOnce(&mut untrusted::Reader<'a>) -> Result<R, E>
+{
+    let inner = expect_tag_and_get_value(input, tag).map_err(|_| error)?;
+    inner.read_all(error, decoder)
+}
+
+fn nonnegative_integer<'a>(input: &mut untrusted::Reader<'a>,
+                           min_value: u8)
+                           -> Result<untrusted::Input<'a>, ring::error::Unspecified> {
+    // Verify that |input|, which has had any leading zero stripped off, is the
+    // encoding of a value of at least |min_value|.
+    fn check_minimum(input: untrusted::Input,
+                     min_value: u8)
+                     -> Result<(), ring::error::Unspecified> {
+        input.read_all(ring::error::Unspecified, |input| {
+            let first_byte = input.read_byte()?;
+            if input.at_end() && first_byte < min_value {
+                return Err(ring::error::Unspecified);
+            }
+            let _ = input.skip_to_end();
+            Ok(())
+        })
+    }
+
+    let value = expect_tag_and_get_value(input, Tag::Integer)?;
+
+    value.read_all(ring::error::Unspecified, |input| {
+        // Empty encodings are not allowed.
+        let first_byte = input.read_byte()?;
+
+        if first_byte == 0 {
+            if input.at_end() {
+                // |value| is the legal encoding of zero.
+                if min_value > 0 {
+                    return Err(ring::error::Unspecified);
+                }
+                return Ok(value);
+            }
+
+            let r = input.skip_to_end();
+            r.read_all(ring::error::Unspecified, |input| {
+                let second_byte = input.read_byte()?;
+                if (second_byte & 0x80) == 0 {
+                    // A leading zero is only allowed when the value's high bit
+                    // is set.
+                    return Err(ring::error::Unspecified);
+                }
+                let _ = input.skip_to_end();
+                Ok(())
+            })?;
+            check_minimum(r, min_value)?;
+            return Ok(r);
+        }
+
+        // Negative values are not allowed.
+        if (first_byte & 0x80) != 0 {
+            return Err(ring::error::Unspecified);
+        }
+
+        let _ = input.skip_to_end();
+        check_minimum(value, min_value)?;
+        Ok(value)
+    })
+}
+
+/// Parses a positive DER integer, returning the big-endian-encoded value, sans
+/// any leading zero byte.
+#[inline]
+pub fn positive_integer<'a>(input: &mut untrusted::Reader<'a>)
+                            -> Result<untrusted::Input<'a>, ring::error::Unspecified> {
+    nonnegative_integer(input, 1)
+}
+
+
+pub struct Der<'a, W: Write + 'a> {
+    writer: &'a mut W,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct Error;
+
+impl From<ring::error::Unspecified> for Error {
+    fn from(_: ring::error::Unspecified) -> Error {
+        Error
+    }
+}
+
+impl From<io::Error> for Error {
+    fn from(_: io::Error) -> Error {
+        Error
+    }
+}
+
+
+impl<'a, W: Write> Der<'a, W> {
+    pub fn new(writer: &'a mut W) -> Self {
+        Der { writer: writer }
+    }
+
+    fn length_of_length(len: usize) -> u8 {
+        let mut i = len;
+        let mut num_bytes = 1;
+
+        while i > 255 {
+            num_bytes += 1;
+            i >>= 8;
+        }
+
+        num_bytes
+    }
+
+    fn write_len(&mut self, len: usize) -> Result<(), Error> {
+        if len >= 128 {
+            let n = Self::length_of_length(len);
+            self.writer.write_all(&[0x80 | n])?;
+
+            for i in (1..n + 1).rev() {
+                self.writer.write_all(&[(len >> ((i - 1) * 8)) as u8])?;
+            }
+        } else {
+            self.writer.write_all(&[len as u8])?;
+        }
+
+        Ok(())
+    }
+
+    pub fn write_integer(&mut self, input: untrusted::Input) -> Result<(), Error> {
+        self.writer.write_all(&[Tag::Integer as u8])?;
+        let mut buf = Vec::new();
+
+        input.read_all(Error, |read| {
+                while let Ok(byte) = read.read_byte() {
+                    buf.push(byte);
+                }
+
+                Ok(())
+            })?;
+
+        self.write_len(buf.len())?;
+
+        Ok(self.writer.write_all(&mut buf)?)
+    }
+
+    pub fn write_sequence<F: FnOnce(&mut Der<Vec<u8>>) -> Result<(), Error>>
+        (&mut self,
+         func: F)
+         -> Result<(), Error> {
+        self.writer.write_all(&[Tag::Sequence as u8])?;
+        let mut buf = Vec::new();
+
+        {
+            let mut inner = Der::new(&mut buf);
+            func(&mut inner)?;
+        }
+
+        self.write_len(buf.len())?;
+        Ok(self.writer.write_all(&buf)?)
+    }
+}
+
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use untrusted;
+
+    fn with_good_i<F, R>(value: &[u8], f: F)
+        where F: FnOnce(&mut untrusted::Reader) -> Result<R, ring::error::Unspecified>
+    {
+        let r = untrusted::Input::from(value).read_all(ring::error::Unspecified, f);
+        assert!(r.is_ok());
+    }
+
+    fn with_bad_i<F, R>(value: &[u8], f: F)
+        where F: FnOnce(&mut untrusted::Reader) -> Result<R, ring::error::Unspecified>
+    {
+        let r = untrusted::Input::from(value).read_all(ring::error::Unspecified, f);
+        assert!(r.is_err());
+    }
+
+    static ZERO_INTEGER: &'static [u8] = &[0x02, 0x01, 0x00];
+
+    static GOOD_POSITIVE_INTEGERS: &'static [(&'static [u8], u8)] =
+        &[(&[0x02, 0x01, 0x01], 0x01),
+          (&[0x02, 0x01, 0x02], 0x02),
+          (&[0x02, 0x01, 0x7e], 0x7e),
+          (&[0x02, 0x01, 0x7f], 0x7f),
+
+          // Values that need to have an 0x00 prefix to disambiguate them from
+          // them from negative values.
+          (&[0x02, 0x02, 0x00, 0x80], 0x80),
+          (&[0x02, 0x02, 0x00, 0x81], 0x81),
+          (&[0x02, 0x02, 0x00, 0xfe], 0xfe),
+          (&[0x02, 0x02, 0x00, 0xff], 0xff)];
+
+    static BAD_NONNEGATIVE_INTEGERS: &'static [&'static [u8]] = &[&[], // At end of input
+                                                                  &[0x02], // Tag only
+                                                                  &[0x02, 0x00], // Empty value
+
+                                                                  // Length mismatch
+                                                                  &[0x02, 0x00, 0x01],
+                                                                  &[0x02, 0x01],
+                                                                  &[0x02, 0x01, 0x00, 0x01],
+                                                                  &[0x02, 0x01, 0x01, 0x00], // Would be valid if last byte is ignored.
+                                                                  &[0x02, 0x02, 0x01],
+
+                                                                  // Negative values
+                                                                  &[0x02, 0x01, 0x80],
+                                                                  &[0x02, 0x01, 0xfe],
+                                                                  &[0x02, 0x01, 0xff],
+
+                                                                  // Values that have an unnecessary leading 0x00
+                                                                  &[0x02, 0x02, 0x00, 0x00],
+                                                                  &[0x02, 0x02, 0x00, 0x01],
+                                                                  &[0x02, 0x02, 0x00, 0x02],
+                                                                  &[0x02, 0x02, 0x00, 0x7e],
+                                                                  &[0x02, 0x02, 0x00, 0x7f]];
+
+    #[test]
+    fn test_positive_integer() {
+        with_bad_i(ZERO_INTEGER, |input| {
+            let _ = positive_integer(input)?;
+            Ok(())
+        });
+        for &(ref test_in, test_out) in GOOD_POSITIVE_INTEGERS.iter() {
+            with_good_i(test_in, |input| {
+                let test_out = [test_out];
+                assert_eq!(positive_integer(input)?,
+                           untrusted::Input::from(&test_out[..]));
+                Ok(())
+            });
+        }
+        for &test_in in BAD_NONNEGATIVE_INTEGERS.iter() {
+            with_bad_i(test_in, |input| {
+                let _ = positive_integer(input)?;
+                Ok(())
+            });
+        }
+    }
+}
diff --git a/src/rsa/mod.rs b/src/rsa/mod.rs
new file mode 100644
index 0000000..3a46bdb
--- /dev/null
+++ b/src/rsa/mod.rs
@@ -0,0 +1,165 @@
+//! Helper module for RSA key encoding / decoding.
+
+mod der;
+
+use untrusted::Input;
+
+use self::der::{Tag, Der};
+
+/// Corresponds to `1.2.840.113549.1.1.1 rsaEncryption(PKCS #1)`
+const RSA_PKCS1_OID: &'static [u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];
+
+pub fn convert_to_pkcs1<'a>(input: &[u8]) -> Vec<u8> {
+    // if we ever move away from `ring`, this needs to do an explicit key size check (>= 2048)
+    from_pkcs1(input)
+        .or_else(|| from_spki(input))
+        .unwrap_or_else(|| input.to_vec())
+}
+
+fn from_pkcs1(input: &[u8]) -> Option<Vec<u8>> {
+    let _input = Input::from(&input);
+    _input.read_all(der::Error, |i| {
+            der::nested(i, Tag::Sequence, der::Error, |i| {
+                let _ = der::positive_integer(i)?;
+                let _ = der::positive_integer(i)?;
+                // if the input was already pkcs1, just return it
+                Ok(input.to_vec())
+            })
+        })
+        .ok()
+}
+
+fn from_spki(input: &[u8]) -> Option<Vec<u8>> {
+    let _input = Input::from(&input);
+    _input.read_all(der::Error, |i| {
+            der::nested(i, Tag::Sequence, der::Error, |i| {
+                der::nested(i, Tag::Sequence, der::Error, |i| {
+                    let oid = der::expect_tag_and_get_value(i, Tag::OID)?;
+                    if oid != Input::from(RSA_PKCS1_OID) {
+                        return Err(der::Error);
+                    }
+
+                    let _ = der::expect_tag_and_get_value(i, Tag::Null)?;
+                    Ok(())
+                })?;
+
+                der::nested(i, Tag::BitString, der::Error, |i| {
+                    // wtf why
+                    let _ = der::expect_tag_and_get_value(i, Tag::EOC)?;
+                    Ok(i.skip_to_end().iter().cloned().collect())
+                    //der::nested(i, Tag::Sequence, der::Error, |i| {
+                    //    let n = der::positive_integer(i)?;
+                    //    let e = der::positive_integer(i)?;
+                    //    write_pkcs1(n, e)
+                    //})
+                })
+            })
+        })
+        .ok()
+}
+
+#[allow(dead_code)]
+fn write_pkcs1(n: Input, e: Input) -> Result<Vec<u8>, der::Error> {
+    let mut output = Vec::new();
+    {
+        let mut _der = Der::new(&mut output);
+        _der.write_sequence(|_der| {
+                _der.write_integer(n)?;
+                _der.write_integer(e)
+            })?;
+    }
+
+    Ok(output)
+}
+
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use pem;
+    use std::fs::File;
+    use std::io::Read;
+
+    fn read_file(path: &str) -> Vec<u8> {
+        let mut file = File::open(path).expect("couldn't open file");
+        let mut buf = Vec::new();
+        file.read_to_end(&mut buf).expect("couldn't read file");
+        buf
+    }
+
+    #[test]
+    fn test_write_pkcs1() {
+        let contents = read_file("./tests/rsa/pkcs1-1.pub");
+        let contents = pem::parse(&contents).expect("not PEM").contents;
+
+        let n = &[0x00, 0x9d, 0xda, 0x85, 0x17, 0x15, 0x67, 0xab, 0xb1, 0x63, 0x8a, 0x13, 0x01,
+                  0xee, 0xc0, 0x63, 0x7c, 0xc3, 0x08, 0x4b, 0x6d, 0x75, 0xd8, 0x70, 0x74, 0x3d,
+                  0xab, 0x98, 0xef, 0x00, 0xd0, 0xf2, 0x04, 0xe7, 0x7d, 0xb5, 0xa4, 0x08, 0xe3,
+                  0x90, 0xda, 0x4b, 0xe1, 0xd1, 0xff, 0x0f, 0x87, 0x8d, 0x6b, 0x43, 0x58, 0x99,
+                  0x88, 0xf6, 0x99, 0xab, 0xe7, 0x90, 0xfb, 0x2a, 0xa1, 0x3c, 0x2b, 0x0f, 0x24,
+                  0xa4, 0x9e, 0xab, 0xd1, 0xfc, 0xa0, 0xe0, 0xa8, 0x9f, 0x82, 0x48, 0xe5, 0xa7,
+                  0xd2, 0x4d, 0x44, 0xe4, 0x0b, 0x43, 0x66, 0x03, 0x54, 0x8d, 0xdd, 0xc3, 0x0c,
+                  0x26, 0xf5, 0x20, 0x36, 0xbf, 0xae, 0x05, 0x63, 0x9c, 0xf8, 0x81, 0xeb, 0xf7,
+                  0x4a, 0x3a, 0xc4, 0x14, 0xee, 0xce, 0x99, 0x66, 0x9f, 0x3c, 0xe3, 0x18, 0x21,
+                  0x8d, 0x68, 0xe3, 0x0b, 0xb7, 0xb3, 0xf7, 0xca, 0xe1, 0x7c, 0xab, 0xd5, 0x17,
+                  0x6f, 0x50, 0xc1, 0x38, 0x1b, 0xea, 0x62, 0xeb, 0x46, 0x07, 0x95, 0x01, 0xfb,
+                  0xd3, 0x1a, 0xd0, 0xae, 0x1c, 0xe6, 0x53, 0x27, 0x53, 0x2d, 0x08, 0x55, 0xbe,
+                  0xa3, 0xd6, 0xd1, 0x02, 0x14, 0xa4, 0xa2, 0xe1, 0x14, 0xde, 0xa4, 0x0e, 0x54,
+                  0x00, 0xe5, 0x79, 0x2c, 0x4d, 0x93, 0xe8, 0x6b, 0x3c, 0xf6, 0x44, 0x63, 0x85,
+                  0x3c, 0x6f, 0x56, 0xc2, 0x80, 0x02, 0x3f, 0x76, 0xcf, 0x75, 0x46, 0x5f, 0x9a,
+                  0x49, 0x47, 0xdc, 0xe6, 0xe9, 0x9a, 0xc0, 0x6e, 0x34, 0x9e, 0x9f, 0xd2, 0xdf,
+                  0xbc, 0x55, 0xa0, 0x77, 0x61, 0xf3, 0xd5, 0x0c, 0xb8, 0x77, 0xd2, 0x66, 0xd2,
+                  0x24, 0x9a, 0x25, 0xbe, 0x55, 0x1b, 0x4e, 0xbf, 0x3b, 0x82, 0x4c, 0x4f, 0x51,
+                  0x57, 0x7c, 0x8b, 0xf6, 0x38, 0xfe, 0x4d, 0x97, 0x32, 0xa8, 0xc8, 0x3c, 0x69,
+                  0xe5, 0x91, 0x15, 0x2c, 0x75, 0x8d, 0x92, 0xc1, 0xc7, 0x6b];
+
+        let e = &[0x01, 0x00, 0x01];
+        let bytes = write_pkcs1(Input::from(n), Input::from(e));
+
+        assert_eq!(bytes, Ok(contents));
+    }
+
+    #[test]
+    fn pkcs1_noop_conversion_1() {
+        let contents = read_file("./tests/rsa/pkcs1-1.pub");
+        let contents = pem::parse(&contents).expect("not PEM").contents;
+        assert_eq!(convert_to_pkcs1(&contents), contents);
+    }
+
+    #[test]
+    fn pkcs1_noop_conversion_2() {
+        let contents = read_file("./tests/rsa/pkcs1-2.pub");
+        let contents = pem::parse(&contents).expect("not PEM").contents;
+        assert_eq!(convert_to_pkcs1(&contents), contents);
+    }
+
+    #[test]
+    fn pkcs1_from_spki_conversion_1() {
+        let spki = read_file("./tests/rsa/spki-1.pub");
+        let spki = pem::parse(&spki).expect("not PEM").contents;
+
+        let pkcs1 = read_file("./tests/rsa/pkcs1-1.pub");
+        let pkcs1 = pem::parse(&pkcs1).expect("not PEM").contents;
+
+        for (i, (a, b)) in convert_to_pkcs1(&spki).iter().zip(pkcs1.iter()).enumerate() {
+            println!("{} {} {}", i, a, b);
+        }
+
+        assert!(convert_to_pkcs1(&spki) == pkcs1);
+    }
+
+    #[test]
+    fn pkcs1_from_spki_conversion_2() {
+        let spki = read_file("./tests/rsa/spki-2.pub");
+        let spki = pem::parse(&spki).expect("not PEM").contents;
+
+        let pkcs1 = read_file("./tests/rsa/pkcs1-2.pub");
+        let pkcs1 = pem::parse(&pkcs1).expect("not PEM").contents;
+
+        for (i, (a, b)) in convert_to_pkcs1(&spki).iter().zip(pkcs1.iter()).enumerate() {
+            println!("{} {} {}", i, a, b);
+        }
+
+        assert!(convert_to_pkcs1(&spki) == pkcs1);
+    }
+}
diff --git a/src/tuf.rs b/src/tuf.rs
index 0227057..8b666e4 100644
--- a/src/tuf.rs
+++ b/src/tuf.rs
@@ -392,7 +392,6 @@
                                                      Some(meta.length),
                                                      Some((&hash_alg, &expected_hash.0)),
                                                      &mut temp_file)?;
-
         // TODO ? check downloaded version matches what was in the timestamp.json
 
         match self.snapshot {
@@ -591,9 +590,10 @@
             }
         };
 
-        let signed = json::from_slice(&buf)?;
-        let safe_bytes = Self::verify_meta::<R>(signed, role, threshold, trusted_ids, available_keys)?;
-        let meta: M = json::from_slice(&safe_bytes)?;
+        let signed: SignedMetadata<R> = json::from_slice(&buf)?;
+        // TODO clone
+        Self::verify_meta::<R>(signed.clone(), role, threshold, trusted_ids, available_keys)?;
+        let meta: M = json::from_value(signed.signed)?;
 
         if !allow_expired && meta.expires() <= &UTC::now() {
             return Err(Error::ExpiredMetadata(role.clone()));
@@ -721,7 +721,8 @@
                 if let Some(&mut json::Value::Object(ref mut root)) = roles.get_mut("root") {
                     if let Some(&mut json::Value::Array(ref mut key_ids)) = root.get_mut("keyids") {
                         key_ids.clear();
-                        key_ids.extend(root_keys.iter().map(|k| json!(k.value.key_id().0)));
+                        key_ids.extend(root_keys.iter()
+                            .map(|k| json::Value::String(k.value.key_id().0)));
                     }
                 }
             }
@@ -735,9 +736,9 @@
                                 threshold: i32,
                                 trusted_ids: &[KeyId],
                                 available_keys: &HashMap<KeyId, Key>)
-                                -> Result<Vec<u8>, Error> {
+                                -> Result<(), Error> {
         let bytes =
-            cjson::canonicalize(signed.signed).map_err(|err| Error::CanonicalJsonError(err))?;
+            cjson::canonicalize(&signed.signed).map_err(|err| Error::CanonicalJsonError(err))?;
 
         let unique_count = signed.signatures
             .iter()
@@ -755,7 +756,7 @@
                 if let Some(key) = k {
                     m.insert(id, key);
                 } else {
-                    debug!("Unknown key ID: {:?}", id);
+                    debug!("unknown key id: {:?}", id);
                 }
                 m
             });
@@ -779,7 +780,7 @@
                     Err(e) => warn!("Failed to verify with key ID {:?}: {:?}", &sig.key_id, e),
                 }
                 if valid_sigs == threshold {
-                    return Ok(bytes);
+                    return Ok(());
                 }
             }
         }
diff --git a/tests/cjson/root.cjson b/tests/cjson/root.cjson
new file mode 100644
index 0000000..e132582
--- /dev/null
+++ b/tests/cjson/root.cjson
@@ -0,0 +1,33 @@
+{"signatures":[{"keyid":"ed3c845525d34937bf0584989a76fa300c0a6072de714b11b8dca535e10b9088","method":"rsassa-pss-sha256","sig":"49bdc0586c83afad062c608a70a5cfd6dd32bf47eb7830960322723615f723fbf43c7d80dab455b08114624d89de740b2547bece8fda01a8acaecf651fcbe98f291971de695fb2f00889bd1ec076e9d431c4c004791dc5e223f023301eca865c0ede005952dfa4fc7b1a591f8875445872d6162039d5e59418998e0c99a15ad28689b82b72099926354ca253cffafe7a3f7ececb9ecddda35cecb05ea9ca184c7ea942ea8a1a49b7445803dd3a14d74697d220f59cef5d56bc9b640d8aca2328f0d6ea4aa25ba79164ecdf31904a176b6100188970d37860c2612cf4571613fdbaa29080be50cae2966dd46a677acaf1dd500d7a86d025877aab0fdaa72f1856"}],"signed":{"_type":"Root","consistent_snapshot":false,"expires":"2038-01-19T03:14:06Z","keys":{"0bde74244f238cd6d4e3839d35a5b248d6cde5759b760ebdbee45a386109a41a":{"keytype":"rsa","keyval":{"public":"-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8QLzN27B3+DKhWR8nJZO
+ScZ6XS2mubhqcr3giT+WJejjVH4zIlaeC5tosaY96VKI5vRd4aFgQwq2XEx+YTJM
+dAaUGOUSvPYa+iv+O0ilPeYyXazoCaxEDj5c5EQCWJh5EJO+6fdnF7oqFrfGk2kx
+54kY8wBfYEr2psXLTuidKLmxu1gZLymRcH7oDHggFahii0HqXGUSrcTzsuqLI83/
+ulQ0VmJhkY9zooTgh+NhUvKKVGNy7O2rNQLzPXFyeun9bOeKhKiCnUIt0kxtFJPg
+l6u8X46qg4uKqVbDbv2oUV5jTfVDlGwJVAWvC8JPC8N1P0BRmGGqA3NGxpmMRBa6
+fQIDAQAB
+-----END PUBLIC KEY-----"}},"a4304752b0a81316df3d0d1c331e882fe284f1239fe394f31211a19da02e16bd":{"keytype":"rsa","keyval":{"public":"-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoeX9pEiyvBLIl46oLfUJ
+KphECc0sojOmVEtp9rAP/aChHdYHl3usyOexNyQNX7vucmk9rnK/aOFJwo5Mtf5r
+Yl4sIMK5jqI2mvjGjuminjBDGoLCRiK3mFxAGyr4t0AtxtpW8AR4tvgUfl3DS+aB
+XRYBhsx6zndQsPfrggakyv9xeA4uORDQ2ivrq+4BkDCXo65ZA61qFSsfPaShfij0
+klGPLVbyTMc+offlfiiL6ogd6MI49wNb+IDslAPfwrPZ4M3SLWVMmb6+JclFzwp0
+p+CURLWXCptDC9muFSMxZTwaBWBiresHEN36zBoU27ziZCNHpaIgGhjRaJVjzjBo
+MQIDAQAB
+-----END PUBLIC KEY-----"}},"dbfa99f76c6a89191a6747a5e6ed63da202ce77a1f32ae85e152f325ca9a4117":{"keytype":"rsa","keyval":{"public":"-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr0weyzfZ42HVeafaN3WN
+Tz9PNsy/p2AqgPWYLD/lZRO0VwTRfQ/IM30NQ2DdK1Nu/uEkKTFs+Opx1sL+ezCJ
+M2BtqxZKzWlpdGR5Fu2VhXC+pd4FzSBPD3H70Ln85fsww2dnJOVZyxL++vgRKj/I
+QM26ind9pkDhdowSj9bu45Sn8M1BJ7WWoxj4JMficgeSQ4DhrP6Xx326hLLOp0uf
+LxJbNNCeBqlmpZR+wi5MO21L+1JLEhFxCpmrhii4IILi6zooi5Je8RLW544IkzQE
+5sCX/q9PdnEXc7yOKksja9J6tHcUR6cLWdbGAH4MpUGaT91eDKMh9nNNtwoDpDNV
+qwIDAQAB
+-----END PUBLIC KEY-----"}},"ed3c845525d34937bf0584989a76fa300c0a6072de714b11b8dca535e10b9088":{"keytype":"rsa","keyval":{"public":"-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAndqFFxVnq7FjihMB7sBj
+fMMIS2112HB0PauY7wDQ8gTnfbWkCOOQ2kvh0f8Ph41rQ1iZiPaZq+eQ+yqhPCsP
+JKSeq9H8oOCon4JI5afSTUTkC0NmA1SN3cMMJvUgNr+uBWOc+IHr90o6xBTuzplm
+nzzjGCGNaOMLt7P3yuF8q9UXb1DBOBvqYutGB5UB+9Ma0K4c5lMnUy0IVb6j1tEC
+FKSi4RTepA5UAOV5LE2T6Gs89kRjhTxvVsKAAj92z3VGX5pJR9zm6ZrAbjSen9Lf
+vFWgd2Hz1Qy4d9Jm0iSaJb5VG06/O4JMT1FXfIv2OP5NlzKoyDxp5ZEVLHWNksHH
+awIDAQAB
+-----END PUBLIC KEY-----"}}},"roles":{"root":{"keyids":["ed3c845525d34937bf0584989a76fa300c0a6072de714b11b8dca535e10b9088"],"threshold":1},"snapshot":{"keyids":["dbfa99f76c6a89191a6747a5e6ed63da202ce77a1f32ae85e152f325ca9a4117"],"threshold":1},"targets":{"keyids":["0bde74244f238cd6d4e3839d35a5b248d6cde5759b760ebdbee45a386109a41a"],"threshold":1},"timestamp":{"keyids":["a4304752b0a81316df3d0d1c331e882fe284f1239fe394f31211a19da02e16bd"],"threshold":1}},"version":1}}
\ No newline at end of file
diff --git a/tests/cjson/root.json b/tests/cjson/root.json
index 8417118..4aee181 100644
--- a/tests/cjson/root.json
+++ b/tests/cjson/root.json
@@ -1 +1,67 @@
-{"signatures":[{"keyid":"edbef910373211b9358743b82fdc93df5925ee374a032335605cc0ad88198be5","method":"ed25519","sig":"ebeb73585c1908d24d4a6fe8a71e47b6847ef8224d7d6256eb47e2a0961fb5f41277cdfbd8647048091f18672a130eb0630843359dcc2896741d9314bd1a3e02"}],"signed":{"_type":"Root","consistent_snapshot":false,"expires":"2038-01-19T03:14:06Z","keys":{"86cde9bc080169837e7c25e136a197f9b572828c6d7358165a3316023066f955":{"keytype":"ed25519","keyval":{"public":"d28ff85e56a01a7fc545cccba7733f6bb97d736ebb80993f8198b3290edd4ba7"}},"bc7ba0c97704e55a3b7e24112f92bb4b56d27bea8beb669e50ffdbe17b6ae375":{"keytype":"ed25519","keyval":{"public":"200de6ac8ddcab44b9ed40ac71904d1c4c873cc1b20e183b6edaea6504657297"}},"c9952c718dd7942231929bd7a1f0559a27ad70c1a6e99eb7cefacb17fcce36d3":{"keytype":"ed25519","keyval":{"public":"04aee67fc4119ac01b8645400ff5ca7af4953eae27accf0cecfe9e22ff098d4d"}},"edbef910373211b9358743b82fdc93df5925ee374a032335605cc0ad88198be5":{"keytype":"ed25519","keyval":{"public":"2ca92b0dc29b78f64d28bcc2b1081025ea843a2ee88c3fe840fb9db85604ca98"}}},"roles":{"root":{"keyids":["edbef910373211b9358743b82fdc93df5925ee374a032335605cc0ad88198be5"],"threshold":1},"snapshot":{"keyids":["86cde9bc080169837e7c25e136a197f9b572828c6d7358165a3316023066f955"],"threshold":1},"targets":{"keyids":["c9952c718dd7942231929bd7a1f0559a27ad70c1a6e99eb7cefacb17fcce36d3"],"threshold":1},"timestamp":{"keyids":["bc7ba0c97704e55a3b7e24112f92bb4b56d27bea8beb669e50ffdbe17b6ae375"],"threshold":1}},"version":1}}
\ No newline at end of file
+{
+  "signatures": [
+    {
+      "keyid": "ed3c845525d34937bf0584989a76fa300c0a6072de714b11b8dca535e10b9088",
+      "method": "rsassa-pss-sha256",
+      "sig": "49bdc0586c83afad062c608a70a5cfd6dd32bf47eb7830960322723615f723fbf43c7d80dab455b08114624d89de740b2547bece8fda01a8acaecf651fcbe98f291971de695fb2f00889bd1ec076e9d431c4c004791dc5e223f023301eca865c0ede005952dfa4fc7b1a591f8875445872d6162039d5e59418998e0c99a15ad28689b82b72099926354ca253cffafe7a3f7ececb9ecddda35cecb05ea9ca184c7ea942ea8a1a49b7445803dd3a14d74697d220f59cef5d56bc9b640d8aca2328f0d6ea4aa25ba79164ecdf31904a176b6100188970d37860c2612cf4571613fdbaa29080be50cae2966dd46a677acaf1dd500d7a86d025877aab0fdaa72f1856"
+    }
+  ],
+  "signed": {
+    "_type": "Root",
+    "consistent_snapshot": false,
+    "expires": "2038-01-19T03:14:06Z",
+    "keys": {
+      "0bde74244f238cd6d4e3839d35a5b248d6cde5759b760ebdbee45a386109a41a": {
+        "keytype": "rsa",
+        "keyval": {
+          "public": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8QLzN27B3+DKhWR8nJZO\nScZ6XS2mubhqcr3giT+WJejjVH4zIlaeC5tosaY96VKI5vRd4aFgQwq2XEx+YTJM\ndAaUGOUSvPYa+iv+O0ilPeYyXazoCaxEDj5c5EQCWJh5EJO+6fdnF7oqFrfGk2kx\n54kY8wBfYEr2psXLTuidKLmxu1gZLymRcH7oDHggFahii0HqXGUSrcTzsuqLI83/\nulQ0VmJhkY9zooTgh+NhUvKKVGNy7O2rNQLzPXFyeun9bOeKhKiCnUIt0kxtFJPg\nl6u8X46qg4uKqVbDbv2oUV5jTfVDlGwJVAWvC8JPC8N1P0BRmGGqA3NGxpmMRBa6\nfQIDAQAB\n-----END PUBLIC KEY-----"
+        }
+      },
+      "a4304752b0a81316df3d0d1c331e882fe284f1239fe394f31211a19da02e16bd": {
+        "keytype": "rsa",
+        "keyval": {
+          "public": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoeX9pEiyvBLIl46oLfUJ\nKphECc0sojOmVEtp9rAP/aChHdYHl3usyOexNyQNX7vucmk9rnK/aOFJwo5Mtf5r\nYl4sIMK5jqI2mvjGjuminjBDGoLCRiK3mFxAGyr4t0AtxtpW8AR4tvgUfl3DS+aB\nXRYBhsx6zndQsPfrggakyv9xeA4uORDQ2ivrq+4BkDCXo65ZA61qFSsfPaShfij0\nklGPLVbyTMc+offlfiiL6ogd6MI49wNb+IDslAPfwrPZ4M3SLWVMmb6+JclFzwp0\np+CURLWXCptDC9muFSMxZTwaBWBiresHEN36zBoU27ziZCNHpaIgGhjRaJVjzjBo\nMQIDAQAB\n-----END PUBLIC KEY-----"
+        }
+      },
+      "dbfa99f76c6a89191a6747a5e6ed63da202ce77a1f32ae85e152f325ca9a4117": {
+        "keytype": "rsa",
+        "keyval": {
+          "public": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr0weyzfZ42HVeafaN3WN\nTz9PNsy/p2AqgPWYLD/lZRO0VwTRfQ/IM30NQ2DdK1Nu/uEkKTFs+Opx1sL+ezCJ\nM2BtqxZKzWlpdGR5Fu2VhXC+pd4FzSBPD3H70Ln85fsww2dnJOVZyxL++vgRKj/I\nQM26ind9pkDhdowSj9bu45Sn8M1BJ7WWoxj4JMficgeSQ4DhrP6Xx326hLLOp0uf\nLxJbNNCeBqlmpZR+wi5MO21L+1JLEhFxCpmrhii4IILi6zooi5Je8RLW544IkzQE\n5sCX/q9PdnEXc7yOKksja9J6tHcUR6cLWdbGAH4MpUGaT91eDKMh9nNNtwoDpDNV\nqwIDAQAB\n-----END PUBLIC KEY-----"
+        }
+      },
+      "ed3c845525d34937bf0584989a76fa300c0a6072de714b11b8dca535e10b9088": {
+        "keytype": "rsa",
+        "keyval": {
+          "public": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAndqFFxVnq7FjihMB7sBj\nfMMIS2112HB0PauY7wDQ8gTnfbWkCOOQ2kvh0f8Ph41rQ1iZiPaZq+eQ+yqhPCsP\nJKSeq9H8oOCon4JI5afSTUTkC0NmA1SN3cMMJvUgNr+uBWOc+IHr90o6xBTuzplm\nnzzjGCGNaOMLt7P3yuF8q9UXb1DBOBvqYutGB5UB+9Ma0K4c5lMnUy0IVb6j1tEC\nFKSi4RTepA5UAOV5LE2T6Gs89kRjhTxvVsKAAj92z3VGX5pJR9zm6ZrAbjSen9Lf\nvFWgd2Hz1Qy4d9Jm0iSaJb5VG06/O4JMT1FXfIv2OP5NlzKoyDxp5ZEVLHWNksHH\nawIDAQAB\n-----END PUBLIC KEY-----"
+        }
+      }
+    },
+    "roles": {
+      "root": {
+        "keyids": [
+          "ed3c845525d34937bf0584989a76fa300c0a6072de714b11b8dca535e10b9088"
+        ],
+        "threshold": 1
+      },
+      "snapshot": {
+        "keyids": [
+          "dbfa99f76c6a89191a6747a5e6ed63da202ce77a1f32ae85e152f325ca9a4117"
+        ],
+        "threshold": 1
+      },
+      "targets": {
+        "keyids": [
+          "0bde74244f238cd6d4e3839d35a5b248d6cde5759b760ebdbee45a386109a41a"
+        ],
+        "threshold": 1
+      },
+      "timestamp": {
+        "keyids": [
+          "a4304752b0a81316df3d0d1c331e882fe284f1239fe394f31211a19da02e16bd"
+        ],
+        "threshold": 1
+      }
+    },
+    "version": 1
+  }
+}
diff --git a/tests/rsa/pkcs1-1.pub b/tests/rsa/pkcs1-1.pub
new file mode 100644
index 0000000..7ca5280
--- /dev/null
+++ b/tests/rsa/pkcs1-1.pub
@@ -0,0 +1,8 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIIBCgKCAQEAndqFFxVnq7FjihMB7sBjfMMIS2112HB0PauY7wDQ8gTnfbWkCOOQ
+2kvh0f8Ph41rQ1iZiPaZq+eQ+yqhPCsPJKSeq9H8oOCon4JI5afSTUTkC0NmA1SN
+3cMMJvUgNr+uBWOc+IHr90o6xBTuzplmnzzjGCGNaOMLt7P3yuF8q9UXb1DBOBvq
+YutGB5UB+9Ma0K4c5lMnUy0IVb6j1tECFKSi4RTepA5UAOV5LE2T6Gs89kRjhTxv
+VsKAAj92z3VGX5pJR9zm6ZrAbjSen9LfvFWgd2Hz1Qy4d9Jm0iSaJb5VG06/O4JM
+T1FXfIv2OP5NlzKoyDxp5ZEVLHWNksHHawIDAQAB
+-----END RSA PUBLIC KEY-----
diff --git a/tests/rsa/pkcs1-2.pub b/tests/rsa/pkcs1-2.pub
new file mode 100644
index 0000000..47701da
--- /dev/null
+++ b/tests/rsa/pkcs1-2.pub
@@ -0,0 +1,13 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIICCgKCAgEA6si2eKLhg5EwZajX8b4PpLmPnnhJElIEadkb7nJnW4kCk5QO5TwH
+tfRS7snPULxH6gDiTMKBMRMNOw9wXOwNZ/DTLeaKs7ScJS24tLnjwI8dEj5h0N8n
+eBvkFrStEFoNxy0unRJHWnnc+j3TdCWf5k1VbZWS5fREECtq767fDBs/l55iI176
+6x+vSPbdt0mXsTiFLf4eT1ISskvToG7/K3TkTyTUYLVdCfOYnWgsbgjiemjwzoyW
+CgxHQGSh6CaGWccvczp1WNL757VP/HzpsmVE/mr0T6V3k/lGAp0mEbjyJKcnk2E5
+LaLjSqfk+b4yJgmQXP68HOLRWL57bUgZnx85GpHpQfE0MX0OmuwQsbpQxxiErSax
+3Lrh3sWsa0dUIggb0/UWKir6omoMwjYcqR4Sfp2vI0yjusLah3nF6bV6b+zgA/Zy
++tffsW7ntxjOY/d8hQRJtKmEJ54XJSCEOay6E1NfCj/G9UGNuKt/rnhWghkxoOuG
+lDAo5O4UCjwyDavinAb/3E2Q6tSZJR0g6z7mNBDZewmViHB+5b9demT/xaloyxe0
+QrevtTlh/NbytEki0NQgXE/6sXHd/R0Y3wqDtggWZbeWTqSD+HiyglPzgLULpXMP
+NhO05bhlbHnP4srkacjBFSfQYcl7odkQZeqFbCk+k0/cYYQ+6bd2IpUCAwEAAQ==
+-----END RSA PUBLIC KEY-----
diff --git a/tests/rsa/spki-1.pub b/tests/rsa/spki-1.pub
new file mode 100644
index 0000000..44b0ef9
--- /dev/null
+++ b/tests/rsa/spki-1.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAndqFFxVnq7FjihMB7sBj
+fMMIS2112HB0PauY7wDQ8gTnfbWkCOOQ2kvh0f8Ph41rQ1iZiPaZq+eQ+yqhPCsP
+JKSeq9H8oOCon4JI5afSTUTkC0NmA1SN3cMMJvUgNr+uBWOc+IHr90o6xBTuzplm
+nzzjGCGNaOMLt7P3yuF8q9UXb1DBOBvqYutGB5UB+9Ma0K4c5lMnUy0IVb6j1tEC
+FKSi4RTepA5UAOV5LE2T6Gs89kRjhTxvVsKAAj92z3VGX5pJR9zm6ZrAbjSen9Lf
+vFWgd2Hz1Qy4d9Jm0iSaJb5VG06/O4JMT1FXfIv2OP5NlzKoyDxp5ZEVLHWNksHH
+awIDAQAB
+-----END PUBLIC KEY-----
diff --git a/tests/rsa/spki-2.pub b/tests/rsa/spki-2.pub
new file mode 100644
index 0000000..e4d767e
--- /dev/null
+++ b/tests/rsa/spki-2.pub
@@ -0,0 +1,14 @@
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6si2eKLhg5EwZajX8b4P
+pLmPnnhJElIEadkb7nJnW4kCk5QO5TwHtfRS7snPULxH6gDiTMKBMRMNOw9wXOwN
+Z/DTLeaKs7ScJS24tLnjwI8dEj5h0N8neBvkFrStEFoNxy0unRJHWnnc+j3TdCWf
+5k1VbZWS5fREECtq767fDBs/l55iI1766x+vSPbdt0mXsTiFLf4eT1ISskvToG7/
+K3TkTyTUYLVdCfOYnWgsbgjiemjwzoyWCgxHQGSh6CaGWccvczp1WNL757VP/Hzp
+smVE/mr0T6V3k/lGAp0mEbjyJKcnk2E5LaLjSqfk+b4yJgmQXP68HOLRWL57bUgZ
+nx85GpHpQfE0MX0OmuwQsbpQxxiErSax3Lrh3sWsa0dUIggb0/UWKir6omoMwjYc
+qR4Sfp2vI0yjusLah3nF6bV6b+zgA/Zy+tffsW7ntxjOY/d8hQRJtKmEJ54XJSCE
+Oay6E1NfCj/G9UGNuKt/rnhWghkxoOuGlDAo5O4UCjwyDavinAb/3E2Q6tSZJR0g
+6z7mNBDZewmViHB+5b9demT/xaloyxe0QrevtTlh/NbytEki0NQgXE/6sXHd/R0Y
+3wqDtggWZbeWTqSD+HiyglPzgLULpXMPNhO05bhlbHnP4srkacjBFSfQYcl7odkQ
+ZeqFbCk+k0/cYYQ+6bd2IpUCAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/tests/vectors.rs b/tests/vectors.rs
index 5acc59e..95a5215 100644
--- a/tests/vectors.rs
+++ b/tests/vectors.rs
@@ -1,4 +1,5 @@
 extern crate data_encoding;
+extern crate pem;
 extern crate serde;
 #[macro_use]
 extern crate serde_derive;
@@ -11,6 +12,7 @@
 use std::fs::{self, File, DirEntry};
 use std::io::{self, Read};
 use std::path::{PathBuf, Path};
+use std::str;
 use tempdir::TempDir;
 use tuf::{Tuf, Config, Error, RemoteRepo};
 use tuf::meta::{Key, KeyValue, KeyType};
@@ -68,7 +70,7 @@
     }
 }
 
-fn run_test_vector(test_path: &str, test_type: TestType) {
+fn run_test_vector(test_path: &str, test_type: TestType, pin_root_keys: bool) {
     let temp_dir = TempDir::new("rust-tuf").expect("couldn't make temp dir");
     let temp_path = temp_dir.into_path();
 
@@ -93,28 +95,6 @@
     println!("The test vector path is: {}",
              vector_path.to_string_lossy().into_owned());
 
-    let root_keys = test_vector.root_keys
-        .iter()
-        .map(|k| {
-            let file_path = vector_path.join("keys").join(k.path.clone());
-            let mut file = File::open(file_path).expect("couldn't open file");
-            let mut key = String::new();
-            file.read_to_string(&mut key).expect("couldn't read key");
-
-            match k.typ.as_ref() {
-                "ed25519" => {
-                    let val = HEXLOWER.decode(key.replace("\n", "").as_ref())
-                        .expect("key value not hex");
-                    Key {
-                        typ: KeyType::Ed25519,
-                        value: KeyValue(val),
-                    }
-                }
-                x => panic!("unknown key type: {}", x),
-            }
-        })
-        .collect();
-
     let config = match test_type {
         TestType::File => Config::build()
             .remote(RemoteRepo::File(vector_path.join("repo"))),
@@ -125,7 +105,63 @@
         .finish()
         .expect("bad config");
 
-    match (Tuf::from_root_keys(root_keys, config), &test_vector.error) {
+    let tuf = if pin_root_keys {
+        let root_keys = test_vector.root_keys
+            .iter()
+            .map(|k| {
+                let file_path = vector_path.join("keys").join(k.path.clone());
+                let mut file = File::open(file_path)
+                    .expect("couldn't open file");
+                let mut buf = Vec::new();
+                file.read_to_end(&mut buf).expect("couldn't read key");
+
+                let len = buf.len();
+                if buf[len - 1] == b'\n' {
+                    buf.truncate(len - 1)
+                }
+
+                let key = str::from_utf8(&buf).expect("not utf-8").to_string();
+
+                match k.typ.as_ref() {
+                    "ed25519" => {
+                        let val = HEXLOWER.decode(key.replace("\n", "").as_ref())
+                            .expect("key value not hex");
+                        Key {
+                            typ: KeyType::Ed25519,
+                            value: KeyValue {
+                                typ: KeyType::Ed25519,   
+                                value: val,
+                                original: key,
+                            },
+                        }
+                    }
+                    "rsa" => {
+                        let val = pem::parse(key.clone())
+                            .expect("key value not pem");
+                        Key {
+                            typ: KeyType::Rsa,
+                            value: KeyValue {
+                                typ: KeyType::Rsa,   
+                                value: val.contents,
+                                original: key,
+                            },
+                        }
+                    }
+                    x => panic!("unknown key type: {}", x),
+                }
+            })
+            .collect();
+        Tuf::from_root_keys(root_keys, config)
+    } else {
+        Tuf::initialize(&temp_path)
+            .expect("failed to initialize");
+        fs::copy(vector_path.join("repo").join("1.root.json"),
+                 temp_path.join("metadata").join("current").join("root.json"))
+            .expect("failed to copy root.json");
+        Tuf::new(config)
+    };
+
+    match (tuf, &test_vector.error) {
         (Ok(ref tuf), &None) => {
             // first time pulls remote
             assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()), Ok(()));
@@ -151,6 +187,11 @@
                     format!("Role: {}, err: {}", role, err))
         }
 
+        (Err(Error::UnmetThreshold(_)), &Some(ref err))
+            if err == &"IllegalRsaKeySize".to_string() => {
+            ()
+        }
+
         (Err(Error::UnmetThreshold(ref role)), &Some(ref err))
             if err.starts_with("UnmetThreshold::") => {
             assert!(err.to_lowercase()
@@ -215,15 +256,27 @@
             use $crate::{run_test_vector, TestType};
 
             #[test]
-            fn file_test() {
-                run_test_vector($name, TestType::File)
+            fn file_pinned() {
+                run_test_vector($name, TestType::File, true)
+            }
+
+            #[test]
+            fn file_unpinned() {
+                run_test_vector($name, TestType::File, false)
             }
 
             // TODO no idea how windows shell scipting works
             #[cfg(not(windows))]
             #[test]
-            fn http_test() {
-                run_test_vector($name, TestType::Http)
+            fn http_pinned() {
+                run_test_vector($name, TestType::Http, true)
+            }
+
+            // TODO no idea how windows shell scipting works
+            #[cfg(not(windows))]
+            #[test]
+            fn http_unpinned() {
+                run_test_vector($name, TestType::Http, false)
             }
         }
     }
@@ -231,10 +284,10 @@
 
 test_cases!("001", _001);
 test_cases!("002", _002);
-// test_cases!("003", _003);
-// test_cases!("004", _004);
+test_cases!("003", _003);
+test_cases!("004", _004);
 test_cases!("005", _005);
-// test_cases!("006", _006);
+test_cases!("006", _006);
 test_cases!("007", _007);
 test_cases!("008", _008);
 test_cases!("009", _009);
@@ -255,24 +308,24 @@
 test_cases!("024", _024);
 test_cases!("025", _025);
 test_cases!("026", _026);
-// test_cases!("027", _027);
-// test_cases!("028", _028);
+test_cases!("027", _027);
+test_cases!("028", _028);
 test_cases!("029", _029);
 test_cases!("030", _030);
 test_cases!("031", _031);
 test_cases!("032", _032);
 test_cases!("033", _033);
 test_cases!("034", _034);
-// test_cases!("035", _035);
-// test_cases!("036", _036);
+test_cases!("035", _035);
+test_cases!("036", _036);
 test_cases!("037", _037);
 test_cases!("038", _038);
 test_cases!("039", _039);
 test_cases!("040", _040);
-// test_cases!("041", _041);
-// test_cases!("042", _042);
-// test_cases!("043", _043);
-// test_cases!("044", _044);
+test_cases!("041", _041);
+test_cases!("042", _042);
+test_cases!("043", _043);
+test_cases!("044", _044);
 test_cases!("045", _045);
 test_cases!("046", _046);
 test_cases!("047", _047);