From fe59436763b407418e66bf47aa7f1fb34470d093 Mon Sep 17 00:00:00 2001 From: Maksim Kita <kitaetoya@gmail.com> Date: Mon, 30 May 2022 04:52:39 +0200 Subject: [PATCH] HTTPDigestCredentials added support for RFC7616 algorithms (#3026) --- Foundation/include/Poco/SHA2Engine.h | 10 +- Foundation/src/SHA2Engine.cpp | 60 +++++++-- Foundation/testsuite/src/SHA2EngineTest.cpp | 56 +++++++- Foundation/testsuite/src/SHA2EngineTest.h | 2 + Net/include/Poco/Net/HTTPDigestCredentials.h | 17 ++- Net/src/HTTPDigestCredentials.cpp | 131 +++++++++++++++++-- Net/testsuite/src/HTTPCredentialsTest.cpp | 79 +++++++++++ Net/testsuite/src/HTTPCredentialsTest.h | 3 + 8 files changed, 331 insertions(+), 27 deletions(-) diff --git a/Foundation/include/Poco/SHA2Engine.h b/Foundation/include/Poco/SHA2Engine.h index 67417100c..c1de5cd50 100644 --- a/Foundation/include/Poco/SHA2Engine.h +++ b/Foundation/include/Poco/SHA2Engine.h @@ -38,10 +38,12 @@ class Foundation_API SHA2Engine: public DigestEngine public: enum ALGORITHM { - SHA_224 = 224, - SHA_256 = 256, - SHA_384 = 384, - SHA_512 = 512 + SHA_224 = 1, + SHA_256, + SHA_384, + SHA_512, + SHA_512_224, + SHA_512_256 }; SHA2Engine(ALGORITHM algorithm = SHA_256); diff --git a/Foundation/src/SHA2Engine.cpp b/Foundation/src/SHA2Engine.cpp index b09b30a79..cc3e84031 100644 --- a/Foundation/src/SHA2Engine.cpp +++ b/Foundation/src/SHA2Engine.cpp @@ -41,7 +41,6 @@ typedef struct Poco::UInt64 state64[8]; } state; - SHA2Engine::ALGORITHM size; unsigned char buffer[128]; } HASHCONTEXT; @@ -274,7 +273,7 @@ void SHA2Engine::updateImpl(const void* buffer_, std::size_t count) Poco::UInt32 left = 0; HASHCONTEXT* pContext = (HASHCONTEXT*)_context; unsigned char* data = (unsigned char*)buffer_; - if (pContext->size > SHA_256) + if (_algorithm > SHA_256) { left = (Poco::UInt32)(pContext->total.total64[0] & 0x7F); size_t fill = 128 - left; @@ -323,7 +322,31 @@ void SHA2Engine::updateImpl(const void* buffer_, std::size_t count) std::size_t SHA2Engine::digestLength() const { - return (size_t)((int)_algorithm / 8); + size_t result = 0; + + switch (_algorithm) + { + case SHA_224: + result = 224; + break; + case SHA_256: + result = 256; + break; + case SHA_384: + result = 384; + break; + case SHA_512: + result = 512; + break; + case SHA_512_224: + result = 224; + break; + case SHA_512_256: + result = 256; + break; + } + + return result / 8; } @@ -332,7 +355,6 @@ void SHA2Engine::reset() if (_context != NULL) free(_context); _context = calloc(1, sizeof(HASHCONTEXT)); HASHCONTEXT* pContext = (HASHCONTEXT*)_context; - pContext->size = _algorithm; if (_algorithm == SHA_224) { pContext->state.state32[0] = 0xC1059ED8; @@ -366,7 +388,7 @@ void SHA2Engine::reset() pContext->state.state64[6] = UL64(0xDB0C2E0D64F98FA7); pContext->state.state64[7] = UL64(0x47B5481DBEFA4FA4); } - else + else if (_algorithm == SHA_512) { pContext->state.state64[0] = UL64(0x6A09E667F3BCC908); pContext->state.state64[1] = UL64(0xBB67AE8584CAA73B); @@ -377,6 +399,27 @@ void SHA2Engine::reset() pContext->state.state64[6] = UL64(0x1F83D9ABFB41BD6B); pContext->state.state64[7] = UL64(0x5BE0CD19137E2179); } + else if (_algorithm == SHA_512_224) + { + pContext->state.state64[0] = UL64(0x8C3D37C819544DA2); + pContext->state.state64[1] = UL64(0x73E1996689DCD4D6); + pContext->state.state64[2] = UL64(0x1DFAB7AE32FF9C82); + pContext->state.state64[3] = UL64(0x679DD514582F9FCF); + pContext->state.state64[4] = UL64(0x0F6D2B697BD44DA8); + pContext->state.state64[5] = UL64(0x77E36F7304C48942); + pContext->state.state64[6] = UL64(0x3F9D85A86A1D36C8); + pContext->state.state64[7] = UL64(0x1112E6AD91D692A1); + } else + { + pContext->state.state64[0] = UL64(0x22312194FC2BF72C); + pContext->state.state64[1] = UL64(0x9F555FA3C84C64C2); + pContext->state.state64[2] = UL64(0x2393B86B6F53B151); + pContext->state.state64[3] = UL64(0x963877195940EABD); + pContext->state.state64[4] = UL64(0x96283EE2A88EFFE3); + pContext->state.state64[5] = UL64(0xBE5E1E2553863992); + pContext->state.state64[6] = UL64(0x2B0199FC2C85B8AA); + pContext->state.state64[7] = UL64(0x0EB72DDC81C52CA2); + } } @@ -388,7 +431,8 @@ const DigestEngine::Digest& SHA2Engine::digest() size_t last, padn; unsigned char hash[64]; memset(hash, 0, 64); - if (pContext->size > SHA_256) + + if (_algorithm > SHA_256) { unsigned char msglen[16]; Poco::UInt64 high = (pContext->total.total64[0] >> 61) | (pContext->total.total64[1] << 3); @@ -405,7 +449,7 @@ const DigestEngine::Digest& SHA2Engine::digest() PUT_UINT64(pContext->state.state64[3], hash, 24); PUT_UINT64(pContext->state.state64[4], hash, 32); PUT_UINT64(pContext->state.state64[5], hash, 40); - if (pContext->size > SHA_384) + if (_algorithm > SHA_384) { PUT_UINT64(pContext->state.state64[6], hash, 48); PUT_UINT64(pContext->state.state64[7], hash, 56); @@ -429,7 +473,7 @@ const DigestEngine::Digest& SHA2Engine::digest() PUT_UINT32(pContext->state.state32[4], hash, 16); PUT_UINT32(pContext->state.state32[5], hash, 20); PUT_UINT32(pContext->state.state32[6], hash, 24); - if (pContext->size > SHA_224) PUT_UINT32(pContext->state.state32[7], hash, 28); + if (_algorithm > SHA_224) PUT_UINT32(pContext->state.state32[7], hash, 28); } _digest.insert(_digest.begin(), hash, hash + digestLength()); reset(); diff --git a/Foundation/testsuite/src/SHA2EngineTest.cpp b/Foundation/testsuite/src/SHA2EngineTest.cpp index 90ec4a5e7..0ba0c64d3 100644 --- a/Foundation/testsuite/src/SHA2EngineTest.cpp +++ b/Foundation/testsuite/src/SHA2EngineTest.cpp @@ -36,6 +36,9 @@ void SHA2EngineTest::testSHA224() { SHA2Engine engine(SHA2Engine::SHA_224); + engine.update(""); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + engine.update("abc"); assertTrue (DigestEngine::digestToHex(engine.digest()) == "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"); @@ -55,6 +58,9 @@ void SHA2EngineTest::testSHA256() { SHA2Engine engine(SHA2Engine::SHA_256); + engine.update(""); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + engine.update("abc"); assertTrue (DigestEngine::digestToHex(engine.digest()) == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); @@ -74,6 +80,9 @@ void SHA2EngineTest::testSHA384() { SHA2Engine engine(SHA2Engine::SHA_384); + engine.update(""); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"); + engine.update("abc"); assertTrue (DigestEngine::digestToHex(engine.digest()) == "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"); @@ -93,6 +102,9 @@ void SHA2EngineTest::testSHA512() { SHA2Engine engine(SHA2Engine::SHA_512); + engine.update(""); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); + engine.update("abc"); assertTrue (DigestEngine::digestToHex(engine.digest()) == "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); @@ -107,6 +119,47 @@ void SHA2EngineTest::testSHA512() assertTrue (DigestEngine::digestToHex(engine.digest()) == "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); } +void SHA2EngineTest::testSHA512_224() +{ + SHA2Engine engine(SHA2Engine::SHA_512_224); + + engine.update(""); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); + + engine.update("abc"); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"); + + engine.update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174"); + + engine.update("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9"); + + for (int i = 0; i < 1000000; ++i) + engine.update('a'); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); +} + +void SHA2EngineTest::testSHA512_256() +{ + SHA2Engine engine(SHA2Engine::SHA_512_256); + + engine.update(""); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); + + engine.update("abc"); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"); + + engine.update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461"); + + engine.update("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a"); + + for (int i = 0; i < 1000000; ++i) + engine.update('a'); + assertTrue (DigestEngine::digestToHex(engine.digest()) == "9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); +} void SHA2EngineTest::setUp() { @@ -126,6 +179,7 @@ CppUnit::Test* SHA2EngineTest::suite() CppUnit_addTest(pSuite, SHA2EngineTest, testSHA256); CppUnit_addTest(pSuite, SHA2EngineTest, testSHA384); CppUnit_addTest(pSuite, SHA2EngineTest, testSHA512); - + CppUnit_addTest(pSuite, SHA2EngineTest, testSHA512_224); + CppUnit_addTest(pSuite, SHA2EngineTest, testSHA512_256); return pSuite; } diff --git a/Foundation/testsuite/src/SHA2EngineTest.h b/Foundation/testsuite/src/SHA2EngineTest.h index b1a8bf326..ffd795878 100644 --- a/Foundation/testsuite/src/SHA2EngineTest.h +++ b/Foundation/testsuite/src/SHA2EngineTest.h @@ -28,6 +28,8 @@ public: void testSHA256(); void testSHA384(); void testSHA512(); + void testSHA512_224(); + void testSHA512_256(); void setUp(); void tearDown(); diff --git a/Net/include/Poco/Net/HTTPDigestCredentials.h b/Net/include/Poco/Net/HTTPDigestCredentials.h index c321c9fc8..b8c5af438 100644 --- a/Net/include/Poco/Net/HTTPDigestCredentials.h +++ b/Net/include/Poco/Net/HTTPDigestCredentials.h @@ -120,6 +120,9 @@ public: /// and HTTPAuthenticationParams by recomputing the response and comparing /// it with what's in the request. + bool isAlgorithmSupported(const std::string& algorithm) const; + /// Check if digest algorithm is supported + static std::string createNonce(); /// Creates a random nonce string. @@ -133,7 +136,19 @@ private: void updateAuthParams(const HTTPRequest& request); int updateNonceCounter(const std::string& nonce); - static const std::string DEFAULT_ALGORITHM; + class DigestEngineProvider; + + static const std::string MD_5_ALGORITHM; + static const std::string MD_5_SESS_ALGORITHM; + static const std::string SHA_ALGORITHM; + static const std::string SHA_SESS_ALGORITHM; + static const std::string SHA_256_ALGORITHM; + static const std::string SHA_256_SESS_ALGORITHM; + static const std::string SHA_512_256_ALGORITHM; + static const std::string SHA_512_256_SESS_ALGORITHM; + static const std::string SHA_512_ALGORITHM; + static const std::string SHA_512_SESS_ALGORITHM; + static const std::vector<std::string> SUPPORTED_ALGORITHMS; static const std::string DEFAULT_QOP; static const std::string NONCE_PARAM; static const std::string REALM_PARAM; diff --git a/Net/src/HTTPDigestCredentials.cpp b/Net/src/HTTPDigestCredentials.cpp index ac810a6a2..b8543c56e 100644 --- a/Net/src/HTTPDigestCredentials.cpp +++ b/Net/src/HTTPDigestCredentials.cpp @@ -18,12 +18,15 @@ #include "Poco/DateTimeFormatter.h" #include "Poco/Exception.h" #include "Poco/MD5Engine.h" +#include "Poco/SHA1Engine.h" +#include "Poco/SHA2Engine.h" #include "Poco/Net/HTTPDigestCredentials.h" #include "Poco/Net/HTTPRequest.h" #include "Poco/Net/HTTPResponse.h" #include "Poco/NumberFormatter.h" #include "Poco/StringTokenizer.h" +#include <iostream> namespace { @@ -68,7 +71,28 @@ namespace Net { const std::string HTTPDigestCredentials::SCHEME = "Digest"; -const std::string HTTPDigestCredentials::DEFAULT_ALGORITHM("MD5"); +const std::vector<std::string> HTTPDigestCredentials::SUPPORTED_ALGORITHMS = { + "MD5", + "MD5-sess", + "SHA", + "SHA-sess", + "SHA-256", + "SHA-256-sess", + "SHA-512-256", + "SHA-512-256-sess", + "SHA-512", + "SHA-512-sess" +}; +const std::string HTTPDigestCredentials::MD_5_ALGORITHM = "MD5"; +const std::string HTTPDigestCredentials::MD_5_SESS_ALGORITHM = "MD5-sess"; +const std::string HTTPDigestCredentials::SHA_ALGORITHM = "SHA"; +const std::string HTTPDigestCredentials::SHA_SESS_ALGORITHM = "SHA-sess"; +const std::string HTTPDigestCredentials::SHA_256_ALGORITHM = "SHA-256"; +const std::string HTTPDigestCredentials::SHA_256_SESS_ALGORITHM = "SHA-256-sess"; +const std::string HTTPDigestCredentials::SHA_512_256_ALGORITHM = "SHA-512-256"; +const std::string HTTPDigestCredentials::SHA_512_256_SESS_ALGORITHM = "SHA-512-256-sess"; +const std::string HTTPDigestCredentials::SHA_512_ALGORITHM = "SHA-512"; +const std::string HTTPDigestCredentials::SHA_512_SESS_ALGORITHM = "SHA-512-sess"; const std::string HTTPDigestCredentials::DEFAULT_QOP(""); const std::string HTTPDigestCredentials::NONCE_PARAM("nonce"); const std::string HTTPDigestCredentials::REALM_PARAM("realm"); @@ -84,6 +108,44 @@ const std::string HTTPDigestCredentials::NC_PARAM("nc"); int HTTPDigestCredentials::_nonceCounter(0); Poco::FastMutex HTTPDigestCredentials::_nonceMutex; +class HTTPDigestCredentials::DigestEngineProvider { +public: + DigestEngineProvider(std::string algorithm): _algorithm(algorithm) { + _isSessionAlgorithm = _algorithm.find("sess") != std::string::npos; + } + + DigestEngine& engine() { + if (icompare(_algorithm, SHA_ALGORITHM) == 0 || icompare(_algorithm, SHA_SESS_ALGORITHM) == 0) + { + return _sha1Engine; + } + if (icompare(_algorithm, SHA_256_ALGORITHM) == 0 || icompare(_algorithm, SHA_256_SESS_ALGORITHM) == 0) + { + return _sha256Engine; + } else if (icompare(_algorithm, SHA_512_256_ALGORITHM) == 0 || icompare(_algorithm, SHA_512_256_SESS_ALGORITHM) == 0) + { + return _sha512_256Engine; + } else if (icompare(_algorithm, SHA_512_ALGORITHM) == 0 || icompare(_algorithm, SHA_512_SESS_ALGORITHM) == 0) + { + return _sha512; + } + else { + return _md5Engine; + } + } + + bool isSessionAlgorithm() { + return _isSessionAlgorithm; + } +private: + std::string _algorithm; + SHA1Engine _sha1Engine; + MD5Engine _md5Engine; + SHA2Engine _sha256Engine { SHA2Engine::ALGORITHM::SHA_256 }; + SHA2Engine _sha512_256Engine { SHA2Engine::ALGORITHM::SHA_512_256 }; + SHA2Engine _sha512 { SHA2Engine::ALGORITHM::SHA_512 }; + bool _isSessionAlgorithm; +}; HTTPDigestCredentials::HTTPDigestCredentials() { @@ -191,11 +253,6 @@ void HTTPDigestCredentials::createAuthParams(const HTTPRequest& request, const H if (!responseAuthParams.has(NONCE_PARAM) || !responseAuthParams.has(REALM_PARAM)) throw InvalidArgumentException("Invalid HTTP authentication parameters"); - const std::string& algorithm = responseAuthParams.get(ALGORITHM_PARAM, DEFAULT_ALGORITHM); - - if (icompare(algorithm, DEFAULT_ALGORITHM) != 0) - throw NotImplementedException("Unsupported digest algorithm", algorithm); - const std::string& nonce = responseAuthParams.get(NONCE_PARAM); const std::string& qop = responseAuthParams.get(QOP_PARAM, DEFAULT_QOP); const std::string& realm = responseAuthParams.getRealm(); @@ -208,6 +265,10 @@ void HTTPDigestCredentials::createAuthParams(const HTTPRequest& request, const H { _requestAuthParams.set(OPAQUE_PARAM, responseAuthParams.get(OPAQUE_PARAM)); } + if (responseAuthParams.has(ALGORITHM_PARAM)) + { + _requestAuthParams.set(ALGORITHM_PARAM, responseAuthParams.get(ALGORITHM_PARAM)); + } if (qop.empty()) { @@ -233,10 +294,8 @@ void HTTPDigestCredentials::createAuthParams(const HTTPRequest& request, const H } } - void HTTPDigestCredentials::updateAuthParams(const HTTPRequest& request) { - MD5Engine engine; const std::string qop = _requestAuthParams.get(QOP_PARAM, DEFAULT_QOP); const std::string realm = _requestAuthParams.getRealm(); const std::string nonce = _requestAuthParams.get(NONCE_PARAM); @@ -245,6 +304,11 @@ void HTTPDigestCredentials::updateAuthParams(const HTTPRequest& request) if (qop.empty()) { + /// Assume that https://tools.ietf.org/html/rfc7616 does not supported + /// and still using https://tools.ietf.org/html/rfc2069#section-2.4 + + MD5Engine engine; + const std::string ha1 = digest(engine, _username, realm, _password); const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); @@ -252,13 +316,28 @@ void HTTPDigestCredentials::updateAuthParams(const HTTPRequest& request) } else if (icompare(qop, AUTH_PARAM) == 0) { - const std::string cnonce = _requestAuthParams.get(CNONCE_PARAM); + const std::string algorithm = _requestAuthParams.get(ALGORITHM_PARAM, MD_5_ALGORITHM); - const std::string ha1 = digest(engine, _username, realm, _password); - const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); + if (!isAlgorithmSupported(algorithm)) { + throw NotImplementedException("Unsupported digest algorithm", algorithm); + } + + const std::string cnonce = _requestAuthParams.get(CNONCE_PARAM); const std::string nc = formatNonceCounter(updateNonceCounter(nonce)); + DigestEngineProvider engineProvider(algorithm); + DigestEngine &engine = engineProvider.engine(); + + std::string ha1 = digest(engine, _username, realm, _password); + + if (engineProvider.isSessionAlgorithm()) { + ha1 = digest(engine, ha1, nonce, cnonce); + } + + const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); + _requestAuthParams.set(NC_PARAM, nc); + _requestAuthParams.set(CNONCE_PARAM, cnonce); _requestAuthParams.set(RESPONSE_PARAM, digest(engine, ha1, nonce, nc, cnonce, qop, ha2)); } } @@ -277,18 +356,34 @@ bool HTTPDigestCredentials::verifyAuthParams(const HTTPRequest& request, const H const std::string& realm = params.getRealm(); const std::string& qop = params.get(QOP_PARAM, DEFAULT_QOP); std::string response; - MD5Engine engine; if (qop.empty()) { + MD5Engine engine; + const std::string ha1 = digest(engine, _username, realm, _password); const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); response = digest(engine, ha1, nonce, ha2); } else if (icompare(qop, AUTH_PARAM) == 0) { + const std::string& algorithm = params.get(ALGORITHM_PARAM, MD_5_ALGORITHM); + + if (!isAlgorithmSupported(algorithm)) { + throw NotImplementedException("Unsupported digest algorithm", algorithm); + } + + DigestEngineProvider engineProvider(algorithm); + DigestEngine& engine = engineProvider.engine(); + const std::string& cnonce = params.get(CNONCE_PARAM); const std::string& nc = params.get(NC_PARAM); - const std::string ha1 = digest(engine, _username, realm, _password); + + std::string ha1 = digest(engine, _username, realm, _password); + + if (engineProvider.isSessionAlgorithm()) { + ha1 = digest(engine, ha1, nonce, cnonce); + } + const std::string ha2 = digest(engine, request.getMethod(), request.getURI()); response = digest(engine, ha1, nonce, nc, cnonce, qop, ha2); } @@ -309,5 +404,15 @@ int HTTPDigestCredentials::updateNonceCounter(const std::string& nonce) return iter->second; } +bool HTTPDigestCredentials::isAlgorithmSupported(const std::string& algorithm) const +{ + bool isAlgorithmSupported = std::find_if(std::begin(SUPPORTED_ALGORITHMS), + std::end(SUPPORTED_ALGORITHMS), + [&algorithm](const std::string& supportedAlgorithm) { + return icompare(algorithm, supportedAlgorithm) == 0; + }) != std::end(SUPPORTED_ALGORITHMS); + + return isAlgorithmSupported; +} } } // namespace Poco::Net diff --git a/Net/testsuite/src/HTTPCredentialsTest.cpp b/Net/testsuite/src/HTTPCredentialsTest.cpp index f9738b768..d0c0dce27 100644 --- a/Net/testsuite/src/HTTPCredentialsTest.cpp +++ b/Net/testsuite/src/HTTPCredentialsTest.cpp @@ -214,6 +214,45 @@ void HTTPCredentialsTest::testDigestCredentialsQoP() assertTrue (params.size() == 9); } +void HTTPCredentialsTest::testDigestCredentialsQoPSHA256() +{ + HTTPDigestCredentials creds("user", "s3cr3t"); + HTTPRequest request(HTTPRequest::HTTP_GET, "/digest/"); + HTTPResponse response; + response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth,auth-int\", algorithm=SHA-256"); + creds.authenticate(request, response); + + HTTPAuthenticationParams params(request); + assertTrue (params["nonce"] == "212573bb90170538efad012978ab811f%lu"); + assertTrue (params["realm"] == "TestDigest"); + assertTrue (params["response"] != "40e4889cfbd0e561f71e3107a2863bc4"); + assertTrue (params["uri"] == "/digest/"); + assertTrue (params["username"] == "user"); + assertTrue (params["opaque"] == "opaque"); + assertTrue (params["cnonce"] != ""); + assertTrue (params["nc"] == "00000001"); + assertTrue (params["qop"] == "auth"); + assertTrue (params["algorithm"] == "SHA-256"); + assertTrue (params.size() == 10); + + std::string cnonce = params["cnonce"]; + std::string aresp = params["response"]; + + params.clear(); + + creds.updateAuthInfo(request); + params.fromRequest(request); + assertTrue (params["nonce"] == "212573bb90170538efad012978ab811f%lu"); + assertTrue (params["realm"] == "TestDigest"); + assertTrue (params["response"] != aresp); + assertTrue (params["uri"] == "/digest/"); + assertTrue (params["username"] == "user"); + assertTrue (params["opaque"] == "opaque"); + assertTrue (params["cnonce"] == cnonce); + assertTrue (params["nc"] == "00000002"); + assertTrue (params["qop"] == "auth"); + assertTrue (params.size() == 10); +} void HTTPCredentialsTest::testCredentialsBasic() { @@ -311,6 +350,43 @@ void HTTPCredentialsTest::testVerifyAuthInfoQoP() assertTrue (!creds.verifyAuthInfo(request)); } +void HTTPCredentialsTest::testVerifyAuthInfoQoPSHA256() { + HTTPDigestCredentials sha256Creds("user", "s3cr3t"); + HTTPRequest sha256Request(HTTPRequest::HTTP_GET, "/digest/"); + HTTPResponse sha256Response; + sha256Response.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth,auth-int\", algorithm=SHA-256"); + sha256Creds.authenticate(sha256Request, sha256Response); + assertTrue (sha256Creds.verifyAuthInfo(sha256Request)); + + sha256Request.set("Authorization", "Digest cnonce=\"f9c80ffd1c3bc4ee47ed92b704ba75a4\", nc=00000001, nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth\", realm=\"TestDigest\", response=\"ff0e90b9aa019120ea0ed6e23ce95d9a\", uri=\"/digest/\", username=\"user\", algorithm=SHA-256"); + assertTrue (!sha256Creds.verifyAuthInfo(sha256Request)); + + HTTPDigestCredentials sha256SessCreds("user", "s3cr3t"); + HTTPRequest sha256SessRequest(HTTPRequest::HTTP_GET, "/digest/"); + HTTPResponse sha256SessResponse; + sha256SessResponse.set("WWW-Authenticate", "Digest realm=\"TestDigest\", nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth,auth-int\", algorithm=SHA-256-sess"); + sha256SessCreds.authenticate(sha256SessRequest, sha256SessResponse); + assertTrue (sha256SessCreds.verifyAuthInfo(sha256SessRequest)); + + sha256SessRequest.set("Authorization", "Digest cnonce=\"f9c80ffd1c3bc4ee47ed92b704ba75a4\", nc=00000001, nonce=\"212573bb90170538efad012978ab811f%lu\", opaque=\"opaque\", qop=\"auth\", realm=\"TestDigest\", response=\"ff0e90b9aa019120ea0ed6e23ce95d9a\", uri=\"/digest/\", username=\"user\", algorithm=SHA-256-sess"); + assertTrue (!sha256SessCreds.verifyAuthInfo(sha256SessRequest)); +} + +void HTTPCredentialsTest::testIsAlgorithmSupported() { + HTTPDigestCredentials sha256Creds("user", "s3cr3t"); + + assertTrue (sha256Creds.isAlgorithmSupported("MD5")); + assertTrue (sha256Creds.isAlgorithmSupported("MD5-sess")); + assertTrue (sha256Creds.isAlgorithmSupported("SHA")); + assertTrue (sha256Creds.isAlgorithmSupported("SHA-sess")); + assertTrue (sha256Creds.isAlgorithmSupported("SHA-256")); + assertTrue (sha256Creds.isAlgorithmSupported("SHA-256-sess")); + assertTrue (sha256Creds.isAlgorithmSupported("SHA-512-256")); + assertTrue (sha256Creds.isAlgorithmSupported("SHA-512-256-sess")); + assertTrue (sha256Creds.isAlgorithmSupported("SHA-512")); + assertTrue (sha256Creds.isAlgorithmSupported("SHA-512-sess")); + assertFalse (sha256Creds.isAlgorithmSupported("random_algorithm")); +} void HTTPCredentialsTest::setUp() { @@ -333,6 +409,7 @@ CppUnit::Test* HTTPCredentialsTest::suite() CppUnit_addTest(pSuite, HTTPCredentialsTest, testAuthenticationParamsMultipleHeaders); CppUnit_addTest(pSuite, HTTPCredentialsTest, testDigestCredentials); CppUnit_addTest(pSuite, HTTPCredentialsTest, testDigestCredentialsQoP); + CppUnit_addTest(pSuite, HTTPCredentialsTest, testDigestCredentialsQoPSHA256); CppUnit_addTest(pSuite, HTTPCredentialsTest, testCredentialsBasic); CppUnit_addTest(pSuite, HTTPCredentialsTest, testProxyCredentialsBasic); CppUnit_addTest(pSuite, HTTPCredentialsTest, testCredentialsDigest); @@ -341,6 +418,8 @@ CppUnit::Test* HTTPCredentialsTest::suite() CppUnit_addTest(pSuite, HTTPCredentialsTest, testExtractCredentials); CppUnit_addTest(pSuite, HTTPCredentialsTest, testVerifyAuthInfo); CppUnit_addTest(pSuite, HTTPCredentialsTest, testVerifyAuthInfoQoP); + CppUnit_addTest(pSuite, HTTPCredentialsTest, testVerifyAuthInfoQoPSHA256); + CppUnit_addTest(pSuite, HTTPCredentialsTest, testIsAlgorithmSupported); return pSuite; } diff --git a/Net/testsuite/src/HTTPCredentialsTest.h b/Net/testsuite/src/HTTPCredentialsTest.h index 56c9cb0d5..ba8a0f07a 100644 --- a/Net/testsuite/src/HTTPCredentialsTest.h +++ b/Net/testsuite/src/HTTPCredentialsTest.h @@ -31,6 +31,7 @@ public: void testAuthenticationParamsMultipleHeaders(); void testDigestCredentials(); void testDigestCredentialsQoP(); + void testDigestCredentialsQoPSHA256(); void testCredentialsBasic(); void testProxyCredentialsBasic(); void testCredentialsDigest(); @@ -39,6 +40,8 @@ public: void testExtractCredentials(); void testVerifyAuthInfo(); void testVerifyAuthInfoQoP(); + void testVerifyAuthInfoQoPSHA256(); + void testIsAlgorithmSupported(); void setUp(); void tearDown();