From 75f1ceeb6426c93a449162148034fbee0ee68984 Mon Sep 17 00:00:00 2001 From: Zhang Jun Hao Date: Mon, 25 May 2020 21:36:16 +0800 Subject: [PATCH] feat(wpa_supplicant): add PMF and WPA3 support --- .../port/esp_supplicant/esp_wifi_driver.h | 84 + .../port/esp_supplicant/esp_wpa3.c | 240 +++ .../port/esp_supplicant/esp_wpa3_i.h | 28 + components/wpa_supplicant/src/common/sae.c | 1453 +++++++++++++++++ components/wpa_supplicant/src/common/sae.h | 89 + .../wpa_supplicant/src/crypto/aes-ccm.c | 215 +++ .../wpa_supplicant/src/crypto/aes-omac1.c | 169 ++ components/wpa_supplicant/src/crypto/ccmp.c | 354 ++++ components/wpa_supplicant/src/crypto/ccmp.h | 28 + .../src/crypto/crypto_mbedtls-bignum.c | 273 ++++ .../src/crypto/crypto_mbedtls-ec.c | 988 +++++++++++ .../wpa_supplicant/src/rsn_supp/pmksa_cache.c | 522 ++++++ .../wpa_supplicant/src/rsn_supp/pmksa_cache.h | 134 ++ 13 files changed, 4577 insertions(+) create mode 100644 components/wpa_supplicant/port/esp_supplicant/esp_wifi_driver.h create mode 100644 components/wpa_supplicant/port/esp_supplicant/esp_wpa3.c create mode 100644 components/wpa_supplicant/port/esp_supplicant/esp_wpa3_i.h create mode 100644 components/wpa_supplicant/src/common/sae.c create mode 100644 components/wpa_supplicant/src/common/sae.h create mode 100644 components/wpa_supplicant/src/crypto/aes-ccm.c create mode 100644 components/wpa_supplicant/src/crypto/aes-omac1.c create mode 100644 components/wpa_supplicant/src/crypto/ccmp.c create mode 100644 components/wpa_supplicant/src/crypto/ccmp.h create mode 100644 components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c create mode 100644 components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c create mode 100644 components/wpa_supplicant/src/rsn_supp/pmksa_cache.c create mode 100644 components/wpa_supplicant/src/rsn_supp/pmksa_cache.h diff --git a/components/wpa_supplicant/port/esp_supplicant/esp_wifi_driver.h b/components/wpa_supplicant/port/esp_supplicant/esp_wifi_driver.h new file mode 100644 index 00000000..704624bf --- /dev/null +++ b/components/wpa_supplicant/port/esp_supplicant/esp_wifi_driver.h @@ -0,0 +1,84 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP_WIFI_DRIVER_H_ +#define _ESP_WIFI_DRIVER_H_ + +#include "esp_err.h" +#include "esp_wifi.h" + +enum { + NONE_AUTH = 0x01, + WPA_AUTH_UNSPEC = 0x02, + WPA_AUTH_PSK = 0x03, + WPA2_AUTH_ENT = 0x04, + WPA2_AUTH_PSK = 0x05, + WPA_AUTH_CCKM = 0x06, + WPA2_AUTH_CCKM = 0x07, + WPA2_AUTH_PSK_SHA256= 0x08, + WPA3_AUTH_PSK = 0x09, + WPA2_AUTH_INVALID = 0x0a, +}; + +struct wifi_ssid { + int len; + uint8_t ssid[32]; +}; + +#define WPA_IGTK_LEN 16 +typedef struct { + uint8_t keyid[2]; + uint8_t pn[6]; + uint8_t igtk[WPA_IGTK_LEN]; +} wifi_wpa_igtk_t; + +/*wpa_auth.c*/ +uint8_t *esp_wifi_ap_get_prof_pmk_internal(void); +void *esp_wifi_get_hostap_private_internal(void); +int esp_wifi_set_ap_key_internal(int alg, const u8 *addr, int idx, u8 *key, size_t key_len); +bool esp_wifi_wpa_ptk_init_done_internal(uint8_t *mac); + +/*wpa.c*/ + +bool esp_wifi_sta_is_ap_notify_completed_rsne_internal(void); +struct wifi_ssid *esp_wifi_sta_get_prof_ssid_internal(void); +uint8_t *esp_wifi_sta_get_prof_password_internal(void); +uint8_t *esp_wifi_sta_get_prof_pmk_internal(void); +uint8_t esp_wifi_sta_get_reset_param_internal(void); +uint8_t esp_wifi_sta_set_reset_param_internal(uint8_t reset_flag); +int esp_wifi_sta_update_ap_info_internal(void); +bool esp_wifi_sta_is_running_internal(void); + +int esp_wifi_get_macaddr_internal(uint8_t if_index, uint8_t *macaddr); +uint8_t *esp_wifi_sta_get_ap_info_prof_pmk_internal(void); + +/*wpa_main.c*/ +int esp_wifi_set_sta_key_internal(int alg, u8 *addr, int key_idx, int set_tx, + u8 *seq, size_t seq_len, u8 *key, size_t key_len, int key_entry_valid); +int esp_wifi_get_sta_key_internal(uint8_t *ifx, int *alg, u8 *addr, int *key_idx, + u8 *key, size_t key_len, int key_entry_valid); +uint8_t esp_wifi_sta_get_prof_authmode_internal(void); +bool esp_wifi_sta_prof_is_wpa_internal(void); +bool esp_wifi_sta_prof_is_wpa2_internal(void); +bool esp_wifi_sta_prof_is_wpa3_internal(void); +void esp_wifi_deauthenticate_internal(u8 reason_code); +uint8_t esp_wifi_sta_get_pairwise_cipher_internal(void); +uint8_t esp_wifi_sta_get_group_cipher_internal(void); +int esp_wifi_set_appie_internal(uint8_t type, uint8_t *ie, uint16_t len, uint8_t flag); +bool esp_wifi_auth_done_internal(void); +uint16_t esp_wifi_sta_pmf_enabled(void); +wifi_cipher_type_t esp_wifi_sta_get_mgmt_group_cipher(void); +int esp_wifi_set_igtk_internal(uint8_t if_index, const wifi_wpa_igtk_t *igtk); + +#endif diff --git a/components/wpa_supplicant/port/esp_supplicant/esp_wpa3.c b/components/wpa_supplicant/port/esp_supplicant/esp_wpa3.c new file mode 100644 index 00000000..3332810f --- /dev/null +++ b/components/wpa_supplicant/port/esp_supplicant/esp_wpa3.c @@ -0,0 +1,240 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef CONFIG_WPA3_SAE + +#include "common/sae.h" +#include "common/ieee802_11_defs.h" +#include "esp_wifi_driver.h" +#include "rsn_supp/wpa.h" + +static struct sae_data g_sae_data; +static struct wpabuf *g_sae_token = NULL; +static struct wpabuf *g_sae_commit = NULL; +static struct wpabuf *g_sae_confirm = NULL; +int g_allowed_groups[] = { IANA_SECP256R1, 0 }; + +static esp_err_t wpa3_build_sae_commit(u8 *bssid) +{ + int default_group = IANA_SECP256R1; + u32 len = 0; + u8 own_addr[ETH_ALEN]; + const u8 *pw; + + if (wpa_sta_is_cur_pmksa_set()) { + wpa_printf(MSG_INFO, "wpa3: Skip SAE and use cached PMK instead"); + return ESP_FAIL; + } + + if (g_sae_commit) { + wpabuf_free(g_sae_commit); + g_sae_commit = NULL; + } + + if (g_sae_token) { + len = wpabuf_len(g_sae_token); + goto reuse_data; + } + + memset(&g_sae_data, 0, sizeof(g_sae_data)); + if (sae_set_group(&g_sae_data, default_group)) { + wpa_printf(MSG_ERROR, "wpa3: could not set SAE group %d", default_group); + return ESP_FAIL; + } + + esp_wifi_get_macaddr_internal(WIFI_IF_STA, own_addr); + if (!bssid) { + wpa_printf(MSG_ERROR, "wpa3: cannot prepare SAE commit with no BSSID!"); + return ESP_FAIL; + } + + pw = (const u8 *)esp_wifi_sta_get_prof_password_internal(); + if (sae_prepare_commit(own_addr, bssid, pw, strlen((const char *)pw), NULL, &g_sae_data) < 0) { + wpa_printf(MSG_ERROR, "wpa3: failed to prepare SAE commit!"); + return ESP_FAIL; + } + +reuse_data: + len += SAE_COMMIT_MAX_LEN; + g_sae_commit = wpabuf_alloc(len); + if (!g_sae_commit) { + wpa_printf(MSG_ERROR, "wpa3: failed to allocate buffer for commit msg"); + return ESP_FAIL; + } + + if (sae_write_commit(&g_sae_data, g_sae_commit, g_sae_token, NULL) != ESP_OK) { + wpa_printf(MSG_ERROR, "wpa3: failed to write SAE commit msg"); + wpabuf_free(g_sae_commit); + g_sae_commit = NULL; + return ESP_FAIL; + } + + if (g_sae_token) { + wpabuf_free(g_sae_token); + g_sae_token = NULL; + } + g_sae_data.state = SAE_COMMITTED; + + return ESP_OK; +} + +static esp_err_t wpa3_build_sae_confirm(void) +{ + if (g_sae_data.state != SAE_COMMITTED) + return ESP_FAIL; + + if (g_sae_confirm) { + wpabuf_free(g_sae_confirm); + g_sae_confirm = NULL; + } + + g_sae_confirm = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + if (!g_sae_confirm) { + wpa_printf(MSG_ERROR, "wpa3: failed to allocate buffer for confirm msg"); + return ESP_FAIL; + } + + if (sae_write_confirm(&g_sae_data, g_sae_confirm) != ESP_OK) { + wpa_printf(MSG_ERROR, "wpa3: failed to write SAE confirm msg"); + wpabuf_free(g_sae_confirm); + g_sae_confirm = NULL; + return ESP_FAIL; + } + g_sae_data.state = SAE_CONFIRMED; + + return ESP_OK; +} + +void esp_wpa3_free_sae_data(void) +{ + if (g_sae_commit) { + wpabuf_free(g_sae_commit); + g_sae_commit = NULL; + } + + if (g_sae_confirm) { + wpabuf_free(g_sae_confirm); + g_sae_confirm = NULL; + } + sae_clear_data(&g_sae_data); +} + +u8 *wpa3_build_sae_msg(u8 *bssid, u32 sae_msg_type, u32 *sae_msg_len) +{ + u8 *buf = NULL; + + switch (sae_msg_type) { + case SAE_MSG_COMMIT: + if (ESP_OK != wpa3_build_sae_commit(bssid)) + return NULL; + *sae_msg_len = (u32)wpabuf_len(g_sae_commit); + buf = wpabuf_mhead_u8(g_sae_commit); + break; + case SAE_MSG_CONFIRM: + if (ESP_OK != wpa3_build_sae_confirm()) + return NULL; + *sae_msg_len = (u32)wpabuf_len(g_sae_confirm); + buf = wpabuf_mhead_u8(g_sae_confirm); + break; + default: + break; + } + + return buf; +} + +static int wpa3_parse_sae_commit(u8 *buf, u32 len, u16 status) +{ + int ret; + + if (g_sae_data.state != SAE_COMMITTED) { + wpa_printf(MSG_ERROR, "wpa3: failed to parse SAE commit in state(%d)!", + g_sae_data.state); + return ESP_FAIL; + } + + if (status == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ) { + if (g_sae_token) + wpabuf_free(g_sae_token); + g_sae_token = wpabuf_alloc_copy(buf + 2, len - 2); + return ESP_OK; + } + + ret = sae_parse_commit(&g_sae_data, buf, len, NULL, 0, g_allowed_groups); + if (ret) { + wpa_printf(MSG_ERROR, "wpa3: could not parse commit(%d)", ret); + return ret; + } + + ret = sae_process_commit(&g_sae_data); + if (ret) { + wpa_printf(MSG_ERROR, "wpa3: could not process commit(%d)", ret); + return ret; + } + + return ESP_OK; +} + +static int wpa3_parse_sae_confirm(u8 *buf, u32 len) +{ + if (g_sae_data.state != SAE_CONFIRMED) { + wpa_printf(MSG_ERROR, "wpa3: failed to parse SAE commit in state(%d)!", + g_sae_data.state); + return ESP_FAIL; + } + + if (sae_check_confirm(&g_sae_data, buf, len) != ESP_OK) { + wpa_printf(MSG_ERROR, "wpa3: failed to parse SAE confirm"); + return ESP_FAIL; + } + g_sae_data.state = SAE_ACCEPTED; + + wpa_set_pmk(g_sae_data.pmk, g_sae_data.pmkid, true); + memcpy(esp_wifi_sta_get_ap_info_prof_pmk_internal(), g_sae_data.pmk, PMK_LEN); + + return ESP_OK; +} + +int wpa3_parse_sae_msg(u8 *buf, u32 len, u32 sae_msg_type, u16 status) +{ + int ret = ESP_OK; + + switch (sae_msg_type) { + case SAE_MSG_COMMIT: + ret = wpa3_parse_sae_commit(buf, len, status); + break; + case SAE_MSG_CONFIRM: + ret = wpa3_parse_sae_confirm(buf, len); + esp_wpa3_free_sae_data(); + break; + default: + wpa_printf(MSG_ERROR, "wpa3: Invalid SAE msg type(%d)!", sae_msg_type); + ret = ESP_FAIL; + break; + } + + return ret; +} +#else +u8 *wpa3_build_sae_msg(u8 *bssid, u32 sae_msg_type, u32 *sae_msg_len) +{ + return NULL; +} + +int wpa3_parse_sae_msg(u8 *buf, u32 len, u32 sae_msg_type, u16 status) +{ + return ESP_FAIL; +} + +#endif /* CONFIG_WPA3_SAE */ diff --git a/components/wpa_supplicant/port/esp_supplicant/esp_wpa3_i.h b/components/wpa_supplicant/port/esp_supplicant/esp_wpa3_i.h new file mode 100644 index 00000000..694fc422 --- /dev/null +++ b/components/wpa_supplicant/port/esp_supplicant/esp_wpa3_i.h @@ -0,0 +1,28 @@ + +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ESP_WPA3_H +#define ESP_WPA3_H + +#include "esp_wifi_driver.h" + +#ifdef CONFIG_WPA3_SAE + + +#else /* CONFIG_WPA3_SAE */ + + +#endif /* CONFIG_WPA3_SAE */ +#endif /* ESP_WPA3_H */ diff --git a/components/wpa_supplicant/src/common/sae.c b/components/wpa_supplicant/src/common/sae.c new file mode 100644 index 00000000..e63aa759 --- /dev/null +++ b/components/wpa_supplicant/src/common/sae.c @@ -0,0 +1,1453 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef CONFIG_WPA3_SAE + +#include "utils/includes.h" +#include "utils/common.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "crypto/dh_groups.h" +#include "ieee802_11_defs.h" +#include "sae.h" +#include "esp_wifi_crypto_types.h" + +/*TBD Move the this api to proper files once they are taken out of lib*/ +void wpabuf_clear_free(struct wpabuf *buf) +{ + if (buf) { + os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf)); + wpabuf_free(buf); + } +} + +int sae_set_group(struct sae_data *sae, int group) +{ + struct sae_temporary_data *tmp; + + sae_clear_data(sae); + tmp = sae->tmp = os_zalloc(sizeof(*tmp)); + if (tmp == NULL) + return ESP_FAIL; + + /* First, check if this is an ECC group */ + tmp->ec = crypto_ec_init(group); + if (tmp->ec) { + wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", + group); + sae->group = group; + tmp->prime_len = crypto_ec_prime_len(tmp->ec); + tmp->prime = crypto_ec_get_prime(tmp->ec); + tmp->order = crypto_ec_get_order(tmp->ec); + return ESP_OK; + } + + /* Not an ECC group, check FFC */ + tmp->dh = dh_groups_get(group); + if (tmp->dh) { + wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", + group); + sae->group = group; + tmp->prime_len = tmp->dh->prime_len; + if (tmp->prime_len > SAE_MAX_PRIME_LEN) { + sae_clear_data(sae); + return ESP_FAIL; + } + + tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, + tmp->prime_len); + if (tmp->prime_buf == NULL) { + sae_clear_data(sae); + return ESP_FAIL; + } + tmp->prime = tmp->prime_buf; + + tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, + tmp->dh->order_len); + if (tmp->order_buf == NULL) { + sae_clear_data(sae); + return ESP_FAIL; + } + tmp->order = tmp->order_buf; + + return ESP_OK; + } + + /* Unsupported group */ + wpa_printf(MSG_DEBUG, + "SAE: Group %d not supported by the crypto library", group); + return ESP_FAIL; +} + +void sae_clear_temp_data(struct sae_data *sae) +{ + struct sae_temporary_data *tmp; + if (sae == NULL || sae->tmp == NULL) + return; + tmp = sae->tmp; + crypto_ec_deinit(tmp->ec); + crypto_bignum_deinit(tmp->prime_buf, 0); + crypto_bignum_deinit(tmp->order_buf, 0); + crypto_bignum_deinit(tmp->sae_rand, 1); + crypto_bignum_deinit(tmp->pwe_ffc, 1); + crypto_bignum_deinit(tmp->own_commit_scalar, 0); + crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); + crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); + crypto_ec_point_deinit(tmp->pwe_ecc, 1); + crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); + crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); + os_free(tmp->pw_id); + bin_clear_free(tmp, sizeof(*tmp)); + sae->tmp = NULL; +} + +void sae_clear_data(struct sae_data *sae) +{ + if (sae == NULL) + return; + sae_clear_temp_data(sae); + crypto_bignum_deinit(sae->peer_commit_scalar, 0); + os_memset(sae, 0, sizeof(*sae)); +} + +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; +} + +static struct crypto_bignum * sae_get_rand(struct sae_data *sae) +{ + u8 val[SAE_MAX_PRIME_LEN]; + int iter = 0; + struct crypto_bignum *bn = NULL; + int order_len_bits = crypto_bignum_bits(sae->tmp->order); + size_t order_len = (order_len_bits + 7) / 8; + + if (order_len > sizeof(val)) + return NULL; + + for (;;) { + if (iter++ > 100 || random_get_bytes(val, order_len) < 0) + return NULL; + if (order_len_bits % 8) + buf_shift_right(val, order_len, 8 - order_len_bits % 8); + bn = crypto_bignum_init_set(val, order_len); + if (bn == NULL) + return NULL; + if (crypto_bignum_is_zero(bn) || + crypto_bignum_is_one(bn) || + crypto_bignum_cmp(bn, sae->tmp->order) >= 0) { + crypto_bignum_deinit(bn, 0); + continue; + } + break; + } + + os_memset(val, 0, order_len); + return bn; +} + +static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) +{ + crypto_bignum_deinit(sae->tmp->sae_rand, 1); + sae->tmp->sae_rand = sae_get_rand(sae); + if (sae->tmp->sae_rand == NULL) + return NULL; + return sae_get_rand(sae); +} + +static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) +{ + wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR + " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); + if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { + os_memcpy(key, addr1, ETH_ALEN); + os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); + } else { + os_memcpy(key, addr2, ETH_ALEN); + os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); + } +} + +static struct crypto_bignum * +get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, + int *r_odd) +{ + for (;;) { + struct crypto_bignum *r; + u8 tmp[SAE_MAX_ECC_PRIME_LEN]; + + if (random_get_bytes(tmp, prime_len) < 0) + break; + if (prime_bits % 8) + buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); + if (os_memcmp(tmp, prime, prime_len) >= 0) + continue; + r = crypto_bignum_init_set(tmp, prime_len); + if (!r) + break; + if (crypto_bignum_is_zero(r)) { + crypto_bignum_deinit(r, 0); + continue; + } + + *r_odd = tmp[prime_len - 1] & 0x01; + return r; + } + + return NULL; +} + +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 struct crypto_bignum *y_sqr) +{ + struct crypto_bignum *r, *num; + int r_odd, check, res = -1; + + /* + * Use the blinding technique to mask y_sqr while determining + * whether it is a quadratic residue modulo p to avoid leaking + * timing information while determining the Legendre symbol. + * + * v = y_sqr + * 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); + if (!r) + return ESP_FAIL; + + num = crypto_bignum_init(); + if (!num || + crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || + 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; + } + + res = crypto_bignum_legendre(num, sae->tmp->prime); + if (res == -2) { + res = -1; + goto fail; + } + res = res == check; +fail: + crypto_bignum_deinit(num, 1); + crypto_bignum_deinit(r, 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, + struct crypto_bignum **ret_x_cand) +{ + 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) */ + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + prime, sae->tmp->prime_len, pwd_value, bits) < 0) + return ESP_FAIL; + if (bits % 8) + buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", + pwd_value, sae->tmp->prime_len); + + if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) + return ESP_OK; + + x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (!x_cand) + return ESP_FAIL; + y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); + if (!y_sqr) { + crypto_bignum_deinit(x_cand, 1); + return ESP_FAIL; + } + + 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; +} + +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; + + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); + + /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ + if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, + bits) < 0) + return ESP_FAIL; + 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 ESP_OK; + } + + /* PWE = pwd-value^((p-1)/r) modulo p */ + + a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + + if (sae->tmp->dh->safe_prime) { + /* + * r = (p-1)/2 for the group used here, so this becomes: + * PWE = pwd-value^2 modulo p + */ + exp[0] = 2; + b = crypto_bignum_init_set(exp, sizeof(exp)); + } else { + /* Calculate exponent: (p-1)/r */ + exp[0] = 1; + 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; + } + } + + if (a == NULL || b == NULL) + res = -1; + else + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); + + crypto_bignum_deinit(a, 0); + crypto_bignum_deinit(b, 0); + + if (res < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); + return ESP_FAIL; + } + + /* if (PWE > 1) --> found */ + if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { + wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); + return ESP_OK; + } + + wpa_printf(MSG_DEBUG, "SAE: PWE found"); + return 1; +} + +static int get_random_qr_qnr(const u8 *prime, size_t prime_len, + const struct crypto_bignum *prime_bn, + size_t prime_bits, struct crypto_bignum **qr, + struct crypto_bignum **qnr) +{ + *qr = NULL; + *qnr = NULL; + + while (!(*qr) || !(*qnr)) { + u8 tmp[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *q; + int res; + + if (random_get_bytes(tmp, prime_len) < 0) + break; + if (prime_bits % 8) + buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); + if (os_memcmp(tmp, prime, prime_len) >= 0) + continue; + q = crypto_bignum_init_set(tmp, prime_len); + if (!q) + break; + res = crypto_bignum_legendre(q, prime_bn); + + if (res == 1 && !(*qr)) + *qr = q; + else if (res == -1 && !(*qnr)) + *qnr = q; + else + crypto_bignum_deinit(q, 0); + } + + return (*qr && *qnr) ? 0 : -1; +} + +static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, + const u8 *addr2, const u8 *password, + size_t password_len, const char *identifier) +{ + u8 counter, k = 40; + u8 addrs[2 * ETH_ALEN]; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem; + u8 dummy_password[32]; + size_t dummy_password_len; + int pwd_seed_odd = 0; + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + size_t prime_len; + struct crypto_bignum *x = NULL, *qr, *qnr; + size_t bits; + int res; + + dummy_password_len = password_len; + if (dummy_password_len > sizeof(dummy_password)) + dummy_password_len = sizeof(dummy_password); + if (random_get_bytes(dummy_password, dummy_password_len) < 0) + return ESP_FAIL; + + prime_len = sae->tmp->prime_len; + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + prime_len) < 0) + return ESP_FAIL; + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + + /* + * Create a random quadratic residue (qr) and quadratic non-residue + * (qnr) modulo p for blinding purposes during the loop. + */ + if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, + &qr, &qnr) < 0) + return ESP_FAIL; + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + if (identifier) + wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", + identifier); + + /* + * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * base = password [|| identifier] + * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), + * base || counter) + */ + sae_pwd_seed_key(addr1, addr2, addrs); + + addr[0] = password; + len[0] = password_len; + num_elem = 1; + if (identifier) { + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + addr[num_elem] = &counter; + len[num_elem] = sizeof(counter); + num_elem++; + + /* + * Continue for at least k iterations to protect against side-channel + * attacks that attempt to determine the number of iterations required + * in the loop. + */ + for (counter = 1; counter <= k || !x; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; + struct crypto_bignum *x_cand; + + if (counter > 200) { + /* This should not happen in practice */ + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); + break; + } + + wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + 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); + if (res < 0) + goto fail; + if (res > 0 && !x) { + wpa_printf(MSG_DEBUG, + "SAE: Selected pwd-seed with counter %u", + counter); + x = x_cand; + pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; + os_memset(pwd_seed, 0, sizeof(pwd_seed)); + + /* + * Use a dummy password for the following rounds, if + * any. + */ + addr[0] = dummy_password; + len[0] = dummy_password_len; + } else if (res > 0) { + crypto_bignum_deinit(x_cand, 1); + } + } + + if (!x) { + wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); + res = -1; + goto fail; + } + + if (!sae->tmp->pwe_ecc) + sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->pwe_ecc) + res = -1; + else + 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 + * is a result. + */ + wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); + } + +fail: + crypto_bignum_deinit(qr, 0); + crypto_bignum_deinit(qnr, 0); + + return res; +} + +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 addrs[2 * ETH_ALEN]; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem; + int found = 0; + + if (sae->tmp->pwe_ffc == NULL) { + sae->tmp->pwe_ffc = crypto_bignum_init(); + if (sae->tmp->pwe_ffc == NULL) + return ESP_FAIL; + } + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + + /* + * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), + * password [|| identifier] || counter) + */ + sae_pwd_seed_key(addr1, addr2, addrs); + + addr[0] = password; + len[0] = password_len; + num_elem = 1; + if (identifier) { + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + addr[num_elem] = &counter; + len[num_elem] = sizeof(counter); + num_elem++; + + for (counter = 1; !found; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; + int res; + + if (counter > 200) { + /* This should not happen in practice */ + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); + break; + } + + wpa_printf(MSG_DEBUG, "SAE: counter = %u", 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 (res < 0) + break; + if (res > 0) { + wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); + found = 1; + } + } + + return found ? 0 : -1; +} + +static int sae_derive_commit_element_ecc(struct sae_data *sae, + struct crypto_bignum *mask) +{ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ecc) { + sae->tmp->own_commit_element_ecc = + crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->own_commit_element_ecc) + return ESP_FAIL; + } + + if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, + sae->tmp->own_commit_element_ecc) < 0 || + crypto_ec_point_invert(sae->tmp->ec, + sae->tmp->own_commit_element_ecc) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); + return ESP_FAIL; + } + + return ESP_OK; +} + +static int sae_derive_commit_element_ffc(struct sae_data *sae, + struct crypto_bignum *mask) +{ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ffc) { + sae->tmp->own_commit_element_ffc = crypto_bignum_init(); + if (!sae->tmp->own_commit_element_ffc) + return ESP_FAIL; + } + + if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, + sae->tmp->own_commit_element_ffc) < 0 || + crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, + sae->tmp->prime, + sae->tmp->own_commit_element_ffc) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); + return ESP_FAIL; + } + + return ESP_OK; +} + +static int sae_derive_commit(struct sae_data *sae) +{ + struct crypto_bignum *mask; + int ret = -1; + unsigned int counter = 0; + + do { + counter++; + if (counter > 100) { + /* + * This cannot really happen in practice if the random + * number generator is working. Anyway, to avoid even a + * theoretical infinite loop, break out after 100 + * attemps. + */ + return ESP_FAIL; + } + + mask = sae_get_rand_and_mask(sae); + if (mask == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); + return ESP_FAIL; + } + + /* commit-scalar = (rand + mask) modulo r */ + if (!sae->tmp->own_commit_scalar) { + sae->tmp->own_commit_scalar = crypto_bignum_init(); + if (!sae->tmp->own_commit_scalar) + goto fail; + } + crypto_bignum_add(sae->tmp->sae_rand, mask, + sae->tmp->own_commit_scalar); + crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, + sae->tmp->own_commit_scalar); + } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || + crypto_bignum_is_one(sae->tmp->own_commit_scalar)); + + if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || + (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)) + goto fail; + + ret = 0; +fail: + crypto_bignum_deinit(mask, 1); + return ret; +} + +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, + const u8 *password, size_t password_len, + const char *identifier, struct sae_data *sae) +{ + if (sae->tmp == NULL || + (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, + password_len, + identifier) < 0) || + (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, + password_len, + identifier) < 0) || + sae_derive_commit(sae) < 0) + return ESP_FAIL; + return ESP_OK; +} + +static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) +{ + struct crypto_ec_point *K; + int ret = -1; + + K = crypto_ec_point_init(sae->tmp->ec); + if (K == NULL) + 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 (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, + sae->peer_commit_scalar, K) < 0 || + crypto_ec_point_add(sae->tmp->ec, K, + sae->tmp->peer_commit_element_ecc, K) < 0 || + crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || + crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || + crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); + + ret = 0; +fail: + crypto_ec_point_deinit(K, 1); + return ret; +} + +static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) +{ + struct crypto_bignum *K; + int ret = -1; + + K = crypto_bignum_init(); + if (K == NULL) + goto fail; + + /* + * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), + * PEER-COMMIT-ELEMENT))) + * If K is identity element (one), reject. + * k = F(K) (= x coordinate) + */ + + if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, + sae->tmp->prime, K) < 0 || + crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, + sae->tmp->prime, K) < 0 || + crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 + || + crypto_bignum_is_one(K) || + crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < + 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); + + ret = 0; +fail: + crypto_bignum_deinit(K, 1); + return ret; +} + +static int sae_derive_keys(struct sae_data *sae, const u8 *k) +{ + u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; + u8 keyseed[SHA256_MAC_LEN]; + u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; + struct crypto_bignum *tmp; + int ret = -1; + + tmp = crypto_bignum_init(); + if (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, sae->tmp->prime_len, + keyseed); + wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); + + crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, + tmp); + crypto_bignum_mod(tmp, sae->tmp->order, tmp); + crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); + if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", + val, sae->tmp->prime_len, keys, sizeof(keys)) < 0) + goto fail; + os_memset(keyseed, 0, sizeof(keyseed)); + os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); + os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); + os_memset(keys, 0, sizeof(keys)); + wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); + + ret = 0; +fail: + crypto_bignum_deinit(tmp, 0); + return ret; +} + +int sae_process_commit(struct sae_data *sae) +{ + u8 k[SAE_MAX_PRIME_LEN]; + if (sae->tmp == NULL || + (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || + (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || + sae_derive_keys(sae, k) < 0) + return ESP_FAIL; + return ESP_OK; +} + +int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, + const struct wpabuf *token, const char *identifier) +{ + u8 *pos; + + if (sae->tmp == NULL) + return ESP_FAIL; + + wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ + if (token) { + wpabuf_put_buf(buf, token); + wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", + wpabuf_head(token), wpabuf_len(token)); + } + pos = wpabuf_put(buf, sae->tmp->prime_len); + if (crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, + sae->tmp->prime_len, sae->tmp->prime_len) < 0) { + wpa_printf(MSG_ERROR, "SAE: failed bignum operation on own commit scalar"); + return ESP_FAIL; + } + wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", + pos, sae->tmp->prime_len); + if (sae->tmp->ec) { + pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); + if (crypto_ec_point_to_bin(sae->tmp->ec, + sae->tmp->own_commit_element_ecc, + pos, pos + sae->tmp->prime_len) < 0) { + wpa_printf(MSG_ERROR, "SAE: failed bignum op while deriving ec point"); + return ESP_FAIL; + } + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", + pos, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", + pos + sae->tmp->prime_len, sae->tmp->prime_len); + } else { + pos = wpabuf_put(buf, sae->tmp->prime_len); + if (crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, + sae->tmp->prime_len, sae->tmp->prime_len) < 0) { + wpa_printf(MSG_ERROR, "SAE: failed bignum operation on commit elem ffc"); + return ESP_FAIL; + } + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", + pos, sae->tmp->prime_len); + } + + if (identifier) { + /* Password Identifier element */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + os_strlen(identifier)); + wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); + wpabuf_put_str(buf, identifier); + wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", + identifier); + } + return ESP_OK; +} + +u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) +{ + if (allowed_groups) { + int i; + for (i = 0; allowed_groups[i] > 0; i++) { + if (allowed_groups[i] == group) + break; + } + if (allowed_groups[i] != group) { + wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " + "enabled in the current configuration", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + } + + if (sae->state == SAE_COMMITTED && group != sae->group) { + wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + if (group != sae->group && sae_set_group(sae, group) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + if (sae->tmp == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (sae->tmp->dh && !allowed_groups) { + wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " + "explicit configuration enabling it", group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + return WLAN_STATUS_SUCCESS; +} + +static int sae_is_password_id_elem(const u8 *pos, const u8 *end) +{ + int ret = end - pos >= 3 && + pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 && + end - pos - 2 >= pos[1] && + pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; + + return ret; +} + +static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, + const u8 *end, const u8 **token, + size_t *token_len) +{ + size_t scalar_elem_len, tlen; + const u8 *elem; + + if (token) + *token = NULL; + if (token_len) + *token_len = 0; + + scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; + if (scalar_elem_len >= (size_t) (end - *pos)) + return; /* No extra data beyond peer scalar and element */ + + /* It is a bit difficult to parse this now that there is an + * optional variable length Anti-Clogging Token field and + * optional variable length Password Identifier element in the + * frame. We are sending out fixed length Anti-Clogging Token + * fields, so use that length as a requirement for the received + * token and check for the presence of possible Password + * Identifier element based on the element header information. + */ + tlen = end - (*pos + scalar_elem_len); + + if (tlen < SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", + (unsigned int) tlen); + return; + } + + elem = *pos + scalar_elem_len; + if (sae_is_password_id_elem(elem, end)) { + /* Password Identifier element takes out all available + * extra octets, so there can be no Anti-Clogging token in + * this frame. */ + return; + } + + elem += SHA256_MAC_LEN; + if (sae_is_password_id_elem(elem, end)) { + /* Password Identifier element is included in the end, so + * remove its length from the Anti-Clogging token field. */ + tlen -= 2 + elem[1]; + } + + wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); + if (token) + *token = *pos; + if (token_len) + *token_len = tlen; + *pos += tlen; +} + +static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, + const u8 *end) +{ + struct crypto_bignum *peer_scalar; + + if (sae->tmp->prime_len > end - *pos) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); + if (peer_scalar == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* + * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for + * the peer and it is in Authenticated state, the new Commit Message + * shall be dropped if the peer-scalar is identical to the one used in + * the existing protocol instance. + */ + if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && + crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { + wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " + "peer-commit-scalar"); + crypto_bignum_deinit(peer_scalar, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* 1 < scalar < r */ + if (crypto_bignum_is_zero(peer_scalar) || + crypto_bignum_is_one(peer_scalar) || + crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); + crypto_bignum_deinit(peer_scalar, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + crypto_bignum_deinit(sae->peer_commit_scalar, 0); + sae->peer_commit_scalar = peer_scalar; + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", + *pos, sae->tmp->prime_len); + *pos += sae->tmp->prime_len; + + return WLAN_STATUS_SUCCESS; +} + +static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, + const u8 *end) +{ + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + + if (2 * sae->tmp->prime_len > end - *pos) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for " + "commit-element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + sae->tmp->prime_len) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* element x and y coordinates < p */ + if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || + os_memcmp(*pos + sae->tmp->prime_len, prime, + sae->tmp->prime_len) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " + "element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", + *pos, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", + *pos + sae->tmp->prime_len, sae->tmp->prime_len); + + crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); + sae->tmp->peer_commit_element_ecc = + crypto_ec_point_from_bin(sae->tmp->ec, *pos); + if (sae->tmp->peer_commit_element_ecc == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (!crypto_ec_point_is_on_curve(sae->tmp->ec, + sae->tmp->peer_commit_element_ecc)) { + wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + *pos += 2 * sae->tmp->prime_len; + + return WLAN_STATUS_SUCCESS; +} + +static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, + const u8 *end) +{ + struct crypto_bignum *res, *one; + const u8 one_bin[1] = { 0x01 }; + + if (sae->tmp->prime_len > end - *pos) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for " + "commit-element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, + sae->tmp->prime_len); + + crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); + sae->tmp->peer_commit_element_ffc = + crypto_bignum_init_set(*pos, sae->tmp->prime_len); + if (sae->tmp->peer_commit_element_ffc == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + /* 1 < element < p - 1 */ + res = crypto_bignum_init(); + one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); + if (!res || !one || + crypto_bignum_sub(sae->tmp->prime, one, res) || + crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || + crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || + crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { + crypto_bignum_deinit(res, 0); + crypto_bignum_deinit(one, 0); + wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + crypto_bignum_deinit(one, 0); + + /* scalar-op(r, ELEMENT) = 1 modulo p */ + if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, + sae->tmp->order, sae->tmp->prime, res) < 0 || + !crypto_bignum_is_one(res)) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); + crypto_bignum_deinit(res, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + crypto_bignum_deinit(res, 0); + + *pos += sae->tmp->prime_len; + + return WLAN_STATUS_SUCCESS; +} + +static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, + const u8 *end) +{ + if (sae->tmp->dh) + return sae_parse_commit_element_ffc(sae, pos, end); + return sae_parse_commit_element_ecc(sae, pos, end); +} + +static int sae_parse_password_identifier(struct sae_data *sae, + const u8 *pos, const u8 *end) +{ + wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", + pos, end - pos); + if (!sae_is_password_id_elem(pos, end)) { + if (sae->tmp->pw_id) { + wpa_printf(MSG_DEBUG, + "SAE: No Password Identifier included, but expected one (%s)", + sae->tmp->pw_id); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + os_free(sae->tmp->pw_id); + sae->tmp->pw_id = NULL; + return WLAN_STATUS_SUCCESS; /* No Password Identifier */ + } + + if (sae->tmp->pw_id && + (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) || + os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) { + wpa_printf(MSG_DEBUG, + "SAE: The included Password Identifier does not match the expected one (%s)", + sae->tmp->pw_id); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + + os_free(sae->tmp->pw_id); + sae->tmp->pw_id = os_malloc(pos[1]); + if (!sae->tmp->pw_id) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1); + sae->tmp->pw_id[pos[1] - 1] = '\0'; + return WLAN_STATUS_SUCCESS; +} + +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, + const u8 **token, size_t *token_len, int *allowed_groups) +{ + const u8 *pos = data, *end = data + len; + u16 res; + + /* Check Finite Cyclic Group */ + if (end - pos < 2) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); + if (res != WLAN_STATUS_SUCCESS) + return res; + pos += 2; + + /* Optional Anti-Clogging Token */ + sae_parse_commit_token(sae, &pos, end, token, token_len); + + /* commit-scalar */ + res = sae_parse_commit_scalar(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* commit-element */ + res = sae_parse_commit_element(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* Optional Password Identifier element */ + res = sae_parse_password_identifier(sae, pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* + * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as + * the values we sent which would be evidence of a reflection attack. + */ + if (!sae->tmp->own_commit_scalar || + crypto_bignum_cmp(sae->tmp->own_commit_scalar, + sae->peer_commit_scalar) != 0 || + (sae->tmp->dh && + (!sae->tmp->own_commit_element_ffc || + crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, + sae->tmp->peer_commit_element_ffc) != 0)) || + (sae->tmp->ec && + (!sae->tmp->own_commit_element_ecc || + crypto_ec_point_cmp(sae->tmp->ec, + sae->tmp->own_commit_element_ecc, + sae->tmp->peer_commit_element_ecc) != 0))) + return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ + + /* + * This is a reflection attack - return special value to trigger caller + * to silently discard the frame instead of replying with a specific + * status code. + */ + return SAE_SILENTLY_DISCARD; +} + +static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const u8 *element1, size_t element1_len, + const struct crypto_bignum *scalar2, + const u8 *element2, size_t element2_len, + u8 *confirm) +{ + const u8 *addr[5]; + size_t len[5]; + u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; + + /* Confirm + * CN(key, X, Y, Z, ...) = + * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) + * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, + * peer-commit-scalar, PEER-COMMIT-ELEMENT) + * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, + * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) + */ + addr[0] = sc; + len[0] = 2; + crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), + sae->tmp->prime_len); + addr[1] = scalar_b1; + len[1] = sae->tmp->prime_len; + addr[2] = element1; + len[2] = element1_len; + crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), + sae->tmp->prime_len); + addr[3] = scalar_b2; + len[3] = sae->tmp->prime_len; + addr[4] = element2; + len[4] = element2_len; + hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, + confirm); +} + +static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const struct crypto_ec_point *element1, + const struct crypto_bignum *scalar2, + const struct crypto_ec_point *element2, + u8 *confirm) +{ + u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; + u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; + + if (crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, + element_b1 + sae->tmp->prime_len) < 0) { + wpa_printf(MSG_ERROR, "SAE: failed bignum op while deriving ec point"); + return ESP_FAIL; + } + if (crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, + element_b2 + sae->tmp->prime_len) < 0) { + wpa_printf(MSG_ERROR, "SAE: failed bignum op while deriving ec point"); + return ESP_FAIL; + } + + sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, + scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); + return ESP_OK; +} + +static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const struct crypto_bignum *element1, + const struct crypto_bignum *scalar2, + const struct crypto_bignum *element2, + u8 *confirm) +{ + u8 element_b1[SAE_MAX_PRIME_LEN]; + u8 element_b2[SAE_MAX_PRIME_LEN]; + + if (crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), + sae->tmp->prime_len) < 0) { + wpa_printf(MSG_ERROR, "SAE: failed bignum op while generating SAE confirm - e1"); + return ESP_FAIL; + } + if (crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), + sae->tmp->prime_len) < 0) { + wpa_printf(MSG_ERROR, "SAE: failed bignum op while generating SAE confirm - e2"); + return ESP_FAIL; + } + + sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, + scalar2, element_b2, sae->tmp->prime_len, confirm); + return ESP_OK; +} + +int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) +{ + const u8 *sc; + + if (sae->tmp == NULL) + return ESP_FAIL; + + /* Send-Confirm */ + sc = wpabuf_put(buf, 0); + wpabuf_put_le16(buf, sae->send_confirm); + if (sae->send_confirm < 0xffff) + sae->send_confirm++; + + if (sae->tmp->ec) { + if (sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ecc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ecc, + wpabuf_put(buf, SHA256_MAC_LEN))) { + wpa_printf(MSG_ERROR, "SAE: failed generate SAE confirm (ecc)"); + return ESP_FAIL; + } + } else { + if (sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ffc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ffc, + wpabuf_put(buf, SHA256_MAC_LEN))) { + wpa_printf(MSG_ERROR, "SAE: failed generate SAE confirm (ffc)"); + return ESP_FAIL; + } + } + return ESP_OK; +} + +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) +{ + u8 verifier[SHA256_MAC_LEN]; + + if (len < 2 + SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); + return ESP_FAIL; + } + + wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); + + if (sae->tmp == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); + return ESP_FAIL; + } + + if (sae->tmp->ec) { + if (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)) { + wpa_printf(MSG_ERROR, "SAE: failed to check SAE confirm (ecc)"); + return ESP_FAIL; + } + } else { + if (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)) { + wpa_printf(MSG_ERROR, "SAE: failed check SAE confirm (ffc)"); + return ESP_FAIL; + } + } + + if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); + wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", + data + 2, SHA256_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", + verifier, SHA256_MAC_LEN); + return ESP_FAIL; + } + + return ESP_OK; +} + +const char * sae_state_txt(enum sae_state state) +{ + switch (state) { + case SAE_NOTHING: + return "Nothing"; + case SAE_COMMITTED: + return "Committed"; + case SAE_CONFIRMED: + return "Confirmed"; + case SAE_ACCEPTED: + return "Accepted"; + } + return "?"; +} + +#endif /* CONFIG_WPA3_SAE */ diff --git a/components/wpa_supplicant/src/common/sae.h b/components/wpa_supplicant/src/common/sae.h new file mode 100644 index 00000000..24437c0d --- /dev/null +++ b/components/wpa_supplicant/src/common/sae.h @@ -0,0 +1,89 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifdef CONFIG_WPA3_SAE + +#ifndef SAE_H +#define SAE_H + +#include "esp_err.h" +#include "utils/includes.h" +#include "utils/common.h" +#include "utils/wpa_debug.h" + +#define SAE_KCK_LEN 32 +#define SAE_PMK_LEN 32 +#define SAE_PMKID_LEN 16 +#define SAE_KEYSEED_KEY_LEN 32 +#define SAE_MAX_PRIME_LEN 512 +#define SAE_MAX_ECC_PRIME_LEN 66 +#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) +#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) + +/* Special value returned by sae_parse_commit() */ +#define SAE_SILENTLY_DISCARD 65535 + +struct sae_temporary_data { + u8 kck[SAE_KCK_LEN]; + struct crypto_bignum *own_commit_scalar; + struct crypto_bignum *own_commit_element_ffc; + struct crypto_ec_point *own_commit_element_ecc; + struct crypto_bignum *peer_commit_element_ffc; + struct crypto_ec_point *peer_commit_element_ecc; + struct crypto_ec_point *pwe_ecc; + struct crypto_bignum *pwe_ffc; + struct crypto_bignum *sae_rand; + struct crypto_ec *ec; + int prime_len; + const struct dh_group *dh; + const struct crypto_bignum *prime; + const struct crypto_bignum *order; + struct crypto_bignum *prime_buf; + struct crypto_bignum *order_buf; + char *pw_id; +}; + +enum { + SAE_MSG_COMMIT = 1, + SAE_MSG_CONFIRM = 2, +}; + +enum sae_state { + SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED +}; + +struct sae_data { + enum sae_state state; + u16 send_confirm; + u8 pmk[SAE_PMK_LEN]; + u8 pmkid[SAE_PMKID_LEN]; + struct crypto_bignum *peer_commit_scalar; + int group; + unsigned int sync; /* protocol instance variable: Sync */ + u16 rc; /* protocol instance variable: Rc (received send-confirm) */ + struct sae_temporary_data *tmp; +}; + +int sae_set_group(struct sae_data *sae, int group); +void sae_clear_temp_data(struct sae_data *sae); +void sae_clear_data(struct sae_data *sae); + +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, + const u8 *password, size_t password_len, + const char *identifier, struct sae_data *sae); +int sae_process_commit(struct sae_data *sae); +int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, + const struct wpabuf *token, const char *identifier); +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, + const u8 **token, size_t *token_len, int *allowed_groups); +int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); +u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group); +const char * sae_state_txt(enum sae_state state); + +#endif /* SAE_H */ +#endif /* CONFIG_WPA3_SAE */ diff --git a/components/wpa_supplicant/src/crypto/aes-ccm.c b/components/wpa_supplicant/src/crypto/aes-ccm.c new file mode 100644 index 00000000..ca3acc26 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/aes-ccm.c @@ -0,0 +1,215 @@ +/* + * Counter with CBC-MAC (CCM) with AES + * + * Copyright (c) 2010-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef CONFIG_IEEE80211W + +#include "utils/includes.h" + +#include "utils/common.h" +#include "aes.h" +#include "aes_wrap.h" + + +static void xor_aes_block(u8 *dst, const u8 *src) +{ + u32 *d = (u32 *) dst; + u32 *s = (u32 *) src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} + + +static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce, + const u8 *aad, size_t aad_len, size_t plain_len, + u8 *x) +{ + u8 aad_buf[2 * AES_BLOCK_SIZE]; + u8 b[AES_BLOCK_SIZE]; + + /* Authentication */ + /* B_0: Flags | Nonce N | l(m) */ + b[0] = aad_len ? 0x40 : 0 /* Adata */; + b[0] |= (((M - 2) / 2) /* M' */ << 3); + b[0] |= (L - 1) /* L' */; + os_memcpy(&b[1], nonce, 15 - L); + WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); + + wpa_hexdump_key(MSG_DEBUG, "CCM B_0", b, AES_BLOCK_SIZE); + aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */ + + if (!aad_len) + return; + + WPA_PUT_BE16(aad_buf, aad_len); + os_memcpy(aad_buf + 2, aad, aad_len); + os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); + + xor_aes_block(aad_buf, x); + aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ + + if (aad_len > AES_BLOCK_SIZE - 2) { + xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); + /* X_3 = E(K, X_2 XOR B_2) */ + aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x); + } +} + + +static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + + for (i = 0; i < len / AES_BLOCK_SIZE; i++) { + /* X_i+1 = E(K, X_i XOR B_i) */ + xor_aes_block(x, data); + data += AES_BLOCK_SIZE; + aes_encrypt(aes, x, x); + } + if (last) { + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + x[i] ^= *data++; + aes_encrypt(aes, x, x); + } +} + + +static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a) +{ + /* A_i = Flags | Nonce N | Counter i */ + a[0] = L - 1; /* Flags = L' */ + os_memcpy(&a[1], nonce, 15 - L); +} + + +static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out, + u8 *a) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + + /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ + for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + /* S_i = E(K, A_i) */ + aes_encrypt(aes, a, out); + xor_aes_block(out, in); + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + } + if (last) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + aes_encrypt(aes, a, out); + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + *out++ ^= *in++; + } +} + + +static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth) +{ + size_t i; + u8 tmp[AES_BLOCK_SIZE]; + + wpa_hexdump_key(MSG_DEBUG, "CCM T", x, M); + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + aes_encrypt(aes, a, tmp); + for (i = 0; i < M; i++) + auth[i] = x[i] ^ tmp[i]; + wpa_hexdump_key(MSG_DEBUG, "CCM U", auth, M); +} + + +static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t) +{ + size_t i; + u8 tmp[AES_BLOCK_SIZE]; + + wpa_hexdump_key(MSG_DEBUG, "CCM U", auth, M); + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + aes_encrypt(aes, a, tmp); + for (i = 0; i < M; i++) + t[i] = auth[i] ^ tmp[i]; + wpa_hexdump_key(MSG_DEBUG, "CCM T", t, M); +} + + +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth) +{ + const size_t L = 2; + void *aes; + u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return -1; + + aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x); + aes_ccm_auth(aes, plain, plain_len, x); + + /* Encryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_encr(aes, L, plain, plain_len, crypt, a); + aes_ccm_encr_auth(aes, M, x, a, auth); + + aes_encrypt_deinit(aes); + + return 0; +} + + +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain) +{ + const size_t L = 2; + void *aes; + u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + u8 t[AES_BLOCK_SIZE]; + + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return -1; + + /* Decryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_decr_auth(aes, M, a, auth, t); + + /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ + aes_ccm_encr(aes, L, crypt, crypt_len, plain, a); + + aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x); + aes_ccm_auth(aes, plain, crypt_len, x); + + aes_encrypt_deinit(aes); + + if (os_memcmp(x, t, M) != 0) { + wpa_printf(MSG_DEBUG, "CCM: Auth mismatch"); + return -1; + } + + return 0; +} +#endif /* CONFIG_IEEE80211W */ diff --git a/components/wpa_supplicant/src/crypto/aes-omac1.c b/components/wpa_supplicant/src/crypto/aes-omac1.c new file mode 100644 index 00000000..415b7955 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/aes-omac1.c @@ -0,0 +1,169 @@ +/* + * One-key CBC MAC (OMAC1) hash with AES + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + + +#include "utils/common.h" +#include "crypto/aes.h" +#include "crypto/aes_wrap.h" + +static void gf_mulx(u8 *pad) +{ + int i, carry; + + carry = pad[0] & 0x80; + for (i = 0; i < AES_BLOCK_SIZE - 1; i++) + pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); + pad[AES_BLOCK_SIZE - 1] <<= 1; + if (carry) + pad[AES_BLOCK_SIZE - 1] ^= 0x87; +} + + +/** + * omac1_aes_vector - One-Key CBC MAC (OMAC1) hash with AES + * @key: Key for the hash operation + * @key_len: Key length in octets + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + * + * This is a mode for using block cipher (AES in this case) for authentication. + * OMAC1 was standardized with the name CMAC by NIST in a Special Publication + * (SP) 800-38B. + */ +int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + void *ctx; + u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; + const u8 *pos, *end; + size_t i, e, left, total_len; + + ctx = aes_encrypt_init(key, key_len); + if (ctx == NULL) + return -1; + os_memset(cbc, 0, AES_BLOCK_SIZE); + + total_len = 0; + for (e = 0; e < num_elem; e++) + total_len += len[e]; + left = total_len; + + e = 0; + pos = addr[0]; + end = pos + len[0]; + + while (left >= AES_BLOCK_SIZE) { + for (i = 0; i < AES_BLOCK_SIZE; i++) { + cbc[i] ^= *pos++; + if (pos >= end) { + /* + * Stop if there are no more bytes to process + * since there are no more entries in the array. + */ + if (i + 1 == AES_BLOCK_SIZE && + left == AES_BLOCK_SIZE) + break; + e++; + pos = addr[e]; + end = pos + len[e]; + } + } + if (left > AES_BLOCK_SIZE) + aes_encrypt(ctx, cbc, cbc); + left -= AES_BLOCK_SIZE; + } + + os_memset(pad, 0, AES_BLOCK_SIZE); + aes_encrypt(ctx, pad, pad); + gf_mulx(pad); + + if (left || total_len == 0) { + for (i = 0; i < left; i++) { + cbc[i] ^= *pos++; + if (pos >= end) { + /* + * Stop if there are no more bytes to process + * since there are no more entries in the array. + */ + if (i + 1 == left) + break; + e++; + pos = addr[e]; + end = pos + len[e]; + } + } + cbc[left] ^= 0x80; + gf_mulx(pad); + } + + for (i = 0; i < AES_BLOCK_SIZE; i++) + pad[i] ^= cbc[i]; + aes_encrypt(ctx, pad, mac); + aes_encrypt_deinit(ctx); + return 0; +} + + +/** + * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128 + * @key: 128-bit key for the hash operation + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + * + * This is a mode for using block cipher (AES in this case) for authentication. + * OMAC1 was standardized with the name CMAC by NIST in a Special Publication + * (SP) 800-38B. + */ +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +/** + * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC) + * @key: 128-bit key for the hash operation + * @data: Data buffer for which a MAC is determined + * @data_len: Length of data buffer in bytes + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + * + * This is a mode for using block cipher (AES in this case) for authentication. + * OMAC1 was standardized with the name CMAC by NIST in a Special Publication + * (SP) 800-38B. + */ +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} + + +/** + * omac1_aes_256 - One-Key CBC MAC (OMAC1) hash with AES-256 (aka AES-CMAC) + * @key: 256-bit key for the hash operation + * @data: Data buffer for which a MAC is determined + * @data_len: Length of data buffer in bytes + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + * + * This is a mode for using block cipher (AES in this case) for authentication. + * OMAC1 was standardized with the name CMAC by NIST in a Special Publication + * (SP) 800-38B. + */ +int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} diff --git a/components/wpa_supplicant/src/crypto/ccmp.c b/components/wpa_supplicant/src/crypto/ccmp.c new file mode 100644 index 00000000..f5814a78 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/ccmp.c @@ -0,0 +1,354 @@ +/* + * CTR with CBC-MAC Protocol (CCMP) + * Copyright (c) 2010-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef CONFIG_IEEE80211W + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "aes.h" +#include "aes_wrap.h" + +static void ccmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data, + u8 *aad, size_t *aad_len, u8 *nonce) +{ + u16 fc, stype, seq; + int qos = 0, addr4 = 0; + u8 *pos; + + nonce[0] = 0; + + fc = le_to_host16(hdr->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == + (WLAN_FC_TODS | WLAN_FC_FROMDS)) + addr4 = 1; + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { + fc &= ~0x0070; /* Mask subtype bits */ + if (stype & 0x08) { + const u8 *qc; + qos = 1; + fc &= ~WLAN_FC_ORDER; + qc = (const u8 *) (hdr + 1); + if (addr4) + qc += ETH_ALEN; + nonce[0] = qc[0] & 0x0f; + } + } else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) + nonce[0] |= 0x10; /* Management */ + + fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA); + fc |= WLAN_FC_ISWEP; + WPA_PUT_LE16(aad, fc); + pos = aad + 2; + os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN); + pos += 3 * ETH_ALEN; + seq = le_to_host16(hdr->seq_ctrl); + seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */ + WPA_PUT_LE16(pos, seq); + pos += 2; + + os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2); + pos += addr4 * ETH_ALEN; + if (qos) { + pos[0] &= ~0x70; + if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */) + pos[0] &= ~0x80; + pos++; + *pos++ = 0x00; + } + + *aad_len = pos - aad; + + os_memcpy(nonce + 1, hdr->addr2, ETH_ALEN); + nonce[7] = data[7]; /* PN5 */ + nonce[8] = data[6]; /* PN4 */ + nonce[9] = data[5]; /* PN3 */ + nonce[10] = data[4]; /* PN2 */ + nonce[11] = data[1]; /* PN1 */ + nonce[12] = data[0]; /* PN0 */ +} + + +static void ccmp_aad_nonce_pv1(const u8 *hdr, const u8 *a1, const u8 *a2, + const u8 *a3, const u8 *pn, + u8 *aad, size_t *aad_len, u8 *nonce) +{ + u16 fc, type; + u8 *pos; + + nonce[0] = BIT(5); /* PV1 */ + /* TODO: Priority for QMF; 0 is used for Data frames */ + + fc = WPA_GET_LE16(hdr); + type = (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2; + + if (type == 1) + nonce[0] |= 0x10; /* Management */ + + fc &= ~(BIT(10) | BIT(11) | BIT(13) | BIT(14) | BIT(15)); + fc |= BIT(12); + WPA_PUT_LE16(aad, fc); + pos = aad + 2; + if (type == 0 || type == 3) { + const u8 *sc; + + os_memcpy(pos, a1, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, a2, ETH_ALEN); + pos += ETH_ALEN; + + if (type == 0) { + /* Either A1 or A2 contains SID */ + sc = hdr + 2 + 2 + ETH_ALEN; + } else { + /* Both A1 and A2 contain full addresses */ + sc = hdr + 2 + 2 * ETH_ALEN; + } + /* SC with Sequence Number subfield (bits 4-15 of the Sequence + * Control field) masked to 0. */ + *pos++ = *sc & 0x0f; + *pos++ = 0; + + if (a3) { + os_memcpy(pos, a3, ETH_ALEN); + pos += ETH_ALEN; + } + } + + *aad_len = pos - aad; + + os_memcpy(nonce + 1, a2, ETH_ALEN); + nonce[7] = pn[5]; /* PN5 */ + nonce[8] = pn[4]; /* PN4 */ + nonce[9] = pn[3]; /* PN3 */ + nonce[10] = pn[2]; /* PN2 */ + nonce[11] = pn[1]; /* PN1 */ + nonce[12] = pn[0]; /* PN0 */ +} + + +u8 * ccmp_decrypt(const u8 *tk, const u8 *hdr, const u8 *data, + size_t data_len, size_t *decrypted_len) +{ + u8 aad[30], nonce[13]; + size_t aad_len; + size_t mlen; + u8 *plain; + + if (data_len < 8 + 8) + return NULL; + + plain = os_malloc(data_len + AES_BLOCK_SIZE); + if (plain == NULL) + return NULL; + + mlen = data_len - 8 - 8; + + os_memset(aad, 0, sizeof(aad)); + ccmp_aad_nonce((const struct ieee80211_hdr *)hdr, data, aad, &aad_len, nonce); + //wpa_hexdump(MSG_DEBUG, "CCMP AAD", aad, aad_len); + //wpa_hexdump(MSG_DEBUG, "CCMP nonce", nonce, 13); + + if (aes_ccm_ad(tk, 16, nonce, 8, data + 8, mlen, aad, aad_len, + data + 8 + mlen, plain) < 0) { + os_free(plain); + return NULL; + } + //wpa_hexdump(MSG_DEBUG, "CCMP decrypted", plain, mlen); + + *decrypted_len = mlen; + return plain; +} + + +void ccmp_get_pn(u8 *pn, const u8 *data) +{ + pn[0] = data[7]; /* PN5 */ + pn[1] = data[6]; /* PN4 */ + pn[2] = data[5]; /* PN3 */ + pn[3] = data[4]; /* PN2 */ + pn[4] = data[1]; /* PN1 */ + pn[5] = data[0]; /* PN0 */ +} + + +u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, + u8 *pn, int keyid, size_t *encrypted_len) +{ + u8 aad[30], nonce[13]; + size_t aad_len, plen; + u8 *crypt, *pos; + struct ieee80211_hdr *hdr; + + if (len < hdrlen || hdrlen < 24) + return NULL; + plen = len - hdrlen; + + crypt = os_malloc(hdrlen + 8 + plen + 8 + AES_BLOCK_SIZE); + if (crypt == NULL) + return NULL; + + os_memcpy(crypt, frame, hdrlen); + hdr = (struct ieee80211_hdr *) crypt; + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + pos = crypt + hdrlen; + *pos++ = pn[5]; /* PN0 */ + *pos++ = pn[4]; /* PN1 */ + *pos++ = 0x00; /* Rsvd */ + *pos++ = 0x20 | (keyid << 6); + *pos++ = pn[3]; /* PN2 */ + *pos++ = pn[2]; /* PN3 */ + *pos++ = pn[1]; /* PN4 */ + *pos++ = pn[0]; /* PN5 */ + + os_memset(aad, 0, sizeof(aad)); + ccmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce); + wpa_hexdump(MSG_DEBUG, "CCMP AAD", aad, aad_len); + wpa_hexdump(MSG_DEBUG, "CCMP nonce", nonce, 13); + + if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len, + pos, pos + plen) < 0) { + os_free(crypt); + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "CCMP encrypted", crypt + hdrlen + 8, plen); + + *encrypted_len = hdrlen + 8 + plen + 8; + + return crypt; +} + + +u8 * ccmp_encrypt_pv1(const u8 *tk, const u8 *a1, const u8 *a2, const u8 *a3, + const u8 *frame, size_t len, + size_t hdrlen, const u8 *pn, int keyid, + size_t *encrypted_len) +{ + u8 aad[24], nonce[13]; + size_t aad_len, plen; + u8 *crypt, *pos; + struct ieee80211_hdr *hdr; + + if (len < hdrlen || hdrlen < 12) + return NULL; + plen = len - hdrlen; + + crypt = os_malloc(hdrlen + plen + 8 + AES_BLOCK_SIZE); + if (crypt == NULL) + return NULL; + + os_memcpy(crypt, frame, hdrlen); + hdr = (struct ieee80211_hdr *) crypt; + hdr->frame_control |= host_to_le16(BIT(12)); /* Protected Frame */ + pos = crypt + hdrlen; + + os_memset(aad, 0, sizeof(aad)); + ccmp_aad_nonce_pv1(crypt, a1, a2, a3, pn, aad, &aad_len, nonce); + wpa_hexdump(MSG_DEBUG, "CCMP AAD", aad, aad_len); + wpa_hexdump(MSG_DEBUG, "CCMP nonce", nonce, sizeof(nonce)); + + if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len, + pos, pos + plen) < 0) { + os_free(crypt); + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "CCMP encrypted", crypt + hdrlen, plen); + + *encrypted_len = hdrlen + plen + 8; + + return crypt; +} + + +u8 * ccmp_256_decrypt(const u8 *tk, const u8 *hdr, const u8 *data, + size_t data_len, size_t *decrypted_len) +{ + u8 aad[30], nonce[13]; + size_t aad_len; + size_t mlen; + u8 *plain; + + if (data_len < 8 + 16) + return NULL; + + plain = os_malloc(data_len + AES_BLOCK_SIZE); + if (plain == NULL) + return NULL; + + mlen = data_len - 8 - 16; + + os_memset(aad, 0, sizeof(aad)); + ccmp_aad_nonce((const struct ieee80211_hdr *)hdr, data, aad, &aad_len, nonce); + wpa_hexdump(MSG_DEBUG, "CCMP-256 AAD", aad, aad_len); + wpa_hexdump(MSG_DEBUG, "CCMP-256 nonce", nonce, 13); + + if (aes_ccm_ad(tk, 32, nonce, 16, data + 8, mlen, aad, aad_len, + data + 8 + mlen, plain) < 0) { + os_free(plain); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "CCMP-256 decrypted", plain, mlen); + + *decrypted_len = mlen; + return plain; +} + + +u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, + u8 *pn, int keyid, size_t *encrypted_len) +{ + u8 aad[30], nonce[13]; + size_t aad_len, plen; + u8 *crypt, *pos; + struct ieee80211_hdr *hdr; + + if (len < hdrlen || hdrlen < 24) + return NULL; + plen = len - hdrlen; + + crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE); + if (crypt == NULL) + return NULL; + + os_memcpy(crypt, frame, hdrlen); + hdr = (struct ieee80211_hdr *) crypt; + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + pos = crypt + hdrlen; + *pos++ = pn[5]; /* PN0 */ + *pos++ = pn[4]; /* PN1 */ + *pos++ = 0x00; /* Rsvd */ + *pos++ = 0x20 | (keyid << 6); + *pos++ = pn[3]; /* PN2 */ + *pos++ = pn[2]; /* PN3 */ + *pos++ = pn[1]; /* PN4 */ + *pos++ = pn[0]; /* PN5 */ + + os_memset(aad, 0, sizeof(aad)); + ccmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce); + wpa_hexdump(MSG_DEBUG, "CCMP-256 AAD", aad, aad_len); + wpa_hexdump(MSG_DEBUG, "CCMP-256 nonce", nonce, 13); + + if (aes_ccm_ae(tk, 32, nonce, 16, frame + hdrlen, plen, aad, aad_len, + pos, pos + plen) < 0) { + os_free(crypt); + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "CCMP-256 encrypted", crypt + hdrlen + 8, + plen); + + *encrypted_len = hdrlen + 8 + plen + 16; + + return crypt; +} +#endif /* CONFIG_IEEE80211W */ diff --git a/components/wpa_supplicant/src/crypto/ccmp.h b/components/wpa_supplicant/src/crypto/ccmp.h new file mode 100644 index 00000000..aeca0602 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/ccmp.h @@ -0,0 +1,28 @@ +/* + * wlantest - IEEE 802.11 protocol monitoring and testing tool + * Copyright (c) 2010-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef CONFIG_IEEE80211W +#ifndef CCMP_H +#define CCMP_H + +u8 * ccmp_decrypt(const u8 *tk, const u8 *hdr, const u8 *data, + size_t data_len, size_t *decrypted_len); +u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, + u8 *pn, int keyid, size_t *encrypted_len); +u8 * ccmp_encrypt_pv1(const u8 *tk, const u8 *a1, const u8 *a2, const u8 *a3, + const u8 *frame, size_t len, + size_t hdrlen, const u8 *pn, int keyid, + size_t *encrypted_len); +void ccmp_get_pn(u8 *pn, const u8 *data); +u8 * ccmp_256_decrypt(const u8 *tk, const u8 *hdr, const u8 *data, + size_t data_len, size_t *decrypted_len); +u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, + u8 *pn, int keyid, size_t *encrypted_len); + +#endif /* CCMP_H */ +#endif /* CONFIG_IEEE80211W */ diff --git a/components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c b/components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c new file mode 100644 index 00000000..c61346c7 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c @@ -0,0 +1,273 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef ESP_PLATFORM +#include "esp_system.h" +#include "mbedtls/bignum.h" +#endif + +#include "utils/includes.h" +#include "utils/common.h" +#include "crypto.h" +#include "crypto/random.h" +#include "sha256.h" +#include "mbedtls/pk.h" + +struct crypto_bignum *crypto_bignum_init(void) +{ + mbedtls_mpi *bn = os_zalloc(sizeof(mbedtls_mpi)); + if (bn == NULL) { + return NULL; + } + + mbedtls_mpi_init(bn); + + return (struct crypto_bignum *)bn; +} + + +struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len) +{ + int ret = 0; + mbedtls_mpi *bn = os_zalloc(sizeof(mbedtls_mpi)); + if (bn == NULL) { + return NULL; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(bn, buf, len)); + return (struct crypto_bignum *) bn; + +cleanup: + os_free(bn); + return NULL; +} + + +void crypto_bignum_deinit(struct crypto_bignum *n, int clear) +{ + mbedtls_mpi_free((mbedtls_mpi *)n); + os_free((mbedtls_mpi *)n); +} + + +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) +{ + int num_bytes, offset; + + if (padlen > buflen) { + return -1; + } + + num_bytes = mbedtls_mpi_size((mbedtls_mpi *) a); + + if ((size_t) num_bytes > buflen) { + return -1; + } + if (padlen > (size_t) num_bytes) { + offset = padlen - num_bytes; + } else { + offset = 0; + } + + os_memset(buf, 0, offset); + mbedtls_mpi_write_binary((mbedtls_mpi *) a, buf + offset, mbedtls_mpi_size((mbedtls_mpi *)a) ); + + return num_bytes + offset; +} + + +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return mbedtls_mpi_add_mpi((mbedtls_mpi *) c, (const mbedtls_mpi *) a, (const mbedtls_mpi *) b) ? + -1 : 0; +} + + +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return mbedtls_mpi_mod_mpi((mbedtls_mpi *) c, (const mbedtls_mpi *) a, (const mbedtls_mpi *) b) ? -1 : 0; +} + + +int crypto_bignum_exptmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d) +{ + return mbedtls_mpi_exp_mod((mbedtls_mpi *) d, (const mbedtls_mpi *) a, (const mbedtls_mpi *) b, (const mbedtls_mpi *) c, NULL) ? -1 : 0; + +} + + +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return mbedtls_mpi_inv_mod((mbedtls_mpi *) c, (const mbedtls_mpi *) a, + (const mbedtls_mpi *) b) ? -1 : 0; +} + + +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return mbedtls_mpi_sub_mpi((mbedtls_mpi *) c, (const mbedtls_mpi *) a, (const mbedtls_mpi *) b) ? + -1 : 0; +} + + +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return mbedtls_mpi_div_mpi((mbedtls_mpi *) c, NULL, (const mbedtls_mpi *) a, (const mbedtls_mpi *) b) ? + -1 : 0; +} + + +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d) +{ + int res; + + mbedtls_mpi temp; + mbedtls_mpi_init(&temp); + + res = mbedtls_mpi_mul_mpi(&temp, (const mbedtls_mpi *) a, (const mbedtls_mpi *) b); + if (res) { + return -1; + } + + res = mbedtls_mpi_mod_mpi((mbedtls_mpi *) d, &temp, (mbedtls_mpi *) c); + + mbedtls_mpi_free(&temp); + return res ? -1 : 0; +} + + +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b) +{ + return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *) a, (const mbedtls_mpi *) b); +} + + +int crypto_bignum_bits(const struct crypto_bignum *a) +{ + return mbedtls_mpi_bitlen((const mbedtls_mpi *) a); +} + + +int crypto_bignum_is_zero(const struct crypto_bignum *a) +{ + return (mbedtls_mpi_cmp_int((const mbedtls_mpi *) a, 0) == 0); +} + + +int crypto_bignum_is_one(const struct crypto_bignum *a) +{ + return (mbedtls_mpi_cmp_int((const mbedtls_mpi *) a, 1) == 0); +} + + +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) +{ + mbedtls_mpi exp, tmp; + int res = -2, ret; + + mbedtls_mpi_init(&exp); + mbedtls_mpi_init(&tmp); + + /* exp = (p-1) / 2 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *) p, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&exp, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *) a, &exp, (const mbedtls_mpi *) p, NULL)); + + if (mbedtls_mpi_cmp_int(&tmp, 1) == 0) { + res = 1; + } else if (mbedtls_mpi_cmp_int(&tmp, 0) == 0 + /* The below check is workaround for the case where HW + * does not behave properly for X ^ A mod M when X is + * power of M. Instead of returning value 0, value M is + * returned.*/ + || mbedtls_mpi_cmp_mpi(&tmp, (const mbedtls_mpi *)p) == 0) { + res = 0; + } else { + res = -1; + } + +cleanup: + mbedtls_mpi_free(&tmp); + mbedtls_mpi_free(&exp); + return res; +} + +int crypto_bignum_to_string(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) +{ + int num_bytes, offset; + size_t outlen; + + if (padlen > buflen) { + return -1; + } + + num_bytes = mbedtls_mpi_size((mbedtls_mpi *) a); + + if (padlen > (size_t) num_bytes) { + offset = padlen - num_bytes; + } else { + offset = 0; + } + + os_memset(buf, 0, offset); + mbedtls_mpi_write_string((mbedtls_mpi *) a, 16, (char *)(buf + offset), + mbedtls_mpi_size((mbedtls_mpi *)a), &outlen); + + return outlen; +} + +int crypto_bignum_addmod(struct crypto_bignum *a, + struct crypto_bignum *b, + struct crypto_bignum *c, + struct crypto_bignum *d) +{ + struct crypto_bignum *tmp = crypto_bignum_init(); + int ret = -1; + + if (mbedtls_mpi_add_mpi((mbedtls_mpi *) tmp, (const mbedtls_mpi *) b, (const mbedtls_mpi *) c) < 0) + goto fail; + + if (mbedtls_mpi_mod_mpi( (mbedtls_mpi *) a, (const mbedtls_mpi *) tmp, (const mbedtls_mpi *) d) < 0) + goto fail; + + ret = 0; +fail: + crypto_bignum_deinit(tmp, 0); + return ret; +} + +void crypto_free_buffer(unsigned char *buf) +{ + os_free(buf); +} diff --git a/components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c b/components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c new file mode 100644 index 00000000..92b9fd9e --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c @@ -0,0 +1,988 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef ESP_PLATFORM +#include "esp_system.h" +#include "mbedtls/bignum.h" +#endif + +#include "utils/includes.h" +#include "utils/common.h" +#include "crypto.h" +#include "sha256.h" +#include "crypto/random.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" + +#include "mbedtls/pk.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/sha256.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" + +#define ECP_PRV_DER_MAX_BYTES 29 + 3 * MBEDTLS_ECP_MAX_BYTES + +#ifdef CONFIG_ECC +struct crypto_ec { + mbedtls_ecp_group group; +}; + +int crypto_rng_wrapper(void *ctx, unsigned char *buf, size_t len) +{ + return random_get_bytes(buf, len); +} + +struct crypto_ec *crypto_ec_init(int group) +{ + struct crypto_ec *e; + + mbedtls_ecp_group_id grp_id; + + /* IANA registry to mbedtls internal mapping*/ + switch (group) { + case IANA_SECP256R1: + /* For now just support NIST-P256. + * This is of type "short Weierstrass". + */ + grp_id = MBEDTLS_ECP_DP_SECP256R1; + break; + default: + return NULL; + + } + e = os_zalloc(sizeof(*e)); + if (e == NULL) { + return NULL; + } + + mbedtls_ecp_group_init(&e->group); + + if (mbedtls_ecp_group_load(&e->group, grp_id)) { + crypto_ec_deinit(e); + e = NULL; + } + + return e; +} + + +void crypto_ec_deinit(struct crypto_ec *e) +{ + if (e == NULL) { + return; + } + + mbedtls_ecp_group_free(&e->group); + os_free(e); +} + + +struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e) +{ + mbedtls_ecp_point *pt; + if (e == NULL) { + return NULL; + } + + pt = os_zalloc(sizeof(mbedtls_ecp_point)); + + if( pt == NULL) { + return NULL; + } + + mbedtls_ecp_point_init(pt); + + return (struct crypto_ec_point *) pt; +} + + +size_t crypto_ec_prime_len(struct crypto_ec *e) +{ + return mbedtls_mpi_size(&e->group.P); +} + + +size_t crypto_ec_prime_len_bits(struct crypto_ec *e) +{ + return mbedtls_mpi_bitlen(&e->group.P); +} +struct crypto_ec_group *crypto_ec_get_group_byname(const char *name) +{ + struct crypto_ec *e; + const mbedtls_ecp_curve_info *curve = mbedtls_ecp_curve_info_from_name(name); + + e = os_zalloc(sizeof(*e)); + if (e == NULL) { + return NULL; + } + + mbedtls_ecp_group_init( &e->group ); + + if (mbedtls_ecp_group_load(&e->group, curve->grp_id)) { + crypto_ec_deinit(e); + e = NULL; + } + + return (struct crypto_ec_group *) &e->group; +} + +const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->group.P; +} + + +const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->group.N; +} + + +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) +{ + mbedtls_ecp_point_free((mbedtls_ecp_point *) p); + os_free(p); +} + +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y) +{ + int len = mbedtls_mpi_size(&e->group.P); + + if (x) { + if(crypto_bignum_to_bin((struct crypto_bignum *) & ((mbedtls_ecp_point *) point)->X, + x, len, len) < 0) { + return -1; + } + + } + + if (y) { + if(crypto_bignum_to_bin((struct crypto_bignum *) & ((mbedtls_ecp_point *) point)->Y, + y, len, len) < 0) { + return -1; + } + } + + return 0; +} + +int crypto_ec_get_affine_coordinates(struct crypto_ec *e, struct crypto_ec_point *pt, + struct crypto_bignum *x, struct crypto_bignum *y) +{ + int ret = -1; + mbedtls_ecp_point *point = (mbedtls_ecp_point *)pt; + + if (!mbedtls_ecp_is_zero(point) && (mbedtls_mpi_cmp_int( &point->Z, 1 ) == 0 )) { + // Affine coordinates mean that z should be 1, + wpa_printf(MSG_ERROR, "Z coordinate is neither 0 or 1"); + return -1; + } + + if (x) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy((mbedtls_mpi*) x, &((mbedtls_ecp_point* )point)->X)); + } + if (y) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy((mbedtls_mpi*) y, &((mbedtls_ecp_point* )point)->Y)); + } + return 0; +cleanup: + return ret; +} + +struct crypto_ec_point *crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val) +{ + mbedtls_ecp_point *pt; + int len, ret; + + if (e == NULL) { + return NULL; + } + + len = mbedtls_mpi_size(&e->group.P); + + pt = os_zalloc(sizeof(mbedtls_ecp_point)); + mbedtls_ecp_point_init(pt); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pt->X, val, len)); + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pt->Y, val + len, len)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset((&pt->Z), 1)); + + return (struct crypto_ec_point *) pt; + +cleanup: + mbedtls_ecp_point_free(pt); + os_free(pt); + return NULL; +} + + +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c) +{ + int ret; + mbedtls_mpi one; + + mbedtls_mpi_init(&one); + + MBEDTLS_MPI_CHK(mbedtls_mpi_lset( &one, 1 )); + MBEDTLS_MPI_CHK(mbedtls_ecp_muladd(&e->group, (mbedtls_ecp_point *) c, &one, (const mbedtls_ecp_point *)a , &one, (const mbedtls_ecp_point *)b)); + +cleanup: + mbedtls_mpi_free(&one); + return ret ? -1 : 0; +} + + +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res) +{ + int ret; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + + MBEDTLS_MPI_CHK(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + NULL, 0)); + + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(&e->group, + (mbedtls_ecp_point *) res, + (const mbedtls_mpi *)b, + (const mbedtls_ecp_point *)p, + mbedtls_ctr_drbg_random, + &ctr_drbg)); +cleanup: + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + return ret ? -1 : 0; +} + + +/* Currently mbedtls does not have any function for inverse + * This function calculates inverse of a point. + * Set R = -P + */ +static int ecp_opp(const mbedtls_ecp_group *grp, mbedtls_ecp_point *R, const mbedtls_ecp_point *P) +{ + int ret = 0; + + /* Copy */ + if (R != P) { + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(R, P)); + } + + /* In-place opposite */ + if (mbedtls_mpi_cmp_int(&R->Y, 0) != 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&R->Y, &grp->P, &R->Y)); + } + +cleanup: + return (ret ); +} + +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) +{ + return ecp_opp(&e->group, (mbedtls_ecp_point *) p, (mbedtls_ecp_point *) p) ? -1 : 0; +} + +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit) +{ + mbedtls_mpi temp; + mbedtls_mpi *y_sqr, *y; + mbedtls_mpi_init(&temp); + int ret = 0; + + y = &((mbedtls_ecp_point *)p)->Y; + + /* Faster way to find sqrt + * Works only with curves having prime p + * such that p ≡ 3 (mod 4) + * y_ = (y2 ^ ((p+1)/4)) mod p + * + * if LSB of both x and y are same: y = y_ + * else y = p - y_ + * y_bit is LSB of x + */ + y_bit = (y_bit != 0); + + y_sqr = (mbedtls_mpi *) crypto_ec_point_compute_y_sqr(e, x); + + if (y_sqr) { + + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&temp, &e->group.P, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_div_int(&temp, NULL, &temp, 4)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(y, y_sqr, &temp, &e->group.P, NULL)); + + if (y_bit != mbedtls_mpi_get_bit(y, 0)) + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(y, &e->group.P, y)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&((mbedtls_ecp_point* )p)->X, (const mbedtls_mpi*) x)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&((mbedtls_ecp_point *)p)->Z, 1)); + } else { + ret = 1; + } +cleanup: + mbedtls_mpi_free(&temp); + mbedtls_mpi_free(y_sqr); + os_free(y_sqr); + return ret ? -1 : 0; +} + +int crypto_get_order(struct crypto_ec_group *group, struct crypto_bignum *x) +{ + return mbedtls_mpi_copy((mbedtls_mpi *) x, &((mbedtls_ecp_group *)group)->N); +} + +struct crypto_bignum *crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) +{ + mbedtls_mpi temp, temp2, num; + int ret = 0; + + mbedtls_mpi *y_sqr = os_zalloc(sizeof(mbedtls_mpi)); + if (y_sqr == NULL) { + return NULL; + } + + mbedtls_mpi_init(&temp); + mbedtls_mpi_init(&temp2); + mbedtls_mpi_init(&num); + mbedtls_mpi_init(y_sqr); + + /* y^2 = x^3 + ax + b mod P*/ + /* mbedtls does not have mod-add or mod-mul apis. + * + */ + + /* Calculate x^3 mod P*/ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&num, 3)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&temp, (const mbedtls_mpi *) x, &num, &e->group.P, NULL)); + + /* Calculate ax mod P*/ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&num, -3)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&temp2, (const mbedtls_mpi *) x, &num)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp2, &temp2, &e->group.P)); + + /* Calculate ax + b mod P. Note that b is already < P*/ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&temp2, &temp2, &e->group.B)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp2, &temp2, &e->group.P)); + + /* Calculate x^3 + ax + b mod P*/ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&temp2, &temp2, &temp)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(y_sqr, &temp2, &e->group.P)); + +cleanup: + mbedtls_mpi_free(&temp); + mbedtls_mpi_free(&temp2); + mbedtls_mpi_free(&num); + if (ret) { + mbedtls_mpi_free(y_sqr); + os_free(y_sqr); + return NULL; + } else { + return (struct crypto_bignum *) y_sqr; + } +} + +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return mbedtls_ecp_is_zero((mbedtls_ecp_point *) p); +} + +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + mbedtls_mpi y_sqr_lhs, *y_sqr_rhs = NULL, two; + int ret = 0, on_curve = 0; + + mbedtls_mpi_init(&y_sqr_lhs); + mbedtls_mpi_init(&two); + + /* Calculate y^2 mod P*/ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&two, 2)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&y_sqr_lhs, &((const mbedtls_ecp_point *)p)->Y , &two, &e->group.P, NULL)); + + y_sqr_rhs = (mbedtls_mpi *) crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *) & ((const mbedtls_ecp_point *)p)->X); + + if (y_sqr_rhs && (mbedtls_mpi_cmp_mpi(y_sqr_rhs, &y_sqr_lhs) == 0)) { + on_curve = 1; + } + +cleanup: + mbedtls_mpi_free(&y_sqr_lhs); + mbedtls_mpi_free(&two); + mbedtls_mpi_free(y_sqr_rhs); + os_free(y_sqr_rhs); + return (ret == 0) && (on_curve == 1); +} + +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b) +{ + return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *) a, + (const mbedtls_ecp_point *) b); +} +int crypto_key_compare(struct crypto_key *key1, struct crypto_key *key2) +{ + if (mbedtls_pk_check_pair((mbedtls_pk_context *)key1, (mbedtls_pk_context *)key2) < 0) + return 0; + + return 1; +} + +void crypto_debug_print_point(const char *title, struct crypto_ec *e, + const struct crypto_ec_point *point) +{ + u8 x[32], y[32]; + + if (crypto_ec_point_to_bin(e, point, x, y) < 0) { + wpa_printf(MSG_ERROR, "error: failed to get corrdinates\n"); + return; + } + + wpa_hexdump(MSG_ERROR, "x:", x, 32); + wpa_hexdump(MSG_ERROR, "y:", y, 32); +} + +static struct crypto_key *crypto_alloc_key(void) +{ + mbedtls_pk_context *key = os_malloc(sizeof(*key)); + + if (!key) { + wpa_printf(MSG_ERROR, "%s: memory allocation failed\n", __func__); + return NULL; + } + mbedtls_pk_init(key); + + return (struct crypto_key *)key; +} + + +struct crypto_key * crypto_ec_set_pubkey_point(const struct crypto_ec_group *group, + const u8 *buf, size_t len) +{ + mbedtls_ecp_point *point = NULL; + struct crypto_key *pkey = NULL; + int ret; + mbedtls_pk_context *key = (mbedtls_pk_context *)crypto_alloc_key(); + + if (!key) { + wpa_printf(MSG_ERROR, "%s: memory allocation failed\n", __func__); + return NULL; + } + + point = (mbedtls_ecp_point *)crypto_ec_point_from_bin((struct crypto_ec *)group, buf); + if (crypto_ec_point_is_at_infinity((struct crypto_ec *)group, (struct crypto_ec_point *)point)) { + wpa_printf(MSG_ERROR, "Point is at infinity"); + goto fail; + } + if (!crypto_ec_point_is_on_curve((struct crypto_ec *)group, (struct crypto_ec_point *)point)) { + wpa_printf(MSG_ERROR, "Point not on curve"); + goto fail; + } + + if (mbedtls_ecp_check_pubkey((mbedtls_ecp_group *)group, point) < 0) { //typecast + // ideally should have failed in upper condition, duplicate code?? + wpa_printf(MSG_ERROR, "Invalid key"); + goto fail; + } + mbedtls_ecp_keypair *ecp_key = malloc(sizeof (*ecp_key)); + if (!ecp_key) { + wpa_printf(MSG_ERROR, "key allocation failed"); + goto fail; + } + + /* Init keypair */ + mbedtls_ecp_keypair_init(ecp_key); + // TODO Is it needed? check? + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(&ecp_key->Q, point)); + + /* Assign values */ + if( ( ret = mbedtls_pk_setup( key, + mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY) ) ) != 0 ) + goto fail; + + if (key->pk_ctx) + os_free(key->pk_ctx); + key->pk_ctx = ecp_key; + mbedtls_ecp_copy(&mbedtls_pk_ec(*key)->Q, point); + mbedtls_ecp_group_load(&mbedtls_pk_ec(*key)->grp, MBEDTLS_ECP_DP_SECP256R1); + + pkey = (struct crypto_key *)key; +cleanup: + crypto_ec_point_deinit((struct crypto_ec_point *)point, 0); + return pkey; +fail: + if (point) + crypto_ec_point_deinit((struct crypto_ec_point *)point, 0); + if (key) + mbedtls_pk_free(key); + pkey = NULL; + return pkey; +} + + +void crypto_ec_free_key(struct crypto_key *key) +{ + mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + mbedtls_pk_free(pkey); + os_free(key); +} + +struct crypto_ec_point *crypto_ec_get_public_key(struct crypto_key *key) +{ + mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + + return (struct crypto_ec_point *)&mbedtls_pk_ec(*pkey)->Q; +} + + +int crypto_ec_get_priv_key_der(struct crypto_key *key, unsigned char **key_data, int *key_len) +{ + mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + char der_data[ECP_PRV_DER_MAX_BYTES]; + + *key_len = mbedtls_pk_write_key_der(pkey, (unsigned char *)der_data, ECP_PRV_DER_MAX_BYTES); + if (!*key_len) + return -1; + + *key_data = os_malloc(*key_len); + + if (!*key_data) { + wpa_printf(MSG_ERROR, "memory allocation failed\n"); + return -1; + } + os_memcpy(*key_data, der_data, *key_len); + + return 0; +} + +struct crypto_ec_group *crypto_ec_get_group_from_key(struct crypto_key *key) +{ + mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + + return (struct crypto_ec_group *)&(mbedtls_pk_ec(*pkey)->grp); +} + +struct crypto_bignum *crypto_ec_get_private_key(struct crypto_key *key) +{ + mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + + return ((struct crypto_bignum *)&(mbedtls_pk_ec(*pkey)->d)); +} + +int crypto_ec_get_publickey_buf(struct crypto_key *key, u8 *key_buf, int len) +{ + mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + unsigned char buf[MBEDTLS_MPI_MAX_SIZE + 10]; /* tag, length + MPI */ + unsigned char *c = buf + sizeof(buf ); + size_t pk_len = 0; + + memset(buf, 0, sizeof(buf) ); + pk_len = mbedtls_pk_write_pubkey( &c, buf, pkey); + + if (!pk_len) + return -1; + + if (len == 0) + return pk_len; + + os_memcpy(key_buf, buf + MBEDTLS_MPI_MAX_SIZE + 10 - pk_len, pk_len); + + return pk_len; +} + +int crypto_write_pubkey_der(struct crypto_key *key, unsigned char **key_buf) +{ + unsigned char output_buf[1600] = {0}; + int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key, output_buf, 1600); + if (len <= 0) + return 0; + + *key_buf = os_malloc(len); + if (!*key_buf) { + return 0; + } + os_memcpy(*key_buf, output_buf + 1600 - len, len); + + return len; +} + +struct crypto_key *crypto_ec_get_key(const u8 *privkey, size_t privkey_len) +{ + int ret; + mbedtls_pk_context *kctx = (mbedtls_pk_context *)crypto_alloc_key(); + + if (!kctx) { + wpa_printf(MSG_ERROR, "memory allocation failed\n"); + return NULL; + } + ret = mbedtls_pk_parse_key(kctx, privkey, privkey_len, NULL, 0); + + if (ret < 0) { + //crypto_print_error_string(ret); + goto fail; + } + + return (struct crypto_key *)kctx; + +fail: + mbedtls_pk_free(kctx); + os_free(kctx); + return NULL; +} + +unsigned int crypto_ec_get_mbedtls_to_nist_group_id(int id) +{ + unsigned int nist_grpid = 0; + switch (id) { + case MBEDTLS_ECP_DP_SECP256R1: + nist_grpid = 19; + break; + case MBEDTLS_ECP_DP_SECP384R1: + nist_grpid = 20; + break; + case MBEDTLS_ECP_DP_SECP521R1: + nist_grpid = 21; + break; + case MBEDTLS_ECP_DP_BP256R1: + nist_grpid = 28; + break; + case MBEDTLS_ECP_DP_BP384R1: + nist_grpid = 29; + break; + case MBEDTLS_ECP_DP_BP512R1: + nist_grpid = 30; + break; + default: + break; + } + + return nist_grpid; +} + +int crypto_ec_get_curve_id(const struct crypto_ec_group *group) +{ + mbedtls_ecp_group *grp = (mbedtls_ecp_group *)group; + return (crypto_ec_get_mbedtls_to_nist_group_id(grp->id)); +} + +int crypto_ecdh(struct crypto_key *key_own, struct crypto_key *key_peer, + u8 *secret, size_t *secret_len) +{ + mbedtls_ecdh_context *ctx; + mbedtls_pk_context *own = (mbedtls_pk_context *)key_own; + mbedtls_pk_context *peer = (mbedtls_pk_context *)key_peer; + + int ret = -1; + + *secret_len = 0; + ctx = os_malloc(sizeof(*ctx)); + if (!ctx) { + wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s", + __func__); + return -1; + } + + mbedtls_ecdh_init(ctx); + /* No need to setup, done through mbedtls_ecdh_get_params */ + + /* set params from our key */ + if (mbedtls_ecdh_get_params(ctx, mbedtls_pk_ec(*own), MBEDTLS_ECDH_OURS) < 0) { + wpa_printf(MSG_ERROR, "failed to set our ecdh params\n"); + goto fail; + } + +#ifndef DPP_MAX_SHARED_SECRET_LEN +#define DPP_MAX_SHARED_SECRET_LEN 66 +#endif + /* set params from peers key */ + if (mbedtls_ecdh_get_params(ctx, mbedtls_pk_ec(*peer), MBEDTLS_ECDH_THEIRS) < 0) { + wpa_printf(MSG_ERROR, "failed to set peer's ecdh params\n"); + goto fail; + } + + if (mbedtls_ecdh_calc_secret(ctx, secret_len, secret, DPP_MAX_SHARED_SECRET_LEN, NULL, NULL) < 0) { + wpa_printf(MSG_ERROR, "failed to calculate secret\n"); + goto fail; + } + + if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) { + wpa_printf(MSG_ERROR, "secret len=%d is too big\n", *secret_len); + goto fail; + } + + ret = 0; + +fail: + mbedtls_ecdh_free(ctx); + os_free(ctx); + return ret; +} + + +int crypto_ecdsa_get_sign(unsigned char *hash, + const struct crypto_bignum *r, const struct crypto_bignum *s, struct crypto_key *csign, int hash_len) +{ + int ret = -1; + mbedtls_pk_context *pkey = (mbedtls_pk_context *)csign; + + mbedtls_ecdsa_context *ctx = os_malloc(sizeof(*ctx)); + if (!ctx) { + wpa_printf(MSG_ERROR,"failed to allcate memory\n"); + return -1; + } + mbedtls_ecdsa_init(ctx); + + if (mbedtls_ecdsa_from_keypair(ctx, mbedtls_pk_ec(*pkey)) < 0) { + goto fail; + } + ret = mbedtls_ecdsa_sign(&ctx->grp, (mbedtls_mpi *)r, (mbedtls_mpi *)s, + &ctx->d, hash, SHA256_MAC_LEN, crypto_rng_wrapper, NULL); + +fail: + mbedtls_ecdsa_free(ctx); + os_free(ctx); + + return ret; +} + +int crypto_edcsa_sign_verify(const unsigned char *hash, + const struct crypto_bignum *r, const struct crypto_bignum *s, struct crypto_key *csign, int hlen) +{ + mbedtls_pk_context *pkey = (mbedtls_pk_context *)csign; + int ret = 0; + + mbedtls_ecdsa_context *ctx = os_malloc(sizeof(*ctx)); + if (!ctx) { + wpa_printf(MSG_ERROR, "failed to allcate memory\n"); + return ret; + } + mbedtls_ecdsa_init(ctx); + + if (mbedtls_ecdsa_from_keypair(ctx, mbedtls_pk_ec(*pkey)) < 0) + return ret; + + if((ret = mbedtls_ecdsa_verify(&ctx->grp, hash, hlen, + &ctx->Q, (mbedtls_mpi *)r, (mbedtls_mpi *)s)) != 0){ + wpa_printf(MSG_ERROR, "ecdsa verification failed\n"); + return ret; + } + + mbedtls_ecdsa_free(ctx); + os_free(ctx); + + return ret; +} + +void crypto_debug_print_ec_key(const char *title, struct crypto_key *key) +{ +#ifdef DEBUG_PRINT + mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + mbedtls_ecp_keypair *ecp = mbedtls_pk_ec( *pkey ); + u8 x[32], y[32], d[32]; + wpa_printf(MSG_ERROR, "curve: %s\n", + mbedtls_ecp_curve_info_from_grp_id( ecp->grp.id )->name ); + int len = mbedtls_mpi_size((mbedtls_mpi *)crypto_ec_get_prime((struct crypto_ec *)crypto_ec_get_group_from_key(key))); + + wpa_printf(MSG_ERROR, "prime len is %d\n", len); + crypto_ec_point_to_bin((struct crypto_ec *)crypto_ec_get_group_from_key(key), crypto_ec_get_public_key(key), x, y); + crypto_bignum_to_bin(crypto_ec_get_private_key(key), + d, len, len); + wpa_hexdump(MSG_ERROR, "Q_x:", x, 32); + wpa_hexdump(MSG_ERROR, "Q_y:", y, 32); + wpa_hexdump(MSG_ERROR, "d: ", d , 32); +#endif +} + +struct crypto_key *crypto_ec_parse_subpub_key(const unsigned char *p, size_t len) +{ + int ret; + mbedtls_pk_context *pkey = (mbedtls_pk_context *)crypto_alloc_key(); + ret = mbedtls_pk_parse_subpubkey((unsigned char **)&p, p + len, pkey); + + if (ret < 0) { + os_free(pkey); + return NULL; + } + + return (struct crypto_key *)pkey; +} + +int crypto_is_ec_key(struct crypto_key *key) +{ + int ret = mbedtls_pk_can_do((mbedtls_pk_context *)key, MBEDTLS_PK_ECKEY); + return ret; +} + +struct crypto_key * crypto_ec_gen_keypair(u16 ike_group) +{ + mbedtls_pk_context *kctx = (mbedtls_pk_context *)crypto_alloc_key(); + + if (!kctx) { + wpa_printf(MSG_ERROR, "%s: memory allocation failed\n", __func__); + return NULL; + } + + if(mbedtls_pk_setup(kctx, + mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) != 0 ) + goto fail; + + mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*kctx), //get this from argument + crypto_rng_wrapper, NULL); + + return (struct crypto_key *)kctx; +fail: + mbedtls_pk_free(kctx); + os_free(kctx); + return NULL; +} + +/* + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * } + */ +static int pk_write_ec_param( unsigned char **p, unsigned char *start, + mbedtls_ecp_keypair *ec ) +{ + int ret; + size_t len = 0; + const char *oid; + size_t oid_len; + + if( ( ret = mbedtls_oid_get_oid_by_ec_grp( ec->grp.id, &oid, &oid_len ) ) != 0 ) + return( ret ); + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_oid( p, start, oid, oid_len ) ); + + return( (int) len ); +} + +static int pk_write_ec_pubkey_formatted( unsigned char **p, unsigned char *start, + mbedtls_ecp_keypair *ec, int format ) +{ + int ret; + size_t len = 0; + unsigned char buf[MBEDTLS_ECP_MAX_PT_LEN]; + + if( ( ret = mbedtls_ecp_point_write_binary( &ec->grp, &ec->Q, + format, + &len, buf, sizeof( buf ) ) ) != 0 ) + { + return( ret ); + } + + if( *p < start || (size_t)( *p - start ) < len ) + return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); + + *p -= len; + memcpy( *p, buf, len ); + + return( (int) len ); +} + +int mbedtls_pk_write_pubkey_formatted( unsigned char **p, unsigned char *start, + const mbedtls_pk_context *key, int format ) +{ + int ret; + size_t len = 0; + + if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY ) + MBEDTLS_ASN1_CHK_ADD( len, pk_write_ec_pubkey_formatted( p, start, mbedtls_pk_ec( *key ), format ) ); + else + return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE ); + + return( (int) len ); +} + +int crypto_pk_write_formatted_pubkey_der(mbedtls_pk_context *key, unsigned char *buf, size_t size, int format) +{ + int ret; + unsigned char *c; + size_t len = 0, par_len = 0, oid_len; + const char *oid; + + if( size == 0 ) + return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); + + c = buf + size; + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_pk_write_pubkey_formatted( &c, buf, key, format) ); + + if( c - buf < 1 ) + return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); + + /* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ + *--c = 0; + len += 1; + + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) ); + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_BIT_STRING ) ); + + if( ( ret = mbedtls_oid_get_oid_by_pk_alg( mbedtls_pk_get_type( key ), + &oid, &oid_len ) ) != 0 ) + { + return( ret ); + } + + if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY ) + { + MBEDTLS_ASN1_CHK_ADD( par_len, pk_write_ec_param( &c, buf, mbedtls_pk_ec( *key ) ) ); + } + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_algorithm_identifier( &c, buf, oid, oid_len, + par_len ) ); + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) ); + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE ) ); + + return( (int) len ); +} + +int crypto_ec_write_pub_key(struct crypto_key *key, unsigned char **key_buf) +{ + unsigned char output_buf[1600] = {0}; + int len = crypto_pk_write_formatted_pubkey_der((mbedtls_pk_context *)key, output_buf, 1600, 1); + if (len <= 0) + return 0; + + *key_buf = os_malloc(len); + if (!*key_buf) { + wpa_printf(MSG_ERROR, "%s: memory allocation failed\n", __func__); + return 0; + } + os_memcpy(*key_buf, output_buf + 1600 - len, len); + + return len; +} +#endif /* CONFIG_ECC */ diff --git a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c new file mode 100644 index 00000000..b8ea823d --- /dev/null +++ b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c @@ -0,0 +1,522 @@ +/* + * WPA Supplicant - RSN PMKSA cache + * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "rsn_supp/wpa.h" +#include "rsn_supp/wpa_i.h" +#include "common/eapol_common.h" +#include "common/ieee802_11_defs.h" +#include "pmksa_cache.h" +#include "esp_timer.h" + +#ifdef IEEE8021X_EAPOL + +static const int pmksa_cache_max_entries = 10; +static const int dot11RSNAConfigPMKLifetime = 43200; +static const int dot11RSNAConfigPMKReauthThreshold = 70; + +struct rsn_pmksa_cache { + struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ + int pmksa_count; /* number of entries in PMKSA cache */ + struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ + esp_timer_handle_t cache_timeout_timer; + + void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, + enum pmksa_free_reason reason); + void *ctx; +}; + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + wpa_bin_clear_free(entry, sizeof(*entry)); +} + + +static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry, + enum pmksa_free_reason reason) +{ + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx, reason); + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + int64_t now_sec = esp_timer_get_time() / 1e6; + + while (pmksa->pmksa && pmksa->pmksa->expiration <= now_sec) { + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + pmksa->pmksa = entry->next; + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->aa)); + pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); + } + + pmksa_cache_set_expiration(pmksa); +} + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) +{ + int sec; + int64_t now_sec = esp_timer_get_time() / 1e6; + + esp_timer_stop(pmksa->cache_timeout_timer); + if (pmksa->pmksa == NULL) + return; + sec = pmksa->pmksa->expiration - now_sec; + if (sec < 0) + sec = 0; + + esp_timer_start_once(pmksa->cache_timeout_timer, (sec + 1) * 1e6); +} + +/** + * pmksa_cache_add - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @network_ctx: Network configuration context for this PMK + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function create a PMKSA entry for a new PMK and adds it to the PMKSA + * cache. If an old entry is already in the cache for the same Authenticator, + * this entry will be replaced with the new entry. PMKID will be calculated + * based on the PMK and the driver interface is notified of the new PMKID. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + struct rsn_pmksa_cache_entry *entry, *pos, *prev; + int64_t now_sec = esp_timer_get_time() / 1e6; + + if (pmk_len > PMK_LEN) + return NULL; + + if (wpa_key_mgmt_suite_b(akmp) && !kck) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + if (pmkid) + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + else + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); + entry->expiration = now_sec + dot11RSNAConfigPMKLifetime; + entry->reauth_time = now_sec + dot11RSNAConfigPMKLifetime * + dot11RSNAConfigPMKReauthThreshold / 100; + entry->akmp = akmp; + os_memcpy(entry->aa, aa, ETH_ALEN); + entry->network_ctx = network_ctx; + + /* Replace an old entry for the same Authenticator (if found) with the + * new entry */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { + if (pos->pmk_len == pmk_len && + os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 && + os_memcmp_const(pos->pmkid, entry->pmkid, + PMKID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "WPA: reusing previous " + "PMKSA entry"); + os_free(entry); + return pos; + } + if (prev == NULL) + pmksa->pmksa = pos->next; + else + prev->next = pos->next; + + /* + * If OKC is used, there may be other PMKSA cache + * entries based on the same PMK. These needs to be + * flushed so that a new entry can be created based on + * the new PMK. Only clear other entries if they have a + * matching PMK and this PMK has been used successfully + * with the current AP, i.e., if opportunistic flag has + * been cleared in wpa_supplicant_key_neg_complete(). + */ + wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " + "the current AP and any PMKSA cache entry " + "that was based on the old PMK"); + if (!pos->opportunistic) + pmksa_cache_flush(pmksa, network_ctx, pos->pmk, + pos->pmk_len); + pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); + break; + } + prev = pos; + pos = pos->next; + } + + if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + pos = pmksa->pmksa; + + if (pos == pmksa->sm->cur_pmksa) { + /* + * Never remove the current PMKSA cache entry, since + * it's in use, and removing it triggers a needless + * deauthentication. + */ + pos = pos->next; + pmksa->pmksa->next = pos ? pos->next : NULL; + } else + pmksa->pmksa = pos->next; + + if (pos) { + wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " + "PMKSA cache entry (for " MACSTR ") to " + "make room for new one", + MAC2STR(pos->aa)); + pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); + } + } + + /* Add the new entry; order by expiration time */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = pmksa->pmksa; + pmksa->pmksa = entry; + pmksa_cache_set_expiration(pmksa); + } else { + entry->next = prev->next; + prev->next = entry; + } + pmksa->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR + " network_ctx=%p", MAC2STR(entry->aa), network_ctx); + + return entry; +} + + +/** + * pmksa_cache_flush - Flush PMKSA cache entries for a specific network + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context or %NULL to flush all entries + * @pmk: PMK to match for or %NYLL to match all PMKs + * @pmk_len: PMK length + */ +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len) +{ + struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; + int removed = 0; + + entry = pmksa->pmksa; + while (entry) { + if ((entry->network_ctx == network_ctx || + network_ctx == NULL) && + (pmk == NULL || + (pmk_len == entry->pmk_len && + os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " + "for " MACSTR, MAC2STR(entry->aa)); + if (prev) + prev->next = entry->next; + else + pmksa->pmksa = entry->next; + tmp = entry; + entry = entry->next; + pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); + removed++; + } else { + prev = entry; + entry = entry->next; + } + } + /*if (removed) + pmksa_cache_set_expiration(pmksa);*/ +} + + +/** + * pmksa_cache_deinit - Free all entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + */ +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + struct rsn_pmksa_cache_entry *entry, *prev; + + if (pmksa == NULL) + return; + + entry = pmksa->pmksa; + pmksa->pmksa = NULL; + while (entry) { + prev = entry; + entry = entry->next; + os_free(prev); + } + pmksa_cache_set_expiration(pmksa); + esp_timer_stop(pmksa->cache_timeout_timer); + esp_timer_delete(pmksa->cache_timeout_timer); + os_free(pmksa); +} + + +/** + * pmksa_cache_get - Fetch a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @aa: Authenticator address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * @network_ctx: Network context or %NULL to match any + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *aa, const u8 *pmkid, + const void *network_ctx) +{ + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + while (entry) { + if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && + (pmkid == NULL || + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && + (network_ctx == NULL || network_ctx == entry->network_ctx)) + return entry; + entry = entry->next; + } + return NULL; +} + + +static struct rsn_pmksa_cache_entry * +pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa) +{ + struct rsn_pmksa_cache_entry *new_entry; + + new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, + NULL, NULL, 0, + aa, pmksa->sm->own_addr, + old_entry->network_ctx, old_entry->akmp); + if (new_entry == NULL) + return NULL; + + /* TODO: reorder entries based on expiration time? */ + new_entry->expiration = old_entry->expiration; + new_entry->opportunistic = 1; + + return new_entry; +} + + +/** + * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context + * @aa: Authenticator address for the new AP + * Returns: Pointer to a new PMKSA cache entry or %NULL if not available + * + * Try to create a new PMKSA cache entry opportunistically by guessing that the + * new AP is sharing the same PMK as another AP that has the same SSID and has + * already an entry in PMKSA cache. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *aa) +{ + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + + wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); + if (network_ctx == NULL) + return NULL; + while (entry) { + if (entry->network_ctx == network_ctx) { + entry = pmksa_cache_clone_entry(pmksa, entry, aa); + if (entry) { + wpa_printf(MSG_DEBUG, "RSN: added " + "opportunistic PMKSA cache entry " + "for " MACSTR, MAC2STR(aa)); + } + return entry; + } + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_get_current - Get the current used PMKSA entry + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to the current PMKSA cache entry or %NULL if not available + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return NULL; + return sm->cur_pmksa; +} + + +/** + * pmksa_cache_clear_current - Clear the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_cache_clear_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + sm->cur_pmksa = NULL; +} + + +/** + * pmksa_cache_set_current - Set the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmkid: PMKID for selecting PMKSA or %NULL if not used + * @bssid: BSSID for PMKSA or %NULL if not used + * @network_ctx: Network configuration context + * @try_opportunistic: Whether to allow opportunistic PMKSA caching + * Returns: 0 if PMKSA was found or -1 if no matching entry was found + */ +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, void *network_ctx, + int try_opportunistic) +{ + struct rsn_pmksa_cache *pmksa = sm->pmksa; + wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " + "try_opportunistic=%d", network_ctx, try_opportunistic); + if (pmkid) + wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", + pmkid, PMKID_LEN); + if (bssid) + wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, + MAC2STR(bssid)); + + sm->cur_pmksa = NULL; + if (pmkid) + sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, + network_ctx); + if (sm->cur_pmksa == NULL && bssid) + sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, + network_ctx); + if (sm->cur_pmksa == NULL && try_opportunistic && bssid) + sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, + network_ctx, + bssid); + if (sm->cur_pmksa) { + wpa_hexdump(MSG_ERROR, "RSN: PMKSA cache entry found - PMKID", + sm->cur_pmksa->pmkid, PMKID_LEN); + return 0; + } + wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); + return -1; +} + + +/** + * pmksa_cache_list - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA command. + */ +int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) +{ + int i, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + int64_t now_sec = esp_timer_get_time() / 1e6; + ret = os_snprintf(pos, buf + len - pos, + "Index / AA / PMKID / expiration (in seconds) / " + "opportunistic\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + i = 0; + entry = pmksa->pmksa; + while (entry) { + i++; + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->aa)); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, + PMKID_LEN); + ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + (int) (entry->expiration - now_sec), + entry->opportunistic); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + entry = entry->next; + } + return pos - buf; +} + + +/** + * pmksa_cache_init - Initialize PMKSA cache + * @free_cb: Callback function to be called when a PMKSA cache entry is freed + * @ctx: Context pointer for free_cb function + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to PMKSA cache data or %NULL on failure + */ +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + void *ctx, struct wpa_sm *sm) +{ + struct rsn_pmksa_cache *pmksa; + + pmksa = os_zalloc(sizeof(*pmksa)); + if (pmksa) { + pmksa->free_cb = free_cb; + pmksa->ctx = ctx; + pmksa->sm = sm; + pmksa->pmksa_count = 0; + pmksa->pmksa = NULL; + + esp_timer_create_args_t pmksa_cache_timeout_timer_create = { + .callback = &pmksa_cache_expire, + .arg = pmksa, + .dispatch_method = ESP_TIMER_TASK, + .name = "pmksa_timeout_timer" + }; + esp_timer_create(&pmksa_cache_timeout_timer_create, &(pmksa->cache_timeout_timer)); + } + + return pmksa; +} + +#endif /* IEEE8021X_EAPOL */ diff --git a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h new file mode 100644 index 00000000..e2c9156a --- /dev/null +++ b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h @@ -0,0 +1,134 @@ +/* + * wpa_supplicant - WPA2/RSN PMKSA cache functions + * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PMKSA_CACHE_H +#define PMKSA_CACHE_H + +/** + * struct rsn_pmksa_cache_entry - PMKSA cache entry + */ +struct rsn_pmksa_cache_entry { + struct rsn_pmksa_cache_entry *next; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 aa[ETH_ALEN]; + + os_time_t reauth_time; + + /** + * network_ctx - Network configuration context + * + * This field is only used to match PMKSA cache entries to a specific + * network configuration (e.g., a specific SSID and security policy). + * This can be a pointer to the configuration entry, but PMKSA caching + * code does not dereference the value and this could be any kind of + * identifier. + */ + void *network_ctx; + int opportunistic; +}; + +struct rsn_pmksa_cache; + +enum pmksa_free_reason { + PMKSA_FREE, + PMKSA_REPLACE, + PMKSA_EXPIRE, +}; + +#ifdef IEEE8021X_EAPOL + +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + void *ctx, struct wpa_sm *sm); +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *aa, const u8 *pmkid, + const void *network_ctx); +int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); +struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp); +struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); +void pmksa_cache_clear_current(struct wpa_sm *sm); +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, void *network_ctx, + int try_opportunistic); +struct rsn_pmksa_cache_entry * +pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, + void *network_ctx, const u8 *aa); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len); + +#else /* IEEE8021X_EAPOL */ + + static inline struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + void *ctx, struct wpa_sm *sm) +{ + return (void *) -1; +} + +static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ +} + + static inline struct rsn_pmksa_cache_entry * +pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, + const void *network_ctx) +{ + return NULL; +} + + static inline struct rsn_pmksa_cache_entry * +pmksa_cache_get_current(struct wpa_sm *sm) +{ + return NULL; +} + +static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, + size_t len) +{ + return -1; +} + + static inline struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + return NULL; +} + +static inline void pmksa_cache_clear_current(struct wpa_sm *sm) +{ +} + +static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, + void *network_ctx, + int try_opportunistic) +{ + return -1; +} + +static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, + void *network_ctx, + const u8 *pmk, size_t pmk_len) +{ +} + +#endif /* IEEE8021X_EAPOL */ + +#endif /* PMKSA_CACHE_H */