All of lore.kernel.org
 help / color / mirror / Atom feed
* [Openvpn-devel] [M] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
@ 2024-01-25 12:38 ` plaisthos (Code Review)
  2024-02-02 12:48 ` [Openvpn-devel] [L] " plaisthos (Code Review)
                   ` (29 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-01-25 12:38 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 14601 bytes --]

Attention is currently required from: flichtenheld.

Hello flichtenheld,

I'd like you to do a code review.
Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to review the following change.


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters for AEAD data channel
ciphers in TLS mode ciphers. No effort has been made to support
larger packet counters in any scenario since the other scenarios
are all legacy.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids) forward. But this is an obscure edge that we can
(currently) live with.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
10 files changed, 105 insertions(+), 20 deletions(-)



  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/1

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 9988ebe..81b33fe 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, longiv, false))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -384,6 +385,8 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
     /* Combine IV from explicit part from packet and implicit part from context */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
@@ -409,7 +412,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 95a5b31..0ef13e0 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -283,6 +283,11 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we normally use.
+     */
+

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index cd37b36..7db8d06 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2327,6 +2327,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -3297,6 +3301,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = false;
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 4344126..a80b9f4 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 39f00c0..3f8fccf 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8690,6 +8690,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index e4c122c..5766c97 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -691,6 +691,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 33c8670..6579ff9 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);


 /**
@@ -1369,13 +1371,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1513,14 +1517,15 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
         size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1935,6 +1940,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index f085e0d..53bf763 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -311,7 +311,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -361,6 +360,8 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;
 
+    bool data_v3_features_supported; /**< dco supports new data channel features */
+
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;

@@ -490,8 +491,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 0ca6d42..0b4ad8a 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index 0ded052..125d24b 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -128,9 +128,12 @@
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

@@ -142,6 +145,11 @@
         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
                co->key_ctx_bi.encrypt.implicit_iv, OPENVPN_MAX_IV_LENGTH);
         co->key_ctx_bi.decrypt.implicit_iv_len = impl_iv_len;
+
+        if (longiv)
+        {
+            co->flags |= CO_64_BIT_PKT_ID;
+        }
     }
 }

@@ -280,6 +288,25 @@
 }

 static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none");
+    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none");
+    co.flags |= CO_64_BIT_PKT_ID;
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+
+static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
     struct crypto_options co = init_crypto_options(cipher, auth);
@@ -289,24 +316,30 @@


 static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}
+
+static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -336,8 +369,7 @@
         return;
     }

-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 1
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newchange

[-- Attachment #2: Type: text/html, Size: 25886 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
  2024-01-25 12:38 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement support for larger packet counter sizes plaisthos (Code Review)
@ 2024-02-02 12:48 ` plaisthos (Code Review)
  2024-02-05 11:24 ` flichtenheld (Code Review)
                   ` (28 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-02-02 12:48 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 26818 bytes --]

Attention is currently required from: flichtenheld.

Hello flichtenheld, 

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to look at the new patch set (#2).


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters for AEAD data channel
ciphers in TLS mode ciphers. No effort has been made to support
larger packet counters in any scenario since the other scenarios
are all legacy.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids) forward. But this is an obscure edge that we can
(currently) live with.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/packet_id.c
M src/openvpn/packet_id.h
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
12 files changed, 360 insertions(+), 40 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/2

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 9988ebe..747a5f2 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,6 +388,8 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
     /* Combine IV from explicit part from packet and implicit part from context */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
@@ -409,7 +415,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 95a5b31..0ef13e0 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -283,6 +283,11 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we normally use.
+     */
+

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index cd37b36..7db8d06 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2327,6 +2327,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -3297,6 +3301,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = false;
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 4344126..a80b9f4 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 39f00c0..3f8fccf 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8690,6 +8690,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index ef83248..f67f8ee 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }

+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }

 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index af62c89..e0546a1 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);

 /**
+ * Variant of packet_id_read that expect the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire.
+ *
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,23 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);

+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seens as
+ * a flat 64 bit counter
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID too
+ * @param long_form     If true, also update and write time_t to buf
+ * @param prepend       If true, prepend to buffer, otherwise append.
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index e4c122c..5766c97 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -691,6 +691,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 33c8670..6579ff9 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);


 /**
@@ -1369,13 +1371,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1513,14 +1517,15 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
         size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1935,6 +1940,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index f085e0d..53bf763 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -311,7 +311,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -361,6 +360,8 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;

+    bool data_v3_features_supported; /**< dco supports new data channel features */
+
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;

@@ -490,8 +491,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 0ca6d42..0b4ad8a 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index 0ded052..94543d4 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -98,7 +98,7 @@
 {
     struct gc_arena gc = gc_new();

-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);

@@ -124,24 +124,32 @@
 }

 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;

         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
                co->key_ctx_bi.encrypt.implicit_iv, OPENVPN_MAX_IV_LENGTH);
         co->key_ctx_bi.decrypt.implicit_iv_len = impl_iv_len;
+
+        if (longiv)
+        {
+            co->flags |= CO_64_BIT_PKT_ID;
+        }
     }
 }

@@ -195,7 +203,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));

-    init_implicit_iv(co);
     update_time();

     /* Test encryption, decryption for all packet sizes */
@@ -235,24 +242,36 @@
     gc_free(&gc);
 }

-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};

-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));

-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};

     struct key_type kt = create_kt(cipher, auth, "ssl-test");

     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);

     return co;
 }
@@ -265,7 +284,6 @@

 }

-
 /* This adds a few more methods that strictly necessary but this allows
  * us to see which exact test was run from the backtrace of the test
  * when it fails */
@@ -273,8 +291,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -282,31 +319,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }

+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}

 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -336,8 +378,7 @@
         return;
     }

-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void
@@ -351,6 +392,154 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }

+static struct key2
+create_key()
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf) + 4, packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf) + 4, packetid1, 4);
+    }
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}

 int
 main(void)
@@ -367,6 +556,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };

 #if defined(ENABLE_CRYPTO_OPENSSL)

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 2
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset

[-- Attachment #2: Type: text/html, Size: 54350 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
  2024-01-25 12:38 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement support for larger packet counter sizes plaisthos (Code Review)
  2024-02-02 12:48 ` [Openvpn-devel] [L] " plaisthos (Code Review)
@ 2024-02-05 11:24 ` flichtenheld (Code Review)
  2024-02-05 12:24 ` flichtenheld (Code Review)
                   ` (27 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-02-05 11:24 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 3380 bytes --]

Attention is currently required from: plaisthos.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 2: Code-Review-1

(11 comments)

Commit Message:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/9474d66b_41a49dc1 :
PS2, Line 27: larger packet counters in any scenario since the other scenarios
Maybe nicer "in any other scenario since those are all legacy"


http://gerrit.openvpn.net/c/openvpn/+/507/comment/c459f6e9_1c6a2ee5 :
PS2, Line 38: 2^32 packet ids) forward. But this is an obscure edge that we can
remove second "forward"


http://gerrit.openvpn.net/c/openvpn/+/507/comment/e22305ad_aea02a52 :
PS2, Line 41: Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Missing sign-off


Patchset:

PS2:
Didn't get through the whole change, yet, but since I accumulated already a sizeable number of comments, here they are...


File src/openvpn/crypto.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/f5a0f5fa_c7d56d07 :
PS2, Line 287:     /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
This needs WAY more explanation. What is the difference between CO_PACKET_ID_LONG_FORM and this? CO_PACKET_ID_LONG_FORM is only for non-AEAD data packets and this is only for AEAD packets, right? I think this should be mentioned here.


http://gerrit.openvpn.net/c/openvpn/+/507/comment/44aff7d2_675e1cf8 :
PS2, Line 288:      * counter instead of the 32 bit that we normally use.
"normally use" -> "use by default". 32bit will remain the default, but hopefully not the norm.


File src/openvpn/packet_id.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/586075bf_d6c272af :
PS2, Line 250:  * Variant of packet_id_read that expect the timestamp first and packet
"expects"


http://gerrit.openvpn.net/c/openvpn/+/507/comment/01116f80_49cacdec :
PS2, Line 273:  * will always use a variant of the packet id that can just be seens as
"seen"


http://gerrit.openvpn.net/c/openvpn/+/507/comment/27267e93_2e924d4c :
PS2, Line 274:  * a flat 64 bit counter
add full stop at the end


http://gerrit.openvpn.net/c/openvpn/+/507/comment/a6ddc660_5b3f2b5e :
PS2, Line 277:  * @param buf           Buffer to write the packet ID too
"too" -> "to"


http://gerrit.openvpn.net/c/openvpn/+/507/comment/42ac15af_a6087522 :
PS2, Line 279:  * @param prepend       If true, prepend to buffer, otherwise append.
prepend parameter does not exist in this variant. Why is that? Is CO_AEAD_TAG_AT_THE_END now always true?



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 2
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Mon, 05 Feb 2024 11:24:24 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 6970 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (2 preceding siblings ...)
  2024-02-05 11:24 ` flichtenheld (Code Review)
@ 2024-02-05 12:24 ` flichtenheld (Code Review)
  2024-02-09 14:52 ` plaisthos (Code Review)
                   ` (26 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-02-05 12:24 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 3511 bytes --]

Attention is currently required from: plaisthos.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 2:

(8 comments)

Patchset:

PS2:
Some more comments now that I understand the code a bit better


File src/openvpn/crypto.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/373e1697_ed598058 :
PS2, Line 397:         const size_t packet_iv_len = iv_len - ctx->implicit_iv_len;
So implicit_iv_len also depends on the longiv flag, but that relationship is hidden in a completely separate part of the code? IMHO that is very confusing. Wouldn't it be easier if we just fill the implicit_iv fully always and check how much of it to use here? It think that would make the code simpler and easier to understand.


File src/openvpn/init.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/db73a5bd_f5a88cfb :
PS2, Line 3304:     /* Check if the DCO drivers support the new 64bit packet counter and
missing "TODO"? Currently we do not seem to check this but just assume that DCO doesn't support it?


File src/openvpn/packet_id.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/9644908d_23b0dda7 :
PS2, Line 279:  * @param prepend       If true, prepend to buffer, otherwise append.
> prepend parameter does not exist in this variant. […]
Sorry, confusing two different things here. It does not exist because it would be always false anyway in the places where this is used. Still needs to be removed from documentation, though.


File src/openvpn/ssl_common.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/612bae7e_d66dfc56 :
PS2, Line 363:     bool data_v3_features_supported; /**< dco supports new data channel features */
Why DCO? Doesn't the non-DCO code also support the features?


File src/openvpn/ssl_ncp.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/88a16718_ea9da9d3 :
PS2, Line 433:     if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
since this is based on both the TAG_AT_THE_END and 64_BIT_PKT_ID, maybe would be better to move this to its own patch instead of mixing it into the existing two?


File tests/unit_tests/openvpn/test_ssl.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/c0271a29_2d76ebf0 :
PS2, Line 151:             co->flags |= CO_64_BIT_PKT_ID;
why is this required? I don't see any indication that we overwrite co->flags?


http://gerrit.openvpn.net/c/openvpn/+/507/comment/581f83d7_d1986ad4 :
PS2, Line 494:     buf_advance(&buf, 4);
might be nicer to move the this block before the packetid test to avoid the "+ 4"?



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 2
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Mon, 05 Feb 2024 12:24:14 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: flichtenheld <frank@...2641...>
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 6726 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (3 preceding siblings ...)
  2024-02-05 12:24 ` flichtenheld (Code Review)
@ 2024-02-09 14:52 ` plaisthos (Code Review)
  2024-02-09 14:59 ` plaisthos (Code Review)
                   ` (25 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-02-09 14:52 UTC (permalink / raw)
  Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 5424 bytes --]

Attention is currently required from: flichtenheld.

plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 2:

(16 comments)

Commit Message:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/ad2cfd5e_b853527e :
PS2, Line 27: larger packet counters in any scenario since the other scenarios
> Maybe nicer "in any other scenario since those are all legacy"
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/d0565850_8921e027 :
PS2, Line 38: 2^32 packet ids) forward. But this is an obscure edge that we can
> remove second "forward"
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/f6f357ad_2afeb6e6 :
PS2, Line 41: Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
> Missing sign-off
Done


File src/openvpn/crypto.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/aee34092_a4a37691 :
PS2, Line 287:     /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
> This needs WAY more explanation. […]
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/7ba9075c_7f276f76 :
PS2, Line 288:      * counter instead of the 32 bit that we normally use.
> "normally use" -> "use by default". 32bit will remain the default, but hopefully not the norm.
Done


File src/openvpn/crypto.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/7b8ef00b_afc620f9 :
PS2, Line 397:         const size_t packet_iv_len = iv_len - ctx->implicit_iv_len;
> So implicit_iv_len also depends on the longiv flag, but that relationship is hidden in a completely  […]
Well we also only store the part of the IV that is needed for filling it to the required 12 bytes. So with short IV we store 8 bytes here and with long IV 4 bytes.

I can look into refactoring this or at least writing a better comment.


File src/openvpn/init.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/2a9600b7_98e09add :
PS2, Line 3304:     /* Check if the DCO drivers support the new 64bit packet counter and
> missing "TODO"? Currently we do not seem to check this but just assume that DCO doesn't support it?
Yes. There is currently no DCO implementation that has this feature as this PR just adds the feature. But I will will add a stub method that always returns false instead.


File src/openvpn/packet_id.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/fb5fb5ef_e3ecee08 :
PS2, Line 250:  * Variant of packet_id_read that expect the timestamp first and packet
> "expects"
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/b56f9064_b92507d0 :
PS2, Line 273:  * will always use a variant of the packet id that can just be seens as
> "seen"
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/6a732452_444cd0be :
PS2, Line 274:  * a flat 64 bit counter
> add full stop at the end
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/e88332e1_f6640f30 :
PS2, Line 277:  * @param buf           Buffer to write the packet ID too
> "too" -> "to"
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/70329f6b_bb92daa0 :
PS2, Line 279:  * @param prepend       If true, prepend to buffer, otherwise append.
> Sorry, confusing two different things here. […]
Done


File src/openvpn/ssl_common.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/87fa0aeb_65109819 :
PS2, Line 363:     bool data_v3_features_supported; /**< dco supports new data channel features */
> Why DCO? Doesn't the non-DCO code also support the features?
Clarified the doxygen comment


File src/openvpn/ssl_ncp.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/0b74fcf6_d3632ce2 :
PS2, Line 433:     if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
> since this is based on both the TAG_AT_THE_END and 64_BIT_PKT_ID, maybe would be better to move this […]
The thing here is that I didn't want to introduce two IV_PROTO flags and so the 2nd patch for the two introduces the IV_PROTO_DATA_V3 flag and all the logic for it.


File tests/unit_tests/openvpn/test_ssl.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/b11e4b53_74e1e29b :
PS2, Line 151:             co->flags |= CO_64_BIT_PKT_ID;
> why is this required? I don't see any indication that we overwrite co->flags?
yeah seems to be old leftover code. Thanks for pointing out.


http://gerrit.openvpn.net/c/openvpn/+/507/comment/82c35dbd_ccb22c51 :
PS2, Line 494:     buf_advance(&buf, 4);
> might be nicer to move the this block before the packetid test to avoid the "+ 4"?
Done



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 2
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Fri, 09 Feb 2024 14:52:54 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: flichtenheld <frank@...2641...>
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 12019 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (4 preceding siblings ...)
  2024-02-09 14:52 ` plaisthos (Code Review)
@ 2024-02-09 14:59 ` plaisthos (Code Review)
  2024-03-18 15:54 ` flichtenheld (Code Review)
                   ` (24 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-02-09 14:59 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 29015 bytes --]

Attention is currently required from: flichtenheld.

Hello flichtenheld, 

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to look at the new patch set (#3).

The following approvals got outdated and were removed:
Code-Review-1 by flichtenheld


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/dco.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/packet_id.c
M src/openvpn/packet_id.h
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl.h
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
14 files changed, 386 insertions(+), 42 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/3

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 9988ebe..7307a7f 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context,
+     * the length of the implicit length is initialised when the implicit is
+     * initialised in init_key_contexts when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 95a5b31..b1d3749 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */

 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,15 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big ending number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 96d95c2..d993559 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers();

+/**
+ * Return if the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */

 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }

+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index cd37b36..f8fc8cd 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2327,6 +2327,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -3297,6 +3301,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options) && dco_supports_data_v3(c))
+    {
+        to.data_v3_features_supported = false;
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 4344126..a80b9f4 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 39f00c0..3f8fccf 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8690,6 +8690,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index ef83248..f67f8ee 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }

+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }

 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index af62c89..262e65e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);

 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);

+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index e4c122c..5766c97 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -691,6 +691,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 33c8670..6579ff9 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);


 /**
@@ -1369,13 +1371,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1513,14 +1517,15 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
         size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1935,6 +1940,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 71b99db..044dc16 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)

+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /* Default field in X509 to be username */
 #define X509_USERNAME_FIELD_DEFAULT "CN"

diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index f085e0d..5541974 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -311,7 +311,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -361,6 +360,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;

+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;

@@ -490,8 +494,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 0ca6d42..0b4ad8a 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index 2ccfa45..b7e298f 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -98,7 +98,7 @@
 {
     struct gc_arena gc = gc_new();

-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);

@@ -124,19 +124,22 @@
 }

 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;

         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -195,7 +198,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));

-    init_implicit_iv(co);
     update_time();

     /* Test encryption, decryption for all packet sizes */
@@ -235,24 +237,36 @@
     gc_free(&gc);
 }

-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};

-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));

-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};

     struct key_type kt = create_kt(cipher, auth, "ssl-test");

     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);

     return co;
 }
@@ -271,8 +285,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -280,31 +313,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }

+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}

 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -334,8 +372,7 @@
         return;
     }

-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void
@@ -349,6 +386,154 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }

+static struct key2
+create_key()
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}

 int
 main(void)
@@ -365,6 +550,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };

 #if defined(ENABLE_CRYPTO_OPENSSL)

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 3
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset

[-- Attachment #2: Type: text/html, Size: 58392 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (5 preceding siblings ...)
  2024-02-09 14:59 ` plaisthos (Code Review)
@ 2024-03-18 15:54 ` flichtenheld (Code Review)
  2024-03-27 10:34 ` plaisthos (Code Review)
                   ` (23 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-03-18 15:54 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 2724 bytes --]

Attention is currently required from: plaisthos.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 3: Code-Review-2

(7 comments)

Patchset:

PS3:
DCO check is broken. Otherwise mostly documentation/comment nitpicks.


File src/openvpn/crypto.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/425de6c1_e1212b4d :
PS3, Line 292:      * big ending number in the wire format.
"endian"?


File src/openvpn/crypto.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/aaac8918_9f1ed366 :
PS3, Line 393:     /* Combine IV from explicit part from packet and implicit part from context,
End on ".", not ","


http://gerrit.openvpn.net/c/openvpn/+/507/comment/fa740d6c_61efdac1 :
PS3, Line 394:      * the length of the implicit length is initialised when the implicit is
That sentence is very unwieldy. Maybe: "implicit_iv and implicit_iv_len are initialised in init_key_contexts together with the keys".


File src/openvpn/dco.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/fe17f58d_4c954e37 :
PS3, Line 253:  * Return if the dco implementation supports the new protocol features of
"if" -> "whether"


File src/openvpn/init.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/60255894_aea373fc :
PS3, Line 3308:         to.data_v3_features_supported = false;
I think you mean "to.data_v3_features_supported = dco_supports_data_v3(c)" ?


File src/openvpn/ssl_ncp.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/0600a4d8_bb1a41ed :
PS2, Line 433:     if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
> The thing here is that I didn't want to introduce two IV_PROTO flags and so the 2nd patch for the tw […]
Acknowledged



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 3
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Mon, 18 Mar 2024 15:54:38 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Comment-In-Reply-To: plaisthos <arne-openvpn@...1227...>
Comment-In-Reply-To: flichtenheld <frank@...2641...>
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 5842 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (6 preceding siblings ...)
  2024-03-18 15:54 ` flichtenheld (Code Review)
@ 2024-03-27 10:34 ` plaisthos (Code Review)
  2024-03-27 10:40 ` plaisthos (Code Review)
                   ` (22 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-03-27 10:34 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 28889 bytes --]

Attention is currently required from: plaisthos.

Hello flichtenheld,

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to look at the new patch set (#4).


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/dco.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/packet_id.c
M src/openvpn/packet_id.h
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl.h
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
14 files changed, 386 insertions(+), 42 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/4

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 9758c8c..51c9eb9 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context.
+     * packet_iv_len and implicit_iv are initialised in init_key_contexts
+     * when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 61184bc..5188c9c 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */

 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,15 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big endian number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 50ebb35..6d886f4 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers();

+/**
+ * Return whether the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */

 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }

+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index c56ce48..9184094 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2316,6 +2316,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -3286,6 +3290,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = dco_supports_data_v3(c);
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 162b23e..4ac2c0d 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index cb8ff88..ac1c293 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8687,6 +8687,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index be28999..5224879 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }

+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }

 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index 558361a..d4b5f1e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);

 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);

+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 10806ac..2f0fc19 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -691,6 +691,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 7895a37..d0a5bab 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);
 

 /**
@@ -1369,13 +1371,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1513,14 +1517,15 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
         size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1935,6 +1940,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 98e59e8..1f770e0 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)

+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /* Default field in X509 to be username */
 #define X509_USERNAME_FIELD_DEFAULT "CN"

diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 5a7dd60..dc87c80 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -311,7 +311,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -361,6 +360,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;

+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;
 
@@ -490,8 +494,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 73ec9f5..bfb8658 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index 8d73858..dd3a108 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -98,7 +98,7 @@
 {
     struct gc_arena gc = gc_new();

-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);

@@ -124,19 +124,22 @@
 }

 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;

         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -195,7 +198,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));

-    init_implicit_iv(co);
     update_time();

     /* Test encryption, decryption for all packet sizes */
@@ -235,24 +237,36 @@
     gc_free(&gc);
 }

-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};

-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));

-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};

     struct key_type kt = create_kt(cipher, auth, "ssl-test");

     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);

     return co;
 }
@@ -271,8 +285,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -280,31 +313,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }

+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}

 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -334,8 +372,7 @@
         return;
     }
 
-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void
@@ -349,6 +386,154 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }

+static struct key2
+create_key()
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}

 int
 main(void)
@@ -365,6 +550,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };

 #if defined(ENABLE_CRYPTO_OPENSSL)

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 4
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-MessageType: newpatchset

[-- Attachment #2: Type: text/html, Size: 58256 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (7 preceding siblings ...)
  2024-03-27 10:34 ` plaisthos (Code Review)
@ 2024-03-27 10:40 ` plaisthos (Code Review)
  2024-03-27 11:33 ` flichtenheld (Code Review)
                   ` (21 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-03-27 10:40 UTC (permalink / raw)
  Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 2126 bytes --]

Attention is currently required from: flichtenheld.

plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 4:

(5 comments)

File src/openvpn/crypto.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/f254a2ce_f57578ee :
PS3, Line 292:      * big ending number in the wire format.
> "endian"?
Acknowledged


File src/openvpn/crypto.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/5002c984_e4c79e7d :
PS3, Line 393:     /* Combine IV from explicit part from packet and implicit part from context,
> End on ". […]
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/7c48e081_1bd56f08 :
PS3, Line 394:      * the length of the implicit length is initialised when the implicit is
> That sentence is very unwieldy. […]
Done


File src/openvpn/dco.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/2852f4e6_e5ca91be :
PS3, Line 253:  * Return if the dco implementation supports the new protocol features of
> "if" -> "whether"
Done


File src/openvpn/init.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/7d199642_142d7abf :
PS3, Line 3308:         to.data_v3_features_supported = false;
> I think you mean "to. […]
Acknowledged



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 4
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Comment-Date: Wed, 27 Mar 2024 10:40:22 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: flichtenheld <frank@...2641...>
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 4800 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (8 preceding siblings ...)
  2024-03-27 10:40 ` plaisthos (Code Review)
@ 2024-03-27 11:33 ` flichtenheld (Code Review)
  2024-04-29 15:50 ` plaisthos (Code Review)
                   ` (20 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-03-27 11:33 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 1134 bytes --]

Attention is currently required from: plaisthos.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 4: Code-Review+1

(1 comment)

Patchset:

PS4:
Looks good to me, but probably others should also review before we go ahead with this



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 4
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Wed, 27 Mar 2024 11:33:34 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2261 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (9 preceding siblings ...)
  2024-03-27 11:33 ` flichtenheld (Code Review)
@ 2024-04-29 15:50 ` plaisthos (Code Review)
  2024-04-30 11:34 ` flichtenheld (Code Review)
                   ` (19 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-04-29 15:50 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 30297 bytes --]

Attention is currently required from: flichtenheld, plaisthos.

Hello flichtenheld,

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to look at the new patch set (#5).

The following approvals got outdated and were removed:
Code-Review+1 by flichtenheld


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

While this implementation under hood allows one of the two
to be enabled individually we do not expose this functionality
but require the two protocol flags aead-tag-end and pkt-id-64-bit
to be always come together. This allows other data channel
implementations to only support a limited set of data channel
formats.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/dco.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/packet_id.c
M src/openvpn/packet_id.h
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl.h
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
14 files changed, 402 insertions(+), 42 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/5

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 9758c8c..51c9eb9 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context.
+     * packet_iv_len and implicit_iv are initialised in init_key_contexts
+     * when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 61184bc..ccaba7c 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */

 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,20 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big endian number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */
+
+    /* Note that even though this software implementation allows to define
+     * CO_AEAD_TAG_AT_THE_END and CO_64_BIT_PKT_ID independently, we only
+     * allow both to be used together to avoid having to implement
+     * the other variations in other data channel (DCO) implementations */

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 50ebb35..6d886f4 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers();

+/**
+ * Return whether the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */

 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }

+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index c56ce48..8103d17 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2316,6 +2316,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -2688,6 +2692,17 @@
                 "this server");
             return false;
         }
+
+        /* Ensure that for proto v3 is enabled fully or not at all */
+        bool aead_end = (c->options.imported_protocol_flags & CO_AEAD_TAG_AT_THE_END);
+        bool longpktiud = (c->options.imported_protocol_flags |= CO_64_BIT_PKT_ID);
+
+        if (aead_end != longpktiud)
+        {
+            msg(D_PUSH_ERRORS, "OPTIONS ERROR: Aead tag at the end and 64 bit"
+                "packet counter must be enabled together.");
+            return false;
+        }
     }

     if (found & OPT_P_PUSH_MTU)
@@ -3286,6 +3301,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = dco_supports_data_v3(c);
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 162b23e..4ac2c0d 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 26d2b35..9b98f70 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8687,6 +8687,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index be28999..5224879 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }

+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }

 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index 558361a..d4b5f1e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);

 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);

+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 10806ac..2f0fc19 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -691,6 +691,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 7c49451..1a321a1 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);


 /**
@@ -1369,13 +1371,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1513,14 +1517,15 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
         size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1935,6 +1940,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 98e59e8..1f770e0 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)

+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /* Default field in X509 to be username */
 #define X509_USERNAME_FIELD_DEFAULT "CN"

diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 5a7dd60..dc87c80 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -311,7 +311,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -361,6 +360,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;

+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;

@@ -490,8 +494,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 73ec9f5..bfb8658 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index 8d73858..dd3a108 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -98,7 +98,7 @@
 {
     struct gc_arena gc = gc_new();

-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);

@@ -124,19 +124,22 @@
 }

 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;

         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -195,7 +198,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));

-    init_implicit_iv(co);
     update_time();

     /* Test encryption, decryption for all packet sizes */
@@ -235,24 +237,36 @@
     gc_free(&gc);
 }

-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};

-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));

-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};

     struct key_type kt = create_kt(cipher, auth, "ssl-test");

     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);

     return co;
 }
@@ -271,8 +285,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -280,31 +313,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }

+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}

 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -334,8 +372,7 @@
         return;
     }

-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void
@@ -349,6 +386,154 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }

+static struct key2
+create_key()
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}

 int
 main(void)
@@ -365,6 +550,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };

 #if defined(ENABLE_CRYPTO_OPENSSL)

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 5
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-MessageType: newpatchset

[-- Attachment #2: Type: text/html, Size: 60695 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (10 preceding siblings ...)
  2024-04-29 15:50 ` plaisthos (Code Review)
@ 2024-04-30 11:34 ` flichtenheld (Code Review)
  2024-04-30 12:13 ` plaisthos (Code Review)
                   ` (18 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-04-30 11:34 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 1122 bytes --]

Attention is currently required from: plaisthos.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 5: Code-Review-2

(1 comment)

Patchset:

PS5:
All t_client tests fail with this change. Need to investigate that first.



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 5
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Tue, 30 Apr 2024 11:34:53 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2249 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (11 preceding siblings ...)
  2024-04-30 11:34 ` flichtenheld (Code Review)
@ 2024-04-30 12:13 ` plaisthos (Code Review)
  2024-07-12 12:15 ` flichtenheld (Code Review)
                   ` (17 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-04-30 12:13 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 30142 bytes --]

Attention is currently required from: plaisthos.

Hello flichtenheld,

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to look at the new patch set (#6).


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

While this implementation under hood allows one of the two
to be enabled individually we do not expose this functionality
but require the two protocol flags aead-tag-end and pkt-id-64-bit
to be always come together. This allows other data channel
implementations to only support a limited set of data channel
formats.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/dco.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/packet_id.c
M src/openvpn/packet_id.h
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl.h
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
14 files changed, 402 insertions(+), 42 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/6

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 9758c8c..51c9eb9 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context.
+     * packet_iv_len and implicit_iv are initialised in init_key_contexts
+     * when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 61184bc..ccaba7c 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */

 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,20 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big endian number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */
+
+    /* Note that even though this software implementation allows to define
+     * CO_AEAD_TAG_AT_THE_END and CO_64_BIT_PKT_ID independently, we only
+     * allow both to be used together to avoid having to implement
+     * the other variations in other data channel (DCO) implementations */

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 50ebb35..6d886f4 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers();

+/**
+ * Return whether the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */

 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }

+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index c56ce48..f0f923b 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2316,6 +2316,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -2688,6 +2692,17 @@
                 "this server");
             return false;
         }
+
+        /* Ensure that for proto v3 is enabled fully or not at all */
+        bool aead_end = (c->options.imported_protocol_flags & CO_AEAD_TAG_AT_THE_END);
+        bool longpktiud = (c->options.imported_protocol_flags & CO_64_BIT_PKT_ID);
+
+        if (aead_end != longpktiud)
+        {
+            msg(D_PUSH_ERRORS, "OPTIONS ERROR: Aead tag at the end and 64 bit"
+                "packet counter must be enabled together.");
+            return false;
+        }
     }

     if (found & OPT_P_PUSH_MTU)
@@ -3286,6 +3301,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = dco_supports_data_v3(c);
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 162b23e..4ac2c0d 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 26d2b35..9b98f70 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8687,6 +8687,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index be28999..5224879 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }

+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }

 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index 558361a..d4b5f1e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);

 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);

+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 10806ac..2f0fc19 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -691,6 +691,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 7c49451..1a321a1 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);


 /**
@@ -1369,13 +1371,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1513,14 +1517,15 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
         size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1935,6 +1940,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 98e59e8..1f770e0 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)

+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /* Default field in X509 to be username */
 #define X509_USERNAME_FIELD_DEFAULT "CN"

diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 5a7dd60..dc87c80 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -311,7 +311,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -361,6 +360,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;

+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;

@@ -490,8 +494,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 73ec9f5..bfb8658 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index 8d73858..dd3a108 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -98,7 +98,7 @@
 {
     struct gc_arena gc = gc_new();

-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);

@@ -124,19 +124,22 @@
 }

 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;

         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -195,7 +198,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));

-    init_implicit_iv(co);
     update_time();

     /* Test encryption, decryption for all packet sizes */
@@ -235,24 +237,36 @@
     gc_free(&gc);
 }

-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};

-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));

-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};

     struct key_type kt = create_kt(cipher, auth, "ssl-test");

     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);

     return co;
 }
@@ -271,8 +285,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -280,31 +313,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }

+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}

 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -334,8 +372,7 @@
         return;
     }

-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void
@@ -349,6 +386,154 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }

+static struct key2
+create_key()
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}

 int
 main(void)
@@ -365,6 +550,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };

 #if defined(ENABLE_CRYPTO_OPENSSL)

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-MessageType: newpatchset

[-- Attachment #2: Type: text/html, Size: 60496 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (12 preceding siblings ...)
  2024-04-30 12:13 ` plaisthos (Code Review)
@ 2024-07-12 12:15 ` flichtenheld (Code Review)
  2024-07-31 14:12 ` stipa (Code Review)
                   ` (16 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-07-12 12:15 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 1007 bytes --]

Attention is currently required from: plaisthos.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 6: -Code-Review


--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Fri, 12 Jul 2024 12:15:34 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 1822 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (13 preceding siblings ...)
  2024-07-12 12:15 ` flichtenheld (Code Review)
@ 2024-07-31 14:12 ` stipa (Code Review)
  2024-08-01 11:57 ` stipa (Code Review)
                   ` (15 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: stipa (Code Review) @ 2024-07-31 14:12 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 2312 bytes --]

Attention is currently required from: plaisthos.

stipa has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 6: Code-Review+1

(6 comments)

Patchset:

PS6:
A few minor comments, otherwise LGTM


File src/openvpn/crypto.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/057e647e_235cce26 :
PS6, Line 364: openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
Is it so that for DATA_V1:

  ad_start points to packet_id

and for DATA_V2:

  ad_start points to opcode/peer-id

?


File src/openvpn/init.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/68137a62_4480a3d5 :
PS6, Line 2696:         /* Ensure that for proto v3 is enabled fully or not at all */
Ensure that both aead_tag_end and long_pkt_id are enabled for DATA_V3 ?


File src/openvpn/ssl.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/758ca11c_70a5b728 :
PS6, Line 1528:         impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
why not move declaration of impl_iv_len here?


File src/openvpn/ssl_common.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/e1373829_32fe9ec0 :
PS6, Line 314:     bool disable_occ;
this looks like an unrelated change


http://gerrit.openvpn.net/c/openvpn/+/507/comment/b333e775_ad0af8c2 :
PS6, Line 493:     int limit_next;             /* used for traffic shaping on the control channel */
ditto



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Wed, 31 Jul 2024 14:12:10 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 5055 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (14 preceding siblings ...)
  2024-07-31 14:12 ` stipa (Code Review)
@ 2024-08-01 11:57 ` stipa (Code Review)
  2024-08-01 12:12 ` plaisthos (Code Review)
                   ` (14 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: stipa (Code Review) @ 2024-08-01 11:57 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 1274 bytes --]

Attention is currently required from: plaisthos.

stipa has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 6:

(1 comment)

File src/openvpn/init.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/0ded24fc_f7a6d46d :
PS6, Line 2702:             msg(D_PUSH_ERRORS, "OPTIONS ERROR: Aead tag at the end and 64 bit"
missing space after "bit"



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Thu, 01 Aug 2024 11:57:59 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2324 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (15 preceding siblings ...)
  2024-08-01 11:57 ` stipa (Code Review)
@ 2024-08-01 12:12 ` plaisthos (Code Review)
  2024-08-13 10:53 ` stipa (Code Review)
                   ` (13 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-08-01 12:12 UTC (permalink / raw)
  Cc: stipa <lstipakov@

[-- Attachment #1: Type: text/plain, Size: 1846 bytes --]

Attention is currently required from: stipa.

plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 6:

(3 comments)

File src/openvpn/crypto.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/339be4fb_e4a7dcd8 :
PS6, Line 364: openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
> Is it so that for DATA_V1: […]
yes. But that has been always the case.


File src/openvpn/ssl_common.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/ed094001_4358b379 :
PS6, Line 314:     bool disable_occ;
> this looks like an unrelated change
Yes. But I don't think it warranted an extra commit and I modify that part anyway.


http://gerrit.openvpn.net/c/openvpn/+/507/comment/a566bc93_6c0e149b :
PS6, Line 493:     int limit_next;             /* used for traffic shaping on the control channel */
> ditto
same, just removing an unused variable



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: stipa <lstipakov@...277...>
Gerrit-Comment-Date: Thu, 01 Aug 2024 12:12:34 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: stipa <lstipakov@...277...>
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 3719 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (16 preceding siblings ...)
  2024-08-01 12:12 ` plaisthos (Code Review)
@ 2024-08-13 10:53 ` stipa (Code Review)
  2024-08-13 10:53 ` stipa (Code Review)
                   ` (12 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: stipa (Code Review) @ 2024-08-13 10:53 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 1259 bytes --]

Attention is currently required from: plaisthos.

stipa has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 6: Code-Review-2

(1 comment)

Patchset:

PS6:
GHA failures, such as:

test_ssl.c:390:1: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
  390 | create_key()

also many from clang-asan.



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Tue, 13 Aug 2024 10:53:31 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2513 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (17 preceding siblings ...)
  2024-08-13 10:53 ` stipa (Code Review)
@ 2024-08-13 10:53 ` stipa (Code Review)
  2024-08-13 11:07 ` plaisthos (Code Review)
                   ` (11 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: stipa (Code Review) @ 2024-08-13 10:53 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 1175 bytes --]

Attention is currently required from: plaisthos.

stipa has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 6:

(1 comment)

Patchset:

PS6:
Link to GHA failures:

https://github.com/lstipakov/openvpn/actions/runs/10368312973/job/28701630904



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 6
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Tue, 13 Aug 2024 10:53:54 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2268 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (18 preceding siblings ...)
  2024-08-13 10:53 ` stipa (Code Review)
@ 2024-08-13 11:07 ` plaisthos (Code Review)
  2024-08-13 11:23 ` plaisthos (Code Review)
                   ` (10 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-08-13 11:07 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 30449 bytes --]

Attention is currently required from: plaisthos.

Hello flichtenheld, stipa,

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to look at the new patch set (#7).


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

While this implementation under hood allows one of the two
to be enabled individually we do not expose this functionality
but require the two protocol flags aead-tag-end and pkt-id-64-bit
to be always come together. This allows other data channel
implementations to only support a limited set of data channel
formats.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/dco.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/packet_id.c
M src/openvpn/packet_id.h
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl.h
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
14 files changed, 403 insertions(+), 43 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/7

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index c226727..6a639b7 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context.
+     * packet_iv_len and implicit_iv are initialised in init_key_contexts
+     * when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 61184bc..ccaba7c 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */

 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,20 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big endian number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */
+
+    /* Note that even though this software implementation allows to define
+     * CO_AEAD_TAG_AT_THE_END and CO_64_BIT_PKT_ID independently, we only
+     * allow both to be used together to avoid having to implement
+     * the other variations in other data channel (DCO) implementations */

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 035474f..3ce2c31 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers(void);

+/**
+ * Return whether the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */

 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }

+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 3100100..beca012 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2332,6 +2332,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -2704,6 +2708,17 @@
                 "this server");
             return false;
         }
+
+        /* Ensure that for proto v3 is enabled fully or not at all */
+        bool aead_end = (c->options.imported_protocol_flags & CO_AEAD_TAG_AT_THE_END);
+        bool longpktiud = (c->options.imported_protocol_flags & CO_64_BIT_PKT_ID);
+
+        if (aead_end != longpktiud)
+        {
+            msg(D_PUSH_ERRORS, "OPTIONS ERROR: Aead tag at the end and 64 bit"
+                "packet counter must be enabled together.");
+            return false;
+        }
     }

     if (found & OPT_P_PUSH_MTU)
@@ -3302,6 +3317,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = dco_supports_data_v3(c);
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 03177bb..f086736 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index d2ef895..bb7570a 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8696,6 +8696,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index be28999..5224879 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }

+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }

 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index 558361a..d4b5f1e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);

 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);

+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 6c06374..0397e0c 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -693,6 +693,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 14c38cf..4ef76d6 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);


 /**
@@ -1381,13 +1383,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1525,14 +1529,15 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
         size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1947,6 +1952,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 6c2bfc3..052cb76 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)

+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /** Supports the --dns option after all the incompatible changes */
 #define IV_PROTO_DNS_OPTION_V2   (1<<11)

diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 5bc2f2a..80bb502 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -314,7 +314,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -364,6 +363,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;

+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;
 
@@ -493,8 +497,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 968858e..a7a7c2f 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index a4b2101..d9948c6 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -98,7 +98,7 @@
 {
     struct gc_arena gc = gc_new();

-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);

@@ -124,19 +124,22 @@
 }

 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;

         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -195,7 +198,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));

-    init_implicit_iv(co);
     update_time();

     /* Test encryption, decryption for all packet sizes */
@@ -235,24 +237,36 @@
     gc_free(&gc);
 }

-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};

-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));

-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};

     struct key_type kt = create_kt(cipher, auth, "ssl-test");

     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);

     return co;
 }
@@ -262,7 +276,6 @@
 {
     packet_id_free(&co->packet_id);
     free_key_ctx_bi(&co->key_ctx_bi);
-
 }

 /* This adds a few more methods than strictly necessary but this allows
@@ -271,8 +284,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -280,31 +312,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }

+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}

 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -334,8 +371,7 @@
         return;
     }

-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void
@@ -349,6 +385,155 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }

+static struct key2
+create_key(void)
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    uninit_crypto_options(&co);
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}

 int
 main(void)
@@ -365,6 +550,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };

 #if defined(ENABLE_CRYPTO_OPENSSL)

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 7
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-MessageType: newpatchset

[-- Attachment #2: Type: text/html, Size: 61055 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (19 preceding siblings ...)
  2024-08-13 11:07 ` plaisthos (Code Review)
@ 2024-08-13 11:23 ` plaisthos (Code Review)
  2024-08-13 11:46 ` stipa (Code Review)
                   ` (9 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-08-13 11:23 UTC (permalink / raw)
  Cc: stipa <lstipakov@

[-- Attachment #1: Type: text/plain, Size: 1309 bytes --]

Attention is currently required from: stipa.

plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 7:

(1 comment)

Patchset:

PS6:
> GHA failures, such as: […]
the create_key is solved, however I cannot reproduce the clang asan errors in my GHA (https://github.com/schwabe/openvpn/actions/runs/10368459816/job/28702114044)



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 7
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: stipa <lstipakov@...277...>
Gerrit-Comment-Date: Tue, 13 Aug 2024 11:23:03 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: stipa <lstipakov@...277...>
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2478 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (20 preceding siblings ...)
  2024-08-13 11:23 ` plaisthos (Code Review)
@ 2024-08-13 11:46 ` stipa (Code Review)
  2024-08-14 13:16 ` flichtenheld (Code Review)
                   ` (8 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: stipa (Code Review) @ 2024-08-13 11:46 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 1150 bytes --]

Attention is currently required from: plaisthos.

stipa has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 7: Code-Review+1

(1 comment)

Patchset:

PS7:
Tested with dco-win data_v3 client implementation, looks good,



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 7
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Comment-Date: Tue, 13 Aug 2024 11:46:01 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2324 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (21 preceding siblings ...)
  2024-08-13 11:46 ` stipa (Code Review)
@ 2024-08-14 13:16 ` flichtenheld (Code Review)
  2024-09-10  9:24 ` plaisthos (Code Review)
                   ` (7 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-08-14 13:16 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 1697 bytes --]

Attention is currently required from: plaisthos, stipa.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 8: Code-Review+1

(2 comments)

File src/openvpn/ssl_common.h:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/68ee673d_068c1805 :
PS6, Line 314:     bool disable_occ;
> Yes. But I don't think it warranted an extra commit and I modify that part anyway.
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/0a1701ec_5a81108c :
PS6, Line 493:     int limit_next;             /* used for traffic shaping on the control channel */
> same, just removing an unused variable
Done



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 8
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: stipa <lstipakov@...277...>
Gerrit-Comment-Date: Wed, 14 Aug 2024 13:16:57 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Comment-In-Reply-To: plaisthos <arne-openvpn@...1227...>
Comment-In-Reply-To: stipa <lstipakov@...277...>
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 3395 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (22 preceding siblings ...)
  2024-08-14 13:16 ` flichtenheld (Code Review)
@ 2024-09-10  9:24 ` plaisthos (Code Review)
  2024-09-10 10:05 ` plaisthos (Code Review)
                   ` (6 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-09-10  9:24 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 30769 bytes --]

Attention is currently required from: flichtenheld, stipa.

Hello flichtenheld, stipa,

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to look at the new patch set (#10).

The following approvals got outdated and were removed:
Code-Review+1 by flichtenheld, Code-Review+1 by stipa


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

While this implementation under hood allows one of the two
to be enabled individually we do not expose this functionality
but require the two protocol flags aead-tag-end and pkt-id-64-bit
to be always come together. This allows other data channel
implementations to only support a limited set of data channel
formats.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/dco.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/packet_id.c
M src/openvpn/packet_id.h
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl.h
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
14 files changed, 405 insertions(+), 44 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/10

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index c226727..6a639b7 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context.
+     * packet_iv_len and implicit_iv are initialised in init_key_contexts
+     * when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 61184bc..ccaba7c 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */

 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,20 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big endian number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */
+
+    /* Note that even though this software implementation allows to define
+     * CO_AEAD_TAG_AT_THE_END and CO_64_BIT_PKT_ID independently, we only
+     * allow both to be used together to avoid having to implement
+     * the other variations in other data channel (DCO) implementations */

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 035474f..3ce2c31 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers(void);

+/**
+ * Return whether the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */

 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }

+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index dd56961..72d79c6 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2311,6 +2311,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -2679,6 +2683,19 @@
                 "this server");
             return false;
         }
+
+        /* Ensure that for proto v3 is fully enabled (both tag at end and
+         * 64 bit counter) or not at all to avoid having to test/implement
+         * 4 modes in data channels instead of just two */
+        bool aead_end = (c->options.imported_protocol_flags & CO_AEAD_TAG_AT_THE_END);
+        bool longpktiud = (c->options.imported_protocol_flags & CO_64_BIT_PKT_ID);
+
+        if (aead_end != longpktiud)
+        {
+            msg(D_PUSH_ERRORS, "OPTIONS ERROR: AEAD tag at the end and 64 bit "
+                "packet counter must be enabled together.");
+            return false;
+        }
     }

     if (found & OPT_P_PUSH_MTU)
@@ -3277,6 +3294,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = dco_supports_data_v3(c);
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 0509911..b7d30bf 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 649f48b..2fd2e81 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8723,6 +8723,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index be28999..5224879 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }

+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }

 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index 558361a..d4b5f1e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);

 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);

+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 6c06374..0397e0c 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -693,6 +693,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index e2be614..aafa9f2 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);


 /**
@@ -1388,13 +1390,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1532,14 +1536,14 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
-        size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        size_t impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1975,6 +1979,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index eea1323..e72b0e5 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)

+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /** Supports the --dns option after all the incompatible changes */
 #define IV_PROTO_DNS_OPTION_V2   (1<<11)

diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 5bc2f2a..80bb502 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -314,7 +314,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -364,6 +363,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;

+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;

@@ -493,8 +497,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 968858e..a7a7c2f 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index a1ca344..e3d03e6 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -192,7 +192,7 @@
 {
     struct gc_arena gc = gc_new();

-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);

@@ -278,19 +278,22 @@
 }

 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;

         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -349,7 +352,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));

-    init_implicit_iv(co);
     update_time();

     /* Test encryption, decryption for all packet sizes */
@@ -389,24 +391,36 @@
     gc_free(&gc);
 }

-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};

-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));

-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};

     struct key_type kt = create_kt(cipher, auth, "ssl-test");

     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);

     return co;
 }
@@ -416,7 +430,6 @@
 {
     packet_id_free(&co->packet_id);
     free_key_ctx_bi(&co->key_ctx_bi);
-
 }

 /* This adds a few more methods than strictly necessary but this allows
@@ -425,8 +438,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -434,31 +466,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }

+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}

 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -488,8 +525,7 @@
         return;
     }

-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void
@@ -503,6 +539,155 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }

+static struct key2
+create_key(void)
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    uninit_crypto_options(&co);
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}

 int
 main(void)
@@ -521,6 +706,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };

 #if defined(ENABLE_CRYPTO_OPENSSL)

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 10
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Attention: stipa <lstipakov@...277...>
Gerrit-MessageType: newpatchset

[-- Attachment #2: Type: text/html, Size: 61558 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (23 preceding siblings ...)
  2024-09-10  9:24 ` plaisthos (Code Review)
@ 2024-09-10 10:05 ` plaisthos (Code Review)
  2024-09-10 10:37 ` flichtenheld (Code Review)
                   ` (5 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-09-10 10:05 UTC (permalink / raw)
  Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 1891 bytes --]

Attention is currently required from: flichtenheld, stipa.

plaisthos has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 10:

(3 comments)

File src/openvpn/init.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/109f8bbf_34e87541 :
PS6, Line 2696:         /* Ensure that for proto v3 is enabled fully or not at all */
> Ensure that both aead_tag_end and long_pkt_id are enabled for DATA_V3 ?
Done


http://gerrit.openvpn.net/c/openvpn/+/507/comment/d9907f20_32a208a8 :
PS6, Line 2702:             msg(D_PUSH_ERRORS, "OPTIONS ERROR: Aead tag at the end and 64 bit"
> missing space after "bit"
Done


File src/openvpn/ssl.c:

http://gerrit.openvpn.net/c/openvpn/+/507/comment/8de88348_c0036eed :
PS6, Line 1528:         impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
> why not move declaration of impl_iv_len here?
Done



--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 10
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Attention: stipa <lstipakov@...277...>
Gerrit-Comment-Date: Tue, 10 Sep 2024 10:05:39 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: stipa <lstipakov@...277...>
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 3827 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (24 preceding siblings ...)
  2024-09-10 10:05 ` plaisthos (Code Review)
@ 2024-09-10 10:37 ` flichtenheld (Code Review)
  2024-09-10 10:46 ` [Openvpn-devel] [PATCH v10] " Frank Lichtenheld
                   ` (4 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-09-10 10:37 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: stipa <lstipakov@

[-- Attachment #1: Type: text/plain, Size: 1110 bytes --]

Attention is currently required from: plaisthos, stipa.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 10: Code-Review+2


--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 10
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: stipa <lstipakov@...277...>
Gerrit-Comment-Date: Tue, 10 Sep 2024 10:37:11 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2011 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [PATCH v10] Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (25 preceding siblings ...)
  2024-09-10 10:37 ` flichtenheld (Code Review)
@ 2024-09-10 10:46 ` Frank Lichtenheld
  2024-09-10 11:16 ` [Openvpn-devel] [L] Change in openvpn[master]: " plaisthos (Code Review)
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: Frank Lichtenheld @ 2024-09-10 10:46 UTC (permalink / raw)
  To: openvpn-devel; +Cc: Arne Schwabe <arne@

From: Arne Schwabe <arne@...1227...>

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

While this implementation under hood allows one of the two
to be enabled individually we do not expose this functionality
but require the two protocol flags aead-tag-end and pkt-id-64-bit
to be always come together. This allows other data channel
implementations to only support a limited set of data channel
formats.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
Acked-by: Frank Lichtenheld <frank@...2641...>
Acked-by: Lev Stipakov <lev@...515...>
---

This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.

Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/507
This mail reflects revision 10 of this Change.

Acked-by according to Gerrit (reflected above):
Frank Lichtenheld <frank@...2641...>

NOTE:
I've added an Acked-by from Lev manually. He did approve version 9
which only has a minimal code-change compared to 10, so I think
it better reflects the discussion.

        
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index c226727..6a639b7 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
 
     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);
 
         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));
 
-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context.
+     * packet_iv_len and implicit_iv are initialised in init_key_contexts
+     * when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }
 
     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 61184bc..ccaba7c 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */
 
 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,20 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big endian number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */
+
+    /* Note that even though this software implementation allows to define
+     * CO_AEAD_TAG_AT_THE_END and CO_64_BIT_PKT_ID independently, we only
+     * allow both to be used together to avoid having to implement
+     * the other variations in other data channel (DCO) implementations */
 
     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 035474f..3ce2c31 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers(void);
 
+/**
+ * Return whether the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */
 
 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }
 
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index dd56961..72d79c6 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2311,6 +2311,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }
 
     if (buf_len(&out) > strlen(header))
@@ -2679,6 +2683,19 @@
                 "this server");
             return false;
         }
+
+        /* Ensure that for proto v3 is fully enabled (both tag at end and
+         * 64 bit counter) or not at all to avoid having to test/implement
+         * 4 modes in data channels instead of just two */
+        bool aead_end = (c->options.imported_protocol_flags & CO_AEAD_TAG_AT_THE_END);
+        bool longpktiud = (c->options.imported_protocol_flags & CO_64_BIT_PKT_ID);
+
+        if (aead_end != longpktiud)
+        {
+            msg(D_PUSH_ERRORS, "OPTIONS ERROR: AEAD tag at the end and 64 bit "
+                "packet counter must be enabled together.");
+            return false;
+        }
     }
 
     if (found & OPT_P_PUSH_MTU)
@@ -3277,6 +3294,16 @@
         to.push_peer_info_detail = 1;
     }
 
+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = dco_supports_data_v3(c);
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }
 
     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 0509911..b7d30bf 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }
 
+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */
 
     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 649f48b..2fd2e81 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8723,6 +8723,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index be28999..5224879 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }
 
+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }
 
 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index 558361a..d4b5f1e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);
 
 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);
 
+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 6c06374..0397e0c 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -693,6 +693,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }
 
     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index e2be614..aafa9f2 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);
 
 
 /**
@@ -1388,13 +1390,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }
 
@@ -1532,14 +1536,14 @@
 }
 
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
-        size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        size_t impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1975,6 +1979,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif
 
+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);
 
         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index eea1323..e72b0e5 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)
 
+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /** Supports the --dns option after all the incompatible changes */
 #define IV_PROTO_DNS_OPTION_V2   (1<<11)
 
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 5bc2f2a..80bb502 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -314,7 +314,6 @@
 
     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -364,6 +363,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;
 
+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;
 
@@ -493,8 +497,6 @@
      */
     int key_id;
 
-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;
 
     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 968858e..a7a7c2f 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }
 
+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index a1ca344..e3d03e6 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -192,7 +192,7 @@
 {
     struct gc_arena gc = gc_new();
 
-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);
 
@@ -278,19 +278,22 @@
 }
 
 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;
 
+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
 
         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;
 
         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -349,7 +352,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));
 
-    init_implicit_iv(co);
     update_time();
 
     /* Test encryption, decryption for all packet sizes */
@@ -389,24 +391,36 @@
     gc_free(&gc);
 }
 
-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};
 
-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
 
-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};
 
     struct key_type kt = create_kt(cipher, auth, "ssl-test");
 
     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);
 
     return co;
 }
@@ -416,7 +430,6 @@
 {
     packet_id_free(&co->packet_id);
     free_key_ctx_bi(&co->key_ctx_bi);
-
 }
 
 /* This adds a few more methods than strictly necessary but this allows
@@ -425,8 +438,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -434,31 +466,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
 
+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}
 
 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }
 
 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }
 
 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }
 
 static void
@@ -488,8 +525,7 @@
         return;
     }
 
-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }
 
 static void
@@ -503,6 +539,155 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }
 
+static struct key2
+create_key(void)
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    uninit_crypto_options(&co);
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}
 
 int
 main(void)
@@ -521,6 +706,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };
 
 #if defined(ENABLE_CRYPTO_OPENSSL)


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (26 preceding siblings ...)
  2024-09-10 10:46 ` [Openvpn-devel] [PATCH v10] " Frank Lichtenheld
@ 2024-09-10 11:16 ` plaisthos (Code Review)
  2024-09-10 11:47 ` flichtenheld (Code Review)
                   ` (2 subsequent siblings)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-09-10 11:16 UTC (permalink / raw)
  To: flichtenheld <frank@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 30819 bytes --]

Attention is currently required from: flichtenheld, plaisthos, stipa.

Hello flichtenheld, stipa,

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/507?usp=email

to look at the new patch set (#11).

The following approvals got outdated and were removed:
Code-Review+2 by flichtenheld


Change subject: Implement support for larger packet counter sizes
......................................................................

Implement support for larger packet counter sizes

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

While this implementation under hood allows one of the two
to be enabled individually we do not expose this functionality
but require the two protocol flags aead-tag-end and pkt-id-64-bit
to be always come together. This allows other data channel
implementations to only support a limited set of data channel
formats.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
---
M src/openvpn/crypto.c
M src/openvpn/crypto.h
M src/openvpn/dco.h
M src/openvpn/init.c
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/packet_id.c
M src/openvpn/packet_id.h
M src/openvpn/push.c
M src/openvpn/ssl.c
M src/openvpn/ssl.h
M src/openvpn/ssl_common.h
M src/openvpn/ssl_ncp.c
M tests/unit_tests/openvpn/test_ssl.c
14 files changed, 405 insertions(+), 44 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/07/507/11

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index c226727..6a639b7 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;

     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);

         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));

-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context.
+     * packet_iv_len and implicit_iv are initialised in init_key_contexts
+     * when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }

     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 61184bc..ccaba7c 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */

 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,20 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big endian number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */
+
+    /* Note that even though this software implementation allows to define
+     * CO_AEAD_TAG_AT_THE_END and CO_64_BIT_PKT_ID independently, we only
+     * allow both to be used together to avoid having to implement
+     * the other variations in other data channel (DCO) implementations */

     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 035474f..3ce2c31 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers(void);

+/**
+ * Return whether the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */

 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }

+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index dd56961..ff0fb98 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2311,6 +2311,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }

     if (buf_len(&out) > strlen(header))
@@ -2679,6 +2683,19 @@
                 "this server");
             return false;
         }
+
+        /* Ensure that for proto v3 is fully enabled (both tag at end and
+         * 64 bit counter) or not at all to avoid having to test/implement
+         * 4 modes in data channels instead of just two */
+        bool aead_end = (c->options.imported_protocol_flags & CO_AEAD_TAG_AT_THE_END);
+        bool long_packet_id = (c->options.imported_protocol_flags & CO_64_BIT_PKT_ID);
+
+        if (aead_end != long_packet_id)
+        {
+            msg(D_PUSH_ERRORS, "OPTIONS ERROR: AEAD tag at the end and 64 bit "
+                "packet counter must be enabled together.");
+            return false;
+        }
     }

     if (found & OPT_P_PUSH_MTU)
@@ -3277,6 +3294,16 @@
         to.push_peer_info_detail = 1;
     }

+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = dco_supports_data_v3(c);
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }

     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 0509911..b7d30bf 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */

     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 649f48b..2fd2e81 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8723,6 +8723,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index be28999..5224879 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }

+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }

 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index 558361a..d4b5f1e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);

 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);

+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 6c06374..0397e0c 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -693,6 +693,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }

     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index e2be614..aafa9f2 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);


 /**
@@ -1388,13 +1390,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }

@@ -1532,14 +1536,14 @@
 }

 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
-        size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        size_t impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1975,6 +1979,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif

+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);

         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index eea1323..e72b0e5 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)

+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /** Supports the --dns option after all the incompatible changes */
 #define IV_PROTO_DNS_OPTION_V2   (1<<11)

diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 5bc2f2a..80bb502 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -314,7 +314,6 @@

     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -364,6 +363,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;

+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;

@@ -493,8 +497,6 @@
      */
     int key_id;

-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;

     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 968858e..a7a7c2f 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }

+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index a1ca344..e3d03e6 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -192,7 +192,7 @@
 {
     struct gc_arena gc = gc_new();

-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);

@@ -278,19 +278,22 @@
 }

 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;

+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);

         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;

         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -349,7 +352,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));

-    init_implicit_iv(co);
     update_time();

     /* Test encryption, decryption for all packet sizes */
@@ -389,24 +391,36 @@
     gc_free(&gc);
 }

-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};

-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));

-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};

     struct key_type kt = create_kt(cipher, auth, "ssl-test");

     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);

     return co;
 }
@@ -416,7 +430,6 @@
 {
     packet_id_free(&co->packet_id);
     free_key_ctx_bi(&co->key_ctx_bi);
-
 }

 /* This adds a few more methods than strictly necessary but this allows
@@ -425,8 +438,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -434,31 +466,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }

+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}

 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }

 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }

 static void
@@ -488,8 +525,7 @@
         return;
     }

-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }

 static void
@@ -503,6 +539,155 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }

+static struct key2
+create_key(void)
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    uninit_crypto_options(&co);
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}

 int
 main(void)
@@ -521,6 +706,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };

 #if defined(ENABLE_CRYPTO_OPENSSL)

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 11
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: flichtenheld <frank@...2641...>
Gerrit-Attention: stipa <lstipakov@...277...>
Gerrit-MessageType: newpatchset

[-- Attachment #2: Type: text/html, Size: 61652 bytes --]

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (27 preceding siblings ...)
  2024-09-10 11:16 ` [Openvpn-devel] [L] Change in openvpn[master]: " plaisthos (Code Review)
@ 2024-09-10 11:47 ` flichtenheld (Code Review)
  2024-09-10 16:34 ` [Openvpn-devel] [PATCH v11] " Gert Doering
  2024-09-30 12:54 ` [Openvpn-devel] [L] Change in openvpn[master]: " plaisthos (Code Review)
  30 siblings, 0 replies; 33+ messages in thread
From: flichtenheld (Code Review) @ 2024-09-10 11:47 UTC (permalink / raw)
  To: plaisthos <arne-openvpn@; +Cc: stipa <lstipakov@

[-- Attachment #1: Type: text/plain, Size: 1110 bytes --]

Attention is currently required from: plaisthos, stipa.

flichtenheld has posted comments on this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Patch Set 11: Code-Review+2


--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 11
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-Attention: plaisthos <arne-openvpn@...1227...>
Gerrit-Attention: stipa <lstipakov@...277...>
Gerrit-Comment-Date: Tue, 10 Sep 2024 11:47:22 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

[-- Attachment #2: Type: text/html, Size: 2011 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [PATCH v11] Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (28 preceding siblings ...)
  2024-09-10 11:47 ` flichtenheld (Code Review)
@ 2024-09-10 16:34 ` Gert Doering
  2024-09-10 19:51   ` Steffan Karger
  2024-09-30 12:54 ` [Openvpn-devel] [L] Change in openvpn[master]: " plaisthos (Code Review)
  30 siblings, 1 reply; 33+ messages in thread
From: Gert Doering @ 2024-09-10 16:34 UTC (permalink / raw)
  To: openvpn-devel

From: Arne Schwabe <arne@...1227...>

With DCO and possible future hardware assisted OpenVPN acceleration we
are approaching the point where 32 bit IVs are not cutting it any more.

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.

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.

This introduces the 64bit packet counters 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.

While we still keep the old --secret logic around we use the same
weird unix timestamp + packet counter format to avoid refactoring the
code now and again when we remove --secret code but DCO
implementations are free to use just a single 64 bit counter. One
other small downside of this approach is that when rollover happens
and we get reordering all the older packets are thrown away since
the distance between the packet before and after the rollover is
quite large as we probably jump forward more than 1s (or more than
2^32 packet ids). But this is an obscure edge that we can
(currently) live with.

While this implementation under hood allows one of the two
to be enabled individually we do not expose this functionality
but require the two protocol flags aead-tag-end and pkt-id-64-bit
to be always come together. This allows other data channel
implementations to only support a limited set of data channel
formats.

Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Signed-off-by: Arne Schwabe <arne@...1227...>
Acked-by: Frank Lichtenheld <frank@...2641...>
---

This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.

Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/507
This mail reflects revision 11 of this Change.

Acked-by according to Gerrit (reflected above):
Frank Lichtenheld <frank@...2641...>

        
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index c226727..6a639b7 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -68,6 +68,7 @@
     const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt;
     uint8_t *mac_out = NULL;
     const int mac_len = OPENVPN_AEAD_TAG_LENGTH;
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
 
     /* IV, packet-ID and implicit IV required for this mode. */
     ASSERT(ctx->cipher);
@@ -86,7 +87,7 @@
         buf_set_write(&iv_buffer, iv, iv_len);
 
         /* IV starts with packet id to make the IV unique for packet */
-        if (!packet_id_write(&opt->packet_id.send, &iv_buffer, false, false))
+        if (!packet_id_write_flat(&opt->packet_id.send, &iv_buffer, longiv))
         {
             msg(D_CRYPT_ERRORS, "ENCRYPT ERROR: packet ID roll over");
             goto err;
@@ -355,6 +356,9 @@
  * Set buf->len to 0 and return false on decrypt error.
  *
  * On success, buf is set to point to plaintext, true is returned.
+ *
+ * This method assumes that everything between ad_start and BPTR(buf) is
+ * authenticated data and therefore has no ad_len parameter
  */
 static bool
 openvpn_decrypt_aead(struct buffer *buf, struct buffer work,
@@ -384,7 +388,11 @@
     /* IV and Packet ID required for this mode */
     ASSERT(packet_id_initialized(&opt->packet_id));
 
-    /* Combine IV from explicit part from packet and implicit part from context */
+    bool longiv = opt->flags & CO_64_BIT_PKT_ID;
+
+    /* Combine IV from explicit part from packet and implicit part from context.
+     * packet_iv_len and implicit_iv are initialised in init_key_contexts
+     * when keys are initialised as well */
     {
         uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 };
         const int iv_len = cipher_ctx_iv_length(ctx->cipher);
@@ -409,7 +417,7 @@
     }
 
     /* Read packet ID from packet */
-    if (!packet_id_read(&pin, buf, false))
+    if (!packet_id_read_flat(&pin, buf, longiv))
     {
         CRYPT_ERROR("error reading packet-id");
     }
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 61184bc..ccaba7c 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -248,8 +248,10 @@
      *   OpenVPN process startups. */
 
 #define CO_PACKET_ID_LONG_FORM  (1<<0)
-    /**< Bit-flag indicating whether to use
-    *   OpenVPN's long packet ID format. */
+    /**< Bit-flag indicating whether to use OpenVPN's long packet ID format.
+     * This format puts [4 byte counter][4byte timestamp] on the wire in
+     * big endian/network endian format.
+     **/
 #define CO_IGNORE_PACKET_ID     (1<<1)
     /**< Bit-flag indicating whether to ignore
      *   the packet ID of a received packet.
@@ -283,6 +285,20 @@
     /**< Bit-flag indicating that the AEAD tag is at the end of the
      *   packet.
      */
+#define CO_64_BIT_PKT_ID  (1<<9)
+    /**< Bit-flag indicating that we should use a 64 bit (8 byte) packet
+     * counter instead of the 32 bit that we use by default. The difference to
+     * the normal CO_PACKET_ID_LONG_FORM packet ID is that this a real 64 bit
+     * big endian number in the wire format.
+     *
+     * This is only used for AEAD encryption. Other encryption (--static,
+     * --tls-crypt, --tls-auth,...) uses the old format for compatibility
+     */
+
+    /* Note that even though this software implementation allows to define
+     * CO_AEAD_TAG_AT_THE_END and CO_64_BIT_PKT_ID independently, we only
+     * allow both to be used together to avoid having to implement
+     * the other variations in other data channel (DCO) implementations */
 
     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 035474f..3ce2c31 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -249,6 +249,16 @@
  */
 const char *dco_get_supported_ciphers(void);
 
+/**
+ * Return whether the dco implementation supports the new protocol features of
+ * a 64 bit packet counter and AEAD tag at the end.
+ */
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
+
 #else /* if defined(ENABLE_DCO) */
 
 typedef void *dco_context_t;
@@ -380,5 +390,10 @@
     return "";
 }
 
+static inline bool
+dco_supports_data_v3(struct context *c)
+{
+    return false;
+}
 #endif /* defined(ENABLE_DCO) */
 #endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index dd56961..ff0fb98 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2311,6 +2311,10 @@
         {
             buf_printf(&out, " aead-tag-end");
         }
+        if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+        {
+            buf_printf(&out, " pkt-id-64-bit");
+        }
     }
 
     if (buf_len(&out) > strlen(header))
@@ -2679,6 +2683,19 @@
                 "this server");
             return false;
         }
+
+        /* Ensure that for proto v3 is fully enabled (both tag at end and
+         * 64 bit counter) or not at all to avoid having to test/implement
+         * 4 modes in data channels instead of just two */
+        bool aead_end = (c->options.imported_protocol_flags & CO_AEAD_TAG_AT_THE_END);
+        bool long_packet_id = (c->options.imported_protocol_flags & CO_64_BIT_PKT_ID);
+
+        if (aead_end != long_packet_id)
+        {
+            msg(D_PUSH_ERRORS, "OPTIONS ERROR: AEAD tag at the end and 64 bit "
+                "packet counter must be enabled together.");
+            return false;
+        }
     }
 
     if (found & OPT_P_PUSH_MTU)
@@ -3277,6 +3294,16 @@
         to.push_peer_info_detail = 1;
     }
 
+    /* Check if the DCO drivers support the new 64bit packet counter and
+     * AEAD tag at the end */
+    if (dco_enabled(options))
+    {
+        to.data_v3_features_supported = dco_supports_data_v3(c);
+    }
+    else
+    {
+        to.data_v3_features_supported = true;
+    }
 
     /* should we not xmit any packets until we get an initial
      * response from client? */
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 0509911..b7d30bf 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1851,6 +1851,13 @@
         o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
     }
 
+    if (tls_multi->session[TM_ACTIVE].opt->data_v3_features_supported
+        && (proto & IV_PROTO_DATA_V3))
+    {
+        o->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
+        o->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */
 
     /* if we have already created our key, we cannot *change* our own
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 649f48b..2fd2e81 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8723,6 +8723,10 @@
             {
                 options->imported_protocol_flags |= CO_AEAD_TAG_AT_THE_END;
             }
+            else if (streq(p[j], "pkt-id-64-bit"))
+            {
+                options->imported_protocol_flags |= CO_64_BIT_PKT_ID;
+            }
             else
             {
                 msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index be28999..5224879 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -320,6 +320,31 @@
     return true;
 }
 
+bool
+packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+    packet_id_type net_id;
+    net_time_t net_time;
+
+    pin->id = 0;
+    pin->time = 0;
+
+    if (long_form)
+    {
+        if (!buf_read(buf, &net_time, sizeof(net_time)))
+        {
+            return false;
+        }
+        pin->time = ntohtime(net_time);
+    }
+    if (!buf_read(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+    pin->id = ntohpid(net_id);
+    return true;
+}
+
 static bool
 packet_id_send_update(struct packet_id_send *p, bool long_form)
 {
@@ -344,6 +369,30 @@
 }
 
 bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf, bool long_form)
+{
+    if (!packet_id_send_update(p, long_form))
+    {
+        return false;
+    }
+
+    const packet_id_type net_id = htonpid(p->id);
+    const net_time_t net_time = htontime(p->time);
+
+    if (long_form && !buf_write(buf, &net_time, sizeof(net_time)))
+    {
+        return false;
+    }
+
+    if (!buf_write(buf, &net_id, sizeof(net_id)))
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool
 packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form,
                 bool prepend)
 {
diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h
index 558361a..d4b5f1e 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -244,10 +244,17 @@
  * Read/write a packet ID to/from the buffer.  Short form is sequence number
  * only.  Long form is sequence number and timestamp.
  */
-
 bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_form);
 
 /**
+ * Variant of packet_id_read that expects the timestamp first and packet
+ * counter after that to form a flat 64bit counter on the wire if we are
+ * using the long form.
+ */
+bool packet_id_read_flat(struct packet_id_net *pin, struct buffer *buf, bool long_form);
+
+
+/**
  * Write a packet ID to buf, and update the packet ID state.
  *
  * @param p             Packet ID state.
@@ -260,6 +267,22 @@
 bool packet_id_write(struct packet_id_send *p, struct buffer *buf,
                      bool long_form, bool prepend);
 
+
+/**
+ * Write a packet ID to buf, and update the packet ID state. This variant
+ * will always use a variant of the packet id that can just be seen as
+ * a flat 64 bit counter.
+ *
+ * @param p             Packet ID state.
+ * @param buf           Buffer to write the packet ID to
+ * @param long_form     If true, also update and write time_t to buf
+ *
+ * @return true if successful, false otherwise.
+ */
+bool
+packet_id_write_flat(struct packet_id_send *p, struct buffer *buf,
+                     bool long_form);
+
 /*
  * Inline functions.
  */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 6c06374..0397e0c 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -693,6 +693,10 @@
     {
         buf_printf(&proto_flags, " aead-tag-end");
     }
+    if (o->imported_protocol_flags & CO_64_BIT_PKT_ID)
+    {
+        buf_printf(&proto_flags, " pkt-id-64-bit");
+    }
 
     if (buf_len(&proto_flags) > 0)
     {
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index e2be614..aafa9f2 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -105,9 +105,11 @@
  * @param ctx                   Encrypt/decrypt key context
  * @param key                   HMAC key, used to calculate implicit IV
  * @param key_len               HMAC key length
+ * @param long_pkt_id           64-bit packet counters are used
  */
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len);
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len,
+                           bool long_pkt_id);
 
 
 /**
@@ -1388,13 +1390,15 @@
     }
     else
     {
+        bool longiv = ks->crypto_options.flags & CO_64_BIT_PKT_ID;
         init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
         /* Initialize implicit IVs */
-        key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+        key_ctx_update_implicit_iv(&key->encrypt,
+                                   key2->keys[(int)server].hmac,
+                                   MAX_HMAC_KEY_LENGTH, longiv);
         key_ctx_update_implicit_iv(&key->decrypt,
                                    key2->keys[1 - (int)server].hmac,
-                                   MAX_HMAC_KEY_LENGTH);
+                                   MAX_HMAC_KEY_LENGTH, longiv);
     }
 }
 
@@ -1532,14 +1536,14 @@
 }
 
 static void
-key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
+key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key,
+                           size_t key_len, bool longiv)
 {
     /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */
     if (cipher_ctx_mode_aead(ctx->cipher))
     {
-        size_t impl_iv_len = 0;
         ASSERT(cipher_ctx_iv_length(ctx->cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
-        impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - sizeof(packet_id_type);
+        size_t impl_iv_len = cipher_ctx_iv_length(ctx->cipher) - packet_id_size(longiv);
         ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(impl_iv_len <= key_len);
         memcpy(ctx->implicit_iv, key, impl_iv_len);
@@ -1975,6 +1979,12 @@
         iv_proto |= IV_PROTO_DYN_TLS_CRYPT;
 #endif
 
+        /* support for AEAD tag at the end and 8 byte IV */
+        if (session->opt->data_v3_features_supported)
+        {
+            iv_proto |= IV_PROTO_DATA_V3;
+        }
+
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);
 
         if (session->opt->push_peer_info_detail > 1)
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index eea1323..e72b0e5 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -107,6 +107,9 @@
 /** Support to dynamic tls-crypt (renegotiation with TLS-EKM derived tls-crypt key) */
 #define IV_PROTO_DYN_TLS_CRYPT   (1<<9)
 
+/** Support for the AEAD tag at the end and larger AEAD packet id */
+#define IV_PROTO_DATA_V3        (1<<10)
+
 /** Supports the --dns option after all the incompatible changes */
 #define IV_PROTO_DNS_OPTION_V2   (1<<11)
 
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 5bc2f2a..80bb502 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -314,7 +314,6 @@
 
     /* from command line */
     bool single_session;
-    bool disable_occ;
     int mode;
     bool pull;
     /**
@@ -364,6 +363,11 @@
     const char *config_ciphername;
     const char *config_ncp_ciphers;
 
+
+    /** whether our underlying data channel supports new data channel
+     * features. This is always true for the internal implementation but
+     * can be false for DCO implementations */
+    bool data_v3_features_supported;
     bool tls_crypt_v2;
     const char *tls_crypt_v2_verify_script;
 
@@ -493,8 +497,6 @@
      */
     int key_id;
 
-    int limit_next;             /* used for traffic shaping on the control channel */
-
     int verify_maxlevel;
 
     char *common_name;
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index 968858e..a7a7c2f 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -430,6 +430,12 @@
         session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
     }
 
+    if (session->opt->data_v3_features_supported && (iv_proto_peer & IV_PROTO_DATA_V3))
+    {
+        session->opt->crypto_flags |= CO_AEAD_TAG_AT_THE_END;
+        session->opt->crypto_flags |= CO_64_BIT_PKT_ID;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {
diff --git a/tests/unit_tests/openvpn/test_ssl.c b/tests/unit_tests/openvpn/test_ssl.c
index a1ca344..e3d03e6 100644
--- a/tests/unit_tests/openvpn/test_ssl.c
+++ b/tests/unit_tests/openvpn/test_ssl.c
@@ -192,7 +192,7 @@
 {
     struct gc_arena gc = gc_new();
 
-    struct tls_root_ctx ctx = { 0 };
+    struct tls_root_ctx ctx = {0};
     tls_ctx_client_new(&ctx);
     tls_ctx_load_cert_file(&ctx, unittest_cert, true);
 
@@ -278,19 +278,22 @@
 }
 
 static void
-init_implicit_iv(struct crypto_options *co)
+init_implicit_iv(struct crypto_options *co, struct key2 *key2)
 {
     cipher_ctx_t *cipher = co->key_ctx_bi.encrypt.cipher;
 
+
     if (cipher_ctx_mode_aead(cipher))
     {
-        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - sizeof(packet_id_type);
+        bool longiv = co->flags & CO_64_BIT_PKT_ID;
+
+        size_t impl_iv_len = cipher_ctx_iv_length(cipher) - packet_id_size(longiv);
         ASSERT(cipher_ctx_iv_length(cipher) <= OPENVPN_MAX_IV_LENGTH);
         ASSERT(cipher_ctx_iv_length(cipher) >= OPENVPN_AEAD_MIN_IV_LEN);
 
         /* Generate dummy implicit IV */
-        ASSERT(rand_bytes(co->key_ctx_bi.encrypt.implicit_iv,
-                          OPENVPN_MAX_IV_LENGTH));
+        ASSERT(memcpy(co->key_ctx_bi.encrypt.implicit_iv, key2->keys[0].hmac,
+                      OPENVPN_MAX_IV_LENGTH));
         co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len;
 
         memcpy(co->key_ctx_bi.decrypt.implicit_iv,
@@ -349,7 +352,6 @@
     /* init work */
     ASSERT(buf_init(&work, frame.buf.headroom));
 
-    init_implicit_iv(co);
     update_time();
 
     /* Test encryption, decryption for all packet sizes */
@@ -389,24 +391,36 @@
     gc_free(&gc);
 }
 
-
-
 struct crypto_options
-init_crypto_options(const char *cipher, const char *auth)
+init_crypto_options(const char *cipher, const char *auth, int flags,
+                    struct key2 *statickey)
 {
-    struct key2 key2 = { .n = 2};
+    struct key2 key2 = {.n = 2};
 
-    ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
-    ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
-    ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
-    ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
+    if (statickey)
+    {
+        /* Use chosen static key instead of random key when defined */
+        key2 = *statickey;
+    }
+    else
+    {
+        ASSERT(rand_bytes(key2.keys[0].cipher, sizeof(key2.keys[0].cipher)));
+        ASSERT(rand_bytes(key2.keys[0].hmac, sizeof(key2.keys[0].hmac)));
+        ASSERT(rand_bytes(key2.keys[1].cipher, sizeof(key2.keys[1].cipher)));
+        ASSERT(rand_bytes(key2.keys[1].hmac, sizeof(key2.keys)[1].hmac));
 
-    struct crypto_options co = { 0 };
+    }
+
+    struct crypto_options co = {0};
 
     struct key_type kt = create_kt(cipher, auth, "ssl-test");
 
     init_key_ctx_bi(&co.key_ctx_bi, &key2, 0, &kt, "unit-test-ssl");
-    packet_id_init(&co.packet_id,  5, 5, "UNITTEST", 0);
+    packet_id_init(&co.packet_id, 5, 5, "UNITTEST", 0);
+
+    co.flags |= flags;
+
+    init_implicit_iv(&co, &key2);
 
     return co;
 }
@@ -416,7 +430,6 @@
 {
     packet_id_free(&co->packet_id);
     free_key_ctx_bi(&co->key_ctx_bi);
-
 }
 
 /* This adds a few more methods than strictly necessary but this allows
@@ -425,8 +438,27 @@
 static void
 run_data_channel_with_cipher_end(const char *cipher)
 {
-    struct crypto_options co = init_crypto_options(cipher, "none");
-    co.flags |= CO_AEAD_TAG_AT_THE_END;
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END, NULL);
+
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_cipher_end_and_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_AEAD_TAG_AT_THE_END | CO_64_BIT_PKT_ID, NULL);
+    do_data_channel_round_trip(&co);
+    uninit_crypto_options(&co);
+}
+
+static void
+run_data_channel_with_long_pkt_counter(const char *cipher)
+{
+    struct crypto_options co = init_crypto_options(cipher, "none",
+                                                   CO_64_BIT_PKT_ID, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
@@ -434,31 +466,36 @@
 static void
 run_data_channel_with_cipher(const char *cipher, const char *auth)
 {
-    struct crypto_options co = init_crypto_options(cipher, auth);
+    struct crypto_options co = init_crypto_options(cipher, auth, 0, NULL);
     do_data_channel_round_trip(&co);
     uninit_crypto_options(&co);
 }
 
+static void
+run_aead_channel_tests(const char *cipher)
+{
+    run_data_channel_with_cipher_end(cipher);
+    run_data_channel_with_cipher(cipher, "none");
+    run_data_channel_with_cipher_end_and_long_pkt_counter(cipher);
+    run_data_channel_with_long_pkt_counter(cipher);
+}
 
 static void
 test_data_channel_roundtrip_aes_128_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-128-GCM");
-    run_data_channel_with_cipher("AES-128-GCM", "none");
+    run_aead_channel_tests("AES-128-GCM");
 }
 
 static void
 test_data_channel_roundtrip_aes_192_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-192-GCM");
-    run_data_channel_with_cipher("AES-192-GCM", "none");
+    run_aead_channel_tests("AES-192-GCM");
 }
 
 static void
 test_data_channel_roundtrip_aes_256_gcm(void **state)
 {
-    run_data_channel_with_cipher_end("AES-256-GCM");
-    run_data_channel_with_cipher("AES-256-GCM", "none");
+    run_aead_channel_tests("AES-256-GCM");
 }
 
 static void
@@ -488,8 +525,7 @@
         return;
     }
 
-    run_data_channel_with_cipher_end("ChaCha20-Poly1305");
-    run_data_channel_with_cipher("ChaCha20-Poly1305", "none");
+    run_aead_channel_tests("ChaCha20-Poly1305");
 }
 
 static void
@@ -503,6 +539,155 @@
     run_data_channel_with_cipher("BF-CBC", "SHA1");
 }
 
+static struct key2
+create_key(void)
+{
+    struct key2 key2 = {.n = 2};
+
+    const uint8_t key[] =
+    {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F',
+     'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
+
+    static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
+
+    /* copy the key a few times to ensure to have the size we need for
+     * Statickey but XOR it to not repeat it */
+    uint8_t keydata[sizeof(key2.keys)];
+
+    for (int i = 0; i < sizeof(key2.keys); i++)
+    {
+        keydata[i] = (uint8_t) (key[i % sizeof(key)] ^ i);
+    }
+
+
+    ASSERT(memcpy(key2.keys[0].cipher, keydata, sizeof(key2.keys[0].cipher)));
+    ASSERT(memcpy(key2.keys[0].hmac, keydata + 64, sizeof(key2.keys[0].hmac)));
+    ASSERT(memcpy(key2.keys[1].cipher, keydata + 128, sizeof(key2.keys[1].cipher)));
+    ASSERT(memcpy(key2.keys[1].hmac, keydata + 192, sizeof(key2.keys)[1].hmac));
+
+    return key2;
+}
+
+static void
+test_data_channel_known_vectors_run(bool longpktcounter)
+{
+    struct key2 key2 = create_key();
+
+    int flags = longpktcounter ? CO_64_BIT_PKT_ID : 0;
+    flags |= CO_AEAD_TAG_AT_THE_END;
+
+    struct crypto_options co = init_crypto_options("AES-256-GCM", "none", flags,
+                                                   &key2);
+
+    struct gc_arena gc = gc_new();
+
+    /* initialise frame for the test */
+    struct frame frame;
+    init_frame_parameters(&frame);
+
+    struct buffer src = alloc_buf_gc(frame.buf.payload_size, &gc);
+    struct buffer work = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&frame), &gc);
+    struct buffer buf = clear_buf();
+    void *buf_p;
+
+    /* init work */
+    ASSERT(buf_init(&work, frame.buf.headroom));
+
+    now = 0;
+
+    /* msg(M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); */
+
+    /*
+     * Load src with known data.
+     */
+    ASSERT(buf_init(&src, 0));
+    const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
+
+    ASSERT(buf_write(&src, plaintext, strlen(plaintext)));
+
+    /* copy source to input buf */
+    buf = work;
+    buf_p = buf_write_alloc(&buf, BLEN(&src));
+    ASSERT(buf_p);
+    memcpy(buf_p, BPTR(&src), BLEN(&src));
+
+    /* initialize work buffer with buf.headroom bytes of prepend capacity */
+    ASSERT(buf_init(&encrypt_workspace, frame.buf.headroom));
+
+    /* add packet opcode and peer id */
+    buf_write_u8(&encrypt_workspace, 7);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 0);
+    buf_write_u8(&encrypt_workspace, 23);
+
+    /* encrypt */
+    openvpn_encrypt(&buf, encrypt_workspace, &co);
+
+    /* separate buffer in authenticated data and encrypted data */
+    uint8_t *ad_start = BPTR(&buf);
+    buf_advance(&buf, 4);
+
+    if (longpktcounter)
+    {
+        uint8_t packetid1[8] = {0, 0, 0, 0, 0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 8);
+    }
+    else
+    {
+        uint8_t packetid1[4] = {0, 0, 0, 1};
+        assert_memory_equal(BPTR(&buf), packetid1, 4);
+    }
+
+    uint8_t *tag_location = BEND(&buf) - OPENVPN_AEAD_TAG_LENGTH;
+
+    if (longpktcounter)
+    {
+        const uint8_t exp_tag_long[16] =
+        {0x52, 0xee, 0xef, 0xdb, 0x34, 0xb7, 0xbd, 0x79, 0xfe, 0xbf, 0x69, 0xd0, 0x4e, 0x92, 0xfe, 0x4b};
+        assert_memory_equal(tag_location, exp_tag_long, OPENVPN_AEAD_TAG_LENGTH);
+    }
+    else
+    {
+        const uint8_t exp_tag_short[16] =
+        {0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
+        assert_memory_equal(tag_location, exp_tag_short, OPENVPN_AEAD_TAG_LENGTH);
+    }
+
+    if (longpktcounter)
+    {
+        const uint8_t bytesat14[6] = {0xc7, 0x40, 0x47, 0x81, 0xac, 0x8c};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+    else
+    {
+        const uint8_t bytesat14[6] = {0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
+        assert_memory_equal(BPTR(&buf) + 14, bytesat14, sizeof(bytesat14));
+    }
+
+    /* decrypt */
+    openvpn_decrypt(&buf, decrypt_workspace, &co, &frame, ad_start);
+
+    /* compare */
+    assert_int_equal(buf.len, strlen(plaintext));
+    assert_memory_equal(BPTR(&buf), plaintext, strlen(plaintext));
+
+    uninit_crypto_options(&co);
+    gc_free(&gc);
+}
+
+static void
+test_data_channel_known_vectors_longpktid(void **state)
+{
+    test_data_channel_known_vectors_run(true);
+}
+
+static void
+test_data_channel_known_vectors_shortpktid(void **state)
+{
+    test_data_channel_known_vectors_run(false);
+}
 
 int
 main(void)
@@ -521,6 +706,8 @@
         cmocka_unit_test(test_data_channel_roundtrip_aes_192_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_aes_256_cbc),
         cmocka_unit_test(test_data_channel_roundtrip_bf_cbc),
+        cmocka_unit_test(test_data_channel_known_vectors_longpktid),
+        cmocka_unit_test(test_data_channel_known_vectors_shortpktid)
     };
 
 #if defined(ENABLE_CRYPTO_OPENSSL)


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [Openvpn-devel] [PATCH v11] Implement support for larger packet counter sizes
  2024-09-10 16:34 ` [Openvpn-devel] [PATCH v11] " Gert Doering
@ 2024-09-10 19:51   ` Steffan Karger
  2024-09-11  5:58     ` Gert Doering
  0 siblings, 1 reply; 33+ messages in thread
From: Steffan Karger @ 2024-09-10 19:51 UTC (permalink / raw)
  To: openvpn-devel

Hi,

TL;DR: I don't think this should be merged yet. My primary concern is 
that we don't have any means to limit key usage to a safe value. I 
raised this concern back in December 2023:

https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg27791.html

If we allow for packet counter of 64 bits, we would allow users to 
exceed the safe limits determined for AES-GCM in the context of TLS. As 
far as I can tell, we don't have a reason to allow for looser limits. So 
before this patch goes in, I really think we should discuss what those 
limits should be for OpenVPN *and* enforce them.

This might have been overlooked, because attention was drawn to my 
proposal to make the upper bits "implicit" in the same mail.

On 10-09-2024 18:34, Gert Doering wrote:
> From: Arne Schwabe <arne@...1227...>
> 
> With DCO and possible future hardware assisted OpenVPN acceleration we
> are approaching the point where 32 bit IVs are not cutting it any more.

s/IVs/packet counters/. See my mail from Dec 2023.

> 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.
> 
> 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.
> 
> This introduces the 64bit packet counters 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.

Note that for AES-GCM, assuming limits similar to TLS, we likely won't 
be able to postpone key refresh for much longer than we currently do. 
For ChaCha-Poly we can, because of the larger auth tag.

So if we want to improve things for AES-GCM, we probably need other 
optimizations. I have some ideas, but was hoping to do some research and 
a write-up during the train ride to the hackathon, so we could discuss 
it further in Karlsruhe.

-Steffan


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [Openvpn-devel] [PATCH v11] Implement support for larger packet counter sizes
  2024-09-10 19:51   ` Steffan Karger
@ 2024-09-11  5:58     ` Gert Doering
  0 siblings, 0 replies; 33+ messages in thread
From: Gert Doering @ 2024-09-11  5:58 UTC (permalink / raw)
  To: Steffan Karger <steffan@; +Cc: openvpn-devel

[-- Attachment #1: Type: text/plain, Size: 1404 bytes --]

Hi,

On Tue, Sep 10, 2024 at 09:51:45PM +0200, Steffan Karger wrote:
> TL;DR: I don't think this should be merged yet.
> [..]
> So if we want to improve things for AES-GCM, we probably need other
> optimizations. I have some ideas, but was hoping to do some research and a
> write-up during the train ride to the hackathon, so we could discuss it
> further in Karlsruhe.

Thanks for having a look.  This ruins my planned work for today :-) - but
since we are not in a great hurry *and* Karlsruhe is just next week, it
will not harm to delay for a few weeks if the end result gets better.

Just for the record - I've done all the testing on v11 yesterday, and
it behaves very well interoping with "older peers" (no DATA_V3) and
also "falling back to DATA_V2 if DCO is active" (as there is no code
yet to do V3 with DCO).  master-to-master negotiates DATA_V3 and still
pings :-) - so, the code we have is good to be merged, if we decide to
keep it that way.  If not, I know what I need to test and how.

gert
-- 
"If was one thing all people took for granted, was conviction that if you 
 feed honest figures into a computer, honest figures come out. Never doubted 
 it myself till I met a computer with a sense of humor."
                             Robert A. Heinlein, The Moon is a Harsh Mistress

Gert Doering - Munich, Germany                             gert@...1296...

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [Openvpn-devel] [L] Change in openvpn[master]: Implement support for larger packet counter sizes
       [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
                   ` (29 preceding siblings ...)
  2024-09-10 16:34 ` [Openvpn-devel] [PATCH v11] " Gert Doering
@ 2024-09-30 12:54 ` plaisthos (Code Review)
  30 siblings, 0 replies; 33+ messages in thread
From: plaisthos (Code Review) @ 2024-09-30 12:54 UTC (permalink / raw)
  Cc: flichtenheld <frank@

[-- Attachment #1: Type: text/plain, Size: 836 bytes --]

plaisthos has abandoned this change. ( http://gerrit.openvpn.net/c/openvpn/+/507?usp=email )

Change subject: Implement support for larger packet counter sizes
......................................................................


Abandoned

New version coming.
--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/507?usp=email
To unsubscribe, or for help writing mail filters, visit http://gerrit.openvpn.net/settings

Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I01e258e97351b5aa4b9e561f5b35ddc2318569e2
Gerrit-Change-Number: 507
Gerrit-PatchSet: 11
Gerrit-Owner: plaisthos <arne-openvpn@...1227...>
Gerrit-Reviewer: flichtenheld <frank@...2641...>
Gerrit-Reviewer: stipa <lstipakov@...277...>
Gerrit-CC: openvpn-devel <openvpn-devel@lists.sourceforge.net>
Gerrit-MessageType: abandon

[-- Attachment #2: Type: text/html, Size: 1477 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2024-09-30 12:54 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <gerrit.1706186293000.I01e258e97351b5aa4b9e561f5b35ddc2318569e2@...2715...>
2024-01-25 12:38 ` [Openvpn-devel] [M] Change in openvpn[master]: Implement support for larger packet counter sizes plaisthos (Code Review)
2024-02-02 12:48 ` [Openvpn-devel] [L] " plaisthos (Code Review)
2024-02-05 11:24 ` flichtenheld (Code Review)
2024-02-05 12:24 ` flichtenheld (Code Review)
2024-02-09 14:52 ` plaisthos (Code Review)
2024-02-09 14:59 ` plaisthos (Code Review)
2024-03-18 15:54 ` flichtenheld (Code Review)
2024-03-27 10:34 ` plaisthos (Code Review)
2024-03-27 10:40 ` plaisthos (Code Review)
2024-03-27 11:33 ` flichtenheld (Code Review)
2024-04-29 15:50 ` plaisthos (Code Review)
2024-04-30 11:34 ` flichtenheld (Code Review)
2024-04-30 12:13 ` plaisthos (Code Review)
2024-07-12 12:15 ` flichtenheld (Code Review)
2024-07-31 14:12 ` stipa (Code Review)
2024-08-01 11:57 ` stipa (Code Review)
2024-08-01 12:12 ` plaisthos (Code Review)
2024-08-13 10:53 ` stipa (Code Review)
2024-08-13 10:53 ` stipa (Code Review)
2024-08-13 11:07 ` plaisthos (Code Review)
2024-08-13 11:23 ` plaisthos (Code Review)
2024-08-13 11:46 ` stipa (Code Review)
2024-08-14 13:16 ` flichtenheld (Code Review)
2024-09-10  9:24 ` plaisthos (Code Review)
2024-09-10 10:05 ` plaisthos (Code Review)
2024-09-10 10:37 ` flichtenheld (Code Review)
2024-09-10 10:46 ` [Openvpn-devel] [PATCH v10] " Frank Lichtenheld
2024-09-10 11:16 ` [Openvpn-devel] [L] Change in openvpn[master]: " plaisthos (Code Review)
2024-09-10 11:47 ` flichtenheld (Code Review)
2024-09-10 16:34 ` [Openvpn-devel] [PATCH v11] " Gert Doering
2024-09-10 19:51   ` Steffan Karger
2024-09-11  5:58     ` Gert Doering
2024-09-30 12:54 ` [Openvpn-devel] [L] Change in openvpn[master]: " plaisthos (Code Review)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.