CVE-2023-36327: Integer Overflow or Wraparound (CWE-190)
value: Integer Overflow vulnerability in RELIC before commit 421f2e91cf2ba42473d4d54daf24e295679e290e, allows attackers to execute arbitrary code and cause a denial of service in pos argument in bn_get_prime function.
CWE description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
CVSS v3 base score: 9.8
Published: 2023-09-01T16:15Z
Repository: https://github.com/relic-toolkit/relic
Language: C
Files changed: include/relic_bn.h, include/relic_label.h, src/bn/relic_bn_prime.c, test/test_cp.c
Commit date: 2022-11-14 21:49:05 +0100
Commit message: Remove dangerous API for returning small primes from an internal table.

Diff: diff --git a/include/relic_bn.h b/include/relic_bn.h
index c93fa4c4..1b5b3244 100644
--- a/include/relic_bn.h
+++ b/include/relic_bn.h
@@ -1268,19 +1268,10 @@ int bn_smb_leg(const bn_t a, const bn_t b);
  * @throw ERR_NO_VALID		- if there input is even or negative.
  * @return the result.
  */
 int bn_smb_jac(const bn_t a, const bn_t b);
 
-/**
- * Returns a small precomputed prime from a given position in the list of prime
- * numbers.
- *
- * @param[in] pos			- the position in the prime sequence.
- * @return a prime if the position is lower than 512, 0 otherwise.
- */
-dig_t bn_get_prime(int pos);
-
 /**
  * Tests if a number is a probable prime.
  *
  * @param[in] a				- the multiple precision integer to test.
  * @return 1 if a is prime, 0 otherwise.
diff --git a/include/relic_label.h b/include/relic_label.h
index 1591c5ec..d123a5c6 100644
--- a/include/relic_label.h
+++ b/include/relic_label.h
@@ -262,11 +262,10 @@
 #undef bn_gcd_ext_mid
 #undef bn_gcd_ext_dig
 #undef bn_lcm
 #undef bn_smb_leg
 #undef bn_smb_jac
-#undef bn_get_prime
 #undef bn_is_prime
 #undef bn_is_prime_basic
 #undef bn_is_prime_rabin
 #undef bn_is_prime_solov
 #undef bn_gen_prime_basic
@@ -371,11 +370,10 @@
 #define bn_gcd_ext_mid 	RLC_PREFIX(bn_gcd_ext_mid)
 #define bn_gcd_ext_dig 	RLC_PREFIX(bn_gcd_ext_dig)
 #define bn_lcm 	RLC_PREFIX(bn_lcm)
 #define bn_smb_leg 	RLC_PREFIX(bn_smb_leg)
 #define bn_smb_jac 	RLC_PREFIX(bn_smb_jac)
-#define bn_get_prime 	RLC_PREFIX(bn_get_prime)
 #define bn_is_prime 	RLC_PREFIX(bn_is_prime)
 #define bn_is_prime_basic 	RLC_PREFIX(bn_is_prime_basic)
 #define bn_is_prime_rabin 	RLC_PREFIX(bn_is_prime_rabin)
 #define bn_is_prime_solov 	RLC_PREFIX(bn_is_prime_solov)
 #define bn_gen_prime_basic 	RLC_PREFIX(bn_gen_prime_basic)
diff --git a/src/bn/relic_bn_prime.c b/src/bn/relic_bn_prime.c
index aa4247b9..ed232457 100644
--- a/src/bn/relic_bn_prime.c
+++ b/src/bn/relic_bn_prime.c
@@ -168,17 +168,10 @@ static void bn_exp(bn_t c, const bn_t a, const bn_t b, const bn_t m) {
 
 /*============================================================================*/
 /* Public definitions                                                         */
 /*============================================================================*/
 
-dig_t bn_get_prime(int pos) {
-	if (pos >= BASIC_TESTS) {
-		return 0;
-	}
-	return primes[pos];
-}
-
 int bn_is_prime(const bn_t a) {
 	int result;
 
 	result = 0;
 	if (!bn_is_prime_basic(a)) {
diff --git a/test/test_cp.c b/test/test_cp.c
index f6baec56..65198769 100644
--- a/test/test_cp.c
+++ b/test/test_cp.c
@@ -130,10 +130,11 @@ static int benaloh(void) {
 	bn_t a, b;
 	dig_t in, out;
 	uint8_t buf[RLC_BN_BITS / 8 + 1];
 	size_t len;
 	int result;
+	dig_t prime = 0xFB;
 
 	bn_null(a);
 	bn_null(b);
 	bdpe_null(pub);
 	bdpe_null(prv);
@@ -142,32 +143,32 @@ static int benaloh(void) {
 		bn_new(a);
 		bn_new(b);
 		bdpe_new(pub);
 		bdpe_new(prv);
 
-		result = cp_bdpe_gen(pub, prv, bn_get_prime(47), RLC_BN_BITS);
+		result = cp_bdpe_gen(pub, prv, prime, RLC_BN_BITS);
 
 		TEST_CASE("benaloh encryption/decryption is correct") {
 			TEST_ASSERT(result == RLC_OK, end);
 			len = RLC_BN_BITS / 8 + 1;
 			rand_bytes(buf, 1);
-			in = buf[0] % bn_get_prime(47);
+			in = buf[0] % prime;
 			TEST_ASSERT(cp_bdpe_enc(buf, &len, in, pub) == RLC_OK, end);
 			TEST_ASSERT(cp_bdpe_dec(&out, buf, len, prv) == RLC_OK, end);
 			TEST_ASSERT(in == out, end);
 		} TEST_END;
 
 		TEST_CASE("benaloh encryption/decryption is homomorphic") {
 			TEST_ASSERT(result == RLC_OK, end);
 			len = RLC_BN_BITS / 8 + 1;
 			rand_bytes(buf, 1);
-			in = buf[0] % bn_get_prime(47);
+			in = buf[0] % prime;
 			TEST_ASSERT(cp_bdpe_enc(buf, &len, in, pub) == RLC_OK, end);
 			bn_read_bin(a, buf, len);
 			rand_bytes(buf, 1);
-			out = (buf[0] % bn_get_prime(47));
-			in = (in + out) % bn_get_prime(47);
+			out = (buf[0] % prime);
+			in = (in + out) % prime;
 			TEST_ASSERT(cp_bdpe_enc(buf, &len, out, pub) == RLC_OK, end);
 			bn_read_bin(b, buf, len);
 			bn_mul(a, a, b);
 			bn_mod(a, a, pub->n);
 			len = bn_size_bin(pub->n);
@@ -2235,30 +2236,32 @@ static int psi(void) {
 					N) == RLC_OK, end);
 				TEST_ASSERT(l == k, end);
 			}
 		} TEST_END;
 
-		TEST_CASE("pairing-based laconic private set intersection is correct") {
-			pc_get_ord(q);
-			for (int j = 0; j < M; j++) {
-				bn_rand_mod(x[j], q);
-			}
-			for (int j = 0; j < N; j++) {
-				bn_rand_mod(y[j], q);
-			}
-			TEST_ASSERT(cp_pbpsi_gen(q, ss, s, M) == RLC_OK, end);
-			TEST_ASSERT(cp_pbpsi_ask(d, r, x, s, M) == RLC_OK, end);
-			for (int k = 0; k <= N; k++) {
-				for (int j = 0; j < k; j++) {
-					bn_copy(y[j], x[j]);
+		if (pc_param_set_any() == RLC_OK) {
+			TEST_CASE("pairing-based laconic private set intersection is correct") {
+				pc_get_ord(q);
+				for (int j = 0; j < M; j++) {
+					bn_rand_mod(x[j], q);
 				}
-				TEST_ASSERT(cp_pbpsi_ans(t, u, ss, d[0], y, N) == RLC_OK, end);
-				TEST_ASSERT(cp_pbpsi_int(z, &l, d, x, M, t, u, N) == RLC_OK,
-					end);
-				TEST_ASSERT(l == k, end);
-			}
-		} TEST_END;
+				for (int j = 0; j < N; j++) {
+					bn_rand_mod(y[j], q);
+				}
+				TEST_ASSERT(cp_pbpsi_gen(q, ss, s, M) == RLC_OK, end);
+				TEST_ASSERT(cp_pbpsi_ask(d, r, x, s, M) == RLC_OK, end);
+				for (int k = 0; k <= N; k++) {
+					for (int j = 0; j < k; j++) {
+						bn_copy(y[j], x[j]);
+					}
+					TEST_ASSERT(cp_pbpsi_ans(t, u, ss, d[0], y, N) == RLC_OK, end);
+					TEST_ASSERT(cp_pbpsi_int(z, &l, d, x, M, t, u, N) == RLC_OK,
+						end);
+					TEST_ASSERT(l == k, end);
+				}
+			} TEST_END;
+		}
 	}
 	RLC_CATCH_ANY {
 		RLC_ERROR(end);
 	}
 	code = RLC_OK;
@@ -2296,20 +2299,10 @@ int main(void) {
 		return 1;
 	}
 
 	util_banner("Tests for the CP module", 0);
 
-	#if defined(WITH_BN) && defined(WITH_PC)
-		util_banner("Protocols based on accumulators:\n", 0);
-		if (pc_param_set_any() == RLC_OK) {
-			if (psi() != RLC_OK) {
-				core_clean();
-				return 1;
-			}
-		}
-	#endif
-
 #if defined(WITH_BN)
 	util_banner("Protocols based on integer factorization:\n", 0);
 	if (rsa() != RLC_OK) {
 		core_clean();
 		return 1;
@@ -2464,10 +2457,18 @@ int main(void) {
 			return 1;
 		}
 	}
 #endif
 
+#if defined(WITH_BN) && defined(WITH_PC)
+	util_banner("Protocols based on accumulators:\n", 0);
+	if (psi() != RLC_OK) {
+		core_clean();
+		return 1;
+	}
+#endif
+
 	util_banner("All tests have passed.\n", 0);
 
 	core_clean();
 	return 0;
 }

Vulnerable code: /**
 * Returns a small precomputed prime from a given position in the list of prime
 * numbers.
 *
 * @param[in] pos			- the position in the prime sequence.
 * @return a prime if the position is lower than 512, 0 otherwise.
 */
dig_t bn_get_prime(int pos);

#undef bn_get_prime
#define bn_get_prime 	RLC_PREFIX(bn_get_prime)
dig_t bn_get_prime(int pos) {
	if (pos >= BASIC_TESTS) {
		return 0;
	}
	return primes[pos];
}

		result = cp_bdpe_gen(pub, prv, bn_get_prime(47), RLC_BN_BITS);
			in = buf[0] % bn_get_prime(47);
			in = buf[0] % bn_get_prime(47);
			out = (buf[0] % bn_get_prime(47));
			in = (in + out) % bn_get_prime(47);
		TEST_CASE("pairing-based laconic private set intersection is correct") {
			pc_get_ord(q);
			for (int j = 0; j < M; j++) {
				bn_rand_mod(x[j], q);
			}
			for (int j = 0; j < N; j++) {
				bn_rand_mod(y[j], q);
			}
			TEST_ASSERT(cp_pbpsi_gen(q, ss, s, M) == RLC_OK, end);
			TEST_ASSERT(cp_pbpsi_ask(d, r, x, s, M) == RLC_OK, end);
			for (int k = 0; k <= N; k++) {
				for (int j = 0; j < k; j++) {
					bn_copy(y[j], x[j]);
				TEST_ASSERT(cp_pbpsi_ans(t, u, ss, d[0], y, N) == RLC_OK, end);
				TEST_ASSERT(cp_pbpsi_int(z, &l, d, x, M, t, u, N) == RLC_OK,
					end);
				TEST_ASSERT(l == k, end);
			}
		} TEST_END;
	#if defined(WITH_BN) && defined(WITH_PC)
		util_banner("Protocols based on accumulators:\n", 0);
		if (pc_param_set_any() == RLC_OK) {
			if (psi() != RLC_OK) {
				core_clean();
				return 1;
			}
		}
	#endif

Fixed code: 	dig_t prime = 0xFB;
		result = cp_bdpe_gen(pub, prv, prime, RLC_BN_BITS);
			in = buf[0] % prime;
			in = buf[0] % prime;
			out = (buf[0] % prime);
			in = (in + out) % prime;
		if (pc_param_set_any() == RLC_OK) {
			TEST_CASE("pairing-based laconic private set intersection is correct") {
				pc_get_ord(q);
				for (int j = 0; j < M; j++) {
					bn_rand_mod(x[j], q);
				for (int j = 0; j < N; j++) {
					bn_rand_mod(y[j], q);
				}
				TEST_ASSERT(cp_pbpsi_gen(q, ss, s, M) == RLC_OK, end);
				TEST_ASSERT(cp_pbpsi_ask(d, r, x, s, M) == RLC_OK, end);
				for (int k = 0; k <= N; k++) {
					for (int j = 0; j < k; j++) {
						bn_copy(y[j], x[j]);
					}
					TEST_ASSERT(cp_pbpsi_ans(t, u, ss, d[0], y, N) == RLC_OK, end);
					TEST_ASSERT(cp_pbpsi_int(z, &l, d, x, M, t, u, N) == RLC_OK,
						end);
					TEST_ASSERT(l == k, end);
				}
			} TEST_END;
		}
#if defined(WITH_BN) && defined(WITH_PC)
	util_banner("Protocols based on accumulators:\n", 0);
	if (psi() != RLC_OK) {
		core_clean();
		return 1;
	}
#endif