mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-05-12 13:09:29 +00:00
Update Accounts + service_traits
This commit is contained in:
parent
def297f132
commit
3bf8ecb904
@ -1,14 +1,15 @@
|
|||||||
use crypto::Ed25519SigningKey;
|
use crypto::{Ed25519SigningKey, Ed25519VerifyingKey};
|
||||||
use openmls::prelude::SignatureScheme;
|
use openmls::prelude::SignatureScheme;
|
||||||
use openmls_traits::signatures::Signer;
|
use openmls_traits::signatures::Signer;
|
||||||
|
|
||||||
use crate::types::AccountId;
|
use crate::{conversation::IdentityProvider, types::AccountId};
|
||||||
|
|
||||||
/// Logos Account represents a single account across
|
/// Logos Account represents a single account across
|
||||||
/// multiple installations and services.
|
/// multiple installations and services.
|
||||||
pub struct LogosAccount {
|
pub struct LogosAccount {
|
||||||
id: AccountId,
|
id: AccountId,
|
||||||
signing_key: Ed25519SigningKey,
|
signing_key: Ed25519SigningKey,
|
||||||
|
verifying_key: Ed25519VerifyingKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogosAccount {
|
impl LogosAccount {
|
||||||
@ -17,9 +18,11 @@ impl LogosAccount {
|
|||||||
/// TODO: (P1) Remove once implementation is ready.
|
/// TODO: (P1) Remove once implementation is ready.
|
||||||
pub fn new_test(explicit_id: impl Into<String>) -> Self {
|
pub fn new_test(explicit_id: impl Into<String>) -> Self {
|
||||||
let signing_key = Ed25519SigningKey::generate();
|
let signing_key = Ed25519SigningKey::generate();
|
||||||
|
let verifying_key = signing_key.verifying_key()
|
||||||
Self {
|
Self {
|
||||||
id: AccountId::new(explicit_id.into()),
|
id: AccountId::new(explicit_id.into()),
|
||||||
signing_key,
|
signing_key,
|
||||||
|
verifying_key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,3 +41,13 @@ impl Signer for LogosAccount {
|
|||||||
SignatureScheme::ED25519
|
SignatureScheme::ED25519
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IdentityProvider for LogosAccount {
|
||||||
|
fn friendly_name(&self) -> String {
|
||||||
|
self.id.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_key(&self) -> &Ed25519VerifyingKey {
|
||||||
|
&self.verifying_key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use std::cell::Ref;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::account::LogosAccount;
|
||||||
use crate::conversation::{Convo, GroupConvo, IdentityProvider};
|
use crate::conversation::{Convo, GroupConvo, IdentityProvider};
|
||||||
use crate::ctx::ClientCtx;
|
use crate::ctx::ClientCtx;
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ use crate::{
|
|||||||
inbox::Inbox,
|
inbox::Inbox,
|
||||||
inbox_v2::InboxV2,
|
inbox_v2::InboxV2,
|
||||||
proto::{EncryptedPayload, EnvelopeV1, Message},
|
proto::{EncryptedPayload, EnvelopeV1, Message},
|
||||||
types::{AddressedEnvelope, ContentData},
|
types::{AccountId, AddressedEnvelope, ContentData},
|
||||||
};
|
};
|
||||||
use crypto::{Identity, PublicKey};
|
use crypto::{Identity, PublicKey};
|
||||||
use storage::{ChatStore, ConversationKind};
|
use storage::{ChatStore, ConversationKind};
|
||||||
@ -59,11 +60,11 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
let identity = Rc::new(identity);
|
let identity = Rc::new(identity);
|
||||||
let inbox = Inbox::new(Rc::clone(&store), Rc::clone(&identity));
|
let inbox = Inbox::new(Rc::clone(&store), Rc::clone(&identity));
|
||||||
|
|
||||||
let pq_inbox = InboxV2::new();
|
let pq_inbox = InboxV2::new_with_account(LogosAccount::new_test(name));
|
||||||
|
|
||||||
// Subscribe
|
// Subscribe
|
||||||
ctx.ds()
|
ctx.ds()
|
||||||
.subscribe(pq_inbox.delivery_address())
|
.subscribe(&pq_inbox.delivery_address())
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -97,11 +98,11 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
|
|
||||||
let identity = Rc::new(identity);
|
let identity = Rc::new(identity);
|
||||||
let inbox = Inbox::new(Rc::clone(&chat_store), Rc::clone(&identity));
|
let inbox = Inbox::new(Rc::clone(&chat_store), Rc::clone(&identity));
|
||||||
let mut pq_inbox = InboxV2::new();
|
let mut pq_inbox = InboxV2::new_with_account(LogosAccount::new_test(name));
|
||||||
pq_inbox.register(&mut ctx)?;
|
pq_inbox.register(&mut ctx)?;
|
||||||
|
|
||||||
ctx.ds()
|
ctx.ds()
|
||||||
.subscribe(pq_inbox.delivery_address())
|
.subscribe(&pq_inbox.delivery_address())
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -128,8 +129,8 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the unique identifier associated with the account
|
/// Returns the unique identifier associated with the account
|
||||||
pub fn account_id(&self) -> String {
|
pub fn account_id(&self) -> &AccountId {
|
||||||
self.pq_inbox.account.friendly_name()
|
self.pq_inbox.account_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn installation_name(&self) -> &str {
|
pub fn installation_name(&self) -> &str {
|
||||||
@ -162,7 +163,7 @@ impl<DS: DeliveryService, RS: RegistrationService, CS: ChatStore + 'static> Cont
|
|||||||
|
|
||||||
pub fn create_group_convo(
|
pub fn create_group_convo(
|
||||||
&mut self,
|
&mut self,
|
||||||
participants: &[&str],
|
participants: &[&AccountId],
|
||||||
) -> Result<Box<dyn GroupConvo<DS, RS, CS>>, ChatError> {
|
) -> Result<Box<dyn GroupConvo<DS, RS, CS>>, ChatError> {
|
||||||
let mut convo = self.pq_inbox.create_group_v1(&mut self.client_ctx)?;
|
let mut convo = self.pq_inbox.create_group_v1(&mut self.client_ctx)?;
|
||||||
self.client_ctx
|
self.client_ctx
|
||||||
|
|||||||
@ -2,9 +2,9 @@ pub mod group_v1;
|
|||||||
mod privatev1;
|
mod privatev1;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DeliveryService, RegistrationService,
|
DeliveryService, service_traits::KeyPackageProvider,
|
||||||
ctx::ClientCtx,
|
ctx::ClientCtx,
|
||||||
types::{AddressedEncryptedPayload, ContentData},
|
types::{AccountId, AddressedEncryptedPayload, ContentData},
|
||||||
};
|
};
|
||||||
use chat_proto::logoschat::encryption::EncryptedPayload;
|
use chat_proto::logoschat::encryption::EncryptedPayload;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@ -42,11 +42,11 @@ pub trait Convo: Id + Debug {
|
|||||||
fn convo_type(&self) -> ConversationKind;
|
fn convo_type(&self) -> ConversationKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GroupConvo<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>: Convo {
|
pub trait GroupConvo<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>: Convo {
|
||||||
fn add_member(
|
fn add_member(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
members: &[&str],
|
members: &[&AccountId],
|
||||||
) -> Result<(), ChatError>;
|
) -> Result<(), ChatError>;
|
||||||
|
|
||||||
// Default implementation which dispatches envelopes to the DeliveryService
|
// Default implementation which dispatches envelopes to the DeliveryService
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::cell::{Ref, RefCell};
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use blake2::{Blake2b, Digest, digest::consts::U6};
|
use blake2::{Blake2b, Digest, digest::consts::U6};
|
||||||
@ -10,8 +10,9 @@ use openmls_libcrux_crypto::Provider as LibcruxProvider;
|
|||||||
use openmls_traits::signatures::Signer as OpenMlsSigner;
|
use openmls_traits::signatures::Signer as OpenMlsSigner;
|
||||||
use storage::{ChatStore, ConversationKind};
|
use storage::{ChatStore, ConversationKind};
|
||||||
|
|
||||||
|
use crate::types::AccountId;
|
||||||
use crate::{
|
use crate::{
|
||||||
DeliveryService, RegistrationService,
|
DeliveryService, service_traits::KeyPackageProvider,
|
||||||
conversation::{ChatError, ConversationId, Convo, GroupConvo, Id},
|
conversation::{ChatError, ConversationId, Convo, GroupConvo, Id},
|
||||||
ctx::ClientCtx,
|
ctx::ClientCtx,
|
||||||
types::{AddressedEncryptedPayload, ContentData},
|
types::{AddressedEncryptedPayload, ContentData},
|
||||||
@ -19,37 +20,38 @@ use crate::{
|
|||||||
|
|
||||||
pub trait IdentityProvider: OpenMlsSigner {
|
pub trait IdentityProvider: OpenMlsSigner {
|
||||||
fn friendly_name(&self) -> String;
|
fn friendly_name(&self) -> String;
|
||||||
fn public_key(&self) -> Ed25519VerifyingKey;
|
fn public_key(&self) -> &Ed25519VerifyingKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MlsInitializer {
|
pub trait MlsContext {
|
||||||
fn invite_to_group_v1<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
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, RS: KeyPackageProvider, CS: ChatStore>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
account_id: &str,
|
account_id: &AccountId,
|
||||||
welcome: &MlsMessageOut,
|
welcome: &MlsMessageOut,
|
||||||
) -> Result<(), ChatError>;
|
) -> Result<(), ChatError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MlsCtx {
|
pub struct GroupV1Convo<Ctx: MlsContext> {
|
||||||
type IDENT: IdentityProvider;
|
|
||||||
type INIT: MlsInitializer;
|
|
||||||
|
|
||||||
fn ident(&self) -> &Self::IDENT;
|
|
||||||
fn provider(&self) -> Ref<'_, LibcruxProvider>;
|
|
||||||
fn init(&self) -> &Self::INIT;
|
|
||||||
|
|
||||||
// Build an MLS Credential from the supplied IdentityProvider
|
|
||||||
fn get_credential(&self) -> CredentialWithKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GroupV1Convo<Ctx: MlsCtx> {
|
|
||||||
ctx: Rc<RefCell<Ctx>>,
|
ctx: Rc<RefCell<Ctx>>,
|
||||||
pub(crate) mls_group: MlsGroup, // TODO: (!) Fix Visibility
|
pub(crate) mls_group: MlsGroup, // TODO: (!) Fix Visibility
|
||||||
convo_id: String,
|
convo_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsCtx> std::fmt::Debug for GroupV1Convo<Ctx> {
|
impl<Ctx: MlsContext> std::fmt::Debug for GroupV1Convo<Ctx> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("GroupV1Convo")
|
f.debug_struct("GroupV1Convo")
|
||||||
.field("name", &self.ctx.borrow().ident().friendly_name())
|
.field("name", &self.ctx.borrow().ident().friendly_name())
|
||||||
@ -59,14 +61,13 @@ impl<Ctx: MlsCtx> std::fmt::Debug for GroupV1Convo<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsCtx> GroupV1Convo<Ctx> {
|
impl<Ctx: MlsContext> GroupV1Convo<Ctx> {
|
||||||
pub fn new<DS: DeliveryService>(ctx: Ctx, ds: &mut DS) -> Self {
|
pub fn new<DS: DeliveryService>(ctx: Rc<RefCell<Ctx>>, ds: &mut DS) -> Self {
|
||||||
let config = Self::mls_create_config();
|
let config = Self::mls_create_config();
|
||||||
let ctx = Rc::new(RefCell::new(ctx));
|
|
||||||
let mls_group = {
|
let mls_group = {
|
||||||
let ctx_ref = ctx.borrow();
|
let ctx_ref = ctx.borrow();
|
||||||
MlsGroup::new(
|
MlsGroup::new(
|
||||||
&*ctx_ref.provider(),
|
ctx_ref.provider(),
|
||||||
ctx_ref.ident(),
|
ctx_ref.ident(),
|
||||||
&config,
|
&config,
|
||||||
ctx_ref.get_credential(),
|
ctx_ref.get_credential(),
|
||||||
@ -99,11 +100,11 @@ impl<Ctx: MlsCtx> GroupV1Convo<Ctx> {
|
|||||||
let ctx_borrow = ctx.borrow();
|
let ctx_borrow = ctx.borrow();
|
||||||
let provider = ctx_borrow.provider();
|
let provider = ctx_borrow.provider();
|
||||||
|
|
||||||
StagedWelcome::build_from_welcome(&*provider, &Self::mls_join_config(), welcome)
|
StagedWelcome::build_from_welcome(provider, &Self::mls_join_config(), welcome)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_group(&*provider)
|
.into_group(provider)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -147,9 +148,9 @@ impl<Ctx: MlsCtx> GroupV1Convo<Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe<DS: DeliveryService>(ds: &mut DS, convo_id: &str) -> Result<(), ChatError> {
|
fn subscribe<DS: DeliveryService>(ds: &mut DS, convo_id: &str) -> Result<(), ChatError> {
|
||||||
ds.subscribe(Self::delivery_address_from_id(&convo_id))
|
ds.subscribe(&Self::delivery_address_from_id(&convo_id))
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
ds.subscribe(Self::ctrl_delivery_address_from_id(&convo_id))
|
ds.subscribe(&Self::ctrl_delivery_address_from_id(&convo_id))
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -190,14 +191,14 @@ impl<Ctx: MlsCtx> GroupV1Convo<Ctx> {
|
|||||||
Self::ctrl_delivery_address_from_id(&self.convo_id)
|
Self::ctrl_delivery_address_from_id(&self.convo_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_package_for_account<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
fn key_package_for_account<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
ident: &str,
|
ident: &AccountId,
|
||||||
) -> Result<KeyPackage, ChatError> {
|
) -> Result<KeyPackage, ChatError> {
|
||||||
let retrieved_bytes = ctx
|
let retrieved_bytes = ctx
|
||||||
.contact_registry()
|
.contact_registry()
|
||||||
.retreive(ident)
|
.retrieve(ident)
|
||||||
.map_err(|e| ChatError::Generic(e.to_string()))?;
|
.map_err(|e| ChatError::Generic(e.to_string()))?;
|
||||||
|
|
||||||
// dbg!(ctx.contact_registry());
|
// dbg!(ctx.contact_registry());
|
||||||
@ -214,13 +215,13 @@ impl<Ctx: MlsCtx> GroupV1Convo<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsCtx> Id for GroupV1Convo<Ctx> {
|
impl<Ctx: MlsContext> Id for GroupV1Convo<Ctx> {
|
||||||
fn id(&self) -> ConversationId<'_> {
|
fn id(&self) -> ConversationId<'_> {
|
||||||
&self.convo_id
|
&self.convo_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsCtx> Convo for GroupV1Convo<Ctx> {
|
impl<Ctx: MlsContext> Convo for GroupV1Convo<Ctx> {
|
||||||
fn send_message(
|
fn send_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
content: &[u8],
|
content: &[u8],
|
||||||
@ -229,7 +230,7 @@ impl<Ctx: MlsCtx> Convo for GroupV1Convo<Ctx> {
|
|||||||
let provider = ctx_ref.provider();
|
let provider = ctx_ref.provider();
|
||||||
let mls_message_out = self
|
let mls_message_out = self
|
||||||
.mls_group
|
.mls_group
|
||||||
.create_message(&*provider, ctx_ref.ident(), content)
|
.create_message(provider, ctx_ref.ident(), content)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let a = AddressedEncryptedPayload {
|
let a = AddressedEncryptedPayload {
|
||||||
@ -281,7 +282,7 @@ impl<Ctx: MlsCtx> Convo for GroupV1Convo<Ctx> {
|
|||||||
|
|
||||||
let processed = self
|
let processed = self
|
||||||
.mls_group
|
.mls_group
|
||||||
.process_message(&*provider, protocol_message)
|
.process_message(provider, protocol_message)
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
|
|
||||||
match processed.into_content() {
|
match processed.into_content() {
|
||||||
@ -292,7 +293,7 @@ impl<Ctx: MlsCtx> Convo for GroupV1Convo<Ctx> {
|
|||||||
})),
|
})),
|
||||||
ProcessedMessageContent::StagedCommitMessage(commit) => {
|
ProcessedMessageContent::StagedCommitMessage(commit) => {
|
||||||
self.mls_group
|
self.mls_group
|
||||||
.merge_staged_commit(&*provider, *commit)
|
.merge_staged_commit(provider, *commit)
|
||||||
.map_err(ChatError::generic)?;
|
.map_err(ChatError::generic)?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -313,13 +314,13 @@ impl<Ctx: MlsCtx> Convo for GroupV1Convo<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: MlsCtx, DS: DeliveryService, RS: RegistrationService, CS: ChatStore>
|
impl<Ctx: MlsContext, DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>
|
||||||
GroupConvo<DS, RS, CS> for GroupV1Convo<Ctx>
|
GroupConvo<DS, RS, CS> for GroupV1Convo<Ctx>
|
||||||
{
|
{
|
||||||
fn add_member(
|
fn add_member(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
members: &[&str],
|
members: &[&AccountId],
|
||||||
) -> Result<(), ChatError> {
|
) -> Result<(), ChatError> {
|
||||||
// add_members returns:
|
// add_members returns:
|
||||||
// commit — the Commit message Alice broadcasts to all members
|
// commit — the Commit message Alice broadcasts to all members
|
||||||
@ -345,16 +346,14 @@ impl<Ctx: MlsCtx, DS: DeliveryService, RS: RegistrationService, CS: ChatStore>
|
|||||||
|
|
||||||
let (commit, welcome, _group_info) = self
|
let (commit, welcome, _group_info) = self
|
||||||
.mls_group
|
.mls_group
|
||||||
.add_members(&*provider, ctx_ref.ident(), keypkgs.iter().as_slice())
|
.add_members(provider, ctx_ref.ident(), keypkgs.iter().as_slice())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.mls_group.merge_pending_commit(&*provider).unwrap();
|
self.mls_group.merge_pending_commit(provider).unwrap();
|
||||||
|
|
||||||
// TODO: (P3) Evaluate privacy/performance implications of an aggregated Welcome for multiple users
|
// TODO: (P3) Evaluate privacy/performance implications of an aggregated Welcome for multiple users
|
||||||
for account_id in members {
|
for account_id in members {
|
||||||
ctx_ref
|
ctx_ref.invite_user(ctx, account_id, &welcome)?;
|
||||||
.init()
|
|
||||||
.invite_to_group_v1(ctx, account_id, &welcome)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let encrypted_payload = EncryptedPayload {
|
let encrypted_payload = EncryptedPayload {
|
||||||
|
|||||||
@ -6,14 +6,15 @@ use std::{
|
|||||||
use storage::ChatStore;
|
use storage::ChatStore;
|
||||||
|
|
||||||
use crate::{DeliveryService, RegistrationService};
|
use crate::{DeliveryService, RegistrationService};
|
||||||
|
use crate::service_traits::KeyPackageProvider;
|
||||||
|
|
||||||
pub struct ClientCtx<DS: DeliveryService, RS: RegistrationService, CS: ChatStore> {
|
pub struct ClientCtx<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore> {
|
||||||
ds: DS,
|
ds: DS,
|
||||||
contact_registry: RS,
|
contact_registry: RS,
|
||||||
convo_store: Rc<RefCell<CS>>, // TODO: (P2) Remove Rc/Refcell
|
convo_store: Rc<RefCell<CS>>, // TODO: (P2) Remove Rc/Refcell
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, DS: DeliveryService, RS: RegistrationService, CS: ChatStore> ClientCtx<DS, RS, CS> {
|
impl<'a, DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore> ClientCtx<DS, RS, CS> {
|
||||||
pub fn new(ds: DS, contact_registry: RS, convo_store: Rc<RefCell<CS>>) -> Self {
|
pub fn new(ds: DS, contact_registry: RS, convo_store: Rc<RefCell<CS>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ds,
|
ds,
|
||||||
@ -26,11 +27,17 @@ impl<'a, DS: DeliveryService, RS: RegistrationService, CS: ChatStore> ClientCtx<
|
|||||||
&mut self.ds
|
&mut self.ds
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contact_registry(&'a mut self) -> &'a mut RS {
|
pub fn contact_registry(&'a self) -> &'a RS {
|
||||||
&mut self.contact_registry
|
&self.contact_registry
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store(&'a self) -> RefMut<'a, CS> {
|
pub fn store(&'a self) -> RefMut<'a, CS> {
|
||||||
self.convo_store.borrow_mut()
|
self.convo_store.borrow_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, DS: DeliveryService, RS: RegistrationService, CS: ChatStore> ClientCtx<DS, RS, CS> {
|
||||||
|
pub fn contact_registry_mut(&'a mut self) -> &'a mut RS {
|
||||||
|
&mut self.contact_registry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
use std::{fmt::Debug, fmt::Display};
|
|
||||||
|
|
||||||
use crate::types::AddressedEnvelope;
|
|
||||||
|
|
||||||
pub trait DeliveryService {
|
|
||||||
type Error: Display;
|
|
||||||
fn publish(&mut self, envelope: AddressedEnvelope) -> Result<(), Self::Error>;
|
|
||||||
fn subscribe(&mut self, delivery_address: String) -> Result<(), Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RegistrationService: Debug {
|
|
||||||
type Error: Display;
|
|
||||||
fn register(&mut self, identity: String, key_bundle: Vec<u8>) -> Result<(), Self::Error>;
|
|
||||||
fn retreive(&self, identity: &str) -> Result<Option<Vec<u8>>, Self::Error>;
|
|
||||||
}
|
|
||||||
@ -1,131 +1,108 @@
|
|||||||
use std::cell::{Ref, RefCell};
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use chat_proto::logoschat::envelope::EnvelopeV1;
|
use chat_proto::logoschat::envelope::EnvelopeV1;
|
||||||
use crypto::Ed25519SigningKey;
|
|
||||||
use crypto::Ed25519VerifyingKey;
|
|
||||||
use openmls::prelude::tls_codec::Serialize;
|
use openmls::prelude::tls_codec::Serialize;
|
||||||
use openmls::prelude::*;
|
use openmls::prelude::*;
|
||||||
use openmls_libcrux_crypto::Provider as LibcruxProvider;
|
use openmls_libcrux_crypto::Provider as LibcruxProvider;
|
||||||
use openmls_traits::signatures::Signer;
|
|
||||||
use prost::{Message, Oneof};
|
use prost::{Message, Oneof};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use storage::ChatStore;
|
use storage::ChatStore;
|
||||||
use storage::ConversationMeta;
|
use storage::ConversationMeta;
|
||||||
|
|
||||||
use crate::AddressedEnvelope;
|
use crate::AddressedEnvelope;
|
||||||
use crate::ChatError;
|
use crate::ChatError;
|
||||||
use crate::DeliveryService;
|
use crate::DeliveryService;
|
||||||
use crate::RegistrationService;
|
use crate::account::LogosAccount;
|
||||||
use crate::conversation::GroupConvo;
|
use crate::conversation::GroupConvo;
|
||||||
use crate::conversation::group_v1::{MlsCtx, MlsInitializer};
|
use crate::conversation::group_v1::MlsContext;
|
||||||
use crate::conversation::{GroupV1Convo, IdentityProvider};
|
use crate::conversation::{GroupV1Convo, IdentityProvider};
|
||||||
use crate::ctx::ClientCtx;
|
use crate::ctx::ClientCtx;
|
||||||
|
use crate::types::AccountId;
|
||||||
use crate::utils::{blake2b_hex, hash_size};
|
use crate::utils::{blake2b_hex, hash_size};
|
||||||
|
use crate::RegistrationService;
|
||||||
static ACCOUNT_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
use crate::service_traits::KeyPackageProvider;
|
||||||
const ACCOUNT_NAMES: &[&str] = &["Saro", "Raya", "Pax"];
|
pub struct PqMlsContext {
|
||||||
#[derive(Clone)]
|
ident_provider: LogosAccount,
|
||||||
pub struct LogosAccount {
|
provider: LibcruxProvider,
|
||||||
id: String,
|
|
||||||
signing_key: Ed25519SigningKey,
|
|
||||||
// x25519_key: crypto::PrivateKey,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogosAccount {
|
impl MlsContext for PqMlsContext {
|
||||||
pub fn new() -> Self {
|
|
||||||
let idx = ACCOUNT_COUNTER.fetch_add(1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
let id = if idx < ACCOUNT_NAMES.len() {
|
|
||||||
ACCOUNT_NAMES[idx % ACCOUNT_NAMES.len()].to_string()
|
|
||||||
} else {
|
|
||||||
use rand_core::{OsRng, RngCore};
|
|
||||||
const CHARSET: &[u8] =
|
|
||||||
b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
||||||
let i: String = (0..8)
|
|
||||||
.map(|_| {
|
|
||||||
let idx = (OsRng.next_u32() as usize) % CHARSET.len();
|
|
||||||
CHARSET[idx] as char
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
i
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
signing_key: Ed25519SigningKey::generate(),
|
|
||||||
// x25519_key: crypto::PrivateKey::random(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Signer for LogosAccount {
|
|
||||||
fn sign(&self, payload: &[u8]) -> Result<Vec<u8>, openmls_traits::signatures::SignerError> {
|
|
||||||
Ok(self.signing_key.sign(payload).as_ref().to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature_scheme(&self) -> SignatureScheme {
|
|
||||||
SignatureScheme::ED25519
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IdentityProvider for LogosAccount {
|
|
||||||
fn friendly_name(&self) -> String {
|
|
||||||
self.id.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn public_key(&self) -> Ed25519VerifyingKey {
|
|
||||||
self.signing_key.verifying_key()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct MlsContext<Init: MlsInitializer> {
|
|
||||||
pub ident_provider: LogosAccount,
|
|
||||||
pub initializer: Init,
|
|
||||||
provider: Rc<RefCell<LibcruxProvider>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Init: MlsInitializer + Clone> MlsCtx for MlsContext<Init> {
|
|
||||||
type IDENT = LogosAccount;
|
type IDENT = LogosAccount;
|
||||||
type INIT = Init;
|
|
||||||
|
|
||||||
fn ident(&self) -> &LogosAccount {
|
fn ident(&self) -> &LogosAccount {
|
||||||
&self.ident_provider
|
&self.ident_provider
|
||||||
}
|
}
|
||||||
|
|
||||||
fn provider(&self) -> Ref<'_, LibcruxProvider> {
|
fn provider(&self) -> &LibcruxProvider {
|
||||||
self.provider.borrow()
|
&self.provider
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self) -> &Init {
|
fn invite_user<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
||||||
&self.initializer
|
&self,
|
||||||
}
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
|
account_id: &AccountId,
|
||||||
|
welcome: &MlsMessageOut,
|
||||||
|
) -> Result<(), ChatError> {
|
||||||
|
let invite = GroupV1HeavyInvite {
|
||||||
|
welcome_bytes: welcome.to_bytes()?,
|
||||||
|
};
|
||||||
|
|
||||||
// Build an MLS Credential from the supplied IdentityProvider
|
let frame = InboxV2Frame {
|
||||||
fn get_credential(&self) -> CredentialWithKey {
|
payload: Some(InviteType::GroupV1(invite)),
|
||||||
CredentialWithKey {
|
};
|
||||||
credential: BasicCredential::new(self.ident_provider.friendly_name().into()).into(),
|
|
||||||
signature_key: self.ident_provider.public_key().as_ref().into(),
|
let envelope = EnvelopeV1 {
|
||||||
}
|
conversation_hint: ProtocolParams::conversation_id_for_account_id(&account_id),
|
||||||
|
salt: 0,
|
||||||
|
payload: frame.encode_to_vec().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let outbound_msg = AddressedEnvelope {
|
||||||
|
delivery_address: ProtocolParams::delivery_address_for_account_id(&account_id),
|
||||||
|
data: envelope.encode_to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.ds().publish(outbound_msg).map_err(ChatError::generic)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
struct InboxProtocolParams {}
|
||||||
|
|
||||||
|
impl InboxProtocolParams {
|
||||||
|
fn delivery_address_for_account_id(account_id: &AccountId) -> String {
|
||||||
|
blake2b_hex::<hash_size::AccountId>(&["InboxV2|", "delivery_address|", account_id.as_str()])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn conversation_id_for_account_id(account_id: &AccountId) -> String {
|
||||||
|
blake2b_hex::<hash_size::Testing>(&["InboxV2|", "conversation_id|", account_id.as_str()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProtocolParams = InboxProtocolParams;
|
||||||
|
|
||||||
pub struct InboxV2 {
|
pub struct InboxV2 {
|
||||||
pub account: LogosAccount, // TODO: (!) don't expose account
|
account_id: AccountId,
|
||||||
mls_provider: Rc<RefCell<LibcruxProvider>>,
|
ctx: Rc<RefCell<PqMlsContext>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InboxV2 {
|
impl<'a> InboxV2 {
|
||||||
pub fn new() -> Self {
|
pub fn new_with_account(account: LogosAccount) -> Self {
|
||||||
let account = LogosAccount::new();
|
let account_id = account.account_id().clone();
|
||||||
let mls_provider = Rc::new(RefCell::new(LibcruxProvider::new().unwrap()));
|
let provider = LibcruxProvider::new().unwrap();
|
||||||
Self {
|
Self {
|
||||||
account,
|
account_id,
|
||||||
mls_provider,
|
ctx: Rc::new(RefCell::new(PqMlsContext {
|
||||||
|
ident_provider: account,
|
||||||
|
provider,
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn account_id(&self) -> &AccountId {
|
||||||
|
&self.account_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
pub fn register<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
@ -134,29 +111,29 @@ impl<'a> InboxV2 {
|
|||||||
|
|
||||||
let bytes = keypackage.tls_serialize_detached()?;
|
let bytes = keypackage.tls_serialize_detached()?;
|
||||||
|
|
||||||
ctx.contact_registry()
|
ctx.contact_registry_mut()
|
||||||
.register(self.account.friendly_name(), bytes)
|
.register(&self.ctx.borrow().ident_provider.friendly_name(), bytes)
|
||||||
.map_err(ChatError::generic)?; //TODO: (P1) create an address scheme instead of using names
|
.map_err(ChatError::generic)?; //TODO: (P1) create an address scheme instead of using names
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delivery_address(&self) -> String {
|
pub fn delivery_address(&self) -> String {
|
||||||
Self::delivery_address_for_account_id(&self.account.id)
|
ProtocolParams::delivery_address_for_account_id(&self.account_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> String {
|
pub fn id(&self) -> String {
|
||||||
Self::conversation_id_for_account_id(&self.account.id)
|
ProtocolParams::conversation_id_for_account_id(&self.account_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_group_v1<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
pub fn create_group_v1<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
) -> Result<GroupV1Convo<MlsContext<InboxV2>>, ChatError> {
|
) -> Result<GroupV1Convo<PqMlsContext>, ChatError> {
|
||||||
let convo = GroupV1Convo::new(self.assemble_ctx(), ctx.ds());
|
let convo = GroupV1Convo::new(self.assemble_ctx(), ctx.ds());
|
||||||
Ok(convo)
|
Ok(convo)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_frame<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
pub fn handle_frame<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
payload_bytes: &[u8],
|
payload_bytes: &[u8],
|
||||||
@ -174,15 +151,11 @@ impl<'a> InboxV2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assemble_ctx(&self) -> MlsContext<InboxV2> {
|
fn assemble_ctx(&self) -> Rc<RefCell<PqMlsContext>> {
|
||||||
MlsContext {
|
self.ctx.clone()
|
||||||
ident_provider: self.account.clone(),
|
|
||||||
initializer: self.clone(),
|
|
||||||
provider: self.mls_provider.clone(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn persist_convo<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
fn persist_convo<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &'a ClientCtx<DS, RS, CS>,
|
ctx: &'a ClientCtx<DS, RS, CS>,
|
||||||
convo: impl GroupConvo<DS, RS, CS>,
|
convo: impl GroupConvo<DS, RS, CS>,
|
||||||
@ -199,7 +172,7 @@ impl<'a> InboxV2 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_heavy_invite<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
fn handle_heavy_invite<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
invite: GroupV1HeavyInvite,
|
invite: GroupV1HeavyInvite,
|
||||||
@ -213,15 +186,12 @@ impl<'a> InboxV2 {
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mls_ctx = Rc::new(RefCell::new(self.assemble_ctx()));
|
let convo = GroupV1Convo::new_from_welcome(self.assemble_ctx(), ctx.ds(), welcome);
|
||||||
|
|
||||||
let convo = GroupV1Convo::new_from_welcome(mls_ctx, ctx.ds(), welcome);
|
|
||||||
self.persist_convo(ctx, convo)
|
self.persist_convo(ctx, convo)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_keypackage(&self) -> Result<KeyPackage, ChatError> {
|
fn create_keypackage(&self) -> Result<KeyPackage, ChatError> {
|
||||||
let mls_ctx = self.assemble_ctx();
|
let ctx_borrow = self.ctx.borrow();
|
||||||
|
|
||||||
let capabilities = Capabilities::builder()
|
let capabilities = Capabilities::builder()
|
||||||
.ciphersuites(vec![
|
.ciphersuites(vec![
|
||||||
Ciphersuite::MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519,
|
Ciphersuite::MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519,
|
||||||
@ -232,70 +202,28 @@ impl<'a> InboxV2 {
|
|||||||
.leaf_node_capabilities(capabilities)
|
.leaf_node_capabilities(capabilities)
|
||||||
.build(
|
.build(
|
||||||
Ciphersuite::MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519,
|
Ciphersuite::MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519,
|
||||||
&*mls_ctx.provider(),
|
ctx_borrow.provider(),
|
||||||
&self.account,
|
ctx_borrow.ident(),
|
||||||
mls_ctx.get_credential(),
|
ctx_borrow.get_credential(),
|
||||||
)
|
)
|
||||||
.expect("Failed to build KeyPackage");
|
.expect("Failed to build KeyPackage");
|
||||||
|
|
||||||
Ok(a.key_package().clone())
|
Ok(a.key_package().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delivery_address_for_account_id(account_id: &str) -> String {
|
pub fn load_mls_convo<DS: DeliveryService, RS: KeyPackageProvider, CS: ChatStore>(
|
||||||
blake2b_hex::<hash_size::AccountId>(&["InboxV2|", "delivery_address|", account_id])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn conversation_id_for_account_id(account_id: &str) -> String {
|
|
||||||
blake2b_hex::<hash_size::Testing>(&["InboxV2|", "conversation_id|", account_id])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_mls_convo<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
ctx: &mut ClientCtx<DS, RS, CS>,
|
||||||
convo_id: String,
|
convo_id: String,
|
||||||
) -> Result<GroupV1Convo<MlsContext<InboxV2>>, ChatError> {
|
) -> Result<GroupV1Convo<PqMlsContext>, ChatError> {
|
||||||
let mls_ctx = self.assemble_ctx();
|
|
||||||
|
|
||||||
let group_id_bytes = hex::decode(&convo_id).map_err(ChatError::generic)?;
|
let group_id_bytes = hex::decode(&convo_id).map_err(ChatError::generic)?;
|
||||||
let group_id = GroupId::from_slice(&group_id_bytes);
|
let group_id = GroupId::from_slice(&group_id_bytes);
|
||||||
let convo =
|
let convo = GroupV1Convo::load(self.assemble_ctx(), ctx.ds(), convo_id, group_id)?;
|
||||||
GroupV1Convo::load(Rc::new(RefCell::new(mls_ctx)), ctx.ds(), convo_id, group_id)?;
|
|
||||||
|
|
||||||
Ok(convo)
|
Ok(convo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MlsInitializer for InboxV2 {
|
|
||||||
fn invite_to_group_v1<DS: DeliveryService, RS: RegistrationService, CS: ChatStore>(
|
|
||||||
&self,
|
|
||||||
ctx: &mut ClientCtx<DS, RS, CS>,
|
|
||||||
account_id: &str,
|
|
||||||
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: Self::conversation_id_for_account_id(account_id),
|
|
||||||
salt: 0,
|
|
||||||
payload: frame.encode_to_vec().into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let outbound_msg = AddressedEnvelope {
|
|
||||||
delivery_address: Self::delivery_address_for_account_id(account_id),
|
|
||||||
data: envelope.encode_to_vec(),
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.ds().publish(outbound_msg).map_err(ChatError::generic)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Message)]
|
#[derive(Clone, PartialEq, Message)]
|
||||||
pub struct InboxV2Frame {
|
pub struct InboxV2Frame {
|
||||||
#[prost(oneof = "InviteType", tags = "1")]
|
#[prost(oneof = "InviteType", tags = "1")]
|
||||||
|
|||||||
@ -4,7 +4,7 @@ mod conversation;
|
|||||||
mod crypto;
|
mod crypto;
|
||||||
mod ctx;
|
mod ctx;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod external_traits;
|
mod service_traits;
|
||||||
mod inbox;
|
mod inbox;
|
||||||
mod inbox_v2;
|
mod inbox_v2;
|
||||||
mod proto;
|
mod proto;
|
||||||
@ -18,7 +18,8 @@ mod test_utils;
|
|||||||
pub use context::{Context, ConversationId, ConversationIdOwned, Introduction};
|
pub use context::{Context, ConversationId, ConversationIdOwned, Introduction};
|
||||||
pub use conversation::GroupConvo;
|
pub use conversation::GroupConvo;
|
||||||
pub use errors::ChatError;
|
pub use errors::ChatError;
|
||||||
pub use external_traits::{DeliveryService, RegistrationService};
|
pub use service_traits::{DeliveryService, RegistrationService};
|
||||||
pub use sqlite::ChatStorage;
|
pub use sqlite::ChatStorage;
|
||||||
pub use sqlite::StorageConfig;
|
pub use sqlite::StorageConfig;
|
||||||
pub use types::{AddressedEnvelope, ContentData};
|
pub use types::{AccountId, AddressedEnvelope, ContentData};
|
||||||
|
pub use utils::hex_trunc;
|
||||||
|
|||||||
38
core/conversations/src/service_traits.rs
Normal file
38
core/conversations/src/service_traits.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use std::{fmt::Debug, fmt::Display};
|
||||||
|
|
||||||
|
use crate::types::{AccountId, AddressedEnvelope};
|
||||||
|
|
||||||
|
/// A Delivery service is responsible for payload transport.
|
||||||
|
/// This interface allows Conversations to send payloads on the wire as well as
|
||||||
|
/// register interest in delivery_addresses. Client implementations are responsible
|
||||||
|
/// for providing the inbound payloads to Context::handle_payload.
|
||||||
|
pub trait DeliveryService: Debug {
|
||||||
|
type Error: Display;
|
||||||
|
fn publish(&mut self, envelope: AddressedEnvelope) -> Result<(), Self::Error>;
|
||||||
|
fn subscribe(&mut self, delivery_address: &str) -> Result<(), Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manages key bundle storage for MLS group creation/addition while contacts are
|
||||||
|
/// offline.
|
||||||
|
///
|
||||||
|
/// Implement this to provide a contact registry — ach participant publishes their key package
|
||||||
|
/// on registration; others fetch it to initiate a conversation.
|
||||||
|
pub trait RegistrationService: Debug {
|
||||||
|
type Error: Display;
|
||||||
|
fn register(&mut self, identity: &str, key_bundle: Vec<u8>) -> Result<(), Self::Error>;
|
||||||
|
fn retrieve(&self, identity: &AccountId) -> Result<Option<Vec<u8>>, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read-only view of a contact registry. Not part of the public API.
|
||||||
|
/// Satisfied automatically by any `RegistrationService` implementation.
|
||||||
|
pub trait KeyPackageProvider: Debug {
|
||||||
|
type Error: Display;
|
||||||
|
fn retrieve(&self, identity: &AccountId) -> Result<Option<Vec<u8>>, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: RegistrationService> KeyPackageProvider for T {
|
||||||
|
type Error = T::Error;
|
||||||
|
fn retrieve(&self, identity: &AccountId) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||||
|
RegistrationService::retrieve(self, identity)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ use storage::{ConversationMeta, ConversationStore, IdentityStore};
|
|||||||
use storage::{EphemeralKeyStore, RatchetStore};
|
use storage::{EphemeralKeyStore, RatchetStore};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AddressedEnvelope, DeliveryService, RegistrationService,
|
AccountId, AddressedEnvelope, DeliveryService, RegistrationService, KeyPackageProvider,
|
||||||
utils::{blake2b_hex, hash_size::Testing},
|
utils::{blake2b_hex, hash_size::Testing},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -109,17 +109,17 @@ impl DeliveryService for LocalBroadcaster {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe(&mut self, delivery_address: String) -> Result<(), Self::Error> {
|
fn subscribe(&mut self, delivery_address: &str) -> Result<(), Self::Error> {
|
||||||
// Strict temporal ordering of subscriptions is not enforced.
|
// Strict temporal ordering of subscriptions is not enforced.
|
||||||
// Subscruptions are evaluated on polling, not when the message is published
|
// Subscruptions are evaluated on polling, not when the message is published
|
||||||
self.subscriptions.insert(delivery_address);
|
self.subscriptions.insert(delivery_address.to_string());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Contact Registry used for Tests.
|
/// A Contact Registry used for Tests.
|
||||||
/// This implementation stores bundle bytes and then returns them when
|
/// This implementation stores bundle bytes and then returns them when
|
||||||
/// retreived
|
/// retrieved
|
||||||
///
|
///
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -159,16 +159,18 @@ impl Debug for EphemeralRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegistrationService for EphemeralRegistry {
|
impl KeyPackageProvider for EphemeralRegistry {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
fn register(&mut self, identity: String, key_bundle: Vec<u8>) -> Result<(), Self::Error> {
|
fn retrieve(&self, identity: &AccountId) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||||
self.registry.lock().unwrap().insert(identity, key_bundle);
|
Ok(self.registry.lock().unwrap().get(identity.as_str()).cloned())
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn retreive(&self, identity: &str) -> Result<Option<Vec<u8>>, Self::Error> {
|
impl RegistrationService for EphemeralRegistry {
|
||||||
Ok(self.registry.lock().unwrap().get(identity).cloned())
|
fn register(&mut self, identity: &str, key_bundle: Vec<u8>) -> Result<(), Self::Error> {
|
||||||
|
self.registry.lock().unwrap().insert(identity.to_string(), key_bundle);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::fmt;
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
use crate::proto::{self, Message};
|
use crate::proto::{self, Message};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
use components::{EphemeralRegistry, LocalBroadcaster, MemStore};
|
||||||
use libchat::{ChatStorage, ContentData, Context, ConversationId, GroupConvo};
|
use libchat::{ChatStorage, ContentData, Context, ConversationId, GroupConvo, hex_trunc};
|
||||||
|
|
||||||
type TestContext = Context<LocalBroadcaster, EphemeralRegistry, ChatStorage>;
|
type TestContext = Context<LocalBroadcaster, EphemeralRegistry, ChatStorage>;
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ impl DerefMut for Client {
|
|||||||
fn pretty_print(prefix: impl Into<String>) -> Box<dyn Fn(ContentData)> {
|
fn pretty_print(prefix: impl Into<String>) -> Box<dyn Fn(ContentData)> {
|
||||||
let prefix = prefix.into();
|
let prefix = prefix.into();
|
||||||
return Box::new(move |c: ContentData| {
|
return Box::new(move |c: ContentData| {
|
||||||
let cid = c.conversation_id.as_bytes();
|
let cid = hex_trunc(c.conversation_id.as_bytes());
|
||||||
let content = String::from_utf8(c.data).unwrap();
|
let content = String::from_utf8(c.data).unwrap();
|
||||||
println!("{} ({:?}) {}", prefix, cid, content)
|
println!("{} ({:?}) {}", prefix, cid, content)
|
||||||
});
|
});
|
||||||
@ -106,10 +106,8 @@ fn create_group() {
|
|||||||
const SARO: usize = 0;
|
const SARO: usize = 0;
|
||||||
const RAYA: usize = 1;
|
const RAYA: usize = 1;
|
||||||
|
|
||||||
let raya_id = clients[RAYA].account_id();
|
let raya_id = clients[RAYA].account_id().clone();
|
||||||
let s_convo = clients[SARO]
|
let s_convo = clients[SARO].create_group_convo(&[&raya_id]).unwrap();
|
||||||
.create_group_convo(&[raya_id.as_ref()])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let convo_id = s_convo.id();
|
let convo_id = s_convo.id();
|
||||||
|
|
||||||
@ -143,10 +141,10 @@ fn create_group() {
|
|||||||
clients.push(Client::init(pax_ctx, Some(pretty_print(" Pax"))));
|
clients.push(Client::init(pax_ctx, Some(pretty_print(" Pax"))));
|
||||||
const PAX: usize = 2;
|
const PAX: usize = 2;
|
||||||
|
|
||||||
let pax_id = clients[PAX].account_id();
|
let pax_id = clients[PAX].account_id().clone();
|
||||||
clients[SARO]
|
clients[SARO]
|
||||||
.convo(convo_id)
|
.convo(convo_id)
|
||||||
.add_member(&mut clients[SARO].client_ctx(), &[pax_id.as_ref()])
|
.add_member(&mut clients[SARO].client_ctx(), &[&pax_id])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// clients[SARO].process_messages();
|
// clients[SARO].process_messages();
|
||||||
|
|||||||
@ -4,11 +4,11 @@ use std::{
|
|||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use libchat::RegistrationService;
|
use libchat::{AccountId, RegistrationService};
|
||||||
|
|
||||||
/// A Contact Registry used for Tests.
|
/// A Contact Registry used for Tests.
|
||||||
/// This implementation stores bundle bytes and then returns them when
|
/// This implementation stores bundle bytes and then returns them when
|
||||||
/// retreived
|
/// retrieved
|
||||||
///
|
///
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -51,12 +51,17 @@ impl Debug for EphemeralRegistry {
|
|||||||
impl RegistrationService for EphemeralRegistry {
|
impl RegistrationService for EphemeralRegistry {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
fn register(&mut self, identity: String, key_bundle: Vec<u8>) -> Result<(), Self::Error> {
|
fn register(&mut self, identity: &str, key_bundle: Vec<u8>) -> Result<(), Self::Error> {
|
||||||
self.registry.lock().unwrap().insert(identity, key_bundle);
|
self.registry.lock().unwrap().insert(identity.to_string(), key_bundle);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn retreive(&self, identity: &str) -> Result<Option<Vec<u8>>, Self::Error> {
|
fn retrieve(&self, identity: &AccountId) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||||
Ok(self.registry.lock().unwrap().get(identity).cloned())
|
Ok(self
|
||||||
|
.registry
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get(identity.as_str())
|
||||||
|
.cloned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,10 +107,10 @@ impl DeliveryService for LocalBroadcaster {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe(&mut self, delivery_address: String) -> Result<(), Self::Error> {
|
fn subscribe(&mut self, delivery_address: &str) -> Result<(), Self::Error> {
|
||||||
// Strict temporal ordering of subscriptions is not enforced.
|
// Strict temporal ordering of subscriptions is not enforced.
|
||||||
// Subscriptions are evaluated on polling, not when the message is published
|
// Subscriptions are evaluated on polling, not when the message is published
|
||||||
self.subscriptions.insert(delivery_address);
|
self.subscriptions.insert(delivery_address.to_string());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user