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 serial_proxies: vec![],
267 };
268
269 let hello_response = HelloResponse {
270 api_version_major: self.api_version_major,
271 api_version_minor: self.api_version_minor,
272 server_info: self.server_info.clone(),
273 name: self.name.clone(),
274 };
275
276 let encrypt_cypher: Arc<Mutex<Option<CipherState<ChaCha20Poly1305>>>> =
279 Arc::new(Mutex::new(None));
280 let decrypt_cypher: Arc<Mutex<Option<CipherState<ChaCha20Poly1305>>>> =
281 Arc::new(Mutex::new(None));
282
283 trace!("Init Connection: Stage 1");
285 let encryption_key = self.encryption_key.clone();
286
287 let (stream_read, stream_write) = tokio::io::split(stream);
288 let mut stream_read = BufReader::new(stream_read);
289
290 let peeked_bytes = stream_read.fill_buf().await?;
291 if peeked_bytes.is_empty() {
292 return Err("No data".into());
293 }
294
295 trace!("TCP Peeked: {:02X?}", &peeked_bytes[0..1]);
296
297 let preamble = peeked_bytes[0] as usize;
298
299 let plaintext_communication = match preamble {
300 0 => {
301 debug!("Cleartext messaging");
302 true
303 }
304 1 => {
305 trace!("Encrypted messaging");
306 false
307 }
308 _ => {
309 return Err(format!("Invalid marker byte {}", preamble).into());
310 }
311 };
312 let encrypted = !plaintext_communication;
313
314 let decoder = FrameCodec::new(encrypted);
315 let encoder = FrameCodec::new(encrypted);
316 let mut reader = FramedRead::new(stream_read, decoder);
317 let mut writer = FramedWrite::new(stream_write, encoder);
318
319 if plaintext_communication {
320 if self.encryption_key.is_some() {
321 let encoder = FrameCodec::new(true);
322 let writer = FramedWrite::new(writer.into_inner(), encoder);
323 write_error_and_disconnect(writer, ERROR_ONLY_ENCRYPTED).await;
324 return Err(ERROR_ONLY_ENCRYPTED.into());
325 }
326 } else {
327 if self.encryption_key.is_none() {
328 write_error_and_disconnect(writer, "No encrypted communication allowed").await;
329 return Err("No encryption key set, but encrypted communication requested.".into());
330 }
331
332 let frame_noise_hello = reader.next().await.unwrap().unwrap();
333 debug!("Frame 1: {:02X?}", &frame_noise_hello);
334
335 let message_server_hello =
336 packet_encrypted::generate_server_hello_frame(self.name.clone(), self.mac.clone());
337
338 writer.send(message_server_hello.clone()).await.unwrap();
339 writer.flush().await.unwrap();
340
341 let frame_handshake_request = reader.next().await.unwrap().unwrap();
342 debug!("Frame 2: {:02X?}", &frame_handshake_request);
343
344 let mut handshake_state: HandshakeState<X25519, ChaCha20Poly1305, Sha256> =
346 HandshakeState::new(
347 noise_nn_psk0(),
348 false,
349 b"NoiseAPIInit\0\0",
351 None,
352 None,
353 None,
354 None,
355 );
356
357 let noise_psk = BASE64_STANDARD
358 .decode(encryption_key.as_ref().unwrap())
359 .unwrap();
360
361 handshake_state.push_psk(&noise_psk);
362 match handshake_state.read_message_vec(&frame_handshake_request[1..]) {
364 Ok(_) => {}
365 Err(e) => match e.kind() {
366 ErrorKind::Decryption => {
367 write_error_and_disconnect(writer, ERROR_HANDSHAKE_MAC_FAILURE).await;
368 return Err(ERROR_HANDSHAKE_MAC_FAILURE.into());
369 }
370 _ => {
371 debug!("Failed to read message: {}", e);
372 }
373 },
374 }
375
376 let out = handshake_state.write_message_vec(b"").unwrap();
377 {
378 let mut encrypt_cipher_changer = encrypt_cypher.lock().await;
379 let mut decrypt_cipher_changer = decrypt_cypher.lock().await;
380 let (decrypt_cipher, encrypt_cipher) = handshake_state.get_ciphers();
381 *encrypt_cipher_changer = Some(encrypt_cipher);
382 *decrypt_cipher_changer = Some(decrypt_cipher);
383 }
384
385 let mut message_handshake = vec![0];
386 message_handshake.extend(out);
387
388 debug!("Sending handshake");
389 writer.send(message_handshake.clone()).await.unwrap();
390 writer.flush().await.unwrap();
391 }
392
393 debug!("Initialization done.");
394
395 let (cancellation_write_tx, mut cancellation_write_rx) = oneshot::channel();
397
398 let encrypt_cypher_for_write = encrypt_cypher;
400 tokio::spawn(async move {
401 loop {
402 let answer_message: ProtoMessage;
403
404 tokio::select! {
406 biased; cancel_message = &mut cancellation_write_rx => {
408 debug!("Write loop received cancellation signal ({}), exiting.", cancel_message.unwrap());
409 break;
410 }
411 message = answer_messages_rx.recv() => {
412 answer_message = message.unwrap();
413 }
414 };
415
416 debug!("Answer message: {:?}", answer_message);
417
418 if plaintext_communication {
419 writer
420 .send(packet_plaintext::message_to_packet(&answer_message).unwrap())
421 .await
422 .unwrap();
423 } else {
426 let mut encrypt_cipher_changer = encrypt_cypher_for_write.lock().await;
428 writer
429 .send(
430 packet_encrypted::message_to_packet(
431 &answer_message,
432 &mut *encrypt_cipher_changer.as_mut().unwrap(),
433 )
434 .unwrap(),
435 )
436 .await
437 .unwrap();
438 }
439 writer.flush().await.unwrap();
440
441 if matches!(answer_message, ProtoMessage::DisconnectResponse(_)) {
442 debug!("Disconnecting");
443 let mut tcp_write = writer.into_inner();
444 match tcp_write.shutdown().await {
445 Err(err) => {
446 error!("failed to shutdown socket: {:?}", err);
447 break;
448 }
449 _ => break,
450 }
451 }
452 }
453 });
454
455 let answer_messages_tx_clone = answer_messages_tx.clone();
457 tokio::spawn(async move {
459 loop {
460 let next = reader.next().await;
461 if next.is_none() {
462 info!("Read loop stopped because stream finished");
463 let _ = cancellation_write_tx.send("read loop finished");
465 break;
466 }
467 let frame = next.unwrap().unwrap();
468 trace!("TCP Receive: {:02X?}", &frame);
469
470 let message;
471 if encrypted {
472 let mut decrypt_cipher_changer = decrypt_cypher.lock().await;
473 message = packet_encrypted::packet_to_message(
474 &frame,
475 &mut *decrypt_cipher_changer.as_mut().unwrap(),
476 )
477 .unwrap();
478 } else {
479 message = packet_plaintext::packet_to_message(&frame).unwrap();
480 }
481
482 match &message {
484 ProtoMessage::DisconnectRequest(disconnect_request) => {
485 debug!("DisconnectRequest: {:?}", disconnect_request);
486 let response_message = DisconnectResponse {};
487 answer_messages_tx_clone
488 .send(ProtoMessage::DisconnectResponse(response_message))
489 .await
490 .unwrap();
491 continue;
492 }
493 ProtoMessage::PingRequest(ping_request) => {
494 debug!("PingRequest: {:?}", ping_request);
495 let response_message = PingResponse {};
496 answer_messages_tx_clone
497 .send(ProtoMessage::PingResponse(response_message))
498 .await
499 .unwrap();
500 }
501 ProtoMessage::DeviceInfoRequest(device_info_request) => {
502 debug!("DeviceInfoRequest: {:?}", device_info_request);
503 answer_messages_tx_clone
504 .send(ProtoMessage::DeviceInfoResponse(device_info.clone()))
505 .await
506 .unwrap();
507 }
508 ProtoMessage::HelloRequest(hello_request) => {
509 debug!("HelloRequest: {:?}", hello_request);
510
511 answer_messages_tx_clone
512 .send(ProtoMessage::HelloResponse(hello_response.clone()))
513 .await
514 .unwrap();
515 }
516 ProtoMessage::AuthenticationRequest(authentication_request) => {
517 debug!("AuthenticationRequest: {:?}", authentication_request);
518
519 if authentication_request.password != "" {
520 info!("Password Authentication is not supported");
521 } else {
522 let response_message = AuthenticationResponse {
523 invalid_password: false,
524 };
525 answer_messages_tx_clone
526 .send(ProtoMessage::AuthenticationResponse(response_message))
527 .await
528 .unwrap();
529 }
530 }
531 message => {
532 outgoing_messages_tx.send(message.clone()).unwrap();
533 }
534 }
535 }
536 });
537
538 Ok((answer_messages_tx.clone(), outgoing_messages_rx))
539 }
540}