use super::*;
use tox_binary_io::*;
use tox_crypto::*;
use crate::dht::cookie::EncryptedCookie;
use crate::dht::errors::*;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CryptoHandshake {
pub cookie: EncryptedCookie,
pub nonce: Nonce,
pub payload: Vec<u8>
}
impl FromBytes for CryptoHandshake {
named!(from_bytes<CryptoHandshake>, do_parse!(
tag!("\x1a") >>
cookie: call!(EncryptedCookie::from_bytes) >>
nonce: call!(Nonce::from_bytes) >>
payload: take!(248) >>
eof!() >>
(CryptoHandshake {
cookie,
nonce,
payload: payload.to_vec()
})
));
}
impl ToBytes for CryptoHandshake {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_be_u8!(0x1a) >>
gen_call!(|buf, cookie| EncryptedCookie::to_bytes(cookie, buf), &self.cookie) >>
gen_slice!(self.nonce.as_ref()) >>
gen_slice!(self.payload.as_slice())
)
}
}
impl CryptoHandshake {
pub fn new(shared_secret: &PrecomputedKey, payload: &CryptoHandshakePayload, cookie: EncryptedCookie) -> CryptoHandshake {
let nonce = gen_nonce();
let mut buf = [0; 232];
let (_, size) = payload.to_bytes((&mut buf, 0)).unwrap();
let payload = seal_precomputed(&buf[..size], &nonce, shared_secret);
CryptoHandshake {
cookie,
nonce,
payload,
}
}
pub fn get_payload(&self, shared_secret: &PrecomputedKey) -> Result<CryptoHandshakePayload, GetPayloadError> {
let decrypted = open_precomputed(&self.payload, &self.nonce, shared_secret)
.map_err(|()| {
GetPayloadError::decrypt()
})?;
match CryptoHandshakePayload::from_bytes(&decrypted) {
Err(error) => {
Err(GetPayloadError::deserialize(error, decrypted.clone()))
},
Ok((_, payload)) => {
Ok(payload)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CryptoHandshakePayload {
pub base_nonce: Nonce,
pub session_pk: PublicKey,
pub cookie_hash: sha512::Digest,
pub cookie: EncryptedCookie
}
impl FromBytes for CryptoHandshakePayload {
named!(from_bytes<CryptoHandshakePayload>, do_parse!(
base_nonce: call!(Nonce::from_bytes) >>
session_pk: call!(PublicKey::from_bytes) >>
cookie_hash: call!(sha512::Digest::from_bytes) >>
cookie: call!(EncryptedCookie::from_bytes) >>
eof!() >>
(CryptoHandshakePayload {
base_nonce,
session_pk,
cookie_hash,
cookie
})
));
}
impl ToBytes for CryptoHandshakePayload {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_slice!(self.base_nonce.as_ref()) >>
gen_slice!(self.session_pk.as_ref()) >>
gen_slice!(self.cookie_hash.as_ref()) >>
gen_call!(|buf, cookie| EncryptedCookie::to_bytes(cookie, buf), &self.cookie)
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use nom::{Needed, Err};
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
crypto_handshake_encode_decode,
CryptoHandshake {
cookie: EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload: vec![42; 88],
},
nonce: gen_nonce(),
payload: vec![42; 248],
}
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
crypto_handshake_payload_encode_decode,
CryptoHandshakePayload {
base_nonce: gen_nonce(),
session_pk: gen_keypair().0,
cookie_hash: sha512::hash(&[1, 2, 3]),
cookie: EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload: vec![42; 88],
},
}
);
#[test]
fn crypto_handshake_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 cookie = EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload: vec![42; 88],
};
let payload = CryptoHandshakePayload {
base_nonce: gen_nonce(),
session_pk: gen_keypair().0,
cookie_hash: sha512::hash(&[1, 2, 3]),
cookie: EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload: vec![42; 88],
},
};
let crypto_handshake = CryptoHandshake::new(&shared_secret, &payload, cookie);
let decoded_payload = crypto_handshake.get_payload(&shared_secret).unwrap();
assert_eq!(decoded_payload, payload);
}
#[test]
fn crypto_handshake_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 eve_shared_secret = encrypt_precompute(&bob_pk, &eve_sk);
let cookie = EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload: vec![42; 88],
};
let payload = CryptoHandshakePayload {
base_nonce: gen_nonce(),
session_pk: gen_keypair().0,
cookie_hash: sha512::hash(&[1, 2, 3]),
cookie: EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload: vec![42; 88],
},
};
let dht_packet = CryptoHandshake::new(&shared_secret, &payload, cookie);
let decoded_payload = dht_packet.get_payload(&eve_shared_secret);
let error = decoded_payload.err().unwrap();
assert_eq!(*error.kind(), GetPayloadErrorKind::Decrypt);
}
#[test]
fn crypto_handshake_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 cookie = EncryptedCookie {
nonce: secretbox::gen_nonce(),
payload: vec![42; 88],
};
let invalid_payload = [42; 123];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_packet = CryptoHandshake {
cookie: cookie.clone(),
nonce,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&shared_secret);
let error = decoded_payload.err().unwrap();
assert_eq!(*error.kind(), GetPayloadErrorKind::Deserialize {
error: Err::Incomplete(Needed::Size(24)),
payload: invalid_payload.to_vec()
});
let invalid_payload = [];
let invalid_payload_encoded = seal_precomputed(&invalid_payload, &nonce, &shared_secret);
let invalid_packet = CryptoHandshake {
cookie,
nonce,
payload: invalid_payload_encoded
};
let decoded_payload = invalid_packet.get_payload(&shared_secret);
let error = decoded_payload.err().unwrap();
assert_eq!(*error.kind(), GetPayloadErrorKind::Deserialize {
error: Err::Incomplete(Needed::Size(24)),
payload: invalid_payload.to_vec()
});
}
}