From ffe0ad41985d7d5f67ae6fc7d58ffa327243f76b Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Thu, 9 Jan 2025 18:49:28 +0100 Subject: [PATCH] Do not attempt to decrypt packets anymore after 2**36 failed decryptions To avoid attacks (especially on Chacha20-Poly1305) we do not allow decryption anymore after 2**36 failed verifications. After 2**35, we trigger a renegotiation (to avoid that situation). For the theoretical background, see - https://datatracker.ietf.org/doc/draft-irtf-cfrg-aead-limits/ - RFC 9147 (DTLS 1.3) section 4.5.3 "AEAD limits" - https://eprint.iacr.org/2024/051.pdf Change-Id: I81440ac28a1ad553652e201234e5ddfe03a8c190 Signed-off-by: Arne Schwabe Acked-by: MaxF Message-Id: <20250109174928.17862-1-gert@greenie.muc.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg30387.html Signed-off-by: Gert Doering --- src/openvpn/crypto.c | 9 ++++++++- src/openvpn/crypto.h | 28 ++++++++++++++++++++++++++++ src/openvpn/ssl.c | 5 +++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index df38cdd3..ee9b0c6a 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -405,7 +405,13 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work, { static const char error_prefix[] = "AEAD Decrypt error"; struct packet_id_net pin = { 0 }; - const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; + struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; + + if (cipher_decrypt_verify_fail_exceeded(ctx)) + { + CRYPT_DROP("Decryption failed verification limit reached."); + } + int outlen; struct gc_arena gc; @@ -511,6 +517,7 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work, if (!cipher_ctx_final_check_tag(ctx->cipher, BPTR(&work) + outlen, &outlen, tag_ptr, tag_size)) { + ctx->failed_verifications++; CRYPT_DROP("packet tag authentication failed"); } ASSERT(buf_inc_len(&work, outlen)); diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h index 3ad31c53..fe81c7f3 100644 --- a/src/openvpn/crypto.h +++ b/src/openvpn/crypto.h @@ -209,6 +209,8 @@ struct key_ctx * with the current key in number of 128 bit blocks (only used for * AEAD ciphers) */ uint64_t plaintext_blocks; + /** number of failed verification using this cipher */ + uint64_t failed_verifications; }; #define KEY_DIRECTION_BIDIRECTIONAL 0 /* same keys for both directions */ @@ -660,6 +662,32 @@ create_kt(const char *cipher, const char *md, const char *optname) uint64_t cipher_get_aead_limits(const char *ciphername); +/** + * Check if the number of failed decryption is over the acceptable limit. + */ +static inline bool +cipher_decrypt_verify_fail_exceeded(const struct key_ctx *ctx) +{ + /* Use 2**36, same as DTLS 1.3. Strictly speaking this only guarantees + * the security margin for packets up to 2^10 blocks (16384 bytes) + * but we accept slightly lower security bound for the edge + * of Chacha20-Poly1305 and packets over 16k as MTUs over 16k are + * extremely rarely used */ + return ctx->failed_verifications > (1ull << 36); +} + +/** + * Check if the number of failed decryption is approaching the limit and we + * should try to move to a new key + */ +static inline bool +cipher_decrypt_verify_fail_warn(const struct key_ctx *ctx) +{ + /* Use 2**35, half the amount after which we refuse to decrypt */ + return ctx->failed_verifications > (1ull << 35); +} + + /** * Blocksize used for the AEAD limit caluclation * diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index cf7f34f2..e4a7b57a 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -3005,6 +3005,11 @@ should_trigger_renegotiation(const struct tls_session *session, const struct key return true; } + if (cipher_decrypt_verify_fail_warn(&key_ctx_bi->decrypt)) + { + return true; + } + return false; } /* -- 2.47.2