ESPHome 2026.9.0
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 };
41
42 int err = noise_handshakestate_new_by_id(&this->handshake_, &nid, NOISE_ROLE_RESPONDER);
43 if (err != 0) {
44 HANDSHAKE_STEP_LOG("noise_handshakestate_new_by_id", err);
45 return err;
46 }
47 // noise-c keeps its own copy, so the key only passes through the stack here
48 psk_t psk;
49 ctx.load_psk(psk);
50 err = noise_handshakestate_set_pre_shared_key(this->handshake_, psk.data(), psk.size());
51 if (err != 0) {
52 HANDSHAKE_STEP_LOG("noise_handshakestate_set_pre_shared_key", err);
53 return this->fail_init_(err);
54 }
55 err = noise_handshakestate_set_prologue(this->handshake_, prologue, prologue_len);
56 if (err != 0) {
57 HANDSHAKE_STEP_LOG("noise_handshakestate_set_prologue", err);
58 return this->fail_init_(err);
59 }
60 err = noise_handshakestate_start(this->handshake_);
61 if (err != 0) {
62 HANDSHAKE_STEP_LOG("noise_handshakestate_start", err);
63 return this->fail_init_(err);
64 }
65 return 0;
66}
67
71 noise_handshakestate_free(this->handshake_);
72 this->handshake_ = nullptr;
73 return err;
74}
75
77 if (this->handshake_ == nullptr) {
78 // A caller bug: init() was never called, or split() already released the state
79 ESP_LOGVV(TAG, "action() on uninitialized or split handshake");
81 }
82 int raw = noise_handshakestate_get_action(this->handshake_);
83 switch (raw) {
84 case NOISE_ACTION_READ_MESSAGE:
86 case NOISE_ACTION_WRITE_MESSAGE:
88 case NOISE_ACTION_SPLIT:
90 default:
91 // Preserve the raw code in debug logs; callers only see the collapsed enum
92 ESP_LOGVV(TAG, "Unexpected noise action %d", raw);
94 }
95}
96
97int NoiseResponderHandshake::read_message(uint8_t *data, size_t len) {
98 NoiseBuffer mbuf;
99 noise_buffer_init(mbuf);
100 noise_buffer_set_input(mbuf, data, len);
101 return noise_handshakestate_read_message(this->handshake_, &mbuf, nullptr);
102}
103
104int NoiseResponderHandshake::write_message(uint8_t *out, size_t capacity, size_t &out_len) {
105 out_len = 0;
106 NoiseBuffer mbuf;
107 noise_buffer_init(mbuf);
108 noise_buffer_set_output(mbuf, out, capacity);
109 int err = noise_handshakestate_write_message(this->handshake_, &mbuf, nullptr);
110 if (err == 0)
111 out_len = mbuf.size;
112 return err;
113}
114
115int NoiseResponderHandshake::split(NoiseCipherState *&send_cipher, NoiseCipherState *&recv_cipher) {
116 // Defined error postcondition: noise-c leaves the out-params unwritten on
117 // its early error returns, so a caller passing uninitialized locals must
118 // never see garbage to free
119 send_cipher = nullptr;
120 recv_cipher = nullptr;
121 int err = noise_handshakestate_split(this->handshake_, &send_cipher, &recv_cipher);
122 if (err != 0)
123 return err;
124 noise_handshakestate_free(this->handshake_);
125 this->handshake_ = nullptr;
126 return 0;
127}
128
129extern "C" {
130// noise-c's only randomness source (the vendored library compiles no rand of
131// its own); HWRNG backed. Lives in this TU so every handshake consumer links
132// it and the definition can never be dropped from the archive.
133void noise_rand_bytes(void *output, size_t len) {
134 if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
135 ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
136 arch_restart();
137 }
138}
139}
140
141} // namespace esphome::noise
142#endif // USE_NOISE
uint8_t raw[35]
Definition bl0939.h:0
void load_psk(psk_t &out) const
Copy the key out (flash-aware on ESP8266); all zeros when none is set.
Definition noise.cpp:19
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::array< uint8_t, 32 > psk_t
Definition noise.h:11
void noise_rand_bytes(void *output, size_t len)
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:39
uint16_t uint16_t & capacity
Definition helpers.cpp:25