hostapd: SAE/EAP-pwd side-channel attack update

Fixes this security problem:
* SAE/EAP-pwd side-channel attack update
https://w1.fi/security/2019-6/sae-eap-pwd-side-channel-attack-update.txt

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
master
Hauke Mehrtens 5 years ago
parent 9f34bf51d6
commit 7bed9bf10f

@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=hostapd
PKG_RELEASE:=8
PKG_RELEASE:=9
PKG_SOURCE_URL:=http://w1.fi/hostap.git
PKG_SOURCE_PROTO:=git

@ -0,0 +1,40 @@
From 92e1b96c26a84e503847bdd22ebadf697c4031ad Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Sat, 13 Apr 2019 17:20:57 +0300
Subject: EAP-pwd: Disallow ECC groups with a prime under 256 bits
Based on the SAE implementation guidance update to not allow ECC groups
with a prime that is under 256 bits, reject groups 25, 26, and 27 in
EAP-pwd.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/eap_common/eap_pwd_common.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -85,10 +85,23 @@ static int eap_pwd_kdf(const u8 *key, si
}
+static int eap_pwd_suitable_group(u16 num)
+{
+ /* Do not allow ECC groups with prime under 256 bits based on guidance
+ * for the similar design in SAE. */
+ return num == 19 || num == 20 || num == 21 ||
+ num == 28 || num == 29 || num == 30;
+}
+
+
EAP_PWD_group * get_eap_pwd_group(u16 num)
{
EAP_PWD_group *grp;
+ if (!eap_pwd_suitable_group(num)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unsuitable group %u", num);
+ return NULL;
+ }
grp = os_zalloc(sizeof(EAP_PWD_group));
if (!grp)
return NULL;

@ -0,0 +1,54 @@
From db54db11aec763b6fc74715c36e0f9de0d65e206 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Mon, 8 Apr 2019 18:01:07 +0300
Subject: SAE: Reject unsuitable groups based on REVmd changes
The rules defining which DH groups are suitable for SAE use were
accepted into IEEE 802.11 REVmd based on this document:
https://mentor.ieee.org/802.11/dcn/19/11-19-0387-02-000m-addressing-some-sae-comments.docx
Enforce those rules in production builds of wpa_supplicant and hostapd.
CONFIG_TESTING_OPTIONS=y builds can still be used to select any o the
implemented groups to maintain testing coverage.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/common/sae.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -18,10 +18,33 @@
#include "sae.h"
+static int sae_suitable_group(int group)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ /* Allow all groups for testing purposes in non-production builds. */
+ return 1;
+#else /* CONFIG_TESTING_OPTIONS */
+ /* Enforce REVmd rules on which SAE groups are suitable for production
+ * purposes: FFC groups whose prime is >= 3072 bits and ECC groups
+ * defined over a prime field whose prime is >= 256 bits. Furthermore,
+ * ECC groups defined over a characteristic 2 finite field and ECC
+ * groups with a co-factor greater than 1 are not suitable. */
+ return group == 19 || group == 20 || group == 21 ||
+ group == 28 || group == 29 || group == 30 ||
+ group == 15 || group == 16 || group == 17 || group == 18;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
int sae_set_group(struct sae_data *sae, int group)
{
struct sae_temporary_data *tmp;
+ if (!sae_suitable_group(group)) {
+ wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group);
+ return -1;
+ }
+
sae_clear_data(sae);
tmp = sae->tmp = os_zalloc(sizeof(*tmp));
if (tmp == NULL)

@ -0,0 +1,26 @@
From e43f08991f00820c1f711ca254021d5f83b5cd7d Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Thu, 25 Apr 2019 18:52:34 +0300
Subject: [PATCH 1/6] SAE: Use const_time_memcmp() for pwd_value >= prime
comparison
This reduces timing and memory access pattern differences for an
operation that could depend on the used password.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
(cherry picked from commit 8e14b030e558d23f65d761895c07089404e61cf1)
---
src/common/sae.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -317,7 +317,7 @@ static int sae_test_pwd_seed_ecc(struct
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
pwd_value, sae->tmp->prime_len);
- if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
+ if (const_time_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
return 0;
x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);

@ -0,0 +1,65 @@
From 20d7bd83c43fb24c4cf84d3045254d3ee1957166 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Thu, 25 Apr 2019 19:07:05 +0300
Subject: [PATCH 2/6] EAP-pwd: Use const_time_memcmp() for pwd_value >= prime
comparison
This reduces timing and memory access pattern differences for an
operation that could depend on the used password.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
(cherry picked from commit 7958223fdcfe82479e6ed71019a84f6d4cbf799c)
---
src/eap_common/eap_pwd_common.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -144,6 +144,7 @@ int compute_password_element(EAP_PWD_gro
u8 qnr_bin[MAX_ECC_PRIME_LEN];
u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
u8 x_bin[MAX_ECC_PRIME_LEN];
+ u8 prime_bin[MAX_ECC_PRIME_LEN];
struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
struct crypto_hash *hash;
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
@@ -161,6 +162,11 @@ int compute_password_element(EAP_PWD_gro
os_memset(x_bin, 0, sizeof(x_bin));
prime = crypto_ec_get_prime(grp->group);
+ primebitlen = crypto_ec_prime_len_bits(grp->group);
+ primebytelen = crypto_ec_prime_len(grp->group);
+ if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin),
+ primebytelen) < 0)
+ return -1;
cofactor = crypto_bignum_init();
grp->pwe = crypto_ec_point_init(grp->group);
tmp1 = crypto_bignum_init();
@@ -176,8 +182,6 @@ int compute_password_element(EAP_PWD_gro
"curve");
goto fail;
}
- primebitlen = crypto_ec_prime_len_bits(grp->group);
- primebytelen = crypto_ec_prime_len(grp->group);
if ((prfbuf = os_malloc(primebytelen)) == NULL) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
"buffer");
@@ -243,6 +247,8 @@ int compute_password_element(EAP_PWD_gro
if (primebitlen % 8)
buf_shift_right(prfbuf, primebytelen,
8 - primebitlen % 8);
+ if (const_time_memcmp(prfbuf, prime_bin, primebytelen) >= 0)
+ continue;
crypto_bignum_deinit(x_candidate, 1);
x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
@@ -252,9 +258,6 @@ int compute_password_element(EAP_PWD_gro
goto fail;
}
- if (crypto_bignum_cmp(x_candidate, prime) >= 0)
- continue;
-
wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate",
prfbuf, primebytelen);
const_time_select_bin(found, x_bin, prfbuf, primebytelen,

@ -0,0 +1,61 @@
From ee34d8cfbd0fbf7ba7429531d4bee1c43b074d8b Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Thu, 25 Apr 2019 19:23:05 +0300
Subject: [PATCH 3/6] OpenSSL: Use BN_bn2binpad() or BN_bn2bin_padded() if
available
This converts crypto_bignum_to_bin() to use the OpenSSL/BoringSSL
functions BN_bn2binpad()/BN_bn2bin_padded(), when available, to avoid
differences in runtime and memory access patterns depending on the
leading bytes of the BIGNUM value.
OpenSSL 1.0.2 and LibreSSL do not include such functions, so those cases
are still using the previous implementation where the BN_num_bytes()
call may result in different memory access pattern.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
(cherry picked from commit 1e237903f5b5d3117342daf006c5878cdb45e3d3)
---
src/crypto/crypto_openssl.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1227,7 +1227,13 @@ void crypto_bignum_deinit(struct crypto_
int crypto_bignum_to_bin(const struct crypto_bignum *a,
u8 *buf, size_t buflen, size_t padlen)
{
+#ifdef OPENSSL_IS_BORINGSSL
+#else /* OPENSSL_IS_BORINGSSL */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#else
int num_bytes, offset;
+#endif
+#endif /* OPENSSL_IS_BORINGSSL */
if (TEST_FAIL())
return -1;
@@ -1235,6 +1241,14 @@ int crypto_bignum_to_bin(const struct cr
if (padlen > buflen)
return -1;
+#ifdef OPENSSL_IS_BORINGSSL
+ if (BN_bn2bin_padded(buf, padlen, (const BIGNUM *) a) == 0)
+ return -1;
+ return padlen;
+#else /* OPENSSL_IS_BORINGSSL */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+ return BN_bn2binpad((const BIGNUM *) a, buf, padlen);
+#else
num_bytes = BN_num_bytes((const BIGNUM *) a);
if ((size_t) num_bytes > buflen)
return -1;
@@ -1247,6 +1261,8 @@ int crypto_bignum_to_bin(const struct cr
BN_bn2bin((const BIGNUM *) a, buf + offset);
return num_bytes + offset;
+#endif
+#endif /* OPENSSL_IS_BORINGSSL */
}

@ -0,0 +1,54 @@
From a25b48118d75f3c2d7cb1b2c3b4cffb13091a34c Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Mon, 24 Jun 2019 23:01:06 +0300
Subject: [PATCH 4/6] SAE: Run through prf result processing even if it >=
prime
This reduces differences in timing and memory access within the
hunting-and-pecking loop for ECC groups that have a prime that is not
close to a power of two (e.g., Brainpool curves).
Signed-off-by: Jouni Malinen <j@w1.fi>
(cherry picked from commit 147bf7b88a9c231322b5b574263071ca6dbb0503)
---
src/common/sae.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -304,6 +304,8 @@ static int sae_test_pwd_seed_ecc(struct
struct crypto_bignum *y_sqr, *x_cand;
int res;
size_t bits;
+ int cmp_prime;
+ unsigned int in_range;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
@@ -317,8 +319,13 @@ static int sae_test_pwd_seed_ecc(struct
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
pwd_value, sae->tmp->prime_len);
- if (const_time_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
- return 0;
+ cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len);
+ /* Create a const_time mask for selection based on prf result
+ * being smaller than prime. */
+ in_range = const_time_fill_msb((unsigned int) cmp_prime);
+ /* The algorithm description would skip the next steps if
+ * cmp_prime >= 0 (reutnr 0 here), but go through them regardless to
+ * minimize externally observable differences in behavior. */
x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
if (!x_cand)
@@ -330,7 +337,9 @@ static int sae_test_pwd_seed_ecc(struct
res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
crypto_bignum_deinit(y_sqr, 1);
- return res;
+ if (res < 0)
+ return res;
+ return const_time_select_int(in_range, res, 0);
}

@ -0,0 +1,52 @@
From 00a6cc73da61b03c146b6c341d0d1e572bcef432 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Mon, 24 Jun 2019 23:02:51 +0300
Subject: [PATCH 5/6] EAP-pwd: Run through prf result processing even if it >=
prime
This reduces differences in timing and memory access within the
hunting-and-pecking loop for ECC groups that have a prime that is not
close to a power of two (e.g., Brainpool curves).
Signed-off-by: Jouni Malinen <j@w1.fi>
(cherry picked from commit cd803299ca485eb857e37c88f973fccfbb8600e5)
---
src/eap_common/eap_pwd_common.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -155,6 +155,8 @@ int compute_password_element(EAP_PWD_gro
struct crypto_bignum *x_candidate = NULL, *cofactor = NULL;
const struct crypto_bignum *prime;
u8 mask, found_ctr = 0, is_odd = 0;
+ int cmp_prime;
+ unsigned int in_range;
if (grp->pwe)
return -1;
@@ -247,8 +249,13 @@ int compute_password_element(EAP_PWD_gro
if (primebitlen % 8)
buf_shift_right(prfbuf, primebytelen,
8 - primebitlen % 8);
- if (const_time_memcmp(prfbuf, prime_bin, primebytelen) >= 0)
- continue;
+ cmp_prime = const_time_memcmp(prfbuf, prime_bin, primebytelen);
+ /* Create a const_time mask for selection based on prf result
+ * being smaller than prime. */
+ in_range = const_time_fill_msb((unsigned int) cmp_prime);
+ /* The algorithm description would skip the next steps if
+ * cmp_prime >= 0, but go through them regardless to minimize
+ * externally observable differences in behavior. */
crypto_bignum_deinit(x_candidate, 1);
x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
@@ -311,7 +318,7 @@ int compute_password_element(EAP_PWD_gro
goto fail;
mask = const_time_eq(res, check);
found_ctr = const_time_select_u8(found, found_ctr, ctr);
- found |= mask;
+ found |= mask & in_range;
}
if (found == 0) {
wpa_printf(MSG_INFO,

@ -0,0 +1,44 @@
From 558518ed63202e5358116ab7e0afd5e85490f2ef Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Sat, 27 Jul 2019 23:19:17 +0300
Subject: [PATCH 6/6] dragonfly: Disable use of groups using Brainpool curves
Disable groups that use Brainpool curves for now since they leak more
timing information due to the prime not being close to a power of two.
This removes use of groups 28, 29, and 30 from SAE and EAP-pwd.
Signed-off-by: Jouni Malinen <j@w1.fi>
(cherry picked from commit 876c5eaa6dae1a87a17603fc489a44c29eedc2e3)
---
src/common/sae.c | 6 ++++--
src/eap_common/eap_pwd_common.c | 3 +--
2 files changed, 5 insertions(+), 4 deletions(-)
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -28,9 +28,11 @@ static int sae_suitable_group(int group)
* purposes: FFC groups whose prime is >= 3072 bits and ECC groups
* defined over a prime field whose prime is >= 256 bits. Furthermore,
* ECC groups defined over a characteristic 2 finite field and ECC
- * groups with a co-factor greater than 1 are not suitable. */
+ * groups with a co-factor greater than 1 are not suitable. Disable
+ * groups that use Brainpool curves as well for now since they leak more
+ * timing information due to the prime not being close to a power of
+ * two. */
return group == 19 || group == 20 || group == 21 ||
- group == 28 || group == 29 || group == 30 ||
group == 15 || group == 16 || group == 17 || group == 18;
#endif /* CONFIG_TESTING_OPTIONS */
}
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -89,8 +89,7 @@ static int eap_pwd_suitable_group(u16 nu
{
/* Do not allow ECC groups with prime under 256 bits based on guidance
* for the similar design in SAE. */
- return num == 19 || num == 20 || num == 21 ||
- num == 28 || num == 29 || num == 30;
+ return num == 19 || num == 20 || num == 21;
}
Loading…
Cancel
Save