use std::net::SocketAddr;
use crate::packet::{self, AllowedPackets, Packet};
use crate::token::{
self, CHALLENGE_TOKEN_BYTES, CONNECT_TOKEN_NONCE_BYTES, CONNECT_TOKEN_PRIVATE_BYTES,
ChallengeToken, ConnectToken, PrivateConnectToken,
};
use crate::{CONNECT_TOKEN_BYTES, KEY_BYTES, Key, MAX_PACKET_BYTES, USER_DATA_BYTES, UserData};
const VECTOR_PROTOCOL_ID: u64 = 0x1122334455667788;
const VECTOR_CLIENT_ID: u64 = 0x0102030405060708;
const VECTOR_TIMEOUT_SECONDS: i32 = 15;
const VECTOR_CREATE_TIMESTAMP: u64 = 1720000000;
const VECTOR_EXPIRE_TIMESTAMP: u64 = 1720000100;
const VECTOR_CHALLENGE_SEQUENCE: u64 = 1000;
const VECTOR_PACKET_SEQUENCE: u64 = 0x1122334455;
const VECTOR_PRIVATE_KEY: Key = [
0x60, 0x6a, 0xbe, 0x6e, 0xc9, 0x19, 0x10, 0xea, 0x9a, 0x65, 0x62, 0xf6, 0x6f, 0x2b, 0x30, 0xe4,
0x43, 0x71, 0xd6, 0x2c, 0xd1, 0x99, 0x27, 0x26, 0x6b, 0x3c, 0x60, 0xf4, 0xb7, 0x15, 0xab, 0xa1,
];
const PRIVATE_TOKEN_PLAINTEXT: &[u8; CONNECT_TOKEN_PRIVATE_BYTES] =
include_bytes!("../tests/vectors/private_connect_token_plaintext.bin");
const PRIVATE_TOKEN_ENCRYPTED: &[u8; CONNECT_TOKEN_PRIVATE_BYTES] =
include_bytes!("../tests/vectors/private_connect_token_encrypted.bin");
const CONNECT_TOKEN: &[u8; CONNECT_TOKEN_BYTES] =
include_bytes!("../tests/vectors/connect_token.bin");
const CHALLENGE_TOKEN_ENCRYPTED: &[u8; CHALLENGE_TOKEN_BYTES] =
include_bytes!("../tests/vectors/challenge_token_encrypted.bin");
const PACKET_REQUEST: &[u8] = include_bytes!("../tests/vectors/packet_request.bin");
const PACKET_DENIED: &[u8] = include_bytes!("../tests/vectors/packet_denied.bin");
const PACKET_CHALLENGE: &[u8] = include_bytes!("../tests/vectors/packet_challenge.bin");
const PACKET_RESPONSE: &[u8] = include_bytes!("../tests/vectors/packet_response.bin");
const PACKET_KEEP_ALIVE: &[u8] = include_bytes!("../tests/vectors/packet_keep_alive.bin");
const PACKET_PAYLOAD: &[u8] = include_bytes!("../tests/vectors/packet_payload.bin");
const PACKET_DISCONNECT: &[u8] = include_bytes!("../tests/vectors/packet_disconnect.bin");
fn client_to_server_key() -> Key {
std::array::from_fn(|i| i as u8)
}
fn server_to_client_key() -> Key {
std::array::from_fn(|i| 255 - i as u8)
}
fn challenge_key() -> Key {
std::array::from_fn(|i| (i * 2) as u8)
}
fn packet_key() -> Key {
std::array::from_fn(|i| (i + 17) as u8)
}
fn user_data() -> UserData {
std::array::from_fn(|i| i as u8)
}
fn connect_token_nonce() -> [u8; CONNECT_TOKEN_NONCE_BYTES] {
std::array::from_fn(|i| (100 + i) as u8)
}
fn server_addresses() -> Vec<SocketAddr> {
vec!["127.0.0.1:40000".parse().unwrap(), "[::1]:50000".parse().unwrap()]
}
fn private_token() -> PrivateConnectToken {
PrivateConnectToken {
client_id: VECTOR_CLIENT_ID,
timeout_seconds: VECTOR_TIMEOUT_SECONDS,
server_addresses: server_addresses(),
client_to_server_key: client_to_server_key(),
server_to_client_key: server_to_client_key(),
user_data: user_data(),
}
}
#[test]
fn private_connect_token_write_matches_c() {
let mut buffer = [0u8; CONNECT_TOKEN_PRIVATE_BYTES];
private_token().write(&mut buffer);
assert_eq!(&buffer, PRIVATE_TOKEN_PLAINTEXT);
}
#[test]
fn private_connect_token_encrypt_matches_c() {
let mut buffer = *PRIVATE_TOKEN_PLAINTEXT;
token::encrypt_connect_token_private(
&mut buffer,
VECTOR_PROTOCOL_ID,
VECTOR_EXPIRE_TIMESTAMP,
&connect_token_nonce(),
&VECTOR_PRIVATE_KEY,
)
.unwrap();
assert_eq!(&buffer, PRIVATE_TOKEN_ENCRYPTED);
}
#[test]
fn private_connect_token_reads_c_vector() {
let mut buffer = *PRIVATE_TOKEN_ENCRYPTED;
token::decrypt_connect_token_private(
&mut buffer,
VECTOR_PROTOCOL_ID,
VECTOR_EXPIRE_TIMESTAMP,
&connect_token_nonce(),
&VECTOR_PRIVATE_KEY,
)
.unwrap();
let output = PrivateConnectToken::read(&buffer[..]).unwrap();
let expected = private_token();
assert_eq!(output.client_id, expected.client_id);
assert_eq!(output.timeout_seconds, expected.timeout_seconds);
assert_eq!(output.server_addresses, expected.server_addresses);
assert_eq!(output.client_to_server_key, expected.client_to_server_key);
assert_eq!(output.server_to_client_key, expected.server_to_client_key);
assert_eq!(output.user_data, expected.user_data);
}
fn connect_token() -> ConnectToken {
ConnectToken {
protocol_id: VECTOR_PROTOCOL_ID,
create_timestamp: VECTOR_CREATE_TIMESTAMP,
expire_timestamp: VECTOR_EXPIRE_TIMESTAMP,
nonce: connect_token_nonce(),
private_data: Box::new(*PRIVATE_TOKEN_ENCRYPTED),
timeout_seconds: VECTOR_TIMEOUT_SECONDS,
server_addresses: server_addresses(),
client_to_server_key: client_to_server_key(),
server_to_client_key: server_to_client_key(),
}
}
#[test]
fn connect_token_write_matches_c() {
let mut buffer = [0u8; CONNECT_TOKEN_BYTES];
connect_token().write(&mut buffer);
assert_eq!(&buffer, CONNECT_TOKEN);
}
#[test]
fn connect_token_reads_c_vector() {
let output = ConnectToken::read(CONNECT_TOKEN).unwrap();
let expected = connect_token();
assert_eq!(output.protocol_id, expected.protocol_id);
assert_eq!(output.create_timestamp, expected.create_timestamp);
assert_eq!(output.expire_timestamp, expected.expire_timestamp);
assert_eq!(output.nonce, expected.nonce);
assert_eq!(output.private_data, expected.private_data);
assert_eq!(output.timeout_seconds, expected.timeout_seconds);
assert_eq!(output.server_addresses, expected.server_addresses);
assert_eq!(output.client_to_server_key, expected.client_to_server_key);
assert_eq!(output.server_to_client_key, expected.server_to_client_key);
}
#[test]
fn challenge_token_matches_c() {
let challenge_token = ChallengeToken { client_id: VECTOR_CLIENT_ID, user_data: user_data() };
let mut buffer = [0u8; CHALLENGE_TOKEN_BYTES];
challenge_token.write(&mut buffer);
token::encrypt_challenge_token(&mut buffer, VECTOR_CHALLENGE_SEQUENCE, &challenge_key())
.unwrap();
assert_eq!(&buffer, CHALLENGE_TOKEN_ENCRYPTED);
let mut buffer = *CHALLENGE_TOKEN_ENCRYPTED;
token::decrypt_challenge_token(&mut buffer, VECTOR_CHALLENGE_SEQUENCE, &challenge_key())
.unwrap();
let output = ChallengeToken::read(&buffer);
assert_eq!(output.client_id, VECTOR_CLIENT_ID);
assert_eq!(output.user_data, user_data());
}
fn packet_round_trip_c(packet: &Packet, vector: &[u8], allowed_packets: AllowedPackets) -> Packet {
let mut buffer = [0u8; MAX_PACKET_BYTES];
let written = packet::write_packet(
packet,
&mut buffer,
VECTOR_PACKET_SEQUENCE,
&packet_key(),
VECTOR_PROTOCOL_ID,
)
.unwrap();
assert_eq!(&buffer[..written], vector, "written packet bytes differ from the C vector");
let mut vector_copy = vector.to_vec();
let (output, sequence) = packet::read_packet(
&mut vector_copy,
Some(&packet_key()),
VECTOR_PROTOCOL_ID,
VECTOR_CREATE_TIMESTAMP,
Some(&VECTOR_PRIVATE_KEY),
allowed_packets,
None,
)
.expect("the C packet vector failed to read");
if !matches!(output, Packet::Request { .. }) {
assert_eq!(sequence, VECTOR_PACKET_SEQUENCE);
}
output
}
#[test]
fn connection_request_packet_matches_c() {
let packet = Packet::Request {
protocol_id: VECTOR_PROTOCOL_ID,
expire_timestamp: VECTOR_EXPIRE_TIMESTAMP,
nonce: connect_token_nonce(),
private_data: Box::new(*PRIVATE_TOKEN_ENCRYPTED),
};
let output = packet_round_trip_c(&packet, PACKET_REQUEST, AllowedPackets::SERVER);
match output {
Packet::Request { protocol_id, expire_timestamp, nonce, private_data } => {
assert_eq!(protocol_id, VECTOR_PROTOCOL_ID);
assert_eq!(expire_timestamp, VECTOR_EXPIRE_TIMESTAMP);
assert_eq!(nonce, connect_token_nonce());
assert_eq!(private_data[..1008], PRIVATE_TOKEN_PLAINTEXT[..1008]);
assert_eq!(private_data[1008..], PRIVATE_TOKEN_ENCRYPTED[1008..]);
}
_ => panic!("wrong packet type"),
}
}
#[test]
fn connection_denied_packet_matches_c() {
let output = packet_round_trip_c(&Packet::Denied, PACKET_DENIED, AllowedPackets::CLIENT);
assert!(matches!(output, Packet::Denied));
}
#[test]
fn connection_challenge_packet_matches_c() {
let packet = Packet::Challenge {
challenge_token_sequence: VECTOR_CHALLENGE_SEQUENCE,
challenge_token_data: *CHALLENGE_TOKEN_ENCRYPTED,
};
let output = packet_round_trip_c(&packet, PACKET_CHALLENGE, AllowedPackets::CLIENT);
match output {
Packet::Challenge { challenge_token_sequence, challenge_token_data } => {
assert_eq!(challenge_token_sequence, VECTOR_CHALLENGE_SEQUENCE);
assert_eq!(&challenge_token_data, CHALLENGE_TOKEN_ENCRYPTED);
}
_ => panic!("wrong packet type"),
}
}
#[test]
fn connection_response_packet_matches_c() {
let packet = Packet::Response {
challenge_token_sequence: VECTOR_CHALLENGE_SEQUENCE,
challenge_token_data: *CHALLENGE_TOKEN_ENCRYPTED,
};
let output = packet_round_trip_c(&packet, PACKET_RESPONSE, AllowedPackets::SERVER);
match output {
Packet::Response { challenge_token_sequence, challenge_token_data } => {
assert_eq!(challenge_token_sequence, VECTOR_CHALLENGE_SEQUENCE);
assert_eq!(&challenge_token_data, CHALLENGE_TOKEN_ENCRYPTED);
}
_ => panic!("wrong packet type"),
}
}
#[test]
fn connection_keep_alive_packet_matches_c() {
let packet = Packet::KeepAlive { client_index: 7, max_clients: 32 };
let output = packet_round_trip_c(&packet, PACKET_KEEP_ALIVE, AllowedPackets::CLIENT);
match output {
Packet::KeepAlive { client_index, max_clients } => {
assert_eq!(client_index, 7);
assert_eq!(max_clients, 32);
}
_ => panic!("wrong packet type"),
}
}
#[test]
fn connection_payload_packet_matches_c() {
let payload: Vec<u8> = (0..crate::MAX_PAYLOAD_BYTES).map(|i| i as u8).collect();
let packet = Packet::Payload(payload.clone());
let output = packet_round_trip_c(&packet, PACKET_PAYLOAD, AllowedPackets::CLIENT);
match output {
Packet::Payload(data) => assert_eq!(data, payload),
_ => panic!("wrong packet type"),
}
}
#[test]
fn connection_disconnect_packet_matches_c() {
let output =
packet_round_trip_c(&Packet::Disconnect, PACKET_DISCONNECT, AllowedPackets::CLIENT);
assert!(matches!(output, Packet::Disconnect));
}
#[test]
fn version_info_matches_c() {
assert_eq!(&crate::VERSION_INFO, b"NETCODE 1.02\0");
assert_eq!(KEY_BYTES, 32);
assert_eq!(USER_DATA_BYTES, 256);
assert_eq!(CONNECT_TOKEN_BYTES, 2048);
assert_eq!(CONNECT_TOKEN_PRIVATE_BYTES, 1024);
assert_eq!(CHALLENGE_TOKEN_BYTES, 300);
assert_eq!(CONNECT_TOKEN_NONCE_BYTES, 24);
}