Feature: HttpGetter: support MD5 digest authentication
the MD5 scheme should still be widely deployed, even though it is deprecated. it is also still the default if not specific algorithm is requested by the server.
This commit is contained in:
parent
63612e9276
commit
fd3b65f4bd
@ -69,7 +69,7 @@ public:
|
|||||||
char const* getErrorText() const { return _errBuffer; }
|
char const* getErrorText() const { return _errBuffer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String getAuthDigest(String const& authReq, unsigned int counter);
|
std::pair<bool, String> getAuthDigest(String const& authReq, unsigned int counter);
|
||||||
HttpRequestConfig const& _config;
|
HttpRequestConfig const& _config;
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#include "HttpGetter.h"
|
#include "HttpGetter.h"
|
||||||
#include <WiFiClientSecure.h>
|
#include <WiFiClientSecure.h>
|
||||||
#include "mbedtls/sha256.h"
|
#include "mbedtls/sha256.h"
|
||||||
|
#include "mbedtls/md5.h"
|
||||||
#include <base64.h>
|
#include <base64.h>
|
||||||
#include <ESPmDNS.h>
|
#include <ESPmDNS.h>
|
||||||
|
|
||||||
@ -155,8 +156,12 @@ HttpRequestResult HttpGetter::performGetRequest()
|
|||||||
return { false };
|
return { false };
|
||||||
}
|
}
|
||||||
String authReq = upTmpHttpClient->header("WWW-Authenticate");
|
String authReq = upTmpHttpClient->header("WWW-Authenticate");
|
||||||
String authorization = getAuthDigest(authReq, 1);
|
auto authorization = getAuthDigest(authReq, 1);
|
||||||
upTmpHttpClient->addHeader("Authorization", authorization);
|
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".
|
// use a new TCP connection if the server sent "Connection: close".
|
||||||
bool restart = true;
|
bool restart = true;
|
||||||
@ -183,6 +188,29 @@ HttpRequestResult HttpGetter::performGetRequest()
|
|||||||
return { true, std::move(upTmpHttpClient), _spWiFiClient };
|
return { true, std::move(upTmpHttpClient), _spWiFiClient };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<size_t binLen>
|
||||||
|
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<const unsigned char*>(data.c_str()), data.length());
|
||||||
|
mbedtls_md5_finish_ret(&ctx, hash);
|
||||||
|
mbedtls_md5_free(&ctx);
|
||||||
|
|
||||||
|
return bin2hex<sizeof(hash)>(hash);
|
||||||
|
}
|
||||||
|
|
||||||
static String sha256(const String& data) {
|
static String sha256(const String& data) {
|
||||||
uint8_t hash[32];
|
uint8_t hash[32];
|
||||||
|
|
||||||
@ -193,12 +221,7 @@ static String sha256(const String& data) {
|
|||||||
mbedtls_sha256_finish(&ctx, hash);
|
mbedtls_sha256_finish(&ctx, hash);
|
||||||
mbedtls_sha256_free(&ctx);
|
mbedtls_sha256_free(&ctx);
|
||||||
|
|
||||||
char res[sizeof(hash) * 2 + 1];
|
return bin2hex<sizeof(hash)>(hash);
|
||||||
for (int i = 0; i < sizeof(hash); i++) {
|
|
||||||
snprintf(res + (i*2), sizeof(res) - (i*2), "%02x", hash[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String extractParam(String const& authReq, String const& param, char delimiter) {
|
static String extractParam(String const& authReq, String const& param, char delimiter) {
|
||||||
@ -219,7 +242,22 @@ static String getcNonce(int len) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
String HttpGetter::getAuthDigest(String const& authReq, unsigned int counter) {
|
static std::pair<bool, String> 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<bool, String> HttpGetter::getAuthDigest(String const& authReq, unsigned int counter) {
|
||||||
// extracting required parameters for RFC 2617 Digest
|
// extracting required parameters for RFC 2617 Digest
|
||||||
String realm = extractParam(authReq, "realm=\"", '"');
|
String realm = extractParam(authReq, "realm=\"", '"');
|
||||||
String nonce = extractParam(authReq, "nonce=\"", '"');
|
String nonce = extractParam(authReq, "nonce=\"", '"');
|
||||||
@ -228,21 +266,19 @@ String HttpGetter::getAuthDigest(String const& authReq, unsigned int counter) {
|
|||||||
char nc[9];
|
char nc[9];
|
||||||
snprintf(nc, sizeof(nc), "%08x", counter);
|
snprintf(nc, sizeof(nc), "%08x", counter);
|
||||||
|
|
||||||
// sha256 of the user:realm:password
|
auto algo = getAlgo(authReq);
|
||||||
String ha1 = sha256(String(_config.Username) + ":" + realm + ":" + _config.Password);
|
if (!algo.first) { return { false, algo.second }; }
|
||||||
|
|
||||||
// sha256 of method:uri
|
auto hash = (algo.second == "SHA-256") ? &sha256 : &md5;
|
||||||
String ha2 = sha256("GET:" + _uri);
|
String ha1 = hash(String(_config.Username) + ":" + realm + ":" + _config.Password);
|
||||||
|
String ha2 = hash("GET:" + _uri);
|
||||||
// sha256 of h1:nonce:nc:cNonce:auth:h2
|
String response = hash(ha1 + ":" + nonce + ":" + String(nc) +
|
||||||
String response = sha256(ha1 + ":" + nonce + ":" + String(nc) +
|
|
||||||
":" + cNonce + ":" + "auth" + ":" + ha2);
|
":" + cNonce + ":" + "auth" + ":" + ha2);
|
||||||
|
|
||||||
// Final authorization String
|
return { true, String("Digest username=\"") + _config.Username +
|
||||||
return String("Digest username=\"") + _config.Username +
|
|
||||||
"\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" +
|
"\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" +
|
||||||
_uri + "\", cnonce=\"" + cNonce + "\", nc=" + nc +
|
_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)
|
void HttpGetter::addHeader(char const* key, char const* value)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user