Files
DoorCounter/firmware/lib/hmac/hmac.cpp
Peter Woolery 135eb3b46c fix: HMAC format — match server POST\npath\ntimestamp\nsha256(body) scheme
- hmac_sign now takes method+path instead of device_id; builds message as
  method\npath\ntimestamp\nhex(sha256(body)) per server verify_device_hmac
- reporter: header renamed X-HMAC-Signature → X-Signature; passes "POST"+path
- test vector regenerated against new message format; timestamp-diff test updated
- .size() → .length() throughout (Arduino String has no size())

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 10:47:13 -07:00

74 lines
2.5 KiB
C++

// firmware/src/hmac.cpp
#include "hmac.h"
#include "mbedtls/md.h"
#include <stdio.h>
#include <string.h>
static HString bytes_to_hex(const uint8_t* bytes, size_t len) {
HString out;
char buf[3];
for (size_t i = 0; i < len; i++) {
snprintf(buf, sizeof(buf), "%02x", bytes[i]);
out += buf;
}
return out;
}
static void hex_to_bytes(const HString& hex, uint8_t* out, size_t out_len) {
if (hex.length() % 2 != 0) return; // malformed — odd-length hex
for (size_t i = 0; i < out_len && (i * 2 + 1) < hex.length(); i++) {
char byte_str[3] = {hex[i*2], hex[i*2+1], 0};
out[i] = (uint8_t)strtol(byte_str, nullptr, 16);
}
}
static bool sha256(const uint8_t* data, size_t len, uint8_t out[32]) {
mbedtls_md_context_t ctx;
const mbedtls_md_info_t* info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_init(&ctx);
int ret = mbedtls_md_setup(&ctx, info, 0);
if (ret != 0) { mbedtls_md_free(&ctx); return false; }
mbedtls_md_starts(&ctx);
mbedtls_md_update(&ctx, data, len);
mbedtls_md_finish(&ctx, out);
mbedtls_md_free(&ctx);
return true;
}
HString hmac_sign(const HString& secret_hex,
const HString& method,
const HString& path,
uint32_t timestamp,
const HString& body) {
// 1. SHA256(body)
uint8_t body_hash[32] = {};
if (!sha256((const uint8_t*)body.c_str(), body.length(), body_hash)) {
return HString{};
}
HString body_hash_hex = bytes_to_hex(body_hash, 32);
// 2. Build message: method + "\n" + path + "\n" + timestamp + "\n" + sha256(body)
char ts_buf[12];
snprintf(ts_buf, sizeof(ts_buf), "%u", (unsigned)timestamp);
HString message = method + "\n" + path + "\n" + ts_buf + "\n" + body_hash_hex;
// 3. Decode secret from hex
size_t secret_len = secret_hex.length() / 2;
uint8_t secret[64] = {};
hex_to_bytes(secret_hex, secret, secret_len);
// 4. HMAC-SHA256(secret, message)
uint8_t hmac_result[32];
mbedtls_md_context_t ctx;
const mbedtls_md_info_t* info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_init(&ctx);
int ret = mbedtls_md_setup(&ctx, info, 1);
if (ret != 0) { mbedtls_md_free(&ctx); return HString{}; }
mbedtls_md_hmac_starts(&ctx, secret, secret_len);
mbedtls_md_hmac_update(&ctx, (const uint8_t*)message.c_str(), message.length());
mbedtls_md_hmac_finish(&ctx, hmac_result);
mbedtls_md_free(&ctx);
return bytes_to_hex(hmac_result, 32);
}