use std::io::{ TcpStream, IoResult };
use command::{ Command, Packet, Error };
-use crypto::Crypto;
pub struct Client {
socket: TcpStream,
- crypto: Crypto,
current_timestamp: u64
}
pub fn new(address: &str, port: u16) -> IoResult<Client> {
Ok(Client {
socket: try!(TcpStream::connect(address, port)),
- current_timestamp: 0,
- crypto: Crypto::new()
+ current_timestamp: 0
})
}
pub fn start_tests(&mut self) {
- let command = Packet::newRandomCommand(self.current_timestamp);
+ let command = Packet::new_random_command(self.current_timestamp);
self.send(command);
}
use std::io;
use std::rand::{ random, task_rng, distributions };
use std::rand::distributions::IndependentSample;
+use crypto;
pub enum ReadingError {
- ReadIOError(io::IoError)
- // TODO...
+ IOReadError(io::IoError),
+ UnknownPacketTypeReadError, // If the first byte is unknown.
+ UnconsistentDataSizeError, // The payload is too small or too big.
}
+macro_rules! try_read_io(
+ ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(IOReadError(e)) })
+)
+
pub enum WritingError {
WriteIOError(io::IoError)
// TODO...
}
+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>;
-pub enum PacketType {
- CommandType,
- AnswerType
-}
+static MIN_PAYLOAD_SIZE: uint = 6;
+static MAX_PAYLOAD_SIZE: uint = 38;
pub struct CommandPacket {
- t: PacketType,
timestamp: u64,
- commandID: u8,
- commandPayload: Vec<u8>
+ id: u8,
+ payload: Vec<u8> // May vary from 'MIN_PAYLOAD_SIZE' to 'MAX_PAYLOAD_SIZE' bytes.
+}
+
+pub enum ErrorType {
+ DecryptError,
+ AuthError
}
pub struct ErrorPacket {
+ t: ErrorType,
timestamp: u64
}
}
impl Packet {
- pub fn newRandomCommand(timestamp: u64) -> Packet {
-
+ pub fn new_random_command(timestamp: u64) -> Packet {
let mut rng = task_rng();
Command(CommandPacket {
- t: CommandType,
- timestamp: timestamp,
- commandID: random::<u8>(),
- commandPayload: Vec::from_fn(distributions::Range::new(7u, 40u).ind_sample(&mut rng), |idx| 0u8)
+ timestamp: timestamp,
+ id: random::<u8>(),
+ payload: Vec::from_fn(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), |idx| random::<u8>())
})
}
- pub fn write(&self, output: &mut io::Writer) -> WritingResult {
-
+ pub fn write(&self, output: &mut io::Writer) -> WritingResult {
match self {
&Command(ref command) => {
- output.write_u8(0);
- output.write_be_u64(command.timestamp);
+ // Data.
+ let mut data = Vec::with_capacity(command.payload.len() + 1);
+ data.push(command.id);
+ data.push(command.payload.len() as u8);
+ data.push_all(command.payload.as_slice());
+
+ // Padding.
+ let padding_size = if data.len() % 16 == 0 { 16 } else { data.len() % 16 } ;
+ let padding = Vec::from_elem(padding_size, padding_size as u8);
+ data.push_all(padding.as_slice());
+
+ // Encrypt.
+ let encrypted_data = crypto::encrypt(data.as_slice(), iv_from_timestamp(command.timestamp).as_slice());
+
+ // Data size.
+ try_write_io!(output.write_be_u16(encrypted_data.len() as u16));
+
+ // Packet type.
+ try_write_io!(output.write_u8(0));
+
+ // Timestamp.
+ try_write_io!(output.write_be_u64(command.timestamp));
+ // Write encrypted data.
+ try_write_io!(output.write(encrypted_data.as_slice()));
+ // MAC.
+ try_write_io!(output.write(crypto::compute_mac(data.as_slice())));
},
- &Error(error) => ()
+ &Error(error) => {
+ // Padding as data: 16 * '0'.
+ let padding = Vec::from_elem(16, '0' as u8);
+ let encrypted_data = crypto::encrypt(padding.as_slice(), iv_from_timestamp(error.timestamp).as_slice());
+
+ // Data size.
+ try_write_io!(output.write_be_u16(encrypted_data.len() as u16));
+
+ // Error type.
+ try_write_io!(match error.t {
+ DecryptError => output.write_u8(0x0A),
+ AuthError => output.write_u8(0x0B)
+ });
+
+ // Timestamp.
+ try_write_io!(output.write_be_u64(error.timestamp));
+
+ // Encrypt the padding.
+ try_write_io!(output.write(encrypted_data.as_slice()));
+
+ // MAC.
+ try_write_io!(output.write(crypto::compute_mac(padding.as_slice())));
+ }
}
Ok(())
}
pub fn read(input: &mut io::Reader) -> ReadingResult {
- Ok(Packet::newRandomCommand(0))
+ let data_size = try_read_io!(input.read_be_u16());
+
+ match try_read_io!(input.read_byte()) {
+ 0 => {
+ let mut command = CommandPacket { timestamp: try_read_io!(input.read_be_u64()), id: 0, payload: vec!() };
+ let mut encrypted_data = Vec::from_elem(data_size as uint, 0u8);
+ if try_read_io!(input.read(encrypted_data.as_mut_slice_())) != data_size as uint {
+ return Err(IOReadError(io::IoError { kind: io::EndOfFile, desc: "Unable to read all encrypted data", detail: None }));
+ }
+
+ let data = crypto::decrypt(encrypted_data.as_slice(), iv_from_timestamp(command.timestamp).as_slice());
+ if data.len() < MIN_PAYLOAD_SIZE + 2 || data.len() > MAX_PAYLOAD_SIZE + 2 {
+ return Err(UnconsistentDataSizeError)
+ }
+
+ Ok(Command(command))
+ },
+ t if t == 0x0A || t == 0x0B => {
+ let mut error = ErrorPacket { t: if t == 0x0A { DecryptError } else { AuthError }, timestamp: try_read_io!(input.read_be_u64()) };
+ // TODO
+ Ok(Error(error))
+ },
+ _ => Err(UnknownPacketTypeReadError),
+ }
}
-}
\ No newline at end of file
+}
+
+fn iv_from_timestamp(t: u64) -> Vec<u8> {
+ // Initialization vector = 64 * 0u8 + timestamp.
+ let mut iv = io::MemWriter::with_capacity(16);
+ iv.write_be_u64(0u64);
+ iv.write_be_u64(t);
+ iv.unwrap()
+}
-use std::rand::OsRng;
-use std::vec::Vec;
+use std::rand::{ OsRng, Rng };
use std::io::IoResult;
-use std::rand::Rng;
-
+use std::slice::bytes::copy_memory;
use openssl::crypto::hash::SHA256;
use openssl::crypto::hmac::HMAC;
-use std::slice::bytes::copy_memory;
-
-//const KEY_A: &'static [u8] = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d".from_hex().unwrap();
-//static KEY_A: Vec<u8> = vec![1, 2, 3]; // "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d".from_hex().unwrap();
-
-pub struct Crypto {
- key_a: Vec<u8>,
- key_c: Vec<u8>
-}
+use openssl::crypto::symm;
-impl Crypto {
- pub fn new() -> Crypto {
- Crypto {
- key_a: [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].to_vec(),
- key_c: [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].to_vec()
- }
- }
+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];
- pub fn compute_mac(&self, data: &[u8]) -> [u8, ..10] {
- let mut hmac = HMAC(SHA256, self.key_a.as_slice());
- hmac.update(data);
- let mut result = [0u8, ..10];
- copy_memory(&mut result, hmac.finalize().slice(0, 9));
- result
- }
+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, 9));
+ result
}
-pub fn encrypt(plaindata: &Vec<u8>) -> Vec<u8> {
- vec!()
+pub fn encrypt(plaindata: &[u8], iv: &[u8]) -> Vec<u8> {
+ symm::encrypt(symm::AES_256_CBC, KEY_C, iv.to_vec(), plaindata)
}
-pub fn decrypt(cypherdata: &Vec<u8>) -> Vec<u8> {
- vec!()
+pub fn decrypt(cypherdata: &[u8], iv: &[u8]) -> Vec<u8> {
+ symm::decrypt(symm::AES_256_CBC, KEY_C, iv.to_vec(), cypherdata)
}
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());
+ 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
+#![feature(macro_rules)]
+
extern crate openssl;
use std::io;
use std::io::{ TcpListener, TcpStream, Acceptor, Listener, IoError, IoResult };
use std::io::net::tcp::{ TcpAcceptor };
use command::{ Command, Packet, Error };
-use crypto::Crypto;
pub struct Server {
- acceptor: TcpAcceptor,
- crypto: Crypto
+ 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(),
- crypto: Crypto::new()
+ acceptor: acceptor.clone()
};
spawn(proc() {
}
fn handle_client(mut stream: TcpStream) {
- println!("new connection!");
+
let mut current_timestamp = 0u64;
+
match Packet::read(&mut stream) {
Ok(Command(packet)) => println!("CommandPacket"),
Ok(Error(packet)) => println!("AnswerPacket"),