1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use openmls::prelude::{group_info::GroupInfo, MlsMessageOut};
use serde::{Deserialize, Serialize};

use crate::{CryptoResult, MlsError};

/// A [GroupInfo] with metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MlsGroupInfoBundle {
    /// Indicates if the `payload` is encrypted or not
    pub encryption_type: MlsGroupInfoEncryptionType,
    /// Indicates if the `payload` contains a full, partial or referenced [GroupInfo]
    pub ratchet_tree_type: MlsRatchetTreeType,
    /// The [GroupInfo]
    pub payload: GroupInfoPayload,
}

impl MlsGroupInfoBundle {
    /// Creates a new [GroupInfoBundle] with complete and unencrypted [GroupInfo]
    pub(crate) fn try_new_full_plaintext(gi: GroupInfo) -> CryptoResult<Self> {
        use tls_codec::Serialize as _;

        let payload = MlsMessageOut::from(gi);
        let payload = payload.tls_serialize_detached().map_err(MlsError::from)?;
        Ok(Self {
            encryption_type: MlsGroupInfoEncryptionType::Plaintext,
            ratchet_tree_type: MlsRatchetTreeType::Full,
            payload: GroupInfoPayload::Plaintext(payload),
        })
    }
}

#[cfg(test)]
impl MlsGroupInfoBundle {
    pub fn get_group_info(self) -> openmls::prelude::group_info::VerifiableGroupInfo {
        match self.get_payload().extract() {
            openmls::prelude::MlsMessageInBody::GroupInfo(vgi) => vgi,
            _ => panic!("This payload should contain a GroupInfo"),
        }
    }

    pub fn get_payload(mut self) -> openmls::prelude::MlsMessageIn {
        use tls_codec::Deserialize as _;
        match &mut self.payload {
            GroupInfoPayload::Plaintext(gi) => {
                openmls::prelude::MlsMessageIn::tls_deserialize(&mut gi.as_slice()).unwrap()
            }
        }
    }
}

/// # GroupInfoEncryptionType
///
/// In order to guarantee confidentiality of the [GroupInfo] on the wire a domain can
/// request it to be encrypted when sent to the Delivery Service.
///
/// ```text
/// enum {
///     plaintext(1),
///     jwe_encrypted(2),
///     (255)
/// } GroupInfoEncryptionType;
/// ```
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[repr(u8)]
pub enum MlsGroupInfoEncryptionType {
    /// Unencrypted [GroupInfo]
    Plaintext = 1,
    /// [GroupInfo] encrypted in a JWE
    JweEncrypted = 2,
}

/// # RatchetTreeType
///
/// In order to spare some precious bytes, a [GroupInfo] can have different representations.
///
/// ```text
/// enum {
///     full(1),
///     delta(2),
///     by_ref(3),
///     (255)
/// } RatchetTreeType;
/// ```
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[repr(u8)]
pub enum MlsRatchetTreeType {
    /// Plain old and complete [GroupInfo]
    Full = 1,
    /// Contains [GroupInfo] changes since previous epoch (not yet implemented)
    /// (see [draft](https://github.com/rohan-wire/ietf-drafts/blob/main/mahy-mls-ratchet-tree-delta/draft-mahy-mls-ratchet-tree-delta.md))
    Delta = 2,
    /// Not implemented
    ByRef = 3,
}

/// Represents the byte array in [MlsGroupInfoBundle]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GroupInfoPayload {
    /// Unencrypted [GroupInfo]
    Plaintext(Vec<u8>),
    // not implemented
    // Encrypted(Vec<u8>),
}

impl GroupInfoPayload {
    /// Returns the internal byte array
    pub fn bytes(self) -> Vec<u8> {
        match self {
            GroupInfoPayload::Plaintext(gi) => gi,
        }
    }
}