-
Notifications
You must be signed in to change notification settings - Fork 1
Integrate DR into PrivateV1 #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,35 +3,92 @@ use chat_proto::logoschat::{ | |
| encryption::{Doubleratchet, EncryptedPayload, encrypted_payload::Encryption}, | ||
| }; | ||
| use crypto::SecretKey; | ||
| use double_ratchets::{Header, InstallationKeyPair, RatchetState}; | ||
| use prost::{Message, bytes::Bytes}; | ||
| use std::fmt::Debug; | ||
| use x25519_dalek::PublicKey; | ||
|
|
||
| use crate::{ | ||
| conversation::{ChatError, ConversationId, Convo, Id}, | ||
| errors::EncryptionError, | ||
| proto, | ||
| types::AddressedEncryptedPayload, | ||
| utils::timestamp_millis, | ||
| }; | ||
|
|
||
| #[derive(Debug)] | ||
| pub struct PrivateV1Convo {} | ||
| pub struct PrivateV1Convo { | ||
| dr_state: RatchetState, | ||
| } | ||
|
|
||
| impl PrivateV1Convo { | ||
| pub fn new(_seed_key: SecretKey) -> Self { | ||
| Self {} | ||
| pub fn new_initiator(seed_key: SecretKey, remote: PublicKey) -> Self { | ||
| // TODO: Danger - Fix double-ratchets types to Accept SecretKey | ||
| // perhaps update the DH to work with cryptocrate. | ||
| // init_sender doesn't take ownership of the key so a reference can be used. | ||
| let shared_secret: [u8; 32] = seed_key.as_bytes().to_vec().try_into().unwrap(); | ||
| Self { | ||
| dr_state: RatchetState::init_sender(shared_secret, remote), | ||
| } | ||
| } | ||
|
|
||
| fn encrypt(&self, frame: PrivateV1Frame) -> EncryptedPayload { | ||
| // TODO: Integrate DR | ||
| pub fn new_responder(seed_key: SecretKey, dh_self: InstallationKeyPair) -> Self { | ||
| Self { | ||
| // TODO: Danger - Fix double-ratchets types to Accept SecretKey | ||
| dr_state: RatchetState::init_receiver(seed_key.as_bytes().to_owned(), dh_self), | ||
| } | ||
| } | ||
|
Comment on lines
+34
to
+39
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
As InstallationKey is just a thin wrapper around StaticKey @kaichaosun would you be ok if I refactored double-ratchets to use a common keyType as the rest of the cryptography? It would be a StructWrapper around StaticKey which would have the effect:
We can add an AbstractType or Trait Generics if we really want it to be completely independent in the future.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be great! thanks @jazzz |
||
|
|
||
| fn encrypt(&mut self, frame: PrivateV1Frame) -> EncryptedPayload { | ||
| let encoded_bytes = frame.encode_to_vec(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the name can be shorter like
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you mean @kaichaosun ? I don't follow.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It just looks so strange to use |
||
| let (cipher_text, header) = self.dr_state.encrypt_message(&encoded_bytes); | ||
|
|
||
| EncryptedPayload { | ||
| encryption: Some(Encryption::Doubleratchet(Doubleratchet { | ||
| dh: Bytes::from(vec![]), | ||
| msg_num: 0, | ||
| prev_chain_len: 1, | ||
| ciphertext: Bytes::from(frame.encode_to_vec()), | ||
| dh: Bytes::from(Vec::from(header.dh_pub.to_bytes())), | ||
| msg_num: header.msg_num, | ||
| prev_chain_len: header.prev_chain_len, | ||
| ciphertext: Bytes::from(cipher_text), | ||
| aux: "".into(), | ||
| })), | ||
| } | ||
| } | ||
|
|
||
| fn decrypt(&mut self, payload: EncryptedPayload) -> Result<PrivateV1Frame, EncryptionError> { | ||
| // Validate and extract the encryption header or return errors | ||
| let dr_header = if let Some(enc) = payload.encryption { | ||
| if let proto::Encryption::Doubleratchet(dr) = enc { | ||
| dr | ||
| } else { | ||
| return Err(EncryptionError::Decryption( | ||
| "incorrect encryption type".into(), | ||
| )); | ||
| } | ||
| } else { | ||
| return Err(EncryptionError::Decryption("missing payload".into())); | ||
| }; | ||
|
|
||
| // Turn the bytes into a PublicKey | ||
| let byte_arr: [u8; 32] = dr_header | ||
| .dh | ||
| .to_vec() | ||
| .try_into() | ||
| .map_err(|_| EncryptionError::Decryption("invalid public key length".into()))?; | ||
| let dh_pub = PublicKey::from(byte_arr); | ||
|
|
||
| // Build the Header that DR impl expects | ||
| let header = Header { | ||
| dh_pub, | ||
| msg_num: dr_header.msg_num, | ||
| prev_chain_len: dr_header.prev_chain_len, | ||
| }; | ||
|
Comment on lines
+79
to
+83
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Orphan rule is making handling these DR types kind of annoying. I'll look at using Newtype and implementing the conversions there |
||
|
|
||
| // Decrypt into Frame | ||
| let content_bytes = self | ||
| .dr_state | ||
| .decrypt_message(&dr_header.ciphertext, header) | ||
| .map_err(|e| EncryptionError::Decryption(e.to_string()))?; | ||
| Ok(PrivateV1Frame::decode(content_bytes.as_slice()).unwrap()) | ||
| } | ||
| } | ||
|
|
||
| impl Id for PrivateV1Convo { | ||
|
|
@@ -66,3 +123,53 @@ impl Convo for PrivateV1Convo { | |
| self.id().into() | ||
| } | ||
| } | ||
|
|
||
| impl Debug for PrivateV1Convo { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| f.debug_struct("PrivateV1Convo") | ||
| .field("dr_state", &"******") | ||
| .finish() | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use x25519_dalek::StaticSecret; | ||
|
|
||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn test_encrypt_roundtrip() { | ||
| let saro = StaticSecret::random(); | ||
| let raya = StaticSecret::random(); | ||
|
|
||
| let pub_raya = PublicKey::from(&raya); | ||
|
|
||
| let seed_key = saro.diffie_hellman(&pub_raya); | ||
| let send_content_bytes = vec![0, 2, 4, 6, 8]; | ||
| let mut sr_convo = | ||
| PrivateV1Convo::new_initiator(SecretKey::from(seed_key.to_bytes()), pub_raya); | ||
|
|
||
| let installation_key_pair = InstallationKeyPair::from(raya); | ||
| let mut rs_convo = PrivateV1Convo::new_responder( | ||
| SecretKey::from(seed_key.to_bytes()), | ||
| installation_key_pair, | ||
| ); | ||
|
|
||
| let send_frame = PrivateV1Frame { | ||
| conversation_id: "_".into(), | ||
| sender: Bytes::new(), | ||
| timestamp: timestamp_millis(), | ||
| frame_type: Some(FrameType::Content(Bytes::from(send_content_bytes.clone()))), | ||
| }; | ||
| let payload = sr_convo.encrypt(send_frame.clone()); | ||
| let recv_frame = rs_convo.decrypt(payload).unwrap(); | ||
|
|
||
| assert!( | ||
| recv_frame == send_frame, | ||
| "{:?}. {:?}", | ||
| recv_frame, | ||
| send_content_bytes | ||
| ); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to make roles name consistent in different crates.