openmls/messages/
group_info.rs

1//! This module contains all types related to group info handling.
2
3use openmls_traits::crypto::OpenMlsCrypto;
4use openmls_traits::types::Ciphersuite;
5use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
6use thiserror::Error;
7use tls_codec::{
8    Deserialize, Serialize as TlsSerializeTrait, TlsDeserialize, TlsDeserializeBytes, TlsSerialize,
9    TlsSize,
10};
11
12use crate::{
13    binary_tree::LeafNodeIndex,
14    ciphersuite::{
15        signable::{Signable, SignedStruct, Verifiable, VerifiedStruct},
16        AeadKey, AeadNonce, Signature,
17    },
18    extensions::{errors::InvalidExtensionError, Extension, Extensions},
19    group::{GroupContext, GroupEpoch, GroupId},
20    messages::ConfirmationTag,
21};
22
23const SIGNATURE_GROUP_INFO_LABEL: &str = "GroupInfoTBS";
24
25/// A type that represents a group info of which the signature has not been verified.
26/// It implements the [`Verifiable`] trait and can be turned into a group info by calling
27/// `verify(...)` with the signature key of the [`Credential`](crate::credentials::Credential).
28/// When receiving a serialized group info, it can only be deserialized into a
29/// [`VerifiableGroupInfo`], which can then be turned into a group info as described above.
30#[derive(Debug, PartialEq, Clone, TlsDeserialize, TlsDeserializeBytes, TlsSize)]
31#[cfg_attr(any(test, feature = "test-utils"), derive(TlsSerialize))]
32pub struct VerifiableGroupInfo {
33    payload: GroupInfoTBS,
34    signature: Signature,
35}
36
37/// Error related to group info.
38#[derive(Error, Debug, PartialEq, Clone)]
39pub enum GroupInfoError {
40    /// Decryption failed.
41    #[error("Decryption failed.")]
42    DecryptionFailed,
43    /// Malformed.
44    #[error("Malformed.")]
45    Malformed,
46}
47
48impl VerifiableGroupInfo {
49    /// Create a new [`VerifiableGroupInfo`] from its contents.
50    pub fn new(
51        group_context: GroupContext,
52        extensions: Extensions,
53        confirmation_tag: ConfirmationTag,
54        signer: LeafNodeIndex,
55        signature: Signature,
56    ) -> Self {
57        let payload = GroupInfoTBS {
58            group_context,
59            extensions,
60            confirmation_tag,
61            signer,
62        };
63        Self { payload, signature }
64    }
65
66    pub(crate) fn try_from_ciphertext(
67        skey: &AeadKey,
68        nonce: &AeadNonce,
69        ciphertext: &[u8],
70        context: &[u8],
71        crypto: &impl OpenMlsCrypto,
72    ) -> Result<Self, GroupInfoError> {
73        let verifiable_group_info_plaintext = skey
74            .aead_open(crypto, ciphertext, context, nonce)
75            .map_err(|_| GroupInfoError::DecryptionFailed)?;
76
77        let mut verifiable_group_info_plaintext_slice = verifiable_group_info_plaintext.as_slice();
78
79        let verifiable_group_info =
80            VerifiableGroupInfo::tls_deserialize(&mut verifiable_group_info_plaintext_slice)
81                .map_err(|_| GroupInfoError::Malformed)?;
82
83        if !verifiable_group_info_plaintext_slice.is_empty() {
84            return Err(GroupInfoError::Malformed);
85        }
86
87        Ok(verifiable_group_info)
88    }
89
90    /// Get (unverified) ciphersuite of the verifiable group info.
91    ///
92    /// Note: This method should only be used when necessary to verify the group info signature.
93    pub fn ciphersuite(&self) -> Ciphersuite {
94        self.payload.group_context.ciphersuite()
95    }
96
97    /// Get (unverified) signer of the verifiable group info.
98    ///
99    /// Note: This method should only be used when necessary to verify the group info signature.
100    pub(crate) fn signer(&self) -> LeafNodeIndex {
101        self.payload.signer
102    }
103
104    /// Get (unverified) extensions of the verifiable group info.
105    ///
106    /// Note: This method should only be used when necessary to verify the group info signature.
107    pub fn extensions(&self) -> &Extensions {
108        &self.payload.extensions
109    }
110
111    /// Get (unverified) group ID of the verifiable group info.
112    ///
113    /// Note: This method should only be used when necessary to verify the group
114    /// info signature.
115    pub fn group_id(&self) -> &GroupId {
116        self.payload.group_context.group_id()
117    }
118
119    /// Get (unverified) epoch of the verifiable group info.
120    ///
121    /// Note: This method should only be used when necessary to verify the group
122    /// info signature.
123    pub fn epoch(&self) -> GroupEpoch {
124        self.payload.group_context.epoch()
125    }
126}
127
128#[cfg(test)]
129impl VerifiableGroupInfo {
130    pub(crate) fn payload_mut(&mut self) -> &mut GroupInfoTBS {
131        &mut self.payload
132    }
133
134    /// Break the signature for testing purposes.
135    pub(crate) fn break_signature(&mut self) {
136        self.signature.modify(b"");
137    }
138}
139
140#[cfg(any(feature = "test-utils", test))]
141impl From<VerifiableGroupInfo> for GroupInfo {
142    fn from(vgi: VerifiableGroupInfo) -> Self {
143        GroupInfo {
144            payload: vgi.payload,
145            signature: vgi.signature,
146            serialized_payload: None,
147        }
148    }
149}
150
151/// GroupInfo
152///
153/// Note: The struct is split into a `GroupInfoTBS` payload and a signature.
154///
155/// ```c
156/// // draft-ietf-mls-protocol-16
157///
158/// struct {
159///     GroupContext group_context;
160///     Extension extensions<V>;
161///     MAC confirmation_tag;
162///     uint32 signer;
163///     /* SignWithLabel(., "GroupInfoTBS", GroupInfoTBS) */
164///     opaque signature<V>;
165/// } GroupInfo;
166/// ```
167#[derive(Debug, PartialEq, Clone, TlsSize, SerdeSerialize, SerdeDeserialize)]
168#[cfg_attr(feature = "test-utils", derive(TlsDeserialize))]
169pub struct GroupInfo {
170    payload: GroupInfoTBS,
171    signature: Signature,
172    #[serde(skip)]
173    #[tls_codec(skip)]
174    serialized_payload: Option<Vec<u8>>,
175}
176
177impl TlsSerializeTrait for GroupInfo {
178    fn tls_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
179        let mut written = 0;
180        if let Some(ref bytes) = self.serialized_payload {
181            written += writer.write(bytes)?;
182        } else {
183            written += self.payload.tls_serialize(writer)?;
184        }
185        written += self.signature.tls_serialize(writer)?;
186        Ok(written)
187    }
188}
189
190impl GroupInfo {
191    /// Returns the group context.
192    pub fn group_context(&self) -> &GroupContext {
193        &self.payload.group_context
194    }
195
196    /// Returns the [`GroupInfo`] extensions.
197    pub fn extensions(&self) -> &Extensions {
198        &self.payload.extensions
199    }
200
201    /// Returns the [`GroupInfo`] signature.
202    pub fn signature(&self) -> &Signature {
203        &self.signature
204    }
205
206    /// Returns the confirmation tag.
207    pub(crate) fn confirmation_tag(&self) -> &ConfirmationTag {
208        &self.payload.confirmation_tag
209    }
210
211    #[cfg(any(feature = "test-utils", test))]
212    pub(crate) fn into_verifiable_group_info(self) -> VerifiableGroupInfo {
213        VerifiableGroupInfo {
214            payload: GroupInfoTBS {
215                group_context: self.payload.group_context,
216                extensions: self.payload.extensions,
217                confirmation_tag: self.payload.confirmation_tag,
218                signer: self.payload.signer,
219            },
220            signature: self.signature,
221        }
222    }
223}
224
225/// GroupInfo (To Be Signed)
226///
227/// ```c
228/// // draft-ietf-mls-protocol-16
229///
230/// struct {
231///     GroupContext group_context;
232///     Extension extensions<V>;
233///     MAC confirmation_tag;
234///     uint32 signer;
235/// } GroupInfoTBS;
236/// ```
237#[derive(
238    Debug,
239    PartialEq,
240    Clone,
241    TlsDeserialize,
242    TlsDeserializeBytes,
243    TlsSerialize,
244    TlsSize,
245    SerdeSerialize,
246    SerdeDeserialize,
247)]
248pub(crate) struct GroupInfoTBS {
249    group_context: GroupContext,
250    extensions: Extensions,
251    confirmation_tag: ConfirmationTag,
252    signer: LeafNodeIndex,
253}
254
255impl GroupInfoTBS {
256    /// Create a new to-be-signed group info.
257    pub(crate) fn new(
258        group_context: GroupContext,
259        extensions: Extensions,
260        confirmation_tag: ConfirmationTag,
261        signer: LeafNodeIndex,
262    ) -> Result<Self, InvalidExtensionError> {
263        // validate the extensions
264        for extension_type in extensions.iter().map(Extension::extension_type) {
265            if extension_type.is_valid_in_group_info() == Some(false) {
266                return Err(InvalidExtensionError::IllegalInGroupInfo);
267            }
268        }
269
270        Ok(Self {
271            group_context,
272            extensions,
273            confirmation_tag,
274            signer,
275        })
276    }
277}
278
279#[cfg(test)]
280impl GroupInfoTBS {
281    pub(crate) fn group_context_mut(&mut self) -> &mut GroupContext {
282        &mut self.group_context
283    }
284}
285
286// -------------------------------------------------------------------------------------------------
287
288impl Signable for GroupInfoTBS {
289    type SignedOutput = GroupInfo;
290
291    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
292        self.tls_serialize_detached()
293    }
294
295    fn label(&self) -> &str {
296        SIGNATURE_GROUP_INFO_LABEL
297    }
298}
299
300impl SignedStruct<GroupInfoTBS> for GroupInfo {
301    fn from_payload(
302        payload: GroupInfoTBS,
303        signature: Signature,
304        serialized_payload: Vec<u8>,
305    ) -> Self {
306        Self {
307            payload,
308            signature,
309            serialized_payload: Some(serialized_payload),
310        }
311    }
312}
313
314impl Verifiable for VerifiableGroupInfo {
315    type VerifiedStruct = GroupInfo;
316
317    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
318        self.payload.tls_serialize_detached()
319    }
320
321    fn signature(&self) -> &Signature {
322        &self.signature
323    }
324
325    fn label(&self) -> &str {
326        SIGNATURE_GROUP_INFO_LABEL
327    }
328
329    fn verify(
330        self,
331        crypto: &impl OpenMlsCrypto,
332        pk: &crate::ciphersuite::OpenMlsSignaturePublicKey,
333    ) -> Result<Self::VerifiedStruct, crate::ciphersuite::signable::SignatureError> {
334        self.verify_no_out(crypto, pk)?;
335        Ok(GroupInfo {
336            payload: self.payload,
337            signature: self.signature,
338            serialized_payload: None,
339        })
340    }
341}
342
343impl VerifiedStruct for GroupInfo {}