From 42c55f2e8a14a2f1c504cfe62e3d1717e8ac5454 Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Sat, 25 Apr 2015 18:29:13 -0700 Subject: [PATCH] Automatically reserialize non-canonical proof-of-stake block signatures. --- src/key.cpp | 26 ++++++++++++++++++++++++++ src/key.h | 3 +++ src/main.cpp | 27 ++++++++++++++++++++++++++- src/script.cpp | 32 ++++++++++++++++++++++---------- src/script.h | 2 +- 5 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/key.cpp b/src/key.cpp index c37ad83..540d44f 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -224,6 +224,32 @@ bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) { CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0; } +bool CKey::ReserealizeSignature(std::vector& vchSig) +{ + if (vchSig.empty()) + return false; + + unsigned char *pos = &vchSig[0]; + ECDSA_SIG *sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&pos, vchSig.size()); + if (sig == NULL) + return false; + + bool ret = false; + int nSize = i2d_ECDSA_SIG(sig, NULL); + if (nSize > 0) { + vchSig.resize(nSize); // grow or shrink as needed + + pos = &vchSig[0]; + i2d_ECDSA_SIG(sig, &pos); + + ret = true; + } + + ECDSA_SIG_free(sig); + + return ret; +} + void CKey::MakeNewKey(bool fCompressed) { if (!EC_KEY_generate_key(pkey)) diff --git a/src/key.h b/src/key.h index 96fea43..e329cf0 100644 --- a/src/key.h +++ b/src/key.h @@ -161,6 +161,9 @@ public: // Check whether an element of a signature (r or s) is valid. static bool CheckSignatureElement(const unsigned char *vch, int len, bool half); + + // Reserialize to DER + static bool ReserealizeSignature(std::vector& vchSig); }; #endif diff --git a/src/main.cpp b/src/main.cpp index e95376e..70bdce4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2501,6 +2501,25 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } +bool static ReserealizeBlockSignature(CBlock* pblock) +{ + if (pblock->IsProofOfWork()) + { + pblock->vchBlockSig.clear(); + return true; + } + + return CKey::ReserealizeSignature(pblock->vchBlockSig); +} + +bool static IsCanonicalBlockSignature(CBlock* pblock) +{ + if (pblock->IsProofOfWork()) + return pblock->vchBlockSig.empty(); + + return IsDERSignature(pblock->vchBlockSig); +} + bool ProcessBlock(CNode* pfrom, CBlock* pblock) { // Check for duplicate @@ -2510,12 +2529,18 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) if (mapOrphanBlocks.count(hash)) return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str()); - // ppcoin: check proof-of-stake + // Check proof-of-stake // Limited duplicity on stake: prevents block flood attack // Duplicate stake allowed only when there is orphan child block if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str()); + // Strip the garbage from newly received blocks, if we found some + if (!IsCanonicalBlockSignature(pblock)) { + if (!ReserealizeBlockSignature(pblock)) + printf("WARNING: ProcessBlock() : ReserealizeBlockSignature FAILED\n"); + } + // Preliminary checks if (!pblock->CheckBlock(true, true, (pblock->nTime > Checkpoints::GetLastCheckpointTime()))) return error("ProcessBlock() : CheckBlock FAILED"); diff --git a/src/script.cpp b/src/script.cpp index 61ca851..77f9ef4 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -270,10 +270,7 @@ bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { return true; } -bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { - if (!(flags & SCRIPT_VERIFY_STRICTENC)) - return true; - +bool IsDERSignature(const valtype &vchSig, bool fWithHashType, bool fCheckLow) { // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 // A canonical signature exists of: <30> <02> <02> // Where R and S are not negative (their first byte has its highest bit not set), and not @@ -283,18 +280,20 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { return error("Non-canonical signature: too short"); if (vchSig.size() > 73) return error("Non-canonical signature: too long"); - unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); - if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) - return error("Non-canonical signature: unknown hashtype byte"); if (vchSig[0] != 0x30) return error("Non-canonical signature: wrong type"); - if (vchSig[1] != vchSig.size()-3) + if (vchSig[1] != vchSig.size() - (fWithHashType ? 3 : 2)) return error("Non-canonical signature: wrong length marker"); + if (fWithHashType) { + unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); + if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) + return error("Non-canonical signature: unknown hashtype byte"); + } unsigned int nLenR = vchSig[3]; if (5 + nLenR >= vchSig.size()) return error("Non-canonical signature: S length misplaced"); unsigned int nLenS = vchSig[5+nLenR]; - if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) + if ((unsigned long)(nLenR + nLenS + (fWithHashType ? 7 : 6)) != vchSig.size()) return error("Non-canonical signature: R+S length mismatch"); const unsigned char *R = &vchSig[4]; @@ -317,7 +316,13 @@ 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_LOW_S) { + if (fCheckLow) { + unsigned int nLenR = vchSig[3]; + unsigned int nLenS = vchSig[5+nLenR]; + const unsigned char *S = &vchSig[6+nLenR]; + // If the S value is above the order of the curve divided by two, its + // complement modulo the order could have been used instead, which is + // one byte shorter when encoded correctly. if (!CKey::CheckSignatureElement(S, nLenS, true)) return error("Non-canonical signature: S value is unnecessarily high"); } @@ -325,6 +330,13 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { return true; } +bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + + return IsDERSignature(vchSig, true, flags & SCRIPT_VERIFY_LOW_S); +} + bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { CAutoBN_CTX pctx; diff --git a/src/script.h b/src/script.h index dfd2c19..f490631 100644 --- a/src/script.h +++ b/src/script.h @@ -602,9 +602,9 @@ public: }; bool IsCanonicalPubKey(const std::vector &vchPubKey, unsigned int flags); +bool IsDERSignature(const valtype &vchSig, bool fWithHashType=false, bool fCheckLow=false); bool IsCanonicalSignature(const std::vector &vchSig, unsigned int flags); - bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); -- 1.7.1