libsignal_service/groups_v2/
operations.rs

1use std::convert::TryInto;
2
3use base64::prelude::*;
4use bytes::Bytes;
5use libsignal_protocol::{Aci, Pni, 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::{
23        BannedMember, Member, PendingMember, PromotedMember, RequestingMember,
24    },
25    Group, GroupChange, GroupChanges,
26};
27
28pub(crate) struct GroupOperations {
29    pub group_secret_params: GroupSecretParams,
30}
31
32#[derive(Debug, thiserror::Error)]
33pub enum GroupDecodingError {
34    #[error("zero-knowledge group deserialization failure")]
35    ZkGroupDeserializationFailure,
36    #[error("zero-knowledge group verification failure")]
37    ZkGroupVerificationFailure,
38    #[error(transparent)]
39    BincodeError(#[from] bincode::Error),
40    #[error("protobuf message decoding error: {0}")]
41    ProtobufDecodeError(#[from] prost::DecodeError),
42    #[error("wrong group attribute blob")]
43    WrongBlob,
44    #[error("wrong enum value")]
45    WrongEnumValue,
46    #[error("wrong service ID type: should be ACI")]
47    NotAci,
48    #[error("wrong service ID type: should be PNI")]
49    NotPni,
50}
51
52impl From<zkgroup::ZkGroupDeserializationFailure> for GroupDecodingError {
53    fn from(_: zkgroup::ZkGroupDeserializationFailure) -> Self {
54        GroupDecodingError::ZkGroupDeserializationFailure
55    }
56}
57
58impl From<zkgroup::ZkGroupVerificationFailure> for GroupDecodingError {
59    fn from(_: zkgroup::ZkGroupVerificationFailure) -> Self {
60        GroupDecodingError::ZkGroupVerificationFailure
61    }
62}
63
64impl GroupOperations {
65    fn decrypt_service_id(
66        &self,
67        ciphertext: &[u8],
68    ) -> Result<ServiceId, GroupDecodingError> {
69        match self
70            .group_secret_params
71            .decrypt_service_id(bincode::deserialize(ciphertext)?)?
72        {
73            ServiceId::Aci(aci) => Ok(ServiceId::from(aci)),
74            ServiceId::Pni(pni) => Ok(ServiceId::from(pni)),
75        }
76    }
77
78    fn decrypt_aci(
79        &self,
80        ciphertext: &[u8],
81    ) -> Result<Aci, GroupDecodingError> {
82        match self
83            .group_secret_params
84            .decrypt_service_id(bincode::deserialize(ciphertext)?)?
85        {
86            ServiceId::Aci(aci) => Ok(aci),
87            ServiceId::Pni(pni) => {
88                tracing::error!(
89                    "Expected Aci, got Pni: {}",
90                    pni.service_id_string()
91                );
92                Err(GroupDecodingError::NotAci)
93            },
94        }
95    }
96
97    fn decrypt_pni(
98        &self,
99        ciphertext: &[u8],
100    ) -> Result<Pni, GroupDecodingError> {
101        match self
102            .group_secret_params
103            .decrypt_service_id(bincode::deserialize(ciphertext)?)?
104        {
105            ServiceId::Pni(pni) => Ok(pni),
106            ServiceId::Aci(aci) => {
107                tracing::error!(
108                    "Expected Pni, got Aci: {}",
109                    aci.service_id_string()
110                );
111                Err(GroupDecodingError::NotPni)
112            },
113        }
114    }
115
116    fn decrypt_profile_key(
117        &self,
118        encrypted_profile_key: &[u8],
119        decrypted_aci: libsignal_protocol::Aci,
120    ) -> Result<ProfileKey, GroupDecodingError> {
121        Ok(self.group_secret_params.decrypt_profile_key(
122            bincode::deserialize(encrypted_profile_key)?,
123            decrypted_aci,
124        )?)
125    }
126
127    fn decrypt_profile_key_presentation(
128        &self,
129        presentation: &[u8],
130    ) -> Result<(Aci, ProfileKey), GroupDecodingError> {
131        let profile_key_credential_presentation =
132            AnyProfileKeyCredentialPresentation::new(presentation)?;
133
134        match self.group_secret_params.decrypt_service_id(
135            profile_key_credential_presentation.get_uuid_ciphertext(),
136        )? {
137            ServiceId::Aci(aci) => {
138                let profile_key =
139                    self.group_secret_params.decrypt_profile_key(
140                        profile_key_credential_presentation
141                            .get_profile_key_ciphertext(),
142                        aci,
143                    )?;
144                Ok((aci, profile_key))
145            },
146            _ => Err(GroupDecodingError::NotAci),
147        }
148    }
149
150    fn decrypt_pni_aci_promotion_presentation(
151        &self,
152        member: &proto::group_change::actions::PromotePendingPniAciMemberProfileKeyAction,
153    ) -> Result<PromotedMember, GroupDecodingError> {
154        let aci = self.decrypt_aci(&member.user_id)?;
155        let pni = self.decrypt_pni(&member.pni)?;
156        let profile_key = self.decrypt_profile_key(&member.profile_key, aci)?;
157        Ok(PromotedMember {
158            aci,
159            pni,
160            profile_key,
161        })
162    }
163
164    fn decrypt_member(
165        &self,
166        member: EncryptedMember,
167    ) -> Result<Member, GroupDecodingError> {
168        let (aci, profile_key) = if member.presentation.is_empty() {
169            let aci = self.decrypt_aci(&member.user_id)?;
170            let profile_key =
171                self.decrypt_profile_key(&member.profile_key, aci)?;
172            (aci, profile_key)
173        } else {
174            self.decrypt_profile_key_presentation(&member.presentation)?
175        };
176        Ok(Member {
177            aci,
178            profile_key,
179            role: member.role.try_into()?,
180            joined_at_revision: member.joined_at_revision,
181        })
182    }
183
184    fn decrypt_pending_member(
185        &self,
186        member: proto::PendingMember,
187    ) -> Result<PendingMember, GroupDecodingError> {
188        let inner_member =
189            member.member.ok_or(GroupDecodingError::WrongBlob)?;
190        let service_id = self.decrypt_service_id(&inner_member.user_id)?;
191        let added_by_aci = self.decrypt_aci(&member.added_by_user_id)?;
192
193        Ok(PendingMember {
194            address: service_id,
195            role: inner_member.role.try_into()?,
196            added_by_aci,
197            timestamp: member.timestamp,
198        })
199    }
200
201    fn decrypt_requesting_member(
202        &self,
203        member: proto::RequestingMember,
204    ) -> Result<RequestingMember, GroupDecodingError> {
205        let (aci, profile_key) = if member.presentation.is_empty() {
206            let aci = self.decrypt_aci(&member.user_id)?;
207            let profile_key =
208                self.decrypt_profile_key(&member.profile_key, aci)?;
209            (aci, profile_key)
210        } else {
211            self.decrypt_profile_key_presentation(&member.presentation)?
212        };
213        Ok(RequestingMember {
214            profile_key,
215            aci,
216            timestamp: member.timestamp,
217        })
218    }
219
220    fn decrypt_banned_member(
221        &self,
222        member: proto::BannedMember,
223    ) -> Result<BannedMember, GroupDecodingError> {
224        Ok(BannedMember {
225            service_id: self.decrypt_service_id(&member.user_id)?,
226            timestamp: member.timestamp,
227        })
228    }
229
230    fn decrypt_blob(&self, bytes: &[u8]) -> GroupAttributeBlob {
231        if bytes.is_empty() {
232            GroupAttributeBlob::default()
233        } else if bytes.len() < 29 {
234            tracing::warn!("bad encrypted blob length");
235            GroupAttributeBlob::default()
236        } else {
237            self.group_secret_params
238                .decrypt_blob(bytes)
239                .map_err(GroupDecodingError::from)
240                .and_then(|b| {
241                    GroupAttributeBlob::decode(Bytes::copy_from_slice(&b[4..]))
242                        .map_err(GroupDecodingError::ProtobufDecodeError)
243                })
244                .unwrap_or_else(|e| {
245                    tracing::warn!("bad encrypted blob: {}", e);
246                    GroupAttributeBlob::default()
247                })
248        }
249    }
250
251    fn decrypt_title(&self, ciphertext: &[u8]) -> String {
252        use group_attribute_blob::Content;
253        match self.decrypt_blob(ciphertext).content {
254            Some(Content::Title(title)) => title,
255            _ => "".into(),
256        }
257    }
258
259    fn decrypt_description(&self, ciphertext: &[u8]) -> Option<String> {
260        use group_attribute_blob::Content;
261        match self.decrypt_blob(ciphertext).content {
262            Some(Content::Description(d)) => Some(d).filter(|d| !d.is_empty()),
263            _ => None,
264        }
265    }
266
267    fn decrypt_disappearing_message_timer(
268        &self,
269        ciphertext: &[u8],
270    ) -> Option<Timer> {
271        use group_attribute_blob::Content;
272        match self.decrypt_blob(ciphertext).content {
273            Some(Content::DisappearingMessagesDuration(duration)) => {
274                Some(Timer { duration })
275            },
276            _ => None,
277        }
278    }
279
280    pub fn new(group_secret_params: GroupSecretParams) -> Self {
281        Self {
282            group_secret_params,
283        }
284    }
285
286    pub fn decrypt_group(
287        &self,
288        group: proto::Group,
289    ) -> Result<Group, GroupDecodingError> {
290        // Destructuring to catch any future changes
291        let proto::Group {
292            public_key: _,
293            title,
294            avatar,
295            disappearing_messages_timer,
296            access_control,
297            revision,
298            members,
299            pending_members,
300            requesting_members,
301            invite_link_password,
302            description,
303            announcements_only,
304            banned_members,
305        } = group;
306
307        let title = self.decrypt_title(&title);
308
309        let description = self.decrypt_description(&description);
310
311        let disappearing_messages_timer = self
312            .decrypt_disappearing_message_timer(&disappearing_messages_timer);
313
314        let members = members
315            .into_iter()
316            .map(|m| self.decrypt_member(m))
317            .collect::<Result<_, _>>()?;
318
319        let pending_members = pending_members
320            .into_iter()
321            .map(|m| self.decrypt_pending_member(m))
322            .collect::<Result<_, _>>()?;
323
324        let requesting_members = requesting_members
325            .into_iter()
326            .map(|m| self.decrypt_requesting_member(m))
327            .collect::<Result<_, _>>()?;
328
329        let banned_members = banned_members
330            .into_iter()
331            .map(|m| self.decrypt_banned_member(m))
332            .collect::<Result<_, _>>()?;
333
334        let access_control =
335            access_control.map(TryInto::try_into).transpose()?;
336
337        Ok(Group {
338            title,
339            avatar,
340            disappearing_messages_timer,
341            access_control,
342            revision,
343            members,
344            pending_members,
345            requesting_members,
346            invite_link_password,
347            description,
348            announcements_only,
349            banned_members,
350        })
351    }
352
353    pub fn decrypt_group_change(
354        &self,
355        group_change: proto::GroupChange,
356    ) -> Result<GroupChanges, GroupDecodingError> {
357        // Destructuring to catch any future changes
358        let proto::GroupChange {
359            actions,
360            server_signature: _,
361            change_epoch,
362        } = group_change;
363
364        let proto::group_change::Actions {
365            group_id,
366            source_service_id,
367            revision,
368            add_members,
369            delete_members,
370            modify_member_roles,
371            modify_member_profile_keys,
372            add_pending_members,
373            delete_pending_members,
374            promote_pending_members,
375            modify_title,
376            modify_avatar,
377            modify_disappearing_messages_timer,
378            modify_attributes_access,
379            modify_member_access,
380            modify_add_from_invite_link_access,
381            add_requesting_members,
382            delete_requesting_members,
383            promote_requesting_members,
384            modify_invite_link_password,
385            modify_description,
386            modify_announcements_only,
387            add_banned_members,
388            delete_banned_members,
389            promote_pending_pni_aci_members,
390        } = Message::decode(Bytes::from(actions))?;
391
392        let editor = self.decrypt_aci(&source_service_id)?;
393
394        let new_members =
395            add_members
396                .into_iter()
397                .filter_map(|m| m.added)
398                .map(|added| {
399                    Ok(GroupChange::NewMember(self.decrypt_member(added)?))
400                });
401
402        let delete_members = delete_members.into_iter().map(|c| {
403            Ok(GroupChange::DeleteMember(
404                self.decrypt_aci(&c.deleted_user_id)?,
405            ))
406        });
407
408        let modify_member_roles = modify_member_roles.into_iter().map(|m| {
409            Ok(GroupChange::ModifyMemberRole {
410                aci: self.decrypt_aci(&m.user_id)?,
411                role: m.role.try_into()?,
412            })
413        });
414
415        let modify_member_profile_keys =
416            modify_member_profile_keys.into_iter().map(|m| {
417                let (aci, profile_key) =
418                    self.decrypt_profile_key_presentation(&m.presentation)?;
419                Ok(GroupChange::ModifyMemberProfileKey { aci, profile_key })
420            });
421
422        let add_pending_members = add_pending_members
423            .into_iter()
424            .filter_map(|m| m.added)
425            .map(|added| {
426                Ok(GroupChange::NewPendingMember(
427                    self.decrypt_pending_member(added)?,
428                ))
429            });
430
431        let delete_pending_members =
432            delete_pending_members.into_iter().map(|m| {
433                Ok(GroupChange::DeletePendingMember(
434                    self.decrypt_service_id(&m.deleted_user_id)?,
435                ))
436            });
437
438        let promote_pending_members =
439            promote_pending_members.into_iter().map(|m| {
440                let (aci, profile_key) =
441                    self.decrypt_profile_key_presentation(&m.presentation)?;
442                Ok(GroupChange::PromotePendingMember {
443                    address: aci.into(),
444                    profile_key,
445                })
446            });
447
448        let modify_title = modify_title
449            .into_iter()
450            .map(|m| Ok(GroupChange::Title(self.decrypt_title(&m.title))));
451
452        let modify_avatar = modify_avatar
453            .into_iter()
454            .map(|m| Ok(GroupChange::Avatar(m.avatar)));
455
456        let modify_description = modify_description.into_iter().map(|m| {
457            Ok(GroupChange::Description(
458                self.decrypt_description(&m.description),
459            ))
460        });
461
462        let modify_disappearing_messages_timer =
463            modify_disappearing_messages_timer.into_iter().map(|m| {
464                Ok(GroupChange::Timer(
465                    self.decrypt_disappearing_message_timer(&m.timer),
466                ))
467            });
468
469        let modify_attributes_access =
470            modify_attributes_access.into_iter().map(|m| {
471                Ok(GroupChange::AttributeAccess(
472                    m.attributes_access.try_into()?,
473                ))
474            });
475
476        let modify_member_access = modify_member_access.into_iter().map(|m| {
477            Ok(GroupChange::MemberAccess(m.members_access.try_into()?))
478        });
479
480        let add_banned_members = add_banned_members
481            .into_iter()
482            .filter_map(|m| m.added)
483            .map(|m| {
484                Ok(GroupChange::AddBannedMember(self.decrypt_banned_member(m)?))
485            });
486
487        let delete_banned_members =
488            delete_banned_members.into_iter().map(|m| {
489                Ok(GroupChange::DeleteBannedMember(
490                    self.decrypt_service_id(&m.deleted_user_id)?,
491                ))
492            });
493
494        let promote_pending_member =
495            promote_pending_pni_aci_members.into_iter().map(|m| {
496                let promoted =
497                    self.decrypt_pni_aci_promotion_presentation(&m)?;
498                Ok(GroupChange::PromotePendingPniAciMemberProfileKey(promoted))
499            });
500
501        let modify_add_from_invite_link_access =
502            modify_add_from_invite_link_access.into_iter().map(|m| {
503                Ok(GroupChange::InviteLinkAccess(
504                    m.add_from_invite_link_access.try_into()?,
505                ))
506            });
507
508        let add_requesting_members = add_requesting_members
509            .into_iter()
510            .filter_map(|m| m.added)
511            .map(|added| {
512                Ok(GroupChange::NewRequestingMember(
513                    self.decrypt_requesting_member(added)?,
514                ))
515            });
516
517        let delete_requesting_members =
518            delete_requesting_members.into_iter().map(|m| {
519                Ok(GroupChange::DeleteRequestingMember(
520                    self.decrypt_aci(&m.deleted_user_id)?,
521                ))
522            });
523
524        let promote_requesting_members =
525            promote_requesting_members.into_iter().map(|m| {
526                Ok(GroupChange::PromoteRequestingMember {
527                    aci: self.decrypt_aci(&m.user_id)?,
528                    role: m.role.try_into()?,
529                })
530            });
531
532        let modify_invite_link_password =
533            modify_invite_link_password.into_iter().map(|m| {
534                Ok(GroupChange::InviteLinkPassword(
535                    BASE64_RELAXED.encode(m.invite_link_password),
536                ))
537            });
538
539        let modify_announcements_only = modify_announcements_only
540            .into_iter()
541            .map(|m| Ok(GroupChange::AnnouncementOnly(m.announcements_only)));
542
543        let changes: Result<Vec<GroupChange>, GroupDecodingError> = new_members
544            .chain(delete_members)
545            .chain(modify_member_roles)
546            .chain(modify_member_profile_keys)
547            .chain(add_pending_members)
548            .chain(delete_pending_members)
549            .chain(promote_pending_members)
550            .chain(modify_title)
551            .chain(modify_avatar)
552            .chain(modify_disappearing_messages_timer)
553            .chain(modify_attributes_access)
554            .chain(modify_description)
555            .chain(modify_member_access)
556            .chain(add_banned_members)
557            .chain(delete_banned_members)
558            .chain(promote_pending_member)
559            .chain(modify_add_from_invite_link_access)
560            .chain(add_requesting_members)
561            .chain(delete_requesting_members)
562            .chain(promote_requesting_members)
563            .chain(modify_invite_link_password)
564            .chain(modify_announcements_only)
565            .collect();
566
567        Ok(GroupChanges {
568            group_id: group_id
569                .try_into()
570                .map_err(|_| GroupDecodingError::WrongBlob)?,
571            editor,
572            revision,
573            changes: changes?,
574            change_epoch,
575        })
576    }
577
578    pub fn decrypt_avatar(&self, ciphertext: &[u8]) -> Option<Vec<u8>> {
579        use group_attribute_blob::Content;
580        match self.decrypt_blob(ciphertext).content {
581            Some(Content::Avatar(d)) => Some(d).filter(|d| !d.is_empty()),
582            _ => None,
583        }
584    }
585}