Skip to content

Commit 72f754c

Browse files
committed
Merge pull request #3637
6fd7ef2 Also switch the (unused) verification code to low-s instead of even-s. (Pieter Wuille)
2 parents 54f1022 + 6fd7ef2 commit 72f754c

File tree

5 files changed

+78
-25
lines changed

5 files changed

+78
-25
lines changed

src/key.cpp

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -332,30 +332,60 @@ class CECKey {
332332
}
333333
};
334334

335+
int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) {
336+
while (c1len > c2len) {
337+
if (*c1)
338+
return 1;
339+
c1++;
340+
c1len--;
341+
}
342+
while (c2len > c1len) {
343+
if (*c2)
344+
return -1;
345+
c2++;
346+
c2len--;
347+
}
348+
while (c1len > 0) {
349+
if (*c1 > *c2)
350+
return 1;
351+
if (*c2 > *c1)
352+
return -1;
353+
c1++;
354+
c2++;
355+
c1len--;
356+
}
357+
return 0;
358+
}
359+
360+
// Order of secp256k1's generator minus 1.
361+
const unsigned char vchMaxModOrder[32] = {
362+
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
363+
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
364+
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
365+
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40
366+
};
367+
368+
// Half of the order of secp256k1's generator minus 1.
369+
const unsigned char vchMaxModHalfOrder[32] = {
370+
0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
371+
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
372+
0x5D,0x57,0x6E,0x73,0x57,0xA4,0x50,0x1D,
373+
0xDF,0xE9,0x2F,0x46,0x68,0x1B,0x20,0xA0
374+
};
375+
376+
const unsigned char vchZero[0] = {};
377+
378+
335379
}; // end of anonymous namespace
336380

337381
bool CKey::Check(const unsigned char *vch) {
338-
// Do not convert to OpenSSL's data structures for range-checking keys,
339-
// it's easy enough to do directly.
340-
static const unsigned char vchMax[32] = {
341-
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
342-
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
343-
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
344-
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40
345-
};
346-
bool fIsZero = true;
347-
for (int i=0; i<32 && fIsZero; i++)
348-
if (vch[i] != 0)
349-
fIsZero = false;
350-
if (fIsZero)
351-
return false;
352-
for (int i=0; i<32; i++) {
353-
if (vch[i] < vchMax[i])
354-
return true;
355-
if (vch[i] > vchMax[i])
356-
return false;
357-
}
358-
return true;
382+
return CompareBigEndian(vch, 32, vchZero, 0) > 0 &&
383+
CompareBigEndian(vch, 32, vchMaxModOrder, 32) <= 0;
384+
}
385+
386+
bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) {
387+
return CompareBigEndian(vch, len, vchZero, 0) > 0 &&
388+
CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0;
359389
}
360390

361391
void CKey::MakeNewKey(bool fCompressedIn) {

src/key.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ class CKey {
269269

270270
// Load private key and check that public key matches.
271271
bool Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck);
272+
273+
// Check whether an element of a signature (r or s) is valid.
274+
static bool CheckSignatureElement(const unsigned char *vch, int len, bool half);
272275
};
273276

274277
struct CExtPubKey {

src/script.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,12 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) {
286286
if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80))
287287
return error("Non-canonical signature: S value excessively padded");
288288

289-
if (flags & SCRIPT_VERIFY_EVEN_S) {
290-
if (S[nLenS-1] & 1)
291-
return error("Non-canonical signature: S value odd");
289+
if (flags & SCRIPT_VERIFY_LOW_S) {
290+
// If the S value is above the order of the curve divided by two, its
291+
// complement modulo the order could have been used instead, which is
292+
// one byte shorter when encoded correctly.
293+
if (!CKey::CheckSignatureElement(S, nLenS, true))
294+
return error("Non-canonical signature: S value is unnecessarily high");
292295
}
293296

294297
return true;

src/script.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ enum
188188
SCRIPT_VERIFY_NONE = 0,
189189
SCRIPT_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts
190190
SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys
191-
SCRIPT_VERIFY_EVEN_S = (1U << 2), // enforce even S values in signatures (depends on STRICTENC)
191+
SCRIPT_VERIFY_LOW_S = (1U << 2), // enforce low S values (<n/2) in signatures (depends on STRICTENC)
192192
SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it)
193193
SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
194194
};

src/test/canonical_tests.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,21 @@ BOOST_AUTO_TEST_CASE(script_noncanon)
9393
}
9494
}
9595

96+
BOOST_AUTO_TEST_CASE(script_signstrict)
97+
{
98+
for (int i=0; i<100; i++) {
99+
CKey key;
100+
key.MakeNewKey(i & 1);
101+
std::vector<unsigned char> sig;
102+
uint256 hash = GetRandHash();
103+
104+
BOOST_CHECK(key.Sign(hash, sig)); // Generate a random signature.
105+
BOOST_CHECK(key.GetPubKey().Verify(hash, sig)); // Check it.
106+
sig.push_back(0x01); // Append a sighash type.
107+
108+
BOOST_CHECK(IsCanonicalSignature(sig, SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_LOW_S));
109+
BOOST_CHECK(IsCanonicalSignature_OpenSSL(sig));
110+
}
111+
}
112+
96113
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)