// File: Secure_HMAC.ino
#include <Arduino.h>
// Constants
#define BLOCK_SIZE 32 // Block size in bytes
#define OUTPUT_SIZE 8 // Output size in bytes
// Blake2s constants (reduced version)
const uint32_t IV[4] = {0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A};
const uint8_t ROUNDS = 8; // Fewer rounds for lightweight hashing
// Rotate right function
uint32_t ROTR(uint32_t x, uint8_t n) {
return (x >> n) | (x << (32 - n));
}
// Blake2s mix function
void G(uint32_t &a, uint32_t &b, uint32_t &c, uint32_t &d, uint32_t x, uint32_t y) {
a = a + b + x;
d = ROTR(d ^ a, 16);
c = c + d;
b = ROTR(b ^ c, 12);
a = a + b + y;
d = ROTR(d ^ a, 8);
c = c + d;
b = ROTR(b ^ c, 7);
}
// Blake2s compression function
void Blake2sCompress(uint32_t state[4], const uint8_t *block) {
uint32_t m[4];
for (size_t i = 0; i < 4; i++) {
m[i] = (block[i * 4 + 3] << 24) | (block[i * 4 + 2] << 16) | (block[i * 4 + 1] << 8) | block[i * 4];
}
uint32_t v[8];
memcpy(v, state, sizeof(uint32_t) * 4);
memcpy(v + 4, IV, sizeof(uint32_t) * 4);
for (uint8_t r = 0; r < ROUNDS; r++) {
G(v[0], v[4], v[8], v[12], m[0], m[1]);
G(v[1], v[5], v[9], v[13], m[2], m[3]);
G(v[2], v[6], v[10], v[14], m[0], m[1]);
G(v[3], v[7], v[11], v[15], m[2], m[3]);
}
for (size_t i = 0; i < 4; i++) {
state[i] ^= v[i] ^ v[i + 4];
}
}
// Secure lightweight hash function (Blake2s-based)
void SecureHash(const uint8_t *data, size_t len, uint8_t *digest) {
uint32_t state[4] = {IV[0], IV[1], IV[2], IV[3]};
uint8_t block[16] = {0}; // Small block size for lightweight processing
for (size_t i = 0; i < len; i++) {
block[i % 16] = data[i];
if ((i % 16) == 15 || i == len - 1) {
Blake2sCompress(state, block);
memset(block, 0, 16);
}
}
// Copy final state to digest (truncate to 8 bytes for lightweight HMAC)
for (size_t i = 0; i < 8; i++) {
digest[i] = (state[i / 4] >> ((i % 4) * 8)) & 0xFF;
}
}
// XOR buffer with a specific byte
void XORBuffer(uint8_t *buffer, uint8_t value, size_t len) {
for (size_t i = 0; i < len; i++) {
buffer[i] ^= value;
}
}
// Lightweight HMAC Implementation
void HMAC(const uint8_t *key, size_t key_len, const uint8_t *message, size_t message_len, uint8_t *hmac) {
uint8_t key_block[BLOCK_SIZE] = {0};
uint8_t inner_hash[OUTPUT_SIZE];
uint8_t inner_data[BLOCK_SIZE + message_len];
uint8_t outer_data[BLOCK_SIZE + OUTPUT_SIZE];
// Step 1: Prepare the key block
if (key_len > BLOCK_SIZE) {
SecureHash(key, key_len, key_block);
} else {
memcpy(key_block, key, key_len);
}
// Step 2: Create inner padded key and process message
memcpy(inner_data, key_block, BLOCK_SIZE);
XORBuffer(inner_data, 0x36, BLOCK_SIZE); // XOR with inner pad value
memcpy(inner_data + BLOCK_SIZE, message, message_len);
SecureHash(inner_data, BLOCK_SIZE + message_len, inner_hash);
// Step 3: Create outer padded key and process inner hash
memcpy(outer_data, key_block, BLOCK_SIZE);
XORBuffer(outer_data, 0x5C, BLOCK_SIZE); // XOR with outer pad value
memcpy(outer_data + BLOCK_SIZE, inner_hash, OUTPUT_SIZE);
SecureHash(outer_data, BLOCK_SIZE + OUTPUT_SIZE, hmac);
}
// Test the HMAC implementation
void setup() {
Serial.begin(9600);
// Example key and message
uint8_t key[] = "12456A";
uint8_t message[] = "The main problem, Trend Micro said in a paper published Tuesday, is that instead of relying on standard wireless technologies, the industrial remote controllers depend on proprietary RF protocols that are decades old and “are primarily focused on safety at the expense of security.”";
uint8_t hmac[OUTPUT_SIZE];
// Compute HMAC
HMAC(key, sizeof(key) - 1, message, sizeof(message) - 1, hmac);
// Print the resulting HMAC
Serial.print("HMAC: ");
for (size_t i = 0; i < OUTPUT_SIZE; i++) {
if (hmac[i] < 16) Serial.print("0"); // Pad single hex digits
Serial.print(hmac[i], HEX);
Serial.print(" ");
}
Serial.println();
}
void loop() {
// Nothing to do here
}