|
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 |
|