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.
This commit is contained in:
committed by
Pieter Wuille
parent
4323bfeafd
commit
a81cd96805
@@ -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++;
|
||||
|
||||
Reference in New Issue
Block a user