From 63ab79c6b258909fa7b2518432d1e1e75d16281f Mon Sep 17 00:00:00 2001 From: ennucore Date: Mon, 22 Nov 2021 14:25:49 +0300 Subject: [PATCH] MessageBuilder --- Cargo.lock | 17 ++++++++ Cargo.toml | 1 + src/crypto.rs | 4 ++ src/ironforce.rs | 5 +++ src/message.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++++- src/res.rs | 24 +++++++++-- 6 files changed, 150 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ed9fe3..30d9cb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,6 +210,12 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -229,6 +235,7 @@ dependencies = [ "rayon", "rsa", "serde", + "serde_cbor", "sha2", ] @@ -505,6 +512,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.130" diff --git a/Cargo.toml b/Cargo.toml index 00994e2..27c2cb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ rsa = { version = "0.5", features = ["serde"] } serde = { version = "1.0", features = ["derive", "alloc"], default-features = false } rayon = { version = "1.5.1", optional = true } core-error = "0.0.1-rc4" +serde_cbor = "0.11.2" [profile.dev.package.num-bigint-dig] opt-level = 3 diff --git a/src/crypto.rs b/src/crypto.rs index 91456ff..ad164ed 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -25,6 +25,10 @@ impl PublicKey { pub fn encrypt_data(&self, data: &[u8]) -> RsaRes> { self.key.encrypt(&mut OsRng {}, PaddingScheme::PKCS1v15Encrypt, data) } + + pub fn to_vec(&self) -> serde_cbor::Result> { + serde_cbor::to_vec(&self) + } } /// Key pair (public and secret) for a node, should be stored locally diff --git a/src/ironforce.rs b/src/ironforce.rs index bb482b9..342c9f4 100644 --- a/src/ironforce.rs +++ b/src/ironforce.rs @@ -37,6 +37,11 @@ impl IronForce { todo!() } + /// Send a multicast or broadcast message + fn send_to_all(&mut self, _message: Message) -> IFResult<()> { + todo!() + } + /// Send a message through tunnel fn send_through_tunnel(&mut self, _tunnel_id: u64, _message: Message, _direction: Option) -> IFResult<()> { todo!() diff --git a/src/message.rs b/src/message.rs index 8a5cc34..c29c003 100644 --- a/src/message.rs +++ b/src/message.rs @@ -57,11 +57,16 @@ pub enum MessageContent { Plain(Vec), /// Message content bytes encrypted for the recipient Encrypted(Vec), + /// There is no content + None, } +/// The struct for messages that are sent in the network #[derive(Serialize, Deserialize, Clone)] pub struct Message { /// Content of the message (not to be confused with the bytes that we are sending through interfaces) + /// + /// AKA useful payload pub content: MessageContent, /// The type of this message pub message_type: MessageType, @@ -95,7 +100,104 @@ impl Message { todo!() } - pub fn calculate_hash(_content: MessageContent, _message_type: MessageType, _sender: Option) -> Vec { + pub fn calculate_hash(_content: &MessageContent, _message_type: MessageType, _sender_or_encrypted_sender: Option>) -> Vec { todo!() } + + /// Encrypt hash of the message for the recipient + pub fn generate_recipient_verification(hash: Vec, recipient: PublicKey) -> rsa::errors::Result> { + recipient.encrypt_data(&hash) + } +} + + +/// Message builder to create a new message step-by-step, like `Message::build().message_type(...).sign(...)` +pub struct MessageBuilder { + content: MessageContent, + /// The type of the message to be built + message_type: Option, + /// Sender's keys + sender: Option, + /// Recipient's public key (if present, the content will be encrypted and recipient verification field will be set) + recipient: Option, + /// ID of the tunnel that is used + tunnel_id: u64, +} + +impl MessageBuilder { + /// Create a new `MessageBuilder` with default parameters + pub fn new() -> Self { + Self { + content: MessageContent::None, + message_type: None, + sender: None, + recipient: None, + tunnel_id: 0, + } + } + + pub fn content(mut self, cont: Vec) -> Self { + self.content = MessageContent::Plain(cont); + self + } + + /// Sign the message + pub fn sign(mut self, keys: &Keys) -> Self { + self.sender = Some(keys.clone()); + self + } + + /// Set message's recipient (and therefore set recipient verification and encrypt the content) + pub fn recipient(mut self, recipient: PublicKey) -> Self { + self.recipient = Some(recipient); + self + } + + /// Set tunnel id + pub fn tunnel(mut self, tunnel_id: u64) -> Self { + self.tunnel_id = tunnel_id; + self + } + + /// Set message's type + pub fn message_type(mut self, message_type: MessageType) -> Self { + self.message_type = Some(message_type); + self + } + + /// Get the resulting message + pub fn build(self) -> IFResult { + let salt = rand::random(); + let sender_encrypted = if let (Some(sender_keys), Some(recipient)) = (self.sender.as_ref(), self.recipient.as_ref()) { + Some(recipient.encrypt_data(&sender_keys.get_public().to_vec()?)?) + } else { None }; + let hash = Message::calculate_hash( + &self.content, + self.message_type.clone().unwrap(), + sender_encrypted.clone() + .or_else( + || self.sender.as_ref() + .map(|sender_keys| sender_keys.get_public().to_vec().unwrap()) + ), + ); + let recipient_verification = self.recipient.as_ref().map(|rec| rec.encrypt_data(&hash).unwrap()); + let signature = match (self.sender, self.recipient) { + (Some(sender_keys), Some(recipient_key)) => Signature::SignedPrivately { + sender_encrypted: sender_encrypted.unwrap(), + signature: recipient_key.encrypt_data(&sender_keys.sign(&hash)?)?, + }, + (Some(sender_keys), None) => Signature::Signed { sender: sender_keys.get_public(), signature: sender_keys.sign(&hash)? }, + (None, _) => Signature::NotSigned, + }; + Ok(Message { + content: self.content, + message_type: self.message_type.unwrap(), + signature, + salt, + hash, + recipient_verification, + tunnel_id: self.tunnel_id, + network_info: Default::default(), + }) + } } diff --git a/src/res.rs b/src/res.rs index f06648d..9ba0799 100644 --- a/src/res.rs +++ b/src/res.rs @@ -9,13 +9,29 @@ pub enum IFError { General(String), /// A tunnel satisfying some conditions has not been found TunnelNotFound, + /// Error during serialization + SerializationError(String), + /// Error in rsa + CryptoError(String), } -impl From for IFError { - /// Convert from other error - fn from(e: T) -> Self { - Self::General(format!("{:?}", e)) +// impl From for IFError { +// /// Convert from other error +// fn from(e: T) -> Self { +// Self::General(format!("{:?}", e)) +// } +// } + +impl From for IFError { + fn from(e: serde_cbor::Error) -> Self { + Self::SerializationError(format!("{:?}", e)) + } +} + +impl From for IFError { + fn from(e: rsa::errors::Error) -> Self { + Self::CryptoError(format!("{:?}", e)) } }