Begining of some test cases.
authorUmmon <greg.burri@gmail.com>
Sun, 2 Nov 2014 17:07:54 +0000 (18:07 +0100)
committerUmmon <greg.burri@gmail.com>
Sun, 2 Nov 2014 17:07:54 +0000 (18:07 +0100)
Cargo.lock
src/client.rs [deleted file]
src/crypto.rs
src/end_point.rs
src/main.rs
src/packet.rs
src/server.rs [deleted file]

index fb3c642..dd6deb9 100644 (file)
@@ -2,11 +2,11 @@
 name = "lab1_rust"
 version = "0.0.1"
 dependencies = [
- "openssl 0.0.0 (git+https://github.com/sfackler/rust-openssl.git#b41201c3c9fddf42d02f71d11b5d4718eb9e88c4)",
+ "openssl 0.0.0 (git+https://github.com/sfackler/rust-openssl.git#1e706b8ef4d4344b5297b90bcd430cc96f40fb39)",
 ]
 
 [[package]]
 name = "openssl"
 version = "0.0.0"
-source = "git+https://github.com/sfackler/rust-openssl.git#b41201c3c9fddf42d02f71d11b5d4718eb9e88c4"
+source = "git+https://github.com/sfackler/rust-openssl.git#1e706b8ef4d4344b5297b90bcd430cc96f40fb39"
 
diff --git a/src/client.rs b/src/client.rs
deleted file mode 100644 (file)
index 14df2d4..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-use std::io::{ TcpStream, IoResult };
-use packet::{ Command, Packet };
-
-pub struct Client {
-   socket: TcpStream,
-   current_timestamp: u64
-}
-
-impl Client {
-   pub fn new(address: &str, port: u16) -> IoResult<Client> {
-      Ok(Client { 
-         socket: try!(TcpStream::connect(address, port)),
-         current_timestamp: 0
-      })
-   }
-   
-   pub fn close(&mut self) -> IoResult<()> {
-      try!(self.socket.close_read());
-      try!(self.socket.close_write());
-      Ok(())
-   }
-   
-   pub fn start_tests(&mut self) {
-      self.current_timestamp += 1;
-      let timestamp = self.current_timestamp;
-      self.send(Packet { timestamp: timestamp, t: Command(Packet::random_packet_data()) });
-   }
-   
-   fn send(&mut self, p: Packet) {
-      println!("Client sends packet: {}", p);
-      match p.write(&mut self.socket) {
-         Err(e) => println!("Error on sending: {}", e),
-         _ => ()
-      }
-   }
-}
\ No newline at end of file
index ce301ab..60c205b 100644 (file)
@@ -25,7 +25,7 @@ pub fn decrypt(cypherdata: &[u8], iv: &[u8]) -> Vec<u8> {
 }
 
 pub fn generate_key(size_byte: uint) -> IoResult<Vec<u8>>  {
-   let mut bytes = Vec::from_elem(size_byte, 0u8);
+   let mut bytes = Vec::      from_elem(size_byte, 0u8);
    let mut generator = try!(OsRng::new()); // Uses '/dev/urandom' on Unix-like systems.
    generator.fill_bytes(bytes.as_mut_slice_());
    Ok(bytes)
index 9488d13..77af33d 100644 (file)
@@ -1,8 +1,10 @@
-use std::io::{ Acceptor, Listener, TcpStream, IoResult, IoError, EndOfFile };
+use std::io::{ MemWriter, Acceptor, Listener, TcpStream, IoResult, IoError, EndOfFile };
 use std::io::net::tcp::{ TcpAcceptor, TcpListener };
 use packet;
 use packet::{ Packet, Command, Answer, Error, ReadingResult, PacketType };
 
+static DEFAULT_TIMEOUT: Option<u64> = Some(500); // [ms].
+
 pub struct Server {
    acceptor: TcpAcceptor
 }
@@ -17,8 +19,8 @@ struct EndPoint {
 }
 
 impl Server {
-   pub fn new(port: u16) -> IoResult<Server> {
-      let mut acceptor = try!(TcpListener::bind("127.0.0.1", port).listen());
+   pub fn new(interface: &str, port: u16) -> IoResult<Server> {
+      let mut acceptor = try!(TcpListener::bind(interface, port).listen());
       
       let server = Server { 
          acceptor: acceptor.clone()
@@ -49,7 +51,7 @@ impl Server {
          match end_point.read() {         
             Ok(packet@Packet { t: Command(..), .. }) => {
                println!("[Server] Valid command received: {}", packet);
-               let answer = Answer(Packet::random_packet_data());
+               let answer = Answer(Packet::random_packet_data([]));
                match end_point.send(answer.clone()) {
                   Ok(_) =>
                      println!("[Server] Answer sent: {}", answer),
@@ -100,6 +102,71 @@ impl Client {
             println!("[Client] Cannot send this type of packet: {}", other)
       }
    }
+   
+   /// Send some valid and not-valid packets to the server and check the result.
+   pub fn start_tests(address: &str, port: u16) {
+      let execute = |f: &mut |&mut Client| -> bool| -> bool {
+         match Client::new(address, port) {
+            Ok(ref mut client) => (*f)(client),
+            Err(e) => {
+               println!("Unable to create a client. Error: {}", e);
+               false
+            }
+         }
+      };
+   
+      let mut tests = vec!(
+         |client: &mut Client| -> bool {
+            println!("Send a valid packet...");
+            match client.end_point.send_with_result(Command(Packet::random_packet_data([42]))) {
+               Ok(Ok(packet@Packet { t: Answer(..), .. })) =>
+                  true,
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+         
+         |client: &mut Client| -> bool {
+            println!("Send a packet with an unknown type...");
+            client.end_point.current_timestamp += 1;
+            let mut m = MemWriter::new(); 
+            match (Packet { t: Command(Packet::random_packet_data([42])), timestamp: client.end_point.current_timestamp }).write(&mut m) {
+               Err(e) => {
+                  println!("Error: {}", e)
+                  false
+               },
+               _ => {
+                  let mut raw_packet = m.unwrap();
+                  raw_packet[2] = 0xEE; // Alter the type.
+                        
+                  match client.end_point.send_raw_with_result(raw_packet.as_slice()) {
+                     Ok(Err(packet::IOReadError( IoError { kind: TimedOut, .. }))) => true, // The server should not send a packet.
+                     other => {
+                        println!("Error: {}", other);
+                        false
+                     }
+                  }                  
+               }
+            }
+         }
+      );
+      
+      for (i, test) in range(0, tests.len()).zip(tests.iter_mut()) {
+         println!("===== Test case #{}:", i)
+         if execute(test) {
+            println!("===== Test passed");
+         } else {
+            println!("===== Test failed");
+            break;
+         }
+      }     
+      
+      // Send a packet with a wrong timestamp.
+      // Send a packet with a maximum timestamp.
+      // Send a packet with altered encrypted data.
+   }
 }
 
 impl EndPoint {   
@@ -109,9 +176,7 @@ impl EndPoint {
       Ok(())
    }
    
-   /**
-     * Send a packet and wait synchronously an answer.
-     */ 
+   /// Send a packet and wait for an answer synchronously.
    fn send_with_result(&mut self, p: PacketType) -> IoResult<ReadingResult> {
       match self.send(p) {
          Err(e) => Err(e),
@@ -119,11 +184,22 @@ impl EndPoint {
       }
    }
    
+   /// Send arbitrary data and wait for an answer synchronously.
+   /// Do not increment the current timestamp.
+   fn send_raw_with_result(&mut self, p: &[u8]) -> IoResult<ReadingResult> {
+      self.socket.set_timeout(DEFAULT_TIMEOUT);
+      match self.socket.write(p) {
+         Err(e) => Err(e),
+         _ => Ok(self.read())
+      }
+   }
+   
    fn send(&mut self, p: PacketType) -> IoResult<()> {
+      self.socket.set_timeout(DEFAULT_TIMEOUT);
       self.current_timestamp += 1;
       match (Packet { t: p, timestamp: self.current_timestamp }).write(&mut self.socket) {
-         Ok(_) => Ok(()),
-         Err(packet::WriteIOError(e)) => Err(e)
+         Err(packet::WriteIOError(e)) => Err(e),
+         _ => Ok(())
       }
    }
    
@@ -135,11 +211,12 @@ impl EndPoint {
          };
       };
    
+      self.socket.set_timeout(DEFAULT_TIMEOUT);      
       match Packet::read(&mut self.socket) {
          Ok(packet) => {
             if packet.timestamp <= self.current_timestamp {
                println!("Error, timestamp mismatch, current timestamp: {}, packet received: {}", self.current_timestamp, packet);
-               Err(packet::InvalidTimestamp)
+               Err(packet::InvalidTimestampError)
             } else {
                self.current_timestamp = packet.timestamp + 1;
                Ok(packet)
@@ -157,4 +234,4 @@ impl EndPoint {
             other
       }
    }
-}
\ No newline at end of file
+}
index fbffa21..5f2fdf9 100644 (file)
@@ -1,14 +1,16 @@
 #![feature(macro_rules)]
 
 extern crate openssl;
+extern crate serialize;
 
 use std::io;
 use std::os;
 
+use end_point::{ Client, Server };
+
 mod crypto;
 mod packet;
-mod client;
-mod server;
+mod end_point;
 
 /*
 TODO
@@ -32,28 +34,18 @@ fn main() {
          Ok(key) => println!("key: {}", key),
          Err(e) => println!("Unable to generate a key. Error: {}", e)
       }
-   } else {      
-      println!("Starting server.., Press any key to quit");
+   } else {
+      println!("Starting server..., Press any key to quit");
       
-      match server::Server::new(PORT) {
+      match Server::new("::1", PORT) {
          Ok(mut server) => {
             println!("Server started");
-            
-            match client::Client::new("127.0.0.1", PORT) {
-               Ok(mut client) => {
-                  client.start_tests();
-                  let _ = client.close();
-               },
-               Err(e) => { 
-                  println!("Unable to create a client. Error: {}", e);
-                  return
-               }
-            }         
-            
+            Client::start_tests("::1", PORT);            
             io::stdin().read_line().ok().expect("Failed to read line");         
             server.close().ok().expect("Failed to close the server");
          },
-         Err(e) => println!("Unable to create a new server. Error: {}", e)
+         Err(e) =>
+            println!("Unable to create a new server. Error: {}", e)
       }
    }
 }
index 49ae2be..74bf102 100644 (file)
@@ -1,20 +1,22 @@
 use std::io;
 use std::fmt;
-use std::rand::{ random, task_rng, distributions };
+use std::rand::{ Rng, StdRng, SeedableRng, distributions };
 use std::rand::distributions::IndependentSample;
+use serialize::hex::{ ToHex };
 use crypto;
 
 // There are all the errors that may occur when reading an encrypted and authenticated packet.
 #[deriving(Show)]
 pub enum ReadingError {
    IOReadError(io::IoError),
-   UnknownPacketTypeReadError, // If the first byte is unknown.
+   UnknownPacketTypeError, // If the first byte is unknown.
    UnconsistentEncryptedSizeError, 
    UnconsistentDataSizeError, // The data size is not valid.
    UnconsistentMACSizeError, // The MAC hasn't the correct size.
    MACMismatchError, // The uncrypted received data doesn't match to the received MAC. 
    PaddingError, // Padding format error.   
    DataError, // The data are invalid.
+   InvalidTimestampError
 }
 
 // A macro to return a 'IOReadError' in case of error.
@@ -41,53 +43,74 @@ static MIN_PAYLOAD_SIZE: uint = 7;
 static MAX_PAYLOAD_SIZE: uint = 39;
 static FIXED_PACKET_SIZE: uint = 1 + 8 + 10; // Packet type + timestamp + MAC.
 
-#[deriving(Show)]
+#[deriving(Show, Clone)]
 pub struct PacketData {
    id: u8,
    payload: Vec<u8> // The size can vary from 'MIN_PAYLOAD_SIZE' to 'MAX_PAYLOAD_SIZE' bytes.
 }
 
-#[deriving(Show)]
+#[deriving(Show, Clone)]
 pub enum ErrorType {
    CryptError,
    AuthError
 }
 
-#[deriving(Show)]
+#[deriving(Clone)]
 pub enum PacketType {
    Command(PacketData),
    Answer(PacketData),
    Error(ErrorType),
 }
 
+/// Serialized packet format : |LL|P|TTTTTTTT|D...D|MMMMMMMMMM|
+/// Where:
+///   LL: Size on the following data
+///   P: Packet type:
+///      0x00: Command
+///      OxFF: Answer
+///      0x0A: Decrypt error
+///      0x0B: Authentication error
+///   TTTTTTTT: Timestamp (64 bits)
+///      D...D: Encrypted data (AES-256 CBC mode) of:
+///         |I|C...C|P...P| for command and answer packet:
+///            I: Command ID
+///            C: Command payload (from 7 to 39 bytes)
+///            P: Padding from 1 to 16, |I|C...C|P...P| size must be a multiple of 16
+///         |"0000000000000000"| for error packet (16 bytes length)
+///   MMMMMMMMMM: first 10 bytes (most significant) of the HMAC-SHA256 of:
+///      |I|C...C| for command ans answer packet
+///      |"0000000000"| for error packet
+#[deriving(Show)]
 pub struct Packet {
    pub t: PacketType,
    pub timestamp: u64
 }
 
-impl fmt::Show for Packet {
+impl fmt::Show for PacketType {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
       fn data_to_str(data: &PacketData) -> String {
-         format!("id: {}, payload({}): {}", data.id, data.payload.len(), data.payload)
+         format!("id: {}, payload({}): \"{}\"", data.id, data.payload.len(), data.payload.as_slice().to_hex())
       }
-      match self.t {
-         Command(ref data) => write!(formatter, "Command packet {{ timestamp: {}, {} }}", self.timestamp, data_to_str(data)),
-         Answer(ref data) => write!(formatter, "Answer packet {{ timestamp: {}, {} }}", self.timestamp, data_to_str(data)),
-         Error(error_type) => write!(formatter, "Error packet {{ timestamp: {}, errorType: {} }}", self.timestamp, error_type)
+      match self {
+         &Command(ref data) => write!(formatter, "Command {{ {} }}", data_to_str(data)),
+         &Answer(ref data) => write!(formatter, "Answer {{ {} }}", data_to_str(data)),
+         &Error(error_type) => write!(formatter, "Error {{ errorType: {} }}", error_type)
       }
    }
 }
 
 impl Packet {
-   pub fn random_packet_data() -> PacketData {        
-      let mut rng = task_rng();
+   pub fn random_packet_data(seed: &[uint]) -> PacketData {
+      let mut rng = if seed.is_empty() { StdRng::new().unwrap() } else { SeedableRng::from_seed(seed) };
+      let mut payload = Vec::from_elem(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), 0u8);
+      rng.fill_bytes(payload.as_mut_slice_());
       PacketData { 
-         id: random::<u8>(),
-         payload: Vec::from_fn(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), |_| random::<u8>())
+         id: rng.gen::<u8>(),
+         payload: payload
       }
    }
 
-   pub fn write(&self, output: &mut io::Writer) ->  WritingResult {
+   pub fn write(&self, output: &mut io::Writer) -> WritingResult {
       fn packet_data(p: &PacketData) -> Vec<u8> {
          let mut d = Vec::new();
          d.push(p.id);
@@ -143,8 +166,19 @@ impl Packet {
    }
    
    pub fn read(input: &mut io::Reader) -> ReadingResult {
+      fn consume(input: &mut io::Reader, nb_byte: uint) {
+         let _ = input.read_exact(nb_byte);        
+      }
+   
       let data_size = try_read_io!(input.read_be_u16());
-      let packet_type = try_read_io!(input.read_u8());
+     
+      // Read and check the packet type.
+      let packet_type = try_read_io!(input.read_u8());            
+      if ![0x00, 0xFF, 0x0A, 0x0B].iter().any(|p| *p == packet_type) {
+         consume(input, data_size as uint - 1);
+         return Err(UnknownPacketTypeError)
+      }      
+      
       let timestamp = try_read_io!(input.read_be_u64());
       
       let mut encrypted_data = Vec::from_elem(data_size as uint - FIXED_PACKET_SIZE, 0u8);      
@@ -180,23 +214,23 @@ impl Packet {
       
       Ok(Packet {
          t: match packet_type {
-            0x00 | 0xFF => {     
+            // Command or answer.
+            0x00 | 0xFF => {
                if data.len() < MIN_PAYLOAD_SIZE + 1 || data.len() > MAX_PAYLOAD_SIZE + 1 {
                   return Err(UnconsistentDataSizeError)
                }
                let pd = PacketData { id: data[0], payload: data.tail().to_vec() }; // match data.as_slice() { [id, payload..] => PacketData { id: id, payload: payload.to_vec() } };
                match packet_type { 0x00 => Command(pd), _ => Answer(pd) }
             },
-            0x0A | 0x0B => { 
+            // Error.
+            _ => { 
                if data.len() != 16 {
                   return Err(UnconsistentDataSizeError)
                } else if data != Vec::from_elem(16, '0' as u8) {               
                   return Err(DataError)
                }
                match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) }               
-            },
-            _ => 
-               return Err(UnknownPacketTypeReadError)
+            }
          },
          timestamp: timestamp
       })
diff --git a/src/server.rs b/src/server.rs
deleted file mode 100644 (file)
index b655ae7..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-use std::io::{ TcpListener, TcpStream, Acceptor, Listener, IoError, IoResult, EndOfFile };
-use std::io::net::tcp::{ TcpAcceptor };
-use packet::{ Packet, IOReadError };
-
-pub struct Server {
-   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()
-      };
-      
-      spawn(proc() {
-         loop {
-            for stream in acceptor.incoming() {
-               match stream {
-                  Err(_) => return,
-                  Ok(stream) => spawn(proc() {
-                     Server::handle_client(stream);
-                  })
-               }
-            }
-         }
-      });
-      
-      Ok(server)
-   }
-   
-   pub fn close(&mut self) -> IoResult<()> {
-      self.acceptor.close_accept()
-   }
-   
-   fn handle_client(mut stream: TcpStream) { 
-      let mut current_timestamp = 0u64;
-      
-      loop {
-         // stream.set_timeout(Some(1000000000));         
-         match Packet::read(&mut stream) {
-            Ok(packet) => {
-               if packet.timestamp <= current_timestamp {
-                  println!("Error, timestamp mismatch, current timestamp: {}, packet received: {}", current_timestamp, packet);
-               } else {
-                  current_timestamp += 1;
-                  println!("Receive packet: {}", packet);
-               }
-            },
-            // Socket has been closed.
-            Err(IOReadError(IoError { kind: EndOfFile, .. })) => {
-               return;
-            },
-            Err(e) => {
-               println!("Read error: {}", e)
-            }
-         }
-      }
-   }
-}
\ No newline at end of file