Read and writing of packet, work in progress.
authorUmmon <greg.burri@gmail.com>
Tue, 28 Oct 2014 22:14:17 +0000 (23:14 +0100)
committerUmmon <greg.burri@gmail.com>
Tue, 28 Oct 2014 22:14:17 +0000 (23:14 +0100)
src/client.rs
src/command.rs
src/crypto.rs
src/main.rs
src/server.rs

index 8392460..575df05 100644 (file)
@@ -1,10 +1,8 @@
 use std::io::{ TcpStream, IoResult };
 use command::{ Command, Packet, Error };
-use crypto::Crypto;
 
 pub struct Client {
    socket: TcpStream,
-   crypto: Crypto,
    current_timestamp: u64
 }
 
@@ -12,8 +10,7 @@ impl Client {
    pub fn new(address: &str, port: u16) -> IoResult<Client> {
       Ok(Client { 
          socket: try!(TcpStream::connect(address, port)),
-         current_timestamp: 0,
-         crypto: Crypto::new()
+         current_timestamp: 0
       })
    }
    
@@ -24,7 +21,7 @@ impl Client {
    
    pub fn start_tests(&mut self) {
       
-      let command = Packet::newRandomCommand(self.current_timestamp);      
+      let command = Packet::new_random_command(self.current_timestamp);      
       self.send(command);
       
    }
index 7de2afc..31b7235 100644 (file)
@@ -1,33 +1,46 @@
 use std::io;
 use std::rand::{ random, task_rng, distributions };
 use std::rand::distributions::IndependentSample;
+use crypto;
 
 pub enum ReadingError {
-   ReadIOError(io::IoError)
-   // TODO...
+   IOReadError(io::IoError),
+   UnknownPacketTypeReadError, // If the first byte is unknown.
+   UnconsistentDataSizeError, // The payload is too small or too big.
 }
 
+macro_rules! try_read_io(
+   ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(IOReadError(e)) })
+)
+
 pub enum WritingError {
    WriteIOError(io::IoError)
    // TODO...
 }
 
+macro_rules! try_write_io(
+   ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(WriteIOError(e)) })
+)
+
 pub type ReadingResult = Result<Packet, ReadingError>;
 pub type WritingResult = Result<(), WritingError>;
 
-pub enum PacketType {
-   CommandType,
-   AnswerType
-}
+static MIN_PAYLOAD_SIZE: uint = 6;
+static MAX_PAYLOAD_SIZE: uint = 38;
 
 pub struct CommandPacket {
-   t: PacketType,
    timestamp: u64,
-   commandID: u8,
-   commandPayload: Vec<u8>
+   id: u8,
+   payload: Vec<u8> // May vary from 'MIN_PAYLOAD_SIZE' to 'MAX_PAYLOAD_SIZE' bytes.
+}
+
+pub enum ErrorType {
+   DecryptError,
+   AuthError
 }
 
 pub struct ErrorPacket {
+   t: ErrorType,
    timestamp: u64
 }
 
@@ -37,33 +50,107 @@ pub enum Packet {
 }
 
 impl Packet {
-   pub fn newRandomCommand(timestamp: u64) -> Packet {     
-         
+   pub fn new_random_command(timestamp: u64) -> Packet {        
       let mut rng = task_rng();
       Command(CommandPacket {
-         t: CommandType,
-         timestamp: timestamp, 
-         commandID: random::<u8>(),
-         commandPayload: Vec::from_fn(distributions::Range::new(7u, 40u).ind_sample(&mut rng), |idx| 0u8)
+         timestamp: timestamp,
+         id: random::<u8>(),
+         payload: Vec::from_fn(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), |idx| random::<u8>())
       })
    }
 
-   pub fn write(&self, output: &mut io::Writer) ->  WritingResult {
-      
+   pub fn write(&self, output: &mut io::Writer) ->  WritingResult {            
       match self {
          &Command(ref command) => {
-            output.write_u8(0);
-            output.write_be_u64(command.timestamp);
+            // Data.
+            let mut data = Vec::with_capacity(command.payload.len() + 1);
+            data.push(command.id);
+            data.push(command.payload.len() as u8);
+            data.push_all(command.payload.as_slice());
+                        
+            // Padding.
+            let padding_size = if data.len() % 16 == 0 { 16 } else { data.len() % 16 } ;
+            let padding = Vec::from_elem(padding_size, padding_size as u8);
+            data.push_all(padding.as_slice());
+            
+            // Encrypt.
+            let encrypted_data = crypto::encrypt(data.as_slice(), iv_from_timestamp(command.timestamp).as_slice());
+            
+            // Data size.
+            try_write_io!(output.write_be_u16(encrypted_data.len() as u16));
+         
+            // Packet type.
+            try_write_io!(output.write_u8(0));
+            
+            // Timestamp.
+            try_write_io!(output.write_be_u64(command.timestamp));
             
+            // Write encrypted data.
+            try_write_io!(output.write(encrypted_data.as_slice()));
             
+            // MAC.
+            try_write_io!(output.write(crypto::compute_mac(data.as_slice())));
          },
-         &Error(error) => ()
+         &Error(error) => {            
+            // Padding as data: 16 * '0'.
+            let padding = Vec::from_elem(16, '0' as u8);
+            let encrypted_data = crypto::encrypt(padding.as_slice(), iv_from_timestamp(error.timestamp).as_slice());
+           
+            // Data size.
+            try_write_io!(output.write_be_u16(encrypted_data.len() as u16));
+            
+            // Error type.
+            try_write_io!(match error.t {
+               DecryptError => output.write_u8(0x0A),
+               AuthError => output.write_u8(0x0B)
+            });
+            
+            // Timestamp.
+            try_write_io!(output.write_be_u64(error.timestamp));
+            
+            // Encrypt the padding.
+            try_write_io!(output.write(encrypted_data.as_slice()));
+            
+            // MAC.
+            try_write_io!(output.write(crypto::compute_mac(padding.as_slice())));
+         }
       }
    
       Ok(())
    }
    
    pub fn read(input: &mut io::Reader) -> ReadingResult {
-      Ok(Packet::newRandomCommand(0))
+      let data_size = try_read_io!(input.read_be_u16());
+   
+      match try_read_io!(input.read_byte()) {
+         0 => {
+            let mut command = CommandPacket { timestamp: try_read_io!(input.read_be_u64()), id: 0, payload: vec!() };
+            let mut encrypted_data = Vec::from_elem(data_size as uint, 0u8);
+            if try_read_io!(input.read(encrypted_data.as_mut_slice_())) != data_size as uint {
+               return Err(IOReadError(io::IoError { kind: io::EndOfFile, desc: "Unable to read all encrypted data", detail: None }));
+            }
+            
+            let data = crypto::decrypt(encrypted_data.as_slice(), iv_from_timestamp(command.timestamp).as_slice());            
+            if data.len() < MIN_PAYLOAD_SIZE + 2 || data.len() > MAX_PAYLOAD_SIZE + 2 {
+               return Err(UnconsistentDataSizeError)
+            }           
+
+            Ok(Command(command))
+         },
+         t if t == 0x0A || t == 0x0B  => {
+            let mut error = ErrorPacket { t: if t == 0x0A { DecryptError } else { AuthError }, timestamp: try_read_io!(input.read_be_u64()) };
+            // TODO
+            Ok(Error(error))
+         },
+         _ => Err(UnknownPacketTypeReadError),
+      }
    }
-}
\ No newline at end of file
+}
+
+fn iv_from_timestamp(t: u64) -> Vec<u8> {
+   // Initialization vector = 64 * 0u8 + timestamp.
+   let mut iv = io::MemWriter::with_capacity(16);
+   iv.write_be_u64(0u64);
+   iv.write_be_u64(t);
+   iv.unwrap()
+}
index 1e1ddc1..ce301ab 100644 (file)
@@ -1,48 +1,32 @@
-use std::rand::OsRng;
-use std::vec::Vec;
+use std::rand::{ OsRng, Rng };
 use std::io::IoResult;
-use std::rand::Rng;
-
+use std::slice::bytes::copy_memory;
 use openssl::crypto::hash::SHA256;
 use openssl::crypto::hmac::HMAC;
-use std::slice::bytes::copy_memory;
-
-//const KEY_A: &'static [u8] = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d".from_hex().unwrap();
-//static KEY_A: Vec<u8> = vec![1, 2, 3]; // "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d".from_hex().unwrap();
-
-pub struct Crypto {
-   key_a: Vec<u8>,
-   key_c: Vec<u8>
-}
+use openssl::crypto::symm;
 
-impl Crypto {
-   pub fn new() -> Crypto {
-      Crypto {
-         key_a: [125, 31, 131, 118, 143, 180, 252, 53, 211, 217, 79, 240, 128, 91, 252, 87, 104, 236, 145, 198, 163, 203, 161, 12, 53, 56, 218, 40, 221, 95, 171, 140].to_vec(),
-         key_c: [75, 226, 88, 31, 223, 216, 182, 216, 178, 58, 59, 193, 245, 80, 254, 128, 125, 246, 246, 224, 194, 190, 123, 123, 10, 131, 217, 183, 112, 157, 166, 102].to_vec()
-      }
-   }
+static KEY_A: &'static [u8] = [125, 31, 131, 118, 143, 180, 252, 53, 211, 217, 79, 240, 128, 91, 252, 87, 104, 236, 145, 198, 163, 203, 161, 12, 53, 56, 218, 40, 221, 95, 171, 140];
+static KEY_C: &'static [u8] = [75, 226, 88, 31, 223, 216, 182, 216, 178, 58, 59, 193, 245, 80, 254, 128, 125, 246, 246, 224, 194, 190, 123, 123, 10, 131, 217, 183, 112, 157, 166, 102];
 
-   pub fn compute_mac(&self, data: &[u8]) -> [u8, ..10] {
-      let mut hmac = HMAC(SHA256, self.key_a.as_slice());
-      hmac.update(data);
-      let mut result = [0u8, ..10];
-      copy_memory(&mut result, hmac.finalize().slice(0, 9));
-      result
-   }
+pub fn compute_mac(data: &[u8]) -> [u8, ..10] {
+   let mut hmac = HMAC(SHA256, KEY_A);
+   hmac.update(data);
+   let mut result = [0u8, ..10];
+   copy_memory(&mut result, hmac.finalize().slice(0, 9));
+   result
 }
 
-pub fn encrypt(plaindata: &Vec<u8>) -> Vec<u8> {
-   vec!()
+pub fn encrypt(plaindata: &[u8], iv: &[u8]) -> Vec<u8> {
+   symm::encrypt(symm::AES_256_CBC, KEY_C, iv.to_vec(), plaindata)
 }
 
-pub fn decrypt(cypherdata: &Vec<u8>) -> Vec<u8> {
-   vec!()
+pub fn decrypt(cypherdata: &[u8], iv: &[u8]) -> Vec<u8> {
+   symm::decrypt(symm::AES_256_CBC, KEY_C, iv.to_vec(), cypherdata)
 }
 
 pub fn generate_key(size_byte: uint) -> IoResult<Vec<u8>>  {
    let mut bytes = Vec::from_elem(size_byte, 0u8);
-   let mut generator = try!(OsRng::new());
+   let mut generator = try!(OsRng::new()); // Uses '/dev/urandom' on Unix-like systems.
    generator.fill_bytes(bytes.as_mut_slice_());
    Ok(bytes)
 }
\ No newline at end of file
index e64323f..cec3957 100644 (file)
@@ -1,3 +1,5 @@
+#![feature(macro_rules)]
+
 extern crate openssl;
 
 use std::io;
index 3eb1a46..1ce5f95 100644 (file)
@@ -1,19 +1,17 @@
 use std::io::{ TcpListener, TcpStream, Acceptor, Listener, IoError, IoResult };
 use std::io::net::tcp::{ TcpAcceptor };
 use command::{ Command, Packet, Error };
-use crypto::Crypto;
 
 pub struct Server {
-   acceptor: TcpAcceptor,
-   crypto: Crypto
+   acceptor: TcpAcceptor
 }
 
 impl Server {
    pub fn new(port: u16) -> IoResult<Server> {
       let mut acceptor = try!(TcpListener::bind("127.0.0.1", port).listen());
+      
       let server = Server { 
-         acceptor: acceptor.clone(),  
-         crypto: Crypto::new()
+         acceptor: acceptor.clone()
       };
       
       spawn(proc() {
@@ -37,8 +35,9 @@ impl Server {
    }
    
    fn handle_client(mut stream: TcpStream) {   
-      println!("new connection!");
+      
       let mut current_timestamp = 0u64;
+      
       match Packet::read(&mut stream) {
          Ok(Command(packet)) => println!("CommandPacket"),
          Ok(Error(packet)) => println!("AnswerPacket"),