libsignal_service/groups_v2/
operations.rs

1use std::convert::TryInto;
2
3use base64::prelude::*;
4use bytes::Bytes;
5use libsignal_protocol::{Aci, ServiceId};
6use prost::Message;
7use zkgroup::{
8    groups::GroupSecretParams,
9    profiles::{AnyProfileKeyCredentialPresentation, ProfileKey},
10};
11
12use crate::{
13    groups_v2::model::Timer,
14    proto::{
15        self, group_attribute_blob, GroupAttributeBlob,
16        Member as EncryptedMember,
17    },
18    utils::BASE64_RELAXED,
19};
20
21use super::{
22    model::{Member, PendingMember, RequestingMember},
23    Group, GroupChange, GroupChanges,
24};
25
26pub(crate) struct GroupOperations {
27    pub group_secret_params: GroupSecretParams,
28}
29
30#[derive(Debug, thiserror::Error)]
31pub enum GroupDecodingError {
32    #[error("zero-knowledge group deserialization failure")]
33    ZkGroupDeserializationFailure,
34    #[error("zero-knowledge group verification failure")]
35    ZkGroupVerificationFailure,
36    #[error(transparent)]
37    BincodeError(#[from] bincode::Error),
38    #[error("protobuf message decoding error: {0}")]
39    ProtobufDecodeError(#[from] prost::DecodeError),
40    #[error("wrong group attribute blob")]
41    WrongBlob,
42    #[error("wrong enum value")]
43    WrongEnumValue,
44    #[error("wrong service ID type: should be ACI")]
45    NotAci,
46}
47
48impl From<zkgroup::ZkGroupDeserializationFailure> for GroupDecodingError {
49    fn from(_: zkgroup::ZkGroupDeserializationFailure) -> Self {
50        GroupDecodingError::ZkGroupDeserializationFailure
51    }
52}
53
54impl From<zkgroup::ZkGroupVerificationFailure> for GroupDecodingError {
55    fn from(_: zkgroup::ZkGroupVerificationFailure) -> Self {
56        GroupDecodingError::ZkGroupVerificationFailure
57    }
58}
59
60impl GroupOperations {
61    fn decrypt_service_id(
62        &self,
63        ciphertext: &[u8],
64    ) -> Result<ServiceId, GroupDecodingError> {
65        match self
66            .group_secret_params
67            .decrypt_service_id(bincode::deserialize(ciphertext)?)?
68        {
69            ServiceId::Aci(aci) => Ok(ServiceId::from(aci)),
70            ServiceId::Pni(pni) => Ok(ServiceId::from(pni)),
71        }
72    }
73
74    fn decrypt_aci(
75        &self,
76        ciphertext: &[u8],
77    ) -> Result<Aci, GroupDecodingError> {
78        match self
79            .group_secret_params
80            .decrypt_service_id(bincode::deserialize(ciphertext)?)?
81        {
82            ServiceId::Aci(aci) => Ok(aci),
83            ServiceId::Pni(_pni) => Err(GroupDecodingError::NotAci),
84        }
85    }
86
87    fn decrypt_profile_key(
88        &self,
89        encrypted_profile_key: &[u8],
90        decrypted_aci: libsignal_protocol::Aci,
91    ) -> Result<ProfileKey, GroupDecodingError> {
92        Ok(self.group_secret_params.decrypt_profile_key(
93            bincode::deserialize(encrypted_profile_key)?,
94            decrypted_aci,
95        )?)
96    }
97
98    fn decrypt_profile_key_presentation(
99        &self,
100        presentation: &[u8],
101    ) -> Result<(Aci, ProfileKey), GroupDecodingError> {
102        let profile_key_credential_presentation =
103            AnyProfileKeyCredentialPresentation::new(presentation)?;
104
105        match self.group_secret_params.decrypt_service_id(
106            profile_key_credential_presentation.get_uuid_ciphertext(),
107        )? {
108            ServiceId::Aci(aci) => {
109                let profile_key =
110                    self.group_secret_params.decrypt_profile_key(
111                        profile_key_credential_presentation
112                            .get_profile_key_ciphertext(),
113                        aci,
114                    )?;
115                Ok((aci, profile_key))
116            },
117            _ => Err(GroupDecodingError::NotAci),
118        }
119    }
120
121    fn decrypt_member(
122        &self,
123        member: EncryptedMember,
124    ) -> Result<Member, GroupDecodingError> {
125        let (aci, profile_key) = if member.presentation.is_empty() {
126            let aci = self.decrypt_aci(&member.user_id)?;
127            let profile_key =
128                self.decrypt_profile_key(&member.profile_key, aci)?;
129            (aci, profile_key)
130        } else {
131            self.decrypt_profile_key_presentation(&member.presentation)?
132        };
133        Ok(Member {
134            uuid: aci.into(),
135            profile_key,
136            role: member.role.try_into()?,
137            joined_at_revision: member.joined_at_revision,
138        })
139    }
140
141    fn decrypt_pending_member(
142        &self,
143        member: proto::PendingMember,
144    ) -> Result<PendingMember, GroupDecodingError> {
145        let inner_member =
146            member.member.ok_or(GroupDecodingError::WrongBlob)?;
147        let service_id = self.decrypt_service_id(&inner_member.user_id)?;
148        let added_by_uuid = self.decrypt_aci(&member.added_by_user_id)?;
149
150        Ok(PendingMember {
151            address: service_id,
152            role: inner_member.role.try_into()?,
153            added_by_uuid: added_by_uuid.into(),
154            timestamp: member.timestamp,
155        })
156    }
157
158    fn decrypt_requesting_member(
159        &self,
160        member: proto::RequestingMember,
161    ) -> Result<RequestingMember, GroupDecodingError> {
162        let (aci, profile_key) = if member.presentation.is_empty() {
163            let aci = self.decrypt_aci(&member.user_id)?;
164            let profile_key =
165                self.decrypt_profile_key(&member.profile_key, aci)?;
166            (aci, profile_key)
167        } else {
168            self.decrypt_profile_key_presentation(&member.presentation)?
169        };
170        Ok(RequestingMember {
171            profile_key,
172            uuid: aci.into(),
173            timestamp: member.timestamp,
174        })
175    }
176
177    fn decrypt_blob(&self, bytes: &[u8]) -> GroupAttributeBlob {
178        if bytes.is_empty() {
179            GroupAttributeBlob::default()
180        } else if bytes.len() < 29 {
181            tracing::warn!("bad encrypted blob length");
182            GroupAttributeBlob::default()
183        } else {
184            self.group_secret_params
185                .decrypt_blob(bytes)
186                .map_err(GroupDecodingError::from)
187                .and_then(|b| {
188                    GroupAttributeBlob::decode(Bytes::copy_from_slice(&b[4..]))
189                        .map_err(GroupDecodingError::ProtobufDecodeError)
190                })
191                .unwrap_or_else(|e| {
192                    tracing::warn!("bad encrypted blob: {}", e);
193                    GroupAttributeBlob::default()
194                })
195        }
196    }
197
198    fn decrypt_title(&self, ciphertext: &[u8]) -> String {
199        use group_attribute_blob::Content;
200        match self.decrypt_blob(ciphertext).content {
201            Some(Content::Title(title)) => title,
202            _ => "".into(),
203        }
204    }
205
206    fn decrypt_description(&self, ciphertext: &[u8]) -> Option<String> {
207        use group_attribute_blob::Content;
208        match self.decrypt_blob(ciphertext).content {
209            Some(Content::Description(d)) => Some(d).filter(|d| !d.is_empty()),
210            _ => None,
211        }
212    }
213
214    fn decrypt_disappearing_message_timer(
215        &self,
216        ciphertext: &[u8],
217    ) -> Option<Timer> {
218        use group_attribute_blob::Content;
219        match self.decrypt_blob(ciphertext).content {
220            Some(Content::DisappearingMessagesDuration(duration)) => {
221                Some(Timer { duration })
222            },
223            _ => None,
224        }
225    }
226
227    pub fn new(group_secret_params: GroupSecretParams) -> Self {
228        Self {
229            group_secret_params,
230        }
231    }
232
233    pub fn decrypt_group(
234        &self,
235        group: proto::Group,
236    ) -> Result<Group, GroupDecodingError> {
237        let title = self.decrypt_title(&group.title);
238
239        let description = self.decrypt_description(&group.description);
240
241        let disappearing_messages_timer = self
242            .decrypt_disappearing_message_timer(
243                &group.disappearing_messages_timer,
244            );
245
246        let members = group
247            .members
248            .into_iter()
249            .map(|m| self.decrypt_member(m))
250            .collect::<Result<_, _>>()?;
251
252        let pending_members = group
253            .pending_members
254            .into_iter()
255            .map(|m| self.decrypt_pending_member(m))
256            .collect::<Result<_, _>>()?;
257
258        let requesting_members = group
259            .requesting_members
260            .into_iter()
261            .map(|m| self.decrypt_requesting_member(m))
262            .collect::<Result<_, _>>()?;
263
264        Ok(Group {
265            title,
266            avatar: group.avatar,
267            disappearing_messages_timer,
268            access_control: group
269                .access_control
270                .map(TryInto::try_into)
271                .transpose()?,
272            revision: group.revision,
273            members,
274            pending_members,
275            requesting_members,
276            invite_link_password: group.invite_link_password,
277            description,
278        })
279    }
280
281    pub fn decrypt_group_change(
282        &self,
283        group_change: proto::GroupChange,
284    ) -> Result<GroupChanges, GroupDecodingError> {
285        let actions: proto::group_change::Actions =
286            Message::decode(Bytes::from(group_change.actions))?;
287
288        let aci = self.decrypt_aci(&actions.source_service_id)?;
289
290        let new_members =
291            actions.add_members.into_iter().filter_map(|m| m.added).map(
292                |added| Ok(GroupChange::NewMember(self.decrypt_member(added)?)),
293            );
294
295        let delete_members = actions.delete_members.into_iter().map(|c| {
296            Ok(GroupChange::DeleteMember(
297                self.decrypt_aci(&c.deleted_user_id)?.into(),
298            ))
299        });
300
301        let modify_member_roles =
302            actions.modify_member_roles.into_iter().map(|m| {
303                Ok(GroupChange::ModifyMemberRole {
304                    uuid: self.decrypt_aci(&m.user_id)?.into(),
305                    role: m.role.try_into()?,
306                })
307            });
308
309        let modify_member_profile_keys =
310            actions.modify_member_profile_keys.into_iter().map(|m| {
311                let (aci, profile_key) =
312                    self.decrypt_profile_key_presentation(&m.presentation)?;
313                Ok(GroupChange::ModifyMemberProfileKey {
314                    uuid: aci.into(),
315                    profile_key,
316                })
317            });
318
319        let add_pending_members = actions
320            .add_pending_members
321            .into_iter()
322            .filter_map(|m| m.added)
323            .map(|added| {
324                Ok(GroupChange::NewPendingMember(
325                    self.decrypt_pending_member(added)?,
326                ))
327            });
328
329        let delete_pending_members =
330            actions.delete_pending_members.into_iter().map(|m| {
331                Ok(GroupChange::DeletePendingMember(
332                    self.decrypt_aci(&m.deleted_user_id)?.into(),
333                ))
334            });
335
336        let promote_pending_members =
337            actions.promote_pending_members.into_iter().map(|m| {
338                let (aci, profile_key) =
339                    self.decrypt_profile_key_presentation(&m.presentation)?;
340                Ok(GroupChange::PromotePendingMember {
341                    uuid: aci.into(),
342                    profile_key,
343                })
344            });
345
346        let modify_title = actions
347            .modify_title
348            .into_iter()
349            .map(|m| Ok(GroupChange::Title(self.decrypt_title(&m.title))));
350
351        let modify_avatar = actions
352            .modify_avatar
353            .into_iter()
354            .map(|m| Ok(GroupChange::Avatar(m.avatar)));
355
356        let modify_description =
357            actions.modify_description.into_iter().map(|m| {
358                Ok(GroupChange::Description(
359                    self.decrypt_description(&m.description),
360                ))
361            });
362
363        let modify_disappearing_messages_timer = actions
364            .modify_disappearing_messages_timer
365            .into_iter()
366            .map(|m| {
367                Ok(GroupChange::Timer(
368                    self.decrypt_disappearing_message_timer(&m.timer),
369                ))
370            });
371
372        let modify_attributes_access =
373            actions.modify_attributes_access.into_iter().map(|m| {
374                Ok(GroupChange::AttributeAccess(
375                    m.attributes_access.try_into()?,
376                ))
377            });
378
379        let modify_member_access =
380            actions.modify_member_access.into_iter().map(|m| {
381                Ok(GroupChange::MemberAccess(m.members_access.try_into()?))
382            });
383
384        let modify_add_from_invite_link_access = actions
385            .modify_add_from_invite_link_access
386            .into_iter()
387            .map(|m| {
388                Ok(GroupChange::InviteLinkAccess(
389                    m.add_from_invite_link_access.try_into()?,
390                ))
391            });
392
393        let add_requesting_members = actions
394            .add_requesting_members
395            .into_iter()
396            .filter_map(|m| m.added)
397            .map(|added| {
398                Ok(GroupChange::NewRequestingMember(
399                    self.decrypt_requesting_member(added)?,
400                ))
401            });
402
403        let delete_requesting_members =
404            actions.delete_requesting_members.into_iter().map(|m| {
405                Ok(GroupChange::DeleteRequestingMember(
406                    self.decrypt_aci(&m.deleted_user_id)?.into(),
407                ))
408            });
409
410        let promote_requesting_members =
411            actions.promote_requesting_members.into_iter().map(|m| {
412                Ok(GroupChange::PromoteRequestingMember {
413                    uuid: self.decrypt_aci(&m.user_id)?.into(),
414                    role: m.role.try_into()?,
415                })
416            });
417
418        let modify_invite_link_password =
419            actions.modify_invite_link_password.into_iter().map(|m| {
420                Ok(GroupChange::InviteLinkPassword(
421                    BASE64_RELAXED.encode(m.invite_link_password),
422                ))
423            });
424
425        let modify_announcements_only = actions
426            .modify_announcements_only
427            .into_iter()
428            .map(|m| Ok(GroupChange::AnnouncementOnly(m.announcements_only)));
429
430        let changes: Result<Vec<GroupChange>, GroupDecodingError> = new_members
431            .chain(delete_members)
432            .chain(modify_member_roles)
433            .chain(modify_member_profile_keys)
434            .chain(add_pending_members)
435            .chain(delete_pending_members)
436            .chain(promote_pending_members)
437            .chain(modify_title)
438            .chain(modify_avatar)
439            .chain(modify_disappearing_messages_timer)
440            .chain(modify_attributes_access)
441            .chain(modify_description)
442            .chain(modify_member_access)
443            .chain(modify_add_from_invite_link_access)
444            .chain(add_requesting_members)
445            .chain(delete_requesting_members)
446            .chain(promote_requesting_members)
447            .chain(modify_invite_link_password)
448            .chain(modify_announcements_only)
449            .collect();
450
451        Ok(GroupChanges {
452            editor: aci.into(),
453            revision: actions.revision,
454            changes: changes?,
455        })
456    }
457
458    pub fn decrypt_avatar(&self, ciphertext: &[u8]) -> Option<Vec<u8>> {
459        use group_attribute_blob::Content;
460        match self.decrypt_blob(ciphertext).content {
461            Some(Content::Avatar(d)) => Some(d).filter(|d| !d.is_empty()),
462            _ => None,
463        }
464    }
465}