diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2012-12-26 21:10:49 +0100 |
---|---|---|
committer | Pieter Wuille <pieterw@google.com> | 2013-08-16 00:17:50 +0200 |
commit | a81cd96805ce6b65cca3a40ebbd3b2eb428abb7b (patch) | |
tree | 6bd44b6534c2041f0728f8b985d7c766f85c41cb /src/script.cpp | |
parent | 4323bfeafda4a5e0101710d94b518d41819a2325 (diff) |
Only create signatures with even S, and verification mode to check.
To fix a minor malleability found by Sergio Lerner (reported here:
https://bitcointalk.org/index.php?topic=8392.msg1245898#msg1245898)
The problem is that if (R,S) is a valid ECDSA signature for a given
message and public key, (R,-S) is also valid. Modulo N (the order
of the secp256k1 curve), this means that both (R,S) and (R,N-S) are
valid. Given that N is odd, S and N-S have a different lowest bit.
We solve the problem by forcing signatures to have an even S value,
excluding one of the alternatives.
This commit just changes the signing code to always produce even S
values, and adds a verification mode to check it. This code is not
enabled anywhere yet. Existing tests in key_tests.cpp verify that
the produced signatures are still valid.
Diffstat (limited to 'src/script.cpp')
-rw-r--r-- | src/script.cpp | 26 |
1 files changed, 17 insertions, 9 deletions
diff --git a/src/script.cpp b/src/script.cpp index 5699fbfb6a..2df2e9f0d5 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -227,7 +227,10 @@ const char* GetOpName(opcodetype opcode) } } -bool IsCanonicalPubKey(const valtype &vchPubKey) { +bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + if (vchPubKey.size() < 33) return error("Non-canonical public key: too short"); if (vchPubKey[0] == 0x04) { @@ -242,7 +245,10 @@ bool IsCanonicalPubKey(const valtype &vchPubKey) { return true; } -bool IsCanonicalSignature(const valtype &vchSig) { +bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + // 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 @@ -286,6 +292,11 @@ bool IsCanonicalSignature(const valtype &vchSig) { 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"); + } + return true; } @@ -302,7 +313,6 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co if (script.size() > 10000) return false; int nOpCount = 0; - bool fStrictEncodings = flags & SCRIPT_VERIFY_STRICTENC; try { @@ -841,9 +851,8 @@ 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 = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); - if (fSuccess) - fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + bool fSuccess = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && + CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); popstack(stack); popstack(stack); @@ -903,9 +912,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co valtype& vchPubKey = stacktop(-ikey); // Check signature - bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); - if (fOk) - fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && + CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); if (fOk) { isig++; |