You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: rfcs/003-chromium-encryption.md
+12-4Lines changed: 12 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,7 @@ Every encrypted value begins with a 3-byte prefix that identifies the cipher ver
17
17
| Prefix | Version | Meaning |
18
18
|--------|---------|---------|
19
19
|`v10`| CipherV10 | Chrome 80+ standard encryption (AES-GCM on Windows, AES-CBC on macOS/Linux) |
20
+
|`v11`| CipherV11 | Linux-only: AES-CBC variant where the key comes from libsecret / kwallet. Same algorithm and parameters as `v10` — only the key source differs |
| (none) | CipherDPAPI | Pre-Chrome 80 raw DPAPI encryption (Windows only, no prefix) |
22
23
@@ -69,9 +70,12 @@ With the master key, each encrypted value is decrypted as AES-256-GCM:
69
70
70
71
## 5. Linux Encryption
71
72
72
-
Chromium on Linux retrieves a per-browser secret from D-Bus Secret Service (GNOME Keyring or KDE Wallet). The label matches the browser's storage name (e.g. "Chrome Safe Storage", "Chromium Safe Storage"). If D-Bus is unavailable, the hardcoded fallback password `peanuts` is used.
73
+
Chromium on Linux has two obfuscation prefixes that share the same AES-128-CBC algorithm and PBKDF2 parameters — only the key source differs:
73
74
74
-
The master key is derived via PBKDF2 with different parameters than macOS:
75
+
-**`v10`** — the PBKDF2 password is the hardcoded string `peanuts`. Chromium writes this prefix when no keyring backend is available (headless sessions, `--password-store=basic`, LXQt, etc.).
76
+
-**`v11`** — the PBKDF2 password is a random string read from D-Bus Secret Service (GNOME Keyring or KDE Wallet). The libsecret/kwallet item label matches the browser's storage name (e.g. "Chrome Safe Storage", "Brave Safe Storage"). Chromium writes this prefix whenever a keyring backend is available at encrypt time. On first run, Chromium generates and stores the random password automatically.
77
+
78
+
Both prefixes are derived through the same PBKDF2 parameters:
75
79
76
80
| Parameter | Value |
77
81
|-----------|-------|
@@ -80,7 +84,11 @@ The master key is derived via PBKDF2 with different parameters than macOS:
80
84
| Iterations | 1 |
81
85
| Key length | 16 bytes (AES-128) |
82
86
83
-
Decryption uses the same AES-128-CBC scheme as macOS (fixed IV of 16 space bytes, PKCS5 padding).
87
+
Decryption uses AES-128-CBC with a fixed IV of 16 space bytes (`0x20`) and PKCS5 padding — identical to macOS except for the PBKDF2 iteration count.
88
+
89
+
**Mixed v10/v11 in the same profile.** Because Chromium selects the prefix at encrypt time, a single profile may contain both versions if the keyring backend availability changed between sessions. Chromium decrypts each record independently by inspecting its prefix.
90
+
91
+
**kEmptyKey legacy retry.** Chromium's `DecryptString` retries any failed v10/v11 decryption with a second key, `kEmptyKey = PBKDF2("", "saltysalt", 1, 16, sha1)`. This exists to recover data corrupted by a KWallet initialization race in Chrome ~89 (see `crbug.com/40055416`), where some records were written with this zero-derived key. Chromium never uses `kEmptyKey` for encryption — it is decrypt-only. HackBrowserData mirrors this retry for parity.
84
92
85
93
## 6. v20 App-Bound Encryption (Chrome 127+)
86
94
@@ -105,7 +113,7 @@ The high-level decryption path for any encrypted Chromium value:
105
113
106
114
1.**Detect version** -- inspect the first 3 bytes of the ciphertext
107
115
2.**Route by version**:
108
-
-`v10` -- strip prefix, call platform-specific decryption (AES-CBC on macOS/Linux, AES-GCM on Windows)
116
+
-`v10`/ `v11`-- strip prefix, call platform-specific decryption (AES-CBC on macOS/Linux, AES-GCM on Windows). On Linux, a failed decryption retries once with `kEmptyKey` to recover legacy crbug.com/40055416 data
109
117
-`v20` -- not yet supported, return error
110
118
- DPAPI (no prefix) -- call Windows `CryptUnprotectData` directly (Windows only; returns error on other platforms)
111
119
3.**Return plaintext** -- the decrypted bytes are interpreted as a UTF-8 string
0 commit comments