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(u16);
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: u16) -> Option<Self> {
64        let is_grease = (id & 0x0f0f == 0x0a0a) && (id & 0xff == (id >> 8)) && id != 0xfefe;
65        (!is_grease && matches!(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(u16);
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-08")]
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    /// The GREASE component with ID 0x8A8A.
115    Grease8A8A = 0x8a8a,
116    /// The GREASE component with ID 0x9A9A.
117    Grease9A9A = 0x9a9a,
118    /// The GREASE component with ID 0xAAAA.
119    GreaseAAAA = 0xaaaa,
120    /// The GREASE component with ID 0xBABA.
121    GreaseBABA = 0xbaba,
122    /// The GREASE component with ID 0xCACA.
123    GreaseCACA = 0xcaca,
124    /// The GREASE component with ID 0xDADA.
125    GreaseDADA = 0xdada,
126    /// The GREASE component with ID 0xEAEA.
127    GreaseEAEA = 0xeaea,
128
129    /// An unknown component id in the standardized range (0x0000-0x7fff)
130    Unknown(UnknownComponentId),
131
132    /// A component id from the private range (0x8000-0xffff)
133    Private(PrivateComponentId),
134}
135
136impl From<ComponentId> for ComponentType {
137    fn from(value: ComponentId) -> Self {
138        // We allow this here because we use the overlapping one at the bottom (..0x8000) as a catch-all.
139        // This would be annoying to do explicitly because of the sparse GREASE values.
140        #[allow(clippy::match_overlapping_arm)]
141        match value {
142            0 => Self::Reserved,
143            1 => Self::AppComponents,
144            2 => Self::SafeAad,
145            3 => Self::ComponentMediaTypes,
146            4 => Self::LastResortKeyPackage,
147            5 => Self::AppAck,
148
149            0x0a0a => Self::Grease0A0A,
150            0x1a1a => Self::Grease1A1A,
151            0x2a2a => Self::Grease2A2A,
152            0x3a3a => Self::Grease3A3A,
153            0x4a4a => Self::Grease4A4A,
154            0x5a5a => Self::Grease5A5A,
155            0x6a6a => Self::Grease6A6A,
156            0x7a7a => Self::Grease7A7A,
157            0x8a8a => Self::Grease8A8A,
158            0x9a9a => Self::Grease9A9A,
159            0xaaaa => Self::GreaseAAAA,
160            0xbaba => Self::GreaseBABA,
161            0xcaca => Self::GreaseCACA,
162            0xdada => Self::GreaseDADA,
163            0xeaea => Self::GreaseEAEA,
164
165            6..0x8000 => Self::Unknown(UnknownComponentId(value)),
166            0x8000.. => Self::Private(PrivateComponentId(value)),
167        }
168    }
169}
170
171impl From<ComponentType> for ComponentId {
172    fn from(value: ComponentType) -> Self {
173        match value {
174            ComponentType::Reserved => 0,
175            ComponentType::AppComponents => 1,
176            ComponentType::SafeAad => 2,
177            ComponentType::ComponentMediaTypes => 3,
178            ComponentType::LastResortKeyPackage => 4,
179            ComponentType::AppAck => 5,
180
181            ComponentType::Grease0A0A => 0x0a0a,
182            ComponentType::Grease1A1A => 0x1a1a,
183            ComponentType::Grease2A2A => 0x2a2a,
184            ComponentType::Grease3A3A => 0x3a3a,
185            ComponentType::Grease4A4A => 0x4a4a,
186            ComponentType::Grease5A5A => 0x5a5a,
187            ComponentType::Grease6A6A => 0x6a6a,
188            ComponentType::Grease7A7A => 0x7a7a,
189            ComponentType::Grease8A8A => 0x8a8a,
190            ComponentType::Grease9A9A => 0x9a9a,
191            ComponentType::GreaseAAAA => 0xaaaa,
192            ComponentType::GreaseBABA => 0xbaba,
193            ComponentType::GreaseCACA => 0xcaca,
194            ComponentType::GreaseDADA => 0xdada,
195            ComponentType::GreaseEAEA => 0xeaea,
196
197            ComponentType::Unknown(UnknownComponentId(id)) => id,
198            ComponentType::Private(PrivateComponentId(id)) => id,
199        }
200    }
201}
202
203/// Label for safe encryption/decryption as defined in Section 4.2 of the MLS Extensions draft
204#[derive(Debug, TlsSerialize, TlsSize)]
205pub(crate) struct ComponentOperationLabel {
206    /// "Application"
207    base_label: VLBytes,
208    component_id: ComponentId,
209    label: VLBytes,
210}
211
212const COMPONENT_OPERATION_BASE_LABEL: &[u8] = b"Application";
213
214impl ComponentOperationLabel {
215    /// Creates a new ComponentOperationLabel, prefixed with "Application"
216    pub fn new(component_id: ComponentId, label: &str) -> Self {
217        Self {
218            base_label: COMPONENT_OPERATION_BASE_LABEL.into(),
219            component_id,
220            label: label.as_bytes().into(),
221        }
222    }
223}