Import Mbed OS hard-float snapshot

This commit is contained in:
Beslan
2026-06-01 20:15:04 +03:00
commit d3738e2f89
16278 changed files with 10628036 additions and 0 deletions

View File

@@ -0,0 +1,462 @@
/*
* Copyright (c) 2016-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#include "nsconfig.h"
#include <string.h>
#include "ns_types.h"
#include "ns_list.h"
#include "ns_trace.h"
#include "nsdynmemLIB.h"
#include "fhss_config.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "6LoWPAN/ws/ws_config.h"
#include "6LoWPAN/ws/ws_cfg_settings.h"
#include "Security/protocols/sec_prot_cfg.h"
#include "Security/kmp/kmp_addr.h"
#include "Security/kmp/kmp_api.h"
#include "Security/PANA/pana_eap_header.h"
#include "Security/eapol/eapol_helper.h"
#include "Security/eapol/kde_helper.h"
#include "Security/protocols/sec_prot_certs.h"
#include "Security/protocols/sec_prot_keys.h"
#include "Security/protocols/sec_prot.h"
#include "Security/protocols/sec_prot_lib.h"
#include "Security/protocols/fwh_sec_prot/auth_fwh_sec_prot.h"
#include "Service_Libs/hmac/hmac_sha1.h"
#include "Service_Libs/nist_aes_kw/nist_aes_kw.h"
#ifdef HAVE_WS
#define TRACE_GROUP "afwh"
typedef enum {
FWH_STATE_INIT = SEC_STATE_INIT,
FWH_STATE_CREATE_REQ = SEC_STATE_CREATE_REQ,
FWH_STATE_MESSAGE_2 = SEC_STATE_FIRST,
FWH_STATE_MESSAGE_4,
FWH_STATE_FINISH = SEC_STATE_FINISH,
FWH_STATE_FINISHED = SEC_STATE_FINISHED
} fwh_sec_prot_state_e;
typedef enum {
FWH_MESSAGE_UNKNOWN = 0,
FWH_MESSAGE_1,
FWH_MESSAGE_2,
FWH_MESSAGE_3,
FWH_MESSAGE_4
} fwh_sec_prot_msg_e;
typedef struct {
sec_prot_common_t common; /**< Common data */
eapol_pdu_t recv_eapol_pdu; /**< Received EAPOL PDU */
fwh_sec_prot_msg_e recv_msg; /**< Received message */
uint8_t nonce[EAPOL_KEY_NONCE_LEN]; /**< Authenticator nonce */
uint8_t new_ptk[PTK_LEN]; /**< PTK (384 bits) */
uint8_t remote_eui64[8]; /**< Remote EUI-64 used to calculate PTK */
void *recv_pdu; /**< received pdu */
uint16_t recv_size; /**< received pdu size */
} fwh_sec_prot_int_t;
static uint16_t auth_fwh_sec_prot_size(void);
static int8_t auth_fwh_sec_prot_init(sec_prot_t *prot);
static void auth_fwh_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys);
static void auth_fwh_sec_prot_delete(sec_prot_t *prot);
static int8_t auth_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size);
static fwh_sec_prot_msg_e auth_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys);
static void auth_fwh_sec_prot_state_machine(sec_prot_t *prot);
static int8_t auth_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_e msg);
static void auth_fwh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks);
static int8_t auth_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t *sec_keys);
static int8_t auth_fwh_sec_prot_mic_validate(sec_prot_t *prot);
#define fwh_sec_prot_get(prot) (fwh_sec_prot_int_t *) &prot->data
int8_t auth_fwh_sec_prot_register(kmp_service_t *service)
{
if (!service) {
return -1;
}
if (kmp_service_sec_protocol_register(service, IEEE_802_11_4WH, auth_fwh_sec_prot_size, auth_fwh_sec_prot_init) < 0) {
return -1;
}
return 0;
}
static uint16_t auth_fwh_sec_prot_size(void)
{
return sizeof(fwh_sec_prot_int_t);
}
static int8_t auth_fwh_sec_prot_init(sec_prot_t *prot)
{
prot->create_req = auth_fwh_sec_prot_create_request;
prot->create_resp = 0;
prot->receive = auth_fwh_sec_prot_receive;
prot->delete = auth_fwh_sec_prot_delete;
prot->state_machine = auth_fwh_sec_prot_state_machine;
prot->timer_timeout = auth_fwh_sec_prot_timer_timeout;
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
sec_prot_init(&data->common);
sec_prot_state_set(prot, &data->common, FWH_STATE_INIT);
data->common.ticks = 15 * 10; // 15 seconds
uint8_t eui64[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
sec_prot_lib_nonce_init(data->nonce, eui64, 1000);
return 0;
}
static void auth_fwh_sec_prot_delete(sec_prot_t *prot)
{
// No op at the moment
(void) prot;
}
static void auth_fwh_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys)
{
prot->sec_keys = sec_keys;
// Call state machine
prot->state_machine_call(prot);
}
static int8_t auth_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
int8_t ret_val = -1;
// Decoding is successful
if (eapol_parse_pdu_header(pdu, size, &data->recv_eapol_pdu)) {
// Get message
data->recv_msg = auth_fwh_sec_prot_message_get(&data->recv_eapol_pdu, prot->sec_keys);
if (data->recv_msg != FWH_MESSAGE_UNKNOWN) {
tr_info("4WH: recv %s, eui-64: %s", data->recv_msg == FWH_MESSAGE_2 ? "Message 2" : "Message 4", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
// Call state machine
data->recv_pdu = pdu;
data->recv_size = size;
prot->state_machine(prot);
} else {
tr_error("4WH: recv error, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
}
ret_val = 0;
} else {
tr_error("4WH: recv error, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
}
memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t));
data->recv_msg = FWH_MESSAGE_UNKNOWN;
data->recv_pdu = 0;
data->recv_size = 0;
return ret_val;
}
static fwh_sec_prot_msg_e auth_fwh_sec_prot_message_get(eapol_pdu_t *eapol_pdu, sec_prot_keys_t *sec_keys)
{
fwh_sec_prot_msg_e msg = FWH_MESSAGE_UNKNOWN;
if (!eapol_pdu->msg.key.key_information.pairwise_key) {
// This is mismatch between KMP ID indicating 802.11/4WH and key type
return FWH_MESSAGE_UNKNOWN;
}
uint8_t key_mask = eapol_pdu_key_mask_get(eapol_pdu);
switch (key_mask) {
case KEY_INFO_KEY_MIC:
// Only accept message from supplicant with expected replay counter
if (eapol_pdu->msg.key.replay_counter == sec_prot_keys_pmk_replay_cnt_get(sec_keys)) {
msg = FWH_MESSAGE_2;
}
break;
case KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME:
// Only accept message from supplicant with expected replay counter
if (eapol_pdu->msg.key.replay_counter == sec_prot_keys_pmk_replay_cnt_get(sec_keys)) {
msg = FWH_MESSAGE_4;
}
break;
default:
break;
}
return msg;
}
static int8_t auth_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_e msg)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
uint16_t kde_len = 0;
switch (msg) {
case FWH_MESSAGE_1:
kde_len = KDE_PMKID_LEN;
break;
case FWH_MESSAGE_3:
kde_len = KDE_GTK_LEN + KDE_LIFETIME_LEN + KDE_GTKL_LEN;
kde_len = kde_len + 8; // One 64 bit block for AES Key Wrap
kde_len = kde_padded_length_calc(kde_len);
break;
default:
break;
}
uint8_t *kde_start = ns_dyn_mem_temporary_alloc(kde_len);
if (!kde_start) {
return -1;
}
uint8_t *kde_end = kde_start;
switch (msg) {
case FWH_MESSAGE_1: {
uint8_t pmkid[PMKID_LEN];
if (sec_prot_lib_pmkid_generate(prot, pmkid, true) < 0) {
ns_dyn_mem_free(kde_start);
return -1;
}
kde_end = kde_pmkid_write(kde_end, pmkid);
}
break;
case FWH_MESSAGE_3: {
uint8_t gtk_index;
uint8_t *gtk = sec_prot_keys_get_gtk_to_insert(prot->sec_keys, &gtk_index);
if (gtk) {
kde_end = kde_gtk_write(kde_end, gtk_index, gtk);
uint32_t gtk_lifetime = sec_prot_keys_gtk_lifetime_get(prot->sec_keys->gtks, gtk_index);
kde_end = kde_lifetime_write(kde_end, gtk_lifetime);
}
uint8_t gtkl = sec_prot_keys_fresh_gtkl_get(prot->sec_keys->gtks);
kde_end = kde_gtkl_write(kde_end, gtkl);
kde_padding_write(kde_end, kde_start + kde_len);
}
break;
default:
break;
}
eapol_pdu_t eapol_pdu;
uint16_t eapol_pdu_size = eapol_pdu_key_frame_init(&eapol_pdu, kde_len, NULL);
eapol_pdu.msg.key.key_information.pairwise_key = true;
switch (msg) {
case FWH_MESSAGE_1:
if (!sec_prot_keys_pmk_replay_cnt_increment(prot->sec_keys)) {
ns_dyn_mem_free(kde_start);
return 1;
}
eapol_pdu.msg.key.replay_counter = sec_prot_keys_pmk_replay_cnt_get(prot->sec_keys);
eapol_pdu.msg.key.key_information.key_ack = true;
eapol_pdu.msg.key.key_length = EAPOL_KEY_LEN;
eapol_pdu.msg.key.key_nonce = data->nonce;
break;
case FWH_MESSAGE_3:
if (!sec_prot_keys_pmk_replay_cnt_increment(prot->sec_keys)) {
ns_dyn_mem_free(kde_start);
return -1;
}
eapol_pdu.msg.key.replay_counter = sec_prot_keys_pmk_replay_cnt_get(prot->sec_keys);
eapol_pdu.msg.key.key_information.install = true;
eapol_pdu.msg.key.key_information.key_ack = true;
eapol_pdu.msg.key.key_information.key_mic = true;
eapol_pdu.msg.key.key_information.secured_key_frame = true;
eapol_pdu.msg.key.key_information.encrypted_key_data = true;
eapol_pdu.msg.key.key_nonce = data->nonce;
eapol_pdu.msg.key.key_length = EAPOL_KEY_LEN;
break;
default:
break;
}
uint8_t *eapol_pdu_frame = sec_prot_lib_message_build(data->new_ptk, kde_start, kde_len, &eapol_pdu, eapol_pdu_size, prot->header_size);
ns_dyn_mem_free(kde_start);
if (eapol_pdu_frame == NULL) {
return -1;
}
tr_info("4WH: send %s, eui-64: %s", msg == FWH_MESSAGE_1 ? "Message 1" : "Message 3", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) {
return -1;
}
return 0;
}
static void auth_fwh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
sec_prot_timer_timeout_handle(prot, &data->common, &prot->prot_cfg->sec_prot_trickle_params, ticks);
}
static void auth_fwh_sec_prot_state_machine(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
// 4WH authenticator state machine
switch (sec_prot_state_get(&data->common)) {
case FWH_STATE_INIT:
tr_info("4WH: init");
prot->timer_start(prot);
sec_prot_state_set(prot, &data->common, FWH_STATE_CREATE_REQ);
break;
// Wait KMP-CREATE.request
case FWH_STATE_CREATE_REQ:
tr_info("4WH: start, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
// Set default timeout for the total maximum length of the negotiation
sec_prot_default_timeout_set(&data->common);
uint8_t *pmk = sec_prot_keys_pmk_get(prot->sec_keys);
if (!pmk) { // If PMK is not set fails
prot->create_conf(prot, SEC_RESULT_ERROR);
sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED);
return;
}
// KMP-CREATE.confirm
prot->create_conf(prot, SEC_RESULT_OK);
// Sends 4WH Message 1
sec_prot_lib_nonce_generate(data->nonce);
auth_fwh_sec_prot_message_send(prot, FWH_MESSAGE_1);
// Start trickle timer to re-send if no response
sec_prot_timer_trickle_start(&data->common, &prot->prot_cfg->sec_prot_trickle_params);
sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_2);
break;
// Wait 4WH message 2
case FWH_STATE_MESSAGE_2:
if (sec_prot_result_timeout_check(&data->common)) {
// Re-sends 4WH Message 1
sec_prot_lib_nonce_generate(data->nonce);
auth_fwh_sec_prot_message_send(prot, FWH_MESSAGE_1);
} else {
if (data->recv_msg != FWH_MESSAGE_2) {
return;
}
if (auth_fwh_sec_prot_ptk_generate(prot, prot->sec_keys) < 0) {
return;
}
if (auth_fwh_sec_prot_mic_validate(prot) < 0) {
memset(data->new_ptk, 0, PTK_LEN);
return;
}
// Sends 4WH Message 3
auth_fwh_sec_prot_message_send(prot, FWH_MESSAGE_3);
// Start trickle timer to re-send if no response
sec_prot_timer_trickle_start(&data->common, &prot->prot_cfg->sec_prot_trickle_params);
sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_4);
}
break;
// Wait 4WH message 4
case FWH_STATE_MESSAGE_4:
if (sec_prot_result_timeout_check(&data->common)) {
// Re-sends 4WH Message 3
auth_fwh_sec_prot_message_send(prot, FWH_MESSAGE_3);
} else {
if (data->recv_msg != FWH_MESSAGE_4) {
return;
}
if (auth_fwh_sec_prot_mic_validate(prot) < 0) {
return;
}
// PTK is fresh for installing any GTKs
sec_prot_keys_ptk_installed_gtk_hash_clear_all(prot->sec_keys);
/* Store the hash for to-be installed GTK as used for the PTK, on 4WH
this stores only the hash in NVM and does not affect otherwise */
sec_prot_keys_ptk_installed_gtk_hash_set(prot->sec_keys, true);
// If GTK was inserted set it valid
sec_prot_keys_gtkl_from_gtk_insert_index_set(prot->sec_keys);
// Reset PTK mismatch
sec_prot_keys_ptk_mismatch_reset(prot->sec_keys);
// Update PTK
sec_prot_keys_ptk_write(prot->sec_keys, data->new_ptk, prot->timer_cfg->ptk_lifetime);
sec_prot_keys_ptk_eui_64_write(prot->sec_keys, data->remote_eui64);
sec_prot_state_set(prot, &data->common, FWH_STATE_FINISH);
}
break;
case FWH_STATE_FINISH:
tr_info("4WH: finish, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
// KMP-FINISHED.indication,
prot->finished_ind(prot, sec_prot_result_get(&data->common), 0);
sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED);
break;
case FWH_STATE_FINISHED: {
uint8_t *remote_eui_64 = sec_prot_remote_eui_64_addr_get(prot);
tr_info("4WH: finished, eui-64: %s", remote_eui_64 ? trace_array(sec_prot_remote_eui_64_addr_get(prot), 8) : "not set");
prot->timer_stop(prot);
prot->finished(prot);
break;
}
default:
break;
}
}
static int8_t auth_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t *sec_keys)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
uint8_t local_eui64[8];
prot->addr_get(prot, local_eui64, data->remote_eui64);
uint8_t *remote_nonce = data->recv_eapol_pdu.msg.key.key_nonce;
if (!remote_nonce) {
tr_error("SNonce invalid");
return 1;
}
uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys);
sec_prot_lib_ptk_calc(pmk, local_eui64, data->remote_eui64, data->nonce, remote_nonce, data->new_ptk);
return 0;
}
static int8_t auth_fwh_sec_prot_mic_validate(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
return sec_prot_lib_mic_validate(data->new_ptk, data->recv_eapol_pdu.msg.key.key_mic, data->recv_pdu, data->recv_size);
}
#endif /* HAVE_WS */

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2016-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* 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 AUTH_FWH_SEC_PROT_H_
#define AUTH_FWH_SEC_PROT_H_
/*
* Authenticator Four Way Handshake (4WH) security protocol. 4WH protocol is
* specified in IEEE 802.11 and Wi-SUN FANWG-FANTPS.
*
*/
/**
* auth_fwh_sec_prot_register register authenticator 4WH protocol to KMP service
*
* \param service KMP service
*
* \return < 0 failure
* \return >= 0 success
*/
int8_t auth_fwh_sec_prot_register(kmp_service_t *service);
/**
* auth_fwh_sec_prot_timing_adjust Adjust retries and timings of the 4WH protocol
*
* Timing value is a generic number between 0 to 32 that goes from fast and
* reactive network to low bandwidth and long latency.
*
* example value definitions:
* 0-8 very fast network
* 9-16 medium network
* 16-24 slow network
* 25-32 extremely slow network
*
* There is no need to have lots variations in every layer if protocol is not very active in any case.
*
* \param timing Timing value.
*
* \return < 0 failure
* \return >= 0 success
*
*/
int8_t auth_fwh_sec_prot_timing_adjust(uint8_t timing);
#endif /* AUTH_FWH_SEC_PROT_H_ */

View File

@@ -0,0 +1,585 @@
/*
* Copyright (c) 2016-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#include "nsconfig.h"
#include <string.h>
#include "ns_types.h"
#include "ns_list.h"
#include "ns_trace.h"
#include "nsdynmemLIB.h"
#include "fhss_config.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "6LoWPAN/ws/ws_config.h"
#include "Security/protocols/sec_prot_cfg.h"
#include "Security/kmp/kmp_addr.h"
#include "Security/kmp/kmp_api.h"
#include "Security/PANA/pana_eap_header.h"
#include "Security/eapol/eapol_helper.h"
#include "Security/eapol/kde_helper.h"
#include "Security/protocols/sec_prot_certs.h"
#include "Security/protocols/sec_prot_keys.h"
#include "Security/protocols/sec_prot.h"
#include "Security/protocols/sec_prot_lib.h"
#include "Security/protocols/fwh_sec_prot/supp_fwh_sec_prot.h"
#include "Service_Libs/hmac/hmac_sha1.h"
#include "Service_Libs/nist_aes_kw/nist_aes_kw.h"
#ifdef HAVE_WS
#define TRACE_GROUP "sfwh"
typedef enum {
FWH_STATE_INIT = SEC_STATE_INIT,
FWH_STATE_CREATE_RESP = SEC_STATE_CREATE_RESP,
FWH_STATE_CREATE_IND = SEC_STATE_CREATE_IND,
FWH_STATE_MESSAGE_1 = SEC_STATE_FIRST,
FWH_STATE_MESSAGE_3,
FWH_STATE_MESSAGE_3_RETRY_WAIT,
FWH_STATE_CREATE_RESP_SUPP_RETRY,
FWH_STATE_FINISH = SEC_STATE_FINISH,
FWH_STATE_FINISHED = SEC_STATE_FINISHED
} fwh_sec_prot_state_e;
typedef enum {
FWH_MESSAGE_UNKNOWN = 0,
FWH_MESSAGE_1,
FWH_MESSAGE_2,
FWH_MESSAGE_3,
FWH_MESSAGE_4
} fwh_sec_prot_msg_e;
#define KEY_INFO_INSTALL 0x01
#define KEY_INFO_KEY_ACK 0x02
#define KEY_INFO_KEY_MIC 0x04
#define KEY_INFO_SECURED_KEY_FRAME 0x08
typedef struct {
sec_prot_common_t common; /**< Common data */
eapol_pdu_t recv_eapol_pdu; /**< Received EAPOL PDU */
fwh_sec_prot_msg_e recv_msg; /**< Received message */
uint8_t snonce[EAPOL_KEY_NONCE_LEN]; /**< Supplicant nonce */
uint8_t anonce[EAPOL_KEY_NONCE_LEN]; /**< Authenticator nonce */
uint8_t new_ptk[PTK_LEN]; /**< PTK (384 bits) */
uint8_t remote_eui64[8]; /**< Remote EUI-64 used to calculate PTK */
void *recv_pdu; /**< received pdu */
uint16_t recv_size; /**< received pdu size */
uint64_t recv_replay_cnt; /**< received replay counter */
bool msg3_received : 1; /**< Valid Message 3 has been received */
bool msg3_retry_wait : 1; /**< Waiting for Message 3 retry */
bool recv_replay_cnt_set : 1; /**< received replay counter set */
} fwh_sec_prot_int_t;
static uint16_t supp_fwh_sec_prot_size(void);
static int8_t supp_fwh_sec_prot_init(sec_prot_t *prot);
static void supp_fwh_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result);
static void supp_fwh_sec_prot_delete(sec_prot_t *prot);
static int8_t supp_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size);
static fwh_sec_prot_msg_e supp_fwh_sec_prot_message_get(sec_prot_t *prot, eapol_pdu_t *eapol_pdu);
static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot);
static int8_t supp_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_e msg);
static void supp_fwh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks);
static int8_t supp_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t *sec_keys);
static int8_t supp_fwh_sec_prot_mic_validate(sec_prot_t *prot);
static void supp_fwh_sec_prot_recv_replay_counter_store(sec_prot_t *prot);
static bool supp_fwh_sec_prot_recv_replay_cnt_compare(uint64_t received_counter, sec_prot_t *prot);
static void supp_fwh_sec_prot_anonce_store(sec_prot_t *prot);
static int8_t supp_fwh_sec_prot_anonce_validate(sec_prot_t *prot);
static void supp_fwh_sec_prot_security_replay_counter_update(sec_prot_t *prot);
static int8_t supp_fwh_kde_handle(sec_prot_t *prot);
#define fwh_sec_prot_get(prot) (fwh_sec_prot_int_t *) &prot->data
int8_t supp_fwh_sec_prot_register(kmp_service_t *service)
{
if (!service) {
return -1;
}
if (kmp_service_sec_protocol_register(service, IEEE_802_11_4WH, supp_fwh_sec_prot_size, supp_fwh_sec_prot_init) < 0) {
return -1;
}
return 0;
}
static uint16_t supp_fwh_sec_prot_size(void)
{
return sizeof(fwh_sec_prot_int_t);
}
static int8_t supp_fwh_sec_prot_init(sec_prot_t *prot)
{
prot->create_req = 0;
prot->create_resp = supp_fwh_sec_prot_create_response;
prot->receive = supp_fwh_sec_prot_receive;
prot->delete = supp_fwh_sec_prot_delete;
prot->state_machine = supp_fwh_sec_prot_state_machine;
prot->timer_timeout = supp_fwh_sec_prot_timer_timeout;
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
sec_prot_init(&data->common);
sec_prot_state_set(prot, &data->common, FWH_STATE_INIT);
data->common.ticks = prot->prot_cfg->sec_prot_retry_timeout;
data->msg3_received = false;
data->msg3_retry_wait = false;
data->recv_replay_cnt = 0;
data->recv_replay_cnt_set = false;
uint8_t eui64[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
sec_prot_lib_nonce_init(data->snonce, eui64, 1000);
return 0;
}
static void supp_fwh_sec_prot_delete(sec_prot_t *prot)
{
// No op at the moment
(void) prot;
}
static void supp_fwh_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
// Call state machine
sec_prot_result_set(&data->common, result);
prot->state_machine_call(prot);
}
static int8_t supp_fwh_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
int8_t ret_val = -1;
// Decoding is successful
if (eapol_parse_pdu_header(pdu, size, &data->recv_eapol_pdu)) {
// Get message
data->recv_msg = supp_fwh_sec_prot_message_get(prot, &data->recv_eapol_pdu);
if (data->recv_msg != FWH_MESSAGE_UNKNOWN) {
tr_info("4WH: recv %s", data->recv_msg == FWH_MESSAGE_1 ? "Message 1" : "Message 3");
// Call state machine
data->recv_pdu = pdu;
data->recv_size = size;
prot->state_machine(prot);
} else {
tr_error("4WH: recv error");
}
ret_val = 0;
} else {
tr_error("4WH: recv error");
}
memset(&data->recv_eapol_pdu, 0, sizeof(eapol_pdu_t));
data->recv_msg = FWH_MESSAGE_UNKNOWN;
data->recv_pdu = 0;
data->recv_size = 0;
return ret_val;
}
static fwh_sec_prot_msg_e supp_fwh_sec_prot_message_get(sec_prot_t *prot, eapol_pdu_t *eapol_pdu)
{
fwh_sec_prot_msg_e msg = FWH_MESSAGE_UNKNOWN;
if (!eapol_pdu->msg.key.key_information.pairwise_key) {
// This is mismatch between KMP ID indicating 802.11/4WH and key type
return FWH_MESSAGE_UNKNOWN;
}
uint8_t key_mask = eapol_pdu_key_mask_get(eapol_pdu);
switch (key_mask) {
// Message 1
case KEY_INFO_KEY_ACK:
/* Must have valid replay counter, both larger for PMK and larger that is used on
* the four way handshake session (note: PMK replay counter is not updated for Message 1
* but session specific counter is)
*/
if (sec_prot_keys_pmk_replay_cnt_compare(eapol_pdu->msg.key.replay_counter, prot->sec_keys) &&
supp_fwh_sec_prot_recv_replay_cnt_compare(eapol_pdu->msg.key.replay_counter, prot)) {
msg = FWH_MESSAGE_1;
} else {
tr_error("4WH: invalid replay counter %"PRId64, eapol_pdu->msg.key.replay_counter);
}
break;
// Message 3
case KEY_INFO_INSTALL | KEY_INFO_KEY_ACK | KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME:
// Must have valid replay counter
if (sec_prot_keys_pmk_replay_cnt_compare(eapol_pdu->msg.key.replay_counter, prot->sec_keys)) {
if (eapol_pdu->msg.key.key_information.encrypted_key_data) {
// This should include the GTK KDE, Lifetime KDE and GTKL KDE.
// At least some of them should be present
msg = FWH_MESSAGE_3;
}
} else {
tr_error("4WH: invalid replay counter %"PRId64, eapol_pdu->msg.key.replay_counter);
}
break;
default:
break;
}
return msg;
}
static int8_t supp_fwh_sec_prot_message_send(sec_prot_t *prot, fwh_sec_prot_msg_e msg)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
eapol_pdu_t eapol_pdu;
uint16_t eapol_pdu_size = eapol_pdu_key_frame_init(&eapol_pdu, 0, NULL);
eapol_pdu.msg.key.key_information.pairwise_key = true;
switch (msg) {
case FWH_MESSAGE_2:
eapol_pdu.msg.key.replay_counter = data->recv_replay_cnt;
eapol_pdu.msg.key.key_information.key_mic = true;
eapol_pdu.msg.key.key_length = 0;
eapol_pdu.msg.key.key_nonce = data->snonce;
break;
case FWH_MESSAGE_4:
eapol_pdu.msg.key.replay_counter = data->recv_replay_cnt;
eapol_pdu.msg.key.key_information.key_mic = true;
eapol_pdu.msg.key.key_information.secured_key_frame = true;
eapol_pdu.msg.key.key_length = 0;
break;
default:
break;
}
uint8_t *eapol_pdu_frame = sec_prot_lib_message_build(data->new_ptk, 0, 0, &eapol_pdu, eapol_pdu_size, prot->header_size);
if (eapol_pdu_frame == NULL) {
return -1;
}
tr_info("4WH: send %s", msg == FWH_MESSAGE_2 ? "Message 2" : "Message 4");
if (prot->send(prot, eapol_pdu_frame, eapol_pdu_size + prot->header_size) < 0) {
return -1;
}
return 0;
}
static void supp_fwh_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
sec_prot_timer_timeout_handle(prot, &data->common, NULL, ticks);
}
static void supp_fwh_sec_prot_state_machine(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
// 4WH supplicant state machine
switch (sec_prot_state_get(&data->common)) {
case FWH_STATE_INIT:
tr_info("4WH: init");
prot->timer_start(prot);
sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_1);
break;
// Wait 4WH message 1 (starts handshake on supplicant)
case FWH_STATE_MESSAGE_1:
if (data->recv_msg != FWH_MESSAGE_1) {
return;
}
// PMKID must be valid
if (supp_fwh_kde_handle(prot) < 0) {
return;
}
// Set default timeout for the total maximum length of the negotiation
sec_prot_default_timeout_set(&data->common);
tr_info("4WH: start");
// Store authenticator nonce for check when 4WH Message 3 is received
supp_fwh_sec_prot_anonce_store(prot);
sec_prot_lib_nonce_generate(data->snonce);
if (supp_fwh_sec_prot_ptk_generate(prot, prot->sec_keys) < 0) {
return;
}
supp_fwh_sec_prot_recv_replay_counter_store(prot);
// Send KMP-CREATE.indication
prot->create_ind(prot);
sec_prot_state_set(prot, &data->common, FWH_STATE_CREATE_RESP);
break;
// Wait KMP-CREATE.response
case FWH_STATE_CREATE_RESP:
if (sec_prot_result_ok_check(&data->common)) {
// Send 4WH message 2
supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_2);
data->common.ticks = prot->prot_cfg->sec_prot_retry_timeout;
sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_3);
} else {
// Ready to be deleted
sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED);
}
break;
// Wait 4WH message 3 (message 2 has been sent)
case FWH_STATE_MESSAGE_3:
if (data->recv_msg == FWH_MESSAGE_1) {
// PMKID must be valid
if (supp_fwh_kde_handle(prot) < 0) {
return;
}
// Store authenticator nonce for check when 4WH Message 3 is received
supp_fwh_sec_prot_anonce_store(prot);
sec_prot_lib_nonce_generate(data->snonce);
if (supp_fwh_sec_prot_ptk_generate(prot, prot->sec_keys) < 0) {
return;
}
supp_fwh_sec_prot_recv_replay_counter_store(prot);
// Send 4WH message 2
supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_2);
data->common.ticks = prot->prot_cfg->sec_prot_retry_timeout;
return;
} else if (data->recv_msg != FWH_MESSAGE_3) {
return;
}
// MIC must be valid
if (supp_fwh_sec_prot_mic_validate(prot) < 0) {
return;
}
// Nonce must match to 4WH Message 1
if (supp_fwh_sec_prot_anonce_validate(prot) < 0) {
return;
}
// Must have at least GTKL
if (supp_fwh_kde_handle(prot) < 0) {
return;
}
supp_fwh_sec_prot_recv_replay_counter_store(prot);
supp_fwh_sec_prot_security_replay_counter_update(prot);
data->msg3_received = true;
// Sends 4WH Message 4
supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_4);
data->common.ticks = prot->prot_cfg->sec_prot_retry_timeout;
sec_prot_state_set(prot, &data->common, FWH_STATE_FINISH);
break;
case FWH_STATE_FINISH:
if (data->msg3_retry_wait) {
tr_info("4WH: Message 3 retry timeout");
sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED);
return;
}
// If Message 3 has been received updates key data and waits for Message 3 retry
if (data->msg3_received) {
data->msg3_retry_wait = true;
tr_info("4WH: finish, wait Message 3 retry");
sec_prot_keys_ptk_write(prot->sec_keys, data->new_ptk, prot->timer_cfg->ptk_lifetime);
sec_prot_keys_ptk_eui_64_write(prot->sec_keys, data->remote_eui64);
data->common.ticks = 60 * 10; // 60 seconds
// KMP-FINISHED.indication
prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys);
sec_prot_state_set(prot, &data->common, FWH_STATE_MESSAGE_3_RETRY_WAIT);
} else {
tr_info("4WH: finish");
// KMP-FINISHED.indication
prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys);
sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED);
}
break;
case FWH_STATE_MESSAGE_3_RETRY_WAIT:
if (sec_prot_result_timeout_check(&data->common)) {
tr_info("4WH: Message 3 retry timeout");
sec_prot_state_set(prot, &data->common, FWH_STATE_FINISHED);
} else {
if (data->recv_msg != FWH_MESSAGE_3) {
return;
}
// MIC must be valid
if (supp_fwh_sec_prot_mic_validate(prot) < 0) {
return;
}
// Nonce must match to 4WH Message 1
if (supp_fwh_sec_prot_anonce_validate(prot) < 0) {
return;
}
// Must have at least GTKL
if (supp_fwh_kde_handle(prot) < 0) {
return;
}
supp_fwh_sec_prot_recv_replay_counter_store(prot);
supp_fwh_sec_prot_security_replay_counter_update(prot);
tr_info("4WH: send Message 4 again");
supp_fwh_sec_prot_message_send(prot, FWH_MESSAGE_4);
}
break;
case FWH_STATE_FINISHED:
tr_info("4WH: finished");
prot->timer_stop(prot);
prot->finished(prot);
break;
default:
break;
}
}
static int8_t supp_fwh_sec_prot_ptk_generate(sec_prot_t *prot, sec_prot_keys_t *sec_keys)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
uint8_t local_eui64[8];
prot->addr_get(prot, local_eui64, data->remote_eui64);
uint8_t *remote_nonce = data->recv_eapol_pdu.msg.key.key_nonce;
if (!remote_nonce) {
tr_error("No ANonce");
return -1;
}
uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys);
sec_prot_lib_ptk_calc(pmk, local_eui64, data->remote_eui64, data->snonce, remote_nonce, data->new_ptk);
return 0;
}
static int8_t supp_fwh_sec_prot_mic_validate(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
return sec_prot_lib_mic_validate(data->new_ptk, data->recv_eapol_pdu.msg.key.key_mic, data->recv_pdu, data->recv_size);
}
static void supp_fwh_sec_prot_recv_replay_counter_store(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
data->recv_replay_cnt = data->recv_eapol_pdu.msg.key.replay_counter;
data->recv_replay_cnt_set = true;
}
static bool supp_fwh_sec_prot_recv_replay_cnt_compare(uint64_t received_counter, sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
// If previous value is set must be greater
if (data->recv_replay_cnt_set && received_counter > data->recv_replay_cnt) {
return true;
} else if (!data->recv_replay_cnt_set && received_counter >= data->recv_replay_cnt) {
// Otherwise allows also same value e.g. zero
return true;
}
return false;
}
static void supp_fwh_sec_prot_anonce_store(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
memcpy(data->anonce, data->recv_eapol_pdu.msg.key.key_nonce, EAPOL_KEY_NONCE_LEN);
}
static int8_t supp_fwh_sec_prot_anonce_validate(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
if (memcmp(data->anonce, data->recv_eapol_pdu.msg.key.key_nonce, EAPOL_KEY_NONCE_LEN) != 0) {
tr_error("ANonce invalid");
return -1;
}
return 0;
}
static void supp_fwh_sec_prot_security_replay_counter_update(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
sec_prot_keys_pmk_replay_cnt_set(prot->sec_keys, data->recv_eapol_pdu.msg.key.replay_counter);
}
static int8_t supp_fwh_kde_handle(sec_prot_t *prot)
{
fwh_sec_prot_int_t *data = fwh_sec_prot_get(prot);
uint16_t kde_len;
uint8_t *kde = sec_prot_lib_message_handle(data->new_ptk, &kde_len, &data->recv_eapol_pdu);
if (!kde) {
return -1;
}
switch (data->recv_msg) {
case FWH_MESSAGE_1: {
uint8_t recv_pmkid[PMKID_LEN];
uint8_t calc_pmkid[PMKID_LEN];
if (kde_pmkid_read(kde, kde_len, recv_pmkid) < 0) {
goto error;
}
if (sec_prot_lib_pmkid_generate(prot, calc_pmkid, false) < 0) {
goto error;
}
if (memcmp(recv_pmkid, calc_pmkid, PMKID_LEN) != 0) {
goto error;
}
}
break;
case FWH_MESSAGE_3:
// If a valid new GTK value present, insert it
if (sec_prot_lib_gtk_read(kde, kde_len, prot->sec_keys) < 0) {
goto error;
}
break;
default:
break;
}
ns_dyn_mem_free(kde);
return 0;
error:
tr_error("Invalid KDEs");
ns_dyn_mem_free(kde);
return -1;
}
#endif /* HAVE_WS */

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2016-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* 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 SUPP_FWH_SEC_PROT_H_
#define SUPP_FWH_SEC_PROT_H_
/*
* Supplicant Four Way Handshake (4WH) security protocol. 4WH protocol is
* specified in IEEE 802.11 and Wi-SUN FANWG-FANTPS.
*
*/
/**
* supp_fwh_sec_prot_register register supplicant 4WH protocol to KMP service
*
* \param service KMP service
*
* \return < 0 failure
* \return >= 0 success
*/
int8_t supp_fwh_sec_prot_register(kmp_service_t *service);
#endif /* SUPP_FWH_SEC_PROT_H_ */