Skip to main content

bitwarden_exporters/
export.rs

1use 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
52/// See [crate::ExporterClient::export_cxf] for more documentation.
53pub(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
74/// Encrypts a parsed/imported cipher for the user's vault, or for an organization when
75/// `organization_id` is set. Shared by the importers (`import_kdbx`) and by CXF import; lives here
76/// alongside the `ImportingCipher` interchange model and the `From<ImportingCipher> for CipherView`
77/// bridge.
78pub 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    // Get passkey from cipher if cipher is type login
87    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    // Select the encryption format based on the account's current security state, matching how
99    // regular cipher saves choose between the blob and legacy field-level formats.
100    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
111/// See [crate::ExporterClient::import_cxf] for more documentation.
112pub(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}