mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-02-10 00:43:09 +00:00
Add Introductions (#22)
This commit is contained in:
parent
fe23c39321
commit
d40e72be9b
@ -6,9 +6,11 @@ use crate::{
|
||||
conversation::{ConversationId, ConversationIdOwned, ConversationStore},
|
||||
identity::Identity,
|
||||
inbox::Inbox,
|
||||
proto,
|
||||
types::{ContentData, PayloadData},
|
||||
};
|
||||
|
||||
pub use crate::inbox::Introduction;
|
||||
// This is the main entry point to the conversations api.
|
||||
// Ctx manages lifetimes of objects to process and generate payloads.
|
||||
pub struct Context {
|
||||
@ -30,17 +32,16 @@ impl Context {
|
||||
|
||||
pub fn create_private_convo(
|
||||
&mut self,
|
||||
remote_bundle: &PrekeyBundle,
|
||||
remote_bundle: &Introduction,
|
||||
content: String,
|
||||
) -> ConversationIdOwned {
|
||||
let (convo, _payloads) = self
|
||||
) -> (ConversationIdOwned, Vec<proto::EncryptedPayload>) {
|
||||
let (convo, payloads) = self
|
||||
.inbox
|
||||
.invite_to_private_convo(remote_bundle, content)
|
||||
.unwrap_or_else(|_| todo!("Log/Surface Error"));
|
||||
|
||||
self.store.insert_convo(convo)
|
||||
|
||||
// TODO: Change return type to handle outbout packets.
|
||||
let convo_id = self.store.insert_convo(convo);
|
||||
(convo_id, payloads)
|
||||
}
|
||||
|
||||
pub fn send_content(&mut self, _convo_id: ConversationId, _content: &[u8]) -> Vec<PayloadData> {
|
||||
|
||||
@ -12,4 +12,8 @@ pub enum ChatError {
|
||||
BadBundleValue(String),
|
||||
#[error("handshake initiated with a unknown ephemeral key")]
|
||||
UnknownEphemeralKey(),
|
||||
#[error("expected a different key length")]
|
||||
InvalidKeyLength,
|
||||
#[error("bytes provided to {0} failed")]
|
||||
BadParsing(&'static str),
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
mod handshake;
|
||||
mod inbox;
|
||||
mod introduction;
|
||||
|
||||
pub use inbox::Inbox;
|
||||
pub use introduction::Introduction;
|
||||
|
||||
@ -8,6 +8,7 @@ use std::rc::Rc;
|
||||
|
||||
use crypto::{PrekeyBundle, SecretKey};
|
||||
|
||||
use crate::context::Introduction;
|
||||
use crate::conversation::{ChatError, ConversationId, Convo, ConvoFactory, Id, PrivateV1Convo};
|
||||
use crate::crypto::{Blake2b128, CopyBytes, Digest, PublicKey, StaticSecret};
|
||||
use crate::identity::Identity;
|
||||
@ -66,13 +67,21 @@ impl Inbox {
|
||||
|
||||
pub fn invite_to_private_convo(
|
||||
&self,
|
||||
remote_bundle: &PrekeyBundle,
|
||||
remote_bundle: &Introduction,
|
||||
initial_message: String,
|
||||
) -> Result<(PrivateV1Convo, Vec<proto::EncryptedPayload>), ChatError> {
|
||||
let mut rng = OsRng;
|
||||
|
||||
// TODO: Include signature in introduction bundle. Manaully fill for now
|
||||
let pkb = PrekeyBundle {
|
||||
identity_key: remote_bundle.installation_key,
|
||||
signed_prekey: remote_bundle.ephemeral_key,
|
||||
signature: [0u8; 64],
|
||||
onetime_prekey: None,
|
||||
};
|
||||
|
||||
let (seed_key, ephemeral_pub) =
|
||||
InboxHandshake::perform_as_initiator(&self.ident.secret(), remote_bundle, &mut rng);
|
||||
InboxHandshake::perform_as_initiator(&self.ident.secret(), &pkb, &mut rng);
|
||||
|
||||
let mut convo = PrivateV1Convo::new(seed_key);
|
||||
|
||||
@ -89,8 +98,8 @@ impl Inbox {
|
||||
let header = proto::InboxHeaderV1 {
|
||||
initiator_static: self.ident.public_key().copy_to_bytes(),
|
||||
initiator_ephemeral: ephemeral_pub.copy_to_bytes(),
|
||||
responder_static: remote_bundle.identity_key.copy_to_bytes(),
|
||||
responder_ephemeral: remote_bundle.signed_prekey.copy_to_bytes(),
|
||||
responder_static: remote_bundle.installation_key.copy_to_bytes(),
|
||||
responder_ephemeral: remote_bundle.ephemeral_key.copy_to_bytes(),
|
||||
};
|
||||
|
||||
let handshake = proto::InboxHandshakeV1 {
|
||||
@ -226,7 +235,7 @@ mod tests {
|
||||
|
||||
let bundle = raya_inbox.create_bundle();
|
||||
let (_, payloads) = saro_inbox
|
||||
.invite_to_private_convo(&bundle, "hello".into())
|
||||
.invite_to_private_convo(&bundle.into(), "hello".into())
|
||||
.unwrap();
|
||||
|
||||
let encrypted_payload = payloads
|
||||
|
||||
65
conversations/src/inbox/introduction.rs
Normal file
65
conversations/src/inbox/introduction.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use crypto::PrekeyBundle;
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use crate::errors::ChatError;
|
||||
|
||||
/// Supplies remote participants with the required keys to use Inbox protocol
|
||||
pub struct Introduction {
|
||||
pub installation_key: PublicKey,
|
||||
pub ephemeral_key: PublicKey,
|
||||
}
|
||||
|
||||
impl From<PrekeyBundle> for Introduction {
|
||||
fn from(value: PrekeyBundle) -> Self {
|
||||
Introduction {
|
||||
installation_key: value.identity_key,
|
||||
ephemeral_key: value.signed_prekey,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Introduction {
|
||||
fn into(self) -> Vec<u8> {
|
||||
// TODO: avoid copies, via writing directly to slice
|
||||
let link = format!(
|
||||
"Bundle:{}:{}",
|
||||
hex::encode(self.installation_key.as_bytes()),
|
||||
hex::encode(self.ephemeral_key.as_bytes()),
|
||||
);
|
||||
|
||||
link.into_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for Introduction {
|
||||
type Error = ChatError;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
let str_value =
|
||||
String::from_utf8(value).map_err(|_| ChatError::BadParsing("Introduction"))?;
|
||||
let parts: Vec<&str> = str_value.splitn(3, ':').collect();
|
||||
|
||||
if parts[0] != "Bundle" {
|
||||
return Err(ChatError::BadBundleValue(
|
||||
"not recognized as an introduction bundle".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let installation_bytes: [u8; 32] = hex::decode(parts[1])
|
||||
.map_err(|_| ChatError::BadParsing("installation_key"))?
|
||||
.try_into()
|
||||
.map_err(|_| ChatError::InvalidKeyLength)?;
|
||||
let installation_key = PublicKey::from(installation_bytes);
|
||||
|
||||
let ephemeral_bytes: [u8; 32] = hex::decode(parts[1])
|
||||
.map_err(|_| ChatError::BadParsing("ephemeral_key"))?
|
||||
.try_into()
|
||||
.map_err(|_| ChatError::InvalidKeyLength)?;
|
||||
let ephemeral_key = PublicKey::from(ephemeral_bytes);
|
||||
|
||||
Ok(Introduction {
|
||||
installation_key,
|
||||
ephemeral_key,
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user