From e89ecaf0792dea1d36b6f071cb32bf79665c8e37 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 3 May 2020 01:19:49 -0400 Subject: refactor encrypt/decrypt methods to indicate symmetric encryption since we're going to have to also implement asymmetric encryption --- src/actions.rs | 4 +- src/bin/rbw-agent/actions.rs | 10 ++- src/cipherstring.rs | 197 +++++++++++++++++++++++++------------------ 3 files changed, 124 insertions(+), 87 deletions(-) diff --git a/src/actions.rs b/src/actions.rs index 861d734..9284677 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -16,7 +16,7 @@ pub async fn login( .login(&identity.email, &identity.master_password_hash) .await?; let master_keys = crate::cipherstring::CipherString::new(&protected_key)? - .decrypt_locked(&identity.keys)?; + .decrypt_locked_symmetric(&identity.keys)?; Ok(( access_token, @@ -39,7 +39,7 @@ pub async fn unlock( let protected_key = crate::cipherstring::CipherString::new(protected_key)?; - match protected_key.decrypt_locked(&identity.keys) { + 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), diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs index d17f5f1..5b3444d 100644 --- a/src/bin/rbw-agent/actions.rs +++ b/src/bin/rbw-agent/actions.rs @@ -226,7 +226,7 @@ pub async fn decrypt( .context("failed to parse encrypted secret")?; let plaintext = String::from_utf8( cipherstring - .decrypt(&keys) + .decrypt_symmetric(&keys) .context("failed to decrypt encrypted secret")?, ) .context("failed to parse decrypted secret")?; @@ -250,9 +250,11 @@ pub async fn encrypt( "failed to find encryption keys in in-memory state" )); }; - let cipherstring = - rbw::cipherstring::CipherString::encrypt(keys, plaintext.as_bytes()) - .context("failed to encrypt plaintext secret")?; + let cipherstring = rbw::cipherstring::CipherString::encrypt_symmetric( + keys, + plaintext.as_bytes(), + ) + .context("failed to encrypt plaintext secret")?; respond_encrypt(sock, cipherstring.to_string()).await?; diff --git a/src/cipherstring.rs b/src/cipherstring.rs index 09d5c88..15c9add 100644 --- a/src/cipherstring.rs +++ b/src/cipherstring.rs @@ -3,11 +3,13 @@ use crate::prelude::*; use block_modes::BlockMode as _; use rand::RngCore as _; -pub struct CipherString { - ty: u8, - iv: Vec, - ciphertext: Vec, - mac: Option>, +pub enum CipherString { + Symmetric { + // ty: 2 (AES_256_CBC_HMAC_SHA256) + iv: Vec, + ciphertext: Vec, + mac: Option>, + }, } impl CipherString { @@ -25,33 +27,37 @@ impl CipherString { let ty = ty[0] - b'0'; let contents = parts[1]; - let parts: Vec<&str> = contents.split('|').collect(); - if parts.len() < 2 || parts.len() > 3 { - return Err(Error::InvalidCipherString); + match ty { + 2 => { + let parts: Vec<&str> = contents.split('|').collect(); + if parts.len() < 2 || parts.len() > 3 { + return Err(Error::InvalidCipherString); + } + + let iv = base64::decode(parts[0]) + .context(crate::error::InvalidBase64)?; + let ciphertext = base64::decode(parts[1]) + .context(crate::error::InvalidBase64)?; + let mac = if parts.len() > 2 { + Some( + base64::decode(parts[2]) + .context(crate::error::InvalidBase64)?, + ) + } else { + None + }; + + Ok(Self::Symmetric { + iv, + ciphertext, + mac, + }) + } + _ => Err(Error::InvalidCipherString), } - - let iv = - base64::decode(parts[0]).context(crate::error::InvalidBase64)?; - let ciphertext = - base64::decode(parts[1]).context(crate::error::InvalidBase64)?; - let mac = if parts.len() > 2 { - Some( - base64::decode(parts[2]) - .context(crate::error::InvalidBase64)?, - ) - } else { - None - }; - - Ok(Self { - ty, - iv, - ciphertext, - mac, - }) } - pub fn encrypt( + pub fn encrypt_symmetric( keys: &crate::locked::Keys, plaintext: &[u8], ) -> Result { @@ -73,81 +79,110 @@ impl CipherString { digest.update(&ciphertext); let mac = digest.sign().as_ref().to_vec(); - Ok(Self { - ty: 2, + Ok(Self::Symmetric { iv, ciphertext, mac: Some(mac), }) } - pub fn decrypt(&self, keys: &crate::locked::Keys) -> Result> { - let cipher = self.decrypt_common(keys)?; - cipher - .decrypt_vec(&self.ciphertext) - .context(crate::error::Decrypt) - } - - pub fn decrypt_locked( + pub fn decrypt_symmetric( &self, keys: &crate::locked::Keys, - ) -> Result { - let mut res = crate::locked::Vec::new(); - res.extend(self.ciphertext.iter().copied()); - let cipher = self.decrypt_common(keys)?; - cipher - .decrypt(res.data_mut()) - .context(crate::error::Decrypt)?; - Ok(res) + ) -> Result> { + match self { + Self::Symmetric { + iv, + ciphertext, + mac, + } => { + let cipher = decrypt_common_symmetric( + keys, + iv, + ciphertext, + mac.as_deref(), + )?; + cipher + .decrypt_vec(ciphertext) + .context(crate::error::Decrypt) + } + } } - fn decrypt_common( + pub fn decrypt_locked_symmetric( &self, keys: &crate::locked::Keys, - ) -> Result< - block_modes::Cbc, - > { - if self.ty != 2 { - unimplemented!() + ) -> Result { + match self { + Self::Symmetric { + iv, + ciphertext, + mac, + } => { + let mut res = crate::locked::Vec::new(); + res.extend(ciphertext.iter().copied()); + let cipher = decrypt_common_symmetric( + keys, + iv, + ciphertext, + mac.as_deref(), + )?; + cipher + .decrypt(res.data_mut()) + .context(crate::error::Decrypt)?; + Ok(res) + } } + } +} - if let Some(mac) = &self.mac { - let key = - ring::hmac::Key::new(ring::hmac::HMAC_SHA256, keys.mac_key()); - // it'd be nice to not have to pull this into a vec, but ring - // doesn't currently support non-contiguous verification. see - // https://github.com/briansmith/ring/issues/615 - let data: Vec<_> = self - .iv - .iter() - .chain(self.ciphertext.iter()) - .copied() - .collect(); - - if ring::hmac::verify(&key, &data, mac).is_err() { - return Err(Error::InvalidMac); - } +fn decrypt_common_symmetric( + keys: &crate::locked::Keys, + iv: &[u8], + ciphertext: &[u8], + mac: Option<&[u8]>, +) -> Result> +{ + if let Some(mac) = mac { + let key = + ring::hmac::Key::new(ring::hmac::HMAC_SHA256, keys.mac_key()); + // it'd be nice to not have to pull this into a vec, but ring + // doesn't currently support non-contiguous verification. see + // https://github.com/briansmith/ring/issues/615 + let data: Vec<_> = + iv.iter().chain(ciphertext.iter()).copied().collect(); + + if ring::hmac::verify(&key, &data, mac).is_err() { + return Err(Error::InvalidMac); } + } - // ring doesn't currently support CBC ciphers, so we have to do it - // manually. see https://github.com/briansmith/ring/issues/588 - Ok(block_modes::Cbc::< + // ring doesn't currently support CBC ciphers, so we have to do it + // manually. see https://github.com/briansmith/ring/issues/588 + Ok(block_modes::Cbc::< aes::Aes256, block_modes::block_padding::Pkcs7, - >::new_var(keys.enc_key(), &self.iv) + >::new_var(keys.enc_key(), iv) .context(crate::error::CreateBlockMode)?) - } } impl std::fmt::Display for CipherString { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let iv = base64::encode(&self.iv); - let ciphertext = base64::encode(&self.ciphertext); - if let Some(mac) = &self.mac { - let mac = base64::encode(&mac); - write!(f, "{}.{}|{}|{}", self.ty, iv, ciphertext, mac) - } else { - write!(f, "{}.{}|{}", self.ty, iv, ciphertext) + match self { + Self::Symmetric { + iv, + ciphertext, + mac, + } => { + let iv = base64::encode(&iv); + let ciphertext = base64::encode(&ciphertext); + if let Some(mac) = &mac { + let mac = base64::encode(&mac); + write!(f, "2.{}|{}|{}", iv, ciphertext, mac) + } else { + write!(f, "2.{}|{}", iv, ciphertext) + } + } } } } -- cgit v1.2.3-54-g00ecf