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