ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
noise_handshake.cpp
Go to the documentation of this file.
1#include "noise_handshake.h"
2#ifdef USE_NOISE
4#include "esphome/core/hal.h"
6#include "esphome/core/log.h"
7
8namespace esphome::noise {
9
10ESPHOME_LOG_TAG(TAG, "noise");
11
12// Log the failing noise-c call at the same verbosity the api helper used
13// before this class existed; callers only see one collapsed error code.
14#define HANDSHAKE_STEP_LOG(func_name, err_code) \
15 ESP_LOGVV(TAG, "%s failed: %s", LOG_STR_ARG(LOG_STR(func_name)), LOG_STR_ARG(noise_err_to_logstr(err_code)))
16
18 if (this->handshake_ != nullptr) {
19 noise_handshakestate_free(this->handshake_);
20 this->handshake_ = nullptr;
21 }
22}
23
24int NoiseResponderHandshake::init(const NoiseContext &ctx, const uint8_t *prologue, size_t prologue_len) {
25 if (this->handshake_ != nullptr) {
26 noise_handshakestate_free(this->handshake_);
27 this->handshake_ = nullptr;
28 }
29 // Noise_NNpsk0_25519_ChaChaPoly_SHA256, built on the stack:
30 // noise_handshakestate_new_by_id copies it, so a member would waste
31 // 104 bytes per connection, and a static const would sit in RAM on
32 // ESP8266 (.rodata is DRAM there).
33 const NoiseProtocolId nid = {
34 .prefix_id = NOISE_PREFIX_STANDARD,
35 .pattern_id = NOISE_PATTERN_NN,
36 .modifier_ids = {NOISE_MODIFIER_PSK0},
37 .dh_id = NOISE_DH_CURVE25519,
38 .cipher_id = NOISE_CIPHER_CHACHAPOLY,
39 .hash_id = NOISE_HASH_SHA256,
40 .hybrid_id = NOISE_DH_NONE,
41 .reserved = {},
42 };
43
44 int err = noise_handshakestate_new_by_id(&this->handshake_, &nid, NOISE_ROLE_RESPONDER);
45 if (err != 0) {
46 HANDSHAKE_STEP_LOG("noise_handshakestate_new_by_id", err);
47 return err;
48 }
49 // noise-c keeps its own copy, so the key only passes through the stack here
50 psk_t psk;
51 ctx.load_psk(psk);
52 err = noise_handshakestate_set_pre_shared_key(this->handshake_, psk.data(), psk.size());
53 if (err != 0) {
54 HANDSHAKE_STEP_LOG("noise_handshakestate_set_pre_shared_key", err);
55 return this->fail_init_(err);
56 }
57 err = noise_handshakestate_set_prologue(this->handshake_, prologue, prologue_len);
58 if (err != 0) {
59 HANDSHAKE_STEP_LOG("noise_handshakestate_set_prologue", err);
60 return this->fail_init_(err);
61 }
62#ifdef USE_NOISE_SPARE_EPHEMERAL
64 // Not fatal: the handshake generates its own key instead
65 if (err != 0) {
66 HANDSHAKE_STEP_LOG("noise_handshakestate_set_local_ephemeral", err);
67 }
68#endif
69 err = noise_handshakestate_start(this->handshake_);
70 if (err != 0) {
71 HANDSHAKE_STEP_LOG("noise_handshakestate_start", err);
72 return this->fail_init_(err);
73 }
74 return 0;
75}
76
80 noise_handshakestate_free(this->handshake_);
81 this->handshake_ = nullptr;
82 return err;
83}
84
86 if (this->handshake_ == nullptr) {
87 // A caller bug: init() was never called, or split() already released the state
88 ESP_LOGVV(TAG, "action() on uninitialized or split handshake");
90 }
91 int raw = noise_handshakestate_get_action(this->handshake_);
92 switch (raw) {
93 case NOISE_ACTION_READ_MESSAGE:
95 case NOISE_ACTION_WRITE_MESSAGE:
97 case NOISE_ACTION_SPLIT:
99 default:
100 // Preserve the raw code in debug logs; callers only see the collapsed enum
101 ESP_LOGVV(TAG, "Unexpected noise action %d", raw);
103 }
104}
105
106int NoiseResponderHandshake::read_message(uint8_t *data, size_t len) {
107 CpuFrequencyBoost boost;
108 NoiseBuffer mbuf;
109 noise_buffer_init(mbuf);
110 noise_buffer_set_input(mbuf, data, len);
111 return noise_handshakestate_read_message(this->handshake_, &mbuf, nullptr);
112}
113
114int NoiseResponderHandshake::write_message(uint8_t *out, size_t capacity, size_t &out_len) {
115 out_len = 0;
116 // The key agreement cannot be split across loop passes
118 CpuFrequencyBoost boost;
119 NoiseBuffer mbuf;
120 noise_buffer_init(mbuf);
121 noise_buffer_set_output(mbuf, out, capacity);
122 int err = noise_handshakestate_write_message(this->handshake_, &mbuf, nullptr);
123 if (err == 0)
124 out_len = mbuf.size;
125 return err;
126}
127
128int NoiseResponderHandshake::split(NoiseCipherState *&send_cipher, NoiseCipherState *&recv_cipher) {
129 // Defined error postcondition: noise-c leaves the out-params unwritten on
130 // its early error returns, so a caller passing uninitialized locals must
131 // never see garbage to free
132 send_cipher = nullptr;
133 recv_cipher = nullptr;
134 int err = noise_handshakestate_split(this->handshake_, &send_cipher, &recv_cipher);
135 if (err != 0)
136 return err;
137 noise_handshakestate_free(this->handshake_);
138 this->handshake_ = nullptr;
139 return 0;
140}
141
142extern "C" {
143// noise-c's only randomness source (the vendored library compiles no rand of
144// its own); HWRNG backed. Lives in this TU so every handshake consumer links
145// it and the definition can never be dropped from the archive.
146void noise_rand_bytes(void *output, size_t len) {
147 if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
148 ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
149 arch_restart();
150 }
151}
152}
153
154} // namespace esphome::noise
155#endif // USE_NOISE
uint8_t raw[35]
Definition bl0939.h:0
Runs the CPU at 160 MHz while alive.
Definition helpers.h:2136
Leaves a stretch of the current loop pass out of the blocking warning.
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
ESPHOME_LOG_TAG(TAG, "noise")
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:68
void arch_restart()
Definition hal.cpp:36
uint16_t uint16_t & capacity
Definition helpers.cpp:34