include $(TOPDIR)/rules.mk
PKG_NAME:=hostapd
-PKG_RELEASE:=2
+PKG_RELEASE:=3
PKG_SOURCE_URL:=http://w1.fi/hostap.git
PKG_SOURCE_PROTO:=git
--- /dev/null
+From d42c477cc794163a3757956bbffca5cea000923c Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Tue, 26 Feb 2019 11:43:03 +0200
+Subject: [PATCH 01/14] OpenSSL: Use constant time operations for private
+ bignums
+
+This helps in reducing measurable timing differences in operations
+involving private information. BoringSSL has removed BN_FLG_CONSTTIME
+and expects specific constant time functions to be called instead, so a
+bit different approach is needed depending on which library is used.
+
+The main operation that needs protection against side channel attacks is
+BN_mod_exp() that depends on private keys (the public key validation
+step in crypto_dh_derive_secret() is an exception that can use the
+faster version since it does not depend on private keys).
+
+crypto_bignum_div() is currently used only in SAE FFC case with not
+safe-prime groups and only with values that do not depend on private
+keys, so it is not critical to protect it.
+
+crypto_bignum_inverse() is currently used only in SAE FFC PWE
+derivation. The additional protection here is targeting only OpenSSL.
+BoringSSL may need conversion to using BN_mod_inverse_blinded().
+
+This is related to CVE-2019-9494 and CVE-2019-9495.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/crypto/crypto_openssl.c | 20 +++++++++++++++-----
+ 1 file changed, 15 insertions(+), 5 deletions(-)
+
+--- a/src/crypto/crypto_openssl.c
++++ b/src/crypto/crypto_openssl.c
+@@ -549,7 +549,8 @@ int crypto_mod_exp(const u8 *base, size_
+ bn_result == NULL)
+ goto error;
+
+- if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
++ if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus,
++ ctx, NULL) != 1)
+ goto error;
+
+ *result_len = BN_bn2bin(bn_result, result);
+@@ -1295,8 +1296,9 @@ int crypto_bignum_exptmod(const struct c
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
+- res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+- (const BIGNUM *) c, bnctx);
++ res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a,
++ (const BIGNUM *) b, (const BIGNUM *) c,
++ bnctx, NULL);
+ BN_CTX_free(bnctx);
+
+ return res ? 0 : -1;
+@@ -1315,6 +1317,11 @@ int crypto_bignum_inverse(const struct c
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
++#ifdef OPENSSL_IS_BORINGSSL
++ /* TODO: use BN_mod_inverse_blinded() ? */
++#else /* OPENSSL_IS_BORINGSSL */
++ BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
++#endif /* OPENSSL_IS_BORINGSSL */
+ res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
+ (const BIGNUM *) b, bnctx);
+ BN_CTX_free(bnctx);
+@@ -1348,6 +1355,9 @@ int crypto_bignum_div(const struct crypt
+ bnctx = BN_CTX_new();
+ if (bnctx == NULL)
+ return -1;
++#ifndef OPENSSL_IS_BORINGSSL
++ BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
++#endif /* OPENSSL_IS_BORINGSSL */
+ res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
+ (const BIGNUM *) b, bnctx);
+ BN_CTX_free(bnctx);
+@@ -1439,8 +1449,8 @@ int crypto_bignum_legendre(const struct
+ /* exp = (p-1) / 2 */
+ !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
+ !BN_rshift1(exp, exp) ||
+- !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
+- bnctx))
++ !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp,
++ (const BIGNUM *) p, bnctx, NULL))
+ goto fail;
+
+ if (BN_is_word(tmp, 1))
--- /dev/null
+From 6e34f618d37ddbb5854c42e2ad4fca83492fa7b7 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Wed, 27 Feb 2019 18:38:30 +0200
+Subject: [PATCH 02/14] Add helper functions for constant time operations
+
+These functions can be used to help implement constant time operations
+for various cryptographic operations that must minimize externally
+observable differences in processing (both in timing and also in
+internal cache use, etc.).
+
+This is related to CVE-2019-9494 and CVE-2019-9495.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/utils/const_time.h | 191 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 191 insertions(+)
+ create mode 100644 src/utils/const_time.h
+
+--- /dev/null
++++ b/src/utils/const_time.h
+@@ -0,0 +1,191 @@
++/*
++ * Helper functions for constant time operations
++ * Copyright (c) 2019, The Linux Foundation
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ *
++ * These helper functions can be used to implement logic that needs to minimize
++ * externally visible differences in execution path by avoiding use of branches,
++ * avoiding early termination or other time differences, and forcing same memory
++ * access pattern regardless of values.
++ */
++
++#ifndef CONST_TIME_H
++#define CONST_TIME_H
++
++
++#if defined(__clang__)
++#define NO_UBSAN_UINT_OVERFLOW \
++ __attribute__((no_sanitize("unsigned-integer-overflow")))
++#else
++#define NO_UBSAN_UINT_OVERFLOW
++#endif
++
++
++/**
++ * const_time_fill_msb - Fill all bits with MSB value
++ * @val: Input value
++ * Returns: Value with all the bits set to the MSB of the input val
++ */
++static inline unsigned int const_time_fill_msb(unsigned int val)
++{
++ /* Move the MSB to LSB and multiple by -1 to fill in all bits. */
++ return (val >> (sizeof(val) * 8 - 1)) * ~0U;
++}
++
++
++/* Returns: -1 if val is zero; 0 if val is not zero */
++static inline unsigned int const_time_is_zero(unsigned int val)
++ NO_UBSAN_UINT_OVERFLOW
++{
++ /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */
++ return const_time_fill_msb(~val & (val - 1));
++}
++
++
++/* Returns: -1 if a == b; 0 if a != b */
++static inline unsigned int const_time_eq(unsigned int a, unsigned int b)
++{
++ return const_time_is_zero(a ^ b);
++}
++
++
++/* Returns: -1 if a == b; 0 if a != b */
++static inline u8 const_time_eq_u8(unsigned int a, unsigned int b)
++{
++ return (u8) const_time_eq(a, b);
++}
++
++
++/**
++ * const_time_eq_bin - Constant time memory comparison
++ * @a: First buffer to compare
++ * @b: Second buffer to compare
++ * @len: Number of octets to compare
++ * Returns: -1 if buffers are equal, 0 if not
++ *
++ * This function is meant for comparing passwords or hash values where
++ * difference in execution time or memory access pattern could provide external
++ * observer information about the location of the difference in the memory
++ * buffers. The return value does not behave like memcmp(), i.e.,
++ * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike
++ * memcmp(), the execution time of const_time_eq_bin() does not depend on the
++ * contents of the compared memory buffers, but only on the total compared
++ * length.
++ */
++static inline unsigned int const_time_eq_bin(const void *a, const void *b,
++ size_t len)
++{
++ const u8 *aa = a;
++ const u8 *bb = b;
++ size_t i;
++ u8 res = 0;
++
++ for (i = 0; i < len; i++)
++ res |= aa[i] ^ bb[i];
++
++ return const_time_is_zero(res);
++}
++
++
++/**
++ * const_time_select - Constant time unsigned int selection
++ * @mask: 0 (false) or -1 (true) to identify which value to select
++ * @true_val: Value to select for the true case
++ * @false_val: Value to select for the false case
++ * Returns: true_val if mask == -1, false_val if mask == 0
++ */
++static inline unsigned int const_time_select(unsigned int mask,
++ unsigned int true_val,
++ unsigned int false_val)
++{
++ return (mask & true_val) | (~mask & false_val);
++}
++
++
++/**
++ * const_time_select_int - Constant time int selection
++ * @mask: 0 (false) or -1 (true) to identify which value to select
++ * @true_val: Value to select for the true case
++ * @false_val: Value to select for the false case
++ * Returns: true_val if mask == -1, false_val if mask == 0
++ */
++static inline int const_time_select_int(unsigned int mask, int true_val,
++ int false_val)
++{
++ return (int) const_time_select(mask, (unsigned int) true_val,
++ (unsigned int) false_val);
++}
++
++
++/**
++ * const_time_select_u8 - Constant time u8 selection
++ * @mask: 0 (false) or -1 (true) to identify which value to select
++ * @true_val: Value to select for the true case
++ * @false_val: Value to select for the false case
++ * Returns: true_val if mask == -1, false_val if mask == 0
++ */
++static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val)
++{
++ return (u8) const_time_select(mask, true_val, false_val);
++}
++
++
++/**
++ * const_time_select_s8 - Constant time s8 selection
++ * @mask: 0 (false) or -1 (true) to identify which value to select
++ * @true_val: Value to select for the true case
++ * @false_val: Value to select for the false case
++ * Returns: true_val if mask == -1, false_val if mask == 0
++ */
++static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val)
++{
++ return (s8) const_time_select(mask, (unsigned int) true_val,
++ (unsigned int) false_val);
++}
++
++
++/**
++ * const_time_select_bin - Constant time binary buffer selection copy
++ * @mask: 0 (false) or -1 (true) to identify which value to copy
++ * @true_val: Buffer to copy for the true case
++ * @false_val: Buffer to copy for the false case
++ * @len: Number of octets to copy
++ * @dst: Destination buffer for the copy
++ *
++ * This function copies the specified buffer into the destination buffer using
++ * operations with identical memory access pattern regardless of which buffer
++ * is being copied.
++ */
++static inline void const_time_select_bin(u8 mask, const u8 *true_val,
++ const u8 *false_val, size_t len,
++ u8 *dst)
++{
++ size_t i;
++
++ for (i = 0; i < len; i++)
++ dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]);
++}
++
++
++static inline int const_time_memcmp(const void *a, const void *b, size_t len)
++{
++ const u8 *aa = a;
++ const u8 *bb = b;
++ int diff, res = 0;
++ unsigned int mask;
++
++ if (len == 0)
++ return 0;
++ do {
++ len--;
++ diff = (int) aa[len] - (int) bb[len];
++ mask = const_time_is_zero((unsigned int) diff);
++ res = const_time_select_int(mask, res, diff);
++ } while (len);
++
++ return res;
++}
++
++#endif /* CONST_TIME_H */
--- /dev/null
+From c93461c1d98f52681717a088776ab32fd97872b0 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Fri, 8 Mar 2019 00:24:12 +0200
+Subject: [PATCH 03/14] OpenSSL: Use constant time selection for
+ crypto_bignum_legendre()
+
+Get rid of the branches that depend on the result of the Legendre
+operation. This is needed to avoid leaking information about different
+temporary results in blinding mechanisms.
+
+This is related to CVE-2019-9494 and CVE-2019-9495.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/crypto/crypto_openssl.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/src/crypto/crypto_openssl.c
++++ b/src/crypto/crypto_openssl.c
+@@ -24,6 +24,7 @@
+ #endif /* CONFIG_ECC */
+
+ #include "common.h"
++#include "utils/const_time.h"
+ #include "wpabuf.h"
+ #include "dh_group5.h"
+ #include "sha1.h"
+@@ -1435,6 +1436,7 @@ int crypto_bignum_legendre(const struct
+ BN_CTX *bnctx;
+ BIGNUM *exp = NULL, *tmp = NULL;
+ int res = -2;
++ unsigned int mask;
+
+ if (TEST_FAIL())
+ return -2;
+@@ -1453,12 +1455,13 @@ int crypto_bignum_legendre(const struct
+ (const BIGNUM *) p, bnctx, NULL))
+ goto fail;
+
+- if (BN_is_word(tmp, 1))
+- res = 1;
+- else if (BN_is_zero(tmp))
+- res = 0;
+- else
+- res = -1;
++ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use
++ * constant time selection to avoid branches here. */
++ res = -1;
++ mask = const_time_eq(BN_is_word(tmp, 1), 1);
++ res = const_time_select_int(mask, 1, res);
++ mask = const_time_eq(BN_is_zero(tmp), 1);
++ res = const_time_select_int(mask, 0, res);
+
+ fail:
+ BN_clear_free(tmp);
--- /dev/null
+From 6513db3e96c43c2e36805cf5ead349765d18eaf7 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Tue, 26 Feb 2019 13:05:09 +0200
+Subject: [PATCH 05/14] SAE: Minimize timing differences in PWE derivation
+
+The QR test result can provide information about the password to an
+attacker, so try to minimize differences in how the
+sae_test_pwd_seed_ecc() result is used. (CVE-2019-9494)
+
+Use heap memory for the dummy password to allow the same password length
+to be used even with long passwords.
+
+Use constant time selection functions to track the real vs. dummy
+variables so that the exact same operations can be performed for both QR
+test results.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 106 ++++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 57 insertions(+), 49 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -9,6 +9,7 @@
+ #include "includes.h"
+
+ #include "common.h"
++#include "utils/const_time.h"
+ #include "crypto/crypto.h"
+ #include "crypto/sha256.h"
+ #include "crypto/random.h"
+@@ -269,15 +270,12 @@ static int sae_test_pwd_seed_ecc(struct
+ const u8 *prime,
+ const struct crypto_bignum *qr,
+ const struct crypto_bignum *qnr,
+- struct crypto_bignum **ret_x_cand)
++ u8 *pwd_value)
+ {
+- u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
+ struct crypto_bignum *y_sqr, *x_cand;
+ int res;
+ size_t bits;
+
+- *ret_x_cand = NULL;
+-
+ wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+ /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+@@ -286,7 +284,7 @@ static int sae_test_pwd_seed_ecc(struct
+ prime, sae->tmp->prime_len, pwd_value, bits) < 0)
+ return -1;
+ if (bits % 8)
+- buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
++ buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
+ wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
+ pwd_value, sae->tmp->prime_len);
+
+@@ -297,20 +295,13 @@ static int sae_test_pwd_seed_ecc(struct
+ if (!x_cand)
+ return -1;
+ y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
+- if (!y_sqr) {
+- crypto_bignum_deinit(x_cand, 1);
++ crypto_bignum_deinit(x_cand, 1);
++ if (!y_sqr)
+ return -1;
+- }
+
+ res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
+ crypto_bignum_deinit(y_sqr, 1);
+- if (res <= 0) {
+- crypto_bignum_deinit(x_cand, 1);
+- return res;
+- }
+-
+- *ret_x_cand = x_cand;
+- return 1;
++ return res;
+ }
+
+
+@@ -431,25 +422,30 @@ static int sae_derive_pwe_ecc(struct sae
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem;
+- u8 dummy_password[32];
+- size_t dummy_password_len;
++ u8 *dummy_password, *tmp_password;
+ int pwd_seed_odd = 0;
+ u8 prime[SAE_MAX_ECC_PRIME_LEN];
+ size_t prime_len;
+- struct crypto_bignum *x = NULL, *qr, *qnr;
++ struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
++ u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
++ u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
+ size_t bits;
+- int res;
+-
+- dummy_password_len = password_len;
+- if (dummy_password_len > sizeof(dummy_password))
+- dummy_password_len = sizeof(dummy_password);
+- if (random_get_bytes(dummy_password, dummy_password_len) < 0)
+- return -1;
++ int res = -1;
++ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
++ * mask */
++
++ os_memset(x_bin, 0, sizeof(x_bin));
++
++ dummy_password = os_malloc(password_len);
++ tmp_password = os_malloc(password_len);
++ if (!dummy_password || !tmp_password ||
++ random_get_bytes(dummy_password, password_len) < 0)
++ goto fail;
+
+ prime_len = sae->tmp->prime_len;
+ if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+ prime_len) < 0)
+- return -1;
++ goto fail;
+ bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+
+ /*
+@@ -458,7 +454,7 @@ static int sae_derive_pwe_ecc(struct sae
+ */
+ if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
+ &qr, &qnr) < 0)
+- return -1;
++ goto fail;
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+ password, password_len);
+@@ -474,7 +470,7 @@ static int sae_derive_pwe_ecc(struct sae
+ */
+ sae_pwd_seed_key(addr1, addr2, addrs);
+
+- addr[0] = password;
++ addr[0] = tmp_password;
+ len[0] = password_len;
+ num_elem = 1;
+ if (identifier) {
+@@ -491,9 +487,8 @@ static int sae_derive_pwe_ecc(struct sae
+ * attacks that attempt to determine the number of iterations required
+ * in the loop.
+ */
+- for (counter = 1; counter <= k || !x; counter++) {
++ for (counter = 1; counter <= k || !found; counter++) {
+ u8 pwd_seed[SHA256_MAC_LEN];
+- struct crypto_bignum *x_cand;
+
+ if (counter > 200) {
+ /* This should not happen in practice */
+@@ -501,40 +496,49 @@ static int sae_derive_pwe_ecc(struct sae
+ break;
+ }
+
+- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
++ wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
++ const_time_select_bin(found, dummy_password, password,
++ password_len, tmp_password);
+ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+ addr, len, pwd_seed) < 0)
+ break;
+
+ res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+- prime, qr, qnr, &x_cand);
++ prime, qr, qnr, x_cand_bin);
++ const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
++ x_bin);
++ pwd_seed_odd = const_time_select_u8(
++ found, pwd_seed_odd,
++ pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
++ os_memset(pwd_seed, 0, sizeof(pwd_seed));
+ if (res < 0)
+ goto fail;
+- if (res > 0 && !x) {
+- wpa_printf(MSG_DEBUG,
+- "SAE: Selected pwd-seed with counter %u",
+- counter);
+- x = x_cand;
+- pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
+- os_memset(pwd_seed, 0, sizeof(pwd_seed));
+-
+- /*
+- * Use a dummy password for the following rounds, if
+- * any.
+- */
+- addr[0] = dummy_password;
+- len[0] = dummy_password_len;
+- } else if (res > 0) {
+- crypto_bignum_deinit(x_cand, 1);
+- }
++ /* Need to minimize differences in handling res == 0 and 1 here
++ * to avoid differences in timing and instruction cache access,
++ * so use const_time_select_*() to make local copies of the
++ * values based on whether this loop iteration was the one that
++ * found the pwd-seed/x. */
++
++ /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
++ * (with res converted to 0/0xff) handles this in constant time.
++ */
++ found |= res * 0xff;
++ wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
++ res, found);
+ }
+
+- if (!x) {
++ if (!found) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+ res = -1;
+ goto fail;
+ }
+
++ x = crypto_bignum_init_set(x_bin, prime_len);
++ if (!x) {
++ res = -1;
++ goto fail;
++ }
++
+ if (!sae->tmp->pwe_ecc)
+ sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+ if (!sae->tmp->pwe_ecc)
+@@ -543,7 +547,6 @@ static int sae_derive_pwe_ecc(struct sae
+ res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
+ sae->tmp->pwe_ecc, x,
+ pwd_seed_odd);
+- crypto_bignum_deinit(x, 1);
+ if (res < 0) {
+ /*
+ * This should not happen since we already checked that there
+@@ -555,6 +558,11 @@ static int sae_derive_pwe_ecc(struct sae
+ fail:
+ crypto_bignum_deinit(qr, 0);
+ crypto_bignum_deinit(qnr, 0);
++ os_free(dummy_password);
++ bin_clear_free(tmp_password, password_len);
++ crypto_bignum_deinit(x, 1);
++ os_memset(x_bin, 0, sizeof(x_bin));
++ os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
+
+ return res;
+ }
--- /dev/null
+From 362704dda04507e7ebb8035122e83d9f0ae7c320 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Tue, 26 Feb 2019 19:34:38 +0200
+Subject: [PATCH 06/14] SAE: Avoid branches in is_quadratic_residue_blind()
+
+Make the non-failure path in the function proceed without branches based
+on r_odd and in constant time to minimize risk of observable differences
+in timing or cache use. (CVE-2019-9494)
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 64 ++++++++++++++++++++++++++++++++------------------------
+ 1 file changed, 37 insertions(+), 27 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -209,12 +209,14 @@ get_rand_1_to_p_1(const u8 *prime, size_
+
+ static int is_quadratic_residue_blind(struct sae_data *sae,
+ const u8 *prime, size_t bits,
+- const struct crypto_bignum *qr,
+- const struct crypto_bignum *qnr,
++ const u8 *qr, const u8 *qnr,
+ const struct crypto_bignum *y_sqr)
+ {
+- struct crypto_bignum *r, *num;
++ struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
+ int r_odd, check, res = -1;
++ u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN];
++ size_t prime_len = sae->tmp->prime_len;
++ unsigned int mask;
+
+ /*
+ * Use the blinding technique to mask y_sqr while determining
+@@ -225,7 +227,7 @@ static int is_quadratic_residue_blind(st
+ * r = a random number between 1 and p-1, inclusive
+ * num = (v * r * r) modulo p
+ */
+- r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
++ r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd);
+ if (!r)
+ return -1;
+
+@@ -235,41 +237,45 @@ static int is_quadratic_residue_blind(st
+ crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
+ goto fail;
+
+- if (r_odd) {
+- /*
+- * num = (num * qr) module p
+- * LGR(num, p) = 1 ==> quadratic residue
+- */
+- if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
+- goto fail;
+- check = 1;
+- } else {
+- /*
+- * num = (num * qnr) module p
+- * LGR(num, p) = -1 ==> quadratic residue
+- */
+- if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
+- goto fail;
+- check = -1;
+- }
++ /*
++ * Need to minimize differences in handling different cases, so try to
++ * avoid branches and timing differences.
++ *
++ * If r_odd:
++ * num = (num * qr) module p
++ * LGR(num, p) = 1 ==> quadratic residue
++ * else:
++ * num = (num * qnr) module p
++ * LGR(num, p) = -1 ==> quadratic residue
++ */
++ mask = const_time_is_zero(r_odd);
++ const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
++ qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
++ if (!qr_or_qnr ||
++ crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0)
++ goto fail;
++ /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */
++ check = const_time_select_int(mask, -1, 1);
+
+ res = crypto_bignum_legendre(num, sae->tmp->prime);
+ if (res == -2) {
+ res = -1;
+ goto fail;
+ }
+- res = res == check;
++ /* branchless version of res = res == check
++ * (res is -1, 0, or 1; check is -1 or 1) */
++ mask = const_time_eq(res, check);
++ res = const_time_select_int(mask, 1, 0);
+ fail:
+ crypto_bignum_deinit(num, 1);
+ crypto_bignum_deinit(r, 1);
++ crypto_bignum_deinit(qr_or_qnr, 1);
+ return res;
+ }
+
+
+ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
+- const u8 *prime,
+- const struct crypto_bignum *qr,
+- const struct crypto_bignum *qnr,
++ const u8 *prime, const u8 *qr, const u8 *qnr,
+ u8 *pwd_value)
+ {
+ struct crypto_bignum *y_sqr, *x_cand;
+@@ -429,6 +435,8 @@ static int sae_derive_pwe_ecc(struct sae
+ struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
+ u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
+ u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
++ u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
++ u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
+ size_t bits;
+ int res = -1;
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+@@ -453,7 +461,9 @@ static int sae_derive_pwe_ecc(struct sae
+ * (qnr) modulo p for blinding purposes during the loop.
+ */
+ if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
+- &qr, &qnr) < 0)
++ &qr, &qnr) < 0 ||
++ crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
++ crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
+ goto fail;
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+@@ -504,7 +514,7 @@ static int sae_derive_pwe_ecc(struct sae
+ break;
+
+ res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+- prime, qr, qnr, x_cand_bin);
++ prime, qr_bin, qnr_bin, x_cand_bin);
+ const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
+ x_bin);
+ pwd_seed_odd = const_time_select_u8(
--- /dev/null
+From 90839597cc4016b33f00055b12d59174c62770a3 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Sat, 2 Mar 2019 12:24:09 +0200
+Subject: [PATCH 07/14] SAE: Mask timing of MODP groups 22, 23, 24
+
+These groups have significant probability of coming up with pwd-value
+that is equal or greater than the prime and as such, need for going
+through the PWE derivation loop multiple times. This can result in
+sufficient timing different to allow an external observer to determine
+how many rounds are needed and that can leak information about the used
+password.
+
+Force at least 40 loop rounds for these MODP groups similarly to the ECC
+group design to mask timing. This behavior is not described in IEEE Std
+802.11-2016 for SAE, but it does not result in different values (i.e.,
+only different timing), so such implementation specific countermeasures
+can be done without breaking interoperability with other implementation.
+
+Note: These MODP groups 22, 23, and 24 are not considered sufficiently
+strong to be used with SAE (or more or less anything else). As such,
+they should never be enabled in runtime configuration for any production
+use cases. These changes to introduce additional protection to mask
+timing is only for completeness of implementation and not an indication
+that these groups should be used.
+
+This is related to CVE-2019-9494.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 38 ++++++++++++++++++++++++++++----------
+ 1 file changed, 28 insertions(+), 10 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -578,22 +578,27 @@ fail:
+ }
+
+
++static int sae_modp_group_require_masking(int group)
++{
++ /* Groups for which pwd-value is likely to be >= p frequently */
++ return group == 22 || group == 23 || group == 24;
++}
++
++
+ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
+ const u8 *addr2, const u8 *password,
+ size_t password_len, const char *identifier)
+ {
+- u8 counter;
++ u8 counter, k;
+ u8 addrs[2 * ETH_ALEN];
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem;
+ int found = 0;
++ struct crypto_bignum *pwe = NULL;
+
+- if (sae->tmp->pwe_ffc == NULL) {
+- sae->tmp->pwe_ffc = crypto_bignum_init();
+- if (sae->tmp->pwe_ffc == NULL)
+- return -1;
+- }
++ crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
++ sae->tmp->pwe_ffc = NULL;
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+ password, password_len);
+@@ -617,7 +622,9 @@ static int sae_derive_pwe_ffc(struct sae
+ len[num_elem] = sizeof(counter);
+ num_elem++;
+
+- for (counter = 1; !found; counter++) {
++ k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
++
++ for (counter = 1; counter <= k || !found; counter++) {
+ u8 pwd_seed[SHA256_MAC_LEN];
+ int res;
+
+@@ -627,19 +634,30 @@ static int sae_derive_pwe_ffc(struct sae
+ break;
+ }
+
+- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
++ wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
+ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+ addr, len, pwd_seed) < 0)
+ break;
+- res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
++ if (!pwe) {
++ pwe = crypto_bignum_init();
++ if (!pwe)
++ break;
++ }
++ res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
+ if (res < 0)
+ break;
+ if (res > 0) {
+- wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+ found = 1;
++ if (!sae->tmp->pwe_ffc) {
++ wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
++ sae->tmp->pwe_ffc = pwe;
++ pwe = NULL;
++ }
+ }
+ }
+
++ crypto_bignum_deinit(pwe, 1);
++
+ return found ? 0 : -1;
+ }
+
--- /dev/null
+From f8f20717f87eff1f025f48ed585c7684debacf72 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Sat, 2 Mar 2019 12:45:33 +0200
+Subject: [PATCH 08/14] SAE: Use const_time selection for PWE in FFC
+
+This is an initial step towards making the FFC case use strictly
+constant time operations similarly to the ECC case.
+sae_test_pwd_seed_ffc() does not yet have constant time behavior,
+though.
+
+This is related to CVE-2019-9494.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 53 +++++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 35 insertions(+), 18 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -589,17 +589,28 @@ static int sae_derive_pwe_ffc(struct sae
+ const u8 *addr2, const u8 *password,
+ size_t password_len, const char *identifier)
+ {
+- u8 counter, k;
++ u8 counter, k, sel_counter = 0;
+ u8 addrs[2 * ETH_ALEN];
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem;
+- int found = 0;
+- struct crypto_bignum *pwe = NULL;
++ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
++ * mask */
++ u8 mask;
++ struct crypto_bignum *pwe;
++ size_t prime_len = sae->tmp->prime_len * 8;
++ u8 *pwe_buf;
+
+ crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
+ sae->tmp->pwe_ffc = NULL;
+
++ /* Allocate a buffer to maintain selected and candidate PWE for constant
++ * time selection. */
++ pwe_buf = os_zalloc(prime_len * 2);
++ pwe = crypto_bignum_init();
++ if (!pwe_buf || !pwe)
++ goto fail;
++
+ wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+ password, password_len);
+
+@@ -638,27 +649,33 @@ static int sae_derive_pwe_ffc(struct sae
+ if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+ addr, len, pwd_seed) < 0)
+ break;
+- if (!pwe) {
+- pwe = crypto_bignum_init();
+- if (!pwe)
+- break;
+- }
+ res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
++ /* res is -1 for fatal failure, 0 if a valid PWE was not found,
++ * or 1 if a valid PWE was found. */
+ if (res < 0)
+ break;
+- if (res > 0) {
+- found = 1;
+- if (!sae->tmp->pwe_ffc) {
+- wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+- sae->tmp->pwe_ffc = pwe;
+- pwe = NULL;
+- }
+- }
++ /* Store the candidate PWE into the second half of pwe_buf and
++ * the selected PWE in the beginning of pwe_buf using constant
++ * time selection. */
++ if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
++ prime_len) < 0)
++ break;
++ const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
++ prime_len, pwe_buf);
++ sel_counter = const_time_select_u8(found, sel_counter, counter);
++ mask = const_time_eq_u8(res, 1);
++ found = const_time_select_u8(found, found, mask);
+ }
+
+- crypto_bignum_deinit(pwe, 1);
++ if (!found)
++ goto fail;
+
+- return found ? 0 : -1;
++ wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
++ sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
++fail:
++ crypto_bignum_deinit(pwe, 1);
++ bin_clear_free(pwe_buf, prime_len * 2);
++ return sae->tmp->pwe_ffc ? 0 : -1;
+ }
+
+
--- /dev/null
+From cff138b0747fa39765cbc641b66cfa5d7f1735d1 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Sat, 2 Mar 2019 16:05:56 +0200
+Subject: [PATCH 09/14] SAE: Use constant time operations in
+ sae_test_pwd_seed_ffc()
+
+Try to avoid showing externally visible timing or memory access
+differences regardless of whether the derived pwd-value is smaller than
+the group prime.
+
+This is related to CVE-2019-9494.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 75 ++++++++++++++++++++++++++++++++++----------------------
+ 1 file changed, 46 insertions(+), 29 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -311,14 +311,17 @@ static int sae_test_pwd_seed_ecc(struct
+ }
+
+
++/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
++ * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
+ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
+ struct crypto_bignum *pwe)
+ {
+ u8 pwd_value[SAE_MAX_PRIME_LEN];
+ size_t bits = sae->tmp->prime_len * 8;
+ u8 exp[1];
+- struct crypto_bignum *a, *b;
+- int res;
++ struct crypto_bignum *a, *b = NULL;
++ int res, is_val;
++ u8 pwd_value_valid;
+
+ wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+@@ -330,16 +333,29 @@ static int sae_test_pwd_seed_ffc(struct
+ wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
+ sae->tmp->prime_len);
+
+- if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
+- {
+- wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
+- return 0;
+- }
++ /* Check whether pwd-value < p */
++ res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
++ sae->tmp->prime_len);
++ /* pwd-value >= p is invalid, so res is < 0 for the valid cases and
++ * the negative sign can be used to fill the mask for constant time
++ * selection */
++ pwd_value_valid = const_time_fill_msb(res);
++
++ /* If pwd-value >= p, force pwd-value to be < p and perform the
++ * calculations anyway to hide timing difference. The derived PWE will
++ * be ignored in that case. */
++ pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
+
+ /* PWE = pwd-value^((p-1)/r) modulo p */
+
++ res = -1;
+ a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
++ if (!a)
++ goto fail;
+
++ /* This is an optimization based on the used group that does not depend
++ * on the password in any way, so it is fine to use separate branches
++ * for this step without constant time operations. */
+ if (sae->tmp->dh->safe_prime) {
+ /*
+ * r = (p-1)/2 for the group used here, so this becomes:
+@@ -353,33 +369,34 @@ static int sae_test_pwd_seed_ffc(struct
+ b = crypto_bignum_init_set(exp, sizeof(exp));
+ if (b == NULL ||
+ crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
+- crypto_bignum_div(b, sae->tmp->order, b) < 0) {
+- crypto_bignum_deinit(b, 0);
+- b = NULL;
+- }
++ crypto_bignum_div(b, sae->tmp->order, b) < 0)
++ goto fail;
+ }
+
+- if (a == NULL || b == NULL)
+- res = -1;
+- else
+- res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+-
+- crypto_bignum_deinit(a, 0);
+- crypto_bignum_deinit(b, 0);
+-
+- if (res < 0) {
+- wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
+- return -1;
+- }
+-
+- /* if (PWE > 1) --> found */
+- if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
+- wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
+- return 0;
+- }
++ if (!b)
++ goto fail;
+
+- wpa_printf(MSG_DEBUG, "SAE: PWE found");
+- return 1;
++ res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
++ if (res < 0)
++ goto fail;
++
++ /* There were no fatal errors in calculations, so determine the return
++ * value using constant time operations. We get here for number of
++ * invalid cases which are cleared here after having performed all the
++ * computation. PWE is valid if pwd-value was less than prime and
++ * PWE > 1. Start with pwd-value check first and then use constant time
++ * operations to clear res to 0 if PWE is 0 or 1.
++ */
++ res = const_time_select_u8(pwd_value_valid, 1, 0);
++ is_val = crypto_bignum_is_zero(pwe);
++ res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
++ is_val = crypto_bignum_is_one(pwe);
++ res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
++
++fail:
++ crypto_bignum_deinit(a, 1);
++ crypto_bignum_deinit(b, 1);
++ return res;
+ }
+
+