openmls/group/
group_context.rs1use openmls_traits::crypto::OpenMlsCrypto;
4use openmls_traits::types::Ciphersuite;
5
6use super::*;
7#[cfg(feature = "extensions-draft-08")]
8use crate::component::{ComponentId, ComponentType, ComponentsList};
9#[cfg(feature = "extensions-draft-08")]
10use crate::extensions::AppDataDictionary;
11use crate::{
12 error::LibraryError,
13 framing::{mls_auth_content::AuthenticatedContent, ConfirmedTranscriptHashInput},
14 versions::ProtocolVersion,
15};
16
17#[derive(
34 Debug,
35 Clone,
36 PartialEq,
37 Eq,
38 Serialize,
39 Deserialize,
40 TlsSerialize,
41 TlsDeserialize,
42 TlsDeserializeBytes,
43 TlsSize,
44)]
45pub struct GroupContext {
46 protocol_version: ProtocolVersion,
47 ciphersuite: Ciphersuite,
48 group_id: GroupId,
49 epoch: GroupEpoch,
50 tree_hash: VLBytes,
51 confirmed_transcript_hash: VLBytes,
52 extensions: Extensions<Self>,
53}
54
55#[cfg(any(feature = "test-utils", test))]
56impl GroupContext {
57 pub(crate) fn set_epoch(&mut self, epoch: GroupEpoch) {
58 self.epoch = epoch;
59 }
60
61 #[cfg(test)]
63 pub(crate) fn set_ciphersuite(&mut self, ciphersuite: Ciphersuite) {
64 self.ciphersuite = ciphersuite;
65 }
66}
67
68impl GroupContext {
69 pub(crate) fn new(
71 ciphersuite: Ciphersuite,
72 group_id: GroupId,
73 epoch: impl Into<GroupEpoch>,
74 tree_hash: Vec<u8>,
75 confirmed_transcript_hash: Vec<u8>,
76 extensions: Extensions<Self>,
77 ) -> Self {
78 GroupContext {
79 ciphersuite,
80 protocol_version: ProtocolVersion::Mls10,
81 group_id,
82 epoch: epoch.into(),
83 tree_hash: tree_hash.into(),
84 confirmed_transcript_hash: confirmed_transcript_hash.into(),
85 extensions,
86 }
87 }
88
89 #[cfg(feature = "extensions-draft-08")]
90 pub(crate) fn app_data_dict(&self) -> Option<&AppDataDictionary> {
91 self.extensions
92 .app_data_dictionary()
93 .map(|extension| extension.dictionary())
94 }
95
96 #[cfg(feature = "extensions-draft-08")]
104 pub fn safe_aad_required(&self) -> bool {
105 let safe_aad_id = ComponentId::from(ComponentType::SafeAad);
106 self.app_data_dict()
107 .is_some_and(|dict| dict.contains(&safe_aad_id))
108 }
109
110 #[cfg(feature = "extensions-draft-08")]
118 pub fn safe_aad_required_components(
119 &self,
120 ) -> Result<Option<Vec<ComponentId>>, crate::framing::SafeAadError> {
121 let safe_aad_id = ComponentId::from(ComponentType::SafeAad);
122 let Some(dict) = self.app_data_dict() else {
123 return Ok(None);
124 };
125 let Some(raw) = dict.get(&safe_aad_id) else {
126 return Ok(None);
127 };
128 use tls_codec::Deserialize as _;
129 let list = ComponentsList::tls_deserialize_exact(raw)
130 .map_err(|err| crate::framing::SafeAadError::Codec(err.to_string()))?;
131 Ok(Some(list.into_ids()))
132 }
133
134 pub(crate) fn create_initial_group_context(
136 ciphersuite: Ciphersuite,
137 group_id: GroupId,
138 tree_hash: Vec<u8>,
139 extensions: Extensions<Self>,
140 ) -> Self {
141 Self::new(ciphersuite, group_id, 0, tree_hash, vec![], extensions)
143 }
144
145 pub(crate) fn increment_epoch(&mut self) {
147 self.epoch.increment()
148 }
149
150 pub(crate) fn update_tree_hash(&mut self, new_tree_hash: Vec<u8>) {
152 self.tree_hash = new_tree_hash.into()
153 }
154
155 pub(crate) fn update_confirmed_transcript_hash(
158 &mut self,
159 crypto: &impl OpenMlsCrypto,
160 interim_transcript_hash: &[u8],
161 authenticated_content: &AuthenticatedContent,
162 ) -> Result<(), LibraryError> {
163 let confirmed_transcript_hash = {
164 let input = ConfirmedTranscriptHashInput::try_from(authenticated_content)
165 .map_err(|_| LibraryError::custom("PublicMessage did not contain a commit"))?;
166
167 input.calculate_confirmed_transcript_hash(
168 crypto,
169 self.ciphersuite,
170 interim_transcript_hash,
171 )?
172 };
173
174 self.confirmed_transcript_hash = confirmed_transcript_hash.into();
175
176 Ok(())
177 }
178
179 pub fn protocol_version(&self) -> ProtocolVersion {
181 self.protocol_version
182 }
183
184 pub fn ciphersuite(&self) -> Ciphersuite {
186 self.ciphersuite
187 }
188
189 pub fn group_id(&self) -> &GroupId {
191 &self.group_id
192 }
193
194 pub fn epoch(&self) -> GroupEpoch {
196 self.epoch
197 }
198
199 pub fn tree_hash(&self) -> &[u8] {
201 self.tree_hash.as_slice()
202 }
203
204 pub fn confirmed_transcript_hash(&self) -> &[u8] {
206 self.confirmed_transcript_hash.as_slice()
207 }
208
209 pub(crate) fn set_extensions(&mut self, extensions: Extensions<GroupContext>) {
210 self.extensions = extensions;
211 }
212
213 pub fn extensions(&self) -> &Extensions<GroupContext> {
215 &self.extensions
216 }
217
218 pub fn required_capabilities(&self) -> Option<&RequiredCapabilitiesExtension> {
220 self.extensions.required_capabilities()
221 }
222}