0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-19 11:42:15 +02:00

Differentiate Packet ID types into data channel and control channel ids

Data channel packet ids (in the formats that OpenVPN 3.x supports)
are plain 32 or 64 bit ids while control channel is a 32 bit time + 32
bit counter id. Seperate these more clearly and let CBC mode use the
same Packet ID implementation that AEAD mode uses.

Also add more unit tests related to data channel tests packets by
adapting the control channel test where applicable and add a few more
related to packet id wrapping

Signed-off-by: Arne Schwabe <arne@openvpn.net>
This commit is contained in:
Arne Schwabe 2024-08-16 11:52:42 +02:00 committed by Jenkins-dev
parent 16b2c4afe0
commit c78aaecad7
18 changed files with 536 additions and 303 deletions

View File

@ -32,7 +32,7 @@
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id_aead.hpp>
#include <openvpn/crypto/packet_id_data.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/crypto/cryptodc.hpp>
@ -85,12 +85,12 @@ class Crypto : public CryptoDCInstance
}
// for encrypt
Nonce(const Nonce &ref, PacketIDAEADSend &pid_send, const unsigned char *op32)
Nonce(const Nonce &ref, PacketIDDataSend &pid_send, const unsigned char *op32)
{
/** Copy op code and tail of packet ID */
std::memcpy(data, ref.data, sizeof(data));
Buffer buf(data + data_offset_pkt_id, PacketIDAEAD::long_id_size, false);
Buffer buf(data + data_offset_pkt_id, PacketIDData::long_id_size, false);
pid_send.write_next(buf);
if (op32)
{
@ -102,13 +102,13 @@ class Crypto : public CryptoDCInstance
}
// for encrypt
void prepend_ad(Buffer &buf, const PacketIDAEADSend &pid_send) const
void prepend_ad(Buffer &buf, const PacketIDDataSend &pid_send) const
{
buf.prepend(data + data_offset_pkt_id, pid_send.length());
}
// for decrypt
Nonce(const Nonce &ref, const PacketIDAEADReceive &recv_pid, Buffer &buf, const unsigned char *op32)
Nonce(const Nonce &ref, const PacketIDDataReceive &recv_pid, Buffer &buf, const unsigned char *op32)
{
/* Copy opcode and tail of packet ID */
std::memcpy(data, ref.data, sizeof(data));
@ -125,10 +125,10 @@ class Crypto : public CryptoDCInstance
}
// for decrypt
bool verify_packet_id(PacketIDAEADReceive &pid_recv, const PacketID::time_t now)
bool verify_packet_id(PacketIDDataReceive &pid_recv, const PacketIDControl::time_t now)
{
Buffer buf(data + data_offset_pkt_id, PacketIDAEAD::long_id_size, true);
const PacketIDAEAD pid = pid_recv.read_next(buf);
Buffer buf(data + data_offset_pkt_id, PacketIDData::long_id_size, true);
const PacketIDData pid = pid_recv.read_next(buf);
return pid_recv.test_add(pid, now); // verify packet ID
}
@ -142,12 +142,12 @@ class Crypto : public CryptoDCInstance
return ad_op32 ? data : data + data_offset_pkt_id;
}
size_t ad_len(const PacketIDAEADSend &pid_send) const
size_t ad_len(const PacketIDDataSend &pid_send) const
{
return (ad_op32 ? op32_size : 0) + pid_send.length();
}
size_t ad_len(const PacketIDAEADReceive &pid_recv) const
size_t ad_len(const PacketIDDataReceive &pid_recv) const
{
return (ad_op32 ? op32_size : 0) + pid_recv.length();
}
@ -168,7 +168,7 @@ class Crypto : public CryptoDCInstance
{
typename CRYPTO_API::CipherContextAEAD impl;
Nonce nonce;
PacketIDAEADSend pid_send{false};
PacketIDDataSend pid_send{false};
BufferAllocated work;
};
@ -176,7 +176,7 @@ class Crypto : public CryptoDCInstance
{
typename CRYPTO_API::CipherContextAEAD impl;
Nonce nonce;
PacketIDAEADReceive pid_recv{};
PacketIDDataReceive pid_recv{};
BufferAllocated work;
};
@ -197,7 +197,7 @@ class Crypto : public CryptoDCInstance
// Encrypt/Decrypt
// returns true if packet ID is close to wrapping
bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override
bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
{
// only process non-null packets
if (buf.size())
@ -251,7 +251,7 @@ class Crypto : public CryptoDCInstance
return e.pid_send.wrap_warning();
}
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override
Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
{
// only process non-null packets
if (buf.size())
@ -337,7 +337,7 @@ class Crypto : public CryptoDCInstance
const int recv_unit,
const SessionStats::Ptr &recv_stats_arg) override
{
e.pid_send = PacketIDAEADSend{dc_settings.use64bitPktCounter()};
e.pid_send = PacketIDDataSend{dc_settings.use64bitPktCounter()};
d.pid_recv.init(recv_name, recv_unit, dc_settings.use64bitPktCounter(), recv_stats_arg);
}

View File

@ -58,13 +58,13 @@ class CryptoCHM : public CryptoDCInstance
// Encrypt/Decrypt
/* returns true if packet ID is close to wrapping */
bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override
bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
{
encrypt_.encrypt(buf, now);
encrypt_.encrypt(buf);
return encrypt_.pid_send.wrap_warning();
}
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override
Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
{
return decrypt_.decrypt(buf, now);
}
@ -90,10 +90,10 @@ class CryptoCHM : public CryptoDCInstance
const SessionStats::Ptr &recv_stats_arg) override
{
/* CBC encryption always uses short packet ID */
auto pid_form = PacketID::SHORT_FORM;
constexpr bool wide = false;
encrypt_.pid_send.init(pid_form);
decrypt_.pid_recv.init(pid_form, recv_name, recv_unit, recv_stats_arg);
encrypt_.pid_send = PacketIDDataSend{wide};
decrypt_.pid_recv.init(recv_name, recv_unit, wide, recv_stats_arg);
}
bool consider_compression(const CompressContext &comp_ctx) override

View File

@ -33,7 +33,7 @@
#include <openvpn/common/rc.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/crypto/cryptoalgs.hpp>
#include <openvpn/compress/compress.hpp>
@ -48,9 +48,9 @@ class CryptoDCInstance : public RC<thread_unsafe_refcount>
// Encrypt/Decrypt
// returns true if packet ID is close to wrapping
virtual bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) = 0;
virtual bool encrypt(BufferAllocated &buf, const unsigned char *op32) = 0;
virtual Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) = 0;
virtual Error::Type decrypt(BufferAllocated &buf, std::time_t now, const unsigned char *op32) = 0;
// Initialization

View File

@ -34,7 +34,7 @@
#include <openvpn/crypto/cipher.hpp>
#include <openvpn/crypto/ovpnhmac.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/log/sessionstats.hpp>
namespace openvpn {
@ -45,7 +45,7 @@ class DecryptCHM
public:
OPENVPN_SIMPLE_EXCEPTION(chm_unsupported_cipher_mode);
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now)
Error::Type decrypt(BufferAllocated &buf, const std::time_t now)
{
// skip null packets
if (!buf.size())
@ -118,19 +118,13 @@ class DecryptCHM
Frame::Ptr frame;
CipherContext<CRYPTO_API> cipher;
OvpnHMAC<CRYPTO_API> hmac;
PacketIDReceive pid_recv;
PacketIDDataReceive pid_recv;
private:
bool verify_packet_id(BufferAllocated &buf, const PacketID::time_t now)
bool verify_packet_id(BufferAllocated &buf, const std::time_t now)
{
// ignore packet ID if pid_recv is not initialized
if (pid_recv.initialized())
{
const PacketID pid = pid_recv.read_next(buf);
if (!pid_recv.test_add(pid, now, true)) // verify packet ID
return false;
}
return true;
const PacketIDData pid = pid_recv.read_next(buf);
return pid_recv.test_add(pid, now);
}
BufferAllocated work;

View File

@ -35,7 +35,7 @@
#include <openvpn/crypto/cipher.hpp>
#include <openvpn/crypto/ovpnhmac.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_data.hpp>
namespace openvpn {
template <typename CRYPTO_API>
@ -44,7 +44,7 @@ class EncryptCHM
public:
OPENVPN_SIMPLE_EXCEPTION(chm_unsupported_cipher_mode);
void encrypt(BufferAllocated &buf, const PacketID::time_t now)
void encrypt(BufferAllocated &buf)
{
// skip null packets
if (!buf.size())
@ -64,7 +64,7 @@ class EncryptCHM
rng->rand_bytes(iv_buf, iv_length);
// generate fresh outgoing packet ID and prepend to cleartext buffer
pid_send.write_next(buf, true, now);
pid_send.prepend_next(buf);
}
else
{
@ -95,7 +95,7 @@ class EncryptCHM
else // no encryption
{
// generate fresh outgoing packet ID and prepend to cleartext buffer
pid_send.write_next(buf, true, now);
pid_send.prepend_next(buf);
// HMAC the cleartext
prepend_hmac(buf);
@ -110,7 +110,7 @@ class EncryptCHM
Frame::Ptr frame;
CipherContext<CRYPTO_API> cipher;
OvpnHMAC<CRYPTO_API> hmac;
PacketIDSend pid_send;
PacketIDDataSend pid_send{false};
private:
// compute HMAC signature of data buffer,

View File

@ -21,8 +21,7 @@
// Manage OpenVPN protocol Packet IDs for packet replay detection
#ifndef OPENVPN_CRYPTO_PACKET_ID_H
#define OPENVPN_CRYPTO_PACKET_ID_H
#pragma once
#include <string>
#include <cstring>
@ -42,59 +41,38 @@
namespace openvpn {
/*
* Communicate packet-id over the wire.
* A short packet-id is just a 32 bit
* sequence number. A long packet-id
* includes a timestamp as well.
* Control channel Packet ID. These IDs have the format
*
* Long packet-ids are used as IVs for
* CFB/OFB ciphers.
* | 32 bit integer timestamp in BE | 32 bit packet counter in BE |
*
* This data structure is always sent
* over the net in network byte order,
* by calling htonl, ntohl, on the
* 32-bit data elements, id_t and
* net_time_t, to change them to and
* from network order.
* This format of long packet-ids is also used as IVs for CFB/OFB ciphers
* in OpenVPN 2.x but OpenVPN 3.x supports only CBC and AEAD ciphers, so
* it is only used for control channel and control chanel authentication/encryption
* schemes like tls-auth/tls-crypt.
*
* In addition, time is converted to
* a PacketID::net_time_t before sending,
* since openvpn always
* uses a 32-bit time_t but some
* 64 bit platforms use a
* 64 bit time_t.
* This data structure is always sent over the net in network byte order,
* by calling htonl, ntohl, on the 32-bit data elements, id_t and
* net_time_t, to change them to and from network order.
*/
struct PacketID
struct PacketIDControl
{
typedef std::uint32_t id_t;
typedef std::uint32_t net_time_t;
typedef Time::base_type time_t;
enum
{
SHORT_FORM = 0, // short form of ID (4 bytes)
LONG_FORM = 1, // long form of ID (8 bytes)
UNDEF = 0, // special undefined/null id_t value
};
id_t id; // legal values are 1 through 2^32-1
time_t time; // converted to PacketID::net_time_t before transmission
static constexpr size_t size(const int form)
static constexpr size_t size()
{
if (form == PacketID::LONG_FORM)
return longidsize;
else
return shortidsize;
return idsize;
}
constexpr static size_t shortidsize = sizeof(id_t);
constexpr static size_t longidsize = sizeof(id_t) + sizeof(net_time_t);
constexpr static size_t idsize = sizeof(id_t) + sizeof(net_time_t);
bool is_valid() const
{
return id != UNDEF;
return id != 0;
}
void reset()
@ -104,7 +82,7 @@ struct PacketID
}
template <typename BufType> // so it can take a Buffer or a ConstBuffer
void read(BufType &buf, const int form)
void read(BufType &buf)
{
id_t net_id;
net_time_t net_time;
@ -112,16 +90,11 @@ struct PacketID
buf.read((unsigned char *)&net_id, sizeof(net_id));
id = ntohl(net_id);
if (form == LONG_FORM)
{
buf.read((unsigned char *)&net_time, sizeof(net_time));
time = ntohl(net_time);
}
else
time = time_t(0);
buf.read((unsigned char *)&net_time, sizeof(net_time));
time = ntohl(net_time);
}
void write(Buffer &buf, const int form, const bool prepend) const
void write(Buffer &buf, const bool prepend) const
{
const id_t net_id = htonl(id);
const net_time_t net_time = htonl(static_cast<uint32_t>(time & 0x00000000FFFFFFFF));
@ -130,15 +103,13 @@ struct PacketID
if (prepend)
{
if (form == LONG_FORM)
buf.prepend((unsigned char *)&net_time, sizeof(net_time));
buf.prepend((unsigned char *)&net_time, sizeof(net_time));
buf.prepend((unsigned char *)&net_id, sizeof(net_id));
}
else
{
buf.write((unsigned char *)&net_id, sizeof(net_id));
if (form == LONG_FORM)
buf.write((unsigned char *)&net_time, sizeof(net_time));
buf.write((unsigned char *)&net_time, sizeof(net_time));
}
}
@ -150,51 +121,36 @@ struct PacketID
}
};
struct PacketIDConstruct : public PacketID
{
PacketIDConstruct(const PacketID::time_t v_time = PacketID::time_t(0), const PacketID::id_t v_id = PacketID::id_t(0))
{
id = v_id;
time = v_time;
}
};
class PacketIDSend
class PacketIDControlSend
{
public:
OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap);
PacketIDSend()
{
init(PacketID::SHORT_FORM);
}
explicit PacketIDSend(int form, PacketID::id_t start_at = PacketID::id_t(0))
explicit PacketIDControlSend(PacketIDControl::id_t start_at = PacketIDControl::id_t(0))
{
init(form, start_at);
init(start_at);
}
/**
* @param form PacketID::LONG_FORM or PacketID::SHORT_FORM
* @param start_at initial id for the sending
*/
void init(const int form, PacketID::id_t start_at = 0)
void init(PacketIDControl::id_t start_at = 0)
{
pid_.id = start_at;
pid_.time = PacketID::time_t(0);
form_ = form;
pid_.time = PacketIDControl::time_t(0);
}
PacketID next(const PacketID::time_t now)
PacketIDControl next(const PacketIDControl::time_t now)
{
PacketID ret;
PacketIDControl ret;
if (!pid_.time)
pid_.time = now;
ret.id = ++pid_.id;
if (unlikely(!pid_.id)) // wraparound
{
if (form_ != PacketID::LONG_FORM)
throw packet_id_wrap();
pid_.time = now;
ret.id = pid_.id = 1;
}
@ -202,35 +158,22 @@ class PacketIDSend
return ret;
}
void write_next(Buffer &buf, const bool prepend, const PacketID::time_t now)
void write_next(Buffer &buf, const bool prepend, const PacketIDControl::time_t now)
{
const PacketID pid = next(now);
pid.write(buf, form_, prepend);
}
/*
* In TLS mode, when a packet ID gets to this level,
* start thinking about triggering a new
* SSL/TLS handshake.
*/
bool wrap_warning() const
{
const PacketID::id_t wrap_at = 0xFF000000;
return pid_.id >= wrap_at;
const PacketIDControl pid = next(now);
pid.write(buf, prepend);
}
std::string str() const
{
std::string ret;
ret = pid_.str();
if (form_ == PacketID::LONG_FORM)
ret += 'L';
ret += 'L';
return ret;
}
private:
PacketID pid_;
int form_;
PacketIDControl pid_;
};
/*
@ -243,7 +186,7 @@ class PacketIDSend
*/
template <unsigned int REPLAY_WINDOW_ORDER,
unsigned int PKTID_RECV_EXPIRE>
class PacketIDReceiveType
class PacketIDControlReceiveType
{
public:
static constexpr unsigned int REPLAY_WINDOW_BYTES = 1 << REPLAY_WINDOW_ORDER;
@ -252,13 +195,11 @@ class PacketIDReceiveType
OPENVPN_SIMPLE_EXCEPTION(packet_id_not_initialized);
// TODO: [OVPN3-933] Consider RAII'ifying this code
PacketIDReceiveType()
: initialized_(false)
PacketIDControlReceiveType()
{
}
void init(const int form_arg,
const char *name_arg,
void init(const char *name_arg,
const int unit_arg,
const SessionStats::Ptr &stats_arg)
{
@ -270,20 +211,19 @@ class PacketIDReceiveType
time_high = 0;
id_floor = 0;
max_backtrack = 0;
form = form_arg;
unit = unit_arg;
name = name_arg;
stats = stats_arg;
std::memset(history, 0, sizeof(history));
}
bool initialized() const
[[nodiscard]] bool initialized() const
{
return initialized_;
}
bool test_add(const PacketID &pin,
const PacketID::time_t now,
bool test_add(const PacketIDControl &pin,
const PacketIDControl::time_t now,
const bool mod) // don't modify history unless mod is true
{
const Error::Type err = do_test_add(pin, now, mod);
@ -296,8 +236,8 @@ class PacketIDReceiveType
return true;
}
Error::Type do_test_add(const PacketID &pin,
const PacketID::time_t now,
Error::Type do_test_add(const PacketIDControl &pin,
const PacketIDControl::time_t now,
const bool mod) // don't modify history unless mod is true
{
// make sure we were initialized
@ -402,12 +342,12 @@ class PacketIDReceiveType
return Error::SUCCESS;
}
PacketID read_next(Buffer &buf) const
PacketIDControl read_next(Buffer &buf) const
{
if (!initialized_)
throw packet_id_not_initialized();
PacketID pid;
pid.read(buf, form);
PacketIDControl pid{};
pid.read(buf);
return pid;
}
@ -424,18 +364,17 @@ class PacketIDReceiveType
return (base + i) & (REPLAY_WINDOW_SIZE - 1);
}
bool initialized_;
bool initialized_ = false;
unsigned int base; // bit position of deque base in history
unsigned int extent; // extent (in bits) of deque in history
PacketID::time_t expire; // expiration of history
PacketID::id_t id_high; // highest sequence number received
PacketID::time_t time_high; // highest time stamp received
PacketID::id_t id_floor; // we will only accept backtrack IDs > id_floor
unsigned int max_backtrack;
unsigned int base = 0; // bit position of deque base in history
unsigned int extent = 0; // extent (in bits) of deque in history
PacketIDControl::time_t expire = 0; // expiration of history
PacketIDControl::id_t id_high = 0; // highest sequence number received
PacketIDControl::time_t time_high = 0; // highest time stamp received
PacketIDControl::id_t id_floor = 0; // we will only accept backtrack IDs > id_floor
unsigned int max_backtrack = 0;
int form; // PacketID::LONG_FORM or PacketID::SHORT_FORM
int unit; // unit number of this object (for debugging)
int unit = -1; // unit number of this object (for debugging)
std::string name; // name of this object (for debugging)
SessionStats::Ptr stats;
@ -445,8 +384,6 @@ class PacketIDReceiveType
// Our standard packet ID window with order=8 (window size=2048).
// and recv expire=30 seconds.
typedef PacketIDReceiveType<8, 30> PacketIDReceive;
typedef PacketIDControlReceiveType<8, 30> PacketIDControlReceive;
} // namespace openvpn
#endif // OPENVPN_CRYPTO_PACKET_ID_H

View File

@ -43,24 +43,25 @@
namespace openvpn {
/**
* Communicate packet-id over the wire for AEAD
* Communicate packet-id over the wire for data channel packets
* A short packet-id is just a 32 bit sequence number. A long packet-id is a
* 64 bit sequence number. This sequence number is reused for AEAD IV.
* 64 bit sequence number. This sequence number is reused for AEAD IV when
* AEAD is used as a cipher. CBC transmits an additional IV.
*
* This data structure is always sent over the net in network byte order,
*
* This class is different from PacketID in the way that it always uses
* a "flat" packet id that is either 32 or 64 bit while PacketID has a long
* This class is different from PacketIDControl in the way that it always uses
* a "flat" packet id that is either 32 or 64 bit while PacketIDControl has a long
* packet id that is 32bit + 32bit but follow different rules and includes
* a timestamp. Merging PacketIDAEAD and PacketID would result in a much
* more convoluted and hard to understand class than keeping them seperate
* a timestamp. Merging PacketIData and PacketIDControl would result in a much
* more convoluted and hard to understand class than keeping them separate.
*
*/
struct PacketIDAEAD
struct PacketIDData
{
typedef std::uint64_t aead_id_t;
typedef std::uint64_t data_id_t;
aead_id_t id = 0; // legal values are 1 through 2^64-1
data_id_t id = 0; // legal values are 1 through 2^64-1
bool wide = false;
/**
@ -81,12 +82,12 @@ struct PacketIDAEAD
}
explicit PacketIDAEAD(bool wide_arg)
explicit PacketIDData(bool wide_arg)
: wide(wide_arg)
{
}
explicit PacketIDAEAD(bool wide_arg, aead_id_t id_arg)
explicit PacketIDData(bool wide_arg, data_id_t id_arg)
: id(id_arg), wide(wide_arg)
{
}
@ -94,14 +95,14 @@ struct PacketIDAEAD
constexpr static std::size_t short_id_size = sizeof(std::uint32_t);
constexpr static std::size_t long_id_size = sizeof(std::uint64_t);
bool is_valid() const
[[nodiscard]] bool is_valid() const
{
return id != 0;
}
void reset()
{
id = aead_id_t(0);
id = data_id_t(0);
}
/**
@ -139,7 +140,23 @@ struct PacketIDAEAD
}
}
std::string str() const
/** Prepend the packet id to a buffer */
void write_prepend(Buffer &buf) const
{
if (wide)
{
const std::uint64_t net_id = Endian::rev64(id);
buf.prepend(reinterpret_cast<const unsigned char *>(&net_id), sizeof(net_id));
}
else
{
const std::uint32_t net_id = htonl(static_cast<std::uint32_t>(id));
buf.prepend(reinterpret_cast<const unsigned char *>(&net_id), sizeof(net_id));
}
}
[[nodiscard]] std::string str() const
{
std::ostringstream os;
os << std::hex << "[0x" << id << "]";
@ -147,12 +164,12 @@ struct PacketIDAEAD
}
};
class PacketIDAEADSend
class PacketIDDataSend
{
public:
OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap);
PacketIDAEADSend(bool wide_arg)
explicit PacketIDDataSend(bool wide_arg)
: pid_(wide_arg)
{
}
@ -163,10 +180,10 @@ class PacketIDAEADSend
* @throws packet_id_wrap if the packet id space is exhausted
* @return packet id to use next.
*/
PacketIDAEAD next()
[[nodiscard]] PacketIDData next()
{
++pid_.id;
PacketIDAEAD ret{pid_.wide, pid_.id};
PacketIDData ret{pid_.wide, pid_.id};
if (!pid_.wide && unlikely(pid_.id == std::numeric_limits<std::uint32_t>::max())) // wraparound
{
throw packet_id_wrap();
@ -184,26 +201,21 @@ class PacketIDAEADSend
*/
void write_next(Buffer &buf)
{
const PacketIDAEAD pid = next();
const PacketIDData pid = next();
pid.write(buf);
}
/**
* When a VPN runs in TLS mode (the only mode that OpenVPN supports,
* there is no --secret mode anymore), it needs to be warned about wrapping to
* start thinking about triggering a new SSL/TLS handshake.
* This method can be called to see if that level has been reached.
* increases the packet id and prepends it to a buffer
* @param buf buffer to write to
*/
bool wrap_warning() const
void prepend_next(Buffer &buf)
{
if (pid_.wide)
return false;
const PacketIDAEAD::aead_id_t wrap_at = 0xFF000000;
return pid_.id >= wrap_at;
const PacketIDData pid = next();
pid.write_prepend(buf);
}
std::string str() const
[[nodiscard]] std::string str() const
{
std::string ret;
ret = pid_.str();
@ -221,8 +233,28 @@ class PacketIDAEADSend
return pid_.size();
}
private:
PacketIDAEAD pid_;
/**
* When a VPN runs in TLS mode (the only mode that OpenVPN supports,
* there is no --secret mode anymore), it needs to be warned about wrapping to
* start thinking about triggering a new SSL/TLS handshake.
* This method can be called to see if that level has been reached.
*
* For 64bit counters, even with (non-existing) 1 byte packets, we would need
* to transfer 16 EB (exabytes) and 1,6 ZB (zettabytes) with 100 byte packets.
* This is not reachable in reasonable amount of time. And we still have the
* failsafe to throw an exception if we would overflow the ocunter.
*/
bool wrap_warning() const
{
if (pid_.wide)
return false;
const PacketIDData::data_id_t wrap_at = 0xFF000000;
return pid_.id >= wrap_at;
}
protected:
PacketIDData pid_;
};
/*
@ -234,7 +266,7 @@ class PacketIDAEADSend
*/
template <unsigned int REPLAY_WINDOW_ORDER,
unsigned int PKTID_RECV_EXPIRE>
class PacketIDAEADReceiveType
class PacketIDDataReceiveType
{
public:
static constexpr unsigned int REPLAY_WINDOW_BYTES = 1u << REPLAY_WINDOW_ORDER;
@ -268,7 +300,7 @@ class PacketIDAEADReceiveType
* @param now Current time to check that reordered packets are in the allowed time
* @return true if the packet id is okay and has been accepted
*/
[[nodiscard]] bool test_add(const PacketIDAEAD &pin,
[[nodiscard]] bool test_add(const PacketIDData &pin,
const Time::base_type now)
{
const Error::Type err = do_test_add(pin, now);
@ -290,7 +322,7 @@ class PacketIDAEADReceiveType
* @param now Current time to check that reordered packets are in the allowed time
* @return Error::SUCCESS if successful, otherwise PKTID_EXPIRE, PKTID_BACKTRACK or PKTID_REPLAY
*/
[[nodiscard]] Error::Type do_test_add(const PacketIDAEAD &pin,
[[nodiscard]] Error::Type do_test_add(const PacketIDData &pin,
const Time::base_type now)
{
// expire backtracks at or below id_floor after PKTID_RECV_EXPIRE time
@ -364,9 +396,9 @@ class PacketIDAEADReceiveType
return Error::SUCCESS;
}
PacketIDAEAD read_next(Buffer &buf) const
PacketIDData read_next(Buffer &buf) const
{
PacketIDAEAD pid{wide};
PacketIDData pid{wide};
pid.read(buf);
return pid;
}
@ -380,11 +412,11 @@ class PacketIDAEADReceiveType
[[nodiscard]] std::size_t constexpr length() const
{
return PacketIDAEAD::size(wide);
return PacketIDData::size(wide);
}
private:
[[nodiscard]] constexpr std::size_t replay_index(PacketIDAEAD::aead_id_t i) const
[[nodiscard]] constexpr std::size_t replay_index(PacketIDData::data_id_t i) const
{
return (base + i) & (REPLAY_WINDOW_SIZE - 1);
}
@ -392,8 +424,8 @@ class PacketIDAEADReceiveType
std::size_t base; // bit position of deque base in history
std::size_t extent; // extent (in bits) of deque in history
Time::base_type expire; // expiration of history
PacketIDAEAD::aead_id_t id_high; // highest sequence number received
PacketIDAEAD::aead_id_t id_floor; // we will only accept backtrack IDs > id_floor
PacketIDData::data_id_t id_high; // highest sequence number received
PacketIDData::data_id_t id_floor; // we will only accept backtrack IDs > id_floor
//!< 32 or 64 bit packet counter
bool wide;
@ -408,6 +440,6 @@ class PacketIDAEADReceiveType
// Our standard packet ID window with order=8 (window size=2048).
// and recv expire=30 seconds.
typedef PacketIDAEADReceiveType<8, 30> PacketIDAEADReceive;
typedef PacketIDDataReceiveType<8, 30> PacketIDDataReceive;
} // namespace openvpn

View File

@ -32,7 +32,7 @@
#include <openvpn/common/memneq.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/cryptoalgs.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/ssl/psid.hpp>
namespace openvpn {
@ -190,7 +190,7 @@ class TLSCryptContext : public RC<thread_unsafe_refcount>
// [OP] [PSID] [PID] [HMAC] [...]
//
constexpr const static size_t hmac_offset = 1 + ProtoSessionID::SIZE + PacketID::longidsize;
constexpr const static size_t hmac_offset = 1 + ProtoSessionID::SIZE + PacketIDControl::idsize;
};

View File

@ -108,12 +108,12 @@ class Instance : public CryptoDCInstance
// should never be reached.
// returns true if packet ID is close to wrapping
bool encrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override
bool encrypt(BufferAllocated &buf, const unsigned char *op32) override
{
throw korekey_error("encrypt");
}
Error::Type decrypt(BufferAllocated &buf, const PacketID::time_t now, const unsigned char *op32) override
Error::Type decrypt(BufferAllocated &buf, const std::time_t now, const unsigned char *op32) override
{
throw korekey_error("decrypt");
}

View File

@ -30,7 +30,7 @@
#include <openvpn/common/socktypes.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/reliable/relcommon.hpp>
namespace openvpn {

View File

@ -24,13 +24,15 @@
#ifndef OPENVPN_RELIABLE_RELCOMMON_H
#define OPENVPN_RELIABLE_RELCOMMON_H
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_control.hpp>
namespace openvpn {
namespace reliable {
typedef PacketID::id_t id_t;
}
typedef std::uint32_t id_t;
constexpr static std::size_t id_size = sizeof(id_t);
} // namespace reliable
template <typename PACKET>
class ReliableMessageBase

View File

@ -66,7 +66,7 @@
#include <openvpn/crypto/ovpnhmac.hpp>
#include <openvpn/crypto/tls_crypt.hpp>
#include <openvpn/crypto/tls_crypt_v2.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/bs64_data_limit.hpp>
#include <openvpn/log/sessionstats.hpp>
@ -1180,11 +1180,11 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
{
dc_overhead = dc.context().encap_overhead();
}
const size_t adj = protocol.extra_transport_bytes() + // extra 2 bytes for TCP-streamed packet length
(enable_op32 ? 4 : 1) + // leading op
comp_ctx.extra_payload_bytes() + // compression header
PacketID::size(PacketID::SHORT_FORM) + // sequence number
dc_overhead; // data channel crypto layer overhead
const size_t adj = protocol.extra_transport_bytes() + // extra 2 bytes for TCP-streamed packet length
(enable_op32 ? 4 : 1) + // leading op
comp_ctx.extra_payload_bytes() + // compression header
PacketIDData::size(false) + // sequence number
dc_overhead; // data channel crypto layer overhead
return (unsigned int)adj;
}
@ -1438,8 +1438,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
if (tls_wrap_mode == TLS_CRYPT || tls_wrap_mode == TLS_CRYPT_V2)
{
PacketID pid;
pid.read(b, PacketID::LONG_FORM);
PacketIDControl pid;
pid.read(b);
out << " PID=" << pid.str();
const unsigned char *hmac = b.read_alloc(hmac_size);
@ -1453,8 +1453,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
const unsigned char *hmac = b.read_alloc(hmac_size);
out << " HMAC=" << render_hex(hmac, hmac_size);
PacketID pid;
pid.read(b, PacketID::LONG_FORM);
PacketIDControl pid;
pid.read(b);
out << " PID=" << pid.str();
}
@ -2213,7 +2213,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
}
if (CryptoAlgs::mode(c.dc.cipher()) == CryptoAlgs::CBC_HMAC)
payload_overhead += PacketID::size(PacketID::SHORT_FORM);
payload_overhead += PacketIDData::size(false);
// account for IPv4 and TCP headers of the payload, mssfix method
// will add 20 extra bytes if payload is IPv6
@ -2225,7 +2225,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
// in CBC mode, the packet id is part of the payload size / overhead
if (CryptoAlgs::mode(c.dc.cipher()) != CryptoAlgs::CBC_HMAC)
overhead += PacketID::size(PacketID::SHORT_FORM);
overhead += PacketIDData::size(false);
if (c.mss_parms.mtu)
{
@ -2361,7 +2361,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
orig_size,
1 + ProtoSessionID::SIZE,
proto.hmac_size,
PacketID::size(PacketID::LONG_FORM)))
PacketIDControl::size()))
{
return false;
}
@ -2372,10 +2372,10 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
return false;
// read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv);
const PacketIDControl pid = proto.ta_pid_recv.read_next(recv);
// get current time_t
const PacketID::time_t t = now->seconds_since_epoch();
const PacketIDControl::time_t t = now->seconds_since_epoch();
// verify tls_auth packet ID
const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false);
@ -2401,11 +2401,11 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
// get source PSID
ProtoSessionID src_psid(recv);
// read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv);
const PacketIDControl pid = proto.ta_pid_recv.read_next(recv);
recv.advance(proto.hmac_size);
const size_t head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM);
const size_t head_size = 1 + ProtoSessionID::SIZE + PacketIDControl::size();
const size_t data_offset = head_size + proto.hmac_size;
if (orig_size < data_offset)
return false;
@ -2445,7 +2445,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
}
// get current time_t
const PacketID::time_t t = now->seconds_since_epoch();
const PacketIDControl::time_t t = now->seconds_since_epoch();
// verify tls_auth packet ID
const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false);
@ -2507,7 +2507,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
static_assert(sizeof(op32) == OP_SIZE_V2, "OP_SIZE_V2 inconsistency");
// encrypt packet
pid_wrap = crypto->encrypt(buf, now->seconds_since_epoch(), (const unsigned char *)&op32);
pid_wrap = crypto->encrypt(buf, (const unsigned char *)&op32);
// prepend op
buf.prepend((const unsigned char *)&op32, sizeof(op32));
@ -2515,7 +2515,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
else
{
// encrypt packet
pid_wrap = crypto->encrypt(buf, now->seconds_since_epoch(), nullptr);
pid_wrap = crypto->encrypt(buf, nullptr);
// prepend op
buf.push_front(op_compose(DATA_V1, key_id_));
@ -3002,7 +3002,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
buf.size(),
1 + ProtoSessionID::SIZE,
proto.hmac_size,
PacketID::size(PacketID::LONG_FORM));
PacketIDControl::size());
}
void gen_head_tls_crypt(const unsigned int opcode, BufferAllocated &buf)
@ -3104,7 +3104,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
gen_head(ACK_V1, buf);
}
bool decapsulate_post_process(Packet &pkt, ProtoSessionID &src_psid, const PacketID pid)
bool decapsulate_post_process(Packet &pkt, ProtoSessionID &src_psid, const PacketIDControl pid)
{
Buffer &recv = *pkt.buf;
@ -3116,7 +3116,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
return false;
// get current time_t
const PacketID::time_t t = now->seconds_since_epoch();
const PacketIDControl::time_t t = now->seconds_since_epoch();
// verify tls_auth/crypt packet ID
const bool pid_ok = proto.ta_pid_recv.test_add(pid, t, false);
@ -3190,7 +3190,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
orig_size,
1 + ProtoSessionID::SIZE,
proto.hmac_size,
PacketID::size(PacketID::LONG_FORM)))
PacketIDControl::size()))
{
proto.stats->error(Error::HMAC_ERROR);
if (proto.is_tcp())
@ -3200,7 +3200,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
}
// read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv);
const PacketIDControl pid = proto.ta_pid_recv.read_next(recv);
return decapsulate_post_process(pkt, src_psid, pid);
}
@ -3216,7 +3216,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
// get source PSID
ProtoSessionID src_psid(recv);
// get tls-crypt packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv);
const PacketIDControl pid = proto.ta_pid_recv.read_next(recv);
// skip the hmac
recv.advance(proto.hmac_size);
@ -3319,7 +3319,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
const size_t orig_size = recv.size();
const size_t hmac_size = proto.config->tls_crypt_context->digest_size();
const size_t tls_frame_size = 1 + ProtoSessionID::SIZE
+ PacketID::size(PacketID::LONG_FORM)
+ PacketIDControl::size()
+ hmac_size
// the following is the tls-crypt payload
+ sizeof(char) // length of ACK array
@ -3631,7 +3631,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
net_buf.size(),
1 + ProtoSessionID::SIZE,
ta_hmac_recv->output_size(),
PacketID::size(PacketID::LONG_FORM));
PacketIDControl::size());
}
catch (BufferException &)
{
@ -3825,8 +3825,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
// get HMAC size from Digest object
hmac_size = c.tls_crypt_context->digest_size();
ta_pid_send.init(PacketID::LONG_FORM);
ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats);
ta_pid_send.init();
ta_pid_recv.init("SSL-CC", 0, stats);
reset_tls_crypt(c, dyn_key);
}
@ -3874,7 +3874,7 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
upcoming_key_id = 0;
unsigned int key_dir;
const PacketID::id_t EARLY_NEG_START = 0x0f000000;
const PacketIDControl::id_t EARLY_NEG_START = 0x0f000000;
// tls-auth initialization
reset_tls_wrap_mode(c);
@ -3883,8 +3883,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
case TLS_CRYPT:
reset_tls_crypt(c, c.tls_key);
// init tls_crypt packet ID
ta_pid_send.init(PacketID::LONG_FORM);
ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats);
ta_pid_send.init();
ta_pid_recv.init("SSL-CC", 0, stats);
break;
case TLS_CRYPT_V2:
if (is_server())
@ -3897,8 +3897,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
/** tls-auth/tls-crypt packet id. We start with a different id here
* to indicate EARLY_NEG_START/CONTROL_WKC_V1 support */
// init tls_crypt packet ID
ta_pid_send.init(PacketID::LONG_FORM, EARLY_NEG_START);
ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats);
ta_pid_send.init(EARLY_NEG_START);
ta_pid_recv.init("SSL-CC", 0, stats);
break;
case TLS_AUTH:
// init OvpnHMACInstance
@ -3929,8 +3929,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
* expect to handle the 1st packet, id 0.
*
*/
ta_pid_send.init(PacketID::LONG_FORM, cookie_psid.defined() ? 1 : 0);
ta_pid_recv.init(PacketID::LONG_FORM, "SSL-CC", 0, stats);
ta_pid_send.init(cookie_psid.defined() ? 1 : 0);
ta_pid_recv.init("SSL-CC", 0, stats);
break;
case TLS_PLAIN:
break;
@ -4702,8 +4702,8 @@ class ProtoContext : public logging::LoggingMixin<OPENVPN_DEBUG_PROTO,
TLSCryptInstance::Ptr tls_crypt_server;
TLSCryptMetadata::Ptr tls_crypt_metadata;
PacketIDSend ta_pid_send;
PacketIDReceive ta_pid_recv;
PacketIDControlSend ta_pid_send;
PacketIDControlReceive ta_pid_recv;
ProtoSessionID psid_self;
ProtoSessionID psid_peer;

View File

@ -144,7 +144,7 @@ class PsidCookieImpl : public PsidCookie
{
static const size_t hmac_size = ta_hmac_recv_->output_size();
// ovpn_hmac_cmp checks for adequate pkt_buf.size()
bool pkt_hmac_valid = ta_hmac_recv_->ovpn_hmac_cmp(pkt_buf.c_data(), pkt_buf.size(), 1 + SID_SIZE, hmac_size, long_pktid_size_);
bool pkt_hmac_valid = ta_hmac_recv_->ovpn_hmac_cmp(pkt_buf.c_data(), pkt_buf.size(), 1 + SID_SIZE, hmac_size, PacketIDControl::idsize);
if (!pkt_hmac_valid)
{
// JMD_TODO: log failure? Logging DDoS?
@ -155,7 +155,7 @@ class PsidCookieImpl : public PsidCookie
static const size_t reqd_packet_size
// clang-format off
// [op_field] [cli_psid] [HMAC] [cli_auth_pktid] [cli_pktid]
= 1 + SID_SIZE + hmac_size + long_pktid_size_ + short_pktid_size_;
= 1 + SID_SIZE + hmac_size + PacketIDControl::idsize + reliable::id_size;
// clang-format on
if (pkt_buf.size() < reqd_packet_size)
{
@ -169,10 +169,13 @@ class PsidCookieImpl : public PsidCookie
// decapsulate_tls_auth
const ProtoSessionID cli_psid(recv_buf_copy);
recv_buf_copy.advance(hmac_size);
PacketID cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC
cli_auth_pktid.read(recv_buf_copy, PacketID::LONG_FORM);
PacketID cli_pktid; // a.k.a., packet_id in draft RFC
cli_pktid.read(recv_buf_copy, PacketID::SHORT_FORM);
PacketIDControl cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC
cli_auth_pktid.read(recv_buf_copy);
uint8_t cli_net_id[4]; // a.k.a., packet_id in draft RFC
recv_buf_copy.read(cli_net_id, sizeof(cli_net_id));
// start building the server reply HARD_RESET packet
BufferAllocated send_buf;
@ -186,12 +189,11 @@ class PsidCookieImpl : public PsidCookie
// prepend_dest_psid_and_acks
cli_psid.prepend(send_buf);
const id_t cli_net_id = htonl(cli_pktid.id);
send_buf.prepend((unsigned char *)&cli_net_id, sizeof(cli_net_id));
send_buf.prepend(cli_net_id, sizeof(cli_net_id));
send_buf.push_front((unsigned char)1);
// gen head
PacketIDSend svr_auth_pid(PacketID::LONG_FORM);
PacketIDControlSend svr_auth_pid{};
svr_auth_pid.write_next(send_buf, true, now_->seconds_since_epoch());
// make space for tls-auth HMAC
send_buf.prepend_alloc(ta_hmac_send_->output_size());
@ -202,7 +204,7 @@ class PsidCookieImpl : public PsidCookie
const unsigned char op_field = CookieHelper::get_server_hard_reset_opfield();
send_buf.push_front(op_field);
// write hmac
ta_hmac_send_->ovpn_hmac_gen(send_buf.data(), send_buf.size(), 1 + SID_SIZE, ta_hmac_send_->output_size(), long_pktid_size_);
ta_hmac_send_->ovpn_hmac_gen(send_buf.data(), send_buf.size(), 1 + SID_SIZE, ta_hmac_send_->output_size(), PacketIDControl::idsize);
// consumer's implementation to send the SERVER_HARD_RESET to the client
bool send_ok = pctb_->psid_cookie_send_const(send_buf, pcaib);
@ -218,7 +220,7 @@ class PsidCookieImpl : public PsidCookie
{
static const size_t hmac_size = ta_hmac_recv_->output_size();
// ovpn_hmac_cmp checks for adequate pkt_buf.size()
bool pkt_hmac_valid = ta_hmac_recv_->ovpn_hmac_cmp(pkt_buf.c_data(), pkt_buf.size(), 1 + SID_SIZE, hmac_size, long_pktid_size_);
bool pkt_hmac_valid = ta_hmac_recv_->ovpn_hmac_cmp(pkt_buf.c_data(), pkt_buf.size(), 1 + SID_SIZE, hmac_size, PacketIDControl::idsize);
if (!pkt_hmac_valid)
{
// JMD_TODO: log failure? Logging DDoS?
@ -228,7 +230,7 @@ class PsidCookieImpl : public PsidCookie
static const size_t reqd_packet_size
// clang-format off
// [op_field] [cli_psid] [HMAC] [cli_auth_pktid] [acked] [srv_psid]
= 1 + SID_SIZE + hmac_size + long_pktid_size_ + 5 + SID_SIZE;
= 1 + SID_SIZE + hmac_size + PacketIDControl::size() + 5 + SID_SIZE;
// the fixed size, 5, of the [acked] field recognizes that the client's first
// response will ack exactly one packet, the server's HARD_RESET
// clang-format on
@ -244,8 +246,10 @@ class PsidCookieImpl : public PsidCookie
// decapsulate_tls_auth
const ProtoSessionID cli_psid(recv_buf_copy);
recv_buf_copy.advance(hmac_size);
PacketID cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC
cli_auth_pktid.read(recv_buf_copy, PacketID::LONG_FORM);
PacketIDControl cli_auth_pktid; // a.k.a, replay_packet_id in draft RFC
cli_auth_pktid.read(recv_buf_copy);
unsigned int ack_count = recv_buf_copy[0];
if (ack_count != 1)
{
@ -349,8 +353,6 @@ class PsidCookieImpl : public PsidCookie
}
static constexpr CryptoAlgs::Type digest_ = CryptoAlgs::Type::SHA256;
static constexpr size_t long_pktid_size_ = PacketID::size(PacketID::LONG_FORM);
static constexpr size_t short_pktid_size_ = PacketID::size(PacketID::SHORT_FORM);
const ProtoContext::ProtoConfig &pcfg_;
bool not_tls_auth_mode_;

View File

@ -63,7 +63,8 @@ add_executable(coreUnitTests
test_ostream_containers.cpp
test_parseargv.cpp
test_path.cpp
test_pktid.cpp
test_pktid_control.cpp
test_pktid_data.cpp
test_prefixlen.cpp
test_randapi.cpp
test_rc.cpp

View File

@ -149,11 +149,11 @@ void test_datachannel_crypto(bool tag_at_the_end, bool longpktcounter = false)
const unsigned char *data = work.data();
EXPECT_TRUE(std::memcmp(data, plaintext, std::strlen(plaintext)) == 0);
const openvpn::PacketID::time_t now = 42;
const std::time_t now = 42;
const unsigned char op32[]{7, 0, 0, 23};
bool const wrapwarn = cryptodc.encrypt(work, now, op32);
bool const wrapwarn = cryptodc.encrypt(work, op32);
ASSERT_FALSE(wrapwarn);
size_t pkt_counter_len = longpktcounter ? 8 : 4;

View File

@ -1,29 +1,38 @@
#include "test_common.h"
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_aead.hpp>
#include <openvpn/crypto/packet_id_control.hpp>
using namespace openvpn;
struct PacketIDControlConstruct : public PacketIDControl
{
PacketIDControlConstruct(const PacketIDControl::time_t v_time = PacketIDControl::time_t(0), const PacketIDControl::id_t v_id = PacketIDControl::id_t(0))
{
id = v_id;
time = v_time;
}
};
template <typename PIDRecv>
void testcase(PIDRecv &pr,
const PacketID::time_t t,
const PacketID::time_t pkt_time,
const PacketID::id_t pkt_id,
const PacketIDControl::time_t t,
const PacketIDControl::time_t pkt_time,
const PacketIDControl::id_t pkt_id,
const Error::Type expected_status)
{
const PacketIDConstruct pid(pkt_time, pkt_id);
const PacketIDControlConstruct pid(pkt_time, pkt_id);
const Error::Type status = pr.do_test_add(pid, t, true);
// OPENVPN_LOG("[" << t << "] id=" << pkt_id << " time=" << pkt_time << ' ' << Error::name(status));
ASSERT_EQ(status, expected_status);
}
template <typename PIDRecv>
void do_packet_id_recv_test()
TEST(misc, pktid_test_control)
{
typedef PacketIDControlReceiveType<3, 5> PIDRecv;
SessionStats::Ptr stats(new SessionStats());
PIDRecv pr;
pr.init(PacketID::SHORT_FORM, "test", 0, stats);
pr.init("test", 0, stats);
testcase(pr, 0, 0, 0, Error::PKTID_INVALID);
testcase(pr, 1, 0, 1, Error::SUCCESS);
@ -78,12 +87,6 @@ void do_packet_id_recv_test()
testcase(pr, 85, 15, 66, Error::SUCCESS);
}
TEST(misc, pktid_test_normal)
{
do_packet_id_recv_test<PacketIDReceiveType<3, 5>>();
}
template <unsigned int ORDER, unsigned int EXPIRE>
void perfiter(const long n,
const long range,
@ -91,20 +94,20 @@ void perfiter(const long n,
const long iter_per_step_pre,
long &count)
{
typedef PacketIDReceiveType<ORDER, EXPIRE> PIDRecv;
typedef PacketIDControlReceiveType<ORDER, EXPIRE> PIDRecv;
const long iter_per_step = iter_per_step_pre * step;
// OPENVPN_LOG("ITER order=" << ORDER << " n=" << n << " range=" << range << " step=" << step << " iter_per_step="
// << iter_per_step);
constexpr PacketID::time_t pkt_time = 1234;
constexpr PacketIDControl::time_t pkt_time = 1234;
MTRand urand;
std::vector<bool> bv(n);
long high = 0;
SessionStats::Ptr stats(new SessionStats());
PIDRecv pr;
pr.init(PacketID::SHORT_FORM, "test", 0, stats);
pr.init("test", 0, stats);
for (long i = 1; i < n; i += step)
{
@ -123,7 +126,7 @@ void perfiter(const long n,
expected = Error::PKTID_BACKTRACK;
else if (bv[id])
expected = Error::PKTID_REPLAY;
const PacketIDConstruct pid(0, static_cast<unsigned>(id));
const PacketIDControlConstruct pid(0, static_cast<unsigned>(id));
const Error::Type result = pr.do_test_add(pid, pkt_time, true);
++count;
#define INFO "i=" << i << " id=" << id << " high=" << high << " result=" << Error::name(result) << " expected=" << Error::name(expected)
@ -139,7 +142,7 @@ void perfiter(const long n,
template <unsigned int ORDER, unsigned int EXPIRE>
void perf(long &count)
{
typedef PacketIDReceiveType<ORDER, EXPIRE> PIDRecv;
typedef PacketIDControlReceiveType<ORDER, EXPIRE> PIDRecv;
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
@ -151,13 +154,11 @@ void perf(long &count)
perfiter<ORDER, EXPIRE>(20000, 4, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
}
TEST(misc, pktid_perf)
TEST(misc, pktid_control_perf)
{
{
long count = 0;
perf<3, 5>(count);
perf<6, 5>(count);
perf<8, 5>(count);
// ASSERT_EQ(4746439, count);
}
long count = 0;
perf<3, 5>(count);
perf<6, 5>(count);
perf<8, 5>(count);
// ASSERT_EQ(4746439, count);
}

View File

@ -0,0 +1,264 @@
#include "test_common.h"
#include <openvpn/crypto/packet_id_control.hpp>
#include <openvpn/crypto/packet_id_data.hpp>
using namespace openvpn;
struct PacketIDDataConstruct : public PacketIDData
{
explicit PacketIDDataConstruct(const PacketIDData::data_id_t v_id = PacketIDData::data_id_t{0}, bool wide = false)
: PacketIDData(wide)
{
id = v_id;
}
};
template <typename PIDRecv>
void testcase(PIDRecv &pr,
const std::time_t t,
const PacketIDData::data_id_t pkt_id,
const Error::Type expected_status)
{
bool wide = pr.length() > 4;
const PacketIDDataConstruct pid(pkt_id, wide);
const Error::Type status = pr.do_test_add(pid, t);
// OPENVPN_LOG("[" << t << "] id=" << pkt_id << ' ' << Error::name(status));
ASSERT_EQ(status, expected_status);
}
void do_packet_id_recv_test_short_ids(bool usewide)
{
typedef PacketIDDataReceiveType<3, 5> PIDRecv;
SessionStats::Ptr stats(new SessionStats());
PIDRecv pr;
pr.init("test", 0, usewide, stats);
testcase(pr, 0, 0, Error::PKTID_INVALID);
testcase(pr, 1, 1, Error::SUCCESS);
testcase(pr, 1, 1, Error::PKTID_REPLAY); /* replay */
testcase(pr, 2, 2, Error::SUCCESS);
testcase(pr, 3, 4, Error::SUCCESS);
testcase(pr, 4, 1, Error::PKTID_REPLAY); /* replay */
testcase(pr, 5, 3, Error::SUCCESS);
testcase(pr, 6, 8, Error::SUCCESS);
testcase(pr, 10, 5, Error::SUCCESS);
testcase(pr, 15, 7, Error::PKTID_EXPIRE); /* expire backtrack */
testcase(pr, 20, 127, Error::SUCCESS);
testcase(pr, 20, 127, Error::PKTID_REPLAY);
testcase(pr, 21, 128, Error::SUCCESS);
testcase(pr, 22, 64, Error::PKTID_BACKTRACK); /* large backtrack */
testcase(pr, 23, 65, Error::SUCCESS);
testcase(pr, 24, 66, Error::SUCCESS);
testcase(pr, 30, 0, Error::PKTID_INVALID);
testcase(pr, 33, 3, Error::PKTID_BACKTRACK); /* time backtrack */
testcase(pr, 40, 0xfffffffe, Error::SUCCESS);
testcase(pr, 41, 0xffffffff, Error::SUCCESS);
}
TEST(misc, do_packet_id_recv_test_long_ids)
{
typedef PacketIDDataReceiveType<3, 5> PIDRecv;
PIDRecv pr;
SessionStats::Ptr stats{new SessionStats()};
pr.init("test", 0, true, stats);
testcase(pr, 40, 0xfffffffe, Error::SUCCESS);
testcase(pr, 41, 0xffffffff, Error::SUCCESS);
testcase(pr, 42, 0x100000001, Error::SUCCESS);
testcase(pr, 42, 0xffffff0d, Error::PKTID_BACKTRACK);
testcase(pr, 50, 0x200000000, Error::SUCCESS);
testcase(pr, 50, 0x500000000, Error::SUCCESS);
testcase(pr, 50, 0x400000000, Error::PKTID_BACKTRACK);
testcase(pr, 50, 0x399999999, Error::PKTID_BACKTRACK);
testcase(pr, 50, 0x3ffffffff, Error::PKTID_BACKTRACK);
testcase(pr, 50, 0x4ffffffff, Error::SUCCESS);
}
TEST(misc, pktid_test_data_32bit)
{
do_packet_id_recv_test_short_ids(false);
}
TEST(misc, pktid_test_data_64bit)
{
do_packet_id_recv_test_short_ids(true);
}
template <unsigned int ORDER, unsigned int EXPIRE>
void perfiter(const long n,
const long range,
const long step,
const long iter_per_step_pre,
long &count)
{
typedef PacketIDDataReceiveType<ORDER, EXPIRE> PIDRecv;
const long iter_per_step = iter_per_step_pre * step;
// OPENVPN_LOG("ITER order=" << ORDER << " n=" << n << " range=" << range << " step=" << step << " iter_per_step="
// << iter_per_step);
constexpr std::time_t pkt_time = 1234;
MTRand urand;
std::vector<bool> bv(n);
long high = 0;
SessionStats::Ptr stats(new SessionStats());
PIDRecv pr;
pr.init("test", 0, false, stats);
for (long i = 1; i < n; i += step)
{
for (long j = 0; j < iter_per_step; ++j)
{
const long delta = long(urand.randrange32(static_cast<uint32_t>(range))) - range / 2;
const long id = i + delta;
if (id >= 0 && id < n)
{
if (id > high)
high = id;
Error::Type expected = Error::SUCCESS;
if (!id)
expected = Error::PKTID_INVALID;
else if (high - id >= (const long)PIDRecv::REPLAY_WINDOW_SIZE)
expected = Error::PKTID_BACKTRACK;
else if (bv[id])
expected = Error::PKTID_REPLAY;
const PacketIDDataConstruct pid(id);
const Error::Type result = pr.do_test_add(pid, pkt_time);
++count;
#define INFO "i=" << i << " id=" << id << " high=" << high << " result=" << Error::name(result) << " expected=" << Error::name(expected)
// OPENVPN_LOG(INFO);
ASSERT_EQ(result, expected) << INFO;
if (expected == Error::SUCCESS)
bv[id] = true;
}
}
}
}
template <unsigned int ORDER, unsigned int EXPIRE>
void perf(long &count)
{
typedef PacketIDDataReceiveType<ORDER, EXPIRE> PIDRecv;
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 3, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 2, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, PIDRecv::REPLAY_WINDOW_SIZE * 2, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
perfiter<ORDER, EXPIRE>(20000, 16, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, 16, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
perfiter<ORDER, EXPIRE>(20000, 4, 1, 10, count);
perfiter<ORDER, EXPIRE>(20000, 4, PIDRecv::REPLAY_WINDOW_SIZE / 2, 10, count);
}
class PacketIDDataSendTest : public openvpn::PacketIDDataSend
{
public:
PacketIDDataSendTest(bool wide, std::uint64_t start)
: openvpn::PacketIDDataSend(wide)
{
pid_ = PacketIDDataConstruct{start, wide};
}
};
TEST(misc, pktid_32_bit_overrun_32bit_counter)
{
PacketIDDataSendTest pidsend{false, 0xfffffffc};
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffd]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffe]");
EXPECT_THROW(ret = pidsend.next(), PacketIDDataSend::packet_id_wrap);
}
TEST(misc, pktid_32_bit_overrun_64bit_counter)
{
PacketIDDataSendTest pidsend{true, 0xfffffffd};
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffe]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xffffffff]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0x100000000]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0x100000001]");
}
TEST(misc, pktid_64_bit_overrun_64bit_counter)
{
PacketIDDataSendTest pidsend{true, 0xfffffffffffffffc};
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffffffffffd]");
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfffffffffffffffe]");
EXPECT_THROW(ret = pidsend.next(), PacketIDDataSend::packet_id_wrap);
}
TEST(misc, pktid_32_bit_warn)
{
PacketIDDataSendTest pidsend{false, 0xfefffffe};
EXPECT_FALSE(pidsend.wrap_warning());
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfeffffff]");
EXPECT_FALSE(pidsend.wrap_warning());
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xff000000]");
EXPECT_TRUE(pidsend.wrap_warning());
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xff000001]");
EXPECT_TRUE(pidsend.wrap_warning());
}
TEST(misc, pktid_64_bit_warn_32bit)
{
// Test we are not warning at 32bit
PacketIDDataSendTest pidsend{true, 0xfefffffe};
EXPECT_FALSE(pidsend.wrap_warning());
auto ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xfeffffff]");
EXPECT_FALSE(pidsend.wrap_warning());
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xff000000]");
EXPECT_FALSE(pidsend.wrap_warning());
ret = pidsend.next();
EXPECT_EQ(ret.str(), "[0xff000001]");
EXPECT_FALSE(pidsend.wrap_warning());
}
TEST(misc, pktid_data_perf)
{
{
long count = 0;
perf<3, 5>(count);
perf<6, 5>(count);
perf<8, 5>(count);
// ASSERT_EQ(4746439, count);
}
}

View File

@ -18,7 +18,7 @@
#include <openvpn/reliable/relrecv.hpp>
#include <openvpn/reliable/relsend.hpp>
#include <openvpn/reliable/relack.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/packet_id_control.hpp>
using namespace openvpn;
@ -154,14 +154,14 @@ typedef ReliableRecvTemplate<Packet> ReliableRecv;
struct Message
{
openvpn::PacketID::id_t id;
openvpn::PacketIDControl::id_t id;
BufferPtr buffer;
};
void print_msg(const Time::Duration t,
const char *title,
BufferPtr &buf,
const openvpn::PacketID::id_t id,
const openvpn::PacketIDControl::id_t id,
std::stringstream &case_detail)
{
case_detail << t.raw() << ' ' << title
@ -175,7 +175,7 @@ void test(MTRand &rand,
const Time::Duration end,
const Time::Duration step,
const Time::Duration end_sends,
const openvpn::PacketID::id_t relsize,
const openvpn::PacketIDControl::id_t relsize,
const size_t wiresize,
const unsigned int reorder_prob,
const unsigned int drop_prob,
@ -193,8 +193,8 @@ void test(MTRand &rand,
long iterations = 0;
Time::Duration t;
openvpn::PacketID::id_t send_id = 0;
openvpn::PacketID::id_t rec_id = 0;
openvpn::PacketIDControl::id_t send_id = 0;
openvpn::PacketIDControl::id_t rec_id = 0;
for (t = Time::Duration(); t < end; t += step)
{
@ -204,7 +204,7 @@ void test(MTRand &rand,
// sender processes ACKs received from receiver
while (!acklist.empty())
{
const openvpn::PacketID::id_t id = acklist.front();
const openvpn::PacketIDControl::id_t id = acklist.front();
acklist.pop_front();
if (rand.randrange(40)) // with small probability, simulate a dropped ACK
// JMD_TODO: why wouldn't this have drop_prob probability
@ -221,7 +221,7 @@ void test(MTRand &rand,
// scan the sender history for un-ACKed packets that need to be retransmitted
if (now >= retrans)
{
for (openvpn::PacketID::id_t i = send.head_id(); i < send.tail_id(); ++i)
for (openvpn::PacketIDControl::id_t i = send.head_id(); i < send.tail_id(); ++i)
{
ReliableSend::Message &m = send.ref_by_id(i);
if (m.ready_retransmit(now))
@ -327,7 +327,7 @@ void test(MTRand &rand,
struct test_params
{
int test_case;
openvpn::PacketID::id_t relsize;
openvpn::PacketIDControl::id_t relsize;
size_t wiresize;
unsigned int reorder_prob;
unsigned int drop_prob;