1use 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
13pub const COOKIE_TIMEOUT: u64 = 15;
15
16#[derive(Clone, Debug, Eq, PartialEq)]
44pub struct Cookie {
45 pub time: u64,
47 pub real_pk: PublicKey,
49 pub dht_pk: PublicKey,
51}
52
53impl Cookie {
54 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 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#[derive(Clone, Debug, Eq, PartialEq)]
104pub struct EncryptedCookie {
105 pub nonce: secretbox::Nonce,
107 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 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 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 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 let encrypted_cookie = EncryptedCookie::new(&symmetric_key, &payload);
201 let decoded_payload = encrypted_cookie.get_payload(&symmetric_key).unwrap();
203 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 let encrypted_cookie = EncryptedCookie::new(&symmetric_key, &payload);
215 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 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 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}