Add crate logos-accounts (#103)

Update InboxV2 to use IdentProvider

Create Full featured Provider

Introduce MlsIdentityProvider

Flatten MLSContext

Cleanup warnings until future integration PR

remove duplicate

Update account_id comments
This commit is contained in:
Jazz Turner-Baggs 2026-06-02 15:21:21 -07:00 committed by GitHub
parent 4df23aad63
commit 6f5838af51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 278 additions and 185 deletions

1
Cargo.lock generated
View File

@ -1415,6 +1415,7 @@ dependencies = [
"hex",
"openmls",
"openmls_libcrux_crypto 0.3.1",
"openmls_memory_storage 0.5.0",
"openmls_traits 0.5.0",
"prost",
"rand_core 0.6.4",

View File

@ -20,6 +20,7 @@ double-ratchets = { path = "../double-ratchets" }
hex = "0.4.3"
openmls = { version = "0.8.1", features = ["libcrux-provider"] }
openmls_libcrux_crypto = "0.3.1"
openmls_memory_storage = "0.5.0"
openmls_traits = "0.5.0"
prost = "0.14.1"
rand_core = { version = "0.6" }

View File

@ -1,11 +1,13 @@
use crypto::{Ed25519SigningKey, Ed25519VerifyingKey};
use crypto::{Ed25519Signature, Ed25519SigningKey, Ed25519VerifyingKey};
use openmls::prelude::SignatureScheme;
use openmls_traits::signatures::Signer;
use crate::{conversation::IdentityProvider, types::AccountId};
use crate::{AccountId, IdentityProvider};
/// Logos Account represents a single account across
/// multiple installations and services.
///
/// Deprecated!
pub struct LogosAccount {
id: AccountId,
signing_key: Ed25519SigningKey,
@ -25,10 +27,6 @@ impl LogosAccount {
verifying_key,
}
}
pub fn account_id(&self) -> &AccountId {
&self.id
}
}
impl Signer for LogosAccount {
@ -43,10 +41,18 @@ impl Signer for LogosAccount {
}
impl IdentityProvider for LogosAccount {
fn friendly_name(&self) -> String {
fn account_id(&self) -> &AccountId {
&self.id
}
fn display_name(&self) -> String {
self.id.to_string()
}
fn sign(&self, payload: &[u8]) -> Ed25519Signature {
self.signing_key.sign(payload)
}
fn public_key(&self) -> &Ed25519VerifyingKey {
&self.verifying_key
}

View File

@ -25,10 +25,11 @@ pub use crate::inbox::Introduction;
// Ctx manages lifetimes of objects to process and generate payloads.
pub struct Context<DS: DeliveryService, RS: RegistrationService, CS: ChatStore> {
identity: Rc<Identity>,
_account: Rc<RefCell<LogosAccount>>,
ds: Rc<RefCell<DS>>,
store: Rc<RefCell<CS>>,
inbox: Inbox<CS>,
pq_inbox: InboxV2<DS, RS, CS>,
pq_inbox: InboxV2<LogosAccount, DS, RS, CS>,
}
impl<DS, RS, CS> Context<DS, RS, CS>
@ -50,6 +51,7 @@ where
let name = name.into();
// Services for sharing with Converastions/Inboxes
let account = Rc::new(RefCell::new(LogosAccount::new_test(name.to_string())));
let ds = Rc::new(RefCell::new(delivery));
let contact_registry = Rc::new(RefCell::new(registration));
let store = Rc::new(RefCell::new(store));
@ -80,6 +82,7 @@ where
Ok(Self {
identity,
_account: account,
ds,
store,
inbox,
@ -100,6 +103,7 @@ where
let identity = Identity::new(&name);
// Services for sharing with Converastions/Inboxes
let account = Rc::new(RefCell::new(LogosAccount::new_test(name.to_string())));
let ds = Rc::new(RefCell::new(delivery));
let contact_registry = Rc::new(RefCell::new(registration));
let store = Rc::new(RefCell::new(chat_store));
@ -127,6 +131,7 @@ where
Ok(Self {
identity,
_account: account,
ds,
store,
pq_inbox,

View File

@ -12,7 +12,7 @@ use std::fmt::Debug;
use storage::ConversationKind;
pub use crate::errors::ChatError;
pub use group_v1::{GroupV1Convo, IdentityProvider};
pub use group_v1::GroupV1Convo;
pub use privatev1::PrivateV1Convo;
pub type ConversationId = String;

View File

@ -8,15 +8,14 @@ use std::rc::Rc;
use blake2::{Blake2b, Digest, digest::consts::U6};
use chat_proto::logoschat::encryption::{EncryptedPayload, Plaintext, encrypted_payload};
use chat_proto::logoschat::reliability::ReliablePayload;
use crypto::Ed25519VerifyingKey;
use openmls::prelude::tls_codec::Deserialize;
use openmls::prelude::*;
use openmls_libcrux_crypto::Provider as LibcruxProvider;
use openmls_traits::signatures::Signer as OpenMlsSigner;
use prost::Message as _;
use prost::Message;
use storage::ConversationKind;
use crate::IdentityProvider;
use crate::causal_history::CausalHistoryStore;
use crate::inbox_v2::{MlsIdentityProvider, MlsProvider};
use crate::types::AccountId;
use crate::{
DeliveryService,
@ -26,51 +25,9 @@ use crate::{
types::AddressedEncryptedPayload,
};
/// Provides the identity information needed to participate in an MLS group.
///
/// Implementors must also implement [`OpenMlsSigner`] so they can sign MLS
/// messages. The two methods here supply what [`MlsContext::get_credential`]
/// needs to build a [`CredentialWithKey`]: `friendly_name` becomes the
/// `BasicCredential` label and `public_key` becomes the signature-verification key.
pub trait IdentityProvider: OpenMlsSigner {
fn friendly_name(&self) -> String;
fn public_key(&self) -> &Ed25519VerifyingKey;
}
/// Connects the MLS protocol engine to app-level identity and transport.
///
/// `GroupV1Convo` is generic over this trait so the MLS logic stays
/// independent of how identities are stored or how invites are delivered.
/// Implementors supply:
/// - a [`LibcruxProvider`] for MLS crypto operations
/// - an [`IdentityProvider`] for signing and credential construction
/// - [`invite_user`] — the app-specific logic for routing a [`Welcome`]
/// message to a new member's inbox
pub trait MlsContext {
type IDENT: IdentityProvider;
fn ident(&self) -> &Self::IDENT;
fn provider(&self) -> &LibcruxProvider;
// Build an MLS Credential from the supplied IdentityProvider
fn get_credential(&self) -> CredentialWithKey {
CredentialWithKey {
credential: BasicCredential::new(self.ident().friendly_name().into()).into(),
signature_key: self.ident().public_key().as_ref().into(),
}
}
fn invite_user<DS: DeliveryService>(
&self,
ds: &mut DS,
account_id: &AccountId,
welcome: &MlsMessageOut,
) -> Result<(), ChatError>;
}
pub struct GroupV1Convo<MlsCtx, DS, KP> {
ctx: Rc<RefCell<MlsCtx>>,
account_id: AccountId,
pub struct GroupV1Convo<IP: IdentityProvider, MP, DS, KP> {
identity_provider: Rc<RefCell<MlsIdentityProvider<IP>>>,
mls_provider: Rc<RefCell<MP>>,
ds: Rc<RefCell<DS>>,
keypkg_provider: Rc<RefCell<KP>>,
mls_group: MlsGroup,
@ -78,52 +35,51 @@ pub struct GroupV1Convo<MlsCtx, DS, KP> {
causal: CausalHistoryStore,
}
impl<MlsCtx, DS, KP> std::fmt::Debug for GroupV1Convo<MlsCtx, DS, KP>
impl<IP, MP, DS, KP> std::fmt::Debug for GroupV1Convo<IP, MP, DS, KP>
where
MlsCtx: MlsContext,
IP: IdentityProvider,
MP: MlsProvider,
DS: DeliveryService,
KP: KeyPackageProvider,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GroupV1Convo")
.field("name", &self.ctx.borrow().ident().friendly_name())
.field("name", &self.identity_provider.borrow().display_name())
.field("convo_id", &self.convo_id)
.field("mls_epoch", &self.mls_group.epoch())
.finish_non_exhaustive()
}
}
impl<MlsCtx, DS, KP> GroupV1Convo<MlsCtx, DS, KP>
impl<IP, MP, DS, KP> GroupV1Convo<IP, MP, DS, KP>
where
MlsCtx: MlsContext,
IP: IdentityProvider,
MP: MlsProvider,
DS: DeliveryService,
KP: KeyPackageProvider,
{
// Create a new conversation with the creator as the only participant.
pub fn new(
ctx: Rc<RefCell<MlsCtx>>,
account_id: AccountId,
identity_provider: Rc<RefCell<MlsIdentityProvider<IP>>>,
mls_provider: Rc<RefCell<MP>>,
ds: Rc<RefCell<DS>>,
keypkg_provider: Rc<RefCell<KP>>,
causal: CausalHistoryStore,
) -> Result<Self, ChatError> {
let config = Self::mls_create_config();
let mls_group = {
let ctx_ref = ctx.borrow();
MlsGroup::new(
ctx_ref.provider(),
ctx_ref.ident(),
&config,
ctx_ref.get_credential(),
)
.unwrap()
let mls_provider_ref = mls_provider.borrow();
let signer = identity_provider.borrow();
let credential = signer.get_credential();
MlsGroup::new(&*mls_provider_ref, &*signer, &config, credential).unwrap()
};
let convo_id = hex::encode(mls_group.group_id().as_slice());
Self::subscribe(&mut ds.borrow_mut(), &convo_id)?;
Ok(Self {
ctx,
account_id,
identity_provider,
mls_provider,
ds,
keypkg_provider,
mls_group,
@ -134,17 +90,15 @@ where
// Constructs a new conversation upon receiving a MlsWelcome message.
pub fn new_from_welcome(
ctx: Rc<RefCell<MlsCtx>>,
account_id: AccountId,
identity_provider: Rc<RefCell<MlsIdentityProvider<IP>>>,
mls_provider: Rc<RefCell<MP>>,
ds: Rc<RefCell<DS>>,
keypkg_provider: Rc<RefCell<KP>>,
causal: CausalHistoryStore,
welcome: Welcome,
) -> Result<Self, ChatError> {
let mls_group = {
let ctx_borrow = ctx.borrow();
let provider = ctx_borrow.provider();
let provider = &*mls_provider.borrow();
StagedWelcome::build_from_welcome(provider, &Self::mls_join_config(), welcome)
.unwrap()
.build()
@ -157,8 +111,8 @@ where
Self::subscribe(&mut *ds.borrow_mut(), &convo_id)?;
Ok(Self {
ctx,
account_id,
identity_provider,
mls_provider,
ds,
keypkg_provider,
mls_group,
@ -168,23 +122,23 @@ where
}
pub fn load(
ctx: Rc<RefCell<MlsCtx>>,
account_id: AccountId,
identity_provider: Rc<RefCell<MlsIdentityProvider<IP>>>,
mls_provider: Rc<RefCell<MP>>,
ds: Rc<RefCell<DS>>,
keypkg_provider: Rc<RefCell<KP>>,
causal: CausalHistoryStore,
convo_id: String,
group_id: GroupId,
) -> Result<Self, ChatError> {
let mls_group = MlsGroup::load(ctx.borrow().provider().storage(), &group_id)
let mls_group = MlsGroup::load(mls_provider.borrow().storage(), &group_id)
.map_err(ChatError::generic)?
.ok_or_else(|| ChatError::NoConvo("mls group not found".into()))?;
Self::subscribe(&mut *ds.borrow_mut(), &convo_id)?;
Ok(GroupV1Convo {
ctx,
account_id,
identity_provider,
mls_provider,
ds,
keypkg_provider,
mls_group,
@ -251,17 +205,16 @@ where
};
let key_package_in = KeyPackageIn::tls_deserialize(&mut keypkg_bytes.as_slice())?;
let keypkg = key_package_in.validate(
self.ctx.borrow().provider().crypto(),
ProtocolVersion::Mls10,
)?; //TODO: P3 - Hardcoded Protocol Version
let keypkg =
key_package_in.validate(self.mls_provider.borrow().crypto(), ProtocolVersion::Mls10)?; //TODO: P3 - Hardcoded Protocol Version
Ok(keypkg)
}
}
impl<MlsCtx, DS, KP> Id for GroupV1Convo<MlsCtx, DS, KP>
impl<IP, MP, DS, KP> Id for GroupV1Convo<IP, MP, DS, KP>
where
MlsCtx: MlsContext,
IP: IdentityProvider,
MP: MlsProvider,
DS: DeliveryService,
KP: KeyPackageProvider,
{
@ -270,9 +223,10 @@ where
}
}
impl<MlsCtx, DS, KP> Convo for GroupV1Convo<MlsCtx, DS, KP>
impl<IP, MP, DS, KP> Convo for GroupV1Convo<IP, MP, DS, KP>
where
MlsCtx: MlsContext,
IP: IdentityProvider,
MP: MlsProvider,
DS: DeliveryService,
KP: KeyPackageProvider,
{
@ -280,16 +234,19 @@ where
&mut self,
content: &[u8],
) -> Result<Vec<AddressedEncryptedPayload>, ChatError> {
let ctx_ref = self.ctx.borrow();
let provider = ctx_ref.provider();
let sender_id = self.account_id.as_str();
let reliable = self.causal.on_send(&self.convo_id, sender_id, content);
let sender_id = self.identity_provider.borrow();
let reliable =
self.causal
.on_send(&self.convo_id, sender_id.account_id().as_str(), content);
let wire = reliable.encode_to_vec();
let mls_message_out = self
.mls_group
.create_message(provider, ctx_ref.ident(), &wire)
.create_message(
&*self.mls_provider.borrow(),
&*self.identity_provider.borrow(),
&wire,
)
.unwrap();
let a = AddressedEncryptedPayload {
@ -325,8 +282,7 @@ where
.try_into_protocol_message()
.map_err(ChatError::generic)?;
let ctx_borrow = self.ctx.borrow();
let provider = ctx_borrow.provider();
let provider = &*self.mls_provider.borrow();
if protocol_message.epoch() < self.mls_group.epoch() {
// TODO: (P1) Add logging for messages arriving from past epoch.
@ -373,9 +329,10 @@ where
}
}
impl<MlsCtx, DS, KP> GroupConvo<DS, KP> for GroupV1Convo<MlsCtx, DS, KP>
impl<IP, MP, DS, KP> GroupConvo<DS, KP> for GroupV1Convo<IP, MP, DS, KP>
where
MlsCtx: MlsContext,
IP: IdentityProvider,
MP: MlsProvider,
DS: DeliveryService,
KP: KeyPackageProvider,
{
@ -384,8 +341,8 @@ where
// welcome — the Welcome message sent privately to each new joiner
// _group_info — used for external joins; ignore for now
fn add_member(&mut self, members: &[&AccountId]) -> Result<(), ChatError> {
let ctx_ref = self.ctx.borrow();
let provider = ctx_ref.provider();
let identity_provider = &*self.identity_provider.borrow();
let mls_provider = &*self.mls_provider.borrow();
if members.len() > 50 {
// This is a temporary limit that originates from the the De-MLS epoch time.
@ -403,14 +360,18 @@ where
let (commit, welcome, _group_info) = self
.mls_group
.add_members(provider, ctx_ref.ident(), keypkgs.iter().as_slice())
.add_members(mls_provider, identity_provider, keypkgs.iter().as_slice())
.unwrap();
self.mls_group.merge_pending_commit(provider).unwrap();
self.mls_group.merge_pending_commit(mls_provider).unwrap();
// TODO: (P3) Evaluate privacy/performance implications of an aggregated Welcome for multiple users
for account_id in members {
ctx_ref.invite_user(&mut *self.ds.borrow_mut(), account_id, &welcome)?;
self.mls_provider.borrow().invite_user(
&mut *self.ds.borrow_mut(),
account_id,
&welcome,
)?;
}
let encrypted_payload = EncryptedPayload {

View File

@ -1,10 +1,14 @@
mod identity;
mod mls_provider;
pub use identity::MlsIdentityProvider;
use std::cell::RefCell;
use std::rc::Rc;
use chat_proto::logoschat::envelope::EnvelopeV1;
use openmls::prelude::tls_codec::Serialize;
use openmls::prelude::*;
use openmls_libcrux_crypto::Provider as LibcruxProvider;
use prost::{Message, Oneof};
use storage::ChatStore;
use storage::ConversationKind;
@ -13,62 +17,18 @@ use storage::ConversationMeta;
use crate::AddressedEnvelope;
use crate::ChatError;
use crate::DeliveryService;
use crate::IdentityProvider;
use crate::RegistrationService;
use crate::account::LogosAccount;
use crate::causal_history::CausalHistoryStore;
use crate::causal_history::MissingMessage;
use crate::conversation::ConversationId;
use crate::conversation::GroupConvo;
use crate::conversation::group_v1::MlsContext;
use crate::conversation::{GroupV1Convo, Id, IdentityProvider};
// use crate::GroupConvo;
use crate::conversation::{ConversationId, GroupConvo, GroupV1Convo, Id};
use crate::outcomes::{ConversationClass, InboxOutcome, NewConversation};
use crate::types::AccountId;
use crate::utils::{blake2b_hex, hash_size};
pub struct PqMlsContext {
ident_provider: LogosAccount,
provider: LibcruxProvider,
}
impl MlsContext for PqMlsContext {
type IDENT = LogosAccount;
fn ident(&self) -> &LogosAccount {
&self.ident_provider
}
fn provider(&self) -> &LibcruxProvider {
&self.provider
}
fn invite_user<DS: DeliveryService>(
&self,
ds: &mut DS,
account_id: &AccountId,
welcome: &MlsMessageOut,
) -> Result<(), ChatError> {
let invite = GroupV1HeavyInvite {
welcome_bytes: welcome.to_bytes()?,
};
let frame = InboxV2Frame {
payload: Some(InviteType::GroupV1(invite)),
};
let envelope = EnvelopeV1 {
conversation_hint: conversation_id_for(account_id),
salt: 0,
payload: frame.encode_to_vec().into(),
};
let outbound_msg = AddressedEnvelope {
delivery_address: delivery_address_for(account_id),
data: envelope.encode_to_vec(),
};
ds.publish(outbound_msg).map_err(ChatError::generic)?;
Ok(())
}
}
use mls_provider::MlsEphemeralPqProvider;
// Define unique Identifiers derivations used in InboxV2
fn delivery_address_for(account_id: &AccountId) -> String {
@ -79,42 +39,57 @@ fn conversation_id_for(account_id: &AccountId) -> String {
blake2b_hex::<hash_size::ConvoId>(&["InboxV2|", "conversation_id|", account_id.as_str()])
}
/// An Extension trait which extends OpenMlsProvider to add required functionality
/// All MLS based Conversation should use this trait for defining requirements.
pub trait MlsProvider: OpenMlsProvider {
fn invite_user<DS: DeliveryService>(
&self,
ds: &mut DS,
account_id: &AccountId,
welcome: &MlsMessageOut,
) -> Result<(), ChatError>;
}
/// An PQ focused Conversation initializer.
/// InboxV2 Incorporates an Account based identity system to support PQ based conversation protocols
/// such as MLS.
pub struct InboxV2<DS, RS, CS> {
pub struct InboxV2<IP, DS, RS, CS>
where
IP: IdentityProvider,
{
// Account_id field is an owned value, so it can be returned via reference.
account_id: AccountId,
account: Rc<RefCell<MlsIdentityProvider<IP>>>,
ds: Rc<RefCell<DS>>,
reg_service: Rc<RefCell<RS>>,
store: Rc<RefCell<CS>>,
causal: CausalHistoryStore,
ctx: Rc<RefCell<PqMlsContext>>,
mls_provider: Rc<RefCell<MlsEphemeralPqProvider>>,
}
impl<DS, CS, RS> InboxV2<DS, RS, CS>
impl<IP, DS, CS, RS> InboxV2<IP, DS, RS, CS>
where
IP: IdentityProvider,
DS: DeliveryService,
RS: RegistrationService,
CS: ChatStore,
{
pub fn new(
account: LogosAccount,
account: IP,
ds: Rc<RefCell<DS>>,
reg_service: Rc<RefCell<RS>>,
store: Rc<RefCell<CS>>,
) -> Self {
let account_id = account.account_id().clone();
let provider = LibcruxProvider::new().unwrap();
let provider = MlsEphemeralPqProvider::new().unwrap();
Self {
account_id,
account: Rc::new(RefCell::new(MlsIdentityProvider::new(account))),
ds,
reg_service,
store,
causal: CausalHistoryStore::new(),
ctx: Rc::new(RefCell::new(PqMlsContext {
ident_provider: account,
provider,
})),
mls_provider: Rc::new(RefCell::new(provider)),
}
}
@ -130,10 +105,7 @@ where
// "LastResort" package or publish multiple
self.reg_service
.borrow_mut()
.register(
&self.ctx.borrow().ident_provider.friendly_name(),
keypackage_bytes,
)
.register(self.account_id().as_str(), keypackage_bytes)
.map_err(ChatError::generic)
}
@ -145,10 +117,12 @@ where
conversation_id_for(&self.account_id)
}
pub fn create_group_v1(&self) -> Result<GroupV1Convo<PqMlsContext, DS, RS>, ChatError> {
pub fn create_group_v1(
&self,
) -> Result<GroupV1Convo<IP, MlsEphemeralPqProvider, DS, RS>, ChatError> {
GroupV1Convo::new(
self.ctx.clone(),
self.account_id.clone(),
self.account.clone(),
self.mls_provider.clone(),
self.ds.clone(),
self.reg_service.clone(),
self.causal.clone(),
@ -193,8 +167,8 @@ where
};
let convo = GroupV1Convo::new_from_welcome(
self.ctx.clone(),
self.account_id.clone(),
self.account.clone(),
self.mls_provider.clone(),
self.ds.clone(),
self.reg_service.clone(),
self.causal.clone(),
@ -212,20 +186,21 @@ where
}
fn create_keypackage(&self) -> Result<KeyPackage, ChatError> {
let ctx_borrow = self.ctx.borrow();
let capabilities = Capabilities::builder()
.ciphersuites(vec![
Ciphersuite::MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519,
])
.extensions(vec![ExtensionType::ApplicationId])
.build();
let signer = self.account.borrow();
let a = KeyPackage::builder()
.leaf_node_capabilities(capabilities)
.build(
Ciphersuite::MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519,
ctx_borrow.provider(),
ctx_borrow.ident(),
ctx_borrow.get_credential(),
&*self.mls_provider.borrow(),
&*signer,
signer.get_credential(),
)
.expect("Failed to build KeyPackage");
@ -235,12 +210,12 @@ where
pub fn load_mls_convo(
&self,
convo_id: String,
) -> Result<GroupV1Convo<PqMlsContext, DS, RS>, ChatError> {
) -> Result<GroupV1Convo<IP, MlsEphemeralPqProvider, DS, RS>, ChatError> {
let group_id_bytes = hex::decode(&convo_id).map_err(ChatError::generic)?;
let group_id = GroupId::from_slice(&group_id_bytes);
let convo = GroupV1Convo::load(
self.ctx.clone(),
self.account_id.clone(),
self.account.clone(),
self.mls_provider.clone(),
self.ds.clone(),
self.reg_service.clone(),
self.causal.clone(),

View File

@ -0,0 +1,67 @@
use std::ops::Deref;
use openmls::credentials::{BasicCredential, CredentialWithKey};
use openmls_traits::{
signatures::{Signer, SignerError},
types::SignatureScheme,
};
use crate::{AccountId, IdentityProvider};
/// A Wrapper for an IdentityProvider which provides MLS specific functionality
///
/// This type stops OpenMLS internal from leaking outside of the crate.
/// Developers provider a simple IdentitityProvider, and Signer and Credential generation
/// is provided
pub struct MlsIdentityProvider<T: IdentityProvider>(T);
impl<T: IdentityProvider> MlsIdentityProvider<T> {
pub fn new(inner: T) -> Self {
Self(inner)
}
pub fn get_credential(&self) -> CredentialWithKey {
CredentialWithKey {
credential: BasicCredential::new(self.account_id().as_str().as_bytes().to_vec()).into(),
signature_key: self.public_key().as_ref().into(),
}
}
}
impl<T: IdentityProvider> Deref for MlsIdentityProvider<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: IdentityProvider> IdentityProvider for MlsIdentityProvider<T> {
fn account_id(&self) -> &AccountId {
self.0.account_id()
}
fn display_name(&self) -> String {
self.0.display_name()
}
fn sign(&self, payload: &[u8]) -> crypto::Ed25519Signature {
self.0.sign(payload)
}
fn public_key(&self) -> &crypto::Ed25519VerifyingKey {
self.0.public_key()
}
}
// Implement Signer directly for MlsIdentityProvider, so that openmls Signer contstraint
// does not leave the module.
impl<T: IdentityProvider> Signer for MlsIdentityProvider<T> {
fn sign(&self, payload: &[u8]) -> Result<Vec<u8>, SignerError> {
Ok(self.0.sign(payload).as_ref().to_vec())
}
fn signature_scheme(&self) -> SignatureScheme {
SignatureScheme::ED25519
}
}

View File

@ -0,0 +1,77 @@
use openmls::framing::MlsMessageOut;
use openmls_libcrux_crypto::CryptoProvider as LibcruxCryptoProvider;
use openmls_memory_storage::MemoryStorage;
use openmls_traits::OpenMlsProvider;
use openmls_traits::types::CryptoError;
use prost::Message;
use crate::{AccountId, ChatError, DeliveryService};
use super::{
AddressedEnvelope, EnvelopeV1, GroupV1HeavyInvite, InboxV2Frame, InviteType, MlsProvider,
conversation_id_for, delivery_address_for,
};
/// This is a Post-Quantum based MLS provider with in memory storage
pub struct MlsEphemeralPqProvider {
crypto: LibcruxCryptoProvider,
storage: MemoryStorage,
}
impl MlsEphemeralPqProvider {
pub fn new() -> Result<Self, CryptoError> {
let crypto = LibcruxCryptoProvider::new()?;
let storage = MemoryStorage::default();
Ok(Self { crypto, storage })
}
}
impl MlsProvider for MlsEphemeralPqProvider {
fn invite_user<DS: DeliveryService>(
&self,
ds: &mut DS,
account_id: &AccountId,
welcome: &MlsMessageOut,
) -> Result<(), ChatError> {
let invite = GroupV1HeavyInvite {
welcome_bytes: welcome.to_bytes()?,
};
let frame = InboxV2Frame {
payload: Some(InviteType::GroupV1(invite)),
};
let envelope = EnvelopeV1 {
conversation_hint: conversation_id_for(account_id),
salt: 0,
payload: frame.encode_to_vec().into(),
};
let outbound_msg = AddressedEnvelope {
delivery_address: delivery_address_for(account_id),
data: envelope.encode_to_vec(),
};
ds.publish(outbound_msg).map_err(ChatError::generic)?;
Ok(())
}
}
impl OpenMlsProvider for MlsEphemeralPqProvider {
type CryptoProvider = LibcruxCryptoProvider;
type RandProvider = LibcruxCryptoProvider;
type StorageProvider = openmls_memory_storage::MemoryStorage;
fn storage(&self) -> &Self::StorageProvider {
&self.storage
}
fn crypto(&self) -> &Self::CryptoProvider {
&self.crypto
}
fn rand(&self) -> &Self::RandProvider {
&self.crypto
}
}