Skip to content

Commit

Permalink
Merge bitcoin#13666: Always create signatures with Low R values
Browse files Browse the repository at this point in the history
  • Loading branch information
kwvg committed Jul 9, 2021
1 parent be39cc9 commit a651d03
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 56 deletions.
24 changes: 22 additions & 2 deletions src/key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,35 @@ CPubKey CKey::GetPubKey() const {
return result;
}

bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, uint32_t test_case) const {
// Check that the sig has a low R value and will be less than 71 bytes
bool SigHasLowR(const secp256k1_ecdsa_signature* sig)
{
unsigned char compact_sig[64];
secp256k1_ecdsa_signature_serialize_compact(secp256k1_context_sign, compact_sig, sig);

// In DER serialization, all values are interpreted as big-endian, signed integers. The highest bit in the integer indicates
// its signed-ness; 0 is positive, 1 is negative. When the value is interpreted as a negative integer, it must be converted
// to a positive value by prepending a 0x00 byte so that the highest bit is 0. We can avoid this prepending by ensuring that
// our highest bit is always 0, and thus we must check that the first byte is less than 0x80.
return compact_sig[0] < 0x80;
}

bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool grind, uint32_t test_case) const {
if (!fValid)
return false;
vchSig.resize(CPubKey::SIGNATURE_SIZE);
size_t nSigLen = CPubKey::SIGNATURE_SIZE;
unsigned char extra_entropy[32] = {0};
WriteLE32(extra_entropy, test_case);
secp256k1_ecdsa_signature sig;
int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : nullptr);
uint32_t counter = 0;
int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, (!grind && test_case) ? extra_entropy : nullptr);

// Grind for low R
while (ret && !SigHasLowR(&sig) && grind) {
WriteLE32(extra_entropy, ++counter);
ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, extra_entropy);
}
assert(ret);
secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, vchSig.data(), &nSigLen, &sig);
vchSig.resize(nSigLen);
Expand Down
2 changes: 1 addition & 1 deletion src/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class CKey
* Create a DER-serialized signature.
* The test_case parameter tweaks the deterministic nonce.
*/
bool Sign(const uint256& hash, std::vector<unsigned char>& vchSig, uint32_t test_case = 0) const;
bool Sign(const uint256& hash, std::vector<unsigned char>& vchSig, bool grind = true, uint32_t test_case = 0) const;

/**
* Create a compact signature (65 bytes), which allows reconstructing the used public key.
Expand Down
22 changes: 13 additions & 9 deletions src/script/sign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,28 +362,32 @@ class DummySignatureChecker final : public BaseSignatureChecker
const DummySignatureChecker DUMMY_CHECKER;

class DummySignatureCreator final : public BaseSignatureCreator {
private:
char m_r_len = 32;
char m_s_len = 32;
public:
DummySignatureCreator() {}
DummySignatureCreator(char r_len, char s_len) : m_r_len(r_len), m_s_len(s_len) {}
const BaseSignatureChecker& Checker() const override { return DUMMY_CHECKER; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override
{
// Create a dummy signature that is a valid DER-encoding
vchSig.assign(72, '\000');
vchSig.assign(m_r_len + m_s_len + 7, '\000');
vchSig[0] = 0x30;
vchSig[1] = 69;
vchSig[1] = m_r_len + m_s_len + 4;
vchSig[2] = 0x02;
vchSig[3] = 33;
vchSig[3] = m_r_len;
vchSig[4] = 0x01;
vchSig[4 + 33] = 0x02;
vchSig[5 + 33] = 32;
vchSig[6 + 33] = 0x01;
vchSig[6 + 33 + 32] = SIGHASH_ALL;
vchSig[4 + m_r_len] = 0x02;
vchSig[5 + m_r_len] = m_s_len;
vchSig[6 + m_r_len] = 0x01;
vchSig[6 + m_r_len + m_s_len] = SIGHASH_ALL;
return true;
}
};
}

const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator();
const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(32, 32);
const BaseSignatureCreator& DUMMY_MAXIMUM_SIGNATURE_CREATOR = DummySignatureCreator(33, 32);
const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();

bool IsSolvable(const SigningProvider& provider, const CScript& script)
Expand Down
2 changes: 2 additions & 0 deletions src/script/sign.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {

/** A signature creator that just produces 72-byte empty signatures. */
extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR;
/** A signature creator that just produces 72-byte empty signatures. */
extern const BaseSignatureCreator& DUMMY_MAXIMUM_SIGNATURE_CREATOR;

typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair;

Expand Down
36 changes: 36 additions & 0 deletions src/test/key_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,40 @@ BOOST_AUTO_TEST_CASE(key_test1)
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()
2 changes: 1 addition & 1 deletion src/test/script_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ class TestBuilder
std::vector<unsigned char> vchSig, r, s;
uint32_t iter = 0;
do {
key.Sign(hash, vchSig, iter++);
key.Sign(hash, vchSig, false, iter++);
if ((lenS == 33) != (vchSig[5 + vchSig[3]] == 33)) {
NegateSignatureS(vchSig);
}
Expand Down
31 changes: 15 additions & 16 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2010,30 +2010,29 @@ int64_t CWalletTx::GetTxTime() const
return n ? n : nTimeReceived;
}

// Helper for producing a max-sized low-S signature (eg 72 bytes)
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout) const
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) const
{
// Fill in dummy signatures for fee calculation.
const CScript& scriptPubKey = txout.scriptPubKey;
SignatureData sigdata;

if (!ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata))
{
if (!ProduceSignature(*this, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
return false;
} else {
UpdateInput(tx_in, sigdata);
}
UpdateInput(tx_in, sigdata);
return true;
}

// Helper for producing a bunch of max-sized low-S signatures (eg 72 bytes)
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts) const
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig) const
{
// Fill in dummy signatures for fee calculation.
int nIn = 0;
for (const auto& txout : txouts)
{
if (!DummySignInput(txNew.vin[nIn], txout)) {
if (!DummySignInput(txNew.vin[nIn], txout, use_max_sig)) {
return false;
}

Expand All @@ -2042,7 +2041,7 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
return true;
}

int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet)
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
// Look up the inputs. We should have already checked that this transaction
Expand All @@ -2056,26 +2055,26 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall
assert(input.prevout.n < mi->second.tx->vout.size());
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
}
return CalculateMaximumSignedTxSize(tx, wallet, txouts);
return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
}

// txouts needs to be in the order of tx.vin
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts)
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
{
CMutableTransaction txNew(tx);
if (!wallet->DummySignTx(txNew, txouts)) {
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
// implies that we can sign for every input.
return -1;
}
return ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
}

int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet)
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
{
CMutableTransaction txn;
txn.vin.push_back(CTxIn(COutPoint()));
if (!wallet->DummySignInput(txn.vin[0], txout)) {
if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
// implies that we can sign for every input.
return -1;
Expand Down Expand Up @@ -2980,7 +2979,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
bool solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey);
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));

vCoins.push_back(COutput(pcoin, i, nDepth, spendable, solvable, safeTx));
vCoins.push_back(COutput(pcoin, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));

// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
Expand Down
29 changes: 16 additions & 13 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ class CMerkleTx
};

//Get the marginal bytes of spending the specified output
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet);
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);

/**
* A transaction with a bunch of additional info that only the owner cares about.
Expand Down Expand Up @@ -491,9 +491,9 @@ class CWalletTx : public CMerkleTx
CAmount GetDenominatedCredit(bool unconfirmed, bool fUseCache=true) const;

// Get the marginal bytes if spending the specified output from this transaction
int GetSpendSize(unsigned int out) const
int GetSpendSize(unsigned int out, bool use_max_sig = false) const
{
return CalculateMaximumSignedInputSize(tx->vout[out], pwallet);
return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig);
}

void GetAmounts(std::list<COutputEntry>& listReceived,
Expand Down Expand Up @@ -556,20 +556,23 @@ class COutput
/** Whether we know how to spend this output, ignoring the lack of keys */
bool fSolvable;

/** Whether to use the maximum sized, 72 byte signature when calculating the size of the input spend. This should only be set when watch-only outputs are allowed */
bool use_max_sig;

/**
* Whether this output is considered safe to spend. Unconfirmed transactions
* from outside keys and unconfirmed replacement transactions are considered
* unsafe and will not be used to fund new spending transactions.
*/
bool fSafe;

COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn)
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false)
{
tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1;
tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in;
// If known and signable by the given wallet, compute nInputBytes
// Failure will keep this value -1
if (fSpendable && tx) {
nInputBytes = tx->GetSpendSize(i);
nInputBytes = tx->GetSpendSize(i, use_max_sig);
}
}

Expand Down Expand Up @@ -1084,14 +1087,14 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries);
bool AddAccountingEntry(const CAccountingEntry&);
bool AddAccountingEntry(const CAccountingEntry&, WalletBatch *batch);
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts) const
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
{
std::vector<CTxOut> v_txouts(txouts.size());
std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
return DummySignTx(txNew, v_txouts);
return DummySignTx(txNew, v_txouts, use_max_sig);
}
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts) const;
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout) const;
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const;

CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
Expand Down Expand Up @@ -1416,9 +1419,9 @@ class WalletRescanReserver
};

// Calculate the size of the transaction assuming all signatures are max size
// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
// Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
// be IsAllFromMe).
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet);
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts);
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false);
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
#endif // BITCOIN_WALLET_WALLET_H

0 comments on commit a651d03

Please sign in to comment.