Merge #17577: refactor: deduplicate the message sign/verify code

e193a84fb2 Refactor message hashing into a utility function (Jeffrey Czyz)
f8f0d9893d Deduplicate the message signing code (Vasil Dimov)
2ce3447eb1 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 e193a84fb2
  achow101:
    ACK e193a84fb2
  instagibbs:
    utACK e193a84fb2
  meshcollider:
    utACK e193a84fb2

Tree-SHA512: b0e02a7d4623a98c8f8c77627af1725e6df07700de4630c2f75da6beacdf55414c38ba147bc6d2a757491ab07c827dddf93e8632fe600478760e255714ddab88
This commit is contained in:
Samuel Dobson
2020-02-25 23:24:54 +13:00
9 changed files with 326 additions and 83 deletions

78
src/util/message.cpp Normal file
View File

@@ -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();
}

68
src/util/message.h Normal file
View File

@@ -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

View File

@@ -21,5 +21,3 @@ std::string FormatStateMessage(const ValidationState &state)
return state.GetRejectReason();
}
const std::string strMessageMagic = "Bitcoin Signed Message:\n";

View File

@@ -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