tox_packet/dht/
cookie.rs

1/*! Cookie struct
2*/
3
4use super::*;
5use nom::number::complete::be_u64;
6
7use std::time::SystemTime;
8
9use tox_binary_io::*;
10use tox_crypto::*;
11use crate::dht::errors::*;
12
13/// Number of seconds that generated cookie is valid
14pub const COOKIE_TIMEOUT: u64 = 15;
15
16/** Cookie is a struct that holds two public keys of a node: long term key and
17short term DHT key.
18
19When Alice establishes `net_crypto` connection with Bob she sends
20`CookieRequest` packet to Bob with her public keys and receives encrypted
21`Cookie` with these keys from `CookieResponse` packet. When Alice obtains a
22`Cookie` she uses it to send `CryptoHandshake` packet. This packet will contain
23received from Bob cookie and new `Cookie` generated by Alice. Then Bob checks
24his `Coocke` and uses `Cookie` from Alice to send `CryptoHandshake` packet to
25her.
26
27Only node that encrypted a `Cookie` can decrypt it so when node gets
28`CryptoHandshake` packet with `Cookie` it can check that the sender of this
29packet received a cookie response.
30
31Cookie also contains the time when it was generated. It's considered invalid
32after 15 seconds have elapsed since the moment of generation.
33
34Serialized form:
35
36Length | Content
37------ | ------
38`8`    | Cookie timestamp
39`32`   | Long term `PublicKey`
40`32`   | DHT `PublicKey`
41
42*/
43#[derive(Clone, Debug, Eq, PartialEq)]
44pub struct Cookie {
45    /// Time when this cookie was generated
46    pub time: u64,
47    /// Long term `PublicKey`
48    pub real_pk: PublicKey,
49    /// DHT `PublicKey`
50    pub dht_pk: PublicKey,
51}
52
53impl Cookie {
54    /// Create new `Cookie`
55    pub fn new(real_pk: PublicKey, dht_pk: PublicKey) -> Cookie {
56        Cookie {
57            time: unix_time(SystemTime::now()),
58            real_pk,
59            dht_pk,
60        }
61    }
62
63    /** Check if this cookie is timed out.
64
65    Cookie considered timed out after 15 seconds since it was created.
66
67    */
68    pub fn is_timed_out(&self) -> bool {
69        self.time + COOKIE_TIMEOUT < unix_time(SystemTime::now())
70    }
71}
72
73impl FromBytes for Cookie {
74    named!(from_bytes<Cookie>, do_parse!(
75        time: be_u64 >>
76        real_pk: call!(PublicKey::from_bytes) >>
77        dht_pk: call!(PublicKey::from_bytes) >>
78        eof!() >>
79        (Cookie { time, real_pk, dht_pk })
80    ));
81}
82
83impl ToBytes for Cookie {
84    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
85        do_gen!(buf,
86            gen_be_u64!(self.time) >>
87            gen_slice!(self.real_pk.as_ref()) >>
88            gen_slice!(self.dht_pk.as_ref())
89        )
90    }
91}
92
93/** Encrypted with symmetric key `Cookie`.
94
95Serialized form:
96
97Length | Content
98------ | ------
99`24`   | Nonce
100`88`   | Payload
101
102*/
103#[derive(Clone, Debug, Eq, PartialEq)]
104pub struct EncryptedCookie {
105    /// Nonce for the current encrypted payload
106    pub nonce: secretbox::Nonce,
107    /// Encrypted payload
108    pub payload: Vec<u8>,
109}
110
111impl FromBytes for EncryptedCookie {
112    named!(from_bytes<EncryptedCookie>, do_parse!(
113        nonce: call!(secretbox::Nonce::from_bytes) >>
114        payload: take!(88) >>
115        (EncryptedCookie { nonce, payload: payload.to_vec() })
116    ));
117}
118
119impl ToBytes for EncryptedCookie {
120    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
121        do_gen!(buf,
122            gen_slice!(self.nonce.as_ref()) >>
123            gen_slice!(self.payload.as_slice())
124        )
125    }
126}
127
128impl EncryptedCookie {
129    /// Create `EncryptedCookie` from `Cookie` encrypting it with `symmetric_key`
130    pub fn new(symmetric_key: &secretbox::Key, payload: &Cookie) -> EncryptedCookie {
131        let nonce = secretbox::gen_nonce();
132        let mut buf = [0; 72];
133        let (_, size) = payload.to_bytes((&mut buf, 0)).unwrap();
134        let payload = secretbox::seal(&buf[..size], &nonce, symmetric_key);
135
136        EncryptedCookie {
137            nonce,
138            payload,
139        }
140    }
141    /** Decrypt payload with symmetric key and try to parse it as `Cookie`.
142
143    Returns `Error` in case of failure:
144
145    - fails to decrypt
146    - fails to parse `Cookie`
147    */
148    pub fn get_payload(&self, symmetric_key: &secretbox::Key) -> Result<Cookie, GetPayloadError> {
149        let decrypted = secretbox::open(&self.payload, &self.nonce, symmetric_key)
150            .map_err(|()| {
151                GetPayloadError::decrypt()
152            })?;
153        match Cookie::from_bytes(&decrypted) {
154            Err(error) => {
155                Err(GetPayloadError::deserialize(error, decrypted.clone()))
156            },
157            Ok((_, payload)) => {
158                Ok(payload)
159            }
160        }
161    }
162    /// Calculate SHA512 hash of encrypted cookie together with nonce
163    pub fn hash(&self) -> sha512::Digest {
164        let mut buf = [0; 112];
165        let (_, size) = self.to_bytes((&mut buf, 0)).unwrap();
166        sha512::hash(&buf[..size])
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use nom::{Err, error::ErrorKind};
174
175    encode_decode_test!(
176        tox_crypto::crypto_init().unwrap(),
177        cookie_encode_decode,
178        Cookie {
179            time: 12345,
180            real_pk: gen_keypair().0,
181            dht_pk: gen_keypair().0,
182        }
183    );
184
185    encode_decode_test!(
186        tox_crypto::crypto_init().unwrap(),
187        encrypted_cookie_encode_decode,
188        EncryptedCookie {
189            nonce: secretbox::gen_nonce(),
190            payload: vec![42; 88],
191        }
192    );
193
194    #[test]
195    fn cookie_encrypt_decrypt() {
196        crypto_init().unwrap();
197        let symmetric_key = secretbox::gen_key();
198        let payload = Cookie::new(gen_keypair().0, gen_keypair().0);
199        // encode payload with symmetric key
200        let encrypted_cookie = EncryptedCookie::new(&symmetric_key, &payload);
201        // decode payload with symmetric key
202        let decoded_payload = encrypted_cookie.get_payload(&symmetric_key).unwrap();
203        // payloads should be equal
204        assert_eq!(decoded_payload, payload);
205    }
206
207    #[test]
208    fn cookie_encrypt_decrypt_invalid_key() {
209        crypto_init().unwrap();
210        let symmetric_key = secretbox::gen_key();
211        let eve_symmetric_key = secretbox::gen_key();
212        let payload = Cookie::new(gen_keypair().0, gen_keypair().0);
213        // encode payload with symmetric key
214        let encrypted_cookie = EncryptedCookie::new(&symmetric_key, &payload);
215        // try to decode payload with eve's symmetric key
216        let decoded_payload = encrypted_cookie.get_payload(&eve_symmetric_key);
217        assert!(decoded_payload.is_err());
218        assert_eq!(*decoded_payload.err().unwrap().kind(), GetPayloadErrorKind::Decrypt);
219    }
220
221    #[test]
222    fn cookie_encrypt_decrypt_invalid() {
223        crypto_init().unwrap();
224        let symmetric_key = secretbox::gen_key();
225        let nonce = secretbox::gen_nonce();
226        // Try long invalid array
227        let invalid_payload = [42; 123];
228        let invalid_payload_encoded = secretbox::seal(&invalid_payload, &nonce, &symmetric_key);
229        let invalid_encrypted_cookie = EncryptedCookie {
230            nonce,
231            payload: invalid_payload_encoded
232        };
233        let decoded_payload = invalid_encrypted_cookie.get_payload(&symmetric_key);
234        let error = decoded_payload.err().unwrap();
235        assert_eq!(*error.kind(), GetPayloadErrorKind::Deserialize {
236            error: Err::Error((vec![42; 51], ErrorKind::Eof)),
237            payload: invalid_payload.to_vec()
238        });
239        // Try short incomplete array
240        let invalid_payload = [];
241        let invalid_payload_encoded = secretbox::seal(&invalid_payload, &nonce, &symmetric_key);
242        let invalid_encrypted_cookie = EncryptedCookie {
243            nonce,
244            payload: invalid_payload_encoded
245        };
246        let decoded_payload = invalid_encrypted_cookie.get_payload(&symmetric_key);
247        let error = decoded_payload.err().unwrap();
248        assert_eq!(*error.kind(), GetPayloadErrorKind::Deserialize {
249            error: Err::Error((vec![], ErrorKind::Eof)),
250            payload: invalid_payload.to_vec()
251        });
252    }
253
254    #[test]
255    fn cookie_timed_out() {
256        crypto_init().unwrap();
257        let mut cookie = Cookie::new(gen_keypair().0, gen_keypair().0);
258        assert!(!cookie.is_timed_out());
259        cookie.time -= COOKIE_TIMEOUT + 1;
260        assert!(cookie.is_timed_out());
261    }
262
263    #[test]
264    fn hash_depends_on_all_fields() {
265        crypto_init().unwrap();
266        let nonce = secretbox::gen_nonce();
267        let payload = vec![42; 88];
268        let cookie = EncryptedCookie {
269            nonce,
270            payload: payload.clone()
271        };
272
273        let cookie_1 = EncryptedCookie {
274            nonce,
275            payload: vec![43; 88]
276        };
277        let cookie_2 = EncryptedCookie {
278            nonce: secretbox::gen_nonce(),
279            payload
280        };
281
282        assert_ne!(cookie.hash(), cookie_1.hash());
283        assert_ne!(cookie.hash(), cookie_2.hash());
284    }
285}