openmls/
skip_validation.rs

1//! This module contains helpers for skipping validation. It is built such that setting the flag to
2//! disable validation can only by set when the "test-utils" feature is enabled.
3//! This module is used in two places, and they use different parts of it.
4//! Code that performs validation and wants to check whether a check is disabled only uses the
5//! [`is_disabled`] submodule. It contains getter functions that read the current state of the
6//! flag.
7//! Test code that disables checks uses the code in the [`checks`] submodule. It contains a module
8//! for each check that can be disabled, and a getter for a handle, protected by a [`Mutex`]. This
9//! is done because the flag state is shared between tests, and tests that set and unset the same
10//! checks are not safe to run concurrently.
11//! For example, a test could cann [`checks::confirmation_tag::handle`] to get a handle to disable
12//! and re-enable the validation of confirmation tags.
13
14pub(crate) mod is_disabled {
15    use super::checks::*;
16
17    pub(crate) fn confirmation_tag() -> bool {
18        confirmation_tag::FLAG.load(core::sync::atomic::Ordering::Relaxed)
19    }
20
21    pub(crate) fn leaf_node_lifetime() -> bool {
22        leaf_node_lifetime::FLAG.load(core::sync::atomic::Ordering::Relaxed)
23    }
24}
25
26#[cfg(test)]
27use std::sync::atomic::AtomicBool;
28
29/// Contains a reference to a flag. Provides convenience functions to set and clear the flag.
30#[cfg(test)]
31#[derive(Clone, Copy, Debug)]
32pub struct SkipValidationHandle {
33    // we keep this field so we can see which handle this is when printing it. we don't need it otherwise
34    #[allow(dead_code)]
35    name: &'static str,
36    flag: &'static AtomicBool,
37}
38
39/// Contains the flags and functions that return handles to control them.
40pub(crate) mod checks {
41    /// Disables validation of the confirmation_tag.
42    pub(crate) mod confirmation_tag {
43        use std::sync::atomic::AtomicBool;
44
45        /// A way of disabling verification and validation of confirmation tags.
46        pub(in crate::skip_validation) static FLAG: AtomicBool = AtomicBool::new(false);
47
48        #[cfg(test)]
49        pub(crate) use lock::handle;
50
51        #[cfg(test)]
52        mod lock {
53            use super::FLAG;
54            use crate::skip_validation::SkipValidationHandle;
55            use once_cell::sync::Lazy;
56            use std::sync::{Mutex, MutexGuard};
57
58            /// The name of the check that can be skipped here
59            const NAME: &str = "confirmation_tag";
60
61            /// A mutex needed to run tests that use this flag sequentially
62            static MUTEX: Lazy<Mutex<SkipValidationHandle>> =
63                Lazy::new(|| Mutex::new(SkipValidationHandle::new_confirmation_tag_handle()));
64
65            /// Takes the mutex and returns the control handle to the validation skipper
66            pub(crate) fn handle() -> MutexGuard<'static, SkipValidationHandle> {
67                MUTEX.lock().unwrap_or_else(|e| {
68                    panic!("error taking skip-validation mutex for '{NAME}': {e}")
69                })
70            }
71
72            impl SkipValidationHandle {
73                pub fn new_confirmation_tag_handle() -> Self {
74                    Self {
75                        name: NAME,
76                        flag: &FLAG,
77                    }
78                }
79            }
80        }
81    }
82
83    /// Disables validation of leaf node lifetimes
84    pub(crate) mod leaf_node_lifetime {
85        use std::sync::atomic::AtomicBool;
86
87        /// A way of disabling verification and validation of leaf node lifetimes.
88        pub(in crate::skip_validation) static FLAG: AtomicBool = AtomicBool::new(false);
89
90        #[cfg(test)]
91        pub(crate) use lock::handle;
92
93        #[cfg(test)]
94        mod lock {
95            use super::FLAG;
96            use crate::skip_validation::SkipValidationHandle;
97            use once_cell::sync::Lazy;
98            use std::sync::{Mutex, MutexGuard};
99
100            /// The name of the check that can be skipped here
101            const NAME: &str = "leaf_node_lifetime";
102
103            /// A mutex needed to run tests that use this flag sequentially
104            static MUTEX: Lazy<Mutex<SkipValidationHandle>> =
105                Lazy::new(|| Mutex::new(SkipValidationHandle::new_leaf_node_lifetime_handle()));
106
107            /// Takes the mutex and returns the control handle to the validation skipper
108            pub(crate) fn handle() -> MutexGuard<'static, SkipValidationHandle> {
109                MUTEX.lock().unwrap_or_else(|e| {
110                    panic!("error taking skip-validation mutex for '{NAME}': {e}")
111                })
112            }
113
114            impl SkipValidationHandle {
115                pub fn new_leaf_node_lifetime_handle() -> Self {
116                    Self {
117                        name: NAME,
118                        flag: &FLAG,
119                    }
120                }
121            }
122        }
123    }
124}
125
126#[cfg(test)]
127impl SkipValidationHandle {
128    /// Disables validation for the check controlled by this handle
129    pub fn disable_validation(self) {
130        self.flag.store(true, core::sync::atomic::Ordering::Relaxed);
131    }
132
133    /// Enables validation for the check controlled by this handle
134    pub fn enable_validation(self) {
135        self.flag
136            .store(false, core::sync::atomic::Ordering::Relaxed);
137    }
138
139    /// Runs function `f` with validation disabled
140    pub fn with_disabled<R, F: FnMut() -> R>(self, mut f: F) -> R {
141        self.disable_validation();
142        let r = f();
143        self.enable_validation();
144        r
145    }
146}