Browse Source

Converted KeyPair struct to a trait.

Matthew Carr 2 years ago
parent
commit
f0256889cf

+ 2 - 1
crates/.vscode/settings.json

@@ -18,5 +18,6 @@
         "writecap",
         "writecaps",
         "Xsalsa"
-    ]
+    ],
+    "editor.rulers": [100]
 }

+ 4 - 4
crates/.vscode/tasks.json

@@ -4,8 +4,8 @@
     "version": "2.0.0",
     "tasks": [
         {
-            "type": "cargo",
-            "subcommand": "test",
+            "command": "cargo",
+            "args": ["test"],
             "problemMatcher": [
                 "$rustc"
             ],
@@ -13,8 +13,8 @@
             "label": "test"
         },
         {
-            "type": "cargo",
-            "subcommand": "build",
+            "command": "cargo",
+            "args": ["build"],
             "problemMatcher": [
                 "$rustc"
             ],

+ 111 - 70
crates/btnode/src/crypto/mod.rs

@@ -20,7 +20,6 @@ use serde::{
 };
 use std::{
     str::FromStr,
-    sync::Arc,
 };
 use strum_macros::{EnumString, EnumDiscriminants, Display};
 
@@ -363,13 +362,19 @@ impl Owned for AsymKeyPub {
     }
 }
 
-pub(crate) trait AsymKeyPriv: Decryptor + Signer {}
+impl CredsPub for AsymKeyPub {}
 
 pub(crate) struct RsaPriv {
     pkey: PKey<Private>,
 }
 
 impl RsaPriv {
+    pub(crate) fn new(der: &[u8]) -> Result<RsaPriv> {
+        let rsa = Rsa::private_key_from_der(der).map_err(Error::from)?;
+        let pkey = PKey::from_rsa(rsa).map_err(Error::from)?;
+        Ok(RsaPriv { pkey })
+    }
+
     fn digest() -> MessageDigest {
         MessageDigest::sha256()
     }
@@ -391,7 +396,7 @@ impl Decryptor for RsaPriv {
 }
 
 impl Signer for RsaPriv {
-    fn sign<'a>(&self, parts: &mut dyn Iterator<Item=&'a [u8]>) -> Result<Signature> {
+    fn sign<'a, I: Iterator<Item=&'a [u8]>>(&self, parts: &mut I) -> Result<Signature> {
         let digest = RsaPriv::digest();
         let mut signature = RsaPriv::signature_buf();
 
@@ -405,45 +410,74 @@ impl Signer for RsaPriv {
     }
 }
 
-impl AsymKeyPriv for RsaPriv {}
+impl CredsPriv for RsaPriv {}
 
-impl RsaPriv {
-    pub(crate) fn new(der: &[u8]) -> Result<RsaPriv> {
-        let rsa = Rsa::private_key_from_der(der).map_err(Error::from)?;
-        let pkey = PKey::from_rsa(rsa).map_err(Error::from)?;
-        Ok(RsaPriv { pkey })
+pub(crate) struct ConcreteCreds<T: CredsPriv> {
+    public: AsymKeyPub,
+    private: T,
+}
+
+impl<T: CredsPriv> ConcreteCreds<T> {
+    pub(crate) fn new(public: AsymKeyPub, private: T) -> ConcreteCreds<T> {
+        ConcreteCreds { public, private }
     }
 }
 
-pub(crate) struct KeyPair {
-    pub(crate) public: AsymKeyPub,
-    pub(crate) private: Arc<dyn AsymKeyPriv>,
+
+impl ConcreteCreds<RsaPriv> {
+    pub(crate) fn generate() -> Result<ConcreteCreds<RsaPriv>> {
+        let key = Rsa::generate(4096)?;
+        // TODO: Separating the keys this way seems inefficient. Investigate alternatives.
+        let public_der = key.public_key_to_der().map_err(Error::from)?;
+        let private_der = key.private_key_to_der().map_err(Error::from)?;
+        Ok(ConcreteCreds {
+            public: AsymKeyPub::new(AsymKeyKind::Rsa, public_der.as_slice())?,
+            private: RsaPriv::new(private_der.as_slice())?,
+        })
+    }
 }
 
-impl KeyPair {
-    pub(crate) fn generate(kind: AsymKeyKind) -> Result<KeyPair> {
-        match kind {
-            AsymKeyKind::Rsa => {
-                let key = Rsa::generate(4096)?;
-                // TODO: Separating the keys this way seems inefficient. Investigate alternatives.
-                let public_der = key.public_key_to_der().map_err(Error::from)?;
-                let private_der = key.private_key_to_der().map_err(Error::from)?;
-                Ok(KeyPair {
-                    public: AsymKeyPub::new(AsymKeyKind::Rsa, public_der.as_slice())?,
-                    private: Arc::new(RsaPriv::new(private_der.as_slice())?),
-                })
-            },
-        }
+impl<T: CredsPriv> Verifier for ConcreteCreds<T> {
+    fn verify<'a, I: Iterator<Item=&'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<bool> {
+        self.public.verify(parts, signature)
     }
 }
 
-impl Owned for KeyPair {
+impl<T: CredsPriv> Encryptor for ConcreteCreds<T> {
+    fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
+        self.public.encrypt(slice)
+    }
+}
+
+impl<T: CredsPriv> Owned for ConcreteCreds<T> {
     fn owner_of_kind(&self, kind: HashKind) -> Principal {
         self.public.owner_of_kind(kind)
     }
 }
 
-trait Encryptor {
+impl<T: CredsPriv> CredsPub for ConcreteCreds<T> {}
+
+impl<T: CredsPriv> Signer for ConcreteCreds<T> {
+    fn sign<'a, I: Iterator<Item=&'a [u8]>>(&self, parts: &mut I) -> Result<Signature> {
+        self.private.sign(parts)
+    }
+}
+
+impl<T: CredsPriv> Decryptor for ConcreteCreds<T> {
+    fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
+        self.private.decrypt(slice)
+    }
+}
+
+impl<T: CredsPriv> CredsPriv for ConcreteCreds<T> {}
+
+impl<T: CredsPriv> Creds for ConcreteCreds<T> {
+    fn public(&self) -> &AsymKeyPub {
+        &self.public
+    }
+}
+
+pub(crate) trait Encryptor {
     fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>>;
 }
 
@@ -481,10 +515,10 @@ impl Decryptor for SymKey {
 }
 
 pub(crate) trait Signer {
-    fn sign<'a>(&self, parts: &mut dyn Iterator<Item=&'a [u8]>) -> Result<Signature>;
+    fn sign<'a, I: Iterator<Item=&'a [u8]>>(&self, parts: &mut I) -> Result<Signature>;
 }
 
-trait Verifier {
+pub(crate) trait Verifier {
     fn verify<'a, I: Iterator<Item=&'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<bool>;
 }
 
@@ -498,8 +532,19 @@ impl Verifier for AsymKeyPub {
     }
 }
 
-pub(crate) fn encrypt_block(
-    mut block: Block, principal: &Principal, key: &KeyPair
+/// Trait for types which can be used as public credentials.
+pub(crate) trait CredsPub: Verifier + Encryptor + Owned {}
+
+/// Trait for types which contain private credentials.
+pub(crate) trait CredsPriv: Decryptor + Signer {}
+
+/// Trait for types which contain both public and private credentials.
+pub(crate) trait Creds: CredsPriv + CredsPub {
+    fn public(&self) -> &AsymKeyPub;
+}
+
+pub(crate) fn encrypt_block<C: Creds>(
+    mut block: Block, principal: &Principal, creds: &C
 ) -> Result<Block> {
     let body = match block.body {
         Cryptotext::Cipher(_) => return Ok(block),
@@ -507,15 +552,15 @@ pub(crate) fn encrypt_block(
     };
     let (principal_owned, read_cap) = block.readcaps.remove_entry(principal)
         .ok_or(Error::NoReadCap)?;
-    let block_key = decrypt(read_cap, key.private.as_ref())?;
+    let block_key = decrypt(read_cap, creds)?;
     let new_body = block_key.encrypt(&body)?;
     block.body = Cryptotext::Cipher(new_body);
-    block.readcaps.insert(principal_owned, encrypt(&block_key, &key.public)?);
+    block.readcaps.insert(principal_owned, encrypt(&block_key, creds)?);
     Ok(block)
 }
 
-pub(crate) fn decrypt_block(
-    mut block: Block, principal: &Principal, key: &dyn AsymKeyPriv
+pub(crate) fn decrypt_block<K: CredsPriv>(
+    mut block: Block, principal: &Principal, key: &K
 ) -> Result<Block> {
     let body = match block.body {
         Cryptotext::Plain(_) => return Ok(block),
@@ -536,10 +581,8 @@ fn encrypt<T: Serialize, K: Encryptor>(value: &T, key: &K) -> Result<Cryptotext<
     Ok(Cryptotext::Cipher(vec?))
 }
 
-/// TODO: When the trait upcasting feature lands in the stable channel change the type of `key`
-/// to `dyn Decryptor`. Tracking issue: https://github.com/rust-lang/rust/issues/65991
-fn decrypt<T: Serialize + DeserializeOwned>(
-    cryptotext: Cryptotext<T>, key: &dyn AsymKeyPriv
+fn decrypt<T: Serialize + DeserializeOwned, K: CredsPriv>(
+    cryptotext: Cryptotext<T>, key: &K
 ) -> Result<T> {
     let data = match cryptotext {
         Cryptotext::Plain(value) => return Ok(value),
@@ -550,15 +593,15 @@ fn decrypt<T: Serialize + DeserializeOwned>(
 }
 
 #[derive(Serialize)]
-struct SigHeader<'a> {
+struct HeaderSigInput<'a> {
     path: &'a Path,
     readcaps: &'a HashMap<Principal, Cryptotext<SymKey>>,
     writecap: &'a Writecap,
 }
 
-impl<'a> From<&'a Block> for SigHeader<'a> {
-    fn from(block: &'a Block) -> SigHeader<'a> {
-        SigHeader {
+impl<'a> From<&'a Block> for HeaderSigInput<'a> {
+    fn from(block: &'a Block) -> HeaderSigInput<'a> {
+        HeaderSigInput {
             path: &block.path,
             readcaps: &block.readcaps,
             writecap: &block.writecap,
@@ -576,13 +619,13 @@ fn get_body(block: &Block) -> Result<&[u8]> {
     Ok(body)
 }
 
-pub(crate) fn sign_block(
-    block: &mut Block, writecap: Writecap, priv_key: &dyn AsymKeyPriv
+pub(crate) fn sign_block<K: Signer>(
+    block: &mut Block, writecap: Writecap, priv_key: &K
 ) -> Result<()> {
     block.writecap = writecap;
     let body = get_body(block)?;
-    let sig_header = SigHeader::from(&*block);
-    let header = to_vec(&sig_header)?;
+    let sig_input = HeaderSigInput::from(&*block);
+    let header = to_vec(&sig_input)?;
     let signature = priv_key.sign(&mut [header.as_slice(), body].into_iter())?;
     block.signature = signature;
     Ok(())
@@ -590,23 +633,23 @@ pub(crate) fn sign_block(
 
 pub(crate) fn verify_block(block: &Block) -> Result<bool> {
     let body = get_body(block)?;
-    let sig_header = SigHeader::from(&*block);
-    let header = to_vec(&sig_header)?;
+    let sig_input = HeaderSigInput::from(&*block);
+    let header = to_vec(&sig_input)?;
     let parts = [header.as_slice(), body].into_iter();
     block.writecap.signing_key.verify(parts, block.signature.as_slice())
 }
 
 #[derive(Serialize)]
-struct WritecapSig<'a> {
+struct WritecapSigInput<'a> {
     issued_to: &'a Principal,
     path: &'a Path,
     expires: &'a Epoch,
     signing_key: &'a AsymKeyPub,
 }
 
-impl<'a> From<&'a Writecap> for WritecapSig<'a> {
-    fn from(writecap: &'a Writecap) -> WritecapSig<'a> {
-        WritecapSig {
+impl<'a> From<&'a Writecap> for WritecapSigInput<'a> {
+    fn from(writecap: &'a Writecap) -> WritecapSigInput<'a> {
+        WritecapSigInput {
             issued_to: &writecap.issued_to,
             path: &writecap.path,
             expires: &writecap.expires,
@@ -615,10 +658,8 @@ impl<'a> From<&'a Writecap> for WritecapSig<'a> {
     }
 }
 
-/// TODO: When the trait upcasting feature lands in the stable channel change the type of `priv_key`
-/// to `dyn Decryptor`. Tracking issue: https://github.com/rust-lang/rust/issues/65991
-pub(crate) fn sign_writecap(writecap: &mut Writecap, priv_key: &dyn AsymKeyPriv) -> Result<()> {
-    let sig_input = to_vec(&WritecapSig::from(&*writecap))?;
+pub(crate) fn sign_writecap<K: Signer>(writecap: &mut Writecap, priv_key: &K) -> Result<()> {
+    let sig_input = to_vec(&WritecapSigInput::from(&*writecap))?;
     writecap.signature = priv_key.sign(&mut [sig_input.as_slice()].into_iter())?;
     Ok(())
 }
@@ -662,7 +703,7 @@ pub(crate) fn verify_writecap(
                 return Err(WritecapAuthzErr::NotChained);
             }
         }
-        let sig = WritecapSig::from(writecap);
+        let sig = WritecapSigInput::from(writecap);
         sig_input.clear();
         write_to(&sig, &mut sig_input).map_err(|e| WritecapAuthzErr::Serde(e.to_string()))?;
         let valid = writecap.signing_key 
@@ -695,17 +736,17 @@ mod tests {
     use super::*;
     use test_helpers::*;
 
-    fn encrypt_decrypt_block_test_case(
-        mut actual: Block, principal: &Principal, key: &KeyPair
+    fn encrypt_decrypt_block_test_case<C: Creds>(
+        mut actual: Block, principal: &Principal, creds: &C
     ) -> Result<()> {
         let expected = actual.clone();
-        actual = encrypt_block(actual, principal, key)?;
+        actual = encrypt_block(actual, principal, creds)?;
         let encrypted = match &actual.body {
             Cryptotext::Cipher(_) => true,
             Cryptotext::Plain(_) => false
         };
         assert!(encrypted);
-        actual = decrypt_block(actual, principal, key.private.as_ref())?;
+        actual = decrypt_block(actual, principal, creds)?;
         assert_eq!(expected, actual);
         Ok(())
     }
@@ -733,8 +774,8 @@ mod tests {
         let key = make_key_pair();
         let header = b"About: lyrics".as_slice();
         let message = b"Everything that feels so good is bad bad bad.".as_slice();
-        let signature = key.private.sign(&mut [header, message].into_iter())?;
-        let verified = key.public.verify([header, message].into_iter(), signature.as_slice())?;
+        let signature = key.sign(&mut [header, message].into_iter())?;
+        let verified = key.verify([header, message].into_iter(), signature.as_slice())?;
         assert_eq!(true, verified);
         Ok(())
     }
@@ -749,12 +790,12 @@ mod tests {
             issued_to: Principal(Hash::Sha2_256(PRINCIPAL)),
             path: make_path(vec!["contacts", "emergency"]),
             expires: Epoch(1649904316),
-            signing_key: key.public.clone(),
+            signing_key: key.public().clone(),
             signature: Signature::Rsa(SIGNATURE),
             next: None,
         };
         block = encrypt_block(block, &principal, &key)?;
-        sign_block(&mut block, writecap, key.private.as_ref())?;
+        sign_block(&mut block, writecap, &key)?;
         assert_eq!(true, verify_block(&block)?);
         Ok(())
     }
@@ -817,7 +858,7 @@ mod tests {
     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.private.as_ref())?;
+        sign_writecap(&mut root_writecap, &root_key)?;
         let node_key = AsymKeyPub {
             kind: AsymKeyKind::Rsa,
             pkey: PKey::from_rsa(Rsa::public_key_from_der(NODE_PUBLIC_KEY.as_slice())?)?
@@ -835,7 +876,7 @@ mod tests {
         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.private.as_ref())?;
+        sign_writecap(&mut root_writecap, &root_key)?;
         let node_key = AsymKeyPub {
             kind: AsymKeyKind::Rsa,
             pkey: PKey::from_rsa(Rsa::public_key_from_der(NODE_PUBLIC_KEY.as_slice())?)?
@@ -896,7 +937,7 @@ mod tests {
         #[test]
         fn rsa_signature_len() -> Result<()> {
             let key = make_key_pair();
-            let signature = key.private.sign(&mut [NODE_PUBLIC_KEY.as_slice()].into_iter())?;
+            let signature = key.sign(&mut [NODE_PUBLIC_KEY.as_slice()].into_iter())?;
             let length = match signature {
                 Signature::Rsa(data) => data.len(),
             };

+ 13 - 14
crates/btnode/src/test_helpers.rs

@@ -3,7 +3,7 @@
 use super::*;
 use crypto::*;
 use serde_block_tree::{Error, Result};
-use std::{ fs::File, io::Write, fmt::Write as FmtWrite, sync::Arc };
+use std::{ fs::File, io::Write, fmt::Write as FmtWrite };
 
 pub const PRINCIPAL: [u8; 32] = [
     0x75, 0x28, 0xA9, 0xE0, 0x9D, 0x24, 0xBA, 0xB3, 0x79, 0x56, 0x15, 0x68, 0xFD, 0xA4, 0xE2, 0xA4,
@@ -491,47 +491,46 @@ pub(crate) fn make_writecap() -> Result<Writecap> {
         root_writecap, root_key, issued_to, vec!["contacts", "emergency"])
 }
 
-pub(crate) fn make_writecap_trusted_by(
-    next: Writecap, trusting_key: KeyPair, issued_to: Principal, path_components: Vec<&str>
+pub(crate) fn make_writecap_trusted_by<C: Creds>(
+    next: Writecap, trusting_creds: C, issued_to: Principal, path_components: Vec<&str>
 ) -> Result<Writecap> {
     let hour_hence = Epoch::now() + Duration::from_secs(3600);
     let mut writecap = Writecap {
         issued_to,
         path: make_path_with_owner(next.path.owner.clone(), path_components),
         expires: hour_hence,
-        signing_key: trusting_key.public,
+        signing_key: trusting_creds.public().clone(),
         signature: Signature::default(),
         next: Some(Box::from(next)),
     };
-    crypto::sign_writecap(&mut writecap, trusting_key.private.as_ref())
+    crypto::sign_writecap(&mut writecap, &trusting_creds)
         .map_err(convert_err)?;
     Ok(writecap)
 }
 
-pub(crate) fn make_key_pair() -> KeyPair {
-    KeyPair {
-        public: AsymKeyPub::new(AsymKeyKind::Rsa, ROOT_PUBLIC_KEY.as_slice()).unwrap(),
-        private: Arc::new(RsaPriv::new(ROOT_PRIVATE_KEY.as_slice()).unwrap()),
-    }
+pub(crate) fn make_key_pair() -> impl Creds {
+    let public = AsymKeyPub::new(AsymKeyKind::Rsa, ROOT_PUBLIC_KEY.as_slice()).unwrap();
+    let private = RsaPriv::new(ROOT_PRIVATE_KEY.as_slice()).unwrap();
+    ConcreteCreds::new(public, private)
 }
 
-pub(crate) fn make_self_signed_writecap() -> Result<(Writecap, KeyPair)> {
+pub(crate) fn make_self_signed_writecap() -> Result<(Writecap, impl Creds)> {
     let key = make_key_pair();
     Ok((make_self_signed_writecap_with(&key)?, key))
 }
 
-pub(crate) fn make_self_signed_writecap_with(key: &KeyPair) -> Result<Writecap> {
+pub(crate) fn make_self_signed_writecap_with<C: Creds>(key: &C) -> Result<Writecap> {
     let root_principal = key.owner();
     let hour_hence = Epoch::now() + Duration::from_secs(3600);
     let mut writecap = Writecap {
         issued_to: root_principal.clone(),
         path: make_path_with_owner(root_principal, vec![]),
         expires: hour_hence,
-        signing_key: key.public.clone(),
+        signing_key: key.public().clone(),
         signature: Signature::default(),
         next: None,
     };
-    crypto::sign_writecap(&mut writecap, key.private.as_ref())
+    crypto::sign_writecap(&mut writecap, key)
         .map_err(convert_err)?;
     Ok(writecap)
 }