From dce70562a1c46e419328cb7507f1c89e3de7041d Mon Sep 17 00:00:00 2001 From: Hinton Date: Mon, 9 Dec 2024 12:51:28 +0100 Subject: [PATCH] Update credential exchange crate --- Cargo.lock | 2 +- crates/bitwarden-exporters/Cargo.toml | 2 +- crates/bitwarden-exporters/src/cxp/export.rs | 58 +++++++------------- crates/bitwarden-exporters/src/cxp/import.rs | 34 +++++++----- 4 files changed, 43 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 178b122e..60c1123c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1026,7 +1026,7 @@ dependencies = [ [[package]] name = "credential-exchange-types" version = "0.1.0" -source = "git+https://github.com/bitwarden/credential-exchange.git?rev=28c947a776f727334598e4ef070292204d4e71d4#28c947a776f727334598e4ef070292204d4e71d4" +source = "git+https://github.com/bitwarden/credential-exchange.git?rev=60bf99f097af72144b0eaa757ccb50fd46049f24#60bf99f097af72144b0eaa757ccb50fd46049f24" dependencies = [ "data-encoding", "jose-jwk", diff --git a/crates/bitwarden-exporters/Cargo.toml b/crates/bitwarden-exporters/Cargo.toml index 463c6562..84241c3a 100644 --- a/crates/bitwarden-exporters/Cargo.toml +++ b/crates/bitwarden-exporters/Cargo.toml @@ -24,7 +24,7 @@ bitwarden-crypto = { workspace = true } bitwarden-fido = { workspace = true } bitwarden-vault = { workspace = true } chrono = { workspace = true, features = ["std"] } -credential-exchange-types = { git = "https://github.com/bitwarden/credential-exchange.git", rev = "28c947a776f727334598e4ef070292204d4e71d4" } +credential-exchange-types = { git = "https://github.com/bitwarden/credential-exchange.git", rev = "60bf99f097af72144b0eaa757ccb50fd46049f24" } csv = "1.3.0" schemars = { workspace = true } serde = { workspace = true } diff --git a/crates/bitwarden-exporters/src/cxp/export.rs b/crates/bitwarden-exporters/src/cxp/export.rs index 073b53c5..ba8bcbcb 100644 --- a/crates/bitwarden-exporters/src/cxp/export.rs +++ b/crates/bitwarden-exporters/src/cxp/export.rs @@ -6,7 +6,7 @@ use bitwarden_vault::{Totp, TotpAlgorithm}; use credential_exchange_types::{ format::{ Account as CxpAccount, BasicAuthCredential, Credential, EditableField, FieldType, Item, - ItemType, OTPHashAlgorithm, PasskeyCredential, + ItemType, NoteCredential, OTPHashAlgorithm, PasskeyCredential, TotpCredential, }, B64Url, }; @@ -54,13 +54,13 @@ impl TryFrom for Item { let mut credentials: Vec = value.r#type.clone().into(); if let Some(note) = value.notes { - credentials.push(Credential::Note { content: note }); + credentials.push(Credential::Note(Box::new(NoteCredential { content: note }))); } Ok(Self { id: value.id.as_bytes().as_slice().into(), - creation_at: value.creation_date.timestamp() as u64, - modified_at: value.revision_date.timestamp() as u64, + creation_at: Some(value.creation_date.timestamp() as u64), + modified_at: Some(value.revision_date.timestamp() as u64), ty: value.r#type.try_into()?, title: value.name, subtitle: None, @@ -97,7 +97,8 @@ impl From for Vec { CipherType::Card(_) => vec![], // TODO(PM-15451): Add support for identities. CipherType::Identity(_) => vec![], - // Secure Notes only contains a note field which is handled by `TryFrom for Item`. + // Secure Notes only contains a note field which is handled by `TryFrom for + // Item`. CipherType::SecureNote(_) => vec![], // TODO(PM-15448): Add support for SSH Keys. CipherType::SshKey(_) => vec![], @@ -110,13 +111,13 @@ impl From for Vec { let mut credentials = vec![]; if login.username.is_some() || login.password.is_some() || !login.login_uris.is_empty() { - credentials.push(Credential::BasicAuth(login.clone().into())); + credentials.push(Credential::BasicAuth(Box::new(login.clone().into()))); } if let Some(totp) = login.totp { if let Ok(totp) = totp.parse::() { // TODO(PM-15389): Properly set username/issuer. - credentials.push(Credential::Totp { + credentials.push(Credential::Totp(Box::new(TotpCredential { secret: totp.secret.into(), period: totp.period as u8, digits: totp.digits as u8, @@ -128,7 +129,7 @@ impl From for Vec { TotpAlgorithm::Steam => OTPHashAlgorithm::Unknown("steam".to_string()), }, issuer: None, - }) + }))) } } @@ -136,7 +137,7 @@ impl From for Vec { for fido2_credential in fido2_credentials { let c = fido2_credential.try_into(); if let Ok(c) = c { - credentials.push(Credential::Passkey(c)) + credentials.push(Credential::Passkey(Box::new(c))) } } } @@ -217,8 +218,6 @@ impl TryFrom for PasskeyCredential { #[cfg(test)] mod tests { - use chrono::{DateTime, Utc}; - use super::*; use crate::{Field, LoginUri}; @@ -299,17 +298,9 @@ mod tests { let item: Item = cipher.try_into().unwrap(); - assert_eq!( - item.creation_at, - "2024-01-30T11:23:54.416Z" - .parse::>() - .unwrap() - .timestamp() as u64 - ); - assert_eq!(item.id.to_string(), "JcjEFLRGSOmhvbEHALvXQA"); - assert_eq!(item.creation_at, 1706613834); - assert_eq!(item.modified_at, 1706623773); + assert_eq!(item.creation_at, Some(1706613834)); + assert_eq!(item.modified_at, Some(1706623773)); assert_eq!(item.ty, ItemType::Login); assert_eq!(item.title, "Bitwarden"); assert_eq!(item.subtitle, None); @@ -343,20 +334,13 @@ mod tests { let credential = &item.credentials[1]; match credential { - Credential::Totp { - secret, - period, - digits, - username, - algorithm, - issuer, - } => { - assert_eq!(String::from(secret.clone()), "JBSWY3DPEHPK3PXP"); - assert_eq!(*period, 30); - assert_eq!(*digits, 6); - assert_eq!(username, ""); - assert_eq!(*algorithm, OTPHashAlgorithm::Sha1); - assert!(issuer.is_none()); + Credential::Totp(totp) => { + assert_eq!(String::from(totp.secret.clone()), "JBSWY3DPEHPK3PXP"); + assert_eq!(totp.period, 30); + assert_eq!(totp.digits, 6); + assert_eq!(totp.username, ""); + assert_eq!(totp.algorithm, OTPHashAlgorithm::Sha1); + assert!(totp.issuer.is_none()); } _ => panic!("Expected Credential::Passkey"), } @@ -379,8 +363,8 @@ mod tests { let credential = &item.credentials[3]; match credential { - Credential::Note { content } => { - assert_eq!(content, "My note"); + Credential::Note(n) => { + assert_eq!(n.content, "My note"); } _ => panic!("Expected Credential::Passkey"), } diff --git a/crates/bitwarden-exporters/src/cxp/import.rs b/crates/bitwarden-exporters/src/cxp/import.rs index bdb7ca3f..7c9ac5e3 100644 --- a/crates/bitwarden-exporters/src/cxp/import.rs +++ b/crates/bitwarden-exporters/src/cxp/import.rs @@ -17,6 +17,15 @@ pub(crate) fn parse_cxf(payload: String) -> Result, CxpErro fn parse_item(value: Item) -> Vec { let grouped = group_credentials_by_type(value.credentials); + let creation_date = value + .creation_at + .and_then(|ts| DateTime::from_timestamp(ts as i64, 0)) + .unwrap_or(Utc::now()); + let revision_date = value + .modified_at + .and_then(|ts| DateTime::from_timestamp(ts as i64, 0)) + .unwrap_or(Utc::now()); + match value.ty { ItemType::Login => { let basic_auth = grouped.basic_auth.first(); @@ -51,8 +60,7 @@ fn parse_item(value: Item) -> Vec { rp_name: Some(p.rp_id.clone()), user_display_name: Some(p.user_display_name.clone()), discoverable: "true".to_string(), - creation_date: DateTime::from_timestamp(value.creation_at as i64, 0) - .unwrap_or(Utc::now()), + creation_date, }] }), }; @@ -65,10 +73,8 @@ fn parse_item(value: Item) -> Vec { favorite: false, reprompt: 0, fields: vec![], - revision_date: DateTime::from_timestamp(value.modified_at as i64, 0) - .unwrap_or(Utc::now()), - creation_date: DateTime::from_timestamp(value.creation_at as i64, 0) - .unwrap_or(Utc::now()), + revision_date, + creation_date, deleted_date: None, }] } @@ -81,14 +87,14 @@ fn group_credentials_by_type(credentials: Vec) -> GroupedCredentials basic_auth: credentials .iter() .filter_map(|c| match c { - Credential::BasicAuth(basic_auth) => Some(basic_auth.clone()), + Credential::BasicAuth(basic_auth) => Some(*basic_auth.clone()), _ => None, }) .collect(), passkey: credentials .iter() .filter_map(|c| match c { - Credential::Passkey(passkey) => Some(passkey.clone()), + Credential::Passkey(passkey) => Some(*passkey.clone()), _ => None, }) .collect(), @@ -108,8 +114,8 @@ mod tests { fn test_parse_item() { let item = Item { id: [0, 1, 2, 3, 4, 5, 6].as_ref().into(), - creation_at: 1706613834, - modified_at: 1706623773, + creation_at: Some(1706613834), + modified_at: Some(1706623773), ty: ItemType::Login, title: "Bitwarden".to_string(), subtitle: None, @@ -135,13 +141,13 @@ mod tests { .unwrap() .as_slice() .into(), - creation_at: 1732181986, - modified_at: 1732182026, + creation_at: Some(1732181986), + modified_at: Some(1732182026), ty: ItemType::Login, title: "opotonniee.github.io".to_string(), subtitle: None, favorite: None, - credentials: vec![Credential::Passkey(PasskeyCredential { + credentials: vec![Credential::Passkey(Box::new(PasskeyCredential { credential_id: URL_SAFE_NO_PAD .decode("6NiHiekW4ZY8vYHa-ucbvA") .unwrap() @@ -161,7 +167,7 @@ mod tests { .as_slice() .into(), fido2_extensions: None, - })], + }))], tags: None, extensions: None, };