Implement Content::send_content (#31)

* Implement Content::send_content

* rename stamp fn
This commit is contained in:
Jazz Turner-Baggs 2026-01-29 23:36:18 +07:00 committed by GitHub
parent 10940321ff
commit 5a98258ff1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 129 additions and 42 deletions

View File

@ -112,7 +112,15 @@ pub fn send_content(
convo_handle: ConvoHandle,
content: c_slice::Ref<'_, u8>,
) -> PayloadResult {
let payloads = ctx.0.send_content(convo_handle, &content);
let payloads = match ctx.0.send_content(convo_handle, &content) {
Ok(p) => p,
Err(_) => {
return PayloadResult {
error_code: ErrorCode::UnknownError as i32,
payloads: safer_ffi::Vec::EMPTY,
};
}
};
let ffi_payloads: Vec<Payload> = payloads
.into_iter()

View File

@ -1,11 +1,11 @@
use std::{collections::HashMap, rc::Rc, sync::Arc};
use crate::{
conversation::{ConversationId, ConversationStore, Convo, Id},
conversation::{ConversationStore, Convo, Id},
errors::ChatError,
identity::Identity,
inbox::Inbox,
types::{ContentData, PayloadData},
types::{AddressedEnvelope, ContentData},
};
pub use crate::inbox::Introduction;
@ -53,22 +53,37 @@ impl Context {
&mut self,
remote_bundle: &Introduction,
content: String,
) -> (ConvoHandle, Vec<PayloadData>) {
) -> (ConvoHandle, Vec<AddressedEnvelope>) {
let (convo, payloads) = self
.inbox
.invite_to_private_convo(remote_bundle, content)
.unwrap_or_else(|_| todo!("Log/Surface Error"));
let payload_bytes = payloads
.into_iter()
.map(|p| p.to_envelope(convo.id().to_string()))
.collect();
let convo_handle = self.add_convo(convo);
(convo_handle, payloads)
(convo_handle, payload_bytes)
}
pub fn send_content(&mut self, convo_id: ConvoHandle, _content: &[u8]) -> Vec<PayloadData> {
// !TODO Replace Mock
vec![PayloadData {
delivery_address: format!("addr-for-{convo_id}"),
data: vec![40, 30, 20, 10],
}]
pub fn send_content(
&mut self,
convo_handle: ConvoHandle,
content: &[u8],
) -> Result<Vec<AddressedEnvelope>, ChatError> {
// Lookup convo from handle
let convo = self.get_convo_mut(convo_handle)?;
// Generate encrypted payloads
let payloads = convo.send_message(content)?;
// Attach conversation_ids to Envelopes
Ok(payloads
.into_iter()
.map(|p| p.to_envelope(convo.remote_id()))
.collect())
}
pub fn handle_payload(&mut self, _payload: &[u8]) -> Option<ContentData> {
@ -92,6 +107,19 @@ impl Context {
handle
}
// Returns a mutable reference to a Convo for a given ConvoHandle
fn get_convo_mut(&mut self, handle: ConvoHandle) -> Result<&mut dyn Convo, ChatError> {
let convo_id = self
.convo_handle_map
.get(&handle)
.ok_or_else(|| ChatError::NoConvo(handle))?
.clone();
self.store
.get_mut(&convo_id)
.ok_or_else(|| ChatError::NoConvo(handle))
}
}
#[cfg(test)]

View File

@ -3,7 +3,7 @@ use std::fmt::Debug;
use std::sync::Arc;
pub use crate::errors::ChatError;
use crate::types::ContentData;
use crate::types::{AddressedEncryptedPayload, ContentData};
pub type ConversationId<'a> = &'a str;
pub type ConversationIdOwned = Arc<str>;
@ -20,7 +20,10 @@ pub trait ConvoFactory: Id + Debug {
}
pub trait Convo: Id + Debug {
fn send_message(&mut self, content: &[u8]) -> Result<Vec<EncryptedPayload>, ChatError>;
fn send_message(&mut self, content: &[u8])
-> Result<Vec<AddressedEncryptedPayload>, ChatError>;
fn remote_id(&self) -> String;
}
pub struct ConversationStore {
@ -76,6 +79,5 @@ impl ConversationStore {
mod group_test;
mod privatev1;
use crate::proto::EncryptedPayload;
pub use group_test::GroupTestConvo;
pub use privatev1::PrivateV1Convo;

View File

@ -1,6 +1,7 @@
use chat_proto::logoschat::encryption::EncryptedPayload;
use crate::conversation::{ChatError, ConversationId, Convo, Id};
use crate::{
conversation::{ChatError, ConversationId, Convo, Id},
types::AddressedEncryptedPayload,
};
#[derive(Debug)]
pub struct GroupTestConvo {}
@ -19,7 +20,14 @@ impl Id for GroupTestConvo {
}
impl Convo for GroupTestConvo {
fn send_message(&mut self, _content: &[u8]) -> Result<Vec<EncryptedPayload>, ChatError> {
fn send_message(
&mut self,
_content: &[u8],
) -> Result<Vec<AddressedEncryptedPayload>, ChatError> {
Ok(vec![])
}
fn remote_id(&self) -> String {
self.id().to_string()
}
}

View File

@ -7,6 +7,7 @@ use prost::{Message, bytes::Bytes};
use crate::{
conversation::{ChatError, ConversationId, Convo, Id},
types::AddressedEncryptedPayload,
utils::timestamp_millis,
};
@ -41,7 +42,10 @@ impl Id for PrivateV1Convo {
}
impl Convo for PrivateV1Convo {
fn send_message(&mut self, content: &[u8]) -> Result<Vec<EncryptedPayload>, ChatError> {
fn send_message(
&mut self,
content: &[u8],
) -> Result<Vec<AddressedEncryptedPayload>, ChatError> {
let frame = PrivateV1Frame {
conversation_id: self.id().into(),
sender: "delete".into(),
@ -49,8 +53,16 @@ impl Convo for PrivateV1Convo {
frame_type: Some(FrameType::Content(content.to_vec().into())),
};
let ef = self.encrypt(frame);
let data = self.encrypt(frame);
Ok(vec![ef])
Ok(vec![AddressedEncryptedPayload {
delivery_address: "delivery_address".into(),
data,
}])
}
fn remote_id(&self) -> String {
//TODO: Implement as per spec
self.id().into()
}
}

View File

@ -18,4 +18,6 @@ pub enum ChatError {
InvalidKeyLength,
#[error("bytes provided to {0} failed")]
BadParsing(&'static str),
#[error("convo with handle: {0} was not found")]
NoConvo(u32),
}

View File

@ -1,4 +1,3 @@
use chat_proto::logoschat::inbox::InboxV1Frame;
use hex;
use prost::Message;
use prost::bytes::Bytes;
@ -13,8 +12,8 @@ use crate::conversation::{ChatError, ConversationId, Convo, ConvoFactory, Id, Pr
use crate::crypto::{Blake2b128, CopyBytes, Digest, PublicKey, StaticSecret};
use crate::identity::Identity;
use crate::inbox::handshake::InboxHandshake;
use crate::proto::{self};
use crate::types::{ContentData, PayloadData};
use crate::proto;
use crate::types::{AddressedEncryptedPayload, ContentData};
/// Compute the deterministic Delivery_address for an installation
fn delivery_address_for_installation(_: PublicKey) -> String {
@ -75,7 +74,7 @@ impl Inbox {
&self,
remote_bundle: &Introduction,
initial_message: String,
) -> Result<(PrivateV1Convo, Vec<PayloadData>), ChatError> {
) -> Result<(PrivateV1Convo, Vec<AddressedEncryptedPayload>), ChatError> {
let mut rng = OsRng;
// TODO: Include signature in introduction bundle. Manaully fill for now
@ -91,12 +90,12 @@ impl Inbox {
let mut convo = PrivateV1Convo::new(seed_key);
let mut initial_payloads = convo.send_message(initial_message.as_bytes())?;
let mut payloads = convo.send_message(initial_message.as_bytes())?;
// Wrap First payload in Invite
if let Some(first_message) = initial_payloads.get_mut(0) {
let old = first_message.clone();
let frame = Self::wrap_in_invite(old);
if let Some(first_message) = payloads.get_mut(0) {
// Take the the value of .data - it's being replaced at the end of this block
let frame = Self::wrap_in_invite(std::mem::take(&mut first_message.data));
// TODO: Encrypt frame
let ciphertext = frame.encode_to_vec();
@ -113,21 +112,16 @@ impl Inbox {
payload: Bytes::from_owner(ciphertext),
};
*first_message = proto::EncryptedPayload {
// Update the address field with the Inbox delivery_Address
first_message.delivery_address =
delivery_address_for_installation(remote_bundle.installation_key);
// Update the data field with new Payload
first_message.data = proto::EncryptedPayload {
encryption: Some(proto::Encryption::InboxHandshake(handshake)),
};
}
// Convert Encrypted Payloads to PayloadData
let payload_data = initial_payloads
.iter()
.map(|p| PayloadData {
delivery_address: delivery_address_for_installation(remote_bundle.installation_key),
data: p.encode_to_vec(),
})
.collect();
Ok((convo, payload_data))
Ok((convo, payloads))
}
fn wrap_in_invite(payload: proto::EncryptedPayload) -> proto::InboxV1Frame {
@ -174,7 +168,7 @@ impl Inbox {
);
// TODO: Decrypt Content
let frame = InboxV1Frame::decode(handshake.payload)?;
let frame = proto::InboxV1Frame::decode(handshake.payload)?;
Ok((seed_key, frame))
}

View File

@ -1,5 +1,9 @@
pub use chat_proto::logoschat::encryption::encrypted_payload::Encryption;
pub use chat_proto::logoschat::encryption::inbox_handshake_v1::InboxHeaderV1;
pub use chat_proto::logoschat::encryption::{EncryptedPayload, InboxHandshakeV1};
pub use chat_proto::logoschat::envelope::EnvelopeV1;
pub use chat_proto::logoschat::inbox::InboxV1Frame;
pub use chat_proto::logoschat::invite::InvitePrivateV1;
pub use prost::Message;
pub use prost::bytes::Bytes;

View File

@ -1,6 +1,10 @@
use crate::proto::{self, Message};
// FFI Type definitions
// This struct represents Outbound data.
// It wraps an encoded payload with a delivery address, so it can be handled by the delivery service.
pub struct PayloadData {
pub struct AddressedEnvelope {
pub delivery_address: String,
pub data: Vec<u8>,
}
@ -11,3 +15,28 @@ pub struct ContentData {
pub conversation_id: String,
pub data: Vec<u8>,
}
// Internal type Definitions
// Used by Conversations to attach addresses to outbound encrypted payloads
pub(crate) struct AddressedEncryptedPayload {
pub delivery_address: String,
pub data: proto::EncryptedPayload,
}
impl AddressedEncryptedPayload {
// Wrap in an envelope and prepare for transmission
pub fn to_envelope(self, convo_id: String) -> AddressedEnvelope {
let envelope = proto::EnvelopeV1 {
// TODO: conversation_id should be obscured
conversation_hint: convo_id,
salt: 0,
payload: proto::Bytes::copy_from_slice(self.data.encode_to_vec().as_slice()),
};
AddressedEnvelope {
delivery_address: self.delivery_address,
data: envelope.encode_to_vec(),
}
}
}