live_messaging_verify.py reads descriptors with live_rooms_verify.py, Key IDs, keys, AES-GCM and plaintexts with e2ee_verify.py, and implements the rest: every record, the receipts-track object of jwp-receipts' binary form, a conversation entry by entry, what a principal is owed, and calls. It agrees with wai-rs on every vector, fails every mutated copy of the corpus, and catches each of its own checks dropped. The corpus gains receipt-level and plaintext-level controls, a conversation under AES_256_GCM_SHA512_128, and history requests that state whether the requester is admitted (§9.3). Co-Authored-By: Claude Opus 5.5 <[email protected]>
78 lines
5.4 KiB
Markdown
78 lines
5.4 KiB
Markdown
# live-conformance/messaging — conversations and calls
|
|
|
|
The corpus of [`extensions/live-messaging.md`](../../extensions/live-messaging.md) §13:
|
|
conversations on end-to-end encrypted rooms of [live-rooms](../../extensions/live-rooms.md) as a
|
|
history store's log holds them (epoch statements, sealed messages with their receipts, and
|
|
acknowledgements), what principals are owed of a log, calls, and every record of the extension
|
|
on its own.
|
|
|
|
**Conformance.** A conforming verifier reaches, for every vector, the outcome it states:
|
|
|
|
- each of `conversations`, given its `input` (`descriptor`, `record`, `statement`, the
|
|
`secrets` of the epochs it opens, and the `entries` in the log's order, each at its
|
|
`position`; §11.1): its refusal, `reason` and `at` (§11.2), or the view of §11.5: every
|
|
epoch with its roster; every message at its position with its `author`, `group`, `link`
|
|
(none where its receipts do not read) and `outcome` (`accepted`, `sealed`, or the reason
|
|
§11.3 refuses it), and for an accepted one its `kind`, `t`, `target` and `retracted_by`, a
|
|
text's or a reply's `status`, current `text`, `edits` and `reactions`, and a text's
|
|
`replies`; every acknowledgement's `outcome`; and `acknowledged`, the last group each member
|
|
acknowledged delivered and read of each author;
|
|
- each of `history` (§9.3): the content positions the requester is `owed` of the named
|
|
conversation's log from position `from`, given whether it is `admitted`, or `refused`;
|
|
- each of `calls` (§10): its refusal, `reason` and `at`, or each invitation's `callee`, `t`,
|
|
`deadline`, `state` and the `at` of its answer;
|
|
- each of `records` (§2): `ok`, or the reason the record is refused when it is read alone,
|
|
checked to name `room` and verified under `key`.
|
|
|
|
`about` says what each vector is, and is not checked.
|
|
|
|
**Provenance.** Generated here: `wai_live_messaging_vectors gen` writes `vectors.json` from
|
|
conversations and calls authored in code, from fixed seeds. Each key is SHA-256 of
|
|
`wai:live-messaging/corpus/key`, a zero byte and the principal's index (`principals` lists their
|
|
public keys). Each epoch's application secret is SHA-256 of `wai:live-messaging/corpus/secret`,
|
|
a zero byte, the generation and the epoch: it stands where an MLS exporter gives one, the
|
|
boundary confidentiality §14 draws, so the corpus runs no MLS; every message is sealed under it by
|
|
confidentiality §4, in the suite the conversation's record names. In a conversation's log, the
|
|
position before each epoch statement is left empty, where a store holds the GroupInfo or commit
|
|
that opened it; a verifier is not given handshake messages (§11.1). Ed25519 and AES-GCM under a
|
|
fixed key and nonce are deterministic, so the file is the same bytes on every machine. The
|
|
generator asserts that the reference reaches the outcome written for every message and
|
|
acknowledgement, and that the corpus gives every reason of §12, before it writes. No recorded
|
|
data.
|
|
|
|
## Layout
|
|
|
|
`vectors.json`: `principals` (informative), `key_seed`, `secret_seed`, `conversations`,
|
|
`history`, `calls` and `records`.
|
|
|
|
- `conversation`: alice creates the group; bob and carol join. A thread on alice's root with
|
|
two replies; reactions, one retracted; edits of the root and of a reply; a text retracted;
|
|
acknowledgements of delivery and reading, and one of each refusal of §8; then one message
|
|
refused for each content reason of §11.3 steps 8 to 12, each a control of one defect; and a
|
|
padded message.
|
|
- `removal`: a message before any epoch statement; bob removed, his message after the removal
|
|
refused `sender_not_member` while his earlier one stands; dave joining later at bob's old
|
|
leaf; one message refused for each reason of §11.3 steps 3 to 7: a stale epoch, another's
|
|
leaf, an empty leaf, properties that do not read, no Key ID, a gap, receipts in the legacy
|
|
form, of another key, naming a parent not held, naming none, with two objects, a changed
|
|
signature, a total beside the unmetered marker, another position, another root, a payload
|
|
changed after it was signed, a payload sealed under another secret, an inner digest that is
|
|
not the payload's, and a plaintext that does not read.
|
|
- `sealed_reader`: a verifier without epoch 1's secret: its message is `sealed`, and its
|
|
acknowledgement is checked.
|
|
- `suite_256`: a conversation under `AES_256_GCM_SHA512_128`.
|
|
- refused: the descriptor's signature; no confidentiality record; a record's suite, its MLS
|
|
suite, a recorded room; each rule of the statement; and each rule of §7.3.
|
|
- `history`: against `removal`, a member from the start, bob from the start and from his
|
|
removal, carol from a later position, dave, eve never admitted and eve admitted and waiting
|
|
for her Welcome, and alice past the log's end.
|
|
- `calls`: a call between two principals accepted; a call among four in which one accepts, one
|
|
declines and one is missed by the clock's answer; a call cancelled; one still ringing; and one
|
|
refused for each reason of §10.1 to §10.3.
|
|
- `records`: each of the seven records as signed, then cut, renamed, of another version,
|
|
extended, read beside another room, checked under another key, and with its signature changed.
|
|
|
|
The second implementation, `../live_messaging_verify.py`, reads descriptors with
|
|
`../live_rooms_verify.py` and Key IDs, keys, AES-GCM and plaintexts with `../e2ee_verify.py`,
|
|
and implements the rest from the extension's text; it also mutates copies of the corpus and
|
|
drops each of its own checks in turn, and requires the corpus to catch each one.
|