Root Cause: auth_tx_acl can skip signature::authenticate_transaction when input note consumption is allowed, but transaction fees are still charged to the executing account.
Example:
- A public account uses
AuthSingleSigAcl with allow_unauthorized_input_notes=true.
- An attacker submits a transaction that consumes a (possibly zero-asset) input note, creates no output notes, and calls no configured trigger procedures.
auth_tx_acl takes the no-signature branch and the account still pays the transaction fee.
Location: miden-standards/asm/account_components/auth/singlesig_acl.masm
AuthSingleSigAcl computes auth_required as the OR of (1) "a trigger procedure root was detected as called", (2) "output notes were created and allow_unauthorized_output_notes is disabled", and (3) "input notes were consumed and allow_unauthorized_input_notes is disabled" (see the auth_required computation in auth_tx_acl). When auth_required is false, the procedure returns successfully without calling signature::authenticate_transaction, meaning the account can be executed in a "no-signature" mode.
If allow_unauthorized_input_notes=true, then input note consumption does not force authentication (see the input-note gate in auth_tx_acl). In this configuration, a transaction can consume at least one input note while avoiding both output-note creation and trigger procedures, keeping auth_required=false and skipping signature verification. Since transaction fees are still charged to the executing account, this enables fee-drain and sustained denial of service by repeatedly executing unsigned transactions until the account's fee-paying balance is exhausted; this is amplified by the no-signature nonce logic that may preserve the nonce when no pre-fee state change occurs (see the conditional nonce bump in auth_tx_acl and the fee-parameter commitment described in signature::authenticate_transaction).
How to resolve this issue?:
Approach 1:
We can consider requiring authentication whenever tx::get_num_input_notes != 0 for accounts that may be targeted by third-party executors, or otherwise ensuring that "no-signature" transactions cannot cause fee payment on behalf of the account (e.g., by forcing a nonce increment on any note consumption, or by requiring a signature for any transaction that consumes notes). Consider documenting and enforcing that allow_unauthorized_input_notes=true is unsafe for public accounts unless an additional executor authorization mechanism exists.
Approach 2:
Originally specified by @PhilippGackstatter in this comment: #2958 (comment)
We can flip the ACL semantics, mirroring the pattern already used by AuthMultisig::compute_transaction_threshold: instead of listing the procedures that trigger a signature requirement, we list the procedures that are exempt from it. Everything not on the exempt list defaults to requiring a signature, so forgetting to register a new setter can never silently leave it permissionless. This also lets us removing allow_unauthorized_output_notes and allow_unauthorized_input_notes flags.
Under the proposed semantics auth_tx_acl enforces three invariants and computes auth_required as their OR:
- any called procedure that is not on the exempt list forces
auth_required = true.
- if any input note is consumed AND no procedure from the exempt list was called during the transaction,
auth_required = true. This is the replacement for the removed allow_unauthorized_input_notes.
- if any output note was created,
auth_required = true, unconditionally.
Root Cause:
auth_tx_aclcan skipsignature::authenticate_transactionwhen input note consumption is allowed, but transaction fees are still charged to the executing account.Example:
AuthSingleSigAclwithallow_unauthorized_input_notes=true.auth_tx_acltakes the no-signature branch and the account still pays the transaction fee.Location:
miden-standards/asm/account_components/auth/singlesig_acl.masmAuthSingleSigAclcomputesauth_requiredas the OR of (1) "a trigger procedure root was detected as called", (2) "output notes were created andallow_unauthorized_output_notesis disabled", and (3) "input notes were consumed andallow_unauthorized_input_notesis disabled" (see theauth_requiredcomputation inauth_tx_acl). Whenauth_requiredis false, the procedure returns successfully without callingsignature::authenticate_transaction, meaning the account can be executed in a "no-signature" mode.If
allow_unauthorized_input_notes=true, then input note consumption does not force authentication (see the input-note gate inauth_tx_acl). In this configuration, a transaction can consume at least one input note while avoiding both output-note creation and trigger procedures, keepingauth_required=falseand skipping signature verification. Since transaction fees are still charged to the executing account, this enables fee-drain and sustained denial of service by repeatedly executing unsigned transactions until the account's fee-paying balance is exhausted; this is amplified by the no-signature nonce logic that may preserve the nonce when no pre-fee state change occurs (see the conditional nonce bump inauth_tx_acland the fee-parameter commitment described insignature::authenticate_transaction).How to resolve this issue?:
Approach 1:
We can consider requiring authentication whenever
tx::get_num_input_notes != 0for accounts that may be targeted by third-party executors, or otherwise ensuring that "no-signature" transactions cannot cause fee payment on behalf of the account (e.g., by forcing a nonce increment on any note consumption, or by requiring a signature for any transaction that consumes notes). Consider documenting and enforcing thatallow_unauthorized_input_notes=trueis unsafe for public accounts unless an additional executor authorization mechanism exists.Approach 2:
Originally specified by @PhilippGackstatter in this comment: #2958 (comment)
We can flip the ACL semantics, mirroring the pattern already used by
AuthMultisig::compute_transaction_threshold: instead of listing the procedures that trigger a signature requirement, we list the procedures that are exempt from it. Everything not on the exempt list defaults to requiring a signature, so forgetting to register a new setter can never silently leave it permissionless. This also lets us removingallow_unauthorized_output_notesandallow_unauthorized_input_notesflags.Under the proposed semantics
auth_tx_aclenforces three invariants and computesauth_requiredas their OR:auth_required = true.auth_required = true. This is the replacement for the removedallow_unauthorized_input_notes.auth_required = true, unconditionally.