From 10f44687651c0bb7376f8726de6e9d323b1a50c5 Mon Sep 17 00:00:00 2001
From: Rahul Jain <rahul.jain@suse.com>
Date: Tue, 21 Apr 2026 14:18:08 +0530
Subject: [PATCH] Fix CVE-2026-35334: gmp plugin RSA decryption

---
 .../plugins/gmp/gmp_rsa_private_key.c         |  59 ++++++----
 src/libstrongswan/utils/utils.h               |   1 +
 src/libstrongswan/utils/utils/constant_time.h | 103 ++++++++++++++++++
 3 files changed, 141 insertions(+), 22 deletions(-)
 create mode 100644 src/libstrongswan/utils/utils/constant_time.h

diff --git a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c
index 47784b6..317e953 100644
--- a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c
+++ b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c
@@ -495,8 +495,8 @@ METHOD(private_key_t, decrypt, bool,
 	private_gmp_rsa_private_key_t *this, encryption_scheme_t scheme,
 	void *params, chunk_t crypto, chunk_t *plain)
 {
-	chunk_t em, stripped;
-	bool success = FALSE;
+	chunk_t em;
+	u_int valid, i, j, found_sep = 0, sep_index = 0, m_index;
 
 	if (scheme != ENCRYPT_RSA_PKCS1)
 	{
@@ -505,33 +505,48 @@ METHOD(private_key_t, decrypt, bool,
 		return FALSE;
 	}
 	/* rsa decryption using PKCS#1 RSADP */
-	stripped = em = rsadp(this, crypto);
-
-	/* PKCS#1 v1.5 8.1 encryption-block formatting (EB = 00 || 02 || PS || 00 || D) */
-
-	/* check for hex pattern 00 02 in decrypted message */
-	if ((*stripped.ptr++ != 0x00) || (*(stripped.ptr++) != 0x02))
+	em = rsadp(this, crypto);
+	if (em.len != this->k)
 	{
-		DBG1(DBG_LIB, "incorrect padding - probably wrong rsa key");
-		goto end;
+		return FALSE;
 	}
-	stripped.len -= 2;
-
+	/* PKCS#1 v1.5, RFC 8017, section 7.2.2 message structure:
+	 * EM = 00 || 02 || PS || 00 || M */
+ 
+	/* check for hex pattern 00 02 in decrypted message */
+	valid  = constant_time_eq(em.ptr[0], 0x00);
+	valid &= constant_time_eq(em.ptr[1], 0x02);
 	/* the plaintext data starts after first 0x00 byte */
-	while (stripped.len-- > 0 && *stripped.ptr++ != 0x00)
+	for (i = 2; i < em.len; i++)
+	{
+		u_int zero = constant_time_eq(em.ptr[i], 0x00);
 
-	if (stripped.len == 0)
+		sep_index = constant_time_select(i, sep_index, ~found_sep & zero);
+		found_sep |= zero;
+	}
+	/* make sure PS is at least eight bytes long (plus the initial bytes) */
+	valid &= constant_time_ge(sep_index, 10);
+
+	/* instead of copying the message directly, we try not to reveal the message
+	 * length i.e. where the 0x00 byte was. and since clearing a chunk is
+	 * relatively efficient, i.e. doesn't leak much, we always allocate and copy
+	 * a value and then clear it if the structure was invalid */
+	m_index = constant_time_select(sep_index + 1, 11, valid);
+ 
+	*plain = chunk_alloc(this->k);
+	for (i = 0, j = 0; i < em.len; i++)
 	{
-		DBG1(DBG_LIB, "no plaintext data");
-		goto end;
+		plain->ptr[j] = em.ptr[i];
+		j += constant_time_ge(i, m_index);
 	}
+	plain->len = j;
 
-	*plain = chunk_clone(stripped);
-	success = TRUE;
-
-end:
-	chunk_clear(&em);
-	return success;
+	if (!valid)
+	{
+		chunk_clear(plain);
+	}
+ 	chunk_clear(&em);
+	return valid;
 }
 
 METHOD(private_key_t, get_keysize, int,
diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h
index 42d0114..ab0be72 100644
--- a/src/libstrongswan/utils/utils.h
+++ b/src/libstrongswan/utils/utils.h
@@ -53,6 +53,7 @@
 #include "utils/atomics.h"
 #include "utils/align.h"
 #include "utils/byteorder.h"
+#include "utils/constant_time.h"
 #include "utils/string.h"
 #include "utils/memory.h"
 #include "utils/strerror.h"
diff --git a/src/libstrongswan/utils/utils/constant_time.h b/src/libstrongswan/utils/utils/constant_time.h
new file mode 100644
index 0000000..0c2c6a2
--- /dev/null
+++ b/src/libstrongswan/utils/utils/constant_time.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2026 Tobias Brunner
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup constant_time_i constant_time
+ * @{ @ingroup constant_time_i
+ */
+
+#ifndef CONSTANT_TIME_H_
+#define CONSTANT_TIME_H_
+
+#include <stdint.h>
+
+/**
+ * Check if the given values are not equal in constant time.
+ *
+ * @param x		first value to check
+ * @param y		second value to check
+ * @return		1 if values are not equal, 0 otherwise
+ */
+static inline u_int constant_time_neq(uint32_t x, uint32_t y)
+{
+	return ((x-y) | (y-x)) >> 31;
+}
+
+/**
+ * Check if the given values are equal in constant time.
+ *
+ * @param x		first value to check
+ * @param y		second value to check
+ * @return		1 if values are equal, 0 otherwise
+ */
+static inline u_int constant_time_eq(uint32_t x, uint32_t y)
+{
+	return 1 ^ constant_time_neq(x, y);
+}
+
+/**
+ * Compare the two values and return 1 if the first argument is lower than
+ * the second in constant time.
+ *
+ * @param x		first value to check
+ * @param y		second value to check
+ * @return		1 if first value is lower than second
+ */
+static inline u_int constant_time_lt(uint32_t x, uint32_t y)
+{
+	return (x ^ ((x^y) | ((x-y) ^ y))) >> 31;
+}
+
+/**
+ * Compare the two values and return 1 if the first argument greater or equal to
+ * the second in constant time.
+ *
+ * @param x		first value to check
+ * @param y		second value to check
+ * @return		1 if first value is greater or equal to the second
+ */
+static inline u_int constant_time_ge(uint32_t x, uint32_t y)
+{
+	return 1 ^ constant_time_lt(x, y);
+}
+
+/**
+ * Return a 32-bit all bit-set mask if the given value is not 0.
+ *
+ * @param x		value to check
+ * @return		0xffffffff if value is != 0, 0 otherwise
+ */
+static inline uint32_t constant_time_mask(uint32_t x)
+{
+	return -(uint32_t)constant_time_neq(x, 0);
+}
+
+/**
+ * Select one of two values depending on whether the condition is != 0 or not.
+ * Basically equivalent to 'c ? x : y'.
+ *
+ * @param x		first value to select
+ * @param y		second value to select
+ * @param c		condition
+ * @return		x if c is != 0, y otherwise
+ */
+static inline uint32_t constant_time_select(uint32_t x, uint32_t y, uint32_t c)
+{
+	uint32_t m = constant_time_mask(c);
+	return (x & m) | (y & ~m);
+}
+
+#endif /** CONSTANT_TIME_H_ @} */
-- 
2.50.0

