|
@@ -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::*;
|