Skip to content

Commit a07d589

Browse files
committed
feat(hash): Add hashing library and new algorithms
1 parent 7bfd451 commit a07d589

24 files changed

+1894
-73
lines changed

CMakeLists.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ set(CORE_SRCS
5454
cores/esp32/freertos_stats.cpp
5555
cores/esp32/FunctionalInterrupt.cpp
5656
cores/esp32/HardwareSerial.cpp
57+
cores/esp32/HashBuilder.cpp
5758
cores/esp32/HEXBuilder.cpp
5859
cores/esp32/IPAddress.cpp
5960
cores/esp32/libb64/cdecode.c
@@ -62,7 +63,6 @@ set(CORE_SRCS
6263
cores/esp32/main.cpp
6364
cores/esp32/MD5Builder.cpp
6465
cores/esp32/Print.cpp
65-
cores/esp32/SHA1Builder.cpp
6666
cores/esp32/stdlib_noniso.c
6767
cores/esp32/Stream.cpp
6868
cores/esp32/StreamString.cpp
@@ -93,6 +93,7 @@ set(ARDUINO_ALL_LIBRARIES
9393
Ethernet
9494
FFat
9595
FS
96+
Hash
9697
HTTPClient
9798
HTTPUpdate
9899
Insights
@@ -154,6 +155,13 @@ set(ARDUINO_LIBRARY_FS_SRCS
154155
libraries/FS/src/FS.cpp
155156
libraries/FS/src/vfs_api.cpp)
156157

158+
set(ARDUINO_LIBRARY_Hash_SRCS
159+
libraries/Hash/src/SHA1Builder.cpp
160+
libraries/Hash/src/SHA2Builder.cpp
161+
libraries/Hash/src/SHA3Builder.cpp
162+
libraries/Hash/src/PBKDF2_HMACBuilder.cpp
163+
)
164+
157165
set(ARDUINO_LIBRARY_HTTPClient_SRCS libraries/HTTPClient/src/HTTPClient.cpp)
158166

159167
set(ARDUINO_LIBRARY_HTTPUpdate_SRCS libraries/HTTPUpdate/src/HTTPUpdate.cpp)

cores/esp32/HEXBuilder.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1818
*/
1919

20-
#include <Arduino.h>
21-
#include <HEXBuilder.h>
20+
#include "HEXBuilder.h"
2221

2322
static uint8_t hex_char_to_byte(uint8_t c) {
2423
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa))

cores/esp32/HEXBuilder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <WString.h>
2424
#include <Stream.h>
2525

26+
// Basic hex/byte conversion class to be used by hash builders
27+
2628
class HEXBuilder {
2729
public:
2830
static size_t hex2bytes(unsigned char *out, size_t maxlen, String &in);

cores/esp32/HashBuilder.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "HashBuilder.h"
16+
17+
void HashBuilder::add(const char *data) {
18+
add((const uint8_t *)data, strlen(data));
19+
}
20+
21+
void HashBuilder::add(String data) {
22+
add(data.c_str());
23+
}
24+
25+
void HashBuilder::addHexString(const char *data) {
26+
size_t len = strlen(data);
27+
uint8_t *tmp = (uint8_t *)malloc(len / 2);
28+
if (tmp == NULL) {
29+
return;
30+
}
31+
hex2bytes(tmp, len / 2, data);
32+
add(tmp, len / 2);
33+
free(tmp);
34+
}
35+
36+
void HashBuilder::addHexString(String data) {
37+
addHexString(data.c_str());
38+
}

cores/esp32/HashBuilder.h

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,26 @@
2020

2121
#include "HEXBuilder.h"
2222

23+
// Base class for hash builders
24+
2325
class HashBuilder : public HEXBuilder {
2426
public:
2527
virtual ~HashBuilder() {}
2628
virtual void begin() = 0;
2729

2830
virtual void add(const uint8_t *data, size_t len) = 0;
29-
virtual void add(const char *data) {
30-
add((const uint8_t *)data, strlen(data));
31-
}
32-
virtual void add(String data) {
33-
add(data.c_str());
34-
}
35-
36-
virtual void addHexString(const char *data) = 0;
37-
virtual void addHexString(String data) {
38-
addHexString(data.c_str());
39-
}
31+
void add(const char *data);
32+
void add(String data);
33+
34+
void addHexString(const char *data);
35+
void addHexString(String data);
4036

4137
virtual bool addStream(Stream &stream, const size_t maxLen) = 0;
4238
virtual void calculate() = 0;
4339
virtual void getBytes(uint8_t *output) = 0;
4440
virtual void getChars(char *output) = 0;
4541
virtual String toString() = 0;
42+
virtual size_t getHashSize() const = 0;
4643
};
4744

4845
#endif

cores/esp32/MD5Builder.cpp

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@
1717
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1818
*/
1919

20-
#include <Arduino.h>
21-
#include <HEXBuilder.h>
22-
#include <MD5Builder.h>
20+
#include "HEXBuilder.h"
21+
#include "MD5Builder.h"
2322

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

33-
void MD5Builder::addHexString(const char *data) {
34-
size_t len = strlen(data);
35-
uint8_t *tmp = (uint8_t *)malloc(len / 2);
36-
if (tmp == NULL) {
37-
return;
38-
}
39-
hex2bytes(tmp, len / 2, data);
40-
add(tmp, len / 2);
41-
free(tmp);
42-
}
43-
4432
bool MD5Builder::addStream(Stream &stream, const size_t maxLen) {
4533
const int buf_size = 512;
4634
int maxLengthLeft = maxLen;

cores/esp32/MD5Builder.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,16 @@ class MD5Builder : public HashBuilder {
3535
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];
3636

3737
public:
38-
void begin(void) override;
39-
4038
using HashBuilder::add;
41-
void add(const uint8_t *data, size_t len) override;
42-
43-
using HashBuilder::addHexString;
44-
void addHexString(const char *data) override;
4539

40+
void begin(void) override;
41+
void add(const uint8_t *data, size_t len) override;
4642
bool addStream(Stream &stream, const size_t maxLen) override;
4743
void calculate(void) override;
4844
void getBytes(uint8_t *output) override;
4945
void getChars(char *output) override;
5046
String toString(void) override;
47+
size_t getHashSize() const override { return ESP_ROM_MD5_DIGEST_LEN; }
5148
};
5249

5350
#endif

libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino renamed to libraries/Hash/examples/HEX/HEX.ino

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
/*
2+
Usage example for the HEXBuilder class.
3+
4+
This example shows how to convert a HEX string to a binary buffer and vice versa.
5+
*/
6+
17
#include <HEXBuilder.h>
28

39
void setup() {
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
Usage example for the PBKDF2_HMACBuilder class.
3+
4+
This example shows how to use the Hash library to hash data using the PBKDF2_HMACBuilder class.
5+
PBKDF2_HMAC (Password-Based Key Derivation Function 2) is a key derivation function that uses a password and a salt to derive a key.
6+
7+
The PBKDF2_HMACBuilder class takes for arguments:
8+
- A HashBuilder object to use for the HMAC (SHA1Builder, SHA2Builder, SHA3Builder, etc.)
9+
- A password string (default: empty)
10+
- A salt string (default: empty)
11+
- The number of iterations (default: 1000)
12+
*/
13+
14+
#include <SHA1Builder.h>
15+
#include <SHA2Builder.h>
16+
#include <PBKDF2_HMACBuilder.h>
17+
18+
void setup() {
19+
Serial.begin(115200);
20+
Serial.println("\n\nPBKDF2-HMAC Example");
21+
Serial.println("===================");
22+
23+
// Test 1: Basic PBKDF2-HMAC-SHA1
24+
Serial.println("\n1. PBKDF2-HMAC-SHA1 Test (1 iteration)");
25+
{
26+
SHA1Builder sha1;
27+
PBKDF2_HMACBuilder pbkdf2(&sha1, "password", "salt", 1);
28+
29+
pbkdf2.begin();
30+
pbkdf2.calculate();
31+
32+
Serial.print("Password: ");
33+
Serial.println("password");
34+
Serial.print("Salt: ");
35+
Serial.println("salt");
36+
Serial.print("Iterations: ");
37+
Serial.println(1);
38+
Serial.print("Output (hex): ");
39+
Serial.println(pbkdf2.toString());
40+
41+
// Expected: 0c60c80f961f0e71f3a9b524af6012062fe037a6
42+
String expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
43+
String result = pbkdf2.toString();
44+
45+
if (result.equalsIgnoreCase(expected)) {
46+
Serial.println("✓ PASS: Output matches expected value");
47+
} else {
48+
Serial.println("✗ FAIL: Output does not match expected value");
49+
Serial.print("Expected: ");
50+
Serial.println(expected);
51+
Serial.print("Got: ");
52+
Serial.println(result);
53+
}
54+
}
55+
56+
// Test 2: PBKDF2-HMAC-SHA1 with more iterations
57+
Serial.println("\n2. PBKDF2-HMAC-SHA1 Test (1000 iterations)");
58+
{
59+
SHA1Builder sha1;
60+
PBKDF2_HMACBuilder pbkdf2(&sha1);
61+
62+
const char* password = "password";
63+
const char* salt = "salt";
64+
65+
pbkdf2.begin();
66+
pbkdf2.setPassword(password);
67+
pbkdf2.setSalt(salt);
68+
pbkdf2.setIterations(1000);
69+
pbkdf2.calculate();
70+
71+
Serial.print("Password: ");
72+
Serial.println(password);
73+
Serial.print("Salt: ");
74+
Serial.println(salt);
75+
Serial.print("Iterations: ");
76+
Serial.println(1000);
77+
Serial.print("Output (hex): ");
78+
Serial.println(pbkdf2.toString());
79+
80+
// Expected: 6e88be8bad7eae9d9e10aa061224034fed48d03f
81+
String expected = "6e88be8bad7eae9d9e10aa061224034fed48d03f";
82+
String result = pbkdf2.toString();
83+
84+
if (result.equalsIgnoreCase(expected)) {
85+
Serial.println("✓ PASS: Output matches expected value");
86+
} else {
87+
Serial.println("✗ FAIL: Output does not match expected value");
88+
Serial.print("Expected: ");
89+
Serial.println(expected);
90+
Serial.print("Got: ");
91+
Serial.println(result);
92+
}
93+
}
94+
95+
// Test 3: PBKDF2-HMAC-SHA256 with different password and salt
96+
Serial.println("\n3. PBKDF2-HMAC-SHA256 Test");
97+
{
98+
SHA256Builder sha256;
99+
PBKDF2_HMACBuilder pbkdf2(&sha256, "mySecretPassword", "randomSalt123", 100);
100+
101+
pbkdf2.begin();
102+
pbkdf2.calculate();
103+
104+
Serial.print("Password: ");
105+
Serial.println("mySecretPassword");
106+
Serial.print("Salt: ");
107+
Serial.println("randomSalt123");
108+
Serial.print("Iterations: ");
109+
Serial.println(100);
110+
Serial.print("Output (hex): ");
111+
Serial.println(pbkdf2.toString());
112+
113+
// Expected: 4ce309e56a37e0a4b9b84b98ed4a94e6c5cd5926cfd3baca3a6dea8c5d7903e8
114+
String expected = "4ce309e56a37e0a4b9b84b98ed4a94e6c5cd5926cfd3baca3a6dea8c5d7903e8";
115+
String result = pbkdf2.toString();
116+
117+
if (result.equalsIgnoreCase(expected)) {
118+
Serial.println("✓ PASS: Output matches expected value");
119+
} else {
120+
Serial.println("✗ FAIL: Output does not match expected value");
121+
Serial.print("Expected: ");
122+
Serial.println(expected);
123+
Serial.print("Got: ");
124+
Serial.println(result);
125+
}
126+
}
127+
128+
// Test 4: PBKDF2-HMAC-SHA1 with byte arrays
129+
Serial.println("\n4. PBKDF2-HMAC-SHA1 Test (byte arrays)");
130+
{
131+
SHA1Builder sha1; // or any other hash algorithm based on HashBuilder
132+
PBKDF2_HMACBuilder pbkdf2(&sha1);
133+
134+
uint8_t password[] = {0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64}; // "password" in bytes
135+
uint8_t salt[] = {0x73, 0x61, 0x6c, 0x74}; // "salt" in bytes
136+
137+
pbkdf2.begin();
138+
pbkdf2.setPassword(password, sizeof(password));
139+
pbkdf2.setSalt(salt, sizeof(salt));
140+
pbkdf2.setIterations(1);
141+
pbkdf2.calculate();
142+
143+
Serial.print("Password (bytes): ");
144+
for (int i = 0; i < sizeof(password); i++) {
145+
Serial.print((char)password[i]);
146+
}
147+
Serial.println();
148+
Serial.print("Salt (bytes): ");
149+
for (int i = 0; i < sizeof(salt); i++) {
150+
Serial.print((char)salt[i]);
151+
}
152+
Serial.println();
153+
Serial.print("Iterations: ");
154+
Serial.println(1);
155+
Serial.print("Output (hex): ");
156+
Serial.println(pbkdf2.toString());
157+
158+
// Expected: 0c60c80f961f0e71f3a9b524af6012062fe037a6 (same as test 1)
159+
String expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
160+
String result = pbkdf2.toString();
161+
162+
if (result.equalsIgnoreCase(expected)) {
163+
Serial.println("✓ PASS: Output matches expected value");
164+
} else {
165+
Serial.println("✗ FAIL: Output does not match expected value");
166+
Serial.print("Expected: ");
167+
Serial.println(expected);
168+
Serial.print("Got: ");
169+
Serial.println(result);
170+
}
171+
}
172+
173+
Serial.println("\nPBKDF2-HMAC tests completed!");
174+
}
175+
176+
void loop() {
177+
// Nothing to do in loop
178+
}

0 commit comments

Comments
 (0)