openmls/group/
group_context.rs

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