lkundrak / rpms / hostapd

Forked from rpms/hostapd 4 years ago
Clone

Blame 0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch

John W. Linville aeb7fa6
From cff138b0747fa39765cbc641b66cfa5d7f1735d1 Mon Sep 17 00:00:00 2001
John W. Linville aeb7fa6
From: Jouni Malinen <jouni@codeaurora.org>
John W. Linville aeb7fa6
Date: Sat, 2 Mar 2019 16:05:56 +0200
John W. Linville aeb7fa6
Subject: [PATCH 09/14] SAE: Use constant time operations in
John W. Linville aeb7fa6
 sae_test_pwd_seed_ffc()
John W. Linville aeb7fa6
John W. Linville aeb7fa6
Try to avoid showing externally visible timing or memory access
John W. Linville aeb7fa6
differences regardless of whether the derived pwd-value is smaller than
John W. Linville aeb7fa6
the group prime.
John W. Linville aeb7fa6
John W. Linville aeb7fa6
This is related to CVE-2019-9494.
John W. Linville aeb7fa6
John W. Linville aeb7fa6
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
John W. Linville aeb7fa6
---
John W. Linville aeb7fa6
 src/common/sae.c | 75 ++++++++++++++++++++++++++++++++++----------------------
John W. Linville aeb7fa6
 1 file changed, 46 insertions(+), 29 deletions(-)
John W. Linville aeb7fa6
John W. Linville aeb7fa6
diff --git a/src/common/sae.c b/src/common/sae.c
John W. Linville aeb7fa6
index fa9a145..eaf825d 100644
John W. Linville aeb7fa6
--- a/src/common/sae.c
John W. Linville aeb7fa6
+++ b/src/common/sae.c
John W. Linville aeb7fa6
@@ -334,14 +334,17 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
John W. Linville aeb7fa6
 }
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
+/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
John W. Linville aeb7fa6
+ * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
John W. Linville aeb7fa6
 static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
John W. Linville aeb7fa6
 				 struct crypto_bignum *pwe)
John W. Linville aeb7fa6
 {
John W. Linville aeb7fa6
 	u8 pwd_value[SAE_MAX_PRIME_LEN];
John W. Linville aeb7fa6
 	size_t bits = sae->tmp->prime_len * 8;
John W. Linville aeb7fa6
 	u8 exp[1];
John W. Linville aeb7fa6
-	struct crypto_bignum *a, *b;
John W. Linville aeb7fa6
-	int res;
John W. Linville aeb7fa6
+	struct crypto_bignum *a, *b = NULL;
John W. Linville aeb7fa6
+	int res, is_val;
John W. Linville aeb7fa6
+	u8 pwd_value_valid;
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
@@ -353,16 +356,29 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
John W. Linville aeb7fa6
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
John W. Linville aeb7fa6
 			sae->tmp->prime_len);
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
-	if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
John W. Linville aeb7fa6
-	{
John W. Linville aeb7fa6
-		wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
John W. Linville aeb7fa6
-		return 0;
John W. Linville aeb7fa6
-	}
John W. Linville aeb7fa6
+	/* Check whether pwd-value < p */
John W. Linville aeb7fa6
+	res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
John W. Linville aeb7fa6
+				sae->tmp->prime_len);
John W. Linville aeb7fa6
+	/* pwd-value >= p is invalid, so res is < 0 for the valid cases and
John W. Linville aeb7fa6
+	 * the negative sign can be used to fill the mask for constant time
John W. Linville aeb7fa6
+	 * selection */
John W. Linville aeb7fa6
+	pwd_value_valid = const_time_fill_msb(res);
John W. Linville aeb7fa6
+
John W. Linville aeb7fa6
+	/* If pwd-value >= p, force pwd-value to be < p and perform the
John W. Linville aeb7fa6
+	 * calculations anyway to hide timing difference. The derived PWE will
John W. Linville aeb7fa6
+	 * be ignored in that case. */
John W. Linville aeb7fa6
+	pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
 	/* PWE = pwd-value^((p-1)/r) modulo p */
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
+	res = -1;
John W. Linville aeb7fa6
 	a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
John W. Linville aeb7fa6
+	if (!a)
John W. Linville aeb7fa6
+		goto fail;
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
+	/* This is an optimization based on the used group that does not depend
John W. Linville aeb7fa6
+	 * on the password in any way, so it is fine to use separate branches
John W. Linville aeb7fa6
+	 * for this step without constant time operations. */
John W. Linville aeb7fa6
 	if (sae->tmp->dh->safe_prime) {
John W. Linville aeb7fa6
 		/*
John W. Linville aeb7fa6
 		 * r = (p-1)/2 for the group used here, so this becomes:
John W. Linville aeb7fa6
@@ -376,33 +392,34 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
John W. Linville aeb7fa6
 		b = crypto_bignum_init_set(exp, sizeof(exp));
John W. Linville aeb7fa6
 		if (b == NULL ||
John W. Linville aeb7fa6
 		    crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
John W. Linville aeb7fa6
-		    crypto_bignum_div(b, sae->tmp->order, b) < 0) {
John W. Linville aeb7fa6
-			crypto_bignum_deinit(b, 0);
John W. Linville aeb7fa6
-			b = NULL;
John W. Linville aeb7fa6
-		}
John W. Linville aeb7fa6
+		    crypto_bignum_div(b, sae->tmp->order, b) < 0)
John W. Linville aeb7fa6
+			goto fail;
John W. Linville aeb7fa6
 	}
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
-	if (a == NULL || b == NULL)
John W. Linville aeb7fa6
-		res = -1;
John W. Linville aeb7fa6
-	else
John W. Linville aeb7fa6
-		res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
John W. Linville aeb7fa6
-
John W. Linville aeb7fa6
-	crypto_bignum_deinit(a, 0);
John W. Linville aeb7fa6
-	crypto_bignum_deinit(b, 0);
John W. Linville aeb7fa6
+	if (!b)
John W. Linville aeb7fa6
+		goto fail;
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
-	if (res < 0) {
John W. Linville aeb7fa6
-		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
John W. Linville aeb7fa6
-		return -1;
John W. Linville aeb7fa6
-	}
John W. Linville aeb7fa6
+	res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
John W. Linville aeb7fa6
+	if (res < 0)
John W. Linville aeb7fa6
+		goto fail;
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
-	/* if (PWE > 1) --> found */
John W. Linville aeb7fa6
-	if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
John W. Linville aeb7fa6
-		wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
John W. Linville aeb7fa6
-		return 0;
John W. Linville aeb7fa6
-	}
John W. Linville aeb7fa6
+	/* There were no fatal errors in calculations, so determine the return
John W. Linville aeb7fa6
+	 * value using constant time operations. We get here for number of
John W. Linville aeb7fa6
+	 * invalid cases which are cleared here after having performed all the
John W. Linville aeb7fa6
+	 * computation. PWE is valid if pwd-value was less than prime and
John W. Linville aeb7fa6
+	 * PWE > 1. Start with pwd-value check first and then use constant time
John W. Linville aeb7fa6
+	 * operations to clear res to 0 if PWE is 0 or 1.
John W. Linville aeb7fa6
+	 */
John W. Linville aeb7fa6
+	res = const_time_select_u8(pwd_value_valid, 1, 0);
John W. Linville aeb7fa6
+	is_val = crypto_bignum_is_zero(pwe);
John W. Linville aeb7fa6
+	res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
John W. Linville aeb7fa6
+	is_val = crypto_bignum_is_one(pwe);
John W. Linville aeb7fa6
+	res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
-	wpa_printf(MSG_DEBUG, "SAE: PWE found");
John W. Linville aeb7fa6
-	return 1;
John W. Linville aeb7fa6
+fail:
John W. Linville aeb7fa6
+	crypto_bignum_deinit(a, 1);
John W. Linville aeb7fa6
+	crypto_bignum_deinit(b, 1);
John W. Linville aeb7fa6
+	return res;
John W. Linville aeb7fa6
 }
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
 
John W. Linville aeb7fa6
-- 
John W. Linville aeb7fa6
2.7.4
John W. Linville aeb7fa6