]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SAE: Add processing of the commit message
authorJouni Malinen <j@w1.fi>
Sun, 30 Dec 2012 20:16:18 +0000 (22:16 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 12 Jan 2013 15:51:52 +0000 (17:51 +0200)
This adds validation of the received commit messages and key derivation
for SAE.

Signed-hostap: Jouni Malinen <j@w1.fi>

src/ap/ieee802_11.c
src/common/sae.c
src/common/sae.h
wpa_supplicant/sme.c

index a597b74d3ac819c28734a93f006827513601d861..506b781abe3e927c77bc2ad052b764e2693b54c0 100644 (file)
@@ -318,11 +318,16 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
 
 #ifdef CONFIG_SAE
 
-static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
-                                            struct sta_info *sta)
+static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
+                                              struct sta_info *sta)
 {
        struct wpabuf *buf;
 
+       if (hapd->conf->ssid.wpa_passphrase == NULL) {
+               wpa_printf(MSG_DEBUG, "SAE: No password available");
+               return NULL;
+       }
+
        if (sae_prepare_commit(hapd->own_addr, sta->addr,
                               (u8 *) hapd->conf->ssid.wpa_passphrase,
                               os_strlen(hapd->conf->ssid.wpa_passphrase),
@@ -331,6 +336,11 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
                return NULL;
        }
 
+       if (sae_process_commit(sta->sae) < 0) {
+               wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+               return NULL;
+       }
+
        buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
        if (buf == NULL)
                return NULL;
@@ -357,24 +367,6 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
 }
 
 
-static u16 handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta,
-                            const u8 *data, size_t len)
-{
-       wpa_hexdump(MSG_DEBUG, "SAE commit fields", data, len);
-
-       /* Check Finite Cyclic Group */
-       if (len < 2)
-               return WLAN_STATUS_UNSPECIFIED_FAILURE;
-       if (WPA_GET_LE16(data) != 19) {
-               wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
-                          WPA_GET_LE16(data));
-               return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
-       }
-
-       return WLAN_STATUS_SUCCESS;
-}
-
-
 static u16 handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta,
                              const u8 *data, size_t len)
 {
@@ -408,12 +400,12 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
                               "start SAE authentication (RX commit)");
-               resp = handle_sae_commit(hapd, sta, mgmt->u.auth.variable,
-                                        ((u8 *) mgmt) + len -
-                                        mgmt->u.auth.variable);
+               resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
+                                       ((const u8 *) mgmt) + len -
+                                       mgmt->u.auth.variable);
                if (resp == WLAN_STATUS_SUCCESS) {
                        sta->sae->state = SAE_COMMIT;
-                       data = auth_build_sae_commit(hapd, sta);
+                       data = auth_process_sae_commit(hapd, sta);
                        if (data == NULL)
                                resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
                }
index 9907df1adc9c641345782bc97bf7e09f0aebe68e..e628d4694d3ad874cafb8abb3d49b39d16cbbddd 100644 (file)
@@ -15,6 +15,7 @@
 #include "common.h"
 #include "crypto/sha256.h"
 #include "crypto/random.h"
+#include "ieee802_11_defs.h"
 #include "sae.h"
 
 
@@ -46,6 +47,17 @@ static int val_zero_or_one(const u8 *val, size_t len)
 }
 
 
+static int val_zero(const u8 *val, size_t len)
+{
+       size_t i;
+       for (i = 0; i < len; i++) {
+               if (val[i])
+                       return 0;
+       }
+       return 1;
+}
+
+
 static int sae_get_rand(u8 *val)
 {
        int iter = 0;
@@ -62,6 +74,33 @@ static int sae_get_rand(u8 *val)
 }
 
 
+static EC_POINT * alloc_elem(EC_GROUP *group, const u8 *val, size_t len)
+{
+       BIGNUM *x, *y;
+       EC_POINT *elem;
+
+       x = BN_bin2bn(val, len, NULL);
+       y = BN_bin2bn(val + len, len, NULL);
+       elem = EC_POINT_new(group);
+       if (x == NULL || y == NULL || elem == NULL) {
+               BN_free(x);
+               BN_free(y);
+               EC_POINT_free(elem);
+               return NULL;
+       }
+
+       if (!EC_POINT_set_affine_coordinates_GFp(group, elem, x, y, NULL)) {
+               EC_POINT_free(elem);
+               elem = NULL;
+       }
+
+       BN_free(x);
+       BN_free(y);
+
+       return elem;
+}
+
+
 static void sae_bn_to_bin(const BIGNUM *bn, u8 *bin, size_t len)
 {
        int offset = len - BN_num_bytes(bn);
@@ -302,6 +341,152 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
 }
 
 
+static int sae_check_peer_commit(struct sae_data *sae)
+{
+       /* 0 < scalar < r */
+       if (val_zero(sae->peer_commit_scalar, 32) ||
+           os_memcmp(sae->peer_commit_scalar, group19_order,
+                     sizeof(group19_prime)) >= 0) {
+               wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
+               return -1;
+       }
+
+       /* element x and y coordinates < p */
+       if (os_memcmp(sae->peer_commit_element, group19_prime,
+                     sizeof(group19_prime)) >= 0 ||
+           os_memcmp(sae->peer_commit_element + 32, group19_prime,
+                     sizeof(group19_prime)) >= 0) {
+               wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
+                          "element");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int sae_derive_k(struct sae_data *sae, u8 *k, BN_CTX *bnctx,
+                       EC_GROUP *group)
+{
+       EC_POINT *pwe, *peer_elem, *K;
+       BIGNUM *k_bn, *rand_bn, *peer_scalar;
+       int ret = -1;
+
+       pwe = alloc_elem(group, sae->pwe, 32);
+       peer_scalar = BN_bin2bn(sae->peer_commit_scalar, 32, NULL);
+       peer_elem = alloc_elem(group, sae->peer_commit_element, 32);
+       K = EC_POINT_new(group);
+       k_bn = BN_new();
+       rand_bn = BN_bin2bn(sae->sae_rand, 32, NULL);
+       if (pwe == NULL || peer_elem == NULL || peer_scalar == NULL ||
+           K == NULL || k_bn == NULL || rand_bn == NULL)
+               goto fail;
+
+       if (!EC_POINT_is_on_curve(group, peer_elem, NULL)) {
+               wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
+               goto fail;
+       }
+
+       /*
+        * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+        *                                        PEER-COMMIT-ELEMENT)))
+        * If K is identity element (point-at-infinity), reject
+        * k = F(K) (= x coordinate)
+        */
+
+       if (!EC_POINT_mul(group, K, NULL, pwe, peer_scalar, bnctx) ||
+           !EC_POINT_add(group, K, K, peer_elem, bnctx) ||
+           !EC_POINT_mul(group, K, NULL, K, rand_bn, bnctx) ||
+           EC_POINT_is_at_infinity(group, K) ||
+           !EC_POINT_get_affine_coordinates_GFp(group, K, k_bn, NULL, bnctx)) {
+               wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+               goto fail;
+       }
+
+       sae_bn_to_bin(k_bn, k, 32);
+       wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, 32);
+
+       ret = 0;
+fail:
+       EC_POINT_free(pwe);
+       EC_POINT_free(peer_elem);
+       EC_POINT_clear_free(K);
+       BN_free(k_bn);
+       BN_free(rand_bn);
+       return ret;
+}
+
+
+static int sae_derive_keys(struct sae_data *sae, const u8 *k, BN_CTX *bnctx)
+{
+       u8 null_key[32], val[32];
+       u8 keyseed[SHA256_MAC_LEN];
+       u8 keys[32 + 32];
+       BIGNUM *order, *own_scalar, *peer_scalar, *tmp;
+       int ret = -1;
+
+       order = BN_bin2bn(group19_order, sizeof(group19_order), NULL);
+       own_scalar = BN_bin2bn(sae->own_commit_scalar, 32, NULL);
+       peer_scalar = BN_bin2bn(sae->peer_commit_scalar, 32, NULL);
+       tmp = BN_new();
+       if (order == NULL || own_scalar == NULL || peer_scalar == NULL ||
+           tmp == NULL)
+               goto fail;
+
+       /* keyseed = H(<0>32, k)
+        * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
+        *                      (commit-scalar + peer-commit-scalar) modulo r)
+        * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
+        */
+
+       os_memset(null_key, 0, sizeof(null_key));
+       hmac_sha256(null_key, sizeof(null_key), k, 32, keyseed);
+       wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
+
+       BN_add(tmp, own_scalar, peer_scalar);
+       BN_mod(tmp, tmp, order, bnctx);
+       sae_bn_to_bin(tmp, val, sizeof(group19_prime));
+       wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, 16);
+       sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
+                  val, sizeof(val), keys, sizeof(keys));
+       os_memcpy(sae->kck, keys, 32);
+       os_memcpy(sae->pmk, keys + 32, 32);
+       wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->kck, 32);
+       wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, 32);
+
+       ret = 0;
+fail:
+       BN_free(order);
+       BN_free(own_scalar);
+       BN_free(tmp);
+       return ret;
+}
+
+
+int sae_process_commit(struct sae_data *sae)
+{
+       BN_CTX *bnctx;
+       EC_GROUP *group;
+       int ret = 0;
+       u8 k[32];
+
+       if (sae_check_peer_commit(sae) < 0)
+               return -1;
+
+       bnctx = BN_CTX_new();
+       group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+       if (bnctx == NULL || group == NULL ||
+           sae_derive_k(sae, k, bnctx, group) < 0 ||
+           sae_derive_keys(sae, k, bnctx) < 0)
+               ret = -1;
+
+       EC_GROUP_free(group);
+       BN_CTX_free(bnctx);
+
+       return ret;
+}
+
+
 void sae_write_commit(struct sae_data *sae, struct wpabuf *buf)
 {
        wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
@@ -309,3 +494,51 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf)
        wpabuf_put_data(buf, sae->own_commit_scalar, 32);
        wpabuf_put_data(buf, sae->own_commit_element, 2 * 32);
 }
+
+
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len)
+{
+       const u8 *pos = data, *end = data + len;
+       size_t val_len;
+
+       wpa_hexdump(MSG_DEBUG, "SAE: Commit fields", data, len);
+
+       /* Check Finite Cyclic Group */
+       if (pos + 2 > end)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       if (WPA_GET_LE16(pos) != 19) {
+               wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
+                          WPA_GET_LE16(pos));
+               return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+       }
+       pos += 2;
+       val_len = 32;
+
+       if (pos + val_len > end) {
+               wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+       os_memcpy(sae->peer_commit_scalar, pos, val_len);
+       wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
+                   sae->peer_commit_scalar, val_len);
+       pos += val_len;
+
+       if (pos + 2 * val_len > end) {
+               wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+                          "commit-element");
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+       os_memcpy(sae->peer_commit_element, pos, 2 * val_len);
+       wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
+                   sae->peer_commit_element, val_len);
+       wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
+                   sae->peer_commit_element + val_len, val_len);
+       pos += 2 * val_len;
+
+       if (end > pos) {
+               wpa_hexdump(MSG_DEBUG, "SAE: Unexpected extra data in commit",
+                           pos, end - pos);
+       }
+
+       return WLAN_STATUS_SUCCESS;
+}
index 63676a010c7f6b0bb163602995fdcf167ac5cd15..4f689ed092b71f2868ef7d925bcdfbeddc031427 100644 (file)
 struct sae_data {
        enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } state;
        u16 send_confirm;
+       u8 kck[32];
+       u8 pmk[32];
        u8 own_commit_scalar[32];
        u8 own_commit_element[2 * 32];
+       u8 peer_commit_scalar[32];
+       u8 peer_commit_element[2 * 32];
        u8 pwe[2 * 32];
        u8 sae_rand[32];
 };
@@ -23,6 +27,8 @@ struct sae_data {
 int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
                       const u8 *password, size_t password_len,
                       struct sae_data *sae);
+int sae_process_commit(struct sae_data *sae);
 void sae_write_commit(struct sae_data *sae, struct wpabuf *buf);
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len);
 
 #endif /* SAE_H */
index b3afa704663552d44e205e9a0cf39c98cc11928d..a5a90b5f8839a4de5d9c7ddb6fcb5e31f5181a20 100644 (file)
@@ -401,24 +401,6 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_SAE
 
-static int sme_sae_process_commit(struct wpa_supplicant *wpa_s, const u8 *data,
-                                 size_t len)
-{
-       /* Check Finite Cyclic Group */
-       if (len < 2)
-               return -1;
-       if (WPA_GET_LE16(data) != 19) {
-               wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
-                          WPA_GET_LE16(data));
-               return -1;
-       }
-
-       /* TODO */
-
-       return 0;
-}
-
-
 static int sme_sae_process_confirm(struct wpa_supplicant *wpa_s, const u8 *data,
                                   size_t len)
 {
@@ -451,8 +433,16 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        return -1;
                if (wpa_s->sme.sae.state != SAE_COMMIT)
                        return -1;
-               if (sme_sae_process_commit(wpa_s, data, len) < 0)
+               if (sae_parse_commit(&wpa_s->sme.sae, data, len) !=
+                   WLAN_STATUS_SUCCESS)
                        return -1;
+
+               if (sae_process_commit(&wpa_s->sme.sae) < 0) {
+                       wpa_printf(MSG_DEBUG, "SAE: Failed to process peer "
+                                  "commit");
+                       return -1;
+               }
+
                sme_send_authentication(wpa_s, wpa_s->current_bss,
                                        wpa_s->current_ssid, 0);
                return 0;