OpenMLS Traits
⚠️ These traits are responsible for all cryptographic operations and randomness within OpenMLS. Please ensure you know what you're doing when implementing your own versions.
Because implementing the OpenMLSCryptoProvider
is challenging, requires
tremendous care, and is not what the average OpenMLS consumer wants to (or should)
do, we provide two implementations that can be used.
Rust Crypto Provider The go-to default at the moment is an implementation using commonly used, native Rust crypto implementations.
Libcrux Crypto Provider A crypto provider backed by the high-assurance cryptography library [libcrux]. Currently only supports relatively modern x86 and amd64 CPUs, as it requires AES-NI, SIMD and AVX.
The Traits
There are 4 different traits defined in the OpenMLS traits crate.
OpenMlsRand
This trait defines two functions to generate arrays and vectors, and is used by OpenMLS to generate randomness for key generation and random identifiers. While there is the commonly used rand crate, not all implementations use it. OpenMLS, therefore, defines its own randomness trait that needs to be implemented by an OpenMLS crypto provider. It simply needs to implement two functions to generate cryptographically secure randomness and store it in an array or vector.
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 bytes.
fn random_vec(&self, len: usize) -> Result<Vec<u8>, Self::Error>;
}
OpenMlsCrypto
This trait defines all cryptographic functions required by OpenMLS. In particular:
- HKDF
- Hashing
- AEAD
- Signatures
- HPKE
};
OpenMlsKeyStore
This trait defines a CRUD API for a key store that is used to store long-term key material from OpenMLS.
The key store provides functions to store
, read
, and delete
values.
Note that it does not allow updating values.
Instead, entries must be deleted and newly stored.
/// Identifier used to downcast the actual entity within an [OpenMlsKeyStore] method.
/// In case for example you need to select a SQL table depending on the entity type
const ID: MlsEntityId;
}
/// Blanket impl for when you have to lookup a list of entities from the keystore
impl<T> MlsEntity for Vec<T>
where
T: MlsEntity + std::fmt::Debug,
{
const ID: MlsEntityId = T::ID;
}
/// The Key Store trait
pub trait OpenMlsKeyStore {
/// The error type returned by the [`OpenMlsKeyStore`].
type Error: std::error::Error + std::fmt::Debug;
/// Store a value `v` that implements the [`MlsEntity`] trait for
/// serialization for ID `k`.
///
/// Returns an error if storing fails.
fn store<V: MlsEntity>(&self, k: &[u8], v: &V) -> Result<(), Self::Error>
where
Self: Sized;
NOTE: Right now, key material must be extracted from the key store. This will most likely change in the future.
OpenMlsCryptoProvider
Additionally, there's a wrapper trait defined that is expected to be passed into the public OpenMLS API. Some OpenMLS APIs require only one of the sub-traits, though.
/// to perform randomness generation, cryptographic operations, and key storage.
pub trait OpenMlsProvider {
type CryptoProvider: crypto::OpenMlsCrypto;
type RandProvider: random::OpenMlsRand;
type KeyStoreProvider: key_store::OpenMlsKeyStore;
/// Get the crypto provider.
fn crypto(&self) -> &Self::CryptoProvider;
/// Get the randomness provider.
fn rand(&self) -> &Self::RandProvider;
/// Get the key store provider.
fn key_store(&self) -> &Self::KeyStoreProvider;
Implementation Notes
It is not necessary to implement all sub-traits if one functionality is missing. Suppose you want to use a persisting key store. In that case, it is sufficient to do a new implementation of the key store trait and combine it with one of the provided crypto and randomness trait implementations.