mirror of https://github.com/ARMmbed/mbed-os.git
297 lines
9.0 KiB
C
297 lines
9.0 KiB
C
/*
|
|
* 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/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/key_sec_prot/key_sec_prot.h"
|
|
|
|
#ifdef HAVE_WS
|
|
|
|
#define TRACE_GROUP "ksep"
|
|
|
|
typedef enum {
|
|
KEY_INIT = 0,
|
|
KEY_CREATE_REQ,
|
|
KEY_CREATE_RESP,
|
|
KEY_FINISH,
|
|
} key_sec_prot_state_e;
|
|
|
|
typedef struct {
|
|
key_sec_prot_state_e state; /**< Protocol state machine state */
|
|
sec_prot_result_e result; /**< Result for ongoing negotiation */
|
|
} key_sec_prot_int_t;
|
|
|
|
static uint16_t key_sec_prot_size(void);
|
|
static int8_t key_sec_prot_init(sec_prot_t *prot);
|
|
|
|
static void key_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys);
|
|
static void key_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result);
|
|
static void key_sec_prot_delete(sec_prot_t *prot);
|
|
static int8_t key_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size);
|
|
static void key_sec_prot_state_machine(sec_prot_t *prot);
|
|
|
|
#define key_sec_prot_get(prot) (key_sec_prot_int_t *) &prot->data
|
|
|
|
int8_t key_sec_prot_register(kmp_service_t *service)
|
|
{
|
|
if (!service) {
|
|
return -1;
|
|
}
|
|
|
|
if (kmp_service_sec_protocol_register(service, IEEE_802_1X_MKA_KEY, key_sec_prot_size, key_sec_prot_init) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (kmp_service_sec_protocol_register(service, IEEE_802_11_GKH_KEY, key_sec_prot_size, key_sec_prot_init) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint16_t key_sec_prot_size(void)
|
|
{
|
|
return sizeof(key_sec_prot_int_t);
|
|
}
|
|
|
|
static int8_t key_sec_prot_init(sec_prot_t *prot)
|
|
{
|
|
prot->create_req = key_sec_prot_create_request;
|
|
prot->create_resp = key_sec_prot_create_response;
|
|
|
|
prot->receive = key_sec_prot_receive;
|
|
prot->delete = key_sec_prot_delete;
|
|
prot->state_machine = key_sec_prot_state_machine;
|
|
|
|
key_sec_prot_int_t *data = key_sec_prot_get(prot);
|
|
data->state = KEY_INIT;
|
|
data->result = SEC_RESULT_OK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void key_sec_prot_delete(sec_prot_t *prot)
|
|
{
|
|
// No op at the moment
|
|
(void) prot;
|
|
}
|
|
|
|
static void key_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys)
|
|
{
|
|
key_sec_prot_int_t *data = key_sec_prot_get(prot);
|
|
|
|
uint16_t kde_len = KDE_GTKL_LEN;
|
|
|
|
uint8_t *pmk = sec_prot_keys_pmk_get(sec_keys);
|
|
uint8_t pmkid[PMKID_LEN];
|
|
if (pmk) {
|
|
if (sec_prot_lib_pmkid_generate(prot, pmkid, false) >= 0) {
|
|
kde_len += KDE_PMKID_LEN;
|
|
} else {
|
|
pmk = NULL;
|
|
}
|
|
}
|
|
|
|
uint8_t *ptk = sec_prot_keys_ptk_get(sec_keys);
|
|
uint8_t ptkid[PTKID_LEN];
|
|
if (ptk) {
|
|
if (sec_prot_lib_ptkid_generate(prot, ptkid, false) >= 0) {
|
|
kde_len += KDE_PTKID_LEN;
|
|
} else {
|
|
ptk = NULL;
|
|
}
|
|
}
|
|
|
|
uint8_t *kde_start = ns_dyn_mem_temporary_alloc(kde_len);
|
|
if (!kde_start) {
|
|
return;
|
|
}
|
|
|
|
uint8_t *kde_end = kde_start;
|
|
|
|
if (pmk) {
|
|
kde_end = kde_pmkid_write(kde_end, pmkid);
|
|
}
|
|
|
|
if (ptk) {
|
|
kde_end = kde_ptkid_write(kde_end, ptkid);
|
|
}
|
|
|
|
uint8_t gtkl = sec_prot_keys_fresh_gtkl_get(sec_keys->gtks);
|
|
kde_end = kde_gtkl_write(kde_end, gtkl);
|
|
|
|
kde_len = kde_end - kde_start;
|
|
|
|
eapol_pdu_t eapol_pdu;
|
|
|
|
uint16_t eapol_pdu_size = eapol_pdu_key_frame_init(&eapol_pdu, kde_len, kde_start);
|
|
|
|
uint8_t *eapol_decoded_data = ns_dyn_mem_temporary_alloc(eapol_pdu_size + prot->header_size); // In future fill with data that defines eapol message
|
|
|
|
if (!eapol_decoded_data) {
|
|
data->result = SEC_RESULT_ERR_NO_MEM;
|
|
} else {
|
|
eapol_pdu.msg.key.key_information.install = false;
|
|
eapol_pdu.msg.key.key_information.pairwise_key = false;
|
|
eapol_pdu.msg.key.key_information.request = true;
|
|
eapol_pdu.msg.key.replay_counter = 0;
|
|
eapol_pdu.msg.key.key_length = 0;
|
|
eapol_write_pdu_frame(eapol_decoded_data + prot->header_size, &eapol_pdu);
|
|
|
|
tr_info("Initial EAPOL-Key send, PMKID %s PTKID %s GTKL %x", pmk ? "set" : "not set", ptk ? "set" : "not set", gtkl);
|
|
|
|
if (prot->send(prot, eapol_decoded_data, eapol_pdu_size + prot->header_size) < 0) {
|
|
data->result = SEC_RESULT_ERR_NO_MEM;
|
|
}
|
|
}
|
|
|
|
ns_dyn_mem_free(kde_start);
|
|
|
|
data->state = KEY_CREATE_REQ;
|
|
prot->state_machine_call(prot);
|
|
}
|
|
|
|
static void key_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result)
|
|
{
|
|
key_sec_prot_int_t *data = key_sec_prot_get(prot);
|
|
data->state = KEY_CREATE_RESP;
|
|
data->result = result;
|
|
prot->state_machine_call(prot);
|
|
}
|
|
|
|
static int8_t key_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size)
|
|
{
|
|
eapol_pdu_t eapol_pdu;
|
|
|
|
tr_info("Initial EAPOL-Key recv, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
|
|
|
|
// Decoding is successful
|
|
if (eapol_parse_pdu_header(pdu, size, &eapol_pdu)) {
|
|
if (eapol_pdu.packet_type != EAPOL_KEY_TYPE) {
|
|
tr_info("not EAPOL-Key packet");
|
|
prot->finished(prot);
|
|
return -1;
|
|
}
|
|
|
|
uint16_t kde_len;
|
|
uint8_t *kde = sec_prot_lib_message_handle(prot->sec_keys->ptk, &kde_len, &eapol_pdu);
|
|
if (!kde) {
|
|
tr_error("no KDEs");
|
|
prot->finished(prot);
|
|
return -1;
|
|
}
|
|
|
|
// Default assumption is that PMK and PTK are not valid
|
|
prot->sec_keys->pmk_mismatch = true;
|
|
prot->sec_keys->ptk_mismatch = true;
|
|
|
|
// Checks if supplicant indicates that it has valid PMK
|
|
uint8_t remote_keyid[KEYID_LEN];
|
|
if (kde_pmkid_read(kde, kde_len, remote_keyid) >= 0) {
|
|
uint8_t pmkid[PMKID_LEN];
|
|
if (sec_prot_lib_pmkid_generate(prot, pmkid, true) >= 0) {
|
|
if (memcmp(remote_keyid, pmkid, PMKID_LEN) == 0) {
|
|
prot->sec_keys->pmk_mismatch = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Checks if supplicant indicates that it has valid PTK
|
|
if (kde_ptkid_read(kde, kde_len, remote_keyid) >= 0) {
|
|
uint8_t ptkid[PTKID_LEN];
|
|
if (sec_prot_lib_ptkid_generate(prot, ptkid, true) >= 0) {
|
|
if (memcmp(remote_keyid, ptkid, PTKID_LEN) == 0) {
|
|
prot->sec_keys->ptk_mismatch = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the GTKL that supplicant indicates
|
|
uint8_t gtkl;
|
|
if (kde_gtkl_read(kde, kde_len, >kl) >= 0) {
|
|
prot->sec_keys->gtkl = gtkl;
|
|
} else {
|
|
tr_error("no GTKL");
|
|
return -1;
|
|
}
|
|
|
|
tr_info("PMK %s PTK %s GTKL %x", prot->sec_keys->pmk_mismatch ? "not live" : "live", prot->sec_keys->ptk_mismatch ? "not live" : "live", gtkl);
|
|
|
|
ns_dyn_mem_free(kde);
|
|
|
|
prot->create_ind(prot);
|
|
return 0;
|
|
} else {
|
|
tr_error("Invalid");
|
|
// No error handling yet, indicate just that ready to be deleted
|
|
prot->finished(prot);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static void key_sec_prot_state_machine(sec_prot_t *prot)
|
|
{
|
|
key_sec_prot_int_t *data = key_sec_prot_get(prot);
|
|
|
|
// Mixes currently supplicant and authenticator states
|
|
switch (data->state) {
|
|
case KEY_INIT:
|
|
// empty
|
|
break;
|
|
case KEY_CREATE_REQ:
|
|
// KMP-CREATE.confirm
|
|
prot->create_conf(prot, data->result);
|
|
|
|
if (data->result == SEC_RESULT_OK) {
|
|
// KMP-FINISHED.indication, no meaning for eapol-key, just completes transfer
|
|
prot->finished_ind(prot, SEC_RESULT_OK, 0);
|
|
}
|
|
// Ready to be deleted
|
|
prot->finished(prot);
|
|
break;
|
|
case KEY_CREATE_RESP:
|
|
if (data->result == SEC_RESULT_OK) {
|
|
// KMP-FINISHED.indication, no meaning for eapol-key, just completes transfer
|
|
prot->finished_ind(prot, SEC_RESULT_OK, 0);
|
|
}
|
|
// Ready to be deleted
|
|
prot->finished(prot);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif /* HAVE_WS */
|
|
|