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        aci: &[u8],
130        profile_key: &[u8],
131        presentation: &[u8],
132    ) -> Result<(Aci, ProfileKey), GroupDecodingError> {
133        if presentation.is_empty() {
134            let aci = self.decrypt_aci(aci)?;
135            let profile_key = self.decrypt_profile_key(profile_key, aci)?;
136            return Ok((aci, profile_key));
137        }
138
139        let profile_key_credential_presentation =
140            AnyProfileKeyCredentialPresentation::new(presentation)?;
141
142        match self.group_secret_params.decrypt_service_id(
143            profile_key_credential_presentation.get_uuid_ciphertext(),
144        )? {
145            ServiceId::Aci(aci) => {
146                let profile_key =
147                    self.group_secret_params.decrypt_profile_key(
148                        profile_key_credential_presentation
149                            .get_profile_key_ciphertext(),
150                        aci,
151                    )?;
152                Ok((aci, profile_key))
153            },
154            _ => Err(GroupDecodingError::NotAci),
155        }
156    }
157
158    fn decrypt_pni_aci_promotion_presentation(
159        &self,
160        member: &proto::group_change::actions::PromotePendingPniAciMemberProfileKeyAction,
161    ) -> Result<PromotedMember, GroupDecodingError> {
162        let aci = self.decrypt_aci(&member.user_id)?;
163        let pni = self.decrypt_pni(&member.pni)?;
164        let profile_key = self.decrypt_profile_key(&member.profile_key, aci)?;
165        Ok(PromotedMember {
166            aci,
167            pni,
168            profile_key,
169        })
170    }
171
172    fn decrypt_member(
173        &self,
174        member: EncryptedMember,
175    ) -> Result<Member, GroupDecodingError> {
176        let (aci, profile_key) = self.decrypt_profile_key_presentation(
177            &member.user_id,
178            &member.profile_key,
179            &member.presentation,
180        )?;
181        Ok(Member {
182            aci,
183            profile_key,
184            role: member.role.try_into()?,
185            joined_at_revision: member.joined_at_revision,
186        })
187    }
188
189    fn decrypt_pending_member(
190        &self,
191        member: proto::PendingMember,
192    ) -> Result<PendingMember, GroupDecodingError> {
193        let inner_member =
194            member.member.ok_or(GroupDecodingError::WrongBlob)?;
195        let service_id = self.decrypt_service_id(&inner_member.user_id)?;
196        let added_by_aci = self.decrypt_aci(&member.added_by_user_id)?;
197
198        Ok(PendingMember {
199            address: service_id,
200            role: inner_member.role.try_into()?,
201            added_by_aci,
202            timestamp: member.timestamp,
203        })
204    }
205
206    fn decrypt_requesting_member(
207        &self,
208        member: proto::RequestingMember,
209    ) -> Result<RequestingMember, GroupDecodingError> {
210        let (aci, profile_key) = self.decrypt_profile_key_presentation(
211            &member.user_id,
212            &member.profile_key,
213            &member.presentation,
214        )?;
215        Ok(RequestingMember {
216            profile_key,
217            aci,
218            timestamp: member.timestamp,
219        })
220    }
221
222    fn decrypt_banned_member(
223        &self,
224        member: proto::BannedMember,
225    ) -> Result<BannedMember, GroupDecodingError> {
226        Ok(BannedMember {
227            service_id: self.decrypt_service_id(&member.user_id)?,
228            timestamp: member.timestamp,
229        })
230    }
231
232    fn decrypt_blob(&self, bytes: &[u8]) -> GroupAttributeBlob {
233        if bytes.is_empty() {
234            GroupAttributeBlob::default()
235        } else if bytes.len() < 29 {
236            tracing::warn!("bad encrypted blob length");
237            GroupAttributeBlob::default()
238        } else {
239            self.group_secret_params
240                .decrypt_blob(bytes)
241                .map_err(GroupDecodingError::from)
242                .and_then(|b| {
243                    GroupAttributeBlob::decode(Bytes::copy_from_slice(&b[4..]))
244                        .map_err(GroupDecodingError::ProtobufDecodeError)
245                })
246                .unwrap_or_else(|e| {
247                    tracing::warn!("bad encrypted blob: {}", e);
248                    GroupAttributeBlob::default()
249                })
250        }
251    }
252
253    fn decrypt_title(&self, ciphertext: &[u8]) -> String {
254        use group_attribute_blob::Content;
255        match self.decrypt_blob(ciphertext).content {
256            Some(Content::Title(title)) => title,
257            _ => "".into(),
258        }
259    }
260
261    fn decrypt_description(&self, ciphertext: &[u8]) -> Option<String> {
262        use group_attribute_blob::Content;
263        match self.decrypt_blob(ciphertext).content {
264            Some(Content::Description(d)) => Some(d).filter(|d| !d.is_empty()),
265            _ => None,
266        }
267    }
268
269    fn decrypt_disappearing_message_timer(
270        &self,
271        ciphertext: &[u8],
272    ) -> Option<Timer> {
273        use group_attribute_blob::Content;
274        match self.decrypt_blob(ciphertext).content {
275            Some(Content::DisappearingMessagesDuration(duration)) => {
276                Some(Timer { duration })
277            },
278            _ => None,
279        }
280    }
281
282    pub fn new(group_secret_params: GroupSecretParams) -> Self {
283        Self {
284            group_secret_params,
285        }
286    }
287
288    pub fn decrypt_group(
289        &self,
290        group: proto::Group,
291    ) -> Result<Group, GroupDecodingError> {
292        // Destructuring to catch any future changes
293        let proto::Group {
294            public_key: _,
295            title,
296            avatar,
297            disappearing_messages_timer,
298            access_control,
299            revision,
300            members,
301            pending_members,
302            requesting_members,
303            invite_link_password,
304            description,
305            announcements_only,
306            banned_members,
307        } = group;
308
309        let title = self.decrypt_title(&title);
310
311        let description = self.decrypt_description(&description);
312
313        let disappearing_messages_timer = self
314            .decrypt_disappearing_message_timer(&disappearing_messages_timer);
315
316        let members = members
317            .into_iter()
318            .map(|m| self.decrypt_member(m))
319            .collect::<Result<_, _>>()?;
320
321        let pending_members = pending_members
322            .into_iter()
323            .map(|m| self.decrypt_pending_member(m))
324            .collect::<Result<_, _>>()?;
325
326        let requesting_members = requesting_members
327            .into_iter()
328            .map(|m| self.decrypt_requesting_member(m))
329            .collect::<Result<_, _>>()?;
330
331        let banned_members = banned_members
332            .into_iter()
333            .map(|m| self.decrypt_banned_member(m))
334            .collect::<Result<_, _>>()?;
335
336        let access_control =
337            access_control.map(TryInto::try_into).transpose()?;
338
339        Ok(Group {
340            title,
341            avatar,
342            disappearing_messages_timer,
343            access_control,
344            revision,
345            members,
346            pending_members,
347            requesting_members,
348            invite_link_password,
349            description,
350            announcements_only,
351            banned_members,
352        })
353    }
354
355    pub fn decrypt_group_change(
356        &self,
357        group_change: proto::GroupChange,
358    ) -> Result<GroupChanges, GroupDecodingError> {
359        // Destructuring to catch any future changes
360        let proto::GroupChange {
361            actions,
362            server_signature: _,
363            change_epoch,
364        } = group_change;
365
366        let proto::group_change::Actions {
367            group_id,
368            source_service_id,
369            revision,
370            add_members,
371            delete_members,
372            modify_member_roles,
373            modify_member_profile_keys,
374            add_pending_members,
375            delete_pending_members,
376            promote_pending_members,
377            modify_title,
378            modify_avatar,
379            modify_disappearing_messages_timer,
380            modify_attributes_access,
381            modify_member_access,
382            modify_add_from_invite_link_access,
383            add_requesting_members,
384            delete_requesting_members,
385            promote_requesting_members,
386            modify_invite_link_password,
387            modify_description,
388            modify_announcements_only,
389            add_banned_members,
390            delete_banned_members,
391            promote_pending_pni_aci_members,
392        } = Message::decode(Bytes::from(actions))?;
393
394        let editor = self.decrypt_aci(&source_service_id)?;
395
396        let new_members =
397            add_members
398                .into_iter()
399                .filter_map(|m| m.added)
400                .map(|added| {
401                    Ok(GroupChange::NewMember(self.decrypt_member(added)?))
402                });
403
404        let delete_members = delete_members.into_iter().map(|c| {
405            Ok(GroupChange::DeleteMember(
406                self.decrypt_aci(&c.deleted_user_id)?,
407            ))
408        });
409
410        let modify_member_roles = modify_member_roles.into_iter().map(|m| {
411            Ok(GroupChange::ModifyMemberRole {
412                aci: self.decrypt_aci(&m.user_id)?,
413                role: m.role.try_into()?,
414            })
415        });
416
417        let modify_member_profile_keys =
418            modify_member_profile_keys.into_iter().map(|m| {
419                let (aci, profile_key) = self
420                    .decrypt_profile_key_presentation(
421                        &m.user_id,
422                        &m.profile_key,
423                        &m.presentation,
424                    )?;
425                Ok(GroupChange::ModifyMemberProfileKey { aci, profile_key })
426            });
427
428        let add_pending_members = add_pending_members
429            .into_iter()
430            .filter_map(|m| m.added)
431            .map(|added| {
432                Ok(GroupChange::NewPendingMember(
433                    self.decrypt_pending_member(added)?,
434                ))
435            });
436
437        let delete_pending_members =
438            delete_pending_members.into_iter().map(|m| {
439                Ok(GroupChange::DeletePendingMember(
440                    self.decrypt_service_id(&m.deleted_user_id)?,
441                ))
442            });
443
444        let promote_pending_members =
445            promote_pending_members.into_iter().map(|m| {
446                let (aci, profile_key) = self
447                    .decrypt_profile_key_presentation(
448                        &m.user_id,
449                        &m.profile_key,
450                        &m.presentation,
451                    )?;
452                Ok(GroupChange::PromotePendingMember {
453                    address: aci.into(),
454                    profile_key,
455                })
456            });
457
458        let modify_title = modify_title
459            .into_iter()
460            .map(|m| Ok(GroupChange::Title(self.decrypt_title(&m.title))));
461
462        let modify_avatar = modify_avatar
463            .into_iter()
464            .map(|m| Ok(GroupChange::Avatar(m.avatar)));
465
466        let modify_description = modify_description.into_iter().map(|m| {
467            Ok(GroupChange::Description(
468                self.decrypt_description(&m.description),
469            ))
470        });
471
472        let modify_disappearing_messages_timer =
473            modify_disappearing_messages_timer.into_iter().map(|m| {
474                Ok(GroupChange::Timer(
475                    self.decrypt_disappearing_message_timer(&m.timer),
476                ))
477            });
478
479        let modify_attributes_access =
480            modify_attributes_access.into_iter().map(|m| {
481                Ok(GroupChange::AttributeAccess(
482                    m.attributes_access.try_into()?,
483                ))
484            });
485
486        let modify_member_access = modify_member_access.into_iter().map(|m| {
487            Ok(GroupChange::MemberAccess(m.members_access.try_into()?))
488        });
489
490        let add_banned_members = add_banned_members
491            .into_iter()
492            .filter_map(|m| m.added)
493            .map(|m| {
494                Ok(GroupChange::AddBannedMember(self.decrypt_banned_member(m)?))
495            });
496
497        let delete_banned_members =
498            delete_banned_members.into_iter().map(|m| {
499                Ok(GroupChange::DeleteBannedMember(
500                    self.decrypt_service_id(&m.deleted_user_id)?,
501                ))
502            });
503
504        let promote_pending_member =
505            promote_pending_pni_aci_members.into_iter().map(|m| {
506                let promoted =
507                    self.decrypt_pni_aci_promotion_presentation(&m)?;
508                Ok(GroupChange::PromotePendingPniAciMemberProfileKey(promoted))
509            });
510
511        let modify_add_from_invite_link_access =
512            modify_add_from_invite_link_access.into_iter().map(|m| {
513                Ok(GroupChange::InviteLinkAccess(
514                    m.add_from_invite_link_access.try_into()?,
515                ))
516            });
517
518        let add_requesting_members = add_requesting_members
519            .into_iter()
520            .filter_map(|m| m.added)
521            .map(|added| {
522                Ok(GroupChange::NewRequestingMember(
523                    self.decrypt_requesting_member(added)?,
524                ))
525            });
526
527        let delete_requesting_members =
528            delete_requesting_members.into_iter().map(|m| {
529                Ok(GroupChange::DeleteRequestingMember(
530                    self.decrypt_aci(&m.deleted_user_id)?,
531                ))
532            });
533
534        let promote_requesting_members =
535            promote_requesting_members.into_iter().map(|m| {
536                Ok(GroupChange::PromoteRequestingMember {
537                    aci: self.decrypt_aci(&m.user_id)?,
538                    role: m.role.try_into()?,
539                })
540            });
541
542        let modify_invite_link_password =
543            modify_invite_link_password.into_iter().map(|m| {
544                Ok(GroupChange::InviteLinkPassword(
545                    BASE64_RELAXED.encode(m.invite_link_password),
546                ))
547            });
548
549        let modify_announcements_only = modify_announcements_only
550            .into_iter()
551            .map(|m| Ok(GroupChange::AnnouncementOnly(m.announcements_only)));
552
553        let changes: Result<Vec<GroupChange>, GroupDecodingError> = new_members
554            .chain(delete_members)
555            .chain(modify_member_roles)
556            .chain(modify_member_profile_keys)
557            .chain(add_pending_members)
558            .chain(delete_pending_members)
559            .chain(promote_pending_members)
560            .chain(modify_title)
561            .chain(modify_avatar)
562            .chain(modify_disappearing_messages_timer)
563            .chain(modify_attributes_access)
564            .chain(modify_description)
565            .chain(modify_member_access)
566            .chain(add_banned_members)
567            .chain(delete_banned_members)
568            .chain(promote_pending_member)
569            .chain(modify_add_from_invite_link_access)
570            .chain(add_requesting_members)
571            .chain(delete_requesting_members)
572            .chain(promote_requesting_members)
573            .chain(modify_invite_link_password)
574            .chain(modify_announcements_only)
575            .collect();
576
577        Ok(GroupChanges {
578            group_id: group_id
579                .try_into()
580                .map_err(|_| GroupDecodingError::WrongBlob)?,
581            editor,
582            revision,
583            changes: changes?,
584            change_epoch,
585        })
586    }
587
588    pub fn decrypt_avatar(&self, ciphertext: &[u8]) -> Option<Vec<u8>> {
589        use group_attribute_blob::Content;
590        match self.decrypt_blob(ciphertext).content {
591            Some(Content::Avatar(d)) => Some(d).filter(|d| !d.is_empty()),
592            _ => None,
593        }
594    }
595}