blindpsbt: refuse to blind a PSET output with no amount
What changed, and why it matters
This commit fixes a crash/undefined-behavior bug in the Elements wallet's confidential-transaction blinding code. When processing a specially crafted Partially Signed Elements Transaction (PSET), the software would blindly use an output amount that might not exist, which can crash the program or cause unpredictable behavior. The fix now checks whether the amount is present and refuses to blind the output if it is missing.
Apply the patch. Consider adding regression tests for crafted v0 PSETs with missing amount fields and review other optional PSET fields for similar missing-validation issues.
Security signals we found
Undefined behavior from dereferencing a disengaged std::optional
Missing input validation on optional PSET field
Potential denial-of-service/crash via crafted PSET
Confidential transaction blinding code path affected
Evidence from the diff
In src/blindpsbt.cpp, BlindPSBT iterated over PSBT outputs and dereferenced output.amount after checking only m_blinder_index. For PSET v0, the amount field is optional (enforced only for m_psbt_version >= 2), so a crafted v0 PSET with m_blinder_index set but amount absent reached the blinding loop with output.amount == std::nullopt. Dereferencing a disengaged std::optional is undefined behavior. The patch adds an explicit nullopt check and returns BlindingStatus::INVALID_BLINDER.
Changed components
src/blindpsbt.cppBlindPSBT functionPSET v0 output handlingInspect captured patch +8 / −0
### src/blindpsbt.cpp
@@ -508,6 +508,14 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
// Check this is our output to blind
if (output.m_blinder_index == std::nullopt || our_input_data.count(*output.m_blinder_index) == 0) continue;
+ // PSET v0 does not require an output amount (it is only enforced for
+ // m_psbt_version >= 2), so a crafted v0 PSET can reach the blinding
+ // loop with output.amount == nullopt. Dereferencing it is undefined
+ // behaviour. Refuse to blind such an output.
+ if (output.amount == std::nullopt) {
+ return BlindingStatus::INVALID_BLINDER;
+ }
+
// Things we are going to stuff into the PSBTOutput if everything is successful
CConfidentialValue value_commitment;
CConfidentialAsset asset_commitment;Why this scored 62/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.