From 00de6d8e758f5d0dd39a6dcffaea41760d7d1e18 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 11 May 2026 09:33:01 -0700 Subject: [PATCH] Add Stub for GroupV2 --- core/core_client/src/conversation.rs | 1 + core/core_client/src/conversation/group_v2.rs | 203 ++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 core/core_client/src/conversation/group_v2.rs diff --git a/core/core_client/src/conversation.rs b/core/core_client/src/conversation.rs index e257c6b..64a0c02 100644 --- a/core/core_client/src/conversation.rs +++ b/core/core_client/src/conversation.rs @@ -1,4 +1,5 @@ mod group_v1; +mod group_v2; use crate::{AccountId, ContentData, DeliveryService, RegistrationService}; use chat_proto::logoschat::encryption::EncryptedPayload; diff --git a/core/core_client/src/conversation/group_v2.rs b/core/core_client/src/conversation/group_v2.rs new file mode 100644 index 0000000..bfc0692 --- /dev/null +++ b/core/core_client/src/conversation/group_v2.rs @@ -0,0 +1,203 @@ +/// GroupV2 is a conversationType which provides effecient handling of multiple participants +/// Properties: +/// - Harvest Now Decrypt Later (HNDL) protection provided by XWING +/// - Multiple +use std::cell::RefCell; +use std::rc::Rc; + +use blake2::{Blake2b, Digest, digest::consts::U6}; +use chat_proto::logoschat::encryption::{EncryptedPayload, Plaintext, encrypted_payload}; +use openmls::prelude::tls_codec::Deserialize; +use openmls::prelude::*; + +use crate::AccountId; +use crate::conversation::{ConversationIdRef, ServiceContext}; +use crate::inbox_v2::{MlsIdentityProvider, MlsProvider}; +use crate::{ + AddressedEncryptedPayload, ContentData, DeliveryService, IdentityProvider, RegistrationService, + conversation::{BaseConvo, BaseGroupConvo, ChatError, Id}, +}; + +pub struct GroupV2Convo { + mls_provider: Rc>, + convo_id: String, +} + +impl std::fmt::Debug for GroupV2Convo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GroupV2Convo") + .field("convo_id", &self.convo_id) + .finish_non_exhaustive() + } +} + +impl GroupV2Convo { + // Create a new conversation with the creator as the only participant. + pub fn new( + identity_provider: MlsIdentityProvider, + mls_provider: Rc>, + ) -> Result { + // let config = Self::mls_create_config(); + // let credential = identity_provider.get_credential(); + + todo!(); + } + + // Constructs a new conversation upon receiving a MlsWelcome message. + pub fn new_from_welcome( + mls_provider: Rc>, + welcome: Welcome, + ) -> Result { + todo!() + } + + fn mls_create_config() -> MlsGroupCreateConfig { + MlsGroupCreateConfig::builder() + .ciphersuite(Ciphersuite::MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519) + .use_ratchet_tree_extension(true) // This is handy for now, until there is central store for this data + .build() + } + + fn mls_join_config() -> MlsGroupJoinConfig { + MlsGroupJoinConfig::builder().build() + } + + fn delivery_address_from_id(convo_id: &str) -> String { + let hash = Blake2b::::new() + .chain_update("delivery_addr|") + .chain_update(convo_id) + .finalize(); + hex::encode(hash) + } + + fn delivery_address(&self) -> String { + Self::delivery_address_from_id(&self.convo_id) + } + + fn ctrl_delivery_address_from_id(convo_id: &str) -> String { + Self::delivery_address_from_id(convo_id) + } + + fn ctrl_delivery_address(&self) -> String { + Self::ctrl_delivery_address_from_id(&self.convo_id) + } +} + +impl Id for GroupV2Convo +where + MP: MlsProvider, +{ + fn id(&self) -> ConversationIdRef<'_> { + &self.convo_id + } +} + +impl BaseConvo for GroupV2Convo +where + IP: IdentityProvider, + MP: MlsProvider, + DS: DeliveryService, + RS: RegistrationService, + // KP: RegistrationService, +{ + fn init(&self, service_ctx: &mut super::ServiceContext) -> Result<(), ChatError> { + // Configure the delivery service to listen for the required delivery addresses. + + service_ctx + .ds + .subscribe(&Self::delivery_address_from_id(&self.convo_id)) + .map_err(ChatError::generic)?; + service_ctx + .ds + .subscribe(&Self::ctrl_delivery_address_from_id(&self.convo_id)) + .map_err(ChatError::generic)?; + + Ok(()) + } + + fn send_content( + &mut self, + service_ctx: &mut super::ServiceContext, + content: &[u8], + ) -> Result<(), ChatError> { + let signer = MlsIdentityProvider(&service_ctx.identity_provider); + + todo!(); + } + + fn handle_frame( + &mut self, + _service_ctx: &mut super::ServiceContext, + encoded_payload: EncryptedPayload, + ) -> Result, ChatError> { + let bytes = match encoded_payload.encryption { + Some(encrypted_payload::Encryption::Plaintext(pt)) => pt.payload, + _ => { + return Err(ChatError::generic("Expected plaintext")); + } + }; + + todo!() + } +} + +impl BaseGroupConvo for GroupV2Convo +where + IP: IdentityProvider, + MP: MlsProvider, + DS: DeliveryService, + RS: RegistrationService, +{ + // add_members returns: + // commit — the Commit message Alice broadcasts to all members + // welcome — the Welcome message sent privately to each new joiner + // _group_info — used for external joins; ignore for now + fn add_member( + &mut self, + service_ctx: &mut ServiceContext, + members: &[&AccountId], + ) -> Result<(), ChatError> { + 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. + return Err(ChatError::generic( + "Cannot add more than 50 Members at a time", + )); + } + + if members.is_empty() { + return Ok(()); + } + + todo!(); + } +} + +impl GroupV2Convo { + fn key_package_for_account< + IP: IdentityProvider, + DS: DeliveryService, + RS: RegistrationService, + >( + &self, + service_ctx: &mut ServiceContext, + ident: &AccountId, + ) -> Result { + let retrieved_bytes = service_ctx + .rs + .retrieve(ident) + .map_err(|e: RS::Error| ChatError::Generic(e.to_string()))?; + + // dbg!(ctx.contact_registry()); + let Some(keypkg_bytes) = retrieved_bytes else { + return Err(ChatError::generic("Group Contact Not Found")); + }; + + let key_package_in = KeyPackageIn::tls_deserialize(&mut keypkg_bytes.as_slice())?; + let keypkg = key_package_in + .validate(self.mls_provider.borrow().crypto(), ProtocolVersion::Mls10) + .map_err(ChatError::generic)?; //TODO: P3 - Hardcoded Protocol Version + Ok(keypkg) + } +}