Browse Source

Started working on the WriteCap verification function.

Matthew Carr 2 years ago
parent
commit
bd75ae946a
3 changed files with 108 additions and 21 deletions
  1. 107 16
      crates/node/src/crypto.rs
  2. 0 2
      crates/node/src/main.rs
  3. 1 3
      crates/node/src/test_helpers.rs

+ 107 - 16
crates/node/src/crypto.rs

@@ -8,10 +8,10 @@ use openssl::{
     symm::{Cipher, encrypt as openssl_encrypt, decrypt as openssl_decrypt},
     rand::rand_bytes,
     rsa::{Rsa, Padding as OpensslPadding},
-    hash::MessageDigest,
+    hash::{hash, MessageDigest},
     sign::{Signer, Verifier}
 };
-use serde_block_tree::{self, to_vec, from_vec};
+use serde_block_tree::{self, to_vec, from_vec, write_to};
 use serde::de::{DeserializeOwned};
 
 /// Data that may or may not be encrypted.
@@ -187,6 +187,19 @@ impl Key {
             },
         }
     }
+
+    /// Hashes this key and returns the `Principal` it is for.
+    fn to_principal(&self) -> Principal {
+        let slice = match self {
+            Key::Rsa { public, .. } => public.as_slice(),
+            Key::Aes256Cbc { key, .. } => key,
+            Key::Aes256Ctr { key, .. } => key,
+        };
+        let mut buf = [0; 32];
+        let bytes = hash(MessageDigest::sha256(), slice).unwrap();
+        buf.copy_from_slice(&*bytes);
+        Principal(Hash::Sha2_256(buf))
+    }
 }
 
 enum EncryptionAlgo<'a> {
@@ -304,10 +317,11 @@ struct SignAlgo {
 }
 
 impl SignAlgo {
-    fn sign(&mut self, header: &[u8], body: &[u8]) -> Result<()> {
+    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&mut self, parts: I) -> Result<()> {
         let mut signer = Signer::new(self.digest, &self.key).map_err(Error::from)?;
-        signer.update(header).map_err(Error::from)?;
-        signer.update(body).map_err(Error::from)?;
+        for part in parts {
+            signer.update(part).map_err(Error::from)?;
+        }
         let buf = self.signature.as_mut_slice();
         signer.sign(buf).map_err(Error::from)?;
         Ok(())
@@ -337,10 +351,13 @@ struct VerifyAlgo {
 }
 
 impl VerifyAlgo {
-    fn verify(&self, header: &[u8], body: &[u8], signature: &[u8]) -> Result<bool> {
+    fn verify<'a, I: Iterator<Item = &'a [u8]>>(
+        &'a self, parts: I, signature: &[u8]
+    ) -> Result<bool> {
         let mut verifier = Verifier::new(self.digest, &self.key).map_err(Error::from)?;
-        verifier.update(header).map_err(Error::from)?;
-        verifier.update(body).map_err(Error::from)?;
+        for part in parts {
+            verifier.update(part).map_err(Error::from)?;
+        }
         verifier.verify(signature).map_err(Error::from)
     }
 }
@@ -454,7 +471,7 @@ pub(crate) fn sign_block(versioned_block: &mut VersionedBlock, write_cap: WriteC
     let sig_header = SigHeader::from(&*block);
     let header = to_vec(&sig_header)?;
     let mut sign_algo = SignAlgo::try_from(&block.write_cap.signing_key)?;
-    sign_algo.sign(header.as_slice(), body)?;
+    sign_algo.sign(std::array::IntoIter::new([header.as_slice(), body]))?;
     block.signature = sign_algo.signature;
     Ok(())
 }
@@ -465,8 +482,66 @@ pub(crate) fn verify_block(versioned_block: &VersionedBlock) -> Result<bool> {
     let sig_header = SigHeader::from(&*block);
     let header = to_vec(&sig_header)?;
     let verify_algo = VerifyAlgo::try_from(&block.write_cap.signing_key)?;
-    // TODO: Verify the chain in the write cap.
-    verify_algo.verify(header.as_slice(), body, block.signature.as_slice())
+    let parts = std::array::IntoIter::new([header.as_slice(), body]);
+    if !verify_algo.verify(parts, block.signature.as_slice())? {
+        return Ok(false);
+    }
+    Ok(true)
+}
+
+#[derive(Serialize)]
+struct WriteCapSig<'a> {
+    issued_to: &'a Principal,
+    path: &'a Path,
+    expires: &'a Epoch,
+    signing_key: &'a Key,
+}
+
+impl<'a> From<&'a WriteCap> for WriteCapSig<'a> {
+    fn from(write_cap: &'a WriteCap) -> WriteCapSig<'a> {
+        WriteCapSig {
+            issued_to: &write_cap.issued_to,
+            path: &write_cap.path,
+            expires: &write_cap.expires,
+            signing_key: &write_cap.signing_key,
+        }
+    }
+}
+
+/// 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> {
+    let mut prev: Option<&WriteCap> = None;
+    let mut sig_input = Vec::new();
+    loop {
+        // TODO: Verify that `write_cap.path` contains `path`.
+        // TODO: Verify that `write_cap.expires` is not in the past.
+        if let Some(prev) = &prev {
+            if prev.signing_key.to_principal() != write_cap.issued_to {
+                return Ok(false);
+            }
+        }
+        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(
+            std::array::IntoIter::new([sig_input.as_slice()]), write_cap.signature.as_slice())?;
+        if !valid {
+            return Ok(false);
+        }
+        match &write_cap.next {
+            Some(next) => {
+                prev = Some(write_cap);
+                write_cap = next;
+            },
+            None => {
+                // We're at the root key.
+                // TODO: Verify that the fingerprint of `write_cap.signing_key` matches to first
+                // component of `path`.
+                return Ok(true);
+            }
+        }
+    }
 }
 
 #[cfg(test)]
@@ -536,12 +611,14 @@ mod tests {
     #[test]
     fn rsa_sign_and_verify() -> Result<()> {
         let key = Key::generate(KeyId::Rsa)?;
-        let header = b"About: lyrics";
-        let message = b"Everything that feels so good is bad bad bad.";
+        let header = b"About: lyrics".as_slice();
+        let message = b"Everything that feels so good is bad bad bad.".as_slice();
         let mut signer = SignAlgo::try_from(&key)?;
-        signer.sign(header, message)?;
+        let parts = std::array::IntoIter::new([header, message]);
+        signer.sign(parts)?;
         let verifier = VerifyAlgo::try_from(&key)?;
-        let verified = verifier.verify(header, message, signer.signature.as_slice())?;
+        let parts = std::array::IntoIter::new([header, message]);
+        let verified = verifier.verify(parts, signer.signature.as_slice())?;
         assert_eq!(true, verified);
         Ok(())
     }
@@ -554,7 +631,6 @@ mod tests {
         let key = Key::generate(KeyId::Rsa)?;
         let write_cap = WriteCap {
             issued_to: Principal(Hash::Sha2_256(PRINCIPAL)),
-            issued_by: Principal(Hash::Sha2_256(PRINCIPAL)),
             path: Path::try_from("contacts/emergency")
                 .map_err(|err| Error::Message(err.to_string()))?,
             expires: Epoch(1649904316),
@@ -568,6 +644,21 @@ mod tests {
         Ok(())
     }
 
+    #[test]
+    fn to_principal() {
+        let key = Key::Rsa {
+            public: Vec::from(RSA_KEY),
+            private: None,
+            padding: RsaPadding::Pkcs1,
+        };
+        let actual = key.to_principal();
+        let expected = Principal(Hash::Sha2_256([
+            85, 20, 110, 220, 18, 26, 1, 140, 86, 169, 1, 117, 52, 76, 250, 221, 61, 74, 19, 59,
+            131, 225, 180, 78, 252, 126, 230, 147, 76, 205, 219, 238
+        ]));
+        assert_eq!(expected, actual);
+    }
+
     /// Tests that validate the dependencies of this module.
     mod dependency_tests {
         use super::*;

+ 0 - 2
crates/node/src/main.rs

@@ -65,8 +65,6 @@ impl ReadCap {
 struct WriteCap {
     /// The principal this `WriteCap` was issued to.
     issued_to: Principal,
-    /// The principal that issued this write cap.
-    issued_by: Principal,
     /// The path where this write caps's validity begins.
     path: Path,
     /// The point in time after which this write cap is no longer valid.

+ 1 - 3
crates/node/src/test_helpers.rs

@@ -60,7 +60,7 @@ pub const KEY: [u8; 32] = [
     0xBB, 0xF9, 0xFE, 0xD0, 0xC1, 0xF7, 0x90, 0x34, 0x69, 0xB7, 0xE7, 0xC6, 0x1C, 0x46, 0x85, 0x48,
 ];
 
-const RSA_KEY: [u8; 550] = [
+pub const RSA_KEY: [u8; 550] = [
     0xEC, 0xB5, 0x31, 0xFB, 0xF7, 0x1B, 0x81, 0x55, 0x20, 0xF2, 0x4D, 0x09, 0xDC, 0x19, 0x37, 0x07,
     0x08, 0x41, 0xB5, 0xC2, 0xDE, 0xF8, 0x33, 0xF9, 0x42, 0x79, 0xFF, 0x79, 0x4F, 0x45, 0x4F, 0x99,
     0x3C, 0x80, 0xE7, 0x91, 0xC1, 0xA1, 0x6C, 0xB4, 0xBF, 0x1B, 0x64, 0xCE, 0xE6, 0xB4, 0x7D, 0xB8,
@@ -125,7 +125,6 @@ pub(crate) fn make_principal() -> Principal {
 pub(crate) fn make_write_cap() -> Result<WriteCap> {
     Ok(WriteCap {
         issued_to: Principal(Hash::Sha2_256(PRINCIPAL)),
-        issued_by: Principal(Hash::Sha2_256(PRINCIPAL)),
         path: Path::try_from("contacts/emergency").map_err(|err| Error::Message(err.to_string()))?,
         expires: Epoch(1649904316),
         signing_key: Key::Rsa {
@@ -134,7 +133,6 @@ pub(crate) fn make_write_cap() -> Result<WriteCap> {
         signature: Signature::Rsa(SIGNATURE),
         next: Some(Box::from(WriteCap {
             issued_to: Principal(Hash::Sha2_256(PRINCIPAL)),
-            issued_by: Principal(Hash::Sha2_256(PRINCIPAL)),
             path: Path::try_from("contacts").map_err(|err| Error::Message(err.to_string()))?,
             expires: Epoch(1649994316),
             signing_key: Key::Rsa {