1use std::collections::{HashMap, HashSet};
9use std::fmt::{Debug, Display};
10use std::marker::PhantomData;
11
12use serde::{Deserialize, Serialize};
13use thiserror::Error;
14
15use crate::crypto::Rng;
16use crate::data_scheme::{GroupSecret, GroupSecretError, SecretBundle, SecretBundleState};
17use crate::key_bundle::LongTermKeyBundle;
18use crate::traits::{
19 GroupMembership, IdentityHandle, IdentityManager, IdentityRegistry, OperationId, PreKeyManager,
20 PreKeyRegistry,
21};
22use crate::two_party::{TwoParty, TwoPartyError, TwoPartyMessage, TwoPartyState};
23
24pub struct Dcgka<ID, OP, PKI, DGM, KMG> {
27 _marker: PhantomData<(ID, OP, PKI, DGM, KMG)>,
28}
29
30#[derive(Debug, Serialize, Deserialize)]
32#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
33pub struct DcgkaState<ID, OP, PKI, DGM, KMG>
34where
35 ID: IdentityHandle,
36 OP: OperationId,
37 PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, LongTermKeyBundle>,
38 DGM: GroupMembership<ID, OP>,
39 KMG: IdentityManager<KMG::State> + PreKeyManager,
40{
41 pub pki: PKI::State,
44
45 pub my_keys: KMG::State,
48
49 pub my_id: ID,
51
52 pub two_party: HashMap<ID, TwoPartyState<LongTermKeyBundle>>,
55
56 pub dgm: DGM::State,
58}
59
60impl<ID, OP, PKI, DGM, KMG> Dcgka<ID, OP, PKI, DGM, KMG>
61where
62 ID: IdentityHandle,
63 OP: OperationId,
64 PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, LongTermKeyBundle>,
65 DGM: GroupMembership<ID, OP>,
66 KMG: IdentityManager<KMG::State> + PreKeyManager,
67{
68 pub fn init(
72 my_id: ID,
73 my_keys: KMG::State,
74 pki: PKI::State,
75 dgm: DGM::State,
76 ) -> DcgkaState<ID, OP, PKI, DGM, KMG> {
77 DcgkaState {
78 pki,
79 my_id,
80 my_keys,
81 two_party: HashMap::new(),
82 dgm,
83 }
84 }
85
86 pub fn process(
94 y: DcgkaState<ID, OP, PKI, DGM, KMG>,
95 input: ProcessInput<ID, OP, DGM>,
96 ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
97 let ProcessInput {
98 sender,
99 control_message,
100 direct_message,
101 seq,
102 } = input;
103 let (y_i, output) = match control_message {
104 ControlMessage::Create { initial_members } => {
105 Self::process_create(y, &sender, initial_members, direct_message)?
106 }
107 ControlMessage::Update => Self::process_update(y, &sender, direct_message)?,
108 ControlMessage::Remove { removed } => {
109 Self::process_remove(y, sender, seq, &removed, direct_message)?
110 }
111 ControlMessage::Add { added } => {
112 Self::process_add(y, sender, seq, added, direct_message)?
113 }
114 };
115 Ok((y_i, output))
116 }
117
118 pub fn create(
122 y: DcgkaState<ID, OP, PKI, DGM, KMG>,
123 initial_members: Vec<ID>,
124 group_secret: &GroupSecret,
125 rng: &Rng,
126 ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
127 let mut initial_members: Vec<ID> =
129 initial_members.into_iter().fold(Vec::new(), |mut acc, id| {
130 if !acc.contains(&id) {
131 acc.push(id);
132 }
133 acc
134 });
135
136 if !initial_members.contains(&y.my_id) {
138 initial_members.push(y.my_id);
139 }
140
141 let control_message = ControlMessage::Create {
143 initial_members: initial_members.clone(),
144 };
145
146 let (y_ii, direct_messages) =
148 Self::send_group_secret(y, &initial_members, group_secret, rng)?;
149
150 Ok((
151 y_ii,
152 OperationOutput {
153 control_message,
154 direct_messages,
155 },
156 ))
157 }
158
159 fn process_create(
161 mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
162 sender: &ID,
163 initial_members: Vec<ID>,
164 direct_message: Option<DirectMessage<ID, OP, DGM>>,
165 ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
166 y.dgm =
167 DGM::create(y.my_id, &initial_members).map_err(|err| DcgkaError::DgmOperation(err))?;
168 Self::process_secret(y, sender, direct_message)
169 }
170
171 pub fn update(
173 y: DcgkaState<ID, OP, PKI, DGM, KMG>,
174 group_secret: &GroupSecret,
175 rng: &Rng,
176 ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
177 let control_message = ControlMessage::Update;
178
179 let recipient_ids: Vec<ID> = Self::members(&y)?
180 .into_iter()
181 .filter(|member| member != &y.my_id)
182 .collect();
183
184 let (y_i, direct_messages) = Self::send_group_secret(y, &recipient_ids, group_secret, rng)?;
185
186 Ok((
187 y_i,
188 OperationOutput {
189 control_message,
190 direct_messages,
191 },
192 ))
193 }
194
195 fn process_update(
197 y: DcgkaState<ID, OP, PKI, DGM, KMG>,
198 sender: &ID,
199 direct_message: Option<DirectMessage<ID, OP, DGM>>,
200 ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
201 Self::process_secret(y, sender, direct_message)
202 }
203
204 pub fn remove(
208 y: DcgkaState<ID, OP, PKI, DGM, KMG>,
209 removed: ID,
210 group_secret: &GroupSecret,
211 rng: &Rng,
212 ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
213 let control_message = ControlMessage::Remove { removed };
214
215 let recipient_ids: Vec<ID> = Self::members(&y)?
216 .into_iter()
217 .filter(|member| member != &y.my_id && member != &removed)
218 .collect();
219
220 let (y_i, direct_messages) = Self::send_group_secret(y, &recipient_ids, group_secret, rng)?;
221
222 Ok((
223 y_i,
224 OperationOutput {
225 control_message,
226 direct_messages,
227 },
228 ))
229 }
230
231 fn process_remove(
233 mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
234 sender: ID,
235 seq: OP,
236 removed: &ID,
237 direct_message: Option<DirectMessage<ID, OP, DGM>>,
238 ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
239 y.dgm = DGM::remove(y.dgm, sender, removed, seq)
240 .map_err(|err| DcgkaError::DgmOperation(err))?;
241 Self::process_secret(y, &sender, direct_message)
242 }
243
244 pub fn add(
250 y: DcgkaState<ID, OP, PKI, DGM, KMG>,
251 added: ID,
252 bundle: &SecretBundleState,
253 rng: &Rng,
254 ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
255 let control_message = ControlMessage::Add { added };
257
258 let (y_i, ciphertext) = {
260 let bundle_bytes = bundle.to_bytes()?;
261 Self::encrypt_to(y, &added, &bundle_bytes, rng)?
262 };
263 let direct_message = DirectMessage {
264 recipient: added,
265 content: DirectMessageContent::Welcome {
266 ciphertext,
267 history: y_i.dgm.clone(),
268 },
269 };
270
271 Ok((
272 y_i,
273 OperationOutput {
274 control_message,
275 direct_messages: vec![direct_message],
276 },
277 ))
278 }
279
280 fn process_add(
283 mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
284 sender: ID,
285 seq: OP,
286 added: ID,
287 direct_message: Option<DirectMessage<ID, OP, DGM>>,
288 ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
289 y.dgm = DGM::add(y.dgm, sender, added, seq).map_err(|err| DcgkaError::DgmOperation(err))?;
290
291 if added == y.my_id {
292 let Some(DirectMessage {
293 recipient,
294 content:
295 DirectMessageContent::Welcome {
296 ciphertext,
297 history,
298 },
299 ..
300 }) = direct_message
301 else {
302 return match direct_message {
303 Some(direct_message) => Err(DcgkaError::UnexpectedDirectMessageType(
304 DirectMessageType::Welcome,
305 direct_message.message_type(),
306 )),
307 None => Err(DcgkaError::MissingDirectMessage(DirectMessageType::Welcome)),
308 };
309 };
310
311 if recipient != y.my_id {
312 return Err(DcgkaError::NotOurDirectMessage(y.my_id, recipient));
313 }
314
315 return Self::process_welcome(y, sender, ciphertext, history);
316 }
317
318 Ok((y, GroupSecretOutput::None))
319 }
320
321 fn process_welcome(
324 mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
325 sender: ID,
326 ciphertext: TwoPartyMessage,
327 history: DGM::State,
328 ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
329 y.dgm = DGM::from_welcome(y.my_id, history).map_err(|err| DcgkaError::DgmOperation(err))?;
330
331 let (y_i, bundle) = {
332 let (y_i, plaintext) = Self::decrypt_from(y, &sender, ciphertext)?;
333 let bundle = SecretBundle::try_from_bytes(&plaintext)?;
334 (y_i, bundle)
335 };
336
337 Ok((y_i, GroupSecretOutput::Bundle(bundle)))
338 }
339
340 fn send_group_secret(
344 y: DcgkaState<ID, OP, PKI, DGM, KMG>,
345 recipients: &[ID],
346 group_secret: &GroupSecret,
347 rng: &Rng,
348 ) -> SendSecretResult<ID, OP, PKI, DGM, KMG> {
349 let mut direct_messages: Vec<DirectMessage<ID, OP, DGM>> =
350 Vec::with_capacity(recipients.len());
351
352 let y_i = {
353 let mut y_loop = y;
354 for recipient in recipients {
355 if recipient == &y_loop.my_id {
357 continue;
358 }
359
360 let (y_next, ciphertext) =
362 Self::encrypt_to(y_loop, recipient, &group_secret.to_bytes()?, rng)?;
363 y_loop = y_next;
364
365 direct_messages.push(DirectMessage {
366 recipient: *recipient,
367 content: DirectMessageContent::TwoParty { ciphertext },
368 });
369 }
370 y_loop
371 };
372
373 Ok((y_i, direct_messages))
374 }
375
376 fn process_secret(
379 y: DcgkaState<ID, OP, PKI, DGM, KMG>,
380 sender: &ID,
381 direct_message: Option<DirectMessage<ID, OP, DGM>>,
382 ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
383 let Some(direct_message) = direct_message else {
384 return Ok((y, GroupSecretOutput::None));
385 };
386
387 let DirectMessage {
388 recipient,
389 content: DirectMessageContent::TwoParty { ciphertext },
390 ..
391 } = direct_message
392 else {
393 return Err(DcgkaError::UnexpectedDirectMessageType(
394 DirectMessageType::TwoParty,
395 direct_message.message_type(),
396 ));
397 };
398
399 if recipient != y.my_id {
400 return Err(DcgkaError::NotOurDirectMessage(y.my_id, recipient));
401 }
402
403 let (y_i, plaintext) = Self::decrypt_from(y, sender, ciphertext)?;
404 let group_secret = GroupSecret::try_from_bytes(&plaintext)?;
405
406 Ok((y_i, GroupSecretOutput::Secret(group_secret)))
407 }
408
409 fn encrypt_to(
414 mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
415 recipient: &ID,
416 plaintext: &[u8],
417 rng: &Rng,
418 ) -> DcgkaResult<ID, OP, PKI, DGM, KMG, TwoPartyMessage> {
419 let y_2sm = match y.two_party.remove(recipient) {
420 Some(y_2sm) => y_2sm,
421 None => {
422 let (pki_i, prekey_bundle) = PKI::key_bundle(y.pki, recipient)
423 .map_err(|err| DcgkaError::PreKeyRegistry(err))?;
424 y.pki = pki_i;
425 let prekey_bundle = prekey_bundle.ok_or(DcgkaError::MissingPreKeys(*recipient))?;
426 TwoParty::<KMG, LongTermKeyBundle>::init(prekey_bundle)
427 }
428 };
429 let (y_2sm_i, ciphertext) =
430 TwoParty::<KMG, LongTermKeyBundle>::send(y_2sm, &y.my_keys, plaintext, rng)?;
431 y.two_party.insert(*recipient, y_2sm_i);
432 Ok((y, ciphertext))
433 }
434
435 fn decrypt_from(
439 mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
440 sender: &ID,
441 ciphertext: TwoPartyMessage,
442 ) -> DcgkaResult<ID, OP, PKI, DGM, KMG, Vec<u8>> {
443 let y_2sm = match y.two_party.remove(sender) {
444 Some(y_2sm) => y_2sm,
445 None => {
446 let (pki_i, prekey_bundle) = PKI::key_bundle(y.pki, sender)
447 .map_err(|err| DcgkaError::PreKeyRegistry(err))?;
448 y.pki = pki_i;
449 let prekey_bundle = prekey_bundle.ok_or(DcgkaError::MissingPreKeys(*sender))?;
450 TwoParty::<KMG, LongTermKeyBundle>::init(prekey_bundle)
451 }
452 };
453 let (y_2sm_i, y_my_keys_i, plaintext) =
454 TwoParty::<KMG, LongTermKeyBundle>::receive(y_2sm, y.my_keys, ciphertext)?;
455 y.my_keys = y_my_keys_i;
456 y.two_party.insert(*sender, y_2sm_i);
457 Ok((y, plaintext))
458 }
459
460 pub fn members(
462 y: &DcgkaState<ID, OP, PKI, DGM, KMG>,
463 ) -> Result<HashSet<ID>, DcgkaError<ID, OP, PKI, DGM, KMG>> {
464 let members = DGM::members(&y.dgm).map_err(|err| DcgkaError::GroupMembership(err))?;
465 Ok(members)
466 }
467}
468
469pub type SendSecretResult<ID, OP, PKI, DGM, KMG> = Result<
470 (
471 DcgkaState<ID, OP, PKI, DGM, KMG>,
472 Vec<DirectMessage<ID, OP, DGM>>,
473 ),
474 DcgkaError<ID, OP, PKI, DGM, KMG>,
475>;
476
477pub type DcgkaResult<ID, OP, PKI, DGM, KMG, T> =
478 Result<(DcgkaState<ID, OP, PKI, DGM, KMG>, T), DcgkaError<ID, OP, PKI, DGM, KMG>>;
479
480pub type DcgkaProcessResult<ID, OP, PKI, DGM, KMG> =
481 DcgkaResult<ID, OP, PKI, DGM, KMG, GroupSecretOutput>;
482
483pub type DcgkaOperationResult<ID, OP, PKI, DGM, KMG> =
484 DcgkaResult<ID, OP, PKI, DGM, KMG, OperationOutput<ID, OP, DGM>>;
485
486#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
491pub enum ControlMessage<ID> {
492 Create { initial_members: Vec<ID> },
493 Update,
494 Remove { removed: ID },
495 Add { added: ID },
496}
497
498impl<ID> Display for ControlMessage<ID> {
499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500 write!(
501 f,
502 "{}",
503 match self {
504 ControlMessage::Create { .. } => "create",
505 ControlMessage::Update => "update",
506 ControlMessage::Remove { .. } => "remove",
507 ControlMessage::Add { .. } => "add",
508 }
509 )
510 }
511}
512
513#[derive(Clone, Debug)]
515pub struct ProcessInput<ID, OP, DGM>
516where
517 DGM: GroupMembership<ID, OP>,
518{
519 pub seq: OP,
522
523 pub sender: ID,
525
526 pub control_message: ControlMessage<ID>,
528
529 pub direct_message: Option<DirectMessage<ID, OP, DGM>>,
534}
535
536#[derive(Debug, PartialEq, Eq)]
538pub enum GroupSecretOutput {
539 None,
540 Secret(GroupSecret),
541 Bundle(SecretBundleState),
542}
543
544#[derive(Debug)]
548pub struct OperationOutput<ID, OP, DGM>
549where
550 DGM: GroupMembership<ID, OP>,
551{
552 pub control_message: ControlMessage<ID>,
554
555 pub direct_messages: Vec<DirectMessage<ID, OP, DGM>>,
557}
558
559#[derive(Clone, Debug, Serialize, Deserialize)]
568#[serde(bound(serialize = "DGM::State: Serialize, ID: Serialize"))]
569#[serde(bound(deserialize = "DGM::State: Deserialize<'de>, ID: Deserialize<'de>"))]
570pub struct DirectMessage<ID, OP, DGM>
571where
572 DGM: GroupMembership<ID, OP>,
573{
574 pub recipient: ID,
575 pub content: DirectMessageContent<ID, OP, DGM>,
576}
577
578#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
579pub enum DirectMessageType {
580 Welcome,
581 TwoParty,
582}
583
584impl Display for DirectMessageType {
585 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
586 write!(
587 f,
588 "{}",
589 match self {
590 DirectMessageType::Welcome => "welcome",
591 DirectMessageType::TwoParty => "2sm",
592 }
593 )
594 }
595}
596
597impl<ID, OP, DGM> DirectMessage<ID, OP, DGM>
598where
599 DGM: GroupMembership<ID, OP>,
600{
601 pub fn message_type(&self) -> DirectMessageType {
602 match self.content {
603 DirectMessageContent::Welcome { .. } => DirectMessageType::Welcome,
604 DirectMessageContent::TwoParty { .. } => DirectMessageType::TwoParty,
605 }
606 }
607}
608
609#[derive(Clone, Debug, Serialize, Deserialize)]
610#[serde(bound(serialize = "DGM::State: Serialize"))]
611#[serde(bound(deserialize = "DGM::State: Deserialize<'de>"))]
612pub enum DirectMessageContent<ID, OP, DGM>
613where
614 DGM: GroupMembership<ID, OP>,
615{
616 Welcome {
617 ciphertext: TwoPartyMessage,
618 history: DGM::State,
619 },
620 TwoParty {
621 ciphertext: TwoPartyMessage,
622 },
623}
624
625#[derive(Debug, Error)]
626pub enum DcgkaError<ID, OP, PKI, DGM, KMG>
627where
628 PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, LongTermKeyBundle>,
629 DGM: GroupMembership<ID, OP>,
630 KMG: PreKeyManager,
631{
632 #[error("expected direct message of type \"{0}\" but got nothing instead")]
633 MissingDirectMessage(DirectMessageType),
634
635 #[error("expected direct message of type \"{0}\" but got message of type \"{1}\" instead")]
636 UnexpectedDirectMessageType(DirectMessageType, DirectMessageType),
637
638 #[error("direct message recipient mismatch, expected recipient: {1}, actual recipient: {0}")]
639 NotOurDirectMessage(ID, ID),
640
641 #[error("computing members view from dgm failed: {0}")]
642 GroupMembership(DGM::Error),
643
644 #[error("dgm operation failed: {0}")]
645 DgmOperation(DGM::Error),
646
647 #[error("failed retrieving bundle from pre key registry: {0}")]
648 PreKeyRegistry(<PKI as PreKeyRegistry<ID, LongTermKeyBundle>>::Error),
649
650 #[error("failed retrieving identity from registry: {0}")]
651 IdentityRegistry(<PKI as IdentityRegistry<ID, PKI::State>>::Error),
652
653 #[error("missing key bundle for member {0}")]
654 MissingPreKeys(ID),
655
656 #[error(transparent)]
657 GroupSecret(#[from] GroupSecretError),
658
659 #[error(transparent)]
660 KeyManager(KMG::Error),
661
662 #[error(transparent)]
663 TwoParty(#[from] TwoPartyError),
664}