In this entry, we will see what SHA-256 is, how it works, and how to use it on an ESP32.
SHA-256 is a secure hashing algorithm that will be very useful when performing tasks such as:
- Authentication: Validating the identity of devices or users.
- Secure storage: Protecting passwords and keys on embedded devices.
- Verifying data integrity: Ensuring that data has not been altered during transmission.
But first, let’s briefly see what SHA-256 is 👇.
What is SHA-256
SHA-256 is a member of the SHA-2 family of hash functions (Secure Hash Algorithm 2) developed by the National Institute of Standards and Technology (NIST).
This hash function takes an input of any length and generates a 256-bit (32-byte) value represented as a 64-digit hexadecimal value.
Main features of SHA-256
- Output size: 256 bits (32 bytes).
- Collision resistance: It is computationally infeasible to find two different inputs that produce the same hash.
- Deterministic: The same input will always produce the same hash.
- Irreversible: It is not possible to reconstruct the original input from the hash.
The SHA-256 hash is a unique and fixed representation of the original data, meaning that any change in the data will generate a completely different hash.
SHA-256 is a one-way algorithm, which means that it is not possible to obtain the original data from the hash.
This makes it a very useful tool for verifying data integrity and protecting against unauthorized modifications.
Implementing SHA-256 on the ESP32
The ESP32 has a hardware encryption module that includes support for SHA-256. This allows for efficient hashing operations without overloading the CPU.
Let’s see how to implement SHA-256 on the ESP32 using the mbedTLS
library.
#include "mbedtls/md.h"
#include "stdlib.h"
void printBlockAsHex(std::vector<uint8_t> data)
{
for(int i = 0; i < data.size(); i++)
{
Serial.printf("%02X ", data[i]);
}
}
std::vector<uint8_t> generateSHA256(std::string& text)
{
std::vector<uint8_t> hash(32);
mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
mbedtls_md_starts(&ctx);
mbedtls_md_update(&ctx, (const unsigned char*)text.c_str(), text.length());
mbedtls_md_finish(&ctx, hash.data());
mbedtls_md_free(&ctx);
return hash;
}
void setup()
{
Serial.begin(115200);
delay(2000);
std::string message = "www.luisllamas.es";
auto hash = generateSHA256(message);
Serial.print(message.c_str());
Serial.print("\nHash: ");
printBlockAsHex(hash);
}
void loop() {}
input
: This is the string we want to hash.input_len
: Length of the input string.output
: An array of 32 bytes to store the resulting hash.
mbedtls_sha256_context
: Structure that holds the state of the hash calculation.mbedtls_sha256_init
: Initializes the context.mbedtls_sha256_starts
: Starts the hash calculation (the second parameter0
indicates that we are using SHA-256).mbedtls_sha256_update
: Processes the input string.mbedtls_sha256_finish
: Finishes the calculation and stores the hash in the output buffer.mbedtls_sha256_free
: Frees the resources of the context.
When you run it, you will get something like this.
Practical Examples
Data integrity verification
Suppose you are sending data from a sensor to a server via Wi-Fi. To ensure that the data has not been altered during transmission, you can calculate the SHA-256 hash of the data before sending it and then compare the hash on the server.
// Calculate the hash of the sensor data
unsigned char sensor_data[] = {0x01, 0x02, 0x03, 0x04};
unsigned char hash[32];
mbedtls_sha256(sensor_data, sizeof(sensor_data), hash, 0);
// Send data and hash to the server
send_to_server(sensor_data, sizeof(sensor_data), hash, sizeof(hash));
On the server, you can recalculate the hash and compare it with the received one to verify integrity.
Secure password storage
Instead of storing passwords in plain text, you can store their SHA-256 hash. This protects the passwords even if the device is compromised.
// Hash the password before storing it
const char *password = "my_secret_password";
unsigned char password_hash[32];
mbedtls_sha256((const unsigned char *)password, strlen(password), password_hash, 0);
// Store the hash in non-volatile memory
store_password_hash(password_hash, sizeof(password_hash));
Device authentication
SHA-256 can also be used to authenticate devices on a network. For example, you can generate a token based on a shared secret and the current time, and then send the SHA-256 hash of the token to validate the device’s identity.
// Generate an authentication token
char token[64];
snprintf(token, sizeof(token), "%s%ld", "shared_secret", get_current_time());
unsigned char token_hash[32];
mbedtls_sha256((const unsigned char *)token, strlen(token), token_hash, 0);
// Send the hash for authentication
send_authentication_token(token_hash, sizeof(token_hash));