Skip to content

Commit 30b6273

Browse files
Use low R compact signatures
1 parent 2a96154 commit 30b6273

File tree

4 files changed

+30
-9
lines changed

4 files changed

+30
-9
lines changed

src/key.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,19 @@ bool SigHasLowR(const secp256k1_ecdsa_signature* sig)
205205
return compact_sig[0] < 0x80;
206206
}
207207

208+
bool SigHasLowR(const secp256k1_ecdsa_recoverable_signature* sig)
209+
{
210+
int rec = -1;
211+
unsigned char compact_sig[64];
212+
secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, compact_sig, &rec, sig);
213+
214+
// In DER serialization, all values are interpreted as big-endian, signed integers. The highest bit in the integer indicates
215+
// its signed-ness; 0 is positive, 1 is negative. When the value is interpreted as a negative integer, it must be converted
216+
// to a positive value by prepending a 0x00 byte so that the highest bit is 0. We can avoid this prepending by ensuring that
217+
// our highest bit is always 0, and thus we must check that the first byte is less than 0x80.
218+
return compact_sig[0] < 0x80;
219+
}
220+
208221
bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool grind, uint32_t test_case) const {
209222
if (!keydata)
210223
return false;
@@ -246,18 +259,26 @@ bool CKey::VerifyPubKey(const CPubKey& pubkey) const {
246259
return pubkey.Verify(hash, vchSig);
247260
}
248261

249-
bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const {
250-
if (!keydata)
262+
bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig, unsigned char header, bool grind) const {
263+
if (!keydata || header > 0xF8)
251264
return false;
252265
vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE);
253266
int rec = -1;
267+
unsigned char extra_entropy[32] = {0};
254268
secp256k1_ecdsa_recoverable_signature rsig;
269+
uint32_t counter = 0;
255270
int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &rsig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, nullptr);
271+
272+
// Grind for low R
273+
while (ret && !SigHasLowR(&rsig) && grind) {
274+
WriteLE32(extra_entropy, ++counter);
275+
ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &rsig, hash.begin(), UCharCast(begin()), secp256k1_nonce_function_rfc6979, extra_entropy);
276+
}
256277
assert(ret);
257278
ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, &vchSig[1], &rec, &rsig);
258279
assert(ret);
259280
assert(rec != -1);
260-
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
281+
vchSig[0] = header + rec + (fCompressed ? 4 : 0);
261282
// Additional verification step to prevent using a potentially corrupted signature
262283
secp256k1_pubkey epk, rpk;
263284
ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &epk, UCharCast(begin()));

src/key.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class CKey
153153
* 0x1D = second key with even y, 0x1E = second key with odd y,
154154
* add 0x04 for compressed keys.
155155
*/
156-
bool SignCompact(const uint256& hash, std::vector<unsigned char>& vchSig) const;
156+
bool SignCompact(const uint256& hash, std::vector<unsigned char>& vchSig, unsigned char header = 27, bool grind = true) const;
157157

158158
/**
159159
* Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this,

src/pubkey.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,11 @@ bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchS
291291
return secp256k1_ecdsa_verify(secp256k1_context_static, &sig, hash.begin(), &pubkey);
292292
}
293293

294-
bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
295-
if (vchSig.size() != COMPACT_SIGNATURE_SIZE)
294+
bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig, unsigned char header) {
295+
if (vchSig.size() != COMPACT_SIGNATURE_SIZE || header > vchSig[0])
296296
return false;
297-
int recid = (vchSig[0] - 27) & 3;
298-
bool fComp = ((vchSig[0] - 27) & 4) != 0;
297+
int recid = (vchSig[0] - header) & 3;
298+
bool fComp = ((vchSig[0] - header) & 4) != 0;
299299
secp256k1_pubkey pubkey;
300300
secp256k1_ecdsa_recoverable_signature sig;
301301
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_context_static, &sig, &vchSig[1], recid)) {

src/pubkey.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ class CPubKey
253253
static bool CheckLowS(const std::vector<unsigned char>& vchSig);
254254

255255
//! Recover a public key from a compact signature.
256-
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig);
256+
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig, unsigned char header = 27);
257257

258258
//! Turn this public key into an uncompressed public key.
259259
bool Decompress();

0 commit comments

Comments
 (0)