Add some tests.
authorUmmon <greg.burri@gmail.com>
Sun, 2 Nov 2014 22:29:24 +0000 (23:29 +0100)
committerUmmon <greg.burri@gmail.com>
Sun, 2 Nov 2014 22:29:24 +0000 (23:29 +0100)
src/end_point.rs
src/main.rs
src/packet.rs

index 77af33d..199fff7 100644 (file)
@@ -21,11 +21,11 @@ struct EndPoint {
 impl Server {
    pub fn new(interface: &str, port: u16) -> IoResult<Server> {
       let mut acceptor = try!(TcpListener::bind(interface, port).listen());
-      
-      let server = Server { 
+
+      let server = Server {
          acceptor: acceptor.clone()
       };
-      
+
       spawn(proc() {
          loop {
             for stream in acceptor.incoming() {
@@ -38,17 +38,17 @@ impl Server {
             }
          }
       });
-      
+
       Ok(server)
    }
-   
+
    pub fn close(&mut self) -> IoResult<()> {
       self.acceptor.close_accept()
    }
-   
-   fn handle_client(mut end_point: EndPoint) {      
+
+   fn handle_client(mut end_point: EndPoint) {
       loop {
-         match end_point.read() {         
+         match end_point.read() {
             Ok(packet@Packet { t: Command(..), .. }) => {
                println!("[Server] Valid command received: {}", packet);
                let answer = Answer(Packet::random_packet_data([]));
@@ -67,7 +67,7 @@ impl Server {
             other =>
                println!("[Server] Error or invalid packet: {}", other)
          }
-      }         
+      }
    }
 }
 
@@ -78,11 +78,11 @@ impl Client {
          current_timestamp: 0
       }})
    }
-   
+
    pub fn close(&mut self) -> IoResult<()> {
       self.end_point.close()
    }
-   
+
    pub fn send(&mut self, packet: PacketType) {
       match packet {
          Command(_) => {
@@ -102,7 +102,7 @@ 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 {
@@ -114,68 +114,140 @@ impl Client {
             }
          }
       };
-   
+
+      fn raw_packet(timestamp: u64) -> Vec<u8> {
+         let mut m = MemWriter::new();
+         match (Packet { t: Command(Packet::random_packet_data([42])), timestamp: timestamp }).write(&mut m) {
+            Err(e) => vec!(),
+            _ => m.unwrap()
+         }
+      }
+
       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,
+               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)
+            let mut raw_packet = raw_packet(client.end_point.current_timestamp);
+            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, // OK: the server should not send any packet.
+               other => {
+                  println!("Error: {}", other);
                   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
-                     }
-                  }                  
                }
             }
-         }
+         },
+
+         |client: &mut Client| -> bool {
+            println!("Send a packet with an old timestamp...");
+            client.end_point.current_timestamp += 1;
+            let raw_packet = raw_packet(0);
+            match client.end_point.send_raw_with_result(raw_packet.as_slice()) {
+               Ok(Err(packet::IOReadError( IoError { kind: TimedOut, .. }))) => true, // OK: the server should not send any packet.
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+
+         |client: &mut Client| -> bool {
+            println!("Send a packet with altered crypted data...");
+            client.end_point.current_timestamp += 1;
+            let mut raw_packet = raw_packet(client.end_point.current_timestamp);
+            raw_packet[11] = 0xDE;
+            raw_packet[12] = 0xAD;
+            raw_packet[13] = 0xBE;
+            raw_packet[14] = 0xEF;
+            match client.end_point.send_raw_with_result(raw_packet.as_slice()) {
+               Ok(Ok(packet@Packet { t: Error(packet::AuthError), .. })) => true,
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+
+         |client: &mut Client| -> bool {
+            println!("Send a packet with too small data...");
+            let mut command = Command(Packet::new_packet_data(0, Vec::from_elem(6, 0x00)));
+            match client.end_point.send_with_result(command) {
+               Ok(Err(packet::IOReadError( IoError { kind: TimedOut, .. }))) => true, // OK: the server should not send any packet.
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+
+         |client: &mut Client| -> bool {
+            println!("Send a packet with too large data...");
+            let mut command = Command(Packet::new_packet_data(0, Vec::from_elem(40, 0x00)));
+            match client.end_point.send_with_result(command) {
+               Ok(Err(packet::IOReadError( IoError { kind: TimedOut, .. }))) => true, // OK: the server should not send any packet.
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+
+         |client: &mut Client| -> bool {
+            println!("Send a packet with wrong padding...");
+            client.end_point.current_timestamp += 1;
+            let mut m = MemWriter::new();
+            let raw_packet = match (Packet { t: Command(Packet::random_packet_data([42])), timestamp: client.end_point.current_timestamp }).write_with_padding_fun(&mut m, |_, _| -> u8 { 0 }) {
+               Err(e) => vec!(),
+               _ => m.unwrap()
+            };
+
+            match client.end_point.send_raw_with_result(raw_packet.as_slice()) {
+               Ok(Ok(packet@Packet { t: Error(packet::CryptError), .. })) => true,
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
       );
-      
-      for (i, test) in range(0, tests.len()).zip(tests.iter_mut()) {
+
+      let mut nb_test_passed = 0;
+      for (i, test) in range(1, tests.len()+1).zip(tests.iter_mut()) {
          println!("===== Test case #{}:", i)
          if execute(test) {
+            nb_test_passed += 1;
             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.
+      }
+
+      if nb_test_passed == tests.len() {
+         println!("All tests passed");
+      } else {
+         println!("#{} test(s) failed", tests.len() - nb_test_passed);
+      }
    }
 }
 
-impl EndPoint {   
+impl EndPoint {
    fn close(&mut self) -> IoResult<()> {
       try!(self.socket.close_read());
       try!(self.socket.close_write());
       Ok(())
    }
-   
+
    /// Send a packet and wait for an answer synchronously.
    fn send_with_result(&mut self, p: PacketType) -> IoResult<ReadingResult> {
       match self.send(p) {
@@ -183,7 +255,7 @@ impl EndPoint {
          _ => Ok(self.read())
       }
    }
-   
+
    /// 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> {
@@ -193,7 +265,7 @@ impl EndPoint {
          _ => Ok(self.read())
       }
    }
-   
+
    fn send(&mut self, p: PacketType) -> IoResult<()> {
       self.socket.set_timeout(DEFAULT_TIMEOUT);
       self.current_timestamp += 1;
@@ -202,7 +274,7 @@ impl EndPoint {
          _ => Ok(())
       }
    }
-   
+
    fn read(&mut self) -> ReadingResult {
       fn send_error(ep: &mut EndPoint, error_type: packet::ErrorType) {
          match ep.send(Error(error_type)) {
@@ -210,8 +282,8 @@ impl EndPoint {
             Ok(_) => ()
          };
       };
-   
-      self.socket.set_timeout(DEFAULT_TIMEOUT);      
+
+      self.socket.set_timeout(DEFAULT_TIMEOUT);
       match Packet::read(&mut self.socket) {
          Ok(packet) => {
             if packet.timestamp <= self.current_timestamp {
@@ -223,10 +295,12 @@ impl EndPoint {
             }
          },
          e @ Err(packet::PaddingError) => {
+            self.current_timestamp += 1;
             send_error(self, packet::CryptError);
             e
          },
          e @ Err(packet::MACMismatchError) => {
+            self.current_timestamp += 1;
             send_error(self, packet::AuthError);
             e
          },
index 5f2fdf9..e9e911a 100644 (file)
@@ -12,21 +12,15 @@ mod crypto;
 mod packet;
 mod end_point;
 
-/*
-TODO
-   * Comment stocker les clefs? à quels critères doivent elle répondre?
-   * 
-*/
-
 const PORT: u16 = 4221;
 
 fn print_usage() {
    println!("{} <genkey> | <tests> | ...", os::args()[0]);
 }
 
-fn main() {   
+fn main() {
    let args = os::args();
-   
+
    if args.iter().any(|a| a.as_slice() == "--help" || a.as_slice() == "-h") {
       print_usage();
    } else if args.len() > 1 && args[1].as_slice() == "genkey" {
@@ -36,12 +30,12 @@ fn main() {
       }
    } else {
       println!("Starting server..., Press any key to quit");
-      
+
       match Server::new("::1", PORT) {
          Ok(mut server) => {
             println!("Server started");
-            Client::start_tests("::1", PORT);            
-            io::stdin().read_line().ok().expect("Failed to read line");         
+            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) =>
index 74bf102..ece32c4 100644 (file)
@@ -10,11 +10,11 @@ use crypto;
 pub enum ReadingError {
    IOReadError(io::IoError),
    UnknownPacketTypeError, // If the first byte is unknown.
-   UnconsistentEncryptedSizeError, 
+   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.   
+   MACMismatchError, // The uncrypted received data doesn't match to the received MAC.
+   PaddingError, // Padding format error.
    DataError, // The data are invalid.
    InvalidTimestampError
 }
@@ -104,94 +104,110 @@ impl Packet {
       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 { 
+      PacketData {
          id: rng.gen::<u8>(),
          payload: payload
       }
    }
 
+   pub fn new_packet_data(id: u8, payload: Vec<u8>) -> PacketData {
+      PacketData { id: id, payload: payload }
+   }
+
    pub fn write(&self, output: &mut io::Writer) -> WritingResult {
+      self.write_with_padding_fun(output, |_: uint, padding_length: uint| -> u8 {
+         padding_length as u8
+      })
+   }
+
+   /// 'padd_fun' is function to fill the padding. The first argument is the index of the current byte, starting at 0.
+   /// The second argument is the padding length.
+   pub fn write_with_padding_fun(&self, output: &mut io::Writer, padd_fun: |uint, uint| -> u8) -> WritingResult {
       fn packet_data(p: &PacketData) -> Vec<u8> {
          let mut d = Vec::new();
          d.push(p.id);
          d.push_all(p.payload.as_slice());
          d
       }
-      
-      // Data to be encrypted.    
-      let mut data = 
+
+      // Data to be encrypted.
+      let mut data =
          match self.t {
             Command(ref p) | Answer(ref p) => packet_data(p),
             Error(_) => Vec::from_elem(16, '0' as u8) // Padding as data: 16 * '0'.
          };
-         
+
       // Compute the MAC
-      let mac = crypto::compute_mac(data.as_slice());        
-   
+      let mac = crypto::compute_mac(data.as_slice());
+
       // Padding.
       match self.t {
          Command(_) | Answer(_) => {
             let padding_size = if data.len() % 16 == 0 { 16 } else { data.len() % 16 } ;
-            data.push_all(Vec::from_elem(padding_size, padding_size as u8).as_slice());
+            data.reserve_additional(padding_size);
+            for i in range(0, padding_size) {
+               data.push(padd_fun(i, padding_size));
+            }
          },
          _ => ()
       }
-         
+
       // Encrypt.
-      let encrypted_data = crypto::encrypt(data.as_slice(), iv_from_timestamp(self.timestamp).as_slice());         
-         
+      let encrypted_data = crypto::encrypt(data.as_slice(), iv_from_timestamp(self.timestamp).as_slice());
+
       // Write packet length.
       try_write_io!(output.write_be_u16((encrypted_data.len() + FIXED_PACKET_SIZE) as u16));
-      
+
       // Write packet type.
       try_write_io!(output.write_u8(
          match self.t {
             Command(_) => 0x00,
             Answer(_) => 0xFF,
             Error(CryptError) => 0x0A,
-            Error(AuthError) => 0x0B 
+            Error(AuthError) => 0x0B
          }
       ));
-      
+
       // Write timestamp.
       try_write_io!(output.write_be_u64(self.timestamp));
-      
+
       // Write encrypted data.
       try_write_io!(output.write(encrypted_data.as_slice()));
-      
+
       // Write the MAC.
       try_write_io!(output.write(mac));
-         
+
       Ok(())
    }
-   
+
    pub fn read(input: &mut io::Reader) -> ReadingResult {
       fn consume(input: &mut io::Reader, nb_byte: uint) {
-         let _ = input.read_exact(nb_byte);        
+         let _ = input.read_exact(nb_byte);
       }
-   
+
       let data_size = try_read_io!(input.read_be_u16());
-     
+
       // Read and check the packet type.
-      let packet_type = try_read_io!(input.read_u8());            
+      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);      
+
+      let mut encrypted_data = Vec::from_elem(data_size as uint - FIXED_PACKET_SIZE, 0u8);
       if try_read_io!(input.read(encrypted_data.as_mut_slice_())) != encrypted_data.len() {
          return Err(UnconsistentEncryptedSizeError)
       }
       let mut data = crypto::decrypt(encrypted_data.as_slice(), iv_from_timestamp(timestamp).as_slice());
-                     
-      // Control the size and the content of the padding then remove it.      
+
+      // Control the size and the content of the padding then remove it.
       if packet_type == 0x00 || packet_type == 0xFF {
          match data.last() {
-            Some(&padding_size) => {            
+            Some(&padding_size) => {
                if padding_size as uint > data.len() || !data.slice_from(data.len() - padding_size as uint).iter().any(|b| *b == padding_size) {
+                  consume(input, 10);
                   return Err(PaddingError)
                }
                let data_length = data.len() - padding_size as uint;
@@ -201,7 +217,7 @@ impl Packet {
                return Err(PaddingError)
          }
       }
-            
+
       // Read an verify the MAC.
       let mut mac_read = [0u8, ..10];
       if try_read_io!(input.read(mac_read)) != mac_read.len() {
@@ -211,7 +227,7 @@ impl Packet {
       if mac_read != mac_data {
          return Err(MACMismatchError)
       }
-      
+
       Ok(Packet {
          t: match packet_type {
             // Command or answer.
@@ -223,13 +239,13 @@ impl Packet {
                match packet_type { 0x00 => Command(pd), _ => Answer(pd) }
             },
             // Error.
-            _ => { 
+            _ => {
                if data.len() != 16 {
                   return Err(UnconsistentDataSizeError)
-               } else if data != Vec::from_elem(16, '0' as u8) {               
+               } else if data != Vec::from_elem(16, '0' as u8) {
                   return Err(DataError)
                }
-               match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) }               
+               match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) }
             }
          },
          timestamp: timestamp