mirror of
https://github.com/esphome/esphome.git
synced 2026-08-30 01:26:43 +00:00
[noise] Inline the resume MAC and key helpers so try_accept calls the KDF directly
This commit is contained in:
@@ -9,19 +9,18 @@
|
||||
|
||||
namespace esphome::noise {
|
||||
|
||||
static const char LABEL_OFFER[] PROGMEM = "offer";
|
||||
static const char LABEL_CONFIRM[] PROGMEM = "confirm";
|
||||
static const char LABEL_KEYS[] PROGMEM = "keys";
|
||||
const char RESUME_LABEL_OFFER[6] PROGMEM = "offer";
|
||||
const char RESUME_LABEL_CONFIRM[8] PROGMEM = "confirm";
|
||||
const char RESUME_LABEL_KEYS[5] PROGMEM = "keys";
|
||||
// Largest KDF input: "keys" || client_nonce || server_nonce || SHA256(prologue)
|
||||
static constexpr size_t RESUME_KDF_MAX_DATA = sizeof(LABEL_KEYS) - 1 + RESUME_NONCE_SIZE + RESUME_NONCE_SIZE + 32;
|
||||
static_assert(sizeof(LABEL_CONFIRM) - 1 + RESUME_NONCE_SIZE + RESUME_NONCE_SIZE <= RESUME_KDF_MAX_DATA,
|
||||
static constexpr size_t RESUME_KDF_MAX_DATA =
|
||||
sizeof(RESUME_LABEL_KEYS) - 1 + RESUME_NONCE_SIZE + RESUME_NONCE_SIZE + 32;
|
||||
static_assert(sizeof(RESUME_LABEL_CONFIRM) - 1 + RESUME_NONCE_SIZE + RESUME_NONCE_SIZE <= RESUME_KDF_MAX_DATA,
|
||||
"MAC input must fit the KDF buffer");
|
||||
|
||||
/// Noise-construction HKDF-SHA256 keyed with the ticket secret over
|
||||
/// label || a || b [|| SHA256(hash_in)]. out2 == nullptr means MAC only.
|
||||
static bool resume_kdf(const uint8_t *secret, const char *label, size_t label_len, const uint8_t *a, size_t a_len,
|
||||
const uint8_t *b, size_t b_len, const uint8_t *hash_in, size_t hash_in_len, uint8_t *out1,
|
||||
size_t out1_len, uint8_t *out2) {
|
||||
bool resume_kdf(const uint8_t *secret, const char *label, size_t label_len, const uint8_t *a, size_t a_len,
|
||||
const uint8_t *b, size_t b_len, const uint8_t *hash_in, size_t hash_in_len, uint8_t *out1,
|
||||
size_t out1_len, uint8_t *out2) {
|
||||
uint8_t data[RESUME_KDF_MAX_DATA];
|
||||
uint8_t scratch[32];
|
||||
size_t len = label_len + a_len + b_len;
|
||||
@@ -118,24 +117,6 @@ void ResumeTicketCache::clear() {
|
||||
this->used_mask_ = 0;
|
||||
}
|
||||
|
||||
bool resume_compute_offer_mac(const uint8_t *secret, const uint8_t *session_id, const uint8_t *client_nonce,
|
||||
uint8_t *out_mac) {
|
||||
return resume_kdf(secret, LABEL_OFFER, sizeof(LABEL_OFFER) - 1, session_id, RESUME_SESSION_ID_SIZE, client_nonce,
|
||||
RESUME_NONCE_SIZE, nullptr, 0, out_mac, RESUME_MAC_SIZE, nullptr);
|
||||
}
|
||||
|
||||
bool resume_compute_confirm_mac(const uint8_t *secret, const uint8_t *client_nonce, const uint8_t *server_nonce,
|
||||
uint8_t *out_mac) {
|
||||
return resume_kdf(secret, LABEL_CONFIRM, sizeof(LABEL_CONFIRM) - 1, client_nonce, RESUME_NONCE_SIZE, server_nonce,
|
||||
RESUME_NONCE_SIZE, nullptr, 0, out_mac, RESUME_MAC_SIZE, nullptr);
|
||||
}
|
||||
|
||||
bool resume_derive_keys(const uint8_t *secret, const uint8_t *client_nonce, const uint8_t *server_nonce,
|
||||
const uint8_t *prologue, size_t prologue_len, uint8_t *k_c2d, uint8_t *k_d2c) {
|
||||
return resume_kdf(secret, LABEL_KEYS, sizeof(LABEL_KEYS) - 1, client_nonce, RESUME_NONCE_SIZE, server_nonce,
|
||||
RESUME_NONCE_SIZE, prologue, prologue_len, k_c2d, 32, k_d2c);
|
||||
}
|
||||
|
||||
NoiseCipherState *resume_make_cipher(const uint8_t *key) {
|
||||
NoiseCipherState *cipher = nullptr;
|
||||
if (noise_cipherstate_new_by_id(&cipher, NOISE_CIPHER_CHACHAPOLY) != NOISE_ERROR_NONE) {
|
||||
|
||||
@@ -76,19 +76,39 @@ class ResumeTicketCache {
|
||||
uint8_t next_{0};
|
||||
};
|
||||
|
||||
/// HKDF labels, PROGMEM on ESP8266.
|
||||
extern const char RESUME_LABEL_OFFER[6];
|
||||
extern const char RESUME_LABEL_CONFIRM[8];
|
||||
extern const char RESUME_LABEL_KEYS[5];
|
||||
|
||||
/// Noise-construction HKDF-SHA256 keyed with the ticket secret over
|
||||
/// label || a || b [|| SHA256(hash_in)]. out2 == nullptr means MAC only.
|
||||
bool resume_kdf(const uint8_t *secret, const char *label, size_t label_len, const uint8_t *a, size_t a_len,
|
||||
const uint8_t *b, size_t b_len, const uint8_t *hash_in, size_t hash_in_len, uint8_t *out1,
|
||||
size_t out1_len, uint8_t *out2);
|
||||
|
||||
/// offer_mac for the ClientHello resume offer (what a client computes and
|
||||
/// try_accept checks).
|
||||
bool resume_compute_offer_mac(const uint8_t *secret, const uint8_t *session_id, const uint8_t *client_nonce,
|
||||
uint8_t *out_mac);
|
||||
inline bool resume_compute_offer_mac(const uint8_t *secret, const uint8_t *session_id, const uint8_t *client_nonce,
|
||||
uint8_t *out_mac) {
|
||||
return resume_kdf(secret, RESUME_LABEL_OFFER, sizeof(RESUME_LABEL_OFFER) - 1, session_id, RESUME_SESSION_ID_SIZE,
|
||||
client_nonce, RESUME_NONCE_SIZE, nullptr, 0, out_mac, RESUME_MAC_SIZE, nullptr);
|
||||
}
|
||||
|
||||
/// confirm_mac for the ServerHello extension.
|
||||
bool resume_compute_confirm_mac(const uint8_t *secret, const uint8_t *client_nonce, const uint8_t *server_nonce,
|
||||
uint8_t *out_mac);
|
||||
inline bool resume_compute_confirm_mac(const uint8_t *secret, const uint8_t *client_nonce, const uint8_t *server_nonce,
|
||||
uint8_t *out_mac) {
|
||||
return resume_kdf(secret, RESUME_LABEL_CONFIRM, sizeof(RESUME_LABEL_CONFIRM) - 1, client_nonce, RESUME_NONCE_SIZE,
|
||||
server_nonce, RESUME_NONCE_SIZE, nullptr, 0, out_mac, RESUME_MAC_SIZE, nullptr);
|
||||
}
|
||||
|
||||
/// Derive the transport keys. k_c2d encrypts client-to-device traffic,
|
||||
/// k_d2c device-to-client.
|
||||
bool resume_derive_keys(const uint8_t *secret, const uint8_t *client_nonce, const uint8_t *server_nonce,
|
||||
const uint8_t *prologue, size_t prologue_len, uint8_t *k_c2d, uint8_t *k_d2c);
|
||||
inline bool resume_derive_keys(const uint8_t *secret, const uint8_t *client_nonce, const uint8_t *server_nonce,
|
||||
const uint8_t *prologue, size_t prologue_len, uint8_t *k_c2d, uint8_t *k_d2c) {
|
||||
return resume_kdf(secret, RESUME_LABEL_KEYS, sizeof(RESUME_LABEL_KEYS) - 1, client_nonce, RESUME_NONCE_SIZE,
|
||||
server_nonce, RESUME_NONCE_SIZE, prologue, prologue_len, k_c2d, 32, k_d2c);
|
||||
}
|
||||
|
||||
/// Build a ChaChaPoly cipher state keyed with key (32 bytes); nullptr on
|
||||
/// failure. Nonce counter starts at 0, exactly like a post-split cipher.
|
||||
|
||||
Reference in New Issue
Block a user