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/Esp.cpp
|
||||||
cores/esp32/FunctionalInterrupt.cpp
|
cores/esp32/FunctionalInterrupt.cpp
|
||||||
cores/esp32/HardwareSerial.cpp
|
cores/esp32/HardwareSerial.cpp
|
||||||
|
cores/esp32/HEXBuilder.cpp
|
||||||
cores/esp32/IPAddress.cpp
|
cores/esp32/IPAddress.cpp
|
||||||
cores/esp32/libb64/cdecode.c
|
cores/esp32/libb64/cdecode.c
|
||||||
cores/esp32/libb64/cencode.c
|
cores/esp32/libb64/cencode.c
|
||||||
cores/esp32/main.cpp
|
cores/esp32/main.cpp
|
||||||
cores/esp32/MD5Builder.cpp
|
cores/esp32/MD5Builder.cpp
|
||||||
cores/esp32/Print.cpp
|
cores/esp32/Print.cpp
|
||||||
|
cores/esp32/SHA1Builder.cpp
|
||||||
cores/esp32/stdlib_noniso.c
|
cores/esp32/stdlib_noniso.c
|
||||||
cores/esp32/Stream.cpp
|
cores/esp32/Stream.cpp
|
||||||
cores/esp32/StreamString.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
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
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)
|
#include <Arduino.h>
|
||||||
{
|
#include <HEXBuilder.h>
|
||||||
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) :
|
#include <MD5Builder.h>
|
||||||
(c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) :
|
|
||||||
(c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MD5Builder::begin(void)
|
void MD5Builder::begin(void)
|
||||||
{
|
{
|
||||||
|
|
@ -32,23 +27,19 @@ void MD5Builder::begin(void)
|
||||||
esp_rom_md5_init(&_ctx);
|
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);
|
esp_rom_md5_update(&_ctx, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MD5Builder::addHexString(const char * data)
|
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);
|
uint8_t * tmp = (uint8_t*)malloc(len/2);
|
||||||
if(tmp == NULL) {
|
if(tmp == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for(i=0; i<len; i+=2) {
|
hex2bytes(tmp, len/2, data);
|
||||||
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);
|
|
||||||
}
|
|
||||||
add(tmp, len/2);
|
add(tmp, len/2);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
}
|
}
|
||||||
|
|
@ -105,9 +96,7 @@ void MD5Builder::getBytes(uint8_t * output)
|
||||||
|
|
||||||
void MD5Builder::getChars(char * output)
|
void MD5Builder::getChars(char * output)
|
||||||
{
|
{
|
||||||
for(uint8_t i = 0; i < ESP_ROM_MD5_DIGEST_LEN; i++) {
|
bytes2hex(output, ESP_ROM_MD5_DIGEST_LEN*2+1, _buf, ESP_ROM_MD5_DIGEST_LEN);
|
||||||
sprintf(output + (i * 2), "%02x", _buf[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String MD5Builder::toString(void)
|
String MD5Builder::toString(void)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
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
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Lesser General Public
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
|
@ -15,9 +15,11 @@
|
||||||
You should have received a copy of the GNU Lesser General Public
|
You should have received a copy of the GNU Lesser General Public
|
||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
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__
|
#ifndef MD5Builder_h
|
||||||
#define __ESP8266_MD5_BUILDER__
|
#define MD5Builder_h
|
||||||
|
|
||||||
#include <WString.h>
|
#include <WString.h>
|
||||||
#include <Stream.h>
|
#include <Stream.h>
|
||||||
|
|
@ -25,41 +27,27 @@
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_rom_md5.h"
|
#include "esp_rom_md5.h"
|
||||||
|
|
||||||
class MD5Builder
|
#include "HashBuilder.h"
|
||||||
|
|
||||||
|
class MD5Builder : public HashBuilder
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
md5_context_t _ctx;
|
md5_context_t _ctx;
|
||||||
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];
|
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];
|
||||||
public:
|
public:
|
||||||
void begin(void);
|
void begin(void) override;
|
||||||
void add(uint8_t * data, uint16_t len);
|
|
||||||
void add(const char * data)
|
using HashBuilder::add;
|
||||||
{
|
void add(uint8_t * data, size_t len) override;
|
||||||
add((uint8_t*)data, strlen(data));
|
|
||||||
}
|
using HashBuilder::addHexString;
|
||||||
void add(char * data)
|
void addHexString(const char * data) override;
|
||||||
{
|
|
||||||
add((const char*)data);
|
bool addStream(Stream & stream, const size_t maxLen) override;
|
||||||
}
|
void calculate(void) override;
|
||||||
void add(String data)
|
void getBytes(uint8_t * output) override;
|
||||||
{
|
void getChars(char * output) override;
|
||||||
add(data.c_str());
|
String toString(void) override;
|
||||||
}
|
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#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 <Arduino.h>
|
||||||
#include <esp32-hal-log.h>
|
#include <esp32-hal-log.h>
|
||||||
|
#include <libb64/cdecode.h>
|
||||||
#include <libb64/cencode.h>
|
#include <libb64/cencode.h>
|
||||||
#include "esp_random.h"
|
#include "esp_random.h"
|
||||||
#include "WiFiServer.h"
|
#include "WiFiServer.h"
|
||||||
|
|
@ -31,7 +32,8 @@
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
#include "detail/RequestHandlersImpl.h"
|
#include "detail/RequestHandlersImpl.h"
|
||||||
#include "MD5Builder.h"
|
#include "MD5Builder.h"
|
||||||
|
#include "SHA1Builder.h"
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
static const char AUTHORIZATION_HEADER[] = "Authorization";
|
static const char AUTHORIZATION_HEADER[] = "Authorization";
|
||||||
static const char qop_auth[] PROGMEM = "qop=auth";
|
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 Content_Length[] = "Content-Length";
|
||||||
static const char ETAG_HEADER[] = "If-None-Match";
|
static const char ETAG_HEADER[] = "If-None-Match";
|
||||||
|
|
||||||
|
|
||||||
WebServer::WebServer(IPAddress addr, int port)
|
WebServer::WebServer(IPAddress addr, int port)
|
||||||
: _corsEnabled(false)
|
: _corsEnabled(false)
|
||||||
, _server(addr, port)
|
, _server(addr, port)
|
||||||
|
|
@ -128,63 +129,147 @@ static String md5str(String &in){
|
||||||
return md5.toString();
|
return md5.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebServer::authenticate(const char * username, const char * password){
|
bool WebServer::authenticateBasicSHA1(const char * _username, const char * _sha1Base64orHex) {
|
||||||
if(hasHeader(FPSTR(AUTHORIZATION_HEADER))) {
|
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 (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));
|
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
|
||||||
if(authReq.startsWith(F("Basic"))){
|
if (authReq.startsWith(AuthTypeBasic)) {
|
||||||
authReq = authReq.substring(6);
|
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();
|
authReq.trim();
|
||||||
char toencodeLen = strlen(username)+strlen(password)+1;
|
|
||||||
char *toencode = (char *)malloc(toencodeLen + 1);
|
/* base64 encoded string is always shorter (or equal) in length */
|
||||||
if(toencode == NULL){
|
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 = "";
|
authReq = "";
|
||||||
return false;
|
/* 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;
|
||||||
}
|
}
|
||||||
char *encoded = (char *)malloc(base64_encode_expected_len(toencodeLen)+1);
|
|
||||||
if(encoded == NULL){
|
|
||||||
authReq = "";
|
authReq = "";
|
||||||
free(toencode);
|
log_v("Authentication %s", ret ? "Success" : "Failed");
|
||||||
return false;
|
return ret;
|
||||||
}
|
} else if (authReq.startsWith(AuthTypeDigest)) {
|
||||||
sprintf(toencode, "%s:%s", username, password);
|
log_v("Trying to authenticate using Digest Auth");
|
||||||
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);
|
authReq = authReq.substring(7);
|
||||||
log_v("%s", authReq.c_str());
|
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
|
// extracting required parameters for RFC 2069 simpler Digest
|
||||||
|
String _username = _extractParam(authReq, F("username=\""), '\"');
|
||||||
String _realm = _extractParam(authReq, F("realm=\""), '\"');
|
String _realm = _extractParam(authReq, F("realm=\""), '\"');
|
||||||
String _nonce = _extractParam(authReq, F("nonce=\""),'\"');
|
|
||||||
String _uri = _extractParam(authReq, F("uri=\""), '\"');
|
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 _response = _extractParam(authReq, F("response=\""), '\"');
|
||||||
String _opaque = _extractParam(authReq, F("opaque=\""), '\"');
|
String _opaque = _extractParam(authReq, F("opaque=\""), '\"');
|
||||||
|
|
||||||
if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) {
|
if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length()))
|
||||||
authReq = "";
|
goto exf;
|
||||||
return false;
|
|
||||||
}
|
if ((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm))
|
||||||
if((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) {
|
goto exf;
|
||||||
authReq = "";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// parameters for the RFC 2617 newer Digest
|
// parameters for the RFC 2617 newer Digest
|
||||||
String _nc, _cnonce;
|
String _nc, _cnonce;
|
||||||
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||||
_nc = _extractParam(authReq, F("nc="), ',');
|
_nc = _extractParam(authReq, F("nc="), ',');
|
||||||
_cnonce = _extractParam(authReq, F("cnonce=\""), '\"');
|
_cnonce = _extractParam(authReq, F("cnonce=\""), '\"');
|
||||||
}
|
}
|
||||||
String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password));
|
|
||||||
log_v("Hash of user:realm:pass=%s", _H1.c_str());
|
log_v("Hash of user:realm:pass=%s", _H1.c_str());
|
||||||
String _H2 = "";
|
String _H2 = "";
|
||||||
if (_currentMethod == HTTP_GET) {
|
if (_currentMethod == HTTP_GET) {
|
||||||
|
|
@ -205,14 +290,24 @@ bool WebServer::authenticate(const char * username, const char * password){
|
||||||
} else {
|
} else {
|
||||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
|
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
|
||||||
}
|
}
|
||||||
log_v("The Proper response=%s", _responsecheck.c_str());
|
|
||||||
if(_response == _responsecheck){
|
|
||||||
authReq = "";
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exf:
|
||||||
authReq = "";
|
authReq = "";
|
||||||
}
|
log_v("Authentication Failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,11 +327,11 @@ void WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, co
|
||||||
_srealm = String(realm);
|
_srealm = String(realm);
|
||||||
}
|
}
|
||||||
if(mode == BASIC_AUTH) {
|
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 {
|
} else {
|
||||||
_snonce=_getRandomHexString();
|
_snonce=_getRandomHexString();
|
||||||
_sopaque=_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;
|
using namespace mime;
|
||||||
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
|
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
||||||
UPLOAD_FILE_ABORTED };
|
UPLOAD_FILE_ABORTED };
|
||||||
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
|
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
|
#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_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_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_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_UNKNOWN ((size_t) -1)
|
||||||
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
|
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
|
||||||
|
|
@ -82,7 +83,36 @@ public:
|
||||||
virtual void close();
|
virtual void close();
|
||||||
void stop();
|
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 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("") );
|
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") );
|
||||||
|
|
||||||
typedef std::function<void(void)> THandlerFunction;
|
typedef std::function<void(void)> THandlerFunction;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue