Skip to main content

openmls/extensions/
mod.rs

1//! # Extensions
2//!
3//! In MLS, extensions appear in the following places:
4//!
5//! - In [`KeyPackages`](`crate::key_packages`), to describe client capabilities
6//!   and aspects of their participation in the group.
7//!
8//! - In `GroupInfo`, to inform new members of the group's parameters and to
9//!   provide any additional information required to join the group.
10//!
11//! - In the `GroupContext` object, to ensure that all members of the group have
12//!   a consistent view of the parameters in use.
13//!
14//! Note that `GroupInfo` and `GroupContext` are not exposed via OpenMLS' public
15//! API.
16//!
17//! OpenMLS supports the following extensions:
18//!
19//! - [`ApplicationIdExtension`] (KeyPackage extension)
20//! - [`RatchetTreeExtension`] (GroupInfo extension)
21//! - [`RequiredCapabilitiesExtension`] (GroupContext extension)
22//! - [`ExternalPubExtension`] (GroupInfo extension)
23
24use std::{
25    convert::Infallible,
26    fmt::Debug,
27    io::{Read, Write},
28    marker::PhantomData,
29};
30
31use serde::{Deserialize, Serialize};
32
33// Private
34#[cfg(feature = "extensions-draft")]
35mod app_data_dict_extension;
36mod application_id_extension;
37mod codec;
38mod external_pub_extension;
39mod external_sender_extension;
40mod last_resort;
41mod ratchet_tree_extension;
42mod required_capabilities;
43use errors::*;
44
45// Public
46pub mod errors;
47
48// Public re-exports
49#[cfg(feature = "extensions-draft")]
50pub use app_data_dict_extension::{AppDataDictionary, AppDataDictionaryExtension};
51pub use application_id_extension::ApplicationIdExtension;
52pub use external_pub_extension::ExternalPubExtension;
53pub use external_sender_extension::{
54    ExternalSender, ExternalSendersExtension, SenderExtensionIndex,
55};
56pub use last_resort::LastResortExtension;
57pub use ratchet_tree_extension::RatchetTreeExtension;
58pub use required_capabilities::RequiredCapabilitiesExtension;
59
60use tls_codec::{
61    Deserialize as TlsDeserializeTrait, DeserializeBytes, Error, Serialize as TlsSerializeTrait,
62    Size, TlsDeserialize, TlsSerialize, TlsSize,
63};
64
65use crate::{
66    group::GroupContext, key_packages::KeyPackage, messages::group_info::GroupInfo,
67    treesync::LeafNode,
68};
69
70#[cfg(test)]
71mod tests;
72
73/// MLS Extension Types
74///
75/// Copied from draft-ietf-mls-protocol-16:
76///
77/// | Value            | Name                     | Message(s) | Recommended | Reference |
78/// |:-----------------|:-------------------------|:-----------|:------------|:----------|
79/// | 0x0000           | RESERVED                 | N/A        | N/A         | RFC XXXX  |
80/// | 0x0001           | application_id           | LN         | Y           | RFC XXXX  |
81/// | 0x0002           | ratchet_tree             | GI         | Y           | RFC XXXX  |
82/// | 0x0003           | required_capabilities    | GC         | Y           | RFC XXXX  |
83/// | 0x0004           | external_pub             | GI         | Y           | RFC XXXX  |
84/// | 0x0005           | external_senders         | GC         | Y           | RFC XXXX  |
85/// | 0xff00  - 0xffff | Reserved for Private Use | N/A        | N/A         | RFC XXXX  |
86///
87/// Note: OpenMLS does not provide a `Reserved` variant in [ExtensionType].
88#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
89#[cfg_attr(
90    feature = "0-8-1-storage-format",
91    derive(serde::Serialize, serde::Deserialize)
92)]
93#[cfg_attr(
94    not(feature = "0-8-1-storage-format"),
95    derive(
96        openmls_serialization_helpers::Serialize,
97        openmls_serialization_helpers::Deserialize,
98    )
99)]
100pub enum ExtensionType {
101    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 0)]
102    /// The application id extension allows applications to add an explicit,
103    /// application-defined identifier to a KeyPackage.
104    ApplicationId,
105
106    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 1)]
107    /// The ratchet tree extensions provides the whole public state of the
108    /// ratchet tree.
109    RatchetTree,
110
111    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 2)]
112    /// The required capabilities extension defines the configuration of a group
113    /// that imposes certain requirements on clients in the group.
114    RequiredCapabilities,
115
116    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 3)]
117    /// To join a group via an External Commit, a new member needs a GroupInfo
118    /// with an ExternalPub extension present in its extensions field.
119    ExternalPub,
120
121    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 4)]
122    /// Group context extension that contains the credentials and signature keys
123    /// of senders that are permitted to send external proposals to the group.
124    ExternalSenders,
125
126    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 5)]
127    /// KeyPackage extension that marks a KeyPackage for use in a last resort
128    /// scenario.
129    LastResort,
130
131    #[cfg(feature = "extensions-draft")]
132    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 8)]
133    /// AppDataDictionary extension
134    AppDataDictionary,
135
136    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 7)]
137    /// A GREASE extension type for ensuring extensibility.
138    Grease(u16),
139
140    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 6)]
141    /// A currently unknown extension type.
142    Unknown(u16),
143}
144
145impl ExtensionType {
146    /// Returns true for all extension types that are considered "default" by the spec.
147    pub(crate) fn is_default(self) -> bool {
148        match self {
149            ExtensionType::ApplicationId
150            | ExtensionType::RatchetTree
151            | ExtensionType::RequiredCapabilities
152            | ExtensionType::ExternalPub
153            | ExtensionType::ExternalSenders => true,
154            ExtensionType::LastResort | ExtensionType::Grease(_) | ExtensionType::Unknown(_) => {
155                false
156            }
157            #[cfg(feature = "extensions-draft")]
158            ExtensionType::AppDataDictionary => false,
159        }
160    }
161
162    /// Returns whether an extension type is valid when used in leaf nodes.
163    /// Returns None if validity can not be determined.
164    /// This is the case for unknown extensions.
165    //  https://validation.openmls.tech/#valn1601
166    pub(crate) fn is_valid_in_leaf_node(self) -> bool {
167        match self {
168            ExtensionType::Grease(_)
169            | ExtensionType::LastResort
170            | ExtensionType::RatchetTree
171            | ExtensionType::RequiredCapabilities
172            | ExtensionType::ExternalPub
173            | ExtensionType::ExternalSenders => false,
174            ExtensionType::Unknown(_) | ExtensionType::ApplicationId => true,
175            #[cfg(feature = "extensions-draft")]
176            ExtensionType::AppDataDictionary => true,
177        }
178    }
179    pub(crate) fn is_valid_in_group_info(self) -> Option<bool> {
180        match self {
181            ExtensionType::Grease(_)
182            | ExtensionType::LastResort
183            | ExtensionType::RequiredCapabilities
184            | ExtensionType::ExternalSenders
185            | ExtensionType::ApplicationId => Some(false),
186            ExtensionType::RatchetTree | ExtensionType::ExternalPub => Some(true),
187            ExtensionType::Unknown(_) => None,
188            #[cfg(feature = "extensions-draft")]
189            ExtensionType::AppDataDictionary => Some(true),
190        }
191    }
192
193    pub(crate) fn is_valid_in_key_package(self) -> bool {
194        match self {
195            ExtensionType::Grease(_)
196            | ExtensionType::RatchetTree
197            | ExtensionType::RequiredCapabilities
198            | ExtensionType::ExternalPub
199            | ExtensionType::ExternalSenders
200            | ExtensionType::ApplicationId => false,
201            ExtensionType::Unknown(_) | ExtensionType::LastResort => true,
202            #[cfg(feature = "extensions-draft")]
203            ExtensionType::AppDataDictionary => true,
204        }
205    }
206
207    pub(crate) fn is_valid_in_group_context(self) -> bool {
208        match self {
209            ExtensionType::RequiredCapabilities
210            | ExtensionType::ExternalSenders
211            | ExtensionType::Unknown(_) => true,
212            #[cfg(feature = "extensions-draft")]
213            ExtensionType::AppDataDictionary => true,
214            _ => false,
215        }
216    }
217
218    /// Returns true if this is a GREASE extension type.
219    ///
220    /// GREASE values are used to ensure implementations properly handle unknown
221    /// extension types. See [RFC 9420 Section 13.5](https://www.rfc-editor.org/rfc/rfc9420.html#section-13.5).
222    pub fn is_grease(&self) -> bool {
223        matches!(self, ExtensionType::Grease(_))
224    }
225}
226
227impl Size for ExtensionType {
228    fn tls_serialized_len(&self) -> usize {
229        2
230    }
231}
232
233impl TlsDeserializeTrait for ExtensionType {
234    fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, Error>
235    where
236        Self: Sized,
237    {
238        let mut extension_type = [0u8; 2];
239        bytes.read_exact(&mut extension_type)?;
240
241        Ok(ExtensionType::from(u16::from_be_bytes(extension_type)))
242    }
243}
244
245impl DeserializeBytes for ExtensionType {
246    fn tls_deserialize_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error>
247    where
248        Self: Sized,
249    {
250        let mut bytes_ref = bytes;
251        let extension_type = ExtensionType::tls_deserialize(&mut bytes_ref)?;
252        Ok((extension_type, bytes_ref))
253    }
254}
255
256impl TlsSerializeTrait for ExtensionType {
257    fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, Error> {
258        writer.write_all(&u16::from(*self).to_be_bytes())?;
259
260        Ok(2)
261    }
262}
263
264impl From<u16> for ExtensionType {
265    fn from(a: u16) -> Self {
266        match a {
267            1 => ExtensionType::ApplicationId,
268            2 => ExtensionType::RatchetTree,
269            3 => ExtensionType::RequiredCapabilities,
270            4 => ExtensionType::ExternalPub,
271            5 => ExtensionType::ExternalSenders,
272            #[cfg(feature = "extensions-draft")]
273            6 => ExtensionType::AppDataDictionary,
274            10 => ExtensionType::LastResort,
275            unknown if crate::grease::is_grease_value(unknown) => ExtensionType::Grease(unknown),
276            unknown => ExtensionType::Unknown(unknown),
277        }
278    }
279}
280
281impl From<ExtensionType> for u16 {
282    fn from(value: ExtensionType) -> Self {
283        match value {
284            ExtensionType::ApplicationId => 1,
285            ExtensionType::RatchetTree => 2,
286            ExtensionType::RequiredCapabilities => 3,
287            ExtensionType::ExternalPub => 4,
288            ExtensionType::ExternalSenders => 5,
289            #[cfg(feature = "extensions-draft")]
290            ExtensionType::AppDataDictionary => 6,
291            ExtensionType::LastResort => 10,
292            ExtensionType::Grease(value) => value,
293            ExtensionType::Unknown(unknown) => unknown,
294        }
295    }
296}
297
298/// # Extension
299///
300/// An extension is one of the [`Extension`] enum values.
301/// The enum provides a set of common functionality for all extensions.
302///
303/// See the individual extensions for more details on each extension.
304///
305/// ```c
306/// // draft-ietf-mls-protocol-16
307/// struct {
308///     ExtensionType extension_type;
309///     opaque extension_data<V>;
310/// } Extension;
311/// ```
312#[derive(Debug, Clone, PartialEq, Eq)]
313#[cfg_attr(
314    feature = "0-8-1-storage-format",
315    derive(serde::Serialize, serde::Deserialize)
316)]
317#[cfg_attr(
318    not(feature = "0-8-1-storage-format"),
319    derive(
320        openmls_serialization_helpers::Serialize,
321        openmls_serialization_helpers::Deserialize,
322    )
323)]
324pub enum Extension {
325    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 0)]
326    /// An [`ApplicationIdExtension`]
327    ApplicationId(ApplicationIdExtension),
328
329    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 1)]
330    /// A [`RatchetTreeExtension`]
331    RatchetTree(RatchetTreeExtension),
332
333    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 2)]
334    /// A [`RequiredCapabilitiesExtension`]
335    RequiredCapabilities(RequiredCapabilitiesExtension),
336
337    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 3)]
338    /// An [`ExternalPubExtension`]
339    ExternalPub(ExternalPubExtension),
340
341    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 4)]
342    /// An [`ExternalSendersExtension`]
343    ExternalSenders(ExternalSendersExtension),
344
345    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 7)]
346    /// An [`AppDataDictionaryExtension`]
347    #[cfg(feature = "extensions-draft")]
348    AppDataDictionary(AppDataDictionaryExtension),
349
350    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 5)]
351    /// A [`LastResortExtension`]
352    LastResort(LastResortExtension),
353
354    #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 6)]
355    /// A currently unknown extension.
356    Unknown(u16, UnknownExtension),
357}
358
359/// A unknown/unparsed extension represented by raw bytes.
360#[derive(
361    PartialEq, Eq, Clone, Debug, Serialize, Deserialize, TlsSize, TlsSerialize, TlsDeserialize,
362)]
363pub struct UnknownExtension(pub Vec<u8>);
364
365/// A Extension for Object of type T
366#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
367pub struct Extensions<T> {
368    unique: Vec<Extension>,
369    #[serde(skip)]
370    _object: core::marker::PhantomData<T>,
371}
372
373#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, TlsSize, TlsSerialize, TlsDeserialize)]
374/// Any object
375pub struct AnyObject;
376
377impl<T> Default for Extensions<T> {
378    fn default() -> Self {
379        Self {
380            unique: vec![],
381            _object: PhantomData,
382        }
383    }
384}
385
386impl<T> Size for Extensions<T> {
387    fn tls_serialized_len(&self) -> usize {
388        Vec::tls_serialized_len(&self.unique)
389    }
390}
391
392impl<T> TlsSerializeTrait for Extensions<T> {
393    fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, Error> {
394        self.unique.tls_serialize(writer)
395    }
396}
397
398impl<T: ExtensionValidator> TlsDeserializeTrait for Extensions<T>
399where
400    InvalidExtensionError: From<T::Error>,
401{
402    fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, Error>
403    where
404        Self: Sized,
405    {
406        let candidate: Vec<Extension> = Vec::tls_deserialize(bytes)?;
407        Extensions::<T>::try_from(candidate)
408            .map_err(|_| Error::DecodingError("Found duplicate extensions".into()))
409    }
410}
411
412impl<T: ExtensionValidator> DeserializeBytes for Extensions<T>
413where
414    InvalidExtensionError: From<T::Error>,
415{
416    fn tls_deserialize_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error>
417    where
418        Self: Sized,
419    {
420        let mut bytes_ref = bytes;
421        let extensions = Extensions::<T>::tls_deserialize(&mut bytes_ref)?;
422        Ok((extensions, bytes_ref))
423    }
424}
425
426impl<T: ExtensionValidator> Extensions<T> {
427    /// Create an empty extension list.
428    pub fn empty() -> Self {
429        Self {
430            unique: vec![],
431            _object: PhantomData,
432        }
433    }
434
435    /// Returns an iterator over the extension list.
436    pub fn iter(&self) -> impl Iterator<Item = &Extension> {
437        self.unique.iter()
438    }
439
440    /// Remove an extension from the extension list.
441    ///
442    /// Returns the removed extension or `None` when there is no extension with
443    /// the given extension type.
444    pub fn remove(&mut self, extension_type: ExtensionType) -> Option<Extension> {
445        if let Some(pos) = self
446            .unique
447            .iter()
448            .position(|ext| ext.extension_type() == extension_type)
449        {
450            Some(self.unique.remove(pos))
451        } else {
452            None
453        }
454    }
455
456    /// Returns `true` iff the extension list contains an extension with the
457    /// given extension type.
458    pub fn contains(&self, extension_type: ExtensionType) -> bool {
459        self.unique
460            .iter()
461            .any(|ext| ext.extension_type() == extension_type)
462    }
463}
464
465impl<T> Extensions<T>
466where
467    T: ExtensionValidator,
468    InvalidExtensionError: From<T::Error>,
469{
470    /// Create an extension list with a single extension.
471    pub fn single(extension: Extension) -> Result<Self, InvalidExtensionError> {
472        T::validate_extension_type(&extension)?;
473        Ok(Self {
474            unique: vec![extension],
475            _object: PhantomData,
476        })
477    }
478
479    /// Create an extension list with multiple extensions.
480    ///
481    /// This function will fail when the list of extensions contains duplicate
482    /// extension types.
483    pub fn from_vec(extensions: Vec<Extension>) -> Result<Self, InvalidExtensionError> {
484        extensions.try_into()
485    }
486
487    /// Validate if the extensions are valid for this context
488    pub fn validate<'a>(
489        extensions: impl Iterator<Item = &'a Extension>,
490    ) -> Result<(), InvalidExtensionError> {
491        for ext in extensions {
492            T::validate_extension_type(ext)?;
493        }
494        Ok(())
495    }
496
497    /// Add an extension to the extension list.
498    ///
499    /// Returns an error when there already is an extension with the same
500    /// extension type.
501    pub fn add(&mut self, extension: Extension) -> Result<(), InvalidExtensionError> {
502        T::validate_extension_type(&extension)?;
503        if self.contains(extension.extension_type()) {
504            return Err(InvalidExtensionError::Duplicate);
505        }
506
507        self.unique.push(extension);
508
509        Ok(())
510    }
511
512    /// Add an extension to the extension list (or replace an existing one.)
513    ///
514    /// Returns the replaced extension (if any).
515    pub fn add_or_replace(
516        &mut self,
517        extension: Extension,
518    ) -> Result<Option<Extension>, InvalidExtensionError> {
519        T::validate_extension_type(&extension)?;
520        let replaced = self.remove(extension.extension_type());
521        self.unique.push(extension);
522        Ok(replaced)
523    }
524}
525
526impl Extensions<AnyObject> {
527    /// Assume that the extensions contain the given extension type.
528    ///
529    /// # Safety
530    ///
531    /// The caller must guarantee that the extensions are of the correct type.
532    #[cfg(feature = "unchecked-conversions")]
533    pub fn into_unchecked<T>(self) -> Extensions<T> {
534        Extensions {
535            unique: self.unique,
536            _object: PhantomData,
537        }
538    }
539}
540
541/// Can be implemented by a type to validate extensions.
542pub trait ExtensionValidator {
543    /// The error returned by the validator
544    type Error;
545
546    /// Check if the extension is valid.
547    fn validate_extension_type(ext: &Extension) -> Result<(), Self::Error>;
548}
549
550impl ExtensionValidator for AnyObject {
551    type Error = Infallible;
552
553    fn validate_extension_type(_ext: &Extension) -> Result<(), Infallible> {
554        Ok(())
555    }
556}
557
558impl<T: ExtensionValidator> TryFrom<Vec<Extension>> for Extensions<T>
559where
560    InvalidExtensionError: From<T::Error>,
561{
562    type Error = InvalidExtensionError;
563
564    fn try_from(candidate: Vec<Extension>) -> Result<Self, Self::Error> {
565        let mut unique: Vec<Extension> = Vec::new();
566        for extension in candidate.into_iter() {
567            T::validate_extension_type(&extension)?;
568
569            if unique
570                .iter()
571                .any(|ext| ext.extension_type() == extension.extension_type())
572            {
573                return Err(InvalidExtensionError::Duplicate);
574            } else {
575                unique.push(extension);
576            }
577        }
578
579        Ok(Self {
580            unique,
581            _object: PhantomData,
582        })
583    }
584}
585
586// https://validation.openmls.tech/#valn1602
587impl ExtensionValidator for GroupInfo {
588    type Error = ExtensionTypeNotValidInGroupInfoError;
589
590    fn validate_extension_type(
591        ext: &Extension,
592    ) -> Result<(), ExtensionTypeNotValidInGroupInfoError> {
593        if ext.extension_type().is_valid_in_group_info() == Some(true)
594            || ext.extension_type().is_valid_in_group_info().is_none()
595        {
596            Ok(())
597        } else {
598            Err(ExtensionTypeNotValidInGroupInfoError(ext.extension_type()))
599        }
600    }
601}
602
603// https://validation.openmls.tech/#valn1603
604impl ExtensionValidator for GroupContext {
605    type Error = ExtensionTypeNotValidInGroupContextError;
606
607    fn validate_extension_type(
608        ext: &Extension,
609    ) -> Result<(), ExtensionTypeNotValidInGroupContextError> {
610        if ext.extension_type().is_valid_in_group_context() {
611            Ok(())
612        } else {
613            Err(ExtensionTypeNotValidInGroupContextError(
614                ext.extension_type(),
615            ))
616        }
617    }
618}
619
620// https://validation.openmls.tech/#valn1604
621impl ExtensionValidator for KeyPackage {
622    type Error = ExtensionTypeNotValidInKeyPackageError;
623
624    fn validate_extension_type(
625        ext: &Extension,
626    ) -> Result<(), ExtensionTypeNotValidInKeyPackageError> {
627        if ext.extension_type().is_valid_in_key_package() {
628            Ok(())
629        } else {
630            Err(ExtensionTypeNotValidInKeyPackageError(ext.extension_type()))
631        }
632    }
633}
634
635// https://validation.openmls.tech/#valn1601
636impl ExtensionValidator for LeafNode {
637    type Error = ExtensionTypeNotValidInLeafNodeError;
638
639    fn validate_extension_type(
640        ext: &Extension,
641    ) -> Result<(), ExtensionTypeNotValidInLeafNodeError> {
642        if ext.extension_type().is_valid_in_leaf_node() {
643            Ok(())
644        } else {
645            Err(ExtensionTypeNotValidInLeafNodeError(ext.extension_type()))
646        }
647    }
648}
649
650impl<T> Extensions<T> {
651    fn find_by_type(&self, extension_type: ExtensionType) -> Option<&Extension> {
652        self.unique
653            .iter()
654            .find(|ext| ext.extension_type() == extension_type)
655    }
656
657    /// Get a reference to the [`ApplicationIdExtension`] if there is any.
658    pub fn application_id(&self) -> Option<&ApplicationIdExtension> {
659        self.find_by_type(ExtensionType::ApplicationId)
660            .and_then(|e| match e {
661                Extension::ApplicationId(e) => Some(e),
662                _ => None,
663            })
664    }
665
666    /// Get a reference to the [`RatchetTreeExtension`] if there is any.
667    pub fn ratchet_tree(&self) -> Option<&RatchetTreeExtension> {
668        self.find_by_type(ExtensionType::RatchetTree)
669            .and_then(|e| match e {
670                Extension::RatchetTree(e) => Some(e),
671                _ => None,
672            })
673    }
674
675    /// Get a reference to the [`RequiredCapabilitiesExtension`] if there is
676    /// any.
677    pub fn required_capabilities(&self) -> Option<&RequiredCapabilitiesExtension> {
678        self.find_by_type(ExtensionType::RequiredCapabilities)
679            .and_then(|e| match e {
680                Extension::RequiredCapabilities(e) => Some(e),
681                _ => None,
682            })
683    }
684
685    /// Get a reference to the [`ExternalPubExtension`] if there is any.
686    pub fn external_pub(&self) -> Option<&ExternalPubExtension> {
687        self.find_by_type(ExtensionType::ExternalPub)
688            .and_then(|e| match e {
689                Extension::ExternalPub(e) => Some(e),
690                _ => None,
691            })
692    }
693
694    /// Get a reference to the [`ExternalSendersExtension`] if there is any.
695    pub fn external_senders(&self) -> Option<&ExternalSendersExtension> {
696        self.find_by_type(ExtensionType::ExternalSenders)
697            .and_then(|e| match e {
698                Extension::ExternalSenders(e) => Some(e),
699                _ => None,
700            })
701    }
702
703    #[cfg(feature = "extensions-draft")]
704    /// Get a reference to the [`AppDataDictionaryExtension`] if there is any.
705    pub fn app_data_dictionary(&self) -> Option<&AppDataDictionaryExtension> {
706        self.find_by_type(ExtensionType::AppDataDictionary)
707            .and_then(|e| match e {
708                Extension::AppDataDictionary(e) => Some(e),
709                _ => None,
710            })
711    }
712
713    /// Get a reference to the [`UnknownExtension`] with the given type id, if there is any.
714    pub fn unknown(&self, extension_type_id: u16) -> Option<&UnknownExtension> {
715        let extension_type: ExtensionType = extension_type_id.into();
716
717        match extension_type {
718            ExtensionType::Unknown(_) => self.find_by_type(extension_type).and_then(|e| match e {
719                Extension::Unknown(_, e) => Some(e),
720                _ => None,
721            }),
722            _ => None,
723        }
724    }
725}
726
727impl Extension {
728    /// Get a reference to this extension as [`ApplicationIdExtension`].
729    /// Returns an [`ExtensionError::InvalidExtensionType`] if called on an
730    /// [`Extension`] that's not an [`ApplicationIdExtension`].
731    pub fn as_application_id_extension(&self) -> Result<&ApplicationIdExtension, ExtensionError> {
732        match self {
733            Self::ApplicationId(e) => Ok(e),
734            _ => Err(ExtensionError::InvalidExtensionType(
735                "This is not an ApplicationIdExtension".into(),
736            )),
737        }
738    }
739    #[cfg(feature = "extensions-draft")]
740    /// Get a reference to this extension as [`AppDataDictionaryExtension`].
741    /// Returns an [`ExtensionError::InvalidExtensionType`] if called on an
742    /// [`Extension`] that's not an [`AppDataDictionaryExtension`].
743    pub fn as_app_data_dictionary_extension(
744        &self,
745    ) -> Result<&AppDataDictionaryExtension, ExtensionError> {
746        match self {
747            Self::AppDataDictionary(e) => Ok(e),
748            _ => Err(ExtensionError::InvalidExtensionType(
749                "This is not an AppDataDictionaryExtension".into(),
750            )),
751        }
752    }
753
754    /// Get a reference to this extension as [`RatchetTreeExtension`].
755    /// Returns an [`ExtensionError::InvalidExtensionType`] if called on
756    /// an [`Extension`] that's not a [`RatchetTreeExtension`].
757    pub fn as_ratchet_tree_extension(&self) -> Result<&RatchetTreeExtension, ExtensionError> {
758        match self {
759            Self::RatchetTree(rte) => Ok(rte),
760            _ => Err(ExtensionError::InvalidExtensionType(
761                "This is not a RatchetTreeExtension".into(),
762            )),
763        }
764    }
765
766    /// Get a reference to this extension as [`RequiredCapabilitiesExtension`].
767    /// Returns an [`ExtensionError::InvalidExtensionType`] error if called on
768    /// an [`Extension`] that's not a [`RequiredCapabilitiesExtension`].
769    pub fn as_required_capabilities_extension(
770        &self,
771    ) -> Result<&RequiredCapabilitiesExtension, ExtensionError> {
772        match self {
773            Self::RequiredCapabilities(e) => Ok(e),
774            _ => Err(ExtensionError::InvalidExtensionType(
775                "This is not a RequiredCapabilitiesExtension".into(),
776            )),
777        }
778    }
779
780    /// Get a reference to this extension as [`ExternalPubExtension`].
781    /// Returns an [`ExtensionError::InvalidExtensionType`] error if called on
782    /// an [`Extension`] that's not a [`ExternalPubExtension`].
783    pub fn as_external_pub_extension(&self) -> Result<&ExternalPubExtension, ExtensionError> {
784        match self {
785            Self::ExternalPub(e) => Ok(e),
786            _ => Err(ExtensionError::InvalidExtensionType(
787                "This is not an ExternalPubExtension".into(),
788            )),
789        }
790    }
791
792    /// Get a reference to this extension as [`ExternalSendersExtension`].
793    /// Returns an [`ExtensionError::InvalidExtensionType`] error if called on
794    /// an [`Extension`] that's not a [`ExternalSendersExtension`].
795    pub fn as_external_senders_extension(
796        &self,
797    ) -> Result<&ExternalSendersExtension, ExtensionError> {
798        match self {
799            Self::ExternalSenders(e) => Ok(e),
800            _ => Err(ExtensionError::InvalidExtensionType(
801                "This is not an ExternalSendersExtension".into(),
802            )),
803        }
804    }
805
806    /// Returns the [`ExtensionType`]
807    #[inline]
808    pub const fn extension_type(&self) -> ExtensionType {
809        match self {
810            Extension::ApplicationId(_) => ExtensionType::ApplicationId,
811            Extension::RatchetTree(_) => ExtensionType::RatchetTree,
812            Extension::RequiredCapabilities(_) => ExtensionType::RequiredCapabilities,
813            Extension::ExternalPub(_) => ExtensionType::ExternalPub,
814            Extension::ExternalSenders(_) => ExtensionType::ExternalSenders,
815            #[cfg(feature = "extensions-draft")]
816            Extension::AppDataDictionary(_) => ExtensionType::AppDataDictionary,
817            Extension::LastResort(_) => ExtensionType::LastResort,
818            Extension::Unknown(kind, _) => ExtensionType::Unknown(*kind),
819        }
820    }
821}
822
823macro_rules! impl_from_extensions_validator {
824    ($validator:ty, $error:ty) => {
825        impl From<Extensions<$validator>> for Extensions<AnyObject> {
826            fn from(value: Extensions<$validator>) -> Self {
827                Extensions {
828                    unique: value.unique,
829                    _object: PhantomData,
830                }
831            }
832        }
833
834        impl TryFrom<Extensions<AnyObject>> for Extensions<$validator> {
835            type Error = $error;
836
837            fn try_from(value: Extensions<AnyObject>) -> Result<Self, $error> {
838                value
839                    .unique
840                    .iter()
841                    .try_for_each(<$validator as ExtensionValidator>::validate_extension_type)?;
842
843                Ok(Extensions {
844                    unique: value.unique,
845                    _object: PhantomData,
846                })
847            }
848        }
849    };
850}
851
852impl_from_extensions_validator!(GroupContext, ExtensionTypeNotValidInGroupContextError);
853impl_from_extensions_validator!(LeafNode, ExtensionTypeNotValidInLeafNodeError);
854impl_from_extensions_validator!(KeyPackage, ExtensionTypeNotValidInKeyPackageError);
855
856#[cfg(any(feature = "test-utils", test))]
857impl Extensions<AnyObject> {
858    /// Coerces the extensions to an Extensions with the given validator. Unsafe.
859    pub(crate) fn coerce<T: ExtensionValidator>(self) -> Extensions<T> {
860        Extensions {
861            unique: self.unique,
862            _object: PhantomData,
863        }
864    }
865}
866#[cfg(test)]
867mod test {
868    use itertools::Itertools;
869    use tls_codec::{Deserialize, Serialize, VLBytes};
870
871    use crate::{ciphersuite::HpkePublicKey, extensions::*};
872
873    #[test]
874    fn add() {
875        let mut extensions: Extensions<AnyObject> = Extensions::default();
876        extensions
877            .add(Extension::RequiredCapabilities(
878                RequiredCapabilitiesExtension::default(),
879            ))
880            .unwrap();
881        assert!(extensions
882            .add(Extension::RequiredCapabilities(
883                RequiredCapabilitiesExtension::default()
884            ))
885            .is_err());
886    }
887
888    #[test]
889    fn add_try_from() {
890        // Create some extensions with different extension types and test that
891        // duplicates are rejected. The extension content does not matter in this test.
892        let ext_x = Extension::ApplicationId(ApplicationIdExtension::new(b"Test"));
893        let ext_y = Extension::RequiredCapabilities(RequiredCapabilitiesExtension::default());
894
895        let tests = [
896            (vec![], true),
897            (vec![ext_x.clone()], true),
898            (vec![ext_x.clone(), ext_x.clone()], false),
899            (vec![ext_x.clone(), ext_x.clone(), ext_x.clone()], false),
900            (vec![ext_y.clone()], true),
901            (vec![ext_y.clone(), ext_y.clone()], false),
902            (vec![ext_y.clone(), ext_y.clone(), ext_y.clone()], false),
903            (vec![ext_x.clone(), ext_y.clone()], true),
904            (vec![ext_y.clone(), ext_x.clone()], true),
905            (vec![ext_x.clone(), ext_x.clone(), ext_y.clone()], false),
906            (vec![ext_y.clone(), ext_y.clone(), ext_x.clone()], false),
907            (vec![ext_x.clone(), ext_y.clone(), ext_y.clone()], false),
908            (vec![ext_y.clone(), ext_x.clone(), ext_x.clone()], false),
909            (vec![ext_x.clone(), ext_y.clone(), ext_x.clone()], false),
910            (vec![ext_y.clone(), ext_x, ext_y], false),
911        ];
912
913        for (test, should_work) in tests.into_iter() {
914            // Test `add`.
915            {
916                let mut extensions: Extensions<AnyObject> = Extensions::default();
917
918                let mut works = true;
919                for ext in test.iter() {
920                    match extensions.add(ext.clone()) {
921                        Ok(_) => {}
922                        Err(InvalidExtensionError::Duplicate) => {
923                            works = false;
924                        }
925                        _ => panic!("This should have never happened."),
926                    }
927                }
928
929                println!("{:?}, {:?}", test.clone(), should_work);
930                assert_eq!(works, should_work);
931            }
932
933            // Test `try_from`.
934            if should_work {
935                assert!(Extensions::<AnyObject>::try_from(test).is_ok());
936            } else {
937                assert!(Extensions::<AnyObject>::try_from(test).is_err());
938            }
939        }
940    }
941
942    #[test]
943    fn ensure_ordering() {
944        // Create some extensions with different extension types and test
945        // that all permutations keep their order after being (de)serialized.
946        // The extension content does not matter in this test.
947        let ext_x = Extension::ApplicationId(ApplicationIdExtension::new(b"Test"));
948        let ext_y = Extension::ExternalPub(ExternalPubExtension::new(HpkePublicKey::new(vec![])));
949        let ext_z = Extension::RequiredCapabilities(RequiredCapabilitiesExtension::default());
950
951        for candidate in [ext_x, ext_y, ext_z]
952            .into_iter()
953            .permutations(3)
954            .collect::<Vec<_>>()
955        {
956            let candidate: Extensions<AnyObject> = Extensions::try_from(candidate).unwrap();
957            let bytes = candidate.tls_serialize_detached().unwrap();
958            let got = Extensions::tls_deserialize(&mut bytes.as_slice()).unwrap();
959            assert_eq!(candidate, got);
960        }
961    }
962
963    #[test]
964    fn that_unknown_extensions_are_de_serialized_correctly() {
965        let extension_types = [0x0000u16, 0x0A0A, 0x7A7A, 0xF100, 0xFFFF];
966        let extension_datas = [vec![], vec![0], vec![1, 2, 3]];
967
968        for extension_type in extension_types.into_iter() {
969            for extension_data in extension_datas.iter() {
970                // Construct an unknown extension manually.
971                let test = {
972                    let mut buf = extension_type.to_be_bytes().to_vec();
973                    buf.append(
974                        &mut VLBytes::new(extension_data.clone())
975                            .tls_serialize_detached()
976                            .unwrap(),
977                    );
978                    buf
979                };
980
981                // Test deserialization.
982                let got = Extension::tls_deserialize_exact(&test).unwrap();
983
984                match got {
985                    Extension::Unknown(got_extension_type, ref got_extension_data) => {
986                        assert_eq!(extension_type, got_extension_type);
987                        assert_eq!(extension_data, &got_extension_data.0);
988                    }
989                    other => panic!("Expected `Extension::Unknown`, got {other:?}"),
990                }
991
992                // Test serialization.
993                let got_serialized = got.tls_serialize_detached().unwrap();
994                assert_eq!(test, got_serialized);
995            }
996        }
997    }
998}