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 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 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}