1use base64::prelude::*;
49use futures::sink::SinkExt;
50use log::debug;
51use log::error;
52use log::info;
53use log::trace;
54use noise_protocol::CipherState;
55use noise_protocol::ErrorKind;
56use noise_protocol::HandshakeState;
57use noise_protocol::patterns::noise_nn_psk0;
58use noise_rust_crypto::ChaCha20Poly1305;
59use noise_rust_crypto::Sha256;
60use noise_rust_crypto::X25519;
61use std::sync::Arc;
62use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
63use tokio::sync::Mutex;
64use tokio::sync::broadcast;
65use tokio::sync::mpsc;
66use tokio::sync::oneshot;
67use tokio_stream::StreamExt;
68use tokio_util::codec::FramedRead;
69use tokio_util::codec::FramedWrite;
70use typed_builder::TypedBuilder;
71
72use crate::frame::FrameCodec;
73use crate::packet_encrypted;
74use crate::packet_plaintext;
75use crate::parser::ProtoMessage;
76use crate::proto::{
77 self, AuthenticationResponse, DeviceInfoResponse, DisconnectResponse, HelloResponse,
78 PingResponse,
79};
80
81async fn write_error_and_disconnect<W>(mut writer: FramedWrite<W, FrameCodec>, message: &str)
82where
83 W: AsyncWrite + Unpin,
84{
85 error!("API Failure: {}. Disconnecting.", message);
86 let packet = [[1].to_vec(), message.as_bytes().to_vec()].concat();
87 writer.send(packet).await.unwrap();
88 writer.flush().await.unwrap();
89 let mut tcp_write = writer.into_inner();
90 if let Err(err) = tcp_write.shutdown().await {
91 error!("failed to shutdown socket: {:?}", err);
92 }
93}
94
95const ERROR_ONLY_ENCRYPTED: &str = "Only key encryption is enabled";
96const ERROR_HANDSHAKE_MAC_FAILURE: &str = "Handshake MAC failure";
97
98#[derive(TypedBuilder, Clone)]
134pub struct EspHomeApi {
135 name: String,
137
138 #[builder(default = None, setter(strip_option(fallback=encryption_key_opt)))]
139 encryption_key: Option<String>,
140
141 #[builder(default = 1)]
142 api_version_major: u32,
143 #[builder(default = 10)]
144 api_version_minor: u32,
145 #[builder(default="Rust: esphome-native-api".to_string())]
146 server_info: String,
147
148 #[builder(default = None, setter(strip_option(fallback=friendly_name_opt)))]
149 friendly_name: Option<String>,
150
151 #[builder(default = None, setter(strip_option(fallback=mac_opt)))]
152 mac: Option<String>,
153
154 #[builder(default = None, setter(strip_option(fallback=model_opt)))]
155 model: Option<String>,
156
157 #[builder(default = None, setter(strip_option(fallback=manufacturer_opt)))]
158 manufacturer: Option<String>,
159 #[builder(default = None, setter(strip_option(fallback=suggested_area_opt)))]
160 suggested_area: Option<String>,
161 #[builder(default = None, setter(strip_option(fallback=bluetooth_mac_address_opt)))]
162 bluetooth_mac_address: Option<String>,
163
164 #[builder(default = None, setter(strip_option(fallback=project_name_opt)))]
165 project_name: Option<String>,
166
167 #[builder(default = None, setter(strip_option(fallback=project_version_opt)))]
168 project_version: Option<String>,
169 #[builder(default = None, setter(strip_option(fallback=compilation_time_opt)))]
170 compilation_time: Option<String>,
171
172 #[builder(default = 0)]
173 legacy_bluetooth_proxy_version: u32,
174 #[builder(default = 0)]
175 bluetooth_proxy_feature_flags: u32,
176 #[builder(default = 0)]
177 legacy_voice_assistant_version: u32,
178 #[builder(default = 0)]
179 voice_assistant_feature_flags: u32,
180}
181
182impl EspHomeApi {
184 pub async fn start<S>(
223 &self,
224 stream: S,
225 ) -> Result<
226 (
227 mpsc::Sender<ProtoMessage>,
228 broadcast::Receiver<ProtoMessage>,
229 ),
230 Box<dyn std::error::Error>,
231 >
232 where
233 S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
234 {
235 let (answer_messages_tx, mut answer_messages_rx) = mpsc::channel::<ProtoMessage>(16);
237 let (outgoing_messages_tx, outgoing_messages_rx) = broadcast::channel::<ProtoMessage>(16);
238
239 #[allow(deprecated)]
240 let device_info = DeviceInfoResponse {
241 api_encryption_supported: self.encryption_key.is_some(),
242 uses_password: false,
243 name: self.name.clone(),
244 mac_address: self.mac.clone().unwrap_or_default(),
245 esphome_version: proto::VERSION.to_owned(),
246 compilation_time: self.compilation_time.clone().unwrap_or_default(),
247 model: self.model.clone().unwrap_or_default(),
248 has_deep_sleep: false,
249 project_name: self.project_name.clone().unwrap_or_default(),
250 project_version: self.project_version.clone().unwrap_or_default(),
251 webserver_port: 0,
252 legacy_bluetooth_proxy_version: self.legacy_bluetooth_proxy_version,
254 bluetooth_proxy_feature_flags: self.bluetooth_proxy_feature_flags,
255 manufacturer: self.manufacturer.clone().unwrap_or_default(),
256 friendly_name: self.friendly_name.clone().unwrap_or(self.name.clone()),
257 legacy_voice_assistant_version: self.legacy_voice_assistant_version,
258 voice_assistant_feature_flags: self.voice_assistant_feature_flags,
259 suggested_area: self.suggested_area.clone().unwrap_or_default(),
260 bluetooth_mac_address: self.bluetooth_mac_address.clone().unwrap_or_default(),
261 areas: vec![],
262 devices: vec![],
263 area: None,
264 zwave_proxy_feature_flags: 0,
265 zwave_home_id: 0,
266 };
267
268 let hello_response = HelloResponse {
269 api_version_major: self.api_version_major,
270 api_version_minor: self.api_version_minor,
271 server_info: self.server_info.clone(),
272 name: self.name.clone(),
273 };
274
275 let encrypt_cypher: Arc<Mutex<Option<CipherState<ChaCha20Poly1305>>>> =
278 Arc::new(Mutex::new(None));
279 let decrypt_cypher: Arc<Mutex<Option<CipherState<ChaCha20Poly1305>>>> =
280 Arc::new(Mutex::new(None));
281
282 trace!("Init Connection: Stage 1");
284 let encryption_key = self.encryption_key.clone();
285
286 let (stream_read, stream_write) = tokio::io::split(stream);
287 let mut stream_read = BufReader::new(stream_read);
288
289 let peeked_bytes = stream_read.fill_buf().await?;
290 if peeked_bytes.is_empty() {
291 return Err("No data".into());
292 }
293
294 trace!("TCP Peeked: {:02X?}", &peeked_bytes[0..1]);
295
296 let preamble = peeked_bytes[0] as usize;
297
298 let plaintext_communication = match preamble {
299 0 => {
300 debug!("Cleartext messaging");
301 true
302 }
303 1 => {
304 trace!("Encrypted messaging");
305 false
306 }
307 _ => {
308 return Err(format!("Invalid marker byte {}", preamble).into());
309 }
310 };
311 let encrypted = !plaintext_communication;
312
313 let decoder = FrameCodec::new(encrypted);
314 let encoder = FrameCodec::new(encrypted);
315 let mut reader = FramedRead::new(stream_read, decoder);
316 let mut writer = FramedWrite::new(stream_write, encoder);
317
318 if plaintext_communication {
319 if self.encryption_key.is_some() {
320 let encoder = FrameCodec::new(true);
321 let writer = FramedWrite::new(writer.into_inner(), encoder);
322 write_error_and_disconnect(writer, ERROR_ONLY_ENCRYPTED).await;
323 return Err(ERROR_ONLY_ENCRYPTED.into());
324 }
325 } else {
326 if self.encryption_key.is_none() {
327 write_error_and_disconnect(writer, "No encrypted communication allowed").await;
328 return Err("No encryption key set, but encrypted communication requested.".into());
329 }
330
331 let frame_noise_hello = reader.next().await.unwrap().unwrap();
332 debug!("Frame 1: {:02X?}", &frame_noise_hello);
333
334 let message_server_hello =
335 packet_encrypted::generate_server_hello_frame(self.name.clone(), self.mac.clone());
336
337 writer.send(message_server_hello.clone()).await.unwrap();
338 writer.flush().await.unwrap();
339
340 let frame_handshake_request = reader.next().await.unwrap().unwrap();
341 debug!("Frame 2: {:02X?}", &frame_handshake_request);
342
343 let mut handshake_state: HandshakeState<X25519, ChaCha20Poly1305, Sha256> =
345 HandshakeState::new(
346 noise_nn_psk0(),
347 false,
348 b"NoiseAPIInit\0\0",
350 None,
351 None,
352 None,
353 None,
354 );
355
356 let noise_psk = BASE64_STANDARD
357 .decode(encryption_key.as_ref().unwrap())
358 .unwrap();
359
360 handshake_state.push_psk(&noise_psk);
361 match handshake_state.read_message_vec(&frame_handshake_request[1..]) {
363 Ok(_) => {}
364 Err(e) => match e.kind() {
365 ErrorKind::Decryption => {
366 write_error_and_disconnect(writer, ERROR_HANDSHAKE_MAC_FAILURE).await;
367 return Err(ERROR_HANDSHAKE_MAC_FAILURE.into());
368 }
369 _ => {
370 debug!("Failed to read message: {}", e);
371 }
372 },
373 }
374
375 let out = handshake_state.write_message_vec(b"").unwrap();
376 {
377 let mut encrypt_cipher_changer = encrypt_cypher.lock().await;
378 let mut decrypt_cipher_changer = decrypt_cypher.lock().await;
379 let (decrypt_cipher, encrypt_cipher) = handshake_state.get_ciphers();
380 *encrypt_cipher_changer = Some(encrypt_cipher);
381 *decrypt_cipher_changer = Some(decrypt_cipher);
382 }
383
384 let mut message_handshake = vec![0];
385 message_handshake.extend(out);
386
387 debug!("Sending handshake");
388 writer.send(message_handshake.clone()).await.unwrap();
389 writer.flush().await.unwrap();
390 }
391
392 debug!("Initialization done.");
393
394 let (cancellation_write_tx, mut cancellation_write_rx) = oneshot::channel();
396
397 let encrypt_cypher_for_write = encrypt_cypher;
399 tokio::spawn(async move {
400 loop {
401 let answer_message: ProtoMessage;
402
403 tokio::select! {
405 biased; cancel_message = &mut cancellation_write_rx => {
407 debug!("Write loop received cancellation signal ({}), exiting.", cancel_message.unwrap());
408 break;
409 }
410 message = answer_messages_rx.recv() => {
411 answer_message = message.unwrap();
412 }
413 };
414
415 debug!("Answer message: {:?}", answer_message);
416
417 if plaintext_communication {
418 writer
419 .send(packet_plaintext::message_to_packet(&answer_message).unwrap())
420 .await
421 .unwrap();
422 } else {
425 let mut encrypt_cipher_changer = encrypt_cypher_for_write.lock().await;
427 writer
428 .send(
429 packet_encrypted::message_to_packet(
430 &answer_message,
431 &mut *encrypt_cipher_changer.as_mut().unwrap(),
432 )
433 .unwrap(),
434 )
435 .await
436 .unwrap();
437 }
438 writer.flush().await.unwrap();
439
440 if matches!(answer_message, ProtoMessage::DisconnectResponse(_)) {
441 debug!("Disconnecting");
442 let mut tcp_write = writer.into_inner();
443 match tcp_write.shutdown().await {
444 Err(err) => {
445 error!("failed to shutdown socket: {:?}", err);
446 break;
447 }
448 _ => break,
449 }
450 }
451 }
452 });
453
454 let answer_messages_tx_clone = answer_messages_tx.clone();
456 tokio::spawn(async move {
458 loop {
459 let next = reader.next().await;
460 if next.is_none() {
461 info!("Read loop stopped because stream finished");
462 let _ = cancellation_write_tx.send("read loop finished");
464 break;
465 }
466 let frame = next.unwrap().unwrap();
467 trace!("TCP Receive: {:02X?}", &frame);
468
469 let message;
470 if encrypted {
471 let mut decrypt_cipher_changer = decrypt_cypher.lock().await;
472 message = packet_encrypted::packet_to_message(
473 &frame,
474 &mut *decrypt_cipher_changer.as_mut().unwrap(),
475 )
476 .unwrap();
477 } else {
478 message = packet_plaintext::packet_to_message(&frame).unwrap();
479 }
480
481 match &message {
483 ProtoMessage::DisconnectRequest(disconnect_request) => {
484 debug!("DisconnectRequest: {:?}", disconnect_request);
485 let response_message = DisconnectResponse {};
486 answer_messages_tx_clone
487 .send(ProtoMessage::DisconnectResponse(response_message))
488 .await
489 .unwrap();
490 continue;
491 }
492 ProtoMessage::PingRequest(ping_request) => {
493 debug!("PingRequest: {:?}", ping_request);
494 let response_message = PingResponse {};
495 answer_messages_tx_clone
496 .send(ProtoMessage::PingResponse(response_message))
497 .await
498 .unwrap();
499 }
500 ProtoMessage::DeviceInfoRequest(device_info_request) => {
501 debug!("DeviceInfoRequest: {:?}", device_info_request);
502 answer_messages_tx_clone
503 .send(ProtoMessage::DeviceInfoResponse(device_info.clone()))
504 .await
505 .unwrap();
506 }
507 ProtoMessage::HelloRequest(hello_request) => {
508 debug!("HelloRequest: {:?}", hello_request);
509
510 answer_messages_tx_clone
511 .send(ProtoMessage::HelloResponse(hello_response.clone()))
512 .await
513 .unwrap();
514 }
515 ProtoMessage::AuthenticationRequest(authentication_request) => {
516 debug!("AuthenticationRequest: {:?}", authentication_request);
517
518 if authentication_request.password != "" {
519 info!("Password Authentication is not supported");
520 } else {
521 let response_message = AuthenticationResponse {
522 invalid_password: false,
523 };
524 answer_messages_tx_clone
525 .send(ProtoMessage::AuthenticationResponse(response_message))
526 .await
527 .unwrap();
528 }
529 }
530 message => {
531 outgoing_messages_tx.send(message.clone()).unwrap();
532 }
533 }
534 }
535 });
536
537 Ok((answer_messages_tx.clone(), outgoing_messages_rx))
538 }
539}