fix(eip8130): canonical sender_auth encodings (tx-hash/gas malleability) - #4439
Draft
chunter-cb wants to merge 1 commit into
Draft
fix(eip8130): canonical sender_auth encodings (tx-hash/gas malleability)#4439chunter-cb wants to merge 1 commit into
chunter-cb wants to merge 1 commit into
Conversation
…ash/gas malleability
sender_auth cannot be covered by the sender/payer signature hashes (a
signature cannot sign over itself), yet every auth byte is billed as
EIP-2028 payload gas. Combined with non-canonical auth encodings, a relayer
could mutate the auth blob to mint a second valid transaction hash and shrink
the execution-gas budget for the same signer without the key.
Two encodings were accepted for the same authorization:
- EOA sender path: alloy's parser normalizes v in {0,1,27,28} to the same
parity, so v could be flipped to a non-canonical byte (txid malleability).
Enforce v in {27,28} (Electrum notation) and a strict 65-byte length before
parsing, matching the k1 authenticator's existing check. Applied to both the
checked and unchecked recovery paths so all entry points agree.
- WebAuthn: abi_decode_params ignores trailing/non-canonical ABI bytes, which
decode to the same value (and pass the P-256 check) but change the tx hash
and inflate payload gas. Require the input to equal its canonical ABI
re-encoding, rejecting only malleated blobs (honest abi.encode output is
unaffected). Covers both sender and payer auth via the shared dispatch.
Adds consensus tests asserting that changing an unsigned authentication byte
(EOA v -> {0,1}; WebAuthn trailing byte) invalidates the transaction.
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes an auth-encoding malleability + gas-griefing vector on the EIP-8130 pipeline.
sender_auth/payer_authcannot be covered by the sender/payer signature hashes (a signature can't sign over itself), yet every auth byte is billed as EIP-2028 payload gas (Eip8130IntrinsicGas::payload_costfolds over the full signed encoding). Because two distinct byte encodings were accepted for the same authorization, any relayer could mutate the auth blob to:Root causes fixed
EOA sender
v— the empty-senderpath recovered throughalloy_primitives::Signature::try_from, whosenormalize_vmaps{0,1,27,28}to the same parity. Sovcould be flipped to a non-canonical byte and still recover the same signer. Now enforce a strict 65-byte length andv ∈ {27,28}(Electrum notation) before parsing, matching the k1 authenticator's existingrecover_k1check. Applied in the sharedrecover_eoa_sender_with, so the checked and unchecked paths agree (no path can accept a tx another rejects). Honest tooling (Signature::as_bytes→27 + y_parity) is unaffected.WebAuthn —
abi_decode_paramsignores trailing / non-canonical ABI bytes, which decode to the same value and still pass the P-256 check, but change the tx hash and inflate payload gas. Now require the input to equal its canonical ABI re-encoding (decoded.abi_encode_params() != data → reject). Covers sender and payer auth via the shared dispatch. P-256 already enforced a fixed 129-byte length, so no trailing bytes were possible there.Tests
recover_eoa_sender_rejects_noncanonical_v: rewritingvfrom{27,28}to the{0,1}encoding of the same parity is rejected on both recovery paths.webauthn_rejects_trailing_bytes: appending an unsigned trailing byte to an otherwise-valid blob is rejected.Test plan
cargo test -p base-execution-eip8130(187 passed)cargo test -p base-common-consensus --all-features(eip8130 suite green; new test passes)cargo clippy -p base-execution-eip8130clean (default features)Notes / parity
The tx-level sender/payer signatures and intrinsic-gas accounting are node/protocol concerns, not
Keystore.solsurface, so there is no contract-parity regression. The WebAuthn canonical-ABI check is intentionally stricter than OZabi.decode(which tolerates trailing bytes), but only rejects malleated blobs — honestabi.encodeoutput always matches its canonical re-encoding — so no honest transaction is rejected.