Skip to main content

openmls/
component.rs

1//! Components from the extensions draft.
2
3use serde::{Deserialize, Serialize};
4use tls_codec::{TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, VLBytes};
5
6/// A ComponentId that uniquely identifies a component within the scope of an application.
7pub type ComponentId = u16;
8
9/// An entry in the [`AppDataDictionary`].
10///
11/// [`AppDataDictionary`]: crate::extensions::AppDataDictionary
12#[derive(
13    PartialEq,
14    Eq,
15    Clone,
16    Debug,
17    Serialize,
18    Deserialize,
19    TlsSerialize,
20    TlsDeserialize,
21    TlsDeserializeBytes,
22    TlsSize,
23)]
24pub struct ComponentData {
25    component_id: ComponentId,
26    data: VLBytes,
27}
28
29impl ComponentData {
30    /// Return the [`ComponentId`] for this entry.
31    pub fn id(&self) -> ComponentId {
32        self.component_id
33    }
34
35    /// Return the data as a byte slice.
36    pub fn into_data(self) -> VLBytes {
37        self.data
38    }
39
40    /// Return the data as a byte slice.
41    pub fn data(&self) -> &[u8] {
42        self.data.as_ref()
43    }
44
45    /// Consumes the struct and returns its component parts.
46    pub fn into_parts(self) -> (ComponentId, VLBytes) {
47        (self.component_id, self.data)
48    }
49
50    /// Create a new ComponentData from parts.
51    pub fn from_parts(component_id: ComponentId, data: VLBytes) -> Self {
52        Self { component_id, data }
53    }
54}
55
56/// An unknown component id in the standardized range (0x0000-0x7fff)
57#[repr(transparent)]
58pub struct UnknownComponentId(ComponentId);
59
60impl UnknownComponentId {
61    /// Creates a new [`UnknownComponentId`]. Only returns [`Some`] if it is not covered by any of the
62    /// specified ranges.
63    pub fn new(id: ComponentId) -> Option<Self> {
64        let is_grease = (id & 0x0f0f == 0x0a0a) && (id & 0xff == (id >> 8)) && id != 0xfafa;
65        (!is_grease && id < 0x8000).then_some(Self(id))
66    }
67}
68
69/// A component id from the private range (0x8000-0xffff)
70#[repr(transparent)]
71pub struct PrivateComponentId(ComponentId);
72
73impl PrivateComponentId {
74    /// Creates a new [`PrivateComponentId`]. Only returns [`Some`] if it is in the range specified
75    /// for private components (`0x8000..=0xffff`)
76    /// specified ranges.
77    pub fn new(id: u16) -> Option<Self> {
78        matches!(id, 0x8000..).then_some(Self(id))
79    }
80}
81
82#[cfg(feature = "extensions-draft")]
83#[repr(u16)]
84/// The type of a Component ID
85pub enum ComponentType {
86    /// ComponentId 0 is reserved
87    Reserved = 0,
88    /// The AppComponents component.
89    AppComponents = 1,
90    /// The SafeAad component.
91    SafeAad = 2,
92    /// The ComponentMediaTypes component.
93    ComponentMediaTypes = 3,
94    /// The LastResortKeyPackage component.
95    LastResortKeyPackage = 4,
96    /// The AppAck component.
97    AppAck = 5,
98    /// The GREASE component with ID 0x0A0A.
99    Grease0A0A = 0x0a0a,
100    /// The GREASE component with ID 0x1A1A.
101    Grease1A1A = 0x1a1a,
102    /// The GREASE component with ID 0x2A2A.
103    Grease2A2A = 0x2a2a,
104    /// The GREASE component with ID 0x3A3A.
105    Grease3A3A = 0x3a3a,
106    /// The GREASE component with ID 0x4A4A.
107    Grease4A4A = 0x4a4a,
108    /// The GREASE component with ID 0x5A5A.
109    Grease5A5A = 0x5a5a,
110    /// The GREASE component with ID 0x6A6A.
111    Grease6A6A = 0x6a6a,
112    /// The GREASE component with ID 0x7A7A.
113    Grease7A7A = 0x7a7a,
114
115    /// An unknown component id in the standardized range (0x0000-0x7fff)
116    Unknown(UnknownComponentId),
117
118    /// A component id from the private range (0x8000-0xffff)
119    Private(PrivateComponentId),
120}
121
122impl From<ComponentId> for ComponentType {
123    fn from(value: ComponentId) -> Self {
124        // We allow this here because we use the overlapping one at the bottom (..0x8000) as a catch-all.
125        // This would be annoying to do explicitly because of the sparse GREASE values.
126        #[allow(clippy::match_overlapping_arm)]
127        match value {
128            0 => Self::Reserved,
129            1 => Self::AppComponents,
130            2 => Self::SafeAad,
131            3 => Self::ComponentMediaTypes,
132            4 => Self::LastResortKeyPackage,
133            5 => Self::AppAck,
134
135            0x0a0a => Self::Grease0A0A,
136            0x1a1a => Self::Grease1A1A,
137            0x2a2a => Self::Grease2A2A,
138            0x3a3a => Self::Grease3A3A,
139            0x4a4a => Self::Grease4A4A,
140            0x5a5a => Self::Grease5A5A,
141            0x6a6a => Self::Grease6A6A,
142            0x7a7a => Self::Grease7A7A,
143
144            6..0x8000 => Self::Unknown(UnknownComponentId(value)),
145            0x8000.. => Self::Private(PrivateComponentId(value)),
146        }
147    }
148}
149
150impl From<ComponentType> for ComponentId {
151    fn from(value: ComponentType) -> Self {
152        match value {
153            ComponentType::Reserved => 0,
154            ComponentType::AppComponents => 1,
155            ComponentType::SafeAad => 2,
156            ComponentType::ComponentMediaTypes => 3,
157            ComponentType::LastResortKeyPackage => 4,
158            ComponentType::AppAck => 5,
159
160            ComponentType::Grease0A0A => 0x0a0a,
161            ComponentType::Grease1A1A => 0x1a1a,
162            ComponentType::Grease2A2A => 0x2a2a,
163            ComponentType::Grease3A3A => 0x3a3a,
164            ComponentType::Grease4A4A => 0x4a4a,
165            ComponentType::Grease5A5A => 0x5a5a,
166            ComponentType::Grease6A6A => 0x6a6a,
167            ComponentType::Grease7A7A => 0x7a7a,
168
169            ComponentType::Unknown(UnknownComponentId(id)) => id,
170            ComponentType::Private(PrivateComponentId(id)) => id,
171        }
172    }
173}
174
175/// A list of [`ComponentId`]s.
176///
177/// Used as the body of the `safe_aad` component in the `app_data_dictionary`
178/// extension. When present in a LeafNode it lists supported components. When
179/// present in the GroupContext it lists components whose Safe AAD must be
180/// understood by the entire group.
181///
182/// ```tls
183/// struct {
184///     ComponentID component_ids<V>;
185/// } ComponentsList;
186/// ```
187#[cfg(feature = "extensions-draft")]
188#[derive(
189    PartialEq,
190    Eq,
191    Clone,
192    Debug,
193    Default,
194    Serialize,
195    Deserialize,
196    TlsSerialize,
197    TlsDeserialize,
198    TlsDeserializeBytes,
199    TlsSize,
200)]
201pub struct ComponentsList {
202    component_ids: Vec<ComponentId>,
203}
204
205#[cfg(feature = "extensions-draft")]
206impl ComponentsList {
207    /// Create a new [`ComponentsList`] from a vector of [`ComponentId`]s.
208    pub fn new(component_ids: Vec<ComponentId>) -> Self {
209        Self { component_ids }
210    }
211
212    /// Access the underlying ids.
213    pub fn ids(&self) -> &[ComponentId] {
214        &self.component_ids
215    }
216
217    /// Consume self and return the ids.
218    pub fn into_ids(self) -> Vec<ComponentId> {
219        self.component_ids
220    }
221}
222
223/// Label for safe encryption/decryption as defined in Section 4.2 of the MLS Extensions draft
224#[derive(Debug, TlsSerialize, TlsSize)]
225pub(crate) struct ComponentOperationLabel {
226    /// "Application"
227    base_label: VLBytes,
228    component_id: ComponentId,
229    label: VLBytes,
230}
231
232const COMPONENT_OPERATION_BASE_LABEL: &[u8] = b"Application";
233
234impl ComponentOperationLabel {
235    /// Creates a new ComponentOperationLabel, prefixed with "Application"
236    pub fn new(component_id: ComponentId, label: &str) -> Self {
237        Self {
238            base_label: COMPONENT_OPERATION_BASE_LABEL.into(),
239            component_id,
240            label: label.as_bytes().into(),
241        }
242    }
243}