Both remaining protocol rows turned out to be stale. AccountKdf.localAccountSalt is pinned to the published vector and wired into
sign-in for a local relay; MailboxHoming and AccountIdentity.publishHomes
have shipped the publish and read halves for a while. PARITY.md was describing
a gap that had closed, which is its own kind of wrong — a definition of done
that overstates the work left is one people stop reading.
What was genuinely missing was on the READING side, and on BOTH clients.
An older home record is GENUINELY SIGNED — the account holder published it,
before they added a second relay. So a relay that wants to be somebody's only
mailbox does not have to forge anything: it serves the record it saw first,
that record verifies perfectly, and the sender delivers to one relay believing
it is delivering to all of them. That is the attack multi-homing exists to
survive, and the serial is what was added to stop it.
The relay refuses a lower serial on WRITE. Nothing refused one on READ: verifiedRelays here and verifyHomes on iOS both checked the signature and
stopped. Fixed on both — iOS in ios!116, this is the Kotlin half.
The highest serial seen per address is remembered in the vault and a lower one
is refused. Equal is fine; only strictly lower is a rollback.
The floor rises only for a record that VERIFIED, via a separate verifiedSerial. Otherwise a hostile relay could hand over an unsigned record
carrying Int.MAX_VALUE, have the client raise its floor to it, and lock out
every later genuine record — turning the rollback defence into a denial of
service against the account holder. Tested.
Sealed in the vault and keyed by a hash of the address: which addresses this
device has looked up is exactly the metadata the vault exists to keep off a
stolen phone.
The refusal does not claim an attack. A relay that has genuinely fallen
behind serves an old record for honest reasons, and nothing in a client can
tell that from the other case.
Both remaining protocol rows turned out to be stale.
`AccountKdf.localAccountSalt` is pinned to the published vector and wired into
sign-in for a local relay; `MailboxHoming` and `AccountIdentity.publishHomes`
have shipped the publish and read halves for a while. PARITY.md was describing
a gap that had closed, which is its own kind of wrong — a definition of done
that overstates the work left is one people stop reading.
What was genuinely missing was on the READING side, and on BOTH clients.
An older home record is GENUINELY SIGNED — the account holder published it,
before they added a second relay. So a relay that wants to be somebody's only
mailbox does not have to forge anything: it serves the record it saw first,
that record verifies perfectly, and the sender delivers to one relay believing
it is delivering to all of them. That is the attack multi-homing exists to
survive, and the serial is what was added to stop it.
The relay refuses a lower serial on WRITE. Nothing refused one on READ:
`verifiedRelays` here and `verifyHomes` on iOS both checked the signature and
stopped. Fixed on both — iOS in ios!116, this is the Kotlin half.
The highest serial seen per address is remembered in the vault and a lower one
is refused. Equal is fine; only strictly lower is a rollback.
The floor rises only for a record that VERIFIED, via a separate
`verifiedSerial`. Otherwise a hostile relay could hand over an unsigned record
carrying Int.MAX_VALUE, have the client raise its floor to it, and lock out
every later genuine record — turning the rollback defence into a denial of
service against the account holder. Tested.
Sealed in the vault and keyed by a hash of the address: which addresses this
device has looked up is exactly the metadata the vault exists to keep off a
stolen phone.
The refusal does not claim an attack. A relay that has genuinely fallen
behind serves an old record for honest reasons, and nothing in a client can
tell that from the other case.
8 unit tests. App builds, every JVM suite green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Both remaining protocol rows turned out to be stale.
AccountKdf.localAccountSaltis pinned to the published vector and wired intosign-in for a local relay;
MailboxHomingandAccountIdentity.publishHomeshave shipped the publish and read halves for a while. PARITY.md was describing
a gap that had closed, which is its own kind of wrong — a definition of done
that overstates the work left is one people stop reading.
What was genuinely missing was on the READING side, and on BOTH clients.
An older home record is GENUINELY SIGNED — the account holder published it,
before they added a second relay. So a relay that wants to be somebody's only
mailbox does not have to forge anything: it serves the record it saw first,
that record verifies perfectly, and the sender delivers to one relay believing
it is delivering to all of them. That is the attack multi-homing exists to
survive, and the serial is what was added to stop it.
The relay refuses a lower serial on WRITE. Nothing refused one on READ:
verifiedRelayshere andverifyHomeson iOS both checked the signature andstopped. Fixed on both — iOS in ios!116, this is the Kotlin half.
The highest serial seen per address is remembered in the vault and a lower one
is refused. Equal is fine; only strictly lower is a rollback.
The floor rises only for a record that VERIFIED, via a separate
verifiedSerial. Otherwise a hostile relay could hand over an unsigned recordcarrying Int.MAX_VALUE, have the client raise its floor to it, and lock out
every later genuine record — turning the rollback defence into a denial of
service against the account holder. Tested.
Sealed in the vault and keyed by a hash of the address: which addresses this
device has looked up is exactly the metadata the vault exists to keep off a
stolen phone.
The refusal does not claim an attack. A relay that has genuinely fallen
behind serves an old record for honest reasons, and nothing in a client can
tell that from the other case.
8 unit tests. App builds, every JVM suite green.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
assigned to @paulhitt
changed the description