Split out of the review of #399, where the leaf crate bounds the symptom and cannot close the cause.
The gap
The canonical control-frame signing payload (control_signing_payload in offline-protocol-sealed) is built from the sender, the id, the recipient and the content. It states who, to whom, and what. It states nothing about when, so a frame captured off the air verifies exactly as well on its tenth delivery as on its first.
The destructive case is a key package carrying session_reset, which tears a live session down. Anyone who records one has a repeatable way to break a pair.
What is in place, and what it is not
offline-protocol-leaf remembers the ids of the last few reset frames it acted on, so the same frame cannot spend twice. That bounds a repeat. It does not close replay: an attacker holding a reset frame older than that ring can still spend it once, and the ring is per-peer and bounded because the device it runs on has a few hundred kilobytes of flash.
The phone has no equivalent memory at all.
What closing it needs
A freshness field inside the signed payload: a timestamp the verifier bounds, a counter it tracks, or a nonce it has to have issued. Each is a change to the wire and to both ends, and each has to survive a leaf whose clock is only as good as its last pairing. That is why it is not a device-side patch.
Recorded as an open gap in the reset section of docs/spec/leaf-provisioning.md. Sibling of #396 (unbounded key package lifetimes admitted on import), which came out of the same stage.
Split out of the review of #399, where the leaf crate bounds the symptom and cannot close the cause.
The gap
The canonical control-frame signing payload (
control_signing_payloadinoffline-protocol-sealed) is built from the sender, the id, the recipient and the content. It states who, to whom, and what. It states nothing about when, so a frame captured off the air verifies exactly as well on its tenth delivery as on its first.The destructive case is a key package carrying
session_reset, which tears a live session down. Anyone who records one has a repeatable way to break a pair.What is in place, and what it is not
offline-protocol-leafremembers the ids of the last few reset frames it acted on, so the same frame cannot spend twice. That bounds a repeat. It does not close replay: an attacker holding a reset frame older than that ring can still spend it once, and the ring is per-peer and bounded because the device it runs on has a few hundred kilobytes of flash.The phone has no equivalent memory at all.
What closing it needs
A freshness field inside the signed payload: a timestamp the verifier bounds, a counter it tracks, or a nonce it has to have issued. Each is a change to the wire and to both ends, and each has to survive a leaf whose clock is only as good as its last pairing. That is why it is not a device-side patch.
Recorded as an open gap in the reset section of docs/spec/leaf-provisioning.md. Sibling of #396 (unbounded key package lifetimes admitted on import), which came out of the same stage.