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}