diff --git a/include/HttpGetter.h b/include/HttpGetter.h index 41b354f4..d1690a93 100644 --- a/include/HttpGetter.h +++ b/include/HttpGetter.h @@ -69,7 +69,7 @@ public: char const* getErrorText() const { return _errBuffer; } private: - String getAuthDigest(String const& authReq, unsigned int counter); + std::pair getAuthDigest(String const& authReq, unsigned int counter); HttpRequestConfig const& _config; template diff --git a/src/HttpGetter.cpp b/src/HttpGetter.cpp index 1bd1d9b3..226b8bbe 100644 --- a/src/HttpGetter.cpp +++ b/src/HttpGetter.cpp @@ -2,6 +2,7 @@ #include "HttpGetter.h" #include #include "mbedtls/sha256.h" +#include "mbedtls/md5.h" #include #include @@ -155,8 +156,12 @@ HttpRequestResult HttpGetter::performGetRequest() return { false }; } String authReq = upTmpHttpClient->header("WWW-Authenticate"); - String authorization = getAuthDigest(authReq, 1); - upTmpHttpClient->addHeader("Authorization", authorization); + auto authorization = getAuthDigest(authReq, 1); + if (!authorization.first) { + logError("Digest Error: %s", authorization.second.c_str()); + return { false }; + } + upTmpHttpClient->addHeader("Authorization", authorization.second); // use a new TCP connection if the server sent "Connection: close". bool restart = true; @@ -183,6 +188,29 @@ HttpRequestResult HttpGetter::performGetRequest() return { true, std::move(upTmpHttpClient), _spWiFiClient }; } +template +static String bin2hex(uint8_t* hash) { + size_t constexpr kOutLen = binLen * 2 + 1; + char res[kOutLen]; + for (int i = 0; i < binLen; i++) { + snprintf(res + (i*2), sizeof(res) - (i*2), "%02x", hash[i]); + } + return res; +} + +static String md5(const String& data) { + uint8_t hash[16]; + + mbedtls_md5_context ctx; + mbedtls_md5_init(&ctx); + mbedtls_md5_starts_ret(&ctx); + mbedtls_md5_update_ret(&ctx, reinterpret_cast(data.c_str()), data.length()); + mbedtls_md5_finish_ret(&ctx, hash); + mbedtls_md5_free(&ctx); + + return bin2hex(hash); +} + static String sha256(const String& data) { uint8_t hash[32]; @@ -193,12 +221,7 @@ static String sha256(const String& data) { mbedtls_sha256_finish(&ctx, hash); mbedtls_sha256_free(&ctx); - char res[sizeof(hash) * 2 + 1]; - for (int i = 0; i < sizeof(hash); i++) { - snprintf(res + (i*2), sizeof(res) - (i*2), "%02x", hash[i]); - } - - return res; + return bin2hex(hash); } static String extractParam(String const& authReq, String const& param, char delimiter) { @@ -219,7 +242,22 @@ static String getcNonce(int len) { return s; } -String HttpGetter::getAuthDigest(String const& authReq, unsigned int counter) { +static std::pair getAlgo(String const& authReq) { + // the algorithm is NOT enclosed in double quotes, so we can't use extractParam + auto paramBegin = authReq.indexOf("algorithm="); + if (paramBegin == -1) { return { true, "MD5" }; } // default as per RFC2617 + auto valueBegin = paramBegin + 10; + + String algo = authReq.substring(valueBegin, valueBegin + 3); + if (algo == "MD5") { return { true, algo }; } + + algo = authReq.substring(valueBegin, valueBegin + 7); + if (algo == "SHA-256") { return { true, algo }; } + + return { false, "unsupported digest algorithm" }; +} + +std::pair HttpGetter::getAuthDigest(String const& authReq, unsigned int counter) { // extracting required parameters for RFC 2617 Digest String realm = extractParam(authReq, "realm=\"", '"'); String nonce = extractParam(authReq, "nonce=\"", '"'); @@ -228,21 +266,19 @@ String HttpGetter::getAuthDigest(String const& authReq, unsigned int counter) { char nc[9]; snprintf(nc, sizeof(nc), "%08x", counter); - // sha256 of the user:realm:password - String ha1 = sha256(String(_config.Username) + ":" + realm + ":" + _config.Password); + auto algo = getAlgo(authReq); + if (!algo.first) { return { false, algo.second }; } - // sha256 of method:uri - String ha2 = sha256("GET:" + _uri); - - // sha256 of h1:nonce:nc:cNonce:auth:h2 - String response = sha256(ha1 + ":" + nonce + ":" + String(nc) + + auto hash = (algo.second == "SHA-256") ? &sha256 : &md5; + String ha1 = hash(String(_config.Username) + ":" + realm + ":" + _config.Password); + String ha2 = hash("GET:" + _uri); + String response = hash(ha1 + ":" + nonce + ":" + String(nc) + ":" + cNonce + ":" + "auth" + ":" + ha2); - // Final authorization String - return String("Digest username=\"") + _config.Username + + return { true, String("Digest username=\"") + _config.Username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" + _uri + "\", cnonce=\"" + cNonce + "\", nc=" + nc + - ", qop=auth, response=\"" + response + "\", algorithm=SHA-256"; + ", qop=auth, response=\"" + response + "\", algorithm=" + algo.second }; } void HttpGetter::addHeader(char const* key, char const* value)