From aafefa7f344441c709198e16cd07da11b4651a98 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 3 May 2020 02:53:40 -0400 Subject: also make the agent store decrypted org keys in memory --- Cargo.lock | 1 + Cargo.toml | 1 + src/actions.rs | 36 ++++++++++++++++++++++++++++----- src/bin/rbw-agent/actions.rs | 16 +++++++++++++-- src/cipherstring.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/locked.rs | 14 +++++++++++++ 6 files changed, 109 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2d218b..574145b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1030,6 +1030,7 @@ dependencies = [ "humantime", "log", "nix", + "openssl", "rand", "region", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 52dcb75..b734472 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ env_logger = "0.7" humantime = "1.3" log = "0.4" nix = "0.17" +openssl = "0.10" rand = "0.7" region = "2.1" reqwest = { version = "0.10", features = ["blocking", "json"] } diff --git a/src/actions.rs b/src/actions.rs index b891670..a4b8021 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -32,18 +32,44 @@ pub async fn unlock( password: &crate::locked::Password, iterations: u32, protected_key: &str, -) -> Result { + protected_private_key: &str, + protected_org_keys: &std::collections::HashMap, +) -> Result<( + crate::locked::Keys, + std::collections::HashMap, +)> { let identity = crate::identity::Identity::new(email, password, iterations)?; let protected_key = crate::cipherstring::CipherString::new(protected_key)?; + let key = match protected_key.decrypt_locked_symmetric(&identity.keys) { + Ok(master_keys) => crate::locked::Keys::new(master_keys), + Err(Error::InvalidMac) => return Err(Error::IncorrectPassword), + Err(e) => return Err(e), + }; - match protected_key.decrypt_locked_symmetric(&identity.keys) { - Ok(master_keys) => Ok(crate::locked::Keys::new(master_keys)), - Err(Error::InvalidMac) => Err(Error::IncorrectPassword), - Err(e) => Err(e), + let protected_private_key = + crate::cipherstring::CipherString::new(protected_private_key)?; + let private_key = + match protected_private_key.decrypt_locked_symmetric(&key) { + Ok(private_key) => crate::locked::PrivateKey::new(private_key), + Err(e) => return Err(e), + }; + + let mut org_keys = std::collections::HashMap::new(); + for (org_id, protected_org_key) in protected_org_keys { + let protected_org_key = + crate::cipherstring::CipherString::new(protected_org_key)?; + let org_key = + match protected_org_key.decrypt_locked_asymmetric(&private_key) { + Ok(org_key) => crate::locked::Keys::new(org_key), + Err(e) => return Err(e), + }; + org_keys.insert(org_id.to_string(), org_key); } + + Ok((key, org_keys)) } pub async fn sync( diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs index e24d044..3ca6d51 100644 --- a/src/bin/rbw-agent/actions.rs +++ b/src/bin/rbw-agent/actions.rs @@ -115,6 +115,14 @@ pub async fn unlock( "failed to find protected key in db" )); }; + let protected_private_key = + if let Some(protected_private_key) = db.protected_private_key { + protected_private_key + } else { + return Err(anyhow::anyhow!( + "failed to find protected private key in db" + )); + }; for i in 1u8..=3 { let err = if i > 1 { @@ -135,11 +143,15 @@ pub async fn unlock( &password, iterations, &protected_key, + &protected_private_key, + &db.protected_org_keys, ) .await; match res { - Ok(keys) => { - state.write().await.priv_key = Some(keys); + Ok((keys, org_keys)) => { + let mut state = state.write().await; + state.priv_key = Some(keys); + state.org_keys = org_keys; break; } Err(rbw::error::Error::IncorrectPassword) => { diff --git a/src/cipherstring.rs b/src/cipherstring.rs index 15c9add..7ba64ca 100644 --- a/src/cipherstring.rs +++ b/src/cipherstring.rs @@ -10,6 +10,10 @@ pub enum CipherString { ciphertext: Vec, mac: Option>, }, + Asymmetric { + // ty: 4 (RSA_2048_OAEP_SHA1) + ciphertext: Vec, + }, } impl CipherString { @@ -53,6 +57,11 @@ impl CipherString { mac, }) } + 4 => { + let ciphertext = base64::decode(contents) + .context(crate::error::InvalidBase64)?; + Ok(Self::Asymmetric { ciphertext }) + } _ => Err(Error::InvalidCipherString), } } @@ -106,6 +115,7 @@ impl CipherString { .decrypt_vec(ciphertext) .context(crate::error::Decrypt) } + _ => Err(Error::InvalidCipherString), } } @@ -132,6 +142,40 @@ impl CipherString { .context(crate::error::Decrypt)?; Ok(res) } + _ => Err(Error::InvalidCipherString), + } + } + + pub fn decrypt_locked_asymmetric( + &self, + private_key: &crate::locked::PrivateKey, + ) -> Result { + match self { + Self::Asymmetric { ciphertext } => { + // ring doesn't currently support asymmetric encryption (only + // signatures). see + // https://github.com/briansmith/ring/issues/691 + let pkey = openssl::pkey::PKey::private_key_from_pkcs8( + private_key.private_key(), + ) + .unwrap(); // XXX + let rsa = pkey.rsa().unwrap(); // XXX + + let mut res = crate::locked::Vec::new(); + res.extend(std::iter::repeat(0).take(rsa.size() as usize)); + + let bytes = rsa + .private_decrypt( + ciphertext, + res.data_mut(), + openssl::rsa::Padding::PKCS1_OAEP, + ) + .unwrap(); // XXX + res.truncate(bytes); + + Ok(res) + } + _ => Err(Error::InvalidCipherString), } } } @@ -183,6 +227,10 @@ impl std::fmt::Display for CipherString { write!(f, "2.{}|{}", iv, ciphertext) } } + Self::Asymmetric { ciphertext } => { + let ciphertext = base64::encode(&ciphertext); + write!(f, "4.{}", ciphertext) + } } } } diff --git a/src/locked.rs b/src/locked.rs index 4db0423..3cad927 100644 --- a/src/locked.rs +++ b/src/locked.rs @@ -89,3 +89,17 @@ impl PasswordHash { self.hash.data() } } + +pub struct PrivateKey { + private_key: Vec, +} + +impl PrivateKey { + pub fn new(private_key: Vec) -> Self { + Self { private_key } + } + + pub fn private_key(&self) -> &[u8] { + self.private_key.data() + } +} -- cgit v1.2.3-54-g00ecf