diff options
author | Samuel Dobson <dobsonsa68@gmail.com> | 2020-02-25 23:24:54 +1300 |
---|---|---|
committer | Samuel Dobson <dobsonsa68@gmail.com> | 2020-02-25 23:29:54 +1300 |
commit | 03f98b15ad4f910d25b0fa9024c1880af70d44f5 (patch) | |
tree | 05b9a972168c4ae1ee1fb8271b802e88deca3d43 /src/util | |
parent | a674e89d2771a076d9e9dd182a05b60662ef9cf4 (diff) | |
parent | e193a84fb28068e38d5f54fbfd6208428c5bb655 (diff) |
Merge #17577: refactor: deduplicate the message sign/verify code
e193a84fb28068e38d5f54fbfd6208428c5bb655 Refactor message hashing into a utility function (Jeffrey Czyz)
f8f0d9893d7969bdaa870fadb94ec5d0dfa8334d Deduplicate the message signing code (Vasil Dimov)
2ce3447eb1e25ec7aec4b300dabf6c1e394f1906 Deduplicate the message verifying code (Vasil Dimov)
Pull request description:
The message signing and verifying logic was replicated in a few places
in the code. Consolidate in a newly introduced `MessageSign()` and
`MessageVerify()` and add unit tests for them.
ACKs for top commit:
Sjors:
re-ACK e193a84fb28068e38d5f54fbfd6208428c5bb655
achow101:
ACK e193a84fb28068e38d5f54fbfd6208428c5bb655
instagibbs:
utACK https://github.com/bitcoin/bitcoin/pull/17577/commits/e193a84fb28068e38d5f54fbfd6208428c5bb655
meshcollider:
utACK e193a84fb28068e38d5f54fbfd6208428c5bb655
Tree-SHA512: b0e02a7d4623a98c8f8c77627af1725e6df07700de4630c2f75da6beacdf55414c38ba147bc6d2a757491ab07c827dddf93e8632fe600478760e255714ddab88
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/message.cpp | 78 | ||||
-rw-r--r-- | src/util/message.h | 68 | ||||
-rw-r--r-- | src/util/validation.cpp | 2 | ||||
-rw-r--r-- | src/util/validation.h | 2 |
4 files changed, 146 insertions, 4 deletions
diff --git a/src/util/message.cpp b/src/util/message.cpp new file mode 100644 index 0000000000..17603a43d2 --- /dev/null +++ b/src/util/message.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <hash.h> // For CHashWriter +#include <key.h> // For CKey +#include <key_io.h> // For DecodeDestination() +#include <pubkey.h> // For CPubKey +#include <script/standard.h> // For CTxDestination, IsValidDestination(), PKHash +#include <serialize.h> // For SER_GETHASH +#include <util/message.h> +#include <util/strencodings.h> // For DecodeBase64() + +#include <string> +#include <vector> + +/** + * Text used to signify that a signed message follows and to prevent + * inadvertently signing a transaction. + */ +const std::string MESSAGE_MAGIC = "Bitcoin Signed Message:\n"; + +MessageVerificationResult MessageVerify( + const std::string& address, + const std::string& signature, + const std::string& message) +{ + CTxDestination destination = DecodeDestination(address); + if (!IsValidDestination(destination)) { + return MessageVerificationResult::ERR_INVALID_ADDRESS; + } + + if (boost::get<PKHash>(&destination) == nullptr) { + return MessageVerificationResult::ERR_ADDRESS_NO_KEY; + } + + bool invalid = false; + std::vector<unsigned char> signature_bytes = DecodeBase64(signature.c_str(), &invalid); + if (invalid) { + return MessageVerificationResult::ERR_MALFORMED_SIGNATURE; + } + + CPubKey pubkey; + if (!pubkey.RecoverCompact(MessageHash(message), signature_bytes)) { + return MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED; + } + + if (!(CTxDestination(PKHash(pubkey)) == destination)) { + return MessageVerificationResult::ERR_NOT_SIGNED; + } + + return MessageVerificationResult::OK; +} + +bool MessageSign( + const CKey& privkey, + const std::string& message, + std::string& signature) +{ + std::vector<unsigned char> signature_bytes; + + if (!privkey.SignCompact(MessageHash(message), signature_bytes)) { + return false; + } + + signature = EncodeBase64(signature_bytes.data(), signature_bytes.size()); + + return true; +} + +uint256 MessageHash(const std::string& message) +{ + CHashWriter hasher(SER_GETHASH, 0); + hasher << MESSAGE_MAGIC << message; + + return hasher.GetHash(); +} diff --git a/src/util/message.h b/src/util/message.h new file mode 100644 index 0000000000..01fd14ce2d --- /dev/null +++ b/src/util/message.h @@ -0,0 +1,68 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_MESSAGE_H +#define BITCOIN_UTIL_MESSAGE_H + +#include <key.h> // For CKey +#include <uint256.h> + +#include <string> + +extern const std::string MESSAGE_MAGIC; + +/** The result of a signed message verification. + * Message verification takes as an input: + * - address (with whose private key the message is supposed to have been signed) + * - signature + * - message + */ +enum class MessageVerificationResult { + //! The provided address is invalid. + ERR_INVALID_ADDRESS, + + //! The provided address is valid but does not refer to a public key. + ERR_ADDRESS_NO_KEY, + + //! The provided signature couldn't be parsed (maybe invalid base64). + ERR_MALFORMED_SIGNATURE, + + //! A public key could not be recovered from the provided signature and message. + ERR_PUBKEY_NOT_RECOVERED, + + //! The message was not signed with the private key of the provided address. + ERR_NOT_SIGNED, + + //! The message verification was successful. + OK +}; + +/** Verify a signed message. + * @param[in] address Signer's bitcoin address, it must refer to a public key. + * @param[in] signature The signature in base64 format. + * @param[in] message The message that was signed. + * @return result code */ +MessageVerificationResult MessageVerify( + const std::string& address, + const std::string& signature, + const std::string& message); + +/** Sign a message. + * @param[in] privkey Private key to sign with. + * @param[in] message The message to sign. + * @param[out] signature Signature, base64 encoded, only set if true is returned. + * @return true if signing was successful. */ +bool MessageSign( + const CKey& privkey, + const std::string& message, + std::string& signature); + +/** + * Hashes a message for signing and verification in a manner that prevents + * inadvertently signing a transaction. + */ +uint256 MessageHash(const std::string& message); + +#endif // BITCOIN_UTIL_MESSAGE_H diff --git a/src/util/validation.cpp b/src/util/validation.cpp index 89bc6665a4..ffbee21aeb 100644 --- a/src/util/validation.cpp +++ b/src/util/validation.cpp @@ -21,5 +21,3 @@ std::string FormatStateMessage(const ValidationState &state) return state.GetRejectReason(); } - -const std::string strMessageMagic = "Bitcoin Signed Message:\n"; diff --git a/src/util/validation.h b/src/util/validation.h index da2cf9f102..5ee260a055 100644 --- a/src/util/validation.h +++ b/src/util/validation.h @@ -13,6 +13,4 @@ class ValidationState; /** Convert ValidationState to a human-readable message for logging */ std::string FormatStateMessage(const ValidationState &state); -extern const std::string strMessageMagic; - #endif // BITCOIN_UTIL_VALIDATION_H |