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)]
568pub struct DirectMessage<ID, OP, DGM>
569where
570 DGM: GroupMembership<ID, OP>,
571{
572 pub recipient: ID,
573 pub content: DirectMessageContent<ID, OP, DGM>,
574}
575
576#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
577pub enum DirectMessageType {
578 Welcome,
579 TwoParty,
580}
581
582impl Display for DirectMessageType {
583 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584 write!(
585 f,
586 "{}",
587 match self {
588 DirectMessageType::Welcome => "welcome",
589 DirectMessageType::TwoParty => "2sm",
590 }
591 )
592 }
593}
594
595impl<ID, OP, DGM> DirectMessage<ID, OP, DGM>
596where
597 DGM: GroupMembership<ID, OP>,
598{
599 pub fn message_type(&self) -> DirectMessageType {
600 match self.content {
601 DirectMessageContent::Welcome { .. } => DirectMessageType::Welcome,
602 DirectMessageContent::TwoParty { .. } => DirectMessageType::TwoParty,
603 }
604 }
605}
606
607#[derive(Clone, Debug, Serialize, Deserialize)]
608pub enum DirectMessageContent<ID, OP, DGM>
609where
610 DGM: GroupMembership<ID, OP>,
611{
612 Welcome {
613 ciphertext: TwoPartyMessage,
614 history: DGM::State,
615 },
616 TwoParty {
617 ciphertext: TwoPartyMessage,
618 },
619}
620
621#[derive(Debug, Error)]
622pub enum DcgkaError<ID, OP, PKI, DGM, KMG>
623where
624 PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, LongTermKeyBundle>,
625 DGM: GroupMembership<ID, OP>,
626 KMG: PreKeyManager,
627{
628 #[error("expected direct message of type \"{0}\" but got nothing instead")]
629 MissingDirectMessage(DirectMessageType),
630
631 #[error("expected direct message of type \"{0}\" but got message of type \"{1}\" instead")]
632 UnexpectedDirectMessageType(DirectMessageType, DirectMessageType),
633
634 #[error("direct message recipient mismatch, expected recipient: {1}, actual recipient: {0}")]
635 NotOurDirectMessage(ID, ID),
636
637 #[error("computing members view from dgm failed: {0}")]
638 GroupMembership(DGM::Error),
639
640 #[error("dgm operation failed: {0}")]
641 DgmOperation(DGM::Error),
642
643 #[error("failed retrieving bundle from pre key registry: {0}")]
644 PreKeyRegistry(<PKI as PreKeyRegistry<ID, LongTermKeyBundle>>::Error),
645
646 #[error("failed retrieving identity from registry: {0}")]
647 IdentityRegistry(<PKI as IdentityRegistry<ID, PKI::State>>::Error),
648
649 #[error("missing key bundle for member {0}")]
650 MissingPreKeys(ID),
651
652 #[error(transparent)]
653 GroupSecret(#[from] GroupSecretError),
654
655 #[error(transparent)]
656 KeyManager(KMG::Error),
657
658 #[error(transparent)]
659 TwoParty(#[from] TwoPartyError),
660}