update root metadata via chaining
diff --git a/src/client.rs b/src/client.rs
index 2d79dbb..ea2c62b 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,10 +1,12 @@
//! Clients for high level interactions with TUF repositories.
-use repository::Repository;
+use chrono::offset::Utc;
use Result;
+use error::Error;
use interchange::DataInterchange;
-use metadata::MetadataVersion;
+use metadata::{MetadataVersion, RootMetadata, Role};
+use repository::Repository;
use tuf::Tuf;
/// A client that interacts with TUF repositories.
@@ -37,28 +39,46 @@
}
}
- /// Update TUF metadata from local and remote sources.
+ /// Update TUF metadata from local and remote repositories.
pub fn update(&mut self) -> Result<()> {
self.update_root()
}
fn update_root(&mut self) -> Result<()> {
- // TODO this doesn't build the chain back up from scratch
- let root = self.local.fetch_root(
- &MetadataVersion::None,
- &self.config.max_root_size,
- )?;
- self.tuf.update_root(root)?;
+ Self::update_root_chain(&mut self.tuf, &self.config.max_root_size, &mut self.local)?;
+ Self::update_root_chain(&mut self.tuf, &self.config.max_root_size, &mut self.remote)?;
- // TODO this doesn't build the chain back up from scratch
- let root = self.remote.fetch_root(
- &MetadataVersion::None,
- &self.config.max_root_size,
- )?;
-
- // TODO store the newly fetched roots in the local repo
+ if self.tuf.root().expires() <= &Utc::now() {
+ Err(Error::ExpiredMetadata(Role::Root))
+ } else {
+ Ok(())
+ }
+ }
- self.tuf.update_root(root)
+ fn update_root_chain<T>(tuf: &mut Tuf<D>, max_root_size: &Option<usize>, repo: &mut T) -> Result<()>
+ where
+ T: Repository<D>
+ {
+ let latest_root = repo.fetch_root(&MetadataVersion::None, max_root_size)?;
+ let latest_version = D::deserialize::<RootMetadata>(latest_root.unverified_signed())?
+ .version();
+
+ if latest_version < tuf.root().version() {
+ return Err(Error::VerificationFailure(format!(
+ "Latest root version is lower than current root version: {} < {}",
+ latest_version,
+ tuf.root().version()
+ )))
+ } else if latest_version == tuf.root().version() {
+ return Ok(())
+ }
+
+ for i in (tuf.root().version() + 1)..latest_version {
+ let signed = repo.fetch_root(&MetadataVersion::Number(i), max_root_size)?;
+ tuf.update_root(signed)?;
+ }
+
+ tuf.update_root(latest_root)
}
}
diff --git a/src/crypto.rs b/src/crypto.rs
index 3355919..620be47 100644
--- a/src/crypto.rs
+++ b/src/crypto.rs
@@ -226,7 +226,7 @@
/// Create a `PublicKey` from an RSA `PublicKeyValue`, either SPKI or PKCS#1.
pub fn from_rsa(value: PublicKeyValue, format: KeyFormat) -> Result<Self> {
- // TODO check n > 2048 bits
+ // TODO check n > 2048 bits (but this is ok because `ring` doesn't support less)
let key_id = calculate_key_id(&value);
diff --git a/src/error.rs b/src/error.rs
index 7007883..19c4d5a 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -7,6 +7,7 @@
use std::io;
use std::path::Path;
+use metadata::Role;
use rsa::der;
/// Error type for all TUF related errors.
@@ -18,6 +19,8 @@
Decode(String),
/// There was a problem encoding the metadata.
Encode(String),
+ /// Metadata was expired.
+ ExpiredMetadata(Role),
/// Generic catcher for all errors.
Generic(String),
/// An illegal argument was passed into a function.
diff --git a/src/metadata.rs b/src/metadata.rs
index b9552b8..770fd0d 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -99,7 +99,7 @@
}
/// An immutable reference to the unverified raw data.
- ///
+ ///
/// *WARNING*: This data is untrusted.
pub fn unverified_signed(&self) -> &D::RawData {
&self.signed
diff --git a/src/tuf.rs b/src/tuf.rs
index 6713246..6bc4183 100644
--- a/src/tuf.rs
+++ b/src/tuf.rs
@@ -52,6 +52,11 @@
})
}
+ /// An immutable reference to the root metadata.
+ pub fn root(&self) -> &RootMetadata {
+ &self.root
+ }
+
/// Verify and update the root metadata.
pub fn update_root<V>(&mut self, signed_root: SignedMetadata<D, RootMetadata, V>) -> Result<()>
where
@@ -67,17 +72,20 @@
match root.version() {
x if x == self.root.version() => {
- info!("Attempted to update root to new metadata with the same version. Refusing to update.")
- },
+ info!(
+ "Attempted to update root to new metadata with the same version. Refusing to update."
+ )
+ }
x if x < self.root.version() => {
- return Err(Error::VerificationFailure(format!("Attempted to roll back root at version {} to {}.", self.root.version(), x)))
+ return Err(Error::VerificationFailure(format!(
+ "Attempted to roll back root at version {} to {}.",
+ self.root.version(),
+ x
+ )))
}
_ => (),
}
- // TODO this is allowed to be expired, which is ok for updating the root chain, but not ok
- // for actually verifying anything else later
-
let _ = signed_root.verify(
root.root().threshold(),
root.root().key_ids(),