diff options
author | John L. Jegutanis <erasmospunk@gmail.com> | 2019-09-06 20:54:00 +0200 |
---|---|---|
committer | John L. Jegutanis <erasmospunk@gmail.com> | 2019-10-08 01:45:36 +0300 |
commit | 9743432034586385cfef87df4b377c255ed0cba8 (patch) | |
tree | 27482fb0a24712803f5243868bcac20b1ab84d30 /src | |
parent | febc31c641b6930f6dc9a97d9492c868ae032bc3 (diff) |
Fix bug where duplicate PSBT keys are accepted
As per the BIP 174 spec a PSBT key cannot be duplicated,
however the current code accepts key duplication.
The PSBT key/value entries can be duplicated when the value
is `empty()` or `IsNull()` for `CScript` or `CTxOut` respectively
and if those key/value entries are serialized before the non-empty ones.
For example, the following PSBT, included in the test vectors,
contains a duplicate field:
```
// magic
70736274ff
// global tx
//// key
0100
//// value
2a02000000000140420f000000000017a9146e91b72d5593e7d4391e2ff44e91e985c31641f08700000000
//// separator
00
// no inputs
// outputs
//// key PSBT_OUT_WITNESSSCRIPT
0101
//// value (empty script)
00
//// key PSBT_OUT_WITNESSSCRIPT (same as the above)
0101
//// value (an OP_RETURN script)
016a
//// separator
00
```
Diffstat (limited to 'src')
-rw-r--r-- | src/psbt.h | 29 |
1 files changed, 19 insertions, 10 deletions
diff --git a/src/psbt.h b/src/psbt.h index 6d77db0c6f..802a7c5ba7 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -126,6 +126,9 @@ struct PSBTInput template <typename Stream> inline void Unserialize(Stream& s) { + // Used for duplicate key detection + std::set<std::vector<unsigned char>> key_lookup; + // Read loop bool found_sep = false; while(!s.empty()) { @@ -147,7 +150,7 @@ struct PSBTInput switch(type) { case PSBT_IN_NON_WITNESS_UTXO: { - if (non_witness_utxo) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Non-witness utxo key is more than one byte type"); @@ -158,7 +161,7 @@ struct PSBTInput break; } case PSBT_IN_WITNESS_UTXO: - if (!witness_utxo.IsNull()) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, input witness utxo already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Witness utxo key is more than one byte type"); @@ -189,7 +192,7 @@ struct PSBTInput break; } case PSBT_IN_SIGHASH: - if (sighash_type > 0) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, input sighash type already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Sighash type key is more than one byte type"); @@ -198,7 +201,7 @@ struct PSBTInput break; case PSBT_IN_REDEEMSCRIPT: { - if (!redeem_script.empty()) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, input redeemScript already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Input redeemScript key is more than one byte type"); @@ -208,7 +211,7 @@ struct PSBTInput } case PSBT_IN_WITNESSSCRIPT: { - if (!witness_script.empty()) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, input witnessScript already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Input witnessScript key is more than one byte type"); @@ -223,7 +226,7 @@ struct PSBTInput } case PSBT_IN_SCRIPTSIG: { - if (!final_script_sig.empty()) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Final scriptSig key is more than one byte type"); @@ -233,7 +236,7 @@ struct PSBTInput } case PSBT_IN_SCRIPTWITNESS: { - if (!final_script_witness.IsNull()) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Final scriptWitness key is more than one byte type"); @@ -309,6 +312,9 @@ struct PSBTOutput template <typename Stream> inline void Unserialize(Stream& s) { + // Used for duplicate key detection + std::set<std::vector<unsigned char>> key_lookup; + // Read loop bool found_sep = false; while(!s.empty()) { @@ -330,7 +336,7 @@ struct PSBTOutput switch(type) { case PSBT_OUT_REDEEMSCRIPT: { - if (!redeem_script.empty()) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, output redeemScript already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Output redeemScript key is more than one byte type"); @@ -340,7 +346,7 @@ struct PSBTOutput } case PSBT_OUT_WITNESSSCRIPT: { - if (!witness_script.empty()) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, output witnessScript already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Output witnessScript key is more than one byte type"); @@ -448,6 +454,9 @@ struct PartiallySignedTransaction throw std::ios_base::failure("Invalid PSBT magic bytes"); } + // Used for duplicate key detection + std::set<std::vector<unsigned char>> key_lookup; + // Read global data bool found_sep = false; while(!s.empty()) { @@ -469,7 +478,7 @@ struct PartiallySignedTransaction switch(type) { case PSBT_GLOBAL_UNSIGNED_TX: { - if (tx) { + if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, unsigned tx already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Global unsigned tx key is more than one byte type"); |