X-Git-Url: http://git.euphorik.ch/?p=crypto_lab1.git;a=blobdiff_plain;f=src%2Fpacket.rs;h=632d298ab8be885fc36f93856e2ee7981fd49512;hp=49ae2be0f46e066bf5d1808ef0e2359bf59910f1;hb=911f8410c05d3d16922587a59266096f3c6899e1;hpb=db8678377c7ea4fa7183a54c1bfe095e051882a8 diff --git a/src/packet.rs b/src/packet.rs index 49ae2be..632d298 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. - UnconsistentEncryptedSizeError, + 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. + 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,123 +43,175 @@ 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 +/// |0000000000000000| 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(); - PacketData { - id: random::(), - payload: Vec::from_fn(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), |_| random::()) + 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: rng.gen::(), + payload: payload } } - pub fn write(&self, output: &mut io::Writer) -> WritingResult { + 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'. + Error(_) => Vec::from_elem(16, 0) // 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)); + } }, _ => () } - + + println!("data not crypted: {}", data); + // 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()); + + println!("data crypted: {}", encrypted_data); + // 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 data_size = try_read_io!(input.read_be_u16()); + + // 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); + + 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; @@ -167,7 +221,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() { @@ -177,26 +231,26 @@ impl Packet { if mac_read != mac_data { return Err(MACMismatchError) } - + 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) { + } else if data != Vec::from_elem(16, 0) { return Err(DataError) } - match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) } - }, - _ => - return Err(UnknownPacketTypeReadError) + match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) } + } }, timestamp: timestamp })