use super::*;
use nom::number::complete::be_u64;
use std::time::SystemTime;
use tox_binary_io::*;
use tox_crypto::*;
use crate::dht::errors::*;
pub const COOKIE_TIMEOUT: u64 = 15;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Cookie {
pub time: u64,
pub real_pk: PublicKey,
pub dht_pk: PublicKey,
}
impl Cookie {
pub fn new(real_pk: PublicKey, dht_pk: PublicKey) -> Cookie {
Cookie {
time: unix_time(SystemTime::now()),
real_pk,
dht_pk,
}
}
pub fn is_timed_out(&self) -> bool {
self.time + COOKIE_TIMEOUT < unix_time(SystemTime::now())
}
}
impl FromBytes for Cookie {
named!(from_bytes<Cookie>, do_parse!(
time: be_u64 >>
real_pk: call!(PublicKey::from_bytes) >>
dht_pk: call!(PublicKey::from_bytes) >>
eof!() >>
(Cookie { time, real_pk, dht_pk })
));
}
impl ToBytes for Cookie {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u64!(self.time) >>
gen_slice!(self.real_pk.as_ref()) >>
gen_slice!(self.dht_pk.as_ref())
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EncryptedCookie {
pub nonce: secretbox::Nonce,
pub payload: Vec<u8>,
}
impl FromBytes for EncryptedCookie {
named!(from_bytes<EncryptedCookie>, do_parse!(
nonce: call!(secretbox::Nonce::from_bytes) >>
payload: take!(88) >>
(EncryptedCookie { nonce, payload: payload.to_vec() })
));
}
impl ToBytes for EncryptedCookie {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_slice!(self.nonce.as_ref()) >>
gen_slice!(self.payload.as_slice())
)
}
}
impl EncryptedCookie {
pub fn new(symmetric_key: &secretbox::Key, payload: &Cookie) -> EncryptedCookie {
let nonce = secretbox::gen_nonce();
let mut buf = [0; 72];
let (_, size) = payload.to_bytes((&mut buf, 0)).unwrap();
let payload = secretbox::seal(&buf[..size], &nonce, symmetric_key);
EncryptedCookie {
nonce,
payload,
}
}
pub fn get_payload(&self, symmetric_key: &secretbox::Key) -> Result<Cookie, GetPayloadError> {
let decrypted = secretbox::open(&self.payload, &self.nonce, symmetric_key)
.map_err(|()| {
GetPayloadError::decrypt()
})?;
match Cookie::from_bytes(&decrypted) {
Err(error) => {
Err(GetPayloadError::deserialize(error, decrypted.clone()))
},
Ok((_, payload)) => {
Ok(payload)
}
}
}
pub fn hash(&self) -> sha512::Digest {
let mut buf = [0; 112];
let (_, size) = self.to_bytes((&mut buf, 0)).unwrap();
sha512::hash(&buf[..size])
}
}
#[cfg(test)]
mod tests {
use super::*;
use nom::{Err, error::ErrorKind};
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
cookie_encode_decode,
Cookie {
time: 12345,
real_pk: gen_keypair().0,
dht_pk: gen_keypair().0,
}
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
encrypted_cookie_encode_decode,
EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload: vec![42; 88],
}
);
#[test]
fn cookie_encrypt_decrypt() {
crypto_init().unwrap();
let symmetric_key = secretbox::gen_key();
let payload = Cookie::new(gen_keypair().0, gen_keypair().0);
let encrypted_cookie = EncryptedCookie::new(&symmetric_key, &payload);
let decoded_payload = encrypted_cookie.get_payload(&symmetric_key).unwrap();
assert_eq!(decoded_payload, payload);
}
#[test]
fn cookie_encrypt_decrypt_invalid_key() {
crypto_init().unwrap();
let symmetric_key = secretbox::gen_key();
let eve_symmetric_key = secretbox::gen_key();
let payload = Cookie::new(gen_keypair().0, gen_keypair().0);
let encrypted_cookie = EncryptedCookie::new(&symmetric_key, &payload);
let decoded_payload = encrypted_cookie.get_payload(&eve_symmetric_key);
assert!(decoded_payload.is_err());
assert_eq!(*decoded_payload.err().unwrap().kind(), GetPayloadErrorKind::Decrypt);
}
#[test]
fn cookie_encrypt_decrypt_invalid() {
crypto_init().unwrap();
let symmetric_key = secretbox::gen_key();
let nonce = secretbox::gen_nonce();
let invalid_payload = [42; 123];
let invalid_payload_encoded = secretbox::seal(&invalid_payload, &nonce, &symmetric_key);
let invalid_encrypted_cookie = EncryptedCookie {
nonce,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_encrypted_cookie.get_payload(&symmetric_key);
let error = decoded_payload.err().unwrap();
assert_eq!(*error.kind(), GetPayloadErrorKind::Deserialize {
error: Err::Error((vec![42; 51], ErrorKind::Eof)),
payload: invalid_payload.to_vec()
});
let invalid_payload = [];
let invalid_payload_encoded = secretbox::seal(&invalid_payload, &nonce, &symmetric_key);
let invalid_encrypted_cookie = EncryptedCookie {
nonce,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_encrypted_cookie.get_payload(&symmetric_key);
let error = decoded_payload.err().unwrap();
assert_eq!(*error.kind(), GetPayloadErrorKind::Deserialize {
error: Err::Error((vec![], ErrorKind::Eof)),
payload: invalid_payload.to_vec()
});
}
#[test]
fn cookie_timed_out() {
crypto_init().unwrap();
let mut cookie = Cookie::new(gen_keypair().0, gen_keypair().0);
assert!(!cookie.is_timed_out());
cookie.time -= COOKIE_TIMEOUT + 1;
assert!(cookie.is_timed_out());
}
#[test]
fn hash_depends_on_all_fields() {
crypto_init().unwrap();
let nonce = secretbox::gen_nonce();
let payload = vec![42; 88];
let cookie = EncryptedCookie {
nonce,
payload: payload.clone()
};
let cookie_1 = EncryptedCookie {
nonce,
payload: vec![43; 88]
};
let cookie_2 = EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload
};
assert_ne!(cookie.hash(), cookie_1.hash());
assert_ne!(cookie.hash(), cookie_2.hash());
}
}