Skip to main content

openmls/group/
group_context.rs

1//! # Group Context
2
3use openmls_traits::crypto::OpenMlsCrypto;
4use openmls_traits::types::Ciphersuite;
5
6use super::*;
7#[cfg(feature = "extensions-draft-08")]
8use crate::extensions::AppDataDictionary;
9use crate::{
10    error::LibraryError,
11    framing::{mls_auth_content::AuthenticatedContent, ConfirmedTranscriptHashInput},
12    versions::ProtocolVersion,
13};
14
15/// 8.1 Group Context
16///
17///```c
18/// struct {
19///     ProtocolVersion version = mls10;
20///     CipherSuite cipher_suite;
21///     opaque group_id<V>;
22///     uint64 epoch;
23///     opaque tree_hash<V>;
24///     opaque confirmed_transcript_hash<V>;
25///     Extension extensions<V>;
26/// } GroupContext;
27///
28/// The [`GroupContext`] is a state object maintained which summarizes the group
29/// state agreed upon by each member of the group.
30///```
31#[derive(
32    Debug,
33    Clone,
34    PartialEq,
35    Eq,
36    Serialize,
37    Deserialize,
38    TlsSerialize,
39    TlsDeserialize,
40    TlsDeserializeBytes,
41    TlsSize,
42)]
43pub struct GroupContext {
44    protocol_version: ProtocolVersion,
45    ciphersuite: Ciphersuite,
46    group_id: GroupId,
47    epoch: GroupEpoch,
48    tree_hash: VLBytes,
49    confirmed_transcript_hash: VLBytes,
50    extensions: Extensions<Self>,
51}
52
53#[cfg(any(feature = "test-utils", test))]
54impl GroupContext {
55    pub(crate) fn set_epoch(&mut self, epoch: GroupEpoch) {
56        self.epoch = epoch;
57    }
58
59    /// Set the ciphersuite
60    #[cfg(test)]
61    pub(crate) fn set_ciphersuite(&mut self, ciphersuite: Ciphersuite) {
62        self.ciphersuite = ciphersuite;
63    }
64}
65
66impl GroupContext {
67    /// Create a new group context
68    pub(crate) fn new(
69        ciphersuite: Ciphersuite,
70        group_id: GroupId,
71        epoch: impl Into<GroupEpoch>,
72        tree_hash: Vec<u8>,
73        confirmed_transcript_hash: Vec<u8>,
74        extensions: Extensions<Self>,
75    ) -> Self {
76        GroupContext {
77            ciphersuite,
78            protocol_version: ProtocolVersion::Mls10,
79            group_id,
80            epoch: epoch.into(),
81            tree_hash: tree_hash.into(),
82            confirmed_transcript_hash: confirmed_transcript_hash.into(),
83            extensions,
84        }
85    }
86
87    #[cfg(feature = "extensions-draft-08")]
88    pub(crate) fn app_data_dict(&self) -> Option<&AppDataDictionary> {
89        self.extensions
90            .app_data_dictionary()
91            .map(|extension| extension.dictionary())
92    }
93
94    /// Create the `GroupContext` needed upon creation of a new group.
95    pub(crate) fn create_initial_group_context(
96        ciphersuite: Ciphersuite,
97        group_id: GroupId,
98        tree_hash: Vec<u8>,
99        extensions: Extensions<Self>,
100    ) -> Self {
101        // Note: Confirmed transcript hash is "The zero-length octet string."
102        Self::new(ciphersuite, group_id, 0, tree_hash, vec![], extensions)
103    }
104
105    /// Increment the current [`GroupEpoch`] by one.
106    pub(crate) fn increment_epoch(&mut self) {
107        self.epoch.increment()
108    }
109
110    /// Update the current tree hash to the new value
111    pub(crate) fn update_tree_hash(&mut self, new_tree_hash: Vec<u8>) {
112        self.tree_hash = new_tree_hash.into()
113    }
114
115    /// Update the confirmed transcript hash using the given
116    /// `interim_transcript_hash`, as well as the `commit_content`.
117    pub(crate) fn update_confirmed_transcript_hash(
118        &mut self,
119        crypto: &impl OpenMlsCrypto,
120        interim_transcript_hash: &[u8],
121        authenticated_content: &AuthenticatedContent,
122    ) -> Result<(), LibraryError> {
123        let confirmed_transcript_hash = {
124            let input = ConfirmedTranscriptHashInput::try_from(authenticated_content)
125                .map_err(|_| LibraryError::custom("PublicMessage did not contain a commit"))?;
126
127            input.calculate_confirmed_transcript_hash(
128                crypto,
129                self.ciphersuite,
130                interim_transcript_hash,
131            )?
132        };
133
134        self.confirmed_transcript_hash = confirmed_transcript_hash.into();
135
136        Ok(())
137    }
138
139    /// Return the protocol version.
140    pub fn protocol_version(&self) -> ProtocolVersion {
141        self.protocol_version
142    }
143
144    /// Return the ciphersuite.
145    pub fn ciphersuite(&self) -> Ciphersuite {
146        self.ciphersuite
147    }
148
149    /// Return the group ID.
150    pub fn group_id(&self) -> &GroupId {
151        &self.group_id
152    }
153
154    /// Return the epoch.
155    pub fn epoch(&self) -> GroupEpoch {
156        self.epoch
157    }
158
159    /// Return the tree hash.
160    pub fn tree_hash(&self) -> &[u8] {
161        self.tree_hash.as_slice()
162    }
163
164    /// Return the confirmed transcript hash.
165    pub fn confirmed_transcript_hash(&self) -> &[u8] {
166        self.confirmed_transcript_hash.as_slice()
167    }
168
169    pub(crate) fn set_extensions(&mut self, extensions: Extensions<GroupContext>) {
170        self.extensions = extensions;
171    }
172
173    /// Return the extensions.
174    pub fn extensions(&self) -> &Extensions<GroupContext> {
175        &self.extensions
176    }
177
178    /// Get the required capabilities extension.
179    pub fn required_capabilities(&self) -> Option<&RequiredCapabilitiesExtension> {
180        self.extensions.required_capabilities()
181    }
182}