Skip to content

Commit dc9cbe2

Browse files
authored
Merge branch 'main' into km/pm-40277-key-rotation-corrupts-attachments-fido2-credentials
2 parents 5bd7286 + 15ab4ca commit dc9cbe2

136 files changed

Lines changed: 7920 additions & 1182 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/CLAUDE.md

Lines changed: 71 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,76 @@
11
# Bitwarden Internal SDK
22

3-
Cross-platform Rust SDK implementing Bitwarden's core business logic.
4-
5-
**Rust Edition:** The SDK targets the
6-
[2024](https://doc.rust-lang.org/nightly/edition-guide/rust-2024/index.html) edition of Rust.
7-
8-
**Crate documentation**: Before working in any crate, read available documentation: `CLAUDE.md` for
9-
critical rules, `README.md` for architecture, `examples/` for usage patterns, and `tests/` for
10-
integration tests. **Before making changes or reviewing code, review relevant examples and tests for
11-
the specific functionality you're modifying.**
12-
13-
## Architecture Overview
14-
15-
Monorepo crates organized in **four architectural layers**:
16-
17-
### 1. Foundation Layer
18-
19-
- **bitwarden-crypto**: Cryptographic primitives and protocols, key store for securely working with
20-
keys held in memory.
21-
- **bitwarden-organization-crypto**: Cryptographic primitives and protocols related to organizations
22-
and sharing.
23-
- **bitwarden-state**: Type-safe Repository pattern for SDK state (client-managed vs SDK-managed)
24-
- **bitwarden-threading**: ThreadBoundRunner for !Send types in WASM/GUI contexts (uses PhantomData
25-
marker)
26-
- **bitwarden-ipc**: Type-safe IPC framework with pluggable encryption/transport
27-
- **bitwarden-error**: Error handling across platforms (basic/flat/full modes via proc macro)
28-
- **bitwarden-random**: Single CRNG source for the SDK (`rng()` / `SdkRngImpl`); Use this instead of
29-
`rand::rng()` directly.
30-
- **bitwarden-encoding**, **bitwarden-uuid**: Encoding and UUID utilities
31-
32-
### 2. Core Infrastructure
33-
34-
- **bitwarden-core**: Base Client struct extended by feature crates via extension traits. **DO NOT
35-
add functionality here - use feature crates instead.**
36-
- **bitwarden-api-api**, **bitwarden-api-identity**: Auto-generated API clients (**DO NOT edit -
37-
regenerate from OpenAPI specs**)
38-
39-
### 3. Feature Implementations
40-
41-
- **bitwarden-pm**: PasswordManagerClient wrapping core Client, exposes sub-clients: `auth()`,
42-
`vault()`, `crypto()`, `sends()`, `generators()`, `exporters()`
43-
- Lifecycle: Init → Lock/Unlock → Logout (drops instance)
44-
- Storage: Apps use `HashMap<UserId, PasswordManagerClient>`
45-
- **bitwarden-vault**: Vault item models, encryption/decryption and management
46-
- **bitwarden-collections**: Collection models, encryption/decryption and management
47-
- **bitwarden-auth**: Authentication (send access tokens)
48-
- **bitwarden-send**: Encrypted temporary secret sharing
49-
- **bitwarden-generators**: Password/passphrase generators
50-
- **bitwarden-ssh**: SSH key generation/import
51-
- **bitwarden-exporters**: Vault export/import with multiple formats
52-
- **bitwarden-fido**: FIDO2 two-factor authentication
53-
54-
### 4. Cross-Platform Bindings
55-
56-
- **bitwarden-uniffi**: Mobile bindings (Swift/Kotlin) via UniFFI
57-
- Structs: `#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]`
58-
- Enums: `#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]`
59-
- Include `uniffi::setup_scaffolding!()` in lib.rs
60-
- **bitwarden-wasm-internal**: WebAssembly bindings (**thin bindings only - no business logic**)
61-
- Structs: `#[derive(Serialize, Deserialize)]` with
62-
`#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]`
63-
64-
## Critical Patterns & Rules
65-
66-
### Cryptography (bitwarden-crypto, bitwarden-organization-crypto)
67-
68-
- **DO NOT modify** without careful consideration - backward compatibility is critical
69-
- **KeyStoreContext**: Never hold across await points
70-
- Naming: `derive_` for deterministic key derivation, `make_` for non-deterministic generation
71-
- Use `bitwarden_crypto::safe` module first (password-protected key envelope, data envelope) instead
72-
of more low-level primitives
73-
- IMPORTANT: Use constant time equality checks
74-
- Do not expose low-level / hazmat functions from the crypto crate.
75-
- Do not expose key material from the crypto crate, use key references in the key store instead
76-
- These are foundational crates, **not** client crates, and _shall not_ depend on `bitwarden-core`
77-
or any `bitwarden-core` dependendents.
78-
79-
### State Management (bitwarden-state)
80-
81-
- **Client-managed**: App and SDK share data pool (requires manual setup)
82-
- **SDK-managed**: SDK exclusively handles storage (migration-based, ordering is critical)
83-
- Register types with `register_repository_item!` macro
84-
- Type safety via TypeId-based type erasure with runtime downcast checks
85-
86-
### Threading (bitwarden-threading)
87-
88-
- Use ThreadBoundRunner for !Send types (WASM contexts, GUI handles, Rc<T>)
89-
- Pins state to thread via spawn_local, tasks via mpsc channel
90-
- PhantomData<\*const ()> for !Send marker (zero-cost)
91-
- **Do not use `#[async_trait(?Send)]` in hand-written code.** Wrap `!Send` types (e.g. JS
92-
`extern "C"` bindings holding `JsValue`) in `ThreadBoundRunner` instead, which is `Send + Sync`.
93-
This keeps the SDK compatible with external crates (e.g. `passkey-rs`) that require `Send`
94-
upstream and avoids future refactors. Exceptions: the `bitwarden-api-*` crates (auto-generated
95-
from OpenAPI specs) and `impl`s of `reqwest_middleware::Middleware`, whose trait declaration
96-
applies `?Send` on `wasm32` upstream so impls must mirror it.
97-
98-
### Error Handling (bitwarden-error-macro)
99-
100-
- Three modes: **basic** (string), **flat** (variant), **full** (structure)
101-
- Generates FlatError trait, WASM bindings, TypeScript interfaces, UniFFI errors
102-
- Conditional code generation via cfg! for WASM
103-
104-
### Security Requirements
105-
106-
- **Never log** keys, passwords, or vault data in logs or error paths
107-
- **Redact sensitive data** in all error messages
108-
- **Unsafe blocks** require comments explaining safety and invariants
109-
- **Encryption/decryption changes** must maintain backward compatibility (existing encrypted data
110-
must remain decryptable)
111-
- **Breaking serialization** strongly discouraged - users must decrypt vaults from older versions
112-
113-
### API Changes
114-
115-
- **Breaking changes**: Automated detection via cross-repo workflow (see commit 9574dcc1)
116-
- TypeScript compilation tested against `clients` repo on PR
117-
- Document migration path for clients
118-
119-
### Data Model Conventions
120-
121-
The SDK uses distinct model layers; use the correct type for the context. See the
122-
[data models docs](https://contributing.bitwarden.com/architecture/sdk/data-models) for the full
123-
picture.
124-
125-
| Suffix | Role | Example |
126-
| ---------- | --------------------------------------------------------------------------------- | ------------------------ |
127-
| _(none)_ | Server/storage-layer model — prefer `View`/`Request`/`Response` at API boundaries | `Cipher`, `Send` |
128-
| `View` | Decrypted DTO returned to clients | `CipherView`, `SendView` |
129-
| `Request` | Public input DTO from client into SDK | `CipherCreateRequest` |
130-
| `Response` | Public output DTO from SDK to client | `LoginResponse` |
131-
132-
**Create vs. Edit requests**: When the fields for creating and editing an item differ (e.g. edit
133-
requires an `id`, `revision_date`, or fields that are immutable after creation), use **separate**
134-
`*CreateRequest` and `*EditRequest` structs.
135-
136-
**Variant data**: When a model has a type discriminant with per-variant associated data (e.g. a send
137-
is either a file or text, never both), use an enum with associated data rather than a bare
138-
discriminant field alongside multiple `Option` fields. The mapping from server wire format (numeric
139-
discriminant + optional fields) belongs at the API→domain boundary.
140-
141-
## Development Workflow
142-
143-
**Build & Test:**
144-
145-
- `cargo check --all-features --all-targets` - Quick validation
146-
- `cargo test --workspace --all-features` - Full test suite
147-
148-
**Format & Lint:**
149-
150-
- `npm run lint` - Run every formatting/linting check CI runs (fmt, clippy, sort, udeps, dylint,
151-
doc, prettier, dep-ownership, cargo-lock). Matches `.github/workflows/lint.yml`.
152-
- `npm run lint:fix` - Same set, auto-fixing where the tool supports it.
153-
- `npm run lint -- --only <check>` - Run a single check (e.g. `--only clippy`).
154-
- Underlying script: `scripts/lint.sh`.
155-
156-
**WASM Testing:**
157-
158-
- `cargo test --target wasm32-unknown-unknown --features wasm -p bitwarden-error -p bitwarden-threading -p bitwarden-uuid` -
159-
WASM-specific tests
3+
Bitwarden's internal cross-platform SDK: core business logic in Rust (edition 2024), consumed by web
4+
and desktop clients through WASM bindings and by mobile through UniFFI. Not for public use — the API
5+
is unstable and changes without warning. Two license zones: `crates/` (OSS) and `bitwarden_license/`
6+
(commercial: `bitwarden-sm`, `bitwarden-commercial-vault`).
7+
8+
Path-scoped rules live in `.claude/rules/` and load automatically when you touch matching files
9+
(crypto crates, generated API crates, binding crates, repo-wide Rust conventions). Several crates
10+
also carry their own `CLAUDE.md` and substantive `README.md` (e.g. `bitwarden-state`,
11+
`bitwarden-exporters`, `bitwarden-importers`, `bitwarden-ipc`, `bitwarden-threading`) — read the
12+
crate's docs, `examples/`, and `tests/` before changing it.
13+
14+
## Commands
15+
16+
Toolchain: stable is pinned in `rust-toolchain.toml`; fmt, udeps, and dylint use the nightly pinned
17+
there as `nightly-channel`. CLI tools (cargo-sort, cargo-dylint, cargo-udeps, …) are version-pinned
18+
in `[workspace.metadata.bin]` and invoked via `cargo bin <tool>` (cargo-run-bin; source installs
19+
only, no binstall).
20+
21+
Build & test:
22+
23+
- `cargo check --all-features --all-targets` — quick validation
24+
- `cargo test --workspace --all-features` — full suite (what CI runs)
25+
- `cargo test -p <crate> --all-features <filter>` — single crate / single test
26+
- `cargo test --target wasm32-unknown-unknown -p bitwarden-wasm-internal -p bitwarden-threading -p bitwarden-error -p bitwarden-uuid --all-features`
27+
— WASM suite (matches CI; the test runner is wired up in `.cargo/config.toml`). Browser-dependent
28+
tests:
29+
`cargo test --target wasm32-unknown-unknown -p bitwarden-state --features wasm,browser-tests`
30+
(needs chromedriver).
31+
32+
Lint & format:
33+
34+
- `npm run lint` — every check CI runs (fmt, clippy, sort, udeps, dylint, doc, prettier,
35+
dep-ownership, cargo-lock); `npm run lint:fix` auto-fixes; `npm run lint -- --only <check>` runs
36+
one. Backed by `scripts/lint.sh`.
37+
- **Never run bare `cargo fmt`**`rustfmt.toml` uses nightly-only options that stable rustfmt
38+
silently ignores. Use `npm run lint:fix -- --only fmt`.
39+
- Custom dylint lints live in `support/lints/` (not a workspace member).
40+
- Husky pre-commit runs prettier, clippy, and dylint on staged files (plus udeps and sort when
41+
`Cargo.toml` changes) — commits are slow and can fail on lint.
42+
43+
Generated code:
44+
45+
- `bitwarden-api-api` / `bitwarden-api-identity` are generated from the server's OpenAPI specs —
46+
never edit by hand. Regenerate with `./support/build-api.sh` (expects a sibling `server` checkout)
47+
or the "Update API Bindings" GitHub workflow.
48+
- WASM npm packages (`@bitwarden/sdk-internal`, `@bitwarden/commercial-sdk-internal`) build via
49+
`crates/bitwarden-wasm-internal/build.sh` (`-r` release, `-b` commercial).
50+
51+
## Architecture
52+
53+
Four layers; dependencies point strictly downward:
54+
55+
1. **Foundation**`bitwarden-crypto`, `bitwarden-organization-crypto`, `bitwarden-state`,
56+
`bitwarden-threading`, `bitwarden-ipc`, `bitwarden-error`, `bitwarden-random` (the SDK's single
57+
CRNG source — use it instead of calling `rand::rng()` directly), plus small utilities. These must
58+
not depend on `bitwarden-core` or anything that does.
59+
2. **Core**`bitwarden-core` defines `Client`, a dependency-injection container (user identity,
60+
key store, API configuration, state). **Do not add features here** — feature crates extend
61+
`Client` via extension traits.
62+
3. **Features** — one crate per domain (`bitwarden-vault`, `bitwarden-auth`, `bitwarden-send`,
63+
`bitwarden-generators`, `bitwarden-exporters`, `bitwarden-importers`, `bitwarden-sync`,
64+
`bitwarden-policies`, …). `bitwarden-pm` assembles them into `PasswordManagerClient`, the facade
65+
apps consume — one instance per user (`HashMap<UserId, PasswordManagerClient>`), lifecycle init →
66+
lock/unlock → logout (drop). The current sub-client list is in `crates/bitwarden-pm/src/lib.rs`.
67+
4. **Bindings**`bitwarden-uniffi` (Swift/Kotlin) and `bitwarden-wasm-internal`
68+
(TypeScript/JavaScript). Thin bindings only — no business logic.
16069

16170
## References
16271

163-
- [SDK Architecture](https://contributing.bitwarden.com/architecture/sdk/)
164-
- [Architectural Decision Records (ADRs)](https://contributing.bitwarden.com/architecture/adr/)
165-
- [Contributing Guidelines](https://contributing.bitwarden.com/contributing/)
166-
- [Setup Guide](https://contributing.bitwarden.com/getting-started/sdk/internal/)
167-
- [Code Style](https://contributing.bitwarden.com/contributing/code-style/)
168-
- [Security Whitepaper](https://bitwarden.com/help/bitwarden-security-white-paper/)
169-
- [Security Definitions](https://contributing.bitwarden.com/architecture/security/definitions)
170-
- [Rust 2024 Edition Guide](https://doc.rust-lang.org/edition-guide/rust-2024/)
72+
- [SDK architecture](https://contributing.bitwarden.com/architecture/sdk/) ·
73+
[data models](https://contributing.bitwarden.com/architecture/sdk/data-models) ·
74+
[ADRs](https://contributing.bitwarden.com/architecture/adr/) ·
75+
[code style](https://contributing.bitwarden.com/contributing/code-style/) ·
76+
[security definitions](https://contributing.bitwarden.com/architecture/security/definitions)

.claude/rules/bindings.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
paths:
3+
- "crates/bitwarden-uniffi/**"
4+
- "crates/bitwarden-wasm-internal/**"
5+
---
6+
7+
# Binding crates
8+
9+
These crates are thin bindings only — no business logic. Implement behavior in feature crates and
10+
expose it here.
11+
12+
- `bitwarden-wasm-internal` targets TypeScript/JavaScript. Build with the crate's `./build.sh` (`-r`
13+
release, `-b` commercial); output lands in `npm/` (OSS, published as `@bitwarden/sdk-internal`)
14+
and `bitwarden_license/npm/` (commercial, `@bitwarden/commercial-sdk-internal`). Local client
15+
development uses `npm link` against those directories.
16+
- `bitwarden-uniffi` targets Swift (`swift/`) and Kotlin (`kotlin/`; local Maven publish via
17+
`kotlin/publish-local.sh`).

.claude/rules/crypto.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
paths:
3+
- "crates/bitwarden-crypto/**"
4+
- "crates/bitwarden-organization-crypto/**"
5+
---
6+
7+
# Cryptography crates
8+
9+
Changes here affect every Bitwarden client. Backward compatibility is non-negotiable: data encrypted
10+
by older releases must remain decryptable, so treat serialization and format changes as forbidden
11+
unless explicitly coordinated.
12+
13+
- Prefer the `bitwarden_crypto::safe` module (password-protected key envelope, data envelope) over
14+
low-level primitives.
15+
- Do not expose hazmat functions or raw key material from these crates — hand out key references
16+
into the `KeyStore` instead.
17+
- Never hold a `KeyStoreContext` across an `await` point.
18+
- Naming: `derive_*` for deterministic key derivation, `make_*` for random generation.
19+
- Compare secrets with constant-time equality.
20+
- These are foundation crates: they must not depend on `bitwarden-core` or anything that depends on
21+
it.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
paths:
3+
- "crates/bitwarden-api-api/**"
4+
- "crates/bitwarden-api-identity/**"
5+
---
6+
7+
# Generated API crates
8+
9+
These crates are generated from the Bitwarden server's OpenAPI specs. Do not edit them by hand —
10+
regeneration overwrites everything.
11+
12+
- Regenerate locally with `./support/build-api.sh` (expects a sibling `server` checkout), or via the
13+
"Update API Bindings" GitHub workflow.
14+
- To change the generated output, edit the templates in `support/openapi-template/` or fix the
15+
server-side spec, then regenerate.

.claude/rules/rust-conventions.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
paths:
3+
- "crates/**/*.rs"
4+
- "bitwarden_license/**/*.rs"
5+
---
6+
7+
# Rust conventions
8+
9+
## Data models
10+
11+
Model layers use suffixes (full picture:
12+
[data models docs](https://contributing.bitwarden.com/architecture/sdk/data-models)):
13+
14+
| Suffix | Role | Example |
15+
| ---------- | ------------------------------------- | --------------------- |
16+
| _(none)_ | Server/storage-layer model | `Cipher`, `Send` |
17+
| `View` | Decrypted DTO returned to clients | `CipherView` |
18+
| `Request` | Public input DTO from client into SDK | `CipherCreateRequest` |
19+
| `Response` | Public output DTO from SDK to client | `LoginResponse` |
20+
21+
- Prefer `View`/`Request`/`Response` at API boundaries.
22+
- Use separate `*CreateRequest` / `*EditRequest` structs when create and edit fields differ (e.g.
23+
edit requires an `id` or `revision_date`).
24+
- Variant data: when a model has a type discriminant with per-variant data (a send is file or text,
25+
never both), use an enum with associated data — not a discriminant field plus multiple `Option`s.
26+
Map the server wire format (numeric discriminant + optional fields) to the domain enum at the
27+
API→domain boundary.
28+
29+
## Threading
30+
31+
Do not use `#[async_trait(?Send)]` in hand-written code. Wrap `!Send` values (e.g. JS `extern "C"`
32+
bindings holding `JsValue`) in `ThreadBoundRunner` from `bitwarden-threading`, which is
33+
`Send + Sync`. Exceptions: the generated `bitwarden-api-*` crates and
34+
`reqwest_middleware::Middleware` impls, which must mirror the upstream `?Send` declaration on
35+
wasm32.
36+
37+
## Exposing types across bindings
38+
39+
- UniFFI: `#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]` on structs, `uniffi::Enum` on
40+
enums; crates with UniFFI exports call `uniffi::setup_scaffolding!()` in `lib.rs`.
41+
- WASM: `#[derive(Serialize, Deserialize)]` plus
42+
`#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]`.
43+
- Errors: annotate with `bitwarden-error` (`basic`/`flat`/`full` modes) to generate the WASM,
44+
TypeScript, and UniFFI error bindings.
45+
46+
## Security
47+
48+
- Never log or embed keys, passwords, or vault data in log output or error messages.
49+
- Existing encrypted data must remain decryptable: encryption and serialization changes must stay
50+
backward compatible.

.github/CODEOWNERS

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ crates/bitwarden-user-crypto-management/** @bitwarden/team-key-management-dev
5959
crates/bitwarden-vault/** @bitwarden/team-vault-dev
6060

6161
# Team-owned folders in other crates (to be avoided if possible)
62-
crates/bitwarden-wasm-internal/integration-tests/tests/invite/** @bitwarden/team-admin-console-dev
62+
crates/bitwarden-wasm-internal/integration-tests/tests/organizations/** @bitwarden/team-admin-console-dev
63+
crates/bitwarden-wasm-internal/integration-tests/tests/registration/** @bitwarden/team-auth-dev
6364
crates/bitwarden-wasm-internal/integration-tests/tests/unlock/** @bitwarden/team-key-management-dev
6465
crates/bitwarden-wasm-internal/src/pure_crypto.rs @bitwarden/team-key-management-dev
6566
crates/bitwarden-core/src/key_management/** @bitwarden/team-key-management-dev @bitwarden/team-platform-dev
@@ -69,7 +70,7 @@ crates/bitwarden-ipc/src/crypto_provider/noise/ @bitwarden/team-key-management-d
6970
crates/bw/src/admin_console/** @bitwarden/team-platform-dev
7071
crates/bw/src/auth/** @bitwarden/team-auth-dev
7172
crates/bw/src/key_management/** @bitwarden/team-key-management-dev
72-
crates/bw/src/tools/** @bitwarden/team-platform-dev
73+
crates/bw/src/tools/** @bitwarden/team-tools-dev @bitwarden/team-platform-dev
7374
crates/bw/src/vault/** @bitwarden/team-vault-dev
7475

7576
# BRE for publish workflow changes

0 commit comments

Comments
 (0)