Skip to content

authorization: reject bodies with the wrong number of authorizations#32

Open
ace-degen wants to merge 1 commit into
LayerTwo-Labs:masterfrom
ace-degen:fix/verify-authorizations-count-check
Open

authorization: reject bodies with the wrong number of authorizations#32
ace-degen wants to merge 1 commit into
LayerTwo-Labs:masterfrom
ace-degen:fix/verify-authorizations-count-check

Conversation

@ace-degen

Copy link
Copy Markdown

Severity: high — consensus-level authorization bypass (theft of funds).
A miner can spend another user's UTXO without a valid signature.

Summary

verify_authorizations pairs body.authorizations with the per-input messages using .zip(), which stops at the shorter iterator. A body carrying fewer authorizations than inputs is therefore accepted, and the surplus inputs are never signature-checked.

Block validation reaches this code via Authorization::verify_body (lib/state/block.rsverify_bodyverify_authorizations), so the gap is consensus-critical: a miner can spend a UTXO without a valid signature by ordering an authorized input first and omitting the authorization for the victim's input. The address-binding .zip() in state::block has the same truncation, but it is gated behind verify_body, so enforcing the count in one place closes both.

Comparison with sibling L2s

Thunder, CoinShift, Photon, and Truthcoin-DC all reject a count mismatch before signature verification. plain-bitassets is missing this check. This PR adds the same guard, matching the NotEnoughAuthorizations / TooManyAuthorizations idiom used elsewhere.

Change

  • Add Error::NotEnoughAuthorizations and Error::TooManyAuthorizations.
  • At the top of verify_authorizations, compare body.authorizations.len() against the total input count and reject any mismatch.
  • Add regression tests: too-few (the bypass), too-many, and matching counts.

Testing

cargo test --lib authorization::tests
# rejects_too_few_authorizations       ... ok
# rejects_too_many_authorizations      ... ok
# accepts_matching_authorization_count ... ok

Full cargo test --lib passes; cargo fmt --check and cargo clippy --lib are clean.

`verify_authorizations` paired `body.authorizations` with the per-input
messages using `.zip()`, which stops at the shorter iterator. A body
carrying fewer authorizations than inputs was therefore accepted, leaving
the surplus inputs unverified. Because block validation reaches this code
via `Authorization::verify_body`, a miner could spend a UTXO without a
valid signature by ordering an authorized input first and omitting the
authorization for the victim's input -- a consensus-level authorization
bypass (theft).

The address-binding `.zip()` in `state::block::validate_body` has the same
truncation, but it is gated behind `verify_body`, so enforcing the count
in one place closes both.

Add an explicit count check at the top of `verify_authorizations`,
returning `NotEnoughAuthorizations` / `TooManyAuthorizations`, mirroring
the sibling Rust L2s (e.g. Thunder). Add regression tests for too-few,
too-many, and matching authorization counts.
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.

1 participant