X-Git-Url: http://git.euphorik.ch/?p=crypto_lab1.git;a=blobdiff_plain;f=src%2Fpacket.rs;fp=src%2Fpacket.rs;h=49ae2be0f46e066bf5d1808ef0e2359bf59910f1;hp=0000000000000000000000000000000000000000;hb=db8678377c7ea4fa7183a54c1bfe095e051882a8;hpb=3a33f82b92400ffabdc5fd7bdcbdc9f888277418 diff --git a/src/packet.rs b/src/packet.rs new file mode 100644 index 0000000..49ae2be --- /dev/null +++ b/src/packet.rs @@ -0,0 +1,212 @@ +use std::io; +use std::fmt; +use std::rand::{ random, task_rng, distributions }; +use std::rand::distributions::IndependentSample; +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. + 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. +} + +// 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) + // TODO... +} + +// 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; +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)] +pub struct PacketData { + id: u8, + payload: Vec // The size can vary from 'MIN_PAYLOAD_SIZE' to 'MAX_PAYLOAD_SIZE' bytes. +} + +#[deriving(Show)] +pub enum ErrorType { + CryptError, + AuthError +} + +#[deriving(Show)] +pub enum PacketType { + Command(PacketData), + Answer(PacketData), + Error(ErrorType), +} + +pub struct Packet { + pub t: PacketType, + pub timestamp: u64 +} + +impl fmt::Show for Packet { + 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) + } + 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) + } + } +} + +impl Packet { + pub fn random_packet_data() -> PacketData { + let mut rng = task_rng(); + PacketData { + id: random::(), + payload: Vec::from_fn(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), |_| random::()) + } + } + + pub fn write(&self, output: &mut io::Writer) -> 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 = + 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()); + + // 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()); + }, + _ => () + } + + // Encrypt. + 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 + } + )); + + // 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 { + let data_size = try_read_io!(input.read_be_u16()); + let packet_type = try_read_io!(input.read_u8()); + 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 = crypto::decrypt(encrypted_data.as_slice(), iv_from_timestamp(timestamp).as_slice()); + + // 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) { + 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 { + 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 => { + 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 + }) + } +} + +// Build an initialization vector: 64 * 0u8 + timestamp (128 bits). +fn iv_from_timestamp(timestamp: u64) -> Vec { + let mut iv = io::MemWriter::with_capacity(16); + let _ = iv.write_be_u64(0u64); + let _ = iv.write_be_u64(timestamp); + iv.unwrap() +}