Browse code

dco-win: support for epoch data channel

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>

Lev Stipakov authored on 2025/10/08 21:37:51
Showing 7 changed files
... ...
@@ -56,8 +56,9 @@ dco_install_key(struct tls_multi *multi, struct key_state *ks, const uint8_t *en
56 56
                 const char *ciphername)
57 57
 
58 58
 {
59
-    msg(D_DCO_DEBUG, "%s: peer_id=%d keyid=%d, currently %d keys installed", __func__,
60
-        multi->dco_peer_id, ks->key_id, multi->dco_keys_installed);
59
+    bool epoch = ks->crypto_options.flags & CO_EPOCH_DATA_KEY_FORMAT;
60
+    msg(D_DCO_DEBUG, "%s: peer_id=%d keyid=%d epoch=%d, currently %d keys installed", __func__,
61
+        multi->dco_peer_id, ks->key_id, multi->dco_keys_installed, epoch);
61 62
 
62 63
     /* Install a key in the PRIMARY slot only when no other key exist.
63 64
      * 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
71 71
     }
72 72
 
73 73
     int ret = dco_new_key(multi->dco, multi->dco_peer_id, ks->key_id, slot, encrypt_key, encrypt_iv,
74
-                          decrypt_key, decrypt_iv, ciphername);
74
+                          decrypt_key, decrypt_iv, ciphername, epoch);
75 75
     if ((ret == 0) && (multi->dco_keys_installed < 2))
76 76
     {
77 77
         multi->dco_keys_installed++;
... ...
@@ -251,11 +251,8 @@ const char *dco_get_supported_ciphers(void);
251 251
  * Return whether the dco implementation supports the new protocol features of
252 252
  * a 64 bit packet counter and AEAD tag at the end.
253 253
  */
254
-static inline bool
255
-dco_supports_epoch_data(struct context *c)
256
-{
257
-    return false;
258
-}
254
+bool
255
+dco_supports_epoch_data(struct context *c);
259 256
 #else  /* if defined(ENABLE_DCO) */
260 257
 
261 258
 typedef void *dco_context_t;
... ...
@@ -487,14 +487,14 @@ start_tun(dco_context_t *dco)
487 487
 int
488 488
 dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot,
489 489
             const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key,
490
-            const uint8_t *decrypt_iv, const char *ciphername)
490
+            const uint8_t *decrypt_iv, const char *ciphername, bool epoch)
491 491
 {
492 492
     struct ifdrv drv;
493 493
     nvlist_t *nvl, *encrypt_nvl, *decrypt_nvl;
494 494
     int ret;
495 495
 
496
-    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s", __func__, slot, keyid, peerid,
497
-        ciphername);
496
+    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s, epoch %d", __func__, slot, keyid, peerid,
497
+        ciphername, epoch);
498 498
 
499 499
     nvl = nvlist_create(0);
500 500
 
... ...
@@ -876,4 +876,10 @@ dco_get_supported_ciphers(void)
876 876
     return "none:AES-256-GCM:AES-192-GCM:AES-128-GCM:CHACHA20-POLY1305";
877 877
 }
878 878
 
879
+bool
880
+dco_supports_epoch_data(struct context *c)
881
+{
882
+    return false;
883
+}
884
+
879 885
 #endif /* defined(ENABLE_DCO) && defined(TARGET_FREEBSD) */
... ...
@@ -66,7 +66,7 @@ int dco_del_peer(dco_context_t *dco, unsigned int peerid);
66 66
 
67 67
 int dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot,
68 68
                 const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key,
69
-                const uint8_t *decrypt_iv, const char *ciphername);
69
+                const uint8_t *decrypt_iv, const char *ciphername, bool epoch);
70 70
 
71 71
 int dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot);
72 72
 
... ...
@@ -596,10 +596,10 @@ nla_put_failure:
596 596
 int
597 597
 dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot,
598 598
             const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key,
599
-            const uint8_t *decrypt_iv, const char *ciphername)
599
+            const uint8_t *decrypt_iv, const char *ciphername, bool epoch)
600 600
 {
601
-    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s", __func__, slot, keyid, peerid,
602
-        ciphername);
601
+    msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s, epoch %d", __func__, slot, keyid, peerid,
602
+        ciphername, epoch);
603 603
 
604 604
     const int key_len = cipher_kt_key_size(ciphername);
605 605
     const int nonce_tail_len = 8;
... ...
@@ -1298,4 +1298,10 @@ dco_get_supported_ciphers(void)
1298 1298
     return "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305";
1299 1299
 }
1300 1300
 
1301
+bool
1302
+dco_supports_epoch_data(struct context *c)
1303
+{
1304
+    return false;
1305
+}
1306
+
1301 1307
 #endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */
... ...
@@ -528,7 +528,7 @@ dco_set_peer(dco_context_t *dco, unsigned int peerid, int keepalive_interval, in
528 528
 int
529 529
 dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot,
530 530
             const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key,
531
-            const uint8_t *decrypt_iv, const char *ciphername)
531
+            const uint8_t *decrypt_iv, const char *ciphername, bool epoch)
532 532
 {
533 533
     msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s", __func__, slot, keyid, peerid,
534 534
         ciphername);
... ...
@@ -537,29 +537,42 @@ dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t s
537 537
     size_t key_len = cipher_kt_key_size(ciphername);
538 538
     ASSERT(key_len <= 32);
539 539
 
540
-    OVPN_CRYPTO_DATA crypto_data;
540
+    OVPN_CRYPTO_DATA_V2 crypto_data;
541 541
     ZeroMemory(&crypto_data, sizeof(crypto_data));
542 542
 
543
-    crypto_data.CipherAlg = dco_get_cipher(ciphername);
543
+    OVPN_CRYPTO_DATA *v1 = &crypto_data.V1;
544
+
545
+    v1->CipherAlg = dco_get_cipher(ciphername);
544 546
     ASSERT(keyid >= 0 && keyid <= UCHAR_MAX);
545
-    crypto_data.KeyId = (unsigned char)keyid;
546
-    crypto_data.PeerId = peerid;
547
-    crypto_data.KeySlot = slot;
547
+    v1->KeyId = (unsigned char)keyid;
548
+    v1->PeerId = peerid;
549
+    v1->KeySlot = slot;
548 550
 
549
-    CopyMemory(crypto_data.Encrypt.Key, encrypt_key, key_len);
550
-    crypto_data.Encrypt.KeyLen = (unsigned char)key_len;
551
-    CopyMemory(crypto_data.Encrypt.NonceTail, encrypt_iv, nonce_len);
551
+    /* for epoch we use key material as a seed, no as actual key */
552
+    CopyMemory(v1->Encrypt.Key, encrypt_key, epoch ? 32 : key_len);
553
+    v1->Encrypt.KeyLen = (unsigned char)key_len;
554
+    CopyMemory(v1->Encrypt.NonceTail, encrypt_iv, nonce_len);
552 555
 
553
-    CopyMemory(crypto_data.Decrypt.Key, decrypt_key, key_len);
554
-    crypto_data.Decrypt.KeyLen = (unsigned char)key_len;
555
-    CopyMemory(crypto_data.Decrypt.NonceTail, decrypt_iv, nonce_len);
556
+    CopyMemory(v1->Decrypt.Key, decrypt_key, epoch ? 32 : key_len);
557
+    v1->Decrypt.KeyLen = (unsigned char)key_len;
558
+    CopyMemory(v1->Decrypt.NonceTail, decrypt_iv, nonce_len);
556 559
 
557
-    ASSERT(crypto_data.CipherAlg > 0);
560
+    ASSERT(v1->CipherAlg > 0);
561
+
562
+    DWORD ioctl = OVPN_IOCTL_NEW_KEY;
563
+    VOID *buf = &crypto_data.V1;
564
+    DWORD bufSize = sizeof(crypto_data.V1);
565
+    if (epoch)
566
+    {
567
+        ioctl = OVPN_IOCTL_NEW_KEY_V2;
568
+        crypto_data.CryptoOptions |= CRYPTO_OPTIONS_EPOCH;
569
+        buf = &crypto_data;
570
+        bufSize = sizeof(crypto_data);
571
+    }
558 572
 
559 573
     DWORD bytes_returned = 0;
560 574
 
561
-    if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_NEW_KEY, &crypto_data, sizeof(crypto_data), NULL,
562
-                         0, &bytes_returned, NULL))
575
+    if (!DeviceIoControl(dco->tt->hand, ioctl, buf, bufSize, NULL, 0, &bytes_returned, NULL))
563 576
     {
564 577
         msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_KEY) failed");
565 578
         return -1;
... ...
@@ -1076,4 +1089,11 @@ dco_win_del_iroute_ipv6(dco_context_t *dco, struct in6_addr dst, unsigned int ne
1076 1076
     gc_free(&gc);
1077 1077
 }
1078 1078
 
1079
+bool
1080
+dco_supports_epoch_data(struct context *c)
1081
+{
1082
+    OVPN_VERSION ver = { 0 };
1083
+    return dco_get_version(&ver) && ((ver.Major == 2 && ver.Minor >= 8) || (ver.Major > 2));
1084
+}
1085
+
1079 1086
 #endif /* defined(_WIN32) */
... ...
@@ -118,6 +118,13 @@ typedef struct _OVPN_CRYPTO_DATA {
118 118
 	int PeerId;
119 119
 } OVPN_CRYPTO_DATA, * POVPN_CRYPTO_DATA;
120 120
 
121
+#define CRYPTO_OPTIONS_EPOCH (1<<1)
122
+
123
+typedef struct _OVPN_CRYPTO_DATA_V2 {
124
+    OVPN_CRYPTO_DATA V1;
125
+    UINT32 CryptoOptions;
126
+} OVPN_CRYPTO_DATA_V2, * POVPN_CRYPTO_DATA_V2;
127
+
121 128
 typedef struct _OVPN_MP_SET_PEER {
122 129
     int PeerId;
123 130
     LONG KeepaliveInterval;