p2panda_encryption/message_scheme/
dcgka.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! A decentralized continuous group key agreement protocol (DCGKA) for p2panda's "message
4//! encryption" scheme with strong forward secrecy and post-compromise security.
5//!
6//! ## Protocol
7//!
8//! DCGKA generates a sequence of update secrets for each group member, which are used as input to
9//! a ratchet to encrypt/decrypt application messages sent by that member. Only group members learn
10//! these update secrets, and fresh secrets are generated every time a user is added or removed, or
11//! a PCS update is requested. The DCGKA protocol ensures that all users observe the same sequence
12//! of update secrets for each group member, regardless of the order in which concurrent messages
13//! are received.
14//!
15//! ```text
16//!                ┌────────────────────────────────────────────────┐
17//!                │                 "Outer" Ratchet                │
18//!   Alice        ├────────────────────────────────────────────────┤
19//!     │          │                                                │
20//!     │          │                         Previous Chain Secret  │
21//!     │          │                               for Bob          │
22//! Delivered      │                                                │
23//!  via 2SM       │                                  │             │
24//!     │          │                                  │             │
25//!     │          │                                  │             │   ┌─────┐
26//!     ▼          │                                  │ ◄───────────┼───│"Ack"│
27//!   ┌────┐       │                                  │             │   └─────┘
28//!   │Seed├───────│──►  HKDF                         │             │
29//!   └────┘       │      │           Bob             │             │
30//!                │      │     ┌─────────────┐       │             │
31//!                │      ├───► │Member Secret├───────┼─► HKDF ─────┼──► Update Secret
32//!                │      │     └─────────────┘       │             │        │
33//!                │      │                           ▼             │        │
34//!                │      │         Charlie          HKDF           │        │
35//!                │      │     ┌─────────────┐       │             │        │
36//!                │      ├───► │Member Secret├─...   │             │        ▼
37//!                │      │     └─────────────┘       │             │ ┌───────────────┐
38//!                │      │                           │             │ │Message Ratchet│
39//!                │      │           ...             │             │ └───────────────┘
40//!                │      │     ┌─────────────┐       │             │
41//!                │      └───► │Member Secret├─...   │             │
42//!                │            └─────────────┘       │             │
43//!                │                                  │             │
44//!                │                                  ▼             │
45//!                │                           New Chain Secret     │
46//!                │                                  │             │
47//!                │                                  │             │
48//!                │                                  ▼  ...        │
49//!                │                                                │
50//!                └────────────────────────────────────────────────┘
51//! ```
52//!
53//! To initiate a PCS update, a user generates a fresh random value called a seed secret, and sends
54//! it to each other group member via a two-party secure channel, like in Sender Keys. On receiving
55//! a seed secret, a group member deterministically derives from it an update secret for the
56//! sender's ratchet, and also an update secret for its own ratchet. Moreover, the recipient
57//! broadcasts an unencrypted acknowledgment to the group indicating that it has applied the
58//! update. Every recipient of the acknowledgment then updates not only the ratchet for the sender
59//! of the original update, but also the ratchet for the sender of the acknowledgment. Thus, after
60//! one seed secret has been disseminated via n - 1 two-party messages, and confirmed via n - 1
61//! broadcast acknowledgments, each group member has derived an update secret from it and updated
62//! their ratchet.
63//!
64//! ## Credits
65//!
66//! The implementation follows the DCGKA protocol specified in the paper: "Key Agreement for
67//! Decentralized Secure Group Messaging with Strong Security Guarantees" by Matthew Weidner,
68//! Martin Kleppmann, Daniel Hugenroth, Alastair R. Beresford (2020).
69//!
70//! <https://eprint.iacr.org/2020/1281.pdf>
71//!
72//! Some adjustments have been made to the version in the paper:
73//!
74//! * Renamed `process` to `process_remote`.
75//! * Added `rng` as an argument to most methods.
76//! * `seq` is taken care of _outside_ of this implementation. Methods return control messages
77//!   which need to be manually assigned a "seq", that is a vector clock, hash, seq_num or similar.
78//! * After calling a group operation "create", "add", "remove" or "update" the user needs to process
79//!   the output themselves by calling `process_local`. This allows a user of the API to correctly
80//!   craft a `seq` for their control messages (see point above).
81//! * Instead of sending the history of control messages in "welcome" messages we send the
82//!   "processed" and potentially garbage-collected CRDT state of DGM. This also allows
83//!   implementations where control messages are encrypted as well.
84//! * We're not recording the "add" control message to the history before sending a "welcome"
85//!   message after adding a member, the receiver of the "welcome" message needs to add themselves.
86use std::collections::{HashMap, HashSet};
87use std::fmt::Display;
88use std::marker::PhantomData;
89
90use serde::{Deserialize, Serialize};
91use thiserror::Error;
92
93use crate::crypto::hkdf::{HkdfError, hkdf};
94use crate::crypto::{Rng, RngError, Secret};
95use crate::key_bundle::OneTimeKeyBundle;
96use crate::traits::{
97    AckedGroupMembership, IdentityHandle, IdentityManager, IdentityRegistry, OperationId,
98    PreKeyManager, PreKeyRegistry,
99};
100use crate::two_party::{TwoParty, TwoPartyError, TwoPartyMessage, TwoPartyState};
101
102/// 256-bit secret "outer" chain- and update key.
103const RATCHET_KEY_SIZE: usize = 32;
104
105/// A decentralized continuous group key agreement protocol (DCGKA) for p2panda's "message
106/// encryption" scheme with strong forward secrecy and post-compromise security.
107pub struct Dcgka<ID, OP, PKI, DGM, KMG> {
108    _marker: PhantomData<(ID, OP, PKI, DGM, KMG)>,
109}
110
111/// Serializable state of DCGKA (for persistence).
112#[derive(Debug, Serialize, Deserialize)]
113#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
114pub struct DcgkaState<ID, OP, PKI, DGM, KMG>
115where
116    ID: IdentityHandle,
117    OP: OperationId,
118    PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
119    DGM: AckedGroupMembership<ID, OP>,
120    KMG: IdentityManager<KMG::State> + PreKeyManager,
121{
122    /// Public Key Infrastructure (PKI). From here we retrieve the identity keys and one-time
123    /// prekey bundles for each member to do 2SM.
124    pub(crate) pki: PKI::State,
125
126    /// Our own key manager state holding the secret parts for our own identity keys and published
127    /// one-time prekey bundles so we can do 2SM.
128    pub(crate) my_keys: KMG::State,
129
130    /// Our id which is used as a unique handle inside this group.
131    pub(crate) my_id: ID,
132
133    /// Randomly generated seed we keep temporarily around when creating or updating a group or
134    /// removing a member.
135    pub(crate) next_seed: Option<NextSeed>,
136
137    /// Handlers for each member to manage the "Two-Party Secure Messaging" (2SM) key-agreement
138    /// protocol as specified in the paper.
139    pub(crate) two_party: HashMap<ID, TwoPartyState<OneTimeKeyBundle>>, // "2sm" in paper
140
141    /// Member secrets are "temporary" secrets we derive after receiving a new seed or adding
142    /// someone. We keep them around until we've received an acknowledgment of that member.
143    ///
144    /// We only store the member secrets, and not the seed secret, so that if the user's private
145    /// state is compromised, the adversary obtains only those member secrets that have not yet
146    /// been used.
147    ///
148    /// The first parameter in the key tuple is the "sender" or "original creator" of the update
149    /// secret. They generated the secret during the given "sequence" (second parameter) for a
150    /// "member" (third parameter).
151    pub(crate) member_secrets: HashMap<(ID, OP, ID), ChainSecret>, // key: "(sender, seq, ID)" in paper
152
153    /// Chain secrets for the "outer" key-agreement ratchet.
154    ///
155    /// Secrets for the "inner" message ratchet are returned to the user as part of the
156    /// "sender_update_secret" and "me_update_secret" fields when invoking a group membership
157    /// operation or processing a control message.
158    pub(crate) ratchet: HashMap<ID, ChainSecret>,
159
160    /// Decentralised group membership (DGM) state.
161    pub(crate) dgm: DGM::State,
162}
163
164impl<ID, OP, PKI, DGM, KMG> Dcgka<ID, OP, PKI, DGM, KMG>
165where
166    ID: IdentityHandle,
167    OP: OperationId,
168    PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
169    DGM: AckedGroupMembership<ID, OP>,
170    KMG: IdentityManager<KMG::State> + PreKeyManager,
171{
172    /// Returns new DCGKA state with our own identity and key managers.
173    ///
174    /// Use this when creating a new group or before accepting an invitation to an existing one.
175    pub fn init(
176        my_id: ID,
177        my_keys: KMG::State,
178        pki: PKI::State,
179        dgm: DGM::State,
180    ) -> DcgkaState<ID, OP, PKI, DGM, KMG> {
181        DcgkaState {
182            pki,
183            my_id,
184            my_keys,
185            next_seed: None,
186            two_party: HashMap::new(),
187            member_secrets: HashMap::new(),
188            ratchet: HashMap::new(),
189            dgm,
190        }
191    }
192
193    /// Handler for when a "remote" control message is received from the network.
194    ///
195    /// It takes the user ID of the message sender, a control message, and a direct message (or
196    /// none if there is no associated direct message).
197    ///
198    /// Control messages are expected to be authenticated and causally ordered.
199    pub fn process_remote(
200        y: DcgkaState<ID, OP, PKI, DGM, KMG>,
201        input: ProcessInput<ID, OP, DGM>,
202        rng: &Rng,
203    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
204        let ProcessInput {
205            sender,
206            seq,
207            direct_message,
208            control_message,
209        } = input;
210        assert_ne!(sender, y.my_id, "do not process own control messages");
211        let (y_i, output) = match control_message {
212            ControlMessage::Create { initial_members } => {
213                Self::process_create(y, sender, seq, initial_members, direct_message, rng)?
214            }
215            ControlMessage::Ack {
216                ack_sender,
217                ack_seq,
218            } => Self::process_ack(y, sender, (&ack_sender, ack_seq), direct_message)?,
219            ControlMessage::Update => Self::process_update(y, sender, seq, direct_message, rng)?,
220            ControlMessage::Remove { removed } => {
221                Self::process_remove(y, sender, seq, &removed, direct_message, rng)?
222            }
223            ControlMessage::Add { added } => {
224                Self::process_add(y, sender, seq, added, direct_message, rng)?
225            }
226            ControlMessage::AddAck {
227                ack_sender,
228                ack_seq,
229            } => Self::process_add_ack(y, sender, (&ack_sender, ack_seq), direct_message)?,
230        };
231        Ok((y_i, output))
232    }
233
234    /// Handler which is _always_ called _after_ every local group membership operation
235    /// ("create", "update", "remove" or "add") which was applied by us.
236    ///
237    /// Invoking a membership operation always returns a "control message" which needs to be
238    /// processed by the application _before_ we can process it locally (and thus finally updating
239    /// our local state). This is because some applications have more complex requirements around
240    /// wrapping the control message around their own message type which will be published on the
241    /// network (for example an append-only log entry with signature, vector clocks, etc.) and we
242    /// can't "guess" the resulting operation id.
243    ///
244    /// Calling this method will _never_ yield another control- or direct message.
245    pub fn process_local(
246        y: DcgkaState<ID, OP, PKI, DGM, KMG>,
247        seq: OP,
248        input: OperationOutput<ID, OP, DGM>,
249        rng: &Rng,
250    ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
251        let my_id = y.my_id;
252        let (y_i, output) = match input.control_message {
253            ControlMessage::Create {
254                ref initial_members,
255            } => Self::process_create(y, my_id, seq, initial_members.clone(), None, rng)?,
256            ControlMessage::Update => Self::process_update(y, my_id, seq, None, rng)?,
257            ControlMessage::Remove { removed } => {
258                Self::process_remove(y, my_id, seq, &removed, None, rng)?
259            }
260            ControlMessage::Add { added } => Self::process_add(y, my_id, seq, added, None, rng)?,
261            _ => panic!(
262                "only call process_local after local create, update, remove or add operations"
263            ),
264        };
265
266        // Processing our local group operations should never yield control or direct messages.
267        assert!(output.control_message.is_none());
268        assert!(output.direct_messages.is_empty());
269
270        Ok((
271            y_i,
272            OperationOutput {
273                control_message: input.control_message,
274                direct_messages: input.direct_messages,
275                me_update_secret: Some(output.sender_update_secret.unwrap()),
276            },
277        ))
278    }
279
280    /// Takes a set of users IDs (including us) and creates a new group with those members.
281    ///
282    /// Note that every member ID needs to be unique for this group.
283    ///
284    /// A group is created in three steps: 1. one user calls "create" and broadcasts a control
285    /// message of type "create" (plus direct messages) to the initial members; 2. each member
286    /// processes that message and broadcasts an "ack" control message; 3. each member processes
287    /// the ack from each other member.
288    pub fn create(
289        y: DcgkaState<ID, OP, PKI, DGM, KMG>,
290        initial_members: Vec<ID>,
291        rng: &Rng,
292    ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
293        // De-duplicate members.
294        let mut initial_members: Vec<ID> =
295            initial_members.into_iter().fold(Vec::new(), |mut acc, id| {
296                if !acc.contains(&id) {
297                    acc.push(id);
298                }
299                acc
300            });
301
302        // Add ourselves if the user hasn't done it yet.
303        if !initial_members.contains(&y.my_id) {
304            initial_members.push(y.my_id);
305        }
306
307        // The "create" function constructs the "create" control message.
308        let control_message = ControlMessage::Create {
309            initial_members: initial_members.clone(),
310        };
311
312        // Generate the set of direct messages to send.
313        let (y_ii, direct_messages) = Self::generate_seed(y, &initial_members, rng)?;
314
315        // It then calls process_create to process the control message for this user (as if it had
316        // received the message) before returning.
317        //
318        // process_create returns a tuple after updating the state including an update secret I; we
319        // use these and ignore the rest.
320        Ok((
321            y_ii,
322            OperationOutput {
323                control_message,
324                direct_messages,
325                me_update_secret: None,
326            },
327        ))
328    }
329
330    /// Called by group members when they receive the "create" message.
331    fn process_create(
332        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
333        sender: ID,
334        seq: OP,
335        initial_members: Vec<ID>,
336        direct_message: Option<DirectMessage<ID, OP, DGM>>,
337        rng: &Rng,
338    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
339        y.dgm =
340            DGM::create(y.my_id, &initial_members).map_err(|err| DcgkaError::DgmOperation(err))?;
341        Self::process_seed(y, &sender, seq, direct_message, rng)
342    }
343
344    /// Called by group members when they receive the "ack" message.
345    ///
346    /// In this function, `ackID` and `ackOP` are the user id and id of the acknowledged message.
347    fn process_ack(
348        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
349        sender: ID,
350        ack: (&ID, OP),
351        direct_message: Option<DirectMessage<ID, OP, DGM>>,
352    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
353        // If the acknowledged message was a group membership operation, we record the
354        // acknowledgment. We do this because the member_view function needs to know which
355        // operations have been acknowledged by which user.
356        //
357        // Acking the message will fail if it's an ack of the user's own removal. Thus we will
358        // refuse to process messages from a user that depend on their own removal.
359        if DGM::is_add(&y.dgm, ack.1) && DGM::is_remove(&y.dgm, ack.1) && sender != y.my_id {
360            // This condition will fail for acks of the creation and of updates.
361            y.dgm = DGM::ack(y.dgm, sender, ack.1).map_err(|err| DcgkaError::DgmOperation(err))?;
362        }
363
364        // Read from γ.memberSecret the appropriate member secret that was previously derived from
365        // the seed secret in the message being acknowledged. The member secret is then deleted for
366        // forward secrecy.
367        let member_secret = y.member_secrets.remove(&(*ack.0, ack.1, sender)); // FS
368
369        let (y_i, sender_member_secret) = match (member_secret, direct_message) {
370            (None, None) => return Ok((y, ProcessOutput::default())),
371            (Some(member_secret), _) => (y, member_secret),
372            (
373                None,
374                Some(DirectMessage {
375                    recipient,
376                    content: DirectMessageContent::Forward { ciphertext },
377                    ..
378                }),
379            ) => {
380                if recipient != y.my_id {
381                    // Direct message was not meant for us.
382                    return Ok((y, ProcessOutput::default()));
383                }
384
385                // The recipient of such a message handles concurrent adds here, where the
386                // forwarded member secret is decrypted and then used to update the ratchet for the
387                // "ack" sender. Note that this forwarding behavior does not violate forward
388                // secrecy: an application message can still only be decrypted by those users who
389                // were group members at the time of sending.
390                let (y_i, plaintext) = Self::decrypt_from(y, &sender, ciphertext)?;
391                (y_i, ChainSecret::try_from_bytes(&plaintext)?)
392            }
393            (None, Some(direct_message)) => {
394                return Err(DcgkaError::UnexpectedDirectMessageType(
395                    DirectMessageType::Forward,
396                    direct_message.message_type(),
397                ));
398            }
399        };
400
401        // We update the ratchet for the sender of the "ack" and return the resulting update
402        // secret.
403        let (y_ii, sender_update_secret) =
404            Self::update_ratchet(y_i, &sender, sender_member_secret)?;
405
406        Ok((
407            y_ii,
408            ProcessOutput {
409                control_message: None,
410                direct_messages: Vec::new(),
411                sender_update_secret: Some(sender_update_secret),
412                me_update_secret: None,
413            },
414        ))
415    }
416
417    /// Generate a new seed for the group. This "refreshes" the group's entropy and should be
418    /// called frequently if no other group operations took place for a while.
419    pub fn update(
420        y: DcgkaState<ID, OP, PKI, DGM, KMG>,
421        rng: &Rng,
422    ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
423        let control_message = ControlMessage::Update;
424
425        let recipient_ids: Vec<ID> = Self::member_view(&y, &y.my_id)?
426            .into_iter()
427            .filter(|member| member != &y.my_id)
428            .collect();
429
430        let (y_i, direct_messages) = Self::generate_seed(y, &recipient_ids, rng)?;
431
432        Ok((
433            y_i,
434            OperationOutput {
435                control_message,
436                direct_messages,
437                me_update_secret: None,
438            },
439        ))
440    }
441
442    /// Called by group members when they receive the "update" control message.
443    fn process_update(
444        y: DcgkaState<ID, OP, PKI, DGM, KMG>,
445        sender: ID,
446        seq: OP,
447        direct_message: Option<DirectMessage<ID, OP, DGM>>,
448        rng: &Rng,
449    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
450        Self::process_seed(y, &sender, seq, direct_message, rng)
451    }
452
453    /// Remove a member from the group.
454    ///
455    /// This generates a new seed for the remaining members for post-compromise security (PCS).
456    pub fn remove(
457        y: DcgkaState<ID, OP, PKI, DGM, KMG>,
458        removed: ID,
459        rng: &Rng,
460    ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
461        let control_message = ControlMessage::Remove { removed };
462
463        let recipient_ids: Vec<ID> = Self::member_view(&y, &y.my_id)?
464            .into_iter()
465            .filter(|member| member != &y.my_id && member != &removed)
466            .collect();
467
468        let (y_i, direct_messages) = Self::generate_seed(y, &recipient_ids, rng)?;
469
470        Ok((
471            y_i,
472            OperationOutput {
473                control_message,
474                direct_messages,
475                me_update_secret: None,
476            },
477        ))
478    }
479
480    /// Called by group members when they receive the "remove" control message.
481    fn process_remove(
482        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
483        sender: ID,
484        seq: OP,
485        removed: &ID,
486        direct_message: Option<DirectMessage<ID, OP, DGM>>,
487        rng: &Rng,
488    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
489        y.dgm = DGM::remove(y.dgm, sender, removed, seq)
490            .map_err(|err| DcgkaError::DgmOperation(err))?;
491        Self::process_seed(y, &sender, seq, direct_message, rng)
492    }
493
494    /// Adds a new group member.
495    ///
496    /// The added group member will receive a direct "welcome" message, every member will process
497    /// an "add" control message.
498    pub fn add(
499        y: DcgkaState<ID, OP, PKI, DGM, KMG>,
500        added: ID,
501        rng: &Rng,
502    ) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
503        // Construct a control message of type "add" to broadcast to the group
504        let control_message = ControlMessage::Add { added };
505
506        // Construct a welcome message that is sent to the new member as a direct message.
507        //
508        // The welcome message contains the current KDF ratchet state of the sender, encrypted
509        // using 2SM, and the history of group membership operations to date (necessary so that the
510        // new member can evaluate the DGM function).
511        let (y_i, ciphertext) = {
512            let chain_secret_bytes = y
513                .ratchet
514                .get(&y.my_id)
515                .ok_or(DcgkaError::MissingRatchetSecret)?
516                .as_bytes()
517                .to_vec();
518            Self::encrypt_to(y, &added, &chain_secret_bytes, rng)?
519        };
520        let direct_message = DirectMessage {
521            recipient: added,
522            content: DirectMessageContent::Welcome {
523                ciphertext,
524                history: {
525                    // Send current DGM state to added user. The benefit of doing it like that is
526                    // that we can encrypt group operations as well, otherwise we would need to ask
527                    // the added user to process all previous (unencrypted) group operations
528                    // before.
529                    y_i.dgm.clone()
530                },
531            },
532        };
533
534        Ok((
535            y_i,
536            OperationOutput {
537                control_message,
538                direct_messages: vec![direct_message],
539                me_update_secret: None,
540            },
541        ))
542    }
543
544    /// Called by both the sender and each recipient of an "add" control message, including the new
545    /// group member.
546    ///
547    /// ## Concurrency
548    ///
549    /// Another scenario that needs to be handled is when two users are concurrently added to the
550    /// group. For example, in a group consisting initially of {A, B}, say A adds C to the group,
551    /// while concurrently B adds D. User C first processes its own addition and welcome message,
552    /// and then processes B's addition of D. However, since C was not a group member at the time B
553    /// sent its "add" message, C does not yet have B's ratchet state, so C cannot derive an update
554    /// secret for B's "add" message.
555    ///
556    /// When B finds out about the fact that A has added C, B sends C its ratchet state as usual,
557    /// so C can initialize its copy of B's ratchet as before. Similarly, when D finds out about
558    /// the fact that A has added C, D sends its ratchet state to C along with the "add-ack"
559    /// message. The existing logic therefore handles the concurrent additions: after all acks have
560    /// been delivered, C and D have both initialized their copies of all four ratchets, and so
561    /// they are able to decrypt application messages that any group member sent after processing
562    /// their addition.
563    fn process_add(
564        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
565        sender: ID,
566        seq: OP,
567        added: ID,
568        direct_message: Option<DirectMessage<ID, OP, DGM>>,
569        rng: &Rng,
570    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
571        // Local user is the new group member being added. Call "process_welcome" instead and
572        // return early.
573        if added == y.my_id {
574            let Some(DirectMessage {
575                recipient,
576                content:
577                    DirectMessageContent::Welcome {
578                        ciphertext,
579                        history,
580                    },
581                ..
582            }) = direct_message
583            else {
584                return match direct_message {
585                    Some(direct_message) => Err(DcgkaError::UnexpectedDirectMessageType(
586                        DirectMessageType::Welcome,
587                        direct_message.message_type(),
588                    )),
589                    None => Err(DcgkaError::MissingDirectMessage(DirectMessageType::Welcome)),
590                };
591            };
592
593            if recipient != y.my_id {
594                return Err(DcgkaError::NotOurDirectMessage(y.my_id, recipient));
595            }
596
597            return Self::process_welcome(y, sender, seq, history, ciphertext);
598        }
599
600        // Otherwise extend γ.history with the add operation.
601        y.dgm = DGM::add(y.dgm, sender, added, seq).map_err(|err| DcgkaError::DgmOperation(err))?;
602
603        // Determine whether the local user was already a group member at the time the "add"
604        // message was sent. This is true in the common case, but may be false if multiple users
605        // were added concurrently.
606        let is_concurrent = !Self::member_view(&y, &sender)?
607            .iter()
608            .any(|member| member == &y.my_id);
609
610        let (y_ii, sender_update_secret) = if is_concurrent {
611            (y, None)
612        } else {
613            // We twice update the ratchet for the sender of the "add" message. In both calls to
614            // update_ratchet, rather than using a random seed secret, the ratchet input is a
615            // constant string ("welcome" and "add" respectively). It is sufficient to use
616            // constants here because all existing group members are allowed to know the next
617            // update secrets following the add operation.
618
619            // 1. The value returned by the first ratchet update is stored in γ.memberSecret as the
620            //    added user's first member secret;
621            let (mut y_i, sender_member_secret) =
622                Self::update_ratchet(y, &sender, ChainSecret::from_welcome())?;
623            y_i.member_secrets.insert(
624                (sender, seq, added),
625                ChainSecret::from(sender_member_secret),
626            );
627            // 2. The result of the second ratchet update becomes Isender, the update secret for
628            //    the sender of the "add".
629            let (y_ii, sender_update_secret) =
630                Self::update_ratchet(y_i, &sender, ChainSecret::from_add())?;
631            (y_ii, Some(sender_update_secret))
632        };
633
634        // If the local user is the sender, we return that update secret.
635        if sender == y_ii.my_id {
636            return Ok((
637                y_ii,
638                ProcessOutput {
639                    control_message: None,
640                    direct_messages: Vec::new(),
641                    sender_update_secret,
642                    me_update_secret: None,
643                },
644            ));
645        }
646
647        // Otherwise, we need to acknowledge the "add" message, so we construct a control message
648        // of type "add-ack" to broadcast (note that add has its own acknowledgment type, whereas
649        // create, update and remove all use "ack").
650        let control = ControlMessage::AddAck {
651            ack_sender: sender,
652            ack_seq: seq,
653        };
654
655        // We then use 2SM to encrypt our current ratchet state to send as a direct message to the
656        // added user, so that they can decrypt subsequent messages we send.
657        let (y_iii, ciphertext) = {
658            let chain_secret_bytes = y_ii
659                .ratchet
660                .get(&y_ii.my_id)
661                .ok_or(DcgkaError::MissingRatchetSecret)?
662                .as_bytes()
663                .to_vec();
664            Self::encrypt_to(y_ii, &added, &chain_secret_bytes, rng)?
665        };
666        let forward = DirectMessage {
667            recipient: added,
668            content: DirectMessageContent::Forward { ciphertext },
669        };
670
671        // Finally, we call process_add_ack to compute the local user's update secret Ime, and
672        // return it with Isender.
673        let (y_iv, output) = {
674            let my_id = y_iii.my_id;
675            Self::process_add_ack(y_iii, my_id, (&sender, seq), None)?
676        };
677        let me_update_secret = output.sender_update_secret;
678
679        Ok((
680            y_iv,
681            ProcessOutput {
682                control_message: Some(control),
683                direct_messages: vec![forward],
684                sender_update_secret,
685                me_update_secret,
686            },
687        ))
688    }
689
690    /// Called by both the sender and each recipient of an "add-ack" message, including the new
691    /// group member.
692    fn process_add_ack(
693        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
694        sender: ID,
695        ack: (&ID, OP),
696        direct_message: Option<DirectMessage<ID, OP, DGM>>,
697    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
698        // Add the acknowledgment to γ.history, like in process_ack.
699        y.dgm = DGM::ack(y.dgm, sender, ack.1).map_err(|err| DcgkaError::DgmOperation(err))?;
700
701        // If the current user is the new group member, the "add_ack" message is accompanied by the
702        // direct message that we constructed in process_add; this direct message dmsg contains the
703        // encrypted ratchet state of the sender of the "add_ack", so we decrypt it.
704        let y_i = if let Some(direct_message) = direct_message {
705            if let DirectMessage {
706                recipient,
707                content: DirectMessageContent::Forward { ciphertext },
708                ..
709            } = direct_message
710            {
711                if recipient != y.my_id {
712                    return Err(DcgkaError::NotOurDirectMessage(y.my_id, recipient));
713                }
714
715                let (mut y_i, plaintext) = Self::decrypt_from(y, &sender, ciphertext)?;
716                let chain_secret = ChainSecret::try_from_bytes(&plaintext)?;
717                y_i.ratchet.insert(sender, chain_secret);
718                y_i
719            } else {
720                return Err(DcgkaError::UnexpectedDirectMessageType(
721                    DirectMessageType::Forward,
722                    direct_message.message_type(),
723                ));
724            }
725        } else {
726            y
727        };
728
729        // Check if the local user was already a group member at the time the "add_ack" was sent
730        // (which may not be the case when there are concurrent additions).
731        let is_concurrent = !Self::member_view(&y_i, &sender)?
732            .iter()
733            .any(|member| member == &y_i.my_id);
734
735        if !is_concurrent {
736            // If so, we compute a new update secret I for the sender of the "add_ack" by calling
737            // update_ratchet with the constant string "add". In the case of the new member, the
738            // ratchet state was just previously initialized. This ratchet update allows all group
739            // members, including the new one, to derive each member's update secret for the add
740            // operation, but it prevents the new group member from obtaining any update secret
741            // from before they were added.
742            let (y_ii, sender_update_secret) =
743                Self::update_ratchet(y_i, &sender, ChainSecret::from_add())?;
744            return Ok((
745                y_ii,
746                ProcessOutput {
747                    control_message: None,
748                    direct_messages: Vec::new(),
749                    sender_update_secret: Some(sender_update_secret),
750                    me_update_secret: None,
751                },
752            ));
753        }
754
755        Ok((y_i, ProcessOutput::default()))
756    }
757
758    /// Second function called by a newly added group member (the first is the call to init that
759    /// sets up their state).
760    fn process_welcome(
761        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
762        sender: ID,
763        seq: OP,
764        history: DGM::State,
765        ciphertext: TwoPartyMessage,
766    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
767        // Adding user's copy of γ.history sent in their welcome message, which is used to
768        // initialize the added user's history.
769        //
770        // Note that we might receive multiple welcome messages when peers added us concurrently. A
771        // DGM implementation might want to account for this.
772        y.dgm = DGM::from_welcome(y.dgm, history).map_err(|err| DcgkaError::DgmOperation(err))?;
773
774        // Add ourselves.
775        y.dgm =
776            DGM::add(y.dgm, sender, y.my_id, seq).map_err(|err| DcgkaError::DgmOperation(err))?;
777
778        // Ciphertext of the adding user's ratchet state, which we decrypt.
779        let y_i = {
780            let (mut y_i, plaintext) = Self::decrypt_from(y, &sender, ciphertext)?;
781            let chain_secret = ChainSecret::try_from_bytes(&plaintext)?;
782            y_i.ratchet.insert(sender, chain_secret);
783            y_i
784        };
785
786        // After γ.ratchet[sender] is initialized, we can call update_ratchet twice with the
787        // constant strings "welcome" and "add": exactly the same ratchet operations as every other
788        // group member performs in process_add.
789        //
790        // As before, the result of the first update_ratchet call becomes the first member secret
791        // for the added user, and the second returns Isender, the update secret for the sender of
792        // the add operation.
793        let y_ii = {
794            let (mut y_ii, member_secret) =
795                Self::update_ratchet(y_i, &sender, ChainSecret::from_welcome())?;
796            y_ii.member_secrets
797                .insert((sender, seq, y_ii.my_id), ChainSecret::from(member_secret));
798            y_ii
799        };
800
801        let (y_iii, sender_update_secret) =
802            Self::update_ratchet(y_ii, &sender, ChainSecret::from_add())?;
803
804        // Finally, the new group member constructs an "ack" control message (not "add_ack") to
805        // broadcast and calls process_ack to compute their first update secret Ime.
806        let control = ControlMessage::Ack {
807            ack_sender: sender,
808            ack_seq: seq,
809        };
810
811        // process_ack works as described previously, reading from γ.memberSecret the member secret
812        // we just generated, and passing it to update_ratchet.
813        //
814        // The previous ratchet state for the new member is the empty string ε, as set up by init,
815        // so this step initializes the new member's ratchet. Every other group member, on
816        // receiving the new member's "ack", will initialize their copy of the new member's ratchet
817        // in the same way. By the end of process_welcome, the new group member has obtained update
818        // secrets for themselves and the user who added them. They then use those secrets to
819        // initialize the ratchets for application messages, allowing them to send messages and
820        // decrypt messages from the user who added them. The ratchets for other group members are
821        // initialized by process_add_ack.
822        let (y_iv, output) = {
823            let my_id = y_iii.my_id;
824            Self::process_ack(y_iii, my_id, (&sender, seq), None)?
825        };
826        let me_update_secret = output.sender_update_secret;
827
828        Ok((
829            y_iv,
830            ProcessOutput {
831                control_message: Some(control),
832                direct_messages: Vec::new(),
833                sender_update_secret: Some(sender_update_secret),
834                me_update_secret: Some(
835                    me_update_secret.expect("sender update secret from process_ack"),
836                ),
837            },
838        ))
839    }
840
841    /// Generates a seed secret using a secure source of random bits, then calls `encrypt_to` to
842    /// encrypt it for each other group member using the 2SM protocol. It returns the updated
843    /// protocol state and the set of direct messages to send.
844    fn generate_seed(
845        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
846        recipients: &[ID],
847        rng: &Rng,
848    ) -> GenerateSeedResult<ID, OP, PKI, DGM, KMG> {
849        let mut direct_messages: Vec<DirectMessage<ID, OP, DGM>> =
850            Vec::with_capacity(recipients.len());
851
852        // Generate next seed.
853        let next_seed_bytes = rng.random_array()?;
854        y.next_seed = Some(NextSeed::from_bytes(next_seed_bytes));
855
856        let y_i = {
857            let mut y_loop = y;
858            for recipient in recipients {
859                // Skip ourselves.
860                if recipient == &y_loop.my_id {
861                    continue;
862                }
863
864                // Encrypt to every recipient.
865                let (y_next, ciphertext) =
866                    Self::encrypt_to(y_loop, recipient, &next_seed_bytes, rng)?;
867                y_loop = y_next;
868
869                direct_messages.push(DirectMessage {
870                    recipient: *recipient,
871                    content: DirectMessageContent::TwoParty { ciphertext },
872                });
873            }
874            y_loop
875        };
876
877        Ok((y_i, direct_messages))
878    }
879
880    /// Handle the next seed for the group, either generated locally by us or received as an
881    /// encrypted message by another member.
882    ///
883    /// We use the seed to derive independent member secrets for each group member by combining
884    /// the seed secret and each user ID using HKDF.
885    fn process_seed(
886        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
887        sender: &ID,
888        seq: OP,
889        direct_message: Option<DirectMessage<ID, OP, DGM>>,
890        rng: &Rng,
891    ) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
892        // Determine the set of users who were group members at the time the control message was
893        // sent, and hence the set of recipients of the message.
894        let recipients: Vec<ID> = Self::member_view(&y, sender)?
895            .into_iter()
896            .filter(|member| member != sender)
897            .collect();
898
899        // Attempt to obtain the seed secret.
900        let (mut y_i, next_seed) = if sender == &y.my_id {
901            // 1. If the control message was sent by the local user, the last call to generate_seed
902            //    placed the seed secret in γ.nextSeed, so we read that variable and then delete
903            //    its contents.
904            let next_seed = y.next_seed.take().expect("seed was generated before"); // FS
905            (y, next_seed)
906        } else if recipients.iter().any(|member| member == &y.my_id) {
907            // 2. If the control message was sent by another user, and the local user is one of its
908            //    recipients, we use decrypt_from to decrypt the direct message containing the seed
909            //    secret.
910            let Some(DirectMessage {
911                recipient,
912                content: DirectMessageContent::TwoParty { ciphertext },
913                ..
914            }) = direct_message
915            else {
916                return match direct_message {
917                    Some(direct_message) => Err(DcgkaError::UnexpectedDirectMessageType(
918                        DirectMessageType::TwoParty,
919                        direct_message.message_type(),
920                    )),
921                    None => Err(DcgkaError::MissingDirectMessage(
922                        DirectMessageType::TwoParty,
923                    )),
924                };
925            };
926
927            if recipient != y.my_id {
928                return Err(DcgkaError::NotOurDirectMessage(y.my_id, recipient));
929            }
930
931            let (y_i, plaintext) = Self::decrypt_from(y, sender, ciphertext)?;
932            (y_i, NextSeed::try_from_bytes(&plaintext)?)
933        } else {
934            // 3. Otherwise we return an "ack" message without deriving an update secret. Case 3
935            //    may occur when a group member is added concurrently to other messages.
936            let control = ControlMessage::Ack {
937                ack_sender: *sender,
938                ack_seq: seq,
939            };
940            return Ok((
941                y,
942                ProcessOutput {
943                    control_message: Some(control),
944                    direct_messages: Vec::new(),
945                    sender_update_secret: None,
946                    me_update_secret: None,
947                },
948            ));
949        };
950
951        // Derive independent member secrets for each group member from the seed secret by
952        // combining the seed secret and each user ID using HKDF. The secret for the sender of the
953        // message is stored in senderSecret, and those for the other group members are stored in
954        // γ.memberSecret; the latter are used when we receive acknowledgments from those users.
955        for recipient in &recipients {
956            let recipient_identity_key = PKI::identity_key(&y_i.pki, recipient)
957                .map_err(|err| DcgkaError::IdentityRegistry(err))?
958                .ok_or(DcgkaError::MissingIdentityKey(*recipient))?;
959            let recipient_member_secret: [u8; RATCHET_KEY_SIZE] = hkdf(
960                b"update",
961                &{
962                    let mut ikm = Vec::with_capacity(RATCHET_KEY_SIZE * 2);
963                    ikm.extend_from_slice(next_seed.as_bytes());
964                    ikm.extend_from_slice(recipient_identity_key.as_bytes());
965                    ikm
966                },
967                None,
968            )?;
969            y_i.member_secrets.insert(
970                (*sender, seq, *recipient),
971                ChainSecret::from_bytes(recipient_member_secret),
972            );
973        }
974
975        // The sender's member secret is used immediately to update their KDF ratchet and compute
976        // their update secret Isender, using update_ratchet.
977        let (y_ii, sender_update_secret) = {
978            let sender_identity_key = PKI::identity_key(&y_i.pki, sender)
979                .map_err(|err| DcgkaError::IdentityRegistry(err))?
980                .ok_or(DcgkaError::MissingIdentityKey(*sender))?;
981            let sender_member_secret: [u8; RATCHET_KEY_SIZE] = hkdf(
982                b"update",
983                &{
984                    let mut ikm = Vec::with_capacity(RATCHET_KEY_SIZE * 2);
985                    ikm.extend_from_slice(next_seed.as_bytes());
986                    ikm.extend_from_slice(sender_identity_key.as_bytes());
987                    ikm
988                },
989                None,
990            )?;
991            Self::update_ratchet(y_i, sender, ChainSecret::from_bytes(sender_member_secret))?
992        };
993
994        // We only store the member secrets, and not the seed secret, so that if the user's private
995        // state is compromised, the adversary obtains only those member secrets that have not yet
996        // been used.
997        drop(next_seed); // FS for "next_seed".
998
999        // If the local user is the sender of the control message, we are now finished and return
1000        // the update secret.
1001        if sender == &y_ii.my_id {
1002            return Ok((
1003                y_ii,
1004                ProcessOutput {
1005                    control_message: None,
1006                    direct_messages: Vec::new(),
1007                    sender_update_secret: Some(sender_update_secret),
1008                    me_update_secret: None,
1009                },
1010            ));
1011        }
1012
1013        // If we received the seed secret from another user, we construct an "ack" control message
1014        // to broadcast, including the sender ID and sequence number of the message we are
1015        // acknowledging.
1016        let control = ControlMessage::Ack {
1017            ack_sender: *sender,
1018            ack_seq: seq,
1019        };
1020
1021        // Care is required when an add operation occurs concurrently with an update, remove, or
1022        // another add operation. We want all intended recipients to learn every update secret,
1023        // since otherwise some users would not be able to decrypt some messages, despite being a
1024        // group member.
1025        //
1026        // For example, consider a group with members {A, B, C}, and say A performs an update while
1027        // concurrently C adds D to the group. When A distributes a new seed secret through
1028        // 2SM-encrypted direct messages, D will not be a recipient of one of those direct
1029        // messages, since A did not know about D's addition at the time of sending. D cannot
1030        // derive any of the member secrets for this update. When B updates its KDF ratchet using
1031        // A's seed secret, it will compute an update secret that D does not know, and D will not
1032        // be able to decrypt B's subsequent application messages.
1033        //
1034        // In this example, B may receive the add and the update in either order. If B processes
1035        // A's update first, the seed secret from A is already incorporated into B's ratchet state
1036        // at the time of adding D; since B sends this ratchet state to D along with its "add-ack"
1037        // message, no further action is needed. On the other hand, if B processes the addition of
1038        // D first, then when B subsequently processes A's update, B must take the member secret it
1039        // derives from A's seed secret and forward it to D, so that D can compute B's update
1040        // secret for A's update.
1041        //
1042        // Recall that first we set recipients to be the set of group members at the time the
1043        // update/remove was sent, except for the sender. We then compute the current set of
1044        // members according to the local node. The set difference thus computes the set of users
1045        // whose additions have been processed by the local user, but who were not yet known to
1046        // the sender of the update.
1047        //
1048        // If there are any such users, we construct a direct message to each of them. One of the
1049        // member secrets we computed before is the member secret for the local user. We
1050        // 2SM-encrypt that member secret for each of the users who need it. This set forward is
1051        // sent as direct messages along with the "ack".
1052        let (y_iii, forward_messages) = {
1053            let members: Vec<ID> = Self::member_view(&y_ii, &y_ii.my_id)?
1054                .into_iter()
1055                .filter(|member| member != sender && !recipients.contains(member))
1056                .collect();
1057            let mut forward_messages = Vec::with_capacity(members.len());
1058
1059            let mut y_loop = y_ii;
1060            for member in members {
1061                let member_secret_bytes = y_loop
1062                    .member_secrets
1063                    .get(&(*sender, seq, y_loop.my_id))
1064                    .ok_or(DcgkaError::MissingMemberSecret(*sender, seq))?
1065                    .as_bytes()
1066                    .to_vec();
1067                let (y_next, ciphertext) =
1068                    Self::encrypt_to(y_loop, &member, &member_secret_bytes, rng)?;
1069                y_loop = y_next;
1070                forward_messages.push(DirectMessage {
1071                    recipient: member,
1072                    content: DirectMessageContent::Forward { ciphertext },
1073                });
1074            }
1075
1076            (y_loop, forward_messages)
1077        };
1078
1079        // Compute an update secret Ime for the local user.
1080        let (y_iv, output) = {
1081            let my_id = y_iii.my_id;
1082            Self::process_ack(y_iii, my_id, (sender, seq), None)?
1083        };
1084        let me_update_secret = output.sender_update_secret;
1085
1086        Ok((
1087            y_iv,
1088            ProcessOutput {
1089                control_message: Some(control),
1090                direct_messages: forward_messages,
1091                sender_update_secret: Some(sender_update_secret),
1092                me_update_secret,
1093            },
1094        ))
1095    }
1096
1097    /// Uses 2SM to encrypt a direct message for another group member. The first time a message is
1098    /// encrypted to a particular recipient ID, the 2SM protocol state is initialized and stored in
1099    /// γ.2sm[ID]. We then use 2SM-Send to encrypt the message, and store the updated protocol
1100    /// state in γ.
1101    fn encrypt_to(
1102        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
1103        recipient: &ID,
1104        plaintext: &[u8],
1105        rng: &Rng,
1106    ) -> DcgkaResult<ID, OP, PKI, DGM, KMG, TwoPartyMessage> {
1107        let y_2sm = match y.two_party.remove(recipient) {
1108            Some(y_2sm) => y_2sm,
1109            None => {
1110                let (pki_i, prekey_bundle) = PKI::key_bundle(y.pki, recipient)
1111                    .map_err(|err| DcgkaError::PreKeyRegistry(err))?;
1112                y.pki = pki_i;
1113                let prekey_bundle = prekey_bundle.ok_or(DcgkaError::MissingPreKeys(*recipient))?;
1114                TwoParty::<KMG, OneTimeKeyBundle>::init(prekey_bundle)
1115            }
1116        };
1117        let (y_2sm_i, ciphertext) =
1118            TwoParty::<KMG, OneTimeKeyBundle>::send(y_2sm, &y.my_keys, plaintext, rng)?;
1119        y.two_party.insert(*recipient, y_2sm_i);
1120        Ok((y, ciphertext))
1121    }
1122
1123    /// Is the reverse of encrypt_to. It similarly initializes the protocol state on first use, and
1124    /// then uses 2SM-Receive to decrypt the ciphertext, with the protocol state stored in
1125    /// γ.2sm[ID].
1126    fn decrypt_from(
1127        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
1128        sender: &ID,
1129        ciphertext: TwoPartyMessage,
1130    ) -> DcgkaResult<ID, OP, PKI, DGM, KMG, Vec<u8>> {
1131        let y_2sm = match y.two_party.remove(sender) {
1132            Some(y_2sm) => y_2sm,
1133            None => {
1134                let (pki_i, prekey_bundle) = PKI::key_bundle(y.pki, sender)
1135                    .map_err(|err| DcgkaError::PreKeyRegistry(err))?;
1136                y.pki = pki_i;
1137                let prekey_bundle = prekey_bundle.ok_or(DcgkaError::MissingPreKeys(*sender))?;
1138                TwoParty::<KMG, OneTimeKeyBundle>::init(prekey_bundle)
1139            }
1140        };
1141        let (y_2sm_i, y_my_keys_i, plaintext) =
1142            TwoParty::<KMG, OneTimeKeyBundle>::receive(y_2sm, y.my_keys, ciphertext)?;
1143        y.my_keys = y_my_keys_i;
1144        y.two_party.insert(*sender, y_2sm_i);
1145        Ok((y, plaintext))
1146    }
1147
1148    /// Generates the next update secret for group member ID. It implements the outer KDF of the
1149    /// ratchet. The ratchet state is stored in γ.ratchet[ID]; we use an HMAC-based key derivation
1150    /// function HKDF to combine the ratchet state with an input, producing an update secret and a
1151    /// new ratchet state.
1152    fn update_ratchet(
1153        mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
1154        member: &ID,
1155        member_secret: ChainSecret,
1156    ) -> DcgkaResult<ID, OP, PKI, DGM, KMG, UpdateSecret> {
1157        let identity_key = PKI::identity_key(&y.pki, member)
1158            .map_err(|err| DcgkaError::IdentityRegistry(err))?
1159            .ok_or(DcgkaError::MissingIdentityKey(*member))?;
1160        // Mix in the previous "outer" ratchet chain secret if it exists.
1161        let previous_outer_ratchet_key = y.ratchet.get(member);
1162        let update_secret: [u8; RATCHET_KEY_SIZE] = hkdf(
1163            b"update",
1164            &{
1165                let mut ikm = Vec::with_capacity(RATCHET_KEY_SIZE * 3);
1166                if let Some(previous_outer_ratchet_key) = previous_outer_ratchet_key {
1167                    ikm.extend_from_slice(previous_outer_ratchet_key.as_bytes());
1168                }
1169                ikm.extend_from_slice(member_secret.as_bytes());
1170                ikm.extend_from_slice(identity_key.as_bytes());
1171                ikm
1172            },
1173            None,
1174        )?;
1175        let next_outer_ratchet_key: [u8; RATCHET_KEY_SIZE] = hkdf(
1176            b"chain",
1177            &{
1178                let mut ikm = Vec::with_capacity(RATCHET_KEY_SIZE * 3);
1179                if let Some(previous_outer_ratchet_key) = previous_outer_ratchet_key {
1180                    ikm.extend_from_slice(previous_outer_ratchet_key.as_bytes());
1181                }
1182                ikm.extend_from_slice(member_secret.as_bytes());
1183                ikm.extend_from_slice(identity_key.as_bytes());
1184                ikm
1185            },
1186            None,
1187        )?;
1188        drop(member_secret); // FS for `member_secret`
1189        y.ratchet
1190            .insert(*member, ChainSecret::from_bytes(next_outer_ratchet_key));
1191        Ok((y, UpdateSecret::from_bytes(update_secret)))
1192    }
1193
1194    /// Computes the set of group members at the time of the most recent control message sent by
1195    /// user ID. It works by filtering the set of group membership operations to contain only those
1196    /// seen by ID, and then invoking the Decentralised Group Membership function DGM to compute
1197    /// the group membership.
1198    pub fn member_view(
1199        y: &DcgkaState<ID, OP, PKI, DGM, KMG>,
1200        viewer: &ID,
1201    ) -> Result<HashSet<ID>, DcgkaError<ID, OP, PKI, DGM, KMG>> {
1202        let members =
1203            DGM::members_view(&y.dgm, viewer).map_err(|err| DcgkaError::MembersView(err))?;
1204        Ok(members)
1205    }
1206}
1207
1208pub type GenerateSeedResult<ID, OP, PKI, DGM, KMG> = Result<
1209    (
1210        DcgkaState<ID, OP, PKI, DGM, KMG>,
1211        Vec<DirectMessage<ID, OP, DGM>>,
1212    ),
1213    DcgkaError<ID, OP, PKI, DGM, KMG>,
1214>;
1215
1216pub type DcgkaResult<ID, OP, PKI, DGM, KMG, T> =
1217    Result<(DcgkaState<ID, OP, PKI, DGM, KMG>, T), DcgkaError<ID, OP, PKI, DGM, KMG>>;
1218
1219pub type DcgkaProcessResult<ID, OP, PKI, DGM, KMG> =
1220    DcgkaResult<ID, OP, PKI, DGM, KMG, ProcessOutput<ID, OP, DGM>>;
1221
1222pub type DcgkaOperationResult<ID, OP, PKI, DGM, KMG> =
1223    DcgkaResult<ID, OP, PKI, DGM, KMG, OperationOutput<ID, OP, DGM>>;
1224
1225/// Message that should be broadcast to the group.
1226///
1227/// The control message must be distributed to the other group members through Authenticated Causal
1228/// Broadcast, calling the process function on the recipient when they are delivered.
1229#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1230pub enum ControlMessage<ID, OP> {
1231    Create { initial_members: Vec<ID> },
1232    Ack { ack_sender: ID, ack_seq: OP },
1233    Update,
1234    Remove { removed: ID },
1235    Add { added: ID },
1236    AddAck { ack_sender: ID, ack_seq: OP },
1237}
1238
1239impl<ID, OP> Display for ControlMessage<ID, OP> {
1240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1241        write!(
1242            f,
1243            "{}",
1244            match self {
1245                ControlMessage::Create { .. } => "create",
1246                ControlMessage::Ack { .. } => "ack",
1247                ControlMessage::Update => "update",
1248                ControlMessage::Remove { .. } => "remove",
1249                ControlMessage::Add { .. } => "add",
1250                ControlMessage::AddAck { .. } => "add_ack",
1251            }
1252        )
1253    }
1254}
1255
1256/// Arguments required to process a group operation received from another member.
1257#[derive(Clone, Debug)]
1258pub struct ProcessInput<ID, OP, DGM>
1259where
1260    DGM: AckedGroupMembership<ID, OP>,
1261{
1262    /// Sequence number, which consecutively numbers successive control messages from the same
1263    /// sender.
1264    pub seq: OP,
1265
1266    /// Author of this message.
1267    pub sender: ID,
1268
1269    /// Message received from this author.
1270    pub control_message: ControlMessage<ID, OP>,
1271
1272    /// Optional direct message for us.
1273    ///
1274    /// Applications need to filter the direct message for the correct recipient before passing it
1275    /// as an input. There can always only be max. 1 direct message per recipient.
1276    pub direct_message: Option<DirectMessage<ID, OP, DGM>>,
1277}
1278
1279/// Calling "process" returns a 4-tuple `(control, dmsgs, Is, Ir)`, where `Is` is an update secret
1280/// for the sender of the message being processed, `Ir` is an update secret for the recipient.
1281#[derive(Debug)]
1282pub struct ProcessOutput<ID, OP, DGM>
1283where
1284    DGM: AckedGroupMembership<ID, OP>,
1285{
1286    /// Control message that should be broadcast to the group.
1287    pub control_message: Option<ControlMessage<ID, OP>>,
1288
1289    /// Direct messages to be sent to specific members.
1290    pub direct_messages: Vec<DirectMessage<ID, OP, DGM>>,
1291
1292    /// Our next update key for the message ratchet for incoming messages from the sender.
1293    pub sender_update_secret: Option<UpdateSecret>,
1294
1295    /// Our next update key for the message ratchet for outgoing messages to the group.
1296    pub me_update_secret: Option<UpdateSecret>,
1297}
1298
1299impl<ID, OP, DGM> Default for ProcessOutput<ID, OP, DGM>
1300where
1301    DGM: AckedGroupMembership<ID, OP>,
1302{
1303    fn default() -> Self {
1304        Self {
1305            control_message: None,
1306            direct_messages: Vec::new(),
1307            sender_update_secret: None,
1308            me_update_secret: None,
1309        }
1310    }
1311}
1312
1313/// Calling "create", "add", "remove" and "update" return a tuple of three variables (`control`,
1314/// `dmsgs` and `I`) after changing the state for the current user.
1315///
1316/// `control` is a control message that should be broadcast to the group, `dmsgs` is a set of `(u,
1317/// m)` pairs where `m` is a direct message that should be sent to user `u`, and `I` is a new
1318/// update secret for the current user.
1319pub struct OperationOutput<ID, OP, DGM>
1320where
1321    DGM: AckedGroupMembership<ID, OP>,
1322{
1323    /// Control message that should be broadcast to the group.
1324    pub control_message: ControlMessage<ID, OP>,
1325
1326    /// Set of messages directly to be sent to specific users.
1327    pub direct_messages: Vec<DirectMessage<ID, OP, DGM>>,
1328
1329    /// Our next update key for the message ratchet for outgoing messages to the group.
1330    pub me_update_secret: Option<UpdateSecret>,
1331}
1332
1333/// Direct message that should be sent to a single member.
1334///
1335/// The direct message must be distributed to the other group members through Authenticated Causal
1336/// Broadcast, calling the process function on the recipient when they are delivered.
1337///
1338/// If direct messages are sent along with a control message, we assume that the direct message for
1339/// the appropriate recipient is delivered in the same call to process. Our algorithm never sends a
1340/// direct message without an associated broadcast control message.
1341#[derive(Clone, Debug, Serialize, Deserialize)]
1342pub struct DirectMessage<ID, OP, DGM>
1343where
1344    DGM: AckedGroupMembership<ID, OP>,
1345{
1346    pub recipient: ID,
1347    pub content: DirectMessageContent<ID, OP, DGM>,
1348}
1349
1350#[derive(Clone, Debug, Serialize, Deserialize)]
1351pub enum DirectMessageContent<ID, OP, DGM>
1352where
1353    DGM: AckedGroupMembership<ID, OP>,
1354{
1355    Welcome {
1356        ciphertext: TwoPartyMessage,
1357        history: DGM::State,
1358    },
1359    TwoParty {
1360        ciphertext: TwoPartyMessage,
1361    },
1362    Forward {
1363        ciphertext: TwoPartyMessage,
1364    },
1365}
1366
1367#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1368pub enum DirectMessageType {
1369    Welcome,
1370    TwoParty,
1371    Forward,
1372}
1373
1374impl Display for DirectMessageType {
1375    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1376        write!(
1377            f,
1378            "{}",
1379            match self {
1380                DirectMessageType::Welcome => "welcome",
1381                DirectMessageType::TwoParty => "2sm",
1382                DirectMessageType::Forward => "forward",
1383            }
1384        )
1385    }
1386}
1387
1388impl<ID, OP, DGM> DirectMessage<ID, OP, DGM>
1389where
1390    DGM: AckedGroupMembership<ID, OP>,
1391{
1392    pub fn message_type(&self) -> DirectMessageType {
1393        match self.content {
1394            DirectMessageContent::Welcome { .. } => DirectMessageType::Welcome,
1395            DirectMessageContent::TwoParty { .. } => DirectMessageType::TwoParty,
1396            DirectMessageContent::Forward { .. } => DirectMessageType::Forward,
1397        }
1398    }
1399}
1400
1401/// Randomly generated seed we keep temporarily around when creating or updating a group or
1402/// removing a member.
1403#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1404#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
1405pub struct NextSeed(Secret<RATCHET_KEY_SIZE>);
1406
1407impl NextSeed {
1408    pub fn from_bytes(bytes: [u8; RATCHET_KEY_SIZE]) -> Self {
1409        Self(Secret::from_bytes(bytes))
1410    }
1411
1412    pub fn try_from_bytes<ID, OP, PKI, DGM, KMG>(
1413        bytes: &[u8],
1414    ) -> Result<Self, DcgkaError<ID, OP, PKI, DGM, KMG>>
1415    where
1416        PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
1417        DGM: AckedGroupMembership<ID, OP>,
1418        KMG: PreKeyManager,
1419    {
1420        let bytes: [u8; RATCHET_KEY_SIZE] =
1421            bytes.try_into().map_err(|_| DcgkaError::InvalidKeySize)?;
1422        Ok(Self::from_bytes(bytes))
1423    }
1424
1425    pub(crate) fn as_bytes(&self) -> &[u8; RATCHET_KEY_SIZE] {
1426        self.0.as_bytes()
1427    }
1428}
1429
1430/// Chain secret for the "outer" key-agreement ratchet.
1431#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1432#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
1433pub struct ChainSecret(Secret<RATCHET_KEY_SIZE>);
1434
1435impl ChainSecret {
1436    pub fn from_welcome() -> Self {
1437        // BLAKE3 hash of the byte-representation of the utf8 string "welcome".
1438        Self::from_bytes([
1439            168, 234, 241, 118, 147, 12, 137, 47, 48, 26, 61, 243, 183, 11, 158, 143, 99, 219, 142,
1440            131, 41, 18, 245, 167, 132, 195, 241, 26, 89, 106, 154, 134,
1441        ])
1442    }
1443
1444    pub fn from_add() -> Self {
1445        // BLAKE3 hash of the byte-representation of the utf8 string "add".
1446        Self::from_bytes([
1447            58, 3, 204, 193, 45, 117, 68, 208, 41, 238, 11, 13, 169, 250, 180, 215, 22, 4, 43, 226,
1448            179, 34, 182, 188, 85, 49, 221, 39, 150, 98, 220, 156,
1449        ])
1450    }
1451
1452    pub fn from_bytes(bytes: [u8; RATCHET_KEY_SIZE]) -> Self {
1453        Self(Secret::from_bytes(bytes))
1454    }
1455
1456    pub fn try_from_bytes<ID, OP, PKI, DGM, KEY>(
1457        bytes: &[u8],
1458    ) -> Result<Self, DcgkaError<ID, OP, PKI, DGM, KEY>>
1459    where
1460        PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
1461        DGM: AckedGroupMembership<ID, OP>,
1462        KEY: PreKeyManager,
1463    {
1464        let bytes: [u8; RATCHET_KEY_SIZE] =
1465            bytes.try_into().map_err(|_| DcgkaError::InvalidKeySize)?;
1466        Ok(Self::from_bytes(bytes))
1467    }
1468
1469    pub(crate) fn as_bytes(&self) -> &[u8; RATCHET_KEY_SIZE] {
1470        self.0.as_bytes()
1471    }
1472}
1473
1474impl From<UpdateSecret> for ChainSecret {
1475    fn from(value: UpdateSecret) -> Self {
1476        Self(value.0)
1477    }
1478}
1479
1480/// Chain secret for the "inner" message ratchet.
1481#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1482#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
1483pub struct UpdateSecret(Secret<RATCHET_KEY_SIZE>);
1484
1485impl UpdateSecret {
1486    pub fn from_bytes(bytes: [u8; RATCHET_KEY_SIZE]) -> Self {
1487        Self(Secret::from_bytes(bytes))
1488    }
1489
1490    pub fn try_from_bytes<ID, OP, PKI, DGM, KMG>(
1491        bytes: &[u8],
1492    ) -> Result<Self, DcgkaError<ID, OP, PKI, DGM, KMG>>
1493    where
1494        PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
1495        DGM: AckedGroupMembership<ID, OP>,
1496        KMG: PreKeyManager,
1497    {
1498        let bytes: [u8; RATCHET_KEY_SIZE] =
1499            bytes.try_into().map_err(|_| DcgkaError::InvalidKeySize)?;
1500        Ok(Self::from_bytes(bytes))
1501    }
1502
1503    pub(crate) fn as_bytes(&self) -> &[u8; RATCHET_KEY_SIZE] {
1504        self.0.as_bytes()
1505    }
1506}
1507
1508impl From<UpdateSecret> for Secret<RATCHET_KEY_SIZE> {
1509    fn from(update_secret: UpdateSecret) -> Self {
1510        update_secret.0
1511    }
1512}
1513
1514#[derive(Debug, Error)]
1515pub enum DcgkaError<ID, OP, PKI, DGM, KMG>
1516where
1517    PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
1518    DGM: AckedGroupMembership<ID, OP>,
1519    KMG: PreKeyManager,
1520{
1521    #[error("the given key does not match the required 32 byte length")]
1522    InvalidKeySize,
1523
1524    #[error("expected ratchet secret but couldn't find anything")]
1525    MissingRatchetSecret,
1526
1527    #[error("expected message secret for {0} at seq {1} but couldn't find anything")]
1528    MissingMemberSecret(ID, OP),
1529
1530    #[error("expected direct message of type \"{0}\" but got nothing instead")]
1531    MissingDirectMessage(DirectMessageType),
1532
1533    #[error("expected direct message of type \"{0}\" but got message of type \"{1}\" instead")]
1534    UnexpectedDirectMessageType(DirectMessageType, DirectMessageType),
1535
1536    #[error("direct message recipient mismatch, expected recipient: {1}, actual recipient: {0}")]
1537    NotOurDirectMessage(ID, ID),
1538
1539    #[error("computing members view from dgm failed: {0}")]
1540    MembersView(DGM::Error),
1541
1542    #[error("dgm operation failed: {0}")]
1543    DgmOperation(DGM::Error),
1544
1545    #[error("failed retrieving bundle from pre key registry: {0}")]
1546    PreKeyRegistry(<PKI as PreKeyRegistry<ID, OneTimeKeyBundle>>::Error),
1547
1548    #[error("failed retrieving identity from registry: {0}")]
1549    IdentityRegistry(<PKI as IdentityRegistry<ID, PKI::State>>::Error),
1550
1551    #[error("missing key bundle for member {0}")]
1552    MissingPreKeys(ID),
1553
1554    #[error("missing identity key for member {0}")]
1555    MissingIdentityKey(ID),
1556
1557    #[error(transparent)]
1558    Rng(#[from] RngError),
1559
1560    #[error(transparent)]
1561    KeyManager(KMG::Error),
1562
1563    #[error(transparent)]
1564    TwoParty(#[from] TwoPartyError),
1565
1566    #[error(transparent)]
1567    Hdkf(#[from] HkdfError),
1568}