Skip to main content

openmls/group/mls_group/
config.rs

1//! Configuration module for [`MlsGroup`] configurations.
2//!
3//! ## Building an MlsGroupCreateConfig
4//! The [`MlsGroupCreateConfigBuilder`] makes it easy to build configurations for the
5//! [`MlsGroup`].
6//!
7//! ```
8//! use openmls::prelude::*;
9//!
10//! let group_config = MlsGroupCreateConfig::builder()
11//!     .use_ratchet_tree_extension(true)
12//!     .build();
13//! ```
14//!
15//! See [`MlsGroupCreateConfigBuilder`](MlsGroupCreateConfigBuilder#implementations) for
16//! all options that can be configured.
17//!
18//! ### Wire format policies
19//! Only some combination of possible wire formats are valid within OpenMLS.
20//! The [`WIRE_FORMAT_POLICIES`] lists all valid options that can be set.
21//!
22//! ```
23//! use openmls::prelude::*;
24//!
25//! let group_config = MlsGroupCreateConfig::builder()
26//!     .wire_format_policy(MIXED_CIPHERTEXT_WIRE_FORMAT_POLICY)
27//!     .build();
28//! ```
29
30#[cfg(not(target_arch = "wasm32"))]
31use std::time::SystemTime;
32#[cfg(target_arch = "wasm32")]
33use web_time::SystemTime;
34
35use super::*;
36use crate::{
37    extensions::Extensions,
38    key_packages::Lifetime,
39    tree::sender_ratchet::SenderRatchetConfiguration,
40    treesync::{errors::LeafNodeValidationError, node::leaf_node::Capabilities},
41};
42use serde::{Deserialize, Serialize};
43
44/// Configures the automatic deletion of past epoch secrets.
45///
46/// **WARNING**
47///
48/// Policies other than `MaxEpochs(0)` enable the storage of message secrets from past epochs.
49/// It is a trade-off between functionality and forward secrecy and should only be enabled
50/// if the Delivery Service cannot guarantee that application messages will be sent in
51/// the same epoch in which they were generated. The number for `max_epochs` should be
52/// as low as possible.
53///
54/// If the MaxEpochs policy is set to MaxEpochs(usize::MAX), if will be returned
55/// as KeepAll after deserialization due to backwards compatibility constraints.
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub enum PastEpochDeletionPolicy {
58    /// Keep at most `n` past epoch secrets.
59    MaxEpochs(usize),
60    /// Keep all past epoch secrets.
61    ///
62    /// NOTE: The application is responsible for deleting past epoch secrets when
63    /// `KeepAll` is set. Past epoch secrets can be deleted manually using:
64    /// - [`MlsGroup::delete_past_epoch_secrets()`]
65    KeepAll,
66}
67
68impl Default for PastEpochDeletionPolicy {
69    fn default() -> Self {
70        Self::MaxEpochs(0)
71    }
72}
73
74impl Serialize for PastEpochDeletionPolicy {
75    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
76    where
77        S: serde::Serializer,
78    {
79        let usize = match self {
80            Self::MaxEpochs(epochs) => *epochs,
81            Self::KeepAll => usize::MAX,
82        };
83        serializer.serialize_u64(usize as u64)
84    }
85}
86
87impl<'de> Deserialize<'de> for PastEpochDeletionPolicy {
88    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89    where
90        D: serde::Deserializer<'de>,
91    {
92        let epochs = u64::deserialize(deserializer)?;
93        let epochs = usize::try_from(epochs).map_err(serde::de::Error::custom)?;
94        if epochs == usize::MAX {
95            Ok(Self::KeepAll)
96        } else {
97            Ok(Self::MaxEpochs(epochs))
98        }
99    }
100}
101
102/// The input to [`MlsGroup::delete_past_epoch_secrets()`].
103///
104/// This struct can be used for manual deletion of past epoch secrets by the application.
105///
106/// An [`MlsGroup`] also applies automatic deletion of past epoch secrets by default.
107///
108/// For more information, see [`PastEpochDeletionPolicy`] and [`MlsGroup::set_past_epoch_deletion_policy()`].
109///
110/// These methods can be used by the application to set up time-based deletion schedules:
111/// - [`PastEpochDeletion::before_timestamp()`]
112/// - [`PastEpochDeletion::older_than_duration()`]
113///
114/// **NOTE**: Epoch secrets that were created using `openmls=0.8.1` or earlier will not yet include a timestamp.
115/// After migration, these may not always be deleted by applying a time-based [`PastEpochDeletion`]. Only if a new secret that does include a timestamp is added later, and it matches the time-based condition in the [`PastEpochDeletion`], all earlier past epoch secrets without timestamps will be deleted, as well. However, otherwise, past epoch secrets without timestamps will not be affected by applying time-based [`PastEpochDeletion`]s.
116///
117/// To manually delete all past epoch secrets without timestamps, see:
118/// [`PastEpochDeletion::delete_all_without_timestamps()`]
119pub struct PastEpochDeletion {
120    pub(crate) config: Option<PastEpochDeletionTimeConfig>,
121    pub(crate) max_past_epochs: Option<usize>,
122}
123
124/// A duration or timestamp before which to delete past epoch secrets.
125pub(crate) enum PastEpochDeletionTimeConfig {
126    OlderThanDuration(std::time::Duration),
127    BeforeTimestamp(SystemTime),
128    DeleteAllWithoutTimestamp,
129}
130
131impl PastEpochDeletion {
132    /// Delete all past epoch secrets older than a provided duration.
133    pub fn older_than_duration(duration: std::time::Duration) -> Self {
134        Self {
135            config: Some(PastEpochDeletionTimeConfig::OlderThanDuration(duration)),
136            max_past_epochs: None,
137        }
138    }
139
140    /// Delete all past epoch secrets before a provided timestamp.
141    pub fn before_timestamp(timestamp: SystemTime) -> Self {
142        Self {
143            config: Some(PastEpochDeletionTimeConfig::BeforeTimestamp(timestamp)),
144            max_past_epochs: None,
145        }
146    }
147
148    /// Delete all past epoch secrets without timestamps.
149    ///
150    /// NOTE: This will delete all past epoch secrets having the legacy
151    /// format that does not include a timestamp.
152    pub fn delete_all_without_timestamps() -> Self {
153        Self {
154            config: Some(PastEpochDeletionTimeConfig::DeleteAllWithoutTimestamp),
155            max_past_epochs: None,
156        }
157    }
158
159    /// Delete all past epoch secrets.
160    pub fn delete_all() -> Self {
161        Self {
162            config: None,
163            max_past_epochs: None,
164        }
165    }
166
167    /// Set the number of `max_past_epochs` that should be kept, at most.
168    pub fn max_past_epochs(mut self, max_past_epochs: usize) -> Self {
169        self.max_past_epochs = Some(max_past_epochs);
170        self
171    }
172}
173
174impl PastEpochDeletionPolicy {
175    pub(crate) fn max_epochs(&self) -> Option<usize> {
176        match self {
177            Self::MaxEpochs(epochs) => Some(*epochs),
178            Self::KeepAll => None,
179        }
180    }
181}
182
183/// The [`MlsGroupJoinConfig`] contains all configuration parameters that are
184/// relevant to group operation at runtime. It is used to configure the group's
185/// behaviour when joining an existing group. To configure a newly created
186/// group, use [`MlsGroupCreateConfig`].
187#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
188pub struct MlsGroupJoinConfig {
189    /// Defines the wire format policy for outgoing and incoming handshake messages.
190    /// Application are always encrypted regardless.
191    pub(crate) wire_format_policy: WireFormatPolicy,
192    /// Size of padding in bytes
193    pub(crate) padding_size: usize,
194    /// Maximum number of past epochs for which application messages
195    /// can be decrypted. The default is 0.
196    #[serde(alias = "max_past_epochs")]
197    // alias for backwards compatibility after renaming field
198    pub(crate) past_epoch_deletion_policy: PastEpochDeletionPolicy,
199    /// Number of resumption secrets to keep
200    pub(crate) number_of_resumption_psks: usize,
201    /// Flag to indicate the Ratchet Tree Extension should be used
202    pub(crate) use_ratchet_tree_extension: bool,
203    /// Sender ratchet configuration
204    pub(crate) sender_ratchet_configuration: SenderRatchetConfiguration,
205}
206
207impl MlsGroupJoinConfig {
208    /// Returns a builder for [`MlsGroupJoinConfig`].
209    pub fn builder() -> MlsGroupJoinConfigBuilder {
210        MlsGroupJoinConfigBuilder::new()
211    }
212
213    /// Returns the wire format policy set in this  [`MlsGroupJoinConfig`].
214    pub fn wire_format_policy(&self) -> WireFormatPolicy {
215        self.wire_format_policy
216    }
217
218    /// Returns the padding size set in this  [`MlsGroupJoinConfig`].
219    pub fn padding_size(&self) -> usize {
220        self.padding_size
221    }
222
223    /// Returns the [`SenderRatchetConfiguration`] set in this  [`MlsGroupJoinConfig`].
224    pub fn sender_ratchet_configuration(&self) -> &SenderRatchetConfiguration {
225        &self.sender_ratchet_configuration
226    }
227
228    /// Returns the max past epochs configured in this [`MlsGroupJoinConfig`]
229    pub(crate) fn max_past_epochs(&self) -> Option<usize> {
230        self.past_epoch_deletion_policy.max_epochs()
231    }
232
233    pub(crate) fn past_epoch_deletion_policy(&self) -> &PastEpochDeletionPolicy {
234        &self.past_epoch_deletion_policy
235    }
236}
237
238/// Specifies configuration for the creation of an [`MlsGroup`]. Refer to the
239/// [User Manual](https://book.openmls.tech/user_manual/group_config.html) for
240/// more information about the different configuration values.
241#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
242pub struct MlsGroupCreateConfig {
243    /// Capabilities advertised in the creator's leaf node
244    pub(crate) capabilities: Capabilities,
245    /// Lifetime of the own leaf node
246    pub(crate) lifetime: Lifetime,
247    /// Ciphersuite and protocol version
248    pub(crate) ciphersuite: Ciphersuite,
249    /// Configuration parameters relevant to group operation at runtime
250    pub(crate) join_config: MlsGroupJoinConfig,
251    /// List of initial group context extensions
252    pub(crate) group_context_extensions: Extensions<GroupContext>,
253    /// List of initial leaf node extensions
254    pub(crate) leaf_node_extensions: Extensions<LeafNode>,
255}
256
257impl Default for MlsGroupCreateConfig {
258    fn default() -> Self {
259        Self {
260            capabilities: Capabilities::default(),
261            lifetime: Lifetime::default(),
262            ciphersuite: Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519,
263            join_config: MlsGroupJoinConfig::default(),
264            group_context_extensions: Extensions::default(),
265            leaf_node_extensions: Extensions::default(),
266        }
267    }
268}
269
270/// Builder struct for an [`MlsGroupJoinConfig`].
271#[derive(Default)]
272pub struct MlsGroupJoinConfigBuilder {
273    join_config: MlsGroupJoinConfig,
274}
275
276impl MlsGroupJoinConfigBuilder {
277    /// Creates a new builder with default values.
278    fn new() -> Self {
279        Self {
280            join_config: MlsGroupJoinConfig::default(),
281        }
282    }
283
284    /// Sets the `wire_format` property of the [`MlsGroupJoinConfig`].
285    pub fn wire_format_policy(mut self, wire_format_policy: WireFormatPolicy) -> Self {
286        self.join_config.wire_format_policy = wire_format_policy;
287        self
288    }
289
290    /// Sets the `padding_size` property of the [`MlsGroupJoinConfig`].
291    pub fn padding_size(mut self, padding_size: usize) -> Self {
292        self.join_config.padding_size = padding_size;
293        self
294    }
295
296    /// Sets the `max_past_epochs` property of the [`MlsGroupJoinConfig`].
297    ///
298    /// This method overrides the policy set by [`Self::set_past_epoch_deletion_policy()`],
299    /// and is equivalent to setting the past epoch deletion policy to
300    /// `PastEpochDeletionPolicy::MaxEpochs(max_past_epochs)`.
301    ///
302    /// **WARNING**
303    ///
304    /// This feature enables the storage of message secrets from past epochs.
305    /// It is a trade-off between functionality and forward secrecy and should only be enabled
306    /// if the Delivery Service cannot guarantee that application messages will be sent in
307    /// the same epoch in which they were generated. The number for `max_epochs` should be
308    /// as low as possible.
309    pub fn max_past_epochs(mut self, max_past_epochs: usize) -> Self {
310        self.join_config.past_epoch_deletion_policy =
311            PastEpochDeletionPolicy::MaxEpochs(max_past_epochs);
312        self
313    }
314
315    /// Set the policy for deleting past epoch secrets.
316    ///
317    /// By default, storage of past epoch secrets is disabled.
318    ///
319    /// This method overrides the configuration set by [`Self::max_past_epochs()`].
320    ///
321    /// **WARNING**
322    ///
323    /// This feature enables the storage of message secrets from past epochs.
324    /// It is a trade-off between functionality and forward secrecy and should only be enabled
325    /// if the Delivery Service cannot guarantee that application messages will be sent in
326    /// the same epoch in which they were generated. The number for `max_epochs` should be
327    /// as low as possible.
328    pub fn set_past_epoch_deletion_policy(mut self, policy: PastEpochDeletionPolicy) -> Self {
329        self.join_config.past_epoch_deletion_policy = policy;
330        self
331    }
332
333    /// Sets the `number_of_resumption_psks` property of the [`MlsGroupJoinConfig`].
334    pub fn number_of_resumption_psks(mut self, number_of_resumption_psks: usize) -> Self {
335        self.join_config.number_of_resumption_psks = number_of_resumption_psks;
336        self
337    }
338
339    /// Sets the `use_ratchet_tree_extension` property of the [`MlsGroupJoinConfig`].
340    pub fn use_ratchet_tree_extension(mut self, use_ratchet_tree_extension: bool) -> Self {
341        self.join_config.use_ratchet_tree_extension = use_ratchet_tree_extension;
342        self
343    }
344
345    /// Sets the `sender_ratchet_configuration` property of the [`MlsGroupJoinConfig`].
346    pub fn sender_ratchet_configuration(
347        mut self,
348        sender_ratchet_configuration: SenderRatchetConfiguration,
349    ) -> Self {
350        self.join_config.sender_ratchet_configuration = sender_ratchet_configuration;
351        self
352    }
353
354    /// Finalizes the builder and returns an [`MlsGroupJoinConfig`].
355    pub fn build(self) -> MlsGroupJoinConfig {
356        self.join_config
357    }
358}
359
360impl MlsGroupCreateConfig {
361    /// Returns a builder for [`MlsGroupCreateConfig`]
362    pub fn builder() -> MlsGroupCreateConfigBuilder {
363        MlsGroupCreateConfigBuilder::new()
364    }
365
366    /// Returns the [`MlsGroupCreateConfig`] wire format policy.
367    pub fn wire_format_policy(&self) -> WireFormatPolicy {
368        self.join_config.wire_format_policy
369    }
370
371    /// Returns the [`MlsGroupCreateConfig`] padding size.
372    pub fn padding_size(&self) -> usize {
373        self.join_config.padding_size
374    }
375
376    /// Returns the [`MlsGroupCreateConfig`] max past epochs.
377    pub fn max_past_epochs(&self) -> Option<usize> {
378        self.join_config.max_past_epochs()
379    }
380
381    /// Returns the [`MlsGroupCreateConfig`] number of resumption psks.
382    pub fn number_of_resumption_psks(&self) -> usize {
383        self.join_config.number_of_resumption_psks
384    }
385
386    /// Returns the [`MlsGroupCreateConfig`] boolean flag that indicates whether ratchet_tree_extension should be used.
387    pub fn use_ratchet_tree_extension(&self) -> bool {
388        self.join_config.use_ratchet_tree_extension
389    }
390
391    /// Returns the [`MlsGroupCreateConfig`] sender ratchet configuration.
392    pub fn sender_ratchet_configuration(&self) -> &SenderRatchetConfiguration {
393        &self.join_config.sender_ratchet_configuration
394    }
395
396    /// Returns the [`Extensions`] set as the initial group context.
397    /// This does not contain the initial group context extensions
398    /// added from builder calls to `external_senders` or `required_capabilities`.
399    pub fn group_context_extensions(&self) -> &Extensions<GroupContext> {
400        &self.group_context_extensions
401    }
402
403    /// Returns the [`MlsGroupCreateConfig`] lifetime configuration.
404    pub fn lifetime(&self) -> &Lifetime {
405        &self.lifetime
406    }
407
408    /// Returns the [`Ciphersuite`].
409    pub fn ciphersuite(&self) -> Ciphersuite {
410        self.ciphersuite
411    }
412
413    #[cfg(any(feature = "test-utils", test))]
414    pub fn test_default(ciphersuite: Ciphersuite) -> Self {
415        Self::builder()
416            .wire_format_policy(WireFormatPolicy::new(
417                OutgoingWireFormatPolicy::AlwaysPlaintext,
418                IncomingWireFormatPolicy::Mixed,
419            ))
420            .ciphersuite(ciphersuite)
421            .build()
422    }
423
424    /// Returns the [`MlsGroupJoinConfig`] of groups created with this create config.
425    pub fn join_config(&self) -> &MlsGroupJoinConfig {
426        &self.join_config
427    }
428}
429
430/// Builder for an [`MlsGroupCreateConfig`].
431#[derive(Default, Debug)]
432pub struct MlsGroupCreateConfigBuilder {
433    config: MlsGroupCreateConfig,
434}
435
436impl MlsGroupCreateConfigBuilder {
437    /// Creates a new builder with default values.
438    fn new() -> Self {
439        MlsGroupCreateConfigBuilder {
440            config: MlsGroupCreateConfig::default(),
441        }
442    }
443
444    /// Sets the `wire_format` property of the MlsGroupCreateConfig.
445    pub fn wire_format_policy(mut self, wire_format_policy: WireFormatPolicy) -> Self {
446        self.config.join_config.wire_format_policy = wire_format_policy;
447        self
448    }
449
450    /// Sets the `padding_size` property of the MlsGroupCreateConfig.
451    pub fn padding_size(mut self, padding_size: usize) -> Self {
452        self.config.join_config.padding_size = padding_size;
453        self
454    }
455
456    /// Sets the `max_past_epochs` property of the MlsGroupCreateConfig.
457    /// This allows application messages from previous epochs to be decrypted.
458    ///
459    /// This method overrides the policy set by [`Self::set_past_epoch_deletion_policy()`],
460    /// and is equivalent to setting the past epoch deletion policy to
461    /// `PastEpochDeletionPolicy::MaxEpochs(max_past_epochs)`.
462    ///
463    /// **WARNING**
464    ///
465    /// This feature enables the storage of message secrets from past epochs.
466    /// It is a trade-off between functionality and forward secrecy and should only be enabled
467    /// if the Delivery Service cannot guarantee that application messages will be sent in
468    /// the same epoch in which they were generated. The number for `max_epochs` should be
469    /// as low as possible.
470    pub fn max_past_epochs(mut self, max_past_epochs: usize) -> Self {
471        self.config.join_config.past_epoch_deletion_policy =
472            PastEpochDeletionPolicy::MaxEpochs(max_past_epochs);
473        self
474    }
475
476    /// Set the policy for deleting past epoch secrets.
477    ///
478    /// By default, storage of past epoch secrets is disabled.
479    ///
480    /// This method overrides the configuration set by [`Self::max_past_epochs()`].
481    ///
482    /// **WARNING**
483    ///
484    /// This feature enables the storage of message secrets from past epochs.
485    /// It is a trade-off between functionality and forward secrecy and should only be enabled
486    /// if the Delivery Service cannot guarantee that application messages will be sent in
487    /// the same epoch in which they were generated. The number for `max_epochs` should be
488    /// as low as possible.
489    pub fn set_past_epoch_deletion_policy(mut self, policy: PastEpochDeletionPolicy) -> Self {
490        self.config.join_config.past_epoch_deletion_policy = policy;
491        self
492    }
493
494    /// Sets the `number_of_resumption_psks` property of the MlsGroupCreateConfig.
495    pub fn number_of_resumption_psks(mut self, number_of_resumption_psks: usize) -> Self {
496        self.config.join_config.number_of_resumption_psks = number_of_resumption_psks;
497        self
498    }
499
500    /// Sets the `use_ratchet_tree_extension` property of the MlsGroupCreateConfig.
501    pub fn use_ratchet_tree_extension(mut self, use_ratchet_tree_extension: bool) -> Self {
502        self.config.join_config.use_ratchet_tree_extension = use_ratchet_tree_extension;
503        self
504    }
505
506    /// Sets the `capabilities` of the group creator's leaf node.
507    pub fn capabilities(mut self, capabilities: Capabilities) -> Self {
508        self.config.capabilities = capabilities;
509        self
510    }
511
512    /// Sets the `sender_ratchet_configuration` property of the MlsGroupCreateConfig.
513    /// See [`SenderRatchetConfiguration`] for more information.
514    pub fn sender_ratchet_configuration(
515        mut self,
516        sender_ratchet_configuration: SenderRatchetConfiguration,
517    ) -> Self {
518        self.config.join_config.sender_ratchet_configuration = sender_ratchet_configuration;
519        self
520    }
521
522    /// Sets the `lifetime` property of the MlsGroupCreateConfig.
523    pub fn lifetime(mut self, lifetime: Lifetime) -> Self {
524        self.config.lifetime = lifetime;
525        self
526    }
527
528    /// Sets the `ciphersuite` property of the MlsGroupCreateConfig.
529    pub fn ciphersuite(mut self, ciphersuite: Ciphersuite) -> Self {
530        self.config.ciphersuite = ciphersuite;
531        self
532    }
533
534    /// Sets initial group context extensions.
535    pub fn with_group_context_extensions(mut self, extensions: Extensions<GroupContext>) -> Self {
536        self.config.group_context_extensions = extensions;
537        self
538    }
539
540    /// Sets extensions of the group creator's [`LeafNode`].
541    ///
542    /// Returns an error if the extension types are not valid in a leaf node.
543    pub fn with_leaf_node_extensions(
544        mut self,
545        extensions: Extensions<LeafNode>,
546    ) -> Result<Self, LeafNodeValidationError> {
547        // Make sure that the extension type is supported in this context.
548        // This means that the leaf node needs to have support listed in the
549        // the capabilities (https://validation.openmls.tech/#valn0107).
550        if !self.config.capabilities.contains_extensions(&extensions) {
551            return Err(LeafNodeValidationError::ExtensionsNotInCapabilities);
552        }
553
554        // Note that the extensions have already been checked to be allowed here.
555        self.config.leaf_node_extensions = extensions;
556        Ok(self)
557    }
558
559    /// Finalizes the builder and returns an [`MlsGroupCreateConfig`].
560    pub fn build(self) -> MlsGroupCreateConfig {
561        self.config
562    }
563}
564
565/// Defines what wire format is acceptable for incoming handshake messages.
566/// Note that application messages must always be encrypted.
567#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
568pub enum IncomingWireFormatPolicy {
569    /// Handshake messages must always be PrivateMessage
570    AlwaysCiphertext,
571    /// Handshake messages must always be PublicMessage
572    AlwaysPlaintext,
573    /// Handshake messages can either be PrivateMessage or PublicMessage
574    Mixed,
575}
576
577impl IncomingWireFormatPolicy {
578    pub(crate) fn is_compatible_with(&self, wire_format: WireFormat) -> bool {
579        match self {
580            IncomingWireFormatPolicy::AlwaysCiphertext => wire_format == WireFormat::PrivateMessage,
581            IncomingWireFormatPolicy::AlwaysPlaintext => wire_format == WireFormat::PublicMessage,
582            IncomingWireFormatPolicy::Mixed => {
583                wire_format == WireFormat::PrivateMessage
584                    || wire_format == WireFormat::PublicMessage
585            }
586        }
587    }
588}
589
590/// Defines what wire format should be used for outgoing handshake messages.
591/// Note that application messages must always be encrypted.
592#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
593pub enum OutgoingWireFormatPolicy {
594    /// Handshake messages must always be PrivateMessage
595    AlwaysCiphertext,
596    /// Handshake messages must always be PublicMessage
597    AlwaysPlaintext,
598}
599
600/// Defines what wire format is desired for outgoing handshake messages.
601/// Note that application messages must always be encrypted.
602#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
603pub struct WireFormatPolicy {
604    outgoing: OutgoingWireFormatPolicy,
605    incoming: IncomingWireFormatPolicy,
606}
607
608impl WireFormatPolicy {
609    /// Creates a new wire format policy from an [`OutgoingWireFormatPolicy`]
610    /// and an [`IncomingWireFormatPolicy`].
611    #[cfg(any(feature = "test-utils", test))]
612    pub(crate) fn new(
613        outgoing: OutgoingWireFormatPolicy,
614        incoming: IncomingWireFormatPolicy,
615    ) -> Self {
616        Self { outgoing, incoming }
617    }
618
619    /// Returns a reference to the wire format policy's outgoing wire format policy.
620    pub fn outgoing(&self) -> OutgoingWireFormatPolicy {
621        self.outgoing
622    }
623
624    /// Returns a reference to the wire format policy's incoming wire format policy.
625    pub fn incoming(&self) -> IncomingWireFormatPolicy {
626        self.incoming
627    }
628}
629
630impl Default for WireFormatPolicy {
631    fn default() -> Self {
632        PURE_CIPHERTEXT_WIRE_FORMAT_POLICY
633    }
634}
635
636impl From<OutgoingWireFormatPolicy> for WireFormat {
637    fn from(outgoing: OutgoingWireFormatPolicy) -> Self {
638        match outgoing {
639            OutgoingWireFormatPolicy::AlwaysCiphertext => WireFormat::PrivateMessage,
640            OutgoingWireFormatPolicy::AlwaysPlaintext => WireFormat::PublicMessage,
641        }
642    }
643}
644
645/// All valid wire format policy combinations.
646/// - [`PURE_PLAINTEXT_WIRE_FORMAT_POLICY`]
647/// - [`PURE_CIPHERTEXT_WIRE_FORMAT_POLICY`]
648/// - [`MIXED_PLAINTEXT_WIRE_FORMAT_POLICY`]
649/// - [`MIXED_CIPHERTEXT_WIRE_FORMAT_POLICY`]
650pub const WIRE_FORMAT_POLICIES: [WireFormatPolicy; 4] = [
651    PURE_PLAINTEXT_WIRE_FORMAT_POLICY,
652    PURE_CIPHERTEXT_WIRE_FORMAT_POLICY,
653    MIXED_PLAINTEXT_WIRE_FORMAT_POLICY,
654    MIXED_CIPHERTEXT_WIRE_FORMAT_POLICY,
655];
656
657/// Incoming and outgoing wire formats are always plaintext.
658pub const PURE_PLAINTEXT_WIRE_FORMAT_POLICY: WireFormatPolicy = WireFormatPolicy {
659    outgoing: OutgoingWireFormatPolicy::AlwaysPlaintext,
660    incoming: IncomingWireFormatPolicy::AlwaysPlaintext,
661};
662
663/// Incoming and outgoing wire formats are always ciphertext.
664pub const PURE_CIPHERTEXT_WIRE_FORMAT_POLICY: WireFormatPolicy = WireFormatPolicy {
665    outgoing: OutgoingWireFormatPolicy::AlwaysCiphertext,
666    incoming: IncomingWireFormatPolicy::AlwaysCiphertext,
667};
668
669/// Incoming wire formats can be mixed while outgoing wire formats are always
670/// plaintext.
671pub const MIXED_PLAINTEXT_WIRE_FORMAT_POLICY: WireFormatPolicy = WireFormatPolicy {
672    outgoing: OutgoingWireFormatPolicy::AlwaysPlaintext,
673    incoming: IncomingWireFormatPolicy::Mixed,
674};
675
676/// Incoming wire formats can be mixed while outgoing wire formats are always
677/// ciphertext.
678pub const MIXED_CIPHERTEXT_WIRE_FORMAT_POLICY: WireFormatPolicy = WireFormatPolicy {
679    outgoing: OutgoingWireFormatPolicy::AlwaysCiphertext,
680    incoming: IncomingWireFormatPolicy::Mixed,
681};