Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional safety checks in PSBT signer #13917

Merged
merged 4 commits into from Aug 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/script/sign.cpp
Expand Up @@ -244,17 +244,33 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t
input.FillSignatureData(sigdata);

// Get UTXO
bool require_witness_sig = false;
CTxOut utxo;
if (input.non_witness_utxo) {
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit "Additional sanity checks in SignPSBTInput"

nit, could reflow comments.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they're fine.

if (input.non_witness_utxo->GetHash() != tx.vin[index].prevout.hash) return false;
// If both witness and non-witness UTXO are provided, verify that they match. This check shouldn't
// matter, as the PSBT deserializer enforces only one of both is provided, and the only way both
// can be present is when they're added simultaneously by FillPSBT (in which case they always match).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment based on the BIP/spec or implementation? What about other implementations that may not do what FillPSBT does in Bitcoin Core? I understand that the check is done anyway, but the comment sounds like it could be skipped due to an implementation detail, which sounds error-prone.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@achow101 just opened a PR that adds the actual requirements to test for to BIP 174, including a simple implementation in pseudocode that implements it.

// Still, check in order to not rely on callers to enforce this.
if (!input.witness_utxo.IsNull() && input.non_witness_utxo->vout[tx.vin[index].prevout.n] != input.witness_utxo) return false;
utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n];
} else if (!input.witness_utxo.IsNull()) {
utxo = input.witness_utxo;
// When we're taking our information from a witness UTXO, we can't verify it is actually data from
// the output being spent. This is safe in case a witness signature is produced (which includes this
// information directly in the hash), but not for non-witness signatures. Remember that we require
// a witness signature in this situation.
require_witness_sig = true;
} else {
return false;
}

MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
sigdata.witness = false;
bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
// Verify that a witness signature was produced in case one was required.
if (require_witness_sig && !sigdata.witness) return false;
input.FromSignatureData(sigdata);
return sig_complete;
}
Expand Down
2 changes: 1 addition & 1 deletion src/script/sign.h
Expand Up @@ -678,7 +678,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);

/** Signs a PSBTInput */
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash = 1);

/** Extract signature data from a transaction input, and insert it. */
Expand Down
15 changes: 9 additions & 6 deletions src/wallet/rpcwallet.cpp
Expand Up @@ -4504,10 +4504,11 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C

// If we don't know about this input, skip it and let someone else deal with it
const uint256& txhash = txin.prevout.hash;
const auto& it = pwallet->mapWallet.find(txhash);
const auto it = pwallet->mapWallet.find(txhash);
if (it != pwallet->mapWallet.end()) {
const CWalletTx& wtx = it->second;
CTxOut utxo = wtx.tx->vout[txin.prevout.n];
// Update both UTXOs from the wallet.
input.non_witness_utxo = wtx.tx;
input.witness_utxo = utxo;
}
Expand All @@ -4524,11 +4525,13 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C
complete &= SignPSBTInput(PublicOnlySigningProvider(pwallet), *psbtx.tx, input, sigdata, i, sighash_type);
}

// Drop the unnecessary UTXO
if (sigdata.witness) {
input.non_witness_utxo = nullptr;
} else {
input.witness_utxo.SetNull();
if (it != pwallet->mapWallet.end()) {
// Drop the unnecessary UTXO if we added both from the wallet.
if (sigdata.witness) {
input.non_witness_utxo = nullptr;
} else {
input.witness_utxo.SetNull();
}
}

// Get public key paths
Expand Down