Skip to main content

saorsa_mls/
lib.rs

1// Copyright 2024 Saorsa Labs
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4#![forbid(unsafe_code)]
5#![warn(clippy::all, rust_2018_idioms)]
6
7//! # Saorsa MLS - Message Layer Security Protocol (RFC 9420) with Post-Quantum Cryptography
8//!
9//! This crate implements the Message Layer Security (MLS) protocol as specified in
10//! [RFC 9420](https://datatracker.ietf.org/doc/rfc9420/) for secure group communication,
11//! enhanced with post-quantum cryptographic algorithms for quantum resistance.
12//!
13//! MLS provides:
14//!
15//! - **End-to-end encryption** for group messaging
16//! - **Forward secrecy** - past messages remain secure even if keys are compromised
17//! - **Post-compromise security** - the group can heal after a compromise
18//! - **Asynchronous group management** - members can join/leave without real-time coordination
19//! - **Scalable tree-based key derivation** using `TreeKEM`
20//!
21//! ## Core Components
22//!
23//! - `protocol`: MLS protocol message structures and state machines
24//! - `crypto`: Cryptographic primitives and key derivation
25//! - `group`: Group state management and `TreeKEM` operations
26//! - `member`: Member identity and authentication
27//!
28//! ## Example Usage
29//!
30//! ```rust,no_run
31//! use saorsa_mls::{MlsGroup, MemberIdentity, MemberId, GroupConfig};
32//!
33//! # async fn example() -> anyhow::Result<()> {
34//! // Create a new MLS group
35//! let config = GroupConfig::default();
36//! let creator_identity = MemberIdentity::generate(MemberId::generate())?;
37//! let mut group = MlsGroup::new(config, creator_identity).await?;
38//!
39//! // Add members to the group
40//! let new_member = MemberIdentity::generate(MemberId::generate())?;
41//! let welcome = group.add_member(&new_member).await?;
42//!
43//! // Send encrypted messages
44//! let message = group.encrypt_message(b"Hello, secure group!")?;
45//! let decrypted = group.decrypt_message(&message)?;
46//! # Ok(())
47//! # }
48//! ```
49
50use std::time::Duration;
51use thiserror::Error;
52
53pub mod api;
54pub mod crypto;
55pub mod group;
56pub mod member;
57pub mod protocol;
58pub mod quic_integration;
59
60pub use api::{
61    add_member, group_new, group_new_with_config, recv, remove_member, send, Ciphertext,
62    CommitOptions, GroupId as SimpleGroupId, Identity,
63};
64pub use crypto::{
65    AeadCipher, CipherSuite, CipherSuiteId, Hash, HpkeContext, KeyPair, KeySchedule, MlsAead,
66    MlsHash, MlsKem, MlsSignature,
67};
68pub use group::{GroupConfig, GroupId, GroupState, MlsGroup};
69pub use member::{
70    Credential, CredentialType, GroupMember, KeyPackage, MemberId, MemberIdentity, MemberState,
71    TrustStore,
72};
73pub use protocol::{AuditLogEntry, *};
74
75/// Errors that can occur in MLS operations
76#[derive(Debug, Error)]
77pub enum MlsError {
78    #[error("Cryptographic operation failed: {0}")]
79    CryptoError(String),
80
81    #[error("Invalid group state: {0}")]
82    InvalidGroupState(String),
83
84    #[error("Member not found: {0:?}")]
85    MemberNotFound(MemberId),
86
87    #[error("Unauthorized operation: {0}")]
88    Unauthorized(String),
89
90    #[error("Protocol error: {0}")]
91    ProtocolError(String),
92
93    #[error("Serialization error: {0}")]
94    SerializationError(String),
95
96    #[error("Key derivation failed: {0}")]
97    KeyDerivationError(String),
98
99    #[error("Message decryption failed")]
100    DecryptionFailed,
101
102    #[error("Invalid epoch: expected {expected}, got {actual}")]
103    InvalidEpoch { expected: u64, actual: u64 },
104
105    #[error("`TreeKEM` operation failed: {0}")]
106    TreeKemError(String),
107
108    #[error("Invalid message: {0}")]
109    InvalidMessage(String),
110
111    #[error("Deserialization error: {0}")]
112    DeserializationError(String),
113
114    #[error("IO error: {0}")]
115    Io(#[from] std::io::Error),
116}
117
118pub type Result<T> = std::result::Result<T, MlsError>;
119
120/// MLS protocol version
121pub const MLS_VERSION: u16 = 1;
122
123/// Maximum group size (`TreeKEM` limitation)
124pub const MAX_GROUP_SIZE: usize = 65536; // 2^16
125
126/// Key rotation interval
127pub const DEFAULT_KEY_ROTATION_INTERVAL: Duration = Duration::from_secs(24 * 3600); // 24 hours
128
129/// Message sequence number type
130pub type MessageSequence = u64;
131
132/// Epoch number for group state versioning
133pub type EpochNumber = u64;
134
135/// Wire format version for backwards compatibility
136#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
137pub struct WireFormat {
138    pub version: u16,
139    pub extensions: Vec<u16>,
140}
141
142impl Default for WireFormat {
143    fn default() -> Self {
144        Self {
145            version: MLS_VERSION,
146            extensions: Vec::new(),
147        }
148    }
149}
150
151/// MLS configuration parameters
152#[derive(Debug, Clone)]
153pub struct MlsConfig {
154    /// Maximum number of members in a group
155    pub max_group_size: usize,
156    /// Key rotation interval
157    pub key_rotation_interval: Duration,
158    /// Enable post-compromise security
159    pub enable_pcs: bool,
160    /// Enable forward secrecy
161    pub enable_fs: bool,
162    /// Cipher suite to use
163    pub cipher_suite: CipherSuite,
164    /// Maximum message age before rejection
165    pub max_message_age: Duration,
166}
167
168impl Default for MlsConfig {
169    fn default() -> Self {
170        Self {
171            max_group_size: MAX_GROUP_SIZE,
172            key_rotation_interval: DEFAULT_KEY_ROTATION_INTERVAL,
173            enable_pcs: true,
174            enable_fs: true,
175            cipher_suite: CipherSuite::default(),
176            max_message_age: Duration::from_secs(300), // 5 minutes
177        }
178    }
179}
180
181/// MLS statistics for monitoring
182#[derive(Debug, Clone, Default)]
183pub struct MlsStats {
184    pub groups_active: usize,
185    pub messages_sent: u64,
186    pub messages_received: u64,
187    pub key_rotations: u64,
188    pub member_additions: u64,
189    pub member_removals: u64,
190    pub epoch_transitions: u64,
191}
192
193// Default is now derived
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_mls_config_defaults() {
201        let config = MlsConfig::default();
202        assert_eq!(config.max_group_size, MAX_GROUP_SIZE);
203        assert_eq!(config.key_rotation_interval, DEFAULT_KEY_ROTATION_INTERVAL);
204        assert!(config.enable_pcs);
205        assert!(config.enable_fs);
206    }
207
208    #[test]
209    fn test_wire_format_default() {
210        let format = WireFormat::default();
211        assert_eq!(format.version, MLS_VERSION);
212        assert!(format.extensions.is_empty());
213    }
214
215    #[test]
216    fn test_mls_stats_default() {
217        let stats = MlsStats::default();
218        assert_eq!(stats.groups_active, 0);
219        assert_eq!(stats.messages_sent, 0);
220        assert_eq!(stats.messages_received, 0);
221    }
222}