Skip to content

Commit

Permalink
[PM-3133] Cipher List Decryption (#145)
Browse files Browse the repository at this point in the history
* Cipher encryption

* Cipher list decryption

* Cipher Decryption

* Fix formatting

* Restructure folders
  • Loading branch information
dani-garcia authored Aug 1, 2023
1 parent 35c3c66 commit 55bd4f6
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 7 deletions.
7 changes: 7 additions & 0 deletions crates/bitwarden-json/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ impl Client {
MobileCiphersCommand::Decrypt(cmd) => {
self.0.vault().ciphers().decrypt(cmd).await.into_string()
}
MobileCiphersCommand::DecryptList(cmd) => self
.0
.vault()
.ciphers()
.decrypt_list(cmd)
.await
.into_string(),
},
},
},
Expand Down
11 changes: 9 additions & 2 deletions crates/bitwarden-json/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use bitwarden::{
use bitwarden::mobile::{
kdf::PasswordHashRequest,
vault::{
CipherDecryptRequest, CipherEncryptRequest, FolderDecryptListRequest, FolderDecryptRequest,
FolderEncryptRequest,
CipherDecryptListRequest, CipherDecryptRequest, CipherEncryptRequest,
FolderDecryptListRequest, FolderDecryptRequest, FolderEncryptRequest,
},
};

Expand Down Expand Up @@ -269,4 +269,11 @@ pub enum MobileCiphersCommand {
/// Returns: [CipherDecryptResponse](bitwarden::mobile::vault::CipherDecryptResponse)
///
Decrypt(CipherDecryptRequest),
/// > Requires having previously initialized the cryptography parameters
/// Decrypts the provided ciphers. Note that some sensitive fields might not be included in the response.
/// To get them, use `DecryptCipher` for each cipher individually when those fields are needed
///
/// Returns: [CipherDecryptListResponse](bitwarden::mobile::vault::CipherDecryptListResponse)
///
DecryptList(CipherDecryptListRequest),
}
16 changes: 16 additions & 0 deletions crates/bitwarden/src/mobile/vault/ciphers/cipher_decrypt_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::vault::{Cipher, CipherListView};

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CipherDecryptListRequest {
pub ciphers: Vec<Cipher>,
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CipherDecryptListResponse {
pub ciphers: Vec<CipherListView>,
}
2 changes: 2 additions & 0 deletions crates/bitwarden/src/mobile/vault/ciphers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod cipher_decrypt;
mod cipher_decrypt_list;
mod cipher_encrypt;

pub use cipher_decrypt::{CipherDecryptRequest, CipherDecryptResponse};
pub use cipher_decrypt_list::{CipherDecryptListRequest, CipherDecryptListResponse};
pub use cipher_encrypt::{CipherEncryptRequest, CipherEncryptResponse};
15 changes: 13 additions & 2 deletions crates/bitwarden/src/mobile/vault/client_ciphers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::{
};

use super::{
client_vault::ClientVault, CipherDecryptRequest, CipherDecryptResponse, CipherEncryptRequest,
CipherEncryptResponse,
client_vault::ClientVault, CipherDecryptListRequest, CipherDecryptListResponse,
CipherDecryptRequest, CipherDecryptResponse, CipherEncryptRequest, CipherEncryptResponse,
};

pub struct ClientCiphers<'a> {
Expand All @@ -29,6 +29,17 @@ impl<'a> ClientCiphers<'a> {

Ok(CipherDecryptResponse { cipher })
}

pub async fn decrypt_list(
&self,
req: CipherDecryptListRequest,
) -> Result<CipherDecryptListResponse> {
let enc = self.client.get_encryption_settings()?;

let ciphers = req.ciphers.decrypt(enc, &None)?;

Ok(CipherDecryptListResponse { ciphers })
}
}

impl<'a> ClientVault<'a> {
Expand Down
3 changes: 2 additions & 1 deletion crates/bitwarden/src/mobile/vault/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ pub use folders::{
};

pub use ciphers::{
CipherDecryptRequest, CipherDecryptResponse, CipherEncryptRequest, CipherEncryptResponse,
CipherDecryptListRequest, CipherDecryptListResponse, CipherDecryptRequest,
CipherDecryptResponse, CipherEncryptRequest, CipherEncryptResponse,
};
84 changes: 84 additions & 0 deletions crates/bitwarden/src/vault/cipher/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,87 @@ impl Decryptable<CipherView> for Cipher {
})
}
}

impl Cipher {
fn get_decrypted_subtitle(
&self,
enc: &EncryptionSettings,
org_id: &Option<Uuid>,
) -> Result<String> {
Ok(match self.r#type {
CipherType::Login => {
let Some(login) = &self.login else { return Ok(String::new()) };
login.username.decrypt(enc, org_id).unwrap_or_default()
}
CipherType::SecureNote => String::new(),
CipherType::Card => {
let Some(card) = &self.card else { return Ok(String::new()) };
let mut sub_title = String::new();

if let Some(brand) = &card.brand {
sub_title.push_str(&brand.decrypt(enc, org_id)?);
}

if let Some(number) = &card.number {
let number = number.decrypt(enc, org_id)?;
let number_len = number.len();
if number_len > 4 {
if !sub_title.is_empty() {
sub_title.push_str(", ");
}

// On AMEX cards we show 5 digits instead of 4
let digit_count = match &number[0..2] {
"34" | "37" => 5,
_ => 4,
};

sub_title.push_str(&number[(number_len - digit_count)..]);
}
}

sub_title
}
CipherType::Identity => {
let Some(identity) = &self.identity else { return Ok(String::new()) };
let mut sub_title = String::new();

if let Some(first_name) = &identity.first_name {
sub_title.push_str(&first_name.decrypt(enc, org_id)?);
}

if let Some(last_name) = &identity.last_name {
if !sub_title.is_empty() {
sub_title.push(' ');
}
sub_title.push_str(&last_name.decrypt(enc, org_id)?);
}

sub_title
}
})
}
}

impl Decryptable<CipherListView> for Cipher {
fn decrypt(&self, enc: &EncryptionSettings, _: &Option<Uuid>) -> Result<CipherListView> {
let org_id = &self.organization_id;
Ok(CipherListView {
id: self.id,
organization_id: self.organization_id,
folder_id: self.folder_id,
collection_ids: self.collection_ids.clone(),
name: self.name.decrypt(enc, org_id)?,
sub_title: self.get_decrypted_subtitle(enc, org_id)?,
r#type: self.r#type,
favorite: self.favorite,
reprompt: self.reprompt,
edit: self.edit,
view_password: self.view_password,
attachments: self.attachments.len(),
creation_date: self.creation_date,
deleted_date: self.deleted_date,
revision_date: self.revision_date,
})
}
}
2 changes: 1 addition & 1 deletion crates/bitwarden/src/vault/cipher/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ pub(crate) mod login;
pub(crate) mod password_history;
pub(crate) mod secure_note;

pub use cipher::{Cipher, CipherView};
pub use cipher::{Cipher, CipherListView, CipherView};
2 changes: 1 addition & 1 deletion crates/bitwarden/src/vault/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ mod folder;

pub use folder::{Folder, FolderView};

pub use cipher::{Cipher, CipherView};
pub use cipher::{Cipher, CipherListView, CipherView};

0 comments on commit 55bd4f6

Please sign in to comment.