MessageSign()
and
MessageVerify()
and add unit tests for them.
MessageSign()
and
MessageVerify()
and add unit tests for them.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
15+// We moved a call to boost::get() here from another file. It is a good practice
16+// to include the header that provides it (<boost/variant/get.hpp>). However
17+// test/lint/lint-includes.sh:90 complains that a new Boost dependency is
18+// introduced because even though boost::get() is used in many other places in
19+// the code, nobody has bothered to include <boost/variant/get.hpp>.
20+//#include <boost/variant/get.hpp> // For boost::get()
For some reason it is possible to use boost::get()
without including the header that provides it. Probably included from somewhere else, maybe from QT stuff.
Just drop lines 15-20 or somehow silence the lint script to allow this header to be included (a new boost dependency is not added by this patch)?
Affairs with conflicting PRs:
#17557 util: Refactor message hashing into a utility function Both PRs target the same duplicated code! However this PR (#17577) has broader scope as it deduplicates more code.
#17493 util: Forbid ambiguous repeated assignments in config file Purely mechanical conflicts. Both PRs touch neighboring code. Either one can be merged first, the second will need to be adjusted (easy).
#17473 refactor: Settings code cleanups Purely mechanical conflicts. Same as above.
#17399 validation: Templatize ValidationState instead of subclassing Purely mechanical conflicts. Same as above.
#16545 Implement missing error checking for ArgsManager flags Purely mechanical conflicts. Same as above.
@jkczyz What do you think about this PR and its relation to #17557? Merge both in one PR?
I have #17557 based on an outstanding PR (#17399), though I suppose it didn’t have to be. The former resulted from a comment in the latter. I deliberately kept the change small so it could hopefully be merged quicker. Though I wanted to add a test to clarify why the “magic” string is needed.
That said, from a design perspective, I prefer your PR as it hides the complexity around signing a message.
Conflicts
Reviewers, this pull request conflicts with the following ones:
* [#17593](https://drahtbot.github.io/bitcoin_core_issue_redirect/r/17593.html) (test: move more utility functions into test utility library by mzumsande) * [#17581](https://drahtbot.github.io/bitcoin_core_issue_redirect/r/17581.html) (refactor: Remove settings merge reverse precedence code by ryanofsky) * [#17580](https://drahtbot.github.io/bitcoin_core_issue_redirect/r/17580.html) (refactor: Add ALLOW_LIST flags and enforce usage in CheckArgFlags by ryanofsky) * [#17493](https://drahtbot.github.io/bitcoin_core_issue_redirect/r/17493.html) (util: Forbid ambiguous multiple assignments in config file by ryanofsky) * [#17473](https://drahtbot.github.io/bitcoin_core_issue_redirect/r/17473.html) (refactor: Settings code cleanups by ryanofsky) * [#17399](https://drahtbot.github.io/bitcoin_core_issue_redirect/r/17399.html) (validation: Templatize ValidationState instead of subclassing by jkczyz) * [#16545](https://drahtbot.github.io/bitcoin_core_issue_redirect/r/16545.html) (refactor: Implement missing error checking for ArgsManager flags by ryanofsky)
All of the above conflicts are mechanical, small and easy to resolve.
@instagibbs Thanks for the review! Where would you put the “Bitcoin Signed Message Protocol” label?
We do message signing and verification now. What about encryption and decryption? Would that be useful feature or just a bloat that is rarely used? If encryption may be added in the future then maybe just “Bitcoin Message Protocol” would be a better name, or “Bitcoin Text Message Protocol”.
This PR (https://github.com/bitcoin/bitcoin/pull/17577) deduplicates message signing and message verification code.
I peeked at https://github.com/bitcoin/bitcoin/pull/18115/ and among other things it deduplicates the message signing code from 2 of 3 places and does not deduplicate the message verification code. Assuming it also fixes the 3rd place where message signing code is repeated, then it will achieve half of this PR.
Should https://github.com/bitcoin/bitcoin/pull/18115/ hit master
before this PR, then I will trim this PR to only consolidate the message verification code.
228+ const std::string& signature = ui->signatureIn_VM->text().toStdString();
229+ const std::string& message = ui->messageIn_VM->document()->toPlainText().toStdString();
230+
231+ switch (MessageVerify(address, signature, message)) {
232+ case MessageVerificationResult::ERR_INVALID_ADDRESS:
233+ ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
s could be moved out of the switch
to further reduce duplication.
I peeked at #18115 and among other things it deduplicates the message signing code from 2 of 3 places and does not deduplicate the message verification code. Assuming it also fixes the 3rd place…
That was fixed and now #18115 deduplicates all 3 message signing repetitions.
MessageSign() from #17577 is equivalent to SignMessage() from #18115. The former has unit tests added in #17577.
19+static const std::string MESSAGE_MAGIC = "Bitcoin Signed Message:\n";
20+
21+bool MessageSign(
22+ const CKey& privkey,
23+ const std::string& message,
24+ std::string* signature)
std::string&
) instead of pointer.
2100+{
2101+ const std::string unsigned_tx = "...";
2102+ const uint256 signature_hash = Hash(unsigned_tx.begin(), unsigned_tx.end());
2103+
2104+ const uint256 message_hash = MessageHash(unsigned_tx);
2105+ BOOST_CHECK_NE(message_hash, signature_hash);
Bitcoin Signed Message:\n...
and compare that with message_hash
.
Notice: assuming CHashWriter hasher(SER_GETHASH, 0);
, then:
0hasher << "ab" << "cd";
would produce a different result than:
0hasher << "abcd";
14@@ -14,7 +15,27 @@
15 #include <string>
16 #include <vector>
17
18-const std::string strMessageMagic = "Bitcoin Signed Message:\n";
19+static const std::string MESSAGE_MAGIC = "Bitcoin Signed Message:\n";
I’m seeing a new compiler warning:
0In file included from ./script/signingprovider.h:13,
1 from ./outputtype.h:10,
2 from rpc/misc.cpp:8:
3rpc/misc.cpp: In function ‘UniValue verifymessage(const JSONRPCRequest&)’:
4./sync.h:179:104: warning: control reaches end of non-void function [-Wreturn-type]
5 179 | #define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
6 | ^
7rpc/misc.cpp:273:5: note: in expansion of macro ‘LOCK’
8 273 | LOCK(cs_main);
9 | ^~~~
289+ case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
290+ case MessageVerificationResult::ERR_NOT_SIGNED:
291+ return false;
292+ case MessageVerificationResult::OK:
293+ return true;
294 }
return false
statement after this switch
.
-Wreturn-type
warning on macOS. Only one of the Travis machines noticed it: https://travis-ci.org/bitcoin/bitcoin/jobs/649943882?utm_medium=notification&utm_source=github_status
return false;
, thanks!
The logic of verifying a message was duplicated in 2 places:
src/qt/signverifymessagedialog.cpp
SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
src/rpc/misc.cpp
verifymessage()
with the only difference being the result handling. Move the logic into
a dedicated
src/util/message.cpp
MessageVerify()
which returns a set of result codes, call it from the 2 places and just
handle the results differently in the callers.
The logic of signing a message was duplicated in 3 places:
src/qt/signverifymessagedialog.cpp
SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
src/rpc/misc.cpp
signmessagewithprivkey()
src/wallet/rpcwallet.cpp
signmessage()
Move the logic into
src/util/message.cpp
MessageSign()
and call it from all the 3 places.
And add unit test for it.
The purpose of using a preamble or "magic" text as part of signing and
verifying a message was not given when the code was repeated in a few
locations. Make a test showing how it is used to prevent inadvertently
signing a transaction.
39+/** Verify a signed message.
40+ * @param[in] address Signer's bitcoin address, it must refer to a public key.
41+ * @param[in] signature The signature in base64 format.
42+ * @param[in] message The message that was signed.
43+ * @return result code */
44+MessageVerificationResult MessageVerify(
utACK e193a84fb28068e38d5f54fbfd6208428c5bb655
Nice cleanup, thanks!