use super::*;
use toxcore::binary_io::*;
use toxcore::crypto_core::*;
use nom::rest;
use std::io::{Error, ErrorKind};
const ONION_REQUEST_0_MIN_PAYLOAD_SIZE: usize = (SIZE_IPPORT + MACBYTES) * 2 + PUBLICKEYBYTES;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OnionRequest0 {
pub nonce: Nonce,
pub temporary_pk: PublicKey,
pub payload: Vec<u8>
}
impl FromBytes for OnionRequest0 {
named!(from_bytes<OnionRequest0>, do_parse!(
verify!(rest_len, |len| len <= ONION_MAX_PACKET_SIZE) >>
tag!(&[0x80][..]) >>
nonce: call!(Nonce::from_bytes) >>
temporary_pk: call!(PublicKey::from_bytes) >>
payload: verify!(rest, |payload: &[u8]| payload.len() >= ONION_REQUEST_0_MIN_PAYLOAD_SIZE) >>
(OnionRequest0 {
nonce,
temporary_pk,
payload: payload.to_vec()
})
));
}
impl ToBytes for OnionRequest0 {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_cond!(
self.payload.len() < ONION_REQUEST_0_MIN_PAYLOAD_SIZE,
|buf| gen_error(buf, 0)
) >>
gen_be_u8!(0x80) >>
gen_slice!(self.nonce.as_ref()) >>
gen_slice!(self.temporary_pk.as_ref()) >>
gen_slice!(self.payload) >>
gen_len_limit(ONION_MAX_PACKET_SIZE)
)
}
}
impl OnionRequest0 {
pub fn new(shared_secret: &PrecomputedKey, temporary_pk: &PublicKey, payload: OnionRequest0Payload) -> OnionRequest0 {
let nonce = gen_nonce();
let mut buf = [0; ONION_MAX_PACKET_SIZE];
let (_, size) = payload.to_bytes((&mut buf, 0)).unwrap();
let payload = seal_precomputed(&buf[..size], &nonce, shared_secret);
OnionRequest0 { nonce, temporary_pk: *temporary_pk, payload }
}
pub fn get_payload(&self, shared_secret: &PrecomputedKey) -> Result<OnionRequest0Payload, Error> {
let decrypted = open_precomputed(&self.payload, &self.nonce, shared_secret)
.map_err(|()| {
debug!("Decrypting OnionRequest0 failed!");
Error::new(ErrorKind::Other, "OnionRequest0 decrypt error.")
})?;
match OnionRequest0Payload::from_bytes(&decrypted) {
IResult::Incomplete(e) => {
debug!(target: "Onion", "OnionRequest0Payload deserialize error: {:?}", e);
Err(Error::new(ErrorKind::Other,
format!("OnionRequest0Payload deserialize error: {:?}", e)))
},
IResult::Error(e) => {
debug!(target: "Onion", "OnionRequest0Payload deserialize error: {:?}", e);
Err(Error::new(ErrorKind::Other,
format!("OnionRequest0Payload deserialize error: {:?}", e)))
},
IResult::Done(_, inner) => {
Ok(inner)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OnionRequest0Payload {
pub ip_port: IpPort,
pub temporary_pk: PublicKey,
pub inner: Vec<u8>
}
impl FromBytes for OnionRequest0Payload{
named!(from_bytes<OnionRequest0Payload>, do_parse!(
ip_port: call!(IpPort::from_udp_bytes) >>
temporary_pk: call!(PublicKey::from_bytes) >>
inner: rest >>
(OnionRequest0Payload {
ip_port,
temporary_pk,
inner: inner.to_vec()
})
));
}
impl ToBytes for OnionRequest0Payload {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_call!(|buf, ip_port| IpPort::to_udp_bytes(ip_port, buf), &self.ip_port) >>
gen_slice!(self.temporary_pk.as_ref()) >>
gen_slice!(self.inner)
)
}
}
#[cfg(test)]
mod tests {
use super::*;
encode_decode_test!(
onion_request_0_encode_decode,
OnionRequest0 {
nonce: gen_nonce(),
temporary_pk: gen_keypair().0,
payload: vec![42; ONION_REQUEST_0_MIN_PAYLOAD_SIZE]
}
);
encode_decode_test!(
onion_request_0_payload_encode_decode,
OnionRequest0Payload {
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "5.6.7.8".parse().unwrap(),
port: 12345
},
temporary_pk: gen_keypair().0,
inner: vec![42; ONION_REQUEST_0_MIN_PAYLOAD_SIZE]
}
);
#[test]
fn onion_request_0_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 payload = OnionRequest0Payload {
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "5.6.7.8".parse().unwrap(),
port: 12345
},
temporary_pk: gen_keypair().0,
inner: vec![42; ONION_REQUEST_0_MIN_PAYLOAD_SIZE]
};
let onion_packet = OnionRequest0::new(&shared_secret, &alice_pk, payload.clone());
let decoded_payload = onion_packet.get_payload(&shared_secret).unwrap();
assert_eq!(decoded_payload, payload);
}
#[test]
fn onion_request_0_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 payload = OnionRequest0Payload {
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "5.6.7.8".parse().unwrap(),
port: 12345
},
temporary_pk: gen_keypair().0,
inner: vec![42; ONION_REQUEST_0_MIN_PAYLOAD_SIZE]
};
let onion_packet = OnionRequest0::new(&shared_secret, &alice_pk, payload.clone());
let eve_shared_secret = encrypt_precompute(&bob_pk, &eve_sk);
let decoded_payload = onion_packet.get_payload(&eve_shared_secret);
assert!(decoded_payload.is_err());
}
#[test]
fn onion_request_0_decrypt_invalid() {
let (_alice_pk, alice_sk) = gen_keypair();
let (bob_pk, _bob_sk) = gen_keypair();
let shared_secret = precompute(&bob_pk, &alice_sk);
let nonce = gen_nonce();
let temporary_pk = gen_keypair().0;
let invalid_payload = [42; 123];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_onion_request_0 = OnionRequest0 {
nonce,
temporary_pk,
payload: invalid_payload_encoded
};
assert!(invalid_onion_request_0.get_payload(&shared_secret).is_err());
let invalid_payload = [];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_onion_request_0 = OnionRequest0 {
nonce,
temporary_pk,
payload: invalid_payload_encoded
};
assert!(invalid_onion_request_0.get_payload(&shared_secret).is_err());
}
}