diff --git a/src/ripple/sslutil/api/ECDSACanonical.h b/src/ripple/sslutil/api/ECDSACanonical.h new file mode 100644 index 000000000..11643dc75 --- /dev/null +++ b/src/ripple/sslutil/api/ECDSACanonical.h @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SSLUTIL_ECDSACANONICAL_H_INCLUDED +#define RIPPLE_SSLUTIL_ECDSACANONICAL_H_INCLUDED + +namespace ripple { + +enum class ECDSA +{ + not_strict = 0, + strict +}; + +/** Checks whether a secp256k1 ECDSA signature is canonical. + Return value is true if the signature is canonical. + If mustBeStrict is specified, the signature must be + strictly canonical (one and only one valid form). + The return value for something that is not an ECDSA + signature is unspecified. (But the function will not crash.) +*/ +bool isCanonicalECDSASig (void const* signature, size_t sigLen, ECDSA mustBeStrict); + +inline bool isCanonicalECDSASig (Blob const& signature, ECDSA mustBeStrict) +{ + return signature.empty() ? false : isCanonicalECDSASig (&signature[0], signature.size(), mustBeStrict); +} + +/** Converts a canonical secp256k1 ECDSA signature to a + fully-canonical one. Returns true if the original signature + was already fully-canonical. The behavior if something + that is not a canonical secp256k1 ECDSA signature is + passed is unspecified. The signature buffer must be large + enough to accommodate the largest valid fully-canonical + secp256k1 ECDSA signature (72 bytes). +*/ +bool makeCanonicalECDSASig (void *signature, size_t& sigLen); + +} + +#endif diff --git a/src/ripple/sslutil/impl/ECDSACanonical.cpp b/src/ripple/sslutil/impl/ECDSACanonical.cpp new file mode 100644 index 000000000..d7138b777 --- /dev/null +++ b/src/ripple/sslutil/impl/ECDSACanonical.cpp @@ -0,0 +1,299 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +namespace ripple { + +namespace detail { + // A simple wrapper for a BIGNUM to make it + // easier to allocate, construct, and free them + struct BigNum + { + BIGNUM* num; + + BigNum (BigNum const&) = delete; + BigNum& operator=(BigNum const&) = delete; + + BigNum (const char *hex) + { + num = BN_new (); + BN_hex2bn (&num, hex); + } + + BigNum () + { + num = BN_new (); + } + + BigNum (unsigned char const* ptr, size_t len) + { + num = BN_new (); + BN_bin2bn (ptr, len, num); + } + + ~BigNum () + { + BN_free (num); + } + + operator BIGNUM* () + { + return num; + } + }; + + // The SECp256k1 modulus + static BigNum modulus ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); +} + +bool isCanonicalECDSASig (void const* vSig, size_t sigLen, ECDSA strict_param) +{ + // Make sure signature is canonical + // To protect against signature morphing attacks + // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + // and https://github.com/sipa/bitcoin/commit/58bc86e37fda1aec270bccb3df6c20fbd2a6591c + + // Signature should be: + // <30> [ <02> ] [ <02> ] + + const bool strict = strict_param == ECDSA::strict; + + unsigned char const* sig = reinterpret_cast (vSig); + + if ((sigLen < 10) || (sigLen > 74)) + return false; + + if ((sig[0] != 0x30) || (sig[1] != (sigLen - 2))) + return false; + + // Find R and check its length + int rPos = 4, rLen = sig[3]; + if ((rLen < 2) || ((rLen + 6) > sigLen)) + return false; + + // Find S and check its length + int sPos = rLen + 6, sLen = sig [rLen + 5]; + if ((sLen < 2) || ((rLen + sLen + 6) != sigLen)) + return false; + + if ((sig[rPos - 2] != 0x02) || (sig[sPos - 2] != 0x02)) + return false; // R or S have wrong type + + if ((sig[rPos] & 0x80) != 0) + return false; // R is negative + + if ((sig[rPos] == 0) && ((sig[rPos + 1] & 0x80) == 0)) + return false; // R is padded + + if ((sig[sPos] & 0x80) != 0) + return false; // S is negative + + if ((sig[sPos] == 0) && ((sig[sPos + 1] & 0x80) == 0)) + return false; // S is padded + + detail::BigNum bnR (&sig[rPos], rLen); + detail::BigNum bnS (&sig[sPos], sLen); + if ((BN_cmp (bnR, detail::modulus) != -1) || (BN_cmp (bnS, detail::modulus) != -1)) + return false; // R or S greater than modulus + + if (!strict) + return true; + + detail::BigNum mS; + BN_sub (mS, detail::modulus, bnS); + return BN_cmp (bnS, mS) != 1; +} + + +// Returns true if original signature was alread canonical +bool makeCanonicalECDSASig (void* vSig, size_t& sigLen) +{ +// Signature is (r,s) where 0 < s < g +// If (g-s) (vSig); + bool ret = false; + + // Find internals + int rLen = sig[3]; + int sPos = rLen + 6, sLen = sig[rLen + 5]; + + detail::BigNum origS, newS; + BN_bin2bn (&sig[sPos], sLen, origS); + BN_sub (newS, detail::modulus, origS); + + if (BN_cmp (origS, newS) == 1) + { // original signature is not fully canonical + unsigned char newSbuf [64]; + int newSlen = BN_bn2bin (newS, newSbuf); + + if ((newSbuf[0] & 0x80) == 0) + { // no extra padding byte is needed + sig[1] = sig[1] - sLen + newSlen; + sig[sPos - 1] = newSlen; + memcpy (&sig[sPos], newSbuf, newSlen); + } + else + { // an extra padding byte is needed + sig[1] = sig[1] - sLen + newSlen + 1; + sig[sPos - 1] = newSlen + 1; + sig[sPos] = 0; + memcpy (&sig[sPos + 1], newSbuf, newSlen); + } + sigLen = sig[1] + 2; + } + else + ret = true; + + return ret; +} + +template +void hex_to_binary (FwdIter first, FwdIter last, Container& out) +{ + struct Table + { + int val[256]; + Table () + { + std::fill (val, val+256, 0); + for (int i = 0; i < 10; ++i) + val ['0'+i] = i; + for (int i = 0; i < 6; ++i) + { + val ['A'+i] = 10 + i; + val ['a'+i] = 10 + i; + } + } + int operator[] (int i) + { + return val[i]; + } + }; + + static Table lut; + out.reserve (std::distance (first, last) / 2); + while (first != last) + { + auto const hi (lut[(*first++)]); + auto const lo (lut[(*first++)]); + out.push_back ((hi*16)+lo); + } +} + +class ECDSACanonicalTests : public UnitTest +{ +public: + ECDSACanonicalTests () : UnitTest ("ECDSACanonical", "ripple") + { + } + + bool isCanonical (std::string const& hex) + { + Blob j; + hex_to_binary (hex.begin(), hex.end(), j); + return isCanonicalECDSASig (&j[0], j.size(), ECDSA::not_strict); + } + + void runTest () + { + beginTestCase ("canonicalSignatures"); + + expect (isCanonical("304402203932c892e2e550f3af8ee4ce9c215a87f9bb" + "831dcac87b2838e2c2eaa891df0c022030b61dd36543125d56b9f9f3a1f" + "53189e5af33cdda8d77a5209aec03978fa001"), "canonical signature"); + + expect (isCanonical("30450220076045be6f9eca28ff1ec606b833d0b87e70b" + "2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688" + "eca1d2ba7f6b180620eaa03488e6585db6ba01"), "canonical signature"); + + expect (isCanonical("3046022100876045be6f9eca28ff1ec606b833d0b87e7" + "0b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c" + "2eca1d2ba7f6b180620eaa03488e6585db6ba"), "canonical signature"); + + expect (!isCanonical("3005" "0201FF" "0200"), "tooshort"); + + expect (!isCanonical("3047" + "0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "toolong"); + + expect (!isCanonical("3144" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "type"); + + expect (!isCanonical("3045" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "totallength"); + + expect (!isCanonical( + "301F" "01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1"), + "Slenoob"); + + expect (!isCanonical("3045" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed00"), + "R+S"); + + expect (!isCanonical("3044" + "01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "Rtype"); + + expect (!isCanonical("3024" "0200" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "Rlen=0"); + + expect (!isCanonical("3044" + "02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "R<0"); + + expect (!isCanonical("3045" + "0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "Rpadded"); + + expect (!isCanonical("3044" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105012" + "02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "Stype"); + + expect (!isCanonical("3024" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "0200"), + "Slen=0"); + + expect (!isCanonical("3044" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "S<0"); + + expect (!isCanonical("3045" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed"), + "Spadded"); + + } +}; + +static ECDSACanonicalTests ECDSACTests; + +} diff --git a/src/ripple/sslutil/ripple_sslutil.cpp b/src/ripple/sslutil/ripple_sslutil.cpp index c510753b8..488bff699 100644 --- a/src/ripple/sslutil/ripple_sslutil.cpp +++ b/src/ripple/sslutil/ripple_sslutil.cpp @@ -22,5 +22,6 @@ #include "ripple_sslutil.h" #include "impl/CBigNum.cpp" +#include "impl/ECDSACanonical.cpp" #include "impl/DHUtil.cpp" #include "impl/HashUtilities.cpp" diff --git a/src/ripple/sslutil/ripple_sslutil.h b/src/ripple/sslutil/ripple_sslutil.h index 427c85043..c1374ccb6 100644 --- a/src/ripple/sslutil/ripple_sslutil.h +++ b/src/ripple/sslutil/ripple_sslutil.h @@ -38,5 +38,6 @@ using namespace beast; #include "api/CBigNum.h" #include "api/DHUtil.h" #include "api/HashUtilities.h" +#include "api/ECDSACanonical.h" #endif diff --git a/src/ripple_app/ledger/LedgerProposal.cpp b/src/ripple_app/ledger/LedgerProposal.cpp index 0af7f1f39..b89647a47 100644 --- a/src/ripple_app/ledger/LedgerProposal.cpp +++ b/src/ripple_app/ledger/LedgerProposal.cpp @@ -61,7 +61,7 @@ uint256 LedgerProposal::getSigningHash () const bool LedgerProposal::checkSign (const std::string& signature, uint256 const& signingHash) { - return mPublicKey.verifyNodePublic (signingHash, signature); + return mPublicKey.verifyNodePublic (signingHash, signature, ECDSA::not_strict); } bool LedgerProposal::changePosition (uint256 const& newPosition, uint32 closeTime) diff --git a/src/ripple_app/ledger/SerializedValidation.cpp b/src/ripple_app/ledger/SerializedValidation.cpp index 9b08b9cb2..857c2cd0a 100644 --- a/src/ripple_app/ledger/SerializedValidation.cpp +++ b/src/ripple_app/ledger/SerializedValidation.cpp @@ -92,8 +92,11 @@ bool SerializedValidation::isValid (uint256 const& signingHash) const { try { + const ECDSA fullyCanonical = getFlags () & vfFullyCanonicalSig ? + ECDSA::strict : ECDSA::not_strict; RippleAddress raPublicKey = RippleAddress::createNodePublic (getFieldVL (sfSigningPubKey)); - return raPublicKey.isValid () && raPublicKey.verifyNodePublic (signingHash, getFieldVL (sfSignature)); + return raPublicKey.isValid () && + raPublicKey.verifyNodePublic (signingHash, getFieldVL (sfSignature), fullyCanonical); } catch (...) { diff --git a/src/ripple_app/ledger/SerializedValidation.h b/src/ripple_app/ledger/SerializedValidation.h index d483e6be0..6fc577725 100644 --- a/src/ripple_app/ledger/SerializedValidation.h +++ b/src/ripple_app/ledger/SerializedValidation.h @@ -20,6 +20,9 @@ #ifndef RIPPLE_SERIALIZEDVALIDATION_H #define RIPPLE_SERIALIZEDVALIDATION_H +// Validation flags +const uint32 vfFullyCanonicalSig = 0x80000000; // signature is fully canonical + class SerializedValidation : public STObject , public CountedObject diff --git a/src/ripple_app/misc/SerializedTransaction.cpp b/src/ripple_app/misc/SerializedTransaction.cpp index d94860152..6473cf9b4 100644 --- a/src/ripple_app/misc/SerializedTransaction.cpp +++ b/src/ripple_app/misc/SerializedTransaction.cpp @@ -217,7 +217,9 @@ bool SerializedTransaction::checkSign (const RippleAddress& naAccountPublic) con { try { - return naAccountPublic.accountPublicVerify (getSigningHash (), getFieldVL (sfTxnSignature)); + const ECDSA fullyCanonical = (getFlags() & tfFullyCanonicalSig) ? + ECDSA::strict : ECDSA::not_strict; + return naAccountPublic.accountPublicVerify (getSigningHash (), getFieldVL (sfTxnSignature), fullyCanonical); } catch (...) { diff --git a/src/ripple_app/tx/OfferCancelTransactor.cpp b/src/ripple_app/tx/OfferCancelTransactor.cpp index f565c6456..9d639715d 100644 --- a/src/ripple_app/tx/OfferCancelTransactor.cpp +++ b/src/ripple_app/tx/OfferCancelTransactor.cpp @@ -29,7 +29,7 @@ TER OfferCancelTransactor::doApply () const uint32 uTxFlags = mTxn.getFlags (); - if (uTxFlags) + if (uTxFlags & tfUniversalMask) { WriteLog (lsINFO, OfferCancelTransactor) << "OfferCancel: Malformed transaction: Invalid flags set."; diff --git a/src/ripple_app/tx/RegularKeySetTransactor.cpp b/src/ripple_app/tx/RegularKeySetTransactor.cpp index be760e2d9..c8c4508d0 100644 --- a/src/ripple_app/tx/RegularKeySetTransactor.cpp +++ b/src/ripple_app/tx/RegularKeySetTransactor.cpp @@ -39,7 +39,7 @@ TER RegularKeySetTransactor::doApply () const uint32 uTxFlags = mTxn.getFlags (); - if (uTxFlags) + if (uTxFlags & tfUniversalMask) { WriteLog (lsINFO, RegularKeySetTransactor) << "RegularKeySet: Malformed transaction: Invalid flags set."; diff --git a/src/ripple_app/tx/WalletAddTransactor.cpp b/src/ripple_app/tx/WalletAddTransactor.cpp index 754835bc6..3172631ae 100644 --- a/src/ripple_app/tx/WalletAddTransactor.cpp +++ b/src/ripple_app/tx/WalletAddTransactor.cpp @@ -31,7 +31,7 @@ TER WalletAddTransactor::doApply () const uint32 uTxFlags = mTxn.getFlags (); - if (uTxFlags) + if (uTxFlags & tfUniversalMask) { WriteLog (lsINFO, WalletAddTransactor) << "WalletAdd: Malformed transaction: Invalid flags set."; @@ -39,7 +39,8 @@ TER WalletAddTransactor::doApply () } // FIXME: This should be moved to the transaction's signature check logic and cached - if (!naMasterPubKey.accountPublicVerify (Serializer::getSHA512Half (uAuthKeyID.begin (), uAuthKeyID.size ()), vucSignature)) + if (!naMasterPubKey.accountPublicVerify ( + Serializer::getSHA512Half (uAuthKeyID.begin (), uAuthKeyID.size ()), vucSignature, ECDSA::not_strict)) { Log::out() << "WalletAdd: unauthorized: bad signature "; diff --git a/src/ripple_data/crypto/CKey.h b/src/ripple_data/crypto/CKey.h index fcc1e01a0..b1040c14f 100644 --- a/src/ripple_data/crypto/CKey.h +++ b/src/ripple_data/crypto/CKey.h @@ -271,16 +271,16 @@ public: bool Sign (uint256 const& hash, Blob& vchSig) { - unsigned char pchSig[10000]; - unsigned int nSize = 0; - - vchSig.clear (); + unsigned char pchSig[128]; + unsigned int nSize = sizeof(pchSig)/sizeof(pchSig[0]) - 1; if (!ECDSA_sign (0, (unsigned char*)hash.begin (), hash.size (), pchSig, &nSize, pkey)) return false; - vchSig.resize (nSize); - memcpy (&vchSig[0], pchSig, nSize); + size_t len = nSize; + makeCanonicalECDSASig (pchSig, len); + vchSig.resize (len); + memcpy (&vchSig[0], pchSig, len); return true; } diff --git a/src/ripple_data/protocol/RippleAddress.cpp b/src/ripple_data/protocol/RippleAddress.cpp index 5f2fd9d73..10587a22e 100644 --- a/src/ripple_data/protocol/RippleAddress.cpp +++ b/src/ripple_data/protocol/RippleAddress.cpp @@ -161,12 +161,14 @@ void RippleAddress::setNodePublic (Blob const& vPublic) SetData (VER_NODE_PUBLIC, vPublic); } -bool RippleAddress::verifyNodePublic (uint256 const& hash, Blob const& vchSig) const +bool RippleAddress::verifyNodePublic (uint256 const& hash, Blob const& vchSig, ECDSA fullyCanonical) const { CKey pubkey = CKey (); bool bVerified; - if (!pubkey.SetPubKey (getNodePublic ())) + bVerified = isCanonicalECDSASig (vchSig, fullyCanonical); + + if (bVerified && !pubkey.SetPubKey (getNodePublic ())) { // Failed to set public key. bVerified = false; @@ -179,11 +181,11 @@ bool RippleAddress::verifyNodePublic (uint256 const& hash, Blob const& vchSig) c return bVerified; } -bool RippleAddress::verifyNodePublic (uint256 const& hash, const std::string& strSig) const +bool RippleAddress::verifyNodePublic (uint256 const& hash, const std::string& strSig, ECDSA fullyCanonical) const { Blob vchSig (strSig.begin (), strSig.end ()); - return verifyNodePublic (hash, vchSig); + return verifyNodePublic (hash, vchSig, fullyCanonical); } // @@ -442,12 +444,13 @@ void RippleAddress::setAccountPublic (const RippleAddress& generator, int seq) setAccountPublic (pubkey.GetPubKey ()); } -bool RippleAddress::accountPublicVerify (uint256 const& uHash, Blob const& vucSig) const +bool RippleAddress::accountPublicVerify (uint256 const& uHash, Blob const& vucSig, ECDSA fullyCanonical) const { CKey ckPublic; - bool bVerified; - if (!ckPublic.SetPubKey (getAccountPublic ())) + bool bVerified = isCanonicalECDSASig (vucSig, fullyCanonical); + + if (bVerified && !ckPublic.SetPubKey (getAccountPublic ())) { // Bad private key. WriteLog (lsWARNING, RippleAddress) << "accountPublicVerify: Bad private key."; @@ -919,7 +922,7 @@ public: Blob vucTextSig; naNodePrivate.signNodePrivate (uHash, vucTextSig); - expect (naNodePublic.verifyNodePublic (uHash, vucTextSig), "Verify failed."); + expect (naNodePublic.verifyNodePublic (uHash, vucTextSig, ECDSA::strict), "Verify failed."); // Construct a public generator from the seed. RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naSeed); @@ -944,12 +947,14 @@ public: // Check account signing. expect (naAccountPrivate0.accountPrivateSign (uHash, vucTextSig), "Signing failed."); - expect (naAccountPublic0.accountPublicVerify (uHash, vucTextSig), "Verify failed."); - expect (!naAccountPublic1.accountPublicVerify (uHash, vucTextSig), "Anti-verify failed."); + expect (naAccountPublic0.accountPublicVerify (uHash, vucTextSig, ECDSA::strict), "Verify failed."); + expect (!naAccountPublic1.accountPublicVerify (uHash, vucTextSig, ECDSA::not_strict), "Anti-verify failed."); + expect (!naAccountPublic1.accountPublicVerify (uHash, vucTextSig, ECDSA::strict), "Anti-verify failed."); expect (naAccountPrivate1.accountPrivateSign (uHash, vucTextSig), "Signing failed."); - expect (naAccountPublic1.accountPublicVerify (uHash, vucTextSig), "Verify failed."); - expect (!naAccountPublic0.accountPublicVerify (uHash, vucTextSig), "Anti-verify failed."); + expect (naAccountPublic1.accountPublicVerify (uHash, vucTextSig, ECDSA::strict), "Verify failed."); + expect (!naAccountPublic0.accountPublicVerify (uHash, vucTextSig, ECDSA::not_strict), "Anti-verify failed."); + expect (!naAccountPublic0.accountPublicVerify (uHash, vucTextSig, ECDSA::strict), "Anti-verify failed."); // Check account encryption. Blob vucTextCipher diff --git a/src/ripple_data/protocol/RippleAddress.h b/src/ripple_data/protocol/RippleAddress.h index 2d094746d..7a9822e9e 100644 --- a/src/ripple_data/protocol/RippleAddress.h +++ b/src/ripple_data/protocol/RippleAddress.h @@ -20,6 +20,8 @@ #ifndef RIPPLE_RIPPLEADDRESS_H #define RIPPLE_RIPPLEADDRESS_H +#include "../ripple/sslutil/api/ECDSACanonical.h" + // // Used to hold addresses and parse and produce human formats. // @@ -65,8 +67,8 @@ public: bool setNodePublic (const std::string& strPublic); void setNodePublic (Blob const& vPublic); - bool verifyNodePublic (uint256 const& hash, Blob const& vchSig) const; - bool verifyNodePublic (uint256 const& hash, const std::string& strSig) const; + bool verifyNodePublic (uint256 const& hash, Blob const& vchSig, ECDSA mustBeFullyCanonical) const; + bool verifyNodePublic (uint256 const& hash, const std::string& strSig, ECDSA mustBeFullyCanonical) const; static RippleAddress createNodePublic (const RippleAddress& naSeed); static RippleAddress createNodePublic (Blob const& vPublic); @@ -127,7 +129,7 @@ public: void setAccountPublic (Blob const& vPublic); void setAccountPublic (const RippleAddress& generator, int seq); - bool accountPublicVerify (uint256 const& uHash, Blob const& vucSig) const; + bool accountPublicVerify (uint256 const& uHash, Blob const& vucSig, ECDSA mustBeFullyCanonical) const; static RippleAddress createAccountPublic (Blob const& vPublic) { diff --git a/src/ripple_data/protocol/TxFlags.h b/src/ripple_data/protocol/TxFlags.h index 5012b71c5..39eccd34d 100644 --- a/src/ripple_data/protocol/TxFlags.h +++ b/src/ripple_data/protocol/TxFlags.h @@ -38,6 +38,11 @@ public: }; // VFALCO TODO Move all flags into this container after some study. +// Universal Transaction flags: +const uint32 tfFullyCanonicalSig = 0x80000000; +const uint32 tfUniversal = tfFullyCanonicalSig; +const uint32 tfUniversalMask = ~ tfUniversal; + // AccountSet flags: // VFALCO TODO Javadoc comment every one of these constants //const uint32 TxFlag::requireDestTag = 0x00010000; @@ -62,18 +67,18 @@ const uint32 tfPassive = 0x00010000; const uint32 tfImmediateOrCancel = 0x00020000; const uint32 tfFillOrKill = 0x00040000; const uint32 tfSell = 0x00080000; -const uint32 tfOfferCreateMask = ~ (tfPassive | tfImmediateOrCancel | tfFillOrKill | tfSell); +const uint32 tfOfferCreateMask = ~ (tfUniversal | tfPassive | tfImmediateOrCancel | tfFillOrKill | tfSell); // Payment flags: const uint32 tfNoRippleDirect = 0x00010000; const uint32 tfPartialPayment = 0x00020000; const uint32 tfLimitQuality = 0x00040000; -const uint32 tfPaymentMask = ~ (tfPartialPayment | tfLimitQuality | tfNoRippleDirect); +const uint32 tfPaymentMask = ~ (tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect); // TrustSet flags: const uint32 tfSetfAuth = 0x00010000; const uint32 tfSetNoRipple = 0x00020000; const uint32 tfClearNoRipple = 0x00040000; -const uint32 tfTrustSetMask = ~ (tfSetfAuth | tfSetNoRipple | tfClearNoRipple); +const uint32 tfTrustSetMask = ~ (tfUniversal | tfSetfAuth | tfSetNoRipple | tfClearNoRipple); #endif diff --git a/src/ripple_data/ripple_data.h b/src/ripple_data/ripple_data.h index 080334c19..3ea736904 100644 --- a/src/ripple_data/ripple_data.h +++ b/src/ripple_data/ripple_data.h @@ -22,6 +22,7 @@ #include "../ripple_basics/ripple_basics.h" #include "../ripple/json/ripple_json.h" +#include "../ripple/sslutil/api/ECDSACanonical.h" struct bignum_st; typedef struct bignum_st BIGNUM; diff --git a/src/ripple_overlay/impl/PeerImp.h b/src/ripple_overlay/impl/PeerImp.h index dc268bc87..f9db5abe7 100644 --- a/src/ripple_overlay/impl/PeerImp.h +++ b/src/ripple_overlay/impl/PeerImp.h @@ -1397,7 +1397,7 @@ private: { m_journal.info << "Hello: Disconnect: Bad node public key."; } - else if (!m_nodePublicKey.verifyNodePublic (m_secureCookie, packet.nodeproof ())) + else if (!m_nodePublicKey.verifyNodePublic (m_secureCookie, packet.nodeproof (), ECDSA::not_strict)) { // Unable to verify they have private key for claimed public key. m_journal.info << "Hello: Disconnect: Failed to verify session.";