Skip to content

bound the peeling decoder on forged tables - #12

Merged
noot merged 4 commits into
mainfrom
noot/mse-decode-checks
Nov 4, 2026
Merged

noot merged 4 commits into
mainfrom
noot/mse-decode-checks

Conversation

@noot

@noot noot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Adds the two guards the paper's decoder has: a peeled key must hash to the cell it was read from, and at most capacity elements are emitted. MseParams records the capacity, the channel passes the ρ it already receives, and the two cases surface as ChannelError::MalformedEncoding(MseError::{OverCapacity, KeyMismatch}).

paper divergence

In "Multiset Encodings from Invertible Bloom Lookup Tables", decode checks before every peel that the cell's key indeed hashes to that cell and that fewer than ρ elements have been output, returning ⊥ otherwise; the text says this does not affect honest encodings but bounds decode to ρ+1 iterations on every input. The correctness proof in the appendix relies on honest sums never tripping either check. The code had neither, so a single corrupt client could submit a forged table and make the recipient peel garbage until the counters cycled. This is a denial of service against the recipient only; the decoder sees the decrypted sum, so nothing per client leaks.

Overlaps with the draft in #8, which adds the same guards as part of the parameter alignment. If that lands first this can be closed.

verification

All tests pass. New unit tests: a table whose count-one cell holds a shifted key is rejected on the first peel, and capacity plus one honest elements are refused.

noot added 3 commits October 29, 2026 15:25
Adds the paper's two decode guards: a peeled key must hash to the cell it was
read from, and at most capacity elements are emitted. Honest sums never trip
them; a forged contribution now costs the recipient at most capacity peels
instead of running until the counters cycle. The table parameters record the
capacity, which the paper's theorem already treats as a scheme parameter.
@noot
noot requested a balanced review from Copilot October 30, 2026 18:37
@noot noot changed the title bounds the peeling decoder on forged tables bound the peeling decoder on forged tables Oct 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The capacity check in src/mse.rs casts usize to i64, allowing oversized values such as usize::MAX to pass on 64-bit targets and violate the counter no-wrap constraint.

1 open issue
What changed in this PR

This PR hardens the multiset peeling decoder against forged tables by checking key-to-cell consistency and limiting emitted elements to the configured capacity.

Changes:

  • Records capacity in MseParams and passes the channel’s capacity into it.
  • Adds decoder guards and exposes their failures through ChannelError::MalformedEncoding.
  • Adds rejection tests, updates constructor calls, and documents the bounded decoding behavior.
File Description
tests/​rs_mode_e2e.rs Supplies capacity when constructing multiset parameters.
tests/​mse_e2e.rs Updates test parameters with explicit capacities.
src/​mse.rs Adds capacity, decoder guards, error variants, and rejection tests.
src/​channel.rs Passes capacity and maps guard failures to channel errors.
README.md Documents malformed-table errors and the peel limit.

Review effort: Balanced

Comment thread src/mse.rs Outdated
assert!(delta >= 1, "δ must be ≥ 1");
assert!(capacity >= 1, "capacity must be ≥ 1");
assert!(
(capacity as i64) < T,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, fixed: now compares as u64 so a huge capacity can't wrap negative

@ameba23 ameba23 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

I think we should merge this regardless of whether #8 is merged as the error handling is a bit better.

I didnt measure performance impact but this adds an extra sha256 per decoded element.

One thing i find a bit confusing not related to this PR - i think src/mse.rs should be iblt.rs. I'm guessing its named mse because it was added first and prony later, but in my understanding both are multi-set encodings.

Comment thread src/mse.rs
) -> Self {
assert!(gamma >= 2, "γ must be ≥ 2 (Theorem 3 needs γ ≥ 2)");
assert!(delta >= 1, "δ must be ≥ 1");
assert!(capacity >= 1, "capacity must be ≥ 1");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess we could use NonZeroUsize but this matches the how delta is handled to lets keep it like this.

@noot
noot merged commit 9f07274 into main Nov 4, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants