# Chromium mock keychain: cookies under a public password

> Chromium's --use-mock-keychain keeps cookie encryption on but uses a password from its own source. We read a live session cookie off disk, then sealed it.

Source: https://mosaicbrowser.com/blog/mock-keychain-cookies

## Chromium's mock keychain, and why we encrypt the store

Chromium's --use-mock-keychain keeps cookie encryption on but uses a password from its own source. We read a live session cookie off disk, then sealed it.

![1 sec: One command read a session cookie off the disk, beside a screenshot: importing a cookie jar into a Mosaic profile](https://mosaicbrowser.com/covers/mock-keychain-cookies.webp)

Chromium's `--use-mock-keychain` does not switch cookie encryption off. It answers the request for the macOS keychain password with a fixed string written into Chromium's own source, and encrypts with that. Every cookie file made under the flag opens for anyone who has the file. We measured it on our own disk on 14 September 2026, and that measurement is why Mosaic seals its store and its cookie keys itself.

## What the mock keychain does to cookie encryption

On a Mac, Chromium encrypts each cookie value before it writes it to the profile's `Cookies` database. The password normally lives in the login keychain, in an item called `Chromium Safe Storage`. Chromium stretches it with PBKDF2-HMAC-SHA1 (salt `saltysalt`, 1003 iterations) into an AES-128-CBC key, and every encrypted value starts with the tag `v10`.

`--use-mock-keychain` leaves all of that in place. The derivation runs, the format is the same, the `v10` tag is still there. The only change is the password, which is now `mock_password` on every machine that passes the flag. Encrypted with a key everybody knows is, for a file on a disk, the same as not encrypted.

## Why our engine launches with it anyway

The first time we started a finished build of Tesseract Chrome, our own Chromium, it wrote `Local State` and then did nothing: no window, no DevTools port, 0% CPU. `sample` on the process named the cause in three seconds. The main thread was inside `SecItemCopyMatching`, waiting for the keychain.

That build was ad-hoc signed, so macOS treated it as an application it had never seen and put the Safe Storage item behind an access dialog. The browser waited for the answer before it had a window to show. And every launcher bundle we made was a new code identity to the keychain, so "Always Allow" granted one launch and the next one asked again. On a machine running hundreds of profiles that is not a dialog anyone can live with.

So every launch carries `--use-mock-keychain`, with `--password-store=basic` beside it as the Linux equivalent. The flag solved the dialog. It also handed every profile's cookies the same public password, and we went and measured what that meant.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

[How it works: Local first](https://mosaicbrowser.com/features/local-first)

## What one command read off our own disk

One command, no special privileges, against a real profile on the development Mac:

```
DECRYPTED — password "mock_password", offset 32
  .dev.to  _Devto_Forem_Session = "bb0cbcf3b126b691797c6126a7f3ae48"

```

That is a live session cookie, read off the disk in about a second. Beside it, `mosaic.json`, the file that held every profile, was readable by any user on the machine and carried every proxy password in plain text.

Separation was never the problem. Each profile has its own data folder, and when we compared nineteen cookie jars on one machine there was no overlap between them. What the shared password cost was confidentiality at rest: a stolen laptop folder, a copied backup, a disk pulled from a dead machine. Mosaic keeps everything on the computer, as [local-first](https://mosaicbrowser.com/features/local-first) describes, and that is only worth something if the computer is a safe place for it.

## What we built instead

One data key, and everything sensitive sealed under it.

1. **The store.** `mosaic.json`, proxy credentials included, is sealed with AES-256-GCM and a fresh 12-byte nonce on every write, because the store is rewritten on every edit and a reused nonce is the one way to break GCM outright.
2. **A cookie password per profile.** It is derived from the data key and the profile's id, so there is no second secret to keep in step, and a profile removed from the store takes its key with it.
3. **A switch to hand it over.** Patch `0139-embedder-cookie-key` adds `--os-crypt-password` to the engine. It is read before the keychain is touched, so a profile carrying its own key raises no dialog and never falls back to the constant. Chromium's derivation and its `v10` format are unchanged, so the patch touches two files and nothing else in Chromium needs to know.

We changed it in C++ rather than in any script a page could see, for the same reason we move fingerprint values there ([why C++ and not JavaScript](https://mosaicbrowser.com/blog/cpp-not-javascript) has the longer argument).

## What it does not protect

The password reaches the engine on its command line, where `ps` shows it to the user who started it. That trade is deliberate. The threat this fixes is a copied folder, and a command line is never written to disk. Code already running as you could read the key out of Mosaic's memory whatever we did, and we do not claim otherwise.

The data key itself has to be kept somewhere, and that is a separate choice. At the time of writing it sits in a file only your user can read by default, or is wrapped in a password if you turn on app lock in Settings › Security. A failed decrypt throws an error. It never falls back to an empty store, because a wrong key that looks like a first run would show you a library with no profiles in it.

We had already moved per-profile [downloads out of the shared folder](https://mosaicbrowser.com/blog/downloads-are-evidence) for a related reason: what a profile leaves on disk is evidence of that profile. The engine and the patch series behind it are in [why we built our own Chromium](https://mosaicbrowser.com/blog/why-we-built-our-own-chromium).

## Questions

### Does --use-mock-keychain turn off cookie encryption in Chromium?

No. Chromium still encrypts cookie values in the same format. The flag only changes where the password comes from: a fixed string in Chromium's source instead of the macOS keychain, so anyone with the file can decrypt it.

### Why would a browser launch with the mock keychain at all?

Because the real keychain asks. An ad-hoc signed Chromium reading its Safe Storage item gets an access dialog before it has drawn a window, and the browser waits on it. With hundreds of profiles on one machine, that dialog is not something a person can keep answering.

### How does Mosaic encrypt cookies instead?

Each profile gets its own cookie password, derived from Mosaic's data key and the profile's id, and handed to the engine through a switch our patch adds. The cookie file keeps Chromium's format, but its key exists only inside Mosaic's vault.

### Does the shared mock password link profiles together?

No. Separation comes from each profile having its own data folder, and nineteen cookie jars on one machine showed no overlap. What a shared password costs is confidentiality on disk, not separation.

## Related

[Local first: your profiles stay on your computer](https://mosaicbrowser.com/features/local-first): Mosaic has no cloud copy of your profiles. Profiles, cookies and proxies stay on your computer, encrypted on disk, with app lock, daily backups and a trash.

[Why we built our own Chromium instead of injecting JavaScript](https://mosaicbrowser.com/blog/why-we-built-our-own-chromium): Mosaic changes fingerprint values in C++ inside its own engine. Three measured differences between Chrome and plain Chromium explain why that matters.

[A downloaded file is evidence of its profile](https://mosaicbrowser.com/blog/downloads-are-evidence): With several browser profiles, one shared Downloads folder mixes every profile's files and outlives them. Mosaic gives each profile its own folder instead.

[Why we change fingerprints in C++, not JavaScript](https://mosaicbrowser.com/blog/cpp-not-javascript): A JavaScript spoof is one realm deep. An iframe read the real source of 33 of our patched functions; the same values changed in C++ agree everywhere.

[App-Bound Encryption, and why tools stopped reading Chrome's cookies](https://mosaicbrowser.com/blog/chrome-app-bound-encryption): App-Bound Encryption ties Chrome's cookie key on Windows to Chrome itself, so tools reading the Cookies file broke. Export from inside the browser.

[Device-bound sessions and the end of copying a cookie](https://mosaicbrowser.com/blog/device-bound-session-credentials): With Device Bound Session Credentials in Chrome, a site reissues its session cookie only to the device with the key. A copied cookie lasts until it expires.

[Chrome kept third-party cookies. What that means for cookie jars](https://mosaicbrowser.com/blog/chrome-keeps-third-party-cookies): Chrome is not removing third-party cookies: Google dropped the phase-out and then the prompt. A jar per account matters as much as it did before.



## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
