ESPHome 2026.9.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
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 psk_t &psk, 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 err = noise_handshakestate_set_pre_shared_key(this->handshake_, psk.data(), psk.size());
48 if (err != 0) {
49 HANDSHAKE_STEP_LOG("noise_handshakestate_set_pre_shared_key", err);
50 return this->fail_init_(err);
51 }
52 err = noise_handshakestate_set_prologue(this->handshake_, prologue, prologue_len);
53 if (err != 0) {
54 HANDSHAKE_STEP_LOG("noise_handshakestate_set_prologue", err);
55 return this->fail_init_(err);
56 }
57 err = noise_handshakestate_start(this->handshake_);
58 if (err != 0) {
59 HANDSHAKE_STEP_LOG("noise_handshakestate_start", err);
60 return this->fail_init_(err);
61 }
62 return 0;
63}
64
68 noise_handshakestate_free(this->handshake_);
69 this->handshake_ = nullptr;
70 return err;
71}
72
74 if (this->handshake_ == nullptr) {
75 // A caller bug: init() was never called, or split() already released the state
76 ESP_LOGVV(TAG, "action() on uninitialized or split handshake");
78 }
79 int raw = noise_handshakestate_get_action(this->handshake_);
80 switch (raw) {
81 case NOISE_ACTION_READ_MESSAGE:
83 case NOISE_ACTION_WRITE_MESSAGE:
85 case NOISE_ACTION_SPLIT:
87 default:
88 // Preserve the raw code in debug logs; callers only see the collapsed enum
89 ESP_LOGVV(TAG, "Unexpected noise action %d", raw);
91 }
92}
93
94int NoiseResponderHandshake::read_message(uint8_t *data, size_t len) {
95 NoiseBuffer mbuf;
96 noise_buffer_init(mbuf);
97 noise_buffer_set_input(mbuf, data, len);
98 return noise_handshakestate_read_message(this->handshake_, &mbuf, nullptr);
99}
100
101int NoiseResponderHandshake::write_message(uint8_t *out, size_t capacity, size_t &out_len) {
102 out_len = 0;
103 NoiseBuffer mbuf;
104 noise_buffer_init(mbuf);
105 noise_buffer_set_output(mbuf, out, capacity);
106 int err = noise_handshakestate_write_message(this->handshake_, &mbuf, nullptr);
107 if (err == 0)
108 out_len = mbuf.size;
109 return err;
110}
111
112int NoiseResponderHandshake::split(NoiseCipherState *&send_cipher, NoiseCipherState *&recv_cipher) {
113 // Defined error postcondition: noise-c leaves the out-params unwritten on
114 // its early error returns, so a caller passing uninitialized locals must
115 // never see garbage to free
116 send_cipher = nullptr;
117 recv_cipher = nullptr;
118 int err = noise_handshakestate_split(this->handshake_, &send_cipher, &recv_cipher);
119 if (err != 0)
120 return err;
121 noise_handshakestate_free(this->handshake_);
122 this->handshake_ = nullptr;
123 return 0;
124}
125
126extern "C" {
127// noise-c's only randomness source (the vendored library compiles no rand of
128// its own); HWRNG backed. Lives in this TU so every handshake consumer links
129// it and the definition can never be dropped from the archive.
130void noise_rand_bytes(void *output, size_t len) {
131 if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
132 ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
133 arch_restart();
134 }
135}
136}
137
138} // namespace esphome::noise
139#endif // USE_NOISE
uint8_t raw[35]
Definition bl0939.h:0
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 psk_t &psk, const uint8_t *prologue, size_t prologue_len)
Create and start the handshake with the given PSK and 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