dash/src/test/key_tests.cpp
Wladimir J. van der Laan 90154c6074
Merge #13666: Always create signatures with Low R values
e306be742932d4ea5aca0ea4768e54b2fc3dc6a0 Use 72 byte dummy signatures when watching only inputs may be used (Andrew Chow)
48b1473c898129a99212e2db36c61cf93625ea17 Use 71 byte signature for DUMMY_SIGNATURE_CREATOR (Andrew Chow)
18dfea0dd082af18dfb02981b7ee1cd44d514388 Always create 70 byte signatures with low R values (Andrew Chow)

Pull request description:

  When creating signatures for transactions, always make one which has a 32 byte or smaller R and 32 byte or smaller S value. This results in signatures that are always less than 71 bytes (32 byte R + 32 byte S + 6 bytes DER + 1 byte sighash) with low R values. In most cases, the signature will be 71 bytes.

  Because R is not mutable in the same way that S is, a low R value can only be found by trying different nonces. RFC 6979 for deterministic nonce generation has the option to specify additional entropy, so we simply use that and add a uin32_t counter which we increment in order to try different nonces. Nonces are sill deterministically generated as the nonce used will the be the first one where the counter results in a nonce that results in a low R value. Because different nonces need to be tried, time to produce a signature does increase. On average, it takes twice as long to make a signature as two signatures need to be created, on average, to find one with a low R.

  Having a fixed size signature makes size calculations easier and also saves half a byte of transaction size, on average.

  DUMMY_SIGNATURE_CREATOR has been modified to produce 71 byte dummy signatures instead of 72 byte signatures.

Tree-SHA512: 3cd791505126ce92da7c631856a97ba0b59e87d9c132feff6e0eef1dc47768e81fbb38bfbe970371bedf9714b7f61a13a5fe9f30f962c81734092a4d19a4ef33
2021-07-06 20:29:32 +03:00

192 lines
7.9 KiB
C++

// Copyright (c) 2012-2015 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 <key.h>
#include <key_io.h>
#include <script/script.h>
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <test/test_dash.h>
#include <string>
#include <vector>
#include <boost/test/unit_test.hpp>
static const std::string strSecret1 = "7qh6LYnLN2w2ntz2wwUhRUEgkQ2j8XB16FGw77ZRDZmC29bn7cD";
static const std::string strSecret2 = "7rve4MxeWFQHGbSYH6J2yaaZd3MBUqoDEwN6ZAZ6ZHmhTT4r3hW";
static const std::string strSecret1C = "XBuxZHH6TqXUuaSjbVTFR1DQSYecxCB9QA1Koyx5tTc3ddhqEnhm";
static const std::string strSecret2C = "XHMkZqWcY6Zkoq1j42NBijD8z5N5FtNy2Wx7WyAfXX2HZgxry8cr";
static const std::string addr1 = "Xywgfc872nn5CKtpATCoAjZCc4v96pJczy";
static const std::string addr2 = "XpmouUj9KKJ99ZuU331ZS1KqsboeFnLGgK";
static const std::string addr1C = "XxV9h4Xmv6Pup8tVAQmH97K6grzvDwMG9F";
static const std::string addr2C = "Xn7ZrYdExuk79Dm7CJCw7sfUWi2qWJSbRy";
static const std::string strAddressBad = "Xta1praZQjyELweyMByXyiREw1ZRsjXzVP";
BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(key_test1)
{
CKey key1 = DecodeSecret(strSecret1);
BOOST_CHECK(key1.IsValid() && !key1.IsCompressed());
CKey key2 = DecodeSecret(strSecret2);
BOOST_CHECK(key2.IsValid() && !key2.IsCompressed());
CKey key1C = DecodeSecret(strSecret1C);
BOOST_CHECK(key1C.IsValid() && key1C.IsCompressed());
CKey key2C = DecodeSecret(strSecret2C);
BOOST_CHECK(key2C.IsValid() && key2C.IsCompressed());
CKey bad_key = DecodeSecret(strAddressBad);
BOOST_CHECK(!bad_key.IsValid());
CPubKey pubkey1 = key1. GetPubKey();
CPubKey pubkey2 = key2. GetPubKey();
CPubKey pubkey1C = key1C.GetPubKey();
CPubKey pubkey2C = key2C.GetPubKey();
BOOST_CHECK(key1.VerifyPubKey(pubkey1));
BOOST_CHECK(!key1.VerifyPubKey(pubkey1C));
BOOST_CHECK(!key1.VerifyPubKey(pubkey2));
BOOST_CHECK(!key1.VerifyPubKey(pubkey2C));
BOOST_CHECK(!key1C.VerifyPubKey(pubkey1));
BOOST_CHECK(key1C.VerifyPubKey(pubkey1C));
BOOST_CHECK(!key1C.VerifyPubKey(pubkey2));
BOOST_CHECK(!key1C.VerifyPubKey(pubkey2C));
BOOST_CHECK(!key2.VerifyPubKey(pubkey1));
BOOST_CHECK(!key2.VerifyPubKey(pubkey1C));
BOOST_CHECK(key2.VerifyPubKey(pubkey2));
BOOST_CHECK(!key2.VerifyPubKey(pubkey2C));
BOOST_CHECK(!key2C.VerifyPubKey(pubkey1));
BOOST_CHECK(!key2C.VerifyPubKey(pubkey1C));
BOOST_CHECK(!key2C.VerifyPubKey(pubkey2));
BOOST_CHECK(key2C.VerifyPubKey(pubkey2C));
BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID()));
BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID()));
BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID()));
BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID()));
for (int n=0; n<16; n++)
{
std::string strMsg = strprintf("Very secret message %i: 11", n);
uint256 hashMsg = Hash(strMsg.begin(), strMsg.end());
// normal signatures
std::vector<unsigned char> sign1, sign2, sign1C, sign2C;
BOOST_CHECK(key1.Sign (hashMsg, sign1));
BOOST_CHECK(key2.Sign (hashMsg, sign2));
BOOST_CHECK(key1C.Sign(hashMsg, sign1C));
BOOST_CHECK(key2C.Sign(hashMsg, sign2C));
BOOST_CHECK( pubkey1.Verify(hashMsg, sign1));
BOOST_CHECK(!pubkey1.Verify(hashMsg, sign2));
BOOST_CHECK( pubkey1.Verify(hashMsg, sign1C));
BOOST_CHECK(!pubkey1.Verify(hashMsg, sign2C));
BOOST_CHECK(!pubkey2.Verify(hashMsg, sign1));
BOOST_CHECK( pubkey2.Verify(hashMsg, sign2));
BOOST_CHECK(!pubkey2.Verify(hashMsg, sign1C));
BOOST_CHECK( pubkey2.Verify(hashMsg, sign2C));
BOOST_CHECK( pubkey1C.Verify(hashMsg, sign1));
BOOST_CHECK(!pubkey1C.Verify(hashMsg, sign2));
BOOST_CHECK( pubkey1C.Verify(hashMsg, sign1C));
BOOST_CHECK(!pubkey1C.Verify(hashMsg, sign2C));
BOOST_CHECK(!pubkey2C.Verify(hashMsg, sign1));
BOOST_CHECK( pubkey2C.Verify(hashMsg, sign2));
BOOST_CHECK(!pubkey2C.Verify(hashMsg, sign1C));
BOOST_CHECK( pubkey2C.Verify(hashMsg, sign2C));
// compact signatures (with key recovery)
std::vector<unsigned char> csign1, csign2, csign1C, csign2C;
BOOST_CHECK(key1.SignCompact (hashMsg, csign1));
BOOST_CHECK(key2.SignCompact (hashMsg, csign2));
BOOST_CHECK(key1C.SignCompact(hashMsg, csign1C));
BOOST_CHECK(key2C.SignCompact(hashMsg, csign2C));
CPubKey rkey1, rkey2, rkey1C, rkey2C;
BOOST_CHECK(rkey1.RecoverCompact (hashMsg, csign1));
BOOST_CHECK(rkey2.RecoverCompact (hashMsg, csign2));
BOOST_CHECK(rkey1C.RecoverCompact(hashMsg, csign1C));
BOOST_CHECK(rkey2C.RecoverCompact(hashMsg, csign2C));
BOOST_CHECK(rkey1 == pubkey1);
BOOST_CHECK(rkey2 == pubkey2);
BOOST_CHECK(rkey1C == pubkey1C);
BOOST_CHECK(rkey2C == pubkey2C);
}
// test deterministic signing
std::vector<unsigned char> detsig, detsigc;
std::string strMsg = "Very deterministic message";
uint256 hashMsg = Hash(strMsg.begin(), strMsg.end());
BOOST_CHECK(key1.Sign(hashMsg, detsig));
BOOST_CHECK(key1C.Sign(hashMsg, detsigc));
BOOST_CHECK(detsig == detsigc);
BOOST_CHECK(detsig == ParseHex("304402205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d022014ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"));
BOOST_CHECK(key2.Sign(hashMsg, detsig));
BOOST_CHECK(key2C.Sign(hashMsg, detsigc));
BOOST_CHECK(detsig == detsigc);
BOOST_CHECK(detsig == ParseHex("3044022052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd5022061d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"));
BOOST_CHECK(key1.SignCompact(hashMsg, detsig));
BOOST_CHECK(key1C.SignCompact(hashMsg, detsigc));
BOOST_CHECK(detsig == ParseHex("1c5dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d14ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"));
BOOST_CHECK(detsigc == ParseHex("205dbbddda71772d95ce91cd2d14b592cfbc1dd0aabd6a394b6c2d377bbe59d31d14ddda21494a4e221f0824f0b8b924c43fa43c0ad57dccdaa11f81a6bd4582f6"));
BOOST_CHECK(key2.SignCompact(hashMsg, detsig));
BOOST_CHECK(key2C.SignCompact(hashMsg, detsigc));
BOOST_CHECK(detsig == ParseHex("1c52d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd561d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"));
BOOST_CHECK(detsigc == ParseHex("2052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd561d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"));
}
BOOST_AUTO_TEST_CASE(key_signature_tests)
{
// When entropy is specified, we should see at least one high R signature within 20 signatures
CKey key = DecodeSecret(strSecret1);
std::string msg = "A message to be signed";
uint256 msg_hash = Hash(msg.begin(), msg.end());
std::vector<unsigned char> sig;
bool found = false;
for (int i = 1; i <=20; ++i) {
sig.clear();
key.Sign(msg_hash, sig, false, i);
found = sig[3] == 0x21 && sig[4] == 0x00;
if (found) {
break;
}
}
BOOST_CHECK(found);
// When entropy is not specified, we should always see low R signatures that are less than 70 bytes in 256 tries
// We should see at least one signature that is less than 70 bytes.
found = true;
bool found_small = false;
for (int i = 0; i < 256; ++i) {
sig.clear();
std::string msg = "A message to be signed" + std::to_string(i);
msg_hash = Hash(msg.begin(), msg.end());
key.Sign(msg_hash, sig);
found = sig[3] == 0x20;
BOOST_CHECK(sig.size() <= 70);
found_small |= sig.size() < 70;
}
BOOST_CHECK(found);
BOOST_CHECK(found_small);
}
BOOST_AUTO_TEST_SUITE_END()