core_crypto/mls/conversation/leaf_node_validation.rs
1//! cf <https://www.rfc-editor.org/rfc/rfc9420.html#name-leaf-node-validation>
2
3#[cfg(test)]
4mod tests {
5 use openmls::prelude::Lifetime;
6 use wasm_bindgen_test::*;
7
8 use crate::{test_utils::*, CryptoError, MlsError};
9 use openmls::prelude::{AddMembersError, KeyPackageVerifyError, ProposeAddMemberError};
10
11 wasm_bindgen_test_configure!(run_in_browser);
12
13 mod stages {
14
15 use super::*;
16
17 /// The validity of a LeafNode needs to be verified at the following stages:
18 /// When a LeafNode is downloaded in a KeyPackage, before it is used to add the client to the group
19 #[apply(all_cred_cipher)]
20 #[wasm_bindgen_test]
21 async fn should_validate_leaf_node_when_adding(case: TestCase) {
22 run_test_with_client_ids(
23 case.clone(),
24 ["alice", "bob"],
25 move |[mut alice_central, bob_central]| {
26 Box::pin(async move {
27 let expiration_time = 14;
28 let start = fluvio_wasm_timer::Instant::now();
29 let id = conversation_id();
30 alice_central
31 .context
32 .new_conversation(&id, case.credential_type, case.cfg.clone())
33 .await
34 .unwrap();
35
36 // should fail when creating Add proposal
37 let invalid_kp = bob_central.new_keypackage(&case, Lifetime::new(expiration_time)).await;
38
39 // Give time to the KeyPackage to expire
40 let expiration_time = core::time::Duration::from_secs(expiration_time);
41 let elapsed = start.elapsed();
42 if expiration_time > elapsed {
43 async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1))
44 .await;
45 }
46
47 let proposal_creation = alice_central.context.new_add_proposal(&id, invalid_kp).await;
48 assert!(matches!(
49 proposal_creation.unwrap_err(),
50 CryptoError::MlsError(MlsError::ProposeAddMemberError(
51 ProposeAddMemberError::KeyPackageVerifyError(KeyPackageVerifyError::InvalidLeafNode(_))
52 ))
53 ));
54 assert!(alice_central.pending_proposals(&id).await.is_empty());
55
56 // should fail when creating Add commits
57 let expiration_time = 14;
58 let start = fluvio_wasm_timer::Instant::now();
59
60 let invalid_kp = bob_central.new_keypackage(&case, Lifetime::new(expiration_time)).await;
61
62 // Give time to the KeyPackage to expire
63 let expiration_time = core::time::Duration::from_secs(expiration_time);
64 let elapsed = start.elapsed();
65 if expiration_time > elapsed {
66 async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1))
67 .await;
68 }
69
70 let commit_creation = alice_central
71 .context
72 .add_members_to_conversation(&id, vec![invalid_kp.into()])
73 .await;
74
75 assert!(matches!(
76 commit_creation.unwrap_err(),
77 CryptoError::MlsError(MlsError::MlsAddMembersError(
78 AddMembersError::KeyPackageVerifyError(KeyPackageVerifyError::InvalidLeafNode(_))
79 ))
80 ));
81 assert!(alice_central.pending_proposals(&id).await.is_empty());
82 assert!(alice_central.pending_commit(&id).await.is_none());
83 })
84 },
85 )
86 .await
87 }
88
89 /// The validity of a LeafNode needs to be verified at the following stages:
90 /// When a LeafNode is received by a group member in an Add, Update, or Commit message
91 #[apply(all_cred_cipher)]
92 #[wasm_bindgen_test]
93 async fn should_validate_leaf_node_when_receiving_expired_add_proposal(case: TestCase) {
94 run_test_with_client_ids(
95 case.clone(),
96 ["alice", "bob", "charlie"],
97 move |[alice_central, bob_central, charlie_central]| {
98 Box::pin(async move {
99 let expiration_time = 14;
100 let start = fluvio_wasm_timer::Instant::now();
101 let id = conversation_id();
102 alice_central
103 .context
104 .new_conversation(&id, case.credential_type, case.cfg.clone())
105 .await
106 .unwrap();
107 alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap();
108
109 let invalid_kp = charlie_central
110 .new_keypackage(&case, Lifetime::new(expiration_time))
111 .await;
112
113 let proposal = alice_central.context.new_add_proposal(&id, invalid_kp).await.unwrap();
114 let proposal = proposal.proposal.to_bytes().unwrap();
115
116 let elapsed = start.elapsed();
117 // Give time to the certificate to expire
118 let expiration_time = core::time::Duration::from_secs(expiration_time);
119 if expiration_time > elapsed {
120 async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1))
121 .await;
122 }
123
124 let decrypting = bob_central.context.decrypt_message(&id, proposal).await;
125
126 // TODO: currently succeeds as we don't anymore validate KeyPackage lifetime upon reception: find another way to craft an invalid KeyPackage. Tracking issue number: WPB-9623
127 decrypting.unwrap();
128 /*assert!(matches!(
129 decrypting.unwrap_err(),
130 CryptoError::MlsError(MlsError::MlsMessageError(ProcessMessageError::ValidationError(
131 ValidationError::KeyPackageVerifyError(KeyPackageVerifyError::InvalidLeafNode(
132 LeafNodeValidationError::Lifetime(_)
133 ))
134 )))
135 ));*/
136 })
137 },
138 )
139 .await
140 }
141
142 /// The validity of a LeafNode needs to be verified at the following stages:
143 /// When a LeafNode is received by a group member in an Add, Update, or Commit message
144 #[apply(all_cred_cipher)]
145 #[wasm_bindgen_test]
146 async fn should_validate_leaf_node_when_receiving_add_commit(case: TestCase) {
147 run_test_with_client_ids(
148 case.clone(),
149 ["alice", "bob", "charlie"],
150 move |[alice_central, bob_central, charlie_central]| {
151 Box::pin(async move {
152 let expiration_time = 14;
153 let start = fluvio_wasm_timer::Instant::now();
154 let id = conversation_id();
155 alice_central
156 .context
157 .new_conversation(&id, case.credential_type, case.cfg.clone())
158 .await
159 .unwrap();
160 alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap();
161
162 // should fail when receiving Add commit
163 let invalid_kp = charlie_central
164 .new_keypackage(&case, Lifetime::new(expiration_time))
165 .await;
166
167 let commit = alice_central
168 .context
169 .add_members_to_conversation(&id, vec![invalid_kp.into()])
170 .await
171 .unwrap();
172 let commit = commit.commit.to_bytes().unwrap();
173
174 let elapsed = start.elapsed();
175 // Give time to the certificate to expire
176 let expiration_time = core::time::Duration::from_secs(expiration_time);
177 if expiration_time > elapsed {
178 async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1))
179 .await;
180 }
181
182 let decrypting = bob_central.context.decrypt_message(&id, commit).await;
183
184 // TODO: currently succeeds as we don't anymore validate KeyPackage lifetime upon reception: find another way to craft an invalid KeyPackage. Tracking issue number: WPB-9623
185 decrypting.unwrap();
186 /*assert!(matches!(
187 decrypting.unwrap_err(),
188 CryptoError::MlsError(MlsError::MlsMessageError(ProcessMessageError::ValidationError(
189 ValidationError::KeyPackageVerifyError(KeyPackageVerifyError::InvalidLeafNode(_))
190 )))
191 ));*/
192 })
193 },
194 )
195 .await
196 }
197
198 /// The validity of a LeafNode needs to be verified at the following stages:
199 /// When a client validates a ratchet tree, e.g., when joining a group or after processing a Commit
200 #[apply(all_cred_cipher)]
201 #[wasm_bindgen_test]
202 async fn should_validate_leaf_node_when_receiving_welcome(case: TestCase) {
203 run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| {
204 Box::pin(async move {
205 let expiration_time = 14;
206 let start = fluvio_wasm_timer::Instant::now();
207 let id = conversation_id();
208 alice_central
209 .context
210 .new_conversation(&id, case.credential_type, case.cfg.clone())
211 .await
212 .unwrap();
213
214 let invalid_kp = bob_central.new_keypackage(&case, Lifetime::new(expiration_time)).await;
215 let commit = alice_central
216 .context
217 .add_members_to_conversation(&id, vec![invalid_kp.into()])
218 .await
219 .unwrap();
220 alice_central.context.commit_accepted(&id).await.unwrap();
221
222 let elapsed = start.elapsed();
223 // Give time to the certificate to expire
224 let expiration_time = core::time::Duration::from_secs(expiration_time);
225 if expiration_time > elapsed {
226 async_std::task::sleep(expiration_time - elapsed + core::time::Duration::from_secs(1)).await;
227 }
228
229 let process_welcome = bob_central
230 .context
231 .process_welcome_message(commit.welcome.into(), case.custom_cfg())
232 .await;
233
234 // TODO: currently succeeds as we don't anymore validate KeyPackage lifetime upon reception: find another way to craft an invalid KeyPackage. Tracking issue number: WPB-9623
235 process_welcome.unwrap();
236 /*assert!(matches!(
237 process_welcome.unwrap_err(),
238 CryptoError::MlsError(MlsError::MlsWelcomeError(WelcomeError::PublicGroupError(
239 CreationFromExternalError::TreeSyncError(
240 TreeSyncFromNodesError::LeafNodeValidationError(LeafNodeValidationError::Lifetime(
241 _
242 ))
243 )
244 )))
245 ));*/
246 })
247 })
248 .await
249 }
250 }
251}