libsignal_service/groups_v2/
operations.rs1use 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}