diff options
| author | schererleander <leander@schererleander.de> | 2026-01-20 08:34:54 +0100 |
|---|---|---|
| committer | schererleander <leander@schererleander.de> | 2026-01-20 08:34:54 +0100 |
| commit | 85ea4e995a75abe061f6fc375ea0481084dddd43 (patch) | |
| tree | 7eb5d57653ecd8f041aeac4e68d7d554c1168681 /libraries/ESP_Async_WebServer/src/WebAuthentication.cpp | |
Diffstat (limited to 'libraries/ESP_Async_WebServer/src/WebAuthentication.cpp')
| -rw-r--r-- | libraries/ESP_Async_WebServer/src/WebAuthentication.cpp | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/libraries/ESP_Async_WebServer/src/WebAuthentication.cpp b/libraries/ESP_Async_WebServer/src/WebAuthentication.cpp new file mode 100644 index 0000000..7ed7814 --- /dev/null +++ b/libraries/ESP_Async_WebServer/src/WebAuthentication.cpp @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov + +#include "WebAuthentication.h" +#include <libb64/cencode.h> +#if defined(ESP32) || defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) +#include <MD5Builder.h> +#else +#include "md5.h" +#endif +#include "literals.h" + +using namespace asyncsrv; + +// Basic Auth hash = base64("username:password") + +bool checkBasicAuthentication(const char *hash, const char *username, const char *password) { + if (username == NULL || password == NULL || hash == NULL) { + return false; + } + return generateBasicHash(username, password).equalsIgnoreCase(hash); +} + +String generateBasicHash(const char *username, const char *password) { + if (username == NULL || password == NULL) { + return emptyString; + } + + size_t toencodeLen = strlen(username) + strlen(password) + 1; + + char *toencode = new char[toencodeLen + 1]; + if (toencode == NULL) { + return emptyString; + } + char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; + if (encoded == NULL) { + delete[] toencode; + return emptyString; + } + sprintf_P(toencode, PSTR("%s:%s"), username, password); + if (base64_encode_chars(toencode, toencodeLen, encoded) > 0) { + String res = String(encoded); + delete[] toencode; + delete[] encoded; + return res; + } + delete[] toencode; + delete[] encoded; + return emptyString; +} + +static bool getMD5(uint8_t *data, uint16_t len, char *output) { // 33 bytes or more +#if defined(ESP32) || defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) + MD5Builder md5; + md5.begin(); + md5.add(data, len); + md5.calculate(); + md5.getChars(output); +#else + md5_context_t _ctx; + + uint8_t *_buf = (uint8_t *)malloc(16); + if (_buf == NULL) { + return false; + } + memset(_buf, 0x00, 16); + + MD5Init(&_ctx); + MD5Update(&_ctx, data, len); + MD5Final(_buf, &_ctx); + + for (uint8_t i = 0; i < 16; i++) { + sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]); + } + + free(_buf); +#endif + return true; +} + +String genRandomMD5() { +#ifdef ESP8266 + uint32_t r = RANDOM_REG32; +#else + uint32_t r = rand(); +#endif + char *out = (char *)malloc(33); + if (out == NULL || !getMD5((uint8_t *)(&r), 4, out)) { +#ifdef ESP32 + log_e("Failed to allocate"); +#endif + return emptyString; + } + String res = String(out); + free(out); + return res; +} + +static String stringMD5(const String &in) { + char *out = (char *)malloc(33); + if (out == NULL || !getMD5((uint8_t *)(in.c_str()), in.length(), out)) { +#ifdef ESP32 + log_e("Failed to allocate"); +#endif + return emptyString; + } + String res = String(out); + free(out); + return res; +} + +String generateDigestHash(const char *username, const char *password, const char *realm) { + if (username == NULL || password == NULL || realm == NULL) { + return emptyString; + } + char *out = (char *)malloc(33); + if (out == NULL) { +#ifdef ESP32 + log_e("Failed to allocate"); +#endif + return emptyString; + } + + String in; + if (!in.reserve(strlen(username) + strlen(realm) + strlen(password) + 2)) { +#ifdef ESP32 + log_e("Failed to allocate"); +#endif + free(out); + return emptyString; + } + + in.concat(username); + in.concat(':'); + in.concat(realm); + in.concat(':'); + in.concat(password); + + if (!getMD5((uint8_t *)(in.c_str()), in.length(), out)) { +#ifdef ESP32 + log_e("Failed to allocate"); +#endif + free(out); + return emptyString; + } + + in = String(out); + free(out); + return in; +} + +bool checkDigestAuthentication( + const char *header, const char *method, const char *username, const char *password, const char *realm, bool passwordIsHash, const char *nonce, + const char *opaque, const char *uri +) { + if (username == NULL || password == NULL || header == NULL || method == NULL) { + // os_printf("AUTH FAIL: missing required fields\n"); + return false; + } + + String myHeader(header); + int nextBreak = myHeader.indexOf(','); + if (nextBreak < 0) { + // os_printf("AUTH FAIL: no variables\n"); + return false; + } + + String myUsername; + String myRealm; + String myNonce; + String myUri; + String myResponse; + String myQop; + String myNc; + String myCnonce; + + myHeader += (char)0x2c; // ',' + myHeader += (char)0x20; // ' ' + do { + String avLine(myHeader.substring(0, nextBreak)); + avLine.trim(); + myHeader = myHeader.substring(nextBreak + 1); + nextBreak = myHeader.indexOf(','); + + int eqSign = avLine.indexOf('='); + if (eqSign < 0) { + // os_printf("AUTH FAIL: no = sign\n"); + return false; + } + String varName(avLine.substring(0, eqSign)); + avLine = avLine.substring(eqSign + 1); + if (avLine.startsWith(String('"'))) { + avLine = avLine.substring(1, avLine.length() - 1); + } + + if (varName.equals(T_username)) { + if (!avLine.equals(username)) { + // os_printf("AUTH FAIL: username\n"); + return false; + } + myUsername = avLine; + } else if (varName.equals(T_realm)) { + if (realm != NULL && !avLine.equals(realm)) { + // os_printf("AUTH FAIL: realm\n"); + return false; + } + myRealm = avLine; + } else if (varName.equals(T_nonce)) { + if (nonce != NULL && !avLine.equals(nonce)) { + // os_printf("AUTH FAIL: nonce\n"); + return false; + } + myNonce = avLine; + } else if (varName.equals(T_opaque)) { + if (opaque != NULL && !avLine.equals(opaque)) { + // os_printf("AUTH FAIL: opaque\n"); + return false; + } + } else if (varName.equals(T_uri)) { + if (uri != NULL && !avLine.equals(uri)) { + // os_printf("AUTH FAIL: uri\n"); + return false; + } + myUri = avLine; + } else if (varName.equals(T_response)) { + myResponse = avLine; + } else if (varName.equals(T_qop)) { + myQop = avLine; + } else if (varName.equals(T_nc)) { + myNc = avLine; + } else if (varName.equals(T_cnonce)) { + myCnonce = avLine; + } + } while (nextBreak > 0); + + String ha1 = passwordIsHash ? password : stringMD5(myUsername + ':' + myRealm + ':' + password).c_str(); + String ha2 = stringMD5(String(method) + ':' + myUri); + String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + ha2; + + if (myResponse.equals(stringMD5(response))) { + // os_printf("AUTH SUCCESS\n"); + return true; + } + + // os_printf("AUTH FAIL: password\n"); + return false; +} |
