Skip to main content

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    prelude::ExtensionTypeNotValidInGroupInfoError,
22};
23
24const SIGNATURE_GROUP_INFO_LABEL: &str = "GroupInfoTBS";
25
26/// A type that represents a group info of which the signature has not been verified.
27/// It implements the [`Verifiable`] trait and can be turned into a group info by calling
28/// `verify(...)` with the signature key of the [`Credential`](crate::credentials::Credential).
29/// When receiving a serialized group info, it can only be deserialized into a
30/// [`VerifiableGroupInfo`], which can then be turned into a group info as described above.
31#[derive(Debug, PartialEq, Clone, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize)]
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<GroupInfo>,
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) leaf index of the member that signed the verifiable group info.
98    ///
99    /// Note: This method should only be used when necessary to verify the group info signature.
100    pub 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<GroupInfo> {
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    /// Get (unverified) group context of the verifiable group info.
128    pub fn group_context(&self) -> &GroupContext {
129        &self.payload.group_context
130    }
131}
132
133#[cfg(test)]
134impl VerifiableGroupInfo {
135    pub(crate) fn payload_mut(&mut self) -> &mut GroupInfoTBS {
136        &mut self.payload
137    }
138
139    /// Break the signature for testing purposes.
140    pub(crate) fn break_signature(&mut self) {
141        self.signature.modify(b"");
142    }
143}
144
145#[cfg(any(feature = "test-utils", test))]
146impl From<VerifiableGroupInfo> for GroupInfo {
147    fn from(vgi: VerifiableGroupInfo) -> Self {
148        GroupInfo {
149            payload: vgi.payload,
150            signature: vgi.signature,
151            serialized_payload: None,
152        }
153    }
154}
155
156/// GroupInfo
157///
158/// Note: The struct is split into a `GroupInfoTBS` payload and a signature.
159///
160/// ```c
161/// // draft-ietf-mls-protocol-16
162///
163/// struct {
164///     GroupContext group_context;
165///     Extension extensions<V>;
166///     MAC confirmation_tag;
167///     uint32 signer;
168///     /* SignWithLabel(., "GroupInfoTBS", GroupInfoTBS) */
169///     opaque signature<V>;
170/// } GroupInfo;
171/// ```
172///
173/// Deserializing a `GroupInfo` with serde does not verify its signature. Only
174/// deserialize data that was serialized by this application, and use
175/// [`VerifiableGroupInfo`] for group infos received from others.
176#[derive(Debug, Clone, TlsSize, SerdeSerialize, SerdeDeserialize)]
177#[cfg_attr(feature = "test-utils", derive(TlsDeserialize))]
178pub struct GroupInfo {
179    payload: GroupInfoTBS,
180    signature: Signature,
181    #[serde(skip)]
182    #[tls_codec(skip)]
183    serialized_payload: Option<Vec<u8>>,
184}
185
186// `serialized_payload` is a cache: it is set after signing and unset after
187// verifying or deserializing, so it must not take part in the comparison.
188impl PartialEq for GroupInfo {
189    fn eq(&self, other: &Self) -> bool {
190        self.payload == other.payload && self.signature == other.signature
191    }
192}
193
194impl TlsSerializeTrait for GroupInfo {
195    fn tls_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
196        let mut written = 0;
197        if let Some(ref bytes) = self.serialized_payload {
198            writer.write_all(bytes)?;
199            written += bytes.len();
200        } else {
201            written += self.payload.tls_serialize(writer)?;
202        }
203        written += self.signature.tls_serialize(writer)?;
204        Ok(written)
205    }
206}
207
208impl GroupInfo {
209    /// Returns the group context.
210    pub fn group_context(&self) -> &GroupContext {
211        &self.payload.group_context
212    }
213
214    /// Returns the [`GroupInfo`] extensions.
215    pub fn extensions(&self) -> &Extensions<GroupInfo> {
216        &self.payload.extensions
217    }
218
219    /// Returns the [`GroupInfo`] signature.
220    pub fn signature(&self) -> &Signature {
221        &self.signature
222    }
223
224    /// Returns the leaf index of the member that signed the group info.
225    pub fn signer(&self) -> LeafNodeIndex {
226        self.payload.signer
227    }
228
229    /// Returns the confirmation tag.
230    pub(crate) fn confirmation_tag(&self) -> &ConfirmationTag {
231        &self.payload.confirmation_tag
232    }
233
234    /// Returns the GroupInfo with a type that signals it is unverified.
235    /// A form of downcasting to an equivalent type with a weaker type invariant.
236    pub(crate) fn into_verifiable_group_info(self) -> VerifiableGroupInfo {
237        VerifiableGroupInfo {
238            payload: GroupInfoTBS {
239                group_context: self.payload.group_context,
240                extensions: self.payload.extensions,
241                confirmation_tag: self.payload.confirmation_tag,
242                signer: self.payload.signer,
243            },
244            signature: self.signature,
245        }
246    }
247}
248
249/// GroupInfo (To Be Signed)
250///
251/// ```c
252/// // draft-ietf-mls-protocol-16
253///
254/// struct {
255///     GroupContext group_context;
256///     Extension extensions<V>;
257///     MAC confirmation_tag;
258///     uint32 signer;
259/// } GroupInfoTBS;
260/// ```
261#[derive(
262    Debug,
263    PartialEq,
264    Clone,
265    TlsDeserialize,
266    TlsDeserializeBytes,
267    TlsSerialize,
268    TlsSize,
269    SerdeSerialize,
270    SerdeDeserialize,
271)]
272pub(crate) struct GroupInfoTBS {
273    group_context: GroupContext,
274    extensions: Extensions<GroupInfo>,
275    confirmation_tag: ConfirmationTag,
276    signer: LeafNodeIndex,
277}
278
279impl GroupInfoTBS {
280    /// Create a new to-be-signed group info.
281    pub(crate) fn new(
282        group_context: GroupContext,
283        extensions: Extensions<GroupInfo>,
284        confirmation_tag: ConfirmationTag,
285        signer: LeafNodeIndex,
286    ) -> Result<Self, InvalidExtensionError> {
287        // validate the extensions
288        for extension_type in extensions.iter().map(Extension::extension_type) {
289            if extension_type.is_valid_in_group_info() == Some(false) {
290                return Err(InvalidExtensionError::ExtensionTypeNotValidInGroupInfo(
291                    ExtensionTypeNotValidInGroupInfoError(extension_type),
292                ));
293            }
294        }
295
296        Ok(Self {
297            group_context,
298            extensions,
299            confirmation_tag,
300            signer,
301        })
302    }
303}
304
305#[cfg(test)]
306impl GroupInfoTBS {
307    pub(crate) fn group_context_mut(&mut self) -> &mut GroupContext {
308        &mut self.group_context
309    }
310}
311
312// -------------------------------------------------------------------------------------------------
313
314impl Signable for GroupInfoTBS {
315    type SignedOutput = GroupInfo;
316
317    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
318        self.tls_serialize_detached()
319    }
320
321    fn label(&self) -> &str {
322        SIGNATURE_GROUP_INFO_LABEL
323    }
324}
325
326impl SignedStruct<GroupInfoTBS> for GroupInfo {
327    fn from_payload(
328        payload: GroupInfoTBS,
329        signature: Signature,
330        serialized_payload: Vec<u8>,
331    ) -> Self {
332        Self {
333            payload,
334            signature,
335            serialized_payload: Some(serialized_payload),
336        }
337    }
338}
339
340impl Verifiable for VerifiableGroupInfo {
341    type VerifiedStruct = GroupInfo;
342
343    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
344        self.payload.tls_serialize_detached()
345    }
346
347    fn signature(&self) -> &Signature {
348        &self.signature
349    }
350
351    fn label(&self) -> &str {
352        SIGNATURE_GROUP_INFO_LABEL
353    }
354
355    fn verify(
356        self,
357        crypto: &impl OpenMlsCrypto,
358        pk: &crate::ciphersuite::OpenMlsSignaturePublicKey,
359    ) -> Result<Self::VerifiedStruct, crate::ciphersuite::signable::SignatureError> {
360        self.verify_no_out(crypto, pk)?;
361        Ok(GroupInfo {
362            payload: self.payload,
363            signature: self.signature,
364            serialized_payload: None,
365        })
366    }
367}
368
369impl VerifiedStruct for GroupInfo {}