Web server simplifications and handers (#7429)
* First stab ad simplyfing webserver auth and adding a handler. * Tweaks after testing against docs and latest Library tree * Add documentatin for callback handler * Bodge to allow things to compile without the dependencies * Remove dependency on sodium to make it compile with 4.4 * Fix hex conversion * Move some common HEX functions into a static HEX class, remove those from MD5 and add some examples. This allows for the cleanup of various to/from HEX routines elsewhere. * Remove some duplicated code * Add simplfiied HEXBuilder under MD5Bulder to CMakefile. * Update for 3.0.0 and QoL improvements * Remove examples that depend on external libraries * Skip H2 testing * Formatting improvements * Move builders examples to Utilities folder * Fix indentation * Add HashBuilder abstract class * Add SHA1Builder * Fix comment * Fix whitespace * Fix crashes and improve log messages * Fix indentation for webserver --------- Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com> Co-authored-by: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>
This commit is contained in:
parent
a114af068b
commit
e581717bf3
21 changed files with 1417 additions and 146 deletions
|
|
@ -50,12 +50,14 @@ set(CORE_SRCS
|
|||
cores/esp32/Esp.cpp
|
||||
cores/esp32/FunctionalInterrupt.cpp
|
||||
cores/esp32/HardwareSerial.cpp
|
||||
cores/esp32/HEXBuilder.cpp
|
||||
cores/esp32/IPAddress.cpp
|
||||
cores/esp32/libb64/cdecode.c
|
||||
cores/esp32/libb64/cencode.c
|
||||
cores/esp32/main.cpp
|
||||
cores/esp32/MD5Builder.cpp
|
||||
cores/esp32/Print.cpp
|
||||
cores/esp32/SHA1Builder.cpp
|
||||
cores/esp32/stdlib_noniso.c
|
||||
cores/esp32/Stream.cpp
|
||||
cores/esp32/StreamString.cpp
|
||||
|
|
|
|||
71
cores/esp32/HEXBuilder.cpp
Normal file
71
cores/esp32/HEXBuilder.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp32 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HEXBuilder.h>
|
||||
|
||||
static uint8_t hex_char_to_byte(uint8_t c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) :
|
||||
(c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) :
|
||||
(c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0x10; // unknown char is 16
|
||||
}
|
||||
|
||||
size_t HEXBuilder::hex2bytes(unsigned char * out, size_t maxlen, String &in) {
|
||||
return hex2bytes(out, maxlen, in.c_str());
|
||||
}
|
||||
|
||||
size_t HEXBuilder::hex2bytes(unsigned char * out, size_t maxlen, const char * in) {
|
||||
size_t len = 0;
|
||||
for(;*in;in++) {
|
||||
uint8_t c = hex_char_to_byte(*in);
|
||||
// Silently skip anything unknown.
|
||||
if (c > 15)
|
||||
continue;
|
||||
|
||||
if (len & 1) {
|
||||
if (len/2 < maxlen)
|
||||
out[len/2] |= c;
|
||||
} else {
|
||||
if (len/2 < maxlen)
|
||||
out[len/2] = c<<4;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
return (len + 1)/2;
|
||||
}
|
||||
|
||||
size_t HEXBuilder::bytes2hex(char * out, size_t maxlen, const unsigned char * in, size_t len) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
if (i*2 + 1 < maxlen) {
|
||||
sprintf(out + (i * 2), "%02x", in[i]);
|
||||
}
|
||||
}
|
||||
return len * 2 + 1;
|
||||
}
|
||||
|
||||
String HEXBuilder::bytes2hex(const unsigned char * in, size_t len) {
|
||||
size_t maxlen = len * 2 + 1;
|
||||
char * out = (char *) malloc(maxlen);
|
||||
if (!out) return String();
|
||||
bytes2hex(out, maxlen, in, len);
|
||||
String ret = String(out);
|
||||
free(out);
|
||||
return ret;
|
||||
}
|
||||
34
cores/esp32/HEXBuilder.h
Normal file
34
cores/esp32/HEXBuilder.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp32 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef HEXBuilder_h
|
||||
#define HEXBuilder_h
|
||||
|
||||
#include <WString.h>
|
||||
#include <Stream.h>
|
||||
|
||||
class HEXBuilder {
|
||||
public:
|
||||
static size_t hex2bytes(unsigned char * out, size_t maxlen, String & in);
|
||||
static size_t hex2bytes(unsigned char * out, size_t maxlen, const char * in);
|
||||
|
||||
static String bytes2hex(const unsigned char * in, size_t len);
|
||||
static size_t bytes2hex(char * out, size_t maxlen, const unsigned char * in, size_t len);
|
||||
};
|
||||
#endif
|
||||
60
cores/esp32/HashBuilder.h
Normal file
60
cores/esp32/HashBuilder.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef HashBuilder_h
|
||||
#define HashBuilder_h
|
||||
|
||||
#include <WString.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#include "HEXBuilder.h"
|
||||
|
||||
class HashBuilder : public HEXBuilder
|
||||
{
|
||||
public:
|
||||
virtual ~HashBuilder() {}
|
||||
virtual void begin() = 0;
|
||||
|
||||
virtual void add(uint8_t* data, size_t len) = 0;
|
||||
virtual void add(const char* data)
|
||||
{
|
||||
add((uint8_t*)data, strlen(data));
|
||||
}
|
||||
virtual void add(char* data)
|
||||
{
|
||||
add((const char*)data);
|
||||
}
|
||||
virtual void add(String data)
|
||||
{
|
||||
add(data.c_str());
|
||||
}
|
||||
|
||||
virtual void addHexString(const char* data) = 0;
|
||||
virtual void addHexString(char* data)
|
||||
{
|
||||
addHexString((const char*)data);
|
||||
}
|
||||
virtual void addHexString(String data)
|
||||
{
|
||||
addHexString(data.c_str());
|
||||
}
|
||||
|
||||
virtual bool addStream(Stream& stream, const size_t maxLen) = 0;
|
||||
virtual void calculate() = 0;
|
||||
virtual void getBytes(uint8_t* output) = 0;
|
||||
virtual void getChars(char* output) = 0;
|
||||
virtual String toString() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -16,15 +16,10 @@
|
|||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <MD5Builder.h>
|
||||
|
||||
static uint8_t hex_char_to_byte(uint8_t c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) :
|
||||
(c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) :
|
||||
(c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0;
|
||||
}
|
||||
#include <Arduino.h>
|
||||
#include <HEXBuilder.h>
|
||||
#include <MD5Builder.h>
|
||||
|
||||
void MD5Builder::begin(void)
|
||||
{
|
||||
|
|
@ -32,23 +27,19 @@ void MD5Builder::begin(void)
|
|||
esp_rom_md5_init(&_ctx);
|
||||
}
|
||||
|
||||
void MD5Builder::add(uint8_t * data, uint16_t len)
|
||||
void MD5Builder::add(uint8_t * data, size_t len)
|
||||
{
|
||||
esp_rom_md5_update(&_ctx, data, len);
|
||||
}
|
||||
|
||||
void MD5Builder::addHexString(const char * data)
|
||||
{
|
||||
uint16_t i, len = strlen(data);
|
||||
size_t len = strlen(data);
|
||||
uint8_t * tmp = (uint8_t*)malloc(len/2);
|
||||
if(tmp == NULL) {
|
||||
return;
|
||||
}
|
||||
for(i=0; i<len; i+=2) {
|
||||
uint8_t high = hex_char_to_byte(data[i]);
|
||||
uint8_t low = hex_char_to_byte(data[i+1]);
|
||||
tmp[i/2] = (high & 0x0F) << 4 | (low & 0x0F);
|
||||
}
|
||||
hex2bytes(tmp, len/2, data);
|
||||
add(tmp, len/2);
|
||||
free(tmp);
|
||||
}
|
||||
|
|
@ -105,9 +96,7 @@ void MD5Builder::getBytes(uint8_t * output)
|
|||
|
||||
void MD5Builder::getChars(char * output)
|
||||
{
|
||||
for(uint8_t i = 0; i < ESP_ROM_MD5_DIGEST_LEN; i++) {
|
||||
sprintf(output + (i * 2), "%02x", _buf[i]);
|
||||
}
|
||||
bytes2hex(output, ESP_ROM_MD5_DIGEST_LEN*2+1, _buf, ESP_ROM_MD5_DIGEST_LEN);
|
||||
}
|
||||
|
||||
String MD5Builder::toString(void)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
/*
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
This file is part of the esp32 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
|
|
@ -15,9 +15,11 @@
|
|||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Modified 10 Jan 2024 by Lucas Saavedra Vaz (Use abstract class HashBuilder)
|
||||
*/
|
||||
#ifndef __ESP8266_MD5_BUILDER__
|
||||
#define __ESP8266_MD5_BUILDER__
|
||||
#ifndef MD5Builder_h
|
||||
#define MD5Builder_h
|
||||
|
||||
#include <WString.h>
|
||||
#include <Stream.h>
|
||||
|
|
@ -25,41 +27,27 @@
|
|||
#include "esp_system.h"
|
||||
#include "esp_rom_md5.h"
|
||||
|
||||
class MD5Builder
|
||||
#include "HashBuilder.h"
|
||||
|
||||
class MD5Builder : public HashBuilder
|
||||
{
|
||||
private:
|
||||
md5_context_t _ctx;
|
||||
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];
|
||||
public:
|
||||
void begin(void);
|
||||
void add(uint8_t * data, uint16_t len);
|
||||
void add(const char * data)
|
||||
{
|
||||
add((uint8_t*)data, strlen(data));
|
||||
}
|
||||
void add(char * data)
|
||||
{
|
||||
add((const char*)data);
|
||||
}
|
||||
void add(String data)
|
||||
{
|
||||
add(data.c_str());
|
||||
}
|
||||
void addHexString(const char * data);
|
||||
void addHexString(char * data)
|
||||
{
|
||||
addHexString((const char*)data);
|
||||
}
|
||||
void addHexString(String data)
|
||||
{
|
||||
addHexString(data.c_str());
|
||||
}
|
||||
bool addStream(Stream & stream, const size_t maxLen);
|
||||
void calculate(void);
|
||||
void getBytes(uint8_t * output);
|
||||
void getChars(char * output);
|
||||
String toString(void);
|
||||
void begin(void) override;
|
||||
|
||||
using HashBuilder::add;
|
||||
void add(uint8_t * data, size_t len) override;
|
||||
|
||||
using HashBuilder::addHexString;
|
||||
void addHexString(const char * data) override;
|
||||
|
||||
bool addStream(Stream & stream, const size_t maxLen) override;
|
||||
void calculate(void) override;
|
||||
void getBytes(uint8_t * output) override;
|
||||
void getChars(char * output) override;
|
||||
String toString(void) override;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
367
cores/esp32/SHA1Builder.cpp
Normal file
367
cores/esp32/SHA1Builder.cpp
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* FIPS-180-1 compliant SHA-1 implementation
|
||||
*
|
||||
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is part of mbed TLS (https://tls.mbed.org)
|
||||
* Modified for esp32 by Lucas Saavedra Vaz on 11 Jan 2024
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SHA1Builder.h>
|
||||
|
||||
// 32-bit integer manipulation macros (big endian)
|
||||
|
||||
#ifndef GET_UINT32_BE
|
||||
#define GET_UINT32_BE(n,b,i) \
|
||||
{ \
|
||||
(n) = ((uint32_t) (b)[(i) ] << 24) \
|
||||
| ((uint32_t) (b)[(i) + 1] << 16) \
|
||||
| ((uint32_t) (b)[(i) + 2] << 8) \
|
||||
| ((uint32_t) (b)[(i) + 3] ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef PUT_UINT32_BE
|
||||
#define PUT_UINT32_BE(n,b,i) \
|
||||
{ \
|
||||
(b)[(i) ] = (uint8_t) ((n) >> 24); \
|
||||
(b)[(i) + 1] = (uint8_t) ((n) >> 16); \
|
||||
(b)[(i) + 2] = (uint8_t) ((n) >> 8); \
|
||||
(b)[(i) + 3] = (uint8_t) ((n) ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
|
||||
static const uint8_t sha1_padding[64] =
|
||||
{
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
// Private methods
|
||||
|
||||
void SHA1Builder::process(const uint8_t* data)
|
||||
{
|
||||
uint32_t temp, W[16], A, B, C, D, E;
|
||||
|
||||
GET_UINT32_BE(W[ 0], data, 0);
|
||||
GET_UINT32_BE(W[ 1], data, 4);
|
||||
GET_UINT32_BE(W[ 2], data, 8);
|
||||
GET_UINT32_BE(W[ 3], data, 12);
|
||||
GET_UINT32_BE(W[ 4], data, 16);
|
||||
GET_UINT32_BE(W[ 5], data, 20);
|
||||
GET_UINT32_BE(W[ 6], data, 24);
|
||||
GET_UINT32_BE(W[ 7], data, 28);
|
||||
GET_UINT32_BE(W[ 8], data, 32);
|
||||
GET_UINT32_BE(W[ 9], data, 36);
|
||||
GET_UINT32_BE(W[10], data, 40);
|
||||
GET_UINT32_BE(W[11], data, 44);
|
||||
GET_UINT32_BE(W[12], data, 48);
|
||||
GET_UINT32_BE(W[13], data, 52);
|
||||
GET_UINT32_BE(W[14], data, 56);
|
||||
GET_UINT32_BE(W[15], data, 60);
|
||||
|
||||
#define sha1_S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
|
||||
|
||||
#define sha1_R(t) \
|
||||
( \
|
||||
temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \
|
||||
W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \
|
||||
(W[t & 0x0F] = sha1_S(temp,1)) \
|
||||
)
|
||||
|
||||
#define sha1_P(a,b,c,d,e,x) \
|
||||
{ \
|
||||
e += sha1_S(a,5) + sha1_F(b,c,d) + sha1_K + x; b = sha1_S(b,30); \
|
||||
}
|
||||
|
||||
A = state[0];
|
||||
B = state[1];
|
||||
C = state[2];
|
||||
D = state[3];
|
||||
E = state[4];
|
||||
|
||||
#define sha1_F(x,y,z) (z ^ (x & (y ^ z)))
|
||||
#define sha1_K 0x5A827999
|
||||
|
||||
sha1_P(A, B, C, D, E, W[0]);
|
||||
sha1_P(E, A, B, C, D, W[1]);
|
||||
sha1_P(D, E, A, B, C, W[2]);
|
||||
sha1_P(C, D, E, A, B, W[3]);
|
||||
sha1_P(B, C, D, E, A, W[4]);
|
||||
sha1_P(A, B, C, D, E, W[5]);
|
||||
sha1_P(E, A, B, C, D, W[6]);
|
||||
sha1_P(D, E, A, B, C, W[7]);
|
||||
sha1_P(C, D, E, A, B, W[8]);
|
||||
sha1_P(B, C, D, E, A, W[9]);
|
||||
sha1_P(A, B, C, D, E, W[10]);
|
||||
sha1_P(E, A, B, C, D, W[11]);
|
||||
sha1_P(D, E, A, B, C, W[12]);
|
||||
sha1_P(C, D, E, A, B, W[13]);
|
||||
sha1_P(B, C, D, E, A, W[14]);
|
||||
sha1_P(A, B, C, D, E, W[15]);
|
||||
sha1_P(E, A, B, C, D, sha1_R(16));
|
||||
sha1_P(D, E, A, B, C, sha1_R(17));
|
||||
sha1_P(C, D, E, A, B, sha1_R(18));
|
||||
sha1_P(B, C, D, E, A, sha1_R(19));
|
||||
|
||||
#undef sha1_K
|
||||
#undef sha1_F
|
||||
|
||||
#define sha1_F(x,y,z) (x ^ y ^ z)
|
||||
#define sha1_K 0x6ED9EBA1
|
||||
|
||||
sha1_P(A, B, C, D, E, sha1_R(20));
|
||||
sha1_P(E, A, B, C, D, sha1_R(21));
|
||||
sha1_P(D, E, A, B, C, sha1_R(22));
|
||||
sha1_P(C, D, E, A, B, sha1_R(23));
|
||||
sha1_P(B, C, D, E, A, sha1_R(24));
|
||||
sha1_P(A, B, C, D, E, sha1_R(25));
|
||||
sha1_P(E, A, B, C, D, sha1_R(26));
|
||||
sha1_P(D, E, A, B, C, sha1_R(27));
|
||||
sha1_P(C, D, E, A, B, sha1_R(28));
|
||||
sha1_P(B, C, D, E, A, sha1_R(29));
|
||||
sha1_P(A, B, C, D, E, sha1_R(30));
|
||||
sha1_P(E, A, B, C, D, sha1_R(31));
|
||||
sha1_P(D, E, A, B, C, sha1_R(32));
|
||||
sha1_P(C, D, E, A, B, sha1_R(33));
|
||||
sha1_P(B, C, D, E, A, sha1_R(34));
|
||||
sha1_P(A, B, C, D, E, sha1_R(35));
|
||||
sha1_P(E, A, B, C, D, sha1_R(36));
|
||||
sha1_P(D, E, A, B, C, sha1_R(37));
|
||||
sha1_P(C, D, E, A, B, sha1_R(38));
|
||||
sha1_P(B, C, D, E, A, sha1_R(39));
|
||||
|
||||
#undef sha1_K
|
||||
#undef sha1_F
|
||||
|
||||
#define sha1_F(x,y,z) ((x & y) | (z & (x | y)))
|
||||
#define sha1_K 0x8F1BBCDC
|
||||
|
||||
sha1_P(A, B, C, D, E, sha1_R(40));
|
||||
sha1_P(E, A, B, C, D, sha1_R(41));
|
||||
sha1_P(D, E, A, B, C, sha1_R(42));
|
||||
sha1_P(C, D, E, A, B, sha1_R(43));
|
||||
sha1_P(B, C, D, E, A, sha1_R(44));
|
||||
sha1_P(A, B, C, D, E, sha1_R(45));
|
||||
sha1_P(E, A, B, C, D, sha1_R(46));
|
||||
sha1_P(D, E, A, B, C, sha1_R(47));
|
||||
sha1_P(C, D, E, A, B, sha1_R(48));
|
||||
sha1_P(B, C, D, E, A, sha1_R(49));
|
||||
sha1_P(A, B, C, D, E, sha1_R(50));
|
||||
sha1_P(E, A, B, C, D, sha1_R(51));
|
||||
sha1_P(D, E, A, B, C, sha1_R(52));
|
||||
sha1_P(C, D, E, A, B, sha1_R(53));
|
||||
sha1_P(B, C, D, E, A, sha1_R(54));
|
||||
sha1_P(A, B, C, D, E, sha1_R(55));
|
||||
sha1_P(E, A, B, C, D, sha1_R(56));
|
||||
sha1_P(D, E, A, B, C, sha1_R(57));
|
||||
sha1_P(C, D, E, A, B, sha1_R(58));
|
||||
sha1_P(B, C, D, E, A, sha1_R(59));
|
||||
|
||||
#undef sha1_K
|
||||
#undef sha1_F
|
||||
|
||||
#define sha1_F(x,y,z) (x ^ y ^ z)
|
||||
#define sha1_K 0xCA62C1D6
|
||||
|
||||
sha1_P(A, B, C, D, E, sha1_R(60));
|
||||
sha1_P(E, A, B, C, D, sha1_R(61));
|
||||
sha1_P(D, E, A, B, C, sha1_R(62));
|
||||
sha1_P(C, D, E, A, B, sha1_R(63));
|
||||
sha1_P(B, C, D, E, A, sha1_R(64));
|
||||
sha1_P(A, B, C, D, E, sha1_R(65));
|
||||
sha1_P(E, A, B, C, D, sha1_R(66));
|
||||
sha1_P(D, E, A, B, C, sha1_R(67));
|
||||
sha1_P(C, D, E, A, B, sha1_R(68));
|
||||
sha1_P(B, C, D, E, A, sha1_R(69));
|
||||
sha1_P(A, B, C, D, E, sha1_R(70));
|
||||
sha1_P(E, A, B, C, D, sha1_R(71));
|
||||
sha1_P(D, E, A, B, C, sha1_R(72));
|
||||
sha1_P(C, D, E, A, B, sha1_R(73));
|
||||
sha1_P(B, C, D, E, A, sha1_R(74));
|
||||
sha1_P(A, B, C, D, E, sha1_R(75));
|
||||
sha1_P(E, A, B, C, D, sha1_R(76));
|
||||
sha1_P(D, E, A, B, C, sha1_R(77));
|
||||
sha1_P(C, D, E, A, B, sha1_R(78));
|
||||
sha1_P(B, C, D, E, A, sha1_R(79));
|
||||
|
||||
#undef sha1_K
|
||||
#undef sha1_F
|
||||
|
||||
state[0] += A;
|
||||
state[1] += B;
|
||||
state[2] += C;
|
||||
state[3] += D;
|
||||
state[4] += E;
|
||||
}
|
||||
|
||||
// Public methods
|
||||
|
||||
void SHA1Builder::begin(void)
|
||||
{
|
||||
total[0] = 0;
|
||||
total[1] = 0;
|
||||
|
||||
state[0] = 0x67452301;
|
||||
state[1] = 0xEFCDAB89;
|
||||
state[2] = 0x98BADCFE;
|
||||
state[3] = 0x10325476;
|
||||
state[4] = 0xC3D2E1F0;
|
||||
|
||||
memset(buffer, 0x00, sizeof(buffer));
|
||||
memset(hash, 0x00, sizeof(hash));
|
||||
}
|
||||
|
||||
void SHA1Builder::add(uint8_t* data, size_t len)
|
||||
{
|
||||
size_t fill;
|
||||
uint32_t left;
|
||||
|
||||
if(len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
left = total[0] & 0x3F;
|
||||
fill = 64 - left;
|
||||
|
||||
total[0] += (uint32_t) len;
|
||||
total[0] &= 0xFFFFFFFF;
|
||||
|
||||
if(total[0] < (uint32_t) len)
|
||||
{
|
||||
total[1]++;
|
||||
}
|
||||
|
||||
if(left && len >= fill)
|
||||
{
|
||||
memcpy((void *) (buffer + left), data, fill);
|
||||
process(buffer);
|
||||
data += fill;
|
||||
len -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
while(len >= 64)
|
||||
{
|
||||
process(data);
|
||||
data += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
if(len > 0) {
|
||||
memcpy((void *) (buffer + left), data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void SHA1Builder::addHexString(const char * data)
|
||||
{
|
||||
uint16_t len = strlen(data);
|
||||
uint8_t * tmp = (uint8_t*)malloc(len/2);
|
||||
if(tmp == NULL) {
|
||||
return;
|
||||
}
|
||||
hex2bytes(tmp, len/2, data);
|
||||
add(tmp, len/2);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
bool SHA1Builder::addStream(Stream & stream, const size_t maxLen)
|
||||
{
|
||||
const int buf_size = 512;
|
||||
int maxLengthLeft = maxLen;
|
||||
uint8_t * buf = (uint8_t*) malloc(buf_size);
|
||||
|
||||
if(!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int bytesAvailable = stream.available();
|
||||
while((bytesAvailable > 0) && (maxLengthLeft > 0)) {
|
||||
|
||||
// determine number of bytes to read
|
||||
int readBytes = bytesAvailable;
|
||||
if(readBytes > maxLengthLeft) {
|
||||
readBytes = maxLengthLeft ; // read only until max_len
|
||||
}
|
||||
if(readBytes > buf_size) {
|
||||
readBytes = buf_size; // not read more the buffer can handle
|
||||
}
|
||||
|
||||
// read data and check if we got something
|
||||
int numBytesRead = stream.readBytes(buf, readBytes);
|
||||
if(numBytesRead< 1) {
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update SHA1 with buffer payload
|
||||
add(buf, numBytesRead);
|
||||
|
||||
// update available number of bytes
|
||||
maxLengthLeft -= numBytesRead;
|
||||
bytesAvailable = stream.available();
|
||||
}
|
||||
free(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SHA1Builder::calculate(void)
|
||||
{
|
||||
uint32_t last, padn;
|
||||
uint32_t high, low;
|
||||
uint8_t msglen[8];
|
||||
|
||||
high = (total[0] >> 29) | (total[1] << 3);
|
||||
low = (total[0] << 3);
|
||||
|
||||
PUT_UINT32_BE(high, msglen, 0);
|
||||
PUT_UINT32_BE(low, msglen, 4);
|
||||
|
||||
last = total[0] & 0x3F;
|
||||
padn = (last < 56) ? (56 - last) : (120 - last);
|
||||
|
||||
add((uint8_t*)sha1_padding, padn);
|
||||
add(msglen, 8);
|
||||
|
||||
PUT_UINT32_BE(state[0], hash, 0);
|
||||
PUT_UINT32_BE(state[1], hash, 4);
|
||||
PUT_UINT32_BE(state[2], hash, 8);
|
||||
PUT_UINT32_BE(state[3], hash, 12);
|
||||
PUT_UINT32_BE(state[4], hash, 16);
|
||||
}
|
||||
|
||||
void SHA1Builder::getBytes(uint8_t * output)
|
||||
{
|
||||
memcpy(output, hash, SHA1_HASH_SIZE);
|
||||
}
|
||||
|
||||
void SHA1Builder::getChars(char * output)
|
||||
{
|
||||
bytes2hex(output, SHA1_HASH_SIZE*2+1, hash, SHA1_HASH_SIZE);
|
||||
}
|
||||
|
||||
String SHA1Builder::toString(void)
|
||||
{
|
||||
char out[(SHA1_HASH_SIZE * 2) + 1];
|
||||
getChars(out);
|
||||
return String(out);
|
||||
}
|
||||
51
cores/esp32/SHA1Builder.h
Normal file
51
cores/esp32/SHA1Builder.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SHA1Builder_h
|
||||
#define SHA1Builder_h
|
||||
|
||||
#include <WString.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#include "HashBuilder.h"
|
||||
|
||||
#define SHA1_HASH_SIZE 20
|
||||
|
||||
class SHA1Builder : public HashBuilder
|
||||
{
|
||||
private:
|
||||
uint32_t total[2]; /* number of bytes processed */
|
||||
uint32_t state[5]; /* intermediate digest state */
|
||||
unsigned char buffer[64]; /* data block being processed */
|
||||
uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */
|
||||
|
||||
void process(const uint8_t* data);
|
||||
|
||||
public:
|
||||
void begin() override;
|
||||
|
||||
using HashBuilder::add;
|
||||
void add(uint8_t* data, size_t len) override;
|
||||
|
||||
using HashBuilder::addHexString;
|
||||
void addHexString(const char* data) override;
|
||||
|
||||
bool addStream(Stream& stream, const size_t maxLen) override;
|
||||
void calculate() override;
|
||||
void getBytes(uint8_t* output) override;
|
||||
void getChars(char* output) override;
|
||||
String toString() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
71
libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino
Normal file
71
libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#include <HEXBuilder.h>
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial) { delay(10); }
|
||||
Serial.println("\n\n\nStart.");
|
||||
|
||||
// Convert a HEX string like 6c6c6f20576f726c64 to a binary buffer
|
||||
{
|
||||
const char * out = "Hello World";
|
||||
const char * hexin = "48656c6c6f20576f726c6400"; // As the string above is \0 terminated too
|
||||
|
||||
unsigned char buff[256];
|
||||
size_t len = HEXBuilder::hex2bytes(buff, sizeof(buff), hexin);
|
||||
|
||||
if (len != 1 + strlen(out))
|
||||
Serial.println("Odd - length 1 is wrong");
|
||||
|
||||
if (memcmp(buff, out, len) != 0)
|
||||
Serial.println("Odd - decode 1 went wrong");
|
||||
|
||||
// Safe to print this binary buffer -- as we've included a \0 in the hex sequence.
|
||||
Serial.printf("IN: <%s>\nOUT <%s\\0>\n", hexin, buff);
|
||||
};
|
||||
|
||||
{
|
||||
String helloHEX = "48656c6c6f20576f726c64";
|
||||
const char hello[] = "Hello World";
|
||||
|
||||
unsigned char buff[256];
|
||||
size_t len = HEXBuilder::hex2bytes(buff, sizeof(buff), helloHEX);
|
||||
|
||||
if (len != strlen(hello))
|
||||
Serial.println("Odd - length 2 is wrong");
|
||||
|
||||
if (strcmp((char *) buff, hello) != 0)
|
||||
Serial.println("Odd - decode 2 went wrong");
|
||||
}
|
||||
|
||||
{
|
||||
const unsigned char helloBytes[] = { 0x48, 0x56, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
|
||||
String helloHEX = "48566c6c6f20576f726c64";
|
||||
|
||||
|
||||
String out = HEXBuilder::bytes2hex(helloBytes, sizeof(helloBytes));
|
||||
if (out.length() != 2 * sizeof(helloBytes))
|
||||
Serial.println("Odd - length 3 is wrong");
|
||||
|
||||
// we need to ignore case - as a hex string can be spelled in uppercase and lowercase
|
||||
if (!out.equalsIgnoreCase(helloHEX)) {
|
||||
Serial.println("Odd - decode 3 went wrong");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const unsigned char helloBytes[] = { 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
|
||||
const char helloHex[] = "6c6c6f20576f726c64";
|
||||
|
||||
char buff[256];
|
||||
size_t len = HEXBuilder::bytes2hex(buff, sizeof(buff), helloBytes, sizeof(helloBytes));
|
||||
if (len != 1 + 2 * sizeof(helloBytes))
|
||||
Serial.println("Odd - length 4 is wrong");
|
||||
|
||||
// we need to ignore case - as a hex string can be spelled in uppercase and lowercase
|
||||
if (strcasecmp(buff, helloHex))
|
||||
Serial.println("Odd - decode 4 went wrong");
|
||||
}
|
||||
Serial.println("Done.");
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
96
libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino
Normal file
96
libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include <MD5Builder.h>
|
||||
|
||||
// Occasionally it is useful to compare a password that the user
|
||||
// has entered to a build in string. However this means that the
|
||||
// password ends up `in the clear' in the firmware and in your
|
||||
// source code.
|
||||
//
|
||||
// MD5Builder helps you obfuscate this (it is not terribly secure, MD5
|
||||
// has been phased out as insecure eons ago) by letting you create an
|
||||
// MD5 of the data the user entered; and then compare this to an MD5
|
||||
// string that you have put in your code.
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial) { delay(10); }
|
||||
Serial.println("\n\n\nStart.");
|
||||
|
||||
// Check if a password obfuscated in an MD5 actually
|
||||
// matches the original string.
|
||||
//
|
||||
// echo -n "Hello World" | openssl md5
|
||||
{
|
||||
String md5 = "b10a8db164e0754105b7a99be72e3fe5";
|
||||
String password = "Hello World";
|
||||
|
||||
MD5Builder md;
|
||||
|
||||
md.begin();
|
||||
md.add(password);
|
||||
md.calculate();
|
||||
|
||||
String result = md.toString();
|
||||
|
||||
if (!md5.equalsIgnoreCase(result))
|
||||
Serial.println("Odd - failing MD5 on String");
|
||||
else
|
||||
Serial.println("OK!");
|
||||
}
|
||||
|
||||
// Check that this also work if we add the password not as
|
||||
// a normal string - but as a string with the HEX values.
|
||||
{
|
||||
String passwordAsHex = "48656c6c6f20576f726c64";
|
||||
String md5 = "b10a8db164e0754105b7a99be72e3fe5";
|
||||
|
||||
MD5Builder md;
|
||||
|
||||
md.begin();
|
||||
md.addHexString(passwordAsHex);
|
||||
md.calculate();
|
||||
|
||||
String result = md.toString();
|
||||
|
||||
if (!md5.equalsIgnoreCase(result)) {
|
||||
Serial.println("Odd - failing MD5 on hex string");
|
||||
Serial.println(md5);
|
||||
Serial.println(result);
|
||||
}
|
||||
else
|
||||
Serial.println("OK!");
|
||||
|
||||
}
|
||||
|
||||
// Check that this also work if we add the password as
|
||||
// an unsigned byte array.
|
||||
{
|
||||
uint8_t password[] = { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
|
||||
String md5 = "b10a8db164e0754105b7a99be72e3fe5";
|
||||
MD5Builder md;
|
||||
|
||||
md.begin();
|
||||
md.add(password, sizeof(password));
|
||||
md.calculate();
|
||||
|
||||
String result = md.toString();
|
||||
|
||||
if (!md5.equalsIgnoreCase(result))
|
||||
Serial.println("Odd - failing MD5 on byte array");
|
||||
else
|
||||
Serial.println("OK!");
|
||||
|
||||
// And also check that we can compare this as pure, raw, bytes
|
||||
uint8_t raw[16] = { 0xb1, 0x0a, 0x8d, 0xb1, 0x64, 0xe0, 0x75, 0x41,
|
||||
0x05, 0xb7, 0xa9, 0x9b, 0xe7, 0x2e, 0x3f, 0xe5
|
||||
};
|
||||
uint8_t res[16];
|
||||
md.getBytes(res);
|
||||
if (memcmp(raw, res, 16))
|
||||
Serial.println("Odd - failing MD5 on byte array when compared as bytes");
|
||||
else
|
||||
Serial.println("OK!");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
#include <SHA1Builder.h>
|
||||
|
||||
// Occasionally it is useful to compare a password that the user
|
||||
// has entered to a build in string. However this means that the
|
||||
// password ends up `in the clear' in the firmware and in your
|
||||
// source code.
|
||||
//
|
||||
// SHA1Builder helps you obfuscate this (This is not much more secure.
|
||||
// SHA1 is past its retirement age and long obsolte/insecure, but it helps
|
||||
// a little) by letting you create an (unsalted!) SHA1 of the data the
|
||||
// user entered; and then compare this to an SHA1 string that you have put
|
||||
// in your code.
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial) { delay(10); }
|
||||
Serial.println("\n\n\nStart.");
|
||||
|
||||
// Check if a password obfuscated in an SHA1 actually
|
||||
// matches the original string.
|
||||
//
|
||||
// echo -n "Hello World" | openssl sha1
|
||||
{
|
||||
String sha1_str = "0a4d55a8d778e5022fab701977c5d840bbc486d0";
|
||||
String password = "Hello World";
|
||||
|
||||
SHA1Builder sha;
|
||||
|
||||
sha.begin();
|
||||
sha.add(password);
|
||||
sha.calculate();
|
||||
|
||||
String result = sha.toString();
|
||||
|
||||
if (!sha1_str.equalsIgnoreCase(result))
|
||||
Serial.println("Odd - failing SHA1 on String");
|
||||
else
|
||||
Serial.println("OK!");
|
||||
}
|
||||
|
||||
// Check that this also work if we add the password not as
|
||||
// a normal string - but as a string with the HEX values.
|
||||
{
|
||||
String passwordAsHex = "48656c6c6f20576f726c64";
|
||||
String sha1_str = "0a4d55a8d778e5022fab701977c5d840bbc486d0";
|
||||
|
||||
SHA1Builder sha;
|
||||
|
||||
sha.begin();
|
||||
sha.addHexString(passwordAsHex);
|
||||
sha.calculate();
|
||||
|
||||
String result = sha.toString();
|
||||
|
||||
if (!sha1_str.equalsIgnoreCase(result)) {
|
||||
Serial.println("Odd - failing SHA1 on hex string");
|
||||
Serial.println(sha1_str);
|
||||
Serial.println(result);
|
||||
}
|
||||
else
|
||||
Serial.println("OK!");
|
||||
|
||||
}
|
||||
|
||||
// Check that this also work if we add the password as
|
||||
// an unsigned byte array.
|
||||
{
|
||||
uint8_t password[] = { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
|
||||
String sha1_str = "0a4d55a8d778e5022fab701977c5d840bbc486d0";
|
||||
SHA1Builder sha;
|
||||
|
||||
sha.begin();
|
||||
sha.add(password, sizeof(password));
|
||||
sha.calculate();
|
||||
|
||||
String result = sha.toString();
|
||||
|
||||
if (!sha1_str.equalsIgnoreCase(result))
|
||||
Serial.println("Odd - failing SHA1 on byte array");
|
||||
else
|
||||
Serial.println("OK!");
|
||||
|
||||
// And also check that we can compare this as pure, raw, bytes
|
||||
uint8_t raw[20] = { 0x0a, 0x4d, 0x55, 0xa8, 0xd7, 0x78, 0xe5, 0x02, 0x2f, 0xab,
|
||||
0x70, 0x19, 0x77, 0xc5, 0xd8, 0x40, 0xbb, 0xc4, 0x86, 0xd0
|
||||
};
|
||||
uint8_t res[20];
|
||||
sha.getBytes(res);
|
||||
if (memcmp(raw, res, 20))
|
||||
Serial.println("Odd - failing SHA1 on byte array when compared as bytes");
|
||||
else
|
||||
Serial.println("OK!");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#include <WiFi.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
const char* ssid = "........";
|
||||
const char* password = "........";
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
typedef struct credentials_t {
|
||||
String username;
|
||||
String password;
|
||||
} credentials_t;
|
||||
|
||||
credentials_t passwdfile[] = {
|
||||
{ "admin", "esp32" },
|
||||
{ "fred", "41234123" },
|
||||
{ "charlie", "sdfsd" },
|
||||
{ "alice", "vambdnkuhj" },
|
||||
{ "bob", "svcdbjhws12" },
|
||||
};
|
||||
const size_t N_CREDENTIALS = sizeof(passwdfile) / sizeof(credentials_t);
|
||||
|
||||
String * credentialsHandler(HTTPAuthMethod mode, String username, String params[])
|
||||
{
|
||||
for (int i = 0; i < N_CREDENTIALS; i++) {
|
||||
if (username == passwdfile[i].username)
|
||||
return new String(passwdfile[i].password);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial) { delay(10); }
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("WiFi Connect Failed! Rebooting...");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
ArduinoOTA.begin();
|
||||
|
||||
server.on("/", []() {
|
||||
if (!server.authenticate(&credentialsHandler)) {
|
||||
server.requestAuthentication();
|
||||
return;
|
||||
}
|
||||
server.send(200, "text/plain", "Login OK");
|
||||
});
|
||||
server.begin();
|
||||
|
||||
Serial.print("Open http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("/ in your browser to see it working");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
server.handleClient();
|
||||
delay(2);//allow the cpu to switch to other tasks
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#include <WiFi.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
const char* ssid = "........";
|
||||
const char* password = "........";
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
typedef struct credentials_t {
|
||||
char * username;
|
||||
char * password;
|
||||
} credentials_t;
|
||||
|
||||
credentials_t passwdfile[] = {
|
||||
{ "admin", "esp32" },
|
||||
{ "fred", "41234123" },
|
||||
{ "charlie", "sdfsd" },
|
||||
{ "alice", "vambdnkuhj" },
|
||||
{ "bob", "svcdbjhws12" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial) { delay(10); }
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("WiFi Connect Failed! Rebooting...");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
ArduinoOTA.begin();
|
||||
|
||||
server.on("/", []() {
|
||||
if (!server.authenticate([](HTTPAuthMethod mode, String username, String params[]) -> String * {
|
||||
// Scan the password list for the username and return the password if
|
||||
// we find the username.
|
||||
//
|
||||
for (credentials_t * entry = passwdfile; entry->username; entry++) {
|
||||
if (username == entry->username) {
|
||||
return new String(entry->password);
|
||||
};
|
||||
};
|
||||
// we've not found the user in the list.
|
||||
return NULL;
|
||||
}))
|
||||
{
|
||||
server.requestAuthentication();
|
||||
return;
|
||||
}
|
||||
server.send(200, "text/plain", "Login OK");
|
||||
});
|
||||
server.begin();
|
||||
|
||||
Serial.print("Open http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("/ in your browser to see it working");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
server.handleClient();
|
||||
delay(2);//allow the cpu to switch to other tasks
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#include <WiFi.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
// Rather than specify the password as plaintext; we
|
||||
// provide it as an (unsalted!) SHA1 hash. This is not
|
||||
// much more secure (SHA1 is past its retirement age,
|
||||
// and long obsolte/insecure) - but it helps a little.
|
||||
|
||||
const char* ssid = "........";
|
||||
const char* password = "........";
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
// Passwords as plaintext - human readable and easily visible in
|
||||
// the sourcecode and in the firmware/binary.
|
||||
const char* www_username = "admin";
|
||||
const char* www_password = "esp32";
|
||||
|
||||
// The sha1 of 'esp32' (without the trailing \0) expressed as 20
|
||||
// bytes of hex. Created by for example 'echo -n esp32 | openssl sha1'
|
||||
// or http://www.sha1-online.com.
|
||||
const char* www_username_hex = "hexadmin";
|
||||
const char* www_password_hex = "8cb124f8c277c16ec0b2ee00569fd151a08e342b";
|
||||
|
||||
// The same; but now expressed as a base64 string (e.g. as commonly used
|
||||
// by webservers). Created by ` echo -n esp32 | openssl sha1 -binary | openssl base64`
|
||||
const char* www_username_base64 = "base64admin";
|
||||
const char* www_password_base64 = "jLEk+MJ3wW7Asu4AVp/RUaCONCs=";
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial) { delay(10); }
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("WiFi Connect Failed! Rebooting...");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
ArduinoOTA.begin();
|
||||
|
||||
server.on("/", []() {
|
||||
if (server.authenticate(www_username, www_password)) {
|
||||
server.send(200, "text/plain", "Login against cleartext password OK");
|
||||
return;
|
||||
}
|
||||
if (server.authenticateBasicSHA1(www_username_hex, www_password_hex)) {
|
||||
server.send(200, "text/plain", "Login against HEX of the SHA1 of the password OK");
|
||||
return;
|
||||
}
|
||||
if (server.authenticateBasicSHA1(www_username_base64, www_password_base64)) {
|
||||
server.send(200, "text/plain", "Login against Base64 of the SHA1 of the password OK");
|
||||
return;
|
||||
}
|
||||
Serial.println("No/failed authentication");
|
||||
return server.requestAuthentication();
|
||||
});
|
||||
|
||||
server.begin();
|
||||
|
||||
Serial.print("Open http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("/ in your browser to see it working");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
server.handleClient();
|
||||
delay(2);//allow the cpu to switch to other tasks
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
#include <WiFi.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WebServer.h>
|
||||
#include <SHA1Builder.h>
|
||||
|
||||
// We have two options - we either come in with a bearer
|
||||
// token - i.e. a special header or API token; or we
|
||||
// get a normal HTTP style basic auth prompt.
|
||||
//
|
||||
// To do a bearer fetch - use something like Swagger or with curl:
|
||||
//
|
||||
// curl https://myesp.com/ -H "Authorization: Bearer SecritToken"
|
||||
//
|
||||
// We avoid hardcoding this "SecritToken" into the code by
|
||||
// using a SHA1 instead (which is not paricularly secure).
|
||||
|
||||
// Create the secret token SHA1 with:
|
||||
// echo -n SecritToken | openssl sha1
|
||||
|
||||
String secret_token_hex = "d2cce6b472959484a21c3194080c609b8a2c910b";
|
||||
|
||||
// Wifi credentials
|
||||
|
||||
const char* ssid = "........";
|
||||
const char* password = "........";
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
// Rather than specify the admin password as plaintext; we
|
||||
// provide it as an (unsalted!) SHA1 hash. This is not
|
||||
// much more secure (SHA1 is past its retirement age,
|
||||
// and long obsolte/insecure) - but it helps a little.
|
||||
|
||||
// The sha1 of 'esp32' (without the trailing \0) expressed as 20
|
||||
// bytes of hex. Created by for example 'echo -n esp32 | openssl sha1'
|
||||
// or http://www.sha1-online.com.
|
||||
const char* www_username_hex = "admin";
|
||||
const char* www_password_hex = "8cb124f8c277c16ec0b2ee00569fd151a08e342b";
|
||||
|
||||
static unsigned char _bearer[20];
|
||||
|
||||
String* check_bearer_or_auth(HTTPAuthMethod mode, String authReq, String params[]) {
|
||||
// we expect authReq to be "bearer some-secret"
|
||||
String lcAuthReq = authReq;
|
||||
lcAuthReq.toLowerCase();
|
||||
if (mode == OTHER_AUTH && (lcAuthReq.startsWith("bearer "))) {
|
||||
String secret = authReq.substring(7);
|
||||
secret.trim();
|
||||
|
||||
uint8_t sha1[20];
|
||||
SHA1Builder sha_builder;
|
||||
|
||||
sha_builder.begin();
|
||||
sha_builder.add((uint8_t*) secret.c_str(), secret.length());
|
||||
sha_builder.calculate();
|
||||
sha_builder.getBytes(sha1);
|
||||
|
||||
if (memcmp(_bearer, sha1, sizeof(_bearer)) == 0) {
|
||||
Serial.println("Bearer token matches");
|
||||
return new String("anything non null");
|
||||
} else {
|
||||
Serial.println("Bearer token does not match");
|
||||
}
|
||||
} else if (mode == BASIC_AUTH) {
|
||||
bool ret = server.authenticateBasicSHA1(www_username_hex, www_password_hex);
|
||||
if (ret) {
|
||||
Serial.println("Basic auth succeeded");
|
||||
return new String(params[0]);
|
||||
} else {
|
||||
Serial.println("Basic auth failed");
|
||||
}
|
||||
}
|
||||
|
||||
// No auth found
|
||||
return NULL;
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial) { delay(10); }
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("WiFi Connect Failed! Rebooting...");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
ArduinoOTA.begin();
|
||||
|
||||
// Convert token to a convenient binary representation.
|
||||
size_t len = HEXBuilder::hex2bytes(_bearer, sizeof(_bearer), secret_token_hex);
|
||||
if (len != 20)
|
||||
Serial.println("Bearer token does not look like a valid SHA1 hex string ?!");
|
||||
|
||||
server.on("/", []() {
|
||||
if (!server.authenticate(&check_bearer_or_auth)) {
|
||||
Serial.println("No/failed authentication");
|
||||
return server.requestAuthentication();
|
||||
}
|
||||
Serial.println("Authentication succeeded");
|
||||
server.send(200, "text/plain", "Login OK");
|
||||
return;
|
||||
});
|
||||
|
||||
server.begin();
|
||||
|
||||
Serial.print("Open http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("/ in your browser to see it working");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
server.handleClient();
|
||||
delay(2);//allow the cpu to switch to other tasks
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
#include <esp32-hal-log.h>
|
||||
#include <libb64/cdecode.h>
|
||||
#include <libb64/cencode.h>
|
||||
#include "esp_random.h"
|
||||
#include "WiFiServer.h"
|
||||
|
|
@ -31,7 +32,8 @@
|
|||
#include "FS.h"
|
||||
#include "detail/RequestHandlersImpl.h"
|
||||
#include "MD5Builder.h"
|
||||
|
||||
#include "SHA1Builder.h"
|
||||
#include "base64.h"
|
||||
|
||||
static const char AUTHORIZATION_HEADER[] = "Authorization";
|
||||
static const char qop_auth[] PROGMEM = "qop=auth";
|
||||
|
|
@ -40,7 +42,6 @@ static const char WWW_Authenticate[] = "WWW-Authenticate";
|
|||
static const char Content_Length[] = "Content-Length";
|
||||
static const char ETAG_HEADER[] = "If-None-Match";
|
||||
|
||||
|
||||
WebServer::WebServer(IPAddress addr, int port)
|
||||
: _corsEnabled(false)
|
||||
, _server(addr, port)
|
||||
|
|
@ -128,91 +129,185 @@ static String md5str(String &in){
|
|||
return md5.toString();
|
||||
}
|
||||
|
||||
bool WebServer::authenticate(const char * username, const char * password){
|
||||
if(hasHeader(FPSTR(AUTHORIZATION_HEADER))) {
|
||||
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
|
||||
if(authReq.startsWith(F("Basic"))){
|
||||
authReq = authReq.substring(6);
|
||||
authReq.trim();
|
||||
char toencodeLen = strlen(username)+strlen(password)+1;
|
||||
char *toencode = (char *)malloc(toencodeLen + 1);
|
||||
if(toencode == NULL){
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
char *encoded = (char *)malloc(base64_encode_expected_len(toencodeLen)+1);
|
||||
if(encoded == NULL){
|
||||
authReq = "";
|
||||
free(toencode);
|
||||
return false;
|
||||
}
|
||||
sprintf(toencode, "%s:%s", username, password);
|
||||
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) {
|
||||
authReq = "";
|
||||
free(toencode);
|
||||
free(encoded);
|
||||
return true;
|
||||
}
|
||||
free(toencode);
|
||||
free(encoded);;
|
||||
} else if(authReq.startsWith(F("Digest"))) {
|
||||
authReq = authReq.substring(7);
|
||||
log_v("%s", authReq.c_str());
|
||||
String _username = _extractParam(authReq,F("username=\""),'\"');
|
||||
if(!_username.length() || _username != String(username)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
String _realm = _extractParam(authReq, F("realm=\""),'\"');
|
||||
String _nonce = _extractParam(authReq, F("nonce=\""),'\"');
|
||||
String _uri = _extractParam(authReq, F("uri=\""),'\"');
|
||||
String _response = _extractParam(authReq, F("response=\""),'\"');
|
||||
String _opaque = _extractParam(authReq, F("opaque=\""),'\"');
|
||||
bool WebServer::authenticateBasicSHA1(const char * _username, const char * _sha1Base64orHex) {
|
||||
return WebServer::authenticate([_username,_sha1Base64orHex](HTTPAuthMethod mode, String username, String params[])->String * {
|
||||
// rather than work on a password to compare with; we take the sha1 of the
|
||||
// password received over the wire and compare that to the base64 encoded
|
||||
// sha1 passed as _sha1base64. That way there is no need to have a
|
||||
// plaintext password in the code/binary (though note that SHA1 is well
|
||||
// past its retirement age). When that matches - we `cheat' by returning
|
||||
// the password we got in the first place; so the normal BasicAuth
|
||||
// can be completed. Note that this cannot work for a digest auth -
|
||||
// as there the password in the clear is part of the calculation.
|
||||
|
||||
if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) {
|
||||
if (params == nullptr) {
|
||||
log_e("Something went wrong. params is NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t sha1[20];
|
||||
char sha1calc[48]; // large enough for base64 and Hex represenation
|
||||
String ret;
|
||||
SHA1Builder sha_builder;
|
||||
base64 b64;
|
||||
|
||||
log_v("Trying to authenticate user %s using SHA1.", username.c_str());
|
||||
sha_builder.begin();
|
||||
sha_builder.add((uint8_t*) params[0].c_str(), params[0].length());
|
||||
sha_builder.calculate();
|
||||
sha_builder.getBytes(sha1);
|
||||
|
||||
// we can either decode _sha1base64orHex and then compare the 20 bytes;
|
||||
// or encode the sha we calculated. We pick the latter as encoding of a
|
||||
// fixed array of 20 bytes is safer than operating on something external.
|
||||
if (strlen(_sha1Base64orHex) == 20 * 2) { // 2 chars per byte
|
||||
sha_builder.bytes2hex(sha1calc, sizeof(sha1calc), sha1, sizeof(sha1));
|
||||
log_v("Calculated SHA1 in hex: %s", sha1calc);
|
||||
} else {
|
||||
ret = b64.encode(sha1, sizeof(sha1));
|
||||
ret.toCharArray(sha1calc, sizeof(sha1calc));
|
||||
log_v("Calculated SHA1 in base64: %s", sha1calc);
|
||||
}
|
||||
|
||||
return ((username.equalsConstantTime(_username)) &&
|
||||
(String((char*)sha1calc).equalsConstantTime(_sha1Base64orHex)) &&
|
||||
(mode == BASIC_AUTH) /* to keep things somewhat time constant. */
|
||||
) ? new String(params[0]) : NULL;
|
||||
});
|
||||
}
|
||||
|
||||
bool WebServer::authenticate(const char * _username, const char * _password) {
|
||||
return WebServer::authenticate([_username,_password](HTTPAuthMethod mode, String username, String params[])->String * {
|
||||
return username.equalsConstantTime(_username) ? new String(_password) : NULL;
|
||||
});
|
||||
}
|
||||
|
||||
bool WebServer::authenticate(THandlerFunctionAuthCheck fn) {
|
||||
if (!hasHeader(FPSTR(AUTHORIZATION_HEADER))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
|
||||
if (authReq.startsWith(AuthTypeBasic)) {
|
||||
log_v("Trying to authenticate using Basic Auth");
|
||||
bool ret = false;
|
||||
|
||||
authReq = authReq.substring(6); // length of AuthTypeBasic including the space at the end.
|
||||
authReq.trim();
|
||||
|
||||
/* base64 encoded string is always shorter (or equal) in length */
|
||||
char* decoded = (authReq.length() < HTTP_MAX_BASIC_AUTH_LEN) ? new char[authReq.length()] : NULL;
|
||||
if (decoded) {
|
||||
char* p;
|
||||
if (base64_decode_chars(authReq.c_str(), authReq.length(), decoded) && (p = index(decoded, ':')) && p) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
if((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
String _nc,_cnonce;
|
||||
if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_nc = _extractParam(authReq, F("nc="), ',');
|
||||
_cnonce = _extractParam(authReq, F("cnonce=\""),'\"');
|
||||
}
|
||||
String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password));
|
||||
log_v("Hash of user:realm:pass=%s", _H1.c_str());
|
||||
String _H2 = "";
|
||||
if(_currentMethod == HTTP_GET){
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
}else if(_currentMethod == HTTP_POST){
|
||||
_H2 = md5str(String(F("POST:")) + _uri);
|
||||
}else if(_currentMethod == HTTP_PUT){
|
||||
_H2 = md5str(String(F("PUT:")) + _uri);
|
||||
}else if(_currentMethod == HTTP_DELETE){
|
||||
_H2 = md5str(String(F("DELETE:")) + _uri);
|
||||
}else{
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
}
|
||||
log_v("Hash of GET:uri=%s", _H2.c_str());
|
||||
String _responsecheck = "";
|
||||
if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
|
||||
} else {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
|
||||
}
|
||||
log_v("The Proper response=%s", _responsecheck.c_str());
|
||||
if(_response == _responsecheck){
|
||||
authReq = "";
|
||||
return true;
|
||||
/* Note: rfc7617 guarantees that there will not be an escaped colon in the username itself.
|
||||
* Note: base64_decode_chars() guarantees a terminating \0
|
||||
*/
|
||||
*p = '\0';
|
||||
char * _username = decoded, * _password = p + 1;
|
||||
String params[] = {
|
||||
_password,
|
||||
_srealm
|
||||
};
|
||||
String* password = fn(BASIC_AUTH, _username, params);
|
||||
|
||||
if (password) {
|
||||
ret = password->equalsConstantTime(_password);
|
||||
// we're more concerned about the password; as the attacker already
|
||||
// knows the _pasword. Arduino's string handling is simple; it reallocs
|
||||
// even when smaller; so a memset is enough (no capacity/size).
|
||||
memset((void *)password->c_str(), 0, password->length());
|
||||
delete password;
|
||||
}
|
||||
}
|
||||
delete[] decoded;
|
||||
}
|
||||
authReq = "";
|
||||
log_v("Authentication %s", ret ? "Success" : "Failed");
|
||||
return ret;
|
||||
} else if (authReq.startsWith(AuthTypeDigest)) {
|
||||
log_v("Trying to authenticate using Digest Auth");
|
||||
authReq = authReq.substring(7);
|
||||
log_v("%s", authReq.c_str());
|
||||
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
String _username = _extractParam(authReq, F("username=\""), '\"');
|
||||
String _realm = _extractParam(authReq, F("realm=\""), '\"');
|
||||
String _uri = _extractParam(authReq, F("uri=\""), '\"');
|
||||
if (!_username.length())
|
||||
goto exf;
|
||||
|
||||
String params[] = {
|
||||
_realm,
|
||||
_uri
|
||||
};
|
||||
String* password = fn(DIGEST_AUTH, _username, params);
|
||||
if (!password)
|
||||
goto exf;
|
||||
|
||||
String _H1 = md5str(String(_username) + ':' + _realm + ':' + * password);
|
||||
// we're extra concerned; as digest request us to know the password
|
||||
// in the clear.
|
||||
memset((void *) password->c_str(), 0, password->length());
|
||||
delete password;
|
||||
_username = "";
|
||||
|
||||
String _nonce = _extractParam(authReq, F("nonce=\""), '\"');
|
||||
String _response = _extractParam(authReq, F("response=\""), '\"');
|
||||
String _opaque = _extractParam(authReq, F("opaque=\""), '\"');
|
||||
|
||||
if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length()))
|
||||
goto exf;
|
||||
|
||||
if ((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm))
|
||||
goto exf;
|
||||
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
String _nc, _cnonce;
|
||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_nc = _extractParam(authReq, F("nc="), ',');
|
||||
_cnonce = _extractParam(authReq, F("cnonce=\""), '\"');
|
||||
}
|
||||
|
||||
log_v("Hash of user:realm:pass=%s", _H1.c_str());
|
||||
String _H2 = "";
|
||||
if (_currentMethod == HTTP_GET) {
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_POST) {
|
||||
_H2 = md5str(String(F("POST:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_PUT) {
|
||||
_H2 = md5str(String(F("PUT:")) + _uri);
|
||||
} else if (_currentMethod == HTTP_DELETE) {
|
||||
_H2 = md5str(String(F("DELETE:")) + _uri);
|
||||
} else {
|
||||
_H2 = md5str(String(F("GET:")) + _uri);
|
||||
}
|
||||
log_v("Hash of GET:uri=%s", _H2.c_str());
|
||||
String _responsecheck = "";
|
||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
|
||||
} else {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
|
||||
}
|
||||
authReq = "";
|
||||
|
||||
log_v("The Proper response=%s", _responsecheck.c_str());
|
||||
bool ret = _response == _responsecheck;
|
||||
log_v("Authentication %s", ret ? "Success" : "Failed");
|
||||
return ret;
|
||||
} else if (authReq.length()) {
|
||||
// OTHER_AUTH
|
||||
log_v("Trying to authenticate using Other Auth, authReq=%s", authReq.c_str());
|
||||
String* ret = fn(OTHER_AUTH, authReq, {});
|
||||
if (ret) {
|
||||
log_v("Authentication Success");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
exf:
|
||||
authReq = "";
|
||||
log_v("Authentication Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -232,11 +327,11 @@ void WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, co
|
|||
_srealm = String(realm);
|
||||
}
|
||||
if(mode == BASIC_AUTH) {
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\"")));
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), AuthTypeBasic + String(F(" realm=\"")) + _srealm + String(F("\"")));
|
||||
} else {
|
||||
_snonce=_getRandomHexString();
|
||||
_sopaque=_getRandomHexString();
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\"")));
|
||||
sendHeader(String(FPSTR(WWW_Authenticate)), AuthTypeDigest + String(F(" realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\"")));
|
||||
}
|
||||
using namespace mime;
|
||||
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
|
||||
|
|
@ -411,8 +506,8 @@ void WebServer::_prepareHeader(String& response, int code, const char* content_t
|
|||
}
|
||||
if (_corsEnabled) {
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Origin")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Methods")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Headers")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Methods")), String("*"));
|
||||
sendHeader(String(FPSTR("Access-Control-Allow-Headers")), String("*"));
|
||||
}
|
||||
sendHeader(String(F("Connection")), String(F("close")));
|
||||
|
||||
|
|
@ -543,9 +638,9 @@ String WebServer::pathArg(unsigned int i) {
|
|||
|
||||
String WebServer::arg(String name) {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if ( _postArgs[j].key == name )
|
||||
return _postArgs[j].value;
|
||||
}
|
||||
if ( _postArgs[j].key == name )
|
||||
return _postArgs[j].value;
|
||||
}
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if ( _currentArgs[i].key == name )
|
||||
return _currentArgs[i].value;
|
||||
|
|
@ -571,9 +666,9 @@ int WebServer::args() {
|
|||
|
||||
bool WebServer::hasArg(String name) {
|
||||
for (int j = 0; j < _postArgsLen; ++j) {
|
||||
if (_postArgs[j].key == name)
|
||||
return true;
|
||||
}
|
||||
if (_postArgs[j].key == name)
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < _currentArgCount; ++i) {
|
||||
if (_currentArgs[i].key == name)
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
||||
UPLOAD_FILE_ABORTED };
|
||||
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
|
||||
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
||||
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH, OTHER_AUTH };
|
||||
|
||||
#define HTTP_DOWNLOAD_UNIT_SIZE 1436
|
||||
|
||||
|
|
@ -46,6 +46,7 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
|||
#define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive
|
||||
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
|
||||
#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection
|
||||
#define HTTP_MAX_BASIC_AUTH_LEN 256 // maximum length of a basic Auth base64 encoded username:password string
|
||||
|
||||
#define CONTENT_LENGTH_UNKNOWN ((size_t) -1)
|
||||
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
|
||||
|
|
@ -82,12 +83,41 @@ public:
|
|||
virtual void close();
|
||||
void stop();
|
||||
|
||||
const String AuthTypeDigest = F("Digest");
|
||||
const String AuthTypeBasic = F("Basic");
|
||||
|
||||
/* Callbackhandler for authentication. The extra parameters depend on the
|
||||
* HTTPAuthMethod mode:
|
||||
*
|
||||
* BASIC_AUTH enteredUsernameOrReq contains the username entered by the user
|
||||
* param[0] password entered (in the clear)
|
||||
* param[1] authentication realm.
|
||||
*
|
||||
* To return - the password the user entered password is compared to. Or Null on fail.
|
||||
*
|
||||
* DIGEST_AUTH enteredUsernameOrReq contains the username entered by the user
|
||||
* param[0] autenticaiton realm
|
||||
* param[1] authentication URI
|
||||
*
|
||||
* To return - the password of which the digest will be based on for comparison. Or NULL
|
||||
* to fail.
|
||||
*
|
||||
* OTHER_AUTH enteredUsernameOrReq rest of the auth line.
|
||||
* params empty array
|
||||
*
|
||||
* To return - NULL to fail; or any string.
|
||||
*/
|
||||
typedef std::function<String * (HTTPAuthMethod mode, String enteredUsernameOrReq, String extraParams[])> THandlerFunctionAuthCheck;
|
||||
|
||||
bool authenticate(THandlerFunctionAuthCheck fn);
|
||||
bool authenticate(const char * username, const char * password);
|
||||
bool authenticateBasicSHA1(const char * _username, const char * _sha1AsBase64orHex);
|
||||
|
||||
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") );
|
||||
|
||||
typedef std::function<void(void)> THandlerFunction;
|
||||
void on(const Uri &uri, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads
|
||||
void addHandler(RequestHandler* handler);
|
||||
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
|
||||
|
|
|
|||
Loading…
Reference in a new issue