use nom::{be_u64, rest};
use std::io::{Error, ErrorKind};
use toxcore::binary_io::*;
use toxcore::crypto_core::*;
use toxcore::dht::codec::*;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DhtRequest {
pub rpk: PublicKey,
pub spk: PublicKey,
pub nonce: Nonce,
pub payload: Vec<u8>,
}
impl ToBytes for DhtRequest {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0x20) >>
gen_slice!(self.rpk.as_ref()) >>
gen_slice!(self.spk.as_ref()) >>
gen_slice!(self.nonce.as_ref()) >>
gen_slice!(self.payload.as_slice())
)
}
}
impl FromBytes for DhtRequest {
named!(from_bytes<DhtRequest>, do_parse!(
tag!("\x20") >>
rpk: call!(PublicKey::from_bytes) >>
spk: call!(PublicKey::from_bytes) >>
nonce: call!(Nonce::from_bytes) >>
payload: map!(rest, |bytes| bytes.to_vec() ) >>
(DhtRequest { rpk, spk, nonce, payload })
));
}
impl DhtRequest {
pub fn new(shared_secret: &PrecomputedKey, rpk: &PublicKey, spk: &PublicKey, dp: DhtRequestPayload) -> DhtRequest {
let nonce = gen_nonce();
let mut buf = [0; MAX_DHT_PACKET_SIZE];
let (_, size) = dp.to_bytes((&mut buf, 0)).unwrap();
let payload = seal_precomputed(&buf[..size], &nonce, shared_secret);
DhtRequest {
rpk: *rpk,
spk: *spk,
nonce,
payload,
}
}
pub fn get_payload(&self, own_secret_key: &SecretKey) -> Result<DhtRequestPayload, Error>
{
debug!(target: "DhtRequest", "Getting packet data from DhtRequest.");
trace!(target: "DhtRequest", "With DhtRequest: {:?}", self);
let decrypted = open(&self.payload, &self.nonce, &self.spk,
own_secret_key)
.map_err(|()| {
debug!("Decrypting DhtRequest failed!");
Error::new(ErrorKind::Other, "DhtRequest decrypt error.")
});
match DhtRequestPayload::from_bytes(&decrypted?) {
IResult::Incomplete(e) => {
debug!(target: "DhtRequest", "DhtRequest deserialize error: {:?}", e);
Err(Error::new(ErrorKind::Other,
format!("DhtRequest deserialize error: {:?}", e)))
},
IResult::Error(e) => {
debug!(target: "DhtRequest", "DhtRequest deserialize error: {:?}", e);
Err(Error::new(ErrorKind::Other,
format!("DhtRequest deserialize error: {:?}", e)))
},
IResult::Done(_, packet) => {
Ok(packet)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DhtRequestPayload {
NatPingRequest(NatPingRequest),
NatPingResponse(NatPingResponse),
DhtPkAnnounce(DhtPkAnnounce),
}
impl ToBytes for DhtRequestPayload {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
match *self {
DhtRequestPayload::NatPingRequest(ref p) => p.to_bytes(buf),
DhtRequestPayload::NatPingResponse(ref p) => p.to_bytes(buf),
DhtRequestPayload::DhtPkAnnounce(ref p) => p.to_bytes(buf),
}
}
}
impl FromBytes for DhtRequestPayload {
named!(from_bytes<DhtRequestPayload>, alt!(
map!(NatPingRequest::from_bytes, DhtRequestPayload::NatPingRequest) |
map!(NatPingResponse::from_bytes, DhtRequestPayload::NatPingResponse) |
map!(DhtPkAnnounce::from_bytes, DhtRequestPayload::DhtPkAnnounce)
));
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct NatPingRequest {
pub id: u64,
}
impl FromBytes for NatPingRequest {
named!(from_bytes<NatPingRequest>, do_parse!(
tag!(&[0xfe][..]) >>
tag!("\x00") >>
id: be_u64 >>
(NatPingRequest { id })
));
}
impl ToBytes for NatPingRequest {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0xfe) >>
gen_be_u8!(0x00) >>
gen_be_u64!(self.id)
)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct NatPingResponse {
pub id: u64,
}
impl FromBytes for NatPingResponse {
named!(from_bytes<NatPingResponse>, do_parse!(
tag!(&[0xfe][..]) >>
tag!("\x01") >>
id: be_u64 >>
(NatPingResponse { id })
));
}
impl ToBytes for NatPingResponse {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0xfe) >>
gen_be_u8!(0x01) >>
gen_be_u64!(self.id)
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DhtPkAnnounce {
pub pk: PublicKey,
pub nonce: Nonce,
pub payload: Vec<u8>
}
impl FromBytes for DhtPkAnnounce {
named!(from_bytes<DhtPkAnnounce>, do_parse!(
tag!(&[0x9c][..]) >>
pk: call!(PublicKey::from_bytes) >>
nonce: call!(Nonce::from_bytes) >>
payload: rest >>
(DhtPkAnnounce { pk, nonce, payload: payload.to_vec() })
));
}
impl ToBytes for DhtPkAnnounce {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0x9c) >>
gen_slice!(self.pk.as_ref()) >>
gen_slice!(self.nonce.as_ref()) >>
gen_slice!(self.payload.as_slice())
)
}
}
#[cfg(test)]
mod tests {
use toxcore::dht::packet::dht_request::*;
encode_decode_test!(
nat_ping_request_payload_encode_decode,
DhtRequestPayload::NatPingRequest(NatPingRequest { id: 42 })
);
encode_decode_test!(
nat_ping_response_payload_encode_decode,
DhtRequestPayload::NatPingResponse(NatPingResponse { id: 42 })
);
encode_decode_test!(
dht_pk_announce_payload_encode_decode,
DhtRequestPayload::DhtPkAnnounce(DhtPkAnnounce {
pk: gen_keypair().0,
nonce: gen_nonce(),
payload: vec![42; 123]
})
);
#[test]
fn dht_request_payload_encrypt_decrypt() {
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, bob_sk) = gen_keypair();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let test_payloads = vec![
DhtRequestPayload::NatPingRequest(NatPingRequest { id: 42 }),
DhtRequestPayload::NatPingResponse(NatPingResponse { id: 42 })
];
for payload in test_payloads {
let dht_request = DhtRequest::new(&shared_secret, &bob_pk, &alice_pk, payload.clone());
let decoded_payload = dht_request.get_payload(&bob_sk).unwrap();
assert_eq!(decoded_payload, payload);
}
}
#[test]
fn dht_request_payload_encrypt_decrypt_invalid_key() {
let (alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let (_eve_pk, eve_sk) = gen_keypair();
let shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let test_payloads = vec![
DhtRequestPayload::NatPingRequest(NatPingRequest { id: 42 }),
DhtRequestPayload::NatPingResponse(NatPingResponse { id: 42 })
];
for payload in test_payloads {
let dht_request = DhtRequest::new(&shared_secret, &bob_pk, &alice_pk, payload.clone());
let decoded_payload = dht_request.get_payload(&eve_sk);
assert!(decoded_payload.is_err());
}
}
#[test]
fn dht_request_decode_invalid() {
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 = DhtRequest {
rpk: bob_pk,
spk: alice_pk,
nonce,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&bob_sk);
assert!(decoded_payload.is_err());
let invalid_payload = [0xfe];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_packet = DhtRequest {
rpk: bob_pk,
spk: alice_pk,
nonce,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&bob_sk);
assert!(decoded_payload.is_err());
}
}