mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-07-15 08:32:42 +08:00
feat(wpa_supplicant): add PMF and WPA3 support
This commit is contained in:
@ -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
|
240
components/wpa_supplicant/port/esp_supplicant/esp_wpa3.c
Normal file
240
components/wpa_supplicant/port/esp_supplicant/esp_wpa3.c
Normal file
@ -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 */
|
28
components/wpa_supplicant/port/esp_supplicant/esp_wpa3_i.h
Normal file
28
components/wpa_supplicant/port/esp_supplicant/esp_wpa3_i.h
Normal file
@ -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 */
|
1453
components/wpa_supplicant/src/common/sae.c
Normal file
1453
components/wpa_supplicant/src/common/sae.c
Normal file
File diff suppressed because it is too large
Load Diff
89
components/wpa_supplicant/src/common/sae.h
Normal file
89
components/wpa_supplicant/src/common/sae.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Simultaneous authentication of equals
|
||||
* Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* 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 */
|
215
components/wpa_supplicant/src/crypto/aes-ccm.c
Normal file
215
components/wpa_supplicant/src/crypto/aes-ccm.c
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Counter with CBC-MAC (CCM) with AES
|
||||
*
|
||||
* Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* 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 */
|
169
components/wpa_supplicant/src/crypto/aes-omac1.c
Normal file
169
components/wpa_supplicant/src/crypto/aes-omac1.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* One-key CBC MAC (OMAC1) hash with AES
|
||||
*
|
||||
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* 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);
|
||||
}
|
354
components/wpa_supplicant/src/crypto/ccmp.c
Normal file
354
components/wpa_supplicant/src/crypto/ccmp.c
Normal file
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* CTR with CBC-MAC Protocol (CCMP)
|
||||
* Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* 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 */
|
28
components/wpa_supplicant/src/crypto/ccmp.h
Normal file
28
components/wpa_supplicant/src/crypto/ccmp.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* wlantest - IEEE 802.11 protocol monitoring and testing tool
|
||||
* Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* 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 */
|
273
components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c
Normal file
273
components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c
Normal file
@ -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);
|
||||
}
|
988
components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c
Normal file
988
components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c
Normal file
@ -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 */
|
522
components/wpa_supplicant/src/rsn_supp/pmksa_cache.c
Normal file
522
components/wpa_supplicant/src/rsn_supp/pmksa_cache.c
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
* WPA Supplicant - RSN PMKSA cache
|
||||
* Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* 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 */
|
134
components/wpa_supplicant/src/rsn_supp/pmksa_cache.h
Normal file
134
components/wpa_supplicant/src/rsn_supp/pmksa_cache.h
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* wpa_supplicant - WPA2/RSN PMKSA cache functions
|
||||
* Copyright (c) 2003-2009, 2011-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* 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 */
|
Reference in New Issue
Block a user