Skip to content

[SEC-A-06] RotateChannelKey allows divergent per-recipient keys (cross-channel/cross-recipient leak) #308

@intendednull

Description

@intendednull

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions