From: CryptoManiac Date: Fri, 18 Jul 2014 18:20:07 +0000 (+0400) Subject: Use low S to break signatures malleability issue X-Git-Tag: v0.4.4.6-nvc-update5^2~12 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=07bc41cd07f81fffadcf5b0fb8838e84a159389e Use low S to break signatures malleability issue --- diff --git a/src/key.cpp b/src/key.cpp index 2065de7..4a7d7fd 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -120,6 +120,51 @@ err: return ret; } +int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) { + while (c1len > c2len) { + if (*c1) + return 1; + c1++; + c1len--; + } + while (c2len > c1len) { + if (*c2) + return -1; + c2++; + c2len--; + } + while (c1len > 0) { + if (*c1 > *c2) + return 1; + if (*c2 > *c1) + return -1; + c1++; + c2++; + c1len--; + } + return 0; +} + +// Order of secp256k1's generator minus 1. +const unsigned char vchMaxModOrder[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40 +}; + +// Half of the order of secp256k1's generator minus 1. +const unsigned char vchMaxModHalfOrder[32] = { + 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x5D,0x57,0x6E,0x73,0x57,0xA4,0x50,0x1D, + 0xDF,0xE9,0x2F,0x46,0x68,0x1B,0x20,0xA0 +}; + +const unsigned char vchZero[0] = {}; + + + void CKey::SetCompressedPubKey() { EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED); @@ -174,6 +219,11 @@ bool CKey::IsCompressed() const return fCompressedPubKey; } +bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) { + return CompareBigEndian(vch, len, vchZero, 0) > 0 && + CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0; +} + void CKey::MakeNewKey(bool fCompressed) { if (!EC_KEY_generate_key(pkey)) diff --git a/src/key.h b/src/key.h index 51130f7..96fea43 100644 --- a/src/key.h +++ b/src/key.h @@ -158,6 +158,9 @@ public: bool VerifyCompact(uint256 hash, const std::vector& vchSig); bool IsValid(); + + // Check whether an element of a signature (r or s) is valid. + static bool CheckSignatureElement(const unsigned char *vch, int len, bool half); }; #endif diff --git a/src/main.cpp b/src/main.cpp index 73bcccf..a63c008 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1548,23 +1548,11 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fScriptChecks=true, unsigned int flags=STANDARD_SCRIPT_VERIFY_FLAGS, std::vector *pvChecks = NULL); + bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs, std::map& mapTestPool, const CDiskTxPos& posThisTx, const CBlockIndex* pindexBlock, + bool fBlock, bool fMiner, bool fScriptChecks=true, + unsigned int flags=STANDARD_SCRIPT_VERIFY_FLAGS, std::vector *pvChecks = NULL); bool ClientConnectInputs(); bool CheckTransaction() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); diff --git a/src/script.cpp b/src/script.cpp index 4d3586a..bcd3e94 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -317,9 +317,9 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) return error("Non-canonical signature: S value excessively padded"); - if (flags & SCRIPT_VERIFY_EVEN_S) { - if (S[nLenS-1] & 1) - return error("Non-canonical signature: S value odd"); + if (flags & SCRIPT_VERIFY_LOW_S) { + if (!CKey::CheckSignatureElement(S, nLenS, true)) + return error("Non-canonical signature: S value is unnecessarily high"); } return true; diff --git a/src/script.h b/src/script.h index dc98f9a..ed03059 100644 --- a/src/script.h +++ b/src/script.h @@ -35,7 +35,7 @@ enum SCRIPT_VERIFY_NONE = 0, SCRIPT_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys - SCRIPT_VERIFY_EVEN_S = (1U << 2), // enforce even S values in signatures (depends on STRICTENC) + SCRIPT_VERIFY_LOW_S = (1U << 2), // enforce low S values in signatures (depends on STRICTENC) SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it) SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length };