mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 04:02:15 +02:00
dbd5995d7a
1. Added specific details on DATA_V2/peer-id/float support. 2. For AEAD mode, emphasized that the leading 8 bytes (4 bytes for DATA_V2/peer-id and 4 for packet ID) is all included in the AD. 3. Added specific details on protocol negotiation where the client indicates protocol extension availability with IV_x parameters in the peer info string, and the server responds by pushing directives to the client to enable the feature. 4. Added "TCP nonlinear mode" section, a new protocol extension that is needed by multithreaded TCP servers.
199 lines
8.4 KiB
Plaintext
199 lines
8.4 KiB
Plaintext
OpenVPN Protocol extensions
|
|
2015-01-06
|
|
|
|
1. DATA_V2 opcode with 24-bit peer ID
|
|
|
|
* The DATA_V2 opcode is 9.
|
|
* The DATA_V2 opcode/key_id byte is followed by 3 additional
|
|
(network endian) bytes indicating the peer ID.
|
|
* If a 4-byte DATA_V2 header is passed through ntohl,
|
|
the resulting high 8 bits will be the DATA_V2 opcode/key_id,
|
|
and the lower 24 bits will be the peer ID.
|
|
* A disabled peer ID is denoted by 0xFFFFFF.
|
|
* Server tells the client to use DATA_V2/peer_id by pushing
|
|
the directive "peer-id ID" where ID is a decimal integer
|
|
in the range [-1, 16777215]. Setting the peer ID to -1
|
|
transmits DATA_V2 packets with the peer ID field set to
|
|
0xFFFFFF. Setting the peer_id to -1 is the same as
|
|
setting it to 16777215 (i.e. 0xFFFFFF).
|
|
* Client never transmits DATA_V2 packets unless the server
|
|
has pushed a "peer-id" directive.
|
|
* Server never pushes a "peer-id" directive unless the
|
|
client has indicated its support for DATA_V2 by
|
|
including "IV_PROTO=2" in the peer info data.
|
|
* When DATA_V2 is used for "float" functionality, the server
|
|
must perform the following checks before allowing
|
|
a client to float, i.e. to assume a new source address.
|
|
(a) verify integrity (HMAC or GCM auth tag, replay
|
|
protection, etc.) of the DATA_V2 packet, and
|
|
(b) ensure that the float doesn't clobber a pre-existing
|
|
client (i.e. if the address floated to is already
|
|
owned by another client) unless it can be verified
|
|
that the pre-existing client is a previous instance
|
|
of the floating client.
|
|
|
|
2. AEAD mode
|
|
|
|
To support AEAD crypto modes such as AES-GCM, some protocol
|
|
changes are in order. AES-GCM, for example, requires a 12
|
|
byte unique nonce for every packet. I would propose that 4
|
|
bytes be taken from the Packet ID which increments for every
|
|
packet and therefore provides uniqueness. The remaining 8
|
|
bytes would be derived from the random key material that would
|
|
normally be used to key the HMAC key. This is possible since
|
|
AEAD modes use a combined key for encryption and integrity
|
|
checking, therefore the random key material for HMAC is
|
|
unused and can be repurposed as an AEAD nonce source. The 8
|
|
byte nonce component derived from the HMAC keying material
|
|
would remain constant for a given Key State. Only the 4 byte
|
|
Packet ID would increment for each packet. Because AEAD
|
|
encryption can be compromised if the nonce ever repeats for
|
|
a given key, the implementation MUST disable encryption
|
|
for a key if the 32-bit packet ID wraps. In practical usage,
|
|
renegotiation usually preempts wrapping, so the
|
|
disable-encryption-on-wrap feature is a failsafe.
|
|
|
|
AEAD Nonce:
|
|
|
|
[ Packet ID ] [ HMAC keying material ]
|
|
[ 4 bytes ] [ 8 bytes ]
|
|
[ AEAD nonce total: 12 bytes ]
|
|
|
|
TLS wire protocol:
|
|
|
|
[ DATA_V2 opcode ] [ Packet ID ] [ AEAD Auth tag ] [ ciphertext ]
|
|
[ 4 bytes ] [ 4 bytes ] [ 16 bytes ]
|
|
[ AEAD additional data (AD) ]
|
|
|
|
Static Key wire protocol:
|
|
|
|
[ DATA_V2 opcode ] [ Packet ID ] [ Nonce tail (random) ] [ AEAD Auth tag ] [ ciphertext ]
|
|
[ AEAD nonce ]
|
|
[ 4 bytes ] [ 8 bytes ] [ 4 bytes ] [ 16 bytes ]
|
|
[ AEAD additional data (AD) ]
|
|
|
|
Note that the AEAD additional data (AD) includes all data
|
|
preceding the AEAD Auth tag including the DATA_V2/peer_id
|
|
opcode and packet ID.
|
|
|
|
Also, note that because the HMAC keying material used to derive
|
|
the last 8 bytes of the AEAD nonce is negotiated once per key
|
|
as part of the control channel handshake, we can omit it from the
|
|
data channel packets, thereby saving 8 bytes per packet. So
|
|
only the 4-byte Packet ID component of the nonce must be
|
|
transmitted with every packet.
|
|
|
|
When negotiating AEAD mode, the client indicates its support
|
|
of AES-128-GCM, AES-192-GCM, and AES-192-GCM by including:
|
|
|
|
IV_NCP=2
|
|
|
|
in the peer info string (NCP stands for Negotiable Crypto
|
|
Parameters).
|
|
|
|
When the IV_NCP value is 2 or higher, it indicates that
|
|
the server may push an AEAD "cipher" directive, e.g.:
|
|
|
|
push "cipher AES-128-GCM"
|
|
|
|
In the future, the IV_NCP value (2 in the current
|
|
implementation) may be increased to indicate the
|
|
availability of additional negotiable ciphers.
|
|
|
|
3. Compression V2
|
|
|
|
I have observed that compression in many cases, even when
|
|
enabled, often does not produce packet size reduction
|
|
because much of the packet data typically generated by web
|
|
sessions is already compressed. Further, the single byte that
|
|
precedes the packet and indicates whether or not compression
|
|
occurred has the unfortunate side effect of misaligning the IP
|
|
packet in cases where compression did not occur. To remedy this,
|
|
I propose a Compression V2 header that is optimized for the
|
|
case where compression does not occur.
|
|
|
|
a. No compression occurred and first byte of IP/Ethernet packet
|
|
is NOT 0x50 (0 bytes of overhead and maintains alignment):
|
|
|
|
[ uncompressed IP/Ethernet packet ]
|
|
|
|
b. No compression occurred and first byte of IP/Ethernet packet
|
|
is 0x50 (2 bytes of overhead but unlikely since no known
|
|
IP packet can begin with 0x50):
|
|
|
|
[ 0x50 ] [ 0x00 ] [ uncompressed IP/Ethernet packet ]
|
|
|
|
c. Compression occurred (2 bytes of overhead):
|
|
|
|
[ 0x50 ] [ compression Alg ID byte ] [ compressed IP/Ethernet packet ]
|
|
|
|
Compression Alg ID is one-byte algorithm identifier
|
|
for LZ4 (0x1), LZO (0x2), or Snappy (0x3).
|
|
|
|
This approach has several beneficial effects:
|
|
|
|
1. In the common case where compression does not occur, no
|
|
compression op is required, therefore there is zero overhead.
|
|
|
|
2. When compression does not occur, the IP/Ethernet packet
|
|
alignment is retained.
|
|
|
|
3. This technique does not require any byte swapping with
|
|
the tail of the packet which can potentially incur an
|
|
expensive cache miss.
|
|
|
|
When negotiating Compression V2 mode, the client indicates its
|
|
support by including the following in the peer info string:
|
|
|
|
IV_LZ4v2=1 -> LZ4 compression available in V2 format
|
|
IV_COMP_STUBv2=1 -> stub compression available in V2 format
|
|
(i.e. disable compression but still
|
|
retain compression framing)
|
|
|
|
In response, the server can push to the client:
|
|
|
|
push "compress lz4-v2" -> enable LZ4 compression in V2 format
|
|
|
|
or
|
|
|
|
push "compress stub-v2" -> disable compression but retain
|
|
compression framing in V2 format
|
|
|
|
4. TCP nonlinear mode
|
|
|
|
The OpenVPN 2.x packet ID and replay protection code, when running
|
|
in TCP mode, requires that the packet ID follows a linearly
|
|
incrementing sequence, i.e. 1, 2, 2, 3, ... This was a reasonable
|
|
requirement, since the reliable nature of TCP guaranteed that a
|
|
linear sequence of packet IDs transmitted by the sender would be
|
|
received in the same order by the receiver.
|
|
|
|
However, recent work has shown that multithreaded OpenVPN servers
|
|
may not be able to guarantee TCP packet ID linearity (on the
|
|
transmit side) without incurring a performance penalty. This
|
|
is because the packet ID for transmitted packets must be allocated
|
|
before the packet is encrypted, while a multithreaded OpenVPN server
|
|
might be concurrently encrypting and transmitting multiple packets
|
|
using different threads, where the order that the threads complete
|
|
encryption and transmit the packet is non-deterministic. This
|
|
non-deterministic ordering of packets over the TCP session means
|
|
that the client might see out-of-order packets and a non-linear
|
|
packet ID progression, just as clients now see with UDP.
|
|
|
|
My proposed solution to the issue is to relax the Packet ID
|
|
validation on the receiver side to allow non-linear packet ID
|
|
sequences, even in TCP mode. This essentially means that the
|
|
packet ID validation logic is now the same for both UDP and TCP.
|
|
|
|
The client indicates its ability to process non-linear packet
|
|
ID sequences in TCP mode by including the following in the
|
|
peer info string:
|
|
|
|
IV_TCPNL=1 -> TCP non-linear receiver supported
|
|
|
|
When the server receives a IV_TCPNL setting of 1 from the
|
|
client, it may transmit out-of-order packets in TCP mode.
|
|
Otherwise, servers must use other means (such as using thread
|
|
synchronization primitives) to ensure strictly linear packet
|
|
ID ordering in TCP mode.
|