Add the report.
authorUmmon <greg.burri@gmail.com>
Tue, 4 Nov 2014 12:58:45 +0000 (13:58 +0100)
committerUmmon <greg.burri@gmail.com>
Tue, 4 Nov 2014 12:58:45 +0000 (13:58 +0100)
18 files changed:
.gitignore
Cargo.lock [deleted file]
Cargo.toml [deleted file]
icr14_lab_01.pdf [new file with mode: 0644]
lab1_rust/Cargo.lock [new file with mode: 0644]
lab1_rust/Cargo.toml [new file with mode: 0644]
lab1_rust/src/crypto.rs [new file with mode: 0644]
lab1_rust/src/end_point.rs [new file with mode: 0644]
lab1_rust/src/main.rs [new file with mode: 0644]
lab1_rust/src/oracle_machine.rs [new file with mode: 0644]
lab1_rust/src/packet.rs [new file with mode: 0644]
rapport/main.bib [new file with mode: 0644]
rapport/main.tex [new file with mode: 0644]
src/crypto.rs [deleted file]
src/end_point.rs [deleted file]
src/main.rs [deleted file]
src/oracle_machine.rs [deleted file]
src/packet.rs [deleted file]

index 37727f9..5139597 100644 (file)
@@ -8,4 +8,12 @@
 *.exe
 
 # Generated by Cargo
 *.exe
 
 # Generated by Cargo
-/target/
+target/
+
+# Tex
+*.log
+*.aux
+*.bbl
+*.blg
+*.dvi
+*.backup
diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644 (file)
index dd6deb9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[root]
-name = "lab1_rust"
-version = "0.0.1"
-dependencies = [
- "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#1e706b8ef4d4344b5297b90bcd430cc96f40fb39"
-
diff --git a/Cargo.toml b/Cargo.toml
deleted file mode 100644 (file)
index 3f218e2..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-
-name = "lab1_rust"
-version = "0.0.1"
-authors = ["Ummon <greg.burri@gmail.com>"]
-
-
-[[bin]]
-
-name = "lab1_rust"
-
-
-[dependencies.openssl]
-
-git = "https://github.com/sfackler/rust-openssl.git"
\ No newline at end of file
diff --git a/icr14_lab_01.pdf b/icr14_lab_01.pdf
new file mode 100644 (file)
index 0000000..cf0fb9d
Binary files /dev/null and b/icr14_lab_01.pdf differ
diff --git a/lab1_rust/Cargo.lock b/lab1_rust/Cargo.lock
new file mode 100644 (file)
index 0000000..dd6deb9
--- /dev/null
@@ -0,0 +1,12 @@
+[root]
+name = "lab1_rust"
+version = "0.0.1"
+dependencies = [
+ "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#1e706b8ef4d4344b5297b90bcd430cc96f40fb39"
+
diff --git a/lab1_rust/Cargo.toml b/lab1_rust/Cargo.toml
new file mode 100644 (file)
index 0000000..3f218e2
--- /dev/null
@@ -0,0 +1,15 @@
+[package]
+
+name = "lab1_rust"
+version = "0.0.1"
+authors = ["Ummon <greg.burri@gmail.com>"]
+
+
+[[bin]]
+
+name = "lab1_rust"
+
+
+[dependencies.openssl]
+
+git = "https://github.com/sfackler/rust-openssl.git"
\ No newline at end of file
diff --git a/lab1_rust/src/crypto.rs b/lab1_rust/src/crypto.rs
new file mode 100644 (file)
index 0000000..d9fe182
--- /dev/null
@@ -0,0 +1,54 @@
+use std::rand::{ OsRng, Rng };
+use std::io::IoResult;
+use std::slice::bytes::copy_memory;
+use openssl::crypto::hash::SHA256;
+use openssl::crypto::hmac::HMAC;
+use openssl::crypto::symm;
+
+// These aren't the keys you're looking for.
+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];
+
+/// Only returns the first ten bytes.
+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, 10));
+   result
+}
+
+/// Encrypt may fail if the provided data size isn't a multiple of 16.
+pub fn encrypt(plaindata: &[u8], iv: &[u8]) -> Option<Vec<u8>> {
+   let c = symm::Crypter::new(symm::AES_256_CBC);
+   c.init(symm::Encrypt, KEY_C, iv.to_vec());
+   c.pad(false); // Padding disabled!
+   let r = c.update(plaindata);
+   let rest = c.finalize();
+   if rest.is_empty() {
+      Some(r)
+   } else {
+      None
+   }
+}
+
+/// Decrypt may fail if the provided data size isn't a multiple of 16.
+pub fn decrypt(cypherdata: &[u8], iv: &[u8]) -> Option<Vec<u8>> {
+   let c = symm::Crypter::new(symm::AES_256_CBC);
+   c.init(symm::Decrypt, KEY_C, iv.to_vec());
+   c.pad(false); // Padding disabled!
+   let r = c.update(cypherdata);
+   let rest = c.finalize();
+   if rest.is_empty() {
+      Some(r)
+   } else {
+      None
+   }
+}
+
+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()); // Uses '/dev/urandom' on Unix-like systems.
+   generator.fill_bytes(bytes.as_mut_slice_());
+   Ok(bytes)
+}
\ No newline at end of file
diff --git a/lab1_rust/src/end_point.rs b/lab1_rust/src/end_point.rs
new file mode 100644 (file)
index 0000000..2d0a66c
--- /dev/null
@@ -0,0 +1,319 @@
+use std::io;
+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 };
+
+// Default timeout when waiting data on a stream (for instance: 'TcpStream::read').
+static DEFAULT_TIMEOUT: Option<u64> = Some(500); // [ms].
+
+pub struct Server {
+   acceptor: TcpAcceptor
+}
+
+pub struct Client {
+   end_point: EndPoint,
+}
+
+pub struct EndPoint {
+   socket: TcpStream,
+   current_timestamp: u64
+}
+
+impl Server {
+   pub fn new(interface: &str, port: u16) -> IoResult<Server> {
+      let mut acceptor = try!(TcpListener::bind(interface, port).listen());
+
+      let server = Server {
+         acceptor: acceptor.clone()
+      };
+
+      spawn(proc() {
+         loop {
+            for stream in acceptor.incoming() {
+               match stream {
+                  Ok(stream) => spawn(proc() {
+                     Server::handle_client(EndPoint::new(stream));
+                  }),
+                  _ => return
+               }
+            }
+         }
+      });
+
+      Ok(server)
+   }
+
+   pub fn close(&mut self) -> IoResult<()> {
+      self.acceptor.close_accept()
+   }
+
+   fn handle_client(mut end_point: EndPoint) {
+      loop {
+         match end_point.read() {
+            Ok(packet@Packet { t: Command(..), .. }) => {
+               println!("[Server] Valid command received: {}", packet);
+               let answer = Answer(Packet::random_packet_data([]));
+               match end_point.send(answer.clone()) {
+                  Ok(_) =>
+                     println!("[Server] Answer sent: {}", answer),
+                  Err(e) =>
+                     println!("[Server] Can't send the answer. Error: {}", e)
+               }
+            },
+            // Socket has been closed.
+            Err(packet::IOReadError(IoError { kind: EndOfFile, .. })) => {
+               println!("[Server] Connection closed: EOF");
+               return
+            },
+            other =>
+               println!("[Server] Error or invalid packet: {}", other)
+         }
+      }
+   }
+}
+
+impl Client {
+   pub fn new(address: &str, port: u16) -> IoResult<Client> {
+      Ok(Client { end_point: EndPoint {
+         socket: try!(TcpStream::connect(address, port)),
+         current_timestamp: 0
+      }})
+   }
+
+   pub fn close(&mut self) -> IoResult<()> {
+      self.end_point.close()
+   }
+
+   pub fn send(&mut self, packet: PacketType) {
+      match packet {
+         Command(_) => {
+            println!("[Client] Sending: {}", packet);
+            match self.end_point.send_with_result(packet) {
+               Ok(Ok(packet@Packet { t: Answer(..), .. })) =>
+                  println!("[Client] Command transmitted correctly, answer: {}", packet),
+               Ok(Ok(packet)) =>
+                  println!("[Client] Command transmitted correctly, wrong answer: {}", packet),
+               Ok(Err(e)) =>
+                  println!("[Client] Answer error: {}", e),
+               Err(e) =>
+                  println!("[Client] Can't send the packet. Error: {}", e)
+            }
+         },
+         other =>
+            println!("[Client] Cannot send this type of packet: {}", other)
+      }
+   }
+
+   /// Send some valid and not-valid packets to the server and check the result.
+   /// For each test a new client is created.
+   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
+            }
+         }
+      };
+
+      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(_) => vec!(),
+            _ => m.unwrap()
+         }
+      }
+
+      let mut tests = vec!(
+         // 1)
+         |client: &mut Client| -> bool {
+            println!("Sending a valid packet...");
+            match client.end_point.send_with_result(Command(Packet::random_packet_data([42]))) {
+               Ok(Ok(Packet { t: Answer(..), .. })) => true,
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+         // 2)
+         |client: &mut Client| -> bool {
+            println!("Sending a packet with an unknown type...");
+            client.end_point.current_timestamp += 1;
+            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: io::TimedOut, .. }))) => true, // OK: the server should not send any packet.
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+         // 3)
+         |client: &mut Client| -> bool {
+            println!("Sending 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: io::TimedOut, .. }))) => true, // OK: the server should not send any packet.
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+         // 4)
+         |client: &mut Client| -> bool {
+            println!("Sending a packet with altered crypted data (do not alter the padding)...");
+            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 { t: Error(packet::AuthError), .. })) => true,
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+         // 5)
+         |client: &mut Client| -> bool {
+            println!("Sending a packet with too small data...");
+            let 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: io::TimedOut, .. }))) => true, // OK: the server should not send any packet.
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+         // 6)
+         |client: &mut Client| -> bool {
+            println!("Sending a packet with too large data...");
+            let 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: io::TimedOut, .. }))) => true, // OK: the server should not send any packet.
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+         // 7)
+         |client: &mut Client| -> bool {
+            println!("Sending a packet with wrong padding (all 0)...");
+            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(_) => vec!(),
+               _ => m.unwrap()
+            };
+
+            match client.end_point.send_raw_with_result(raw_packet.as_slice()) {
+               Ok(Ok(Packet { t: Error(packet::CryptError), .. })) => true,
+               other => {
+                  println!("Error: {}", other);
+                  false
+               }
+            }
+         },
+      );
+
+      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");
+         }
+      }
+
+      if nb_test_passed == tests.len() {
+         println!("All tests passed");
+      } else {
+         println!("#{} test(s) failed", tests.len() - nb_test_passed);
+      }
+   }
+}
+
+impl EndPoint {
+   pub fn new(socket: TcpStream) -> EndPoint {
+      EndPoint { socket: socket, current_timestamp: 0 }
+   }
+
+   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) {
+         Err(e) => Err(e),
+         _ => 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> {
+      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) {
+         Err(packet::WriteIOError(e)) => Err(e),
+         _ => Ok(())
+      }
+   }
+
+   fn read(&mut self) -> ReadingResult {
+      fn send_error(ep: &mut EndPoint, error_type: packet::ErrorType) {
+         match ep.send(Error(error_type)) {
+            Err(e) => println!("Unable to send error packet: {}", e),
+            Ok(_) => ()
+         };
+      };
+
+      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::InvalidTimestampError)
+            } else {
+               self.current_timestamp = packet.timestamp + 1;
+               Ok(packet)
+            }
+         },
+         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
+         },
+         other =>
+            other
+      }
+   }
+}
diff --git a/lab1_rust/src/main.rs b/lab1_rust/src/main.rs
new file mode 100644 (file)
index 0000000..64aef28
--- /dev/null
@@ -0,0 +1,59 @@
+#![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 end_point;
+mod oracle_machine;
+
+const PORT: u16 = 4221;
+
+fn print_usage() {
+   println!(
+      r"{} genkey | tests | oracle-weak | oracle-fixed
+   genkey: Generate a 256 bits key
+   tests: launch some tests between a client and a weak server
+   oracle-weak: launch a padding oracle attack against a weak server
+   oracle-fixed: launch a padding oracle attack against a fixed server",
+      os::args()[0]
+   );
+}
+
+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" {
+      match crypto::generate_key(256 / 8) {
+         Ok(key) => println!("key: {}", key),
+         Err(e) => println!("Unable to generate a key. Error: {}", e)
+      }
+   } else {
+      println!("Starting server...");
+
+      match Server::new("::1", PORT) {
+         Ok(mut server) => {
+            println!("Server started");
+
+            if args.len() > 1 && args[1].as_slice() == "tests" {
+               Client::start_tests("::1", PORT);
+            }
+
+            println!("Press any key to quit");
+            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)
+      }
+   }
+}
diff --git a/lab1_rust/src/oracle_machine.rs b/lab1_rust/src/oracle_machine.rs
new file mode 100644 (file)
index 0000000..68aef94
--- /dev/null
@@ -0,0 +1,19 @@
+use std::io;
+use std::io::{ TcpStream };
+use end_point::EndPoint;
+
+/// Try to decypher a cyphered data block by using an oracle on the provided address and port.
+/// May prints some message on the stdout.
+pub fn decypher(address: &str, port: u16, cypherblock: [u8, ..16]) -> Option<Vec<u8>> {
+   let end_point = EndPoint::new(
+      match TcpStream::connect(address, port) {
+         Ok(s) => s,
+         _ => {
+            println!("Unable to connect to the oracle on {}:{}", address, port);
+            return None
+         }
+      }
+   );
+
+   None
+}
diff --git a/lab1_rust/src/packet.rs b/lab1_rust/src/packet.rs
new file mode 100644 (file)
index 0000000..73eecea
--- /dev/null
@@ -0,0 +1,268 @@
+use std::io;
+use std::fmt;
+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),
+   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.
+macro_rules! try_read_io(
+   ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(IOReadError(e)) })
+)
+
+// There are all the errors that may occur when encrypting, authenticating and writing a packet.
+#[deriving(Show)]
+pub enum WritingError {
+   WriteIOError(io::IoError),
+   EncryptError,
+}
+
+// A macro to return a 'IOWritingError' in case of error.
+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>;
+
+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, Clone)]
+pub struct PacketData {
+   id: u8,
+   payload: Vec<u8> // The size can vary from 'MIN_PAYLOAD_SIZE' to 'MAX_PAYLOAD_SIZE' bytes.
+}
+
+#[deriving(Show, Clone)]
+pub enum ErrorType {
+   CryptError,
+   AuthError
+}
+
+#[deriving(Clone)]
+pub enum PacketType {
+   Command(PacketData),
+   Answer(PacketData),
+   Error(ErrorType),
+}
+
+/// Serialized packet format : |LL|P|TTTTTTTT|D...D|MMMMMMMMMM|
+/// Where:
+///   LL: Size of 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 and answer packet
+///      |0000000000000000| for error packet
+#[deriving(Show)]
+pub struct Packet {
+   pub t: PacketType,
+   pub timestamp: u64
+}
+
+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.as_slice().to_hex())
+      }
+      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(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: 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, |_, padding_length: uint| -> u8 {
+         padding_length as u8
+      })
+   }
+
+   /// 'padd_fun' is function defining 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 =
+         match self.t {
+            Command(ref p) | Answer(ref p) => packet_data(p),
+            Error(_) => Vec::from_elem(16, 0) // Padding as data: 16 * 0.
+         };
+
+      // Compute the MAC
+      let mac = crypto::compute_mac(data.as_slice());
+
+      // Padding.
+      match self.t {
+         Command(_) | Answer(_) => {
+            let padding_size = if data.len() % 16 == 0 { 16 } else { 16 - data.len() % 16 } ;
+            data.reserve_additional(padding_size);
+            for i in range(0, padding_size) {
+               data.push(padd_fun(i, padding_size));
+            }
+         },
+         _ => ()
+      }
+
+      // Encrypt.
+      let encrypted_data = match crypto::encrypt(data.as_slice(), iv_from_timestamp(self.timestamp).as_slice()) {
+         Some(d) => d,
+         _ => return Err(EncryptError)
+      };
+
+      // 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
+         }
+      ));
+
+      // 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 data_size = try_read_io!(input.read_be_u16());
+
+      // 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);
+      if try_read_io!(input.read(encrypted_data.as_mut_slice_())) != encrypted_data.len() {
+         return Err(UnconsistentEncryptedSizeError)
+      }
+      let mut data = match crypto::decrypt(encrypted_data.as_slice(), iv_from_timestamp(timestamp).as_slice()) {
+         Some(d) => d,
+         _ => return Err(UnconsistentEncryptedSizeError)
+      };
+
+      // 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) => {
+               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;
+               data.truncate(data_length);
+            },
+            None =>
+               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() {
+         return Err(UnconsistentMACSizeError)
+      }
+      let mac_data = crypto::compute_mac(data.as_slice());
+      if mac_read != mac_data {
+         return Err(MACMismatchError)
+      }
+
+      Ok(Packet {
+         t: match packet_type {
+            // 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) }
+            },
+            // Error.
+            _ => {
+               if data.len() != 16 {
+                  return Err(UnconsistentDataSizeError)
+               } else if data != Vec::from_elem(16, 0) {
+                  return Err(DataError)
+               }
+               match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) }
+            }
+         },
+         timestamp: timestamp
+      })
+   }
+}
+
+// Build an initialization vector: 64 * 0u8 + timestamp (128 bits).
+fn iv_from_timestamp(timestamp: u64) -> Vec<u8> {
+   let mut iv = io::MemWriter::with_capacity(16);
+   let _ = iv.write_be_u64(0u64);
+   let _ = iv.write_be_u64(timestamp);
+   iv.unwrap()
+}
diff --git a/rapport/main.bib b/rapport/main.bib
new file mode 100644 (file)
index 0000000..1647767
--- /dev/null
@@ -0,0 +1,35 @@
+@misc {wiki-replay-attack,
+   author = "Wikipedia",
+   title = "Replay attack --- {W}ikipedia{,} The Free Encyclopedia",
+   year = "2014",
+   url = "\url{http://en.wikipedia.org/wiki/Replay_attack}"
+ }
+ @misc {wiki-authentication-encryption,
+   author = "Wikipedia",
+   title = "Authenticated encryption --- {W}ikipedia{,} The Free Encyclopedia",
+   year = "2014",
+   url = "\url{http://en.wikipedia.org/wiki/Authenticated_encryption}"
+ }
+ @misc {wiki-TLS,
+   author = "Wikipedia",
+   title = "Transport Layer Security --- {W}ikipedia{,} The Free Encyclopedia",
+   year = "2014",
+   url = "\url{http://en.wikipedia.org/wiki/Transport_Layer_Security}"
+ }
+ @misc {wiki-block-cipher-mode-of-operation,
+   author = "Wikipedia",
+   title = "Block cipher mode of operation --- {W}ikipedia{,} The Free Encyclopedia",
+   year = "2014",
+   url = "\url{http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation}"
+ }
+ @misc {wiki-padding-oracle-attack,
+   author = "Wikipedia",
+   title = "Padding oracle attack --- {W}ikipedia{,} The Free Encyclopedia",
+   year = "2014",
+   url = "\url{http://en.wikipedia.org/wiki/Padding_oracle_attack}"
+ }
diff --git a/rapport/main.tex b/rapport/main.tex
new file mode 100644 (file)
index 0000000..e83451d
--- /dev/null
@@ -0,0 +1,102 @@
+\documentclass[a4paper,10pt]{article}
+
+\usepackage[francais]{babel}
+\usepackage[utf8]{inputenc}
+\usepackage[T1]{fontenc}
+\usepackage{lmodern}
+
+\usepackage{listings}
+\usepackage{url}
+
+\title{ICR - Labo \#1 : \textit{MAC-and-Encrypt and Padding Oracles}}
+\author{G.Burri}
+
+\begin{document}
+
+\lstset{language=C}
+\nocite{*}
+
+\maketitle
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Introduction}
+
+Le but de ce laboratoire est d'expérimenter le chiffrement symétrique \emph{AES} ainsi que l'authentification par \emph{MAC}, de mettre en Ã©vidence des problèmes de sécurité liés Ã  un protocole choisi propre et de montrer des solutions afin de corriger ces problèmes.
+
+Nous utiliseront \emph{AES-256} en mode \emph{CBC} pour chiffrer les données ainsi que \emph{HMAC-SHA256} pour l'authentification.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Protocol simulator}
+
+\subsection{Structure du code}
+
+\subsection{Quelle est la stratégie recommendée en pratique parmi les trois listées ci après ?}
+
+\begin{itemize}
+   \item \emph{MAC-and-Encrypt} : $Enc(M)|MAC(M)$ ;
+   \item \emph{MAC-then-Encrypt} : $Enc(M|MAC(M))$ ;
+   \item \emph{Encrypt-then-MAC} : $Enc(M)|MAC(Enc(M))$.
+\end{itemize}
+
+\subsubsection{Quelle stratégie est utilisée par \emph{TLS} ?}
+
+\emph{TSL} utilise la deuxième version (\emph{MAC-then-Encrypt}). Ã€ noté que le \emph{MAC} est optionnel.
+
+Une proposition \footnote{https://tools.ietf.org/html/draft-ietf-tls-encrypt-then-mac-02} existe afin d'utiliser du \textit{Encrypt-then-MAC} pour \emph{TSL}.
+
+
+\subsubsection{Quelle stratégie est utilisée par \emph{SSH} ?}
+
+\emph{SSH} utilise la même méthode utilisée dans ce laboratoire, c'est Ã  dire la première : \emph{MAC-and-Encrypt}.
+
+\subsection{Quel est le rôle du timestamp en terme de sécurité ?}
+
+Permet de minimiser certaines attaques comme l'attaque par rejeu (\emph{replay attack})\cite{wiki-replay-attack} où un attaquant réutilise tel-quel tout ou une partie d'un message intercepté au préalable.
+
+Dans notre cas un attaquant ne pourra pas rejouer une commande tel quelle, elle serait rejetée par le serveur ayant un \emph{timestamp} supérieur. Si l'attaquant essaie de renvoyer un paquet avec un timestamp modifié, alors les données décodées ne seront plus validées par la \emph{MAC} car le vecteur d'initialisation utilisé (\emph{IV}) lors du déchiffrement est composé en partie par le \emph{timestamp}.
+
+
+\subsection{Y a-t-il un moyen d'effectuer une attaque de type \emph{denial-of-service} sur notre dispositif ?}
+
+Via une \emph{replay attack} en modifiant le \emph{timestamp} pour qu'il soit valide le dispositif va devoir déchiffrer les données puis calculer le \emph{MAC} avant de se rendre compte que le paquet est invalide et envoyer une réponse qui sera chiffrée et authentifiée. Dans ce cas on peut faire travailler Ã©normément le dispositif en lui envoyant le plus de paquet Ã  déchiffrer que le permet le débit du moyen de communication utilisé. Cela peut amener le dispositif a Ãªtre surchargé.
+
+
+\subsection{À la place d'utiliser un \emph{IV} aléatoire, le mode \emph{CBC} implémente une approche basée sur un \emph{nonce}. Que peut-on dire de sa sécurité ?}
+
+
+
+\subsection{Remarques concernant la sécurité de notre protocole}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Utilisation du serveur comme d'un Oracle de déchiffrement}
+
+\subsection{Historique de l'attaque par oracle Ã  l'aide du remplissage}
+
+
+
+\subsection{Explication de l'attaque pour notre cas}
+
+\subsection{Calcul de la complexité moyenne de l'attaque}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Correction du protocole}
+
+\subsection{Description}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Conclusion}
+
+
+%http://crypto.stackexchange.com/a/205
+%https://en.wikipedia.org/wiki/Malleability_%28cryptography%29
+
+\bibliographystyle{plain}
+\bibliography{main}
+
+\end{document}
diff --git a/src/crypto.rs b/src/crypto.rs
deleted file mode 100644 (file)
index d9fe182..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-use std::rand::{ OsRng, Rng };
-use std::io::IoResult;
-use std::slice::bytes::copy_memory;
-use openssl::crypto::hash::SHA256;
-use openssl::crypto::hmac::HMAC;
-use openssl::crypto::symm;
-
-// These aren't the keys you're looking for.
-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];
-
-/// Only returns the first ten bytes.
-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, 10));
-   result
-}
-
-/// Encrypt may fail if the provided data size isn't a multiple of 16.
-pub fn encrypt(plaindata: &[u8], iv: &[u8]) -> Option<Vec<u8>> {
-   let c = symm::Crypter::new(symm::AES_256_CBC);
-   c.init(symm::Encrypt, KEY_C, iv.to_vec());
-   c.pad(false); // Padding disabled!
-   let r = c.update(plaindata);
-   let rest = c.finalize();
-   if rest.is_empty() {
-      Some(r)
-   } else {
-      None
-   }
-}
-
-/// Decrypt may fail if the provided data size isn't a multiple of 16.
-pub fn decrypt(cypherdata: &[u8], iv: &[u8]) -> Option<Vec<u8>> {
-   let c = symm::Crypter::new(symm::AES_256_CBC);
-   c.init(symm::Decrypt, KEY_C, iv.to_vec());
-   c.pad(false); // Padding disabled!
-   let r = c.update(cypherdata);
-   let rest = c.finalize();
-   if rest.is_empty() {
-      Some(r)
-   } else {
-      None
-   }
-}
-
-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()); // Uses '/dev/urandom' on Unix-like systems.
-   generator.fill_bytes(bytes.as_mut_slice_());
-   Ok(bytes)
-}
\ No newline at end of file
diff --git a/src/end_point.rs b/src/end_point.rs
deleted file mode 100644 (file)
index 2d0a66c..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-use std::io;
-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 };
-
-// Default timeout when waiting data on a stream (for instance: 'TcpStream::read').
-static DEFAULT_TIMEOUT: Option<u64> = Some(500); // [ms].
-
-pub struct Server {
-   acceptor: TcpAcceptor
-}
-
-pub struct Client {
-   end_point: EndPoint,
-}
-
-pub struct EndPoint {
-   socket: TcpStream,
-   current_timestamp: u64
-}
-
-impl Server {
-   pub fn new(interface: &str, port: u16) -> IoResult<Server> {
-      let mut acceptor = try!(TcpListener::bind(interface, port).listen());
-
-      let server = Server {
-         acceptor: acceptor.clone()
-      };
-
-      spawn(proc() {
-         loop {
-            for stream in acceptor.incoming() {
-               match stream {
-                  Ok(stream) => spawn(proc() {
-                     Server::handle_client(EndPoint::new(stream));
-                  }),
-                  _ => return
-               }
-            }
-         }
-      });
-
-      Ok(server)
-   }
-
-   pub fn close(&mut self) -> IoResult<()> {
-      self.acceptor.close_accept()
-   }
-
-   fn handle_client(mut end_point: EndPoint) {
-      loop {
-         match end_point.read() {
-            Ok(packet@Packet { t: Command(..), .. }) => {
-               println!("[Server] Valid command received: {}", packet);
-               let answer = Answer(Packet::random_packet_data([]));
-               match end_point.send(answer.clone()) {
-                  Ok(_) =>
-                     println!("[Server] Answer sent: {}", answer),
-                  Err(e) =>
-                     println!("[Server] Can't send the answer. Error: {}", e)
-               }
-            },
-            // Socket has been closed.
-            Err(packet::IOReadError(IoError { kind: EndOfFile, .. })) => {
-               println!("[Server] Connection closed: EOF");
-               return
-            },
-            other =>
-               println!("[Server] Error or invalid packet: {}", other)
-         }
-      }
-   }
-}
-
-impl Client {
-   pub fn new(address: &str, port: u16) -> IoResult<Client> {
-      Ok(Client { end_point: EndPoint {
-         socket: try!(TcpStream::connect(address, port)),
-         current_timestamp: 0
-      }})
-   }
-
-   pub fn close(&mut self) -> IoResult<()> {
-      self.end_point.close()
-   }
-
-   pub fn send(&mut self, packet: PacketType) {
-      match packet {
-         Command(_) => {
-            println!("[Client] Sending: {}", packet);
-            match self.end_point.send_with_result(packet) {
-               Ok(Ok(packet@Packet { t: Answer(..), .. })) =>
-                  println!("[Client] Command transmitted correctly, answer: {}", packet),
-               Ok(Ok(packet)) =>
-                  println!("[Client] Command transmitted correctly, wrong answer: {}", packet),
-               Ok(Err(e)) =>
-                  println!("[Client] Answer error: {}", e),
-               Err(e) =>
-                  println!("[Client] Can't send the packet. Error: {}", e)
-            }
-         },
-         other =>
-            println!("[Client] Cannot send this type of packet: {}", other)
-      }
-   }
-
-   /// Send some valid and not-valid packets to the server and check the result.
-   /// For each test a new client is created.
-   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
-            }
-         }
-      };
-
-      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(_) => vec!(),
-            _ => m.unwrap()
-         }
-      }
-
-      let mut tests = vec!(
-         // 1)
-         |client: &mut Client| -> bool {
-            println!("Sending a valid packet...");
-            match client.end_point.send_with_result(Command(Packet::random_packet_data([42]))) {
-               Ok(Ok(Packet { t: Answer(..), .. })) => true,
-               other => {
-                  println!("Error: {}", other);
-                  false
-               }
-            }
-         },
-         // 2)
-         |client: &mut Client| -> bool {
-            println!("Sending a packet with an unknown type...");
-            client.end_point.current_timestamp += 1;
-            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: io::TimedOut, .. }))) => true, // OK: the server should not send any packet.
-               other => {
-                  println!("Error: {}", other);
-                  false
-               }
-            }
-         },
-         // 3)
-         |client: &mut Client| -> bool {
-            println!("Sending 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: io::TimedOut, .. }))) => true, // OK: the server should not send any packet.
-               other => {
-                  println!("Error: {}", other);
-                  false
-               }
-            }
-         },
-         // 4)
-         |client: &mut Client| -> bool {
-            println!("Sending a packet with altered crypted data (do not alter the padding)...");
-            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 { t: Error(packet::AuthError), .. })) => true,
-               other => {
-                  println!("Error: {}", other);
-                  false
-               }
-            }
-         },
-         // 5)
-         |client: &mut Client| -> bool {
-            println!("Sending a packet with too small data...");
-            let 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: io::TimedOut, .. }))) => true, // OK: the server should not send any packet.
-               other => {
-                  println!("Error: {}", other);
-                  false
-               }
-            }
-         },
-         // 6)
-         |client: &mut Client| -> bool {
-            println!("Sending a packet with too large data...");
-            let 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: io::TimedOut, .. }))) => true, // OK: the server should not send any packet.
-               other => {
-                  println!("Error: {}", other);
-                  false
-               }
-            }
-         },
-         // 7)
-         |client: &mut Client| -> bool {
-            println!("Sending a packet with wrong padding (all 0)...");
-            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(_) => vec!(),
-               _ => m.unwrap()
-            };
-
-            match client.end_point.send_raw_with_result(raw_packet.as_slice()) {
-               Ok(Ok(Packet { t: Error(packet::CryptError), .. })) => true,
-               other => {
-                  println!("Error: {}", other);
-                  false
-               }
-            }
-         },
-      );
-
-      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");
-         }
-      }
-
-      if nb_test_passed == tests.len() {
-         println!("All tests passed");
-      } else {
-         println!("#{} test(s) failed", tests.len() - nb_test_passed);
-      }
-   }
-}
-
-impl EndPoint {
-   pub fn new(socket: TcpStream) -> EndPoint {
-      EndPoint { socket: socket, current_timestamp: 0 }
-   }
-
-   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) {
-         Err(e) => Err(e),
-         _ => 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> {
-      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) {
-         Err(packet::WriteIOError(e)) => Err(e),
-         _ => Ok(())
-      }
-   }
-
-   fn read(&mut self) -> ReadingResult {
-      fn send_error(ep: &mut EndPoint, error_type: packet::ErrorType) {
-         match ep.send(Error(error_type)) {
-            Err(e) => println!("Unable to send error packet: {}", e),
-            Ok(_) => ()
-         };
-      };
-
-      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::InvalidTimestampError)
-            } else {
-               self.current_timestamp = packet.timestamp + 1;
-               Ok(packet)
-            }
-         },
-         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
-         },
-         other =>
-            other
-      }
-   }
-}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644 (file)
index 64aef28..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#![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 end_point;
-mod oracle_machine;
-
-const PORT: u16 = 4221;
-
-fn print_usage() {
-   println!(
-      r"{} genkey | tests | oracle-weak | oracle-fixed
-   genkey: Generate a 256 bits key
-   tests: launch some tests between a client and a weak server
-   oracle-weak: launch a padding oracle attack against a weak server
-   oracle-fixed: launch a padding oracle attack against a fixed server",
-      os::args()[0]
-   );
-}
-
-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" {
-      match crypto::generate_key(256 / 8) {
-         Ok(key) => println!("key: {}", key),
-         Err(e) => println!("Unable to generate a key. Error: {}", e)
-      }
-   } else {
-      println!("Starting server...");
-
-      match Server::new("::1", PORT) {
-         Ok(mut server) => {
-            println!("Server started");
-
-            if args.len() > 1 && args[1].as_slice() == "tests" {
-               Client::start_tests("::1", PORT);
-            }
-
-            println!("Press any key to quit");
-            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)
-      }
-   }
-}
diff --git a/src/oracle_machine.rs b/src/oracle_machine.rs
deleted file mode 100644 (file)
index 68aef94..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-use std::io;
-use std::io::{ TcpStream };
-use end_point::EndPoint;
-
-/// Try to decypher a cyphered data block by using an oracle on the provided address and port.
-/// May prints some message on the stdout.
-pub fn decypher(address: &str, port: u16, cypherblock: [u8, ..16]) -> Option<Vec<u8>> {
-   let end_point = EndPoint::new(
-      match TcpStream::connect(address, port) {
-         Ok(s) => s,
-         _ => {
-            println!("Unable to connect to the oracle on {}:{}", address, port);
-            return None
-         }
-      }
-   );
-
-   None
-}
diff --git a/src/packet.rs b/src/packet.rs
deleted file mode 100644 (file)
index 73eecea..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-use std::io;
-use std::fmt;
-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),
-   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.
-macro_rules! try_read_io(
-   ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(IOReadError(e)) })
-)
-
-// There are all the errors that may occur when encrypting, authenticating and writing a packet.
-#[deriving(Show)]
-pub enum WritingError {
-   WriteIOError(io::IoError),
-   EncryptError,
-}
-
-// A macro to return a 'IOWritingError' in case of error.
-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>;
-
-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, Clone)]
-pub struct PacketData {
-   id: u8,
-   payload: Vec<u8> // The size can vary from 'MIN_PAYLOAD_SIZE' to 'MAX_PAYLOAD_SIZE' bytes.
-}
-
-#[deriving(Show, Clone)]
-pub enum ErrorType {
-   CryptError,
-   AuthError
-}
-
-#[deriving(Clone)]
-pub enum PacketType {
-   Command(PacketData),
-   Answer(PacketData),
-   Error(ErrorType),
-}
-
-/// Serialized packet format : |LL|P|TTTTTTTT|D...D|MMMMMMMMMM|
-/// Where:
-///   LL: Size of 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 and answer packet
-///      |0000000000000000| for error packet
-#[deriving(Show)]
-pub struct Packet {
-   pub t: PacketType,
-   pub timestamp: u64
-}
-
-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.as_slice().to_hex())
-      }
-      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(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: 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, |_, padding_length: uint| -> u8 {
-         padding_length as u8
-      })
-   }
-
-   /// 'padd_fun' is function defining 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 =
-         match self.t {
-            Command(ref p) | Answer(ref p) => packet_data(p),
-            Error(_) => Vec::from_elem(16, 0) // Padding as data: 16 * 0.
-         };
-
-      // Compute the MAC
-      let mac = crypto::compute_mac(data.as_slice());
-
-      // Padding.
-      match self.t {
-         Command(_) | Answer(_) => {
-            let padding_size = if data.len() % 16 == 0 { 16 } else { 16 - data.len() % 16 } ;
-            data.reserve_additional(padding_size);
-            for i in range(0, padding_size) {
-               data.push(padd_fun(i, padding_size));
-            }
-         },
-         _ => ()
-      }
-
-      // Encrypt.
-      let encrypted_data = match crypto::encrypt(data.as_slice(), iv_from_timestamp(self.timestamp).as_slice()) {
-         Some(d) => d,
-         _ => return Err(EncryptError)
-      };
-
-      // 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
-         }
-      ));
-
-      // 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 data_size = try_read_io!(input.read_be_u16());
-
-      // 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);
-      if try_read_io!(input.read(encrypted_data.as_mut_slice_())) != encrypted_data.len() {
-         return Err(UnconsistentEncryptedSizeError)
-      }
-      let mut data = match crypto::decrypt(encrypted_data.as_slice(), iv_from_timestamp(timestamp).as_slice()) {
-         Some(d) => d,
-         _ => return Err(UnconsistentEncryptedSizeError)
-      };
-
-      // 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) => {
-               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;
-               data.truncate(data_length);
-            },
-            None =>
-               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() {
-         return Err(UnconsistentMACSizeError)
-      }
-      let mac_data = crypto::compute_mac(data.as_slice());
-      if mac_read != mac_data {
-         return Err(MACMismatchError)
-      }
-
-      Ok(Packet {
-         t: match packet_type {
-            // 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) }
-            },
-            // Error.
-            _ => {
-               if data.len() != 16 {
-                  return Err(UnconsistentDataSizeError)
-               } else if data != Vec::from_elem(16, 0) {
-                  return Err(DataError)
-               }
-               match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) }
-            }
-         },
-         timestamp: timestamp
-      })
-   }
-}
-
-// Build an initialization vector: 64 * 0u8 + timestamp (128 bits).
-fn iv_from_timestamp(timestamp: u64) -> Vec<u8> {
-   let mut iv = io::MemWriter::with_capacity(16);
-   let _ = iv.write_be_u64(0u64);
-   let _ = iv.write_be_u64(timestamp);
-   iv.unwrap()
-}