mls_rs/
external_client.rs1use crate::{
6 client::MlsError,
7 group::{
8 cipher_suite_provider, framing::MlsMessage, message_processor::validate_key_package,
9 ExportedTree,
10 },
11 time::MlsTime,
12 KeyPackage,
13};
14
15pub mod builder;
16mod config;
17mod group;
18
19pub(crate) use config::ExternalClientConfig;
20use mls_rs_core::{
21 crypto::{CryptoProvider, SignatureSecretKey},
22 identity::SigningIdentity,
23};
24
25use builder::{ExternalBaseConfig, ExternalClientBuilder};
26
27pub use group::{ExternalGroup, ExternalReceivedMessage, ExternalSnapshot};
28
29pub struct ExternalClient<C> {
45 config: C,
46 signing_data: Option<(SignatureSecretKey, SigningIdentity)>,
47}
48
49impl ExternalClient<()> {
50 pub fn builder() -> ExternalClientBuilder<ExternalBaseConfig> {
51 ExternalClientBuilder::new()
52 }
53}
54
55impl<C> ExternalClient<C>
56where
57 C: ExternalClientConfig + Clone,
58{
59 pub(crate) fn new(
60 config: C,
61 signing_data: Option<(SignatureSecretKey, SigningIdentity)>,
62 ) -> Self {
63 Self {
64 config,
65 signing_data,
66 }
67 }
68
69 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
79 pub async fn observe_group(
80 &self,
81 group_info: MlsMessage,
82 tree_data: Option<ExportedTree<'_>>,
83 maybe_time: Option<MlsTime>,
84 ) -> Result<ExternalGroup<C>, MlsError> {
85 ExternalGroup::join(
86 self.config.clone(),
87 self.signing_data.clone(),
88 group_info,
89 tree_data,
90 maybe_time,
91 )
92 .await
93 }
94
95 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
99 pub async fn load_group(
100 &self,
101 snapshot: ExternalSnapshot,
102 ) -> Result<ExternalGroup<C>, MlsError> {
103 #[cfg(feature = "tree_index")]
104 let identity_provider = self.config.identity_provider();
105
106 let cipher_suite_provider = cipher_suite_provider(
107 self.config.crypto_provider(),
108 snapshot.state.context.cipher_suite,
109 )?;
110
111 Ok(ExternalGroup {
112 config: self.config.clone(),
113 signing_data: self.signing_data.clone(),
114 state: snapshot
115 .state
116 .import(
117 #[cfg(feature = "tree_index")]
118 &identity_provider,
119 )
120 .await?,
121 cipher_suite_provider,
122 })
123 }
124
125 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
130 pub async fn load_group_with_ratchet_tree(
131 &self,
132 mut snapshot: ExternalSnapshot,
133 tree_data: ExportedTree<'_>,
134 ) -> Result<ExternalGroup<C>, MlsError> {
135 snapshot.state.public_tree.nodes = tree_data.0.into_owned();
136
137 self.load_group(snapshot).await
138 }
139
140 #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
141 pub async fn validate_key_package(
142 &self,
143 key_package: MlsMessage,
144 timestamp: Option<MlsTime>,
145 ) -> Result<KeyPackage, MlsError> {
146 let version = key_package.version;
147
148 let key_package = key_package
149 .into_key_package()
150 .ok_or(MlsError::UnexpectedMessageType)?;
151
152 let cs = self
153 .config
154 .crypto_provider()
155 .cipher_suite_provider(key_package.cipher_suite)
156 .ok_or(MlsError::UnsupportedCipherSuite(key_package.cipher_suite))?;
157
158 let id = self.config.identity_provider();
159
160 validate_key_package(&key_package, version, &cs, &id, timestamp).await?;
161
162 Ok(key_package)
163 }
164
165 pub fn identity_provider(&self) -> <C as ExternalClientConfig>::IdentityProvider {
167 self.config.identity_provider()
168 }
169}
170
171#[cfg(test)]
172pub(crate) mod tests_utils {
173 use crate::{
174 client::test_utils::{TEST_CIPHER_SUITE, TEST_PROTOCOL_VERSION},
175 key_package::test_utils::test_key_package_message,
176 };
177
178 pub use super::builder::test_utils::*;
179
180 #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]
181 async fn external_client_can_validate_key_package() {
182 let kp = test_key_package_message(TEST_PROTOCOL_VERSION, TEST_CIPHER_SUITE, "john").await;
183 let server = TestExternalClientBuilder::new_for_test().build();
184 let validated_kp = server.validate_key_package(kp.clone(), None).await.unwrap();
185
186 assert_eq!(kp.into_key_package().unwrap(), validated_kp);
187 }
188}