Skip to main content

openmls/
grease.rs

1//! # GREASE Support for MLS
2//!
3//! This module implements GREASE (Generate Random Extensions And Sustain
4//! Extensibility) as defined in [RFC 9420 Section 13.5](https://www.rfc-editor.org/rfc/rfc9420.html#section-13.5).
5//!
6//! GREASE values are used to ensure that implementations properly handle
7//! unknown values and maintain extensibility. The GREASE values follow a
8//! specific pattern where both bytes are of the form `0x_A` (e.g., 0x0A0A,
9//! 0x1A1A, 0x2A2A, etc.).
10//!
11//! ## Purpose
12//!
13//! GREASE helps prevent extensibility failures by:
14//! - Ensuring implementations don't reject unknown values
15//! - Testing that parsers properly handle unexpected values
16//! - Maintaining forward compatibility
17//!
18//! ## Usage in OpenMLS
19//!
20//! OpenMLS provides tools for working with GREASE values:
21//!
22//! - **Recognition**: GREASE values are automatically recognized during
23//!   deserialization and stored in dedicated `Grease` variants (e.g.,
24//!   `ProposalType::Grease`, `ExtensionType::Grease`).
25//!
26//! - **Validation**: During capability validation, GREASE values are treated
27//!   the same as unknown values and filtered out appropriately.
28//!
29//! - **Injection**: Library users can inject random GREASE values into
30//!   capabilities using the [`Capabilities::with_grease`](crate::treesync::node::leaf_node::Capabilities::with_grease)
31//!   method or by manually adding `Grease` variants.
32//!
33//! ## Example
34//!
35//! ```
36//! use openmls::prelude::*;
37//! use openmls_rust_crypto::OpenMlsRustCrypto;
38//!
39//! let provider = OpenMlsRustCrypto::default();
40//!
41//! // Inject random GREASE values into capabilities
42//! let capabilities = Capabilities::builder()
43//!     .with_grease(provider.rand())
44//!     .build();
45//!
46//! // Capabilities now contain random GREASE values
47//! assert!(capabilities.ciphersuites().iter().any(|cs| cs.is_grease()));
48//! ```
49
50use openmls_traits::random::OpenMlsRand;
51
52// Re-export GREASE constants and functions from traits crate
53pub use openmls_traits::grease::{is_grease_value, GREASE_VALUES};
54
55/// Returns a random GREASE value from the set of valid GREASE values.
56///
57/// This function uses the provided random number generator to select one of the
58/// 15 valid GREASE values defined in RFC 9420.
59///
60/// # Arguments
61///
62/// * `rand` - A random number generator implementing [`OpenMlsRand`]
63///
64/// # Returns
65///
66/// A randomly selected GREASE value
67///
68/// # Examples
69///
70/// ```
71/// use openmls::grease::random_grease_value;
72/// use openmls_rust_crypto::RustCrypto;
73///
74/// let crypto = RustCrypto::default();
75/// let grease = random_grease_value(&crypto);
76/// assert!(openmls::grease::is_grease_value(grease));
77/// ```
78pub fn random_grease_value(rand: &impl OpenMlsRand) -> u16 {
79    // Generate a random index into the GREASE_VALUES array
80    let random_bytes: [u8; 1] = rand
81        .random_array()
82        .expect("Failed to generate random bytes");
83    let index = (random_bytes[0] % GREASE_VALUES.len() as u8) as usize;
84    GREASE_VALUES[index]
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_grease_values_are_valid() {
93        // All defined GREASE values should be recognized as GREASE
94        for &value in &GREASE_VALUES {
95            assert!(
96                is_grease_value(value),
97                "GREASE value 0x{:04X} not recognized",
98                value
99            );
100        }
101    }
102
103    #[test]
104    fn test_non_grease_values() {
105        // Test various non-GREASE values
106        let non_grease = [
107            0x0000, 0x0001, 0x0002, 0x00FF, 0x0100, 0x0A00, 0x00A0, 0x1111, 0x2222, 0xFFFF, 0x0B0B,
108            0x1B1B, // Wrong pattern (0xB instead of 0xA)
109        ];
110
111        for &value in &non_grease {
112            assert!(
113                !is_grease_value(value),
114                "Non-GREASE value 0x{:04X} incorrectly identified as GREASE",
115                value
116            );
117        }
118    }
119
120    #[test]
121    fn test_all_grease_values_unique() {
122        // Ensure all GREASE values are unique
123        let mut sorted = GREASE_VALUES;
124        sorted.sort_unstable();
125        for i in 1..sorted.len() {
126            assert_ne!(
127                sorted[i - 1],
128                sorted[i],
129                "Duplicate GREASE value found: 0x{:04X}",
130                sorted[i]
131            );
132        }
133    }
134
135    #[test]
136    fn test_grease_values_count() {
137        // RFC 9420 defines exactly 15 GREASE values
138        assert_eq!(GREASE_VALUES.len(), 15);
139    }
140
141    #[test]
142    fn test_random_grease_value() {
143        let crypto = openmls_rust_crypto::RustCrypto::default();
144
145        // Generate multiple random GREASE values and verify they're all valid
146        for _ in 0..100 {
147            let value = random_grease_value(&crypto);
148            assert!(
149                is_grease_value(value),
150                "random_grease_value returned non-GREASE value: 0x{:04X}",
151                value
152            );
153        }
154    }
155}