PSBT fee and signing-gate hardening - #134
Merged
Merged
Conversation
odudex
commented
Aug 7, 2026
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%.
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.
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.