Commit 57761bd
committed
release: 0.2.0-alpha.1 (SPEC draft-03) — mandatory session binding + normative transition table
Phase B of the open-issues plan. Closes the remaining two items from
the round-2 cryptographic review. Breaking wire change at the signed-
consent-body layer; the envelope (SPEC §1-§11) is unchanged, so
FRAME / INPUT / FRAME_LZ4 traffic is still interoperable with any
0.1.x peer.
Issue #1 — Mandatory session_fingerprint (SPEC §12.3.1)
Every signed consent body now carries a 32-byte session_fingerprint
derived locally per-peer via HKDF-SHA-256:
salt = b"xenia-session-fingerprint-v1"
ikm = current session_key
info = source_id || epoch || request_id_be (17 bytes)
L = 32
Both peers derive the same value; receivers re-derive and compare
in constant time. Closes the "loose session binding" replay-across-
sessions gap that draft-02r1 documented as a known limitation. The
big-endian request_id in `info` also buys per-ceremony replay
protection: the fingerprint for request_id=7 is unrelated to the
one for request_id=8, so a captured response for one ceremony
cannot replay as a response to the other.
API additions:
- Session::session_fingerprint(request_id) -> [u8; 32]
- Session::sign_consent_{request,response,revocation}(core, sk)
- Session::verify_consent_{request,response,revocation}(msg, expected_pk)
All three Cores gain `session_fingerprint: [u8; 32]` at a fixed
position in canonical field order. Test vectors 07/08/09 regenerated
(shared fingerprint 5b94fb75…d7d4 — same session + same request_id=7).
Issue #3 — Normative transition table + ConsentProtocolViolation (SPEC §12.6.1)
The consent state machine is fully normative now: every (state,
event, request_id) tuple maps to a specific next state or one of
three defined protocol violations.
WireError::ConsentProtocolViolation(ConsentViolation)
ConsentViolation::RevocationBeforeApproval { request_id }
ConsentViolation::ContradictoryResponse { request_id, prior_approved, new_approved }
ConsentViolation::StaleResponseForUnknownRequest { request_id }
Key design decisions baked into the table:
- LegacyBypass is sticky; an unsolicited Request does NOT auto-
promote (prevents state-hijacking by a malicious peer).
- Revocation from AwaitingRequest or Requested is a hard error
(nothing to revoke; the peer is broken).
- Contradictory Response (Denied-after-Approved, or vice-versa,
on the same request_id) is a hard error. SPEC §12.6.2 records
the UI guidance: a user "changing their mind" must emit a
fresh ConsentRevocation, not a contradictory Response.
- A higher request_id from any non-LegacyBypass state starts a
fresh ceremony (terminal states are not absolute — you can
always renegotiate).
- On violation, the session state is NOT mutated. The wire does
not own the transport; the caller decides whether to tear down.
ConsentEvent variants now carry request_id:
Request{request_id}, ResponseApproved{request_id},
ResponseDenied{request_id}, Revocation{request_id}
Session::observe_consent now returns
Result<ConsentState, ConsentViolation>.
SPEC draft-03
- §1.4 version + breaking-change header
- §1.4.1 compat matrix (envelope compatible; signed bodies not)
- §12.3 three message structures updated with session_fingerprint,
canonical field order declared normative
- §12.3.1 NEW — session fingerprint derivation (HKDF-SHA-256
parameters, constant-time comparison requirement,
rekey interaction, rationale for MANDATORY vs OPTIONAL)
- §12.3.2 renumbered from old §12.3.1 (canonical bincode encoding)
- §12.6 sticky LegacyBypass + state-hijacking rationale
- §12.6.1 NEW — full normative transition table
- §12.6.2 NEW — UI guidance for "change of mind" after approval
- §12.7 FRAME gating header updated to draft-03
- §12.8 security-properties list: session binding is now TIGHT,
new protocol-violation-detection bullet
- App B draft-03 row
Dependencies
+ hkdf = "0.12" (consent feature)
+ sha2 = "0.10" (consent feature)
No new runtime deps when the consent feature is disabled.
Tests
101 tests green (up from 95 at 0.1.0-alpha.5):
- 44 lib unit (+1 new: consent_request_rejects_tampered_fingerprint)
- 13 integration_consent (up from 7: +violation/fingerprint tests)
- 5 proptest_consent + 9 proptest_replay_window
- 5 integration_rekey_replay + 10 integration_rekey
- 4 smoke_fuzz
- 11 test_vector_validation (+1: vectors_07_08_09_share_session_fingerprint)
- 1 doctest
cargo clippy --all-features --all-targets -- -D warnings : clean
cargo clippy --no-default-features --all-targets -- -D warnings : clean
cargo package : 470.8 KiB (145.9 KiB compressed)
Plan status
All four round-2 review items now closed:
- #1 session_binding field : closed (mandatory)
- #2 split Pending : closed in 0.1.0-alpha.5
- #3 duplicate/conflict transitions : closed
- #4 configurable replay window : closed in 0.1.0-alpha.5
Migration from 0.1.0-alpha.5
1. `Session::new` / `Session::builder` callers: no change.
2. Struct-literal constructions of the three Cores gain
`session_fingerprint: [u8; 32]`. Use `Session::sign_consent_*`
helpers to have the fingerprint derived+injected automatically.
3. Every `ConsentEvent::*` pattern match gains `{ request_id }`.
4. Every `observe_consent(...)` call returns `Result` — handle
`ConsentViolation` as a session-teardown signal.
5. draft-03 consent bodies do not interoperate with 0.1.x — both
sides must upgrade.1 parent cf739dc commit 57761bd
22 files changed
Lines changed: 1623 additions & 504 deletions
File tree
- examples
- src
- test-vectors
- tests
- xenia-viewer-web
- src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
10 | 148 | | |
11 | 149 | | |
12 | 150 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
32 | | - | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| |||
41 | 42 | | |
42 | 43 | | |
43 | 44 | | |
| 45 | + | |
| 46 | + | |
44 | 47 | | |
45 | 48 | | |
46 | 49 | | |
| |||
0 commit comments