diff --git a/Foundation/include/Poco/DigestEngine.h b/Foundation/include/Poco/DigestEngine.h index 48bd9192e..b22ab6d89 100644 --- a/Foundation/include/Poco/DigestEngine.h +++ b/Foundation/include/Poco/DigestEngine.h @@ -40,19 +40,19 @@ public: DigestEngine(); virtual ~DigestEngine(); - + void update(const void* data, std::size_t length); void update(char data); void update(const std::string& data); /// Updates the digest with the given data. - + virtual std::size_t digestLength() const = 0; /// Returns the length of the digest in bytes. virtual void reset() = 0; /// Resets the engine so that a new /// digest can be computed. - + virtual const Digest& digest() = 0; /// Finishes the computation of the digest and /// returns the message digest. Resets the engine @@ -66,11 +66,16 @@ public: static Digest digestFromHex(const std::string& digest); /// Converts a string created by digestToHex back to its Digest presentation + static bool constantTimeEquals(const Digest& d1, const Digest& d2); + /// Compares two Digest values using a constant-time comparison + /// algorithm. This can be used to prevent timing attacks + /// (as discussed in ). + protected: virtual void updateImpl(const void* data, std::size_t length) = 0; /// Updates the digest with the given data. Must be implemented /// by subclasses. - + private: DigestEngine(const DigestEngine&); DigestEngine& operator = (const DigestEngine&); @@ -96,7 +101,7 @@ inline void DigestEngine::update(char data) inline void DigestEngine::update(const std::string& data) { - updateImpl(data.data(), data.size()); + updateImpl(data.data(), data.size()); } diff --git a/Foundation/src/DigestEngine.cpp b/Foundation/src/DigestEngine.cpp index 4e51792e5..2c01eb6b8 100644 --- a/Foundation/src/DigestEngine.cpp +++ b/Foundation/src/DigestEngine.cpp @@ -79,5 +79,21 @@ DigestEngine::Digest DigestEngine::digestFromHex(const std::string& digest) } +bool DigestEngine::constantTimeEquals(const Digest& d1, const Digest& d2) +{ + if (d1.size() != d2.size()) return false; + + int result = 0; + Digest::const_iterator it1 = d1.begin(); + Digest::const_iterator it2 = d2.begin(); + Digest::const_iterator end1 = d1.end(); + while (it1 != end1) + { + result |= *it1++ ^ *it2++; + } + return result == 0; +} + + } // namespace Poco diff --git a/Foundation/testsuite/src/MD5EngineTest.cpp b/Foundation/testsuite/src/MD5EngineTest.cpp index 0ac56050b..54ae0a25c 100644 --- a/Foundation/testsuite/src/MD5EngineTest.cpp +++ b/Foundation/testsuite/src/MD5EngineTest.cpp @@ -48,7 +48,7 @@ void MD5EngineTest::testMD5() engine.update("abcdefghijklmnopqrstuvwxyz"); assert (DigestEngine::digestToHex(engine.digest()) == "c3fcd3d76192e4007dfb496cca67e13b"); - + engine.update("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); engine.update("abcdefghijklmnopqrstuvwxyz0123456789"); assert (DigestEngine::digestToHex(engine.digest()) == "d174ab98d277d9f5a5611c2c9f419d9f"); @@ -58,6 +58,17 @@ void MD5EngineTest::testMD5() } +void MD5EngineTest::testConstantTimeEquals() +{ + DigestEngine::Digest d1 = DigestEngine::digestFromHex("d41d8cd98f00b204e9800998ecf8427e"); + DigestEngine::Digest d2 = DigestEngine::digestFromHex("d41d8cd98f00b204e9800998ecf8427e"); + DigestEngine::Digest d3 = DigestEngine::digestFromHex("0cc175b9c0f1b6a831c399e269772661"); + + assert (DigestEngine::constantTimeEquals(d1, d2)); + assert (!DigestEngine::constantTimeEquals(d1, d3)); +} + + void MD5EngineTest::setUp() { } @@ -73,6 +84,7 @@ CppUnit::Test* MD5EngineTest::suite() CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("MD5EngineTest"); CppUnit_addTest(pSuite, MD5EngineTest, testMD5); + CppUnit_addTest(pSuite, MD5EngineTest, testConstantTimeEquals); return pSuite; } diff --git a/Foundation/testsuite/src/MD5EngineTest.h b/Foundation/testsuite/src/MD5EngineTest.h index dec9c641a..50834c123 100644 --- a/Foundation/testsuite/src/MD5EngineTest.h +++ b/Foundation/testsuite/src/MD5EngineTest.h @@ -25,6 +25,7 @@ public: ~MD5EngineTest(); void testMD5(); + void testConstantTimeEquals(); void setUp(); void tearDown();