From 41a084219380d36b46973303f3404263c152b685 Mon Sep 17 00:00:00 2001 From: ennucore Date: Mon, 29 Nov 2021 12:42:19 +0300 Subject: [PATCH] Cryptography in message (signing, checks), new serialization for PublicKey --- examples/test_ip_connection.rs | 39 +++++-- src/crypto.rs | 69 ++++++++++--- src/interface.rs | 19 ++-- src/ironforce.rs | 31 ++++-- src/message.rs | 184 +++++++++++++++++++++++++++------ 5 files changed, 266 insertions(+), 76 deletions(-) diff --git a/examples/test_ip_connection.rs b/examples/test_ip_connection.rs index 9980298..5620d74 100644 --- a/examples/test_ip_connection.rs +++ b/examples/test_ip_connection.rs @@ -1,11 +1,17 @@ -use std::net; -use ironforce::interfaces::ip::IPInterface; +#[cfg(feature = "std")] use ironforce::interface::Interface; +#[cfg(feature = "std")] +use ironforce::interfaces::ip::IPInterface; +#[cfg(feature = "std")] +use std::net; +#[cfg(feature = "std")] fn main() { - let mut listener = match net::TcpListener::bind("0.0.0.0:60000") { + let mut listener = match net::TcpListener::bind("0.0.0.0:60000") { Ok(tmp) => tmp, - Err(_) => { return; } + Err(_) => { + return; + } }; let mut interface1 = IPInterface { id: String::from("IP interface"), @@ -14,9 +20,11 @@ fn main() { }; let option_listener = net::TcpListener::bind("0.0.0.0:50000"); - let mut listener= match option_listener { + let mut listener = match option_listener { Ok(tmp) => tmp, - Err(_) => { return; } + Err(_) => { + return; + } }; let mut interface2 = IPInterface { id: String::from("IP interface"), @@ -24,17 +32,26 @@ fn main() { listener, }; - let t1 = std::thread::spawn(move || { interface1.send(&[0,0,1,1], Some(String::from("0.0.0.0:50000"))); interface1}); - let t2 = std::thread::spawn(move || { interface2.main_loop_iteration(); interface2}); + let t1 = std::thread::spawn(move || { + interface1.send(&[0, 0, 1, 1], Some(String::from("0.0.0.0:50000"))); + interface1 + }); + let t2 = std::thread::spawn(move || { + interface2.main_loop_iteration(); + interface2 + }); let res1 = t1.join(); match res1 { Ok(_) => println!("Ok"), - Err(e) => println!("{:?}", e) + Err(e) => println!("{:?}", e), } let res2 = t2.join(); match res2 { Ok(_) => println!("Ok"), - Err(e) => println!("{:?}", e) + Err(e) => println!("{:?}", e), } // res1.unwrap(); -} \ No newline at end of file +} + +#[cfg(not(feature = "std"))] +fn main() {} diff --git a/src/crypto.rs b/src/crypto.rs index 05e7bd0..07bd609 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,12 +1,18 @@ /// This module has wrappers for cryptography with RSA algorithms. /// Its main structs - `PublicKey` and `Keys` implement all functions for key generation, signatures and asymmetric encryption use alloc::vec::Vec; +use alloc::string::String; use serde::{Deserialize, Serialize}; -use rsa::{RsaPublicKey, RsaPrivateKey, PaddingScheme, PublicKey as RPK}; +use rsa::{RsaPublicKey, RsaPrivateKey, PaddingScheme, PublicKey as RPK, PublicKeyParts, BigUint}; use rsa::errors::Result as RsaRes; use rand::rngs::OsRng; +use sha2::{Digest, Sha224}; +use alloc::vec; +use crate::res::{IFError, IFResult}; static KEY_LENGTH: usize = 2048; +static ENCRYPTION_CHUNK_SIZE: usize = 240; +static ENCRYPTION_OUTPUT_CHUNK_SIZE: usize = 256; /// Public key of a node @@ -18,26 +24,50 @@ pub struct PublicKey { impl PublicKey { /// Check if the sign is valid for given data and key pub fn verify_sign(&self, data: &[u8], sign: &[u8]) -> bool { - self.key.verify(PaddingScheme::PKCS1v15Sign { hash: None }, data, sign).is_ok() + self.key.verify(PaddingScheme::PKCS1v15Sign { hash: None }, &Sha224::new().chain(data).result().to_vec(), sign).is_ok() } /// Encrypt some data for a user with this public key pub fn encrypt_data(&self, data: &[u8]) -> RsaRes> { - self.key.encrypt(&mut OsRng {}, PaddingScheme::PKCS1v15Encrypt, data) + if data.len() <= ENCRYPTION_CHUNK_SIZE { + self.key.encrypt(&mut OsRng {}, PaddingScheme::PKCS1v15Encrypt, data) + } else { + let mut res = self.key.encrypt(&mut OsRng {}, PaddingScheme::PKCS1v15Encrypt, &data[..ENCRYPTION_CHUNK_SIZE])?; + res.extend(self.encrypt_data(&data[ENCRYPTION_CHUNK_SIZE..])?); + Ok(res) + } } - pub fn to_vec(&self) -> serde_cbor::Result> { - serde_cbor::to_vec(&self.key) + pub fn to_vec(&self) -> Vec { + let n_bytes = self.key.n().to_bytes_be(); + let e_bytes = self.key.e().to_bytes_be(); + let mut res = vec![ + (n_bytes.len() / 256) as u8, (n_bytes.len() % 256) as u8, + (e_bytes.len() / 256) as u8, (e_bytes.len() % 256) as u8, + ]; + res.extend(n_bytes); + res.extend(e_bytes); + res } - pub fn from_vec(data: Vec) -> serde_cbor::Result { - serde_cbor::from_slice(data.as_slice()) + pub fn from_vec(data: Vec) -> IFResult { + if data.len() < 4 { + return Err(IFError::SerializationError(String::from("Not enough bytes in serialized PublicKey"))) + } + let n_len = data[0] as usize * 256 + data[1] as usize; + let e_len = data[2] as usize * 256 + data[3] as usize; + if data.len() != e_len + n_len + 4 { + return Err(IFError::SerializationError(String::from("Not enough bytes in serialized PublicKey"))) + } + let n_bytes = &data[4..n_len + 4]; + let e_bytes = &data[4 + n_len..e_len + n_len + 4]; + Ok(Self { key: RsaPublicKey::new(BigUint::from_bytes_be(n_bytes), BigUint::from_bytes_be(e_bytes))? }) } } impl PublicKey { fn hash(&self) -> Vec { - self.to_vec().unwrap() + self.to_vec() } } @@ -64,12 +94,18 @@ impl Keys { impl Keys { /// Sign content using these keys pub fn sign(&self, content: &[u8]) -> RsaRes> { - self.private_key.sign(PaddingScheme::PKCS1v15Sign { hash: None }, content) + self.private_key.sign(PaddingScheme::PKCS1v15Sign { hash: None }, &Sha224::new().chain(content).result().to_vec()) } /// Decrypt data pub fn decrypt_data(&self, data_encrypted: &[u8]) -> RsaRes> { - self.private_key.decrypt(PaddingScheme::PKCS1v15Encrypt, data_encrypted) + if data_encrypted.len() <= ENCRYPTION_OUTPUT_CHUNK_SIZE { + self.private_key.decrypt(PaddingScheme::PKCS1v15Encrypt, data_encrypted) + } else { + let mut res = self.private_key.decrypt(PaddingScheme::PKCS1v15Encrypt, &data_encrypted[..ENCRYPTION_OUTPUT_CHUNK_SIZE])?; + res.extend(self.decrypt_data(&data_encrypted[ENCRYPTION_OUTPUT_CHUNK_SIZE..])?); + Ok(res) + } } /// Get public key @@ -80,17 +116,19 @@ impl Keys { #[test] fn test_encrypt() { - let data = vec![0, 5, 8, 135, 67]; + let data = vec![0, 5, 8, 135, 67, 45, 32, 5]; let keys = Keys::generate(); + let data_encrypted = keys.get_public().encrypt_data(&data).unwrap(); assert_eq!( - keys.decrypt_data(&keys.get_public().encrypt_data(&data).unwrap()).unwrap(), + keys.decrypt_data(&data_encrypted).unwrap(), data ); + assert_eq!( + keys.decrypt_data(&keys.get_public().encrypt_data(&data.repeat(300)).unwrap()).unwrap(), + data.repeat(300) + ); } -#[cfg(test)] -use alloc::vec; - #[test] fn test_invalid_encrypt() { let data = vec![0, 5, 8, 135, 67]; @@ -104,6 +142,7 @@ fn test_signing() { let data = vec![0, 5, 8, 135, 67]; let keys = Keys::generate(); assert!(keys.get_public().verify_sign(&data, &keys.sign(&data).unwrap())); + assert!(keys.get_public().verify_sign(&data.repeat(340), &keys.sign(&data.repeat(340)).unwrap())); } #[test] diff --git a/src/interface.rs b/src/interface.rs index 1ae750a..eeec7cf 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -1,7 +1,6 @@ -use alloc::string::String; use crate::message::MessageBytes; use crate::res::IFResult; - +use alloc::string::String; /// Some data that can be provided to the interface to send the message to a target. /// @@ -16,7 +15,6 @@ pub trait InterfaceRequirements {} #[cfg(feature = "std")] pub trait InterfaceRequirements: Send + Sync {} - /// An interface that can be used to pub trait Interface: InterfaceRequirements { /// Run one main loop iteration. @@ -28,13 +26,17 @@ pub trait Interface: InterfaceRequirements { fn main_loop_iteration(&mut self) -> IFResult<()>; /// Check if `main_loop_iteration` stops execution and waits for a message fn has_blocking_main(&self) -> bool { - false // hopefully... + false // hopefully... } /// Get some way of identification for this interface fn id(&self) -> &str; /// Send a message. If no `interface_data` is provided, we should consider it to be a broadcast. /// If, on the other hand, `interface_data` is not `None`, it should be used to send the message to the target. - fn send(&mut self, message: &[u8] /*MessageBytes*/, interface_data: Option) -> IFResult<()>; + fn send( + &mut self, + message: &[u8], /*MessageBytes*/ + interface_data: Option, + ) -> IFResult<()>; /// Receive a message through this interface. Returns a result with an option of (message bytes, target). /// `None` means there is no message available at the time. /// The implementations of this function shouldn't wait for new messages, but @@ -65,14 +67,13 @@ pub mod test_interface { } fn send(&mut self, message: &[u8], interface_data: Option) -> IFResult<()> { - self.messages.push((Vec::from(message), interface_data.unwrap_or_default())); + self.messages + .push((Vec::from(message), interface_data.unwrap_or_default())); Ok(()) } fn receive(&mut self) -> IFResult> { - Ok( - self.messages.pop() - ) + Ok(self.messages.pop()) } } } diff --git a/src/ironforce.rs b/src/ironforce.rs index 342c9f4..34123b4 100644 --- a/src/ironforce.rs +++ b/src/ironforce.rs @@ -1,11 +1,10 @@ -use alloc::vec::Vec; -use alloc::vec; use crate::crypto::PublicKey; use crate::message::{Message, MessageType, ServiceMessageType}; use crate::res::{IFError, IFResult}; use crate::transport::Transport; use crate::tunnel::Tunnel; - +use alloc::vec; +use alloc::vec::Vec; /// Main worker pub struct IronForce { @@ -43,16 +42,30 @@ impl IronForce { } /// Send a message through tunnel - fn send_through_tunnel(&mut self, _tunnel_id: u64, _message: Message, _direction: Option) -> IFResult<()> { + fn send_through_tunnel( + &mut self, + _tunnel_id: u64, + _message: Message, + _direction: Option, + ) -> IFResult<()> { todo!() } /// Send a message to another node, /// creating a new tunnel if needed pub fn send_message(&mut self, message: Message, destination: PublicKey) -> IFResult<()> { - if let Some(Some(tunnel_id)) = self.tunnels.iter() - .find(|t| t.target_node.as_ref() == Some(&destination) || t.nodes_in_tunnel.as_ref().map(|nodes| nodes.contains(&destination)) == Some(true)) - .map(|tunnel| tunnel.id) { + if let Some(Some(tunnel_id)) = self + .tunnels + .iter() + .find(|t| { + t.target_node.as_ref() == Some(&destination) + || t.nodes_in_tunnel + .as_ref() + .map(|nodes| nodes.contains(&destination)) + == Some(true) + }) + .map(|tunnel| tunnel.id) + { self.send_through_tunnel(tunnel_id, message, None) } else { Err(IFError::TunnelNotFound) @@ -67,8 +80,8 @@ impl IronForce { ServiceMessageType::TunnelBuilding(_tunnel) => { todo!() } - } - MessageType::SingleCast | MessageType::Broadcast => { self.messages.push(message) } + }, + MessageType::SingleCast | MessageType::Broadcast => self.messages.push(message), } } diff --git a/src/message.rs b/src/message.rs index 77b7619..defcc9e 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,12 +1,11 @@ -use alloc::string::String; -use alloc::vec::Vec; use crate::crypto::{Keys, PublicKey}; use crate::res::IFResult; use crate::tunnel::TunnelPublic; -use serde::{Serialize, Deserialize}; +use alloc::string::String; +use alloc::vec::Vec; +use serde::{Deserialize, Serialize}; use sha2::Digest; - /// A serialized message pub(crate) type MessageBytes = Vec; @@ -27,6 +26,19 @@ pub enum Signature { }, } +impl Signature { + /// Get sender's key or its encrypted version for hashing + pub(crate) fn sender_or_encrypted_sender(&self) -> Option> { + match &self { + Signature::NotSigned => None, + Signature::Signed { sender, .. } => Some(sender.to_vec()), + Signature::SignedPrivately { + sender_encrypted, .. + } => Some(sender_encrypted.clone()), + } + } +} + /// Network name and version #[derive(Serialize, Deserialize, Clone)] pub struct NetworkInfo { @@ -36,7 +48,10 @@ pub struct NetworkInfo { impl Default for NetworkInfo { fn default() -> Self { - Self { version: String::from("0.1.0"), network_name: String::from("test") } + Self { + version: String::from("0.1.0"), + network_name: String::from("test"), + } } } @@ -52,14 +67,16 @@ impl MessageType { match self { MessageType::SingleCast => Vec::from([0]), MessageType::Broadcast => Vec::from([1]), - MessageType::Service(ServiceMessageType::TunnelBuilding(tunnel)) => [2, 0].iter().chain(tunnel.hash().iter()).copied().collect() + MessageType::Service(ServiceMessageType::TunnelBuilding(tunnel)) => { + [2, 0].iter().chain(tunnel.hash().iter()).copied().collect() + } } } } #[derive(Serialize, Deserialize, Clone)] pub enum ServiceMessageType { - TunnelBuilding(TunnelPublic) + TunnelBuilding(TunnelPublic), } #[derive(Serialize, Deserialize, Clone)] @@ -78,12 +95,14 @@ impl MessageContent { MessageContent::Plain(v) => sha2::Sha512::new() .chain(&[0u8; 1]) .chain(v.as_slice()) - .result().to_vec(), + .result() + .to_vec(), MessageContent::Encrypted(v) => sha2::Sha512::new() .chain(&[1; 1]) .chain(v.as_slice()) - .result().to_vec(), - MessageContent::None => Vec::new() + .result() + .to_vec(), + MessageContent::None => Vec::new(), } } } @@ -113,36 +132,106 @@ pub struct Message { impl Message { /// Verify message's hash - pub fn verify(&self) -> bool { - todo!() + pub fn verify_hash(&self) -> bool { + self.hash + == Self::calculate_hash( + &self.content, + self.message_type.clone(), + self.signature.sender_or_encrypted_sender(), + &self.network_info, + ) + } + + /// Verify sender's signature + pub fn verify_signature(&self, recipient_keys: Keys) -> bool { + match &self.signature { + Signature::NotSigned => true, + Signature::Signed { signature, sender } => { + sender.verify_sign(self.hash.as_slice(), signature.as_slice()) + } + Signature::SignedPrivately { signature, .. } => { + if let Some(sender) = self.get_sender(&recipient_keys) { + sender.verify_sign( + self.hash.as_slice(), + &match recipient_keys.decrypt_data(signature.as_slice()) { + Ok(r) => r, + Err(_e) => return false, + }, + ) + } else { + false + } + } + } } /// Check if this message is for this set of keys - pub fn check_recipient(&self, _keys: Keys) -> bool { - todo!() + pub fn check_recipient(&self, keys: Keys) -> bool { + keys.decrypt_data(&self.recipient_verification.clone().unwrap_or_default()) + .is_ok() } /// Get decrypted content of the message - pub fn get_decrypted(&self, _keys: Keys) -> IFResult> { - todo!() + pub fn get_decrypted(&self, keys: Keys) -> IFResult> { + Ok(match &self.content { + MessageContent::Plain(c) => c.clone(), + MessageContent::Encrypted(encrypted_content) => { + keys.decrypt_data(encrypted_content.as_slice())? + } + MessageContent::None => Vec::new(), + }) } - pub fn calculate_hash(content: &MessageContent, message_type: MessageType, sender_or_encrypted_sender: Option>, network_info: &NetworkInfo) -> Vec { + pub fn calculate_hash( + content: &MessageContent, + message_type: MessageType, + sender_or_encrypted_sender: Option>, + network_info: &NetworkInfo, + ) -> Vec { sha2::Sha512::new() .chain(content.hash().as_slice()) .chain(message_type.hash().as_slice()) .chain(sender_or_encrypted_sender.unwrap_or_default().as_slice()) .chain(network_info.network_name.as_bytes()) .chain(network_info.version.as_bytes()) - .result().to_vec() + .result() + .to_vec() } /// Encrypt hash of the message for the recipient - pub fn generate_recipient_verification(hash: Vec, recipient: PublicKey) -> rsa::errors::Result> { + pub fn generate_recipient_verification( + hash: Vec, + recipient: PublicKey, + ) -> rsa::errors::Result> { recipient.encrypt_data(&hash) } -} + /// Try to get sender from the signature + fn get_sender(&self, keys: &Keys) -> Option { + match &self.signature { + Signature::NotSigned => None, + Signature::Signed { sender, .. } => Some(sender.clone()), + Signature::SignedPrivately { + sender_encrypted, .. + } => { + if let Some(Some(res)) = keys + .decrypt_data(sender_encrypted.as_slice()) + .ok() + .map(|k| PublicKey::from_vec(k).ok()) + { + Some(res) + } else { + None + } + } + } + } + + /// Create new MessageBuilder + pub fn build() -> MessageBuilder { + MessageBuilder::new() + } +} /// Message builder to create a new message step-by-step, like `Message::build().message_type(...).sign(...)` pub struct MessageBuilder { @@ -201,27 +290,37 @@ impl MessageBuilder { /// 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 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 network_info = NetworkInfo::default(); 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()) - ), - &network_info + sender_encrypted.clone().or_else(|| { + self.sender + .as_ref() + .map(|sender_keys| sender_keys.get_public().to_vec()) + }), + &network_info, ); - let recipient_verification = self.recipient.as_ref().map(|rec| rec.encrypt_data(&hash).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)? }, + (Some(sender_keys), None) => Signature::Signed { + sender: sender_keys.get_public(), + signature: sender_keys.sign(&hash)?, + }, (None, _) => Signature::NotSigned, }; Ok(Message { @@ -239,11 +338,16 @@ impl MessageBuilder { #[cfg(test)] use alloc::vec; +#[cfg(feature = "std")] +#[cfg(test)] +use std::println; #[test] fn test_hashing_message_type() { let msg_type_1 = MessageType::Broadcast; - let msg_type_2 = MessageType::Service(ServiceMessageType::TunnelBuilding(TunnelPublic::new_for_test())); + let msg_type_2 = MessageType::Service(ServiceMessageType::TunnelBuilding( + TunnelPublic::new_for_test(), + )); assert_eq!(msg_type_1.hash(), msg_type_1.hash()); assert_eq!(msg_type_2.hash(), msg_type_2.hash()); assert_ne!(msg_type_1.hash(), msg_type_2.hash()) @@ -260,3 +364,19 @@ fn test_hash_message_content() { assert_ne!(content_1.hash(), content_3.hash()); assert_ne!(content_3.hash(), content_2.hash()); } + +#[test] +fn test_building_message() -> IFResult<()> { + let keys_1 = Keys::generate(); + let keys_2 = Keys::generate(); + let msg = Message::build() + .content(b"hello".to_vec()) + .sign(&keys_1) + .recipient(keys_2.get_public()) + .tunnel(1) + .message_type(MessageType::SingleCast) + .build()?; + assert!(msg.verify_hash()); + assert!(msg.verify_signature(keys_2)); + Ok(()) +}