tox_packet/onion/
mod.rs

1/*! Onion UDP Packets
2*/
3
4mod onion_announce_request;
5mod onion_announce_response;
6mod inner_onion_request;
7mod inner_onion_response;
8mod onion_data_request;
9mod onion_data_response;
10mod onion_request_0;
11mod onion_request_1;
12mod onion_request_2;
13mod onion_response_1;
14mod onion_response_2;
15mod onion_response_3;
16mod friend_request;
17
18pub use self::onion_announce_request::*;
19pub use self::onion_announce_response::*;
20pub use self::inner_onion_request::*;
21pub use self::inner_onion_response::*;
22pub use self::onion_data_request::*;
23pub use self::onion_data_response::*;
24pub use self::onion_request_0::*;
25pub use self::onion_request_1::*;
26pub use self::onion_request_2::*;
27pub use self::onion_response_1::*;
28pub use self::onion_response_2::*;
29pub use self::onion_response_3::*;
30pub use self::friend_request::*;
31
32use tox_binary_io::*;
33use tox_crypto::*;
34use crate::dht::packed_node::PackedNode;
35use crate::ip_port::*;
36
37use nom::{
38    named,
39    do_parse,
40    tag,
41    call,
42    alt,
43    map,
44    map_res,
45    flat_map,
46    cond,
47    switch,
48    take,
49    value,
50    verify,
51    eof
52};
53
54use cookie_factory::{
55    do_gen,
56    gen_slice,
57    gen_call,
58    gen_cond,
59    gen_be_u8,
60    gen_le_u64,
61    gen_many_ref
62};
63
64use nom::{
65    Err,
66    number::complete::le_u8,
67    combinator::{rest, rest_len},
68};
69use std::io::{Error, ErrorKind};
70
71const ONION_SEND_BASE: usize = PUBLICKEYBYTES + SIZE_IPPORT + MACBYTES;
72const ONION_SEND_1: usize = secretbox::NONCEBYTES + ONION_SEND_BASE * 3;
73const MAX_ONION_DATA_SIZE: usize = ONION_MAX_PACKET_SIZE - (ONION_SEND_1 + 1); // 1 is for packet_id
74const MIN_ONION_DATA_REQUEST_SIZE: usize = 1 + PUBLICKEYBYTES + secretbox::NONCEBYTES + PUBLICKEYBYTES + MACBYTES; // 1 is for packet_id
75/// Maximum size in butes of Onion Data Request packet
76pub const MAX_DATA_REQUEST_SIZE: usize = MAX_ONION_DATA_SIZE - MIN_ONION_DATA_REQUEST_SIZE;
77/// Minimum size in bytes of Onion Data Response packet
78pub const MIN_ONION_DATA_RESPONSE_SIZE: usize = PUBLICKEYBYTES + MACBYTES;
79/// Maximum size in bytes of Onion Data Response inner payload
80pub const MAX_ONION_CLIENT_DATA_SIZE: usize = MAX_DATA_REQUEST_SIZE - MIN_ONION_DATA_RESPONSE_SIZE;
81
82/// Size of first `OnionReturn` struct with no inner `OnionReturn`s.
83pub const ONION_RETURN_1_SIZE: usize = secretbox::NONCEBYTES + SIZE_IPPORT + MACBYTES; // 59
84/// Size of second `OnionReturn` struct with one inner `OnionReturn`.
85pub const ONION_RETURN_2_SIZE: usize = secretbox::NONCEBYTES + SIZE_IPPORT + MACBYTES + ONION_RETURN_1_SIZE; // 118
86/// Size of third `OnionReturn` struct with two inner `OnionReturn`s.
87pub const ONION_RETURN_3_SIZE: usize = secretbox::NONCEBYTES + SIZE_IPPORT + MACBYTES + ONION_RETURN_2_SIZE; // 177
88
89/// The maximum size of onion packet including public key, nonce, packet kind
90/// byte, onion return.
91pub const ONION_MAX_PACKET_SIZE: usize = 1400;
92
93/** Encrypted onion return addresses. Payload contains encrypted with symmetric
94key `IpPort` and possibly inner `OnionReturn`.
95
96When DHT node receives OnionRequest packet it appends `OnionReturn` to the end
97of the next request packet it will send. So when DHT node receives OnionResponse
98packet it will know where to send the next response packet by decrypting
99`OnionReturn` from received packet. If node can't decrypt `OnionReturn` that
100means that onion path is expired and packet should be dropped.
101
102Serialized form:
103
104Length                | Content
105--------              | ------
106`24`                  | `Nonce`
107`35` or `94` or `153` | Payload
108
109where payload is encrypted inner `OnionReturn`
110
111*/
112#[derive(Clone, Debug, Eq, PartialEq)]
113pub struct OnionReturn {
114    /// Nonce for the current encrypted payload
115    pub nonce: secretbox::Nonce,
116    /// Encrypted payload
117    pub payload: Vec<u8>
118}
119
120impl FromBytes for OnionReturn {
121    named!(from_bytes<OnionReturn>, do_parse!(
122        nonce: call!(secretbox::Nonce::from_bytes) >>
123        payload: rest >>
124        (OnionReturn { nonce, payload: payload.to_vec() })
125    ));
126}
127
128impl ToBytes for OnionReturn {
129    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
130        do_gen!(buf,
131            gen_slice!(self.nonce.as_ref()) >>
132            gen_slice!(self.payload.as_slice())
133        )
134    }
135}
136
137impl OnionReturn {
138    #[allow(clippy::needless_pass_by_value)]
139    fn inner_to_bytes<'a>(ip_port: &IpPort, inner: Option<&OnionReturn>, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
140        do_gen!(buf,
141            gen_call!(|buf, ip_port| IpPort::to_bytes(ip_port, buf, IpPortPadding::WithPadding), ip_port) >>
142            gen_call!(|buf, inner| match inner {
143                Some(inner) => OnionReturn::to_bytes(inner, buf),
144                None => Ok(buf)
145            }, inner)
146        )
147    }
148
149    named!(inner_from_bytes<(IpPort, Option<OnionReturn>)>, do_parse!(
150        ip_port: call!(IpPort::from_bytes, IpPortPadding::WithPadding) >>
151        rest_len: rest_len >>
152        inner: cond!(rest_len > 0, OnionReturn::from_bytes) >>
153        (ip_port, inner)
154    ));
155
156    /// Create new `OnionReturn` object using symmetric key for encryption.
157    pub fn new(symmetric_key: &secretbox::Key, ip_port: &IpPort, inner: Option<&OnionReturn>) -> OnionReturn {
158        let nonce = secretbox::gen_nonce();
159        let mut buf = [0; ONION_RETURN_2_SIZE + SIZE_IPPORT];
160        let (_, size) = OnionReturn::inner_to_bytes(ip_port, inner, (&mut buf, 0)).unwrap();
161        let payload = secretbox::seal(&buf[..size], &nonce, symmetric_key);
162
163        OnionReturn { nonce, payload }
164    }
165
166    /** Decrypt payload with symmetric key and try to parse it as `IpPort` with possibly inner `OnionReturn`.
167
168    Returns `Error` in case of failure:
169
170    - fails to decrypt
171    - fails to parse as `IpPort` with possibly inner `OnionReturn`
172    */
173    pub fn get_payload(&self, symmetric_key: &secretbox::Key) -> Result<(IpPort, Option<OnionReturn>), Error> {
174        let decrypted = secretbox::open(&self.payload, &self.nonce, symmetric_key)
175            .map_err(|()| {
176                Error::new(ErrorKind::Other, "OnionReturn decrypt error.")
177            })?;
178        match OnionReturn::inner_from_bytes(&decrypted) {
179            Err(Err::Incomplete(e)) => {
180                Err(Error::new(ErrorKind::Other,
181                               format!("Inner onion return deserialize error: {:?}", e)))
182            },
183            Err(Err::Error(e)) => {
184                Err(Error::new(ErrorKind::Other,
185                               format!("Inner onion return deserialize error: {:?}", e)))
186            },
187            Err(Err::Failure(e)) => {
188                Err(Error::new(ErrorKind::Other,
189                               format!("Inner onion return deserialize error: {:?}", e)))
190            },
191            Ok((_, inner)) => {
192                Ok(inner)
193            }
194        }
195    }
196}
197
198/** Represents the result of sent `AnnounceRequest`.
199
200Also known as `is_stored` number.
201
202*/
203#[derive(Clone, Copy, Debug, Eq, PartialEq)]
204pub enum AnnounceStatus {
205    /// Failed to announce ourselves or find requested node
206    Failed = 0,
207    /// Requested node is found by its long term `PublicKey`
208    Found = 1,
209    /// We successfully announced ourselves
210    Announced = 2
211}
212
213impl FromBytes for AnnounceStatus {
214    named!(from_bytes<AnnounceStatus>, switch!(le_u8,
215        0 => value!(AnnounceStatus::Failed) |
216        1 => value!(AnnounceStatus::Found) |
217        2 => value!(AnnounceStatus::Announced)
218    ));
219}
220
221impl ToBytes for AnnounceStatus {
222    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
223        gen_be_u8!(buf, *self as u8)
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230
231    const ONION_RETURN_1_PAYLOAD_SIZE: usize = ONION_RETURN_1_SIZE - secretbox::NONCEBYTES;
232
233    encode_decode_test!(
234        tox_crypto::crypto_init().unwrap(),
235        onion_return_encode_decode,
236        OnionReturn {
237            nonce: secretbox::gen_nonce(),
238            payload: vec![42; ONION_RETURN_1_PAYLOAD_SIZE]
239        }
240    );
241
242    encode_decode_test!(
243        tox_crypto::crypto_init().unwrap(),
244        announce_status_failed,
245        AnnounceStatus::Failed
246    );
247
248    encode_decode_test!(
249        tox_crypto::crypto_init().unwrap(),
250        announce_status_found,
251        AnnounceStatus::Found
252    );
253
254    encode_decode_test!(
255        tox_crypto::crypto_init().unwrap(),
256        announce_status_accounced,
257        AnnounceStatus::Announced
258    );
259
260    #[test]
261    fn onion_return_encrypt_decrypt() {
262        crypto_init().unwrap();
263        let alice_symmetric_key = secretbox::gen_key();
264        let bob_symmetric_key = secretbox::gen_key();
265        // alice encrypt
266        let ip_port_1 = IpPort {
267            protocol: ProtocolType::UDP,
268            ip_addr: "5.6.7.8".parse().unwrap(),
269            port: 12345
270        };
271        let onion_return_1 = OnionReturn::new(&alice_symmetric_key, &ip_port_1, None);
272        // bob encrypt
273        let ip_port_2 = IpPort {
274            protocol: ProtocolType::UDP,
275            ip_addr: "7.8.5.6".parse().unwrap(),
276            port: 54321
277        };
278        let onion_return_2 = OnionReturn::new(&bob_symmetric_key, &ip_port_2, Some(&onion_return_1));
279        // bob can decrypt it's return address
280        let (decrypted_ip_port_2, decrypted_onion_return_1) = onion_return_2.get_payload(&bob_symmetric_key).unwrap();
281        assert_eq!(decrypted_ip_port_2, ip_port_2);
282        assert_eq!(decrypted_onion_return_1.unwrap(), onion_return_1);
283        // alice can decrypt it's return address
284        let (decrypted_ip_port_1, none) = onion_return_1.get_payload(&alice_symmetric_key).unwrap();
285        assert_eq!(decrypted_ip_port_1, ip_port_1);
286        assert!(none.is_none());
287    }
288
289    #[test]
290    fn onion_return_encrypt_decrypt_invalid_key() {
291        crypto_init().unwrap();
292        let alice_symmetric_key = secretbox::gen_key();
293        let bob_symmetric_key = secretbox::gen_key();
294        let eve_symmetric_key = secretbox::gen_key();
295        // alice encrypt
296        let ip_port_1 = IpPort {
297            protocol: ProtocolType::UDP,
298            ip_addr: "5.6.7.8".parse().unwrap(),
299            port: 12345
300        };
301        let onion_return_1 = OnionReturn::new(&alice_symmetric_key, &ip_port_1, None);
302        // bob encrypt
303        let ip_port_2 = IpPort {
304            protocol: ProtocolType::UDP,
305            ip_addr: "7.8.5.6".parse().unwrap(),
306            port: 54321
307        };
308        let onion_return_2 = OnionReturn::new(&bob_symmetric_key, &ip_port_2, Some(&onion_return_1));
309        // eve can't decrypt return addresses
310        assert!(onion_return_1.get_payload(&eve_symmetric_key).is_err());
311        assert!(onion_return_2.get_payload(&eve_symmetric_key).is_err());
312    }
313
314    #[test]
315    fn onion_return_decrypt_invalid() {
316        crypto_init().unwrap();
317        let symmetric_key = secretbox::gen_key();
318        let nonce = secretbox::gen_nonce();
319        // Try long invalid array
320        let invalid_payload = [42; 123];
321        let invalid_payload_encoded = secretbox::seal(&invalid_payload, &nonce, &symmetric_key);
322        let invalid_onion_return = OnionReturn {
323            nonce,
324            payload: invalid_payload_encoded
325        };
326        assert!(invalid_onion_return.get_payload(&symmetric_key).is_err());
327        // Try short incomplete array
328        let invalid_payload = [];
329        let invalid_payload_encoded = secretbox::seal(&invalid_payload, &nonce, &symmetric_key);
330        let invalid_onion_return = OnionReturn {
331            nonce,
332            payload: invalid_payload_encoded
333        };
334        assert!(invalid_onion_return.get_payload(&symmetric_key).is_err());
335    }
336}