2022-09-18 22:49:22 +02:00
|
|
|
// 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
|
2019-05-09 18:04:52 +02:00
|
|
|
#include <script/standard.h> // For CTxDestination, IsValidDestination(), PKHash
|
2022-09-18 22:49:22 +02:00
|
|
|
#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 = "DarkCoin 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;
|
|
|
|
}
|
|
|
|
|
2019-05-09 18:04:52 +02:00
|
|
|
if (std::get_if<PKHash>(&destination) == nullptr) {
|
2022-09-18 22:49:22 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-05-09 18:04:52 +02:00
|
|
|
if (!(CTxDestination(PKHash(pubkey)) == destination)) {
|
2022-09-18 22:49:22 +02:00
|
|
|
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);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint256 MessageHash(const std::string& message)
|
|
|
|
{
|
|
|
|
CHashWriter hasher(SER_GETHASH, 0);
|
|
|
|
hasher << MESSAGE_MAGIC << message;
|
|
|
|
|
|
|
|
return hasher.GetHash();
|
|
|
|
}
|
Merge #18115: wallet: Pass in transactions and messages for signing instead of exporting the private keys
d2774c09cfcc6c5c967d40bb094eabc8c0bdb6bf Clear any input_errors for an input after it is signed (Andrew Chow)
dc174881ad8498a6905ba282a48077bc5c8037a7 Replace GetSigningProvider with GetSolvingProvider (Andrew Chow)
6a9c429084b40356aa36aa67992da35f61c2f6a2 Move direct calls to MessageSign into new SignMessage functions in CWallet and ScriptPubKeyMan (Andrew Chow)
82a30fade70a2a95c2bbeac4aa06dafda600479d Move key and script filling and signing from CWallet::FillPSBT to ScriptPubKeyMan::FillPSBT (Andrew Chow)
3d70dd99f9f74eef70b19ff6f6f850adc0d5ef8f Move FillPSBT to be a member of CWallet (Andrew Chow)
a4af324d15c1ee43c2abd11a304ae18c7ee82eb0 Use CWallet::SignTransaction in CreateTransaction and signrawtransactionwithwallet (Andrew Chow)
f37de927442d3f024926a66c436d59e391c8696a Implement CWallet::SignTransaction using ScriptPubKeyMan::SignTransaction (Andrew Chow)
d999dd588cab0ff479bc7bee8c9fc33880265ec6 Add SignTransaction function to ScriptPubKeyMan and LegacyScriptPubKeyMan (Andrew Chow)
2c52b59d0a44a86d94fee4e437978d822862c542 Refactor rawtransaction's SignTransaction into generic SignTransaction function (Andrew Chow)
Pull request description:
Following #17261, the way to sign transactions, PSBTs, and messages was to use `GetSigningProvider()` and get a `SigningProvider` containing the private keys. However this may not be feasible for future `ScriptPubKeyMan`s, such as for hardware wallets. Instead of exporting a `SigningProvider` containing private keys, we need to pass these things into the `ScriptPubKeyMan` (via `CWallet`) so that they can do whatever is needed internally to sign them. This is largely a refactor as the logic of processing transactions, PSBTs, and messages for is moved into `LegacyScriptPubKeyMan` and `CWallet` instead of being handled by the caller (e.g. `signrawtransaction`).
To help with this, I've refactored the 3(!) implementations of a `SignTransaction()` function into one generic one. This function will be called by `signrawtransactionwithkey` and `LegacyScriptPubKeyMan::SignTransaction()`. `CWallet::CreateTransaction()` is changed to call `CWallet::SignTransaction()` which in turn, calls `LegacyScriptPubKeyMan::SignTransaction()`. Other `ScriptPubKeyMan`s may implement `SignTransaction()` differently.
`FillPSBT()` is moved to be a member function of `CWallet` and the `psbtwallet.cpp/h` files removed. It is further split so that `CWallet` handles filling the UTXOs while the `ScriptPubKeyMan` handles adding keys, derivation paths, scripts, and signatures. In the end `LegacyScriptPubKeyMan::FillPSBT` still calls `SignPSBTInput`, but the `SigningProvider` is internal to `LegacyScriptPubKeyMan`. Other `ScriptPubKeyMan`s may do something different.
A new `SignMessage()` function is added to both `CWallet` and `ScriptPubKeyMan`. Instead of having the caller (i.e. `signmessage` or the sign message dialog) get the private key, hash the message, and sign, `ScriptPubKeyMan` will now handle that (`CWallet` passes through to the `ScriptPubKeyMan`s as it does for many functions). This signing code is thus consolidated into `LegacyScriptPubKeyMan::SignMessage()`, though other `ScriptPubKeyMan`s may implement it differently. Additionally, a `SigningError` enum is introduced for the different errors that we expect to see from `SignMessage()`.
Lastly, `GetSigningProvider()` is renamed to `GetPublicSigningProvider()`. It will now only provide pubkeys, key origins, and scripts. `LegacySigningProvider` has it's `GetKey` and `HaveKey` functions changed to only return false. Future implementations should return `HidingSigningProvider`s where private keys are hidden.
Other things like `dumpprivkey` and `dumpwallet` are not changed because they directly need and access the `LegacyScriptPubKeyMan` so are not relevant to future changes.
ACKs for top commit:
instagibbs:
reACK https://github.com/bitcoin/bitcoin/pull/18115/commits/d2774c09cfcc6c5c967d40bb094eabc8c0bdb6bf
Sjors:
re-utACK d2774c09cfcc6c5c967d40bb094eabc8c0bdb6bf
meshcollider:
re-utACK d2774c09cfcc6c5c967d40bb094eabc8c0bdb6bf
Tree-SHA512: 89c83e7e7e9315e283fae145a2264648a9d7f7ace8f3281cb3f44f0b013c988d67ba4fa9726e50c643c0ed921bdd269adaec984840d11acf4a681f3e8a582cc1
2020-03-09 20:56:38 +01:00
|
|
|
|
|
|
|
std::string SigningResultString(const SigningResult res)
|
|
|
|
{
|
|
|
|
switch (res) {
|
|
|
|
case SigningResult::OK:
|
|
|
|
return "No error";
|
|
|
|
case SigningResult::PRIVATE_KEY_NOT_AVAILABLE:
|
|
|
|
return "Private key not available";
|
|
|
|
case SigningResult::SIGNING_FAILED:
|
|
|
|
return "Sign failed";
|
|
|
|
// no default case, so the compiler can warn about missing cases
|
|
|
|
}
|
|
|
|
assert(false);
|
|
|
|
}
|