docs: record why the database is not encrypted, and drop the claim that it is
Asked whether encryption at rest could be added. It can, and it should not be yet — so the reasoning goes in the document that owns the claim, before it is forgotten and re-derived from scratch. The architecture decides it. ReminderCoordinator collects Room flows at every process start, including processes started with no Activity after a reboot, and ReminderWorker reads the forecast on a schedule. The key would have to be readable with no user present, which is the availability condition the platform key already has. A key anything running as this app can reach unattended defends a file, not a process — and against every adversary this document already lists, it adds nothing. What it would genuinely add is narrow: a file copied off the device and read elsewhere, a phone with no screen lock, and crypto-shredding on delete. The cost is about +1 MB downloaded and +2 MB installed on a 2 MB app, and a new class of total loss — a Keystore key can be lost in the field, platform backup is off, and there is no telemetry that would tell us it happened. Today a database that will not open is a bug. Afterwards it would be somebody's history. The condition that reverses it is written down too: a key bound to the app lock's user secret, once export has given the user a copy they control. BRAND_GUIDE carried an artwork brief reading "explaining encrypted local data storage" — a false claim waiting for somebody to draw it, on the one subject where a padlock reads as a promise. Replaced with what is true and is a better story anyway: the data never leaves, so there is no server to breach and nothing to sell. SECURITY_CHECKLIST gains the row that would have caught it. Nothing shipped ever claimed encryption; grep over app, core and domain for strings and illustrations comes back empty. This closes the gap between that and the documents. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
424a513336
commit
17cddb2a61
|
|
@ -731,7 +731,18 @@ Example:
|
|||
|
||||
or:
|
||||
|
||||
**“Create an onboarding illustration explaining encrypted local data storage using this visual system.”**
|
||||
**“Create an onboarding illustration explaining that cycle history stays on this device using this visual system.”**
|
||||
|
||||
Note the wording, because it changed. This example used to say *encrypted local
|
||||
data storage*, and the database is **not** encrypted — see *Deliberately out of
|
||||
scope* in [`../security/SECURITY.md`](../security/SECURITY.md), which owns that
|
||||
claim and explains why.
|
||||
|
||||
A prompt is a claim the moment somebody draws it. A padlock on an onboarding
|
||||
screen is a promise, and this one would have been a promise the product does not
|
||||
keep — which is worse than saying nothing, because the true story here is
|
||||
genuinely good: the data never leaves, so there is no server to breach and
|
||||
nothing to sell. Draw that instead.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,33 @@ Written down so an unknown gap becomes a known one:
|
|||
- **A rooted or compromised device.** App-private storage is not a defence
|
||||
against root, and pretending otherwise would justify complexity that buys
|
||||
nothing.
|
||||
- **Encryption of the cycle database at rest.** Evaluated on 2026-08-19 and
|
||||
deliberately not built. Android's file-based encryption already covers the
|
||||
case people picture: a phone that is off, or that has not been unlocked since
|
||||
boot, has app-private storage that cannot be read at all. What app-level
|
||||
encryption would add is narrower — a file *copied off* the device and read
|
||||
elsewhere, a phone with no screen lock set at all, and crypto-shredding on
|
||||
delete.
|
||||
|
||||
This app's own architecture decides the rest. `ReminderCoordinator` collects
|
||||
Room flows at **every process start**, including processes started with no
|
||||
Activity after a reboot, and `ReminderWorker` reads the forecast on a
|
||||
schedule. So the key would have to be available with no user present — which
|
||||
is the same availability condition the platform key already has. A key that
|
||||
anything running as this app can reach unattended defends a **file**, not a
|
||||
**process**, and against every adversary listed above it adds nothing.
|
||||
|
||||
The cost is recorded so nobody has to re-derive it: roughly +1 MB downloaded
|
||||
and +2 MB installed on a 2 MB app. The risk matters more than the size. A key
|
||||
held in the Android Keystore can be lost in the field, and with platform
|
||||
backup off there is no second copy and no telemetry that would tell us it
|
||||
happened. Today, failing to open the database is a bug. Afterwards it would be
|
||||
somebody's history, gone, silently.
|
||||
|
||||
**What would reverse this:** a key bound to the app lock's own user secret,
|
||||
once export exists and has given the user a copy they control. That version
|
||||
does defend a seized, unlocked phone — and it is only defensible once losing
|
||||
the key is not the same as losing everything.
|
||||
- **Forensic recovery of deleted rows.** Delete My Data removes the data through
|
||||
the database; it does not overwrite flash.
|
||||
- **Someone who knows the unlock PIN.** Biometric/PIN gating raises the bar over
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ The one group that is not generic. Every item proves part of
|
|||
### What the device and the lock screen expose
|
||||
|
||||
- [ ] Cycle history is in app-private storage only — proves nothing landed in shared or external storage
|
||||
- [ ] No shipped string, illustration, screenshot or store listing says the cycle database is **encrypted** — proves the product does not claim a control [`SECURITY.md`](SECURITY.md) records as deliberately out of scope. It is on this list because the claim is attractive, easy to draw, and was already sitting in an artwork brief before anybody checked whether it was true
|
||||
- [ ] Lock-screen text in **Discreet** and **Maximum privacy** modes contains no menstrual detail, checked on a real lock screen at every mode — proves the notification privacy feature actually works, which is the breach most likely to happen
|
||||
- [ ] The health database's inclusion in platform auto-backup is a **recorded decision**, not a default — proves the local-first promise is not undone by the OS
|
||||
- [ ] Release build has verbose logging off and no raw cycle date in any crash payload — proves §45's logging rule survived the build type
|
||||
|
|
|
|||
Loading…
Reference in New Issue