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, 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#[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#[derive(Error, Debug, PartialEq, Clone)]
38pub enum GroupInfoError {
39 #[error("Decryption failed.")]
41 DecryptionFailed,
42 #[error("Malformed.")]
44 Malformed,
45}
46
47impl VerifiableGroupInfo {
48 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 pub fn ciphersuite(&self) -> Ciphersuite {
93 self.payload.group_context.ciphersuite()
94 }
95
96 pub(crate) fn signer(&self) -> LeafNodeIndex {
100 self.payload.signer
101 }
102
103 pub fn extensions(&self) -> &Extensions {
107 &self.payload.extensions
108 }
109
110 pub fn group_id(&self) -> &GroupId {
115 self.payload.group_context.group_id()
116 }
117
118 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 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#[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 pub fn group_context(&self) -> &GroupContext {
175 &self.payload.group_context
176 }
177
178 pub fn extensions(&self) -> &Extensions {
180 &self.payload.extensions
181 }
182
183 pub fn signature(&self) -> &Signature {
185 &self.signature
186 }
187
188 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#[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 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
261impl 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 {}