use std::rand::{ Rng, StdRng, SeedableRng, distributions };
use std::rand::distributions::IndependentSample;
use serialize::hex::{ ToHex };
+use self::PacketType::{ Command, Answer, Error };
use crypto;
+use utils::from_elem;
+#[derive(Show, Copy)]
pub enum Variant {
Weak, // The MAC is computed on data without padding.
Fixed // The MAC is computed on data and padding.
}
// There are all the errors that may occur when reading an encrypted and authenticated packet.
-#[deriving(Show)]
+#[derive(Show)]
pub enum ReadingError {
- IOReadError(io::IoError),
- 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
+ IO(io::IoError),
+ UnknownPacketType, // If the first byte is unknown.
+ UnconsistentEncryptedSize,
+ UnconsistentDataSize, // The data size is not valid.
+ UnconsistentMACSize, // The MAC hasn't the correct size.
+ MACMismatch, // The uncrypted received data doesn't match to the received MAC.
+ Padding, // Padding format error.
+ Data, // The data are invalid.
+ InvalidTimestamp
}
-// A macro to return a 'IOReadError' in case of error.
+// A macro to return a 'Err(ReadingError::IO(..))' in case of error.
macro_rules! try_read_io(
- ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(IOReadError(e)) })
-)
+ ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(ReadingError::IO(e)) })
+);
// There are all the errors that may occur when encrypting, authenticating and writing a packet.
-#[deriving(Show)]
+#[derive(Show)]
pub enum WritingError {
- WriteIOError(io::IoError),
- EncryptError,
+ IO(io::IoError),
+ Encrypt,
}
-// A macro to return a 'IOWritingError' in case of error.
+// A macro to return a 'Err(WritingError::IO(..))' in case of error.
macro_rules! try_write_io(
- ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(WriteIOError(e)) })
-)
+ ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(WritingError::IO(e)) })
+);
pub type ReadingResult = Result<Packet, ReadingError>;
pub type WritingResult = Result<(), WritingError>;
static MAX_PAYLOAD_SIZE: uint = 39;
static FIXED_PACKET_SIZE: uint = 1 + 8 + 10; // Packet type + timestamp + MAC.
-#[deriving(Show, Clone)]
+#[derive(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, Clone)]
+#[derive(Show, Clone)]
pub enum ErrorType {
- CryptError,
- AuthError
+ Crypt,
+ Auth
}
-#[deriving(Clone)]
+#[derive(Clone)]
pub enum PacketType {
Command(PacketData),
Answer(PacketData),
/// 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)
+/// 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:
/// for command and answer packet:
/// |I|C...C| for weak variant
/// |I|C...C|P...P|for fixed variant
/// |0000000000000000| for error packet
-#[deriving(Show)]
+#[derive(Show)]
pub struct Packet {
pub t: PacketType,
pub timestamp: u64
-}
+}
impl fmt::Show for PacketType {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
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)
+ &Error(ref error_type) => write!(formatter, "Error {{ errorType: {} }}", error_type)
}
}
}
impl Packet {
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_());
+ let mut payload = 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::<u8>(),
payload: payload
let mut data =
match self.t {
Command(ref p) | Answer(ref p) => packet_data(p),
- Error(_) => Vec::from_elem(16, 0) // Padding as data: 16 * 0.
+ Error(_) => from_elem(16, 0) // Padding as data: 16 * 0.
};
let data_size = data.len();
match self.t {
Command(_) | Answer(_) => {
let padding_size = if data.len() % 16 == 0 { 16 } else { 16 - data.len() % 16 } ;
- data.reserve_additional(padding_size);
+ data.reserve(padding_size);
for i in range(0, padding_size) {
data.push(padd_fun(i, padding_size));
}
_ => ()
}
- // Compute the MAC. It depends the choosen variant.
- let mac = crypto::compute_mac(data.slice_to(match variant { Weak => data_size, _ => data.len() }));
+ // Computes the MAC. It depends of the choosen variant.
+ let mac = crypto::compute_mac(data.slice_to(match variant { Variant::Weak => data_size, _ => data.len() }));
- // Encrypt.
+ // Encrypts.
let encrypted_data = match crypto::encrypt(data.as_slice(), iv_from_timestamp(self.timestamp).as_slice()) {
Some(d) => d,
- _ => return Err(EncryptError)
+ _ => return Err(WritingError::Encrypt)
};
- // Write packet length.
+ // Writes packet length.
try_write_io!(output.write_be_u16((encrypted_data.len() + FIXED_PACKET_SIZE) as u16));
- // Write packet type.
+ // Writes packet type.
try_write_io!(output.write_u8(
match self.t {
Command(_) => 0x00,
Answer(_) => 0xFF,
- Error(CryptError) => 0x0A,
- Error(AuthError) => 0x0B
+ Error(ErrorType::Crypt) => 0x0A,
+ Error(ErrorType::Auth) => 0x0B
}
));
- // Write timestamp.
+ // Writes timestamp.
try_write_io!(output.write_be_u64(self.timestamp));
- // Write encrypted data.
+ // Writes encrypted data.
try_write_io!(output.write(encrypted_data.as_slice()));
- // Write the MAC.
- try_write_io!(output.write(mac));
+ // Writes the MAC.
+ try_write_io!(output.write(&mac));
Ok(())
}
let data_size = try_read_io!(input.read_be_u16());
- // Read and check the packet type.
+ // Reads and checks 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)
+ return Err(ReadingError::UnknownPacketType)
}
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 encrypted_data = 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(ReadingError::UnconsistentEncryptedSize)
}
let mut data = match crypto::decrypt(encrypted_data.as_slice(), iv_from_timestamp(timestamp).as_slice()) {
Some(d) => d,
- _ => return Err(UnconsistentEncryptedSizeError)
+ _ => return Err(ReadingError::UnconsistentEncryptedSize)
};
- // Read the MAC.
- let mut mac_read = [0u8, ..10];
- if try_read_io!(input.read(mac_read)) != mac_read.len() {
- return Err(UnconsistentMACSizeError)
+ // Reads the MAC.
+ let mut mac_read = [0u8; 10];
+ if try_read_io!(input.read(&mut mac_read)) != mac_read.len() {
+ return Err(ReadingError::UnconsistentMACSize)
}
- match variant { Fixed if mac_read != crypto::compute_mac(data.as_slice()) => return Err(MACMismatchError), _ => () };
+ match variant { Variant::Fixed if mac_read != crypto::compute_mac(data.as_slice()) => return Err(ReadingError::MACMismatch), _ => () };
- // Control the size and the content of the padding then remove it.
+ // Controls the size and the content of the padding then removes it.
if packet_type == 0x00 || packet_type == 0xFF {
match data.last() {
Some(&padding_size) => {
if padding_size as uint > data.len() || padding_size == 0 || data.slice_from(data.len() - padding_size as uint).iter().any(|b| *b != padding_size) {
- return Err(PaddingError)
+ return Err(ReadingError::Padding)
}
let data_length = data.len() - padding_size as uint;
data.truncate(data_length);
},
None =>
- return Err(PaddingError)
+ return Err(ReadingError::Padding)
}
}
- match variant { Weak if mac_read != crypto::compute_mac(data.as_slice()) => return Err(MACMismatchError), _ => () };
+ match variant { Variant::Weak if mac_read != crypto::compute_mac(data.as_slice()) => return Err(ReadingError::MACMismatch), _ => () };
Ok(Packet {
t: match packet_type {
// Command or answer.
0x00 | 0xFF => {
if data.len() < MIN_PAYLOAD_SIZE + 1 || data.len() > MAX_PAYLOAD_SIZE + 1 {
- return Err(UnconsistentDataSizeError)
+ return Err(ReadingError::UnconsistentDataSize)
}
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) }
// Error.
_ => {
if data.len() != 16 {
- return Err(UnconsistentDataSizeError)
- } else if data != Vec::from_elem(16, 0) {
- return Err(DataError)
+ return Err(ReadingError::UnconsistentDataSize)
+ } else if data != from_elem(16, 0) {
+ return Err(ReadingError::Data)
}
- match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) }
+ match packet_type { 0x0A => Error(ErrorType::Crypt), _ => Error(ErrorType::Auth) }
}
},
timestamp: timestamp
}
}
-// Build an initialization vector: 64 * 0u8 + timestamp (128 bits).
+// Builds an initialization vector: 64 * 0u8 + timestamp (128 bits).
fn iv_from_timestamp(timestamp: u64) -> Vec<u8> {
let mut iv = io::MemWriter::with_capacity(16);
let _ = iv.write_be_u64(0u64);
let _ = iv.write_be_u64(timestamp);
- iv.unwrap()
+ iv.into_inner()
}