Rotate identity keys, not only prekeys (relay#25) #98

Merged
paulhitt merged 1 commits from feat/rotate-identity-keys into main 2026-09-04 20:40:47 +00:00
paulhitt commented 2026-09-04 13:41:42 +00:00 (Migrated from gitlab.com)

Rotation already worked, and that was the problem. A device
re-registering with a different ik_sig simply overwrote the old one.
The call is authorised by the OLD key — the token's cnf must match
what is on file — so the caller had to hold the key being replaced. What
nothing established was that the NEW key's holder had anything to do
with it.

That gap matters because of who can reach it. Anyone able to mint a
token for a device and sign one request with the old key — a stolen
device in the window before revocation, a compromised issuer — could
point the identity at a key they control, and every future correspondent
would fetch the substitute as though it were a rotation the owner
performed.

A rotation may now carry a continuity proof: the old key's signature
over fnrot-v1|<device>|<old>|<new>. Only somebody holding the old key
can produce it, and only for one named successor — the statement carries
both keys and the device id, so a captured proof cannot be replayed onto
a different new key or onto another device of the same account. Both are
tested.

Absent is accepted; wrong is refused. Every client that predates
the field sends nothing, and refusing them would break rotation for
everybody already installed. A present-and-wrong proof is somebody
asking to be believed and failing, which is a different thing entirely.

What changes for the unproven case is the record. A proven
rotation logs as rotate, an unproven one as rotate_unproven — two
kinds, not one kind with a flag, because the key log is what somebody
audits later and the distinction has to survive into it. A monitor, or
the other party's client, can then treat them differently rather than
being told they are the same event.

A refused rotation appends nothing to the log. A key the relay would
not serve must not be committed to.

592 relay tests in 52 suites, green — all seven core paths included.

**Rotation already worked, and that was the problem.** A device re-registering with a different `ik_sig` simply overwrote the old one. The call is authorised by the OLD key — the token's `cnf` must match what is on file — so the caller had to hold the key being replaced. What nothing established was that the NEW key's holder had anything to do with it. That gap matters because of who can reach it. Anyone able to mint a token for a device and sign one request with the old key — a stolen device in the window before revocation, a compromised issuer — could point the identity at a key they control, and every future correspondent would fetch the substitute as though it were a rotation the owner performed. A rotation may now carry a **continuity proof**: the old key's signature over `fnrot-v1|<device>|<old>|<new>`. Only somebody holding the old key can produce it, and only for one named successor — the statement carries both keys and the device id, so a captured proof cannot be replayed onto a different new key or onto another device of the same account. Both are tested. **Absent is accepted; wrong is refused.** Every client that predates the field sends nothing, and refusing them would break rotation for everybody already installed. A present-and-wrong proof is somebody asking to be believed and failing, which is a different thing entirely. **What changes for the unproven case is the record.** A proven rotation logs as `rotate`, an unproven one as `rotate_unproven` — two kinds, not one kind with a flag, because the key log is what somebody audits later and the distinction has to survive into it. A monitor, or the other party's client, can then treat them differently rather than being told they are the same event. A refused rotation appends nothing to the log. A key the relay would not serve must not be committed to. 592 relay tests in 52 suites, green — all seven core paths included.
paulhitt commented 2026-09-04 13:41:42 +00:00 (Migrated from gitlab.com)

assigned to @paulhitt

assigned to @paulhitt
paulhitt commented 2026-09-04 20:40:42 +00:00 (Migrated from gitlab.com)

deleted the feat/witness-cosigned-heads branch. This merge request now targets the main branch

deleted the `feat/witness-cosigned-heads` branch. This merge request now targets the `main` branch
paulhitt (Migrated from gitlab.com) merged commit 7261167f6e into main 2026-09-04 20:40:47 +00:00
paulhitt commented 2026-09-09 18:59:12 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
Sign in to join this conversation.