bitwarden_exporters/
export.rs

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