aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2020-05-03 01:19:49 -0400
committerJesse Luehrs <doy@tozt.net>2020-05-03 01:54:08 -0400
commite89ecaf0792dea1d36b6f071cb32bf79665c8e37 (patch)
tree773f85d0c08691a795cb512986ce6650054de66b
parente28b23f713fda315d28aaf6a375a720aae166f78 (diff)
downloadrbw-e89ecaf0792dea1d36b6f071cb32bf79665c8e37.tar.gz
rbw-e89ecaf0792dea1d36b6f071cb32bf79665c8e37.zip
refactor encrypt/decrypt methods to indicate symmetric encryption
since we're going to have to also implement asymmetric encryption
-rw-r--r--src/actions.rs4
-rw-r--r--src/bin/rbw-agent/actions.rs10
-rw-r--r--src/cipherstring.rs197
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<u8>,
- ciphertext: Vec<u8>,
- mac: Option<Vec<u8>>,
+pub enum CipherString {
+ Symmetric {
+ // ty: 2 (AES_256_CBC_HMAC_SHA256)
+ iv: Vec<u8>,
+ ciphertext: Vec<u8>,
+ mac: Option<Vec<u8>>,
+ },
}
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<Self> {
@@ -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<Vec<u8>> {
- 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<crate::locked::Vec> {
- 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<Vec<u8>> {
+ 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<aes::Aes256, block_modes::block_padding::Pkcs7>,
- > {
- if self.ty != 2 {
- unimplemented!()
+ ) -> Result<crate::locked::Vec> {
+ 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<block_modes::Cbc<aes::Aes256, block_modes::block_padding::Pkcs7>>
+{
+ 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)
+ }
+ }
}
}
}