Skip to main content

bitwarden_exporters/
export.rs

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