]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
dco-win: support for epoch data channel
authorLev Stipakov <lev@openvpn.net>
Wed, 8 Oct 2025 12:37:51 +0000 (14:37 +0200)
committerGert Doering <gert@greenie.muc.de>
Wed, 8 Oct 2025 12:59:46 +0000 (14:59 +0200)
Starting from 2.8.0, dco-win driver supports epoch data channel.

This commit adds missing userspace part to query DCO drivers for epoch
data format support (always "false" for now for Linux and FreeBSD, true
if Win-DCO driver is 2.8 or later), and pass "CRYPTO_OPTIONS_EPOCH"
flag via a new OVPN_IOCTL_NEW_KEY_V2 ioctl() to windows driver to turn
it on, if negotiated.

Change-Id: Ib5ed5969dcd405a47e34ed8479b7ffaaa5c43080
Signed-off-by: Lev Stipakov <lev@openvpn.net>
Acked-by: Arne Schwabe <arne-openvpn@rfc2549.org>
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1219
Message-Id: <20251008123757.18670-1-gert@greenie.muc.de>
URL: https://sourceforge.net/p/openvpn/mailman/message/59243920/
Signed-off-by: Gert Doering <gert@greenie.muc.de>
src/openvpn/dco.c
src/openvpn/dco.h
src/openvpn/dco_freebsd.c
src/openvpn/dco_internal.h
src/openvpn/dco_linux.c
src/openvpn/dco_win.c
src/openvpn/ovpn_dco_win.h

index 6afc68086fbb2e04e340273c19c42223f7b5b84a..8fb46629c4e5e4a9d81599fb42d80c486beffbe9 100644 (file)
@@ -56,8 +56,9 @@ dco_install_key(struct tls_multi *multi, struct key_state *ks, const uint8_t *en
                 const char *ciphername)
 
 {
-    msg(D_DCO_DEBUG, "%s: peer_id=%d keyid=%d, currently %d keys installed", __func__,
-        multi->dco_peer_id, ks->key_id, multi->dco_keys_installed);
+    bool epoch = ks->crypto_options.flags & CO_EPOCH_DATA_KEY_FORMAT;
+    msg(D_DCO_DEBUG, "%s: peer_id=%d keyid=%d epoch=%d, currently %d keys installed", __func__,
+        multi->dco_peer_id, ks->key_id, multi->dco_keys_installed, epoch);
 
     /* Install a key in the PRIMARY slot only when no other key exist.
      * From that moment on, any new key will be installed in the SECONDARY
@@ -71,7 +72,7 @@ dco_install_key(struct tls_multi *multi, struct key_state *ks, const uint8_t *en
     }
 
     int ret = dco_new_key(multi->dco, multi->dco_peer_id, ks->key_id, slot, encrypt_key, encrypt_iv,
-                          decrypt_key, decrypt_iv, ciphername);
+                          decrypt_key, decrypt_iv, ciphername, epoch);
     if ((ret == 0) && (multi->dco_keys_installed < 2))
     {
         multi->dco_keys_installed++;
index a362977e1936a3a192167be0785ba449bf17913b..e5e87093c34283e5541b1dede62e435330228c78 100644 (file)
@@ -251,11 +251,8 @@ 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_epoch_data(struct context *c)
-{
-    return false;
-}
+bool
+dco_supports_epoch_data(struct context *c);
 #else  /* if defined(ENABLE_DCO) */
 
 typedef void *dco_context_t;
index b9f6bc7ccbe6fe9d6c036ba471ad14cfc343f4b1..947a769aba0b3bc9b3060e637833e27b6cd3cdc5 100644 (file)
@@ -487,14 +487,14 @@ start_tun(dco_context_t *dco)
 int
 dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot,
             const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key,
-            const uint8_t *decrypt_iv, const char *ciphername)
+            const uint8_t *decrypt_iv, const char *ciphername, bool epoch)
 {
     struct ifdrv drv;
     nvlist_t *nvl, *encrypt_nvl, *decrypt_nvl;
     int ret;
 
-    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s", __func__, slot, keyid, peerid,
-        ciphername);
+    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s, epoch %d", __func__, slot, keyid, peerid,
+        ciphername, epoch);
 
     nvl = nvlist_create(0);
 
@@ -876,4 +876,10 @@ dco_get_supported_ciphers(void)
     return "none:AES-256-GCM:AES-192-GCM:AES-128-GCM:CHACHA20-POLY1305";
 }
 
+bool
+dco_supports_epoch_data(struct context *c)
+{
+    return false;
+}
+
 #endif /* defined(ENABLE_DCO) && defined(TARGET_FREEBSD) */
index 86af0034bb5323116fcbe16048fcd6620a842266..97a7048cb4c42df69b42d96fabc3bd7c429e5a35 100644 (file)
@@ -66,7 +66,7 @@ int dco_del_peer(dco_context_t *dco, unsigned int peerid);
 
 int dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot,
                 const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key,
-                const uint8_t *decrypt_iv, const char *ciphername);
+                const uint8_t *decrypt_iv, const char *ciphername, bool epoch);
 
 int dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot);
 
index d46fa46a0ebf931fea3c02f1ac0134c3228a3534..0ae30b185ee7eac2706b722c93cc947744e664b0 100644 (file)
@@ -596,10 +596,10 @@ nla_put_failure:
 int
 dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot,
             const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key,
-            const uint8_t *decrypt_iv, const char *ciphername)
+            const uint8_t *decrypt_iv, const char *ciphername, bool epoch)
 {
-    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s", __func__, slot, keyid, peerid,
-        ciphername);
+    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s, epoch %d", __func__, slot, keyid, peerid,
+        ciphername, epoch);
 
     const int key_len = cipher_kt_key_size(ciphername);
     const int nonce_tail_len = 8;
@@ -1298,4 +1298,10 @@ dco_get_supported_ciphers(void)
     return "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305";
 }
 
+bool
+dco_supports_epoch_data(struct context *c)
+{
+    return false;
+}
+
 #endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */
index 30307dedeed7a1edb6df20a3e2f5ea825b5f6ff4..ca5eedfd61b8a953bf5ef20b6a6d9bddf45db2c7 100644 (file)
@@ -528,7 +528,7 @@ dco_set_peer(dco_context_t *dco, unsigned int peerid, int keepalive_interval, in
 int
 dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot,
             const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key,
-            const uint8_t *decrypt_iv, const char *ciphername)
+            const uint8_t *decrypt_iv, const char *ciphername, bool epoch)
 {
     msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s", __func__, slot, keyid, peerid,
         ciphername);
@@ -537,29 +537,42 @@ dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t s
     size_t key_len = cipher_kt_key_size(ciphername);
     ASSERT(key_len <= 32);
 
-    OVPN_CRYPTO_DATA crypto_data;
+    OVPN_CRYPTO_DATA_V2 crypto_data;
     ZeroMemory(&crypto_data, sizeof(crypto_data));
 
-    crypto_data.CipherAlg = dco_get_cipher(ciphername);
+    OVPN_CRYPTO_DATA *v1 = &crypto_data.V1;
+
+    v1->CipherAlg = dco_get_cipher(ciphername);
     ASSERT(keyid >= 0 && keyid <= UCHAR_MAX);
-    crypto_data.KeyId = (unsigned char)keyid;
-    crypto_data.PeerId = peerid;
-    crypto_data.KeySlot = slot;
+    v1->KeyId = (unsigned char)keyid;
+    v1->PeerId = peerid;
+    v1->KeySlot = slot;
 
-    CopyMemory(crypto_data.Encrypt.Key, encrypt_key, key_len);
-    crypto_data.Encrypt.KeyLen = (unsigned char)key_len;
-    CopyMemory(crypto_data.Encrypt.NonceTail, encrypt_iv, nonce_len);
+    /* for epoch we use key material as a seed, no as actual key */
+    CopyMemory(v1->Encrypt.Key, encrypt_key, epoch ? 32 : key_len);
+    v1->Encrypt.KeyLen = (unsigned char)key_len;
+    CopyMemory(v1->Encrypt.NonceTail, encrypt_iv, nonce_len);
 
-    CopyMemory(crypto_data.Decrypt.Key, decrypt_key, key_len);
-    crypto_data.Decrypt.KeyLen = (unsigned char)key_len;
-    CopyMemory(crypto_data.Decrypt.NonceTail, decrypt_iv, nonce_len);
+    CopyMemory(v1->Decrypt.Key, decrypt_key, epoch ? 32 : key_len);
+    v1->Decrypt.KeyLen = (unsigned char)key_len;
+    CopyMemory(v1->Decrypt.NonceTail, decrypt_iv, nonce_len);
 
-    ASSERT(crypto_data.CipherAlg > 0);
+    ASSERT(v1->CipherAlg > 0);
+
+    DWORD ioctl = OVPN_IOCTL_NEW_KEY;
+    VOID *buf = &crypto_data.V1;
+    DWORD bufSize = sizeof(crypto_data.V1);
+    if (epoch)
+    {
+        ioctl = OVPN_IOCTL_NEW_KEY_V2;
+        crypto_data.CryptoOptions |= CRYPTO_OPTIONS_EPOCH;
+        buf = &crypto_data;
+        bufSize = sizeof(crypto_data);
+    }
 
     DWORD bytes_returned = 0;
 
-    if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_NEW_KEY, &crypto_data, sizeof(crypto_data), NULL,
-                         0, &bytes_returned, NULL))
+    if (!DeviceIoControl(dco->tt->hand, ioctl, buf, bufSize, NULL, 0, &bytes_returned, NULL))
     {
         msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_KEY) failed");
         return -1;
@@ -1076,4 +1089,11 @@ dco_win_del_iroute_ipv6(dco_context_t *dco, struct in6_addr dst, unsigned int ne
     gc_free(&gc);
 }
 
+bool
+dco_supports_epoch_data(struct context *c)
+{
+    OVPN_VERSION ver = { 0 };
+    return dco_get_version(&ver) && ((ver.Major == 2 && ver.Minor >= 8) || (ver.Major > 2));
+}
+
 #endif /* defined(_WIN32) */
index 9e1378a81276d72b1dbc1145af214a682cf92670..e76770be88b6194ce94c398bd00243ac3ab40f75 100644 (file)
@@ -118,6 +118,13 @@ typedef struct _OVPN_CRYPTO_DATA {
        int PeerId;
 } OVPN_CRYPTO_DATA, * POVPN_CRYPTO_DATA;
 
+#define CRYPTO_OPTIONS_EPOCH (1<<1)
+
+typedef struct _OVPN_CRYPTO_DATA_V2 {
+    OVPN_CRYPTO_DATA V1;
+    UINT32 CryptoOptions;
+} OVPN_CRYPTO_DATA_V2, * POVPN_CRYPTO_DATA_V2;
+
 typedef struct _OVPN_MP_SET_PEER {
     int PeerId;
     LONG KeepaliveInterval;