mirror of
https://gitlab.com/shadow_contributor/ntptun.git
synced 2026-10-03 03:46:50 +00:00
147 lines
5.5 KiB
C++
147 lines
5.5 KiB
C++
#include "ntptun/Carrier.hpp"
|
|
#include "ntptun/Bytes.hpp"
|
|
#include "ntptun/KeyStore.hpp"
|
|
#include "TestUtil.hpp"
|
|
|
|
using namespace ntptun;
|
|
|
|
namespace {
|
|
|
|
Key make_key(Byte seed) {
|
|
Key k{};
|
|
for (std::size_t i = 0; i < k.size(); ++i) {
|
|
k[i] = static_cast<Byte>(seed + i);
|
|
}
|
|
return k;
|
|
}
|
|
|
|
// Minimal syntactically valid IPv4 datagram whose Total Length equals its size.
|
|
ByteVector make_ipv4(std::size_t len) {
|
|
ByteVector p(len, 0);
|
|
p[0] = 0x45; // version 4, IHL 5
|
|
store_be16(p.data() + 2, static_cast<std::uint16_t>(len));
|
|
// A recognizable source address for routing tests.
|
|
p[12] = 10; p[13] = 0; p[14] = 0; p[15] = 7;
|
|
return p;
|
|
}
|
|
|
|
KeyStore store_with_default(const Key& k) {
|
|
KeyStore ks;
|
|
ks.set_default_key(k);
|
|
return ks;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void run_tests() {
|
|
const Key key = make_key(1);
|
|
const KeyStore keys = store_with_default(key);
|
|
const std::uint16_t client_id = 42;
|
|
const std::uint64_t ts = 0x1122334455667788ULL;
|
|
|
|
// --- Round trip of an IP datagram (client -> server) --------------------
|
|
{
|
|
const ByteVector inner = make_ipv4(40);
|
|
const ByteVector value = encode_carrier_value(
|
|
client_id, kKindIpDatagram, inner, key, Direction::ClientToServer, ts);
|
|
// Extension value length must be 4-byte aligned.
|
|
CHECK_EQ(value.size() % 4, static_cast<std::size_t>(0));
|
|
|
|
DecodedCarrier out;
|
|
const CarrierStatus st = decode_carrier_value(
|
|
value, keys, Direction::ClientToServer, ts, out);
|
|
CHECK_EQ(st, CarrierStatus::Ok);
|
|
CHECK_EQ(out.client_id, client_id);
|
|
CHECK_EQ(out.kind, kKindIpDatagram);
|
|
CHECK_EQ(out.inner_ip.size(), inner.size());
|
|
CHECK(out.inner_ip == inner);
|
|
}
|
|
|
|
// --- Round trip with padding (non-multiple-of-4 inner length) -----------
|
|
{
|
|
const ByteVector inner = make_ipv4(22); // 22 % 4 == 2 -> 2 pad bytes
|
|
ByteVector value = encode_carrier_value(
|
|
client_id, kKindIpDatagram, inner, key, Direction::ServerToClient, ts);
|
|
|
|
DecodedCarrier out;
|
|
CHECK_EQ(decode_carrier_value(value, keys, Direction::ServerToClient, ts, out),
|
|
CarrierStatus::Ok);
|
|
CHECK(out.inner_ip == inner);
|
|
|
|
// Tampering with any sealed byte fails Poly1305 authentication; an
|
|
// unauthenticated carrier is indistinguishable from unrelated traffic
|
|
// and therefore takes the relay path.
|
|
value.back() ^= 0xFF;
|
|
CHECK_EQ(decode_carrier_value(value, keys, Direction::ServerToClient, ts, out),
|
|
CarrierStatus::NotTunnel);
|
|
}
|
|
|
|
// --- Empty poll ---------------------------------------------------------
|
|
{
|
|
const ByteVector value = encode_carrier_value(
|
|
client_id, kKindEmptyPoll, ByteSpan{}, key, Direction::ClientToServer, ts);
|
|
DecodedCarrier out;
|
|
CHECK_EQ(decode_carrier_value(value, keys, Direction::ClientToServer, ts, out),
|
|
CarrierStatus::Ok);
|
|
CHECK_EQ(out.kind, kKindEmptyPoll);
|
|
CHECK(out.inner_ip.empty());
|
|
}
|
|
|
|
// --- Wrong key => not our tunnel (relay path) ---------------------------
|
|
{
|
|
const ByteVector inner = make_ipv4(40);
|
|
const ByteVector value = encode_carrier_value(
|
|
client_id, kKindIpDatagram, inner, key, Direction::ClientToServer, ts);
|
|
const KeyStore other = store_with_default(make_key(99));
|
|
DecodedCarrier out;
|
|
CHECK_EQ(decode_carrier_value(value, other, Direction::ClientToServer, ts, out),
|
|
CarrierStatus::NotTunnel);
|
|
}
|
|
|
|
// --- Wrong direction / wrong timestamp => not our tunnel ----------------
|
|
{
|
|
const ByteVector inner = make_ipv4(40);
|
|
const ByteVector value = encode_carrier_value(
|
|
client_id, kKindIpDatagram, inner, key, Direction::ClientToServer, ts);
|
|
DecodedCarrier out;
|
|
CHECK_EQ(decode_carrier_value(value, keys, Direction::ServerToClient, ts, out),
|
|
CarrierStatus::NotTunnel);
|
|
CHECK_EQ(decode_carrier_value(value, keys, Direction::ClientToServer, ts + 1, out),
|
|
CarrierStatus::NotTunnel);
|
|
}
|
|
|
|
// --- No key configured => relay path ------------------------------------
|
|
{
|
|
const ByteVector inner = make_ipv4(40);
|
|
const ByteVector value = encode_carrier_value(
|
|
client_id, kKindIpDatagram, inner, key, Direction::ClientToServer, ts);
|
|
KeyStore empty;
|
|
DecodedCarrier out;
|
|
CHECK_EQ(decode_carrier_value(value, empty, Direction::ClientToServer, ts, out),
|
|
CarrierStatus::NotTunnel);
|
|
}
|
|
|
|
// --- Per-client key isolation -------------------------------------------
|
|
{
|
|
KeyStore ks;
|
|
ks.set_client_key(42, make_key(1));
|
|
ks.set_client_key(43, make_key(2));
|
|
const ByteVector inner = make_ipv4(40);
|
|
// Encoded for client 42 with its key.
|
|
const ByteVector value = encode_carrier_value(
|
|
42, kKindIpDatagram, inner, make_key(1), Direction::ClientToServer, ts);
|
|
DecodedCarrier out;
|
|
CHECK_EQ(decode_carrier_value(value, ks, Direction::ClientToServer, ts, out),
|
|
CarrierStatus::Ok);
|
|
CHECK_EQ(out.client_id, static_cast<std::uint16_t>(42));
|
|
|
|
// Same bytes but claiming client 43 (id field is clear) resolves the
|
|
// wrong key, so it is not accepted as a tunnel carrier.
|
|
ByteVector spoof = value;
|
|
store_be16(spoof.data(), 43);
|
|
CHECK_EQ(decode_carrier_value(spoof, ks, Direction::ClientToServer, ts, out),
|
|
CarrierStatus::NotTunnel);
|
|
}
|
|
}
|
|
|
|
TEST_MAIN()
|