mls_rs/
grease.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright by contributors to this project.
3// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4
5use 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}