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