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}