feat(wpa_supplicant): add PMF and WPA3 support

This commit is contained in:
Zhang Jun Hao
2020-05-25 21:36:16 +08:00
parent 4c94b94e01
commit 75f1ceeb64
13 changed files with 4577 additions and 0 deletions

View File

@ -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

View 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 */

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

View 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);
}

View 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 */

View 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 */

View 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);
}

View 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 */

View 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 */

View 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 */