This commit is contained in:
2026-05-22 21:52:50 +03:00
commit be7c60e4dd
1854 changed files with 583428 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
/*
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() {
Serial.begin(115200);
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() {}
+95
View File
@@ -0,0 +1,95 @@
#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);
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,178 @@
/*
Usage example for the PBKDF2_HMACBuilder class.
This example shows how to use the Hash library to hash data using the PBKDF2_HMACBuilder class.
PBKDF2_HMAC (Password-Based Key Derivation Function 2) is a key derivation function that uses a password and a salt to derive a key.
The PBKDF2_HMACBuilder class takes for arguments:
- A HashBuilder object to use for the HMAC (SHA1Builder, SHA2Builder, SHA3Builder, etc.)
- A password string (default: empty)
- A salt string (default: empty)
- The number of iterations (default: 1000)
*/
#include <SHA1Builder.h>
#include <SHA2Builder.h>
#include <PBKDF2_HMACBuilder.h>
void setup() {
Serial.begin(115200);
Serial.println("\n\nPBKDF2-HMAC Example");
Serial.println("===================");
// Test 1: Basic PBKDF2-HMAC-SHA1
Serial.println("\n1. PBKDF2-HMAC-SHA1 Test (1 iteration)");
{
SHA1Builder sha1;
PBKDF2_HMACBuilder pbkdf2(&sha1, "password", "salt", 1);
pbkdf2.begin();
pbkdf2.calculate();
Serial.print("Password: ");
Serial.println("password");
Serial.print("Salt: ");
Serial.println("salt");
Serial.print("Iterations: ");
Serial.println(1);
Serial.print("Output (hex): ");
Serial.println(pbkdf2.toString());
// Expected: 0c60c80f961f0e71f3a9b524af6012062fe037a6
String expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
String result = pbkdf2.toString();
if (result.equalsIgnoreCase(expected)) {
Serial.println("✓ PASS: Output matches expected value");
} else {
Serial.println("✗ FAIL: Output does not match expected value");
Serial.print("Expected: ");
Serial.println(expected);
Serial.print("Got: ");
Serial.println(result);
}
}
// Test 2: PBKDF2-HMAC-SHA1 with more iterations
Serial.println("\n2. PBKDF2-HMAC-SHA1 Test (1000 iterations)");
{
SHA1Builder sha1;
PBKDF2_HMACBuilder pbkdf2(&sha1);
const char *password = "password";
const char *salt = "salt";
pbkdf2.begin();
pbkdf2.setPassword(password);
pbkdf2.setSalt(salt);
pbkdf2.setIterations(1000);
pbkdf2.calculate();
Serial.print("Password: ");
Serial.println(password);
Serial.print("Salt: ");
Serial.println(salt);
Serial.print("Iterations: ");
Serial.println(1000);
Serial.print("Output (hex): ");
Serial.println(pbkdf2.toString());
// Expected: 6e88be8bad7eae9d9e10aa061224034fed48d03f
String expected = "6e88be8bad7eae9d9e10aa061224034fed48d03f";
String result = pbkdf2.toString();
if (result.equalsIgnoreCase(expected)) {
Serial.println("✓ PASS: Output matches expected value");
} else {
Serial.println("✗ FAIL: Output does not match expected value");
Serial.print("Expected: ");
Serial.println(expected);
Serial.print("Got: ");
Serial.println(result);
}
}
// Test 3: PBKDF2-HMAC-SHA256 with different password and salt
Serial.println("\n3. PBKDF2-HMAC-SHA256 Test");
{
SHA256Builder sha256;
PBKDF2_HMACBuilder pbkdf2(&sha256, "mySecretPassword", "randomSalt123", 100);
pbkdf2.begin();
pbkdf2.calculate();
Serial.print("Password: ");
Serial.println("mySecretPassword");
Serial.print("Salt: ");
Serial.println("randomSalt123");
Serial.print("Iterations: ");
Serial.println(100);
Serial.print("Output (hex): ");
Serial.println(pbkdf2.toString());
// Expected: 4ce309e56a37e0a4b9b84b98ed4a94e6c5cd5926cfd3baca3a6dea8c5d7903e8
String expected = "4ce309e56a37e0a4b9b84b98ed4a94e6c5cd5926cfd3baca3a6dea8c5d7903e8";
String result = pbkdf2.toString();
if (result.equalsIgnoreCase(expected)) {
Serial.println("✓ PASS: Output matches expected value");
} else {
Serial.println("✗ FAIL: Output does not match expected value");
Serial.print("Expected: ");
Serial.println(expected);
Serial.print("Got: ");
Serial.println(result);
}
}
// Test 4: PBKDF2-HMAC-SHA1 with byte arrays
Serial.println("\n4. PBKDF2-HMAC-SHA1 Test (byte arrays)");
{
SHA1Builder sha1; // or any other hash algorithm based on HashBuilder
PBKDF2_HMACBuilder pbkdf2(&sha1);
uint8_t password[] = {0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64}; // "password" in bytes
uint8_t salt[] = {0x73, 0x61, 0x6c, 0x74}; // "salt" in bytes
pbkdf2.begin();
pbkdf2.setPassword(password, sizeof(password));
pbkdf2.setSalt(salt, sizeof(salt));
pbkdf2.setIterations(1);
pbkdf2.calculate();
Serial.print("Password (bytes): ");
for (int i = 0; i < sizeof(password); i++) {
Serial.print((char)password[i]);
}
Serial.println();
Serial.print("Salt (bytes): ");
for (int i = 0; i < sizeof(salt); i++) {
Serial.print((char)salt[i]);
}
Serial.println();
Serial.print("Iterations: ");
Serial.println(1);
Serial.print("Output (hex): ");
Serial.println(pbkdf2.toString());
// Expected: 0c60c80f961f0e71f3a9b524af6012062fe037a6 (same as test 1)
String expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
String result = pbkdf2.toString();
if (result.equalsIgnoreCase(expected)) {
Serial.println("✓ PASS: Output matches expected value");
} else {
Serial.println("✗ FAIL: Output does not match expected value");
Serial.print("Expected: ");
Serial.println(expected);
Serial.print("Got: ");
Serial.println(result);
}
}
Serial.println("\nPBKDF2-HMAC tests completed!");
}
void loop() {
// Nothing to do in loop
}
+96
View File
@@ -0,0 +1,96 @@
#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 obsolete/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);
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() {}
+95
View File
@@ -0,0 +1,95 @@
/*
Usage example for the SHA2Builder class.
This example shows how to use the SHA2 library to hash data using the SHA2Builder class.
SHA2 (Secure Hash Algorithm 2) provides different output sizes: SHA-224, SHA-256, SHA-384, and SHA-512.
Available constructors:
- SHA224Builder(): 224-bit hash output
- SHA256Builder(): 256-bit hash output
- SHA384Builder(): 384-bit hash output
- SHA512Builder(): 512-bit hash output
- SHA2Builder(size_t hash_size): Generic class that can be used to create any SHA2 variant implemented
*/
#include <SHA2Builder.h>
// Expected hash values for validation
const char *EXPECTED_HELLO_WORLD_SHA256 = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e";
const char *EXPECTED_HELLO_WORLD_SHA512 =
"2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b";
const char *EXPECTED_TEST_MESSAGE_SHA224 = "155b033d801d4dd59b783d76ac3059053c00b2c28340a5a36a427a76";
const char *EXPECTED_TEST_MESSAGE_SHA384 = "efd336618cbc96551936e5897e6af391d2480513ff8d4fc744e34462edb3111477d2b889c4d5e80e23b5f9d1b636fbd7";
// Validation function
bool validateHash(const String &calculated, const char *expected, const String &test_name) {
bool passed = (calculated == expected);
Serial.print(test_name);
Serial.print(": ");
Serial.println(passed ? "PASS" : "FAIL");
Serial.print(" Expected: ");
Serial.println(expected);
Serial.print(" Got: ");
Serial.println(calculated);
return passed;
}
void setup() {
Serial.begin(115200);
Serial.println("\n\n\nStart.");
// Using SHA2Builder class directly with different hash sizes
{
String test_data = "Hello World";
Serial.println("Test data: " + test_data);
// Create SHA-256 (default hash size)
SHA2Builder sha2_256;
sha2_256.begin();
sha2_256.add(test_data);
sha2_256.calculate();
String hash_256 = sha2_256.toString();
validateHash(hash_256, EXPECTED_HELLO_WORLD_SHA256, "SHA-256 validation");
// Create SHA-512
SHA2Builder sha2_512(SHA2_512_HASH_SIZE);
sha2_512.begin();
sha2_512.add(test_data);
sha2_512.calculate();
String hash_512 = sha2_512.toString();
validateHash(hash_512, EXPECTED_HELLO_WORLD_SHA512, "SHA-512 validation");
}
// Example using SHA224Builder and SHA384Builder
// There are other constructors for other hash sizes available:
// - SHA224Builder()
// - SHA256Builder()
// - SHA384Builder()
// - SHA512Builder()
// - SHA2Builder(size_t hash_size)
{
String test_data = "Test message";
Serial.println("Test data: " + test_data);
// Create SHA-224 using specific constructor
SHA224Builder sha2_224;
sha2_224.begin();
sha2_224.add(test_data);
sha2_224.calculate();
String hash_224 = sha2_224.toString();
validateHash(hash_224, EXPECTED_TEST_MESSAGE_SHA224, "SHA224Builder validation");
// Create SHA-384 using specific constructor
SHA384Builder sha2_384;
sha2_384.begin();
sha2_384.add(test_data);
sha2_384.calculate();
String hash_384 = sha2_384.toString();
validateHash(hash_384, EXPECTED_TEST_MESSAGE_SHA384, "SHA384Builder validation");
}
Serial.println("Done.");
}
void loop() {}
+95
View File
@@ -0,0 +1,95 @@
/*
Usage example for the SHA3Builder class.
This example shows how to use the SHA3 library to hash data using the SHA3Builder class.
SHA3 (Secure Hash Algorithm 3) provides different output sizes: SHA3-224, SHA3-256, SHA3-384, and SHA3-512.
Available constructors:
- SHA3_224Builder(): 224-bit hash output
- SHA3_256Builder(): 256-bit hash output
- SHA3_384Builder(): 384-bit hash output
- SHA3_512Builder(): 512-bit hash output
- SHA3Builder(size_t hash_size): Generic class that can be used to create any SHA3 variant implemented
*/
#include <SHA3Builder.h>
// Expected hash values for validation
const char *EXPECTED_HELLO_WORLD_SHA3_256 = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51";
const char *EXPECTED_HELLO_WORLD_SHA3_512 =
"3d58a719c6866b0214f96b0a67b37e51a91e233ce0be126a08f35fdf4c043c6126f40139bfbc338d44eb2a03de9f7bb8eff0ac260b3629811e389a5fbee8a894";
const char *EXPECTED_TEST_MESSAGE_SHA3_224 = "27af391bcb3b86f21b73c42c4abbde4791c395dc650243eede85de0c";
const char *EXPECTED_TEST_MESSAGE_SHA3_384 = "adb18f6b164672c566950bfefa48c5a851d48ee184f249a19e723d753b7536fcd048c3443aff7ebe433fce63c81726ea";
// Validation function
bool validateHash(const String &calculated, const char *expected, const String &test_name) {
bool passed = (calculated == expected);
Serial.print(test_name);
Serial.print(": ");
Serial.println(passed ? "PASS" : "FAIL");
Serial.print(" Expected: ");
Serial.println(expected);
Serial.print(" Got: ");
Serial.println(calculated);
return passed;
}
void setup() {
Serial.begin(115200);
Serial.println("\n\n\nStart.");
// Using SHA3Builder class directly with different hash sizes
{
String test_data = "Hello World";
Serial.println("Test data: " + test_data);
// Create SHA3-256 (default hash size)
SHA3Builder sha3_256;
sha3_256.begin();
sha3_256.add(test_data);
sha3_256.calculate();
String hash_256 = sha3_256.toString();
validateHash(hash_256, EXPECTED_HELLO_WORLD_SHA3_256, "SHA3-256 validation");
// Create SHA3-512
SHA3Builder sha3_512(SHA3_512_HASH_SIZE);
sha3_512.begin();
sha3_512.add(test_data);
sha3_512.calculate();
String hash_512 = sha3_512.toString();
validateHash(hash_512, EXPECTED_HELLO_WORLD_SHA3_512, "SHA3-512 validation");
}
// Example using SHA3_224Builder and SHA3_384Builder
// There are other constructors for other hash sizes available:
// - SHA3_224Builder()
// - SHA3_256Builder()
// - SHA3_384Builder()
// - SHA3_512Builder()
// - SHA3Builder(size_t hash_size)
{
String test_data = "Test message";
Serial.println("Test data: " + test_data);
// Create SHA3-224 using specific constructor
SHA3_224Builder sha3_224;
sha3_224.begin();
sha3_224.add(test_data);
sha3_224.calculate();
String hash_224 = sha3_224.toString();
validateHash(hash_224, EXPECTED_TEST_MESSAGE_SHA3_224, "SHA3_224Builder validation");
// Create SHA3-384 using specific constructor
SHA3_384Builder sha3_384;
sha3_384.begin();
sha3_384.add(test_data);
sha3_384.calculate();
String hash_384 = sha3_384.toString();
validateHash(hash_384, EXPECTED_TEST_MESSAGE_SHA3_384, "SHA3_384Builder validation");
}
Serial.println("Done.");
}
void loop() {}
@@ -0,0 +1,166 @@
/*
Usage example for the SHA3Builder class with streams.
This example shows how to use the SHA3 library to hash data from streams using the addStream method.
This is useful for hashing large files or data that comes from various stream sources like:
- File streams
- Network streams
- Memory streams
- Custom stream implementations
Available constructors:
- SHA3_224Builder(): 224-bit hash output
- SHA3_256Builder(): 256-bit hash output
- SHA3_384Builder(): 384-bit hash output
- SHA3_512Builder(): 512-bit hash output
- SHA3Builder(size_t hash_size): Generic class that can be used to create any SHA3 variant implemented
*/
#include <SHA3Builder.h>
#include <Stream.h>
// Expected hash values for validation
const char *EXPECTED_STREAM_TEST_SHA3_256 = "7094efc774885c7a785b408c5da86636cb8adc79156c0f162c6fd7e49f4c505e";
const char *EXPECTED_MAX_SHA3_224_FULL = "ad0e69e04a7258d7cab4272a08ac69f8b43f4e45f9c49c9abb0628af";
const char *EXPECTED_MAX_SHA3_224_10 = "9b55096e998cda6b96d3f2828c4ccda8c9964a1ad98989fb8b0fcd26";
const char *EXPECTED_COMBINED_SHA3_256 = "4a32307fe03bf9f600c5d124419985fd4d42c1639e6a23ab044f107c3b95a189";
// Validation function
bool validateHash(const String &calculated, const char *expected, const String &test_name) {
bool passed = (calculated == expected);
Serial.print(test_name);
Serial.print(": ");
Serial.println(passed ? "PASS" : "FAIL");
Serial.print(" Expected: ");
Serial.println(expected);
Serial.print(" Got: ");
Serial.println(calculated);
return passed;
}
// Custom stream class for demonstration
class TestStream : public Stream {
private:
String data;
size_t position;
public:
TestStream(String input_data) : data(input_data), position(0) {}
virtual int available() override {
return data.length() - position;
}
virtual int read() override {
if (position < data.length()) {
return data.charAt(position++);
}
return -1;
}
virtual int peek() override {
if (position < data.length()) {
return data.charAt(position);
}
return -1;
}
virtual size_t write(uint8_t) override {
return 0; // Read-only stream
}
size_t length() {
return data.length();
}
void reset() {
position = 0;
}
};
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(10);
}
Serial.println("\n\nSHA3 Stream Example");
Serial.println("===================");
// Example 1: Using addStream with a custom stream
{
Serial.println("\n1. Hashing data from a custom stream:");
const char *test_data = "This is a test message for streaming hash calculation. "
"It contains multiple sentences to demonstrate how the "
"addStream method processes data in chunks.";
TestStream stream(test_data);
SHA3_256Builder sha3_256;
sha3_256.begin();
// Hash the entire stream
// First argument is the stream, second argument is the maximum length to be read from the stream
sha3_256.addStream(stream, stream.length()); // Reading the entire stream
sha3_256.calculate();
String hash_256 = sha3_256.toString();
validateHash(hash_256, EXPECTED_STREAM_TEST_SHA3_256, "Stream test validation");
}
// Example 2: Using addStream with different maximum lengths
{
Serial.println("\n2. Comparing different maximum lengths with streams:");
const char *test_data = "Streaming hash test with different maximum lengths";
TestStream stream(test_data);
// SHA3-224 with a hardcoded maximum length
stream.reset();
SHA3_224Builder sha3_224_10;
sha3_224_10.begin();
sha3_224_10.addStream(stream, 10); // Passing a hardcoded maximum length to be read from the stream
sha3_224_10.calculate();
String hash_224_10 = sha3_224_10.toString();
validateHash(hash_224_10, EXPECTED_MAX_SHA3_224_10, "SHA3-224 with 10 bytes");
// SHA3-224 with the full stream
stream.reset();
SHA3_224Builder sha3_224_full;
sha3_224_full.begin();
sha3_224_full.addStream(stream, stream.length()); // Reading the entire stream
sha3_224_full.calculate();
String hash_224_full = sha3_224_full.toString();
validateHash(hash_224_full, EXPECTED_MAX_SHA3_224_FULL, "SHA3-224 with full stream");
}
// Example 3: Combining add() and addStream()
{
Serial.println("\n3. Combining add() and addStream():");
const char *stream_data = "Additional data from stream";
TestStream stream(stream_data);
SHA3_256Builder sha3_256;
sha3_256.begin();
// Add some data directly
sha3_256.add("Initial data: ");
// Add data from stream
sha3_256.addStream(stream, stream.length());
// Add more data directly
sha3_256.add(" : Final data");
sha3_256.calculate();
String hash_256 = sha3_256.toString();
validateHash(hash_256, EXPECTED_COMBINED_SHA3_256, "Combined data validation");
}
Serial.println("\nStream example completed!");
}
void loop() {
// Nothing to do in loop
}
+51
View File
@@ -0,0 +1,51 @@
#######################################
# Syntax Coloring Map For Hash
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
HashBuilder KEYWORD1
HEXBuilder KEYWORD1
MD5Builder KEYWORD1
SHA1Builder KEYWORD1
SHA2Builder KEYWORD1
SHA224Builder KEYWORD1
SHA256Builder KEYWORD1
SHA384Builder KEYWORD1
SHA512Builder KEYWORD1
SHA3Builder KEYWORD1
PBKDF2_HMACBuilder KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
add KEYWORD2
addHexString KEYWORD2
addStream KEYWORD2
calculate KEYWORD2
getBytes KEYWORD2
getChars KEYWORD2
toString KEYWORD2
hex2bytes KEYWORD2
bytes2hex KEYWORD2
getHashSize KEYWORD2
setPassword KEYWORD2
setSalt KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
SHA1_HASH_SIZE LITERAL1
SHA2_224_HASH_SIZE LITERAL1
SHA2_256_HASH_SIZE LITERAL1
SHA2_384_HASH_SIZE LITERAL1
SHA2_512_HASH_SIZE LITERAL1
SHA3_224_HASH_SIZE LITERAL1
SHA3_256_HASH_SIZE LITERAL1
SHA3_384_HASH_SIZE LITERAL1
SHA3_512_HASH_SIZE LITERAL1
+9
View File
@@ -0,0 +1,9 @@
name=Hash
version=3.3.7
author=lucasssvaz
maintainer=lucasssvaz
sentence=Bundle of hashing functions for the ESP32
paragraph=This library provides a set of hashing functions to be used in the sketches
category=Security
url=
architectures=esp32
+258
View File
@@ -0,0 +1,258 @@
// 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 <Arduino.h>
#include "PBKDF2_HMACBuilder.h"
// Block size for HMAC (64 bytes for SHA-1, SHA-256, SHA-512)
#define HMAC_BLOCK_SIZE 64
PBKDF2_HMACBuilder::PBKDF2_HMACBuilder(HashBuilder *hash, String password, String salt, uint32_t iterations) {
this->hashBuilder = hash;
this->hashSize = hashBuilder->getHashSize();
this->iterations = iterations;
// Initialize pointers
this->password = nullptr;
this->salt = nullptr;
this->passwordLen = 0;
this->saltLen = 0;
this->derivedKey = nullptr;
this->derivedKeyLen = 0;
this->calculated = false;
if (password.length() > 0) {
setPassword(password);
}
if (salt.length() > 0) {
setSalt(salt);
}
}
PBKDF2_HMACBuilder::~PBKDF2_HMACBuilder() {
clearData();
}
void PBKDF2_HMACBuilder::clearData() {
if (derivedKey != nullptr) {
forced_memzero(derivedKey, derivedKeyLen);
delete[] derivedKey;
derivedKey = nullptr;
}
derivedKeyLen = 0;
calculated = false;
}
void PBKDF2_HMACBuilder::hmac(const uint8_t *key, size_t keyLen, const uint8_t *data, size_t dataLen, uint8_t *output) {
uint8_t keyPad[HMAC_BLOCK_SIZE];
uint8_t outerPad[HMAC_BLOCK_SIZE];
uint8_t innerHash[64]; // Large enough for any hash
// Prepare key
if (keyLen > HMAC_BLOCK_SIZE) {
// Key is longer than block size, hash it
hashBuilder->begin();
hashBuilder->add(key, keyLen);
hashBuilder->calculate();
hashBuilder->getBytes(keyPad);
keyLen = hashSize;
} else {
// Copy key to keyPad
memcpy(keyPad, key, keyLen);
}
// Pad key with zeros if necessary
if (keyLen < HMAC_BLOCK_SIZE) {
memset(keyPad + keyLen, 0, HMAC_BLOCK_SIZE - keyLen);
}
// Create outer and inner pads
for (int i = 0; i < HMAC_BLOCK_SIZE; i++) {
outerPad[i] = keyPad[i] ^ 0x5c;
keyPad[i] = keyPad[i] ^ 0x36;
}
// Inner hash: H(K XOR ipad, text)
hashBuilder->begin();
hashBuilder->add(keyPad, HMAC_BLOCK_SIZE);
hashBuilder->add(data, dataLen);
hashBuilder->calculate();
hashBuilder->getBytes(innerHash);
// Outer hash: H(K XOR opad, inner_hash)
hashBuilder->begin();
hashBuilder->add(outerPad, HMAC_BLOCK_SIZE);
hashBuilder->add(innerHash, hashSize);
hashBuilder->calculate();
hashBuilder->getBytes(output);
}
// HashBuilder interface methods
void PBKDF2_HMACBuilder::begin() {
clearData();
}
void PBKDF2_HMACBuilder::add(const uint8_t *data, size_t len) {
log_w("PBKDF2_HMACBuilder::add sets only the password. Use setPassword() and setSalt() instead.");
setPassword(data, len);
}
bool PBKDF2_HMACBuilder::addStream(Stream &stream, const size_t maxLen) {
log_e("PBKDF2_HMACBuilder does not support addStream. Use setPassword() and setSalt() instead.");
(void)stream;
(void)maxLen;
return false;
}
void PBKDF2_HMACBuilder::calculate() {
if (password == nullptr || salt == nullptr) {
log_e("Error: Password or salt not set.");
return;
}
// Set default output size to hash size if not specified
if (derivedKeyLen == 0) {
derivedKeyLen = hashSize;
}
// Allocate output buffer
if (derivedKey != nullptr) {
forced_memzero(derivedKey, derivedKeyLen);
delete[] derivedKey;
}
derivedKey = new uint8_t[derivedKeyLen];
// Perform PBKDF2-HMAC
pbkdf2_hmac(password, passwordLen, salt, saltLen, iterations, derivedKey, derivedKeyLen);
calculated = true;
}
void PBKDF2_HMACBuilder::getBytes(uint8_t *output) {
if (!calculated || derivedKey == nullptr) {
log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided.");
return;
}
memcpy(output, derivedKey, derivedKeyLen);
}
void PBKDF2_HMACBuilder::getChars(char *output) {
if (!calculated || derivedKey == nullptr) {
log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided.");
return;
}
bytes2hex(output, derivedKeyLen * 2 + 1, derivedKey, derivedKeyLen);
}
String PBKDF2_HMACBuilder::toString() {
if (!calculated || derivedKey == nullptr) {
log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided.");
return "";
}
char out[(derivedKeyLen * 2) + 1];
getChars(out);
return String(out);
}
// PBKDF2 specific methods
void PBKDF2_HMACBuilder::setPassword(const uint8_t *password, size_t len) {
if (this->password != nullptr) {
forced_memzero(this->password, len);
delete[] this->password;
}
this->password = new uint8_t[len];
memcpy(this->password, password, len);
this->passwordLen = len;
calculated = false;
}
void PBKDF2_HMACBuilder::setPassword(const char *password) {
setPassword((const uint8_t *)password, strlen(password));
}
void PBKDF2_HMACBuilder::setPassword(String password) {
setPassword((const uint8_t *)password.c_str(), password.length());
}
void PBKDF2_HMACBuilder::setSalt(const uint8_t *salt, size_t len) {
if (this->salt != nullptr) {
forced_memzero(this->salt, len);
delete[] this->salt;
}
this->salt = new uint8_t[len];
memcpy(this->salt, salt, len);
this->saltLen = len;
calculated = false;
}
void PBKDF2_HMACBuilder::setSalt(const char *salt) {
setSalt((const uint8_t *)salt, strlen(salt));
}
void PBKDF2_HMACBuilder::setSalt(String salt) {
setSalt((const uint8_t *)salt.c_str(), salt.length());
}
void PBKDF2_HMACBuilder::setIterations(uint32_t iterations) {
this->iterations = iterations;
}
void PBKDF2_HMACBuilder::setHashAlgorithm(HashBuilder *hash) {
// Set the hash algorithm to use for the HMAC
// Note: We don't delete hashBuilder here as it might be owned by the caller
// The caller is responsible for managing the hashBuilder lifetime
hashBuilder = hash;
hashSize = hashBuilder->getHashSize();
}
void PBKDF2_HMACBuilder::pbkdf2_hmac(
const uint8_t *password, size_t passwordLen, const uint8_t *salt, size_t saltLen, uint32_t iterations, uint8_t *output, size_t outputLen
) {
uint8_t u1[64]; // Large enough for any hash
uint8_t u2[64];
uint8_t saltWithBlock[256]; // Salt + block number
uint8_t block[64];
size_t blocks = (outputLen + hashSize - 1) / hashSize;
for (size_t i = 1; i <= blocks; i++) {
// Prepare salt || INT(i)
memcpy(saltWithBlock, salt, saltLen);
saltWithBlock[saltLen] = (i >> 24) & 0xFF;
saltWithBlock[saltLen + 1] = (i >> 16) & 0xFF;
saltWithBlock[saltLen + 2] = (i >> 8) & 0xFF;
saltWithBlock[saltLen + 3] = i & 0xFF;
// U1 = HMAC(password, salt || INT(i))
hmac(password, passwordLen, saltWithBlock, saltLen + 4, u1);
memcpy(block, u1, hashSize);
// U2 = HMAC(password, U1)
for (uint32_t j = 1; j < iterations; j++) {
hmac(password, passwordLen, u1, hashSize, u2);
memcpy(u1, u2, hashSize);
// XOR with previous result
for (size_t k = 0; k < hashSize; k++) {
block[k] ^= u1[k];
}
}
// Copy block to output
size_t copyLen = (i == blocks) ? (outputLen - (i - 1) * hashSize) : hashSize;
memcpy(output + (i - 1) * hashSize, block, copyLen);
}
}
+73
View File
@@ -0,0 +1,73 @@
// 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.
#ifndef PBKDF2_HMACBuilder_h
#define PBKDF2_HMACBuilder_h
#include <WString.h>
#include <Stream.h>
#include "HashBuilder.h"
class PBKDF2_HMACBuilder : public HashBuilder {
private:
HashBuilder *hashBuilder;
size_t hashSize;
uint32_t iterations;
// Password and salt storage
uint8_t *password;
size_t passwordLen;
uint8_t *salt;
size_t saltLen;
// Output storage
uint8_t *derivedKey;
size_t derivedKeyLen;
bool calculated;
void hmac(const uint8_t *key, size_t keyLen, const uint8_t *data, size_t dataLen, uint8_t *output);
void pbkdf2_hmac(const uint8_t *password, size_t passwordLen, const uint8_t *salt, size_t saltLen, uint32_t iterations, uint8_t *output, size_t outputLen);
void clearData();
public:
using HashBuilder::add;
// Constructor takes a hash builder instance
PBKDF2_HMACBuilder(HashBuilder *hash, String password = "", String salt = "", uint32_t iterations = 10000);
~PBKDF2_HMACBuilder();
// Standard HashBuilder interface
void begin() override;
void add(const uint8_t *data, size_t len) 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;
size_t getHashSize() const override {
return derivedKeyLen;
}
// PBKDF2 specific methods
void setPassword(const uint8_t *password, size_t len);
void setPassword(const char *password);
void setPassword(String password);
void setSalt(const uint8_t *salt, size_t len);
void setSalt(const char *salt);
void setSalt(String salt);
void setIterations(uint32_t iterations);
void setHashAlgorithm(HashBuilder *hash);
};
#endif
+336
View File
@@ -0,0 +1,336 @@
// 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.
//
// Based on mbed TLS (https://tls.mbed.org)
#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) {
finalized = false;
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(const uint8_t *data, size_t len) {
size_t fill;
uint32_t left;
if (finalized || 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);
}
}
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];
if (finalized) {
return;
}
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);
finalized = true;
}
void SHA1Builder::getBytes(uint8_t *output) {
memcpy(output, hash, SHA1_HASH_SIZE);
}
void SHA1Builder::getChars(char *output) {
if (!finalized || output == nullptr) {
log_e("Error: SHA1 not calculated or no output buffer provided.");
return;
}
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
View 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 */
bool finalized; /* Whether hash has been finalized */
void process(const uint8_t *data);
public:
using HashBuilder::add;
SHA1Builder() : finalized(false) {}
void begin() override;
void add(const uint8_t *data, size_t len) 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;
size_t getHashSize() const override {
return SHA1_HASH_SIZE;
}
};
#endif
+421
View File
@@ -0,0 +1,421 @@
// 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 <algorithm>
#include <string.h>
#include "esp32-hal-log.h"
#include "SHA2Builder.h"
// SHA-256 constants
static const uint32_t sha256_k[64] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
// SHA-512 constants
static const uint64_t sha512_k[80] = {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL,
0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,
0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL,
0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL,
0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,
0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL,
0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL,
0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,
0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL};
// Macros for bit manipulation
#define ROTR32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
#define CH32(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
#define CH64(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ32(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define MAJ64(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0_32(x) (ROTR32(x, 2) ^ ROTR32(x, 13) ^ ROTR32(x, 22))
#define EP0_64(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39))
#define EP1_32(x) (ROTR32(x, 6) ^ ROTR32(x, 11) ^ ROTR32(x, 25))
#define EP1_64(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41))
#define SIG0_32(x) (ROTR32(x, 7) ^ ROTR32(x, 18) ^ ((x) >> 3))
#define SIG0_64(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ ((x) >> 7))
#define SIG1_32(x) (ROTR32(x, 17) ^ ROTR32(x, 19) ^ ((x) >> 10))
#define SIG1_64(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ ((x) >> 6))
// Byte order conversion
#define BYTESWAP32(x) ((((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24))
#define BYTESWAP64(x) (((uint64_t)BYTESWAP32((uint32_t)((x) >> 32))) | (((uint64_t)BYTESWAP32((uint32_t)(x))) << 32))
// Constructor
SHA2Builder::SHA2Builder(size_t hash_size) : hash_size(hash_size), buffer_size(0), finalized(false), total_length(0) {
// Determine block size and algorithm family
if (hash_size == SHA2_224_HASH_SIZE || hash_size == SHA2_256_HASH_SIZE) {
block_size = SHA2_256_BLOCK_SIZE;
is_sha512 = false;
} else if (hash_size == SHA2_384_HASH_SIZE || hash_size == SHA2_512_HASH_SIZE) {
block_size = SHA2_512_BLOCK_SIZE;
is_sha512 = true;
} else {
log_e("Invalid hash size: %d", hash_size);
block_size = 0;
is_sha512 = false;
}
}
// Initialize the hash computation
void SHA2Builder::begin() {
// Clear the state and buffer
memset(state_32, 0, sizeof(state_32));
memset(state_64, 0, sizeof(state_64));
memset(buffer, 0, sizeof(buffer));
buffer_size = 0;
finalized = false;
total_length = 0;
// Initialize state based on algorithm
if (!is_sha512) {
// SHA-224/256 initial values
if (hash_size == SHA2_224_HASH_SIZE) {
// SHA-224 initial values
state_32[0] = 0xc1059ed8;
state_32[1] = 0x367cd507;
state_32[2] = 0x3070dd17;
state_32[3] = 0xf70e5939;
state_32[4] = 0xffc00b31;
state_32[5] = 0x68581511;
state_32[6] = 0x64f98fa7;
state_32[7] = 0xbefa4fa4;
} else {
// SHA-256 initial values
state_32[0] = 0x6a09e667;
state_32[1] = 0xbb67ae85;
state_32[2] = 0x3c6ef372;
state_32[3] = 0xa54ff53a;
state_32[4] = 0x510e527f;
state_32[5] = 0x9b05688c;
state_32[6] = 0x1f83d9ab;
state_32[7] = 0x5be0cd19;
}
} else {
// SHA-384/512 initial values
if (hash_size == SHA2_384_HASH_SIZE) {
// SHA-384 initial values
state_64[0] = 0xcbbb9d5dc1059ed8ULL;
state_64[1] = 0x629a292a367cd507ULL;
state_64[2] = 0x9159015a3070dd17ULL;
state_64[3] = 0x152fecd8f70e5939ULL;
state_64[4] = 0x67332667ffc00b31ULL;
state_64[5] = 0x8eb44a8768581511ULL;
state_64[6] = 0xdb0c2e0d64f98fa7ULL;
state_64[7] = 0x47b5481dbefa4fa4ULL;
} else {
// SHA-512 initial values
state_64[0] = 0x6a09e667f3bcc908ULL;
state_64[1] = 0xbb67ae8584caa73bULL;
state_64[2] = 0x3c6ef372fe94f82bULL;
state_64[3] = 0xa54ff53a5f1d36f1ULL;
state_64[4] = 0x510e527fade682d1ULL;
state_64[5] = 0x9b05688c2b3e6c1fULL;
state_64[6] = 0x1f83d9abfb41bd6bULL;
state_64[7] = 0x5be0cd19137e2179ULL;
}
}
}
// Process a block for SHA-256
void SHA2Builder::process_block_sha256(const uint8_t *data) {
uint32_t w[64];
uint32_t a, b, c, d, e, f, g, h;
uint32_t t1, t2;
// Prepare message schedule
for (int i = 0; i < 16; i++) {
w[i] = BYTESWAP32(((uint32_t *)data)[i]);
}
for (int i = 16; i < 64; i++) {
w[i] = SIG1_32(w[i - 2]) + w[i - 7] + SIG0_32(w[i - 15]) + w[i - 16];
}
// Initialize working variables
a = state_32[0];
b = state_32[1];
c = state_32[2];
d = state_32[3];
e = state_32[4];
f = state_32[5];
g = state_32[6];
h = state_32[7];
// Main loop
for (int i = 0; i < 64; i++) {
t1 = h + EP1_32(e) + CH32(e, f, g) + sha256_k[i] + w[i];
t2 = EP0_32(a) + MAJ32(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
// Add the compressed chunk to the current hash value
state_32[0] += a;
state_32[1] += b;
state_32[2] += c;
state_32[3] += d;
state_32[4] += e;
state_32[5] += f;
state_32[6] += g;
state_32[7] += h;
}
// Process a block for SHA-512
void SHA2Builder::process_block_sha512(const uint8_t *data) {
uint64_t w[80];
uint64_t a, b, c, d, e, f, g, h;
uint64_t t1, t2;
// Prepare message schedule
for (int i = 0; i < 16; i++) {
w[i] = BYTESWAP64(((uint64_t *)data)[i]);
}
for (int i = 16; i < 80; i++) {
w[i] = SIG1_64(w[i - 2]) + w[i - 7] + SIG0_64(w[i - 15]) + w[i - 16];
}
// Initialize working variables
a = state_64[0];
b = state_64[1];
c = state_64[2];
d = state_64[3];
e = state_64[4];
f = state_64[5];
g = state_64[6];
h = state_64[7];
// Main loop
for (int i = 0; i < 80; i++) {
t1 = h + EP1_64(e) + CH64(e, f, g) + sha512_k[i] + w[i];
t2 = EP0_64(a) + MAJ64(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
// Add the compressed chunk to the current hash value
state_64[0] += a;
state_64[1] += b;
state_64[2] += c;
state_64[3] += d;
state_64[4] += e;
state_64[5] += f;
state_64[6] += g;
state_64[7] += h;
}
// Add data to the hash computation
void SHA2Builder::add(const uint8_t *data, size_t len) {
if (finalized || len == 0) {
return;
}
total_length += len;
size_t offset = 0;
// Process any buffered data first
if (buffer_size > 0) {
size_t to_copy = std::min(len, block_size - buffer_size);
memcpy(buffer + buffer_size, data, to_copy);
buffer_size += to_copy;
offset += to_copy;
if (buffer_size == block_size) {
if (is_sha512) {
process_block_sha512(buffer);
} else {
process_block_sha256(buffer);
}
buffer_size = 0;
}
}
// Process full blocks
while (offset + block_size <= len) {
if (is_sha512) {
process_block_sha512(data + offset);
} else {
process_block_sha256(data + offset);
}
offset += block_size;
}
// Buffer remaining data
if (offset < len) {
memcpy(buffer, data + offset, len - offset);
buffer_size = len - offset;
}
}
// Add data from a stream
bool SHA2Builder::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;
}
if (readBytes > buf_size) {
readBytes = buf_size;
}
// Read data and check if we got something
int numBytesRead = stream.readBytes(buf, readBytes);
if (numBytesRead < 1) {
free(buf);
return false;
}
// Update SHA2 with buffer payload
add(buf, numBytesRead);
// Update available number of bytes
maxLengthLeft -= numBytesRead;
bytesAvailable = stream.available();
}
free(buf);
return true;
}
// Pad the input according to SHA2 specification
void SHA2Builder::pad() {
// Calculate the number of bytes we have
uint64_t bit_length = total_length * 8;
// Add the bit '1' to the message
buffer[buffer_size++] = 0x80;
// Pad with zeros until we have enough space for the length
while (buffer_size + 8 > block_size) {
if (buffer_size < block_size) {
buffer[buffer_size++] = 0x00;
} else {
// Process the block
if (is_sha512) {
process_block_sha512(buffer);
} else {
process_block_sha256(buffer);
}
buffer_size = 0;
}
}
// Pad with zeros to make room for the length
while (buffer_size + 8 < block_size) {
buffer[buffer_size++] = 0x00;
}
// Add the length in bits
if (is_sha512) {
// For SHA-512, length is 128 bits (16 bytes)
// We only use the lower 64 bits for now
for (int i = 0; i < 8; i++) {
buffer[block_size - 8 + i] = (uint8_t)(bit_length >> (56 - i * 8));
}
// Set the upper 64 bits to 0 (for SHA-384/512, length is limited to 2^128-1)
for (int i = 0; i < 8; i++) {
buffer[block_size - 16 + i] = 0x00;
}
} else {
// For SHA-256, length is 64 bits (8 bytes)
for (int i = 0; i < 8; i++) {
buffer[block_size - 8 + i] = (uint8_t)(bit_length >> (56 - i * 8));
}
}
}
// Finalize the hash computation
void SHA2Builder::calculate() {
if (finalized) {
return;
}
// Pad the input
pad();
// Process the final block
if (is_sha512) {
process_block_sha512(buffer);
} else {
process_block_sha256(buffer);
}
// Extract bytes from the state
if (is_sha512) {
for (size_t i = 0; i < hash_size; i++) {
hash[i] = (uint8_t)(state_64[i >> 3] >> (56 - ((i & 0x7) << 3)));
}
} else {
for (size_t i = 0; i < hash_size; i++) {
hash[i] = (uint8_t)(state_32[i >> 2] >> (24 - ((i & 0x3) << 3)));
}
}
finalized = true;
}
// Get the hash as bytes
void SHA2Builder::getBytes(uint8_t *output) {
memcpy(output, hash, hash_size);
}
// Get the hash as hex string
void SHA2Builder::getChars(char *output) {
if (!finalized || output == nullptr) {
log_e("Error: SHA2 not calculated or no output buffer provided.");
return;
}
bytes2hex(output, hash_size * 2 + 1, hash, hash_size);
}
// Get the hash as String
String SHA2Builder::toString() {
char out[(hash_size * 2) + 1];
getChars(out);
return String(out);
}
+96
View File
@@ -0,0 +1,96 @@
// 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.
#ifndef SHA2Builder_h
#define SHA2Builder_h
#include <WString.h>
#include <Stream.h>
#include "HashBuilder.h"
// SHA2 constants
#define SHA2_224_HASH_SIZE 28
#define SHA2_256_HASH_SIZE 32
#define SHA2_384_HASH_SIZE 48
#define SHA2_512_HASH_SIZE 64
#define SHA2_224_BLOCK_SIZE 64
#define SHA2_256_BLOCK_SIZE 64
#define SHA2_384_BLOCK_SIZE 128
#define SHA2_512_BLOCK_SIZE 128
// SHA2 state sizes (in 32-bit words for SHA-224/256, 64-bit words for SHA-384/512)
#define SHA2_224_STATE_SIZE 8
#define SHA2_256_STATE_SIZE 8
#define SHA2_384_STATE_SIZE 8
#define SHA2_512_STATE_SIZE 8
class SHA2Builder : public HashBuilder {
protected:
uint32_t state_32[8]; // SHA-224/256 state (256 bits)
uint64_t state_64[8]; // SHA-384/512 state (512 bits)
uint8_t buffer[128]; // Input buffer (max block size)
size_t block_size; // Block size
size_t hash_size; // Output hash size
size_t buffer_size; // Current buffer size
bool finalized; // Whether hash has been finalized
bool is_sha512; // Whether using SHA-512 family
uint8_t hash[64]; // Hash result
uint64_t total_length; // Total length of input data
void process_block_sha256(const uint8_t *data);
void process_block_sha512(const uint8_t *data);
void pad();
public:
using HashBuilder::add;
SHA2Builder(size_t hash_size = SHA2_256_HASH_SIZE);
virtual ~SHA2Builder() {}
void begin() override;
void add(const uint8_t *data, size_t len) 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;
size_t getHashSize() const override {
return hash_size;
}
};
class SHA224Builder : public SHA2Builder {
public:
SHA224Builder() : SHA2Builder(SHA2_224_HASH_SIZE) {}
};
class SHA256Builder : public SHA2Builder {
public:
SHA256Builder() : SHA2Builder(SHA2_256_HASH_SIZE) {}
};
class SHA384Builder : public SHA2Builder {
public:
SHA384Builder() : SHA2Builder(SHA2_384_HASH_SIZE) {}
};
class SHA512Builder : public SHA2Builder {
public:
SHA512Builder() : SHA2Builder(SHA2_512_HASH_SIZE) {}
};
#endif
+267
View File
@@ -0,0 +1,267 @@
// 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 <algorithm>
#include "esp32-hal-log.h"
#include "SHA3Builder.h"
// Keccak round constants
static const uint64_t keccak_round_constants[24] = {0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808AULL, 0x8000000080008000ULL,
0x000000000000808BULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
0x000000000000008AULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000AULL,
0x000000008000808BULL, 0x800000000000008BULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800AULL, 0x800000008000000AULL,
0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL};
// Rho rotation constants
static const uint32_t rho[6] = {0x3f022425, 0x1c143a09, 0x2c3d3615, 0x27191713, 0x312b382e, 0x3e030832};
// Pi permutation constants
static const uint32_t pi[6] = {0x110b070a, 0x10050312, 0x04181508, 0x0d13170f, 0x0e14020c, 0x01060916};
// Macros for bit manipulation
#define ROTR64(x, y) (((x) << (64U - (y))) | ((x) >> (y)))
// Keccak-f permutation
void SHA3Builder::keccak_f(uint64_t state[25]) {
uint64_t lane[5];
uint64_t *s = state;
int i;
for (int round = 0; round < 24; round++) {
uint64_t t;
// Theta step
for (i = 0; i < 5; i++) {
lane[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20];
}
for (i = 0; i < 5; i++) {
t = lane[(i + 4) % 5] ^ ROTR64(lane[(i + 1) % 5], 63);
s[i] ^= t;
s[i + 5] ^= t;
s[i + 10] ^= t;
s[i + 15] ^= t;
s[i + 20] ^= t;
}
// Rho step
for (i = 1; i < 25; i += 4) {
uint32_t r = rho[(i - 1) >> 2];
for (int j = i; j < i + 4; j++) {
uint8_t r8 = (uint8_t)(r >> 24);
r <<= 8;
s[j] = ROTR64(s[j], r8);
}
}
// Pi step
t = s[1];
for (i = 0; i < 24; i += 4) {
uint32_t p = pi[i >> 2];
for (unsigned j = 0; j < 4; j++) {
uint64_t tmp = s[p & 0xff];
s[p & 0xff] = t;
t = tmp;
p >>= 8;
}
}
// Chi step
for (i = 0; i <= 20; i += 5) {
lane[0] = s[i];
lane[1] = s[i + 1];
lane[2] = s[i + 2];
lane[3] = s[i + 3];
lane[4] = s[i + 4];
s[i + 0] ^= (~lane[1]) & lane[2];
s[i + 1] ^= (~lane[2]) & lane[3];
s[i + 2] ^= (~lane[3]) & lane[4];
s[i + 3] ^= (~lane[4]) & lane[0];
s[i + 4] ^= (~lane[0]) & lane[1];
}
// Iota step
s[0] ^= keccak_round_constants[round];
}
}
// Process a block of data
void SHA3Builder::process_block(const uint8_t *data) {
// XOR the data into the state using byte-level operations
for (size_t i = 0; i < rate; i++) {
size_t state_idx = i >> 3; // i / 8
size_t bit_offset = (i & 0x7) << 3; // (i % 8) * 8
uint64_t byte_val = (uint64_t)data[i] << bit_offset;
state[state_idx] ^= byte_val;
}
// Apply Keccak-f permutation
keccak_f(state);
}
// Pad the input according to SHA3 specification
void SHA3Builder::pad() {
// Clear the buffer first
memset(buffer + buffer_size, 0, rate - buffer_size);
// Add the domain separator (0x06) at the current position
buffer[buffer_size] = 0x06;
// Set the last byte to indicate the end (0x80)
buffer[rate - 1] = 0x80;
}
// Constructor
SHA3Builder::SHA3Builder(size_t hash_size) : hash_size(hash_size), buffer_size(0), finalized(false) {
// Calculate rate based on hash size
if (hash_size == SHA3_224_HASH_SIZE) {
rate = SHA3_224_RATE;
} else if (hash_size == SHA3_256_HASH_SIZE) {
rate = SHA3_256_RATE;
} else if (hash_size == SHA3_384_HASH_SIZE) {
rate = SHA3_384_RATE;
} else if (hash_size == SHA3_512_HASH_SIZE) {
rate = SHA3_512_RATE;
} else {
log_e("Invalid hash size: %d", hash_size);
rate = 0; // Invalid hash size
}
}
// Initialize the hash computation
void SHA3Builder::begin() {
// Clear the state
memset(state, 0, sizeof(state));
memset(buffer, 0, sizeof(buffer));
buffer_size = 0;
finalized = false;
}
// Add data to the hash computation
void SHA3Builder::add(const uint8_t *data, size_t len) {
if (finalized || len == 0) {
return;
}
size_t offset = 0;
// Process any buffered data first
if (buffer_size > 0) {
size_t to_copy = std::min(len, rate - buffer_size);
memcpy(buffer + buffer_size, data, to_copy);
buffer_size += to_copy;
offset += to_copy;
if (buffer_size == rate) {
process_block(buffer);
buffer_size = 0;
}
}
// Process full blocks
while (offset + rate <= len) {
process_block(data + offset);
offset += rate;
}
// Buffer remaining data
if (offset < len) {
memcpy(buffer, data + offset, len - offset);
buffer_size = len - offset;
}
}
// Add data from a stream
bool SHA3Builder::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;
}
if (readBytes > buf_size) {
readBytes = buf_size;
}
// Read data and check if we got something
int numBytesRead = stream.readBytes(buf, readBytes);
if (numBytesRead < 1) {
free(buf);
return false;
}
// Update SHA3 with buffer payload
add(buf, numBytesRead);
// Update available number of bytes
maxLengthLeft -= numBytesRead;
bytesAvailable = stream.available();
}
free(buf);
return true;
}
// Finalize the hash computation
void SHA3Builder::calculate() {
if (finalized) {
return;
}
// Pad the input
pad();
// Process the final block
process_block(buffer);
// Extract bytes from the state
for (size_t i = 0; i < hash_size; i++) {
size_t state_idx = i >> 3; // i / 8
size_t bit_offset = (i & 0x7) << 3; // (i % 8) * 8
hash[i] = (uint8_t)(state[state_idx] >> bit_offset);
}
finalized = true;
}
// Get the hash as bytes
void SHA3Builder::getBytes(uint8_t *output) {
memcpy(output, hash, hash_size);
}
// Get the hash as hex string
void SHA3Builder::getChars(char *output) {
if (!finalized || output == nullptr) {
log_e("Error: SHA3 not calculated or no output buffer provided.");
return;
}
bytes2hex(output, hash_size * 2 + 1, hash, hash_size);
}
// Get the hash as String
String SHA3Builder::toString() {
char out[(hash_size * 2) + 1];
getChars(out);
return String(out);
}
+89
View File
@@ -0,0 +1,89 @@
// 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.
#ifndef SHA3Builder_h
#define SHA3Builder_h
#include <WString.h>
#include <Stream.h>
#include "HashBuilder.h"
// SHA3 constants
#define SHA3_224_HASH_SIZE 28
#define SHA3_256_HASH_SIZE 32
#define SHA3_384_HASH_SIZE 48
#define SHA3_512_HASH_SIZE 64
#define SHA3_224_RATE 144
#define SHA3_256_RATE 136
#define SHA3_384_RATE 104
#define SHA3_512_RATE 72
#define SHA3_STATE_SIZE 200 // 1600 bits = 200 bytes
class SHA3Builder : public HashBuilder {
protected:
uint64_t state[25]; // SHA3 state (1600 bits)
uint8_t buffer[200]; // Input buffer
size_t rate; // Rate (block size)
size_t hash_size; // Output hash size
size_t buffer_size; // Current buffer size
bool finalized; // Whether hash has been finalized
uint8_t hash[64]; // Hash result
void keccak_f(uint64_t state[25]);
void process_block(const uint8_t *data);
void pad();
public:
using HashBuilder::add;
SHA3Builder(size_t hash_size = SHA3_256_HASH_SIZE);
virtual ~SHA3Builder() {}
void begin() override;
void add(const uint8_t *data, size_t len) 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;
size_t getHashSize() const override {
return hash_size;
}
};
class SHA3_224Builder : public SHA3Builder {
public:
SHA3_224Builder() : SHA3Builder(SHA3_224_HASH_SIZE) {}
};
class SHA3_256Builder : public SHA3Builder {
public:
SHA3_256Builder() : SHA3Builder(SHA3_256_HASH_SIZE) {}
};
class SHA3_384Builder : public SHA3Builder {
public:
SHA3_384Builder() : SHA3Builder(SHA3_384_HASH_SIZE) {}
};
class SHA3_512Builder : public SHA3Builder {
public:
SHA3_512Builder() : SHA3Builder(SHA3_512_HASH_SIZE) {}
};
#endif