fix SAE and EAP_PWD vulnerabilities
CVE-2019-9494 (cache attack against SAE) CVE-2019-9495 (cache attack against EAP-pwd) CVE-2019-9496 (SAE confirm missing state validation in hostapd/AP) CVE-2019-9497 (EAP-pwd server not checking for reflection attack) CVE-2019-9498 (EAP-pwd server missing commit validation for scalar/element) CVE-2019-9499 (EAP-pwd peer missing commit validation for scalar/element) Signed-off-by: Davide Caratti <dcaratti@redhat.com>
This commit is contained in:
parent
ba7c0cc053
commit
f3c614c6af
@ -0,0 +1,93 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
|
||||||
|
index 9c2ba58..ac53cc8 100644
|
||||||
|
--- a/src/crypto/crypto_openssl.c
|
||||||
|
+++ b/src/crypto/crypto_openssl.c
|
||||||
|
@@ -607,7 +607,8 @@ int crypto_mod_exp(const u8 *base, size_t base_len,
|
||||||
|
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);
|
||||||
|
@@ -1360,8 +1361,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a,
|
||||||
|
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;
|
||||||
|
@@ -1380,6 +1382,11 @@ int crypto_bignum_inverse(const struct crypto_bignum *a,
|
||||||
|
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);
|
||||||
|
@@ -1413,6 +1420,9 @@ int crypto_bignum_div(const struct crypto_bignum *a,
|
||||||
|
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);
|
||||||
|
@@ -1504,8 +1514,8 @@ int crypto_bignum_legendre(const struct crypto_bignum *a,
|
||||||
|
/* 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))
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
343
0001-add-sanity-tests-for-standalone-wpa_supplicant.patch
Normal file
343
0001-add-sanity-tests-for-standalone-wpa_supplicant.patch
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
From c7e62303fb92f4608599a77ade315b9b5c9e161d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-Id: <c7e62303fb92f4608599a77ade315b9b5c9e161d.1553704253.git.dcaratti@redhat.com>
|
||||||
|
From: Davide Caratti <dcaratti@redhat.com>
|
||||||
|
Date: Tue, 29 Jan 2019 19:01:59 +0100
|
||||||
|
Subject: [PATCH] add sanity tests for standalone wpa_supplicant
|
||||||
|
|
||||||
|
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
|
||||||
|
---
|
||||||
|
tests/tests.yml | 13 ++
|
||||||
|
tests/wpa_supplicant_standalone/Makefile | 63 ++++++
|
||||||
|
tests/wpa_supplicant_standalone/PURPOSE | 3 +
|
||||||
|
tests/wpa_supplicant_standalone/runtest.sh | 219 +++++++++++++++++++++
|
||||||
|
4 files changed, 298 insertions(+)
|
||||||
|
create mode 100644 tests/tests.yml
|
||||||
|
create mode 100644 tests/wpa_supplicant_standalone/Makefile
|
||||||
|
create mode 100644 tests/wpa_supplicant_standalone/PURPOSE
|
||||||
|
create mode 100755 tests/wpa_supplicant_standalone/runtest.sh
|
||||||
|
|
||||||
|
diff --git a/tests/tests.yml b/tests/tests.yml
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..bab9514
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/tests.yml
|
||||||
|
@@ -0,0 +1,13 @@
|
||||||
|
+---
|
||||||
|
+- hosts: localhost
|
||||||
|
+ roles:
|
||||||
|
+ - role: standard-test-beakerlib
|
||||||
|
+ tags:
|
||||||
|
+ - classic
|
||||||
|
+ tests:
|
||||||
|
+ - wpa_supplicant_standalone
|
||||||
|
+ required_packages:
|
||||||
|
+ - wpa_supplicant
|
||||||
|
+ - iproute
|
||||||
|
+ - iw
|
||||||
|
+ - util-linux
|
||||||
|
diff --git a/tests/wpa_supplicant_standalone/Makefile b/tests/wpa_supplicant_standalone/Makefile
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..c4bfe53
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/wpa_supplicant_standalone/Makefile
|
||||||
|
@@ -0,0 +1,63 @@
|
||||||
|
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
+#
|
||||||
|
+# Makefile of /CoreOS/wpa_supplicant/Sanity/wpa_supplicant_standalone
|
||||||
|
+# Description: sanity test for wpa_supplicant
|
||||||
|
+# Author: Davide Caratti <dcaratti@redhat.com>
|
||||||
|
+#
|
||||||
|
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
+#
|
||||||
|
+# Copyright (c) 2019 Red Hat, Inc.
|
||||||
|
+#
|
||||||
|
+# This program is free software: you can redistribute it and/or
|
||||||
|
+# modify it under the terms of the GNU General Public License as
|
||||||
|
+# published by the Free Software Foundation, either version 2 of
|
||||||
|
+# the License, or (at your option) any later version.
|
||||||
|
+#
|
||||||
|
+# This program is distributed in the hope that it will be
|
||||||
|
+# useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
+# PURPOSE. See the GNU General Public License for more details.
|
||||||
|
+#
|
||||||
|
+# You should have received a copy of the GNU General Public License
|
||||||
|
+# along with this program. If not, see http://www.gnu.org/licenses/.
|
||||||
|
+#
|
||||||
|
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
+
|
||||||
|
+export TEST=/CoreOS/wpa_supplicant/Sanity/wpa_supplicant_standalone
|
||||||
|
+export TESTVERSION=1.0
|
||||||
|
+
|
||||||
|
+BUILT_FILES=
|
||||||
|
+
|
||||||
|
+FILES=$(METADATA) runtest.sh Makefile PURPOSE
|
||||||
|
+
|
||||||
|
+.PHONY: all install download clean
|
||||||
|
+
|
||||||
|
+run: $(FILES) build
|
||||||
|
+ ./runtest.sh
|
||||||
|
+
|
||||||
|
+build: $(BUILT_FILES)
|
||||||
|
+ test -x runtest.sh || chmod a+x runtest.sh
|
||||||
|
+
|
||||||
|
+clean:
|
||||||
|
+ rm -f *~ $(BUILT_FILES)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+include /usr/share/rhts/lib/rhts-make.include
|
||||||
|
+
|
||||||
|
+$(METADATA): Makefile
|
||||||
|
+ @echo "Owner: Davide Caratti <dcaratti@redhat.com>" > $(METADATA)
|
||||||
|
+ @echo "Name: $(TEST)" >> $(METADATA)
|
||||||
|
+ @echo "TestVersion: $(TESTVERSION)" >> $(METADATA)
|
||||||
|
+ @echo "Path: $(TEST_DIR)" >> $(METADATA)
|
||||||
|
+ @echo "Description: sanity test for wpa_supplicant" >> $(METADATA)
|
||||||
|
+ @echo "Type: Sanity" >> $(METADATA)
|
||||||
|
+ @echo "TestTime: 10m" >> $(METADATA)
|
||||||
|
+ @echo "RunFor: wpa_supplicant" >> $(METADATA)
|
||||||
|
+ @echo "Requires: util-linux iproute iw wpa_supplicant" >> $(METADATA)
|
||||||
|
+ @echo "Priority: Normal" >> $(METADATA)
|
||||||
|
+ @echo "License: GPLv2+" >> $(METADATA)
|
||||||
|
+ @echo "Confidential: no" >> $(METADATA)
|
||||||
|
+ @echo "Destructive: no" >> $(METADATA)
|
||||||
|
+ @echo "Releases: -RHEL4 -RHELClient5 -RHELServer5" >> $(METADATA)
|
||||||
|
+
|
||||||
|
+ rhts-lint $(METADATA)
|
||||||
|
diff --git a/tests/wpa_supplicant_standalone/PURPOSE b/tests/wpa_supplicant_standalone/PURPOSE
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..a183dc3
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/wpa_supplicant_standalone/PURPOSE
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+PURPOSE of /CoreOS/wpa_supplicant/Sanity/wpa_supplicant_standalone
|
||||||
|
+Description: sanity test for wpa_supplicant
|
||||||
|
+Author: Davide Caratti <dcaratti@redhat.com>
|
||||||
|
diff --git a/tests/wpa_supplicant_standalone/runtest.sh b/tests/wpa_supplicant_standalone/runtest.sh
|
||||||
|
new file mode 100755
|
||||||
|
index 0000000..16390d8
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/wpa_supplicant_standalone/runtest.sh
|
||||||
|
@@ -0,0 +1,219 @@
|
||||||
|
+#!/bin/bash
|
||||||
|
+# vim: dict+=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k
|
||||||
|
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
+#
|
||||||
|
+# runtest.sh of /CoreOS/wpa_supplicant/Sanity/wpa_supplicant_standalone
|
||||||
|
+# Description: sanity test for wpa_supplicant
|
||||||
|
+# Author: Davide Caratti <dcaratti@redhat.com>
|
||||||
|
+#
|
||||||
|
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
+#
|
||||||
|
+# Copyright (c) 2019 Red Hat, Inc.
|
||||||
|
+#
|
||||||
|
+# This program is free software: you can redistribute it and/or
|
||||||
|
+# modify it under the terms of the GNU General Public License as
|
||||||
|
+# published by the Free Software Foundation, either version 2 of
|
||||||
|
+# the License, or (at your option) any later version.
|
||||||
|
+#
|
||||||
|
+# This program is distributed in the hope that it will be
|
||||||
|
+# useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||||
|
+# PURPOSE. See the GNU General Public License for more details.
|
||||||
|
+#
|
||||||
|
+# You should have received a copy of the GNU General Public License
|
||||||
|
+# along with this program. If not, see http://www.gnu.org/licenses/.
|
||||||
|
+#
|
||||||
|
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
+
|
||||||
|
+# Include Beaker environment
|
||||||
|
+. /usr/bin/rhts-environment.sh || exit 1
|
||||||
|
+. /usr/share/beakerlib/beakerlib.sh || exit 1
|
||||||
|
+
|
||||||
|
+PACKAGE="wpa_supplicant"
|
||||||
|
+
|
||||||
|
+MACSTA="00:00:0a:bb:e1:1a"
|
||||||
|
+IFACEAP="wlan0"
|
||||||
|
+IFACESTA="wlan1"
|
||||||
|
+
|
||||||
|
+open_ap() {
|
||||||
|
+ local SSID=${1:-notreallyassid}
|
||||||
|
+
|
||||||
|
+ cat >openap.conf <<-EOF
|
||||||
|
+ network={
|
||||||
|
+ frequency=2412
|
||||||
|
+ ssid="$SSID"
|
||||||
|
+ mode=2
|
||||||
|
+ key_mgmt=NONE
|
||||||
|
+ }
|
||||||
|
+ EOF
|
||||||
|
+ wpa_supplicant -ddd -Dnl80211 -i$IFACEAP -copenap.conf -B -fopenap.log -Pw1ap.pid
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+open_sta() {
|
||||||
|
+ local SSID=${1:-notreallyassid}
|
||||||
|
+
|
||||||
|
+ cat >opensta.conf <<-EOF
|
||||||
|
+ network={
|
||||||
|
+ ssid="$SSID"
|
||||||
|
+ key_mgmt=NONE
|
||||||
|
+ }
|
||||||
|
+ EOF
|
||||||
|
+ wpa_supplicant -ddd -Dnl80211 -i$IFACESTA -copensta.conf -B -fopensta.log -Pw1sta.pid
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+wpa2psk_ap() {
|
||||||
|
+ local SSID=${1:-notreallyassid}
|
||||||
|
+
|
||||||
|
+ cat >wpapskap.conf <<-EOF
|
||||||
|
+ network={
|
||||||
|
+ frequency=2437
|
||||||
|
+ ssid="$SSID"
|
||||||
|
+ mode=2
|
||||||
|
+ key_mgmt=WPA-PSK
|
||||||
|
+ pairwise=CCMP
|
||||||
|
+ group=CCMP
|
||||||
|
+ psk="hunter2?"
|
||||||
|
+ }
|
||||||
|
+ EOF
|
||||||
|
+ wpa_supplicant -ddd -Dnl80211 -i$IFACEAP -cwpapskap.conf -B -fwpapskap.log -Pw2ap.pid
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+wpa2psk_sta() {
|
||||||
|
+ local SSID=${1:-notreallyassid}
|
||||||
|
+
|
||||||
|
+ cat >wpapsksta.conf <<-EOF
|
||||||
|
+ network={
|
||||||
|
+ frequency=2437
|
||||||
|
+ ssid="$SSID"
|
||||||
|
+ proto=WPA
|
||||||
|
+ key_mgmt=WPA-PSK
|
||||||
|
+ pairwise=CCMP
|
||||||
|
+ group=CCMP
|
||||||
|
+ psk="hunter2?"
|
||||||
|
+ }
|
||||||
|
+ EOF
|
||||||
|
+ wpa_supplicant -ddd -Dnl80211 -i$IFACESTA -cwpapsksta.conf -B -fwpapsksta.log -Pw2sta.pid
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+kill_supplicants() {
|
||||||
|
+ local a=`cat w*.pid`
|
||||||
|
+ local iter=0
|
||||||
|
+
|
||||||
|
+ while [ ${#a} -gt 0 -a $iter -lt 10 ]; do
|
||||||
|
+ for a in $a; do
|
||||||
|
+ kill $a 1>/dev/null 2>&1
|
||||||
|
+ sleep 1
|
||||||
|
+ done
|
||||||
|
+ a=`cat w*.pid`
|
||||||
|
+ iter=$((iter+1))
|
||||||
|
+ done
|
||||||
|
+
|
||||||
|
+ ip link set dev $IFACEAP down
|
||||||
|
+ ip link set dev $IFACESTA down
|
||||||
|
+
|
||||||
|
+ if [ $iter -ge 10 -a ${#a} -gt 0 ]; then
|
||||||
|
+ return 1
|
||||||
|
+ else
|
||||||
|
+ return 0
|
||||||
|
+ fi
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+check_for_associated_sta()
|
||||||
|
+{
|
||||||
|
+
|
||||||
|
+ local assoc_found=0 assoc_missed=0
|
||||||
|
+
|
||||||
|
+ ip link set dev $IFACEAP up
|
||||||
|
+ while sleep 2; do
|
||||||
|
+ if iw dev $IFACEAP station dump | grep -i $MACSTA ; then
|
||||||
|
+ assoc_found=$((assoc_found+1))
|
||||||
|
+ rlLog "found $MACSTA in $IFACEAP associations ($assoc_found)"
|
||||||
|
+ else
|
||||||
|
+ if [ $assoc_found -gt 0 ]; then
|
||||||
|
+ rlLog "association disappeared after $assoc_found cycles"
|
||||||
|
+ return 1
|
||||||
|
+ fi
|
||||||
|
+ rlLog "didn't find association ($assoc_missed)"
|
||||||
|
+ assoc_missed=$((assoc_missed+1))
|
||||||
|
+ fi
|
||||||
|
+ if [ $assoc_missed -gt 5 ]; then
|
||||||
|
+ rlLog "timeout waiting for $MACSTA in $IFACEAP station dump"
|
||||||
|
+ return 1
|
||||||
|
+ fi
|
||||||
|
+ if [ $assoc_found -gt 5 ]; then
|
||||||
|
+ return 0
|
||||||
|
+ fi
|
||||||
|
+ done
|
||||||
|
+ rlLog "sleep failed!"
|
||||||
|
+ return 1
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+check_for_running_aps()
|
||||||
|
+{
|
||||||
|
+ local probe_ok=0 probe_missed=0
|
||||||
|
+
|
||||||
|
+ ip link set dev $IFACESTA up
|
||||||
|
+ while sleep 1; do
|
||||||
|
+ if iw dev $IFACESTA scan | grep "${1:-notreallyassid}"; then
|
||||||
|
+ probe_ok=$((probe_ok+1))
|
||||||
|
+ rlLog "$probe_ok probe received"
|
||||||
|
+ else
|
||||||
|
+ if [ $probe_ok -gt 0 ]; then
|
||||||
|
+ rlLog "probe failure after $probe_ok attempts"
|
||||||
|
+ return 1
|
||||||
|
+ fi
|
||||||
|
+ rlLog "missed probe response"
|
||||||
|
+ probe_missed=$((probe_missed+1))
|
||||||
|
+ fi
|
||||||
|
+ if [ $probe_missed -gt 5 ]; then
|
||||||
|
+ rlLog "timeout waiting for beacons"
|
||||||
|
+ return 1
|
||||||
|
+ fi
|
||||||
|
+ if [ $probe_ok -gt 5 ]; then
|
||||||
|
+ return 0
|
||||||
|
+ fi
|
||||||
|
+ done
|
||||||
|
+ rlLog "sleep failed!"
|
||||||
|
+ return 1
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+rlJournalStart
|
||||||
|
+ rlPhaseStartSetup
|
||||||
|
+ rlAssertRpm $PACKAGE
|
||||||
|
+ # avoid randomizing MAC for wlan0 and wlan1
|
||||||
|
+ rlRun "systemctl stop NetworkManager"
|
||||||
|
+ # allow scans
|
||||||
|
+ rlRun "systemctl stop wpa_supplicant"
|
||||||
|
+ rlRun "rfkill unblock wifi"
|
||||||
|
+ rlRun "modprobe mac80211_hwsim radio=2"
|
||||||
|
+ rlRun "ip link set dev $IFACESTA address $MACSTA"
|
||||||
|
+ rlRun "TmpDir=\$(mktemp -d)" 0 "Creating tmp directory"
|
||||||
|
+ rlRun "pushd $TmpDir"
|
||||||
|
+ rlPhaseEnd
|
||||||
|
+
|
||||||
|
+ rlPhaseStartTest
|
||||||
|
+ # cleartext wifi
|
||||||
|
+ rlRun "kill_supplicants"
|
||||||
|
+ rlRun "open_ap test_OPEN"
|
||||||
|
+ rlRun "check_for_running_aps test_OPEN"
|
||||||
|
+ rlRun "open_sta test_OPEN"
|
||||||
|
+ rlRun "check_for_associated_sta test_OPEN"
|
||||||
|
+
|
||||||
|
+ # WPA2 personal
|
||||||
|
+ rlRun "kill_supplicants"
|
||||||
|
+ rlRun "wpa2psk_ap test_WPAPSK"
|
||||||
|
+ rlRun "check_for_running_aps test_WPAPSK"
|
||||||
|
+ rlRun "wpa2psk_sta test_WPAPSK"
|
||||||
|
+ rlRun "check_for_associated_sta test_WPAPSK"
|
||||||
|
+ rlPhaseEnd
|
||||||
|
+
|
||||||
|
+ rlPhaseStartCleanup
|
||||||
|
+ rlRun kill_supplicants
|
||||||
|
+ rlRun "popd"
|
||||||
|
+ rlRun "rm -r $TmpDir" 0 "Removing tmp directory"
|
||||||
|
+ rlRun "modprobe -r mac80211_hwsim"
|
||||||
|
+ rlRun "systemctl restart wpa_supplicant"
|
||||||
|
+ rlRun "systemctl restart NetworkManager"
|
||||||
|
+ rlPhaseEnd
|
||||||
|
+ rlJournalPrintText
|
||||||
|
+rlJournalEnd
|
||||||
|
--
|
||||||
|
2.20.1
|
||||||
|
|
218
0002-Add-helper-functions-for-constant-time-operations.patch
Normal file
218
0002-Add-helper-functions-for-constant-time-operations.patch
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
diff --git a/src/utils/const_time.h b/src/utils/const_time.h
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..ab8f611
|
||||||
|
--- /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 */
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
@ -0,0 +1,60 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
|
||||||
|
index ac53cc8..0f52101 100644
|
||||||
|
--- 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"
|
||||||
|
@@ -1500,6 +1501,7 @@ int crypto_bignum_legendre(const struct crypto_bignum *a,
|
||||||
|
BN_CTX *bnctx;
|
||||||
|
BIGNUM *exp = NULL, *tmp = NULL;
|
||||||
|
int res = -2;
|
||||||
|
+ unsigned int mask;
|
||||||
|
|
||||||
|
if (TEST_FAIL())
|
||||||
|
return -2;
|
||||||
|
@@ -1518,12 +1520,13 @@ int crypto_bignum_legendre(const struct crypto_bignum *a,
|
||||||
|
(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);
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
324
0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch
Normal file
324
0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
From aaf65feac67c3993935634eefe5bc76b9fce03aa Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jouni Malinen <jouni@codeaurora.org>
|
||||||
|
Date: Tue, 26 Feb 2019 11:59:45 +0200
|
||||||
|
Subject: [PATCH 04/14] EAP-pwd: Use constant time and memory access for
|
||||||
|
finding the PWE
|
||||||
|
|
||||||
|
This algorithm could leak information to external observers in form of
|
||||||
|
timing differences or memory access patterns (cache use). While the
|
||||||
|
previous implementation had protection against the most visible timing
|
||||||
|
differences (looping 40 rounds and masking the legendre operation), it
|
||||||
|
did not protect against memory access patterns between the two possible
|
||||||
|
code paths in the masking operations. That might be sufficient to allow
|
||||||
|
an unprivileged process running on the same device to be able to
|
||||||
|
determine which path is being executed through a cache attack and based
|
||||||
|
on that, determine information about the used password.
|
||||||
|
|
||||||
|
Convert the PWE finding loop to use constant time functions and
|
||||||
|
identical memory access path without different branches for the QR/QNR
|
||||||
|
cases to minimize possible side-channel information similarly to the
|
||||||
|
changes done for SAE authentication. (CVE-2019-9495)
|
||||||
|
|
||||||
|
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||||
|
---
|
||||||
|
src/eap_common/eap_pwd_common.c | 187 +++++++++++++++++++++-------------------
|
||||||
|
1 file changed, 99 insertions(+), 88 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
|
||||||
|
index 02fe01e..e49aaf8 100644
|
||||||
|
--- a/src/eap_common/eap_pwd_common.c
|
||||||
|
+++ b/src/eap_common/eap_pwd_common.c
|
||||||
|
@@ -8,11 +8,15 @@
|
||||||
|
|
||||||
|
#include "includes.h"
|
||||||
|
#include "common.h"
|
||||||
|
+#include "utils/const_time.h"
|
||||||
|
#include "crypto/sha256.h"
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
#include "eap_defs.h"
|
||||||
|
#include "eap_pwd_common.h"
|
||||||
|
|
||||||
|
+#define MAX_ECC_PRIME_LEN 66
|
||||||
|
+
|
||||||
|
+
|
||||||
|
/* The random function H(x) = HMAC-SHA256(0^32, x) */
|
||||||
|
struct crypto_hash * eap_pwd_h_init(void)
|
||||||
|
{
|
||||||
|
@@ -102,6 +106,15 @@ EAP_PWD_group * get_eap_pwd_group(u16 num)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+static void buf_shift_right(u8 *buf, size_t len, size_t bits)
|
||||||
|
+{
|
||||||
|
+ size_t i;
|
||||||
|
+ for (i = len - 1; i > 0; i--)
|
||||||
|
+ buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
|
||||||
|
+ buf[0] >>= bits;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* compute a "random" secret point on an elliptic curve based
|
||||||
|
* on the password and identities.
|
||||||
|
@@ -113,17 +126,27 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
const u8 *token)
|
||||||
|
{
|
||||||
|
struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
|
||||||
|
+ struct crypto_bignum *qr_or_qnr = NULL;
|
||||||
|
+ u8 qr_bin[MAX_ECC_PRIME_LEN];
|
||||||
|
+ u8 qnr_bin[MAX_ECC_PRIME_LEN];
|
||||||
|
+ u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
|
||||||
|
+ u8 x_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;
|
||||||
|
- int is_odd, ret = 0, check, found = 0;
|
||||||
|
- size_t primebytelen, primebitlen;
|
||||||
|
- struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
|
||||||
|
+ int ret = 0, check, res;
|
||||||
|
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
|
||||||
|
+ * mask */
|
||||||
|
+ size_t primebytelen = 0, primebitlen;
|
||||||
|
+ struct crypto_bignum *x_candidate = NULL, *cofactor = NULL;
|
||||||
|
const struct crypto_bignum *prime;
|
||||||
|
+ u8 mask, found_ctr = 0, is_odd = 0;
|
||||||
|
|
||||||
|
if (grp->pwe)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
+ os_memset(x_bin, 0, sizeof(x_bin));
|
||||||
|
+
|
||||||
|
prime = crypto_ec_get_prime(grp->group);
|
||||||
|
cofactor = crypto_bignum_init();
|
||||||
|
grp->pwe = crypto_ec_point_init(grp->group);
|
||||||
|
@@ -152,8 +175,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
|
||||||
|
/* get a random quadratic residue and nonresidue */
|
||||||
|
while (!qr || !qnr) {
|
||||||
|
- int res;
|
||||||
|
-
|
||||||
|
if (crypto_bignum_rand(tmp1, prime) < 0)
|
||||||
|
goto fail;
|
||||||
|
res = crypto_bignum_legendre(tmp1, prime);
|
||||||
|
@@ -167,6 +188,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
if (!tmp1)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
+ if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin),
|
||||||
|
+ primebytelen) < 0 ||
|
||||||
|
+ crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin),
|
||||||
|
+ primebytelen) < 0)
|
||||||
|
+ goto fail;
|
||||||
|
|
||||||
|
os_memset(prfbuf, 0, primebytelen);
|
||||||
|
ctr = 0;
|
||||||
|
@@ -194,17 +220,16 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
eap_pwd_h_update(hash, &ctr, sizeof(ctr));
|
||||||
|
eap_pwd_h_final(hash, pwe_digest);
|
||||||
|
|
||||||
|
- crypto_bignum_deinit(rnd, 1);
|
||||||
|
- rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN);
|
||||||
|
- if (!rnd) {
|
||||||
|
- wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd");
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
+ is_odd = const_time_select_u8(
|
||||||
|
+ found, is_odd, pwe_digest[SHA256_MAC_LEN - 1] & 0x01);
|
||||||
|
if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
|
||||||
|
(u8 *) "EAP-pwd Hunting And Pecking",
|
||||||
|
os_strlen("EAP-pwd Hunting And Pecking"),
|
||||||
|
prfbuf, primebitlen) < 0)
|
||||||
|
goto fail;
|
||||||
|
+ if (primebitlen % 8)
|
||||||
|
+ buf_shift_right(prfbuf, primebytelen,
|
||||||
|
+ 8 - primebitlen % 8);
|
||||||
|
|
||||||
|
crypto_bignum_deinit(x_candidate, 1);
|
||||||
|
x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
|
||||||
|
@@ -214,24 +239,13 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /*
|
||||||
|
- * eap_pwd_kdf() returns a string of bits 0..primebitlen but
|
||||||
|
- * BN_bin2bn will treat that string of bits as a big endian
|
||||||
|
- * number. If the primebitlen is not an even multiple of 8
|
||||||
|
- * then excessive bits-- those _after_ primebitlen-- so now
|
||||||
|
- * we have to shift right the amount we masked off.
|
||||||
|
- */
|
||||||
|
- if ((primebitlen % 8) &&
|
||||||
|
- crypto_bignum_rshift(x_candidate,
|
||||||
|
- (8 - (primebitlen % 8)),
|
||||||
|
- x_candidate) < 0)
|
||||||
|
- goto fail;
|
||||||
|
-
|
||||||
|
if (crypto_bignum_cmp(x_candidate, prime) >= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
- wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
|
||||||
|
- prfbuf, primebytelen);
|
||||||
|
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate",
|
||||||
|
+ prfbuf, primebytelen);
|
||||||
|
+ const_time_select_bin(found, x_bin, prfbuf, primebytelen,
|
||||||
|
+ x_bin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* compute y^2 using the equation of the curve
|
||||||
|
@@ -261,13 +275,15 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
* Flip a coin, multiply by the random quadratic residue or the
|
||||||
|
* random quadratic nonresidue and record heads or tails.
|
||||||
|
*/
|
||||||
|
- if (crypto_bignum_is_odd(tmp1)) {
|
||||||
|
- crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
|
||||||
|
- check = 1;
|
||||||
|
- } else {
|
||||||
|
- crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
|
||||||
|
- check = -1;
|
||||||
|
- }
|
||||||
|
+ mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1);
|
||||||
|
+ check = const_time_select_s8(mask, 1, -1);
|
||||||
|
+ const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen,
|
||||||
|
+ qr_or_qnr_bin);
|
||||||
|
+ crypto_bignum_deinit(qr_or_qnr, 1);
|
||||||
|
+ qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen);
|
||||||
|
+ if (!qr_or_qnr ||
|
||||||
|
+ crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0)
|
||||||
|
+ goto fail;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now it's safe to do legendre, if check is 1 then it's
|
||||||
|
@@ -275,59 +291,12 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
* change result), if check is -1 then it's the opposite test
|
||||||
|
* (multiplying a qr by qnr would make a qnr).
|
||||||
|
*/
|
||||||
|
- if (crypto_bignum_legendre(tmp2, prime) == check) {
|
||||||
|
- if (found == 1)
|
||||||
|
- continue;
|
||||||
|
-
|
||||||
|
- /* need to unambiguously identify the solution */
|
||||||
|
- is_odd = crypto_bignum_is_odd(rnd);
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * We know x_candidate is a quadratic residue so set
|
||||||
|
- * it here.
|
||||||
|
- */
|
||||||
|
- if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
|
||||||
|
- x_candidate,
|
||||||
|
- is_odd) != 0) {
|
||||||
|
- wpa_printf(MSG_INFO,
|
||||||
|
- "EAP-pwd: Could not solve for y");
|
||||||
|
- continue;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * If there's a solution to the equation then the point
|
||||||
|
- * must be on the curve so why check again explicitly?
|
||||||
|
- * OpenSSL code says this is required by X9.62. We're
|
||||||
|
- * not X9.62 but it can't hurt just to be sure.
|
||||||
|
- */
|
||||||
|
- if (!crypto_ec_point_is_on_curve(grp->group,
|
||||||
|
- grp->pwe)) {
|
||||||
|
- wpa_printf(MSG_INFO,
|
||||||
|
- "EAP-pwd: point is not on curve");
|
||||||
|
- continue;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (!crypto_bignum_is_one(cofactor)) {
|
||||||
|
- /* make sure the point is not in a small
|
||||||
|
- * sub-group */
|
||||||
|
- if (crypto_ec_point_mul(grp->group, grp->pwe,
|
||||||
|
- cofactor,
|
||||||
|
- grp->pwe) != 0) {
|
||||||
|
- wpa_printf(MSG_INFO,
|
||||||
|
- "EAP-pwd: cannot multiply generator by order");
|
||||||
|
- continue;
|
||||||
|
- }
|
||||||
|
- if (crypto_ec_point_is_at_infinity(grp->group,
|
||||||
|
- grp->pwe)) {
|
||||||
|
- wpa_printf(MSG_INFO,
|
||||||
|
- "EAP-pwd: point is at infinity");
|
||||||
|
- continue;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- wpa_printf(MSG_DEBUG,
|
||||||
|
- "EAP-pwd: found a PWE in %d tries", ctr);
|
||||||
|
- found = 1;
|
||||||
|
- }
|
||||||
|
+ res = crypto_bignum_legendre(tmp2, prime);
|
||||||
|
+ if (res == -2)
|
||||||
|
+ goto fail;
|
||||||
|
+ mask = const_time_eq(res, check);
|
||||||
|
+ found_ctr = const_time_select_u8(found, found_ctr, ctr);
|
||||||
|
+ found |= mask;
|
||||||
|
}
|
||||||
|
if (found == 0) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
@@ -335,6 +304,44 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
num);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * We know x_candidate is a quadratic residue so set it here.
|
||||||
|
+ */
|
||||||
|
+ crypto_bignum_deinit(x_candidate, 1);
|
||||||
|
+ x_candidate = crypto_bignum_init_set(x_bin, primebytelen);
|
||||||
|
+ if (!x_candidate ||
|
||||||
|
+ crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate,
|
||||||
|
+ is_odd) != 0) {
|
||||||
|
+ wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * If there's a solution to the equation then the point must be on the
|
||||||
|
+ * curve so why check again explicitly? OpenSSL code says this is
|
||||||
|
+ * required by X9.62. We're not X9.62 but it can't hurt just to be sure.
|
||||||
|
+ */
|
||||||
|
+ if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) {
|
||||||
|
+ wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!crypto_bignum_is_one(cofactor)) {
|
||||||
|
+ /* make sure the point is not in a small sub-group */
|
||||||
|
+ if (crypto_ec_point_mul(grp->group, grp->pwe, cofactor,
|
||||||
|
+ grp->pwe) != 0) {
|
||||||
|
+ wpa_printf(MSG_INFO,
|
||||||
|
+ "EAP-pwd: cannot multiply generator by order");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+ if (crypto_ec_point_is_at_infinity(grp->group, grp->pwe)) {
|
||||||
|
+ wpa_printf(MSG_INFO, "EAP-pwd: point is at infinity");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %02d tries", found_ctr);
|
||||||
|
+
|
||||||
|
if (0) {
|
||||||
|
fail:
|
||||||
|
crypto_ec_point_deinit(grp->pwe, 1);
|
||||||
|
@@ -344,14 +351,18 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
|
||||||
|
/* cleanliness and order.... */
|
||||||
|
crypto_bignum_deinit(cofactor, 1);
|
||||||
|
crypto_bignum_deinit(x_candidate, 1);
|
||||||
|
- crypto_bignum_deinit(rnd, 1);
|
||||||
|
crypto_bignum_deinit(pm1, 0);
|
||||||
|
crypto_bignum_deinit(tmp1, 1);
|
||||||
|
crypto_bignum_deinit(tmp2, 1);
|
||||||
|
crypto_bignum_deinit(qr, 1);
|
||||||
|
crypto_bignum_deinit(qnr, 1);
|
||||||
|
+ crypto_bignum_deinit(qr_or_qnr, 1);
|
||||||
|
crypto_bignum_deinit(one, 0);
|
||||||
|
- os_free(prfbuf);
|
||||||
|
+ bin_clear_free(prfbuf, primebytelen);
|
||||||
|
+ os_memset(qr_bin, 0, sizeof(qr_bin));
|
||||||
|
+ os_memset(qnr_bin, 0, sizeof(qnr_bin));
|
||||||
|
+ os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin));
|
||||||
|
+ os_memset(pwe_digest, 0, sizeof(pwe_digest));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
241
0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch
Normal file
241
0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
diff --git a/src/common/sae.c b/src/common/sae.c
|
||||||
|
index 8129a7c..d55323b 100644
|
||||||
|
--- 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"
|
||||||
|
@@ -292,15 +293,12 @@ 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,
|
||||||
|
- 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) */
|
||||||
|
@@ -309,7 +307,7 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
|
||||||
|
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);
|
||||||
|
|
||||||
|
@@ -320,20 +318,13 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -454,25 +445,30 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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;
|
||||||
|
+ int res = -1;
|
||||||
|
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
|
||||||
|
+ * mask */
|
||||||
|
|
||||||
|
- 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;
|
||||||
|
+ 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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -481,7 +477,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
@@ -497,7 +493,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
*/
|
||||||
|
sae_pwd_seed_key(addr1, addr2, addrs);
|
||||||
|
|
||||||
|
- addr[0] = password;
|
||||||
|
+ addr[0] = tmp_password;
|
||||||
|
len[0] = password_len;
|
||||||
|
num_elem = 1;
|
||||||
|
if (identifier) {
|
||||||
|
@@ -514,9 +510,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
* 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 */
|
||||||
|
@@ -524,36 +519,45 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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));
|
||||||
|
+ /* 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);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- /*
|
||||||
|
- * 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);
|
||||||
|
- }
|
||||||
|
+ 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) {
|
||||||
|
- wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
|
||||||
|
res = -1;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
@@ -566,7 +570,6 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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
|
||||||
|
@@ -578,6 +581,11 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
144
0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch
Normal file
144
0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
diff --git a/src/common/sae.c b/src/common/sae.c
|
||||||
|
index d55323b..5df9b95 100644
|
||||||
|
--- a/src/common/sae.c
|
||||||
|
+++ b/src/common/sae.c
|
||||||
|
@@ -232,12 +232,14 @@ get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
|
||||||
|
|
||||||
|
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
|
||||||
|
@@ -248,7 +250,7 @@ static int is_quadratic_residue_blind(struct sae_data *sae,
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
@@ -258,41 +260,45 @@ static int is_quadratic_residue_blind(struct sae_data *sae,
|
||||||
|
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;
|
||||||
|
@@ -452,6 +458,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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_*
|
||||||
|
@@ -476,7 +484,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
* (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",
|
||||||
|
@@ -527,7 +537,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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(
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
118
0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch
Normal file
118
0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
diff --git a/src/common/sae.c b/src/common/sae.c
|
||||||
|
index 5df9b95..75b1b4a 100644
|
||||||
|
--- a/src/common/sae.c
|
||||||
|
+++ b/src/common/sae.c
|
||||||
|
@@ -601,22 +601,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);
|
||||||
|
@@ -640,7 +645,9 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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;
|
||||||
|
|
||||||
|
@@ -650,19 +657,30 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
105
0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch
Normal file
105
0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
diff --git a/src/common/sae.c b/src/common/sae.c
|
||||||
|
index 75b1b4a..fa9a145 100644
|
||||||
|
--- a/src/common/sae.c
|
||||||
|
+++ b/src/common/sae.c
|
||||||
|
@@ -612,17 +612,28 @@ 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, 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);
|
||||||
|
|
||||||
|
@@ -661,27 +672,33 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
136
0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch
Normal file
136
0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
diff --git a/src/common/sae.c b/src/common/sae.c
|
||||||
|
index fa9a145..eaf825d 100644
|
||||||
|
--- a/src/common/sae.c
|
||||||
|
+++ b/src/common/sae.c
|
||||||
|
@@ -334,14 +334,17 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+/* 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);
|
||||||
|
|
||||||
|
@@ -353,16 +356,29 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
|
||||||
|
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:
|
||||||
|
@@ -376,33 +392,34 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
|
||||||
|
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 (!b)
|
||||||
|
+ goto fail;
|
||||||
|
|
||||||
|
- if (res < 0) {
|
||||||
|
- wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
+ res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
|
||||||
|
+ if (res < 0)
|
||||||
|
+ goto fail;
|
||||||
|
|
||||||
|
- /* if (PWE > 1) --> found */
|
||||||
|
- if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
|
||||||
|
- wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
+ /* 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);
|
||||||
|
|
||||||
|
- wpa_printf(MSG_DEBUG, "SAE: PWE found");
|
||||||
|
- return 1;
|
||||||
|
+fail:
|
||||||
|
+ crypto_bignum_deinit(a, 1);
|
||||||
|
+ crypto_bignum_deinit(b, 1);
|
||||||
|
+ return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
57
0010-SAE-Fix-confirm-message-validation-in-error-cases.patch
Normal file
57
0010-SAE-Fix-confirm-message-validation-in-error-cases.patch
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
From ac8fa9ef198640086cf2ce7c94673be2b6a018a0 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jouni Malinen <jouni@codeaurora.org>
|
||||||
|
Date: Tue, 5 Mar 2019 23:43:25 +0200
|
||||||
|
Subject: [PATCH 10/14] SAE: Fix confirm message validation in error cases
|
||||||
|
|
||||||
|
Explicitly verify that own and peer commit scalar/element are available
|
||||||
|
when trying to check SAE confirm message. It could have been possible to
|
||||||
|
hit a NULL pointer dereference if the peer element could not have been
|
||||||
|
parsed. (CVE-2019-9496)
|
||||||
|
|
||||||
|
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||||
|
---
|
||||||
|
src/common/sae.c | 14 +++++++++++---
|
||||||
|
1 file changed, 11 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/common/sae.c b/src/common/sae.c
|
||||||
|
index eaf825d..5a50294 100644
|
||||||
|
--- a/src/common/sae.c
|
||||||
|
+++ b/src/common/sae.c
|
||||||
|
@@ -1487,23 +1487,31 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
|
||||||
|
|
||||||
|
- if (sae->tmp == NULL) {
|
||||||
|
+ if (!sae->tmp || !sae->peer_commit_scalar ||
|
||||||
|
+ !sae->tmp->own_commit_scalar) {
|
||||||
|
wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (sae->tmp->ec)
|
||||||
|
+ if (sae->tmp->ec) {
|
||||||
|
+ if (!sae->tmp->peer_commit_element_ecc ||
|
||||||
|
+ !sae->tmp->own_commit_element_ecc)
|
||||||
|
+ return -1;
|
||||||
|
sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
|
||||||
|
sae->tmp->peer_commit_element_ecc,
|
||||||
|
sae->tmp->own_commit_scalar,
|
||||||
|
sae->tmp->own_commit_element_ecc,
|
||||||
|
verifier);
|
||||||
|
- else
|
||||||
|
+ } else {
|
||||||
|
+ if (!sae->tmp->peer_commit_element_ffc ||
|
||||||
|
+ !sae->tmp->own_commit_element_ffc)
|
||||||
|
+ return -1;
|
||||||
|
sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
|
||||||
|
sae->tmp->peer_commit_element_ffc,
|
||||||
|
sae->tmp->own_commit_scalar,
|
||||||
|
sae->tmp->own_commit_element_ffc,
|
||||||
|
verifier);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
58
0011-EAP-pwd-server-Verify-received-scalar-and-element.patch
Normal file
58
0011-EAP-pwd-server-Verify-received-scalar-and-element.patch
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
From 70ff850e89fbc8bc7da515321b4d15b5eef70581 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
|
||||||
|
Date: Sun, 31 Mar 2019 17:13:06 +0200
|
||||||
|
Subject: [PATCH 11/14] EAP-pwd server: Verify received scalar and element
|
||||||
|
|
||||||
|
When processing an EAP-pwd Commit frame, the peer's scalar and element
|
||||||
|
(elliptic curve point) were not validated. This allowed an adversary to
|
||||||
|
bypass authentication, and impersonate any user if the crypto
|
||||||
|
implementation did not verify the validity of the EC point.
|
||||||
|
|
||||||
|
Fix this vulnerability by assuring the received scalar lies within the
|
||||||
|
valid range, and by checking that the received element is not the point
|
||||||
|
at infinity and lies on the elliptic curve being used. (CVE-2019-9498)
|
||||||
|
|
||||||
|
The vulnerability is only exploitable if OpenSSL version 1.0.2 or lower
|
||||||
|
is used, or if LibreSSL or wolfssl is used. Newer versions of OpenSSL
|
||||||
|
(and also BoringSSL) implicitly validate the elliptic curve point in
|
||||||
|
EC_POINT_set_affine_coordinates_GFp(), preventing the attack.
|
||||||
|
|
||||||
|
Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
|
||||||
|
---
|
||||||
|
src/eap_server/eap_server_pwd.c | 20 ++++++++++++++++++++
|
||||||
|
1 file changed, 20 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
|
||||||
|
index d0fa54a..74979da 100644
|
||||||
|
--- a/src/eap_server/eap_server_pwd.c
|
||||||
|
+++ b/src/eap_server/eap_server_pwd.c
|
||||||
|
@@ -718,6 +718,26 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
goto fin;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* verify received scalar */
|
||||||
|
+ if (crypto_bignum_is_zero(data->peer_scalar) ||
|
||||||
|
+ crypto_bignum_is_one(data->peer_scalar) ||
|
||||||
|
+ crypto_bignum_cmp(data->peer_scalar,
|
||||||
|
+ crypto_ec_get_order(data->grp->group)) >= 0) {
|
||||||
|
+ wpa_printf(MSG_INFO,
|
||||||
|
+ "EAP-PWD (server): received scalar is invalid");
|
||||||
|
+ goto fin;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* verify received element */
|
||||||
|
+ if (!crypto_ec_point_is_on_curve(data->grp->group,
|
||||||
|
+ data->peer_element) ||
|
||||||
|
+ crypto_ec_point_is_at_infinity(data->grp->group,
|
||||||
|
+ data->peer_element)) {
|
||||||
|
+ wpa_printf(MSG_INFO,
|
||||||
|
+ "EAP-PWD (server): received element is invalid");
|
||||||
|
+ goto fin;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* check to ensure peer's element is not in a small sub-group */
|
||||||
|
if (!crypto_bignum_is_one(cofactor)) {
|
||||||
|
if (crypto_ec_point_mul(data->grp->group, data->peer_element,
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
45
0012-EAP-pwd-server-Detect-reflection-attacks.patch
Normal file
45
0012-EAP-pwd-server-Detect-reflection-attacks.patch
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
From d63edfa90243e9a7de6ae5c275032f2cc79fef95 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
|
||||||
|
Date: Sun, 31 Mar 2019 17:26:01 +0200
|
||||||
|
Subject: [PATCH 12/14] EAP-pwd server: Detect reflection attacks
|
||||||
|
|
||||||
|
When processing an EAP-pwd Commit frame, verify that the peer's scalar
|
||||||
|
and elliptic curve element differ from the one sent by the server. This
|
||||||
|
prevents reflection attacks where the adversary reflects the scalar and
|
||||||
|
element sent by the server. (CVE-2019-9497)
|
||||||
|
|
||||||
|
The vulnerability allows an adversary to complete the EAP-pwd handshake
|
||||||
|
as any user. However, the adversary does not learn the negotiated
|
||||||
|
session key, meaning the subsequent 4-way handshake would fail. As a
|
||||||
|
result, this cannot be abused to bypass authentication unless EAP-pwd is
|
||||||
|
used in non-WLAN cases without any following key exchange that would
|
||||||
|
require the attacker to learn the MSK.
|
||||||
|
|
||||||
|
Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
|
||||||
|
---
|
||||||
|
src/eap_server/eap_server_pwd.c | 9 +++++++++
|
||||||
|
1 file changed, 9 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
|
||||||
|
index 74979da..16057e9 100644
|
||||||
|
--- a/src/eap_server/eap_server_pwd.c
|
||||||
|
+++ b/src/eap_server/eap_server_pwd.c
|
||||||
|
@@ -753,6 +753,15 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* detect reflection attacks */
|
||||||
|
+ if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 ||
|
||||||
|
+ crypto_ec_point_cmp(data->grp->group, data->my_element,
|
||||||
|
+ data->peer_element) == 0) {
|
||||||
|
+ wpa_printf(MSG_INFO,
|
||||||
|
+ "EAP-PWD (server): detected reflection attack!");
|
||||||
|
+ goto fin;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* compute the shared key, k */
|
||||||
|
if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe,
|
||||||
|
data->peer_scalar, K) < 0) ||
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
58
0013-EAP-pwd-client-Verify-received-scalar-and-element.patch
Normal file
58
0013-EAP-pwd-client-Verify-received-scalar-and-element.patch
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
From 8ad8585f91823ddcc3728155e288e0f9f872e31a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
|
||||||
|
Date: Sun, 31 Mar 2019 17:43:44 +0200
|
||||||
|
Subject: [PATCH 13/14] EAP-pwd client: Verify received scalar and element
|
||||||
|
|
||||||
|
When processing an EAP-pwd Commit frame, the server's scalar and element
|
||||||
|
(elliptic curve point) were not validated. This allowed an adversary to
|
||||||
|
bypass authentication, and act as a rogue Access Point (AP) if the
|
||||||
|
crypto implementation did not verify the validity of the EC point.
|
||||||
|
|
||||||
|
Fix this vulnerability by assuring the received scalar lies within the
|
||||||
|
valid range, and by checking that the received element is not the point
|
||||||
|
at infinity and lies on the elliptic curve being used. (CVE-2019-9499)
|
||||||
|
|
||||||
|
The vulnerability is only exploitable if OpenSSL version 1.0.2 or lower
|
||||||
|
is used, or if LibreSSL or wolfssl is used. Newer versions of OpenSSL
|
||||||
|
(and also BoringSSL) implicitly validate the elliptic curve point in
|
||||||
|
EC_POINT_set_affine_coordinates_GFp(), preventing the attack.
|
||||||
|
|
||||||
|
Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
|
||||||
|
---
|
||||||
|
src/eap_peer/eap_pwd.c | 20 ++++++++++++++++++++
|
||||||
|
1 file changed, 20 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
|
||||||
|
index 761c16a..5a05e54 100644
|
||||||
|
--- a/src/eap_peer/eap_pwd.c
|
||||||
|
+++ b/src/eap_peer/eap_pwd.c
|
||||||
|
@@ -594,6 +594,26 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
goto fin;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* verify received scalar */
|
||||||
|
+ if (crypto_bignum_is_zero(data->server_scalar) ||
|
||||||
|
+ crypto_bignum_is_one(data->server_scalar) ||
|
||||||
|
+ crypto_bignum_cmp(data->server_scalar,
|
||||||
|
+ crypto_ec_get_order(data->grp->group)) >= 0) {
|
||||||
|
+ wpa_printf(MSG_INFO,
|
||||||
|
+ "EAP-PWD (peer): received scalar is invalid");
|
||||||
|
+ goto fin;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* verify received element */
|
||||||
|
+ if (!crypto_ec_point_is_on_curve(data->grp->group,
|
||||||
|
+ data->server_element) ||
|
||||||
|
+ crypto_ec_point_is_at_infinity(data->grp->group,
|
||||||
|
+ data->server_element)) {
|
||||||
|
+ wpa_printf(MSG_INFO,
|
||||||
|
+ "EAP-PWD (peer): received element is invalid");
|
||||||
|
+ goto fin;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* check to ensure server's element is not in a small sub-group */
|
||||||
|
if (!crypto_bignum_is_one(cofactor)) {
|
||||||
|
if (crypto_ec_point_mul(data->grp->group, data->server_element,
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
331
0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch
Normal file
331
0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
From 16d4f1069118aa19bfce013493e1ac5783f92f1d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jouni Malinen <jouni@codeaurora.org>
|
||||||
|
Date: Fri, 5 Apr 2019 02:12:50 +0300
|
||||||
|
Subject: [PATCH 14/14] EAP-pwd: Check element x,y coordinates explicitly
|
||||||
|
|
||||||
|
This adds an explicit check for 0 < x,y < prime based on RFC 5931,
|
||||||
|
2.8.5.2.2 requirement. The earlier checks might have covered this
|
||||||
|
implicitly, but it is safer to avoid any dependency on implicit checks
|
||||||
|
and specific crypto library behavior. (CVE-2019-9498 and CVE-2019-9499)
|
||||||
|
|
||||||
|
Furthermore, this moves the EAP-pwd element and scalar parsing and
|
||||||
|
validation steps into shared helper functions so that there is no need
|
||||||
|
to maintain two separate copies of this common functionality between the
|
||||||
|
server and peer implementations.
|
||||||
|
|
||||||
|
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||||||
|
---
|
||||||
|
src/eap_common/eap_pwd_common.c | 106 ++++++++++++++++++++++++++++++++++++++++
|
||||||
|
src/eap_common/eap_pwd_common.h | 3 ++
|
||||||
|
src/eap_peer/eap_pwd.c | 45 ++---------------
|
||||||
|
src/eap_server/eap_server_pwd.c | 45 ++---------------
|
||||||
|
4 files changed, 117 insertions(+), 82 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
|
||||||
|
index e49aaf8..c28b56d 100644
|
||||||
|
--- a/src/eap_common/eap_pwd_common.c
|
||||||
|
+++ b/src/eap_common/eap_pwd_common.c
|
||||||
|
@@ -428,3 +428,109 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static int eap_pwd_element_coord_ok(const struct crypto_bignum *prime,
|
||||||
|
+ const u8 *buf, size_t len)
|
||||||
|
+{
|
||||||
|
+ struct crypto_bignum *val;
|
||||||
|
+ int ok = 1;
|
||||||
|
+
|
||||||
|
+ val = crypto_bignum_init_set(buf, len);
|
||||||
|
+ if (!val || crypto_bignum_is_zero(val) ||
|
||||||
|
+ crypto_bignum_cmp(val, prime) >= 0)
|
||||||
|
+ ok = 0;
|
||||||
|
+ crypto_bignum_deinit(val, 0);
|
||||||
|
+ return ok;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
|
||||||
|
+ const u8 *buf)
|
||||||
|
+{
|
||||||
|
+ struct crypto_ec_point *element;
|
||||||
|
+ const struct crypto_bignum *prime;
|
||||||
|
+ size_t prime_len;
|
||||||
|
+ struct crypto_bignum *cofactor = NULL;
|
||||||
|
+
|
||||||
|
+ prime = crypto_ec_get_prime(group->group);
|
||||||
|
+ prime_len = crypto_ec_prime_len(group->group);
|
||||||
|
+
|
||||||
|
+ /* RFC 5931, 2.8.5.2.2: 0 < x,y < p */
|
||||||
|
+ if (!eap_pwd_element_coord_ok(prime, buf, prime_len) ||
|
||||||
|
+ !eap_pwd_element_coord_ok(prime, buf + prime_len, prime_len)) {
|
||||||
|
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid coordinate in element");
|
||||||
|
+ return NULL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ element = crypto_ec_point_from_bin(group->group, buf);
|
||||||
|
+ if (!element) {
|
||||||
|
+ wpa_printf(MSG_INFO, "EAP-pwd: EC point from element failed");
|
||||||
|
+ return NULL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* RFC 5931, 2.8.5.2.2: on curve and not the point at infinity */
|
||||||
|
+ if (!crypto_ec_point_is_on_curve(group->group, element) ||
|
||||||
|
+ crypto_ec_point_is_at_infinity(group->group, element)) {
|
||||||
|
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid element");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ cofactor = crypto_bignum_init();
|
||||||
|
+ if (!cofactor || crypto_ec_cofactor(group->group, cofactor) < 0) {
|
||||||
|
+ wpa_printf(MSG_INFO,
|
||||||
|
+ "EAP-pwd: Unable to get cofactor for curve");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!crypto_bignum_is_one(cofactor)) {
|
||||||
|
+ struct crypto_ec_point *point;
|
||||||
|
+ int ok = 1;
|
||||||
|
+
|
||||||
|
+ /* check to ensure peer's element is not in a small sub-group */
|
||||||
|
+ point = crypto_ec_point_init(group->group);
|
||||||
|
+ if (!point ||
|
||||||
|
+ crypto_ec_point_mul(group->group, element,
|
||||||
|
+ cofactor, point) != 0 ||
|
||||||
|
+ crypto_ec_point_is_at_infinity(group->group, point))
|
||||||
|
+ ok = 0;
|
||||||
|
+ crypto_ec_point_deinit(point, 0);
|
||||||
|
+
|
||||||
|
+ if (!ok) {
|
||||||
|
+ wpa_printf(MSG_INFO,
|
||||||
|
+ "EAP-pwd: Small sub-group check on peer element failed");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+out:
|
||||||
|
+ crypto_bignum_deinit(cofactor, 0);
|
||||||
|
+ return element;
|
||||||
|
+fail:
|
||||||
|
+ crypto_ec_point_deinit(element, 0);
|
||||||
|
+ element = NULL;
|
||||||
|
+ goto out;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf)
|
||||||
|
+{
|
||||||
|
+ struct crypto_bignum *scalar;
|
||||||
|
+ const struct crypto_bignum *order;
|
||||||
|
+ size_t order_len;
|
||||||
|
+
|
||||||
|
+ order = crypto_ec_get_order(group->group);
|
||||||
|
+ order_len = crypto_ec_order_len(group->group);
|
||||||
|
+
|
||||||
|
+ /* RFC 5931, 2.8.5.2: 1 < scalar < r */
|
||||||
|
+ scalar = crypto_bignum_init_set(buf, order_len);
|
||||||
|
+ if (!scalar || crypto_bignum_is_zero(scalar) ||
|
||||||
|
+ crypto_bignum_is_one(scalar) ||
|
||||||
|
+ crypto_bignum_cmp(scalar, order) >= 0) {
|
||||||
|
+ wpa_printf(MSG_INFO, "EAP-pwd: received scalar is invalid");
|
||||||
|
+ crypto_bignum_deinit(scalar, 0);
|
||||||
|
+ scalar = NULL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return scalar;
|
||||||
|
+}
|
||||||
|
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
|
||||||
|
index 6b07cf8..2387e59 100644
|
||||||
|
--- a/src/eap_common/eap_pwd_common.h
|
||||||
|
+++ b/src/eap_common/eap_pwd_common.h
|
||||||
|
@@ -67,5 +67,8 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
|
||||||
|
struct crypto_hash * eap_pwd_h_init(void);
|
||||||
|
void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
|
||||||
|
void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
|
||||||
|
+struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
|
||||||
|
+ const u8 *buf);
|
||||||
|
+struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf);
|
||||||
|
|
||||||
|
#endif /* EAP_PWD_COMMON_H */
|
||||||
|
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
|
||||||
|
index 5a05e54..f37b974 100644
|
||||||
|
--- a/src/eap_peer/eap_pwd.c
|
||||||
|
+++ b/src/eap_peer/eap_pwd.c
|
||||||
|
@@ -308,7 +308,7 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
const struct wpabuf *reqData,
|
||||||
|
const u8 *payload, size_t payload_len)
|
||||||
|
{
|
||||||
|
- struct crypto_ec_point *K = NULL, *point = NULL;
|
||||||
|
+ struct crypto_ec_point *K = NULL;
|
||||||
|
struct crypto_bignum *mask = NULL, *cofactor = NULL;
|
||||||
|
const u8 *ptr = payload;
|
||||||
|
u8 *scalar = NULL, *element = NULL;
|
||||||
|
@@ -572,63 +572,27 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
/* process the request */
|
||||||
|
data->k = crypto_bignum_init();
|
||||||
|
K = crypto_ec_point_init(data->grp->group);
|
||||||
|
- point = crypto_ec_point_init(data->grp->group);
|
||||||
|
- if (!data->k || !K || !point) {
|
||||||
|
+ if (!data->k || !K) {
|
||||||
|
wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
|
||||||
|
"fail");
|
||||||
|
goto fin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* element, x then y, followed by scalar */
|
||||||
|
- data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr);
|
||||||
|
+ data->server_element = eap_pwd_get_element(data->grp, ptr);
|
||||||
|
if (!data->server_element) {
|
||||||
|
wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
|
||||||
|
"fail");
|
||||||
|
goto fin;
|
||||||
|
}
|
||||||
|
ptr += prime_len * 2;
|
||||||
|
- data->server_scalar = crypto_bignum_init_set(ptr, order_len);
|
||||||
|
+ data->server_scalar = eap_pwd_get_scalar(data->grp, ptr);
|
||||||
|
if (!data->server_scalar) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"EAP-PWD (peer): setting peer scalar fail");
|
||||||
|
goto fin;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* verify received scalar */
|
||||||
|
- if (crypto_bignum_is_zero(data->server_scalar) ||
|
||||||
|
- crypto_bignum_is_one(data->server_scalar) ||
|
||||||
|
- crypto_bignum_cmp(data->server_scalar,
|
||||||
|
- crypto_ec_get_order(data->grp->group)) >= 0) {
|
||||||
|
- wpa_printf(MSG_INFO,
|
||||||
|
- "EAP-PWD (peer): received scalar is invalid");
|
||||||
|
- goto fin;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* verify received element */
|
||||||
|
- if (!crypto_ec_point_is_on_curve(data->grp->group,
|
||||||
|
- data->server_element) ||
|
||||||
|
- crypto_ec_point_is_at_infinity(data->grp->group,
|
||||||
|
- data->server_element)) {
|
||||||
|
- wpa_printf(MSG_INFO,
|
||||||
|
- "EAP-PWD (peer): received element is invalid");
|
||||||
|
- goto fin;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* check to ensure server's element is not in a small sub-group */
|
||||||
|
- if (!crypto_bignum_is_one(cofactor)) {
|
||||||
|
- if (crypto_ec_point_mul(data->grp->group, data->server_element,
|
||||||
|
- cofactor, point) < 0) {
|
||||||
|
- wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
|
||||||
|
- "server element by order!\n");
|
||||||
|
- goto fin;
|
||||||
|
- }
|
||||||
|
- if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
|
||||||
|
- wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
|
||||||
|
- "is at infinity!\n");
|
||||||
|
- goto fin;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* compute the shared key, k */
|
||||||
|
if (crypto_ec_point_mul(data->grp->group, data->grp->pwe,
|
||||||
|
data->server_scalar, K) < 0 ||
|
||||||
|
@@ -702,7 +666,6 @@ fin:
|
||||||
|
crypto_bignum_deinit(mask, 1);
|
||||||
|
crypto_bignum_deinit(cofactor, 1);
|
||||||
|
crypto_ec_point_deinit(K, 1);
|
||||||
|
- crypto_ec_point_deinit(point, 1);
|
||||||
|
if (data->outbuf == NULL)
|
||||||
|
eap_pwd_state(data, FAILURE);
|
||||||
|
else
|
||||||
|
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
|
||||||
|
index 16057e9..f6c75cf 100644
|
||||||
|
--- a/src/eap_server/eap_server_pwd.c
|
||||||
|
+++ b/src/eap_server/eap_server_pwd.c
|
||||||
|
@@ -669,7 +669,7 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
{
|
||||||
|
const u8 *ptr;
|
||||||
|
struct crypto_bignum *cofactor = NULL;
|
||||||
|
- struct crypto_ec_point *K = NULL, *point = NULL;
|
||||||
|
+ struct crypto_ec_point *K = NULL;
|
||||||
|
int res = 0;
|
||||||
|
size_t prime_len, order_len;
|
||||||
|
|
||||||
|
@@ -688,9 +688,8 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
|
||||||
|
data->k = crypto_bignum_init();
|
||||||
|
cofactor = crypto_bignum_init();
|
||||||
|
- point = crypto_ec_point_init(data->grp->group);
|
||||||
|
K = crypto_ec_point_init(data->grp->group);
|
||||||
|
- if (!data->k || !cofactor || !point || !K) {
|
||||||
|
+ if (!data->k || !cofactor || !K) {
|
||||||
|
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
|
||||||
|
"fail");
|
||||||
|
goto fin;
|
||||||
|
@@ -704,55 +703,20 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
|
||||||
|
/* element, x then y, followed by scalar */
|
||||||
|
ptr = payload;
|
||||||
|
- data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr);
|
||||||
|
+ data->peer_element = eap_pwd_get_element(data->grp, ptr);
|
||||||
|
if (!data->peer_element) {
|
||||||
|
wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
|
||||||
|
"fail");
|
||||||
|
goto fin;
|
||||||
|
}
|
||||||
|
ptr += prime_len * 2;
|
||||||
|
- data->peer_scalar = crypto_bignum_init_set(ptr, order_len);
|
||||||
|
+ data->peer_scalar = eap_pwd_get_scalar(data->grp, ptr);
|
||||||
|
if (!data->peer_scalar) {
|
||||||
|
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
|
||||||
|
"fail");
|
||||||
|
goto fin;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* verify received scalar */
|
||||||
|
- if (crypto_bignum_is_zero(data->peer_scalar) ||
|
||||||
|
- crypto_bignum_is_one(data->peer_scalar) ||
|
||||||
|
- crypto_bignum_cmp(data->peer_scalar,
|
||||||
|
- crypto_ec_get_order(data->grp->group)) >= 0) {
|
||||||
|
- wpa_printf(MSG_INFO,
|
||||||
|
- "EAP-PWD (server): received scalar is invalid");
|
||||||
|
- goto fin;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* verify received element */
|
||||||
|
- if (!crypto_ec_point_is_on_curve(data->grp->group,
|
||||||
|
- data->peer_element) ||
|
||||||
|
- crypto_ec_point_is_at_infinity(data->grp->group,
|
||||||
|
- data->peer_element)) {
|
||||||
|
- wpa_printf(MSG_INFO,
|
||||||
|
- "EAP-PWD (server): received element is invalid");
|
||||||
|
- goto fin;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* check to ensure peer's element is not in a small sub-group */
|
||||||
|
- if (!crypto_bignum_is_one(cofactor)) {
|
||||||
|
- if (crypto_ec_point_mul(data->grp->group, data->peer_element,
|
||||||
|
- cofactor, point) != 0) {
|
||||||
|
- wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
|
||||||
|
- "multiply peer element by order");
|
||||||
|
- goto fin;
|
||||||
|
- }
|
||||||
|
- if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
|
||||||
|
- wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
|
||||||
|
- "is at infinity!\n");
|
||||||
|
- goto fin;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* detect reflection attacks */
|
||||||
|
if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 ||
|
||||||
|
crypto_ec_point_cmp(data->grp->group, data->my_element,
|
||||||
|
@@ -804,7 +768,6 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
|
||||||
|
|
||||||
|
fin:
|
||||||
|
crypto_ec_point_deinit(K, 1);
|
||||||
|
- crypto_ec_point_deinit(point, 1);
|
||||||
|
crypto_bignum_deinit(cofactor, 1);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
--
|
||||||
|
2.7.4
|
||||||
|
|
@ -7,7 +7,7 @@ Summary: WPA/WPA2/IEEE 802.1X Supplicant
|
|||||||
Name: wpa_supplicant
|
Name: wpa_supplicant
|
||||||
Epoch: 1
|
Epoch: 1
|
||||||
Version: 2.7
|
Version: 2.7
|
||||||
Release: 4%{?dist}
|
Release: 5%{?dist}
|
||||||
License: BSD
|
License: BSD
|
||||||
Source0: http://w1.fi/releases/%{name}-%{version}%{rcver}%{snapshot}.tar.gz
|
Source0: http://w1.fi/releases/%{name}-%{version}%{rcver}%{snapshot}.tar.gz
|
||||||
Source1: build-config
|
Source1: build-config
|
||||||
@ -34,6 +34,27 @@ Patch6: wpa_supplicant-gui-qt4.patch
|
|||||||
# http://lists.infradead.org/pipermail/hostap/2019-January/039338.html
|
# http://lists.infradead.org/pipermail/hostap/2019-January/039338.html
|
||||||
Patch7: https://github.com/NetworkManager/hostap/commit/24b22f0.patch#/0001-dbus-Expose-support-of-SAE-key-management.patch
|
Patch7: https://github.com/NetworkManager/hostap/commit/24b22f0.patch#/0001-dbus-Expose-support-of-SAE-key-management.patch
|
||||||
|
|
||||||
|
# CVE-2019-9494 http://w1.fi/security/2019-1
|
||||||
|
# CVE-2019-9495 http://w1.fi/security/2019-2
|
||||||
|
# CVE-2019-9496 http://w1.fi/security/2019-3
|
||||||
|
# CVE-2019-9497 http://w1.fi/security/2019-4
|
||||||
|
# CVE-2019-9498 http://w1.fi/security/2019-4
|
||||||
|
# CVE-2019-9499 http://w1.fi/security/2019-4
|
||||||
|
Patch8: http://w1.fi/security/2019-1/0001-OpenSSL-Use-constant-time-operations-for-private-big.patch
|
||||||
|
Patch9: http://w1.fi/security/2019-1/0002-Add-helper-functions-for-constant-time-operations.patch
|
||||||
|
Patch10: http://w1.fi/security/2019-1/0003-OpenSSL-Use-constant-time-selection-for-crypto_bignu.patch
|
||||||
|
Patch11: http://w1.fi/security/2019-2/0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch
|
||||||
|
Patch12: http://w1.fi/security/2019-1/0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch
|
||||||
|
Patch13: http://w1.fi/security/2019-1/0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch
|
||||||
|
Patch14: http://w1.fi/security/2019-1/0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch
|
||||||
|
Patch15: http://w1.fi/security/2019-1/0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch
|
||||||
|
Patch16: http://w1.fi/security/2019-1/0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch
|
||||||
|
Patch17: http://w1.fi/security/2019-3/0010-SAE-Fix-confirm-message-validation-in-error-cases.patch
|
||||||
|
Patch18: http://w1.fi/security/2019-4/0011-EAP-pwd-server-Verify-received-scalar-and-element.patch
|
||||||
|
Patch19: http://w1.fi/security/2019-4/0012-EAP-pwd-server-Detect-reflection-attacks.patch
|
||||||
|
Patch20: http://w1.fi/security/2019-4/0013-EAP-pwd-client-Verify-received-scalar-and-element.patch
|
||||||
|
Patch21: http://w1.fi/security/2019-4/0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch
|
||||||
|
|
||||||
URL: http://w1.fi/wpa_supplicant/
|
URL: http://w1.fi/wpa_supplicant/
|
||||||
|
|
||||||
%if %{build_gui}
|
%if %{build_gui}
|
||||||
@ -173,6 +194,15 @@ chmod -R 0644 %{name}/examples/*.py
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Apr 12 2019 Davide Caratti <dcaratti@redhat.com> - 1:2.7-5
|
||||||
|
- fix SAE and EAP_PWD vulnerabilities:
|
||||||
|
CVE-2019-9494 (cache attack against SAE)
|
||||||
|
CVE-2019-9495 (cache attack against EAP-pwd)
|
||||||
|
CVE-2019-9496 (SAE confirm missing state validation in hostapd/AP)
|
||||||
|
CVE-2019-9497 (EAP-pwd server not checking for reflection attack)
|
||||||
|
CVE-2019-9498 (EAP-pwd server missing commit validation for scalar/element)
|
||||||
|
CVE-2019-9499 (EAP-pwd peer missing commit validation for scalar/element)
|
||||||
|
|
||||||
* Sun Feb 03 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1:2.7-4
|
* Sun Feb 03 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1:2.7-4
|
||||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user