use anyhow::{Result, anyhow};
use wacore::libsignal::protocol::{
CiphertextMessage, PreKeySignalMessage, SignalMessage, UsePQRatchet, message_decrypt,
message_encrypt,
};
use wacore::message_processing::EncType;
use wacore::messages::MessageUtils;
use wacore::types::jid::{JidExt, make_sender_key_name};
use wacore_binary::Jid;
use wacore_binary::Node;
use crate::client::Client;
pub struct Signal<'a> {
client: &'a Client,
}
impl<'a> Signal<'a> {
pub(crate) fn new(client: &'a Client) -> Self {
Self { client }
}
pub async fn encrypt_message(&self, jid: &Jid, plaintext: &[u8]) -> Result<(EncType, Vec<u8>)> {
let encryption_jid = self.client.resolve_encryption_jid(jid).await;
let signal_addr = encryption_jid.to_protocol_address();
let lock = self.client.session_lock_for(signal_addr.as_str()).await;
let _guard = lock.lock().await;
let mut adapter = self.client.signal_adapter().await;
let encrypted = message_encrypt(
plaintext,
&signal_addr,
&mut adapter.session_store,
&mut adapter.identity_store,
)
.await?;
drop(_guard);
self.client.flush_signal_cache().await?;
let (_, is_prekey, bytes) = wacore::send::extract_ciphertext(encrypted)
.ok_or_else(|| anyhow!("unexpected ciphertext variant"))?;
let enc_type = if is_prekey {
EncType::PreKeyMessage
} else {
EncType::Message
};
Ok((enc_type, bytes.into_vec()))
}
pub async fn decrypt_message(
&self,
jid: &Jid,
enc_type: EncType,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
let parsed = match enc_type {
EncType::PreKeyMessage => {
CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(ciphertext)?)
}
EncType::Message => {
CiphertextMessage::SignalMessage(SignalMessage::try_from(ciphertext)?)
}
EncType::SenderKey => {
return Err(anyhow!("use decrypt_group_message for sender-key messages"));
}
};
let encryption_jid = self.client.resolve_encryption_jid(jid).await;
let signal_addr = encryption_jid.to_protocol_address();
let lock = self.client.session_lock_for(signal_addr.as_str()).await;
let _guard = lock.lock().await;
let mut adapter = self.client.signal_adapter().await;
let mut rng = rand::make_rng::<rand::rngs::StdRng>();
let plaintext = message_decrypt(
&parsed,
&signal_addr,
&mut adapter.session_store,
&mut adapter.identity_store,
&mut adapter.pre_key_store,
&adapter.signed_pre_key_store,
&mut rng,
UsePQRatchet::No,
)
.await?;
drop(_guard);
self.client.flush_signal_cache().await?;
Ok(plaintext.to_vec())
}
pub async fn encrypt_group_message(
&self,
group_jid: &Jid,
plaintext: &[u8],
) -> Result<(Option<Vec<u8>>, Vec<u8>)> {
let own_jid = self.client.get_own_jid_for_group(group_jid).await?;
let sender_addr = own_jid.to_protocol_address();
let sender_key_name = make_sender_key_name(group_jid, &sender_addr);
let device_store = self.client.persistence_manager.get_device_arc().await;
let device_guard = device_store.read().await;
let key_exists = self
.client
.signal_cache
.get_sender_key(&sender_key_name, &*device_guard.backend)
.await?
.is_some();
drop(device_guard);
let mut adapter = self.client.signal_adapter().await;
let mut rng = rand::make_rng::<rand::rngs::StdRng>();
let skdm_bytes = if !key_exists {
Some(
wacore::send::create_sender_key_distribution_message_for_group(
&mut adapter.sender_key_store,
group_jid,
&sender_addr,
)
.await?,
)
} else {
None
};
let ciphertext = wacore::send::encrypt_group_message(
&mut adapter.sender_key_store,
group_jid,
&sender_addr,
plaintext,
&mut rng,
)
.await?;
self.client.flush_signal_cache().await?;
Ok((skdm_bytes, ciphertext.into_serialized().into_vec()))
}
pub async fn decrypt_group_message(
&self,
group_jid: &Jid,
sender_jid: &Jid,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
let sender_key_name =
make_sender_key_name(group_jid, &sender_jid.to_non_ad().to_protocol_address());
let mut adapter = self.client.signal_adapter().await;
let plaintext = wacore::libsignal::protocol::group_decrypt(
ciphertext,
&mut adapter.sender_key_store,
&sender_key_name,
)
.await?;
self.client.flush_signal_cache().await?;
Ok(plaintext.to_vec())
}
pub async fn validate_session(&self, jid: &Jid) -> Result<bool> {
let resolved = self.client.resolve_encryption_jid(jid).await;
let signal_addr = resolved.to_protocol_address();
let device_store = self.client.persistence_manager.get_device_arc().await;
let device_guard = device_store.read().await;
self.client
.signal_cache
.has_session(&signal_addr, &*device_guard.backend)
.await
.map_err(|e| anyhow!("session check failed: {e}"))
}
pub async fn delete_sessions(&self, jids: &[Jid]) -> Result<()> {
for jid in jids {
let resolved = self.client.resolve_encryption_jid(jid).await;
let addr = resolved.to_protocol_address();
let lock = self.client.session_lock_for(addr.as_str()).await;
let _guard = lock.lock().await;
self.client.signal_cache.delete_session(&addr).await;
self.client.signal_cache.delete_identity(&addr).await;
}
self.client.flush_signal_cache().await?;
Ok(())
}
pub async fn create_participant_nodes(
&self,
recipient_jids: &[Jid],
message: &waproto::whatsapp::Message,
) -> Result<(Vec<Node>, bool)> {
let device_jids = self.client.get_user_devices(recipient_jids).await?;
self.client.ensure_e2e_sessions(&device_jids).await?;
let lock_jids = self.client.build_session_lock_keys(&device_jids).await;
let session_mutexes = self.client.session_mutexes_for(&lock_jids).await;
let mut _session_guards = Vec::with_capacity(session_mutexes.len());
for mutex in &session_mutexes {
_session_guards.push(mutex.lock().await);
}
let plaintext = MessageUtils::encode_and_pad(message);
let mut adapter = self.client.signal_adapter().await;
let mediatype = wacore::send::media_type_from_message(message);
let hide_decrypt_fail = wacore::send::should_hide_decrypt_fail(message);
let mut stores = adapter.as_signal_stores();
let result = wacore::send::encrypt_for_devices(
&*self.client.runtime,
&mut stores,
self.client,
&device_jids,
&plaintext,
hide_decrypt_fail,
mediatype,
)
.await?;
drop(_session_guards);
self.client.flush_signal_cache().await?;
Ok((result.participant_nodes, result.includes_prekey_message))
}
pub async fn assert_sessions(&self, jids: &[Jid]) -> Result<()> {
self.client.ensure_e2e_sessions(jids).await
}
pub async fn get_user_devices(&self, jids: &[Jid]) -> Result<Vec<Jid>> {
self.client.get_user_devices(jids).await
}
}
impl Client {
pub fn signal(&self) -> Signal<'_> {
Signal::new(self)
}
}