Documentation Index Fetch the complete documentation index at: https://mintlify.com/openmls/openmls/llms.txt
Use this file to discover all available pages before exploring further.
The OpenMlsRand trait defines the functionality required by OpenMLS to source cryptographically secure randomness. It is used for key generation, random identifiers, and other operations requiring unpredictable values.
Trait definition
The random provider trait is simple but critical:
pub trait OpenMlsRand {
type Error : std :: error :: Error + Debug ;
/// Fill an array with random bytes.
fn random_array < const N : usize >( & self ) -> Result <[ u8 ; N ], Self :: Error >;
/// Fill a vector of length `len` with random bytes.
fn random_vec ( & self , len : usize ) -> Result < Vec < u8 >, Self :: Error >;
}
Why a custom trait?
While Rust has the commonly used rand crate , not all implementations use it. OpenMLS defines its own randomness trait because:
Different platforms may have different entropy sources
Some environments (like secure enclaves or embedded systems) need custom RNG implementations
Applications may need to integrate with specific cryptographic libraries or hardware RNGs
WebAssembly and other environments require platform-specific random sources
Required methods
Generate random array
fn random_array < const N : usize >( & self ) -> Result <[ u8 ; N ], Self :: Error >;
Fills a fixed-size array with cryptographically secure random bytes. This is useful when the size is known at compile time.
Example usage:
// Generate a 32-byte random value
let random_bytes : [ u8 ; 32 ] = provider . rand () . random_array () ? ;
Generate random vector
fn random_vec ( & self , len : usize ) -> Result < Vec < u8 >, Self :: Error >;
Fills a vector of the specified length with cryptographically secure random bytes. This is useful when the size is determined at runtime.
Example usage:
// Generate 64 random bytes
let random_bytes : Vec < u8 > = provider . rand () . random_vec ( 64 ) ? ;
Example implementation
Here’s how the Rust Crypto provider implements OpenMlsRand:
use openmls_traits :: random :: OpenMlsRand ;
use rand :: { RngCore , SeedableRng };
use std :: sync :: RwLock ;
#[derive( Debug )]
pub struct RustCrypto {
rng : RwLock < rand_chacha :: ChaCha20Rng >,
}
impl Default for RustCrypto {
fn default () -> Self {
Self {
rng : RwLock :: new ( rand_chacha :: ChaCha20Rng :: from_entropy ()),
}
}
}
impl OpenMlsRand for RustCrypto {
type Error = RandError ;
fn random_array < const N : usize >( & self ) -> Result <[ u8 ; N ], Self :: Error > {
let mut rng = self . rng . write ()
. map_err ( | _ | Self :: Error :: LockPoisoned ) ? ;
let mut out = [ 0 u8 ; N ];
rng . try_fill_bytes ( & mut out )
. map_err ( | _ | Self :: Error :: NotEnoughRandomness ) ? ;
Ok ( out )
}
fn random_vec ( & self , len : usize ) -> Result < Vec < u8 >, Self :: Error > {
let mut rng = self . rng . write ()
. map_err ( | _ | Self :: Error :: LockPoisoned ) ? ;
let mut out = vec! [ 0 u8 ; len ];
rng . try_fill_bytes ( & mut out )
. map_err ( | _ | Self :: Error :: NotEnoughRandomness ) ? ;
Ok ( out )
}
}
#[derive(thiserror :: Error , Debug , Copy , Clone , PartialEq , Eq )]
pub enum RandError {
#[error( "RNG lock is poisoned." )]
LockPoisoned ,
#[error( "Unable to collect enough randomness." )]
NotEnoughRandomness ,
}
Key implementation details
Thread safety
The implementation uses RwLock to ensure thread-safe access to the RNG state. This is important because:
OpenMLS may be used in multi-threaded environments
Multiple operations might need random values concurrently
RNG state must be properly protected from race conditions
Entropy source
The example uses ChaCha20Rng::from_entropy() which:
Seeds the RNG from the operating system’s entropy source
Provides a cryptographically secure pseudo-random number generator
Is suitable for cryptographic operations
Error handling
The implementation defines specific error types:
LockPoisoned: The RNG lock is in a poisoned state (typically from a panic in another thread)
NotEnoughRandomness: The RNG couldn’t generate the requested randomness
Using OS random sources directly
For some platforms, you might want to use OS-specific APIs:
use getrandom :: getrandom;
pub struct OsRandomProvider ;
impl OpenMlsRand for OsRandomProvider {
type Error = getrandom :: Error ;
fn random_array < const N : usize >( & self ) -> Result <[ u8 ; N ], Self :: Error > {
let mut out = [ 0 u8 ; N ];
getrandom ( & mut out ) ? ;
Ok ( out )
}
fn random_vec ( & self , len : usize ) -> Result < Vec < u8 >, Self :: Error > {
let mut out = vec! [ 0 u8 ; len ];
getrandom ( & mut out ) ? ;
Ok ( out )
}
}
WebAssembly implementation
For WebAssembly environments:
#[cfg(target_arch = "wasm32" )]
pub struct WebRandomProvider ;
#[cfg(target_arch = "wasm32" )]
impl OpenMlsRand for WebRandomProvider {
type Error = JsError ;
fn random_array < const N : usize >( & self ) -> Result <[ u8 ; N ], Self :: Error > {
let mut out = [ 0 u8 ; N ];
// Use web_sys::window().crypto().get_random_values()
get_random_values ( & mut out ) ? ;
Ok ( out )
}
fn random_vec ( & self , len : usize ) -> Result < Vec < u8 >, Self :: Error > {
let mut out = vec! [ 0 u8 ; len ];
get_random_values ( & mut out ) ? ;
Ok ( out )
}
}
Hardware RNG integration
For systems with hardware RNG support:
pub struct HardwareRandomProvider {
// Handle to hardware RNG device
device : HardwareRngDevice ,
}
impl OpenMlsRand for HardwareRandomProvider {
type Error = HardwareRngError ;
fn random_array < const N : usize >( & self ) -> Result <[ u8 ; N ], Self :: Error > {
let mut out = [ 0 u8 ; N ];
self . device . fill_bytes ( & mut out ) ? ;
Ok ( out )
}
fn random_vec ( & self , len : usize ) -> Result < Vec < u8 >, Self :: Error > {
let mut out = vec! [ 0 u8 ; len ];
self . device . fill_bytes ( & mut out ) ? ;
Ok ( out )
}
}
Security considerations
The random provider is critical for security. Weak or predictable random values can compromise the entire MLS protocol.
Requirements for cryptographic randomness
Unpredictability : Output must be computationally indistinguishable from true random
Proper seeding : RNG must be seeded from a secure entropy source
No bias : All output bits should have equal probability
Forward secrecy : Past outputs should not reveal future outputs
Backtracking resistance : Compromise of current state should not reveal past outputs
Testing randomness
While you cannot fully test randomness in automated tests, you should:
Verify the implementation uses a CSPRNG (Cryptographically Secure Pseudo-Random Number Generator)
Ensure proper seeding from OS entropy sources
Test error handling (e.g., what happens if entropy is exhausted)
Verify thread safety under concurrent access
#[test]
fn test_random_provider () {
let provider = RustCrypto :: default ();
// Test array generation
let arr1 : [ u8 ; 32 ] = provider . random_array () . unwrap ();
let arr2 : [ u8 ; 32 ] = provider . random_array () . unwrap ();
assert_ne! ( arr1 , arr2 , "Random arrays should be different" );
// Test vector generation
let vec1 = provider . random_vec ( 64 ) . unwrap ();
let vec2 = provider . random_vec ( 64 ) . unwrap ();
assert_ne! ( vec1 , vec2 , "Random vectors should be different" );
assert_eq! ( vec1 . len (), 64 , "Vector should have requested length" );
}
Combining with crypto provider
Often the same type implements both OpenMlsCrypto and OpenMlsRand:
pub struct RustCrypto {
rng : RwLock < rand_chacha :: ChaCha20Rng >,
}
impl OpenMlsCrypto for RustCrypto {
// ... crypto implementations
}
impl OpenMlsRand for RustCrypto {
// ... random implementations
}
// Use in provider
impl OpenMlsProvider for OpenMlsRustCrypto {
type CryptoProvider = RustCrypto ;
type RandProvider = RustCrypto ; // Same type!
type StorageProvider = MemoryStorage ;
fn crypto ( & self ) -> & Self :: CryptoProvider {
& self . crypto
}
fn rand ( & self ) -> & Self :: RandProvider {
& self . crypto // Same instance
}
fn storage ( & self ) -> & Self :: StorageProvider {
& self . storage
}
}
Using the random provider
Once implemented, the random provider is used throughout OpenMLS:
use openmls :: prelude ::* ;
let provider = OpenMlsRustCrypto :: default ();
// OpenMLS uses the random provider internally for:
// - Generating key material
// - Creating random identifiers
// - Nonce generation
// - Other cryptographic operations requiring randomness
let credential = BasicCredential :: new ( b"user@example.com" . to_vec ());
let credential_with_key = CredentialWithKey {
credential : credential . into (),
signature_key : provider . crypto () . signature_key_gen (
SignatureScheme :: ED25519
) ?. 1. into (),
};
// Key package generation uses random provider internally
let key_package = KeyPackage :: builder ()
. build ( ciphersuite , & provider , & signer , credential_with_key ) ? ;
See also
Crypto provider Learn about the crypto provider trait
Storage provider Learn about the storage provider trait
Custom implementation Complete guide to implementing custom providers
Overview Back to provider traits overview