use super::*;
use tox_binary_io::*;
use tox_crypto::*;
use crate::dht::*;
use nom::combinator::{rest, rest_len};
pub const MAX_ONION_RESPONSE_PAYLOAD_SIZE: usize = MAX_ONION_CLIENT_DATA_SIZE + PUBLICKEYBYTES + MACBYTES;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OnionDataResponse {
pub nonce: Nonce,
pub temporary_pk: PublicKey,
pub payload: Vec<u8>
}
impl FromBytes for OnionDataResponse {
named!(from_bytes<OnionDataResponse>, do_parse!(
verify!(rest_len, |len| *len <= ONION_MAX_PACKET_SIZE) >>
tag!(&[0x86][..]) >>
nonce: call!(Nonce::from_bytes) >>
temporary_pk: call!(PublicKey::from_bytes) >>
payload: rest >>
(OnionDataResponse {
nonce,
temporary_pk,
payload: payload.to_vec()
})
));
}
impl ToBytes for OnionDataResponse {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0x86) >>
gen_slice!(self.nonce.as_ref()) >>
gen_slice!(self.temporary_pk.as_ref()) >>
gen_slice!(self.payload.as_slice()) >>
gen_len_limit(ONION_MAX_PACKET_SIZE)
)
}
}
impl OnionDataResponse {
pub fn new(shared_secret: &PrecomputedKey, temporary_pk: PublicKey, nonce: Nonce, payload: &OnionDataResponsePayload) -> OnionDataResponse {
let mut buf = [0; MAX_ONION_RESPONSE_PAYLOAD_SIZE];
let (_, size) = payload.to_bytes((&mut buf, 0)).unwrap();
let payload = seal_precomputed(&buf[..size], &nonce, shared_secret);
OnionDataResponse {
nonce,
temporary_pk,
payload,
}
}
pub fn get_payload(&self, shared_secret: &PrecomputedKey) -> Result<OnionDataResponsePayload, GetPayloadError> {
let decrypted = open_precomputed(&self.payload, &self.nonce, shared_secret)
.map_err(|()| {
GetPayloadError::decrypt()
})?;
match OnionDataResponsePayload::from_bytes(&decrypted) {
Err(error) => {
Err(GetPayloadError::deserialize(error, decrypted.clone()))
},
Ok((_, inner)) => {
Ok(inner)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OnionDataResponsePayload {
pub real_pk: PublicKey,
pub payload: Vec<u8>,
}
impl FromBytes for OnionDataResponsePayload {
named!(from_bytes<OnionDataResponsePayload>, do_parse!(
real_pk: call!(PublicKey::from_bytes) >>
payload: rest >>
(OnionDataResponsePayload {
real_pk,
payload: payload.to_vec()
})
));
}
impl ToBytes for OnionDataResponsePayload {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_slice!(self.real_pk.as_ref()) >>
gen_slice!(self.payload.as_slice())
)
}
}
impl OnionDataResponsePayload {
pub fn new(shared_secret: &PrecomputedKey, real_pk: PublicKey, nonce: &Nonce, payload: &OnionDataResponseInnerPayload) -> OnionDataResponsePayload {
let mut buf = [0; MAX_ONION_CLIENT_DATA_SIZE];
let (_, size) = payload.to_bytes((&mut buf, 0)).unwrap();
let payload = seal_precomputed(&buf[..size], nonce, shared_secret);
OnionDataResponsePayload {
real_pk,
payload,
}
}
pub fn get_payload(&self, nonce: &Nonce, shared_secret: &PrecomputedKey) -> Result<OnionDataResponseInnerPayload, GetPayloadError> {
let decrypted = open_precomputed(&self.payload, nonce, shared_secret)
.map_err(|()| {
GetPayloadError::decrypt()
})?;
match OnionDataResponseInnerPayload::from_bytes(&decrypted) {
Err(error) => {
Err(GetPayloadError::deserialize(error, decrypted.clone()))
},
Ok((_, inner)) => {
Ok(inner)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OnionDataResponseInnerPayload {
DhtPkAnnounce(DhtPkAnnouncePayload),
FriendRequest(FriendRequest),
}
impl FromBytes for OnionDataResponseInnerPayload {
named!(from_bytes<OnionDataResponseInnerPayload>, alt!(
map!(DhtPkAnnouncePayload::from_bytes, OnionDataResponseInnerPayload::DhtPkAnnounce) |
map!(FriendRequest::from_bytes, OnionDataResponseInnerPayload::FriendRequest)
));
}
impl ToBytes for OnionDataResponseInnerPayload {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
match *self {
OnionDataResponseInnerPayload::DhtPkAnnounce(ref p) => p.to_bytes(buf),
OnionDataResponseInnerPayload::FriendRequest(ref p) => p.to_bytes(buf),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packed_node::*;
use crate::toxid::NoSpam;
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
onion_data_response_encode_decode,
OnionDataResponse {
nonce: gen_nonce(),
temporary_pk: gen_keypair().0,
payload: vec![42; 123]
}
);
#[test]
fn onion_data_response_encrypt_decrypt() {
crypto_init().unwrap();
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let nonce = gen_nonce();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let payload = OnionDataResponsePayload {
real_pk: gen_keypair().0,
payload: vec![42; 123],
};
let packet = OnionDataResponse::new(&shared_secret, alice_pk, nonce, &payload);
let decoded_payload = packet.get_payload(&shared_secret).unwrap();
assert_eq!(decoded_payload, payload);
}
#[test]
fn onion_data_response_encrypt_decrypt_invalid_key() {
crypto_init().unwrap();
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let (_eve_pk, eve_sk) = gen_keypair();
let nonce = gen_nonce();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let shared_secret_invalid = encrypt_precompute(&bob_pk, &eve_sk);
let payload = OnionDataResponsePayload {
real_pk: gen_keypair().0,
payload: vec![42; 123],
};
let packet = OnionDataResponse::new(&shared_secret, alice_pk, nonce, &payload);
let decoded_payload = packet.get_payload(&shared_secret_invalid);
assert!(decoded_payload.is_err());
}
#[test]
fn onion_data_response_encrypt_decrypt_invalid() {
crypto_init().unwrap();
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let nonce = gen_nonce();
let invalid_payload = [];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_packet = OnionDataResponse {
nonce,
temporary_pk: alice_pk,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&shared_secret);
assert!(decoded_payload.is_err());
}
#[test]
fn onion_data_response_payload_encrypt_decrypt() {
crypto_init().unwrap();
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let nonce = gen_nonce();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let payload = OnionDataResponseInnerPayload::DhtPkAnnounce(DhtPkAnnouncePayload {
no_reply: 42,
dht_pk: gen_keypair().0,
nodes: vec![
TcpUdpPackedNode {
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "127.0.0.1".parse().unwrap(),
port: 12345
},
pk: gen_keypair().0,
},
],
});
let packet = OnionDataResponsePayload::new(&shared_secret, alice_pk, &nonce, &payload);
let decoded_payload = packet.get_payload(&nonce, &shared_secret).unwrap();
assert_eq!(decoded_payload, payload);
}
#[test]
fn onion_data_response_payload_encrypt_decrypt_invalid_key() {
crypto_init().unwrap();
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let (_eve_pk, eve_sk) = gen_keypair();
let nonce = gen_nonce();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let shared_secret_invalid = encrypt_precompute(&bob_pk, &eve_sk);
let payload = OnionDataResponseInnerPayload::DhtPkAnnounce(DhtPkAnnouncePayload {
no_reply: 42,
dht_pk: gen_keypair().0,
nodes: vec![
TcpUdpPackedNode {
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "127.0.0.1".parse().unwrap(),
port: 12345
},
pk: gen_keypair().0,
},
],
});
let packet = OnionDataResponsePayload::new(&shared_secret, alice_pk, &nonce, &payload);
let decoded_payload = packet.get_payload(&nonce, &shared_secret_invalid);
assert!(decoded_payload.is_err());
}
#[test]
fn onion_data_response_payload_encrypt_decrypt_invalid() {
crypto_init().unwrap();
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let nonce = gen_nonce();
let invalid_payload = [42; 123];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_packet = OnionDataResponsePayload {
real_pk: alice_pk,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&nonce, &shared_secret);
assert!(decoded_payload.is_err());
let invalid_payload = [];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_packet = OnionDataResponsePayload {
real_pk: alice_pk,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&nonce, &shared_secret);
assert!(decoded_payload.is_err());
}
#[test]
fn onion_data_response_payload_encrypt_decrypt_friend_request() {
crypto_init().unwrap();
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let nonce = gen_nonce();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let friend_request = FriendRequest::new(NoSpam::random(), "1234".to_owned());
let payload = OnionDataResponseInnerPayload::FriendRequest(friend_request);
let packet = OnionDataResponsePayload::new(&shared_secret, alice_pk, &nonce, &payload);
let decoded_payload = packet.get_payload(&nonce, &shared_secret).unwrap();
assert_eq!(payload, decoded_payload);
}
}