@@ -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));
}
@@ -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
@@ -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);
@@ -3147,6 +3150,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)
{
@@ -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_RENOG)
+ {
+ o->imported_protocol_flags |= CO_USE_SECURE_RENEGOTIATION;
+ }
#endif
if (proto & IV_PROTO_CC_EXIT_NOTIFY)
@@ -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 renog 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;
@@ -8553,6 +8553,10 @@ add_option(struct options *options,
{
options->imported_protocol_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
}
+ else if (streq(p[j], "secure-renog"))
+ {
+ options->imported_protocol_flags |= CO_USE_SECURE_RENEGOTIATION;
+ }
#endif
else
{
@@ -665,6 +665,10 @@ 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-renog");
+ }
if (buf_len(&proto_flags) > 0)
{
@@ -362,7 +362,7 @@ calc_control_channel_frame_overhead(const struct tls_session *session)
/* Message packet id */
overhead += sizeof(packet_id_type);
- if (session->tls_wrap.mode == TLS_WRAP_CRYPT)
+ if (session->tls_wrap.mode == TLS_WRAP_CRYPT || session->tls_wrap_renog.mode == TLS_WRAP_CRYPT)
{
overhead += tls_crypt_buf_overhead();
}
@@ -1146,6 +1146,7 @@ tls_session_init(struct tls_multi *multi, struct tls_session *session)
session->tls_wrap = session->opt->tls_wrap;
session->tls_wrap.work = alloc_buf(BUF_SIZE(&session->opt->frame));
+
/* initialize packet ID replay window for --tls-auth */
packet_id_init(&session->tls_wrap.opt.packet_id,
session->opt->replay_window,
@@ -1191,6 +1192,7 @@ static void
tls_session_free(struct tls_session *session, bool clear)
{
tls_wrap_free(&session->tls_wrap);
+ tls_wrap_free(&session->tls_wrap_renog);
for (size_t i = 0; i < KS_SIZE; ++i)
{
@@ -1708,6 +1710,7 @@ cleanup:
return ret;
}
+
bool
tls_session_update_crypto_params_do_work(struct tls_multi *multi,
struct tls_session *session,
@@ -1755,6 +1758,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_renegotition_key(multi, session))
+ {
+ return false;
+ }
+ }
return tls_session_generate_data_channel_keys(multi, session);
}
@@ -2062,6 +2076,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_RENOG;
#endif
buf_printf(&out, "IV_PROTO=%d\n", iv_proto);
@@ -3225,6 +3240,7 @@ tls_multi_process(struct tls_multi *multi,
}
else
{
+ /* TODO: secure negotiation fix? */
reset_session(multi, session);
}
}
@@ -3674,7 +3690,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)
{
@@ -3684,7 +3700,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;
@@ -3739,8 +3755,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;
}
@@ -3761,8 +3777,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;
}
@@ -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 renogiations with TLS-EKM dervied tls-crypt key */
+#define IV_PROTO_SECURE_RENOG (1<<9)
+
/* Default field in X509 to be username */
#define X509_USERNAME_FIELD_DEFAULT "CN"
@@ -472,6 +476,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
@@ -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_RENOG_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
@@ -275,6 +275,8 @@ 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 */
+ struct key2 original_tlscrypt_keydata;
+ /**< original key data to be xored in to the key for secure renegotiation */
};
/*
@@ -466,6 +468,10 @@ struct tls_session
/* authenticate control packets */
struct tls_wrap_ctx tls_wrap;
+ /* Specific tls-crypt for renegotiations, if this is valid,
+ * tls_wrap_renog.mode is TLS_WRAP_CRYPT, otherwise ignore it */
+ struct tls_wrap_ctx tls_wrap_renog;
+
int initial_opcode; /* our initial P_ opcode */
struct session_id session_id; /* our random session ID */
@@ -453,6 +453,10 @@ p2p_ncp_set_options(struct tls_multi *multi, struct tls_session *session)
}
}
+ if (iv_proto_peer & IV_PROTO_SECURE_RENOG)
+ {
+ session->opt->crypto_flags |= CO_USE_SECURE_RENEGOTIATION;
+ }
#endif /* if defined(HAVE_EXPORT_KEYING_MATERIAL) */
}
@@ -193,7 +193,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;
}
@@ -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_renog.mode == TLS_WRAP_CRYPT)
+ {
+ return &session->tls_wrap_renog;
+ }
+ 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
@@ -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 dest = dest XOR data
+ */
+static void
+xor_key2_key(struct key2 *dest, const struct key2 *data)
+{
+ ASSERT(dest->n == 2 && data->n == 2);
+ for (int k = 0; k < 2; k++)
+ {
+ for (int j = 0; j < MAX_CIPHER_KEY_LENGTH; j++)
+ {
+ dest->keys[k].cipher[j] = dest->keys[k].cipher[j] ^ data->keys[k].cipher[j];
+ }
+
+ for (int j = 0; j < MAX_HMAC_KEY_LENGTH; j++)
+ {
+ dest->keys[k].hmac[j] = dest->keys[k].hmac[j] ^ data->keys[k].hmac[j];
+ }
+
+ }
+}
+
+bool
+tls_session_generate_secure_renegotition_key(struct tls_multi *multi,
+ struct tls_session *session)
+{
+ session->tls_wrap_renog.opt = session->tls_wrap.opt;
+ session->tls_wrap_renog.mode = TLS_WRAP_CRYPT;
+ session->tls_wrap_renog.cleanup_key_ctx = true;
+ session->tls_wrap_renog.work = alloc_buf(BUF_SIZE(&session->opt->frame));
+ session->tls_wrap_renog.opt.pid_persist = NULL;
+
+ packet_id_init(&session->tls_wrap_renog.opt.packet_id,
+ session->opt->replay_window,
+ session->opt->replay_time,
+ "TLS_WRAP_RENOG", session->key_id);
+
+
+ struct key2 rengokeys;
+ if (!key_state_export_keying_material(session, EXPORT_SECURE_RENOG_LABEL,
+ strlen(EXPORT_SECURE_RENOG_LABEL),
+ rengokeys.keys, sizeof(rengokeys.keys)))
+ {
+ return false;
+ }
+ rengokeys.n = 2;
+
+ if (session->tls_wrap.mode == TLS_WRAP_CRYPT)
+ {
+ xor_key2_key(&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_renog.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);
@@ -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_renegotition_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);
@@ -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;
}
@@ -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_renog_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_renegotition_key(&multi, &session);
+
+ struct tls_wrap_ctx *rctx = &session.tls_wrap_renog;
+
+ 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_renog);
+
+ /* 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_renegotition_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_renog);
+
+ /* 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_renegotition_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_renog);
+
+
+ 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_renog_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),
Currently we have only one slot for renegotiation of the session/keys If a replayed/faked packet is inserted by a malicous attacker, the legimate peer cannot renegotiate anymore. This commit introduces dynamic tls-crypt. When both peer support this feature, both peer create a dynamic tls-crypt key using TLS EKM (export key material) and will enforce using that key and tls-crypt for all renegotiations. Since one of tls-crypt/tls-crypt-v2 purpose is to provide poor man's post quantum crypto guarantees, we have to ensure that the dynamic key tls-crypt key that replace the original tls-crypt key is as strong as the orginal key to avoid problems if there is a weak RNG or TLS EKM produces weak keys. We ensure this but XORing the original key with the key from TLS EKM. If tls-crypt/tls-cryptv2 is not active, we use just the key generated by TLS EKM. OpenVPN 2.x reserves the TM_ACTIVE session for renegotians. When a SOFT_RESET_V1 packet is received, the active TLS session is moved from KS_PRIMARY to KS_SECONDARY. If the SOFT_RESET_V1 came from a replay or a was faked (no tls-auth/tls-crypt), the session is blocked until the TLS renegotiation attempt times out, blocking the legimitate client. Using a dynamic tls-crypt key here block any SOFT_RESET_V1 as replay and fake packets will not have a matching authentication/encryption are discarded. HARD_RESET packets that are from a reconnecting peer are instead put in the TM_UNTRUSTED/KS_PRIMARY slot until they are sufficiently verified, so the dyanmic tls-crypt key is not used here. Replay/fake packets also do not block the legimiate client. The issue was initially reported by Fabio Streun <fabio.streun@inf.ethz.ch> Signed-off-by: Arne Schwabe <arne@rfc2549.org> --- src/openvpn/crypto.c | 7 +- src/openvpn/crypto.h | 16 +++- src/openvpn/init.c | 8 +- src/openvpn/multi.c | 4 + src/openvpn/openvpn.h | 2 + src/openvpn/options.c | 4 + src/openvpn/push.c | 4 + src/openvpn/ssl.c | 30 ++++++-- src/openvpn/ssl.h | 5 ++ src/openvpn/ssl_backend.h | 1 + src/openvpn/ssl_common.h | 6 ++ src/openvpn/ssl_ncp.c | 4 + src/openvpn/ssl_pkt.c | 2 +- src/openvpn/ssl_pkt.h | 21 +++++ src/openvpn/tls_crypt.c | 93 ++++++++++++++++++++--- src/openvpn/tls_crypt.h | 19 ++++- tests/unit_tests/openvpn/test_pkt.c | 17 ++++- tests/unit_tests/openvpn/test_tls_crypt.c | 85 +++++++++++++++++++++ 18 files changed, 299 insertions(+), 29 deletions(-)