Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions crates/bitwarden-exporters/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# bitwarden-exporters

Read any available documentation: [README.md](./README.md) for architecture and
[resources/](./resources/) for test fixtures.

## Critical Rules

**Export types are separate from vault types**: `Cipher`, `Folder`, and `ImportingCipher` in this
crate are intentionally duplicated from `bitwarden-vault` to maintain a stable export API. Do not
replace them with vault types directly.

**CSV only supports Login and SecureNote**: Cards, identities, and SSH keys are silently skipped
during CSV export. This is intentional β€” the format cannot represent them.

**Encrypted JSON uses account KDF settings**: The password-protected export derives its encryption
key using the user's configured KDF (PBKDF2 or Argon2id). The `Client` must have KDF parameters
available or the export will fail.

**CXF import must manually encrypt fido2 credentials**: When importing CXF payloads containing
passkeys, the import path calls `set_new_fido2_credentials` to encrypt them via the
`KeyStoreContext` before returning. Do not skip this step.
109 changes: 109 additions & 0 deletions crates/bitwarden-exporters/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,112 @@
# Bitwarden Exporters

Contains the export and import functionality for Bitwarden Password Manager.

## Supported formats

| Format | Export | Import | Source |
| ------------------- | ------ | ------ | ----------------------- |
| CSV | Yes | No | `src/csv.rs` |
| JSON | Yes | No | `src/json.rs` |
| Encrypted JSON | Yes | No | `src/encrypted_json.rs` |
| Credential Exchange | Yes | Yes | `src/cxf/` |

- **CSV** β€” Flat export of Login and SecureNote items only. Cards, identities, and SSH keys are
excluded.
- **JSON** β€” Unencrypted JSON containing all cipher types (Login, SecureNote, Card, Identity,
SshKey) and folders.
- **Encrypted JSON** β€” Same structure as JSON, but password-protected using the account's KDF
settings (PBKDF2 or Argon2id).
- **CXF (Credential Exchange Format)** β€” [FIDO Alliance standard][cxf-spec] for transferring
credentials between providers. This is the only format that supports both export and import.

[cxf-spec]: https://fidoalliance.org/specifications-credential-exchange-specifications/

## Crate structure

```text
src/
β”œβ”€β”€ lib.rs # Public types and re-exports
β”œβ”€β”€ exporter_client.rs # ExporterClient + ExporterClientExt trait (entry point)
β”œβ”€β”€ export.rs # Orchestrates decrypt β†’ format β†’ output
β”œβ”€β”€ models.rs # Conversions between vault models and export types
β”œβ”€β”€ error.rs # ExportError (aggregates per-format errors)
β”œβ”€β”€ csv.rs # CSV formatter
β”œβ”€β”€ json.rs # JSON formatter
β”œβ”€β”€ encrypted_json.rs # Password-protected JSON formatter
β”œβ”€β”€ uniffi_support.rs # UniFFI mobile bindings support
└── cxf/ # Credential Exchange Format
β”œβ”€β”€ mod.rs
β”œβ”€β”€ export.rs # build_cxf()
β”œβ”€β”€ import.rs # parse_cxf()
└── *.rs # Per-credential-type converters (login, card, identity, etc.)
```

## Data flow

### Export (CSV & JSON)

[`ExporterClient::export_vault`] decrypts ciphers and folders via the `KeyStore`, passes them
through the chosen format module (`csv`, `json`, or `encrypted_json`), and returns the result as a
`String`.

```rust,no_run
use bitwarden_core::Client;
use bitwarden_exporters::{ExporterClientExt, ExportFormat};
# use bitwarden_vault::{Cipher, Folder};

fn export(client: &Client, folders: Vec<Folder>, ciphers: Vec<Cipher>) {
let export = client
.exporters()
.export_vault(folders, ciphers, ExportFormat::Json)
.unwrap();
}
```

### Credential Exchange

#### Import

[`ExporterClient::import_cxf`] parses a CXF JSON string into [`ImportingCipher`] values, encrypts
each one via the `KeyStore`, and returns `Vec<Cipher>` ready for storage.

```rust,no_run
use bitwarden_core::Client;
use bitwarden_exporters::ExporterClientExt;

fn import(client: &Client, cxf_payload: String) {
let ciphers = client
.exporters()
.import_cxf(cxf_payload)
.unwrap();
}
```

#### Export

[`ExporterClient::export_cxf`] decrypts ciphers and converts them to the [Credential Exchange
Format][cxf-spec].

```rust,no_run
use bitwarden_core::Client;
use bitwarden_exporters::{Account, ExporterClientExt};
# use bitwarden_vault::Cipher;

fn export_cxf(client: &Client, account: Account, ciphers: Vec<Cipher>) {
let cxf_json = client
.exporters()
.export_cxf(account, ciphers)
.unwrap();
}
```

## Testing

Run the crate tests with:

```sh
cargo test -p bitwarden-exporters
```

Test fixtures live in the `resources/` directory (sample JSON exports, CXF payloads from Dashlane,
1Password, and Devolutions).
Loading