Audit finding from #300 (commit 679f9fe)
Severity: medium
Category: crypto / authorization
File: crates/state/src/materialize.rs:565
Obvious fix: no
Description
A peer with ManageChannels permission can submit RotateChannelKey { channel_id, encrypted_keys } where the entries are independent (EndpointId, Vec<u8>) pairs. The state machine accepts any byte blob per recipient without verifying that all recipients receive the same underlying key. A malicious rotator can hand peer A a key K1 and peer B a different key K2, splitting the channel: A's messages decrypt for A but not B, and vice versa, while everyone still believes they're in the same encrypted channel. There is no on-DAG witness that ties the per-recipient ciphertexts to one canonical channel-key commitment.
Impact / Threat
An admin (or compromised admin) silently partitions an encrypted channel: each victim sees only a subset of messages while the rotator can read both halves. Equivalent to cross-channel/cross-session leakage in spirit. Not detectable from state alone.
Suggested fix
Add a key_commitment: [u8; 32] field to RotateChannelKey (e.g. H("willow-channel-key-commit" || channel_id || epoch || key)) and require that all subsequently-decrypted message keys hash to the same commitment. Reject rotates whose recipients can't reconstruct the same commitment from their decrypted blob. Alternatively require that the encrypted blobs use a deterministic AEAD-of-key with a public per-rotation nonce so any two recipients can verify they received the same key.
Verify
rg -n "RotateChannelKey" crates/state/src/materialize.rs crates/state/src/event.rs
Audit finding from #300 (commit 679f9fe)
Severity: medium
Category: crypto / authorization
File: crates/state/src/materialize.rs:565
Obvious fix: no
Description
A peer with
ManageChannelspermission can submitRotateChannelKey { channel_id, encrypted_keys }where the entries are independent(EndpointId, Vec<u8>)pairs. The state machine accepts any byte blob per recipient without verifying that all recipients receive the same underlying key. A malicious rotator can hand peer A a key K1 and peer B a different key K2, splitting the channel: A's messages decrypt for A but not B, and vice versa, while everyone still believes they're in the same encrypted channel. There is no on-DAG witness that ties the per-recipient ciphertexts to one canonical channel-key commitment.Impact / Threat
An admin (or compromised admin) silently partitions an encrypted channel: each victim sees only a subset of messages while the rotator can read both halves. Equivalent to cross-channel/cross-session leakage in spirit. Not detectable from state alone.
Suggested fix
Add a
key_commitment: [u8; 32]field toRotateChannelKey(e.g.H("willow-channel-key-commit" || channel_id || epoch || key)) and require that all subsequently-decrypted message keys hash to the same commitment. Reject rotates whose recipients can't reconstruct the same commitment from their decrypted blob. Alternatively require that the encrypted blobs use a deterministic AEAD-of-key with a public per-rotation nonce so any two recipients can verify they received the same key.Verify
rg -n "RotateChannelKey" crates/state/src/materialize.rs crates/state/src/event.rs