1#[cfg(not(feature = "std"))]
42use alloc::{string::String, sync::Arc, vec::Vec};
43#[cfg(feature = "std")]
44use std::sync::Arc;
45
46use crate::document::{ENCRYPTED_MARKER, KEY_EXCHANGE_MARKER, PEER_E2EE_MARKER};
47use crate::document_sync::DocumentSync;
48use crate::observer::{DisconnectReason, HiveEvent, HiveObserver, SecurityViolationKind};
49use crate::peer::{HivePeer, PeerManagerConfig};
50use crate::peer_manager::PeerManager;
51use crate::security::{
52 KeyExchangeMessage, MeshEncryptionKey, PeerEncryptedMessage, PeerSessionManager, SessionState,
53};
54use crate::sync::crdt::{EventType, PeripheralType};
55use crate::NodeId;
56
57#[cfg(feature = "std")]
58use crate::observer::ObserverManager;
59
60#[derive(Debug, Clone)]
62pub struct HiveMeshConfig {
63 pub node_id: NodeId,
65
66 pub callsign: String,
68
69 pub mesh_id: String,
71
72 pub peripheral_type: PeripheralType,
74
75 pub peer_config: PeerManagerConfig,
77
78 pub sync_interval_ms: u64,
80
81 pub auto_broadcast_events: bool,
83
84 pub encryption_secret: Option<[u8; 32]>,
90
91 pub strict_encryption: bool,
99}
100
101impl HiveMeshConfig {
102 pub fn new(node_id: NodeId, callsign: &str, mesh_id: &str) -> Self {
104 Self {
105 node_id,
106 callsign: callsign.into(),
107 mesh_id: mesh_id.into(),
108 peripheral_type: PeripheralType::SoldierSensor,
109 peer_config: PeerManagerConfig::with_mesh_id(mesh_id),
110 sync_interval_ms: 5000,
111 auto_broadcast_events: true,
112 encryption_secret: None,
113 strict_encryption: false,
114 }
115 }
116
117 pub fn with_encryption(mut self, secret: [u8; 32]) -> Self {
122 self.encryption_secret = Some(secret);
123 self
124 }
125
126 pub fn with_peripheral_type(mut self, ptype: PeripheralType) -> Self {
128 self.peripheral_type = ptype;
129 self
130 }
131
132 pub fn with_sync_interval(mut self, interval_ms: u64) -> Self {
134 self.sync_interval_ms = interval_ms;
135 self
136 }
137
138 pub fn with_peer_timeout(mut self, timeout_ms: u64) -> Self {
140 self.peer_config.peer_timeout_ms = timeout_ms;
141 self
142 }
143
144 pub fn with_max_peers(mut self, max: usize) -> Self {
146 self.peer_config.max_peers = max;
147 self
148 }
149
150 pub fn with_strict_encryption(mut self) -> Self {
158 self.strict_encryption = true;
159 self
160 }
161}
162
163#[cfg(feature = "std")]
168pub struct HiveMesh {
169 config: HiveMeshConfig,
171
172 peer_manager: PeerManager,
174
175 document_sync: DocumentSync,
177
178 observers: ObserverManager,
180
181 last_sync_ms: std::sync::atomic::AtomicU32,
183
184 last_cleanup_ms: std::sync::atomic::AtomicU32,
186
187 encryption_key: Option<MeshEncryptionKey>,
189
190 peer_sessions: std::sync::Mutex<Option<PeerSessionManager>>,
192}
193
194#[cfg(feature = "std")]
195impl HiveMesh {
196 pub fn new(config: HiveMeshConfig) -> Self {
198 let peer_manager = PeerManager::new(config.node_id, config.peer_config.clone());
199 let document_sync = DocumentSync::with_peripheral_type(
200 config.node_id,
201 &config.callsign,
202 config.peripheral_type,
203 );
204
205 let encryption_key = config
207 .encryption_secret
208 .map(|secret| MeshEncryptionKey::from_shared_secret(&config.mesh_id, &secret));
209
210 Self {
211 config,
212 peer_manager,
213 document_sync,
214 observers: ObserverManager::new(),
215 last_sync_ms: std::sync::atomic::AtomicU32::new(0),
216 last_cleanup_ms: std::sync::atomic::AtomicU32::new(0),
217 encryption_key,
218 peer_sessions: std::sync::Mutex::new(None),
219 }
220 }
221
222 pub fn is_encryption_enabled(&self) -> bool {
226 self.encryption_key.is_some()
227 }
228
229 pub fn is_strict_encryption_enabled(&self) -> bool {
233 self.config.strict_encryption && self.encryption_key.is_some()
234 }
235
236 pub fn enable_encryption(&mut self, secret: &[u8; 32]) {
241 self.encryption_key = Some(MeshEncryptionKey::from_shared_secret(
242 &self.config.mesh_id,
243 secret,
244 ));
245 }
246
247 pub fn disable_encryption(&mut self) {
249 self.encryption_key = None;
250 }
251
252 fn encrypt_document(&self, plaintext: &[u8]) -> Vec<u8> {
257 match &self.encryption_key {
258 Some(key) => {
259 match key.encrypt_to_bytes(plaintext) {
261 Ok(ciphertext) => {
262 let mut buf = Vec::with_capacity(2 + ciphertext.len());
263 buf.push(ENCRYPTED_MARKER);
264 buf.push(0x00); buf.extend_from_slice(&ciphertext);
266 buf
267 }
268 Err(e) => {
269 log::error!("Encryption failed: {}", e);
270 plaintext.to_vec()
272 }
273 }
274 }
275 None => plaintext.to_vec(),
276 }
277 }
278
279 fn decrypt_document<'a>(
287 &self,
288 data: &'a [u8],
289 source_hint: Option<&str>,
290 ) -> Option<std::borrow::Cow<'a, [u8]>> {
291 if data.len() >= 2 && data[0] == ENCRYPTED_MARKER {
293 let _reserved = data[1];
295 let encrypted_payload = &data[2..];
296
297 match &self.encryption_key {
298 Some(key) => match key.decrypt_from_bytes(encrypted_payload) {
299 Ok(plaintext) => Some(std::borrow::Cow::Owned(plaintext)),
300 Err(e) => {
301 log::warn!("Decryption failed (wrong key or corrupted): {}", e);
302 self.notify(HiveEvent::SecurityViolation {
303 kind: SecurityViolationKind::DecryptionFailed,
304 source: source_hint.map(String::from),
305 });
306 None
307 }
308 },
309 None => {
310 log::warn!("Received encrypted document but encryption not enabled");
311 None
312 }
313 }
314 } else {
315 if self.config.strict_encryption && self.encryption_key.is_some() {
318 log::warn!(
319 "Rejected unencrypted document in strict encryption mode (source: {:?})",
320 source_hint
321 );
322 self.notify(HiveEvent::SecurityViolation {
323 kind: SecurityViolationKind::UnencryptedInStrictMode,
324 source: source_hint.map(String::from),
325 });
326 None
327 } else {
328 Some(std::borrow::Cow::Borrowed(data))
330 }
331 }
332 }
333
334 pub fn enable_peer_e2ee(&self) {
342 let mut sessions = self.peer_sessions.lock().unwrap();
343 if sessions.is_none() {
344 *sessions = Some(PeerSessionManager::new(self.config.node_id));
345 log::info!(
346 "Per-peer E2EE enabled for node {:08X}",
347 self.config.node_id.as_u32()
348 );
349 }
350 }
351
352 pub fn disable_peer_e2ee(&self) {
356 let mut sessions = self.peer_sessions.lock().unwrap();
357 *sessions = None;
358 log::info!("Per-peer E2EE disabled");
359 }
360
361 pub fn is_peer_e2ee_enabled(&self) -> bool {
363 self.peer_sessions.lock().unwrap().is_some()
364 }
365
366 pub fn peer_e2ee_public_key(&self) -> Option<[u8; 32]> {
370 self.peer_sessions
371 .lock()
372 .unwrap()
373 .as_ref()
374 .map(|s| s.our_public_key())
375 }
376
377 pub fn initiate_peer_e2ee(&self, peer_node_id: NodeId, now_ms: u64) -> Option<Vec<u8>> {
383 let mut sessions = self.peer_sessions.lock().unwrap();
384 let session_mgr = sessions.as_mut()?;
385
386 let key_exchange = session_mgr.initiate_session(peer_node_id, now_ms);
387 let mut buf = Vec::with_capacity(2 + 37);
388 buf.push(KEY_EXCHANGE_MARKER);
389 buf.push(0x00); buf.extend_from_slice(&key_exchange.encode());
391
392 log::info!(
393 "Initiated E2EE session with peer {:08X}",
394 peer_node_id.as_u32()
395 );
396 Some(buf)
397 }
398
399 pub fn has_peer_e2ee_session(&self, peer_node_id: NodeId) -> bool {
401 self.peer_sessions
402 .lock()
403 .unwrap()
404 .as_ref()
405 .is_some_and(|s| s.has_session(peer_node_id))
406 }
407
408 pub fn peer_e2ee_session_state(&self, peer_node_id: NodeId) -> Option<SessionState> {
410 self.peer_sessions
411 .lock()
412 .unwrap()
413 .as_ref()
414 .and_then(|s| s.session_state(peer_node_id))
415 }
416
417 pub fn send_peer_e2ee(
422 &self,
423 peer_node_id: NodeId,
424 plaintext: &[u8],
425 now_ms: u64,
426 ) -> Option<Vec<u8>> {
427 let mut sessions = self.peer_sessions.lock().unwrap();
428 let session_mgr = sessions.as_mut()?;
429
430 match session_mgr.encrypt_for_peer(peer_node_id, plaintext, now_ms) {
431 Ok(encrypted) => {
432 let mut buf = Vec::with_capacity(2 + encrypted.encode().len());
433 buf.push(PEER_E2EE_MARKER);
434 buf.push(0x00); buf.extend_from_slice(&encrypted.encode());
436 Some(buf)
437 }
438 Err(e) => {
439 log::warn!(
440 "Failed to encrypt for peer {:08X}: {:?}",
441 peer_node_id.as_u32(),
442 e
443 );
444 None
445 }
446 }
447 }
448
449 pub fn close_peer_e2ee(&self, peer_node_id: NodeId) {
451 let mut sessions = self.peer_sessions.lock().unwrap();
452 if let Some(session_mgr) = sessions.as_mut() {
453 session_mgr.close_session(peer_node_id);
454 self.notify(HiveEvent::PeerE2eeClosed { peer_node_id });
455 log::info!(
456 "Closed E2EE session with peer {:08X}",
457 peer_node_id.as_u32()
458 );
459 }
460 }
461
462 pub fn peer_e2ee_session_count(&self) -> usize {
464 self.peer_sessions
465 .lock()
466 .unwrap()
467 .as_ref()
468 .map(|s| s.session_count())
469 .unwrap_or(0)
470 }
471
472 pub fn peer_e2ee_established_count(&self) -> usize {
474 self.peer_sessions
475 .lock()
476 .unwrap()
477 .as_ref()
478 .map(|s| s.established_count())
479 .unwrap_or(0)
480 }
481
482 fn handle_key_exchange(&self, data: &[u8], now_ms: u64) -> Option<Vec<u8>> {
487 if data.len() < 2 || data[0] != KEY_EXCHANGE_MARKER {
488 return None;
489 }
490
491 let payload = &data[2..];
492 let msg = KeyExchangeMessage::decode(payload)?;
493
494 let mut sessions = self.peer_sessions.lock().unwrap();
495 let session_mgr = sessions.as_mut()?;
496
497 let (response, established) = session_mgr.handle_key_exchange(&msg, now_ms)?;
498
499 if established {
500 self.notify(HiveEvent::PeerE2eeEstablished {
501 peer_node_id: msg.sender_node_id,
502 });
503 log::info!(
504 "E2EE session established with peer {:08X}",
505 msg.sender_node_id.as_u32()
506 );
507 }
508
509 let mut buf = Vec::with_capacity(2 + 37);
511 buf.push(KEY_EXCHANGE_MARKER);
512 buf.push(0x00);
513 buf.extend_from_slice(&response.encode());
514 Some(buf)
515 }
516
517 fn handle_peer_e2ee_message(&self, data: &[u8], now_ms: u64) -> Option<Vec<u8>> {
522 if data.len() < 2 || data[0] != PEER_E2EE_MARKER {
523 return None;
524 }
525
526 let payload = &data[2..];
527 let msg = PeerEncryptedMessage::decode(payload)?;
528
529 let mut sessions = self.peer_sessions.lock().unwrap();
530 let session_mgr = sessions.as_mut()?;
531
532 match session_mgr.decrypt_from_peer(&msg, now_ms) {
533 Ok(plaintext) => {
534 self.notify(HiveEvent::PeerE2eeMessageReceived {
536 from_node: msg.sender_node_id,
537 data: plaintext.clone(),
538 });
539 Some(plaintext)
540 }
541 Err(e) => {
542 log::warn!(
543 "Failed to decrypt E2EE message from {:08X}: {:?}",
544 msg.sender_node_id.as_u32(),
545 e
546 );
547 None
548 }
549 }
550 }
551
552 pub fn node_id(&self) -> NodeId {
556 self.config.node_id
557 }
558
559 pub fn callsign(&self) -> &str {
561 &self.config.callsign
562 }
563
564 pub fn mesh_id(&self) -> &str {
566 &self.config.mesh_id
567 }
568
569 pub fn device_name(&self) -> String {
571 format!(
572 "HIVE_{}-{:08X}",
573 self.config.mesh_id,
574 self.config.node_id.as_u32()
575 )
576 }
577
578 pub fn add_observer(&self, observer: Arc<dyn HiveObserver>) {
582 self.observers.add(observer);
583 }
584
585 pub fn remove_observer(&self, observer: &Arc<dyn HiveObserver>) {
587 self.observers.remove(observer);
588 }
589
590 pub fn send_emergency(&self, timestamp: u64) -> Vec<u8> {
597 let data = self.document_sync.send_emergency(timestamp);
598 self.notify(HiveEvent::MeshStateChanged {
599 peer_count: self.peer_manager.peer_count(),
600 connected_count: self.peer_manager.connected_count(),
601 });
602 self.encrypt_document(&data)
603 }
604
605 pub fn send_ack(&self, timestamp: u64) -> Vec<u8> {
610 let data = self.document_sync.send_ack(timestamp);
611 self.notify(HiveEvent::MeshStateChanged {
612 peer_count: self.peer_manager.peer_count(),
613 connected_count: self.peer_manager.connected_count(),
614 });
615 self.encrypt_document(&data)
616 }
617
618 pub fn clear_event(&self) {
620 self.document_sync.clear_event();
621 }
622
623 pub fn is_emergency_active(&self) -> bool {
625 self.document_sync.is_emergency_active()
626 }
627
628 pub fn is_ack_active(&self) -> bool {
630 self.document_sync.is_ack_active()
631 }
632
633 pub fn current_event(&self) -> Option<EventType> {
635 self.document_sync.current_event()
636 }
637
638 pub fn start_emergency(&self, timestamp: u64, known_peers: &[u32]) -> Vec<u8> {
647 let data = self.document_sync.start_emergency(timestamp, known_peers);
648 self.notify(HiveEvent::MeshStateChanged {
649 peer_count: self.peer_manager.peer_count(),
650 connected_count: self.peer_manager.connected_count(),
651 });
652 self.encrypt_document(&data)
653 }
654
655 pub fn start_emergency_with_known_peers(&self, timestamp: u64) -> Vec<u8> {
659 let peers: Vec<u32> = self
660 .peer_manager
661 .get_peers()
662 .iter()
663 .map(|p| p.node_id.as_u32())
664 .collect();
665 self.start_emergency(timestamp, &peers)
666 }
667
668 pub fn ack_emergency(&self, timestamp: u64) -> Option<Vec<u8>> {
673 let result = self.document_sync.ack_emergency(timestamp);
674 if result.is_some() {
675 self.notify(HiveEvent::MeshStateChanged {
676 peer_count: self.peer_manager.peer_count(),
677 connected_count: self.peer_manager.connected_count(),
678 });
679 }
680 result.map(|data| self.encrypt_document(&data))
681 }
682
683 pub fn clear_emergency(&self) {
685 self.document_sync.clear_emergency();
686 }
687
688 pub fn has_active_emergency(&self) -> bool {
690 self.document_sync.has_active_emergency()
691 }
692
693 pub fn get_emergency_status(&self) -> Option<(u32, u64, usize, usize)> {
697 self.document_sync.get_emergency_status()
698 }
699
700 pub fn has_peer_acked(&self, peer_id: u32) -> bool {
702 self.document_sync.has_peer_acked(peer_id)
703 }
704
705 pub fn all_peers_acked(&self) -> bool {
707 self.document_sync.all_peers_acked()
708 }
709
710 pub fn on_ble_discovered(
716 &self,
717 identifier: &str,
718 name: Option<&str>,
719 rssi: i8,
720 mesh_id: Option<&str>,
721 now_ms: u64,
722 ) -> Option<HivePeer> {
723 let (node_id, is_new) = self
724 .peer_manager
725 .on_discovered(identifier, name, rssi, mesh_id, now_ms)?;
726
727 let peer = self.peer_manager.get_peer(node_id)?;
728
729 if is_new {
730 self.notify(HiveEvent::PeerDiscovered { peer: peer.clone() });
731 self.notify_mesh_state_changed();
732 }
733
734 Some(peer)
735 }
736
737 pub fn on_ble_connected(&self, identifier: &str, now_ms: u64) -> Option<NodeId> {
741 let node_id = self.peer_manager.on_connected(identifier, now_ms)?;
742 self.notify(HiveEvent::PeerConnected { node_id });
743 self.notify_mesh_state_changed();
744 Some(node_id)
745 }
746
747 pub fn on_ble_disconnected(
749 &self,
750 identifier: &str,
751 reason: DisconnectReason,
752 ) -> Option<NodeId> {
753 let (node_id, reason) = self.peer_manager.on_disconnected(identifier, reason)?;
754 self.notify(HiveEvent::PeerDisconnected { node_id, reason });
755 self.notify_mesh_state_changed();
756 Some(node_id)
757 }
758
759 pub fn on_peer_disconnected(&self, node_id: NodeId, reason: DisconnectReason) {
763 if self
764 .peer_manager
765 .on_disconnected_by_node_id(node_id, reason)
766 {
767 self.notify(HiveEvent::PeerDisconnected { node_id, reason });
768 self.notify_mesh_state_changed();
769 }
770 }
771
772 pub fn on_incoming_connection(&self, identifier: &str, node_id: NodeId, now_ms: u64) -> bool {
776 let is_new = self
777 .peer_manager
778 .on_incoming_connection(identifier, node_id, now_ms);
779
780 if is_new {
781 if let Some(peer) = self.peer_manager.get_peer(node_id) {
782 self.notify(HiveEvent::PeerDiscovered { peer });
783 }
784 }
785
786 self.notify(HiveEvent::PeerConnected { node_id });
787 self.notify_mesh_state_changed();
788
789 is_new
790 }
791
792 pub fn on_ble_data_received(
799 &self,
800 identifier: &str,
801 data: &[u8],
802 now_ms: u64,
803 ) -> Option<DataReceivedResult> {
804 let node_id = self.peer_manager.get_node_id(identifier)?;
806
807 if data.len() >= 2 {
809 match data[0] {
810 KEY_EXCHANGE_MARKER => {
811 let _response = self.handle_key_exchange(data, now_ms);
813 return None;
815 }
816 PEER_E2EE_MARKER => {
817 let _plaintext = self.handle_peer_e2ee_message(data, now_ms);
819 return None;
821 }
822 _ => {}
823 }
824 }
825
826 let decrypted = self.decrypt_document(data, Some(identifier))?;
828
829 let result = self.document_sync.merge_document(&decrypted)?;
831
832 self.peer_manager.record_sync(node_id, now_ms);
834
835 if result.is_emergency() {
837 self.notify(HiveEvent::EmergencyReceived {
838 from_node: result.source_node,
839 });
840 } else if result.is_ack() {
841 self.notify(HiveEvent::AckReceived {
842 from_node: result.source_node,
843 });
844 }
845
846 if result.counter_changed {
847 self.notify(HiveEvent::DocumentSynced {
848 from_node: result.source_node,
849 total_count: result.total_count,
850 });
851 }
852
853 Some(DataReceivedResult {
854 source_node: result.source_node,
855 is_emergency: result.is_emergency(),
856 is_ack: result.is_ack(),
857 counter_changed: result.counter_changed,
858 emergency_changed: result.emergency_changed,
859 total_count: result.total_count,
860 event_timestamp: result.event.as_ref().map(|e| e.timestamp).unwrap_or(0),
861 })
862 }
863
864 pub fn on_ble_data_received_from_node(
870 &self,
871 node_id: NodeId,
872 data: &[u8],
873 now_ms: u64,
874 ) -> Option<DataReceivedResult> {
875 if data.len() >= 2 {
877 match data[0] {
878 KEY_EXCHANGE_MARKER => {
879 let _response = self.handle_key_exchange(data, now_ms);
880 return None;
881 }
882 PEER_E2EE_MARKER => {
883 let _plaintext = self.handle_peer_e2ee_message(data, now_ms);
884 return None;
885 }
886 _ => {}
887 }
888 }
889
890 let source_hint = format!("node:{:08X}", node_id.as_u32());
892 let decrypted = self.decrypt_document(data, Some(&source_hint))?;
893
894 let result = self.document_sync.merge_document(&decrypted)?;
896
897 self.peer_manager.record_sync(node_id, now_ms);
899
900 if result.is_emergency() {
902 self.notify(HiveEvent::EmergencyReceived {
903 from_node: result.source_node,
904 });
905 } else if result.is_ack() {
906 self.notify(HiveEvent::AckReceived {
907 from_node: result.source_node,
908 });
909 }
910
911 if result.counter_changed {
912 self.notify(HiveEvent::DocumentSynced {
913 from_node: result.source_node,
914 total_count: result.total_count,
915 });
916 }
917
918 Some(DataReceivedResult {
919 source_node: result.source_node,
920 is_emergency: result.is_emergency(),
921 is_ack: result.is_ack(),
922 counter_changed: result.counter_changed,
923 emergency_changed: result.emergency_changed,
924 total_count: result.total_count,
925 event_timestamp: result.event.as_ref().map(|e| e.timestamp).unwrap_or(0),
926 })
927 }
928
929 pub fn on_ble_data(
937 &self,
938 identifier: &str,
939 data: &[u8],
940 now_ms: u64,
941 ) -> Option<DataReceivedResult> {
942 if data.len() >= 2 {
944 match data[0] {
945 KEY_EXCHANGE_MARKER => {
946 let _response = self.handle_key_exchange(data, now_ms);
947 return None;
948 }
949 PEER_E2EE_MARKER => {
950 let _plaintext = self.handle_peer_e2ee_message(data, now_ms);
951 return None;
952 }
953 _ => {}
954 }
955 }
956
957 let decrypted = self.decrypt_document(data, Some(identifier))?;
959
960 let result = self.document_sync.merge_document(&decrypted)?;
962
963 self.peer_manager.record_sync(result.source_node, now_ms);
965
966 self.peer_manager
968 .on_incoming_connection(identifier, result.source_node, now_ms);
969
970 if result.is_emergency() {
972 self.notify(HiveEvent::EmergencyReceived {
973 from_node: result.source_node,
974 });
975 } else if result.is_ack() {
976 self.notify(HiveEvent::AckReceived {
977 from_node: result.source_node,
978 });
979 }
980
981 if result.counter_changed {
982 self.notify(HiveEvent::DocumentSynced {
983 from_node: result.source_node,
984 total_count: result.total_count,
985 });
986 }
987
988 Some(DataReceivedResult {
989 source_node: result.source_node,
990 is_emergency: result.is_emergency(),
991 is_ack: result.is_ack(),
992 counter_changed: result.counter_changed,
993 emergency_changed: result.emergency_changed,
994 total_count: result.total_count,
995 event_timestamp: result.event.as_ref().map(|e| e.timestamp).unwrap_or(0),
996 })
997 }
998
999 pub fn tick(&self, now_ms: u64) -> Option<Vec<u8>> {
1009 use std::sync::atomic::Ordering;
1010
1011 let now_ms_32 = now_ms as u32;
1013
1014 let last_cleanup = self.last_cleanup_ms.load(Ordering::Relaxed);
1016 let cleanup_elapsed = now_ms_32.wrapping_sub(last_cleanup);
1017 if cleanup_elapsed >= self.config.peer_config.cleanup_interval_ms as u32 {
1018 self.last_cleanup_ms.store(now_ms_32, Ordering::Relaxed);
1019 let removed = self.peer_manager.cleanup_stale(now_ms);
1020 for node_id in &removed {
1021 self.notify(HiveEvent::PeerLost { node_id: *node_id });
1022 }
1023 if !removed.is_empty() {
1024 self.notify_mesh_state_changed();
1025 }
1026 }
1027
1028 let last_sync = self.last_sync_ms.load(Ordering::Relaxed);
1030 let sync_elapsed = now_ms_32.wrapping_sub(last_sync);
1031 if sync_elapsed >= self.config.sync_interval_ms as u32 {
1032 self.last_sync_ms.store(now_ms_32, Ordering::Relaxed);
1033 if self.peer_manager.connected_count() > 0 {
1035 let doc = self.document_sync.build_document();
1036 return Some(self.encrypt_document(&doc));
1037 }
1038 }
1039
1040 None
1041 }
1042
1043 pub fn get_peers(&self) -> Vec<HivePeer> {
1047 self.peer_manager.get_peers()
1048 }
1049
1050 pub fn get_connected_peers(&self) -> Vec<HivePeer> {
1052 self.peer_manager.get_connected_peers()
1053 }
1054
1055 pub fn get_peer(&self, node_id: NodeId) -> Option<HivePeer> {
1057 self.peer_manager.get_peer(node_id)
1058 }
1059
1060 pub fn peer_count(&self) -> usize {
1062 self.peer_manager.peer_count()
1063 }
1064
1065 pub fn connected_count(&self) -> usize {
1067 self.peer_manager.connected_count()
1068 }
1069
1070 pub fn matches_mesh(&self, device_mesh_id: Option<&str>) -> bool {
1072 self.peer_manager.matches_mesh(device_mesh_id)
1073 }
1074
1075 pub fn total_count(&self) -> u64 {
1077 self.document_sync.total_count()
1078 }
1079
1080 pub fn document_version(&self) -> u32 {
1082 self.document_sync.version()
1083 }
1084
1085 pub fn version(&self) -> u32 {
1087 self.document_sync.version()
1088 }
1089
1090 pub fn update_health(&self, battery_percent: u8) {
1092 self.document_sync.update_health(battery_percent);
1093 }
1094
1095 pub fn update_activity(&self, activity: u8) {
1097 self.document_sync.update_activity(activity);
1098 }
1099
1100 pub fn update_health_full(&self, battery_percent: u8, activity: u8) {
1102 self.document_sync
1103 .update_health_full(battery_percent, activity);
1104 }
1105
1106 pub fn build_document(&self) -> Vec<u8> {
1110 let doc = self.document_sync.build_document();
1111 self.encrypt_document(&doc)
1112 }
1113
1114 pub fn peers_needing_sync(&self, now_ms: u64) -> Vec<HivePeer> {
1116 self.peer_manager.peers_needing_sync(now_ms)
1117 }
1118
1119 fn notify(&self, event: HiveEvent) {
1122 self.observers.notify(event);
1123 }
1124
1125 fn notify_mesh_state_changed(&self) {
1126 self.notify(HiveEvent::MeshStateChanged {
1127 peer_count: self.peer_manager.peer_count(),
1128 connected_count: self.peer_manager.connected_count(),
1129 });
1130 }
1131}
1132
1133#[derive(Debug, Clone)]
1135pub struct DataReceivedResult {
1136 pub source_node: NodeId,
1138
1139 pub is_emergency: bool,
1141
1142 pub is_ack: bool,
1144
1145 pub counter_changed: bool,
1147
1148 pub emergency_changed: bool,
1150
1151 pub total_count: u64,
1153
1154 pub event_timestamp: u64,
1156}
1157
1158#[cfg(all(test, feature = "std"))]
1159mod tests {
1160 use super::*;
1161 use crate::observer::CollectingObserver;
1162
1163 fn create_mesh(node_id: u32, callsign: &str) -> HiveMesh {
1164 let config = HiveMeshConfig::new(NodeId::new(node_id), callsign, "TEST");
1165 HiveMesh::new(config)
1166 }
1167
1168 #[test]
1169 fn test_mesh_creation() {
1170 let mesh = create_mesh(0x12345678, "ALPHA-1");
1171
1172 assert_eq!(mesh.node_id().as_u32(), 0x12345678);
1173 assert_eq!(mesh.callsign(), "ALPHA-1");
1174 assert_eq!(mesh.mesh_id(), "TEST");
1175 assert_eq!(mesh.device_name(), "HIVE_TEST-12345678");
1176 }
1177
1178 #[test]
1179 fn test_peer_discovery() {
1180 let mesh = create_mesh(0x11111111, "ALPHA-1");
1181 let observer = Arc::new(CollectingObserver::new());
1182 mesh.add_observer(observer.clone());
1183
1184 let peer = mesh.on_ble_discovered(
1186 "device-uuid",
1187 Some("HIVE_TEST-22222222"),
1188 -65,
1189 Some("TEST"),
1190 1000,
1191 );
1192
1193 assert!(peer.is_some());
1194 let peer = peer.unwrap();
1195 assert_eq!(peer.node_id.as_u32(), 0x22222222);
1196
1197 let events = observer.events();
1199 assert!(events
1200 .iter()
1201 .any(|e| matches!(e, HiveEvent::PeerDiscovered { .. })));
1202 assert!(events
1203 .iter()
1204 .any(|e| matches!(e, HiveEvent::MeshStateChanged { .. })));
1205 }
1206
1207 #[test]
1208 fn test_connection_lifecycle() {
1209 let mesh = create_mesh(0x11111111, "ALPHA-1");
1210 let observer = Arc::new(CollectingObserver::new());
1211 mesh.add_observer(observer.clone());
1212
1213 mesh.on_ble_discovered(
1215 "device-uuid",
1216 Some("HIVE_TEST-22222222"),
1217 -65,
1218 Some("TEST"),
1219 1000,
1220 );
1221
1222 let node_id = mesh.on_ble_connected("device-uuid", 2000);
1223 assert_eq!(node_id, Some(NodeId::new(0x22222222)));
1224 assert_eq!(mesh.connected_count(), 1);
1225
1226 let node_id = mesh.on_ble_disconnected("device-uuid", DisconnectReason::RemoteRequest);
1228 assert_eq!(node_id, Some(NodeId::new(0x22222222)));
1229 assert_eq!(mesh.connected_count(), 0);
1230
1231 let events = observer.events();
1233 assert!(events
1234 .iter()
1235 .any(|e| matches!(e, HiveEvent::PeerConnected { .. })));
1236 assert!(events
1237 .iter()
1238 .any(|e| matches!(e, HiveEvent::PeerDisconnected { .. })));
1239 }
1240
1241 #[test]
1242 fn test_emergency_flow() {
1243 let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1244 let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1245
1246 let observer2 = Arc::new(CollectingObserver::new());
1247 mesh2.add_observer(observer2.clone());
1248
1249 let doc = mesh1.send_emergency(1000);
1251 assert!(mesh1.is_emergency_active());
1252
1253 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1255
1256 assert!(result.is_some());
1257 let result = result.unwrap();
1258 assert!(result.is_emergency);
1259 assert_eq!(result.source_node.as_u32(), 0x11111111);
1260
1261 let events = observer2.events();
1263 assert!(events
1264 .iter()
1265 .any(|e| matches!(e, HiveEvent::EmergencyReceived { .. })));
1266 }
1267
1268 #[test]
1269 fn test_ack_flow() {
1270 let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1271 let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1272
1273 let observer2 = Arc::new(CollectingObserver::new());
1274 mesh2.add_observer(observer2.clone());
1275
1276 let doc = mesh1.send_ack(1000);
1278 assert!(mesh1.is_ack_active());
1279
1280 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1282
1283 assert!(result.is_some());
1284 let result = result.unwrap();
1285 assert!(result.is_ack);
1286
1287 let events = observer2.events();
1289 assert!(events
1290 .iter()
1291 .any(|e| matches!(e, HiveEvent::AckReceived { .. })));
1292 }
1293
1294 #[test]
1295 fn test_tick_cleanup() {
1296 let config = HiveMeshConfig::new(NodeId::new(0x11111111), "ALPHA-1", "TEST")
1297 .with_peer_timeout(10_000);
1298 let mesh = HiveMesh::new(config);
1299
1300 let observer = Arc::new(CollectingObserver::new());
1301 mesh.add_observer(observer.clone());
1302
1303 mesh.on_ble_discovered(
1305 "device-uuid",
1306 Some("HIVE_TEST-22222222"),
1307 -65,
1308 Some("TEST"),
1309 1000,
1310 );
1311 assert_eq!(mesh.peer_count(), 1);
1312
1313 mesh.tick(5000);
1315 assert_eq!(mesh.peer_count(), 1);
1316
1317 mesh.tick(20000);
1319 assert_eq!(mesh.peer_count(), 0);
1320
1321 let events = observer.events();
1323 assert!(events
1324 .iter()
1325 .any(|e| matches!(e, HiveEvent::PeerLost { .. })));
1326 }
1327
1328 #[test]
1329 fn test_tick_sync_broadcast() {
1330 let config = HiveMeshConfig::new(NodeId::new(0x11111111), "ALPHA-1", "TEST")
1331 .with_sync_interval(5000);
1332 let mesh = HiveMesh::new(config);
1333
1334 mesh.on_ble_discovered(
1336 "device-uuid",
1337 Some("HIVE_TEST-22222222"),
1338 -65,
1339 Some("TEST"),
1340 1000,
1341 );
1342 mesh.on_ble_connected("device-uuid", 1000);
1343
1344 let _result = mesh.tick(0);
1346 let result = mesh.tick(3000);
1350 assert!(result.is_none());
1351
1352 let result = mesh.tick(6000);
1354 assert!(result.is_some());
1355
1356 let result = mesh.tick(6100);
1358 assert!(result.is_none());
1359
1360 let result = mesh.tick(12000);
1362 assert!(result.is_some());
1363 }
1364
1365 #[test]
1366 fn test_incoming_connection() {
1367 let mesh = create_mesh(0x11111111, "ALPHA-1");
1368 let observer = Arc::new(CollectingObserver::new());
1369 mesh.add_observer(observer.clone());
1370
1371 let is_new = mesh.on_incoming_connection("central-uuid", NodeId::new(0x22222222), 1000);
1373
1374 assert!(is_new);
1375 assert_eq!(mesh.peer_count(), 1);
1376 assert_eq!(mesh.connected_count(), 1);
1377
1378 let events = observer.events();
1380 assert!(events
1381 .iter()
1382 .any(|e| matches!(e, HiveEvent::PeerDiscovered { .. })));
1383 assert!(events
1384 .iter()
1385 .any(|e| matches!(e, HiveEvent::PeerConnected { .. })));
1386 }
1387
1388 #[test]
1389 fn test_mesh_filtering() {
1390 let mesh = create_mesh(0x11111111, "ALPHA-1");
1391
1392 let peer = mesh.on_ble_discovered(
1394 "device-uuid-1",
1395 Some("HIVE_OTHER-22222222"),
1396 -65,
1397 Some("OTHER"),
1398 1000,
1399 );
1400 assert!(peer.is_none());
1401 assert_eq!(mesh.peer_count(), 0);
1402
1403 let peer = mesh.on_ble_discovered(
1405 "device-uuid-2",
1406 Some("HIVE_TEST-33333333"),
1407 -65,
1408 Some("TEST"),
1409 1000,
1410 );
1411 assert!(peer.is_some());
1412 assert_eq!(mesh.peer_count(), 1);
1413 }
1414
1415 fn create_encrypted_mesh(node_id: u32, callsign: &str, secret: [u8; 32]) -> HiveMesh {
1418 let config =
1419 HiveMeshConfig::new(NodeId::new(node_id), callsign, "TEST").with_encryption(secret);
1420 HiveMesh::new(config)
1421 }
1422
1423 #[test]
1424 fn test_encryption_enabled() {
1425 let secret = [0x42u8; 32];
1426 let mesh = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1427
1428 assert!(mesh.is_encryption_enabled());
1429 }
1430
1431 #[test]
1432 fn test_encryption_disabled_by_default() {
1433 let mesh = create_mesh(0x11111111, "ALPHA-1");
1434
1435 assert!(!mesh.is_encryption_enabled());
1436 }
1437
1438 #[test]
1439 fn test_encrypted_document_exchange() {
1440 let secret = [0x42u8; 32];
1441 let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1442 let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret);
1443
1444 let doc = mesh1.build_document();
1446
1447 assert!(doc.len() >= 2);
1449 assert_eq!(doc[0], crate::document::ENCRYPTED_MARKER);
1450
1451 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1453
1454 assert!(result.is_some());
1455 let result = result.unwrap();
1456 assert_eq!(result.source_node.as_u32(), 0x11111111);
1457 }
1458
1459 #[test]
1460 fn test_encrypted_emergency_exchange() {
1461 let secret = [0x42u8; 32];
1462 let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1463 let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret);
1464
1465 let observer = Arc::new(CollectingObserver::new());
1466 mesh2.add_observer(observer.clone());
1467
1468 let doc = mesh1.send_emergency(1000);
1470
1471 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1473
1474 assert!(result.is_some());
1475 let result = result.unwrap();
1476 assert!(result.is_emergency);
1477
1478 let events = observer.events();
1480 assert!(events
1481 .iter()
1482 .any(|e| matches!(e, HiveEvent::EmergencyReceived { .. })));
1483 }
1484
1485 #[test]
1486 fn test_wrong_key_fails_decrypt() {
1487 let secret1 = [0x42u8; 32];
1488 let secret2 = [0x43u8; 32]; let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret1);
1490 let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret2);
1491
1492 let doc = mesh1.build_document();
1494
1495 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1497
1498 assert!(result.is_none());
1499 }
1500
1501 #[test]
1502 fn test_unencrypted_mesh_can_read_unencrypted() {
1503 let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1504 let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1505
1506 let doc = mesh1.build_document();
1508
1509 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1511
1512 assert!(result.is_some());
1513 }
1514
1515 #[test]
1516 fn test_encrypted_mesh_can_receive_unencrypted() {
1517 let secret = [0x42u8; 32];
1519 let mesh1 = create_mesh(0x11111111, "ALPHA-1"); let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret); let doc = mesh1.build_document();
1524
1525 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1527
1528 assert!(result.is_some());
1529 }
1530
1531 #[test]
1532 fn test_unencrypted_mesh_cannot_receive_encrypted() {
1533 let secret = [0x42u8; 32];
1534 let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret); let mesh2 = create_mesh(0x22222222, "BRAVO-1"); let doc = mesh1.build_document();
1539
1540 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1542
1543 assert!(result.is_none());
1544 }
1545
1546 #[test]
1547 fn test_enable_disable_encryption() {
1548 let mut mesh = create_mesh(0x11111111, "ALPHA-1");
1549
1550 assert!(!mesh.is_encryption_enabled());
1551
1552 let secret = [0x42u8; 32];
1554 mesh.enable_encryption(&secret);
1555 assert!(mesh.is_encryption_enabled());
1556
1557 let doc = mesh.build_document();
1559 assert_eq!(doc[0], crate::document::ENCRYPTED_MARKER);
1560
1561 mesh.disable_encryption();
1563 assert!(!mesh.is_encryption_enabled());
1564
1565 let doc = mesh.build_document();
1567 assert_ne!(doc[0], crate::document::ENCRYPTED_MARKER);
1568 }
1569
1570 #[test]
1571 fn test_encryption_overhead() {
1572 let secret = [0x42u8; 32];
1573 let mesh_encrypted = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1574 let mesh_unencrypted = create_mesh(0x22222222, "BRAVO-1");
1575
1576 let doc_encrypted = mesh_encrypted.build_document();
1577 let doc_unencrypted = mesh_unencrypted.build_document();
1578
1579 let overhead = doc_encrypted.len() - doc_unencrypted.len();
1585 assert_eq!(overhead, 30); }
1587
1588 #[test]
1591 fn test_peer_e2ee_enable_disable() {
1592 let mesh = create_mesh(0x11111111, "ALPHA-1");
1593
1594 assert!(!mesh.is_peer_e2ee_enabled());
1595 assert!(mesh.peer_e2ee_public_key().is_none());
1596
1597 mesh.enable_peer_e2ee();
1598 assert!(mesh.is_peer_e2ee_enabled());
1599 assert!(mesh.peer_e2ee_public_key().is_some());
1600
1601 mesh.disable_peer_e2ee();
1602 assert!(!mesh.is_peer_e2ee_enabled());
1603 }
1604
1605 #[test]
1606 fn test_peer_e2ee_initiate_session() {
1607 let mesh = create_mesh(0x11111111, "ALPHA-1");
1608 mesh.enable_peer_e2ee();
1609
1610 let key_exchange = mesh.initiate_peer_e2ee(NodeId::new(0x22222222), 1000);
1611 assert!(key_exchange.is_some());
1612
1613 let key_exchange = key_exchange.unwrap();
1614 assert_eq!(key_exchange[0], crate::document::KEY_EXCHANGE_MARKER);
1616
1617 assert_eq!(mesh.peer_e2ee_session_count(), 1);
1619 assert_eq!(mesh.peer_e2ee_established_count(), 0);
1620 }
1621
1622 #[test]
1623 fn test_peer_e2ee_full_handshake() {
1624 let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1625 let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1626
1627 mesh1.enable_peer_e2ee();
1628 mesh2.enable_peer_e2ee();
1629
1630 let observer1 = Arc::new(CollectingObserver::new());
1631 let observer2 = Arc::new(CollectingObserver::new());
1632 mesh1.add_observer(observer1.clone());
1633 mesh2.add_observer(observer2.clone());
1634
1635 let key_exchange1 = mesh1
1637 .initiate_peer_e2ee(NodeId::new(0x22222222), 1000)
1638 .unwrap();
1639
1640 let response = mesh2.handle_key_exchange(&key_exchange1, 1000);
1642 assert!(response.is_some());
1643
1644 assert!(mesh2.has_peer_e2ee_session(NodeId::new(0x11111111)));
1646
1647 let key_exchange2 = response.unwrap();
1649 let _ = mesh1.handle_key_exchange(&key_exchange2, 1000);
1650
1651 assert!(mesh1.has_peer_e2ee_session(NodeId::new(0x22222222)));
1653
1654 let events1 = observer1.events();
1656 assert!(events1
1657 .iter()
1658 .any(|e| matches!(e, HiveEvent::PeerE2eeEstablished { .. })));
1659
1660 let events2 = observer2.events();
1661 assert!(events2
1662 .iter()
1663 .any(|e| matches!(e, HiveEvent::PeerE2eeEstablished { .. })));
1664 }
1665
1666 #[test]
1667 fn test_peer_e2ee_encrypt_decrypt() {
1668 let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1669 let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1670
1671 mesh1.enable_peer_e2ee();
1672 mesh2.enable_peer_e2ee();
1673
1674 let key_exchange1 = mesh1
1676 .initiate_peer_e2ee(NodeId::new(0x22222222), 1000)
1677 .unwrap();
1678 let key_exchange2 = mesh2.handle_key_exchange(&key_exchange1, 1000).unwrap();
1679 mesh1.handle_key_exchange(&key_exchange2, 1000);
1680
1681 let plaintext = b"Secret message from mesh1";
1683 let encrypted = mesh1.send_peer_e2ee(NodeId::new(0x22222222), plaintext, 2000);
1684 assert!(encrypted.is_some());
1685
1686 let encrypted = encrypted.unwrap();
1687 assert_eq!(encrypted[0], crate::document::PEER_E2EE_MARKER);
1689
1690 let observer2 = Arc::new(CollectingObserver::new());
1692 mesh2.add_observer(observer2.clone());
1693
1694 let decrypted = mesh2.handle_peer_e2ee_message(&encrypted, 2000);
1695 assert!(decrypted.is_some());
1696 assert_eq!(decrypted.unwrap(), plaintext);
1697
1698 let events = observer2.events();
1700 assert!(events.iter().any(|e| matches!(
1701 e,
1702 HiveEvent::PeerE2eeMessageReceived { from_node, data }
1703 if from_node.as_u32() == 0x11111111 && data == plaintext
1704 )));
1705 }
1706
1707 #[test]
1708 fn test_peer_e2ee_bidirectional() {
1709 let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1710 let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1711
1712 mesh1.enable_peer_e2ee();
1713 mesh2.enable_peer_e2ee();
1714
1715 let key_exchange1 = mesh1
1717 .initiate_peer_e2ee(NodeId::new(0x22222222), 1000)
1718 .unwrap();
1719 let key_exchange2 = mesh2.handle_key_exchange(&key_exchange1, 1000).unwrap();
1720 mesh1.handle_key_exchange(&key_exchange2, 1000);
1721
1722 let msg1 = mesh1
1724 .send_peer_e2ee(NodeId::new(0x22222222), b"Hello from mesh1", 2000)
1725 .unwrap();
1726 let dec1 = mesh2.handle_peer_e2ee_message(&msg1, 2000).unwrap();
1727 assert_eq!(dec1, b"Hello from mesh1");
1728
1729 let msg2 = mesh2
1731 .send_peer_e2ee(NodeId::new(0x11111111), b"Hello from mesh2", 2000)
1732 .unwrap();
1733 let dec2 = mesh1.handle_peer_e2ee_message(&msg2, 2000).unwrap();
1734 assert_eq!(dec2, b"Hello from mesh2");
1735 }
1736
1737 #[test]
1738 fn test_peer_e2ee_close_session() {
1739 let mesh = create_mesh(0x11111111, "ALPHA-1");
1740 mesh.enable_peer_e2ee();
1741
1742 let observer = Arc::new(CollectingObserver::new());
1743 mesh.add_observer(observer.clone());
1744
1745 mesh.initiate_peer_e2ee(NodeId::new(0x22222222), 1000);
1747 assert_eq!(mesh.peer_e2ee_session_count(), 1);
1748
1749 mesh.close_peer_e2ee(NodeId::new(0x22222222));
1751
1752 let events = observer.events();
1754 assert!(events
1755 .iter()
1756 .any(|e| matches!(e, HiveEvent::PeerE2eeClosed { .. })));
1757 }
1758
1759 #[test]
1760 fn test_peer_e2ee_without_enabling() {
1761 let mesh = create_mesh(0x11111111, "ALPHA-1");
1762
1763 let result = mesh.initiate_peer_e2ee(NodeId::new(0x22222222), 1000);
1765 assert!(result.is_none());
1766
1767 let result = mesh.send_peer_e2ee(NodeId::new(0x22222222), b"test", 1000);
1768 assert!(result.is_none());
1769
1770 assert!(!mesh.has_peer_e2ee_session(NodeId::new(0x22222222)));
1771 }
1772
1773 #[test]
1774 fn test_peer_e2ee_overhead() {
1775 let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1776 let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1777
1778 mesh1.enable_peer_e2ee();
1779 mesh2.enable_peer_e2ee();
1780
1781 let key_exchange1 = mesh1
1783 .initiate_peer_e2ee(NodeId::new(0x22222222), 1000)
1784 .unwrap();
1785 let key_exchange2 = mesh2.handle_key_exchange(&key_exchange1, 1000).unwrap();
1786 mesh1.handle_key_exchange(&key_exchange2, 1000);
1787
1788 let plaintext = b"Test message";
1790 let encrypted = mesh1
1791 .send_peer_e2ee(NodeId::new(0x22222222), plaintext, 2000)
1792 .unwrap();
1793
1794 let overhead = encrypted.len() - plaintext.len();
1803 assert_eq!(overhead, 46);
1804 }
1805
1806 fn create_strict_encrypted_mesh(node_id: u32, callsign: &str, secret: [u8; 32]) -> HiveMesh {
1809 let config = HiveMeshConfig::new(NodeId::new(node_id), callsign, "TEST")
1810 .with_encryption(secret)
1811 .with_strict_encryption();
1812 HiveMesh::new(config)
1813 }
1814
1815 #[test]
1816 fn test_strict_encryption_enabled() {
1817 let secret = [0x42u8; 32];
1818 let mesh = create_strict_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1819
1820 assert!(mesh.is_encryption_enabled());
1821 assert!(mesh.is_strict_encryption_enabled());
1822 }
1823
1824 #[test]
1825 fn test_strict_encryption_disabled_by_default() {
1826 let secret = [0x42u8; 32];
1827 let mesh = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1828
1829 assert!(mesh.is_encryption_enabled());
1830 assert!(!mesh.is_strict_encryption_enabled());
1831 }
1832
1833 #[test]
1834 fn test_strict_encryption_requires_encryption_enabled() {
1835 let config = HiveMeshConfig::new(NodeId::new(0x11111111), "ALPHA-1", "TEST")
1837 .with_strict_encryption(); let mesh = HiveMesh::new(config);
1839
1840 assert!(!mesh.is_encryption_enabled());
1841 assert!(!mesh.is_strict_encryption_enabled());
1842 }
1843
1844 #[test]
1845 fn test_strict_mode_accepts_encrypted_documents() {
1846 let secret = [0x42u8; 32];
1847 let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1848 let mesh2 = create_strict_encrypted_mesh(0x22222222, "BRAVO-1", secret);
1849
1850 let doc = mesh1.build_document();
1852 assert_eq!(doc[0], crate::document::ENCRYPTED_MARKER);
1853
1854 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1856 assert!(result.is_some());
1857 }
1858
1859 #[test]
1860 fn test_strict_mode_rejects_unencrypted_documents() {
1861 let secret = [0x42u8; 32];
1862 let mesh1 = create_mesh(0x11111111, "ALPHA-1"); let mesh2 = create_strict_encrypted_mesh(0x22222222, "BRAVO-1", secret); let observer = Arc::new(CollectingObserver::new());
1866 mesh2.add_observer(observer.clone());
1867
1868 let doc = mesh1.build_document();
1870 assert_ne!(doc[0], crate::document::ENCRYPTED_MARKER);
1871
1872 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1874 assert!(result.is_none());
1875
1876 let events = observer.events();
1878 assert!(events.iter().any(|e| matches!(
1879 e,
1880 HiveEvent::SecurityViolation {
1881 kind: crate::observer::SecurityViolationKind::UnencryptedInStrictMode,
1882 ..
1883 }
1884 )));
1885 }
1886
1887 #[test]
1888 fn test_non_strict_mode_accepts_unencrypted_documents() {
1889 let secret = [0x42u8; 32];
1890 let mesh1 = create_mesh(0x11111111, "ALPHA-1"); let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret); let doc = mesh1.build_document();
1895
1896 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1898 assert!(result.is_some());
1899 }
1900
1901 #[test]
1902 fn test_strict_mode_security_violation_event_includes_source() {
1903 let secret = [0x42u8; 32];
1904 let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1905 let mesh2 = create_strict_encrypted_mesh(0x22222222, "BRAVO-1", secret);
1906
1907 let observer = Arc::new(CollectingObserver::new());
1908 mesh2.add_observer(observer.clone());
1909
1910 let doc = mesh1.build_document();
1911
1912 mesh2.on_ble_discovered(
1914 "test-device-uuid",
1915 Some("HIVE_TEST-11111111"),
1916 -65,
1917 Some("TEST"),
1918 500,
1919 );
1920 mesh2.on_ble_connected("test-device-uuid", 600);
1921
1922 let _result = mesh2.on_ble_data_received("test-device-uuid", &doc, 1000);
1923
1924 let events = observer.events();
1926 let violation = events.iter().find(|e| {
1927 matches!(
1928 e,
1929 HiveEvent::SecurityViolation {
1930 kind: crate::observer::SecurityViolationKind::UnencryptedInStrictMode,
1931 ..
1932 }
1933 )
1934 });
1935 assert!(violation.is_some());
1936
1937 if let Some(HiveEvent::SecurityViolation { source, .. }) = violation {
1938 assert!(source.is_some());
1939 assert_eq!(source.as_ref().unwrap(), "test-device-uuid");
1940 }
1941 }
1942
1943 #[test]
1944 fn test_decryption_failure_emits_security_violation() {
1945 let secret1 = [0x42u8; 32];
1946 let secret2 = [0x43u8; 32]; let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret1);
1948 let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret2);
1949
1950 let observer = Arc::new(CollectingObserver::new());
1951 mesh2.add_observer(observer.clone());
1952
1953 let doc = mesh1.build_document();
1955
1956 let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1958 assert!(result.is_none());
1959
1960 let events = observer.events();
1962 assert!(events.iter().any(|e| matches!(
1963 e,
1964 HiveEvent::SecurityViolation {
1965 kind: crate::observer::SecurityViolationKind::DecryptionFailed,
1966 ..
1967 }
1968 )));
1969 }
1970
1971 #[test]
1972 fn test_strict_mode_builder_chain() {
1973 let secret = [0x42u8; 32];
1974 let config = HiveMeshConfig::new(NodeId::new(0x11111111), "ALPHA-1", "TEST")
1975 .with_encryption(secret)
1976 .with_strict_encryption()
1977 .with_sync_interval(10_000)
1978 .with_peer_timeout(60_000);
1979
1980 let mesh = HiveMesh::new(config);
1981
1982 assert!(mesh.is_encryption_enabled());
1983 assert!(mesh.is_strict_encryption_enabled());
1984 }
1985}