ESPHome 2026.10.2
Loading...
Searching...
No Matches
noise_handshake.cpp
Go to the documentation of this file.
1#include "noise_handshake.h"
2#ifdef USE_NOISE
3#include "esphome/core/hal.h"
5#include "esphome/core/log.h"
6
7namespace esphome::noise {
8
9static const char *const TAG = "noise";
10
11// Log the failing noise-c call at the same verbosity the api helper used
12// before this class existed; callers only see one collapsed error code.
13#define HANDSHAKE_STEP_LOG(func_name, err_code) \
14 ESP_LOGVV(TAG, "%s failed: %s", LOG_STR_ARG(LOG_STR(func_name)), LOG_STR_ARG(noise_err_to_logstr(err_code)))
15
17 if (this->handshake_ != nullptr) {
18 noise_handshakestate_free(this->handshake_);
19 this->handshake_ = nullptr;
20 }
21}
22
23int NoiseResponderHandshake::init(const NoiseContext &ctx, const uint8_t *prologue, size_t prologue_len) {
24 if (this->handshake_ != nullptr) {
25 noise_handshakestate_free(this->handshake_);
26 this->handshake_ = nullptr;
27 }
28 // Noise_NNpsk0_25519_ChaChaPoly_SHA256, built on the stack:
29 // noise_handshakestate_new_by_id copies it, so a member would waste
30 // 104 bytes per connection, and a static const would sit in RAM on
31 // ESP8266 (.rodata is DRAM there).
32 const NoiseProtocolId nid = {
33 .prefix_id = NOISE_PREFIX_STANDARD,
34 .pattern_id = NOISE_PATTERN_NN,
35 .modifier_ids = {NOISE_MODIFIER_PSK0},
36 .dh_id = NOISE_DH_CURVE25519,
37 .cipher_id = NOISE_CIPHER_CHACHAPOLY,
38 .hash_id = NOISE_HASH_SHA256,
39 .hybrid_id = NOISE_DH_NONE,
40 .reserved = {},
41 };
42
43 int err = noise_handshakestate_new_by_id(&this->handshake_, &nid, NOISE_ROLE_RESPONDER);
44 if (err != 0) {
45 HANDSHAKE_STEP_LOG("noise_handshakestate_new_by_id", err);
46 return err;
47 }
48 // noise-c keeps its own copy, so the key only passes through the stack here
49 psk_t psk;
50 ctx.load_psk(psk);
51 err = noise_handshakestate_set_pre_shared_key(this->handshake_, psk.data(), psk.size());
52 if (err != 0) {
53 HANDSHAKE_STEP_LOG("noise_handshakestate_set_pre_shared_key", err);
54 return this->fail_init_(err);
55 }
56 err = noise_handshakestate_set_prologue(this->handshake_, prologue, prologue_len);
57 if (err != 0) {
58 HANDSHAKE_STEP_LOG("noise_handshakestate_set_prologue", err);
59 return this->fail_init_(err);
60 }
61#ifdef USE_NOISE_SPARE_EPHEMERAL
63 // Not fatal: the handshake generates its own key instead
64 if (err != 0) {
65 HANDSHAKE_STEP_LOG("noise_handshakestate_set_local_ephemeral", err);
66 }
67#endif
68 err = noise_handshakestate_start(this->handshake_);
69 if (err != 0) {
70 HANDSHAKE_STEP_LOG("noise_handshakestate_start", err);
71 return this->fail_init_(err);
72 }
73 return 0;
74}
75
79 noise_handshakestate_free(this->handshake_);
80 this->handshake_ = nullptr;
81 return err;
82}
83
85 if (this->handshake_ == nullptr) {
86 // A caller bug: init() was never called, or split() already released the state
87 ESP_LOGVV(TAG, "action() on uninitialized or split handshake");
89 }
90 int raw = noise_handshakestate_get_action(this->handshake_);
91 switch (raw) {
92 case NOISE_ACTION_READ_MESSAGE:
94 case NOISE_ACTION_WRITE_MESSAGE:
96 case NOISE_ACTION_SPLIT:
98 default:
99 // Preserve the raw code in debug logs; callers only see the collapsed enum
100 ESP_LOGVV(TAG, "Unexpected noise action %d", raw);
102 }
103}
104
105int NoiseResponderHandshake::read_message(uint8_t *data, size_t len) {
106 CpuFrequencyBoost boost;
107 NoiseBuffer mbuf;
108 noise_buffer_init(mbuf);
109 noise_buffer_set_input(mbuf, data, len);
110 return noise_handshakestate_read_message(this->handshake_, &mbuf, nullptr);
111}
112
113int NoiseResponderHandshake::write_message(uint8_t *out, size_t capacity, size_t &out_len) {
114 out_len = 0;
115 CpuFrequencyBoost boost;
116 NoiseBuffer mbuf;
117 noise_buffer_init(mbuf);
118 noise_buffer_set_output(mbuf, out, capacity);
119 int err = noise_handshakestate_write_message(this->handshake_, &mbuf, nullptr);
120 if (err == 0)
121 out_len = mbuf.size;
122 return err;
123}
124
125int NoiseResponderHandshake::split(NoiseCipherState *&send_cipher, NoiseCipherState *&recv_cipher) {
126 // Defined error postcondition: noise-c leaves the out-params unwritten on
127 // its early error returns, so a caller passing uninitialized locals must
128 // never see garbage to free
129 send_cipher = nullptr;
130 recv_cipher = nullptr;
131 int err = noise_handshakestate_split(this->handshake_, &send_cipher, &recv_cipher);
132 if (err != 0)
133 return err;
134 noise_handshakestate_free(this->handshake_);
135 this->handshake_ = nullptr;
136 return 0;
137}
138
139extern "C" {
140// noise-c's only randomness source (the vendored library compiles no rand of
141// its own); HWRNG backed. Lives in this TU so every handshake consumer links
142// it and the definition can never be dropped from the archive.
143void noise_rand_bytes(void *output, size_t len) {
144 if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
145 ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
146 arch_restart();
147 }
148}
149}
150
151} // namespace esphome::noise
152#endif // USE_NOISE
uint8_t raw[35]
Definition bl0939.h:0
Runs the CPU at 160 MHz while alive.
Definition helpers.h:2143
void load_psk(psk_t &out) const
Copy the key out (flash-aware on ESP8266); all zeros when none is set.
Definition noise.cpp:18
int write_message(uint8_t *out, size_t capacity, size_t &out_len)
Produce the next handshake message into out; out_len receives its size and is zero on error.
Action action() const
ACTION_FAILED is the catch-all: returned before init(), after split() has released the state,...
int init(const NoiseContext &ctx, const uint8_t *prologue, size_t prologue_len)
Create and start the handshake with the context's PSK and the prologue.
int fail_init_(int err)
Release a half-initialized state so a failed init() leaves the object as if init() was never called.
int read_message(uint8_t *data, size_t len)
Process one received handshake message.
int split(NoiseCipherState *&send_cipher, NoiseCipherState *&recv_cipher)
Hand out the transport ciphers and free the handshake state.
std::vector< uint8_t > & out
Definition gif.cpp:49
std::array< uint8_t, 32 > psk_t
Definition noise.h:14
void noise_rand_bytes(void *output, size_t len)
int consume_spare_ephemeral(NoiseHandshakeState *state)
Move the key pair into a handshake that has not started and wipe the slot; 0 unless noise-c rejects i...
Definition noise.cpp:46
bool random_bytes(uint8_t *data, size_t len)
Generate len random bytes using the platform's secure RNG (hardware RNG or OS CSPRNG).
Definition helpers.cpp:20
const void size_t len
Definition hal.h:64
void arch_restart()
Definition hal.cpp:36
uint16_t uint16_t & capacity
Definition helpers.cpp:33