Begining of some test cases.
[crypto_lab1.git] / src / packet.rs
index 49ae2be..74bf102 100644 (file)
@@ -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.
+   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.
@@ -41,53 +43,74 @@ 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<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);
@@ -143,8 +166,19 @@ impl Packet {
    }
    
    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);      
@@ -180,23 +214,23 @@ impl Packet {
       
       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
       })