From 786dd3ba778ac05917b00e42e8d82929db9fa529 Mon Sep 17 00:00:00 2001 From: Ummon Date: Sun, 2 Nov 2014 23:29:24 +0100 Subject: [PATCH] Add some tests. --- src/end_point.rs | 170 ++++++++++++++++++++++++++++++++++------------- src/main.rs | 16 ++--- src/packet.rs | 90 ++++++++++++++----------- 3 files changed, 180 insertions(+), 96 deletions(-) diff --git a/src/end_point.rs b/src/end_point.rs index 77af33d..199fff7 100644 --- a/src/end_point.rs +++ b/src/end_point.rs @@ -21,11 +21,11 @@ struct EndPoint { impl Server { pub fn new(interface: &str, port: u16) -> IoResult { 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 { + 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 { 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 { @@ -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 }, diff --git a/src/main.rs b/src/main.rs index 5f2fdf9..e9e911a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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!("{} | | ...", 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) => diff --git a/src/packet.rs b/src/packet.rs index 74bf102..ece32c4 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -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::(), payload: payload } } + pub fn new_packet_data(id: u8, payload: Vec) -> 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 { 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 -- 2.45.2