docs(auth): reading the full reading's fixes (ADR 0017)
ADR 0017 gets "Reading the fixes (21 Oct 2026)": each finding and what was done, and "The rules as they stand" brought up to date (resets take effect once 72 hours outside hold-outs passed and lapse 30 days after they were asked; GitHub-only accounts go 30 days after the end; a member takes the register's moment by its own clock; nothing secret copied on the device). The module README and Learn's AGENTS.md say the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
0c9bc3e007
commit
8d6c54ef22
1 changed files with 80 additions and 16 deletions
|
|
@ -535,7 +535,64 @@ The device side's rest (21 Oct 2026):
|
|||
`isPasswordSetup`, `srpVerify` and M2, `isNewPassword` and the phrase's proof key, bound
|
||||
to the instance and the purpose.
|
||||
|
||||
### The rules as they stand (21 Oct 2026)
|
||||
### Reading the fixes (21 Oct 2026)
|
||||
|
||||
A reading of the full reading's 26 fixes, server and device apart, by two reviewers who
|
||||
hadn't seen them made. Nothing critical or high; the proof binding held byte for byte.
|
||||
What it found, and what was done (the developer decided A1+A2, the clipboard and the
|
||||
keychain on 21 Oct; the 30-day grace for GitHub-only accounts was proposed and kept):
|
||||
|
||||
- **A1, A2 (medium):** every refusal at the address's ceiling moved a reset to 72 hours
|
||||
after it, so someone knowing only the address kept the owner's own reset from ever
|
||||
taking effect with one burst every three days, and kept a forgotten one alive. Now a
|
||||
hold-out that begins before a reset takes effect moves it on only by the time it holds
|
||||
the owner out and no hold-out counted before did (`account_reset.heldOutUntil`, under
|
||||
a row lock): the owner gets 72 hours outside hold-outs in total. And every reset not
|
||||
carried out lapses 30 days after it was asked (`resetLivesAtMost`), whatever moved it,
|
||||
besides a week after it took effect.
|
||||
- **C1 (low):** a member whose clock ran ahead kept sessions made in its gap. A member
|
||||
now ends every session of the account made before the register's moment or before
|
||||
now, whichever is later, and neither waits nor refuses; the register sends its clock
|
||||
with each notice (`sentAt`), so each member takes the moment by its own. The
|
||||
`passwordChanged` refusal, sent only by the clock checks, went.
|
||||
- **B1 (low):** a member asked the register whose account it is inside the proof's try,
|
||||
so tries while the register was down ended the owner's session. Asked first now; while
|
||||
the register is down, a person still anonymous on the member deletes there.
|
||||
- **D1, D2 (low):** a member would have deleted everyone with a GitHub ID (it keeps no
|
||||
addresses), and each deletion delivered every notice, one failure aborting the run.
|
||||
The deletion runs on the register alone, 30 days after the end
|
||||
(`gitHubAccountsKeptAfterEnd`: a date set wrong can be put right first), each account
|
||||
on its own (one failing kept, with its GitHub ID, for the next day), the members told
|
||||
once after all of them.
|
||||
- **Nits:** the background delivery of session notices logs; a begin checks the
|
||||
address's ceiling after the network's limits; the module's README lists every pruning
|
||||
step.
|
||||
- **The clipboard (medium):** copies reached clipboard histories and cloud clipboards,
|
||||
and Android often couldn't empty it again. Nothing secret is copied any more: codes
|
||||
are saved, like the phrase; an authenticator's key is scanned, opened or typed.
|
||||
- **Saving (medium-low):** a saved file was made, then set to 0600, and written into
|
||||
whatever the path held. Now a new file beside it, 0600 as it is made (`O_EXCL`,
|
||||
`O_NOFOLLOW`), written through its descriptor and renamed over the path; a symbolic
|
||||
link or another user's file is refused. Windows writes in place, with its folder's
|
||||
rights.
|
||||
- **The phrase typed back (medium-low):** shown in recents and screenshots; its fields
|
||||
are inside a `SecretScreen` now.
|
||||
- **The keychain (low-medium):** `first_unlock_this_device` left the keys readable on a
|
||||
locked phone unlocked once since starting. Now `unlocked_this_device`; keeping keys
|
||||
deletes the entry first, which a delete finds whatever its class.
|
||||
- **http:// and https:// on one host (low-medium):** one place on the device, so
|
||||
removing one deleted the other's account. The places keep their names; such addresses
|
||||
are one account there: choosing one takes the other's place, and removing one forgets
|
||||
nothing the other uses (`InstanceStorage.sharesWith`).
|
||||
- **Lows:** no address in debug logs (`SignInWaysState`, `sign_in.json`'s errors); the
|
||||
keyring that can't be read counts the keys as not here, and Delete goes on whatever
|
||||
reading the account met; secrets kept out of captures on macOS (`sharingType = .none`)
|
||||
and Windows (`WDA_EXCLUDEFROMCAPTURE`) too, untried on either; the main isolate's x and
|
||||
key-encryption key wiped once done with (`PasswordUnlock.forget`).
|
||||
- **Left as they are:** the per-address cap on codes by mail (the re-review's 9);
|
||||
`sharingType = .none` doesn't hold against ScreenCaptureKit on macOS 15 and later.
|
||||
|
||||
### The rules as they stand (21 Oct 2026, after reading the fixes)
|
||||
|
||||
Who changes what of an account, with which proofs. "Password" is SRP's proof bound to
|
||||
the instance's base URL the device addressed, and to what it approves (the purpose and
|
||||
|
|
@ -556,14 +613,18 @@ wrong in a row, the owner mailed).
|
|||
| New recovery codes | — | password + code |
|
||||
| New recovery phrase | — | password + code |
|
||||
| New password | phrase's proof + code (keys kept) | password + code |
|
||||
| Authenticator reset | password + (mailed code or phrase's proof); takes effect after 72 h (pushed out while the owner is held out), owner mailed, called off by a sign-in with a code; lapses a week after taking effect unless carried out | password + code calls it off |
|
||||
| Account reset (new keys) | mailed code (`accountReset`) asks, a new one carries it out after 72 h (pushed out alike), owner mailed, called off by any sign-in with the password; lapses a week after taking effect | password + code calls it off |
|
||||
| Authenticator reset | password + (mailed code or phrase's proof); takes effect once 72 h outside hold-outs of the owner passed, owner mailed, called off by a sign-in with a code; lapses a week after taking effect unless carried out, and 30 days after it was asked | password + code calls it off |
|
||||
| Account reset (new keys) | mailed code (`accountReset`) asks, a new one carries it out once 72 h outside hold-outs passed, owner mailed, called off by any sign-in with the password; lapses a week after taking effect, and 30 days after it was asked | password + code calls it off |
|
||||
| Delete the account | — | password + code; where no second factor works, password + mailed code (`deletion`) |
|
||||
|
||||
Every reset carried out, and every new password (a setup's replaced too), ends the
|
||||
account's sessions in every service (a change keeps the session asking). A session ended
|
||||
for ten wrong proofs leaves its owner a warning; a session made since dismisses it, and a
|
||||
new password clears it. At GitHub's end, accounts that sign in with GitHub alone go.
|
||||
new password clears it. 30 days after GitHub's end, accounts that sign in with GitHub
|
||||
alone go, on the register. A member ends the sessions an account had on it before the
|
||||
register's moment by its own clock. On the device nothing secret is copied, every
|
||||
secret shown or typed back is kept out of captures, and a saved file is the own user's
|
||||
from the start.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue