Skip to content

Commit

Permalink
[PM-3138] Send encryption (#211)
Browse files Browse the repository at this point in the history
* Send encryption

* Add tests

* Create get_encryption helper

* Reduce visibility of get_key
  • Loading branch information
dani-garcia authored Sep 1, 2023
1 parent e10753b commit 265588c
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 10 deletions.
48 changes: 48 additions & 0 deletions crates/bitwarden/src/crypto/enc_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,31 @@ impl EncString {
.into()),
}
}

#[cfg(feature = "mobile")]
pub(crate) fn to_buffer(&self) -> Result<Vec<u8>> {
let mut buf;

match self {
EncString::AesCbc256_B64 { iv, data } => {
buf = Vec::with_capacity(1 + 16 + data.len());
buf.push(self.enc_type());
buf.extend_from_slice(iv);
buf.extend_from_slice(data);
}
EncString::AesCbc128_HmacSha256_B64 { iv, mac, data }
| EncString::AesCbc256_HmacSha256_B64 { iv, mac, data } => {
buf = Vec::with_capacity(1 + 16 + 32 + data.len());
buf.push(self.enc_type());
buf.extend_from_slice(iv);
buf.extend_from_slice(mac);
buf.extend_from_slice(data);
}
_ => todo!(),
}

Ok(buf)
}
}

impl Display for EncString {
Expand Down Expand Up @@ -302,4 +327,27 @@ mod tests {
assert_eq!(t.key.to_string(), cipher);
assert_eq!(serde_json::to_string(&t).unwrap(), serialized);
}

#[cfg(feature = "mobile")]
#[test]
fn test_enc_from_to_buffer() {
let enc_str: &str = "2.pMS6/icTQABtulw52pq2lg==|XXbxKxDTh+mWiN1HjH2N1w==|Q6PkuT+KX/axrgN9ubD5Ajk2YNwxQkgs3WJM0S0wtG8=";
let enc_string: EncString = enc_str.parse().unwrap();

let enc_buf = enc_string.to_buffer().unwrap();

assert_eq!(
enc_buf,
vec![
2, 164, 196, 186, 254, 39, 19, 64, 0, 109, 186, 92, 57, 218, 154, 182, 150, 67,
163, 228, 185, 63, 138, 95, 246, 177, 174, 3, 125, 185, 176, 249, 2, 57, 54, 96,
220, 49, 66, 72, 44, 221, 98, 76, 209, 45, 48, 180, 111, 93, 118, 241, 43, 16, 211,
135, 233, 150, 136, 221, 71, 140, 125, 141, 215
]
);

let enc_string_new = EncString::from_buffer(&enc_buf).unwrap();

assert_eq!(enc_string_new.to_string(), enc_str)
}
}
34 changes: 30 additions & 4 deletions crates/bitwarden/src/mobile/vault/client_sends.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::path::Path;

use crate::{
client::encryption_settings::EncryptionSettings,
crypto::{Decryptable, EncString},
crypto::{Decryptable, EncString, Encryptable},
error::Result,
vault::{Send, SendListView, SendView},
Client,
Expand Down Expand Up @@ -45,13 +44,40 @@ impl<'a> ClientSends<'a> {

pub async fn decrypt_buffer(&self, send: Send, encrypted_buffer: &[u8]) -> Result<Vec<u8>> {
let enc = self.client.get_encryption_settings()?;
let key = Send::get_key(&send.key, enc, &None)?;
let enc = EncryptionSettings::new_single_key(key);
let enc = Send::get_encryption(&send.key, enc, &None)?;

let buf = EncString::from_buffer(encrypted_buffer)?;

enc.decrypt_bytes(&buf, &None)
}

pub async fn encrypt(&self, send_view: SendView) -> Result<Send> {
let enc = self.client.get_encryption_settings()?;

let send = send_view.encrypt(enc, &None)?;

Ok(send)
}

pub async fn encrypt_file(
&self,
send: Send,
decrypted_file_path: &Path,
encrypted_file_path: &Path,
) -> Result<()> {
let data = std::fs::read(decrypted_file_path).unwrap();
let encrypted = self.encrypt_buffer(send, &data).await?;
std::fs::write(encrypted_file_path, encrypted)?;
Ok(())
}

pub async fn encrypt_buffer(&self, send: Send, buffer: &[u8]) -> Result<Vec<u8>> {
let enc = self.client.get_encryption_settings()?;
let enc = Send::get_encryption(&send.key, enc, &None)?;

let enc = enc.encrypt(buffer, &None)?;
enc.to_buffer()
}
}

impl<'a> ClientVault<'a> {
Expand Down
73 changes: 67 additions & 6 deletions crates/bitwarden/src/vault/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use uuid::Uuid;

use crate::{
client::encryption_settings::EncryptionSettings,
crypto::{derive_shareable_key, Decryptable, EncString, SymmetricCryptoKey},
crypto::{derive_shareable_key, Decryptable, EncString, Encryptable, SymmetricCryptoKey},
error::Result,
};

Expand Down Expand Up @@ -126,7 +126,7 @@ pub struct SendListView {
}

impl Send {
pub(crate) fn get_key(
fn get_key(
key: &EncString,
enc: &EncryptionSettings,
org_id: &Option<Uuid>,
Expand All @@ -135,6 +135,15 @@ impl Send {
let key = derive_shareable_key(key.try_into().unwrap(), "send", Some("send"));
Ok(key)
}

pub(crate) fn get_encryption(
key: &EncString,
enc: &EncryptionSettings,
org_id: &Option<Uuid>,
) -> Result<EncryptionSettings> {
let key = Send::get_key(key, enc, org_id)?;
Ok(EncryptionSettings::new_single_key(key))
}
}

impl Decryptable<SendTextView> for SendText {
Expand All @@ -146,6 +155,15 @@ impl Decryptable<SendTextView> for SendText {
}
}

impl Encryptable<SendText> for SendTextView {
fn encrypt(self, enc: &EncryptionSettings, org_id: &Option<Uuid>) -> Result<SendText> {
Ok(SendText {
text: self.text.encrypt(enc, org_id)?,
hidden: self.hidden,
})
}
}

impl Decryptable<SendFileView> for SendFile {
fn decrypt(&self, enc: &EncryptionSettings, org_id: &Option<Uuid>) -> Result<SendFileView> {
Ok(SendFileView {
Expand All @@ -157,11 +175,21 @@ impl Decryptable<SendFileView> for SendFile {
}
}

impl Encryptable<SendFile> for SendFileView {
fn encrypt(self, enc: &EncryptionSettings, org_id: &Option<Uuid>) -> Result<SendFile> {
Ok(SendFile {
id: self.id.clone(),
file_name: self.file_name.encrypt(enc, org_id)?,
size: self.size.clone(),
size_name: self.size_name.clone(),
})
}
}

impl Decryptable<SendView> for Send {
fn decrypt(&self, enc: &EncryptionSettings, org_id: &Option<Uuid>) -> Result<SendView> {
// For sends, we first decrypt the send key with the user key, and stretch it to it's full size
let key = Send::get_key(&self.key, enc, org_id)?;
let enc_owned = EncryptionSettings::new_single_key(key);
let enc_owned = Send::get_encryption(&self.key, enc, org_id)?;

// For the rest of the fields, we ignore the provided EncryptionSettings and use a new one with the stretched key
let enc = &enc_owned;
Expand Down Expand Up @@ -194,8 +222,7 @@ impl Decryptable<SendView> for Send {
impl Decryptable<SendListView> for Send {
fn decrypt(&self, enc: &EncryptionSettings, org_id: &Option<Uuid>) -> Result<SendListView> {
// For sends, we first decrypt the send key with the user key, and stretch it to it's full size
let key = Send::get_key(&self.key, enc, org_id)?;
let enc_owned = EncryptionSettings::new_single_key(key);
let enc_owned = Send::get_encryption(&self.key, enc, org_id)?;

// For the rest of the fields, we ignore the provided EncryptionSettings and use a new one with the stretched key
let enc = &enc_owned;
Expand All @@ -216,6 +243,40 @@ impl Decryptable<SendListView> for Send {
}
}

impl Encryptable<Send> for SendView {
fn encrypt(self, enc: &EncryptionSettings, org_id: &Option<Uuid>) -> Result<Send> {
// For sends, we first decrypt the send key with the user key, and stretch it to it's full size
let key = Send::get_key(&self.key, enc, org_id)?;
let enc_owned = EncryptionSettings::new_single_key(key);

// For the rest of the fields, we ignore the provided EncryptionSettings and use a new one with the stretched key
let enc = &enc_owned;

Ok(Send {
id: self.id,
access_id: self.access_id,

name: self.name.encrypt(enc, org_id)?,
notes: self.notes.encrypt(enc, org_id)?,
key: self.key.clone(),
password: self.password.clone(),

r#type: self.r#type,
file: self.file.encrypt(enc, org_id)?,
text: self.text.encrypt(enc, org_id)?,

max_access_count: self.max_access_count,
access_count: self.access_count,
disabled: self.disabled,
hide_email: self.hide_email,

revision_date: self.revision_date,
deletion_date: self.deletion_date,
expiration_date: self.expiration_date,
})
}
}

#[cfg(test)]
mod tests {
use super::Send;
Expand Down

0 comments on commit 265588c

Please sign in to comment.