Change the structure of a packet.
[crypto_lab1.git] / src / packet.rs
1 use std::io;
2 use std::fmt;
3 use std::rand::{ random, task_rng, distributions };
4 use std::rand::distributions::IndependentSample;
5 use crypto;
6
7 // There are all the errors that may occur when reading an encrypted and authenticated packet.
8 #[deriving(Show)]
9 pub enum ReadingError {
10 IOReadError(io::IoError),
11 UnknownPacketTypeReadError, // If the first byte is unknown.
12 UnconsistentEncryptedSizeError,
13 UnconsistentDataSizeError, // The data size is not valid.
14 UnconsistentMACSizeError, // The MAC hasn't the correct size.
15 MACMismatchError, // The uncrypted received data doesn't match to the received MAC.
16 PaddingError, // Padding format error.
17 DataError, // The data are invalid.
18 }
19
20 // A macro to return a 'IOReadError' in case of error.
21 macro_rules! try_read_io(
22 ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(IOReadError(e)) })
23 )
24
25 // There are all the errors that may occur when encrypting, authenticating and writing a packet.
26 #[deriving(Show)]
27 pub enum WritingError {
28 WriteIOError(io::IoError)
29 // TODO...
30 }
31
32 // A macro to return a 'IOWritingError' in case of error.
33 macro_rules! try_write_io(
34 ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(WriteIOError(e)) })
35 )
36
37 pub type ReadingResult = Result<Packet, ReadingError>;
38 pub type WritingResult = Result<(), WritingError>;
39
40 static MIN_PAYLOAD_SIZE: uint = 7;
41 static MAX_PAYLOAD_SIZE: uint = 39;
42 static FIXED_PACKET_SIZE: uint = 1 + 8 + 10; // Packet type + timestamp + MAC.
43
44 #[deriving(Show)]
45 pub struct PacketData {
46 id: u8,
47 payload: Vec<u8> // The size can vary from 'MIN_PAYLOAD_SIZE' to 'MAX_PAYLOAD_SIZE' bytes.
48 }
49
50 #[deriving(Show)]
51 pub enum ErrorType {
52 CryptError,
53 AuthError
54 }
55
56 #[deriving(Show)]
57 pub enum PacketType {
58 Command(PacketData),
59 Answer(PacketData),
60 Error(ErrorType),
61 }
62
63 pub struct Packet {
64 pub t: PacketType,
65 pub timestamp: u64
66 }
67
68 impl fmt::Show for Packet {
69 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
70 fn data_to_str(data: &PacketData) -> String {
71 format!("id: {}, payload({}): {}", data.id, data.payload.len(), data.payload)
72 }
73 match self.t {
74 Command(ref data) => write!(formatter, "Command packet {{ timestamp: {}, {} }}", self.timestamp, data_to_str(data)),
75 Answer(ref data) => write!(formatter, "Answer packet {{ timestamp: {}, {} }}", self.timestamp, data_to_str(data)),
76 Error(error_type) => write!(formatter, "Error packet {{ timestamp: {}, errorType: {} }}", self.timestamp, error_type)
77 }
78 }
79 }
80
81 impl Packet {
82 pub fn random_packet_data() -> PacketData {
83 let mut rng = task_rng();
84 PacketData {
85 id: random::<u8>(),
86 payload: Vec::from_fn(distributions::Range::new(MIN_PAYLOAD_SIZE, MAX_PAYLOAD_SIZE + 1).ind_sample(&mut rng), |_| random::<u8>())
87 }
88 }
89
90 pub fn write(&self, output: &mut io::Writer) -> WritingResult {
91 fn packet_data(p: &PacketData) -> Vec<u8> {
92 let mut d = Vec::new();
93 d.push(p.id);
94 d.push_all(p.payload.as_slice());
95 d
96 }
97
98 // Data to be encrypted.
99 let mut data =
100 match self.t {
101 Command(ref p) | Answer(ref p) => packet_data(p),
102 Error(_) => Vec::from_elem(16, '0' as u8) // Padding as data: 16 * '0'.
103 };
104
105 // Compute the MAC
106 let mac = crypto::compute_mac(data.as_slice());
107
108 // Padding.
109 match self.t {
110 Command(_) | Answer(_) => {
111 let padding_size = if data.len() % 16 == 0 { 16 } else { data.len() % 16 } ;
112 data.push_all(Vec::from_elem(padding_size, padding_size as u8).as_slice());
113 },
114 _ => ()
115 }
116
117 // Encrypt.
118 let encrypted_data = crypto::encrypt(data.as_slice(), iv_from_timestamp(self.timestamp).as_slice());
119
120 // Write packet length.
121 try_write_io!(output.write_be_u16((encrypted_data.len() + FIXED_PACKET_SIZE) as u16));
122
123 // Write packet type.
124 try_write_io!(output.write_u8(
125 match self.t {
126 Command(_) => 0x00,
127 Answer(_) => 0xFF,
128 Error(CryptError) => 0x0A,
129 Error(AuthError) => 0x0B
130 }
131 ));
132
133 // Write timestamp.
134 try_write_io!(output.write_be_u64(self.timestamp));
135
136 // Write encrypted data.
137 try_write_io!(output.write(encrypted_data.as_slice()));
138
139 // Write the MAC.
140 try_write_io!(output.write(mac));
141
142 Ok(())
143 }
144
145 pub fn read(input: &mut io::Reader) -> ReadingResult {
146 let data_size = try_read_io!(input.read_be_u16());
147 let packet_type = try_read_io!(input.read_u8());
148 let timestamp = try_read_io!(input.read_be_u64());
149
150 let mut encrypted_data = Vec::from_elem(data_size as uint - FIXED_PACKET_SIZE, 0u8);
151 if try_read_io!(input.read(encrypted_data.as_mut_slice_())) != encrypted_data.len() {
152 return Err(UnconsistentEncryptedSizeError)
153 }
154 let mut data = crypto::decrypt(encrypted_data.as_slice(), iv_from_timestamp(timestamp).as_slice());
155
156 // Control the size and the content of the padding then remove it.
157 if packet_type == 0x00 || packet_type == 0xFF {
158 match data.last() {
159 Some(&padding_size) => {
160 if padding_size as uint > data.len() || !data.slice_from(data.len() - padding_size as uint).iter().any(|b| *b == padding_size) {
161 return Err(PaddingError)
162 }
163 let data_length = data.len() - padding_size as uint;
164 data.truncate(data_length);
165 },
166 None =>
167 return Err(PaddingError)
168 }
169 }
170
171 // Read an verify the MAC.
172 let mut mac_read = [0u8, ..10];
173 if try_read_io!(input.read(mac_read)) != mac_read.len() {
174 return Err(UnconsistentMACSizeError)
175 }
176 let mac_data = crypto::compute_mac(data.as_slice());
177 if mac_read != mac_data {
178 return Err(MACMismatchError)
179 }
180
181 Ok(Packet {
182 t: match packet_type {
183 0x00 | 0xFF => {
184 if data.len() < MIN_PAYLOAD_SIZE + 1 || data.len() > MAX_PAYLOAD_SIZE + 1 {
185 return Err(UnconsistentDataSizeError)
186 }
187 let pd = PacketData { id: data[0], payload: data.tail().to_vec() }; // match data.as_slice() { [id, payload..] => PacketData { id: id, payload: payload.to_vec() } };
188 match packet_type { 0x00 => Command(pd), _ => Answer(pd) }
189 },
190 0x0A | 0x0B => {
191 if data.len() != 16 {
192 return Err(UnconsistentDataSizeError)
193 } else if data != Vec::from_elem(16, '0' as u8) {
194 return Err(DataError)
195 }
196 match packet_type { 0x0A => Error(CryptError), _ => Error(AuthError) }
197 },
198 _ =>
199 return Err(UnknownPacketTypeReadError)
200 },
201 timestamp: timestamp
202 })
203 }
204 }
205
206 // Build an initialization vector: 64 * 0u8 + timestamp (128 bits).
207 fn iv_from_timestamp(timestamp: u64) -> Vec<u8> {
208 let mut iv = io::MemWriter::with_capacity(16);
209 let _ = iv.write_be_u64(0u64);
210 let _ = iv.write_be_u64(timestamp);
211 iv.unwrap()
212 }