From 50fa8552b1452cfd9a09c7556403aec15806c771 Mon Sep 17 00:00:00 2001 From: Matti Viljanen Date: Sun, 9 Feb 2025 22:22:54 +0200 Subject: [PATCH] Add missing group change types --- src/groups_v2/model.rs | 50 +++++++++++++++++++------ src/groups_v2/operations.rs | 75 ++++++++++++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 13 deletions(-) diff --git a/src/groups_v2/model.rs b/src/groups_v2/model.rs index 75990ba59..457462cb7 100644 --- a/src/groups_v2/model.rs +++ b/src/groups_v2/model.rs @@ -1,7 +1,7 @@ use std::{convert::TryFrom, convert::TryInto}; use derivative::Derivative; -use libsignal_protocol::ServiceId; +use libsignal_protocol::{Aci, Pni, ServiceId}; use serde::{Deserialize, Serialize}; use uuid::Uuid; use zkgroup::profiles::ProfileKey; @@ -54,6 +54,34 @@ impl PartialEq for RequestingMember { } } +#[derive(Derivative, Clone)] +#[derivative(Debug)] +pub struct BannedMember { + pub uuid: Uuid, + pub timestamp: u64, +} + +impl PartialEq for BannedMember { + fn eq(&self, other: &Self) -> bool { + self.uuid == other.uuid + } +} + +#[derive(Derivative, Clone)] +#[derivative(Debug)] +pub struct PromotedMember { + pub aci: Aci, + pub pni: Pni, + #[derivative(Debug = "ignore")] + pub profile_key: ProfileKey, +} + +impl PartialEq for PromotedMember { + fn eq(&self, other: &Self) -> bool { + self.aci == other.aci && self.pni == other.pni + } +} + #[derive(Copy, Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum AccessRequired { Unknown, @@ -105,7 +133,6 @@ pub enum GroupChange { #[derivative(Debug = "ignore")] profile_key: ProfileKey, }, - // for open groups NewPendingMember(PendingMember), DeletePendingMember(Uuid), PromotePendingMember { @@ -113,23 +140,24 @@ pub enum GroupChange { #[derivative(Debug = "ignore")] profile_key: ProfileKey, }, - // when admin control is enabled - NewRequestingMember(RequestingMember), - DeleteRequestingMember(Uuid), - PromoteRequestingMember { - uuid: Uuid, - role: Role, - }, - // group metadata Title(String), Avatar(String), Timer(Option), - Description(Option), AttributeAccess(AccessRequired), MemberAccess(AccessRequired), InviteLinkAccess(AccessRequired), + NewRequestingMember(RequestingMember), + DeleteRequestingMember(Uuid), + PromoteRequestingMember { + uuid: Uuid, + role: Role, + }, InviteLinkPassword(String), + Description(Option), AnnouncementOnly(bool), + AddBannedMember(BannedMember), + DeleteBannedMember(Uuid), + PromotePendingPniAciMemberProfileKey(PromotedMember), } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] diff --git a/src/groups_v2/operations.rs b/src/groups_v2/operations.rs index e14c1059e..0722f24c0 100644 --- a/src/groups_v2/operations.rs +++ b/src/groups_v2/operations.rs @@ -2,7 +2,7 @@ use std::convert::TryInto; use base64::prelude::*; use bytes::Bytes; -use libsignal_protocol::{Aci, ServiceId}; +use libsignal_protocol::{Aci, Pni, ServiceId}; use prost::Message; use zkgroup::{ groups::GroupSecretParams, @@ -19,7 +19,9 @@ use crate::{ }; use super::{ - model::{Member, PendingMember, RequestingMember}, + model::{ + BannedMember, Member, PendingMember, PromotedMember, RequestingMember, + }, Group, GroupChange, GroupChanges, }; @@ -43,6 +45,8 @@ pub enum GroupDecodingError { WrongEnumValue, #[error("wrong service ID type: should be ACI")] NotAci, + #[error("wrong service ID type: should be PNI")] + NotPni, } impl From for GroupDecodingError { @@ -84,6 +88,19 @@ impl GroupOperations { } } + fn decrypt_pni( + &self, + ciphertext: &[u8], + ) -> Result { + match self + .group_secret_params + .decrypt_service_id(bincode::deserialize(ciphertext)?)? + { + ServiceId::Pni(pni) => Ok(pni), + ServiceId::Aci(_aci) => Err(GroupDecodingError::NotPni), + } + } + fn decrypt_profile_key( &self, encrypted_profile_key: &[u8], @@ -118,6 +135,20 @@ impl GroupOperations { } } + fn decrypt_pni_aci_promotion_presentation( + &self, + member: &proto::group_change::actions::PromotePendingPniAciMemberProfileKeyAction, + ) -> Result { + let aci = self.decrypt_aci(&member.user_id)?; + let pni = self.decrypt_pni(&member.pni)?; + let profile_key = self.decrypt_profile_key(&member.profile_key, aci)?; + Ok(PromotedMember { + aci, + pni, + profile_key, + }) + } + fn decrypt_member( &self, member: EncryptedMember, @@ -174,6 +205,17 @@ impl GroupOperations { }) } + fn decrypt_banned_member( + &self, + member: proto::BannedMember, + ) -> Result { + let aci = self.decrypt_aci(&member.user_id)?; + Ok(BannedMember { + uuid: aci.into(), + timestamp: member.timestamp, + }) + } + fn decrypt_blob(&self, bytes: &[u8]) -> GroupAttributeBlob { if bytes.is_empty() { GroupAttributeBlob::default() @@ -381,6 +423,32 @@ impl GroupOperations { Ok(GroupChange::MemberAccess(m.members_access.try_into()?)) }); + let add_banned_members = actions + .add_banned_members + .into_iter() + .filter_map(|m| m.added) + .map(|added| { + Ok(GroupChange::AddBannedMember( + self.decrypt_banned_member(added)?, + )) + }); + + let delete_banned_members = + actions.delete_banned_members.into_iter().map(|added| { + Ok(GroupChange::DeleteBannedMember( + self.decrypt_aci(&added.deleted_user_id)?.into(), + )) + }); + + let promote_pending_member = actions + .promote_pending_pni_aci_members + .into_iter() + .map(|m| { + let promoted = + self.decrypt_pni_aci_promotion_presentation(&m)?; + Ok(GroupChange::PromotePendingPniAciMemberProfileKey(promoted)) + }); + let modify_add_from_invite_link_access = actions .modify_add_from_invite_link_access .into_iter() @@ -440,6 +508,9 @@ impl GroupOperations { .chain(modify_attributes_access) .chain(modify_description) .chain(modify_member_access) + .chain(add_banned_members) + .chain(delete_banned_members) + .chain(promote_pending_member) .chain(modify_add_from_invite_link_access) .chain(add_requesting_members) .chain(delete_requesting_members)