Skip to content

fix(python): Fixes for Python code scanning alerts #11668

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/scripts/merge_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@


def load_package(filename):
pkg = json.load(open(filename))["packages"][0]
with open(filename) as f:
pkg = json.load(f)["packages"][0]
print("Loaded package {0} from {1}".format(pkg["name"], filename), file=sys.stderr)
print("{0} platform(s), {1} tools".format(len(pkg["platforms"]), len(pkg["tools"])), file=sys.stderr)
return pkg
Expand Down
10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ set(CORE_SRCS
cores/esp32/freertos_stats.cpp
cores/esp32/FunctionalInterrupt.cpp
cores/esp32/HardwareSerial.cpp
cores/esp32/HashBuilder.cpp
cores/esp32/HEXBuilder.cpp
cores/esp32/IPAddress.cpp
cores/esp32/libb64/cdecode.c
Expand All @@ -62,7 +63,6 @@ set(CORE_SRCS
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
Expand Down Expand Up @@ -93,6 +93,7 @@ set(ARDUINO_ALL_LIBRARIES
Ethernet
FFat
FS
Hash
HTTPClient
HTTPUpdate
Insights
Expand Down Expand Up @@ -154,6 +155,13 @@ set(ARDUINO_LIBRARY_FS_SRCS
libraries/FS/src/FS.cpp
libraries/FS/src/vfs_api.cpp)

set(ARDUINO_LIBRARY_Hash_SRCS
libraries/Hash/src/SHA1Builder.cpp
libraries/Hash/src/SHA2Builder.cpp
libraries/Hash/src/SHA3Builder.cpp
libraries/Hash/src/PBKDF2_HMACBuilder.cpp
)

set(ARDUINO_LIBRARY_HTTPClient_SRCS libraries/HTTPClient/src/HTTPClient.cpp)

set(ARDUINO_LIBRARY_HTTPUpdate_SRCS libraries/HTTPUpdate/src/HTTPUpdate.cpp)
Expand Down
3 changes: 1 addition & 2 deletions cores/esp32/HEXBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <Arduino.h>
#include <HEXBuilder.h>
#include "HEXBuilder.h"

static uint8_t hex_char_to_byte(uint8_t c) {
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa))
Expand Down
2 changes: 2 additions & 0 deletions cores/esp32/HEXBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include <WString.h>
#include <Stream.h>

// Basic hex/byte conversion class to be used by hash builders

class HEXBuilder {
public:
static size_t hex2bytes(unsigned char *out, size_t maxlen, String &in);
Expand Down
38 changes: 38 additions & 0 deletions cores/esp32/HashBuilder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2025 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.

#include "HashBuilder.h"

void HashBuilder::add(const char *data) {
add((const uint8_t *)data, strlen(data));
}

void HashBuilder::add(String data) {
add(data.c_str());
}

void HashBuilder::addHexString(const char *data) {
size_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);
}

void HashBuilder::addHexString(String data) {
addHexString(data.c_str());
}
19 changes: 8 additions & 11 deletions cores/esp32/HashBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,26 @@

#include "HEXBuilder.h"

// Base class for hash builders

class HashBuilder : public HEXBuilder {
public:
virtual ~HashBuilder() {}
virtual void begin() = 0;

virtual void add(const uint8_t *data, size_t len) = 0;
virtual void add(const char *data) {
add((const uint8_t *)data, strlen(data));
}
virtual void add(String data) {
add(data.c_str());
}

virtual void addHexString(const char *data) = 0;
virtual void addHexString(String data) {
addHexString(data.c_str());
}
void add(const char *data);
void add(String data);

void addHexString(const char *data);
void addHexString(String data);

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;
virtual size_t getHashSize() const = 0;
};

#endif
16 changes: 2 additions & 14 deletions cores/esp32/MD5Builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <Arduino.h>
#include <HEXBuilder.h>
#include <MD5Builder.h>
#include "HEXBuilder.h"
#include "MD5Builder.h"

void MD5Builder::begin(void) {
memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN);
Expand All @@ -30,17 +29,6 @@ void MD5Builder::add(const uint8_t *data, size_t len) {
esp_rom_md5_update(&_ctx, data, len);
}

void MD5Builder::addHexString(const char *data) {
size_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 MD5Builder::addStream(Stream &stream, const size_t maxLen) {
const int buf_size = 512;
int maxLengthLeft = maxLen;
Expand Down
9 changes: 3 additions & 6 deletions cores/esp32/MD5Builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,16 @@ class MD5Builder : public HashBuilder {
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];

public:
void begin(void) override;

using HashBuilder::add;
void add(const uint8_t *data, size_t len) override;

using HashBuilder::addHexString;
void addHexString(const char *data) override;

void begin(void) override;
void add(const uint8_t *data, size_t len) 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;
size_t getHashSize() const override { return ESP_ROM_MD5_DIGEST_LEN; }
};

#endif
16 changes: 12 additions & 4 deletions libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

const char *ssid = "..........";
const char *password = "..........";
uint32_t last_ota_time = 0;

void setup() {
Serial.begin(115200);
Expand All @@ -40,9 +41,13 @@ void setup() {
// No authentication by default
// ArduinoOTA.setPassword("admin");

// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
// Password can be set with plain text (will be hashed internally)
// The authentication uses PBKDF2-HMAC-SHA256 with 10,000 iterations
// ArduinoOTA.setPassword("admin");

// Or set password with pre-hashed value (SHA256 hash of "admin")
// SHA256(admin) = 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
// ArduinoOTA.setPasswordHash("8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918");

ArduinoOTA
.onStart([]() {
Expand All @@ -60,7 +65,10 @@ void setup() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
if (millis() - last_ota_time > 500) {
Serial.printf("Progress: %u%%\n", (progress / (total / 100)));
last_ota_time = millis();
}
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
Expand Down
66 changes: 44 additions & 22 deletions libraries/ArduinoOTA/src/ArduinoOTA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
#include "ArduinoOTA.h"
#include "NetworkClient.h"
#include "ESPmDNS.h"
#include "MD5Builder.h"
#include "SHA2Builder.h"
#include "PBKDF2_HMACBuilder.h"
#include "Update.h"

// #define OTA_DEBUG Serial
Expand Down Expand Up @@ -72,18 +73,20 @@ String ArduinoOTAClass::getHostname() {

ArduinoOTAClass &ArduinoOTAClass::setPassword(const char *password) {
if (_state == OTA_IDLE && password) {
MD5Builder passmd5;
passmd5.begin();
passmd5.add(password);
passmd5.calculate();
// Hash the password with SHA256 for storage (not plain text)
SHA256Builder pass_hash;
pass_hash.begin();
pass_hash.add(password);
pass_hash.calculate();
_password.clear();
_password = passmd5.toString();
_password = pass_hash.toString();
}
return *this;
}

ArduinoOTAClass &ArduinoOTAClass::setPasswordHash(const char *password) {
if (_state == OTA_IDLE && password) {
// Store the pre-hashed password directly
_password.clear();
_password = password;
}
Expand Down Expand Up @@ -188,17 +191,18 @@ void ArduinoOTAClass::_onRx() {
_udp_ota.read();
_md5 = readStringUntil('\n');
_md5.trim();
if (_md5.length() != 32) {
if (_md5.length() != 32) { // MD5 produces 32 character hex string for firmware integrity
log_e("bad md5 length");
return;
}

if (_password.length()) {
MD5Builder nonce_md5;
nonce_md5.begin();
nonce_md5.add(String(micros()));
nonce_md5.calculate();
_nonce = nonce_md5.toString();
// Generate a random challenge (nonce)
SHA256Builder nonce_sha256;
nonce_sha256.begin();
nonce_sha256.add(String(micros()) + String(random(1000000)));
nonce_sha256.calculate();
_nonce = nonce_sha256.toString();

_udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort());
_udp_ota.printf("AUTH %s", _nonce.c_str());
Expand All @@ -222,20 +226,37 @@ void ArduinoOTAClass::_onRx() {
_udp_ota.read();
String cnonce = readStringUntil(' ');
String response = readStringUntil('\n');
if (cnonce.length() != 32 || response.length() != 32) {
if (cnonce.length() != 64 || response.length() != 64) { // SHA256 produces 64 character hex string
log_e("auth param fail");
_state = OTA_IDLE;
return;
}

String challenge = _password + ":" + String(_nonce) + ":" + cnonce;
MD5Builder _challengemd5;
_challengemd5.begin();
_challengemd5.add(challenge);
_challengemd5.calculate();
String result = _challengemd5.toString();

if (result.equals(response)) {
// Verify the challenge/response using PBKDF2-HMAC-SHA256
// The client should derive a key using PBKDF2-HMAC-SHA256 with:
// - password: the OTA password (or its hash if using setPasswordHash)
// - salt: nonce + cnonce
// - iterations: 10000 (or configurable)
// Then hash the challenge with the derived key

String salt = _nonce + ":" + cnonce;
SHA256Builder sha256;
// Use the stored password hash for PBKDF2 derivation
PBKDF2_HMACBuilder pbkdf2(&sha256, _password, salt, 10000);

pbkdf2.begin();
pbkdf2.calculate();
String derived_key = pbkdf2.toString();

// Create challenge: derived_key + nonce + cnonce
String challenge = derived_key + ":" + _nonce + ":" + cnonce;
SHA256Builder challenge_sha256;
challenge_sha256.begin();
challenge_sha256.add(challenge);
challenge_sha256.calculate();
String expected_response = challenge_sha256.toString();

if (expected_response.equals(response)) {
_udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort());
_udp_ota.print("OK");
_udp_ota.endPacket();
Expand Down Expand Up @@ -266,7 +287,8 @@ void ArduinoOTAClass::_runUpdate() {
_state = OTA_IDLE;
return;
}
Update.setMD5(_md5.c_str());

Update.setMD5(_md5.c_str()); // Note: Update library still uses MD5 for firmware integrity, this is separate from authentication

if (_start_callback) {
_start_callback();
Expand Down
2 changes: 1 addition & 1 deletion libraries/ArduinoOTA/src/ArduinoOTA.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class ArduinoOTAClass {
//Sets the password that will be required for OTA. Default NULL
ArduinoOTAClass &setPassword(const char *password);

//Sets the password as above but in the form MD5(password). Default NULL
//Sets the password as above but in the form SHA256(password). Default NULL
ArduinoOTAClass &setPasswordHash(const char *password);

//Sets the partition label to write to when updating SPIFFS. Default NULL
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/*
Usage example for the HEXBuilder class.

This example shows how to convert a HEX string to a binary buffer and vice versa.
*/

#include <HEXBuilder.h>

void setup() {
Expand Down
Loading
Loading