diff options
Diffstat (limited to 'src/script/sign.h')
-rw-r--r-- | src/script/sign.h | 95 |
1 files changed, 75 insertions, 20 deletions
diff --git a/src/script/sign.h b/src/script/sign.h index 7ade715ee2..20c7203b26 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -20,6 +20,17 @@ class CTransaction; struct CMutableTransaction; +struct KeyOriginInfo +{ + unsigned char fingerprint[4]; + std::vector<uint32_t> path; + + friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b) + { + return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path; + } +}; + /** An interface to be implemented by keystores that support signing. */ class SigningProvider { @@ -28,29 +39,36 @@ public: virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; } virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; } virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; } + virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; } }; extern const SigningProvider& DUMMY_SIGNING_PROVIDER; -class PublicOnlySigningProvider : public SigningProvider +class HidingSigningProvider : public SigningProvider { private: + const bool m_hide_secret; + const bool m_hide_origin; const SigningProvider* m_provider; public: - PublicOnlySigningProvider(const SigningProvider* provider) : m_provider(provider) {} - bool GetCScript(const CScriptID &scriptid, CScript& script) const; - bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const; + HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {} + bool GetCScript(const CScriptID& scriptid, CScript& script) const override; + bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override; + bool GetKey(const CKeyID& keyid, CKey& key) const override; + bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; }; struct FlatSigningProvider final : public SigningProvider { std::map<CScriptID, CScript> scripts; std::map<CKeyID, CPubKey> pubkeys; + std::map<CKeyID, KeyOriginInfo> origins; std::map<CKeyID, CKey> keys; bool GetCScript(const CScriptID& scriptid, CScript& script) const override; bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override; + bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; bool GetKey(const CKeyID& keyid, CKey& key) const override; }; @@ -98,7 +116,7 @@ struct SignatureData { CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs. CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144. std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness. - std::map<CKeyID, CPubKey> misc_pubkeys; + std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys; SignatureData() {} explicit SignatureData(const CScript& script) : scriptSig(script) {} @@ -136,7 +154,7 @@ static constexpr uint8_t PSBT_SEPARATOR = 0x00; template<typename Stream, typename... X> void SerializeToVector(Stream& s, const X&... args) { - WriteCompactSize(s, GetSerializeSizeMany(s, args...)); + WriteCompactSize(s, GetSerializeSizeMany(s.GetVersion(), args...)); SerializeMany(s, args...); } @@ -155,7 +173,7 @@ void UnserializeFromVector(Stream& s, X&... args) // Deserialize HD keypaths into a map template<typename Stream> -void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths) +void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths) { // Make sure that the key is the size of pubkey + 1 if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) { @@ -172,25 +190,34 @@ void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std // Read in key path uint64_t value_len = ReadCompactSize(s); - std::vector<uint32_t> keypath; - for (unsigned int i = 0; i < value_len; i += sizeof(uint32_t)) { + if (value_len % 4 || value_len == 0) { + throw std::ios_base::failure("Invalid length for HD key path"); + } + + KeyOriginInfo keypath; + s >> keypath.fingerprint; + for (unsigned int i = 4; i < value_len; i += sizeof(uint32_t)) { uint32_t index; s >> index; - keypath.push_back(index); + keypath.path.push_back(index); } // Add to map - hd_keypaths.emplace(pubkey, keypath); + hd_keypaths.emplace(pubkey, std::move(keypath)); } // Serialize HD keypaths to a stream from a map template<typename Stream> -void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths, uint8_t type) +void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, uint8_t type) { for (auto keypath_pair : hd_keypaths) { + if (!keypath_pair.first.IsValid()) { + throw std::ios_base::failure("Invalid CPubKey being serialized"); + } SerializeToVector(s, type, MakeSpan(keypath_pair.first)); - WriteCompactSize(s, keypath_pair.second.size() * sizeof(uint32_t)); - for (auto& path : keypath_pair.second) { + WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t)); + s << keypath_pair.second.fingerprint; + for (const auto& path : keypath_pair.second.path) { s << path; } } @@ -205,7 +232,7 @@ struct PSBTInput CScript witness_script; CScript final_script_sig; CScriptWitness final_script_witness; - std::map<CPubKey, std::vector<uint32_t>> hd_keypaths; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; std::map<CKeyID, SigPair> partial_sigs; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; int sighash_type = 0; @@ -283,6 +310,7 @@ struct PSBTInput template <typename Stream> inline void Unserialize(Stream& s) { // Read loop + bool found_sep = false; while(!s.empty()) { // Read std::vector<unsigned char> key; @@ -290,7 +318,10 @@ struct PSBTInput // the key is empty if that was actually a separator byte // This is a special case for key lengths 0 as those are not allowed (except for separator) - if (key.empty()) return; + if (key.empty()) { + found_sep = true; + break; + } // First byte of key is the type unsigned char type = key[0]; @@ -405,6 +436,10 @@ struct PSBTInput break; } } + + if (!found_sep) { + throw std::ios_base::failure("Separator is missing at the end of an input map"); + } } template <typename Stream> @@ -418,7 +453,7 @@ struct PSBTOutput { CScript redeem_script; CScript witness_script; - std::map<CPubKey, std::vector<uint32_t>> hd_keypaths; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; bool IsNull() const; @@ -458,6 +493,7 @@ struct PSBTOutput template <typename Stream> inline void Unserialize(Stream& s) { // Read loop + bool found_sep = false; while(!s.empty()) { // Read std::vector<unsigned char> key; @@ -465,7 +501,10 @@ struct PSBTOutput // the key is empty if that was actually a separator byte // This is a special case for key lengths 0 as those are not allowed (except for separator) - if (key.empty()) return; + if (key.empty()) { + found_sep = true; + break; + } // First byte of key is the type unsigned char type = key[0]; @@ -510,6 +549,10 @@ struct PSBTOutput } } } + + if (!found_sep) { + throw std::ios_base::failure("Separator is missing at the end of an output map"); + } } template <typename Stream> @@ -531,6 +574,7 @@ struct PartiallySignedTransaction bool IsSane() const; PartiallySignedTransaction() {} PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {} + explicit PartiallySignedTransaction(const CTransaction& tx); // Only checks if they refer to the same transaction friend bool operator==(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b) @@ -585,6 +629,7 @@ struct PartiallySignedTransaction } // Read global data + bool found_sep = false; while(!s.empty()) { // Read std::vector<unsigned char> key; @@ -592,7 +637,10 @@ struct PartiallySignedTransaction // the key is empty if that was actually a separator byte // This is a special case for key lengths 0 as those are not allowed (except for separator) - if (key.empty()) break; + if (key.empty()) { + found_sep = true; + break; + } // First byte of key is the type unsigned char type = key[0]; @@ -632,6 +680,10 @@ struct PartiallySignedTransaction } } + if (!found_sep) { + throw std::ios_base::failure("Separator is missing at the end of the global map"); + } + // Make sure that we got an unsigned tx if (!tx) { throw std::ios_base::failure("No unsigned transcation was provided"); @@ -686,8 +738,11 @@ 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); +/** Checks whether a PSBTInput is already signed. */ +bool PSBTInputSigned(PSBTInput& input); + /** 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); +bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL); /** Extract signature data from a transaction input, and insert it. */ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); |