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