bitwarden_exporters/
export.rs1use bitwarden_collections::collection::Collection;
2use bitwarden_core::{Client, OrganizationId, key_management::KeySlotIds};
3use bitwarden_crypto::{CompositeEncryptable, IdentifyKey, KeyStoreContext};
4use bitwarden_vault::{
5 Cipher, CipherView, EncryptMode, Folder, FolderView, should_use_blob_encryption,
6};
7
8use crate::{
9 ExportError, ExportFormat, ImportingCipher,
10 csv::export_csv,
11 cxf::{Account, build_cxf, parse_cxf},
12 encrypted_json::export_encrypted_json,
13 json::export_json,
14};
15
16pub(crate) async fn export_vault(
17 client: &Client,
18 folders: Vec<Folder>,
19 ciphers: Vec<Cipher>,
20 format: ExportFormat,
21) -> Result<String, ExportError> {
22 let key_store = client.internal.get_key_store();
23
24 let folders: Vec<FolderView> = key_store.decrypt_list(&folders)?;
25 let folders: Vec<crate::Folder> = folders.into_iter().flat_map(|f| f.try_into()).collect();
26
27 let ciphers: Vec<crate::Cipher> = ciphers
28 .into_iter()
29 .flat_map(|c| crate::Cipher::from_cipher(key_store, c))
30 .collect();
31
32 match format {
33 ExportFormat::Csv => Ok(export_csv(folders, ciphers)?),
34 ExportFormat::Json => Ok(export_json(folders, ciphers)?),
35 ExportFormat::EncryptedJson { password } => Ok(export_encrypted_json(
36 folders,
37 ciphers,
38 password,
39 client.internal.get_kdf().await?,
40 )?),
41 }
42}
43
44pub(crate) fn export_organization_vault(
45 _collections: Vec<Collection>,
46 _ciphers: Vec<Cipher>,
47 _format: ExportFormat,
48) -> Result<String, ExportError> {
49 todo!();
50}
51
52pub(crate) fn export_cxf(
54 client: &Client,
55 account: Account,
56 ciphers: Vec<Cipher>,
57) -> Result<String, ExportError> {
58 let key_store = client.internal.get_key_store();
59
60 let mut ciphers: Vec<crate::Cipher> = ciphers
61 .into_iter()
62 .flat_map(|c| crate::Cipher::from_cipher(key_store, c))
63 .collect();
64
65 for cipher in &mut ciphers {
66 if let crate::CipherType::Login(login) = &mut cipher.r#type {
67 login.sanitize_uris();
68 }
69 }
70
71 Ok(build_cxf(account, ciphers)?)
72}
73
74pub fn encrypt_import(
79 ctx: &mut KeyStoreContext<KeySlotIds>,
80 cipher: ImportingCipher,
81 organization_id: Option<OrganizationId>,
82) -> Result<Cipher, ExportError> {
83 let mut view: CipherView = cipher.clone().into();
84 view.organization_id = organization_id;
85
86 let passkey = match cipher.r#type {
88 crate::CipherType::Login(login) => login.fido2_credentials,
89 _ => None,
90 };
91
92 if let Some(passkey) = passkey {
93 let passkeys = passkey.into_iter().map(|p| p.into()).collect();
94
95 view.set_new_fido2_credentials(ctx, passkeys)?;
96 }
97
98 let key = view.key_identifier();
101 let mode = if should_use_blob_encryption(ctx, organization_id) {
102 EncryptMode::Blob(view)
103 } else {
104 EncryptMode::Legacy(view)
105 };
106 let new_cipher = mode.encrypt_composite(ctx, key)?;
107
108 Ok(new_cipher)
109}
110
111pub(crate) fn import_cxf(client: &Client, payload: String) -> Result<Vec<Cipher>, ExportError> {
113 let key_store = client.internal.get_key_store();
114 let mut ctx = key_store.context();
115
116 let ciphers = parse_cxf(payload)?;
117 let ciphers: Result<Vec<Cipher>, _> = ciphers
118 .into_iter()
119 .map(|c| encrypt_import(&mut ctx, c, None))
120 .collect();
121
122 ciphers
123}