use super::*;
use nom::{
cond, many0,
number::complete::be_u64,
combinator::rest,
};
use tox_binary_io::*;
use tox_crypto::*;
use crate::dht::errors::*;
use crate::packed_node::*;
#[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, shared_secret: &PrecomputedKey) -> Result<DhtRequestPayload, GetPayloadError> {
let decrypted = open_precomputed(&self.payload, &self.nonce, shared_secret)
.map_err(|()| {
GetPayloadError::decrypt()
})?;
match DhtRequestPayload::from_bytes(&decrypted) {
Err(error) => {
Err(GetPayloadError::deserialize(error, decrypted.clone()))
},
Ok((_, payload)) => {
Ok(payload)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DhtRequestPayload {
NatPingRequest(NatPingRequest),
NatPingResponse(NatPingResponse),
DhtPkAnnounce(DhtPkAnnounce),
HardeningRequest(HardeningRequest),
HardeningResponse(HardeningResponse),
}
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),
DhtRequestPayload::HardeningRequest(ref p) => p.to_bytes(buf),
DhtRequestPayload::HardeningResponse(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) |
map!(HardeningRequest::from_bytes, DhtRequestPayload::HardeningRequest) |
map!(HardeningResponse::from_bytes, DhtRequestPayload::HardeningResponse)
));
}
#[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 real_pk: PublicKey,
pub nonce: Nonce,
pub payload: Vec<u8>
}
impl FromBytes for DhtPkAnnounce {
named!(from_bytes<DhtPkAnnounce>, do_parse!(
tag!(&[0x9c][..]) >>
real_pk: call!(PublicKey::from_bytes) >>
nonce: call!(Nonce::from_bytes) >>
payload: rest >>
(DhtPkAnnounce { real_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.real_pk.as_ref()) >>
gen_slice!(self.nonce.as_ref()) >>
gen_slice!(self.payload.as_slice())
)
}
}
impl DhtPkAnnounce {
pub fn new(shared_secret: &PrecomputedKey, real_pk: PublicKey, payload: &DhtPkAnnouncePayload) -> DhtPkAnnounce {
let nonce = gen_nonce();
let mut buf = [0; 245];
let (_, size) = payload.to_bytes((&mut buf, 0)).unwrap();
let payload = seal_precomputed(&buf[..size], &nonce, shared_secret);
DhtPkAnnounce {
real_pk,
nonce,
payload,
}
}
pub fn get_payload(&self, shared_secret: &PrecomputedKey) -> Result<DhtPkAnnouncePayload, GetPayloadError> {
let decrypted = open_precomputed(&self.payload, &self.nonce, shared_secret)
.map_err(|()| {
GetPayloadError::decrypt()
})?;
match DhtPkAnnouncePayload::from_bytes(&decrypted) {
Err(error) => {
Err(GetPayloadError::deserialize(error, decrypted.clone()))
},
Ok((_, payload)) => {
Ok(payload)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DhtPkAnnouncePayload {
pub no_reply: u64,
pub dht_pk: PublicKey,
pub nodes: Vec<TcpUdpPackedNode>,
}
impl FromBytes for DhtPkAnnouncePayload {
named!(from_bytes<DhtPkAnnouncePayload>, do_parse!(
tag!(&[0x9c][..]) >>
no_reply: be_u64 >>
dht_pk: call!(PublicKey::from_bytes) >>
nodes: many0!(TcpUdpPackedNode::from_bytes) >>
cond!(nodes.len() <= 4, eof!()) >>
(DhtPkAnnouncePayload {
no_reply,
dht_pk,
nodes,
})
));
}
impl ToBytes for DhtPkAnnouncePayload {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0x9c) >>
gen_be_u64!(self.no_reply) >>
gen_slice!(self.dht_pk.as_ref()) >>
gen_cond!(
self.nodes.len() <= 4,
gen_many_ref!(&self.nodes, |buf, node| TcpUdpPackedNode::to_bytes(node, buf))
)
)
}
}
impl DhtPkAnnouncePayload {
pub fn new(dht_pk: PublicKey, nodes: Vec<TcpUdpPackedNode>) -> Self {
use std::time::SystemTime;
DhtPkAnnouncePayload {
no_reply: unix_time(SystemTime::now()),
dht_pk,
nodes,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct HardeningRequest;
impl FromBytes for HardeningRequest {
named!(from_bytes<HardeningRequest>, do_parse!(
tag!("\x30") >>
tag!("\x02") >>
rest >>
(HardeningRequest)
));
}
impl ToBytes for HardeningRequest {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0x30) >>
gen_be_u8!(0x02)
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HardeningResponse;
impl FromBytes for HardeningResponse {
named!(from_bytes<HardeningResponse>, do_parse!(
tag!("\x30") >>
tag!("\x03") >>
rest >>
(HardeningResponse)
));
}
impl ToBytes for HardeningResponse {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0x30) >>
gen_be_u8!(0x03)
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use nom::{Needed, Err, error::ErrorKind};
use crate::ip_port::*;
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
nat_ping_request_encode_decode,
DhtRequestPayload::NatPingRequest(NatPingRequest { id: 42 })
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
nat_ping_response_encode_decode,
DhtRequestPayload::NatPingResponse(NatPingResponse { id: 42 })
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
dht_pk_announce_encode_decode,
DhtRequestPayload::DhtPkAnnounce(DhtPkAnnounce {
real_pk: gen_keypair().0,
nonce: gen_nonce(),
payload: vec![42; 123]
})
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
hardening_request_encode_decode,
DhtRequestPayload::HardeningRequest(HardeningRequest)
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
hardening_response_encode_decode,
DhtRequestPayload::HardeningResponse(HardeningResponse)
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
dht_pk_announce_payload_encode_decode,
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,
},
TcpUdpPackedNode {
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "127.0.0.1".parse().unwrap(),
port: 12346,
},
pk: gen_keypair().0,
},
TcpUdpPackedNode {
ip_port: IpPort {
protocol: ProtocolType::TCP,
ip_addr: "127.0.0.2".parse().unwrap(),
port: 12345,
},
pk: gen_keypair().0,
},
TcpUdpPackedNode {
ip_port: IpPort {
protocol: ProtocolType::TCP,
ip_addr: "127.0.0.2".parse().unwrap(),
port: 12346,
},
pk: gen_keypair().0,
},
],
}
);
#[test]
fn dht_request_payload_encrypt_decrypt() {
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 test_payloads = vec![
DhtRequestPayload::NatPingRequest(NatPingRequest { id: 42 }),
DhtRequestPayload::NatPingResponse(NatPingResponse { id: 42 }),
DhtRequestPayload::DhtPkAnnounce(DhtPkAnnounce { real_pk: gen_keypair().0, nonce: gen_nonce(), payload: vec![42; 123] }),
DhtRequestPayload::HardeningRequest(HardeningRequest),
DhtRequestPayload::HardeningResponse(HardeningResponse)
];
for payload in test_payloads {
let dht_request = DhtRequest::new(&shared_secret, &bob_pk, &alice_pk, &payload);
let precomputed_key = precompute(&dht_request.spk, &bob_sk);
let decoded_payload = dht_request.get_payload(&precomputed_key).unwrap();
assert_eq!(decoded_payload, payload);
}
}
#[test]
fn dht_request_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 shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let test_payloads = vec![
DhtRequestPayload::NatPingRequest(NatPingRequest { id: 42 }),
DhtRequestPayload::NatPingResponse(NatPingResponse { id: 42 }),
DhtRequestPayload::DhtPkAnnounce(DhtPkAnnounce { real_pk: gen_keypair().0, nonce: gen_nonce(), payload: vec![42; 123] }),
DhtRequestPayload::HardeningRequest(HardeningRequest),
DhtRequestPayload::HardeningResponse(HardeningResponse)
];
for payload in test_payloads {
let dht_request = DhtRequest::new(&shared_secret, &bob_pk, &alice_pk, &payload);
let precomputed_key = precompute(&dht_request.spk, &eve_sk);
let decoded_payload = dht_request.get_payload(&precomputed_key);
let error = decoded_payload.err().unwrap();
assert_eq!(*error.kind(), GetPayloadErrorKind::Decrypt);
}
}
#[test]
fn dht_request_decode_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 = DhtRequest {
rpk: bob_pk,
spk: alice_pk,
nonce,
payload: invalid_payload_encoded
};
let precomputed_key = precompute(&alice_pk, &bob_sk);
let decoded_payload = invalid_packet.get_payload(&precomputed_key);
let error = decoded_payload.err().unwrap();
assert_eq!(*error.kind(), GetPayloadErrorKind::Deserialize {
error: Err::Error((invalid_payload.to_vec(), ErrorKind::Alt)),
payload: invalid_payload.to_vec()
});
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(&precomputed_key);
let error = decoded_payload.err().unwrap();
assert_eq!(*error.kind(), GetPayloadErrorKind::Deserialize {
error: Err::Incomplete(Needed::Size(1)),
payload: invalid_payload.to_vec()
});
}
#[test]
fn dht_pk_announce_payload_encrypt_decrypt() {
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 payload = DhtPkAnnouncePayload::new(
gen_keypair().0,
vec![
TcpUdpPackedNode {
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "127.0.0.1".parse().unwrap(),
port: 12345,
},
pk: gen_keypair().0,
},
],
);
let packet = DhtPkAnnounce::new(&shared_secret, alice_pk, &payload);
let decoded_payload = packet.get_payload(&shared_secret).unwrap();
assert_eq!(decoded_payload, payload);
}
#[test]
fn dht_pk_announce_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 shared_secret = encrypt_precompute(&bob_pk, &alice_sk);
let shared_secret_invalid = encrypt_precompute(&bob_pk, &eve_sk);
let payload = DhtPkAnnouncePayload::new(
gen_keypair().0,
vec![
TcpUdpPackedNode {
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "127.0.0.1".parse().unwrap(),
port: 12345,
},
pk: gen_keypair().0,
},
],
);
let packet = DhtPkAnnounce::new(&shared_secret, alice_pk, &payload);
let decoded_payload = packet.get_payload(&shared_secret_invalid);
assert!(decoded_payload.is_err());
}
#[test]
fn dht_pk_announce_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 = DhtPkAnnounce {
real_pk: alice_pk,
nonce,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&shared_secret);
assert!(decoded_payload.is_err());
let invalid_payload = [];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_packet = DhtPkAnnounce {
real_pk: alice_pk,
nonce,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&shared_secret);
assert!(decoded_payload.is_err());
}
}