nrc_mls/
lib.rs

1//! A Rust implementation of the Nostr Message Layer Security (MLS) protocol
2//!
3//! This crate provides functionality for implementing secure group messaging in Nostr using the MLS protocol.
4//! It handles group creation, member management, message encryption/decryption, key management, and storage of groups and messages.
5//! The implementation follows the MLS specification while integrating with Nostr's event system.
6
7#![forbid(unsafe_code)]
8#![warn(missing_docs)]
9#![warn(rustdoc::bare_urls)]
10#![cfg_attr(docsrs, feature(doc_auto_cfg))]
11#![doc = include_str!("../README.md")]
12
13use nrc_mls_storage::NostrMlsStorageProvider;
14use openmls::prelude::*;
15use openmls_rust_crypto::RustCrypto;
16
17mod constant;
18pub mod error;
19pub mod extension;
20pub mod groups;
21pub mod key_packages;
22pub mod messages;
23pub mod prelude;
24#[cfg(test)]
25pub mod test_util;
26mod util;
27pub mod welcomes;
28
29use self::constant::{DEFAULT_CIPHERSUITE, REQUIRED_EXTENSIONS};
30pub use self::error::Error;
31
32/// The main struct for the Nostr MLS implementation.
33///
34/// This struct provides the core functionality for MLS operations in Nostr:
35/// - Group management (creation, updates, member management)
36/// - Message handling (encryption, decryption, processing)
37/// - Key management (key packages, welcome messages)
38///
39/// It uses a generic storage provider that implements the `NostrMlsStorageProvider` trait,
40/// allowing for flexible storage backends.
41#[derive(Debug)]
42pub struct NostrMls<Storage>
43where
44    Storage: NostrMlsStorageProvider,
45{
46    /// The MLS ciphersuite used for cryptographic operations
47    pub ciphersuite: Ciphersuite,
48    /// Required MLS extensions for Nostr functionality
49    pub extensions: Vec<ExtensionType>,
50    /// The OpenMLS provider implementation for cryptographic and storage operations
51    pub provider: NostrMlsProvider<Storage>,
52}
53
54/// Provider implementation for OpenMLS that integrates with Nostr.
55///
56/// This struct implements the OpenMLS Provider trait, providing:
57/// - Cryptographic operations through RustCrypto
58/// - Storage operations through the generic Storage type
59/// - Random number generation through RustCrypto
60#[derive(Debug)]
61pub struct NostrMlsProvider<Storage>
62where
63    Storage: NostrMlsStorageProvider,
64{
65    crypto: RustCrypto,
66    storage: Storage,
67}
68
69impl<Storage> OpenMlsProvider for NostrMlsProvider<Storage>
70where
71    Storage: NostrMlsStorageProvider,
72{
73    type CryptoProvider = RustCrypto;
74    type RandProvider = RustCrypto;
75    type StorageProvider = Storage::OpenMlsStorageProvider;
76
77    fn storage(&self) -> &Self::StorageProvider {
78        self.storage.openmls_storage()
79    }
80
81    fn crypto(&self) -> &Self::CryptoProvider {
82        &self.crypto
83    }
84
85    fn rand(&self) -> &Self::RandProvider {
86        &self.crypto
87    }
88}
89
90impl<Storage> NostrMls<Storage>
91where
92    Storage: NostrMlsStorageProvider,
93{
94    /// Construct new nostr MLS instance
95    pub fn new(storage: Storage) -> Self {
96        Self {
97            ciphersuite: DEFAULT_CIPHERSUITE,
98            extensions: REQUIRED_EXTENSIONS.to_vec(),
99            provider: NostrMlsProvider {
100                crypto: RustCrypto::default(),
101                storage,
102            },
103        }
104    }
105
106    /// Get nostr MLS capabilities
107    #[inline]
108    pub(crate) fn capabilities(&self) -> Capabilities {
109        Capabilities::new(
110            None,
111            Some(&[self.ciphersuite]),
112            Some(&self.extensions),
113            None,
114            None,
115        )
116    }
117
118    /// Get nostr mls group's required capabilities extension
119    #[inline]
120    pub(crate) fn required_capabilities_extension(&self) -> Extension {
121        Extension::RequiredCapabilities(RequiredCapabilitiesExtension::new(
122            &self.extensions,
123            &[],
124            &[],
125        ))
126    }
127
128    /// Get the ciphersuite value
129    pub(crate) fn ciphersuite_value(&self) -> u16 {
130        self.ciphersuite.into()
131    }
132
133    /// Get the extensions value
134    pub(crate) fn extensions_value(&self) -> String {
135        self.extensions
136            .iter()
137            .map(|e| format!("{:?}", e))
138            .collect::<Vec<String>>()
139            .join(",")
140    }
141
142    /// Get the storage provider
143    pub(crate) fn storage(&self) -> &Storage {
144        &self.provider.storage
145    }
146}
147
148/// Tests module for nostr-mls
149#[cfg(test)]
150pub mod tests {
151    use nostr_mls_memory_storage::NostrMlsMemoryStorage;
152
153    use super::*;
154
155    /// Create a test NostrMls instance with an in-memory storage provider
156    pub fn create_test_nostr_mls() -> NostrMls<NostrMlsMemoryStorage> {
157        NostrMls::new(NostrMlsMemoryStorage::default())
158    }
159}