From 922506105e18d93d82328f8affb2ea49b5a55fd5 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 17 Jan 2019 12:25:38 -0800 Subject: [PATCH] owe: allow group 20 + group negotiation ELL ECC supports group 20 (P384) so OWE can also support it. This also adds group negotiation, where OWE can choose a different group than the default if the AP requests it. A check needed to be added in netdev in order for the negotiation to work. The RFC says that if a group is not supported association should be rejected with code 77 (unsupported finite cyclic group) and association should be started again. This rejection was causing a connect event to be emitted by the kernel (in addition to an associate event) which would result in netdev terminating the connection, which we didn't want. Since OWE receives the rejected associate event it can intelligently decide whether it really wants to terminate (out of supported groups) or try the next available group. This also utilizes the new MIC/KEK/KCK length changes, since OWE dictates the lengths of those keys. --- src/netdev.c | 13 +++++++ src/owe.c | 105 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 91 insertions(+), 27 deletions(-) diff --git a/src/netdev.c b/src/netdev.c index a3492d1e..c1568604 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -1830,6 +1830,19 @@ static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev) return; error: + /* + * RFC 8110 Section 4.3 - OWE Association + * A client that receives an 802.11 association response with a status + * code of seventy-seven SHOULD retry OWE with a different supported + * group... + * + * Note: OWE (should have) received this already in an associate + * response and will handle it. + */ + if (netdev->owe && *status_code == + MMPDU_REASON_CODE_UNSUPP_FINITE_CYCLIC_GROUP) + return; + netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED; netdev_connect_failed(NULL, netdev); } diff --git a/src/owe.c b/src/owe.c index 54d8834f..d61dc929 100644 --- a/src/owe.c +++ b/src/owe.c @@ -28,18 +28,14 @@ #include "owe.h" #include "mpdu.h" -/* - * TODO: Once other groups are added, this will need to be dynamic. OWE does - * support retries with different groups, but this is not yet implemented since - * only group 19 is supported. - */ -#define OWE_DEFAULT_GROUP 19 - struct owe_sm { struct handshake_state *hs; const struct l_ecc_curve *curve; struct l_ecc_scalar *private; struct l_ecc_point *public_key; + uint8_t retry; + uint16_t group; + const unsigned int *ecc_groups; owe_tx_authenticate_func_t auth_tx; owe_tx_associate_func_t assoc_tx; @@ -47,6 +43,30 @@ struct owe_sm { void *user_data; }; +static bool owe_reset(struct owe_sm *owe) +{ + /* + * Reset OWE with a different curve group and generate a new key pair + */ + if (owe->ecc_groups[owe->retry] == 0) + return false; + + owe->group = owe->ecc_groups[owe->retry]; + owe->curve = l_ecc_curve_get_ike_group(owe->group); + + if (owe->private) + l_ecc_scalar_free(owe->private); + + if (owe->public_key) + l_ecc_point_free(owe->public_key); + + if (!l_ecdh_generate_key_pair(owe->curve, &owe->private, + &owe->public_key)) + return false; + + return true; +} + struct owe_sm *owe_sm_new(struct handshake_state *hs, owe_tx_authenticate_func_t auth, owe_tx_associate_func_t assoc, @@ -59,10 +79,9 @@ struct owe_sm *owe_sm_new(struct handshake_state *hs, owe->assoc_tx = assoc; owe->user_data = user_data; owe->complete = complete; - owe->curve = l_ecc_curve_get_ike_group(OWE_DEFAULT_GROUP); + owe->ecc_groups = l_ecc_curve_get_supported_ike_groups(); - if (!l_ecdh_generate_key_pair(owe->curve, &owe->private, - &owe->public_key)) { + if (!owe_reset(owe)) { l_free(owe); return NULL; } @@ -105,7 +124,7 @@ void owe_rx_authenticate(struct owe_sm *owe) */ buf[0] = IE_TYPE_EXTENSION; buf[2] = IE_TYPE_OWE_DH_PARAM - 256; - l_put_le16(OWE_DEFAULT_GROUP, buf + 3); /* group */ + l_put_le16(owe->group, buf + 3); /* group */ len = l_ecc_point_get_x(owe->public_key, buf + 5, L_ECC_SCALAR_MAX_BYTES); buf[1] = 3 + len; /* length */ @@ -125,13 +144,16 @@ static bool owe_compute_keys(struct owe_sm *owe, const void *public_key, { struct l_ecc_scalar *shared_secret; uint8_t ss_buf[L_ECC_SCALAR_MAX_BYTES]; - uint8_t prk[32]; - uint8_t pmk[32]; + uint8_t prk[L_ECC_SCALAR_MAX_BYTES]; + uint8_t pmk[L_ECC_SCALAR_MAX_BYTES]; uint8_t pmkid[16]; - uint8_t key[32 + 32 + 2]; + uint8_t key[L_ECC_SCALAR_MAX_BYTES + L_ECC_SCALAR_MAX_BYTES + 2]; + uint8_t *ptr = key; struct iovec iov[2]; struct l_checksum *sha; struct l_ecc_point *other_public; + ssize_t nbytes; + enum l_checksum_type type; other_public = l_ecc_point_from_data(owe->curve, L_ECC_POINT_TYPE_COMPLIANT, @@ -148,30 +170,43 @@ static bool owe_compute_keys(struct owe_sm *owe, const void *public_key, l_ecc_point_free(other_public); - l_ecc_scalar_get_data(shared_secret, ss_buf, sizeof(ss_buf)); + nbytes = l_ecc_scalar_get_data(shared_secret, ss_buf, sizeof(ss_buf)); l_ecc_scalar_free(shared_secret); - l_ecc_point_get_x(owe->public_key, key, sizeof(key)); - memcpy(key + 32, public_key, 32); - l_put_le16(OWE_DEFAULT_GROUP, key + 64); + ptr += l_ecc_point_get_x(owe->public_key, ptr, sizeof(key)); + memcpy(ptr, public_key, nbytes); + ptr += nbytes; + l_put_le16(owe->group, ptr); + ptr += 2; + + switch (owe->group) { + case 19: + type = L_CHECKSUM_SHA256; + break; + case 20: + type = L_CHECKSUM_SHA384; + break; + default: + goto failed; + } /* prk = HKDF-extract(C | A | group, z) */ - if (!hkdf_extract(L_CHECKSUM_SHA256, key, 66, 1, prk, ss_buf, 32)) + if (!hkdf_extract(type, key, ptr - key, 1, prk, ss_buf, nbytes)) goto failed; /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ - if (!hkdf_expand(L_CHECKSUM_SHA256, prk, 32, "OWE Key Generation", - strlen("OWE Key Generation"), pmk, 32)) + if (!hkdf_expand(type, prk, nbytes, "OWE Key Generation", + strlen("OWE Key Generation"), pmk, nbytes)) goto failed; - sha = l_checksum_new(L_CHECKSUM_SHA256); + sha = l_checksum_new(type); /* PMKID = Truncate-128(Hash(C | A)) */ - iov[0].iov_base = key; /* first 32 bytes of key are owe->public_key */ - iov[0].iov_len = 32; + iov[0].iov_base = key; /* first nbytes of key are owe->public_key */ + iov[0].iov_len = nbytes; iov[1].iov_base = (void *) public_key; - iov[1].iov_len = 32; + iov[1].iov_len = nbytes; l_checksum_updatev(sha, iov, 2); @@ -179,7 +214,7 @@ static bool owe_compute_keys(struct owe_sm *owe, const void *public_key, l_checksum_free(sha); - handshake_state_set_pmk(owe->hs, pmk, 32); + handshake_state_set_pmk(owe->hs, pmk, nbytes); handshake_state_set_pmkid(owe->hs, pmkid); return true; @@ -209,6 +244,22 @@ void owe_rx_associate(struct owe_sm *owe, const uint8_t *frame, size_t len) body = mmpdu_body(mpdu); + if (body->status_code == MMPDU_REASON_CODE_UNSUPP_FINITE_CYCLIC_GROUP) { + /* retry with another group, if possible */ + owe->retry++; + + if (!owe_reset(owe)) { + owe->complete(body->status_code, owe->user_data); + return; + } + + l_debug("OWE retrying with group %u", owe->group); + + owe_start(owe); + + return; + } + ie_tlv_iter_init(&iter, body->ies, (const uint8_t *) mpdu + len - body->ies); @@ -254,7 +305,7 @@ void owe_rx_associate(struct owe_sm *owe, const uint8_t *frame, size_t len) goto owe_failed; } - if (l_get_le16(owe_dh) != 19) { + if (l_get_le16(owe_dh) != owe->group) { l_error("associate response contained unsupported group %u", l_get_le16(owe_dh)); goto owe_failed;