openmls/group/public_group/
diff.rs1use std::collections::HashSet;
6
7use openmls_traits::crypto::OpenMlsCrypto;
8use openmls_traits::types::Ciphersuite;
9use serde::{Deserialize, Serialize};
10use tls_codec::Serialize as TlsSerialize;
11
12use super::PublicGroup;
13use crate::{
14 binary_tree::{array_representation::TreeSize, LeafNodeIndex},
15 error::LibraryError,
16 extensions::Extensions,
17 framing::{mls_auth_content::AuthenticatedContent, public_message::InterimTranscriptHashInput},
18 group::GroupContext,
19 messages::{proposals::AddProposal, ConfirmationTag, EncryptedGroupSecrets},
20 schedule::{psk::PreSharedKeyId, CommitSecret, JoinerSecret},
21 treesync::{
22 diff::{StagedTreeSyncDiff, TreeSyncDiff},
23 errors::ApplyUpdatePathError,
24 node::{
25 encryption_keys::EncryptionKeyPair, leaf_node::LeafNode,
26 parent_node::PlainUpdatePathNode,
27 },
28 treekem::{DecryptPathParams, UpdatePath, UpdatePathNode},
29 RatchetTree,
30 },
31};
32
33pub(crate) mod apply_proposals;
34pub(crate) mod compute_path;
35
36pub(crate) struct PublicGroupDiff<'a> {
37 diff: TreeSyncDiff<'a>,
38 group_context: GroupContext,
39 interim_transcript_hash: Vec<u8>,
40 confirmation_tag: ConfirmationTag,
42}
43
44impl<'a> PublicGroupDiff<'a> {
45 pub(super) fn new(public_group: &'a PublicGroup) -> PublicGroupDiff<'a> {
47 Self {
48 diff: public_group.treesync().empty_diff(),
49 group_context: public_group.group_context().clone(),
50 interim_transcript_hash: public_group.interim_transcript_hash().to_vec(),
51 confirmation_tag: public_group.confirmation_tag().clone(),
52 }
53 }
54
55 pub(crate) fn into_staged_diff(
58 self,
59 crypto: &impl OpenMlsCrypto,
60 ciphersuite: Ciphersuite,
61 ) -> Result<StagedPublicGroupDiff, LibraryError> {
62 let staged_diff = self.diff.into_staged_diff(crypto, ciphersuite)?;
63 Ok(StagedPublicGroupDiff {
64 staged_diff,
65 group_context: self.group_context,
66 interim_transcript_hash: self.interim_transcript_hash,
67 confirmation_tag: self.confirmation_tag,
68 })
69 }
70
71 #[allow(clippy::too_many_arguments)]
81 pub(crate) fn encrypt_group_secrets(
82 &self,
83 joiner_secret: &JoinerSecret,
84 invited_members: Vec<(LeafNodeIndex, AddProposal)>,
85 plain_path_option: Option<&[PlainUpdatePathNode]>,
86 presharedkeys: &[PreSharedKeyId],
87 encrypted_group_info: &[u8],
88 crypto: &impl OpenMlsCrypto,
89 leaf_index: LeafNodeIndex,
90 ) -> Result<Vec<EncryptedGroupSecrets>, LibraryError> {
91 self.diff.encrypt_group_secrets(
92 joiner_secret,
93 invited_members,
94 plain_path_option,
95 presharedkeys,
96 encrypted_group_info,
97 crypto,
98 leaf_index,
99 )
100 }
101
102 pub(crate) fn tree_size(&self) -> TreeSize {
104 self.diff.tree_size()
105 }
106
107 pub(crate) fn export_ratchet_tree(&self) -> RatchetTree {
110 self.diff.export_ratchet_tree()
111 }
112
113 pub(crate) fn decrypt_path(
125 &self,
126 crypto: &impl OpenMlsCrypto,
127 owned_keys: &[&EncryptionKeyPair],
128 own_leaf_index: LeafNodeIndex,
129 sender_leaf_index: LeafNodeIndex,
130 update_path: &[UpdatePathNode],
131 exclusion_list: &HashSet<&LeafNodeIndex>,
132 ) -> Result<(Vec<EncryptionKeyPair>, CommitSecret), ApplyUpdatePathError> {
133 let params = DecryptPathParams {
134 update_path,
135 sender_leaf_index,
136 exclusion_list,
137 group_context: &self
138 .group_context()
139 .tls_serialize_detached()
140 .map_err(LibraryError::missing_bound_check)?,
141 };
142 self.diff.decrypt_path(
143 crypto,
144 self.group_context().ciphersuite(),
145 params,
146 owned_keys,
147 own_leaf_index,
148 )
149 }
150
151 pub(crate) fn leaf(&self, index: LeafNodeIndex) -> Option<&LeafNode> {
153 self.diff.leaf(index)
154 }
155
156 pub(crate) fn apply_received_update_path(
163 &mut self,
164 crypto: &impl OpenMlsCrypto,
165 ciphersuite: Ciphersuite,
166 sender_leaf_index: LeafNodeIndex,
167 update_path: &UpdatePath,
168 ) -> Result<(), ApplyUpdatePathError> {
169 self.diff
170 .apply_received_update_path(crypto, ciphersuite, sender_leaf_index, update_path)
171 }
172
173 pub(crate) fn update_interim_transcript_hash(
177 &mut self,
178 ciphersuite: Ciphersuite,
179 crypto: &impl OpenMlsCrypto,
180 confirmation_tag: ConfirmationTag,
181 ) -> Result<(), LibraryError> {
182 let interim_transcript_hash = {
183 let input = InterimTranscriptHashInput::from(&confirmation_tag);
184
185 input.calculate_interim_transcript_hash(
186 crypto,
187 ciphersuite,
188 self.group_context.confirmed_transcript_hash(),
189 )?
190 };
191
192 self.confirmation_tag = confirmation_tag;
193 self.interim_transcript_hash = interim_transcript_hash;
194
195 Ok(())
196 }
197
198 pub(crate) fn update_group_context(
202 &mut self,
203 crypto: &impl OpenMlsCrypto,
204 extensions: Option<Extensions>,
205 ) -> Result<(), LibraryError> {
206 let new_tree_hash = self
208 .diff
209 .compute_tree_hashes(crypto, self.group_context().ciphersuite())?;
210 self.group_context.update_tree_hash(new_tree_hash);
211 self.group_context.increment_epoch();
212 if let Some(extensions) = extensions {
213 self.group_context.set_extensions(extensions);
214 }
215 Ok(())
216 }
217
218 pub(crate) fn update_confirmed_transcript_hash(
222 &mut self,
223 crypto: &impl OpenMlsCrypto,
224 commit_content: &AuthenticatedContent,
225 ) -> Result<(), LibraryError> {
226 self.group_context.update_confirmed_transcript_hash(
227 crypto,
228 &self.interim_transcript_hash,
229 commit_content,
230 )
231 }
232
233 pub(crate) fn group_context(&self) -> &GroupContext {
234 &self.group_context
235 }
236}
237
238#[derive(Debug, Serialize, Deserialize)]
241#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))]
242pub(crate) struct StagedPublicGroupDiff {
243 pub(super) staged_diff: StagedTreeSyncDiff,
244 pub(super) group_context: GroupContext,
245 pub(super) interim_transcript_hash: Vec<u8>,
246 pub(super) confirmation_tag: ConfirmationTag,
247}
248
249impl StagedPublicGroupDiff {
250 pub(crate) fn group_context(&self) -> &GroupContext {
252 &self.group_context
253 }
254}