Skip to main content

wacore_ng/store/
traits.rs

1//! Storage traits for the WhatsApp client.
2//!
3//! This module defines 4 domain-grouped traits that together form the `Backend` trait:
4//!
5//! - [`SignalStore`]: Signal protocol cryptographic operations (identity, sessions, keys)
6//! - [`AppSyncStore`]: WhatsApp app state synchronization
7//! - [`ProtocolStore`]: WhatsApp Web protocol alignment (SKDM, LID mapping, device registry)
8//! - [`DeviceStore`]: Device persistence operations
9
10use crate::appstate::hash::HashState;
11use crate::store::error::Result;
12use async_trait::async_trait;
13use serde::{Deserialize, Serialize};
14use wacore_appstate_ng::processor::AppStateMutationMAC;
15use wacore_binary_ng::jid::Jid;
16
17/// App state synchronization key for WhatsApp's app state protocol.
18#[derive(Debug, Clone, Default, Serialize, Deserialize)]
19pub struct AppStateSyncKey {
20    pub key_data: Vec<u8>,
21    pub fingerprint: Vec<u8>,
22    pub timestamp: i64,
23}
24
25/// Entry representing a LID to Phone Number mapping.
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct LidPnMappingEntry {
28    /// The LID user part (e.g., "100000012345678")
29    pub lid: String,
30    /// The phone number user part (e.g., "559980000001")
31    pub phone_number: String,
32    /// Unix timestamp when the mapping was first learned
33    pub created_at: i64,
34    /// Unix timestamp when the mapping was last updated
35    pub updated_at: i64,
36    /// The source from which this mapping was learned (e.g., "usync", "peer_pn_message")
37    pub learning_source: String,
38}
39
40/// Trusted contact privacy token entry.
41///
42/// Matches WhatsApp Web's Chat.tcToken / tcTokenTimestamp / tcTokenSenderTimestamp.
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct TcTokenEntry {
45    /// Raw token bytes received from the server.
46    pub token: Vec<u8>,
47    /// Unix timestamp (seconds) when the token was received.
48    pub token_timestamp: i64,
49    /// Unix timestamp (seconds) when we last issued our token to this contact.
50    pub sender_timestamp: Option<i64>,
51}
52
53/// Device information for registry tracking.
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct DeviceInfo {
56    /// The device ID (0 = primary device, 1+ = companion devices)
57    pub device_id: u32,
58    /// The key index, if known
59    pub key_index: Option<u32>,
60}
61
62/// Device list record matching WhatsApp Web's DeviceListRecord structure.
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct DeviceListRecord {
65    /// The user part of the JID (phone number or LID)
66    pub user: String,
67    /// List of known devices for this user
68    pub devices: Vec<DeviceInfo>,
69    /// Timestamp when this record was last updated
70    pub timestamp: i64,
71    /// Participant hash from usync, if available
72    pub phash: Option<String>,
73}
74
75/// Signal protocol cryptographic storage operations.
76///
77/// Handles identity keys, sessions, pre-keys, signed pre-keys, and sender keys
78/// for end-to-end encryption.
79#[async_trait]
80pub trait SignalStore: Send + Sync {
81    // --- Identity Operations ---
82
83    /// Store an identity key for a remote address.
84    async fn put_identity(&self, address: &str, key: [u8; 32]) -> Result<()>;
85
86    /// Load an identity key for a remote address.
87    async fn load_identity(&self, address: &str) -> Result<Option<Vec<u8>>>;
88
89    /// Delete an identity key.
90    async fn delete_identity(&self, address: &str) -> Result<()>;
91
92    // --- Session Operations ---
93
94    /// Get an encrypted session for an address.
95    async fn get_session(&self, address: &str) -> Result<Option<Vec<u8>>>;
96
97    /// Store an encrypted session.
98    async fn put_session(&self, address: &str, session: &[u8]) -> Result<()>;
99
100    /// Delete a session.
101    async fn delete_session(&self, address: &str) -> Result<()>;
102
103    /// Check if a session exists. Default implementation uses `get_session`.
104    async fn has_session(&self, address: &str) -> Result<bool> {
105        Ok(self.get_session(address).await?.is_some())
106    }
107
108    // --- PreKey Operations ---
109
110    /// Store a pre-key.
111    async fn store_prekey(&self, id: u32, record: &[u8], uploaded: bool) -> Result<()>;
112
113    /// Store multiple pre-keys in a single batch operation.
114    /// Default implementation falls back to individual `store_prekey` calls.
115    async fn store_prekeys_batch(&self, keys: &[(u32, Vec<u8>)], uploaded: bool) -> Result<()> {
116        for (id, record) in keys {
117            self.store_prekey(*id, record, uploaded).await?;
118        }
119        Ok(())
120    }
121
122    /// Load a pre-key by ID.
123    async fn load_prekey(&self, id: u32) -> Result<Option<Vec<u8>>>;
124
125    /// Remove a pre-key.
126    async fn remove_prekey(&self, id: u32) -> Result<()>;
127
128    /// Get the maximum pre-key ID currently stored, or 0 if none exist.
129    /// Used for migration when `next_pre_key_id` counter is not yet initialized.
130    async fn get_max_prekey_id(&self) -> Result<u32>;
131
132    // --- Signed PreKey Operations ---
133
134    /// Store a signed pre-key.
135    async fn store_signed_prekey(&self, id: u32, record: &[u8]) -> Result<()>;
136
137    /// Load a signed pre-key by ID.
138    async fn load_signed_prekey(&self, id: u32) -> Result<Option<Vec<u8>>>;
139
140    /// Load all signed pre-keys. Returns (id, record) pairs.
141    async fn load_all_signed_prekeys(&self) -> Result<Vec<(u32, Vec<u8>)>>;
142
143    /// Remove a signed pre-key.
144    async fn remove_signed_prekey(&self, id: u32) -> Result<()>;
145
146    // --- Sender Key Operations ---
147
148    /// Store a sender key for group messaging.
149    async fn put_sender_key(&self, address: &str, record: &[u8]) -> Result<()>;
150
151    /// Get a sender key.
152    async fn get_sender_key(&self, address: &str) -> Result<Option<Vec<u8>>>;
153
154    /// Delete a sender key.
155    async fn delete_sender_key(&self, address: &str) -> Result<()>;
156}
157
158/// WhatsApp app state synchronization storage.
159///
160/// Handles sync keys, version tracking, and mutation MACs for the app state protocol.
161#[async_trait]
162pub trait AppSyncStore: Send + Sync {
163    /// Get an app state sync key by ID.
164    async fn get_sync_key(&self, key_id: &[u8]) -> Result<Option<AppStateSyncKey>>;
165
166    /// Set an app state sync key.
167    async fn set_sync_key(&self, key_id: &[u8], key: AppStateSyncKey) -> Result<()>;
168
169    /// Get the app state version for a collection.
170    async fn get_version(&self, name: &str) -> Result<HashState>;
171
172    /// Set the app state version for a collection.
173    async fn set_version(&self, name: &str, state: HashState) -> Result<()>;
174
175    /// Store mutation MACs for a version.
176    async fn put_mutation_macs(
177        &self,
178        name: &str,
179        version: u64,
180        mutations: &[AppStateMutationMAC],
181    ) -> Result<()>;
182
183    /// Get a mutation MAC by index.
184    async fn get_mutation_mac(&self, name: &str, index_mac: &[u8]) -> Result<Option<Vec<u8>>>;
185
186    /// Delete mutation MACs by their index MACs.
187    async fn delete_mutation_macs(&self, name: &str, index_macs: &[Vec<u8>]) -> Result<()>;
188
189    /// Get the most recently stored app state sync key ID.
190    async fn get_latest_sync_key_id(&self) -> Result<Option<Vec<u8>>>;
191}
192
193/// WhatsApp Web protocol alignment storage.
194///
195/// Handles SKDM tracking, LID-PN mapping, base key collision detection,
196/// device registry, and sender key status.
197#[async_trait]
198pub trait ProtocolStore: Send + Sync {
199    // --- SKDM Tracking ---
200
201    /// Get device JIDs that have received SKDM for a group.
202    async fn get_skdm_recipients(&self, group_jid: &str) -> Result<Vec<Jid>>;
203
204    /// Record devices that have received SKDM for a group.
205    async fn add_skdm_recipients(&self, group_jid: &str, device_jids: &[Jid]) -> Result<()>;
206
207    /// Clear SKDM recipients for a group (call when sender key is rotated).
208    async fn clear_skdm_recipients(&self, group_jid: &str) -> Result<()>;
209
210    // --- LID-PN Mapping ---
211
212    /// Get a mapping by LID.
213    async fn get_lid_mapping(&self, lid: &str) -> Result<Option<LidPnMappingEntry>>;
214
215    /// Get a mapping by phone number (returns the most recent LID for that phone).
216    async fn get_pn_mapping(&self, phone: &str) -> Result<Option<LidPnMappingEntry>>;
217
218    /// Store or update a LID-PN mapping.
219    async fn put_lid_mapping(&self, entry: &LidPnMappingEntry) -> Result<()>;
220
221    /// Get all LID-PN mappings (for cache warm-up).
222    async fn get_all_lid_mappings(&self) -> Result<Vec<LidPnMappingEntry>>;
223
224    // --- Base Key Collision Detection ---
225
226    /// Save the base key for a session address during retry collision detection.
227    async fn save_base_key(&self, address: &str, message_id: &str, base_key: &[u8]) -> Result<()>;
228
229    /// Check if the current session has the same base key as the saved one.
230    async fn has_same_base_key(
231        &self,
232        address: &str,
233        message_id: &str,
234        current_base_key: &[u8],
235    ) -> Result<bool>;
236
237    /// Delete a base key entry.
238    async fn delete_base_key(&self, address: &str, message_id: &str) -> Result<()>;
239
240    // --- Device Registry ---
241
242    /// Update the device list for a user (called after usync responses).
243    async fn update_device_list(&self, record: DeviceListRecord) -> Result<()>;
244
245    /// Get all known devices for a user.
246    async fn get_devices(&self, user: &str) -> Result<Option<DeviceListRecord>>;
247
248    // --- Sender Key Status (Lazy Deletion) ---
249
250    /// Mark a participant's sender key as needing regeneration for a group.
251    async fn mark_forget_sender_key(&self, group_jid: &str, participant: &str) -> Result<()>;
252
253    /// Get participants that need fresh SKDM (marked for forget).
254    /// Consumes the marks (deletes them after reading).
255    async fn consume_forget_marks(&self, group_jid: &str) -> Result<Vec<String>>;
256
257    // --- TcToken Storage ---
258
259    /// Get a trusted contact token for a JID (stored under LID).
260    async fn get_tc_token(&self, jid: &str) -> Result<Option<TcTokenEntry>>;
261
262    /// Store or update a trusted contact token for a JID.
263    async fn put_tc_token(&self, jid: &str, entry: &TcTokenEntry) -> Result<()>;
264
265    /// Delete a trusted contact token for a JID.
266    async fn delete_tc_token(&self, jid: &str) -> Result<()>;
267
268    /// Get all JIDs that have stored tc tokens.
269    async fn get_all_tc_token_jids(&self) -> Result<Vec<String>>;
270
271    /// Delete tc tokens with token_timestamp older than cutoff. Returns count deleted.
272    async fn delete_expired_tc_tokens(&self, cutoff_timestamp: i64) -> Result<u32>;
273}
274
275/// Device data persistence operations.
276#[async_trait]
277pub trait DeviceStore: Send + Sync {
278    /// Save device data.
279    async fn save(&self, device: &crate::store::Device) -> Result<()>;
280
281    /// Load device data.
282    async fn load(&self) -> Result<Option<crate::store::Device>>;
283
284    /// Check if a device exists.
285    async fn exists(&self) -> Result<bool>;
286
287    /// Create a new device row and return its generated device_id.
288    async fn create(&self) -> Result<i32>;
289
290    /// Create a snapshot of the database state.
291    /// The argument `name` can be used to label the snapshot file.
292    /// `extra_content` can be used to save a related binary blob (e.g. the message that caused the failure).
293    async fn snapshot_db(&self, _name: &str, _extra_content: Option<&[u8]>) -> Result<()> {
294        Ok(())
295    }
296}
297
298/// Combined storage backend trait.
299///
300/// Any type implementing all four domain traits automatically implements `Backend`.
301pub trait Backend: SignalStore + AppSyncStore + ProtocolStore + DeviceStore + Send + Sync {}
302
303impl<T> Backend for T where T: SignalStore + AppSyncStore + ProtocolStore + DeviceStore + Send + Sync
304{}