Add hmac-sha256 support (#12437)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
This commit is contained in:
@@ -215,6 +215,7 @@ esphome/components/hlk_fm22x/* @OnFreund
|
||||
esphome/components/hlw8032/* @rici4kubicek
|
||||
esphome/components/hm3301/* @freekode
|
||||
esphome/components/hmac_md5/* @dwmw2
|
||||
esphome/components/hmac_sha256/* @dwmw2
|
||||
esphome/components/homeassistant/* @esphome/core @OttoWinter
|
||||
esphome/components/homeassistant/number/* @landonr
|
||||
esphome/components/homeassistant/switch/* @Links2004
|
||||
|
||||
6
esphome/components/hmac_sha256/__init__.py
Normal file
6
esphome/components/hmac_sha256/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import esphome.config_validation as cv
|
||||
|
||||
AUTO_LOAD = ["sha256"]
|
||||
CODEOWNERS = ["@dwmw2"]
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({})
|
||||
102
esphome/components/hmac_sha256/hmac_sha256.cpp
Normal file
102
esphome/components/hmac_sha256/hmac_sha256.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "hmac_sha256.h"
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_HOST)
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::hmac_sha256 {
|
||||
|
||||
constexpr size_t SHA256_DIGEST_SIZE = 32;
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
|
||||
HmacSHA256::~HmacSHA256() { mbedtls_md_free(&this->ctx_); }
|
||||
|
||||
void HmacSHA256::init(const uint8_t *key, size_t len) {
|
||||
mbedtls_md_init(&this->ctx_);
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
mbedtls_md_setup(&this->ctx_, md_info, 1); // 1 = HMAC mode
|
||||
mbedtls_md_hmac_starts(&this->ctx_, key, len);
|
||||
}
|
||||
|
||||
void HmacSHA256::add(const uint8_t *data, size_t len) { mbedtls_md_hmac_update(&this->ctx_, data, len); }
|
||||
|
||||
void HmacSHA256::calculate() { mbedtls_md_hmac_finish(&this->ctx_, this->digest_); }
|
||||
|
||||
void HmacSHA256::get_bytes(uint8_t *output) { memcpy(output, this->digest_, SHA256_DIGEST_SIZE); }
|
||||
|
||||
void HmacSHA256::get_hex(char *output) {
|
||||
for (size_t i = 0; i < SHA256_DIGEST_SIZE; i++) {
|
||||
sprintf(output + (i * 2), "%02x", this->digest_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool HmacSHA256::equals_bytes(const uint8_t *expected) {
|
||||
return memcmp(this->digest_, expected, SHA256_DIGEST_SIZE) == 0;
|
||||
}
|
||||
|
||||
bool HmacSHA256::equals_hex(const char *expected) {
|
||||
char hex_output[SHA256_DIGEST_SIZE * 2 + 1];
|
||||
this->get_hex(hex_output);
|
||||
hex_output[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||
return strncmp(hex_output, expected, SHA256_DIGEST_SIZE * 2) == 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
HmacSHA256::~HmacSHA256() = default;
|
||||
|
||||
// HMAC block size for SHA256 (RFC 2104)
|
||||
constexpr size_t HMAC_BLOCK_SIZE = 64;
|
||||
|
||||
void HmacSHA256::init(const uint8_t *key, size_t len) {
|
||||
uint8_t ipad[HMAC_BLOCK_SIZE], opad[HMAC_BLOCK_SIZE];
|
||||
|
||||
memset(ipad, 0, sizeof(ipad));
|
||||
if (len > HMAC_BLOCK_SIZE) {
|
||||
sha256::SHA256 keysha256;
|
||||
keysha256.init();
|
||||
keysha256.add(key, len);
|
||||
keysha256.calculate();
|
||||
keysha256.get_bytes(ipad);
|
||||
} else {
|
||||
memcpy(ipad, key, len);
|
||||
}
|
||||
memcpy(opad, ipad, sizeof(opad));
|
||||
|
||||
for (size_t i = 0; i < HMAC_BLOCK_SIZE; i++) {
|
||||
ipad[i] ^= 0x36;
|
||||
opad[i] ^= 0x5c;
|
||||
}
|
||||
|
||||
this->ihash_.init();
|
||||
this->ihash_.add(ipad, sizeof(ipad));
|
||||
|
||||
this->ohash_.init();
|
||||
this->ohash_.add(opad, sizeof(opad));
|
||||
}
|
||||
|
||||
void HmacSHA256::add(const uint8_t *data, size_t len) { this->ihash_.add(data, len); }
|
||||
|
||||
void HmacSHA256::calculate() {
|
||||
uint8_t ibytes[32];
|
||||
|
||||
this->ihash_.calculate();
|
||||
this->ihash_.get_bytes(ibytes);
|
||||
|
||||
this->ohash_.add(ibytes, sizeof(ibytes));
|
||||
this->ohash_.calculate();
|
||||
}
|
||||
|
||||
void HmacSHA256::get_bytes(uint8_t *output) { this->ohash_.get_bytes(output); }
|
||||
|
||||
void HmacSHA256::get_hex(char *output) { this->ohash_.get_hex(output); }
|
||||
|
||||
bool HmacSHA256::equals_bytes(const uint8_t *expected) { return this->ohash_.equals_bytes(expected); }
|
||||
|
||||
bool HmacSHA256::equals_hex(const char *expected) { return this->ohash_.equals_hex(expected); }
|
||||
|
||||
#endif // USE_ESP32 || USE_LIBRETINY
|
||||
|
||||
} // namespace esphome::hmac_sha256
|
||||
#endif
|
||||
59
esphome/components/hmac_sha256/hmac_sha256.h
Normal file
59
esphome/components/hmac_sha256/hmac_sha256.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_HOST)
|
||||
|
||||
#include <string>
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
#include "mbedtls/md.h"
|
||||
#else
|
||||
#include "esphome/components/sha256/sha256.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::hmac_sha256 {
|
||||
|
||||
class HmacSHA256 {
|
||||
public:
|
||||
HmacSHA256() = default;
|
||||
~HmacSHA256();
|
||||
|
||||
/// Initialize a new HMAC-SHA256 digest computation.
|
||||
void init(const uint8_t *key, size_t len);
|
||||
void init(const char *key, size_t len) { this->init((const uint8_t *) key, len); }
|
||||
void init(const std::string &key) { this->init(key.c_str(), key.length()); }
|
||||
|
||||
/// Add bytes of data for the digest.
|
||||
void add(const uint8_t *data, size_t len);
|
||||
void add(const char *data, size_t len) { this->add((const uint8_t *) data, len); }
|
||||
|
||||
/// Compute the digest, based on the provided data.
|
||||
void calculate();
|
||||
|
||||
/// Retrieve the HMAC-SHA256 digest as bytes.
|
||||
/// The output must be able to hold 32 bytes or more.
|
||||
void get_bytes(uint8_t *output);
|
||||
|
||||
/// Retrieve the HMAC-SHA256 digest as hex characters.
|
||||
/// The output must be able to hold 64 bytes or more.
|
||||
void get_hex(char *output);
|
||||
|
||||
/// Compare the digest against a provided byte-encoded digest (32 bytes).
|
||||
bool equals_bytes(const uint8_t *expected);
|
||||
|
||||
/// Compare the digest against a provided hex-encoded digest (64 bytes).
|
||||
bool equals_hex(const char *expected);
|
||||
|
||||
protected:
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
static constexpr size_t SHA256_DIGEST_SIZE = 32;
|
||||
mbedtls_md_context_t ctx_{};
|
||||
uint8_t digest_[SHA256_DIGEST_SIZE]{};
|
||||
#else
|
||||
sha256::SHA256 ihash_;
|
||||
sha256::SHA256 ohash_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome::hmac_sha256
|
||||
#endif
|
||||
34
tests/components/hmac_sha256/common.yaml
Normal file
34
tests/components/hmac_sha256/common.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
esphome:
|
||||
on_boot:
|
||||
- lambda: |-
|
||||
// Test HMAC-SHA256 functionality
|
||||
#ifdef USE_SHA256
|
||||
using esphome::hmac_sha256::HmacSHA256;
|
||||
HmacSHA256 hmac;
|
||||
|
||||
// Test with key "key" and message "The quick brown fox jumps over the lazy dog"
|
||||
const char* key = "key";
|
||||
const char* message = "The quick brown fox jumps over the lazy dog";
|
||||
|
||||
hmac.init(key, strlen(key));
|
||||
hmac.add(message, strlen(message));
|
||||
hmac.calculate();
|
||||
|
||||
char hex_output[65];
|
||||
hmac.get_hex(hex_output);
|
||||
hex_output[64] = '\0';
|
||||
|
||||
ESP_LOGD("HMAC_SHA256", "HMAC-SHA256('%s', '%s') = %s", key, message, hex_output);
|
||||
|
||||
// Expected: f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
|
||||
const char* expected = "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8";
|
||||
if (strcmp(hex_output, expected) == 0) {
|
||||
ESP_LOGI("HMAC_SHA256", "Test PASSED");
|
||||
} else {
|
||||
ESP_LOGE("HMAC_SHA256", "Test FAILED. Expected %s", expected);
|
||||
}
|
||||
#else
|
||||
ESP_LOGW("HMAC_SHA256", "HMAC-SHA256 not available on this platform");
|
||||
#endif
|
||||
|
||||
hmac_sha256:
|
||||
1
tests/components/hmac_sha256/test.bk72xx-ard.yaml
Normal file
1
tests/components/hmac_sha256/test.bk72xx-ard.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/hmac_sha256/test.esp32-ard.yaml
Normal file
1
tests/components/hmac_sha256/test.esp32-ard.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/hmac_sha256/test.esp32-idf.yaml
Normal file
1
tests/components/hmac_sha256/test.esp32-idf.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/hmac_sha256/test.esp8266-ard.yaml
Normal file
1
tests/components/hmac_sha256/test.esp8266-ard.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/hmac_sha256/test.host.yaml
Normal file
1
tests/components/hmac_sha256/test.host.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/hmac_sha256/test.rp2040-ard.yaml
Normal file
1
tests/components/hmac_sha256/test.rp2040-ard.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/hmac_sha256/test.rtl87xx-ard.yaml
Normal file
1
tests/components/hmac_sha256/test.rtl87xx-ard.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
Reference in New Issue
Block a user