Bitwarden Database Seeder
A class library for generating and inserting properly encrypted test data into Bitwarden databases.
Domain Taxonomy
Cipher Encryption States
| Term | Description | Stored in DB? |
|---|---|---|
| CipherView | Plaintext/decrypted form. Human-readable data. | Never |
| Cipher | Encrypted form. All sensitive fields are EncStrings. | Yes |
The "View" suffix always denotes plaintext. No suffix means encrypted.
EncString Format
Encrypted strings follow this format:
2.{iv}|{ciphertext}|{mac}
- 2 = Algorithm type (AES-256-CBC-HMAC-SHA256)
- iv = Initialization vector (base64)
- ciphertext = Encrypted data (base64)
- mac = Message authentication code (base64)
Data Structure Differences
SDK Structure (nested):
{ "name": "2.x...", "login": { "username": "2.y...", "password": "2.z..." } }
Server Structure (flat, stored in Cipher.Data):
{ "Name": "2.x...", "Username": "2.y...", "Password": "2.z..." }
The seeder transforms SDK output to server format before database insertion.
Key Hierarchy
Organization Key (or User Key)
│
├──▶ Encrypts Cipher.Key (optional per-cipher key)
│
└──▶ Encrypts cipher fields directly (if no per-cipher key)
For seeding, we encrypt directly with the organization key.
Entity Relationships
Organization
│
├── Collections ──┬── CollectionCipher ──┐
│ │ │
└── Ciphers ──────┴──────────────────────┘
Ciphers belong to organizations and are assigned to collections via the CollectionCipher join table.
Project Structure
Factories
Create individual domain entities with realistic encrypted data.
| Factory | Purpose |
|---|---|
CipherSeeder |
Creates encrypted Cipher entities via Rust SDK |
CollectionSeeder |
Creates collections with encrypted names |
OrganizationSeeder |
Creates organizations with keys |
UserSeeder |
Creates users with encrypted credentials |
Recipes
Bulk data operations using BulkCopy for performance.
| Recipe | Purpose |
|---|---|
CiphersRecipe |
Bulk create ciphers and assign to collections |
CollectionsRecipe |
Create collections with user permissions |
GroupsRecipe |
Create groups with collection access |
OrganizationWithUsersRecipe |
Full org setup with users |
Models
DTOs for SDK interop and data transformation.
| Model | Purpose |
|---|---|
CipherViewDto |
Plaintext input to SDK encryption |
EncryptedCipherDto |
Parses SDK's encrypted output |
Rust SDK Integration
The seeder uses FFI calls to the Rust SDK for cryptographically correct encryption:
CipherViewDto → RustSdkService.EncryptCipher() → EncryptedCipherDto → Server Format
This ensures seeded data can be decrypted and displayed in the actual Bitwarden clients.