fix(auth)!: the owner's proof is bound to what it approves

A signed-in change's proof covered only its challenge and the instance,
so whoever could read and change the traffic (an http:// instance, a
TLS-intercepting proxy, TLS broken: what SRP is for) could keep the owner's
proof and code and swap in a new password, a recovery phrase or an
authenticator of their own, or turn a proof made for new recovery codes
into a deletion (the full reading's device medium 1).

SRP's identity now also carries what the proof is for and the digest of
what it sets, which the register works out from the call that brings it,
never from the device: a new password (its digest), a new phrase (its
wrapping and proof key), confirming the first or another authenticator and
removing one (its ID), new recovery codes, calling resets off, deleting;
the sessionless reset of an authenticator its own, apart from signing in.
A proof made for one of them counts for no other. Tests made for each
refusal fail with the binding taken out on both sides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-21 14:12:11 +02:00
commit 92d2df4695
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -16,7 +16,10 @@ import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// What a signed-in owner proves to change how their account is secured
/// (ADR 0017; the third reading, as Proton has it): the password's SRP
/// proof for a challenge `password.beginChange` gave, bound to the base URL
/// the device addressed, and a TOTP code from one of the account's confirmed
/// the device addressed and to what it approves (SRP's identity carries a
/// purpose and the digest of what is set, which the register works out from
/// the call that brings the proof, so a proof made for one change counts for
/// no other), and a TOTP code from one of the account's confirmed
/// authenticators or one of its recovery codes. Adding an authenticator once
/// one is confirmed, removing a confirmed one, new recovery codes, a new
/// recovery phrase and a new password take it: a session alone never gets a