Skip to content

Commit 9c7eb76

Browse files
Added a few more ESP32-ism files used by ESPAsyncWebServer (#17)
* Added a few more ESP32-ism files used by ESPAsyncWebServer * Fix problems with MD5Builder and cbuf * Eliminated MD5Builder's dependency on BearSSL by copying publicly-available md5 code directly into MD5Builder and adjusting it to use class variables instead of a separate context structure. * Fixed the well-known off-by-one size bug in the cbuf code that was copied from the ESP32 Arduino code. The sizes returned by all methods now reflect that actual data capacity of the buffer, without counting the sentinel byte that should be hidden. I asked for multiple AI reviews of the new code and also verified that it is compatible with the one use of cbuf in ESPAsyncWebServer * This time for sure on cbuf * Corrected comment about digest input size * Update ArduinoCore-Linux/cores/arduino/libb64/cencode.c Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update ArduinoCore-Linux/cores/arduino/libb64/cdecode.c Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update ArduinoCore-Linux/cores/arduino/cbuf.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update ArduinoCore-Linux/cores/arduino/libb64/cdecode.c Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent b5d77bb commit 9c7eb76

File tree

10 files changed

+973
-0
lines changed

10 files changed

+973
-0
lines changed
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
/*
2+
MD5Builder - Simple MD5 hash calculations
3+
4+
Updated for the Pico by Earle F. Philhower, III
5+
6+
Modified from the ESP8266 version which is
7+
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
8+
This file is part of the esp8266 core for Arduino environment.
9+
10+
This library is free software; you can redistribute it and/or
11+
modify it under the terms of the GNU Lesser General Public
12+
License as published by the Free Software Foundation; either
13+
version 2.1 of the License, or (at your option) any later version.
14+
15+
This library is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18+
Lesser General Public License for more details.
19+
20+
You should have received a copy of the GNU Lesser General Public
21+
License along with this library; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
25+
#include <Arduino.h>
26+
#include <MD5Builder.h>
27+
#include <memory>
28+
29+
/*
30+
* Constants defined by the MD5 algorithm
31+
*/
32+
#define A 0x67452301
33+
#define B 0xefcdab89
34+
#define C 0x98badcfe
35+
#define D 0x10325476
36+
37+
static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
38+
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
39+
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
40+
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
41+
42+
static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
43+
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
44+
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
45+
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
46+
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
47+
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
48+
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
49+
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
50+
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
51+
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
52+
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
53+
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
54+
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
55+
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
56+
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
57+
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
58+
59+
/*
60+
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
61+
*/
62+
static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
70+
71+
/*
72+
* Bit-manipulation functions defined by the MD5 algorithm
73+
*/
74+
#ifdef F
75+
#undef F
76+
#endif
77+
#define F(X, Y, Z) ((X & Y) | (~X & Z))
78+
#define G(X, Y, Z) ((X & Z) | (Y & ~Z))
79+
#define H(X, Y, Z) (X ^ Y ^ Z)
80+
#define I(X, Y, Z) (Y ^ (X | ~Z))
81+
82+
/*
83+
* Rotates a 32-bit word left by n bits
84+
*/
85+
static inline uint32_t rotateLeft(uint32_t x, uint32_t n){
86+
return (x << n) | (x >> (32 - n));
87+
}
88+
89+
/*
90+
* Step on 512 bits of input with the main MD5 algorithm.
91+
*/
92+
static void md5Step(uint32_t *buffer, uint32_t *input){
93+
uint32_t AA = buffer[0];
94+
uint32_t BB = buffer[1];
95+
uint32_t CC = buffer[2];
96+
uint32_t DD = buffer[3];
97+
98+
uint32_t E;
99+
100+
unsigned int j;
101+
102+
for(unsigned int i = 0; i < 64; ++i){
103+
switch(i / 16){
104+
case 0:
105+
E = F(BB, CC, DD);
106+
j = i;
107+
break;
108+
case 1:
109+
E = G(BB, CC, DD);
110+
j = ((i * 5) + 1) % 16;
111+
break;
112+
case 2:
113+
E = H(BB, CC, DD);
114+
j = ((i * 3) + 5) % 16;
115+
break;
116+
default:
117+
E = I(BB, CC, DD);
118+
j = (i * 7) % 16;
119+
break;
120+
}
121+
122+
uint32_t temp = DD;
123+
DD = CC;
124+
CC = BB;
125+
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);
126+
AA = temp;
127+
}
128+
129+
buffer[0] += AA;
130+
buffer[1] += BB;
131+
buffer[2] += CC;
132+
buffer[3] += DD;
133+
}
134+
135+
/*
136+
* Add some amount of input to the context
137+
*
138+
* If the input fills out a block of 512 bits, apply the algorithm (md5Step)
139+
* and save the result in the buffer. Also updates the overall size.
140+
*/
141+
void MD5Builder::add(const uint8_t* input_buffer, const size_t input_len) {
142+
uint32_t input[16];
143+
size_t offset = _size % 64;
144+
_size += input_len;
145+
146+
// Copy each byte in input_buffer into the next space in our context input
147+
for(size_t i = 0; i < input_len; ++i){
148+
_input[offset++] = (uint8_t)*(input_buffer + i);
149+
150+
// If we've filled our context input, copy it into our local array input
151+
// then reset the offset to 0 and fill in a new buffer.
152+
// Every time we fill out a chunk, we run it through the algorithm
153+
// to enable some back and forth between cpu and i/o
154+
if(offset % 64 == 0){
155+
for(unsigned int j = 0; j < 16; ++j){
156+
// Convert to little-endian
157+
// The local variable `input` our 512-bit chunk separated into 32-bit words
158+
// we can use in calculations
159+
input[j] = (uint32_t)(_input[(j * 4) + 3]) << 24 |
160+
(uint32_t)(_input[(j * 4) + 2]) << 16 |
161+
(uint32_t)(_input[(j * 4) + 1]) << 8 |
162+
(uint32_t)(_input[(j * 4)]);
163+
}
164+
md5Step(_buffer, input);
165+
offset = 0;
166+
}
167+
}
168+
}
169+
170+
static bool hex_char_to_nibble(uint8_t c, uint8_t& nibble) {
171+
if (c >= 'a' && c <= 'f') {
172+
nibble = c - ((uint8_t)'a' - 0xA);
173+
return true;
174+
}
175+
if (c >= 'A' && c <= 'F') {
176+
nibble = c - ((uint8_t)'A' - 0xA);
177+
return true;
178+
}
179+
if (c >= '0' && c <= '9') {
180+
nibble = c - (uint8_t)'0';
181+
return true;
182+
}
183+
return false;
184+
}
185+
186+
void MD5Builder::begin(void) {
187+
_size = 0;
188+
189+
_buffer[0] = (uint32_t)A;
190+
_buffer[1] = (uint32_t)B;
191+
_buffer[2] = (uint32_t)C;
192+
_buffer[3] = (uint32_t)D;
193+
}
194+
195+
void MD5Builder::addHexString(const char * data) {
196+
size_t len = strlen(data);
197+
198+
// Require an even number of hex characters; odd lengths cannot form full bytes.
199+
if ((len == 0) || (len % 2 != 0)) {
200+
return;
201+
}
202+
203+
constexpr size_t chunk_size = 64;
204+
uint8_t tmp[chunk_size];
205+
size_t byte_count = len / 2;
206+
207+
for (size_t processed = 0; processed < byte_count;) {
208+
size_t remaining = byte_count - processed;
209+
size_t this_chunk = (remaining > chunk_size) ? chunk_size : remaining;
210+
211+
for (size_t i = 0; i < this_chunk; ++i) {
212+
size_t hex_index = (processed + i) * 2;
213+
uint8_t high;
214+
uint8_t low;
215+
216+
if (!hex_char_to_nibble(static_cast<uint8_t>(data[hex_index]), high) ||
217+
!hex_char_to_nibble(static_cast<uint8_t>(data[hex_index + 1]), low)) {
218+
return;
219+
}
220+
221+
tmp[i] = static_cast<uint8_t>((high << 4) | low);
222+
}
223+
224+
add(tmp, this_chunk);
225+
processed += this_chunk;
226+
}
227+
}
228+
229+
bool MD5Builder::addStream(Stream &stream, const size_t maxLen) {
230+
const int buf_size = 512;
231+
size_t maxLengthLeft = maxLen;
232+
233+
auto buf = std::unique_ptr<uint8_t[]> {new (std::nothrow) uint8_t[buf_size]};
234+
235+
if (!buf) {
236+
return false;
237+
}
238+
239+
size_t bytesAvailable = stream.available();
240+
while ((bytesAvailable > 0) && (maxLengthLeft > 0)) {
241+
242+
// determine number of bytes to read
243+
size_t readBytes = bytesAvailable;
244+
if (readBytes > maxLengthLeft) {
245+
readBytes = maxLengthLeft; // read only until max_len
246+
}
247+
if (readBytes > buf_size) {
248+
readBytes = buf_size; // not read more the buffer can handle
249+
}
250+
251+
// read data and check if we got something
252+
size_t numBytesRead = stream.readBytes(buf.get(), readBytes);
253+
if (numBytesRead < 1) {
254+
return false;
255+
}
256+
257+
// Update MD5 with buffer payload
258+
add(buf.get(), numBytesRead);
259+
260+
// update available number of bytes
261+
maxLengthLeft -= numBytesRead;
262+
bytesAvailable = stream.available();
263+
}
264+
265+
return true;
266+
}
267+
268+
/*
269+
* Pad the current input so its length is congruent to 448 bits (56 bytes) modulo 512 bits,
270+
* then append the size in bits to the very end, and save the result of the final iteration
271+
* into digest.
272+
*/
273+
void MD5Builder::calculate(void) {
274+
uint32_t input[16];
275+
size_t offset = _size % 64;
276+
size_t padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
277+
278+
// Fill in the padding and undo the changes to size that resulted from the update
279+
add(PADDING, padding_length);
280+
_size -= padding_length;
281+
282+
// Do a final update (internal to this function)
283+
// Last two 32-bit words are the two halves of the size (converted from bytes to bits)
284+
for(unsigned int j = 0; j < 14; ++j){
285+
input[j] = (uint32_t)(_input[(j * 4) + 3]) << 24 |
286+
(uint32_t)(_input[(j * 4) + 2]) << 16 |
287+
(uint32_t)(_input[(j * 4) + 1]) << 8 |
288+
(uint32_t)(_input[(j * 4)]);
289+
}
290+
uint64_t bit_length = _size * 8ULL;
291+
input[14] = (uint32_t)(bit_length);
292+
input[15] = (uint32_t)(bit_length >> 32);
293+
294+
md5Step(_buffer, input);
295+
296+
// Move the result into digest (convert from little-endian)
297+
for(unsigned int i = 0; i < 4; ++i){
298+
_digest[(i * 4) + 0] = (uint8_t)((_buffer[i] & 0x000000FF));
299+
_digest[(i * 4) + 1] = (uint8_t)((_buffer[i] & 0x0000FF00) >> 8);
300+
_digest[(i * 4) + 2] = (uint8_t)((_buffer[i] & 0x00FF0000) >> 16);
301+
_digest[(i * 4) + 3] = (uint8_t)((_buffer[i] & 0xFF000000) >> 24);
302+
}
303+
}
304+
305+
void MD5Builder::getBytes(uint8_t * output) const {
306+
memcpy(output, _digest, 16);
307+
}
308+
309+
void MD5Builder::getChars(char * output) const {
310+
for (uint8_t i = 0; i < 16; i++) {
311+
sprintf(output + (i * 2), "%02x", _digest[i]);
312+
}
313+
}
314+
315+
String MD5Builder::toString(void) const {
316+
char out[33];
317+
getChars(out);
318+
return String(out);
319+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
MD5Builder - Simple MD5 hash calculations
3+
4+
Updated for the Pico by Earle F. Philhower, III
5+
Updated for Linux by Mitch Bradley
6+
7+
Modified from the ESP8266 version which is
8+
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
9+
This file is part of the esp8266 core for Arduino environment.
10+
11+
This library is free software; you can redistribute it and/or
12+
modify it under the terms of the GNU Lesser General Public
13+
License as published by the Free Software Foundation; either
14+
version 2.1 of the License, or (at your option) any later version.
15+
16+
This library is distributed in the hope that it will be useful,
17+
but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19+
Lesser General Public License for more details.
20+
21+
You should have received a copy of the GNU Lesser General Public
22+
License along with this library; if not, write to the Free Software
23+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24+
*/
25+
26+
#pragma once
27+
28+
#include <api/String.h>
29+
#include <Stream.h>
30+
#include <cstring>
31+
32+
class MD5Builder {
33+
private:
34+
uint64_t _size; // Size of input in bytes
35+
uint32_t _buffer[4]; // Current accumulation of hash
36+
uint8_t _input[64]; // Input to be used in the next step
37+
uint8_t _digest[16]; // Result of algorithm
38+
public:
39+
void begin(void);
40+
void add(const uint8_t * data, const size_t len);
41+
void add(const char * data) {
42+
add((const uint8_t*)data, strlen(data));
43+
}
44+
void add(char * data) {
45+
add((const char*)data);
46+
}
47+
void add(const String& data) {
48+
add(data.c_str());
49+
}
50+
void addHexString(const char * data);
51+
void addHexString(char * data) {
52+
addHexString((const char*)data);
53+
}
54+
void addHexString(const String& data) {
55+
addHexString(data.c_str());
56+
}
57+
bool addStream(Stream & stream, const size_t maxLen);
58+
void calculate(void);
59+
void getBytes(uint8_t * output) const;
60+
void getChars(char * output) const;
61+
String toString(void) const;
62+
};

0 commit comments

Comments
 (0)