Skip to content

PSBT fee and signing-gate hardening - #134

Merged
odudex merged 6 commits into
masterfrom
fee-hardening
Aug 7, 2026
Merged

PSBT fee and signing-gate hardening#134
odudex merged 6 commits into
masterfrom
fee-hardening

Conversation

@odudex

@odudex odudex commented Aug 7, 2026

Copy link
Copy Markdown
Owner
  • Input amounts are checked against the prevout txid. The fee was read from whichever utxo the PSBT offered, so a fabricated prev tx produced a wrong fee on screen and an understated witness_utxo displayed a lie while signing used the truth. Amounts are now classified proven/asserted/invalid/missing, contradictions are refused before review, and an unproven fee is marked on screen.
  • Sighash flags other than ALL are refused. Under NONE, SINGLE or ANYONECANPAY the signature survives while the parts it doesn't cover get rewritten. The reviewed outputs and fee are not what gets broadcast.
  • Partial signing is reported. Signing one of two inputs used to look like a clean success; the shortfall is now named before export.
  • Refused inputs can no longer collect a signature. wally_psbt_sign() signs any input whose keypath names the key it was handed, so a UTXO listed under someone else's fingerprint was shown as external and signed anyway. Refused inputs are snapshotted before signing and restored after, with discarded signatures counted and reported.
  • The fee is shown as a share of the inputs, flagged red at or above 10%.

odudex added 6 commits August 7, 2026 11:04
The fee was read from whichever utxo the PSBT offered, witness_utxo
first, with no check that a supplied previous transaction is the one
being spent. libwally applies that txid check only when signing a legacy
input, so a fabricated prev tx produced a wrong fee on screen, and an
understated witness_utxo next to a genuine prev tx displayed the lie
while signing used the truth.

Amounts are now classified as proven, asserted, invalid or missing, and
the proven value wins for display. Contradictory data is refused before
the review screen. Unproven amounts still sign -- coordinators trim
PSBT_IN_NON_WITNESS_UTXO for air-gapped transfers -- but the review
screen marks the fee unproven and Sign raises a confirmation first.
Signing logged per-input failures and reported success as long as one
input signed, so a session that signed one of two inputs looked clean.
That is exactly what an attacker collecting one signature per session
needs to stay unnoticed.

psbt_sign now counts inputs the policy cleared against inputs that
actually gained a signature, diffing the ECDSA map, PSBT_IN_TAP_KEY_SIG
and the taproot leaf signatures rather than trusting WALLY_OK, and the
scan page names the shortfall before offering export.
libwally honours whatever PSBT_IN_SIGHASH_TYPE byte an input declares and
Kern never looked at it. Under NONE, SINGLE or ANYONECANPAY a signature
stays valid while the parts it does not cover are rewritten, so the
outputs and fee on the review screen are not what gets broadcast.

Refused in the review gate and again per-input inside psbt_sign, matching
how the ownership policy is enforced in both places.
The review screen printed the fee with nothing to judge it against, so a
fee worth more than the payment read the same as a normal one.

The fee row now carries its percentage of the total inputs, and at or
above 10% the screen flags it in red. Both fee warnings stay on the review
screen rather than as a prompt behind Sign: the unproven-fee confirmation
is dropped too, since it repeated what the screen already says.
wally_psbt_sign() walks every input and signs any whose keypath names the
key it was given, so classifying each input in the signing loop decided
nothing: an input Kern refused still picked up a signature if it named a
key another input signs with. An attacker could list a UTXO of ours under
someone else's fingerprint, have it shown as external and not ours, and
harvest the signature anyway.

Classification now runs up front, refused inputs are snapshotted before
signing and restored after, and discarded signatures are counted and
reported. A PSBT whose fingerprints are simply wrong will now fail to
sign rather than working by accident.
@odudex
odudex merged commit cba8875 into master Aug 7, 2026
16 checks passed
@odudex
odudex deleted the fee-hardening branch August 13, 2026 13:30
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