From 6f5838af517a1c5f34a99393a20b8c8fabd80a66 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:21:21 -0700 Subject: [PATCH] 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 --- Cargo.lock | 1 + core/conversations/Cargo.toml | 1 + core/conversations/src/account.rs | 20 ++- core/conversations/src/context.rs | 7 +- core/conversations/src/conversation.rs | 2 +- .../src/conversation/group_v1.rs | 163 +++++++----------- core/conversations/src/inbox_v2.rs | 125 ++++++-------- core/conversations/src/inbox_v2/identity.rs | 67 +++++++ .../src/inbox_v2/mls_provider.rs | 77 +++++++++ 9 files changed, 278 insertions(+), 185 deletions(-) create mode 100644 core/conversations/src/inbox_v2/identity.rs create mode 100644 core/conversations/src/inbox_v2/mls_provider.rs diff --git a/Cargo.lock b/Cargo.lock index 0e1ba08..b9e84aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/core/conversations/Cargo.toml b/core/conversations/Cargo.toml index 9e80862..b699369 100644 --- a/core/conversations/Cargo.toml +++ b/core/conversations/Cargo.toml @@ -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" } diff --git a/core/conversations/src/account.rs b/core/conversations/src/account.rs index 161710f..b59ada2 100644 --- a/core/conversations/src/account.rs +++ b/core/conversations/src/account.rs @@ -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 } diff --git a/core/conversations/src/context.rs b/core/conversations/src/context.rs index e9143fa..315303f 100644 --- a/core/conversations/src/context.rs +++ b/core/conversations/src/context.rs @@ -25,10 +25,11 @@ pub use crate::inbox::Introduction; // Ctx manages lifetimes of objects to process and generate payloads. pub struct Context { identity: Rc, + _account: Rc>, ds: Rc>, store: Rc>, inbox: Inbox, - pq_inbox: InboxV2, + pq_inbox: InboxV2, } impl Context @@ -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, diff --git a/core/conversations/src/conversation.rs b/core/conversations/src/conversation.rs index 8b81fe6..83a869c 100644 --- a/core/conversations/src/conversation.rs +++ b/core/conversations/src/conversation.rs @@ -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; diff --git a/core/conversations/src/conversation/group_v1.rs b/core/conversations/src/conversation/group_v1.rs index ca0c498..3b973b4 100644 --- a/core/conversations/src/conversation/group_v1.rs +++ b/core/conversations/src/conversation/group_v1.rs @@ -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( - &self, - ds: &mut DS, - account_id: &AccountId, - welcome: &MlsMessageOut, - ) -> Result<(), ChatError>; -} - -pub struct GroupV1Convo { - ctx: Rc>, - account_id: AccountId, +pub struct GroupV1Convo { + identity_provider: Rc>>, + mls_provider: Rc>, ds: Rc>, keypkg_provider: Rc>, mls_group: MlsGroup, @@ -78,52 +35,51 @@ pub struct GroupV1Convo { causal: CausalHistoryStore, } -impl std::fmt::Debug for GroupV1Convo +impl std::fmt::Debug for GroupV1Convo 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 GroupV1Convo +impl GroupV1Convo 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>, - account_id: AccountId, + identity_provider: Rc>>, + mls_provider: Rc>, ds: Rc>, keypkg_provider: Rc>, causal: CausalHistoryStore, ) -> Result { 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>, - account_id: AccountId, + identity_provider: Rc>>, + mls_provider: Rc>, ds: Rc>, keypkg_provider: Rc>, causal: CausalHistoryStore, welcome: Welcome, ) -> Result { 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>, - account_id: AccountId, + identity_provider: Rc>>, + mls_provider: Rc>, ds: Rc>, keypkg_provider: Rc>, causal: CausalHistoryStore, convo_id: String, group_id: GroupId, ) -> Result { - 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 Id for GroupV1Convo +impl Id for GroupV1Convo where - MlsCtx: MlsContext, + IP: IdentityProvider, + MP: MlsProvider, DS: DeliveryService, KP: KeyPackageProvider, { @@ -270,9 +223,10 @@ where } } -impl Convo for GroupV1Convo +impl Convo for GroupV1Convo where - MlsCtx: MlsContext, + IP: IdentityProvider, + MP: MlsProvider, DS: DeliveryService, KP: KeyPackageProvider, { @@ -280,16 +234,19 @@ where &mut self, content: &[u8], ) -> Result, 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 GroupConvo for GroupV1Convo +impl GroupConvo for GroupV1Convo 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 { diff --git a/core/conversations/src/inbox_v2.rs b/core/conversations/src/inbox_v2.rs index affcddb..9e41a9a 100644 --- a/core/conversations/src/inbox_v2.rs +++ b/core/conversations/src/inbox_v2.rs @@ -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( - &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::(&["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( + &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 { +pub struct InboxV2 +where + IP: IdentityProvider, +{ + // Account_id field is an owned value, so it can be returned via reference. account_id: AccountId, + account: Rc>>, ds: Rc>, reg_service: Rc>, store: Rc>, causal: CausalHistoryStore, - ctx: Rc>, + mls_provider: Rc>, } -impl InboxV2 +impl InboxV2 where + IP: IdentityProvider, DS: DeliveryService, RS: RegistrationService, CS: ChatStore, { pub fn new( - account: LogosAccount, + account: IP, ds: Rc>, reg_service: Rc>, store: Rc>, ) -> 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, ChatError> { + pub fn create_group_v1( + &self, + ) -> Result, 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 { - 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, ChatError> { + ) -> Result, 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(), diff --git a/core/conversations/src/inbox_v2/identity.rs b/core/conversations/src/inbox_v2/identity.rs new file mode 100644 index 0000000..53ec51a --- /dev/null +++ b/core/conversations/src/inbox_v2/identity.rs @@ -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); + +impl MlsIdentityProvider { + 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 Deref for MlsIdentityProvider { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IdentityProvider for MlsIdentityProvider { + 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 Signer for MlsIdentityProvider { + fn sign(&self, payload: &[u8]) -> Result, SignerError> { + Ok(self.0.sign(payload).as_ref().to_vec()) + } + + fn signature_scheme(&self) -> SignatureScheme { + SignatureScheme::ED25519 + } +} diff --git a/core/conversations/src/inbox_v2/mls_provider.rs b/core/conversations/src/inbox_v2/mls_provider.rs new file mode 100644 index 0000000..6f617bf --- /dev/null +++ b/core/conversations/src/inbox_v2/mls_provider.rs @@ -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 { + let crypto = LibcruxCryptoProvider::new()?; + let storage = MemoryStorage::default(); + + Ok(Self { crypto, storage }) + } +} + +impl MlsProvider for MlsEphemeralPqProvider { + fn invite_user( + &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 + } +}