Expand description
Bitwarden, read here with no other program: your vault’s logins and their one-time codes, to fill a site’s form when you ask. Read only: nothing is ever written to the vault.
As Bitwarden’s own clients do (its security white paper; its SDK under
GPL-3.0, bitwarden-crypto): the master key is derived from your master
password and e-mail (PBKDF2-SHA256 or Argon2id, as your account says),
hashed once more to log in, stretched with HKDF to open your user key,
which opens each item (AES-256-CBC with HMAC-SHA256, or COSE for newer
accounts); an organisation’s key comes through your RSA key. Or your
security key alone, as Bitwarden’s “log in with passkey”: the key’s secret
(WebAuthn PRF) opens an RSA key Bitwarden keeps for that passkey, which
opens your user key. The master password and the keys live in memory
only, wiped when dropped; the keyring keeps the “remember this device”
token, nothing else.
Structs§
- Found
- A login found by a search.
- Hit 🔒
- A match, before the best are kept: what it is ordered by (not the login chosen last, how its site matched, how near the page, not the user name typed whole), which login, how its site matched, which of its sites to show.
- Item
- A login of the vault.
- Key
- A symmetric key: AES-256 with its HMAC key, or a COSE key (newer accounts).
- Logins
- The vault’s logins, opened, with what each is searched by.
- Opening
- What the chooser opens with for a page (
Logins::opening). - Page 🔒
- The address the logins are wanted for: a site’s page, an account’s server.
- Passkey
Ask - What Bitwarden asks of a passkey to log in: the options the browser takes (JSON), and the token that goes back with the key’s answer.
- Searched 🔒
- What a login is searched by, folded once as the vault opens: its name, its user name, its sites and their domains. Never its password, its notes or its one-time code’s secret: no search reads them.
- Server
- Bitwarden’s addresses: its US or EU cloud, or your own server (Vaultwarden too).
- Unlocked
- The vault, open: the keys that read it, and how to ask for it.
Enums§
- By
- How a login’s site matched the site searched, the best first.
- Kdf
- How the master key is derived, as the account says.
- Problem
- What went wrong, for the window to say.
- Site
Asked 🔒 - The site searched, as typed: an address (“ameli.fr”, a page’s whole address), or words (“ameli”, “credit agricole”).
Constants§
- A256GCM 🔒
- CLIENT_
VERSION 🔒 - The version declared when the server does not say its own. Bitwarden
refuses clients too far behind it (“Please update your app to continue
using Bitwarden”: 2024.12.0 was refused in October 2026), so the server’s
own version is declared when it gives one (
client_version). - XAES_
256_ 🔒GCM - XCHACH
A20_ 🔒POLY1305 - COSE algorithms Bitwarden uses (private use,
bitwarden-crypto/src/cose).
Functions§
- account
- The account a session is for: the e-mail its access token names, in lower case.
- agent 🔒
- HTTPS only, redirects included: what logs in (your password’s hash) and your vault never travel in clear, as mail never does.
- answer 🔒
- Reads an answer: its status and its JSON. A large vault is more than ureq’s 10 MB; one that could not be read whole is not an empty vault.
- ask_
token 🔒 - Asks Bitwarden for a session:
formis the login;emailgoes in the header Bitwarden expects with a password. - b64 🔒
- base32 🔒
- RFC 4648 base32, letters and digits 2–7, spaces and padding ignored.
- begins_
label 🔒 - Whether
wordbegins a label ofhost, or a part of one between hyphens: “ameli” in ameli.fr and assure.ameli.fr, not in camelia.com; “agricole” in credit-agricole.fr; “ameli.f”, typed on the way to “ameli.fr”, in ameli.fr. - begins_
word 🔒 - Whether
wordbegins a word oftext: “ameli” in “Mon Ameli”, not in “Camelia”. - cbc 🔒
- AES-256-CBC with PKCS#7 padding.
- client_
version 🔒 - The version to declare to this server: its own (
GET {api}/config, kept for the session), so Sioul keeps within the versions it accepts; elseCLIENT_VERSION. - cose 🔒
- A COSE_Encrypt0 message (RFC 9052 §5.2): its algorithm, its nonce, its text, opened with the protected header as additional data; padded UTF-8 unpadded.
- decrypt
- An EncString opened: type 0 (AES-CBC), 2 (AES-CBC with HMAC, checked first), 7 (COSE).
- decrypt_
rsa - An EncString opened with an RSA key: type 3 (OAEP-SHA256), 4 (OAEP-SHA1), 5 and 6 (the same, their MAC unused).
- device_
id - This computer, as Bitwarden knows it: one identifier kept in
path, so it is not a new device each time. - device_
type 🔒 - domain 🔒
- The domain a host is registered under (
sites::domain_of: “ameli.fr” for assure.ameli.fr); an address in numbers (192.168.1.1) or a computer’s name (one label) is its own. - domain_
of_ address - The registrable domain of an address or a host, as the search reads it:
ameli.frforhttps://assure.ameli.fr/…. - field 🔒
- A field, in the camel case of current servers or the Pascal case of older ones.
- folded 🔒
- Case and accents aside, as everywhere in Sioul (
text::fold_char). - form_
encode 🔒 - hosts_
of - The sites of a login, as their hosts in lower case: its addresses that are web pages, with or without “https://”. An app’s (“androidapp://…”) is no site, nor is a pattern (“^https?://…”, for Bitwarden’s matching by regex).
- is_
version 🔒 - “2026.9.2”: a version as Bitwarden writes them.
- key_
page - Where a security key is asked: a page of the vault’s own (its light
security-key page, with nothing to show unasked), so the key signs for the
one address Bitwarden takes keys’ signatures from. Held unseen in the
window;
key_scriptasks the key there. - key_
script - The script that asks the key in that page:
optionsas Bitwarden gave them (a second step’s, orpasskey_ask’s for the key alone). The answer waits in the page until taken (window.sioulKey()): for a second step, {“token”}, the step’s code; for the key alone, {“response”, “secret”}; else {“error”} (“not-allowed”: not used in time, or cancelled). - master_
key - The master key, from the master password and the e-mail (trimmed, in lower case). The settings come from the server, and are held to Bitwarden’s own bounds: below its clients’ minimums, a server would make your password cheaper to guess from what it is sent (its clients refuse them too); above its own maximums, it would only exhaust this computer.
- passkey_
ask - A passkey login begun: asked of no account, the key says whose it is.
- passkey_
salt - The salt of the key’s secret that opens the vault (WebAuthn PRF), as Bitwarden’s apps ask it: SHA-256 of “passwordless-login”.
- password_
hash - The hash that logs in: the master key hashed once with the password.
- pbkdf2 🔒
- PBKDF2-HMAC-SHA256, 32 bytes (RFC 8018).
- prelogin 🔒
- How the account derives its master key.
- refusal 🔒
- Why Bitwarden refused a login, in its words.
- send_
email_ code - Sends the code of the e-mail second step (provider 1).
- session 🔒
- The session Bitwarden gave, with its user key opened: the account’s RSA key opened with it (at the top, or where newer servers put it).
- sync
- Reads the vault: every login, opened.
- text 🔒
- A string field: an EncString opened as UTF-8.
- totp
- The one-time code now (RFC 6238; RFC 4226 for the truncation): from a base32
secret, an
otpauth://totp/…address (its digits, period and SHA-1, SHA-256 or SHA-512), or Steam’s five characters. - unlock
- Logs in and opens the vault.
factoris a second step asked before (provider, code);remembereda token kept from a former login;new_devicethe code Bitwarden mailed for a new device. - unlock_
with_ passkey - Logs in with a passkey and opens the vault with the key’s secret:
tokenaspasskey_askgave it,responsethe key’s answer (JSON, as Bitwarden’s apps send it),secretthe key’s PRF output (base64url). No second step: Bitwarden counts the key’s PIN as one. - unpad 🔒
- Bitwarden’s padding: the last byte says how many to drop.
- user_
key_ 🔒of_ passkey - The user key, opened with a passkey’s secret: the secret stretched as a
master key is (HKDF-Expand, “enc” and “mac”) opens the RSA key Bitwarden
keeps for the passkey, which opens the user key (
WebAuthnPrfOption). - web_
host 🔒 - whole_
word 🔒 - Whether
wordis a whole word oftext: “google” in “Google Play”, not in “Googleplex”.