1use crate::frame::construct_frame;
2use crate::frame::to_encrypted_frame;
3use crate::frame::to_unencrypted_frame;
4use crate::packet_encrypted;
5use crate::parser;
6use crate::parser::ProtoMessage;
7use crate::proto::version_2025_12_1::AuthenticationResponse;
8use crate::proto::version_2025_12_1::DeviceInfoResponse;
9use crate::proto::version_2025_12_1::DisconnectResponse;
10use crate::proto::version_2025_12_1::HelloResponse;
11use crate::proto::version_2025_12_1::PingResponse;
12use base64::prelude::*;
13use byteorder::BigEndian;
14use byteorder::ByteOrder;
15use constant_time_eq::constant_time_eq;
16use log::debug;
17use log::error;
18use log::info;
19use log::trace;
20use log::warn;
21use noise_protocol::CipherState;
22use noise_protocol::ErrorKind;
23use noise_protocol::HandshakeState;
24use noise_protocol::patterns::noise_nn_psk0;
25use noise_rust_crypto::ChaCha20Poly1305;
26use noise_rust_crypto::Sha256;
27use noise_rust_crypto::X25519;
28use std::sync::Arc;
29use std::sync::atomic::AtomicBool;
30use tokio::io::{AsyncReadExt, AsyncWriteExt};
31use tokio::net::TcpStream;
32use tokio::sync::Mutex;
33use tokio::sync::broadcast;
34use typed_builder::TypedBuilder;
35
36#[derive(Debug)]
37pub(crate) enum EncryptionState {
38 Uninitialized,
39 ClientHandshake,
40 ServerHello,
41 Initialized,
42 Failure,
43}
44
45#[derive(TypedBuilder)]
46pub struct EspHomeApi {
47 #[builder(default=Arc::new(AtomicBool::new(false)))]
49 pub(crate) password_authenticated: Arc<AtomicBool>,
50 #[builder(default=Arc::new(AtomicBool::new(false)))]
51 pub(crate) key_authenticated: Arc<AtomicBool>,
52
53 #[builder(default=Arc::new(AtomicBool::new(false)))]
54 pub(crate) encrypted_api: Arc<AtomicBool>,
55
56 #[builder(default=Arc::new(Mutex::new(EncryptionState::Uninitialized)))]
57 pub(crate) encryption_state: Arc<Mutex<EncryptionState>>,
58
59 #[builder(default=Arc::new(Mutex::new(None)), setter(skip))]
60 pub(crate) handshake_state:
61 Arc<Mutex<Option<HandshakeState<X25519, ChaCha20Poly1305, Sha256>>>>,
62 #[builder(default=Arc::new(Mutex::new(None)), setter(skip))]
63 pub(crate) encrypt_cypher: Arc<Mutex<Option<CipherState<ChaCha20Poly1305>>>>,
64 #[builder(default=Arc::new(Mutex::new(None)), setter(skip))]
65 pub(crate) decrypt_cypher: Arc<Mutex<Option<CipherState<ChaCha20Poly1305>>>>,
66
67 name: String,
68
69 #[builder(default = None, setter(strip_option(fallback=password_opt)))]
70 #[deprecated(note = "https://esphome.io/components/api.html#configuration-variables")]
71 password: Option<String>,
72 #[builder(default = None, setter(strip_option(fallback=encryption_key_opt)))]
73 encryption_key: Option<String>,
74
75 #[builder(default = 1)]
76 api_version_major: u32,
77 #[builder(default = 10)]
78 api_version_minor: u32,
79 #[builder(default="Rust: esphome-native-api".to_string())]
80 server_info: String,
81
82 #[builder(default = None, setter(strip_option(fallback=friendly_name_opt)))]
83 friendly_name: Option<String>,
84
85 #[builder(default = None, setter(strip_option(fallback=mac_opt)))]
86 mac: Option<String>,
87
88 #[builder(default = None, setter(strip_option(fallback=model_opt)))]
89 model: Option<String>,
90
91 #[builder(default = None, setter(strip_option(fallback=manufacturer_opt)))]
92 manufacturer: Option<String>,
93 #[builder(default = None, setter(strip_option(fallback=suggested_area_opt)))]
94 suggested_area: Option<String>,
95 #[builder(default = None, setter(strip_option(fallback=bluetooth_mac_address_opt)))]
96 bluetooth_mac_address: Option<String>,
97
98 #[builder(default = None, setter(strip_option(fallback=project_name_opt)))]
99 project_name: Option<String>,
100
101 #[builder(default = None, setter(strip_option(fallback=project_version_opt)))]
102 project_version: Option<String>,
103 #[builder(default = None, setter(strip_option(fallback=compilation_time_opt)))]
104 compilation_time: Option<String>,
105
106 #[builder(default = 0)]
107 legacy_bluetooth_proxy_version: u32,
108 #[builder(default = 0)]
109 bluetooth_proxy_feature_flags: u32,
110 #[builder(default = 0)]
111 legacy_voice_assistant_version: u32,
112 #[builder(default = 0)]
113 voice_assistant_feature_flags: u32,
114
115 #[builder(default = "2025.4.0".to_string())]
116 esphome_version: String,
117}
118
119impl EspHomeApi {
121 pub async fn start(
124 &mut self,
125 tcp_stream: TcpStream,
126 ) -> Result<
127 (
128 broadcast::Sender<ProtoMessage>,
129 broadcast::Receiver<ProtoMessage>,
130 ),
131 Box<dyn std::error::Error>,
132 > {
133 if self.password.is_none() && self.encryption_key.is_none() {
134 self.password_authenticated
135 .store(true, std::sync::atomic::Ordering::Relaxed);
136 }
137
138 let (answer_messages_tx, mut answer_messages_rx) = broadcast::channel::<ProtoMessage>(16);
140 let (messages_tx, mut messages_rx) = broadcast::channel::<ProtoMessage>(16);
142 let (outgoing_messages_tx, mut outgoing_messages_rx) =
143 broadcast::channel::<ProtoMessage>(16);
144
145 let (mut read, mut write) = tcp_stream.into_split();
147
148 let device_info = DeviceInfoResponse {
149 api_encryption_supported: self.encryption_key.is_some(),
150 uses_password: self.password.is_some(),
151 name: self.name.clone(),
152 mac_address: self.mac.clone().unwrap_or_default(),
153 esphome_version: self.esphome_version.clone(),
154 compilation_time: self.compilation_time.clone().unwrap_or_default(),
155 model: self.model.clone().unwrap_or_default(),
156 has_deep_sleep: false,
157 project_name: self.project_name.clone().unwrap_or_default(),
158 project_version: self.project_version.clone().unwrap_or_default(),
159 webserver_port: 0,
160 legacy_bluetooth_proxy_version: self.legacy_bluetooth_proxy_version,
162 bluetooth_proxy_feature_flags: self.bluetooth_proxy_feature_flags,
163 manufacturer: self.manufacturer.clone().unwrap_or_default(),
164 friendly_name: self.friendly_name.clone().unwrap_or(self.name.clone()),
165 legacy_voice_assistant_version: self.legacy_voice_assistant_version,
166 voice_assistant_feature_flags: self.voice_assistant_feature_flags,
167 suggested_area: self.suggested_area.clone().unwrap_or_default(),
168 bluetooth_mac_address: self.bluetooth_mac_address.clone().unwrap_or_default(),
169 areas: vec![],
170 devices: vec![],
171 area: None,
172 zwave_proxy_feature_flags:0,
173 zwave_home_id: 0
174 };
175
176 if self.encryption_key.is_some() {
177 debug!("Encryption enabled");
178 self.encrypted_api
179 .store(true, std::sync::atomic::Ordering::Relaxed);
180 }
181
182 let hello_response = HelloResponse {
183 api_version_major: self.api_version_major,
184 api_version_minor: self.api_version_minor,
185 server_info: self.server_info.clone(),
186 name: self.name.clone(),
187 };
188 let password_clone = self.password.clone();
189 let encrypted_api = self.encrypted_api.clone();
190 let encrypt_cypher_clone = self.encrypt_cypher.clone();
191 let decrypt_cypher_clone = self.decrypt_cypher.clone();
192 let encryption_state = self.encryption_state.clone();
193 let handshake_state_clone = self.handshake_state.clone();
194 let answer_messages_tx_clone = answer_messages_tx.clone();
195 let messages_tx_clone = messages_tx.clone();
196 tokio::spawn(async move {
197 tokio::select! {
198 _ = answer_messages_tx_clone.closed() => {
199 info!("CLOSED");
200 }
201 _ = messages_tx_clone.closed() => {
202 info!("CLOSED");
203 }
204 };
205 });
206
207 tokio::spawn(async move {
209 let mut disconnect = false;
210 loop {
211 let mut answer_buf: Vec<u8> = vec![];
212 let answer_message: ProtoMessage;
213 let encryption = encrypted_api.load(std::sync::atomic::Ordering::Relaxed);
214
215 let mut initialized = false;
217 {
218 let mut encryption_state_changer = encryption_state.lock().await;
219 match *encryption_state_changer {
220 EncryptionState::Initialized => {
221 initialized = true;
222 }
223 _ => {
224 initialized = true;
225 }
226 }
227 }
228
229 if encryption && !initialized {
231 answer_message = answer_messages_rx.recv().await.unwrap();
232 } else {
233 tokio::select! {
234 biased; message = answer_messages_rx.recv() => {
236 answer_message = message.unwrap();
237 }
238 message = messages_rx.recv() => {
239 answer_message = message.unwrap();
240 }
241 };
242 }
243
244 if encryption {
245 {
246 let mut first_run = false;
247 let mut encryption_state_changer = encryption_state.lock().await;
248 match *encryption_state_changer {
249 EncryptionState::ClientHandshake => {
250 let mut message_server_hello: Vec<u8> = Vec::new();
251
252 let encryption_protocol: Vec<u8> = vec![1];
253 let node_name = b"test_node";
254 let node_mac_address = b"00:00:00:00:00:01";
255 message_server_hello.extend(encryption_protocol);
256 message_server_hello.extend(node_name);
257 message_server_hello.extend(b"\0");
258 message_server_hello.extend(node_mac_address);
259 message_server_hello.extend(b"\0");
260
261 let len_u16 = message_server_hello.len() as u16;
262 let len_bytes = len_u16.to_be_bytes();
263 let length: Vec<u8> = vec![len_bytes[0], len_bytes[1]];
264
265 let mut hello_frame = vec![1];
266 hello_frame.extend(length);
267 hello_frame.extend(message_server_hello);
268
269 debug!("Sending server hello: {:02X?}", &hello_frame);
270 write
271 .write_all(&hello_frame)
272 .await
273 .expect("failed to write encrypted response");
274 write.flush().await.expect("failed to flush server hello");
275
276 *encryption_state_changer = EncryptionState::ServerHello;
277 first_run = true;
278 }
279 _ => {}
280 }
281
282 match *encryption_state_changer {
283 EncryptionState::ServerHello => {
284 let out: Vec<u8>;
285 let mut handshake_state_change = handshake_state_clone.lock().await;
286
287 if handshake_state_change.is_none() {
288 *encryption_state_changer = EncryptionState::Failure;
289 } else {
290 let handshake_state =
291 (*handshake_state_change).as_mut().unwrap();
292
293 out = handshake_state.write_message_vec(b"").unwrap();
294 {
295 let mut encrypt_cipher_changer =
296 encrypt_cypher_clone.lock().await;
297 let mut decrypt_cipher_changer =
298 decrypt_cypher_clone.lock().await;
299 let (decrypt_cipher, encrypt_cipher) =
300 handshake_state.get_ciphers();
301 *encrypt_cipher_changer = Some(encrypt_cipher);
302 *decrypt_cipher_changer = Some(decrypt_cipher);
303 }
304
305 let mut message_handshake = vec![0];
306 message_handshake.extend(out);
307
308 let len_u16 = message_handshake.len() as u16;
309 let len_bytes = len_u16.to_be_bytes();
310 let length: Vec<u8> = vec![len_bytes[0], len_bytes[1]];
311
312 let mut encrypted_frame = vec![1];
313 encrypted_frame.extend(length);
314 encrypted_frame.extend(message_handshake);
315
316 debug!("Sending handshake: {:02X?}", &encrypted_frame);
317 write
318 .write_all(&encrypted_frame)
319 .await
320 .expect("failed to write encrypted response");
321
322 *encryption_state_changer = EncryptionState::Initialized;
323 }
324 }
325 _ => {}
326 }
327 match *encryption_state_changer {
328 EncryptionState::Initialized => {
329 if first_run {
330 continue;
331 }
332 debug!("Answer message: {:?}", answer_message);
333 {
335 let mut encrypt_cipher_changer =
336 encrypt_cypher_clone.lock().await;
337 let encrypted_frame = to_encrypted_frame(
338 &answer_message,
339 &mut *encrypt_cipher_changer.as_mut().unwrap(),
340 )
341 .unwrap();
342
343 answer_buf = [answer_buf, encrypted_frame].concat();
344 }
345 }
346 _ => {
347 let packet = [
348 [1].to_vec(),
349 "Only key encryption is enabled".as_bytes().to_vec(),
350 ]
351 .concat();
352 answer_buf = construct_frame(&packet, true).unwrap();
353 disconnect = true;
354 }
355 }
356 match *encryption_state_changer {
357 EncryptionState::Failure => {
358 error!("Encrypted API Failure. Disconnecting.");
359 let packet =
360 [[1].to_vec(), "Handshake MAC failure".as_bytes().to_vec()]
361 .concat();
362 answer_buf = construct_frame(&packet, true).unwrap();
363 disconnect = true;
364 }
366 _ => {}
367 }
368 }
369 } else {
370 debug!("Answer message: {:?}", answer_message);
371 answer_buf =
372 [answer_buf, to_unencrypted_frame(&answer_message).unwrap()].concat();
373 }
374
375 match answer_message {
376 ProtoMessage::DisconnectResponse(_) => {
377 disconnect = true;
378 }
379 _ => {}
380 }
381
382 trace!("TCP Send: {:02X?}", &answer_buf);
383
384 match write.write_all(&answer_buf).await {
385 Err(err) => {
386 error!("Failed to write data to socket: {:?}", err);
387 break;
388 }
389 _ => {}
390 }
391
392 write.flush().await.expect("failed to flush data to socket");
393
394 if disconnect {
395 debug!("Disconnecting");
396 match write.shutdown().await {
397 Err(err) => {
398 error!("failed to shutdown socket: {:?}", err);
399 break;
400 },
401 _ => break
402 }
403 }
404 }
405 });
406
407 let answer_messages_tx_clone = answer_messages_tx.clone();
409 let handshake_state_clone = self.handshake_state.clone();
410 let encryption_state = self.encryption_state.clone();
411 let encryption_key = self.encryption_key.clone();
412 let encrypted_api = self.encrypted_api.clone();
413 let decrypt_cypher_clone = self.decrypt_cypher.clone();
414 let password_authenticated = self.password_authenticated.clone();
415 let key_authenticated = self.key_authenticated.clone();
416
417 tokio::spawn(async move {
419 let mut buf = vec![0; 1024];
420
421 loop {
422 let n = read
423 .read(&mut buf)
424 .await
425 .expect("failed to read data from socket");
426
427 if n == 0 {
428 return;
429 }
430
431 trace!("TCP Receive: {:02X?}", &buf[0..n]);
432
433 let mut cursor = 0;
434
435 while cursor < n {
436 let message;
440 let preamble = buf[cursor] as usize;
441 trace!("Cursor: {:?}", &cursor);
442 match preamble {
445 0 => {
446 trace!("Cleartext message");
447 let len = buf[cursor + 1] as usize;
451 message = cleartext_frame_to_message(
452 &buf[cursor + 2..cursor + 3 + len].to_vec(),
453 )
454 .unwrap();
455 cursor += 3 + len;
456
457 match &message {
458 ProtoMessage::HelloRequest(hello_request) => {
459 debug!("HelloRequest: {:?}", hello_request);
460
461 answer_messages_tx_clone
462 .send(ProtoMessage::HelloResponse(hello_response.clone()))
463 .unwrap();
464 continue;
465 }
466 _ => {}
467 }
468 }
469 1 => {
470 trace!("Encrypted message");
471
472 let mut encryption_state_changer = encryption_state.lock().await;
473 match *encryption_state_changer {
474 EncryptionState::Uninitialized => {
475 trace!("Encryption State: Uninitialized");
476
477 encrypted_api.store(true, std::sync::atomic::Ordering::Relaxed);
478
479 let mut handshake_state: HandshakeState<
481 X25519,
482 ChaCha20Poly1305,
483 Sha256,
484 > = HandshakeState::new(
485 noise_nn_psk0(),
486 false,
487 b"NoiseAPIInit\0\0",
489 None,
490 None,
491 None,
492 None,
493 );
494
495 encrypted_api.store(true, std::sync::atomic::Ordering::Relaxed);
496 let noise_psk = BASE64_STANDARD
497 .decode(encryption_key.as_ref().unwrap())
498 .unwrap();
499
500 handshake_state.push_psk(&noise_psk);
501 match handshake_state.read_message_vec(&buf[3 + 3 + 1..n]) {
502 Ok(_) => {
503 {
504 let mut mutex_changer =
505 handshake_state_clone.lock().await;
506 *mutex_changer = Option::Some(handshake_state);
507 }
508 }
509 Err(e) => {
510 match e.kind() {
511 ErrorKind::Decryption => {
513 warn!("Decryption failed: {}", e);
514 }
515 _ => {
516 debug!("Failed to read message: {}", e);
517 }
518 }
519 }
520 }
521
522 answer_messages_tx_clone
524 .send(ProtoMessage::HelloResponse(
525 hello_response.clone(),
526 ))
527 .unwrap();
528 *encryption_state_changer =
529 EncryptionState::ClientHandshake;
530 cursor += n;
531 continue;
532 }
533 EncryptionState::Initialized => {
534 trace!("Encryption State: Initialized");
535 let len =
536 BigEndian::read_u16(&buf[cursor + 1..cursor + 3]) as usize;
537 let decrypted_message = &buf[cursor + 3..cursor + len + 3];
539 {
541 let mut decrypt_cipher_changer =
542 decrypt_cypher_clone.lock().await;
543 message = packet_encrypted::packet_to_message(
544 decrypted_message,
545 &mut *decrypt_cipher_changer.as_mut().unwrap(),
546 )
547 .unwrap();
548 }
549 key_authenticated
550 .store(true, std::sync::atomic::Ordering::Relaxed);
551
552 cursor += 3 + len;
553 }
554 _ => {
555 debug!(
556 "Wrong encryption state: {:?}",
557 *encryption_state_changer
558 );
559 return;
560 }
561 }
562 }
563 _ => {
564 debug!("Marker byte invalid: {}", preamble);
565 return;
566 }
567 }
568
569 match &message {
571 ProtoMessage::HelloRequest(_) => {
572 answer_messages_tx_clone
573 .send(ProtoMessage::HelloResponse(
574 hello_response.clone(),
575 ))
576 .unwrap();
577 }
578 ProtoMessage::AuthenticationRequest(connect_request) => {
579 debug!("AuthenticationRequest: {:?}", connect_request);
580 let mut valid = false;
581 if encryption_key.is_some() {
582 valid = true;
583 } else {
584 if let Some(password) = password_clone.clone() {
585 valid = constant_time_eq(
586 connect_request.password.as_bytes(),
587 password.as_bytes(),
588 );
589 }
590 }
591
592 password_authenticated
593 .store(valid, std::sync::atomic::Ordering::Relaxed);
594 let response_message = AuthenticationResponse {
595 invalid_password: !valid,
596 };
597 debug!("AuthenticationResponse: {:?}", response_message);
598 answer_messages_tx_clone
599 .send(ProtoMessage::AuthenticationResponse(response_message))
600 .unwrap();
601 continue;
602 }
603 ProtoMessage::DisconnectRequest(disconnect_request) => {
604 debug!("DisconnectRequest: {:?}", disconnect_request);
605 let response_message = DisconnectResponse {};
606 answer_messages_tx_clone
607 .send(ProtoMessage::DisconnectResponse(response_message))
608 .unwrap();
609 continue;
610 }
611 _ => {}
612 }
613 let auth_test = key_authenticated.load(std::sync::atomic::Ordering::Relaxed)
614 || password_authenticated.load(std::sync::atomic::Ordering::Relaxed);
615 info!("Authenticated: {}", auth_test);
616
617 if !auth_test {
618 answer_messages_tx_clone
619 .send(ProtoMessage::DisconnectResponse(DisconnectResponse {}))
620 .unwrap();
621 continue;
622 }
623
624 match &message {
626 ProtoMessage::PingRequest(ping_request) => {
627 debug!("PingRequest: {:?}", ping_request);
628 let response_message = PingResponse {};
629 answer_messages_tx_clone
630 .send(ProtoMessage::PingResponse(response_message))
631 .unwrap();
632 }
633 ProtoMessage::DeviceInfoRequest(device_info_request) => {
634 debug!("DeviceInfoRequest: {:?}", device_info_request);
635 answer_messages_tx_clone
636 .send(ProtoMessage::DeviceInfoResponse(device_info.clone()))
637 .unwrap();
638 }
639 ProtoMessage::HelloRequest(_) => {}
641 message => {
642 outgoing_messages_tx.send(message.clone()).unwrap();
643 }
644 }
645 }
646 }
647 });
648
649 Ok((messages_tx.clone(), outgoing_messages_rx))
650 }
651}
652
653#[cfg(test)]
654mod tests {
655 use super::*;
657
658 #[test]
659 fn test_basic_server_instantiation() {
660 EspHomeApi::builder()
661 .name("test_device".to_string())
662 .build();
663 }
664}
665
666pub fn cleartext_frame_to_message(
667 buffer: &[u8],
668) -> Result<ProtoMessage, Box<dyn std::error::Error>> {
669 let message_type = buffer[0] as usize;
670 let packet_content = &buffer[1..];
671 debug!("Message type: {}", message_type);
672 debug!("Message: {:02X?}", packet_content);
673 Ok(parser::parse_proto_message(message_type, &packet_content).unwrap())
674}