Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
117 changes: 117 additions & 0 deletions ArduinoCore-Linux/cores/arduino/MD5Builder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
MD5Builder - Simple MD5 hash calculations

Updated for the Pico by Earle F. Philhower, III

Modified from the ESP8266 version which is
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 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 <MD5Builder.h>
#include <memory>

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;
}

void MD5Builder::begin(void) {
memset(_buf, 0x00, 16);
br_md5_init(&_ctx);
}

void MD5Builder::add(const uint8_t * data, const uint16_t len) {
br_md5_update(&_ctx, data, len);
}

void MD5Builder::addHexString(const char * data) {
uint16_t i, len = strlen(data);
auto tmp = std::unique_ptr<uint8_t[]> {new (std::nothrow) uint8_t[len / 2]};

if (!tmp) {
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);
}
add(tmp.get(), len / 2);
Comment thread
MitchBradley marked this conversation as resolved.
Outdated
}

bool MD5Builder::addStream(Stream &stream, const size_t maxLen) {
const int buf_size = 512;
int maxLengthLeft = maxLen;

Comment on lines +229 to +232
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addStream(), maxLen is a size_t but it is immediately narrowed into int maxLengthLeft = maxLen;. On 64-bit hosts or large inputs this can overflow/truncate and cause incorrect loop behavior. Use size_t for the remaining length (and adjust comparisons/casts around stream.available() / readBytes) to avoid narrowing.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed; maxLengthLeft is now size_t

auto buf = std::unique_ptr<uint8_t[]> {new (std::nothrow) uint8_t[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.get(), readBytes);
if (numBytesRead < 1) {
return false;
}

// Update MD5 with buffer payload
br_md5_update(&_ctx, buf.get(), numBytesRead);

// update available number of bytes
maxLengthLeft -= numBytesRead;
bytesAvailable = stream.available();
}

return true;
}

void MD5Builder::calculate(void) {
br_md5_out(&_ctx, _buf);
}

void MD5Builder::getBytes(uint8_t * output) const {
memcpy(output, _buf, 16);
}

void MD5Builder::getChars(char * output) const {
for (uint8_t i = 0; i < 16; i++) {
sprintf(output + (i * 2), "%02x", _buf[i]);
}
}

String MD5Builder::toString(void) const {
char out[33];
getChars(out);
return String(out);
}
59 changes: 59 additions & 0 deletions ArduinoCore-Linux/cores/arduino/MD5Builder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
MD5Builder - Simple MD5 hash calculations

Updated for the Pico by Earle F. Philhower, III

Modified from the ESP8266 version which is
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 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
*/

#pragma once

#include <api/String.h>
#include <Stream.h>
#include <bearssl/bearssl_hash.h>

class MD5Builder {
private:
br_md5_context _ctx;
uint8_t _buf[16];
public:
void begin(void);
void add(const uint8_t * data, const uint16_t len);
void add(const char * data) {
add((const uint8_t*)data, strlen(data));
}
Comment on lines +26 to +43
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MD5Builder.h uses strlen() in inline methods but does not include <string.h>/<cstring>. This makes the header non-self-contained and can fail to compile depending on include order. Please include the proper header (and/or avoid calling strlen in the header).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added "include "

void add(char * data) {
add((const char*)data);
}
void add(const String& data) {
add(data.c_str());
}
void addHexString(const char * data);
void addHexString(char * data) {
addHexString((const char*)data);
}
void addHexString(const String& data) {
addHexString(data.c_str());
}
bool addStream(Stream & stream, const size_t maxLen);
void calculate(void);
void getBytes(uint8_t * output) const;
void getChars(char * output) const;
String toString(void) const;
};
196 changes: 196 additions & 0 deletions ArduinoCore-Linux/cores/arduino/cbuf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
cbuf.cpp - Circular buffer implementation
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 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 "cbuf.h"

cbuf::cbuf(size_t size) :
next(NULL), _size(size+1), _buf(new char[size+1]), _bufend(_buf + size + 1), _begin(_buf), _end(_begin)
{
}
Comment thread
MitchBradley marked this conversation as resolved.

cbuf::~cbuf()
{
delete[] _buf;
}

size_t cbuf::resizeAdd(size_t addSize)
{
return resize(_size + addSize);
Comment thread
MitchBradley marked this conversation as resolved.
}

size_t cbuf::resize(size_t newSize)
{

size_t bytes_available = available();
newSize += 1;
// not lose any data
// if data can be lost use remove or flush before resize
if((newSize < bytes_available) || (newSize == _size)) {
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cbuf::resize(), after newSize += 1 (to account for the sentinel), the check if ((newSize < bytes_available) ...) is too weak. When newSize == bytes_available the code will set _end = newbuf + bytes_available, which equals _bufend and later writes will dereference _end out of bounds. The condition should ensure the internal buffer size is strictly greater than the number of stored bytes (e.g., newSize <= bytes_available should reject), accounting for the sentinel slot.

Suggested change
if((newSize < bytes_available) || (newSize == _size)) {
if((newSize <= bytes_available) || (newSize == _size)) {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

return _size;
}

char *newbuf = new char[newSize];
char *oldbuf = _buf;

if(!newbuf) {
return _size;
}

Comment thread
MitchBradley marked this conversation as resolved.
Outdated
if(_buf) {
read(newbuf, bytes_available);
memset((newbuf + bytes_available), 0x00, (newSize - bytes_available));
}

_begin = newbuf;
_end = newbuf + bytes_available;
_bufend = newbuf + newSize;
_size = newSize;

_buf = newbuf;
delete[] oldbuf;

return _size;
}

size_t cbuf::available() const
{
if(_end >= _begin) {
return _end - _begin;
}
return _size - (_begin - _end);
}
Comment thread
MitchBradley marked this conversation as resolved.

size_t cbuf::size()
{
return _size;
}
Comment thread
MitchBradley marked this conversation as resolved.

size_t cbuf::room() const
{
if(_end >= _begin) {
return _size - (_end - _begin) - 1;
}
return _begin - _end - 1;
}

int cbuf::peek()
{
if(empty()) {
return -1;
}

return static_cast<int>(*_begin);
}

size_t cbuf::peek(char *dst, size_t size)
{
size_t bytes_available = available();
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
size_t size_read = size_to_read;
char * begin = _begin;
if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) {
size_t top_size = _bufend - _begin;
memcpy(dst, _begin, top_size);
begin = _buf;
size_to_read -= top_size;
dst += top_size;
}
memcpy(dst, begin, size_to_read);
return size_read;
}

int cbuf::read()
{
if(empty()) {
return -1;
}

char result = *_begin;
_begin = wrap_if_bufend(_begin + 1);
return static_cast<int>(result);
}

size_t cbuf::read(char* dst, size_t size)
{
size_t bytes_available = available();
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
size_t size_read = size_to_read;
if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) {
size_t top_size = _bufend - _begin;
memcpy(dst, _begin, top_size);
_begin = _buf;
size_to_read -= top_size;
dst += top_size;
}
memcpy(dst, _begin, size_to_read);
_begin = wrap_if_bufend(_begin + size_to_read);
return size_read;
}

size_t cbuf::write(char c)
{
if(full()) {
return 0;
}

*_end = c;
_end = wrap_if_bufend(_end + 1);
return 1;
}

size_t cbuf::write(const char* src, size_t size)
{
size_t bytes_available = room();
size_t size_to_write = (size < bytes_available) ? size : bytes_available;
size_t size_written = size_to_write;
if(_end >= _begin && size_to_write > (size_t) (_bufend - _end)) {
size_t top_size = _bufend - _end;
memcpy(_end, src, top_size);
_end = _buf;
size_to_write -= top_size;
src += top_size;
}
memcpy(_end, src, size_to_write);
_end = wrap_if_bufend(_end + size_to_write);
return size_written;
}

void cbuf::flush()
{
_begin = _buf;
_end = _buf;
}

size_t cbuf::remove(size_t size)
{
size_t bytes_available = available();
if(size >= bytes_available) {
flush();
return 0;
}
size_t size_to_remove = (size < bytes_available) ? size : bytes_available;
if(_end < _begin && size_to_remove > (size_t) (_bufend - _begin)) {
size_t top_size = _bufend - _begin;
_begin = _buf;
size_to_remove -= top_size;
}
_begin = wrap_if_bufend(_begin + size_to_remove);
return available();
}
Loading
Loading