|
@@ -599,31 +599,55 @@ pub(crate) fn sign_writecap(write_cap: &mut WriteCap, key: &Key) -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+/// The types of errors which can occur when verifying a writecap chain is authorized to write to
|
|
|
+/// a given path.
|
|
|
+#[derive(Debug, PartialEq)]
|
|
|
+pub(crate) enum WritecapAuthzErr {
|
|
|
+ /// The chain is not valid for use on the given path.
|
|
|
+ UnauthorizedPath,
|
|
|
+ /// At least one writecap in the chain is expired.
|
|
|
+ Expired,
|
|
|
+ /// The given writecaps do not actually form a chain.
|
|
|
+ NotChained,
|
|
|
+ /// There is an invalid signature in the chain.
|
|
|
+ InvalidSignature,
|
|
|
+ /// The principal the root writecap was issued to does not own the given path.
|
|
|
+ RootDoesNotOwnPath,
|
|
|
+ /// An error occured while serializing a writecap.
|
|
|
+ Serde(String),
|
|
|
+ /// A cryptographic error occurred while attempting to verify a writecap.
|
|
|
+ Crypto(String),
|
|
|
+}
|
|
|
+
|
|
|
/// Verifies that the given `WriteCap` actually grants permission to write to the given `Path`.
|
|
|
-fn verify_write_cap(mut write_cap: &WriteCap, path: &Path) -> Result<bool> {
|
|
|
+pub(crate) fn verify_writecap(
|
|
|
+ mut write_cap: &WriteCap, path: &Path
|
|
|
+) -> std::result::Result<(), WritecapAuthzErr> {
|
|
|
let mut prev: Option<&WriteCap> = None;
|
|
|
let mut sig_input = Vec::new();
|
|
|
+ let now = Epoch::now();
|
|
|
loop {
|
|
|
if !write_cap.path.contains(path) {
|
|
|
- return Ok(false);
|
|
|
+ return Err(WritecapAuthzErr::UnauthorizedPath);
|
|
|
}
|
|
|
- let now = Epoch::now();
|
|
|
if write_cap.expires <= now {
|
|
|
- return Ok(false);
|
|
|
+ return Err(WritecapAuthzErr::Expired);
|
|
|
}
|
|
|
if let Some(prev) = &prev {
|
|
|
if prev.signing_key.to_principal() != write_cap.issued_to {
|
|
|
- return Ok(false);
|
|
|
+ return Err(WritecapAuthzErr::NotChained);
|
|
|
}
|
|
|
}
|
|
|
let sig = WriteCapSig::from(write_cap);
|
|
|
sig_input.clear();
|
|
|
- write_to(&sig, &mut sig_input.as_mut_slice())?;
|
|
|
- let verify_algo = VerifyAlgo::try_from(&write_cap.signing_key)?;
|
|
|
- let valid = verify_algo.verify(
|
|
|
- [sig_input.as_slice()].into_iter(), write_cap.signature.as_slice())?;
|
|
|
+ write_to(&sig, &mut sig_input).map_err(|e| WritecapAuthzErr::Serde(e.to_string()))?;
|
|
|
+ let verify_algo = VerifyAlgo::try_from(&write_cap.signing_key)
|
|
|
+ .map_err(|e| WritecapAuthzErr::Crypto(e.to_string()))?;
|
|
|
+ let valid = verify_algo
|
|
|
+ .verify([sig_input.as_slice()].into_iter(), write_cap.signature.as_slice())
|
|
|
+ .map_err(|e| WritecapAuthzErr::Crypto(e.to_string()))?;
|
|
|
if !valid {
|
|
|
- return Ok(false);
|
|
|
+ return Err(WritecapAuthzErr::InvalidSignature);
|
|
|
}
|
|
|
match &write_cap.next {
|
|
|
Some(next) => {
|
|
@@ -633,7 +657,12 @@ fn verify_write_cap(mut write_cap: &WriteCap, path: &Path) -> Result<bool> {
|
|
|
None => {
|
|
|
// We're at the root key. As long as the signer of this writecap is the owner of
|
|
|
// the path, then the writecap is valid.
|
|
|
- return Ok(write_cap.signing_key.to_principal() == path.owner);
|
|
|
+ if write_cap.signing_key.to_principal() == path.owner {
|
|
|
+ return Ok(());
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return Err(WritecapAuthzErr::RootDoesNotOwnPath)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -752,6 +781,75 @@ mod tests {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+ #[test]
|
|
|
+ fn verify_writecap_valid() -> Result<()> {
|
|
|
+ let writecap = make_writecap()?;
|
|
|
+ let result = verify_writecap(&writecap, &writecap.path);
|
|
|
+ assert_eq!(Ok(()), result);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn verify_writecap_invalid_signature() -> Result<()> {
|
|
|
+ let mut writecap = make_writecap()?;
|
|
|
+ writecap.signature = Signature::default();
|
|
|
+ let result = verify_writecap(&writecap, &writecap.path);
|
|
|
+ assert_eq!(Err(WritecapAuthzErr::InvalidSignature), result);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn verify_writecap_invalid_path_not_contained() -> Result<()> {
|
|
|
+ let writecap = make_writecap()?;
|
|
|
+ let mut path = writecap.path.clone();
|
|
|
+ path.components.pop();
|
|
|
+ // `path` is now a superpath of `writecap.path`, thus the writecap is not authorized to
|
|
|
+ // write to it.
|
|
|
+ let result = verify_writecap(&writecap, &path);
|
|
|
+ assert_eq!(Err(WritecapAuthzErr::UnauthorizedPath), result);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn verify_writecap_invalid_expired() -> Result<()> {
|
|
|
+ let mut writecap = make_writecap()?;
|
|
|
+ writecap.expires = Epoch::now() - Duration::from_secs(1);
|
|
|
+ let result = verify_writecap(&writecap, &writecap.path);
|
|
|
+ assert_eq!(Err(WritecapAuthzErr::Expired), result);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn verify_writecap_invalid_not_chained() -> Result<()> {
|
|
|
+ let (mut root_writecap, root_key) = make_self_signed_writecap()?;
|
|
|
+ root_writecap.issued_to = Principal(Hash::Sha2_256([0; 32]));
|
|
|
+ sign_writecap(&mut root_writecap, &root_key)?;
|
|
|
+ let node_key = Key::Rsa {
|
|
|
+ public: Vec::from(NODE_PUBLIC_KEY), private: None, padding: RsaPadding::Pkcs1
|
|
|
+ };
|
|
|
+ let writecap = make_writecap_trusted_by(
|
|
|
+ root_writecap, &root_key, &node_key, vec!["apps", "contacts"])?;
|
|
|
+ let result = verify_writecap(&writecap, &writecap.path);
|
|
|
+ assert_eq!(Err(WritecapAuthzErr::NotChained), result);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn verify_writecap_invalid_root_doesnt_own_path() -> Result<()> {
|
|
|
+ let (mut root_writecap, root_key) = make_self_signed_writecap()?;
|
|
|
+ let owner = Principal(Hash::Sha2_256([0; 32]));
|
|
|
+ root_writecap.path = make_path_with_owner(owner, vec![]);
|
|
|
+ sign_writecap(&mut root_writecap, &root_key)?;
|
|
|
+ let node_key = Key::Rsa {
|
|
|
+ public: Vec::from(NODE_PUBLIC_KEY), private: None, padding: RsaPadding::Pkcs1
|
|
|
+ };
|
|
|
+ let writecap = make_writecap_trusted_by(
|
|
|
+ root_writecap, &root_key, &node_key, vec!["apps", "contacts"])?;
|
|
|
+ let result = verify_writecap(&writecap, &writecap.path);
|
|
|
+ assert_eq!(Err(WritecapAuthzErr::RootDoesNotOwnPath), result);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
/// Tests that validate the dependencies of this module.
|
|
|
mod dependency_tests {
|
|
|
use super::*;
|