diff --git a/Changes.rst b/Changes.rst
index fc5a1a853..5c3876512 100644
--- a/Changes.rst
+++ b/Changes.rst
@@ -99,6 +99,12 @@ Inline auth username and password
     missing OpenVPN will prompt for input via stdin. This applies to inline'd
     http-proxy-user-pass too.
 
+Secure renegotiation
+    When both peer are OpenVPN 2.6.0+, OpenVPN will use secure renegotiation
+    using a dynamically created tls-crypt key. This ensure that only the
+    previously authenticated peer can do trigger renegotiation and complete
+    renegotiations. This also closes CVE-2021-3568.
+
 
 Deprecated features
 -------------------
diff --git a/src/openvpn/auth_token.h b/src/openvpn/auth_token.h
index 5e23d8c44..0a847ce56 100644
--- a/src/openvpn/auth_token.h
+++ b/src/openvpn/auth_token.h
@@ -43,7 +43,7 @@
  *
  * The second timestamp is the time the token was renewed/regenerated and is used
  * to determine if this token has been renewed in the acceptable time range
- * (2 * renogiation timeout)
+ * (2 * renegoiation timeout)
  *
  * The session id is a random string of 12 byte (or 16 in base64) that is not
  * used by OpenVPN itself but kept intact so that external logging/management
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 9e10f64ee..4a8f514cd 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -1121,7 +1121,8 @@ void
 crypto_read_openvpn_key(const struct key_type *key_type,
                         struct key_ctx_bi *ctx, const char *key_file,
                         bool key_inline, const int key_direction,
-                        const char *key_name, const char *opt_name)
+                        const char *key_name, const char *opt_name,
+                        struct key2 *keydata)
 {
     struct key2 key2;
     struct key_direction_state kds;
@@ -1149,6 +1150,10 @@ crypto_read_openvpn_key(const struct key_type *key_type,
 
     /* initialize key in both directions */
     init_key_ctx_bi(ctx, &key2, key_direction, key_type, key_name);
+    if (keydata)
+    {
+        *keydata = key2;
+    }
     secure_memzero(&key2, sizeof(key2));
 }
 
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 5ea889081..e7177ea43 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -234,7 +234,14 @@ struct crypto_options
      *   both sending and receiving
      *   directions. */
     struct packet_id packet_id; /**< Current packet ID state for both
-                                 *   sending and receiving directions. */
+                                 *   sending and receiving directions.
+                                 *
+                                 *   This contains the packet id that is
+                                 *   used for replay protection.
+                                 *
+                                 *   The packet id also used as the IV
+                                 *   for AEAD/OFB/CFG ciphers.
+                                 *   */
     struct packet_id_persist *pid_persist;
     /**< Persistent packet ID state for
      *   keeping state between successive
@@ -268,6 +275,10 @@ struct crypto_options
     /**< Bit-flag indicating that explicit exit notifies should be
      * sent via the control channel instead of using an OCC message
      */
+#define CO_USE_SECURE_RENEGOTIATION   (1<<7)
+    /**< Bit-flag indicating that renegotiations are using tls-crypt
+     *   with a TLS-EKM derived key.
+     */
 
     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
@@ -530,7 +541,8 @@ void key2_print(const struct key2 *k,
 void crypto_read_openvpn_key(const struct key_type *key_type,
                              struct key_ctx_bi *ctx, const char *key_file,
                              bool key_inline, const int key_direction,
-                             const char *key_name, const char *opt_name);
+                             const char *key_name, const char *opt_name,
+                             struct key2 *keydata);
 
 /*
  * Inline functions
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index ed2ccb815..3e24396f5 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2783,7 +2783,7 @@ do_init_crypto_static(struct context *c, const unsigned int flags)
                                 options->shared_secret_file,
                                 options->shared_secret_file_inline,
                                 options->key_direction, "Static Key Encryption",
-                                "secret");
+                                "secret", NULL);
     }
     else
     {
@@ -2823,13 +2823,15 @@ do_init_tls_wrap_key(struct context *c)
                                 options->ce.tls_auth_file,
                                 options->ce.tls_auth_file_inline,
                                 options->ce.key_direction,
-                                "Control Channel Authentication", "tls-auth");
+                                "Control Channel Authentication", "tls-auth",
+                                NULL);
     }
 
     /* TLS handshake encryption+authentication (--tls-crypt) */
     if (options->ce.tls_crypt_file)
     {
         tls_crypt_init_key(&c->c1.ks.tls_wrap_key,
+                           &c->c1.ks.original_tlscrypt_keydata,
                            options->ce.tls_crypt_file,
                            options->ce.tls_crypt_file_inline,
                            options->tls_server);
@@ -2847,6 +2849,7 @@ do_init_tls_wrap_key(struct context *c)
         else
         {
             tls_crypt_v2_init_client_key(&c->c1.ks.tls_wrap_key,
+                                         &c->c1.ks.original_tlscrypt_keydata,
                                          &c->c1.ks.tls_crypt_v2_wkc,
                                          options->ce.tls_crypt_v2_file,
                                          options->ce.tls_crypt_v2_file_inline);
@@ -3149,6 +3152,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
         to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
         to.tls_wrap.opt.pid_persist = &c->c1.pid_persist;
         to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM;
+        to.tls_wrap.original_tlscrypt_keydata = c->c1.ks.original_tlscrypt_keydata;
 
         if (options->ce.tls_crypt_v2_file)
         {
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index b9b087e01..afd7ec3ee 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1803,6 +1803,10 @@ multi_client_set_protocol_options(struct context *c)
     {
         o->imported_protocol_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
     }
+    if (proto & IV_PROTO_SECURE_RENEG)
+    {
+        o->imported_protocol_flags |= CO_USE_SECURE_RENEGOTIATION;
+    }
 #endif
 
     if (proto & IV_PROTO_CC_EXIT_NOTIFY)
diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h
index c543cbf60..9df613179 100644
--- a/src/openvpn/openvpn.h
+++ b/src/openvpn/openvpn.h
@@ -65,6 +65,8 @@ struct key_schedule
     /* optional TLS control channel wrapping */
     struct key_type tls_auth_key_type;
     struct key_ctx_bi tls_wrap_key;
+    /** original tls-crypt preserved to xored into the tls_crypt renegotiation key */
+    struct key2 original_tlscrypt_keydata;
     struct key_ctx tls_crypt_v2_server_key;
     struct buffer tls_crypt_v2_wkc;             /**< Wrapped client key */
     struct key_ctx auth_token_key;
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 9f5e4b35d..f03b9131f 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -8554,6 +8554,10 @@ add_option(struct options *options,
             {
                 options->imported_protocol_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
             }
+            else if (streq(p[j], "secure-reneg"))
+            {
+                options->imported_protocol_flags |= CO_USE_SECURE_RENEGOTIATION;
+            }
 #endif
             else
             {
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 1bad00605..b2e46f1ca 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -668,6 +668,11 @@ prepare_push_reply(struct context *c, struct gc_arena *gc,
         push_option_fmt(gc, push_list, M_USAGE, "key-derivation tls-ekm");
     }
 
+    if (o->imported_protocol_flags & CO_USE_SECURE_RENEGOTIATION)
+    {
+        buf_printf(&proto_flags, " secure-reneg");
+    }
+
     if (buf_len(&proto_flags) > 0)
     {
         push_option_fmt(gc, push_list, M_USAGE, "protocol-flags%s", buf_str(&proto_flags));
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index d9b218191..a8d0bf9b0 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -1153,6 +1153,7 @@ static void
 tls_session_free(struct tls_session *session, bool clear)
 {
     tls_wrap_free(&session->tls_wrap);
+    tls_wrap_free(&session->tls_wrap_reneg);
 
     for (size_t i = 0; i < KS_SIZE; ++i)
     {
@@ -1717,6 +1718,17 @@ tls_session_update_crypto_params_do_work(struct tls_multi *multi,
         frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms");
     }
 
+    if (session->key[KS_PRIMARY].key_id == 0
+        && session->opt->crypto_flags & CO_USE_SECURE_RENEGOTIATION)
+    {
+        /* If the secure renegotiation has been negotiated, and we are on the
+         * first session (key_id = 0), generate a tls-crypt key for following
+         * renegotiations */
+        if (!tls_session_generate_secure_renegotiation_key(multi, session))
+        {
+            return false;
+        }
+    }
     return tls_session_generate_data_channel_keys(multi, session);
 }
 
@@ -2021,6 +2033,7 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
 
 #ifdef HAVE_EXPORT_KEYING_MATERIAL
         iv_proto |= IV_PROTO_TLS_KEY_EXPORT;
+        iv_proto |= IV_PROTO_SECURE_RENEG;
 #endif
 
         buf_printf(&out, "IV_PROTO=%d\n", iv_proto);
@@ -3532,7 +3545,7 @@ tls_pre_decrypt(struct tls_multi *multi,
 
         /*
          * If --single-session, don't allow any hard-reset connection request
-         * unless it the first packet of the session.
+         * unless it is the first packet of the session.
          */
         if (multi->opt.single_session)
         {
@@ -3542,7 +3555,7 @@ tls_pre_decrypt(struct tls_multi *multi,
             goto error;
         }
 
-        if (!read_control_auth(buf, &session->tls_wrap, from,
+        if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id), from,
                                session->opt))
         {
             goto error;
@@ -3597,8 +3610,8 @@ tls_pre_decrypt(struct tls_multi *multi,
          */
         if (op == P_CONTROL_SOFT_RESET_V1 && ks->state >= S_GENERATED_KEYS)
         {
-            if (!read_control_auth(buf, &session->tls_wrap, from,
-                                   session->opt))
+            if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id),
+                                   from, session->opt))
             {
                 goto error;
             }
@@ -3619,8 +3632,8 @@ tls_pre_decrypt(struct tls_multi *multi,
                 do_burst = true;
             }
 
-            if (!read_control_auth(buf, &session->tls_wrap, from,
-                                   session->opt))
+            if (!read_control_auth(buf, tls_session_get_tls_wrap(session, key_id),
+                                   from, session->opt))
             {
                 goto error;
             }
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 9ae6ae8fc..370467deb 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -43,6 +43,7 @@
 #include "ssl_common.h"
 #include "ssl_backend.h"
 #include "ssl_pkt.h"
+#include "tls_crypt.h"
 
 /* Used in the TLS PRF function */
 #define KEY_EXPANSION_ID "OpenVPN"
@@ -103,6 +104,9 @@
 /** Support for AUTH_FAIL,TEMP messages */
 #define IV_PROTO_AUTH_FAIL_TEMP  (1<<8)
 
+/** Support to secure renegoiations with TLS-EKM dervied tls-crypt key */
+#define IV_PROTO_SECURE_RENEG    (1<<9)
+
 /* Default field in X509 to be username */
 #define X509_USERNAME_FIELD_DEFAULT "CN"
 
@@ -476,6 +480,7 @@ tls_wrap_free(struct tls_wrap_ctx *tls_wrap)
 
     free_buf(&tls_wrap->tls_crypt_v2_metadata);
     free_buf(&tls_wrap->work);
+    secure_memzero(&tls_wrap->original_tlscrypt_keydata, sizeof(tls_wrap->original_tlscrypt_keydata));
 }
 
 static inline bool
diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h
index 1bd336999..82fb7f94b 100644
--- a/src/openvpn/ssl_backend.h
+++ b/src/openvpn/ssl_backend.h
@@ -391,6 +391,7 @@ void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx,
 
 #define EXPORT_KEY_DATA_LABEL       "EXPORTER-OpenVPN-datakeys"
 #define EXPORT_P2P_PEERID_LABEL     "EXPORTER-OpenVPN-p2p-peerid"
+#define EXPORT_SECURE_RENEG_LABEL   "EXPORTER-OpenVPN-secure-renegotiation"
 /**
  * Keying Material Exporters [RFC 5705] allows additional keying material to be
  * derived from existing TLS channel. This exported keying material can then be
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 6135556a9..fdc9f0626 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -274,6 +274,15 @@ struct tls_wrap_ctx
     struct buffer tls_crypt_v2_metadata;     /**< Received from client */
     bool cleanup_key_ctx;                    /**< opt.key_ctx_bi is owned by
                                               *   this context */
+    /** original key data to be xored in to the key for secure renegotiation.
+     *
+     * We keep the original key data to ensure that the newly generated key
+     * for the secure renegotiation has the same level of quality by using
+     * xor with the original key. This gives us the same same entropy/randomness
+     * as the original tls-crypt key to ensure the post-quantum use case of
+     * tls-crypt still holds true
+     * */
+    struct key2 original_tlscrypt_keydata;
 };
 
 /*
@@ -467,6 +476,10 @@ struct tls_session
     /* authenticate control packets */
     struct tls_wrap_ctx tls_wrap;
 
+    /* Specific tls-crypt for renegotiations, if this is valid,
+     * tls_wrap_reneg.mode is TLS_WRAP_CRYPT, otherwise ignore it */
+    struct tls_wrap_ctx tls_wrap_reneg;
+
     int initial_opcode;         /* our initial P_ opcode */
     struct session_id session_id; /* our random session ID */
 
diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c
index b6884af96..8de35323c 100644
--- a/src/openvpn/ssl_ncp.c
+++ b/src/openvpn/ssl_ncp.c
@@ -461,6 +461,10 @@ p2p_ncp_set_options(struct tls_multi *multi, struct tls_session *session)
 
         }
     }
+    if (iv_proto_peer & IV_PROTO_SECURE_RENEG)
+    {
+        session->opt->crypto_flags |= CO_USE_SECURE_RENEGOTIATION;
+    }
 #endif /* if defined(HAVE_EXPORT_KEYING_MATERIAL) */
 }
 
diff --git a/src/openvpn/ssl_pkt.c b/src/openvpn/ssl_pkt.c
index 0083fc470..b58f30d07 100644
--- a/src/openvpn/ssl_pkt.c
+++ b/src/openvpn/ssl_pkt.c
@@ -183,7 +183,7 @@ write_control_auth(struct tls_session *session,
 
     msg(D_TLS_DEBUG, "%s(): %s", __func__, packet_opcode_name(opcode));
 
-    tls_wrap_control(&session->tls_wrap, header, buf, &session->session_id);
+    tls_wrap_control(tls_session_get_tls_wrap(session, ks->key_id), header, buf, &session->session_id);
 
     *to_link_addr = &ks->remote_addr;
 }
diff --git a/src/openvpn/ssl_pkt.h b/src/openvpn/ssl_pkt.h
index 45e0a81f5..7d13690dd 100644
--- a/src/openvpn/ssl_pkt.h
+++ b/src/openvpn/ssl_pkt.h
@@ -273,6 +273,27 @@ packet_opcode_name(int op)
     }
 }
 
+/**
+ * Determines if the current session should use the renegotiation tls wrap
+ * struct instead the normal one and returns it
+ *
+ * @param session
+ * @param key_id    key_id of the received/or to be send packet
+ * @return
+ */
+static inline struct tls_wrap_ctx *
+tls_session_get_tls_wrap(struct tls_session *session, int key_id)
+{
+    if (key_id > 0 && session->tls_wrap_reneg.mode == TLS_WRAP_CRYPT)
+    {
+        return &session->tls_wrap_reneg;
+    }
+    else
+    {
+        return &session->tls_wrap;
+    }
+}
+
 /* initial packet id (instead of 0) that indicates that the peer supports
  * early protocol negotiation. This will make the packet id turn a bit faster
  * but the network time part of the packet id takes care of that. And
diff --git a/src/openvpn/tls_crypt.c b/src/openvpn/tls_crypt.c
index 2fc791119..e95de63b0 100644
--- a/src/openvpn/tls_crypt.c
+++ b/src/openvpn/tls_crypt.c
@@ -60,8 +60,8 @@ tls_crypt_buf_overhead(void)
 }
 
 void
-tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file,
-                   bool key_inline, bool tls_server)
+tls_crypt_init_key(struct key_ctx_bi *key, struct key2 *keydata,
+                   const char *key_file, bool key_inline, bool tls_server)
 {
     const int key_direction = tls_server ?
                               KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE;
@@ -71,9 +71,77 @@ tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file,
         msg(M_FATAL, "ERROR: --tls-crypt not supported");
     }
     crypto_read_openvpn_key(&kt, key, key_file, key_inline, key_direction,
-                            "Control Channel Encryption", "tls-crypt");
+                            "Control Channel Encryption", "tls-crypt", keydata);
 }
 
+/**
+ * Will produce key = key XOR other
+ */
+static void
+xor_key2(struct key2 *key, const struct key2 *other)
+{
+    ASSERT(key->n == 2 && other->n == 2);
+    for (int k = 0; k < 2; k++)
+    {
+        for (int j = 0; j < MAX_CIPHER_KEY_LENGTH; j++)
+        {
+            key->keys[k].cipher[j] = key->keys[k].cipher[j] ^ other->keys[k].cipher[j];
+        }
+
+        for (int j = 0; j < MAX_HMAC_KEY_LENGTH; j++)
+        {
+            key->keys[k].hmac[j] = key->keys[k].hmac[j] ^ other->keys[k].hmac[j];
+        }
+
+    }
+}
+
+bool
+tls_session_generate_secure_renegotiation_key(struct tls_multi *multi,
+                                              struct tls_session *session)
+{
+    session->tls_wrap_reneg.opt = session->tls_wrap.opt;
+    session->tls_wrap_reneg.mode = TLS_WRAP_CRYPT;
+    session->tls_wrap_reneg.cleanup_key_ctx = true;
+    session->tls_wrap_reneg.work = alloc_buf(BUF_SIZE(&session->opt->frame));
+    session->tls_wrap_reneg.opt.pid_persist = NULL;
+
+    packet_id_init(&session->tls_wrap_reneg.opt.packet_id,
+                   session->opt->replay_window,
+                   session->opt->replay_time,
+                   "TLS_WRAP_RENEG", session->key_id);
+
+
+    struct key2 rengokeys;
+    if (!key_state_export_keying_material(session, EXPORT_SECURE_RENEG_LABEL,
+                                          strlen(EXPORT_SECURE_RENEG_LABEL),
+                                          rengokeys.keys, sizeof(rengokeys.keys)))
+    {
+        return false;
+    }
+    rengokeys.n = 2;
+
+    if (session->tls_wrap.mode == TLS_WRAP_CRYPT)
+    {
+        xor_key2(&rengokeys, &session->tls_wrap.original_tlscrypt_keydata);
+    }
+
+    const int key_direction = session->opt->server ?
+                              KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE;
+
+    struct key_direction_state kds;
+    key_direction_state_init(&kds, key_direction);
+
+    struct key_type kt = tls_crypt_kt();
+
+    init_key_ctx_bi(&session->tls_wrap_reneg.opt.key_ctx_bi, &rengokeys, key_direction,
+                    &kt, "secure renegotiation");
+    secure_memzero(&rengokeys, sizeof(rengokeys));
+
+    return true;
+}
+
+
 bool
 tls_crypt_wrap(const struct buffer *src, struct buffer *dst,
                struct crypto_options *opt)
@@ -266,8 +334,9 @@ tls_crypt_v2_load_client_key(struct key_ctx_bi *key, const struct key2 *key2,
 }
 
 void
-tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct buffer *wkc_buf,
-                             const char *key_file, bool key_inline)
+tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct key2 *original_key,
+                             struct buffer *wkc_buf, const char *key_file,
+                             bool key_inline)
 {
     struct buffer client_key = alloc_buf(TLS_CRYPT_V2_CLIENT_KEY_LEN
                                          + TLS_CRYPT_V2_MAX_WKC_LEN);
@@ -285,7 +354,7 @@ tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct buffer *wkc_buf,
     }
 
     tls_crypt_v2_load_client_key(key, &key2, false);
-    secure_memzero(&key2, sizeof(key2));
+    *original_key = key2;
 
     *wkc_buf = client_key;
 }
@@ -570,15 +639,14 @@ tls_crypt_v2_extract_client_key(struct buffer *buf,
         return false;
     }
 
-    struct key2 client_key = { 0 };
     ctx->tls_crypt_v2_metadata = alloc_buf(TLS_CRYPT_V2_MAX_METADATA_LEN);
-    if (!tls_crypt_v2_unwrap_client_key(&client_key,
+    if (!tls_crypt_v2_unwrap_client_key(&ctx->original_tlscrypt_keydata,
                                         &ctx->tls_crypt_v2_metadata,
                                         wrapped_client_key,
                                         &ctx->tls_crypt_v2_server_key))
     {
         msg(D_TLS_ERRORS, "Can not unwrap tls-crypt-v2 client key");
-        secure_memzero(&client_key, sizeof(client_key));
+        secure_memzero(&ctx->original_tlscrypt_keydata, sizeof(ctx->original_tlscrypt_keydata));
         return false;
     }
 
@@ -587,8 +655,8 @@ tls_crypt_v2_extract_client_key(struct buffer *buf,
     ctx->cleanup_key_ctx = true;
     ctx->opt.flags |= CO_PACKET_ID_LONG_FORM;
     memset(&ctx->opt.key_ctx_bi, 0, sizeof(ctx->opt.key_ctx_bi));
-    tls_crypt_v2_load_client_key(&ctx->opt.key_ctx_bi, &client_key, true);
-    secure_memzero(&client_key, sizeof(client_key));
+    tls_crypt_v2_load_client_key(&ctx->opt.key_ctx_bi,
+                                 &ctx->original_tlscrypt_keydata, true);
 
     /* Remove client key from buffer so tls-crypt code can unwrap message */
     ASSERT(buf_inc_len(buf, -(BLEN(&wrapped_client_key))));
@@ -688,8 +756,9 @@ tls_crypt_v2_write_client_key_file(const char *filename,
     /* Sanity check: load client key (as "client") */
     struct key_ctx_bi test_client_key;
     struct buffer test_wrapped_client_key;
+    struct key2 keydata;
     msg(D_GENKEY, "Testing client-side key loading...");
-    tls_crypt_v2_init_client_key(&test_client_key, &test_wrapped_client_key,
+    tls_crypt_v2_init_client_key(&test_client_key, &keydata, &test_wrapped_client_key,
                                  client_file, client_inline);
     free_key_ctx_bi(&test_client_key);
 
diff --git a/src/openvpn/tls_crypt.h b/src/openvpn/tls_crypt.h
index 928ff5475..21b012ce4 100644
--- a/src/openvpn/tls_crypt.h
+++ b/src/openvpn/tls_crypt.h
@@ -110,12 +110,24 @@
  * @param key           The key context to initialize
  * @param key_file      The file to read the key from or the key itself if
  *                      key_inline is true.
+ * @param keydata       The keydata used to create key will be written here.
  * @param key_inline    True if key_file contains an inline key, False
  *                      otherwise.
  * @param tls_server    Must be set to true is this is a TLS server instance.
  */
-void tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file,
-                        bool key_inline, bool tls_server);
+void tls_crypt_init_key(struct key_ctx_bi *key, struct key2 *keydata,
+                        const char *key_file, bool key_inline, bool tls_server);
+
+/**
+ * Generates a TLS Crypt to be used in the secure renegotiation using the
+ * TLS EKM exporter function.
+ * @param multi     multi session struct
+ * @param session   session that will be used for the TLS EKM exporter
+ * @return          true iff generating the key was successful
+ */
+bool
+tls_session_generate_secure_renegotiation_key(struct tls_multi *multi,
+                                              struct tls_session *session);
 
 /**
  * Returns the maximum overhead (in bytes) added to the destination buffer by
@@ -171,6 +183,8 @@ void tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt,
  *
  * @param key               Key structure to be initialized with the client
  *                          key.
+ * @param original_key      contains the key data, that has been used to
+ *                          initialise the key parameter
  * @param wrapped_key_buf   Returns buffer containing the wrapped key that will
  *                          be sent to the server when connecting.  Caller must
  *                          free this buffer when no longer needed.
@@ -180,6 +194,7 @@ void tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt,
  *                          otherwise.
  */
 void tls_crypt_v2_init_client_key(struct key_ctx_bi *key,
+                                  struct key2 *original_key,
                                   struct buffer *wrapped_key_buf,
                                   const char *key_file, bool key_inline);
 
diff --git a/tests/unit_tests/openvpn/test_pkt.c b/tests/unit_tests/openvpn/test_pkt.c
index 2d771e301..d05f981f7 100644
--- a/tests/unit_tests/openvpn/test_pkt.c
+++ b/tests/unit_tests/openvpn/test_pkt.c
@@ -55,6 +55,16 @@ parse_line(const char *line, char **p, const int n, const char *file,
     return 0;
 }
 
+/* Define this function here as dummy since including the ssl_*.c files
+ * leads to having to include even more unrelated code */
+bool
+key_state_export_keying_material(struct tls_session *session,
+                                 const char *label, size_t label_size,
+                                 void *ekm, size_t ekm_size)
+{
+    ASSERT(0);
+}
+
 const char *
 print_link_socket_actual(const struct link_socket_actual *act, struct gc_arena *gc)
 {
@@ -183,7 +193,8 @@ init_tas_auth(int key_direction)
 
     crypto_read_openvpn_key(&tls_crypt_kt, &tas.tls_wrap.opt.key_ctx_bi,
                             static_key, true, key_direction,
-                            "Control Channel Authentication", "tls-auth");
+                            "Control Channel Authentication", "tls-auth",
+                            NULL);
 
     return tas;
 }
@@ -195,7 +206,9 @@ init_tas_crypt(bool server)
     tas.tls_wrap.mode = TLS_WRAP_CRYPT;
     tas.tls_wrap.opt.flags |= (CO_IGNORE_PACKET_ID|CO_PACKET_ID_LONG_FORM);
 
-    tls_crypt_init_key(&tas.tls_wrap.opt.key_ctx_bi, static_key, true, server);
+    tls_crypt_init_key(&tas.tls_wrap.opt.key_ctx_bi,
+                       &tas.tls_wrap.original_tlscrypt_keydata, static_key,
+                       true, server);
 
     return tas;
 }
diff --git a/tests/unit_tests/openvpn/test_tls_crypt.c b/tests/unit_tests/openvpn/test_tls_crypt.c
index 82bb0a266..e5b189696 100644
--- a/tests/unit_tests/openvpn/test_tls_crypt.c
+++ b/tests/unit_tests/openvpn/test_tls_crypt.c
@@ -40,6 +40,18 @@
 
 #include "mock_msg.h"
 
+/* Define this function here as dummy since including the ssl_*.c files
+ * leads to having to include even more unrelated code */
+bool
+key_state_export_keying_material(struct tls_session *session,
+                                 const char *label, size_t label_size,
+                                 void *ekm, size_t ekm_size)
+{
+    memset(ekm, 0xba, ekm_size);
+    return true;
+}
+
+
 #define TESTBUF_SIZE            128
 
 /* Defines for use in the tests and the mock parse_line() */
@@ -141,6 +153,7 @@ struct test_tls_crypt_context {
     struct buffer unwrapped;
 };
 
+
 static int
 test_tls_crypt_setup(void **state)
 {
@@ -218,6 +231,75 @@ tls_crypt_loopback(void **state)
                         BLEN(&ctx->source));
 }
 
+
+/**
+ * Test generating secure renegotiation key
+ */
+static void
+test_tls_crypt_secure_reneg_key(void **state)
+{
+    struct test_tls_crypt_context *ctx =
+        (struct test_tls_crypt_context *)*state;
+
+    struct gc_arena gc = gc_new();
+
+    struct tls_multi multi = { 0 };
+    struct tls_session session = { 0 };
+
+    struct tls_options tls_opt = { 0 };
+    tls_opt.replay_window = 32;
+    tls_opt.replay_time = 60;
+    tls_opt.frame.buf.payload_size = 512;
+    session.opt = &tls_opt;
+
+    tls_session_generate_secure_renegotiation_key(&multi, &session);
+
+    struct tls_wrap_ctx *rctx = &session.tls_wrap_reneg;
+
+    tls_crypt_wrap(&ctx->source, &rctx->work, &rctx->opt);
+    assert_int_equal(buf_len(&ctx->source) + 40, buf_len(&rctx->work));
+
+    uint8_t expected_ciphertext[] = {
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x19, 0x27, 0x7f, 0x1c, 0x8d, 0x6e, 0x6a,
+        0x77, 0x96, 0xa8, 0x55, 0x33, 0x7b, 0x9c, 0xfb, 0x56, 0xe1, 0xf1, 0x3a, 0x87, 0x0e, 0x66, 0x47,
+        0xdf, 0xa1, 0x95, 0xc9, 0x2c, 0x17, 0xa0, 0x15, 0xba, 0x49, 0x67, 0xa1, 0x1d, 0x55, 0xea, 0x1a,
+        0x06, 0xa7
+    };
+    assert_memory_equal(BPTR(&rctx->work), expected_ciphertext, buf_len(&rctx->work));
+    tls_wrap_free(&session.tls_wrap_reneg);
+
+    /* Use previous tls-crypt key as 0x00, with xor we should have the same key
+     * and expect the same result */
+    session.tls_wrap.mode = TLS_WRAP_CRYPT;
+    memset(&session.tls_wrap.original_tlscrypt_keydata.keys, 0x00, sizeof(session.tls_wrap.original_tlscrypt_keydata.keys));
+    session.tls_wrap.original_tlscrypt_keydata.n = 2;
+
+    tls_session_generate_secure_renegotiation_key(&multi, &session);
+    tls_crypt_wrap(&ctx->source, &rctx->work, &rctx->opt);
+    assert_int_equal(buf_len(&ctx->source) + 40, buf_len(&rctx->work));
+
+    assert_memory_equal(BPTR(&rctx->work), expected_ciphertext, buf_len(&rctx->work));
+    tls_wrap_free(&session.tls_wrap_reneg);
+
+    /* XOR should not force a different key */
+    memset(&session.tls_wrap.original_tlscrypt_keydata.keys, 0x42, sizeof(session.tls_wrap.original_tlscrypt_keydata.keys));
+    tls_session_generate_secure_renegotiation_key(&multi, &session);
+
+    tls_crypt_wrap(&ctx->source, &rctx->work, &rctx->opt);
+    assert_int_equal(buf_len(&ctx->source) + 40, buf_len(&rctx->work));
+
+    /* packet id at the start should be equal */
+    assert_memory_equal(BPTR(&rctx->work), expected_ciphertext, 8);
+
+    /* Skip packet id */
+    buf_advance(&rctx->work, 8);
+    assert_memory_not_equal(BPTR(&rctx->work), expected_ciphertext, buf_len(&rctx->work));
+    tls_wrap_free(&session.tls_wrap_reneg);
+
+
+    gc_free(&gc);
+}
+
 /**
  * Check that zero-byte messages are successfully wrapped-and-unwrapped.
  */
@@ -632,6 +714,9 @@ main(void)
         cmocka_unit_test_setup_teardown(tls_crypt_v2_wrap_unwrap_dst_too_small,
                                         test_tls_crypt_v2_setup,
                                         test_tls_crypt_v2_teardown),
+        cmocka_unit_test_setup_teardown(test_tls_crypt_secure_reneg_key,
+                                        test_tls_crypt_setup,
+                                        test_tls_crypt_teardown),
         cmocka_unit_test(test_tls_crypt_v2_write_server_key_file),
         cmocka_unit_test(test_tls_crypt_v2_write_client_key_file),
         cmocka_unit_test(test_tls_crypt_v2_write_client_key_file_metadata),
