1use 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#[derive(Debug, PartialEq, Clone, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize)]
32pub struct VerifiableGroupInfo {
33 payload: GroupInfoTBS,
34 signature: Signature,
35}
36
37#[derive(Error, Debug, PartialEq, Clone)]
39pub enum GroupInfoError {
40 #[error("Decryption failed.")]
42 DecryptionFailed,
43 #[error("Malformed.")]
45 Malformed,
46}
47
48impl VerifiableGroupInfo {
49 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 pub fn ciphersuite(&self) -> Ciphersuite {
94 self.payload.group_context.ciphersuite()
95 }
96
97 pub fn signer(&self) -> LeafNodeIndex {
101 self.payload.signer
102 }
103
104 pub fn extensions(&self) -> &Extensions<GroupInfo> {
108 &self.payload.extensions
109 }
110
111 pub fn group_id(&self) -> &GroupId {
116 self.payload.group_context.group_id()
117 }
118
119 pub fn epoch(&self) -> GroupEpoch {
124 self.payload.group_context.epoch()
125 }
126
127 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 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#[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
186impl 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 pub fn group_context(&self) -> &GroupContext {
211 &self.payload.group_context
212 }
213
214 pub fn extensions(&self) -> &Extensions<GroupInfo> {
216 &self.payload.extensions
217 }
218
219 pub fn signature(&self) -> &Signature {
221 &self.signature
222 }
223
224 pub fn signer(&self) -> LeafNodeIndex {
226 self.payload.signer
227 }
228
229 pub(crate) fn confirmation_tag(&self) -> &ConfirmationTag {
231 &self.payload.confirmation_tag
232 }
233
234 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#[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 pub(crate) fn new(
282 group_context: GroupContext,
283 extensions: Extensions<GroupInfo>,
284 confirmation_tag: ConfirmationTag,
285 signer: LeafNodeIndex,
286 ) -> Result<Self, InvalidExtensionError> {
287 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
312impl 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 {}