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