| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| // Copyright by contributors to this project. |
| // SPDX-License-Identifier: (Apache-2.0 OR MIT) |
| |
| use crate::{ |
| client::MlsError, |
| group::{framing::MlsMessage, message_processor::validate_key_package, ExportedTree}, |
| KeyPackage, |
| }; |
| |
| pub mod builder; |
| mod config; |
| mod group; |
| |
| pub(crate) use config::ExternalClientConfig; |
| use mls_rs_core::{ |
| crypto::{CryptoProvider, SignatureSecretKey}, |
| identity::SigningIdentity, |
| }; |
| |
| use builder::{ExternalBaseConfig, ExternalClientBuilder}; |
| |
| pub use group::{ExternalGroup, ExternalReceivedMessage, ExternalSnapshot}; |
| |
| /// A client capable of observing a group's state without having |
| /// private keys required to read content. |
| /// |
| /// This structure is useful when an application is sending |
| /// plaintext control messages in order to allow a central server |
| /// to facilitate communication between users. |
| /// |
| /// # Warning |
| /// |
| /// This structure will only be able to observe groups that were |
| /// created by clients that have the `encrypt_control_messages` |
| /// option returned by [`MlsRules::encryption_options`](`crate::MlsRules::encryption_options`) |
| /// set to `false`. Any control messages that are sent encrypted |
| /// over the wire will break the ability of this client to track |
| /// the resulting group state. |
| pub struct ExternalClient<C> { |
| config: C, |
| signing_data: Option<(SignatureSecretKey, SigningIdentity)>, |
| } |
| |
| impl ExternalClient<()> { |
| pub fn builder() -> ExternalClientBuilder<ExternalBaseConfig> { |
| ExternalClientBuilder::new() |
| } |
| } |
| |
| impl<C> ExternalClient<C> |
| where |
| C: ExternalClientConfig + Clone, |
| { |
| pub(crate) fn new( |
| config: C, |
| signing_data: Option<(SignatureSecretKey, SigningIdentity)>, |
| ) -> Self { |
| Self { |
| config, |
| signing_data, |
| } |
| } |
| |
| /// Begin observing a group based on a GroupInfo message created by |
| /// [Group::group_info_message](crate::group::Group::group_info_message) |
| /// |
| ///`tree_data` is required to be provided out of band if the client that |
| /// created GroupInfo message did not did not use the `ratchet_tree_extension` |
| /// according to [`MlsRules::commit_options`](crate::MlsRules::commit_options) |
| /// at the time the welcome message |
| /// was created. `tree_data` can be exported from a group using the |
| /// [export tree function](crate::group::Group::export_tree). |
| #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] |
| pub async fn observe_group( |
| &self, |
| group_info: MlsMessage, |
| tree_data: Option<ExportedTree<'_>>, |
| ) -> Result<ExternalGroup<C>, MlsError> { |
| ExternalGroup::join( |
| self.config.clone(), |
| self.signing_data.clone(), |
| group_info, |
| tree_data, |
| ) |
| .await |
| } |
| |
| /// Load an existing observed group by loading a snapshot that was |
| /// generated by |
| /// [ExternalGroup::snapshot](self::ExternalGroup::snapshot). |
| #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] |
| pub async fn load_group( |
| &self, |
| snapshot: ExternalSnapshot, |
| ) -> Result<ExternalGroup<C>, MlsError> { |
| ExternalGroup::from_snapshot(self.config.clone(), snapshot).await |
| } |
| |
| #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] |
| pub async fn validate_key_package( |
| &self, |
| key_package: MlsMessage, |
| ) -> Result<KeyPackage, MlsError> { |
| let version = key_package.version; |
| |
| let key_package = key_package |
| .into_key_package() |
| .ok_or(MlsError::UnexpectedMessageType)?; |
| |
| let cs = self |
| .config |
| .crypto_provider() |
| .cipher_suite_provider(key_package.cipher_suite) |
| .ok_or(MlsError::UnsupportedCipherSuite(key_package.cipher_suite))?; |
| |
| let id = self.config.identity_provider(); |
| |
| validate_key_package(&key_package, version, &cs, &id).await?; |
| |
| Ok(key_package) |
| } |
| } |
| |
| #[cfg(test)] |
| pub(crate) mod tests_utils { |
| use crate::{ |
| client::test_utils::{TEST_CIPHER_SUITE, TEST_PROTOCOL_VERSION}, |
| key_package::test_utils::test_key_package_message, |
| }; |
| |
| pub use super::builder::test_utils::*; |
| |
| #[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))] |
| async fn external_client_can_validate_key_package() { |
| let kp = test_key_package_message(TEST_PROTOCOL_VERSION, TEST_CIPHER_SUITE, "john").await; |
| let server = TestExternalClientBuilder::new_for_test().build(); |
| let validated_kp = server.validate_key_package(kp.clone()).await.unwrap(); |
| |
| assert_eq!(kp.into_key_package().unwrap(), validated_kp); |
| } |
| } |