full working example
fixes #93
fixes #101
diff --git a/src/client.rs b/src/client.rs
index 6cca35f..ccba448 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -87,15 +87,16 @@
///
/// Returns `true` if an update occurred and `false` otherwise.
pub fn update_remote(&mut self) -> Result<bool> {
- Ok(
- Self::update_root(&mut self.tuf, &mut self.remote, &self.config.max_root_size)? ||
- Self::update_timestamp(
- &mut self.tuf,
- &mut self.remote,
- &self.config.max_timestamp_size,
- )? || Self::update_snapshot(&mut self.tuf, &mut self.remote)? ||
- Self::update_targets(&mut self.tuf, &mut self.local)?,
- )
+ let r = Self::update_root(&mut self.tuf, &mut self.remote, &self.config.max_root_size)?;
+ let ts = Self::update_timestamp(
+ &mut self.tuf,
+ &mut self.remote,
+ &self.config.max_timestamp_size,
+ )?;
+ let sn = Self::update_snapshot(&mut self.tuf, &mut self.remote)?;
+ let ta = Self::update_targets(&mut self.tuf, &mut self.remote)?;
+
+ Ok(r || ts || sn || ta)
}
/// Returns `true` if an update occurred and `false` otherwise.
@@ -110,7 +111,7 @@
max_root_size,
None,
)?;
- let latest_version = D::deserialize::<RootMetadata>(latest_root.unverified_signed())?
+ let latest_version = D::deserialize::<RootMetadata>(latest_root.signed())?
.version();
if latest_version < tuf.root().version() {
@@ -173,7 +174,7 @@
{
let snapshot_description = match tuf.timestamp() {
Some(ts) => {
- match ts.meta().get(&MetadataPath::from_role(&Role::Timestamp)) {
+ 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 \
@@ -187,7 +188,7 @@
.clone();
if snapshot_description.version() <= tuf.snapshot().map(|s| s.version()).unwrap_or(0) {
- return Ok(false)
+ return Ok(false);
}
let snap = repo.fetch_metadata(
@@ -221,7 +222,7 @@
.clone();
if targets_description.version() <= tuf.targets().map(|t| t.version()).unwrap_or(0) {
- return Ok(false)
+ return Ok(false);
}
let targets = repo.fetch_metadata(
diff --git a/src/lib.rs b/src/lib.rs
index d1c02d0..0a3d3c9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,7 +19,7 @@
//! use tuf::Tuf;
//! use tuf::crypto::KeyId;
//! use tuf::client::{Client, Config};
-//! use tuf::metadata::{RootMetadata, Unverified, SignedMetadata, Role, MetadataPath,
+//! use tuf::metadata::{RootMetadata, SignedMetadata, Role, MetadataPath,
//! MetadataVersion};
//! use tuf::interchange::JsonDataInterchange;
//! use tuf::repository::{Repository, FileSystemRepository, HttpRepository};
diff --git a/src/metadata.rs b/src/metadata.rs
index 8fcdcea..406e2ab 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -133,19 +133,6 @@
Ok(())
}
-/// Trait used to represent whether a piece of data is verified or not.
-pub trait VerificationStatus: Debug + PartialEq {}
-
-/// Type used to represent verified data.
-#[derive(Debug, PartialEq)]
-pub struct Verified {}
-impl VerificationStatus for Verified {}
-
-/// Type used to represent unverified data.
-#[derive(Debug, PartialEq)]
-pub struct Unverified {}
-impl VerificationStatus for Unverified {}
-
/// The TUF role.
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Role {
@@ -230,11 +217,10 @@
/// A piece of raw metadata with attached signatures.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
-pub struct SignedMetadata<D, M, V>
+pub struct SignedMetadata<D, M>
where
D: DataInterchange,
M: Metadata,
- V: VerificationStatus,
{
signatures: Vec<Signature>,
signed: D::RawData,
@@ -242,22 +228,19 @@
_interchage: PhantomData<D>,
#[serde(skip_serializing, skip_deserializing)]
_metadata: PhantomData<M>,
- #[serde(skip_serializing, skip_deserializing)]
- _verification: PhantomData<V>,
}
-impl<D, M, V> SignedMetadata<D, M, V>
+impl<D, M> SignedMetadata<D, M>
where
D: DataInterchange,
M: Metadata,
- V: VerificationStatus,
{
/// Create a new `SignedMetadata`.
pub fn new(
metadata: &M,
private_key: &PrivateKey,
scheme: SignatureScheme,
- ) -> Result<SignedMetadata<D, M, Unverified>> {
+ ) -> Result<SignedMetadata<D, M>> {
let raw = D::serialize(metadata)?;
let bytes = D::canonicalize(&raw)?;
let sig = private_key.sign(&bytes, scheme)?;
@@ -266,7 +249,6 @@
signed: raw,
_interchage: PhantomData,
_metadata: PhantomData,
- _verification: PhantomData,
})
}
@@ -280,20 +262,18 @@
&mut self.signatures
}
- /// An immutable reference to the unverified raw data.
- ///
- /// **WARNING**: This data is untrusted.
- pub fn unverified_signed(&self) -> &D::RawData {
+ /// An immutable reference to the raw data.
+ pub fn signed(&self) -> &D::RawData {
&self.signed
}
- /// Verify this metadata and convert its type to `Verified`.
+ /// Verify this metadata.
pub fn verify(
- self,
+ &self,
threshold: u32,
authorized_key_ids: &HashSet<KeyId>,
available_keys: &HashMap<KeyId, PublicKey>,
- ) -> Result<SignedMetadata<D, M, Verified>> {
+ ) -> Result<()> {
if self.signatures.len() < 1 {
return Err(Error::VerificationFailure(
"The metadata was not signed with any authorized keys."
@@ -344,13 +324,7 @@
}
if signatures_needed == 0 {
- Ok(SignedMetadata {
- signatures: self.signatures,
- signed: self.signed,
- _interchage: PhantomData,
- _metadata: PhantomData,
- _verification: PhantomData,
- })
+ Ok(())
} else {
Err(Error::VerificationFailure(format!(
"Signature threshold not met: {}/{}",
@@ -361,17 +335,6 @@
}
}
-impl<D, M> SignedMetadata<D, M, Verified>
-where
- D: DataInterchange,
- M: Metadata,
-{
- /// An immutable reference to the verified raw data.
- pub fn signed(&self) -> &D::RawData {
- self.unverified_signed()
- }
-}
-
/// Metadata for the root role.
#[derive(Debug, PartialEq)]
pub struct RootMetadata {
@@ -717,9 +680,7 @@
impl MetadataDescription {
/// Create a new `MetadataDescription`.
- pub fn new(
- version: u32,
- ) -> Result<Self> {
+ pub fn new(version: u32) -> Result<Self> {
if version < 1 {
return Err(Error::IllegalArgument(format!(
"Metadata version must be greater than zero. Found: {}",
@@ -727,9 +688,7 @@
)));
}
- Ok(MetadataDescription {
- version: version,
- })
+ Ok(MetadataDescription { version: version })
}
/// The version of the described metadata.
@@ -1281,7 +1240,7 @@
let key = PrivateKey::from_pkcs8(ED25519_1_PK8).unwrap();
- let signed = SignedMetadata::<JsonDataInterchange, SnapshotMetadata, Unverified>::new(
+ let signed = SignedMetadata::<JsonDataInterchange, SnapshotMetadata>::new(
&snapshot,
&key,
SignatureScheme::Ed25519,
@@ -1309,7 +1268,7 @@
let encoded = json::to_value(&signed).unwrap();
assert_eq!(encoded, jsn);
- let decoded: SignedMetadata<JsonDataInterchange, SnapshotMetadata, Unverified> =
+ let decoded: SignedMetadata<JsonDataInterchange, SnapshotMetadata> =
json::from_value(encoded).unwrap();
assert_eq!(decoded, signed);
}
diff --git a/src/repository.rs b/src/repository.rs
index bdeab19..04f9d2c 100644
--- a/src/repository.rs
+++ b/src/repository.rs
@@ -15,8 +15,8 @@
use Result;
use crypto::{self, HashAlgorithm, HashValue};
use error::Error;
-use metadata::{SignedMetadata, MetadataVersion, Unverified, Verified, Role, Metadata, TargetPath,
- TargetDescription, MetadataPath};
+use metadata::{SignedMetadata, MetadataVersion, Role, Metadata, TargetPath, TargetDescription,
+ MetadataPath};
use interchange::DataInterchange;
/// Top-level trait that represents a TUF repository and contains all the ways it can be interacted
@@ -37,7 +37,7 @@
role: &Role,
meta_path: &MetadataPath,
version: &MetadataVersion,
- metadata: &SignedMetadata<D, M, Verified>,
+ metadata: &SignedMetadata<D, M>,
) -> Result<()>
where
M: Metadata;
@@ -50,7 +50,7 @@
version: &MetadataVersion,
max_size: &Option<usize>,
hash_data: Option<(&HashAlgorithm, &HashValue)>,
- ) -> Result<SignedMetadata<D, M, Unverified>>
+ ) -> Result<SignedMetadata<D, M>>
where
M: Metadata;
@@ -207,7 +207,7 @@
role: &Role,
meta_path: &MetadataPath,
version: &MetadataVersion,
- metadata: &SignedMetadata<D, M, Verified>,
+ metadata: &SignedMetadata<D, M>,
) -> Result<()>
where
M: Metadata,
@@ -236,7 +236,7 @@
version: &MetadataVersion,
max_size: &Option<usize>,
hash_data: Option<(&HashAlgorithm, &HashValue)>,
- ) -> Result<SignedMetadata<D, M, Unverified>>
+ ) -> Result<SignedMetadata<D, M>>
where
M: Metadata,
{
@@ -366,7 +366,7 @@
_: &Role,
_: &MetadataPath,
_: &MetadataVersion,
- _: &SignedMetadata<D, M, Verified>,
+ _: &SignedMetadata<D, M>,
) -> Result<()>
where
M: Metadata,
@@ -383,7 +383,7 @@
version: &MetadataVersion,
max_size: &Option<usize>,
hash_data: Option<(&HashAlgorithm, &HashValue)>,
- ) -> Result<SignedMetadata<D, M, Unverified>>
+ ) -> Result<SignedMetadata<D, M>>
where
M: Metadata,
{
@@ -451,7 +451,7 @@
role: &Role,
meta_path: &MetadataPath,
version: &MetadataVersion,
- metadata: &SignedMetadata<D, M, Verified>,
+ metadata: &SignedMetadata<D, M>,
) -> Result<()>
where
M: Metadata,
@@ -473,7 +473,7 @@
version: &MetadataVersion,
max_size: &Option<usize>,
hash_data: Option<(&HashAlgorithm, &HashValue)>,
- ) -> Result<SignedMetadata<D, M, Unverified>>
+ ) -> Result<SignedMetadata<D, M>>
where
M: Metadata,
{
diff --git a/src/tuf.rs b/src/tuf.rs
index 5dd3e8e..238f004 100644
--- a/src/tuf.rs
+++ b/src/tuf.rs
@@ -8,8 +8,8 @@
use crypto::KeyId;
use error::Error;
use interchange::DataInterchange;
-use metadata::{SignedMetadata, RootMetadata, VerificationStatus, TimestampMetadata, Role,
- SnapshotMetadata, MetadataPath, TargetsMetadata, TargetPath, TargetDescription};
+use metadata::{SignedMetadata, RootMetadata, TimestampMetadata, Role, SnapshotMetadata,
+ MetadataPath, TargetsMetadata, TargetPath, TargetDescription};
/// Contains trusted TUF metadata and can be used to verify other metadata and targets.
#[derive(Debug)]
@@ -24,13 +24,10 @@
impl<D: DataInterchange> Tuf<D> {
/// Create a new `TUF` struct from a known set of pinned root keys that are used to verify the
/// signed metadata.
- pub fn from_root_pinned<V>(
- mut signed_root: SignedMetadata<D, RootMetadata, V>,
+ pub fn from_root_pinned(
+ mut signed_root: SignedMetadata<D, RootMetadata>,
root_key_ids: &[KeyId],
- ) -> Result<Self>
- where
- V: VerificationStatus,
- {
+ ) -> Result<Self> {
signed_root.signatures_mut().retain(|s| {
root_key_ids.contains(s.key_id())
});
@@ -41,11 +38,8 @@
///
/// **WARNING**: This is trust-on-first-use (TOFU) and offers weaker security guarantees than
/// the related method `from_root_pinned`.
- pub fn from_root<V>(signed_root: SignedMetadata<D, RootMetadata, V>) -> Result<Self>
- where
- V: VerificationStatus,
- {
- let root = D::deserialize::<RootMetadata>(signed_root.unverified_signed())?;
+ pub fn from_root(signed_root: SignedMetadata<D, RootMetadata>) -> Result<Self> {
+ let root = D::deserialize::<RootMetadata>(signed_root.signed())?;
let _ = signed_root.verify(
root.root().threshold(),
root.root().key_ids(),
@@ -93,20 +87,14 @@
}
/// Verify and update the root metadata.
- pub fn update_root<V>(
- &mut self,
- signed_root: SignedMetadata<D, RootMetadata, V>,
- ) -> Result<bool>
- where
- V: VerificationStatus,
- {
- let signed_root = signed_root.verify(
+ pub fn update_root(&mut self, signed_root: SignedMetadata<D, RootMetadata>) -> Result<bool> {
+ signed_root.verify(
self.root.root().threshold(),
self.root.root().key_ids(),
self.root.keys(),
)?;
- let root = D::deserialize::<RootMetadata>(signed_root.unverified_signed())?;
+ let root = D::deserialize::<RootMetadata>(signed_root.signed())?;
match root.version() {
x if x == self.root.version() => {
@@ -139,14 +127,11 @@
}
/// Verify and update the timestamp metadata.
- pub fn update_timestamp<V>(
+ pub fn update_timestamp(
&mut self,
- signed_timestamp: SignedMetadata<D, TimestampMetadata, V>,
- ) -> Result<bool>
- where
- V: VerificationStatus,
- {
- let signed_timestamp = signed_timestamp.verify(
+ signed_timestamp: SignedMetadata<D, TimestampMetadata>,
+ ) -> Result<bool> {
+ signed_timestamp.verify(
self.root.timestamp().threshold(),
self.root.timestamp().key_ids(),
self.root.keys(),
@@ -174,13 +159,10 @@
}
/// Verify and update the snapshot metadata.
- pub fn update_snapshot<V>(
+ pub fn update_snapshot(
&mut self,
- signed_snapshot: SignedMetadata<D, SnapshotMetadata, V>,
- ) -> Result<bool>
- where
- V: VerificationStatus,
- {
+ signed_snapshot: SignedMetadata<D, SnapshotMetadata>,
+ ) -> Result<bool> {
let snapshot = {
let root = self.safe_root_ref()?;
let timestamp = self.safe_timestamp_ref()?;
@@ -205,7 +187,7 @@
return Ok(false);
}
- let signed_snapshot = signed_snapshot.verify(
+ signed_snapshot.verify(
root.snapshot().threshold(),
root.snapshot().key_ids(),
root.keys(),
@@ -234,13 +216,10 @@
}
/// Verify and update the targets metadata.
- pub fn update_targets<V>(
+ pub fn update_targets(
&mut self,
- signed_targets: SignedMetadata<D, TargetsMetadata, V>,
- ) -> Result<bool>
- where
- V: VerificationStatus,
- {
+ signed_targets: SignedMetadata<D, TargetsMetadata>,
+ ) -> Result<bool> {
let targets = {
let root = self.safe_root_ref()?;
let snapshot = self.safe_snapshot_ref()?;
@@ -265,7 +244,7 @@
return Ok(false);
}
- let signed_targets = signed_targets.verify(
+ signed_targets.verify(
root.targets().threshold(),
root.targets().key_ids(),
root.keys(),
diff --git a/tests/simple_example.rs b/tests/simple_example.rs
new file mode 100644
index 0000000..cc8b691
--- /dev/null
+++ b/tests/simple_example.rs
@@ -0,0 +1,199 @@
+extern crate chrono;
+extern crate tuf;
+
+use chrono::prelude::*;
+use chrono::offset::Utc;
+use std::collections::{HashSet, HashMap};
+use tuf::{Tuf, Error};
+use tuf::client::{Client, Config};
+use tuf::crypto::{PrivateKey, SignatureScheme, KeyId};
+use tuf::interchange::JsonDataInterchange;
+use tuf::metadata::{RoleDefinition, RootMetadata, Role, MetadataVersion, MetadataPath,
+ SignedMetadata, TargetDescription, TargetPath, TargetsMetadata,
+ MetadataDescription, SnapshotMetadata, TimestampMetadata};
+use tuf::repository::{EphemeralRepository, Repository};
+
+// Ironically, this is far from simple, but it's as simple as it can be made.
+
+const ED25519_1_PK8: &'static [u8] = include_bytes!("./ed25519/ed25519-1.pk8.der");
+const ED25519_2_PK8: &'static [u8] = include_bytes!("./ed25519/ed25519-2.pk8.der");
+const ED25519_3_PK8: &'static [u8] = include_bytes!("./ed25519/ed25519-3.pk8.der");
+const ED25519_4_PK8: &'static [u8] = include_bytes!("./ed25519/ed25519-4.pk8.der");
+
+#[test]
+fn main() {
+ let mut remote = EphemeralRepository::<JsonDataInterchange>::new();
+ let root_key_ids = init_server(&mut remote).unwrap();
+ init_client(root_key_ids, remote).unwrap();
+}
+
+fn init_client(
+ root_key_ids: Vec<KeyId>,
+ mut remote: EphemeralRepository<JsonDataInterchange>,
+) -> Result<(), Error> {
+ let local = EphemeralRepository::<JsonDataInterchange>::new();
+ let config = Config::build().finish()?;
+ let root = remote.fetch_metadata(
+ &Role::Root,
+ &MetadataPath::from_role(&Role::Root),
+ &MetadataVersion::None,
+ config.max_root_size(),
+ None,
+ )?;
+
+ let tuf = Tuf::<JsonDataInterchange>::from_root_pinned(root, &root_key_ids)?;
+ let mut client = Client::new(tuf, config, local, remote)?;
+ match client.update_local() {
+ Ok(_) => (),
+ Err(e) => println!("{:?}", e),
+ }
+ let _ = client.update_remote()?;
+ client.fetch_target(&TargetPath::new("grendel".into())?)
+}
+
+fn init_server(remote: &mut EphemeralRepository<JsonDataInterchange>) -> Result<Vec<KeyId>, Error> {
+ // in real life, you wouldn't want these keys on the same machine ever
+ let root_key = PrivateKey::from_pkcs8(ED25519_1_PK8)?;
+ let snapshot_key = PrivateKey::from_pkcs8(ED25519_2_PK8)?;
+ let targets_key = PrivateKey::from_pkcs8(ED25519_3_PK8)?;
+ let timestamp_key = PrivateKey::from_pkcs8(ED25519_4_PK8)?;
+
+ //// build the root ////
+
+ let keys = vec![
+ root_key.public().clone(),
+ snapshot_key.public().clone(),
+ targets_key.public().clone(),
+ 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)?;
+
+ let mut key_ids = HashSet::new();
+ key_ids.insert(snapshot_key.key_id().clone());
+ let snapshot_def = RoleDefinition::new(1, key_ids)?;
+
+ let mut key_ids = HashSet::new();
+ key_ids.insert(targets_key.key_id().clone());
+ let targets_def = RoleDefinition::new(1, key_ids)?;
+
+ let mut key_ids = HashSet::new();
+ key_ids.insert(timestamp_key.key_id().clone());
+ let timestamp_def = RoleDefinition::new(1, key_ids)?;
+
+ let root = RootMetadata::new(
+ 1,
+ Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
+ false,
+ keys,
+ root_def,
+ snapshot_def,
+ targets_def,
+ timestamp_def,
+ )?;
+
+ let signed = SignedMetadata::<JsonDataInterchange, RootMetadata>::new(
+ &root,
+ &root_key,
+ SignatureScheme::Ed25519,
+ )?;
+
+ remote.store_metadata(
+ &Role::Root,
+ &MetadataPath::new("root".into())?,
+ &MetadataVersion::Number(1),
+ &signed,
+ )?;
+ remote.store_metadata(
+ &Role::Root,
+ &MetadataPath::new("root".into())?,
+ &MetadataVersion::None,
+ &signed,
+ )?;
+
+ //// build the targets ////
+
+ let target_file: &[u8] = b"things fade, alternatives exclude";
+ let target_path = TargetPath::new("grendel".into())?;
+ let target_description = TargetDescription::from_reader(target_file)?;
+ let _ = remote.store_target(target_file, &target_path, &target_description);
+
+ let mut target_map = HashMap::new();
+ let _ = target_map.insert(target_path, target_description);
+ let targets = TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), target_map)?;
+
+ let signed = SignedMetadata::<JsonDataInterchange, TargetsMetadata>::new(
+ &targets,
+ &targets_key,
+ SignatureScheme::Ed25519,
+ )?;
+
+ remote.store_metadata(
+ &Role::Targets,
+ &MetadataPath::new("targets".into())?,
+ &MetadataVersion::Number(1),
+ &signed,
+ )?;
+ remote.store_metadata(
+ &Role::Targets,
+ &MetadataPath::new("targets".into())?,
+ &MetadataVersion::None,
+ &signed,
+ )?;
+
+ //// build the snapshot ////
+ let mut meta_map = HashMap::new();
+ let path = MetadataPath::new("targets".into())?;
+ let desc = MetadataDescription::new(1)?;
+ let _ = meta_map.insert(path, desc);
+ let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)?;
+
+ let signed = SignedMetadata::<JsonDataInterchange, SnapshotMetadata>::new(
+ &snapshot,
+ &snapshot_key,
+ SignatureScheme::Ed25519,
+ )?;
+
+ remote.store_metadata(
+ &Role::Snapshot,
+ &MetadataPath::new("snapshot".into())?,
+ &MetadataVersion::Number(1),
+ &signed,
+ )?;
+ remote.store_metadata(
+ &Role::Snapshot,
+ &MetadataPath::new("snapshot".into())?,
+ &MetadataVersion::None,
+ &signed,
+ )?;
+
+ //// build the timestamp ////
+ let mut meta_map = HashMap::new();
+ let path = MetadataPath::new("snapshot".into())?;
+ let desc = MetadataDescription::new(1)?;
+ let _ = meta_map.insert(path, desc);
+ let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)?;
+
+ let signed = SignedMetadata::<JsonDataInterchange, TimestampMetadata>::new(
+ ×tamp,
+ ×tamp_key,
+ SignatureScheme::Ed25519,
+ )?;
+
+ remote.store_metadata(
+ &Role::Timestamp,
+ &MetadataPath::new("timestamp".into())?,
+ &MetadataVersion::Number(1),
+ &signed,
+ )?;
+ remote.store_metadata(
+ &Role::Timestamp,
+ &MetadataPath::new("timestamp".into())?,
+ &MetadataVersion::None,
+ &signed,
+ )?;
+
+ Ok(vec![root_key.key_id().clone()])
+}