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"
+++ /dev/null
-use std::io::{ TcpStream, IoResult };
-use packet::{ Command, Packet };
-
-pub struct Client {
- socket: TcpStream,
- current_timestamp: u64
-}
-
-impl Client {
- pub fn new(address: &str, port: u16) -> IoResult<Client> {
- Ok(Client {
- socket: try!(TcpStream::connect(address, port)),
- current_timestamp: 0
- })
- }
-
- pub fn close(&mut self) -> IoResult<()> {
- try!(self.socket.close_read());
- try!(self.socket.close_write());
- Ok(())
- }
-
- pub fn start_tests(&mut self) {
- self.current_timestamp += 1;
- let timestamp = self.current_timestamp;
- self.send(Packet { timestamp: timestamp, t: Command(Packet::random_packet_data()) });
- }
-
- fn send(&mut self, p: Packet) {
- println!("Client sends packet: {}", p);
- match p.write(&mut self.socket) {
- Err(e) => println!("Error on sending: {}", e),
- _ => ()
- }
- }
-}
\ No newline at end of file
}
pub fn generate_key(size_byte: uint) -> IoResult<Vec<u8>> {
- let mut bytes = Vec::from_elem(size_byte, 0u8);
+ let mut bytes = Vec:: from_elem(size_byte, 0u8);
let mut generator = try!(OsRng::new()); // Uses '/dev/urandom' on Unix-like systems.
generator.fill_bytes(bytes.as_mut_slice_());
Ok(bytes)
-use std::io::{ Acceptor, Listener, TcpStream, IoResult, IoError, EndOfFile };
+use std::io::{ MemWriter, Acceptor, Listener, TcpStream, IoResult, IoError, EndOfFile };
use std::io::net::tcp::{ TcpAcceptor, TcpListener };
use packet;
use packet::{ Packet, Command, Answer, Error, ReadingResult, PacketType };
+static DEFAULT_TIMEOUT: Option<u64> = Some(500); // [ms].
+
pub struct Server {
acceptor: TcpAcceptor
}
}
impl Server {
- pub fn new(port: u16) -> IoResult<Server> {
- let mut acceptor = try!(TcpListener::bind("127.0.0.1", port).listen());
+ pub fn new(interface: &str, port: u16) -> IoResult<Server> {
+ let mut acceptor = try!(TcpListener::bind(interface, port).listen());
let server = Server {
acceptor: acceptor.clone()
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),
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 {
Ok(())
}
- /**
- * Send a packet and wait synchronously an answer.
- */
+ /// Send a packet and wait for an answer synchronously.
fn send_with_result(&mut self, p: PacketType) -> IoResult<ReadingResult> {
match self.send(p) {
Err(e) => Err(e),
}
}
+ /// Send arbitrary data and wait for an answer synchronously.
+ /// Do not increment the current timestamp.
+ fn send_raw_with_result(&mut self, p: &[u8]) -> IoResult<ReadingResult> {
+ self.socket.set_timeout(DEFAULT_TIMEOUT);
+ match self.socket.write(p) {
+ Err(e) => Err(e),
+ _ => Ok(self.read())
+ }
+ }
+
fn send(&mut self, p: PacketType) -> IoResult<()> {
+ self.socket.set_timeout(DEFAULT_TIMEOUT);
self.current_timestamp += 1;
match (Packet { t: p, timestamp: self.current_timestamp }).write(&mut self.socket) {
- Ok(_) => Ok(()),
- Err(packet::WriteIOError(e)) => Err(e)
+ Err(packet::WriteIOError(e)) => Err(e),
+ _ => Ok(())
}
}
};
};
+ 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)
other
}
}
-}
\ No newline at end of file
+}
#![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
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)
}
}
}
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.
static MAX_PAYLOAD_SIZE: uint = 39;
static FIXED_PACKET_SIZE: uint = 1 + 8 + 10; // Packet type + timestamp + MAC.
-#[deriving(Show)]
+#[deriving(Show, Clone)]
pub struct PacketData {
id: u8,
payload: Vec<u8> // The size can vary from 'MIN_PAYLOAD_SIZE' to 'MAX_PAYLOAD_SIZE' bytes.
}
-#[deriving(Show)]
+#[deriving(Show, Clone)]
pub enum ErrorType {
CryptError,
AuthError
}
-#[deriving(Show)]
+#[deriving(Clone)]
pub enum PacketType {
Command(PacketData),
Answer(PacketData),
Error(ErrorType),
}
+/// Serialized packet format : |LL|P|TTTTTTTT|D...D|MMMMMMMMMM|
+/// Where:
+/// LL: Size on the following data
+/// P: Packet type:
+/// 0x00: Command
+/// OxFF: Answer
+/// 0x0A: Decrypt error
+/// 0x0B: Authentication error
+/// TTTTTTTT: Timestamp (64 bits)
+/// D...D: Encrypted data (AES-256 CBC mode) of:
+/// |I|C...C|P...P| for command and answer packet:
+/// I: Command ID
+/// C: Command payload (from 7 to 39 bytes)
+/// P: Padding from 1 to 16, |I|C...C|P...P| size must be a multiple of 16
+/// |"0000000000000000"| for error packet (16 bytes length)
+/// MMMMMMMMMM: first 10 bytes (most significant) of the HMAC-SHA256 of:
+/// |I|C...C| for command ans answer packet
+/// |"0000000000"| for error packet
+#[deriving(Show)]
pub struct Packet {
pub t: PacketType,
pub timestamp: u64
}
-impl fmt::Show for Packet {
+impl fmt::Show for PacketType {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fn data_to_str(data: &PacketData) -> String {
- format!("id: {}, payload({}): {}", data.id, data.payload.len(), data.payload)
+ format!("id: {}, payload({}): \"{}\"", data.id, data.payload.len(), data.payload.as_slice().to_hex())
}
- match self.t {
- Command(ref data) => write!(formatter, "Command packet {{ timestamp: {}, {} }}", self.timestamp, data_to_str(data)),
- Answer(ref data) => write!(formatter, "Answer packet {{ timestamp: {}, {} }}", self.timestamp, data_to_str(data)),
- Error(error_type) => write!(formatter, "Error packet {{ timestamp: {}, errorType: {} }}", self.timestamp, error_type)
+ match self {
+ &Command(ref data) => write!(formatter, "Command {{ {} }}", data_to_str(data)),
+ &Answer(ref data) => write!(formatter, "Answer {{ {} }}", data_to_str(data)),
+ &Error(error_type) => write!(formatter, "Error {{ errorType: {} }}", error_type)
}
}
}
impl Packet {
- pub fn random_packet_data() -> PacketData {
- let mut rng = task_rng();
+ pub fn random_packet_data(seed: &[uint]) -> PacketData {
+ let mut rng = if seed.is_empty() { StdRng::new().unwrap() } else { SeedableRng::from_seed(seed) };
+ let mut payload = Vec::from_elem(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), 0u8);
+ rng.fill_bytes(payload.as_mut_slice_());
PacketData {
- id: random::<u8>(),
- payload: Vec::from_fn(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), |_| random::<u8>())
+ id: rng.gen::<u8>(),
+ payload: payload
}
}
- pub fn write(&self, output: &mut io::Writer) -> WritingResult {
+ pub fn write(&self, output: &mut io::Writer) -> WritingResult {
fn packet_data(p: &PacketData) -> Vec<u8> {
let mut d = Vec::new();
d.push(p.id);
}
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);
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
})
+++ /dev/null
-use std::io::{ TcpListener, TcpStream, Acceptor, Listener, IoError, IoResult, EndOfFile };
-use std::io::net::tcp::{ TcpAcceptor };
-use packet::{ Packet, IOReadError };
-
-pub struct Server {
- acceptor: TcpAcceptor
-}
-
-impl Server {
- pub fn new(port: u16) -> IoResult<Server> {
- let mut acceptor = try!(TcpListener::bind("127.0.0.1", port).listen());
-
- let server = Server {
- acceptor: acceptor.clone()
- };
-
- spawn(proc() {
- loop {
- for stream in acceptor.incoming() {
- match stream {
- Err(_) => return,
- Ok(stream) => spawn(proc() {
- Server::handle_client(stream);
- })
- }
- }
- }
- });
-
- Ok(server)
- }
-
- pub fn close(&mut self) -> IoResult<()> {
- self.acceptor.close_accept()
- }
-
- fn handle_client(mut stream: TcpStream) {
- let mut current_timestamp = 0u64;
-
- loop {
- // stream.set_timeout(Some(1000000000));
- match Packet::read(&mut stream) {
- Ok(packet) => {
- if packet.timestamp <= current_timestamp {
- println!("Error, timestamp mismatch, current timestamp: {}, packet received: {}", current_timestamp, packet);
- } else {
- current_timestamp += 1;
- println!("Receive packet: {}", packet);
- }
- },
- // Socket has been closed.
- Err(IOReadError(IoError { kind: EndOfFile, .. })) => {
- return;
- },
- Err(e) => {
- println!("Read error: {}", e)
- }
- }
- }
- }
-}
\ No newline at end of file