diff options
author | Jeff Garzik <jgarzik@exmulti.com> | 2012-10-20 10:56:04 -0700 |
---|---|---|
committer | Jeff Garzik <jgarzik@exmulti.com> | 2012-10-20 10:56:04 -0700 |
commit | dee0ee2ac9d6feb49eac9683c01a0eeed4389449 (patch) | |
tree | 9eaf50e73a51a3b40da9cb557090a794138e9f35 /src/script.cpp | |
parent | 485d667748b776a1932e3e14dc1b9dfe2ba841d0 (diff) | |
parent | 58bc86e37fda1aec270bccb3df6c20fbd2a6591c (diff) |
Merge pull request #1742 from sipa/canonical
Check for canonical public keys and signatures
Diffstat (limited to 'src/script.cpp')
-rw-r--r-- | src/script.cpp | 92 |
1 files changed, 79 insertions, 13 deletions
diff --git a/src/script.cpp b/src/script.cpp index 4357a9a1b3..89bc5632bc 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -253,7 +253,68 @@ const char* GetOpName(opcodetype opcode) } } -bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) +bool IsCanonicalPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() < 33) + return error("Non-canonical public key: too short"); + if (vchPubKey[0] == 0x04) { + if (vchPubKey.size() != 65) + return error("Non-canonical public key: invalid length for uncompressed key"); + } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { + if (vchPubKey.size() != 33) + return error("Non-canonical public key: invalid length for compressed key"); + } else { + return error("Non-canonical public key: compressed nor uncompressed"); + } + return true; +} + +bool IsCanonicalSignature(const valtype &vchSig) { + // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + // A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype> + // Where R and S are not negative (their first byte has its highest bit not set), and not + // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, + // in which case a single 0 byte is necessary and even required). + if (vchSig.size() < 9) + return error("Non-canonical signature: too short"); + if (vchSig.size() > 73) + return error("Non-canonical signature: too long"); + if (vchSig[vchSig.size() - 1] & 0x7C) + 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) + return error("Non-canonical signature: wrong length marker"); + 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()) + return error("Non-canonical signature: R+S length mismatch"); + + const unsigned char *R = &vchSig[4]; + if (R[-2] != 0x02) + return error("Non-canonical signature: R value type mismatch"); + if (nLenR == 0) + return error("Non-canonical signature: R length is zero"); + if (R[0] & 0x80) + return error("Non-canonical signature: R value negative"); + if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) + return error("Non-canonical signature: R value excessively padded"); + + const unsigned char *S = &vchSig[6+nLenR]; + if (S[-2] != 0x02) + return error("Non-canonical signature: S value type mismatch"); + if (nLenS == 0) + return error("Non-canonical signature: S length is zero"); + if (S[0] & 0x80) + return error("Non-canonical signature: S value negative"); + if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) + return error("Non-canonical signature: S value excessively padded"); + + return true; +} + +bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, bool fStrictEncodings, int nHashType) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); @@ -944,7 +1005,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); - bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); + if (fSuccess) + fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); popstack(stack); popstack(stack); @@ -1004,8 +1067,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co valtype& vchPubKey = stacktop(-ikey); // Check signature - if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType)) - { + bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); + if (fOk) + fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + + if (fOk) { isig++; nSigsCount--; } @@ -1572,14 +1638,14 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto } bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - bool fValidatePayToScriptHash, int nHashType) + bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType) { vector<vector<unsigned char> > stack, stackCopy; - if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptSig, txTo, nIn, fStrictEncodings, nHashType)) return false; if (fValidatePayToScriptHash) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptPubKey, txTo, nIn, fStrictEncodings, nHashType)) return false; if (stack.empty()) return false; @@ -1597,7 +1663,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stackCopy); - if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType)) + if (!EvalScript(stackCopy, pubKey2, txTo, nIn, fStrictEncodings, nHashType)) return false; if (stackCopy.empty()) return false; @@ -1640,7 +1706,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa } // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, 0); + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, true, 0); } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) @@ -1653,7 +1719,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); } -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType) +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType) { assert(nIn < txTo.vin.size()); const CTxIn& txin = txTo.vin[nIn]; @@ -1664,7 +1730,7 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig if (txin.prevout.hash != txFrom.GetHash()) return false; - return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType); + return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, fStrictEncodings, nHashType); } static CScript PushAll(const vector<valtype>& values) @@ -1782,9 +1848,9 @@ CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsign Solver(scriptPubKey, txType, vSolutions); vector<valtype> stack1; - EvalScript(stack1, scriptSig1, CTransaction(), 0, 0); + EvalScript(stack1, scriptSig1, CTransaction(), 0, true, 0); vector<valtype> stack2; - EvalScript(stack2, scriptSig2, CTransaction(), 0, 0); + EvalScript(stack2, scriptSig2, CTransaction(), 0, true, 0); return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); } |