On Mon, Dec 12, 2022 at 12:27:45PM +0100, Arne Schwabe wrote: > 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
"peers" > material) and will enforce using that key and tls-crypt for all > renegotiations. This also add an additional protection layer for General question about this feature: We trigger using this key on key_id > 0, so if I understand the code correctly, it will be used first when we want to renegotiate. But will it then continued to be used? What exactly is the state after the successful renegotiation? > renegotiations to be taken over by an illegimate client, binding the > renegotiations tightly to the original session. Especially when 2FA, webauth > or similar authentication is used, many third party setup ignore the need > to secure renegotiation with an auth-token. > > Since one of tls-crypt/tls-crypt-v2 purposes 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 "if A or if B" is I think easier to read instead "if (A or B)" (in natural language), so I would recommend "if TLS EKM". > 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. We also do not use hashing or anything else on the original key > before XOR to avoid any potential of a structure in the key or something > else that might weaken post-quantum use cases. > > OpenVPN 2.x reserves the TM_ACTIVE session for renegotians. When a "renegotiations" > SOFT_RESET_V1 packet is received, the active TLS session is moved from > KS_PRIMARY to KS_SECONDARY. Here an attacker could theorectically send a > faked/replayed SOFT_RESET_V1 and firste packet containing the TLS client "first" > hello. If this happens, 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 (and following "blocks" > packets) as replay and fake packets will not have a matching > authentication/encryption are discarded. "and will be 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. "legitimate" > This commit delays the purging of the original tls-crypt key data from > directly after passing it to crypto library to tls_wrap_free. We do this > to allow us mixing the new exported key with the original key. Since need "we need" > to be able to generate the secure renegotiation key, deleting > the key after generating a secure renegotiation key is not an option. > Even when the client does not support secure renegotiation, deleting the > key is not a good as the reconnecting client or (especially in p2p mode "a good option" ? "as the" -> "since when the" > with float) another client does the reconnect, we might need to generate a > secure renegotiation key. Delaying the deletion of the key has also little > effect as the key is still present in the OpenSSL/mbed TLS structures in > the tls_wrap structure, so only the number the keys is in memory would "only the number of times the key" ? > be reduced. [...] > diff --git a/Changes.rst b/Changes.rst > index fe91ece2e..d0b8ac21a 100644 > --- a/Changes.rst > +++ b/Changes.rst > @@ -111,6 +111,12 @@ Tun MTU can be pushed > directive ``--tun-mtu-max`` has been introduced to increase the maximum > pushable MTU size (defaults to 1600). > > +Secure renegotiation > + When both peer are OpenVPN 2.6.0+, OpenVPN will use secure renegotiation "peers" > + using a dynamically created tls-crypt key. This ensure that only the > + previously authenticated peer can do trigger renegotiation and complete > + renegotiations. > + > Improved control channel packet size control (``max-packet-size``) > The size of control channel is no longer tied to > ``--link-mtu``/``--tun-mtu`` and can be set using ``--max-packet-size``. > 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) "renegotiation" > * > * 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/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 */ "original tls-crypt 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/ssl.h b/src/openvpn/ssl.h > index 55c672d44..1550fc79d 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 */ "renegotiations" "derived" > +#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_pkt.h b/src/openvpn/ssl_pkt.h > index 9bb3ca958..dc3a53bf6 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 Since this seems to be where the actual magic happens, maybe we could have a slightly more verbose comment here for people not as familiar with the OpenVPN state machine? Will the renegotiation key be refreshed as part of the renegotiation or do all future renegotiations the same key? > + * > + * @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.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 Crypt key" ? > + * 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 I think there should be no comma here. > + * 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); > [...] Regards, -- Frank Lichtenheld _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel