From 9b4cc60653c8b091f737b6acfc0dbc1882f38e52 Mon Sep 17 00:00:00 2001 From: Gitea Date: Tue, 19 Jul 2022 10:46:32 +0300 Subject: [PATCH] 8 bit key; PRNG; XOR encryption --- src/crypt/keys.rs | 46 ++++++++++++++++++ src/crypt/mod.rs | 3 ++ src/{protocol/constants.rs => crypt/rand.rs} | 20 ++++++-- src/crypt/xor.rs | 50 ++++++++++++++++++++ src/main.rs | 1 + src/nodes/node.rs | 9 ++++ src/protocol/mod.rs | 1 - src/protocol/packets.rs | 43 ++++++++++++++--- src/protocol/specs.rs | 35 ++++++++++---- 9 files changed, 187 insertions(+), 21 deletions(-) create mode 100644 src/crypt/keys.rs create mode 100644 src/crypt/mod.rs rename src/{protocol/constants.rs => crypt/rand.rs} (61%) create mode 100644 src/crypt/xor.rs diff --git a/src/crypt/keys.rs b/src/crypt/keys.rs new file mode 100644 index 0000000..228d02f --- /dev/null +++ b/src/crypt/keys.rs @@ -0,0 +1,46 @@ +/* +ddtu - digital data transferring utility +Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. +*/ + +use crate::crypt::rand; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub struct Key8bit { + pub k: u8, +} + +impl Key8bit { + pub fn new(key: u8) -> Key8bit { + return Key8bit{ + k: key, + } + } + + pub fn new_random() -> Key8bit { + return Key8bit{ + k: rand::rand_u16(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u16) as u8, + } + } +} + +#[cfg(test)] +mod tests { + use crate::crypt::keys::Key8bit; + + #[test] + fn test_key8bit_generation() { + assert_eq!(Key8bit::new(255).k, 255); + Key8bit::new_random(); + } +} \ No newline at end of file diff --git a/src/crypt/mod.rs b/src/crypt/mod.rs new file mode 100644 index 0000000..98a7eee --- /dev/null +++ b/src/crypt/mod.rs @@ -0,0 +1,3 @@ +pub mod keys; +pub mod rand; +pub mod xor; \ No newline at end of file diff --git a/src/protocol/constants.rs b/src/crypt/rand.rs similarity index 61% rename from src/protocol/constants.rs rename to src/crypt/rand.rs index 9ba45ac..adb3acc 100644 --- a/src/protocol/constants.rs +++ b/src/crypt/rand.rs @@ -14,9 +14,19 @@ GNU Affero General Public License for more details. */ -pub const CONNECTION_SHUTDOWN_PACKET_ID: u8 = 1; -pub const TEXT_PACKET_ID: u8 = 2; -pub const FILEINFO_PACKET_ID: u8 = 3; -pub const FILEDATA_PACKET_ID: u8 = 4; +pub fn rand_u16(seed: u16) -> u16 { + return ((seed & 47851) + (seed >> 8 | 7943)) as u16; +} -pub const CHUNK_SIZE: usize = 262_144; // 256 KB \ No newline at end of file +#[cfg(test)] +mod tests { + use crate::crypt::rand::rand_u16; + + #[test] + fn test_rand_u16() { + assert_eq!(rand_u16(10), 7953); + assert_eq!(rand_u16(12345), 20320); + assert_eq!(rand_u16(60000), 51791); + assert_eq!(rand_u16(64550), 55329); + } +} \ No newline at end of file diff --git a/src/crypt/xor.rs b/src/crypt/xor.rs new file mode 100644 index 0000000..c81a598 --- /dev/null +++ b/src/crypt/xor.rs @@ -0,0 +1,50 @@ +/* +ddtu - digital data transferring utility +Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. +*/ + +use crate::crypt::keys::Key8bit; + +pub fn encrypt(plain: &mut [u8], key: &Key8bit) { + for i in 0..plain.len() { + plain[i] = plain[i] ^ key.k; + } +} + +pub fn decrypt(encrypted: &mut[u8], key: &Key8bit) { + for i in 0..encrypted.len() { + encrypted[i] = encrypted[i] ^ key.k; + } +} + +#[cfg(test)] +mod tests { + use crate::crypt::keys::Key8bit; + use crate::crypt::xor; + + #[test] + fn test_xor() { + let plaintext = "plain text 123"; + let mut plaintext_bytes: Vec = Vec::with_capacity(plaintext.bytes().len()); + for b in plaintext.bytes() { + plaintext_bytes.push(b); + } + + let key = Key8bit::new(45); + + xor::encrypt(&mut plaintext_bytes, &key); + xor::decrypt(&mut plaintext_bytes, &key); + + assert_eq!(String::from_utf8_lossy(&plaintext_bytes), plaintext); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 508c51a..f87d4ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ GNU Affero General Public License for more details. mod util; mod protocol; mod args; +mod crypt; mod fsys; use args::parser::{Args, RunMode}; diff --git a/src/nodes/node.rs b/src/nodes/node.rs index e69de29..f82ca6b 100644 --- a/src/nodes/node.rs +++ b/src/nodes/node.rs @@ -0,0 +1,9 @@ +pub struct Node { + +} + +impl Node { + pub fn new() -> Node { + return Node{}; + } +} \ No newline at end of file diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 3659e85..8523e86 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,3 +1,2 @@ pub mod packets; -pub mod constants; pub mod specs; \ No newline at end of file diff --git a/src/protocol/packets.rs b/src/protocol/packets.rs index 5678b87..e9caf08 100644 --- a/src/protocol/packets.rs +++ b/src/protocol/packets.rs @@ -15,7 +15,6 @@ GNU Affero General Public License for more details. use crate::util::error::Error; use crate::util::buf_read::{read_u128_slice, read_utf8_string_slice}; -use crate::protocol::constants; use crate::protocol::specs::*; use crate::fsys::file::ChunkedFile; @@ -46,7 +45,7 @@ impl Packet for TextPacket { fn as_bytes(&self) -> Vec { let mut packet_as_bytes: Vec = Vec::::new(); - packet_as_bytes.extend_from_slice(&constants::TEXT_PACKET_ID.to_be_bytes()); + packet_as_bytes.extend_from_slice(&TEXT_PACKET_ID.to_be_bytes()); let text_length: u128 = self.text.len() as u128; packet_as_bytes.extend_from_slice(&text_length.to_be_bytes()); @@ -64,7 +63,7 @@ impl Packet for TextPacket { // retrieve and check for packet type byte match packet_bytes[0].to_be() { - constants::TEXT_PACKET_ID => {} + TEXT_PACKET_ID => {} _ => { return Some(Error::new("packet type is not of a text packet")); } @@ -207,7 +206,7 @@ impl Packet for FileInfoPacket { pub struct FileDataPacket { pub file_id: u128, pub chunk_no: u128, - pub chunk: [u8; constants::CHUNK_SIZE], + pub chunk: [u8; CHUNK_SIZE], } impl FileDataPacket { @@ -215,7 +214,7 @@ impl FileDataPacket { return FileDataPacket { file_id: 0, chunk_no: 0, - chunk: [0; constants::CHUNK_SIZE], + chunk: [0; CHUNK_SIZE], } } } @@ -272,7 +271,39 @@ impl Packet for ConnectionShutdownPacket { } fn as_bytes(&self) -> Vec { - return vec!(constants::CONNECTION_SHUTDOWN_PACKET_ID.to_be_bytes()[0]); + return vec!(CONNECTION_SHUTDOWN_PACKET_ID.to_be_bytes()[0]); + } + + fn from_bytes(&mut self, _packet_bytes: Vec) -> Option { + return None; + } +} + +pub struct HandshakePaket { + pub protocol_version: u8, + pub use_encryption: bool, + pub encryption_type: u8, + pub encryption_key: Vec, +} + +impl HandshakePaket { + fn empty() -> HandshakePaket { + return HandshakePaket{ + protocol_version: PROTOCOL_LATEST_VERSION, + use_encryption: false, + encryption_type: ECRYPTION_XOR, + encryption_key: vec!(0, 0, 0, 0, 0, 0, 0, 0), + }; + } +} + +impl Packet for HandshakePaket { + fn get_type(&self) -> PacketType { + return PacketType::Handshake; + } + + fn as_bytes(&self) -> Vec { + return vec!(HANDSHAKE_PACKET_ID.to_be_bytes()[0]); } fn from_bytes(&mut self, _packet_bytes: Vec) -> Option { diff --git a/src/protocol/specs.rs b/src/protocol/specs.rs index d1b9dfe..5a915b1 100644 --- a/src/protocol/specs.rs +++ b/src/protocol/specs.rs @@ -20,10 +20,11 @@ GNU Affero General Public License for more details. First 16 bytes - the total packet length The next 1 byte - number that represents this packet's type (ID) where: -1: CONNECTION SHUTDOWN PACKET -2: TEXT PACKET -3: FILEINFO PACKET -4: FILEDATA PACKET +1: HANDSHAKE PACKET +2: CONNECTION SHUTDOWN PACKET +3: TEXT PACKET +4: FILEINFO PACKET +5: FILEDATA PACKET Then the internal structure varies from one packet type to the other, but the content-types are encoded as follows: @@ -34,19 +35,35 @@ String - u128 representing the string length and then UTF-8 encoded character by */ use crate::util::error::Error; -use crate::protocol::constants; use crate::protocol::packets::*; use std::net; use std::io::Write; use std::io::Read; +// Protocol versioning +pub const PROTOCOL_LATEST_VERSION: u8 = 1; + +// Packets' IDs +pub const HANDSHAKE_PACKET_ID: u8 = 1; +pub const CONNECTION_SHUTDOWN_PACKET_ID: u8 = 2; +pub const TEXT_PACKET_ID: u8 = 3; +pub const FILEINFO_PACKET_ID: u8 = 4; +pub const FILEDATA_PACKET_ID: u8 = 5; + +// File chunk size +pub const CHUNK_SIZE: usize = 262_144; // 256 KB + +// Encryption types +pub const ECRYPTION_XOR: u8 = 1; + #[derive(PartialEq, Eq)] pub enum PacketType { ConnectionShutdown, TextData, FileInfo, FileData, + Handshake, } pub trait Packet { @@ -104,11 +121,11 @@ pub fn read_next_packet(conn: &mut net::TcpStream) -> Result, Er let packet_type_byte: u8 = u8::from_be_bytes([packet_bytes[0]]); match packet_type_byte { - constants::CONNECTION_SHUTDOWN_PACKET_ID => { + CONNECTION_SHUTDOWN_PACKET_ID => { return Ok(Box::new(ConnectionShutdownPacket{})); } - constants::TEXT_PACKET_ID => { + TEXT_PACKET_ID => { let mut new_text_packet: TextPacket = TextPacket::empty(); let result = new_text_packet.from_bytes(packet_bytes); match result { @@ -122,7 +139,7 @@ pub fn read_next_packet(conn: &mut net::TcpStream) -> Result, Er } } - constants::FILEINFO_PACKET_ID => { + FILEINFO_PACKET_ID => { let mut new_fileinfo_packet: FileInfoPacket = FileInfoPacket::empty(); let result = new_fileinfo_packet.from_bytes(packet_bytes); match result { @@ -136,7 +153,7 @@ pub fn read_next_packet(conn: &mut net::TcpStream) -> Result, Er } } - constants::FILEDATA_PACKET_ID => { + FILEDATA_PACKET_ID => { let mut new_filedata_packet: FileDataPacket = FileDataPacket::empty(); let result = new_filedata_packet.from_bytes(packet_bytes); match result {