Skip to content

Commit d553d37

Browse files
gbubemismithdani-garciaHintonJingo88renovate[bot]
authored
[PM-25818] Migrate Basic Cipher Create, Edit, and Get Operations to SDK (#455)
Co-authored-by: Daniel García <[email protected]> Co-authored-by: Oscar Hinton <[email protected]> Co-authored-by: Jason Ng <[email protected]> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Nik Gilmore <[email protected]>
1 parent a4a1acd commit d553d37

File tree

15 files changed

+1802
-24
lines changed

15 files changed

+1802
-24
lines changed

crates/bitwarden-vault/src/cipher/attachment.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use bitwarden_api_api::models::CipherAttachmentModel;
12
use bitwarden_core::key_management::{KeyIds, SymmetricKeyId};
23
use bitwarden_crypto::{
34
CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext,
@@ -25,6 +26,15 @@ pub struct Attachment {
2526
pub key: Option<EncString>,
2627
}
2728

29+
impl From<Attachment> for CipherAttachmentModel {
30+
fn from(attachment: Attachment) -> Self {
31+
Self {
32+
file_name: attachment.file_name.map(|f| f.to_string()),
33+
key: attachment.key.map(|k| k.to_string()),
34+
}
35+
}
36+
}
37+
2838
#[allow(missing_docs)]
2939
#[derive(Serialize, Deserialize, Debug, Clone)]
3040
#[serde(rename_all = "camelCase", deny_unknown_fields)]
@@ -51,6 +61,34 @@ pub struct AttachmentView {
5161
pub decrypted_key: Option<String>,
5262
}
5363

64+
impl AttachmentView {
65+
pub(crate) fn reencrypt_key(
66+
&mut self,
67+
ctx: &mut KeyStoreContext<KeyIds>,
68+
old_key: SymmetricKeyId,
69+
new_key: SymmetricKeyId,
70+
) -> Result<(), CryptoError> {
71+
if let Some(attachment_key) = &mut self.key {
72+
let tmp_attachment_key_id = SymmetricKeyId::Local("attachment_key");
73+
ctx.unwrap_symmetric_key(old_key, tmp_attachment_key_id, attachment_key)?;
74+
*attachment_key = ctx.wrap_symmetric_key(new_key, tmp_attachment_key_id)?;
75+
}
76+
Ok(())
77+
}
78+
79+
pub(crate) fn reencrypt_keys(
80+
attachment_views: &mut Vec<AttachmentView>,
81+
ctx: &mut KeyStoreContext<KeyIds>,
82+
old_key: SymmetricKeyId,
83+
new_key: SymmetricKeyId,
84+
) -> Result<(), CryptoError> {
85+
for attachment in attachment_views {
86+
attachment.reencrypt_key(ctx, old_key, new_key)?;
87+
}
88+
Ok(())
89+
}
90+
}
91+
5492
#[allow(missing_docs)]
5593
#[derive(Serialize, Deserialize, Debug)]
5694
#[serde(rename_all = "camelCase", deny_unknown_fields)]

crates/bitwarden-vault/src/cipher/card.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,19 @@ impl TryFrom<CipherCardModel> for Card {
127127
}
128128
}
129129

130+
impl From<Card> for bitwarden_api_api::models::CipherCardModel {
131+
fn from(card: Card) -> Self {
132+
Self {
133+
cardholder_name: card.cardholder_name.map(|n| n.to_string()),
134+
brand: card.brand.map(|b| b.to_string()),
135+
number: card.number.map(|n| n.to_string()),
136+
exp_month: card.exp_month.map(|m| m.to_string()),
137+
exp_year: card.exp_year.map(|y| y.to_string()),
138+
code: card.code.map(|c| c.to_string()),
139+
}
140+
}
141+
}
142+
130143
impl CipherKind for Card {
131144
fn decrypt_subtitle(
132145
&self,

crates/bitwarden-vault/src/cipher/cipher.rs

Lines changed: 87 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bitwarden_api_api::models::CipherDetailsResponseModel;
1+
use bitwarden_api_api::models::{CipherDetailsResponseModel, CipherResponseModel};
22
use bitwarden_collections::collection::CollectionId;
33
use bitwarden_core::{
44
MissingFieldError, OrganizationId, UserId,
@@ -30,8 +30,8 @@ use super::{
3030
secure_note, ssh_key,
3131
};
3232
use crate::{
33-
EncryptError, Fido2CredentialFullView, Fido2CredentialView, FolderId, Login, LoginView,
34-
VaultParseError, password_history,
33+
AttachmentView, EncryptError, Fido2CredentialFullView, Fido2CredentialView, FolderId, Login,
34+
LoginView, VaultParseError, password_history,
3535
};
3636

3737
uuid_newtype!(pub CipherId);
@@ -79,11 +79,12 @@ pub enum CipherType {
7979
}
8080

8181
#[allow(missing_docs)]
82-
#[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug, PartialEq)]
82+
#[derive(Clone, Copy, Default, Serialize_repr, Deserialize_repr, Debug, PartialEq)]
8383
#[repr(u8)]
8484
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
8585
#[cfg_attr(feature = "wasm", wasm_bindgen)]
8686
pub enum CipherRepromptType {
87+
#[default]
8788
None = 0,
8889
Password = 1,
8990
}
@@ -472,10 +473,8 @@ impl CipherView {
472473

473474
#[allow(missing_docs)]
474475
pub fn generate_checksums(&mut self) {
475-
if let Some(uris) = self.login.as_mut().and_then(|l| l.uris.as_mut()) {
476-
for uri in uris {
477-
uri.generate_checksum();
478-
}
476+
if let Some(l) = self.login.as_mut() {
477+
l.generate_checksums();
479478
}
480479
}
481480

@@ -493,13 +492,7 @@ impl CipherView {
493492
new_key: SymmetricKeyId,
494493
) -> Result<(), CryptoError> {
495494
if let Some(attachments) = &mut self.attachments {
496-
for attachment in attachments {
497-
if let Some(attachment_key) = &mut attachment.key {
498-
let tmp_attachment_key_id = SymmetricKeyId::Local("attachment_key");
499-
ctx.unwrap_symmetric_key(old_key, tmp_attachment_key_id, attachment_key)?;
500-
*attachment_key = ctx.wrap_symmetric_key(new_key, tmp_attachment_key_id)?;
501-
}
502-
}
495+
AttachmentView::reencrypt_keys(attachments, ctx, old_key, new_key)?;
503496
}
504497
Ok(())
505498
}
@@ -528,11 +521,7 @@ impl CipherView {
528521
new_key: SymmetricKeyId,
529522
) -> Result<(), CryptoError> {
530523
if let Some(login) = self.login.as_mut() {
531-
if let Some(fido2_credentials) = &mut login.fido2_credentials {
532-
let dec_fido2_credentials: Vec<Fido2CredentialFullView> =
533-
fido2_credentials.decrypt(ctx, old_key)?;
534-
*fido2_credentials = dec_fido2_credentials.encrypt_composite(ctx, new_key)?;
535-
}
524+
login.reencrypt_fido2_credentials(ctx, old_key, new_key)?;
536525
}
537526
Ok(())
538527
}
@@ -695,6 +684,15 @@ impl Decryptable<KeyIds, SymmetricKeyId, CipherListView> for Cipher {
695684
}
696685
}
697686

687+
#[cfg(feature = "wasm")]
688+
impl wasm_bindgen::__rt::VectorIntoJsValue for CipherView {
689+
fn vector_into_jsvalue(
690+
vector: wasm_bindgen::__rt::std::boxed::Box<[Self]>,
691+
) -> wasm_bindgen::JsValue {
692+
wasm_bindgen::__rt::js_value_vector_into_jsvalue(vector)
693+
}
694+
}
695+
698696
impl IdentifyKey<SymmetricKeyId> for Cipher {
699697
fn key_identifier(&self) -> SymmetricKeyId {
700698
match self.organization_id {
@@ -796,6 +794,75 @@ impl From<bitwarden_api_api::models::CipherRepromptType> for CipherRepromptType
796794
}
797795
}
798796

797+
impl From<CipherType> for bitwarden_api_api::models::CipherType {
798+
fn from(t: CipherType) -> Self {
799+
match t {
800+
CipherType::Login => bitwarden_api_api::models::CipherType::Login,
801+
CipherType::SecureNote => bitwarden_api_api::models::CipherType::SecureNote,
802+
CipherType::Card => bitwarden_api_api::models::CipherType::Card,
803+
CipherType::Identity => bitwarden_api_api::models::CipherType::Identity,
804+
CipherType::SshKey => bitwarden_api_api::models::CipherType::SSHKey,
805+
}
806+
}
807+
}
808+
809+
impl From<CipherRepromptType> for bitwarden_api_api::models::CipherRepromptType {
810+
fn from(t: CipherRepromptType) -> Self {
811+
match t {
812+
CipherRepromptType::None => bitwarden_api_api::models::CipherRepromptType::None,
813+
CipherRepromptType::Password => bitwarden_api_api::models::CipherRepromptType::Password,
814+
}
815+
}
816+
}
817+
818+
impl TryFrom<CipherResponseModel> for Cipher {
819+
type Error = VaultParseError;
820+
821+
fn try_from(cipher: CipherResponseModel) -> Result<Self, Self::Error> {
822+
Ok(Self {
823+
id: cipher.id.map(CipherId::new),
824+
organization_id: cipher.organization_id.map(OrganizationId::new),
825+
folder_id: cipher.folder_id.map(FolderId::new),
826+
collection_ids: vec![], // CipherResponseModel doesn't include collection_ids
827+
name: require!(cipher.name).parse()?,
828+
notes: EncString::try_from_optional(cipher.notes)?,
829+
r#type: require!(cipher.r#type).into(),
830+
login: cipher.login.map(|l| (*l).try_into()).transpose()?,
831+
identity: cipher.identity.map(|i| (*i).try_into()).transpose()?,
832+
card: cipher.card.map(|c| (*c).try_into()).transpose()?,
833+
secure_note: cipher.secure_note.map(|s| (*s).try_into()).transpose()?,
834+
ssh_key: cipher.ssh_key.map(|s| (*s).try_into()).transpose()?,
835+
favorite: cipher.favorite.unwrap_or(false),
836+
reprompt: cipher
837+
.reprompt
838+
.map(|r| r.into())
839+
.unwrap_or(CipherRepromptType::None),
840+
organization_use_totp: cipher.organization_use_totp.unwrap_or(false),
841+
edit: cipher.edit.unwrap_or(false),
842+
permissions: cipher.permissions.map(|p| (*p).try_into()).transpose()?,
843+
view_password: cipher.view_password.unwrap_or(true),
844+
local_data: None, // Not sent from server
845+
attachments: cipher
846+
.attachments
847+
.map(|a| a.into_iter().map(|a| a.try_into()).collect())
848+
.transpose()?,
849+
fields: cipher
850+
.fields
851+
.map(|f| f.into_iter().map(|f| f.try_into()).collect())
852+
.transpose()?,
853+
password_history: cipher
854+
.password_history
855+
.map(|p| p.into_iter().map(|p| p.try_into()).collect())
856+
.transpose()?,
857+
creation_date: require!(cipher.creation_date).parse()?,
858+
deleted_date: cipher.deleted_date.map(|d| d.parse()).transpose()?,
859+
revision_date: require!(cipher.revision_date).parse()?,
860+
key: EncString::try_from_optional(cipher.key)?,
861+
archived_date: cipher.archived_date.map(|d| d.parse()).transpose()?,
862+
})
863+
}
864+
}
865+
799866
#[cfg(test)]
800867
mod tests {
801868

0 commit comments

Comments
 (0)