1use crate::client::Client;
2use crate::store::signal_adapter::SignalProtocolStoreAdapter;
3use crate::types::events::Event;
4use crate::types::message::MessageInfo;
5use chrono::DateTime;
6use log::warn;
7use prost::Message as ProtoMessage;
8use rand::TryRngCore;
9use std::sync::Arc;
10use wacore::libsignal::crypto::DecryptionError;
11use wacore::libsignal::protocol::SenderKeyDistributionMessage;
12use wacore::libsignal::protocol::group_decrypt;
13use wacore::libsignal::protocol::process_sender_key_distribution_message;
14use wacore::libsignal::protocol::{
15 PreKeySignalMessage, SignalMessage, SignalProtocolError, UsePQRatchet, message_decrypt,
16};
17use wacore::libsignal::protocol::{
18 PublicKey as SignalPublicKey, SENDERKEY_MESSAGE_CURRENT_VERSION,
19};
20use wacore::libsignal::store::sender_key_name::SenderKeyName;
21use wacore::messages::MessageUtils;
22use wacore::types::jid::JidExt;
23use wacore_binary::jid::Jid;
24use wacore_binary::jid::JidExt as _;
25use wacore_binary::node::Node;
26use waproto::whatsapp::{self as wa};
27
28impl Client {
29 pub(crate) async fn handle_encrypted_message(self: Arc<Self>, node: Arc<Node>) {
30 let info = match self.parse_message_info(&node).await {
31 Ok(info) => info,
32 Err(e) => {
33 log::warn!("Failed to parse message info: {e:?}");
34 return;
35 }
36 };
37
38 let sender_encryption_jid = {
41 let sender = &info.source.sender;
42 let alt = info.source.sender_alt.as_ref();
43 let pn_server = wacore_binary::jid::DEFAULT_USER_SERVER;
44 let lid_server = wacore_binary::jid::HIDDEN_USER_SERVER;
45
46 if sender.server == lid_server {
47 if let Some(alt_jid) = alt {
48 if alt_jid.server == pn_server {
49 alt_jid.clone()
50 } else {
51 sender.clone()
53 }
54 } else if info.source.is_from_me {
55 if let Some(own_pn) = self.get_pn().await {
57 log::debug!(
58 "Self-sent message from LID {}, using own phone number {}:{} for decryption",
59 sender,
60 own_pn.user,
61 sender.device
62 );
63 Jid {
64 user: own_pn.user,
65 server: own_pn.server,
66 agent: own_pn.agent,
67 device: sender.device,
68 integrator: own_pn.integrator,
69 }
70 } else {
71 log::warn!("Self-sent message from LID but own phone number not available");
72 sender.clone()
73 }
74 } else {
75 sender.clone()
77 }
78 } else {
79 sender.clone()
81 }
82 };
83
84 log::debug!(
85 "Message from {} (sender: {}, encryption JID: {}, is_from_me: {})",
86 info.source.chat,
87 info.source.sender,
88 sender_encryption_jid,
89 info.source.is_from_me
90 );
91
92 let mut all_enc_nodes = Vec::new();
93
94 let direct_enc_nodes = node.get_children_by_tag("enc");
95 all_enc_nodes.extend(direct_enc_nodes);
96
97 let participants = node.get_optional_child_by_tag(&["participants"]);
98 if let Some(participants_node) = participants {
99 let to_nodes = participants_node.get_children_by_tag("to");
100 for to_node in to_nodes {
101 let to_jid = to_node.attrs().string("jid");
102 let own_jid = self.get_pn().await;
103
104 if let Some(our_jid) = own_jid
105 && to_jid == our_jid.to_string()
106 {
107 let enc_children = to_node.get_children_by_tag("enc");
108 all_enc_nodes.extend(enc_children);
109 }
110 }
111 }
112
113 if all_enc_nodes.is_empty() {
114 log::warn!("Received message without <enc> child: {}", node.tag);
115 return;
116 }
117
118 let mut session_enc_nodes = Vec::new();
119 let mut group_content_enc_nodes = Vec::new();
120
121 for &enc_node in &all_enc_nodes {
122 let enc_type = enc_node.attrs().string("type");
123
124 if let Some(handler) = self.custom_enc_handlers.get(&enc_type) {
126 let handler_clone = handler.clone();
127 let client_clone = self.clone();
128 let info_clone = info.clone();
129 let enc_node_clone = Arc::new(enc_node.clone());
130
131 tokio::spawn(async move {
132 if let Err(e) = handler_clone
133 .handle(client_clone, &enc_node_clone, &info_clone)
134 .await
135 {
136 log::warn!("Custom handler for enc type '{}' failed: {e:?}", enc_type);
137 }
138 });
139 continue;
140 }
141
142 match enc_type.as_str() {
144 "pkmsg" | "msg" => session_enc_nodes.push(enc_node),
145 "skmsg" => group_content_enc_nodes.push(enc_node),
146 _ => log::warn!("Unknown enc type: {enc_type}"),
147 }
148 }
149
150 log::debug!(
151 "Starting PASS 1: Processing {} session establishment messages (pkmsg/msg)",
152 session_enc_nodes.len()
153 );
154 let (
155 session_decrypted_successfully,
156 session_had_duplicates,
157 session_dispatched_undecryptable,
158 ) = self
159 .clone()
160 .process_session_enc_batch(&session_enc_nodes, &info, &sender_encryption_jid)
161 .await;
162
163 log::debug!(
164 "Starting PASS 2: Processing {} group content messages (skmsg)",
165 group_content_enc_nodes.len()
166 );
167
168 if !group_content_enc_nodes.is_empty() {
174 let should_process_skmsg = session_enc_nodes.is_empty()
175 || session_decrypted_successfully
176 || session_had_duplicates;
177
178 if should_process_skmsg {
179 if let Err(e) = self
180 .clone()
181 .process_group_enc_batch(
182 &group_content_enc_nodes,
183 &info,
184 &sender_encryption_jid,
185 )
186 .await
187 {
188 log::warn!("Batch group decrypt encountered error (continuing): {e:?}");
189 }
190 } else {
191 if !session_had_duplicates {
193 warn!(
194 "Skipping skmsg decryption for message {} from {} because the initial session/senderkey message failed to decrypt. This prevents a retry loop.",
195 info.id, info.source.sender
196 );
197 if !session_dispatched_undecryptable {
200 self.core.event_bus.dispatch(&Event::UndecryptableMessage(
201 crate::types::events::UndecryptableMessage {
202 info: info.clone(),
203 is_unavailable: false,
204 unavailable_type: crate::types::events::UnavailableType::Unknown,
205 decrypt_fail_mode: crate::types::events::DecryptFailMode::Show,
206 },
207 ));
208 }
209
210 }
216 }
219 } else if !session_decrypted_successfully
220 && !session_had_duplicates
221 && !session_enc_nodes.is_empty()
222 {
223 warn!(
225 "Message {} from {} failed to decrypt and has no group content. Dispatching UndecryptableMessage event.",
226 info.id, info.source.sender
227 );
228 self.core.event_bus.dispatch(&Event::UndecryptableMessage(
232 crate::types::events::UndecryptableMessage {
233 info: info.clone(),
234 is_unavailable: false,
235 unavailable_type: crate::types::events::UnavailableType::Unknown,
236 decrypt_fail_mode: crate::types::events::DecryptFailMode::Show,
237 },
238 ));
239 }
241 }
242
243 async fn process_session_enc_batch(
244 self: Arc<Self>,
245 enc_nodes: &[&wacore_binary::node::Node],
246 info: &MessageInfo,
247 sender_encryption_jid: &Jid,
248 ) -> (bool, bool, bool) {
249 use wacore::libsignal::protocol::CiphertextMessage;
251 if enc_nodes.is_empty() {
252 return (false, false, false);
253 }
254
255 let mut adapter =
256 SignalProtocolStoreAdapter::new(self.persistence_manager.get_device_arc().await);
257 let rng = rand::rngs::OsRng;
258 let mut any_success = false;
259 let mut any_duplicate = false;
260 let mut dispatched_undecryptable = false;
261
262 for enc_node in enc_nodes {
263 let ciphertext = match &enc_node.content {
264 Some(wacore_binary::node::NodeContent::Bytes(b)) => b.clone(),
265 _ => {
266 log::warn!("Enc node has no byte content (batch session)");
267 continue;
268 }
269 };
270 let enc_type = enc_node.attrs().string("type");
271 let padding_version = enc_node.attrs().optional_u64("v").unwrap_or(2) as u8;
272
273 let parsed_message = if enc_type == "pkmsg" {
274 match PreKeySignalMessage::try_from(ciphertext.as_slice()) {
275 Ok(m) => CiphertextMessage::PreKeySignalMessage(m),
276 Err(e) => {
277 log::error!("Failed to parse PreKeySignalMessage: {e:?}");
278 continue;
279 }
280 }
281 } else {
282 match SignalMessage::try_from(ciphertext.as_slice()) {
283 Ok(m) => CiphertextMessage::SignalMessage(m),
284 Err(e) => {
285 log::error!("Failed to parse SignalMessage: {e:?}");
286 continue;
287 }
288 }
289 };
290
291 let signal_address = sender_encryption_jid.to_protocol_address();
292
293 let decrypt_res = message_decrypt(
294 &parsed_message,
295 &signal_address,
296 &mut adapter.session_store,
297 &mut adapter.identity_store,
298 &mut adapter.pre_key_store,
299 &adapter.signed_pre_key_store,
300 &mut rng.unwrap_err(),
301 UsePQRatchet::No,
302 )
303 .await;
304
305 match decrypt_res {
306 Ok(padded_plaintext) => {
307 any_success = true;
308 if let Err(e) = self
309 .clone()
310 .handle_decrypted_plaintext(
311 &enc_type,
312 &padded_plaintext,
313 padding_version,
314 info,
315 )
316 .await
317 {
318 log::warn!("Failed processing plaintext (batch session): {e:?}");
319 }
320 }
321 Err(e) => {
322 if let SignalProtocolError::DuplicatedMessage(chain, counter) = e {
324 log::debug!(
325 "Skipping already-processed message from {} (chain {}, counter {}). This is normal during reconnection.",
326 info.source.sender,
327 chain,
328 counter
329 );
330 any_duplicate = true;
332 continue;
333 }
334 if let SignalProtocolError::SessionNotFound(_) = e {
336 warn!(
337 "Gracefully failing decryption for {} from {} due to missing session. This is common during offline sync. Dispatching UndecryptableMessage event.",
338 enc_type, info.source.sender
339 );
340 self.core.event_bus.dispatch(&Event::UndecryptableMessage(
342 crate::types::events::UndecryptableMessage {
343 info: info.clone(),
344 is_unavailable: false,
345 unavailable_type: crate::types::events::UnavailableType::Unknown,
346 decrypt_fail_mode: crate::types::events::DecryptFailMode::Show,
347 },
348 ));
349 dispatched_undecryptable = true;
350 continue;
352 } else {
353 log::error!("Batch session decrypt failed (type: {}): {:?}", enc_type, e);
355 }
356 }
357 }
358 }
359 (any_success, any_duplicate, dispatched_undecryptable)
360 }
361
362 async fn process_group_enc_batch(
363 self: Arc<Self>,
364 enc_nodes: &[&wacore_binary::node::Node],
365 info: &MessageInfo,
366 _sender_encryption_jid: &Jid,
367 ) -> Result<(), DecryptionError> {
368 if enc_nodes.is_empty() {
369 return Ok(());
370 }
371 let device_arc = self.persistence_manager.get_device_arc().await;
372
373 for enc_node in enc_nodes {
374 let ciphertext = match &enc_node.content {
375 Some(wacore_binary::node::NodeContent::Bytes(b)) => b.clone(),
376 _ => {
377 log::warn!("Enc node has no byte content (batch group)");
378 continue;
379 }
380 };
381 let padding_version = enc_node.attrs().optional_u64("v").unwrap_or(2) as u8;
382
383 let sender_address = info.source.sender.to_protocol_address();
388 let sender_key_name =
389 SenderKeyName::new(info.source.chat.to_string(), sender_address.to_string());
390
391 log::debug!(
392 "Looking up sender key for group {} with sender address {} (from sender JID: {})",
393 info.source.chat,
394 sender_address,
395 info.source.sender
396 );
397
398 let decrypt_result = {
399 let mut device_guard = device_arc.write().await;
400 group_decrypt(ciphertext.as_slice(), &mut *device_guard, &sender_key_name).await
401 };
402
403 match decrypt_result {
404 Ok(padded_plaintext) => {
405 if let Err(e) = self
406 .clone()
407 .handle_decrypted_plaintext(
408 "skmsg",
409 &padded_plaintext,
410 padding_version,
411 info,
412 )
413 .await
414 {
415 log::warn!("Failed processing group plaintext (batch): {e:?}");
416 }
417 }
418 Err(SignalProtocolError::DuplicatedMessage(iteration, counter)) => {
419 log::debug!(
420 "Skipping already-processed sender key message from {} in group {} (iteration {}, counter {}). This is normal during reconnection.",
421 info.source.sender,
422 info.source.chat,
423 iteration,
424 counter
425 );
426 }
428 Err(SignalProtocolError::NoSenderKeyState) => {
429 warn!(
430 "No sender key state for batched group message from {}, sending retry receipt.",
431 info.source.sender
432 );
433 let client_clone = self.clone();
434 let info_clone = info.clone();
435 tokio::spawn(async move {
436 if let Err(e) = client_clone.send_retry_receipt(&info_clone).await {
437 log::error!("Failed to send retry receipt (batch): {:?}", e);
438 }
439 });
440 }
441 Err(e) => {
442 log::error!(
443 "Group batch decrypt failed for group {} sender {}: {:?}",
444 sender_key_name.group_id(),
445 sender_key_name.sender_id(),
446 e
447 );
448 }
449 }
450 }
451 Ok(())
452 }
453
454 async fn handle_decrypted_plaintext(
455 self: Arc<Self>,
456 enc_type: &str,
457 padded_plaintext: &[u8],
458 padding_version: u8,
459 info: &MessageInfo,
460 ) -> Result<(), anyhow::Error> {
461 let plaintext_slice = MessageUtils::unpad_message_ref(padded_plaintext, padding_version)?;
462 log::info!(
463 "Successfully decrypted message from {}: {} bytes (type: {}) [batch path]",
464 info.source.sender,
465 plaintext_slice.len(),
466 enc_type
467 );
468
469 if enc_type == "skmsg" {
470 match wa::Message::decode(plaintext_slice) {
471 Ok(group_msg) => {
472 self.core
473 .event_bus
474 .dispatch(&Event::Message(Box::new(group_msg), info.clone()));
475 }
476 Err(e) => log::warn!("Failed to unmarshal decrypted skmsg plaintext: {e}"),
477 }
478 } else {
479 match wa::Message::decode(plaintext_slice) {
480 Ok(original_msg) => {
481 if let Some(skdm) = &original_msg.sender_key_distribution_message
482 && let Some(axolotl_bytes) = &skdm.axolotl_sender_key_distribution_message
483 {
484 self.handle_sender_key_distribution_message(
485 &info.source.chat,
486 &info.source.sender,
487 axolotl_bytes,
488 )
489 .await;
490 }
491
492 if let Some(protocol_msg) = &original_msg.protocol_message
493 && let Some(keys) = &protocol_msg.app_state_sync_key_share
494 {
495 self.handle_app_state_sync_key_share(keys).await;
496 }
497
498 if let Some(protocol_msg) = &original_msg.protocol_message
499 && let Some(history_sync) = &protocol_msg.history_sync_notification
500 {
501 log::info!(
502 "Received HistorySyncNotification, dispatching for download and processing."
503 );
504 let client_clone = self.clone();
505 let history_sync_clone = history_sync.clone();
506 let msg_id = info.id.clone();
507 tokio::spawn(async move {
508 client_clone
510 .handle_history_sync(msg_id, history_sync_clone)
511 .await;
512 });
513 }
514
515 self.core
516 .event_bus
517 .dispatch(&Event::Message(Box::new(original_msg), info.clone()));
518 }
519 Err(e) => log::warn!("Failed to unmarshal decrypted pkmsg/msg plaintext: {e}"),
520 }
521 }
522 Ok(())
523 }
524
525 pub(crate) async fn parse_message_info(
526 &self,
527 node: &Node,
528 ) -> Result<MessageInfo, anyhow::Error> {
529 let mut attrs = node.attrs();
530 let device_snapshot = self.persistence_manager.get_device_snapshot().await;
531 let own_jid = device_snapshot.pn.clone().unwrap_or_default();
532 let own_lid = device_snapshot.lid.clone();
533 let from = attrs.jid("from");
534
535 let mut source = if from.server == wacore_binary::jid::BROADCAST_SERVER {
536 let participant = attrs.jid("participant");
538 let is_from_me = participant.is_same_user_as(&own_jid)
539 || (own_lid.is_some() && participant.is_same_user_as(own_lid.as_ref().unwrap()));
540
541 crate::types::message::MessageSource {
542 chat: from.clone(),
543 sender: participant.clone(),
544 is_from_me,
545 is_group: true, broadcast_list_owner: if from.user != wacore_binary::jid::STATUS_BROADCAST_USER {
547 Some(participant.clone())
548 } else {
549 None
550 },
551 ..Default::default()
552 }
553 } else if from.is_group() {
554 let sender = attrs.jid("participant");
555 let sender_alt = if let Some(addressing_mode) = attrs
556 .optional_string("addressing_mode")
557 .map(|s| s.to_ascii_lowercase())
558 {
559 match addressing_mode.as_str() {
560 "lid" => attrs.optional_jid("participant_pn"),
561 _ => attrs.optional_jid("participant_lid"),
562 }
563 } else {
564 None
565 };
566
567 let is_from_me = sender.is_same_user_as(&own_jid)
568 || (own_lid.is_some() && sender.is_same_user_as(own_lid.as_ref().unwrap()));
569
570 crate::types::message::MessageSource {
571 chat: from.clone(),
572 sender: sender.clone(),
573 is_from_me,
574 is_group: true,
575 sender_alt,
576 ..Default::default()
577 }
578 } else if from.is_same_user_as(&own_jid) {
579 crate::types::message::MessageSource {
580 chat: attrs.non_ad_jid("recipient"),
581 sender: from.clone(),
582 is_from_me: true,
583 ..Default::default()
584 }
585 } else {
586 crate::types::message::MessageSource {
587 chat: from.to_non_ad(),
588 sender: from.clone(),
589 is_from_me: false,
590 ..Default::default()
591 }
592 };
593
594 source.addressing_mode = attrs
595 .optional_string("addressing_mode")
596 .map(|s| s.to_ascii_lowercase())
597 .and_then(|s| match s.as_str() {
598 "pn" => Some(crate::types::message::AddressingMode::Pn),
599 "lid" => Some(crate::types::message::AddressingMode::Lid),
600 _ => None,
601 });
602
603 Ok(MessageInfo {
604 source,
605 id: attrs.string("id"),
606 push_name: attrs
607 .optional_string("notify")
608 .map(|s| s.to_string())
609 .unwrap_or_default(),
610 timestamp: DateTime::from_timestamp(attrs.unix_time("t"), 0).unwrap(),
611 ..Default::default()
612 })
613 }
614
615 pub(crate) async fn handle_app_state_sync_key_share(
616 &self,
617 keys: &wa::message::AppStateSyncKeyShare,
618 ) {
619 let device_snapshot = self.persistence_manager.get_device_snapshot().await;
620 let key_store = device_snapshot.backend.clone();
621
622 let mut stored_count = 0;
623 let mut failed_count = 0;
624
625 for key in &keys.keys {
626 if let Some(key_id_proto) = &key.key_id
627 && let Some(key_id) = &key_id_proto.key_id
628 && let Some(key_data) = &key.key_data
629 && let Some(fingerprint) = &key_data.fingerprint
630 && let Some(data) = &key_data.key_data
631 {
632 let fingerprint_bytes = fingerprint.encode_to_vec();
633 let new_key = crate::store::traits::AppStateSyncKey {
634 key_data: data.clone(),
635 fingerprint: fingerprint_bytes,
636 timestamp: key_data.timestamp(),
637 };
638
639 if let Err(e) = key_store.set_app_state_sync_key(key_id, new_key).await {
640 log::error!(
641 "Failed to store app state sync key {:?}: {:?}",
642 hex::encode(key_id),
643 e
644 );
645 failed_count += 1;
646 } else {
647 stored_count += 1;
648 }
649 }
650 }
651
652 if stored_count > 0 || failed_count > 0 {
653 log::info!(
654 target: "Client/AppState",
655 "Processed app state key share: {} stored, {} failed.",
656 stored_count,
657 failed_count
658 );
659 }
660
661 if stored_count > 0
663 && !self
664 .initial_app_state_keys_received
665 .swap(true, std::sync::atomic::Ordering::Relaxed)
666 {
667 self.initial_keys_synced_notifier.notify_waiters();
669 }
670 }
671
672 async fn handle_sender_key_distribution_message(
673 self: &Arc<Self>,
674 group_jid: &Jid,
675 sender_jid: &Jid,
676 axolotl_bytes: &[u8],
677 ) {
678 let skdm = match SenderKeyDistributionMessage::try_from(axolotl_bytes) {
679 Ok(msg) => msg,
680 Err(e1) => match wa::SenderKeyDistributionMessage::decode(axolotl_bytes) {
681 Ok(go_msg) => {
682 match SignalPublicKey::from_djb_public_key_bytes(&go_msg.signing_key.unwrap()) {
683 Ok(pub_key) => {
684 match SenderKeyDistributionMessage::new(
685 SENDERKEY_MESSAGE_CURRENT_VERSION,
686 go_msg.id.unwrap(),
687 go_msg.iteration.unwrap(),
688 go_msg.chain_key.unwrap(),
689 pub_key,
690 ) {
691 Ok(skdm) => skdm,
692 Err(e) => {
693 log::error!(
694 "Failed to construct SKDM from Go format from {}: {:?} (original parse error: {:?})",
695 sender_jid,
696 e,
697 e1
698 );
699 return;
700 }
701 }
702 }
703 Err(e) => {
704 log::error!(
705 "Failed to parse public key from Go SKDM for {}: {:?} (original parse error: {:?})",
706 sender_jid,
707 e,
708 e1
709 );
710 return;
711 }
712 }
713 }
714 Err(e2) => {
715 log::error!(
716 "Failed to parse SenderKeyDistributionMessage (standard and Go fallback) from {}: primary: {:?}, fallback: {:?}",
717 sender_jid,
718 e1,
719 e2
720 );
721 return;
722 }
723 },
724 };
725
726 let device_arc = self.persistence_manager.get_device_arc().await;
727 let mut device_guard = device_arc.write().await;
728
729 let sender_address = sender_jid.to_protocol_address();
730
731 let sender_key_name = SenderKeyName::new(group_jid.to_string(), sender_address.to_string());
732
733 if let Err(e) =
734 process_sender_key_distribution_message(&sender_key_name, &skdm, &mut *device_guard)
735 .await
736 {
737 log::error!(
738 "Failed to process SenderKeyDistributionMessage from {}: {:?}",
739 sender_jid,
740 e
741 );
742 } else {
743 log::info!(
744 "Successfully processed sender key distribution for group {} from {}",
745 group_jid,
746 sender_jid
747 );
748 }
749 }
750}
751
752#[cfg(test)]
753mod tests {
754 use super::*;
755 use crate::store::persistence_manager::PersistenceManager;
756 use crate::store::sqlite_store::SqliteStore;
757 use std::sync::Arc;
758 use wacore_binary::builder::NodeBuilder;
759 use wacore_binary::jid::Jid;
760
761 #[tokio::test]
762 async fn test_parse_message_info_for_status_broadcast() {
763 let backend = Arc::new(
765 SqliteStore::new("file:memdb_status_test?mode=memory&cache=shared")
766 .await
767 .expect("Failed to create test backend"),
768 );
769 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
770 let (client, _sync_rx) = Client::new(pm).await;
771
772 let participant_jid_str = "556899336555:42@s.whatsapp.net";
773 let status_broadcast_jid_str = "status@broadcast";
774
775 let node = NodeBuilder::new("message")
777 .attr("from", status_broadcast_jid_str)
778 .attr("id", "8A8CCCC7E6E466D9EE8CA11A967E485A")
779 .attr("participant", participant_jid_str)
780 .attr("t", "1759295366")
781 .attr("type", "media")
782 .build();
783
784 let info = client
786 .parse_message_info(&node)
787 .await
788 .expect("parse_message_info should not fail");
789
790 let expected_sender: Jid = participant_jid_str.parse().unwrap();
792 let expected_chat: Jid = status_broadcast_jid_str.parse().unwrap();
793
794 assert_eq!(
795 info.source.sender, expected_sender,
796 "The sender should be the 'participant' JID, not 'status@broadcast'"
797 );
798 assert_eq!(
799 info.source.chat, expected_chat,
800 "The chat should be 'status@broadcast'"
801 );
802 assert!(
803 info.source.is_group,
804 "Broadcast messages should be treated as group-like"
805 );
806 }
807
808 #[tokio::test]
809 async fn test_process_session_enc_batch_handles_session_not_found_gracefully() {
810 use wacore::libsignal::protocol::{IdentityKeyPair, KeyPair, SignalMessage};
811
812 let backend = Arc::new(
814 SqliteStore::new("file:memdb_graceful_fail?mode=memory&cache=shared")
815 .await
816 .expect("Failed to create test backend"),
817 );
818 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
819 let (client, _sync_rx) = Client::new(pm).await;
820
821 let sender_jid: Jid = "1234567890@s.whatsapp.net".parse().unwrap();
822 let info = MessageInfo {
823 source: crate::types::message::MessageSource {
824 sender: sender_jid.clone(),
825 chat: sender_jid.clone(),
826 ..Default::default()
827 },
828 ..Default::default()
829 };
830
831 let dummy_key = [0u8; 32];
833 let sender_ratchet = KeyPair::generate(&mut rand::rngs::OsRng.unwrap_err()).public_key;
834 let sender_identity_pair = IdentityKeyPair::generate(&mut rand::rngs::OsRng.unwrap_err());
835 let receiver_identity_pair = IdentityKeyPair::generate(&mut rand::rngs::OsRng.unwrap_err());
836 let signal_message = SignalMessage::new(
837 4,
838 &dummy_key,
839 sender_ratchet,
840 0,
841 0,
842 b"test",
843 sender_identity_pair.identity_key(),
844 receiver_identity_pair.identity_key(),
845 )
846 .unwrap();
847
848 let enc_node = NodeBuilder::new("enc")
849 .attr("type", "msg")
850 .bytes(signal_message.serialized().to_vec())
851 .build();
852 let enc_nodes = vec![&enc_node];
853
854 let (success, had_duplicates, dispatched) = client
858 .process_session_enc_batch(&enc_nodes, &info, &sender_jid)
859 .await;
860
861 assert!(
864 !success && !had_duplicates && dispatched,
865 "process_session_enc_batch should return (false, false, true) when SessionNotFound occurs and dispatches event"
866 );
867
868 }
871
872 #[tokio::test]
873 async fn test_handle_encrypted_message_skips_skmsg_after_msg_failure() {
874 use wacore::libsignal::protocol::{IdentityKeyPair, KeyPair, SignalMessage};
875
876 let backend = Arc::new(
878 SqliteStore::new("file:memdb_skip_skmsg_test?mode=memory&cache=shared")
879 .await
880 .expect("Failed to create test backend"),
881 );
882 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
883 let (client, _sync_rx) = Client::new(pm).await;
884
885 let sender_jid: Jid = "1234567890@s.whatsapp.net".parse().unwrap();
886 let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
887
888 let dummy_key = [0u8; 32];
891 let sender_ratchet = KeyPair::generate(&mut rand::rngs::OsRng.unwrap_err()).public_key;
892 let sender_identity_pair = IdentityKeyPair::generate(&mut rand::rngs::OsRng.unwrap_err());
893 let receiver_identity_pair = IdentityKeyPair::generate(&mut rand::rngs::OsRng.unwrap_err());
894 let signal_message = SignalMessage::new(
895 4,
896 &dummy_key,
897 sender_ratchet,
898 0,
899 0,
900 b"test",
901 sender_identity_pair.identity_key(),
902 receiver_identity_pair.identity_key(),
903 )
904 .unwrap();
905
906 let msg_node = NodeBuilder::new("enc")
907 .attr("type", "msg")
908 .bytes(signal_message.serialized().to_vec())
909 .build();
910
911 let skmsg_node = NodeBuilder::new("enc")
912 .attr("type", "skmsg")
913 .bytes(vec![4, 5, 6])
914 .build();
915
916 let message_node = Arc::new(
917 NodeBuilder::new("message")
918 .attr("from", group_jid.to_string())
919 .attr("participant", sender_jid.to_string())
920 .attr("id", "test-id-123")
921 .attr("t", "12345")
922 .children(vec![msg_node, skmsg_node])
923 .build(),
924 );
925
926 client.handle_encrypted_message(message_node).await;
929
930 }
935
936 #[tokio::test]
955 #[ignore = "Requires valid whatsapp.db with active session to reproduce. This is a known issue documented in README.md"]
956 async fn test_lid_group_message_without_session() {
957 use crate::store::sqlite_store::SqliteStore;
958 use std::sync::Arc;
959 use wacore_binary::builder::NodeBuilder;
960 use wacore_binary::jid::Jid;
961
962 let backend = Arc::new(
970 SqliteStore::new("whatsapp.db")
971 .await
972 .expect("Failed to open whatsapp.db - ensure you have an authenticated session"),
973 );
974 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
975 let (client, _sync_rx) = Client::new(pm).await;
976
977 let lid_sender: Jid = "236395184570386.1:75@lid".parse().unwrap();
979 let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
980
981 let pkmsg_node = NodeBuilder::new("enc")
984 .attr("type", "pkmsg")
985 .attr("v", "2")
986 .bytes(vec![])
987 .build();
988
989 let skmsg_node = NodeBuilder::new("enc")
990 .attr("type", "skmsg")
991 .attr("v", "2")
992 .bytes(vec![])
993 .build();
994
995 let message_node = Arc::new(
996 NodeBuilder::new("message")
997 .attr("from", group_jid.to_string())
998 .attr("participant", lid_sender.to_string())
999 .attr("id", "LID_TEST_MSG_001")
1000 .attr("t", "1759296831")
1001 .attr("type", "text")
1002 .attr("notify", "LID Test User")
1003 .attr("offline", "1")
1004 .attr("addressing_mode", "lid")
1005 .children(vec![pkmsg_node, skmsg_node])
1006 .build(),
1007 );
1008
1009 client.handle_encrypted_message(message_node).await;
1012
1013 println!("ā
LID message handled gracefully (but not decrypted - this is the known issue)");
1020 }
1021
1022 #[tokio::test]
1034 async fn test_self_sent_lid_group_message_sender_key_mismatch() {
1035 use crate::store::sqlite_store::SqliteStore;
1036 use std::sync::Arc;
1037 use wacore::libsignal::protocol::{
1038 SenderKeyStore, create_sender_key_distribution_message,
1039 process_sender_key_distribution_message,
1040 };
1041 use wacore::libsignal::store::sender_key_name::SenderKeyName;
1042
1043 let backend = Arc::new(
1045 SqliteStore::new("file:memdb_sender_key_test?mode=memory&cache=shared")
1046 .await
1047 .expect("Failed to create test backend"),
1048 );
1049 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1050 let (_client, _sync_rx) = Client::new(pm.clone()).await;
1051
1052 let own_lid: Jid = "236395184570386.1:75@lid".parse().unwrap();
1055 let own_phone: Jid = "559984726662:75@s.whatsapp.net".parse().unwrap();
1056 let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
1057
1058 let lid_protocol_address = own_lid.to_protocol_address();
1061 let lid_sender_key_name =
1062 SenderKeyName::new(group_jid.to_string(), lid_protocol_address.to_string());
1063
1064 let device_arc = pm.get_device_arc().await;
1065 let skdm = {
1066 let mut device_guard = device_arc.write().await;
1067 create_sender_key_distribution_message(
1068 &lid_sender_key_name,
1069 &mut *device_guard,
1070 &mut rand::rngs::OsRng.unwrap_err(),
1071 )
1072 .await
1073 .expect("Failed to create SKDM")
1074 };
1075
1076 {
1078 let mut device_guard = device_arc.write().await;
1079 process_sender_key_distribution_message(
1080 &lid_sender_key_name,
1081 &skdm,
1082 &mut *device_guard,
1083 )
1084 .await
1085 .expect("Failed to process SKDM with LID address");
1086 }
1087
1088 println!(
1089 "ā
Step 1: Stored sender key under LID address: {}",
1090 lid_protocol_address
1091 );
1092
1093 let phone_protocol_address = own_phone.to_protocol_address();
1095 let phone_sender_key_name =
1096 SenderKeyName::new(group_jid.to_string(), phone_protocol_address.to_string());
1097
1098 let phone_lookup_result = {
1099 let mut device_guard = device_arc.write().await;
1100 device_guard.load_sender_key(&phone_sender_key_name).await
1101 };
1102
1103 println!(
1104 "ā Step 2: Lookup with phone number address failed (expected): {}",
1105 phone_protocol_address
1106 );
1107 assert!(
1108 phone_lookup_result.unwrap().is_none(),
1109 "Sender key should NOT be found when looking up with phone number address (this demonstrates the bug)"
1110 );
1111
1112 let lid_lookup_result = {
1114 let mut device_guard = device_arc.write().await;
1115 device_guard.load_sender_key(&lid_sender_key_name).await
1116 };
1117
1118 println!("ā
Step 3: Lookup with LID address succeeded (this is the fix)");
1119 assert!(
1120 lid_lookup_result.unwrap().is_some(),
1121 "Sender key SHOULD be found when looking up with LID address (same as storage)"
1122 );
1123
1124 println!("\nšÆ Summary:");
1125 println!(" - LID protocol address: {}", lid_protocol_address);
1126 println!(" - Phone protocol address: {}", phone_protocol_address);
1127 println!(
1128 " - Storage key format: {}:{}",
1129 group_jid, lid_protocol_address
1130 );
1131 println!(" - Bug: Using phone address for lookup after storing with LID address");
1132 println!(" - Fix: Always use info.source.sender (LID) for both storage and retrieval");
1133 }
1134
1135 #[tokio::test]
1140 async fn test_multiple_lid_participants_sender_key_isolation() {
1141 use crate::store::sqlite_store::SqliteStore;
1142 use std::sync::Arc;
1143 use wacore::libsignal::protocol::{
1144 SenderKeyStore, create_sender_key_distribution_message,
1145 process_sender_key_distribution_message,
1146 };
1147 use wacore::libsignal::store::sender_key_name::SenderKeyName;
1148
1149 let backend = Arc::new(
1150 SqliteStore::new("file:memdb_multi_lid_test?mode=memory&cache=shared")
1151 .await
1152 .expect("Failed to create test backend"),
1153 );
1154 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1155 let (_client, _sync_rx) = Client::new(pm.clone()).await;
1156
1157 let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
1158
1159 let participants = vec![
1161 ("236395184570386.1:75@lid", "559984726662:75@s.whatsapp.net"),
1162 ("987654321000000.2:42@lid", "551234567890:42@s.whatsapp.net"),
1163 ("111222333444555.3:10@lid", "559876543210:10@s.whatsapp.net"),
1164 ];
1165
1166 let device_arc = pm.get_device_arc().await;
1167
1168 for (lid_str, _phone_str) in &participants {
1170 let lid_jid: Jid = lid_str.parse().unwrap();
1171 let lid_protocol_address = lid_jid.to_protocol_address();
1172 let lid_sender_key_name =
1173 SenderKeyName::new(group_jid.to_string(), lid_protocol_address.to_string());
1174
1175 let skdm = {
1176 let mut device_guard = device_arc.write().await;
1177 create_sender_key_distribution_message(
1178 &lid_sender_key_name,
1179 &mut *device_guard,
1180 &mut rand::rngs::OsRng.unwrap_err(),
1181 )
1182 .await
1183 .expect("Failed to create SKDM")
1184 };
1185
1186 let mut device_guard = device_arc.write().await;
1187 process_sender_key_distribution_message(
1188 &lid_sender_key_name,
1189 &skdm,
1190 &mut *device_guard,
1191 )
1192 .await
1193 .expect("Failed to process SKDM");
1194 }
1195
1196 for (lid_str, phone_str) in &participants {
1198 let lid_jid: Jid = lid_str.parse().unwrap();
1199 let phone_jid: Jid = phone_str.parse().unwrap();
1200
1201 let lid_protocol_address = lid_jid.to_protocol_address();
1202 let phone_protocol_address = phone_jid.to_protocol_address();
1203
1204 let lid_sender_key_name =
1205 SenderKeyName::new(group_jid.to_string(), lid_protocol_address.to_string());
1206 let phone_sender_key_name =
1207 SenderKeyName::new(group_jid.to_string(), phone_protocol_address.to_string());
1208
1209 let lid_lookup = {
1211 let mut device_guard = device_arc.write().await;
1212 device_guard.load_sender_key(&lid_sender_key_name).await
1213 };
1214 assert!(
1215 lid_lookup.unwrap().is_some(),
1216 "Sender key for {} should be found with LID address",
1217 lid_str
1218 );
1219
1220 let phone_lookup = {
1222 let mut device_guard = device_arc.write().await;
1223 device_guard.load_sender_key(&phone_sender_key_name).await
1224 };
1225 assert!(
1226 phone_lookup.unwrap().is_none(),
1227 "Sender key for {} should NOT be found with phone number address",
1228 lid_str
1229 );
1230 }
1231
1232 println!(
1233 "ā
All {} LID participants have isolated sender keys",
1234 participants.len()
1235 );
1236 }
1237
1238 #[test]
1245 fn test_lid_jid_parsing_edge_cases() {
1246 use wacore_binary::jid::Jid;
1247
1248 let lid1: Jid = "236395184570386.1:75@lid".parse().unwrap();
1250 assert_eq!(lid1.user, "236395184570386.1");
1251 assert_eq!(lid1.device, 75);
1252 assert_eq!(lid1.agent, 0);
1253
1254 let lid2: Jid = "123.456.789.0:50@lid".parse().unwrap();
1256 assert_eq!(lid2.user, "123.456.789.0");
1257 assert_eq!(lid2.device, 50);
1258 assert_eq!(lid2.agent, 0);
1259
1260 let lid3: Jid = "987654321000000.5@lid".parse().unwrap();
1262 assert_eq!(lid3.user, "987654321000000.5");
1263 assert_eq!(lid3.device, 0);
1264 assert_eq!(lid3.agent, 0);
1265
1266 let lid4: Jid = "111222333444555666777.999:1@lid".parse().unwrap();
1268 assert_eq!(lid4.user, "111222333444555666777.999");
1269 assert_eq!(lid4.device, 1);
1270 assert_eq!(lid4.agent, 0);
1271 }
1272
1273 #[test]
1278 fn test_lid_protocol_address_consistency() {
1279 use wacore::types::jid::JidExt as CoreJidExt;
1280 use wacore_binary::jid::Jid;
1281
1282 let test_cases = vec![
1283 ("236395184570386.1:75@lid", "236395184570386.1", 75),
1284 ("987654321000000.2:42@lid", "987654321000000.2", 42),
1285 ("111.222.333:10@lid", "111.222.333", 10),
1286 ];
1287
1288 for (jid_str, expected_name, expected_device) in test_cases {
1289 let lid_jid: Jid = jid_str.parse().unwrap();
1290 let protocol_addr = lid_jid.to_protocol_address();
1291
1292 assert_eq!(
1293 protocol_addr.name(),
1294 expected_name,
1295 "Protocol address name should match user portion exactly for {}",
1296 jid_str
1297 );
1298 assert_eq!(
1299 u32::from(protocol_addr.device_id()),
1300 expected_device,
1301 "Protocol address device should match for {}",
1302 jid_str
1303 );
1304 }
1305 }
1306
1307 #[tokio::test]
1314 async fn test_parse_message_info_sender_alt_extraction() {
1315 use crate::store::sqlite_store::SqliteStore;
1316 use std::sync::Arc;
1317 use wacore_binary::builder::NodeBuilder;
1318
1319 let backend = Arc::new(
1320 SqliteStore::new("file:memdb_sender_alt_test?mode=memory&cache=shared")
1321 .await
1322 .expect("Failed to create test backend"),
1323 );
1324 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1325
1326 {
1328 let device_arc = pm.get_device_arc().await;
1329 let mut device = device_arc.write().await;
1330 device.pn = Some("559984726662@s.whatsapp.net".parse().unwrap());
1331 device.lid = Some("236395184570386.1@lid".parse().unwrap());
1332 }
1333
1334 let (client, _sync_rx) = Client::new(pm).await;
1335
1336 let lid_group_node = NodeBuilder::new("message")
1338 .attr("from", "120363021033254949@g.us")
1339 .attr("participant", "987654321000000.2:42@lid")
1340 .attr("participant_pn", "551234567890:42@s.whatsapp.net")
1341 .attr("addressing_mode", "lid")
1342 .attr("id", "test1")
1343 .attr("t", "12345")
1344 .build();
1345
1346 let info1 = client.parse_message_info(&lid_group_node).await.unwrap();
1347 assert_eq!(info1.source.sender.user, "987654321000000.2");
1348 assert!(info1.source.sender_alt.is_some());
1349 assert_eq!(
1350 info1.source.sender_alt.as_ref().unwrap().user,
1351 "551234567890"
1352 );
1353
1354 let self_lid_node = NodeBuilder::new("message")
1356 .attr("from", "120363021033254949@g.us")
1357 .attr("participant", "236395184570386.1:75@lid")
1358 .attr("participant_pn", "559984726662:75@s.whatsapp.net")
1359 .attr("addressing_mode", "lid")
1360 .attr("id", "test2")
1361 .attr("t", "12346")
1362 .build();
1363
1364 let info2 = client.parse_message_info(&self_lid_node).await.unwrap();
1365 assert!(
1366 info2.source.is_from_me,
1367 "Should detect self-sent LID message"
1368 );
1369 assert_eq!(info2.source.sender.user, "236395184570386.1");
1370 assert!(info2.source.sender_alt.is_some());
1371 assert_eq!(
1372 info2.source.sender_alt.as_ref().unwrap().user,
1373 "559984726662"
1374 );
1375
1376 println!("ā
sender_alt extraction working correctly for LID groups");
1377 }
1378
1379 #[test]
1384 fn test_lid_to_phone_mapping_for_device_queries() {
1385 use std::collections::HashMap;
1386 use wacore::client::context::GroupInfo;
1387 use wacore::types::message::AddressingMode;
1388 use wacore_binary::jid::Jid;
1389
1390 let mut lid_to_pn_map = HashMap::new();
1392 lid_to_pn_map.insert(
1393 "236395184570386.1".to_string(),
1394 "559984726662@s.whatsapp.net".parse().unwrap(),
1395 );
1396 lid_to_pn_map.insert(
1397 "987654321000000.2".to_string(),
1398 "551234567890@s.whatsapp.net".parse().unwrap(),
1399 );
1400
1401 let mut group_info = GroupInfo::new(
1402 vec![
1403 "236395184570386.1:75@lid".parse().unwrap(),
1404 "987654321000000.2:42@lid".parse().unwrap(),
1405 ],
1406 AddressingMode::Lid,
1407 );
1408 group_info.set_lid_to_pn_map(lid_to_pn_map.clone());
1409
1410 let jids_to_query: Vec<Jid> = group_info
1412 .participants
1413 .iter()
1414 .map(|jid| {
1415 let base_jid = jid.to_non_ad();
1416 if base_jid.server == "lid"
1417 && let Some(phone_jid) = group_info.phone_jid_for_lid_user(&base_jid.user)
1418 {
1419 return phone_jid.to_non_ad();
1420 }
1421 base_jid
1422 })
1423 .collect();
1424
1425 for jid in &jids_to_query {
1427 assert_eq!(
1428 jid.server, "s.whatsapp.net",
1429 "Device query should use phone number, got: {}",
1430 jid
1431 );
1432 }
1433
1434 assert_eq!(jids_to_query.len(), 2);
1435 assert!(jids_to_query.iter().any(|j| j.user == "559984726662"));
1436 assert!(jids_to_query.iter().any(|j| j.user == "551234567890"));
1437
1438 println!("ā
LID-to-phone mapping working correctly for device queries");
1439 }
1440
1441 #[test]
1446 fn test_mixed_lid_and_phone_participants() {
1447 use std::collections::HashMap;
1448 use wacore::client::context::GroupInfo;
1449 use wacore::types::message::AddressingMode;
1450 use wacore_binary::jid::Jid;
1451
1452 let mut lid_to_pn_map = HashMap::new();
1453 lid_to_pn_map.insert(
1454 "236395184570386.1".to_string(),
1455 "559984726662@s.whatsapp.net".parse().unwrap(),
1456 );
1457
1458 let mut group_info = GroupInfo::new(
1459 vec![
1460 "236395184570386.1:75@lid".parse().unwrap(), "551234567890:42@s.whatsapp.net".parse().unwrap(), ],
1463 AddressingMode::Lid,
1464 );
1465 group_info.set_lid_to_pn_map(lid_to_pn_map.clone());
1466
1467 let jids_to_query: Vec<Jid> = group_info
1468 .participants
1469 .iter()
1470 .map(|jid| {
1471 let base_jid = jid.to_non_ad();
1472 if base_jid.server == "lid"
1473 && let Some(phone_jid) = group_info.phone_jid_for_lid_user(&base_jid.user)
1474 {
1475 return phone_jid.to_non_ad();
1476 }
1477 base_jid
1478 })
1479 .collect();
1480
1481 assert_eq!(jids_to_query.len(), 2);
1483 for jid in &jids_to_query {
1484 assert_eq!(jid.server, "s.whatsapp.net");
1485 }
1486
1487 println!("ā
Mixed LID and phone number participants handled correctly");
1488 }
1489
1490 #[test]
1495 fn test_own_jid_check_in_lid_mode() {
1496 use std::collections::HashMap;
1497 use wacore_binary::jid::Jid;
1498
1499 let own_lid: Jid = "236395184570386.1@lid".parse().unwrap();
1500 let own_phone: Jid = "559984726662@s.whatsapp.net".parse().unwrap();
1501
1502 let mut lid_to_pn_map = HashMap::new();
1503 lid_to_pn_map.insert("236395184570386.1".to_string(), own_phone.clone());
1504
1505 let own_base_jid = own_lid.to_non_ad();
1507 let own_jid_to_check = if own_base_jid.server == "lid" {
1508 lid_to_pn_map
1509 .get(&own_base_jid.user)
1510 .map(|pn| pn.to_non_ad())
1511 .unwrap_or_else(|| own_base_jid.clone())
1512 } else {
1513 own_base_jid.clone()
1514 };
1515
1516 assert_eq!(own_jid_to_check.user, "559984726662");
1518 assert_eq!(own_jid_to_check.server, "s.whatsapp.net");
1519
1520 println!("ā
Own JID check correctly uses phone number in LID mode");
1521 }
1522
1523 #[tokio::test]
1526 async fn test_sender_key_always_uses_display_jid() {
1527 use crate::store::sqlite_store::SqliteStore;
1528 use std::sync::Arc;
1529 use wacore::libsignal::protocol::{SenderKeyStore, create_sender_key_distribution_message};
1530 use wacore::libsignal::store::sender_key_name::SenderKeyName;
1531
1532 let backend = Arc::new(
1533 SqliteStore::new("file:memdb_display_jid_test?mode=memory&cache=shared")
1534 .await
1535 .expect("Failed to create test backend"),
1536 );
1537 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1538 let (_client, _sync_rx) = Client::new(pm.clone()).await;
1539
1540 let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
1541 let display_jid: Jid = "236395184570386.1:75@lid".parse().unwrap();
1542 let encryption_jid: Jid = "559984726662:75@s.whatsapp.net".parse().unwrap();
1543
1544 let display_protocol_address = display_jid.to_protocol_address();
1546 let display_sender_key_name =
1547 SenderKeyName::new(group_jid.to_string(), display_protocol_address.to_string());
1548
1549 let device_arc = pm.get_device_arc().await;
1550 {
1551 let mut device_guard = device_arc.write().await;
1552 create_sender_key_distribution_message(
1553 &display_sender_key_name,
1554 &mut *device_guard,
1555 &mut rand::rngs::OsRng.unwrap_err(),
1556 )
1557 .await
1558 .expect("Failed to create SKDM");
1559 }
1560
1561 let lookup_with_display = {
1563 let mut device_guard = device_arc.write().await;
1564 device_guard.load_sender_key(&display_sender_key_name).await
1565 };
1566 assert!(
1567 lookup_with_display.unwrap().is_some(),
1568 "Sender key should be found with display JID (LID)"
1569 );
1570
1571 let encryption_protocol_address = encryption_jid.to_protocol_address();
1573 let encryption_sender_key_name = SenderKeyName::new(
1574 group_jid.to_string(),
1575 encryption_protocol_address.to_string(),
1576 );
1577
1578 let lookup_with_encryption = {
1579 let mut device_guard = device_arc.write().await;
1580 device_guard
1581 .load_sender_key(&encryption_sender_key_name)
1582 .await
1583 };
1584 assert!(
1585 lookup_with_encryption.unwrap().is_none(),
1586 "Sender key should NOT be found with encryption JID (phone number)"
1587 );
1588
1589 println!("ā
Sender key operations correctly use display JID, not encryption JID");
1590 }
1591
1592 #[tokio::test]
1601 async fn test_second_message_with_only_skmsg_decrypts() {
1602 use crate::store::sqlite_store::SqliteStore;
1603 use std::sync::Arc;
1604 use wacore::libsignal::protocol::{
1605 create_sender_key_distribution_message, process_sender_key_distribution_message,
1606 };
1607 use wacore::libsignal::store::sender_key_name::SenderKeyName;
1608 use wacore_binary::builder::NodeBuilder;
1609
1610 let backend = Arc::new(
1611 SqliteStore::new("file:memdb_second_msg_test?mode=memory&cache=shared")
1612 .await
1613 .expect("Failed to create test backend"),
1614 );
1615 let pm = Arc::new(PersistenceManager::new(backend).await.unwrap());
1616 let (client, _sync_rx) = Client::new(pm.clone()).await;
1617
1618 let sender_jid: Jid = "236395184570386.1:75@lid".parse().unwrap();
1619 let group_jid: Jid = "120363021033254949@g.us".parse().unwrap();
1620
1621 let sender_protocol_address = sender_jid.to_protocol_address();
1623 let sender_key_name =
1624 SenderKeyName::new(group_jid.to_string(), sender_protocol_address.to_string());
1625
1626 let device_arc = pm.get_device_arc().await;
1627 {
1628 let mut device_guard = device_arc.write().await;
1629 let skdm = create_sender_key_distribution_message(
1630 &sender_key_name,
1631 &mut *device_guard,
1632 &mut rand::rngs::OsRng.unwrap_err(),
1633 )
1634 .await
1635 .expect("Failed to create SKDM");
1636
1637 process_sender_key_distribution_message(&sender_key_name, &skdm, &mut *device_guard)
1638 .await
1639 .expect("Failed to process SKDM");
1640 }
1641
1642 println!("ā
Step 1: Sender key established for {}", sender_jid);
1643
1644 let skmsg_ciphertext = {
1647 let mut device_guard = device_arc.write().await;
1648 let sender_key_msg = wacore::libsignal::protocol::group_encrypt(
1649 &mut *device_guard,
1650 &sender_key_name,
1651 b"ping",
1652 &mut rand::rngs::OsRng.unwrap_err(),
1653 )
1654 .await
1655 .expect("Failed to encrypt with sender key");
1656 sender_key_msg.serialized().to_vec()
1657 };
1658
1659 let skmsg_node = NodeBuilder::new("enc")
1660 .attr("type", "skmsg")
1661 .attr("v", "2")
1662 .bytes(skmsg_ciphertext)
1663 .build();
1664
1665 let message_node = Arc::new(
1666 NodeBuilder::new("message")
1667 .attr("from", group_jid.to_string())
1668 .attr("participant", sender_jid.to_string())
1669 .attr("id", "SECOND_MSG_TEST")
1670 .attr("t", "1759306493")
1671 .attr("type", "text")
1672 .attr("addressing_mode", "lid")
1673 .children(vec![skmsg_node])
1674 .build(),
1675 );
1676
1677 client.handle_encrypted_message(message_node).await;
1684
1685 println!("ā
Step 2: Second message with only skmsg processed successfully");
1686
1687 }
1691}