use serialize::hex::{ ToHex };
use crypto;
+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)]
pub enum ReadingError {
/// 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 and answer packet
+/// 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)]
pub struct Packet {
PacketData { id: id, payload: payload }
}
- pub fn write(&self, output: &mut io::Writer) -> WritingResult {
- self.write_with_padding_fun(output, |_, padding_length: uint| -> u8 {
+ pub fn write(&self, output: &mut io::Writer, variant: Variant) -> WritingResult {
+ self.write_with_padding_fun(output, variant, |_, padding_length: uint| -> u8 {
padding_length as u8
})
}
/// 'padd_fun' is function defining 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 {
+ pub fn write_with_padding_fun(&self, output: &mut io::Writer, variant: Variant, padd_fun: |uint, uint| -> u8) -> WritingResult {
fn packet_data(p: &PacketData) -> Vec<u8> {
let mut d = Vec::new();
d.push(p.id);
Error(_) => Vec::from_elem(16, 0) // Padding as data: 16 * 0.
};
- // Compute the MAC
- let mac = crypto::compute_mac(data.as_slice());
+ let data_size = data.len();
// Padding.
match self.t {
_ => ()
}
+ // Compute the MAC. It depends the choosen variant.
+ let mac = crypto::compute_mac(data.slice_to(match variant { Weak => data_size, _ => data.len() }));
+
// Encrypt.
let encrypted_data = match crypto::encrypt(data.as_slice(), iv_from_timestamp(self.timestamp).as_slice()) {
Some(d) => d,
Ok(())
}
- pub fn read(input: &mut io::Reader) -> ReadingResult {
+ pub fn read(input: &mut io::Reader, variant: Variant) -> ReadingResult {
fn consume(input: &mut io::Reader, nb_byte: uint) {
let _ = input.read_exact(nb_byte);
}
_ => return Err(UnconsistentEncryptedSizeError)
};
+ // Read the MAC.
+ let mut mac_read = [0u8, ..10];
+ if try_read_io!(input.read(mac_read)) != mac_read.len() {
+ return Err(UnconsistentMACSizeError)
+ }
+
+ match variant { Fixed if mac_read != crypto::compute_mac(data.as_slice()) => return Err(MACMismatchError), _ => () };
+
// 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) {
- consume(input, 10);
+ 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)
}
let data_length = data.len() - padding_size as uint;
}
}
- // 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)
- }
+ match variant { Weak if mac_read != crypto::compute_mac(data.as_slice()) => return Err(MACMismatchError), _ => () };
Ok(Packet {
t: match packet_type {