Browse Source

Wrote a messenger)

master
Lev 3 years ago
parent
commit
68d2fb2af3
  1. 3389
      Cargo.lock
  2. 3
      Cargo.toml
  3. 3238
      degeon/Cargo.lock
  4. 12
      degeon/Cargo.toml
  5. 6
      degeon/src/gui_events.rs
  6. 70
      degeon/src/main.rs
  7. 14
      degeon/src/message.rs
  8. 270
      degeon/src/state.rs
  9. 113
      src/crypto.rs
  10. 11
      src/ironforce.rs
  11. 2
      src/lib.rs

3389
Cargo.lock generated

File diff suppressed because it is too large Load Diff

3
Cargo.toml

@ -5,6 +5,9 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
members = ["degeon"]
[features]
default = []
std = ["rayon"]

3238
degeon/Cargo.lock generated

File diff suppressed because it is too large Load Diff

12
degeon/Cargo.toml

@ -0,0 +1,12 @@
[package]
name = "degeon"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
iced = { version = "0.3", features = ["glow"] }
ironforce = { path = "../", features = ["std"] }
base64 = "0.13.0"
serde = { version = "1.0" }

6
degeon/src/gui_events.rs

@ -0,0 +1,6 @@
#[derive(Clone, Debug)]
pub enum GuiEvent {
ChatSelect(usize),
Typed(String),
SendClick,
}

70
degeon/src/main.rs

@ -0,0 +1,70 @@
extern crate serde;
mod message;
mod state;
mod gui_events;
use iced::Sandbox;
use ironforce::res::IFResult;
use ironforce::{IronForce, Message, MessageType, PublicKey};
use crate::state::State;
fn main_if() -> IFResult<()> {
let ironforce = IronForce::from_file("".to_string())?;
let if_keys = ironforce.keys.clone();
println!(
"Our public key: {}",
base64::encode(if_keys.get_public().to_vec().as_slice())
);
let (_thread, if_mutex) = ironforce.launch_main_loop(100);
let stdin = std::io::stdin();
let if_mutex_clone = if_mutex.clone();
let if_keys_clone = if_keys.clone();
std::thread::spawn(move || loop {
if let Some(msg) = if_mutex_clone.lock().unwrap().read_message() {
println!(
"New message: {}",
String::from_utf8(msg.get_decrypted(&if_keys_clone).unwrap()).unwrap()
);
}
std::thread::sleep(std::time::Duration::from_millis(300))
});
loop {
let mut buf = String::new();
stdin.read_line(&mut buf)?;
let msg_base = if buf.starts_with('>') {
let target_base64 = buf
.split(')')
.next()
.unwrap()
.trim_start_matches(">(")
.to_string();
let target = if let Ok(res) = base64::decode(target_base64) {
res
} else {
println!("Wrong b64.");
continue;
};
buf = buf
.split(')')
.skip(1)
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join(")");
Message::build()
.message_type(MessageType::SingleCast)
.recipient(&PublicKey::from_vec(target).unwrap())
} else {
Message::build().message_type(MessageType::Broadcast)
};
if_mutex
.lock()
.unwrap()
.send_to_all(msg_base.content(buf.into_bytes()).sign(&if_keys).build()?)?;
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// let ironforce = IronForce::from_file("".to_string()).unwrap();
// let _if_keys = ironforce.keys.clone();
Ok(State::run(iced::Settings::default())?)
}

14
degeon/src/message.rs

@ -0,0 +1,14 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Message {
Text(String),
File(Vec<u8>),
Service(ServiceMsg),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ServiceMsg {
NameRequest,
NameStatement(String),
}

270
degeon/src/state.rs

@ -0,0 +1,270 @@
use crate::gui_events::GuiEvent;
use crate::message::Message;
use core::default::Default;
use iced::{
button, Align, Button, Column, Element, HorizontalAlignment, Length, Row, Sandbox, Settings,
Text, TextInput, VerticalAlignment,
};
use ironforce::{Keys, PublicKey};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)]
pub struct Chat {
pkey: PublicKey,
messages: Vec<(bool, Message)>,
name: String,
scrolled: f32,
pub input: String,
}
pub fn view_message(msg: &(bool, Message)) -> Option<Element<GuiEvent>> {
let msg = &msg.1;
match msg {
Message::Text(t) => Some(
iced::Container::new(Text::new(t.as_str()))
.padding(10)
.style(style::Container::Message)
.into(),
),
Message::File(_) => None,
Message::Service(_) => None,
}
}
mod style {
use iced::container::Style;
use iced::{button, Background, Color, Vector};
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Button {
Primary,
Secondary,
Destructive,
InactiveChat,
}
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(match self {
Button::Primary => Color::from_rgb(0.11, 0.35, 0.75),
Button::Secondary => Color::from_rgb(0.3, 0.1, 0.7),
Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2),
Button::InactiveChat => Color::from_rgb(0.3, 0.52, 0.9),
})),
border_radius: 5.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: if self != &Button::InactiveChat { Color::WHITE } else { Color::BLACK },
..button::Style::default()
}
}
}
pub enum Container {
Primary,
Message,
}
impl iced::container::StyleSheet for Container {
fn style(&self) -> Style {
iced::container::Style {
text_color: Some(Color::WHITE),
background: Some(Background::Color(match self {
Container::Primary => Color::from_rgb(18. / 256., 25. / 256., 70. / 256.),
Container::Message => Color::from_rgb(0., 0.1, 0.8),
})),
border_radius: 5.0,
border_width: 0.6,
border_color: Color::TRANSPARENT,
}
}
}
}
impl Chat {
pub fn header<'a>(name: String) -> Element<'a, GuiEvent> {
iced::container::Container::new(Text::new(name.as_str()).color(iced::Color::WHITE))
.style(style::Container::Primary)
.width(Length::Fill)
.height(Length::Units(50))
.padding(10)
.into()
}
pub fn send_field<'a>(
input: String,
text_input_state: &'a mut iced::text_input::State,
send_button_state: &'a mut iced::button::State,
) -> Element<'a, GuiEvent> {
Row::new()
.width(Length::Fill)
.padding(15)
.push(
TextInput::new(text_input_state, "Message", input.as_str(), |st| {
GuiEvent::Typed(st)
})
.padding(8)
.width(Length::Fill),
)
.push(
Button::new(send_button_state, Text::new("Send"))
.on_press(GuiEvent::SendClick)
.style(style::Button::Secondary)
.padding(20)
.width(Length::Units(80)),
)
.spacing(25)
.height(Length::Units(100))
.into()
}
pub fn preview<'a>(&'a self, state: &'a mut button::State, i: usize, is_selected: bool) -> Element<'a, GuiEvent> {
Button::new(state, Text::new(self.name.as_str()))
.width(Length::Fill)
.padding(10)
.style(if is_selected { style::Button::Primary } else { style::Button::InactiveChat })
.on_press(GuiEvent::ChatSelect(i))
.into()
}
pub fn view<'a>(
&'a self,
text_input_state: &'a mut iced::text_input::State,
send_button_state: &'a mut iced::button::State,
) -> Element<'a, GuiEvent> {
let msgs = self.messages.iter().filter_map(view_message).collect();
Column::new()
.align_items(Align::End)
.height(Length::Fill)
.width(Length::FillPortion(4))
.push(Self::header(self.name.clone()))
.push(
Column::with_children(msgs)
.padding(20)
.spacing(10)
.align_items(Align::End)
.height(Length::FillPortion(9)),
)
.spacing(10)
.push(Self::send_field(
self.input.to_string(),
text_input_state,
send_button_state,
))
.into()
}
pub fn example(i: usize) -> Chat {
Self {
pkey: Keys::generate().get_public(),
messages: vec![(false, Message::Text(format!("Example message {}", i)))],
name: format!("Example user ({})", i),
scrolled: 0.0,
input: "".to_string(),
}
}
}
#[derive(Default, Clone, Debug)]
pub struct State {
chats: Vec<Chat>,
my_name: String,
selected_chat: usize,
pub send_button_state: iced::button::State,
text_input_state: iced::text_input::State,
preview_button_states: Vec<button::State>,
}
impl State {
fn chat_list<'a>(
chats: &'a Vec<Chat>,
preview_button_states: &'a mut Vec<button::State>,
selected: usize
) -> Element<'a, GuiEvent> {
Column::with_children(
chats
.iter()
.zip(preview_button_states.iter_mut())
.enumerate()
.map(|(i, (chat, state))| chat.preview(state, i, i == selected))
.collect(),
)
.padding(20)
.spacing(10)
.align_items(Align::Start)
.width(Length::FillPortion(1))
.into()
}
pub fn active_chat<'a>(
chats: &'a Vec<Chat>,
selected_chat: usize,
send_button_state: &'a mut button::State,
text_input_state: &'a mut iced::text_input::State,
) -> Element<'a, GuiEvent> {
if selected_chat >= chats.len() {
Text::new("No chat")
.horizontal_alignment(HorizontalAlignment::Center)
.vertical_alignment(VerticalAlignment::Center)
.width(Length::FillPortion(4))
.into()
} else {
chats[selected_chat].view(text_input_state, send_button_state)
}
}
}
impl Sandbox for State {
type Message = GuiEvent;
fn new() -> Self {
let mut st = Self::default();
st.chats = vec![Chat::example(1), Chat::example(2)];
st.preview_button_states = vec![Default::default(), Default::default()];
st
}
fn title(&self) -> String {
String::from("Degeon")
}
fn update(&mut self, message: GuiEvent) {
match message {
GuiEvent::ChatSelect(i) => self.selected_chat = i,
GuiEvent::Typed(st) => self.chats[self.selected_chat].input = st,
GuiEvent::SendClick => {
if self.chats[self.selected_chat].input.is_empty() {
return;
}
let new_msg = Message::Text(self.chats[self.selected_chat].input.clone());
self.chats[self.selected_chat].input = String::new();
self.chats[self.selected_chat]
.messages
.push((true, new_msg));
// todo
}
}
}
fn view(&mut self) -> Element<GuiEvent> {
let Self {
chats,
selected_chat,
send_button_state,
text_input_state,
preview_button_states,
..
} = self;
Row::new()
.padding(20)
.push(Self::chat_list(chats, preview_button_states, *selected_chat))
.push(Self::active_chat(
chats,
*selected_chat,
send_button_state,
text_input_state,
))
.height(Length::Fill)
.into()
}
}

113
src/crypto.rs

@ -1,20 +1,19 @@
use crate::res::{IFError, IFResult};
use alloc::string::String;
use alloc::vec;
/// 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, PublicKeyParts, BigUint};
use rsa::errors::Result as RsaRes;
use rand::rngs::OsRng;
use rsa::errors::Result as RsaRes;
use rsa::{BigUint, PaddingScheme, PublicKey as RPK, PublicKeyParts, RsaPrivateKey, RsaPublicKey};
use serde::{Deserialize, Serialize};
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
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct PublicKey {
@ -24,15 +23,26 @@ 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 }, &Sha224::new().chain(data).result().to_vec(), 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<Vec<u8>> {
if data.len() <= ENCRYPTION_CHUNK_SIZE {
self.key.encrypt(&mut OsRng {}, PaddingScheme::PKCS1v15Encrypt, data)
self.key
.encrypt(&mut OsRng {}, PaddingScheme::PKCS1v15Encrypt, data)
} else {
let mut res = self.key.encrypt(&mut OsRng {}, PaddingScheme::PKCS1v15Encrypt, &data[..ENCRYPTION_CHUNK_SIZE])?;
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)
}
@ -42,8 +52,10 @@ impl PublicKey {
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,
(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);
@ -52,16 +64,38 @@ impl PublicKey {
pub fn from_vec(data: Vec<u8>) -> IFResult<Self> {
if data.len() < 4 {
return Err(IFError::SerializationError(String::from("Not enough bytes in serialized PublicKey")))
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")))
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))? })
Ok(Self {
key: RsaPublicKey::new(
BigUint::from_bytes_be(n_bytes),
BigUint::from_bytes_be(e_bytes),
)?,
})
}
/// Get a short string that's kind of a hash
pub fn get_short_id(&self) -> String {
alloc::string::String::from_utf8(
self.to_vec()
.iter()
.skip(90)
.take(5)
.map(|c| c % 26 + 97)
.collect::<Vec<u8>>(),
)
.unwrap()
}
}
@ -82,7 +116,8 @@ impl Keys {
/// Generate new random key
pub fn generate() -> Self {
let mut rng = OsRng;
let private_key = RsaPrivateKey::new(&mut rng, KEY_LENGTH).expect("failed to generate a key");
let private_key =
RsaPrivateKey::new(&mut rng, KEY_LENGTH).expect("failed to generate a key");
let public_key = RsaPublicKey::from(&private_key);
Self {
private_key,
@ -94,15 +129,22 @@ impl Keys {
impl Keys {
/// Sign content using these keys
pub fn sign(&self, content: &[u8]) -> RsaRes<Vec<u8>> {
self.private_key.sign(PaddingScheme::PKCS1v15Sign { hash: None }, &Sha224::new().chain(content).result().to_vec())
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<Vec<u8>> {
if data_encrypted.len() <= ENCRYPTION_OUTPUT_CHUNK_SIZE {
self.private_key.decrypt(PaddingScheme::PKCS1v15Encrypt, data_encrypted)
self.private_key
.decrypt(PaddingScheme::PKCS1v15Encrypt, data_encrypted)
} else {
let mut res = self.private_key.decrypt(PaddingScheme::PKCS1v15Encrypt, &data_encrypted[..ENCRYPTION_OUTPUT_CHUNK_SIZE])?;
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)
}
@ -110,7 +152,9 @@ impl Keys {
/// Get public key
pub fn get_public(&self) -> PublicKey {
PublicKey { key: self.public_key.clone() }
PublicKey {
key: self.public_key.clone(),
}
}
}
@ -119,12 +163,10 @@ fn test_encrypt() {
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(&data_encrypted).unwrap(), data);
assert_eq!(
keys.decrypt_data(&data_encrypted).unwrap(),
data
);
assert_eq!(
keys.decrypt_data(&keys.get_public().encrypt_data(&data.repeat(300)).unwrap()).unwrap(),
keys.decrypt_data(&keys.get_public().encrypt_data(&data.repeat(300)).unwrap())
.unwrap(),
data.repeat(300)
);
}
@ -134,15 +176,21 @@ fn test_invalid_encrypt() {
let data = vec![0, 5, 8, 135, 67];
let keys_1 = Keys::generate();
let keys_2 = Keys::generate();
assert!(keys_2.decrypt_data(&keys_1.get_public().encrypt_data(&data).unwrap()).is_err());
assert!(keys_2
.decrypt_data(&keys_1.get_public().encrypt_data(&data).unwrap())
.is_err());
}
#[test]
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()));
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]
@ -150,10 +198,15 @@ fn test_invalid_signing() {
let data = vec![0, 5, 8, 135, 67];
let keys_1 = Keys::generate();
let keys_2 = Keys::generate();
assert!(!keys_2.get_public().verify_sign(&data, &keys_1.sign(&data).unwrap()));
assert!(!keys_2
.get_public()
.verify_sign(&data, &keys_1.sign(&data).unwrap()));
}
#[test]
fn test_pkey_caching() {
assert_ne!(Keys::generate().get_public().hash(), Keys::generate().get_public().hash())
assert_ne!(
Keys::generate().get_public().hash(),
Keys::generate().get_public().hash()
)
}

11
src/ironforce.rs

@ -305,16 +305,7 @@ impl IronForce {
}
fn short_id(&self) -> alloc::string::String {
let vec_data = self.keys.get_public().to_vec();
alloc::string::String::from_utf8(
vec_data
.iter()
.skip(90)
.take(5)
.map(|c| c % 26 + 97)
.collect::<Vec<u8>>(),
)
.unwrap()
self.keys.get_public().get_short_id()
}
pub fn get_serialization_data(&self) -> IFSerializationData {

2
src/lib.rs

@ -23,7 +23,7 @@ pub mod res;
mod tunnel;
pub use ironforce::{IronForce, DEFAULT_FILE};
pub use ironforce::IronForce;
pub use message::{Message, MessageType};
pub use crypto::{Keys, PublicKey};

Loading…
Cancel
Save