1#![allow(clippy::type_complexity)]
6use std::collections::{HashSet, VecDeque};
7use std::marker::PhantomData;
8
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use crate::crypto::xchacha20::{XAeadError, XAeadNonce};
13use crate::crypto::{Rng, RngError};
14use crate::data_scheme::data::{decrypt_data, encrypt_data};
15use crate::data_scheme::dcgka::{
16 ControlMessage, Dcgka, DcgkaError, DcgkaState, DirectMessage, GroupSecretOutput,
17 OperationOutput, ProcessInput,
18};
19use crate::data_scheme::group_secret::{
20 GroupSecret, GroupSecretError, GroupSecretId, SecretBundle, SecretBundleState,
21};
22use crate::key_bundle::LongTermKeyBundle;
23use crate::traits::{
24 GroupMembership, GroupMessage, GroupMessageContent, IdentityHandle, IdentityManager,
25 IdentityRegistry, OperationId, Ordering, PreKeyManager, PreKeyRegistry,
26};
27
28pub struct EncryptionGroup<ID, OP, PKI, DGM, KMG, ORD> {
30 _marker: PhantomData<(ID, OP, PKI, DGM, KMG, ORD)>,
31}
32
33#[derive(Debug, Serialize, Deserialize)]
35#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
36pub struct GroupState<ID, OP, PKI, DGM, KMG, ORD>
37where
38 ID: IdentityHandle,
39 OP: OperationId,
40 PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, LongTermKeyBundle>,
41 PKI::State: Clone,
42 DGM: GroupMembership<ID, OP>,
43 KMG: IdentityManager<KMG::State> + PreKeyManager,
44 KMG::State: Clone,
45 ORD: Ordering<ID, OP, DGM>,
46{
47 pub my_id: ID,
48 pub dcgka: DcgkaState<ID, OP, PKI, DGM, KMG>,
49 pub orderer: ORD::State,
50 pub secrets: SecretBundleState,
51 pub is_welcomed: bool,
52}
53
54impl<ID, OP, PKI, DGM, KMG, ORD> EncryptionGroup<ID, OP, PKI, DGM, KMG, ORD>
55where
56 ID: IdentityHandle,
57 OP: OperationId,
58 PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, LongTermKeyBundle>,
59 PKI::State: Clone,
60 DGM: GroupMembership<ID, OP>,
61 KMG: IdentityManager<KMG::State> + PreKeyManager,
62 KMG::State: Clone,
63 ORD: Ordering<ID, OP, DGM>,
64{
65 #[allow(unused)]
69 pub fn init(
70 my_id: ID,
71 my_keys: KMG::State,
72 pki: PKI::State,
73 dgm: DGM::State,
74 orderer: ORD::State,
75 ) -> GroupState<ID, OP, PKI, DGM, KMG, ORD> {
76 GroupState {
77 my_id,
78 dcgka: Dcgka::init(my_id, my_keys, pki, dgm),
79 orderer,
80 secrets: SecretBundle::init(),
81 is_welcomed: false,
82 }
83 }
84
85 pub fn create(
87 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
88 initial_members: Vec<ID>,
89 rng: &Rng,
90 ) -> GroupResult<ORD::Message, ID, OP, PKI, DGM, KMG, ORD> {
91 if y.is_welcomed {
92 return Err(GroupError::GroupAlreadyEstablished);
93 }
94
95 let group_secret = SecretBundle::generate(&y.secrets, rng)?;
97
98 let (y_dcgka_i, pre) = Dcgka::create(y.dcgka, initial_members, &group_secret, rng)?;
100 y.dcgka = y_dcgka_i;
101
102 let (mut y_i, message) = Self::process_local(y, pre, Some(group_secret))?;
103
104 let y_orderer_i = ORD::set_welcome(y_i.orderer, &message).map_err(GroupError::Orderer)?;
106 y_i.orderer = y_orderer_i;
107 y_i.is_welcomed = true;
108
109 Ok((y_i, message))
110 }
111
112 pub fn add(
114 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
115 added: ID,
116 rng: &Rng,
117 ) -> GroupResult<ORD::Message, ID, OP, PKI, DGM, KMG, ORD> {
118 if !y.is_welcomed {
119 return Err(GroupError::GroupNotYetEstablished);
120 }
121
122 if y.my_id == added {
123 return Err(GroupError::NotAddOurselves);
124 }
125
126 let (y_dcgka_i, pre) = Dcgka::add(y.dcgka, added, &y.secrets, rng)?;
128 y.dcgka = y_dcgka_i;
129
130 Self::process_local(y, pre, None)
131 }
132
133 pub fn remove(
135 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
136 removed: ID,
137 rng: &Rng,
138 ) -> GroupResult<ORD::Message, ID, OP, PKI, DGM, KMG, ORD> {
139 if !y.is_welcomed {
140 return Err(GroupError::GroupNotYetEstablished);
141 }
142
143 let group_secret = SecretBundle::generate(&y.secrets, rng)?;
145
146 let (y_dcgka_i, pre) = Dcgka::remove(y.dcgka, removed, &group_secret, rng)?;
148 y.dcgka = y_dcgka_i;
149
150 Self::process_local(y, pre, Some(group_secret))
151 }
152
153 pub fn update(
155 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
156 rng: &Rng,
157 ) -> GroupResult<ORD::Message, ID, OP, PKI, DGM, KMG, ORD> {
158 if !y.is_welcomed {
159 return Err(GroupError::GroupNotYetEstablished);
160 }
161
162 let group_secret = SecretBundle::generate(&y.secrets, rng)?;
164
165 let (y_dcgka_i, pre) = Dcgka::update(y.dcgka, &group_secret, rng)?;
167 y.dcgka = y_dcgka_i;
168
169 Self::process_local(y, pre, Some(group_secret))
170 }
171
172 pub fn receive(
180 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
181 message: &ORD::Message,
182 ) -> GroupResult<Vec<GroupOutput<ID, OP, DGM, ORD>>, ID, OP, PKI, DGM, KMG, ORD> {
183 let message_content = message.content();
184 let mut is_create_or_welcome = false;
185
186 if let GroupMessageContent::Control(ControlMessage::Create {
189 ref initial_members,
190 }) = message_content
191 {
192 if y.is_welcomed {
193 return Err(GroupError::GroupAlreadyEstablished);
194 }
195
196 if initial_members.contains(&y.my_id) {
197 is_create_or_welcome = true;
198 }
199 }
200
201 if let GroupMessageContent::Control(ControlMessage::Add { added }) = message_content
203 && !y.is_welcomed
204 && added == y.my_id
205 {
206 is_create_or_welcome = true;
207 }
208
209 let y_orderer_i = ORD::queue(y.orderer, message).map_err(GroupError::Orderer)?;
210 y.orderer = y_orderer_i;
211
212 if !y.is_welcomed && !is_create_or_welcome {
213 return Ok((y, vec![]));
217 }
218
219 if !y.is_welcomed && is_create_or_welcome {
220 let y_orderer_i = ORD::set_welcome(y.orderer, message).map_err(GroupError::Orderer)?;
223 y.orderer = y_orderer_i;
224 }
225
226 let mut results = Vec::new();
227 let mut y_loop = y;
228
229 let mut control_messages = VecDeque::new();
230 let mut application_messages = VecDeque::new();
231
232 loop {
234 let (y_orderer_next, result) =
235 ORD::next_ready_message(y_loop.orderer).map_err(GroupError::Orderer)?;
236 y_loop.orderer = y_orderer_next;
237
238 let Some(message) = result else {
239 break;
240 };
241
242 match message.content() {
243 GroupMessageContent::Control(_) => {
244 control_messages.push_back(message);
245 }
246 GroupMessageContent::Application { .. } => {
247 application_messages.push_back(message);
248 }
249 }
250 }
251
252 while let Some(message) = control_messages.pop_front() {
254 let (y_next, result) = Self::process_ready(y_loop, &message)?;
255 y_loop = y_next;
256 if let Some(message) = result {
257 results.push(message);
258 }
259 }
260
261 while let Some(message) = application_messages.pop_front() {
263 let (y_next, result) = Self::process_ready(y_loop, &message)?;
264 y_loop = y_next;
265 if let Some(message) = result {
266 results.push(message);
267 }
268 }
269
270 Ok((y_loop, results))
271 }
272
273 pub fn send(
278 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
279 plaintext: &[u8],
280 rng: &Rng,
281 ) -> GroupResult<ORD::Message, ID, OP, PKI, DGM, KMG, ORD> {
282 if !y.is_welcomed {
283 return Err(GroupError::GroupNotYetEstablished);
284 }
285
286 let Some(group_secret) = y.secrets.latest() else {
287 return Err(GroupError::NoGroupSecretAvailable);
288 };
289
290 let secret_id = group_secret.id();
292 let (nonce, ciphertext) = Self::encrypt(group_secret, plaintext, rng)?;
293
294 let (y_orderer_i, message) =
296 ORD::next_application_message(y.orderer, secret_id, nonce, ciphertext)
297 .map_err(GroupError::Orderer)?;
298 y.orderer = y_orderer_i;
299
300 Ok((y, message))
301 }
302
303 pub fn members(
305 y: &GroupState<ID, OP, PKI, DGM, KMG, ORD>,
306 ) -> Result<HashSet<ID>, GroupError<ID, OP, PKI, DGM, KMG, ORD>> {
307 let members = Dcgka::members(&y.dcgka)?;
308 Ok(members)
309 }
310
311 #[allow(unused)]
316 pub fn update_secrets<F>(
317 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
318 update_fn: F,
319 ) -> GroupState<ID, OP, PKI, DGM, KMG, ORD>
320 where
321 F: FnOnce(SecretBundleState) -> SecretBundleState,
322 {
323 y.secrets = update_fn(y.secrets);
324 y
325 }
326
327 fn process_local(
329 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
330 output: OperationOutput<ID, OP, DGM>,
331 group_secret: Option<GroupSecret>,
332 ) -> GroupResult<ORD::Message, ID, OP, PKI, DGM, KMG, ORD> {
333 let (y_orderer_i, message) =
335 ORD::next_control_message(y.orderer, &output.control_message, &output.direct_messages)
336 .map_err(GroupError::Orderer)?;
337 y.orderer = y_orderer_i;
338
339 let (y_dcgka_i, _) = Dcgka::process(
341 y.dcgka,
342 ProcessInput {
343 seq: message.id(),
344 sender: message.sender(),
345 control_message: output.control_message,
346 direct_message: None,
347 },
348 )?;
349 y.dcgka = y_dcgka_i;
350
351 if let Some(group_secret) = group_secret {
353 y.secrets = SecretBundle::insert(y.secrets, group_secret);
354 }
355
356 Ok((y, message))
357 }
358
359 fn process_ready(
361 y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
362 message: &ORD::Message,
363 ) -> GroupResult<Option<GroupOutput<ID, OP, DGM, ORD>>, ID, OP, PKI, DGM, KMG, ORD> {
364 match message.content() {
365 GroupMessageContent::Control(control_message) => {
366 let direct_message = message
367 .direct_messages()
368 .into_iter()
369 .find(|dm| dm.recipient == y.my_id);
370
371 let (mut y_i, output) = Self::process_remote(
372 y,
373 message.id(),
374 message.sender(),
375 control_message,
376 direct_message,
377 )?;
378
379 let we_are_members = Self::members(&y_i)?.contains(&y_i.my_id);
381 if !y_i.is_welcomed && we_are_members {
382 y_i.is_welcomed = true;
383 }
384
385 let is_removed = y_i.is_welcomed && !we_are_members;
387 if is_removed {
388 Ok((y_i, Some(GroupOutput::Removed)))
389 } else {
390 Ok((y_i, output.map(|msg| GroupOutput::Control(msg))))
391 }
392 }
393 GroupMessageContent::Application {
394 group_secret_id,
395 ciphertext,
396 nonce,
397 } => {
398 let (y_i, plaintext) = Self::decrypt(y, nonce, group_secret_id, ciphertext)?;
399 Ok((y_i, Some(GroupOutput::Application { plaintext })))
400 }
401 }
402 }
403
404 fn process_remote(
406 mut y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
407 seq: OP,
408 sender: ID,
409 control_message: ControlMessage<ID>,
410 direct_message: Option<DirectMessage<ID, OP, DGM>>,
411 ) -> GroupResult<Option<ORD::Message>, ID, OP, PKI, DGM, KMG, ORD> {
412 let (y_dcgka_i, output) = Dcgka::process(
413 y.dcgka,
414 ProcessInput {
415 seq,
416 sender,
417 control_message,
418 direct_message,
419 },
420 )?;
421 y.dcgka = y_dcgka_i;
422
423 y.secrets = match output {
425 GroupSecretOutput::Secret(group_secret) => {
426 SecretBundle::insert(y.secrets, group_secret)
427 }
428 GroupSecretOutput::Bundle(secret_bundle_state) => {
429 SecretBundle::extend(y.secrets, secret_bundle_state)
430 }
431 GroupSecretOutput::None => y.secrets,
432 };
433
434 Ok((y, None))
436 }
437
438 fn encrypt(
440 group_secret: &GroupSecret,
441 plaintext: &[u8],
442 rng: &Rng,
443 ) -> Result<(XAeadNonce, Vec<u8>), GroupError<ID, OP, PKI, DGM, KMG, ORD>> {
444 let nonce: XAeadNonce = rng.random_array()?;
445 let ciphertext = encrypt_data(plaintext, group_secret, nonce)?;
446 Ok((nonce, ciphertext))
447 }
448
449 fn decrypt(
451 y: GroupState<ID, OP, PKI, DGM, KMG, ORD>,
452 nonce: XAeadNonce,
453 group_secret_id: GroupSecretId,
454 ciphertext: Vec<u8>,
455 ) -> GroupResult<Vec<u8>, ID, OP, PKI, DGM, KMG, ORD> {
456 let Some(group_secret) = y.secrets.get(&group_secret_id) else {
457 return Err(GroupError::UnknownGroupSecret(hex::encode(group_secret_id)));
458 };
459 let plaintext = decrypt_data(&ciphertext, group_secret, nonce)?;
460 Ok((y, plaintext))
461 }
462}
463
464pub type GroupResult<T, ID, OP, PKI, DGM, KMG, ORD> =
465 Result<(GroupState<ID, OP, PKI, DGM, KMG, ORD>, T), GroupError<ID, OP, PKI, DGM, KMG, ORD>>;
466
467#[derive(Clone, Debug, PartialEq, Eq)]
469pub enum GroupOutput<ID, OP, DGM, ORD>
470where
471 DGM: GroupMembership<ID, OP>,
472 ORD: Ordering<ID, OP, DGM>,
473{
474 Control(ORD::Message),
476
477 Application { plaintext: Vec<u8> },
479
480 Removed,
482}
483
484#[derive(Debug, Error)]
485pub enum GroupError<ID, OP, PKI, DGM, KMG, ORD>
486where
487 PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, LongTermKeyBundle>,
488 DGM: GroupMembership<ID, OP>,
489 KMG: PreKeyManager,
490 ORD: Ordering<ID, OP, DGM>,
491{
492 #[error(transparent)]
493 Rng(#[from] RngError),
494
495 #[error(transparent)]
496 Dcgka(#[from] DcgkaError<ID, OP, PKI, DGM, KMG>),
497
498 #[error(transparent)]
499 Orderer(ORD::Error),
500
501 #[error(transparent)]
502 XAead(#[from] XAeadError),
503
504 #[error(transparent)]
505 GroupSecret(#[from] GroupSecretError),
506
507 #[error("creating or joining a group is not possible, state is already established")]
508 GroupAlreadyEstablished,
509
510 #[error("state is not ready yet, group needs to be created or joined first")]
511 GroupNotYetEstablished,
512
513 #[error("can not add ourselves to the group")]
514 NotAddOurselves,
515
516 #[error("we do not have created or learned about any group secrets yet")]
517 NoGroupSecretAvailable,
518
519 #[error("tried to decrypt message with an unknown group secret: {0}")]
520 UnknownGroupSecret(String),
521}
522
523#[cfg(test)]
524mod tests {
525 use crate::crypto::Rng;
526 use crate::data_scheme::group::GroupOutput;
527 use crate::data_scheme::test_utils::network::init_group_state;
528 use crate::traits::{GroupMembership, Ordering};
529
530 use super::{EncryptionGroup, GroupError};
531
532 pub fn assert_payload<ID, OP, DGM, ORD>(
533 messages: &[GroupOutput<ID, OP, DGM, ORD>],
534 expected_payload: &[u8],
535 ) where
536 DGM: GroupMembership<ID, OP>,
537 ORD: Ordering<ID, OP, DGM>,
538 {
539 let message = messages.first().expect("expected at least one message");
540 if let GroupOutput::Application { plaintext } = message {
541 assert_eq!(
542 plaintext, expected_payload,
543 "expected payload does not match"
544 );
545 } else {
546 panic!("expected application message");
547 }
548 }
549
550 #[test]
551 fn post_compromise_security() {
552 let rng = Rng::from_seed([1; 32]);
553
554 let alice = 0;
555 let bob = 1;
556 let charlie = 2;
557
558 let [y_alice, y_bob, y_charlie] = init_group_state([alice, bob, charlie], &rng);
559
560 let (y_alice, alice_message_0) =
562 EncryptionGroup::create(y_alice, vec![alice, bob, charlie], &rng).unwrap();
563 let (y_bob, _) = EncryptionGroup::receive(y_bob, &alice_message_0).unwrap();
564 let (y_charlie, _) = EncryptionGroup::receive(y_charlie, &alice_message_0).unwrap();
565
566 let (y_alice, alice_message_1) = EncryptionGroup::send(y_alice, b"Da Da Da", &rng).unwrap();
568
569 let (y_bob, bob_output) = EncryptionGroup::receive(y_bob, &alice_message_1).unwrap();
571 assert_payload(&bob_output, b"Da Da Da");
572 let (y_charlie, charlie_output) =
573 EncryptionGroup::receive(y_charlie, &alice_message_1).unwrap();
574 assert_payload(&charlie_output, b"Da Da Da");
575
576 let (y_bob, bob_message_0) = EncryptionGroup::remove(y_bob, charlie, &rng).unwrap();
578 let (y_alice, alice_output) = EncryptionGroup::receive(y_alice, &bob_message_0).unwrap();
579 assert!(alice_output.is_empty());
580
581 assert_eq!(
583 y_alice.secrets.latest().unwrap().id(),
584 y_bob.secrets.latest().unwrap().id()
585 );
586
587 let (y_charlie, charlie_output) =
589 EncryptionGroup::receive(y_charlie, &bob_message_0).unwrap();
590 let GroupOutput::Removed = charlie_output.first().unwrap() else {
591 panic!("expected removed output");
592 };
593
594 let (_y_alice, alice_message_2) =
596 EncryptionGroup::send(y_alice, b"Ich lieb dich nicht / Du liebst mich nicht", &rng)
597 .unwrap();
598
599 let (_y_bob, bob_output) = EncryptionGroup::receive(y_bob, &alice_message_2).unwrap();
601 assert_payload(&bob_output, b"Ich lieb dich nicht / Du liebst mich nicht");
602
603 assert!(matches!(
605 EncryptionGroup::receive(y_charlie, &alice_message_2),
606 Err(GroupError::UnknownGroupSecret(_))
607 ));
608 }
609}