From: Ummon Date: Sun, 2 Nov 2014 17:07:54 +0000 (+0100) Subject: Begining of some test cases. X-Git-Url: http://git.euphorik.ch/index.cgi?a=commitdiff_plain;h=8226b0eced022af21512d87902bf6e252afe1a67;p=crypto_lab1.git Begining of some test cases. --- diff --git a/Cargo.lock b/Cargo.lock index fb3c642..dd6deb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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 index 14df2d4..0000000 --- a/src/client.rs +++ /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 { - 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 diff --git a/src/crypto.rs b/src/crypto.rs index ce301ab..60c205b 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -25,7 +25,7 @@ pub fn decrypt(cypherdata: &[u8], iv: &[u8]) -> Vec { } pub fn generate_key(size_byte: uint) -> IoResult> { - 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) diff --git a/src/end_point.rs b/src/end_point.rs index 9488d13..77af33d 100644 --- a/src/end_point.rs +++ b/src/end_point.rs @@ -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 = Some(500); // [ms]. + pub struct Server { acceptor: TcpAcceptor } @@ -17,8 +19,8 @@ struct EndPoint { } impl Server { - pub fn new(port: u16) -> IoResult { - let mut acceptor = try!(TcpListener::bind("127.0.0.1", port).listen()); + pub fn new(interface: &str, port: u16) -> IoResult { + 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 { 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 { + 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 +} diff --git a/src/main.rs b/src/main.rs index fbffa21..5f2fdf9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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) } } } diff --git a/src/packet.rs b/src/packet.rs index 49ae2be..74bf102 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -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 // 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::(), - payload: Vec::from_fn(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), |_| random::()) + id: rng.gen::(), + 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 { 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 index b655ae7..0000000 --- a/src/server.rs +++ /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 { - 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