Skip to content

Commit d546c8d

Browse files
committed
Implement epoch key data format
With DCO and possible future hardware assisted OpenVPN acceleration we are approaching the point where 32 bit IVs are not cutting it any more, especially if we are limiting the IVs to the safe limits of AES-GCM where the limit is more 2^29. To illustrate the problem, some back of the envelope math here: If we want to keep the current 3600s renegotiation interval and have a safety margin of 25% (when we trigger renegotiation) we have about 3.2 million packets (2*32 * 0.7) to work with. That translates to about 835k packets per second. Currently, implementation trigger the renegotiation at 0xff00000000 or at 7/8 of the AEAD usage limit. With 1300 Byte packets that translates into 8-9 Gbit/s. That is far from unrealistic any more. Current DCO implementations are already in spitting distance to that or might even reach (for a single client connection) that if you have extremely fast single core performance CPU. With the AEAD usage limit, these limits are almost a factor of 8 lower so with the limit becomes 1-2 GBit/s. This is already reached without DCO on some platforms. This introduces the epoch data format for AEAD data channel ciphers in TLS mode ciphers. No effort has been made to support larger packet counters in any other scenario since those are all legacy. This uses the same approach of epoch keys as (D)TLS 1.3 does and switches the data channel regularly for affected AEAD ciphers when reaching the usage limit. For Chacha20-Poly1305, which does not suffer the same problems as AES-GCM, the full 48 bit of packet counter are used only after that the same logic to switch to a new key as with AES-GCM is done. Change-Id: I00751c42cb04e30205ba8e6584530831e0d143c5 Signed-off-by: Arne Schwabe <[email protected]>
1 parent f649a3c commit d546c8d

14 files changed

+444
-61
lines changed

CMakeLists.txt

+8-1
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@ if (BUILD_TESTING)
705705

706706
target_sources(test_auth_token PRIVATE
707707
src/openvpn/base64.c
708+
src/openvpn/crypto_epoch.c
708709
src/openvpn/crypto_mbedtls.c
709710
src/openvpn/crypto_openssl.c
710711
src/openvpn/crypto.c
@@ -733,9 +734,10 @@ if (BUILD_TESTING)
733734
tests/unit_tests/openvpn/mock_win32_execve.c
734735
src/openvpn/argv.c
735736
src/openvpn/base64.c
736-
src/openvpn/crypto.c
737+
src/openvpn/crypto_epoch.c
737738
src/openvpn/crypto_mbedtls.c
738739
src/openvpn/crypto_openssl.c
740+
src/openvpn/crypto.c
739741
src/openvpn/cryptoapi.c
740742
src/openvpn/env_set.c
741743
src/openvpn/mss.c
@@ -761,6 +763,7 @@ if (BUILD_TESTING)
761763
)
762764

763765
target_sources(test_ncp PRIVATE
766+
src/openvpn/crypto_epoch.c
764767
src/openvpn/crypto_mbedtls.c
765768
src/openvpn/crypto_openssl.c
766769
src/openvpn/crypto.c
@@ -782,6 +785,7 @@ if (BUILD_TESTING)
782785
tests/unit_tests/openvpn/mock_win32_execve.c
783786
src/openvpn/argv.c
784787
src/openvpn/base64.c
788+
src/openvpn/crypto_epoch.c
785789
src/openvpn/crypto_mbedtls.c
786790
src/openvpn/crypto_openssl.c
787791
src/openvpn/crypto.c
@@ -835,9 +839,11 @@ if (BUILD_TESTING)
835839
target_compile_options(test_networking PRIVATE -UNDEBUG)
836840
target_sources(test_networking PRIVATE
837841
src/openvpn/networking_sitnl.c
842+
src/openvpn/crypto_epoch.c
838843
src/openvpn/crypto_mbedtls.c
839844
src/openvpn/crypto_openssl.c
840845
src/openvpn/crypto.c
846+
src/openvpn/crypto_epoch.c
841847
src/openvpn/otime.c
842848
src/openvpn/packet_id.c
843849
)
@@ -853,6 +859,7 @@ if (BUILD_TESTING)
853859
tests/unit_tests/openvpn/mock_win32_execve.c
854860
src/openvpn/argv.c
855861
src/openvpn/base64.c
862+
src/openvpn/crypto_epoch.c
856863
src/openvpn/crypto_mbedtls.c
857864
src/openvpn/crypto_openssl.c
858865
src/openvpn/crypto.c

src/openvpn/crypto.c

+102-35
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
#include <string.h>
3333

3434
#include "crypto.h"
35+
#include "crypto_epoch.h"
36+
#include "packet_id.h"
3537
#include "error.h"
3638
#include "integer.h"
3739
#include "platform.h"
@@ -68,7 +70,15 @@ openvpn_encrypt_aead(struct buffer *buf, struct buffer work,
6870
{
6971
struct gc_arena gc;
7072
int outlen = 0;
73+
const bool use_epoch_data_format = opt->flags & CO_EPOCH_DATA_KEY_FORMAT;
74+
75+
if (use_epoch_data_format)
76+
{
77+
epoch_check_send_iterate(opt);
78+
}
79+
7180
const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
81+
7282
uint8_t *mac_out = NULL;
7383
const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
7484

@@ -89,14 +99,24 @@ openvpn_encrypt_aead(struct buffer *buf, struct buffer work,
8999
buf_set_write(&iv_buffer, iv, iv_len);
90100

91101
/* IV starts with packet id to make the IV unique for packet */
92-
if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
102+
if (use_epoch_data_format)
93103
{
94-
msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
95-
goto err;
104+
if (!packet_id_write_epoch(&opt->packet_id.send, ctx->epoch, &iv_buffer))
105+
{
106+
msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
107+
goto err;
108+
}
109+
}
110+
else
111+
{
112+
if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
113+
{
114+
msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
115+
goto err;
116+
}
96117
}
97-
98118
/* Write packet id part of IV to work buffer */
99-
ASSERT(buf_write(&work, iv, packet_id_size(false)));
119+
ASSERT(buf_write(&work, iv, buf_len(&iv_buffer)));
100120

101121
/* Remainder of IV consists of implicit part (unique per session)
102122
* XOR of packet and implicit IV */
@@ -128,7 +148,7 @@ openvpn_encrypt_aead(struct buffer *buf, struct buffer work,
128148
dmsg(D_PACKET_CONTENT, "ENCRYPT AD: %s",
129149
format_hex(BPTR(&work), BLEN(&work), 0, &gc));
130150

131-
if (!(opt->flags & CO_EPOCH_DATA_KEY_FORMAT))
151+
if (!use_epoch_data_format)
132152
{
133153
/* Reserve space for authentication tag */
134154
mac_out = buf_write_alloc(&work, mac_len);
@@ -149,7 +169,7 @@ openvpn_encrypt_aead(struct buffer *buf, struct buffer work,
149169
ASSERT(buf_inc_len(&work, outlen));
150170

151171
/* if the tag is at end the end, allocate it now */
152-
if (opt->flags & CO_EPOCH_DATA_KEY_FORMAT)
172+
if (use_epoch_data_format)
153173
{
154174
/* Reserve space for authentication tag */
155175
mac_out = buf_write_alloc(&work, mac_len);
@@ -365,14 +385,35 @@ cipher_get_aead_limits(const char *ciphername)
365385

366386
bool
367387
crypto_check_replay(struct crypto_options *opt,
368-
const struct packet_id_net *pin, const char *error_prefix,
388+
const struct packet_id_net *pin, uint16_t epoch,
389+
const char *error_prefix,
369390
struct gc_arena *gc)
370391
{
371392
bool ret = false;
372-
packet_id_reap_test(&opt->packet_id.rec);
373-
if (packet_id_test(&opt->packet_id.rec, pin))
393+
struct packet_id_rec *recv;
394+
395+
if (epoch == 0 || opt->key_ctx_bi.decrypt.epoch == epoch)
396+
{
397+
recv = &opt->packet_id.rec;
398+
}
399+
else if (epoch == opt->epoch_retiring_data_receive_key.epoch)
400+
{
401+
recv = &opt->epoch_retiring_key_pid_recv;
402+
}
403+
else
404+
{
405+
/* We have an epoch that is neither current or old recv key but
406+
* is authenticated, ie we need to move to a new current recv key */
407+
msg(D_GENKEY, "Received data packet with new epoch %d. Updating "
408+
"receive key", epoch);
409+
epoch_replace_update_recv_key(opt, epoch);
410+
recv = &opt->packet_id.rec;
411+
}
412+
413+
packet_id_reap_test(recv);
414+
if (packet_id_test(recv, pin))
374415
{
375-
packet_id_add(&opt->packet_id.rec, pin);
416+
packet_id_add(recv, pin);
376417
if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM))
377418
{
378419
packet_id_persist_save_obj(opt->pid_persist, &opt->packet_id);
@@ -408,8 +449,9 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
408449
static const char error_prefix[] = "AEAD Decrypt error";
409450
struct packet_id_net pin = { 0 };
410451
const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt;
411-
int outlen;
412452
struct gc_arena gc;
453+
const bool use_epoch_data_format = opt->flags & CO_EPOCH_DATA_KEY_FORMAT;
454+
const int tag_size = OPENVPN_AEAD_TAG_LENGTH;
413455

414456
gc_init(&gc);
415457

@@ -428,20 +470,58 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
428470
/* IV and Packet ID required for this mode */
429471
ASSERT(packet_id_initialized(&opt->packet_id));
430472

473+
/* Ensure that the packet size is long enough */
474+
int min_packet_len = packet_id_size(false) + tag_size + 1;
475+
476+
if (use_epoch_data_format)
477+
{
478+
min_packet_len += sizeof(uint32_t);
479+
}
480+
481+
if (buf->len < min_packet_len)
482+
{
483+
CRYPT_ERROR("missing IV info, missing tag or no payload");
484+
}
485+
486+
uint16_t epoch = 0;
431487
/* Combine IV from explicit part from packet and implicit part from context */
432488
{
433489
uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
434490
const int iv_len = cipher_ctx_iv_length(ctx->cipher);
435-
const size_t packet_iv_len = packet_id_size(false);
436491

437-
if (buf->len < packet_id_size(false))
492+
/* Read packet id. For epoch data format also lookup the epoch key
493+
* to be able to use the implicit IV of the correct decryption key */
494+
if (use_epoch_data_format)
438495
{
439-
CRYPT_ERROR("missing IV info");
440-
}
496+
/* packet ID format is 16 bit epoch + 48 per epoch packet-counter */
497+
const size_t packet_iv_len = sizeof(uint64_t);
441498

442-
memcpy(iv, BPTR(buf), packet_iv_len);
499+
/* copy the epoch-counter part into the IV */
500+
memcpy(iv, BPTR(buf), packet_iv_len);
443501

444-
/* Remainder of IV consists of implicit part (unique per session)
502+
epoch = packet_id_read_epoch(&pin, buf);
503+
if (epoch == 0)
504+
{
505+
CRYPT_ERROR("error reading packet-id");
506+
}
507+
ctx = epoch_lookup_decrypt_key(opt, epoch);
508+
if (!ctx)
509+
{
510+
CRYPT_ERROR("data packet with unknown epoch");
511+
}
512+
}
513+
else
514+
{
515+
const size_t packet_iv_len = packet_id_size(false);
516+
/* Packet ID form is a 32 bit packet counter */
517+
memcpy(iv, BPTR(buf), packet_iv_len);
518+
if (!packet_id_read(&pin, buf, false))
519+
{
520+
CRYPT_ERROR("error reading packet-id");
521+
}
522+
}
523+
524+
/* Remainder of IV consists of implicit part (unique per session/epoch key)
445525
* XOR of packet counter and implicit IV */
446526
for (int i = 0; i < iv_len; i++)
447527
{
@@ -457,25 +537,12 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
457537
}
458538
}
459539

460-
/* Read packet ID from packet */
461-
if (!packet_id_read(&pin, buf, false))
462-
{
463-
CRYPT_ERROR("error reading packet-id");
464-
}
465-
466-
/* keep the tag value to feed in later */
467-
const int tag_size = OPENVPN_AEAD_TAG_LENGTH;
468-
if (buf->len < tag_size + 1)
469-
{
470-
CRYPT_ERROR("missing tag or no payload");
471-
}
472-
473540
const int ad_size = BPTR(buf) - ad_start;
474541

475542
uint8_t *tag_ptr = NULL;
476543
int data_len = 0;
477544

478-
if (opt->flags & CO_EPOCH_DATA_KEY_FORMAT)
545+
if (use_epoch_data_format)
479546
{
480547
data_len = BLEN(buf) - tag_size;
481548
tag_ptr = BPTR(buf) + data_len;
@@ -496,13 +563,13 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
496563
CRYPT_ERROR("potential buffer overflow");
497564
}
498565

499-
500566
/* feed in tag and the authenticated data */
501567
ASSERT(cipher_ctx_update_ad(ctx->cipher, ad_start, ad_size));
502568
dmsg(D_PACKET_CONTENT, "DECRYPT AD: %s",
503569
format_hex(ad_start, ad_size, 0, &gc));
504570

505571
/* Decrypt and authenticate packet */
572+
int outlen;
506573
if (!cipher_ctx_update(ctx->cipher, BPTR(&work), &outlen, BPTR(buf),
507574
data_len))
508575
{
@@ -525,7 +592,7 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
525592
dmsg(D_PACKET_CONTENT, "DECRYPT TO: %s",
526593
format_hex(BPTR(&work), BLEN(&work), 80, &gc));
527594

528-
if (!crypto_check_replay(opt, &pin, error_prefix, &gc))
595+
if (!crypto_check_replay(opt, &pin, epoch, error_prefix, &gc))
529596
{
530597
goto error_exit;
531598
}
@@ -696,7 +763,7 @@ openvpn_decrypt_v1(struct buffer *buf, struct buffer work,
696763
}
697764
}
698765

699-
if (have_pin && !crypto_check_replay(opt, &pin, error_prefix, &gc))
766+
if (have_pin && !crypto_check_replay(opt, &pin, 0, error_prefix, &gc))
700767
{
701768
goto error_exit;
702769
}

src/openvpn/crypto.h

+1
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ bool openvpn_decrypt(struct buffer *buf, struct buffer work,
518518
*/
519519
bool crypto_check_replay(struct crypto_options *opt,
520520
const struct packet_id_net *pin,
521+
uint16_t epoch,
521522
const char *error_prefix,
522523
struct gc_arena *gc);
523524

src/openvpn/crypto_backend.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
#include "basic.h"
3939
#include "buffer.h"
4040

41-
/* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */
41+
/* TLS uses a tag of 128 bits, let's do the same for OpenVPN */
4242
#define OPENVPN_AEAD_TAG_LENGTH 16
4343

4444
/* Maximum cipher block size (bytes) */

src/openvpn/dco.h

+14
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ int dco_get_peer_stats(struct context *c);
249249
*/
250250
const char *dco_get_supported_ciphers(void);
251251

252+
/**
253+
* Return whether the dco implementation supports the new protocol features of
254+
* a 64 bit packet counter and AEAD tag at the end.
255+
*/
256+
static inline bool
257+
dco_supports_epoch_data(struct context *c)
258+
{
259+
return false;
260+
}
252261
#else /* if defined(ENABLE_DCO) */
253262

254263
typedef void *dco_context_t;
@@ -380,5 +389,10 @@ dco_get_supported_ciphers(void)
380389
return "";
381390
}
382391

392+
static inline bool
393+
dco_supports_epoch_data(struct context *c)
394+
{
395+
return false;
396+
}
383397
#endif /* defined(ENABLE_DCO) */
384398
#endif /* ifndef DCO_H */

src/openvpn/init.c

+22
Original file line numberDiff line numberDiff line change
@@ -2761,6 +2761,19 @@ do_deferred_options(struct context *c, const unsigned int found)
27612761
}
27622762
}
27632763

2764+
/* Ensure that for epoch data format is only enabled if also data v2
2765+
* is enabled */
2766+
bool epoch_data = (c->options.imported_protocol_flags & CO_EPOCH_DATA_KEY_FORMAT);
2767+
bool datav2_enabled = (c->options.peer_id >= 0 && c->options.peer_id < MAX_PEER_ID);
2768+
2769+
if (epoch_data && !datav2_enabled)
2770+
{
2771+
msg(D_PUSH_ERRORS, "OPTIONS ERROR: Epoch key data format tag requires "
2772+
"data v2 (peer-id) to be enabled.");
2773+
return false;
2774+
}
2775+
2776+
27642777
if (found & OPT_P_PUSH_MTU)
27652778
{
27662779
/* MTU has changed, check that the pushed MTU is small enough to
@@ -3357,6 +3370,15 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
33573370
to.push_peer_info_detail = 1;
33583371
}
33593372

3373+
/* Check if the DCO drivers support the epoch data format */
3374+
if (dco_enabled(options))
3375+
{
3376+
to.data_epoch_supported = dco_supports_epoch_data(c);
3377+
}
3378+
else
3379+
{
3380+
to.data_epoch_supported = true;
3381+
}
33603382

33613383
/* should we not xmit any packets until we get an initial
33623384
* response from client? */

src/openvpn/multi.c

+6
Original file line numberDiff line numberDiff line change
@@ -1849,6 +1849,12 @@ multi_client_set_protocol_options(struct context *c)
18491849
}
18501850
#endif
18511851

1852+
if (tls_multi->session[TM_ACTIVE].opt->data_epoch_supported
1853+
&& (proto & IV_PROTO_DATA_EPOCH))
1854+
{
1855+
o->imported_protocol_flags |= CO_EPOCH_DATA_KEY_FORMAT;
1856+
}
1857+
18521858
if (proto & IV_PROTO_CC_EXIT_NOTIFY)
18531859
{
18541860
o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;

0 commit comments

Comments
 (0)