mirror of https://github.com/ARMmbed/mbed-os.git
741 lines
24 KiB
C
741 lines
24 KiB
C
/*
|
|
* Copyright (c) 2019-2020, Pelion 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 "Service_Libs/Trickle/trickle.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/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/eap_tls_sec_prot/eap_tls_sec_prot_lib.h"
|
|
#include "Security/protocols/tls_sec_prot/tls_sec_prot.h"
|
|
#include "Security/protocols/tls_sec_prot/tls_sec_prot_lib.h"
|
|
|
|
#ifdef HAVE_WS
|
|
|
|
#define TRACE_GROUP "tlsp"
|
|
|
|
typedef enum {
|
|
TLS_STATE_INIT = SEC_STATE_INIT,
|
|
TLS_STATE_CREATE_REQ = SEC_STATE_CREATE_REQ,
|
|
TLS_STATE_CREATE_RESP = SEC_STATE_CREATE_RESP,
|
|
TLS_STATE_CREATE_IND = SEC_STATE_CREATE_IND,
|
|
|
|
TLS_STATE_CLIENT_HELLO = SEC_STATE_FIRST,
|
|
TLS_STATE_CONFIGURE,
|
|
TLS_STATE_PROCESS,
|
|
|
|
TLS_STATE_FINISH = SEC_STATE_FINISH,
|
|
TLS_STATE_FINISHED = SEC_STATE_FINISHED
|
|
} eap_tls_sec_prot_state_e;
|
|
|
|
typedef struct tls_sec_prot_lib_int_s tls_sec_prot_lib_int_t;
|
|
|
|
typedef struct {
|
|
sec_prot_common_t common; /**< Common data */
|
|
uint8_t new_pmk[PMK_LEN]; /**< New Pair Wise Master Key */
|
|
tls_data_t tls_send; /**< TLS send buffer */
|
|
tls_data_t tls_recv; /**< TLS receive buffer */
|
|
uint32_t int_timer; /**< TLS intermediate timer timeout */
|
|
uint32_t fin_timer; /**< TLS final timer timeout */
|
|
bool fin_timer_timeout; /**< TLS final timer has timeouted */
|
|
bool timer_running : 1; /**< TLS timer running */
|
|
bool finished : 1; /**< TLS finished */
|
|
bool calculating : 1; /**< TLS is calculating */
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
bool queued : 1; /**< TLS is queued */
|
|
#endif
|
|
bool library_init : 1; /**< TLS library has been initialized */
|
|
tls_sec_prot_lib_int_t *tls_sec_inst; /**< TLS security library storage, SHALL BE THE LAST FIELD */
|
|
} tls_sec_prot_int_t;
|
|
|
|
// TLS server EC queue is currently disabled, since EC calculation is made on server in one go
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
typedef struct {
|
|
ns_list_link_t link; /**< Link */
|
|
sec_prot_t *prot; /**< Protocol instance */
|
|
} tls_sec_prot_queue_t;
|
|
#endif
|
|
|
|
static uint16_t tls_sec_prot_size(void);
|
|
static int8_t client_tls_sec_prot_init(sec_prot_t *prot);
|
|
static int8_t server_tls_sec_prot_init(sec_prot_t *prot);
|
|
|
|
static void tls_sec_prot_create_request(sec_prot_t *prot, sec_prot_keys_t *sec_keys);
|
|
static void tls_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result);
|
|
static void tls_sec_prot_delete(sec_prot_t *prot);
|
|
static int8_t tls_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size);
|
|
static void tls_sec_prot_finished_send(sec_prot_t *prot);
|
|
|
|
static void client_tls_sec_prot_state_machine(sec_prot_t *prot);
|
|
static void server_tls_sec_prot_state_machine(sec_prot_t *prot);
|
|
|
|
static void tls_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks);
|
|
|
|
static int16_t tls_sec_prot_tls_send(void *handle, const void *buf, size_t len);
|
|
static int16_t tls_sec_prot_tls_receive(void *handle, unsigned char *buf, size_t len);
|
|
static void tls_sec_prot_tls_export_keys(void *handle, const uint8_t *master_secret, const uint8_t *eap_tls_key_material);
|
|
static void tls_sec_prot_tls_set_timer(void *handle, uint32_t inter, uint32_t fin);
|
|
static int8_t tls_sec_prot_tls_get_timer(void *handle);
|
|
|
|
static int8_t tls_sec_prot_tls_configure_and_connect(sec_prot_t *prot, bool is_server);
|
|
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
static bool tls_sec_prot_queue_check(sec_prot_t *prot);
|
|
static bool tls_sec_prot_queue_process(sec_prot_t *prot);
|
|
static void tls_sec_prot_queue_remove(sec_prot_t *prot);
|
|
#else
|
|
#define tls_sec_prot_queue_process(prot) true
|
|
#define tls_sec_prot_queue_remove(prot)
|
|
#endif /* SERVER_TLS_EC_CALC_QUEUE */
|
|
|
|
static uint16_t tls_sec_prot_send_buffer_size_get(sec_prot_t *prot);
|
|
|
|
#define tls_sec_prot_get(prot) (tls_sec_prot_int_t *) &prot->data
|
|
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
static NS_LIST_DEFINE(tls_sec_prot_queue, tls_sec_prot_queue_t, link);
|
|
#endif
|
|
|
|
int8_t client_tls_sec_prot_register(kmp_service_t *service)
|
|
{
|
|
if (!service) {
|
|
return -1;
|
|
}
|
|
|
|
if (kmp_service_sec_protocol_register(service, TLS_PROT, tls_sec_prot_size, client_tls_sec_prot_init) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int8_t server_tls_sec_prot_register(kmp_service_t *service)
|
|
{
|
|
if (!service) {
|
|
return -1;
|
|
}
|
|
|
|
if (kmp_service_sec_protocol_register(service, TLS_PROT, tls_sec_prot_size, server_tls_sec_prot_init) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint16_t tls_sec_prot_size(void)
|
|
{
|
|
return sizeof(tls_sec_prot_int_t) + tls_sec_prot_lib_size();
|
|
}
|
|
|
|
static int8_t client_tls_sec_prot_init(sec_prot_t *prot)
|
|
{
|
|
prot->create_req = tls_sec_prot_create_request;
|
|
prot->create_resp = NULL;
|
|
prot->receive = tls_sec_prot_receive;
|
|
prot->delete = tls_sec_prot_delete;
|
|
prot->state_machine = client_tls_sec_prot_state_machine;
|
|
prot->timer_timeout = tls_sec_prot_timer_timeout;
|
|
prot->finished_send = tls_sec_prot_finished_send;
|
|
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
sec_prot_init(&data->common);
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_INIT);
|
|
|
|
memset(data->new_pmk, 0, PMK_LEN);
|
|
data->finished = false;
|
|
// Set from security parameters
|
|
eap_tls_sec_prot_lib_message_init(&data->tls_recv);
|
|
eap_tls_sec_prot_lib_message_init(&data->tls_send);
|
|
data->int_timer = 0;
|
|
data->fin_timer = 0;
|
|
data->fin_timer_timeout = false;
|
|
data->timer_running = false;
|
|
data->calculating = false;
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
data->queued = false;
|
|
#endif
|
|
data->library_init = false;
|
|
return 0;
|
|
}
|
|
|
|
static int8_t server_tls_sec_prot_init(sec_prot_t *prot)
|
|
{
|
|
prot->create_req = NULL;
|
|
prot->create_resp = tls_sec_prot_create_response;
|
|
prot->receive = tls_sec_prot_receive;
|
|
prot->delete = tls_sec_prot_delete;
|
|
prot->state_machine = server_tls_sec_prot_state_machine;
|
|
prot->timer_timeout = tls_sec_prot_timer_timeout;
|
|
prot->finished_send = tls_sec_prot_finished_send;
|
|
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
sec_prot_init(&data->common);
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_INIT);
|
|
|
|
memset(data->new_pmk, 0, PMK_LEN);
|
|
data->finished = false;
|
|
// Set from security parameters
|
|
eap_tls_sec_prot_lib_message_init(&data->tls_recv);
|
|
eap_tls_sec_prot_lib_message_init(&data->tls_send);
|
|
data->int_timer = 0;
|
|
data->fin_timer = 0;
|
|
data->fin_timer_timeout = false;
|
|
data->timer_running = false;
|
|
data->calculating = false;
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
data->queued = false;
|
|
#endif
|
|
data->library_init = false;
|
|
return 0;
|
|
}
|
|
|
|
static void tls_sec_prot_delete(sec_prot_t *prot)
|
|
{
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
eap_tls_sec_prot_lib_message_free(&data->tls_send);
|
|
eap_tls_sec_prot_lib_message_free(&data->tls_recv);
|
|
if (data->library_init) {
|
|
tr_info("TLS: free library");
|
|
tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst);
|
|
}
|
|
tls_sec_prot_queue_remove(prot);
|
|
}
|
|
|
|
static void tls_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 void tls_sec_prot_create_response(sec_prot_t *prot, sec_prot_result_e result)
|
|
{
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
// Call state machine
|
|
sec_prot_result_set(&data->common, result);
|
|
prot->state_machine_call(prot);
|
|
}
|
|
|
|
static int8_t tls_sec_prot_receive(sec_prot_t *prot, void *pdu, uint16_t size)
|
|
{
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
// Discards old data
|
|
eap_tls_sec_prot_lib_message_free(&data->tls_recv);
|
|
|
|
data->tls_recv.data = pdu;
|
|
data->tls_recv.total_len = size;
|
|
|
|
prot->state_machine(prot);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tls_sec_prot_finished_send(sec_prot_t *prot)
|
|
{
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
prot->timer_start(prot);
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_FINISHED);
|
|
}
|
|
|
|
static void tls_sec_prot_timer_timeout(sec_prot_t *prot, uint16_t ticks)
|
|
{
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
if (data->timer_running) {
|
|
if (data->int_timer > ticks) {
|
|
data->int_timer -= ticks;
|
|
} else {
|
|
data->int_timer = 0;
|
|
}
|
|
|
|
if (data->fin_timer > ticks) {
|
|
data->fin_timer -= ticks;
|
|
} else {
|
|
if (data->fin_timer > 0) {
|
|
data->fin_timer_timeout = true;
|
|
data->fin_timer = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Checks if TLS sessions queue is enabled, and if queue is enabled whether the
|
|
session is first in the queue i.e. allowed to process */
|
|
if (tls_sec_prot_queue_process(prot)) {
|
|
if (data->fin_timer_timeout) {
|
|
data->fin_timer_timeout = false;
|
|
prot->state_machine(prot);
|
|
} else if (data->calculating
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
|| data->queued
|
|
#endif
|
|
) {
|
|
prot->state_machine(prot);
|
|
}
|
|
}
|
|
|
|
sec_prot_timer_timeout_handle(prot, &data->common, NULL, ticks);
|
|
}
|
|
|
|
static void client_tls_sec_prot_state_machine(sec_prot_t *prot)
|
|
{
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
int8_t result;
|
|
|
|
switch (sec_prot_state_get(&data->common)) {
|
|
case TLS_STATE_INIT:
|
|
tr_debug("TLS: init");
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_CREATE_REQ);
|
|
prot->timer_start(prot);
|
|
// Set default timeout for the total maximum length of the negotiation
|
|
sec_prot_default_timeout_set(&data->common);
|
|
break;
|
|
|
|
// Wait KMP-CREATE.request
|
|
case TLS_STATE_CREATE_REQ:
|
|
tr_debug("TLS: start");
|
|
|
|
prot->create_conf(prot, SEC_RESULT_OK);
|
|
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_CONFIGURE);
|
|
|
|
prot->state_machine_call(prot);
|
|
break;
|
|
|
|
case TLS_STATE_CONFIGURE:
|
|
if (tls_sec_prot_tls_configure_and_connect(prot, false) < 0) {
|
|
sec_prot_result_set(&data->common, SEC_RESULT_CONF_ERROR);
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH);
|
|
return;
|
|
}
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_PROCESS);
|
|
prot->state_machine(prot);
|
|
break;
|
|
|
|
case TLS_STATE_PROCESS:
|
|
result = tls_sec_prot_lib_process((tls_security_t *) &data->tls_sec_inst);
|
|
|
|
if (result == TLS_SEC_PROT_LIB_CALCULATING) {
|
|
data->calculating = true;
|
|
prot->state_machine_call(prot);
|
|
return;
|
|
} else {
|
|
data->calculating = false;
|
|
}
|
|
|
|
if (data->tls_send.data) {
|
|
prot->send(prot, data->tls_send.data, data->tls_send.handled_len);
|
|
eap_tls_sec_prot_lib_message_init(&data->tls_send);
|
|
}
|
|
|
|
if (result != TLS_SEC_PROT_LIB_CONTINUE) {
|
|
if (result == TLS_SEC_PROT_LIB_ERROR) {
|
|
tr_error("TLS: error");
|
|
sec_prot_result_set(&data->common, SEC_RESULT_ERROR);
|
|
}
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH);
|
|
}
|
|
break;
|
|
|
|
case TLS_STATE_FINISH:
|
|
tr_debug("TLS: finish");
|
|
|
|
data->calculating = false;
|
|
|
|
if (sec_prot_result_ok_check(&data->common)) {
|
|
sec_prot_keys_pmk_write(prot->sec_keys, data->new_pmk, prot->sec_cfg->timer_cfg.pmk_lifetime);
|
|
}
|
|
|
|
// KMP-FINISHED.indication,
|
|
prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys);
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_FINISHED);
|
|
|
|
tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst);
|
|
data->library_init = false;
|
|
break;
|
|
|
|
case TLS_STATE_FINISHED:
|
|
tr_debug("TLS: finished, free %s", data->library_init ? "T" : "F");
|
|
if (data->library_init) {
|
|
tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst);
|
|
data->library_init = false;
|
|
}
|
|
prot->timer_stop(prot);
|
|
prot->finished(prot);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void server_tls_sec_prot_state_machine(sec_prot_t *prot)
|
|
{
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
int8_t result;
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
bool client_hello = false;
|
|
#endif
|
|
|
|
switch (sec_prot_state_get(&data->common)) {
|
|
case TLS_STATE_INIT:
|
|
tr_debug("TLS: init");
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_CLIENT_HELLO);
|
|
prot->timer_start(prot);
|
|
// Set default timeout for the total maximum length of the negotiation
|
|
sec_prot_default_timeout_set(&data->common);
|
|
break;
|
|
|
|
// Wait EAP request, Identity (starts handshake on supplicant)
|
|
case TLS_STATE_CLIENT_HELLO:
|
|
tr_debug("TLS: start, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
|
|
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
client_hello = true;
|
|
#endif
|
|
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_CREATE_RESP);
|
|
|
|
// Send KMP-CREATE.indication
|
|
prot->create_ind(prot);
|
|
break;
|
|
|
|
// Wait KMP-CREATE.response
|
|
case TLS_STATE_CREATE_RESP:
|
|
if (sec_prot_result_ok_check(&data->common)) {
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_CONFIGURE);
|
|
prot->state_machine_call(prot);
|
|
} else {
|
|
// Ready to be deleted
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_FINISHED);
|
|
}
|
|
break;
|
|
|
|
case TLS_STATE_CONFIGURE:
|
|
if (tls_sec_prot_tls_configure_and_connect(prot, true) < 0) {
|
|
sec_prot_result_set(&data->common, SEC_RESULT_CONF_ERROR);
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH);
|
|
return;
|
|
}
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_PROCESS);
|
|
prot->state_machine(prot);
|
|
break;
|
|
|
|
case TLS_STATE_PROCESS:
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
// If not client hello, reserves slot on TLS queue
|
|
if (!client_hello && !tls_sec_prot_queue_check(prot)) {
|
|
data->queued = true;
|
|
return;
|
|
} else {
|
|
data->queued = false;
|
|
}
|
|
#endif
|
|
|
|
result = tls_sec_prot_lib_process((tls_security_t *) &data->tls_sec_inst);
|
|
|
|
if (result == TLS_SEC_PROT_LIB_CALCULATING) {
|
|
data->calculating = true;
|
|
prot->state_machine_call(prot);
|
|
return;
|
|
} else {
|
|
data->calculating = false;
|
|
}
|
|
|
|
if (data->tls_send.data) {
|
|
prot->send(prot, data->tls_send.data, data->tls_send.handled_len);
|
|
eap_tls_sec_prot_lib_message_init(&data->tls_send);
|
|
}
|
|
|
|
if (result != TLS_SEC_PROT_LIB_CONTINUE) {
|
|
if (result == TLS_SEC_PROT_LIB_ERROR) {
|
|
tr_error("TLS: error, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
|
|
sec_prot_result_set(&data->common, SEC_RESULT_ERROR);
|
|
}
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_FINISH);
|
|
}
|
|
break;
|
|
|
|
case TLS_STATE_FINISH:
|
|
tr_debug("TLS: finish, eui-64: %s", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
|
|
|
|
data->calculating = false;
|
|
|
|
if (sec_prot_result_ok_check(&data->common)) {
|
|
sec_prot_keys_pmk_write(prot->sec_keys, data->new_pmk, prot->sec_cfg->timer_cfg.pmk_lifetime);
|
|
}
|
|
|
|
// KMP-FINISHED.indication,
|
|
prot->finished_ind(prot, sec_prot_result_get(&data->common), prot->sec_keys);
|
|
sec_prot_state_set(prot, &data->common, TLS_STATE_FINISHED);
|
|
|
|
tls_sec_prot_queue_remove(prot);
|
|
tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst);
|
|
data->library_init = false;
|
|
break;
|
|
|
|
case TLS_STATE_FINISHED: {
|
|
uint8_t *remote_eui_64 = sec_prot_remote_eui_64_addr_get(prot);
|
|
tr_debug("TLS: finished, eui-64: %s free %s", remote_eui_64 ? trace_array(sec_prot_remote_eui_64_addr_get(prot), 8) : "not set", data->library_init ? "T" : "F");
|
|
if (data->library_init) {
|
|
tls_sec_prot_lib_free((tls_security_t *) &data->tls_sec_inst);
|
|
data->library_init = false;
|
|
}
|
|
prot->timer_stop(prot);
|
|
prot->finished(prot);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int16_t tls_sec_prot_tls_send(void *handle, const void *buf, size_t len)
|
|
{
|
|
sec_prot_t *prot = handle;
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
if (!data->tls_send.data) {
|
|
uint16_t buffer_len = tls_sec_prot_send_buffer_size_get(prot);
|
|
eap_tls_sec_prot_lib_message_allocate(&data->tls_send, prot->header_size, buffer_len);
|
|
}
|
|
if (!data->tls_send.data) {
|
|
return -1;
|
|
}
|
|
|
|
/* If send buffer is too small for the TLS payload, re-allocates */
|
|
uint16_t new_len = prot->header_size + data->tls_send.handled_len + len;
|
|
if (new_len > data->tls_send.total_len) {
|
|
tr_error("TLS send buffer size too small: %i < %i, allocating new: %i", data->tls_send.total_len, new_len, data->tls_send.total_len + TLS_SEC_PROT_SEND_BUFFER_SIZE_INCREMENT);
|
|
if (eap_tls_sec_prot_lib_message_realloc(&data->tls_send, prot->header_size,
|
|
data->tls_send.total_len + TLS_SEC_PROT_SEND_BUFFER_SIZE_INCREMENT) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
memcpy(data->tls_send.data + prot->header_size + data->tls_send.handled_len, buf, len);
|
|
data->tls_send.handled_len += len;
|
|
|
|
return len;
|
|
}
|
|
|
|
static int16_t tls_sec_prot_tls_receive(void *handle, unsigned char *buf, size_t len)
|
|
{
|
|
sec_prot_t *prot = handle;
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
if (data->tls_recv.data && len > 0) {
|
|
|
|
uint16_t copy_len = len;
|
|
bool all_copied = false;
|
|
|
|
if ((uint16_t) copy_len >= data->tls_recv.total_len - data->tls_recv.handled_len) {
|
|
copy_len = data->tls_recv.total_len - data->tls_recv.handled_len;
|
|
all_copied = true;
|
|
}
|
|
|
|
memcpy(buf, data->tls_recv.data + data->tls_recv.handled_len, copy_len);
|
|
|
|
data->tls_recv.handled_len += copy_len;
|
|
|
|
if (all_copied) {
|
|
eap_tls_sec_prot_lib_message_free(&data->tls_recv);
|
|
}
|
|
|
|
return copy_len;
|
|
}
|
|
|
|
return TLS_SEC_PROT_LIB_NO_DATA;
|
|
}
|
|
|
|
static void tls_sec_prot_tls_export_keys(void *handle, const uint8_t *master_secret, const uint8_t *eap_tls_key_material)
|
|
{
|
|
(void) master_secret;
|
|
|
|
sec_prot_t *prot = handle;
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
if (eap_tls_key_material) {
|
|
memcpy(data->new_pmk, eap_tls_key_material, PMK_LEN);
|
|
}
|
|
|
|
#ifdef EXTRA_DEBUG_INFO
|
|
const uint8_t *print_data = eap_tls_key_material;
|
|
uint16_t print_data_len = 128;
|
|
while (true) {
|
|
tr_debug("EAP-TLS key material %s", trace_array(print_data, print_data_len > 32 ? 32 : print_data_len));
|
|
if (print_data_len > 32) {
|
|
print_data_len -= 32;
|
|
print_data += 32;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void tls_sec_prot_tls_set_timer(void *handle, uint32_t inter, uint32_t fin)
|
|
{
|
|
sec_prot_t *prot = handle;
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
if (fin == 0) {
|
|
data->timer_running = false;
|
|
data->int_timer = 0;
|
|
data->fin_timer = 0;
|
|
return;
|
|
}
|
|
|
|
data->timer_running = true;
|
|
data->int_timer = inter / 100;
|
|
data->fin_timer = fin / 100;
|
|
}
|
|
|
|
static int8_t tls_sec_prot_tls_get_timer(void *handle)
|
|
{
|
|
sec_prot_t *prot = handle;
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
if (!data->timer_running) {
|
|
return TLS_SEC_PROT_LIB_TIMER_CANCELLED;
|
|
} else if (data->fin_timer == 0) {
|
|
return TLS_SEC_PROT_LIB_TIMER_FIN_EXPIRY;
|
|
} else if (data->int_timer == 0) {
|
|
return TLS_SEC_PROT_LIB_TIMER_INT_EXPIRY;
|
|
}
|
|
|
|
return TLS_SEC_PROT_LIB_TIMER_NO_EXPIRY;
|
|
}
|
|
|
|
static int8_t tls_sec_prot_tls_configure_and_connect(sec_prot_t *prot, bool is_server)
|
|
{
|
|
tls_sec_prot_int_t *data = tls_sec_prot_get(prot);
|
|
|
|
// Must be free if library initialize is done
|
|
data->library_init = true;
|
|
if (tls_sec_prot_lib_init((tls_security_t *)&data->tls_sec_inst) < 0) {
|
|
tr_error("TLS: library init fail");
|
|
return -1;
|
|
}
|
|
|
|
tls_sec_prot_lib_set_cb_register((tls_security_t *)&data->tls_sec_inst, prot,
|
|
tls_sec_prot_tls_send, tls_sec_prot_tls_receive, tls_sec_prot_tls_export_keys,
|
|
tls_sec_prot_tls_set_timer, tls_sec_prot_tls_get_timer);
|
|
|
|
if (tls_sec_prot_lib_connect((tls_security_t *)&data->tls_sec_inst, is_server, prot->sec_keys->certs) < 0) {
|
|
tr_error("TLS: library connect fail");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef SERVER_TLS_EC_CALC_QUEUE
|
|
static bool tls_sec_prot_queue_check(sec_prot_t *prot)
|
|
{
|
|
bool queue_add = true;
|
|
bool queue_continue = false;
|
|
uint8_t entry_index = 0;
|
|
|
|
// Checks if TLS queue is empty or this instance is the first entry
|
|
if (ns_list_is_empty(&tls_sec_prot_queue)) {
|
|
queue_continue = true;
|
|
} else {
|
|
ns_list_foreach(tls_sec_prot_queue_t, entry, &tls_sec_prot_queue) {
|
|
if (entry->prot == prot) {
|
|
queue_add = false;
|
|
if (entry_index < 3) {
|
|
queue_continue = true;
|
|
break;
|
|
} else {
|
|
queue_continue = false;
|
|
}
|
|
}
|
|
entry_index++;
|
|
}
|
|
}
|
|
|
|
// Adds entry to queue if not there already
|
|
if (queue_add) {
|
|
tr_debug("TLS QUEUE add index: %i, eui-64: %s", entry_index, trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
|
|
tls_sec_prot_queue_t *entry = ns_dyn_mem_temporary_alloc(sizeof(tls_sec_prot_queue_t));
|
|
if (entry) {
|
|
entry->prot = prot;
|
|
ns_list_add_to_end(&tls_sec_prot_queue, entry);
|
|
}
|
|
}
|
|
|
|
return queue_continue;
|
|
}
|
|
|
|
static bool tls_sec_prot_queue_process(sec_prot_t *prot)
|
|
{
|
|
if (ns_list_is_empty(&tls_sec_prot_queue)) {
|
|
return true;
|
|
}
|
|
|
|
uint8_t entry_index = 0;
|
|
ns_list_foreach(tls_sec_prot_queue_t, entry, &tls_sec_prot_queue) {
|
|
if (entry->prot == prot) {
|
|
return true;
|
|
}
|
|
if (entry_index > 2) {
|
|
return false;
|
|
}
|
|
entry_index++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void tls_sec_prot_queue_remove(sec_prot_t *prot)
|
|
{
|
|
ns_list_foreach_safe(tls_sec_prot_queue_t, entry, &tls_sec_prot_queue) {
|
|
if (entry->prot == prot) {
|
|
ns_list_remove(&tls_sec_prot_queue, entry);
|
|
ns_dyn_mem_free(entry);
|
|
tr_debug("TLS QUEUE remove%s, eui-64: %s", ns_list_is_empty(&tls_sec_prot_queue) ? " last" : "", trace_array(sec_prot_remote_eui_64_addr_get(prot), 8));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static uint16_t tls_sec_prot_send_buffer_size_get(sec_prot_t *prot)
|
|
{
|
|
return TLS_SEC_PROT_SEND_BUFFER_SIZE + sec_prot_certs_own_cert_chain_len_get(prot->sec_keys->certs);
|
|
}
|
|
|
|
#endif /* HAVE_WS */
|