1use mls_rs_core::{crypto::CipherSuiteProvider, extension::ExtensionList, group::Capabilities};
6
7use crate::{
8 client::MlsError,
9 group::{GroupInfo, NewMemberInfo},
10 key_package::KeyPackage,
11 tree_kem::leaf_node::LeafNode,
12};
13
14impl LeafNode {
15 pub fn ungreased_capabilities(&self) -> Capabilities {
16 let mut capabilitites = self.capabilities.clone();
17 grease_functions::ungrease(&mut capabilitites.cipher_suites);
18 grease_functions::ungrease(&mut capabilitites.extensions);
19 grease_functions::ungrease(&mut capabilitites.proposals);
20 grease_functions::ungrease(&mut capabilitites.credentials);
21 capabilitites
22 }
23
24 pub fn ungreased_extensions(&self) -> ExtensionList {
25 let mut extensions = self.extensions.clone();
26 grease_functions::ungrease_extensions(&mut extensions);
27 extensions
28 }
29
30 pub fn grease<P: CipherSuiteProvider>(&mut self, cs: &P) -> Result<(), MlsError> {
31 grease_functions::grease(&mut self.capabilities.cipher_suites, cs)?;
32 grease_functions::grease(&mut self.capabilities.proposals, cs)?;
33 grease_functions::grease(&mut self.capabilities.credentials, cs)?;
34
35 let mut new_extensions = grease_functions::grease_extensions(&mut self.extensions, cs)?;
36 self.capabilities.extensions.append(&mut new_extensions);
37
38 Ok(())
39 }
40}
41
42impl KeyPackage {
43 pub fn grease<P: CipherSuiteProvider>(&mut self, cs: &P) -> Result<(), MlsError> {
44 grease_functions::grease_extensions(&mut self.extensions, cs).map(|_| ())
45 }
46
47 pub fn ungreased_extensions(&self) -> ExtensionList {
48 let mut extensions = self.extensions.clone();
49 grease_functions::ungrease_extensions(&mut extensions);
50 extensions
51 }
52}
53
54impl GroupInfo {
55 pub fn grease<P: CipherSuiteProvider>(&mut self, cs: &P) -> Result<(), MlsError> {
56 grease_functions::grease_extensions(&mut self.extensions, cs).map(|_| ())
57 }
58
59 pub fn ungrease(&mut self) {
60 grease_functions::ungrease_extensions(&mut self.extensions)
61 }
62}
63
64impl NewMemberInfo {
65 pub fn ungrease(&mut self) {
66 grease_functions::ungrease_extensions(&mut self.group_info_extensions)
67 }
68}
69
70#[cfg(feature = "grease")]
71mod grease_functions {
72 use core::ops::Deref;
73
74 use mls_rs_core::{
75 crypto::CipherSuiteProvider,
76 error::IntoAnyError,
77 extension::{Extension, ExtensionList, ExtensionType},
78 };
79
80 use super::MlsError;
81
82 pub const GREASE_VALUES: &[u16] = &[
83 0x0A0A, 0x1A1A, 0x2A2A, 0x3A3A, 0x4A4A, 0x5A5A, 0x6A6A, 0x7A7A, 0x8A8A, 0x9A9A, 0xAAAA,
84 0xBABA, 0xCACA, 0xDADA, 0xEAEA,
85 ];
86
87 pub fn grease<T: From<u16>, P: CipherSuiteProvider>(
88 array: &mut Vec<T>,
89 cs: &P,
90 ) -> Result<(), MlsError> {
91 array.push(random_grease_value(cs)?.into());
92 Ok(())
93 }
94
95 pub fn grease_extensions<P: CipherSuiteProvider>(
96 extensions: &mut ExtensionList,
97 cs: &P,
98 ) -> Result<Vec<ExtensionType>, MlsError> {
99 let grease_value = random_grease_value(cs)?;
100 extensions.set(Extension::new(grease_value.into(), vec![]));
101 Ok(vec![grease_value.into()])
102 }
103
104 fn random_grease_value<P: CipherSuiteProvider>(cs: &P) -> Result<u16, MlsError> {
105 let index = cs
106 .random_bytes_vec(1)
107 .map_err(|e| MlsError::CryptoProviderError(e.into_any_error()))?[0];
108
109 Ok(GREASE_VALUES[index as usize % GREASE_VALUES.len()])
110 }
111
112 pub fn ungrease<T: Deref<Target = u16>>(array: &mut Vec<T>) {
113 array.retain(|x| !GREASE_VALUES.contains(&**x));
114 }
115
116 pub fn ungrease_extensions(extensions: &mut ExtensionList) {
117 for e in GREASE_VALUES {
118 extensions.remove((*e).into())
119 }
120 }
121}
122
123#[cfg(not(feature = "grease"))]
124mod grease_functions {
125 use core::ops::Deref;
126
127 use alloc::vec::Vec;
128
129 use mls_rs_core::{
130 crypto::CipherSuiteProvider,
131 extension::{ExtensionList, ExtensionType},
132 };
133
134 use super::MlsError;
135
136 pub fn grease<T: From<u16>, P: CipherSuiteProvider>(
137 _array: &mut [T],
138 _cs: &P,
139 ) -> Result<(), MlsError> {
140 Ok(())
141 }
142
143 pub fn grease_extensions<P: CipherSuiteProvider>(
144 _extensions: &mut ExtensionList,
145 _cs: &P,
146 ) -> Result<Vec<ExtensionType>, MlsError> {
147 Ok(Vec::new())
148 }
149
150 pub fn ungrease<T: Deref<Target = u16>>(_array: &mut [T]) {}
151
152 pub fn ungrease_extensions(_extensions: &mut ExtensionList) {}
153}
154
155#[cfg(all(test, feature = "grease"))]
156mod tests {
157 #[cfg(target_arch = "wasm32")]
158 use wasm_bindgen_test::wasm_bindgen_test as test;
159
160 use std::ops::Deref;
161
162 use mls_rs_core::extension::ExtensionList;
163
164 use crate::{
165 client::test_utils::{test_client_with_key_pkg, TEST_CIPHER_SUITE, TEST_PROTOCOL_VERSION},
166 group::test_utils::test_group,
167 };
168
169 use super::grease_functions::GREASE_VALUES;
170
171 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
172 async fn key_package_is_greased() {
173 let key_pkg = test_client_with_key_pkg(TEST_PROTOCOL_VERSION, TEST_CIPHER_SUITE, "alice")
174 .await
175 .1
176 .into_key_package()
177 .unwrap();
178
179 assert!(is_ext_greased(&key_pkg.extensions));
180 assert!(is_ext_greased(&key_pkg.leaf_node.extensions));
181 assert!(is_greased(&key_pkg.leaf_node.capabilities.cipher_suites));
182 assert!(is_greased(&key_pkg.leaf_node.capabilities.extensions));
183 assert!(is_greased(&key_pkg.leaf_node.capabilities.proposals));
184 assert!(is_greased(&key_pkg.leaf_node.capabilities.credentials));
185
186 assert!(!is_greased(
187 &key_pkg.leaf_node.capabilities.protocol_versions
188 ));
189 }
190
191 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
192 async fn group_info_is_greased() {
193 let group_info = test_group(TEST_PROTOCOL_VERSION, TEST_CIPHER_SUITE)
194 .await
195 .group
196 .group_info_message_allowing_ext_commit(false)
197 .await
198 .unwrap()
199 .into_group_info()
200 .unwrap();
201
202 assert!(is_ext_greased(&group_info.extensions));
203 }
204
205 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
206 async fn public_api_is_not_greased() {
207 let member = test_group(TEST_PROTOCOL_VERSION, TEST_CIPHER_SUITE)
208 .await
209 .group
210 .roster()
211 .member_with_index(0)
212 .unwrap();
213
214 assert!(!is_ext_greased(member.extensions()));
215 assert!(!is_greased(member.capabilities().protocol_versions()));
216 assert!(!is_greased(member.capabilities().cipher_suites()));
217 assert!(!is_greased(member.capabilities().extensions()));
218 assert!(!is_greased(member.capabilities().proposals()));
219 assert!(!is_greased(member.capabilities().credentials()));
220 }
221
222 fn is_greased<T: Deref<Target = u16>>(list: &[T]) -> bool {
223 list.iter().any(|v| GREASE_VALUES.contains(v))
224 }
225
226 fn is_ext_greased(extensions: &ExtensionList) -> bool {
227 extensions
228 .iter()
229 .any(|ext| GREASE_VALUES.contains(&*ext.extension_type()))
230 }
231}